@jinntec/fore 2.1.1 → 2.3.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.
- package/README.md +2 -2
- package/dist/fore-dev.js +2 -2
- package/dist/fore-dev.js.map +1 -1
- package/dist/fore.js +2 -2
- package/dist/fore.js.map +1 -1
- package/index.js +4 -2
- package/package.json +10 -38
- package/resources/fore.css +263 -262
- package/resources/vars.css +8 -0
- package/src/DependencyNotifyingDomFacade.js +1 -0
- package/src/ForeElementMixin.js +243 -228
- package/src/actions/abstract-action.js +407 -398
- package/src/actions/fx-action.js +6 -6
- package/src/actions/fx-append.js +1 -1
- package/src/actions/fx-call.js +63 -62
- package/src/actions/fx-confirm.js +11 -11
- package/src/actions/fx-construct-done.js +4 -4
- package/src/actions/fx-copy.js +36 -36
- package/src/actions/fx-delete.js +77 -79
- package/src/actions/fx-dispatch.js +14 -14
- package/src/actions/fx-hide.js +15 -15
- package/src/actions/fx-insert.js +9 -12
- package/src/actions/fx-insertchild.js +88 -0
- package/src/actions/fx-load.js +188 -185
- package/src/actions/fx-message.js +18 -19
- package/src/actions/fx-refresh.js +10 -10
- package/src/actions/fx-reload.js +16 -14
- package/src/actions/fx-replace.js +0 -1
- package/src/actions/fx-reset.js +31 -31
- package/src/actions/fx-send.js +59 -52
- package/src/actions/fx-setattribute.js +48 -49
- package/src/actions/fx-setfocus.js +54 -58
- package/src/actions/fx-setvalue.js +85 -81
- package/src/actions/fx-show.js +11 -11
- package/src/actions/fx-toggle.js +2 -2
- package/src/actions/fx-toggleboolean.js +38 -39
- package/src/actions/fx-unmodified.js +27 -0
- package/src/actions/fx-update.js +6 -6
- package/src/dep_graph.js +1 -1
- package/src/drawdown.js +23 -30
- package/src/events.js +19 -19
- package/src/fore.js +143 -125
- package/src/functions/common-function.js +24 -25
- package/src/functions/fx-function.js +6 -99
- package/src/functions/fx-functionlib.js +56 -0
- package/src/functions/registerFunction.js +112 -0
- package/src/fx-bind.js +19 -24
- package/src/fx-connection.js +226 -0
- package/src/fx-fore.js +844 -815
- package/src/fx-header.js +4 -4
- package/src/fx-instance.js +25 -29
- package/src/fx-model.js +405 -380
- package/src/fx-submission.js +399 -397
- package/src/fx-var.js +13 -12
- package/src/getInScopeContext.js +102 -89
- package/src/json-util.js +24 -24
- package/src/lab/fore-component.js +48 -52
- package/src/lab/instance-inspector.js +13 -16
- package/src/modelitem.js +48 -10
- package/src/relevance.js +12 -16
- package/src/tools/adi.js +858 -812
- package/src/tools/fx-action-log.js +377 -295
- package/src/tools/fx-devtools.js +210 -210
- package/src/tools/fx-dom-inspector.js +123 -122
- package/src/tools/fx-json-instance.js +262 -253
- package/src/tools/fx-log-item.js +6 -11
- package/src/tools/fx-log-settings.js +358 -301
- package/src/tools/fx-minimap.js +186 -177
- package/src/tools/helpers.js +1 -1
- package/src/ui/abstract-control.js +73 -53
- package/src/ui/fx-case.js +59 -7
- package/src/ui/fx-container.js +13 -14
- package/src/ui/fx-control.js +572 -538
- package/src/ui/fx-dialog.js +3 -3
- package/src/ui/fx-droptarget.js +3 -4
- package/src/ui/fx-group.js +4 -2
- package/src/ui/fx-hint.js +3 -0
- package/src/ui/fx-inspector.js +5 -5
- package/src/ui/fx-items.js +11 -11
- package/src/ui/fx-output.js +10 -12
- package/src/ui/fx-repeat-attributes.js +23 -27
- package/src/ui/fx-repeat.js +347 -344
- package/src/ui/fx-repeatitem.js +75 -68
- package/src/ui/fx-switch.js +39 -14
- package/src/ui/fx-trigger.js +4 -4
- package/src/withDraggability.js +193 -191
- package/src/xpath-evaluation.js +969 -958
- package/src/xpath-util.js +161 -134
package/src/fore.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
import getInScopeContext from
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import getInScopeContext from './getInScopeContext.js';
|
|
2
|
+
import {
|
|
3
|
+
evaluateXPath,
|
|
4
|
+
evaluateXPathToFirstNode,
|
|
5
|
+
evaluateXPathToString,
|
|
6
|
+
evaluateXPathToNodes,
|
|
7
|
+
} from './xpath-evaluation.js';
|
|
8
|
+
import { XPathUtil } from './xpath-util.js';
|
|
4
9
|
|
|
5
10
|
/**
|
|
6
11
|
* Class hosting common utility functions used throughout all fore elements
|
|
@@ -18,34 +23,33 @@ export class Fore {
|
|
|
18
23
|
|
|
19
24
|
static TYPE_DEFAULT = 'xs:string';
|
|
20
25
|
|
|
21
|
-
|
|
26
|
+
/**
|
|
22
27
|
* Loads and return a piece of HTML
|
|
23
|
-
* @param url - the Url to load from
|
|
28
|
+
* @param url {String} - the Url to load from
|
|
24
29
|
* @returns {Promise<string>}
|
|
25
30
|
*/
|
|
26
|
-
static async loadHtml(url){
|
|
27
|
-
try{
|
|
28
|
-
const response = await fetch(url,
|
|
31
|
+
static async loadHtml(url) {
|
|
32
|
+
try {
|
|
33
|
+
const response = await fetch(url, {
|
|
29
34
|
method: 'GET',
|
|
30
35
|
mode: 'cors',
|
|
31
36
|
credentials: 'same-origin',
|
|
32
37
|
headers: {
|
|
33
|
-
'Content-Type':
|
|
38
|
+
'Content-Type': 'text/html',
|
|
34
39
|
},
|
|
35
40
|
});
|
|
36
41
|
const responseContentType = response.headers.get('content-type').toLowerCase();
|
|
37
42
|
if (responseContentType.startsWith('text/html')) {
|
|
38
43
|
return response.text();
|
|
39
|
-
} else {
|
|
40
|
-
Fore.dispatch(this, 'error', {
|
|
41
|
-
message: `Response has wrong contentType '${responseContentType}'. Should be 'text/html'`,
|
|
42
|
-
level:'Error'
|
|
43
|
-
});
|
|
44
44
|
}
|
|
45
|
-
|
|
45
|
+
Fore.dispatch(this, 'error', {
|
|
46
|
+
message: `Response has wrong contentType '${responseContentType}'. Should be 'text/html'`,
|
|
47
|
+
level: 'Error',
|
|
48
|
+
});
|
|
49
|
+
} catch (error) {
|
|
46
50
|
Fore.dispatch(this, 'error', {
|
|
47
51
|
message: `Html couldn't be loaded from '${url}'`,
|
|
48
|
-
level:'Error'
|
|
52
|
+
level: 'Error',
|
|
49
53
|
});
|
|
50
54
|
}
|
|
51
55
|
}
|
|
@@ -54,75 +58,80 @@ export class Fore {
|
|
|
54
58
|
* loads a Fore element from given `src`. Always returns the first occurrence of a `<fx-fore>`. The retured element
|
|
55
59
|
* will replace the `replace` element in the DOM.
|
|
56
60
|
*
|
|
57
|
-
* @param replace the element with a `src` attribute to resolvé.
|
|
58
|
-
* @param src the Url to resolve
|
|
59
|
-
* @param selector a querySelector expression to fetch certain element from loaded document
|
|
60
|
-
* @returns {Promise<
|
|
61
|
+
* @param {Element} replace the element with a `src` attribute to resolvé.
|
|
62
|
+
* @param {string} src the Url to resolve
|
|
63
|
+
* @param {string} selector a querySelector expression to fetch certain element from loaded document
|
|
64
|
+
* @returns {Promise<HTMLElement>} The replacement element
|
|
61
65
|
*/
|
|
62
|
-
static async loadForeFromSrc(replace, src, selector){
|
|
63
|
-
if(!src){
|
|
66
|
+
static async loadForeFromSrc(replace, src, selector) {
|
|
67
|
+
if (!src) {
|
|
64
68
|
Fore.dispatch(this, 'error', {
|
|
65
69
|
detail: {
|
|
66
|
-
message:
|
|
70
|
+
message: "No 'src' attribute present",
|
|
67
71
|
},
|
|
68
72
|
});
|
|
73
|
+
return null;
|
|
69
74
|
}
|
|
70
|
-
await Fore.loadHtml(src)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
});
|
|
75
|
+
const data = await Fore.loadHtml(src);
|
|
76
|
+
const parsed = new DOMParser().parseFromString(data, 'text/html');
|
|
77
|
+
// const theFore = parsed.querySelector('fx-fore');
|
|
78
|
+
const foreElement = parsed.querySelector(selector);
|
|
79
|
+
// console.log('foreElement', foreElement)
|
|
80
|
+
if (!foreElement) {
|
|
81
|
+
Fore.dispatch(this, 'error', {
|
|
82
|
+
detail: {
|
|
83
|
+
message: `Fore element not found in '${src}'. Maybe wrapped within 'template' element?`,
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
foreElement.setAttribute('from-src', src);
|
|
89
|
+
const thisAttrs = replace.attributes;
|
|
90
|
+
Array.from(thisAttrs).forEach(attr => {
|
|
91
|
+
if (attr.name !== 'src') {
|
|
92
|
+
foreElement.setAttribute(attr.name, attr.value);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
replace.replaceWith(foreElement);
|
|
96
|
+
return foreElement;
|
|
93
97
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
98
|
+
|
|
99
|
+
static buildPredicates(node) {
|
|
100
|
+
let attrPredicate = '';
|
|
101
|
+
Array.from(node.attributes).forEach(attr => {
|
|
97
102
|
// attrMap.set(attr.nodeName,attr.nodeValue);
|
|
98
103
|
// if(attr.nodeName !== 'xmlns'){
|
|
99
104
|
// if(attr.nodeValue !== ''){
|
|
100
105
|
// attrPredicate += `[@${attr.nodeName}='${attr.nodeValue}']`;
|
|
101
106
|
// }else{
|
|
102
|
-
|
|
103
|
-
|
|
107
|
+
attrPredicate += `[@${attr.nodeName}]`;
|
|
108
|
+
// }
|
|
104
109
|
// }
|
|
105
110
|
});
|
|
106
|
-
return attrPredicate
|
|
111
|
+
return attrPredicate;
|
|
107
112
|
}
|
|
108
113
|
|
|
109
|
-
|
|
110
114
|
/**
|
|
111
115
|
* returns true if target element is the widget itself or some element within the widget.
|
|
112
|
-
* @param target an event target
|
|
116
|
+
* @param {HTMLElement} target an event target
|
|
113
117
|
* @returns {boolean}
|
|
114
118
|
*/
|
|
115
119
|
static isWidget(target) {
|
|
116
|
-
if(target?.classList.contains(
|
|
120
|
+
if (target?.classList.contains('widget')) return true;
|
|
117
121
|
let parent = target.parentNode;
|
|
118
|
-
while(parent && parent.nodeName !== 'FX-CONTROL'){
|
|
119
|
-
if(parent?.classList?.contains('widget')) return true;
|
|
122
|
+
while (parent && parent.nodeName !== 'FX-CONTROL') {
|
|
123
|
+
if (parent?.classList?.contains('widget')) return true;
|
|
120
124
|
parent = parent.parentNode;
|
|
121
125
|
}
|
|
122
126
|
return false;
|
|
123
127
|
}
|
|
124
128
|
|
|
125
|
-
|
|
129
|
+
/**
|
|
130
|
+
* Get a string that can be used as a path to a node
|
|
131
|
+
*
|
|
132
|
+
* @param {Node} node
|
|
133
|
+
* @returns {string}
|
|
134
|
+
*/
|
|
126
135
|
static getDomNodeIndexString(node) {
|
|
127
136
|
const indexes = [];
|
|
128
137
|
let currentNode = node;
|
|
@@ -139,9 +148,14 @@ export class Fore {
|
|
|
139
148
|
return indexes.join('.');
|
|
140
149
|
}
|
|
141
150
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
151
|
+
/**
|
|
152
|
+
* Get the expression part of something
|
|
153
|
+
* @param {string} input
|
|
154
|
+
* @returns {string}
|
|
155
|
+
*/
|
|
156
|
+
static getExpression(input) {
|
|
157
|
+
if (input.startsWith('{') && input.endsWith('}')) {
|
|
158
|
+
return input.substring(1, input.length - 1);
|
|
145
159
|
}
|
|
146
160
|
return input;
|
|
147
161
|
}
|
|
@@ -149,11 +163,13 @@ export class Fore {
|
|
|
149
163
|
/**
|
|
150
164
|
* returns the next `fx-fore` element upwards in tree
|
|
151
165
|
*
|
|
152
|
-
* @param start
|
|
153
|
-
* @returns {
|
|
166
|
+
* @param {HTMLElement|Text} start
|
|
167
|
+
* @returns {import('./fx-fore.js').FxFore}
|
|
154
168
|
*/
|
|
155
169
|
static getFore(start) {
|
|
156
|
-
return start.nodeType === Node.TEXT_NODE
|
|
170
|
+
return start.nodeType === Node.TEXT_NODE
|
|
171
|
+
? start.parentNode.closest('fx-fore')
|
|
172
|
+
: start.closest('fx-fore');
|
|
157
173
|
}
|
|
158
174
|
|
|
159
175
|
static get ACTION_ELEMENTS() {
|
|
@@ -205,6 +221,10 @@ export class Fore {
|
|
|
205
221
|
return 'http://www.w3.org/2002/xforms';
|
|
206
222
|
}
|
|
207
223
|
|
|
224
|
+
/**
|
|
225
|
+
* @param {string} elementName
|
|
226
|
+
* @returns {boolean}
|
|
227
|
+
*/
|
|
208
228
|
static isActionElement(elementName) {
|
|
209
229
|
return Fore.ACTION_ELEMENTS.includes(elementName);
|
|
210
230
|
}
|
|
@@ -235,16 +255,14 @@ export class Fore {
|
|
|
235
255
|
];
|
|
236
256
|
}
|
|
237
257
|
|
|
238
|
-
static get MODEL_ELEMENTS(){
|
|
239
|
-
return [
|
|
240
|
-
'FX-BIND',
|
|
241
|
-
'FX-FUNCTION',
|
|
242
|
-
'FX-MODEL',
|
|
243
|
-
'FX-INSTANCE',
|
|
244
|
-
'FX-SUBMISSION',
|
|
245
|
-
];
|
|
258
|
+
static get MODEL_ELEMENTS() {
|
|
259
|
+
return ['FX-BIND', 'FX-FUNCTION', 'FX-MODEL', 'FX-INSTANCE', 'FX-SUBMISSION'];
|
|
246
260
|
}
|
|
247
261
|
|
|
262
|
+
/**
|
|
263
|
+
* @param {string} elementName
|
|
264
|
+
* @returns {boolean}
|
|
265
|
+
*/
|
|
248
266
|
static isUiElement(elementName) {
|
|
249
267
|
const found = Fore.UI_ELEMENTS.includes(elementName);
|
|
250
268
|
if (found) {
|
|
@@ -256,9 +274,9 @@ export class Fore {
|
|
|
256
274
|
/**
|
|
257
275
|
* recursively refreshes all UI Elements.
|
|
258
276
|
*
|
|
259
|
-
* @param startElement
|
|
260
|
-
* @param force
|
|
261
|
-
* @returns {Promise<
|
|
277
|
+
* @param {HTMLElement} startElement
|
|
278
|
+
* @param {boolean} force
|
|
279
|
+
* @returns {Promise<void>}
|
|
262
280
|
*/
|
|
263
281
|
static async refreshChildren(startElement, force) {
|
|
264
282
|
const refreshed = new Promise(resolve => {
|
|
@@ -300,45 +318,46 @@ export class Fore {
|
|
|
300
318
|
return refreshed;
|
|
301
319
|
}
|
|
302
320
|
|
|
303
|
-
static copyDom(inputElement){
|
|
321
|
+
static copyDom(inputElement) {
|
|
304
322
|
console.time('convert');
|
|
305
323
|
const target = new DOMParser().parseFromString('<fx-fore></fx-fore>', 'text/html');
|
|
306
|
-
console.log('copyDom new doc',target);
|
|
307
|
-
console.log('copyDom new body',target.body);
|
|
308
|
-
console.log('copyDom new body',target.querySelector('fx-fore'));
|
|
324
|
+
console.log('copyDom new doc', target);
|
|
325
|
+
console.log('copyDom new body', target.body);
|
|
326
|
+
console.log('copyDom new body', target.querySelector('fx-fore'));
|
|
309
327
|
const newFore = target.querySelector('fx-fore');
|
|
310
|
-
this.convertFromSimple(inputElement,newFore);
|
|
328
|
+
this.convertFromSimple(inputElement, newFore);
|
|
311
329
|
newFore.removeAttribute('convert');
|
|
312
330
|
console.log('converted', newFore);
|
|
313
331
|
console.timeEnd('convert');
|
|
314
332
|
return newFore;
|
|
315
333
|
}
|
|
316
|
-
|
|
334
|
+
|
|
335
|
+
static convertFromSimple(startElement, targetElement) {
|
|
317
336
|
const children = startElement.childNodes;
|
|
318
337
|
if (children) {
|
|
319
338
|
Array.from(children).forEach(node => {
|
|
320
339
|
const lookFor = `FX-${node.nodeName.toUpperCase()}`;
|
|
321
|
-
if (
|
|
322
|
-
|
|
323
|
-
|
|
340
|
+
if (
|
|
341
|
+
Fore.MODEL_ELEMENTS.includes(lookFor) ||
|
|
342
|
+
Fore.UI_ELEMENTS.includes(lookFor) ||
|
|
343
|
+
Fore.ACTION_ELEMENTS.includes(lookFor)
|
|
324
344
|
) {
|
|
325
345
|
const conv = targetElement.ownerDocument.createElement(lookFor);
|
|
326
346
|
console.log('conv', node, conv);
|
|
327
347
|
targetElement.appendChild(conv);
|
|
328
|
-
Fore.copyAttributes(node,conv);
|
|
329
|
-
Fore.convertFromSimple(node,conv);
|
|
330
|
-
} else{
|
|
331
|
-
|
|
332
|
-
if(node.nodeType === Node.TEXT_NODE){
|
|
348
|
+
Fore.copyAttributes(node, conv);
|
|
349
|
+
Fore.convertFromSimple(node, conv);
|
|
350
|
+
} else {
|
|
351
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
333
352
|
const copied = targetElement.ownerDocument.createTextNode(node.textContent);
|
|
334
353
|
targetElement.appendChild(copied);
|
|
335
354
|
}
|
|
336
355
|
|
|
337
|
-
if(node.nodeType === Node.ELEMENT_NODE){
|
|
356
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
338
357
|
const copied = targetElement.ownerDocument.createElement(node.nodeName);
|
|
339
358
|
targetElement.appendChild(copied);
|
|
340
|
-
Fore.copyAttributes(node,targetElement);
|
|
341
|
-
Fore.convertFromSimple(node,copied);
|
|
359
|
+
Fore.copyAttributes(node, targetElement);
|
|
360
|
+
Fore.convertFromSimple(node, copied);
|
|
342
361
|
}
|
|
343
362
|
}
|
|
344
363
|
});
|
|
@@ -347,10 +366,7 @@ export class Fore {
|
|
|
347
366
|
|
|
348
367
|
static copyAttributes(source, target) {
|
|
349
368
|
return Array.from(source.attributes).forEach(attribute => {
|
|
350
|
-
target.setAttribute(
|
|
351
|
-
attribute.nodeName,
|
|
352
|
-
attribute.nodeValue,
|
|
353
|
-
);
|
|
369
|
+
target.setAttribute(attribute.nodeName, attribute.nodeValue);
|
|
354
370
|
});
|
|
355
371
|
}
|
|
356
372
|
|
|
@@ -389,13 +405,13 @@ export class Fore {
|
|
|
389
405
|
// return new DOMParser().parseFromString(htmlResponse, 'text/html');
|
|
390
406
|
// return response.text();
|
|
391
407
|
return response.text().then(result =>
|
|
392
|
-
|
|
393
|
-
|
|
408
|
+
// console.log('xml ********', result);
|
|
409
|
+
new DOMParser().parseFromString(result, 'text/html'),
|
|
394
410
|
);
|
|
395
411
|
}
|
|
396
412
|
if (
|
|
397
|
-
|
|
398
|
-
|
|
413
|
+
responseContentType.startsWith('text/plain') ||
|
|
414
|
+
responseContentType.startsWith('text/markdown')
|
|
399
415
|
) {
|
|
400
416
|
// console.log("********** inside res plain *********");
|
|
401
417
|
return response.text();
|
|
@@ -412,7 +428,7 @@ export class Fore {
|
|
|
412
428
|
return 'done';
|
|
413
429
|
}
|
|
414
430
|
|
|
415
|
-
/*
|
|
431
|
+
/*
|
|
416
432
|
static evaluateAttributeTemplateExpression(expr, node) {
|
|
417
433
|
const matches = expr.match(/{[^}]*}/g);
|
|
418
434
|
if (matches) {
|
|
@@ -471,7 +487,7 @@ export class Fore {
|
|
|
471
487
|
}
|
|
472
488
|
|
|
473
489
|
static async dispatch(target, eventName, detail) {
|
|
474
|
-
|
|
490
|
+
if (!XPathUtil.contains(target?.ownerDocument, target)) {
|
|
475
491
|
// The target is gone from the document. This happens when we are done with a refresh that removed the component
|
|
476
492
|
return;
|
|
477
493
|
}
|
|
@@ -491,17 +507,17 @@ export class Fore {
|
|
|
491
507
|
// console.log('!!! DISPATCH_DONE', eventName);
|
|
492
508
|
}
|
|
493
509
|
|
|
494
|
-
static formatXml
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
510
|
+
static formatXml(xml) {
|
|
511
|
+
const reg = /(>)(<)(\/*)/g;
|
|
512
|
+
const wsexp = / *(.*) +\n/g;
|
|
513
|
+
const contexp = /(<.+>)(.+\n)/g;
|
|
498
514
|
xml = xml.replace(reg, '$1\n$2$3').replace(wsexp, '$1\n').replace(contexp, '$1\n$2');
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
515
|
+
let formatted = '';
|
|
516
|
+
const lines = xml.split('\n');
|
|
517
|
+
let indent = 0;
|
|
518
|
+
let lastType = 'other';
|
|
503
519
|
// 4 types of tags - single, closing, opening, other (text, doctype, comment) - 4*4 = 16 transitions
|
|
504
|
-
|
|
520
|
+
const transitions = {
|
|
505
521
|
'single->single': 0,
|
|
506
522
|
'single->closing': -1,
|
|
507
523
|
'single->opening': 0,
|
|
@@ -517,32 +533,34 @@ export class Fore {
|
|
|
517
533
|
'other->single': 0,
|
|
518
534
|
'other->closing': -1,
|
|
519
535
|
'other->opening': 0,
|
|
520
|
-
'other->other': 0
|
|
536
|
+
'other->other': 0,
|
|
521
537
|
};
|
|
522
538
|
|
|
523
|
-
for (
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
539
|
+
for (let i = 0; i < lines.length; i++) {
|
|
540
|
+
const ln = lines[i];
|
|
541
|
+
const single = Boolean(ln.match(/<.+\/>/)); // is this line a single tag? ex. <br />
|
|
542
|
+
const closing = Boolean(ln.match(/<\/.+>/)); // is this a closing tag? ex. </a>
|
|
543
|
+
const opening = Boolean(ln.match(/<[^!].*>/)); // is this even a tag (that's not <!something>)
|
|
544
|
+
const type = single ? 'single' : closing ? 'closing' : opening ? 'opening' : 'other';
|
|
545
|
+
const fromTo = `${lastType}->${type}`;
|
|
530
546
|
lastType = type;
|
|
531
|
-
|
|
547
|
+
let padding = '';
|
|
532
548
|
|
|
533
549
|
indent += transitions[fromTo];
|
|
534
|
-
for (
|
|
550
|
+
for (let j = 0; j < indent; j++) {
|
|
535
551
|
padding += ' ';
|
|
536
552
|
}
|
|
537
553
|
|
|
538
|
-
formatted += padding + ln
|
|
554
|
+
formatted += `${padding + ln}\n`;
|
|
539
555
|
}
|
|
540
556
|
}
|
|
541
557
|
|
|
542
|
-
static stringifiedComponent(element){
|
|
543
|
-
return `<${element.localName} ${Array.from(element.attributes)
|
|
558
|
+
static stringifiedComponent(element) {
|
|
559
|
+
return `<${element.localName} ${Array.from(element.attributes)
|
|
560
|
+
.map(attr => `${attr.name}="${attr.value}"`)
|
|
561
|
+
.join(' ')}>…</${element.localName}>`;
|
|
544
562
|
}
|
|
545
|
-
/*
|
|
563
|
+
/*
|
|
546
564
|
static async loadForeFromUrl(hostElement, url) {
|
|
547
565
|
// console.log('########## loading Fore from ', this.src, '##########');
|
|
548
566
|
await fetch(url, {
|
|
@@ -1,28 +1,27 @@
|
|
|
1
1
|
export function prettifyXml(source) {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
2
|
+
const xmlDoc = new DOMParser().parseFromString(source, 'application/xml');
|
|
3
|
+
const xsltDoc = new DOMParser().parseFromString(
|
|
4
|
+
[
|
|
5
|
+
// describes how we want to modify the XML - indent everything
|
|
6
|
+
'<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">',
|
|
7
|
+
' <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>',
|
|
8
|
+
' <xsl:strip-space elements="*"/>',
|
|
9
|
+
' <xsl:template match="text()">', // change to just text() to strip space in text nodes
|
|
10
|
+
' <xsl:value-of select="normalize-space(.)"/>',
|
|
11
|
+
' </xsl:template>',
|
|
12
|
+
' <xsl:template match="node()|@*">',
|
|
13
|
+
' <xsl:copy>',
|
|
14
|
+
' <xsl:apply-templates select="node()|@*"/>',
|
|
15
|
+
' </xsl:copy>',
|
|
16
|
+
' </xsl:template>',
|
|
17
|
+
'</xsl:stylesheet>',
|
|
18
|
+
].join('\n'),
|
|
19
|
+
'application/xml',
|
|
20
|
+
);
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return resultXml;
|
|
22
|
+
const xsltProcessor = new XSLTProcessor();
|
|
23
|
+
xsltProcessor.importStylesheet(xsltDoc);
|
|
24
|
+
const resultDoc = xsltProcessor.transformToDocument(xmlDoc);
|
|
25
|
+
const resultXml = new XMLSerializer().serializeToString(resultDoc);
|
|
26
|
+
return resultXml;
|
|
28
27
|
}
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import { evaluateXPath, globallyDeclaredFunctionLocalNames } from '../xpath-evaluation.js';
|
|
1
|
+
import ForeElementMixin from '../ForeElementMixin.js';
|
|
2
|
+
import registerFunction from './registerFunction.js';
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* Allows to extend a form with local custom functions.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export class FxFunction extends foreElementMixin(HTMLElement) {
|
|
6
|
+
*/
|
|
7
|
+
export class FxFunction extends ForeElementMixin {
|
|
10
8
|
constructor() {
|
|
11
9
|
super();
|
|
12
10
|
this.attachShadow({ mode: 'open' });
|
|
@@ -16,104 +14,13 @@ export class FxFunction extends foreElementMixin(HTMLElement) {
|
|
|
16
14
|
this.style.display = 'none';
|
|
17
15
|
|
|
18
16
|
this.signature = this.hasAttribute('signature') ? this.getAttribute('signature') : null;
|
|
19
|
-
if (this.signature === null) {
|
|
20
|
-
console.error('signature is a required attribute');
|
|
21
|
-
}
|
|
22
17
|
this.type = this.hasAttribute('type') ? this.getAttribute('type') : null;
|
|
23
|
-
this.shadowRoot.innerHTML =
|
|
18
|
+
this.shadowRoot.innerHTML = '<slot></slot>';
|
|
24
19
|
|
|
25
20
|
this.override = this.hasAttribute('override') ? this.getAttribute('override') : 'true';
|
|
26
21
|
this.functionBody = this.innerText;
|
|
27
22
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
// Parse the signature to something useful
|
|
31
|
-
// Signature is of the form `my:sumproduct($p as xs:decimal*, $q as xs:decimal*) as xs:decimal` or local:something($a as item()*) as item()*
|
|
32
|
-
const signatureParseResult = this.signature.match(
|
|
33
|
-
/(?:(?<prefix>[^:]*):)?(?<localName>[^(]+)\((?<params>(?:\(\)|[^)])*)\)(?: as (?<returnType>.*))?/,
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
if (!signatureParseResult) {
|
|
37
|
-
throw new Error(`Function signature ${this.signature} could not be parsed`);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const { prefix, localName, params, returnType } = signatureParseResult.groups;
|
|
41
|
-
|
|
42
|
-
// TODO: lookup prefix
|
|
43
|
-
const functionIdentifier =
|
|
44
|
-
prefix === 'local' || !prefix
|
|
45
|
-
? { namespaceURI: 'http://www.w3.org/2005/xquery-local-functions', localName }
|
|
46
|
-
: `${prefix}:${localName}`;
|
|
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
|
-
|
|
54
|
-
const paramParts = params
|
|
55
|
-
? params.split(',').map(param => {
|
|
56
|
-
const match = param.match(/(?<variableName>\$[^\s]+)(?:\sas\s(?<varType>.+))/);
|
|
57
|
-
if (!match) {
|
|
58
|
-
throw new Error(`Param ${param} could not be parsed`);
|
|
59
|
-
}
|
|
60
|
-
const { variableName, varType } = match.groups;
|
|
61
|
-
return {
|
|
62
|
-
variableName,
|
|
63
|
-
variableType: varType || 'item()*',
|
|
64
|
-
};
|
|
65
|
-
})
|
|
66
|
-
: [];
|
|
67
|
-
|
|
68
|
-
switch (type) {
|
|
69
|
-
case 'text/javascript': {
|
|
70
|
-
// eslint-disable-next-line no-new-func
|
|
71
|
-
const fun = new Function(
|
|
72
|
-
'_domFacade',
|
|
73
|
-
...paramParts.map(paramPart => paramPart.variableName),
|
|
74
|
-
'form',
|
|
75
|
-
this.functionBody,
|
|
76
|
-
);
|
|
77
|
-
registerCustomXPathFunction(
|
|
78
|
-
functionIdentifier,
|
|
79
|
-
paramParts.map(paramPart => paramPart.variableType),
|
|
80
|
-
returnType || 'item()*',
|
|
81
|
-
(...args) => fun.apply(this.getInScopeContext(), [...args, this.getOwnerForm()]),
|
|
82
|
-
);
|
|
83
|
-
break;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
case 'text/xquf':
|
|
87
|
-
case 'text/xquery':
|
|
88
|
-
case 'text/xpath': {
|
|
89
|
-
const typedValueFactories = paramParts.map(param => createTypedValueFactory(param.variableType));
|
|
90
|
-
const language = type === 'text/xpath' ?
|
|
91
|
-
'XPath3.1' : type === 'text/xquery' ?
|
|
92
|
-
'XQuery3.1' : 'XQueryUpdate3.1';
|
|
93
|
-
const fun = (domFacade, ...args) =>
|
|
94
|
-
evaluateXPath(
|
|
95
|
-
this.functionBody,
|
|
96
|
-
this.getInScopeContext(),
|
|
97
|
-
this.getOwnerForm(),
|
|
98
|
-
paramParts.reduce((variablesByName, paramPart, i) => {
|
|
99
|
-
// Because we know the XPath type here (from the function declaration) we do not have to depend on the implicit typings
|
|
100
|
-
variablesByName[paramPart.variableName.replace('$', '')] = typedValueFactories[i](args[i]);
|
|
101
|
-
return variablesByName;
|
|
102
|
-
}, {}),
|
|
103
|
-
{language}
|
|
104
|
-
);
|
|
105
|
-
registerCustomXPathFunction(
|
|
106
|
-
functionIdentifier,
|
|
107
|
-
paramParts.map(paramPart => paramPart.variableType),
|
|
108
|
-
returnType || 'item()*',
|
|
109
|
-
fun,
|
|
110
|
-
);
|
|
111
|
-
break;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
default:
|
|
115
|
-
throw new Error(`Unexpected mimetype ${type} for function`);
|
|
116
|
-
}
|
|
23
|
+
registerFunction(this, this);
|
|
117
24
|
}
|
|
118
25
|
}
|
|
119
26
|
if (!customElements.get('fx-function')) {
|