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
JavaScript
"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