@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
package/src/drawdown.js CHANGED
@@ -4,72 +4,67 @@
4
4
  */
5
5
 
6
6
  function markdown(src) {
7
- var rx_lt = /</g;
8
- var rx_gt = />/g;
9
- var rx_space = /\t|\r|\uf8ff/g;
10
- var rx_escape = /\\([\\\|`*_{}\[\]()#+\-~])/g;
11
- var rx_hr = /^([*\-=_] *){3,}$/gm;
12
- var rx_blockquote = /\n *&gt; *([^]*?)(?=(\n|$){2})/g;
13
- var rx_list = /\n( *)(?:[*\-+]|((\d+)|([a-z])|[A-Z])[.)]) +([^]*?)(?=(\n|$){2})/g;
14
- var rx_listjoin = /<\/(ol|ul)>\n\n<\1>/g;
15
- var rx_highlight = /(^|[^A-Za-z\d\\])(([*_])|(~)|(\^)|(--)|(\+\+)|`)(\2?)([^<]*?)\2\8(?!\2)(?=\W|_|$)/g;
16
- var rx_code = /\n((```|~~~).*\n?([^]*?)\n?\2|(( .*?\n)+))/g;
17
- var rx_link = /((!?)\[(.*?)\]\((.*?)( ".*")?\)|\\([\\`*_{}\[\]()#+\-.!~]))/g;
18
- var rx_table = /\n(( *\|.*?\| *\n)+)/g;
19
- var rx_thead = /^.*\n( *\|( *\:?-+\:?-+\:? *\|)* *\n|)/;
20
- var rx_row = /.*\n/g;
21
- var rx_cell = /\||(.*?[^\\])\|/g;
22
- var rx_heading = /(?=^|>|\n)([>\s]*?)(#{1,6}) (.*?)( #*)? *(?=\n|$)/g;
23
- var rx_para = /(?=^|>|\n)\s*\n+([^<]+?)\n+\s*(?=\n|<|$)/g;
24
- var rx_stash = /-\d+\uf8ff/g;
7
+ const rx_lt = /</g;
8
+ const rx_gt = />/g;
9
+ const rx_space = /\t|\r|\uf8ff/g;
10
+ const rx_escape = /\\([\\\|`*_{}\[\]()#+\-~])/g;
11
+ const rx_hr = /^([*\-=_] *){3,}$/gm;
12
+ const rx_blockquote = /\n *&gt; *([^]*?)(?=(\n|$){2})/g;
13
+ const rx_list = /\n( *)(?:[*\-+]|((\d+)|([a-z])|[A-Z])[.)]) +([^]*?)(?=(\n|$){2})/g;
14
+ const rx_listjoin = /<\/(ol|ul)>\n\n<\1>/g;
15
+ const rx_highlight = /(^|[^A-Za-z\d\\])(([*_])|(~)|(\^)|(--)|(\+\+)|`)(\2?)([^<]*?)\2\8(?!\2)(?=\W|_|$)/g;
16
+ const rx_code = /\n((```|~~~).*\n?([^]*?)\n?\2|(( {4}.*?\n)+))/g;
17
+ const rx_link = /((!?)\[(.*?)\]\((.*?)( ".*")?\)|\\([\\`*_{}\[\]()#+\-.!~]))/g;
18
+ const rx_table = /\n(( *\|.*?\| *\n)+)/g;
19
+ const rx_thead = /^.*\n( *\|( *\:?-+\:?-+\:? *\|)* *\n|)/;
20
+ const rx_row = /.*\n/g;
21
+ const rx_cell = /\||(.*?[^\\])\|/g;
22
+ const rx_heading = /(?=^|>|\n)([>\s]*?)(#{1,6}) (.*?)( #*)? *(?=\n|$)/g;
23
+ const rx_para = /(?=^|>|\n)\s*\n+([^<]+?)\n+\s*(?=\n|<|$)/g;
24
+ const rx_stash = /-\d+\uf8ff/g;
25
25
 
26
26
  function replace(rex, fn) {
27
27
  src = src.replace(rex, fn);
28
28
  }
29
29
 
30
30
  function element(tag, content) {
31
- return '<' + tag + '>' + content + '</' + tag + '>';
31
+ return `<${tag}>${content}</${tag}>`;
32
32
  }
33
33
 
34
34
  function blockquote(src) {
35
- return src.replace(rx_blockquote, function(all, content) {
36
- return element('blockquote', blockquote(highlight(content.replace(/^ *&gt; */gm, ''))));
37
- });
35
+ return src.replace(rx_blockquote, (all, content) =>
36
+ element('blockquote', blockquote(highlight(content.replace(/^ *&gt; */gm, '')))),
37
+ );
38
38
  }
39
39
 
40
40
  function list(src) {
41
- return src.replace(rx_list, function(all, ind, ol, num, low, content) {
42
- var entry = element(
41
+ return src.replace(rx_list, (all, ind, ol, num, low, content) => {
42
+ const entry = element(
43
43
  'li',
44
44
  highlight(
45
45
  content
46
- .split(RegExp('\n ?' + ind + '(?:(?:\\d+|[a-zA-Z])[.)]|[*\\-+]) +', 'g'))
46
+ .split(RegExp(`\n ?${ind}(?:(?:\\d+|[a-zA-Z])[.)]|[*\\-+]) +`, 'g'))
47
47
  .map(list)
48
48
  .join('</li><li>'),
49
49
  ),
50
50
  );
51
51
 
52
- return (
53
- '\n' +
54
- (ol
55
- ? '<ol start="' +
56
- (num
57
- ? ol + '">'
58
- : parseInt(ol, 36) -
59
- 9 +
60
- '" style="list-style-type:' +
61
- (low ? 'low' : 'upp') +
62
- 'er-alpha">') +
63
- entry +
64
- '</ol>'
65
- : element('ul', entry))
66
- );
52
+ return `\n${
53
+ ol
54
+ ? `<ol start="${
55
+ num
56
+ ? `${ol}">`
57
+ : `${parseInt(ol, 36) - 9}" style="list-style-type:${low ? 'low' : 'upp'}er-alpha">`
58
+ }${entry}</ol>`
59
+ : element('ul', entry)
60
+ }`;
67
61
  });
68
62
  }
69
63
 
70
64
  function highlight(src) {
71
- return src.replace(rx_highlight, function(all, _, p1, emp, sub, sup, small, big, p2, content) {
72
- return (
65
+ return src.replace(
66
+ rx_highlight,
67
+ (all, _, p1, emp, sub, sup, small, big, p2, content) =>
73
68
  _ +
74
69
  element(
75
70
  emp
@@ -88,19 +83,18 @@ function markdown(src) {
88
83
  ? 'big'
89
84
  : 'code',
90
85
  highlight(content),
91
- )
92
- );
93
- });
86
+ ),
87
+ );
94
88
  }
95
89
 
96
90
  function unesc(str) {
97
91
  return str.replace(rx_escape, '$1');
98
92
  }
99
93
 
100
- var stash = [];
101
- var si = 0;
94
+ const stash = [];
95
+ let si = 0;
102
96
 
103
- src = '\n' + src + '\n';
97
+ src = `\n${src}\n`;
104
98
 
105
99
  replace(rx_lt, '&lt;');
106
100
  replace(rx_gt, '&gt;');
@@ -117,56 +111,47 @@ function markdown(src) {
117
111
  replace(rx_listjoin, '');
118
112
 
119
113
  // code
120
- replace(rx_code, function(all, p1, p2, p3, p4) {
121
- stash[--si] = element('pre', element('code', p3 || p4.replace(/^ /gm, '')));
122
- return si + '\uf8ff';
114
+ replace(rx_code, (all, p1, p2, p3, p4) => {
115
+ stash[--si] = element('pre', element('code', p3 || p4.replace(/^ {4}/gm, '')));
116
+ return `${si}\uf8ff`;
123
117
  });
124
118
 
125
119
  // link or image
126
- replace(rx_link, function(all, p1, p2, p3, p4, p5, p6) {
120
+ replace(rx_link, (all, p1, p2, p3, p4, p5, p6) => {
127
121
  stash[--si] = p4
128
122
  ? p2
129
- ? '<img src="' + p4 + '" alt="' + p3 + '"/>'
130
- : '<a href="' + p4 + '">' + unesc(highlight(p3)) + '</a>'
123
+ ? `<img src="${p4}" alt="${p3}"/>`
124
+ : `<a href="${p4}">${unesc(highlight(p3))}</a>`
131
125
  : p6;
132
- return si + '\uf8ff';
126
+ return `${si}\uf8ff`;
133
127
  });
134
128
 
135
129
  // table
136
- replace(rx_table, function(all, table) {
137
- var sep = table.match(rx_thead)[1];
138
- return (
139
- '\n' +
140
- element(
141
- 'table',
142
- table.replace(rx_row, function(row, ri) {
143
- return row == sep
144
- ? ''
145
- : element(
146
- 'tr',
147
- row.replace(rx_cell, function(all, cell, ci) {
148
- return ci ? element(sep && !ri ? 'th' : 'td', unesc(highlight(cell || ''))) : '';
149
- }),
150
- );
151
- }),
152
- )
153
- );
130
+ replace(rx_table, (all, table) => {
131
+ const sep = table.match(rx_thead)[1];
132
+ return `\n${element(
133
+ 'table',
134
+ table.replace(rx_row, (row, ri) =>
135
+ row == sep
136
+ ? ''
137
+ : element(
138
+ 'tr',
139
+ row.replace(rx_cell, (all, cell, ci) =>
140
+ ci ? element(sep && !ri ? 'th' : 'td', unesc(highlight(cell || ''))) : '',
141
+ ),
142
+ ),
143
+ ),
144
+ )}`;
154
145
  });
155
146
 
156
147
  // heading
157
- replace(rx_heading, function(all, _, p1, p2) {
158
- return _ + element('h' + p1.length, unesc(highlight(p2)));
159
- });
148
+ replace(rx_heading, (all, _, p1, p2) => _ + element(`h${p1.length}`, unesc(highlight(p2))));
160
149
 
161
150
  // paragraph
162
- replace(rx_para, function(all, content) {
163
- return element('p', unesc(highlight(content)));
164
- });
151
+ replace(rx_para, (all, content) => element('p', unesc(highlight(content))));
165
152
 
166
153
  // stash
167
- replace(rx_stash, function(all) {
168
- return stash[parseInt(all)];
169
- });
154
+ replace(rx_stash, all => stash[parseInt(all)]);
170
155
 
171
156
  return src.trim();
172
157
  }
package/src/fore.js CHANGED
@@ -127,7 +127,12 @@ export class Fore {
127
127
  const { children } = startElement;
128
128
  if (children) {
129
129
  Array.from(children).forEach(element => {
130
+ if (element.nodeName.toUpperCase() === 'FX-FORE') {
131
+ resolve('done');
132
+ }
130
133
  if (Fore.isUiElement(element.nodeName) && typeof element.refresh === 'function') {
134
+ // console.log('refreshing', element, element?.ref);
135
+ // console.log('refreshing ',element);
131
136
  element.refresh();
132
137
  } else if (element.nodeName.toUpperCase() !== 'FX-MODEL') {
133
138
  Fore.refreshChildren(element, force);
@@ -140,14 +145,21 @@ export class Fore {
140
145
  return refreshed;
141
146
  }
142
147
 
143
- static isRepeated(element) {
144
- return element.closest('fx-repeatitem') !== null;
145
- }
146
-
147
- static getRepeatTarget(element, id) {
148
- const repeatContextItem = element.closest('fx-repeatitem');
149
- const target = repeatContextItem.querySelector(`#${id}`);
150
- return target;
148
+ /**
149
+ * Alternative to `closest` that respects subcontrol boundaries
150
+ */
151
+ static getClosest(querySelector, start) {
152
+ while (!start.matches(querySelector)) {
153
+ if (start.matches('fx-fore')) {
154
+ // Subform reached. Bail out
155
+ return null;
156
+ }
157
+ start = start.parentNode;
158
+ if (!start) {
159
+ return null;
160
+ }
161
+ }
162
+ return start;
151
163
  }
152
164
 
153
165
  /**
@@ -193,13 +205,11 @@ export class Fore {
193
205
  static fadeOutElement(element, duration) {
194
206
  // const duration = duration;
195
207
  let fadeOut = () => {
196
-
197
208
  // Stop all current animations
198
209
  if (element.getAnimations) {
199
210
  element.getAnimations().map(anim => anim.finish());
200
211
  }
201
212
 
202
-
203
213
  // Play the animation with the newly specified duration
204
214
  fadeOut = element.animate(
205
215
  {
@@ -214,33 +224,36 @@ export class Fore {
214
224
 
215
225
  static dispatch(target, eventName, detail) {
216
226
  const event = new CustomEvent(eventName, {
217
- composed: true,
227
+ composed: false,
218
228
  bubbles: true,
219
229
  detail,
220
230
  });
221
- console.log('firing', event);
231
+ console.log('dispatching', event);
222
232
  target.dispatchEvent(event);
223
233
  }
224
234
 
225
235
  static prettifyXml(source) {
226
236
  const xmlDoc = new DOMParser().parseFromString(source, 'application/xml');
227
237
  const xsltDoc = new DOMParser().parseFromString(
228
- [
229
- // describes how we want to modify the XML - indent everything
230
- '<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">',
231
- ' <xsl:strip-space elements="*"/>',
232
- ' <xsl:template match="para[content-style][not(text())]">', // change to just text() to strip space in text nodes
233
- ' <xsl:value-of select="normalize-space(.)"/>',
234
- ' </xsl:template>',
235
- ' <xsl:template match="node()|@*">',
236
- ' <xsl:copy><xsl:apply-templates select="node()|@*"/></xsl:copy>',
237
- ' </xsl:template>',
238
- ' <xsl:output indent="yes"/>',
239
- '</xsl:stylesheet>',
240
- ].join('\n'),
241
- 'application/xml',
238
+ [
239
+ // describes how we want to modify the XML - indent everything
240
+ '<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">',
241
+ ' <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>',
242
+ ' <xsl:strip-space elements="*"/>',
243
+ ' <xsl:template match="text()">', // change to just text() to strip space in text nodes
244
+ ' <xsl:value-of select="normalize-space(.)"/>',
245
+ ' </xsl:template>',
246
+ ' <xsl:template match="node()|@*">',
247
+ ' <xsl:copy>',
248
+ ' <xsl:apply-templates select="node()|@*"/>',
249
+ ' </xsl:copy>',
250
+ ' </xsl:template>',
251
+ '</xsl:stylesheet>',
252
+ ].join('\n'),
253
+ 'application/xml',
242
254
  );
243
255
 
256
+
244
257
  const xsltProcessor = new XSLTProcessor();
245
258
  xsltProcessor.importStylesheet(xsltDoc);
246
259
  const resultDoc = xsltProcessor.transformToDocument(xmlDoc);
@@ -248,6 +261,110 @@ export class Fore {
248
261
  return resultXml;
249
262
  }
250
263
 
264
+ static formatXml (xml) {
265
+ var reg = /(>)(<)(\/*)/g;
266
+ var wsexp = / *(.*) +\n/g;
267
+ var contexp = /(<.+>)(.+\n)/g;
268
+ xml = xml.replace(reg, '$1\n$2$3').replace(wsexp, '$1\n').replace(contexp, '$1\n$2');
269
+ var pad = 0;
270
+ var formatted = '';
271
+ var lines = xml.split('\n');
272
+ var indent = 0;
273
+ var lastType = 'other';
274
+ // 4 types of tags - single, closing, opening, other (text, doctype, comment) - 4*4 = 16 transitions
275
+ var transitions = {
276
+ 'single->single': 0,
277
+ 'single->closing': -1,
278
+ 'single->opening': 0,
279
+ 'single->other': 0,
280
+ 'closing->single': 0,
281
+ 'closing->closing': -1,
282
+ 'closing->opening': 0,
283
+ 'closing->other': 0,
284
+ 'opening->single': 1,
285
+ 'opening->closing': 0,
286
+ 'opening->opening': 1,
287
+ 'opening->other': 1,
288
+ 'other->single': 0,
289
+ 'other->closing': -1,
290
+ 'other->opening': 0,
291
+ 'other->other': 0
292
+ };
293
+
294
+ for (var i = 0; i < lines.length; i++) {
295
+ var ln = lines[i];
296
+ var single = Boolean(ln.match(/<.+\/>/)); // is this line a single tag? ex. <br />
297
+ var closing = Boolean(ln.match(/<\/.+>/)); // is this a closing tag? ex. </a>
298
+ var opening = Boolean(ln.match(/<[^!].*>/)); // is this even a tag (that's not <!something>)
299
+ var type = single ? 'single' : closing ? 'closing' : opening ? 'opening' : 'other';
300
+ var fromTo = lastType + '->' + type;
301
+ lastType = type;
302
+ var padding = '';
303
+
304
+ indent += transitions[fromTo];
305
+ for (var j = 0; j < indent; j++) {
306
+ padding += ' ';
307
+ }
308
+
309
+ formatted += padding + ln + '\n';
310
+ }
311
+ }
312
+
313
+ static async loadForeFromUrl(hostElement, url) {
314
+ console.log('########## loading Fore from ', this.src, '##########');
315
+ await fetch(url, {
316
+ method: 'GET',
317
+ mode: 'cors',
318
+ credentials: 'include',
319
+ headers: {
320
+ 'Content-Type': 'text/html',
321
+ },
322
+ })
323
+ .then(response => {
324
+ const responseContentType = response.headers.get('content-type').toLowerCase();
325
+ console.log('********** responseContentType *********', responseContentType);
326
+ if (responseContentType.startsWith('text/html')) {
327
+ return response.text().then(result =>
328
+ // console.log('xml ********', result);
329
+ new DOMParser().parseFromString(result, 'text/html'),
330
+ );
331
+ }
332
+ return 'done';
333
+ })
334
+ .then(data => {
335
+ // const theFore = fxEvaluateXPathToFirstNode('//fx-fore', data.firstElementChild);
336
+ const theFore = data.querySelector('fx-fore');
337
+ // console.log('thefore', theFore)
338
+ if (!theFore) {
339
+ hostElement.dispatchEvent(
340
+ new CustomEvent('error', {
341
+ composed: false,
342
+ bubbles: true,
343
+ detail: {
344
+ message: 'cyclic graph',
345
+ },
346
+ }),
347
+ );
348
+ }
349
+ hostElement.appendChild(theFore);
350
+ theFore.classList.add('widget');
351
+ // return theFore;
352
+ // theFore.setAttribute('from-src', this.src);
353
+ // this.replaceWith(theFore);
354
+ })
355
+ .catch(error => {
356
+ hostElement.dispatchEvent(
357
+ new CustomEvent('error', {
358
+ composed: false,
359
+ bubbles: true,
360
+ detail: {
361
+ error: error,
362
+ message: `'${url}' not found or does not contain Fore element.`,
363
+ },
364
+ }),
365
+ );
366
+ });
367
+ }
251
368
 
252
369
  /**
253
370
  * clear all text nodes and attribute values to get a 'clean' template.
@@ -1,6 +1,6 @@
1
1
  import { registerCustomXPathFunction } from 'fontoxpath';
2
2
  import { foreElementMixin } from '../ForeElementMixin.js';
3
- import { evaluateXPath } from '../xpath-evaluation.js';
3
+ import { evaluateXPath, globallyDeclaredFunctionLocalNames } from '../xpath-evaluation.js';
4
4
 
5
5
  /**
6
6
  * Allows to extend a form with local custom functions.
@@ -41,10 +41,16 @@ export class FxFunction extends foreElementMixin(HTMLElement) {
41
41
 
42
42
  // TODO: lookup prefix
43
43
  const functionIdentifier =
44
- prefix === 'local'
44
+ prefix === 'local' || !prefix
45
45
  ? { namespaceURI: 'http://www.w3.org/2005/xquery-local-functions', localName }
46
46
  : `${prefix}:${localName}`;
47
47
 
48
+ // Make the function available globally w/o a prefix. See the functionNameResolver for for how
49
+ // this is picked up
50
+ if (!prefix) {
51
+ globallyDeclaredFunctionLocalNames.push(localName);
52
+ }
53
+
48
54
  const paramParts = params
49
55
  ? params.split(',').map(param => {
50
56
  const match = param.match(/(?<variableName>\$[^\s]+)(?:\sas\s(?<varType>.+))/);
@@ -77,7 +83,12 @@ export class FxFunction extends foreElementMixin(HTMLElement) {
77
83
  break;
78
84
  }
79
85
 
86
+ case 'text/xquf':
87
+ case 'text/xquery':
80
88
  case 'text/xpath': {
89
+ const language = type === 'text/xpath' ?
90
+ 'XPath3.1' : type === 'text/xquery' ?
91
+ 'XQuery3.1' : 'XQueryUpdate3.1';
81
92
  const fun = (domFacade, ...args) =>
82
93
  evaluateXPath(
83
94
  this.functionBody,
@@ -87,6 +98,7 @@ export class FxFunction extends foreElementMixin(HTMLElement) {
87
98
  variablesByName[paramPart.variableName.replace('$', '')] = args[i];
88
99
  return variablesByName;
89
100
  }, {}),
101
+ {language}
90
102
  );
91
103
  registerCustomXPathFunction(
92
104
  functionIdentifier,
@@ -102,4 +114,6 @@ export class FxFunction extends foreElementMixin(HTMLElement) {
102
114
  }
103
115
  }
104
116
  }
105
- customElements.define('fx-function', FxFunction);
117
+ if (!customElements.get('fx-function')) {
118
+ customElements.define('fx-function', FxFunction);
119
+ }