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