@lmjs/core 2.0.0 → 2.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/dist/lumenjs-core-with-plugins.js +6 -6
- package/dist/lumenjs-core.js +7 -7
- package/package.json +1 -1
- package/src/_re.js +469 -67
- package/src/dom-shim.js +167 -0
- package/src/index-bootstrap.js +7 -1
- package/src/lstnrs.js +20 -2
- package/src/walk.js +101 -13
- package/vendor/reconnecting-websocket.js +25 -0
package/src/_re.js
CHANGED
|
@@ -17,6 +17,7 @@ class _v {
|
|
|
17
17
|
static name;
|
|
18
18
|
static type;
|
|
19
19
|
static vars;
|
|
20
|
+
static fns;
|
|
20
21
|
static rvs;
|
|
21
22
|
static _pv;
|
|
22
23
|
static mx;
|
|
@@ -29,6 +30,16 @@ class _v {
|
|
|
29
30
|
this.hst = obj.hst ?? [];
|
|
30
31
|
this.views = obj.views ?? [];
|
|
31
32
|
this.vars = obj.vars ?? {};
|
|
33
|
+
// 2026-09-17: per-instance event-handler functions (see walk.js's
|
|
34
|
+
// matching addition, and lstnrs.js's evalEvAttr) — a subview's own
|
|
35
|
+
// `function bump(){}` still becomes a real `window.bump` too (plain
|
|
36
|
+
// <script>-tag semantics, unchanged), but multiple mounted
|
|
37
|
+
// instances of the same subview file all define a same-named
|
|
38
|
+
// function, so window.bump silently becomes whichever instance's
|
|
39
|
+
// script ran last — every instance's @click ended up calling THAT
|
|
40
|
+
// one instance's handler regardless of which was actually clicked.
|
|
41
|
+
// .fns is this instance's own, unambiguous copy.
|
|
42
|
+
this.fns = obj.fns ?? {};
|
|
32
43
|
this.rvs = obj.rvs ?? {};
|
|
33
44
|
this._pv = obj._pv ?? null;
|
|
34
45
|
this.mx = obj.mx ?? [];
|
|
@@ -110,6 +121,67 @@ function reportLumenError(info) {
|
|
|
110
121
|
|
|
111
122
|
function _x(_x) {
|
|
112
123
|
var currPath = [];
|
|
124
|
+
// 2026-09-17: generalized from a position-specific check
|
|
125
|
+
// (`currPath[1] == "vars"`, only ever matched a flat `_vt.<root>.vars`
|
|
126
|
+
// write) to a real owner-walk — needed for per-instance subview
|
|
127
|
+
// scoping (`_vt.View.views[i].vars`, and its nested form
|
|
128
|
+
// `_vt.View.views[0].views[2].vars`), where "vars" can sit at any
|
|
129
|
+
// depth. `path` is currPath *without* the trailing "vars" segment.
|
|
130
|
+
// `Global` is a real special case, not a bug in this generalization:
|
|
131
|
+
// it has no `._re` of its own (see the object literal below — just
|
|
132
|
+
// `{vars:{}}`) since Global-var writes need to re-render whichever
|
|
133
|
+
// view is *currently* looking at that value, exactly like the
|
|
134
|
+
// pre-existing behavior this preserves. Everything else (View itself,
|
|
135
|
+
// or any views[i]/views[i].views[j]/... chain) is walked against the
|
|
136
|
+
// raw (non-proxied) tree — using the outer `_x` param directly, not
|
|
137
|
+
// the `_vt` proxy — so this traversal itself can't re-enter these
|
|
138
|
+
// traps and corrupt the in-flight currPath.
|
|
139
|
+
function _dispatchVarsUpdate(key) {
|
|
140
|
+
// "vars" can be anywhere in currPath, not just at index 1 — find
|
|
141
|
+
// it. The var name to update() is whatever comes right after it
|
|
142
|
+
// in currPath (e.g. `_vt.View.vars["obj"].nested = x` — the write
|
|
143
|
+
// itself is on "obj"'s own proxy, so `key` here is "nested", but
|
|
144
|
+
// the reactive var is "obj", one segment before it in currPath);
|
|
145
|
+
// if "vars" is the *last* segment, there's no next segment yet —
|
|
146
|
+
// that means this write IS the var (`_vt.View.vars["x"] = ...`),
|
|
147
|
+
// so `key` itself (the set() trap's own key) is the var name.
|
|
148
|
+
let varsIdx = currPath.indexOf("vars");
|
|
149
|
+
if (varsIdx === -1) return;
|
|
150
|
+
let varName = varsIdx < currPath.length - 1 ? currPath[varsIdx + 1] : key;
|
|
151
|
+
let rootPath = currPath.slice(0, varsIdx);
|
|
152
|
+
// Global has no ._re of its own (see its plain {vars:{}} literal
|
|
153
|
+
// below) — a Global write always re-renders whichever view is
|
|
154
|
+
// currently looking at it, exactly like the pre-existing behavior
|
|
155
|
+
// this preserves, for both the flat and nested-property case.
|
|
156
|
+
if (rootPath[0] === "Global") {
|
|
157
|
+
// 2026-09-18, real gap found and fixed: this file's own design
|
|
158
|
+
// comment above _vt's declaration says a Global var should be
|
|
159
|
+
// usable by "ordinary view code" just by referencing the
|
|
160
|
+
// name — but nothing ever made that true for a VIEW's own
|
|
161
|
+
// <script> statements (as opposed to mustaches/:if conditions,
|
|
162
|
+
// which already fall back to _vt.Global.vars via getVal()/
|
|
163
|
+
// evalExp()'s own merge). A view's compiled script only
|
|
164
|
+
// rewrites references to names IT ITSELF declares — it has no
|
|
165
|
+
// way to know index.js declared some other name as Global, so
|
|
166
|
+
// a bare reference to it (e.g. `rx.post(...)` where `rx =
|
|
167
|
+
// new RX(...)` was set in index.js's init()) resolved via the
|
|
168
|
+
// real JS scope chain, which only ever finds a real `window.rx`
|
|
169
|
+
// if something put it there — nothing did. Mirroring every
|
|
170
|
+
// Global write onto `window[varName]` too makes plain bare-
|
|
171
|
+
// identifier references resolve correctly and naturally,
|
|
172
|
+
// with zero per-script rewriting needed.
|
|
173
|
+
try {
|
|
174
|
+
if (typeof window !== 'undefined') window[varName] = _x.Global.vars[varName];
|
|
175
|
+
} catch (e) { }
|
|
176
|
+
if (_vt.View._re) _vt.View._re.update(varName);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
let owner = _x;
|
|
180
|
+
for (let i = 0; i < rootPath.length && owner; i++) {
|
|
181
|
+
owner = owner[rootPath[i]];
|
|
182
|
+
}
|
|
183
|
+
if (owner && owner._re) owner._re.update(varName);
|
|
184
|
+
}
|
|
113
185
|
const handler = {
|
|
114
186
|
get(target, key) {
|
|
115
187
|
if (key == "__isProxy") return true;
|
|
@@ -136,31 +208,12 @@ function _x(_x) {
|
|
|
136
208
|
// var _View = currPath[1] == "views" ? currPath[2] : "";
|
|
137
209
|
|
|
138
210
|
try {
|
|
139
|
-
// cl(["hiiiiii", currPath, key, value]);
|
|
140
211
|
// `_vt.View._re` genuinely doesn't exist yet during
|
|
141
212
|
// index.js's own bootstrap (before any view has mounted —
|
|
142
213
|
// see index-bootstrap.js) — that's the normal case now,
|
|
143
|
-
// not an error, so it's guarded
|
|
144
|
-
// throw into the catch below
|
|
145
|
-
|
|
146
|
-
if (_vt.View._re) _vt.View._re.update(key);
|
|
147
|
-
} else if (currPath.length > 2 && currPath[1] == "vars") {
|
|
148
|
-
let ky = clone(currPath[2]);
|
|
149
|
-
if (_vt.View._re) _vt.View._re.update(ky);
|
|
150
|
-
}
|
|
151
|
-
// if (currPath.length) {
|
|
152
|
-
// _vt.View._re.set(key, value);
|
|
153
|
-
// }
|
|
154
|
-
// cl(currPath, _View);
|
|
155
|
-
// eval("var kira = " + "_vt." + currPath.join("."));
|
|
156
|
-
// if (kira.__isProxy) {
|
|
157
|
-
// currPath.push(key);
|
|
158
|
-
// eval("kira = " + "_vt." + currPath.join("."));
|
|
159
|
-
// }
|
|
160
|
-
|
|
161
|
-
// if (kira) {
|
|
162
|
-
// // if (_el.data('_re')) _el.data('_re').set(key, value);
|
|
163
|
-
// }
|
|
214
|
+
// not an error, so it's guarded (inside _dispatchVarsUpdate)
|
|
215
|
+
// rather than left to throw into the catch below.
|
|
216
|
+
_dispatchVarsUpdate(key);
|
|
164
217
|
} catch (e) {
|
|
165
218
|
cl(e);
|
|
166
219
|
}
|
|
@@ -175,31 +228,7 @@ function _x(_x) {
|
|
|
175
228
|
delete target[key];
|
|
176
229
|
|
|
177
230
|
try {
|
|
178
|
-
|
|
179
|
-
// `_vt.View._re` genuinely doesn't exist yet during
|
|
180
|
-
// index.js's own bootstrap (before any view has mounted —
|
|
181
|
-
// see index-bootstrap.js) — that's the normal case now,
|
|
182
|
-
// not an error, so it's guarded here rather than left to
|
|
183
|
-
// throw into the catch below on every such write.
|
|
184
|
-
if (currPath.length == 2 && currPath[1] == "vars") {
|
|
185
|
-
if (_vt.View._re) _vt.View._re.update(key);
|
|
186
|
-
} else if (currPath.length > 2 && currPath[1] == "vars") {
|
|
187
|
-
let ky = clone(currPath[2]);
|
|
188
|
-
if (_vt.View._re) _vt.View._re.update(ky);
|
|
189
|
-
}
|
|
190
|
-
// if (currPath.length) {
|
|
191
|
-
// _vt.View._re.set(key, value);
|
|
192
|
-
// }
|
|
193
|
-
// cl(currPath, _View);
|
|
194
|
-
// eval("var kira = " + "_vt." + currPath.join("."));
|
|
195
|
-
// if (kira.__isProxy) {
|
|
196
|
-
// currPath.push(key);
|
|
197
|
-
// eval("kira = " + "_vt." + currPath.join("."));
|
|
198
|
-
// }
|
|
199
|
-
|
|
200
|
-
// if (kira) {
|
|
201
|
-
// // if (_el.data('_re')) _el.data('_re').set(key, value);
|
|
202
|
-
// }
|
|
231
|
+
_dispatchVarsUpdate(key);
|
|
203
232
|
} catch (e) {
|
|
204
233
|
cl(e);
|
|
205
234
|
}
|
|
@@ -240,9 +269,27 @@ function _x(_x) {
|
|
|
240
269
|
// its vars (V1's documented convention for app-wide state like `isAdmin`)
|
|
241
270
|
// need to outlive any single view. concatVarsAtLevel() below merges it into
|
|
242
271
|
// every scope lookup so ordinary view code can just reference the name.
|
|
272
|
+
// 2026-09-17, real bug found and fixed: `.fns` was missing here (only
|
|
273
|
+
// `.vars` existed) — `walk.js`'s `_fnRegisterStatement()` (added for
|
|
274
|
+
// per-instance subview scoping, see the per-instance-scoping work above)
|
|
275
|
+
// unconditionally builds `<targetRoot>.fns["name"] = name;` for EVERY
|
|
276
|
+
// top-level function declaration, regardless of target — including
|
|
277
|
+
// `targetKey: "Global"`, used to compile index.js/project scripts. Any
|
|
278
|
+
// real project with so much as one function declaration in index.js (an
|
|
279
|
+
// extremely common pattern) crashed with "Cannot set properties of
|
|
280
|
+
// undefined (setting '<fnName>')" the moment that line ran. Nearly
|
|
281
|
+
// invisible in `lm serve` dev mode — index-bootstrap.js's synchronous XHR
|
|
282
|
+
// fetch-and-eval of the compiled index.js is wrapped in a try/catch that
|
|
283
|
+
// only `console.log`s the failure (never `console.error`/throws), so nothing
|
|
284
|
+
// looked broken as long as the crash landed after anything load-bearing
|
|
285
|
+
// (Reactor()) in that same script. Fatal in a real `lm build` production
|
|
286
|
+
// bundle, where the compiled index.js is concatenated inline, unguarded —
|
|
287
|
+
// an uncaught exception there aborts every remaining statement in the
|
|
288
|
+
// whole bundle file, breaking the entire app on every real production
|
|
289
|
+
// site with this pattern. Found deploying lumenjs.com's own real build.
|
|
243
290
|
let _vt = _x({
|
|
244
291
|
"View": new _v({}),
|
|
245
|
-
"Global": { "vars": {} }
|
|
292
|
+
"Global": { "vars": {}, "fns": {} }
|
|
246
293
|
});
|
|
247
294
|
|
|
248
295
|
class _lm {
|
|
@@ -388,7 +435,6 @@ class _lm {
|
|
|
388
435
|
} else {
|
|
389
436
|
effect.nd.setAttribute(effect.name, this.render(effect));
|
|
390
437
|
if (effect.name == "view") {
|
|
391
|
-
cl("We Must Trigger View Change", effect, this.render(effect));
|
|
392
438
|
effect.nd.subPath = this.render(effect);
|
|
393
439
|
renderView(this.render(effect), true, {});
|
|
394
440
|
}
|
|
@@ -407,6 +453,19 @@ class _lm {
|
|
|
407
453
|
this.updateCXRs();
|
|
408
454
|
this.updateLXRs();
|
|
409
455
|
this.updateVXRs();
|
|
456
|
+
|
|
457
|
+
// 2026-09-18: same Reactor({tick}) fix as update() below — also
|
|
458
|
+
// fired on the very first render (not just subsequent reactive
|
|
459
|
+
// updates), since a real tick() use (e.g. hljs.highlightAll())
|
|
460
|
+
// needs to run on initial content too, not just after the first
|
|
461
|
+
// change.
|
|
462
|
+
if (typeof appSettings !== 'undefined' && appSettings && typeof appSettings.tick === 'function') {
|
|
463
|
+
try {
|
|
464
|
+
appSettings.tick();
|
|
465
|
+
} catch (e) {
|
|
466
|
+
cl(e);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
410
469
|
}
|
|
411
470
|
|
|
412
471
|
chainConnected(cx) {
|
|
@@ -423,13 +482,25 @@ class _lm {
|
|
|
423
482
|
let subsNames = [];
|
|
424
483
|
for (let i = 0; i < this.view.views.length; i++) {
|
|
425
484
|
const _view = this.view.views[i];
|
|
426
|
-
cl("We Must Render A SubView", _view, subsNames.includes(_view.subPath), subsNames, _view.subPath);
|
|
427
485
|
if (!subsNames.includes(_view.subPath)) subsNames.push(_view.subPath);
|
|
428
486
|
}
|
|
429
487
|
// cl(subsNames);
|
|
488
|
+
// 2026-09-17: real bug found and fixed — this used to always call
|
|
489
|
+
// the plain, no-args renderView(n, true, {}), which only ever
|
|
490
|
+
// searches _vt.View.views (the MAIN view's own array). That's
|
|
491
|
+
// correct when `this` IS the main view, but updateVXRs() also
|
|
492
|
+
// runs on every SUBVIEW instance's own _re (renderAll() calls it
|
|
493
|
+
// unconditionally) — for a subview that itself hosts further
|
|
494
|
+
// subviews, `this.view.views` holds THOSE, not the main view's,
|
|
495
|
+
// so the old call could never find them: nested subviews never
|
|
496
|
+
// rendered at all. Passing this.view.views + this.view.scopePath
|
|
497
|
+
// (set once, in renderHST(), when this instance itself was
|
|
498
|
+
// rendered — ["View"] for the main view, unchanged) lets
|
|
499
|
+
// renderView() search the RIGHT array and address the RIGHT
|
|
500
|
+
// nested scope regardless of how deep this instance is.
|
|
430
501
|
for (let i = 0; i < subsNames.length; i++) {
|
|
431
502
|
const n = subsNames[i];
|
|
432
|
-
renderView(n, true, {});
|
|
503
|
+
renderView(n, true, {}, 'views', this.view.views, this.view.scopePath || ["View"]);
|
|
433
504
|
}
|
|
434
505
|
}
|
|
435
506
|
|
|
@@ -531,7 +602,6 @@ class _lm {
|
|
|
531
602
|
} else {
|
|
532
603
|
effect.nd.setAttribute(effect.name, this.render(effect));
|
|
533
604
|
if (effect.name == "view") {
|
|
534
|
-
cl("We Must Trigger View Change", effect, this.render(effect));
|
|
535
605
|
effect.nd.subPath = this.render(effect);
|
|
536
606
|
renderView(this.render(effect), true, {});
|
|
537
607
|
}
|
|
@@ -551,6 +621,24 @@ class _lm {
|
|
|
551
621
|
const sbscr = this.sbscrbs[sbscsi];
|
|
552
622
|
sbscr._re.update(k);
|
|
553
623
|
}
|
|
624
|
+
|
|
625
|
+
// 2026-09-18: Reactor({tick}) — documented ("runs after every
|
|
626
|
+
// re-render, anywhere", lumenjs-spec.md §5.4/llms.md §3) but never
|
|
627
|
+
// actually called anywhere in this runtime — see this fix's fuller
|
|
628
|
+
// comment at Reactor()'s own definition (vendor/reconnecting-
|
|
629
|
+
// websocket.js) for how this was found. update() is the real
|
|
630
|
+
// per-reactive-change render point (unlike renderAll(), which only
|
|
631
|
+
// ever runs once per _lm instance, at initial mount) — this is
|
|
632
|
+
// where "after every re-render, anywhere" actually lives. Guarded
|
|
633
|
+
// on appSettings existing since update() can fire during index.js's
|
|
634
|
+
// own bootstrap, before Reactor() has run.
|
|
635
|
+
if (typeof appSettings !== 'undefined' && appSettings && typeof appSettings.tick === 'function') {
|
|
636
|
+
try {
|
|
637
|
+
appSettings.tick();
|
|
638
|
+
} catch (e) {
|
|
639
|
+
cl(e);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
554
642
|
}
|
|
555
643
|
|
|
556
644
|
async updateLXRs(k) {
|
|
@@ -632,12 +720,61 @@ class _lm {
|
|
|
632
720
|
vx['value'] = marray[index]['value'];
|
|
633
721
|
} else {
|
|
634
722
|
if (forX['as']['k']) vx[forX['as']['k']] = marray[index];
|
|
723
|
+
// 2026-09-17, real bug found and fixed: a bare
|
|
724
|
+
// `:for="items"` (no `as item` alias) is
|
|
725
|
+
// documented — and was V1's real, working
|
|
726
|
+
// behavior — to spread each item's own object
|
|
727
|
+
// keys directly into loop scope, so
|
|
728
|
+
// `items = [{number:1}]` lets a mustache read
|
|
729
|
+
// `{{number}}` with no alias at all. Nothing in
|
|
730
|
+
// this branch ever did that spread — `vx` had
|
|
731
|
+
// no alias key AND no spread keys, so a bare
|
|
732
|
+
// mustache like this always evaluated as an
|
|
733
|
+
// undefined identifier and silently rendered
|
|
734
|
+
// blank. Only spread for a plain-object item —
|
|
735
|
+
// a primitive (string/number) array item has no
|
|
736
|
+
// keys to spread and is read via the untouched
|
|
737
|
+
// alias path instead.
|
|
738
|
+
else if (marray[index] && typeof marray[index] === 'object') {
|
|
739
|
+
for (const k2 in marray[index]) {
|
|
740
|
+
if (Object.prototype.hasOwnProperty.call(marray[index], k2)) {
|
|
741
|
+
vx[k2] = marray[index][k2];
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
}
|
|
635
745
|
}
|
|
636
746
|
// cl(forX, vx);
|
|
637
747
|
// cl("VXXXX", vx, this);
|
|
638
748
|
|
|
639
749
|
if (forIf) {
|
|
640
|
-
|
|
750
|
+
// 2026-09-17, real bug found and fixed:
|
|
751
|
+
// evalExp()'s 2nd param is a NAMES array to
|
|
752
|
+
// look up in _vt.View.vars/_vt.Global.vars (see
|
|
753
|
+
// its other real call site, `this.evalExp(cx.content,
|
|
754
|
+
// this.reactiveVariables)`) — but `vx` here is
|
|
755
|
+
// a VALUES object (the just-built loop-item
|
|
756
|
+
// scope), not a names array. `vars.length` on a
|
|
757
|
+
// plain object is undefined, so evalExp's own
|
|
758
|
+
// `for (i=0; i<vars.length; i++)` loop never
|
|
759
|
+
// ran even once — every `:for-if` condition
|
|
760
|
+
// referencing the loop item (`item.value > 10`,
|
|
761
|
+
// the exact documented example) always
|
|
762
|
+
// evaluated against an empty scope and either
|
|
763
|
+
// threw (caught, filtered out) or read
|
|
764
|
+
// `undefined`. evalExp() already has a real,
|
|
765
|
+
// working mechanism for exactly this — it
|
|
766
|
+
// merges `this.vrs` (the loop-scope override
|
|
767
|
+
// object) at the highest priority — so route
|
|
768
|
+
// through that instead of a second, broken
|
|
769
|
+
// parameter shape.
|
|
770
|
+
let _prevVrs = this.vrs;
|
|
771
|
+
this.vrs = vx;
|
|
772
|
+
let isTrue;
|
|
773
|
+
try {
|
|
774
|
+
isTrue = this.evalExp(forIf, []);
|
|
775
|
+
} finally {
|
|
776
|
+
this.vrs = _prevVrs;
|
|
777
|
+
}
|
|
641
778
|
if (!isTrue) continue;
|
|
642
779
|
}
|
|
643
780
|
|
|
@@ -670,7 +807,29 @@ class _lm {
|
|
|
670
807
|
|
|
671
808
|
// cl("ATR", arrayToRender);
|
|
672
809
|
let oldATR = cx.atr;
|
|
673
|
-
cx.atr
|
|
810
|
+
// 2026-09-17, real bug found and fixed: cx.atr used to
|
|
811
|
+
// store a reference to arrayToRender's own item objects,
|
|
812
|
+
// not an independent snapshot — arrayToRender's items are
|
|
813
|
+
// the actual current elements of the reactive array (e.g.
|
|
814
|
+
// _vt.View.vars["features"]), so a completely ordinary
|
|
815
|
+
// update pattern (mutate an item in place inside .map(),
|
|
816
|
+
// return the same reference — `f.done = !f.done; return
|
|
817
|
+
// f;`, not "rebuild a new object") left oldATR and the
|
|
818
|
+
// next render's arrayToRender pointing at the exact same,
|
|
819
|
+
// already-mutated objects by the time compareArrays()
|
|
820
|
+
// ran. deepCompare()'s JSON.stringify comparison was
|
|
821
|
+
// never wrong — it was comparing an object against
|
|
822
|
+
// itself, so every 'update' action was silently lost and
|
|
823
|
+
// the DOM never re-rendered. clone() (already used a few
|
|
824
|
+
// lines above for `vals`) gives cx.atr a true independent
|
|
825
|
+
// snapshot instead. Found clicking a real @click-toggled
|
|
826
|
+
// :for item on lumenjs.com's own real V2 homepage — no
|
|
827
|
+
// existing test used an in-place item mutation, only
|
|
828
|
+
// whole-array reassignment with new items (see render-
|
|
829
|
+
// hooks.integration.test.js's `images = [...,'c']`),
|
|
830
|
+
// which never exposed this since a length change always
|
|
831
|
+
// hits compareArrays()'s add/remove branches regardless.
|
|
832
|
+
cx.atr = clone(arrayToRender);
|
|
674
833
|
// cl(oldATR, arrayToRender);
|
|
675
834
|
|
|
676
835
|
const actions = this.compareArrays(oldATR, arrayToRender);
|
|
@@ -924,6 +1083,21 @@ class _lm {
|
|
|
924
1083
|
vars[ky] = this.vrs[ky]
|
|
925
1084
|
}
|
|
926
1085
|
}
|
|
1086
|
+
// 2026-09-17: per-instance subview scoping — a subview's own
|
|
1087
|
+
// declared vars live on `this.view.vars` (wired up in
|
|
1088
|
+
// renderHST()/renderView() to be the SAME object its compiled
|
|
1089
|
+
// script's `_vt.View.views[i].vars[...]` writes reach), not
|
|
1090
|
+
// in the flat `_vt.View.vars` checked above (that's still
|
|
1091
|
+
// only ever the MAIN view). Checked last/highest-priority,
|
|
1092
|
+
// same shadowing shape as the `this.vrs` merge just above —
|
|
1093
|
+
// an instance's own var wins over anything it inherited.
|
|
1094
|
+
if (this.view && this.view.vars) {
|
|
1095
|
+
for (const ky in this.view.vars) {
|
|
1096
|
+
if (Object.prototype.hasOwnProperty.call(this.view.vars, ky)) {
|
|
1097
|
+
vars[ky] = this.view.vars[ky];
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
927
1101
|
} catch (e) {
|
|
928
1102
|
cl(e);
|
|
929
1103
|
}
|
|
@@ -1157,16 +1331,97 @@ class _lm {
|
|
|
1157
1331
|
createEl(tag, attrs, children, events, doc) {
|
|
1158
1332
|
const _el = document.createElement(tag);
|
|
1159
1333
|
|
|
1334
|
+
// 2026-09-17: every element knows which _lm instance rendered it —
|
|
1335
|
+
// needed so a click on it can resolve its @click handler against
|
|
1336
|
+
// the RIGHT instance's own .fns (see lstnrs.js's evalEvAttr), and
|
|
1337
|
+
// (below) so an @init handler on a subview mount resolves against
|
|
1338
|
+
// ITS creator's own functions/scope, not window. Stamped before
|
|
1339
|
+
// anything below that might need it. Non-enumerable: real bug
|
|
1340
|
+
// found and fixed — an ordinary DOM element has no own enumerable
|
|
1341
|
+
// properties by default, so JSON.stringify(someElement) safely
|
|
1342
|
+
// produced "{}" everywhere this codebase already did that (e.g. a
|
|
1343
|
+
// real test asserting on JSON.stringify(view._slots), real DOM
|
|
1344
|
+
// nodes). An ENUMERABLE back-reference here closes a genuine
|
|
1345
|
+
// cycle (element -> _ownerRe -> that instance's own _RealDOM ->
|
|
1346
|
+
// the same element) and breaks every one of those call sites. Defined this way
|
|
1347
|
+
// instead of a plain `=` assignment so it's reachable
|
|
1348
|
+
// (`el._ownerRe`) but invisible to JSON.stringify/for-in/
|
|
1349
|
+
// Object.keys, matching how a parent-pointer is normally done.
|
|
1350
|
+
Object.defineProperty(_el, '_ownerRe', { value: this, enumerable: false, configurable: true, writable: true });
|
|
1351
|
+
|
|
1160
1352
|
_el.isSub = false;
|
|
1161
1353
|
if (attrs.hasOwnProperty('view')) {
|
|
1162
1354
|
_el.isSub = true;
|
|
1163
1355
|
_el.subPath = attrs['view'];
|
|
1356
|
+
// 2026-09-17: per-instance subview scoping. `views[]` used to
|
|
1357
|
+
// hold just the raw element — every instance of the same
|
|
1358
|
+
// subview file shared one flat _vt.View.vars, so two mounts of
|
|
1359
|
+
// the same .view file collided on the same variable slots (a
|
|
1360
|
+
// real, confirmed bug — clicking one instance changed a value
|
|
1361
|
+
// neither instance's DOM reflected). DOM elements already
|
|
1362
|
+
// carry arbitrary extra JS properties throughout this file
|
|
1363
|
+
// (.isSub, .subPath, .events) — .vars/.views here follow that
|
|
1364
|
+
// same convention rather than a new parallel structure. .vars
|
|
1365
|
+
// is this instance's own scope, addressed at
|
|
1366
|
+
// _vt.View.views[i].vars once renderHST() wires up the path
|
|
1367
|
+
// (see renderView()); .views lets this instance itself host
|
|
1368
|
+
// further nested subviews the same way.
|
|
1369
|
+
_el.vars = {};
|
|
1370
|
+
_el.views = [];
|
|
1371
|
+
_el.fns = {};
|
|
1372
|
+
// 2026-09-17: `@init="fn"` — the actual, decided mechanism
|
|
1373
|
+
// for giving a subview mount its own data at creation time
|
|
1374
|
+
// (replaces an earlier, narrower `:data="name"` attempt —
|
|
1375
|
+
// reusing the existing @before-render/@after-render hook
|
|
1376
|
+
// machinery instead of a bespoke attribute, per real
|
|
1377
|
+
// discussion of real use cases: a subview file mounted
|
|
1378
|
+
// multiple times needing distinct config per instance — the
|
|
1379
|
+
// same widget showing a different crypto coin per mount, or
|
|
1380
|
+
// a :for item needing its own data slice). `fn`'s return
|
|
1381
|
+
// value (a plain object) is merged into this instance's own
|
|
1382
|
+
// `.vars`, so it can hand over multiple named values at once
|
|
1383
|
+
// — not tied to matching a parent variable's name the way
|
|
1384
|
+
// `:data` was. Reads an ancestor's own variable by name
|
|
1385
|
+
// still needs no special syntax at all (getVal()/evalExp()'s
|
|
1386
|
+
// existing fallthrough already covers that); @init is for
|
|
1387
|
+
// data that's computed/selected specifically for this one
|
|
1388
|
+
// mount. fireRenderHook() needed a real fix first (used to
|
|
1389
|
+
// discard its handler's return value entirely) — see its own
|
|
1390
|
+
// comment. Deliberately one-way — a subview writing back to
|
|
1391
|
+
// a PARENT variable (a steps-form wizard sharing accumulated
|
|
1392
|
+
// state across steps, for instance) is a real, different,
|
|
1393
|
+
// not-yet-designed mechanism, not what this covers.
|
|
1394
|
+
//
|
|
1395
|
+
// Calls evalEvAttr() directly rather than fireRenderHook()
|
|
1396
|
+
// (which every other @before-render/@after-render call site
|
|
1397
|
+
// uses): createEl() itself isn't async, and this result has
|
|
1398
|
+
// to be ready synchronously, before the subview actually
|
|
1399
|
+
// renders (a later, separate, real async call). An @init
|
|
1400
|
+
// handler that returns a Promise can't be awaited here —
|
|
1401
|
+
// deliberately unsupported for now, not silently broken:
|
|
1402
|
+
// only a plain object return is used.
|
|
1403
|
+
if (doc && doc.evs && doc.evs.hasOwnProperty('@init')) {
|
|
1404
|
+
let _initAttr = doc.evs['@init'];
|
|
1405
|
+
if (_initAttr) {
|
|
1406
|
+
// 3rd arg: the CALLING scope's own loop-local bindings
|
|
1407
|
+
// (e.g. `item` from `:for="items as item"`) — these
|
|
1408
|
+
// aren't real JS variables an @init handler could
|
|
1409
|
+
// otherwise close over (it's an ordinary top-level
|
|
1410
|
+
// function, not defined inside the loop), so without
|
|
1411
|
+
// this there'd be no way for it to know which
|
|
1412
|
+
// iteration it's being called for at all.
|
|
1413
|
+
let _initResult = evalEvAttr(_initAttr, { cType: 'init' }, $(_el), 'init', this.vrs);
|
|
1414
|
+
if (_initResult && typeof _initResult === 'object' && typeof _initResult.then !== 'function') {
|
|
1415
|
+
Object.assign(_el.vars, _initResult);
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1164
1419
|
this.view.views.push(_el);
|
|
1165
1420
|
}
|
|
1166
1421
|
|
|
1167
1422
|
_el.events = {};
|
|
1168
1423
|
for (const prop in attrs) {
|
|
1169
|
-
if (prop == "view" || prop == ":if" || prop == ":else-if" || prop == ":else" || prop == ":for" || prop == ":for-limit" || prop == ":for-offset" || prop == ":for-if") continue;
|
|
1424
|
+
if (prop == "view" || prop == ":data" || prop == ":if" || prop == ":else-if" || prop == ":else" || prop == ":for" || prop == ":for-limit" || prop == ":for-offset" || prop == ":for-if") continue;
|
|
1170
1425
|
try {
|
|
1171
1426
|
// cl("DD", doc);
|
|
1172
1427
|
let val = (doc && doc.ax.hasOwnProperty(prop)) ? "" : attrs[prop];
|
|
@@ -1270,6 +1525,39 @@ class _lm {
|
|
|
1270
1525
|
? _vt.View.vars[__name]
|
|
1271
1526
|
: _vt.Global.vars[__name];
|
|
1272
1527
|
}
|
|
1528
|
+
// 2026-09-17, real bug found and fixed: getVal() (mustache
|
|
1529
|
+
// text, e.g. {{f.name}}) has always merged this.vrs in after
|
|
1530
|
+
// the View/Global loop above — the local :for-loop-scoped
|
|
1531
|
+
// binding (f, k, v, index — set via tx._re.vrs = vx, see the
|
|
1532
|
+
// :for update path above) correctly shadows/supplies names a
|
|
1533
|
+
// view's own script never declares. evalExp() (used for
|
|
1534
|
+
// :if="f.done"/:else-if conditions) never had the same merge
|
|
1535
|
+
// — any :if/:else-if condition referencing a :for loop
|
|
1536
|
+
// variable's property could never evaluate correctly, in any
|
|
1537
|
+
// LumenJS V2 project, not just this page. It "looked" correct
|
|
1538
|
+
// on first render only by coincidence: :else isn't gated by
|
|
1539
|
+
// evalExp at all, so an all-false-by-default loop (every item
|
|
1540
|
+
// starting unchecked) rendered the same whether the condition
|
|
1541
|
+
// genuinely evaluated false or silently threw and was caught
|
|
1542
|
+
// below. Found clicking a real @click-toggled :for item on
|
|
1543
|
+
// lumenjs.com's own real V2 homepage.
|
|
1544
|
+
for (const ky in this.vrs) {
|
|
1545
|
+
if (Object.prototype.hasOwnProperty.call(this.vrs, ky)) {
|
|
1546
|
+
rvars[ky] = this.vrs[ky];
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
// 2026-09-17: same per-instance subview merge as getVal()
|
|
1550
|
+
// above — a subview's own vars live on this.view.vars, not
|
|
1551
|
+
// the flat _vt.View.vars checked above (always the main
|
|
1552
|
+
// view). See getVal()'s matching comment for the full
|
|
1553
|
+
// rationale.
|
|
1554
|
+
if (this.view && this.view.vars) {
|
|
1555
|
+
for (const ky in this.view.vars) {
|
|
1556
|
+
if (Object.prototype.hasOwnProperty.call(this.view.vars, ky)) {
|
|
1557
|
+
rvars[ky] = this.view.vars[ky];
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1273
1561
|
} catch (e) {
|
|
1274
1562
|
cl(e);
|
|
1275
1563
|
}
|
|
@@ -1615,6 +1903,22 @@ class _lm {
|
|
|
1615
1903
|
}
|
|
1616
1904
|
(par.view._slots ?? (par.view._slots = {}))[slotName] = childs;
|
|
1617
1905
|
}
|
|
1906
|
+
} else if (doc.attrs && doc.attrs.hasOwnProperty('tpl') && !doc.attrs.hasOwnProperty(':for')) {
|
|
1907
|
+
// 2026-09-17: `tpl="name"` used WITHOUT `:for=` on the
|
|
1908
|
+
// same element — the real, documented (lumenjs-
|
|
1909
|
+
// spec.md §4.3) shape always pairs the two, and only
|
|
1910
|
+
// makes sense paired: a template renders inside
|
|
1911
|
+
// whatever repeated context it's used from (see
|
|
1912
|
+
// renderSection()'s own tpl handling, for the
|
|
1913
|
+
// WITH-:for case — an element carrying both never
|
|
1914
|
+
// reaches this branch at all, since :for makes it a
|
|
1915
|
+
// 'sections'-type node the switch above already
|
|
1916
|
+
// dispatched elsewhere). Reported as a real error
|
|
1917
|
+
// rather than silently rendering nothing.
|
|
1918
|
+
reportLumenError({
|
|
1919
|
+
stage: 'tpl',
|
|
1920
|
+
error: new Error('tpl="' + doc.attrs['tpl'] + '" must be used together with :for on the same element — templates only render inside a repeated/list context.'),
|
|
1921
|
+
});
|
|
1618
1922
|
} else {
|
|
1619
1923
|
el = par.createEl(doc.name, doc.attrs, [], doc.evs, doc);
|
|
1620
1924
|
|
|
@@ -1797,7 +2101,26 @@ class _lm {
|
|
|
1797
2101
|
}
|
|
1798
2102
|
|
|
1799
2103
|
|
|
1800
|
-
|
|
2104
|
+
// 2026-09-17: `scopePath` (new) — where in the _vt tree THIS render's own
|
|
2105
|
+
// compiled <script> should write/read its reactive vars. Defaults to
|
|
2106
|
+
// ["View"] (the main view, unchanged behavior for every existing caller).
|
|
2107
|
+
// A subview mount passes its own ["View","views",i] (or the nested form
|
|
2108
|
+
// for a subview hosting a subview) — see renderView()'s isSub branch.
|
|
2109
|
+
// `ownVars`/`ownFns`/`ownViews` (new): for a subview render, the SAME
|
|
2110
|
+
// objects the mount element's `.vars`/`.fns`/`.views` already point to
|
|
2111
|
+
// (not fresh `{}`/`[]`s) — so a write through the compiled script's
|
|
2112
|
+
// `_vt.View.views[i].vars[...]`/`.fns[...]` path (which resolves to that
|
|
2113
|
+
// element's `.vars`/`.fns`) and a read through this instance's own
|
|
2114
|
+
// `getVal()`/`evalExp()` (`this.view.vars`) or `evalEvAttr()`
|
|
2115
|
+
// (`this.view.fns`) see the exact same data. `ownViews` matters for
|
|
2116
|
+
// NESTED subviews specifically: createEl() pushes onto `this.view.views`
|
|
2117
|
+
// while THIS instance is rendering — without linking that to the mount
|
|
2118
|
+
// element's own `.views`, a subview hosting further subviews pushed them
|
|
2119
|
+
// into a freshly-constructed, orphaned array nothing else could ever
|
|
2120
|
+
// find (real bug found testing exactly this — nested subviews mounted
|
|
2121
|
+
// and rendered their own content correctly, but their OWN vars/state
|
|
2122
|
+
// were unreachable from outside, landing in a throwaway array).
|
|
2123
|
+
async function renderHST(hst, n, type = 'main', tx, _pv = null, scopePath = ["View"], ownVars, ownFns, ownViews) {
|
|
1801
2124
|
|
|
1802
2125
|
var reactiveVariables = hst.reactiveVars;
|
|
1803
2126
|
hst = hst.hst;
|
|
@@ -1806,10 +2129,16 @@ async function renderHST(hst, n, type = 'main', tx, _pv = null) {
|
|
|
1806
2129
|
"name": n,
|
|
1807
2130
|
"type": type,
|
|
1808
2131
|
"hst": hst,
|
|
1809
|
-
"vars": tx?.vx,
|
|
2132
|
+
"vars": ownVars ?? tx?.vx,
|
|
2133
|
+
"fns": ownFns,
|
|
2134
|
+
"views": ownViews,
|
|
1810
2135
|
"rvs": reactiveVariables,
|
|
1811
2136
|
"_pv": _pv
|
|
1812
2137
|
}));
|
|
2138
|
+
// 2026-09-17: persisted so THIS instance can later render ITS OWN
|
|
2139
|
+
// subviews (updateVXRs() -> renderView()) at the right nested path —
|
|
2140
|
+
// see both of those for the other half of this.
|
|
2141
|
+
_re.view.scopePath = scopePath;
|
|
1813
2142
|
|
|
1814
2143
|
if (Object.keys(_re._cc).length) {
|
|
1815
2144
|
|
|
@@ -1882,7 +2211,7 @@ async function renderHST(hst, n, type = 'main', tx, _pv = null) {
|
|
|
1882
2211
|
const jses = _re._jj[ky];
|
|
1883
2212
|
for (let inde = 0; inde < jses.length; inde++) {
|
|
1884
2213
|
const nd = jses[inde].nd;
|
|
1885
|
-
let code = getWatcher(nd._jst, _re.view, reactiveVariables).code;
|
|
2214
|
+
let code = getWatcher(nd._jst, _re.view, reactiveVariables, scopePath).code;
|
|
1886
2215
|
code = `try { ` + code + ` } catch (e) { reportLumenError({ stage: 'script', error: e }); }`;
|
|
1887
2216
|
// sourceURL: DevTools shows this <script>'s errors against
|
|
1888
2217
|
// "<viewname>.view" instead of an anonymous VM context.
|
|
@@ -1936,8 +2265,16 @@ async function fireRenderHook(cx, n, containerEl, extra) {
|
|
|
1936
2265
|
let ev = Object.assign({ cType: n }, extra || {});
|
|
1937
2266
|
let result = evalEvAttr(attrVal, ev, $(containerEl), n);
|
|
1938
2267
|
if (result && typeof result.then === 'function') {
|
|
1939
|
-
|
|
2268
|
+
// 2026-09-17: this used to just await-and-discard. @init (new,
|
|
2269
|
+
// subview mounts) needs the handler's actual return value — its
|
|
2270
|
+
// whole purpose is "compute this instance's own initial data" —
|
|
2271
|
+
// so a Promise result is now awaited AND returned, not thrown
|
|
2272
|
+
// away. Every existing @before-render/@after-render caller
|
|
2273
|
+
// already ignores this function's return value (none of them
|
|
2274
|
+
// needed it), so this is additive, not a behavior change for them.
|
|
2275
|
+
try { return await result; } catch (e) { return undefined; }
|
|
1940
2276
|
}
|
|
2277
|
+
return result;
|
|
1941
2278
|
}
|
|
1942
2279
|
|
|
1943
2280
|
// 2026-09-16: V1's real automatic plugin-init pass — [sl] (Select2),
|
|
@@ -2126,7 +2463,45 @@ async function renderSection(tx, doc, par, k, sectionsData) {
|
|
|
2126
2463
|
|
|
2127
2464
|
var hst = doc.children;
|
|
2128
2465
|
|
|
2129
|
-
|
|
2466
|
+
// 2026-09-17: `tpl="name"` on a :for element (`<li :for="items as
|
|
2467
|
+
// item" tpl="row"></li>`, the real, documented — lumenjs-spec.md
|
|
2468
|
+
// §4.3 — shape; never built in V2 until now). A :for element's own
|
|
2469
|
+
// `doc.children` is empty (there's nothing between its tags in the
|
|
2470
|
+
// source), which is exactly what's rendered per item above — so
|
|
2471
|
+
// this substitutes the template file's own HST as this section's
|
|
2472
|
+
// children instead, walked by the SAME engine instance rendering
|
|
2473
|
+
// everything else here (this section already has its own vx/vrs —
|
|
2474
|
+
// no separate scope, no isolated instance, just this item's content).
|
|
2475
|
+
if (doc.attrs && doc.attrs.hasOwnProperty('tpl')) {
|
|
2476
|
+
let _payload = (typeof _vcD !== 'undefined' && _vcD) || (typeof _vcData !== 'undefined' ? _vcData : undefined);
|
|
2477
|
+
let _tplName = doc.attrs['tpl'];
|
|
2478
|
+
let _tplKey = btoa("src/tpls/" + _tplName + ".tpl");
|
|
2479
|
+
let _tplEntry = _payload && _payload.tpls && _payload.tpls[_tplKey];
|
|
2480
|
+
if (_tplEntry) {
|
|
2481
|
+
hst = _tplEntry.hst;
|
|
2482
|
+
} else {
|
|
2483
|
+
reportLumenError({
|
|
2484
|
+
stage: 'tpl',
|
|
2485
|
+
error: new Error('tpl="' + _tplName + '" — no such file at src/tpls/' + _tplName + '.tpl'),
|
|
2486
|
+
});
|
|
2487
|
+
}
|
|
2488
|
+
}
|
|
2489
|
+
|
|
2490
|
+
// 2026-09-17: real bug found and fixed — a subview mounted INSIDE a
|
|
2491
|
+
// :for item (e.g. <div :for="items as item"><div view="widgets/
|
|
2492
|
+
// card"></div></div>) never worked at all: renderHST() used to always
|
|
2493
|
+
// build a fresh, empty `.views` array for whatever it constructs
|
|
2494
|
+
// (see _v's constructor), so createEl() calls made while rendering
|
|
2495
|
+
// THIS item's own content pushed the subview's mount element onto a
|
|
2496
|
+
// throwaway array nothing else could ever find — renderView()'s
|
|
2497
|
+
// isSub search only ever looks at the ENCLOSING view's real `.views`
|
|
2498
|
+
// (or a nested subview's own, itself wired the same way). Passing
|
|
2499
|
+
// par's own `.view.views`/`.scopePath` through here makes every
|
|
2500
|
+
// loop item share the SAME real registry as its enclosing view, so
|
|
2501
|
+
// subview mounts inside a loop accumulate into one addressable array
|
|
2502
|
+
// (sequential index, in render order) exactly like a static mount —
|
|
2503
|
+
// same mechanism either way, just a dynamically-arriving index.
|
|
2504
|
+
let _re = await renderHST({ hst, mxes: [] }, tx.key, 'section', tx, par, par?.view?.scopePath || ["View"], undefined, undefined, par?.view?.views);
|
|
2130
2505
|
// if(par) {
|
|
2131
2506
|
// _re.reactiveVariables = par.reactiveVariables;
|
|
2132
2507
|
// }
|
|
@@ -2157,7 +2532,15 @@ async function renderSection(tx, doc, par, k, sectionsData) {
|
|
|
2157
2532
|
return _re;
|
|
2158
2533
|
}
|
|
2159
2534
|
|
|
2160
|
-
|
|
2535
|
+
// `viewsArr`/`scopeBase` (2026-09-17): which subview registry to search
|
|
2536
|
+
// and which scope path to address matches under — default to the main
|
|
2537
|
+
// view's own (_vt.View.views / ["View"]) for the top-level, non-recursive
|
|
2538
|
+
// caller (ws.js's real navigation, index-bootstrap.js, etc. — all
|
|
2539
|
+
// unaffected, still work exactly as before). A subview instance rendering
|
|
2540
|
+
// ITS OWN subviews (updateVXRs(), above) passes its own `.views`/
|
|
2541
|
+
// `.scopePath` instead, so nested subviews resolve against the RIGHT
|
|
2542
|
+
// array at the RIGHT nested address instead of always the main view's.
|
|
2543
|
+
async function renderView(n, isSub, d, type = 'views', viewsArr, scopeBase = ["View"]) {
|
|
2161
2544
|
let filePath = "src/views/" + n + ".view"
|
|
2162
2545
|
if (type == 'layouts') filePath = "src/layouts/" + n + ".layout";
|
|
2163
2546
|
|
|
@@ -2213,23 +2596,42 @@ async function renderView(n, isSub, d, type = 'views') {
|
|
|
2213
2596
|
// return;
|
|
2214
2597
|
|
|
2215
2598
|
if (isSub) {
|
|
2599
|
+
// 2026-09-17: per-instance subview scoping — each matched
|
|
2600
|
+
// element needs ITS OWN index within the search array (not its
|
|
2601
|
+
// position in this filtered-by-subPath list) so its compiled
|
|
2602
|
+
// script rewrites into `<scopeBase>.views[<that exact index>].vars`,
|
|
2603
|
+
// not the flat `_vt.View.vars` every subview used to collide on.
|
|
2604
|
+
// Searches viewsArr (this main view's _vt.View.views by
|
|
2605
|
+
// default — unchanged for the top-level caller) or, when
|
|
2606
|
+
// called recursively from a subview instance's own
|
|
2607
|
+
// updateVXRs(), THAT instance's own `.views` — see
|
|
2608
|
+
// updateVXRs() for why nested subviews need this.
|
|
2609
|
+
let searchArr = viewsArr || _vt.View.views;
|
|
2216
2610
|
let els = [];
|
|
2217
|
-
for (let i = 0; i <
|
|
2218
|
-
let _el =
|
|
2611
|
+
for (let i = 0; i < searchArr.length; i++) {
|
|
2612
|
+
let _el = searchArr[i];
|
|
2219
2613
|
if (_el.__isProxy) _el = _el.target;
|
|
2220
|
-
if (_el.subPath == n) els.push(_el);
|
|
2614
|
+
if (_el.subPath == n) els.push({ el: _el, viewsIndex: i });
|
|
2221
2615
|
}
|
|
2222
2616
|
if (els.length) {
|
|
2223
2617
|
// cl(els);
|
|
2224
2618
|
for (let i = 0; i < els.length; i++) {
|
|
2225
|
-
|
|
2226
|
-
|
|
2619
|
+
const { el, viewsIndex } = els[i];
|
|
2620
|
+
// Create/reuse el.vars BEFORE rendering, so the fresh
|
|
2621
|
+
// _v instance renderHST() constructs uses this EXACT
|
|
2622
|
+
// object as its own .vars — see renderHST()'s ownVars
|
|
2623
|
+
// param comment for why that identity matters.
|
|
2624
|
+
el.vars = el.vars || {};
|
|
2625
|
+
el.views = el.views || [];
|
|
2626
|
+
el.fns = el.fns || {};
|
|
2627
|
+
let _re = await renderHST(hst, n, 'sub', undefined, null, scopeBase.concat(["views", viewsIndex]), el.vars, el.fns, el.views);
|
|
2628
|
+
el._re = _re;
|
|
2227
2629
|
el.innerHTML = "";
|
|
2228
2630
|
// cl(el);
|
|
2229
2631
|
el.append(..._re._RealDOM);
|
|
2230
2632
|
_re.renderAll();
|
|
2231
2633
|
}
|
|
2232
|
-
// _re._RealDOM.forEach(_el => el[0].appendChild(_el))
|
|
2634
|
+
// _re._RealDOM.forEach(_el => el[0].appendChild(_el))
|
|
2233
2635
|
// cl(_re);
|
|
2234
2636
|
}
|
|
2235
2637
|
} else {
|