@jinntec/fore 1.10.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-model.js CHANGED
@@ -104,7 +104,7 @@ export class FxModel extends HTMLElement {
104
104
  *
105
105
  */
106
106
  async modelConstruct() {
107
- // console.log('### <<<<< dispatching model-construct >>>>>');
107
+ console.log('### <<<<< dispatching model-construct >>>>>');
108
108
  // this.dispatchEvent(new CustomEvent('model-construct', { detail: this }));
109
109
  Fore.dispatch(this, 'model-construct', {model: this});
110
110
 
@@ -122,12 +122,12 @@ export class FxModel extends HTMLElement {
122
122
  this.instances = Array.from(instances);
123
123
  // console.log('_modelConstruct this.instances ', this.instances);
124
124
  // Await until the model-construct-done event is handled off
125
-
126
125
  await Fore.dispatch(this, 'model-construct-done', {model: this});
127
126
  this.inited = true;
128
127
  this.updateModel();
129
128
  } else {
130
129
  // ### if there's no instance one will created
130
+ console.log('### <<<<< dispatching model-construct-done >>>>>');
131
131
  await this.dispatchEvent(
132
132
  new CustomEvent('model-construct-done', {
133
133
  composed: false,
@@ -165,6 +165,8 @@ export class FxModel extends HTMLElement {
165
165
  }
166
166
 
167
167
  rebuild() {
168
+ console.log('### <<<<< rebuild() >>>>>');
169
+
168
170
  this.mainGraph = new DepGraph(false); // do: should be moved down below binds.length check but causes errors in tests.
169
171
  this.modelItems = [];
170
172
 
@@ -194,10 +196,13 @@ export class FxModel extends HTMLElement {
194
196
  * todo: use 'changed' flag on modelItems to determine subgraph for recalculation. Flag already exists but is not used.
195
197
  */
196
198
  recalculate() {
199
+
197
200
  if (!this.mainGraph) {
198
201
  return;
199
202
  }
200
203
 
204
+ console.log('### <<<<< recalculate() >>>>>');
205
+
201
206
  // console.log('changed nodes ', this.changed);
202
207
  this.computes = 0;
203
208
 
@@ -361,14 +366,13 @@ export class FxModel extends HTMLElement {
361
366
 
362
367
  if (this.modelItems.length === 0) return true;
363
368
 
364
- console.time('revalidate');
369
+ console.log('### <<<<< revalidate() >>>>>');
365
370
 
366
371
  // reset submission validation
367
372
  // this.parentNode.classList.remove('submit-validation-failed')
368
373
  let valid = true;
369
374
  this.modelItems.forEach(modelItem => {
370
375
  // console.log('validating node ', modelItem.node);
371
-
372
376
  const {bind} = modelItem;
373
377
  if (bind) {
374
378
  /*
@@ -382,7 +386,10 @@ export class FxModel extends HTMLElement {
382
386
  // console.log('modelItem validity computed: ', compute);
383
387
  modelItem.constraint = compute;
384
388
  this.formElement.addToRefresh(modelItem); // let fore know that modelItem needs refresh
385
- if (!compute) valid = false;
389
+ if (!compute) {
390
+ console.log('validation failed on modelitem ', modelItem);
391
+ valid = false;
392
+ }
386
393
  }
387
394
  }
388
395
  if (typeof bind.hasAttribute === 'function' && bind.hasAttribute('required')) {
@@ -393,6 +400,7 @@ export class FxModel extends HTMLElement {
393
400
  modelItem.required = compute;
394
401
  this.formElement.addToRefresh(modelItem); // let fore know that modelItem needs refresh
395
402
  if (!modelItem.node.textContent) {
403
+ console.log('validation failed on modelitem ', modelItem);
396
404
  valid = false;
397
405
  }
398
406
  // if (!compute) valid = false;
@@ -452,7 +460,12 @@ export class FxModel extends HTMLElement {
452
460
  const instArray = Array.from(this.instances);
453
461
  const found = instArray.find(inst => inst.id === id);
454
462
  if(!found){
455
- 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
+ });
456
469
  }
457
470
  return found;
458
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});
@@ -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, 'unknown');
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}`;
@@ -14,7 +14,7 @@ import { evaluateXPathToFirstNode} from '../xpath-evaluation.js';
14
14
  export default class AbstractControl extends foreElementMixin(HTMLElement) {
15
15
  constructor() {
16
16
  super();
17
- this.value = '';
17
+ this.value = null;
18
18
  this.display = this.style.display;
19
19
  this.required = false;
20
20
  this.readonly = false;
@@ -24,27 +24,6 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
24
24
  // this.attachShadow({ mode: 'open' });
25
25
  }
26
26
 
27
-
28
- // todo: discuss - this is a hack to circumvent that modelItems in toRefresh diverge from the modelItems in
29
- // the model in some situations. This code first looks for refresh
30
- /*
31
- getModelItem() {
32
- console.log('toRefreshModelItems', this.getOwnerForm().toRefresh);
33
- const s = this.modelItem.path;
34
- console.log('toRefreshModelItems path', s);
35
-
36
- const toRefresh = this.getOwnerForm().toRefresh;
37
- let mi;
38
- if(toRefresh){
39
- mi = this.getOwnerForm().toRefresh.find(m => m.path === s);
40
- }
41
-
42
- return mi? mi: super.getModelItem();
43
- // console.log('toRefreshModelItems realitem', mi);
44
-
45
- }
46
- */
47
-
48
27
  // eslint-disable-next-line class-methods-use-this
49
28
  getWidget() {
50
29
  throw new Error('You have to implement the method getWidget!');
@@ -65,6 +44,8 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
65
44
  // await this.updateComplete;
66
45
  // await this.getWidget();
67
46
  this.oldVal = this.nodeset ? this.nodeset : null;
47
+ // console.log('oldVal',this.oldVal);
48
+
68
49
  this.evalInContext();
69
50
 
70
51
  // todo this if should be removed - see above
@@ -105,7 +86,8 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
105
86
  }else{
106
87
  // ### this actually makes the control nonrelevant
107
88
  // todo: we should call a template function here to allow detachment of event-listeners and resetting eventual state
108
- this.style.display = 'none';
89
+ // this.style.display = 'none';
90
+ this.setAttribute('nonrelevant','');
109
91
  }
110
92
  return;
111
93
  }
@@ -124,6 +106,7 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
124
106
  } else {
125
107
  this.value = this.modelItem.value;
126
108
  }
109
+ // console.log('newVal',this.value);
127
110
 
128
111
  // console.log('value of widget',this.value);
129
112
 
@@ -160,9 +143,8 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
160
143
  Fore.dispatch(this,'init',{});
161
144
  }
162
145
  if (!this.getOwnerForm().ready) return; // state change event do not fire during init phase (initial refresh)
163
- if (currentVal !== this.value ) {
164
- // todo: discuss how to prevent unnecessary/unwanted value-changes e.g. when repeatitems are inserted
165
- // if (currentVal !== this.value && this.visited) {
146
+ // if oldVal is null we haven't received a concrete value yet
147
+ if (this.oldVal !== null && currentVal !== this.value) {
166
148
  Fore.dispatch(this, 'value-changed', { path: this.modelItem.path , value:this.modelItem.value});
167
149
  }
168
150
  }
@@ -337,20 +319,25 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
337
319
  handleRelevant() {
338
320
  // console.log('mip valid', this.modelItem.enabled);
339
321
  const item = this.modelItem.node;
322
+ this.removeAttribute('relevant');
323
+ this.removeAttribute('nonrelevant');
340
324
  if (Array.isArray(item) && item.length === 0) {
341
325
  this._dispatchEvent('nonrelevant');
342
- this.style.display = 'none';
326
+ this.setAttribute('nonrelevant','');
327
+ // this.style.display = 'none';
343
328
  return;
344
329
  }
345
330
  if (this.isEnabled() !== this.modelItem.relevant) {
346
331
  if (this.modelItem.relevant) {
347
332
  this._dispatchEvent('relevant');
348
333
  // this._fadeIn(this, this.display);
349
- this.style.display = this.display;
334
+ this.setAttribute('relevant','');
335
+ // this.style.display = this.display;
350
336
  } else {
351
337
  this._dispatchEvent('nonrelevant');
352
338
  // this._fadeOut(this);
353
- this.style.display = 'none';
339
+ this.setAttribute('nonrelevant','');
340
+ // this.style.display = 'none';
354
341
  }
355
342
  }
356
343
  }
@@ -360,11 +347,7 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
360
347
  }
361
348
 
362
349
  isValid() {
363
- // return this.valid;
364
- if (this.hasAttribute('invalid')) {
365
- return false;
366
- }
367
- return true;
350
+ return this.hasAttribute('invalid') ? false : true;
368
351
  }
369
352
 
370
353
  isReadonly() {
@@ -373,11 +356,7 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
373
356
  }
374
357
 
375
358
  isEnabled() {
376
- // if(this.style.display === 'none' || this.classList.contains('non-relevant')){
377
- if (this.style.display === 'none') {
378
- return false;
379
- }
380
- return true;
359
+ return !this.hasAttribute('nonrelevant');
381
360
  }
382
361
 
383
362
  // eslint-disable-next-line class-methods-use-this
@@ -123,12 +123,8 @@ export default class FxControl extends XfAbstractControl {
123
123
 
124
124
  this.addEventListener('keyup', () => {
125
125
  FxModel.dataChanged = true;
126
- /*
127
- if (!this.classList.contains('visited')) {
128
- this.classList.add('visited');
129
- }
130
- */
131
126
  });
127
+
132
128
  // ### convenience marker event
133
129
  if (this.updateEvent === 'enter') {
134
130
  this.widget.addEventListener('keyup', event => {
@@ -494,9 +490,13 @@ export default class FxControl extends XfAbstractControl {
494
490
  this.dispatchEvent(new CustomEvent('loaded', {detail: {fore: theFore}}));
495
491
  } catch (error) {
496
492
  // console.log('error', error);
497
- this.getOwnerForm().dispatchEvent(
498
- new CustomEvent('error', {detail: {message: `${this.url} not found`}}),
499
- );
493
+ Fore.dispatch(this, 'error', {
494
+ origin: this,
495
+ message: `control couldn't be loaded from url '${this.url}'`,
496
+ expr:xpath,
497
+ level:'Error'
498
+ });
499
+
500
500
  }
501
501
  }
502
502
 
@@ -550,9 +550,6 @@ export default class FxControl extends XfAbstractControl {
550
550
  // const nodeset = evaluateXPathToNodes(ref, inscope, this);
551
551
  const nodeset = evaluateXPath(ref, inscope, this);
552
552
 
553
- // ### bail out when nodeset is array and empty
554
- if (Array.isArray(nodeset) && nodeset.length === 0) return;
555
-
556
553
  // ### clear items
557
554
  const {children} = widget;
558
555
  Array.from(children).forEach(child => {
@@ -561,6 +558,9 @@ export default class FxControl extends XfAbstractControl {
561
558
  }
562
559
  });
563
560
 
561
+ // ### bail out when nodeset is array and empty
562
+ if (Array.isArray(nodeset) && nodeset.length === 0) return;
563
+
564
564
  // ### build the items
565
565
  const {template} = this;
566
566
  if (template) {
@@ -574,7 +574,7 @@ export default class FxControl extends XfAbstractControl {
574
574
  this.widget.insertBefore(option, firstTemplateChild);
575
575
  }
576
576
 
577
- if (nodeset.length) {
577
+ if (nodeset.length !== 0) {
578
578
  // console.log('nodeset', nodeset);
579
579
  const fragment = document.createDocumentFragment();
580
580
  // console.time('offscreen');
@@ -135,9 +135,9 @@ export class FxRepeatAttributes extends foreElementMixin(HTMLElement) {
135
135
  if (mutations[0].type === 'childList') {
136
136
  const added = mutations[0].addedNodes[0];
137
137
  if (added) {
138
- const instance = XPathUtil.resolveInstance(this);
139
138
 
140
- const path = XPathUtil.getPath(added, instance);
139
+ const instance = XPathUtil.resolveInstance(this,this.ref);
140
+ const path = XPathUtil.getPath(added, instance);
141
141
  // this.dispatch('path-mutated',{'path':path,'nodeset':this.nodeset,'index': this.index});
142
142
  // this.index = index;
143
143
  // const prev = mutations[0].previousSibling.previousElementSibling;
@@ -207,13 +207,17 @@ export class FxRepeat extends foreElementMixin(HTMLElement) {
207
207
  });
208
208
  }
209
209
 
210
+ /*
210
211
  this.touchedPaths = new Set();
211
212
  const instance = XPathUtil.resolveInstance(this, this.ref);
212
213
  const depTrackDomfacade = new DependencyNotifyingDomFacade((node) => {
213
214
  this.touchedPaths.add(XPathUtil.getPath(node, instance));
214
215
  });
215
216
  const rawNodeset = evaluateXPath(this.ref, inscope, this, {}, {}, depTrackDomfacade );
216
- console.log('Touched!', this.ref, [...this.touchedPaths].join(', '));
217
+ */
218
+ const rawNodeset = evaluateXPath(this.ref, inscope, this);
219
+
220
+ // console.log('Touched!', this.ref, [...this.touchedPaths].join(', '));
217
221
  if (rawNodeset.length === 1 && Array.isArray(rawNodeset[0])) {
218
222
  // This XPath likely returned an XPath array. Just collapse to that array
219
223
  this.nodeset = rawNodeset[0];
@@ -229,6 +233,13 @@ export class FxRepeat extends foreElementMixin(HTMLElement) {
229
233
  // console.time('repeat-refresh', this);
230
234
  this._evalNodeset();
231
235
 
236
+ // ### register ourselves as boundControl
237
+ /*
238
+ const modelItem = this.getModelItem();
239
+ if (!modelItem.boundControls.includes(this)) {
240
+ modelItem.boundControls.push(this);
241
+ }
242
+ */
232
243
  // console.log('repeat refresh nodeset ', this.nodeset);
233
244
  // console.log('repeatCount', this.repeatCount);
234
245
 
@@ -91,15 +91,9 @@ export class FxRepeatitem extends foreElementMixin(HTMLElement) {
91
91
  }
92
92
 
93
93
  if (this.modelItem && !this.modelItem.relevant) {
94
- // await Fore.fadeOutElement(this)
95
- // this.style.display = 'none';
96
- this.style.display = 'none';
94
+ this.setAttribute('nonrelevant','');
97
95
  } else {
98
- // if(this.hasAttribute('repeat-index')){
99
- // Fore.fadeInElement(this);
100
- // }
101
- // this.style.display = this.display;
102
- this.style.display = 'block';
96
+ this.setAttribute('relevant','');
103
97
  }
104
98
 
105
99
  /*
@@ -116,19 +116,9 @@ export class FxTrigger extends XfAbstractControl {
116
116
  }
117
117
  }
118
118
 
119
- /*
120
- async refresh() {
121
- super.refresh();
122
- // console.log('fx-button refresh');
123
-
124
- const elements = this.querySelectorAll(':scope > *');
125
- elements.forEach(element => {
126
- if (typeof element.refresh === 'function') {
127
- element.refresh();
128
- }
129
- });
130
- }
131
- */
119
+ async refresh() {
120
+ super.refresh();
121
+ }
132
122
  }
133
123
 
134
124
  if (!customElements.get('fx-trigger')) {