UNPKG

xpath-ts2

Version:

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

632 lines 17.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Operators = exports.XPathContext = exports.XNodeSet = exports.XString = exports.XNumber = exports.XBoolean = exports.Expression = void 0; const avl_tree_1 = require("./avl-tree"); const types_1 = require("./utils/types"); // tslint:disable:prefer-for-of // tslint:disable:member-ordering class Expression { toString() { return '<Expression>'; } evaluate(_c) { throw new Error('Could not evaluate expression.'); } get string() { throw new Error('Could not evaluate expression.'); } get number() { throw new Error('Could not evaluate expression.'); } get bool() { throw new Error('Could not evaluate expression.'); } get nodeset() { throw new Error('Could not evaluate expression.'); } get stringValue() { throw new Error('Could not evaluate expression.'); } get numberValue() { throw new Error('Could not evaluate expression.'); } get booleanValue() { throw new Error('Could not evaluate expression.'); } equals(_r) { throw new Error('Could not evaluate expression.'); } notequal(_r) { throw new Error('Could not evaluate expression.'); } lessthan(_r) { throw new Error('Could not evaluate expression.'); } greaterthan(_r) { throw new Error('Could not evaluate expression.'); } lessthanorequal(_r) { throw new Error('Could not evaluate expression.'); } greaterthanorequal(_r) { throw new Error('Could not evaluate expression.'); } } exports.Expression = Expression; class XBoolean extends Expression { static TRUE = new XBoolean(true); static FALSE = new XBoolean(false); b; constructor(b) { super(); this.b = Boolean(b); } toString() { return this.b.toString(); } evaluate(_c) { return this; } get string() { return new XString(this.b); } get number() { return new XNumber(this.b); } get bool() { return this; } get nodeset() { throw new Error('Cannot convert boolean to nodeset'); } get stringValue() { return this.string.stringValue; } get numberValue() { return this.number.numberValue; } get booleanValue() { return this.b; } not() { return new XBoolean(!this.b); } equals(r) { if (r instanceof XString || r instanceof XNumber) { return this.equals(r.bool); } else if (r instanceof XNodeSet) { return r.compareWithBoolean(this, Operators.equals); } else if (r instanceof XBoolean) { return new XBoolean(this.b === r.b); } else { throw new Error('Unsupported type'); } } notequal(r) { if (r instanceof XString || r instanceof XNumber) { return this.notequal(r.bool); } else if (r instanceof XNodeSet) { return r.compareWithBoolean(this, Operators.notequal); } else if (r instanceof XBoolean) { return new XBoolean(this.b !== r.b); } else { throw new Error('Unsupported type'); } } lessthan(r) { return this.number.lessthan(r); } greaterthan(r) { return this.number.greaterthan(r); } lessthanorequal(r) { return this.number.lessthanorequal(r); } greaterthanorequal(r) { return this.number.greaterthanorequal(r); } } exports.XBoolean = XBoolean; class XNumber extends Expression { num; constructor(n) { super(); this.num = typeof n === 'string' ? this.parse(n) : Number(n); } get numberFormat() { return /^\s*-?[0-9]*\.?[0-9]+\s*$/; } parse(s) { // XPath representation of numbers is more restrictive than what Number() or parseFloat() allow return this.numberFormat.test(s) ? parseFloat(s) : Number.NaN; } toString() { const strValue = this.num.toString(); if (strValue.indexOf('e-') !== -1) { return padSmallNumber(strValue); } if (strValue.indexOf('e') !== -1) { return padLargeNumber(strValue); } return strValue; } evaluate(_c) { return this; } get string() { return new XString(this.toString()); } get number() { return this; } get bool() { return new XBoolean(this.num); } get nodeset() { throw new Error('Cannot convert string to nodeset'); } get stringValue() { return this.string.stringValue; } get numberValue() { return this.num; } get booleanValue() { return this.bool.booleanValue; } negate() { return new XNumber(-this.num); } equals(r) { if (r instanceof XBoolean) { return this.bool.equals(r); } else if (r instanceof XString) { return this.string.equals(r); } else if (r instanceof XNodeSet) { return r.compareWithNumber(this, Operators.equals); } else if (r instanceof XNumber) { return new XBoolean(this.num === r.num); } else { throw new Error('Unsupported type'); } } notequal(r) { if (r instanceof XBoolean) { return this.bool.notequal(r); } else if (r instanceof XString) { return this.notequal(r.number); } else if (r instanceof XNodeSet) { return r.compareWithNumber(this, Operators.notequal); } else if (r instanceof XNumber) { return new XBoolean(this.num !== r.num); } else { throw new Error('Unsupported type'); } } lessthan(r) { if (r instanceof XNodeSet) { return r.compareWithNumber(this, Operators.greaterthan); } else if (r instanceof XBoolean || r instanceof XString) { return this.lessthan(r.number); } else if (r instanceof XNumber) { return new XBoolean(this.num < r.num); } else { throw new Error('Unsupported type'); } } greaterthan(r) { if (r instanceof XNodeSet) { return r.compareWithNumber(this, Operators.lessthan); } else if (r instanceof XBoolean || r instanceof XString) { return this.greaterthan(r.number); } else if (r instanceof XNumber) { return new XBoolean(this.num > r.num); } else { throw new Error('Unsupported type'); } } lessthanorequal(r) { if (r instanceof XNodeSet) { return r.compareWithNumber(this, Operators.greaterthanorequal); } else if (r instanceof XBoolean || r instanceof XString) { return this.lessthanorequal(r.number); } else if (r instanceof XNumber) { return new XBoolean(this.num <= r.num); } else { throw new Error('Unsupported type'); } } greaterthanorequal(r) { if (r instanceof XNodeSet) { return r.compareWithNumber(this, Operators.lessthanorequal); } else if (r instanceof XBoolean || r instanceof XString) { return this.greaterthanorequal(r.number); } else if (r instanceof XNumber) { return new XBoolean(this.num >= r.num); } else { throw new Error('Unsupported type'); } } plus(r) { return new XNumber(this.num + r.num); } minus(r) { return new XNumber(this.num - r.num); } multiply(r) { return new XNumber(this.num * r.num); } div(r) { return new XNumber(this.num / r.num); } mod(r) { return new XNumber(this.num % r.num); } } exports.XNumber = XNumber; function padSmallNumber(numberStr) { const parts = numberStr.split('e-'); let base = parts[0].replace('.', ''); const exponent = Number(parts[1]); for (let i = 0; i < exponent - 1; i += 1) { base = '0' + base; } return '0.' + base; } function padLargeNumber(numberStr) { const parts = numberStr.split('e'); let base = parts[0].replace('.', ''); const exponent = Number(parts[1]); const zerosToAppend = exponent + 1 - base.length; for (let i = 0; i < zerosToAppend; i += 1) { base += '0'; } return base; } class XString extends Expression { str; constructor(s) { super(); this.str = String(s); } toString() { return this.str; } evaluate(_c) { return this; } get string() { return this; } get number() { return new XNumber(this.str); } get bool() { return new XBoolean(this.str); } get nodeset() { throw new Error('Cannot convert string to nodeset'); } get stringValue() { return this.str; } get numberValue() { return this.number.numberValue; } get booleanValue() { return this.bool.booleanValue; } equals(r) { if (r instanceof XBoolean) { return this.bool.equals(r); } else if (r instanceof XNumber) { return this.number.equals(r); } else if (r instanceof XNodeSet) { return r.compareWithString(this, Operators.equals); } else if (r instanceof XString) { return new XBoolean(this.str === r.str); } else { throw new Error('Unsupported type'); } } notequal(r) { if (r instanceof XBoolean) { return this.bool.notequal(r); } else if (r instanceof XNumber) { return this.number.notequal(r); } else if (r instanceof XNodeSet) { return r.compareWithString(this, Operators.notequal); } else if (r instanceof XString) { return new XBoolean(this.str !== r.str); } else { throw new Error('Unsupported type'); } } lessthan(r) { return this.number.lessthan(r); } greaterthan(r) { return this.number.greaterthan(r); } lessthanorequal(r) { return this.number.lessthanorequal(r); } greaterthanorequal(r) { return this.number.greaterthanorequal(r); } } exports.XString = XString; class XNodeSet extends Expression { static compareWith(o) { return function (r) { if (r instanceof XString) { return this.compareWithString(r, o); } else if (r instanceof XNumber) { return this.compareWithNumber(r, o); } else if (r instanceof XBoolean) { return this.compareWithBoolean(r, o); } else if (r instanceof XNodeSet) { return this.compareWithNodeSet(r, o); } else { throw new Error('Unsupported type'); } }; } tree; nodes; size; constructor() { super(); this.tree = null; this.nodes = []; this.size = 0; } toString() { const p = this.first(); if (p == null) { return ''; } return this.stringForNode(p) || ''; } evaluate(_c) { return this; } get string() { return new XString(this.toString()); } get stringValue() { return this.toString(); } get number() { return new XNumber(this.string); } get numberValue() { return Number(this.string); } get bool() { return new XBoolean(this.booleanValue); } get booleanValue() { return !!this.size; } get nodeset() { return this; } stringForNode(n) { if ((0, types_1.isDocument)(n) || (0, types_1.isElement)(n) || (0, types_1.isFragment)(n)) { return this.stringForContainerNode(n); } else if ((0, types_1.isAttribute)(n)) { return n.value || n.nodeValue || ''; } else if ((0, types_1.isNamespaceNode)(n)) { return n.value; } return n.nodeValue || ''; } stringForContainerNode(n) { let s = ''; for (let n2 = n.firstChild; n2 != null; n2 = n2.nextSibling) { if ((0, types_1.isElement)(n2) || (0, types_1.isText)(n2) || (0, types_1.isCData)(n2) || (0, types_1.isDocument)(n2) || (0, types_1.isFragment)(n2)) { s += this.stringForNode(n2); } } return s; } buildTree() { if (!this.tree && this.nodes.length) { this.tree = new avl_tree_1.AVLTree(this.nodes[0]); for (let i = 1; i < this.nodes.length; i += 1) { this.tree.add(this.nodes[i]); } } return this.tree; } first() { let p = this.buildTree(); if (p == null) { return null; } while (p.left != null) { p = p.left; } return p.node; } add(n) { for (let i = 0; i < this.nodes.length; i += 1) { if (n === this.nodes[i]) { return; } } this.tree = null; this.nodes.push(n); this.size += 1; } addArray(ns) { const self = this; ns.forEach((x) => self.add(x)); } /** * Returns an array of the node set's contents in document order */ toArray() { const a = []; this.toArrayRec(this.buildTree(), a); return a; } toArrayRec(t, a) { if (t != null) { this.toArrayRec(t.left, a); a.push(t.node); this.toArrayRec(t.right, a); } } /** * Returns an array of the node set's contents in arbitrary order */ toUnsortedArray() { return this.nodes.slice(); } compareWithString(r, o) { const a = this.toUnsortedArray(); for (let i = 0; i < a.length; i++) { const n = a[i]; const l = new XString(this.stringForNode(n)); const res = o(l, r); if (res.booleanValue) { return res; } } return new XBoolean(false); } compareWithNumber(r, o) { const a = this.toUnsortedArray(); for (let i = 0; i < a.length; i++) { const n = a[i]; const l = new XNumber(this.stringForNode(n)); const res = o(l, r); if (res.booleanValue) { return res; } } return new XBoolean(false); } compareWithBoolean(r, o) { return o(this.bool, r); } compareWithNodeSet(r, o) { const arr = this.toUnsortedArray(); const oInvert = (lop, rop) => { return o(rop, lop); }; for (let i = 0; i < arr.length; i++) { const l = new XString(this.stringForNode(arr[i])); const res = r.compareWithString(l, oInvert); if (res.booleanValue) { return res; } } return new XBoolean(false); } equals = XNodeSet.compareWith(Operators.equals); notequal = XNodeSet.compareWith(Operators.notequal); lessthan = XNodeSet.compareWith(Operators.lessthan); greaterthan = XNodeSet.compareWith(Operators.greaterthan); lessthanorequal = XNodeSet.compareWith(Operators.lessthanorequal); greaterthanorequal = XNodeSet.compareWith(Operators.greaterthanorequal); union(r) { const ns = new XNodeSet(); ns.addArray(this.toUnsortedArray()); ns.addArray(r.toUnsortedArray()); return ns; } } exports.XNodeSet = XNodeSet; class XPathContext { variableResolver; namespaceResolver; functionResolver; contextNode = undefined; virtualRoot; expressionContextNode = undefined; isHtml = undefined; contextSize; contextPosition; allowAnyNamespaceForNoPrefix = undefined; caseInsensitive = undefined; constructor(vr, nr, fr) { this.variableResolver = vr; this.namespaceResolver = nr; this.functionResolver = fr; this.virtualRoot = null; this.contextSize = 0; this.contextPosition = 0; } clone() { return Object.assign(new XPathContext(this.variableResolver, this.namespaceResolver, this.functionResolver), this); } extend(newProps) { return Object.assign(new XPathContext(this.variableResolver, this.namespaceResolver, this.functionResolver), this, newProps); } } exports.XPathContext = XPathContext; class Operators { static equals(l, r) { return l.equals(r); } static notequal(l, r) { return l.notequal(r); } static lessthan(l, r) { return l.lessthan(r); } static greaterthan(l, r) { return l.greaterthan(r); } static lessthanorequal(l, r) { return l.lessthanorequal(r); } static greaterthanorequal(l, r) { return l.greaterthanorequal(r); } } exports.Operators = Operators; //# sourceMappingURL=xpath-types.js.map