@jinntec/fore 2.2.0 → 2.3.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/README.md +1 -1
- package/dist/fore-dev.js +2 -2
- package/dist/fore-dev.js.map +1 -1
- package/dist/fore.js +2 -2
- package/dist/fore.js.map +1 -1
- package/index.js +3 -2
- package/package.json +10 -38
- package/resources/fore.css +263 -262
- package/resources/vars.css +8 -0
- package/src/DependencyNotifyingDomFacade.js +1 -0
- package/src/ForeElementMixin.js +243 -228
- package/src/actions/abstract-action.js +407 -398
- package/src/actions/fx-action.js +6 -6
- package/src/actions/fx-append.js +1 -1
- package/src/actions/fx-call.js +63 -62
- package/src/actions/fx-confirm.js +11 -11
- package/src/actions/fx-construct-done.js +4 -4
- package/src/actions/fx-copy.js +36 -36
- package/src/actions/fx-delete.js +77 -79
- package/src/actions/fx-dispatch.js +14 -14
- package/src/actions/fx-hide.js +15 -15
- package/src/actions/fx-insert.js +9 -12
- package/src/actions/fx-insertchild.js +88 -0
- package/src/actions/fx-load.js +188 -185
- package/src/actions/fx-message.js +18 -19
- package/src/actions/fx-refresh.js +10 -10
- package/src/actions/fx-reload.js +16 -14
- package/src/actions/fx-replace.js +0 -1
- package/src/actions/fx-reset.js +31 -31
- package/src/actions/fx-send.js +59 -52
- package/src/actions/fx-setattribute.js +48 -49
- package/src/actions/fx-setfocus.js +54 -58
- package/src/actions/fx-setvalue.js +85 -81
- package/src/actions/fx-show.js +11 -11
- package/src/actions/fx-toggle.js +2 -2
- package/src/actions/fx-toggleboolean.js +38 -39
- package/src/actions/fx-unmodified.js +27 -0
- package/src/actions/fx-update.js +6 -6
- package/src/dep_graph.js +1 -1
- package/src/drawdown.js +23 -30
- package/src/events.js +19 -19
- package/src/fore.js +143 -125
- package/src/functions/common-function.js +24 -25
- package/src/functions/fx-function.js +4 -6
- package/src/functions/fx-functionlib.js +39 -42
- package/src/functions/registerFunction.js +95 -86
- package/src/fx-bind.js +19 -24
- package/src/fx-connection.js +226 -0
- package/src/fx-fore.js +846 -815
- package/src/fx-header.js +4 -4
- package/src/fx-instance.js +23 -27
- package/src/fx-model.js +405 -383
- package/src/fx-submission.js +399 -397
- package/src/fx-var.js +6 -5
- package/src/getInScopeContext.js +102 -89
- package/src/json-util.js +24 -24
- package/src/lab/fore-component.js +48 -52
- package/src/lab/instance-inspector.js +13 -16
- package/src/modelitem.js +48 -10
- package/src/relevance.js +12 -16
- package/src/tools/adi.js +858 -812
- package/src/tools/fx-action-log.js +377 -295
- package/src/tools/fx-devtools.js +210 -210
- package/src/tools/fx-dom-inspector.js +123 -122
- package/src/tools/fx-json-instance.js +262 -253
- package/src/tools/fx-log-item.js +6 -11
- package/src/tools/fx-log-settings.js +358 -301
- package/src/tools/fx-minimap.js +186 -177
- package/src/tools/helpers.js +1 -1
- package/src/ui/abstract-control.js +74 -56
- package/src/ui/fx-case.js +59 -7
- package/src/ui/fx-container.js +13 -14
- package/src/ui/fx-control.js +572 -538
- package/src/ui/fx-dialog.js +3 -3
- package/src/ui/fx-droptarget.js +3 -4
- package/src/ui/fx-group.js +4 -2
- package/src/ui/fx-hint.js +3 -0
- package/src/ui/fx-inspector.js +5 -5
- package/src/ui/fx-items.js +11 -11
- package/src/ui/fx-output.js +10 -12
- package/src/ui/fx-repeat-attributes.js +23 -27
- package/src/ui/fx-repeat.js +347 -344
- package/src/ui/fx-repeatitem.js +75 -68
- package/src/ui/fx-switch.js +39 -14
- package/src/ui/fx-trigger.js +4 -4
- package/src/withDraggability.js +193 -191
- package/src/xpath-evaluation.js +969 -959
- package/src/xpath-util.js +161 -134
package/src/xpath-evaluation.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
2
|
+
evaluateXPath as fxEvaluateXPath,
|
|
3
|
+
evaluateXPathToBoolean as fxEvaluateXPathToBoolean,
|
|
4
|
+
evaluateXPathToFirstNode as fxEvaluateXPathToFirstNode,
|
|
5
|
+
evaluateXPathToNodes as fxEvaluateXPathToNodes,
|
|
6
|
+
evaluateXPathToNumber as fxEvaluateXPathToNumber,
|
|
7
|
+
evaluateXPathToString as fxEvaluateXPathToString,
|
|
8
|
+
evaluateXPathToStrings as fxEvaluateXPathToStrings,
|
|
9
|
+
parseScript,
|
|
10
|
+
registerCustomXPathFunction,
|
|
11
|
+
registerXQueryModule,
|
|
12
12
|
} from 'fontoxpath';
|
|
13
13
|
|
|
14
|
-
import {XPathUtil} from './xpath-util.js';
|
|
15
|
-
import {prettifyXml} from './functions/common-function.js';
|
|
14
|
+
import { XPathUtil } from './xpath-util.js';
|
|
15
|
+
import { prettifyXml } from './functions/common-function.js';
|
|
16
16
|
|
|
17
17
|
const XFORMS_NAMESPACE_URI = 'http://www.w3.org/2002/xforms';
|
|
18
18
|
|
|
@@ -23,35 +23,42 @@ const createdNamespaceResolversByXPathQueryAndNode = new Map();
|
|
|
23
23
|
export const globallyDeclaredFunctionLocalNames = [];
|
|
24
24
|
|
|
25
25
|
function getCachedNamespaceResolver(xpath, node) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
if (!createdNamespaceResolversByXPathQueryAndNode.has(xpath)) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
return createdNamespaceResolversByXPathQueryAndNode.get(xpath).get(node) || null;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
function setCachedNamespaceResolver(xpath, node, resolver) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
if (!createdNamespaceResolversByXPathQueryAndNode.has(xpath)) {
|
|
34
|
+
return createdNamespaceResolversByXPathQueryAndNode.set(xpath, new Map());
|
|
35
|
+
}
|
|
36
|
+
return createdNamespaceResolversByXPathQueryAndNode.get(xpath).set(node, resolver);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
const xhtmlNamespaceResolver = prefix => {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
if (!prefix) {
|
|
41
|
+
return 'http://www.w3.org/1999/xhtml';
|
|
42
|
+
}
|
|
43
|
+
return undefined;
|
|
44
44
|
};
|
|
45
45
|
export function isInShadow(node) {
|
|
46
|
-
|
|
46
|
+
return node.getRootNode() instanceof ShadowRoot;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
/**
|
|
50
50
|
* Resolve an id in scope. Behaves like the algorithm defined on https://www.w3.org/community/xformsusers/wiki/XForms_2.0#idref-resolve
|
|
51
|
+
*
|
|
52
|
+
* @param {string} id
|
|
53
|
+
* @param {Node} sourceObject
|
|
54
|
+
* @param {string} nodeName
|
|
55
|
+
*
|
|
56
|
+
* @returns {HTMLElement} The element with that ID, resolved with respect to repeats
|
|
51
57
|
*/
|
|
52
58
|
export function resolveId(id, sourceObject, nodeName = null) {
|
|
53
|
-
|
|
54
|
-
|
|
59
|
+
const query =
|
|
60
|
+
'outermost(ancestor-or-self::fx-fore[1]/(descendant::fx-fore|descendant::*[@id = $id]))[not(self::fx-fore)]';
|
|
61
|
+
/*
|
|
55
62
|
if (nodeName === 'fx-instance') {
|
|
56
63
|
// Instance elements can only be in the `model` element
|
|
57
64
|
// query = 'ancestor-or-self::fx-fore[1]/fx-model/fx-instance[@id = $id]';
|
|
@@ -63,119 +70,122 @@ export function resolveId(id, sourceObject, nodeName = null) {
|
|
|
63
70
|
return document.getElementById(id);
|
|
64
71
|
}
|
|
65
72
|
*/
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
73
|
+
if (sourceObject.nodeType === Node.TEXT_NODE) {
|
|
74
|
+
sourceObject = sourceObject.parentNode;
|
|
75
|
+
}
|
|
76
|
+
if (sourceObject.nodeType === Node.ATTRIBUTE_NODE) {
|
|
77
|
+
sourceObject = sourceObject.ownerElement;
|
|
78
|
+
}
|
|
79
|
+
if (sourceObject.parentNode.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
|
|
80
|
+
sourceObject = sourceObject.parentNode.host;
|
|
81
|
+
}
|
|
82
|
+
const ownerForm =
|
|
83
|
+
sourceObject.localName === 'fx-fore' ? sourceObject : sourceObject.closest('fx-fore');
|
|
84
|
+
const elementsWithId = ownerForm.querySelectorAll(`[id='${id}']`);
|
|
85
|
+
if (elementsWithId.length === 1) {
|
|
86
|
+
// A single one is found. Assume no ID reuse.
|
|
87
|
+
const targetObject = elementsWithId[0];
|
|
88
|
+
if (nodeName && targetObject.localName !== nodeName) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
return targetObject;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const allMatchingTargetObjects = fxEvaluateXPathToNodes(
|
|
95
|
+
query,
|
|
96
|
+
sourceObject,
|
|
97
|
+
null,
|
|
98
|
+
{ id },
|
|
99
|
+
{ namespaceResolver: xhtmlNamespaceResolver },
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
if (allMatchingTargetObjects.length === 0) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (
|
|
107
|
+
allMatchingTargetObjects.length === 1 &&
|
|
108
|
+
fxEvaluateXPathToBoolean(
|
|
109
|
+
'(ancestor::fx-fore | ancestor::fx-repeat)[last()]/self::fx-fore',
|
|
110
|
+
allMatchingTargetObjects[0],
|
|
111
|
+
null,
|
|
112
|
+
null,
|
|
113
|
+
{ namespaceResolver: xhtmlNamespaceResolver },
|
|
114
|
+
)
|
|
115
|
+
) {
|
|
116
|
+
// If the target element is not repeated, then the search for the target object is trivial since
|
|
117
|
+
// there is only one associated with the target element that bears the matching ID. This is true
|
|
118
|
+
// regardless of whether or not the source object is repeated. However, if the target element is
|
|
119
|
+
// repeated, then additional information must be used to help select a target object from among
|
|
120
|
+
// those associated with the identified target element.
|
|
121
|
+
const targetObject = allMatchingTargetObjects[0];
|
|
122
|
+
if (nodeName && targetObject.localName !== nodeName) {
|
|
123
|
+
return null;
|
|
74
124
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
125
|
+
return targetObject;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// SPEC:
|
|
129
|
+
|
|
130
|
+
// 12.2.1 References to Elements within a repeat Element
|
|
131
|
+
|
|
132
|
+
// When the target element that is identified by the IDREF of a source object has one or more
|
|
133
|
+
// repeat elements as ancestors, then the set of ancestor repeats are partitioned into two
|
|
134
|
+
// subsets, those in common with the source element and those that are not in common. Any ancestor
|
|
135
|
+
// repeat elements of the target element not in common with the source element are descendants of
|
|
136
|
+
// the repeat elements that the source and target element have in common, if any.
|
|
137
|
+
|
|
138
|
+
// For the repeat elements that are in common, the desired target object exists in the same set of
|
|
139
|
+
// run-time objects that contains the source object. Then, for each ancestor repeat of the target
|
|
140
|
+
// element that is not in common with the source element, the current index of the repeat
|
|
141
|
+
// determines the set of run-time objects that contains the desired target object.
|
|
142
|
+
for (const ancestorRepeatItem of fxEvaluateXPathToNodes(
|
|
143
|
+
'ancestor::fx-repeatitem => reverse()',
|
|
144
|
+
sourceObject,
|
|
145
|
+
null,
|
|
146
|
+
null,
|
|
147
|
+
{ namespaceResolver: xhtmlNamespaceResolver },
|
|
148
|
+
)) {
|
|
149
|
+
const foundTargetObjects = allMatchingTargetObjects.filter(to =>
|
|
150
|
+
XPathUtil.contains(ancestorRepeatItem, to),
|
|
151
|
+
);
|
|
152
|
+
switch (foundTargetObjects.length) {
|
|
153
|
+
case 0:
|
|
154
|
+
// Nothing found: ignore
|
|
155
|
+
break;
|
|
156
|
+
case 1: {
|
|
157
|
+
// A single one is found: the target object is directly in a common repeat
|
|
158
|
+
const targetObject = foundTargetObjects[0];
|
|
80
159
|
if (nodeName && targetObject.localName !== nodeName) {
|
|
81
|
-
|
|
160
|
+
return null;
|
|
82
161
|
}
|
|
83
162
|
return targetObject;
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (allMatchingTargetObjects.length === 0) {
|
|
94
|
-
return null;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if (
|
|
98
|
-
allMatchingTargetObjects.length === 1 &&
|
|
99
|
-
fxEvaluateXPathToBoolean(
|
|
100
|
-
'(ancestor::fx-fore | ancestor::fx-repeat)[last()]/self::fx-fore',
|
|
101
|
-
allMatchingTargetObjects[0],
|
|
163
|
+
}
|
|
164
|
+
default: {
|
|
165
|
+
// Multiple target objects are found: they are in a repeat that is not common with the
|
|
166
|
+
// source object We found a target object in a common repeat! We now need to find the one
|
|
167
|
+
// that is in the repeatitem identified at the current index
|
|
168
|
+
const targetObject = foundTargetObjects.find(to =>
|
|
169
|
+
fxEvaluateXPathToNodes(
|
|
170
|
+
'every $ancestor of ancestor::fx-repeatitem satisfies $ancestor is $ancestor/../child::fx-repeatitem[../@repeat-index]',
|
|
171
|
+
to,
|
|
102
172
|
null,
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
// repeated, then additional information must be used to help select a target object from among
|
|
111
|
-
// those associated with the identified target element.
|
|
112
|
-
const targetObject = allMatchingTargetObjects[0];
|
|
173
|
+
{},
|
|
174
|
+
),
|
|
175
|
+
);
|
|
176
|
+
if (!targetObject) {
|
|
177
|
+
// Nothing valid found for whatever reason. This might be something dynamic?
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
113
180
|
if (nodeName && targetObject.localName !== nodeName) {
|
|
114
|
-
|
|
181
|
+
return null;
|
|
115
182
|
}
|
|
116
183
|
return targetObject;
|
|
184
|
+
}
|
|
117
185
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
// 12.2.1 References to Elements within a repeat Element
|
|
122
|
-
|
|
123
|
-
// When the target element that is identified by the IDREF of a source object has one or more
|
|
124
|
-
// repeat elements as ancestors, then the set of ancestor repeats are partitioned into two
|
|
125
|
-
// subsets, those in common with the source element and those that are not in common. Any ancestor
|
|
126
|
-
// repeat elements of the target element not in common with the source element are descendants of
|
|
127
|
-
// the repeat elements that the source and target element have in common, if any.
|
|
128
|
-
|
|
129
|
-
// For the repeat elements that are in common, the desired target object exists in the same set of
|
|
130
|
-
// run-time objects that contains the source object. Then, for each ancestor repeat of the target
|
|
131
|
-
// element that is not in common with the source element, the current index of the repeat
|
|
132
|
-
// determines the set of run-time objects that contains the desired target object.
|
|
133
|
-
for (const ancestorRepeatItem of fxEvaluateXPathToNodes(
|
|
134
|
-
'ancestor::fx-repeatitem => reverse()',
|
|
135
|
-
sourceObject,
|
|
136
|
-
null,
|
|
137
|
-
null,
|
|
138
|
-
{namespaceResolver: xhtmlNamespaceResolver},
|
|
139
|
-
)) {
|
|
140
|
-
const foundTargetObjects = allMatchingTargetObjects.filter(to =>
|
|
141
|
-
XPathUtil.contains(ancestorRepeatItem, to),
|
|
142
|
-
);
|
|
143
|
-
switch (foundTargetObjects.length) {
|
|
144
|
-
case 0:
|
|
145
|
-
// Nothing found: ignore
|
|
146
|
-
break;
|
|
147
|
-
case 1: {
|
|
148
|
-
// A single one is found: the target object is directly in a common repeat
|
|
149
|
-
const targetObject = foundTargetObjects[0];
|
|
150
|
-
if (nodeName && targetObject.localName !== nodeName) {
|
|
151
|
-
return null;
|
|
152
|
-
}
|
|
153
|
-
return targetObject;
|
|
154
|
-
}
|
|
155
|
-
default: {
|
|
156
|
-
// Multiple target objects are found: they are in a repeat that is not common with the source object
|
|
157
|
-
// We found a target object in a common repeat! We now need to find the one that is in the repeatitem identified at the current index
|
|
158
|
-
const targetObject = foundTargetObjects.find(to =>
|
|
159
|
-
fxEvaluateXPathToNodes(
|
|
160
|
-
'every $ancestor of ancestor::fx-repeatitem satisfies $ancestor is $ancestor/../child::fx-repeatitem[../@repeat-index]',
|
|
161
|
-
to,
|
|
162
|
-
null,
|
|
163
|
-
{},
|
|
164
|
-
),
|
|
165
|
-
);
|
|
166
|
-
if (!targetObject) {
|
|
167
|
-
// Nothing valid found for whatever reason. This might be something dynamic?
|
|
168
|
-
return null;
|
|
169
|
-
}
|
|
170
|
-
if (nodeName && targetObject.localName !== nodeName) {
|
|
171
|
-
return null;
|
|
172
|
-
}
|
|
173
|
-
return targetObject;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
// We found no target objects in common repeats. The id is unresolvable
|
|
178
|
-
return null;
|
|
186
|
+
}
|
|
187
|
+
// We found no target objects in common repeats. The id is unresolvable
|
|
188
|
+
return null;
|
|
179
189
|
}
|
|
180
190
|
|
|
181
191
|
// Make namespace resolving use the `instance` element that is related to here
|
|
@@ -184,35 +194,39 @@ const xmlDocument = new DOMParser().parseFromString('<xml />', 'text/xml');
|
|
|
184
194
|
const instanceReferencesByQuery = new Map();
|
|
185
195
|
|
|
186
196
|
function findInstanceReferences(xpathQuery) {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
+
if (!xpathQuery.includes('instance')) {
|
|
198
|
+
// No call to the instance function anyway: short-circuit and prevent AST processing
|
|
199
|
+
return [];
|
|
200
|
+
}
|
|
201
|
+
if (instanceReferencesByQuery.has(xpathQuery)) {
|
|
202
|
+
return instanceReferencesByQuery.get(xpathQuery);
|
|
203
|
+
}
|
|
204
|
+
const xpathAST = parseScript(xpathQuery, {}, xmlDocument);
|
|
205
|
+
const instanceReferences = fxEvaluateXPathToStrings(
|
|
206
|
+
`descendant::xqx:functionCallExpr
|
|
197
207
|
[xqx:functionName = "instance"]
|
|
198
208
|
/xqx:arguments
|
|
199
209
|
/xqx:stringConstantExpr
|
|
200
210
|
/xqx:value`,
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
211
|
+
xpathAST,
|
|
212
|
+
null,
|
|
213
|
+
{},
|
|
214
|
+
{
|
|
215
|
+
namespaceResolver: prefix =>
|
|
216
|
+
prefix === 'xqx' ? 'http://www.w3.org/2005/XQueryX' : undefined,
|
|
217
|
+
},
|
|
218
|
+
);
|
|
209
219
|
|
|
210
|
-
|
|
220
|
+
instanceReferencesByQuery.set(xpathQuery, instanceReferences);
|
|
211
221
|
|
|
212
|
-
|
|
222
|
+
return instanceReferences;
|
|
213
223
|
}
|
|
224
|
+
/**
|
|
225
|
+
* @typedef {function(string):string} NamespaceResolver
|
|
226
|
+
*/
|
|
214
227
|
|
|
215
228
|
/**
|
|
229
|
+
* @function
|
|
216
230
|
* Resolve a namespace. Needs a namespace prefix and the element that is most closely related to the
|
|
217
231
|
* XPath in which the namespace is being resolved. The prefix will be resolved by using the
|
|
218
232
|
* ancestry of said element.
|
|
@@ -220,157 +234,167 @@ function findInstanceReferences(xpathQuery) {
|
|
|
220
234
|
* It has two ways of doing so:
|
|
221
235
|
*
|
|
222
236
|
* - If the prefix is defined in an `xmlns:XXX="YYY"` namespace declaration, it will return 'YYY'.
|
|
223
|
-
* - If the prefix is the empty prefix and there is an `xpath-default-namespace="YYY"` attribute in
|
|
224
|
-
* - ancestry, that attribute will be used and 'YYY' will be returned
|
|
237
|
+
* - If the prefix is the empty prefix and there is an `xpath-default-namespace="YYY"` attribute in
|
|
238
|
+
* - the * ancestry, that attribute will be used and 'YYY' will be returned
|
|
225
239
|
*
|
|
226
|
-
* @param {
|
|
227
|
-
* @param {
|
|
240
|
+
* @param {string} xpathQuery
|
|
241
|
+
* @param {HTMLElement} formElement
|
|
242
|
+
* @returns {NamespaceResolver} The namespace resolver for this context
|
|
228
243
|
*/
|
|
229
244
|
function createNamespaceResolver(xpathQuery, formElement) {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
// Nothing found: let's just assume we're supposed to use the `default` instance
|
|
249
|
-
instanceReferences = ['default'];
|
|
245
|
+
const cachedResolver = getCachedNamespaceResolver(xpathQuery, formElement);
|
|
246
|
+
if (cachedResolver) {
|
|
247
|
+
return cachedResolver;
|
|
248
|
+
}
|
|
249
|
+
let instanceReferences = findInstanceReferences(xpathQuery);
|
|
250
|
+
if (instanceReferences.length === 0) {
|
|
251
|
+
// No instance functions. Look up further in the hierarchy to see if we can deduce the intended context from there
|
|
252
|
+
const ancestorComponent =
|
|
253
|
+
formElement.parentNode &&
|
|
254
|
+
formElement.parentNode.nodeType === formElement.ELEMENT_NODE &&
|
|
255
|
+
formElement.parentNode.closest('[ref]');
|
|
256
|
+
if (ancestorComponent) {
|
|
257
|
+
const resolver = createNamespaceResolver(
|
|
258
|
+
ancestorComponent.getAttribute('ref'),
|
|
259
|
+
ancestorComponent,
|
|
260
|
+
);
|
|
261
|
+
setCachedNamespaceResolver(xpathQuery, formElement, resolver);
|
|
262
|
+
return resolver;
|
|
250
263
|
}
|
|
264
|
+
// Nothing found: let's just assume we're supposed to use the `default` instance
|
|
265
|
+
instanceReferences = ['default'];
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (instanceReferences.length === 1) {
|
|
269
|
+
// console.log(`resolving ${xpathQuery} with ${instanceReferences[0]}`);
|
|
270
|
+
let instance;
|
|
271
|
+
if (instanceReferences[0] === 'default') {
|
|
272
|
+
/**
|
|
273
|
+
* @type {HTMLElement}
|
|
274
|
+
*/
|
|
275
|
+
const actualForeElement = fxEvaluateXPathToFirstNode(
|
|
276
|
+
'ancestor-or-self::fx-fore[1]',
|
|
277
|
+
formElement,
|
|
278
|
+
null,
|
|
279
|
+
null,
|
|
280
|
+
{ namespaceResolver: xhtmlNamespaceResolver },
|
|
281
|
+
);
|
|
251
282
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
null,
|
|
260
|
-
null,
|
|
261
|
-
{namespaceResolver: xhtmlNamespaceResolver},
|
|
262
|
-
);
|
|
263
|
-
|
|
264
|
-
instance = actualForeElement && actualForeElement.querySelector('fx-instance');
|
|
265
|
-
} else {
|
|
266
|
-
instance = resolveId(instanceReferences[0], formElement, 'fx-instance');
|
|
267
|
-
}
|
|
268
|
-
if (instance && instance.hasAttribute('xpath-default-namespace')) {
|
|
269
|
-
const xpathDefaultNamespace = instance.getAttribute('xpath-default-namespace');
|
|
270
|
-
/*
|
|
283
|
+
instance = actualForeElement && actualForeElement.querySelector('fx-instance');
|
|
284
|
+
} else {
|
|
285
|
+
instance = resolveId(instanceReferences[0], formElement, 'fx-instance');
|
|
286
|
+
}
|
|
287
|
+
if (instance && instance.hasAttribute('xpath-default-namespace')) {
|
|
288
|
+
const xpathDefaultNamespace = instance.getAttribute('xpath-default-namespace');
|
|
289
|
+
/*
|
|
271
290
|
console.log(
|
|
272
291
|
`Resolving the xpath ${xpathQuery} with the default namespace set to ${xpathDefaultNamespace}`,
|
|
273
292
|
);
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
setCachedNamespaceResolver(xpathQuery, formElement, resolveNamespacePrefix);
|
|
282
|
-
return resolveNamespacePrefix;
|
|
293
|
+
*/
|
|
294
|
+
/**
|
|
295
|
+
* @type {NamespaceResolver}
|
|
296
|
+
*/
|
|
297
|
+
const resolveNamespacePrefix = prefix => {
|
|
298
|
+
if (!prefix) {
|
|
299
|
+
return xpathDefaultNamespace;
|
|
283
300
|
}
|
|
301
|
+
return undefined;
|
|
302
|
+
};
|
|
303
|
+
setCachedNamespaceResolver(xpathQuery, formElement, resolveNamespacePrefix);
|
|
304
|
+
return resolveNamespacePrefix;
|
|
284
305
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
306
|
+
}
|
|
307
|
+
if (instanceReferences.length > 1) {
|
|
308
|
+
console.warn(
|
|
309
|
+
`More than one instance is used in the query "${xpathQuery}". The default namespace resolving will be used`,
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const xpathDefaultNamespace =
|
|
314
|
+
fxEvaluateXPathToString('ancestor-or-self::*/@xpath-default-namespace[last()]', formElement) ||
|
|
315
|
+
'';
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* @type {NamespaceResolver}
|
|
319
|
+
*/
|
|
320
|
+
const resolveNamespacePrefix = function resolveNamespacePrefix(prefix) {
|
|
321
|
+
if (prefix === '') {
|
|
322
|
+
return xpathDefaultNamespace;
|
|
289
323
|
}
|
|
290
324
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
325
|
+
// Note: ideally we should use Node#lookupNamespaceURI. However, the nodes we are passed are
|
|
326
|
+
// XML. The best we can do is emulate the `xmlns:xxx` namespace declarations by regarding them as
|
|
327
|
+
// attributes. Which they technically ARE NOT!
|
|
294
328
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
// attributes. Which they technically ARE NOT!
|
|
303
|
-
|
|
304
|
-
return fxEvaluateXPathToString(
|
|
305
|
-
'ancestor-or-self::*/@*[name() = "xmlns:" || $prefix][last()]',
|
|
306
|
-
formElement,
|
|
307
|
-
null,
|
|
308
|
-
{prefix},
|
|
309
|
-
);
|
|
310
|
-
};
|
|
329
|
+
return fxEvaluateXPathToString(
|
|
330
|
+
'ancestor-or-self::*/@*[name() = "xmlns:" || $prefix][last()]',
|
|
331
|
+
formElement,
|
|
332
|
+
null,
|
|
333
|
+
{ prefix },
|
|
334
|
+
);
|
|
335
|
+
};
|
|
311
336
|
|
|
312
|
-
|
|
313
|
-
|
|
337
|
+
setCachedNamespaceResolver(xpathQuery, formElement, resolveNamespacePrefix);
|
|
338
|
+
return resolveNamespacePrefix;
|
|
314
339
|
}
|
|
315
340
|
|
|
316
341
|
function createNamespaceResolverForNode(query, contextNode, formElement) {
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
342
|
+
if (((contextNode && contextNode.ownerDocument) || contextNode) === window.document) {
|
|
343
|
+
// Running a query on the HTML DOM. Don't bother resolving namespaces in any other way
|
|
344
|
+
return xhtmlNamespaceResolver;
|
|
345
|
+
}
|
|
346
|
+
return createNamespaceResolver(query, formElement);
|
|
322
347
|
}
|
|
323
348
|
|
|
324
|
-
|
|
325
349
|
/**
|
|
326
350
|
* Implementation of the functionNameResolver passed to FontoXPath to
|
|
327
351
|
* redirect function resolving for unprefixed functions to either the fn or the xf namespace
|
|
328
352
|
*/
|
|
329
353
|
// eslint-disable-next-line no-unused-vars
|
|
330
|
-
function functionNameResolver({prefix, localName}, _arity) {
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
354
|
+
function functionNameResolver({ prefix, localName }, _arity) {
|
|
355
|
+
switch (localName) {
|
|
356
|
+
// TODO: put the full XForms library functions set here
|
|
357
|
+
case 'context':
|
|
358
|
+
case 'base64encode':
|
|
359
|
+
case 'boolean-from-string':
|
|
360
|
+
case 'current':
|
|
361
|
+
case 'depends':
|
|
362
|
+
case 'event':
|
|
363
|
+
case 'fore-attr':
|
|
364
|
+
case 'index':
|
|
365
|
+
case 'instance':
|
|
366
|
+
case 'json2xml':
|
|
367
|
+
case 'xml2Json':
|
|
368
|
+
case 'log':
|
|
369
|
+
case 'parse':
|
|
370
|
+
case 'local-date':
|
|
371
|
+
case 'local-dateTime':
|
|
372
|
+
case 'logtree':
|
|
373
|
+
case 'uri':
|
|
374
|
+
case 'uri-fragment':
|
|
375
|
+
case 'uri-host':
|
|
376
|
+
case 'uri-param':
|
|
377
|
+
case 'uri-path':
|
|
378
|
+
case 'uri-relpath':
|
|
379
|
+
case 'uri-port':
|
|
380
|
+
case 'uri-query':
|
|
381
|
+
case 'uri-scheme':
|
|
382
|
+
case 'uri-scheme-specific-part':
|
|
383
|
+
return { namespaceURI: XFORMS_NAMESPACE_URI, localName };
|
|
384
|
+
default:
|
|
385
|
+
if (prefix === '' && globallyDeclaredFunctionLocalNames.includes(localName)) {
|
|
386
|
+
// The function has been declared without a prefix and is called here without a prefix.
|
|
387
|
+
// Just make this work. It is the developer-friendly way
|
|
388
|
+
return { namespaceURI: 'http://www.w3.org/2005/xquery-local-functions', localName };
|
|
389
|
+
}
|
|
390
|
+
if (prefix === 'fn' || prefix === '') {
|
|
391
|
+
return { namespaceURI: 'http://www.w3.org/2005/xpath-functions', localName };
|
|
392
|
+
}
|
|
393
|
+
if (prefix === 'local') {
|
|
394
|
+
return { namespaceURI: 'http://www.w3.org/2005/xquery-local-functions', localName };
|
|
395
|
+
}
|
|
396
|
+
return null;
|
|
397
|
+
}
|
|
374
398
|
}
|
|
375
399
|
|
|
376
400
|
/**
|
|
@@ -379,40 +403,39 @@ function functionNameResolver({prefix, localName}, _arity) {
|
|
|
379
403
|
*
|
|
380
404
|
* @param {Node} formElement The element that declares the XPath
|
|
381
405
|
*
|
|
382
|
-
* @
|
|
406
|
+
* @returns {Object} A key-value mapping of the variables
|
|
383
407
|
*/
|
|
384
408
|
function getVariablesInScope(formElement) {
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
}
|
|
409
|
+
let closestActualFormElement = formElement;
|
|
410
|
+
while (closestActualFormElement && !('inScopeVariables' in closestActualFormElement)) {
|
|
411
|
+
closestActualFormElement =
|
|
412
|
+
closestActualFormElement.nodeType === Node.ATTRIBUTE_NODE
|
|
413
|
+
? closestActualFormElement.ownerElement
|
|
414
|
+
: closestActualFormElement.parentNode;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
if (!closestActualFormElement) {
|
|
418
|
+
return {};
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const variables = {};
|
|
422
|
+
if (closestActualFormElement.inScopeVariables) {
|
|
423
|
+
for (const key of closestActualFormElement.inScopeVariables.keys()) {
|
|
424
|
+
const varElementOrValue = closestActualFormElement.inScopeVariables.get(key);
|
|
425
|
+
if (!varElementOrValue) {
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
if (varElementOrValue.nodeType) {
|
|
429
|
+
// We are a var element, set the value to the value computed there
|
|
430
|
+
variables[key] = varElementOrValue.value;
|
|
431
|
+
// variables[key] = varElementOrValue.inScopeVariables.get(key);
|
|
432
|
+
} else {
|
|
433
|
+
// We are a direct value. This is used to leak in event variables
|
|
434
|
+
variables[key] = varElementOrValue;
|
|
435
|
+
}
|
|
414
436
|
}
|
|
415
|
-
|
|
437
|
+
}
|
|
438
|
+
return variables;
|
|
416
439
|
}
|
|
417
440
|
|
|
418
441
|
/**
|
|
@@ -421,7 +444,9 @@ function getVariablesInScope(formElement) {
|
|
|
421
444
|
*
|
|
422
445
|
* @param {string} xpath The XPath to run
|
|
423
446
|
* @param {Node} contextNode The start of the XPath
|
|
424
|
-
* @param {
|
|
447
|
+
* @param {import('./ForeElementMixin.js').default} formElement The form element associated to the XPath
|
|
448
|
+
* @param {Object} variables Any variables to pass to the XPath
|
|
449
|
+
* @param {Object} options Any options to pass to the XPath
|
|
425
450
|
*/
|
|
426
451
|
/*
|
|
427
452
|
export function evaluateXPath(xpath, contextNode, formElement, variables = {}, options={}, domFacade = null) {
|
|
@@ -447,43 +472,45 @@ export function evaluateXPath(xpath, contextNode, formElement, variables = {}, o
|
|
|
447
472
|
);
|
|
448
473
|
}
|
|
449
474
|
*/
|
|
450
|
-
export function evaluateXPath(xpath, contextNode, formElement, variables = {}, options={}) {
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
const result = fxEvaluateXPath(
|
|
456
|
-
xpath,
|
|
457
|
-
contextNode,
|
|
458
|
-
null,
|
|
459
|
-
{...variablesInScope, ...variables},
|
|
460
|
-
fxEvaluateXPath.ALL_RESULTS_TYPE,
|
|
461
|
-
{
|
|
462
|
-
debug: true,
|
|
463
|
-
currentContext: {formElement, variables},
|
|
464
|
-
moduleImports: {
|
|
465
|
-
xf: XFORMS_NAMESPACE_URI,
|
|
466
|
-
},
|
|
467
|
-
functionNameResolver,
|
|
468
|
-
namespaceResolver,
|
|
469
|
-
language: options.language || evaluateXPath.XPATH_3_1
|
|
470
|
-
},
|
|
471
|
-
);
|
|
472
|
-
// console.log('evaluateXPath',xpath, result);
|
|
473
|
-
return result;
|
|
474
|
-
}catch (e){
|
|
475
|
-
formElement.dispatchEvent(new CustomEvent('error', {
|
|
476
|
-
composed: false,
|
|
477
|
-
bubbles: true,
|
|
478
|
-
detail: {
|
|
479
|
-
origin: formElement,
|
|
480
|
-
message: `Expression '${xpath}' failed`,
|
|
481
|
-
expr:xpath,
|
|
482
|
-
level:'Error'
|
|
483
|
-
},
|
|
484
|
-
}));
|
|
475
|
+
export function evaluateXPath(xpath, contextNode, formElement, variables = {}, options = {}) {
|
|
476
|
+
try {
|
|
477
|
+
const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
|
|
478
|
+
const variablesInScope = getVariablesInScope(formElement);
|
|
485
479
|
|
|
486
|
-
|
|
480
|
+
const result = fxEvaluateXPath(
|
|
481
|
+
xpath,
|
|
482
|
+
contextNode,
|
|
483
|
+
null,
|
|
484
|
+
{ ...variablesInScope, ...variables },
|
|
485
|
+
fxEvaluateXPath.ALL_RESULTS_TYPE,
|
|
486
|
+
{
|
|
487
|
+
debug: true,
|
|
488
|
+
currentContext: { formElement, variables },
|
|
489
|
+
moduleImports: {
|
|
490
|
+
xf: XFORMS_NAMESPACE_URI,
|
|
491
|
+
},
|
|
492
|
+
functionNameResolver,
|
|
493
|
+
namespaceResolver,
|
|
494
|
+
language: options.language || fxEvaluateXPath.XPATH_3_1_LANGUAGE,
|
|
495
|
+
},
|
|
496
|
+
);
|
|
497
|
+
// console.log('evaluateXPath',xpath, result);
|
|
498
|
+
return result;
|
|
499
|
+
} catch (e) {
|
|
500
|
+
formElement.dispatchEvent(
|
|
501
|
+
new CustomEvent('error', {
|
|
502
|
+
composed: false,
|
|
503
|
+
bubbles: true,
|
|
504
|
+
detail: {
|
|
505
|
+
origin: formElement,
|
|
506
|
+
message: `Expression '${xpath}' failed`,
|
|
507
|
+
expr: xpath,
|
|
508
|
+
level: 'Error',
|
|
509
|
+
},
|
|
510
|
+
}),
|
|
511
|
+
);
|
|
512
|
+
|
|
513
|
+
/*
|
|
487
514
|
formElement.dispatchEvent(
|
|
488
515
|
new CustomEvent('error', {
|
|
489
516
|
composed: false,
|
|
@@ -497,45 +524,47 @@ export function evaluateXPath(xpath, contextNode, formElement, variables = {}, o
|
|
|
497
524
|
}),
|
|
498
525
|
);
|
|
499
526
|
*/
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
527
|
+
// Return 'nothing' in hope the rest of the page can forgive this
|
|
528
|
+
return [];
|
|
529
|
+
}
|
|
503
530
|
}
|
|
504
531
|
/**
|
|
505
532
|
* Evaluate an XPath to the first Node
|
|
506
533
|
*
|
|
507
534
|
* @param {string} xpath The XPath to run
|
|
508
535
|
* @param {Node} contextNode The start of the XPath
|
|
509
|
-
* @param {
|
|
510
|
-
* @
|
|
536
|
+
* @param {import('./ForeElementMixin.js').default} formElement The form element associated to the XPath
|
|
537
|
+
* @returns {Node} The first node found in the XPath
|
|
511
538
|
*/
|
|
512
539
|
export function evaluateXPathToFirstNode(xpath, contextNode, formElement) {
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
}
|
|
538
|
-
|
|
540
|
+
try {
|
|
541
|
+
const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
|
|
542
|
+
const variablesInScope = getVariablesInScope(formElement);
|
|
543
|
+
const result = fxEvaluateXPathToFirstNode(xpath, contextNode, null, variablesInScope, {
|
|
544
|
+
defaultFunctionNamespaceURI: XFORMS_NAMESPACE_URI,
|
|
545
|
+
moduleImports: {
|
|
546
|
+
xf: XFORMS_NAMESPACE_URI,
|
|
547
|
+
},
|
|
548
|
+
currentContext: { formElement },
|
|
549
|
+
functionNameResolver,
|
|
550
|
+
namespaceResolver,
|
|
551
|
+
});
|
|
552
|
+
// console.log('evaluateXPathToFirstNode',xpath, result);
|
|
553
|
+
return result;
|
|
554
|
+
} catch (e) {
|
|
555
|
+
formElement.dispatchEvent(
|
|
556
|
+
new CustomEvent('error', {
|
|
557
|
+
composed: false,
|
|
558
|
+
bubbles: true,
|
|
559
|
+
detail: {
|
|
560
|
+
origin: formElement,
|
|
561
|
+
message: `Expression '${xpath}' failed`,
|
|
562
|
+
expr: xpath,
|
|
563
|
+
level: 'Error',
|
|
564
|
+
},
|
|
565
|
+
}),
|
|
566
|
+
);
|
|
567
|
+
}
|
|
539
568
|
}
|
|
540
569
|
|
|
541
570
|
/**
|
|
@@ -543,36 +572,38 @@ export function evaluateXPathToFirstNode(xpath, contextNode, formElement) {
|
|
|
543
572
|
*
|
|
544
573
|
* @param {string} xpath The XPath to run
|
|
545
574
|
* @param {Node} contextNode The start of the XPath
|
|
546
|
-
* @param {
|
|
575
|
+
* @param {import('./ForeElementMixin.js').default} formElement The form element associated to the XPath
|
|
547
576
|
* @return {Node[]} All nodes
|
|
548
577
|
*/
|
|
549
578
|
export function evaluateXPathToNodes(xpath, contextNode, formElement) {
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
579
|
+
try {
|
|
580
|
+
const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
|
|
581
|
+
const variablesInScope = getVariablesInScope(formElement);
|
|
553
582
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
}
|
|
575
|
-
|
|
583
|
+
const result = fxEvaluateXPathToNodes(xpath, contextNode, null, variablesInScope, {
|
|
584
|
+
currentContext: { formElement },
|
|
585
|
+
functionNameResolver,
|
|
586
|
+
moduleImports: {
|
|
587
|
+
xf: XFORMS_NAMESPACE_URI,
|
|
588
|
+
},
|
|
589
|
+
namespaceResolver,
|
|
590
|
+
});
|
|
591
|
+
// console.log('evaluateXPathToNodes',xpath, result);
|
|
592
|
+
return result;
|
|
593
|
+
} catch (e) {
|
|
594
|
+
formElement.dispatchEvent(
|
|
595
|
+
new CustomEvent('error', {
|
|
596
|
+
composed: false,
|
|
597
|
+
bubbles: true,
|
|
598
|
+
detail: {
|
|
599
|
+
origin: formElement,
|
|
600
|
+
message: `Expression '${xpath}' failed`,
|
|
601
|
+
expr: xpath,
|
|
602
|
+
level: 'Error',
|
|
603
|
+
},
|
|
604
|
+
}),
|
|
605
|
+
);
|
|
606
|
+
}
|
|
576
607
|
}
|
|
577
608
|
|
|
578
609
|
/**
|
|
@@ -580,34 +611,36 @@ export function evaluateXPathToNodes(xpath, contextNode, formElement) {
|
|
|
580
611
|
*
|
|
581
612
|
* @param {string} xpath The XPath to run
|
|
582
613
|
* @param {Node} contextNode The start of the XPath
|
|
583
|
-
* @param {
|
|
614
|
+
* @param {import('./ForeElementMixin.js').default} formElement The form element associated to the XPath
|
|
584
615
|
* @return {boolean}
|
|
585
616
|
*/
|
|
586
617
|
export function evaluateXPathToBoolean(xpath, contextNode, formElement) {
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
618
|
+
try {
|
|
619
|
+
const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
|
|
620
|
+
const variablesInScope = getVariablesInScope(formElement);
|
|
590
621
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
}
|
|
610
|
-
|
|
622
|
+
return fxEvaluateXPathToBoolean(xpath, contextNode, null, variablesInScope, {
|
|
623
|
+
currentContext: { formElement },
|
|
624
|
+
functionNameResolver,
|
|
625
|
+
moduleImports: {
|
|
626
|
+
xf: XFORMS_NAMESPACE_URI,
|
|
627
|
+
},
|
|
628
|
+
namespaceResolver,
|
|
629
|
+
});
|
|
630
|
+
} catch (e) {
|
|
631
|
+
formElement.dispatchEvent(
|
|
632
|
+
new CustomEvent('error', {
|
|
633
|
+
composed: false,
|
|
634
|
+
bubbles: true,
|
|
635
|
+
detail: {
|
|
636
|
+
origin: formElement,
|
|
637
|
+
message: `Expression '${xpath}' failed`,
|
|
638
|
+
expr: xpath,
|
|
639
|
+
level: 'Error',
|
|
640
|
+
},
|
|
641
|
+
}),
|
|
642
|
+
);
|
|
643
|
+
}
|
|
611
644
|
}
|
|
612
645
|
|
|
613
646
|
/**
|
|
@@ -616,42 +649,38 @@ export function evaluateXPathToBoolean(xpath, contextNode, formElement) {
|
|
|
616
649
|
* @param {string} xpath The XPath to run
|
|
617
650
|
* @param {Node} contextNode The start of the XPath
|
|
618
651
|
* @param {Node} formElement The form element associated to the XPath
|
|
619
|
-
* @param {DomFacade} [domFacade=null] A DomFacade is used in bindings to intercept DOM
|
|
620
|
-
* access. This is used to determine dependencies between bind elements.
|
|
621
652
|
* @param {Node} formElement The element where the XPath is defined: used for namespace resolving
|
|
653
|
+
* @param {import('fontoxpath').IDomFacade} [domFacade=null] A DomFacade is used in bindings to intercept DOM
|
|
654
|
+
* access. This is used to determine dependencies between bind elements.
|
|
622
655
|
* @return {string}
|
|
623
656
|
*/
|
|
624
|
-
export function evaluateXPathToString(
|
|
625
|
-
|
|
626
|
-
contextNode,
|
|
627
|
-
formElement
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
level:'Error'
|
|
652
|
-
},
|
|
653
|
-
}));
|
|
654
|
-
}
|
|
657
|
+
export function evaluateXPathToString(xpath, contextNode, formElement, domFacade = null) {
|
|
658
|
+
try {
|
|
659
|
+
const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
|
|
660
|
+
const variablesInScope = getVariablesInScope(formElement);
|
|
661
|
+
|
|
662
|
+
return fxEvaluateXPathToString(xpath, contextNode, domFacade, variablesInScope, {
|
|
663
|
+
currentContext: { formElement },
|
|
664
|
+
functionNameResolver,
|
|
665
|
+
moduleImports: {
|
|
666
|
+
xf: XFORMS_NAMESPACE_URI,
|
|
667
|
+
},
|
|
668
|
+
namespaceResolver,
|
|
669
|
+
});
|
|
670
|
+
} catch (e) {
|
|
671
|
+
formElement.dispatchEvent(
|
|
672
|
+
new CustomEvent('error', {
|
|
673
|
+
composed: false,
|
|
674
|
+
bubbles: true,
|
|
675
|
+
detail: {
|
|
676
|
+
origin: formElement,
|
|
677
|
+
message: `Expression '${xpath}' failed`,
|
|
678
|
+
expr: xpath,
|
|
679
|
+
level: 'Error',
|
|
680
|
+
},
|
|
681
|
+
}),
|
|
682
|
+
);
|
|
683
|
+
}
|
|
655
684
|
}
|
|
656
685
|
|
|
657
686
|
/**
|
|
@@ -660,43 +689,42 @@ export function evaluateXPathToString(
|
|
|
660
689
|
* @param {string} xpath The XPath to run
|
|
661
690
|
* @param {Node} contextNode The start of the XPath
|
|
662
691
|
* @param {Node} formElement The form element associated to the XPath
|
|
663
|
-
* @param {DomFacade} [domFacade=null] A DomFacade is used in bindings to intercept DOM
|
|
664
|
-
* access. This is used to determine dependencies between bind elements.
|
|
665
692
|
* @param {Node} formElement The element where the XPath is defined: used for namespace resolving
|
|
666
|
-
* @
|
|
693
|
+
* @param {import('fontoxpath').IDomFacade} [domFacade=null] A DomFacade is used in bindings to intercept DOM
|
|
694
|
+
* access. This is used to determine dependencies between bind elements.
|
|
695
|
+
* @return {string[]}
|
|
667
696
|
*/
|
|
668
|
-
export function evaluateXPathToStrings(
|
|
669
|
-
|
|
670
|
-
contextNode,
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
}
|
|
697
|
+
export function evaluateXPathToStrings(xpath, contextNode, formElement, domFacade = null) {
|
|
698
|
+
try {
|
|
699
|
+
const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
|
|
700
|
+
return fxEvaluateXPathToStrings(
|
|
701
|
+
xpath,
|
|
702
|
+
contextNode,
|
|
703
|
+
domFacade,
|
|
704
|
+
{},
|
|
705
|
+
{
|
|
706
|
+
currentContext: { formElement },
|
|
707
|
+
functionNameResolver,
|
|
708
|
+
moduleImports: {
|
|
709
|
+
xf: XFORMS_NAMESPACE_URI,
|
|
710
|
+
},
|
|
711
|
+
namespaceResolver,
|
|
712
|
+
},
|
|
713
|
+
);
|
|
714
|
+
} catch (e) {
|
|
715
|
+
formElement.dispatchEvent(
|
|
716
|
+
new CustomEvent('error', {
|
|
717
|
+
composed: false,
|
|
718
|
+
bubbles: true,
|
|
719
|
+
detail: {
|
|
720
|
+
origin: formElement,
|
|
721
|
+
message: `Expression '${xpath}' failed`,
|
|
722
|
+
expr: xpath,
|
|
723
|
+
level: 'Error',
|
|
724
|
+
},
|
|
725
|
+
}),
|
|
726
|
+
);
|
|
727
|
+
}
|
|
700
728
|
}
|
|
701
729
|
|
|
702
730
|
/**
|
|
@@ -705,78 +733,74 @@ export function evaluateXPathToStrings(
|
|
|
705
733
|
* @param {string} xpath The XPath to run
|
|
706
734
|
* @param {Node} contextNode The start of the XPath
|
|
707
735
|
* @param {Node} formElement The form element associated to the XPath
|
|
708
|
-
* @param {DomFacade} [domFacade=null] A DomFacade is used in bindings to intercept DOM
|
|
709
736
|
* @param {Node} formElement The element where the XPath is defined: used for namespace resolving
|
|
737
|
+
* @param {import('fontoxpath').IDomFacade} [domFacade=null] A DomFacade is used in bindings to intercept DOM
|
|
710
738
|
* access. This is used to determine dependencies between bind elements.
|
|
711
|
-
* @return {
|
|
739
|
+
* @return {number}
|
|
712
740
|
*/
|
|
713
|
-
export function evaluateXPathToNumber(
|
|
714
|
-
|
|
715
|
-
contextNode,
|
|
716
|
-
formElement
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
level:'Error'
|
|
741
|
-
},
|
|
742
|
-
}));
|
|
743
|
-
}
|
|
741
|
+
export function evaluateXPathToNumber(xpath, contextNode, formElement, domFacade = null) {
|
|
742
|
+
try {
|
|
743
|
+
const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
|
|
744
|
+
const variablesInScope = getVariablesInScope(formElement);
|
|
745
|
+
|
|
746
|
+
return fxEvaluateXPathToNumber(xpath, contextNode, domFacade, variablesInScope, {
|
|
747
|
+
currentContext: { formElement },
|
|
748
|
+
functionNameResolver,
|
|
749
|
+
moduleImports: {
|
|
750
|
+
xf: XFORMS_NAMESPACE_URI,
|
|
751
|
+
},
|
|
752
|
+
namespaceResolver,
|
|
753
|
+
});
|
|
754
|
+
} catch (e) {
|
|
755
|
+
formElement.dispatchEvent(
|
|
756
|
+
new CustomEvent('error', {
|
|
757
|
+
composed: false,
|
|
758
|
+
bubbles: true,
|
|
759
|
+
detail: {
|
|
760
|
+
origin: formElement,
|
|
761
|
+
message: `Expression '${xpath}' failed`,
|
|
762
|
+
expr: xpath,
|
|
763
|
+
level: 'Error',
|
|
764
|
+
},
|
|
765
|
+
}),
|
|
766
|
+
);
|
|
767
|
+
}
|
|
744
768
|
}
|
|
745
769
|
|
|
746
770
|
const contextFunction = (dynamicContext, string) => {
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
}
|
|
762
|
-
}
|
|
771
|
+
const caller = dynamicContext.currentContext.formElement;
|
|
772
|
+
let instance = null;
|
|
773
|
+
if (string) {
|
|
774
|
+
instance = resolveId(string, caller);
|
|
775
|
+
} else {
|
|
776
|
+
instance = XPathUtil.getParentBindingElement(caller);
|
|
777
|
+
}
|
|
778
|
+
if (instance) {
|
|
779
|
+
if (instance.nodeName === 'FX-REPEAT') {
|
|
780
|
+
const { nodeset } = instance;
|
|
781
|
+
for (let parent = caller; parent; parent = parent.parentNode) {
|
|
782
|
+
if (parent.parentNode === instance) {
|
|
783
|
+
const offset = Array.from(parent.parentNode.children).indexOf(parent);
|
|
784
|
+
return nodeset[offset];
|
|
763
785
|
}
|
|
764
|
-
|
|
786
|
+
}
|
|
765
787
|
}
|
|
788
|
+
return instance.nodeset;
|
|
789
|
+
}
|
|
766
790
|
|
|
767
|
-
|
|
791
|
+
return caller.getInScopeContext();
|
|
768
792
|
};
|
|
769
793
|
|
|
770
794
|
// todo: implement
|
|
771
795
|
const currentFunction = (dynamicContext, string) => {
|
|
772
|
-
|
|
773
|
-
|
|
796
|
+
const caller = dynamicContext.currentContext.formElement;
|
|
797
|
+
return null;
|
|
774
798
|
};
|
|
775
799
|
|
|
776
800
|
const elementFunction = (dynamicContext, string) => {
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
801
|
+
const caller = dynamicContext.currentContext.formElement;
|
|
802
|
+
const newElement = document.createElement(string);
|
|
803
|
+
return newElement;
|
|
780
804
|
};
|
|
781
805
|
|
|
782
806
|
/**
|
|
@@ -784,10 +808,10 @@ const elementFunction = (dynamicContext, string) => {
|
|
|
784
808
|
* @return instance data for given id serialized to string.
|
|
785
809
|
*/
|
|
786
810
|
registerCustomXPathFunction(
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
811
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'context' },
|
|
812
|
+
[],
|
|
813
|
+
'item()?',
|
|
814
|
+
contextFunction,
|
|
791
815
|
);
|
|
792
816
|
|
|
793
817
|
/**
|
|
@@ -795,24 +819,24 @@ registerCustomXPathFunction(
|
|
|
795
819
|
* @return instance data for given id serialized to string.
|
|
796
820
|
*/
|
|
797
821
|
registerCustomXPathFunction(
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
822
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'context' },
|
|
823
|
+
['xs:string'],
|
|
824
|
+
'item()?',
|
|
825
|
+
contextFunction,
|
|
802
826
|
);
|
|
803
827
|
|
|
804
828
|
registerCustomXPathFunction(
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
829
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'current' },
|
|
830
|
+
['xs:string'],
|
|
831
|
+
'item()?',
|
|
832
|
+
currentFunction,
|
|
809
833
|
);
|
|
810
834
|
|
|
811
835
|
registerCustomXPathFunction(
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
836
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'element' },
|
|
837
|
+
['xs:string'],
|
|
838
|
+
'item()?',
|
|
839
|
+
elementFunction,
|
|
816
840
|
);
|
|
817
841
|
|
|
818
842
|
/**
|
|
@@ -820,53 +844,53 @@ registerCustomXPathFunction(
|
|
|
820
844
|
* @return instance data for given id serialized to string.
|
|
821
845
|
*/
|
|
822
846
|
registerCustomXPathFunction(
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
847
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'log' },
|
|
848
|
+
['xs:string?'],
|
|
849
|
+
'xs:string?',
|
|
850
|
+
(dynamicContext, string) => {
|
|
851
|
+
const { formElement } = dynamicContext.currentContext;
|
|
852
|
+
const instance = resolveId(string, formElement, 'fx-instance');
|
|
853
|
+
if (instance) {
|
|
854
|
+
if (instance.getAttribute('type') === 'json') {
|
|
855
|
+
console.warn('log() does not work for JSON yet');
|
|
856
|
+
// return JSON.stringify(instance.getDefaultContext());
|
|
857
|
+
} else {
|
|
858
|
+
const def = new XMLSerializer().serializeToString(instance.getDefaultContext());
|
|
859
|
+
return prettifyXml(def);
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
return null;
|
|
863
|
+
},
|
|
840
864
|
);
|
|
841
865
|
registerCustomXPathFunction(
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
866
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'fore-attr' },
|
|
867
|
+
['xs:string?'],
|
|
868
|
+
'xs:string?',
|
|
869
|
+
(dynamicContext, string) => {
|
|
870
|
+
const { formElement } = dynamicContext.currentContext;
|
|
871
|
+
|
|
872
|
+
let parent = formElement;
|
|
873
|
+
if (formElement.nodeType === Node.TEXT_NODE) {
|
|
874
|
+
parent = formElement.parentNode;
|
|
875
|
+
}
|
|
876
|
+
const foreElement = parent.closest('fx-fore');
|
|
877
|
+
if (foreElement.hasAttribute(string)) {
|
|
878
|
+
return foreElement.getAttribute(string);
|
|
879
|
+
}
|
|
880
|
+
return null;
|
|
881
|
+
},
|
|
858
882
|
);
|
|
859
883
|
|
|
860
884
|
registerCustomXPathFunction(
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
885
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'parse' },
|
|
886
|
+
['xs:string?'],
|
|
887
|
+
'element()?',
|
|
888
|
+
(_dynamicContext, string) => {
|
|
889
|
+
const parser = new DOMParser();
|
|
890
|
+
const out = parser.parseFromString(string, 'application/xml');
|
|
891
|
+
console.log('parse', out);
|
|
892
|
+
|
|
893
|
+
/*
|
|
870
894
|
const {formElement} = dynamicContext.currentContext;
|
|
871
895
|
const instance = resolveId(string, formElement, 'fx-instance');
|
|
872
896
|
if (instance) {
|
|
@@ -879,53 +903,53 @@ registerCustomXPathFunction(
|
|
|
879
903
|
}
|
|
880
904
|
}
|
|
881
905
|
*/
|
|
882
|
-
|
|
883
|
-
|
|
906
|
+
return out.firstElementChild;
|
|
907
|
+
},
|
|
884
908
|
);
|
|
885
909
|
|
|
886
910
|
function buildTree(tree, data) {
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
911
|
+
if (!data) return;
|
|
912
|
+
if (data.nodeType === Node.ELEMENT_NODE) {
|
|
913
|
+
if (data.children) {
|
|
914
|
+
const details = document.createElement('details');
|
|
915
|
+
details.setAttribute('data-path', data.nodeName);
|
|
916
|
+
const summary = document.createElement('summary');
|
|
917
|
+
|
|
918
|
+
let display = ` <${data.nodeName}`;
|
|
919
|
+
Array.from(data.attributes).forEach(attr => {
|
|
920
|
+
display += ` ${attr.nodeName}="${attr.nodeValue}"`;
|
|
921
|
+
});
|
|
922
|
+
|
|
923
|
+
let contents;
|
|
924
|
+
if (
|
|
925
|
+
data.firstChild &&
|
|
926
|
+
data.firstChild.nodeType === Node.TEXT_NODE &&
|
|
927
|
+
data.firstChild.data.trim() !== ''
|
|
928
|
+
) {
|
|
929
|
+
// console.log('whoooooooooopp');
|
|
930
|
+
contents = data.firstChild.nodeValue;
|
|
931
|
+
display += `>${contents}</${data.nodeName}>`;
|
|
932
|
+
} else {
|
|
933
|
+
display += '>';
|
|
934
|
+
}
|
|
935
|
+
summary.textContent = display;
|
|
936
|
+
|
|
937
|
+
details.appendChild(summary);
|
|
938
|
+
if (data.childElementCount !== 0) {
|
|
939
|
+
details.setAttribute('open', 'open');
|
|
940
|
+
} else {
|
|
941
|
+
summary.setAttribute('style', 'list-style:none;');
|
|
942
|
+
}
|
|
943
|
+
tree.appendChild(details);
|
|
944
|
+
|
|
945
|
+
Array.from(data.children).forEach(child => {
|
|
946
|
+
// if(child.nodeType === Node.ELEMENT_NODE){
|
|
947
|
+
// child.parentNode.appendChild(buildTree(child));
|
|
948
|
+
buildTree(details, child);
|
|
949
|
+
// }
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
} /* else if(data.nodeType === Node.ATTRIBUTE_NODE){
|
|
929
953
|
//create span for now
|
|
930
954
|
// const span = document.createElement('span');
|
|
931
955
|
// span.style.background = 'grey';
|
|
@@ -936,229 +960,220 @@ function buildTree(tree, data) {
|
|
|
936
960
|
tree.textContent = data;
|
|
937
961
|
} */
|
|
938
962
|
|
|
939
|
-
|
|
963
|
+
// return tree;
|
|
940
964
|
}
|
|
941
965
|
|
|
942
966
|
registerCustomXPathFunction(
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
967
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'logtree' },
|
|
968
|
+
['xs:string?'],
|
|
969
|
+
'element()?',
|
|
970
|
+
(dynamicContext, string) => {
|
|
971
|
+
const { formElement } = dynamicContext.currentContext;
|
|
972
|
+
const instance = resolveId(string, formElement, 'fx-instance');
|
|
973
|
+
|
|
974
|
+
if (instance) {
|
|
975
|
+
// const def = new XMLSerializer().serializeToString(instance.getDefaultContext());
|
|
976
|
+
// const def = JSON.stringify(instance.getDefaultContext());
|
|
977
|
+
|
|
978
|
+
const treeDiv = document.createElement('div');
|
|
979
|
+
treeDiv.setAttribute('class', 'logtree');
|
|
980
|
+
// const datatree = buildTree(tree,instance.getDefaultContext());
|
|
981
|
+
// return tree.appendChild(datatree);
|
|
982
|
+
// return buildTree(root,instance.getDefaultContext());;
|
|
983
|
+
const form = dynamicContext.currentContext.formElement;
|
|
984
|
+
const logtree = form.querySelector('.logtree');
|
|
985
|
+
if (logtree) {
|
|
986
|
+
logtree.parentNode.removeChild(logtree);
|
|
987
|
+
}
|
|
988
|
+
const tree = buildTree(treeDiv, instance.getDefaultContext());
|
|
989
|
+
if (tree) {
|
|
990
|
+
form.appendChild(tree);
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
return null;
|
|
994
|
+
},
|
|
971
995
|
);
|
|
972
996
|
|
|
973
997
|
const instance = (dynamicContext, string) => {
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
998
|
+
// Spec: https://www.w3.org/TR/xforms-xpath/#The_XForms_Function_Library#The_instance.28.29_Function
|
|
999
|
+
// TODO: handle no string passed (null will be passed instead)
|
|
1000
|
+
|
|
1001
|
+
/**
|
|
1002
|
+
* @type {import('./fx-fore.js').FxFore}
|
|
1003
|
+
*/
|
|
1004
|
+
const formElement = fxEvaluateXPathToFirstNode(
|
|
1005
|
+
'ancestor-or-self::fx-fore[1]',
|
|
1006
|
+
dynamicContext.currentContext.formElement,
|
|
1007
|
+
null,
|
|
1008
|
+
null,
|
|
1009
|
+
{ namespaceResolver: xhtmlNamespaceResolver },
|
|
1010
|
+
);
|
|
1011
|
+
|
|
1012
|
+
let lookup = null;
|
|
1013
|
+
if (string === null || string === 'default') {
|
|
1014
|
+
lookup = formElement.getModel().getDefaultInstance();
|
|
1015
|
+
} else {
|
|
1016
|
+
lookup = formElement.getModel().getInstance(string);
|
|
1017
|
+
if (!lookup) {
|
|
1018
|
+
document.querySelector('fx-fore').dispatchEvent(
|
|
1019
|
+
new CustomEvent('error', {
|
|
1020
|
+
composed: true,
|
|
1021
|
+
bubbles: true,
|
|
1022
|
+
detail: {
|
|
1023
|
+
origin: 'functions',
|
|
1024
|
+
message: `Instance not found '${string}'`,
|
|
1025
|
+
level: 'Error',
|
|
1026
|
+
},
|
|
1027
|
+
}),
|
|
1028
|
+
);
|
|
1001
1029
|
}
|
|
1030
|
+
}
|
|
1002
1031
|
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
return context;
|
|
1032
|
+
const context = lookup.getDefaultContext();
|
|
1033
|
+
if (!context) {
|
|
1034
|
+
return null;
|
|
1035
|
+
}
|
|
1036
|
+
return context;
|
|
1009
1037
|
};
|
|
1010
1038
|
|
|
1011
1039
|
registerCustomXPathFunction(
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1040
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'index' },
|
|
1041
|
+
['xs:string?'],
|
|
1042
|
+
'xs:integer?',
|
|
1043
|
+
(dynamicContext, string) => {
|
|
1044
|
+
const { formElement } = dynamicContext.currentContext;
|
|
1045
|
+
if (string === null) {
|
|
1046
|
+
return 1;
|
|
1047
|
+
}
|
|
1048
|
+
const repeat = resolveId(string, formElement, 'fx-repeat');
|
|
1021
1049
|
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1050
|
+
// const def = instance.getInstanceData();
|
|
1051
|
+
if (repeat) {
|
|
1052
|
+
return repeat.getAttribute('index');
|
|
1053
|
+
}
|
|
1054
|
+
return Number(1);
|
|
1055
|
+
},
|
|
1028
1056
|
);
|
|
1029
1057
|
|
|
1030
1058
|
// Note that this is not to spec. The spec enforces elements to be returned from the
|
|
1031
1059
|
// instance. However, we allow instances to actually be JSON!
|
|
1032
1060
|
registerCustomXPathFunction(
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1061
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'instance' },
|
|
1062
|
+
[],
|
|
1063
|
+
'item()?',
|
|
1064
|
+
domFacade => instance(domFacade, null),
|
|
1037
1065
|
);
|
|
1038
1066
|
|
|
1039
1067
|
registerCustomXPathFunction(
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1068
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'instance' },
|
|
1069
|
+
['xs:string?'],
|
|
1070
|
+
'item()?',
|
|
1071
|
+
instance,
|
|
1044
1072
|
);
|
|
1045
|
-
const getAttributes = (value) => {
|
|
1046
|
-
if (Array.isArray(value)) {
|
|
1047
|
-
return ` type="array"`;
|
|
1048
|
-
} else if (typeof value === 'number') {
|
|
1049
|
-
return ` type="number"`;
|
|
1050
|
-
} else if (typeof value === 'boolean') {
|
|
1051
|
-
return ` type="boolean"`;
|
|
1052
|
-
}
|
|
1053
|
-
return '';
|
|
1054
|
-
};
|
|
1055
1073
|
|
|
1056
|
-
const jsonToXml = (
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1074
|
+
const jsonToXml = (_dynamicContext, json) => {
|
|
1075
|
+
const escapeXml = str =>
|
|
1076
|
+
str.replace(
|
|
1077
|
+
/[^\u0009\u000A\u000D\u0020-\uD7FF\uE000-\uFFFD]/g,
|
|
1078
|
+
char => `\\u${char.charCodeAt(0).toString(16).padStart(4, '0')}`,
|
|
1079
|
+
);
|
|
1062
1080
|
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
}
|
|
1093
|
-
});
|
|
1081
|
+
const convert = (obj, parent) => {
|
|
1082
|
+
const type = typeof obj;
|
|
1083
|
+
if (type === 'number') {
|
|
1084
|
+
parent.setAttribute('type', 'number');
|
|
1085
|
+
parent.textContent = obj.toString();
|
|
1086
|
+
} else if (type === 'boolean') {
|
|
1087
|
+
parent.setAttribute('type', 'boolean');
|
|
1088
|
+
parent.textContent = obj.toString();
|
|
1089
|
+
} else if (obj === null) {
|
|
1090
|
+
const node = document.createElement('_');
|
|
1091
|
+
node.setAttribute('type', 'null');
|
|
1092
|
+
parent.appendChild(node);
|
|
1093
|
+
} else if (type === 'string') {
|
|
1094
|
+
parent.textContent = escapeXml(obj);
|
|
1095
|
+
} else if (Array.isArray(obj)) {
|
|
1096
|
+
parent.setAttribute('type', 'array');
|
|
1097
|
+
obj.forEach(item => {
|
|
1098
|
+
const node = document.createElement('_');
|
|
1099
|
+
convert(item, node);
|
|
1100
|
+
node.textContent = item;
|
|
1101
|
+
parent.appendChild(node);
|
|
1102
|
+
});
|
|
1103
|
+
} else if (type === 'object') {
|
|
1104
|
+
parent.setAttribute('type', 'object');
|
|
1105
|
+
Object.entries(obj).forEach(([key, value]) => {
|
|
1106
|
+
if (value) {
|
|
1107
|
+
const childNode = document.createElement(key.replace(/[^a-zA-Z0-9_]/g, '_'));
|
|
1108
|
+
convert(value, childNode);
|
|
1109
|
+
parent.appendChild(childNode);
|
|
1094
1110
|
}
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
const root = document.createElement('json');
|
|
1098
|
-
if(Array.isArray(json)){
|
|
1099
|
-
root.setAttribute('type', 'array');
|
|
1100
|
-
}else{
|
|
1101
|
-
root.setAttribute('type', 'object');
|
|
1111
|
+
});
|
|
1102
1112
|
}
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1113
|
+
};
|
|
1114
|
+
|
|
1115
|
+
const root = document.createElement('json');
|
|
1116
|
+
if (Array.isArray(json)) {
|
|
1117
|
+
root.setAttribute('type', 'array');
|
|
1118
|
+
} else {
|
|
1119
|
+
root.setAttribute('type', 'object');
|
|
1120
|
+
}
|
|
1121
|
+
convert(json, root);
|
|
1122
|
+
// return root.outerHTML;
|
|
1123
|
+
console.log('xml', root);
|
|
1124
|
+
return root;
|
|
1107
1125
|
};
|
|
1108
1126
|
|
|
1109
1127
|
registerCustomXPathFunction(
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1128
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'json2xml' },
|
|
1129
|
+
['item()?'],
|
|
1130
|
+
'item()?',
|
|
1131
|
+
jsonToXml,
|
|
1114
1132
|
);
|
|
1115
|
-
const xmlToJson = (
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
}
|
|
1140
|
-
obj[childName].push(childValue);
|
|
1141
|
-
} else {
|
|
1142
|
-
obj[childName] = childValue;
|
|
1143
|
-
}
|
|
1144
|
-
}
|
|
1145
|
-
return obj;
|
|
1146
|
-
} else if (isTextNode(node)) {
|
|
1147
|
-
return node.textContent;
|
|
1133
|
+
const xmlToJson = (_dynamicContext, xml) => {
|
|
1134
|
+
const isElementNode = node => node.nodeType === Node.ELEMENT_NODE;
|
|
1135
|
+
|
|
1136
|
+
const isTextNode = node => node.nodeType === Node.TEXT_NODE;
|
|
1137
|
+
|
|
1138
|
+
const parseNode = node => {
|
|
1139
|
+
if (isElementNode(node)) {
|
|
1140
|
+
const obj = {};
|
|
1141
|
+
if (node.hasAttributes()) {
|
|
1142
|
+
obj.type = node.getAttribute('type');
|
|
1143
|
+
}
|
|
1144
|
+
if (node.childNodes.length === 1 && isTextNode(node.firstChild)) {
|
|
1145
|
+
return node.textContent;
|
|
1146
|
+
}
|
|
1147
|
+
for (const child of node.childNodes) {
|
|
1148
|
+
const childName = child.nodeName;
|
|
1149
|
+
const childValue = parseNode(child);
|
|
1150
|
+
if (obj[childName]) {
|
|
1151
|
+
if (!Array.isArray(obj[childName])) {
|
|
1152
|
+
obj[childName] = [obj[childName]];
|
|
1153
|
+
}
|
|
1154
|
+
obj[childName].push(childValue);
|
|
1155
|
+
} else {
|
|
1156
|
+
obj[childName] = childValue;
|
|
1148
1157
|
}
|
|
1158
|
+
}
|
|
1159
|
+
return obj;
|
|
1160
|
+
}
|
|
1161
|
+
if (isTextNode(node)) {
|
|
1162
|
+
return node.textContent;
|
|
1163
|
+
}
|
|
1164
|
+
return undefined;
|
|
1165
|
+
};
|
|
1149
1166
|
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
const root = xmlDoc.documentElement;
|
|
1155
|
-
return parseNode(root);
|
|
1167
|
+
const parser = new DOMParser();
|
|
1168
|
+
const xmlDoc = parser.parseFromString(xml, 'application/xml');
|
|
1169
|
+
const root = xmlDoc.documentElement;
|
|
1170
|
+
return parseNode(root);
|
|
1156
1171
|
};
|
|
1157
1172
|
registerCustomXPathFunction(
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1173
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'xmltoJson' },
|
|
1174
|
+
['item()?'],
|
|
1175
|
+
'item()?',
|
|
1176
|
+
xmlToJson,
|
|
1162
1177
|
);
|
|
1163
1178
|
|
|
1164
1179
|
/*
|
|
@@ -1167,54 +1182,58 @@ const xml = '<json type="object"><given>Mark</given><family>Smith</family></json
|
|
|
1167
1182
|
console.log(xmlToJson(xml));
|
|
1168
1183
|
*/
|
|
1169
1184
|
|
|
1170
|
-
|
|
1171
1185
|
registerCustomXPathFunction(
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1186
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'depends' },
|
|
1187
|
+
['node()*'],
|
|
1188
|
+
'item()?',
|
|
1189
|
+
(_dynamicContext, nodes) =>
|
|
1190
|
+
// console.log('depends on : ', nodes[0]);
|
|
1191
|
+
nodes[0],
|
|
1178
1192
|
);
|
|
1179
1193
|
|
|
1180
1194
|
registerCustomXPathFunction(
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1195
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'event' },
|
|
1196
|
+
['xs:string?'],
|
|
1197
|
+
'item()?',
|
|
1198
|
+
(dynamicContext, arg) => {
|
|
1199
|
+
if (!arg) return null;
|
|
1200
|
+
|
|
1201
|
+
for (
|
|
1202
|
+
let ancestor = dynamicContext.currentContext.formElement;
|
|
1203
|
+
ancestor;
|
|
1204
|
+
ancestor = ancestor.parentNode
|
|
1205
|
+
) {
|
|
1206
|
+
if (!ancestor.currentEvent) {
|
|
1207
|
+
continue;
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
// We have a current event. read the property either from detail, or from the event
|
|
1211
|
+
// itself.
|
|
1212
|
+
// Check detail for custom events! This is how that is passed along
|
|
1213
|
+
if (
|
|
1214
|
+
ancestor.currentEvent.detail &&
|
|
1215
|
+
typeof ancestor.currentEvent.detail === 'object' &&
|
|
1216
|
+
arg in ancestor.currentEvent.detail
|
|
1217
|
+
) {
|
|
1218
|
+
return ancestor.currentEvent.detail[arg];
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
// arg might be `code`, so currentEvent.code should work
|
|
1222
|
+
if (arg.includes('.')) {
|
|
1223
|
+
return _propertyLookup(ancestor.currentEvent, arg);
|
|
1224
|
+
}
|
|
1225
|
+
return ancestor.currentEvent[arg] || null;
|
|
1226
|
+
}
|
|
1227
|
+
return null;
|
|
1228
|
+
},
|
|
1210
1229
|
);
|
|
1211
1230
|
|
|
1212
|
-
function _propertyLookup(obj,path){
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1231
|
+
function _propertyLookup(obj, path) {
|
|
1232
|
+
const parts = path.split('.');
|
|
1233
|
+
if (parts.length == 1) {
|
|
1234
|
+
return obj[parts[0]];
|
|
1235
|
+
}
|
|
1236
|
+
return _propertyLookup(obj[parts[0]], parts.slice(1).join('.'));
|
|
1218
1237
|
}
|
|
1219
1238
|
|
|
1220
1239
|
// Implement the XForms standard functions here.
|
|
@@ -1254,103 +1273,94 @@ registerXQueryModule(`
|
|
|
1254
1273
|
* @return {string}
|
|
1255
1274
|
*/
|
|
1256
1275
|
registerCustomXPathFunction(
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1276
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'base64encode' },
|
|
1277
|
+
['xs:string?'],
|
|
1278
|
+
'xs:string?',
|
|
1279
|
+
(_dynamicContext, string) => btoa(string),
|
|
1261
1280
|
);
|
|
1262
1281
|
|
|
1263
1282
|
registerCustomXPathFunction(
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1283
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'local-date' },
|
|
1284
|
+
[],
|
|
1285
|
+
'xs:string?',
|
|
1286
|
+
(_dynamicContext, _string) => new Date().toLocaleDateString(),
|
|
1268
1287
|
);
|
|
1269
1288
|
registerCustomXPathFunction(
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
return new Date().toLocaleString();
|
|
1275
|
-
},
|
|
1289
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'local-dateTime' },
|
|
1290
|
+
[],
|
|
1291
|
+
'xs:string?',
|
|
1292
|
+
(_dynamicContext, _string) => new Date().toLocaleString(),
|
|
1276
1293
|
);
|
|
1277
1294
|
registerCustomXPathFunction(
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1295
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'uri' },
|
|
1296
|
+
[],
|
|
1297
|
+
'xs:string?',
|
|
1298
|
+
(_dynamicContext, _string) => window.location.href,
|
|
1282
1299
|
);
|
|
1283
1300
|
registerCustomXPathFunction(
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1301
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'uri-fragment' },
|
|
1302
|
+
[],
|
|
1303
|
+
'xs:string?',
|
|
1304
|
+
(_dynamicContext, _arg) => window.location.hash,
|
|
1288
1305
|
);
|
|
1289
1306
|
registerCustomXPathFunction(
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1307
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'uri-host' },
|
|
1308
|
+
[],
|
|
1309
|
+
'xs:string?',
|
|
1310
|
+
(_dynamicContext, _arg) => window.location.host,
|
|
1294
1311
|
);
|
|
1295
1312
|
registerCustomXPathFunction(
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1313
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'uri-query' },
|
|
1314
|
+
[],
|
|
1315
|
+
'xs:string?',
|
|
1316
|
+
(_dynamicContext, _arg) => window.location.search,
|
|
1300
1317
|
);
|
|
1301
1318
|
registerCustomXPathFunction(
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1319
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'uri-relpath' },
|
|
1320
|
+
[],
|
|
1321
|
+
'xs:string?',
|
|
1322
|
+
(_dynamicContext, _arg) => {
|
|
1323
|
+
const path = new URL(window.location.href).pathname;
|
|
1324
|
+
return path.substring(0, path.lastIndexOf('/') + 1);
|
|
1325
|
+
},
|
|
1309
1326
|
);
|
|
1310
1327
|
registerCustomXPathFunction(
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
return new URL(window.location.href).pathname;
|
|
1316
|
-
},
|
|
1328
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'uri-path' },
|
|
1329
|
+
[],
|
|
1330
|
+
'xs:string?',
|
|
1331
|
+
(_dynamicContext, _arg) => new URL(window.location.href).pathname,
|
|
1317
1332
|
);
|
|
1318
1333
|
registerCustomXPathFunction(
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
return window.location.port;
|
|
1324
|
-
},
|
|
1334
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'uri-port' },
|
|
1335
|
+
[],
|
|
1336
|
+
'xs:string?',
|
|
1337
|
+
(_dynamicContext, _arg) => window.location.port,
|
|
1325
1338
|
);
|
|
1326
1339
|
registerCustomXPathFunction(
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
},
|
|
1340
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'uri-param' },
|
|
1341
|
+
['xs:string?'],
|
|
1342
|
+
'xs:string?',
|
|
1343
|
+
(_dynamicContext, arg) => {
|
|
1344
|
+
if (!arg) return null;
|
|
1345
|
+
|
|
1346
|
+
const { search } = window.location;
|
|
1347
|
+
const urlparams = new URLSearchParams(search);
|
|
1348
|
+
const param = urlparams.get(arg);
|
|
1349
|
+
return param || '';
|
|
1350
|
+
},
|
|
1339
1351
|
);
|
|
1340
1352
|
registerCustomXPathFunction(
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
return new URL(window.location.href).protocol;
|
|
1346
|
-
},
|
|
1353
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'uri-scheme' },
|
|
1354
|
+
[],
|
|
1355
|
+
'xs:string?',
|
|
1356
|
+
(_dynamicContext, _arg) => new URL(window.location.href).protocol,
|
|
1347
1357
|
);
|
|
1348
1358
|
registerCustomXPathFunction(
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1359
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'uri-scheme-specific-part' },
|
|
1360
|
+
[],
|
|
1361
|
+
'xs:string?',
|
|
1362
|
+
(_dynamicContext, _arg) => {
|
|
1363
|
+
const uri = window.location.href;
|
|
1364
|
+
return uri.substring(uri.indexOf(':') + 1, uri.length);
|
|
1365
|
+
},
|
|
1356
1366
|
);
|