0
Fork 0
mirror of https://github.com/fastmail/Squire.git synced 2024-12-22 15:23:29 -05:00
Squire/source/node/TreeIterator.ts
Neil Jenkins fe0dfdf6c4 Squire 2.0
This is a massive refactor to port Squire to TypeScript, fix a bunch of small
bugs and modernise our tooling. The development was done on an internal
repository, so apologies to anyone following externally for the commit dump;
updates from here should come as real commits again.

Co-authored-by: Joe Woods <woods@fastmailteam.com>
2023-01-23 13:18:29 +11:00

116 lines
3.2 KiB
TypeScript

type NODE_TYPE = 1 | 4 | 5;
const SHOW_ELEMENT = 1; // NodeFilter.SHOW_ELEMENT;
const SHOW_TEXT = 4; // NodeFilter.SHOW_TEXT;
const SHOW_ELEMENT_OR_TEXT = 5; // SHOW_ELEMENT|SHOW_TEXT;
const always = (): true => true;
class TreeIterator<T extends Node> {
root: Node;
currentNode: Node;
nodeType: NODE_TYPE;
filter: (n: T) => boolean;
constructor(root: Node, nodeType: NODE_TYPE, filter?: (n: T) => boolean) {
this.root = root;
this.currentNode = root;
this.nodeType = nodeType;
this.filter = filter || always;
}
isAcceptableNode(node: Node): boolean {
const nodeType = node.nodeType;
const nodeFilterType =
nodeType === Node.ELEMENT_NODE
? SHOW_ELEMENT
: nodeType === Node.TEXT_NODE
? SHOW_TEXT
: 0;
return !!(nodeFilterType & this.nodeType) && this.filter(node as T);
}
nextNode(): T | null {
const root = this.root;
let current: Node | null = this.currentNode;
let node: Node | null;
while (true) {
node = current.firstChild;
while (!node && current) {
if (current === root) {
break;
}
node = current.nextSibling;
if (!node) {
current = current.parentNode;
}
}
if (!node) {
return null;
}
if (this.isAcceptableNode(node)) {
this.currentNode = node;
return node as T;
}
current = node;
}
}
previousNode(): T | null {
const root = this.root;
let current: Node | null = this.currentNode;
let node: Node | null;
while (true) {
if (current === root) {
return null;
}
node = current.previousSibling;
if (node) {
while ((current = node.lastChild)) {
node = current;
}
} else {
node = current.parentNode;
}
if (!node) {
return null;
}
if (this.isAcceptableNode(node)) {
this.currentNode = node;
return node as T;
}
current = node;
}
}
// Previous node in post-order.
previousPONode(): T | null {
const root = this.root;
let current: Node | null = this.currentNode;
let node: Node | null;
while (true) {
node = current.lastChild;
while (!node && current) {
if (current === root) {
break;
}
node = current.previousSibling;
if (!node) {
current = current.parentNode;
}
}
if (!node) {
return null;
}
if (this.isAcceptableNode(node)) {
this.currentNode = node;
return node as T;
}
current = node;
}
}
}
// ---
export { TreeIterator, SHOW_ELEMENT, SHOW_TEXT, SHOW_ELEMENT_OR_TEXT };