source: OKFNAnnotator (for Zope)/annotator_files/lib/range.js @ 3:6356e78ccf5c

Last change on this file since 3:6356e78ccf5c was 3:6356e78ccf5c, checked in by casties, 12 years ago

new version contains Annotator JS files to be used with FilesystemSite?.

File size: 8.7 KB
Line 
1var Range;
2
3Range = {};
4
5Range.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
18Range.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
84Range.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
182Range.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})();
Note: See TracBrowser for help on using the repository browser.