jsdom
Version:
A JavaScript implementation of many web standards
218 lines (164 loc) • 4.7 kB
JavaScript
"use strict";
const DOMException = require("../generated/DOMException");
const { filter, FILTER_ACCEPT, FILTER_REJECT, FILTER_SKIP } = require("./helpers");
const FIRST = false;
const LAST = true;
const NEXT = false;
const PREVIOUS = true;
exports.implementation = class TreeWalkerImpl {
constructor(globalObject, args, privateData) {
this._active = false;
this.root = privateData.root;
this.currentNode = this.root;
this.whatToShow = privateData.whatToShow;
this.filter = privateData.filter;
this._globalObject = globalObject;
}
get currentNode() {
return this._currentNode;
}
set currentNode(node) {
if (node === null) {
throw DOMException.create(this._globalObject, ["Cannot set currentNode to null", "NotSupportedError"]);
}
this._currentNode = node;
}
parentNode() {
let node = this._currentNode;
while (node !== null && node !== this.root) {
node = node.parentNode;
if (node !== null && filter(this, node) === FILTER_ACCEPT) {
return (this._currentNode = node);
}
}
return null;
}
firstChild() {
return this._traverseChildren(FIRST);
}
lastChild() {
return this._traverseChildren(LAST);
}
previousSibling() {
return this._traverseSiblings(PREVIOUS);
}
nextSibling() {
return this._traverseSiblings(NEXT);
}
previousNode() {
let node = this._currentNode;
while (node !== this.root) {
let sibling = node.previousSibling;
while (sibling !== null) {
node = sibling;
let result = filter(this, node);
while (result !== FILTER_REJECT && node.hasChildNodes()) {
node = node.lastChild;
result = filter(this, node);
}
if (result === FILTER_ACCEPT) {
return (this._currentNode = node);
}
sibling = node.previousSibling;
}
if (node === this.root || node.parentNode === null) {
return null;
}
node = node.parentNode;
if (filter(this, node) === FILTER_ACCEPT) {
return (this._currentNode = node);
}
}
return null;
}
nextNode() {
let node = this._currentNode;
let result = FILTER_ACCEPT;
for (;;) {
while (result !== FILTER_REJECT && node.hasChildNodes()) {
node = node.firstChild;
result = filter(this, node);
if (result === FILTER_ACCEPT) {
return (this._currentNode = node);
}
}
do {
if (node === this.root) {
return null;
}
const sibling = node.nextSibling;
if (sibling !== null) {
node = sibling;
break;
}
node = node.parentNode;
} while (node !== null);
if (node === null) {
return null;
}
result = filter(this, node);
if (result === FILTER_ACCEPT) {
return (this._currentNode = node);
}
}
}
_traverseChildren(type) {
let node = this._currentNode;
node = type === FIRST ? node.firstChild : node.lastChild;
if (node === null) {
return null;
}
main: for (;;) {
const result = filter(this, node);
if (result === FILTER_ACCEPT) {
return (this._currentNode = node);
}
if (result === FILTER_SKIP) {
const child = type === FIRST ? node.firstChild : node.lastChild;
if (child !== null) {
node = child;
continue;
}
}
for (;;) {
const sibling = type === FIRST ? node.nextSibling : node.previousSibling;
if (sibling !== null) {
node = sibling;
continue main;
}
const parent = node.parentNode;
if (parent === null || parent === this.root || parent === this._currentNode) {
return null;
}
node = parent;
}
}
}
_traverseSiblings(type) {
let node = this._currentNode;
if (node === this.root) {
return null;
}
for (;;) {
let sibling = type === NEXT ? node.nextSibling : node.previousSibling;
while (sibling !== null) {
node = sibling;
const result = filter(this, node);
if (result === FILTER_ACCEPT) {
return (this._currentNode = node);
}
sibling = type === NEXT ? node.firstChild : node.lastChild;
if (result === FILTER_REJECT || sibling === null) {
sibling = type === NEXT ? node.nextSibling : node.previousSibling;
}
}
node = node.parentNode;
if (node === null || node === this.root) {
return null;
}
if (filter(this, node) === FILTER_ACCEPT) {
return null;
}
}
}
};