@jinntec/fore 1.0.0-4 → 1.1.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 +26 -38
- 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 +4 -0
- package/package.json +42 -42
- package/resources/fore.css +186 -0
- package/resources/toastify.css +87 -0
- package/resources/{fore-styles.css → vars.css} +0 -292
- package/src/DependencyNotifyingDomFacade.js +10 -16
- package/src/ForeElementMixin.js +26 -19
- package/src/actions/abstract-action.js +30 -9
- package/src/actions/fx-action.js +6 -4
- 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 +10 -6
- package/src/actions/fx-insert.js +49 -39
- package/src/actions/fx-message.js +3 -1
- package/src/actions/fx-refresh.js +15 -1
- package/src/actions/fx-replace.js +73 -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 +15 -10
- package/src/actions/fx-update.js +3 -1
- package/src/dep_graph.js +4 -2
- package/src/drawdown.js +67 -82
- package/src/fore.js +158 -12
- package/src/functions/fx-function.js +17 -3
- package/src/fx-bind.js +42 -202
- package/src/fx-fore.js +599 -567
- package/src/fx-header.js +3 -1
- package/src/fx-instance.js +9 -1
- package/src/fx-model.js +59 -23
- package/src/fx-submission.js +106 -48
- package/src/fx-var.js +7 -4
- package/src/getInScopeContext.js +37 -11
- package/src/modelitem.js +4 -4
- package/src/relevance.js +64 -0
- package/src/ui/abstract-control.js +64 -37
- package/src/ui/fx-alert.js +7 -1
- package/src/ui/fx-case.js +4 -3
- package/src/ui/fx-container.js +7 -1
- package/src/ui/fx-control.js +309 -34
- package/src/ui/fx-dialog.js +54 -40
- package/src/ui/fx-group.js +3 -1
- package/src/ui/fx-hint.js +4 -1
- package/src/ui/fx-inspector.js +120 -17
- package/src/ui/fx-items.js +8 -8
- package/src/ui/fx-output.js +16 -5
- package/src/ui/fx-repeat.js +33 -41
- 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 +621 -576
- package/src/xpath-util.js +15 -8
- 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,8 +153,9 @@ 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>
|
|
158
|
+
<jinn-toast id="sticky" gravity="bottom" position="left" duration="-1" close="true" data-class="sticky-message"></jinn-toast>
|
|
155
159
|
<jinn-toast id="error" text="error" duration="-1" data-class="error" close="true" position="left" gravity="bottom"></jinn-toast>
|
|
156
160
|
<slot></slot>
|
|
157
161
|
<div id="modalMessage" class="overlay">
|
|
@@ -163,579 +167,607 @@ export class FxFore extends HTMLElement {
|
|
|
163
167
|
</div>
|
|
164
168
|
`;
|
|
165
169
|
|
|
166
|
-
|
|
167
|
-
|
|
170
|
+
this.attachShadow({mode: 'open'});
|
|
171
|
+
this.shadowRoot.innerHTML = `
|
|
168
172
|
<style>
|
|
169
173
|
${style}
|
|
170
174
|
</style>
|
|
171
175
|
${html}
|
|
172
176
|
`;
|
|
173
177
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
connectedCallback() {
|
|
180
|
-
this.lazyRefresh = this.hasAttribute('refresh-on-view');
|
|
181
|
-
if (this.lazyRefresh) {
|
|
182
|
-
const options = {
|
|
183
|
-
root: null,
|
|
184
|
-
rootMargin: '0px',
|
|
185
|
-
threshold: 0.3,
|
|
186
|
-
};
|
|
187
|
-
this.intersectionObserver = new IntersectionObserver(this.handleIntersect, options);
|
|
178
|
+
this.toRefresh = [];
|
|
179
|
+
this.initialRun = true;
|
|
180
|
+
this.someInstanceDataStructureChanged = false;
|
|
188
181
|
}
|
|
189
182
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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
|
+
});
|
|
194
245
|
}
|
|
195
246
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
modelElem => modelElem.nodeName.toUpperCase() === 'FX-MODEL',
|
|
201
|
-
);
|
|
202
|
-
if (!modelElement) {
|
|
203
|
-
const generatedModel = document.createElement('FX-model');
|
|
204
|
-
this.appendChild(generatedModel);
|
|
205
|
-
modelElement = generatedModel;
|
|
206
|
-
}
|
|
207
|
-
if (!modelElement.inited) {
|
|
208
|
-
console.log(
|
|
209
|
-
`########## FORE: kick off processing for ... ${window.location.href} ##########`,
|
|
210
|
-
);
|
|
211
|
-
if(this.src){
|
|
212
|
-
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);
|
|
213
251
|
}
|
|
214
|
-
modelElement.modelConstruct();
|
|
215
|
-
}
|
|
216
|
-
this.model = modelElement;
|
|
217
|
-
});
|
|
218
|
-
this.addEventListener('path-mutated', (e) =>{
|
|
219
|
-
console.log('path-mutated event received', e.detail.path, e.detail.index);
|
|
220
|
-
this.someInstanceDataStructureChanged = true;
|
|
221
|
-
});
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
addToRefresh(modelItem){
|
|
226
|
-
const found = this.toRefresh.find(mi => mi.path === modelItem.path );
|
|
227
|
-
if(!found){
|
|
228
|
-
this.toRefresh.push(modelItem);
|
|
229
252
|
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* loads a Fore from an URL given by `src`.
|
|
234
|
-
*
|
|
235
|
-
* Will extract the `fx-fore` element from that target file and use and replace current `fx-fore` element with the loaded one.
|
|
236
|
-
* @private
|
|
237
|
-
*/
|
|
238
|
-
_loadFromSrc() {
|
|
239
|
-
console.log('########## loading Fore from ',this.src ,'##########');
|
|
240
|
-
fetch(this.src, {
|
|
241
|
-
method: 'GET',
|
|
242
|
-
mode: 'cors',
|
|
243
|
-
credentials: 'include',
|
|
244
|
-
headers: {
|
|
245
|
-
'Content-Type': 'text/html',
|
|
246
|
-
},
|
|
247
|
-
})
|
|
248
|
-
.then(response => {
|
|
249
|
-
const responseContentType = response.headers.get('content-type').toLowerCase();
|
|
250
|
-
console.log('********** responseContentType *********', responseContentType);
|
|
251
|
-
if (responseContentType.startsWith('text/html')) {
|
|
252
|
-
// const htmlResponse = response.text();
|
|
253
|
-
// return new DOMParser().parseFromString(htmlResponse, 'text/html');
|
|
254
|
-
// return response.text();
|
|
255
|
-
return response.text().then(result =>
|
|
256
|
-
// console.log('xml ********', result);
|
|
257
|
-
new DOMParser().parseFromString(result, 'text/html'),
|
|
258
|
-
);
|
|
259
|
-
}
|
|
260
|
-
return 'done';
|
|
261
|
-
})
|
|
262
|
-
.then(data => {
|
|
263
|
-
// const theFore = fxEvaluateXPathToFirstNode('//fx-fore', data.firstElementChild);
|
|
264
|
-
const theFore = data.querySelector('fx-fore');
|
|
265
253
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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
|
+
},
|
|
272
269
|
})
|
|
273
|
-
|
|
274
|
-
|
|
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
|
+
}
|
|
275
334
|
});
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
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 (!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
|
+
}
|
|
301
423
|
} else {
|
|
302
|
-
|
|
303
|
-
|
|
424
|
+
Fore.refreshChildren(this, true);
|
|
425
|
+
console.timeEnd('refreshChildren');
|
|
304
426
|
}
|
|
305
|
-
}
|
|
306
|
-
});
|
|
307
|
-
entries[0].target.getOwnerForm().dispatchEvent(new CustomEvent('refresh-done'));
|
|
308
427
|
|
|
309
|
-
|
|
310
|
-
|
|
428
|
+
// ### refresh template expressions
|
|
429
|
+
if (this.initialRun || this.someInstanceDataStructureChanged) {
|
|
430
|
+
this._updateTemplateExpressions();
|
|
431
|
+
this.someInstanceDataStructureChanged = false; // reset
|
|
432
|
+
}
|
|
433
|
+
this._processTemplateExpressions();
|
|
311
434
|
|
|
312
|
-
|
|
313
|
-
return evaluateXPathToNodes(xpath, context, this);
|
|
314
|
-
}
|
|
435
|
+
console.timeEnd('refresh');
|
|
315
436
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
// ### refresh Fore UI elements
|
|
341
|
-
console.time('refreshChildren');
|
|
342
|
-
console.log('toRefresh',this.toRefresh);
|
|
343
|
-
|
|
344
|
-
if(!this.initialRun && this.toRefresh.length !== 0){
|
|
345
|
-
let needsRefresh = false;
|
|
346
|
-
|
|
347
|
-
// ### after recalculation the changed modelItems are copied to 'toRefresh' array for processing
|
|
348
|
-
this.toRefresh.forEach(modelItem => {
|
|
349
|
-
// check if modelItem has boundControls - if so, call refresh() for each of them
|
|
350
|
-
const controlsToRefresh = modelItem.boundControls;
|
|
351
|
-
if(controlsToRefresh){
|
|
352
|
-
controlsToRefresh.forEach(ctrl => {
|
|
353
|
-
ctrl.refresh();
|
|
354
|
-
});
|
|
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 = [];
|
|
355
461
|
}
|
|
356
462
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
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),
|
|
369
489
|
});
|
|
370
|
-
needsRefresh = true;
|
|
371
|
-
}
|
|
372
490
|
}
|
|
373
|
-
});
|
|
374
|
-
this.toRefresh = [];
|
|
375
|
-
if(!needsRefresh){
|
|
376
|
-
console.log('skipping refresh - no dependants');
|
|
377
|
-
}
|
|
378
|
-
}else{
|
|
379
|
-
Fore.refreshChildren(this, true);
|
|
380
|
-
console.timeEnd('refreshChildren');
|
|
381
491
|
}
|
|
382
492
|
|
|
383
|
-
//
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
console.groupEnd();
|
|
393
|
-
console.log('### <<<<< dispatching refresh-done - end of UI update cycle >>>>>');
|
|
394
|
-
this.dispatchEvent(new CustomEvent('refresh-done'));
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
/**
|
|
398
|
-
* entry point for processing of template expression enclosed in '{}' brackets.
|
|
399
|
-
*
|
|
400
|
-
* Expressions are found with an XPath search. For each node an entry is added to the storedTemplateExpressionByNode map.
|
|
401
|
-
*
|
|
402
|
-
*
|
|
403
|
-
* @private
|
|
404
|
-
*/
|
|
405
|
-
_updateTemplateExpressions() {
|
|
406
|
-
// Note the fact we're going over HTML here: therefore the `html` prefix.
|
|
407
|
-
const search =
|
|
408
|
-
"(descendant-or-self::*/(text(), @*))[matches(.,'\\{.*\\}')] except descendant-or-self::fx-model/descendant-or-self::node()/(., @*)";
|
|
409
|
-
|
|
410
|
-
const tmplExpressions = evaluateXPathToNodes(search, this, this);
|
|
411
|
-
console.log('template expressions found ', tmplExpressions);
|
|
412
|
-
|
|
413
|
-
if (!this.storedTemplateExpressions) {
|
|
414
|
-
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);
|
|
415
501
|
}
|
|
416
502
|
|
|
417
|
-
|
|
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
|
+
});
|
|
418
533
|
|
|
419
|
-
|
|
420
|
-
storing expressions and their nodes for re-evaluation
|
|
421
|
-
*/
|
|
422
|
-
Array.from(tmplExpressions).forEach(node => {
|
|
423
|
-
if (this.storedTemplateExpressionByNode.has(node)) {
|
|
424
|
-
// If the node is already known, do not process it twice
|
|
425
|
-
return;
|
|
426
|
-
}
|
|
427
|
-
const expr = this._getTemplateExpression(node);
|
|
428
|
-
|
|
429
|
-
// console.log('storedTemplateExpressionByNode', this.storedTemplateExpressionByNode);
|
|
430
|
-
this.storedTemplateExpressionByNode.set(node, expr);
|
|
431
|
-
});
|
|
432
|
-
console.log('stored template expressions ', this.storedTemplateExpressionByNode);
|
|
433
|
-
|
|
434
|
-
// TODO: Should we clean up nodes that existed but are now gone?
|
|
435
|
-
this._processTemplateExpressions();
|
|
436
|
-
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
_processTemplateExpressions() {
|
|
440
|
-
for (const node of this.storedTemplateExpressionByNode.keys()) {
|
|
441
|
-
this._processTemplateExpression({
|
|
442
|
-
node,
|
|
443
|
-
expr: this.storedTemplateExpressionByNode.get(node),
|
|
444
|
-
});
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
// eslint-disable-next-line class-methods-use-this
|
|
449
|
-
_processTemplateExpression(exprObj) {
|
|
450
|
-
// console.log('processing template expression ', exprObj);
|
|
451
|
-
|
|
452
|
-
const { expr } = exprObj;
|
|
453
|
-
const { node } = exprObj;
|
|
454
|
-
// console.log('expr ', expr);
|
|
455
|
-
this.evaluateTemplateExpression(expr, node, this);
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
/**
|
|
459
|
-
* evaluate a template expression (some expression in {} brackets) on a node (either text- or attribute node.
|
|
460
|
-
* @param expr the XPath to evaluate
|
|
461
|
-
* @param node the node which will get updated with evaluation result
|
|
462
|
-
* @param form the form element
|
|
463
|
-
*/
|
|
464
|
-
evaluateTemplateExpression(expr, node) {
|
|
465
|
-
if (expr === '{}') return;
|
|
466
|
-
const matches = expr.match(/{[^}]*}/g);
|
|
467
|
-
const namespaceContextNode =
|
|
468
|
-
node.nodeType === node.TEXT_NODE ? node.parentNode : node.ownerElement;
|
|
469
|
-
if (matches) {
|
|
470
|
-
matches.forEach(match => {
|
|
471
|
-
// console.log('match ', match);
|
|
472
|
-
let naked = match.substring(1, match.length - 1);
|
|
473
|
-
const inscope = getInScopeContext(node, naked);
|
|
474
|
-
if (!inscope) {
|
|
475
|
-
const errNode =
|
|
476
|
-
node.nodeType === Node.TEXT_NODE || node.nodeType === Node.ATTRIBUTE_NODE
|
|
477
|
-
? node.parentNode
|
|
478
|
-
: node;
|
|
479
|
-
console.warn('no inscope context for ', errNode);
|
|
480
|
-
return;
|
|
481
|
-
}
|
|
482
|
-
// Templates are special: they use the namespace configuration from the place where they are
|
|
483
|
-
// being defined
|
|
484
|
-
const instanceId = XPathUtil.getInstanceId(naked);
|
|
485
|
-
// console.log('target instance ', instanceId);
|
|
486
|
-
const inst = this.getModel().getInstance(instanceId);
|
|
487
|
-
try {
|
|
488
|
-
const result = evaluateXPathToString(naked, inscope, node, null, inst);
|
|
489
|
-
|
|
490
|
-
// console.log('result of eval ', result);
|
|
491
|
-
const replaced = expr.replaceAll(match, result);
|
|
492
|
-
// console.log('result of replacing ', replaced);
|
|
493
|
-
|
|
494
|
-
if (node.nodeType === Node.ATTRIBUTE_NODE) {
|
|
534
|
+
if (node.nodeType === Node.ATTRIBUTE_NODE) {
|
|
495
535
|
const parent = node.ownerElement;
|
|
496
|
-
|
|
497
|
-
// parent.setAttribute(name, replaced);
|
|
498
536
|
parent.setAttribute(node.nodeName, replaced);
|
|
499
|
-
|
|
537
|
+
} else if (node.nodeType === Node.TEXT_NODE) {
|
|
500
538
|
node.textContent = replaced;
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
if (replaced.includes('{')) {
|
|
504
|
-
// console.log('need to go next round');
|
|
505
|
-
|
|
506
|
-
// todo: duplicated code here - see above
|
|
507
|
-
naked = replaced.substring(1, replaced.length);
|
|
508
|
-
this.evaluateTemplateExpression(replaced, node);
|
|
509
|
-
}
|
|
510
|
-
} catch (error) {
|
|
511
|
-
this.dispatchEvent(new CustomEvent('error', { detail: error }));
|
|
512
539
|
}
|
|
513
|
-
});
|
|
514
540
|
}
|
|
515
|
-
}
|
|
516
541
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
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;
|
|
524
551
|
}
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
_handleModelConstructDone() {
|
|
535
|
-
this._initUI();
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
/**
|
|
539
|
-
* If there's no instance element found in a fx-model during init it will construct
|
|
540
|
-
* an instance from UI bindings.
|
|
541
|
-
*
|
|
542
|
-
* @returns {Promise<void>}
|
|
543
|
-
* @private
|
|
544
|
-
*/
|
|
545
|
-
async _lazyCreateInstance() {
|
|
546
|
-
const model = this.querySelector('fx-model');
|
|
547
|
-
if (model.instances.length === 0) {
|
|
548
|
-
console.log('### lazy creation of instance');
|
|
549
|
-
const generatedInstance = document.createElement('fx-instance');
|
|
550
|
-
model.appendChild(generatedInstance);
|
|
551
|
-
|
|
552
|
-
const generated = document.implementation.createDocument(null, 'data', null);
|
|
553
|
-
// const newData = this._generateInstance(this, generated.firstElementChild);
|
|
554
|
-
this._generateInstance(this, generated.firstElementChild);
|
|
555
|
-
generatedInstance.instanceData = generated;
|
|
556
|
-
model.instances.push(generatedInstance);
|
|
557
|
-
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();
|
|
558
561
|
}
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
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
|
+
}
|
|
581
584
|
}
|
|
582
585
|
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
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;
|
|
588
615
|
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
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;
|
|
597
626
|
}
|
|
598
|
-
parent.appendChild(generated);
|
|
599
|
-
parent = generated;
|
|
600
|
-
return parent;
|
|
601
|
-
}
|
|
602
627
|
|
|
603
|
-
|
|
604
|
-
|
|
628
|
+
/*
|
|
629
|
+
_createStep(){
|
|
605
630
|
|
|
606
|
-
|
|
607
|
-
|
|
631
|
+
}
|
|
632
|
+
*/
|
|
608
633
|
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
634
|
+
/*
|
|
635
|
+
_generateInstance(start, parent) {
|
|
636
|
+
if (start.hasAttribute('ref')) {
|
|
637
|
+
const ref = start.getAttribute('ref');
|
|
613
638
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
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);
|
|
619
644
|
|
|
620
645
|
|
|
621
|
-
|
|
622
|
-
|
|
646
|
+
});
|
|
647
|
+
}
|
|
623
648
|
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
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;
|
|
628
656
|
}
|
|
629
|
-
parent.appendChild(generated);
|
|
630
|
-
parent = generated;
|
|
631
|
-
}
|
|
632
657
|
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
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
|
+
}
|
|
637
663
|
}
|
|
664
|
+
return parent;
|
|
638
665
|
}
|
|
639
|
-
|
|
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);
|
|
640
720
|
}
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
* 1. checking if a instance needs to be generated
|
|
647
|
-
* 2. attaching lazy loading intersection observers if `refresh-on-view` attributes are found
|
|
648
|
-
* 3. doing a full refresh of the UI
|
|
649
|
-
*
|
|
650
|
-
* @returns {Promise<void>}
|
|
651
|
-
* @private
|
|
652
|
-
*/
|
|
653
|
-
async _initUI() {
|
|
654
|
-
console.log('### _initUI()');
|
|
655
|
-
|
|
656
|
-
await this._lazyCreateInstance();
|
|
657
|
-
|
|
658
|
-
console.log('registering variables!');
|
|
659
|
-
const variables = new Map();
|
|
660
|
-
(function registerVariables(node) {
|
|
661
|
-
for (const child of node.children) {
|
|
662
|
-
if ('setInScopeVariables' in child) {
|
|
663
|
-
child.setInScopeVariables(variables);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
unRegisterLazyElement(element) {
|
|
724
|
+
if (this.intersectionObserver) {
|
|
725
|
+
this.intersectionObserver.unobserve(element);
|
|
664
726
|
}
|
|
665
|
-
registerVariables(child);
|
|
666
|
-
}
|
|
667
|
-
})(this);
|
|
668
|
-
console.log('Found variables:', variables);
|
|
669
|
-
|
|
670
|
-
const options = {
|
|
671
|
-
root: null,
|
|
672
|
-
rootMargin: '0px',
|
|
673
|
-
threshold: 0.3,
|
|
674
|
-
};
|
|
675
|
-
|
|
676
|
-
await this.refresh();
|
|
677
|
-
// this.style.display='block'
|
|
678
|
-
this.classList.add('fx-ready');
|
|
679
|
-
|
|
680
|
-
this.ready = true;
|
|
681
|
-
this.initialRun = false;
|
|
682
|
-
console.log('### <<<<< dispatching ready >>>>>');
|
|
683
|
-
console.log('########## modelItems: ', this.getModel().modelItems);
|
|
684
|
-
console.log('########## FORE: form fully initialized... ##########');
|
|
685
|
-
this.dispatchEvent(new CustomEvent('ready', {}));
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
registerLazyElement(element) {
|
|
689
|
-
if (this.intersectionObserver) {
|
|
690
|
-
// console.log('registerLazyElement',element);
|
|
691
|
-
this.intersectionObserver.observe(element);
|
|
692
727
|
}
|
|
693
|
-
}
|
|
694
728
|
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
729
|
+
/**
|
|
730
|
+
*
|
|
731
|
+
* @returns {FxModel}
|
|
732
|
+
*/
|
|
733
|
+
getModel() {
|
|
734
|
+
return this.querySelector('fx-model');
|
|
698
735
|
}
|
|
699
|
-
|
|
700
|
-
|
|
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
|
-
} else if (level === 'modeless') {
|
|
732
|
-
// const notification = this.$.modeless;
|
|
733
|
-
this.shadowRoot.querySelector('#message').showToast(msg);
|
|
734
|
-
} else {
|
|
735
|
-
const toast = this.shadowRoot.querySelector('#message');
|
|
736
|
-
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
|
+
}
|
|
737
768
|
}
|
|
738
|
-
}
|
|
739
769
|
}
|
|
740
770
|
|
|
741
|
-
customElements.
|
|
771
|
+
if (!customElements.get('fx-fore')) {
|
|
772
|
+
customElements.define('fx-fore', FxFore);
|
|
773
|
+
}
|