@jinntec/fore 2.2.0 → 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 +1 -1
- 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 +3 -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 +4 -6
- package/src/functions/fx-functionlib.js +39 -42
- package/src/functions/registerFunction.js +95 -86
- package/src/fx-bind.js +19 -24
- package/src/fx-connection.js +226 -0
- package/src/fx-fore.js +846 -815
- package/src/fx-header.js +4 -4
- package/src/fx-instance.js +23 -27
- package/src/fx-model.js +405 -383
- package/src/fx-submission.js +399 -397
- package/src/fx-var.js +6 -5
- 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 +74 -56
- 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 -959
- 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,
|
|
@@ -139,137 +152,136 @@ export class FxModel extends HTMLElement {
|
|
|
139
152
|
);
|
|
140
153
|
}
|
|
141
154
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
/*
|
|
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
|
+
/*
|
|
160
173
|
if (this.skipUpdate){
|
|
161
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%;');
|
|
162
175
|
return;
|
|
163
176
|
}
|
|
164
177
|
*/
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
178
|
+
this.recalculate();
|
|
179
|
+
this.revalidate();
|
|
180
|
+
// console.log('updateModel finished with modelItems ', this.modelItems);
|
|
168
181
|
|
|
169
|
-
|
|
170
|
-
|
|
182
|
+
// console.timeEnd('updateModel');
|
|
183
|
+
}
|
|
171
184
|
|
|
172
|
-
|
|
173
|
-
|
|
185
|
+
rebuild() {
|
|
186
|
+
// console.log(`### <<<<< rebuild() '${this.fore.id}' >>>>>`);
|
|
174
187
|
|
|
175
|
-
|
|
176
|
-
|
|
188
|
+
this.mainGraph = new DepGraph(false); // do: should be moved down below binds.length check but causes errors in tests.
|
|
189
|
+
this.modelItems = [];
|
|
177
190
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
binds.forEach(bind => {
|
|
187
|
-
bind.init(this);
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
console.log(`mainGraph`, this.mainGraph);
|
|
191
|
-
console.log(`rebuild mainGraph calc order`, this.mainGraph.overallOrder());
|
|
192
|
-
|
|
193
|
-
// this.dispatchEvent(new CustomEvent('rebuild-done', {detail: {maingraph: this.mainGraph}}));
|
|
194
|
-
Fore.dispatch(this,'rebuild-done',{maingraph:this.mainGraph});
|
|
195
|
-
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;
|
|
196
197
|
}
|
|
197
198
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
this.subgraph.addNode(modelItem.path, modelItem.node);
|
|
220
|
-
// const dependents = this.mainGraph.dependantsOf(modelItem.path, false);
|
|
221
|
-
// this._addSubgraphDependencies(modelItem.path);
|
|
222
|
-
if (this.mainGraph.hasNode(modelItem.path)) {
|
|
223
|
-
// const dependents = this.mainGraph.directDependantsOf(modelItem.path)
|
|
224
|
-
|
|
225
|
-
const all = this.mainGraph.dependantsOf(modelItem.path, false);
|
|
226
|
-
const dependents = all.reverse();
|
|
227
|
-
if (dependents.length !== 0) {
|
|
228
|
-
dependents.forEach(dep => {
|
|
229
|
-
// const subdep = this.mainGraph.dependentsOf(dep,false);
|
|
230
|
-
// subgraph.addDependency(dep, modelItem.path);
|
|
231
|
-
const val = this.mainGraph.getNodeData(dep);
|
|
232
|
-
this.subgraph.addNode(dep, val);
|
|
233
|
-
if (dep.includes(':')) {
|
|
234
|
-
const path = dep.substring(0, dep.indexOf(':'));
|
|
235
|
-
this.subgraph.addNode(path, val);
|
|
236
|
-
|
|
237
|
-
const deps = this.mainGraph.dependentsOf(modelItem.path, false);
|
|
238
|
-
// if we find the dep to be first in list of dependents we are dependent on ourselves not adding edge to modelItem.path
|
|
239
|
-
if (deps.indexOf(dep) !== 0) {
|
|
240
|
-
this.subgraph.addDependency(dep, modelItem.path);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
// subgraph.addDependency(dep,modelItem.path);
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
});
|
|
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
|
+
}
|
|
248
220
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
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);
|
|
255
253
|
}
|
|
254
|
+
}
|
|
255
|
+
// subgraph.addDependency(dep,modelItem.path);
|
|
256
256
|
});
|
|
257
|
-
|
|
258
|
-
this.formElement.toRefresh = toRefresh;
|
|
259
|
-
this.changed = [];
|
|
260
|
-
Fore.dispatch(this,'recalculate-done',{graph:this.subgraph,computes:this.computes})
|
|
261
|
-
} else {
|
|
262
|
-
const v = this.mainGraph.overallOrder(false);
|
|
263
|
-
v.forEach(path => {
|
|
264
|
-
const node = this.mainGraph.getNodeData(path);
|
|
265
|
-
this.compute(node, path);
|
|
266
|
-
});
|
|
267
|
-
Fore.dispatch(this,'recalculate-done',{graph:this.mainGraph,computes:this.computes})
|
|
257
|
+
}
|
|
268
258
|
}
|
|
269
|
-
|
|
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);
|
|
267
|
+
}
|
|
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 });
|
|
270
280
|
}
|
|
281
|
+
console.log(`${this.parentElement.id} recalculate finished with modelItems `, this.modelItems);
|
|
282
|
+
}
|
|
271
283
|
|
|
272
|
-
|
|
284
|
+
/*
|
|
273
285
|
_addSubgraphDependencies(path){
|
|
274
286
|
const dependents = this.mainGraph.directDependantsOf(path)
|
|
275
287
|
|
|
@@ -306,17 +318,17 @@ export class FxModel extends HTMLElement {
|
|
|
306
318
|
}
|
|
307
319
|
*/
|
|
308
320
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
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
|
+
/*
|
|
320
332
|
if (property === 'readonly') {
|
|
321
333
|
// make sure that calculated items are always readonly
|
|
322
334
|
if(modelItem.bind['calculate']){
|
|
@@ -328,88 +340,90 @@ export class FxModel extends HTMLElement {
|
|
|
328
340
|
}
|
|
329
341
|
}
|
|
330
342
|
*/
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
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
|
+
/*
|
|
342
354
|
console.log(
|
|
343
355
|
`recalculating path ${path} - Expr:'${expr}' computed`,
|
|
344
356
|
modelItem[property],
|
|
345
357
|
);
|
|
346
358
|
*/
|
|
347
|
-
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
this.computes += 1;
|
|
359
|
+
}
|
|
351
360
|
}
|
|
361
|
+
}
|
|
362
|
+
this.computes += 1;
|
|
352
363
|
}
|
|
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
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
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
|
+
/*
|
|
384
395
|
todo: investigate why bind is an element when created in fx-bind.init() and an fx-bind object when
|
|
385
396
|
created lazily.
|
|
386
397
|
*/
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
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
|
+
/*
|
|
413
427
|
if (!this.modelConstructed) {
|
|
414
428
|
// todo: get alert from attribute or child element
|
|
415
429
|
const alert = bind.getAlert();
|
|
@@ -418,105 +432,113 @@ export class FxModel extends HTMLElement {
|
|
|
418
432
|
}
|
|
419
433
|
}
|
|
420
434
|
*/
|
|
421
|
-
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
});
|
|
425
|
-
console.log('modelItems after revalidate: ', this.modelItems);
|
|
426
|
-
return valid;
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
addChanged(modelItem){
|
|
430
|
-
if(this.inited){
|
|
431
|
-
this.changed.push(modelItem);
|
|
435
|
+
}
|
|
432
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);
|
|
433
446
|
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
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
|
+
*/
|
|
452
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
|
+
*/
|
|
453
475
|
if (this.instances.length) {
|
|
454
476
|
return this.instances[0];
|
|
455
477
|
}
|
|
456
478
|
return this.getInstance('default');
|
|
457
479
|
}
|
|
458
480
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
getInstance(id) {
|
|
464
|
-
// console.log('getInstance ', id);
|
|
465
|
-
// console.log('instances ', this.instances);
|
|
466
|
-
// console.log('instances array ',Array.from(this.instances));
|
|
467
|
-
|
|
468
|
-
let found;
|
|
469
|
-
if(id === 'default'){
|
|
470
|
-
found = this.instances[0];
|
|
471
|
-
}
|
|
472
|
-
// ### lookup in local instances first
|
|
473
|
-
if(!found) {
|
|
474
|
-
const instArray = Array.from(this.instances);
|
|
475
|
-
found = instArray.find(inst => inst.id === id);
|
|
476
|
-
const parentFore = this.fore.parentNode.nodeType === Node.DOCUMENT_FRAGMENT_NODE
|
|
477
|
-
? this.fore.parentNode.host.closest('fx-fore')
|
|
478
|
-
: this.fore.parentNode.closest('fx-fore');
|
|
481
|
+
getDefaultInstanceData() {
|
|
482
|
+
return this.instances[0].getInstanceData();
|
|
483
|
+
}
|
|
479
484
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
const parentFore = this.fore.parentNode.nodeType === Node.DOCUMENT_FRAGMENT_NODE
|
|
485
|
-
? this.fore.parentNode.host.closest('fx-fore')
|
|
486
|
-
: this.fore.parentNode.closest('fx-fore');
|
|
487
|
-
if (parentFore) {
|
|
488
|
-
console.log('shared instances from parent', this.parentNode.id);
|
|
489
|
-
const parentInstances = parentFore.getModel().instances;
|
|
490
|
-
const shared = parentInstances.filter(shared => shared.hasAttribute('shared'));
|
|
491
|
-
found = shared.find(found => found.id === id);
|
|
492
|
-
}
|
|
485
|
+
getInstance(id) {
|
|
486
|
+
// console.log('getInstance ', id);
|
|
487
|
+
// console.log('instances ', this.instances);
|
|
488
|
+
// console.log('instances array ',Array.from(this.instances));
|
|
493
489
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
}
|
|
498
|
-
if (id === 'default') {
|
|
499
|
-
return this.getDefaultInstance(); // if id is not found always defaults to first in doc order
|
|
500
|
-
}
|
|
501
|
-
if(!found && this.fore.strict){
|
|
502
|
-
// return this.getDefaultInstance(); // if id is not found always defaults to first in doc order
|
|
503
|
-
Fore.dispatch(this, 'error', {
|
|
504
|
-
origin: this,
|
|
505
|
-
message: `Instance '${id}' does not exist`,
|
|
506
|
-
level:'Error'
|
|
507
|
-
});
|
|
508
|
-
}
|
|
509
|
-
return null;
|
|
490
|
+
let found;
|
|
491
|
+
if (id === 'default') {
|
|
492
|
+
found = this.instances[0];
|
|
510
493
|
}
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
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
|
+
});
|
|
517
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
|
+
}
|
|
518
540
|
}
|
|
519
541
|
|
|
520
542
|
if (!customElements.get('fx-model')) {
|
|
521
|
-
|
|
543
|
+
customElements.define('fx-model', FxModel);
|
|
522
544
|
}
|