@jinntec/fore 1.0.0-4 → 1.1.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.
Files changed (66) hide show
  1. package/README.md +26 -38
  2. package/dist/fore-dev.js +43 -0
  3. package/dist/fore-dev.js.map +1 -0
  4. package/dist/fore.js +37 -0
  5. package/dist/fore.js.map +1 -0
  6. package/index.js +4 -0
  7. package/package.json +42 -42
  8. package/resources/fore.css +186 -0
  9. package/resources/toastify.css +87 -0
  10. package/resources/{fore-styles.css → vars.css} +0 -292
  11. package/src/DependencyNotifyingDomFacade.js +10 -16
  12. package/src/ForeElementMixin.js +26 -19
  13. package/src/actions/abstract-action.js +30 -9
  14. package/src/actions/fx-action.js +6 -4
  15. package/src/actions/fx-append.js +8 -17
  16. package/src/actions/fx-confirm.js +5 -3
  17. package/src/actions/fx-delete.js +6 -3
  18. package/src/actions/fx-dispatch.js +9 -8
  19. package/src/actions/fx-hide.js +10 -6
  20. package/src/actions/fx-insert.js +49 -39
  21. package/src/actions/fx-message.js +3 -1
  22. package/src/actions/fx-refresh.js +15 -1
  23. package/src/actions/fx-replace.js +73 -0
  24. package/src/actions/fx-return.js +42 -0
  25. package/src/actions/fx-send.js +3 -1
  26. package/src/actions/fx-setfocus.js +37 -0
  27. package/src/actions/fx-setvalue.js +58 -51
  28. package/src/actions/fx-show.js +12 -4
  29. package/src/actions/fx-toggle.js +15 -10
  30. package/src/actions/fx-update.js +3 -1
  31. package/src/dep_graph.js +4 -2
  32. package/src/drawdown.js +67 -82
  33. package/src/fore.js +158 -12
  34. package/src/functions/fx-function.js +17 -3
  35. package/src/fx-bind.js +42 -202
  36. package/src/fx-fore.js +599 -567
  37. package/src/fx-header.js +3 -1
  38. package/src/fx-instance.js +9 -1
  39. package/src/fx-model.js +59 -23
  40. package/src/fx-submission.js +106 -48
  41. package/src/fx-var.js +7 -4
  42. package/src/getInScopeContext.js +37 -11
  43. package/src/modelitem.js +4 -4
  44. package/src/relevance.js +64 -0
  45. package/src/ui/abstract-control.js +64 -37
  46. package/src/ui/fx-alert.js +7 -1
  47. package/src/ui/fx-case.js +4 -3
  48. package/src/ui/fx-container.js +7 -1
  49. package/src/ui/fx-control.js +309 -34
  50. package/src/ui/fx-dialog.js +54 -40
  51. package/src/ui/fx-group.js +3 -1
  52. package/src/ui/fx-hint.js +4 -1
  53. package/src/ui/fx-inspector.js +120 -17
  54. package/src/ui/fx-items.js +8 -8
  55. package/src/ui/fx-output.js +16 -5
  56. package/src/ui/fx-repeat.js +33 -41
  57. package/src/ui/fx-repeatitem.js +10 -4
  58. package/src/ui/fx-switch.js +5 -3
  59. package/src/ui/fx-trigger.js +3 -1
  60. package/src/xpath-evaluation.js +621 -576
  61. package/src/xpath-util.js +15 -8
  62. package/dist/fore-all.js +0 -140
  63. package/dist/fore-debug.js +0 -140
  64. package/src/.DS_Store +0 -0
  65. package/src/actions/.DS_Store +0 -0
  66. package/src/ui/.DS_Store +0 -0
@@ -1,67 +1,151 @@
1
1
  import {
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,
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
- import { XPathUtil } from './xpath-util.js';
13
+ import {Fore} from './fore.js';
14
+
15
+ import {XPathUtil} from './xpath-util.js';
14
16
 
15
17
  const XFORMS_NAMESPACE_URI = 'http://www.w3.org/2002/xforms';
16
18
 
17
19
  const createdNamespaceResolversByXPathQueryAndNode = new Map();
18
20
 
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
- }
21
+ // A global registry of function names that are declared in Fore by a developer using the
22
+ // `fx-function` element. These should be available without providing a prefix as well
23
+ export const globallyDeclaredFunctionLocalNames = [];
44
24
 
45
25
  function getCachedNamespaceResolver(xpath, node) {
46
- if (!createdNamespaceResolversByXPathQueryAndNode.has(xpath)) {
47
- return null;
48
- }
49
- return createdNamespaceResolversByXPathQueryAndNode.get(xpath).get(node) || null;
26
+ if (!createdNamespaceResolversByXPathQueryAndNode.has(xpath)) {
27
+ return null;
28
+ }
29
+ return createdNamespaceResolversByXPathQueryAndNode.get(xpath).get(node) || null;
50
30
  }
31
+
51
32
  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);
33
+ if (!createdNamespaceResolversByXPathQueryAndNode.has(xpath)) {
34
+ return createdNamespaceResolversByXPathQueryAndNode.set(xpath, new Map());
35
+ }
36
+ return createdNamespaceResolversByXPathQueryAndNode.get(xpath).set(node, resolver);
56
37
  }
57
38
 
58
39
  const xhtmlNamespaceResolver = prefix => {
59
- if (!prefix) {
60
- return 'http://www.w3.org/1999/xhtml';
61
- }
62
- return undefined;
40
+ if (!prefix) {
41
+ return 'http://www.w3.org/1999/xhtml';
42
+ }
43
+ return undefined;
63
44
  };
64
45
 
46
+ /**
47
+ * Resolve an id in scope. Behaves like the algorithm defined on https://www.w3.org/community/xformsusers/wiki/XForms_2.0#idref-resolve
48
+ */
49
+ export function resolveId(id, sourceObject, nodeName = null) {
50
+ const allMatchingTargetObjects = fxEvaluateXPathToNodes(
51
+ 'outermost(ancestor-or-self::fx-fore[1]/(descendant::fx-fore|descendant::*[@id = $id]))[not(self::fx-fore)]',
52
+ sourceObject,
53
+ null,
54
+ {id},
55
+ {namespaceResolver: xhtmlNamespaceResolver},
56
+ );
57
+
58
+ if (allMatchingTargetObjects.length === 0) {
59
+ return null;
60
+ }
61
+
62
+ if (
63
+ allMatchingTargetObjects.length === 1 &&
64
+ fxEvaluateXPathToBoolean(
65
+ '(ancestor::fx-fore | ancestor::fx-repeat)[last()]/self::fx-fore',
66
+ allMatchingTargetObjects[0],
67
+ null,
68
+ null,
69
+ {namespaceResolver: xhtmlNamespaceResolver},
70
+ )
71
+ ) {
72
+ // If the target element is not repeated, then the search for the target object is trivial since
73
+ // there is only one associated with the target element that bears the matching ID. This is true
74
+ // regardless of whether or not the source object is repeated. However, if the target element is
75
+ // repeated, then additional information must be used to help select a target object from among
76
+ // those associated with the identified target element.
77
+ const targetObject = allMatchingTargetObjects[0];
78
+ if (nodeName && targetObject.localName !== nodeName) {
79
+ return null;
80
+ }
81
+ return targetObject;
82
+ }
83
+
84
+ // SPEC:
85
+
86
+ // 12.2.1 References to Elements within a repeat Element
87
+
88
+ // When the target element that is identified by the IDREF of a source object has one or more
89
+ // repeat elements as ancestors, then the set of ancestor repeats are partitioned into two
90
+ // subsets, those in common with the source element and those that are not in common. Any ancestor
91
+ // repeat elements of the target element not in common with the source element are descendants of
92
+ // the repeat elements that the source and target element have in common, if any.
93
+
94
+ // For the repeat elements that are in common, the desired target object exists in the same set of
95
+ // run-time objects that contains the source object. Then, for each ancestor repeat of the target
96
+ // element that is not in common with the source element, the current index of the repeat
97
+ // determines the set of run-time objects that contains the desired target object.
98
+ for (const ancestorRepeatItem of fxEvaluateXPathToNodes(
99
+ 'ancestor::fx-repeatitem => reverse()',
100
+ sourceObject,
101
+ null,
102
+ null,
103
+ {namespaceResolver: xhtmlNamespaceResolver},
104
+ )) {
105
+ const foundTargetObjects = allMatchingTargetObjects.filter(to =>
106
+ ancestorRepeatItem.contains(to),
107
+ );
108
+ switch (foundTargetObjects.length) {
109
+ case 0:
110
+ // Nothing found: ignore
111
+ break;
112
+ case 1: {
113
+ // A single one is found: the target object is directly in a common repeat
114
+ const targetObject = foundTargetObjects[0];
115
+ if (nodeName && targetObject.localName !== nodeName) {
116
+ return null;
117
+ }
118
+ return targetObject;
119
+ }
120
+ default: {
121
+ // Multiple target objects are found: they are in a repeat that is not common with the source object
122
+ // 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
123
+ const targetObject = foundTargetObjects.find(to =>
124
+ fxEvaluateXPathToNodes(
125
+ 'every $ancestor of ancestor::fx-repeatitem satisfies $ancestor is $ancestor/../child::fx-repeatitem[../@repeat-index]',
126
+ to,
127
+ null,
128
+ {},
129
+ ),
130
+ );
131
+ if (!targetObject) {
132
+ // Nothing valid found for whatever reason. This might be something dynamic?
133
+ return null;
134
+ }
135
+ if (nodeName && targetObject.localName !== nodeName) {
136
+ return null;
137
+ }
138
+ return targetObject;
139
+ }
140
+ }
141
+ }
142
+ // We found no target objects in common repeats. The id is unresolvable
143
+ return null;
144
+ }
145
+
146
+ // Make namespace resolving use the `instance` element that is related to here
147
+ const xmlDocument = new DOMParser().parseFromString('<xml />', 'text/xml');
148
+
65
149
  /**
66
150
  * Resolve a namespace. Needs a namespace prefix and the element that is most closely related to the
67
151
  * XPath in which the namespace is being resolved. The prefix will be resolved by using the
@@ -77,144 +161,148 @@ const xhtmlNamespaceResolver = prefix => {
77
161
  * @param {string} prefix The prefix to resolve
78
162
  */
79
163
  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');
164
+ const cachedResolver = getCachedNamespaceResolver(xpathQuery, formElement);
165
+ if (cachedResolver) {
166
+ return cachedResolver;
167
+ }
87
168
 
88
- const xpathAST = parseScript(xpathQuery, {}, xmlDocument);
89
- let instanceReferences = fxEvaluateXPathToStrings(
90
- `descendant::xqx:functionCallExpr
169
+ const xpathAST = parseScript(xpathQuery, {}, xmlDocument);
170
+ let instanceReferences = fxEvaluateXPathToStrings(
171
+ `descendant::xqx:functionCallExpr
91
172
  [xqx:functionName = "instance"]
92
173
  /xqx:arguments
93
174
  /xqx:stringConstantExpr
94
175
  /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,
176
+ xpathAST,
125
177
  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');
178
+ {},
179
+ {
180
+ namespaceResolver: prefix =>
181
+ prefix === 'xqx' ? 'http://www.w3.org/2005/XQueryX' : undefined,
182
+ },
183
+ );
184
+ if (instanceReferences.length === 0) {
185
+ // No instance functions. Look up further in the hierarchy to see if we can deduce the intended context from there
186
+ const ancestorComponent = fxEvaluateXPathToFirstNode('ancestor::*[@ref][1]', formElement);
187
+ if (ancestorComponent) {
188
+ const resolver = createNamespaceResolver(
189
+ ancestorComponent.getAttribute('ref'),
190
+ ancestorComponent,
191
+ );
192
+ setCachedNamespaceResolver(xpathQuery, formElement, resolver);
193
+ return resolver;
194
+ }
195
+ // Nothing found: let's just assume we're supposed to use the `default` instance
196
+ instanceReferences = ['default'];
133
197
  }
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;
198
+
199
+ if (instanceReferences.length === 1) {
200
+ // console.log(`resolving ${xpathQuery} with ${instanceReferences[0]}`);
201
+ let instance;
202
+ if (instanceReferences[0] === 'default') {
203
+ const actualForeElement = fxEvaluateXPathToFirstNode(
204
+ 'ancestor-or-self::fx-fore[1]',
205
+ formElement,
206
+ null,
207
+ null,
208
+ {namespaceResolver: xhtmlNamespaceResolver},
209
+ );
210
+
211
+ instance = actualForeElement && actualForeElement.querySelector('fx-instance');
212
+ } else {
213
+ instance = resolveId(instanceReferences[0], formElement, 'fx-instance');
214
+ }
215
+ if (instance && instance.hasAttribute('xpath-default-namespace')) {
216
+ const xpathDefaultNamespace = instance.getAttribute('xpath-default-namespace');
217
+ /*
218
+ console.log(
219
+ `Resolving the xpath ${xpathQuery} with the default namespace set to ${xpathDefaultNamespace}`,
220
+ );
221
+ */
222
+ const resolveNamespacePrefix = prefix => {
223
+ if (!prefix) {
224
+ return xpathDefaultNamespace;
225
+ }
226
+ return undefined;
227
+ };
228
+ setCachedNamespaceResolver(xpathQuery, formElement, resolveNamespacePrefix);
229
+ return resolveNamespacePrefix;
144
230
  }
145
- return undefined;
146
- };
147
- setCachedNamespaceResolver(xpathQuery, formElement, resolveNamespacePrefix);
148
- return resolveNamespacePrefix;
149
231
  }
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
- }
232
+ if (instanceReferences.length > 1) {
233
+ console.warn(
234
+ `More than one instance is used in the query "${xpathQuery}". The default namespace resolving will be used`,
235
+ );
236
+ }
156
237
 
157
- const xpathDefaultNamespace =
158
- fxEvaluateXPathToString('ancestor-or-self::*/@xpath-default-namespace[last()]', formElement) ||
159
- '';
238
+ const xpathDefaultNamespace =
239
+ fxEvaluateXPathToString('ancestor-or-self::*/@xpath-default-namespace[last()]', formElement) ||
240
+ '';
160
241
 
161
- const resolveNamespacePrefix = function resolveNamespacePrefix(prefix) {
162
- if (prefix === '') {
163
- return xpathDefaultNamespace;
164
- }
242
+ const resolveNamespacePrefix = function resolveNamespacePrefix(prefix) {
243
+ if (prefix === '') {
244
+ return xpathDefaultNamespace;
245
+ }
165
246
 
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!
247
+ // Note: ideally we should use Node#lookupNamespaceURI. However, the nodes we are passed are
248
+ // XML. The best we can do is emulate the `xmlns:xxx` namespace declarations by regarding them as
249
+ // attributes. Which they technically ARE NOT!
169
250
 
170
- return fxEvaluateXPathToString(
171
- 'ancestor-or-self::*/@*[name() = "xmlns:" || $prefix][last()]',
172
- formElement,
173
- null,
174
- { prefix },
175
- );
176
- };
251
+ return fxEvaluateXPathToString(
252
+ 'ancestor-or-self::*/@*[name() = "xmlns:" || $prefix][last()]',
253
+ formElement,
254
+ null,
255
+ {prefix},
256
+ );
257
+ };
177
258
 
178
- setCachedNamespaceResolver(xpathQuery, formElement, resolveNamespacePrefix);
179
- return resolveNamespacePrefix;
259
+ setCachedNamespaceResolver(xpathQuery, formElement, resolveNamespacePrefix);
260
+ return resolveNamespacePrefix;
180
261
  }
181
262
 
182
263
  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);
264
+ if (((contextNode && contextNode.ownerDocument) || contextNode) === window.document) {
265
+ // Running a query on the HTML DOM. Don't bother resolving namespaces in any other way
266
+ return xhtmlNamespaceResolver;
267
+ }
268
+ return createNamespaceResolver(query, formElement);
188
269
  }
189
270
 
271
+
190
272
  /**
191
273
  * Implementation of the functionNameResolver passed to FontoXPath to
192
274
  * redirect function resolving for unprefixed functions to either the fn or the xf namespace
193
275
  */
194
276
  // 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
- }
277
+ function functionNameResolver({prefix, localName}, _arity) {
278
+ switch (localName) {
279
+ // TODO: put the full XForms library functions set here
280
+ case 'context':
281
+ case 'base64encode':
282
+ case 'boolean-from-string':
283
+ case 'current':
284
+ case 'depends':
285
+ case 'event':
286
+ case 'index':
287
+ case 'instance':
288
+ case 'log':
289
+ case 'parse':
290
+ case 'logtree':
291
+ return {namespaceURI: XFORMS_NAMESPACE_URI, localName};
292
+ default:
293
+ if (prefix === '' && globallyDeclaredFunctionLocalNames.includes(localName)) {
294
+ // The function has been declared without a prefix and is called here without a prefix.
295
+ // Just make this work. It is the developer-friendly way
296
+ return {namespaceURI: 'http://www.w3.org/2005/xquery-local-functions', localName};
297
+ }
298
+ if (prefix === 'fn' || prefix === '') {
299
+ return {namespaceURI: 'http://www.w3.org/2005/xpath-functions', localName};
300
+ }
301
+ if (prefix === 'local') {
302
+ return {namespaceURI: 'http://www.w3.org/2005/xquery-local-functions', localName};
303
+ }
304
+ return null;
305
+ }
218
306
  }
219
307
 
220
308
  /**
@@ -226,21 +314,23 @@ function functionNameResolver({ prefix, localName }, _arity) {
226
314
  * @return {Object} A key-value mapping of the variables
227
315
  */
228
316
  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;
317
+ let closestActualFormElement = formElement;
318
+ while (closestActualFormElement && !('inScopeVariables' in closestActualFormElement)) {
319
+ closestActualFormElement = closestActualFormElement.parentNode;
320
+ }
321
+
322
+ if (!closestActualFormElement) {
323
+ return {};
324
+ }
325
+
326
+ const variables = {};
327
+ if (closestActualFormElement.inScopeVariables) {
328
+ for (const key of closestActualFormElement.inScopeVariables.keys()) {
329
+ const varElement = closestActualFormElement.inScopeVariables.get(key);
330
+ variables[key] = varElement.value;
331
+ }
332
+ }
333
+ return variables;
244
334
  }
245
335
 
246
336
  /**
@@ -251,25 +341,26 @@ function getVariablesInScope(formElement) {
251
341
  * @param {Node} contextNode The start of the XPath
252
342
  * @param {{parentNode}|ForeElementMixin} formElement The form element associated to the XPath
253
343
  */
254
- export function evaluateXPath(xpath, contextNode, formElement, variables = {}) {
255
- const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
256
- const variablesInScope = getVariablesInScope(formElement);
344
+ export function evaluateXPath(xpath, contextNode, formElement, variables = {}, options={}) {
345
+ const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
346
+ const variablesInScope = getVariablesInScope(formElement);
257
347
 
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
- );
348
+ return fxEvaluateXPath(
349
+ xpath,
350
+ contextNode,
351
+ null,
352
+ {...variablesInScope, ...variables},
353
+ fxEvaluateXPath.ALL_RESULTS_TYPE,
354
+ {
355
+ currentContext: {formElement, variables},
356
+ moduleImports: {
357
+ xf: XFORMS_NAMESPACE_URI,
358
+ },
359
+ functionNameResolver,
360
+ namespaceResolver,
361
+ language: options.language || evaluateXPath.XPATH_3_1
362
+ },
363
+ );
273
364
  }
274
365
 
275
366
  /**
@@ -281,16 +372,17 @@ export function evaluateXPath(xpath, contextNode, formElement, variables = {}) {
281
372
  * @return {Node} The first node found by the XPath
282
373
  */
283
374
  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
- });
375
+ const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
376
+ const variablesInScope = getVariablesInScope(formElement);
377
+ return fxEvaluateXPathToFirstNode(xpath, contextNode, null, variablesInScope, {
378
+ defaultFunctionNamespaceURI: XFORMS_NAMESPACE_URI,
379
+ moduleImports: {
380
+ xf: XFORMS_NAMESPACE_URI,
381
+ },
382
+ currentContext: {formElement},
383
+ functionNameResolver,
384
+ namespaceResolver,
385
+ });
294
386
  }
295
387
 
296
388
  /**
@@ -302,17 +394,17 @@ export function evaluateXPathToFirstNode(xpath, contextNode, formElement) {
302
394
  * @return {Node[]} All nodes
303
395
  */
304
396
  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
- });
397
+ const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
398
+ const variablesInScope = getVariablesInScope(formElement);
399
+
400
+ return fxEvaluateXPathToNodes(xpath, contextNode, null, variablesInScope, {
401
+ currentContext: {formElement},
402
+ functionNameResolver,
403
+ moduleImports: {
404
+ xf: XFORMS_NAMESPACE_URI,
405
+ },
406
+ namespaceResolver,
407
+ });
316
408
  }
317
409
 
318
410
  /**
@@ -324,17 +416,17 @@ export function evaluateXPathToNodes(xpath, contextNode, formElement) {
324
416
  * @return {boolean}
325
417
  */
326
418
  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
- });
419
+ const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
420
+ const variablesInScope = getVariablesInScope(formElement);
421
+
422
+ return fxEvaluateXPathToBoolean(xpath, contextNode, null, variablesInScope, {
423
+ currentContext: {formElement},
424
+ functionNameResolver,
425
+ moduleImports: {
426
+ xf: XFORMS_NAMESPACE_URI,
427
+ },
428
+ namespaceResolver,
429
+ });
338
430
  }
339
431
 
340
432
  /**
@@ -349,23 +441,23 @@ export function evaluateXPathToBoolean(xpath, contextNode, formElement) {
349
441
  * @return {string}
350
442
  */
351
443
  export function evaluateXPathToString(
352
- xpath,
353
- contextNode,
354
- formElement,
355
- domFacade = null,
356
- namespaceReferenceNode = formElement,
444
+ xpath,
445
+ contextNode,
446
+ formElement,
447
+ domFacade = null,
448
+ namespaceReferenceNode = formElement,
357
449
  ) {
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
- });
450
+ const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
451
+ const variablesInScope = getVariablesInScope(formElement);
452
+
453
+ return fxEvaluateXPathToString(xpath, contextNode, domFacade, variablesInScope, {
454
+ currentContext: {formElement},
455
+ functionNameResolver,
456
+ moduleImports: {
457
+ xf: XFORMS_NAMESPACE_URI,
458
+ },
459
+ namespaceResolver,
460
+ });
369
461
  }
370
462
 
371
463
  /**
@@ -380,28 +472,28 @@ export function evaluateXPathToString(
380
472
  * @return {string}
381
473
  */
382
474
  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
475
  xpath,
392
476
  contextNode,
393
- domFacade,
394
- {},
395
-
396
- {
397
- currentContext: { formElement },
398
- functionNameResolver,
399
- moduleImports: {
400
- xf: XFORMS_NAMESPACE_URI,
401
- },
402
- namespaceResolver,
403
- },
404
- );
477
+ formElement,
478
+ domFacade = null,
479
+ namespaceReferenceNode = formElement,
480
+ ) {
481
+ const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
482
+ return fxEvaluateXPathToStrings(
483
+ xpath,
484
+ contextNode,
485
+ domFacade,
486
+ {},
487
+
488
+ {
489
+ currentContext: {formElement},
490
+ functionNameResolver,
491
+ moduleImports: {
492
+ xf: XFORMS_NAMESPACE_URI,
493
+ },
494
+ namespaceResolver,
495
+ },
496
+ );
405
497
  }
406
498
 
407
499
  /**
@@ -416,148 +508,54 @@ export function evaluateXPathToStrings(
416
508
  * @return {Number}
417
509
  */
418
510
  export function evaluateXPathToNumber(
419
- xpath,
420
- contextNode,
421
- formElement,
422
- domFacade = null,
423
- namespaceReferenceNode = formElement,
511
+ xpath,
512
+ contextNode,
513
+ formElement,
514
+ domFacade = null,
515
+ namespaceReferenceNode = formElement,
424
516
  ) {
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
- }
475
-
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;
517
+ const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
518
+ const variablesInScope = getVariablesInScope(formElement);
519
+
520
+ return fxEvaluateXPathToNumber(xpath, contextNode, domFacade, variablesInScope, {
521
+ currentContext: {formElement},
522
+ functionNameResolver,
523
+ moduleImports: {
524
+ xf: XFORMS_NAMESPACE_URI,
525
+ },
526
+ namespaceResolver,
527
+ });
536
528
  }
537
529
 
538
530
  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
- }
531
+ const caller = dynamicContext.currentContext.formElement;
532
+ if (string) {
533
+ const instance = resolveId(string, caller);
534
+ if (instance) {
535
+ if (instance.nodeName === 'FX-REPEAT') {
536
+ const {nodeset} = instance;
537
+ for (let parent = caller; parent; parent = parent.parentNode) {
538
+ if (parent.parentNode === instance) {
539
+ const offset = Array.from(parent.parentNode.children).indexOf(parent);
540
+ return nodeset[offset];
541
+ }
542
+ }
543
+ }
544
+ return instance.nodeset;
550
545
  }
551
- }
552
- return instance.nodeset;
553
546
  }
554
- }
555
- const parent = XPathUtil.getParentBindingElement(caller);
556
- const p = caller.nodeName;
557
- // const p = dynamicContext.domFacade.getParentElement();
547
+ const parent = XPathUtil.getParentBindingElement(caller);
548
+ // const p = caller.nodeName;
549
+ // const p = dynamicContext.domFacade.getParentElement();
558
550
 
559
- if (parent) return parent;
560
- return caller.getInScopeContext();
551
+ if (parent) return parent.nodeset;
552
+ return caller.getInScopeContext();
553
+ };
554
+
555
+ // todo: implement
556
+ const currentFunction = (dynamicContext, string) => {
557
+ const caller = dynamicContext.currentContext.formElement;
558
+ return null;
561
559
  };
562
560
 
563
561
  /**
@@ -565,10 +563,10 @@ const contextFunction = (dynamicContext, string) => {
565
563
  * @return instance data for given id serialized to string.
566
564
  */
567
565
  registerCustomXPathFunction(
568
- { namespaceURI: XFORMS_NAMESPACE_URI, localName: 'context' },
569
- [],
570
- 'item()?',
571
- contextFunction,
566
+ {namespaceURI: XFORMS_NAMESPACE_URI, localName: 'context'},
567
+ [],
568
+ 'item()?',
569
+ contextFunction,
572
570
  );
573
571
 
574
572
  /**
@@ -576,10 +574,17 @@ registerCustomXPathFunction(
576
574
  * @return instance data for given id serialized to string.
577
575
  */
578
576
  registerCustomXPathFunction(
579
- { namespaceURI: XFORMS_NAMESPACE_URI, localName: 'context' },
580
- ['xs:string'],
581
- 'item()?',
582
- contextFunction,
577
+ {namespaceURI: XFORMS_NAMESPACE_URI, localName: 'context'},
578
+ ['xs:string'],
579
+ 'item()?',
580
+ contextFunction,
581
+ );
582
+
583
+ registerCustomXPathFunction(
584
+ {namespaceURI: XFORMS_NAMESPACE_URI, localName: 'current'},
585
+ ['xs:string'],
586
+ 'item()?',
587
+ currentFunction,
583
588
  );
584
589
 
585
590
  /**
@@ -587,68 +592,94 @@ registerCustomXPathFunction(
587
592
  * @return instance data for given id serialized to string.
588
593
  */
589
594
  registerCustomXPathFunction(
590
- { namespaceURI: XFORMS_NAMESPACE_URI, localName: 'log' },
591
- ['xs:string?'],
592
- 'xs:string?',
593
- (dynamicContext, string) => {
594
- const { formElement } = dynamicContext.currentContext;
595
- const instance = resolveId(string, formElement, 'fx-instance');
596
- if (instance) {
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
- }
604
- }
605
- return null;
606
- },
595
+ {namespaceURI: XFORMS_NAMESPACE_URI, localName: 'log'},
596
+ ['xs:string?'],
597
+ 'xs:string?',
598
+ (dynamicContext, string) => {
599
+ const {formElement} = dynamicContext.currentContext;
600
+ const instance = resolveId(string, formElement, 'fx-instance');
601
+ if (instance) {
602
+ if (instance.getAttribute('type') === 'json') {
603
+ console.warn('log() does not work for JSON yet');
604
+ // return JSON.stringify(instance.getDefaultContext());
605
+ } else {
606
+ const def = new XMLSerializer().serializeToString(instance.getDefaultContext());
607
+ return Fore.prettifyXml(def);
608
+ }
609
+ }
610
+ return null;
611
+ },
612
+ );
613
+
614
+ registerCustomXPathFunction(
615
+ {namespaceURI: XFORMS_NAMESPACE_URI, localName: 'parse'},
616
+ ['xs:string?'],
617
+ 'element()?',
618
+ (dynamicContext, string) => {
619
+ const parser = new DOMParser();
620
+ const out = parser.parseFromString(string, "application/xml");
621
+ console.log('parse', out);
622
+
623
+ /*
624
+ const {formElement} = dynamicContext.currentContext;
625
+ const instance = resolveId(string, formElement, 'fx-instance');
626
+ if (instance) {
627
+ if (instance.getAttribute('type') === 'json') {
628
+ console.warn('log() does not work for JSON yet');
629
+ // return JSON.stringify(instance.getDefaultContext());
630
+ } else {
631
+ const def = new XMLSerializer().serializeToString(instance.getDefaultContext());
632
+ return Fore.prettifyXml(def);
633
+ }
634
+ }
635
+ */
636
+ return out.firstElementChild;
637
+ },
607
638
  );
608
639
 
609
640
  function buildTree(tree, data) {
610
- if (!data) return;
611
- if (data.nodeType === Node.ELEMENT_NODE) {
612
- if (data.children) {
613
- const details = document.createElement('details');
614
- details.setAttribute('data-path', data.nodeName);
615
- const summary = document.createElement('summary');
616
-
617
- let display = ` <${data.nodeName}`;
618
- Array.from(data.attributes).forEach(attr => {
619
- display += ` ${attr.nodeName}="${attr.nodeValue}"`;
620
- });
621
-
622
- let contents;
623
- if (
624
- data.firstChild &&
625
- data.firstChild.nodeType === Node.TEXT_NODE &&
626
- data.firstChild.data.trim() !== ''
627
- ) {
628
- // console.log('whoooooooooopp');
629
- contents = data.firstChild.nodeValue;
630
- display += `>${contents}</${data.nodeName}>`;
631
- } else {
632
- display += '>';
633
- }
634
- summary.textContent = display;
635
-
636
- details.appendChild(summary);
637
- if (data.childElementCount !== 0) {
638
- details.setAttribute('open', 'open');
639
- } else {
640
- summary.setAttribute('style', 'list-style:none;');
641
- }
642
- tree.appendChild(details);
643
-
644
- Array.from(data.children).forEach(child => {
645
- // if(child.nodeType === Node.ELEMENT_NODE){
646
- // child.parentNode.appendChild(buildTree(child));
647
- buildTree(details, child);
648
- // }
649
- });
650
- }
651
- } /* else if(data.nodeType === Node.ATTRIBUTE_NODE){
641
+ if (!data) return;
642
+ if (data.nodeType === Node.ELEMENT_NODE) {
643
+ if (data.children) {
644
+ const details = document.createElement('details');
645
+ details.setAttribute('data-path', data.nodeName);
646
+ const summary = document.createElement('summary');
647
+
648
+ let display = ` <${data.nodeName}`;
649
+ Array.from(data.attributes).forEach(attr => {
650
+ display += ` ${attr.nodeName}="${attr.nodeValue}"`;
651
+ });
652
+
653
+ let contents;
654
+ if (
655
+ data.firstChild &&
656
+ data.firstChild.nodeType === Node.TEXT_NODE &&
657
+ data.firstChild.data.trim() !== ''
658
+ ) {
659
+ // console.log('whoooooooooopp');
660
+ contents = data.firstChild.nodeValue;
661
+ display += `>${contents}</${data.nodeName}>`;
662
+ } else {
663
+ display += '>';
664
+ }
665
+ summary.textContent = display;
666
+
667
+ details.appendChild(summary);
668
+ if (data.childElementCount !== 0) {
669
+ details.setAttribute('open', 'open');
670
+ } else {
671
+ summary.setAttribute('style', 'list-style:none;');
672
+ }
673
+ tree.appendChild(details);
674
+
675
+ Array.from(data.children).forEach(child => {
676
+ // if(child.nodeType === Node.ELEMENT_NODE){
677
+ // child.parentNode.appendChild(buildTree(child));
678
+ buildTree(details, child);
679
+ // }
680
+ });
681
+ }
682
+ } /* else if(data.nodeType === Node.ATTRIBUTE_NODE){
652
683
  //create span for now
653
684
  // const span = document.createElement('span');
654
685
  // span.style.background = 'grey';
@@ -659,125 +690,139 @@ function buildTree(tree, data) {
659
690
  tree.textContent = data;
660
691
  } */
661
692
 
662
- // return tree;
693
+ // return tree;
663
694
  }
664
695
 
665
696
  registerCustomXPathFunction(
666
- { namespaceURI: XFORMS_NAMESPACE_URI, localName: 'logtree' },
667
- ['xs:string?'],
668
- 'element()?',
669
- (dynamicContext, string) => {
670
- const { formElement } = dynamicContext.currentContext;
671
- const instance = resolveId(string, formElement, 'fx-instance');
672
-
673
- if (instance) {
674
- // const def = new XMLSerializer().serializeToString(instance.getDefaultContext());
675
- // const def = JSON.stringify(instance.getDefaultContext());
676
-
677
- const treeDiv = document.createElement('div');
678
- treeDiv.setAttribute('class', 'logtree');
679
- // const datatree = buildTree(tree,instance.getDefaultContext());
680
- // return tree.appendChild(datatree);
681
- // return buildTree(root,instance.getDefaultContext());;
682
- const form = dynamicContext.currentContext.formElement;
683
- const logtree = form.querySelector('.logtree');
684
- if (logtree) {
685
- logtree.parentNode.removeChild(logtree);
686
- }
687
- const tree = buildTree(treeDiv, instance.getDefaultContext());
688
- if (tree) {
689
- form.appendChild(tree);
690
- }
691
- }
692
- return null;
693
- },
697
+ {namespaceURI: XFORMS_NAMESPACE_URI, localName: 'logtree'},
698
+ ['xs:string?'],
699
+ 'element()?',
700
+ (dynamicContext, string) => {
701
+ const {formElement} = dynamicContext.currentContext;
702
+ const instance = resolveId(string, formElement, 'fx-instance');
703
+
704
+ if (instance) {
705
+ // const def = new XMLSerializer().serializeToString(instance.getDefaultContext());
706
+ // const def = JSON.stringify(instance.getDefaultContext());
707
+
708
+ const treeDiv = document.createElement('div');
709
+ treeDiv.setAttribute('class', 'logtree');
710
+ // const datatree = buildTree(tree,instance.getDefaultContext());
711
+ // return tree.appendChild(datatree);
712
+ // return buildTree(root,instance.getDefaultContext());;
713
+ const form = dynamicContext.currentContext.formElement;
714
+ const logtree = form.querySelector('.logtree');
715
+ if (logtree) {
716
+ logtree.parentNode.removeChild(logtree);
717
+ }
718
+ const tree = buildTree(treeDiv, instance.getDefaultContext());
719
+ if (tree) {
720
+ form.appendChild(tree);
721
+ }
722
+ }
723
+ return null;
724
+ },
694
725
  );
695
726
 
696
727
  const instance = (dynamicContext, string) => {
697
- // Spec: https://www.w3.org/TR/xforms-xpath/#The_XForms_Function_Library#The_instance.28.29_Function
698
- // TODO: handle no string passed (null will be passed instead)
699
-
700
- const formElement = fxEvaluateXPathToFirstNode(
701
- 'ancestor-or-self::fx-fore',
702
- dynamicContext.currentContext.formElement,
703
- null,
704
- null,
705
- { namespaceResolver: xhtmlNamespaceResolver },
706
- );
707
-
708
- // console.log('fnInstance dynamicContext: ', dynamicContext);
709
- // console.log('fnInstance string: ', string);
710
-
711
- const inst = string
712
- ? resolveId(string, formElement, 'fx-instance')
713
- : formElement.querySelector(`fx-instance`);
714
-
715
- // const def = instance.getInstanceData();
716
- if (inst) {
717
- const def = inst.getDefaultContext();
718
- // console.log('target instance root node: ', def);
719
-
720
- return def;
721
- }
722
- return null;
723
- };
728
+ // Spec: https://www.w3.org/TR/xforms-xpath/#The_XForms_Function_Library#The_instance.28.29_Function
729
+ // TODO: handle no string passed (null will be passed instead)
724
730
 
725
- registerCustomXPathFunction(
726
- { namespaceURI: XFORMS_NAMESPACE_URI, localName: 'index' },
727
- ['xs:string?'],
728
- 'xs:integer?',
729
- (dynamicContext, string) => {
730
- const { formElement } = dynamicContext.currentContext;
731
- if (string === null) {
732
- return 1;
733
- }
734
- const repeat = resolveId(string, formElement, 'fx-repeat');
731
+ const formElement = fxEvaluateXPathToFirstNode(
732
+ 'ancestor-or-self::fx-fore[1]',
733
+ dynamicContext.currentContext.formElement,
734
+ null,
735
+ null,
736
+ {namespaceResolver: xhtmlNamespaceResolver},
737
+ );
738
+
739
+ // console.log('fnInstance dynamicContext: ', dynamicContext);
740
+ // console.log('fnInstance string: ', string);
741
+
742
+ const inst = string
743
+ ? resolveId(string, formElement, 'fx-instance')
744
+ : formElement.querySelector(`fx-instance`);
735
745
 
736
746
  // const def = instance.getInstanceData();
737
- if (repeat) {
738
- return repeat.getAttribute('index');
747
+ if (inst) {
748
+ const def = inst.getDefaultContext();
749
+ // console.log('target instance root node: ', def);
750
+
751
+ return def;
739
752
  }
740
- return Number(1);
741
- },
753
+ return null;
754
+ };
755
+
756
+ registerCustomXPathFunction(
757
+ {namespaceURI: XFORMS_NAMESPACE_URI, localName: 'index'},
758
+ ['xs:string?'],
759
+ 'xs:integer?',
760
+ (dynamicContext, string) => {
761
+ const {formElement} = dynamicContext.currentContext;
762
+ if (string === null) {
763
+ return 1;
764
+ }
765
+ const repeat = resolveId(string, formElement, 'fx-repeat');
766
+
767
+ // const def = instance.getInstanceData();
768
+ if (repeat) {
769
+ return repeat.getAttribute('index');
770
+ }
771
+ return Number(1);
772
+ },
742
773
  );
743
774
 
744
775
  // Note that this is not to spec. The spec enforces elements to be returned from the
745
776
  // instance. However, we allow instances to actually be JSON!
746
777
  registerCustomXPathFunction(
747
- { namespaceURI: XFORMS_NAMESPACE_URI, localName: 'instance' },
748
- [],
749
- 'item()?',
750
- domFacade => instance(domFacade, null),
778
+ {namespaceURI: XFORMS_NAMESPACE_URI, localName: 'instance'},
779
+ [],
780
+ 'item()?',
781
+ domFacade => instance(domFacade, null),
751
782
  );
752
783
 
753
784
  registerCustomXPathFunction(
754
- { namespaceURI: XFORMS_NAMESPACE_URI, localName: 'instance' },
755
- ['xs:string?'],
756
- 'item()?',
757
- instance,
785
+ {namespaceURI: XFORMS_NAMESPACE_URI, localName: 'instance'},
786
+ ['xs:string?'],
787
+ 'item()?',
788
+ instance,
758
789
  );
759
790
 
760
791
  registerCustomXPathFunction(
761
- { namespaceURI: XFORMS_NAMESPACE_URI, localName: 'depends' },
762
- ['node()*'],
763
- 'item()?',
764
- (dynamicContext, nodes) =>
765
- // console.log('depends on : ', nodes[0]);
766
- nodes[0],
792
+ {namespaceURI: XFORMS_NAMESPACE_URI, localName: 'depends'},
793
+ ['node()*'],
794
+ 'item()?',
795
+ (dynamicContext, nodes) =>
796
+ // console.log('depends on : ', nodes[0]);
797
+ nodes[0],
767
798
  );
768
799
 
769
800
  registerCustomXPathFunction(
770
- { namespaceURI: XFORMS_NAMESPACE_URI, localName: 'event' },
771
- ['xs:string?'],
772
- 'item()?',
773
- (dynamicContext, arg) => {
774
- const payload = dynamicContext.currentContext.variables[arg];
775
- if (payload.nodeType) {
776
- console.log('got some node as js object');
777
- }
801
+ {namespaceURI: XFORMS_NAMESPACE_URI, localName: 'event'},
802
+ ['xs:string?'],
803
+ 'item()?',
804
+ (dynamicContext, arg) => {
805
+ if (!arg) return [];
806
+
807
+ if (dynamicContext.currentContext.variables) {
808
+ const payload = dynamicContext.currentContext.variables[arg];
809
+ if (payload.nodeType) {
810
+ console.log('got some node as js object');
811
+ }
812
+ if (payload) {
813
+ return dynamicContext.currentContext.variables[arg];
814
+ }
815
+ }
778
816
 
779
- return dynamicContext.currentContext.variables[arg];
780
- },
817
+ if (dynamicContext.currentContext.formElement.inScopeVariables) {
818
+ console.log('event()', dynamicContext.currentContext.formElement.inScopeVariables);
819
+ console.log('event()', dynamicContext.currentContext.formElement.inScopeVariables.get(arg));
820
+ // dynamicContext.currentContext.variables = dynamicContext.currentContext.formElement.inScopeVariables;
821
+ return dynamicContext.currentContext.formElement.inScopeVariables.get(arg);
822
+ }
823
+
824
+ return [];
825
+ },
781
826
  );
782
827
 
783
828
  // Implement the XForms standard functions here.
@@ -791,34 +836,34 @@ registerXQueryModule(`
791
836
 
792
837
  // How to run XQUERY:
793
838
  /**
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 {
839
+ registerXQueryModule(`
840
+ module namespace my-custom-namespace = "my-custom-uri";
841
+ (:~
842
+ Insert attribute somewhere
843
+ ~:)
844
+ declare %public %updating function my-custom-namespace:do-something ($ele as element()) as xs:boolean {
800
845
  if ($ele/@done) then false() else
801
846
  (insert node
802
847
  attribute done {"true"}
803
848
  into $ele, true())
804
849
  };
805
- `)
806
- // At some point:
807
- const contextNode = null;
808
- const pendingUpdatesAndXdmValue = evaluateUpdatingExpressionSync('ns:do-something(.)', contextNode, null, null, {moduleImports: {'ns': 'my-custom-uri'}})
850
+ `)
851
+ // At some point:
852
+ const contextNode = null;
853
+ const pendingUpdatesAndXdmValue = evaluateUpdatingExpressionSync('ns:do-something(.)', contextNode, null, null, {moduleImports: {'ns': 'my-custom-uri'}})
809
854
 
810
- console.log(pendingUpdatesAndXdmValue.xdmValue); // this is true or false, see function
855
+ console.log(pendingUpdatesAndXdmValue.xdmValue); // this is true or false, see function
811
856
 
812
- executePendingUpdateList(pendingUpdatesAndXdmValue.pendingUpdateList, null, null, null);
813
- */
857
+ executePendingUpdateList(pendingUpdatesAndXdmValue.pendingUpdateList, null, null, null);
858
+ */
814
859
 
815
860
  /**
816
861
  * @param input as string
817
862
  * @return {string}
818
863
  */
819
864
  registerCustomXPathFunction(
820
- { namespaceURI: XFORMS_NAMESPACE_URI, localName: 'base64encode' },
821
- ['xs:string?'],
822
- 'xs:string?',
823
- (dynamicContext, string) => btoa(string),
865
+ {namespaceURI: XFORMS_NAMESPACE_URI, localName: 'base64encode'},
866
+ ['xs:string?'],
867
+ 'xs:string?',
868
+ (dynamicContext, string) => btoa(string),
824
869
  );