@jinntec/fore 1.0.0-1 → 1.0.0-4
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 +20 -1
- package/dist/fore-all.js +10 -10
- package/dist/fore-debug.js +140 -0
- package/index.js +8 -0
- package/package.json +7 -6
- package/resources/{vars.css → fore-styles.css} +292 -0
- package/src/DependencyNotifyingDomFacade.js +9 -1
- package/src/ForeElementMixin.js +25 -17
- package/src/actions/abstract-action.js +8 -7
- package/src/actions/fx-action.js +6 -1
- package/src/actions/fx-dispatch.js +10 -2
- package/src/actions/fx-hide.js +23 -0
- package/src/actions/fx-insert.js +9 -9
- package/src/actions/fx-send.js +1 -1
- package/src/actions/fx-setvalue.js +2 -4
- package/src/actions/fx-show.js +23 -0
- package/src/dep_graph.js +9 -0
- package/src/drawdown.js +172 -0
- package/src/fore.js +56 -3
- package/src/fx-bind.js +20 -17
- package/src/fx-fore.js +297 -33
- package/src/fx-instance.js +27 -6
- package/src/fx-model.js +182 -37
- package/src/fx-submission.js +29 -7
- package/src/fx-var.js +43 -0
- package/src/getInScopeContext.js +28 -7
- package/src/modelitem.js +1 -0
- package/src/ui/abstract-control.js +17 -4
- package/src/ui/fx-alert.js +16 -20
- package/src/ui/fx-container.js +9 -4
- package/src/ui/fx-control.js +76 -39
- package/src/ui/fx-dialog.js +68 -0
- package/src/ui/fx-inspector.js +44 -0
- package/src/ui/fx-items.js +120 -0
- package/src/ui/fx-output.js +50 -12
- package/src/ui/fx-repeat.js +83 -16
- package/src/ui/fx-repeatitem.js +11 -4
- package/src/ui/fx-trigger.js +3 -0
- package/src/xpath-evaluation.js +372 -135
- package/src/xpath-util.js +52 -12
- package/dist/fore.js +0 -2
- package/resources/fore.css +0 -86
- package/resources/toastify.css +0 -87
package/src/fx-fore.js
CHANGED
|
@@ -4,24 +4,34 @@ import './fx-model.js';
|
|
|
4
4
|
import '@jinntec/jinn-toast';
|
|
5
5
|
import { evaluateXPathToNodes, evaluateXPathToString } from './xpath-evaluation.js';
|
|
6
6
|
import getInScopeContext from './getInScopeContext.js';
|
|
7
|
+
import { XPathUtil } from './xpath-util.js';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Main class for Fore.Outermost container element for each Fore application.
|
|
10
11
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
12
|
* Root element for Fore. Kicks off initialization and displays messages.
|
|
14
13
|
*
|
|
15
14
|
* fx-fore is the outermost container for each form. A form can have exactly one model
|
|
16
15
|
* with arbitrary number of instances.
|
|
17
16
|
*
|
|
18
|
-
* Main responsiblities are initialization of model, update of UI (refresh) and global messaging
|
|
17
|
+
* Main responsiblities are initialization and updating of model and instances, update of UI (refresh) and global messaging.
|
|
18
|
+
*
|
|
19
|
+
*
|
|
19
20
|
*
|
|
20
21
|
* @ts-check
|
|
21
22
|
*/
|
|
22
23
|
export class FxFore extends HTMLElement {
|
|
23
24
|
static get properties() {
|
|
24
25
|
return {
|
|
26
|
+
/**
|
|
27
|
+
* Setting this marker attribute will refresh the UI in a lazy fashion just updating elements being
|
|
28
|
+
* in viewport.
|
|
29
|
+
*
|
|
30
|
+
* this feature is still experimental and should be used with caution and extra testing
|
|
31
|
+
*/
|
|
32
|
+
lazyRefresh: {
|
|
33
|
+
type: Boolean,
|
|
34
|
+
},
|
|
25
35
|
model: {
|
|
26
36
|
type: Object,
|
|
27
37
|
},
|
|
@@ -31,6 +41,14 @@ export class FxFore extends HTMLElement {
|
|
|
31
41
|
};
|
|
32
42
|
}
|
|
33
43
|
|
|
44
|
+
/**
|
|
45
|
+
* attaches handlers for
|
|
46
|
+
*
|
|
47
|
+
* - `model-construct-done` to trigger the processing of the UI
|
|
48
|
+
* - `message` - to display a message triggered by an fx-message action
|
|
49
|
+
* - `error` - to display an error message
|
|
50
|
+
* - 'compute-exception` - warn about circular dependencies in graph
|
|
51
|
+
*/
|
|
34
52
|
constructor() {
|
|
35
53
|
super();
|
|
36
54
|
this.model = {};
|
|
@@ -42,12 +60,11 @@ export class FxFore extends HTMLElement {
|
|
|
42
60
|
});
|
|
43
61
|
|
|
44
62
|
this.ready = false;
|
|
45
|
-
|
|
46
63
|
this.storedTemplateExpressionByNode = new Map();
|
|
47
64
|
|
|
48
65
|
const style = `
|
|
49
66
|
:host {
|
|
50
|
-
display:
|
|
67
|
+
// display: none;
|
|
51
68
|
height:auto;
|
|
52
69
|
padding:var(--model-element-padding);
|
|
53
70
|
font-family:Roboto, sans-serif;
|
|
@@ -56,6 +73,11 @@ export class FxFore extends HTMLElement {
|
|
|
56
73
|
:host ::slotted(fx-model){
|
|
57
74
|
display:none;
|
|
58
75
|
}
|
|
76
|
+
:host(.fx-ready){
|
|
77
|
+
animation: fadein .4s forwards;
|
|
78
|
+
display:block;
|
|
79
|
+
}
|
|
80
|
+
|
|
59
81
|
#modalMessage .dialogActions{
|
|
60
82
|
text-align:center;
|
|
61
83
|
}
|
|
@@ -118,6 +140,14 @@ export class FxFore extends HTMLElement {
|
|
|
118
140
|
#messageContent{
|
|
119
141
|
margin-top:40px;
|
|
120
142
|
}
|
|
143
|
+
@keyframes fadein {
|
|
144
|
+
0% {
|
|
145
|
+
opacity:0;
|
|
146
|
+
}
|
|
147
|
+
100% {
|
|
148
|
+
opacity:1;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
121
151
|
`;
|
|
122
152
|
|
|
123
153
|
const html = `
|
|
@@ -140,9 +170,29 @@ export class FxFore extends HTMLElement {
|
|
|
140
170
|
</style>
|
|
141
171
|
${html}
|
|
142
172
|
`;
|
|
173
|
+
|
|
174
|
+
this.toRefresh = [];
|
|
175
|
+
this.initialRun = true;
|
|
176
|
+
this.someInstanceDataStructureChanged = false;
|
|
143
177
|
}
|
|
144
178
|
|
|
145
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);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
this.src = this.hasAttribute('src')? this.getAttribute('src'):null;
|
|
191
|
+
if(this.src){
|
|
192
|
+
this._loadFromSrc();
|
|
193
|
+
return ;
|
|
194
|
+
}
|
|
195
|
+
|
|
146
196
|
const slot = this.shadowRoot.querySelector('slot');
|
|
147
197
|
slot.addEventListener('slotchange', event => {
|
|
148
198
|
const children = event.target.assignedElements();
|
|
@@ -155,18 +205,123 @@ export class FxFore extends HTMLElement {
|
|
|
155
205
|
modelElement = generatedModel;
|
|
156
206
|
}
|
|
157
207
|
if (!modelElement.inited) {
|
|
158
|
-
console.log(
|
|
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, '##########');
|
|
213
|
+
}
|
|
159
214
|
modelElement.modelConstruct();
|
|
160
215
|
}
|
|
161
216
|
this.model = modelElement;
|
|
162
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
|
+
}
|
|
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
|
+
|
|
266
|
+
// console.log('thefore', theFore)
|
|
267
|
+
if(!theFore){
|
|
268
|
+
this.dispatchEvent(new CustomEvent('error',{detail:{message: `Fore element not found in '${this.src}'. Maybe wrapped within 'template' element?`}}));
|
|
269
|
+
}
|
|
270
|
+
theFore.setAttribute('from-src', this.src);
|
|
271
|
+
this.replaceWith(theFore);
|
|
272
|
+
})
|
|
273
|
+
.catch(error => {
|
|
274
|
+
this.dispatchEvent(new CustomEvent('error',{detail:{message: `'${this.src}' not found or does not contain Fore element.`}}));
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* refreshes the UI by using IntersectionObserver API. This is the handler being called
|
|
280
|
+
* by the observer whenever elements come into / move out of viewport.
|
|
281
|
+
* @param entries
|
|
282
|
+
* @param observer
|
|
283
|
+
*/
|
|
284
|
+
handleIntersect(entries, observer) {
|
|
285
|
+
console.time('refreshLazy');
|
|
286
|
+
entries.forEach(entry => {
|
|
287
|
+
const { target } = entry;
|
|
288
|
+
|
|
289
|
+
if (entry.isIntersecting) {
|
|
290
|
+
console.log('in view', entry);
|
|
291
|
+
// console.log('repeat in view entry', entry.target);
|
|
292
|
+
// const target = entry.target;
|
|
293
|
+
// if(target.hasAttribute('refresh-on-view')){
|
|
294
|
+
target.classList.add('loaded');
|
|
295
|
+
// }
|
|
296
|
+
|
|
297
|
+
// todo: too restrictive here? what if target is a usual html element? shouldn't it refresh downwards?
|
|
298
|
+
if (typeof target.refresh === 'function') {
|
|
299
|
+
console.log('refreshing target', target);
|
|
300
|
+
target.refresh(target, true);
|
|
301
|
+
} else {
|
|
302
|
+
console.log('refreshing children', target);
|
|
303
|
+
Fore.refreshChildren(target, true);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
entries[0].target.getOwnerForm().dispatchEvent(new CustomEvent('refresh-done'));
|
|
308
|
+
|
|
309
|
+
console.timeEnd('refreshLazy');
|
|
163
310
|
}
|
|
164
311
|
|
|
165
312
|
evaluateToNodes(xpath, context) {
|
|
166
313
|
return evaluateXPathToNodes(xpath, context, this);
|
|
167
314
|
}
|
|
168
315
|
|
|
169
|
-
disconnectedCallback() {
|
|
316
|
+
disconnectedCallback() {
|
|
317
|
+
/*
|
|
318
|
+
this.removeEventListener('model-construct-done', this._handleModelConstructDone);
|
|
319
|
+
this.removeEventListener('message', this._displayMessage);
|
|
320
|
+
this.removeEventListener('error', this._displayError);
|
|
321
|
+
this.storedTemplateExpressionByNode=null;
|
|
322
|
+
this.shadowRoot = undefined;
|
|
323
|
+
*/
|
|
324
|
+
}
|
|
170
325
|
|
|
171
326
|
/**
|
|
172
327
|
* refreshes the whole UI by visiting each bound element (having a 'ref' attribute) and applying the state of
|
|
@@ -176,16 +331,63 @@ export class FxFore extends HTMLElement {
|
|
|
176
331
|
* AVT:
|
|
177
332
|
*
|
|
178
333
|
*/
|
|
179
|
-
async refresh() {
|
|
334
|
+
async refresh(force) {
|
|
180
335
|
// refresh () {
|
|
181
336
|
console.group('### refresh');
|
|
182
|
-
// await this.updateComplete;
|
|
183
337
|
|
|
184
|
-
|
|
185
|
-
|
|
338
|
+
console.time('refresh');
|
|
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
|
+
});
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// ### check if other controls depend on current modelItem
|
|
358
|
+
const mainGraph = this.getModel().mainGraph;
|
|
359
|
+
if(mainGraph && mainGraph.hasNode(modelItem.path)){
|
|
360
|
+
const deps = this.getModel().mainGraph.dependentsOf(modelItem.path, false);
|
|
361
|
+
// ### iterate dependant modelItems and refresh all their boundControls
|
|
362
|
+
if(deps.length !== 0){
|
|
363
|
+
deps.forEach(dep => {
|
|
364
|
+
// ### if changed modelItem has a 'facet' path we use the basePath that is the locationPath without facet name
|
|
365
|
+
const basePath = XPathUtil.getBasePath(dep);
|
|
366
|
+
const modelItemOfDep = this.getModel().modelItems.find(mip => mip.path === basePath);
|
|
367
|
+
// ### refresh all boundControls
|
|
368
|
+
modelItemOfDep.boundControls.forEach(control =>{control.refresh()});
|
|
369
|
+
});
|
|
370
|
+
needsRefresh = true;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
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
|
+
}
|
|
186
382
|
|
|
187
383
|
// ### refresh template expressions
|
|
188
|
-
this.
|
|
384
|
+
if(this.initialRun || this.someInstanceDataStructureChanged){
|
|
385
|
+
this._updateTemplateExpressions();
|
|
386
|
+
this.someInstanceDataStructureChanged = false; //reset
|
|
387
|
+
}
|
|
388
|
+
this._processTemplateExpressions();
|
|
389
|
+
|
|
390
|
+
console.timeEnd('refresh');
|
|
189
391
|
|
|
190
392
|
console.groupEnd();
|
|
191
393
|
console.log('### <<<<< dispatching refresh-done - end of UI update cycle >>>>>');
|
|
@@ -203,14 +405,20 @@ export class FxFore extends HTMLElement {
|
|
|
203
405
|
_updateTemplateExpressions() {
|
|
204
406
|
// Note the fact we're going over HTML here: therefore the `html` prefix.
|
|
205
407
|
const search =
|
|
206
|
-
"(descendant-or-self::*/(text(), @*))[matches(.,'\\{.*\\}')] except descendant-or-self::
|
|
408
|
+
"(descendant-or-self::*/(text(), @*))[matches(.,'\\{.*\\}')] except descendant-or-self::fx-model/descendant-or-self::node()/(., @*)";
|
|
207
409
|
|
|
208
410
|
const tmplExpressions = evaluateXPathToNodes(search, this, this);
|
|
209
411
|
console.log('template expressions found ', tmplExpressions);
|
|
210
412
|
|
|
413
|
+
if (!this.storedTemplateExpressions) {
|
|
414
|
+
this.storedTemplateExpressions = [];
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
console.log('######### storedTemplateExpressions', this.storedTemplateExpressions.length);
|
|
418
|
+
|
|
211
419
|
/*
|
|
212
|
-
|
|
213
|
-
|
|
420
|
+
storing expressions and their nodes for re-evaluation
|
|
421
|
+
*/
|
|
214
422
|
Array.from(tmplExpressions).forEach(node => {
|
|
215
423
|
if (this.storedTemplateExpressionByNode.has(node)) {
|
|
216
424
|
// If the node is already known, do not process it twice
|
|
@@ -218,21 +426,26 @@ export class FxFore extends HTMLElement {
|
|
|
218
426
|
}
|
|
219
427
|
const expr = this._getTemplateExpression(node);
|
|
220
428
|
|
|
429
|
+
// console.log('storedTemplateExpressionByNode', this.storedTemplateExpressionByNode);
|
|
221
430
|
this.storedTemplateExpressionByNode.set(node, expr);
|
|
222
431
|
});
|
|
432
|
+
console.log('stored template expressions ', this.storedTemplateExpressionByNode);
|
|
223
433
|
|
|
224
434
|
// TODO: Should we clean up nodes that existed but are now gone?
|
|
435
|
+
this._processTemplateExpressions();
|
|
436
|
+
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
_processTemplateExpressions() {
|
|
225
440
|
for (const node of this.storedTemplateExpressionByNode.keys()) {
|
|
226
441
|
this._processTemplateExpression({
|
|
227
442
|
node,
|
|
228
443
|
expr: this.storedTemplateExpressionByNode.get(node),
|
|
229
444
|
});
|
|
230
445
|
}
|
|
231
|
-
|
|
232
|
-
console.log('stored template expressions ', this.storedTemplateExpressionByNode);
|
|
233
446
|
}
|
|
234
447
|
|
|
235
|
-
|
|
448
|
+
// eslint-disable-next-line class-methods-use-this
|
|
236
449
|
_processTemplateExpression(exprObj) {
|
|
237
450
|
// console.log('processing template expression ', exprObj);
|
|
238
451
|
|
|
@@ -268,13 +481,15 @@ export class FxFore extends HTMLElement {
|
|
|
268
481
|
}
|
|
269
482
|
// Templates are special: they use the namespace configuration from the place where they are
|
|
270
483
|
// being defined
|
|
271
|
-
|
|
484
|
+
const instanceId = XPathUtil.getInstanceId(naked);
|
|
485
|
+
// console.log('target instance ', instanceId);
|
|
486
|
+
const inst = this.getModel().getInstance(instanceId);
|
|
272
487
|
try {
|
|
273
|
-
const result = evaluateXPathToString(naked, inscope, node, null,
|
|
488
|
+
const result = evaluateXPathToString(naked, inscope, node, null, inst);
|
|
274
489
|
|
|
275
490
|
// console.log('result of eval ', result);
|
|
276
491
|
const replaced = expr.replaceAll(match, result);
|
|
277
|
-
console.log('result of replacing ', replaced);
|
|
492
|
+
// console.log('result of replacing ', replaced);
|
|
278
493
|
|
|
279
494
|
if (node.nodeType === Node.ATTRIBUTE_NODE) {
|
|
280
495
|
const parent = node.ownerElement;
|
|
@@ -286,7 +501,7 @@ export class FxFore extends HTMLElement {
|
|
|
286
501
|
}
|
|
287
502
|
|
|
288
503
|
if (replaced.includes('{')) {
|
|
289
|
-
console.log('need to go next round');
|
|
504
|
+
// console.log('need to go next round');
|
|
290
505
|
|
|
291
506
|
// todo: duplicated code here - see above
|
|
292
507
|
naked = replaced.substring(1, replaced.length);
|
|
@@ -305,25 +520,28 @@ export class FxFore extends HTMLElement {
|
|
|
305
520
|
return node.value;
|
|
306
521
|
}
|
|
307
522
|
if (node.nodeType === Node.TEXT_NODE) {
|
|
308
|
-
return node.textContent;
|
|
523
|
+
return node.textContent.trim();
|
|
309
524
|
}
|
|
310
525
|
return null;
|
|
311
526
|
}
|
|
312
527
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
}
|
|
320
|
-
});
|
|
321
|
-
}
|
|
322
|
-
|
|
528
|
+
/**
|
|
529
|
+
* called when `model-construct-done` event is received to
|
|
530
|
+
* start initing of the UI.
|
|
531
|
+
*
|
|
532
|
+
* @private
|
|
533
|
+
*/
|
|
323
534
|
_handleModelConstructDone() {
|
|
324
535
|
this._initUI();
|
|
325
536
|
}
|
|
326
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
|
+
*/
|
|
327
545
|
async _lazyCreateInstance() {
|
|
328
546
|
const model = this.querySelector('fx-model');
|
|
329
547
|
if (model.instances.length === 0) {
|
|
@@ -422,18 +640,64 @@ export class FxFore extends HTMLElement {
|
|
|
422
640
|
}
|
|
423
641
|
*/
|
|
424
642
|
|
|
643
|
+
/**
|
|
644
|
+
* Start the initialization of the UI by
|
|
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
|
+
*/
|
|
425
653
|
async _initUI() {
|
|
426
654
|
console.log('### _initUI()');
|
|
427
655
|
|
|
428
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);
|
|
664
|
+
}
|
|
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
|
+
|
|
429
676
|
await this.refresh();
|
|
677
|
+
// this.style.display='block'
|
|
678
|
+
this.classList.add('fx-ready');
|
|
679
|
+
|
|
430
680
|
this.ready = true;
|
|
681
|
+
this.initialRun = false;
|
|
431
682
|
console.log('### <<<<< dispatching ready >>>>>');
|
|
432
683
|
console.log('########## modelItems: ', this.getModel().modelItems);
|
|
433
684
|
console.log('########## FORE: form fully initialized... ##########');
|
|
434
685
|
this.dispatchEvent(new CustomEvent('ready', {}));
|
|
435
686
|
}
|
|
436
687
|
|
|
688
|
+
registerLazyElement(element) {
|
|
689
|
+
if (this.intersectionObserver) {
|
|
690
|
+
// console.log('registerLazyElement',element);
|
|
691
|
+
this.intersectionObserver.observe(element);
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
unRegisterLazyElement(element) {
|
|
696
|
+
if (this.intersectionObserver) {
|
|
697
|
+
this.intersectionObserver.unobserve(element);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
|
|
437
701
|
/**
|
|
438
702
|
*
|
|
439
703
|
* @returns {FxModel}
|
package/src/fx-instance.js
CHANGED
|
@@ -144,6 +144,7 @@ export class FxInstance extends HTMLElement {
|
|
|
144
144
|
|
|
145
145
|
_createInstanceData() {
|
|
146
146
|
if (this.type === 'xml') {
|
|
147
|
+
// const doc = new DOMParser().parseFromString('<data data-id="default"></data>', 'application/xml');
|
|
147
148
|
const doc = new DOMParser().parseFromString('<data></data>', 'application/xml');
|
|
148
149
|
this.instanceData = doc;
|
|
149
150
|
}
|
|
@@ -167,7 +168,19 @@ export class FxInstance extends HTMLElement {
|
|
|
167
168
|
.then(response => {
|
|
168
169
|
const responseContentType = response.headers.get('content-type').toLowerCase();
|
|
169
170
|
console.log('********** responseContentType *********', responseContentType);
|
|
170
|
-
if (responseContentType.startsWith('text/
|
|
171
|
+
if (responseContentType.startsWith('text/html')) {
|
|
172
|
+
// const htmlResponse = response.text();
|
|
173
|
+
// return new DOMParser().parseFromString(htmlResponse, 'text/html');
|
|
174
|
+
// return response.text();
|
|
175
|
+
return response.text().then(result =>
|
|
176
|
+
// console.log('xml ********', result);
|
|
177
|
+
new DOMParser().parseFromString(result, 'text/html'),
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
if (
|
|
181
|
+
responseContentType.startsWith('text/plain') ||
|
|
182
|
+
responseContentType.startsWith('text/markdown')
|
|
183
|
+
) {
|
|
171
184
|
// console.log("********** inside res plain *********");
|
|
172
185
|
return response.text();
|
|
173
186
|
}
|
|
@@ -176,16 +189,20 @@ export class FxInstance extends HTMLElement {
|
|
|
176
189
|
return response.json();
|
|
177
190
|
}
|
|
178
191
|
if (responseContentType.startsWith('application/xml')) {
|
|
179
|
-
return response.text().then(result =>
|
|
180
|
-
console.log('xml ********', result);
|
|
181
|
-
|
|
182
|
-
|
|
192
|
+
return response.text().then(result =>
|
|
193
|
+
// console.log('xml ********', result);
|
|
194
|
+
new DOMParser().parseFromString(result, 'application/xml'),
|
|
195
|
+
);
|
|
183
196
|
}
|
|
184
197
|
return 'done';
|
|
185
198
|
})
|
|
186
199
|
.then(data => {
|
|
200
|
+
if (data.nodeType) {
|
|
201
|
+
this.instanceData = data;
|
|
202
|
+
console.log('instanceData loaded: ', this.instanceData);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
187
205
|
this.instanceData = data;
|
|
188
|
-
console.log('instanceData loaded: ', this.instanceData);
|
|
189
206
|
})
|
|
190
207
|
.catch(error => {
|
|
191
208
|
throw new Error(`failed loading data ${error}`);
|
|
@@ -218,6 +235,10 @@ export class FxInstance extends HTMLElement {
|
|
|
218
235
|
// todo: move innerHTML out to shadowDOM (for later reset)
|
|
219
236
|
} else if (this.type === 'json') {
|
|
220
237
|
this.instanceData = JSON.parse(this.textContent);
|
|
238
|
+
} else if (this.type === 'html') {
|
|
239
|
+
this.instanceData = this.firstElementChild.children;
|
|
240
|
+
} else if (this.type === 'text') {
|
|
241
|
+
this.instanceData = this.textContent;
|
|
221
242
|
} else {
|
|
222
243
|
console.warn('unknow type for data ', this.type);
|
|
223
244
|
}
|