UNPKG

xpath-ts2

Version:

DOM 3 and 4 XPath 1.0 implementation for browser and Node.js environment with support for typescript 5.

227 lines (198 loc) 5.25 kB
import { isAttribute } from './utils/types'; // tslint:disable:no-bitwise export class AVLTree { left: AVLTree | null; right: AVLTree | null; node: Node; depth: number; constructor(n: Node) { this.left = null; this.right = null; this.node = n; this.depth = 1; } balance() { const ldepth = this.left == null ? 0 : this.left.depth; const rdepth = this.right == null ? 0 : this.right.depth; if (ldepth > rdepth + 1) { // LR or LL rotation const lldepth = this.left!.left == null ? 0 : this.left!.left!.depth; const lrdepth = this.left!.right == null ? 0 : this.left!.right!.depth; if (lldepth < lrdepth) { // LR rotation consists of a RR rotation of the left child this.left!.rotateRR(); // plus a LL rotation of this node, which happens anyway } this.rotateLL(); } else if (ldepth + 1 < rdepth) { // RR or RL rorarion const rrdepth = this.right!.right == null ? 0 : this.right!.right!.depth; const rldepth = this.right!.left == null ? 0 : this.right!.left!.depth; if (rldepth > rrdepth) { // RR rotation consists of a LL rotation of the right child this.right!.rotateLL(); // plus a RR rotation of this node, which happens anyway } this.rotateRR(); } } rotateLL() { // the left side is too long => rotate from the left (_not_ leftwards) const nodeBefore = this.node; const rightBefore = this.right; this.node = this.left!.node; this.right = this.left; this.left = this.left!.left; this.right!.left = this.right!.right; this.right!.right = rightBefore; this.right!.node = nodeBefore; this.right!.updateInNewLocation(); this.updateInNewLocation(); } rotateRR() { // the right side is too long => rotate from the right (_not_ rightwards) const nodeBefore = this.node; const leftBefore = this.left; this.node = this.right!.node; this.left = this.right!; this.right = this.right!.right; this.left.right = this.left.left; this.left.left = leftBefore; this.left.node = nodeBefore; this.left.updateInNewLocation(); this.updateInNewLocation(); } updateInNewLocation() { this.getDepthFromChildren(); } getDepthFromChildren() { this.depth = this.node == null ? 0 : 1; if (this.left != null) { this.depth = this.left.depth + 1; } if (this.right != null && this.depth <= this.right.depth) { this.depth = this.right.depth + 1; } } add(n: Node): boolean { if (n === this.node) { return false; } const o = nodeOrder(n, this.node); let ret = false; if (o === -1) { if (this.left == null) { this.left = new AVLTree(n); ret = true; } else { ret = this.left.add(n); if (ret) { this.balance(); } } } else if (o === 1) { if (this.right == null) { this.right = new AVLTree(n); ret = true; } else { ret = this.right.add(n); if (ret) { this.balance(); } } } if (ret) { this.getDepthFromChildren(); } return ret; } } function nodeOrder(n1: Node, n2: Node) { if (n1 === n2) { return 0; } if (n1.compareDocumentPosition !== undefined && n2.compareDocumentPosition !== undefined) { try { const cpos = n1.compareDocumentPosition(n2); if (cpos & 0x01) { // not in the same document; return an arbitrary result (is there a better way to do this) return 1; } if (cpos & 0x0a) { // n2 precedes or contains n1 return 1; } if (cpos & 0x14) { // n2 follows or is contained by n1 return -1; } return 0; } catch (_e) { // if compareDocumentPosition exists but is not supported ignore error } } let d1 = 0; let d2 = 0; for (let m1: Node | null = n1; m1 != null; m1 = parentNode(m1)) { d1++; } for (let m2: Node | null = n2; m2 != null; m2 = parentNode(m2)) { d2++; } // step up to same depth if (d1 > d2) { while (d1 > d2) { n1 = parentNode(n1)!; d1--; } if (n1 === n2) { return 1; } } else if (d2 > d1) { while (d2 > d1) { n2 = parentNode(n2)!; d2--; } if (n1 === n2) { return -1; } } let n1Par = parentNode(n1); let n2Par = parentNode(n2); // find common parent while (n1Par !== n2Par) { n1 = n1Par!; n2 = n2Par!; n1Par = parentNode(n1); n2Par = parentNode(n2); } const n1isAttr = isAttribute(n1); const n2isAttr = isAttribute(n2); if (n1isAttr && !n2isAttr) { return -1; } if (!n1isAttr && n2isAttr) { return 1; } if (n1Par) { const cn = n1isAttr ? (n1Par as Element).attributes : n1Par.childNodes; const len = cn.length; for (let i = 0; i < len; i += 1) { const n = cn[i]; if (n === n1) { return -1; } if (n === n2) { return 1; } } } throw new Error('Unexpected: could not determine node order'); } function parentNode(n: Node) { if (isAttribute(n)) { return n.ownerElement; } else { return n.parentNode; } }