@jinntec/fore 1.9.0 → 1.10.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/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/package.json +1 -1
- package/resources/fore.css +4 -0
- package/src/DependencyNotifyingDomFacade.js +2 -2
- package/src/actions/abstract-action.js +24 -11
- package/src/actions/fx-insert.js +13 -6
- package/src/actions/fx-setfocus.js +1 -1
- package/src/fore.js +238 -1
- package/src/fx-bind.js +12 -6
- package/src/fx-fore.js +61 -4
- package/src/fx-model.js +3 -1
- package/src/fx-submission.js +8 -14
- package/src/tools/adi.js +16 -7
- package/src/tools/fx-action-log.js +1 -1
- package/src/ui/abstract-control.js +8 -7
- package/src/ui/fx-control.js +1 -1
- package/src/ui/fx-dialog.js +2 -0
- package/src/ui/fx-group.js +4 -1
- package/src/ui/fx-repeat-attributes.js +4 -1
- package/src/ui/fx-repeat.js +11 -2
- package/src/ui/fx-repeatitem.js +5 -3
- package/src/ui/fx-trigger.js +3 -1
- package/src/xpath-evaluation.js +101 -4
- package/src/xpath-util.js +9 -12
package/package.json
CHANGED
package/resources/fore.css
CHANGED
|
@@ -114,7 +114,7 @@ export class DependencyNotifyingDomFacade {
|
|
|
114
114
|
// eslint-disable-next-line class-methods-use-this
|
|
115
115
|
getNextSibling(node, bucket) {
|
|
116
116
|
for (let sibling = node.nextSibling; sibling; sibling = sibling.nextSibling) {
|
|
117
|
-
if (!getBucketsForNode(sibling).includes(bucket)) {
|
|
117
|
+
if (bucket && !getBucketsForNode(sibling).includes(bucket)) {
|
|
118
118
|
// eslint-disable-next-line no-continue
|
|
119
119
|
continue;
|
|
120
120
|
}
|
|
@@ -149,7 +149,7 @@ export class DependencyNotifyingDomFacade {
|
|
|
149
149
|
previousSibling;
|
|
150
150
|
previousSibling = previousSibling.previousSibling
|
|
151
151
|
) {
|
|
152
|
-
if (!getBucketsForNode(previousSibling).includes(bucket)) {
|
|
152
|
+
if (bucket && !getBucketsForNode(previousSibling).includes(bucket)) {
|
|
153
153
|
// eslint-disable-next-line no-continue
|
|
154
154
|
continue;
|
|
155
155
|
}
|
|
@@ -101,6 +101,8 @@ export class AbstractAction extends foreElementMixin(HTMLElement) {
|
|
|
101
101
|
super();
|
|
102
102
|
this.detail = {};
|
|
103
103
|
this.needsUpdate = false;
|
|
104
|
+
|
|
105
|
+
this.changedPathsQueue = [];
|
|
104
106
|
}
|
|
105
107
|
|
|
106
108
|
connectedCallback() {
|
|
@@ -158,9 +160,9 @@ export class AbstractAction extends foreElementMixin(HTMLElement) {
|
|
|
158
160
|
|
|
159
161
|
async performSafe() {
|
|
160
162
|
try {
|
|
161
|
-
await this.perform();
|
|
162
|
-
// Return
|
|
163
|
-
return
|
|
163
|
+
const changedPaths = await this.perform();
|
|
164
|
+
// Return truthy to indicate success
|
|
165
|
+
return changedPaths;
|
|
164
166
|
} catch (error) {
|
|
165
167
|
const stringifiedComponent = `<${this.localName} ${Array.from(this.attributes).map(attr=>`${attr.name}="${attr.value}"`).join(' ')}>…</${this.localName}>`;
|
|
166
168
|
await Fore.dispatch(this, 'error', {
|
|
@@ -288,7 +290,8 @@ export class AbstractAction extends foreElementMixin(HTMLElement) {
|
|
|
288
290
|
}
|
|
289
291
|
|
|
290
292
|
// Perform the action once. But quit if it errored
|
|
291
|
-
|
|
293
|
+
const result = this.performSafe();
|
|
294
|
+
if (!result) {
|
|
292
295
|
return;
|
|
293
296
|
}
|
|
294
297
|
|
|
@@ -305,7 +308,8 @@ export class AbstractAction extends foreElementMixin(HTMLElement) {
|
|
|
305
308
|
|
|
306
309
|
// After loop is done call actionPerformed to update the model and UI
|
|
307
310
|
await loop();
|
|
308
|
-
|
|
311
|
+
// TODO: pass the result(s)
|
|
312
|
+
this._finalizePerform(resolveThisEvent, []);
|
|
309
313
|
|
|
310
314
|
return;
|
|
311
315
|
}
|
|
@@ -321,13 +325,18 @@ export class AbstractAction extends foreElementMixin(HTMLElement) {
|
|
|
321
325
|
}
|
|
322
326
|
}
|
|
323
327
|
|
|
324
|
-
await this.performSafe();
|
|
325
|
-
|
|
328
|
+
const result = await this.performSafe();
|
|
329
|
+
if (!result) {
|
|
330
|
+
this._finalizePerform(resolveThisEvent, []);
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
this._finalizePerform(resolveThisEvent, result);
|
|
334
|
+
|
|
326
335
|
}
|
|
327
336
|
|
|
328
|
-
_finalizePerform(resolveThisEvent) {
|
|
337
|
+
_finalizePerform(resolveThisEvent, changedPaths) {
|
|
329
338
|
this.currentEvent = null;
|
|
330
|
-
this.actionPerformed();
|
|
339
|
+
this.actionPerformed(changedPaths);
|
|
331
340
|
if (FxFore.outermostHandler === this) {
|
|
332
341
|
FxFore.outermostHandler = null;
|
|
333
342
|
/*
|
|
@@ -363,6 +372,7 @@ export class AbstractAction extends foreElementMixin(HTMLElement) {
|
|
|
363
372
|
this.evalInContext();
|
|
364
373
|
}
|
|
365
374
|
|
|
375
|
+
/*
|
|
366
376
|
this.dispatchEvent(
|
|
367
377
|
new CustomEvent('execute-action', {
|
|
368
378
|
composed: true,
|
|
@@ -371,13 +381,14 @@ export class AbstractAction extends foreElementMixin(HTMLElement) {
|
|
|
371
381
|
detail: {action: this, event: this.event},
|
|
372
382
|
}),
|
|
373
383
|
);
|
|
384
|
+
*/
|
|
374
385
|
|
|
375
386
|
}
|
|
376
387
|
|
|
377
388
|
/**
|
|
378
389
|
* calls the update cycle if action signalled that update is needed.
|
|
379
390
|
*/
|
|
380
|
-
actionPerformed() {
|
|
391
|
+
actionPerformed(changedPaths = []) {
|
|
381
392
|
const model = this.getModel();
|
|
382
393
|
if (!model) {
|
|
383
394
|
return;
|
|
@@ -402,13 +413,15 @@ export class AbstractAction extends foreElementMixin(HTMLElement) {
|
|
|
402
413
|
// console.log('running update cycle for outermostHandler', this);
|
|
403
414
|
model.recalculate();
|
|
404
415
|
model.revalidate();
|
|
405
|
-
model.parentNode.refresh(
|
|
416
|
+
model.parentNode.refresh(false, [...this.changedPathsQueue, ...changedPaths]);
|
|
417
|
+
this.changedPathsQueue = [];
|
|
406
418
|
this.dispatchActionPerformed();
|
|
407
419
|
} else if (this.needsUpdate) {
|
|
408
420
|
// console.log('Update delayed!');
|
|
409
421
|
// We need an update, but the outermost action handler is not done yet. Make this clear!
|
|
410
422
|
// console.log('running actionperformed on', this, ' to be updated by ', FxFore.outermostHandler);
|
|
411
423
|
FxFore.outermostHandler.needsUpdate = true;
|
|
424
|
+
FxFore.outermostHandler.changedPathsQueue.push(...changedPaths);
|
|
412
425
|
}
|
|
413
426
|
|
|
414
427
|
// console.log('running actionperformed on', this, ' outermostHandler', FxFore.outermostHandler);
|
package/src/actions/fx-insert.js
CHANGED
|
@@ -221,14 +221,19 @@ export class FxInsert extends AbstractAction {
|
|
|
221
221
|
}
|
|
222
222
|
}
|
|
223
223
|
}
|
|
224
|
+
// instance('default')/items/item[index()]
|
|
224
225
|
|
|
225
226
|
// console.log('insert context item ', insertLocationNode);
|
|
226
227
|
// console.log('parent ', insertLocationNode.parentNode);
|
|
227
228
|
// console.log('instance ', this.getModel().getDefaultContext());
|
|
228
229
|
// Fore.dispatch()
|
|
229
230
|
|
|
230
|
-
|
|
231
|
-
|
|
231
|
+
const instanceId = XPathUtil.resolveInstance(this, this.getAttribute('context'));
|
|
232
|
+
const inst = this.getModel().getInstance(instanceId);
|
|
233
|
+
// console.log('<<<<<<< resolved instance', inst);
|
|
234
|
+
// Note: the parent to insert under is always the parent of the inserted node. The 'context' is not always the parent if the sequence is empty, or the position is different
|
|
235
|
+
const xpath = XPathUtil.getPath(originSequenceClone.parentNode, instanceId);
|
|
236
|
+
|
|
232
237
|
|
|
233
238
|
const path = Fore.getDomNodeIndexString(originSequenceClone);
|
|
234
239
|
this.dispatchEvent(
|
|
@@ -259,7 +264,9 @@ export class FxInsert extends AbstractAction {
|
|
|
259
264
|
}),
|
|
260
265
|
);
|
|
261
266
|
|
|
262
|
-
|
|
267
|
+
this.needsUpdate = true;
|
|
268
|
+
console.log('Changed!', xpath)
|
|
269
|
+
return [xpath];
|
|
263
270
|
}
|
|
264
271
|
|
|
265
272
|
// eslint-disable-next-line class-methods-use-this
|
|
@@ -273,9 +280,9 @@ export class FxInsert extends AbstractAction {
|
|
|
273
280
|
return null;
|
|
274
281
|
}
|
|
275
282
|
|
|
276
|
-
actionPerformed() {
|
|
277
|
-
this.getModel().rebuild();
|
|
278
|
-
super.actionPerformed();
|
|
283
|
+
actionPerformed(changedPaths) {
|
|
284
|
+
// this.getModel().rebuild();
|
|
285
|
+
super.actionPerformed(changedPaths);
|
|
279
286
|
}
|
|
280
287
|
|
|
281
288
|
/**
|
package/src/fore.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import getInScopeContext from "./getInScopeContext.js";
|
|
2
|
-
import {evaluateXPathToString} from "./xpath-evaluation.js";
|
|
2
|
+
import {evaluateXPath, evaluateXPathToFirstNode, evaluateXPathToString, evaluateXPathToNodes} from "./xpath-evaluation.js";
|
|
3
3
|
import { XPathUtil } from "./xpath-util.js";
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -18,6 +18,243 @@ export class Fore {
|
|
|
18
18
|
|
|
19
19
|
static TYPE_DEFAULT = 'xs:string';
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* recursively walks along a template instance that contains all nodes relevant for editing and
|
|
23
|
+
* applying all nodes being present in partial instance onto it.
|
|
24
|
+
*
|
|
25
|
+
* @param start
|
|
26
|
+
* @param partial
|
|
27
|
+
* @returns {*}
|
|
28
|
+
* @private
|
|
29
|
+
*/
|
|
30
|
+
static combine(start, partial, foreElement,expr){
|
|
31
|
+
if(!start) return;
|
|
32
|
+
|
|
33
|
+
const appended = false;
|
|
34
|
+
// ### get the path of the current element
|
|
35
|
+
let path = evaluateXPath('path()',start,foreElement)[0];
|
|
36
|
+
if(expr){
|
|
37
|
+
path = expr;
|
|
38
|
+
}
|
|
39
|
+
// const attrMap = new Map();
|
|
40
|
+
console.log('########process path', XPathUtil.shortenPath(path));
|
|
41
|
+
const predicates = Fore.buildPredicates(start);
|
|
42
|
+
// console.log('########process search', path);
|
|
43
|
+
// ### search the path in the partial instance
|
|
44
|
+
const toMerge = evaluateXPathToFirstNode( path,partial,foreElement);
|
|
45
|
+
if(expr){
|
|
46
|
+
console.log('skipped one - new toMerge', toMerge);
|
|
47
|
+
}
|
|
48
|
+
// console.log('tomerge node', toMerge);
|
|
49
|
+
// if(toMerge.length === 1){}
|
|
50
|
+
if(toMerge){
|
|
51
|
+
// console.log('merging nodes', toMerge);
|
|
52
|
+
const mergeAttrs = Fore.buildPredicates(toMerge);
|
|
53
|
+
if(predicates === mergeAttrs) {
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
// console.log('####### attr maps', attrMap, toMergeAttrMap);
|
|
57
|
+
// console.log('########process merge attrs', mergeAttrs);
|
|
58
|
+
if(start.getAttribute('type') !== toMerge.getAttribute('type') ){
|
|
59
|
+
console.log('###### element type attr not matching', start, toMerge);
|
|
60
|
+
const nextSibling = start.nextElementSibling;
|
|
61
|
+
if(nextSibling && nextSibling.nodeName === start.nodeName){
|
|
62
|
+
console.log('nextSibling', nextSibling, XPathUtil.shortenPath(path));
|
|
63
|
+
console.log('start', start, XPathUtil.shortenPath(path));
|
|
64
|
+
Fore.combine(start,partial,foreElement,path);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ### iterate the attributes of the template node
|
|
69
|
+
// if(attrMap === toMergeAttrMap){
|
|
70
|
+
// if(start.attributes){
|
|
71
|
+
Array.from(start.attributes).forEach(attr => {
|
|
72
|
+
// ### if the template attribute has a matching attribute in partial...
|
|
73
|
+
if (toMerge.hasAttribute(attr.nodeName)) {
|
|
74
|
+
if (attr.nodeName !== 'xmlns') {
|
|
75
|
+
// console.log('overwrite attr', attr);
|
|
76
|
+
const toMergeAttr = toMerge.getAttribute(attr.nodeName);
|
|
77
|
+
// if (attr.nodeValue !== toMergeAttr.nodeValue) {
|
|
78
|
+
// ### apply the attribute value from partial to template
|
|
79
|
+
if(toMergeAttr){
|
|
80
|
+
start.setAttribute(attr.nodeName, toMergeAttr)
|
|
81
|
+
}
|
|
82
|
+
// }
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
// }
|
|
87
|
+
}else{
|
|
88
|
+
console.log('###### element in template but not in partial',start);
|
|
89
|
+
const nextSibling = start.nextElementSibling;
|
|
90
|
+
if(nextSibling && nextSibling.nodeName === start.nodeName){
|
|
91
|
+
Fore.combine(nextSibling,partial,foreElement,path);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// ### if we don't have children we still might have text
|
|
95
|
+
if(toMerge.children.length === 0){
|
|
96
|
+
const toMergeText = toMerge.textContent;
|
|
97
|
+
if(toMergeText){
|
|
98
|
+
start.textContent = toMergeText;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ### if the template node does not have children but the partial has then copy them over
|
|
103
|
+
if(start.children.length === 0 && toMerge.children.length !== 0){
|
|
104
|
+
const mergeChildren = Array.from(toMerge.children);
|
|
105
|
+
mergeChildren.forEach(child => {
|
|
106
|
+
start.append(child);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ### recurse
|
|
112
|
+
if(start.children){
|
|
113
|
+
Array.from(start.children).forEach(element => {
|
|
114
|
+
/*
|
|
115
|
+
console.log('stepping into element', element);
|
|
116
|
+
if(element.getAttribute('type') !== toMerge.getAttribute('type')){
|
|
117
|
+
// console.log('###### element in template but not in partial');
|
|
118
|
+
// const nextSibling = start.nextElementSibling;
|
|
119
|
+
const nextSibling = element.nextElementSibling;
|
|
120
|
+
if(nextSibling && nextSibling.nodeName === toMerge.nodeName){
|
|
121
|
+
console.log('nextSibling', nextSibling, XPathUtil.shortenPath(path));
|
|
122
|
+
Fore.combine(nextSibling,partial,foreElement,path);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
*/
|
|
126
|
+
return Fore.combine(element, partial,foreElement,null);
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
return start;
|
|
132
|
+
}
|
|
133
|
+
/*
|
|
134
|
+
static combine(start, partial, foreElement,expr){
|
|
135
|
+
if(!start) return;
|
|
136
|
+
// ### get the path of the current element
|
|
137
|
+
let path = evaluateXPath('path()',start,foreElement)[0];
|
|
138
|
+
if(expr){
|
|
139
|
+
path = expr;
|
|
140
|
+
}
|
|
141
|
+
// const attrMap = new Map();
|
|
142
|
+
console.log('########process path', XPathUtil.shortenPath(path));
|
|
143
|
+
const predicates = Fore.buildPredicates(start);
|
|
144
|
+
/!*
|
|
145
|
+
if(predicates){
|
|
146
|
+
path = path.substring(1, path.lastIndexOf('['));
|
|
147
|
+
}
|
|
148
|
+
path += Fore.buildPredicates(start);
|
|
149
|
+
*!/
|
|
150
|
+
// console.log('########process search', path);
|
|
151
|
+
// ### search the path in the partial instance
|
|
152
|
+
const toMerge = evaluateXPathToFirstNode( path,partial,foreElement);
|
|
153
|
+
|
|
154
|
+
// console.log('tomerge node', toMerge);
|
|
155
|
+
// if(toMerge.length === 1){}
|
|
156
|
+
if(toMerge){
|
|
157
|
+
// console.log('merging nodes', toMerge);
|
|
158
|
+
const mergeAttrs = Fore.buildPredicates(toMerge);
|
|
159
|
+
if(predicates === mergeAttrs) {
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
// console.log('####### attr maps', attrMap, toMergeAttrMap);
|
|
163
|
+
// console.log('########process merge attrs', mergeAttrs);
|
|
164
|
+
/!*
|
|
165
|
+
if(toMerge.getAttribute('type') !== start.getAttribute('type')){
|
|
166
|
+
// console.log('###### element in template but not in partial');
|
|
167
|
+
// const nextSibling = start.nextElementSibling;
|
|
168
|
+
const nextSibling = start.nextElementSibling;
|
|
169
|
+
if(nextSibling && nextSibling.nodeName === start.nodeName){
|
|
170
|
+
console.log('nextSibling', nextSibling, XPathUtil.shortenPath(path));
|
|
171
|
+
Fore.combine(nextSibling,partial,foreElement,path);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
*!/
|
|
175
|
+
|
|
176
|
+
// ### iterate the attributes of the template node
|
|
177
|
+
// if(attrMap === toMergeAttrMap){
|
|
178
|
+
// if(start.attributes){
|
|
179
|
+
Array.from(start.attributes).forEach(attr => {
|
|
180
|
+
// ### if the template attribute has a matching attribute in partial...
|
|
181
|
+
if (toMerge.hasAttribute(attr.nodeName)) {
|
|
182
|
+
if (attr.nodeName !== 'xmlns') {
|
|
183
|
+
// console.log('overwrite attr', attr);
|
|
184
|
+
const toMergeAttr = toMerge.getAttribute(attr.nodeName);
|
|
185
|
+
// if (attr.nodeValue !== toMergeAttr.nodeValue) {
|
|
186
|
+
// ### apply the attribute value from partial to template
|
|
187
|
+
if(toMergeAttr){
|
|
188
|
+
start.setAttribute(attr.nodeName, toMergeAttr)
|
|
189
|
+
}
|
|
190
|
+
// }
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
// }
|
|
195
|
+
}else{
|
|
196
|
+
console.log('###### element in template but not in partial',start);
|
|
197
|
+
const nextSibling = start.nextElementSibling;
|
|
198
|
+
if(nextSibling && nextSibling.nodeName === start.nodeName){
|
|
199
|
+
Fore.combine(nextSibling,partial,foreElement,path);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// ### if we don't have children we still might have text
|
|
203
|
+
if(toMerge.children.length === 0){
|
|
204
|
+
const toMergeText = toMerge.textContent;
|
|
205
|
+
if(toMergeText){
|
|
206
|
+
start.textContent = toMergeText;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// ### if the template node does not have children but the partial has then copy them over
|
|
211
|
+
if(start.children.length === 0 && toMerge.children.length !== 0){
|
|
212
|
+
const mergeChildren = Array.from(toMerge.children);
|
|
213
|
+
mergeChildren.forEach(child => {
|
|
214
|
+
start.append(child);
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// ### recurse
|
|
220
|
+
if(start.children){
|
|
221
|
+
Array.from(start.children).forEach(element => {
|
|
222
|
+
/!*
|
|
223
|
+
console.log('stepping into element', element);
|
|
224
|
+
if(element.getAttribute('type') !== toMerge.getAttribute('type')){
|
|
225
|
+
// console.log('###### element in template but not in partial');
|
|
226
|
+
// const nextSibling = start.nextElementSibling;
|
|
227
|
+
const nextSibling = element.nextElementSibling;
|
|
228
|
+
if(nextSibling && nextSibling.nodeName === toMerge.nodeName){
|
|
229
|
+
console.log('nextSibling', nextSibling, XPathUtil.shortenPath(path));
|
|
230
|
+
Fore.combine(nextSibling,partial,foreElement,path);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
*!/
|
|
234
|
+
return Fore.combine(element, partial,foreElement);
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
return start;
|
|
240
|
+
}
|
|
241
|
+
*/
|
|
242
|
+
|
|
243
|
+
static buildPredicates(node){
|
|
244
|
+
let attrPredicate='';
|
|
245
|
+
Array.from(node.attributes).forEach(attr =>{
|
|
246
|
+
// attrMap.set(attr.nodeName,attr.nodeValue);
|
|
247
|
+
// if(attr.nodeName !== 'xmlns'){
|
|
248
|
+
// if(attr.nodeValue !== ''){
|
|
249
|
+
// attrPredicate += `[@${attr.nodeName}='${attr.nodeValue}']`;
|
|
250
|
+
// }else{
|
|
251
|
+
attrPredicate += `[@${attr.nodeName}]`;
|
|
252
|
+
// }
|
|
253
|
+
// }
|
|
254
|
+
});
|
|
255
|
+
return attrPredicate
|
|
256
|
+
}
|
|
257
|
+
|
|
21
258
|
|
|
22
259
|
/**
|
|
23
260
|
* returns true if target element is the widget itself or some element within the widget.
|
package/src/fx-bind.js
CHANGED
|
@@ -79,8 +79,10 @@ export class FxBind extends foreElementMixin(HTMLElement) {
|
|
|
79
79
|
|
|
80
80
|
_buildBindGraph() {
|
|
81
81
|
if (this.bindType === 'xml') {
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
this.nodeset.forEach(node => {
|
|
83
|
+
const instance = XPathUtil.resolveInstance(this);
|
|
84
|
+
|
|
85
|
+
const path = XPathUtil.getPath(node, instance);
|
|
84
86
|
this.model.mainGraph.addNode(path, node);
|
|
85
87
|
|
|
86
88
|
/* ### catching references in the 'ref' itself...
|
|
@@ -156,8 +158,10 @@ export class FxBind extends foreElementMixin(HTMLElement) {
|
|
|
156
158
|
if (!this.model.mainGraph.hasNode(nodeHash)) {
|
|
157
159
|
this.model.mainGraph.addNode(nodeHash, node);
|
|
158
160
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
+
refs.forEach(ref => {
|
|
162
|
+
const instance = XPathUtil.resolveInstance(this, path);
|
|
163
|
+
|
|
164
|
+
const otherPath = XPathUtil.getPath(ref, instance);
|
|
161
165
|
// console.log('otherPath', otherPath)
|
|
162
166
|
|
|
163
167
|
// todo: nasty hack to prevent duplicate pathes like 'a[1]' and 'a[1]/text()[1]' to end up as separate nodes in the graph
|
|
@@ -301,8 +305,10 @@ export class FxBind extends foreElementMixin(HTMLElement) {
|
|
|
301
305
|
const targetNode = node;
|
|
302
306
|
|
|
303
307
|
// const path = fx.evaluateXPath('path()',node);
|
|
304
|
-
|
|
305
|
-
|
|
308
|
+
// const path = this.getPath(node);
|
|
309
|
+
const instance = XPathUtil.resolveInstance(this, this.ref);
|
|
310
|
+
|
|
311
|
+
const path = XPathUtil.getPath(node, instance);
|
|
306
312
|
// const shortPath = this.shortenPath(path);
|
|
307
313
|
|
|
308
314
|
// ### constructing default modelitem - will get evaluated during recalculate()
|
package/src/fx-fore.js
CHANGED
|
@@ -295,6 +295,7 @@ export class FxFore extends HTMLElement {
|
|
|
295
295
|
this.addEventListener('path-mutated', () => {
|
|
296
296
|
this.someInstanceDataStructureChanged = true;
|
|
297
297
|
});
|
|
298
|
+
|
|
298
299
|
}
|
|
299
300
|
|
|
300
301
|
_injectDevtools(){
|
|
@@ -449,7 +450,42 @@ export class FxFore extends HTMLElement {
|
|
|
449
450
|
// console.timeEnd('refresh');
|
|
450
451
|
}
|
|
451
452
|
|
|
452
|
-
async refresh(force) {
|
|
453
|
+
async refresh(force, changedPaths) {
|
|
454
|
+
if (!changedPaths) {
|
|
455
|
+
changedPaths = this.toRefresh.map(item => item.path);
|
|
456
|
+
} else {
|
|
457
|
+
this.toRefresh.push(
|
|
458
|
+
...changedPaths
|
|
459
|
+
.map(
|
|
460
|
+
path =>
|
|
461
|
+
this.getModel()
|
|
462
|
+
.modelItems
|
|
463
|
+
.find(item => item.path === path)
|
|
464
|
+
)
|
|
465
|
+
.filter(Boolean)
|
|
466
|
+
);
|
|
467
|
+
|
|
468
|
+
for(const changedPath of changedPaths) {
|
|
469
|
+
for (const repeat of this.querySelectorAll('fx-repeat')) {
|
|
470
|
+
if (repeat.closest('fx-fore') !== this) {
|
|
471
|
+
continue;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
if (repeat.touchedPaths.has(changedPath)) {
|
|
475
|
+
// Make a temporary model-item-like structure for this
|
|
476
|
+
this.toRefresh.push({
|
|
477
|
+
path: changedPath,
|
|
478
|
+
boundControls: [repeat]
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
console.log('Found a repeat to update!!!', repeat)
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
if (this.isRefreshing) {
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
453
489
|
// refresh () {
|
|
454
490
|
// ### refresh Fore UI elements
|
|
455
491
|
// if (!this.initialRun && this.toRefresh.length !== 0) {
|
|
@@ -468,7 +504,7 @@ export class FxFore extends HTMLElement {
|
|
|
468
504
|
}
|
|
469
505
|
|
|
470
506
|
// ### check if other controls depend on current modelItem
|
|
471
|
-
const {mainGraph} = this.getModel();
|
|
507
|
+
const { mainGraph } = this.getModel();
|
|
472
508
|
if (mainGraph && mainGraph.hasNode(modelItem.path)) {
|
|
473
509
|
const deps = this.getModel().mainGraph.dependentsOf(modelItem.path, false);
|
|
474
510
|
// ### iterate dependant modelItems and refresh all their boundControls
|
|
@@ -501,7 +537,9 @@ export class FxFore extends HTMLElement {
|
|
|
501
537
|
});
|
|
502
538
|
*/
|
|
503
539
|
|
|
504
|
-
|
|
540
|
+
if(this.inited){
|
|
541
|
+
Fore.refreshChildren(this, true);
|
|
542
|
+
}
|
|
505
543
|
// console.timeEnd('refreshChildren');
|
|
506
544
|
}
|
|
507
545
|
|
|
@@ -517,6 +555,13 @@ export class FxFore extends HTMLElement {
|
|
|
517
555
|
// this.initialRun = false;
|
|
518
556
|
this.style.visibility='visible';
|
|
519
557
|
Fore.dispatch(this, 'refresh-done', {});
|
|
558
|
+
|
|
559
|
+
this.isRefreshing = true;
|
|
560
|
+
this.parentNode.closest('fx-fore')?.refresh(false, changedPaths);
|
|
561
|
+
for (const subFore of this.querySelectorAll('fx-fore')) {
|
|
562
|
+
subFore.refresh(false, changedPaths);
|
|
563
|
+
}
|
|
564
|
+
this.isRefreshing = false;
|
|
520
565
|
}
|
|
521
566
|
|
|
522
567
|
/**
|
|
@@ -705,6 +750,18 @@ export class FxFore extends HTMLElement {
|
|
|
705
750
|
*/
|
|
706
751
|
async _lazyCreateInstance() {
|
|
707
752
|
const model = this.querySelector('fx-model');
|
|
753
|
+
// Inherit shared models from the parent component
|
|
754
|
+
|
|
755
|
+
const parentFore = this.parentNode.closest('fx-fore');
|
|
756
|
+
if (parentFore) {
|
|
757
|
+
const sharedInstances = Array.from(parentFore.getModel().querySelectorAll('fx-instance')).filter(instance => instance.hasAttribute('shared'));
|
|
758
|
+
for(const instance of sharedInstances) {
|
|
759
|
+
this.getModel().instances.push(instance);
|
|
760
|
+
}
|
|
761
|
+
this.getModel().updateModel();
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
|
|
708
765
|
if (model.instances.length === 0) {
|
|
709
766
|
// console.log('### lazy creation of instance');
|
|
710
767
|
const generatedInstance = document.createElement('fx-instance');
|
|
@@ -716,7 +773,7 @@ export class FxFore extends HTMLElement {
|
|
|
716
773
|
generatedInstance.instanceData = generated;
|
|
717
774
|
model.instances.push(generatedInstance);
|
|
718
775
|
// console.log('generatedInstance ', this.getModel().getDefaultInstanceData());
|
|
719
|
-
Fore.dispatch(this,'instance-loaded',{instance:this})
|
|
776
|
+
Fore.dispatch(this,'instance-loaded',{instance:this});
|
|
720
777
|
}
|
|
721
778
|
}
|
|
722
779
|
|
package/src/fx-model.js
CHANGED
|
@@ -67,7 +67,9 @@ export class FxModel extends HTMLElement {
|
|
|
67
67
|
// const path = fx.evaluateXPath('path()',node);
|
|
68
68
|
let path;
|
|
69
69
|
if (node.nodeType) {
|
|
70
|
-
|
|
70
|
+
const instance = XPathUtil.resolveInstance(model, ref);
|
|
71
|
+
|
|
72
|
+
path = XPathUtil.getPath(node, instance);
|
|
71
73
|
} else {
|
|
72
74
|
path = null;
|
|
73
75
|
targetNode = node;
|
package/src/fx-submission.js
CHANGED
|
@@ -339,26 +339,20 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
|
|
|
339
339
|
|
|
340
340
|
/*
|
|
341
341
|
if(this.replace === 'merge'){
|
|
342
|
+
if(targetInstance.type !== 'xml') {
|
|
343
|
+
Fore.dispatch(this, "warn", {'message': 'merging of instances only work for type xml'});
|
|
344
|
+
}
|
|
342
345
|
if (targetInstance && targetInstance.type === 'xml') {
|
|
343
346
|
targetInstance.partialInstance = data;
|
|
344
|
-
|
|
345
|
-
|
|
347
|
+
// const resultDoc = new DOMParser(`${data.nodeName}`, 'application/xml');
|
|
348
|
+
// console.log('resultDoc', resultDoc)
|
|
349
|
+
const merged = Fore.combine(targetInstance.instanceData.firstElementChild, data.firstElementChild, this,null);
|
|
346
350
|
console.log('merged', merged);
|
|
347
351
|
|
|
348
352
|
targetInstance.instanceData = merged;
|
|
349
353
|
console.log('merging partial instance',targetInstance.partialInstance)
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
*!/
|
|
353
|
-
|
|
354
|
-
// Skip any refreshes if the model is not yet inited#
|
|
355
|
-
// duplicate from replace='instance'
|
|
356
|
-
// if (this.model.inited) {
|
|
357
|
-
this.model.updateModel(); // force update
|
|
358
|
-
const owner = this.getOwnerForm();
|
|
359
|
-
// owner.mergePartial = true;
|
|
360
|
-
owner.refresh(true);
|
|
361
|
-
// }
|
|
354
|
+
this.model.updateModel();
|
|
355
|
+
this.getOwnerForm().refresh(true);
|
|
362
356
|
}
|
|
363
357
|
}
|
|
364
358
|
*/
|