@microsoft/webui-framework 0.0.17 → 0.0.19

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.
@@ -0,0 +1,1392 @@
1
+ import { getTemplate } from './template.js';
2
+ import { hydrationStart, hydrationEnd } from './lifecycle.js';
3
+ import { createRepeatKeyState, seedHydratedRepeatKeys, syncRepeat, dotWalk, } from './element/diff.js';
4
+ import { collectItemMarkers, nextElement, findByOrdinal, MARKER_COND_START, MARKER_COND_END, MARKER_REPEAT_START, } from './element/markers.js';
5
+ import { injectModuleStyle, } from './element/styles.js';
6
+ import { ATTR_KIND_BOOLEAN, ATTR_KIND_COMPLEX, ATTR_KIND_TEMPLATE, hasNativeLiveProperty, } from './element/types.js';
7
+ import { templateHasRoot, templateRootForAttribute } from './template-roots.js';
8
+ const DEV = typeof __WEBUI_DEV__ === 'undefined' || __WEBUI_DEV__;
9
+ const templateCache = new WeakMap();
10
+ const templateDOMCache = new WeakMap();
11
+ const rootTagCache = new WeakMap();
12
+ const tplOrdinalCache = new WeakMap();
13
+ function getTplOrdinals(tplNode) {
14
+ let map = tplOrdinalCache.get(tplNode);
15
+ if (map)
16
+ return map;
17
+ map = new Map();
18
+ let elemOrd = 0;
19
+ let textOrd = 0;
20
+ const children = tplNode.childNodes;
21
+ for (let k = 0; k < children.length; k++) {
22
+ const type = children[k].nodeType;
23
+ if (type === 1) {
24
+ map.set(k, [1, elemOrd]);
25
+ elemOrd++;
26
+ }
27
+ else if (type === 3) {
28
+ map.set(k, [3, textOrd]);
29
+ textOrd++;
30
+ }
31
+ }
32
+ tplOrdinalCache.set(tplNode, map);
33
+ return map;
34
+ }
35
+ const EMPTY_ARR = [];
36
+ const EMPTY_SET = Object.freeze(new Set());
37
+ const WEBUI_SET_STATE_KEY = Symbol.for('microsoft.webui.setStateKey');
38
+ const templateMetaByCtor = new WeakMap();
39
+ function childNodesArray(parent) {
40
+ const children = parent.childNodes;
41
+ const len = children.length;
42
+ const result = new Array(len);
43
+ for (let i = 0; i < len; i++)
44
+ result[i] = children[i];
45
+ return result;
46
+ }
47
+ function getTemplateDom(meta) {
48
+ let cached = templateDOMCache.get(meta);
49
+ if (cached)
50
+ return cached;
51
+ const div = document.createElement('div');
52
+ div.innerHTML = meta.h;
53
+ templateDOMCache.set(meta, div);
54
+ return div;
55
+ }
56
+ function installTemplateObservedAttributes(ctor, tagName) {
57
+ const meta = getTemplate(tagName);
58
+ if (!meta)
59
+ return;
60
+ templateMetaByCtor.set(ctor, meta);
61
+ const attrs = meta.ta ?? EMPTY_ARR;
62
+ if (attrs.length === 0)
63
+ return;
64
+ const existing = ctor.observedAttributes ?? EMPTY_ARR;
65
+ const merged = new Array(existing.length + attrs.length);
66
+ let count = 0;
67
+ for (let i = 0; i < existing.length; i++) {
68
+ merged[count] = existing[i];
69
+ count += 1;
70
+ }
71
+ for (let attrIndex = 0; attrIndex < attrs.length; attrIndex++) {
72
+ const attrName = attrs[attrIndex];
73
+ let found = false;
74
+ for (let i = 0; i < count; i++) {
75
+ if (merged[i] === attrName) {
76
+ found = true;
77
+ break;
78
+ }
79
+ }
80
+ if (!found) {
81
+ merged[count] = attrName;
82
+ count += 1;
83
+ }
84
+ }
85
+ merged.length = count;
86
+ Object.defineProperty(ctor, 'observedAttributes', {
87
+ get() {
88
+ return merged;
89
+ },
90
+ configurable: true,
91
+ });
92
+ }
93
+ function hasAuthoredMember(instance, key) {
94
+ if (Object.prototype.hasOwnProperty.call(instance, key))
95
+ return true;
96
+ let proto = Object.getPrototypeOf(instance);
97
+ while (proto && proto !== TemplateElement.prototype) {
98
+ if (Object.prototype.hasOwnProperty.call(proto, key))
99
+ return true;
100
+ proto = Object.getPrototypeOf(proto);
101
+ }
102
+ return false;
103
+ }
104
+ export class TemplateElement extends HTMLElement {
105
+ constructor() {
106
+ super(...arguments);
107
+ this.$root = null;
108
+ this.$ready = false;
109
+ this.$hydrated = false;
110
+ this.$deferredSSR = false;
111
+ this.$activatingDeferredSSR = false;
112
+ this.$hasUnknownScopes = false;
113
+ this.$templateState = null;
114
+ this.$dirtyPaths = null;
115
+ this.$pendingFlush = false;
116
+ this.$preReadyWrites = null;
117
+ this.$resolver = (p, s) => this.$resolveValue(p, s);
118
+ }
119
+ [WEBUI_SET_STATE_KEY](key, value) {
120
+ const wasDeferred = this.$deferredSSR;
121
+ this.$beforeExternalStateWrite();
122
+ const owned = this.$setStateKey(key, value);
123
+ this.$afterExternalStateWrite(owned);
124
+ if (owned && wasDeferred && !this.$deferredSSR)
125
+ this.$update(key);
126
+ return owned;
127
+ }
128
+ static define(tagName) {
129
+ installTemplateObservedAttributes(this, tagName);
130
+ customElements.define(tagName, this);
131
+ }
132
+ connectedCallback() {
133
+ const tag = this.tagName.toLowerCase();
134
+ if (this.$deferredSSR) {
135
+ this.$ready = true;
136
+ return;
137
+ }
138
+ if (this.$hydrated && this.$root) {
139
+ hydrationStart();
140
+ this.$ready = true;
141
+ this.$update();
142
+ hydrationEnd();
143
+ return;
144
+ }
145
+ const meta = getTemplate(tag);
146
+ if (!meta) {
147
+ console.warn(`[WebUI] Template metadata for <${tag}> not found. ` +
148
+ `Ensure the component is included in the SSR output or registered via __webui.templates.`);
149
+ return;
150
+ }
151
+ this.$meta = meta;
152
+ this.$mount(meta, false);
153
+ }
154
+ $mount(meta, forceSSR) {
155
+ if (this.$hydrated)
156
+ return;
157
+ const hasShadow = !!this.shadowRoot;
158
+ const wantShadow = hasShadow || !!meta.sd;
159
+ let root;
160
+ let isSSR;
161
+ let clientRoot = null;
162
+ if (hasShadow) {
163
+ root = this.shadowRoot;
164
+ isSSR = true;
165
+ }
166
+ else if (this.childNodes.length > 0 && !meta.sd) {
167
+ root = this;
168
+ isSSR = true;
169
+ }
170
+ else if (wantShadow) {
171
+ root = this.attachShadow({ mode: 'open' });
172
+ isSSR = false;
173
+ }
174
+ else {
175
+ root = this;
176
+ isSSR = false;
177
+ }
178
+ if (isSSR && !forceSSR && this.$shouldDeferSSRHydration()) {
179
+ this.$meta = meta;
180
+ this.$deferredSSR = true;
181
+ this.$ready = true;
182
+ return;
183
+ }
184
+ hydrationStart();
185
+ if (meta.sa)
186
+ injectModuleStyle(meta.sa, this.shadowRoot);
187
+ if (isSSR) {
188
+ if (this.$shouldApplySSRBootstrapState())
189
+ this.$applySSRState();
190
+ this.$root = this.$hydrate(root, meta, getTemplateDom(meta));
191
+ }
192
+ else {
193
+ clientRoot = this.$createStagingRoot(meta);
194
+ this.$root = this.$wire(clientRoot, meta);
195
+ }
196
+ this.$meta = meta;
197
+ this.$hydrated = true;
198
+ this.$ready = true;
199
+ this.$syncAuthoredAttributes();
200
+ if (isSSR && DEV)
201
+ this.$checkHydrationMismatch();
202
+ else
203
+ this.$preReadyWrites = null;
204
+ if (!isSSR && clientRoot) {
205
+ this.$updateInstance(this.$root);
206
+ if (this.$root.repeats.length !== 0 || this.$root.conds.length !== 0) {
207
+ this.$root.nodes = childNodesArray(clientRoot);
208
+ this.$replaceInstanceContainer(this.$root, clientRoot, root);
209
+ }
210
+ this.$appendStagedChildren(root, clientRoot);
211
+ this.$root.container = root;
212
+ }
213
+ hydrationEnd();
214
+ }
215
+ disconnectedCallback() {
216
+ if (this.$root) {
217
+ queueMicrotask(() => {
218
+ if (!this.isConnected)
219
+ this.$destroy();
220
+ });
221
+ }
222
+ }
223
+ $destroy() {
224
+ if (!this.$root) {
225
+ this.$deferredSSR = false;
226
+ this.$ready = false;
227
+ this.$hasUnknownScopes = false;
228
+ return;
229
+ }
230
+ this.$disposeInstance(this.$root, false);
231
+ this.$root = null;
232
+ this.$pathIndex = undefined;
233
+ this.$wildcardBindings = undefined;
234
+ this.$dirtyPaths = null;
235
+ this.$pendingFlush = false;
236
+ this.$preReadyWrites = null;
237
+ this.$ready = false;
238
+ this.$hasUnknownScopes = false;
239
+ }
240
+ $clearRepeatBinding(repeat) {
241
+ repeat.instances.length = 0;
242
+ repeat.container = null;
243
+ repeat.start = null;
244
+ repeat.end = null;
245
+ const state = repeat.keyState;
246
+ if (state) {
247
+ state.keys.length = 0;
248
+ state.nextKeys.length = 0;
249
+ state.map.clear();
250
+ }
251
+ }
252
+ $clearInstance(instance) {
253
+ instance.scope = undefined;
254
+ instance.parent = undefined;
255
+ instance.container = null;
256
+ instance.nodes.length = 0;
257
+ instance.texts.length = 0;
258
+ instance.attrs.length = 0;
259
+ instance.conds.length = 0;
260
+ instance.repeats.length = 0;
261
+ }
262
+ attributeChangedCallback(name, oldValue, newValue) {
263
+ if (Object.is(oldValue, newValue))
264
+ return;
265
+ const wasDeferred = this.$deferredSSR;
266
+ this.$beforeExternalStateWrite();
267
+ const property = this.$templateRootForAttribute(name);
268
+ let changed = false;
269
+ if (property && this.$usesTemplateState(property)) {
270
+ changed = this.$setTemplateState(property, newValue);
271
+ }
272
+ this.$afterExternalStateWrite(changed);
273
+ if (changed && property && wasDeferred && !this.$deferredSSR)
274
+ this.$update(property);
275
+ }
276
+ setState(state) {
277
+ const wasDeferred = this.$deferredSSR;
278
+ this.$beforeExternalStateWrite();
279
+ const keys = Object.keys(state);
280
+ let owned = false;
281
+ for (let i = 0; i < keys.length; i++) {
282
+ const key = keys[i];
283
+ owned = this.$setStateKey(key, state[key]) || owned;
284
+ }
285
+ this.$afterExternalStateWrite(owned);
286
+ if (owned && wasDeferred && !this.$deferredSSR) {
287
+ for (let i = 0; i < keys.length; i++)
288
+ this.$update(keys[i]);
289
+ }
290
+ this.$flushUpdates();
291
+ }
292
+ $observableNames() {
293
+ return EMPTY_SET;
294
+ }
295
+ $beforeExternalStateWrite() {
296
+ }
297
+ $afterExternalStateWrite(_applied) {
298
+ }
299
+ $shouldDeferSSRHydration() {
300
+ return false;
301
+ }
302
+ $activateDeferredSSR() {
303
+ if (!this.$deferredSSR || !this.$meta)
304
+ return;
305
+ this.$deferredSSR = false;
306
+ this.$ready = false;
307
+ this.$preReadyWrites = null;
308
+ this.$activatingDeferredSSR = true;
309
+ try {
310
+ this.$mount(this.$meta, true);
311
+ }
312
+ finally {
313
+ this.$activatingDeferredSSR = false;
314
+ }
315
+ }
316
+ $shouldApplySSRBootstrapState() {
317
+ return true;
318
+ }
319
+ $shouldApplySSRState(_key) {
320
+ return true;
321
+ }
322
+ $syncAuthoredAttributes() {
323
+ }
324
+ $shouldApplyTemplateStateFromSSR(_key) {
325
+ return true;
326
+ }
327
+ $setTemplateState(key, value) {
328
+ const changed = this.$writeTemplateState(key, value);
329
+ if (changed) {
330
+ this.$update(key);
331
+ }
332
+ return changed;
333
+ }
334
+ $writeTemplateState(key, value) {
335
+ if (!this.$templateState) {
336
+ this.$templateState = Object.create(null);
337
+ }
338
+ if (Object.is(this.$templateState[key], value))
339
+ return false;
340
+ this.$templateState[key] = value;
341
+ return true;
342
+ }
343
+ $templateMeta() {
344
+ if (this.$meta)
345
+ return this.$meta;
346
+ const fromCtor = templateMetaByCtor.get(this.constructor);
347
+ if (fromCtor)
348
+ return fromCtor;
349
+ const tagName = this.tagName;
350
+ return tagName ? getTemplate(tagName.toLowerCase()) : undefined;
351
+ }
352
+ $templateRootForAttribute(name) {
353
+ const meta = this.$templateMeta();
354
+ return meta ? templateRootForAttribute(meta, name) : undefined;
355
+ }
356
+ $usesTemplateState(key) {
357
+ const meta = this.$templateMeta();
358
+ return !!meta && templateHasRoot(meta, key) && !hasAuthoredMember(this, key);
359
+ }
360
+ $setStateKey(key, value) {
361
+ if (this.$observableNames().has(key)) {
362
+ this[key] = value;
363
+ return true;
364
+ }
365
+ if (this.$usesTemplateState(key)) {
366
+ this.$setTemplateState(key, value);
367
+ return true;
368
+ }
369
+ return false;
370
+ }
371
+ $applySSRState() {
372
+ const state = window.__webui?.state;
373
+ if (!state || typeof state !== 'object')
374
+ return;
375
+ const observableNames = this.$observableNames();
376
+ const keys = Object.keys(state);
377
+ for (let i = 0; i < keys.length; i++) {
378
+ const key = keys[i];
379
+ if (observableNames.has(key)) {
380
+ if (!this.$shouldApplySSRState(key))
381
+ continue;
382
+ this[`_${key}`] = state[key];
383
+ }
384
+ else if (this.$usesTemplateState(key) && this.$shouldApplyTemplateStateFromSSR(key)) {
385
+ this.$writeTemplateState(key, state[key]);
386
+ }
387
+ }
388
+ }
389
+ $update(path) {
390
+ if (!this.$ready || !this.$root) {
391
+ if (DEV && path && this.isConnected)
392
+ this.$recordPreReadyWrite(path);
393
+ return;
394
+ }
395
+ if (!this.$pathIndex)
396
+ this.$buildPathIndex();
397
+ if (path && this.$pathIndex) {
398
+ const entry = this.$pathIndex.get(path);
399
+ if (entry) {
400
+ if (!this.$dirtyPaths)
401
+ this.$dirtyPaths = new Set();
402
+ this.$dirtyPaths.add(path);
403
+ if (!this.$pendingFlush) {
404
+ this.$pendingFlush = true;
405
+ queueMicrotask(() => this.$flush());
406
+ }
407
+ return;
408
+ }
409
+ }
410
+ this.$dirtyPaths = null;
411
+ this.$updateInstance(this.$root);
412
+ }
413
+ $flushUpdates() {
414
+ if (this.$pendingFlush)
415
+ this.$flush();
416
+ }
417
+ $flush() {
418
+ if (!this.$ready || !this.$root) {
419
+ this.$dirtyPaths = null;
420
+ this.$pendingFlush = false;
421
+ return;
422
+ }
423
+ if (!this.$pathIndex)
424
+ this.$buildPathIndex();
425
+ if (!this.$pathIndex)
426
+ return;
427
+ while (this.$dirtyPaths && this.$dirtyPaths.size > 0) {
428
+ const dirty = this.$dirtyPaths;
429
+ this.$dirtyPaths = null;
430
+ for (const path of dirty) {
431
+ if (!this.$pathIndex)
432
+ this.$buildPathIndex();
433
+ const entry = this.$pathIndex?.get(path);
434
+ if (entry) {
435
+ this.$updateBindings(entry.texts, entry.attrs, entry.conds, entry.repeats);
436
+ }
437
+ }
438
+ if (!this.$pathIndex)
439
+ this.$buildPathIndex();
440
+ if (this.$wildcardBindings) {
441
+ const wc = this.$wildcardBindings;
442
+ this.$updateBindings(wc.texts, wc.attrs, wc.conds, wc.repeats);
443
+ }
444
+ if (!this.$pathIndex)
445
+ this.$buildPathIndex();
446
+ }
447
+ this.$pendingFlush = false;
448
+ }
449
+ $recordPreReadyWrite(path) {
450
+ if (!this.$preReadyWrites)
451
+ this.$preReadyWrites = new Set();
452
+ this.$preReadyWrites.add(path);
453
+ }
454
+ $checkHydrationMismatch() {
455
+ if (!DEV)
456
+ return;
457
+ const writes = this.$preReadyWrites;
458
+ this.$preReadyWrites = null;
459
+ if (!writes || writes.size === 0 || !this.$root)
460
+ return;
461
+ if (!this.$pathIndex)
462
+ this.$buildPathIndex();
463
+ const index = this.$pathIndex;
464
+ if (!index)
465
+ return;
466
+ const ctx = {
467
+ resolver: this.$resolver,
468
+ resolveParts: (parts, scope) => this.$resolveParts(parts, scope),
469
+ resolveValue: (path, scope) => this.$resolveValue(path, scope),
470
+ };
471
+ const tag = this.tagName.toLowerCase();
472
+ void import('./hydration-mismatch.js').then((m) => m.reportHydrationMismatch(tag, writes, index, ctx));
473
+ }
474
+ $resolve(root, path, pathStart = 0) {
475
+ let cur = root;
476
+ for (let i = 0; i < pathStart; i++) {
477
+ const child = cur.childNodes[path[i]];
478
+ if (!child)
479
+ return null;
480
+ cur = child;
481
+ }
482
+ for (let i = pathStart; i < path.length; i++) {
483
+ const child = cur.childNodes[path[i]];
484
+ if (!child)
485
+ return null;
486
+ cur = child;
487
+ }
488
+ return cur;
489
+ }
490
+ $resolveSSR(ssrRoot, tplRoot, path, pathStart = 0) {
491
+ let ssr = ssrRoot;
492
+ let tpl = tplRoot;
493
+ for (let i = 0; i < pathStart; i++) {
494
+ const tplChild = tpl.childNodes[path[i]];
495
+ if (!tplChild)
496
+ return null;
497
+ tpl = tplChild;
498
+ }
499
+ for (let i = pathStart; i < path.length; i++) {
500
+ const idx = path[i];
501
+ const tplChild = tpl.childNodes[idx];
502
+ if (!tplChild)
503
+ return null;
504
+ const ordinals = getTplOrdinals(tpl);
505
+ const entry = ordinals.get(idx);
506
+ if (!entry)
507
+ return null;
508
+ const [nodeType, ordinal] = entry;
509
+ const child = findByOrdinal(ssr, nodeType, ordinal);
510
+ if (!child)
511
+ return null;
512
+ ssr = child;
513
+ tpl = tplChild;
514
+ }
515
+ return ssr;
516
+ }
517
+ $parseTemplate(meta) {
518
+ let cached = templateCache.get(meta);
519
+ if (cached)
520
+ return cached.cloneNode(true);
521
+ const tpl = document.createElement('template');
522
+ tpl.innerHTML = meta.h;
523
+ templateCache.set(meta, tpl.content);
524
+ return tpl.content.cloneNode(true);
525
+ }
526
+ $createStagingRoot(meta) {
527
+ const wrapper = document.createElement('div');
528
+ const fragment = this.$parseTemplate(meta);
529
+ wrapper.appendChild(fragment);
530
+ customElements.upgrade(wrapper);
531
+ return wrapper;
532
+ }
533
+ $appendStagedChildren(root, stagingRoot) {
534
+ const first = stagingRoot.firstChild;
535
+ if (!first)
536
+ return;
537
+ if (!first.nextSibling) {
538
+ root.appendChild(first);
539
+ return;
540
+ }
541
+ const fragment = document.createDocumentFragment();
542
+ while (stagingRoot.firstChild) {
543
+ fragment.appendChild(stagingRoot.firstChild);
544
+ }
545
+ root.appendChild(fragment);
546
+ }
547
+ $replaceInstanceContainer(instance, previous, next) {
548
+ if (previous === next)
549
+ return;
550
+ const stack = [instance];
551
+ while (stack.length > 0) {
552
+ const current = stack.pop();
553
+ if (!current)
554
+ continue;
555
+ if (current.container === previous)
556
+ current.container = next;
557
+ for (let i = 0; i < current.conds.length; i++) {
558
+ const child = current.conds[i].instance;
559
+ if (child)
560
+ stack.push(child);
561
+ }
562
+ for (let i = 0; i < current.repeats.length; i++) {
563
+ const repeat = current.repeats[i];
564
+ if (repeat.container === previous)
565
+ repeat.container = next;
566
+ const children = repeat.instances;
567
+ for (let j = 0; j < children.length; j++)
568
+ stack.push(children[j]);
569
+ }
570
+ }
571
+ }
572
+ $wire(root, meta, scope) {
573
+ const instance = {
574
+ scope, container: root, nodes: childNodesArray(root),
575
+ texts: [], attrs: [], conds: [], repeats: [],
576
+ };
577
+ const textRefs = new Array(meta.tx?.length ?? 0);
578
+ let textRefCount = 0;
579
+ if (meta.tx) {
580
+ for (let i = 0; i < meta.tx.length; i++) {
581
+ const entry = meta.tx[i];
582
+ const [slot, parts] = entry;
583
+ const raw = entry[2] === 1;
584
+ const [parentPath, beforeIndex] = slot;
585
+ const parent = parentPath.length > 0 ? this.$resolve(root, parentPath) : root;
586
+ if (!parent || (parent.nodeType !== 1 && parent.nodeType !== 11))
587
+ continue;
588
+ textRefs[textRefCount] = { parent, ref: parent.childNodes[beforeIndex] || null, parts, raw };
589
+ textRefCount += 1;
590
+ }
591
+ }
592
+ const condRefs = new Array(meta.c?.length ?? 0);
593
+ let condRefCount = 0;
594
+ if (meta.c) {
595
+ for (let i = 0; i < meta.c.length; i++) {
596
+ const [condition, blockIndex, slotMeta] = meta.c[i];
597
+ const [parentPath, beforeIndex] = slotMeta;
598
+ const parent = parentPath.length > 0 ? this.$resolve(root, parentPath) : root;
599
+ if (!parent || (parent.nodeType !== 1 && parent.nodeType !== 11))
600
+ continue;
601
+ condRefs[condRefCount] = { parent, ref: parent.childNodes[beforeIndex] || null, condition: condition, blockIndex };
602
+ condRefCount += 1;
603
+ }
604
+ }
605
+ const repRefs = new Array(meta.r?.length ?? 0);
606
+ let repRefCount = 0;
607
+ if (meta.r) {
608
+ for (let i = 0; i < meta.r.length; i++) {
609
+ const [collection, itemVar, blockIndex, slotMeta, keyPath] = meta.r[i];
610
+ const [parentPath, beforeIndex] = slotMeta;
611
+ const parent = parentPath.length > 0 ? this.$resolve(root, parentPath) : root;
612
+ if (!parent || (parent.nodeType !== 1 && parent.nodeType !== 11))
613
+ continue;
614
+ repRefs[repRefCount] = {
615
+ parent,
616
+ ref: parent.childNodes[beforeIndex] || null,
617
+ collection,
618
+ itemVar,
619
+ blockIndex,
620
+ keyPath,
621
+ };
622
+ repRefCount += 1;
623
+ }
624
+ }
625
+ this.$wireAttrs(instance, meta, scope, (p) => this.$resolve(root, p));
626
+ this.$finalize(instance, root, meta, (r, p) => this.$resolve(r, p), scope);
627
+ for (let i = 0; i < textRefCount; i++) {
628
+ const t = textRefs[i];
629
+ const anchor = document.createComment('');
630
+ t.parent.insertBefore(anchor, t.ref);
631
+ if (t.raw) {
632
+ const container = document.createElement('span');
633
+ t.parent.insertBefore(container, anchor);
634
+ const textNode = document.createTextNode('');
635
+ instance.texts.push({ node: textNode, parts: t.parts, scope, raw: true, rawParent: container });
636
+ }
637
+ else {
638
+ const textNode = document.createTextNode('');
639
+ t.parent.insertBefore(textNode, anchor);
640
+ instance.texts.push({ node: textNode, parts: t.parts, scope });
641
+ }
642
+ }
643
+ for (let i = 0; i < condRefCount; i++) {
644
+ const c = condRefs[i];
645
+ const anchor = document.createComment('');
646
+ c.parent.insertBefore(anchor, c.ref);
647
+ instance.conds.push({
648
+ condition: c.condition,
649
+ blockIndex: c.blockIndex,
650
+ anchor,
651
+ scope,
652
+ owner: instance,
653
+ instance: null,
654
+ });
655
+ }
656
+ for (let i = 0; i < repRefCount; i++) {
657
+ const r = repRefs[i];
658
+ const anchor = document.createComment('');
659
+ r.parent.insertBefore(anchor, r.ref);
660
+ const binding = {
661
+ markerId: i, collection: r.collection, itemVar: r.itemVar, blockIndex: r.blockIndex,
662
+ container: r.parent, start: anchor, end: null,
663
+ scope, owner: instance, instances: [],
664
+ };
665
+ if (r.keyPath !== undefined) {
666
+ binding.keyState = createRepeatKeyState(r.keyPath);
667
+ }
668
+ instance.repeats.push(binding);
669
+ }
670
+ for (let i = 0; i < instance.conds.length; i++)
671
+ this.$toggleCond(instance.conds[i]);
672
+ return instance;
673
+ }
674
+ $hydrate(ssrRoot, meta, tplDom, scope, pathStart = 0) {
675
+ const instance = {
676
+ scope,
677
+ container: (pathStart > 0 ? ssrRoot.parentNode : ssrRoot),
678
+ nodes: pathStart > 0 ? [ssrRoot] : childNodesArray(ssrRoot),
679
+ texts: [], attrs: [], conds: [], repeats: [],
680
+ };
681
+ const staleMarkers = [];
682
+ if (meta.tx) {
683
+ for (let i = 0; i < meta.tx.length; i++) {
684
+ const entry = meta.tx[i];
685
+ const [slot, parts] = entry;
686
+ const raw = entry[2] === 1;
687
+ const [parentPath, beforeIndex] = slot;
688
+ const ssrParent = this.$resolveSSR(ssrRoot, tplDom, parentPath, pathStart);
689
+ if (!ssrParent)
690
+ continue;
691
+ const tplParent = this.$resolve(tplDom, parentPath, pathStart);
692
+ if (!tplParent)
693
+ continue;
694
+ if (raw) {
695
+ const rawParent = ssrParent;
696
+ const textNode = document.createTextNode('');
697
+ instance.texts.push({ node: textNode, parts, scope, raw: true, rawParent });
698
+ }
699
+ else {
700
+ let textNode = this.$findSSRText(ssrParent, tplParent, beforeIndex);
701
+ if (!textNode) {
702
+ textNode = document.createTextNode('');
703
+ const insertRef = this.$findSSRSlotRef(ssrParent, tplParent, beforeIndex);
704
+ ssrParent.insertBefore(textNode, insertRef);
705
+ }
706
+ if (textNode)
707
+ instance.texts.push({ node: textNode, parts, scope });
708
+ }
709
+ }
710
+ }
711
+ this.$wireAttrs(instance, meta, scope, (p) => this.$resolveSSR(ssrRoot, tplDom, p, pathStart));
712
+ if (meta.c) {
713
+ let lastCondMarker = null;
714
+ let lastCondParent = null;
715
+ for (let i = 0; i < meta.c.length; i++) {
716
+ const [condition, blockIndex, slotMeta] = meta.c[i];
717
+ const [parentPath] = slotMeta;
718
+ const ssrParent = this.$resolveSSR(ssrRoot, tplDom, parentPath, pathStart) ?? ssrRoot;
719
+ const blockMeta = this.$block(blockIndex);
720
+ let condInstance = null;
721
+ if (ssrParent !== lastCondParent) {
722
+ lastCondMarker = null;
723
+ lastCondParent = ssrParent;
724
+ }
725
+ const marker = this.$findMarker(ssrParent, MARKER_COND_START, lastCondMarker);
726
+ let condAnchor;
727
+ if (marker) {
728
+ condAnchor = marker;
729
+ }
730
+ else {
731
+ condAnchor = document.createComment('');
732
+ const [, beforeIndex] = slotMeta;
733
+ const insertRef = ssrParent.childNodes[beforeIndex ?? ssrParent.childNodes.length] ?? null;
734
+ ssrParent.insertBefore(condAnchor, insertRef);
735
+ }
736
+ if (marker)
737
+ lastCondMarker = marker;
738
+ if (marker && blockMeta) {
739
+ condInstance = this.$hydrateCondContent(condAnchor, blockMeta, scope);
740
+ if (condInstance)
741
+ condInstance.parent = instance;
742
+ }
743
+ if (marker) {
744
+ const lastNode = condInstance ? condInstance.nodes[condInstance.nodes.length - 1] : condAnchor;
745
+ const endMarker = lastNode?.nextSibling;
746
+ if (endMarker && endMarker.nodeType === 8 && endMarker.data === MARKER_COND_END) {
747
+ staleMarkers.push(endMarker);
748
+ }
749
+ }
750
+ instance.conds.push({
751
+ condition: condition, blockIndex,
752
+ anchor: condAnchor,
753
+ scope, owner: instance, instance: condInstance,
754
+ });
755
+ }
756
+ }
757
+ if (meta.r) {
758
+ let lastRepMarker = null;
759
+ let lastRepParent = null;
760
+ for (let i = 0; i < meta.r.length; i++) {
761
+ const [collection, itemVar, blockIndex, slotMeta, keyPath] = meta.r[i];
762
+ const [parentPath] = slotMeta;
763
+ const ssrParent = this.$resolveSSR(ssrRoot, tplDom, parentPath, pathStart) ?? ssrRoot;
764
+ if (ssrParent !== lastRepParent) {
765
+ lastRepMarker = null;
766
+ lastRepParent = ssrParent;
767
+ }
768
+ const blockMeta = this.$block(blockIndex);
769
+ const blockTplDom = blockMeta ? getTemplateDom(blockMeta) : null;
770
+ const rootTag = blockMeta && blockTplDom?.childNodes.length === 1 && blockTplDom.children.length === 1
771
+ ? this.$rootTag(blockMeta)
772
+ : null;
773
+ const marker = this.$findMarker(ssrParent, MARKER_REPEAT_START, lastRepMarker);
774
+ let anchor;
775
+ if (marker) {
776
+ anchor = marker;
777
+ }
778
+ else {
779
+ anchor = document.createComment('');
780
+ const [, beforeIndex] = slotMeta;
781
+ const tplParent = this.$resolve(tplDom, parentPath, pathStart);
782
+ const staticCount = tplParent ? tplParent.childNodes.length : 0;
783
+ const insertRef = ssrParent.childNodes[Math.min(beforeIndex ?? staticCount, ssrParent.childNodes.length)] ?? null;
784
+ ssrParent.insertBefore(anchor, insertRef);
785
+ }
786
+ lastRepMarker = anchor;
787
+ const repeatInsts = [];
788
+ const hasCollectionState = this.$hasStateRoot(collection, scope);
789
+ const itemsArr = this.$resolveValue(collection, scope);
790
+ const items = Array.isArray(itemsArr) ? itemsArr : [];
791
+ const { items: itemMarkers, end: endMarker } = marker
792
+ ? collectItemMarkers(anchor)
793
+ : { items: [], end: null };
794
+ if (blockMeta && blockTplDom && anchor.parentNode && itemMarkers.length > 0) {
795
+ if (!this.$activatingDeferredSSR
796
+ && hasCollectionState
797
+ && itemMarkers.length !== items.length) {
798
+ console.warn(`[webui] hydration: repeat marker count (${itemMarkers.length}) ≠ data length (${items.length}) for "${collection}"`);
799
+ }
800
+ for (let j = 0; j < itemMarkers.length; j++) {
801
+ const itemValue = items[j];
802
+ const known = hasCollectionState && j < items.length;
803
+ if (!known)
804
+ this.$hasUnknownScopes = true;
805
+ const itemScope = {
806
+ name: itemVar,
807
+ value: itemValue,
808
+ parent: scope,
809
+ known,
810
+ };
811
+ if (rootTag) {
812
+ const itemEl = nextElement(itemMarkers[j]);
813
+ if (itemEl) {
814
+ const childInstance = this.$hydrate(itemEl, blockMeta, blockTplDom, itemScope, 1);
815
+ childInstance.parent = instance;
816
+ repeatInsts.push(childInstance);
817
+ }
818
+ }
819
+ else {
820
+ const itemParent = itemMarkers[j].parentNode;
821
+ const nextBound = j + 1 < itemMarkers.length ? itemMarkers[j + 1] : endMarker;
822
+ const wrapper = document.createElement('div');
823
+ let cursor = itemMarkers[j].nextSibling;
824
+ while (cursor && cursor !== nextBound) {
825
+ const next = cursor.nextSibling;
826
+ wrapper.appendChild(cursor);
827
+ cursor = next;
828
+ }
829
+ const inst = this.$hydrate(wrapper, blockMeta, blockTplDom, itemScope);
830
+ inst.parent = instance;
831
+ inst.nodes = childNodesArray(wrapper);
832
+ let afterNode = itemMarkers[j];
833
+ for (let nodeIndex = 0; nodeIndex < inst.nodes.length; nodeIndex++) {
834
+ const node = inst.nodes[nodeIndex];
835
+ itemParent?.insertBefore(node, afterNode.nextSibling);
836
+ afterNode = node;
837
+ }
838
+ if (itemParent) {
839
+ this.$replaceInstanceContainer(inst, wrapper, itemParent);
840
+ }
841
+ repeatInsts.push(inst);
842
+ }
843
+ }
844
+ for (let m = 0; m < itemMarkers.length; m++) {
845
+ staleMarkers.push(itemMarkers[m]);
846
+ }
847
+ }
848
+ if (endMarker)
849
+ staleMarkers.push(endMarker);
850
+ const binding = {
851
+ markerId: i, collection, itemVar, blockIndex,
852
+ container: (anchor.parentNode ?? ssrRoot),
853
+ start: anchor, end: null,
854
+ scope, owner: instance, instances: repeatInsts,
855
+ synced: hasCollectionState,
856
+ };
857
+ if (keyPath !== undefined) {
858
+ binding.keyState = createRepeatKeyState(keyPath);
859
+ if (hasCollectionState) {
860
+ seedHydratedRepeatKeys(binding, items);
861
+ }
862
+ }
863
+ instance.repeats.push(binding);
864
+ }
865
+ }
866
+ this.$finalize(instance, ssrRoot, meta, (r, p) => this.$resolveSSR(r, tplDom, p, pathStart), scope);
867
+ for (let i = 0; i < staleMarkers.length; i++) {
868
+ staleMarkers[i].parentNode?.removeChild(staleMarkers[i]);
869
+ }
870
+ this.$compactNodeArray(instance);
871
+ return instance;
872
+ }
873
+ $collectConditionalRange(start) {
874
+ const nodes = [];
875
+ let depth = 0;
876
+ let node = start.nextSibling;
877
+ while (node) {
878
+ if (node.nodeType === 8) {
879
+ const data = node.data;
880
+ if (data === MARKER_COND_START) {
881
+ depth++;
882
+ }
883
+ else if (data === MARKER_COND_END) {
884
+ if (depth === 0)
885
+ break;
886
+ depth--;
887
+ }
888
+ }
889
+ nodes.push(node);
890
+ node = node.nextSibling;
891
+ }
892
+ return nodes;
893
+ }
894
+ $hasRootStructuralSlot(meta) {
895
+ if (meta.c) {
896
+ for (let i = 0; i < meta.c.length; i++) {
897
+ if (meta.c[i][2][0].length === 0)
898
+ return true;
899
+ }
900
+ }
901
+ if (meta.r) {
902
+ for (let i = 0; i < meta.r.length; i++) {
903
+ if (meta.r[i][3][0].length === 0)
904
+ return true;
905
+ }
906
+ }
907
+ return false;
908
+ }
909
+ $hydrateCondContent(condAnchor, blockMeta, scope) {
910
+ const rootTag = this.$rootTag(blockMeta);
911
+ const tplDom = getTemplateDom(blockMeta);
912
+ if (rootTag && tplDom.children.length === 1 && !this.$hasRootStructuralSlot(blockMeta)) {
913
+ const el = nextElement(condAnchor);
914
+ if (el) {
915
+ return this.$hydrate(el, blockMeta, tplDom, scope, 1);
916
+ }
917
+ return null;
918
+ }
919
+ const condNodes = this.$collectConditionalRange(condAnchor);
920
+ if (condNodes.length === 0)
921
+ return null;
922
+ const wrapper = document.createElement('div');
923
+ for (let cn = 0; cn < condNodes.length; cn++)
924
+ wrapper.appendChild(condNodes[cn]);
925
+ const inst = this.$hydrate(wrapper, blockMeta, tplDom, scope);
926
+ inst.nodes = childNodesArray(wrapper);
927
+ let afterNode = condAnchor;
928
+ for (let cn = 0; cn < inst.nodes.length; cn++) {
929
+ condAnchor.parentNode?.insertBefore(inst.nodes[cn], afterNode.nextSibling);
930
+ afterNode = inst.nodes[cn];
931
+ }
932
+ const container = condAnchor.parentNode;
933
+ if (container)
934
+ this.$replaceInstanceContainer(inst, wrapper, container);
935
+ return inst;
936
+ }
937
+ $findMarker(parent, data, after) {
938
+ let child = after ? after.nextSibling : parent.firstChild;
939
+ while (child) {
940
+ if (child.nodeType === 8 && child.data === data) {
941
+ return child;
942
+ }
943
+ child = child.nextSibling;
944
+ }
945
+ return null;
946
+ }
947
+ $findSSRText(ssrParent, tplParent, beforeIndex) {
948
+ const ordinals = getTplOrdinals(tplParent);
949
+ let textOrd = 0;
950
+ for (let k = 0; k < beforeIndex; k++) {
951
+ const entry = ordinals.get(k);
952
+ if (entry && entry[0] === 3)
953
+ textOrd++;
954
+ }
955
+ const found = findByOrdinal(ssrParent, 3, textOrd);
956
+ if (found)
957
+ return found;
958
+ let child = ssrParent.firstChild;
959
+ while (child) {
960
+ if (child.nodeType === 3 && child.data && child.data.trim()) {
961
+ return child;
962
+ }
963
+ child = child.nextSibling;
964
+ }
965
+ return null;
966
+ }
967
+ $findSSRSlotRef(ssrParent, tplParent, beforeIndex) {
968
+ const ordinals = getTplOrdinals(tplParent);
969
+ const children = tplParent.childNodes;
970
+ for (let i = beforeIndex; i < children.length; i++) {
971
+ const entry = ordinals.get(i);
972
+ if (!entry)
973
+ continue;
974
+ return findByOrdinal(ssrParent, entry[0], entry[1]);
975
+ }
976
+ return null;
977
+ }
978
+ $rootTag(meta) {
979
+ let cached = rootTagCache.get(meta);
980
+ if (cached !== undefined)
981
+ return cached;
982
+ const h = meta.h;
983
+ if (!h || h.charCodeAt(0) !== 60) {
984
+ rootTagCache.set(meta, null);
985
+ return null;
986
+ }
987
+ let end = 1;
988
+ while (end < h.length) {
989
+ const c = h.charCodeAt(end);
990
+ if (c === 32 || c === 62 || c === 47)
991
+ break;
992
+ end++;
993
+ }
994
+ const tag = h.slice(1, end).toLowerCase();
995
+ rootTagCache.set(meta, tag);
996
+ return tag;
997
+ }
998
+ $wireAttrs(instance, meta, scope, resolve) {
999
+ if (!meta.a || !meta.ag)
1000
+ return;
1001
+ for (let g = 0; g < meta.ag.length; g++) {
1002
+ const [targetPath, start, count] = meta.ag[g];
1003
+ const el = resolve(targetPath);
1004
+ if (!el || el.nodeType !== 1)
1005
+ continue;
1006
+ for (let j = 0; j < count; j++) {
1007
+ const entry = meta.a[start + j];
1008
+ if (entry)
1009
+ instance.attrs.push(this.$makeAttr(el, entry, scope));
1010
+ }
1011
+ }
1012
+ }
1013
+ $finalize(_instance, _root, _meta, _resolver, _scope) { }
1014
+ $makeAttr(el, entry, scope) {
1015
+ const name = entry[0];
1016
+ const kind = entry[1];
1017
+ if (kind === ATTR_KIND_BOOLEAN)
1018
+ return { element: el, name, kind, condition: entry[2], scope };
1019
+ if (kind === ATTR_KIND_TEMPLATE)
1020
+ return { element: el, name, kind, parts: entry[2], scope };
1021
+ return { element: el, name, kind: kind, path: entry[2] || '', scope };
1022
+ }
1023
+ $buildPathIndex() {
1024
+ if (!this.$root)
1025
+ return;
1026
+ const observableNames = this.$observableNames();
1027
+ const index = new Map();
1028
+ const ensure = (key) => {
1029
+ let e = index.get(key);
1030
+ if (!e) {
1031
+ e = { texts: [], attrs: [], conds: [], repeats: [] };
1032
+ index.set(key, e);
1033
+ }
1034
+ return e;
1035
+ };
1036
+ const keyFor = (path) => {
1037
+ const dot = path.indexOf('.');
1038
+ const root = dot > -1 ? path.slice(0, dot) : path;
1039
+ return observableNames.has(root) || this.$usesTemplateState(root) ? root : '*';
1040
+ };
1041
+ const isLocalPath = (path, scope) => {
1042
+ const dot = path.indexOf('.');
1043
+ const root = dot > -1 ? path.slice(0, dot) : path;
1044
+ let current = scope;
1045
+ while (current) {
1046
+ if (current.name === root)
1047
+ return true;
1048
+ current = current.parent;
1049
+ }
1050
+ return false;
1051
+ };
1052
+ const visit = (instance) => {
1053
+ for (const t of instance.texts) {
1054
+ if (t.parts) {
1055
+ for (const p of t.parts) {
1056
+ if (typeof p !== 'string' && !isLocalPath(p[0], t.scope)) {
1057
+ ensure(keyFor(p[0])).texts.push(t);
1058
+ }
1059
+ }
1060
+ }
1061
+ else if (t.path && !isLocalPath(t.path, t.scope)) {
1062
+ ensure(keyFor(t.path)).texts.push(t);
1063
+ }
1064
+ }
1065
+ for (const a of instance.attrs) {
1066
+ if (a.path && !isLocalPath(a.path, a.scope)) {
1067
+ ensure(keyFor(a.path)).attrs.push(a);
1068
+ }
1069
+ if (a.parts) {
1070
+ for (const p of a.parts) {
1071
+ if (typeof p !== 'string' && !isLocalPath(p[0], a.scope)) {
1072
+ ensure(keyFor(p[0])).attrs.push(a);
1073
+ }
1074
+ }
1075
+ }
1076
+ if (a.condition) {
1077
+ for (const p of a.condition[1]) {
1078
+ if (!isLocalPath(p, a.scope))
1079
+ ensure(keyFor(p)).attrs.push(a);
1080
+ }
1081
+ }
1082
+ }
1083
+ for (const c of instance.conds) {
1084
+ for (const p of c.condition[1]) {
1085
+ if (!isLocalPath(p, c.scope))
1086
+ ensure(keyFor(p)).conds.push(c);
1087
+ }
1088
+ if (c.instance)
1089
+ visit(c.instance);
1090
+ }
1091
+ for (const rep of instance.repeats) {
1092
+ if (!isLocalPath(rep.collection, rep.scope)) {
1093
+ ensure(keyFor(rep.collection)).repeats.push(rep);
1094
+ }
1095
+ for (let i = 0; i < rep.instances.length; i++) {
1096
+ visit(rep.instances[i]);
1097
+ }
1098
+ }
1099
+ };
1100
+ visit(this.$root);
1101
+ const wc = index.get('*');
1102
+ if (wc) {
1103
+ index.delete('*');
1104
+ this.$wildcardBindings = wc;
1105
+ }
1106
+ else {
1107
+ this.$wildcardBindings = null;
1108
+ }
1109
+ this.$pathIndex = index;
1110
+ }
1111
+ $updateBindings(texts, attrs, conds, repeats) {
1112
+ const gated = this.$hasUnknownScopes;
1113
+ for (let i = 0; i < texts.length; i++) {
1114
+ const binding = texts[i];
1115
+ if (!gated || !binding.scope || this.$scopeIsKnown(binding.scope))
1116
+ this.$patchText(binding);
1117
+ }
1118
+ for (let i = 0; i < attrs.length; i++) {
1119
+ const binding = attrs[i];
1120
+ if (!gated || !binding.scope || this.$scopeIsKnown(binding.scope))
1121
+ this.$patchAttr(binding);
1122
+ }
1123
+ for (let i = 0; i < conds.length; i++) {
1124
+ const binding = conds[i];
1125
+ if (!gated || !binding.scope || this.$scopeIsKnown(binding.scope))
1126
+ this.$toggleCond(binding);
1127
+ }
1128
+ for (let i = 0; i < repeats.length; i++) {
1129
+ const binding = repeats[i];
1130
+ if (!gated || !binding.scope || this.$scopeIsKnown(binding.scope))
1131
+ syncRepeat(this, binding);
1132
+ }
1133
+ }
1134
+ $scopeIsKnown(scope) {
1135
+ let frame = scope;
1136
+ while (frame) {
1137
+ if (frame.known === false)
1138
+ return false;
1139
+ frame = frame.parent;
1140
+ }
1141
+ return true;
1142
+ }
1143
+ $updateInstance(instance) {
1144
+ this.$updateBindings(instance.texts, instance.attrs, instance.conds, instance.repeats);
1145
+ }
1146
+ $patchText(b) {
1147
+ let val;
1148
+ if (b.parts) {
1149
+ val = this.$resolveParts(b.parts, b.scope);
1150
+ }
1151
+ else if (b.path) {
1152
+ const raw = this.$resolveValue(b.path, b.scope);
1153
+ val = raw == null ? '' : String(raw);
1154
+ }
1155
+ else {
1156
+ return;
1157
+ }
1158
+ if (b.raw && b.rawParent) {
1159
+ if (b.rawParent.innerHTML !== val)
1160
+ b.rawParent.innerHTML = val;
1161
+ }
1162
+ else {
1163
+ if (b.node.data !== val)
1164
+ b.node.data = val;
1165
+ }
1166
+ }
1167
+ $patchAttr(b) {
1168
+ const el = b.element;
1169
+ switch (b.kind) {
1170
+ case ATTR_KIND_COMPLEX: {
1171
+ const v = this.$resolveValue(b.path, b.scope);
1172
+ const target = el;
1173
+ const setStateKey = target[WEBUI_SET_STATE_KEY];
1174
+ if (typeof setStateKey === 'function') {
1175
+ if (setStateKey.call(el, b.name, v)) {
1176
+ const flush = target['$flushUpdates'];
1177
+ if (typeof flush === 'function')
1178
+ flush.call(el);
1179
+ break;
1180
+ }
1181
+ }
1182
+ target[b.name] = v;
1183
+ break;
1184
+ }
1185
+ case ATTR_KIND_BOOLEAN: {
1186
+ const show = b.condition[0](this.$resolver, b.scope);
1187
+ if (show)
1188
+ el.setAttribute(b.name, '');
1189
+ else
1190
+ el.removeAttribute(b.name);
1191
+ if ((b.name === 'checked' || b.name === 'selected' || b.name === 'disabled')
1192
+ && hasNativeLiveProperty(el, b.name)) {
1193
+ el[b.name] = show;
1194
+ }
1195
+ break;
1196
+ }
1197
+ case ATTR_KIND_TEMPLATE: {
1198
+ const v = this.$resolveParts(b.parts, b.scope);
1199
+ if (el.getAttribute(b.name) !== v)
1200
+ el.setAttribute(b.name, v);
1201
+ break;
1202
+ }
1203
+ default: {
1204
+ const v = this.$resolveValue(b.path, b.scope);
1205
+ const s = v == null ? '' : String(v);
1206
+ if ((b.name === 'checked' || b.name === 'selected')
1207
+ && hasNativeLiveProperty(el, b.name)) {
1208
+ el[b.name] = !!v && v !== 'false' && v !== '0';
1209
+ }
1210
+ else if (b.name === 'value' && hasNativeLiveProperty(el, b.name)) {
1211
+ const target = el;
1212
+ if (target.value !== s)
1213
+ target.value = s;
1214
+ }
1215
+ else {
1216
+ if (el.getAttribute(b.name) !== s)
1217
+ el.setAttribute(b.name, s);
1218
+ }
1219
+ break;
1220
+ }
1221
+ }
1222
+ }
1223
+ $toggleCond(c) {
1224
+ const show = c.condition[0](this.$resolver, c.scope);
1225
+ if (show) {
1226
+ let created = false;
1227
+ const container = c.anchor.parentNode;
1228
+ if (!c.instance) {
1229
+ c.instance = this.$createBlockInstance(c.blockIndex, c.scope, c.owner, container ?? undefined);
1230
+ created = c.instance !== null;
1231
+ }
1232
+ else {
1233
+ this.$updateInstance(c.instance);
1234
+ }
1235
+ if (c.instance && container) {
1236
+ this.$insertInstanceAfter(c.anchor, container, c.instance);
1237
+ if (created)
1238
+ this.$changeStructure();
1239
+ }
1240
+ }
1241
+ else if (c.instance) {
1242
+ this.$removeInstance(c.instance);
1243
+ c.instance = null;
1244
+ this.$changeStructure(c.owner);
1245
+ }
1246
+ }
1247
+ $resolveValue(path, scope) {
1248
+ let frame = scope;
1249
+ while (frame) {
1250
+ if (path === frame.name)
1251
+ return frame.value;
1252
+ if (path.length > frame.name.length && path.charCodeAt(frame.name.length) === 46 && path.startsWith(frame.name)) {
1253
+ return dotWalk(frame.value, path, frame.name.length + 1);
1254
+ }
1255
+ frame = frame.parent;
1256
+ }
1257
+ const dot = path.indexOf('.');
1258
+ if (dot === -1)
1259
+ return this.$resolveComponentRoot(path);
1260
+ return dotWalk(this.$resolveComponentRoot(path.substring(0, dot)), path, dot + 1);
1261
+ }
1262
+ $hasStateRoot(path, scope) {
1263
+ let frame = scope;
1264
+ while (frame) {
1265
+ if (path === frame.name
1266
+ || (path.length > frame.name.length
1267
+ && path.charCodeAt(frame.name.length) === 46
1268
+ && path.startsWith(frame.name))) {
1269
+ return frame.value !== undefined;
1270
+ }
1271
+ frame = frame.parent;
1272
+ }
1273
+ const dot = path.indexOf('.');
1274
+ const root = dot === -1 ? path : path.substring(0, dot);
1275
+ return hasAuthoredMember(this, root)
1276
+ || (this.$templateState !== null
1277
+ && Object.prototype.hasOwnProperty.call(this.$templateState, root));
1278
+ }
1279
+ $resolveComponentRoot(root) {
1280
+ const instance = this;
1281
+ if (this.$templateState &&
1282
+ Object.prototype.hasOwnProperty.call(this.$templateState, root) &&
1283
+ !hasAuthoredMember(this, root)) {
1284
+ return this.$templateState[root];
1285
+ }
1286
+ return instance[root];
1287
+ }
1288
+ $resolveParts(parts, scope) {
1289
+ let result = '';
1290
+ for (let i = 0; i < parts.length; i++) {
1291
+ const p = parts[i];
1292
+ if (typeof p === 'string') {
1293
+ result += p;
1294
+ continue;
1295
+ }
1296
+ const v = this.$resolveValue(p[0], scope);
1297
+ result += v == null ? '' : String(v);
1298
+ }
1299
+ return result;
1300
+ }
1301
+ $block(blockIndex) {
1302
+ return this.$meta?.b?.[blockIndex];
1303
+ }
1304
+ $createBlockInstance(blockIndex, scope, parent, container) {
1305
+ const bm = this.$block(blockIndex);
1306
+ if (!bm)
1307
+ return null;
1308
+ const wrapper = this.$createStagingRoot(bm);
1309
+ const inst = this.$wire(wrapper, bm, scope);
1310
+ inst.parent = parent;
1311
+ inst.nodes = childNodesArray(wrapper);
1312
+ this.$updateInstance(inst);
1313
+ if (inst.repeats.length !== 0 || inst.conds.length !== 0) {
1314
+ inst.nodes = childNodesArray(wrapper);
1315
+ this.$replaceInstanceContainer(inst, wrapper, container ?? null);
1316
+ }
1317
+ else if (container) {
1318
+ inst.container = container;
1319
+ }
1320
+ return inst;
1321
+ }
1322
+ $removeInstance(instance) {
1323
+ this.$disposeInstance(instance, true);
1324
+ }
1325
+ $disposeInstance(root, removeNodes) {
1326
+ const stack = [root];
1327
+ while (stack.length > 0) {
1328
+ const instance = stack.pop();
1329
+ if (!instance)
1330
+ continue;
1331
+ const cleanups = instance.cleanups;
1332
+ if (cleanups) {
1333
+ for (const cleanup of cleanups)
1334
+ cleanup();
1335
+ cleanups.length = 0;
1336
+ }
1337
+ if (removeNodes) {
1338
+ for (const node of instance.nodes)
1339
+ node.parentNode?.removeChild(node);
1340
+ }
1341
+ for (const binding of instance.conds) {
1342
+ if (binding.instance)
1343
+ stack.push(binding.instance);
1344
+ binding.instance = null;
1345
+ }
1346
+ for (const repeat of instance.repeats) {
1347
+ for (const child of repeat.instances)
1348
+ stack.push(child);
1349
+ this.$clearRepeatBinding(repeat);
1350
+ }
1351
+ this.$clearInstance(instance);
1352
+ }
1353
+ }
1354
+ $compactNodeArray(instance) {
1355
+ const container = instance.container;
1356
+ if (!container)
1357
+ return;
1358
+ const nodes = instance.nodes;
1359
+ let write = 0;
1360
+ for (let read = 0; read < nodes.length; read++) {
1361
+ const node = nodes[read];
1362
+ if (node.parentNode === container) {
1363
+ nodes[write] = node;
1364
+ write++;
1365
+ }
1366
+ }
1367
+ nodes.length = write;
1368
+ }
1369
+ $changeStructure(removedFrom) {
1370
+ if (removedFrom) {
1371
+ let current = removedFrom;
1372
+ while (current) {
1373
+ this.$compactNodeArray(current);
1374
+ current = current.parent;
1375
+ }
1376
+ }
1377
+ this.$pathIndex = undefined;
1378
+ this.$wildcardBindings = undefined;
1379
+ }
1380
+ $insertInstanceAfter(cursor, container, instance) {
1381
+ this.$replaceInstanceContainer(instance, instance.container, container);
1382
+ const nodes = instance.nodes;
1383
+ if (nodes.length === 0)
1384
+ return cursor;
1385
+ const ref = cursor ? cursor.nextSibling : container.firstChild;
1386
+ if (nodes[0] === ref)
1387
+ return nodes[nodes.length - 1];
1388
+ for (let i = 0; i < nodes.length; i++)
1389
+ container.insertBefore(nodes[i], ref);
1390
+ return nodes[nodes.length - 1];
1391
+ }
1392
+ }