jsdom
Version:
A JavaScript implementation of many web standards
128 lines (103 loc) • 3.55 kB
JavaScript
"use strict";
const { filter, FILTER_ACCEPT } = require("./helpers");
exports.implementation = class NodeIteratorImpl {
constructor(globalObject, args, privateData) {
this._active = false;
this.root = privateData.root;
this.whatToShow = privateData.whatToShow;
this.filter = privateData.filter;
this._referenceNode = this.root;
this._pointerBeforeReferenceNode = true;
this._candidateReferenceNode = null;
this._candidatePointerBeforeReferenceNode = false;
this._globalObject = globalObject;
}
get referenceNode() {
return this._referenceNode;
}
get pointerBeforeReferenceNode() {
return this._pointerBeforeReferenceNode;
}
nextNode() {
return this._traverse("next");
}
previousNode() {
return this._traverse("previous");
}
detach() {
// Intentionally do nothing, per spec.
}
// https://dom.spec.whatwg.org/#concept-nodeiterator-pre-remove
_preRemoveSteps(toBeRemovedNode) {
[this._referenceNode, this._pointerBeforeReferenceNode] = this._adjustNodePointer(
this._referenceNode,
this._pointerBeforeReferenceNode,
toBeRemovedNode
);
if (this._candidateReferenceNode !== null) {
[this._candidateReferenceNode, this._candidatePointerBeforeReferenceNode] = this._adjustNodePointer(
this._candidateReferenceNode,
this._candidatePointerBeforeReferenceNode,
toBeRemovedNode
);
}
}
_adjustNodePointer(referenceNode, pointerBeforeReferenceNode, toBeRemovedNode) {
if (!toBeRemovedNode.contains(referenceNode) || toBeRemovedNode.contains(this.root)) {
return [referenceNode, pointerBeforeReferenceNode];
}
if (pointerBeforeReferenceNode) {
const next = toBeRemovedNode._nextAfterSubtree(this.root);
if (next !== null) {
return [next, true];
}
}
const { previousSibling } = toBeRemovedNode;
const newNode = previousSibling === null ?
toBeRemovedNode.parentNode :
previousSibling._lastInclusiveDescendant();
return [newNode, false];
}
_traverse(direction) {
this._candidateReferenceNode = this._referenceNode;
this._candidatePointerBeforeReferenceNode = this._pointerBeforeReferenceNode;
let result = null;
while (true) {
if (direction === "next") {
if (!this._candidatePointerBeforeReferenceNode) {
const following = this._candidateReferenceNode._nextInTree(this.root);
if (!following) {
break;
}
this._candidateReferenceNode = following;
}
this._candidatePointerBeforeReferenceNode = false;
} else if (direction === "previous") {
if (this._candidatePointerBeforeReferenceNode) {
const preceding = this._candidateReferenceNode._previousInTree(this.root);
if (!preceding) {
break;
}
this._candidateReferenceNode = preceding;
}
this._candidatePointerBeforeReferenceNode = true;
}
const node = this._candidateReferenceNode;
let filterResult;
try {
filterResult = filter(this, node);
} catch (error) {
this._candidateReferenceNode = null;
throw error;
}
if (filterResult === FILTER_ACCEPT) {
this._referenceNode = this._candidateReferenceNode;
this._pointerBeforeReferenceNode = this._candidatePointerBeforeReferenceNode;
result = node;
break;
}
}
this._candidateReferenceNode = null;
return result;
}
};