@jinntec/fore 2.7.2 → 2.8.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/dist/fore-dev.js +2137 -1942
- package/dist/fore.js +10837 -14535
- package/package.json +1 -1
- package/resources/fore.css +3 -0
- package/src/fx-bind.js +1 -1
- package/src/fx-fore.js +298 -143
- package/src/fx-instance.js +2 -2
- package/src/fx-submission.js +18 -0
- package/src/modelitem.js +5 -5
- package/src/tools/deprecation.md +1 -0
- package/src/tools/fx-action-log.js +2 -2
- package/src/ui/abstract-control.js +49 -16
- package/src/xpath-util.js +3 -3
- package/src/DataObserver.js +0 -181
package/package.json
CHANGED
package/resources/fore.css
CHANGED
package/src/fx-bind.js
CHANGED
|
@@ -94,7 +94,7 @@ export class FxBind extends ForeElementMixin {
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
// ✅ only the repeat item gets the _<opNum> suffix; children do not.
|
|
97
|
-
const basePath =
|
|
97
|
+
const basePath = getPath(node, instanceId);
|
|
98
98
|
const path = opNum ? `${basePath}_${opNum}` : basePath;
|
|
99
99
|
|
|
100
100
|
// const path = XPathUtil.getPath(node, instanceId);
|
package/src/fx-fore.js
CHANGED
|
@@ -2,7 +2,7 @@ import { Fore } from './fore.js';
|
|
|
2
2
|
import './fx-instance.js';
|
|
3
3
|
import { FxModel } from './fx-model.js';
|
|
4
4
|
import '@jinntec/jinn-toast';
|
|
5
|
-
import { evaluateXPathToNodes, evaluateXPathToString } from './xpath-evaluation.js';
|
|
5
|
+
import { evaluateXPathToNodes, evaluateXPathToString, createNamespaceResolver } from './xpath-evaluation.js';
|
|
6
6
|
import getInScopeContext from './getInScopeContext.js';
|
|
7
7
|
import { XPathUtil } from './xpath-util.js';
|
|
8
8
|
import { FxRepeatAttributes } from './ui/fx-repeat-attributes.js';
|
|
@@ -43,6 +43,29 @@ export class FxFore extends HTMLElement {
|
|
|
43
43
|
|
|
44
44
|
static draggedItem = null;
|
|
45
45
|
|
|
46
|
+
// Records init gate events that have already happened for a given target (document/window/element).
|
|
47
|
+
// This prevents “missed gate” situations when an fx-fore is replaced (e.g. via src loading)
|
|
48
|
+
// after the init event already fired.
|
|
49
|
+
static _initEventState = new WeakMap();
|
|
50
|
+
|
|
51
|
+
static _hasSeenInitEvent(target, eventName) {
|
|
52
|
+
const set = FxFore._initEventState.get(target);
|
|
53
|
+
return !!(set && set.has(eventName));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
static _markInitEventSeen(target, eventName) {
|
|
57
|
+
let set = FxFore._initEventState.get(target);
|
|
58
|
+
if (!set) {
|
|
59
|
+
set = new Set();
|
|
60
|
+
FxFore._initEventState.set(target, set);
|
|
61
|
+
}
|
|
62
|
+
set.add(eventName);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
static get observedAttributes() {
|
|
66
|
+
return ['src', 'selector'];
|
|
67
|
+
}
|
|
68
|
+
|
|
46
69
|
static get properties() {
|
|
47
70
|
return {
|
|
48
71
|
/**
|
|
@@ -111,6 +134,9 @@ export class FxFore extends HTMLElement {
|
|
|
111
134
|
*/
|
|
112
135
|
this.model = null;
|
|
113
136
|
this.inited = false;
|
|
137
|
+
this._initGatesPromise = null;
|
|
138
|
+
this._warnedWaitForDeprecation = false;
|
|
139
|
+
this._srcLoadPromise = null;
|
|
114
140
|
// this.addEventListener('model-construct-done', this._handleModelConstructDone);
|
|
115
141
|
// todo: refactoring - these should rather go into connectedcallback
|
|
116
142
|
this.addEventListener('message', this._displayMessage);
|
|
@@ -249,8 +275,8 @@ export class FxFore extends HTMLElement {
|
|
|
249
275
|
this._scanForNewTemplateExpressionsNextRefresh = false;
|
|
250
276
|
this.repeatsFromAttributesCreated = false;
|
|
251
277
|
this.validateOn = this.hasAttribute('validate-on')
|
|
252
|
-
|
|
253
|
-
|
|
278
|
+
? this.getAttribute('validate-on')
|
|
279
|
+
: 'update';
|
|
254
280
|
// this.mergePartial = this.hasAttribute('merge-partial')? true:false;
|
|
255
281
|
this.mergePartial = false;
|
|
256
282
|
this.createNodes = this.hasAttribute('create-nodes') ? true : false;
|
|
@@ -259,108 +285,206 @@ export class FxFore extends HTMLElement {
|
|
|
259
285
|
}
|
|
260
286
|
|
|
261
287
|
/**
|
|
262
|
-
*
|
|
263
|
-
*
|
|
288
|
+
* Parse a list of target specs.
|
|
289
|
+
*
|
|
290
|
+
* We accept both comma- and whitespace-separated lists (for backward compatibility with `wait-for`).
|
|
291
|
+
* Each token can be:
|
|
292
|
+
* - "self" (default)
|
|
293
|
+
* - "closest" (closest fx-fore)
|
|
294
|
+
* - "document"
|
|
295
|
+
* - "window"
|
|
296
|
+
* - a CSS selector (no whitespace)
|
|
264
297
|
*/
|
|
265
|
-
|
|
266
|
-
const raw = this.getAttribute('wait-for');
|
|
298
|
+
_parseTargetList(raw) {
|
|
267
299
|
if (!raw) return [];
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
300
|
+
return raw
|
|
301
|
+
.split(/[\s,]+/)
|
|
302
|
+
.map(s => s.trim())
|
|
303
|
+
.filter(Boolean);
|
|
304
|
+
}
|
|
272
305
|
|
|
306
|
+
_findBySelector(sel) {
|
|
273
307
|
const roots = [this.getRootNode?.() ?? document, document];
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
if (sel === 'closest') {
|
|
280
|
-
el = this.closest('fx-fore');
|
|
281
|
-
} else {
|
|
282
|
-
for (const r of roots) {
|
|
283
|
-
if (r && 'querySelector' in r) {
|
|
284
|
-
el = r.querySelector(sel);
|
|
285
|
-
if (el) break;
|
|
286
|
-
}
|
|
287
|
-
}
|
|
308
|
+
for (const r of roots) {
|
|
309
|
+
if (r && 'querySelector' in r) {
|
|
310
|
+
const el = r.querySelector(sel);
|
|
311
|
+
if (el) return el;
|
|
288
312
|
}
|
|
289
|
-
if (el) out.push(el);
|
|
290
313
|
}
|
|
291
|
-
return
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
_isReadyTarget(el) {
|
|
318
|
+
return !!(
|
|
319
|
+
el &&
|
|
320
|
+
(el.ready === true ||
|
|
321
|
+
(el.classList && el.classList.contains('fx-ready')) ||
|
|
322
|
+
(typeof el.hasAttribute === 'function' && el.hasAttribute('ready')))
|
|
323
|
+
);
|
|
292
324
|
}
|
|
293
325
|
|
|
294
326
|
/**
|
|
295
|
-
*
|
|
296
|
-
*
|
|
327
|
+
* Collect all init gates derived from attributes.
|
|
328
|
+
*
|
|
329
|
+
* - `wait-for` (DEPRECATED) becomes: init-on="ready" + init-on-target=<list>
|
|
330
|
+
* - `init-on` / `init-on-target` define a generic event gate
|
|
297
331
|
*/
|
|
298
|
-
|
|
299
|
-
const
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
.
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
332
|
+
_collectInitGates() {
|
|
333
|
+
const gates = [];
|
|
334
|
+
|
|
335
|
+
const waitForRaw = this.getAttribute('wait-for');
|
|
336
|
+
if (waitForRaw) {
|
|
337
|
+
if (!this._warnedWaitForDeprecation) {
|
|
338
|
+
console.warn(
|
|
339
|
+
'[fx-fore] The "wait-for" attribute is deprecated. Use init-on="ready" init-on-target="..." instead.',
|
|
340
|
+
);
|
|
341
|
+
this._warnedWaitForDeprecation = true;
|
|
342
|
+
}
|
|
307
343
|
|
|
308
|
-
|
|
309
|
-
for (const
|
|
310
|
-
|
|
311
|
-
const el = r.querySelector(sel);
|
|
312
|
-
if (el) return el;
|
|
313
|
-
}
|
|
344
|
+
const deps = this._parseTargetList(waitForRaw);
|
|
345
|
+
for (const dep of deps) {
|
|
346
|
+
gates.push({ event: 'ready', targetSpec: dep });
|
|
314
347
|
}
|
|
315
|
-
|
|
316
|
-
};
|
|
348
|
+
}
|
|
317
349
|
|
|
318
|
-
const
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
350
|
+
const initOn = this.getAttribute('init-on');
|
|
351
|
+
const initOnTargetRaw = this.getAttribute('init-on-target');
|
|
352
|
+
if (initOn || initOnTargetRaw) {
|
|
353
|
+
const eventName = initOn || 'ready';
|
|
354
|
+
const targets = initOnTargetRaw ? this._parseTargetList(initOnTargetRaw) : ['self'];
|
|
355
|
+
for (const t of targets) {
|
|
356
|
+
gates.push({ event: eventName, targetSpec: t });
|
|
322
357
|
}
|
|
323
|
-
|
|
324
|
-
return !!(el && el.ready === true);
|
|
325
|
-
};
|
|
358
|
+
}
|
|
326
359
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
360
|
+
return gates;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
_waitForEvent(target, eventName, isSatisfiedFn = null) {
|
|
364
|
+
// If a caller provides an explicit satisfaction check, honor it first.
|
|
365
|
+
if (typeof isSatisfiedFn === 'function' && isSatisfiedFn(target)) {
|
|
366
|
+
FxFore._markInitEventSeen(target, eventName);
|
|
367
|
+
return Promise.resolve();
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Sticky gate: if this event already happened on this target, don't wait again.
|
|
371
|
+
if (FxFore._hasSeenInitEvent(target, eventName)) {
|
|
372
|
+
return Promise.resolve();
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return new Promise(resolve => {
|
|
376
|
+
const ac = new AbortController();
|
|
377
|
+
const on = () => {
|
|
378
|
+
FxFore._markInitEventSeen(target, eventName);
|
|
379
|
+
ac.abort();
|
|
380
|
+
resolve();
|
|
381
|
+
};
|
|
382
|
+
target.addEventListener(eventName, on, { once: true, signal: ac.signal });
|
|
383
|
+
});
|
|
384
|
+
}
|
|
346
385
|
|
|
347
|
-
|
|
348
|
-
|
|
386
|
+
_waitForMatchingEvent(eventName, matchesEventFn, recheckFn = null) {
|
|
387
|
+
if (typeof recheckFn === 'function' && recheckFn()) {
|
|
388
|
+
return Promise.resolve();
|
|
389
|
+
}
|
|
349
390
|
|
|
350
|
-
|
|
391
|
+
return new Promise(resolve => {
|
|
392
|
+
const root = document;
|
|
351
393
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
394
|
+
const cleanupAll = () => {
|
|
395
|
+
root.removeEventListener(eventName, onEvent, true);
|
|
396
|
+
if (mo) mo.disconnect();
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
const onEvent = ev => {
|
|
400
|
+
if (matchesEventFn(ev)) {
|
|
401
|
+
cleanupAll();
|
|
402
|
+
resolve();
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
root.addEventListener(eventName, onEvent, true);
|
|
407
|
+
|
|
408
|
+
// Only used for `ready` (or any other gate that provides a recheck function)
|
|
409
|
+
let mo = null;
|
|
410
|
+
if (typeof recheckFn === 'function') {
|
|
411
|
+
mo = new MutationObserver(() => {
|
|
412
|
+
if (recheckFn()) {
|
|
413
|
+
cleanupAll();
|
|
357
414
|
resolve();
|
|
358
415
|
}
|
|
359
416
|
});
|
|
360
417
|
mo.observe(document.documentElement, { childList: true, subtree: true });
|
|
361
|
-
}
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
}
|
|
362
421
|
|
|
363
|
-
|
|
422
|
+
_waitForInitGate({ event, targetSpec }) {
|
|
423
|
+
// Direct targets
|
|
424
|
+
if (targetSpec === 'self') {
|
|
425
|
+
const satisfied = event === 'ready' ? t => this._isReadyTarget(t) : null;
|
|
426
|
+
return this._waitForEvent(this, event, satisfied);
|
|
427
|
+
}
|
|
428
|
+
if (targetSpec === 'document') {
|
|
429
|
+
return this._waitForEvent(document, event);
|
|
430
|
+
}
|
|
431
|
+
if (targetSpec === 'window') {
|
|
432
|
+
return this._waitForEvent(window, event);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// Special: closest fx-fore
|
|
436
|
+
if (targetSpec === 'closest') {
|
|
437
|
+
const recheckFn =
|
|
438
|
+
event === 'ready' ? () => this._isReadyTarget(this.closest('fx-fore')) : null;
|
|
439
|
+
|
|
440
|
+
const matchesFn = ev => {
|
|
441
|
+
const t = ev.target;
|
|
442
|
+
return t?.tagName === 'FX-FORE' && t.contains(this);
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
return this._waitForMatchingEvent(event, matchesFn, recheckFn);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// Selector targets
|
|
449
|
+
const selector = targetSpec;
|
|
450
|
+
|
|
451
|
+
const recheckFn =
|
|
452
|
+
event === 'ready' ? () => this._isReadyTarget(this._findBySelector(selector)) : null;
|
|
453
|
+
|
|
454
|
+
if (typeof recheckFn === 'function' && recheckFn()) {
|
|
455
|
+
return Promise.resolve();
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
const matchesFn = ev => {
|
|
459
|
+
// Prefer composedPath() so events coming from inside shadow DOM still match
|
|
460
|
+
const path = typeof ev.composedPath === 'function' ? ev.composedPath() : [];
|
|
461
|
+
for (const n of path) {
|
|
462
|
+
if (n && n.matches && n.matches(selector)) return true;
|
|
463
|
+
}
|
|
464
|
+
const t = ev.target;
|
|
465
|
+
return !!(t && t.closest && t.closest(selector));
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
return this._waitForMatchingEvent(event, matchesFn, recheckFn);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Wait until all configured init gates are satisfied.
|
|
473
|
+
* This is the single consolidation point for init gating.
|
|
474
|
+
*/
|
|
475
|
+
_waitForInitGates() {
|
|
476
|
+
if (this._initGatesPromise) return this._initGatesPromise;
|
|
477
|
+
|
|
478
|
+
const gates = this._collectInitGates();
|
|
479
|
+
if (!gates.length) {
|
|
480
|
+
this._initGatesPromise = Promise.resolve();
|
|
481
|
+
return this._initGatesPromise;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
this._initGatesPromise = Promise.all(gates.map(g => this._waitForInitGate(g))).then(
|
|
485
|
+
() => undefined,
|
|
486
|
+
);
|
|
487
|
+
return this._initGatesPromise;
|
|
364
488
|
}
|
|
365
489
|
|
|
366
490
|
_onSlotChange = async ev => {
|
|
@@ -371,14 +495,12 @@ export class FxFore extends HTMLElement {
|
|
|
371
495
|
// avoid double init
|
|
372
496
|
if (this.inited) return;
|
|
373
497
|
|
|
374
|
-
// 2) Wait for
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
return;
|
|
381
|
-
}
|
|
498
|
+
// 2) Wait for init gates (init-on / init-on-target / wait-for)
|
|
499
|
+
try {
|
|
500
|
+
await this._waitForInitGates();
|
|
501
|
+
} catch (e) {
|
|
502
|
+
console.warn('init gating failed', e);
|
|
503
|
+
return;
|
|
382
504
|
}
|
|
383
505
|
|
|
384
506
|
// 3) Bail if we got disconnected/replaced while waiting
|
|
@@ -395,12 +517,12 @@ export class FxFore extends HTMLElement {
|
|
|
395
517
|
}
|
|
396
518
|
// Fallback for odd engines/polyfills
|
|
397
519
|
return (slotEl.assignedNodes({ flatten: true }) || []).filter(
|
|
398
|
-
|
|
520
|
+
n => n.nodeType === Node.ELEMENT_NODE,
|
|
399
521
|
);
|
|
400
522
|
};
|
|
401
523
|
|
|
402
524
|
// SAFE: slotEl is the actual event source, not a fresh query
|
|
403
|
-
const children =
|
|
525
|
+
const children = getAssignedElements();
|
|
404
526
|
|
|
405
527
|
let modelElement = children.find(modelElem => modelElem.nodeName.toUpperCase() === 'FX-MODEL');
|
|
406
528
|
if (!modelElement) {
|
|
@@ -413,8 +535,8 @@ export class FxFore extends HTMLElement {
|
|
|
413
535
|
}
|
|
414
536
|
if (!modelElement.inited) {
|
|
415
537
|
console.info(
|
|
416
|
-
|
|
417
|
-
|
|
538
|
+
`%cFore running ... ${this.id ? '#' + this.id : ''}`,
|
|
539
|
+
'background:#64b5f6; color:white; padding:.5rem; display:inline-block; white-space: nowrap; border-radius:0.3rem;width:100%;',
|
|
418
540
|
);
|
|
419
541
|
|
|
420
542
|
const variables = new Map();
|
|
@@ -435,9 +557,48 @@ export class FxFore extends HTMLElement {
|
|
|
435
557
|
this.inited = true;
|
|
436
558
|
};
|
|
437
559
|
|
|
560
|
+
|
|
561
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
562
|
+
if (oldValue === newValue) return;
|
|
563
|
+
|
|
564
|
+
if (name === 'src') {
|
|
565
|
+
this.src = newValue;
|
|
566
|
+
if (!newValue) {
|
|
567
|
+
// Reset so a later src assignment can load again
|
|
568
|
+
this._srcLoadPromise = null;
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
if (this.isConnected) {
|
|
572
|
+
this._maybeLoadFromSrc();
|
|
573
|
+
}
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
if (name === 'selector') {
|
|
578
|
+
// Selector changes should affect a pending src-load
|
|
579
|
+
if (this.isConnected && this.src && !this._srcLoadPromise) {
|
|
580
|
+
this._maybeLoadFromSrc();
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
_maybeLoadFromSrc() {
|
|
586
|
+
if (!this.src) return null;
|
|
587
|
+
if (this._srcLoadPromise) return this._srcLoadPromise;
|
|
588
|
+
|
|
589
|
+
this._srcLoadPromise = (async () => {
|
|
590
|
+
await this._waitForInitGates();
|
|
591
|
+
if (!this.isConnected) return;
|
|
592
|
+
const selector = this.getAttribute('selector') || 'fx-fore';
|
|
593
|
+
await Fore.loadForeFromSrc(this, this.src, selector);
|
|
594
|
+
})();
|
|
595
|
+
|
|
596
|
+
return this._srcLoadPromise;
|
|
597
|
+
}
|
|
598
|
+
|
|
438
599
|
connectedCallback() {
|
|
439
600
|
const modelElement = Array.from(this.children).find(
|
|
440
|
-
|
|
601
|
+
modelElem => modelElem.nodeName.toUpperCase() === 'FX-MODEL',
|
|
441
602
|
);
|
|
442
603
|
|
|
443
604
|
this.model = modelElement;
|
|
@@ -464,8 +625,8 @@ export class FxFore extends HTMLElement {
|
|
|
464
625
|
},true);
|
|
465
626
|
*/
|
|
466
627
|
this.ignoreExpressions = this.hasAttribute('ignore-expressions')
|
|
467
|
-
|
|
468
|
-
|
|
628
|
+
? this.getAttribute('ignore-expressions')
|
|
629
|
+
: null;
|
|
469
630
|
|
|
470
631
|
this.lazyRefresh = this.hasAttribute('refresh-on-view');
|
|
471
632
|
if (this.lazyRefresh) {
|
|
@@ -479,7 +640,7 @@ export class FxFore extends HTMLElement {
|
|
|
479
640
|
|
|
480
641
|
this.src = this.hasAttribute('src') ? this.getAttribute('src') : null;
|
|
481
642
|
if (this.src) {
|
|
482
|
-
this.
|
|
643
|
+
this._maybeLoadFromSrc();
|
|
483
644
|
return;
|
|
484
645
|
}
|
|
485
646
|
|
|
@@ -542,12 +703,12 @@ export class FxFore extends HTMLElement {
|
|
|
542
703
|
|
|
543
704
|
markAsClean() {
|
|
544
705
|
this.addEventListener(
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
706
|
+
'value-changed',
|
|
707
|
+
() => {
|
|
708
|
+
this.dirtyState = dirtyStates.DIRTY;
|
|
709
|
+
this.classList.toggle('fx-modified')
|
|
710
|
+
},
|
|
711
|
+
{ once: true },
|
|
551
712
|
);
|
|
552
713
|
this.dirtyState = dirtyStates.CLEAN;
|
|
553
714
|
this.classList.remove('fx-modified');
|
|
@@ -560,15 +721,7 @@ export class FxFore extends HTMLElement {
|
|
|
560
721
|
* @private
|
|
561
722
|
*/
|
|
562
723
|
async _loadFromSrc() {
|
|
563
|
-
|
|
564
|
-
if (this.hasAttribute('wait-for')) {
|
|
565
|
-
await this._whenDependenciesReady();
|
|
566
|
-
}
|
|
567
|
-
if(this.hasAttribute('selector')){
|
|
568
|
-
await Fore.loadForeFromSrc(this, this.src, this.getAttribute('selector'));
|
|
569
|
-
}else{
|
|
570
|
-
await Fore.loadForeFromSrc(this, this.src, 'fx-fore');
|
|
571
|
-
}
|
|
724
|
+
return this._maybeLoadFromSrc();
|
|
572
725
|
}
|
|
573
726
|
|
|
574
727
|
/**
|
|
@@ -676,9 +829,9 @@ export class FxFore extends HTMLElement {
|
|
|
676
829
|
this.initialRun = false;
|
|
677
830
|
this.style.visibility = 'visible';
|
|
678
831
|
console.info(
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
832
|
+
`%c ✅ refresh-done on #${this.id}`,
|
|
833
|
+
'background:darkorange; color:black; padding:.5rem; display:inline-block; white-space: nowrap; border-radius:0.3rem;width:100%;',
|
|
834
|
+
this.getModel().modelItems,
|
|
682
835
|
);
|
|
683
836
|
|
|
684
837
|
Fore.dispatch(this, 'refresh-done', {});
|
|
@@ -783,7 +936,7 @@ export class FxFore extends HTMLElement {
|
|
|
783
936
|
*/
|
|
784
937
|
_updateTemplateExpressions() {
|
|
785
938
|
const search =
|
|
786
|
-
|
|
939
|
+
"(descendant-or-self::*!(text(), @*))[contains(., '{')][substring-after(., '{') => contains('}')][not(ancestor-or-self::*[self::fx-model or self::fx-function])]";
|
|
787
940
|
|
|
788
941
|
const tmplExpressions = evaluateXPathToNodes(search, this, this);
|
|
789
942
|
// console.log('template expressions found ', tmplExpressions);
|
|
@@ -870,19 +1023,19 @@ export class FxFore extends HTMLElement {
|
|
|
870
1023
|
if (!inscope) {
|
|
871
1024
|
console.warn('no inscope context for expr', naked);
|
|
872
1025
|
const errNode =
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
1026
|
+
node.nodeType === Node.TEXT_NODE || node.nodeType === Node.ATTRIBUTE_NODE
|
|
1027
|
+
? node.parentNode
|
|
1028
|
+
: node;
|
|
876
1029
|
return match;
|
|
877
1030
|
}
|
|
878
1031
|
// Templates are special: they use the namespace configuration from the place where they are
|
|
879
1032
|
// being defined
|
|
880
|
-
const instanceId = XPathUtil.getInstanceId(naked);
|
|
1033
|
+
const instanceId = XPathUtil.getInstanceId(naked,node);
|
|
881
1034
|
|
|
882
1035
|
// If there is an instance referred
|
|
883
1036
|
const inst = instanceId
|
|
884
|
-
|
|
885
|
-
|
|
1037
|
+
? this.getModel().getInstance(instanceId)
|
|
1038
|
+
: this.getModel().getDefaultInstance();
|
|
886
1039
|
|
|
887
1040
|
try {
|
|
888
1041
|
const result = evaluateXPathToString(naked, inscope, node, null, inst);
|
|
@@ -957,17 +1110,17 @@ export class FxFore extends HTMLElement {
|
|
|
957
1110
|
|
|
958
1111
|
// ##### lazy creation should NOT take place if there's a parent Fore using shared instances
|
|
959
1112
|
const parentFore =
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
1113
|
+
this.parentNode.nodeType !== Node.DOCUMENT_FRAGMENT_NODE
|
|
1114
|
+
? this.parentNode.closest('fx-fore')
|
|
1115
|
+
: null;
|
|
963
1116
|
if (this.parentNode.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
|
|
964
1117
|
console.log('fragment', this.parentNode);
|
|
965
1118
|
}
|
|
966
1119
|
|
|
967
1120
|
if (parentFore) {
|
|
968
1121
|
const shared = parentFore
|
|
969
|
-
|
|
970
|
-
|
|
1122
|
+
.getModel()
|
|
1123
|
+
.instances.filter(shared => shared.hasAttribute('shared'));
|
|
971
1124
|
if (shared.length !== 0) return;
|
|
972
1125
|
}
|
|
973
1126
|
|
|
@@ -988,8 +1141,8 @@ export class FxFore extends HTMLElement {
|
|
|
988
1141
|
}
|
|
989
1142
|
} catch (e) {
|
|
990
1143
|
console.warn(
|
|
991
|
-
|
|
992
|
-
|
|
1144
|
+
'lazyCreateInstance created an error attempting to create a document',
|
|
1145
|
+
e.message,
|
|
993
1146
|
);
|
|
994
1147
|
}
|
|
995
1148
|
}
|
|
@@ -1046,8 +1199,8 @@ export class FxFore extends HTMLElement {
|
|
|
1046
1199
|
*/
|
|
1047
1200
|
async _initUI() {
|
|
1048
1201
|
console.info(
|
|
1049
|
-
|
|
1050
|
-
|
|
1202
|
+
`%cinitUI #${this.id}`,
|
|
1203
|
+
'background:lightblue; color:black; padding:.5rem; display:inline-block; white-space: nowrap; border-radius:0.3rem;width:100%;',
|
|
1051
1204
|
);
|
|
1052
1205
|
|
|
1053
1206
|
const parentFore = this.closest('fx-fore');
|
|
@@ -1087,8 +1240,8 @@ export class FxFore extends HTMLElement {
|
|
|
1087
1240
|
this.initialRun = false;
|
|
1088
1241
|
// console.log('### >>>>> dispatching ready >>>>>', this);
|
|
1089
1242
|
console.info(
|
|
1090
|
-
|
|
1091
|
-
|
|
1243
|
+
`%c ✅ ${this.id ? '#' + this.id : 'Fore'} is ready`,
|
|
1244
|
+
'background:lightgreen; color:black; padding:.5rem; display:inline-block; white-space: nowrap; border-radius:0.3rem;width:100%;',
|
|
1092
1245
|
);
|
|
1093
1246
|
|
|
1094
1247
|
// console.log(`### <<<<< ${this.id} ready >>>>>`);
|
|
@@ -1226,9 +1379,9 @@ export class FxFore extends HTMLElement {
|
|
|
1226
1379
|
* @type {import('./ForeElementMixin.js').default[]}
|
|
1227
1380
|
*/
|
|
1228
1381
|
const boundControls = Array.from(
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1382
|
+
root.querySelectorAll(
|
|
1383
|
+
'fx-control[ref],fx-upload[ref],fx-group[ref],fx-repeat[ref], fx-switch[ref]',
|
|
1384
|
+
),
|
|
1232
1385
|
);
|
|
1233
1386
|
if (root.matches && root.matches('fx-repeatitem')) {
|
|
1234
1387
|
boundControls.unshift(root);
|
|
@@ -1291,7 +1444,7 @@ export class FxFore extends HTMLElement {
|
|
|
1291
1444
|
bound.evalInContext();
|
|
1292
1445
|
if (bound.nodeName !== 'FX-REPEAT') {
|
|
1293
1446
|
// Do not try to get a bind for a nodeSET of a repeat. there are multiple.
|
|
1294
|
-
|
|
1447
|
+
bound.getModelItem().bind?.evalInContext();
|
|
1295
1448
|
}
|
|
1296
1449
|
|
|
1297
1450
|
// console.log('CREATED child', newElement);
|
|
@@ -1343,9 +1496,9 @@ export class FxFore extends HTMLElement {
|
|
|
1343
1496
|
parentNodeset.setAttributeNode(newNode);
|
|
1344
1497
|
} else {
|
|
1345
1498
|
let referenceNode = this._findReferenceNodeForNewElement(
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1499
|
+
newNode,
|
|
1500
|
+
parentNodeset,
|
|
1501
|
+
siblingControl,
|
|
1349
1502
|
);
|
|
1350
1503
|
|
|
1351
1504
|
if (referenceNode) {
|
|
@@ -1396,11 +1549,13 @@ export class FxFore extends HTMLElement {
|
|
|
1396
1549
|
let newElement;
|
|
1397
1550
|
if (ref.includes('/')) {
|
|
1398
1551
|
// multi-step ref expressions
|
|
1399
|
-
|
|
1552
|
+
const namespaceResolver = createNamespaceResolver(ref, this);
|
|
1553
|
+
newElement = XPathUtil.createNodesFromXPath(ref, referenceNode.ownerDocument, this, namespaceResolver);
|
|
1400
1554
|
// console.log('new subtree', newElement);
|
|
1401
1555
|
return newElement;
|
|
1402
1556
|
} else {
|
|
1403
|
-
|
|
1557
|
+
const namespaceResolver = createNamespaceResolver(ref, this);
|
|
1558
|
+
return XPathUtil.createNodesFromXPath(ref, referenceNode.ownerDocument, this, namespaceResolver);
|
|
1404
1559
|
}
|
|
1405
1560
|
}
|
|
1406
1561
|
|