@tradik/xslt3 0.0.0-stage → 1.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/LICENSE.md +28 -0
- package/README.md +98 -2
- package/package.json +43 -4
- package/src/errors.js +20 -0
- package/src/functions/aggregates.js +144 -0
- package/src/functions/arraySort.js +33 -0
- package/src/functions/arrays.js +181 -0
- package/src/functions/attributeTypes.js +58 -0
- package/src/functions/baseUris.js +82 -0
- package/src/functions/collations.js +127 -0
- package/src/functions/collections.js +84 -0
- package/src/functions/context.js +83 -0
- package/src/functions/core.js +167 -0
- package/src/functions/datetime.js +203 -0
- package/src/functions/deepEqual.js +132 -0
- package/src/functions/diagnostics.js +88 -0
- package/src/functions/focus.js +34 -0
- package/src/functions/format/dateComponents.js +176 -0
- package/src/functions/format/datePicture.js +115 -0
- package/src/functions/format/decimalFormat.js +65 -0
- package/src/functions/format/digitPattern.js +145 -0
- package/src/functions/format/formatDateTime.js +204 -0
- package/src/functions/format/formatInteger.js +109 -0
- package/src/functions/format/formatNumber.js +165 -0
- package/src/functions/format/fractionFormat.js +80 -0
- package/src/functions/format/germanWords.js +89 -0
- package/src/functions/format/numberLanguages.js +41 -0
- package/src/functions/format/numberPicture.js +202 -0
- package/src/functions/format/numberWords.js +155 -0
- package/src/functions/format/place.js +74 -0
- package/src/functions/format/timezoneFormat.js +89 -0
- package/src/functions/higherOrder.js +106 -0
- package/src/functions/ids.js +127 -0
- package/src/functions/json/index.js +17 -0
- package/src/functions/json/jsonChars.js +110 -0
- package/src/functions/json/jsonParser.js +203 -0
- package/src/functions/json/jsonStrings.js +116 -0
- package/src/functions/json/jsonToXml.js +116 -0
- package/src/functions/json/parseJson.js +119 -0
- package/src/functions/json/xmlRepresentation.js +103 -0
- package/src/functions/json/xmlToJson.js +147 -0
- package/src/functions/library.js +47 -0
- package/src/functions/mapArrayEqual.js +41 -0
- package/src/functions/maps.js +152 -0
- package/src/functions/math.js +73 -0
- package/src/functions/nodePaths.js +181 -0
- package/src/functions/nodes.js +199 -0
- package/src/functions/numeric.js +148 -0
- package/src/functions/options.js +58 -0
- package/src/functions/parseIetfDate.js +104 -0
- package/src/functions/parseXml.js +77 -0
- package/src/functions/qnames.js +68 -0
- package/src/functions/random.js +128 -0
- package/src/functions/regex/analyzeString.js +144 -0
- package/src/functions/regex/charClass.js +112 -0
- package/src/functions/regex/compile.js +81 -0
- package/src/functions/regex/escapes.js +109 -0
- package/src/functions/regex/parser.js +169 -0
- package/src/functions/regex/reader.js +37 -0
- package/src/functions/regex/regexFunctions.js +140 -0
- package/src/functions/regex/translate.js +102 -0
- package/src/functions/regex/unicodeBlocks.js +194 -0
- package/src/functions/registry.js +140 -0
- package/src/functions/sequences.js +173 -0
- package/src/functions/serializeFunction.js +53 -0
- package/src/functions/signatures.js +105 -0
- package/src/functions/sort.js +102 -0
- package/src/functions/stringMatch.js +45 -0
- package/src/functions/strings.js +194 -0
- package/src/functions/stringsCore.js +66 -0
- package/src/functions/support.js +106 -0
- package/src/functions/textDecoding.js +117 -0
- package/src/functions/ucaCollation.js +127 -0
- package/src/functions/unparsedText.js +71 -0
- package/src/functions/uri.js +166 -0
- package/src/functions/xqueryModule.js +24 -0
- package/src/index.js +38 -0
- package/src/items/array.js +84 -0
- package/src/items/function.js +45 -0
- package/src/items/map.js +120 -0
- package/src/items/mapKey.js +82 -0
- package/src/serialize/index.js +129 -0
- package/src/serialize/markup/content.js +74 -0
- package/src/serialize/markup/dialect.js +178 -0
- package/src/serialize/markup/htmlData.js +70 -0
- package/src/serialize/markup/namespaces.js +145 -0
- package/src/serialize/markup/prolog.js +91 -0
- package/src/serialize/markup/writer.js +183 -0
- package/src/serialize/methods/adaptive.js +131 -0
- package/src/serialize/methods/json.js +158 -0
- package/src/serialize/methods/text.js +43 -0
- package/src/serialize/output/buffer.js +48 -0
- package/src/serialize/output/encoding.js +146 -0
- package/src/serialize/output/expander.js +204 -0
- package/src/serialize/params/fromElement.js +141 -0
- package/src/serialize/params/fromMap.js +128 -0
- package/src/serialize/params/names.js +69 -0
- package/src/serialize/params/settings.js +153 -0
- package/src/serialize/params/values.js +163 -0
- package/src/serialize/sequence.js +111 -0
- package/src/xdm/arithmetic.js +155 -0
- package/src/xdm/atomic.js +142 -0
- package/src/xdm/binary.js +92 -0
- package/src/xdm/calendar.js +71 -0
- package/src/xdm/cast.js +176 -0
- package/src/xdm/compare.js +192 -0
- package/src/xdm/datetime.js +206 -0
- package/src/xdm/decimal.js +200 -0
- package/src/xdm/duration.js +128 -0
- package/src/xdm/generalCompare.js +152 -0
- package/src/xdm/index.js +47 -0
- package/src/xdm/lexical.js +126 -0
- package/src/xdm/nodes.js +170 -0
- package/src/xdm/numeric.js +122 -0
- package/src/xdm/qname.js +65 -0
- package/src/xdm/strings.js +52 -0
- package/src/xdm/temporalArithmetic.js +180 -0
- package/src/xdm/timeline.js +68 -0
- package/src/xdm/types.js +178 -0
- package/src/xpath/eval/atomics.js +45 -0
- package/src/xpath/eval/axes.js +212 -0
- package/src/xpath/eval/calls.js +168 -0
- package/src/xpath/eval/coercion.js +146 -0
- package/src/xpath/eval/compiler.js +71 -0
- package/src/xpath/eval/constructors.js +129 -0
- package/src/xpath/eval/decimalFormats.js +93 -0
- package/src/xpath/eval/descendantMemo.js +77 -0
- package/src/xpath/eval/documentOrder.js +142 -0
- package/src/xpath/eval/domNodes.js +174 -0
- package/src/xpath/eval/dynamicCall.js +27 -0
- package/src/xpath/eval/dynamicContext.js +161 -0
- package/src/xpath/eval/flow.js +115 -0
- package/src/xpath/eval/functionItems.js +194 -0
- package/src/xpath/eval/groupedChildren.js +88 -0
- package/src/xpath/eval/inline.js +71 -0
- package/src/xpath/eval/namespaceNodes.js +90 -0
- package/src/xpath/eval/nodeTests.js +164 -0
- package/src/xpath/eval/operators.js +190 -0
- package/src/xpath/eval/paths.js +163 -0
- package/src/xpath/eval/positional.js +81 -0
- package/src/xpath/eval/predicates.js +56 -0
- package/src/xpath/eval/primary.js +99 -0
- package/src/xpath/eval/resources.js +149 -0
- package/src/xpath/eval/scope.js +108 -0
- package/src/xpath/eval/sequenceType.js +193 -0
- package/src/xpath/eval/staticContext.js +142 -0
- package/src/xpath/eval/steps.js +98 -0
- package/src/xpath/eval/subtype.js +154 -0
- package/src/xpath/eval/typeExprs.js +147 -0
- package/src/xpath/eval/typeNames.js +101 -0
- package/src/xpath/eval/uris.js +21 -0
- package/src/xpath/eval/values.js +64 -0
- package/src/xpath/index.js +217 -0
- package/src/xpath/syntax/ast.js +130 -0
- package/src/xpath/syntax/chars.js +58 -0
- package/src/xpath/syntax/constructors.js +144 -0
- package/src/xpath/syntax/expressions.js +107 -0
- package/src/xpath/syntax/index.js +44 -0
- package/src/xpath/syntax/kindTests.js +134 -0
- package/src/xpath/syntax/lexer.js +220 -0
- package/src/xpath/syntax/operators.js +111 -0
- package/src/xpath/syntax/paths.js +102 -0
- package/src/xpath/syntax/postfix.js +91 -0
- package/src/xpath/syntax/primaries.js +149 -0
- package/src/xpath/syntax/steps.js +146 -0
- package/src/xpath/syntax/syntaxError.js +31 -0
- package/src/xpath/syntax/tokenStream.js +163 -0
- package/src/xpath/syntax/typeAst.js +76 -0
- package/src/xpath/syntax/typeOperators.js +127 -0
- package/src/xpath/syntax/types.js +158 -0
- package/src/xslt/api.js +153 -0
- package/src/xslt/compiler/acceptance.js +178 -0
- package/src/xslt/compiler/accumulatorDecl.js +146 -0
- package/src/xslt/compiler/attributeValues.js +174 -0
- package/src/xslt/compiler/attributes.js +211 -0
- package/src/xslt/compiler/avt.js +103 -0
- package/src/xslt/compiler/body.js +154 -0
- package/src/xslt/compiler/children.js +105 -0
- package/src/xslt/compiler/compatibility.js +130 -0
- package/src/xslt/compiler/components.js +179 -0
- package/src/xslt/compiler/contextItem.js +95 -0
- package/src/xslt/compiler/decimalFormats.js +132 -0
- package/src/xslt/compiler/declarationKinds.js +82 -0
- package/src/xslt/compiler/elementInfo.js +246 -0
- package/src/xslt/compiler/expressions.js +175 -0
- package/src/xslt/compiler/globalContextItem.js +92 -0
- package/src/xslt/compiler/globals.js +209 -0
- package/src/xslt/compiler/linker.js +134 -0
- package/src/xslt/compiler/miscDecl.js +171 -0
- package/src/xslt/compiler/modeDecl.js +88 -0
- package/src/xslt/compiler/moduleLoading.js +111 -0
- package/src/xslt/compiler/modules.js +158 -0
- package/src/xslt/compiler/outputDecl.js +209 -0
- package/src/xslt/compiler/overrideLinks.js +141 -0
- package/src/xslt/compiler/packageChecks.js +84 -0
- package/src/xslt/compiler/packageRegistry.js +95 -0
- package/src/xslt/compiler/packageTable.js +181 -0
- package/src/xslt/compiler/packageVersions.js +143 -0
- package/src/xslt/compiler/packages.js +138 -0
- package/src/xslt/compiler/registry.js +234 -0
- package/src/xslt/compiler/staticStage.js +197 -0
- package/src/xslt/compiler/staticVariables.js +93 -0
- package/src/xslt/compiler/stylesheet.js +205 -0
- package/src/xslt/compiler/tailPosition.js +74 -0
- package/src/xslt/compiler/templatesDecl.js +180 -0
- package/src/xslt/compiler/useAccumulators.js +76 -0
- package/src/xslt/compiler/useWhen.js +94 -0
- package/src/xslt/compiler/visibility.js +160 -0
- package/src/xslt/index.js +11 -0
- package/src/xslt/instructions/analyzeString.js +137 -0
- package/src/xslt/instructions/assert.js +72 -0
- package/src/xslt/instructions/attributeSets.js +115 -0
- package/src/xslt/instructions/conditional.js +99 -0
- package/src/xslt/instructions/control.js +123 -0
- package/src/xslt/instructions/copy.js +151 -0
- package/src/xslt/instructions/elements.js +161 -0
- package/src/xslt/instructions/evaluate.js +189 -0
- package/src/xslt/instructions/fork.js +35 -0
- package/src/xslt/instructions/grouping.js +106 -0
- package/src/xslt/instructions/index.js +94 -0
- package/src/xslt/instructions/iterate.js +182 -0
- package/src/xslt/instructions/literal.js +142 -0
- package/src/xslt/instructions/maps.js +72 -0
- package/src/xslt/instructions/merge.js +190 -0
- package/src/xslt/instructions/number.js +205 -0
- package/src/xslt/instructions/output.js +102 -0
- package/src/xslt/instructions/params.js +110 -0
- package/src/xslt/instructions/resultDocument.js +143 -0
- package/src/xslt/instructions/sort.js +114 -0
- package/src/xslt/instructions/sourceDocument.js +73 -0
- package/src/xslt/instructions/templates.js +153 -0
- package/src/xslt/instructions/text.js +151 -0
- package/src/xslt/instructions/try.js +186 -0
- package/src/xslt/instructions/variables.js +119 -0
- package/src/xslt/names.js +204 -0
- package/src/xslt/patterns/axisOrigins.js +121 -0
- package/src/xslt/patterns/compile.js +151 -0
- package/src/xslt/patterns/general.js +132 -0
- package/src/xslt/patterns/grammar.js +190 -0
- package/src/xslt/patterns/modes.js +153 -0
- package/src/xslt/patterns/paths.js +130 -0
- package/src/xslt/patterns/patternChecks.js +29 -0
- package/src/xslt/patterns/steps.js +162 -0
- package/src/xslt/processor.js +178 -0
- package/src/xslt/runtime/accumulatorFunctions.js +48 -0
- package/src/xslt/runtime/accumulators.js +174 -0
- package/src/xslt/runtime/apply.js +153 -0
- package/src/xslt/runtime/atomicKeys.js +36 -0
- package/src/xslt/runtime/availability.js +162 -0
- package/src/xslt/runtime/baseUri.js +64 -0
- package/src/xslt/runtime/compilerHook.js +27 -0
- package/src/xslt/runtime/conditional.js +110 -0
- package/src/xslt/runtime/context.js +106 -0
- package/src/xslt/runtime/copy.js +123 -0
- package/src/xslt/runtime/copyFunctions.js +71 -0
- package/src/xslt/runtime/copyOrigins.js +60 -0
- package/src/xslt/runtime/defaultMethod.js +42 -0
- package/src/xslt/runtime/documentContent.js +93 -0
- package/src/xslt/runtime/dynamicXPath.js +90 -0
- package/src/xslt/runtime/functionCache.js +65 -0
- package/src/xslt/runtime/functionCall.js +57 -0
- package/src/xslt/runtime/functions.js +191 -0
- package/src/xslt/runtime/groups.js +108 -0
- package/src/xslt/runtime/invocation.js +156 -0
- package/src/xslt/runtime/iteration.js +107 -0
- package/src/xslt/runtime/keys.js +141 -0
- package/src/xslt/runtime/lazy.js +97 -0
- package/src/xslt/runtime/locations.js +86 -0
- package/src/xslt/runtime/machine.js +137 -0
- package/src/xslt/runtime/merging.js +167 -0
- package/src/xslt/runtime/namespaceScope.js +141 -0
- package/src/xslt/runtime/numberFormat.js +89 -0
- package/src/xslt/runtime/numbering.js +168 -0
- package/src/xslt/runtime/params.js +59 -0
- package/src/xslt/runtime/properties.js +103 -0
- package/src/xslt/runtime/rawResults.js +82 -0
- package/src/xslt/runtime/sequenceReceiver.js +102 -0
- package/src/xslt/runtime/snapshot.js +66 -0
- package/src/xslt/runtime/sortKeys.js +119 -0
- package/src/xslt/runtime/streamAvailable.js +60 -0
- package/src/xslt/runtime/strip.js +102 -0
- package/src/xslt/runtime/transformFunction.js +169 -0
- package/src/xslt/runtime/transformation.js +168 -0
- package/src/xslt/runtime/transformationContext.js +85 -0
- package/src/xslt/runtime/treeReceiver.js +194 -0
- package/src/xslt/runtime/values.js +92 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Document order of nodes (XDM 3.1 section 2.4), for any DOM.
|
|
3
|
+
*
|
|
4
|
+
* The nodes of a tree are numbered once, in one firstChild/nextSibling
|
|
5
|
+
* walk, the first time a node of that tree is compared. Sorting then
|
|
6
|
+
* compares numbers, which is fast whatever the DOM; walking ancestors per
|
|
7
|
+
* comparison (compareDocumentPosition in jsdom or xmldom) is not. Numbers
|
|
8
|
+
* grow across trees, so trees are ordered among themselves by when they
|
|
9
|
+
* were (last) numbered.
|
|
10
|
+
*
|
|
11
|
+
* Attributes and namespace nodes are not numbered: they sort after their
|
|
12
|
+
* element and before its first child (element p, its namespace nodes in
|
|
13
|
+
* (p, p + 0.5), its attributes at p + 0.75). Two attributes of the same
|
|
14
|
+
* element are ordered by their index in its attribute list, looked up only
|
|
15
|
+
* for such a tie: walking every attribute list up front would dominate
|
|
16
|
+
* sorting `//item/@price` (and NamedNodeMap is slow in jsdom).
|
|
17
|
+
*
|
|
18
|
+
* The numbering assumes the trees do not change while an instance is in
|
|
19
|
+
* use. One instance serves one evaluation (so a tree changed between two
|
|
20
|
+
* evaluations is numbered again) unless the caller shares one (dynamic
|
|
21
|
+
* option `documentOrder`) over trees it does not change. A node missing
|
|
22
|
+
* from its tree's numbering (added later) makes the tree be numbered
|
|
23
|
+
* again; a node moved within a numbered tree keeps its stale number.
|
|
24
|
+
*
|
|
25
|
+
* @module @tradik/xslt3/xpath/eval/documentOrder
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import { NODE_TYPES, parentOf, rootOf } from "./domNodes.js";
|
|
29
|
+
import { namespaceNodesOf } from "./namespaceNodes.js";
|
|
30
|
+
|
|
31
|
+
/** Offset of the attributes of an element from the element's number. */
|
|
32
|
+
const ATTRIBUTE_OFFSET = 0.75;
|
|
33
|
+
|
|
34
|
+
/** Positions of the nodes of trees, built lazily. */
|
|
35
|
+
export class DocumentOrder {
|
|
36
|
+
constructor() {
|
|
37
|
+
/** @type {Map<Node, number>} */
|
|
38
|
+
this.positions = new Map();
|
|
39
|
+
/** @type {Map<Attr, number>} index in the attribute list */
|
|
40
|
+
this.attributeIndexes = new Map();
|
|
41
|
+
this.next = 0;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Numbers the nodes of the tree containing a node, attributes aside.
|
|
46
|
+
* @param {Node} node
|
|
47
|
+
*/
|
|
48
|
+
index(node) {
|
|
49
|
+
const root = rootOf(node);
|
|
50
|
+
const positions = this.positions;
|
|
51
|
+
positions.set(root, this.next++);
|
|
52
|
+
let current = root.firstChild;
|
|
53
|
+
while (current) {
|
|
54
|
+
positions.set(current, this.next++);
|
|
55
|
+
const first = current.firstChild;
|
|
56
|
+
if (first) {
|
|
57
|
+
current = first;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
while (!current.nextSibling) {
|
|
61
|
+
current = current.parentNode;
|
|
62
|
+
if (current === root || !current) return;
|
|
63
|
+
}
|
|
64
|
+
current = current.nextSibling;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @param {Node} node
|
|
70
|
+
* @returns {number} the position of a node: larger for later nodes
|
|
71
|
+
*/
|
|
72
|
+
position(node) {
|
|
73
|
+
const type = node.nodeType;
|
|
74
|
+
if (type === NODE_TYPES.namespace) {
|
|
75
|
+
const element = parentOf(node);
|
|
76
|
+
const siblings = namespaceNodesOf(element);
|
|
77
|
+
const rank = siblings.indexOf(node) + 1;
|
|
78
|
+
return this.position(element) + rank / (2 * (siblings.length + 1));
|
|
79
|
+
}
|
|
80
|
+
if (type === NODE_TYPES.attribute && node.ownerElement) {
|
|
81
|
+
return this.position(node.ownerElement) + ATTRIBUTE_OFFSET;
|
|
82
|
+
}
|
|
83
|
+
let position = this.positions.get(node);
|
|
84
|
+
if (position === undefined) {
|
|
85
|
+
this.index(node);
|
|
86
|
+
position = this.positions.get(node);
|
|
87
|
+
}
|
|
88
|
+
return position;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* @param {Attr} attribute - An attribute with an owner element
|
|
93
|
+
* @returns {number} its index in the attribute list of its element
|
|
94
|
+
*/
|
|
95
|
+
attributeIndex(attribute) {
|
|
96
|
+
let index = this.attributeIndexes.get(attribute);
|
|
97
|
+
if (index === undefined) {
|
|
98
|
+
const attributes = attribute.ownerElement.attributes;
|
|
99
|
+
for (let i = 0, n = attributes.length; i < n; i++) {
|
|
100
|
+
this.attributeIndexes.set(attributes[i], i);
|
|
101
|
+
}
|
|
102
|
+
index = this.attributeIndexes.get(attribute) ?? 0;
|
|
103
|
+
}
|
|
104
|
+
return index;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Order of two nodes at the same position: the attributes of one
|
|
109
|
+
* element (or the same node twice).
|
|
110
|
+
* @returns {number}
|
|
111
|
+
*/
|
|
112
|
+
tie(a, b) {
|
|
113
|
+
return a === b ? 0 : this.attributeIndex(a) - this.attributeIndex(b);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* @param {Node} a
|
|
118
|
+
* @param {Node} b
|
|
119
|
+
* @returns {number} negative when a comes first, positive when b does,
|
|
120
|
+
* 0 for the same node
|
|
121
|
+
*/
|
|
122
|
+
compare(a, b) {
|
|
123
|
+
if (a === b) return 0;
|
|
124
|
+
return this.position(a) - this.position(b) || this.tie(a, b);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* @param {Node[]} nodes
|
|
129
|
+
* @returns {Node[]} the nodes in document order without duplicates (a
|
|
130
|
+
* new array)
|
|
131
|
+
*/
|
|
132
|
+
sort(nodes) {
|
|
133
|
+
if (nodes.length < 2) return nodes.slice();
|
|
134
|
+
const keyed = nodes.map((node) => ({ node, key: this.position(node) }));
|
|
135
|
+
keyed.sort((x, y) => x.key - y.key || this.tie(x.node, y.node));
|
|
136
|
+
const result = [keyed[0].node];
|
|
137
|
+
for (let i = 1; i < keyed.length; i++) {
|
|
138
|
+
if (keyed[i].node !== keyed[i - 1].node) result.push(keyed[i].node);
|
|
139
|
+
}
|
|
140
|
+
return result;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The XDM view of DOM nodes, for any DOM implementation (browsers, jsdom,
|
|
3
|
+
* @xmldom/xmldom): node kinds, names, parents, children and attributes.
|
|
4
|
+
*
|
|
5
|
+
* - Document fragments are document nodes; CDATA sections are text nodes
|
|
6
|
+
* (adjacent text nodes are not merged); document type nodes are skipped.
|
|
7
|
+
* - Namespace declarations (`xmlns` attributes) are namespace nodes, never
|
|
8
|
+
* attributes.
|
|
9
|
+
* - Namespace nodes are the objects of namespaceNodes.js (nodeType 13).
|
|
10
|
+
*
|
|
11
|
+
* @module @tradik/xslt3/xpath/eval/domNodes
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** DOM nodeType values. */
|
|
15
|
+
export const NODE_TYPES = Object.freeze({
|
|
16
|
+
element: 1,
|
|
17
|
+
attribute: 2,
|
|
18
|
+
text: 3,
|
|
19
|
+
cdata: 4,
|
|
20
|
+
processingInstruction: 7,
|
|
21
|
+
comment: 8,
|
|
22
|
+
document: 9,
|
|
23
|
+
documentType: 10,
|
|
24
|
+
fragment: 11,
|
|
25
|
+
namespace: 13,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
/** Namespace of `xmlns` attributes. */
|
|
29
|
+
export const XMLNS_NAMESPACE = "http://www.w3.org/2000/xmlns/";
|
|
30
|
+
|
|
31
|
+
/** XDM node kinds by DOM nodeType. */
|
|
32
|
+
const KINDS = {
|
|
33
|
+
1: "element",
|
|
34
|
+
2: "attribute",
|
|
35
|
+
3: "text",
|
|
36
|
+
4: "text",
|
|
37
|
+
7: "processing-instruction",
|
|
38
|
+
8: "comment",
|
|
39
|
+
9: "document",
|
|
40
|
+
11: "document",
|
|
41
|
+
13: "namespace",
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/** @param {Node} node @returns {string|undefined} the XDM node kind */
|
|
45
|
+
export const nodeKind = (node) => KINDS[node.nodeType];
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @param {Node} attribute
|
|
49
|
+
* @returns {boolean} whether an attribute is a namespace declaration
|
|
50
|
+
*/
|
|
51
|
+
export function isNamespaceDeclaration(attribute) {
|
|
52
|
+
if (attribute.namespaceURI === XMLNS_NAMESPACE) return true;
|
|
53
|
+
const name = attribute.name ?? attribute.nodeName;
|
|
54
|
+
return (
|
|
55
|
+
!attribute.namespaceURI && (name === "xmlns" || name.startsWith("xmlns:"))
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @param {Node} node
|
|
61
|
+
* @returns {Node|null} the parent in XDM terms (the element of an
|
|
62
|
+
* attribute or namespace node)
|
|
63
|
+
*/
|
|
64
|
+
export function parentOf(node) {
|
|
65
|
+
const type = node.nodeType;
|
|
66
|
+
if (type === 2 || type === 13) return node.ownerElement ?? null;
|
|
67
|
+
return node.parentNode ?? null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @param {Node} node
|
|
72
|
+
* @returns {boolean} whether the node is an XDM node (not a document type)
|
|
73
|
+
*/
|
|
74
|
+
export const isXdmNode = (node) => KINDS[node.nodeType] !== undefined;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* @param {Node} node
|
|
78
|
+
* @returns {boolean} whether the node can have children (element,
|
|
79
|
+
* document, document fragment)
|
|
80
|
+
*/
|
|
81
|
+
export const isContainer = (node) => {
|
|
82
|
+
const type = node.nodeType;
|
|
83
|
+
return type === 1 || type === 9 || type === 11;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* @param {Node} node
|
|
88
|
+
* @returns {Node[]} the children in XDM terms
|
|
89
|
+
*/
|
|
90
|
+
export function childrenOf(node) {
|
|
91
|
+
if (!isContainer(node)) return [];
|
|
92
|
+
const result = [];
|
|
93
|
+
// firstChild/nextSibling: childNodes is a live list behind a Proxy in
|
|
94
|
+
// jsdom, several times slower to iterate
|
|
95
|
+
for (let child = node.firstChild; child; child = child.nextSibling) {
|
|
96
|
+
if (isXdmNode(child)) result.push(child);
|
|
97
|
+
}
|
|
98
|
+
return result;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* @param {Node} node
|
|
103
|
+
* @returns {Attr[]} the attributes of an element, without namespace
|
|
104
|
+
* declarations; none for other nodes
|
|
105
|
+
*/
|
|
106
|
+
export function attributesOf(node) {
|
|
107
|
+
if (node.nodeType !== 1) return [];
|
|
108
|
+
const attributes = node.attributes;
|
|
109
|
+
const result = [];
|
|
110
|
+
for (let i = 0, n = attributes.length; i < n; i++) {
|
|
111
|
+
const attribute = attributes[i];
|
|
112
|
+
if (!isNamespaceDeclaration(attribute)) result.push(attribute);
|
|
113
|
+
}
|
|
114
|
+
return result;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* The attribute of an element with a name, without walking its attribute
|
|
119
|
+
* list (slow in jsdom).
|
|
120
|
+
* @param {Node} node
|
|
121
|
+
* @param {string} uri - Namespace URI, "" for none
|
|
122
|
+
* @param {string} local - Local name
|
|
123
|
+
* @returns {Attr|null} the attribute, null when the element has none of
|
|
124
|
+
* that name (or the node is not an element); undefined when the DOM
|
|
125
|
+
* cannot look attributes up by name
|
|
126
|
+
*/
|
|
127
|
+
export function namedAttributeOf(node, uri, local) {
|
|
128
|
+
if (node.nodeType !== 1) return null;
|
|
129
|
+
if (typeof node.getAttributeNodeNS !== "function") return undefined;
|
|
130
|
+
const attribute = node.getAttributeNodeNS(uri === "" ? null : uri, local);
|
|
131
|
+
return attribute && !isNamespaceDeclaration(attribute) ? attribute : null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** @param {Node} node @returns {string} namespace URI of the name, "" for none */
|
|
135
|
+
export const nodeNamespace = (node) => node.namespaceURI ?? "";
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* @param {Node} node
|
|
139
|
+
* @returns {string} local part of the node name: the target of a
|
|
140
|
+
* processing instruction, the prefix of a namespace node, "" for unnamed
|
|
141
|
+
* nodes
|
|
142
|
+
*/
|
|
143
|
+
export function nodeLocalName(node) {
|
|
144
|
+
switch (node.nodeType) {
|
|
145
|
+
case 1:
|
|
146
|
+
case 2:
|
|
147
|
+
return node.localName ?? node.nodeName;
|
|
148
|
+
case 7:
|
|
149
|
+
return node.target ?? node.nodeName;
|
|
150
|
+
case 13:
|
|
151
|
+
return node.localName;
|
|
152
|
+
default:
|
|
153
|
+
return "";
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* @param {Node} node
|
|
159
|
+
* @returns {string} prefix of an element or attribute name, "" for none
|
|
160
|
+
*/
|
|
161
|
+
export const nodePrefix = (node) =>
|
|
162
|
+
node.nodeType === 1 || node.nodeType === 2 ? (node.prefix ?? "") : "";
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* @param {Node} node
|
|
166
|
+
* @returns {Node} the root of the tree containing the node
|
|
167
|
+
*/
|
|
168
|
+
export function rootOf(node) {
|
|
169
|
+
let current = node;
|
|
170
|
+
for (let parent = parentOf(current); parent; parent = parentOf(current)) {
|
|
171
|
+
current = parent;
|
|
172
|
+
}
|
|
173
|
+
return current;
|
|
174
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The dynamic context of dynamic function calls and inline function
|
|
3
|
+
* bodies, which XSLT 3.0 distinguishes (fn:current-output-uri is absent
|
|
4
|
+
* there).
|
|
5
|
+
*
|
|
6
|
+
* @module @tradik/xslt3/xpath/eval/dynamicCall
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** @type {WeakMap<object, object>} */
|
|
10
|
+
const dynamicCallContexts = new WeakMap();
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The dynamic context of a dynamic function call or an inline function
|
|
14
|
+
* body: the caller's, marked `dynamicCall` (XSLT 3.0 clears the current
|
|
15
|
+
* output URI there, see fn:current-output-uri).
|
|
16
|
+
* @param {object} dyn - Dynamic context the function item was made in
|
|
17
|
+
* @returns {object} a context inheriting from it, one per context
|
|
18
|
+
*/
|
|
19
|
+
export function dynamicCallContext(dyn) {
|
|
20
|
+
let derived = dynamicCallContexts.get(dyn);
|
|
21
|
+
if (!derived) {
|
|
22
|
+
derived = Object.create(dyn);
|
|
23
|
+
derived.dynamicCall = true;
|
|
24
|
+
dynamicCallContexts.set(dyn, derived);
|
|
25
|
+
}
|
|
26
|
+
return derived;
|
|
27
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The dynamic context of one evaluation (XPath 3.1 section 2.1.2), shared
|
|
3
|
+
* by every evaluator of the expression and passed to function
|
|
4
|
+
* implementations: implicit timezone, current date and time (stable during
|
|
5
|
+
* the evaluation), available documents, document order, collation.
|
|
6
|
+
*
|
|
7
|
+
* @module @tradik/xslt3/xpath/eval/dynamicContext
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { XPathError } from "../../errors.js";
|
|
11
|
+
import { DateTimeValue } from "../../xdm/datetime.js";
|
|
12
|
+
import { Decimal } from "../../xdm/decimal.js";
|
|
13
|
+
import { DocumentOrder } from "./documentOrder.js";
|
|
14
|
+
import { deepEqualItem } from "../../functions/mapArrayEqual.js";
|
|
15
|
+
import { compareCodepoints } from "../../xdm/strings.js";
|
|
16
|
+
import { functionItemOf, resolveFunction } from "./functionItems.js";
|
|
17
|
+
import {
|
|
18
|
+
createCollections,
|
|
19
|
+
createTextLoader,
|
|
20
|
+
createXmlParser,
|
|
21
|
+
} from "./resources.js";
|
|
22
|
+
import { resolveUri } from "./uris.js";
|
|
23
|
+
|
|
24
|
+
const XSL_NAMESPACE = "http://www.w3.org/1999/XSL/Transform";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The date and time of a JS Date in a timezone.
|
|
28
|
+
* @param {Date} date
|
|
29
|
+
* @param {number} timezone - Offset in minutes
|
|
30
|
+
* @returns {DateTimeValue}
|
|
31
|
+
*/
|
|
32
|
+
export function dateTimeOf(date, timezone) {
|
|
33
|
+
const shifted = new Date(date.getTime() + timezone * 60000);
|
|
34
|
+
const millis = shifted.getUTCMilliseconds();
|
|
35
|
+
return new DateTimeValue({
|
|
36
|
+
year: shifted.getUTCFullYear(),
|
|
37
|
+
month: shifted.getUTCMonth() + 1,
|
|
38
|
+
day: shifted.getUTCDate(),
|
|
39
|
+
hour: shifted.getUTCHours(),
|
|
40
|
+
minute: shifted.getUTCMinutes(),
|
|
41
|
+
second: Decimal.of(BigInt(shifted.getUTCSeconds() * 1000 + millis), 3),
|
|
42
|
+
timezone,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* A document loader with the stability of fn:doc (the same URI gives the
|
|
48
|
+
* same document during an evaluation). Raises FODC0005 for an invalid URI
|
|
49
|
+
* (a colon not preceded by a valid scheme) and FODC0002 when the loader
|
|
50
|
+
* fails or finds nothing.
|
|
51
|
+
* @param {((uri: string, base?: string) => Node)|undefined} loader - Gets
|
|
52
|
+
* the absolute URI and the base URI it was resolved against
|
|
53
|
+
* @param {string|undefined} baseUri
|
|
54
|
+
* @returns {(uri: string, base?: string) => Node}
|
|
55
|
+
*/
|
|
56
|
+
function documentLoader(loader, baseUri) {
|
|
57
|
+
const documents = new Map();
|
|
58
|
+
return (uri, base = baseUri) => {
|
|
59
|
+
const scheme = /^([^/?#]*):/.exec(uri);
|
|
60
|
+
if (scheme && !/^[A-Za-z][A-Za-z0-9+.-]*$/.test(scheme[1])) {
|
|
61
|
+
throw new XPathError("FODC0005", `Invalid URI ${uri}`);
|
|
62
|
+
}
|
|
63
|
+
const absolute = resolveUri(uri, base);
|
|
64
|
+
if (documents.has(absolute)) return documents.get(absolute);
|
|
65
|
+
let document;
|
|
66
|
+
try {
|
|
67
|
+
document = loader?.(absolute, base);
|
|
68
|
+
} catch (error) {
|
|
69
|
+
throw new XPathError("FODC0002", `Cannot load ${absolute}`, {
|
|
70
|
+
cause: error,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
if (!document) {
|
|
74
|
+
throw new XPathError("FODC0002", `No document at ${absolute}`);
|
|
75
|
+
}
|
|
76
|
+
documents.set(absolute, document);
|
|
77
|
+
return document;
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* A factory of empty documents for functions that build nodes (such as
|
|
83
|
+
* fn:analyze-string): the DOM implementation of the context node, else of
|
|
84
|
+
* the global document.
|
|
85
|
+
* @param {*} contextNode
|
|
86
|
+
* @returns {() => Document}
|
|
87
|
+
*/
|
|
88
|
+
function documentFactory(contextNode) {
|
|
89
|
+
return () => {
|
|
90
|
+
const owner = contextNode?.ownerDocument ?? contextNode;
|
|
91
|
+
const implementation =
|
|
92
|
+
owner?.implementation ?? globalThis.document?.implementation;
|
|
93
|
+
if (!implementation) {
|
|
94
|
+
throw new XPathError(
|
|
95
|
+
"XPDY0130",
|
|
96
|
+
"No DOM implementation to build nodes with: pass createDocument",
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
return implementation.createDocument(null, null, null);
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Creates the dynamic context of an evaluation.
|
|
105
|
+
* @param {import("./staticContext.js").StaticContext} sc
|
|
106
|
+
* @param {object} options - Dynamic options (see xpath/index.js)
|
|
107
|
+
* @param {*} [contextNode] - Context item, whose DOM builds new nodes when
|
|
108
|
+
* `options.createDocument` is absent
|
|
109
|
+
* @returns {object} the dynamic context
|
|
110
|
+
*/
|
|
111
|
+
export function createDynamicContext(sc, options, contextNode) {
|
|
112
|
+
const implicitTimezone = options.implicitTimezone ?? 0;
|
|
113
|
+
const now = options.currentDateTime;
|
|
114
|
+
const currentDateTime =
|
|
115
|
+
now instanceof DateTimeValue
|
|
116
|
+
? now
|
|
117
|
+
: dateTimeOf(now ?? new Date(), implicitTimezone);
|
|
118
|
+
const compareOptions = { implicitTimezone };
|
|
119
|
+
return {
|
|
120
|
+
implicitTimezone,
|
|
121
|
+
currentDateTime,
|
|
122
|
+
defaultCollation: sc.defaultCollation,
|
|
123
|
+
staticBaseUri: sc.baseUri,
|
|
124
|
+
defaultLanguage: "en",
|
|
125
|
+
contextItem: undefined,
|
|
126
|
+
position: 0,
|
|
127
|
+
size: 0,
|
|
128
|
+
loadDocument: documentLoader(options.documentLoader, sc.baseUri),
|
|
129
|
+
loadText: createTextLoader(options.textLoader, sc.baseUri),
|
|
130
|
+
parseXml: createXmlParser(options.xmlParser),
|
|
131
|
+
collection: createCollections(options.collections, sc.baseUri),
|
|
132
|
+
trace: options.trace ?? (() => {}),
|
|
133
|
+
decimalFormats: sc.decimalFormats,
|
|
134
|
+
createDocument: options.createDocument ?? documentFactory(contextNode),
|
|
135
|
+
deepEqualItem: function hook(a, b, itemOptions) {
|
|
136
|
+
return deepEqualItem(a, b, {
|
|
137
|
+
...compareOptions,
|
|
138
|
+
collation: compareCodepoints,
|
|
139
|
+
deepEqualItem: hook,
|
|
140
|
+
...itemOptions,
|
|
141
|
+
});
|
|
142
|
+
},
|
|
143
|
+
order: options.documentOrder ?? new DocumentOrder(),
|
|
144
|
+
// Set by the owner of an evaluation (see descendantMemo.js)
|
|
145
|
+
descendantMemo: null,
|
|
146
|
+
compareOptions,
|
|
147
|
+
compatibleCompareOptions: { ...compareOptions, backwardsCompatible: true },
|
|
148
|
+
lookupFunction(uri, local, arity, ctx) {
|
|
149
|
+
// xsl:original is only reachable by a static call (XSLT 3.0 3.5.3)
|
|
150
|
+
if (uri === XSL_NAMESPACE) return null;
|
|
151
|
+
try {
|
|
152
|
+
// the static context of the calling expression, when one is set
|
|
153
|
+
const callSc = this.sc ?? sc;
|
|
154
|
+
return functionItemOf(resolveFunction(uri, local, arity, callSc), ctx);
|
|
155
|
+
} catch (error) {
|
|
156
|
+
if (error.code === "XPST0017") return null;
|
|
157
|
+
throw error;
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compilers of the control-flow expressions: for, let, some/every, if and
|
|
3
|
+
* the simple map operator `!` (XPath 3.1 sections 3.12 to 3.14, 3.17).
|
|
4
|
+
*
|
|
5
|
+
* @module @tradik/xslt3/xpath/eval/flow
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { effectiveBooleanValue } from "../../xdm/nodes.js";
|
|
9
|
+
import { booleanItem } from "./atomics.js";
|
|
10
|
+
import { bindVariable, withFocus, withVariable } from "./scope.js";
|
|
11
|
+
import { clark, namespaceOf } from "./staticContext.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {import("../syntax/ast.js").Binding} binding
|
|
15
|
+
* @param {import("./scope.js").Scope} scope
|
|
16
|
+
* @returns {string} Clark name of the bound variable
|
|
17
|
+
*/
|
|
18
|
+
const bindingKey = (binding, scope) =>
|
|
19
|
+
clark(namespaceOf(binding.name, scope.sc, ""), binding.name.local);
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Compiles nested bindings, innermost last: `wrap` builds the evaluator of
|
|
23
|
+
* one binding from its input and the evaluator of what it encloses.
|
|
24
|
+
* @param {import("../syntax/ast.js").Binding[]} bindings
|
|
25
|
+
* @param {import("./scope.js").Scope} scope
|
|
26
|
+
* @param {Function} compile
|
|
27
|
+
* @param {(scope: object) => Function} body - Compiles the innermost part
|
|
28
|
+
* @param {(input: Function, inner: Function) => Function} wrap
|
|
29
|
+
* @returns {import("./scope.js").Evaluator}
|
|
30
|
+
*/
|
|
31
|
+
function nest(bindings, scope, compile, body, wrap) {
|
|
32
|
+
const level = (index, current) => {
|
|
33
|
+
if (index === bindings.length) return body(current);
|
|
34
|
+
const binding = bindings[index];
|
|
35
|
+
const input = compile(binding.expr, current);
|
|
36
|
+
const inner = level(
|
|
37
|
+
index + 1,
|
|
38
|
+
bindVariable(current, bindingKey(binding, current)),
|
|
39
|
+
);
|
|
40
|
+
return wrap(input, inner);
|
|
41
|
+
};
|
|
42
|
+
return level(0, scope);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Compilers by node type. */
|
|
46
|
+
export const flowCompilers = {
|
|
47
|
+
ForExpr(node, scope, compile) {
|
|
48
|
+
return nest(
|
|
49
|
+
node.bindings,
|
|
50
|
+
scope,
|
|
51
|
+
compile,
|
|
52
|
+
(inner) => compile(node.returnExpr, inner),
|
|
53
|
+
(input, inner) => (ctx) => {
|
|
54
|
+
const result = [];
|
|
55
|
+
for (const item of input(ctx)) {
|
|
56
|
+
for (const out of inner(withVariable(ctx, [item]))) result.push(out);
|
|
57
|
+
}
|
|
58
|
+
return result;
|
|
59
|
+
},
|
|
60
|
+
);
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
LetExpr(node, scope, compile) {
|
|
64
|
+
return nest(
|
|
65
|
+
node.bindings,
|
|
66
|
+
scope,
|
|
67
|
+
compile,
|
|
68
|
+
(inner) => compile(node.returnExpr, inner),
|
|
69
|
+
(input, inner) => (ctx) => inner(withVariable(ctx, input(ctx))),
|
|
70
|
+
);
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
QuantifiedExpr(node, scope, compile) {
|
|
74
|
+
const some = node.quantifier === "some";
|
|
75
|
+
const test = nest(
|
|
76
|
+
node.bindings,
|
|
77
|
+
scope,
|
|
78
|
+
compile,
|
|
79
|
+
(inner) => {
|
|
80
|
+
const satisfies = compile(node.satisfies, inner);
|
|
81
|
+
return (ctx) => effectiveBooleanValue(satisfies(ctx));
|
|
82
|
+
},
|
|
83
|
+
(input, inner) => (ctx) => {
|
|
84
|
+
for (const item of input(ctx)) {
|
|
85
|
+
if (inner(withVariable(ctx, [item])) === some) return some;
|
|
86
|
+
}
|
|
87
|
+
return !some;
|
|
88
|
+
},
|
|
89
|
+
);
|
|
90
|
+
return (ctx) => [booleanItem(test(ctx))];
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
IfExpr(node, scope, compile) {
|
|
94
|
+
const condition = compile(node.condition, scope);
|
|
95
|
+
const thenExpr = compile(node.thenExpr, scope);
|
|
96
|
+
const elseExpr = compile(node.elseExpr, scope);
|
|
97
|
+
return (ctx) =>
|
|
98
|
+
effectiveBooleanValue(condition(ctx)) ? thenExpr(ctx) : elseExpr(ctx);
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
SimpleMapExpr(node, scope, compile) {
|
|
102
|
+
const left = compile(node.left, scope);
|
|
103
|
+
const right = compile(node.right, scope);
|
|
104
|
+
return (ctx) => {
|
|
105
|
+
const input = left(ctx);
|
|
106
|
+
const result = [];
|
|
107
|
+
const size = input.length;
|
|
108
|
+
for (let i = 0; i < size; i++) {
|
|
109
|
+
const out = right(withFocus(ctx, input[i], i + 1, size));
|
|
110
|
+
for (const item of out) result.push(item);
|
|
111
|
+
}
|
|
112
|
+
return result;
|
|
113
|
+
};
|
|
114
|
+
},
|
|
115
|
+
};
|