comparison annotator_files/lib/range.js @ 3:6356e78ccf5c

new version contains Annotator JS files to be used with FilesystemSite.
author casties
date Thu, 05 Apr 2012 19:37:27 +0200
parents
children 6979313586cf
comparison
equal deleted inserted replaced
2:4c6c8835fc5c 3:6356e78ccf5c
1 var Range;
2
3 Range = {};
4
5 Range.sniff = function(r) {
6 if (r.commonAncestorContainer != null) {
7 return new Range.BrowserRange(r);
8 } else if (typeof r.start === "string") {
9 return new Range.SerializedRange(r);
10 } else if (r.start && typeof r.start === "object") {
11 return new Range.NormalizedRange(r);
12 } else {
13 console.error(_t("Could not sniff range type"));
14 return false;
15 }
16 };
17
18 Range.BrowserRange = (function() {
19
20 function BrowserRange(obj) {
21 this.commonAncestorContainer = obj.commonAncestorContainer;
22 this.startContainer = obj.startContainer;
23 this.startOffset = obj.startOffset;
24 this.endContainer = obj.endContainer;
25 this.endOffset = obj.endOffset;
26 }
27
28 BrowserRange.prototype.normalize = function(root) {
29 var it, node, nr, offset, p, r, _i, _len, _ref;
30 if (this.tainted) {
31 console.error(_t("You may only call normalize() once on a BrowserRange!"));
32 return false;
33 } else {
34 this.tainted = true;
35 }
36 r = {};
37 nr = {};
38 _ref = ['start', 'end'];
39 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
40 p = _ref[_i];
41 node = this[p + 'Container'];
42 offset = this[p + 'Offset'];
43 if (!((node != null) && (offset != null))) return false;
44 if (node.nodeType === 1) {
45 it = node.childNodes[offset];
46 node = it || node.childNodes[offset - 1];
47 if (node.nodeType === 1 && !node.firstChild) {
48 it = null;
49 node = node.previousSibling;
50 }
51 while (node.nodeType !== 3) {
52 node = node.firstChild;
53 }
54 offset = it ? 0 : node.nodeValue.length;
55 }
56 r[p] = node;
57 r[p + 'Offset'] = offset;
58 }
59 nr.start = r.startOffset > 0 ? r.start.splitText(r.startOffset) : r.start;
60 if (r.start === r.end) {
61 if ((r.endOffset - r.startOffset) < nr.start.nodeValue.length) {
62 nr.start.splitText(r.endOffset - r.startOffset);
63 }
64 nr.end = nr.start;
65 } else {
66 if (r.endOffset < r.end.nodeValue.length) r.end.splitText(r.endOffset);
67 nr.end = r.end;
68 }
69 nr.commonAncestor = this.commonAncestorContainer;
70 while (nr.commonAncestor.nodeType !== 1) {
71 nr.commonAncestor = nr.commonAncestor.parentNode;
72 }
73 return new Range.NormalizedRange(nr);
74 };
75
76 BrowserRange.prototype.serialize = function(root, ignoreSelector) {
77 return this.normalize(root).serialize(root, ignoreSelector);
78 };
79
80 return BrowserRange;
81
82 })();
83
84 Range.NormalizedRange = (function() {
85
86 function NormalizedRange(obj) {
87 this.commonAncestor = obj.commonAncestor;
88 this.start = obj.start;
89 this.end = obj.end;
90 }
91
92 NormalizedRange.prototype.normalize = function(root) {
93 return this;
94 };
95
96 NormalizedRange.prototype.limit = function(bounds) {
97 var nodes, parent, startParents, _i, _len, _ref;
98 nodes = $.grep(this.textNodes(), function(node) {
99 return node.parentNode === bounds || $.contains(bounds, node.parentNode);
100 });
101 if (!nodes.length) return null;
102 this.start = nodes[0];
103 this.end = nodes[nodes.length - 1];
104 startParents = $(this.start).parents();
105 _ref = $(this.end).parents();
106 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
107 parent = _ref[_i];
108 if (startParents.index(parent) !== -1) {
109 this.commonAncestor = parent;
110 break;
111 }
112 }
113 return this;
114 };
115
116 NormalizedRange.prototype.serialize = function(root, ignoreSelector) {
117 var end, serialization, start;
118 serialization = function(node, isEnd) {
119 var n, nodes, offset, origParent, textNodes, xpath, _i, _len;
120 if (ignoreSelector) {
121 origParent = $(node).parents(":not(" + ignoreSelector + ")").eq(0);
122 } else {
123 origParent = $(node).parent();
124 }
125 xpath = origParent.xpath(root)[0];
126 textNodes = origParent.textNodes();
127 nodes = textNodes.slice(0, textNodes.index(node));
128 offset = 0;
129 for (_i = 0, _len = nodes.length; _i < _len; _i++) {
130 n = nodes[_i];
131 offset += n.nodeValue.length;
132 }
133 if (isEnd) {
134 return [xpath, offset + node.nodeValue.length];
135 } else {
136 return [xpath, offset];
137 }
138 };
139 start = serialization(this.start);
140 end = serialization(this.end, true);
141 return new Range.SerializedRange({
142 start: start[0],
143 end: end[0],
144 startOffset: start[1],
145 endOffset: end[1]
146 });
147 };
148
149 NormalizedRange.prototype.text = function() {
150 var node;
151 return ((function() {
152 var _i, _len, _ref, _results;
153 _ref = this.textNodes();
154 _results = [];
155 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
156 node = _ref[_i];
157 _results.push(node.nodeValue);
158 }
159 return _results;
160 }).call(this)).join('');
161 };
162
163 NormalizedRange.prototype.textNodes = function() {
164 var end, start, textNodes, _ref;
165 textNodes = $(this.commonAncestor).textNodes();
166 _ref = [textNodes.index(this.start), textNodes.index(this.end)], start = _ref[0], end = _ref[1];
167 return $.makeArray(textNodes.slice(start, end + 1 || 9e9));
168 };
169
170 NormalizedRange.prototype.toRange = function() {
171 var range;
172 range = document.createRange();
173 range.setStartBefore(this.start);
174 range.setEndAfter(this.end);
175 return range;
176 };
177
178 return NormalizedRange;
179
180 })();
181
182 Range.SerializedRange = (function() {
183
184 function SerializedRange(obj) {
185 this.start = obj.start;
186 this.startOffset = obj.startOffset;
187 this.end = obj.end;
188 this.endOffset = obj.endOffset;
189 }
190
191 SerializedRange.prototype._nodeFromXPath = function(xpath) {
192 var customResolver, evaluateXPath, namespace, node, segment;
193 evaluateXPath = function(xp, nsResolver) {
194 if (nsResolver == null) nsResolver = null;
195 return document.evaluate(xp, document, nsResolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
196 };
197 if (!$.isXMLDoc(document.documentElement)) {
198 return evaluateXPath(xpath);
199 } else {
200 customResolver = document.createNSResolver(document.ownerDocument === null ? document.documentElement : document.ownerDocument.documentElement);
201 node = evaluateXPath(xpath, customResolver);
202 if (!node) {
203 xpath = ((function() {
204 var _i, _len, _ref, _results;
205 _ref = xpath.split('/');
206 _results = [];
207 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
208 segment = _ref[_i];
209 if (segment && segment.indexOf(':') === -1) {
210 _results.push(segment.replace(/^([a-z]+)/, 'xhtml:$1'));
211 } else {
212 _results.push(segment);
213 }
214 }
215 return _results;
216 })()).join('/');
217 namespace = document.lookupNamespaceURI(null);
218 customResolver = function(ns) {
219 if (ns === 'xhtml') {
220 return namespace;
221 } else {
222 return document.documentElement.getAttribute('xmlns:' + ns);
223 }
224 };
225 node = evaluateXPath(xpath, customResolver);
226 }
227 return node;
228 }
229 };
230
231 SerializedRange.prototype.normalize = function(root) {
232 var cacXPath, common, endAncestry, i, length, p, parentXPath, range, startAncestry, tn, _i, _j, _len, _len2, _ref, _ref2, _ref3;
233 parentXPath = $(root).xpath()[0];
234 startAncestry = this.start.split("/");
235 endAncestry = this.end.split("/");
236 common = [];
237 range = {};
238 for (i = 0, _ref = startAncestry.length; 0 <= _ref ? i < _ref : i > _ref; 0 <= _ref ? i++ : i--) {
239 if (startAncestry[i] === endAncestry[i]) {
240 common.push(startAncestry[i]);
241 } else {
242 break;
243 }
244 }
245 cacXPath = parentXPath + common.join("/");
246 range.commonAncestorContainer = this._nodeFromXPath(cacXPath);
247 if (!range.commonAncestorContainer) {
248 console.error(_t("Error deserializing range: can't find XPath '") + cacXPath + _t("'. Is this the right document?"));
249 return null;
250 }
251 _ref2 = ['start', 'end'];
252 for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
253 p = _ref2[_i];
254 length = 0;
255 _ref3 = $(this._nodeFromXPath(parentXPath + this[p])).textNodes();
256 for (_j = 0, _len2 = _ref3.length; _j < _len2; _j++) {
257 tn = _ref3[_j];
258 if (length + tn.nodeValue.length >= this[p + 'Offset']) {
259 range[p + 'Container'] = tn;
260 range[p + 'Offset'] = this[p + 'Offset'] - length;
261 break;
262 } else {
263 length += tn.nodeValue.length;
264 }
265 }
266 }
267 return new Range.BrowserRange(range).normalize(root);
268 };
269
270 SerializedRange.prototype.serialize = function(root, ignoreSelector) {
271 return this.normalize(root).serialize(root, ignoreSelector);
272 };
273
274 SerializedRange.prototype.toObject = function() {
275 return {
276 start: this.start,
277 startOffset: this.startOffset,
278 end: this.end,
279 endOffset: this.endOffset
280 };
281 };
282
283 return SerializedRange;
284
285 })();