|
"use strict";
|
|
module.exports = NodeIterator;
|
|
|
|
var NodeFilter = require('./NodeFilter');
|
|
var NodeTraversal = require('./NodeTraversal');
|
|
var utils = require('./utils');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function move(node, stayWithin, directionIsNext) {
|
|
if (directionIsNext) {
|
|
return NodeTraversal.next(node, stayWithin);
|
|
} else {
|
|
if (node === stayWithin) {
|
|
return null;
|
|
}
|
|
return NodeTraversal.previous(node, null);
|
|
}
|
|
}
|
|
|
|
function isInclusiveAncestor(node, possibleChild) {
|
|
for ( ; possibleChild; possibleChild = possibleChild.parentNode) {
|
|
if (node === possibleChild) { return true; }
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function traverse(ni, directionIsNext) {
|
|
var node, beforeNode;
|
|
node = ni._referenceNode;
|
|
beforeNode = ni._pointerBeforeReferenceNode;
|
|
while (true) {
|
|
if (beforeNode === directionIsNext) {
|
|
beforeNode = !beforeNode;
|
|
} else {
|
|
node = move(node, ni._root, directionIsNext);
|
|
if (node === null) {
|
|
return null;
|
|
}
|
|
}
|
|
var result = ni._internalFilter(node);
|
|
if (result === NodeFilter.FILTER_ACCEPT) {
|
|
break;
|
|
}
|
|
}
|
|
ni._referenceNode = node;
|
|
ni._pointerBeforeReferenceNode = beforeNode;
|
|
return node;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function NodeIterator(root, whatToShow, filter) {
|
|
if (!root || !root.nodeType) {
|
|
utils.NotSupportedError();
|
|
}
|
|
|
|
|
|
this._root = root;
|
|
this._referenceNode = root;
|
|
this._pointerBeforeReferenceNode = true;
|
|
this._whatToShow = Number(whatToShow) || 0;
|
|
this._filter = filter || null;
|
|
this._active = false;
|
|
|
|
|
|
root.doc._attachNodeIterator(this);
|
|
}
|
|
|
|
Object.defineProperties(NodeIterator.prototype, {
|
|
root: { get: function root() {
|
|
return this._root;
|
|
} },
|
|
referenceNode: { get: function referenceNode() {
|
|
return this._referenceNode;
|
|
} },
|
|
pointerBeforeReferenceNode: { get: function pointerBeforeReferenceNode() {
|
|
return this._pointerBeforeReferenceNode;
|
|
} },
|
|
whatToShow: { get: function whatToShow() {
|
|
return this._whatToShow;
|
|
} },
|
|
filter: { get: function filter() {
|
|
return this._filter;
|
|
} },
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_internalFilter: { value: function _internalFilter(node) {
|
|
|
|
var result, filter;
|
|
if (this._active) {
|
|
utils.InvalidStateError();
|
|
}
|
|
|
|
|
|
if (!(((1 << (node.nodeType - 1)) & this._whatToShow))) {
|
|
return NodeFilter.FILTER_SKIP;
|
|
}
|
|
|
|
filter = this._filter;
|
|
if (filter === null) {
|
|
result = NodeFilter.FILTER_ACCEPT;
|
|
} else {
|
|
this._active = true;
|
|
try {
|
|
if (typeof filter === 'function') {
|
|
result = filter(node);
|
|
} else {
|
|
result = filter.acceptNode(node);
|
|
}
|
|
} finally {
|
|
this._active = false;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
return (+result);
|
|
} },
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_preremove: { value: function _preremove(toBeRemovedNode) {
|
|
if (isInclusiveAncestor(toBeRemovedNode, this._root)) { return; }
|
|
if (!isInclusiveAncestor(toBeRemovedNode, this._referenceNode)) { return; }
|
|
if (this._pointerBeforeReferenceNode) {
|
|
var next = toBeRemovedNode;
|
|
while (next.lastChild) {
|
|
next = next.lastChild;
|
|
}
|
|
next = NodeTraversal.next(next, this.root);
|
|
if (next) {
|
|
this._referenceNode = next;
|
|
return;
|
|
}
|
|
this._pointerBeforeReferenceNode = false;
|
|
|
|
}
|
|
if (toBeRemovedNode.previousSibling === null) {
|
|
this._referenceNode = toBeRemovedNode.parentNode;
|
|
} else {
|
|
this._referenceNode = toBeRemovedNode.previousSibling;
|
|
var lastChild;
|
|
for (lastChild = this._referenceNode.lastChild;
|
|
lastChild;
|
|
lastChild = this._referenceNode.lastChild) {
|
|
this._referenceNode = lastChild;
|
|
}
|
|
}
|
|
} },
|
|
|
|
|
|
|
|
|
|
|
|
|
|
nextNode: { value: function nextNode() {
|
|
return traverse(this, true);
|
|
} },
|
|
|
|
|
|
|
|
|
|
|
|
|
|
previousNode: { value: function previousNode() {
|
|
return traverse(this, false);
|
|
} },
|
|
|
|
|
|
|
|
|
|
|
|
|
|
detach: { value: function detach() {
|
|
|
|
|
|
|
|
|
|
} },
|
|
|
|
|
|
toString: { value: function toString() {
|
|
return "[object NodeIterator]";
|
|
} },
|
|
});
|
|
|