@jinntec/fore 1.10.2 → 2.0.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jinntec/fore",
3
- "version": "1.10.2",
3
+ "version": "2.0.0",
4
4
  "description": "Fore - declarative user interfaces in plain HTML",
5
5
  "module": "./index.js",
6
6
  "publishConfig": {
@@ -185,7 +185,7 @@ export class AbstractAction extends foreElementMixin(HTMLElement) {
185
185
  * @param e
186
186
  */
187
187
  async execute(e) {
188
- console.log(this);
188
+ console.log(this, this.event);
189
189
  // console.log('execute', this.event);
190
190
 
191
191
 
@@ -5,10 +5,9 @@ import {XPathUtil} from "../xpath-util.js";
5
5
  import {Fore} from "../fore.js";
6
6
 
7
7
  /**
8
- * `fx-message`
9
- *
10
- * Action to display messages to the user.
8
+ * `fx-load`
11
9
  *
10
+ * Action to load a window, tab or embed some Html into the current page at given location.
12
11
  *
13
12
  */
14
13
  class FxLoad extends AbstractAction {
@@ -145,24 +144,14 @@ class FxLoad extends AbstractAction {
145
144
  });
146
145
  const data = await response.text();
147
146
  // console.log('data loaded: ', data);
147
+ // const data = Fore.loadHtml(resolvedUrl);
148
148
 
149
149
  // todo: if data contain '<template' element as first child instanciate and insert it
150
150
  if (!this.attachTo) {
151
151
  this.innerHtml = data;
152
152
  }
153
- /*
154
- if (this.attachTo.startsWith('#')) {
155
- const targetId = this.attachTo.substring(1);
156
- const resolved = resolveId(targetId, this);
157
- resolved.innerHTML = '';
158
- resolved.innerHTML = data;
159
- }
160
- */
161
153
  this._attachToElement(data);
162
-
163
-
164
154
  Fore.dispatch(this, 'loaded', {url: this.url})
165
-
166
155
  } catch (error) {
167
156
  throw new Error(`failed loading data ${error}`);
168
157
  }
@@ -228,27 +217,6 @@ class FxLoad extends AbstractAction {
228
217
  return replaced;
229
218
  }
230
219
 
231
-
232
- /*
233
- _getValue() {
234
- if (this.hasAttribute('value')) {
235
- const valAttr = this.getAttribute('value');
236
- try {
237
- const inscopeContext = getInScopeContext(this, valAttr);
238
- return evaluateXPathToString(valAttr, inscopeContext, this);
239
- } catch (error) {
240
- console.error(error);
241
- Fore.dispatch(this, 'error', {message: error});
242
- }
243
- }
244
- if (this.textContent) {
245
- return this.textContent;
246
- }
247
- return null;
248
- }
249
- */
250
-
251
-
252
220
  }
253
221
 
254
222
  if (!customElements.get('fx-load')) {
@@ -21,6 +21,7 @@ class FxRefresh extends AbstractAction {
21
21
  );
22
22
 
23
23
  if (this.hasAttribute('self')) {
24
+ console.log(`### <<<<< refresh() self ${this} >>>>>`);
24
25
  const control = XPathUtil.getClosest('fx-control', this);
25
26
  if (control) {
26
27
  control.refresh();
@@ -28,11 +29,13 @@ class FxRefresh extends AbstractAction {
28
29
  }
29
30
  }
30
31
  if(this.hasAttribute('force')){
32
+ console.log(`### <<<<< refresh() force ${this} >>>>>`);
31
33
  this.getOwnerForm().forceRefresh();
32
34
  return;
33
35
  }
34
36
  if(this.hasAttribute('control')){
35
37
  const targetId = this.getAttribute('control');
38
+ console.log(`### <<<<< refresh() control '${targetId}' >>>>>`);
36
39
  const ctrl = resolveId(targetId, this);
37
40
  if (ctrl && Fore.isUiElement(ctrl.nodeName) && typeof ctrl.refresh === 'function') {
38
41
  ctrl.refresh();
@@ -32,7 +32,7 @@ class FxToggle extends AbstractAction {
32
32
  const fxSwitch = caseElement.parentNode;
33
33
  fxSwitch.toggle(caseElement);
34
34
  }
35
- // this.needsUpdate = true;
35
+ this.needsUpdate = true;
36
36
  }
37
37
  }
38
38
 
package/src/fore.js CHANGED
@@ -18,6 +18,79 @@ export class Fore {
18
18
 
19
19
  static TYPE_DEFAULT = 'xs:string';
20
20
 
21
+ /**
22
+ * Loads and return a piece of HTML
23
+ * @param url - the Url to load from
24
+ * @returns {Promise<string>}
25
+ */
26
+ static async loadHtml(url){
27
+ try{
28
+ const response = await fetch(url, {
29
+ method: 'GET',
30
+ mode: 'cors',
31
+ credentials: 'same-origin',
32
+ headers: {
33
+ 'Content-Type': "text/html",
34
+ },
35
+ });
36
+ const responseContentType = response.headers.get('content-type').toLowerCase();
37
+ if (responseContentType.startsWith('text/html')) {
38
+ 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
+ }
45
+ } catch (e){
46
+ Fore.dispatch(this, 'error', {
47
+ message: `Html couldn't be loaded from '${url}'`,
48
+ level:'Error'
49
+ });
50
+ }
51
+ }
52
+
53
+ /**
54
+ * loads a Fore element from given `src`. Always returns the first occurrence of a `<fx-fore>`. The retured element
55
+ * will replace the `replace` element in the DOM.
56
+ *
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<void>}
61
+ */
62
+ static async loadForeFromSrc(replace, src, selector){
63
+ if(!src){
64
+ Fore.dispatch(this, 'error', {
65
+ detail: {
66
+ message: `No 'src' attribute present`,
67
+ },
68
+ });
69
+ }
70
+ await Fore.loadHtml(src)
71
+ .then(data => {
72
+ const parsed = new DOMParser().parseFromString(data, 'text/html');
73
+ // const theFore = parsed.querySelector('fx-fore');
74
+ const foreElement = parsed.querySelector(selector);
75
+ // console.log('foreElement', foreElement)
76
+ if (!foreElement) {
77
+ Fore.dispatch(this, 'error', {
78
+ detail: {
79
+ message: `Fore element not found in '${src}'. Maybe wrapped within 'template' element?`,
80
+ },
81
+ });
82
+ }
83
+ foreElement.setAttribute('from-src', src);
84
+ const thisAttrs = replace.attributes;
85
+ Array.from(thisAttrs).forEach(attr =>{
86
+ if(attr.name !== 'src'){
87
+ foreElement.setAttribute(attr.name,attr.value);
88
+ }
89
+ });
90
+ replace.replaceWith(foreElement);
91
+ return foreElement;
92
+ });
93
+ }
21
94
  static buildPredicates(node){
22
95
  let attrPredicate='';
23
96
  Array.from(node.attributes).forEach(attr =>{
@@ -467,6 +540,7 @@ export class Fore {
467
540
  static stringifiedComponent(element){
468
541
  return `<${element.localName} ${Array.from(element.attributes).map(attr=>`${attr.name}="${attr.value}"`).join(' ')}>…</${element.localName}>`;
469
542
  }
543
+ /*
470
544
  static async loadForeFromUrl(hostElement, url) {
471
545
  // console.log('########## loading Fore from ', this.src, '##########');
472
546
  await fetch(url, {
@@ -538,7 +612,7 @@ export class Fore {
538
612
  // this.appendChild(imported);
539
613
  }
540
614
  })
541
- /*.catch(error => {
615
+ /!*.catch(error => {
542
616
  hostElement.dispatchEvent(
543
617
  new CustomEvent('error', {
544
618
  composed: false,
@@ -549,8 +623,9 @@ export class Fore {
549
623
  },
550
624
  }),
551
625
  );
552
- });*/
626
+ });*!/
553
627
  }
628
+ */
554
629
 
555
630
  /**
556
631
  * clear all text nodes and attribute values to get a 'clean' template.
package/src/fx-bind.js CHANGED
@@ -63,7 +63,7 @@ export class FxBind extends foreElementMixin(HTMLElement) {
63
63
  init(model) {
64
64
  this.model = model;
65
65
  // console.log('init binding ', this);
66
- this.instanceId = this._getInstanceId();
66
+ this._getInstanceId();
67
67
  this.bindType = this.getModel().getInstance(this.instanceId).type;
68
68
  // console.log('binding type ', this.bindType);
69
69
 
@@ -399,20 +399,40 @@ export class FxBind extends foreElementMixin(HTMLElement) {
399
399
  return result;
400
400
  }
401
401
 
402
- // todo: more elaborated implementation ;)
402
+ /**
403
+ * return the instance id this bind is associated with. Resolves upwards in binds to either find an expr containing
404
+ * and instance() function or if not found return 'default'.
405
+ * @private
406
+ */
403
407
  _getInstanceId() {
404
408
  const bindExpr = this.getBindingExpr();
405
409
  // console.log('_getInstanceId bindExpr ', bindExpr);
406
410
  if (bindExpr.startsWith('instance(')) {
407
411
  this.instanceId = XPathUtil.getInstanceId(bindExpr);
408
- return this.instanceId;
412
+ return;
409
413
  }
410
- if (this.instanceId) {
411
- return this.instanceId;
414
+ if(!this.instanceId && this.parentNode.nodeName === 'FX-BIND'){
415
+ let parent = this.parentNode;
416
+ while(parent && !this.instanceId){
417
+ const ref = parent.getBindingExpr();
418
+ if (ref.startsWith('instance(')) {
419
+ this.instanceId = XPathUtil.getInstanceId(ref);
420
+ return;
421
+ }
422
+ if(parent.parentNode.nodeName !== 'FX-BIND'){
423
+ this.instanceId = 'default';
424
+ break;
425
+ }
426
+ parent = parent.parentNode;
427
+ }
412
428
  }
413
- return 'default';
429
+ this.instanceId = 'default';
414
430
  }
431
+
432
+
433
+
415
434
  }
435
+
416
436
  if (!customElements.get('fx-bind')) {
417
437
  customElements.define('fx-bind', FxBind);
418
438
  }
package/src/fx-fore.js CHANGED
@@ -56,6 +56,9 @@ export class FxFore extends HTMLElement {
56
56
  ready: {
57
57
  type: Boolean,
58
58
  },
59
+ strict:{
60
+ type: Boolean
61
+ },
59
62
  /**
60
63
  *
61
64
  */
@@ -204,7 +207,7 @@ export class FxFore extends HTMLElement {
204
207
 
205
208
  this.toRefresh = [];
206
209
  this.initialRun = true;
207
- this.someInstanceDataStructureChanged = false;
210
+ this._scanForNewTemplateExpressionsNextRefresh = false;
208
211
  this.repeatsFromAttributesCreated = false;
209
212
  this.validateOn = this.hasAttribute('validate-on') ? this.getAttribute('validate-on'):'update';
210
213
  // this.mergePartial = this.hasAttribute('merge-partial')? true:false;
@@ -214,6 +217,7 @@ export class FxFore extends HTMLElement {
214
217
  connectedCallback() {
215
218
  this.style.visibility = 'hidden';
216
219
  console.time('init');
220
+ this.strict = this.hasAttribute('strict') ? true : false;
217
221
  /*
218
222
  document.addEventListener('ready', (e) =>{
219
223
  if(e.target !== this){
@@ -255,6 +259,7 @@ export class FxFore extends HTMLElement {
255
259
  const slot = this.shadowRoot.querySelector('slot#default');
256
260
  slot.addEventListener('slotchange', async event => {
257
261
  // preliminary addition for auto-conversion of non-prefixed element into prefixed elements. See fore.js
262
+ console.log(`### <<<<< slotchange on '${this.id}' >>>>>`);
258
263
  if(this.inited) return;
259
264
  if(this.hasAttribute('convert')){
260
265
  this.replaceWith(Fore.copyDom(this));
@@ -280,8 +285,8 @@ export class FxFore extends HTMLElement {
280
285
  }
281
286
  if (!modelElement.inited) {
282
287
  console.info(
283
- `%cFore is processing URL ${window.location.href}`,
284
- "background:#64b5f6; color:white; padding:1rem; display:inline-block; white-space: nowrap; border-radius:0.3rem;width:100%;",
288
+ `%cFore is processing fx-fore#${this.id}`,
289
+ "background:#64b5f6; color:white; padding:.5rem; display:inline-block; white-space: nowrap; border-radius:0.3rem;width:100%;",
285
290
  );
286
291
 
287
292
  await modelElement.modelConstruct();
@@ -296,6 +301,9 @@ export class FxFore extends HTMLElement {
296
301
  this.addEventListener('path-mutated', () => {
297
302
  this.someInstanceDataStructureChanged = true;
298
303
  });
304
+ this.addEventListener('refresh', () => {
305
+ this.refresh(true);
306
+ });
299
307
 
300
308
  }
301
309
 
@@ -319,6 +327,15 @@ export class FxFore extends HTMLElement {
319
327
  }
320
328
  }
321
329
 
330
+ /**
331
+ * Raise a flag that there might be new template expressions under some node. This happens with
332
+ * repeats updating (new repeat items can have new template expressions) or switches changing their case (new case = new raw HTML)
333
+ */
334
+ scanForNewTemplateExpressionsNextRefresh() {
335
+ // TODO: also ask for the root of any new HTML: this can prevent some very deep queries.
336
+ this._scanForNewTemplateExpressionsNextRefresh = true;
337
+ }
338
+
322
339
  /**
323
340
  * loads a Fore from an URL given by `src`.
324
341
  *
@@ -327,54 +344,7 @@ export class FxFore extends HTMLElement {
327
344
  */
328
345
  async _loadFromSrc() {
329
346
  // console.log('########## loading Fore from ', this.src, '##########');
330
- await fetch(this.src, {
331
- method: 'GET',
332
- mode: 'cors',
333
- credentials: 'include',
334
- headers: {
335
- 'Content-Type': 'text/html',
336
- },
337
- })
338
- .then(response => {
339
- const responseContentType = response.headers.get('content-type').toLowerCase();
340
- console.log('********** responseContentType *********', responseContentType);
341
- if (responseContentType.startsWith('text/html')) {
342
- // const htmlResponse = response.text();
343
- // return new DOMParser().parseFromString(htmlResponse, 'text/html');
344
- // return response.text();
345
- return response.text().then(result =>
346
- // console.log('xml ********', result);
347
- new DOMParser().parseFromString(result, 'text/html'),
348
- );
349
- }
350
- return 'done';
351
- })
352
- .then(data => {
353
- // const theFore = fxEvaluateXPathToFirstNode('//fx-fore', data.firstElementChild);
354
- const theFore = data.querySelector('fx-fore');
355
-
356
- // console.log('thefore', theFore)
357
- if (!theFore) {
358
- Fore.dispatch(this, 'error', {
359
- detail: {
360
- message: `Fore element not found in '${this.src}'. Maybe wrapped within 'template' element?`,
361
- },
362
- });
363
- }
364
- theFore.setAttribute('from-src', this.src);
365
- const thisAttrs = this.attributes;
366
- Array.from(thisAttrs).forEach(attr =>{
367
- if(attr.name !== 'src'){
368
- theFore.setAttribute(attr.name,attr.value);
369
- }
370
- });
371
- this.replaceWith(theFore);
372
- })
373
- .catch(() => {
374
- Fore.dispatch(this, 'error', {
375
- message: `'${this.src}' not found or does not contain Fore element.`,
376
- });
377
- });
347
+ await Fore.loadForeFromSrc(this,this.src,'fx-fore');
378
348
  }
379
349
 
380
350
  /**
@@ -443,8 +413,11 @@ export class FxFore extends HTMLElement {
443
413
 
444
414
  Fore.refreshChildren(this, true);
445
415
  this._updateTemplateExpressions();
446
- this.someInstanceDataStructureChanged = false; // reset
416
+ this._scanForNewTemplateExpressionsNextRefresh = false; // reset
447
417
  this._processTemplateExpressions();
418
+
419
+ console.log(`### <<<<< refresh-done ${this.id} >>>>>`);
420
+
448
421
  Fore.dispatch(this, 'refresh-done', {});
449
422
 
450
423
  // console.groupEnd();
@@ -493,7 +466,7 @@ export class FxFore extends HTMLElement {
493
466
  return;
494
467
  }
495
468
  this.isRefreshing = true;
496
- console.log('### <<<<< refresh() >>>>>');
469
+ console.log(`### <<<<< refresh() on '${this.id}' >>>>>`);
497
470
 
498
471
  // refresh () {
499
472
  // ### refresh Fore UI elements
@@ -553,9 +526,9 @@ export class FxFore extends HTMLElement {
553
526
  }
554
527
 
555
528
  // ### refresh template expressions
556
- if (this.initialRun || this.someInstanceDataStructureChanged) {
529
+ if (this.initialRun || this._scanForNewTemplateExpressionsNextRefresh) {
557
530
  this._updateTemplateExpressions();
558
- this.someInstanceDataStructureChanged = false; // reset
531
+ this._scanForNewTemplateExpressionsNextRefresh = false; // reset
559
532
  }
560
533
  this._processTemplateExpressions();
561
534
 
@@ -563,14 +536,33 @@ export class FxFore extends HTMLElement {
563
536
  // this.dispatchEvent(new CustomEvent('refresh-done'));
564
537
  // this.initialRun = false;
565
538
  this.style.visibility='visible';
539
+ console.log(`### <<<<< refresh-done ${this.id} >>>>>`);
566
540
  Fore.dispatch(this, 'refresh-done', {});
567
541
 
568
542
  // this.isRefreshing = true;
569
543
  // 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')) {
544
+
545
+ const subFores = Array.from(this.querySelectorAll('fx-fore'));
546
+ /*
547
+ calling the parent to refresh causes errors and inconsistent state. Also it is questionable
548
+ if a child should actually interact with its parent in this way.
549
+
550
+ This only affects the refreshing NOT the data mutation itself which is happening as expected.
551
+
552
+ Current solution is that a child that wants the parent to refresh must do so by adding an additional
553
+ event handler that dispatches an event upwards and having a handler in the parent to refresh itself.
554
+
555
+ So refreshed propagate downwards but not upwards which is at least an option to consider.
556
+
557
+ if(this.parentNode.nodeType !== Node.DOCUMENT_FRAGMENT_NODE){
558
+ // await this.parentNode.closest('fx-fore')?.refresh(false);
559
+ }
560
+ */
561
+ for (const subFore of subFores) {
572
562
  // subFore.refresh(false, changedPaths);
573
- subFore.refresh(false);
563
+ if(subFore.ready){
564
+ await subFore.refresh(false);
565
+ }
574
566
  }
575
567
  this.isRefreshing = false;
576
568
  }
@@ -672,11 +664,11 @@ export class FxFore extends HTMLElement {
672
664
  const naked = match.substring(1, match.length - 1);
673
665
  const inscope = getInScopeContext(node, naked);
674
666
  if (!inscope) {
667
+ console.warn('no inscope context for expr', naked);
675
668
  const errNode =
676
669
  node.nodeType === Node.TEXT_NODE || node.nodeType === Node.ATTRIBUTE_NODE
677
670
  ? node.parentNode
678
671
  : node;
679
- console.warn('no inscope context for ', errNode);
680
672
  return match;
681
673
  }
682
674
  // Templates are special: they use the namespace configuration from the place where they are
@@ -695,11 +687,17 @@ export class FxFore extends HTMLElement {
695
687
  }
696
688
  });
697
689
 
690
+ // Update to the new value. Don't do it though if nothing changed to prevent iframes or
691
+ // images from reloading for example
698
692
  if (node.nodeType === Node.ATTRIBUTE_NODE) {
699
693
  const parent = node.ownerElement;
700
- parent.setAttribute(node.nodeName, replaced);
694
+ if (parent.getAttribute(node.nodeName) !== replaced) {
695
+ parent.setAttribute(node.nodeName, replaced);
696
+ }
701
697
  } else if (node.nodeType === Node.TEXT_NODE) {
702
- node.textContent = replaced;
698
+ if (node.textContent !== replaced) {
699
+ node.textContent = replaced;
700
+ }
703
701
  }
704
702
  }
705
703
 
@@ -770,7 +768,11 @@ export class FxFore extends HTMLElement {
770
768
  const model = this.querySelector('fx-model');
771
769
 
772
770
  // ##### lazy creation should NOT take place if there's a parent Fore using shared instances
773
- const parentFore = this.parentNode.closest('fx-fore');
771
+ const parentFore = this.parentNode.nodeType !== Node.DOCUMENT_FRAGMENT_NODE ? this.parentNode.closest('fx-fore'): null;
772
+ if(this.parentNode.nodeType === Node.DOCUMENT_FRAGMENT_NODE){
773
+ console.log('fragment',this.parentNode)
774
+ }
775
+
774
776
  if(parentFore){
775
777
  const shared = parentFore.getModel().instances.filter(shared => shared.hasAttribute('shared'));
776
778
  if(shared.length !==0) return;
@@ -888,7 +890,7 @@ export class FxFore extends HTMLElement {
888
890
  */
889
891
  async _initUI() {
890
892
  // console.log('### _initUI()');
891
- console.log('### <<<<< _initUI >>>>>');
893
+ console.log(`### <<<<< _initUI '${this.id}' >>>>>`);
892
894
 
893
895
  if (!this.initialRun) return;
894
896
  this.classList.add('initialRun');
@@ -986,8 +988,9 @@ export class FxFore extends HTMLElement {
986
988
  if(e.detail.expr){
987
989
  console.error('Failing expression',e.detail.expr);
988
990
  }
989
- console.error('---');
990
- this._displayError(e);
991
+ if(this.strict){
992
+ this._displayError(e);
993
+ }
991
994
  }
992
995
 
993
996
  _copyToClipboard(target){
@@ -110,6 +110,7 @@ export class FxInstance extends HTMLElement {
110
110
  async init() {
111
111
  // console.log('fx-instance init');
112
112
  await this._initInstance();
113
+ console.log(`### <<<<< instance ${this.id} loaded >>>>> `);
113
114
  this.dispatchEvent(
114
115
  new CustomEvent('instance-loaded', {
115
116
  composed: true,
package/src/fx-model.js CHANGED
@@ -106,7 +106,7 @@ export class FxModel extends HTMLElement {
106
106
  *
107
107
  */
108
108
  async modelConstruct() {
109
- console.log('### <<<<< dispatching model-construct >>>>>');
109
+ console.log(`### <<<<< dispatching model-construct for '${this.fore.id}' >>>>>`);
110
110
  // this.dispatchEvent(new CustomEvent('model-construct', { detail: this }));
111
111
  Fore.dispatch(this, 'model-construct', {model: this});
112
112
 
@@ -129,7 +129,7 @@ export class FxModel extends HTMLElement {
129
129
  this.updateModel();
130
130
  } else {
131
131
  // ### if there's no instance one will created
132
- console.log('### <<<<< dispatching model-construct-done >>>>>');
132
+ console.log(`### <<<<< dispatching model-construct-done for '${this.fore.id}' >>>>>`);
133
133
  await this.dispatchEvent(
134
134
  new CustomEvent('model-construct-done', {
135
135
  composed: false,
@@ -167,7 +167,7 @@ export class FxModel extends HTMLElement {
167
167
  }
168
168
 
169
169
  rebuild() {
170
- console.log('### <<<<< rebuild() >>>>>');
170
+ console.log(`### <<<<< rebuild() '${this.fore.id}' >>>>>`);
171
171
 
172
172
  this.mainGraph = new DepGraph(false); // do: should be moved down below binds.length check but causes errors in tests.
173
173
  this.modelItems = [];
@@ -203,7 +203,7 @@ export class FxModel extends HTMLElement {
203
203
  return;
204
204
  }
205
205
 
206
- console.log('### <<<<< recalculate() >>>>>');
206
+ console.log(`### <<<<< recalculate() '${this.fore.id}' >>>>>`);
207
207
 
208
208
  // console.log('changed nodes ', this.changed);
209
209
  this.computes = 0;
@@ -368,7 +368,7 @@ export class FxModel extends HTMLElement {
368
368
 
369
369
  if (this.modelItems.length === 0) return true;
370
370
 
371
- console.log('### <<<<< revalidate() >>>>>');
371
+ console.log(`### <<<<< revalidate() '${this.fore.id}' >>>>>`);
372
372
 
373
373
  // reset submission validation
374
374
  // this.parentNode.classList.remove('submit-validation-failed')
@@ -402,7 +402,7 @@ export class FxModel extends HTMLElement {
402
402
  modelItem.required = compute;
403
403
  this.formElement.addToRefresh(modelItem); // let fore know that modelItem needs refresh
404
404
  if (!modelItem.node.textContent) {
405
- console.log('validation failed on modelitem ', modelItem);
405
+ console.log('node is required but has no value ', XPathUtil.getDocPath(modelItem.node));
406
406
  valid = false;
407
407
  }
408
408
  // if (!compute) valid = false;
@@ -459,18 +459,40 @@ export class FxModel extends HTMLElement {
459
459
  // console.log('instances ', this.instances);
460
460
  // console.log('instances array ',Array.from(this.instances));
461
461
 
462
- const instArray = Array.from(this.instances);
463
- let found = instArray.find(inst => inst.id === id);
462
+ let found;
463
+ if(id === 'default'){
464
+ found = this.getDefaultInstance();
465
+ }
466
+ // ### lookup in local instances first
464
467
  if(!found) {
465
- const parentFore = this.fore.parentNode.closest('fx-fore');
468
+ const instArray = Array.from(this.instances);
469
+ found = instArray.find(inst => inst.id === id);
470
+ const parentFore = this.fore.parentNode.nodeType === Node.DOCUMENT_FRAGMENT_NODE
471
+ ? this.fore.parentNode.host.closest('fx-fore')
472
+ : this.fore.parentNode.closest('fx-fore');
473
+
474
+ }
475
+ // ### lookup in parent Fore if present
476
+ if(!found){
477
+ // const parentFore = this.fore.parentNode.closest('fx-fore');
478
+ const parentFore = this.fore.parentNode.nodeType === Node.DOCUMENT_FRAGMENT_NODE
479
+ ? this.fore.parentNode.host.closest('fx-fore')
480
+ : this.fore.parentNode.closest('fx-fore');
466
481
  if (parentFore) {
467
482
  console.log('shared instances from parent', this.parentNode.id);
468
483
  const parentInstances = parentFore.getModel().instances;
469
484
  const shared = parentInstances.filter(shared => shared.hasAttribute('shared'));
470
485
  found = shared.find(found => found.id === id);
471
486
  }
487
+
472
488
  }
473
- if(!found){
489
+ if(found){
490
+ return found;
491
+ }
492
+ if (id === 'default') {
493
+ return this.getDefaultInstance(); // if id is not found always defaults to first in doc order
494
+ }
495
+ if(!found && this.fore.strict){
474
496
  // return this.getDefaultInstance(); // if id is not found always defaults to first in doc order
475
497
  Fore.dispatch(this, 'error', {
476
498
  origin: this,
@@ -478,7 +500,7 @@ export class FxModel extends HTMLElement {
478
500
  level:'Error'
479
501
  });
480
502
  }
481
- return found;
503
+ return null;
482
504
  }
483
505
 
484
506
  evalBinding(bindingExpr) {
@@ -85,7 +85,7 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
85
85
 
86
86
  model.recalculate();
87
87
 
88
- if (this.validate === 'true') {
88
+ if (this.validate === 'true' && this.method !== 'get') {
89
89
  const valid = model.revalidate();
90
90
  if (!valid) {
91
91
  console.log('validation failed. Submission stopped');
@@ -61,10 +61,6 @@ export default function getInScopeContext(node, ref) {
61
61
  const parentElement = _getElement(node);
62
62
  // console.log('getInScopeContext parent', parentElement);
63
63
 
64
- if(parentElement.closest('fx-fore').mergePartial){
65
- console.log('mergePartial mode')
66
- }
67
-
68
64
  if(parentElement.nodeName === 'FX-FORE'){
69
65
  return parentElement.getModel().getDefaultInstance().getDefaultContext();
70
66
  }