diff 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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/annotator_files/lib/range.js	Thu Apr 05 19:37:27 2012 +0200
@@ -0,0 +1,285 @@
+var Range;
+
+Range = {};
+
+Range.sniff = function(r) {
+  if (r.commonAncestorContainer != null) {
+    return new Range.BrowserRange(r);
+  } else if (typeof r.start === "string") {
+    return new Range.SerializedRange(r);
+  } else if (r.start && typeof r.start === "object") {
+    return new Range.NormalizedRange(r);
+  } else {
+    console.error(_t("Could not sniff range type"));
+    return false;
+  }
+};
+
+Range.BrowserRange = (function() {
+
+  function BrowserRange(obj) {
+    this.commonAncestorContainer = obj.commonAncestorContainer;
+    this.startContainer = obj.startContainer;
+    this.startOffset = obj.startOffset;
+    this.endContainer = obj.endContainer;
+    this.endOffset = obj.endOffset;
+  }
+
+  BrowserRange.prototype.normalize = function(root) {
+    var it, node, nr, offset, p, r, _i, _len, _ref;
+    if (this.tainted) {
+      console.error(_t("You may only call normalize() once on a BrowserRange!"));
+      return false;
+    } else {
+      this.tainted = true;
+    }
+    r = {};
+    nr = {};
+    _ref = ['start', 'end'];
+    for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+      p = _ref[_i];
+      node = this[p + 'Container'];
+      offset = this[p + 'Offset'];
+      if (!((node != null) && (offset != null))) return false;
+      if (node.nodeType === 1) {
+        it = node.childNodes[offset];
+        node = it || node.childNodes[offset - 1];
+        if (node.nodeType === 1 && !node.firstChild) {
+          it = null;
+          node = node.previousSibling;
+        }
+        while (node.nodeType !== 3) {
+          node = node.firstChild;
+        }
+        offset = it ? 0 : node.nodeValue.length;
+      }
+      r[p] = node;
+      r[p + 'Offset'] = offset;
+    }
+    nr.start = r.startOffset > 0 ? r.start.splitText(r.startOffset) : r.start;
+    if (r.start === r.end) {
+      if ((r.endOffset - r.startOffset) < nr.start.nodeValue.length) {
+        nr.start.splitText(r.endOffset - r.startOffset);
+      }
+      nr.end = nr.start;
+    } else {
+      if (r.endOffset < r.end.nodeValue.length) r.end.splitText(r.endOffset);
+      nr.end = r.end;
+    }
+    nr.commonAncestor = this.commonAncestorContainer;
+    while (nr.commonAncestor.nodeType !== 1) {
+      nr.commonAncestor = nr.commonAncestor.parentNode;
+    }
+    return new Range.NormalizedRange(nr);
+  };
+
+  BrowserRange.prototype.serialize = function(root, ignoreSelector) {
+    return this.normalize(root).serialize(root, ignoreSelector);
+  };
+
+  return BrowserRange;
+
+})();
+
+Range.NormalizedRange = (function() {
+
+  function NormalizedRange(obj) {
+    this.commonAncestor = obj.commonAncestor;
+    this.start = obj.start;
+    this.end = obj.end;
+  }
+
+  NormalizedRange.prototype.normalize = function(root) {
+    return this;
+  };
+
+  NormalizedRange.prototype.limit = function(bounds) {
+    var nodes, parent, startParents, _i, _len, _ref;
+    nodes = $.grep(this.textNodes(), function(node) {
+      return node.parentNode === bounds || $.contains(bounds, node.parentNode);
+    });
+    if (!nodes.length) return null;
+    this.start = nodes[0];
+    this.end = nodes[nodes.length - 1];
+    startParents = $(this.start).parents();
+    _ref = $(this.end).parents();
+    for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+      parent = _ref[_i];
+      if (startParents.index(parent) !== -1) {
+        this.commonAncestor = parent;
+        break;
+      }
+    }
+    return this;
+  };
+
+  NormalizedRange.prototype.serialize = function(root, ignoreSelector) {
+    var end, serialization, start;
+    serialization = function(node, isEnd) {
+      var n, nodes, offset, origParent, textNodes, xpath, _i, _len;
+      if (ignoreSelector) {
+        origParent = $(node).parents(":not(" + ignoreSelector + ")").eq(0);
+      } else {
+        origParent = $(node).parent();
+      }
+      xpath = origParent.xpath(root)[0];
+      textNodes = origParent.textNodes();
+      nodes = textNodes.slice(0, textNodes.index(node));
+      offset = 0;
+      for (_i = 0, _len = nodes.length; _i < _len; _i++) {
+        n = nodes[_i];
+        offset += n.nodeValue.length;
+      }
+      if (isEnd) {
+        return [xpath, offset + node.nodeValue.length];
+      } else {
+        return [xpath, offset];
+      }
+    };
+    start = serialization(this.start);
+    end = serialization(this.end, true);
+    return new Range.SerializedRange({
+      start: start[0],
+      end: end[0],
+      startOffset: start[1],
+      endOffset: end[1]
+    });
+  };
+
+  NormalizedRange.prototype.text = function() {
+    var node;
+    return ((function() {
+      var _i, _len, _ref, _results;
+      _ref = this.textNodes();
+      _results = [];
+      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+        node = _ref[_i];
+        _results.push(node.nodeValue);
+      }
+      return _results;
+    }).call(this)).join('');
+  };
+
+  NormalizedRange.prototype.textNodes = function() {
+    var end, start, textNodes, _ref;
+    textNodes = $(this.commonAncestor).textNodes();
+    _ref = [textNodes.index(this.start), textNodes.index(this.end)], start = _ref[0], end = _ref[1];
+    return $.makeArray(textNodes.slice(start, end + 1 || 9e9));
+  };
+
+  NormalizedRange.prototype.toRange = function() {
+    var range;
+    range = document.createRange();
+    range.setStartBefore(this.start);
+    range.setEndAfter(this.end);
+    return range;
+  };
+
+  return NormalizedRange;
+
+})();
+
+Range.SerializedRange = (function() {
+
+  function SerializedRange(obj) {
+    this.start = obj.start;
+    this.startOffset = obj.startOffset;
+    this.end = obj.end;
+    this.endOffset = obj.endOffset;
+  }
+
+  SerializedRange.prototype._nodeFromXPath = function(xpath) {
+    var customResolver, evaluateXPath, namespace, node, segment;
+    evaluateXPath = function(xp, nsResolver) {
+      if (nsResolver == null) nsResolver = null;
+      return document.evaluate(xp, document, nsResolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
+    };
+    if (!$.isXMLDoc(document.documentElement)) {
+      return evaluateXPath(xpath);
+    } else {
+      customResolver = document.createNSResolver(document.ownerDocument === null ? document.documentElement : document.ownerDocument.documentElement);
+      node = evaluateXPath(xpath, customResolver);
+      if (!node) {
+        xpath = ((function() {
+          var _i, _len, _ref, _results;
+          _ref = xpath.split('/');
+          _results = [];
+          for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+            segment = _ref[_i];
+            if (segment && segment.indexOf(':') === -1) {
+              _results.push(segment.replace(/^([a-z]+)/, 'xhtml:$1'));
+            } else {
+              _results.push(segment);
+            }
+          }
+          return _results;
+        })()).join('/');
+        namespace = document.lookupNamespaceURI(null);
+        customResolver = function(ns) {
+          if (ns === 'xhtml') {
+            return namespace;
+          } else {
+            return document.documentElement.getAttribute('xmlns:' + ns);
+          }
+        };
+        node = evaluateXPath(xpath, customResolver);
+      }
+      return node;
+    }
+  };
+
+  SerializedRange.prototype.normalize = function(root) {
+    var cacXPath, common, endAncestry, i, length, p, parentXPath, range, startAncestry, tn, _i, _j, _len, _len2, _ref, _ref2, _ref3;
+    parentXPath = $(root).xpath()[0];
+    startAncestry = this.start.split("/");
+    endAncestry = this.end.split("/");
+    common = [];
+    range = {};
+    for (i = 0, _ref = startAncestry.length; 0 <= _ref ? i < _ref : i > _ref; 0 <= _ref ? i++ : i--) {
+      if (startAncestry[i] === endAncestry[i]) {
+        common.push(startAncestry[i]);
+      } else {
+        break;
+      }
+    }
+    cacXPath = parentXPath + common.join("/");
+    range.commonAncestorContainer = this._nodeFromXPath(cacXPath);
+    if (!range.commonAncestorContainer) {
+      console.error(_t("Error deserializing range: can't find XPath '") + cacXPath + _t("'. Is this the right document?"));
+      return null;
+    }
+    _ref2 = ['start', 'end'];
+    for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
+      p = _ref2[_i];
+      length = 0;
+      _ref3 = $(this._nodeFromXPath(parentXPath + this[p])).textNodes();
+      for (_j = 0, _len2 = _ref3.length; _j < _len2; _j++) {
+        tn = _ref3[_j];
+        if (length + tn.nodeValue.length >= this[p + 'Offset']) {
+          range[p + 'Container'] = tn;
+          range[p + 'Offset'] = this[p + 'Offset'] - length;
+          break;
+        } else {
+          length += tn.nodeValue.length;
+        }
+      }
+    }
+    return new Range.BrowserRange(range).normalize(root);
+  };
+
+  SerializedRange.prototype.serialize = function(root, ignoreSelector) {
+    return this.normalize(root).serialize(root, ignoreSelector);
+  };
+
+  SerializedRange.prototype.toObject = function() {
+    return {
+      start: this.start,
+      startOffset: this.startOffset,
+      end: this.end,
+      endOffset: this.endOffset
+    };
+  };
+
+  return SerializedRange;
+
+})();