xslt-processor 3.4.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.mts +817 -0
- package/index.d.ts +817 -4
- package/index.js +4467 -12
- package/index.js.map +1 -1
- package/index.mjs +4431 -0
- package/index.mjs.map +1 -0
- package/package.json +23 -18
- package/umd/xslt-processor.global.js +9 -0
- package/umd/xslt-processor.global.js.map +1 -0
- package/constants.d.ts +0 -12
- package/constants.js +0 -17
- package/constants.js.map +0 -1
- package/dom/functions.d.ts +0 -14
- package/dom/functions.js +0 -57
- package/dom/functions.js.map +0 -1
- package/dom/index.d.ts +0 -7
- package/dom/index.js +0 -24
- package/dom/index.js.map +0 -1
- package/dom/util.d.ts +0 -7
- package/dom/util.js +0 -43
- package/dom/util.js.map +0 -1
- package/dom/xbrowser-node.d.ts +0 -9
- package/dom/xbrowser-node.js +0 -32
- package/dom/xbrowser-node.js.map +0 -1
- package/dom/xdocument.d.ts +0 -16
- package/dom/xdocument.js +0 -74
- package/dom/xdocument.js.map +0 -1
- package/dom/xml-functions.d.ts +0 -73
- package/dom/xml-functions.js +0 -402
- package/dom/xml-functions.js.map +0 -1
- package/dom/xml-output-options.d.ts +0 -6
- package/dom/xml-output-options.js +0 -3
- package/dom/xml-output-options.js.map +0 -1
- package/dom/xml-parser.d.ts +0 -47
- package/dom/xml-parser.js +0 -308
- package/dom/xml-parser.js.map +0 -1
- package/dom/xmltoken.d.ts +0 -12
- package/dom/xmltoken.js +0 -102
- package/dom/xmltoken.js.map +0 -1
- package/dom/xnode.d.ts +0 -73
- package/dom/xnode.js +0 -451
- package/dom/xnode.js.map +0 -1
- package/test-without-jest.d.ts +0 -1
- package/test-without-jest.js +0 -63
- package/test-without-jest.js.map +0 -1
- package/umd/constants.d.ts +0 -12
- package/umd/dom/functions.d.ts +0 -14
- package/umd/dom/index.d.ts +0 -7
- package/umd/dom/util.d.ts +0 -7
- package/umd/dom/xbrowser-node.d.ts +0 -9
- package/umd/dom/xdocument.d.ts +0 -16
- package/umd/dom/xml-functions.d.ts +0 -73
- package/umd/dom/xml-output-options.d.ts +0 -6
- package/umd/dom/xml-parser.d.ts +0 -47
- package/umd/dom/xmltoken.d.ts +0 -12
- package/umd/dom/xnode.d.ts +0 -73
- package/umd/index.d.ts +0 -4
- package/umd/test-without-jest.d.ts +0 -1
- package/umd/xpath/common-function.d.ts +0 -8
- package/umd/xpath/expr-context.d.ts +0 -116
- package/umd/xpath/expressions/binary-expr.d.ts +0 -11
- package/umd/xpath/expressions/expression.d.ts +0 -4
- package/umd/xpath/expressions/filter-expr.d.ts +0 -9
- package/umd/xpath/expressions/function-call-expr.d.ts +0 -12
- package/umd/xpath/expressions/index.d.ts +0 -13
- package/umd/xpath/expressions/literal-expr.d.ts +0 -7
- package/umd/xpath/expressions/location-expr.d.ts +0 -15
- package/umd/xpath/expressions/number-expr.d.ts +0 -7
- package/umd/xpath/expressions/path-expr.d.ts +0 -9
- package/umd/xpath/expressions/predicate-expr.d.ts +0 -8
- package/umd/xpath/expressions/step-expr.d.ts +0 -25
- package/umd/xpath/expressions/token-expr.d.ts +0 -7
- package/umd/xpath/expressions/unary-minus-expr.d.ts +0 -8
- package/umd/xpath/expressions/union-expr.d.ts +0 -9
- package/umd/xpath/expressions/variable-expr.d.ts +0 -7
- package/umd/xpath/functions/index.d.ts +0 -4
- package/umd/xpath/functions/internal-functions.d.ts +0 -2
- package/umd/xpath/functions/non-standard.d.ts +0 -12
- package/umd/xpath/functions/standard-20.d.ts +0 -5
- package/umd/xpath/functions/standard.d.ts +0 -40
- package/umd/xpath/functions/xslt-specific.d.ts +0 -3
- package/umd/xpath/grammar-rule-candidate.d.ts +0 -8
- package/umd/xpath/index.d.ts +0 -3
- package/umd/xpath/match-resolver.d.ts +0 -55
- package/umd/xpath/node-tests/index.d.ts +0 -8
- package/umd/xpath/node-tests/node-test-any.d.ts +0 -6
- package/umd/xpath/node-tests/node-test-comment.d.ts +0 -6
- package/umd/xpath/node-tests/node-test-element-or-attribute.d.ts +0 -6
- package/umd/xpath/node-tests/node-test-name.d.ts +0 -10
- package/umd/xpath/node-tests/node-test-nc.d.ts +0 -9
- package/umd/xpath/node-tests/node-test-pi.d.ts +0 -8
- package/umd/xpath/node-tests/node-test-text.d.ts +0 -6
- package/umd/xpath/node-tests/node-test.d.ts +0 -5
- package/umd/xpath/tokens.d.ts +0 -62
- package/umd/xpath/values/boolean-value.d.ts +0 -11
- package/umd/xpath/values/index.d.ts +0 -5
- package/umd/xpath/values/node-set-value.d.ts +0 -11
- package/umd/xpath/values/node-value.d.ts +0 -7
- package/umd/xpath/values/number-value.d.ts +0 -11
- package/umd/xpath/values/string-value.d.ts +0 -11
- package/umd/xpath/xpath-grammar-rules.d.ts +0 -68
- package/umd/xpath/xpath-token-rule.d.ts +0 -7
- package/umd/xpath/xpath.d.ts +0 -174
- package/umd/xpathdebug.d.ts +0 -2
- package/umd/xslt/index.d.ts +0 -3
- package/umd/xslt/xslt-decimal-format-settings.d.ts +0 -28
- package/umd/xslt/xslt-options.d.ts +0 -7
- package/umd/xslt/xslt-parameter.d.ts +0 -5
- package/umd/xslt/xslt.d.ts +0 -275
- package/umd/xslt-processor.js +0 -2
- package/umd/xslt-processor.js.map +0 -1
- package/xpath/common-function.d.ts +0 -8
- package/xpath/common-function.js +0 -32
- package/xpath/common-function.js.map +0 -1
- package/xpath/expr-context.d.ts +0 -116
- package/xpath/expr-context.js +0 -191
- package/xpath/expr-context.js.map +0 -1
- package/xpath/expressions/binary-expr.d.ts +0 -11
- package/xpath/expressions/binary-expr.js +0 -166
- package/xpath/expressions/binary-expr.js.map +0 -1
- package/xpath/expressions/expression.d.ts +0 -4
- package/xpath/expressions/expression.js +0 -10
- package/xpath/expressions/expression.js.map +0 -1
- package/xpath/expressions/filter-expr.d.ts +0 -9
- package/xpath/expressions/filter-expr.js +0 -53
- package/xpath/expressions/filter-expr.js.map +0 -1
- package/xpath/expressions/function-call-expr.d.ts +0 -12
- package/xpath/expressions/function-call-expr.js +0 -97
- package/xpath/expressions/function-call-expr.js.map +0 -1
- package/xpath/expressions/index.d.ts +0 -13
- package/xpath/expressions/index.js +0 -30
- package/xpath/expressions/index.js.map +0 -1
- package/xpath/expressions/literal-expr.d.ts +0 -7
- package/xpath/expressions/literal-expr.js +0 -34
- package/xpath/expressions/literal-expr.js.map +0 -1
- package/xpath/expressions/location-expr.d.ts +0 -15
- package/xpath/expressions/location-expr.js +0 -99
- package/xpath/expressions/location-expr.js.map +0 -1
- package/xpath/expressions/number-expr.d.ts +0 -7
- package/xpath/expressions/number-expr.js +0 -34
- package/xpath/expressions/number-expr.js.map +0 -1
- package/xpath/expressions/path-expr.d.ts +0 -9
- package/xpath/expressions/path-expr.js +0 -52
- package/xpath/expressions/path-expr.js.map +0 -1
- package/xpath/expressions/predicate-expr.d.ts +0 -8
- package/xpath/expressions/predicate-expr.js +0 -41
- package/xpath/expressions/predicate-expr.js.map +0 -1
- package/xpath/expressions/step-expr.d.ts +0 -25
- package/xpath/expressions/step-expr.js +0 -281
- package/xpath/expressions/step-expr.js.map +0 -1
- package/xpath/expressions/token-expr.d.ts +0 -7
- package/xpath/expressions/token-expr.js +0 -34
- package/xpath/expressions/token-expr.js.map +0 -1
- package/xpath/expressions/unary-minus-expr.d.ts +0 -8
- package/xpath/expressions/unary-minus-expr.js +0 -34
- package/xpath/expressions/unary-minus-expr.js.map +0 -1
- package/xpath/expressions/union-expr.d.ts +0 -9
- package/xpath/expressions/union-expr.js +0 -51
- package/xpath/expressions/union-expr.js.map +0 -1
- package/xpath/expressions/variable-expr.d.ts +0 -7
- package/xpath/expressions/variable-expr.js +0 -33
- package/xpath/expressions/variable-expr.js.map +0 -1
- package/xpath/functions/index.d.ts +0 -4
- package/xpath/functions/index.js +0 -21
- package/xpath/functions/index.js.map +0 -1
- package/xpath/functions/internal-functions.d.ts +0 -2
- package/xpath/functions/internal-functions.js +0 -22
- package/xpath/functions/internal-functions.js.map +0 -1
- package/xpath/functions/non-standard.d.ts +0 -12
- package/xpath/functions/non-standard.js +0 -45
- package/xpath/functions/non-standard.js.map +0 -1
- package/xpath/functions/standard-20.d.ts +0 -5
- package/xpath/functions/standard-20.js +0 -26
- package/xpath/functions/standard-20.js.map +0 -1
- package/xpath/functions/standard.d.ts +0 -40
- package/xpath/functions/standard.js +0 -442
- package/xpath/functions/standard.js.map +0 -1
- package/xpath/functions/xslt-specific.d.ts +0 -3
- package/xpath/functions/xslt-specific.js +0 -14
- package/xpath/functions/xslt-specific.js.map +0 -1
- package/xpath/grammar-rule-candidate.d.ts +0 -8
- package/xpath/grammar-rule-candidate.js +0 -3
- package/xpath/grammar-rule-candidate.js.map +0 -1
- package/xpath/index.d.ts +0 -3
- package/xpath/index.js +0 -20
- package/xpath/index.js.map +0 -1
- package/xpath/match-resolver.d.ts +0 -55
- package/xpath/match-resolver.js +0 -137
- package/xpath/match-resolver.js.map +0 -1
- package/xpath/node-tests/index.d.ts +0 -8
- package/xpath/node-tests/index.js +0 -18
- package/xpath/node-tests/index.js.map +0 -1
- package/xpath/node-tests/node-test-any.d.ts +0 -6
- package/xpath/node-tests/node-test-any.js +0 -15
- package/xpath/node-tests/node-test-any.js.map +0 -1
- package/xpath/node-tests/node-test-comment.d.ts +0 -6
- package/xpath/node-tests/node-test-comment.js +0 -15
- package/xpath/node-tests/node-test-comment.js.map +0 -1
- package/xpath/node-tests/node-test-element-or-attribute.d.ts +0 -6
- package/xpath/node-tests/node-test-element-or-attribute.js +0 -16
- package/xpath/node-tests/node-test-element-or-attribute.js.map +0 -1
- package/xpath/node-tests/node-test-name.d.ts +0 -10
- package/xpath/node-tests/node-test-name.js +0 -39
- package/xpath/node-tests/node-test-name.js.map +0 -1
- package/xpath/node-tests/node-test-nc.d.ts +0 -9
- package/xpath/node-tests/node-test-nc.js +0 -17
- package/xpath/node-tests/node-test-nc.js.map +0 -1
- package/xpath/node-tests/node-test-pi.d.ts +0 -8
- package/xpath/node-tests/node-test-pi.js +0 -17
- package/xpath/node-tests/node-test-pi.js.map +0 -1
- package/xpath/node-tests/node-test-text.d.ts +0 -6
- package/xpath/node-tests/node-test-text.js +0 -15
- package/xpath/node-tests/node-test-text.js.map +0 -1
- package/xpath/node-tests/node-test.d.ts +0 -5
- package/xpath/node-tests/node-test.js +0 -3
- package/xpath/node-tests/node-test.js.map +0 -1
- package/xpath/tokens.d.ts +0 -62
- package/xpath/tokens.js +0 -301
- package/xpath/tokens.js.map +0 -1
- package/xpath/values/boolean-value.d.ts +0 -11
- package/xpath/values/boolean-value.js +0 -24
- package/xpath/values/boolean-value.js.map +0 -1
- package/xpath/values/index.d.ts +0 -5
- package/xpath/values/index.js +0 -47
- package/xpath/values/index.js.map +0 -1
- package/xpath/values/node-set-value.d.ts +0 -11
- package/xpath/values/node-set-value.js +0 -28
- package/xpath/values/node-set-value.js.map +0 -1
- package/xpath/values/node-value.d.ts +0 -7
- package/xpath/values/node-value.js +0 -3
- package/xpath/values/node-value.js.map +0 -1
- package/xpath/values/number-value.d.ts +0 -11
- package/xpath/values/number-value.js +0 -24
- package/xpath/values/number-value.js.map +0 -1
- package/xpath/values/string-value.d.ts +0 -11
- package/xpath/values/string-value.js +0 -24
- package/xpath/values/string-value.js.map +0 -1
- package/xpath/xpath-grammar-rules.d.ts +0 -68
- package/xpath/xpath-grammar-rules.js +0 -75
- package/xpath/xpath-grammar-rules.js.map +0 -1
- package/xpath/xpath-token-rule.d.ts +0 -7
- package/xpath/xpath-token-rule.js +0 -3
- package/xpath/xpath-token-rule.js.map +0 -1
- package/xpath/xpath.d.ts +0 -174
- package/xpath/xpath.js +0 -912
- package/xpath/xpath.js.map +0 -1
- package/xpathdebug.d.ts +0 -2
- package/xpathdebug.js +0 -188
- package/xpathdebug.js.map +0 -1
- package/xslt/index.d.ts +0 -3
- package/xslt/index.js +0 -20
- package/xslt/index.js.map +0 -1
- package/xslt/xslt-decimal-format-settings.d.ts +0 -28
- package/xslt/xslt-decimal-format-settings.js +0 -3
- package/xslt/xslt-decimal-format-settings.js.map +0 -1
- package/xslt/xslt-options.d.ts +0 -7
- package/xslt/xslt-options.js +0 -3
- package/xslt/xslt-options.js.map +0 -1
- package/xslt/xslt-parameter.d.ts +0 -5
- package/xslt/xslt-parameter.js +0 -3
- package/xslt/xslt-parameter.js.map +0 -1
- package/xslt/xslt.d.ts +0 -275
- package/xslt/xslt.js +0 -1329
- package/xslt/xslt.js.map +0 -1
package/index.js
CHANGED
|
@@ -1,13 +1,4468 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty
|
|
3
|
-
|
|
4
|
-
var
|
|
5
|
-
|
|
6
|
-
var
|
|
7
|
-
|
|
8
|
-
var
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
var
|
|
12
|
-
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __defProps = Object.defineProperties;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
8
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
9
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
10
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
11
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
12
|
+
var __spreadValues = (a, b) => {
|
|
13
|
+
for (var prop in b || (b = {}))
|
|
14
|
+
if (__hasOwnProp.call(b, prop))
|
|
15
|
+
__defNormalProp(a, prop, b[prop]);
|
|
16
|
+
if (__getOwnPropSymbols)
|
|
17
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
18
|
+
if (__propIsEnum.call(b, prop))
|
|
19
|
+
__defNormalProp(a, prop, b[prop]);
|
|
20
|
+
}
|
|
21
|
+
return a;
|
|
22
|
+
};
|
|
23
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
24
|
+
var __export = (target, all) => {
|
|
25
|
+
for (var name in all)
|
|
26
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
27
|
+
};
|
|
28
|
+
var __copyProps = (to, from, except, desc) => {
|
|
29
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
30
|
+
for (let key of __getOwnPropNames(from))
|
|
31
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
32
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
33
|
+
}
|
|
34
|
+
return to;
|
|
35
|
+
};
|
|
36
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
37
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
38
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
39
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
40
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
41
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
42
|
+
mod
|
|
43
|
+
));
|
|
44
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
45
|
+
var __async = (__this, __arguments, generator) => {
|
|
46
|
+
return new Promise((resolve, reject) => {
|
|
47
|
+
var fulfilled = (value) => {
|
|
48
|
+
try {
|
|
49
|
+
step(generator.next(value));
|
|
50
|
+
} catch (e) {
|
|
51
|
+
reject(e);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
var rejected = (value) => {
|
|
55
|
+
try {
|
|
56
|
+
step(generator.throw(value));
|
|
57
|
+
} catch (e) {
|
|
58
|
+
reject(e);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
62
|
+
step((generator = generator.apply(__this, __arguments)).next());
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// src/index.ts
|
|
67
|
+
var index_exports = {};
|
|
68
|
+
__export(index_exports, {
|
|
69
|
+
ExprContext: () => ExprContext,
|
|
70
|
+
XPath: () => XPath,
|
|
71
|
+
XmlParser: () => XmlParser,
|
|
72
|
+
Xslt: () => Xslt,
|
|
73
|
+
xmlEscapeText: () => xmlEscapeText
|
|
74
|
+
});
|
|
75
|
+
module.exports = __toCommonJS(index_exports);
|
|
76
|
+
|
|
77
|
+
// src/xpath/lib/src/lexer/token.ts
|
|
78
|
+
var XPathToken = class {
|
|
79
|
+
constructor(type, lexeme) {
|
|
80
|
+
this.type = type;
|
|
81
|
+
this.lexeme = lexeme;
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// src/xpath/lib/src/lexer/lexer.ts
|
|
86
|
+
var RESERVED_WORDS = {
|
|
87
|
+
// Location axes (XPath 1.0 complete list)
|
|
88
|
+
"ancestor": { type: "LOCATION", value: "ancestor" },
|
|
89
|
+
"ancestor-or-self": { type: "LOCATION", value: "ancestor-or-self" },
|
|
90
|
+
"attribute": { type: "LOCATION", value: "attribute" },
|
|
91
|
+
"child": { type: "LOCATION", value: "child" },
|
|
92
|
+
"descendant": { type: "LOCATION", value: "descendant" },
|
|
93
|
+
"descendant-or-self": { type: "LOCATION", value: "descendant-or-self" },
|
|
94
|
+
"following": { type: "LOCATION", value: "following" },
|
|
95
|
+
"following-sibling": { type: "LOCATION", value: "following-sibling" },
|
|
96
|
+
"namespace": { type: "LOCATION", value: "namespace" },
|
|
97
|
+
"parent": { type: "LOCATION", value: "parent" },
|
|
98
|
+
"preceding": { type: "LOCATION", value: "preceding" },
|
|
99
|
+
"preceding-sibling": { type: "LOCATION", value: "preceding-sibling" },
|
|
100
|
+
"self": { type: "LOCATION", value: "self" },
|
|
101
|
+
// Node type tests
|
|
102
|
+
"node": { type: "NODE_TYPE", value: "node" },
|
|
103
|
+
"text": { type: "NODE_TYPE", value: "text" },
|
|
104
|
+
"comment": { type: "NODE_TYPE", value: "comment" },
|
|
105
|
+
"processing-instruction": { type: "NODE_TYPE", value: "processing-instruction" },
|
|
106
|
+
// Operators
|
|
107
|
+
"and": { type: "OPERATOR", value: "and" },
|
|
108
|
+
"or": { type: "OPERATOR", value: "or" },
|
|
109
|
+
"div": { type: "OPERATOR", value: "div" },
|
|
110
|
+
"mod": { type: "OPERATOR", value: "mod" },
|
|
111
|
+
// Node set functions
|
|
112
|
+
"last": { type: "FUNCTION", value: "last" },
|
|
113
|
+
"position": { type: "FUNCTION", value: "position" },
|
|
114
|
+
"count": { type: "FUNCTION", value: "count" },
|
|
115
|
+
"id": { type: "FUNCTION", value: "id" },
|
|
116
|
+
"local-name": { type: "FUNCTION", value: "local-name" },
|
|
117
|
+
"namespace-uri": { type: "FUNCTION", value: "namespace-uri" },
|
|
118
|
+
"name": { type: "FUNCTION", value: "name" },
|
|
119
|
+
// String functions
|
|
120
|
+
"string": { type: "FUNCTION", value: "string" },
|
|
121
|
+
"concat": { type: "FUNCTION", value: "concat" },
|
|
122
|
+
"starts-with": { type: "FUNCTION", value: "starts-with" },
|
|
123
|
+
"contains": { type: "FUNCTION", value: "contains" },
|
|
124
|
+
"substring-before": { type: "FUNCTION", value: "substring-before" },
|
|
125
|
+
"substring-after": { type: "FUNCTION", value: "substring-after" },
|
|
126
|
+
"substring": { type: "FUNCTION", value: "substring" },
|
|
127
|
+
"string-length": { type: "FUNCTION", value: "string-length" },
|
|
128
|
+
"normalize-space": { type: "FUNCTION", value: "normalize-space" },
|
|
129
|
+
"translate": { type: "FUNCTION", value: "translate" },
|
|
130
|
+
// Boolean functions
|
|
131
|
+
"boolean": { type: "FUNCTION", value: "boolean" },
|
|
132
|
+
"not": { type: "FUNCTION", value: "not" },
|
|
133
|
+
"true": { type: "FUNCTION", value: "true" },
|
|
134
|
+
"false": { type: "FUNCTION", value: "false" },
|
|
135
|
+
"lang": { type: "FUNCTION", value: "lang" },
|
|
136
|
+
// Number functions
|
|
137
|
+
"number": { type: "FUNCTION", value: "number" },
|
|
138
|
+
"sum": { type: "FUNCTION", value: "sum" },
|
|
139
|
+
"floor": { type: "FUNCTION", value: "floor" },
|
|
140
|
+
"ceiling": { type: "FUNCTION", value: "ceiling" },
|
|
141
|
+
"round": { type: "FUNCTION", value: "round" },
|
|
142
|
+
// XSLT-specific functions (commonly used)
|
|
143
|
+
"document": { type: "FUNCTION", value: "document" },
|
|
144
|
+
"key": { type: "FUNCTION", value: "key" },
|
|
145
|
+
"format-number": { type: "FUNCTION", value: "format-number" },
|
|
146
|
+
"current": { type: "FUNCTION", value: "current" },
|
|
147
|
+
"unparsed-entity-uri": { type: "FUNCTION", value: "unparsed-entity-uri" },
|
|
148
|
+
"generate-id": { type: "FUNCTION", value: "generate-id" },
|
|
149
|
+
"system-property": { type: "FUNCTION", value: "system-property" },
|
|
150
|
+
"element-available": { type: "FUNCTION", value: "element-available" },
|
|
151
|
+
"function-available": { type: "FUNCTION", value: "function-available" }
|
|
152
|
+
};
|
|
153
|
+
var XPathLexer = class {
|
|
154
|
+
/**
|
|
155
|
+
* Check if character is a valid start of an identifier.
|
|
156
|
+
* Supports Unicode letters according to XML NCName specification.
|
|
157
|
+
*/
|
|
158
|
+
isAlpha(char) {
|
|
159
|
+
return /^[a-zA-Z_\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]$/.test(char);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Check if character is valid in an identifier (after the first character).
|
|
163
|
+
* Supports Unicode letters and digits according to XML NCName specification.
|
|
164
|
+
* Note: Hyphen is handled separately in parseIdentifier for reserved words.
|
|
165
|
+
*/
|
|
166
|
+
isAlphaNumeric(char) {
|
|
167
|
+
return /^[a-zA-Z0-9_\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0300-\u036F\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]$/.test(char);
|
|
168
|
+
}
|
|
169
|
+
isNumber(char) {
|
|
170
|
+
return /^[0-9]$/.test(char);
|
|
171
|
+
}
|
|
172
|
+
isWhitespace(char) {
|
|
173
|
+
return /^[\s\t\n\r]$/.test(char);
|
|
174
|
+
}
|
|
175
|
+
peek() {
|
|
176
|
+
return this.expression[this.current];
|
|
177
|
+
}
|
|
178
|
+
peekNext() {
|
|
179
|
+
return this.expression[this.current + 1];
|
|
180
|
+
}
|
|
181
|
+
next() {
|
|
182
|
+
return this.expression[this.current++];
|
|
183
|
+
}
|
|
184
|
+
match(expected) {
|
|
185
|
+
if (this.current >= this.expression.length) return false;
|
|
186
|
+
if (this.expression[this.current] !== expected) return false;
|
|
187
|
+
this.current++;
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
parseIdentifier(firstCharacter) {
|
|
191
|
+
let characters = firstCharacter;
|
|
192
|
+
while (this.current < this.expression.length) {
|
|
193
|
+
const char = this.expression[this.current];
|
|
194
|
+
if (this.isAlphaNumeric(char)) {
|
|
195
|
+
characters += this.next();
|
|
196
|
+
} else if (char === "-") {
|
|
197
|
+
const nextChar = this.expression[this.current + 1];
|
|
198
|
+
if (nextChar && this.isAlphaNumeric(nextChar)) {
|
|
199
|
+
this.current++;
|
|
200
|
+
characters += "-";
|
|
201
|
+
while (this.current < this.expression.length && this.isAlphaNumeric(this.expression[this.current])) {
|
|
202
|
+
characters += this.next();
|
|
203
|
+
}
|
|
204
|
+
} else {
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
} else {
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
const likelyReservedWord = RESERVED_WORDS[characters.toLowerCase()];
|
|
212
|
+
if (likelyReservedWord) {
|
|
213
|
+
return new XPathToken(likelyReservedWord.type, characters);
|
|
214
|
+
}
|
|
215
|
+
if (characters.length > 0) {
|
|
216
|
+
return new XPathToken("IDENTIFIER", characters);
|
|
217
|
+
}
|
|
218
|
+
throw new Error(`Invalid identifier: ${characters}`);
|
|
219
|
+
}
|
|
220
|
+
parseString(quoteChar) {
|
|
221
|
+
let value = "";
|
|
222
|
+
while (this.current < this.expression.length && this.expression[this.current] !== quoteChar) {
|
|
223
|
+
value += this.next();
|
|
224
|
+
}
|
|
225
|
+
if (this.current >= this.expression.length) {
|
|
226
|
+
throw new Error(`Unterminated string literal`);
|
|
227
|
+
}
|
|
228
|
+
this.next();
|
|
229
|
+
return new XPathToken("STRING", value);
|
|
230
|
+
}
|
|
231
|
+
parseNumber(firstCharacter) {
|
|
232
|
+
let characters = firstCharacter;
|
|
233
|
+
while (this.current < this.expression.length && this.isNumber(this.expression[this.current]) && this.expression[this.current] !== ".") {
|
|
234
|
+
characters += this.next();
|
|
235
|
+
}
|
|
236
|
+
if (this.current < this.expression.length && this.expression[this.current] === ".") {
|
|
237
|
+
characters += this.next();
|
|
238
|
+
while (this.current < this.expression.length && this.isNumber(this.expression[this.current])) {
|
|
239
|
+
characters += this.next();
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
if (characters.length > 0) {
|
|
243
|
+
return new XPathToken("NUMBER", characters);
|
|
244
|
+
}
|
|
245
|
+
throw new Error(`Invalid number: ${characters}`);
|
|
246
|
+
}
|
|
247
|
+
scanToken() {
|
|
248
|
+
const char = this.next();
|
|
249
|
+
if (this.isWhitespace(char)) {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
switch (char) {
|
|
253
|
+
case "@":
|
|
254
|
+
return new XPathToken("AT", char);
|
|
255
|
+
case "$":
|
|
256
|
+
return new XPathToken("DOLLAR", char);
|
|
257
|
+
case "|":
|
|
258
|
+
return new XPathToken("PIPE", char);
|
|
259
|
+
case "{":
|
|
260
|
+
return new XPathToken("OPEN_CURLY_BRACKET", char);
|
|
261
|
+
case "}":
|
|
262
|
+
return new XPathToken("CLOSE_CURLY_BRACKET", char);
|
|
263
|
+
case "[":
|
|
264
|
+
return new XPathToken("OPEN_SQUARE_BRACKET", char);
|
|
265
|
+
case "]":
|
|
266
|
+
return new XPathToken("CLOSE_SQUARE_BRACKET", char);
|
|
267
|
+
case "(":
|
|
268
|
+
return new XPathToken("OPEN_PAREN", char);
|
|
269
|
+
case ")":
|
|
270
|
+
return new XPathToken("CLOSE_PAREN", char);
|
|
271
|
+
case "+":
|
|
272
|
+
return new XPathToken("PLUS", char);
|
|
273
|
+
case "-":
|
|
274
|
+
return new XPathToken("MINUS", char);
|
|
275
|
+
case "*":
|
|
276
|
+
return new XPathToken("ASTERISK", char);
|
|
277
|
+
case ",":
|
|
278
|
+
return new XPathToken("COMMA", char);
|
|
279
|
+
// Tokens that may be single or double character
|
|
280
|
+
case ".":
|
|
281
|
+
if (this.match(".")) {
|
|
282
|
+
return new XPathToken("DOT_DOT", "..");
|
|
283
|
+
}
|
|
284
|
+
if (this.peek() && this.isNumber(this.peek())) {
|
|
285
|
+
return this.parseNumber(char);
|
|
286
|
+
}
|
|
287
|
+
return new XPathToken("DOT", char);
|
|
288
|
+
case "/":
|
|
289
|
+
if (this.match("/")) {
|
|
290
|
+
return new XPathToken("DOUBLE_SLASH", "//");
|
|
291
|
+
}
|
|
292
|
+
return new XPathToken("SLASH", char);
|
|
293
|
+
case ":":
|
|
294
|
+
if (this.match(":")) {
|
|
295
|
+
return new XPathToken("COLON_COLON", "::");
|
|
296
|
+
}
|
|
297
|
+
return new XPathToken("COLON", char);
|
|
298
|
+
case "=":
|
|
299
|
+
return new XPathToken("EQUALS", char);
|
|
300
|
+
case "!":
|
|
301
|
+
if (this.match("=")) {
|
|
302
|
+
return new XPathToken("NOT_EQUALS", "!=");
|
|
303
|
+
}
|
|
304
|
+
throw new Error(`Unexpected character: ${char}`);
|
|
305
|
+
case "<":
|
|
306
|
+
if (this.match("=")) {
|
|
307
|
+
return new XPathToken("LESS_THAN_OR_EQUAL", "<=");
|
|
308
|
+
}
|
|
309
|
+
return new XPathToken("LESS_THAN", char);
|
|
310
|
+
case ">":
|
|
311
|
+
if (this.match("=")) {
|
|
312
|
+
return new XPathToken("GREATER_THAN_OR_EQUAL", ">=");
|
|
313
|
+
}
|
|
314
|
+
return new XPathToken("GREATER_THAN", char);
|
|
315
|
+
// String literals
|
|
316
|
+
case "'":
|
|
317
|
+
return this.parseString("'");
|
|
318
|
+
case '"':
|
|
319
|
+
return this.parseString('"');
|
|
320
|
+
default:
|
|
321
|
+
if (this.isNumber(char)) {
|
|
322
|
+
return this.parseNumber(char);
|
|
323
|
+
}
|
|
324
|
+
if (this.isAlpha(char)) {
|
|
325
|
+
return this.parseIdentifier(char);
|
|
326
|
+
}
|
|
327
|
+
throw new Error(`Unexpected character: ${char}`);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
scan(expression) {
|
|
331
|
+
this.expression = expression;
|
|
332
|
+
this.tokens = [];
|
|
333
|
+
this.current = 0;
|
|
334
|
+
while (this.current < this.expression.length) {
|
|
335
|
+
const token = this.scanToken();
|
|
336
|
+
if (token !== null) {
|
|
337
|
+
this.tokens.push(token);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
return this.tokens;
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
// src/xpath/lib/src/expressions/expression.ts
|
|
345
|
+
var XPathExpression = class {
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
// src/xpath/lib/src/expressions/literal-expression.ts
|
|
349
|
+
var XPathStringLiteral = class extends XPathExpression {
|
|
350
|
+
constructor(value) {
|
|
351
|
+
super();
|
|
352
|
+
this.value = value;
|
|
353
|
+
}
|
|
354
|
+
evaluate(_context) {
|
|
355
|
+
return this.value;
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
var XPathNumberLiteral = class extends XPathExpression {
|
|
359
|
+
constructor(value) {
|
|
360
|
+
super();
|
|
361
|
+
this.value = value;
|
|
362
|
+
}
|
|
363
|
+
evaluate(_context) {
|
|
364
|
+
return this.value;
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
// src/xpath/lib/src/expressions/variable-reference-expression.ts
|
|
369
|
+
var XPathVariableReference = class extends XPathExpression {
|
|
370
|
+
constructor(name) {
|
|
371
|
+
super();
|
|
372
|
+
this.name = name;
|
|
373
|
+
}
|
|
374
|
+
evaluate(context) {
|
|
375
|
+
if (!context.variables) {
|
|
376
|
+
throw new Error(`Variable $${this.name} is not defined`);
|
|
377
|
+
}
|
|
378
|
+
if (!(this.name in context.variables)) {
|
|
379
|
+
throw new Error(`Variable $${this.name} is not defined`);
|
|
380
|
+
}
|
|
381
|
+
return context.variables[this.name];
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
// src/xpath/lib/src/expressions/unary-expression.ts
|
|
386
|
+
var XPathUnaryExpression = class extends XPathExpression {
|
|
387
|
+
constructor(operator, operand) {
|
|
388
|
+
super();
|
|
389
|
+
this.operator = operator;
|
|
390
|
+
this.operand = operand;
|
|
391
|
+
}
|
|
392
|
+
evaluate(context) {
|
|
393
|
+
const value = this.operand.evaluate(context);
|
|
394
|
+
switch (this.operator) {
|
|
395
|
+
case "-":
|
|
396
|
+
return -Number(value);
|
|
397
|
+
default:
|
|
398
|
+
throw new Error(`Unknown unary operator: ${this.operator}`);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
// src/xpath/lib/src/expressions/binary-expression.ts
|
|
404
|
+
var XPathBinaryExpression = class extends XPathExpression {
|
|
405
|
+
constructor(left, right, operator) {
|
|
406
|
+
super();
|
|
407
|
+
this.left = left;
|
|
408
|
+
this.right = right;
|
|
409
|
+
this.operator = operator;
|
|
410
|
+
}
|
|
411
|
+
evaluate(context) {
|
|
412
|
+
const leftValue = this.left.evaluate(context);
|
|
413
|
+
const rightValue = this.right.evaluate(context);
|
|
414
|
+
return this.compare(leftValue, rightValue, this.operator);
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* XPath comparison rules:
|
|
418
|
+
* - If both are node-sets: compare each node in left with each node in right
|
|
419
|
+
* - If one is node-set and other is string: convert node-set to strings and compare
|
|
420
|
+
* - If one is node-set and other is number: convert node-set to numbers and compare
|
|
421
|
+
* - If one is node-set and other is boolean: convert node-set to boolean and compare
|
|
422
|
+
* - Otherwise, convert both to numbers for numeric comparison, or strings for equality
|
|
423
|
+
*/
|
|
424
|
+
compare(left, right, operator) {
|
|
425
|
+
const leftIsNodeSet = Array.isArray(left);
|
|
426
|
+
const rightIsNodeSet = Array.isArray(right);
|
|
427
|
+
if (leftIsNodeSet && rightIsNodeSet) {
|
|
428
|
+
return this.compareNodeSets(left, right, operator);
|
|
429
|
+
}
|
|
430
|
+
if (leftIsNodeSet) {
|
|
431
|
+
return this.compareNodeSetToValue(left, right, operator);
|
|
432
|
+
}
|
|
433
|
+
if (rightIsNodeSet) {
|
|
434
|
+
return this.compareValueToNodeSet(left, right, operator);
|
|
435
|
+
}
|
|
436
|
+
return this.comparePrimitives(left, right, operator);
|
|
437
|
+
}
|
|
438
|
+
compareNodeSets(left, right, operator) {
|
|
439
|
+
for (const leftNode of left) {
|
|
440
|
+
const leftStr = this.getStringValue(leftNode);
|
|
441
|
+
for (const rightNode of right) {
|
|
442
|
+
const rightStr = this.getStringValue(rightNode);
|
|
443
|
+
if (this.comparePrimitives(leftStr, rightStr, operator)) {
|
|
444
|
+
return true;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
return false;
|
|
449
|
+
}
|
|
450
|
+
compareNodeSetToValue(nodeSet, value, operator) {
|
|
451
|
+
for (const node of nodeSet) {
|
|
452
|
+
const nodeValue = typeof value === "number" ? Number(this.getStringValue(node)) : this.getStringValue(node);
|
|
453
|
+
if (this.comparePrimitives(nodeValue, value, operator)) {
|
|
454
|
+
return true;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
return false;
|
|
458
|
+
}
|
|
459
|
+
compareValueToNodeSet(value, nodeSet, operator) {
|
|
460
|
+
for (const node of nodeSet) {
|
|
461
|
+
const nodeValue = typeof value === "number" ? Number(this.getStringValue(node)) : this.getStringValue(node);
|
|
462
|
+
if (this.comparePrimitives(value, nodeValue, operator)) {
|
|
463
|
+
return true;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
return false;
|
|
467
|
+
}
|
|
468
|
+
comparePrimitives(left, right, operator) {
|
|
469
|
+
switch (operator) {
|
|
470
|
+
case "=":
|
|
471
|
+
return left == right;
|
|
472
|
+
// Use loose equality for type coercion
|
|
473
|
+
case "!=":
|
|
474
|
+
return left != right;
|
|
475
|
+
case "<":
|
|
476
|
+
return Number(left) < Number(right);
|
|
477
|
+
case ">":
|
|
478
|
+
return Number(left) > Number(right);
|
|
479
|
+
case "<=":
|
|
480
|
+
return Number(left) <= Number(right);
|
|
481
|
+
case ">=":
|
|
482
|
+
return Number(left) >= Number(right);
|
|
483
|
+
default:
|
|
484
|
+
throw new Error(`Unknown operator: ${operator}`);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
getStringValue(node) {
|
|
488
|
+
if (!node) return "";
|
|
489
|
+
if (node.nodeType === 3 || node.nodeType === 2) {
|
|
490
|
+
return node.nodeValue || node.textContent || "";
|
|
491
|
+
}
|
|
492
|
+
if (node.textContent !== void 0) {
|
|
493
|
+
return node.textContent;
|
|
494
|
+
}
|
|
495
|
+
if (node.childNodes) {
|
|
496
|
+
let text = "";
|
|
497
|
+
for (const child of Array.from(node.childNodes)) {
|
|
498
|
+
if (child.nodeType === 3) {
|
|
499
|
+
text += child.nodeValue || "";
|
|
500
|
+
} else if (child.nodeType === 1) {
|
|
501
|
+
text += this.getStringValue(child);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
return text;
|
|
505
|
+
}
|
|
506
|
+
return String(node);
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
// src/xpath/lib/src/expressions/arithmetic-expression.ts
|
|
511
|
+
var XPathArithmeticExpression = class extends XPathExpression {
|
|
512
|
+
constructor(left, right, operator) {
|
|
513
|
+
super();
|
|
514
|
+
this.left = left;
|
|
515
|
+
this.right = right;
|
|
516
|
+
this.operator = operator;
|
|
517
|
+
}
|
|
518
|
+
evaluate(context) {
|
|
519
|
+
const leftValue = Number(this.left.evaluate(context));
|
|
520
|
+
const rightValue = Number(this.right.evaluate(context));
|
|
521
|
+
switch (this.operator) {
|
|
522
|
+
case "+":
|
|
523
|
+
return leftValue + rightValue;
|
|
524
|
+
case "-":
|
|
525
|
+
return leftValue - rightValue;
|
|
526
|
+
case "*":
|
|
527
|
+
return leftValue * rightValue;
|
|
528
|
+
case "div":
|
|
529
|
+
return leftValue / rightValue;
|
|
530
|
+
case "mod":
|
|
531
|
+
return leftValue % rightValue;
|
|
532
|
+
default:
|
|
533
|
+
throw new Error(`Unknown arithmetic operator: ${this.operator}`);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
// src/xpath/lib/src/expressions/logical-expression.ts
|
|
539
|
+
var XPathLogicalExpression = class extends XPathExpression {
|
|
540
|
+
constructor(left, right, operator) {
|
|
541
|
+
super();
|
|
542
|
+
this.left = left;
|
|
543
|
+
this.right = right;
|
|
544
|
+
this.operator = operator;
|
|
545
|
+
}
|
|
546
|
+
toBoolean(value) {
|
|
547
|
+
if (typeof value === "boolean") {
|
|
548
|
+
return value;
|
|
549
|
+
}
|
|
550
|
+
if (typeof value === "number") {
|
|
551
|
+
return value !== 0 && !isNaN(value);
|
|
552
|
+
}
|
|
553
|
+
if (typeof value === "string") {
|
|
554
|
+
return value.length > 0;
|
|
555
|
+
}
|
|
556
|
+
if (Array.isArray(value)) {
|
|
557
|
+
return value.length > 0;
|
|
558
|
+
}
|
|
559
|
+
return !!value;
|
|
560
|
+
}
|
|
561
|
+
evaluate(context) {
|
|
562
|
+
const leftValue = this.toBoolean(this.left.evaluate(context));
|
|
563
|
+
if (this.operator === "and") {
|
|
564
|
+
if (!leftValue) return false;
|
|
565
|
+
return this.toBoolean(this.right.evaluate(context));
|
|
566
|
+
}
|
|
567
|
+
if (this.operator === "or") {
|
|
568
|
+
if (leftValue) return true;
|
|
569
|
+
return this.toBoolean(this.right.evaluate(context));
|
|
570
|
+
}
|
|
571
|
+
throw new Error(`Unknown logical operator: ${this.operator}`);
|
|
572
|
+
}
|
|
573
|
+
};
|
|
574
|
+
|
|
575
|
+
// src/xpath/lib/src/expressions/step-expression.ts
|
|
576
|
+
var XPathStep = class extends XPathExpression {
|
|
577
|
+
constructor(axis, nodeTest, predicates = []) {
|
|
578
|
+
super();
|
|
579
|
+
this.axis = axis;
|
|
580
|
+
this.nodeTest = nodeTest;
|
|
581
|
+
this.predicates = predicates;
|
|
582
|
+
}
|
|
583
|
+
evaluate(context) {
|
|
584
|
+
const node = context == null ? void 0 : context.node;
|
|
585
|
+
if (!node) return [];
|
|
586
|
+
let candidates = this.getNodesByAxis(node, context);
|
|
587
|
+
candidates = candidates.filter((n) => this.matchesNodeTest(n, context));
|
|
588
|
+
candidates = this.applyPredicates(candidates, context);
|
|
589
|
+
return candidates;
|
|
590
|
+
}
|
|
591
|
+
getNodesByAxis(node, context) {
|
|
592
|
+
switch (this.axis) {
|
|
593
|
+
case "child":
|
|
594
|
+
return this.getChildNodes(node);
|
|
595
|
+
case "parent":
|
|
596
|
+
return node.parentNode ? [node.parentNode] : [];
|
|
597
|
+
case "self":
|
|
598
|
+
return [node];
|
|
599
|
+
case "attribute":
|
|
600
|
+
if (node.attributes) {
|
|
601
|
+
return Array.from(node.attributes);
|
|
602
|
+
}
|
|
603
|
+
return Array.from(node.childNodes || []).filter((n) => n.nodeType === 2);
|
|
604
|
+
case "descendant":
|
|
605
|
+
return this.getDescendants(node, false);
|
|
606
|
+
case "descendant-or-self":
|
|
607
|
+
return this.getDescendants(node, true);
|
|
608
|
+
case "ancestor":
|
|
609
|
+
return this.getAncestors(node, false);
|
|
610
|
+
case "ancestor-or-self":
|
|
611
|
+
return this.getAncestors(node, true);
|
|
612
|
+
case "following-sibling":
|
|
613
|
+
return this.getFollowingSiblings(node);
|
|
614
|
+
case "preceding-sibling":
|
|
615
|
+
return this.getPrecedingSiblings(node);
|
|
616
|
+
case "following":
|
|
617
|
+
return this.getFollowing(node);
|
|
618
|
+
case "preceding":
|
|
619
|
+
return this.getPreceding(node);
|
|
620
|
+
case "namespace":
|
|
621
|
+
return [];
|
|
622
|
+
case "self-and-siblings":
|
|
623
|
+
if (context == null ? void 0 : context.nodeList) {
|
|
624
|
+
return context.nodeList.filter((n) => n.nodeType !== 2);
|
|
625
|
+
}
|
|
626
|
+
return [node];
|
|
627
|
+
default:
|
|
628
|
+
return [];
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Get child nodes excluding attribute nodes.
|
|
633
|
+
* XNode stores attributes in childNodes, but XPath child axis doesn't include them.
|
|
634
|
+
*/
|
|
635
|
+
getChildNodes(node) {
|
|
636
|
+
const children = Array.from(node.childNodes || []);
|
|
637
|
+
return children.filter((n) => n.nodeType !== 2);
|
|
638
|
+
}
|
|
639
|
+
getDescendants(node, includeSelf) {
|
|
640
|
+
const result = [];
|
|
641
|
+
if (includeSelf) result.push(node);
|
|
642
|
+
const walk = (n) => {
|
|
643
|
+
for (const child of this.getChildNodes(n)) {
|
|
644
|
+
result.push(child);
|
|
645
|
+
walk(child);
|
|
646
|
+
}
|
|
647
|
+
};
|
|
648
|
+
walk(node);
|
|
649
|
+
return result;
|
|
650
|
+
}
|
|
651
|
+
getAncestors(node, includeSelf) {
|
|
652
|
+
const result = [];
|
|
653
|
+
if (includeSelf) result.push(node);
|
|
654
|
+
let current = node.parentNode;
|
|
655
|
+
while (current) {
|
|
656
|
+
result.push(current);
|
|
657
|
+
current = current.parentNode;
|
|
658
|
+
}
|
|
659
|
+
return result;
|
|
660
|
+
}
|
|
661
|
+
getFollowingSiblings(node) {
|
|
662
|
+
const result = [];
|
|
663
|
+
let sibling = node.nextSibling;
|
|
664
|
+
while (sibling) {
|
|
665
|
+
result.push(sibling);
|
|
666
|
+
sibling = sibling.nextSibling;
|
|
667
|
+
}
|
|
668
|
+
return result;
|
|
669
|
+
}
|
|
670
|
+
getPrecedingSiblings(node) {
|
|
671
|
+
const result = [];
|
|
672
|
+
let sibling = node.previousSibling;
|
|
673
|
+
while (sibling) {
|
|
674
|
+
result.unshift(sibling);
|
|
675
|
+
sibling = sibling.previousSibling;
|
|
676
|
+
}
|
|
677
|
+
return result;
|
|
678
|
+
}
|
|
679
|
+
getFollowing(node) {
|
|
680
|
+
const result = [];
|
|
681
|
+
let sibling = node.nextSibling;
|
|
682
|
+
while (sibling) {
|
|
683
|
+
result.push(sibling);
|
|
684
|
+
result.push(...this.getDescendants(sibling, false));
|
|
685
|
+
sibling = sibling.nextSibling;
|
|
686
|
+
}
|
|
687
|
+
let ancestor = node.parentNode;
|
|
688
|
+
while (ancestor) {
|
|
689
|
+
sibling = ancestor.nextSibling;
|
|
690
|
+
while (sibling) {
|
|
691
|
+
result.push(sibling);
|
|
692
|
+
result.push(...this.getDescendants(sibling, false));
|
|
693
|
+
sibling = sibling.nextSibling;
|
|
694
|
+
}
|
|
695
|
+
ancestor = ancestor.parentNode;
|
|
696
|
+
}
|
|
697
|
+
return result;
|
|
698
|
+
}
|
|
699
|
+
getPreceding(node) {
|
|
700
|
+
const result = [];
|
|
701
|
+
let sibling = node.previousSibling;
|
|
702
|
+
while (sibling) {
|
|
703
|
+
result.unshift(sibling);
|
|
704
|
+
const descendants = this.getDescendants(sibling, false);
|
|
705
|
+
result.unshift(...descendants);
|
|
706
|
+
sibling = sibling.previousSibling;
|
|
707
|
+
}
|
|
708
|
+
let ancestor = node.parentNode;
|
|
709
|
+
while (ancestor) {
|
|
710
|
+
sibling = ancestor.previousSibling;
|
|
711
|
+
while (sibling) {
|
|
712
|
+
result.unshift(sibling);
|
|
713
|
+
const descendants = this.getDescendants(sibling, false);
|
|
714
|
+
result.unshift(...descendants);
|
|
715
|
+
sibling = sibling.previousSibling;
|
|
716
|
+
}
|
|
717
|
+
ancestor = ancestor.parentNode;
|
|
718
|
+
}
|
|
719
|
+
return result;
|
|
720
|
+
}
|
|
721
|
+
matchesNodeTest(node, context) {
|
|
722
|
+
var _a, _b;
|
|
723
|
+
const nodeType = node.nodeType;
|
|
724
|
+
switch (this.nodeTest.type) {
|
|
725
|
+
case "wildcard":
|
|
726
|
+
if (this.nodeTest.name && this.nodeTest.name.endsWith(":*")) {
|
|
727
|
+
const prefix = this.nodeTest.name.slice(0, -2);
|
|
728
|
+
const nsUri = (_a = context == null ? void 0 : context.namespaces) == null ? void 0 : _a[prefix];
|
|
729
|
+
if (!nsUri) return false;
|
|
730
|
+
const nodeNsUri = node.namespaceURI || node.namespaceUri || "";
|
|
731
|
+
return (nodeType === 1 || nodeType === 2) && nodeNsUri === nsUri;
|
|
732
|
+
}
|
|
733
|
+
return nodeType === 1 || nodeType === 2;
|
|
734
|
+
case "name":
|
|
735
|
+
if (nodeType !== 1 && nodeType !== 2) return false;
|
|
736
|
+
const testName = this.nodeTest.name;
|
|
737
|
+
const colonIndex = testName.indexOf(":");
|
|
738
|
+
if (colonIndex > 0) {
|
|
739
|
+
const prefix = testName.substring(0, colonIndex);
|
|
740
|
+
const localName = testName.substring(colonIndex + 1);
|
|
741
|
+
const nsUri = (_b = context == null ? void 0 : context.namespaces) == null ? void 0 : _b[prefix];
|
|
742
|
+
if (!nsUri) {
|
|
743
|
+
return false;
|
|
744
|
+
}
|
|
745
|
+
const nodeLocalName = node.localName || node.nodeName;
|
|
746
|
+
const nodeNsUri = node.namespaceURI || node.namespaceUri || "";
|
|
747
|
+
return nodeLocalName === localName && nodeNsUri === nsUri;
|
|
748
|
+
}
|
|
749
|
+
const nodeName = node.localName || node.nodeName;
|
|
750
|
+
return nodeName === testName;
|
|
751
|
+
case "node-type":
|
|
752
|
+
switch (this.nodeTest.nodeType) {
|
|
753
|
+
case "node":
|
|
754
|
+
return true;
|
|
755
|
+
// matches any node
|
|
756
|
+
case "text":
|
|
757
|
+
return nodeType === 3;
|
|
758
|
+
// text node
|
|
759
|
+
case "comment":
|
|
760
|
+
return nodeType === 8;
|
|
761
|
+
// comment node
|
|
762
|
+
case "processing-instruction":
|
|
763
|
+
return nodeType === 7;
|
|
764
|
+
// processing instruction
|
|
765
|
+
default:
|
|
766
|
+
return false;
|
|
767
|
+
}
|
|
768
|
+
case "processing-instruction":
|
|
769
|
+
if (nodeType !== 7) return false;
|
|
770
|
+
if (this.nodeTest.name) {
|
|
771
|
+
return node.target === this.nodeTest.name;
|
|
772
|
+
}
|
|
773
|
+
return true;
|
|
774
|
+
default:
|
|
775
|
+
return false;
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
applyPredicates(nodes, context) {
|
|
779
|
+
let result = nodes;
|
|
780
|
+
for (const predicate of this.predicates) {
|
|
781
|
+
const filtered = [];
|
|
782
|
+
const size = result.length;
|
|
783
|
+
for (let i = 0; i < result.length; i++) {
|
|
784
|
+
const predicateContext = __spreadProps(__spreadValues({}, context), {
|
|
785
|
+
node: result[i],
|
|
786
|
+
position: i + 1,
|
|
787
|
+
size
|
|
788
|
+
});
|
|
789
|
+
const predicateResult = predicate.evaluate(predicateContext);
|
|
790
|
+
if (typeof predicateResult === "number") {
|
|
791
|
+
if (predicateResult === i + 1) {
|
|
792
|
+
filtered.push(result[i]);
|
|
793
|
+
}
|
|
794
|
+
} else if (this.toBoolean(predicateResult)) {
|
|
795
|
+
filtered.push(result[i]);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
result = filtered;
|
|
799
|
+
}
|
|
800
|
+
return result;
|
|
801
|
+
}
|
|
802
|
+
toBoolean(value) {
|
|
803
|
+
if (typeof value === "boolean") return value;
|
|
804
|
+
if (typeof value === "number") return value !== 0 && !isNaN(value);
|
|
805
|
+
if (typeof value === "string") return value.length > 0;
|
|
806
|
+
if (Array.isArray(value)) return value.length > 0;
|
|
807
|
+
return !!value;
|
|
808
|
+
}
|
|
809
|
+
};
|
|
810
|
+
|
|
811
|
+
// src/xpath/lib/src/expressions/predicate-expression.ts
|
|
812
|
+
var XPathPredicate = class extends XPathExpression {
|
|
813
|
+
constructor(expression) {
|
|
814
|
+
super();
|
|
815
|
+
this.expression = expression;
|
|
816
|
+
}
|
|
817
|
+
evaluate(context) {
|
|
818
|
+
return this.expression.evaluate(context);
|
|
819
|
+
}
|
|
820
|
+
test(context) {
|
|
821
|
+
const result = this.evaluate(context);
|
|
822
|
+
if (typeof result === "number") {
|
|
823
|
+
return result === (context == null ? void 0 : context.position);
|
|
824
|
+
}
|
|
825
|
+
return this.toBoolean(result);
|
|
826
|
+
}
|
|
827
|
+
toBoolean(value) {
|
|
828
|
+
if (typeof value === "boolean") return value;
|
|
829
|
+
if (typeof value === "number") return value !== 0 && !isNaN(value);
|
|
830
|
+
if (typeof value === "string") return value.length > 0;
|
|
831
|
+
if (Array.isArray(value)) return value.length > 0;
|
|
832
|
+
return !!value;
|
|
833
|
+
}
|
|
834
|
+
};
|
|
835
|
+
|
|
836
|
+
// src/xpath/lib/src/expressions/location-path-expression.ts
|
|
837
|
+
var XPathLocationPath = class extends XPathExpression {
|
|
838
|
+
constructor(steps, absolute = false) {
|
|
839
|
+
super();
|
|
840
|
+
this.steps = steps;
|
|
841
|
+
this.absolute = absolute;
|
|
842
|
+
}
|
|
843
|
+
evaluate(context) {
|
|
844
|
+
let nodes;
|
|
845
|
+
if (this.absolute) {
|
|
846
|
+
const root = this.getDocumentRoot(context == null ? void 0 : context.node);
|
|
847
|
+
nodes = root ? [root] : [];
|
|
848
|
+
} else {
|
|
849
|
+
nodes = (context == null ? void 0 : context.node) ? [context.node] : [];
|
|
850
|
+
}
|
|
851
|
+
for (const step of this.steps) {
|
|
852
|
+
const nextNodes = [];
|
|
853
|
+
for (const node of nodes) {
|
|
854
|
+
const stepContext = __spreadProps(__spreadValues({}, context), { node });
|
|
855
|
+
const result = step.evaluate(stepContext);
|
|
856
|
+
nextNodes.push(...result);
|
|
857
|
+
}
|
|
858
|
+
nodes = this.uniqueNodes(nextNodes);
|
|
859
|
+
}
|
|
860
|
+
return nodes;
|
|
861
|
+
}
|
|
862
|
+
getDocumentRoot(node) {
|
|
863
|
+
if (!node) return null;
|
|
864
|
+
let root = node;
|
|
865
|
+
while (root.parentNode) {
|
|
866
|
+
root = root.parentNode;
|
|
867
|
+
}
|
|
868
|
+
return root;
|
|
869
|
+
}
|
|
870
|
+
uniqueNodes(nodes) {
|
|
871
|
+
const seen = /* @__PURE__ */ new Set();
|
|
872
|
+
const result = [];
|
|
873
|
+
for (const node of nodes) {
|
|
874
|
+
if (!seen.has(node)) {
|
|
875
|
+
seen.add(node);
|
|
876
|
+
result.push(node);
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
return result;
|
|
880
|
+
}
|
|
881
|
+
};
|
|
882
|
+
|
|
883
|
+
// src/xpath/lib/src/expressions/filter-expression.ts
|
|
884
|
+
var XPathFilterExpression = class extends XPathExpression {
|
|
885
|
+
constructor(expression, predicate) {
|
|
886
|
+
super();
|
|
887
|
+
this.expression = expression;
|
|
888
|
+
this.predicate = predicate;
|
|
889
|
+
}
|
|
890
|
+
evaluate(context) {
|
|
891
|
+
return [];
|
|
892
|
+
}
|
|
893
|
+
};
|
|
894
|
+
|
|
895
|
+
// src/xpath/lib/src/expressions/union-expression.ts
|
|
896
|
+
var XPathUnionExpression = class extends XPathExpression {
|
|
897
|
+
constructor(left, right) {
|
|
898
|
+
super();
|
|
899
|
+
this.left = left;
|
|
900
|
+
this.right = right;
|
|
901
|
+
}
|
|
902
|
+
evaluate(context) {
|
|
903
|
+
const leftResult = this.left.evaluate(context);
|
|
904
|
+
const rightResult = this.right.evaluate(context);
|
|
905
|
+
const leftNodes = Array.isArray(leftResult) ? leftResult : [];
|
|
906
|
+
const rightNodes = Array.isArray(rightResult) ? rightResult : [];
|
|
907
|
+
return this.unionNodes(leftNodes, rightNodes);
|
|
908
|
+
}
|
|
909
|
+
unionNodes(left, right) {
|
|
910
|
+
const seen = /* @__PURE__ */ new Set();
|
|
911
|
+
const result = [];
|
|
912
|
+
for (const node of left) {
|
|
913
|
+
if (!seen.has(node)) {
|
|
914
|
+
seen.add(node);
|
|
915
|
+
result.push(node);
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
for (const node of right) {
|
|
919
|
+
if (!seen.has(node)) {
|
|
920
|
+
seen.add(node);
|
|
921
|
+
result.push(node);
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
return this.sortByDocumentOrder(result);
|
|
925
|
+
}
|
|
926
|
+
sortByDocumentOrder(nodes) {
|
|
927
|
+
return nodes.sort((a, b) => {
|
|
928
|
+
if (a === b) return 0;
|
|
929
|
+
if (typeof a.compareDocumentPosition === "function") {
|
|
930
|
+
const position = a.compareDocumentPosition(b);
|
|
931
|
+
if (position & 4) return -1;
|
|
932
|
+
if (position & 2) return 1;
|
|
933
|
+
}
|
|
934
|
+
return 0;
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
};
|
|
938
|
+
|
|
939
|
+
// src/xpath/lib/src/expressions/function-call-expression.ts
|
|
940
|
+
var XPathFunctionCall = class extends XPathExpression {
|
|
941
|
+
constructor(name, args) {
|
|
942
|
+
super();
|
|
943
|
+
this.name = name;
|
|
944
|
+
this.args = args;
|
|
945
|
+
}
|
|
946
|
+
evaluate(context) {
|
|
947
|
+
var _a, _b;
|
|
948
|
+
const evaluatedArgs = this.args.map((arg) => arg.evaluate(context));
|
|
949
|
+
switch (this.name) {
|
|
950
|
+
// Node set functions
|
|
951
|
+
case "last":
|
|
952
|
+
return (_a = context.size) != null ? _a : 0;
|
|
953
|
+
case "position":
|
|
954
|
+
return (_b = context.position) != null ? _b : 0;
|
|
955
|
+
case "count":
|
|
956
|
+
return Array.isArray(evaluatedArgs[0]) ? evaluatedArgs[0].length : 0;
|
|
957
|
+
case "local-name":
|
|
958
|
+
return this.localName(evaluatedArgs, context);
|
|
959
|
+
case "namespace-uri":
|
|
960
|
+
return this.namespaceUri(evaluatedArgs, context);
|
|
961
|
+
case "name":
|
|
962
|
+
return this.nodeName(evaluatedArgs, context);
|
|
963
|
+
// String functions
|
|
964
|
+
case "string":
|
|
965
|
+
return this.stringValue(evaluatedArgs, context);
|
|
966
|
+
case "concat":
|
|
967
|
+
return evaluatedArgs.map(String).join("");
|
|
968
|
+
case "starts-with":
|
|
969
|
+
return String(evaluatedArgs[0]).startsWith(String(evaluatedArgs[1]));
|
|
970
|
+
case "contains":
|
|
971
|
+
return String(evaluatedArgs[0]).includes(String(evaluatedArgs[1]));
|
|
972
|
+
case "substring-before":
|
|
973
|
+
return this.substringBefore(evaluatedArgs);
|
|
974
|
+
case "substring-after":
|
|
975
|
+
return this.substringAfter(evaluatedArgs);
|
|
976
|
+
case "substring":
|
|
977
|
+
return this.substring(evaluatedArgs);
|
|
978
|
+
case "string-length":
|
|
979
|
+
return this.stringLength(evaluatedArgs, context);
|
|
980
|
+
case "normalize-space":
|
|
981
|
+
return this.normalizeSpace(evaluatedArgs, context);
|
|
982
|
+
case "translate":
|
|
983
|
+
return this.translate(evaluatedArgs);
|
|
984
|
+
// Boolean functions
|
|
985
|
+
case "boolean":
|
|
986
|
+
return this.toBoolean(evaluatedArgs[0]);
|
|
987
|
+
case "not":
|
|
988
|
+
return !this.toBoolean(evaluatedArgs[0]);
|
|
989
|
+
case "true":
|
|
990
|
+
return true;
|
|
991
|
+
case "false":
|
|
992
|
+
return false;
|
|
993
|
+
case "lang":
|
|
994
|
+
return this.lang(evaluatedArgs, context);
|
|
995
|
+
// Number functions
|
|
996
|
+
case "number":
|
|
997
|
+
return this.toNumber(evaluatedArgs, context);
|
|
998
|
+
case "sum":
|
|
999
|
+
return this.sum(evaluatedArgs);
|
|
1000
|
+
case "floor":
|
|
1001
|
+
return Math.floor(Number(evaluatedArgs[0]));
|
|
1002
|
+
case "ceiling":
|
|
1003
|
+
return Math.ceil(Number(evaluatedArgs[0]));
|
|
1004
|
+
case "round":
|
|
1005
|
+
return Math.round(Number(evaluatedArgs[0]));
|
|
1006
|
+
default:
|
|
1007
|
+
if (context.functions && typeof context.functions[this.name] === "function") {
|
|
1008
|
+
return context.functions[this.name](...evaluatedArgs);
|
|
1009
|
+
}
|
|
1010
|
+
throw new Error(`Unknown function: ${this.name}`);
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
toBoolean(value) {
|
|
1014
|
+
if (typeof value === "boolean") return value;
|
|
1015
|
+
if (typeof value === "number") return value !== 0 && !isNaN(value);
|
|
1016
|
+
if (typeof value === "string") return value.length > 0;
|
|
1017
|
+
if (Array.isArray(value)) return value.length > 0;
|
|
1018
|
+
return !!value;
|
|
1019
|
+
}
|
|
1020
|
+
toNumber(args, context) {
|
|
1021
|
+
if (args.length === 0) {
|
|
1022
|
+
return Number(this.stringValue([], context));
|
|
1023
|
+
}
|
|
1024
|
+
return Number(args[0]);
|
|
1025
|
+
}
|
|
1026
|
+
stringValue(args, context) {
|
|
1027
|
+
var _a, _b, _c, _d;
|
|
1028
|
+
if (args.length === 0) {
|
|
1029
|
+
return (_b = (_a = context.node) == null ? void 0 : _a.textContent) != null ? _b : "";
|
|
1030
|
+
}
|
|
1031
|
+
const value = args[0];
|
|
1032
|
+
if (Array.isArray(value) && value.length > 0) {
|
|
1033
|
+
return (_d = (_c = value[0]) == null ? void 0 : _c.textContent) != null ? _d : String(value[0]);
|
|
1034
|
+
}
|
|
1035
|
+
return String(value);
|
|
1036
|
+
}
|
|
1037
|
+
stringLength(args, context) {
|
|
1038
|
+
if (args.length === 0) {
|
|
1039
|
+
return this.stringValue([], context).length;
|
|
1040
|
+
}
|
|
1041
|
+
return String(args[0]).length;
|
|
1042
|
+
}
|
|
1043
|
+
normalizeSpace(args, context) {
|
|
1044
|
+
const str = args.length === 0 ? this.stringValue([], context) : String(args[0]);
|
|
1045
|
+
return str.trim().replace(/\s+/g, " ");
|
|
1046
|
+
}
|
|
1047
|
+
substringBefore(args) {
|
|
1048
|
+
const str = String(args[0]);
|
|
1049
|
+
const search = String(args[1]);
|
|
1050
|
+
const index = str.indexOf(search);
|
|
1051
|
+
return index === -1 ? "" : str.substring(0, index);
|
|
1052
|
+
}
|
|
1053
|
+
substringAfter(args) {
|
|
1054
|
+
const str = String(args[0]);
|
|
1055
|
+
const search = String(args[1]);
|
|
1056
|
+
const index = str.indexOf(search);
|
|
1057
|
+
return index === -1 ? "" : str.substring(index + search.length);
|
|
1058
|
+
}
|
|
1059
|
+
substring(args) {
|
|
1060
|
+
const str = String(args[0]);
|
|
1061
|
+
const start = Math.round(Number(args[1])) - 1;
|
|
1062
|
+
if (args.length === 2) {
|
|
1063
|
+
return str.substring(Math.max(0, start));
|
|
1064
|
+
}
|
|
1065
|
+
const length = Math.round(Number(args[2]));
|
|
1066
|
+
const adjustedStart = Math.max(0, start);
|
|
1067
|
+
const adjustedLength = Math.min(length - (adjustedStart - start), str.length - adjustedStart);
|
|
1068
|
+
return str.substring(adjustedStart, adjustedStart + adjustedLength);
|
|
1069
|
+
}
|
|
1070
|
+
translate(args) {
|
|
1071
|
+
const str = String(args[0]);
|
|
1072
|
+
const from = String(args[1]);
|
|
1073
|
+
const to = String(args[2]);
|
|
1074
|
+
let result = "";
|
|
1075
|
+
for (const char of str) {
|
|
1076
|
+
const index = from.indexOf(char);
|
|
1077
|
+
if (index === -1) {
|
|
1078
|
+
result += char;
|
|
1079
|
+
} else if (index < to.length) {
|
|
1080
|
+
result += to[index];
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
return result;
|
|
1084
|
+
}
|
|
1085
|
+
localName(args, context) {
|
|
1086
|
+
var _a;
|
|
1087
|
+
const node = this.getNodeArg(args, context);
|
|
1088
|
+
return (_a = node == null ? void 0 : node.localName) != null ? _a : "";
|
|
1089
|
+
}
|
|
1090
|
+
namespaceUri(args, context) {
|
|
1091
|
+
var _a;
|
|
1092
|
+
const node = this.getNodeArg(args, context);
|
|
1093
|
+
return (_a = node == null ? void 0 : node.namespaceURI) != null ? _a : "";
|
|
1094
|
+
}
|
|
1095
|
+
nodeName(args, context) {
|
|
1096
|
+
var _a;
|
|
1097
|
+
const node = this.getNodeArg(args, context);
|
|
1098
|
+
return (_a = node == null ? void 0 : node.nodeName) != null ? _a : "";
|
|
1099
|
+
}
|
|
1100
|
+
getNodeArg(args, context) {
|
|
1101
|
+
if (args.length > 0 && Array.isArray(args[0]) && args[0].length > 0) {
|
|
1102
|
+
return args[0][0];
|
|
1103
|
+
}
|
|
1104
|
+
return context.node;
|
|
1105
|
+
}
|
|
1106
|
+
sum(args) {
|
|
1107
|
+
const nodeSet = args[0];
|
|
1108
|
+
if (!Array.isArray(nodeSet)) return 0;
|
|
1109
|
+
return nodeSet.reduce((acc, node) => {
|
|
1110
|
+
var _a;
|
|
1111
|
+
const value = Number((_a = node == null ? void 0 : node.textContent) != null ? _a : node);
|
|
1112
|
+
return acc + (isNaN(value) ? 0 : value);
|
|
1113
|
+
}, 0);
|
|
1114
|
+
}
|
|
1115
|
+
lang(args, context) {
|
|
1116
|
+
var _a, _b;
|
|
1117
|
+
const targetLang = String(args[0]).toLowerCase();
|
|
1118
|
+
let node = context.node;
|
|
1119
|
+
while (node) {
|
|
1120
|
+
const lang = ((_a = node.getAttribute) == null ? void 0 : _a.call(node, "xml:lang")) || ((_b = node.getAttribute) == null ? void 0 : _b.call(node, "lang"));
|
|
1121
|
+
if (lang) {
|
|
1122
|
+
const nodeLang = lang.toLowerCase();
|
|
1123
|
+
return nodeLang === targetLang || nodeLang.startsWith(targetLang + "-");
|
|
1124
|
+
}
|
|
1125
|
+
node = node.parentNode;
|
|
1126
|
+
}
|
|
1127
|
+
return false;
|
|
1128
|
+
}
|
|
1129
|
+
};
|
|
1130
|
+
|
|
1131
|
+
// src/xpath/lib/src/parser.ts
|
|
1132
|
+
var XPathParser = class {
|
|
1133
|
+
constructor() {
|
|
1134
|
+
this.tokens = [];
|
|
1135
|
+
this.current = 0;
|
|
1136
|
+
}
|
|
1137
|
+
parse(tokens) {
|
|
1138
|
+
this.tokens = tokens;
|
|
1139
|
+
this.current = 0;
|
|
1140
|
+
if (tokens.length === 0) {
|
|
1141
|
+
throw new Error("Empty expression");
|
|
1142
|
+
}
|
|
1143
|
+
const expr = this.parseExpr();
|
|
1144
|
+
if (!this.isAtEnd()) {
|
|
1145
|
+
throw new Error(`Unexpected token: ${this.peek().lexeme}`);
|
|
1146
|
+
}
|
|
1147
|
+
return expr;
|
|
1148
|
+
}
|
|
1149
|
+
// ==================== Token Management ====================
|
|
1150
|
+
peek() {
|
|
1151
|
+
return this.tokens[this.current];
|
|
1152
|
+
}
|
|
1153
|
+
peekNext() {
|
|
1154
|
+
return this.tokens[this.current + 1];
|
|
1155
|
+
}
|
|
1156
|
+
previous() {
|
|
1157
|
+
return this.tokens[this.current - 1];
|
|
1158
|
+
}
|
|
1159
|
+
isAtEnd() {
|
|
1160
|
+
return this.current >= this.tokens.length;
|
|
1161
|
+
}
|
|
1162
|
+
advance() {
|
|
1163
|
+
if (!this.isAtEnd()) this.current++;
|
|
1164
|
+
return this.previous();
|
|
1165
|
+
}
|
|
1166
|
+
check(type) {
|
|
1167
|
+
if (this.isAtEnd()) return false;
|
|
1168
|
+
return this.peek().type === type;
|
|
1169
|
+
}
|
|
1170
|
+
checkLexeme(lexeme) {
|
|
1171
|
+
if (this.isAtEnd()) return false;
|
|
1172
|
+
return this.peek().lexeme === lexeme;
|
|
1173
|
+
}
|
|
1174
|
+
match(...types) {
|
|
1175
|
+
for (const type of types) {
|
|
1176
|
+
if (this.check(type)) {
|
|
1177
|
+
this.advance();
|
|
1178
|
+
return true;
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
return false;
|
|
1182
|
+
}
|
|
1183
|
+
consume(type, message) {
|
|
1184
|
+
var _a, _b;
|
|
1185
|
+
if (this.check(type)) return this.advance();
|
|
1186
|
+
throw new Error(`${message}. Got: ${(_b = (_a = this.peek()) == null ? void 0 : _a.lexeme) != null ? _b : "EOF"}`);
|
|
1187
|
+
}
|
|
1188
|
+
// ==================== Expression Parsing ====================
|
|
1189
|
+
parseExpr() {
|
|
1190
|
+
return this.parseOrExpr();
|
|
1191
|
+
}
|
|
1192
|
+
parseOrExpr() {
|
|
1193
|
+
let left = this.parseAndExpr();
|
|
1194
|
+
while (this.check("OPERATOR") && this.peek().lexeme === "or") {
|
|
1195
|
+
this.advance();
|
|
1196
|
+
const right = this.parseAndExpr();
|
|
1197
|
+
left = new XPathLogicalExpression(left, right, "or");
|
|
1198
|
+
}
|
|
1199
|
+
return left;
|
|
1200
|
+
}
|
|
1201
|
+
parseAndExpr() {
|
|
1202
|
+
let left = this.parseEqualityExpr();
|
|
1203
|
+
while (this.check("OPERATOR") && this.peek().lexeme === "and") {
|
|
1204
|
+
this.advance();
|
|
1205
|
+
const right = this.parseEqualityExpr();
|
|
1206
|
+
left = new XPathLogicalExpression(left, right, "and");
|
|
1207
|
+
}
|
|
1208
|
+
return left;
|
|
1209
|
+
}
|
|
1210
|
+
parseEqualityExpr() {
|
|
1211
|
+
let left = this.parseRelationalExpr();
|
|
1212
|
+
while (this.match("EQUALS", "NOT_EQUALS")) {
|
|
1213
|
+
const operator = this.previous().lexeme;
|
|
1214
|
+
const right = this.parseRelationalExpr();
|
|
1215
|
+
left = new XPathBinaryExpression(left, right, operator);
|
|
1216
|
+
}
|
|
1217
|
+
return left;
|
|
1218
|
+
}
|
|
1219
|
+
parseRelationalExpr() {
|
|
1220
|
+
let left = this.parseAdditiveExpr();
|
|
1221
|
+
while (this.match("LESS_THAN", "GREATER_THAN", "LESS_THAN_OR_EQUAL", "GREATER_THAN_OR_EQUAL")) {
|
|
1222
|
+
const operator = this.previous().lexeme;
|
|
1223
|
+
const right = this.parseAdditiveExpr();
|
|
1224
|
+
left = new XPathBinaryExpression(left, right, operator);
|
|
1225
|
+
}
|
|
1226
|
+
return left;
|
|
1227
|
+
}
|
|
1228
|
+
parseAdditiveExpr() {
|
|
1229
|
+
let left = this.parseMultiplicativeExpr();
|
|
1230
|
+
while (this.match("PLUS", "MINUS")) {
|
|
1231
|
+
const operator = this.previous().lexeme;
|
|
1232
|
+
const right = this.parseMultiplicativeExpr();
|
|
1233
|
+
left = new XPathArithmeticExpression(left, right, operator);
|
|
1234
|
+
}
|
|
1235
|
+
return left;
|
|
1236
|
+
}
|
|
1237
|
+
parseMultiplicativeExpr() {
|
|
1238
|
+
let left = this.parseUnaryExpr();
|
|
1239
|
+
while (true) {
|
|
1240
|
+
if (this.match("ASTERISK")) {
|
|
1241
|
+
const right = this.parseUnaryExpr();
|
|
1242
|
+
left = new XPathArithmeticExpression(left, right, "*");
|
|
1243
|
+
} else if (this.check("OPERATOR") && (this.peek().lexeme === "div" || this.peek().lexeme === "mod")) {
|
|
1244
|
+
const operator = this.advance().lexeme;
|
|
1245
|
+
const right = this.parseUnaryExpr();
|
|
1246
|
+
left = new XPathArithmeticExpression(left, right, operator);
|
|
1247
|
+
} else {
|
|
1248
|
+
break;
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
return left;
|
|
1252
|
+
}
|
|
1253
|
+
parseUnaryExpr() {
|
|
1254
|
+
if (this.match("MINUS")) {
|
|
1255
|
+
const operand = this.parseUnaryExpr();
|
|
1256
|
+
return new XPathUnaryExpression("-", operand);
|
|
1257
|
+
}
|
|
1258
|
+
return this.parseUnionExpr();
|
|
1259
|
+
}
|
|
1260
|
+
parseUnionExpr() {
|
|
1261
|
+
let left = this.parsePathExpr();
|
|
1262
|
+
while (this.match("PIPE")) {
|
|
1263
|
+
const right = this.parsePathExpr();
|
|
1264
|
+
left = new XPathUnionExpression(left, right);
|
|
1265
|
+
}
|
|
1266
|
+
return left;
|
|
1267
|
+
}
|
|
1268
|
+
// ==================== Path Expression Parsing ====================
|
|
1269
|
+
parsePathExpr() {
|
|
1270
|
+
if (this.check("SLASH") || this.check("DOUBLE_SLASH")) {
|
|
1271
|
+
return this.parseLocationPath();
|
|
1272
|
+
}
|
|
1273
|
+
if (this.isStepStart()) {
|
|
1274
|
+
return this.parseLocationPath();
|
|
1275
|
+
}
|
|
1276
|
+
const filterExpr = this.parseFilterExpr();
|
|
1277
|
+
if (this.match("SLASH", "DOUBLE_SLASH")) {
|
|
1278
|
+
const isDescendant = this.previous().type === "DOUBLE_SLASH";
|
|
1279
|
+
const steps = this.parseRelativeLocationPath();
|
|
1280
|
+
if (isDescendant) {
|
|
1281
|
+
steps.unshift(new XPathStep("descendant-or-self", { type: "node-type", nodeType: "node" }));
|
|
1282
|
+
}
|
|
1283
|
+
return new XPathFilterExpression(filterExpr, new XPathLocationPath(steps, false));
|
|
1284
|
+
}
|
|
1285
|
+
return filterExpr;
|
|
1286
|
+
}
|
|
1287
|
+
isStepStart() {
|
|
1288
|
+
if (this.isAtEnd()) return false;
|
|
1289
|
+
const token = this.peek();
|
|
1290
|
+
if (token.type === "DOT" || token.type === "DOT_DOT") return true;
|
|
1291
|
+
if (token.type === "AT") return true;
|
|
1292
|
+
if (token.type === "LOCATION") return true;
|
|
1293
|
+
if (token.type === "NODE_TYPE") return true;
|
|
1294
|
+
if (token.type === "ASTERISK") return true;
|
|
1295
|
+
if (token.type === "IDENTIFIER" || token.type === "OPERATOR" || token.type === "FUNCTION") {
|
|
1296
|
+
const next = this.peekNext();
|
|
1297
|
+
return !next || next.type !== "OPEN_PAREN";
|
|
1298
|
+
}
|
|
1299
|
+
return false;
|
|
1300
|
+
}
|
|
1301
|
+
parseLocationPath() {
|
|
1302
|
+
let absolute = false;
|
|
1303
|
+
const steps = [];
|
|
1304
|
+
if (this.match("SLASH")) {
|
|
1305
|
+
absolute = true;
|
|
1306
|
+
if (!this.isAtEnd() && this.isStepStart()) {
|
|
1307
|
+
steps.push(...this.parseRelativeLocationPath());
|
|
1308
|
+
}
|
|
1309
|
+
} else if (this.match("DOUBLE_SLASH")) {
|
|
1310
|
+
absolute = true;
|
|
1311
|
+
steps.push(new XPathStep("descendant-or-self", { type: "node-type", nodeType: "node" }));
|
|
1312
|
+
steps.push(...this.parseRelativeLocationPath());
|
|
1313
|
+
} else {
|
|
1314
|
+
steps.push(...this.parseRelativeLocationPath());
|
|
1315
|
+
}
|
|
1316
|
+
return new XPathLocationPath(steps, absolute);
|
|
1317
|
+
}
|
|
1318
|
+
parseRelativeLocationPath() {
|
|
1319
|
+
const steps = [];
|
|
1320
|
+
steps.push(this.parseStep());
|
|
1321
|
+
while (this.match("SLASH", "DOUBLE_SLASH")) {
|
|
1322
|
+
const isDescendant = this.previous().type === "DOUBLE_SLASH";
|
|
1323
|
+
if (isDescendant) {
|
|
1324
|
+
steps.push(new XPathStep("descendant-or-self", { type: "node-type", nodeType: "node" }));
|
|
1325
|
+
}
|
|
1326
|
+
steps.push(this.parseStep());
|
|
1327
|
+
}
|
|
1328
|
+
return steps;
|
|
1329
|
+
}
|
|
1330
|
+
parseStep() {
|
|
1331
|
+
if (this.match("DOT")) {
|
|
1332
|
+
return new XPathStep("self", { type: "node-type", nodeType: "node" });
|
|
1333
|
+
}
|
|
1334
|
+
if (this.match("DOT_DOT")) {
|
|
1335
|
+
return new XPathStep("parent", { type: "node-type", nodeType: "node" });
|
|
1336
|
+
}
|
|
1337
|
+
let axis = "child";
|
|
1338
|
+
if (this.match("AT")) {
|
|
1339
|
+
axis = "attribute";
|
|
1340
|
+
} else if (this.check("LOCATION")) {
|
|
1341
|
+
const next = this.peekNext();
|
|
1342
|
+
if (next && next.type === "COLON_COLON") {
|
|
1343
|
+
axis = this.advance().lexeme;
|
|
1344
|
+
this.advance();
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
const nodeTest = this.parseNodeTest();
|
|
1348
|
+
const predicates = this.parsePredicates();
|
|
1349
|
+
return new XPathStep(axis, nodeTest, predicates);
|
|
1350
|
+
}
|
|
1351
|
+
parseNodeTest() {
|
|
1352
|
+
var _a, _b;
|
|
1353
|
+
if (this.match("ASTERISK")) {
|
|
1354
|
+
return { type: "wildcard" };
|
|
1355
|
+
}
|
|
1356
|
+
if (this.check("NODE_TYPE")) {
|
|
1357
|
+
const next = this.peekNext();
|
|
1358
|
+
if (next && next.type === "OPEN_PAREN") {
|
|
1359
|
+
const nodeType = this.advance().lexeme;
|
|
1360
|
+
this.advance();
|
|
1361
|
+
if (nodeType === "processing-instruction" && this.check("STRING")) {
|
|
1362
|
+
const name = this.advance().lexeme;
|
|
1363
|
+
this.consume("CLOSE_PAREN", "Expected ')' after processing-instruction name");
|
|
1364
|
+
return { type: "processing-instruction", name };
|
|
1365
|
+
}
|
|
1366
|
+
this.consume("CLOSE_PAREN", "Expected ')' after node type");
|
|
1367
|
+
return { type: "node-type", nodeType };
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
if (this.check("IDENTIFIER") || this.check("LOCATION") || this.check("FUNCTION") || this.check("NODE_TYPE") || this.check("OPERATOR")) {
|
|
1371
|
+
const name = this.advance().lexeme;
|
|
1372
|
+
if (this.match("COLON")) {
|
|
1373
|
+
if (this.match("ASTERISK")) {
|
|
1374
|
+
return { type: "wildcard", name: `${name}:*` };
|
|
1375
|
+
}
|
|
1376
|
+
if (this.check("IDENTIFIER") || this.check("LOCATION") || this.check("FUNCTION") || this.check("NODE_TYPE") || this.check("OPERATOR")) {
|
|
1377
|
+
const localName = this.advance().lexeme;
|
|
1378
|
+
return { type: "name", name: `${name}:${localName}` };
|
|
1379
|
+
}
|
|
1380
|
+
throw new Error("Expected local name after namespace prefix");
|
|
1381
|
+
}
|
|
1382
|
+
return { type: "name", name };
|
|
1383
|
+
}
|
|
1384
|
+
throw new Error(`Expected node test, got: ${(_b = (_a = this.peek()) == null ? void 0 : _a.lexeme) != null ? _b : "EOF"}`);
|
|
1385
|
+
}
|
|
1386
|
+
parsePredicates() {
|
|
1387
|
+
const predicates = [];
|
|
1388
|
+
while (this.match("OPEN_SQUARE_BRACKET")) {
|
|
1389
|
+
const expr = this.parseExpr();
|
|
1390
|
+
this.consume("CLOSE_SQUARE_BRACKET", "Expected ']' after predicate");
|
|
1391
|
+
predicates.push(new XPathPredicate(expr));
|
|
1392
|
+
}
|
|
1393
|
+
return predicates;
|
|
1394
|
+
}
|
|
1395
|
+
// ==================== Filter Expression Parsing ====================
|
|
1396
|
+
parseFilterExpr() {
|
|
1397
|
+
let expr = this.parsePrimaryExpr();
|
|
1398
|
+
while (this.check("OPEN_SQUARE_BRACKET")) {
|
|
1399
|
+
const predicates = this.parsePredicates();
|
|
1400
|
+
for (const predicate of predicates) {
|
|
1401
|
+
expr = new XPathFilterExpression(expr, predicate);
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
return expr;
|
|
1405
|
+
}
|
|
1406
|
+
parsePrimaryExpr() {
|
|
1407
|
+
var _a, _b;
|
|
1408
|
+
if (this.match("DOLLAR")) {
|
|
1409
|
+
const name = this.consume("IDENTIFIER", "Expected variable name after $").lexeme;
|
|
1410
|
+
return new XPathVariableReference(name);
|
|
1411
|
+
}
|
|
1412
|
+
if (this.match("OPEN_PAREN")) {
|
|
1413
|
+
const expr = this.parseExpr();
|
|
1414
|
+
this.consume("CLOSE_PAREN", "Expected ')' after expression");
|
|
1415
|
+
return expr;
|
|
1416
|
+
}
|
|
1417
|
+
if (this.check("STRING")) {
|
|
1418
|
+
const value = this.advance().lexeme;
|
|
1419
|
+
return new XPathStringLiteral(value);
|
|
1420
|
+
}
|
|
1421
|
+
if (this.check("NUMBER")) {
|
|
1422
|
+
const value = parseFloat(this.advance().lexeme);
|
|
1423
|
+
return new XPathNumberLiteral(value);
|
|
1424
|
+
}
|
|
1425
|
+
if (this.check("FUNCTION") || this.check("IDENTIFIER")) {
|
|
1426
|
+
const next = this.peekNext();
|
|
1427
|
+
if (next && next.type === "OPEN_PAREN") {
|
|
1428
|
+
return this.parseFunctionCall();
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
throw new Error(`Unexpected token in primary expression: ${(_b = (_a = this.peek()) == null ? void 0 : _a.lexeme) != null ? _b : "EOF"}`);
|
|
1432
|
+
}
|
|
1433
|
+
parseFunctionCall() {
|
|
1434
|
+
const name = this.advance().lexeme;
|
|
1435
|
+
this.consume("OPEN_PAREN", "Expected '(' after function name");
|
|
1436
|
+
const args = [];
|
|
1437
|
+
if (!this.check("CLOSE_PAREN")) {
|
|
1438
|
+
do {
|
|
1439
|
+
args.push(this.parseExpr());
|
|
1440
|
+
} while (this.match("COMMA"));
|
|
1441
|
+
}
|
|
1442
|
+
this.consume("CLOSE_PAREN", "Expected ')' after function arguments");
|
|
1443
|
+
return new XPathFunctionCall(name, args);
|
|
1444
|
+
}
|
|
1445
|
+
};
|
|
1446
|
+
|
|
1447
|
+
// src/xpath/lib/src/context.ts
|
|
1448
|
+
function createContext(node, options) {
|
|
1449
|
+
return __spreadValues({
|
|
1450
|
+
node,
|
|
1451
|
+
position: 1,
|
|
1452
|
+
size: 1
|
|
1453
|
+
}, options);
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
// src/xpath/values/string-value.ts
|
|
1457
|
+
var StringValue = class {
|
|
1458
|
+
constructor(value) {
|
|
1459
|
+
this.value = value;
|
|
1460
|
+
this.type = "string";
|
|
1461
|
+
}
|
|
1462
|
+
stringValue() {
|
|
1463
|
+
return String(this.value);
|
|
1464
|
+
}
|
|
1465
|
+
booleanValue() {
|
|
1466
|
+
return this.value.length > 0;
|
|
1467
|
+
}
|
|
1468
|
+
numberValue() {
|
|
1469
|
+
return this.value - 0;
|
|
1470
|
+
}
|
|
1471
|
+
nodeSetValue() {
|
|
1472
|
+
throw this;
|
|
1473
|
+
}
|
|
1474
|
+
};
|
|
1475
|
+
|
|
1476
|
+
// src/xpath/values/number-value.ts
|
|
1477
|
+
var NumberValue = class {
|
|
1478
|
+
constructor(value) {
|
|
1479
|
+
this.value = value;
|
|
1480
|
+
this.type = "number";
|
|
1481
|
+
}
|
|
1482
|
+
stringValue() {
|
|
1483
|
+
return `${this.value}`;
|
|
1484
|
+
}
|
|
1485
|
+
booleanValue() {
|
|
1486
|
+
return !!this.value;
|
|
1487
|
+
}
|
|
1488
|
+
numberValue() {
|
|
1489
|
+
return this.value - 0;
|
|
1490
|
+
}
|
|
1491
|
+
nodeSetValue() {
|
|
1492
|
+
throw this;
|
|
1493
|
+
}
|
|
1494
|
+
};
|
|
1495
|
+
|
|
1496
|
+
// src/xpath/values/boolean-value.ts
|
|
1497
|
+
var BooleanValue = class {
|
|
1498
|
+
constructor(value) {
|
|
1499
|
+
this.value = value;
|
|
1500
|
+
this.type = "boolean";
|
|
1501
|
+
}
|
|
1502
|
+
stringValue() {
|
|
1503
|
+
return `${this.value}`;
|
|
1504
|
+
}
|
|
1505
|
+
booleanValue() {
|
|
1506
|
+
return this.value;
|
|
1507
|
+
}
|
|
1508
|
+
numberValue() {
|
|
1509
|
+
return this.value ? 1 : 0;
|
|
1510
|
+
}
|
|
1511
|
+
nodeSetValue() {
|
|
1512
|
+
throw this;
|
|
1513
|
+
}
|
|
1514
|
+
};
|
|
1515
|
+
|
|
1516
|
+
// src/dom/functions.ts
|
|
1517
|
+
function domGetAttributeValue(node, name) {
|
|
1518
|
+
return node.getAttributeValue(name);
|
|
1519
|
+
}
|
|
1520
|
+
function domSetAttribute(node, name, value) {
|
|
1521
|
+
return node.setAttribute(name, value);
|
|
1522
|
+
}
|
|
1523
|
+
function domAppendChild(node, child) {
|
|
1524
|
+
return node.appendChild(child);
|
|
1525
|
+
}
|
|
1526
|
+
function domCreateTextNode(node, text) {
|
|
1527
|
+
return node.createTextNode(text);
|
|
1528
|
+
}
|
|
1529
|
+
function domCreateElement(doc, name) {
|
|
1530
|
+
return doc.createElement(name);
|
|
1531
|
+
}
|
|
1532
|
+
function domCreateCDATASection(doc, data) {
|
|
1533
|
+
return doc.createCDATASection(data);
|
|
1534
|
+
}
|
|
1535
|
+
function domCreateComment(doc, text) {
|
|
1536
|
+
return doc.createComment(text);
|
|
1537
|
+
}
|
|
1538
|
+
function domCreateDocumentFragment(doc) {
|
|
1539
|
+
return doc.createDocumentFragment();
|
|
1540
|
+
}
|
|
1541
|
+
function domCreateDTDSection(doc, data) {
|
|
1542
|
+
return doc.createDTDSection(data);
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
// src/constants.ts
|
|
1546
|
+
var DOM_ELEMENT_NODE = 1;
|
|
1547
|
+
var DOM_ATTRIBUTE_NODE = 2;
|
|
1548
|
+
var DOM_TEXT_NODE = 3;
|
|
1549
|
+
var DOM_CDATA_SECTION_NODE = 4;
|
|
1550
|
+
var DOM_PROCESSING_INSTRUCTION_NODE = 7;
|
|
1551
|
+
var DOM_COMMENT_NODE = 8;
|
|
1552
|
+
var DOM_DOCUMENT_NODE = 9;
|
|
1553
|
+
var DOM_DOCUMENT_TYPE_NODE = 10;
|
|
1554
|
+
var DOM_DOCUMENT_FRAGMENT_NODE = 11;
|
|
1555
|
+
|
|
1556
|
+
// src/dom/xnode.ts
|
|
1557
|
+
var _XNode = class _XNode {
|
|
1558
|
+
constructor(type, name, opt_value, opt_owner, opt_namespace) {
|
|
1559
|
+
this.id = Math.random() * (Number.MAX_SAFE_INTEGER - 1) + 1;
|
|
1560
|
+
this.childNodes = [];
|
|
1561
|
+
this.visited = false;
|
|
1562
|
+
this.escape = true;
|
|
1563
|
+
this.siblingPosition = -1;
|
|
1564
|
+
this.init(type, name, opt_value, opt_owner, opt_namespace);
|
|
1565
|
+
}
|
|
1566
|
+
/**
|
|
1567
|
+
* Node initialization. Called by the constructor and `recycle` method.
|
|
1568
|
+
* @param type The node type.
|
|
1569
|
+
* @param name The node name.
|
|
1570
|
+
* @param value The node value.
|
|
1571
|
+
* @param owner The node owner.
|
|
1572
|
+
* @param namespaceUri The node namespace.
|
|
1573
|
+
*/
|
|
1574
|
+
init(type, name, value, owner, namespaceUri) {
|
|
1575
|
+
this.nodeType = type - 0;
|
|
1576
|
+
this.nodeName = `${name}`;
|
|
1577
|
+
this.nodeValue = `${value}`;
|
|
1578
|
+
this.ownerDocument = owner;
|
|
1579
|
+
this.namespaceUri = namespaceUri || null;
|
|
1580
|
+
[this.prefix, this.localName] = this.qualifiedNameToParts(`${name}`);
|
|
1581
|
+
this.firstChild = null;
|
|
1582
|
+
this.lastChild = null;
|
|
1583
|
+
this.nextSibling = null;
|
|
1584
|
+
this.previousSibling = null;
|
|
1585
|
+
this.parentNode = null;
|
|
1586
|
+
}
|
|
1587
|
+
qualifiedNameToParts(name) {
|
|
1588
|
+
if (name.includes(":")) {
|
|
1589
|
+
return name.split(":");
|
|
1590
|
+
}
|
|
1591
|
+
return [null, name];
|
|
1592
|
+
}
|
|
1593
|
+
// Traverses the element nodes in the DOM section underneath the given
|
|
1594
|
+
// node and invokes the given callbacks as methods on every element
|
|
1595
|
+
// node encountered. Function opt_pre is invoked before a node's
|
|
1596
|
+
// children are traversed; opt_post is invoked after they are
|
|
1597
|
+
// traversed. Traversal will not be continued if a callback function
|
|
1598
|
+
// returns boolean false. NOTE(mesch): copied from
|
|
1599
|
+
// <//google3/maps/webmaps/javascript/dom.js>.
|
|
1600
|
+
domTraverseElements(node, opt_pre, opt_post) {
|
|
1601
|
+
let ret;
|
|
1602
|
+
if (opt_pre) {
|
|
1603
|
+
ret = opt_pre.call(null, node);
|
|
1604
|
+
if (typeof ret == "boolean" && !ret) {
|
|
1605
|
+
return false;
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
for (let c = node.firstChild; c; c = c.nextSibling) {
|
|
1609
|
+
if (c.nodeType == DOM_ELEMENT_NODE) {
|
|
1610
|
+
ret = this.domTraverseElements.call(this, c, opt_pre, opt_post);
|
|
1611
|
+
if (typeof ret == "boolean" && !ret) {
|
|
1612
|
+
return false;
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
if (opt_post) {
|
|
1617
|
+
ret = opt_post.call(null, node);
|
|
1618
|
+
if (typeof ret == "boolean" && !ret) {
|
|
1619
|
+
return false;
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
// TODO: Do we still need this?
|
|
1624
|
+
static recycle(node) {
|
|
1625
|
+
if (!node) {
|
|
1626
|
+
return;
|
|
1627
|
+
}
|
|
1628
|
+
if (node.constructor.name === "XDocument") {
|
|
1629
|
+
this.recycle(node.documentElement);
|
|
1630
|
+
return;
|
|
1631
|
+
}
|
|
1632
|
+
if (node.constructor != this) {
|
|
1633
|
+
return;
|
|
1634
|
+
}
|
|
1635
|
+
this._unusedXNodes.push(node);
|
|
1636
|
+
for (let c = 0; c < node.childNodes.length; ++c) {
|
|
1637
|
+
this.recycle(node.childNodes[c]);
|
|
1638
|
+
}
|
|
1639
|
+
node.childNodes.length = 0;
|
|
1640
|
+
node.init.call(0, "", "", null);
|
|
1641
|
+
}
|
|
1642
|
+
static create(type, name, value, owner, namespace) {
|
|
1643
|
+
if (this._unusedXNodes.length > 0) {
|
|
1644
|
+
const node = this._unusedXNodes.pop();
|
|
1645
|
+
node.init(type, name, value, owner, namespace);
|
|
1646
|
+
return node;
|
|
1647
|
+
}
|
|
1648
|
+
return new _XNode(type, name, value, owner, namespace);
|
|
1649
|
+
}
|
|
1650
|
+
static clone(node, newOwner) {
|
|
1651
|
+
const newNode = new _XNode(node.nodeType, node.nodeName, node.nodeValue, newOwner, node.namespaceUri);
|
|
1652
|
+
newNode.id = node.id;
|
|
1653
|
+
for (let child of node.childNodes) {
|
|
1654
|
+
newNode.appendChild(_XNode.clone(child, newNode));
|
|
1655
|
+
}
|
|
1656
|
+
return newNode;
|
|
1657
|
+
}
|
|
1658
|
+
appendChild(node) {
|
|
1659
|
+
if (this.childNodes.length === 0) {
|
|
1660
|
+
this.firstChild = node;
|
|
1661
|
+
}
|
|
1662
|
+
node.previousSibling = this.lastChild;
|
|
1663
|
+
node.nextSibling = null;
|
|
1664
|
+
if (this.lastChild) {
|
|
1665
|
+
this.lastChild.nextSibling = node;
|
|
1666
|
+
}
|
|
1667
|
+
node.parentNode = this;
|
|
1668
|
+
this.lastChild = node;
|
|
1669
|
+
this.childNodes.push(node);
|
|
1670
|
+
}
|
|
1671
|
+
replaceChild(newNode, oldNode) {
|
|
1672
|
+
if (oldNode == newNode) {
|
|
1673
|
+
return;
|
|
1674
|
+
}
|
|
1675
|
+
for (let i = 0; i < this.childNodes.length; ++i) {
|
|
1676
|
+
if (this.childNodes[i] == oldNode) {
|
|
1677
|
+
this.childNodes[i] = newNode;
|
|
1678
|
+
let p = oldNode.parentNode;
|
|
1679
|
+
oldNode.parentNode = null;
|
|
1680
|
+
newNode.parentNode = p;
|
|
1681
|
+
p = oldNode.previousSibling;
|
|
1682
|
+
oldNode.previousSibling = null;
|
|
1683
|
+
newNode.previousSibling = p;
|
|
1684
|
+
if (newNode.previousSibling) {
|
|
1685
|
+
newNode.previousSibling.nextSibling = newNode;
|
|
1686
|
+
}
|
|
1687
|
+
p = oldNode.nextSibling;
|
|
1688
|
+
oldNode.nextSibling = null;
|
|
1689
|
+
newNode.nextSibling = p;
|
|
1690
|
+
if (newNode.nextSibling) {
|
|
1691
|
+
newNode.nextSibling.previousSibling = newNode;
|
|
1692
|
+
}
|
|
1693
|
+
if (this.firstChild == oldNode) {
|
|
1694
|
+
this.firstChild = newNode;
|
|
1695
|
+
}
|
|
1696
|
+
if (this.lastChild == oldNode) {
|
|
1697
|
+
this.lastChild = newNode;
|
|
1698
|
+
}
|
|
1699
|
+
break;
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
insertBefore(newNode, oldNode) {
|
|
1704
|
+
if (oldNode == newNode) {
|
|
1705
|
+
return;
|
|
1706
|
+
}
|
|
1707
|
+
if (oldNode.parentNode != this) {
|
|
1708
|
+
return;
|
|
1709
|
+
}
|
|
1710
|
+
if (newNode.parentNode) {
|
|
1711
|
+
newNode.parentNode.removeChild(newNode);
|
|
1712
|
+
}
|
|
1713
|
+
const newChildren = [];
|
|
1714
|
+
for (const c of this.childNodes) {
|
|
1715
|
+
if (c == oldNode) {
|
|
1716
|
+
newChildren.push(newNode);
|
|
1717
|
+
newNode.parentNode = this;
|
|
1718
|
+
newNode.previousSibling = oldNode.previousSibling;
|
|
1719
|
+
oldNode.previousSibling = newNode;
|
|
1720
|
+
if (newNode.previousSibling) {
|
|
1721
|
+
newNode.previousSibling.nextSibling = newNode;
|
|
1722
|
+
}
|
|
1723
|
+
newNode.nextSibling = oldNode;
|
|
1724
|
+
if (this.firstChild == oldNode) {
|
|
1725
|
+
this.firstChild = newNode;
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
newChildren.push(c);
|
|
1729
|
+
}
|
|
1730
|
+
this.childNodes = newChildren;
|
|
1731
|
+
}
|
|
1732
|
+
removeChild(node) {
|
|
1733
|
+
const newChildren = [];
|
|
1734
|
+
for (const c of this.childNodes) {
|
|
1735
|
+
if (c != node) {
|
|
1736
|
+
newChildren.push(c);
|
|
1737
|
+
} else {
|
|
1738
|
+
if (c.previousSibling) {
|
|
1739
|
+
c.previousSibling.nextSibling = c.nextSibling;
|
|
1740
|
+
}
|
|
1741
|
+
if (c.nextSibling) {
|
|
1742
|
+
c.nextSibling.previousSibling = c.previousSibling;
|
|
1743
|
+
}
|
|
1744
|
+
if (this.firstChild == c) {
|
|
1745
|
+
this.firstChild = c.nextSibling;
|
|
1746
|
+
}
|
|
1747
|
+
if (this.lastChild == c) {
|
|
1748
|
+
this.lastChild = c.previousSibling;
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
this.childNodes = newChildren;
|
|
1753
|
+
}
|
|
1754
|
+
hasAttributes() {
|
|
1755
|
+
const attributes = this.childNodes.filter((n) => n.nodeType === DOM_ATTRIBUTE_NODE);
|
|
1756
|
+
return attributes.length > 0;
|
|
1757
|
+
}
|
|
1758
|
+
setAttribute(name, value) {
|
|
1759
|
+
const attributes = this.childNodes.filter((n) => n.nodeType === DOM_ATTRIBUTE_NODE);
|
|
1760
|
+
for (let i = 0; i < attributes.length; ++i) {
|
|
1761
|
+
if (attributes[i].nodeName == name) {
|
|
1762
|
+
attributes[i].nodeValue = `${value}`;
|
|
1763
|
+
return;
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
const newAttribute = _XNode.create(DOM_ATTRIBUTE_NODE, name, value, this);
|
|
1767
|
+
newAttribute.parentNode = this;
|
|
1768
|
+
this.appendChild(newAttribute);
|
|
1769
|
+
}
|
|
1770
|
+
setAttributeNS(namespace, name, value) {
|
|
1771
|
+
const attributes = this.childNodes.filter((n) => n.nodeType === DOM_ATTRIBUTE_NODE);
|
|
1772
|
+
for (let i = 0; i < attributes.length; ++i) {
|
|
1773
|
+
const attribute = attributes[i];
|
|
1774
|
+
if (attribute.namespaceUri == namespace && attribute.localName == this.qualifiedNameToParts(`${name}`)[1]) {
|
|
1775
|
+
attribute.nodeValue = `${value}`;
|
|
1776
|
+
attribute.nodeName = `${name}`;
|
|
1777
|
+
attribute.prefix = this.qualifiedNameToParts(`${name}`)[0];
|
|
1778
|
+
return;
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
const newAttribute = _XNode.create(DOM_ATTRIBUTE_NODE, name, value, this, namespace);
|
|
1782
|
+
newAttribute.parentNode = this;
|
|
1783
|
+
this.appendChild(newAttribute);
|
|
1784
|
+
}
|
|
1785
|
+
getAttributeValue(name) {
|
|
1786
|
+
const attributes = this.childNodes.filter((n) => n.nodeType === DOM_ATTRIBUTE_NODE);
|
|
1787
|
+
for (let i = 0; i < attributes.length; ++i) {
|
|
1788
|
+
if (attributes[i].nodeName === name) {
|
|
1789
|
+
return attributes[i].nodeValue;
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
return null;
|
|
1793
|
+
}
|
|
1794
|
+
getAttributeNS(namespace, localName) {
|
|
1795
|
+
const attributes = this.childNodes.filter((n) => n.nodeType === DOM_ATTRIBUTE_NODE);
|
|
1796
|
+
for (let i = 0; i < attributes.length; ++i) {
|
|
1797
|
+
const attribute = attributes[i];
|
|
1798
|
+
if (attribute.namespaceUri === namespace && attribute.localName === localName) {
|
|
1799
|
+
return attribute.nodeValue;
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
return null;
|
|
1803
|
+
}
|
|
1804
|
+
hasAttribute(name) {
|
|
1805
|
+
const attributes = this.childNodes.filter((n) => n.nodeType === DOM_ATTRIBUTE_NODE);
|
|
1806
|
+
for (let i = 0; i < attributes.length; ++i) {
|
|
1807
|
+
if (attributes[i].nodeName === name) {
|
|
1808
|
+
return true;
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
return false;
|
|
1812
|
+
}
|
|
1813
|
+
hasAttributeNS(namespace, localName) {
|
|
1814
|
+
const attributes = this.childNodes.filter((n) => n.nodeType === DOM_ATTRIBUTE_NODE);
|
|
1815
|
+
for (let i = 0; i < attributes.length; ++i) {
|
|
1816
|
+
const attribute = attributes[i];
|
|
1817
|
+
if (attribute.namespaceUri === namespace && attribute.localName === localName) {
|
|
1818
|
+
return true;
|
|
1819
|
+
}
|
|
1820
|
+
}
|
|
1821
|
+
return false;
|
|
1822
|
+
}
|
|
1823
|
+
removeAttribute(name) {
|
|
1824
|
+
const newChildNodes = [];
|
|
1825
|
+
for (let i = 0; i < this.childNodes.length; ++i) {
|
|
1826
|
+
const childNode = this.childNodes[i];
|
|
1827
|
+
if (childNode.nodeType !== DOM_ATTRIBUTE_NODE) {
|
|
1828
|
+
newChildNodes.push(childNode);
|
|
1829
|
+
continue;
|
|
1830
|
+
}
|
|
1831
|
+
if (childNode.nodeName !== name) {
|
|
1832
|
+
newChildNodes.push(childNode);
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
this.childNodes = newChildNodes;
|
|
1836
|
+
}
|
|
1837
|
+
removeAttributeNS(namespace, localName) {
|
|
1838
|
+
const newChildNodes = [];
|
|
1839
|
+
for (let i = 0; i < this.childNodes.length; ++i) {
|
|
1840
|
+
const childNode = this.childNodes[i];
|
|
1841
|
+
if (childNode.nodeType !== DOM_ATTRIBUTE_NODE) {
|
|
1842
|
+
newChildNodes.push(childNode);
|
|
1843
|
+
continue;
|
|
1844
|
+
}
|
|
1845
|
+
if (childNode.localName !== localName || childNode.namespaceUri !== namespace) {
|
|
1846
|
+
newChildNodes.push(childNode);
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
this.childNodes = newChildNodes;
|
|
1850
|
+
}
|
|
1851
|
+
getElementsByTagName(name) {
|
|
1852
|
+
const ret = [];
|
|
1853
|
+
const self = this;
|
|
1854
|
+
if ("*" == name) {
|
|
1855
|
+
this.domTraverseElements(
|
|
1856
|
+
this,
|
|
1857
|
+
(node) => {
|
|
1858
|
+
if (self == node) return;
|
|
1859
|
+
ret.push(node);
|
|
1860
|
+
},
|
|
1861
|
+
null
|
|
1862
|
+
);
|
|
1863
|
+
} else {
|
|
1864
|
+
this.domTraverseElements(
|
|
1865
|
+
this,
|
|
1866
|
+
(node) => {
|
|
1867
|
+
if (self == node) return;
|
|
1868
|
+
if (node.nodeName == name) {
|
|
1869
|
+
ret.push(node);
|
|
1870
|
+
}
|
|
1871
|
+
},
|
|
1872
|
+
null
|
|
1873
|
+
);
|
|
1874
|
+
}
|
|
1875
|
+
return ret;
|
|
1876
|
+
}
|
|
1877
|
+
getElementsByTagNameNS(namespace, localName) {
|
|
1878
|
+
const ret = [];
|
|
1879
|
+
const self = this;
|
|
1880
|
+
if ("*" == namespace && "*" == localName) {
|
|
1881
|
+
this.domTraverseElements(
|
|
1882
|
+
this,
|
|
1883
|
+
(node) => {
|
|
1884
|
+
if (self == node) return;
|
|
1885
|
+
ret.push(node);
|
|
1886
|
+
},
|
|
1887
|
+
null
|
|
1888
|
+
);
|
|
1889
|
+
} else if ("*" == namespace) {
|
|
1890
|
+
this.domTraverseElements(
|
|
1891
|
+
this,
|
|
1892
|
+
(node) => {
|
|
1893
|
+
if (self == node) return;
|
|
1894
|
+
if (node.localName == localName) ret.push(node);
|
|
1895
|
+
},
|
|
1896
|
+
null
|
|
1897
|
+
);
|
|
1898
|
+
} else if ("*" == localName) {
|
|
1899
|
+
this.domTraverseElements(
|
|
1900
|
+
this,
|
|
1901
|
+
(node) => {
|
|
1902
|
+
if (self == node) return;
|
|
1903
|
+
if (node.namespaceUri == namespace) ret.push(node);
|
|
1904
|
+
},
|
|
1905
|
+
null
|
|
1906
|
+
);
|
|
1907
|
+
} else {
|
|
1908
|
+
this.domTraverseElements(
|
|
1909
|
+
this,
|
|
1910
|
+
(node) => {
|
|
1911
|
+
if (self == node) return;
|
|
1912
|
+
if (node.localName == localName && node.namespaceUri == namespace) {
|
|
1913
|
+
ret.push(node);
|
|
1914
|
+
}
|
|
1915
|
+
},
|
|
1916
|
+
null
|
|
1917
|
+
);
|
|
1918
|
+
}
|
|
1919
|
+
return ret;
|
|
1920
|
+
}
|
|
1921
|
+
getElementById(id) {
|
|
1922
|
+
let ret = null;
|
|
1923
|
+
this.domTraverseElements(
|
|
1924
|
+
this,
|
|
1925
|
+
(node) => {
|
|
1926
|
+
if (node.getAttributeValue("id") == id) {
|
|
1927
|
+
ret = node;
|
|
1928
|
+
return false;
|
|
1929
|
+
}
|
|
1930
|
+
},
|
|
1931
|
+
null
|
|
1932
|
+
);
|
|
1933
|
+
return ret;
|
|
1934
|
+
}
|
|
1935
|
+
getAncestorByLocalName(localName) {
|
|
1936
|
+
if (this.parentNode === null || this.parentNode === void 0) {
|
|
1937
|
+
return void 0;
|
|
1938
|
+
}
|
|
1939
|
+
if (this.parentNode.localName === localName) {
|
|
1940
|
+
return this.parentNode;
|
|
1941
|
+
}
|
|
1942
|
+
return this.parentNode.getAncestorByLocalName(localName);
|
|
1943
|
+
}
|
|
1944
|
+
getAncestorById(id) {
|
|
1945
|
+
if (this.parentNode === null || this.parentNode === void 0) {
|
|
1946
|
+
return void 0;
|
|
1947
|
+
}
|
|
1948
|
+
if (this.parentNode.id === id) {
|
|
1949
|
+
return this.parentNode;
|
|
1950
|
+
}
|
|
1951
|
+
return this.parentNode.getAncestorById(id);
|
|
1952
|
+
}
|
|
1953
|
+
toString() {
|
|
1954
|
+
return `${this.nodeType}, ${this.nodeName}, ${this.nodeValue}`;
|
|
1955
|
+
}
|
|
1956
|
+
};
|
|
1957
|
+
_XNode._unusedXNodes = [];
|
|
1958
|
+
var XNode = _XNode;
|
|
1959
|
+
|
|
1960
|
+
// src/dom/xdocument.ts
|
|
1961
|
+
var XDocument = class extends XNode {
|
|
1962
|
+
constructor() {
|
|
1963
|
+
super(DOM_DOCUMENT_NODE, "#document", null, null);
|
|
1964
|
+
this.documentElement = null;
|
|
1965
|
+
}
|
|
1966
|
+
// TODO: Do we still need this?
|
|
1967
|
+
/* clear() {
|
|
1968
|
+
XNode.recycle(this.documentElement);
|
|
1969
|
+
this.documentElement = null;
|
|
1970
|
+
} */
|
|
1971
|
+
appendChild(node) {
|
|
1972
|
+
super.appendChild(node);
|
|
1973
|
+
this.documentElement = this.childNodes[0];
|
|
1974
|
+
}
|
|
1975
|
+
createElement(name) {
|
|
1976
|
+
return XNode.create(DOM_ELEMENT_NODE, name, null, this);
|
|
1977
|
+
}
|
|
1978
|
+
createElementNS(namespace, name) {
|
|
1979
|
+
return XNode.create(DOM_ELEMENT_NODE, name, null, this, namespace);
|
|
1980
|
+
}
|
|
1981
|
+
createDocumentFragment() {
|
|
1982
|
+
return XNode.create(DOM_DOCUMENT_FRAGMENT_NODE, "#document-fragment", null, this);
|
|
1983
|
+
}
|
|
1984
|
+
createTextNode(value) {
|
|
1985
|
+
return XNode.create(DOM_TEXT_NODE, "#text", value, this);
|
|
1986
|
+
}
|
|
1987
|
+
createAttribute(name) {
|
|
1988
|
+
return XNode.create(DOM_ATTRIBUTE_NODE, name, null, this);
|
|
1989
|
+
}
|
|
1990
|
+
createAttributeNS(namespace, name) {
|
|
1991
|
+
return XNode.create(DOM_ATTRIBUTE_NODE, name, null, this, namespace);
|
|
1992
|
+
}
|
|
1993
|
+
createComment(data) {
|
|
1994
|
+
return XNode.create(DOM_COMMENT_NODE, "#comment", data, this);
|
|
1995
|
+
}
|
|
1996
|
+
createCDATASection(data) {
|
|
1997
|
+
return XNode.create(DOM_CDATA_SECTION_NODE, "#cdata-section", data, this);
|
|
1998
|
+
}
|
|
1999
|
+
createDTDSection(data) {
|
|
2000
|
+
return XNode.create(DOM_DOCUMENT_TYPE_NODE, "#dtd-section", data, this);
|
|
2001
|
+
}
|
|
2002
|
+
};
|
|
2003
|
+
|
|
2004
|
+
// src/dom/xml-functions.ts
|
|
2005
|
+
var import_he = __toESM(require("he"));
|
|
2006
|
+
function xmlValue(node, disallowBrowserSpecificOptimization = false) {
|
|
2007
|
+
if (!node) {
|
|
2008
|
+
return "";
|
|
2009
|
+
}
|
|
2010
|
+
let ret = "";
|
|
2011
|
+
switch (node.nodeType) {
|
|
2012
|
+
case DOM_DOCUMENT_TYPE_NODE:
|
|
2013
|
+
return `<!DOCTYPE ${node.nodeValue}>`;
|
|
2014
|
+
case DOM_TEXT_NODE:
|
|
2015
|
+
case DOM_CDATA_SECTION_NODE:
|
|
2016
|
+
case DOM_ATTRIBUTE_NODE:
|
|
2017
|
+
return node.nodeValue;
|
|
2018
|
+
case DOM_ELEMENT_NODE:
|
|
2019
|
+
case DOM_DOCUMENT_NODE:
|
|
2020
|
+
case DOM_DOCUMENT_FRAGMENT_NODE:
|
|
2021
|
+
if (!disallowBrowserSpecificOptimization) {
|
|
2022
|
+
const browserNode = node;
|
|
2023
|
+
const innerText = browserNode.innerText;
|
|
2024
|
+
if (innerText !== void 0) {
|
|
2025
|
+
return innerText;
|
|
2026
|
+
}
|
|
2027
|
+
const textContent = browserNode.textContent;
|
|
2028
|
+
if (textContent !== void 0) {
|
|
2029
|
+
return textContent;
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
const textNodes = node.childNodes.filter((n) => n.nodeType !== DOM_ATTRIBUTE_NODE);
|
|
2033
|
+
for (let i = 0; i < textNodes.length; ++i) {
|
|
2034
|
+
ret += xmlValue(textNodes[i]);
|
|
2035
|
+
}
|
|
2036
|
+
return ret;
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
function xmlValueLegacyBehavior(node, disallowBrowserSpecificOptimization = false) {
|
|
2040
|
+
if (!node) {
|
|
2041
|
+
return "";
|
|
2042
|
+
}
|
|
2043
|
+
let returnedXmlString = "";
|
|
2044
|
+
switch (node.nodeType) {
|
|
2045
|
+
case DOM_ATTRIBUTE_NODE:
|
|
2046
|
+
case DOM_TEXT_NODE:
|
|
2047
|
+
returnedXmlString += node.nodeValue;
|
|
2048
|
+
break;
|
|
2049
|
+
case DOM_CDATA_SECTION_NODE:
|
|
2050
|
+
returnedXmlString += node.nodeValue;
|
|
2051
|
+
break;
|
|
2052
|
+
case DOM_DOCUMENT_NODE:
|
|
2053
|
+
case DOM_DOCUMENT_FRAGMENT_NODE:
|
|
2054
|
+
case DOM_ELEMENT_NODE:
|
|
2055
|
+
if (!disallowBrowserSpecificOptimization) {
|
|
2056
|
+
const browserNode = node;
|
|
2057
|
+
const innerText = browserNode.innerText;
|
|
2058
|
+
if (innerText !== void 0) {
|
|
2059
|
+
return innerText;
|
|
2060
|
+
}
|
|
2061
|
+
const textContent = browserNode.textContent;
|
|
2062
|
+
if (textContent !== void 0) {
|
|
2063
|
+
return textContent;
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
const len = node.childNodes.length;
|
|
2067
|
+
for (let i = 0; i < len; ++i) {
|
|
2068
|
+
returnedXmlString += xmlValue(node.childNodes[i]);
|
|
2069
|
+
}
|
|
2070
|
+
break;
|
|
2071
|
+
}
|
|
2072
|
+
return returnedXmlString;
|
|
2073
|
+
}
|
|
2074
|
+
function xmlTransformedText(node, options = {
|
|
2075
|
+
cData: true,
|
|
2076
|
+
escape: true,
|
|
2077
|
+
selfClosingTags: true,
|
|
2078
|
+
outputMethod: "xml"
|
|
2079
|
+
}) {
|
|
2080
|
+
const buffer = [];
|
|
2081
|
+
xmlTransformedTextRecursive(node, buffer, options);
|
|
2082
|
+
return buffer.join("");
|
|
2083
|
+
}
|
|
2084
|
+
function xmlTransformedTextRecursive(node, buffer, options) {
|
|
2085
|
+
if (node.visited) return;
|
|
2086
|
+
const nodeType = node.nodeType;
|
|
2087
|
+
const nodeValue = node.nodeValue;
|
|
2088
|
+
if (nodeType === DOM_TEXT_NODE) {
|
|
2089
|
+
if (node.nodeValue && node.nodeValue.trim() !== "") {
|
|
2090
|
+
const finalText = node.escape && options.escape ? xmlEscapeText(node.nodeValue) : xmlUnescapeText(node.nodeValue);
|
|
2091
|
+
buffer.push(finalText);
|
|
2092
|
+
}
|
|
2093
|
+
} else if (nodeType === DOM_CDATA_SECTION_NODE) {
|
|
2094
|
+
if (options.cData) {
|
|
2095
|
+
buffer.push(xmlEscapeText(nodeValue));
|
|
2096
|
+
} else {
|
|
2097
|
+
buffer.push(`<![CDATA[${nodeValue}]]>`);
|
|
2098
|
+
}
|
|
2099
|
+
} else if (nodeType == DOM_COMMENT_NODE) {
|
|
2100
|
+
buffer.push(`<!-- ${nodeValue} -->`);
|
|
2101
|
+
} else if (nodeType == DOM_ELEMENT_NODE) {
|
|
2102
|
+
if (node.nodeName !== null && node.nodeName !== void 0) {
|
|
2103
|
+
xmlElementLogicTrivial(node, buffer, options);
|
|
2104
|
+
} else {
|
|
2105
|
+
xmlElementLogicMuted(node, buffer, options);
|
|
2106
|
+
}
|
|
2107
|
+
} else if (nodeType === DOM_DOCUMENT_NODE || nodeType === DOM_DOCUMENT_FRAGMENT_NODE) {
|
|
2108
|
+
let childNodes = node.firstChild ? [] : node.childNodes;
|
|
2109
|
+
if (node.firstChild) {
|
|
2110
|
+
let child = node.firstChild;
|
|
2111
|
+
while (child) {
|
|
2112
|
+
childNodes.push(child);
|
|
2113
|
+
child = child.nextSibling;
|
|
2114
|
+
}
|
|
2115
|
+
}
|
|
2116
|
+
childNodes.sort((a, b) => a.siblingPosition - b.siblingPosition);
|
|
2117
|
+
for (let i = 0; i < childNodes.length; ++i) {
|
|
2118
|
+
xmlTransformedTextRecursive(childNodes[i], buffer, options);
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
2121
|
+
node.visited = true;
|
|
2122
|
+
}
|
|
2123
|
+
function xmlElementLogicTrivial(node, buffer, options) {
|
|
2124
|
+
buffer.push(`<${xmlFullNodeName(node)}`);
|
|
2125
|
+
let attributes = [];
|
|
2126
|
+
if (node.firstChild) {
|
|
2127
|
+
let child = node.firstChild;
|
|
2128
|
+
while (child) {
|
|
2129
|
+
if (child.nodeType === DOM_ATTRIBUTE_NODE) {
|
|
2130
|
+
attributes.push(child);
|
|
2131
|
+
}
|
|
2132
|
+
child = child.nextSibling;
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
2135
|
+
if (attributes.length === 0) {
|
|
2136
|
+
attributes = node.childNodes.filter((n) => n.nodeType === DOM_ATTRIBUTE_NODE);
|
|
2137
|
+
}
|
|
2138
|
+
for (let i = 0; i < attributes.length; ++i) {
|
|
2139
|
+
const attribute = attributes[i];
|
|
2140
|
+
if (!attribute) {
|
|
2141
|
+
continue;
|
|
2142
|
+
}
|
|
2143
|
+
if (attribute.nodeName && attribute.nodeValue) {
|
|
2144
|
+
buffer.push(` ${xmlFullNodeName(attribute)}="${xmlEscapeAttr(attribute.nodeValue)}"`);
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
let childNodes = [];
|
|
2148
|
+
if (node.firstChild) {
|
|
2149
|
+
let child = node.firstChild;
|
|
2150
|
+
while (child) {
|
|
2151
|
+
if (child.nodeType !== DOM_ATTRIBUTE_NODE) {
|
|
2152
|
+
childNodes.push(child);
|
|
2153
|
+
}
|
|
2154
|
+
child = child.nextSibling;
|
|
2155
|
+
}
|
|
2156
|
+
}
|
|
2157
|
+
if (childNodes.length === 0) {
|
|
2158
|
+
childNodes = node.childNodes.filter((n) => n.nodeType !== DOM_ATTRIBUTE_NODE);
|
|
2159
|
+
}
|
|
2160
|
+
childNodes = childNodes.sort((a, b) => a.siblingPosition - b.siblingPosition);
|
|
2161
|
+
if (childNodes.length === 0) {
|
|
2162
|
+
if (options.outputMethod === "html" && ["hr", "link", "meta"].includes(node.nodeName)) {
|
|
2163
|
+
buffer.push(">");
|
|
2164
|
+
} else if (options.selfClosingTags) {
|
|
2165
|
+
buffer.push("/>");
|
|
2166
|
+
} else {
|
|
2167
|
+
buffer.push(`></${xmlFullNodeName(node)}>`);
|
|
2168
|
+
}
|
|
2169
|
+
} else {
|
|
2170
|
+
buffer.push(">");
|
|
2171
|
+
for (let i = 0; i < childNodes.length; ++i) {
|
|
2172
|
+
xmlTransformedTextRecursive(childNodes[i], buffer, options);
|
|
2173
|
+
}
|
|
2174
|
+
buffer.push(`</${xmlFullNodeName(node)}>`);
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
function xmlElementLogicMuted(node, buffer, options) {
|
|
2178
|
+
let childNodes = [];
|
|
2179
|
+
if (node.firstChild) {
|
|
2180
|
+
let child = node.firstChild;
|
|
2181
|
+
while (child) {
|
|
2182
|
+
childNodes.push(child);
|
|
2183
|
+
child = child.nextSibling;
|
|
2184
|
+
}
|
|
2185
|
+
} else {
|
|
2186
|
+
childNodes = node.childNodes;
|
|
2187
|
+
}
|
|
2188
|
+
childNodes = childNodes.sort((a, b) => a.siblingPosition - b.siblingPosition);
|
|
2189
|
+
for (let i = 0; i < childNodes.length; ++i) {
|
|
2190
|
+
xmlTransformedTextRecursive(childNodes[i], buffer, options);
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
function xmlFullNodeName(node) {
|
|
2194
|
+
const nodeName = node.nodeName;
|
|
2195
|
+
if (node.prefix && nodeName.indexOf(`${node.prefix}:`) != 0) {
|
|
2196
|
+
return `${node.prefix}:${nodeName}`;
|
|
2197
|
+
}
|
|
2198
|
+
return nodeName;
|
|
2199
|
+
}
|
|
2200
|
+
function xmlUnescapeText(text) {
|
|
2201
|
+
return `${text}`.replace(/</g, "<").replace(/>/g, ">");
|
|
2202
|
+
}
|
|
2203
|
+
function xmlEscapeText(s) {
|
|
2204
|
+
return `${s}`.replace(/&/g, "&").replace(/&amp;/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
2205
|
+
}
|
|
2206
|
+
function xmlEscapeAttr(s) {
|
|
2207
|
+
return xmlEscapeText(s).replace(/"/g, """);
|
|
2208
|
+
}
|
|
2209
|
+
function xmlGetAttribute(node, name) {
|
|
2210
|
+
const value = domGetAttributeValue(node, name);
|
|
2211
|
+
if (value) {
|
|
2212
|
+
return import_he.default.decode(value);
|
|
2213
|
+
}
|
|
2214
|
+
return value;
|
|
2215
|
+
}
|
|
2216
|
+
|
|
2217
|
+
// src/dom/xml-parser.ts
|
|
2218
|
+
var import_he2 = __toESM(require("he"));
|
|
2219
|
+
|
|
2220
|
+
// src/dom/xmltoken.ts
|
|
2221
|
+
var XML_S = "[ \r\n]+";
|
|
2222
|
+
var XML_EQ = `(${XML_S})?=(${XML_S})?`;
|
|
2223
|
+
var XML_CHAR_REF = "&#[0-9]+;|&#x[0-9a-fA-F]+;";
|
|
2224
|
+
var XML10_VERSION_INFO = `${XML_S}version${XML_EQ}("1\\.0"|'1\\.0')`;
|
|
2225
|
+
var XML10_BASE_CHAR = "A-Za-z\xC0-\xD6\xD8-\xF6\xF8-\xFF\u0100-\u0131\u0134-\u013E\u0141-\u0148\u014A-\u017E\u0180-\u01C3\u01CD-\u01F0\u01F4-\u01F5\u01FA-\u0217\u0250-\u02A8\u02BB-\u02C1\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03CE\u03D0-\u03D6\u03DA\u03DC\u03DE\u03E0\u03E2-\u03F3\u0401-\u040C\u040E-\u044F\u0451-\u045C\u045E-\u0481\u0490-\u04C4\u04C7-\u04C8\u04CB-\u04CC\u04D0-\u04EB\u04EE-\u04F5\u04F8-\u04F9\u0531-\u0556\u0559\u0561-\u0586\u05D0-\u05EA\u05F0-\u05F2\u0621-\u063A\u0641-\u064A\u0671-\u06B7\u06BA-\u06BE\u06C0-\u06CE\u06D0-\u06D3\u06D5\u06E5-\u06E6\u0905-\u0939\u093D\u0958-\u0961\u0985-\u098C\u098F-\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09DC-\u09DD\u09DF-\u09E1\u09F0-\u09F1\u0A05-\u0A0A\u0A0F-\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32-\u0A33\u0A35-\u0A36\u0A38-\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8B\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2-\u0AB3\u0AB5-\u0AB9\u0ABD\u0AE0\u0B05-\u0B0C\u0B0F-\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32-\u0B33\u0B36-\u0B39\u0B3D\u0B5C-\u0B5D\u0B5F-\u0B61\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99-\u0B9A\u0B9C\u0B9E-\u0B9F\u0BA3-\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB5\u0BB7-\u0BB9\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C60-\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CDE\u0CE0-\u0CE1\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D28\u0D2A-\u0D39\u0D60-\u0D61\u0E01-\u0E2E\u0E30\u0E32-\u0E33\u0E40-\u0E45\u0E81-\u0E82\u0E84\u0E87-\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA-\u0EAB\u0EAD-\u0EAE\u0EB0\u0EB2-\u0EB3\u0EBD\u0EC0-\u0EC4\u0F40-\u0F47\u0F49-\u0F69\u10A0-\u10C5\u10D0-\u10F6\u1100\u1102-\u1103\u1105-\u1107\u1109\u110B-\u110C\u110E-\u1112\u113C\u113E\u1140\u114C\u114E\u1150\u1154-\u1155\u1159\u115F-\u1161\u1163\u1165\u1167\u1169\u116D-\u116E\u1172-\u1173\u1175\u119E\u11A8\u11AB\u11AE-\u11AF\u11B7-\u11B8\u11BA\u11BC-\u11C2\u11EB\u11F0\u11F9\u1E00-\u1E9B\u1EA0-\u1EF9\u1F00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2126\u212A-\u212B\u212E\u2180-\u2182\u3041-\u3094\u30A1-\u30FA\u3105-\u312C\uAC00-\uD7A3";
|
|
2226
|
+
var XML10_IDEOGRAPHIC = "\u4E00-\u9FA5\u3007\u3021-\u3029";
|
|
2227
|
+
var XML10_COMBINING_CHAR = "\u0300-\u0345\u0360-\u0361\u0483-\u0486\u0591-\u05A1\u05A3-\u05B9\u05BB-\u05BD\u05BF\u05C1-\u05C2\u05C4\u064B-\u0652\u0670\u06D6-\u06DC\u06DD-\u06DF\u06E0-\u06E4\u06E7-\u06E8\u06EA-\u06ED\u0901-\u0903\u093C\u093E-\u094C\u094D\u0951-\u0954\u0962-\u0963\u0981-\u0983\u09BC\u09BE\u09BF\u09C0-\u09C4\u09C7-\u09C8\u09CB-\u09CD\u09D7\u09E2-\u09E3\u0A02\u0A3C\u0A3E\u0A3F\u0A40-\u0A42\u0A47-\u0A48\u0A4B-\u0A4D\u0A70-\u0A71\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0B01-\u0B03\u0B3C\u0B3E-\u0B43\u0B47-\u0B48\u0B4B-\u0B4D\u0B56-\u0B57\u0B82-\u0B83\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0C01-\u0C03\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55-\u0C56\u0C82-\u0C83\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5-\u0CD6\u0D02-\u0D03\u0D3E-\u0D43\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB-\u0EBC\u0EC8-\u0ECD\u0F18-\u0F19\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F84\u0F86-\u0F8B\u0F90-\u0F95\u0F97\u0F99-\u0FAD\u0FB1-\u0FB7\u0FB9\u20D0-\u20DC\u20E1\u302A-\u302F\u3099\u309A";
|
|
2228
|
+
var XML10_DIGIT = "0-9\u0660-\u0669\u06F0-\u06F9\u0966-\u096F\u09E6-\u09EF\u0A66-\u0A6F\u0AE6-\u0AEF\u0B66-\u0B6F\u0BE7-\u0BEF\u0C66-\u0C6F\u0CE6-\u0CEF\u0D66-\u0D6F\u0E50-\u0E59\u0ED0-\u0ED9\u0F20-\u0F29";
|
|
2229
|
+
var XML10_EXTENDER = "\xB7\u02D0\u02D1\u0387\u0640\u0E46\u0EC6\u3005\u3031-\u3035\u309D-\u309E\u30FC-\u30FE";
|
|
2230
|
+
var XML10_LETTER = XML10_BASE_CHAR + XML10_IDEOGRAPHIC;
|
|
2231
|
+
var XML10_NAME_CHAR = `${XML10_LETTER + XML10_DIGIT}\\._:${XML10_COMBINING_CHAR}${XML10_EXTENDER}-`;
|
|
2232
|
+
var XML10_NAME = `[${XML10_LETTER}_:][${XML10_NAME_CHAR}]*`;
|
|
2233
|
+
var XML10_ENTITY_REF = `&${XML10_NAME};`;
|
|
2234
|
+
var XML10_REFERENCE = `${XML10_ENTITY_REF}|${XML_CHAR_REF}`;
|
|
2235
|
+
var XML10_ATT_VALUE = `"(([^<&"]|${XML10_REFERENCE})*)"|'(([^<&']|${XML10_REFERENCE})*)'`;
|
|
2236
|
+
var XML10_ATTRIBUTE = `(${XML10_NAME})${XML_EQ}(${XML10_ATT_VALUE})`;
|
|
2237
|
+
var XML11_VERSION_INFO = `${XML_S}version${XML_EQ}("1\\.1"|'1\\.1')`;
|
|
2238
|
+
var XML11_NAME_START_CHAR = ":A-Z_a-z\xC0-\xD6\xD8-\xF6\xF8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD";
|
|
2239
|
+
var XML11_NAME_CHAR = XML11_NAME_START_CHAR + "\\.0-9\xB7\u0300-\u036F\u203F-\u2040-";
|
|
2240
|
+
var XML11_NAME = `[${XML11_NAME_START_CHAR}][${XML11_NAME_CHAR}]*`;
|
|
2241
|
+
var XML11_ENTITY_REF = `&${XML11_NAME};`;
|
|
2242
|
+
var XML11_REFERENCE = `${XML11_ENTITY_REF}|${XML_CHAR_REF}`;
|
|
2243
|
+
var XML11_ATT_VALUE = `"(([^<&"]|${XML11_REFERENCE})*)"|'(([^<&']|${XML11_REFERENCE})*)'`;
|
|
2244
|
+
var XML11_ATTRIBUTE = `(${XML11_NAME})${XML_EQ}(${XML11_ATT_VALUE})`;
|
|
2245
|
+
var XML_NC_NAME_CHAR = `${XML10_LETTER + XML10_DIGIT}\\._${XML10_COMBINING_CHAR}${XML10_EXTENDER}-`;
|
|
2246
|
+
var XML_NC_NAME = `[${XML10_LETTER}_][${XML_NC_NAME_CHAR}]*`;
|
|
2247
|
+
|
|
2248
|
+
// src/dom/xml-parser.ts
|
|
2249
|
+
var XmlParser = class {
|
|
2250
|
+
constructor() {
|
|
2251
|
+
this.regexEmpty = /\/$/;
|
|
2252
|
+
this.XML10_TAGNAME_REGEXP = new RegExp(`^(${XML10_NAME})`);
|
|
2253
|
+
this.XML10_ATTRIBUTE_REGEXP = new RegExp(XML10_ATTRIBUTE, "g");
|
|
2254
|
+
this.XML11_TAGNAME_REGEXP = new RegExp(`^(${XML11_NAME})`);
|
|
2255
|
+
this.XML11_ATTRIBUTE_REGEXP = new RegExp(XML11_ATTRIBUTE, "g");
|
|
2256
|
+
this.lenientHtmlTags = ["hr", "link", "meta"];
|
|
2257
|
+
}
|
|
2258
|
+
/**
|
|
2259
|
+
* The entry point for this parser.
|
|
2260
|
+
* It verifies whether the document seems to be HTML.
|
|
2261
|
+
* HTML is a special case if XML and it should be parsed differently.
|
|
2262
|
+
* @param xmlOrHtml The XML or HTML content to be parsed.
|
|
2263
|
+
* @returns A DOM document.
|
|
2264
|
+
*/
|
|
2265
|
+
xmlParse(xmlOrHtml) {
|
|
2266
|
+
if (xmlOrHtml.toUpperCase().startsWith("<!DOCTYPE HTML")) {
|
|
2267
|
+
return this.htmlParse(xmlOrHtml);
|
|
2268
|
+
}
|
|
2269
|
+
return this.xmlStrictParse(xmlOrHtml);
|
|
2270
|
+
}
|
|
2271
|
+
/**
|
|
2272
|
+
* Given an XNode, returns an object mapping prefixes to their corresponding namespaces in its scope.
|
|
2273
|
+
* Default namespace is treated as if its prefix were the empty string.
|
|
2274
|
+
* @param node The Node.
|
|
2275
|
+
* @returns An object with prefixes and namespace URLs.
|
|
2276
|
+
*/
|
|
2277
|
+
namespaceMapAt(node) {
|
|
2278
|
+
const map = {
|
|
2279
|
+
// reserved namespaces: https://www.w3.org/TR/REC-xml-names/#xmlReserved
|
|
2280
|
+
xmlns: "http://www.w3.org/2000/xmlns/",
|
|
2281
|
+
xml: "http://www.w3.org/XML/1998/namespace"
|
|
2282
|
+
};
|
|
2283
|
+
let n = node;
|
|
2284
|
+
while (n !== null) {
|
|
2285
|
+
for (let i = 0; i < n.childNodes.length; i++) {
|
|
2286
|
+
const childNode = n.childNodes[i];
|
|
2287
|
+
if (childNode.nodeType !== DOM_ATTRIBUTE_NODE) {
|
|
2288
|
+
continue;
|
|
2289
|
+
}
|
|
2290
|
+
if (childNode.nodeName.startsWith("xmlns:")) {
|
|
2291
|
+
const prefix = childNode.nodeName.split(":")[1];
|
|
2292
|
+
if (!(prefix in map)) map[prefix] = childNode.nodeValue;
|
|
2293
|
+
} else if (childNode.nodeName == "xmlns") {
|
|
2294
|
+
if (!("" in map)) map[""] = childNode.nodeValue || null;
|
|
2295
|
+
}
|
|
2296
|
+
}
|
|
2297
|
+
n = n.parentNode;
|
|
2298
|
+
}
|
|
2299
|
+
return map;
|
|
2300
|
+
}
|
|
2301
|
+
/**
|
|
2302
|
+
* HTML needs to be parsed differently because it's a special case of XML.
|
|
2303
|
+
* Sources:
|
|
2304
|
+
*
|
|
2305
|
+
* - https://blog.teamtreehouse.com/to-close-or-not-to-close-tags-in-html5
|
|
2306
|
+
* @param htmlText The HTML text
|
|
2307
|
+
* @returns A DOM document.
|
|
2308
|
+
*/
|
|
2309
|
+
htmlParse(htmlText) {
|
|
2310
|
+
const xmlDocument = new XDocument();
|
|
2311
|
+
const root = xmlDocument;
|
|
2312
|
+
const stack = [];
|
|
2313
|
+
let parent = root;
|
|
2314
|
+
stack.push(parent);
|
|
2315
|
+
let tag = false, quotes = false, doublequotes = false, start = 0;
|
|
2316
|
+
for (let i = 0; i < htmlText.length; ++i) {
|
|
2317
|
+
let char = htmlText.charAt(i);
|
|
2318
|
+
if (tag) {
|
|
2319
|
+
if (!doublequotes && char === "'") {
|
|
2320
|
+
quotes = !quotes;
|
|
2321
|
+
} else if (!quotes && char === '"') {
|
|
2322
|
+
doublequotes = !doublequotes;
|
|
2323
|
+
} else if (!quotes && !doublequotes && char === ">") {
|
|
2324
|
+
let text = htmlText.slice(start, i);
|
|
2325
|
+
if (text.charAt(0) === "/") {
|
|
2326
|
+
stack.pop();
|
|
2327
|
+
parent = stack[stack.length - 1];
|
|
2328
|
+
} else if (text.charAt(0) === "!") {
|
|
2329
|
+
} else {
|
|
2330
|
+
const empty = text.match(this.regexEmpty);
|
|
2331
|
+
const tagName = this.XML10_TAGNAME_REGEXP.exec(text)[1];
|
|
2332
|
+
let node = domCreateElement(xmlDocument, tagName);
|
|
2333
|
+
let attribute;
|
|
2334
|
+
while (attribute = this.XML10_ATTRIBUTE_REGEXP.exec(text)) {
|
|
2335
|
+
const val = import_he2.default.decode(attribute[5] || attribute[7] || "");
|
|
2336
|
+
domSetAttribute(node, attribute[1], val);
|
|
2337
|
+
}
|
|
2338
|
+
node.siblingPosition = parent.childNodes.length;
|
|
2339
|
+
domAppendChild(parent, node);
|
|
2340
|
+
if (!empty && !this.lenientHtmlTags.includes(tagName)) {
|
|
2341
|
+
parent = node;
|
|
2342
|
+
stack.push(node);
|
|
2343
|
+
}
|
|
2344
|
+
}
|
|
2345
|
+
start = i + 1;
|
|
2346
|
+
tag = false;
|
|
2347
|
+
quotes = false;
|
|
2348
|
+
doublequotes = false;
|
|
2349
|
+
}
|
|
2350
|
+
} else {
|
|
2351
|
+
if (char === "<") {
|
|
2352
|
+
let text = htmlText.slice(start, i);
|
|
2353
|
+
if (text && parent !== root) {
|
|
2354
|
+
domAppendChild(parent, domCreateTextNode(xmlDocument, text));
|
|
2355
|
+
}
|
|
2356
|
+
if (htmlText.slice(i + 1, i + 4) === "!--") {
|
|
2357
|
+
let endTagIndex = htmlText.slice(i + 4).indexOf("-->");
|
|
2358
|
+
if (endTagIndex) {
|
|
2359
|
+
let node = domCreateComment(xmlDocument, htmlText.slice(i + 4, i + endTagIndex + 4));
|
|
2360
|
+
domAppendChild(parent, node);
|
|
2361
|
+
i += endTagIndex + 6;
|
|
2362
|
+
}
|
|
2363
|
+
} else if (htmlText.slice(i + 1, i + 9) === "!DOCTYPE") {
|
|
2364
|
+
let endTagIndex = htmlText.slice(i + 9).indexOf(">");
|
|
2365
|
+
if (endTagIndex) {
|
|
2366
|
+
const dtdValue = htmlText.slice(i + 9, i + endTagIndex + 9).trimStart();
|
|
2367
|
+
const node = domCreateDTDSection(xmlDocument, dtdValue);
|
|
2368
|
+
domAppendChild(parent, node);
|
|
2369
|
+
i += endTagIndex + dtdValue.length + 5;
|
|
2370
|
+
}
|
|
2371
|
+
} else {
|
|
2372
|
+
tag = true;
|
|
2373
|
+
}
|
|
2374
|
+
start = i + 1;
|
|
2375
|
+
}
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
return xmlDocument;
|
|
2379
|
+
}
|
|
2380
|
+
/**
|
|
2381
|
+
* Parses the given XML string with our custom, JavaScript XML parser.
|
|
2382
|
+
* @param xml The XML String.
|
|
2383
|
+
* @returns A XDocument.
|
|
2384
|
+
* @author Steffen Meschkat <mesch@google.com>
|
|
2385
|
+
*/
|
|
2386
|
+
xmlStrictParse(xml) {
|
|
2387
|
+
let regexTagname;
|
|
2388
|
+
let regexAttribute;
|
|
2389
|
+
if (xml.match(/^<\?xml/)) {
|
|
2390
|
+
if (xml.search(new RegExp(XML10_VERSION_INFO)) === 5) {
|
|
2391
|
+
regexTagname = this.XML10_TAGNAME_REGEXP;
|
|
2392
|
+
regexAttribute = this.XML10_ATTRIBUTE_REGEXP;
|
|
2393
|
+
} else if (xml.search(new RegExp(XML11_VERSION_INFO)) === 5) {
|
|
2394
|
+
regexTagname = this.XML11_TAGNAME_REGEXP;
|
|
2395
|
+
regexAttribute = this.XML11_ATTRIBUTE_REGEXP;
|
|
2396
|
+
} else {
|
|
2397
|
+
throw new Error("XML VersionInfo has an unknown version number.");
|
|
2398
|
+
}
|
|
2399
|
+
} else {
|
|
2400
|
+
regexTagname = this.XML10_TAGNAME_REGEXP;
|
|
2401
|
+
regexAttribute = this.XML10_ATTRIBUTE_REGEXP;
|
|
2402
|
+
}
|
|
2403
|
+
const xmlDocument = new XDocument();
|
|
2404
|
+
const root = xmlDocument;
|
|
2405
|
+
const stack = [];
|
|
2406
|
+
let parent = root;
|
|
2407
|
+
stack.push(parent);
|
|
2408
|
+
let tag = false, quotes = false, doublequotes = false, start = 0;
|
|
2409
|
+
for (let i = 0; i < xml.length; ++i) {
|
|
2410
|
+
let char = xml.charAt(i);
|
|
2411
|
+
if (tag && !doublequotes && char === "'") {
|
|
2412
|
+
quotes = !quotes;
|
|
2413
|
+
} else if (tag && !quotes && char === '"') {
|
|
2414
|
+
doublequotes = !doublequotes;
|
|
2415
|
+
} else if (tag && char === ">" && !quotes && !doublequotes) {
|
|
2416
|
+
let text = xml.slice(start, i);
|
|
2417
|
+
if (text.charAt(0) === "/") {
|
|
2418
|
+
stack.pop();
|
|
2419
|
+
parent = stack[stack.length - 1];
|
|
2420
|
+
} else if (text.charAt(0) === "?") {
|
|
2421
|
+
} else if (text.charAt(0) === "!") {
|
|
2422
|
+
} else {
|
|
2423
|
+
const empty = text.match(this.regexEmpty);
|
|
2424
|
+
const tagname = regexTagname.exec(text)[1];
|
|
2425
|
+
let node = domCreateElement(xmlDocument, tagname);
|
|
2426
|
+
let attribute;
|
|
2427
|
+
while (attribute = regexAttribute.exec(text)) {
|
|
2428
|
+
const val = import_he2.default.decode(attribute[5] || attribute[7] || "");
|
|
2429
|
+
domSetAttribute(node, attribute[1], val);
|
|
2430
|
+
}
|
|
2431
|
+
node.siblingPosition = parent.childNodes.length;
|
|
2432
|
+
domAppendChild(parent, node);
|
|
2433
|
+
if (!empty) {
|
|
2434
|
+
parent = node;
|
|
2435
|
+
stack.push(node);
|
|
2436
|
+
}
|
|
2437
|
+
const namespaceMap = this.namespaceMapAt(node);
|
|
2438
|
+
if (node.prefix !== null) {
|
|
2439
|
+
if (node.prefix in namespaceMap) node.namespaceUri = namespaceMap[node.prefix];
|
|
2440
|
+
} else {
|
|
2441
|
+
if ("" in namespaceMap) node.namespaceUri = namespaceMap[""];
|
|
2442
|
+
}
|
|
2443
|
+
for (let i2 = 0; i2 < node.childNodes.length; ++i2) {
|
|
2444
|
+
const childNode = node.childNodes[i2];
|
|
2445
|
+
if (childNode.nodeType !== DOM_ATTRIBUTE_NODE) {
|
|
2446
|
+
continue;
|
|
2447
|
+
}
|
|
2448
|
+
if (childNode.prefix !== null && childNode.prefix in namespaceMap) {
|
|
2449
|
+
childNode.namespaceUri = namespaceMap[childNode.prefix];
|
|
2450
|
+
}
|
|
2451
|
+
}
|
|
2452
|
+
}
|
|
2453
|
+
start = i + 1;
|
|
2454
|
+
tag = false;
|
|
2455
|
+
quotes = false;
|
|
2456
|
+
doublequotes = false;
|
|
2457
|
+
} else if (!tag && char === "<") {
|
|
2458
|
+
let text = xml.slice(start, i);
|
|
2459
|
+
if (text && parent !== root) {
|
|
2460
|
+
domAppendChild(parent, domCreateTextNode(xmlDocument, text));
|
|
2461
|
+
}
|
|
2462
|
+
if (xml.slice(i + 1, i + 4) === "!--") {
|
|
2463
|
+
let endTagIndex = xml.slice(i + 4).indexOf("-->");
|
|
2464
|
+
if (endTagIndex) {
|
|
2465
|
+
let node = domCreateComment(xmlDocument, xml.slice(i + 4, i + endTagIndex + 4));
|
|
2466
|
+
domAppendChild(parent, node);
|
|
2467
|
+
i += endTagIndex + 6;
|
|
2468
|
+
}
|
|
2469
|
+
} else if (xml.slice(i + 1, i + 9) === "![CDATA[") {
|
|
2470
|
+
let endTagIndex = xml.slice(i + 9).indexOf("]]>");
|
|
2471
|
+
if (endTagIndex) {
|
|
2472
|
+
let node = domCreateCDATASection(xmlDocument, xml.slice(i + 9, i + endTagIndex + 9));
|
|
2473
|
+
domAppendChild(parent, node);
|
|
2474
|
+
i += endTagIndex + 11;
|
|
2475
|
+
}
|
|
2476
|
+
} else if (xml.slice(i + 1, i + 9) === "!DOCTYPE") {
|
|
2477
|
+
let endTagIndex = xml.slice(i + 9).indexOf(">");
|
|
2478
|
+
if (endTagIndex) {
|
|
2479
|
+
const dtdValue = xml.slice(i + 9, i + endTagIndex + 9).trimStart();
|
|
2480
|
+
const node = domCreateDTDSection(xmlDocument, dtdValue);
|
|
2481
|
+
domAppendChild(parent, node);
|
|
2482
|
+
i += endTagIndex + dtdValue.length + 5;
|
|
2483
|
+
}
|
|
2484
|
+
} else {
|
|
2485
|
+
tag = true;
|
|
2486
|
+
}
|
|
2487
|
+
start = i + 1;
|
|
2488
|
+
}
|
|
2489
|
+
}
|
|
2490
|
+
return root;
|
|
2491
|
+
}
|
|
2492
|
+
};
|
|
2493
|
+
|
|
2494
|
+
// src/xpath/values/node-set-value.ts
|
|
2495
|
+
var NodeSetValue = class {
|
|
2496
|
+
constructor(value) {
|
|
2497
|
+
this.value = value;
|
|
2498
|
+
this.type = "node-set";
|
|
2499
|
+
}
|
|
2500
|
+
stringValue() {
|
|
2501
|
+
if (this.value.length === 0) {
|
|
2502
|
+
return "";
|
|
2503
|
+
}
|
|
2504
|
+
return xmlValue(this.value[0]);
|
|
2505
|
+
}
|
|
2506
|
+
booleanValue() {
|
|
2507
|
+
return this.value.length > 0;
|
|
2508
|
+
}
|
|
2509
|
+
numberValue() {
|
|
2510
|
+
return parseInt(this.stringValue()) - 0;
|
|
2511
|
+
}
|
|
2512
|
+
nodeSetValue() {
|
|
2513
|
+
return this.value;
|
|
2514
|
+
}
|
|
2515
|
+
};
|
|
2516
|
+
|
|
2517
|
+
// src/xpath/xpath.ts
|
|
2518
|
+
var Expression = class {
|
|
2519
|
+
constructor(xpathExpression, nodeConverter) {
|
|
2520
|
+
this.xpathExpression = xpathExpression;
|
|
2521
|
+
this.nodeConverter = nodeConverter;
|
|
2522
|
+
if (xpathExpression instanceof XPathLocationPath) {
|
|
2523
|
+
this.absolute = xpathExpression.absolute;
|
|
2524
|
+
this.steps = xpathExpression.steps.map((step, index) => ({
|
|
2525
|
+
axis: step.axis,
|
|
2526
|
+
nodeTest: step.nodeTest,
|
|
2527
|
+
predicates: step.predicates,
|
|
2528
|
+
// Add methods needed by old code
|
|
2529
|
+
hasPositionalPredicate: false,
|
|
2530
|
+
// TODO: implement proper detection
|
|
2531
|
+
predicate: step.predicates || [],
|
|
2532
|
+
evaluate: (ctx) => {
|
|
2533
|
+
const xpathCtx = this.nodeConverter.exprContextToXPathContext(ctx);
|
|
2534
|
+
const result = step.evaluate(xpathCtx);
|
|
2535
|
+
return this.nodeConverter.wrapResult(result, ctx);
|
|
2536
|
+
}
|
|
2537
|
+
}));
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2540
|
+
/**
|
|
2541
|
+
* Evaluate the expression in the given context.
|
|
2542
|
+
*/
|
|
2543
|
+
evaluate(context) {
|
|
2544
|
+
const xpathContext = this.nodeConverter.exprContextToXPathContext(context);
|
|
2545
|
+
const result = this.xpathExpression.evaluate(xpathContext);
|
|
2546
|
+
return this.nodeConverter.wrapResult(result, context);
|
|
2547
|
+
}
|
|
2548
|
+
};
|
|
2549
|
+
var LocationExpr = class extends Expression {
|
|
2550
|
+
constructor(xpathExpression, nodeConverter) {
|
|
2551
|
+
super(xpathExpression, nodeConverter);
|
|
2552
|
+
this.absolute = xpathExpression.absolute;
|
|
2553
|
+
this.steps = xpathExpression.steps.map((step) => ({
|
|
2554
|
+
axis: step.axis,
|
|
2555
|
+
nodeTest: step.nodeTest,
|
|
2556
|
+
predicates: step.predicates || [],
|
|
2557
|
+
predicate: step.predicates || [],
|
|
2558
|
+
hasPositionalPredicate: this.hasPositionalPredicate(step.predicates || [])
|
|
2559
|
+
}));
|
|
2560
|
+
}
|
|
2561
|
+
hasPositionalPredicate(predicates) {
|
|
2562
|
+
return false;
|
|
2563
|
+
}
|
|
2564
|
+
appendStep(step) {
|
|
2565
|
+
this.steps.push(step);
|
|
2566
|
+
}
|
|
2567
|
+
prependStep(step) {
|
|
2568
|
+
this.steps.unshift(step);
|
|
2569
|
+
}
|
|
2570
|
+
};
|
|
2571
|
+
var UnionExpr = class extends Expression {
|
|
2572
|
+
constructor(xpathExpression, nodeConverter, expr1, expr2) {
|
|
2573
|
+
super(xpathExpression, nodeConverter);
|
|
2574
|
+
this.expr1 = expr1;
|
|
2575
|
+
this.expr2 = expr2;
|
|
2576
|
+
}
|
|
2577
|
+
};
|
|
2578
|
+
var NodeConverter = class {
|
|
2579
|
+
/**
|
|
2580
|
+
* Convert ExprContext to XPathContext for the new XPath implementation.
|
|
2581
|
+
* XNodes are used directly since they implement enough of the XPathNode interface.
|
|
2582
|
+
*/
|
|
2583
|
+
exprContextToXPathContext(exprContext) {
|
|
2584
|
+
const currentNode = exprContext.nodeList[exprContext.position];
|
|
2585
|
+
const xpathNode = this.adaptXNode(currentNode);
|
|
2586
|
+
const nodeList = exprContext.nodeList.map((node) => this.adaptXNode(node));
|
|
2587
|
+
return createContext(xpathNode, {
|
|
2588
|
+
position: exprContext.position + 1,
|
|
2589
|
+
// XPath is 1-based
|
|
2590
|
+
size: exprContext.nodeList.length,
|
|
2591
|
+
nodeList,
|
|
2592
|
+
variables: this.convertVariables(exprContext),
|
|
2593
|
+
functions: this.createCustomFunctions(exprContext),
|
|
2594
|
+
namespaces: exprContext.knownNamespaces
|
|
2595
|
+
});
|
|
2596
|
+
}
|
|
2597
|
+
/**
|
|
2598
|
+
* Adapt XNode to be compatible with XPathNode interface.
|
|
2599
|
+
* We add missing properties but keep the original XNode reference.
|
|
2600
|
+
*/
|
|
2601
|
+
adaptXNode(node) {
|
|
2602
|
+
if (!node) return null;
|
|
2603
|
+
const adapted = node;
|
|
2604
|
+
if (!("namespaceURI" in adapted)) {
|
|
2605
|
+
Object.defineProperty(adapted, "namespaceURI", {
|
|
2606
|
+
get() {
|
|
2607
|
+
return this.namespaceUri;
|
|
2608
|
+
},
|
|
2609
|
+
enumerable: true,
|
|
2610
|
+
configurable: true
|
|
2611
|
+
});
|
|
2612
|
+
}
|
|
2613
|
+
if (!("textContent" in adapted)) {
|
|
2614
|
+
Object.defineProperty(adapted, "textContent", {
|
|
2615
|
+
get() {
|
|
2616
|
+
return this._getTextContent();
|
|
2617
|
+
},
|
|
2618
|
+
enumerable: true,
|
|
2619
|
+
configurable: true
|
|
2620
|
+
});
|
|
2621
|
+
}
|
|
2622
|
+
if (!("_getTextContent" in adapted)) {
|
|
2623
|
+
adapted._getTextContent = function() {
|
|
2624
|
+
if (this.nodeType === 3 || this.nodeType === 2) {
|
|
2625
|
+
return this.nodeValue || "";
|
|
2626
|
+
}
|
|
2627
|
+
if (!this.childNodes) return "";
|
|
2628
|
+
let text = "";
|
|
2629
|
+
for (const child of this.childNodes) {
|
|
2630
|
+
if (child.nodeType === 3) {
|
|
2631
|
+
text += child.nodeValue || "";
|
|
2632
|
+
} else if (child.nodeType === 1) {
|
|
2633
|
+
text += this._getTextContent.call(child);
|
|
2634
|
+
}
|
|
2635
|
+
}
|
|
2636
|
+
return text;
|
|
2637
|
+
};
|
|
2638
|
+
}
|
|
2639
|
+
if (!("attributes" in adapted)) {
|
|
2640
|
+
Object.defineProperty(adapted, "attributes", {
|
|
2641
|
+
get() {
|
|
2642
|
+
return this.childNodes ? this.childNodes.filter((n) => n.nodeType === 2) : [];
|
|
2643
|
+
},
|
|
2644
|
+
enumerable: true,
|
|
2645
|
+
configurable: true
|
|
2646
|
+
});
|
|
2647
|
+
}
|
|
2648
|
+
if (!("getAttribute" in adapted)) {
|
|
2649
|
+
adapted.getAttribute = function(name) {
|
|
2650
|
+
return this.getAttributeValue ? this.getAttributeValue(name) : null;
|
|
2651
|
+
};
|
|
2652
|
+
}
|
|
2653
|
+
return adapted;
|
|
2654
|
+
}
|
|
2655
|
+
/**
|
|
2656
|
+
* Convert XPathNode result back to XNode.
|
|
2657
|
+
* Since we're now using XNodes directly, this is mostly a type cast.
|
|
2658
|
+
*/
|
|
2659
|
+
xPathNodeToXNode(xpathNode) {
|
|
2660
|
+
if (!xpathNode) return null;
|
|
2661
|
+
return xpathNode;
|
|
2662
|
+
}
|
|
2663
|
+
/**
|
|
2664
|
+
* Get text content from an XNode.
|
|
2665
|
+
*/
|
|
2666
|
+
getTextContent(node) {
|
|
2667
|
+
if (node.nodeType === 3 || node.nodeType === 2) {
|
|
2668
|
+
return node.nodeValue || "";
|
|
2669
|
+
}
|
|
2670
|
+
if (!node.childNodes) return "";
|
|
2671
|
+
let text = "";
|
|
2672
|
+
for (const child of node.childNodes) {
|
|
2673
|
+
if (child.nodeType === 3) {
|
|
2674
|
+
text += child.nodeValue || "";
|
|
2675
|
+
} else if (child.nodeType === 1) {
|
|
2676
|
+
text += this.getTextContent(child);
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2679
|
+
return text;
|
|
2680
|
+
}
|
|
2681
|
+
/**
|
|
2682
|
+
* Convert variables from ExprContext format to XPathContext format.
|
|
2683
|
+
*/
|
|
2684
|
+
convertVariables(exprContext) {
|
|
2685
|
+
const variables = {};
|
|
2686
|
+
for (const [name, value] of Object.entries(exprContext.variables || {})) {
|
|
2687
|
+
if (value && typeof value === "object" && "stringValue" in value) {
|
|
2688
|
+
const nodeValue = value;
|
|
2689
|
+
if (nodeValue.type === "node-set") {
|
|
2690
|
+
variables[name] = value.nodeSetValue().map((n) => this.adaptXNode(n));
|
|
2691
|
+
} else if (nodeValue.type === "string") {
|
|
2692
|
+
variables[name] = value.stringValue();
|
|
2693
|
+
} else if (nodeValue.type === "number") {
|
|
2694
|
+
variables[name] = value.numberValue();
|
|
2695
|
+
} else if (nodeValue.type === "boolean") {
|
|
2696
|
+
variables[name] = value.booleanValue();
|
|
2697
|
+
} else {
|
|
2698
|
+
variables[name] = value.stringValue();
|
|
2699
|
+
}
|
|
2700
|
+
} else {
|
|
2701
|
+
variables[name] = value;
|
|
2702
|
+
}
|
|
2703
|
+
}
|
|
2704
|
+
return variables;
|
|
2705
|
+
}
|
|
2706
|
+
/**
|
|
2707
|
+
* Create custom functions for XPath context (like key(), document(), etc.).
|
|
2708
|
+
*/
|
|
2709
|
+
createCustomFunctions(exprContext) {
|
|
2710
|
+
const functions = {};
|
|
2711
|
+
functions["key"] = (keyName, keyValue) => {
|
|
2712
|
+
var _a;
|
|
2713
|
+
const keyDef = (_a = exprContext.keys) == null ? void 0 : _a[keyName];
|
|
2714
|
+
if (keyDef && keyDef[keyValue]) {
|
|
2715
|
+
const nodeSetValue = keyDef[keyValue];
|
|
2716
|
+
return nodeSetValue.nodeSetValue().map((n) => this.adaptXNode(n));
|
|
2717
|
+
}
|
|
2718
|
+
return [];
|
|
2719
|
+
};
|
|
2720
|
+
functions["current"] = () => {
|
|
2721
|
+
const currentNode = exprContext.nodeList[exprContext.position];
|
|
2722
|
+
return [this.adaptXNode(currentNode)];
|
|
2723
|
+
};
|
|
2724
|
+
functions["format-number"] = (number, format, decimalFormatName) => {
|
|
2725
|
+
const settings = exprContext.decimalFormatSettings;
|
|
2726
|
+
return number.toLocaleString();
|
|
2727
|
+
};
|
|
2728
|
+
functions["xml-to-json"] = (nodes) => {
|
|
2729
|
+
if (exprContext.xsltVersion === "1.0") {
|
|
2730
|
+
throw new Error('xml-to-json() is not supported in XSLT 1.0. Use version="3.0" in your stylesheet.');
|
|
2731
|
+
}
|
|
2732
|
+
const node = Array.isArray(nodes) ? nodes[0] : nodes;
|
|
2733
|
+
if (!node) {
|
|
2734
|
+
return '""';
|
|
2735
|
+
}
|
|
2736
|
+
return this.xmlToJson(node);
|
|
2737
|
+
};
|
|
2738
|
+
return functions;
|
|
2739
|
+
}
|
|
2740
|
+
/**
|
|
2741
|
+
* Convert an XML node to a JSON string representation.
|
|
2742
|
+
* This is a simplified implementation of XSLT 3.0's xml-to-json().
|
|
2743
|
+
*/
|
|
2744
|
+
xmlToJson(node) {
|
|
2745
|
+
if (!node) {
|
|
2746
|
+
return '""';
|
|
2747
|
+
}
|
|
2748
|
+
if (node.nodeType === 3) {
|
|
2749
|
+
const text = node.nodeValue || "";
|
|
2750
|
+
return JSON.stringify(text);
|
|
2751
|
+
}
|
|
2752
|
+
if (node.nodeType === 1) {
|
|
2753
|
+
const textContent = this.getTextContent(node);
|
|
2754
|
+
return JSON.stringify(textContent);
|
|
2755
|
+
}
|
|
2756
|
+
if (node.nodeType === 2) {
|
|
2757
|
+
return JSON.stringify(node.nodeValue || "");
|
|
2758
|
+
}
|
|
2759
|
+
return '""';
|
|
2760
|
+
}
|
|
2761
|
+
/**
|
|
2762
|
+
* Wrap XPath result in appropriate NodeValue type.
|
|
2763
|
+
*/
|
|
2764
|
+
wrapResult(result, exprContext) {
|
|
2765
|
+
if (Array.isArray(result)) {
|
|
2766
|
+
const xnodes = result.map((node) => this.xPathNodeToXNode(node)).filter((n) => n !== null);
|
|
2767
|
+
return new NodeSetValue(xnodes);
|
|
2768
|
+
}
|
|
2769
|
+
if (typeof result === "string") {
|
|
2770
|
+
return new StringValue(result);
|
|
2771
|
+
}
|
|
2772
|
+
if (typeof result === "number") {
|
|
2773
|
+
return new NumberValue(result);
|
|
2774
|
+
}
|
|
2775
|
+
if (typeof result === "boolean") {
|
|
2776
|
+
return new BooleanValue(result);
|
|
2777
|
+
}
|
|
2778
|
+
return new NodeSetValue([]);
|
|
2779
|
+
}
|
|
2780
|
+
/**
|
|
2781
|
+
* Clear any internal state if needed.
|
|
2782
|
+
*/
|
|
2783
|
+
clearCache() {
|
|
2784
|
+
}
|
|
2785
|
+
};
|
|
2786
|
+
var XPath = class {
|
|
2787
|
+
constructor() {
|
|
2788
|
+
this.parseCache = /* @__PURE__ */ new Map();
|
|
2789
|
+
this.lexer = new XPathLexer();
|
|
2790
|
+
this.parser = new XPathParser();
|
|
2791
|
+
this.nodeConverter = new NodeConverter();
|
|
2792
|
+
}
|
|
2793
|
+
/**
|
|
2794
|
+
* Parse an XPath expression and return an Expression object.
|
|
2795
|
+
* @param expression The XPath expression string.
|
|
2796
|
+
* @param axis Optional axis override for relative paths.
|
|
2797
|
+
*/
|
|
2798
|
+
xPathParse(expression, axis) {
|
|
2799
|
+
const cacheKey = `${expression}:${axis || ""}`;
|
|
2800
|
+
if (this.parseCache.has(cacheKey)) {
|
|
2801
|
+
return this.parseCache.get(cacheKey);
|
|
2802
|
+
}
|
|
2803
|
+
const tokens = this.lexer.scan(expression);
|
|
2804
|
+
const xpathExpr = this.parser.parse(tokens);
|
|
2805
|
+
const wrappedExpr = this.wrapExpression(xpathExpr, axis);
|
|
2806
|
+
this.parseCache.set(cacheKey, wrappedExpr);
|
|
2807
|
+
return wrappedExpr;
|
|
2808
|
+
}
|
|
2809
|
+
/**
|
|
2810
|
+
* Parse and evaluate an XPath expression.
|
|
2811
|
+
* @param select The XPath expression string.
|
|
2812
|
+
* @param context The expression context.
|
|
2813
|
+
*/
|
|
2814
|
+
xPathEval(select, context) {
|
|
2815
|
+
const expression = this.xPathParse(select);
|
|
2816
|
+
return expression.evaluate(context);
|
|
2817
|
+
}
|
|
2818
|
+
/**
|
|
2819
|
+
* Sort nodes in context according to sort specifications.
|
|
2820
|
+
* @param context The expression context with nodes to sort.
|
|
2821
|
+
* @param sort Array of sort specifications.
|
|
2822
|
+
*/
|
|
2823
|
+
xPathSort(context, sort) {
|
|
2824
|
+
if (sort.length === 0) {
|
|
2825
|
+
return;
|
|
2826
|
+
}
|
|
2827
|
+
const sortList = [];
|
|
2828
|
+
for (let i = 0; i < context.contextSize(); ++i) {
|
|
2829
|
+
const node = context.nodeList[i];
|
|
2830
|
+
const sortItem = {
|
|
2831
|
+
node,
|
|
2832
|
+
key: []
|
|
2833
|
+
};
|
|
2834
|
+
const clonedContext = context.clone([node], 0);
|
|
2835
|
+
for (const s of sort) {
|
|
2836
|
+
const value = s.expr.evaluate(clonedContext);
|
|
2837
|
+
let evalue;
|
|
2838
|
+
if (s.type === "text") {
|
|
2839
|
+
evalue = value.stringValue();
|
|
2840
|
+
} else if (s.type === "number") {
|
|
2841
|
+
evalue = value.numberValue();
|
|
2842
|
+
}
|
|
2843
|
+
sortItem.key.push({
|
|
2844
|
+
value: evalue,
|
|
2845
|
+
order: s.order
|
|
2846
|
+
});
|
|
2847
|
+
}
|
|
2848
|
+
sortItem.key.push({
|
|
2849
|
+
value: i,
|
|
2850
|
+
order: "ascending"
|
|
2851
|
+
});
|
|
2852
|
+
sortList.push(sortItem);
|
|
2853
|
+
}
|
|
2854
|
+
sortList.sort(this.xPathSortByKey);
|
|
2855
|
+
const nodes = [];
|
|
2856
|
+
for (let i = 0; i < sortList.length; ++i) {
|
|
2857
|
+
const node = sortList[i].node;
|
|
2858
|
+
node.siblingPosition = i;
|
|
2859
|
+
nodes.push(node);
|
|
2860
|
+
}
|
|
2861
|
+
context.nodeList = nodes;
|
|
2862
|
+
context.setNode(0);
|
|
2863
|
+
}
|
|
2864
|
+
/**
|
|
2865
|
+
* Comparison function for sorting.
|
|
2866
|
+
*/
|
|
2867
|
+
xPathSortByKey(v1, v2) {
|
|
2868
|
+
for (let i = 0; i < v1.key.length; ++i) {
|
|
2869
|
+
const o = v1.key[i].order === "descending" ? -1 : 1;
|
|
2870
|
+
if (v1.key[i].value > v2.key[i].value) {
|
|
2871
|
+
return 1 * o;
|
|
2872
|
+
}
|
|
2873
|
+
if (v1.key[i].value < v2.key[i].value) {
|
|
2874
|
+
return -1 * o;
|
|
2875
|
+
}
|
|
2876
|
+
}
|
|
2877
|
+
return 0;
|
|
2878
|
+
}
|
|
2879
|
+
/**
|
|
2880
|
+
* Wrap a new XPath expression in the backward-compatible Expression class.
|
|
2881
|
+
*/
|
|
2882
|
+
wrapExpression(xpathExpr, axis) {
|
|
2883
|
+
if (xpathExpr instanceof XPathLocationPath) {
|
|
2884
|
+
if (axis && xpathExpr.steps.length > 0 && !xpathExpr.absolute) {
|
|
2885
|
+
xpathExpr.steps[0].axis = axis;
|
|
2886
|
+
}
|
|
2887
|
+
return new LocationExpr(xpathExpr, this.nodeConverter);
|
|
2888
|
+
}
|
|
2889
|
+
if (xpathExpr instanceof XPathUnionExpression) {
|
|
2890
|
+
const expr1 = this.wrapExpression(xpathExpr.left, axis);
|
|
2891
|
+
const expr2 = this.wrapExpression(xpathExpr.right, axis);
|
|
2892
|
+
return new UnionExpr(xpathExpr, this.nodeConverter, expr1, expr2);
|
|
2893
|
+
}
|
|
2894
|
+
return new Expression(xpathExpr, this.nodeConverter);
|
|
2895
|
+
}
|
|
2896
|
+
/**
|
|
2897
|
+
* Clear parse cache (useful for testing or memory management).
|
|
2898
|
+
*/
|
|
2899
|
+
clearCache() {
|
|
2900
|
+
this.parseCache.clear();
|
|
2901
|
+
this.nodeConverter.clearCache();
|
|
2902
|
+
}
|
|
2903
|
+
};
|
|
2904
|
+
|
|
2905
|
+
// src/xpath/tokens.ts
|
|
2906
|
+
var TOK_NUMBER = {
|
|
2907
|
+
label: "[number]",
|
|
2908
|
+
prec: 35,
|
|
2909
|
+
re: new RegExp("^\\d+(\\.\\d*)?"),
|
|
2910
|
+
key: void 0
|
|
2911
|
+
};
|
|
2912
|
+
|
|
2913
|
+
// src/xpath/expr-context.ts
|
|
2914
|
+
var ExprContext = class _ExprContext {
|
|
2915
|
+
/**
|
|
2916
|
+
* Constructor -- gets the node, its position, the node set it
|
|
2917
|
+
* belongs to, and a parent context as arguments. The parent context
|
|
2918
|
+
* is used to implement scoping rules for variables: if a variable
|
|
2919
|
+
* is not found in the current context, it is looked for in the
|
|
2920
|
+
* parent context, recursively. Except for node, all arguments have
|
|
2921
|
+
* default values: default position is 0, default node set is the
|
|
2922
|
+
* set that contains only the node, and the default parent is null.
|
|
2923
|
+
*
|
|
2924
|
+
* Notice that position starts at 0 at the outside interface;
|
|
2925
|
+
* inside XPath expressions this shows up as position()=1.
|
|
2926
|
+
* @param nodeList TODO
|
|
2927
|
+
* @param opt_position TODO
|
|
2928
|
+
* @param opt_parent TODO
|
|
2929
|
+
* @param opt_caseInsensitive TODO
|
|
2930
|
+
* @param opt_ignoreAttributesWithoutValue TODO
|
|
2931
|
+
* @param opt_returnOnFirstMatch TODO
|
|
2932
|
+
* @param opt_ignoreNonElementNodesForNTA TODO
|
|
2933
|
+
*/
|
|
2934
|
+
constructor(nodeList, xsltVersion = "1.0", opt_position, opt_decimalFormatSettings, opt_variables, opt_knownNamespaces, opt_parent, opt_caseInsensitive, opt_ignoreAttributesWithoutValue, opt_returnOnFirstMatch, opt_ignoreNonElementNodesForNTA) {
|
|
2935
|
+
this.nodeList = nodeList;
|
|
2936
|
+
this.xsltVersion = xsltVersion;
|
|
2937
|
+
this.position = opt_position || 0;
|
|
2938
|
+
this.variables = opt_variables || {};
|
|
2939
|
+
this.keys = (opt_parent == null ? void 0 : opt_parent.keys) || {};
|
|
2940
|
+
this.knownNamespaces = opt_knownNamespaces || {};
|
|
2941
|
+
this.parent = opt_parent || null;
|
|
2942
|
+
this.caseInsensitive = opt_caseInsensitive || false;
|
|
2943
|
+
this.ignoreAttributesWithoutValue = opt_ignoreAttributesWithoutValue || false;
|
|
2944
|
+
this.returnOnFirstMatch = opt_returnOnFirstMatch || false;
|
|
2945
|
+
this.ignoreNonElementNodesForNTA = opt_ignoreNonElementNodesForNTA || false;
|
|
2946
|
+
this.inApplyTemplates = false;
|
|
2947
|
+
this.baseTemplateMatched = false;
|
|
2948
|
+
this.decimalFormatSettings = opt_decimalFormatSettings || {
|
|
2949
|
+
decimalSeparator: ".",
|
|
2950
|
+
groupingSeparator: ",",
|
|
2951
|
+
infinity: "Infinity",
|
|
2952
|
+
minusSign: "-",
|
|
2953
|
+
naN: "NaN",
|
|
2954
|
+
percent: "%",
|
|
2955
|
+
perMille: "\u2030",
|
|
2956
|
+
zeroDigit: "0",
|
|
2957
|
+
digit: "#",
|
|
2958
|
+
patternSeparator: ";"
|
|
2959
|
+
};
|
|
2960
|
+
if (opt_parent) {
|
|
2961
|
+
this.root = opt_parent.root;
|
|
2962
|
+
} else if (this.nodeList[this.position].nodeType == DOM_DOCUMENT_NODE) {
|
|
2963
|
+
this.root = this.nodeList[this.position];
|
|
2964
|
+
} else {
|
|
2965
|
+
this.root = this.nodeList[this.position].ownerDocument;
|
|
2966
|
+
}
|
|
2967
|
+
}
|
|
2968
|
+
/**
|
|
2969
|
+
* clone() -- creates a new context with the current context as
|
|
2970
|
+
* parent. If passed as argument to clone(), the new context has a
|
|
2971
|
+
* different node, position, or node set. What is not passed is
|
|
2972
|
+
* inherited from the cloned context.
|
|
2973
|
+
* @param opt_nodeList TODO
|
|
2974
|
+
* @param opt_position TODO
|
|
2975
|
+
* @returns TODO
|
|
2976
|
+
*/
|
|
2977
|
+
clone(opt_nodeList, opt_position) {
|
|
2978
|
+
return new _ExprContext(
|
|
2979
|
+
opt_nodeList || this.nodeList,
|
|
2980
|
+
this.xsltVersion,
|
|
2981
|
+
typeof opt_position !== "undefined" ? opt_position : this.position,
|
|
2982
|
+
this.decimalFormatSettings,
|
|
2983
|
+
this.variables,
|
|
2984
|
+
this.knownNamespaces,
|
|
2985
|
+
this,
|
|
2986
|
+
this.caseInsensitive,
|
|
2987
|
+
this.ignoreAttributesWithoutValue,
|
|
2988
|
+
this.returnOnFirstMatch,
|
|
2989
|
+
this.ignoreNonElementNodesForNTA
|
|
2990
|
+
);
|
|
2991
|
+
}
|
|
2992
|
+
setVariable(name, value) {
|
|
2993
|
+
if (value instanceof StringValue || value instanceof BooleanValue || value instanceof NumberValue || value instanceof NodeSetValue) {
|
|
2994
|
+
this.variables[name] = value;
|
|
2995
|
+
return;
|
|
2996
|
+
}
|
|
2997
|
+
if ("true" === value) {
|
|
2998
|
+
this.variables[name] = new BooleanValue(true);
|
|
2999
|
+
} else if ("false" === value) {
|
|
3000
|
+
this.variables[name] = new BooleanValue(false);
|
|
3001
|
+
} else if (TOK_NUMBER.re.test(String(value))) {
|
|
3002
|
+
this.variables[name] = new NumberValue(value);
|
|
3003
|
+
} else {
|
|
3004
|
+
this.variables[name] = new StringValue(value);
|
|
3005
|
+
}
|
|
3006
|
+
}
|
|
3007
|
+
getVariable(name) {
|
|
3008
|
+
if (typeof this.variables[name] != "undefined") {
|
|
3009
|
+
return this.variables[name];
|
|
3010
|
+
}
|
|
3011
|
+
if (this.parent) {
|
|
3012
|
+
return this.parent.getVariable(name);
|
|
3013
|
+
}
|
|
3014
|
+
return null;
|
|
3015
|
+
}
|
|
3016
|
+
setNode(position) {
|
|
3017
|
+
this.position = position;
|
|
3018
|
+
}
|
|
3019
|
+
contextSize() {
|
|
3020
|
+
return this.nodeList.length;
|
|
3021
|
+
}
|
|
3022
|
+
isCaseInsensitive() {
|
|
3023
|
+
return this.caseInsensitive;
|
|
3024
|
+
}
|
|
3025
|
+
setCaseInsensitive(caseInsensitive) {
|
|
3026
|
+
return this.caseInsensitive = caseInsensitive;
|
|
3027
|
+
}
|
|
3028
|
+
isIgnoreAttributesWithoutValue() {
|
|
3029
|
+
return this.ignoreAttributesWithoutValue;
|
|
3030
|
+
}
|
|
3031
|
+
setIgnoreAttributesWithoutValue(ignore) {
|
|
3032
|
+
return this.ignoreAttributesWithoutValue = ignore;
|
|
3033
|
+
}
|
|
3034
|
+
isReturnOnFirstMatch() {
|
|
3035
|
+
return this.returnOnFirstMatch;
|
|
3036
|
+
}
|
|
3037
|
+
setReturnOnFirstMatch(returnOnFirstMatch) {
|
|
3038
|
+
return this.returnOnFirstMatch = returnOnFirstMatch;
|
|
3039
|
+
}
|
|
3040
|
+
isIgnoreNonElementNodesForNTA() {
|
|
3041
|
+
return this.ignoreNonElementNodesForNTA;
|
|
3042
|
+
}
|
|
3043
|
+
setIgnoreNonElementNodesForNTA(ignoreNonElementNodesForNTA) {
|
|
3044
|
+
return this.ignoreNonElementNodesForNTA = ignoreNonElementNodesForNTA;
|
|
3045
|
+
}
|
|
3046
|
+
};
|
|
3047
|
+
|
|
3048
|
+
// src/xpath/match-resolver.ts
|
|
3049
|
+
var MatchResolver = class {
|
|
3050
|
+
/**
|
|
3051
|
+
* Entry point for expression matching.
|
|
3052
|
+
* @param expression The expression to be resolved.
|
|
3053
|
+
* @param context The Expression Context.
|
|
3054
|
+
* @returns An array of nodes.
|
|
3055
|
+
*/
|
|
3056
|
+
expressionMatch(expression, context) {
|
|
3057
|
+
if (expression instanceof LocationExpr) {
|
|
3058
|
+
return this.locationExpressionMatch(expression, context);
|
|
3059
|
+
}
|
|
3060
|
+
if (expression instanceof UnionExpr) {
|
|
3061
|
+
return this.unionExpressionMatch(expression, context);
|
|
3062
|
+
}
|
|
3063
|
+
try {
|
|
3064
|
+
const result = expression.evaluate(context);
|
|
3065
|
+
return result.nodeSetValue();
|
|
3066
|
+
} catch (e) {
|
|
3067
|
+
return [];
|
|
3068
|
+
}
|
|
3069
|
+
}
|
|
3070
|
+
/**
|
|
3071
|
+
* Resolves a LocationExpr.
|
|
3072
|
+
* @param expression The Location Expression.
|
|
3073
|
+
* @param context The Expression Context.
|
|
3074
|
+
* @returns Either the results of a relative resolution, or the results of an
|
|
3075
|
+
* absolute resolution.
|
|
3076
|
+
*/
|
|
3077
|
+
locationExpressionMatch(expression, context) {
|
|
3078
|
+
if (!expression.steps || expression.steps.length <= 0) {
|
|
3079
|
+
if (expression.absolute) {
|
|
3080
|
+
const contextNode = context.nodeList[context.position];
|
|
3081
|
+
if (contextNode.nodeName === "#document") {
|
|
3082
|
+
return [contextNode];
|
|
3083
|
+
}
|
|
3084
|
+
return [];
|
|
3085
|
+
}
|
|
3086
|
+
return [context.nodeList[context.position]];
|
|
3087
|
+
}
|
|
3088
|
+
if (expression.absolute) {
|
|
3089
|
+
const firstStep = expression.steps[0];
|
|
3090
|
+
if (firstStep.axis === "self") {
|
|
3091
|
+
return this.absoluteXsltMatchByDocumentNode(expression, context);
|
|
3092
|
+
}
|
|
3093
|
+
return this.absoluteXsltMatch(expression, context);
|
|
3094
|
+
}
|
|
3095
|
+
return this.relativeXsltMatch(expression, context);
|
|
3096
|
+
}
|
|
3097
|
+
/**
|
|
3098
|
+
* Resolves a UnionExpr.
|
|
3099
|
+
* @param expression The Union Expression.
|
|
3100
|
+
* @param context The Expression Context.
|
|
3101
|
+
* @returns The concatenated result of evaluating both sides of the expression.
|
|
3102
|
+
*/
|
|
3103
|
+
unionExpressionMatch(expression, context) {
|
|
3104
|
+
const expr1Nodes = this.expressionMatch(expression.expr1, context);
|
|
3105
|
+
return expr1Nodes.concat(this.expressionMatch(expression.expr2, context));
|
|
3106
|
+
}
|
|
3107
|
+
/**
|
|
3108
|
+
* Finds all the nodes through absolute XPath search, starting on
|
|
3109
|
+
* the #document parent node.
|
|
3110
|
+
* @param expression The Expression.
|
|
3111
|
+
* @param context The Expression Context.
|
|
3112
|
+
* @returns The list of found nodes.
|
|
3113
|
+
*/
|
|
3114
|
+
absoluteXsltMatchByDocumentNode(expression, context) {
|
|
3115
|
+
const clonedContext = context.clone([context.root], 0);
|
|
3116
|
+
const matchedNodes = expression.evaluate(clonedContext).nodeSetValue();
|
|
3117
|
+
const finalList = [];
|
|
3118
|
+
for (const element of matchedNodes) {
|
|
3119
|
+
if (element.id === context.nodeList[context.position].id) {
|
|
3120
|
+
finalList.push(element);
|
|
3121
|
+
}
|
|
3122
|
+
}
|
|
3123
|
+
return finalList;
|
|
3124
|
+
}
|
|
3125
|
+
/**
|
|
3126
|
+
* Finds all the nodes through absolute XPath search, starting with the
|
|
3127
|
+
* first child of the #document node.
|
|
3128
|
+
* @param expression The Expression.
|
|
3129
|
+
* @param context The Expression Context.
|
|
3130
|
+
* @returns The list of found nodes.
|
|
3131
|
+
*/
|
|
3132
|
+
absoluteXsltMatch(expression, context) {
|
|
3133
|
+
var _a;
|
|
3134
|
+
const firstChildOfRoot = context.root.childNodes.find((c) => c.nodeName !== "#dtd-section");
|
|
3135
|
+
if (!firstChildOfRoot) return [];
|
|
3136
|
+
const clonedContext = context.clone([firstChildOfRoot], 0);
|
|
3137
|
+
const matchedNodes = expression.evaluate(clonedContext).nodeSetValue();
|
|
3138
|
+
const finalList = [];
|
|
3139
|
+
let nodeList;
|
|
3140
|
+
if (context.nodeList.length === 1 && context.nodeList[0].nodeName === "#document") {
|
|
3141
|
+
nodeList = [context.nodeList[0].childNodes.find((c) => c.nodeName !== "#dtd-section")];
|
|
3142
|
+
} else {
|
|
3143
|
+
nodeList = context.nodeList;
|
|
3144
|
+
}
|
|
3145
|
+
for (const element of matchedNodes) {
|
|
3146
|
+
if (element.id === ((_a = nodeList[context.position]) == null ? void 0 : _a.id)) {
|
|
3147
|
+
finalList.push(element);
|
|
3148
|
+
}
|
|
3149
|
+
}
|
|
3150
|
+
return finalList;
|
|
3151
|
+
}
|
|
3152
|
+
/**
|
|
3153
|
+
* Tries to find relative nodes from the actual context position.
|
|
3154
|
+
* If found nodes are already in the context, or if they are children of
|
|
3155
|
+
* nodes in the context, they are returned.
|
|
3156
|
+
* @param expression The expression used.
|
|
3157
|
+
* @param context The Expression Context.
|
|
3158
|
+
* @returns The list of found nodes.
|
|
3159
|
+
*/
|
|
3160
|
+
relativeXsltMatch(expression, context) {
|
|
3161
|
+
const clonedContext = context.clone();
|
|
3162
|
+
const nodes = expression.evaluate(clonedContext).nodeSetValue();
|
|
3163
|
+
if (nodes.length === 1 && nodes[0].nodeName === "#document") {
|
|
3164
|
+
return [nodes[0].childNodes[0]];
|
|
3165
|
+
}
|
|
3166
|
+
return nodes;
|
|
3167
|
+
}
|
|
3168
|
+
};
|
|
3169
|
+
|
|
3170
|
+
// src/xpath/node-tests/node-test-any.ts
|
|
3171
|
+
var NodeTestAny = class {
|
|
3172
|
+
constructor() {
|
|
3173
|
+
this.value = new BooleanValue(true);
|
|
3174
|
+
}
|
|
3175
|
+
evaluate() {
|
|
3176
|
+
return this.value;
|
|
3177
|
+
}
|
|
3178
|
+
};
|
|
3179
|
+
|
|
3180
|
+
// src/xpath/node-tests/node-test-comment.ts
|
|
3181
|
+
var NodeTestComment = class {
|
|
3182
|
+
evaluate(ctx) {
|
|
3183
|
+
return new BooleanValue(ctx.nodeList[ctx.position].nodeType == DOM_COMMENT_NODE);
|
|
3184
|
+
}
|
|
3185
|
+
};
|
|
3186
|
+
|
|
3187
|
+
// src/xpath/node-tests/node-test-element-or-attribute.ts
|
|
3188
|
+
var NodeTestElementOrAttribute = class {
|
|
3189
|
+
evaluate(context) {
|
|
3190
|
+
const node = context.nodeList[context.position];
|
|
3191
|
+
return new BooleanValue(node.nodeType == DOM_ELEMENT_NODE || node.nodeType == DOM_ATTRIBUTE_NODE);
|
|
3192
|
+
}
|
|
3193
|
+
};
|
|
3194
|
+
|
|
3195
|
+
// src/xpath/node-tests/node-test-name.ts
|
|
3196
|
+
var NodeTestName = class {
|
|
3197
|
+
constructor(name) {
|
|
3198
|
+
this.name = name;
|
|
3199
|
+
if (name.indexOf(":") > 0) {
|
|
3200
|
+
const nameAndNamespacePrefix = name.split(":");
|
|
3201
|
+
this.namespacePrefix = nameAndNamespacePrefix[0];
|
|
3202
|
+
this.name = nameAndNamespacePrefix[1];
|
|
3203
|
+
}
|
|
3204
|
+
this.re = new RegExp(`^${name}$`, "i");
|
|
3205
|
+
}
|
|
3206
|
+
evaluate(context) {
|
|
3207
|
+
const node = context.nodeList[context.position];
|
|
3208
|
+
if (this.namespacePrefix !== void 0) {
|
|
3209
|
+
const namespaceValue = context.knownNamespaces[this.namespacePrefix];
|
|
3210
|
+
if (namespaceValue !== node.namespaceUri) {
|
|
3211
|
+
return new BooleanValue(false);
|
|
3212
|
+
}
|
|
3213
|
+
if (context.caseInsensitive) {
|
|
3214
|
+
if (node.localName.length !== this.name.length) return new BooleanValue(false);
|
|
3215
|
+
return new BooleanValue(this.re.test(node.localName));
|
|
3216
|
+
}
|
|
3217
|
+
return new BooleanValue(node.localName === this.name);
|
|
3218
|
+
}
|
|
3219
|
+
if (context.caseInsensitive) {
|
|
3220
|
+
if (node.nodeName.length !== this.name.length) return new BooleanValue(false);
|
|
3221
|
+
return new BooleanValue(this.re.test(node.nodeName));
|
|
3222
|
+
}
|
|
3223
|
+
return new BooleanValue(node.nodeName === this.name);
|
|
3224
|
+
}
|
|
3225
|
+
};
|
|
3226
|
+
|
|
3227
|
+
// src/xpath/node-tests/node-test-nc.ts
|
|
3228
|
+
var NodeTestNC = class {
|
|
3229
|
+
constructor(nsprefix) {
|
|
3230
|
+
this.regex = new RegExp(`^${nsprefix}:`);
|
|
3231
|
+
this.nsprefix = nsprefix;
|
|
3232
|
+
}
|
|
3233
|
+
evaluate(ctx) {
|
|
3234
|
+
const n = ctx.nodeList[ctx.position];
|
|
3235
|
+
return new BooleanValue(n.nodeName.match(this.regex));
|
|
3236
|
+
}
|
|
3237
|
+
};
|
|
3238
|
+
|
|
3239
|
+
// src/xpath/node-tests/node-test-pi.ts
|
|
3240
|
+
var NodeTestPI = class {
|
|
3241
|
+
constructor(target) {
|
|
3242
|
+
this.target = target;
|
|
3243
|
+
}
|
|
3244
|
+
evaluate(context) {
|
|
3245
|
+
const node = context.nodeList[context.position];
|
|
3246
|
+
return new BooleanValue(
|
|
3247
|
+
node.nodeType == DOM_PROCESSING_INSTRUCTION_NODE && (!this.target || node.nodeName == this.target)
|
|
3248
|
+
);
|
|
3249
|
+
}
|
|
3250
|
+
};
|
|
3251
|
+
|
|
3252
|
+
// src/xpath/node-tests/node-test-text.ts
|
|
3253
|
+
var NodeTestText = class {
|
|
3254
|
+
evaluate(ctx) {
|
|
3255
|
+
return new BooleanValue(ctx.nodeList[ctx.position].nodeType == DOM_TEXT_NODE);
|
|
3256
|
+
}
|
|
3257
|
+
};
|
|
3258
|
+
|
|
3259
|
+
// src/xslt/functions.ts
|
|
3260
|
+
function calculateStepPriority(step) {
|
|
3261
|
+
const nodeTest = step.nodeTest;
|
|
3262
|
+
const hasPredicates = step.predicate && step.predicate.length > 0 || step.predicates && step.predicates.length > 0;
|
|
3263
|
+
if (hasPredicates) {
|
|
3264
|
+
return 0.5;
|
|
3265
|
+
}
|
|
3266
|
+
if (nodeTest && typeof nodeTest === "object" && "type" in nodeTest) {
|
|
3267
|
+
switch (nodeTest.type) {
|
|
3268
|
+
case "wildcard":
|
|
3269
|
+
if (nodeTest.name && nodeTest.name.endsWith(":*")) {
|
|
3270
|
+
return -0.25;
|
|
3271
|
+
}
|
|
3272
|
+
return -0.5;
|
|
3273
|
+
case "node-type":
|
|
3274
|
+
if (nodeTest.nodeType === "processing-instruction" && nodeTest.name) {
|
|
3275
|
+
return 0;
|
|
3276
|
+
}
|
|
3277
|
+
return -0.5;
|
|
3278
|
+
case "processing-instruction":
|
|
3279
|
+
return nodeTest.name ? 0 : -0.5;
|
|
3280
|
+
case "name":
|
|
3281
|
+
return 0;
|
|
3282
|
+
default:
|
|
3283
|
+
return 0;
|
|
3284
|
+
}
|
|
3285
|
+
}
|
|
3286
|
+
if (nodeTest instanceof NodeTestAny) {
|
|
3287
|
+
return -0.5;
|
|
3288
|
+
}
|
|
3289
|
+
if (nodeTest instanceof NodeTestElementOrAttribute) {
|
|
3290
|
+
return -0.5;
|
|
3291
|
+
}
|
|
3292
|
+
if (nodeTest instanceof NodeTestText) {
|
|
3293
|
+
return -0.5;
|
|
3294
|
+
}
|
|
3295
|
+
if (nodeTest instanceof NodeTestComment) {
|
|
3296
|
+
return -0.5;
|
|
3297
|
+
}
|
|
3298
|
+
if (nodeTest instanceof NodeTestPI) {
|
|
3299
|
+
return nodeTest.target ? 0 : -0.5;
|
|
3300
|
+
}
|
|
3301
|
+
if (nodeTest instanceof NodeTestNC) {
|
|
3302
|
+
return -0.25;
|
|
3303
|
+
}
|
|
3304
|
+
if (nodeTest instanceof NodeTestName) {
|
|
3305
|
+
return 0;
|
|
3306
|
+
}
|
|
3307
|
+
return 0;
|
|
3308
|
+
}
|
|
3309
|
+
function calculateLocationPathPriority(expr) {
|
|
3310
|
+
if (!expr.steps || expr.steps.length === 0) {
|
|
3311
|
+
if (expr.absolute) {
|
|
3312
|
+
return -0.5;
|
|
3313
|
+
}
|
|
3314
|
+
return 0;
|
|
3315
|
+
}
|
|
3316
|
+
if (expr.steps.length > 1) {
|
|
3317
|
+
return 0.5;
|
|
3318
|
+
}
|
|
3319
|
+
return calculateStepPriority(expr.steps[0]);
|
|
3320
|
+
}
|
|
3321
|
+
function calculateUnionExprPriority(expr, xPath) {
|
|
3322
|
+
const priority1 = calculateDefaultPriorityFromExpression(expr.expr1, xPath);
|
|
3323
|
+
const priority2 = calculateDefaultPriorityFromExpression(expr.expr2, xPath);
|
|
3324
|
+
return Math.min(priority1, priority2);
|
|
3325
|
+
}
|
|
3326
|
+
function calculateDefaultPriorityFromExpression(expr, xPath) {
|
|
3327
|
+
if (expr instanceof LocationExpr) {
|
|
3328
|
+
return calculateLocationPathPriority(expr);
|
|
3329
|
+
}
|
|
3330
|
+
if (expr instanceof UnionExpr) {
|
|
3331
|
+
return calculateUnionExprPriority(expr, xPath);
|
|
3332
|
+
}
|
|
3333
|
+
return 0.5;
|
|
3334
|
+
}
|
|
3335
|
+
function calculateDefaultPriority(pattern, xPath) {
|
|
3336
|
+
try {
|
|
3337
|
+
const expr = xPath.xPathParse(pattern, "self-and-siblings");
|
|
3338
|
+
return calculateDefaultPriorityFromExpression(expr, xPath);
|
|
3339
|
+
} catch (e) {
|
|
3340
|
+
console.warn(`Failed to parse pattern "${pattern}" for priority calculation:`, e);
|
|
3341
|
+
return 0;
|
|
3342
|
+
}
|
|
3343
|
+
}
|
|
3344
|
+
function matchesMode(template, mode) {
|
|
3345
|
+
const templateMode = template.getAttributeValue("mode");
|
|
3346
|
+
if (!mode) {
|
|
3347
|
+
return !templateMode || templateMode === "#default";
|
|
3348
|
+
}
|
|
3349
|
+
if (mode === "#all") {
|
|
3350
|
+
return true;
|
|
3351
|
+
}
|
|
3352
|
+
if (templateMode === "#all") {
|
|
3353
|
+
return true;
|
|
3354
|
+
}
|
|
3355
|
+
return templateMode === mode;
|
|
3356
|
+
}
|
|
3357
|
+
function isTemplate(node) {
|
|
3358
|
+
if (node.nodeType !== DOM_ELEMENT_NODE) {
|
|
3359
|
+
return false;
|
|
3360
|
+
}
|
|
3361
|
+
if (node.namespaceUri === "http://www.w3.org/1999/XSL/Transform") {
|
|
3362
|
+
return node.localName === "template";
|
|
3363
|
+
}
|
|
3364
|
+
return node.prefix === "xsl" && node.localName === "template";
|
|
3365
|
+
}
|
|
3366
|
+
function collectAndExpandTemplates(stylesheetElement, mode, xPath) {
|
|
3367
|
+
const templates = [];
|
|
3368
|
+
let docOrder = 0;
|
|
3369
|
+
for (const child of stylesheetElement.childNodes) {
|
|
3370
|
+
if (!isTemplate(child)) {
|
|
3371
|
+
continue;
|
|
3372
|
+
}
|
|
3373
|
+
if (!matchesMode(child, mode)) {
|
|
3374
|
+
continue;
|
|
3375
|
+
}
|
|
3376
|
+
const match = child.getAttributeValue("match");
|
|
3377
|
+
if (!match) {
|
|
3378
|
+
continue;
|
|
3379
|
+
}
|
|
3380
|
+
const priorityAttr = child.getAttributeValue("priority");
|
|
3381
|
+
const explicitPriority = priorityAttr ? parseFloat(priorityAttr) : null;
|
|
3382
|
+
const defaultPriority = calculateDefaultPriority(match, xPath);
|
|
3383
|
+
const effectivePriority = explicitPriority !== null && !isNaN(explicitPriority) ? explicitPriority : defaultPriority;
|
|
3384
|
+
templates.push({
|
|
3385
|
+
template: child,
|
|
3386
|
+
explicitPriority: explicitPriority !== null && !isNaN(explicitPriority) ? explicitPriority : null,
|
|
3387
|
+
defaultPriority,
|
|
3388
|
+
effectivePriority,
|
|
3389
|
+
importPrecedence: 0,
|
|
3390
|
+
// TODO: Set properly when xsl:import is fully implemented
|
|
3391
|
+
documentOrder: docOrder++,
|
|
3392
|
+
matchPattern: match
|
|
3393
|
+
});
|
|
3394
|
+
}
|
|
3395
|
+
return templates;
|
|
3396
|
+
}
|
|
3397
|
+
function splitUnionPattern(pattern) {
|
|
3398
|
+
const alternatives = [];
|
|
3399
|
+
let current = "";
|
|
3400
|
+
let depth = 0;
|
|
3401
|
+
let inSingleQuote = false;
|
|
3402
|
+
let inDoubleQuote = false;
|
|
3403
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
3404
|
+
const char = pattern[i];
|
|
3405
|
+
if (char === "'" && !inDoubleQuote) {
|
|
3406
|
+
inSingleQuote = !inSingleQuote;
|
|
3407
|
+
current += char;
|
|
3408
|
+
} else if (char === '"' && !inSingleQuote) {
|
|
3409
|
+
inDoubleQuote = !inDoubleQuote;
|
|
3410
|
+
current += char;
|
|
3411
|
+
} else if (!inSingleQuote && !inDoubleQuote) {
|
|
3412
|
+
if (char === "[" || char === "(") {
|
|
3413
|
+
depth++;
|
|
3414
|
+
current += char;
|
|
3415
|
+
} else if (char === "]" || char === ")") {
|
|
3416
|
+
depth--;
|
|
3417
|
+
current += char;
|
|
3418
|
+
} else if (char === "|" && depth === 0) {
|
|
3419
|
+
alternatives.push(current.trim());
|
|
3420
|
+
current = "";
|
|
3421
|
+
} else {
|
|
3422
|
+
current += char;
|
|
3423
|
+
}
|
|
3424
|
+
} else {
|
|
3425
|
+
current += char;
|
|
3426
|
+
}
|
|
3427
|
+
}
|
|
3428
|
+
if (current.trim()) {
|
|
3429
|
+
alternatives.push(current.trim());
|
|
3430
|
+
}
|
|
3431
|
+
return alternatives;
|
|
3432
|
+
}
|
|
3433
|
+
function nodeMatchesSinglePattern(node, pattern, context, matchResolver, xPath) {
|
|
3434
|
+
if (pattern === "/") {
|
|
3435
|
+
return node.nodeName === "#document";
|
|
3436
|
+
}
|
|
3437
|
+
if (pattern.startsWith("@")) {
|
|
3438
|
+
if (node.nodeType !== 2) {
|
|
3439
|
+
return false;
|
|
3440
|
+
}
|
|
3441
|
+
const attrPattern = pattern.substring(1);
|
|
3442
|
+
if (attrPattern === "*") {
|
|
3443
|
+
return true;
|
|
3444
|
+
}
|
|
3445
|
+
const attrName = node.localName || node.nodeName;
|
|
3446
|
+
const patternLocalName = attrPattern.includes(":") ? attrPattern.substring(attrPattern.indexOf(":") + 1) : attrPattern;
|
|
3447
|
+
return attrName === patternLocalName || node.nodeName === attrPattern;
|
|
3448
|
+
}
|
|
3449
|
+
const nodeContext = context.clone([node], 0);
|
|
3450
|
+
try {
|
|
3451
|
+
const expr = xPath.xPathParse(pattern, "self-and-siblings");
|
|
3452
|
+
const nodes = matchResolver.expressionMatch(expr, nodeContext);
|
|
3453
|
+
if (nodes.some((n) => n.id === node.id)) {
|
|
3454
|
+
return true;
|
|
3455
|
+
}
|
|
3456
|
+
} catch (e) {
|
|
3457
|
+
}
|
|
3458
|
+
if (pattern === "*" && node.nodeType === DOM_ELEMENT_NODE) {
|
|
3459
|
+
return true;
|
|
3460
|
+
}
|
|
3461
|
+
if (pattern.includes("[") || pattern.includes("/")) {
|
|
3462
|
+
try {
|
|
3463
|
+
const rootContext = context.clone([context.root], 0);
|
|
3464
|
+
const descendantPattern = pattern.startsWith("/") ? pattern : "//" + pattern;
|
|
3465
|
+
const expr = xPath.xPathParse(descendantPattern);
|
|
3466
|
+
const nodes = matchResolver.expressionMatch(expr, rootContext);
|
|
3467
|
+
if (nodes.some((n) => n.id === node.id)) {
|
|
3468
|
+
return true;
|
|
3469
|
+
}
|
|
3470
|
+
} catch (e) {
|
|
3471
|
+
}
|
|
3472
|
+
}
|
|
3473
|
+
return false;
|
|
3474
|
+
}
|
|
3475
|
+
function nodeMatchesPattern(node, pattern, context, matchResolver, xPath) {
|
|
3476
|
+
const alternatives = splitUnionPattern(pattern);
|
|
3477
|
+
for (const alt of alternatives) {
|
|
3478
|
+
if (nodeMatchesSinglePattern(node, alt, context, matchResolver, xPath)) {
|
|
3479
|
+
return true;
|
|
3480
|
+
}
|
|
3481
|
+
}
|
|
3482
|
+
return false;
|
|
3483
|
+
}
|
|
3484
|
+
function selectBestTemplate(templates, context, matchResolver, xPath) {
|
|
3485
|
+
const matching = [];
|
|
3486
|
+
const currentNode = context.nodeList[context.position];
|
|
3487
|
+
for (const t of templates) {
|
|
3488
|
+
try {
|
|
3489
|
+
if (nodeMatchesPattern(currentNode, t.matchPattern, context, matchResolver, xPath)) {
|
|
3490
|
+
matching.push(t);
|
|
3491
|
+
}
|
|
3492
|
+
} catch (e) {
|
|
3493
|
+
console.warn(`Failed to match pattern "${t.matchPattern}":`, e);
|
|
3494
|
+
}
|
|
3495
|
+
}
|
|
3496
|
+
if (matching.length === 0) {
|
|
3497
|
+
return {
|
|
3498
|
+
selectedTemplate: null,
|
|
3499
|
+
hasConflict: false,
|
|
3500
|
+
conflictingTemplates: []
|
|
3501
|
+
};
|
|
3502
|
+
}
|
|
3503
|
+
matching.sort((a, b) => {
|
|
3504
|
+
if (a.importPrecedence !== b.importPrecedence) {
|
|
3505
|
+
return b.importPrecedence - a.importPrecedence;
|
|
3506
|
+
}
|
|
3507
|
+
if (a.effectivePriority !== b.effectivePriority) {
|
|
3508
|
+
return b.effectivePriority - a.effectivePriority;
|
|
3509
|
+
}
|
|
3510
|
+
return b.documentOrder - a.documentOrder;
|
|
3511
|
+
});
|
|
3512
|
+
const winner = matching[0];
|
|
3513
|
+
const conflicts = matching.filter(
|
|
3514
|
+
(t) => t.importPrecedence === winner.importPrecedence && t.effectivePriority === winner.effectivePriority
|
|
3515
|
+
);
|
|
3516
|
+
return {
|
|
3517
|
+
selectedTemplate: winner.template,
|
|
3518
|
+
hasConflict: conflicts.length > 1,
|
|
3519
|
+
conflictingTemplates: conflicts.length > 1 ? conflicts : []
|
|
3520
|
+
};
|
|
3521
|
+
}
|
|
3522
|
+
function emitConflictWarning(result, node) {
|
|
3523
|
+
if (!result.hasConflict || result.conflictingTemplates.length < 2) {
|
|
3524
|
+
return;
|
|
3525
|
+
}
|
|
3526
|
+
const patterns = result.conflictingTemplates.map((t) => `"${t.matchPattern}" (priority: ${t.effectivePriority})`).join(", ");
|
|
3527
|
+
console.warn(
|
|
3528
|
+
`XSLT Warning: Ambiguous template match for node <${node.nodeName}>. Multiple templates match with equal priority: ${patterns}. Using the last one in document order.`
|
|
3529
|
+
);
|
|
3530
|
+
}
|
|
3531
|
+
|
|
3532
|
+
// src/xslt/xslt.ts
|
|
3533
|
+
var Xslt = class {
|
|
3534
|
+
constructor(options = {
|
|
3535
|
+
cData: true,
|
|
3536
|
+
escape: true,
|
|
3537
|
+
selfClosingTags: true,
|
|
3538
|
+
parameters: []
|
|
3539
|
+
}) {
|
|
3540
|
+
this.xPath = new XPath();
|
|
3541
|
+
this.xmlParser = new XmlParser();
|
|
3542
|
+
this.matchResolver = new MatchResolver();
|
|
3543
|
+
this.options = {
|
|
3544
|
+
cData: options.cData === true,
|
|
3545
|
+
escape: options.escape === true,
|
|
3546
|
+
selfClosingTags: options.selfClosingTags === true,
|
|
3547
|
+
parameters: options.parameters || []
|
|
3548
|
+
};
|
|
3549
|
+
this.outputMethod = "xml";
|
|
3550
|
+
this.outputOmitXmlDeclaration = "no";
|
|
3551
|
+
this.decimalFormatSettings = {
|
|
3552
|
+
decimalSeparator: ".",
|
|
3553
|
+
groupingSeparator: ",",
|
|
3554
|
+
infinity: "Infinity",
|
|
3555
|
+
minusSign: "-",
|
|
3556
|
+
naN: "NaN",
|
|
3557
|
+
percent: "%",
|
|
3558
|
+
perMille: "\u2030",
|
|
3559
|
+
zeroDigit: "0",
|
|
3560
|
+
digit: "#",
|
|
3561
|
+
patternSeparator: ";"
|
|
3562
|
+
};
|
|
3563
|
+
this.firstTemplateRan = false;
|
|
3564
|
+
}
|
|
3565
|
+
/**
|
|
3566
|
+
* The exported entry point of the XSL-T processor.
|
|
3567
|
+
* @param xmlDoc The input document root, as DOM node.
|
|
3568
|
+
* @param stylesheet The stylesheet document root, as DOM node.
|
|
3569
|
+
* @returns the processed document, as XML text in a string.
|
|
3570
|
+
*/
|
|
3571
|
+
xsltProcess(xmlDoc, stylesheet) {
|
|
3572
|
+
return __async(this, null, function* () {
|
|
3573
|
+
const outputDocument = new XDocument();
|
|
3574
|
+
this.outputDocument = outputDocument;
|
|
3575
|
+
const expressionContext = new ExprContext([xmlDoc]);
|
|
3576
|
+
if (this.options.parameters.length > 0) {
|
|
3577
|
+
for (const parameter of this.options.parameters) {
|
|
3578
|
+
expressionContext.setVariable(parameter.name, new StringValue(parameter.value));
|
|
3579
|
+
}
|
|
3580
|
+
}
|
|
3581
|
+
yield this.xsltProcessContext(expressionContext, stylesheet, this.outputDocument);
|
|
3582
|
+
const transformedOutputXml = xmlTransformedText(outputDocument, {
|
|
3583
|
+
cData: this.options.cData,
|
|
3584
|
+
escape: this.options.escape,
|
|
3585
|
+
selfClosingTags: this.options.selfClosingTags,
|
|
3586
|
+
outputMethod: this.outputMethod
|
|
3587
|
+
});
|
|
3588
|
+
return transformedOutputXml;
|
|
3589
|
+
});
|
|
3590
|
+
}
|
|
3591
|
+
/**
|
|
3592
|
+
* The main entry point of the XSL-T processor, as explained on the top of the file.
|
|
3593
|
+
* @param context The input document root, as XPath `ExprContext`.
|
|
3594
|
+
* @param template The stylesheet document root, as DOM node.
|
|
3595
|
+
* @param output If set, the output where the transformation should occur.
|
|
3596
|
+
*/
|
|
3597
|
+
xsltProcessContext(context, template, output) {
|
|
3598
|
+
return __async(this, null, function* () {
|
|
3599
|
+
if (!this.isXsltElement(template)) {
|
|
3600
|
+
yield this.xsltPassThrough(context, template, output);
|
|
3601
|
+
} else {
|
|
3602
|
+
let node, select, value, nodes;
|
|
3603
|
+
switch (template.localName) {
|
|
3604
|
+
case "apply-imports":
|
|
3605
|
+
throw new Error(`not implemented: ${template.localName}`);
|
|
3606
|
+
case "apply-templates":
|
|
3607
|
+
yield this.xsltApplyTemplates(context, template, output);
|
|
3608
|
+
break;
|
|
3609
|
+
case "attribute":
|
|
3610
|
+
yield this.xsltAttribute(context, template, output);
|
|
3611
|
+
break;
|
|
3612
|
+
case "attribute-set":
|
|
3613
|
+
throw new Error(`not implemented: ${template.localName}`);
|
|
3614
|
+
case "call-template":
|
|
3615
|
+
yield this.xsltCallTemplate(context, template, output);
|
|
3616
|
+
break;
|
|
3617
|
+
case "choose":
|
|
3618
|
+
yield this.xsltChoose(context, template, output);
|
|
3619
|
+
break;
|
|
3620
|
+
case "comment":
|
|
3621
|
+
yield this.xsltComment(context, template, output);
|
|
3622
|
+
break;
|
|
3623
|
+
case "copy":
|
|
3624
|
+
node = this.xsltCopy(output || this.outputDocument, context.nodeList[context.position]);
|
|
3625
|
+
if (node) {
|
|
3626
|
+
yield this.xsltChildNodes(context, template, node);
|
|
3627
|
+
}
|
|
3628
|
+
break;
|
|
3629
|
+
case "copy-of":
|
|
3630
|
+
select = xmlGetAttribute(template, "select");
|
|
3631
|
+
value = this.xPath.xPathEval(select, context);
|
|
3632
|
+
const destinationNode = output || this.outputDocument;
|
|
3633
|
+
if (value.type === "node-set") {
|
|
3634
|
+
nodes = value.nodeSetValue();
|
|
3635
|
+
for (let i = 0; i < nodes.length; ++i) {
|
|
3636
|
+
this.xsltCopyOf(destinationNode, nodes[i]);
|
|
3637
|
+
}
|
|
3638
|
+
} else {
|
|
3639
|
+
let node2 = domCreateTextNode(this.outputDocument, value.stringValue());
|
|
3640
|
+
node2.siblingPosition = destinationNode.childNodes.length;
|
|
3641
|
+
domAppendChild(destinationNode, node2);
|
|
3642
|
+
}
|
|
3643
|
+
break;
|
|
3644
|
+
case "decimal-format":
|
|
3645
|
+
this.xsltDecimalFormat(context, template);
|
|
3646
|
+
break;
|
|
3647
|
+
case "element":
|
|
3648
|
+
yield this.xsltElement(context, template, output);
|
|
3649
|
+
break;
|
|
3650
|
+
case "fallback":
|
|
3651
|
+
throw new Error(`not implemented: ${template.localName}`);
|
|
3652
|
+
case "for-each":
|
|
3653
|
+
yield this.xsltForEach(context, template, output);
|
|
3654
|
+
break;
|
|
3655
|
+
case "if":
|
|
3656
|
+
yield this.xsltIf(context, template, output);
|
|
3657
|
+
break;
|
|
3658
|
+
case "import":
|
|
3659
|
+
yield this.xsltImport(context, template, output);
|
|
3660
|
+
break;
|
|
3661
|
+
case "include":
|
|
3662
|
+
yield this.xsltInclude(context, template, output);
|
|
3663
|
+
break;
|
|
3664
|
+
case "key":
|
|
3665
|
+
this.xsltKey(context, template);
|
|
3666
|
+
break;
|
|
3667
|
+
case "message":
|
|
3668
|
+
throw new Error(`not implemented: ${template.localName}`);
|
|
3669
|
+
case "namespace-alias":
|
|
3670
|
+
throw new Error(`not implemented: ${template.localName}`);
|
|
3671
|
+
case "number":
|
|
3672
|
+
throw new Error(`not implemented: ${template.localName}`);
|
|
3673
|
+
case "otherwise":
|
|
3674
|
+
throw new Error(`xsl:otherwise can't be used outside of xsl:choose.`);
|
|
3675
|
+
case "output":
|
|
3676
|
+
this.outputMethod = xmlGetAttribute(template, "method");
|
|
3677
|
+
this.outputOmitXmlDeclaration = xmlGetAttribute(template, "omit-xml-declaration");
|
|
3678
|
+
break;
|
|
3679
|
+
case "param":
|
|
3680
|
+
yield this.xsltVariable(context, template, false);
|
|
3681
|
+
break;
|
|
3682
|
+
case "preserve-space":
|
|
3683
|
+
throw new Error(`not implemented: ${template.localName}`);
|
|
3684
|
+
case "processing-instruction":
|
|
3685
|
+
throw new Error(`not implemented: ${template.localName}`);
|
|
3686
|
+
case "sort":
|
|
3687
|
+
this.xsltSort(context, template);
|
|
3688
|
+
break;
|
|
3689
|
+
case "strip-space":
|
|
3690
|
+
throw new Error(`not implemented: ${template.localName}`);
|
|
3691
|
+
case "stylesheet":
|
|
3692
|
+
case "transform":
|
|
3693
|
+
yield this.xsltTransformOrStylesheet(context, template, output);
|
|
3694
|
+
break;
|
|
3695
|
+
case "template":
|
|
3696
|
+
yield this.xsltTemplate(context, template, output);
|
|
3697
|
+
break;
|
|
3698
|
+
case "text":
|
|
3699
|
+
this.xsltText(context, template, output);
|
|
3700
|
+
break;
|
|
3701
|
+
case "value-of":
|
|
3702
|
+
this.xsltValueOf(context, template, output);
|
|
3703
|
+
break;
|
|
3704
|
+
case "variable":
|
|
3705
|
+
yield this.xsltVariable(context, template, true);
|
|
3706
|
+
break;
|
|
3707
|
+
case "when":
|
|
3708
|
+
throw new Error(`xsl:when can't be used outside of xsl:choose.`);
|
|
3709
|
+
case "with-param":
|
|
3710
|
+
throw new Error(`error if here: ${template.localName}`);
|
|
3711
|
+
default:
|
|
3712
|
+
throw new Error(`error if here: ${template.localName}`);
|
|
3713
|
+
}
|
|
3714
|
+
}
|
|
3715
|
+
});
|
|
3716
|
+
}
|
|
3717
|
+
/**
|
|
3718
|
+
* Implements `xsl:apply-templates`.
|
|
3719
|
+
* @param context The Expression Context.
|
|
3720
|
+
* @param template The template.
|
|
3721
|
+
* @param output The output. Only used if there's no corresponding output node already defined.
|
|
3722
|
+
* @protected
|
|
3723
|
+
*/
|
|
3724
|
+
xsltApplyTemplates(context, template, output) {
|
|
3725
|
+
return __async(this, null, function* () {
|
|
3726
|
+
const select = xmlGetAttribute(template, "select");
|
|
3727
|
+
let nodes = [];
|
|
3728
|
+
if (select) {
|
|
3729
|
+
nodes = this.xPath.xPathEval(select, context).nodeSetValue();
|
|
3730
|
+
} else {
|
|
3731
|
+
nodes = context.nodeList[context.position].childNodes;
|
|
3732
|
+
}
|
|
3733
|
+
const mode = xmlGetAttribute(template, "mode");
|
|
3734
|
+
const top = template.ownerDocument.documentElement;
|
|
3735
|
+
const expandedTemplates = collectAndExpandTemplates(top, mode, this.xPath);
|
|
3736
|
+
const modifiedContext = context.clone(nodes);
|
|
3737
|
+
for (let j = 0; j < modifiedContext.contextSize(); ++j) {
|
|
3738
|
+
const currentNode = modifiedContext.nodeList[j];
|
|
3739
|
+
if (currentNode.nodeType === DOM_TEXT_NODE) {
|
|
3740
|
+
const textNodeContext = context.clone(
|
|
3741
|
+
[currentNode],
|
|
3742
|
+
0
|
|
3743
|
+
);
|
|
3744
|
+
this.commonLogicTextNode(textNodeContext, currentNode, output);
|
|
3745
|
+
} else {
|
|
3746
|
+
const clonedContext = modifiedContext.clone(
|
|
3747
|
+
[currentNode],
|
|
3748
|
+
0
|
|
3749
|
+
);
|
|
3750
|
+
clonedContext.inApplyTemplates = true;
|
|
3751
|
+
const selection = selectBestTemplate(
|
|
3752
|
+
expandedTemplates,
|
|
3753
|
+
clonedContext,
|
|
3754
|
+
this.matchResolver,
|
|
3755
|
+
this.xPath
|
|
3756
|
+
);
|
|
3757
|
+
if (selection.hasConflict) {
|
|
3758
|
+
emitConflictWarning(selection, currentNode);
|
|
3759
|
+
}
|
|
3760
|
+
if (selection.selectedTemplate) {
|
|
3761
|
+
yield this.xsltChildNodes(clonedContext, selection.selectedTemplate, output);
|
|
3762
|
+
}
|
|
3763
|
+
}
|
|
3764
|
+
}
|
|
3765
|
+
});
|
|
3766
|
+
}
|
|
3767
|
+
/**
|
|
3768
|
+
* Implements `xsl:attribute`.
|
|
3769
|
+
* @param context The Expression Context.
|
|
3770
|
+
* @param template The template.
|
|
3771
|
+
* @param output The output. Only used if there's no corresponding output node already defined.
|
|
3772
|
+
* @protected
|
|
3773
|
+
*/
|
|
3774
|
+
xsltAttribute(context, template, output) {
|
|
3775
|
+
return __async(this, null, function* () {
|
|
3776
|
+
const nameExpr = xmlGetAttribute(template, "name");
|
|
3777
|
+
const name = this.xsltAttributeValue(nameExpr, context);
|
|
3778
|
+
const documentFragment = domCreateDocumentFragment(this.outputDocument);
|
|
3779
|
+
yield this.xsltChildNodes(context, template, documentFragment);
|
|
3780
|
+
const value = xmlValueLegacyBehavior(documentFragment);
|
|
3781
|
+
if (output) {
|
|
3782
|
+
domSetAttribute(output, name, value);
|
|
3783
|
+
}
|
|
3784
|
+
});
|
|
3785
|
+
}
|
|
3786
|
+
/**
|
|
3787
|
+
* Implements `xsl:call-template`.
|
|
3788
|
+
* @param context The Expression Context.
|
|
3789
|
+
* @param template The template.
|
|
3790
|
+
* @param output The output, used when a fragment is passed by a previous step.
|
|
3791
|
+
*/
|
|
3792
|
+
xsltCallTemplate(context, template, output) {
|
|
3793
|
+
return __async(this, null, function* () {
|
|
3794
|
+
const name = xmlGetAttribute(template, "name");
|
|
3795
|
+
const top = template.ownerDocument.documentElement;
|
|
3796
|
+
const paramContext = context.clone();
|
|
3797
|
+
yield this.xsltWithParam(paramContext, template);
|
|
3798
|
+
for (let i = 0; i < top.childNodes.length; ++i) {
|
|
3799
|
+
let childNode = top.childNodes[i];
|
|
3800
|
+
if (childNode.nodeType === DOM_ELEMENT_NODE && this.isXsltElement(childNode, "template") && domGetAttributeValue(childNode, "name") === name) {
|
|
3801
|
+
yield this.xsltChildNodes(paramContext, childNode, output);
|
|
3802
|
+
break;
|
|
3803
|
+
}
|
|
3804
|
+
}
|
|
3805
|
+
});
|
|
3806
|
+
}
|
|
3807
|
+
/**
|
|
3808
|
+
* Implements `xsl:choose`, its child nodes `xsl:when`, and
|
|
3809
|
+
* `xsl:otherwise`.
|
|
3810
|
+
* @param context The Expression Context.
|
|
3811
|
+
* @param template The template.
|
|
3812
|
+
* @param output The output. Only used if there's no corresponding output node already defined.
|
|
3813
|
+
*/
|
|
3814
|
+
xsltChoose(context, template, output) {
|
|
3815
|
+
return __async(this, null, function* () {
|
|
3816
|
+
for (const childNode of template.childNodes) {
|
|
3817
|
+
if (childNode.nodeType !== DOM_ELEMENT_NODE) {
|
|
3818
|
+
continue;
|
|
3819
|
+
}
|
|
3820
|
+
if (this.isXsltElement(childNode, "when")) {
|
|
3821
|
+
const test = xmlGetAttribute(childNode, "test");
|
|
3822
|
+
if (this.xPath.xPathEval(test, context).booleanValue()) {
|
|
3823
|
+
yield this.xsltChildNodes(context, childNode, output);
|
|
3824
|
+
break;
|
|
3825
|
+
}
|
|
3826
|
+
} else if (this.isXsltElement(childNode, "otherwise")) {
|
|
3827
|
+
yield this.xsltChildNodes(context, childNode, output);
|
|
3828
|
+
break;
|
|
3829
|
+
}
|
|
3830
|
+
}
|
|
3831
|
+
});
|
|
3832
|
+
}
|
|
3833
|
+
/**
|
|
3834
|
+
* Implements `xsl:copy` for all node types.
|
|
3835
|
+
* @param {XNode} destination the node being copied to, part of output document.
|
|
3836
|
+
* @param {XNode} source the node being copied, part in input document.
|
|
3837
|
+
* @returns {XNode|null} If an element node was created, the element node. Otherwise, null.
|
|
3838
|
+
*/
|
|
3839
|
+
xsltCopy(destination, source) {
|
|
3840
|
+
if (source.nodeType == DOM_ELEMENT_NODE) {
|
|
3841
|
+
let node = domCreateElement(this.outputDocument, source.nodeName);
|
|
3842
|
+
if (source.namespaceUri !== null && source.namespaceUri !== void 0) {
|
|
3843
|
+
domSetAttribute(node, "xmlns", source.namespaceUri);
|
|
3844
|
+
}
|
|
3845
|
+
node.siblingPosition = destination.childNodes.length;
|
|
3846
|
+
domAppendChild(destination, node);
|
|
3847
|
+
return node;
|
|
3848
|
+
}
|
|
3849
|
+
if (source.nodeType == DOM_TEXT_NODE) {
|
|
3850
|
+
let node = domCreateTextNode(this.outputDocument, source.nodeValue);
|
|
3851
|
+
node.siblingPosition = destination.childNodes.length;
|
|
3852
|
+
domAppendChild(destination, node);
|
|
3853
|
+
} else if (source.nodeType == DOM_CDATA_SECTION_NODE) {
|
|
3854
|
+
let node = domCreateCDATASection(this.outputDocument, source.nodeValue);
|
|
3855
|
+
node.siblingPosition = destination.childNodes.length;
|
|
3856
|
+
domAppendChild(destination, node);
|
|
3857
|
+
} else if (source.nodeType == DOM_COMMENT_NODE) {
|
|
3858
|
+
let node = domCreateComment(this.outputDocument, source.nodeValue);
|
|
3859
|
+
node.siblingPosition = destination.childNodes.length;
|
|
3860
|
+
domAppendChild(destination, node);
|
|
3861
|
+
} else if (source.nodeType == DOM_ATTRIBUTE_NODE) {
|
|
3862
|
+
domSetAttribute(destination, source.nodeName, source.nodeValue);
|
|
3863
|
+
}
|
|
3864
|
+
return null;
|
|
3865
|
+
}
|
|
3866
|
+
/**
|
|
3867
|
+
* Implements `xsl:comment`.
|
|
3868
|
+
* @param context The Expression Context.
|
|
3869
|
+
* @param template The template.
|
|
3870
|
+
* @param output The output. Only used if there's no corresponding output node already defined.
|
|
3871
|
+
*/
|
|
3872
|
+
xsltComment(context, template, output) {
|
|
3873
|
+
return __async(this, null, function* () {
|
|
3874
|
+
const node = domCreateDocumentFragment(this.outputDocument);
|
|
3875
|
+
yield this.xsltChildNodes(context, template, node);
|
|
3876
|
+
const commentData = xmlValue(node);
|
|
3877
|
+
const commentNode = domCreateComment(this.outputDocument, commentData);
|
|
3878
|
+
const resolvedOutput = output || this.outputDocument;
|
|
3879
|
+
resolvedOutput.appendChild(commentNode);
|
|
3880
|
+
});
|
|
3881
|
+
}
|
|
3882
|
+
/**
|
|
3883
|
+
* Implements `xsl:copy-of` for node-set values of the select
|
|
3884
|
+
* expression. Recurses down the source node tree, which is part of
|
|
3885
|
+
* the input document.
|
|
3886
|
+
* @param {XNode} destination the node being copied to, part of output document.
|
|
3887
|
+
* @param {XNode} source the node being copied, part in input document.
|
|
3888
|
+
*/
|
|
3889
|
+
xsltCopyOf(destination, source) {
|
|
3890
|
+
if (source.nodeType == DOM_DOCUMENT_FRAGMENT_NODE || source.nodeType == DOM_DOCUMENT_NODE) {
|
|
3891
|
+
for (let i = 0; i < source.childNodes.length; ++i) {
|
|
3892
|
+
this.xsltCopyOf(destination, source.childNodes[i]);
|
|
3893
|
+
}
|
|
3894
|
+
} else {
|
|
3895
|
+
const node = this.xsltCopy(destination, source);
|
|
3896
|
+
if (node) {
|
|
3897
|
+
for (let i = 0; i < source.childNodes.length; ++i) {
|
|
3898
|
+
this.xsltCopyOf(node, source.childNodes[i]);
|
|
3899
|
+
}
|
|
3900
|
+
}
|
|
3901
|
+
}
|
|
3902
|
+
}
|
|
3903
|
+
/**
|
|
3904
|
+
* Implements `xsl:decimal-format`, registering the settings in this instance
|
|
3905
|
+
* and the current context.
|
|
3906
|
+
* @param context The Expression Context.
|
|
3907
|
+
* @param template The template.
|
|
3908
|
+
*/
|
|
3909
|
+
xsltDecimalFormat(context, template) {
|
|
3910
|
+
const name = xmlGetAttribute(template, "name");
|
|
3911
|
+
const decimalSeparator = xmlGetAttribute(template, "decimal-separator");
|
|
3912
|
+
const groupingSeparator = xmlGetAttribute(template, "grouping-separator");
|
|
3913
|
+
const infinity = xmlGetAttribute(template, "infinity");
|
|
3914
|
+
const minusSign = xmlGetAttribute(template, "minus-sign");
|
|
3915
|
+
const naN = xmlGetAttribute(template, "NaN");
|
|
3916
|
+
const percent = xmlGetAttribute(template, "percent");
|
|
3917
|
+
const perMille = xmlGetAttribute(template, "per-mille");
|
|
3918
|
+
const zeroDigit = xmlGetAttribute(template, "zero-digit");
|
|
3919
|
+
const digit = xmlGetAttribute(template, "digit");
|
|
3920
|
+
const patternSeparator = xmlGetAttribute(template, "pattern-separator");
|
|
3921
|
+
this.decimalFormatSettings = {
|
|
3922
|
+
name: name || this.decimalFormatSettings.name,
|
|
3923
|
+
decimalSeparator: decimalSeparator || this.decimalFormatSettings.decimalSeparator,
|
|
3924
|
+
groupingSeparator: groupingSeparator || this.decimalFormatSettings.groupingSeparator,
|
|
3925
|
+
infinity: infinity || this.decimalFormatSettings.infinity,
|
|
3926
|
+
minusSign: minusSign || this.decimalFormatSettings.minusSign,
|
|
3927
|
+
naN: naN || this.decimalFormatSettings.naN,
|
|
3928
|
+
percent: percent || this.decimalFormatSettings.percent,
|
|
3929
|
+
perMille: perMille || this.decimalFormatSettings.perMille,
|
|
3930
|
+
zeroDigit: zeroDigit || this.decimalFormatSettings.zeroDigit,
|
|
3931
|
+
digit: digit || this.decimalFormatSettings.digit,
|
|
3932
|
+
patternSeparator: patternSeparator || this.decimalFormatSettings.patternSeparator
|
|
3933
|
+
};
|
|
3934
|
+
context.decimalFormatSettings = this.decimalFormatSettings;
|
|
3935
|
+
}
|
|
3936
|
+
/**
|
|
3937
|
+
* Implements `xsl:element`.
|
|
3938
|
+
* @param context The Expression Context.
|
|
3939
|
+
* @param template The template.
|
|
3940
|
+
*/
|
|
3941
|
+
xsltElement(context, template, output) {
|
|
3942
|
+
return __async(this, null, function* () {
|
|
3943
|
+
const nameExpr = xmlGetAttribute(template, "name");
|
|
3944
|
+
const name = this.xsltAttributeValue(nameExpr, context);
|
|
3945
|
+
const node = domCreateElement(this.outputDocument, name);
|
|
3946
|
+
domAppendChild(output || this.outputDocument, node);
|
|
3947
|
+
const clonedContext = context.clone(void 0, 0);
|
|
3948
|
+
yield this.xsltChildNodes(clonedContext, template, node);
|
|
3949
|
+
});
|
|
3950
|
+
}
|
|
3951
|
+
/**
|
|
3952
|
+
* Implements `xsl:for-each`.
|
|
3953
|
+
* @param context The Expression Context.
|
|
3954
|
+
* @param template The template.
|
|
3955
|
+
* @param output The output.
|
|
3956
|
+
*/
|
|
3957
|
+
xsltForEach(context, template, output) {
|
|
3958
|
+
return __async(this, null, function* () {
|
|
3959
|
+
const select = xmlGetAttribute(template, "select");
|
|
3960
|
+
const nodes = this.xPath.xPathEval(select, context).nodeSetValue();
|
|
3961
|
+
if (nodes.length === 0) {
|
|
3962
|
+
return;
|
|
3963
|
+
}
|
|
3964
|
+
const sortContext = context.clone(nodes);
|
|
3965
|
+
this.xsltSort(sortContext, template);
|
|
3966
|
+
const nodesWithParent = sortContext.nodeList.filter((n) => n.parentNode !== null && n.parentNode !== void 0);
|
|
3967
|
+
if (nodesWithParent.length <= 0) {
|
|
3968
|
+
throw new Error("Nodes with no parents defined.");
|
|
3969
|
+
}
|
|
3970
|
+
for (let i = 0; i < sortContext.contextSize(); ++i) {
|
|
3971
|
+
yield this.xsltChildNodes(sortContext.clone(sortContext.nodeList, i), template, output);
|
|
3972
|
+
}
|
|
3973
|
+
});
|
|
3974
|
+
}
|
|
3975
|
+
/**
|
|
3976
|
+
* Implements `xsl:if`.
|
|
3977
|
+
* @param context The Expression Context.
|
|
3978
|
+
* @param template The template.
|
|
3979
|
+
* @param output The output.
|
|
3980
|
+
*/
|
|
3981
|
+
xsltIf(context, template, output) {
|
|
3982
|
+
return __async(this, null, function* () {
|
|
3983
|
+
const test = xmlGetAttribute(template, "test");
|
|
3984
|
+
if (this.xPath.xPathEval(test, context).booleanValue()) {
|
|
3985
|
+
yield this.xsltChildNodes(context, template, output);
|
|
3986
|
+
}
|
|
3987
|
+
});
|
|
3988
|
+
}
|
|
3989
|
+
/**
|
|
3990
|
+
* Common implementation for `<xsl:import>` and `<xsl:include>`.
|
|
3991
|
+
* @param context The Expression Context.
|
|
3992
|
+
* @param template The template.
|
|
3993
|
+
* @param output The output.
|
|
3994
|
+
* @param isImport Whether this is an import (true) or include (false).
|
|
3995
|
+
*/
|
|
3996
|
+
xsltImportOrInclude(context, template, output, isImport) {
|
|
3997
|
+
return __async(this, null, function* () {
|
|
3998
|
+
const elementName = isImport ? "xsl:import" : "xsl:include";
|
|
3999
|
+
const [major, minor] = process.versions.node.split(".").map(Number);
|
|
4000
|
+
if (major <= 17 && minor < 5) {
|
|
4001
|
+
throw new Error(`Your Node.js version does not support \`<${elementName}>\`. If possible, please update your Node.js version to at least version 17.5.0.`);
|
|
4002
|
+
}
|
|
4003
|
+
if (!global.globalThis.fetch) {
|
|
4004
|
+
global.globalThis.fetch = fetch;
|
|
4005
|
+
global.globalThis.Headers = Headers;
|
|
4006
|
+
global.globalThis.Request = Request;
|
|
4007
|
+
global.globalThis.Response = Response;
|
|
4008
|
+
}
|
|
4009
|
+
const hrefAttributeFind = template.childNodes.filter((n) => n.nodeName === "href");
|
|
4010
|
+
if (hrefAttributeFind.length <= 0) {
|
|
4011
|
+
throw new Error(`<${elementName}> with no href attribute defined.`);
|
|
4012
|
+
}
|
|
4013
|
+
const hrefAttribute = hrefAttributeFind[0];
|
|
4014
|
+
const fetchTest = yield global.globalThis.fetch(hrefAttribute.nodeValue);
|
|
4015
|
+
const fetchResponse = yield fetchTest.text();
|
|
4016
|
+
const includedXslt = this.xmlParser.xmlParse(fetchResponse);
|
|
4017
|
+
yield this.xsltChildNodes(context, includedXslt.childNodes[0], output);
|
|
4018
|
+
});
|
|
4019
|
+
}
|
|
4020
|
+
/**
|
|
4021
|
+
* Implements `<xsl:import>`. For now the code is nearly identical to `<xsl:include>`, but there's
|
|
4022
|
+
* no precedence evaluation implemented yet.
|
|
4023
|
+
* @param context The Expression Context.
|
|
4024
|
+
* @param template The template.
|
|
4025
|
+
* @param output The output.
|
|
4026
|
+
*/
|
|
4027
|
+
xsltImport(context, template, output) {
|
|
4028
|
+
return __async(this, null, function* () {
|
|
4029
|
+
yield this.xsltImportOrInclude(context, template, output, true);
|
|
4030
|
+
});
|
|
4031
|
+
}
|
|
4032
|
+
/**
|
|
4033
|
+
* Implements `xsl:include`.
|
|
4034
|
+
* @param context The Expression Context.
|
|
4035
|
+
* @param template The template.
|
|
4036
|
+
* @param output The output.
|
|
4037
|
+
*/
|
|
4038
|
+
xsltInclude(context, template, output) {
|
|
4039
|
+
return __async(this, null, function* () {
|
|
4040
|
+
yield this.xsltImportOrInclude(context, template, output, false);
|
|
4041
|
+
});
|
|
4042
|
+
}
|
|
4043
|
+
/**
|
|
4044
|
+
* Implements `xsl:key`.
|
|
4045
|
+
* @param context The Expression Context.
|
|
4046
|
+
* @param template The template.
|
|
4047
|
+
*/
|
|
4048
|
+
xsltKey(context, template) {
|
|
4049
|
+
const name = xmlGetAttribute(template, "name");
|
|
4050
|
+
const match = xmlGetAttribute(template, "match");
|
|
4051
|
+
const use = xmlGetAttribute(template, "use");
|
|
4052
|
+
if (!name || !match || !use) {
|
|
4053
|
+
let errorMessage = "<xsl:key> missing required parameters: ";
|
|
4054
|
+
if (!name) {
|
|
4055
|
+
errorMessage += "name, ";
|
|
4056
|
+
}
|
|
4057
|
+
if (!match) {
|
|
4058
|
+
errorMessage += "match, ";
|
|
4059
|
+
}
|
|
4060
|
+
if (!use) {
|
|
4061
|
+
errorMessage += "use, ";
|
|
4062
|
+
}
|
|
4063
|
+
errorMessage = errorMessage.slice(0, -2);
|
|
4064
|
+
throw new Error(errorMessage);
|
|
4065
|
+
}
|
|
4066
|
+
let keyContext;
|
|
4067
|
+
if (context.nodeList[context.position].nodeName === "#document") {
|
|
4068
|
+
keyContext = context.clone(context.nodeList[context.position].childNodes);
|
|
4069
|
+
} else {
|
|
4070
|
+
keyContext = context;
|
|
4071
|
+
}
|
|
4072
|
+
const nodes = this.xsltMatch(match, keyContext);
|
|
4073
|
+
if (!(name in context.keys)) {
|
|
4074
|
+
context.keys[name] = {};
|
|
4075
|
+
}
|
|
4076
|
+
for (const node of nodes) {
|
|
4077
|
+
const nodeContext = context.clone([node]);
|
|
4078
|
+
const attribute = this.xPath.xPathEval(use, nodeContext);
|
|
4079
|
+
const attributeValue = attribute.stringValue();
|
|
4080
|
+
context.keys[name][attributeValue] = new NodeSetValue([node]);
|
|
4081
|
+
}
|
|
4082
|
+
}
|
|
4083
|
+
/**
|
|
4084
|
+
* Orders the current node list in the input context according to the
|
|
4085
|
+
* sort order specified by xsl:sort child nodes of the current
|
|
4086
|
+
* template node. This happens before the operation specified by the
|
|
4087
|
+
* current template node is executed.
|
|
4088
|
+
* @param context The expression context.
|
|
4089
|
+
* @param template The template node.
|
|
4090
|
+
* @todo case-order is not implemented.
|
|
4091
|
+
*/
|
|
4092
|
+
xsltSort(context, template) {
|
|
4093
|
+
const sort = [];
|
|
4094
|
+
for (const childNode of template.childNodes) {
|
|
4095
|
+
if (childNode.nodeType == DOM_ELEMENT_NODE && this.isXsltElement(childNode, "sort")) {
|
|
4096
|
+
const select = xmlGetAttribute(childNode, "select");
|
|
4097
|
+
const expression = this.xPath.xPathParse(select);
|
|
4098
|
+
const type = xmlGetAttribute(childNode, "data-type") || "text";
|
|
4099
|
+
const order = xmlGetAttribute(childNode, "order") || "ascending";
|
|
4100
|
+
sort.push({
|
|
4101
|
+
expr: expression,
|
|
4102
|
+
type,
|
|
4103
|
+
order
|
|
4104
|
+
});
|
|
4105
|
+
}
|
|
4106
|
+
}
|
|
4107
|
+
this.xPath.xPathSort(context, sort);
|
|
4108
|
+
}
|
|
4109
|
+
/**
|
|
4110
|
+
* Implements `xsl:template`.
|
|
4111
|
+
* @param context The Expression Context.
|
|
4112
|
+
* @param template The `<xsl:template>` node.
|
|
4113
|
+
* @param output The output. In general, a fragment that will be used by
|
|
4114
|
+
* the caller.
|
|
4115
|
+
*/
|
|
4116
|
+
xsltTemplate(context, template, output) {
|
|
4117
|
+
return __async(this, null, function* () {
|
|
4118
|
+
if (!context.inApplyTemplates && context.baseTemplateMatched) {
|
|
4119
|
+
return;
|
|
4120
|
+
}
|
|
4121
|
+
const match = xmlGetAttribute(template, "match");
|
|
4122
|
+
if (!match) return;
|
|
4123
|
+
const nodes = this.xsltMatch(match, context, "self-and-siblings");
|
|
4124
|
+
if (nodes.length > 0) {
|
|
4125
|
+
this.firstTemplateRan = true;
|
|
4126
|
+
if (!context.inApplyTemplates) {
|
|
4127
|
+
context.baseTemplateMatched = true;
|
|
4128
|
+
}
|
|
4129
|
+
const templateContext = context.clone(nodes, 0);
|
|
4130
|
+
yield this.xsltChildNodes(templateContext, template, output);
|
|
4131
|
+
}
|
|
4132
|
+
});
|
|
4133
|
+
}
|
|
4134
|
+
xsltText(context, template, output) {
|
|
4135
|
+
const text = xmlValue(template);
|
|
4136
|
+
const node = domCreateTextNode(this.outputDocument, text);
|
|
4137
|
+
const disableOutputEscaping = template.childNodes.filter(
|
|
4138
|
+
(a) => a.nodeType === DOM_ATTRIBUTE_NODE && a.nodeName === "disable-output-escaping"
|
|
4139
|
+
);
|
|
4140
|
+
if (disableOutputEscaping.length > 0 && disableOutputEscaping[0].nodeValue === "yes") {
|
|
4141
|
+
node.escape = false;
|
|
4142
|
+
}
|
|
4143
|
+
const destinationTextNode = output || this.outputDocument;
|
|
4144
|
+
destinationTextNode.appendChild(node);
|
|
4145
|
+
}
|
|
4146
|
+
/**
|
|
4147
|
+
* Implements `<xsl:stylesheet>` and `<xsl:transform>`, and its corresponding
|
|
4148
|
+
* validations.
|
|
4149
|
+
* @param context The Expression Context.
|
|
4150
|
+
* @param template The `<xsl:stylesheet>` or `<xsl:transform>` node.
|
|
4151
|
+
* @param output The output. In general, a fragment that will be used by
|
|
4152
|
+
* the caller.
|
|
4153
|
+
*/
|
|
4154
|
+
xsltTransformOrStylesheet(context, template, output) {
|
|
4155
|
+
return __async(this, null, function* () {
|
|
4156
|
+
for (let stylesheetAttribute of template.childNodes.filter((n) => n.nodeType === DOM_ATTRIBUTE_NODE)) {
|
|
4157
|
+
switch (stylesheetAttribute.nodeName) {
|
|
4158
|
+
case "version":
|
|
4159
|
+
this.version = stylesheetAttribute.nodeValue;
|
|
4160
|
+
if (!["1.0", "2.0", "3.0"].includes(this.version)) {
|
|
4161
|
+
throw new Error(
|
|
4162
|
+
`XSLT version not defined or invalid. Actual resolved version: ${this.version || "(none)"}.`
|
|
4163
|
+
);
|
|
4164
|
+
}
|
|
4165
|
+
context.xsltVersion = this.version;
|
|
4166
|
+
break;
|
|
4167
|
+
default:
|
|
4168
|
+
if (stylesheetAttribute.prefix === "xmlns") {
|
|
4169
|
+
context.knownNamespaces[stylesheetAttribute.localName] = stylesheetAttribute.nodeValue;
|
|
4170
|
+
}
|
|
4171
|
+
break;
|
|
4172
|
+
}
|
|
4173
|
+
}
|
|
4174
|
+
let importsDone = false;
|
|
4175
|
+
for (const child of template.childNodes) {
|
|
4176
|
+
if (child.nodeType === DOM_ELEMENT_NODE) {
|
|
4177
|
+
if (this.isXsltElement(child, "import")) {
|
|
4178
|
+
if (importsDone) {
|
|
4179
|
+
throw new Error("<xsl:import> should be the first child node of <xsl:stylesheet> or <xsl:transform>.");
|
|
4180
|
+
}
|
|
4181
|
+
} else {
|
|
4182
|
+
importsDone = true;
|
|
4183
|
+
}
|
|
4184
|
+
}
|
|
4185
|
+
}
|
|
4186
|
+
const nonTemplates = [];
|
|
4187
|
+
const templates = [];
|
|
4188
|
+
for (const child of template.childNodes) {
|
|
4189
|
+
if (child.nodeType === DOM_ELEMENT_NODE && this.isXsltElement(child, "template")) {
|
|
4190
|
+
templates.push(child);
|
|
4191
|
+
} else {
|
|
4192
|
+
nonTemplates.push(child);
|
|
4193
|
+
}
|
|
4194
|
+
}
|
|
4195
|
+
const contextClone = context.clone();
|
|
4196
|
+
for (const child of nonTemplates) {
|
|
4197
|
+
yield this.xsltProcessContext(contextClone, child, output);
|
|
4198
|
+
}
|
|
4199
|
+
if (templates.length > 0) {
|
|
4200
|
+
const expandedTemplates = collectAndExpandTemplates(template, null, this.xPath);
|
|
4201
|
+
const matchCandidates = [];
|
|
4202
|
+
for (const t of expandedTemplates) {
|
|
4203
|
+
try {
|
|
4204
|
+
const matchedNodes = this.xsltMatch(t.matchPattern, contextClone);
|
|
4205
|
+
if (matchedNodes.length > 0) {
|
|
4206
|
+
matchCandidates.push({ priority: t, matchedNodes });
|
|
4207
|
+
}
|
|
4208
|
+
} catch (e) {
|
|
4209
|
+
console.warn(`Failed to match pattern "${t.matchPattern}":`, e);
|
|
4210
|
+
}
|
|
4211
|
+
}
|
|
4212
|
+
if (matchCandidates.length > 0) {
|
|
4213
|
+
matchCandidates.sort((a, b) => {
|
|
4214
|
+
if (a.priority.importPrecedence !== b.priority.importPrecedence) {
|
|
4215
|
+
return b.priority.importPrecedence - a.priority.importPrecedence;
|
|
4216
|
+
}
|
|
4217
|
+
if (a.priority.effectivePriority !== b.priority.effectivePriority) {
|
|
4218
|
+
return b.priority.effectivePriority - a.priority.effectivePriority;
|
|
4219
|
+
}
|
|
4220
|
+
return b.priority.documentOrder - a.priority.documentOrder;
|
|
4221
|
+
});
|
|
4222
|
+
const winner = matchCandidates[0];
|
|
4223
|
+
const conflicts = matchCandidates.filter(
|
|
4224
|
+
(t) => t.priority.importPrecedence === winner.priority.importPrecedence && t.priority.effectivePriority === winner.priority.effectivePriority
|
|
4225
|
+
);
|
|
4226
|
+
if (conflicts.length > 1) {
|
|
4227
|
+
const patterns = conflicts.map((t) => `"${t.priority.matchPattern}" (priority: ${t.priority.effectivePriority})`).join(", ");
|
|
4228
|
+
console.warn(
|
|
4229
|
+
`XSLT Warning: Ambiguous template match. Multiple templates match with equal priority: ${patterns}. Using the last one in document order.`
|
|
4230
|
+
);
|
|
4231
|
+
}
|
|
4232
|
+
this.firstTemplateRan = true;
|
|
4233
|
+
contextClone.baseTemplateMatched = true;
|
|
4234
|
+
const templateContext = contextClone.clone(winner.matchedNodes, 0);
|
|
4235
|
+
yield this.xsltChildNodes(templateContext, winner.priority.template, output);
|
|
4236
|
+
}
|
|
4237
|
+
}
|
|
4238
|
+
});
|
|
4239
|
+
}
|
|
4240
|
+
xsltValueOf(context, template, output) {
|
|
4241
|
+
const select = xmlGetAttribute(template, "select");
|
|
4242
|
+
const attribute = this.xPath.xPathEval(select, context);
|
|
4243
|
+
const value = attribute.stringValue();
|
|
4244
|
+
const node = domCreateTextNode(this.outputDocument, value);
|
|
4245
|
+
const targetOutput = output || this.outputDocument;
|
|
4246
|
+
node.siblingPosition = targetOutput.childNodes.length;
|
|
4247
|
+
targetOutput.appendChild(node);
|
|
4248
|
+
}
|
|
4249
|
+
/**
|
|
4250
|
+
* Evaluates a variable or parameter and set it in the current input
|
|
4251
|
+
* context. Implements `xsl:variable`, `xsl:param`, and `xsl:with-param`.
|
|
4252
|
+
*
|
|
4253
|
+
* @param context The expression context.
|
|
4254
|
+
* @param template The template node.
|
|
4255
|
+
* @param override flag that defines if the value computed here
|
|
4256
|
+
* overrides the one already in the input context if that is the
|
|
4257
|
+
* case. I.e. decides if this is a default value or a local
|
|
4258
|
+
* value. `xsl:variable` and `xsl:with-param` override; `xsl:param` doesn't.
|
|
4259
|
+
*/
|
|
4260
|
+
xsltVariable(context, template, override) {
|
|
4261
|
+
return __async(this, null, function* () {
|
|
4262
|
+
const name = xmlGetAttribute(template, "name");
|
|
4263
|
+
const select = xmlGetAttribute(template, "select");
|
|
4264
|
+
let value;
|
|
4265
|
+
const nonAttributeChildren = template.childNodes.filter((n) => n.nodeType !== DOM_ATTRIBUTE_NODE);
|
|
4266
|
+
if (nonAttributeChildren.length > 0) {
|
|
4267
|
+
const fragment = domCreateDocumentFragment(template.ownerDocument);
|
|
4268
|
+
yield this.xsltChildNodes(context, template, fragment);
|
|
4269
|
+
value = new NodeSetValue([fragment]);
|
|
4270
|
+
} else if (select) {
|
|
4271
|
+
value = this.xPath.xPathEval(select, context);
|
|
4272
|
+
} else {
|
|
4273
|
+
let parameterValue = "";
|
|
4274
|
+
const filteredParameter = this.options.parameters.filter((p) => p.name === name);
|
|
4275
|
+
if (filteredParameter.length > 0) {
|
|
4276
|
+
parameterValue = filteredParameter[0].value;
|
|
4277
|
+
}
|
|
4278
|
+
value = new StringValue(parameterValue);
|
|
4279
|
+
}
|
|
4280
|
+
if (override || !context.getVariable(name)) {
|
|
4281
|
+
context.setVariable(name, value);
|
|
4282
|
+
}
|
|
4283
|
+
});
|
|
4284
|
+
}
|
|
4285
|
+
/**
|
|
4286
|
+
* Traverses the template node tree. Calls the main processing
|
|
4287
|
+
* function with the current input context for every child node of the
|
|
4288
|
+
* current template node.
|
|
4289
|
+
* @param context Normally the Expression Context.
|
|
4290
|
+
* @param template The XSL-T definition.
|
|
4291
|
+
* @param output If set, the output where the transformation should occur.
|
|
4292
|
+
*/
|
|
4293
|
+
xsltChildNodes(context, template, output) {
|
|
4294
|
+
return __async(this, null, function* () {
|
|
4295
|
+
const contextClone = context.clone();
|
|
4296
|
+
for (let i = 0; i < template.childNodes.length; ++i) {
|
|
4297
|
+
yield this.xsltProcessContext(contextClone, template.childNodes[i], output);
|
|
4298
|
+
}
|
|
4299
|
+
});
|
|
4300
|
+
}
|
|
4301
|
+
/**
|
|
4302
|
+
* This logic is used in two different places:
|
|
4303
|
+
* - `xsltPassThrough`, if the template asks this library to write a text node;
|
|
4304
|
+
* - `xsltProcessContext`, `apply-templates` operation, when the current node is text.
|
|
4305
|
+
*
|
|
4306
|
+
* Text nodes always require a parent, and they never have children.
|
|
4307
|
+
* @param context The Expression Context.
|
|
4308
|
+
* @param template The template, that contains the node value to be written.
|
|
4309
|
+
* @param output The output.
|
|
4310
|
+
*/
|
|
4311
|
+
commonLogicTextNode(context, template, output) {
|
|
4312
|
+
if (output) {
|
|
4313
|
+
let node = domCreateTextNode(this.outputDocument, template.nodeValue);
|
|
4314
|
+
node.siblingPosition = output.childNodes.length;
|
|
4315
|
+
domAppendChild(output, node);
|
|
4316
|
+
}
|
|
4317
|
+
}
|
|
4318
|
+
/**
|
|
4319
|
+
* Passes template text to the output. The current template node does
|
|
4320
|
+
* not specify an XSL-T operation and therefore is appended to the
|
|
4321
|
+
* output with all its attributes. Then continues traversing the
|
|
4322
|
+
* template node tree.
|
|
4323
|
+
* @param context The Expression Context.
|
|
4324
|
+
* @param template The XSLT stylesheet or transformation.
|
|
4325
|
+
* @param output The output.
|
|
4326
|
+
*/
|
|
4327
|
+
xsltPassThrough(context, template, output) {
|
|
4328
|
+
return __async(this, null, function* () {
|
|
4329
|
+
switch (template.nodeType) {
|
|
4330
|
+
case DOM_TEXT_NODE:
|
|
4331
|
+
if (this.xsltPassText(template)) {
|
|
4332
|
+
this.commonLogicTextNode(context, template, output);
|
|
4333
|
+
}
|
|
4334
|
+
break;
|
|
4335
|
+
case DOM_ELEMENT_NODE:
|
|
4336
|
+
let node;
|
|
4337
|
+
let elementContext = context;
|
|
4338
|
+
node = context.nodeList[context.position];
|
|
4339
|
+
let newNode;
|
|
4340
|
+
newNode = domCreateElement(this.outputDocument, template.nodeName);
|
|
4341
|
+
newNode.siblingPosition = node.siblingPosition;
|
|
4342
|
+
domAppendChild(output || this.outputDocument, newNode);
|
|
4343
|
+
yield this.xsltChildNodes(elementContext, template, newNode);
|
|
4344
|
+
const templateAttributes = template.childNodes.filter((a) => (a == null ? void 0 : a.nodeType) === DOM_ATTRIBUTE_NODE);
|
|
4345
|
+
for (const attribute of templateAttributes) {
|
|
4346
|
+
const name = attribute.nodeName;
|
|
4347
|
+
const value = this.xsltAttributeValue(attribute.nodeValue, elementContext);
|
|
4348
|
+
domSetAttribute(newNode, name, value);
|
|
4349
|
+
}
|
|
4350
|
+
break;
|
|
4351
|
+
default:
|
|
4352
|
+
yield this.xsltChildNodes(context, template, output);
|
|
4353
|
+
}
|
|
4354
|
+
});
|
|
4355
|
+
}
|
|
4356
|
+
/**
|
|
4357
|
+
* Determines if a text node in the XSLT template document is to be
|
|
4358
|
+
* stripped according to XSLT whitespace stripping rules.
|
|
4359
|
+
* @see [XSLT], section 3.4.
|
|
4360
|
+
* @param template The XSLT template.
|
|
4361
|
+
* @returns TODO
|
|
4362
|
+
* @todo Whitespace stripping on the input document is
|
|
4363
|
+
* currently not implemented.
|
|
4364
|
+
*/
|
|
4365
|
+
xsltPassText(template) {
|
|
4366
|
+
if (!template.nodeValue.match(/^\s*$/)) {
|
|
4367
|
+
return true;
|
|
4368
|
+
}
|
|
4369
|
+
let element = template.parentNode;
|
|
4370
|
+
if (this.isXsltElement(element, "text")) {
|
|
4371
|
+
return true;
|
|
4372
|
+
}
|
|
4373
|
+
while (element && element.nodeType == DOM_ELEMENT_NODE) {
|
|
4374
|
+
const xmlspace = domGetAttributeValue(element, "xml:space");
|
|
4375
|
+
if (xmlspace) {
|
|
4376
|
+
if (xmlspace == "default") {
|
|
4377
|
+
return false;
|
|
4378
|
+
}
|
|
4379
|
+
if (xmlspace == "preserve") {
|
|
4380
|
+
return true;
|
|
4381
|
+
}
|
|
4382
|
+
}
|
|
4383
|
+
element = element.parentNode;
|
|
4384
|
+
}
|
|
4385
|
+
return false;
|
|
4386
|
+
}
|
|
4387
|
+
findAttributeInContext(attributeName, context) {
|
|
4388
|
+
return context.nodeList[context.position].childNodes.find(
|
|
4389
|
+
(a) => a.nodeType === DOM_ATTRIBUTE_NODE && a.nodeName === attributeName
|
|
4390
|
+
);
|
|
4391
|
+
}
|
|
4392
|
+
/**
|
|
4393
|
+
* Evaluates an XSL-T attribute value template. Attribute value
|
|
4394
|
+
* templates are attributes on XSL-T elements that contain XPath
|
|
4395
|
+
* expressions in braces {}. The XSL-T expressions are evaluated in
|
|
4396
|
+
* the current input context.
|
|
4397
|
+
* @param value TODO
|
|
4398
|
+
* @param context TODO
|
|
4399
|
+
* @returns TODO
|
|
4400
|
+
*/
|
|
4401
|
+
xsltAttributeValue(value, context) {
|
|
4402
|
+
const parts = value.split("{");
|
|
4403
|
+
if (parts.length === 1) {
|
|
4404
|
+
return value;
|
|
4405
|
+
}
|
|
4406
|
+
let ret = "";
|
|
4407
|
+
for (let i = 0; i < parts.length; ++i) {
|
|
4408
|
+
const rp = parts[i].split("}");
|
|
4409
|
+
if (rp.length != 2) {
|
|
4410
|
+
ret += parts[i];
|
|
4411
|
+
continue;
|
|
4412
|
+
}
|
|
4413
|
+
const val = this.xPath.xPathEval(rp[0], context).stringValue();
|
|
4414
|
+
ret += val + rp[1];
|
|
4415
|
+
}
|
|
4416
|
+
return ret;
|
|
4417
|
+
}
|
|
4418
|
+
/**
|
|
4419
|
+
* Evaluates an XPath expression in the current input context as a
|
|
4420
|
+
* match.
|
|
4421
|
+
* @see [XSLT] section 5.2, paragraph 1
|
|
4422
|
+
* @param match TODO
|
|
4423
|
+
* @param context The Expression Context.
|
|
4424
|
+
* @param axis The XPath axis. Used when the match does not start with the parent.
|
|
4425
|
+
* @returns {XNode[]} A list of the found nodes.
|
|
4426
|
+
*/
|
|
4427
|
+
xsltMatch(match, context, axis) {
|
|
4428
|
+
const expression = this.xPath.xPathParse(match, axis);
|
|
4429
|
+
return this.matchResolver.expressionMatch(expression, context);
|
|
4430
|
+
}
|
|
4431
|
+
/**
|
|
4432
|
+
* Sets parameters defined by xsl:with-param child nodes of the
|
|
4433
|
+
* current template node, in the current input context. This happens
|
|
4434
|
+
* before the operation specified by the current template node is
|
|
4435
|
+
* executed.
|
|
4436
|
+
* @param context The Expression Context.
|
|
4437
|
+
* @param template The template node.
|
|
4438
|
+
*/
|
|
4439
|
+
xsltWithParam(context, template) {
|
|
4440
|
+
return __async(this, null, function* () {
|
|
4441
|
+
for (const childNode of template.childNodes) {
|
|
4442
|
+
if (childNode.nodeType === DOM_ELEMENT_NODE && this.isXsltElement(childNode, "with-param")) {
|
|
4443
|
+
yield this.xsltVariable(context, childNode, true);
|
|
4444
|
+
}
|
|
4445
|
+
}
|
|
4446
|
+
});
|
|
4447
|
+
}
|
|
4448
|
+
/**
|
|
4449
|
+
* Test if the given element is an XSLT element, optionally the one with the given name.
|
|
4450
|
+
* @param {XNode} element The element.
|
|
4451
|
+
* @param {string} opt_wantedName The name for comparison.
|
|
4452
|
+
* @returns True, if element is an XSL node. False otherwise.
|
|
4453
|
+
*/
|
|
4454
|
+
isXsltElement(element, opt_wantedName) {
|
|
4455
|
+
if (opt_wantedName && element.localName != opt_wantedName) return false;
|
|
4456
|
+
if (element.namespaceUri) return element.namespaceUri === "http://www.w3.org/1999/XSL/Transform";
|
|
4457
|
+
return element.prefix === "xsl";
|
|
4458
|
+
}
|
|
4459
|
+
};
|
|
4460
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
4461
|
+
0 && (module.exports = {
|
|
4462
|
+
ExprContext,
|
|
4463
|
+
XPath,
|
|
4464
|
+
XmlParser,
|
|
4465
|
+
Xslt,
|
|
4466
|
+
xmlEscapeText
|
|
4467
|
+
});
|
|
13
4468
|
//# sourceMappingURL=index.js.map
|