@jinntec/fore 1.0.0-1 → 1.0.0-2

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,16 +1,67 @@
1
1
  import {
2
2
  evaluateXPath as fxEvaluateXPath,
3
+ evaluateXPathToBoolean as fxEvaluateXPathToBoolean,
3
4
  evaluateXPathToFirstNode as fxEvaluateXPathToFirstNode,
4
5
  evaluateXPathToNodes as fxEvaluateXPathToNodes,
5
- evaluateXPathToBoolean as fxEvaluateXPathToBoolean,
6
- evaluateXPathToString as fxEvaluateXPathToString,
7
6
  evaluateXPathToNumber as fxEvaluateXPathToNumber,
7
+ evaluateXPathToString as fxEvaluateXPathToString,
8
+ evaluateXPathToStrings as fxEvaluateXPathToStrings,
9
+ parseScript,
8
10
  registerCustomXPathFunction,
9
11
  registerXQueryModule,
10
12
  } from 'fontoxpath';
13
+ import { XPathUtil } from './xpath-util.js';
11
14
 
12
15
  const XFORMS_NAMESPACE_URI = 'http://www.w3.org/2002/xforms';
13
16
 
17
+ const createdNamespaceResolversByXPathQueryAndNode = new Map();
18
+
19
+ function prettifyXml(source) {
20
+ const xmlDoc = new DOMParser().parseFromString(source, 'application/xml');
21
+ const xsltDoc = new DOMParser().parseFromString(
22
+ [
23
+ // describes how we want to modify the XML - indent everything
24
+ '<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">',
25
+ ' <xsl:strip-space elements="*"/>',
26
+ ' <xsl:template match="para[content-style][not(text())]">', // change to just text() to strip space in text nodes
27
+ ' <xsl:value-of select="normalize-space(.)"/>',
28
+ ' </xsl:template>',
29
+ ' <xsl:template match="node()|@*">',
30
+ ' <xsl:copy><xsl:apply-templates select="node()|@*"/></xsl:copy>',
31
+ ' </xsl:template>',
32
+ ' <xsl:output indent="yes"/>',
33
+ '</xsl:stylesheet>',
34
+ ].join('\n'),
35
+ 'application/xml',
36
+ );
37
+
38
+ const xsltProcessor = new XSLTProcessor();
39
+ xsltProcessor.importStylesheet(xsltDoc);
40
+ const resultDoc = xsltProcessor.transformToDocument(xmlDoc);
41
+ const resultXml = new XMLSerializer().serializeToString(resultDoc);
42
+ return resultXml;
43
+ }
44
+
45
+ function getCachedNamespaceResolver(xpath, node) {
46
+ if (!createdNamespaceResolversByXPathQueryAndNode.has(xpath)) {
47
+ return null;
48
+ }
49
+ return createdNamespaceResolversByXPathQueryAndNode.get(xpath).get(node) || null;
50
+ }
51
+ function setCachedNamespaceResolver(xpath, node, resolver) {
52
+ if (!createdNamespaceResolversByXPathQueryAndNode.has(xpath)) {
53
+ return createdNamespaceResolversByXPathQueryAndNode.set(xpath, new Map());
54
+ }
55
+ return createdNamespaceResolversByXPathQueryAndNode.get(xpath).set(node, resolver);
56
+ }
57
+
58
+ const xhtmlNamespaceResolver = prefix => {
59
+ if (!prefix) {
60
+ return 'http://www.w3.org/1999/xhtml';
61
+ }
62
+ return undefined;
63
+ };
64
+
14
65
  /**
15
66
  * Resolve a namespace. Needs a namespace prefix and the element that is most closely related to the
16
67
  * XPath in which the namespace is being resolved. The prefix will be resolved by using the
@@ -25,33 +76,115 @@ const XFORMS_NAMESPACE_URI = 'http://www.w3.org/2002/xforms';
25
76
  * @param {Node} contextElement The element that is most closely related with the XPath in which this prefix is resolved.
26
77
  * @param {string} prefix The prefix to resolve
27
78
  */
28
- function resolveNamespacePrefix(contextElement, prefix) {
29
- if (prefix === 'xhtml') {
30
- return 'http://www.w3.org/1999/xhtml';
79
+ function createNamespaceResolver(xpathQuery, formElement) {
80
+ const cachedResolver = getCachedNamespaceResolver(xpathQuery, formElement);
81
+ if (cachedResolver) {
82
+ return cachedResolver;
83
+ }
84
+
85
+ // Make namespace resolving use the `instance` element that is related to here
86
+ const xmlDocument = new DOMParser().parseFromString('<xml />', 'text/xml');
87
+
88
+ const xpathAST = parseScript(xpathQuery, {}, xmlDocument);
89
+ let instanceReferences = fxEvaluateXPathToStrings(
90
+ `descendant::xqx:functionCallExpr
91
+ [xqx:functionName = "instance"]
92
+ /xqx:arguments
93
+ /xqx:stringConstantExpr
94
+ /xqx:value`,
95
+ xpathAST,
96
+ null,
97
+ {},
98
+ {
99
+ namespaceResolver: prefix =>
100
+ prefix === 'xqx' ? 'http://www.w3.org/2005/XQueryX' : undefined,
101
+ },
102
+ );
103
+ if (instanceReferences.length === 0) {
104
+ // No instance functions. Look up further in the hierarchy to see if we can deduce the intended context from there
105
+ const ancestorComponent = fxEvaluateXPathToFirstNode('ancestor::*[@ref][1]', formElement);
106
+ if (ancestorComponent) {
107
+ const resolver = createNamespaceResolver(
108
+ ancestorComponent.getAttribute('ref'),
109
+ ancestorComponent,
110
+ );
111
+ setCachedNamespaceResolver(xpathQuery, formElement, resolver);
112
+ return resolver;
113
+ }
114
+ // Nothing found: let's just assume we're supposed to use the `default` instance
115
+ instanceReferences = ['default'];
31
116
  }
32
117
 
33
- if (prefix === '') {
34
- return (
35
- fxEvaluateXPathToString(
36
- 'ancestor-or-self::*/@xpath-default-namespace[last()]',
37
- contextElement,
38
- ) || null
118
+ if (instanceReferences.length === 1) {
119
+ // console.log(`resolving ${xpathQuery} with ${instanceReferences[0]}`);
120
+ let instance;
121
+ if (instanceReferences[0] === 'default') {
122
+ const actualForeElement = fxEvaluateXPathToFirstNode(
123
+ 'ancestor-or-self::fx-fore',
124
+ formElement,
125
+ null,
126
+ null,
127
+ { namespaceResolver: xhtmlNamespaceResolver },
128
+ );
129
+
130
+ instance = actualForeElement && actualForeElement.querySelector('fx-instance');
131
+ } else {
132
+ instance = resolveId(instanceReferences[0], formElement, 'fx-instance');
133
+ }
134
+ if (instance && instance.hasAttribute('xpath-default-namespace')) {
135
+ const xpathDefaultNamespace = instance.getAttribute('xpath-default-namespace');
136
+ /*
137
+ console.log(
138
+ `Resolving the xpath ${xpathQuery} with the default namespace set to ${xpathDefaultNamespace}`,
139
+ );
140
+ */
141
+ const resolveNamespacePrefix = prefix => {
142
+ if (!prefix) {
143
+ return xpathDefaultNamespace;
144
+ }
145
+ return undefined;
146
+ };
147
+ setCachedNamespaceResolver(xpathQuery, formElement, resolveNamespacePrefix);
148
+ return resolveNamespacePrefix;
149
+ }
150
+ }
151
+ if (instanceReferences.length > 1) {
152
+ console.warn(
153
+ `More than one instance is used in the query "${xpathQuery}". The default namespace resolving will be used`,
39
154
  );
40
155
  }
41
156
 
42
- // Note: ideally we should use Node#lookupNamespaceURI. However, the nodes we are passed are
43
- // XML. The best we can do is emulate the `xmlns:xxx` namespace declarations by regarding them as
44
- // attributes. Which they technically ARE NOT!
157
+ const xpathDefaultNamespace =
158
+ fxEvaluateXPathToString('ancestor-or-self::*/@xpath-default-namespace[last()]', formElement) ||
159
+ '';
45
160
 
46
- const result = fxEvaluateXPathToString(
47
- 'ancestor-or-self::*/@*[name() = "xmlns:" || $prefix][last()]',
48
- contextElement,
49
- null,
50
- { prefix },
51
- );
161
+ const resolveNamespacePrefix = function resolveNamespacePrefix(prefix) {
162
+ if (prefix === '') {
163
+ return xpathDefaultNamespace;
164
+ }
165
+
166
+ // Note: ideally we should use Node#lookupNamespaceURI. However, the nodes we are passed are
167
+ // XML. The best we can do is emulate the `xmlns:xxx` namespace declarations by regarding them as
168
+ // attributes. Which they technically ARE NOT!
52
169
 
53
- console.log('result', result);
54
- return result;
170
+ return fxEvaluateXPathToString(
171
+ 'ancestor-or-self::*/@*[name() = "xmlns:" || $prefix][last()]',
172
+ formElement,
173
+ null,
174
+ { prefix },
175
+ );
176
+ };
177
+
178
+ setCachedNamespaceResolver(xpathQuery, formElement, resolveNamespacePrefix);
179
+ return resolveNamespacePrefix;
180
+ }
181
+
182
+ function createNamespaceResolverForNode(query, contextNode, formElement) {
183
+ if (((contextNode && contextNode.ownerDocument) || contextNode) === window.document) {
184
+ // Running a query on the HTML DOM. Don't bother resolving namespaces in any other way
185
+ return xhtmlNamespaceResolver;
186
+ }
187
+ return createNamespaceResolver(query, formElement);
55
188
  }
56
189
 
57
190
  /**
@@ -62,6 +195,7 @@ function resolveNamespacePrefix(contextElement, prefix) {
62
195
  function functionNameResolver({ prefix, localName }, _arity) {
63
196
  switch (localName) {
64
197
  // TODO: put the full XForms library functions set here
198
+ case 'context':
65
199
  case 'base64encode':
66
200
  case 'boolean-from-string':
67
201
  case 'current':
@@ -92,13 +226,15 @@ function functionNameResolver({ prefix, localName }, _arity) {
92
226
  * @param {{parentNode}|ForeElementMixin} formElement The form element associated to the XPath
93
227
  */
94
228
  export function evaluateXPath(xpath, contextNode, formElement, variables = {}) {
229
+ const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
230
+
95
231
  return fxEvaluateXPath(xpath, contextNode, null, variables, 'xs:anyType', {
96
232
  currentContext: { formElement, variables },
97
233
  moduleImports: {
98
234
  xf: XFORMS_NAMESPACE_URI,
99
235
  },
100
236
  functionNameResolver,
101
- namespaceResolver: prefix => resolveNamespacePrefix(formElement, prefix),
237
+ namespaceResolver,
102
238
  });
103
239
  }
104
240
 
@@ -111,18 +247,19 @@ export function evaluateXPath(xpath, contextNode, formElement, variables = {}) {
111
247
  * @return {Node} The first node found by the XPath
112
248
  */
113
249
  export function evaluateXPathToFirstNode(xpath, contextNode, formElement) {
250
+ const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
114
251
  return fxEvaluateXPathToFirstNode(
115
252
  xpath,
116
253
  contextNode,
117
254
  null,
118
255
  {},
119
256
  {
120
- namespaceResolver: prefix => resolveNamespacePrefix(formElement, prefix),
121
257
  defaultFunctionNamespaceURI: XFORMS_NAMESPACE_URI,
122
258
  moduleImports: {
123
259
  xf: XFORMS_NAMESPACE_URI,
124
260
  },
125
261
  currentContext: { formElement },
262
+ namespaceResolver,
126
263
  },
127
264
  );
128
265
  }
@@ -136,6 +273,7 @@ export function evaluateXPathToFirstNode(xpath, contextNode, formElement) {
136
273
  * @return {Node[]} All nodes
137
274
  */
138
275
  export function evaluateXPathToNodes(xpath, contextNode, formElement) {
276
+ const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
139
277
  return fxEvaluateXPathToNodes(
140
278
  xpath,
141
279
  contextNode,
@@ -147,7 +285,7 @@ export function evaluateXPathToNodes(xpath, contextNode, formElement) {
147
285
  moduleImports: {
148
286
  xf: XFORMS_NAMESPACE_URI,
149
287
  },
150
- namespaceResolver: prefix => resolveNamespacePrefix(formElement, prefix),
288
+ namespaceResolver,
151
289
  },
152
290
  );
153
291
  }
@@ -161,6 +299,7 @@ export function evaluateXPathToNodes(xpath, contextNode, formElement) {
161
299
  * @return {boolean}
162
300
  */
163
301
  export function evaluateXPathToBoolean(xpath, contextNode, formElement) {
302
+ const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
164
303
  return fxEvaluateXPathToBoolean(
165
304
  xpath,
166
305
  contextNode,
@@ -172,7 +311,7 @@ export function evaluateXPathToBoolean(xpath, contextNode, formElement) {
172
311
  moduleImports: {
173
312
  xf: XFORMS_NAMESPACE_URI,
174
313
  },
175
- namespaceResolver: prefix => resolveNamespacePrefix(formElement, prefix),
314
+ namespaceResolver,
176
315
  },
177
316
  );
178
317
  }
@@ -195,6 +334,7 @@ export function evaluateXPathToString(
195
334
  domFacade = null,
196
335
  namespaceReferenceNode = formElement,
197
336
  ) {
337
+ const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
198
338
  return fxEvaluateXPathToString(
199
339
  xpath,
200
340
  contextNode,
@@ -207,7 +347,7 @@ export function evaluateXPathToString(
207
347
  moduleImports: {
208
348
  xf: XFORMS_NAMESPACE_URI,
209
349
  },
210
- namespaceResolver: prefix => resolveNamespacePrefix(namespaceReferenceNode, prefix),
350
+ namespaceResolver,
211
351
  },
212
352
  );
213
353
  }
@@ -230,6 +370,7 @@ export function evaluateXPathToNumber(
230
370
  domFacade = null,
231
371
  namespaceReferenceNode = formElement,
232
372
  ) {
373
+ const namespaceResolver = createNamespaceResolverForNode(xpath, contextNode, formElement);
233
374
  return fxEvaluateXPathToNumber(
234
375
  xpath,
235
376
  contextNode,
@@ -241,22 +382,15 @@ export function evaluateXPathToNumber(
241
382
  moduleImports: {
242
383
  xf: XFORMS_NAMESPACE_URI,
243
384
  },
244
- namespaceResolver: prefix => resolveNamespacePrefix(namespaceReferenceNode, prefix),
385
+ namespaceResolver,
245
386
  },
246
387
  );
247
388
  }
248
389
 
249
- const xhtmlNamespaceResolver = prefix => {
250
- if (!prefix) {
251
- return 'http://www.w3.org/1999/xhtml';
252
- }
253
- return undefined;
254
- };
255
-
256
390
  /**
257
391
  * Resolve an id in scope. Behaves like the algorithm defined on https://www.w3.org/community/xformsusers/wiki/XForms_2.0#idref-resolve
258
392
  */
259
- function resolveId(id, sourceObject, nodeName = null) {
393
+ export function resolveId(id, sourceObject, nodeName = null) {
260
394
  const allMatchingTargetObjects = fxEvaluateXPathToNodes(
261
395
  'outermost(ancestor-or-self::fx-fore[1]/(descendant::xf-fore|descendant::*[@id = $id]))[not(self::fx-fore)]',
262
396
  sourceObject,
@@ -315,41 +449,64 @@ function resolveId(id, sourceObject, nodeName = null) {
315
449
  const foundTargetObjects = allMatchingTargetObjects.filter(to =>
316
450
  ancestorRepeatItem.contains(to),
317
451
  );
318
- if (foundTargetObjects.length === 0) {
319
- continue;
320
- }
321
- if (foundTargetObjects.length === 1) {
322
- // A single one is found: the target object is directly in a common repeat
323
- const targetObject = foundTargetObjects[0];
324
- if (nodeName && targetObject.localName !== nodeName) {
325
- return null;
452
+ switch (foundTargetObjects.length) {
453
+ case 0:
454
+ // Nothing found: ignore
455
+ break;
456
+ case 1: {
457
+ // A single one is found: the target object is directly in a common repeat
458
+ const targetObject = foundTargetObjects[0];
459
+ if (nodeName && targetObject.localName !== nodeName) {
460
+ return null;
461
+ }
462
+ return targetObject;
463
+ }
464
+ default: {
465
+ // Multiple target objects are found: they are in a repeat that is not common with the source object
466
+ // 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
467
+ const targetObject = foundTargetObjects.find(to =>
468
+ fxEvaluateXPathToNodes(
469
+ 'every $ancestor of ancestor::fx-repeatitem satisfies $ancestor is $ancestor/../child::fx-repeatitem[../@repeat-index]',
470
+ to,
471
+ null,
472
+ {},
473
+ ),
474
+ );
475
+ if (!targetObject) {
476
+ // Nothing valid found for whatever reason. This might be something dynamic?
477
+ return null;
478
+ }
479
+ if (nodeName && targetObject.localName !== nodeName) {
480
+ return null;
481
+ }
482
+ return targetObject;
326
483
  }
327
- return targetObject;
328
- }
329
-
330
- // Multiple target objects are found: they are in a repeat that is not common with the source object
331
- // 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
332
- const targetObject = foundTargetObjects.find(to =>
333
- fxEvaluateXPathToNodes(
334
- 'every $ancestor of ancestor::fx-repeatitem satisfies $ancestor is $ancestor/../child::fx-repeatitem[../@repeat-index]',
335
- to,
336
- null,
337
- {},
338
- ),
339
- );
340
- if (!targetObject) {
341
- // Nothing valid found for whatever reason. This might be something dynamic?
342
- return null;
343
- }
344
- if (nodeName && targetObject.localName !== nodeName) {
345
- return null;
346
484
  }
347
- return targetObject;
348
485
  }
349
486
  // We found no target objects in common repeats. The id is unresolvable
350
487
  return null;
351
488
  }
352
489
 
490
+ /**
491
+ * @param id as string
492
+ * @return instance data for given id serialized to string.
493
+ */
494
+ registerCustomXPathFunction(
495
+ { namespaceURI: XFORMS_NAMESPACE_URI, localName: 'context' },
496
+ [],
497
+ 'item()?',
498
+ (dynamicContext, string) => {
499
+ const caller = dynamicContext.currentContext.formElement;
500
+ const parent = XPathUtil.getParentBindingElement(caller);
501
+ // const instance = resolveId('default', caller, 'fx-instance');
502
+ const p = caller.nodeName;
503
+ // const p = dynamicContext.domFacade.getParentElement();
504
+
505
+ if (parent) return parent;
506
+ return caller.getInScopeContext();
507
+ },
508
+ );
509
+
353
510
  /**
354
511
  * @param id as string
355
512
  * @return instance data for given id serialized to string.
@@ -362,8 +519,13 @@ registerCustomXPathFunction(
362
519
  const { formElement } = dynamicContext.currentContext;
363
520
  const instance = resolveId(string, formElement, 'fx-instance');
364
521
  if (instance) {
365
- const def = new XMLSerializer().serializeToString(instance.getDefaultContext());
366
- return def;
522
+ if (instance.getAttribute('type') === 'json') {
523
+ console.warn('log() does not work for JSON yet');
524
+ // return JSON.stringify(instance.getDefaultContext());
525
+ } else {
526
+ const def = new XMLSerializer().serializeToString(instance.getDefaultContext());
527
+ return prettifyXml(def);
528
+ }
367
529
  }
368
530
  return null;
369
531
  },
@@ -500,7 +662,7 @@ registerCustomXPathFunction(
500
662
  if (repeat) {
501
663
  return repeat.getAttribute('index');
502
664
  }
503
- return 1;
665
+ return Number(1);
504
666
  },
505
667
  );
506
668
 
@@ -554,26 +716,26 @@ registerXQueryModule(`
554
716
 
555
717
  // How to run XQUERY:
556
718
  /**
557
- registerXQueryModule(`
558
- module namespace my-custom-namespace = "my-custom-uri";
559
- (:~
560
- Insert attribute somewhere
561
- ~:)
562
- declare %public %updating function my-custom-namespace:do-something ($ele as element()) as xs:boolean {
719
+ registerXQueryModule(`
720
+ module namespace my-custom-namespace = "my-custom-uri";
721
+ (:~
722
+ Insert attribute somewhere
723
+ ~:)
724
+ declare %public %updating function my-custom-namespace:do-something ($ele as element()) as xs:boolean {
563
725
  if ($ele/@done) then false() else
564
726
  (insert node
565
727
  attribute done {"true"}
566
728
  into $ele, true())
567
729
  };
568
- `)
569
- // At some point:
570
- const contextNode = null;
571
- const pendingUpdatesAndXdmValue = evaluateUpdatingExpressionSync('ns:do-something(.)', contextNode, null, null, {moduleImports: {'ns': 'my-custom-uri'}})
730
+ `)
731
+ // At some point:
732
+ const contextNode = null;
733
+ const pendingUpdatesAndXdmValue = evaluateUpdatingExpressionSync('ns:do-something(.)', contextNode, null, null, {moduleImports: {'ns': 'my-custom-uri'}})
572
734
 
573
- console.log(pendingUpdatesAndXdmValue.xdmValue); // this is true or false, see function
735
+ console.log(pendingUpdatesAndXdmValue.xdmValue); // this is true or false, see function
574
736
 
575
- executePendingUpdateList(pendingUpdatesAndXdmValue.pendingUpdateList, null, null, null);
576
- */
737
+ executePendingUpdateList(pendingUpdatesAndXdmValue.pendingUpdateList, null, null, null);
738
+ */
577
739
 
578
740
  /**
579
741
  * @param input as string
package/src/xpath-util.js CHANGED
@@ -9,14 +9,54 @@ import * as fx from 'fontoxpath';
9
9
  */
10
10
 
11
11
  export class XPathUtil {
12
+ static getParentBindingElement(start) {
13
+ /* if (start.parentNode.host) {
14
+ const { host } = start.parentNode;
15
+ if (host.hasAttribute('ref')) {
16
+ return host;
17
+ }
18
+ } else */
19
+ if (start.parentNode && start.parentNode.nodeType !== Node.DOCUMENT_NODE) {
20
+ if (start.parentNode.hasAttribute('ref')) {
21
+ return start.parentNode;
22
+ }
23
+ XPathUtil.getParentBindingElement(start.parentNode);
24
+ }
25
+ return null;
26
+ }
27
+
12
28
  static isAbsolutePath(path) {
13
29
  return path != null && (path.startsWith('/') || path.startsWith('instance('));
14
30
  }
15
31
 
32
+ static isRepeated(element) {
33
+ return element.parentElement.closest('fx-repeatitem');
34
+ }
35
+
16
36
  static isSelfReference(ref) {
17
37
  return ref === '.' || ref === './text()' || ref === 'text()' || ref === '' || ref === null;
18
38
  }
19
39
 
40
+ static getDefaultInstance(boundElement) {
41
+ // const fore = boundElement.closest('fx-fore');
42
+ const fore = XPathUtil.getForeElement(boundElement);
43
+ const defaultInstance = fore.querySelector('fx-instance');
44
+ if (!defaultInstance) {
45
+ throw new Error('no default instance present');
46
+ }
47
+ return defaultInstance;
48
+ }
49
+
50
+ static getForeElement(start) {
51
+ if (start.nodeName === 'FX-FORE') {
52
+ return start;
53
+ }
54
+ if (start.parentNode) {
55
+ return XPathUtil.getForeElement(start.parentNode);
56
+ }
57
+ throw new Error('no Fore element present');
58
+ }
59
+
20
60
  // todo: this will need more work to look upward for instance() expr.
21
61
  static getInstanceId(ref) {
22
62
  if (!ref) {
@@ -32,25 +72,20 @@ export class XPathUtil {
32
72
  // todo: certainly not ideal to rely on duplicating instance id on instance document - better way later ;)
33
73
  static getPath(node) {
34
74
  const path = fx.evaluateXPath('path()', node);
75
+ /*
35
76
  const instanceId = node.ownerDocument.firstElementChild.getAttribute('id');
36
77
  if (instanceId !== null && instanceId !== 'default') {
37
78
  return `#${instanceId}${XPathUtil.shortenPath(path)}`;
38
79
  }
80
+ */
39
81
  return XPathUtil.shortenPath(path);
40
82
  }
41
83
 
42
84
  static shortenPath(path) {
43
- const steps = path.split('/');
44
- let result = '';
45
- for (let i = 2; i < steps.length; i += 1) {
46
- const step = steps[i];
47
- if (step.indexOf('{}') !== -1) {
48
- const q = step.split('{}');
49
- result += `/${q[1]}`;
50
- } else {
51
- result += `/${step}`;
52
- }
53
- }
54
- return result;
85
+ const tmp = path.replaceAll(/(Q{(.*?)\})/g, '');
86
+ // cut off leading slash
87
+ const tmp1 = tmp.substring(1, tmp.length);
88
+ // ### cut-off root node ref
89
+ return tmp1.substring(tmp1.indexOf('/'), tmp.length);
55
90
  }
56
91
  }