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