@jinntec/fore 1.0.0-0 → 1.0.0-3
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 +17 -1
- package/dist/fore-all.js +10 -10
- package/dist/fore.js +1 -1
- package/index.js +8 -0
- package/package.json +4 -1
- package/resources/fore.css +111 -1
- package/src/DependencyNotifyingDomFacade.js +9 -1
- package/src/ForeElementMixin.js +25 -17
- package/src/actions/abstract-action.js +8 -7
- package/src/actions/fx-action.js +6 -1
- package/src/actions/fx-dispatch.js +10 -2
- package/src/actions/fx-hide.js +23 -0
- package/src/actions/fx-insert.js +14 -10
- package/src/actions/fx-send.js +1 -1
- package/src/actions/fx-setvalue.js +2 -4
- package/src/actions/fx-show.js +23 -0
- package/src/dep_graph.js +9 -0
- package/src/drawdown.js +172 -0
- package/src/fore.js +94 -3
- package/src/functions/fx-function.js +2 -2
- package/src/fx-bind.js +20 -17
- package/src/fx-fore.js +306 -33
- package/src/fx-instance.js +27 -6
- package/src/fx-model.js +182 -41
- package/src/fx-submission.js +29 -7
- package/src/fx-var.js +43 -0
- package/src/getInScopeContext.js +28 -7
- package/src/modelitem.js +1 -0
- package/src/ui/abstract-control.js +21 -6
- package/src/ui/fx-alert.js +16 -20
- package/src/ui/fx-container.js +9 -4
- package/src/ui/fx-control.js +76 -39
- package/src/ui/fx-dialog.js +68 -0
- package/src/ui/fx-inspector.js +44 -0
- package/src/ui/fx-items.js +120 -0
- package/src/ui/fx-output.js +77 -9
- package/src/ui/fx-repeat.js +86 -16
- package/src/ui/fx-repeatitem.js +16 -3
- package/src/ui/fx-trigger.js +3 -0
- package/src/xpath-evaluation.js +609 -267
- package/src/xpath-util.js +52 -12
package/src/xpath-evaluation.js
CHANGED
|
@@ -1,15 +1,586 @@
|
|
|
1
1
|
import {
|
|
2
2
|
evaluateXPath as fxEvaluateXPath,
|
|
3
|
+
evaluateXPathToBoolean as fxEvaluateXPathToBoolean,
|
|
3
4
|
evaluateXPathToFirstNode as fxEvaluateXPathToFirstNode,
|
|
4
5
|
evaluateXPathToNodes as fxEvaluateXPathToNodes,
|
|
5
|
-
evaluateXPathToBoolean as fxEvaluateXPathToBoolean,
|
|
6
|
-
evaluateXPathToString as fxEvaluateXPathToString,
|
|
7
6
|
evaluateXPathToNumber as fxEvaluateXPathToNumber,
|
|
7
|
+
evaluateXPathToString as fxEvaluateXPathToString,
|
|
8
|
+
evaluateXPathToStrings as fxEvaluateXPathToStrings,
|
|
9
|
+
parseScript,
|
|
8
10
|
registerCustomXPathFunction,
|
|
9
11
|
registerXQueryModule,
|
|
10
12
|
} from 'fontoxpath';
|
|
13
|
+
import { XPathUtil } from './xpath-util.js';
|
|
14
|
+
|
|
15
|
+
const XFORMS_NAMESPACE_URI = 'http://www.w3.org/2002/xforms';
|
|
16
|
+
|
|
17
|
+
const createdNamespaceResolversByXPathQueryAndNode = new Map();
|
|
18
|
+
|
|
19
|
+
function prettifyXml(source) {
|
|
20
|
+
const xmlDoc = new DOMParser().parseFromString(source, 'application/xml');
|
|
21
|
+
const xsltDoc = new DOMParser().parseFromString(
|
|
22
|
+
[
|
|
23
|
+
// describes how we want to modify the XML - indent everything
|
|
24
|
+
'<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">',
|
|
25
|
+
' <xsl:strip-space elements="*"/>',
|
|
26
|
+
' <xsl:template match="para[content-style][not(text())]">', // change to just text() to strip space in text nodes
|
|
27
|
+
' <xsl:value-of select="normalize-space(.)"/>',
|
|
28
|
+
' </xsl:template>',
|
|
29
|
+
' <xsl:template match="node()|@*">',
|
|
30
|
+
' <xsl:copy><xsl:apply-templates select="node()|@*"/></xsl:copy>',
|
|
31
|
+
' </xsl:template>',
|
|
32
|
+
' <xsl:output indent="yes"/>',
|
|
33
|
+
'</xsl:stylesheet>',
|
|
34
|
+
].join('\n'),
|
|
35
|
+
'application/xml',
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const xsltProcessor = new XSLTProcessor();
|
|
39
|
+
xsltProcessor.importStylesheet(xsltDoc);
|
|
40
|
+
const resultDoc = xsltProcessor.transformToDocument(xmlDoc);
|
|
41
|
+
const resultXml = new XMLSerializer().serializeToString(resultDoc);
|
|
42
|
+
return resultXml;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function getCachedNamespaceResolver(xpath, node) {
|
|
46
|
+
if (!createdNamespaceResolversByXPathQueryAndNode.has(xpath)) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
return createdNamespaceResolversByXPathQueryAndNode.get(xpath).get(node) || null;
|
|
50
|
+
}
|
|
51
|
+
function setCachedNamespaceResolver(xpath, node, resolver) {
|
|
52
|
+
if (!createdNamespaceResolversByXPathQueryAndNode.has(xpath)) {
|
|
53
|
+
return createdNamespaceResolversByXPathQueryAndNode.set(xpath, new Map());
|
|
54
|
+
}
|
|
55
|
+
return createdNamespaceResolversByXPathQueryAndNode.get(xpath).set(node, resolver);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const xhtmlNamespaceResolver = prefix => {
|
|
59
|
+
if (!prefix) {
|
|
60
|
+
return 'http://www.w3.org/1999/xhtml';
|
|
61
|
+
}
|
|
62
|
+
return undefined;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Resolve a namespace. Needs a namespace prefix and the element that is most closely related to the
|
|
67
|
+
* XPath in which the namespace is being resolved. The prefix will be resolved by using the
|
|
68
|
+
* ancestry of said element.
|
|
69
|
+
*
|
|
70
|
+
* It has two ways of doing so:
|
|
71
|
+
*
|
|
72
|
+
* - If the prefix is defined in an `xmlns:XXX="YYY"` namespace declaration, it will return 'YYY'.
|
|
73
|
+
* - If the prefix is the empty prefix and there is an `xpath-default-namespace="YYY"` attribute in the
|
|
74
|
+
* - ancestry, that attribute will be used and 'YYY' will be returned
|
|
75
|
+
*
|
|
76
|
+
* @param {Node} contextElement The element that is most closely related with the XPath in which this prefix is resolved.
|
|
77
|
+
* @param {string} prefix The prefix to resolve
|
|
78
|
+
*/
|
|
79
|
+
function createNamespaceResolver(xpathQuery, formElement) {
|
|
80
|
+
const cachedResolver = getCachedNamespaceResolver(xpathQuery, formElement);
|
|
81
|
+
if (cachedResolver) {
|
|
82
|
+
return cachedResolver;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Make namespace resolving use the `instance` element that is related to here
|
|
86
|
+
const xmlDocument = new DOMParser().parseFromString('<xml />', 'text/xml');
|
|
87
|
+
|
|
88
|
+
const xpathAST = parseScript(xpathQuery, {}, xmlDocument);
|
|
89
|
+
let instanceReferences = fxEvaluateXPathToStrings(
|
|
90
|
+
`descendant::xqx:functionCallExpr
|
|
91
|
+
[xqx:functionName = "instance"]
|
|
92
|
+
/xqx:arguments
|
|
93
|
+
/xqx:stringConstantExpr
|
|
94
|
+
/xqx:value`,
|
|
95
|
+
xpathAST,
|
|
96
|
+
null,
|
|
97
|
+
{},
|
|
98
|
+
{
|
|
99
|
+
namespaceResolver: prefix =>
|
|
100
|
+
prefix === 'xqx' ? 'http://www.w3.org/2005/XQueryX' : undefined,
|
|
101
|
+
},
|
|
102
|
+
);
|
|
103
|
+
if (instanceReferences.length === 0) {
|
|
104
|
+
// No instance functions. Look up further in the hierarchy to see if we can deduce the intended context from there
|
|
105
|
+
const ancestorComponent = fxEvaluateXPathToFirstNode('ancestor::*[@ref][1]', formElement);
|
|
106
|
+
if (ancestorComponent) {
|
|
107
|
+
const resolver = createNamespaceResolver(
|
|
108
|
+
ancestorComponent.getAttribute('ref'),
|
|
109
|
+
ancestorComponent,
|
|
110
|
+
);
|
|
111
|
+
setCachedNamespaceResolver(xpathQuery, formElement, resolver);
|
|
112
|
+
return resolver;
|
|
113
|
+
}
|
|
114
|
+
// Nothing found: let's just assume we're supposed to use the `default` instance
|
|
115
|
+
instanceReferences = ['default'];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (instanceReferences.length === 1) {
|
|
119
|
+
// console.log(`resolving ${xpathQuery} with ${instanceReferences[0]}`);
|
|
120
|
+
let instance;
|
|
121
|
+
if (instanceReferences[0] === 'default') {
|
|
122
|
+
const actualForeElement = fxEvaluateXPathToFirstNode(
|
|
123
|
+
'ancestor-or-self::fx-fore',
|
|
124
|
+
formElement,
|
|
125
|
+
null,
|
|
126
|
+
null,
|
|
127
|
+
{ namespaceResolver: xhtmlNamespaceResolver },
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
instance = actualForeElement && actualForeElement.querySelector('fx-instance');
|
|
131
|
+
} else {
|
|
132
|
+
instance = resolveId(instanceReferences[0], formElement, 'fx-instance');
|
|
133
|
+
}
|
|
134
|
+
if (instance && instance.hasAttribute('xpath-default-namespace')) {
|
|
135
|
+
const xpathDefaultNamespace = instance.getAttribute('xpath-default-namespace');
|
|
136
|
+
/*
|
|
137
|
+
console.log(
|
|
138
|
+
`Resolving the xpath ${xpathQuery} with the default namespace set to ${xpathDefaultNamespace}`,
|
|
139
|
+
);
|
|
140
|
+
*/
|
|
141
|
+
const resolveNamespacePrefix = prefix => {
|
|
142
|
+
if (!prefix) {
|
|
143
|
+
return xpathDefaultNamespace;
|
|
144
|
+
}
|
|
145
|
+
return undefined;
|
|
146
|
+
};
|
|
147
|
+
setCachedNamespaceResolver(xpathQuery, formElement, resolveNamespacePrefix);
|
|
148
|
+
return resolveNamespacePrefix;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (instanceReferences.length > 1) {
|
|
152
|
+
console.warn(
|
|
153
|
+
`More than one instance is used in the query "${xpathQuery}". The default namespace resolving will be used`,
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const xpathDefaultNamespace =
|
|
158
|
+
fxEvaluateXPathToString('ancestor-or-self::*/@xpath-default-namespace[last()]', formElement) ||
|
|
159
|
+
'';
|
|
160
|
+
|
|
161
|
+
const resolveNamespacePrefix = function resolveNamespacePrefix(prefix) {
|
|
162
|
+
if (prefix === '') {
|
|
163
|
+
return xpathDefaultNamespace;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Note: ideally we should use Node#lookupNamespaceURI. However, the nodes we are passed are
|
|
167
|
+
// XML. The best we can do is emulate the `xmlns:xxx` namespace declarations by regarding them as
|
|
168
|
+
// attributes. Which they technically ARE NOT!
|
|
169
|
+
|
|
170
|
+
return fxEvaluateXPathToString(
|
|
171
|
+
'ancestor-or-self::*/@*[name() = "xmlns:" || $prefix][last()]',
|
|
172
|
+
formElement,
|
|
173
|
+
null,
|
|
174
|
+
{ prefix },
|
|
175
|
+
);
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
setCachedNamespaceResolver(xpathQuery, formElement, resolveNamespacePrefix);
|
|
179
|
+
return resolveNamespacePrefix;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function createNamespaceResolverForNode(query, contextNode, formElement) {
|
|
183
|
+
if (((contextNode && contextNode.ownerDocument) || contextNode) === window.document) {
|
|
184
|
+
// Running a query on the HTML DOM. Don't bother resolving namespaces in any other way
|
|
185
|
+
return xhtmlNamespaceResolver;
|
|
186
|
+
}
|
|
187
|
+
return createNamespaceResolver(query, formElement);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Implementation of the functionNameResolver passed to FontoXPath to
|
|
192
|
+
* redirect function resolving for unprefixed functions to either the fn or the xf namespace
|
|
193
|
+
*/
|
|
194
|
+
// eslint-disable-next-line no-unused-vars
|
|
195
|
+
function functionNameResolver({ prefix, localName }, _arity) {
|
|
196
|
+
switch (localName) {
|
|
197
|
+
// TODO: put the full XForms library functions set here
|
|
198
|
+
case 'context':
|
|
199
|
+
case 'base64encode':
|
|
200
|
+
case 'boolean-from-string':
|
|
201
|
+
case 'current':
|
|
202
|
+
case 'depends':
|
|
203
|
+
case 'event':
|
|
204
|
+
case 'index':
|
|
205
|
+
case 'instance':
|
|
206
|
+
case 'log':
|
|
207
|
+
case 'logtree':
|
|
208
|
+
return { namespaceURI: XFORMS_NAMESPACE_URI, localName };
|
|
209
|
+
default:
|
|
210
|
+
if (prefix === '' || prefix === 'fn') {
|
|
211
|
+
return { namespaceURI: 'http://www.w3.org/2005/xpath-functions', localName };
|
|
212
|
+
}
|
|
213
|
+
if (prefix === 'local') {
|
|
214
|
+
return { namespaceURI: 'http://www.w3.org/2005/xquery-local-functions', localName };
|
|
215
|
+
}
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Get the variables in scope of the form element. These are the values of the variables that
|
|
222
|
+
* logically precede the formElement that declares the XPath
|
|
223
|
+
*
|
|
224
|
+
* @param {Node} formElement The element that declares the XPath
|
|
225
|
+
*
|
|
226
|
+
* @return {Object} A key-value mapping of the variables
|
|
227
|
+
*/
|
|
228
|
+
function getVariablesInScope(formElement) {
|
|
229
|
+
let closestActualFormElement = formElement;
|
|
230
|
+
while (closestActualFormElement && !('inScopeVariables' in closestActualFormElement)) {
|
|
231
|
+
closestActualFormElement = closestActualFormElement.parentNode;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (!closestActualFormElement) {
|
|
235
|
+
return {};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const variables = {};
|
|
239
|
+
for (const key of closestActualFormElement.inScopeVariables.keys()) {
|
|
240
|
+
const varElement = closestActualFormElement.inScopeVariables.get(key);
|
|
241
|
+
variables[key] = varElement.value;
|
|
242
|
+
}
|
|
243
|
+
return variables;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Evaluate an XPath to _any_ type. When possible, prefer to use any other function to ensure the
|
|
248
|
+
* type of the output is more predictable.
|
|
249
|
+
*
|
|
250
|
+
* @param {string} xpath The XPath to run
|
|
251
|
+
* @param {Node} contextNode The start of the XPath
|
|
252
|
+
* @param {{parentNode}|ForeElementMixin} formElement The form element associated to the XPath
|
|
253
|
+
*/
|
|
254
|
+
export function evaluateXPath(xpath, contextNode, formElement, variables = {}) {
|
|
255
|
+
const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
|
|
256
|
+
const variablesInScope = getVariablesInScope(formElement);
|
|
257
|
+
|
|
258
|
+
return fxEvaluateXPath(
|
|
259
|
+
xpath,
|
|
260
|
+
contextNode,
|
|
261
|
+
null,
|
|
262
|
+
{ ...variablesInScope, ...variables },
|
|
263
|
+
'xs:anyType',
|
|
264
|
+
{
|
|
265
|
+
currentContext: { formElement, variables },
|
|
266
|
+
moduleImports: {
|
|
267
|
+
xf: XFORMS_NAMESPACE_URI,
|
|
268
|
+
},
|
|
269
|
+
functionNameResolver,
|
|
270
|
+
namespaceResolver,
|
|
271
|
+
},
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Evaluate an XPath to the first Node
|
|
277
|
+
*
|
|
278
|
+
* @param {string} xpath The XPath to run
|
|
279
|
+
* @param {Node} contextNode The start of the XPath
|
|
280
|
+
* @param {Node} formElement The form element associated to the XPath
|
|
281
|
+
* @return {Node} The first node found by the XPath
|
|
282
|
+
*/
|
|
283
|
+
export function evaluateXPathToFirstNode(xpath, contextNode, formElement) {
|
|
284
|
+
const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
|
|
285
|
+
const variablesInScope = getVariablesInScope(formElement);
|
|
286
|
+
return fxEvaluateXPathToFirstNode(xpath, contextNode, null, variablesInScope, {
|
|
287
|
+
defaultFunctionNamespaceURI: XFORMS_NAMESPACE_URI,
|
|
288
|
+
moduleImports: {
|
|
289
|
+
xf: XFORMS_NAMESPACE_URI,
|
|
290
|
+
},
|
|
291
|
+
currentContext: { formElement },
|
|
292
|
+
namespaceResolver,
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Evaluate an XPath to all nodes
|
|
298
|
+
*
|
|
299
|
+
* @param {string} xpath The XPath to run
|
|
300
|
+
* @param {Node} contextNode The start of the XPath
|
|
301
|
+
* @param {Node} formElement The form element associated to the XPath
|
|
302
|
+
* @return {Node[]} All nodes
|
|
303
|
+
*/
|
|
304
|
+
export function evaluateXPathToNodes(xpath, contextNode, formElement) {
|
|
305
|
+
const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
|
|
306
|
+
const variablesInScope = getVariablesInScope(formElement);
|
|
307
|
+
|
|
308
|
+
return fxEvaluateXPathToNodes(xpath, contextNode, null, variablesInScope, {
|
|
309
|
+
currentContext: { formElement },
|
|
310
|
+
functionNameResolver,
|
|
311
|
+
moduleImports: {
|
|
312
|
+
xf: XFORMS_NAMESPACE_URI,
|
|
313
|
+
},
|
|
314
|
+
namespaceResolver,
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Evaluate an XPath to a boolean
|
|
320
|
+
*
|
|
321
|
+
* @param {string} xpath The XPath to run
|
|
322
|
+
* @param {Node} contextNode The start of the XPath
|
|
323
|
+
* @param {Node} formElement The form element associated to the XPath
|
|
324
|
+
* @return {boolean}
|
|
325
|
+
*/
|
|
326
|
+
export function evaluateXPathToBoolean(xpath, contextNode, formElement) {
|
|
327
|
+
const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
|
|
328
|
+
const variablesInScope = getVariablesInScope(formElement);
|
|
329
|
+
|
|
330
|
+
return fxEvaluateXPathToBoolean(xpath, contextNode, null, variablesInScope, {
|
|
331
|
+
currentContext: { formElement },
|
|
332
|
+
functionNameResolver,
|
|
333
|
+
moduleImports: {
|
|
334
|
+
xf: XFORMS_NAMESPACE_URI,
|
|
335
|
+
},
|
|
336
|
+
namespaceResolver,
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Evaluate an XPath to a string
|
|
342
|
+
*
|
|
343
|
+
* @param {string} xpath The XPath to run
|
|
344
|
+
* @param {Node} contextNode The start of the XPath
|
|
345
|
+
* @param {Node} formElement The form element associated to the XPath
|
|
346
|
+
* @param {DomFacade} [domFacade=null] A DomFacade is used in bindings to intercept DOM
|
|
347
|
+
* access. This is used to determine dependencies between bind elements.
|
|
348
|
+
* @param {Node} formElement The element where the XPath is defined: used for namespace resolving
|
|
349
|
+
* @return {string}
|
|
350
|
+
*/
|
|
351
|
+
export function evaluateXPathToString(
|
|
352
|
+
xpath,
|
|
353
|
+
contextNode,
|
|
354
|
+
formElement,
|
|
355
|
+
domFacade = null,
|
|
356
|
+
namespaceReferenceNode = formElement,
|
|
357
|
+
) {
|
|
358
|
+
const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
|
|
359
|
+
const variablesInScope = getVariablesInScope(formElement);
|
|
360
|
+
|
|
361
|
+
return fxEvaluateXPathToString(xpath, contextNode, domFacade, variablesInScope, {
|
|
362
|
+
currentContext: { formElement },
|
|
363
|
+
functionNameResolver,
|
|
364
|
+
moduleImports: {
|
|
365
|
+
xf: XFORMS_NAMESPACE_URI,
|
|
366
|
+
},
|
|
367
|
+
namespaceResolver,
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Evaluate an XPath to a set of strings
|
|
373
|
+
*
|
|
374
|
+
* @param {string} xpath The XPath to run
|
|
375
|
+
* @param {Node} contextNode The start of the XPath
|
|
376
|
+
* @param {Node} formElement The form element associated to the XPath
|
|
377
|
+
* @param {DomFacade} [domFacade=null] A DomFacade is used in bindings to intercept DOM
|
|
378
|
+
* access. This is used to determine dependencies between bind elements.
|
|
379
|
+
* @param {Node} formElement The element where the XPath is defined: used for namespace resolving
|
|
380
|
+
* @return {string}
|
|
381
|
+
*/
|
|
382
|
+
export function evaluateXPathToStrings(
|
|
383
|
+
xpath,
|
|
384
|
+
contextNode,
|
|
385
|
+
formElement,
|
|
386
|
+
domFacade = null,
|
|
387
|
+
namespaceReferenceNode = formElement,
|
|
388
|
+
) {
|
|
389
|
+
const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
|
|
390
|
+
return fxEvaluateXPathToStrings(
|
|
391
|
+
xpath,
|
|
392
|
+
contextNode,
|
|
393
|
+
domFacade,
|
|
394
|
+
{},
|
|
395
|
+
|
|
396
|
+
{
|
|
397
|
+
currentContext: { formElement },
|
|
398
|
+
functionNameResolver,
|
|
399
|
+
moduleImports: {
|
|
400
|
+
xf: XFORMS_NAMESPACE_URI,
|
|
401
|
+
},
|
|
402
|
+
namespaceResolver,
|
|
403
|
+
},
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Evaluate an XPath to a number
|
|
409
|
+
*
|
|
410
|
+
* @param {string} xpath The XPath to run
|
|
411
|
+
* @param {Node} contextNode The start of the XPath
|
|
412
|
+
* @param {Node} formElement The form element associated to the XPath
|
|
413
|
+
* @param {DomFacade} [domFacade=null] A DomFacade is used in bindings to intercept DOM
|
|
414
|
+
* @param {Node} formElement The element where the XPath is defined: used for namespace resolving
|
|
415
|
+
* access. This is used to determine dependencies between bind elements.
|
|
416
|
+
* @return {Number}
|
|
417
|
+
*/
|
|
418
|
+
export function evaluateXPathToNumber(
|
|
419
|
+
xpath,
|
|
420
|
+
contextNode,
|
|
421
|
+
formElement,
|
|
422
|
+
domFacade = null,
|
|
423
|
+
namespaceReferenceNode = formElement,
|
|
424
|
+
) {
|
|
425
|
+
const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
|
|
426
|
+
const variablesInScope = getVariablesInScope(formElement);
|
|
427
|
+
|
|
428
|
+
return fxEvaluateXPathToNumber(xpath, contextNode, domFacade, variablesInScope, {
|
|
429
|
+
currentContext: { formElement },
|
|
430
|
+
functionNameResolver,
|
|
431
|
+
moduleImports: {
|
|
432
|
+
xf: XFORMS_NAMESPACE_URI,
|
|
433
|
+
},
|
|
434
|
+
namespaceResolver,
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Resolve an id in scope. Behaves like the algorithm defined on https://www.w3.org/community/xformsusers/wiki/XForms_2.0#idref-resolve
|
|
440
|
+
*/
|
|
441
|
+
export function resolveId(id, sourceObject, nodeName = null) {
|
|
442
|
+
const allMatchingTargetObjects = fxEvaluateXPathToNodes(
|
|
443
|
+
'outermost(ancestor-or-self::fx-fore[1]/(descendant::xf-fore|descendant::*[@id = $id]))[not(self::fx-fore)]',
|
|
444
|
+
sourceObject,
|
|
445
|
+
null,
|
|
446
|
+
{ id },
|
|
447
|
+
{ namespaceResolver: xhtmlNamespaceResolver },
|
|
448
|
+
);
|
|
449
|
+
|
|
450
|
+
if (allMatchingTargetObjects.length === 0) {
|
|
451
|
+
return null;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
if (
|
|
455
|
+
allMatchingTargetObjects.length === 1 &&
|
|
456
|
+
fxEvaluateXPathToBoolean(
|
|
457
|
+
'(ancestor::fx-fore | ancestor::fx-repeat)[last()]/self::fx-fore',
|
|
458
|
+
allMatchingTargetObjects[0],
|
|
459
|
+
null,
|
|
460
|
+
null,
|
|
461
|
+
{ namespaceResolver: xhtmlNamespaceResolver },
|
|
462
|
+
)
|
|
463
|
+
) {
|
|
464
|
+
// If the target element is not repeated, then the search for the target object is trivial since
|
|
465
|
+
// there is only one associated with the target element that bears the matching ID. This is true
|
|
466
|
+
// regardless of whether or not the source object is repeated. However, if the target element is
|
|
467
|
+
// repeated, then additional information must be used to help select a target object from among
|
|
468
|
+
// those associated with the identified target element.
|
|
469
|
+
const targetObject = allMatchingTargetObjects[0];
|
|
470
|
+
if (nodeName && targetObject.localName !== nodeName) {
|
|
471
|
+
return null;
|
|
472
|
+
}
|
|
473
|
+
return targetObject;
|
|
474
|
+
}
|
|
11
475
|
|
|
12
|
-
|
|
476
|
+
// SPEC:
|
|
477
|
+
|
|
478
|
+
// 12.2.1 References to Elements within a repeat Element
|
|
479
|
+
|
|
480
|
+
// When the target element that is identified by the IDREF of a source object has one or more
|
|
481
|
+
// repeat elements as ancestors, then the set of ancestor repeats are partitioned into two
|
|
482
|
+
// subsets, those in common with the source element and those that are not in common. Any ancestor
|
|
483
|
+
// repeat elements of the target element not in common with the source element are descendants of
|
|
484
|
+
// the repeat elements that the source and target element have in common, if any.
|
|
485
|
+
|
|
486
|
+
// For the repeat elements that are in common, the desired target object exists in the same set of
|
|
487
|
+
// run-time objects that contains the source object. Then, for each ancestor repeat of the target
|
|
488
|
+
// element that is not in common with the source element, the current index of the repeat
|
|
489
|
+
// determines the set of run-time objects that contains the desired target object.
|
|
490
|
+
for (const ancestorRepeatItem of fxEvaluateXPathToNodes(
|
|
491
|
+
'ancestor::fx-repeatitem => reverse()',
|
|
492
|
+
sourceObject,
|
|
493
|
+
null,
|
|
494
|
+
null,
|
|
495
|
+
{ namespaceResolver: xhtmlNamespaceResolver },
|
|
496
|
+
)) {
|
|
497
|
+
const foundTargetObjects = allMatchingTargetObjects.filter(to =>
|
|
498
|
+
ancestorRepeatItem.contains(to),
|
|
499
|
+
);
|
|
500
|
+
switch (foundTargetObjects.length) {
|
|
501
|
+
case 0:
|
|
502
|
+
// Nothing found: ignore
|
|
503
|
+
break;
|
|
504
|
+
case 1: {
|
|
505
|
+
// A single one is found: the target object is directly in a common repeat
|
|
506
|
+
const targetObject = foundTargetObjects[0];
|
|
507
|
+
if (nodeName && targetObject.localName !== nodeName) {
|
|
508
|
+
return null;
|
|
509
|
+
}
|
|
510
|
+
return targetObject;
|
|
511
|
+
}
|
|
512
|
+
default: {
|
|
513
|
+
// Multiple target objects are found: they are in a repeat that is not common with the source object
|
|
514
|
+
// 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
|
|
515
|
+
const targetObject = foundTargetObjects.find(to =>
|
|
516
|
+
fxEvaluateXPathToNodes(
|
|
517
|
+
'every $ancestor of ancestor::fx-repeatitem satisfies $ancestor is $ancestor/../child::fx-repeatitem[../@repeat-index]',
|
|
518
|
+
to,
|
|
519
|
+
null,
|
|
520
|
+
{},
|
|
521
|
+
),
|
|
522
|
+
);
|
|
523
|
+
if (!targetObject) {
|
|
524
|
+
// Nothing valid found for whatever reason. This might be something dynamic?
|
|
525
|
+
return null;
|
|
526
|
+
}
|
|
527
|
+
if (nodeName && targetObject.localName !== nodeName) {
|
|
528
|
+
return null;
|
|
529
|
+
}
|
|
530
|
+
return targetObject;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
// We found no target objects in common repeats. The id is unresolvable
|
|
535
|
+
return null;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
const contextFunction = (dynamicContext, string) => {
|
|
539
|
+
const caller = dynamicContext.currentContext.formElement;
|
|
540
|
+
if (string) {
|
|
541
|
+
const instance = resolveId(string, caller);
|
|
542
|
+
if (instance) {
|
|
543
|
+
if (instance.nodeName === 'FX-REPEAT') {
|
|
544
|
+
const { nodeset } = instance;
|
|
545
|
+
for (let parent = caller; parent; parent = parent.parentNode) {
|
|
546
|
+
if (parent.parentNode === instance) {
|
|
547
|
+
const offset = Array.from(parent.parentNode.children).indexOf(parent);
|
|
548
|
+
return nodeset[offset];
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
return instance.nodeset;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
const parent = XPathUtil.getParentBindingElement(caller);
|
|
556
|
+
const p = caller.nodeName;
|
|
557
|
+
// const p = dynamicContext.domFacade.getParentElement();
|
|
558
|
+
|
|
559
|
+
if (parent) return parent;
|
|
560
|
+
return caller.getInScopeContext();
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* @param id as string
|
|
565
|
+
* @return instance data for given id serialized to string.
|
|
566
|
+
*/
|
|
567
|
+
registerCustomXPathFunction(
|
|
568
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'context' },
|
|
569
|
+
[],
|
|
570
|
+
'item()?',
|
|
571
|
+
contextFunction,
|
|
572
|
+
);
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* @param id as string
|
|
576
|
+
* @return instance data for given id serialized to string.
|
|
577
|
+
*/
|
|
578
|
+
registerCustomXPathFunction(
|
|
579
|
+
{ namespaceURI: XFORMS_NAMESPACE_URI, localName: 'context' },
|
|
580
|
+
['xs:string'],
|
|
581
|
+
'item()?',
|
|
582
|
+
contextFunction,
|
|
583
|
+
);
|
|
13
584
|
|
|
14
585
|
/**
|
|
15
586
|
* @param id as string
|
|
@@ -21,10 +592,15 @@ registerCustomXPathFunction(
|
|
|
21
592
|
'xs:string?',
|
|
22
593
|
(dynamicContext, string) => {
|
|
23
594
|
const { formElement } = dynamicContext.currentContext;
|
|
24
|
-
const instance = formElement
|
|
595
|
+
const instance = resolveId(string, formElement, 'fx-instance');
|
|
25
596
|
if (instance) {
|
|
26
|
-
|
|
27
|
-
|
|
597
|
+
if (instance.getAttribute('type') === 'json') {
|
|
598
|
+
console.warn('log() does not work for JSON yet');
|
|
599
|
+
// return JSON.stringify(instance.getDefaultContext());
|
|
600
|
+
} else {
|
|
601
|
+
const def = new XMLSerializer().serializeToString(instance.getDefaultContext());
|
|
602
|
+
return prettifyXml(def);
|
|
603
|
+
}
|
|
28
604
|
}
|
|
29
605
|
return null;
|
|
30
606
|
},
|
|
@@ -92,13 +668,14 @@ registerCustomXPathFunction(
|
|
|
92
668
|
'element()?',
|
|
93
669
|
(dynamicContext, string) => {
|
|
94
670
|
const { formElement } = dynamicContext.currentContext;
|
|
95
|
-
const instance = formElement
|
|
671
|
+
const instance = resolveId(string, formElement, 'fx-instance');
|
|
672
|
+
|
|
96
673
|
if (instance) {
|
|
97
674
|
// const def = new XMLSerializer().serializeToString(instance.getDefaultContext());
|
|
98
675
|
// const def = JSON.stringify(instance.getDefaultContext());
|
|
99
676
|
|
|
100
|
-
const
|
|
101
|
-
|
|
677
|
+
const treeDiv = document.createElement('div');
|
|
678
|
+
treeDiv.setAttribute('class', 'logtree');
|
|
102
679
|
// const datatree = buildTree(tree,instance.getDefaultContext());
|
|
103
680
|
// return tree.appendChild(datatree);
|
|
104
681
|
// return buildTree(root,instance.getDefaultContext());;
|
|
@@ -107,19 +684,15 @@ registerCustomXPathFunction(
|
|
|
107
684
|
if (logtree) {
|
|
108
685
|
logtree.parentNode.removeChild(logtree);
|
|
109
686
|
}
|
|
110
|
-
|
|
687
|
+
const tree = buildTree(treeDiv, instance.getDefaultContext());
|
|
688
|
+
if (tree) {
|
|
689
|
+
form.appendChild(tree);
|
|
690
|
+
}
|
|
111
691
|
}
|
|
112
692
|
return null;
|
|
113
693
|
},
|
|
114
694
|
);
|
|
115
695
|
|
|
116
|
-
const xhtmlNamespaceResolver = prefix => {
|
|
117
|
-
if (!prefix) {
|
|
118
|
-
return 'http://www.w3.org/1999/xhtml';
|
|
119
|
-
}
|
|
120
|
-
return undefined;
|
|
121
|
-
};
|
|
122
|
-
|
|
123
696
|
const instance = (dynamicContext, string) => {
|
|
124
697
|
// Spec: https://www.w3.org/TR/xforms-xpath/#The_XForms_Function_Library#The_instance.28.29_Function
|
|
125
698
|
// TODO: handle no string passed (null will be passed instead)
|
|
@@ -136,7 +709,7 @@ const instance = (dynamicContext, string) => {
|
|
|
136
709
|
// console.log('fnInstance string: ', string);
|
|
137
710
|
|
|
138
711
|
const inst = string
|
|
139
|
-
? formElement
|
|
712
|
+
? resolveId(string, formElement, 'fx-instance')
|
|
140
713
|
: formElement.querySelector(`fx-instance`);
|
|
141
714
|
|
|
142
715
|
// const def = instance.getInstanceData();
|
|
@@ -155,13 +728,16 @@ registerCustomXPathFunction(
|
|
|
155
728
|
'xs:integer?',
|
|
156
729
|
(dynamicContext, string) => {
|
|
157
730
|
const { formElement } = dynamicContext.currentContext;
|
|
158
|
-
|
|
731
|
+
if (string === null) {
|
|
732
|
+
return 1;
|
|
733
|
+
}
|
|
734
|
+
const repeat = resolveId(string, formElement, 'fx-repeat');
|
|
159
735
|
|
|
160
736
|
// const def = instance.getInstanceData();
|
|
161
737
|
if (repeat) {
|
|
162
738
|
return repeat.getAttribute('index');
|
|
163
739
|
}
|
|
164
|
-
return 1;
|
|
740
|
+
return Number(1);
|
|
165
741
|
},
|
|
166
742
|
);
|
|
167
743
|
|
|
@@ -215,260 +791,26 @@ registerXQueryModule(`
|
|
|
215
791
|
|
|
216
792
|
// How to run XQUERY:
|
|
217
793
|
/**
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
794
|
+
registerXQueryModule(`
|
|
795
|
+
module namespace my-custom-namespace = "my-custom-uri";
|
|
796
|
+
(:~
|
|
797
|
+
Insert attribute somewhere
|
|
798
|
+
~:)
|
|
799
|
+
declare %public %updating function my-custom-namespace:do-something ($ele as element()) as xs:boolean {
|
|
224
800
|
if ($ele/@done) then false() else
|
|
225
801
|
(insert node
|
|
226
802
|
attribute done {"true"}
|
|
227
803
|
into $ele, true())
|
|
228
804
|
};
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
console.log(pendingUpdatesAndXdmValue.xdmValue); // this is true or false, see function
|
|
235
|
-
|
|
236
|
-
executePendingUpdateList(pendingUpdatesAndXdmValue.pendingUpdateList, null, null, null);
|
|
237
|
-
*/
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Implementation of the functionNameResolver passed to FontoXPath to
|
|
241
|
-
* redirect function resolving for unprefixed functions to either the fn or the xf namespace
|
|
242
|
-
*/
|
|
243
|
-
// eslint-disable-next-line no-unused-vars
|
|
244
|
-
function functionNameResolver({ prefix, localName }, _arity) {
|
|
245
|
-
switch (localName) {
|
|
246
|
-
// TODO: put the full XForms library functions set here
|
|
247
|
-
case 'base64encode':
|
|
248
|
-
case 'boolean-from-string':
|
|
249
|
-
case 'depends':
|
|
250
|
-
case 'event':
|
|
251
|
-
case 'index':
|
|
252
|
-
case 'instance':
|
|
253
|
-
case 'log':
|
|
254
|
-
case 'logtree':
|
|
255
|
-
return { namespaceURI: XFORMS_NAMESPACE_URI, localName };
|
|
256
|
-
default:
|
|
257
|
-
if (prefix === '' || prefix === 'fn') {
|
|
258
|
-
return { namespaceURI: 'http://www.w3.org/2005/xpath-functions', localName };
|
|
259
|
-
}
|
|
260
|
-
if (prefix === 'local') {
|
|
261
|
-
return { namespaceURI: 'http://www.w3.org/2005/xquery-local-functions', localName };
|
|
262
|
-
}
|
|
263
|
-
return null;
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* Resolve a namespace. Needs a namespace prefix and the element that is most closely related to the
|
|
269
|
-
* XPath in which the namespace is being resolved. The prefix will be resolved by using the
|
|
270
|
-
* ancestry of said element.
|
|
271
|
-
*
|
|
272
|
-
* It has two ways of doing so:
|
|
273
|
-
*
|
|
274
|
-
* - If the prefix is defined in an `xmlns:XXX="YYY"` namespace declaration, it will return 'YYY'.
|
|
275
|
-
* - If the prefix is the empty prefix and there is an `xpath-default-namespace="YYY"` attribute in the
|
|
276
|
-
* - ancestry, that attribute will be used and 'YYY' will be returned
|
|
277
|
-
*
|
|
278
|
-
* @param {Node} contextElement The element that is most closely related with the XPath in which this prefix is resolved.
|
|
279
|
-
* @param {string} prefix The prefix to resolve
|
|
280
|
-
*/
|
|
281
|
-
function resolveNamespacePrefix(contextElement, prefix) {
|
|
282
|
-
if (prefix === 'xhtml') {
|
|
283
|
-
return 'http://www.w3.org/1999/xhtml';
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
if (prefix === '') {
|
|
287
|
-
return (
|
|
288
|
-
fxEvaluateXPathToString(
|
|
289
|
-
'ancestor-or-self::*/@xpath-default-namespace[last()]',
|
|
290
|
-
contextElement,
|
|
291
|
-
) || null
|
|
292
|
-
);
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// Note: ideally we should use Node#lookupNamespaceURI. However, the nodes we are passed are
|
|
296
|
-
// XML. The best we can do is emulate the `xmlns:xxx` namespace declarations by regarding them as
|
|
297
|
-
// attributes. Which they technically ARE NOT!
|
|
298
|
-
|
|
299
|
-
const result = fxEvaluateXPathToString(
|
|
300
|
-
'ancestor-or-self::*/@*[name() = "xmlns:" || $prefix][last()]',
|
|
301
|
-
contextElement,
|
|
302
|
-
null,
|
|
303
|
-
{ prefix },
|
|
304
|
-
);
|
|
305
|
-
|
|
306
|
-
console.log('result', result);
|
|
307
|
-
return result;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
/**
|
|
311
|
-
* Evaluate an XPath to _any_ type. When possible, prefer to use any other function to ensure the
|
|
312
|
-
* type of the output is more predictable.
|
|
313
|
-
*
|
|
314
|
-
* @param {string} xpath The XPath to run
|
|
315
|
-
* @param {Node} contextNode The start of the XPath
|
|
316
|
-
* @param {{parentNode}|ForeElementMixin} formElement The form element associated to the XPath
|
|
317
|
-
*/
|
|
318
|
-
export function evaluateXPath(xpath, contextNode, formElement, variables = {}) {
|
|
319
|
-
return fxEvaluateXPath(xpath, contextNode, null, variables, 'xs:anyType', {
|
|
320
|
-
currentContext: { formElement, variables },
|
|
321
|
-
moduleImports: {
|
|
322
|
-
xf: XFORMS_NAMESPACE_URI,
|
|
323
|
-
},
|
|
324
|
-
functionNameResolver,
|
|
325
|
-
namespaceResolver: prefix => resolveNamespacePrefix(formElement, prefix),
|
|
326
|
-
});
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
/**
|
|
330
|
-
* Evaluate an XPath to the first Node
|
|
331
|
-
*
|
|
332
|
-
* @param {string} xpath The XPath to run
|
|
333
|
-
* @param {Node} contextNode The start of the XPath
|
|
334
|
-
* @param {Node} formElement The form element associated to the XPath
|
|
335
|
-
* @return {Node} The first node found by the XPath
|
|
336
|
-
*/
|
|
337
|
-
export function evaluateXPathToFirstNode(xpath, contextNode, formElement) {
|
|
338
|
-
return fxEvaluateXPathToFirstNode(
|
|
339
|
-
xpath,
|
|
340
|
-
contextNode,
|
|
341
|
-
null,
|
|
342
|
-
{},
|
|
343
|
-
{
|
|
344
|
-
namespaceResolver: prefix => resolveNamespacePrefix(formElement, prefix),
|
|
345
|
-
defaultFunctionNamespaceURI: XFORMS_NAMESPACE_URI,
|
|
346
|
-
moduleImports: {
|
|
347
|
-
xf: XFORMS_NAMESPACE_URI,
|
|
348
|
-
},
|
|
349
|
-
currentContext: { formElement },
|
|
350
|
-
},
|
|
351
|
-
);
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
/**
|
|
355
|
-
* Evaluate an XPath to all nodes
|
|
356
|
-
*
|
|
357
|
-
* @param {string} xpath The XPath to run
|
|
358
|
-
* @param {Node} contextNode The start of the XPath
|
|
359
|
-
* @param {Node} formElement The form element associated to the XPath
|
|
360
|
-
* @return {Node[]} All nodes
|
|
361
|
-
*/
|
|
362
|
-
export function evaluateXPathToNodes(xpath, contextNode, formElement) {
|
|
363
|
-
return fxEvaluateXPathToNodes(
|
|
364
|
-
xpath,
|
|
365
|
-
contextNode,
|
|
366
|
-
null,
|
|
367
|
-
{},
|
|
368
|
-
{
|
|
369
|
-
currentContext: { formElement },
|
|
370
|
-
functionNameResolver,
|
|
371
|
-
moduleImports: {
|
|
372
|
-
xf: XFORMS_NAMESPACE_URI,
|
|
373
|
-
},
|
|
374
|
-
namespaceResolver: prefix => resolveNamespacePrefix(formElement, prefix),
|
|
375
|
-
},
|
|
376
|
-
);
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
/**
|
|
380
|
-
* Evaluate an XPath to a boolean
|
|
381
|
-
*
|
|
382
|
-
* @param {string} xpath The XPath to run
|
|
383
|
-
* @param {Node} contextNode The start of the XPath
|
|
384
|
-
* @param {Node} formElement The form element associated to the XPath
|
|
385
|
-
* @return {boolean}
|
|
386
|
-
*/
|
|
387
|
-
export function evaluateXPathToBoolean(xpath, contextNode, formElement) {
|
|
388
|
-
return fxEvaluateXPathToBoolean(
|
|
389
|
-
xpath,
|
|
390
|
-
contextNode,
|
|
391
|
-
null,
|
|
392
|
-
{},
|
|
393
|
-
{
|
|
394
|
-
currentContext: { formElement },
|
|
395
|
-
functionNameResolver,
|
|
396
|
-
moduleImports: {
|
|
397
|
-
xf: XFORMS_NAMESPACE_URI,
|
|
398
|
-
},
|
|
399
|
-
namespaceResolver: prefix => resolveNamespacePrefix(formElement, prefix),
|
|
400
|
-
},
|
|
401
|
-
);
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
/**
|
|
405
|
-
* Evaluate an XPath to a string
|
|
406
|
-
*
|
|
407
|
-
* @param {string} xpath The XPath to run
|
|
408
|
-
* @param {Node} contextNode The start of the XPath
|
|
409
|
-
* @param {Node} formElement The form element associated to the XPath
|
|
410
|
-
* @param {DomFacade} [domFacade=null] A DomFacade is used in bindings to intercept DOM
|
|
411
|
-
* access. This is used to determine dependencies between bind elements.
|
|
412
|
-
* @param {Node} formElement The element where the XPath is defined: used for namespace resolving
|
|
413
|
-
* @return {string}
|
|
414
|
-
*/
|
|
415
|
-
export function evaluateXPathToString(
|
|
416
|
-
xpath,
|
|
417
|
-
contextNode,
|
|
418
|
-
formElement,
|
|
419
|
-
domFacade = null,
|
|
420
|
-
namespaceReferenceNode = formElement,
|
|
421
|
-
) {
|
|
422
|
-
return fxEvaluateXPathToString(
|
|
423
|
-
xpath,
|
|
424
|
-
contextNode,
|
|
425
|
-
domFacade,
|
|
426
|
-
{},
|
|
805
|
+
`)
|
|
806
|
+
// At some point:
|
|
807
|
+
const contextNode = null;
|
|
808
|
+
const pendingUpdatesAndXdmValue = evaluateUpdatingExpressionSync('ns:do-something(.)', contextNode, null, null, {moduleImports: {'ns': 'my-custom-uri'}})
|
|
427
809
|
|
|
428
|
-
|
|
429
|
-
currentContext: { formElement },
|
|
430
|
-
functionNameResolver,
|
|
431
|
-
moduleImports: {
|
|
432
|
-
xf: XFORMS_NAMESPACE_URI,
|
|
433
|
-
},
|
|
434
|
-
namespaceResolver: prefix => resolveNamespacePrefix(namespaceReferenceNode, prefix),
|
|
435
|
-
},
|
|
436
|
-
);
|
|
437
|
-
}
|
|
810
|
+
console.log(pendingUpdatesAndXdmValue.xdmValue); // this is true or false, see function
|
|
438
811
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
*
|
|
442
|
-
* @param {string} xpath The XPath to run
|
|
443
|
-
* @param {Node} contextNode The start of the XPath
|
|
444
|
-
* @param {Node} formElement The form element associated to the XPath
|
|
445
|
-
* @param {DomFacade} [domFacade=null] A DomFacade is used in bindings to intercept DOM
|
|
446
|
-
* @param {Node} formElement The element where the XPath is defined: used for namespace resolving
|
|
447
|
-
* access. This is used to determine dependencies between bind elements.
|
|
448
|
-
* @return {Number}
|
|
449
|
-
*/
|
|
450
|
-
export function evaluateXPathToNumber(
|
|
451
|
-
xpath,
|
|
452
|
-
contextNode,
|
|
453
|
-
formElement,
|
|
454
|
-
domFacade = null,
|
|
455
|
-
namespaceReferenceNode = formElement,
|
|
456
|
-
) {
|
|
457
|
-
return fxEvaluateXPathToNumber(
|
|
458
|
-
xpath,
|
|
459
|
-
contextNode,
|
|
460
|
-
domFacade,
|
|
461
|
-
{},
|
|
462
|
-
{
|
|
463
|
-
currentContext: { formElement },
|
|
464
|
-
functionNameResolver,
|
|
465
|
-
moduleImports: {
|
|
466
|
-
xf: XFORMS_NAMESPACE_URI,
|
|
467
|
-
},
|
|
468
|
-
namespaceResolver: prefix => resolveNamespacePrefix(namespaceReferenceNode, prefix),
|
|
469
|
-
},
|
|
470
|
-
);
|
|
471
|
-
}
|
|
812
|
+
executePendingUpdateList(pendingUpdatesAndXdmValue.pendingUpdateList, null, null, null);
|
|
813
|
+
*/
|
|
472
814
|
|
|
473
815
|
/**
|
|
474
816
|
* @param input as string
|