@jinntec/fore 1.0.0-5 → 1.2.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 +7 -28
- package/dist/fore-dev.js +43 -0
- package/dist/fore-dev.js.map +1 -0
- package/dist/fore.js +37 -0
- package/dist/fore.js.map +1 -0
- package/index.js +3 -1
- package/package.json +39 -41
- package/resources/fore.css +27 -54
- package/src/DependencyNotifyingDomFacade.js +5 -13
- package/src/ForeElementMixin.js +15 -22
- package/src/actions/abstract-action.js +34 -10
- package/src/actions/fx-action.js +7 -5
- package/src/actions/fx-append.js +8 -17
- package/src/actions/fx-confirm.js +5 -3
- package/src/actions/fx-delete.js +6 -3
- package/src/actions/fx-dispatch.js +9 -8
- package/src/actions/fx-hide.js +9 -6
- package/src/actions/fx-insert.js +27 -14
- package/src/actions/fx-message.js +3 -1
- package/src/actions/fx-refresh.js +24 -1
- package/src/actions/fx-replace.js +74 -0
- package/src/actions/fx-return.js +42 -0
- package/src/actions/fx-send.js +3 -1
- package/src/actions/fx-setfocus.js +37 -0
- package/src/actions/fx-setvalue.js +58 -51
- package/src/actions/fx-show.js +12 -4
- package/src/actions/fx-toggle.js +13 -9
- package/src/actions/fx-update.js +3 -1
- package/src/dep_graph.js +1 -1
- package/src/drawdown.js +67 -82
- package/src/fore.js +143 -26
- package/src/functions/fx-function.js +17 -3
- package/src/fx-bind.js +40 -200
- package/src/fx-fore.js +598 -568
- package/src/fx-header.js +3 -1
- package/src/fx-instance.js +9 -1
- package/src/fx-model.js +60 -27
- package/src/fx-submission.js +108 -51
- package/src/fx-var.js +7 -4
- package/src/getInScopeContext.js +23 -16
- package/src/modelitem.js +4 -4
- package/src/relevance.js +64 -0
- package/src/ui/abstract-control.js +65 -37
- package/src/ui/fx-alert.js +7 -1
- package/src/ui/fx-case.js +4 -3
- package/src/ui/fx-container.js +4 -2
- package/src/ui/fx-control.js +315 -34
- package/src/ui/fx-dialog.js +50 -45
- package/src/ui/fx-group.js +3 -1
- package/src/ui/fx-hint.js +4 -1
- package/src/ui/fx-inspector.js +118 -17
- package/src/ui/fx-items.js +7 -5
- package/src/ui/fx-output.js +19 -6
- package/src/ui/fx-repeat.js +13 -26
- package/src/ui/fx-repeatitem.js +10 -4
- package/src/ui/fx-switch.js +5 -3
- package/src/ui/fx-trigger.js +3 -1
- package/src/xpath-evaluation.js +622 -553
- package/src/xpath-util.js +2 -6
- package/dist/fore-all.js +0 -140
- package/dist/fore-debug.js +0 -140
- package/src/.DS_Store +0 -0
- package/src/actions/.DS_Store +0 -0
- package/src/ui/.DS_Store +0 -0
package/src/fx-fore.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {Fore} from './fore.js';
|
|
2
2
|
import './fx-instance.js';
|
|
3
3
|
import './fx-model.js';
|
|
4
4
|
import '@jinntec/jinn-toast';
|
|
5
|
-
import {
|
|
5
|
+
import {evaluateXPathToNodes, evaluateXPathToString} from './xpath-evaluation.js';
|
|
6
6
|
import getInScopeContext from './getInScopeContext.js';
|
|
7
|
-
import {
|
|
7
|
+
import {XPathUtil} from './xpath-util.js';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Main class for Fore.Outermost container element for each Fore application.
|
|
@@ -16,53 +16,56 @@ import { XPathUtil } from './xpath-util.js';
|
|
|
16
16
|
*
|
|
17
17
|
* Main responsiblities are initialization and updating of model and instances, update of UI (refresh) and global messaging.
|
|
18
18
|
*
|
|
19
|
-
*
|
|
19
|
+
* @event compute-exception - dispatched in case the dependency graph is cirular
|
|
20
|
+
* @event refresh-done - dispatched after a refresh() run
|
|
21
|
+
* @event ready - dispatched after Fore has fully been initialized
|
|
22
|
+
* @event error - dispatches error when template expression fails to evaluate
|
|
20
23
|
*
|
|
21
24
|
* @ts-check
|
|
22
25
|
*/
|
|
23
26
|
export class FxFore extends HTMLElement {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
27
|
+
static get properties() {
|
|
28
|
+
return {
|
|
29
|
+
/**
|
|
30
|
+
* Setting this marker attribute will refresh the UI in a lazy fashion just updating elements being
|
|
31
|
+
* in viewport.
|
|
32
|
+
*
|
|
33
|
+
* this feature is still experimental and should be used with caution and extra testing
|
|
34
|
+
*/
|
|
35
|
+
lazyRefresh: {
|
|
36
|
+
type: Boolean,
|
|
37
|
+
},
|
|
38
|
+
model: {
|
|
39
|
+
type: Object,
|
|
40
|
+
},
|
|
41
|
+
ready: {
|
|
42
|
+
type: Boolean,
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* attaches handlers for
|
|
49
|
+
*
|
|
50
|
+
* - `model-construct-done` to trigger the processing of the UI
|
|
51
|
+
* - `message` - to display a message triggered by an fx-message action
|
|
52
|
+
* - `error` - to display an error message
|
|
53
|
+
* - 'compute-exception` - warn about circular dependencies in graph
|
|
54
|
+
*/
|
|
55
|
+
constructor() {
|
|
56
|
+
super();
|
|
57
|
+
this.model = {};
|
|
58
|
+
this.addEventListener('model-construct-done', this._handleModelConstructDone);
|
|
59
|
+
this.addEventListener('message', this._displayMessage);
|
|
60
|
+
this.addEventListener('error', this._displayError);
|
|
61
|
+
window.addEventListener('compute-exception', e => {
|
|
62
|
+
console.error('circular dependency: ', e);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
this.ready = false;
|
|
66
|
+
this.storedTemplateExpressionByNode = new Map();
|
|
67
|
+
|
|
68
|
+
const style = `
|
|
66
69
|
:host {
|
|
67
70
|
// display: none;
|
|
68
71
|
height:auto;
|
|
@@ -73,10 +76,10 @@ export class FxFore extends HTMLElement {
|
|
|
73
76
|
:host ::slotted(fx-model){
|
|
74
77
|
display:none;
|
|
75
78
|
}
|
|
76
|
-
:host(.fx-ready){
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
79
|
+
// :host(.fx-ready){
|
|
80
|
+
// animation: fadein .4s forwards;
|
|
81
|
+
// display:block;
|
|
82
|
+
// }
|
|
80
83
|
|
|
81
84
|
#modalMessage .dialogActions{
|
|
82
85
|
text-align:center;
|
|
@@ -150,7 +153,7 @@ export class FxFore extends HTMLElement {
|
|
|
150
153
|
}
|
|
151
154
|
`;
|
|
152
155
|
|
|
153
|
-
|
|
156
|
+
const html = `
|
|
154
157
|
<jinn-toast id="message" gravity="bottom" position="left"></jinn-toast>
|
|
155
158
|
<jinn-toast id="sticky" gravity="bottom" position="left" duration="-1" close="true" data-class="sticky-message"></jinn-toast>
|
|
156
159
|
<jinn-toast id="error" text="error" duration="-1" data-class="error" close="true" position="left" gravity="bottom"></jinn-toast>
|
|
@@ -164,580 +167,607 @@ export class FxFore extends HTMLElement {
|
|
|
164
167
|
</div>
|
|
165
168
|
`;
|
|
166
169
|
|
|
167
|
-
|
|
168
|
-
|
|
170
|
+
this.attachShadow({mode: 'open'});
|
|
171
|
+
this.shadowRoot.innerHTML = `
|
|
169
172
|
<style>
|
|
170
173
|
${style}
|
|
171
174
|
</style>
|
|
172
175
|
${html}
|
|
173
176
|
`;
|
|
174
177
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
connectedCallback() {
|
|
181
|
-
this.lazyRefresh = this.hasAttribute('refresh-on-view');
|
|
182
|
-
if (this.lazyRefresh) {
|
|
183
|
-
const options = {
|
|
184
|
-
root: null,
|
|
185
|
-
rootMargin: '0px',
|
|
186
|
-
threshold: 0.3,
|
|
187
|
-
};
|
|
188
|
-
this.intersectionObserver = new IntersectionObserver(this.handleIntersect, options);
|
|
178
|
+
this.toRefresh = [];
|
|
179
|
+
this.initialRun = true;
|
|
180
|
+
this.someInstanceDataStructureChanged = false;
|
|
189
181
|
}
|
|
190
182
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
183
|
+
connectedCallback() {
|
|
184
|
+
/*
|
|
185
|
+
document.addEventListener('ready', (e) =>{
|
|
186
|
+
if(e.target !== this){
|
|
187
|
+
// e.preventDefault();
|
|
188
|
+
console.log('>>> e', e);
|
|
189
|
+
console.log('event this', this);
|
|
190
|
+
// console.log('event eventPhase', e.eventPhase);
|
|
191
|
+
// console.log('event cancelable', e.cancelable);
|
|
192
|
+
console.log('event target', e.target);
|
|
193
|
+
console.log('event composed', e.composedPath());
|
|
194
|
+
console.log('<<< event stopping');
|
|
195
|
+
e.stopPropagation();
|
|
196
|
+
}else{
|
|
197
|
+
console.log('event proceed', this);
|
|
198
|
+
}
|
|
199
|
+
// e.stopImmediatePropagation();
|
|
200
|
+
},true);
|
|
201
|
+
*/
|
|
202
|
+
|
|
203
|
+
this.lazyRefresh = this.hasAttribute('refresh-on-view');
|
|
204
|
+
if (this.lazyRefresh) {
|
|
205
|
+
const options = {
|
|
206
|
+
root: null,
|
|
207
|
+
rootMargin: '0px',
|
|
208
|
+
threshold: 0.3,
|
|
209
|
+
};
|
|
210
|
+
this.intersectionObserver = new IntersectionObserver(this.handleIntersect, options);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
this.src = this.hasAttribute('src') ? this.getAttribute('src') : null;
|
|
214
|
+
if (this.src) {
|
|
215
|
+
this._loadFromSrc();
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const slot = this.shadowRoot.querySelector('slot');
|
|
220
|
+
slot.addEventListener('slotchange', event => {
|
|
221
|
+
const children = event.target.assignedElements();
|
|
222
|
+
let modelElement = children.find(
|
|
223
|
+
modelElem => modelElem.nodeName.toUpperCase() === 'FX-MODEL',
|
|
224
|
+
);
|
|
225
|
+
if (!modelElement) {
|
|
226
|
+
const generatedModel = document.createElement('fx-model');
|
|
227
|
+
this.appendChild(generatedModel);
|
|
228
|
+
modelElement = generatedModel;
|
|
229
|
+
}
|
|
230
|
+
if (!modelElement.inited) {
|
|
231
|
+
console.log(
|
|
232
|
+
`########## FORE: kick off processing for ... ${window.location.href} ##########`,
|
|
233
|
+
);
|
|
234
|
+
if (this.src) {
|
|
235
|
+
console.log('########## FORE: loaded from ... ', this.src, '##########');
|
|
236
|
+
}
|
|
237
|
+
modelElement.modelConstruct();
|
|
238
|
+
}
|
|
239
|
+
this.model = modelElement;
|
|
240
|
+
});
|
|
241
|
+
this.addEventListener('path-mutated', e => {
|
|
242
|
+
console.log('path-mutated event received', e.detail.path, e.detail.index);
|
|
243
|
+
this.someInstanceDataStructureChanged = true;
|
|
244
|
+
});
|
|
195
245
|
}
|
|
196
246
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
modelElem => modelElem.nodeName.toUpperCase() === 'FX-MODEL',
|
|
202
|
-
);
|
|
203
|
-
if (!modelElement) {
|
|
204
|
-
const generatedModel = document.createElement('FX-model');
|
|
205
|
-
this.appendChild(generatedModel);
|
|
206
|
-
modelElement = generatedModel;
|
|
207
|
-
}
|
|
208
|
-
if (!modelElement.inited) {
|
|
209
|
-
console.log(
|
|
210
|
-
`########## FORE: kick off processing for ... ${window.location.href} ##########`,
|
|
211
|
-
);
|
|
212
|
-
if(this.src){
|
|
213
|
-
console.log('########## FORE: loaded from ... ', this.src, '##########');
|
|
247
|
+
addToRefresh(modelItem) {
|
|
248
|
+
const found = this.toRefresh.find(mi => mi.path === modelItem.path);
|
|
249
|
+
if (!found) {
|
|
250
|
+
this.toRefresh.push(modelItem);
|
|
214
251
|
}
|
|
215
|
-
modelElement.modelConstruct();
|
|
216
|
-
}
|
|
217
|
-
this.model = modelElement;
|
|
218
|
-
});
|
|
219
|
-
this.addEventListener('path-mutated', (e) =>{
|
|
220
|
-
console.log('path-mutated event received', e.detail.path, e.detail.index);
|
|
221
|
-
this.someInstanceDataStructureChanged = true;
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
addToRefresh(modelItem){
|
|
227
|
-
const found = this.toRefresh.find(mi => mi.path === modelItem.path );
|
|
228
|
-
if(!found){
|
|
229
|
-
this.toRefresh.push(modelItem);
|
|
230
252
|
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* loads a Fore from an URL given by `src`.
|
|
235
|
-
*
|
|
236
|
-
* Will extract the `fx-fore` element from that target file and use and replace current `fx-fore` element with the loaded one.
|
|
237
|
-
* @private
|
|
238
|
-
*/
|
|
239
|
-
_loadFromSrc() {
|
|
240
|
-
console.log('########## loading Fore from ',this.src ,'##########');
|
|
241
|
-
fetch(this.src, {
|
|
242
|
-
method: 'GET',
|
|
243
|
-
mode: 'cors',
|
|
244
|
-
credentials: 'include',
|
|
245
|
-
headers: {
|
|
246
|
-
'Content-Type': 'text/html',
|
|
247
|
-
},
|
|
248
|
-
})
|
|
249
|
-
.then(response => {
|
|
250
|
-
const responseContentType = response.headers.get('content-type').toLowerCase();
|
|
251
|
-
console.log('********** responseContentType *********', responseContentType);
|
|
252
|
-
if (responseContentType.startsWith('text/html')) {
|
|
253
|
-
// const htmlResponse = response.text();
|
|
254
|
-
// return new DOMParser().parseFromString(htmlResponse, 'text/html');
|
|
255
|
-
// return response.text();
|
|
256
|
-
return response.text().then(result =>
|
|
257
|
-
// console.log('xml ********', result);
|
|
258
|
-
new DOMParser().parseFromString(result, 'text/html'),
|
|
259
|
-
);
|
|
260
|
-
}
|
|
261
|
-
return 'done';
|
|
262
|
-
})
|
|
263
|
-
.then(data => {
|
|
264
|
-
// const theFore = fxEvaluateXPathToFirstNode('//fx-fore', data.firstElementChild);
|
|
265
|
-
const theFore = data.querySelector('fx-fore');
|
|
266
253
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
254
|
+
/**
|
|
255
|
+
* loads a Fore from an URL given by `src`.
|
|
256
|
+
*
|
|
257
|
+
* Will extract the `fx-fore` element from that target file and use and replace current `fx-fore` element with the loaded one.
|
|
258
|
+
* @private
|
|
259
|
+
*/
|
|
260
|
+
_loadFromSrc() {
|
|
261
|
+
console.log('########## loading Fore from ', this.src, '##########');
|
|
262
|
+
fetch(this.src, {
|
|
263
|
+
method: 'GET',
|
|
264
|
+
mode: 'cors',
|
|
265
|
+
credentials: 'include',
|
|
266
|
+
headers: {
|
|
267
|
+
'Content-Type': 'text/html',
|
|
268
|
+
},
|
|
273
269
|
})
|
|
274
|
-
|
|
275
|
-
|
|
270
|
+
.then(response => {
|
|
271
|
+
const responseContentType = response.headers.get('content-type').toLowerCase();
|
|
272
|
+
console.log('********** responseContentType *********', responseContentType);
|
|
273
|
+
if (responseContentType.startsWith('text/html')) {
|
|
274
|
+
// const htmlResponse = response.text();
|
|
275
|
+
// return new DOMParser().parseFromString(htmlResponse, 'text/html');
|
|
276
|
+
// return response.text();
|
|
277
|
+
return response.text().then(result =>
|
|
278
|
+
// console.log('xml ********', result);
|
|
279
|
+
new DOMParser().parseFromString(result, 'text/html'),
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
return 'done';
|
|
283
|
+
})
|
|
284
|
+
.then(data => {
|
|
285
|
+
// const theFore = fxEvaluateXPathToFirstNode('//fx-fore', data.firstElementChild);
|
|
286
|
+
const theFore = data.querySelector('fx-fore');
|
|
287
|
+
|
|
288
|
+
// console.log('thefore', theFore)
|
|
289
|
+
if (!theFore) {
|
|
290
|
+
Fore.dispatchEvent(this, 'error', {
|
|
291
|
+
detail: {
|
|
292
|
+
message: `Fore element not found in '${this.src}'. Maybe wrapped within 'template' element?`,
|
|
293
|
+
},
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
theFore.setAttribute('from-src', this.src);
|
|
297
|
+
this.replaceWith(theFore);
|
|
298
|
+
})
|
|
299
|
+
.catch(() => {
|
|
300
|
+
Fore.dispatch(this, 'error', {
|
|
301
|
+
message: `'${this.src}' not found or does not contain Fore element.`,
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* refreshes the UI by using IntersectionObserver API. This is the handler being called
|
|
308
|
+
* by the observer whenever elements come into / move out of viewport.
|
|
309
|
+
* @param entries
|
|
310
|
+
* @param observer
|
|
311
|
+
*/
|
|
312
|
+
handleIntersect(entries, observer) {
|
|
313
|
+
console.time('refreshLazy');
|
|
314
|
+
entries.forEach(entry => {
|
|
315
|
+
const {target} = entry;
|
|
316
|
+
|
|
317
|
+
if (entry.isIntersecting) {
|
|
318
|
+
console.log('in view', entry);
|
|
319
|
+
// console.log('repeat in view entry', entry.target);
|
|
320
|
+
// const target = entry.target;
|
|
321
|
+
// if(target.hasAttribute('refresh-on-view')){
|
|
322
|
+
target.classList.add('loaded');
|
|
323
|
+
// }
|
|
324
|
+
|
|
325
|
+
// todo: too restrictive here? what if target is a usual html element? shouldn't it refresh downwards?
|
|
326
|
+
if (typeof target.refresh === 'function') {
|
|
327
|
+
console.log('refreshing target', target);
|
|
328
|
+
target.refresh(target, true);
|
|
329
|
+
} else {
|
|
330
|
+
console.log('refreshing children', target);
|
|
331
|
+
Fore.refreshChildren(target, true);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
276
334
|
});
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
335
|
+
entries[0].target.getOwnerForm().dispatchEvent(new CustomEvent('refresh-done'));
|
|
336
|
+
|
|
337
|
+
console.timeEnd('refreshLazy');
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
evaluateToNodes(xpath, context) {
|
|
341
|
+
return evaluateXPathToNodes(xpath, context, this);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
disconnectedCallback() {
|
|
345
|
+
/*
|
|
346
|
+
this.removeEventListener('model-construct-done', this._handleModelConstructDone);
|
|
347
|
+
this.removeEventListener('message', this._displayMessage);
|
|
348
|
+
this.removeEventListener('error', this._displayError);
|
|
349
|
+
this.storedTemplateExpressionByNode=null;
|
|
350
|
+
this.shadowRoot = undefined;
|
|
351
|
+
*/
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* refreshes the whole UI by visiting each bound element (having a 'ref' attribute) and applying the state of
|
|
356
|
+
* the bound modelItem to the bound element.
|
|
357
|
+
*
|
|
358
|
+
*
|
|
359
|
+
* force - boolean - if true will refresh all children disregarding toRefresh array
|
|
360
|
+
*
|
|
361
|
+
*/
|
|
362
|
+
async forceRefresh() {
|
|
363
|
+
console.time('refresh');
|
|
364
|
+
console.group('### forced refresh', this);
|
|
365
|
+
|
|
366
|
+
Fore.refreshChildren(this, true);
|
|
367
|
+
this._updateTemplateExpressions();
|
|
368
|
+
this.someInstanceDataStructureChanged = false; // reset
|
|
369
|
+
this._processTemplateExpressions();
|
|
370
|
+
Fore.dispatch(this, 'refresh-done', {});
|
|
371
|
+
|
|
372
|
+
console.groupEnd();
|
|
373
|
+
console.timeEnd('refresh');
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
async refresh(force) {
|
|
377
|
+
// refresh () {
|
|
378
|
+
console.group('### refresh');
|
|
379
|
+
|
|
380
|
+
console.time('refresh');
|
|
381
|
+
|
|
382
|
+
// ### refresh Fore UI elements
|
|
383
|
+
// console.time('refreshChildren');
|
|
384
|
+
console.log('toRefresh', this.toRefresh);
|
|
385
|
+
|
|
386
|
+
// if (!this.initialRun && this.toRefresh.length !== 0) {
|
|
387
|
+
if (!force && !this.initialRun && this.toRefresh.length !== 0) {
|
|
388
|
+
let needsRefresh = false;
|
|
389
|
+
|
|
390
|
+
// ### after recalculation the changed modelItems are copied to 'toRefresh' array for processing
|
|
391
|
+
this.toRefresh.forEach(modelItem => {
|
|
392
|
+
// check if modelItem has boundControls - if so, call refresh() for each of them
|
|
393
|
+
const controlsToRefresh = modelItem.boundControls;
|
|
394
|
+
if (controlsToRefresh) {
|
|
395
|
+
controlsToRefresh.forEach(ctrl => {
|
|
396
|
+
ctrl.refresh();
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// ### check if other controls depend on current modelItem
|
|
401
|
+
const {mainGraph} = this.getModel();
|
|
402
|
+
if (mainGraph && mainGraph.hasNode(modelItem.path)) {
|
|
403
|
+
const deps = this.getModel().mainGraph.dependentsOf(modelItem.path, false);
|
|
404
|
+
// ### iterate dependant modelItems and refresh all their boundControls
|
|
405
|
+
if (deps.length !== 0) {
|
|
406
|
+
deps.forEach(dep => {
|
|
407
|
+
// ### if changed modelItem has a 'facet' path we use the basePath that is the locationPath without facet name
|
|
408
|
+
const basePath = XPathUtil.getBasePath(dep);
|
|
409
|
+
const modelItemOfDep = this.getModel().modelItems.find(mip => mip.path === basePath);
|
|
410
|
+
// ### refresh all boundControls
|
|
411
|
+
modelItemOfDep.boundControls.forEach(control => {
|
|
412
|
+
control.refresh();
|
|
413
|
+
});
|
|
414
|
+
});
|
|
415
|
+
needsRefresh = true;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
this.toRefresh = [];
|
|
420
|
+
if (!needsRefresh) {
|
|
421
|
+
console.log('skipping refresh - no dependants');
|
|
422
|
+
}
|
|
302
423
|
} else {
|
|
303
|
-
|
|
304
|
-
|
|
424
|
+
Fore.refreshChildren(this, true);
|
|
425
|
+
// console.timeEnd('refreshChildren');
|
|
305
426
|
}
|
|
306
|
-
}
|
|
307
|
-
});
|
|
308
|
-
entries[0].target.getOwnerForm().dispatchEvent(new CustomEvent('refresh-done'));
|
|
309
427
|
|
|
310
|
-
|
|
311
|
-
|
|
428
|
+
// ### refresh template expressions
|
|
429
|
+
if (this.initialRun || this.someInstanceDataStructureChanged) {
|
|
430
|
+
this._updateTemplateExpressions();
|
|
431
|
+
this.someInstanceDataStructureChanged = false; // reset
|
|
432
|
+
}
|
|
433
|
+
this._processTemplateExpressions();
|
|
312
434
|
|
|
313
|
-
|
|
314
|
-
return evaluateXPathToNodes(xpath, context, this);
|
|
315
|
-
}
|
|
435
|
+
console.timeEnd('refresh');
|
|
316
436
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
// ### refresh Fore UI elements
|
|
342
|
-
console.time('refreshChildren');
|
|
343
|
-
console.log('toRefresh',this.toRefresh);
|
|
344
|
-
|
|
345
|
-
if(!this.initialRun && this.toRefresh.length !== 0){
|
|
346
|
-
let needsRefresh = false;
|
|
347
|
-
|
|
348
|
-
// ### after recalculation the changed modelItems are copied to 'toRefresh' array for processing
|
|
349
|
-
this.toRefresh.forEach(modelItem => {
|
|
350
|
-
// check if modelItem has boundControls - if so, call refresh() for each of them
|
|
351
|
-
const controlsToRefresh = modelItem.boundControls;
|
|
352
|
-
if(controlsToRefresh){
|
|
353
|
-
controlsToRefresh.forEach(ctrl => {
|
|
354
|
-
ctrl.refresh();
|
|
355
|
-
});
|
|
437
|
+
console.groupEnd();
|
|
438
|
+
// console.log('### <<<<< dispatching refresh-done - end of UI update cycle >>>>>');
|
|
439
|
+
// this.dispatchEvent(new CustomEvent('refresh-done'));
|
|
440
|
+
Fore.dispatch(this, 'refresh-done', {});
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* entry point for processing of template expression enclosed in '{}' brackets.
|
|
445
|
+
*
|
|
446
|
+
* Expressions are found with an XPath search. For each node an entry is added to the storedTemplateExpressionByNode map.
|
|
447
|
+
*
|
|
448
|
+
*
|
|
449
|
+
* @private
|
|
450
|
+
*/
|
|
451
|
+
_updateTemplateExpressions() {
|
|
452
|
+
// Note the fact we're going over HTML here: therefore the `html` prefix.
|
|
453
|
+
const search =
|
|
454
|
+
"(descendant-or-self::*/(text(), @*))[not(ancestor-or-self::fx-model)][matches(.,'\\{.*\\}')]";
|
|
455
|
+
|
|
456
|
+
const tmplExpressions = evaluateXPathToNodes(search, this, this);
|
|
457
|
+
// console.log('template expressions found ', tmplExpressions);
|
|
458
|
+
|
|
459
|
+
if (!this.storedTemplateExpressions) {
|
|
460
|
+
this.storedTemplateExpressions = [];
|
|
356
461
|
}
|
|
357
462
|
|
|
358
|
-
//
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
463
|
+
// console.log('######### storedTemplateExpressions', this.storedTemplateExpressions.length);
|
|
464
|
+
|
|
465
|
+
/*
|
|
466
|
+
storing expressions and their nodes for re-evaluation
|
|
467
|
+
*/
|
|
468
|
+
Array.from(tmplExpressions).forEach(node => {
|
|
469
|
+
if (this.storedTemplateExpressionByNode.has(node)) {
|
|
470
|
+
// If the node is already known, do not process it twice
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
const expr = this._getTemplateExpression(node);
|
|
474
|
+
|
|
475
|
+
// console.log('storedTemplateExpressionByNode', this.storedTemplateExpressionByNode);
|
|
476
|
+
this.storedTemplateExpressionByNode.set(node, expr);
|
|
477
|
+
});
|
|
478
|
+
// console.log('stored template expressions ', this.storedTemplateExpressionByNode);
|
|
479
|
+
|
|
480
|
+
// TODO: Should we clean up nodes that existed but are now gone?
|
|
481
|
+
this._processTemplateExpressions();
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
_processTemplateExpressions() {
|
|
485
|
+
for (const node of this.storedTemplateExpressionByNode.keys()) {
|
|
486
|
+
this._processTemplateExpression({
|
|
487
|
+
node,
|
|
488
|
+
expr: this.storedTemplateExpressionByNode.get(node),
|
|
370
489
|
});
|
|
371
|
-
needsRefresh = true;
|
|
372
|
-
}
|
|
373
490
|
}
|
|
374
|
-
});
|
|
375
|
-
this.toRefresh = [];
|
|
376
|
-
if(!needsRefresh){
|
|
377
|
-
console.log('skipping refresh - no dependants');
|
|
378
|
-
}
|
|
379
|
-
}else{
|
|
380
|
-
Fore.refreshChildren(this, true);
|
|
381
|
-
console.timeEnd('refreshChildren');
|
|
382
491
|
}
|
|
383
492
|
|
|
384
|
-
//
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
console.groupEnd();
|
|
394
|
-
console.log('### <<<<< dispatching refresh-done - end of UI update cycle >>>>>');
|
|
395
|
-
this.dispatchEvent(new CustomEvent('refresh-done'));
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
/**
|
|
399
|
-
* entry point for processing of template expression enclosed in '{}' brackets.
|
|
400
|
-
*
|
|
401
|
-
* Expressions are found with an XPath search. For each node an entry is added to the storedTemplateExpressionByNode map.
|
|
402
|
-
*
|
|
403
|
-
*
|
|
404
|
-
* @private
|
|
405
|
-
*/
|
|
406
|
-
_updateTemplateExpressions() {
|
|
407
|
-
// Note the fact we're going over HTML here: therefore the `html` prefix.
|
|
408
|
-
const search =
|
|
409
|
-
"(descendant-or-self::*/(text(), @*))[matches(.,'\\{.*\\}')] except descendant-or-self::fx-model/descendant-or-self::node()/(., @*)";
|
|
410
|
-
|
|
411
|
-
const tmplExpressions = evaluateXPathToNodes(search, this, this);
|
|
412
|
-
console.log('template expressions found ', tmplExpressions);
|
|
413
|
-
|
|
414
|
-
if (!this.storedTemplateExpressions) {
|
|
415
|
-
this.storedTemplateExpressions = [];
|
|
493
|
+
// eslint-disable-next-line class-methods-use-this
|
|
494
|
+
_processTemplateExpression(exprObj) {
|
|
495
|
+
// console.log('processing template expression ', exprObj);
|
|
496
|
+
|
|
497
|
+
const {expr} = exprObj;
|
|
498
|
+
const {node} = exprObj;
|
|
499
|
+
// console.log('expr ', expr);
|
|
500
|
+
this.evaluateTemplateExpression(expr, node, this);
|
|
416
501
|
}
|
|
417
502
|
|
|
418
|
-
|
|
503
|
+
/**
|
|
504
|
+
* evaluate a template expression (some expression in {} brackets) on a node (either text- or attribute node.
|
|
505
|
+
* @param input The string to parse for expressions
|
|
506
|
+
* @param node the node which will get updated with evaluation result
|
|
507
|
+
* @param form the form element
|
|
508
|
+
*/
|
|
509
|
+
evaluateTemplateExpression(expr, node) {
|
|
510
|
+
const replaced = expr.replace(/{[^}]*}/g, match => {
|
|
511
|
+
if (match === '{}') return match;
|
|
512
|
+
const naked = match.substring(1, match.length - 1);
|
|
513
|
+
const inscope = getInScopeContext(node, naked);
|
|
514
|
+
if (!inscope) {
|
|
515
|
+
const errNode =
|
|
516
|
+
node.nodeType === Node.TEXT_NODE || node.nodeType === Node.ATTRIBUTE_NODE
|
|
517
|
+
? node.parentNode
|
|
518
|
+
: node;
|
|
519
|
+
console.warn('no inscope context for ', errNode);
|
|
520
|
+
return match;
|
|
521
|
+
}
|
|
522
|
+
// Templates are special: they use the namespace configuration from the place where they are
|
|
523
|
+
// being defined
|
|
524
|
+
const instanceId = XPathUtil.getInstanceId(naked);
|
|
525
|
+
const inst = this.getModel().getInstance(instanceId);
|
|
526
|
+
try {
|
|
527
|
+
return evaluateXPathToString(naked, inscope, node, null, inst);
|
|
528
|
+
} catch (error) {
|
|
529
|
+
console.log('ignoring unparseable expr');
|
|
530
|
+
return match;
|
|
531
|
+
}
|
|
532
|
+
});
|
|
419
533
|
|
|
420
|
-
|
|
421
|
-
storing expressions and their nodes for re-evaluation
|
|
422
|
-
*/
|
|
423
|
-
Array.from(tmplExpressions).forEach(node => {
|
|
424
|
-
if (this.storedTemplateExpressionByNode.has(node)) {
|
|
425
|
-
// If the node is already known, do not process it twice
|
|
426
|
-
return;
|
|
427
|
-
}
|
|
428
|
-
const expr = this._getTemplateExpression(node);
|
|
429
|
-
|
|
430
|
-
// console.log('storedTemplateExpressionByNode', this.storedTemplateExpressionByNode);
|
|
431
|
-
this.storedTemplateExpressionByNode.set(node, expr);
|
|
432
|
-
});
|
|
433
|
-
console.log('stored template expressions ', this.storedTemplateExpressionByNode);
|
|
434
|
-
|
|
435
|
-
// TODO: Should we clean up nodes that existed but are now gone?
|
|
436
|
-
this._processTemplateExpressions();
|
|
437
|
-
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
_processTemplateExpressions() {
|
|
441
|
-
for (const node of this.storedTemplateExpressionByNode.keys()) {
|
|
442
|
-
this._processTemplateExpression({
|
|
443
|
-
node,
|
|
444
|
-
expr: this.storedTemplateExpressionByNode.get(node),
|
|
445
|
-
});
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
// eslint-disable-next-line class-methods-use-this
|
|
450
|
-
_processTemplateExpression(exprObj) {
|
|
451
|
-
// console.log('processing template expression ', exprObj);
|
|
452
|
-
|
|
453
|
-
const { expr } = exprObj;
|
|
454
|
-
const { node } = exprObj;
|
|
455
|
-
// console.log('expr ', expr);
|
|
456
|
-
this.evaluateTemplateExpression(expr, node, this);
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
/**
|
|
460
|
-
* evaluate a template expression (some expression in {} brackets) on a node (either text- or attribute node.
|
|
461
|
-
* @param expr the XPath to evaluate
|
|
462
|
-
* @param node the node which will get updated with evaluation result
|
|
463
|
-
* @param form the form element
|
|
464
|
-
*/
|
|
465
|
-
evaluateTemplateExpression(expr, node) {
|
|
466
|
-
if (expr === '{}') return;
|
|
467
|
-
const matches = expr.match(/{[^}]*}/g);
|
|
468
|
-
const namespaceContextNode =
|
|
469
|
-
node.nodeType === node.TEXT_NODE ? node.parentNode : node.ownerElement;
|
|
470
|
-
if (matches) {
|
|
471
|
-
matches.forEach(match => {
|
|
472
|
-
// console.log('match ', match);
|
|
473
|
-
let naked = match.substring(1, match.length - 1);
|
|
474
|
-
const inscope = getInScopeContext(node, naked);
|
|
475
|
-
if (!inscope) {
|
|
476
|
-
const errNode =
|
|
477
|
-
node.nodeType === Node.TEXT_NODE || node.nodeType === Node.ATTRIBUTE_NODE
|
|
478
|
-
? node.parentNode
|
|
479
|
-
: node;
|
|
480
|
-
console.warn('no inscope context for ', errNode);
|
|
481
|
-
return;
|
|
482
|
-
}
|
|
483
|
-
// Templates are special: they use the namespace configuration from the place where they are
|
|
484
|
-
// being defined
|
|
485
|
-
const instanceId = XPathUtil.getInstanceId(naked);
|
|
486
|
-
// console.log('target instance ', instanceId);
|
|
487
|
-
const inst = this.getModel().getInstance(instanceId);
|
|
488
|
-
try {
|
|
489
|
-
const result = evaluateXPathToString(naked, inscope, node, null, inst);
|
|
490
|
-
|
|
491
|
-
// console.log('result of eval ', result);
|
|
492
|
-
const replaced = expr.replaceAll(match, result);
|
|
493
|
-
// console.log('result of replacing ', replaced);
|
|
494
|
-
|
|
495
|
-
if (node.nodeType === Node.ATTRIBUTE_NODE) {
|
|
534
|
+
if (node.nodeType === Node.ATTRIBUTE_NODE) {
|
|
496
535
|
const parent = node.ownerElement;
|
|
497
|
-
|
|
498
|
-
// parent.setAttribute(name, replaced);
|
|
499
536
|
parent.setAttribute(node.nodeName, replaced);
|
|
500
|
-
|
|
537
|
+
} else if (node.nodeType === Node.TEXT_NODE) {
|
|
501
538
|
node.textContent = replaced;
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
if (replaced.includes('{')) {
|
|
505
|
-
// console.log('need to go next round');
|
|
506
|
-
|
|
507
|
-
// todo: duplicated code here - see above
|
|
508
|
-
naked = replaced.substring(1, replaced.length);
|
|
509
|
-
this.evaluateTemplateExpression(replaced, node);
|
|
510
|
-
}
|
|
511
|
-
} catch (error) {
|
|
512
|
-
this.dispatchEvent(new CustomEvent('error', { detail: error }));
|
|
513
539
|
}
|
|
514
|
-
});
|
|
515
540
|
}
|
|
516
|
-
}
|
|
517
541
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
542
|
+
// eslint-disable-next-line class-methods-use-this
|
|
543
|
+
_getTemplateExpression(node) {
|
|
544
|
+
if (node.nodeType === Node.ATTRIBUTE_NODE) {
|
|
545
|
+
return node.value;
|
|
546
|
+
}
|
|
547
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
548
|
+
return node.textContent.trim();
|
|
549
|
+
}
|
|
550
|
+
return null;
|
|
525
551
|
}
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
_handleModelConstructDone() {
|
|
536
|
-
this._initUI();
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
/**
|
|
540
|
-
* If there's no instance element found in a fx-model during init it will construct
|
|
541
|
-
* an instance from UI bindings.
|
|
542
|
-
*
|
|
543
|
-
* @returns {Promise<void>}
|
|
544
|
-
* @private
|
|
545
|
-
*/
|
|
546
|
-
async _lazyCreateInstance() {
|
|
547
|
-
const model = this.querySelector('fx-model');
|
|
548
|
-
if (model.instances.length === 0) {
|
|
549
|
-
console.log('### lazy creation of instance');
|
|
550
|
-
const generatedInstance = document.createElement('fx-instance');
|
|
551
|
-
model.appendChild(generatedInstance);
|
|
552
|
-
|
|
553
|
-
const generated = document.implementation.createDocument(null, 'data', null);
|
|
554
|
-
// const newData = this._generateInstance(this, generated.firstElementChild);
|
|
555
|
-
this._generateInstance(this, generated.firstElementChild);
|
|
556
|
-
generatedInstance.instanceData = generated;
|
|
557
|
-
model.instances.push(generatedInstance);
|
|
558
|
-
console.log('generatedInstance ', this.getModel().getDefaultInstanceData());
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* called when `model-construct-done` event is received to
|
|
555
|
+
* start initing of the UI.
|
|
556
|
+
*
|
|
557
|
+
* @private
|
|
558
|
+
*/
|
|
559
|
+
_handleModelConstructDone() {
|
|
560
|
+
this._initUI();
|
|
559
561
|
}
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* If there's no instance element found in a fx-model during init it will construct
|
|
565
|
+
* an instance from UI bindings.
|
|
566
|
+
*
|
|
567
|
+
* @returns {Promise<void>}
|
|
568
|
+
* @private
|
|
569
|
+
*/
|
|
570
|
+
async _lazyCreateInstance() {
|
|
571
|
+
const model = this.querySelector('fx-model');
|
|
572
|
+
if (model.instances.length === 0) {
|
|
573
|
+
console.log('### lazy creation of instance');
|
|
574
|
+
const generatedInstance = document.createElement('fx-instance');
|
|
575
|
+
model.appendChild(generatedInstance);
|
|
576
|
+
|
|
577
|
+
const generated = document.implementation.createDocument(null, 'data', null);
|
|
578
|
+
// const newData = this._generateInstance(this, generated.firstElementChild);
|
|
579
|
+
this._generateInstance(this, generated.firstElementChild);
|
|
580
|
+
generatedInstance.instanceData = generated;
|
|
581
|
+
model.instances.push(generatedInstance);
|
|
582
|
+
console.log('generatedInstance ', this.getModel().getDefaultInstanceData());
|
|
583
|
+
}
|
|
582
584
|
}
|
|
583
585
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
586
|
+
/**
|
|
587
|
+
* @param {Element} start
|
|
588
|
+
* @param {Element} parent
|
|
589
|
+
*/
|
|
590
|
+
_generateInstance(start, parent) {
|
|
591
|
+
if (start.hasAttribute('ref') && !Fore.isActionElement(start.nodeName)) {
|
|
592
|
+
const ref = start.getAttribute('ref');
|
|
593
|
+
|
|
594
|
+
if (ref.includes('/')) {
|
|
595
|
+
console.log('complex path to create ', ref);
|
|
596
|
+
const steps = ref.split('/');
|
|
597
|
+
steps.forEach(step => {
|
|
598
|
+
console.log('step ', step);
|
|
599
|
+
|
|
600
|
+
// const generated = document.createElement(ref);
|
|
601
|
+
parent = this._generateNode(parent, step, start);
|
|
602
|
+
});
|
|
603
|
+
} else {
|
|
604
|
+
parent = this._generateNode(parent, ref, start);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
if (start.hasChildNodes()) {
|
|
609
|
+
const list = start.children;
|
|
610
|
+
for (let i = 0; i < list.length; i += 1) {
|
|
611
|
+
this._generateInstance(list[i], parent);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
return parent;
|
|
589
615
|
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
616
|
+
|
|
617
|
+
// eslint-disable-next-line class-methods-use-this
|
|
618
|
+
_generateNode(parent, step, start) {
|
|
619
|
+
const generated = parent.ownerDocument.createElement(step);
|
|
620
|
+
if (start.children.length === 0) {
|
|
621
|
+
generated.textContent = start.textContent;
|
|
622
|
+
}
|
|
623
|
+
parent.appendChild(generated);
|
|
624
|
+
parent = generated;
|
|
625
|
+
return parent;
|
|
598
626
|
}
|
|
599
|
-
parent.appendChild(generated);
|
|
600
|
-
parent = generated;
|
|
601
|
-
return parent;
|
|
602
|
-
}
|
|
603
627
|
|
|
604
|
-
|
|
605
|
-
|
|
628
|
+
/*
|
|
629
|
+
_createStep(){
|
|
606
630
|
|
|
607
|
-
|
|
608
|
-
|
|
631
|
+
}
|
|
632
|
+
*/
|
|
609
633
|
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
634
|
+
/*
|
|
635
|
+
_generateInstance(start, parent) {
|
|
636
|
+
if (start.hasAttribute('ref')) {
|
|
637
|
+
const ref = start.getAttribute('ref');
|
|
614
638
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
639
|
+
if(ref.includes('/')){
|
|
640
|
+
console.log('complex path to create ', ref);
|
|
641
|
+
const steps = ref.split('/');
|
|
642
|
+
steps.forEach(step => {
|
|
643
|
+
console.log('step ', step);
|
|
620
644
|
|
|
621
645
|
|
|
622
|
-
|
|
623
|
-
|
|
646
|
+
});
|
|
647
|
+
}
|
|
624
648
|
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
649
|
+
// const generated = document.createElement(ref);
|
|
650
|
+
const generated = parent.ownerDocument.createElement(ref);
|
|
651
|
+
if (start.children.length === 0) {
|
|
652
|
+
generated.textContent = start.textContent;
|
|
653
|
+
}
|
|
654
|
+
parent.appendChild(generated);
|
|
655
|
+
parent = generated;
|
|
629
656
|
}
|
|
630
|
-
parent.appendChild(generated);
|
|
631
|
-
parent = generated;
|
|
632
|
-
}
|
|
633
657
|
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
658
|
+
if (start.hasChildNodes()) {
|
|
659
|
+
const list = start.children;
|
|
660
|
+
for (let i = 0; i < list.length; i += 1) {
|
|
661
|
+
this._generateInstance(list[i], parent);
|
|
662
|
+
}
|
|
638
663
|
}
|
|
664
|
+
return parent;
|
|
639
665
|
}
|
|
640
|
-
|
|
666
|
+
*/
|
|
667
|
+
|
|
668
|
+
/**
|
|
669
|
+
* Start the initialization of the UI by
|
|
670
|
+
*
|
|
671
|
+
* 1. checking if a instance needs to be generated
|
|
672
|
+
* 2. attaching lazy loading intersection observers if `refresh-on-view` attributes are found
|
|
673
|
+
* 3. doing a full refresh of the UI
|
|
674
|
+
*
|
|
675
|
+
* @returns {Promise<void>}
|
|
676
|
+
* @private
|
|
677
|
+
*/
|
|
678
|
+
async _initUI() {
|
|
679
|
+
console.log('### _initUI()');
|
|
680
|
+
if (!this.initialRun) return;
|
|
681
|
+
await this._lazyCreateInstance();
|
|
682
|
+
|
|
683
|
+
// console.log('registering variables!');
|
|
684
|
+
const variables = new Map();
|
|
685
|
+
(function registerVariables(node) {
|
|
686
|
+
for (const child of node.children) {
|
|
687
|
+
if ('setInScopeVariables' in child) {
|
|
688
|
+
child.setInScopeVariables(variables);
|
|
689
|
+
}
|
|
690
|
+
registerVariables(child);
|
|
691
|
+
}
|
|
692
|
+
})(this);
|
|
693
|
+
// console.log('Found variables:', variables);
|
|
694
|
+
|
|
695
|
+
/*
|
|
696
|
+
const options = {
|
|
697
|
+
root: null,
|
|
698
|
+
rootMargin: '0px',
|
|
699
|
+
threshold: 0.3,
|
|
700
|
+
};
|
|
701
|
+
*/
|
|
702
|
+
|
|
703
|
+
await this.refresh();
|
|
704
|
+
// this.style.display='block'
|
|
705
|
+
this.classList.add('fx-ready');
|
|
706
|
+
document.body.classList.add('fx-ready');
|
|
707
|
+
|
|
708
|
+
this.ready = true;
|
|
709
|
+
this.initialRun = false;
|
|
710
|
+
// console.log('### >>>>> dispatching ready >>>>>', this);
|
|
711
|
+
console.log('### modelItems: ', this.getModel().modelItems);
|
|
712
|
+
console.log('### <<<<< FORE: form fully initialized...', this);
|
|
713
|
+
Fore.dispatch(this, 'ready', {});
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
registerLazyElement(element) {
|
|
717
|
+
if (this.intersectionObserver) {
|
|
718
|
+
// console.log('registerLazyElement',element);
|
|
719
|
+
this.intersectionObserver.observe(element);
|
|
641
720
|
}
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
* 1. checking if a instance needs to be generated
|
|
648
|
-
* 2. attaching lazy loading intersection observers if `refresh-on-view` attributes are found
|
|
649
|
-
* 3. doing a full refresh of the UI
|
|
650
|
-
*
|
|
651
|
-
* @returns {Promise<void>}
|
|
652
|
-
* @private
|
|
653
|
-
*/
|
|
654
|
-
async _initUI() {
|
|
655
|
-
console.log('### _initUI()');
|
|
656
|
-
|
|
657
|
-
await this._lazyCreateInstance();
|
|
658
|
-
|
|
659
|
-
console.log('registering variables!');
|
|
660
|
-
const variables = new Map();
|
|
661
|
-
(function registerVariables(node) {
|
|
662
|
-
for (const child of node.children) {
|
|
663
|
-
if ('setInScopeVariables' in child) {
|
|
664
|
-
child.setInScopeVariables(variables);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
unRegisterLazyElement(element) {
|
|
724
|
+
if (this.intersectionObserver) {
|
|
725
|
+
this.intersectionObserver.unobserve(element);
|
|
665
726
|
}
|
|
666
|
-
registerVariables(child);
|
|
667
|
-
}
|
|
668
|
-
})(this);
|
|
669
|
-
console.log('Found variables:', variables);
|
|
670
|
-
|
|
671
|
-
const options = {
|
|
672
|
-
root: null,
|
|
673
|
-
rootMargin: '0px',
|
|
674
|
-
threshold: 0.3,
|
|
675
|
-
};
|
|
676
|
-
|
|
677
|
-
await this.refresh();
|
|
678
|
-
// this.style.display='block'
|
|
679
|
-
this.classList.add('fx-ready');
|
|
680
|
-
document.body.classList.add('fx-ready');
|
|
681
|
-
|
|
682
|
-
this.ready = true;
|
|
683
|
-
this.initialRun = false;
|
|
684
|
-
console.log('### <<<<< dispatching ready >>>>>');
|
|
685
|
-
console.log('########## modelItems: ', this.getModel().modelItems);
|
|
686
|
-
console.log('########## FORE: form fully initialized... ##########');
|
|
687
|
-
this.dispatchEvent(new CustomEvent('ready', {}));
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
registerLazyElement(element) {
|
|
691
|
-
if (this.intersectionObserver) {
|
|
692
|
-
// console.log('registerLazyElement',element);
|
|
693
|
-
this.intersectionObserver.observe(element);
|
|
694
727
|
}
|
|
695
|
-
}
|
|
696
728
|
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
729
|
+
/**
|
|
730
|
+
*
|
|
731
|
+
* @returns {FxModel}
|
|
732
|
+
*/
|
|
733
|
+
getModel() {
|
|
734
|
+
return this.querySelector('fx-model');
|
|
700
735
|
}
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
} else if (level === 'sticky') {
|
|
734
|
-
// const notification = this.$.modeless;
|
|
735
|
-
this.shadowRoot.querySelector('#sticky').showToast(msg);
|
|
736
|
-
} else {
|
|
737
|
-
const toast = this.shadowRoot.querySelector('#message');
|
|
738
|
-
toast.showToast(msg);
|
|
736
|
+
|
|
737
|
+
_displayMessage(e) {
|
|
738
|
+
// console.log('_displayMessage',e);
|
|
739
|
+
const {level} = e.detail;
|
|
740
|
+
const msg = e.detail.message;
|
|
741
|
+
this._showMessage(level, msg);
|
|
742
|
+
e.stopPropagation();
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
_displayError(e) {
|
|
746
|
+
// const { error } = e.detail;
|
|
747
|
+
const msg = e.detail.message;
|
|
748
|
+
// this._showMessage('modal', msg);
|
|
749
|
+
const toast = this.shadowRoot.querySelector('#error');
|
|
750
|
+
toast.showToast(msg);
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
_showMessage(level, msg) {
|
|
754
|
+
if (level === 'modal') {
|
|
755
|
+
// this.$.messageContent.innerText = msg;
|
|
756
|
+
// this.$.modalMessage.open();
|
|
757
|
+
|
|
758
|
+
this.shadowRoot.getElementById('messageContent').innerText = msg;
|
|
759
|
+
// this.shadowRoot.getElementById('modalMessage').open();
|
|
760
|
+
this.shadowRoot.getElementById('modalMessage').classList.add('show');
|
|
761
|
+
} else if (level === 'sticky') {
|
|
762
|
+
// const notification = this.$.modeless;
|
|
763
|
+
this.shadowRoot.querySelector('#sticky').showToast(msg);
|
|
764
|
+
} else {
|
|
765
|
+
const toast = this.shadowRoot.querySelector('#message');
|
|
766
|
+
toast.showToast(msg);
|
|
767
|
+
}
|
|
739
768
|
}
|
|
740
|
-
}
|
|
741
769
|
}
|
|
742
770
|
|
|
743
|
-
customElements.
|
|
771
|
+
if (!customElements.get('fx-fore')) {
|
|
772
|
+
customElements.define('fx-fore', FxFore);
|
|
773
|
+
}
|