@jinntec/fore 0.25.0 → 1.0.0-2
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 +75 -22
- package/dist/fore-all.js +11 -11
- package/dist/fore.js +1 -1
- package/index.js +5 -1
- package/package.json +17 -6
- package/resources/fore.css +121 -4
- package/resources/toastify.css +3 -1
- package/src/DependencyNotifyingDomFacade.js +9 -1
- package/src/ForeElementMixin.js +83 -12
- package/src/actions/abstract-action.js +101 -27
- package/src/actions/fx-action.js +4 -2
- package/src/actions/fx-append.js +21 -18
- package/src/actions/fx-confirm.js +22 -0
- package/src/actions/fx-dispatch.js +10 -2
- package/src/actions/fx-insert.js +35 -30
- package/src/actions/fx-message.js +7 -1
- package/src/actions/fx-send.js +1 -1
- package/src/actions/fx-setvalue.js +2 -9
- package/src/dep_graph.js +9 -0
- package/src/drawdown.js +172 -0
- package/src/fore.js +126 -18
- package/src/functions/fx-function.js +2 -2
- package/src/fx-bind.js +11 -7
- package/src/fx-fore.js +283 -67
- package/src/fx-header.js +20 -0
- package/src/fx-instance.js +54 -10
- package/src/fx-model.js +175 -38
- package/src/fx-submission.js +235 -53
- package/src/getInScopeContext.js +2 -3
- package/src/ui/abstract-control.js +23 -44
- package/src/ui/fx-alert.js +20 -19
- package/src/ui/fx-container.js +9 -4
- package/src/ui/fx-control.js +92 -37
- package/src/ui/fx-inspector.js +44 -0
- package/src/ui/fx-items.js +104 -20
- package/src/ui/fx-output.js +92 -3
- package/src/ui/fx-repeat.js +87 -80
- package/src/ui/fx-repeatitem.js +38 -48
- package/src/ui/fx-trigger.js +49 -27
- package/src/xpath-evaluation.js +533 -235
- package/src/xpath-util.js +50 -12
package/src/fx-fore.js
CHANGED
|
@@ -2,21 +2,36 @@ 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 { evaluateXPathToNodes,
|
|
5
|
+
import { evaluateXPathToNodes, evaluateXPathToString } from './xpath-evaluation.js';
|
|
6
|
+
import getInScopeContext from './getInScopeContext.js';
|
|
7
|
+
import { XPathUtil } from './xpath-util.js';
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
|
-
*
|
|
10
|
+
* Main class for Fore.Outermost container element for each Fore application.
|
|
11
|
+
*
|
|
12
|
+
* Root element for Fore. Kicks off initialization and displays messages.
|
|
9
13
|
*
|
|
10
14
|
* fx-fore is the outermost container for each form. A form can have exactly one model
|
|
11
15
|
* with arbitrary number of instances.
|
|
12
16
|
*
|
|
13
|
-
* 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
|
+
*
|
|
14
20
|
*
|
|
15
21
|
* @ts-check
|
|
16
22
|
*/
|
|
17
23
|
export class FxFore extends HTMLElement {
|
|
18
24
|
static get properties() {
|
|
19
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
|
+
},
|
|
20
35
|
model: {
|
|
21
36
|
type: Object,
|
|
22
37
|
},
|
|
@@ -26,6 +41,14 @@ export class FxFore extends HTMLElement {
|
|
|
26
41
|
};
|
|
27
42
|
}
|
|
28
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
|
+
*/
|
|
29
52
|
constructor() {
|
|
30
53
|
super();
|
|
31
54
|
this.model = {};
|
|
@@ -37,10 +60,11 @@ export class FxFore extends HTMLElement {
|
|
|
37
60
|
});
|
|
38
61
|
|
|
39
62
|
this.ready = false;
|
|
63
|
+
this.storedTemplateExpressionByNode = new Map();
|
|
40
64
|
|
|
41
65
|
const style = `
|
|
42
66
|
:host {
|
|
43
|
-
display:
|
|
67
|
+
display: none;
|
|
44
68
|
height:auto;
|
|
45
69
|
padding:var(--model-element-padding);
|
|
46
70
|
font-family:Roboto, sans-serif;
|
|
@@ -49,6 +73,11 @@ export class FxFore extends HTMLElement {
|
|
|
49
73
|
:host ::slotted(fx-model){
|
|
50
74
|
display:none;
|
|
51
75
|
}
|
|
76
|
+
:host(.fx-ready){
|
|
77
|
+
animation: fadein .4s forwards;
|
|
78
|
+
display:block;
|
|
79
|
+
}
|
|
80
|
+
|
|
52
81
|
#modalMessage .dialogActions{
|
|
53
82
|
text-align:center;
|
|
54
83
|
}
|
|
@@ -111,12 +140,19 @@ export class FxFore extends HTMLElement {
|
|
|
111
140
|
#messageContent{
|
|
112
141
|
margin-top:40px;
|
|
113
142
|
}
|
|
114
|
-
|
|
115
|
-
|
|
143
|
+
@keyframes fadein {
|
|
144
|
+
0% {
|
|
145
|
+
opacity:0;
|
|
146
|
+
}
|
|
147
|
+
100% {
|
|
148
|
+
opacity:1;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
116
151
|
`;
|
|
117
152
|
|
|
118
153
|
const html = `
|
|
119
154
|
<jinn-toast id="message" gravity="bottom" position="left"></jinn-toast>
|
|
155
|
+
<jinn-toast id="error" text="error" duration="-1" data-class="error" close="true" position="left" gravity="bottom"></jinn-toast>
|
|
120
156
|
<slot></slot>
|
|
121
157
|
<div id="modalMessage" class="overlay">
|
|
122
158
|
<div class="popup">
|
|
@@ -137,6 +173,16 @@ export class FxFore extends HTMLElement {
|
|
|
137
173
|
}
|
|
138
174
|
|
|
139
175
|
connectedCallback() {
|
|
176
|
+
this.lazyRefresh = this.hasAttribute('refresh-on-view');
|
|
177
|
+
if (this.lazyRefresh) {
|
|
178
|
+
const options = {
|
|
179
|
+
root: null,
|
|
180
|
+
rootMargin: '0px',
|
|
181
|
+
threshold: 0.3,
|
|
182
|
+
};
|
|
183
|
+
this.intersectionObserver = new IntersectionObserver(this.handleIntersect, options);
|
|
184
|
+
}
|
|
185
|
+
|
|
140
186
|
const slot = this.shadowRoot.querySelector('slot');
|
|
141
187
|
slot.addEventListener('slotchange', event => {
|
|
142
188
|
const children = event.target.assignedElements();
|
|
@@ -149,18 +195,62 @@ export class FxFore extends HTMLElement {
|
|
|
149
195
|
modelElement = generatedModel;
|
|
150
196
|
}
|
|
151
197
|
if (!modelElement.inited) {
|
|
152
|
-
console.log(
|
|
198
|
+
console.log(
|
|
199
|
+
`########## FORE: kick off processing for ... ${window.location.href} ##########`,
|
|
200
|
+
);
|
|
153
201
|
modelElement.modelConstruct();
|
|
154
202
|
}
|
|
155
203
|
this.model = modelElement;
|
|
156
204
|
});
|
|
157
205
|
}
|
|
158
206
|
|
|
207
|
+
/**
|
|
208
|
+
* refreshes the UI by using IntersectionObserver API. This is the handler being called
|
|
209
|
+
* by the observer whenever elements come into / move out of viewport.
|
|
210
|
+
* @param entries
|
|
211
|
+
* @param observer
|
|
212
|
+
*/
|
|
213
|
+
handleIntersect(entries, observer) {
|
|
214
|
+
console.time('refreshLazy');
|
|
215
|
+
entries.forEach(entry => {
|
|
216
|
+
const { target } = entry;
|
|
217
|
+
|
|
218
|
+
if (entry.isIntersecting) {
|
|
219
|
+
console.log('in view', entry);
|
|
220
|
+
// console.log('repeat in view entry', entry.target);
|
|
221
|
+
// const target = entry.target;
|
|
222
|
+
// if(target.hasAttribute('refresh-on-view')){
|
|
223
|
+
target.classList.add('loaded');
|
|
224
|
+
// }
|
|
225
|
+
|
|
226
|
+
// todo: too restrictive here? what if target is a usual html element? shouldn't it refresh downwards?
|
|
227
|
+
if (typeof target.refresh === 'function') {
|
|
228
|
+
console.log('refreshing target', target);
|
|
229
|
+
target.refresh(target, true);
|
|
230
|
+
} else {
|
|
231
|
+
console.log('refreshing children', target);
|
|
232
|
+
Fore.refreshChildren(target, true);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
entries[0].target.getOwnerForm().dispatchEvent(new CustomEvent('refresh-done'));
|
|
237
|
+
|
|
238
|
+
console.timeEnd('refreshLazy');
|
|
239
|
+
}
|
|
240
|
+
|
|
159
241
|
evaluateToNodes(xpath, context) {
|
|
160
242
|
return evaluateXPathToNodes(xpath, context, this);
|
|
161
243
|
}
|
|
162
244
|
|
|
163
|
-
disconnectedCallback() {
|
|
245
|
+
disconnectedCallback() {
|
|
246
|
+
/*
|
|
247
|
+
this.removeEventListener('model-construct-done', this._handleModelConstructDone);
|
|
248
|
+
this.removeEventListener('message', this._displayMessage);
|
|
249
|
+
this.removeEventListener('error', this._displayError);
|
|
250
|
+
this.storedTemplateExpressionByNode=null;
|
|
251
|
+
this.shadowRoot = undefined;
|
|
252
|
+
*/
|
|
253
|
+
}
|
|
164
254
|
|
|
165
255
|
/**
|
|
166
256
|
* refreshes the whole UI by visiting each bound element (having a 'ref' attribute) and applying the state of
|
|
@@ -170,16 +260,39 @@ export class FxFore extends HTMLElement {
|
|
|
170
260
|
* AVT:
|
|
171
261
|
*
|
|
172
262
|
*/
|
|
173
|
-
async refresh() {
|
|
263
|
+
async refresh(force) {
|
|
174
264
|
// refresh () {
|
|
175
265
|
console.group('### refresh');
|
|
176
|
-
// await this.updateComplete;
|
|
177
266
|
|
|
178
|
-
|
|
179
|
-
|
|
267
|
+
console.time('refresh');
|
|
268
|
+
|
|
269
|
+
/*
|
|
270
|
+
const changedModelItems = this.getModel().changed;
|
|
271
|
+
const graph = this.getModel().mainGraph;
|
|
272
|
+
let doRefresh = true;
|
|
273
|
+
changedModelItems.forEach(item => {
|
|
274
|
+
if(graph.hasNode(item.path)) {
|
|
275
|
+
const deps = graph.dependentsOf(item.path, false);
|
|
276
|
+
if (deps.length === 0) {
|
|
277
|
+
doRefresh=false;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
this.getModel().changed = [];
|
|
282
|
+
|
|
283
|
+
if (!doRefresh) {
|
|
284
|
+
this.dispatchEvent(new CustomEvent('refresh-done'));
|
|
285
|
+
return ;
|
|
286
|
+
}
|
|
287
|
+
*/
|
|
288
|
+
// ### refresh Fore UI elements
|
|
289
|
+
console.time('refreshChildren');
|
|
290
|
+
Fore.refreshChildren(this, true);
|
|
291
|
+
console.timeEnd('refreshChildren');
|
|
180
292
|
|
|
181
293
|
// ### refresh template expressions
|
|
182
294
|
this._updateTemplateExpressions();
|
|
295
|
+
console.timeEnd('refresh');
|
|
183
296
|
|
|
184
297
|
console.groupEnd();
|
|
185
298
|
console.log('### <<<<< dispatching refresh-done - end of UI update cycle >>>>>');
|
|
@@ -189,7 +302,7 @@ export class FxFore extends HTMLElement {
|
|
|
189
302
|
/**
|
|
190
303
|
* entry point for processing of template expression enclosed in '{}' brackets.
|
|
191
304
|
*
|
|
192
|
-
* Expressions are found with an XPath search. For each node an entry is added to
|
|
305
|
+
* Expressions are found with an XPath search. For each node an entry is added to the storedTemplateExpressionByNode map.
|
|
193
306
|
*
|
|
194
307
|
*
|
|
195
308
|
* @private
|
|
@@ -197,9 +310,9 @@ export class FxFore extends HTMLElement {
|
|
|
197
310
|
_updateTemplateExpressions() {
|
|
198
311
|
// Note the fact we're going over HTML here: therefore the `html` prefix.
|
|
199
312
|
const search =
|
|
200
|
-
"(descendant-or-self::*/(text(), @*))[matches(.,'\\{.*\\}')] except descendant-or-self::
|
|
313
|
+
"(descendant-or-self::*/(text(), @*))[matches(.,'\\{.*\\}')] except descendant-or-self::fx-model/descendant-or-self::node()/(., @*)";
|
|
201
314
|
|
|
202
|
-
const tmplExpressions = evaluateXPathToNodes(search, this,
|
|
315
|
+
const tmplExpressions = evaluateXPathToNodes(search, this, this);
|
|
203
316
|
console.log('template expressions found ', tmplExpressions);
|
|
204
317
|
|
|
205
318
|
if (!this.storedTemplateExpressions) {
|
|
@@ -210,28 +323,93 @@ export class FxFore extends HTMLElement {
|
|
|
210
323
|
storing expressions and their nodes for re-evaluation
|
|
211
324
|
*/
|
|
212
325
|
Array.from(tmplExpressions).forEach(node => {
|
|
326
|
+
if (this.storedTemplateExpressionByNode.has(node)) {
|
|
327
|
+
// If the node is already known, do not process it twice
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
213
330
|
const expr = this._getTemplateExpression(node);
|
|
214
|
-
this.storedTemplateExpressions.push({
|
|
215
|
-
expr,
|
|
216
|
-
node,
|
|
217
|
-
});
|
|
218
|
-
});
|
|
219
331
|
|
|
220
|
-
|
|
221
|
-
this._processTemplateExpression(tmpl);
|
|
332
|
+
this.storedTemplateExpressionByNode.set(node, expr);
|
|
222
333
|
});
|
|
223
334
|
|
|
224
|
-
|
|
335
|
+
// TODO: Should we clean up nodes that existed but are now gone?
|
|
336
|
+
for (const node of this.storedTemplateExpressionByNode.keys()) {
|
|
337
|
+
this._processTemplateExpression({
|
|
338
|
+
node,
|
|
339
|
+
expr: this.storedTemplateExpressionByNode.get(node),
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
console.log('stored template expressions ', this.storedTemplateExpressionByNode);
|
|
225
344
|
}
|
|
226
345
|
|
|
227
346
|
// eslint-disable-next-line class-methods-use-this
|
|
228
347
|
_processTemplateExpression(exprObj) {
|
|
229
|
-
console.log('processing template expression ', exprObj);
|
|
348
|
+
// console.log('processing template expression ', exprObj);
|
|
230
349
|
|
|
231
350
|
const { expr } = exprObj;
|
|
232
351
|
const { node } = exprObj;
|
|
233
352
|
// console.log('expr ', expr);
|
|
234
|
-
evaluateTemplateExpression(expr, node, this);
|
|
353
|
+
this.evaluateTemplateExpression(expr, node, this);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* evaluate a template expression (some expression in {} brackets) on a node (either text- or attribute node.
|
|
358
|
+
* @param expr the XPath to evaluate
|
|
359
|
+
* @param node the node which will get updated with evaluation result
|
|
360
|
+
* @param form the form element
|
|
361
|
+
*/
|
|
362
|
+
evaluateTemplateExpression(expr, node) {
|
|
363
|
+
if (expr === '{}') return;
|
|
364
|
+
const matches = expr.match(/{[^}]*}/g);
|
|
365
|
+
const namespaceContextNode =
|
|
366
|
+
node.nodeType === node.TEXT_NODE ? node.parentNode : node.ownerElement;
|
|
367
|
+
if (matches) {
|
|
368
|
+
matches.forEach(match => {
|
|
369
|
+
// console.log('match ', match);
|
|
370
|
+
let naked = match.substring(1, match.length - 1);
|
|
371
|
+
const inscope = getInScopeContext(node, naked);
|
|
372
|
+
if (!inscope) {
|
|
373
|
+
const errNode =
|
|
374
|
+
node.nodeType === Node.TEXT_NODE || node.nodeType === Node.ATTRIBUTE_NODE
|
|
375
|
+
? node.parentNode
|
|
376
|
+
: node;
|
|
377
|
+
console.warn('no inscope context for ', errNode);
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
// Templates are special: they use the namespace configuration from the place where they are
|
|
381
|
+
// being defined
|
|
382
|
+
const instanceId = XPathUtil.getInstanceId(naked);
|
|
383
|
+
// console.log('target instance ', instanceId);
|
|
384
|
+
const inst = this.getModel().getInstance(instanceId);
|
|
385
|
+
try {
|
|
386
|
+
const result = evaluateXPathToString(naked, inscope, node, null, inst);
|
|
387
|
+
|
|
388
|
+
// console.log('result of eval ', result);
|
|
389
|
+
const replaced = expr.replaceAll(match, result);
|
|
390
|
+
// console.log('result of replacing ', replaced);
|
|
391
|
+
|
|
392
|
+
if (node.nodeType === Node.ATTRIBUTE_NODE) {
|
|
393
|
+
const parent = node.ownerElement;
|
|
394
|
+
|
|
395
|
+
// parent.setAttribute(name, replaced);
|
|
396
|
+
parent.setAttribute(node.nodeName, replaced);
|
|
397
|
+
} else if (node.nodeType === Node.TEXT_NODE) {
|
|
398
|
+
node.textContent = replaced;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
if (replaced.includes('{')) {
|
|
402
|
+
// console.log('need to go next round');
|
|
403
|
+
|
|
404
|
+
// todo: duplicated code here - see above
|
|
405
|
+
naked = replaced.substring(1, replaced.length);
|
|
406
|
+
this.evaluateTemplateExpression(replaced, node);
|
|
407
|
+
}
|
|
408
|
+
} catch (error) {
|
|
409
|
+
this.dispatchEvent(new CustomEvent('error', { detail: error }));
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
}
|
|
235
413
|
}
|
|
236
414
|
|
|
237
415
|
// eslint-disable-next-line class-methods-use-this
|
|
@@ -240,25 +418,28 @@ export class FxFore extends HTMLElement {
|
|
|
240
418
|
return node.value;
|
|
241
419
|
}
|
|
242
420
|
if (node.nodeType === Node.TEXT_NODE) {
|
|
243
|
-
return node.textContent;
|
|
421
|
+
return node.textContent.trim();
|
|
244
422
|
}
|
|
245
423
|
return null;
|
|
246
424
|
}
|
|
247
425
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
}
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
|
|
426
|
+
/**
|
|
427
|
+
* called when `model-construct-done` event is received to
|
|
428
|
+
* start initing of the UI.
|
|
429
|
+
*
|
|
430
|
+
* @private
|
|
431
|
+
*/
|
|
258
432
|
_handleModelConstructDone() {
|
|
259
433
|
this._initUI();
|
|
260
434
|
}
|
|
261
435
|
|
|
436
|
+
/**
|
|
437
|
+
* If there's no instance element found in a fx-model during init it will construct
|
|
438
|
+
* an instance from UI bindings.
|
|
439
|
+
*
|
|
440
|
+
* @returns {Promise<void>}
|
|
441
|
+
* @private
|
|
442
|
+
*/
|
|
262
443
|
async _lazyCreateInstance() {
|
|
263
444
|
const model = this.querySelector('fx-model');
|
|
264
445
|
if (model.instances.length === 0) {
|
|
@@ -318,50 +499,70 @@ export class FxFore extends HTMLElement {
|
|
|
318
499
|
}
|
|
319
500
|
|
|
320
501
|
/*
|
|
321
|
-
|
|
502
|
+
_createStep(){
|
|
322
503
|
|
|
323
|
-
|
|
324
|
-
|
|
504
|
+
}
|
|
505
|
+
*/
|
|
325
506
|
|
|
326
507
|
/*
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
508
|
+
_generateInstance(start, parent) {
|
|
509
|
+
if (start.hasAttribute('ref')) {
|
|
510
|
+
const ref = start.getAttribute('ref');
|
|
330
511
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
512
|
+
if(ref.includes('/')){
|
|
513
|
+
console.log('complex path to create ', ref);
|
|
514
|
+
const steps = ref.split('/');
|
|
515
|
+
steps.forEach(step => {
|
|
516
|
+
console.log('step ', step);
|
|
336
517
|
|
|
337
518
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
// const generated = document.createElement(ref);
|
|
342
|
-
const generated = parent.ownerDocument.createElement(ref);
|
|
343
|
-
if (start.children.length === 0) {
|
|
344
|
-
generated.textContent = start.textContent;
|
|
345
|
-
}
|
|
346
|
-
parent.appendChild(generated);
|
|
347
|
-
parent = generated;
|
|
348
|
-
}
|
|
519
|
+
});
|
|
520
|
+
}
|
|
349
521
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
522
|
+
// const generated = document.createElement(ref);
|
|
523
|
+
const generated = parent.ownerDocument.createElement(ref);
|
|
524
|
+
if (start.children.length === 0) {
|
|
525
|
+
generated.textContent = start.textContent;
|
|
526
|
+
}
|
|
527
|
+
parent.appendChild(generated);
|
|
528
|
+
parent = generated;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
if (start.hasChildNodes()) {
|
|
532
|
+
const list = start.children;
|
|
533
|
+
for (let i = 0; i < list.length; i += 1) {
|
|
534
|
+
this._generateInstance(list[i], parent);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
return parent;
|
|
354
538
|
}
|
|
355
|
-
|
|
356
|
-
return parent;
|
|
357
|
-
}
|
|
358
|
-
*/
|
|
539
|
+
*/
|
|
359
540
|
|
|
541
|
+
/**
|
|
542
|
+
* Start the initialization of the UI by
|
|
543
|
+
*
|
|
544
|
+
* 1. checking if a instance needs to be generated
|
|
545
|
+
* 2. attaching lazy loading intersection observers if `refresh-on-view` attributes are found
|
|
546
|
+
* 3. doing a full refresh of the UI
|
|
547
|
+
*
|
|
548
|
+
* @returns {Promise<void>}
|
|
549
|
+
* @private
|
|
550
|
+
*/
|
|
360
551
|
async _initUI() {
|
|
361
552
|
console.log('### _initUI()');
|
|
362
553
|
|
|
363
554
|
await this._lazyCreateInstance();
|
|
555
|
+
|
|
556
|
+
const options = {
|
|
557
|
+
root: null,
|
|
558
|
+
rootMargin: '0px',
|
|
559
|
+
threshold: 0.3,
|
|
560
|
+
};
|
|
561
|
+
|
|
364
562
|
await this.refresh();
|
|
563
|
+
// this.style.display='block'
|
|
564
|
+
this.classList.add('fx-ready');
|
|
565
|
+
|
|
365
566
|
this.ready = true;
|
|
366
567
|
console.log('### <<<<< dispatching ready >>>>>');
|
|
367
568
|
console.log('########## modelItems: ', this.getModel().modelItems);
|
|
@@ -369,6 +570,19 @@ export class FxFore extends HTMLElement {
|
|
|
369
570
|
this.dispatchEvent(new CustomEvent('ready', {}));
|
|
370
571
|
}
|
|
371
572
|
|
|
573
|
+
registerLazyElement(element) {
|
|
574
|
+
if (this.intersectionObserver) {
|
|
575
|
+
// console.log('registerLazyElement',element);
|
|
576
|
+
this.intersectionObserver.observe(element);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
unRegisterLazyElement(element) {
|
|
581
|
+
if (this.intersectionObserver) {
|
|
582
|
+
this.intersectionObserver.unobserve(element);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
372
586
|
/**
|
|
373
587
|
*
|
|
374
588
|
* @returns {FxModel}
|
|
@@ -386,7 +600,9 @@ export class FxFore extends HTMLElement {
|
|
|
386
600
|
_displayError(e) {
|
|
387
601
|
// const { error } = e.detail;
|
|
388
602
|
const msg = e.detail.message;
|
|
389
|
-
this._showMessage('modal', msg);
|
|
603
|
+
// this._showMessage('modal', msg);
|
|
604
|
+
const toast = this.shadowRoot.querySelector('#error');
|
|
605
|
+
toast.showToast(msg);
|
|
390
606
|
}
|
|
391
607
|
|
|
392
608
|
_showMessage(level, msg) {
|
package/src/fx-header.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { foreElementMixin } from './ForeElementMixin.js';
|
|
2
|
+
|
|
3
|
+
export class FxHeader extends foreElementMixin(HTMLElement) {
|
|
4
|
+
constructor() {
|
|
5
|
+
super();
|
|
6
|
+
this.style.display = 'none';
|
|
7
|
+
this.attachShadow({ mode: 'open' });
|
|
8
|
+
this.shadowRoot.innerHTML = ``;
|
|
9
|
+
|
|
10
|
+
if (!this.hasAttribute('name')) {
|
|
11
|
+
throw new Error('required attribute "name" missing');
|
|
12
|
+
}
|
|
13
|
+
this.name = this.getAttribute('name');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
connectedCallback() {
|
|
17
|
+
this.shadowRoot.innerHTML = ``;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
customElements.define('fx-header', FxHeader);
|
package/src/fx-instance.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Fore } from './fore.js';
|
|
1
2
|
import { evaluateXPathToFirstNode } from './xpath-evaluation.js';
|
|
2
3
|
|
|
3
4
|
/**
|
|
@@ -82,9 +83,20 @@ export class FxInstance extends HTMLElement {
|
|
|
82
83
|
* @returns {Document | T | any}
|
|
83
84
|
*/
|
|
84
85
|
getInstanceData() {
|
|
86
|
+
if (!this.instanceData) {
|
|
87
|
+
this._createInstanceData();
|
|
88
|
+
}
|
|
85
89
|
return this.instanceData;
|
|
86
90
|
}
|
|
87
91
|
|
|
92
|
+
setInstanceData(data) {
|
|
93
|
+
if (!data) {
|
|
94
|
+
this._createInstanceData();
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
this.instanceData = data;
|
|
98
|
+
}
|
|
99
|
+
|
|
88
100
|
/**
|
|
89
101
|
* return the default context (root node of respective instance) for XPath evalution.
|
|
90
102
|
*
|
|
@@ -121,7 +133,7 @@ export class FxInstance extends HTMLElement {
|
|
|
121
133
|
root.appendChild(newNode);
|
|
122
134
|
}
|
|
123
135
|
this.instanceData = doc;
|
|
124
|
-
this.instanceData.firstElementChild.setAttribute('id', this.id);
|
|
136
|
+
// this.instanceData.firstElementChild.setAttribute('id', this.id);
|
|
125
137
|
// resolve('done');
|
|
126
138
|
} else if (this.src) {
|
|
127
139
|
await this._loadData();
|
|
@@ -130,13 +142,25 @@ export class FxInstance extends HTMLElement {
|
|
|
130
142
|
}
|
|
131
143
|
}
|
|
132
144
|
|
|
145
|
+
_createInstanceData() {
|
|
146
|
+
if (this.type === 'xml') {
|
|
147
|
+
// const doc = new DOMParser().parseFromString('<data data-id="default"></data>', 'application/xml');
|
|
148
|
+
const doc = new DOMParser().parseFromString('<data></data>', 'application/xml');
|
|
149
|
+
this.instanceData = doc;
|
|
150
|
+
}
|
|
151
|
+
if (this.type === 'json') {
|
|
152
|
+
this.instanceData = {};
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
133
156
|
async _loadData() {
|
|
134
157
|
const url = `${this.src}`;
|
|
135
|
-
const contentType =
|
|
158
|
+
const contentType = Fore.getContentType(this, 'get');
|
|
159
|
+
|
|
136
160
|
await fetch(url, {
|
|
137
161
|
method: 'GET',
|
|
138
162
|
mode: 'cors',
|
|
139
|
-
credentials: '
|
|
163
|
+
credentials: 'include',
|
|
140
164
|
headers: {
|
|
141
165
|
'Content-Type': contentType,
|
|
142
166
|
},
|
|
@@ -144,7 +168,19 @@ export class FxInstance extends HTMLElement {
|
|
|
144
168
|
.then(response => {
|
|
145
169
|
const responseContentType = response.headers.get('content-type').toLowerCase();
|
|
146
170
|
console.log('********** responseContentType *********', responseContentType);
|
|
147
|
-
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
|
+
) {
|
|
148
184
|
// console.log("********** inside res plain *********");
|
|
149
185
|
return response.text();
|
|
150
186
|
}
|
|
@@ -153,16 +189,20 @@ export class FxInstance extends HTMLElement {
|
|
|
153
189
|
return response.json();
|
|
154
190
|
}
|
|
155
191
|
if (responseContentType.startsWith('application/xml')) {
|
|
156
|
-
return response.text().then(result =>
|
|
157
|
-
console.log('xml ********', result);
|
|
158
|
-
|
|
159
|
-
|
|
192
|
+
return response.text().then(result =>
|
|
193
|
+
// console.log('xml ********', result);
|
|
194
|
+
new DOMParser().parseFromString(result, 'application/xml'),
|
|
195
|
+
);
|
|
160
196
|
}
|
|
161
197
|
return 'done';
|
|
162
198
|
})
|
|
163
199
|
.then(data => {
|
|
200
|
+
if (data.nodeType) {
|
|
201
|
+
this.instanceData = data;
|
|
202
|
+
console.log('instanceData loaded: ', this.instanceData);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
164
205
|
this.instanceData = data;
|
|
165
|
-
console.log('instanceData loaded: ', this.instanceData);
|
|
166
206
|
})
|
|
167
207
|
.catch(error => {
|
|
168
208
|
throw new Error(`failed loading data ${error}`);
|
|
@@ -191,10 +231,14 @@ export class FxInstance extends HTMLElement {
|
|
|
191
231
|
// console.log('instanceData ', this.instanceData.firstElementChild);
|
|
192
232
|
|
|
193
233
|
console.log('fx-instance data: ', this.instanceData);
|
|
194
|
-
this.instanceData.firstElementChild.setAttribute('id', this.id);
|
|
234
|
+
// this.instanceData.firstElementChild.setAttribute('id', this.id);
|
|
195
235
|
// todo: move innerHTML out to shadowDOM (for later reset)
|
|
196
236
|
} else if (this.type === 'json') {
|
|
197
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;
|
|
198
242
|
} else {
|
|
199
243
|
console.warn('unknow type for data ', this.type);
|
|
200
244
|
}
|