@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/fx-fore.js CHANGED
@@ -2,10 +2,9 @@ import {Fore} from './fore.js';
2
2
  import './fx-instance.js';
3
3
  import {FxModel} from './fx-model.js';
4
4
  import '@jinntec/jinn-toast';
5
- import {evaluateXPathToBoolean, evaluateXPathToNodes, evaluateXPathToString} from './xpath-evaluation.js';
5
+ import {evaluateXPathToBoolean, evaluateXPathToNodes, evaluateXPathToFirstNode, evaluateXPathToString} from './xpath-evaluation.js';
6
6
  import getInScopeContext from './getInScopeContext.js';
7
7
  import {XPathUtil} from './xpath-util.js';
8
- // import {FxRepeat} from "./ui/fx-repeat";
9
8
  import {FxRepeatAttributes} from './ui/fx-repeat-attributes.js';
10
9
 
11
10
  /**
@@ -26,6 +25,8 @@ import {FxRepeatAttributes} from './ui/fx-repeat-attributes.js';
26
25
  * @ts-check
27
26
  */
28
27
  export class FxFore extends HTMLElement {
28
+ static outermostHandler = null;
29
+
29
30
  static get properties() {
30
31
  return {
31
32
  /**
@@ -43,6 +44,12 @@ export class FxFore extends HTMLElement {
43
44
  ready: {
44
45
  type: Boolean,
45
46
  },
47
+ /**
48
+ *
49
+ */
50
+ validateOn: {
51
+ type: String
52
+ }
46
53
  };
47
54
  }
48
55
 
@@ -57,9 +64,13 @@ export class FxFore extends HTMLElement {
57
64
  constructor() {
58
65
  super();
59
66
  this.model = {};
67
+ this.inited=false;
60
68
  // this.addEventListener('model-construct-done', this._handleModelConstructDone);
69
+ // todo: refactoring - these should rather go into connectedcallback
61
70
  this.addEventListener('message', this._displayMessage);
62
71
  this.addEventListener('error', this._displayError);
72
+ this.addEventListener('warn', this._displayWarning);
73
+ this.addEventListener('log', this._logError);
63
74
  window.addEventListener('compute-exception', e => {
64
75
  console.error('circular dependency: ', e);
65
76
  });
@@ -67,6 +78,10 @@ export class FxFore extends HTMLElement {
67
78
  this.ready = false;
68
79
  this.storedTemplateExpressionByNode = new Map();
69
80
 
81
+ // Stores the outer most action handler. If an action handler is already running, all
82
+ // updates are included in that one
83
+ this.outermostHandler = null;
84
+
70
85
  const style = `
71
86
  :host {
72
87
  display: block;
@@ -137,13 +152,20 @@ export class FxFore extends HTMLElement {
137
152
  #messageContent{
138
153
  margin-top:40px;
139
154
  }
155
+ .warning{
156
+ background:orange;
157
+ }
140
158
  `;
141
159
 
142
160
  const html = `
161
+ <noscript>This page uses Web Components and needs JavaScript to be enabled..</noscript>
162
+
143
163
  <jinn-toast id="message" gravity="bottom" position="left"></jinn-toast>
144
164
  <jinn-toast id="sticky" gravity="bottom" position="left" duration="-1" close="true" data-class="sticky-message"></jinn-toast>
145
- <jinn-toast id="error" text="error" duration="-1" data-class="error" close="true" position="left" gravity="bottom"></jinn-toast>
146
- <slot></slot>
165
+ <jinn-toast id="error" text="error" duration="-1" data-class="error" close="true" position="left" gravity="bottom" escape-markup="false"></jinn-toast>
166
+ <jinn-toast id="warn" text="warning" duration="-1" data-class="warning" close="true" position="right" gravity="bottom"></jinn-toast>
167
+ <slot id="default"></slot>
168
+ <slot name="messages"></slot>
147
169
  <div id="modalMessage" class="overlay">
148
170
  <div class="popup">
149
171
  <h2></h2>
@@ -151,6 +173,7 @@ export class FxFore extends HTMLElement {
151
173
  <div id="messageContent"></div>
152
174
  </div>
153
175
  </div>
176
+ <slot name="event"></slot>
154
177
  `;
155
178
 
156
179
  this.attachShadow({mode: 'open'});
@@ -165,6 +188,7 @@ export class FxFore extends HTMLElement {
165
188
  this.initialRun = true;
166
189
  this.someInstanceDataStructureChanged = false;
167
190
  this.repeatsFromAttributesCreated = false;
191
+ this.validateOn = this.hasAttribute('validate-on') ? this.getAttribute('validate-on'):'update';
168
192
  }
169
193
 
170
194
  connectedCallback() {
@@ -205,10 +229,12 @@ export class FxFore extends HTMLElement {
205
229
  return;
206
230
  }
207
231
 
208
- const slot = this.shadowRoot.querySelector('slot');
209
- slot.addEventListener('slotchange', async event => {
232
+ this._injectDevtools();
210
233
 
234
+ const slot = this.shadowRoot.querySelector('slot#default');
235
+ slot.addEventListener('slotchange', async event => {
211
236
  // preliminary addition for auto-conversion of non-prefixed element into prefixed elements. See fore.js
237
+ if(this.inited) return;
212
238
  if(this.hasAttribute('convert')){
213
239
  this.replaceWith(Fore.copyDom(this));
214
240
  // Fore.copyDom(this);
@@ -223,6 +249,9 @@ export class FxFore extends HTMLElement {
223
249
  const generatedModel = document.createElement('fx-model');
224
250
  this.appendChild(generatedModel);
225
251
  modelElement = generatedModel;
252
+ // We are going to get a new slotchange event immediately, because we changed a slot.
253
+ // so cancel this one.
254
+ return;
226
255
  }
227
256
  if (!modelElement.inited) {
228
257
  console.info(
@@ -230,23 +259,33 @@ export class FxFore extends HTMLElement {
230
259
  "background:#64b5f6; color:white; padding:1rem; display:inline-block; white-space: nowrap; border-radius:0.3rem;width:100%;",
231
260
  );
232
261
 
233
- if (this.src) {
234
- console.log('########## FORE: loaded from ... ', this.src, '##########');
235
- }
236
262
  await modelElement.modelConstruct();
237
263
  this._handleModelConstructDone();
238
264
  }
239
265
  this.model = modelElement;
240
266
 
241
267
  this._createRepeatsFromAttributes();
268
+ this.inited = true;
242
269
 
243
270
  });
244
271
  this.addEventListener('path-mutated', () => {
245
- // console.log('path-mutated event received', e.detail.path, e.detail.index);
246
272
  this.someInstanceDataStructureChanged = true;
247
273
  });
248
274
  }
249
275
 
276
+ _injectDevtools(){
277
+ if (this.ownerDocument.querySelector('fx-devtools')) {
278
+ // There's already a devtools, so we can ignore this one.
279
+ // One devtools can focus multiple fore elements
280
+ return;
281
+ }
282
+ const search = window.location.search;
283
+ const urlParams = new URLSearchParams(search);
284
+ if(urlParams.has('inspect')){
285
+ const devtools = document.createElement('fx-devtools');
286
+ document.body.appendChild(devtools);
287
+ }
288
+ }
250
289
  addToRefresh(modelItem) {
251
290
  const found = this.toRefresh.find(mi => mi.path === modelItem.path);
252
291
  if (!found) {
@@ -261,7 +300,7 @@ export class FxFore extends HTMLElement {
261
300
  * @private
262
301
  */
263
302
  _loadFromSrc() {
264
- console.log('########## loading Fore from ', this.src, '##########');
303
+ // console.log('########## loading Fore from ', this.src, '##########');
265
304
  fetch(this.src, {
266
305
  method: 'GET',
267
306
  mode: 'cors',
@@ -297,6 +336,12 @@ export class FxFore extends HTMLElement {
297
336
  });
298
337
  }
299
338
  theFore.setAttribute('from-src', this.src);
339
+ const thisAttrs = this.attributes;
340
+ Array.from(thisAttrs).forEach(attr =>{
341
+ if(attr.name !== 'src'){
342
+ theFore.setAttribute(attr.name,attr.value);
343
+ }
344
+ });
300
345
  this.replaceWith(theFore);
301
346
  })
302
347
  .catch(() => {
@@ -313,7 +358,7 @@ export class FxFore extends HTMLElement {
313
358
  * @param observer
314
359
  */
315
360
  handleIntersect(entries, observer) {
316
- console.time('refreshLazy');
361
+ // console.time('refreshLazy');
317
362
 
318
363
  entries.forEach(entry => {
319
364
  const {target} = entry;
@@ -322,7 +367,7 @@ export class FxFore extends HTMLElement {
322
367
  if(fore.initialRun) return;
323
368
 
324
369
  if (entry.isIntersecting) {
325
- console.log('in view', entry);
370
+ // console.log('in view', entry);
326
371
  // console.log('repeat in view entry', entry.target);
327
372
  // const target = entry.target;
328
373
  // if(target.hasAttribute('refresh-on-view')){
@@ -331,17 +376,17 @@ export class FxFore extends HTMLElement {
331
376
 
332
377
  // todo: too restrictive here? what if target is a usual html element? shouldn't it refresh downwards?
333
378
  if (typeof target.refresh === 'function') {
334
- console.log('refreshing target', target);
379
+ // console.log('refreshing target', target);
335
380
  target.refresh(target, true);
336
381
  } else {
337
- console.log('refreshing children', target);
382
+ // console.log('refreshing children', target);
338
383
  Fore.refreshChildren(target, true);
339
384
  }
340
385
  }
341
386
  });
342
387
  entries[0].target.getOwnerForm().dispatchEvent(new CustomEvent('refresh-done'));
343
388
 
344
- console.timeEnd('refreshLazy');
389
+ // console.timeEnd('refreshLazy');
345
390
  }
346
391
 
347
392
  evaluateToNodes(xpath, context) {
@@ -367,8 +412,8 @@ export class FxFore extends HTMLElement {
367
412
  *
368
413
  */
369
414
  async forceRefresh() {
370
- console.time('refresh');
371
- console.group('### forced refresh', this);
415
+ // console.time('refresh');
416
+ // console.group('### forced refresh', this);
372
417
 
373
418
  Fore.refreshChildren(this, true);
374
419
  this._updateTemplateExpressions();
@@ -376,23 +421,16 @@ export class FxFore extends HTMLElement {
376
421
  this._processTemplateExpressions();
377
422
  Fore.dispatch(this, 'refresh-done', {});
378
423
 
379
- console.groupEnd();
380
- console.timeEnd('refresh');
424
+ // console.groupEnd();
425
+ // console.timeEnd('refresh');
381
426
  }
382
427
 
383
428
  async refresh(force) {
384
429
  // refresh () {
385
- console.info('%crefresh','font-style: italic; background: #8bc34a; color:white; padding:0.3rem 5rem 0.3rem 0.3rem; display:block; width:100%;');
386
- console.group('refresh', force);
387
-
388
- console.time('refresh');
389
-
390
430
  // ### refresh Fore UI elements
391
- // console.time('refreshChildren');
392
- console.log('toRefresh', this.toRefresh);
393
-
394
431
  // if (!this.initialRun && this.toRefresh.length !== 0) {
395
432
  if (!force && !this.initialRun && this.toRefresh.length !== 0) {
433
+ // console.log('toRefresh', this.toRefresh);
396
434
  let needsRefresh = false;
397
435
 
398
436
  // ### after recalculation the changed modelItems are copied to 'toRefresh' array for processing
@@ -425,10 +463,20 @@ export class FxFore extends HTMLElement {
425
463
  }
426
464
  });
427
465
  this.toRefresh = [];
466
+ /*
428
467
  if (!needsRefresh) {
429
468
  console.log('no dependants to refresh');
430
469
  }
470
+ */
431
471
  } else {
472
+ // ### resetting visited state for controls to refresh
473
+ /*
474
+ const visited = this.parentNode.querySelectorAll('.visited');
475
+ Array.from(visited).forEach(v =>{
476
+ v.classList.remove('visited');
477
+ });
478
+ */
479
+
432
480
  Fore.refreshChildren(this, true);
433
481
  // console.timeEnd('refreshChildren');
434
482
  }
@@ -440,9 +488,6 @@ export class FxFore extends HTMLElement {
440
488
  }
441
489
  this._processTemplateExpressions();
442
490
 
443
- console.timeEnd('refresh');
444
-
445
- console.groupEnd();
446
491
  // console.log('### <<<<< dispatching refresh-done - end of UI update cycle >>>>>');
447
492
  // this.dispatchEvent(new CustomEvent('refresh-done'));
448
493
  // this.initialRun = false;
@@ -494,12 +539,12 @@ export class FxFore extends HTMLElement {
494
539
  for (const node of Array.from(this.storedTemplateExpressionByNode.keys())) {
495
540
  if (node.nodeType === Node.ATTRIBUTE_NODE) {
496
541
  // Attribute nodes are not contained by the document, but their owner elements are!
497
- if (!node.ownerDocument.contains(node.ownerElement)) {
542
+ if (!XPathUtil.contains(this, node.ownerElement)) {
498
543
  this.storedTemplateExpressionByNode.delete(node);
499
544
  continue;
500
545
  }
501
- } else if (!node.ownerDocument.contains(node)) {
502
- // For all other nodes, if the document does not contain them, they are dead
546
+ } else if (!XPathUtil.contains(this, node)) {
547
+ // For all other nodes, if this `fore` element does not contain them, they are dead
503
548
  this.storedTemplateExpressionByNode.delete(node);
504
549
  continue;
505
550
  }
@@ -521,10 +566,9 @@ export class FxFore extends HTMLElement {
521
566
  }
522
567
 
523
568
  /**
524
- * evaluate a template expression (some expression in {} brackets) on a node (either text- or attribute node.
525
- * @param input The string to parse for expressions
569
+ * evaluate a template expression on a node either text- or attribute node.
570
+ * @param expr The string to parse for expressions
526
571
  * @param node the node which will get updated with evaluation result
527
- * @param form the form element
528
572
  */
529
573
  evaluateTemplateExpression(expr, node) {
530
574
  const replaced = expr.replace(/{[^}]*}/g, match => {
@@ -542,11 +586,15 @@ export class FxFore extends HTMLElement {
542
586
  // Templates are special: they use the namespace configuration from the place where they are
543
587
  // being defined
544
588
  const instanceId = XPathUtil.getInstanceId(naked);
545
- const inst = this.getModel().getInstance(instanceId);
546
- try {
589
+
590
+ // If there is an instance referred
591
+ const inst = instanceId ? this.getModel().getInstance(instanceId) : this.getModel().getDefaultInstance();
592
+
593
+ try {
547
594
  return evaluateXPathToString(naked, inscope, node, null, inst);
548
595
  } catch (error) {
549
- console.log('ignoring unparseable expr');
596
+ console.warn('ignoring unparseable expr', error);
597
+
550
598
  return match;
551
599
  }
552
600
  });
@@ -590,21 +638,17 @@ export class FxFore extends HTMLElement {
590
638
  window.addEventListener('beforeunload', event => {
591
639
  const mustDisplay = evaluateXPathToBoolean(condition, this.getModel().getDefaultContext(), this)
592
640
  if(mustDisplay){
593
- console.log('have to display confirmation')
594
641
  return event.returnValue = 'are you sure';
595
642
  }
596
643
  event.preventDefault();
597
- console.log('do not display confirmation')
598
644
  })
599
645
  }else{
600
646
  window.addEventListener('beforeunload', event => {
601
647
  // if(AbstractAction.dataChanged){
602
648
  if(FxModel.dataChanged){
603
- console.log('have to display confirmation')
604
649
  return event.returnValue = 'are you sure';
605
650
  }
606
651
  event.preventDefault();
607
- console.log('do not display confirmation')
608
652
  })
609
653
  }
610
654
  }
@@ -622,7 +666,7 @@ export class FxFore extends HTMLElement {
622
666
  async _lazyCreateInstance() {
623
667
  const model = this.querySelector('fx-model');
624
668
  if (model.instances.length === 0) {
625
- console.log('### lazy creation of instance');
669
+ // console.log('### lazy creation of instance');
626
670
  const generatedInstance = document.createElement('fx-instance');
627
671
  model.appendChild(generatedInstance);
628
672
 
@@ -631,7 +675,8 @@ export class FxFore extends HTMLElement {
631
675
  this._generateInstance(this, generated.firstElementChild);
632
676
  generatedInstance.instanceData = generated;
633
677
  model.instances.push(generatedInstance);
634
- console.log('generatedInstance ', this.getModel().getDefaultInstanceData());
678
+ // console.log('generatedInstance ', this.getModel().getDefaultInstanceData());
679
+ Fore.dispatch(this,'instance-loaded',{instance:this})
635
680
  }
636
681
  }
637
682
 
@@ -644,11 +689,9 @@ export class FxFore extends HTMLElement {
644
689
  const ref = start.getAttribute('ref');
645
690
 
646
691
  if (ref.includes('/')) {
647
- console.log('complex path to create ', ref);
692
+ // console.log('complex path to create ', ref);
648
693
  const steps = ref.split('/');
649
694
  steps.forEach(step => {
650
- console.log('step ', step);
651
-
652
695
  // const generated = document.createElement(ref);
653
696
  parent = this._generateNode(parent, step, start);
654
697
  });
@@ -728,8 +771,9 @@ export class FxFore extends HTMLElement {
728
771
  * @private
729
772
  */
730
773
  async _initUI() {
731
- console.log('### _initUI()');
774
+ // console.log('### _initUI()');
732
775
  if (!this.initialRun) return;
776
+ this.classList.add('initialRun');
733
777
  await this._lazyCreateInstance();
734
778
 
735
779
  // console.log('registering variables!');
@@ -763,12 +807,8 @@ export class FxFore extends HTMLElement {
763
807
  // console.log('### >>>>> dispatching ready >>>>>', this);
764
808
  // console.log('### modelItems: ', this.getModel().modelItems);
765
809
  Fore.dispatch(this, 'ready', {});
766
- console.info(
767
- `%cPage Initialization done`,
768
- "background:#64b5f6; color:white; padding:1rem; display:block; white-space: nowrap; border-radius:0.3rem;width:100%;",
769
- );
810
+ // console.log('dataChanged', FxModel.dataChanged);
770
811
  console.timeEnd('init');
771
- console.log('dataChanged', FxModel.dataChanged);
772
812
  }
773
813
 
774
814
  registerLazyElement(element) {
@@ -808,6 +848,50 @@ export class FxFore extends HTMLElement {
808
848
  toast.showToast(msg);
809
849
  }
810
850
 
851
+ _displayWarning(e){
852
+ const msg = e.detail.message;
853
+ // this._showMessage('modal', msg);
854
+ const path = XPathUtil.shortenPath(evaluateXPathToString('path()',e.target,this));
855
+ const toast = this.shadowRoot.querySelector('#warn');
856
+ toast.showToast(`WARN: ${path}:${msg}`);
857
+ }
858
+
859
+ _logError(e) {
860
+ e.stopPropagation();
861
+ e.preventDefault();
862
+
863
+ const div = document.createElement('div');
864
+ div.setAttribute('slot','messages');
865
+ div.setAttribute('data-level',e.detail.level);
866
+
867
+ const id = document.createElement('div');
868
+ id.textContent = `"${e.detail.id}"`;
869
+ div.appendChild(id);
870
+
871
+ const path = document.createElement('div');
872
+ const pathExpr = XPathUtil.shortenPath(evaluateXPathToString('path()',e.target,this));
873
+ // console.log('pathExpr',pathExpr)
874
+ path.textContent = pathExpr;
875
+ div.appendChild(path);
876
+
877
+ const message = document.createElement('div');
878
+ message.textContent = e.detail.message;
879
+ div.appendChild(message);
880
+
881
+ /*
882
+ const path = XPathUtil.shortenPath(evaluateXPathToString('path()',e.target,this));
883
+ div.innerText = `${path} :: ${e.detail.message}`;
884
+ */
885
+ this.appendChild(div);
886
+ div.scrollIntoView({behavior: "smooth", block: "end", inline: "nearest"});
887
+
888
+
889
+ const errorElement = evaluateXPathToFirstNode(`/${pathExpr}`,document,null);
890
+ errorElement.classList.add('fore-error');
891
+
892
+
893
+ }
894
+
811
895
  _showMessage(level, msg) {
812
896
  if (level === 'modal') {
813
897
  // this.$.messageContent.innerText = msg;
@@ -834,6 +918,20 @@ export class FxFore extends HTMLElement {
834
918
  const repeats = this.querySelectorAll('[data-ref]');
835
919
  if(repeats){
836
920
  Array.from(repeats).forEach(item =>{
921
+ if(item.closest('fx-control')) return;
922
+ /*
923
+ const parentRepeat = item.closest('fx-repeat');
924
+ if(parentRepeat){
925
+ this.dispatchEvent(
926
+ new CustomEvent('log', {
927
+ composed: false,
928
+ bubbles: true,
929
+ cancelable:true,
930
+ detail: { id:this.id, message: `nesting elements with data-ref attributes within fx-repeat is not supported by now`, level:'Error'},
931
+ }),
932
+ );
933
+ }
934
+ */
837
935
 
838
936
  const table = item.parentNode.closest('table');
839
937
  let host;
@@ -30,12 +30,14 @@ async function handleResponse(response) {
30
30
  // console.log("********** inside res json *********");
31
31
  return response.json();
32
32
  }
33
- if (responseContentType.startsWith('application/xml')) {
33
+ if (responseContentType.startsWith('application/xml') ||
34
+ responseContentType.startsWith('text/xml')) {
35
+ // See https://www.rfc-editor.org/rfc/rfc7303
34
36
  const text = await response.text();
35
37
  // console.log('xml ********', result);
36
38
  return new DOMParser().parseFromString(text, 'application/xml');
37
39
  }
38
- return 'done';
40
+ throw new Error(`unable to handle response content type: ${responseContentType}`);
39
41
  }
40
42
 
41
43
  /**
@@ -49,6 +51,7 @@ export class FxInstance extends HTMLElement {
49
51
  super();
50
52
  this.model = this.parentNode;
51
53
  this.attachShadow({ mode: 'open' });
54
+ this.originalInstance = null;
52
55
  }
53
56
 
54
57
  connectedCallback() {
@@ -67,6 +70,7 @@ export class FxInstance extends HTMLElement {
67
70
  this.type = this.getAttribute('type');
68
71
  } else {
69
72
  this.type = 'xml';
73
+ this.setAttribute('type',this.type);
70
74
  }
71
75
  const style = `
72
76
  :host {
@@ -107,6 +111,11 @@ export class FxInstance extends HTMLElement {
107
111
  return this;
108
112
  }
109
113
 
114
+ reset(){
115
+ // this._useInlineData();
116
+ this.instanceData = this.originalInstance.cloneNode(true);
117
+ }
118
+
110
119
  evalXPath(xpath) {
111
120
  const formElement = this.parentElement.parentElement;
112
121
  const result = evaluateXPathToFirstNode(xpath, this.getDefaultContext(), formElement);
@@ -130,7 +139,8 @@ export class FxInstance extends HTMLElement {
130
139
  this.createInstanceData();
131
140
  return;
132
141
  }
133
- this.instanceData = data;
142
+ this._setInitialData(data);
143
+ // this.instanceData = data;
134
144
  }
135
145
 
136
146
  /**
@@ -168,7 +178,11 @@ export class FxInstance extends HTMLElement {
168
178
  newNode.appendChild(doc.createTextNode(p[1]));
169
179
  root.appendChild(newNode);
170
180
  }
181
+ this._setInitialData(doc);
182
+ /*
171
183
  this.instanceData = doc;
184
+ this.originalInstance = this.instanceData.cloneNode(true);
185
+ */
172
186
  // this.instanceData.firstElementChild.setAttribute('id', this.id);
173
187
  // resolve('done');
174
188
  } else if (this.src) {
@@ -183,9 +197,11 @@ export class FxInstance extends HTMLElement {
183
197
  // const doc = new DOMParser().parseFromString('<data data-id="default"></data>', 'application/xml');
184
198
  const doc = new DOMParser().parseFromString('<data></data>', 'application/xml');
185
199
  this.instanceData = doc;
200
+ this.originalInstance = this.instanceData.cloneNode(true);
186
201
  }
187
202
  if (this.type === 'json') {
188
203
  this.instanceData = {};
204
+ this.originalInstance = [...this.instanceData];
189
205
  }
190
206
  }
191
207
 
@@ -196,8 +212,8 @@ export class FxInstance extends HTMLElement {
196
212
  const key = url.substring(url.indexOf(':') + 1);
197
213
 
198
214
  const doc = new DOMParser().parseFromString('<data></data>', 'application/xml');
199
- const root = doc.firstElementChild;
200
215
  this.instanceData = doc;
216
+ // ### does it make sense to store originalData here?
201
217
 
202
218
  if (!key) {
203
219
  console.warn('no key specified for localStore');
@@ -212,7 +228,6 @@ export class FxInstance extends HTMLElement {
212
228
  }
213
229
  const data = new DOMParser().parseFromString(serialized, 'application/xml');
214
230
  // let data = this._parse(serialized, instance);
215
- // root.appendChild(data);
216
231
  doc.firstElementChild.replaceWith(data.firstElementChild);
217
232
  return;
218
233
  }
@@ -221,25 +236,41 @@ export class FxInstance extends HTMLElement {
221
236
  try {
222
237
  const response = await fetch(url, {
223
238
  method: 'GET',
239
+ /*
224
240
  mode: 'cors',
225
241
  credentials: 'include',
242
+ */
226
243
  headers: {
227
244
  'Content-Type': contentType,
228
245
  },
229
246
  });
230
- const { status } = response;
231
247
  const data = await handleResponse(response);
248
+ this._setInitialData(data);
249
+ /*
232
250
  if (data.nodeType) {
251
+ this._setInitialData(data);
233
252
  this.instanceData = data;
253
+ this.originalInstance = this.instanceData.cloneNode(true);
234
254
  console.log('instanceData loaded: ', this.id, this.instanceData);
235
255
  return;
236
256
  }
237
257
  this.instanceData = data;
258
+ this.originalInstance = [...data];
259
+ */
238
260
  } catch (error) {
239
261
  throw new Error(`failed loading data ${error}`);
240
262
  }
241
263
  }
242
264
 
265
+ _setInitialData(data){
266
+ this.instanceData = data;
267
+ if(data.nodeType){
268
+ this.originalInstance = this.instanceData.cloneNode(true);
269
+ } else {
270
+ this.originalInstance = {...this.instanceData};
271
+ }
272
+ }
273
+
243
274
  _getContentType() {
244
275
  if (this.type === 'xml') {
245
276
  return 'application/xml';
@@ -257,7 +288,8 @@ export class FxInstance extends HTMLElement {
257
288
  const instanceData = new DOMParser().parseFromString(this.innerHTML, 'application/xml');
258
289
 
259
290
  // console.log('fx-instance init id:', this.id);
260
- this.instanceData = instanceData;
291
+ // this.instanceData = instanceData;
292
+ this._setInitialData(instanceData);
261
293
  // console.log('instanceData ', this.instanceData);
262
294
  // console.log('instanceData ', this.instanceData.firstElementChild);
263
295
 
@@ -265,28 +297,34 @@ export class FxInstance extends HTMLElement {
265
297
  // this.instanceData.firstElementChild.setAttribute('id', this.id);
266
298
  // todo: move innerHTML out to shadowDOM (for later reset)
267
299
  } else if (this.type === 'json') {
268
- this.instanceData = JSON.parse(this.textContent);
300
+ // this.instanceData = JSON.parse(this.textContent);
301
+ this._setInitialData(JSON.parse(this.textContent));
269
302
  } else if (this.type === 'html') {
270
- this.instanceData = this.firstElementChild.children;
303
+ // this.instanceData = this.firstElementChild.children;
304
+ this._setInitialData(this.firstElementChild.children)
305
+
271
306
  } else if (this.type === 'text') {
272
- this.instanceData = this.textContent;
307
+ // this.instanceData = this.textContent;
308
+ this._setInitialData(this.textContent)
273
309
  } else {
274
310
  console.warn('unknow type for data ', this.type);
275
311
  }
276
312
  }
277
313
 
278
- _handleResponse() {
279
- console.log('_handleResponse ');
280
- const ajax = this.shadowRoot.getElementById('loader');
281
- const instanceData = new DOMParser().parseFromString(ajax.lastResponse, 'application/xml');
282
- this.instanceData = instanceData;
283
- console.log('data: ', this.instanceData);
284
- }
314
+ // _handleResponse() {
315
+ // console.log('_handleResponse ');
316
+ // const ajax = this.shadowRoot.getElementById('loader');
317
+ // const instanceData = new DOMParser().parseFromString(ajax.lastResponse, 'application/xml');
318
+ // this.instanceData = instanceData;
319
+ // console.log('data: ', this.instanceData);
320
+ // }
285
321
 
322
+ /*
286
323
  _handleError() {
287
324
  const loader = this.shadowRoot.getElementById('loader');
288
325
  console.log('_handleResponse ', loader.lastError);
289
326
  }
327
+ */
290
328
  }
291
329
  if (!customElements.get('fx-instance')) {
292
330
  customElements.define('fx-instance', FxInstance);