UNPKG

xpath-ts2

Version:

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

331 lines 12.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PathExpr = void 0; const consts_1 = require("./consts"); const step_1 = require("./step"); const types_1 = require("./utils/types"); const xpath_namespace_1 = require("./xpath-namespace"); const xpath_types_1 = require("./xpath-types"); class PathExpr extends xpath_types_1.Expression { static predicateMatches(pred, c) { const res = pred.evaluate(c); return res instanceof xpath_types_1.XNumber ? c.contextPosition === res.numberValue : res.booleanValue; } static applyLocationPath(locationPath, xpc, nodes) { if (!locationPath) { return nodes; } const startNodes = locationPath.absolute ? [PathExpr.getRoot(xpc, nodes)] : nodes; return PathExpr.applySteps(locationPath.steps, xpc, startNodes); } static applyStep(step, xpc, node) { const newNodes = []; xpc.contextNode = node; switch (step.axis) { case step_1.Step.ANCESTOR: { // look at all the ancestor nodes if (xpc.contextNode === xpc.virtualRoot) { break; } let m; if ((0, types_1.isAttribute)(xpc.contextNode)) { m = PathExpr.getOwnerElement(xpc.contextNode); } else { m = xpc.contextNode.parentNode; } while (m != null) { if (step.nodeTest.matches(m, xpc)) { newNodes.push(m); } if (m === xpc.virtualRoot) { break; } m = m.parentNode; } break; } case step_1.Step.ANCESTORORSELF: { // look at all the ancestor nodes and the current node for (let m = xpc.contextNode; m != null; m = (0, types_1.isAttribute)(m) ? PathExpr.getOwnerElement(m) : m.parentNode) { if (step.nodeTest.matches(m, xpc)) { newNodes.push(m); } if (m === xpc.virtualRoot) { break; } } break; } case step_1.Step.ATTRIBUTE: { // look at the attributes const nnm = xpc.contextNode.attributes; if (nnm != null) { for (let k = 0; k < nnm.length; k++) { const m = nnm.item(k); if (step.nodeTest.matches(m, xpc)) { newNodes.push(m); } } } break; } case step_1.Step.CHILD: { // look at all child elements for (let m = xpc.contextNode.firstChild; m != null; m = m.nextSibling) { if (step.nodeTest.matches(m, xpc)) { newNodes.push(m); } } break; } case step_1.Step.DESCENDANT: { // look at all descendant nodes const st = [xpc.contextNode.firstChild]; while (st.length > 0) { for (let m = st.pop(); m != null;) { if (step.nodeTest.matches(m, xpc)) { newNodes.push(m); } if (m.firstChild != null) { st.push(m.nextSibling); m = m.firstChild; } else { m = m.nextSibling; } } } break; } case step_1.Step.DESCENDANTORSELF: { // look at self if (step.nodeTest.matches(xpc.contextNode, xpc)) { newNodes.push(xpc.contextNode); } // look at all descendant nodes const st = [xpc.contextNode.firstChild]; while (st.length > 0) { for (let m = st.pop(); m != null;) { if (step.nodeTest.matches(m, xpc)) { newNodes.push(m); } if (m.firstChild != null) { st.push(m.nextSibling); m = m.firstChild; } else { m = m.nextSibling; } } } break; } case step_1.Step.FOLLOWING: { if (xpc.contextNode === xpc.virtualRoot) { break; } for (let n = xpc.contextNode; n != null; n = n.parentNode) { for (let m = n.nextSibling; m != null; m = m.nextSibling) { if (step.nodeTest.matches(m, xpc)) { newNodes.push(m); } } } break; } case step_1.Step.FOLLOWINGSIBLING: { if (xpc.contextNode === xpc.virtualRoot) { break; } for (let m = xpc.contextNode.nextSibling; m != null; m = m.nextSibling) { if (step.nodeTest.matches(m, xpc)) { newNodes.push(m); } } break; } case step_1.Step.NAMESPACE: { const n = {}; if ((0, types_1.isElement)(xpc.contextNode)) { n.xml = consts_1.XML_NAMESPACE_URI; n.xmlns = consts_1.XMLNS_NAMESPACE_URI; for (let m = xpc.contextNode; m != null && (0, types_1.isElement)(m); m = m.parentNode) { for (let k = 0; k < m.attributes.length; k++) { const attr = m.attributes.item(k); const nm = String(attr.name); if (nm === 'xmlns') { if (n[''] === undefined) { n[''] = attr.value; } } else if (nm.length > 6 && nm.substring(0, 6) === 'xmlns:') { const pre = nm.substring(6, nm.length); if (n[pre] === undefined) { n[pre] = attr.value; } } } } // tslint:disable-next-line:forin for (const pre in n) { const nsn = new xpath_namespace_1.XPathNamespace(pre, n[pre], xpc.contextNode); if (step.nodeTest.matches(nsn, xpc)) { newNodes.push(nsn); } } } break; } case step_1.Step.PARENT: { let m = null; if (xpc.contextNode !== xpc.virtualRoot) { if ((0, types_1.isAttribute)(xpc.contextNode)) { m = PathExpr.getOwnerElement(xpc.contextNode); } else { m = xpc.contextNode.parentNode; } } if (m != null && step.nodeTest.matches(m, xpc)) { newNodes.push(m); } break; } case step_1.Step.PRECEDING: { if (xpc.contextNode === xpc.virtualRoot) { break; } for (let n = xpc.contextNode; n != null; n = n.parentNode) { for (let m = n.previousSibling; m != null; m = m.previousSibling) { if (step.nodeTest.matches(m, xpc)) { newNodes.push(m); } } } break; } case step_1.Step.PRECEDINGSIBLING: { if (xpc.contextNode === xpc.virtualRoot) { break; } for (let m = xpc.contextNode.previousSibling; m != null; m = m.previousSibling) { if (step.nodeTest.matches(m, xpc)) { newNodes.push(m); } } break; } case step_1.Step.SELF: { if (step.nodeTest.matches(xpc.contextNode, xpc)) { newNodes.push(xpc.contextNode); } break; } default: } return newNodes; } static getRoot(xpc, nodes) { const firstNode = nodes[0]; if ((0, types_1.isDocument)(firstNode)) { return firstNode; } if (xpc.virtualRoot) { return xpc.virtualRoot; } const ownerDoc = firstNode.ownerDocument; if (ownerDoc) { return ownerDoc; } // IE 5.5 doesn't have ownerDocument? let n = firstNode; while (n.parentNode != null) { n = n.parentNode; } return n; } static applySteps(steps, xpc, nodes) { return steps.reduce((inNodes, step) => { return inNodes .map((node) => { return PathExpr.applyPredicates(step.predicates, xpc, PathExpr.applyStep(step, xpc, node)); }) .flat(); }, nodes); } static getOwnerElement(n) { if (n.ownerElement) { return n.ownerElement; } return null; } static applyPredicates(predicates, c, nodes) { return predicates.reduce((inNodes, pred) => { const ctx = c.extend({ contextSize: inNodes.length }); return inNodes.filter((node, i) => { return PathExpr.predicateMatches(pred, ctx.extend({ contextNode: node, contextPosition: i + 1 })); }); }, nodes); } static predicateString = (e) => `[${e.toString()}]`; static predicatesString = (es) => es.map(PathExpr.predicateString).join(''); filter; filterPredicates; locationPath; constructor(filter, filterPreds, locpath) { super(); this.filter = filter; this.filterPredicates = filterPreds; this.locationPath = locpath; } applyFilter(c, xpc) { if (!this.filter) { return { nodes: [c.contextNode] }; } const ns = this.filter.evaluate(c); if (!(ns instanceof xpath_types_1.XNodeSet)) { if ((this.filterPredicates != null && this.filterPredicates.length > 0) || this.locationPath != null) { throw new Error('Path expression filter must evaluate to a nodeset if predicates or location path are used'); } return { nonNodes: ns }; } return { nodes: PathExpr.applyPredicates(this.filterPredicates || [], xpc, ns.toUnsortedArray()) }; } evaluate(c) { const xpc = c.clone(); const filterResult = this.applyFilter(c, xpc); if (filterResult.nonNodes !== undefined) { return filterResult.nonNodes; } // filterResult.nodes is defined because nonNodes is not const ns = new xpath_types_1.XNodeSet(); ns.addArray(PathExpr.applyLocationPath(this.locationPath, xpc, filterResult.nodes)); return ns; } toString() { if (this.filter !== undefined) { const filterStr = this.filter.toString(); if (this.filter instanceof xpath_types_1.XString) { return `'${filterStr}'`; } if (this.filterPredicates !== undefined && this.filterPredicates.length) { return `(${filterStr})` + PathExpr.predicatesString(this.filterPredicates); } if (this.locationPath !== undefined) { return filterStr + (this.locationPath.absolute ? '' : '/') + this.locationPath.toString(); } return filterStr; } if (this.locationPath !== undefined) { return this.locationPath.toString(); } else { return '<Empty PathExpr>'; } } } exports.PathExpr = PathExpr; //# sourceMappingURL=path-expr.js.map