@jinntec/fore 1.5.0 → 1.7.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 -297
  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-toggleboolean.js +58 -0
  32. package/src/actions/fx-update.js +9 -0
  33. package/src/events.js +0 -1
  34. package/src/fore.js +118 -63
  35. package/src/functions/common-function.js +28 -0
  36. package/src/fx-bind.js +9 -7
  37. package/src/fx-fore.js +153 -55
  38. package/src/fx-instance.js +55 -17
  39. package/src/fx-model.js +31 -33
  40. package/src/fx-submission.js +50 -47
  41. package/src/getInScopeContext.js +8 -10
  42. package/src/lab/fore-component.js +90 -0
  43. package/src/lab/instance-inspector.js +56 -0
  44. package/src/lab/template.html +16 -0
  45. package/src/relevance.js +27 -1
  46. package/src/tools/adi.js +1056 -0
  47. package/src/tools/fx-action-log.js +662 -0
  48. package/src/tools/fx-devtools.js +444 -0
  49. package/src/tools/fx-dom-inspector.js +609 -0
  50. package/src/tools/fx-json-instance.js +435 -0
  51. package/src/tools/fx-log-item.js +133 -0
  52. package/src/tools/fx-log-settings.js +474 -0
  53. package/src/tools/fx-minimap.js +194 -0
  54. package/src/tools/helpers.js +132 -0
  55. package/src/ui/abstract-control.js +41 -3
  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 +10 -43
  67. package/src/ui/fx-repeat.js +5 -7
  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
  *
@@ -119,8 +168,6 @@ export class Fore {
119
168
  /**
120
169
  * recursively refreshes all UI Elements.
121
170
  *
122
- * todo: this could probably made more efficient with significant impact on rendering perf
123
- *
124
171
  * @param startElement
125
172
  * @param force
126
173
  * @returns {Promise<unknown>}
@@ -174,8 +221,8 @@ export class Fore {
174
221
  this.convertFromSimple(inputElement,newFore);
175
222
  newFore.removeAttribute('convert');
176
223
  console.log('converted', newFore);
177
- return newFore;
178
224
  console.timeEnd('convert');
225
+ return newFore;
179
226
  }
180
227
  static convertFromSimple(startElement,targetElement){
181
228
  const children = startElement.childNodes;
@@ -218,28 +265,11 @@ export class Fore {
218
265
  });
219
266
  }
220
267
 
221
-
222
- /**
223
- * Alternative to `closest` that respects subcontrol boundaries
224
- */
225
- static getClosest(querySelector, start) {
226
- while (!start.matches(querySelector)) {
227
- if (start.matches('fx-fore')) {
228
- // Subform reached. Bail out
229
- return null;
230
- }
231
- start = start.parentNode;
232
- if (!start) {
233
- return null;
234
- }
235
- }
236
- return start;
237
- }
238
-
239
268
  /**
240
269
  * returns the proper content-type for instance.
241
270
  *
242
271
  * @param instance an fx-instance element
272
+ * @param contentType - the contentType
243
273
  * @returns {string|null}
244
274
  */
245
275
  static getContentType(instance, contentType) {
@@ -256,6 +286,59 @@ export class Fore {
256
286
  return null;
257
287
  }
258
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
+
259
342
  static fadeInElement(element) {
260
343
  const duration = 600;
261
344
  let fadeIn = () => {
@@ -297,51 +380,24 @@ export class Fore {
297
380
  }
298
381
 
299
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
+ }
300
387
  const event = new CustomEvent(eventName, {
301
388
  composed: false,
302
389
  bubbles: true,
303
390
  detail,
304
391
  });
305
- event.listenerPromises = [];
306
- // console.info('dispatching', event.type, target);
307
- // console.log('!!! DISPATCH_START', eventName);
392
+ event.listenerPromises = [];
308
393
 
309
- target.dispatchEvent(event);
394
+ target.dispatchEvent(event);
310
395
 
311
- // By now, all listeners for the event should have registered their completion promises to us.
312
- if (event.listenerPromises.length) {
313
- await Promise.all(event.listenerPromises);
314
- }
315
- // console.log('!!! DISPATCH_DONE', eventName);
316
- }
317
-
318
- static prettifyXml(source) {
319
- const xmlDoc = new DOMParser().parseFromString(source, 'application/xml');
320
- const xsltDoc = new DOMParser().parseFromString(
321
- [
322
- // describes how we want to modify the XML - indent everything
323
- '<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">',
324
- ' <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>',
325
- ' <xsl:strip-space elements="*"/>',
326
- ' <xsl:template match="text()">', // change to just text() to strip space in text nodes
327
- ' <xsl:value-of select="normalize-space(.)"/>',
328
- ' </xsl:template>',
329
- ' <xsl:template match="node()|@*">',
330
- ' <xsl:copy>',
331
- ' <xsl:apply-templates select="node()|@*"/>',
332
- ' </xsl:copy>',
333
- ' </xsl:template>',
334
- '</xsl:stylesheet>',
335
- ].join('\n'),
336
- 'application/xml',
337
- );
338
-
339
-
340
- const xsltProcessor = new XSLTProcessor();
341
- xsltProcessor.importStylesheet(xsltDoc);
342
- const resultDoc = xsltProcessor.transformToDocument(xmlDoc);
343
- const resultXml = new XMLSerializer().serializeToString(resultDoc);
344
- 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);
345
401
  }
346
402
 
347
403
  static formatXml (xml) {
@@ -349,7 +405,6 @@ export class Fore {
349
405
  var wsexp = / *(.*) +\n/g;
350
406
  var contexp = /(<.+>)(.+\n)/g;
351
407
  xml = xml.replace(reg, '$1\n$2$3').replace(wsexp, '$1\n').replace(contexp, '$1\n$2');
352
- var pad = 0;
353
408
  var formatted = '';
354
409
  var lines = xml.split('\n');
355
410
  var indent = 0;
@@ -394,7 +449,7 @@ export class Fore {
394
449
  }
395
450
 
396
451
  static async loadForeFromUrl(hostElement, url) {
397
- console.log('########## loading Fore from ', this.src, '##########');
452
+ // console.log('########## loading Fore from ', this.src, '##########');
398
453
  await fetch(url, {
399
454
  method: 'GET',
400
455
  mode: 'cors',
@@ -434,7 +489,7 @@ export class Fore {
434
489
  imported.addEventListener(
435
490
  'model-construct-done',
436
491
  e => {
437
- console.log('subcomponent ready', e.target);
492
+ // console.log('subcomponent ready', e.target);
438
493
  const defaultInst = imported.querySelector('fx-instance');
439
494
  // console.log('defaultInst', defaultInst);
440
495
  if(hostElement.initialNode){
@@ -459,7 +514,7 @@ export class Fore {
459
514
  dummy.parentNode.removeChild(dummy);
460
515
  hostElement.shadowRoot.appendChild(imported);
461
516
  } else {
462
- console.log(this, 'replacing widget with',theFore);
517
+ // console.log(this, 'replacing widget with',theFore);
463
518
  dummy.replaceWith(imported);
464
519
  // this.appendChild(imported);
465
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
 
@@ -44,6 +44,7 @@ export class FxBind extends foreElementMixin(HTMLElement) {
44
44
  connectedCallback() {
45
45
  // console.log('connectedCallback ', this);
46
46
  // this.id = this.hasAttribute('id')?this.getAttribute('id'):;
47
+ this.constraint = this.getAttribute('constraint');
47
48
  this.ref = this.getAttribute('ref');
48
49
  this.readonly = this.getAttribute('readonly');
49
50
  this.required = this.getAttribute('required');
@@ -133,6 +134,7 @@ export class FxBind extends foreElementMixin(HTMLElement) {
133
134
  this._addDependencies(constraintRefs, node, path, 'constraint');
134
135
  } else if (this.constraint) {
135
136
  this.model.mainGraph.addNode(`${path}:constraint`, node);
137
+ this.model.mainGraph.addDependency(path, `${path}:constraint`);
136
138
  }
137
139
  });
138
140
  }
@@ -235,7 +237,6 @@ export class FxBind extends foreElementMixin(HTMLElement) {
235
237
  // console.log('#### ', thi+s.nodeset);
236
238
 
237
239
  if (Array.isArray(this.nodeset)) {
238
- // todo - iterate and create
239
240
  // console.log('################################################ ', this.nodeset);
240
241
  // Array.from(this.nodeset).forEach((n, index) => {
241
242
  Array.from(this.nodeset).forEach(n => {
@@ -272,7 +273,7 @@ export class FxBind extends foreElementMixin(HTMLElement) {
272
273
  if bind is the dot expression we use the modelitem of the parent
273
274
  */
274
275
  if (XPathUtil.isSelfReference(this.ref)) {
275
- const parentBoundElement = Fore.getClosest('fx-bind[ref]', this.parentElement);
276
+ const parentBoundElement = XPathUtil.getClosest('fx-bind[ref]', this.parentElement);
276
277
  // console.log('parent bound element ', parentBoundElement);
277
278
 
278
279
  if (parentBoundElement) {
@@ -304,9 +305,9 @@ export class FxBind extends foreElementMixin(HTMLElement) {
304
305
  const path = XPathUtil.getPath(node);
305
306
  // const shortPath = this.shortenPath(path);
306
307
 
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()
308
+ // ### constructing default modelitem - will get evaluated during recalculate()
309
+ // ### constructing default modelitem - will get evaluated during recalculate()
310
+ // ### constructing default modelitem - will get evaluated during recalculate()
310
311
  // const newItem = new ModelItem(shortPath,
311
312
  const newItem = new ModelItem(
312
313
  path,
@@ -325,6 +326,7 @@ export class FxBind extends foreElementMixin(HTMLElement) {
325
326
  newItem.addAlert(alert);
326
327
  }
327
328
 
329
+
328
330
  this.getModel().registerModelItem(newItem);
329
331
  }
330
332