@jinntec/fore 2.1.1 → 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 +2 -2
- 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 +4 -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 +6 -99
- package/src/functions/fx-functionlib.js +56 -0
- package/src/functions/registerFunction.js +112 -0
- package/src/fx-bind.js +19 -24
- package/src/fx-connection.js +226 -0
- package/src/fx-fore.js +844 -815
- package/src/fx-header.js +4 -4
- package/src/fx-instance.js +25 -29
- package/src/fx-model.js +405 -380
- package/src/fx-submission.js +399 -397
- package/src/fx-var.js +13 -12
- 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 +73 -53
- 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 -958
- 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,44 +524,47 @@ export function evaluateXPath(xpath, contextNode, formElement, variables = {}, o
|
|
|
497
524
|
}),
|
|
498
525
|
);
|
|
499
526
|
*/
|
|
500
|
-
|
|
501
|
-
|
|
527
|
+
// Return 'nothing' in hope the rest of the page can forgive this
|
|
528
|
+
return [];
|
|
529
|
+
}
|
|
502
530
|
}
|
|
503
531
|
/**
|
|
504
532
|
* Evaluate an XPath to the first Node
|
|
505
533
|
*
|
|
506
534
|
* @param {string} xpath The XPath to run
|
|
507
535
|
* @param {Node} contextNode The start of the XPath
|
|
508
|
-
* @param {
|
|
509
|
-
* @
|
|
536
|
+
* @param {import('./ForeElementMixin.js').default} formElement The form element associated to the XPath
|
|
537
|
+
* @returns {Node} The first node found in the XPath
|
|
510
538
|
*/
|
|
511
539
|
export function evaluateXPathToFirstNode(xpath, contextNode, formElement) {
|
|
512
|
-
|
|
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
|
-
|
|
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
|
+
}
|
|
538
568
|
}
|
|
539
569
|
|
|
540
570
|
/**
|
|
@@ -542,36 +572,38 @@ export function evaluateXPathToFirstNode(xpath, contextNode, formElement) {
|
|
|
542
572
|
*
|
|
543
573
|
* @param {string} xpath The XPath to run
|
|
544
574
|
* @param {Node} contextNode The start of the XPath
|
|
545
|
-
* @param {
|
|
575
|
+
* @param {import('./ForeElementMixin.js').default} formElement The form element associated to the XPath
|
|
546
576
|
* @return {Node[]} All nodes
|
|
547
577
|
*/
|
|
548
578
|
export function evaluateXPathToNodes(xpath, contextNode, formElement) {
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
579
|
+
try {
|
|
580
|
+
const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
|
|
581
|
+
const variablesInScope = getVariablesInScope(formElement);
|
|
552
582
|
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
}
|
|
574
|
-
|
|
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
|
+
}
|
|
575
607
|
}
|
|
576
608
|
|
|
577
609
|
/**
|
|
@@ -579,34 +611,36 @@ export function evaluateXPathToNodes(xpath, contextNode, formElement) {
|
|
|
579
611
|
*
|
|
580
612
|
* @param {string} xpath The XPath to run
|
|
581
613
|
* @param {Node} contextNode The start of the XPath
|
|
582
|
-
* @param {
|
|
614
|
+
* @param {import('./ForeElementMixin.js').default} formElement The form element associated to the XPath
|
|
583
615
|
* @return {boolean}
|
|
584
616
|
*/
|
|
585
617
|
export function evaluateXPathToBoolean(xpath, contextNode, formElement) {
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
618
|
+
try {
|
|
619
|
+
const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
|
|
620
|
+
const variablesInScope = getVariablesInScope(formElement);
|
|
589
621
|
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
}
|
|
609
|
-
|
|
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
|
+
}
|
|
610
644
|
}
|
|
611
645
|
|
|
612
646
|
/**
|
|
@@ -615,42 +649,38 @@ export function evaluateXPathToBoolean(xpath, contextNode, formElement) {
|
|
|
615
649
|
* @param {string} xpath The XPath to run
|
|
616
650
|
* @param {Node} contextNode The start of the XPath
|
|
617
651
|
* @param {Node} formElement The form element associated to the XPath
|
|
618
|
-
* @param {DomFacade} [domFacade=null] A DomFacade is used in bindings to intercept DOM
|
|
619
|
-
* access. This is used to determine dependencies between bind elements.
|
|
620
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.
|
|
621
655
|
* @return {string}
|
|
622
656
|
*/
|
|
623
|
-
export function evaluateXPathToString(
|
|
624
|
-
|
|
625
|
-
contextNode,
|
|
626
|
-
formElement
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
level:'Error'
|
|
651
|
-
},
|
|
652
|
-
}));
|
|
653
|
-
}
|
|
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
|
+
}
|
|
654
684
|
}
|
|
655
685
|
|
|
656
686
|
/**
|
|
@@ -659,43 +689,42 @@ export function evaluateXPathToString(
|
|
|
659
689
|
* @param {string} xpath The XPath to run
|
|
660
690
|
* @param {Node} contextNode The start of the XPath
|
|
661
691
|
* @param {Node} formElement The form element associated to the XPath
|
|
662
|
-
* @param {DomFacade} [domFacade=null] A DomFacade is used in bindings to intercept DOM
|
|
663
|
-
* access. This is used to determine dependencies between bind elements.
|
|
664
692
|
* @param {Node} formElement The element where the XPath is defined: used for namespace resolving
|
|
665
|
-
* @
|
|
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[]}
|
|
666
696
|
*/
|
|
667
|
-
export function evaluateXPathToStrings(
|
|
668
|
-
|
|
669
|
-
contextNode,
|
|
670
|
-
|
|
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
|
-
}
|
|
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
|
+
}
|
|
699
728
|
}
|
|
700
729
|
|
|
701
730
|
/**
|
|
@@ -704,78 +733,74 @@ export function evaluateXPathToStrings(
|
|
|
704
733
|
* @param {string} xpath The XPath to run
|
|
705
734
|
* @param {Node} contextNode The start of the XPath
|
|
706
735
|
* @param {Node} formElement The form element associated to the XPath
|
|
707
|
-
* @param {DomFacade} [domFacade=null] A DomFacade is used in bindings to intercept DOM
|
|
708
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
|
|
709
738
|
* access. This is used to determine dependencies between bind elements.
|
|
710
|
-
* @return {
|
|
739
|
+
* @return {number}
|
|
711
740
|
*/
|
|
712
|
-
export function evaluateXPathToNumber(
|
|
713
|
-
|
|
714
|
-
contextNode,
|
|
715
|
-
formElement
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
level:'Error'
|
|
740
|
-
},
|
|
741
|
-
}));
|
|
742
|
-
}
|
|
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
|
+
}
|
|
743
768
|
}
|
|
744
769
|
|
|
745
770
|
const contextFunction = (dynamicContext, string) => {
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
}
|
|
761
|
-
}
|
|
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];
|
|
762
785
|
}
|
|
763
|
-
|
|
786
|
+
}
|
|
764
787
|
}
|
|
788
|
+
return instance.nodeset;
|
|
789
|
+
}
|
|
765
790
|
|
|
766
|
-
|
|
791
|
+
return caller.getInScopeContext();
|
|
767
792
|
};
|
|
768
793
|
|
|
769
794
|
// todo: implement
|
|
770
795
|
const currentFunction = (dynamicContext, string) => {
|
|
771
|
-
|
|
772
|
-
|
|
796
|
+
const caller = dynamicContext.currentContext.formElement;
|
|
797
|
+
return null;
|
|
773
798
|
};
|
|
774
799
|
|
|
775
800
|
const elementFunction = (dynamicContext, string) => {
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
801
|
+
const caller = dynamicContext.currentContext.formElement;
|
|
802
|
+
const newElement = document.createElement(string);
|
|
803
|
+
return newElement;
|
|
779
804
|
};
|
|
780
805
|
|
|
781
806
|
/**
|
|
@@ -783,10 +808,10 @@ const elementFunction = (dynamicContext, string) => {
|
|
|
783
808
|
* @return instance data for given id serialized to string.
|
|
784
809
|
*/
|
|
785
810
|
registerCustomXPathFunction(
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
811
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'context' },
|
|
812
|
+
[],
|
|
813
|
+
'item()?',
|
|
814
|
+
contextFunction,
|
|
790
815
|
);
|
|
791
816
|
|
|
792
817
|
/**
|
|
@@ -794,24 +819,24 @@ registerCustomXPathFunction(
|
|
|
794
819
|
* @return instance data for given id serialized to string.
|
|
795
820
|
*/
|
|
796
821
|
registerCustomXPathFunction(
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
822
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'context' },
|
|
823
|
+
['xs:string'],
|
|
824
|
+
'item()?',
|
|
825
|
+
contextFunction,
|
|
801
826
|
);
|
|
802
827
|
|
|
803
828
|
registerCustomXPathFunction(
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
829
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'current' },
|
|
830
|
+
['xs:string'],
|
|
831
|
+
'item()?',
|
|
832
|
+
currentFunction,
|
|
808
833
|
);
|
|
809
834
|
|
|
810
835
|
registerCustomXPathFunction(
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
836
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'element' },
|
|
837
|
+
['xs:string'],
|
|
838
|
+
'item()?',
|
|
839
|
+
elementFunction,
|
|
815
840
|
);
|
|
816
841
|
|
|
817
842
|
/**
|
|
@@ -819,53 +844,53 @@ registerCustomXPathFunction(
|
|
|
819
844
|
* @return instance data for given id serialized to string.
|
|
820
845
|
*/
|
|
821
846
|
registerCustomXPathFunction(
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
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
|
+
},
|
|
839
864
|
);
|
|
840
865
|
registerCustomXPathFunction(
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
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
|
+
},
|
|
857
882
|
);
|
|
858
883
|
|
|
859
884
|
registerCustomXPathFunction(
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
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
|
+
/*
|
|
869
894
|
const {formElement} = dynamicContext.currentContext;
|
|
870
895
|
const instance = resolveId(string, formElement, 'fx-instance');
|
|
871
896
|
if (instance) {
|
|
@@ -878,53 +903,53 @@ registerCustomXPathFunction(
|
|
|
878
903
|
}
|
|
879
904
|
}
|
|
880
905
|
*/
|
|
881
|
-
|
|
882
|
-
|
|
906
|
+
return out.firstElementChild;
|
|
907
|
+
},
|
|
883
908
|
);
|
|
884
909
|
|
|
885
910
|
function buildTree(tree, data) {
|
|
886
|
-
|
|
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
|
-
|
|
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){
|
|
928
953
|
//create span for now
|
|
929
954
|
// const span = document.createElement('span');
|
|
930
955
|
// span.style.background = 'grey';
|
|
@@ -935,229 +960,220 @@ function buildTree(tree, data) {
|
|
|
935
960
|
tree.textContent = data;
|
|
936
961
|
} */
|
|
937
962
|
|
|
938
|
-
|
|
963
|
+
// return tree;
|
|
939
964
|
}
|
|
940
965
|
|
|
941
966
|
registerCustomXPathFunction(
|
|
942
|
-
|
|
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
|
-
|
|
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
|
+
},
|
|
970
995
|
);
|
|
971
996
|
|
|
972
997
|
const instance = (dynamicContext, string) => {
|
|
973
|
-
|
|
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
|
-
|
|
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
|
+
);
|
|
1000
1029
|
}
|
|
1030
|
+
}
|
|
1001
1031
|
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
return context;
|
|
1032
|
+
const context = lookup.getDefaultContext();
|
|
1033
|
+
if (!context) {
|
|
1034
|
+
return null;
|
|
1035
|
+
}
|
|
1036
|
+
return context;
|
|
1008
1037
|
};
|
|
1009
1038
|
|
|
1010
1039
|
registerCustomXPathFunction(
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
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');
|
|
1020
1049
|
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1050
|
+
// const def = instance.getInstanceData();
|
|
1051
|
+
if (repeat) {
|
|
1052
|
+
return repeat.getAttribute('index');
|
|
1053
|
+
}
|
|
1054
|
+
return Number(1);
|
|
1055
|
+
},
|
|
1027
1056
|
);
|
|
1028
1057
|
|
|
1029
1058
|
// Note that this is not to spec. The spec enforces elements to be returned from the
|
|
1030
1059
|
// instance. However, we allow instances to actually be JSON!
|
|
1031
1060
|
registerCustomXPathFunction(
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1061
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'instance' },
|
|
1062
|
+
[],
|
|
1063
|
+
'item()?',
|
|
1064
|
+
domFacade => instance(domFacade, null),
|
|
1036
1065
|
);
|
|
1037
1066
|
|
|
1038
1067
|
registerCustomXPathFunction(
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1068
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'instance' },
|
|
1069
|
+
['xs:string?'],
|
|
1070
|
+
'item()?',
|
|
1071
|
+
instance,
|
|
1043
1072
|
);
|
|
1044
|
-
const getAttributes = (value) => {
|
|
1045
|
-
if (Array.isArray(value)) {
|
|
1046
|
-
return ` type="array"`;
|
|
1047
|
-
} else if (typeof value === 'number') {
|
|
1048
|
-
return ` type="number"`;
|
|
1049
|
-
} else if (typeof value === 'boolean') {
|
|
1050
|
-
return ` type="boolean"`;
|
|
1051
|
-
}
|
|
1052
|
-
return '';
|
|
1053
|
-
};
|
|
1054
1073
|
|
|
1055
|
-
const jsonToXml = (
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
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
|
+
);
|
|
1061
1080
|
|
|
1062
|
-
|
|
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
|
-
});
|
|
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);
|
|
1093
1110
|
}
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
const root = document.createElement('json');
|
|
1097
|
-
if(Array.isArray(json)){
|
|
1098
|
-
root.setAttribute('type', 'array');
|
|
1099
|
-
}else{
|
|
1100
|
-
root.setAttribute('type', 'object');
|
|
1111
|
+
});
|
|
1101
1112
|
}
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
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;
|
|
1106
1125
|
};
|
|
1107
1126
|
|
|
1108
1127
|
registerCustomXPathFunction(
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1128
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'json2xml' },
|
|
1129
|
+
['item()?'],
|
|
1130
|
+
'item()?',
|
|
1131
|
+
jsonToXml,
|
|
1113
1132
|
);
|
|
1114
|
-
const xmlToJson = (
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
}
|
|
1139
|
-
obj[childName].push(childValue);
|
|
1140
|
-
} else {
|
|
1141
|
-
obj[childName] = childValue;
|
|
1142
|
-
}
|
|
1143
|
-
}
|
|
1144
|
-
return obj;
|
|
1145
|
-
} else if (isTextNode(node)) {
|
|
1146
|
-
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;
|
|
1147
1157
|
}
|
|
1158
|
+
}
|
|
1159
|
+
return obj;
|
|
1160
|
+
}
|
|
1161
|
+
if (isTextNode(node)) {
|
|
1162
|
+
return node.textContent;
|
|
1163
|
+
}
|
|
1164
|
+
return undefined;
|
|
1165
|
+
};
|
|
1148
1166
|
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
const root = xmlDoc.documentElement;
|
|
1154
|
-
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);
|
|
1155
1171
|
};
|
|
1156
1172
|
registerCustomXPathFunction(
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1173
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'xmltoJson' },
|
|
1174
|
+
['item()?'],
|
|
1175
|
+
'item()?',
|
|
1176
|
+
xmlToJson,
|
|
1161
1177
|
);
|
|
1162
1178
|
|
|
1163
1179
|
/*
|
|
@@ -1166,54 +1182,58 @@ const xml = '<json type="object"><given>Mark</given><family>Smith</family></json
|
|
|
1166
1182
|
console.log(xmlToJson(xml));
|
|
1167
1183
|
*/
|
|
1168
1184
|
|
|
1169
|
-
|
|
1170
1185
|
registerCustomXPathFunction(
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1186
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'depends' },
|
|
1187
|
+
['node()*'],
|
|
1188
|
+
'item()?',
|
|
1189
|
+
(_dynamicContext, nodes) =>
|
|
1190
|
+
// console.log('depends on : ', nodes[0]);
|
|
1191
|
+
nodes[0],
|
|
1177
1192
|
);
|
|
1178
1193
|
|
|
1179
1194
|
registerCustomXPathFunction(
|
|
1180
|
-
|
|
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
|
-
|
|
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
|
+
},
|
|
1209
1229
|
);
|
|
1210
1230
|
|
|
1211
|
-
function _propertyLookup(obj,path){
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
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('.'));
|
|
1217
1237
|
}
|
|
1218
1238
|
|
|
1219
1239
|
// Implement the XForms standard functions here.
|
|
@@ -1253,103 +1273,94 @@ registerXQueryModule(`
|
|
|
1253
1273
|
* @return {string}
|
|
1254
1274
|
*/
|
|
1255
1275
|
registerCustomXPathFunction(
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1276
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'base64encode' },
|
|
1277
|
+
['xs:string?'],
|
|
1278
|
+
'xs:string?',
|
|
1279
|
+
(_dynamicContext, string) => btoa(string),
|
|
1260
1280
|
);
|
|
1261
1281
|
|
|
1262
1282
|
registerCustomXPathFunction(
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1283
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'local-date' },
|
|
1284
|
+
[],
|
|
1285
|
+
'xs:string?',
|
|
1286
|
+
(_dynamicContext, _string) => new Date().toLocaleDateString(),
|
|
1267
1287
|
);
|
|
1268
1288
|
registerCustomXPathFunction(
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
return new Date().toLocaleString();
|
|
1274
|
-
},
|
|
1289
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'local-dateTime' },
|
|
1290
|
+
[],
|
|
1291
|
+
'xs:string?',
|
|
1292
|
+
(_dynamicContext, _string) => new Date().toLocaleString(),
|
|
1275
1293
|
);
|
|
1276
1294
|
registerCustomXPathFunction(
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1295
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'uri' },
|
|
1296
|
+
[],
|
|
1297
|
+
'xs:string?',
|
|
1298
|
+
(_dynamicContext, _string) => window.location.href,
|
|
1281
1299
|
);
|
|
1282
1300
|
registerCustomXPathFunction(
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1301
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'uri-fragment' },
|
|
1302
|
+
[],
|
|
1303
|
+
'xs:string?',
|
|
1304
|
+
(_dynamicContext, _arg) => window.location.hash,
|
|
1287
1305
|
);
|
|
1288
1306
|
registerCustomXPathFunction(
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1307
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'uri-host' },
|
|
1308
|
+
[],
|
|
1309
|
+
'xs:string?',
|
|
1310
|
+
(_dynamicContext, _arg) => window.location.host,
|
|
1293
1311
|
);
|
|
1294
1312
|
registerCustomXPathFunction(
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1313
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'uri-query' },
|
|
1314
|
+
[],
|
|
1315
|
+
'xs:string?',
|
|
1316
|
+
(_dynamicContext, _arg) => window.location.search,
|
|
1299
1317
|
);
|
|
1300
1318
|
registerCustomXPathFunction(
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
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
|
+
},
|
|
1308
1326
|
);
|
|
1309
1327
|
registerCustomXPathFunction(
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
return new URL(window.location.href).pathname;
|
|
1315
|
-
},
|
|
1328
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'uri-path' },
|
|
1329
|
+
[],
|
|
1330
|
+
'xs:string?',
|
|
1331
|
+
(_dynamicContext, _arg) => new URL(window.location.href).pathname,
|
|
1316
1332
|
);
|
|
1317
1333
|
registerCustomXPathFunction(
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
return window.location.port;
|
|
1323
|
-
},
|
|
1334
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'uri-port' },
|
|
1335
|
+
[],
|
|
1336
|
+
'xs:string?',
|
|
1337
|
+
(_dynamicContext, _arg) => window.location.port,
|
|
1324
1338
|
);
|
|
1325
1339
|
registerCustomXPathFunction(
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
},
|
|
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
|
+
},
|
|
1338
1351
|
);
|
|
1339
1352
|
registerCustomXPathFunction(
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
return new URL(window.location.href).protocol;
|
|
1345
|
-
},
|
|
1353
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'uri-scheme' },
|
|
1354
|
+
[],
|
|
1355
|
+
'xs:string?',
|
|
1356
|
+
(_dynamicContext, _arg) => new URL(window.location.href).protocol,
|
|
1346
1357
|
);
|
|
1347
1358
|
registerCustomXPathFunction(
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
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
|
+
},
|
|
1355
1366
|
);
|