@jinntec/fore 1.4.0 → 1.6.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 (71) hide show
  1. package/dist/fore-dev.js +2 -36
  2. package/dist/fore-dev.js.map +1 -1
  3. package/dist/fore.js +2 -30
  4. package/dist/fore.js.map +1 -1
  5. package/index.js +13 -0
  6. package/package.json +9 -5
  7. package/resources/fore.css +178 -92
  8. package/src/DependencyNotifyingDomFacade.js +1 -2
  9. package/src/ForeElementMixin.js +31 -5
  10. package/src/actions/abstract-action.js +379 -270
  11. package/src/actions/fx-action.js +0 -1
  12. package/src/actions/fx-append.js +1 -2
  13. package/src/actions/fx-confirm.js +12 -0
  14. package/src/actions/fx-copy.js +0 -1
  15. package/src/actions/fx-delete.js +31 -9
  16. package/src/actions/fx-dispatch.js +19 -5
  17. package/src/actions/fx-hide.js +19 -0
  18. package/src/actions/fx-insert.js +72 -8
  19. package/src/actions/fx-load.js +253 -0
  20. package/src/actions/fx-message.js +22 -7
  21. package/src/actions/fx-refresh.js +11 -1
  22. package/src/actions/fx-reload.js +12 -2
  23. package/src/actions/fx-replace.js +5 -4
  24. package/src/actions/fx-reset.js +48 -0
  25. package/src/actions/fx-return.js +0 -1
  26. package/src/actions/fx-send.js +40 -2
  27. package/src/actions/fx-setfocus.js +25 -7
  28. package/src/actions/fx-setvalue.js +32 -4
  29. package/src/actions/fx-show.js +14 -2
  30. package/src/actions/fx-toggle.js +0 -1
  31. package/src/actions/fx-update.js +9 -0
  32. package/src/events.js +0 -1
  33. package/src/fore.js +119 -63
  34. package/src/functions/common-function.js +28 -0
  35. package/src/fx-bind.js +7 -7
  36. package/src/fx-fore.js +207 -54
  37. package/src/fx-instance.js +55 -17
  38. package/src/fx-model.js +31 -33
  39. package/src/fx-submission.js +50 -47
  40. package/src/getInScopeContext.js +22 -8
  41. package/src/lab/fore-component.js +90 -0
  42. package/src/lab/instance-inspector.js +56 -0
  43. package/src/lab/template.html +16 -0
  44. package/src/relevance.js +27 -1
  45. package/src/tools/adi.js +1056 -0
  46. package/src/tools/fx-action-log.js +662 -0
  47. package/src/tools/fx-devtools.js +444 -0
  48. package/src/tools/fx-dom-inspector.js +609 -0
  49. package/src/tools/fx-json-instance.js +435 -0
  50. package/src/tools/fx-log-item.js +133 -0
  51. package/src/tools/fx-log-settings.js +474 -0
  52. package/src/tools/fx-minimap.js +194 -0
  53. package/src/tools/helpers.js +132 -0
  54. package/src/ui/abstract-control.js +41 -3
  55. package/src/ui/fx-action-log.js +358 -0
  56. package/src/ui/fx-alert.js +0 -1
  57. package/src/ui/fx-container.js +14 -3
  58. package/src/ui/fx-control.js +553 -474
  59. package/src/ui/fx-dialog.js +2 -0
  60. package/src/ui/fx-dom-inspector.js +1255 -0
  61. package/src/ui/fx-group.js +3 -4
  62. package/src/ui/fx-hint.js +2 -4
  63. package/src/ui/fx-inspector.js +5 -6
  64. package/src/ui/fx-items.js +55 -14
  65. package/src/ui/fx-output.js +36 -17
  66. package/src/ui/fx-repeat-attributes.js +409 -0
  67. package/src/ui/fx-repeat.js +12 -6
  68. package/src/ui/fx-switch.js +14 -3
  69. package/src/ui/fx-trigger.js +13 -1
  70. package/src/xpath-evaluation.js +109 -26
  71. package/src/xpath-util.js +55 -1
package/src/fore.js CHANGED
@@ -1,3 +1,12 @@
1
+ import getInScopeContext from "./getInScopeContext.js";
2
+ import {evaluateXPathToString} from "./xpath-evaluation.js";
3
+ import { XPathUtil } from "./xpath-util.js";
4
+
5
+ /**
6
+ * Class hosting common utility functions used throughout all fore elements
7
+ *
8
+ * @example ../doc/demo.html
9
+ */
1
10
  export class Fore {
2
11
  static READONLY_DEFAULT = false;
3
12
 
@@ -9,6 +18,46 @@ export class Fore {
9
18
 
10
19
  static TYPE_DEFAULT = 'xs:string';
11
20
 
21
+
22
+ /**
23
+ * returns true if target element is the widget itself or some element within the widget.
24
+ * @param target an event target
25
+ * @returns {boolean}
26
+ */
27
+ static isWidget(target) {
28
+ if(target?.classList.contains("widget")) return true;
29
+ let parent = target.parentNode;
30
+ while(parent && parent.nodeName !== 'FX-CONTROL'){
31
+ if(parent.classList.contains('widget')) return true;
32
+ parent = parent.parentNode;
33
+ }
34
+ return false;
35
+ }
36
+
37
+
38
+ static getDomNodeIndexString(node) {
39
+ const indexes = [];
40
+ let currentNode = node;
41
+
42
+ while (currentNode && currentNode.parentNode) {
43
+ const parent = currentNode.parentNode;
44
+ if (parent.childNodes && parent.childNodes.length > 0) {
45
+ const index = [...parent.childNodes].indexOf(currentNode);
46
+ indexes.unshift(index);
47
+ }
48
+ currentNode = parent;
49
+ }
50
+
51
+ return indexes.join('.');
52
+ }
53
+
54
+ static getExpression(input){
55
+ if(input.startsWith('{') && input.endsWith('}')){
56
+ return input.substring(1, input.length - 1);
57
+ }
58
+ return input;
59
+ }
60
+
12
61
  /**
13
62
  * returns the next `fx-fore` element upwards in tree
14
63
  *
@@ -86,6 +135,7 @@ export class Fore {
86
135
  'FX-RANGE',
87
136
  'FX-REPEAT',
88
137
  'FX-REPEATITEM',
138
+ 'FX-REPEAT-ATTRIBUTES',
89
139
  'FX-SWITCH',
90
140
  'FX-SECRET',
91
141
  'FX-SELECT',
@@ -118,8 +168,6 @@ export class Fore {
118
168
  /**
119
169
  * recursively refreshes all UI Elements.
120
170
  *
121
- * todo: this could probably made more efficient with significant impact on rendering perf
122
- *
123
171
  * @param startElement
124
172
  * @param force
125
173
  * @returns {Promise<unknown>}
@@ -173,8 +221,8 @@ export class Fore {
173
221
  this.convertFromSimple(inputElement,newFore);
174
222
  newFore.removeAttribute('convert');
175
223
  console.log('converted', newFore);
176
- return newFore;
177
224
  console.timeEnd('convert');
225
+ return newFore;
178
226
  }
179
227
  static convertFromSimple(startElement,targetElement){
180
228
  const children = startElement.childNodes;
@@ -217,28 +265,11 @@ export class Fore {
217
265
  });
218
266
  }
219
267
 
220
-
221
- /**
222
- * Alternative to `closest` that respects subcontrol boundaries
223
- */
224
- static getClosest(querySelector, start) {
225
- while (!start.matches(querySelector)) {
226
- if (start.matches('fx-fore')) {
227
- // Subform reached. Bail out
228
- return null;
229
- }
230
- start = start.parentNode;
231
- if (!start) {
232
- return null;
233
- }
234
- }
235
- return start;
236
- }
237
-
238
268
  /**
239
269
  * returns the proper content-type for instance.
240
270
  *
241
271
  * @param instance an fx-instance element
272
+ * @param contentType - the contentType
242
273
  * @returns {string|null}
243
274
  */
244
275
  static getContentType(instance, contentType) {
@@ -255,6 +286,59 @@ export class Fore {
255
286
  return null;
256
287
  }
257
288
 
289
+ static async handleResponse(response) {
290
+ const { status } = response;
291
+ if (status >= 400) {
292
+ // console.log('response status', status);
293
+ alert(`response status: ${status} - failed to load data for '${this.src}' - stopping.`);
294
+ throw new Error(`failed to load data - status: ${status}`);
295
+ }
296
+ const responseContentType = response.headers.get('content-type').toLowerCase();
297
+ // console.log('********** responseContentType *********', responseContentType);
298
+ if (responseContentType.startsWith('text/html')) {
299
+ // const htmlResponse = response.text();
300
+ // return new DOMParser().parseFromString(htmlResponse, 'text/html');
301
+ // return response.text();
302
+ return response.text().then(result =>
303
+ // console.log('xml ********', result);
304
+ new DOMParser().parseFromString(result, 'text/html'),
305
+ );
306
+ }
307
+ if (
308
+ responseContentType.startsWith('text/plain') ||
309
+ responseContentType.startsWith('text/markdown')
310
+ ) {
311
+ // console.log("********** inside res plain *********");
312
+ return response.text();
313
+ }
314
+ if (responseContentType.startsWith('application/json')) {
315
+ // console.log("********** inside res json *********");
316
+ return response.json();
317
+ }
318
+ if (responseContentType.startsWith('application/xml')) {
319
+ const text = await response.text();
320
+ // console.log('xml ********', result);
321
+ return new DOMParser().parseFromString(text, 'application/xml');
322
+ }
323
+ return 'done';
324
+ }
325
+
326
+ static evaluateAttributeTemplateExpression(expr, node) {
327
+ const matches = expr.match(/{[^}]*}/g);
328
+ if (matches) {
329
+ matches.forEach(match => {
330
+ console.log('match ', match);
331
+ const naked = match.substring(1, match.length - 1);
332
+ const inscope = getInScopeContext(node, naked);
333
+ const result = evaluateXPathToString(naked, inscope, node.getOwnerForm());
334
+ const replaced = expr.replaceAll(match, result);
335
+ console.log('replacing ', expr, ' with ', replaced);
336
+ expr = replaced;
337
+ });
338
+ }
339
+ return expr;
340
+ }
341
+
258
342
  static fadeInElement(element) {
259
343
  const duration = 600;
260
344
  let fadeIn = () => {
@@ -296,51 +380,24 @@ export class Fore {
296
380
  }
297
381
 
298
382
  static async dispatch(target, eventName, detail) {
383
+ if (!XPathUtil.contains(target?.ownerDocument, target)) {
384
+ // The target is gone from the document. This happens when we are done with a refresh that removed the component
385
+ return;
386
+ }
299
387
  const event = new CustomEvent(eventName, {
300
388
  composed: false,
301
389
  bubbles: true,
302
390
  detail,
303
391
  });
304
- event.listenerPromises = [];
305
- // console.info('dispatching', event.type, target);
306
- // console.log('!!! DISPATCH_START', eventName);
392
+ event.listenerPromises = [];
307
393
 
308
- target.dispatchEvent(event);
394
+ target.dispatchEvent(event);
309
395
 
310
- // By now, all listeners for the event should have registered their completion promises to us.
311
- if (event.listenerPromises.length) {
312
- await Promise.all(event.listenerPromises);
313
- }
314
- // console.log('!!! DISPATCH_DONE', eventName);
315
- }
316
-
317
- static prettifyXml(source) {
318
- const xmlDoc = new DOMParser().parseFromString(source, 'application/xml');
319
- const xsltDoc = new DOMParser().parseFromString(
320
- [
321
- // describes how we want to modify the XML - indent everything
322
- '<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">',
323
- ' <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>',
324
- ' <xsl:strip-space elements="*"/>',
325
- ' <xsl:template match="text()">', // change to just text() to strip space in text nodes
326
- ' <xsl:value-of select="normalize-space(.)"/>',
327
- ' </xsl:template>',
328
- ' <xsl:template match="node()|@*">',
329
- ' <xsl:copy>',
330
- ' <xsl:apply-templates select="node()|@*"/>',
331
- ' </xsl:copy>',
332
- ' </xsl:template>',
333
- '</xsl:stylesheet>',
334
- ].join('\n'),
335
- 'application/xml',
336
- );
337
-
338
-
339
- const xsltProcessor = new XSLTProcessor();
340
- xsltProcessor.importStylesheet(xsltDoc);
341
- const resultDoc = xsltProcessor.transformToDocument(xmlDoc);
342
- const resultXml = new XMLSerializer().serializeToString(resultDoc);
343
- return resultXml;
396
+ // By now, all listeners for the event should have registered their completion promises to us.
397
+ if (event.listenerPromises.length) {
398
+ await Promise.all(event.listenerPromises);
399
+ }
400
+ // console.log('!!! DISPATCH_DONE', eventName);
344
401
  }
345
402
 
346
403
  static formatXml (xml) {
@@ -348,7 +405,6 @@ export class Fore {
348
405
  var wsexp = / *(.*) +\n/g;
349
406
  var contexp = /(<.+>)(.+\n)/g;
350
407
  xml = xml.replace(reg, '$1\n$2$3').replace(wsexp, '$1\n').replace(contexp, '$1\n$2');
351
- var pad = 0;
352
408
  var formatted = '';
353
409
  var lines = xml.split('\n');
354
410
  var indent = 0;
@@ -393,7 +449,7 @@ export class Fore {
393
449
  }
394
450
 
395
451
  static async loadForeFromUrl(hostElement, url) {
396
- console.log('########## loading Fore from ', this.src, '##########');
452
+ // console.log('########## loading Fore from ', this.src, '##########');
397
453
  await fetch(url, {
398
454
  method: 'GET',
399
455
  mode: 'cors',
@@ -433,7 +489,7 @@ export class Fore {
433
489
  imported.addEventListener(
434
490
  'model-construct-done',
435
491
  e => {
436
- console.log('subcomponent ready', e.target);
492
+ // console.log('subcomponent ready', e.target);
437
493
  const defaultInst = imported.querySelector('fx-instance');
438
494
  // console.log('defaultInst', defaultInst);
439
495
  if(hostElement.initialNode){
@@ -458,7 +514,7 @@ export class Fore {
458
514
  dummy.parentNode.removeChild(dummy);
459
515
  hostElement.shadowRoot.appendChild(imported);
460
516
  } else {
461
- console.log(this, 'replacing widget with',theFore);
517
+ // console.log(this, 'replacing widget with',theFore);
462
518
  dummy.replaceWith(imported);
463
519
  // this.appendChild(imported);
464
520
  }
@@ -0,0 +1,28 @@
1
+ export function prettifyXml(source) {
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
+
22
+
23
+ const xsltProcessor = new XSLTProcessor();
24
+ xsltProcessor.importStylesheet(xsltDoc);
25
+ const resultDoc = xsltProcessor.transformToDocument(xmlDoc);
26
+ const resultXml = new XMLSerializer().serializeToString(resultDoc);
27
+ return resultXml;
28
+ }
package/src/fx-bind.js CHANGED
@@ -1,6 +1,5 @@
1
1
  import { DependencyNotifyingDomFacade } from './DependencyNotifyingDomFacade.js';
2
2
  import { foreElementMixin } from './ForeElementMixin.js';
3
- import { Fore } from './fore.js';
4
3
  import { ModelItem } from './modelitem.js';
5
4
  import {
6
5
  evaluateXPathToBoolean,
@@ -20,8 +19,9 @@ import getInScopeContext from './getInScopeContext.js';
20
19
  * Note: why is fx-bind not extending BoundElement? Though fx-bind has a 'ref' attr it is not bound in the sense of
21
20
  * getting updates about changes of the bound nodes. Instead it acts as a factory for modelItems that are used by
22
21
  * BoundElements to track their state.
22
+ *
23
+ * @customElements
23
24
  */
24
- // export class FxBind extends HTMLElement {
25
25
  export class FxBind extends foreElementMixin(HTMLElement) {
26
26
  static READONLY_DEFAULT = false;
27
27
 
@@ -235,7 +235,6 @@ export class FxBind extends foreElementMixin(HTMLElement) {
235
235
  // console.log('#### ', thi+s.nodeset);
236
236
 
237
237
  if (Array.isArray(this.nodeset)) {
238
- // todo - iterate and create
239
238
  // console.log('################################################ ', this.nodeset);
240
239
  // Array.from(this.nodeset).forEach((n, index) => {
241
240
  Array.from(this.nodeset).forEach(n => {
@@ -272,7 +271,7 @@ export class FxBind extends foreElementMixin(HTMLElement) {
272
271
  if bind is the dot expression we use the modelitem of the parent
273
272
  */
274
273
  if (XPathUtil.isSelfReference(this.ref)) {
275
- const parentBoundElement = Fore.getClosest('fx-bind[ref]', this.parentElement);
274
+ const parentBoundElement = XPathUtil.getClosest('fx-bind[ref]', this.parentElement);
276
275
  // console.log('parent bound element ', parentBoundElement);
277
276
 
278
277
  if (parentBoundElement) {
@@ -304,9 +303,9 @@ export class FxBind extends foreElementMixin(HTMLElement) {
304
303
  const path = XPathUtil.getPath(node);
305
304
  // const shortPath = this.shortenPath(path);
306
305
 
307
- // ### constructiong default modelitem - will get evaluated during reaalculate()
308
- // ### constructiong default modelitem - will get evaluated during reaalculate()
309
- // ### constructiong default modelitem - will get evaluated during reaalculate()
306
+ // ### constructing default modelitem - will get evaluated during recalculate()
307
+ // ### constructing default modelitem - will get evaluated during recalculate()
308
+ // ### constructing default modelitem - will get evaluated during recalculate()
310
309
  // const newItem = new ModelItem(shortPath,
311
310
  const newItem = new ModelItem(
312
311
  path,
@@ -325,6 +324,7 @@ export class FxBind extends foreElementMixin(HTMLElement) {
325
324
  newItem.addAlert(alert);
326
325
  }
327
326
 
327
+
328
328
  this.getModel().registerModelItem(newItem);
329
329
  }
330
330