@jinntec/fore 1.9.0 → 1.10.1

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/src/fx-bind.js CHANGED
@@ -79,8 +79,10 @@ export class FxBind extends foreElementMixin(HTMLElement) {
79
79
 
80
80
  _buildBindGraph() {
81
81
  if (this.bindType === 'xml') {
82
- this.nodeset.forEach(node => {
83
- const path = XPathUtil.getPath(node);
82
+ this.nodeset.forEach(node => {
83
+ const instance = XPathUtil.resolveInstance(this,this.ref);
84
+
85
+ const path = XPathUtil.getPath(node, instance);
84
86
  this.model.mainGraph.addNode(path, node);
85
87
 
86
88
  /* ### catching references in the 'ref' itself...
@@ -156,8 +158,10 @@ export class FxBind extends foreElementMixin(HTMLElement) {
156
158
  if (!this.model.mainGraph.hasNode(nodeHash)) {
157
159
  this.model.mainGraph.addNode(nodeHash, node);
158
160
  }
159
- refs.forEach(ref => {
160
- const otherPath = XPathUtil.getPath(ref);
161
+ refs.forEach(ref => {
162
+ const instance = XPathUtil.resolveInstance(this, path);
163
+
164
+ const otherPath = XPathUtil.getPath(ref, instance);
161
165
  // console.log('otherPath', otherPath)
162
166
 
163
167
  // todo: nasty hack to prevent duplicate pathes like 'a[1]' and 'a[1]/text()[1]' to end up as separate nodes in the graph
@@ -301,8 +305,10 @@ export class FxBind extends foreElementMixin(HTMLElement) {
301
305
  const targetNode = node;
302
306
 
303
307
  // const path = fx.evaluateXPath('path()',node);
304
- // const path = this.getPath(node);
305
- const path = XPathUtil.getPath(node);
308
+ // const path = this.getPath(node);
309
+ const instance = XPathUtil.resolveInstance(this, this.ref);
310
+
311
+ const path = XPathUtil.getPath(node, instance);
306
312
  // const shortPath = this.shortenPath(path);
307
313
 
308
314
  // ### constructing default modelitem - will get evaluated during recalculate()
package/src/fx-fore.js CHANGED
@@ -85,9 +85,10 @@ export class FxFore extends HTMLElement {
85
85
  // this.addEventListener('model-construct-done', this._handleModelConstructDone);
86
86
  // todo: refactoring - these should rather go into connectedcallback
87
87
  this.addEventListener('message', this._displayMessage);
88
- this.addEventListener('error', this._displayError);
88
+ // this.addEventListener('error', this._displayError);
89
+ this.addEventListener('error', this._logError);
89
90
  this.addEventListener('warn', this._displayWarning);
90
- this.addEventListener('log', this._logError);
91
+ // this.addEventListener('log', this._logError);
91
92
  window.addEventListener('compute-exception', e => {
92
93
  console.error('circular dependency: ', e);
93
94
  });
@@ -176,7 +177,7 @@ export class FxFore extends HTMLElement {
176
177
 
177
178
  const html = `
178
179
  <noscript>This page uses Web Components and needs JavaScript to be enabled..</noscript>
179
-
180
+ <!-- <slot name="errors"></slot> -->
180
181
  <jinn-toast id="message" gravity="bottom" position="left"></jinn-toast>
181
182
  <jinn-toast id="sticky" gravity="bottom" position="left" duration="-1" close="true" data-class="sticky-message"></jinn-toast>
182
183
  <jinn-toast id="error" text="error" duration="-1" data-class="error" close="true" position="right" gravity="top" escape-markup="false"></jinn-toast>
@@ -295,6 +296,7 @@ export class FxFore extends HTMLElement {
295
296
  this.addEventListener('path-mutated', () => {
296
297
  this.someInstanceDataStructureChanged = true;
297
298
  });
299
+
298
300
  }
299
301
 
300
302
  _injectDevtools(){
@@ -323,9 +325,9 @@ export class FxFore extends HTMLElement {
323
325
  * Will extract the `fx-fore` element from that target file and use and replace current `fx-fore` element with the loaded one.
324
326
  * @private
325
327
  */
326
- _loadFromSrc() {
328
+ async _loadFromSrc() {
327
329
  // console.log('########## loading Fore from ', this.src, '##########');
328
- fetch(this.src, {
330
+ await fetch(this.src, {
329
331
  method: 'GET',
330
332
  mode: 'cors',
331
333
  credentials: 'include',
@@ -449,7 +451,50 @@ export class FxFore extends HTMLElement {
449
451
  // console.timeEnd('refresh');
450
452
  }
451
453
 
454
+ // async refresh(force, changedPaths) {
452
455
  async refresh(force) {
456
+
457
+ /*
458
+
459
+ if (!changedPaths) {
460
+ changedPaths = this.toRefresh.map(item => item.path);
461
+ } else {
462
+ this.toRefresh.push(
463
+ ...changedPaths
464
+ .map(
465
+ path =>
466
+ this.getModel()
467
+ .modelItems
468
+ .find(item => item.path === path)
469
+ )
470
+ .filter(Boolean)
471
+ );
472
+
473
+ for(const changedPath of changedPaths) {
474
+ for (const repeat of this.querySelectorAll('fx-repeat')) {
475
+ if (repeat.closest('fx-fore') !== this) {
476
+ continue;
477
+ }
478
+
479
+ if (repeat.touchedPaths && repeat.touchedPaths.has(changedPath)) {
480
+ // Make a temporary model-item-like structure for this
481
+ this.toRefresh.push({
482
+ path: changedPath,
483
+ boundControls: [repeat]
484
+ });
485
+
486
+ console.log('Found a repeat to update!!!', repeat)
487
+ }
488
+ }
489
+ }
490
+ }
491
+ */
492
+ if (this.isRefreshing) {
493
+ return;
494
+ }
495
+ this.isRefreshing = true;
496
+ console.log('### <<<<< refresh() >>>>>');
497
+
453
498
  // refresh () {
454
499
  // ### refresh Fore UI elements
455
500
  // if (!this.initialRun && this.toRefresh.length !== 0) {
@@ -468,7 +513,7 @@ export class FxFore extends HTMLElement {
468
513
  }
469
514
 
470
515
  // ### check if other controls depend on current modelItem
471
- const {mainGraph} = this.getModel();
516
+ const { mainGraph } = this.getModel();
472
517
  if (mainGraph && mainGraph.hasNode(modelItem.path)) {
473
518
  const deps = this.getModel().mainGraph.dependentsOf(modelItem.path, false);
474
519
  // ### iterate dependant modelItems and refresh all their boundControls
@@ -501,7 +546,9 @@ export class FxFore extends HTMLElement {
501
546
  });
502
547
  */
503
548
 
504
- Fore.refreshChildren(this, true);
549
+ if(this.inited){
550
+ Fore.refreshChildren(this, true);
551
+ }
505
552
  // console.timeEnd('refreshChildren');
506
553
  }
507
554
 
@@ -517,6 +564,15 @@ export class FxFore extends HTMLElement {
517
564
  // this.initialRun = false;
518
565
  this.style.visibility='visible';
519
566
  Fore.dispatch(this, 'refresh-done', {});
567
+
568
+ // this.isRefreshing = true;
569
+ // this.parentNode.closest('fx-fore')?.refresh(false, changedPaths);
570
+ this.parentNode.closest('fx-fore')?.refresh(false);
571
+ for (const subFore of this.querySelectorAll('fx-fore')) {
572
+ // subFore.refresh(false, changedPaths);
573
+ subFore.refresh(false);
574
+ }
575
+ this.isRefreshing = false;
520
576
  }
521
577
 
522
578
  /**
@@ -604,6 +660,13 @@ export class FxFore extends HTMLElement {
604
660
  * @param node the node which will get updated with evaluation result
605
661
  */
606
662
  evaluateTemplateExpression(expr, node) {
663
+
664
+ // ### do not evaluate template expressions with nonrelevant sections
665
+ if(node.nodeType === Node.ATTRIBUTE_NODE && node.ownerElement.closest('[nonrelevant]')) return;
666
+ if(node.nodeType === Node.TEXT_NODE && node.parentNode.closest('[nonrelevant]')) return;
667
+ if(node.nodeType === Node.ELEMENT_NODE && node.closest('[nonrelevant]')) return;
668
+
669
+ // if(node.closest('[nonrelevant]')) return;
607
670
  const replaced = expr.replace(/{[^}]*}/g, match => {
608
671
  if (match === '{}') return match;
609
672
  const naked = match.substring(1, match.length - 1);
@@ -705,6 +768,18 @@ export class FxFore extends HTMLElement {
705
768
  */
706
769
  async _lazyCreateInstance() {
707
770
  const model = this.querySelector('fx-model');
771
+ // Inherit shared models from the parent component
772
+
773
+ const parentFore = this.parentNode.closest('fx-fore');
774
+ if (parentFore) {
775
+ const sharedInstances = Array.from(parentFore.getModel().querySelectorAll('fx-instance')).filter(instance => instance.hasAttribute('shared'));
776
+ for(const instance of sharedInstances) {
777
+ this.getModel().instances.push(instance);
778
+ }
779
+ this.getModel().updateModel();
780
+ }
781
+
782
+
708
783
  if (model.instances.length === 0) {
709
784
  // console.log('### lazy creation of instance');
710
785
  const generatedInstance = document.createElement('fx-instance');
@@ -716,7 +791,7 @@ export class FxFore extends HTMLElement {
716
791
  generatedInstance.instanceData = generated;
717
792
  model.instances.push(generatedInstance);
718
793
  // console.log('generatedInstance ', this.getModel().getDefaultInstanceData());
719
- Fore.dispatch(this,'instance-loaded',{instance:this})
794
+ Fore.dispatch(this,'instance-loaded',{instance:this});
720
795
  }
721
796
  }
722
797
 
@@ -812,6 +887,8 @@ export class FxFore extends HTMLElement {
812
887
  */
813
888
  async _initUI() {
814
889
  // console.log('### _initUI()');
890
+ console.log('### <<<<< _initUI >>>>>');
891
+
815
892
  if (!this.initialRun) return;
816
893
  this.classList.add('initialRun');
817
894
  await this._lazyCreateInstance();
@@ -845,6 +922,8 @@ export class FxFore extends HTMLElement {
845
922
  this.ready = true;
846
923
  this.initialRun = false;
847
924
  // console.log('### >>>>> dispatching ready >>>>>', this);
925
+ console.log(`### <<<<< ${this.id} ready >>>>>`);
926
+
848
927
  // console.log('### modelItems: ', this.getModel().modelItems);
849
928
  Fore.dispatch(this, 'ready', {});
850
929
  // console.log('dataChanged', FxModel.dataChanged);
@@ -896,39 +975,23 @@ export class FxFore extends HTMLElement {
896
975
  toast.showToast(`WARN: ${path}:${msg}`);
897
976
  }
898
977
 
978
+
899
979
  _logError(e) {
900
980
  e.stopPropagation();
901
981
  e.preventDefault();
902
982
 
903
- const div = document.createElement('div');
904
- div.setAttribute('slot','messages');
905
- div.setAttribute('data-level',e.detail.level);
906
-
907
- const id = document.createElement('div');
908
- id.textContent = `"${e.detail.id}"`;
909
- div.appendChild(id);
910
-
911
- const path = document.createElement('div');
912
- const pathExpr = XPathUtil.shortenPath(evaluateXPathToString('path()',e.target,this));
913
- // console.log('pathExpr',pathExpr)
914
- path.textContent = pathExpr;
915
- div.appendChild(path);
916
-
917
- const message = document.createElement('div');
918
- message.textContent = e.detail.message;
919
- div.appendChild(message);
920
-
921
- /*
922
- const path = XPathUtil.shortenPath(evaluateXPathToString('path()',e.target,this));
923
- div.innerText = `${path} :: ${e.detail.message}`;
924
- */
925
- this.appendChild(div);
926
- div.scrollIntoView({behavior: "smooth", block: "end", inline: "nearest"});
927
-
928
-
929
- const errorElement = evaluateXPathToFirstNode(`/${pathExpr}`,document,null);
930
- errorElement.classList.add('fore-error');
983
+ console.error('ERROR',e.detail.message);
984
+ console.error(e.detail.origin);
985
+ if(e.detail.expr){
986
+ console.error('Failing expression',e.detail.expr);
987
+ }
988
+ console.error('---');
989
+ this._displayError(e);
990
+ }
931
991
 
992
+ _copyToClipboard(target){
993
+ console.log('copyToClipboard' , target.value)
994
+ navigator.clipboard.writeText(target.value);
932
995
 
933
996
  }
934
997
 
package/src/fx-model.js CHANGED
@@ -67,7 +67,9 @@ export class FxModel extends HTMLElement {
67
67
  // const path = fx.evaluateXPath('path()',node);
68
68
  let path;
69
69
  if (node.nodeType) {
70
- path = XPathUtil.getPath(node);
70
+ const instance = XPathUtil.resolveInstance(model, ref);
71
+
72
+ path = XPathUtil.getPath(node, instance);
71
73
  } else {
72
74
  path = null;
73
75
  targetNode = node;
@@ -102,7 +104,7 @@ export class FxModel extends HTMLElement {
102
104
  *
103
105
  */
104
106
  async modelConstruct() {
105
- // console.log('### <<<<< dispatching model-construct >>>>>');
107
+ console.log('### <<<<< dispatching model-construct >>>>>');
106
108
  // this.dispatchEvent(new CustomEvent('model-construct', { detail: this }));
107
109
  Fore.dispatch(this, 'model-construct', {model: this});
108
110
 
@@ -120,12 +122,12 @@ export class FxModel extends HTMLElement {
120
122
  this.instances = Array.from(instances);
121
123
  // console.log('_modelConstruct this.instances ', this.instances);
122
124
  // Await until the model-construct-done event is handled off
123
-
124
125
  await Fore.dispatch(this, 'model-construct-done', {model: this});
125
126
  this.inited = true;
126
127
  this.updateModel();
127
128
  } else {
128
129
  // ### if there's no instance one will created
130
+ console.log('### <<<<< dispatching model-construct-done >>>>>');
129
131
  await this.dispatchEvent(
130
132
  new CustomEvent('model-construct-done', {
131
133
  composed: false,
@@ -163,6 +165,8 @@ export class FxModel extends HTMLElement {
163
165
  }
164
166
 
165
167
  rebuild() {
168
+ console.log('### <<<<< rebuild() >>>>>');
169
+
166
170
  this.mainGraph = new DepGraph(false); // do: should be moved down below binds.length check but causes errors in tests.
167
171
  this.modelItems = [];
168
172
 
@@ -192,10 +196,13 @@ export class FxModel extends HTMLElement {
192
196
  * todo: use 'changed' flag on modelItems to determine subgraph for recalculation. Flag already exists but is not used.
193
197
  */
194
198
  recalculate() {
199
+
195
200
  if (!this.mainGraph) {
196
201
  return;
197
202
  }
198
203
 
204
+ console.log('### <<<<< recalculate() >>>>>');
205
+
199
206
  // console.log('changed nodes ', this.changed);
200
207
  this.computes = 0;
201
208
 
@@ -359,14 +366,13 @@ export class FxModel extends HTMLElement {
359
366
 
360
367
  if (this.modelItems.length === 0) return true;
361
368
 
362
- console.time('revalidate');
369
+ console.log('### <<<<< revalidate() >>>>>');
363
370
 
364
371
  // reset submission validation
365
372
  // this.parentNode.classList.remove('submit-validation-failed')
366
373
  let valid = true;
367
374
  this.modelItems.forEach(modelItem => {
368
375
  // console.log('validating node ', modelItem.node);
369
-
370
376
  const {bind} = modelItem;
371
377
  if (bind) {
372
378
  /*
@@ -380,7 +386,10 @@ export class FxModel extends HTMLElement {
380
386
  // console.log('modelItem validity computed: ', compute);
381
387
  modelItem.constraint = compute;
382
388
  this.formElement.addToRefresh(modelItem); // let fore know that modelItem needs refresh
383
- if (!compute) valid = false;
389
+ if (!compute) {
390
+ console.log('validation failed on modelitem ', modelItem);
391
+ valid = false;
392
+ }
384
393
  }
385
394
  }
386
395
  if (typeof bind.hasAttribute === 'function' && bind.hasAttribute('required')) {
@@ -391,6 +400,7 @@ export class FxModel extends HTMLElement {
391
400
  modelItem.required = compute;
392
401
  this.formElement.addToRefresh(modelItem); // let fore know that modelItem needs refresh
393
402
  if (!modelItem.node.textContent) {
403
+ console.log('validation failed on modelitem ', modelItem);
394
404
  valid = false;
395
405
  }
396
406
  // if (!compute) valid = false;
@@ -450,7 +460,12 @@ export class FxModel extends HTMLElement {
450
460
  const instArray = Array.from(this.instances);
451
461
  const found = instArray.find(inst => inst.id === id);
452
462
  if(!found){
453
- return this.getDefaultInstance(); // if id is not found always defaults to first in doc order
463
+ // return this.getDefaultInstance(); // if id is not found always defaults to first in doc order
464
+ Fore.dispatch(this, 'error', {
465
+ origin: this,
466
+ message: `Instance '${id}' does not exist`,
467
+ level:'Error'
468
+ });
454
469
  }
455
470
  return found;
456
471
  }
@@ -144,6 +144,7 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
144
144
  const data = this._parse(serialized, instance);
145
145
  this._handleResponse(data);
146
146
  // this.dispatch('submit-done', {});
147
+ console.log('### <<<<< submit-done >>>>>');
147
148
  Fore.dispatch(this, 'submit-done', {});
148
149
  this.parameters.clear();
149
150
  return;
@@ -166,6 +167,7 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
166
167
  if (this.method === 'consume') {
167
168
  localStorage.removeItem(key);
168
169
  }
170
+ console.log('### <<<<< submit-done >>>>>');
169
171
  Fore.dispatch(this, 'submit-done', {});
170
172
  }
171
173
  if (this.method === 'post') {
@@ -173,6 +175,7 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
173
175
  const key = resolvedUrl.substring(resolvedUrl.indexOf(':') + 1);
174
176
  localStorage.setItem(key, serialized);
175
177
  this._handleResponse(instance.instanceData);
178
+ console.log('### <<<<< submit-done >>>>>');
176
179
  Fore.dispatch(this, 'submit-done', {});
177
180
  }
178
181
  if (this.method === 'delete') {
@@ -180,6 +183,7 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
180
183
  localStorage.removeItem(key);
181
184
  const newInst = new DOMParser().parseFromString('<data></data>', 'application/xml');
182
185
  this._handleResponse(newInst);
186
+ console.log('### <<<<< submit-done >>>>>');
183
187
  Fore.dispatch(this, 'submit-done', {});
184
188
  }
185
189
 
@@ -228,6 +232,7 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
228
232
  }
229
233
 
230
234
  // this.dispatch('submit-done', {});
235
+ console.log(`### <<<<< ${this.id} submit-done >>>>>`);
231
236
  Fore.dispatch(this, 'submit-done', {});
232
237
  } catch (error) {
233
238
  Fore.dispatch(this, 'submit-error', {error: error.message});
@@ -339,26 +344,20 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
339
344
 
340
345
  /*
341
346
  if(this.replace === 'merge'){
347
+ if(targetInstance.type !== 'xml') {
348
+ Fore.dispatch(this, "warn", {'message': 'merging of instances only work for type xml'});
349
+ }
342
350
  if (targetInstance && targetInstance.type === 'xml') {
343
351
  targetInstance.partialInstance = data;
344
-
345
- const merged = this._mergeXML(targetInstance.instanceData,targetInstance.partialInstance);
352
+ // const resultDoc = new DOMParser(`${data.nodeName}`, 'application/xml');
353
+ // console.log('resultDoc', resultDoc)
354
+ const merged = Fore.combine(targetInstance.instanceData.firstElementChild, data.firstElementChild, this,null);
346
355
  console.log('merged', merged);
347
356
 
348
357
  targetInstance.instanceData = merged;
349
358
  console.log('merging partial instance',targetInstance.partialInstance)
350
- /!*
351
- targetInstance.instanceData not touched here as we want to keep the default instance unmodified as the full template for the UI.
352
- *!/
353
-
354
- // Skip any refreshes if the model is not yet inited#
355
- // duplicate from replace='instance'
356
- // if (this.model.inited) {
357
- this.model.updateModel(); // force update
358
- const owner = this.getOwnerForm();
359
- // owner.mergePartial = true;
360
- owner.refresh(true);
361
- // }
359
+ this.model.updateModel();
360
+ this.getOwnerForm().refresh(true);
362
361
  }
363
362
  }
364
363
  */
package/src/tools/adi.js CHANGED
@@ -11,8 +11,10 @@ import {
11
11
 
12
12
  import {Fore} from '../fore.js';
13
13
 
14
- function isAttributeShown(name) {
15
- return name === 'id' || name === 'ref' || name === 'event';
14
+ function isAttributeShown(name, sourceNode) {
15
+ if(name === 'style') return false;
16
+ return true;
17
+ // return name === 'id' || name === 'ref' || name === 'event';
16
18
  }
17
19
 
18
20
  class ADI {
@@ -187,10 +189,16 @@ class ADI {
187
189
  if (sourceNode.nodeType !== Node.DOCUMENT_NODE) {
188
190
  // tagStart.textContent = '<' + node.nodeName.toLowerCase() + '>';
189
191
 
192
+ /*
193
+ let attrString = `<${sourceNode.nodeName.toLowerCase()} `;
194
+ if(sourceNode.attributes){
195
+ Array.from(sourceNode.attributes).forEach(attr => {
196
+ attrString += `${attr.nodeName}="${attr.nodeValue}" `;
197
+ });
198
+ console.log('ATTRSTRING',attrString);
199
+ }
190
200
  if (sourceNode.nodeName === 'FX-BIND') {
191
- tagStart.textContent = `<${sourceNode.nodeName.toLowerCase()} ref="${sourceNode.getAttribute(
192
- 'ref',
193
- )}">`;
201
+ tagStart.textContent = `<${sourceNode.nodeName.toLowerCase()} ref="${sourceNode.getAttribute('ref')}">`;
194
202
  } else if (sourceNode.nodeName === 'FX-INSERT') {
195
203
  tagStart.textContent = `<${sourceNode.nodeName.toLowerCase()} ref="${sourceNode.getAttribute('ref')}">`;
196
204
  } else if (sourceNode.nodeName === 'FX-INSTANCE') {
@@ -204,16 +212,17 @@ class ADI {
204
212
  } else if (sourceNode.nodeName === 'FX-SUBMISSION') {
205
213
  tagStart.textContent = `<${sourceNode.nodeName.toLowerCase()} id="${sourceNode.getAttribute('id')}">`;
206
214
  } else {
215
+ */
207
216
  const attrString = Array.from(sourceNode.attributes)
208
217
  .filter(
209
- attr => this.isInstanceViewer ? true : isAttributeShown(attr.name))
218
+ attr => this.isInstanceViewer ? true : isAttributeShown(attr.name, sourceNode))
210
219
  .map(attr => `${attr.name}="${attr.value}"`).join(' ');
211
220
  tagStart.textContent = `<${
212
221
  sourceNode.nodeName.toLowerCase()
213
222
  }${
214
223
  attrString ? (` ${attrString}`) : ''
215
224
  }>`;
216
- }
225
+ // }
217
226
 
218
227
  if (withChildren) {
219
228
  tagEnd = newElement('span');
@@ -459,7 +459,7 @@ export class FxActionLog extends HTMLElement {
459
459
  */
460
460
  _logDetails(e) {
461
461
  const eventType = e.type;
462
- const path = XPathUtil.getPath(e.target);
462
+ const path = XPathUtil.getDocPath(e.target);
463
463
  // console.log('>>>> _logDetails', path);
464
464
  const cut = path.substring(path.indexOf('/fx-fore'), path.length);
465
465
  const xpath = `/${cut}`;