summaryrefslogtreecommitdiff
path: root/client/node_modules/domino/lib/Node.js
diff options
context:
space:
mode:
Diffstat (limited to 'client/node_modules/domino/lib/Node.js')
-rw-r--r--client/node_modules/domino/lib/Node.js738
1 files changed, 738 insertions, 0 deletions
diff --git a/client/node_modules/domino/lib/Node.js b/client/node_modules/domino/lib/Node.js
new file mode 100644
index 0000000..66bd6dd
--- /dev/null
+++ b/client/node_modules/domino/lib/Node.js
@@ -0,0 +1,738 @@
+"use strict";
+module.exports = Node;
+
+var EventTarget = require('./EventTarget');
+var LinkedList = require('./LinkedList');
+var NodeUtils = require('./NodeUtils');
+var utils = require('./utils');
+
+// All nodes have a nodeType and an ownerDocument.
+// Once inserted, they also have a parentNode.
+// This is an abstract class; all nodes in a document are instances
+// of a subtype, so all the properties are defined by more specific
+// constructors.
+function Node() {
+ EventTarget.call(this);
+ this.parentNode = null;
+ this._nextSibling = this._previousSibling = this;
+ this._index = undefined;
+}
+
+var ELEMENT_NODE = Node.ELEMENT_NODE = 1;
+var ATTRIBUTE_NODE = Node.ATTRIBUTE_NODE = 2;
+var TEXT_NODE = Node.TEXT_NODE = 3;
+var CDATA_SECTION_NODE = Node.CDATA_SECTION_NODE = 4;
+var ENTITY_REFERENCE_NODE = Node.ENTITY_REFERENCE_NODE = 5;
+var ENTITY_NODE = Node.ENTITY_NODE = 6;
+var PROCESSING_INSTRUCTION_NODE = Node.PROCESSING_INSTRUCTION_NODE = 7;
+var COMMENT_NODE = Node.COMMENT_NODE = 8;
+var DOCUMENT_NODE = Node.DOCUMENT_NODE = 9;
+var DOCUMENT_TYPE_NODE = Node.DOCUMENT_TYPE_NODE = 10;
+var DOCUMENT_FRAGMENT_NODE = Node.DOCUMENT_FRAGMENT_NODE = 11;
+var NOTATION_NODE = Node.NOTATION_NODE = 12;
+
+var DOCUMENT_POSITION_DISCONNECTED = Node.DOCUMENT_POSITION_DISCONNECTED = 0x01;
+var DOCUMENT_POSITION_PRECEDING = Node.DOCUMENT_POSITION_PRECEDING = 0x02;
+var DOCUMENT_POSITION_FOLLOWING = Node.DOCUMENT_POSITION_FOLLOWING = 0x04;
+var DOCUMENT_POSITION_CONTAINS = Node.DOCUMENT_POSITION_CONTAINS = 0x08;
+var DOCUMENT_POSITION_CONTAINED_BY = Node.DOCUMENT_POSITION_CONTAINED_BY = 0x10;
+var DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20;
+
+Node.prototype = Object.create(EventTarget.prototype, {
+
+ // Node that are not inserted into the tree inherit a null parent
+
+ // XXX: the baseURI attribute is defined by dom core, but
+ // a correct implementation of it requires HTML features, so
+ // we'll come back to this later.
+ baseURI: { get: utils.nyi },
+
+ parentElement: { get: function() {
+ return (this.parentNode && this.parentNode.nodeType===ELEMENT_NODE) ? this.parentNode : null;
+ }},
+
+ hasChildNodes: { value: utils.shouldOverride },
+
+ firstChild: { get: utils.shouldOverride },
+
+ lastChild: { get: utils.shouldOverride },
+
+ previousSibling: { get: function() {
+ var parent = this.parentNode;
+ if (!parent) return null;
+ if (this === parent.firstChild) return null;
+ return this._previousSibling;
+ }},
+
+ nextSibling: { get: function() {
+ var parent = this.parentNode, next = this._nextSibling;
+ if (!parent) return null;
+ if (next === parent.firstChild) return null;
+ return next;
+ }},
+
+ textContent: {
+ // Should override for DocumentFragment/Element/Attr/Text/PI/Comment
+ get: function() { return null; },
+ set: function(v) { /* do nothing */ },
+ },
+
+ _countChildrenOfType: { value: function(type) {
+ var sum = 0;
+ for (var kid = this.firstChild; kid !== null; kid = kid.nextSibling) {
+ if (kid.nodeType === type) sum++;
+ }
+ return sum;
+ }},
+
+ _ensureInsertValid: { value: function _ensureInsertValid(node, child, isPreinsert) {
+ var parent = this, i, kid;
+ if (!node.nodeType) throw new TypeError('not a node');
+ // 1. If parent is not a Document, DocumentFragment, or Element
+ // node, throw a HierarchyRequestError.
+ switch (parent.nodeType) {
+ case DOCUMENT_NODE:
+ case DOCUMENT_FRAGMENT_NODE:
+ case ELEMENT_NODE:
+ break;
+ default: utils.HierarchyRequestError();
+ }
+ // 2. If node is a host-including inclusive ancestor of parent,
+ // throw a HierarchyRequestError.
+ if (node.isAncestor(parent)) utils.HierarchyRequestError();
+ // 3. If child is not null and its parent is not parent, then
+ // throw a NotFoundError. (replaceChild omits the 'child is not null'
+ // and throws a TypeError here if child is null.)
+ if (child !== null || !isPreinsert) {
+ if (child.parentNode !== parent) utils.NotFoundError();
+ }
+ // 4. If node is not a DocumentFragment, DocumentType, Element,
+ // Text, ProcessingInstruction, or Comment node, throw a
+ // HierarchyRequestError.
+ switch (node.nodeType) {
+ case DOCUMENT_FRAGMENT_NODE:
+ case DOCUMENT_TYPE_NODE:
+ case ELEMENT_NODE:
+ case TEXT_NODE:
+ case PROCESSING_INSTRUCTION_NODE:
+ case COMMENT_NODE:
+ break;
+ default: utils.HierarchyRequestError();
+ }
+ // 5. If either node is a Text node and parent is a document, or
+ // node is a doctype and parent is not a document, throw a
+ // HierarchyRequestError.
+ // 6. If parent is a document, and any of the statements below, switched
+ // on node, are true, throw a HierarchyRequestError.
+ if (parent.nodeType === DOCUMENT_NODE) {
+ switch (node.nodeType) {
+ case TEXT_NODE:
+ utils.HierarchyRequestError();
+ break;
+ case DOCUMENT_FRAGMENT_NODE:
+ // 6a1. If node has more than one element child or has a Text
+ // node child.
+ if (node._countChildrenOfType(TEXT_NODE) > 0)
+ utils.HierarchyRequestError();
+ switch (node._countChildrenOfType(ELEMENT_NODE)) {
+ case 0:
+ break;
+ case 1:
+ // 6a2. Otherwise, if node has one element child and either
+ // parent has an element child, child is a doctype, or child
+ // is not null and a doctype is following child. [preinsert]
+ // 6a2. Otherwise, if node has one element child and either
+ // parent has an element child that is not child or a
+ // doctype is following child. [replaceWith]
+ if (child !== null /* always true here for replaceWith */) {
+ if (isPreinsert && child.nodeType === DOCUMENT_TYPE_NODE)
+ utils.HierarchyRequestError();
+ for (kid = child.nextSibling; kid !== null; kid = kid.nextSibling) {
+ if (kid.nodeType === DOCUMENT_TYPE_NODE)
+ utils.HierarchyRequestError();
+ }
+ }
+ i = parent._countChildrenOfType(ELEMENT_NODE);
+ if (isPreinsert) {
+ // "parent has an element child"
+ if (i > 0)
+ utils.HierarchyRequestError();
+ } else {
+ // "parent has an element child that is not child"
+ if (i > 1 || (i === 1 && child.nodeType !== ELEMENT_NODE))
+ utils.HierarchyRequestError();
+ }
+ break;
+ default: // 6a1, continued. (more than one Element child)
+ utils.HierarchyRequestError();
+ }
+ break;
+ case ELEMENT_NODE:
+ // 6b. parent has an element child, child is a doctype, or
+ // child is not null and a doctype is following child. [preinsert]
+ // 6b. parent has an element child that is not child or a
+ // doctype is following child. [replaceWith]
+ if (child !== null /* always true here for replaceWith */) {
+ if (isPreinsert && child.nodeType === DOCUMENT_TYPE_NODE)
+ utils.HierarchyRequestError();
+ for (kid = child.nextSibling; kid !== null; kid = kid.nextSibling) {
+ if (kid.nodeType === DOCUMENT_TYPE_NODE)
+ utils.HierarchyRequestError();
+ }
+ }
+ i = parent._countChildrenOfType(ELEMENT_NODE);
+ if (isPreinsert) {
+ // "parent has an element child"
+ if (i > 0)
+ utils.HierarchyRequestError();
+ } else {
+ // "parent has an element child that is not child"
+ if (i > 1 || (i === 1 && child.nodeType !== ELEMENT_NODE))
+ utils.HierarchyRequestError();
+ }
+ break;
+ case DOCUMENT_TYPE_NODE:
+ // 6c. parent has a doctype child, child is non-null and an
+ // element is preceding child, or child is null and parent has
+ // an element child. [preinsert]
+ // 6c. parent has a doctype child that is not child, or an
+ // element is preceding child. [replaceWith]
+ if (child === null) {
+ if (parent._countChildrenOfType(ELEMENT_NODE))
+ utils.HierarchyRequestError();
+ } else {
+ // child is always non-null for [replaceWith] case
+ for (kid = parent.firstChild; kid !== null; kid = kid.nextSibling) {
+ if (kid === child) break;
+ if (kid.nodeType === ELEMENT_NODE)
+ utils.HierarchyRequestError();
+ }
+ }
+ i = parent._countChildrenOfType(DOCUMENT_TYPE_NODE);
+ if (isPreinsert) {
+ // "parent has an doctype child"
+ if (i > 0)
+ utils.HierarchyRequestError();
+ } else {
+ // "parent has an doctype child that is not child"
+ if (i > 1 || (i === 1 && child.nodeType !== DOCUMENT_TYPE_NODE))
+ utils.HierarchyRequestError();
+ }
+ break;
+ }
+ } else {
+ // 5, continued: (parent is not a document)
+ if (node.nodeType === DOCUMENT_TYPE_NODE) utils.HierarchyRequestError();
+ }
+ }},
+
+ insertBefore: { value: function insertBefore(node, child) {
+ var parent = this;
+ // 1. Ensure pre-insertion validity
+ parent._ensureInsertValid(node, child, true);
+ // 2. Let reference child be child.
+ var refChild = child;
+ // 3. If reference child is node, set it to node's next sibling
+ if (refChild === node) { refChild = node.nextSibling; }
+ // 4. Adopt node into parent's node document.
+ parent.doc.adoptNode(node);
+ // 5. Insert node into parent before reference child.
+ node._insertOrReplace(parent, refChild, false);
+ // 6. Return node
+ return node;
+ }},
+
+
+ appendChild: { value: function(child) {
+ // This invokes _appendChild after doing validity checks.
+ return this.insertBefore(child, null);
+ }},
+
+ _appendChild: { value: function(child) {
+ child._insertOrReplace(this, null, false);
+ }},
+
+ removeChild: { value: function removeChild(child) {
+ var parent = this;
+ if (!child.nodeType) throw new TypeError('not a node');
+ if (child.parentNode !== parent) utils.NotFoundError();
+ child.remove();
+ return child;
+ }},
+
+ // To replace a `child` with `node` within a `parent` (this)
+ replaceChild: { value: function replaceChild(node, child) {
+ var parent = this;
+ // Ensure validity (slight differences from pre-insertion check)
+ parent._ensureInsertValid(node, child, false);
+ // Adopt node into parent's node document.
+ if (node.doc !== parent.doc) {
+ // XXX adoptNode has side-effect of removing node from its parent
+ // and generating a mutation event, thus causing the _insertOrReplace
+ // to generate two deletes and an insert instead of a 'move'
+ // event. It looks like the new MutationObserver stuff avoids
+ // this problem, but for now let's only adopt (ie, remove `node`
+ // from its parent) here if we need to.
+ parent.doc.adoptNode(node);
+ }
+ // Do the replace.
+ node._insertOrReplace(parent, child, true);
+ return child;
+ }},
+
+ // See: http://ejohn.org/blog/comparing-document-position/
+ contains: { value: function contains(node) {
+ if (node === null) { return false; }
+ if (this === node) { return true; /* inclusive descendant */ }
+ /* jshint bitwise: false */
+ return (this.compareDocumentPosition(node) &
+ DOCUMENT_POSITION_CONTAINED_BY) !== 0;
+ }},
+
+ compareDocumentPosition: { value: function compareDocumentPosition(that){
+ // Basic algorithm for finding the relative position of two nodes.
+ // Make a list the ancestors of each node, starting with the
+ // document element and proceeding down to the nodes themselves.
+ // Then, loop through the lists, looking for the first element
+ // that differs. The order of those two elements give the
+ // order of their descendant nodes. Or, if one list is a prefix
+ // of the other one, then that node contains the other.
+
+ if (this === that) return 0;
+
+ // If they're not owned by the same document or if one is rooted
+ // and one is not, then they're disconnected.
+ if (this.doc !== that.doc ||
+ this.rooted !== that.rooted)
+ return (DOCUMENT_POSITION_DISCONNECTED +
+ DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC);
+
+ // Get arrays of ancestors for this and that
+ var these = [], those = [];
+ for(var n = this; n !== null; n = n.parentNode) these.push(n);
+ for(n = that; n !== null; n = n.parentNode) those.push(n);
+ these.reverse(); // So we start with the outermost
+ those.reverse();
+
+ if (these[0] !== those[0]) // No common ancestor
+ return (DOCUMENT_POSITION_DISCONNECTED +
+ DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC);
+
+ n = Math.min(these.length, those.length);
+ for(var i = 1; i < n; i++) {
+ if (these[i] !== those[i]) {
+ // We found two different ancestors, so compare
+ // their positions
+ if (these[i].index < those[i].index)
+ return DOCUMENT_POSITION_FOLLOWING;
+ else
+ return DOCUMENT_POSITION_PRECEDING;
+ }
+ }
+
+ // If we get to here, then one of the nodes (the one with the
+ // shorter list of ancestors) contains the other one.
+ if (these.length < those.length)
+ return (DOCUMENT_POSITION_FOLLOWING +
+ DOCUMENT_POSITION_CONTAINED_BY);
+ else
+ return (DOCUMENT_POSITION_PRECEDING +
+ DOCUMENT_POSITION_CONTAINS);
+ }},
+
+ isSameNode: {value : function isSameNode(node) {
+ return this === node;
+ }},
+
+
+ // This method implements the generic parts of node equality testing
+ // and defers to the (non-recursive) type-specific isEqual() method
+ // defined by subclasses
+ isEqualNode: { value: function isEqualNode(node) {
+ if (!node) return false;
+ if (node.nodeType !== this.nodeType) return false;
+
+ // Check type-specific properties for equality
+ if (!this.isEqual(node)) return false;
+
+ // Now check children for number and equality
+ for (var c1 = this.firstChild, c2 = node.firstChild;
+ c1 && c2;
+ c1 = c1.nextSibling, c2 = c2.nextSibling) {
+ if (!c1.isEqualNode(c2)) return false;
+ }
+ return c1 === null && c2 === null;
+ }},
+
+ // This method delegates shallow cloning to a clone() method
+ // that each concrete subclass must implement
+ cloneNode: { value: function(deep) {
+ // Clone this node
+ var clone = this.clone();
+
+ // Handle the recursive case if necessary
+ if (deep) {
+ for (var kid = this.firstChild; kid !== null; kid = kid.nextSibling) {
+ clone._appendChild(kid.cloneNode(true));
+ }
+ }
+
+ return clone;
+ }},
+
+ lookupPrefix: { value: function lookupPrefix(ns) {
+ var e;
+ if (ns === '' || ns === null || ns === undefined) return null;
+ switch(this.nodeType) {
+ case ELEMENT_NODE:
+ return this._lookupNamespacePrefix(ns, this);
+ case DOCUMENT_NODE:
+ e = this.documentElement;
+ return e ? e.lookupPrefix(ns) : null;
+ case ENTITY_NODE:
+ case NOTATION_NODE:
+ case DOCUMENT_FRAGMENT_NODE:
+ case DOCUMENT_TYPE_NODE:
+ return null;
+ case ATTRIBUTE_NODE:
+ e = this.ownerElement;
+ return e ? e.lookupPrefix(ns) : null;
+ default:
+ e = this.parentElement;
+ return e ? e.lookupPrefix(ns) : null;
+ }
+ }},
+
+
+ lookupNamespaceURI: {value: function lookupNamespaceURI(prefix) {
+ if (prefix === '' || prefix === undefined) { prefix = null; }
+ var e;
+ switch(this.nodeType) {
+ case ELEMENT_NODE:
+ return utils.shouldOverride();
+ case DOCUMENT_NODE:
+ e = this.documentElement;
+ return e ? e.lookupNamespaceURI(prefix) : null;
+ case ENTITY_NODE:
+ case NOTATION_NODE:
+ case DOCUMENT_TYPE_NODE:
+ case DOCUMENT_FRAGMENT_NODE:
+ return null;
+ case ATTRIBUTE_NODE:
+ e = this.ownerElement;
+ return e ? e.lookupNamespaceURI(prefix) : null;
+ default:
+ e = this.parentElement;
+ return e ? e.lookupNamespaceURI(prefix) : null;
+ }
+ }},
+
+ isDefaultNamespace: { value: function isDefaultNamespace(ns) {
+ if (ns === '' || ns === undefined) { ns = null; }
+ var defaultNamespace = this.lookupNamespaceURI(null);
+ return (defaultNamespace === ns);
+ }},
+
+ // Utility methods for nodes. Not part of the DOM
+
+ // Return the index of this node in its parent.
+ // Throw if no parent, or if this node is not a child of its parent
+ index: { get: function() {
+ var parent = this.parentNode;
+ if (this === parent.firstChild) return 0; // fast case
+ var kids = parent.childNodes;
+ if (this._index === undefined || kids[this._index] !== this) {
+ // Ensure that we don't have an O(N^2) blowup if none of the
+ // kids have defined indices yet and we're traversing via
+ // nextSibling or previousSibling
+ for (var i=0; i<kids.length; i++) {
+ kids[i]._index = i;
+ }
+ utils.assert(kids[this._index] === this);
+ }
+ return this._index;
+ }},
+
+ // Return true if this node is equal to or is an ancestor of that node
+ // Note that nodes are considered to be ancestors of themselves
+ isAncestor: { value: function(that) {
+ // If they belong to different documents, then they're unrelated.
+ if (this.doc !== that.doc) return false;
+ // If one is rooted and one isn't then they're not related
+ if (this.rooted !== that.rooted) return false;
+
+ // Otherwise check by traversing the parentNode chain
+ for(var e = that; e; e = e.parentNode) {
+ if (e === this) return true;
+ }
+ return false;
+ }},
+
+ // DOMINO Changed the behavior to conform with the specs. See:
+ // https://groups.google.com/d/topic/mozilla.dev.platform/77sIYcpdDmc/discussion
+ ensureSameDoc: { value: function(that) {
+ if (that.ownerDocument === null) {
+ that.ownerDocument = this.doc;
+ }
+ else if(that.ownerDocument !== this.doc) {
+ utils.WrongDocumentError();
+ }
+ }},
+
+ removeChildren: { value: utils.shouldOverride },
+
+ // Insert this node as a child of parent before the specified child,
+ // or insert as the last child of parent if specified child is null,
+ // or replace the specified child with this node, firing mutation events as
+ // necessary
+ _insertOrReplace: { value: function _insertOrReplace(parent, before, isReplace) {
+ var child = this, before_index, i;
+
+ if (child.nodeType === DOCUMENT_FRAGMENT_NODE && child.rooted) {
+ utils.HierarchyRequestError();
+ }
+
+ /* Ensure index of `before` is cached before we (possibly) remove it. */
+ if (parent._childNodes) {
+ before_index = (before === null) ? parent._childNodes.length :
+ before.index; /* ensure _index is cached */
+
+ // If we are already a child of the specified parent, then
+ // the index may have to be adjusted.
+ if (child.parentNode === parent) {
+ var child_index = child.index;
+ // If the child is before the spot it is to be inserted at,
+ // then when it is removed, the index of that spot will be
+ // reduced.
+ if (child_index < before_index) {
+ before_index--;
+ }
+ }
+ }
+
+ // Delete the old child
+ if (isReplace) {
+ if (before.rooted) before.doc.mutateRemove(before);
+ before.parentNode = null;
+ }
+
+ var n = before;
+ if (n === null) { n = parent.firstChild; }
+
+ // If both the child and the parent are rooted, then we want to
+ // transplant the child without uprooting and rerooting it.
+ var bothRooted = child.rooted && parent.rooted;
+ if (child.nodeType === DOCUMENT_FRAGMENT_NODE) {
+ var spliceArgs = [0, isReplace ? 1 : 0], next;
+ for (var kid = child.firstChild; kid !== null; kid = next) {
+ next = kid.nextSibling;
+ spliceArgs.push(kid);
+ kid.parentNode = parent;
+ }
+ var len = spliceArgs.length;
+ // Add all nodes to the new parent, overwriting the old child
+ if (isReplace) {
+ LinkedList.replace(n, len > 2 ? spliceArgs[2] : null);
+ } else if (len > 2 && n !== null) {
+ LinkedList.insertBefore(spliceArgs[2], n);
+ }
+ if (parent._childNodes) {
+ spliceArgs[0] = (before === null) ?
+ parent._childNodes.length : before._index;
+ parent._childNodes.splice.apply(parent._childNodes, spliceArgs);
+ for (i=2; i<len; i++) {
+ spliceArgs[i]._index = spliceArgs[0] + (i - 2);
+ }
+ } else if (parent._firstChild === before) {
+ if (len > 2) {
+ parent._firstChild = spliceArgs[2];
+ } else if (isReplace) {
+ parent._firstChild = null;
+ }
+ }
+ // Remove all nodes from the document fragment
+ if (child._childNodes) {
+ child._childNodes.length = 0;
+ } else {
+ child._firstChild = null;
+ }
+ // Call the mutation handlers
+ // Use spliceArgs since the original array has been destroyed. The
+ // liveness guarantee requires us to clone the array so that
+ // references to the childNodes of the DocumentFragment will be empty
+ // when the insertion handlers are called.
+ if (parent.rooted) {
+ parent.modify();
+ for (i = 2; i < len; i++) {
+ parent.doc.mutateInsert(spliceArgs[i]);
+ }
+ }
+ } else {
+ if (before === child) { return; }
+ if (bothRooted) {
+ // Remove the child from its current position in the tree
+ // without calling remove(), since we don't want to uproot it.
+ child._remove();
+ } else if (child.parentNode) {
+ child.remove();
+ }
+
+ // Insert it as a child of its new parent
+ child.parentNode = parent;
+ if (isReplace) {
+ LinkedList.replace(n, child);
+ if (parent._childNodes) {
+ child._index = before_index;
+ parent._childNodes[before_index] = child;
+ } else if (parent._firstChild === before) {
+ parent._firstChild = child;
+ }
+ } else {
+ if (n !== null) {
+ LinkedList.insertBefore(child, n);
+ }
+ if (parent._childNodes) {
+ child._index = before_index;
+ parent._childNodes.splice(before_index, 0, child);
+ } else if (parent._firstChild === before) {
+ parent._firstChild = child;
+ }
+ }
+ if (bothRooted) {
+ parent.modify();
+ // Generate a move mutation event
+ parent.doc.mutateMove(child);
+ } else if (parent.rooted) {
+ parent.modify();
+ parent.doc.mutateInsert(child);
+ }
+ }
+ }},
+
+
+ // Return the lastModTime value for this node. (For use as a
+ // cache invalidation mechanism. If the node does not already
+ // have one, initialize it from the owner document's modclock
+ // property. (Note that modclock does not return the actual
+ // time; it is simply a counter incremented on each document
+ // modification)
+ lastModTime: { get: function() {
+ if (!this._lastModTime) {
+ this._lastModTime = this.doc.modclock;
+ }
+ return this._lastModTime;
+ }},
+
+ // Increment the owner document's modclock and use the new
+ // value to update the lastModTime value for this node and
+ // all of its ancestors. Nodes that have never had their
+ // lastModTime value queried do not need to have a
+ // lastModTime property set on them since there is no
+ // previously queried value to ever compare the new value
+ // against, so only update nodes that already have a
+ // _lastModTime property.
+ modify: { value: function() {
+ if (this.doc.modclock) { // Skip while doc.modclock == 0
+ var time = ++this.doc.modclock;
+ for(var n = this; n; n = n.parentElement) {
+ if (n._lastModTime) {
+ n._lastModTime = time;
+ }
+ }
+ }
+ }},
+
+ // This attribute is not part of the DOM but is quite helpful.
+ // It returns the document with which a node is associated. Usually
+ // this is the ownerDocument. But ownerDocument is null for the
+ // document object itself, so this is a handy way to get the document
+ // regardless of the node type
+ doc: { get: function() {
+ return this.ownerDocument || this;
+ }},
+
+
+ // If the node has a nid (node id), then it is rooted in a document
+ rooted: { get: function() {
+ return !!this._nid;
+ }},
+
+ normalize: { value: function() {
+ var next;
+ for (var child=this.firstChild; child !== null; child=next) {
+ next = child.nextSibling;
+
+ if (child.normalize) {
+ child.normalize();
+ }
+
+ if (child.nodeType !== Node.TEXT_NODE) {
+ continue;
+ }
+
+ if (child.nodeValue === "") {
+ this.removeChild(child);
+ continue;
+ }
+
+ var prevChild = child.previousSibling;
+ if (prevChild === null) {
+ continue;
+ } else if (prevChild.nodeType === Node.TEXT_NODE) {
+ // merge this with previous and remove the child
+ prevChild.appendData(child.nodeValue);
+ this.removeChild(child);
+ }
+ }
+ }},
+
+ // Convert the children of a node to an HTML string.
+ // This is used by the innerHTML getter
+ // The serialization spec is at:
+ // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#serializing-html-fragments
+ //
+ // The serialization logic is intentionally implemented in a separate
+ // `NodeUtils` helper instead of the more obvious choice of a private
+ // `_serializeOne()` method on the `Node.prototype` in order to avoid
+ // the megamorphic `this._serializeOne` property access, which reduces
+ // performance unnecessarily. If you need specialized behavior for a
+ // certain subclass, you'll need to implement that in `NodeUtils`.
+ // See https://github.com/fgnass/domino/pull/142 for more information.
+ serialize: { value: function() {
+ var s = '';
+ for (var kid = this.firstChild; kid !== null; kid = kid.nextSibling) {
+ s += NodeUtils.serializeOne(kid, this);
+ }
+ return s;
+ }},
+
+ // Non-standard, but often useful for debugging.
+ outerHTML: {
+ get: function() {
+ return NodeUtils.serializeOne(this, { nodeType: 0 });
+ },
+ set: utils.nyi,
+ },
+
+ // mirror node type properties in the prototype, so they are present
+ // in instances of Node (and subclasses)
+ ELEMENT_NODE: { value: ELEMENT_NODE },
+ ATTRIBUTE_NODE: { value: ATTRIBUTE_NODE },
+ TEXT_NODE: { value: TEXT_NODE },
+ CDATA_SECTION_NODE: { value: CDATA_SECTION_NODE },
+ ENTITY_REFERENCE_NODE: { value: ENTITY_REFERENCE_NODE },
+ ENTITY_NODE: { value: ENTITY_NODE },
+ PROCESSING_INSTRUCTION_NODE: { value: PROCESSING_INSTRUCTION_NODE },
+ COMMENT_NODE: { value: COMMENT_NODE },
+ DOCUMENT_NODE: { value: DOCUMENT_NODE },
+ DOCUMENT_TYPE_NODE: { value: DOCUMENT_TYPE_NODE },
+ DOCUMENT_FRAGMENT_NODE: { value: DOCUMENT_FRAGMENT_NODE },
+ NOTATION_NODE: { value: NOTATION_NODE },
+
+ DOCUMENT_POSITION_DISCONNECTED: { value: DOCUMENT_POSITION_DISCONNECTED },
+ DOCUMENT_POSITION_PRECEDING: { value: DOCUMENT_POSITION_PRECEDING },
+ DOCUMENT_POSITION_FOLLOWING: { value: DOCUMENT_POSITION_FOLLOWING },
+ DOCUMENT_POSITION_CONTAINS: { value: DOCUMENT_POSITION_CONTAINS },
+ DOCUMENT_POSITION_CONTAINED_BY: { value: DOCUMENT_POSITION_CONTAINED_BY },
+ DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: { value: DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC },
+});