@jinntec/fore 1.0.0 → 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 +1 -1
- package/dist/fore-dev.js +8 -8
- package/dist/fore-dev.js.map +1 -1
- package/dist/fore.js +7 -7
- package/dist/fore.js.map +1 -1
- package/index.js +1 -0
- package/package.json +5 -3
- package/resources/fore.css +5 -0
- package/src/ForeElementMixin.js +1 -1
- package/src/actions/abstract-action.js +14 -1
- package/src/actions/fx-confirm.js +2 -2
- package/src/actions/fx-refresh.js +4 -0
- package/src/actions/fx-replace.js +8 -3
- package/src/actions/fx-setfocus.js +37 -0
- package/src/actions/fx-show.js +5 -1
- package/src/fore.js +2 -1
- package/src/functions/fx-function.js +6 -0
- package/src/fx-fore.js +587 -590
- package/src/fx-model.js +32 -4
- package/src/fx-submission.js +40 -8
- package/src/relevance.js +1 -2
- package/src/ui/abstract-control.js +10 -3
- package/src/ui/fx-case.js +1 -2
- package/src/ui/fx-control.js +36 -11
- package/src/ui/fx-inspector.js +9 -6
- package/src/ui/fx-output.js +2 -0
- package/src/ui/fx-switch.js +2 -2
- package/src/xpath-evaluation.js +609 -554
- package/src/xpath-util.js +1 -1
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.
|
|
@@ -24,48 +24,48 @@ import { XPathUtil } from './xpath-util.js';
|
|
|
24
24
|
* @ts-check
|
|
25
25
|
*/
|
|
26
26
|
export class FxFore extends HTMLElement {
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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 = `
|
|
69
69
|
:host {
|
|
70
70
|
// display: none;
|
|
71
71
|
height:auto;
|
|
@@ -153,7 +153,7 @@ export class FxFore extends HTMLElement {
|
|
|
153
153
|
}
|
|
154
154
|
`;
|
|
155
155
|
|
|
156
|
-
|
|
156
|
+
const html = `
|
|
157
157
|
<jinn-toast id="message" gravity="bottom" position="left"></jinn-toast>
|
|
158
158
|
<jinn-toast id="sticky" gravity="bottom" position="left" duration="-1" close="true" data-class="sticky-message"></jinn-toast>
|
|
159
159
|
<jinn-toast id="error" text="error" duration="-1" data-class="error" close="true" position="left" gravity="bottom"></jinn-toast>
|
|
@@ -167,610 +167,607 @@ export class FxFore extends HTMLElement {
|
|
|
167
167
|
</div>
|
|
168
168
|
`;
|
|
169
169
|
|
|
170
|
-
|
|
171
|
-
|
|
170
|
+
this.attachShadow({mode: 'open'});
|
|
171
|
+
this.shadowRoot.innerHTML = `
|
|
172
172
|
<style>
|
|
173
173
|
${style}
|
|
174
174
|
</style>
|
|
175
175
|
${html}
|
|
176
176
|
`;
|
|
177
177
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
}
|
|
182
|
-
|
|
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);
|
|
178
|
+
this.toRefresh = [];
|
|
179
|
+
this.initialRun = true;
|
|
180
|
+
this.someInstanceDataStructureChanged = false;
|
|
211
181
|
}
|
|
212
182
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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
|
+
}
|
|
218
212
|
|
|
219
|
-
|
|
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
|
-
);
|
|
213
|
+
this.src = this.hasAttribute('src') ? this.getAttribute('src') : null;
|
|
234
214
|
if (this.src) {
|
|
235
|
-
|
|
215
|
+
this._loadFromSrc();
|
|
216
|
+
return;
|
|
236
217
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
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
|
+
});
|
|
251
245
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
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
|
-
},
|
|
269
|
-
})
|
|
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
|
-
);
|
|
246
|
+
|
|
247
|
+
addToRefresh(modelItem) {
|
|
248
|
+
const found = this.toRefresh.find(mi => mi.path === modelItem.path);
|
|
249
|
+
if (!found) {
|
|
250
|
+
this.toRefresh.push(modelItem);
|
|
281
251
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
252
|
+
}
|
|
253
|
+
|
|
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',
|
|
293
268
|
},
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
269
|
+
})
|
|
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
|
+
}
|
|
302
334
|
});
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
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
|
+
}
|
|
329
423
|
} else {
|
|
330
|
-
|
|
331
|
-
|
|
424
|
+
Fore.refreshChildren(this, true);
|
|
425
|
+
console.timeEnd('refreshChildren');
|
|
332
426
|
}
|
|
333
|
-
}
|
|
334
|
-
});
|
|
335
|
-
entries[0].target.getOwnerForm().dispatchEvent(new CustomEvent('refresh-done'));
|
|
336
427
|
|
|
337
|
-
|
|
338
|
-
|
|
428
|
+
// ### refresh template expressions
|
|
429
|
+
if (this.initialRun || this.someInstanceDataStructureChanged) {
|
|
430
|
+
this._updateTemplateExpressions();
|
|
431
|
+
this.someInstanceDataStructureChanged = false; // reset
|
|
432
|
+
}
|
|
433
|
+
this._processTemplateExpressions();
|
|
339
434
|
|
|
340
|
-
|
|
341
|
-
return evaluateXPathToNodes(xpath, context, this);
|
|
342
|
-
}
|
|
435
|
+
console.timeEnd('refresh');
|
|
343
436
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
// ### refresh Fore UI elements
|
|
369
|
-
console.time('refreshChildren');
|
|
370
|
-
console.log('toRefresh', this.toRefresh);
|
|
371
|
-
|
|
372
|
-
if (!this.initialRun && this.toRefresh.length !== 0) {
|
|
373
|
-
let needsRefresh = false;
|
|
374
|
-
|
|
375
|
-
// ### after recalculation the changed modelItems are copied to 'toRefresh' array for processing
|
|
376
|
-
this.toRefresh.forEach(modelItem => {
|
|
377
|
-
// check if modelItem has boundControls - if so, call refresh() for each of them
|
|
378
|
-
const controlsToRefresh = modelItem.boundControls;
|
|
379
|
-
if (controlsToRefresh) {
|
|
380
|
-
controlsToRefresh.forEach(ctrl => {
|
|
381
|
-
ctrl.refresh();
|
|
382
|
-
});
|
|
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 = [];
|
|
383
461
|
}
|
|
384
462
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
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),
|
|
399
489
|
});
|
|
400
|
-
needsRefresh = true;
|
|
401
|
-
}
|
|
402
490
|
}
|
|
403
|
-
});
|
|
404
|
-
this.toRefresh = [];
|
|
405
|
-
if (!needsRefresh) {
|
|
406
|
-
console.log('skipping refresh - no dependants');
|
|
407
|
-
}
|
|
408
|
-
} else {
|
|
409
|
-
Fore.refreshChildren(this, true);
|
|
410
|
-
console.timeEnd('refreshChildren');
|
|
411
491
|
}
|
|
412
492
|
|
|
413
|
-
//
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
console.groupEnd();
|
|
423
|
-
// console.log('### <<<<< dispatching refresh-done - end of UI update cycle >>>>>');
|
|
424
|
-
this.dispatchEvent(new CustomEvent('refresh-done'));
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
/**
|
|
428
|
-
* entry point for processing of template expression enclosed in '{}' brackets.
|
|
429
|
-
*
|
|
430
|
-
* Expressions are found with an XPath search. For each node an entry is added to the storedTemplateExpressionByNode map.
|
|
431
|
-
*
|
|
432
|
-
*
|
|
433
|
-
* @private
|
|
434
|
-
*/
|
|
435
|
-
_updateTemplateExpressions() {
|
|
436
|
-
// Note the fact we're going over HTML here: therefore the `html` prefix.
|
|
437
|
-
const search =
|
|
438
|
-
"(descendant-or-self::*/(text(), @*))[matches(.,'\\{.*\\}')] except descendant-or-self::fx-model/descendant-or-self::node()/(., @*)";
|
|
439
|
-
|
|
440
|
-
const tmplExpressions = evaluateXPathToNodes(search, this, this);
|
|
441
|
-
// console.log('template expressions found ', tmplExpressions);
|
|
442
|
-
|
|
443
|
-
if (!this.storedTemplateExpressions) {
|
|
444
|
-
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);
|
|
445
501
|
}
|
|
446
502
|
|
|
447
|
-
|
|
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
|
+
});
|
|
448
533
|
|
|
449
|
-
|
|
450
|
-
storing expressions and their nodes for re-evaluation
|
|
451
|
-
*/
|
|
452
|
-
Array.from(tmplExpressions).forEach(node => {
|
|
453
|
-
if (this.storedTemplateExpressionByNode.has(node)) {
|
|
454
|
-
// If the node is already known, do not process it twice
|
|
455
|
-
return;
|
|
456
|
-
}
|
|
457
|
-
const expr = this._getTemplateExpression(node);
|
|
458
|
-
|
|
459
|
-
// console.log('storedTemplateExpressionByNode', this.storedTemplateExpressionByNode);
|
|
460
|
-
this.storedTemplateExpressionByNode.set(node, expr);
|
|
461
|
-
});
|
|
462
|
-
console.log('stored template expressions ', this.storedTemplateExpressionByNode);
|
|
463
|
-
|
|
464
|
-
// TODO: Should we clean up nodes that existed but are now gone?
|
|
465
|
-
this._processTemplateExpressions();
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
_processTemplateExpressions() {
|
|
469
|
-
for (const node of this.storedTemplateExpressionByNode.keys()) {
|
|
470
|
-
this._processTemplateExpression({
|
|
471
|
-
node,
|
|
472
|
-
expr: this.storedTemplateExpressionByNode.get(node),
|
|
473
|
-
});
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
// eslint-disable-next-line class-methods-use-this
|
|
478
|
-
_processTemplateExpression(exprObj) {
|
|
479
|
-
// console.log('processing template expression ', exprObj);
|
|
480
|
-
|
|
481
|
-
const { expr } = exprObj;
|
|
482
|
-
const { node } = exprObj;
|
|
483
|
-
// console.log('expr ', expr);
|
|
484
|
-
this.evaluateTemplateExpression(expr, node, this);
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
/**
|
|
488
|
-
* evaluate a template expression (some expression in {} brackets) on a node (either text- or attribute node.
|
|
489
|
-
* @param expr the XPath to evaluate
|
|
490
|
-
* @param node the node which will get updated with evaluation result
|
|
491
|
-
* @param form the form element
|
|
492
|
-
*/
|
|
493
|
-
evaluateTemplateExpression(expr, node) {
|
|
494
|
-
if (expr === '{}') return;
|
|
495
|
-
const matches = expr.match(/{[^}]*}/g);
|
|
496
|
-
if (matches) {
|
|
497
|
-
matches.forEach(match => {
|
|
498
|
-
// console.log('match ', match);
|
|
499
|
-
let naked = match.substring(1, match.length - 1);
|
|
500
|
-
const inscope = getInScopeContext(node, naked);
|
|
501
|
-
if (!inscope) {
|
|
502
|
-
const errNode =
|
|
503
|
-
node.nodeType === Node.TEXT_NODE || node.nodeType === Node.ATTRIBUTE_NODE
|
|
504
|
-
? node.parentNode
|
|
505
|
-
: node;
|
|
506
|
-
console.warn('no inscope context for ', errNode);
|
|
507
|
-
return;
|
|
508
|
-
}
|
|
509
|
-
// Templates are special: they use the namespace configuration from the place where they are
|
|
510
|
-
// being defined
|
|
511
|
-
const instanceId = XPathUtil.getInstanceId(naked);
|
|
512
|
-
// console.log('target instance ', instanceId);
|
|
513
|
-
const inst = this.getModel().getInstance(instanceId);
|
|
514
|
-
try {
|
|
515
|
-
const result = evaluateXPathToString(naked, inscope, node, null, inst);
|
|
516
|
-
|
|
517
|
-
// console.log('result of eval ', result);
|
|
518
|
-
const replaced = expr.replaceAll(match, result);
|
|
519
|
-
// console.log('result of replacing ', replaced);
|
|
520
|
-
|
|
521
|
-
if (node.nodeType === Node.ATTRIBUTE_NODE) {
|
|
534
|
+
if (node.nodeType === Node.ATTRIBUTE_NODE) {
|
|
522
535
|
const parent = node.ownerElement;
|
|
523
|
-
|
|
524
|
-
// parent.setAttribute(name, replaced);
|
|
525
536
|
parent.setAttribute(node.nodeName, replaced);
|
|
526
|
-
|
|
537
|
+
} else if (node.nodeType === Node.TEXT_NODE) {
|
|
527
538
|
node.textContent = replaced;
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
if (replaced.includes('{')) {
|
|
531
|
-
// console.log('need to go next round');
|
|
532
|
-
|
|
533
|
-
// todo: duplicated code here - see above
|
|
534
|
-
naked = replaced.substring(1, replaced.length);
|
|
535
|
-
this.evaluateTemplateExpression(replaced, node);
|
|
536
|
-
}
|
|
537
|
-
} catch (error) {
|
|
538
|
-
// console.log(error);
|
|
539
|
-
this.dispatchEvent(new CustomEvent('error', { detail: error }));
|
|
540
539
|
}
|
|
541
|
-
});
|
|
542
540
|
}
|
|
543
|
-
}
|
|
544
541
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
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;
|
|
552
551
|
}
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
_handleModelConstructDone() {
|
|
563
|
-
this._initUI();
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
/**
|
|
567
|
-
* If there's no instance element found in a fx-model during init it will construct
|
|
568
|
-
* an instance from UI bindings.
|
|
569
|
-
*
|
|
570
|
-
* @returns {Promise<void>}
|
|
571
|
-
* @private
|
|
572
|
-
*/
|
|
573
|
-
async _lazyCreateInstance() {
|
|
574
|
-
const model = this.querySelector('fx-model');
|
|
575
|
-
if (model.instances.length === 0) {
|
|
576
|
-
console.log('### lazy creation of instance');
|
|
577
|
-
const generatedInstance = document.createElement('fx-instance');
|
|
578
|
-
model.appendChild(generatedInstance);
|
|
579
|
-
|
|
580
|
-
const generated = document.implementation.createDocument(null, 'data', null);
|
|
581
|
-
// const newData = this._generateInstance(this, generated.firstElementChild);
|
|
582
|
-
this._generateInstance(this, generated.firstElementChild);
|
|
583
|
-
generatedInstance.instanceData = generated;
|
|
584
|
-
model.instances.push(generatedInstance);
|
|
585
|
-
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();
|
|
586
561
|
}
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
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
|
+
}
|
|
609
584
|
}
|
|
610
585
|
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
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;
|
|
616
615
|
}
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
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;
|
|
625
626
|
}
|
|
626
|
-
parent.appendChild(generated);
|
|
627
|
-
parent = generated;
|
|
628
|
-
return parent;
|
|
629
|
-
}
|
|
630
627
|
|
|
631
|
-
|
|
632
|
-
|
|
628
|
+
/*
|
|
629
|
+
_createStep(){
|
|
633
630
|
|
|
634
|
-
|
|
635
|
-
|
|
631
|
+
}
|
|
632
|
+
*/
|
|
636
633
|
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
634
|
+
/*
|
|
635
|
+
_generateInstance(start, parent) {
|
|
636
|
+
if (start.hasAttribute('ref')) {
|
|
637
|
+
const ref = start.getAttribute('ref');
|
|
641
638
|
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
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);
|
|
647
644
|
|
|
648
645
|
|
|
649
|
-
|
|
650
|
-
|
|
646
|
+
});
|
|
647
|
+
}
|
|
651
648
|
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
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;
|
|
656
656
|
}
|
|
657
|
-
parent.appendChild(generated);
|
|
658
|
-
parent = generated;
|
|
659
|
-
}
|
|
660
657
|
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
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
|
+
}
|
|
665
663
|
}
|
|
664
|
+
return parent;
|
|
666
665
|
}
|
|
667
|
-
|
|
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);
|
|
668
720
|
}
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
* 1. checking if a instance needs to be generated
|
|
675
|
-
* 2. attaching lazy loading intersection observers if `refresh-on-view` attributes are found
|
|
676
|
-
* 3. doing a full refresh of the UI
|
|
677
|
-
*
|
|
678
|
-
* @returns {Promise<void>}
|
|
679
|
-
* @private
|
|
680
|
-
*/
|
|
681
|
-
async _initUI() {
|
|
682
|
-
console.log('### _initUI()');
|
|
683
|
-
if (!this.initialRun) return;
|
|
684
|
-
await this._lazyCreateInstance();
|
|
685
|
-
|
|
686
|
-
// console.log('registering variables!');
|
|
687
|
-
const variables = new Map();
|
|
688
|
-
(function registerVariables(node) {
|
|
689
|
-
for (const child of node.children) {
|
|
690
|
-
if ('setInScopeVariables' in child) {
|
|
691
|
-
child.setInScopeVariables(variables);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
unRegisterLazyElement(element) {
|
|
724
|
+
if (this.intersectionObserver) {
|
|
725
|
+
this.intersectionObserver.unobserve(element);
|
|
692
726
|
}
|
|
693
|
-
|
|
694
|
-
}
|
|
695
|
-
})(this);
|
|
696
|
-
console.log('Found variables:', variables);
|
|
727
|
+
}
|
|
697
728
|
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
*/
|
|
705
|
-
|
|
706
|
-
await this.refresh();
|
|
707
|
-
// this.style.display='block'
|
|
708
|
-
this.classList.add('fx-ready');
|
|
709
|
-
document.body.classList.add('fx-ready');
|
|
710
|
-
|
|
711
|
-
this.ready = true;
|
|
712
|
-
this.initialRun = false;
|
|
713
|
-
console.log('### >>>>> dispatching ready >>>>>', this);
|
|
714
|
-
console.log('modelItems: ', this.getModel().modelItems);
|
|
715
|
-
console.log('### <<<<< FORE: form fully initialized...', this);
|
|
716
|
-
Fore.dispatch(this, 'ready', {});
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
registerLazyElement(element) {
|
|
720
|
-
if (this.intersectionObserver) {
|
|
721
|
-
// console.log('registerLazyElement',element);
|
|
722
|
-
this.intersectionObserver.observe(element);
|
|
729
|
+
/**
|
|
730
|
+
*
|
|
731
|
+
* @returns {FxModel}
|
|
732
|
+
*/
|
|
733
|
+
getModel() {
|
|
734
|
+
return this.querySelector('fx-model');
|
|
723
735
|
}
|
|
724
|
-
}
|
|
725
736
|
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
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);
|
|
729
751
|
}
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
_displayError(e) {
|
|
749
|
-
// const { error } = e.detail;
|
|
750
|
-
const msg = e.detail.message;
|
|
751
|
-
// this._showMessage('modal', msg);
|
|
752
|
-
const toast = this.shadowRoot.querySelector('#error');
|
|
753
|
-
toast.showToast(msg);
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
_showMessage(level, msg) {
|
|
757
|
-
if (level === 'modal') {
|
|
758
|
-
// this.$.messageContent.innerText = msg;
|
|
759
|
-
// this.$.modalMessage.open();
|
|
760
|
-
|
|
761
|
-
this.shadowRoot.getElementById('messageContent').innerText = msg;
|
|
762
|
-
// this.shadowRoot.getElementById('modalMessage').open();
|
|
763
|
-
this.shadowRoot.getElementById('modalMessage').classList.add('show');
|
|
764
|
-
} else if (level === 'sticky') {
|
|
765
|
-
// const notification = this.$.modeless;
|
|
766
|
-
this.shadowRoot.querySelector('#sticky').showToast(msg);
|
|
767
|
-
} else {
|
|
768
|
-
const toast = this.shadowRoot.querySelector('#message');
|
|
769
|
-
toast.showToast(msg);
|
|
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
|
+
}
|
|
770
768
|
}
|
|
771
|
-
}
|
|
772
769
|
}
|
|
773
770
|
|
|
774
771
|
if (!customElements.get('fx-fore')) {
|
|
775
|
-
|
|
772
|
+
customElements.define('fx-fore', FxFore);
|
|
776
773
|
}
|