@jinntec/fore 1.2.0 → 1.3.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 (42) hide show
  1. package/dist/fore-dev.js +8 -8
  2. package/dist/fore-dev.js.map +1 -1
  3. package/dist/fore.js +7 -7
  4. package/dist/fore.js.map +1 -1
  5. package/index.js +1 -0
  6. package/package.json +2 -2
  7. package/resources/fore.css +95 -72
  8. package/src/actions/abstract-action.js +48 -6
  9. package/src/actions/fx-action.js +1 -2
  10. package/src/actions/fx-confirm.js +2 -2
  11. package/src/actions/fx-delete.js +53 -62
  12. package/src/actions/fx-hide.js +3 -1
  13. package/src/actions/fx-message.js +25 -1
  14. package/src/actions/fx-reload.js +30 -0
  15. package/src/actions/fx-send.js +11 -1
  16. package/src/actions/fx-setfocus.js +32 -5
  17. package/src/actions/fx-setvalue.js +4 -4
  18. package/src/actions/fx-show.js +1 -0
  19. package/src/actions/fx-toggle.js +5 -0
  20. package/src/events.js +24 -0
  21. package/src/fore.js +71 -8
  22. package/src/fx-bind.js +1 -1
  23. package/src/fx-fore.js +65 -14
  24. package/src/fx-instance.js +33 -8
  25. package/src/fx-model.js +435 -441
  26. package/src/fx-submission.js +76 -62
  27. package/src/getInScopeContext.js +91 -83
  28. package/src/modelitem.js +2 -2
  29. package/src/relevance.js +1 -1
  30. package/src/ui/abstract-control.js +108 -22
  31. package/src/ui/fx-alert.js +0 -1
  32. package/src/ui/fx-container.js +22 -26
  33. package/src/ui/fx-control.js +84 -34
  34. package/src/ui/fx-group.js +5 -0
  35. package/src/ui/fx-inspector.js +5 -0
  36. package/src/ui/fx-output.js +14 -14
  37. package/src/ui/fx-repeat.js +2 -2
  38. package/src/ui/fx-repeatitem.js +5 -3
  39. package/src/ui/fx-switch.js +11 -7
  40. package/src/ui/fx-trigger.js +15 -9
  41. package/src/xpath-evaluation.js +28 -17
  42. package/src/xpath-util.js +13 -0
package/src/fx-model.js CHANGED
@@ -1,482 +1,476 @@
1
- import { DepGraph } from './dep_graph.js';
2
- import { Fore } from './fore.js';
1
+ import {DepGraph} from './dep_graph.js';
2
+ import {Fore} from './fore.js';
3
3
  import './fx-instance.js';
4
- import { ModelItem } from './modelitem.js';
5
- import { evaluateXPath, evaluateXPathToBoolean } from './xpath-evaluation.js';
6
- import { XPathUtil } from './xpath-util.js';
4
+ import {ModelItem} from './modelitem.js';
5
+ import {evaluateXPath, evaluateXPathToBoolean} from './xpath-evaluation.js';
6
+ import {XPathUtil} from './xpath-util.js';
7
7
 
8
8
  /**
9
9
  * @ts-check
10
10
  */
11
11
  export class FxModel extends HTMLElement {
12
- constructor() {
13
- super();
14
- // this.id = '';
15
-
16
- this.instances = [];
17
- this.modelItems = [];
18
- this.defaultContext = {};
19
- this.changed = [];
20
-
21
- // this.mainGraph = new DepGraph(false);
22
- this.inited = false;
23
- this.modelConstructed = false;
24
- this.attachShadow({ mode: 'open' });
25
- this.computes = 0;
26
- }
27
-
28
- get formElement() {
29
- return this.parentElement;
30
- }
31
-
32
- connectedCallback() {
33
- // console.log('connectedCallback ', this);
34
- this.shadowRoot.innerHTML = `
12
+ static dataChanged = false;
13
+ constructor() {
14
+ super();
15
+ // this.id = '';
16
+
17
+ this.instances = [];
18
+ this.modelItems = [];
19
+ this.defaultContext = {};
20
+ this.changed = [];
21
+
22
+ // this.mainGraph = new DepGraph(false);
23
+ this.inited = false;
24
+ this.modelConstructed = false;
25
+ this.attachShadow({mode: 'open'});
26
+ this.computes = 0;
27
+ }
28
+
29
+ get formElement() {
30
+ return this.parentElement;
31
+ }
32
+
33
+ connectedCallback() {
34
+ // console.log('connectedCallback ', this);
35
+ this.shadowRoot.innerHTML = `
35
36
  <slot></slot>
36
37
  `;
37
38
 
38
- this.addEventListener('model-construct-done', () => {
39
- this.modelConstructed = true;
40
- // console.log('model-construct-done fired ', this.modelConstructed);
41
- // console.log('model-construct-done fired ', e.detail.model.instances);
42
- },{ once: true });
43
-
44
- this.skipUpdate = false;
45
- }
46
-
47
- static lazyCreateModelItem(model, ref, node) {
48
- // console.log('lazyCreateModelItem ', node);
49
-
50
- let targetNode = {};
51
- if (node === null || node === undefined) return null;
52
- if (node.nodeType === node.TEXT_NODE) {
53
- // const parent = node.parentNode;
54
- // console.log('PARENT ', parent);
55
- targetNode = node.parentNode;
56
- } else {
57
- targetNode = node;
39
+ this.addEventListener('model-construct-done', () => {
40
+ this.modelConstructed = true;
41
+ // console.log('model-construct-done fired ', this.modelConstructed);
42
+ // console.log('model-construct-done fired ', e.detail.model.instances);
43
+ }, {once: true});
44
+
45
+ this.skipUpdate = false;
58
46
  }
59
47
 
60
- // const path = fx.evaluateXPath('path()',node);
61
- let path;
62
- if (node.nodeType) {
63
- path = XPathUtil.getPath(node);
64
- } else {
65
- path = null;
66
- targetNode = node;
48
+ static lazyCreateModelItem(model, ref, node) {
49
+ // console.log('lazyCreateModelItem ', node);
50
+
51
+ let targetNode = {};
52
+ if (node === null || node === undefined) return null;
53
+ if (node.nodeType === node.TEXT_NODE) {
54
+ // const parent = node.parentNode;
55
+ // console.log('PARENT ', parent);
56
+ targetNode = node.parentNode;
57
+ } else {
58
+ targetNode = node;
59
+ }
60
+
61
+ // const path = fx.evaluateXPath('path()',node);
62
+ let path;
63
+ if (node.nodeType) {
64
+ path = XPathUtil.getPath(node);
65
+ } else {
66
+ path = null;
67
+ targetNode = node;
68
+ }
69
+ // const path = XPathUtil.getPath(node);
70
+
71
+ // ### intializing ModelItem with default values (as there is no <fx-bind> matching for given ref)
72
+ const mi = new ModelItem(
73
+ path,
74
+ ref,
75
+ Fore.READONLY_DEFAULT,
76
+ Fore.RELEVANT_DEFAULT,
77
+ Fore.REQUIRED_DEFAULT,
78
+ Fore.CONSTRAINT_DEFAULT,
79
+ Fore.TYPE_DEFAULT,
80
+ targetNode,
81
+ this,
82
+ );
83
+
84
+ // console.log('new ModelItem is instanceof ModelItem ', mi instanceof ModelItem);
85
+ model.registerModelItem(mi);
86
+ return mi;
67
87
  }
68
- // const path = XPathUtil.getPath(node);
69
-
70
- // ### intializing ModelItem with default values (as there is no <fx-bind> matching for given ref)
71
- const mi = new ModelItem(
72
- path,
73
- ref,
74
- Fore.READONLY_DEFAULT,
75
- Fore.RELEVANT_DEFAULT,
76
- Fore.REQUIRED_DEFAULT,
77
- Fore.CONSTRAINT_DEFAULT,
78
- Fore.TYPE_DEFAULT,
79
- targetNode,
80
- this,
81
- );
82
-
83
- // console.log('new ModelItem is instanceof ModelItem ', mi instanceof ModelItem);
84
- model.registerModelItem(mi);
85
- return mi;
86
- }
87
-
88
- /**
89
- * modelConstruct starts actual processing of the model by
90
- *
91
- * 1. loading instances if present or constructing one
92
- * 2. calling updateModel to run the model update cycle of rebuild, recalculate and revalidate
93
- *
94
- * @event model-construct-done is fired once all instances have be loaded or after generating instance
95
- *
96
- */
97
- modelConstruct() {
98
- // console.log('### <<<<< dispatching model-construct >>>>>');
99
- // this.dispatchEvent(new CustomEvent('model-construct', { detail: this }));
100
- this.dispatchEvent(
101
- new CustomEvent('model-construct', {
102
- composed: false,
103
- bubbles: true,
104
- detail: { model: this },
105
- }),
106
- );
107
-
108
- console.time('instance-loading');
109
- const instances = this.querySelectorAll('fx-instance');
110
- if (instances.length > 0) {
111
- console.group('init instances');
112
- const promises = [];
113
- instances.forEach(instance => {
114
- promises.push(instance.init());
115
- });
116
-
117
- Promise.all(promises).then(() => {
118
- this.instances = Array.from(instances);
119
- // console.log('_modelConstruct this.instances ', this.instances);
120
- this.updateModel();
88
+
89
+ /**
90
+ * modelConstruct starts actual processing of the model by
91
+ *
92
+ * 1. loading instances if present or constructing one
93
+ * 2. calling updateModel to run the model update cycle of rebuild, recalculate and revalidate
94
+ *
95
+ * @event model-construct-done is fired once all instances have be loaded or after generating instance
96
+ *
97
+ */
98
+ modelConstruct() {
99
+ // console.log('### <<<<< dispatching model-construct >>>>>');
100
+ // this.dispatchEvent(new CustomEvent('model-construct', { detail: this }));
101
+ Fore.dispatch(this, 'model-construct', {model: this});
102
+
103
+ // console.time('instance-loading');
104
+ const instances = this.querySelectorAll('fx-instance');
105
+ if (instances.length > 0) {
106
+ console.group('init instances');
107
+ const promises = [];
108
+ instances.forEach(instance => {
109
+ promises.push(instance.init());
110
+ });
111
+
112
+ Promise.all(promises).then(() => {
113
+ this.instances = Array.from(instances);
114
+ // console.log('_modelConstruct this.instances ', this.instances);
115
+ this.updateModel();
116
+ this.inited = true;
117
+ Fore.dispatch(this, 'model-construct-done', {model: this});
118
+ });
119
+ console.groupEnd();
120
+ } else {
121
+ // ### if there's no instance one will created
122
+ this.dispatchEvent(
123
+ new CustomEvent('model-construct-done', {
124
+ composed: false,
125
+ bubbles: true,
126
+ detail: {model: this},
127
+ }),
128
+ );
129
+ }
130
+ // console.timeEnd('instance-loading');
121
131
  this.inited = true;
132
+ }
122
133
 
123
- // console.log('### <<<<< dispatching model-construct-done >>>>>');
124
- this.dispatchEvent(
125
- new CustomEvent('model-construct-done', {
126
- composed: false,
127
- bubbles: true,
128
- detail: { model: this },
129
- }),
130
- );
131
- });
132
- console.groupEnd();
133
- } else {
134
- // ### if there's no instance one will created
135
- this.dispatchEvent(
136
- new CustomEvent('model-construct-done', {
137
- composed: false,
138
- bubbles: true,
139
- detail: { model: this },
140
- }),
141
- );
134
+ registerModelItem(modelItem) {
135
+ // console.log('ModelItem registered ', modelItem);
136
+ this.modelItems.push(modelItem);
142
137
  }
143
- console.timeEnd('instance-loading');
144
- this.inited = true;
145
- }
146
-
147
- registerModelItem(modelItem) {
148
- // console.log('ModelItem registered ', modelItem);
149
- this.modelItems.push(modelItem);
150
- }
151
-
152
- /**
153
- * update action triggering the update cycle
154
- */
155
- updateModel() {
156
- // console.time('updateModel');
157
- this.rebuild();
158
- if (this.skipUpdate) return;
159
- this.recalculate();
160
- this.revalidate();
161
- // console.timeEnd('updateModel');
162
- }
163
-
164
- rebuild() {
165
- console.group('### rebuild');
166
- console.time('rebuild');
167
- this.mainGraph = new DepGraph(false); // do: should be moved down below binds.length check but causes errors in tests.
168
- this.modelItems = [];
169
-
170
- // trigger recursive initialization of the fx-bind elements
171
- const binds = this.querySelectorAll('fx-model > fx-bind');
172
- if (binds.length === 0) {
173
- // console.log('skipped model update');
174
- this.skipUpdate = true;
175
- return;
138
+
139
+ /**
140
+ * update action triggering the update cycle
141
+ */
142
+ updateModel() {
143
+ // console.time('updateModel');
144
+ this.rebuild();
145
+ if (this.skipUpdate){
146
+ console.info('%crecalculate/revalidate skipped - no bindings', 'font-style: italic; background: #90a4ae; color:lightgrey; padding:0.3rem 5rem 0.3rem 0.3rem;display:block;width:100%;');
147
+ return;
148
+ }
149
+ this.recalculate();
150
+ this.revalidate();
151
+ // console.timeEnd('updateModel');
176
152
  }
177
153
 
178
- binds.forEach(bind => {
179
- bind.init(this);
180
- });
181
- // console.timeEnd('rebuild');
154
+ rebuild() {
155
+ console.info('%crebuild', 'font-style: italic; background: #90a4ae; color:white; padding:0.3rem 5rem 0.3rem 0.3rem;display:block;width:100%;');
156
+ this.mainGraph = new DepGraph(false); // do: should be moved down below binds.length check but causes errors in tests.
157
+ this.modelItems = [];
158
+
159
+ // trigger recursive initialization of the fx-bind elements
160
+ const binds = this.querySelectorAll('fx-model > fx-bind');
161
+ if (binds.length === 0) {
162
+ // console.log('skipped model update');
163
+ this.skipUpdate = true;
164
+ return;
165
+ }
182
166
 
183
- // console.log(`dependencies of a `, this.mainGraph.dependenciesOf("/Q{}data[1]/Q{}a[1]:required"));
184
- // console.log(`dependencies of b `, this.mainGraph.dependenciesOf("/Q{}data[1]/Q{}b[1]:required"));
185
- console.log(`rebuild mainGraph`, this.mainGraph);
186
- console.log(`rebuild mainGraph calc order`, this.mainGraph.overallOrder());
167
+ binds.forEach(bind => {
168
+ bind.init(this);
169
+ });
187
170
 
188
- this.dispatchEvent(new CustomEvent('rebuild-done', { detail: { maingraph: this.mainGraph } }));
171
+ console.log(`mainGraph`, this.mainGraph);
172
+ // console.log(`rebuild mainGraph calc order`, this.mainGraph.overallOrder());
189
173
 
190
- /*
174
+ // this.dispatchEvent(new CustomEvent('rebuild-done', {detail: {maingraph: this.mainGraph}}));
175
+ Fore.dispatch(this,'rebuild-done',{maingraph:this.mainGraph});
191
176
  console.log(
192
- `rebuild finished with modelItems ${this.modelItems.length} item(s)`,
193
- this.modelItems,
177
+ `rebuild finished with modelItems ${this.modelItems.length} item(s)`,
178
+ this.modelItems,
194
179
  );
195
- */
196
- console.groupEnd();
197
- }
198
-
199
- /**
200
- * recalculation of all modelItems. Uses dependency graph to determine order of computation.
201
- *
202
- * todo: use 'changed' flag on modelItems to determine subgraph for recalculation. Flag already exists but is not used.
203
- */
204
- recalculate() {
205
- if(!this.mainGraph){
206
- return;
207
180
  }
208
181
 
209
- console.group('### recalculate');
210
- console.log('changed nodes ', this.changed);
211
-
212
-
213
- console.time('recalculate');
214
- this.computes = 0;
215
-
216
- this.subgraph = new DepGraph(false);
217
- if (this.changed.length !== 0) {
218
- // ### build the subgraph
219
- this.changed.forEach(modelItem => {
220
- this.subgraph.addNode(modelItem.path, modelItem.node);
221
- // const dependents = this.mainGraph.dependantsOf(modelItem.path, false);
222
- // this._addSubgraphDependencies(modelItem.path);
223
- if (this.mainGraph.hasNode(modelItem.path)) {
224
- // const dependents = this.mainGraph.directDependantsOf(modelItem.path)
225
-
226
- const all = this.mainGraph.dependantsOf(modelItem.path, false);
227
- const dependents = all.reverse();
228
- if (dependents.length !== 0) {
229
- dependents.forEach(dep => {
230
- // const subdep = this.mainGraph.dependentsOf(dep,false);
231
- // subgraph.addDependency(dep, modelItem.path);
232
- const val = this.mainGraph.getNodeData(dep);
233
- this.subgraph.addNode(dep, val);
234
- if (dep.includes(':')) {
235
- const path = dep.substring(0, dep.indexOf(':'));
236
- this.subgraph.addNode(path, val);
237
-
238
- const deps = this.mainGraph.dependentsOf(modelItem.path, false);
239
- // if we find the dep to be first in list of dependents we are dependent on ourselves not adding edge to modelItem.path
240
- if (deps.indexOf(dep) !== 0) {
241
- this.subgraph.addDependency(dep, modelItem.path);
242
- }
243
- }
244
- // subgraph.addDependency(dep,modelItem.path);
245
- });
246
- }
247
- }
248
- });
249
-
250
- // ### compute the subgraph
251
- const ordered = this.subgraph.overallOrder(false);
252
- ordered.forEach(path => {
253
- if (this.mainGraph.hasNode(path)) {
254
- const node = this.mainGraph.getNodeData(path);
255
- this.compute(node, path);
182
+ /**
183
+ * recalculation of all modelItems. Uses dependency graph to determine order of computation.
184
+ *
185
+ * todo: use 'changed' flag on modelItems to determine subgraph for recalculation. Flag already exists but is not used.
186
+ */
187
+ recalculate() {
188
+ console.info('%crecalculate', 'font-style: italic; background: #90a4ae; color:white; padding:0.3rem 5rem 0.3rem 0.3rem;display:block;width:100%;');
189
+
190
+ if (!this.mainGraph) {
191
+ return;
256
192
  }
257
- });
258
- const toRefresh = [...this.changed];
259
- this.formElement.toRefresh = toRefresh;
260
- this.changed = [];
261
- console.log('subgraph', this.subgraph);
262
- this.dispatchEvent(
263
- new CustomEvent('recalculate-done', { detail: { subgraph: this.subgraph } }),
264
- );
265
- } else {
266
- const v = this.mainGraph.overallOrder(false);
267
- v.forEach(path => {
268
- const node = this.mainGraph.getNodeData(path);
269
- this.compute(node, path);
270
- });
271
- }
272
- console.log(`recalculated ${this.computes} modelItems`);
273
-
274
- console.timeEnd('recalculate');
275
- console.log('recalculate finished with modelItems ',this.modelItems);
276
- console.groupEnd();
277
- }
278
-
279
- /*
280
- _addSubgraphDependencies(path){
281
- const dependents = this.mainGraph.directDependantsOf(path)
282
-
283
- const alreadyInGraph = this.subgraph.incomingEdges[path];
284
- // const alreadyInGraph = path in this.subgraph;
285
- if(dependents.length !== 0 && alreadyInGraph.length === 0){
286
-
287
- dependents.forEach(dep => {
288
- // const val= this.mainGraph.getNodeData(dep);
289
- // this.subgraph.addNode(dep,val);
290
- if(dep.includes(':')){
291
- const subpath = dep.substring(0, dep.indexOf(':'));
292
- // this.subgraph.addNode(subpath,val);
293
- this.subgraph.addDependency(subpath,dep);
294
- this.subgraph.addDependency(dep,path);
295
- /!*
296
- const subdeps = this.mainGraph.directDependantsOf(path);
297
- console.log('subdeps',path, subdeps);
298
- subdeps.forEach(sdep => {
299
- const sval= this.mainGraph.getNodeData(sdep);
300
- this.subgraph.addNode(sdep,sval);
301
- console.log('subdep',sdep);
302
- });
303
- *!/
304
- if(this.subgraph.incomingEdges[dep] === 0){
305
- this._addSubgraphDependencies(subpath)
306
- }
307
193
 
194
+ console.group('### recalculate');
195
+ console.log('changed nodes ', this.changed);
196
+
197
+
198
+ console.time('recalculate');
199
+ this.computes = 0;
200
+
201
+ this.subgraph = new DepGraph(false);
202
+ if (this.changed.length !== 0) {
203
+ // ### build the subgraph
204
+ this.changed.forEach(modelItem => {
205
+ this.subgraph.addNode(modelItem.path, modelItem.node);
206
+ // const dependents = this.mainGraph.dependantsOf(modelItem.path, false);
207
+ // this._addSubgraphDependencies(modelItem.path);
208
+ if (this.mainGraph.hasNode(modelItem.path)) {
209
+ // const dependents = this.mainGraph.directDependantsOf(modelItem.path)
210
+
211
+ const all = this.mainGraph.dependantsOf(modelItem.path, false);
212
+ const dependents = all.reverse();
213
+ if (dependents.length !== 0) {
214
+ dependents.forEach(dep => {
215
+ // const subdep = this.mainGraph.dependentsOf(dep,false);
216
+ // subgraph.addDependency(dep, modelItem.path);
217
+ const val = this.mainGraph.getNodeData(dep);
218
+ this.subgraph.addNode(dep, val);
219
+ if (dep.includes(':')) {
220
+ const path = dep.substring(0, dep.indexOf(':'));
221
+ this.subgraph.addNode(path, val);
222
+
223
+ const deps = this.mainGraph.dependentsOf(modelItem.path, false);
224
+ // if we find the dep to be first in list of dependents we are dependent on ourselves not adding edge to modelItem.path
225
+ if (deps.indexOf(dep) !== 0) {
226
+ this.subgraph.addDependency(dep, modelItem.path);
227
+ }
228
+ }
229
+ // subgraph.addDependency(dep,modelItem.path);
230
+ });
231
+ }
308
232
  }
309
233
  });
310
234
 
235
+ // ### compute the subgraph
236
+ const ordered = this.subgraph.overallOrder(false);
237
+ ordered.forEach(path => {
238
+ if (this.mainGraph.hasNode(path)) {
239
+ const node = this.mainGraph.getNodeData(path);
240
+ this.compute(node, path);
241
+ }
242
+ });
243
+ const toRefresh = [...this.changed];
244
+ this.formElement.toRefresh = toRefresh;
245
+ this.changed = [];
246
+ console.log('subgraph', this.subgraph);
247
+ this.dispatchEvent(
248
+ new CustomEvent('recalculate-done', {detail: {subgraph: this.subgraph}}),
249
+ );
250
+ } else {
251
+ const v = this.mainGraph.overallOrder(false);
252
+ v.forEach(path => {
253
+ const node = this.mainGraph.getNodeData(path);
254
+ this.compute(node, path);
255
+ });
311
256
  }
257
+ console.log(`recalculated ${this.computes} modelItems`);
312
258
 
259
+ console.timeEnd('recalculate');
260
+ console.log('recalculate finished with modelItems ', this.modelItems);
261
+ console.groupEnd();
313
262
  }
314
- */
315
-
316
- /**
317
- * (re-) computes a modelItem.
318
- * @param node - the node the modelItem is attached to
319
- * @param path - the canonical XPath of the node
320
- */
321
- compute(node, path) {
322
- const modelItem = this.getModelItem(node);
323
- if (modelItem && path.includes(':')) {
324
- const property = path.split(':')[1];
325
- if (property) {
326
- /*
327
- if (property === 'readonly') {
328
- // make sure that calculated items are always readonly
329
- if(modelItem.bind['calculate']){
330
- modelItem.readonly = true;
331
- }else {
332
- const expr = modelItem.bind[property];
263
+
264
+ /*
265
+ _addSubgraphDependencies(path){
266
+ const dependents = this.mainGraph.directDependantsOf(path)
267
+
268
+ const alreadyInGraph = this.subgraph.incomingEdges[path];
269
+ // const alreadyInGraph = path in this.subgraph;
270
+ if(dependents.length !== 0 && alreadyInGraph.length === 0){
271
+
272
+ dependents.forEach(dep => {
273
+ // const val= this.mainGraph.getNodeData(dep);
274
+ // this.subgraph.addNode(dep,val);
275
+ if(dep.includes(':')){
276
+ const subpath = dep.substring(0, dep.indexOf(':'));
277
+ // this.subgraph.addNode(subpath,val);
278
+ this.subgraph.addDependency(subpath,dep);
279
+ this.subgraph.addDependency(dep,path);
280
+ /!*
281
+ const subdeps = this.mainGraph.directDependantsOf(path);
282
+ console.log('subdeps',path, subdeps);
283
+ subdeps.forEach(sdep => {
284
+ const sval= this.mainGraph.getNodeData(sdep);
285
+ this.subgraph.addNode(sdep,sval);
286
+ console.log('subdep',sdep);
287
+ });
288
+ *!/
289
+ if(this.subgraph.incomingEdges[dep] === 0){
290
+ this._addSubgraphDependencies(subpath)
291
+ }
292
+
293
+ }
294
+ });
295
+
296
+ }
297
+
298
+ }
299
+ */
300
+
301
+ /**
302
+ * (re-) computes a modelItem.
303
+ * @param node - the node the modelItem is attached to
304
+ * @param path - the canonical XPath of the node
305
+ */
306
+ compute(node, path) {
307
+ const modelItem = this.getModelItem(node);
308
+ if (modelItem && path.includes(':')) {
309
+ const property = path.split(':')[1];
310
+ if (property) {
311
+ /*
312
+ if (property === 'readonly') {
313
+ // make sure that calculated items are always readonly
314
+ if(modelItem.bind['calculate']){
315
+ modelItem.readonly = true;
316
+ }else {
317
+ const expr = modelItem.bind[property];
318
+ const compute = evaluateXPathToBoolean(expr, modelItem.node, this);
319
+ modelItem.readonly = compute;
320
+ }
321
+ }
322
+ */
323
+ if (property === 'calculate') {
324
+ const expr = modelItem.bind[property];
325
+ const compute = evaluateXPath(expr, modelItem.node, this);
326
+ modelItem.value = compute;
327
+ modelItem.readonly = true; // calculated nodes are always readonly
328
+ } else if (property !== 'constraint' && property !== 'type') {
329
+ const expr = modelItem.bind[property];
330
+ if (expr) {
333
331
  const compute = evaluateXPathToBoolean(expr, modelItem.node, this);
334
- modelItem.readonly = compute;
332
+ modelItem[property] = compute;
333
+ /*
334
+ console.log(
335
+ `recalculating path ${path} - Expr:'${expr}' computed`,
336
+ modelItem[property],
337
+ );
338
+ */
335
339
  }
336
340
  }
337
- */
338
- if (property === 'calculate') {
339
- const expr = modelItem.bind[property];
340
- const compute = evaluateXPath(expr, modelItem.node, this);
341
- modelItem.value = compute;
342
- modelItem.readonly = true; // calculated nodes are always readonly
343
- } else if (property !== 'constraint' && property !== 'type') {
344
- const expr = modelItem.bind[property];
345
- if (expr) {
346
- const compute = evaluateXPathToBoolean(expr, modelItem.node, this);
347
- modelItem[property] = compute;
348
- console.log(
349
- `recalculating path ${path} - Expr:'${expr}' computed`,
350
- modelItem[property],
351
- );
352
- }
353
- }
354
- }
355
- this.computes += 1;
356
- }
357
- }
358
-
359
- /**
360
- * Iterates all modelItems to calculate the validation status.
361
- *
362
- * Model alerts are given on 'fx-bind' elements as either attribute `alert` or as `fx-alert` child elements.
363
- *
364
- * During model-construct all model alerts are added to the modelItem if any
365
- *
366
- * to revalidate:
367
- * Gets the `constraint` attribute declaration from modelItem.bind
368
- * Computes the XPath to a Boolean
369
- * Updates the modelItem.constraint property
370
- *
371
- * todo: type checking
372
- * todo: run browser validation API
373
- *
374
- */
375
- revalidate() {
376
- if(this.modelItems.length === 0) return true;
377
-
378
- console.group('### revalidate');
379
- console.time('revalidate');
380
- let valid = true;
381
- this.modelItems.forEach(modelItem => {
382
- // console.log('validating node ', modelItem.node);
383
-
384
- const { bind } = modelItem;
385
- if (bind) {
386
- /*
387
- todo: investigate why bind is an element when created in fx-bind.init() and an fx-bind object when
388
- created lazily.
389
- */
390
- if (typeof bind.hasAttribute === 'function' && bind.hasAttribute('constraint')) {
391
- const constraint = bind.getAttribute('constraint');
392
- if (constraint) {
393
- const compute = evaluateXPathToBoolean(constraint, modelItem.node, this);
394
- console.log('modelItem validity computed: ', compute);
395
- modelItem.constraint = compute;
396
- this.formElement.addToRefresh(modelItem); // let fore know that modelItem needs refresh
397
- if (!compute) valid = false;
398
- // ### alerts are added only once during model-construct. Otherwise they would add up in each run of revalidate()
399
- if (!this.modelConstructed) {
400
- // todo: get alert from attribute or child element
401
- const alert = bind.getAlert();
402
- if (alert) {
403
- modelItem.addAlert(alert);
404
- }
405
341
  }
406
- }
342
+ this.computes += 1;
407
343
  }
408
- if (typeof bind.hasAttribute === 'function' && bind.hasAttribute('required')) {
409
- const required = bind.getAttribute('required');
410
- if (required) {
411
- const compute = evaluateXPathToBoolean(required, modelItem.node, this);
412
- console.log('modelItem required computed: ', compute);
413
- modelItem.required = compute;
414
- this.formElement.addToRefresh(modelItem); // let fore know that modelItem needs refresh
415
- if(!modelItem.node.textContent){
416
- valid = false;
417
- }
418
- // if (!compute) valid = false;
419
- /*
420
- if (!this.modelConstructed) {
421
- // todo: get alert from attribute or child element
422
- const alert = bind.getAlert();
423
- if (alert) {
424
- modelItem.addAlert(alert);
425
- }
344
+ }
345
+
346
+ /**
347
+ * Iterates all modelItems to calculate the validation status.
348
+ *
349
+ * Model alerts are given on 'fx-bind' elements as either attribute `alert` or as `fx-alert` child elements.
350
+ *
351
+ * During model-construct all model alerts are added to the modelItem if any
352
+ *
353
+ * to revalidate:
354
+ * Gets the `constraint` attribute declaration from modelItem.bind
355
+ * Computes the XPath to a Boolean
356
+ * Updates the modelItem.constraint property
357
+ *
358
+ * todo: type checking
359
+ * todo: run browser validation API
360
+ *
361
+ */
362
+ revalidate() {
363
+ console.info('%crevalidate', 'font-style: italic; background: #90a4ae; color:white; padding:0.3rem 5rem 0.3rem 0.3rem;display:block;width:100%;');
364
+
365
+ if (this.modelItems.length === 0) return true;
366
+
367
+ console.group('### revalidate');
368
+ console.time('revalidate');
369
+
370
+ // reset submission validation
371
+ // this.parentNode.classList.remove('submit-validation-failed')
372
+ let valid = true;
373
+ this.modelItems.forEach(modelItem => {
374
+ // console.log('validating node ', modelItem.node);
375
+
376
+ const {bind} = modelItem;
377
+ if (bind) {
378
+ /*
379
+ todo: investigate why bind is an element when created in fx-bind.init() and an fx-bind object when
380
+ created lazily.
381
+ */
382
+ if (typeof bind.hasAttribute === 'function' && bind.hasAttribute('constraint')) {
383
+ const constraint = bind.getAttribute('constraint');
384
+ if (constraint) {
385
+ const compute = evaluateXPathToBoolean(constraint, modelItem.node, this);
386
+ // console.log('modelItem validity computed: ', compute);
387
+ modelItem.constraint = compute;
388
+ this.formElement.addToRefresh(modelItem); // let fore know that modelItem needs refresh
389
+ if (!compute) valid = false;
390
+ // ### alerts are added only once during model-construct. Otherwise they would add up in each run of revalidate()
391
+ if (!this.modelConstructed) {
392
+ // todo: get alert from attribute or child element
393
+ const alert = bind.getAlert();
394
+ if (alert) {
395
+ modelItem.addAlert(alert);
396
+ }
397
+ }
398
+ }
399
+ }
400
+ if (typeof bind.hasAttribute === 'function' && bind.hasAttribute('required')) {
401
+ const required = bind.getAttribute('required');
402
+ if (required) {
403
+ const compute = evaluateXPathToBoolean(required, modelItem.node, this);
404
+ // console.log('modelItem required computed: ', compute);
405
+ modelItem.required = compute;
406
+ this.formElement.addToRefresh(modelItem); // let fore know that modelItem needs refresh
407
+ if (!modelItem.node.textContent) {
408
+ console.log('modelItem required check failed: ');
409
+ valid = false;
410
+ }
411
+ // if (!compute) valid = false;
412
+ /*
413
+ if (!this.modelConstructed) {
414
+ // todo: get alert from attribute or child element
415
+ const alert = bind.getAlert();
416
+ if (alert) {
417
+ modelItem.addAlert(alert);
418
+ }
419
+ }
420
+ */
421
+ }
422
+ }
426
423
  }
427
- */
428
- }
429
- }
430
- }
431
- });
432
- console.timeEnd('revalidate');
433
- console.log('modelItems after revalidate: ', this.modelItems);
434
- console.groupEnd();
435
- return valid;
436
- }
437
-
438
- /**
439
- *
440
- * @param node
441
- * @returns {ModelItem}
442
- */
443
- getModelItem(node) {
444
- return this.modelItems.find(m => m.node === node);
445
- }
446
-
447
- /**
448
- * get the default evaluation context for this model.
449
- * @returns {Element} the
450
- */
451
- getDefaultContext() {
452
- return this.instances[0].getDefaultContext();
453
- }
454
-
455
- getDefaultInstance() {
456
- return this?.instances[0];
457
- }
458
-
459
- getDefaultInstanceData() {
460
- console.log('default instance data ', this.instances[0].instanceData);
461
- return this.instances[0].instanceData;
462
- }
463
-
464
- getInstance(id) {
465
- // console.log('getInstance ', id);
466
- // console.log('instances ', this.instances);
467
- // console.log('instances array ',Array.from(this.instances));
468
-
469
- const instArray = Array.from(this.instances);
470
- return instArray.find(inst => inst.id === id);
471
- }
472
-
473
- evalBinding(bindingExpr) {
474
- // console.log('MODEL.evalBinding ', bindingExpr);
475
- // default context of evaluation is always the default instance
476
- const result = this.instances[0].evalXPath(bindingExpr);
477
- return result;
478
- }
424
+ });
425
+ console.timeEnd('revalidate');
426
+ console.log('modelItems after revalidate: ', this.modelItems);
427
+ console.groupEnd();
428
+ return valid;
429
+ }
430
+
431
+ /**
432
+ *
433
+ * @param node
434
+ * @returns {ModelItem}
435
+ */
436
+ getModelItem(node) {
437
+ return this.modelItems.find(m => m.node === node);
438
+ }
439
+
440
+ /**
441
+ * get the default evaluation context for this model.
442
+ * @returns {Element} the
443
+ */
444
+ getDefaultContext() {
445
+ return this.instances[0].getDefaultContext();
446
+ }
447
+
448
+ getDefaultInstance() {
449
+ return this?.instances[0];
450
+ }
451
+
452
+ getDefaultInstanceData() {
453
+ console.log('default instance data ', this.instances[0].instanceData);
454
+ return this.instances[0].instanceData;
455
+ }
456
+
457
+ getInstance(id) {
458
+ // console.log('getInstance ', id);
459
+ // console.log('instances ', this.instances);
460
+ // console.log('instances array ',Array.from(this.instances));
461
+
462
+ const instArray = Array.from(this.instances);
463
+ return instArray.find(inst => inst.id === id);
464
+ }
465
+
466
+ evalBinding(bindingExpr) {
467
+ // console.log('MODEL.evalBinding ', bindingExpr);
468
+ // default context of evaluation is always the default instance
469
+ const result = this.instances[0].evalXPath(bindingExpr);
470
+ return result;
471
+ }
479
472
  }
473
+
480
474
  if (!customElements.get('fx-model')) {
481
- customElements.define('fx-model', FxModel);
475
+ customElements.define('fx-model', FxModel);
482
476
  }