@jinntec/fore 2.1.1 → 2.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.
- package/README.md +2 -2
- package/dist/fore-dev.js +2 -2
- package/dist/fore-dev.js.map +1 -1
- package/dist/fore.js +2 -2
- package/dist/fore.js.map +1 -1
- package/index.js +4 -2
- package/package.json +10 -38
- package/resources/fore.css +263 -262
- package/resources/vars.css +8 -0
- package/src/DependencyNotifyingDomFacade.js +1 -0
- package/src/ForeElementMixin.js +243 -228
- package/src/actions/abstract-action.js +407 -398
- package/src/actions/fx-action.js +6 -6
- package/src/actions/fx-append.js +1 -1
- package/src/actions/fx-call.js +63 -62
- package/src/actions/fx-confirm.js +11 -11
- package/src/actions/fx-construct-done.js +4 -4
- package/src/actions/fx-copy.js +36 -36
- package/src/actions/fx-delete.js +77 -79
- package/src/actions/fx-dispatch.js +14 -14
- package/src/actions/fx-hide.js +15 -15
- package/src/actions/fx-insert.js +9 -12
- package/src/actions/fx-insertchild.js +88 -0
- package/src/actions/fx-load.js +188 -185
- package/src/actions/fx-message.js +18 -19
- package/src/actions/fx-refresh.js +10 -10
- package/src/actions/fx-reload.js +16 -14
- package/src/actions/fx-replace.js +0 -1
- package/src/actions/fx-reset.js +31 -31
- package/src/actions/fx-send.js +59 -52
- package/src/actions/fx-setattribute.js +48 -49
- package/src/actions/fx-setfocus.js +54 -58
- package/src/actions/fx-setvalue.js +85 -81
- package/src/actions/fx-show.js +11 -11
- package/src/actions/fx-toggle.js +2 -2
- package/src/actions/fx-toggleboolean.js +38 -39
- package/src/actions/fx-unmodified.js +27 -0
- package/src/actions/fx-update.js +6 -6
- package/src/dep_graph.js +1 -1
- package/src/drawdown.js +23 -30
- package/src/events.js +19 -19
- package/src/fore.js +143 -125
- package/src/functions/common-function.js +24 -25
- package/src/functions/fx-function.js +6 -99
- package/src/functions/fx-functionlib.js +56 -0
- package/src/functions/registerFunction.js +112 -0
- package/src/fx-bind.js +19 -24
- package/src/fx-connection.js +226 -0
- package/src/fx-fore.js +844 -815
- package/src/fx-header.js +4 -4
- package/src/fx-instance.js +25 -29
- package/src/fx-model.js +405 -380
- package/src/fx-submission.js +399 -397
- package/src/fx-var.js +13 -12
- package/src/getInScopeContext.js +102 -89
- package/src/json-util.js +24 -24
- package/src/lab/fore-component.js +48 -52
- package/src/lab/instance-inspector.js +13 -16
- package/src/modelitem.js +48 -10
- package/src/relevance.js +12 -16
- package/src/tools/adi.js +858 -812
- package/src/tools/fx-action-log.js +377 -295
- package/src/tools/fx-devtools.js +210 -210
- package/src/tools/fx-dom-inspector.js +123 -122
- package/src/tools/fx-json-instance.js +262 -253
- package/src/tools/fx-log-item.js +6 -11
- package/src/tools/fx-log-settings.js +358 -301
- package/src/tools/fx-minimap.js +186 -177
- package/src/tools/helpers.js +1 -1
- package/src/ui/abstract-control.js +73 -53
- package/src/ui/fx-case.js +59 -7
- package/src/ui/fx-container.js +13 -14
- package/src/ui/fx-control.js +572 -538
- package/src/ui/fx-dialog.js +3 -3
- package/src/ui/fx-droptarget.js +3 -4
- package/src/ui/fx-group.js +4 -2
- package/src/ui/fx-hint.js +3 -0
- package/src/ui/fx-inspector.js +5 -5
- package/src/ui/fx-items.js +11 -11
- package/src/ui/fx-output.js +10 -12
- package/src/ui/fx-repeat-attributes.js +23 -27
- package/src/ui/fx-repeat.js +347 -344
- package/src/ui/fx-repeatitem.js +75 -68
- package/src/ui/fx-switch.js +39 -14
- package/src/ui/fx-trigger.js +4 -4
- package/src/withDraggability.js +193 -191
- package/src/xpath-evaluation.js +969 -958
- package/src/xpath-util.js +161 -134
package/src/fx-model.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
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
|
* The model of this Fore scope. It holds all the intances, binding, submissions and custom functions that
|
|
@@ -15,121 +15,134 @@ import {XPathUtil} from './xpath-util.js';
|
|
|
15
15
|
*
|
|
16
16
|
*/
|
|
17
17
|
export class FxModel extends HTMLElement {
|
|
18
|
-
|
|
19
|
-
constructor() {
|
|
20
|
-
super();
|
|
21
|
-
// this.id = '';
|
|
22
|
-
|
|
23
|
-
this.instances = [];
|
|
24
|
-
this.modelItems = [];
|
|
25
|
-
this.defaultContext = {};
|
|
26
|
-
this.changed = [];
|
|
27
|
-
|
|
28
|
-
// this.mainGraph = new DepGraph(false);
|
|
29
|
-
this.inited = false;
|
|
30
|
-
this.modelConstructed = false;
|
|
31
|
-
this.attachShadow({mode: 'open'});
|
|
32
|
-
this.computes = 0;
|
|
33
|
-
this.fore = {};
|
|
34
|
-
}
|
|
18
|
+
static dataChanged = false;
|
|
35
19
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
20
|
+
constructor() {
|
|
21
|
+
super();
|
|
22
|
+
// this.id = '';
|
|
39
23
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
24
|
+
/**
|
|
25
|
+
* @type {import('./fx-instance.js').FxInstance[]}
|
|
26
|
+
*/
|
|
27
|
+
this.instances = [];
|
|
28
|
+
/**
|
|
29
|
+
* @type {import('./modelitem.js').ModelItem[]}
|
|
30
|
+
*/
|
|
31
|
+
this.modelItems = [];
|
|
32
|
+
this.defaultContext = {};
|
|
33
|
+
this.changed = [];
|
|
34
|
+
|
|
35
|
+
// this.mainGraph = new DepGraph(false);
|
|
36
|
+
this.inited = false;
|
|
37
|
+
this.modelConstructed = false;
|
|
38
|
+
this.attachShadow({ mode: 'open' });
|
|
39
|
+
this.computes = 0;
|
|
40
|
+
this.fore = {};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
get formElement() {
|
|
44
|
+
return this.parentElement;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
connectedCallback() {
|
|
48
|
+
// console.log('connectedCallback ', this);
|
|
49
|
+
this.setAttribute('inert', 'true');
|
|
50
|
+
this.shadowRoot.innerHTML = `
|
|
43
51
|
<slot></slot>
|
|
44
52
|
`;
|
|
45
53
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
54
|
+
/*
|
|
55
|
+
this.addEventListener('model-construct-done', () => {
|
|
56
|
+
// this.modelConstructed = true;
|
|
57
|
+
// console.log('model-construct-done fired ', this.modelConstructed);
|
|
58
|
+
// console.log('model-construct-done fired ', e.detail.model.instances);
|
|
59
|
+
},
|
|
60
|
+
{ once: true },
|
|
61
|
+
);
|
|
62
|
+
*/
|
|
51
63
|
|
|
52
|
-
|
|
53
|
-
|
|
64
|
+
this.skipUpdate = false;
|
|
65
|
+
this.fore = this.parentNode;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
static lazyCreateModelItem(model, ref, node) {
|
|
69
|
+
// console.log('lazyCreateModelItem ', node);
|
|
70
|
+
|
|
71
|
+
let targetNode = {};
|
|
72
|
+
if (node === null || node === undefined) return null;
|
|
73
|
+
if (node.nodeType === node.TEXT_NODE) {
|
|
74
|
+
// const parent = node.parentNode;
|
|
75
|
+
// console.log('PARENT ', parent);
|
|
76
|
+
targetNode = node.parentNode;
|
|
77
|
+
} else {
|
|
78
|
+
targetNode = node;
|
|
54
79
|
}
|
|
55
80
|
|
|
56
|
-
|
|
57
|
-
|
|
81
|
+
// const path = fx.evaluateXPath('path()',node);
|
|
82
|
+
let path;
|
|
83
|
+
if (node.nodeType) {
|
|
84
|
+
const instance = XPathUtil.resolveInstance(model, ref);
|
|
58
85
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
// console.log('PARENT ', parent);
|
|
64
|
-
targetNode = node.parentNode;
|
|
65
|
-
} else {
|
|
66
|
-
targetNode = node;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// const path = fx.evaluateXPath('path()',node);
|
|
70
|
-
let path;
|
|
71
|
-
if (node.nodeType) {
|
|
72
|
-
const instance = XPathUtil.resolveInstance(model, ref);
|
|
73
|
-
|
|
74
|
-
path = XPathUtil.getPath(node, instance);
|
|
75
|
-
} else {
|
|
76
|
-
path = null;
|
|
77
|
-
targetNode = node;
|
|
78
|
-
}
|
|
79
|
-
// const path = XPathUtil.getPath(node);
|
|
80
|
-
|
|
81
|
-
// ### intializing ModelItem with default values (as there is no <fx-bind> matching for given ref)
|
|
82
|
-
const mi = new ModelItem(
|
|
83
|
-
path,
|
|
84
|
-
ref,
|
|
85
|
-
Fore.READONLY_DEFAULT,
|
|
86
|
-
Fore.RELEVANT_DEFAULT,
|
|
87
|
-
Fore.REQUIRED_DEFAULT,
|
|
88
|
-
Fore.CONSTRAINT_DEFAULT,
|
|
89
|
-
Fore.TYPE_DEFAULT,
|
|
90
|
-
targetNode,
|
|
91
|
-
this,
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
// console.log('new ModelItem is instanceof ModelItem ', mi instanceof ModelItem);
|
|
95
|
-
model.registerModelItem(mi);
|
|
96
|
-
return mi;
|
|
86
|
+
path = XPathUtil.getPath(node, instance);
|
|
87
|
+
} else {
|
|
88
|
+
path = null;
|
|
89
|
+
targetNode = node;
|
|
97
90
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
91
|
+
// const path = XPathUtil.getPath(node);
|
|
92
|
+
|
|
93
|
+
// ### intializing ModelItem with default values (as there is no <fx-bind> matching for given ref)
|
|
94
|
+
const mi = new ModelItem(
|
|
95
|
+
path,
|
|
96
|
+
ref,
|
|
97
|
+
Fore.READONLY_DEFAULT,
|
|
98
|
+
Fore.RELEVANT_DEFAULT,
|
|
99
|
+
Fore.REQUIRED_DEFAULT,
|
|
100
|
+
Fore.CONSTRAINT_DEFAULT,
|
|
101
|
+
Fore.TYPE_DEFAULT,
|
|
102
|
+
targetNode,
|
|
103
|
+
this,
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
// console.log('new ModelItem is instanceof ModelItem ', mi instanceof ModelItem);
|
|
107
|
+
model.registerModelItem(mi);
|
|
108
|
+
return mi;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* modelConstruct starts actual processing of the model by
|
|
113
|
+
*
|
|
114
|
+
* 1. loading instances if present or constructing one
|
|
115
|
+
* 2. calling updateModel to run the model update cycle of rebuild, recalculate and revalidate
|
|
116
|
+
*
|
|
117
|
+
* @event model-construct-done is fired once all instances have be loaded or after generating instance
|
|
118
|
+
*
|
|
119
|
+
*/
|
|
120
|
+
async modelConstruct() {
|
|
121
|
+
console.log(`### <<<<< dispatching model-construct for '${this.fore.id}' >>>>>`);
|
|
122
|
+
// this.dispatchEvent(new CustomEvent('model-construct', { detail: this }));
|
|
123
|
+
Fore.dispatch(this, 'model-construct', { model: this });
|
|
124
|
+
|
|
125
|
+
// console.time('instance-loading');
|
|
126
|
+
const instances = this.querySelectorAll('fx-instance');
|
|
127
|
+
if (instances.length > 0) {
|
|
128
|
+
const promises = [];
|
|
129
|
+
instances.forEach(instance => {
|
|
130
|
+
promises.push(instance.init());
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// Wait until all the instances are built
|
|
134
|
+
await Promise.all(promises);
|
|
124
135
|
this.instances = Array.from(instances);
|
|
125
136
|
// console.log('_modelConstruct this.instances ', this.instances);
|
|
126
137
|
// Await until the model-construct-done event is handled off
|
|
138
|
+
this.modelConstructed = true;
|
|
127
139
|
await Fore.dispatch(this, 'model-construct-done', {model: this});
|
|
128
140
|
this.inited = true;
|
|
129
141
|
this.updateModel();
|
|
130
142
|
} else {
|
|
131
143
|
// ### if there's no instance one will created
|
|
132
144
|
console.log(`### <<<<< dispatching model-construct-done for '${this.fore.id}' >>>>>`);
|
|
145
|
+
this.modelConstructed = true;
|
|
133
146
|
await this.dispatchEvent(
|
|
134
147
|
new CustomEvent('model-construct-done', {
|
|
135
148
|
composed: false,
|
|
@@ -138,135 +151,137 @@ export class FxModel extends HTMLElement {
|
|
|
138
151
|
}),
|
|
139
152
|
);
|
|
140
153
|
}
|
|
141
|
-
// console.timeEnd('instance-loading');
|
|
142
|
-
this.inited = true;
|
|
143
|
-
}
|
|
144
154
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
155
|
+
const functionlibImports = Array.from(this.querySelectorAll('fx-functionlib'));
|
|
156
|
+
await Promise.all(functionlibImports.map(lib => lib.readyPromise));
|
|
157
|
+
// console.timeEnd('instance-loading');
|
|
158
|
+
this.inited = true;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
registerModelItem(modelItem) {
|
|
162
|
+
// console.log('ModelItem registered ', modelItem);
|
|
163
|
+
this.modelItems.push(modelItem);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* update action triggering the update cycle
|
|
168
|
+
*/
|
|
169
|
+
updateModel() {
|
|
170
|
+
// console.time('updateModel');
|
|
171
|
+
this.rebuild();
|
|
172
|
+
/*
|
|
157
173
|
if (this.skipUpdate){
|
|
158
174
|
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%;');
|
|
159
175
|
return;
|
|
160
176
|
}
|
|
161
177
|
*/
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
178
|
+
this.recalculate();
|
|
179
|
+
this.revalidate();
|
|
180
|
+
// console.log('updateModel finished with modelItems ', this.modelItems);
|
|
165
181
|
|
|
166
|
-
|
|
167
|
-
|
|
182
|
+
// console.timeEnd('updateModel');
|
|
183
|
+
}
|
|
168
184
|
|
|
169
|
-
|
|
170
|
-
|
|
185
|
+
rebuild() {
|
|
186
|
+
// console.log(`### <<<<< rebuild() '${this.fore.id}' >>>>>`);
|
|
171
187
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
// trigger recursive initialization of the fx-bind elements
|
|
176
|
-
const binds = this.querySelectorAll('fx-model > fx-bind');
|
|
177
|
-
if (binds.length === 0) {
|
|
178
|
-
// console.log('skipped model update');
|
|
179
|
-
this.skipUpdate = true;
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
188
|
+
this.mainGraph = new DepGraph(false); // do: should be moved down below binds.length check but causes errors in tests.
|
|
189
|
+
this.modelItems = [];
|
|
182
190
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
// this.dispatchEvent(new CustomEvent('rebuild-done', {detail: {maingraph: this.mainGraph}}));
|
|
191
|
-
Fore.dispatch(this,'rebuild-done',{maingraph:this.mainGraph});
|
|
192
|
-
console.log('mainGraph', this.mainGraph);
|
|
191
|
+
// trigger recursive initialization of the fx-bind elements
|
|
192
|
+
const binds = this.querySelectorAll('fx-model > fx-bind');
|
|
193
|
+
if (binds.length === 0) {
|
|
194
|
+
// console.log('skipped model update');
|
|
195
|
+
this.skipUpdate = true;
|
|
196
|
+
return;
|
|
193
197
|
}
|
|
194
198
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
this.subgraph.addNode(modelItem.path, modelItem.node);
|
|
217
|
-
// const dependents = this.mainGraph.dependantsOf(modelItem.path, false);
|
|
218
|
-
// this._addSubgraphDependencies(modelItem.path);
|
|
219
|
-
if (this.mainGraph.hasNode(modelItem.path)) {
|
|
220
|
-
// const dependents = this.mainGraph.directDependantsOf(modelItem.path)
|
|
221
|
-
|
|
222
|
-
const all = this.mainGraph.dependantsOf(modelItem.path, false);
|
|
223
|
-
const dependents = all.reverse();
|
|
224
|
-
if (dependents.length !== 0) {
|
|
225
|
-
dependents.forEach(dep => {
|
|
226
|
-
// const subdep = this.mainGraph.dependentsOf(dep,false);
|
|
227
|
-
// subgraph.addDependency(dep, modelItem.path);
|
|
228
|
-
const val = this.mainGraph.getNodeData(dep);
|
|
229
|
-
this.subgraph.addNode(dep, val);
|
|
230
|
-
if (dep.includes(':')) {
|
|
231
|
-
const path = dep.substring(0, dep.indexOf(':'));
|
|
232
|
-
this.subgraph.addNode(path, val);
|
|
233
|
-
|
|
234
|
-
const deps = this.mainGraph.dependentsOf(modelItem.path, false);
|
|
235
|
-
// if we find the dep to be first in list of dependents we are dependent on ourselves not adding edge to modelItem.path
|
|
236
|
-
if (deps.indexOf(dep) !== 0) {
|
|
237
|
-
this.subgraph.addDependency(dep, modelItem.path);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
// subgraph.addDependency(dep,modelItem.path);
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
});
|
|
199
|
+
binds.forEach(bind => {
|
|
200
|
+
bind.init(this);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
console.log('mainGraph', this.mainGraph);
|
|
204
|
+
console.log('rebuild mainGraph calc order', this.mainGraph.overallOrder());
|
|
205
|
+
|
|
206
|
+
// this.dispatchEvent(new CustomEvent('rebuild-done', {detail: {maingraph: this.mainGraph}}));
|
|
207
|
+
Fore.dispatch(this, 'rebuild-done', { maingraph: this.mainGraph });
|
|
208
|
+
console.log('mainGraph', this.mainGraph);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* recalculation of all modelItems. Uses dependency graph to determine order of computation.
|
|
213
|
+
*
|
|
214
|
+
* todo: use 'changed' flag on modelItems to determine subgraph for recalculation. Flag already exists but is not used.
|
|
215
|
+
*/
|
|
216
|
+
recalculate() {
|
|
217
|
+
if (!this.mainGraph) {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
245
220
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
221
|
+
// console.log(`### <<<<< recalculate() '${this.fore.id}' >>>>>`);
|
|
222
|
+
|
|
223
|
+
// console.log('changed nodes ', this.changed);
|
|
224
|
+
this.computes = 0;
|
|
225
|
+
|
|
226
|
+
this.subgraph = new DepGraph(false);
|
|
227
|
+
// ### create the subgraph for all changed modelItems
|
|
228
|
+
if (this.changed.length !== 0) {
|
|
229
|
+
// ### build the subgraph
|
|
230
|
+
this.changed.forEach(modelItem => {
|
|
231
|
+
this.subgraph.addNode(modelItem.path, modelItem.node);
|
|
232
|
+
// const dependents = this.mainGraph.dependantsOf(modelItem.path, false);
|
|
233
|
+
// this._addSubgraphDependencies(modelItem.path);
|
|
234
|
+
if (this.mainGraph.hasNode(modelItem.path)) {
|
|
235
|
+
// const dependents = this.mainGraph.directDependantsOf(modelItem.path)
|
|
236
|
+
|
|
237
|
+
const all = this.mainGraph.dependantsOf(modelItem.path, false);
|
|
238
|
+
const dependents = all.reverse();
|
|
239
|
+
if (dependents.length !== 0) {
|
|
240
|
+
dependents.forEach(dep => {
|
|
241
|
+
// const subdep = this.mainGraph.dependentsOf(dep,false);
|
|
242
|
+
// subgraph.addDependency(dep, modelItem.path);
|
|
243
|
+
const val = this.mainGraph.getNodeData(dep);
|
|
244
|
+
this.subgraph.addNode(dep, val);
|
|
245
|
+
if (dep.includes(':')) {
|
|
246
|
+
const path = dep.substring(0, dep.indexOf(':'));
|
|
247
|
+
this.subgraph.addNode(path, val);
|
|
248
|
+
|
|
249
|
+
const deps = this.mainGraph.dependentsOf(modelItem.path, false);
|
|
250
|
+
// if we find the dep to be first in list of dependents we are dependent on ourselves not adding edge to modelItem.path
|
|
251
|
+
if (deps.indexOf(dep) !== 0) {
|
|
252
|
+
this.subgraph.addDependency(dep, modelItem.path);
|
|
252
253
|
}
|
|
254
|
+
}
|
|
255
|
+
// subgraph.addDependency(dep,modelItem.path);
|
|
253
256
|
});
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
Fore.dispatch(this,'recalculate-done',{graph:this.mainGraph,computes:this.computes})
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
// ### compute the subgraph
|
|
262
|
+
const ordered = this.subgraph.overallOrder(false);
|
|
263
|
+
ordered.forEach(path => {
|
|
264
|
+
if (this.mainGraph.hasNode(path)) {
|
|
265
|
+
const node = this.mainGraph.getNodeData(path);
|
|
266
|
+
this.compute(node, path);
|
|
265
267
|
}
|
|
266
|
-
|
|
268
|
+
});
|
|
269
|
+
const toRefresh = [...this.changed];
|
|
270
|
+
this.formElement.toRefresh = toRefresh;
|
|
271
|
+
this.changed = [];
|
|
272
|
+
Fore.dispatch(this, 'recalculate-done', { graph: this.subgraph, computes: this.computes });
|
|
273
|
+
} else {
|
|
274
|
+
const v = this.mainGraph.overallOrder(false);
|
|
275
|
+
v.forEach(path => {
|
|
276
|
+
const node = this.mainGraph.getNodeData(path);
|
|
277
|
+
this.compute(node, path);
|
|
278
|
+
});
|
|
279
|
+
Fore.dispatch(this, 'recalculate-done', { graph: this.mainGraph, computes: this.computes });
|
|
267
280
|
}
|
|
281
|
+
console.log(`${this.parentElement.id} recalculate finished with modelItems `, this.modelItems);
|
|
282
|
+
}
|
|
268
283
|
|
|
269
|
-
|
|
284
|
+
/*
|
|
270
285
|
_addSubgraphDependencies(path){
|
|
271
286
|
const dependents = this.mainGraph.directDependantsOf(path)
|
|
272
287
|
|
|
@@ -303,17 +318,17 @@ export class FxModel extends HTMLElement {
|
|
|
303
318
|
}
|
|
304
319
|
*/
|
|
305
320
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
321
|
+
/**
|
|
322
|
+
* (re-) computes a modelItem.
|
|
323
|
+
* @param {Node} node - the node the modelItem is attached to
|
|
324
|
+
* @param {string} path - the canonical XPath of the node
|
|
325
|
+
*/
|
|
326
|
+
compute(node, path) {
|
|
327
|
+
const modelItem = this.getModelItem(node);
|
|
328
|
+
if (modelItem && path.includes(':')) {
|
|
329
|
+
const property = path.split(':')[1];
|
|
330
|
+
if (property) {
|
|
331
|
+
/*
|
|
317
332
|
if (property === 'readonly') {
|
|
318
333
|
// make sure that calculated items are always readonly
|
|
319
334
|
if(modelItem.bind['calculate']){
|
|
@@ -325,88 +340,90 @@ export class FxModel extends HTMLElement {
|
|
|
325
340
|
}
|
|
326
341
|
}
|
|
327
342
|
*/
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
343
|
+
const expr = modelItem.bind[property];
|
|
344
|
+
if (property === 'calculate') {
|
|
345
|
+
const compute = evaluateXPath(expr, modelItem.node, this);
|
|
346
|
+
modelItem.value = compute;
|
|
347
|
+
modelItem.readonly = true; // calculated nodes are always readonly
|
|
348
|
+
} else if (property !== 'constraint' && property !== 'type') {
|
|
349
|
+
// ### re-compute the Boolean value of all facets expect 'constraint' and 'type' which are handled in revalidate()
|
|
350
|
+
if (expr) {
|
|
351
|
+
const compute = evaluateXPathToBoolean(expr, modelItem.node, this);
|
|
352
|
+
modelItem[property] = compute;
|
|
353
|
+
/*
|
|
339
354
|
console.log(
|
|
340
355
|
`recalculating path ${path} - Expr:'${expr}' computed`,
|
|
341
356
|
modelItem[property],
|
|
342
357
|
);
|
|
343
358
|
*/
|
|
344
|
-
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
this.computes += 1;
|
|
359
|
+
}
|
|
348
360
|
}
|
|
361
|
+
}
|
|
362
|
+
this.computes += 1;
|
|
349
363
|
}
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Iterates all modelItems to calculate the validation status.
|
|
368
|
+
*
|
|
369
|
+
* Model alerts are given on 'fx-bind' elements as either attribute `alert` or as `fx-alert` child elements.
|
|
370
|
+
*
|
|
371
|
+
* During model-construct all model alerts are added to the modelItem if any
|
|
372
|
+
*
|
|
373
|
+
* to revalidate:
|
|
374
|
+
* Gets the `constraint` attribute declaration from modelItem.bind
|
|
375
|
+
* Computes the XPath to a Boolean
|
|
376
|
+
* Updates the modelItem.constraint property
|
|
377
|
+
*
|
|
378
|
+
* todo: type checking
|
|
379
|
+
* todo: run browser validation API
|
|
380
|
+
*
|
|
381
|
+
*/
|
|
382
|
+
revalidate() {
|
|
383
|
+
if (this.modelItems.length === 0) return true;
|
|
384
|
+
|
|
385
|
+
// console.log(`### <<<<< revalidate() '${this.fore.id}' >>>>>`);
|
|
386
|
+
|
|
387
|
+
// reset submission validation
|
|
388
|
+
// this.parentNode.classList.remove('submit-validation-failed')
|
|
389
|
+
let valid = true;
|
|
390
|
+
this.modelItems.forEach(modelItem => {
|
|
391
|
+
// console.log('validating node ', modelItem.node);
|
|
392
|
+
const { bind } = modelItem;
|
|
393
|
+
if (bind) {
|
|
394
|
+
/*
|
|
381
395
|
todo: investigate why bind is an element when created in fx-bind.init() and an fx-bind object when
|
|
382
396
|
created lazily.
|
|
383
397
|
*/
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
398
|
+
if (typeof bind.hasAttribute === 'function' && bind.hasAttribute('constraint')) {
|
|
399
|
+
const constraint = bind.getAttribute('constraint');
|
|
400
|
+
if (constraint && modelItem.node) {
|
|
401
|
+
const compute = evaluateXPathToBoolean(constraint, modelItem.node, this);
|
|
402
|
+
// console.log('modelItem validity computed: ', compute);
|
|
403
|
+
modelItem.constraint = compute;
|
|
404
|
+
this.formElement.addToRefresh(modelItem); // let fore know that modelItem needs refresh
|
|
405
|
+
if (!compute) {
|
|
406
|
+
console.log('validation failed on modelitem ', modelItem);
|
|
407
|
+
valid = false;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
if (typeof bind.hasAttribute === 'function' && bind.hasAttribute('required')) {
|
|
412
|
+
const required = bind.getAttribute('required');
|
|
413
|
+
if (required) {
|
|
414
|
+
const compute = evaluateXPathToBoolean(required, modelItem.node, this);
|
|
415
|
+
// console.log('modelItem required computed: ', compute);
|
|
416
|
+
modelItem.required = compute;
|
|
417
|
+
this.formElement.addToRefresh(modelItem); // let fore know that modelItem needs refresh
|
|
418
|
+
if (!modelItem.node.textContent) {
|
|
419
|
+
console.log(
|
|
420
|
+
'node is required but has no value ',
|
|
421
|
+
XPathUtil.getDocPath(modelItem.node),
|
|
422
|
+
);
|
|
423
|
+
valid = false;
|
|
424
|
+
}
|
|
425
|
+
// if (!compute) valid = false;
|
|
426
|
+
/*
|
|
410
427
|
if (!this.modelConstructed) {
|
|
411
428
|
// todo: get alert from attribute or child element
|
|
412
429
|
const alert = bind.getAlert();
|
|
@@ -415,105 +432,113 @@ export class FxModel extends HTMLElement {
|
|
|
415
432
|
}
|
|
416
433
|
}
|
|
417
434
|
*/
|
|
418
|
-
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
});
|
|
422
|
-
console.log('modelItems after revalidate: ', this.modelItems);
|
|
423
|
-
return valid;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
addChanged(modelItem){
|
|
427
|
-
if(this.inited){
|
|
428
|
-
this.changed.push(modelItem);
|
|
435
|
+
}
|
|
429
436
|
}
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
// console.log('modelItems after revalidate: ', this.modelItems);
|
|
440
|
+
return valid;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
addChanged(modelItem) {
|
|
444
|
+
if (this.inited) {
|
|
445
|
+
this.changed.push(modelItem);
|
|
430
446
|
}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
*
|
|
451
|
+
* @param {Node} node
|
|
452
|
+
* @returns {ModelItem}
|
|
453
|
+
*/
|
|
454
|
+
getModelItem(node) {
|
|
455
|
+
return this.modelItems.find(m => m.node === node);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* get the default evaluation context for this model.
|
|
460
|
+
* @returns {Element}
|
|
461
|
+
*/
|
|
462
|
+
getDefaultContext() {
|
|
463
|
+
return this.instances[0].getDefaultContext();
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* @returns {import('./fx-instance.js').FxInstance}
|
|
468
|
+
*/
|
|
449
469
|
getDefaultInstance() {
|
|
470
|
+
/*
|
|
471
|
+
if (this.instances.length === 0) {
|
|
472
|
+
throw new Error('No instances defined. Fore cannot work without any <data/> elements.');
|
|
473
|
+
}
|
|
474
|
+
*/
|
|
450
475
|
if (this.instances.length) {
|
|
451
476
|
return this.instances[0];
|
|
452
477
|
}
|
|
453
478
|
return this.getInstance('default');
|
|
454
479
|
}
|
|
455
480
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
getInstance(id) {
|
|
461
|
-
// console.log('getInstance ', id);
|
|
462
|
-
// console.log('instances ', this.instances);
|
|
463
|
-
// console.log('instances array ',Array.from(this.instances));
|
|
464
|
-
|
|
465
|
-
let found;
|
|
466
|
-
if(id === 'default'){
|
|
467
|
-
found = this.instances[0];
|
|
468
|
-
}
|
|
469
|
-
// ### lookup in local instances first
|
|
470
|
-
if(!found) {
|
|
471
|
-
const instArray = Array.from(this.instances);
|
|
472
|
-
found = instArray.find(inst => inst.id === id);
|
|
473
|
-
const parentFore = this.fore.parentNode.nodeType === Node.DOCUMENT_FRAGMENT_NODE
|
|
474
|
-
? this.fore.parentNode.host.closest('fx-fore')
|
|
475
|
-
: this.fore.parentNode.closest('fx-fore');
|
|
481
|
+
getDefaultInstanceData() {
|
|
482
|
+
return this.instances[0].getInstanceData();
|
|
483
|
+
}
|
|
476
484
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
const parentFore = this.fore.parentNode.nodeType === Node.DOCUMENT_FRAGMENT_NODE
|
|
482
|
-
? this.fore.parentNode.host.closest('fx-fore')
|
|
483
|
-
: this.fore.parentNode.closest('fx-fore');
|
|
484
|
-
if (parentFore) {
|
|
485
|
-
console.log('shared instances from parent', this.parentNode.id);
|
|
486
|
-
const parentInstances = parentFore.getModel().instances;
|
|
487
|
-
const shared = parentInstances.filter(shared => shared.hasAttribute('shared'));
|
|
488
|
-
found = shared.find(found => found.id === id);
|
|
489
|
-
}
|
|
485
|
+
getInstance(id) {
|
|
486
|
+
// console.log('getInstance ', id);
|
|
487
|
+
// console.log('instances ', this.instances);
|
|
488
|
+
// console.log('instances array ',Array.from(this.instances));
|
|
490
489
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
}
|
|
495
|
-
if (id === 'default') {
|
|
496
|
-
return this.getDefaultInstance(); // if id is not found always defaults to first in doc order
|
|
497
|
-
}
|
|
498
|
-
if(!found && this.fore.strict){
|
|
499
|
-
// return this.getDefaultInstance(); // if id is not found always defaults to first in doc order
|
|
500
|
-
Fore.dispatch(this, 'error', {
|
|
501
|
-
origin: this,
|
|
502
|
-
message: `Instance '${id}' does not exist`,
|
|
503
|
-
level:'Error'
|
|
504
|
-
});
|
|
505
|
-
}
|
|
506
|
-
return null;
|
|
490
|
+
let found;
|
|
491
|
+
if (id === 'default') {
|
|
492
|
+
found = this.instances[0];
|
|
507
493
|
}
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
494
|
+
// ### lookup in local instances first
|
|
495
|
+
if (!found) {
|
|
496
|
+
const instArray = Array.from(this.instances);
|
|
497
|
+
found = instArray.find(inst => inst.id === id);
|
|
498
|
+
const parentFore =
|
|
499
|
+
this.fore.parentNode.nodeType === Node.DOCUMENT_FRAGMENT_NODE
|
|
500
|
+
? this.fore.parentNode.host.closest('fx-fore')
|
|
501
|
+
: this.fore.parentNode.closest('fx-fore');
|
|
502
|
+
}
|
|
503
|
+
// ### lookup in parent Fore if present
|
|
504
|
+
if (!found) {
|
|
505
|
+
// const parentFore = this.fore.parentNode.closest('fx-fore');
|
|
506
|
+
const parentFore =
|
|
507
|
+
this.fore.parentNode.nodeType === Node.DOCUMENT_FRAGMENT_NODE
|
|
508
|
+
? this.fore.parentNode.host.closest('fx-fore')
|
|
509
|
+
: this.fore.parentNode.closest('fx-fore');
|
|
510
|
+
if (parentFore) {
|
|
511
|
+
console.log('shared instances from parent', this.parentNode.id);
|
|
512
|
+
const parentInstances = parentFore.getModel().instances;
|
|
513
|
+
const shared = parentInstances.filter(shared => shared.hasAttribute('shared'));
|
|
514
|
+
found = shared.find(found => found.id === id);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
if (found) {
|
|
518
|
+
return found;
|
|
519
|
+
}
|
|
520
|
+
if (id === 'default') {
|
|
521
|
+
return this.getDefaultInstance(); // if id is not found always defaults to first in doc order
|
|
522
|
+
}
|
|
523
|
+
if (!found && this.fore.strict) {
|
|
524
|
+
// return this.getDefaultInstance(); // if id is not found always defaults to first in doc order
|
|
525
|
+
Fore.dispatch(this, 'error', {
|
|
526
|
+
origin: this,
|
|
527
|
+
message: `Instance '${id}' does not exist`,
|
|
528
|
+
level: 'Error',
|
|
529
|
+
});
|
|
514
530
|
}
|
|
531
|
+
return null;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
evalBinding(bindingExpr) {
|
|
535
|
+
// console.log('MODEL.evalBinding ', bindingExpr);
|
|
536
|
+
// default context of evaluation is always the default instance
|
|
537
|
+
const result = this.instances[0].evalXPath(bindingExpr);
|
|
538
|
+
return result;
|
|
539
|
+
}
|
|
515
540
|
}
|
|
516
541
|
|
|
517
542
|
if (!customElements.get('fx-model')) {
|
|
518
|
-
|
|
543
|
+
customElements.define('fx-model', FxModel);
|
|
519
544
|
}
|