Mercurial > hg > OKFNAnnotator
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 })(); |