@lmjs/core 1.0.7 → 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/build/bundle.js +264 -0
- package/build/plugins-registry.js +216 -0
- package/dist/lumenjs-core-with-plugins.js +59258 -0
- package/dist/lumenjs-core.js +50 -0
- package/dist/lumenjs-plugins.css +2822 -0
- package/package.json +59 -28
- package/src/_re.js +2709 -0
- package/src/dom-shim.js +640 -0
- package/src/index-bootstrap.js +53 -0
- package/src/lstnrs.js +1409 -0
- package/src/walk.js +837 -0
- package/src/workers/css.js +1 -0
- package/src/workers/cssRaw.js +1173 -0
- package/src/workers/esp.js +1 -0
- package/src/workers/espRaw.js +78 -0
- package/src/workers/up.js +1 -0
- package/src/workers/upRaw.js +491 -0
- package/src/workers/work.js +1 -0
- package/src/workers/workRaw.js +1097 -0
- package/src/ws.js +1162 -0
- package/vendor/astring.js +3 -0
- package/vendor/bootstrap2-less-stubs/mixins.less +16 -0
- package/vendor/bootstrap2-less-stubs/variables.less +13 -0
- package/vendor/md5.js +1 -0
- package/vendor/reconnecting-websocket.js +4143 -0
- package/vendor/webworker-helper.js +1 -0
- package/LICENSE +0 -201
- package/README.md +0 -3
- package/index.js +0 -14
package/src/_re.js
ADDED
|
@@ -0,0 +1,2709 @@
|
|
|
1
|
+
// `cl` is normally aliased in vendor/reconnecting-websocket.js (`var cl =
|
|
2
|
+
// console.log;`), but that file is concatenated AFTER this one (see
|
|
3
|
+
// build/bundle.js) — and this file's own Proxy traps below call `cl(e)` in
|
|
4
|
+
// their catch handlers. That's reachable before the vendor file ever runs:
|
|
5
|
+
// index-bootstrap.js (also after this file, before the vendor one) can
|
|
6
|
+
// trigger _x()'s `set` trap (e.g. `_vt.Global.vars["x"] = ...` from a
|
|
7
|
+
// compiled index.js) before any view exists, which throws internally
|
|
8
|
+
// (`_vt.View._re` is undefined) and get caught right here — if `cl` isn't
|
|
9
|
+
// defined yet at that point, the catch handler itself throws, aborting
|
|
10
|
+
// whatever script called it (found 2026-09-14 testing against a real
|
|
11
|
+
// running dev server). Harmless if vendor/reconnecting-websocket.js's own
|
|
12
|
+
// `var cl = console.log;` runs later too — same value, redeclaration is a
|
|
13
|
+
// no-op.
|
|
14
|
+
var cl = console.log;
|
|
15
|
+
|
|
16
|
+
class _v {
|
|
17
|
+
static name;
|
|
18
|
+
static type;
|
|
19
|
+
static vars;
|
|
20
|
+
static fns;
|
|
21
|
+
static rvs;
|
|
22
|
+
static _pv;
|
|
23
|
+
static mx;
|
|
24
|
+
static views;
|
|
25
|
+
static hst;
|
|
26
|
+
static settings;
|
|
27
|
+
constructor(obj) {
|
|
28
|
+
this.name = obj.name ?? 'home';
|
|
29
|
+
this.type = obj.type ?? 'main';
|
|
30
|
+
this.hst = obj.hst ?? [];
|
|
31
|
+
this.views = obj.views ?? [];
|
|
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 ?? {};
|
|
43
|
+
this.rvs = obj.rvs ?? {};
|
|
44
|
+
this._pv = obj._pv ?? null;
|
|
45
|
+
this.mx = obj.mx ?? [];
|
|
46
|
+
this.settings = obj.settings ?? {
|
|
47
|
+
layout: "default",
|
|
48
|
+
requireAuth: false,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Override console.log
|
|
54
|
+
const consoleLogOriginal = console.log;
|
|
55
|
+
console.log = function () {
|
|
56
|
+
for (let i = 0; i < arguments.length; i++) {
|
|
57
|
+
const arg = arguments[i];
|
|
58
|
+
if (arg && arg.hasOwnProperty('__isProxy') || arg?.target) {
|
|
59
|
+
arguments[i] = arguments[i].target;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
consoleLogOriginal.apply(console, arguments);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// ---- Central error reporting (2026-09-13) ----------------------------------
|
|
66
|
+
// Replaces scattered `catch(e){cl(e)}` sites that silently swallowed errors.
|
|
67
|
+
// Wired into a representative, developer-facing subset for now: expression
|
|
68
|
+
// evaluation (scopedEval/lookup/evalExp), the pre-rewrite validator in
|
|
69
|
+
// walk.js, and a compiled <script> block's own runtime errors. Not yet wired
|
|
70
|
+
// into every catch site in this file — that's a deliberate, incremental scope,
|
|
71
|
+
// not an oversight; the ones above are where a developer's own .view mistake
|
|
72
|
+
// actually surfaces, which is what this phase set out to fix.
|
|
73
|
+
//
|
|
74
|
+
// _lumenDevMode is a plain constant for now, not wired to a real build/env
|
|
75
|
+
// flag yet — flip it by hand until that decision is made.
|
|
76
|
+
var _lumenDevMode = true;
|
|
77
|
+
var _lumenErrorLog = [];
|
|
78
|
+
|
|
79
|
+
// HST wire-format version this runtime expects (2026-09-15) — must be
|
|
80
|
+
// bumped in lockstep with packages/cli/lib/fcs.js's HST_FORMAT_VERSION
|
|
81
|
+
// whenever this file's walk()/renderHST() changes what fields it reads
|
|
82
|
+
// from the parser's output. See renderView()'s version check and fcs.js's
|
|
83
|
+
// own HST_FORMAT_VERSION comment for the full rationale.
|
|
84
|
+
var EXPECTED_HST_FORMAT_VERSION = 1;
|
|
85
|
+
|
|
86
|
+
// `_vt.View.vars["x"]` is what a runtime error will mention after the
|
|
87
|
+
// walk.js rewrite — this recovers the developer's original name `x` so the
|
|
88
|
+
// message reads the way the developer would expect, not the compiled form.
|
|
89
|
+
function _translateLumenError(message) {
|
|
90
|
+
if (!message) return message;
|
|
91
|
+
return message.replace(/_vt\.View\.vars\[["'`]([^"'`\]]+)["'`]\]/g, '$1');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function reportLumenError(info) {
|
|
95
|
+
info = info || {};
|
|
96
|
+
var rawMessage = info.message || (info.error && info.error.message) || 'Unknown error';
|
|
97
|
+
var entry = {
|
|
98
|
+
time: new Date().toISOString(),
|
|
99
|
+
stage: info.stage || 'runtime',
|
|
100
|
+
view: info.view || (typeof _vt !== 'undefined' && _vt.View ? _vt.View.name : undefined),
|
|
101
|
+
expr: info.expr,
|
|
102
|
+
message: _translateLumenError(rawMessage),
|
|
103
|
+
hint: info.hint
|
|
104
|
+
};
|
|
105
|
+
_lumenErrorLog.push(entry);
|
|
106
|
+
|
|
107
|
+
if (_lumenDevMode) {
|
|
108
|
+
console.error(
|
|
109
|
+
'[LumenJS] ' + entry.stage + ' error in "' + (entry.view || 'unknown') + '"' +
|
|
110
|
+
(entry.expr ? ' — ' + entry.expr : '') +
|
|
111
|
+
': ' + entry.message +
|
|
112
|
+
(entry.hint ? '\n ' + entry.hint : '')
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
// Production stays quiet on-screen by design (point 6: don't crash the
|
|
116
|
+
// page the way V1's uncaught errors did) — the entry is still kept in
|
|
117
|
+
// _lumenErrorLog for later inspection; no telemetry hook wired yet.
|
|
118
|
+
return entry;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
function _x(_x) {
|
|
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
|
+
}
|
|
185
|
+
const handler = {
|
|
186
|
+
get(target, key) {
|
|
187
|
+
if (key == "__isProxy") return true;
|
|
188
|
+
// 2026-09-14: reset must fire on ANY root key, not just "View" —
|
|
189
|
+
// this closure's currPath is shared by the whole _vt proxy tree,
|
|
190
|
+
// so a stale tail from a prior _vt.Global read (which never hit
|
|
191
|
+
// "View" to reset it) would otherwise get prepended to the next
|
|
192
|
+
// chain and corrupt which key set()'s update(key) fires for.
|
|
193
|
+
if (key == "View" || key == "Global") currPath = [];
|
|
194
|
+
currPath.push(key);
|
|
195
|
+
if (typeof target[key] === 'object' && target[key] !== null && key != "_re") {
|
|
196
|
+
// cl("salsaa", target, key)
|
|
197
|
+
return new Proxy(target[key], handler);
|
|
198
|
+
} else {
|
|
199
|
+
// cl("tartat", target, key);
|
|
200
|
+
return target[key] ?? (key == 'target' ? target : undefined) ?? undefined;
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
set(target, key, value) {
|
|
204
|
+
|
|
205
|
+
// cl(["hiiiiii", key, value]);
|
|
206
|
+
// console.log(`${key} set from ${target[key]} to ${value}`, target, Object.keys({ target })[0]);
|
|
207
|
+
target[key] = value;
|
|
208
|
+
// var _View = currPath[1] == "views" ? currPath[2] : "";
|
|
209
|
+
|
|
210
|
+
try {
|
|
211
|
+
// `_vt.View._re` genuinely doesn't exist yet during
|
|
212
|
+
// index.js's own bootstrap (before any view has mounted —
|
|
213
|
+
// see index-bootstrap.js) — that's the normal case now,
|
|
214
|
+
// not an error, so it's guarded (inside _dispatchVarsUpdate)
|
|
215
|
+
// rather than left to throw into the catch below.
|
|
216
|
+
_dispatchVarsUpdate(key);
|
|
217
|
+
} catch (e) {
|
|
218
|
+
cl(e);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
currPath = [];
|
|
222
|
+
return true;
|
|
223
|
+
},
|
|
224
|
+
deleteProperty(target, key) {
|
|
225
|
+
if (!(key in target)) {
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
delete target[key];
|
|
229
|
+
|
|
230
|
+
try {
|
|
231
|
+
_dispatchVarsUpdate(key);
|
|
232
|
+
} catch (e) {
|
|
233
|
+
cl(e);
|
|
234
|
+
}
|
|
235
|
+
return true;
|
|
236
|
+
},
|
|
237
|
+
ownKeys(target) {
|
|
238
|
+
return Reflect.ownKeys(target);
|
|
239
|
+
},
|
|
240
|
+
has(target, key) {
|
|
241
|
+
return key in target;
|
|
242
|
+
},
|
|
243
|
+
defineProperty(target, key, descriptor) {
|
|
244
|
+
if (descriptor && "value" in descriptor) {
|
|
245
|
+
target[key] = descriptor.value;
|
|
246
|
+
}
|
|
247
|
+
return target;
|
|
248
|
+
},
|
|
249
|
+
getOwnPropertyDescriptor(target, key) {
|
|
250
|
+
const value = target[key];
|
|
251
|
+
return key in target
|
|
252
|
+
? {
|
|
253
|
+
value,
|
|
254
|
+
enumerable: true,
|
|
255
|
+
configurable: true,
|
|
256
|
+
}
|
|
257
|
+
: undefined;
|
|
258
|
+
},
|
|
259
|
+
};
|
|
260
|
+
var x = new Proxy(_x, handler);
|
|
261
|
+
return x;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// "Global" (2026-09-14): a second, permanent root next to "View" — unlike
|
|
265
|
+
// _vt.View (replaced wholesale by a fresh _v() on every main-view navigation,
|
|
266
|
+
// see _lm's constructor below), this object is never reassigned, so state
|
|
267
|
+
// written here survives navigation. This is the target walk.js now rewrites
|
|
268
|
+
// index.js's top-level vars into, since index.js runs once at bootstrap and
|
|
269
|
+
// its vars (V1's documented convention for app-wide state like `isAdmin`)
|
|
270
|
+
// need to outlive any single view. concatVarsAtLevel() below merges it into
|
|
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.
|
|
290
|
+
let _vt = _x({
|
|
291
|
+
"View": new _v({}),
|
|
292
|
+
"Global": { "vars": {}, "fns": {} }
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
class _lm {
|
|
296
|
+
_RealDOM = [];
|
|
297
|
+
_effects = {};
|
|
298
|
+
_cc = {};
|
|
299
|
+
_jj = {};
|
|
300
|
+
_ready = false;
|
|
301
|
+
view = undefined;
|
|
302
|
+
sbscrbs = [];
|
|
303
|
+
reactiveVariables = [];
|
|
304
|
+
vrs = {};
|
|
305
|
+
_CXR = [];
|
|
306
|
+
_LXR = [];
|
|
307
|
+
|
|
308
|
+
constructor(view) {
|
|
309
|
+
this.view = view;
|
|
310
|
+
this.view._re = this;
|
|
311
|
+
this.reactiveVariables = view?.rvs ?? {};
|
|
312
|
+
this.init();
|
|
313
|
+
if (this.view.type == "main") _vt.View = this.view;
|
|
314
|
+
|
|
315
|
+
// this.reactiveVariables = this.view.vars ?? _vt.View.vars ?? [];
|
|
316
|
+
|
|
317
|
+
if (this.view._pv) {
|
|
318
|
+
// cl("Subscribing to " , this.view);
|
|
319
|
+
this.view._pv.subscribe(this.view);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return this;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
init() {
|
|
326
|
+
var par = this;
|
|
327
|
+
this.view.hst.forEach(function (doc) {
|
|
328
|
+
par.walk(doc, null);
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
subscribe(view) {
|
|
333
|
+
this.sbscrbs.push(view);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
scopedEval(context, expr, kk) {
|
|
337
|
+
// cl("Cleaning", expr, kk, context);
|
|
338
|
+
// 2026-09-14: `ctx` was read/deleted here before its own `let ctx =`
|
|
339
|
+
// declaration a few lines down — a real TDZ bug (ReferenceError:
|
|
340
|
+
// Cannot access 'ctx' before initialization), not something either
|
|
341
|
+
// of us introduced. Only triggers on the TypeError-retry path (a
|
|
342
|
+
// failed eval retrying with the offending property stripped), which
|
|
343
|
+
// is common enough — any expression touching a property of
|
|
344
|
+
// something momentarily undefined hits it. Fixed by computing `ctx`
|
|
345
|
+
// first, same effect, no TDZ violation.
|
|
346
|
+
let ctx = this.concatVarsAtLevel(context, this);
|
|
347
|
+
if (kk) {
|
|
348
|
+
if (!ctx.hasOwnProperty(kk)) return undefined;
|
|
349
|
+
delete ctx[kk];
|
|
350
|
+
}
|
|
351
|
+
// cl("CONTEXT");
|
|
352
|
+
// cl(context);
|
|
353
|
+
// cl(ctx);
|
|
354
|
+
try {
|
|
355
|
+
// cl(["LopIsMissing???", context, expr]);
|
|
356
|
+
const evaluator = Function.apply(null, [
|
|
357
|
+
...Object.keys(ctx),
|
|
358
|
+
"expr",
|
|
359
|
+
"return eval(expr)",
|
|
360
|
+
]);
|
|
361
|
+
// cl(evaluator.apply(null, [...Object.values(context), expr]));
|
|
362
|
+
// if(expr=="patientProfile['Gender']") alert([evaluator.apply(null, [...Object.values(context), expr]), expr, Object.keys(context), context['patientProfile'], Object.keys(context.patientProfile)]);
|
|
363
|
+
return evaluator.apply(null, [...Object.values(ctx), expr]);
|
|
364
|
+
} catch (e) {
|
|
365
|
+
// if (_debugMode)
|
|
366
|
+
// cl([expr, context, e]);
|
|
367
|
+
if (e instanceof TypeError) {
|
|
368
|
+
return this.scopedEval(ctx, expr, e.message.split(" ")[0]);
|
|
369
|
+
}
|
|
370
|
+
reportLumenError({ stage: 'expression', expr: expr, error: e });
|
|
371
|
+
return undefined;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
getVals(effect) {
|
|
376
|
+
let val = '';
|
|
377
|
+
// let mss = [];
|
|
378
|
+
if (effect.type == "text") {
|
|
379
|
+
if (!effect.isSplit) {
|
|
380
|
+
for (let i = 0; i < effect.splits.length; i++) {
|
|
381
|
+
let split = effect.splits[i];
|
|
382
|
+
if (split.type == 'mustache') {
|
|
383
|
+
val += this.getVal(split.content);
|
|
384
|
+
} else {
|
|
385
|
+
val += split.content;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
} else {
|
|
389
|
+
val = this.getVal(effect.content);
|
|
390
|
+
}
|
|
391
|
+
} else if (effect.type == "attr" || effect.type == "event") {
|
|
392
|
+
for (let i = 0; i < effect.splits.length; i++) {
|
|
393
|
+
let split = effect.splits[i];
|
|
394
|
+
if (split.type == 'mustache') {
|
|
395
|
+
val += this.getVal(split.content);
|
|
396
|
+
} else {
|
|
397
|
+
val += split.content;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
// mss = effect.mss[effect.name];
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// for (let i = 0; i < mss.length; i++) {
|
|
404
|
+
// const ms = "{{" + mss[i] + "}}";
|
|
405
|
+
// cl(ms, this.getVal(ms, _.cloneDeep(_vt.View.vars)));
|
|
406
|
+
// val = val.split(ms).join(this.getVal(ms, _.cloneDeep(this.reactiveVariables)));
|
|
407
|
+
// }
|
|
408
|
+
|
|
409
|
+
return val;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
renderAll() {
|
|
413
|
+
if (this._ready) return; //CHECK APPLICABILITY
|
|
414
|
+
this._ready = true;
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
for (const rv in this._effects) {
|
|
418
|
+
if (Object.prototype.hasOwnProperty.call(this._effects, rv)) {
|
|
419
|
+
const effects = this._effects[rv];
|
|
420
|
+
for (let ei = 0; ei < effects.length; ei++) {
|
|
421
|
+
const effect = effects[ei];
|
|
422
|
+
// cl("Effect for " + rv, effect);
|
|
423
|
+
// effect.nd.textContent = "1234";
|
|
424
|
+
|
|
425
|
+
if (!effect.nd) continue;
|
|
426
|
+
// if (!effect.nd.isConnected) continue;
|
|
427
|
+
try {
|
|
428
|
+
if (effect.type == "text") {
|
|
429
|
+
// cl(k, effect);
|
|
430
|
+
effect.nd.textContent = this.render(effect);
|
|
431
|
+
} else if (effect.type == "attr") {
|
|
432
|
+
// cl("Setting Attr", k, effect, effect.name, this.render(effect));
|
|
433
|
+
if (effect.name == "value") {
|
|
434
|
+
effect.nd.value = this.render(effect);
|
|
435
|
+
} else {
|
|
436
|
+
effect.nd.setAttribute(effect.name, this.render(effect));
|
|
437
|
+
if (effect.name == "view") {
|
|
438
|
+
effect.nd.subPath = this.render(effect);
|
|
439
|
+
renderView(this.render(effect), true, {});
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
} else if (effect.type == "event") {
|
|
443
|
+
effect.nd.events[effect.name] = this.render(effect);
|
|
444
|
+
}
|
|
445
|
+
} catch (e) {
|
|
446
|
+
cl("Eeeeee", e);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
this.updateCXRs();
|
|
454
|
+
this.updateLXRs();
|
|
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
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
chainConnected(cx) {
|
|
472
|
+
for (let i = cx.chain.length - 1; i >= 0; i--) {
|
|
473
|
+
const cxs = cx.chain[i];
|
|
474
|
+
if (cxs.ref.isPreConnected) {
|
|
475
|
+
return true;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
return false;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
async updateVXRs(k) {
|
|
482
|
+
let subsNames = [];
|
|
483
|
+
for (let i = 0; i < this.view.views.length; i++) {
|
|
484
|
+
const _view = this.view.views[i];
|
|
485
|
+
if (!subsNames.includes(_view.subPath)) subsNames.push(_view.subPath);
|
|
486
|
+
}
|
|
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.
|
|
501
|
+
for (let i = 0; i < subsNames.length; i++) {
|
|
502
|
+
const n = subsNames[i];
|
|
503
|
+
renderView(n, true, {}, 'views', this.view.views, this.view.scopePath || ["View"]);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
async updateCXRs(k) {
|
|
508
|
+
for (let i = 0; i < this._CXR.length; i++) {
|
|
509
|
+
const cx = this._CXR[i];
|
|
510
|
+
if (cx.name == "if") {
|
|
511
|
+
let isTrue = this.evalExp(cx.content, this.reactiveVariables);
|
|
512
|
+
// cl("IF", isTrue, cx.content, this.reactiveVariables, cx);
|
|
513
|
+
if (isTrue) {
|
|
514
|
+
await this.showSectionCX(cx, k);
|
|
515
|
+
} else {
|
|
516
|
+
await this.hideSectionCX(cx);
|
|
517
|
+
}
|
|
518
|
+
} else if (cx.name == "else-if") {
|
|
519
|
+
if (!this.chainConnected(cx)) {
|
|
520
|
+
let isTrue = this.evalExp(cx.content, this.reactiveVariables);
|
|
521
|
+
// cl("ELSE-IF", isTrue, cx.content, this.reactiveVariables, cx);
|
|
522
|
+
if (isTrue) {
|
|
523
|
+
await this.showSectionCX(cx, k);
|
|
524
|
+
} else {
|
|
525
|
+
await this.hideSectionCX(cx);
|
|
526
|
+
}
|
|
527
|
+
} else {
|
|
528
|
+
await this.hideSectionCX(cx);
|
|
529
|
+
}
|
|
530
|
+
} else if (cx.name == "else") {
|
|
531
|
+
// cl("ELSE", !this.chainConnected(cx), cx);
|
|
532
|
+
if (!this.chainConnected(cx)) {
|
|
533
|
+
await this.showSectionCX(cx, k);
|
|
534
|
+
} else {
|
|
535
|
+
await this.hideSectionCX(cx);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// @before-render / @after-render for :if/:else-if/:else (2026-09-14) —
|
|
543
|
+
// shared by all three updateCXRs() branches above. This runs on EVERY
|
|
544
|
+
// reactive update those branches are eligible for, not just when the
|
|
545
|
+
// condition actually flips — `cx.ref.node.isConnected` (a real, always-
|
|
546
|
+
// accurate DOM property) is what gates hook firing to genuine
|
|
547
|
+
// hidden->visible / visible->hidden transitions, not `isPreConnected`
|
|
548
|
+
// (an internal bookkeeping flag with more edge cases across the
|
|
549
|
+
// if/else-if/else chain than is worth relying on here).
|
|
550
|
+
async showSectionCX(cx, k) {
|
|
551
|
+
let wasConnected = cx.ref.node.isConnected;
|
|
552
|
+
cx.ref.isPreConnected = true;
|
|
553
|
+
await renderSection(cx.ref, cx.doc, this, k);
|
|
554
|
+
if (!wasConnected) await fireRenderHook(cx, 'after-render', cx.ref.node, { visible: true });
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
async hideSectionCX(cx) {
|
|
558
|
+
if (cx.ref.node.isConnected) await fireRenderHook(cx, 'before-render', cx.ref.node, { visible: false });
|
|
559
|
+
cx.ref.isPreConnected = false;
|
|
560
|
+
cx.ref.node.replaceWith(cx.ref);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
render(effect) {
|
|
564
|
+
let x = "";
|
|
565
|
+
|
|
566
|
+
try {
|
|
567
|
+
if (effect.type == "text" || effect.type == "attr" || effect.type == "event") {
|
|
568
|
+
x = this.getVals(effect);
|
|
569
|
+
}
|
|
570
|
+
} catch (e) {
|
|
571
|
+
cl(e);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
return x;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
update(k) {
|
|
578
|
+
if (!this._ready) return;
|
|
579
|
+
// cl("Hello", k);
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
// cl(k, this._effects.hasOwnProperty(k));
|
|
583
|
+
|
|
584
|
+
if (this._effects.hasOwnProperty(k)) {
|
|
585
|
+
const effects = this._effects[k];
|
|
586
|
+
// cl(effects);
|
|
587
|
+
for (let ei = 0; ei < effects.length; ei++) {
|
|
588
|
+
const effect = effects[ei];
|
|
589
|
+
// cl("Effect for " + k, effect);
|
|
590
|
+
// effect.nd.textContent = "1234";
|
|
591
|
+
|
|
592
|
+
if (!effect.nd) continue;
|
|
593
|
+
// if (!effect.nd.isConnected) continue;
|
|
594
|
+
try {
|
|
595
|
+
if (effect.type == "text") {
|
|
596
|
+
// cl(k, effect);
|
|
597
|
+
effect.nd.textContent = this.render(effect);
|
|
598
|
+
} else if (effect.type == "attr") {
|
|
599
|
+
// cl("Setting Attr", k, effect, effect.name, this.render(effect));
|
|
600
|
+
if (effect.name == "value") {
|
|
601
|
+
effect.nd.value = this.render(effect);
|
|
602
|
+
} else {
|
|
603
|
+
effect.nd.setAttribute(effect.name, this.render(effect));
|
|
604
|
+
if (effect.name == "view") {
|
|
605
|
+
effect.nd.subPath = this.render(effect);
|
|
606
|
+
renderView(this.render(effect), true, {});
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
} else if (effect.type == "event") {
|
|
610
|
+
effect.nd.events[effect.name] = this.render(effect);
|
|
611
|
+
}
|
|
612
|
+
} catch (e) {
|
|
613
|
+
cl("Eeeeee", e);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
this.updateCXRs(k);
|
|
619
|
+
this.updateLXRs(k);
|
|
620
|
+
for (let sbscsi = 0; sbscsi < this.sbscrbs.length; sbscsi++) {
|
|
621
|
+
const sbscr = this.sbscrbs[sbscsi];
|
|
622
|
+
sbscr._re.update(k);
|
|
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
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
async updateLXRs(k) {
|
|
645
|
+
for (let i = 0; i < this._LXR.length; i++) {
|
|
646
|
+
var cx = this._LXR[i];
|
|
647
|
+
|
|
648
|
+
var forX = cx.forX;
|
|
649
|
+
|
|
650
|
+
if (k && k != forX['js']) continue;
|
|
651
|
+
|
|
652
|
+
var val = this.getVal(forX['js'], "");
|
|
653
|
+
|
|
654
|
+
// cl("FORR", cx, val);
|
|
655
|
+
|
|
656
|
+
var tempVal = [];
|
|
657
|
+
if (this.typeStr(val) == "number") {
|
|
658
|
+
for (let i = 0; i < val; i++) {
|
|
659
|
+
tempVal.push(i);
|
|
660
|
+
}
|
|
661
|
+
val = tempVal;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
|
|
665
|
+
let vals = [];
|
|
666
|
+
let isObj = false;
|
|
667
|
+
if (this.typeStr(val) == "object") {
|
|
668
|
+
isObj = true;
|
|
669
|
+
|
|
670
|
+
for (const oKey in val) {
|
|
671
|
+
if (Object.hasOwnProperty.call(val, oKey)) {
|
|
672
|
+
const item = val[oKey];
|
|
673
|
+
let objj = {
|
|
674
|
+
key: oKey,
|
|
675
|
+
value: item,
|
|
676
|
+
};
|
|
677
|
+
vals.push(objj);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
} else vals = clone(val);
|
|
681
|
+
|
|
682
|
+
if (this.typeStr(vals) == "array" && vals.length > 0) {
|
|
683
|
+
// cl("ARRAY", cx.ref, vals);
|
|
684
|
+
let forIf = cx.cond;
|
|
685
|
+
let limit = vals.length;
|
|
686
|
+
let offset = 0;
|
|
687
|
+
if (cx.limit) limit = (isNaN(cx.limit) ? cx.limit : limit) > vals.length ? vals.length : cx.limit * 1;
|
|
688
|
+
if (cx.offset) offset = (isNaN(cx.offset) ? cx.offset : offset) < 0 ? 0 : cx.offset * 1;
|
|
689
|
+
|
|
690
|
+
|
|
691
|
+
let marray = [];
|
|
692
|
+
if (forIf) {
|
|
693
|
+
marray = vals.slice(offset * 1, vals.length);
|
|
694
|
+
} else {
|
|
695
|
+
marray = vals.slice(offset * 1, limit * 1 + offset * 1);
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
// cl(cx, vals, limit, offset, forIf, marray);
|
|
699
|
+
|
|
700
|
+
let myLimit = 0;
|
|
701
|
+
// let myClones = [];
|
|
702
|
+
|
|
703
|
+
let arrayToRender = [];
|
|
704
|
+
let arrayToRenderVXs = [];
|
|
705
|
+
|
|
706
|
+
for (var index = 0; index < marray.length; index++) {
|
|
707
|
+
if (myLimit == limit * 1) break;
|
|
708
|
+
try {
|
|
709
|
+
let vx = {}; //clone(_vt.View.vars);
|
|
710
|
+
vx['index'] = myLimit;
|
|
711
|
+
if (forX['dx'] != "") vx[forX['dx']] = myLimit;
|
|
712
|
+
if (isObj) {
|
|
713
|
+
if (forX['as']['v'] != "") {
|
|
714
|
+
if (forX['as']['k']) vx[forX['as']['k']] = marray[index]['key'];
|
|
715
|
+
if (forX['as']['v']) vx[forX['as']['v']] = marray[index]['value'];
|
|
716
|
+
} else {
|
|
717
|
+
if (forX['as']['k']) vx[forX['as']['k']] = marray[index];
|
|
718
|
+
}
|
|
719
|
+
vx['key'] = marray[index]['key'];
|
|
720
|
+
vx['value'] = marray[index]['value'];
|
|
721
|
+
} else {
|
|
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
|
+
}
|
|
745
|
+
}
|
|
746
|
+
// cl(forX, vx);
|
|
747
|
+
// cl("VXXXX", vx, this);
|
|
748
|
+
|
|
749
|
+
if (forIf) {
|
|
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
|
+
}
|
|
778
|
+
if (!isTrue) continue;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
let miIndexx = offset * 1 + index * 1;
|
|
782
|
+
|
|
783
|
+
|
|
784
|
+
// let cln = this.createSection(cx, vx, forX, k);
|
|
785
|
+
arrayToRender.push(marray[index]);
|
|
786
|
+
arrayToRenderVXs.push(vx);
|
|
787
|
+
// let basket = await renderSection(cx.ref, cx.doc, this, k, {
|
|
788
|
+
// cx, vx, forX, miIndexx
|
|
789
|
+
// });
|
|
790
|
+
// cl(basket);
|
|
791
|
+
// cl([
|
|
792
|
+
// cx.ref.isConnected,
|
|
793
|
+
// cx.ref.node.isConnected,
|
|
794
|
+
// cx,
|
|
795
|
+
// "REAL INDEX: " + myLimit,
|
|
796
|
+
// miIndexx,
|
|
797
|
+
// forIf,
|
|
798
|
+
// isObj,
|
|
799
|
+
// forX,
|
|
800
|
+
// vx
|
|
801
|
+
// ]);
|
|
802
|
+
myLimit++;
|
|
803
|
+
} catch (e) {
|
|
804
|
+
cl(e);
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
// cl("ATR", arrayToRender);
|
|
809
|
+
let oldATR = 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);
|
|
833
|
+
// cl(oldATR, arrayToRender);
|
|
834
|
+
|
|
835
|
+
const actions = this.compareArrays(oldATR, arrayToRender);
|
|
836
|
+
// cl("ACTNS", actions);
|
|
837
|
+
|
|
838
|
+
// @before-render / @after-render (2026-09-14) — lets a
|
|
839
|
+
// developer tear down and re-initialize a DOM-owning plugin
|
|
840
|
+
// (a slider, for instance) around this :for loop's own
|
|
841
|
+
// diff-and-patch pass, instead of it silently breaking the
|
|
842
|
+
// next time the underlying data changes. Skipped entirely
|
|
843
|
+
// when nothing actually changed. Fires on the very first
|
|
844
|
+
// render too (`actions` is all "add" then), so one
|
|
845
|
+
// @after-render handler covers both "initialize" and
|
|
846
|
+
// "re-initialize after data changed" — no separate manual
|
|
847
|
+
// bootstrap call needed. See fireRenderHook()'s own comment.
|
|
848
|
+
if (actions.length) await fireRenderHook(cx, 'before-render', cx.ref.parentElement, { items: arrayToRender, actions });
|
|
849
|
+
|
|
850
|
+
for (let ai = 0; ai < actions.length; ai++) {
|
|
851
|
+
const actn = actions[ai];
|
|
852
|
+
if (actn.action == 'add') {
|
|
853
|
+
//Add Items To The DOM at Index
|
|
854
|
+
// actn = {
|
|
855
|
+
// "action": "add",
|
|
856
|
+
// "index": 0,
|
|
857
|
+
// "element": {
|
|
858
|
+
// "key": "Name",
|
|
859
|
+
// "value": "Abdul Rahman"
|
|
860
|
+
// }
|
|
861
|
+
// };
|
|
862
|
+
let vx = arrayToRenderVXs[actn.index];
|
|
863
|
+
// cl([cx, vx, isObj, forX])
|
|
864
|
+
let cln = await this.createSection(cx, vx, isObj, forX);
|
|
865
|
+
cx.ref.before(cln);
|
|
866
|
+
|
|
867
|
+
|
|
868
|
+
cln.replaceWith(cln.node);
|
|
869
|
+
// cln.node.innerHTML = "";
|
|
870
|
+
// cln.node.append(...cln._re._RealDOM);
|
|
871
|
+
// cl(cln);
|
|
872
|
+
|
|
873
|
+
} else if (actn.action == 'remove') {
|
|
874
|
+
|
|
875
|
+
// cl("Remove", actn);
|
|
876
|
+
// let vx = arrayToRenderVXs[actn.index];
|
|
877
|
+
// let cln = await this.createSection(cx, vx);
|
|
878
|
+
// cx.ref.before(cln);
|
|
879
|
+
|
|
880
|
+
var keyed = cx.key + '_' + actn.index;
|
|
881
|
+
|
|
882
|
+
let tx = cx.ref?.nodes[keyed];
|
|
883
|
+
|
|
884
|
+
if (tx) {
|
|
885
|
+
tx.remove();
|
|
886
|
+
tx.node.remove();
|
|
887
|
+
delete cx.tx?.nodes[keyed];
|
|
888
|
+
// if(ref.isConnected){
|
|
889
|
+
// } else if(ref.node.isConnected){
|
|
890
|
+
|
|
891
|
+
// }
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
// cln.replaceWith(cln.node);
|
|
895
|
+
// cln.node.innerHTML = "";
|
|
896
|
+
// cln.node.append(...cln._re._RealDOM);
|
|
897
|
+
// cl(cln);
|
|
898
|
+
} else {
|
|
899
|
+
// let vx = arrayToRenderVXs[actn.index];
|
|
900
|
+
// let cln = await this.createSection(cx, vx);
|
|
901
|
+
// cx.ref.before(cln);
|
|
902
|
+
|
|
903
|
+
var keyed = cx.key + '_' + actn.index;
|
|
904
|
+
|
|
905
|
+
let tx = cx.ref?.nodes[keyed];
|
|
906
|
+
|
|
907
|
+
let vx = arrayToRenderVXs[actn.index];
|
|
908
|
+
if (tx) {
|
|
909
|
+
// tx._re.reactiveVariables = [...tx._re.reactiveVariables, ...Object.keys(vx)];
|
|
910
|
+
// cl("UPDATE", [tx], vx, tx._re);
|
|
911
|
+
tx._re.vrs = vx;
|
|
912
|
+
if (tx.isObj) {
|
|
913
|
+
if (tx.forX.as['k'] != "") tx._re.update(tx.forX.as['k']);
|
|
914
|
+
else tx._re.update(tx.forX.js);
|
|
915
|
+
if (tx.forX.as['v'] != "") tx._re.update(tx.forX.as['v']);
|
|
916
|
+
// cl("Updating", tx.forX.as['k'], tx.forX.as['v']);
|
|
917
|
+
for (let actnsi = 0; actnsi < actn.updates.length; actnsi++) {
|
|
918
|
+
const actnu = actn.updates[actnsi];
|
|
919
|
+
tx._re.update(actnu.property);
|
|
920
|
+
}
|
|
921
|
+
} else {
|
|
922
|
+
if (tx.forX.as['k'] != "") tx._re.update(tx.forX.as['k']);
|
|
923
|
+
else tx._re.update(tx.forX.js);
|
|
924
|
+
// cl("Updating", tx.forX);
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
// tx._re.renderAll();
|
|
928
|
+
// if(ref.isConnected){
|
|
929
|
+
// } else if(ref.node.isConnected){
|
|
930
|
+
|
|
931
|
+
// }
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
// cln.replaceWith(cln.node);
|
|
935
|
+
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
if (actions.length) await fireRenderHook(cx, 'after-render', cx.ref.parentElement, { items: arrayToRender, actions });
|
|
940
|
+
|
|
941
|
+
} else {
|
|
942
|
+
// cl("NO ARRAY", cx.ref);
|
|
943
|
+
cx.nodes = [];
|
|
944
|
+
}
|
|
945
|
+
// if (cx.name == "if") {
|
|
946
|
+
// let isTrue = this.evalExp(cx.value, this.reactiveVariables);
|
|
947
|
+
// if (isTrue) {
|
|
948
|
+
// await renderSection(cx.ref, cx.doc, this, k);
|
|
949
|
+
// } else {
|
|
950
|
+
// cx.ref.node.replaceWith(cx.ref);
|
|
951
|
+
// }
|
|
952
|
+
// }
|
|
953
|
+
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
|
|
958
|
+
compareLogic(array1, array2) {
|
|
959
|
+
if (array1.length === array2.length) {
|
|
960
|
+
// Perform update-only algorithm when arrays have the same length
|
|
961
|
+
// ...
|
|
962
|
+
return 1;
|
|
963
|
+
} else {
|
|
964
|
+
if (array1.length > array2.length) {
|
|
965
|
+
// Array1 has more elements, indicating there are deleted items
|
|
966
|
+
// Use two-way comparison to find deleted indexes
|
|
967
|
+
// If successful, perform the necessary removals
|
|
968
|
+
// Otherwise, proceed with update and removal comparisons
|
|
969
|
+
// ...
|
|
970
|
+
return 2;
|
|
971
|
+
} else {
|
|
972
|
+
// Array2 has more elements, indicating there are added items
|
|
973
|
+
// Use two-way comparison to find added indexes
|
|
974
|
+
// If successful, perform the necessary additions
|
|
975
|
+
// Otherwise, proceed with update and addition comparisons
|
|
976
|
+
// ...
|
|
977
|
+
return 3;
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
compareArrays(array1, array2) {
|
|
983
|
+
const actions = [];
|
|
984
|
+
// let cpl = this.compareLogic(array1, array2);
|
|
985
|
+
|
|
986
|
+
// if (cpl == 2) {
|
|
987
|
+
// cl("Deleted", this.findDeletedIndexes(array1, array2));
|
|
988
|
+
// } else if (cpl == 3) {
|
|
989
|
+
// cl("Added", this.findAddedIndexes(array1, array2));
|
|
990
|
+
// }
|
|
991
|
+
|
|
992
|
+
const maxLength = Math.max(array1.length, array2.length);
|
|
993
|
+
for (let i = 0; i < maxLength; i++) {
|
|
994
|
+
const element1 = array1[i];
|
|
995
|
+
const element2 = array2[i];
|
|
996
|
+
|
|
997
|
+
if (!element2) {
|
|
998
|
+
actions.push({
|
|
999
|
+
action: 'remove',
|
|
1000
|
+
index: i
|
|
1001
|
+
});
|
|
1002
|
+
} else if (!element1) {
|
|
1003
|
+
actions.push({
|
|
1004
|
+
action: 'add',
|
|
1005
|
+
index: i,
|
|
1006
|
+
element: element2
|
|
1007
|
+
});
|
|
1008
|
+
} else if (!this.deepCompare(element1, element2)) {
|
|
1009
|
+
actions.push({
|
|
1010
|
+
action: 'update',
|
|
1011
|
+
index: i,
|
|
1012
|
+
updates: this.getUpdates(element1, element2)
|
|
1013
|
+
});
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
return actions;
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
findDeletedIndexes(array1, array2) {
|
|
1021
|
+
const deletedIndexes = [];
|
|
1022
|
+
let par = this;
|
|
1023
|
+
|
|
1024
|
+
array1.forEach((item, index) => {
|
|
1025
|
+
const foundIndex = array2.findIndex((el) => par.deepCompare(el, item));
|
|
1026
|
+
if (foundIndex === -1) {
|
|
1027
|
+
deletedIndexes.push(index);
|
|
1028
|
+
}
|
|
1029
|
+
});
|
|
1030
|
+
|
|
1031
|
+
return deletedIndexes;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
findAddedIndexes(array1, array2) {
|
|
1035
|
+
const addedIndexes = [];
|
|
1036
|
+
let par = this;
|
|
1037
|
+
|
|
1038
|
+
array2.forEach((item, index) => {
|
|
1039
|
+
const foundIndex = array1.findIndex((el) => par.deepCompare(el, item));
|
|
1040
|
+
if (foundIndex === -1) {
|
|
1041
|
+
addedIndexes.push(index);
|
|
1042
|
+
}
|
|
1043
|
+
});
|
|
1044
|
+
|
|
1045
|
+
return addedIndexes;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
deepCompare(obj1, obj2) {
|
|
1049
|
+
// Compare objects by stringifying and comparing their JSON representation
|
|
1050
|
+
return JSON.stringify(obj1) === JSON.stringify(obj2);
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
getUpdates(oldObj, newObj) {
|
|
1054
|
+
const updates = [];
|
|
1055
|
+
for (const key in newObj) {
|
|
1056
|
+
if (newObj.hasOwnProperty(key) && newObj[key] !== oldObj[key]) {
|
|
1057
|
+
updates.push({ property: key, value: newObj[key] });
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
return updates;
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
|
|
1064
|
+
getVal(mo, indexName) {
|
|
1065
|
+
let vars = {};
|
|
1066
|
+
try {
|
|
1067
|
+
for (let i = 0; i < this.reactiveVariables.length; i++) {
|
|
1068
|
+
// 2026-09-14: fall back to _vt.Global.vars when a view
|
|
1069
|
+
// doesn't declare this var itself — the real-world case of
|
|
1070
|
+
// a mustache/:if referencing an index.js global (e.g.
|
|
1071
|
+
// `isAdmin`) that this view's own script never assigns.
|
|
1072
|
+
// concatVarsAtLevel() already does this merge for
|
|
1073
|
+
// lookup()/scopedEval(); getVal() (what mustache text nodes
|
|
1074
|
+
// actually call) had its own separate, unmerged read here
|
|
1075
|
+
// and was silently rendering these as empty.
|
|
1076
|
+
let __name = this.reactiveVariables[i];
|
|
1077
|
+
vars[__name] = _vt.View.vars.hasOwnProperty(__name)
|
|
1078
|
+
? _vt.View.vars[__name]
|
|
1079
|
+
: _vt.Global.vars[__name];
|
|
1080
|
+
}
|
|
1081
|
+
for (const ky in this.vrs) {
|
|
1082
|
+
if (Object.prototype.hasOwnProperty.call(this.vrs, ky)) {
|
|
1083
|
+
vars[ky] = this.vrs[ky]
|
|
1084
|
+
}
|
|
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
|
+
}
|
|
1101
|
+
} catch (e) {
|
|
1102
|
+
cl(e);
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
mo = (mo).trim();
|
|
1106
|
+
// cl(["getVal", mo, vars, indexName]);
|
|
1107
|
+
// if (mo == "patientProfile['Gender']") {
|
|
1108
|
+
// cl(["getVal", mo, vars, indexName]);
|
|
1109
|
+
// }
|
|
1110
|
+
if (mo.slice(0, 2) == "{{") {
|
|
1111
|
+
mo = mo.slice(2, -2);
|
|
1112
|
+
}
|
|
1113
|
+
// if (mo == 'new Date(article.createdAt.split("T")[0]).getDate()')
|
|
1114
|
+
// if(mo=="genders[`patientProfile['Gender']`]") cl(["getVal", mo, vars, indexName]);
|
|
1115
|
+
let value = "";
|
|
1116
|
+
let _mo = mo;
|
|
1117
|
+
|
|
1118
|
+
if (mo.indexOf("`") > -1) {
|
|
1119
|
+
var matchesVal = _mo.match(/\.`[\s\S]*?`/g);
|
|
1120
|
+
// cl(["shuuuu", matchesVal, _mo]);
|
|
1121
|
+
if (matchesVal)
|
|
1122
|
+
for (var y = 0; y < matchesVal.length; y++) {
|
|
1123
|
+
_mo = _mo
|
|
1124
|
+
.split(matchesVal[y])
|
|
1125
|
+
.join(
|
|
1126
|
+
"." +
|
|
1127
|
+
this.getVal(
|
|
1128
|
+
matchesVal[y].substr(1).slice(1, -1),
|
|
1129
|
+
indexName
|
|
1130
|
+
)
|
|
1131
|
+
);
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
var matchesVal = _mo.match(/`[\s\S]*?`/g);
|
|
1135
|
+
// cl(["suuuu", matchesVal, _mo]);
|
|
1136
|
+
if (matchesVal)
|
|
1137
|
+
for (var y = 0; y < matchesVal.length; y++) {
|
|
1138
|
+
// cl([
|
|
1139
|
+
// "3uuu",
|
|
1140
|
+
// this.getVal(matchesVal[y].slice(1, -1), vars, indexName),
|
|
1141
|
+
// matchesVal,
|
|
1142
|
+
// y,
|
|
1143
|
+
// matchesVal[y].slice(1, -1),
|
|
1144
|
+
// vars,
|
|
1145
|
+
// indexName,
|
|
1146
|
+
// ]);
|
|
1147
|
+
_mo = _mo
|
|
1148
|
+
.split(matchesVal[y])
|
|
1149
|
+
.join(
|
|
1150
|
+
"'" +
|
|
1151
|
+
this.getVal(matchesVal[y].slice(1, -1), indexName) +
|
|
1152
|
+
"'"
|
|
1153
|
+
);
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
// cl([this.getVal(_mo, vars, indexName), "kuuuuk", _mo, vars, indexName]);
|
|
1157
|
+
return this.getVal(_mo, indexName);
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
if (mo.indexOf(";") > -1) {
|
|
1161
|
+
let zxx = mo.split(";");
|
|
1162
|
+
mo = $.trim(zxx[0]);
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
if (mo.indexOf(" as ") > -1) {
|
|
1166
|
+
mo = mo.split(" as ");
|
|
1167
|
+
return this.getVal(mo[0], indexName);
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
if (indexName) {
|
|
1171
|
+
indexName = indexName.toString();
|
|
1172
|
+
if (
|
|
1173
|
+
mo.indexOf(indexName) > -1 &&
|
|
1174
|
+
mo != indexName &&
|
|
1175
|
+
vars.hasOwnProperty(indexName) &&
|
|
1176
|
+
mo != "index"
|
|
1177
|
+
) {
|
|
1178
|
+
mo = mo.split(indexName).join(vars[indexName]);
|
|
1179
|
+
return this.getVal(mo, indexName);
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
var Ondex = mo.match(/\bindex\b/g);
|
|
1184
|
+
if (Ondex && mo != "index" && vars.hasOwnProperty("index")) {
|
|
1185
|
+
_mo = mo.replace(/\bindex\b/g, vars["index"]);
|
|
1186
|
+
// cl(["Hassss",mo,vars["index"], vars, this.getVal(_mo, vars, indexName)]);
|
|
1187
|
+
return this.getVal(_mo, indexName);
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
// if (this.isJS(mo, vars)) {
|
|
1191
|
+
// cl("get",mo);
|
|
1192
|
+
value = this.lookup(mo, vars);
|
|
1193
|
+
// } else {
|
|
1194
|
+
// value = this.lookup(mo, vars);
|
|
1195
|
+
// // if (mo == "index") cl("kiki", value);
|
|
1196
|
+
// }
|
|
1197
|
+
|
|
1198
|
+
// cl(["VALZZZZZZZZ", mo, vars, value, value ?? ""]);
|
|
1199
|
+
return value ?? "";
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
concatVarsAtLevel(levelVars, parent) {
|
|
1203
|
+
|
|
1204
|
+
if (!parent.view._pv) {
|
|
1205
|
+
var concatenatedVars = { ...levelVars };
|
|
1206
|
+
if (parent.view.vars) {
|
|
1207
|
+
// Global vars (index.js's top-level state, see _vt.Global
|
|
1208
|
+
// above) sit at the lowest priority here — a view or a
|
|
1209
|
+
// narrower scope declaring the same name shadows it, same
|
|
1210
|
+
// as real JS scoping would.
|
|
1211
|
+
concatenatedVars = { ..._vt.Global.vars, ...parent.view.vars, ...concatenatedVars };
|
|
1212
|
+
}
|
|
1213
|
+
return concatenatedVars;
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
var concatenatedVars = { ...levelVars };
|
|
1217
|
+
if (parent.view.vars) {
|
|
1218
|
+
// Object.assign(concatenatedVars, parent.view.vars);
|
|
1219
|
+
concatenatedVars = { ...parent.view.vars, ...concatenatedVars };
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
return this.concatVarsAtLevel(concatenatedVars, parent.view._pv);
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
lookup(name, vaz) {
|
|
1226
|
+
|
|
1227
|
+
// for (const key in _vt.View.vars) {
|
|
1228
|
+
// if (Object.prototype.hasOwnProperty.call(_vt.View.vars, key)) {
|
|
1229
|
+
// vaz[key] = _vt.View.vars[key];
|
|
1230
|
+
// }
|
|
1231
|
+
// }
|
|
1232
|
+
// if(name == '{{new Date(article.createdAt.split("T")[0]).getDate()}}')
|
|
1233
|
+
// cl(["lookup", name, vaz]);
|
|
1234
|
+
// cl(vaz);
|
|
1235
|
+
// cl(this.concatVarsAtLevel(vaz, this));
|
|
1236
|
+
|
|
1237
|
+
let vars = this.concatVarsAtLevel(vaz, this);
|
|
1238
|
+
// cl("VAAZZZ");
|
|
1239
|
+
// cl(vaz);
|
|
1240
|
+
// cl(vars);
|
|
1241
|
+
|
|
1242
|
+
try {
|
|
1243
|
+
var value;
|
|
1244
|
+
var names,
|
|
1245
|
+
index,
|
|
1246
|
+
lookupHit = false;
|
|
1247
|
+
|
|
1248
|
+
if (this.hasProperty(vars, name)) {
|
|
1249
|
+
value = vars[name];
|
|
1250
|
+
// cl(["luuuu", vars, name, value]);
|
|
1251
|
+
} else if (name.indexOf(".") > -1 && name.indexOf("[") == -1) {
|
|
1252
|
+
// cl(["laaaaaa", vars, name, value]);
|
|
1253
|
+
var value = this.scopedEval(vars, name);
|
|
1254
|
+
// cl(["liiii", vars, name, value]);
|
|
1255
|
+
if (!(value || value == 0)) {
|
|
1256
|
+
value = vars;
|
|
1257
|
+
names = name.split(".");
|
|
1258
|
+
index = 0;
|
|
1259
|
+
while (value != null && index < names.length) {
|
|
1260
|
+
if (index === names.length - 1)
|
|
1261
|
+
lookupHit = this.hasProperty(value, names[index]);
|
|
1262
|
+
value = value[names[index++]];
|
|
1263
|
+
//{{v.0.kuku}} v then v.0 then v.0.kuku ...... iterate through nested chains
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
} else {
|
|
1267
|
+
var value = this.scopedEval(vars, name);
|
|
1268
|
+
// if (name == "patientProfile['Gender']") {
|
|
1269
|
+
// cl(["==zzzzzlookup", name, vaz, value, vars, name]);
|
|
1270
|
+
// }
|
|
1271
|
+
// if (name == 'new Date(article.createdAt.split("T")[0]).getDate()') cl(["laaa", vars, name, value]);
|
|
1272
|
+
if (!(value || value == 0)) {
|
|
1273
|
+
if (name.indexOf(".") == -1 && name.indexOf("[") > -1) {
|
|
1274
|
+
let _name = name;
|
|
1275
|
+
var matchesVal = _name.match(/\[[\s\S]*?\]/g);
|
|
1276
|
+
for (var y = 0; y < matchesVal.length; y++) {
|
|
1277
|
+
if (
|
|
1278
|
+
matchesVal[y].indexOf("'") == -1 &&
|
|
1279
|
+
matchesVal[y].indexOf('"') == -1
|
|
1280
|
+
)
|
|
1281
|
+
_name = _name
|
|
1282
|
+
.split(matchesVal[y])
|
|
1283
|
+
.join("['" + matchesVal[y].slice(1, -1) + "']");
|
|
1284
|
+
}
|
|
1285
|
+
var value = this.scopedEval(vars, _name);
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
if (this.isFunction(value)) value = value.call(value);
|
|
1291
|
+
|
|
1292
|
+
// if (name == "patientProfile['Gender']") {
|
|
1293
|
+
// cl(["===========lookup", name, vaz, value]);
|
|
1294
|
+
// }
|
|
1295
|
+
|
|
1296
|
+
// if (name == 'new Date(article.createdAt.split("T")[0]).getDate()') cl(["lookup", name, vaz, value]);
|
|
1297
|
+
|
|
1298
|
+
// if (value || value==0) return value;
|
|
1299
|
+
|
|
1300
|
+
// else {
|
|
1301
|
+
// value = this.scopedEval(vars, name);
|
|
1302
|
+
// }
|
|
1303
|
+
|
|
1304
|
+
// cl("n: ",name," val: ",value," v: ",vaz)
|
|
1305
|
+
} catch (e) {
|
|
1306
|
+
reportLumenError({ stage: 'lookup', expr: name, error: e });
|
|
1307
|
+
return "";
|
|
1308
|
+
}
|
|
1309
|
+
// if (name == 'new Date(article.createdAt.split("T")[0]).getDate()') cl("halaaaaaa", value);
|
|
1310
|
+
// cl("halaaaaaa", value);
|
|
1311
|
+
return value;
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
|
|
1315
|
+
objectToString = Object.prototype.toString;
|
|
1316
|
+
isArray =
|
|
1317
|
+
Array.isArray ||
|
|
1318
|
+
function isArrayPolyfill(object) {
|
|
1319
|
+
return objectToString.call(object) === "[object Array]";
|
|
1320
|
+
};
|
|
1321
|
+
isFunction(object) {
|
|
1322
|
+
return typeof object === "function";
|
|
1323
|
+
}
|
|
1324
|
+
typeStr(obj) {
|
|
1325
|
+
return this.isArray(obj) ? "array" : typeof obj;
|
|
1326
|
+
}
|
|
1327
|
+
hasProperty(obj, propName) {
|
|
1328
|
+
return obj != null && typeof obj === "object" && propName in obj;
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
createEl(tag, attrs, children, events, doc) {
|
|
1332
|
+
const _el = document.createElement(tag);
|
|
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
|
+
|
|
1352
|
+
_el.isSub = false;
|
|
1353
|
+
if (attrs.hasOwnProperty('view')) {
|
|
1354
|
+
_el.isSub = true;
|
|
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
|
+
}
|
|
1419
|
+
this.view.views.push(_el);
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
_el.events = {};
|
|
1423
|
+
for (const prop in attrs) {
|
|
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;
|
|
1425
|
+
try {
|
|
1426
|
+
// cl("DD", doc);
|
|
1427
|
+
let val = (doc && doc.ax.hasOwnProperty(prop)) ? "" : attrs[prop];
|
|
1428
|
+
if (prop == "value") {
|
|
1429
|
+
_el.value = val;
|
|
1430
|
+
} else _el.setAttribute(prop, val);
|
|
1431
|
+
} catch (e) {
|
|
1432
|
+
cl(e);
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
for (const prop in events) {
|
|
1436
|
+
try {
|
|
1437
|
+
_el.events[prop] = events[prop];
|
|
1438
|
+
} catch (e) {
|
|
1439
|
+
cl(e);
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
if (children.length) _el.append(...children);
|
|
1443
|
+
|
|
1444
|
+
// 2026-09-16: @after-render generalized to fire on MOUNT for every
|
|
1445
|
+
// element, not just :for items / :if sections. Those two already
|
|
1446
|
+
// get their own batch-level hook fired elsewhere (updateLXRs() /
|
|
1447
|
+
// showSectionCX() / hideSectionCX()) — with :items/:visible extra
|
|
1448
|
+
// data a per-element mount fire here can't provide, and firing
|
|
1449
|
+
// both would double-fire for them — so they're explicitly
|
|
1450
|
+
// excluded here via the same directive-attribute check createEl()
|
|
1451
|
+
// already does above. No @before-render equivalent for a generic
|
|
1452
|
+
// mount: there's no prior DOM state to act on before an element
|
|
1453
|
+
// exists for the first time (unlike :for/:if's before-render,
|
|
1454
|
+
// which runs against DOM that's already connected).
|
|
1455
|
+
if (events && events['@after-render'] && !(attrs && (attrs.hasOwnProperty(':for') || attrs.hasOwnProperty(':if') || attrs.hasOwnProperty(':else-if') || attrs.hasOwnProperty(':else')))) {
|
|
1456
|
+
fireRenderHook({ doc: doc }, 'after-render', _el, {});
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
// 2026-09-16: V1's real automatic plugin-init pass, ported
|
|
1460
|
+
// faithfully from bea.js's own renderPlugins()/dtp() (real source:
|
|
1461
|
+
// SBEACDN/s.beacdn.com/beajs/core.js, not reverse-engineered from
|
|
1462
|
+
// minified code) — [sl]/[color]/[time]/[date]/[datetime] elements
|
|
1463
|
+
// become their respective widgets on mount, no explicit script
|
|
1464
|
+
// call needed, matching V1's documented behavior (lumenjs-
|
|
1465
|
+
// spec.md §5.6/§6.7). Built into core directly rather than living
|
|
1466
|
+
// only in the --with-plugins bundle (explicit choice, 2026-09-16)
|
|
1467
|
+
// — autoInitPlugins() itself guards every call on the matching
|
|
1468
|
+
// $.fn.* method actually existing, so a plain (no --with-plugins)
|
|
1469
|
+
// project's dom-shim-based bundle silently no-ops instead of
|
|
1470
|
+
// throwing when it encounters one of these attributes.
|
|
1471
|
+
autoInitPlugins(_el, attrs);
|
|
1472
|
+
|
|
1473
|
+
return _el;
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
// processAttr(n, v, res) {
|
|
1477
|
+
// if (n == 'scoped') {
|
|
1478
|
+
|
|
1479
|
+
// } else if (n == 'binds') {
|
|
1480
|
+
// res.domNode.mxs = v.split(",").map((s) => s.trim());
|
|
1481
|
+
// } else {
|
|
1482
|
+
// if (n.substr(0, 1) == "@") {
|
|
1483
|
+
// res.domNode.setAttribute(n.substr(0,1), v);
|
|
1484
|
+
// } else res.domNode.setAttribute(n, v);
|
|
1485
|
+
// }
|
|
1486
|
+
// }
|
|
1487
|
+
|
|
1488
|
+
// getPVsScope(par) {
|
|
1489
|
+
// let _vz = {};
|
|
1490
|
+
|
|
1491
|
+
// if (par) {
|
|
1492
|
+
// cl("VZZZZ", par.view.vars);
|
|
1493
|
+
// _vz = this.getPVsScope(par.view._pv);
|
|
1494
|
+
// }
|
|
1495
|
+
|
|
1496
|
+
// return _vz;
|
|
1497
|
+
// }
|
|
1498
|
+
|
|
1499
|
+
// getScope() {
|
|
1500
|
+
// let vz = clone(_vt.View.vars) ?? {};
|
|
1501
|
+
// let _vz = this.getPVsScope(this.view._pv);
|
|
1502
|
+
// cl("VXXXX", vz, _vz);
|
|
1503
|
+
|
|
1504
|
+
// return vz;
|
|
1505
|
+
// }
|
|
1506
|
+
|
|
1507
|
+
evalExp(expr, vars) {
|
|
1508
|
+
// let svs = this.getScope();
|
|
1509
|
+
// cl("SVS", svs);
|
|
1510
|
+
// cl("SVS", expr, vars);
|
|
1511
|
+
let rvars = {};
|
|
1512
|
+
try {
|
|
1513
|
+
for (let i = 0; i < vars.length; i++) {
|
|
1514
|
+
// 2026-09-14: same Global-vars fallback as getVal() above —
|
|
1515
|
+
// without it this object always carries an explicit
|
|
1516
|
+
// `name: undefined` for anything index.js declares (since
|
|
1517
|
+
// it's never in _vt.View.vars), and because it's spread
|
|
1518
|
+
// LAST in concatVarsAtLevel()'s merge (highest priority),
|
|
1519
|
+
// that explicit undefined would silently clobber the real
|
|
1520
|
+
// value scopedEval() would otherwise have found in
|
|
1521
|
+
// _vt.Global.vars — breaking :if/:for conditions on any
|
|
1522
|
+
// index.js-declared var, not just mustache text (getVal()).
|
|
1523
|
+
let __name = vars[i];
|
|
1524
|
+
rvars[__name] = _vt.View.vars.hasOwnProperty(__name)
|
|
1525
|
+
? _vt.View.vars[__name]
|
|
1526
|
+
: _vt.Global.vars[__name];
|
|
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
|
+
}
|
|
1561
|
+
} catch (e) {
|
|
1562
|
+
cl(e);
|
|
1563
|
+
}
|
|
1564
|
+
//If Lookaup
|
|
1565
|
+
// let value = this.lookup(expr, vars);
|
|
1566
|
+
// if (value || value == 0) return false;
|
|
1567
|
+
try {
|
|
1568
|
+
var value = this.scopedEval(rvars, expr);
|
|
1569
|
+
// cl("VV", value);
|
|
1570
|
+
if (value && value != 0) return true;
|
|
1571
|
+
} catch (e) {
|
|
1572
|
+
reportLumenError({ stage: 'condition', expr: expr, error: e });
|
|
1573
|
+
return false;
|
|
1574
|
+
}
|
|
1575
|
+
return false;
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
splitTextWithMustaches(text, mustaches) {
|
|
1579
|
+
// Sort mustaches by start index to process in order
|
|
1580
|
+
mustaches.sort((a, b) => a.start - b.start);
|
|
1581
|
+
|
|
1582
|
+
const elements = [];
|
|
1583
|
+
let currentIndex = 0;
|
|
1584
|
+
|
|
1585
|
+
for (const mustache of mustaches) {
|
|
1586
|
+
// Add static text before mustache if any
|
|
1587
|
+
if (currentIndex < mustache.start) {
|
|
1588
|
+
elements.push({
|
|
1589
|
+
type: 'static',
|
|
1590
|
+
content: text.substring(currentIndex, mustache.start)
|
|
1591
|
+
});
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
// Add the mustache
|
|
1595
|
+
elements.push({
|
|
1596
|
+
type: 'mustache',
|
|
1597
|
+
jst: mustache.jst,
|
|
1598
|
+
rvs: mustache.rvs,
|
|
1599
|
+
content: text.substring(mustache.start, mustache.end)
|
|
1600
|
+
});
|
|
1601
|
+
|
|
1602
|
+
// Update current index
|
|
1603
|
+
currentIndex = mustache.end;
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
// Add any remaining static text after the last mustache
|
|
1607
|
+
if (currentIndex < text.length) {
|
|
1608
|
+
elements.push({
|
|
1609
|
+
type: 'static',
|
|
1610
|
+
content: text.substring(currentIndex)
|
|
1611
|
+
});
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
return elements;
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
|
|
1618
|
+
walk(doc, parent) {
|
|
1619
|
+
var par = this;
|
|
1620
|
+
var tx, el;
|
|
1621
|
+
switch (doc.type) {
|
|
1622
|
+
case 'text':
|
|
1623
|
+
|
|
1624
|
+
if (doc.mss.length) {
|
|
1625
|
+
|
|
1626
|
+
let splitIt = true;
|
|
1627
|
+
|
|
1628
|
+
if (doc.tag == "textarea") {
|
|
1629
|
+
splitIt = false;
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
if (splitIt) {
|
|
1633
|
+
let splits = this.splitTextWithMustaches(doc.content, doc.mss);
|
|
1634
|
+
// cl(splits);
|
|
1635
|
+
for (let si = 0; si < splits.length; si++) {
|
|
1636
|
+
const split = splits[si];
|
|
1637
|
+
if (split.type == 'static') {
|
|
1638
|
+
let txnd = document.createTextNode(split.content);
|
|
1639
|
+
if (!parent) par._RealDOM.push(txnd);
|
|
1640
|
+
(tx ?? (tx = [])).push(txnd);
|
|
1641
|
+
} else {
|
|
1642
|
+
let txnd = document.createTextNode("");
|
|
1643
|
+
for (let ri = 0; ri < split.rvs.length; ri++) {
|
|
1644
|
+
const element = split.rvs[ri];
|
|
1645
|
+
(this._effects[element] ?? (this._effects[element] = [])).push({
|
|
1646
|
+
"type": "text",
|
|
1647
|
+
"content": split.content,
|
|
1648
|
+
"jst": split.jst,
|
|
1649
|
+
"rvs": split.rvs,
|
|
1650
|
+
"isSplit": true,
|
|
1651
|
+
"nd": txnd,
|
|
1652
|
+
"tag": doc.tag
|
|
1653
|
+
});
|
|
1654
|
+
}
|
|
1655
|
+
if (!parent) par._RealDOM.push(txnd);
|
|
1656
|
+
(tx ?? (tx = [])).push(txnd);
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
return tx;
|
|
1660
|
+
} else {
|
|
1661
|
+
tx = document.createTextNode("");
|
|
1662
|
+
for (let mui = 0; mui < doc.mss.length; mui++) {
|
|
1663
|
+
const mus = doc.mss[mui];
|
|
1664
|
+
|
|
1665
|
+
for (let ri = 0; ri < mus.rvs.length; ri++) {
|
|
1666
|
+
const element = mus.rvs[ri];
|
|
1667
|
+
(this._effects[element] ?? (this._effects[element] = [])).push({
|
|
1668
|
+
"type": "text",
|
|
1669
|
+
"content": doc.content,
|
|
1670
|
+
"splits": this.splitTextWithMustaches(doc.content, doc.mss),
|
|
1671
|
+
"jst": mus.jst,
|
|
1672
|
+
"rvs": mus.rvs,
|
|
1673
|
+
"isSplit": false,
|
|
1674
|
+
"nd": tx,
|
|
1675
|
+
"tag": doc.tag
|
|
1676
|
+
});
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
if (!parent) par._RealDOM.push(tx);
|
|
1681
|
+
return tx;
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
|
|
1685
|
+
} else {
|
|
1686
|
+
tx = document.createTextNode(doc.content);
|
|
1687
|
+
if (!parent) par._RealDOM.push(tx);
|
|
1688
|
+
return tx;
|
|
1689
|
+
}
|
|
1690
|
+
break;
|
|
1691
|
+
case 'sections':
|
|
1692
|
+
case 'section':
|
|
1693
|
+
var _dd = md5(new Date().getTime() / 1000 + "::" + Math.random());
|
|
1694
|
+
tx = document.createTextNode("");
|
|
1695
|
+
var typeN = null;
|
|
1696
|
+
if (doc.attrs.hasOwnProperty(":else")) typeN = "else";
|
|
1697
|
+
else if (doc.attrs.hasOwnProperty(":else-if")) typeN = "else-if";
|
|
1698
|
+
else if (doc.attrs.hasOwnProperty(":if")) typeN = "if";
|
|
1699
|
+
else typeN = 'for';
|
|
1700
|
+
|
|
1701
|
+
if (doc.type == "section") {
|
|
1702
|
+
|
|
1703
|
+
var chain = [];
|
|
1704
|
+
if (typeN == "else-if" || typeN == "else") {
|
|
1705
|
+
try {
|
|
1706
|
+
let lastInChain = this._CXR.at(-1);
|
|
1707
|
+
if (lastInChain) {
|
|
1708
|
+
_dd = lastInChain.key;
|
|
1709
|
+
chain.push(...lastInChain.chain, lastInChain);
|
|
1710
|
+
}
|
|
1711
|
+
} catch (e) {
|
|
1712
|
+
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1716
|
+
el = par.createEl(doc.name, doc.attrs, [], doc.evs, doc);
|
|
1717
|
+
tx.node = el;
|
|
1718
|
+
|
|
1719
|
+
// cl("SECCCCC", doc);
|
|
1720
|
+
// return;
|
|
1721
|
+
this._CXR.push({
|
|
1722
|
+
"type": "section",
|
|
1723
|
+
"name": typeN,
|
|
1724
|
+
"content": doc.cond,
|
|
1725
|
+
"doc": doc,
|
|
1726
|
+
"key": _dd,
|
|
1727
|
+
"chain": chain,
|
|
1728
|
+
"ref": tx
|
|
1729
|
+
});
|
|
1730
|
+
|
|
1731
|
+
this.setEffects(doc, el);
|
|
1732
|
+
|
|
1733
|
+
} else if (doc.type == "sections") {
|
|
1734
|
+
|
|
1735
|
+
tx.key = _dd;
|
|
1736
|
+
tx.node = null;
|
|
1737
|
+
tx.nodes = {};
|
|
1738
|
+
|
|
1739
|
+
let docRaw = doc;
|
|
1740
|
+
// delete docRaw.attrs[':for'];
|
|
1741
|
+
// delete docRaw.attrs[':for-limit'];
|
|
1742
|
+
// delete docRaw.attrs[':for-if'];
|
|
1743
|
+
// delete docRaw.attrs[':for-offset'];
|
|
1744
|
+
// docRaw.type = 'tag';
|
|
1745
|
+
|
|
1746
|
+
// cl("SECCCCCSSS", doc);
|
|
1747
|
+
// return;
|
|
1748
|
+
|
|
1749
|
+
this._LXR.push({
|
|
1750
|
+
"type": "sections",
|
|
1751
|
+
"name": typeN,
|
|
1752
|
+
"cond": doc.attrs.hasOwnProperty(":for-if") ? doc.attrs[':for-if'] : null,
|
|
1753
|
+
"limit": doc.attrs.hasOwnProperty(":for-limit") ? doc.attrs[':for-limit'] : 0,
|
|
1754
|
+
"offset": doc.attrs.hasOwnProperty(":for-offset") ? doc.attrs[':for-offset'] : 0,
|
|
1755
|
+
"content": doc.content,
|
|
1756
|
+
"forX": doc.forX,
|
|
1757
|
+
"doc": docRaw,
|
|
1758
|
+
"key": _dd,
|
|
1759
|
+
"atr": [],
|
|
1760
|
+
"ref": tx
|
|
1761
|
+
});
|
|
1762
|
+
|
|
1763
|
+
// cl(tx, doc);
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
|
|
1767
|
+
if (!parent) par._RealDOM.push(tx);
|
|
1768
|
+
return tx;
|
|
1769
|
+
break;
|
|
1770
|
+
case 'tag':
|
|
1771
|
+
var _dd = md5(new Date().getTime() / 1000 + "::" + Math.random());
|
|
1772
|
+
|
|
1773
|
+
// cl("DOC", doc);
|
|
1774
|
+
|
|
1775
|
+
if (doc.name.toLowerCase() == "settings") {
|
|
1776
|
+
if (par.view.type == "main") {
|
|
1777
|
+
var defaultSettings = {
|
|
1778
|
+
layout: "default",
|
|
1779
|
+
requireAuth: false,
|
|
1780
|
+
};
|
|
1781
|
+
try {
|
|
1782
|
+
let settingsC = doc.children[0].content;
|
|
1783
|
+
let settings = {};
|
|
1784
|
+
eval("settings = " + settingsC + ";");
|
|
1785
|
+
if (settings) {
|
|
1786
|
+
if (settings.layout == null) settings.layout = "default";
|
|
1787
|
+
if (settings.requireAuth == null) settings.requireAuth = false;
|
|
1788
|
+
par.view.settings = settings;
|
|
1789
|
+
} else {
|
|
1790
|
+
par.view.settings = defaultSettings;
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
} catch (e) {
|
|
1794
|
+
setError(e, "Error in your settings tag inside the '" + par.view.name.toLowerCase() + "' main view!");
|
|
1795
|
+
par.view.settings = defaultSettings;
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
} else if (doc.name.toLowerCase() == "script" || doc.name.toLowerCase() == "js") {
|
|
1799
|
+
let child = doc.children[0];
|
|
1800
|
+
let js = child.content;
|
|
1801
|
+
let jst = child.jst;
|
|
1802
|
+
let isScoped = false;
|
|
1803
|
+
if (doc.attrs.hasOwnProperty('scoped')) {
|
|
1804
|
+
delete doc.attrs['scoped'];
|
|
1805
|
+
isScoped = true;
|
|
1806
|
+
}
|
|
1807
|
+
el = par.createEl("script", doc.attrs, [], doc.evs, doc);
|
|
1808
|
+
el._sc = isScoped;
|
|
1809
|
+
el._dd = _dd;
|
|
1810
|
+
// el._js = js;
|
|
1811
|
+
el._jst = jst;
|
|
1812
|
+
// cl(child);
|
|
1813
|
+
// _jswacrk.trigger('msg', {
|
|
1814
|
+
// "code": js,
|
|
1815
|
+
// "type": par.view.type,
|
|
1816
|
+
// "key": _dd
|
|
1817
|
+
// });
|
|
1818
|
+
// _jsuid[_dd] = el;
|
|
1819
|
+
(par._jj[_dd] ?? (par._jj[_dd] = [])).push({
|
|
1820
|
+
"nd": el,
|
|
1821
|
+
});
|
|
1822
|
+
|
|
1823
|
+
} else if (doc.name.toLowerCase() == "style") {
|
|
1824
|
+
let css = doc.children[0].content;
|
|
1825
|
+
let isScoped = false;
|
|
1826
|
+
|
|
1827
|
+
el = par.createEl("style", {}, [], doc.evs, doc);
|
|
1828
|
+
|
|
1829
|
+
if (doc.attrs.hasOwnProperty('scoped')) {
|
|
1830
|
+
if (!parent) {
|
|
1831
|
+
if (par.view._dd) _dd = par.view._dd;
|
|
1832
|
+
else {
|
|
1833
|
+
par.view._dd = _dd;
|
|
1834
|
+
if (par.view.type == "main") $('[body]')[0].setAttribute('vuid', _dd);
|
|
1835
|
+
}
|
|
1836
|
+
} else {
|
|
1837
|
+
if (parent._dd) _dd = parent._dd;
|
|
1838
|
+
else {
|
|
1839
|
+
parent._dd = _dd;
|
|
1840
|
+
parent.setAttribute('vuid', _dd);
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
delete doc.attrs['scoped'];
|
|
1844
|
+
isScoped = true;
|
|
1845
|
+
el._sc = isScoped;
|
|
1846
|
+
el._dd = _dd;
|
|
1847
|
+
el._css = css;
|
|
1848
|
+
(par._cc[_dd] ?? (par._cc[_dd] = [])).push({
|
|
1849
|
+
"nd": el,
|
|
1850
|
+
"_css": css
|
|
1851
|
+
});
|
|
1852
|
+
} else {
|
|
1853
|
+
el._sc = isScoped;
|
|
1854
|
+
el._dd = _dd;
|
|
1855
|
+
el.textContent = css;
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
|
|
1859
|
+
for (const prop in doc.attrs) {
|
|
1860
|
+
try {
|
|
1861
|
+
_el.setAttribute(prop, doc.attrs[prop]);
|
|
1862
|
+
} catch (e) {
|
|
1863
|
+
cl(e);
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
} else if (doc.name.toLowerCase() == "icon") {
|
|
1867
|
+
let child = par.createEl("span", {
|
|
1868
|
+
"class": "iconify",
|
|
1869
|
+
"data-icon": doc?.icon ?? "mdi:home"
|
|
1870
|
+
}, [], {}, null);
|
|
1871
|
+
|
|
1872
|
+
el = par.createEl("span", doc.attrs, [child], doc.evs, doc);
|
|
1873
|
+
|
|
1874
|
+
} else if (doc.name.toLowerCase() == "slot") {
|
|
1875
|
+
// Layout slot content (2026-09-15) — a view wraps
|
|
1876
|
+
// content meant for a named layout region (anything
|
|
1877
|
+
// other than the default `[body]` target) in
|
|
1878
|
+
// <slot name="...">...</slot>. Collected onto
|
|
1879
|
+
// par.view._slots[name] (an array of real, walked DOM
|
|
1880
|
+
// nodes) instead of being pushed into _RealDOM —
|
|
1881
|
+
// renderView() injects each populated slot into its
|
|
1882
|
+
// matching `[slot="name"]` layout element, after the
|
|
1883
|
+
// default content. Same non-participating-in-_RealDOM
|
|
1884
|
+
// shape as the `settings` branch above (no `el` gets
|
|
1885
|
+
// set here either) — but slot content needs to be
|
|
1886
|
+
// walked/rendered, unlike settings' raw config text, so
|
|
1887
|
+
// this uses a throwaway wrapper purely as a real DOM
|
|
1888
|
+
// parent for that walk; it's never attached anywhere.
|
|
1889
|
+
let slotName = doc.attrs && doc.attrs.name;
|
|
1890
|
+
if (slotName) {
|
|
1891
|
+
let tempWrapper = document.createElement('div');
|
|
1892
|
+
let childs = [];
|
|
1893
|
+
for (let i = 0; i < doc.children.length; i++) {
|
|
1894
|
+
const dc = doc.children[i];
|
|
1895
|
+
let chils = par.walk(dc, tempWrapper);
|
|
1896
|
+
if (chils) {
|
|
1897
|
+
if (Array.isArray(chils)) {
|
|
1898
|
+
if (chils.length) childs = [...childs, ...chils];
|
|
1899
|
+
} else {
|
|
1900
|
+
childs.push(chils);
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
(par.view._slots ?? (par.view._slots = {}))[slotName] = childs;
|
|
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
|
+
});
|
|
1922
|
+
} else {
|
|
1923
|
+
el = par.createEl(doc.name, doc.attrs, [], doc.evs, doc);
|
|
1924
|
+
|
|
1925
|
+
if (!doc.isV && !el.isSub) {
|
|
1926
|
+
let childs = [];
|
|
1927
|
+
for (let i = 0; i < doc.children.length; i++) {
|
|
1928
|
+
const dc = doc.children[i];
|
|
1929
|
+
let chils = par.walk(dc, el);
|
|
1930
|
+
if (chils) {
|
|
1931
|
+
if (Array.isArray(chils)) {
|
|
1932
|
+
if (chils.length) childs = [...childs, ...chils];
|
|
1933
|
+
} else {
|
|
1934
|
+
childs.push(chils);
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
childs.length ? el.append(...childs) : null;
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1942
|
+
this.setEffects(doc, el);
|
|
1943
|
+
|
|
1944
|
+
if (el) {
|
|
1945
|
+
if (!parent) par._RealDOM.push(el);
|
|
1946
|
+
}
|
|
1947
|
+
return el;
|
|
1948
|
+
|
|
1949
|
+
break;
|
|
1950
|
+
case 'comment':
|
|
1951
|
+
|
|
1952
|
+
break;
|
|
1953
|
+
default:
|
|
1954
|
+
break;
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
|
|
1958
|
+
setEffects(doc, el) {
|
|
1959
|
+
let rvs = [];
|
|
1960
|
+
if (Object.keys(doc.ax).length) {
|
|
1961
|
+
for (const attrName in doc.ax) {
|
|
1962
|
+
if (Object.hasOwnProperty.call(doc.ax, attrName)) {
|
|
1963
|
+
const attrMustaches = doc.ax[attrName];
|
|
1964
|
+
for (let i = 0; i < attrMustaches.length; i++) {
|
|
1965
|
+
const mus = attrMustaches[i];
|
|
1966
|
+
for (let ri = 0; ri < mus.rvs.length; ri++) {
|
|
1967
|
+
const element = mus.rvs[ri];
|
|
1968
|
+
rvs.push(element);
|
|
1969
|
+
(this._effects[element] ?? (this._effects[element] = [])).push({
|
|
1970
|
+
"type": "attr",
|
|
1971
|
+
"name": attrName,
|
|
1972
|
+
"content": doc.attrs[attrName],
|
|
1973
|
+
"splits": this.splitTextWithMustaches(doc.attrs[attrName], doc.ax[attrName]),
|
|
1974
|
+
"jst": mus.jst,
|
|
1975
|
+
"rvs": mus.rvs,
|
|
1976
|
+
"nd": el
|
|
1977
|
+
});
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1984
|
+
if (Object.keys(doc.ex).length) {
|
|
1985
|
+
for (const ky in doc.ex) {
|
|
1986
|
+
if (Object.hasOwnProperty.call(doc.ex, ky)) {
|
|
1987
|
+
const mss = doc.ex[ky];
|
|
1988
|
+
for (let i = 0; i < mss.length; i++) {
|
|
1989
|
+
const mus = mss[i];
|
|
1990
|
+
for (let ri = 0; ri < mus.rvs.length; ri++) {
|
|
1991
|
+
const element = mus.rvs[ri];
|
|
1992
|
+
rvs.push(element);
|
|
1993
|
+
(this._effects[element] ?? (this._effects[element] = [])).push({
|
|
1994
|
+
"type": "event",
|
|
1995
|
+
"name": ky,
|
|
1996
|
+
"content": doc.evs[ky],
|
|
1997
|
+
"splits": this.splitTextWithMustaches(doc.evs[ky], doc.ex[ky]),
|
|
1998
|
+
"jst": mus.jst,
|
|
1999
|
+
"rvs": mus.rvs,
|
|
2000
|
+
"nd": el
|
|
2001
|
+
});
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
|
|
2008
|
+
return rvs.filter((value, index, self) => {
|
|
2009
|
+
return self.indexOf(value) === index;
|
|
2010
|
+
});
|
|
2011
|
+
}
|
|
2012
|
+
|
|
2013
|
+
async createSection(cx, vx, isObj, forX) {
|
|
2014
|
+
var keyed = cx.key + '_' + vx.index;
|
|
2015
|
+
// cl("DD", cx, vx, keyed);
|
|
2016
|
+
var tx = document.createTextNode("");
|
|
2017
|
+
var typeN = null;
|
|
2018
|
+
if (cx.doc.attrs.hasOwnProperty(":else")) typeN = "else";
|
|
2019
|
+
else if (cx.doc.attrs.hasOwnProperty(":else-if")) typeN = "else-if";
|
|
2020
|
+
else if (cx.doc.attrs.hasOwnProperty(":if")) typeN = "if";
|
|
2021
|
+
else typeN = 'for';
|
|
2022
|
+
|
|
2023
|
+
// cl("CREATE", cx.doc.name, cx.doc.attrs, [], cx.doc.evs, cx.doc);
|
|
2024
|
+
|
|
2025
|
+
let el = this.createEl(cx.doc.name, cx.doc.attrs, [], cx.doc.evs, cx.doc);
|
|
2026
|
+
tx.node = el;
|
|
2027
|
+
tx.key = cx.key;
|
|
2028
|
+
tx.keyed = keyed;
|
|
2029
|
+
tx.isObj = isObj;
|
|
2030
|
+
tx.forX = forX;
|
|
2031
|
+
tx.vx = vx;
|
|
2032
|
+
|
|
2033
|
+
// cl("RENDERSECTION", tx, cx.doc, this, cx.key);
|
|
2034
|
+
|
|
2035
|
+
// this._CXR.push({
|
|
2036
|
+
// "type": "section",
|
|
2037
|
+
// "name": typeN,
|
|
2038
|
+
// "value": doc.cond,
|
|
2039
|
+
// "doc": doc,
|
|
2040
|
+
// "key": _dd,
|
|
2041
|
+
// "chain": chain,
|
|
2042
|
+
// "ref": tx
|
|
2043
|
+
// });
|
|
2044
|
+
|
|
2045
|
+
|
|
2046
|
+
let scsc = await renderSection(tx, cx.doc, this, cx.key);
|
|
2047
|
+
// cl(23);
|
|
2048
|
+
// cl(scsc);
|
|
2049
|
+
scsc._re = scsc;
|
|
2050
|
+
|
|
2051
|
+
|
|
2052
|
+
|
|
2053
|
+
// let rvs = scsc._re.setEffects(cx.doc, el);
|
|
2054
|
+
// // scsc._re.rvs = _vt.View.vars;
|
|
2055
|
+
// // scsc._re.reactiveVariables = rvs;
|
|
2056
|
+
|
|
2057
|
+
// scsc._re.renderAll('sections');
|
|
2058
|
+
|
|
2059
|
+
cx.ref.nodes[keyed] = tx;
|
|
2060
|
+
|
|
2061
|
+
|
|
2062
|
+
return tx;
|
|
2063
|
+
}
|
|
2064
|
+
|
|
2065
|
+
|
|
2066
|
+
attrString(attrs) {
|
|
2067
|
+
var buff = [];
|
|
2068
|
+
for (var key in attrs) {
|
|
2069
|
+
// if(key != "@click")
|
|
2070
|
+
buff.push(key + '="' + attrs[key] + '"');
|
|
2071
|
+
}
|
|
2072
|
+
if (!buff.length) return '';
|
|
2073
|
+
return ' ' + buff.join(' ');
|
|
2074
|
+
}
|
|
2075
|
+
_stringify(buff, doc) {
|
|
2076
|
+
var par = this;
|
|
2077
|
+
switch (doc.type) {
|
|
2078
|
+
case 'text':
|
|
2079
|
+
return buff + doc.content;
|
|
2080
|
+
case 'tag':
|
|
2081
|
+
buff += '<' + doc.name + (doc.attrs ? par.attrString(doc.attrs) : '') + (doc.isV ? '/>' : '>');
|
|
2082
|
+
if (doc.isV) return buff;
|
|
2083
|
+
for (let i = 0; i < doc.children.length; i++) {
|
|
2084
|
+
const dc = doc.children[i];
|
|
2085
|
+
buff = buff + (par._stringify('', dc));
|
|
2086
|
+
}
|
|
2087
|
+
return buff + '</' + doc.name + '>';
|
|
2088
|
+
case 'comment':
|
|
2089
|
+
// buff += '<!--' + doc.comment + '-->';
|
|
2090
|
+
return buff;
|
|
2091
|
+
default:
|
|
2092
|
+
return '';
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
stringify(doc) {
|
|
2096
|
+
var par = this;
|
|
2097
|
+
return doc.reduce(function (token, rootEl) {
|
|
2098
|
+
return token + par._stringify('', rootEl);
|
|
2099
|
+
}, '');
|
|
2100
|
+
};
|
|
2101
|
+
}
|
|
2102
|
+
|
|
2103
|
+
|
|
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) {
|
|
2124
|
+
|
|
2125
|
+
var reactiveVariables = hst.reactiveVars;
|
|
2126
|
+
hst = hst.hst;
|
|
2127
|
+
|
|
2128
|
+
let _re = new _lm(new _v({
|
|
2129
|
+
"name": n,
|
|
2130
|
+
"type": type,
|
|
2131
|
+
"hst": hst,
|
|
2132
|
+
"vars": ownVars ?? tx?.vx,
|
|
2133
|
+
"fns": ownFns,
|
|
2134
|
+
"views": ownViews,
|
|
2135
|
+
"rvs": reactiveVariables,
|
|
2136
|
+
"_pv": _pv
|
|
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;
|
|
2142
|
+
|
|
2143
|
+
if (Object.keys(_re._cc).length) {
|
|
2144
|
+
|
|
2145
|
+
// let cssObjs = {};
|
|
2146
|
+
// for (const ky in _re._cc) {
|
|
2147
|
+
// if (Object.hasOwnProperty.call(_re._cc, ky)) {
|
|
2148
|
+
// const csses = _re._cc[ky];
|
|
2149
|
+
// for (let inde = 0; inde < csses.length; inde++) {
|
|
2150
|
+
// const css = csses[inde];
|
|
2151
|
+
// let myKey = ky + "_::" + inde;
|
|
2152
|
+
// cssObjs[myKey] = css._css;
|
|
2153
|
+
// }
|
|
2154
|
+
// }
|
|
2155
|
+
// }
|
|
2156
|
+
|
|
2157
|
+
|
|
2158
|
+
|
|
2159
|
+
|
|
2160
|
+
for (const ky in _re._cc) {
|
|
2161
|
+
if (Object.hasOwnProperty.call(_re._cc, ky)) {
|
|
2162
|
+
const csses = _re._cc[ky];
|
|
2163
|
+
for (let inde = 0; inde < csses.length; inde++) {
|
|
2164
|
+
let prom = new defer();
|
|
2165
|
+
const css = csses[inde];
|
|
2166
|
+
_csswrk.trigger('css-ready', {
|
|
2167
|
+
"csses": [css._css],
|
|
2168
|
+
"pre": "[vuid='" + ky + "']",
|
|
2169
|
+
"key": ky
|
|
2170
|
+
});
|
|
2171
|
+
_vuid[ky] = prom;
|
|
2172
|
+
let _csses = await prom;
|
|
2173
|
+
css.nd.textContent = _csses[0];
|
|
2174
|
+
|
|
2175
|
+
// _jsuid[_dd] = el;
|
|
2176
|
+
// prom
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
|
|
2181
|
+
|
|
2182
|
+
|
|
2183
|
+
// let prom = new defer();
|
|
2184
|
+
// var _dd = md5(new Date().getTime() / 1000 + "::" + Math.random());
|
|
2185
|
+
// _csswrk.trigger('css-ready', {
|
|
2186
|
+
// "csses": cssObjs,
|
|
2187
|
+
// "key": _dd,
|
|
2188
|
+
// "pre": _dd
|
|
2189
|
+
// });
|
|
2190
|
+
// _vuid[_dd] = prom;
|
|
2191
|
+
// let _csses = await prom;
|
|
2192
|
+
|
|
2193
|
+
// cl(_csses);
|
|
2194
|
+
|
|
2195
|
+
// for (const ky in _re._cc) {
|
|
2196
|
+
// if (Object.hasOwnProperty.call(_re._cc, ky)) {
|
|
2197
|
+
// const csses = _re._cc[ky];
|
|
2198
|
+
// for (let inde = 0; inde < csses.length; inde++) {
|
|
2199
|
+
// const css = csses[inde];
|
|
2200
|
+
// let myKey = ky + "_::" + inde;
|
|
2201
|
+
// css.nd.textContent = _csses[myKey];
|
|
2202
|
+
// }
|
|
2203
|
+
// }
|
|
2204
|
+
// }
|
|
2205
|
+
|
|
2206
|
+
}
|
|
2207
|
+
|
|
2208
|
+
if (Object.keys(_re._jj).length) {
|
|
2209
|
+
for (const ky in _re._jj) {
|
|
2210
|
+
if (Object.hasOwnProperty.call(_re._jj, ky)) {
|
|
2211
|
+
const jses = _re._jj[ky];
|
|
2212
|
+
for (let inde = 0; inde < jses.length; inde++) {
|
|
2213
|
+
const nd = jses[inde].nd;
|
|
2214
|
+
let code = getWatcher(nd._jst, _re.view, reactiveVariables, scopePath).code;
|
|
2215
|
+
code = `try { ` + code + ` } catch (e) { reportLumenError({ stage: 'script', error: e }); }`;
|
|
2216
|
+
// sourceURL: DevTools shows this <script>'s errors against
|
|
2217
|
+
// "<viewname>.view" instead of an anonymous VM context.
|
|
2218
|
+
// 2026-09-14, real bug found testing a real project
|
|
2219
|
+
// end-to-end: this used to be appended BEFORE the
|
|
2220
|
+
// try/catch wrap above, landing the comment — and
|
|
2221
|
+
// everything after it on the same line, including the
|
|
2222
|
+
// wrap's own closing brace and catch clause — inside
|
|
2223
|
+
// the try block. A `//` comment eats the rest of its
|
|
2224
|
+
// line, so that silently commented out the catch
|
|
2225
|
+
// entirely and left an unterminated `try`, throwing
|
|
2226
|
+
// "Unexpected end of input" the moment any real
|
|
2227
|
+
// <script> block actually ran (about.view's real
|
|
2228
|
+
// script on roxyon.com, in this case). Must come after
|
|
2229
|
+
// the wrap, on its own trailing line.
|
|
2230
|
+
code = code + `\n//# sourceURL=` + (_re.view?.name || 'view') + `.view.generated.js`;
|
|
2231
|
+
nd.textContent = code;
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2236
|
+
|
|
2237
|
+
return _re;
|
|
2238
|
+
}
|
|
2239
|
+
|
|
2240
|
+
// @before-render / @after-render (2026-09-14) — a developer-facing
|
|
2241
|
+
// lifecycle hook fired around a :for loop's diff-and-patch pass
|
|
2242
|
+
// (updateLXRs) or a :if/:else-if/:else section's show/hide transition
|
|
2243
|
+
// (updateCXRs' showSectionCX/hideSectionCX), letting a DOM-owning plugin
|
|
2244
|
+
// (a slider, for instance) safely tear down before its content is
|
|
2245
|
+
// replaced and re-initialize once it's settled — instead of silently
|
|
2246
|
+
// breaking the next time the underlying data or condition changes.
|
|
2247
|
+
// Reuses lstnrs.js's own evalEvAttr() — the same handler-resolution logic
|
|
2248
|
+
// every other @-attribute already goes through (@click, @sl-select, ...:
|
|
2249
|
+
// a plain function name calls window[name](ev, el, ...), anything else is
|
|
2250
|
+
// eval'd as an inline expression) — rather than inventing a second one;
|
|
2251
|
+
// the only new part is WHEN this gets called. Errors inside the handler
|
|
2252
|
+
// itself are already swallowed by evalEvAttr — matches the established,
|
|
2253
|
+
// silent-on-error precedent every other @-attribute handler already has;
|
|
2254
|
+
// not changed here to keep this hook's error behavior consistent with them.
|
|
2255
|
+
async function fireRenderHook(cx, n, containerEl, extra) {
|
|
2256
|
+
if (!containerEl) return;
|
|
2257
|
+
let attrKey = '@' + n;
|
|
2258
|
+
// The parser lifts @-prefixed attributes into `doc.evs` (its events
|
|
2259
|
+
// map) at compile time, same as @click/@sl-select/etc — NOT into
|
|
2260
|
+
// `doc.attrs` alongside plain attributes like :for/:if. Verified
|
|
2261
|
+
// against real parser output (fcs.js's getAllFiles), not assumed.
|
|
2262
|
+
if (!cx.doc || !cx.doc.evs || !cx.doc.evs.hasOwnProperty(attrKey)) return;
|
|
2263
|
+
let attrVal = cx.doc.evs[attrKey];
|
|
2264
|
+
if (!attrVal) return;
|
|
2265
|
+
let ev = Object.assign({ cType: n }, extra || {});
|
|
2266
|
+
let result = evalEvAttr(attrVal, ev, $(containerEl), n);
|
|
2267
|
+
if (result && typeof result.then === 'function') {
|
|
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; }
|
|
2276
|
+
}
|
|
2277
|
+
return result;
|
|
2278
|
+
}
|
|
2279
|
+
|
|
2280
|
+
// 2026-09-16: V1's real automatic plugin-init pass — [sl] (Select2),
|
|
2281
|
+
// [color] (bootstrap-colorpicker), [time]/[date]/[datetime]
|
|
2282
|
+
// (bootstrap-datetimepicker via dtp()) — ported faithfully from bea.js's
|
|
2283
|
+
// own real, un-minified renderPlugins()/dtp() source
|
|
2284
|
+
// (SBEACDN/s.beacdn.com/beajs/core.js:2846/3834, confirmed against the
|
|
2285
|
+
// real V1 build root this session already located, not reverse-engineered
|
|
2286
|
+
// from minified code). Called from createEl() on every element's mount.
|
|
2287
|
+
// Deliberately built into packages/core's own engine rather than kept
|
|
2288
|
+
// inside the --with-plugins bundle only — every call below is guarded on
|
|
2289
|
+
// the matching $.fn.* method actually existing, so a plain project (no
|
|
2290
|
+
// --with-plugins, dom-shim-based, none of these libraries loaded) safely
|
|
2291
|
+
// no-ops instead of throwing when a view happens to use one of these
|
|
2292
|
+
// attributes.
|
|
2293
|
+
function autoInitPlugins(el, attrs) {
|
|
2294
|
+
if (!attrs) return;
|
|
2295
|
+
try {
|
|
2296
|
+
if (attrs.hasOwnProperty('sl') && typeof $.fn.select2 === 'function') {
|
|
2297
|
+
initSl($(el));
|
|
2298
|
+
}
|
|
2299
|
+
if (attrs.hasOwnProperty('color') && typeof $.fn.colorpicker === 'function') {
|
|
2300
|
+
$(el).removeAttr('color').colorpicker({ format: 'rgba' });
|
|
2301
|
+
}
|
|
2302
|
+
if (typeof $.fn.datetimepicker === 'function') {
|
|
2303
|
+
if (attrs.hasOwnProperty('time')) dtp($(el), 'time');
|
|
2304
|
+
if (attrs.hasOwnProperty('date')) dtp($(el), 'date');
|
|
2305
|
+
if (attrs.hasOwnProperty('datetime')) dtp($(el), 'datetime');
|
|
2306
|
+
}
|
|
2307
|
+
} catch (e) { cl(e); }
|
|
2308
|
+
}
|
|
2309
|
+
|
|
2310
|
+
// Real V1 source, ported as-is (SBEACDN core.js:3404-3518) — the `sl-*`
|
|
2311
|
+
// attribute family is documented in lumenjs-spec.md §6.7. `:not(.select2-
|
|
2312
|
+
// hidden-accessible)`-equivalent guard kept even though this only ever
|
|
2313
|
+
// runs once per element (on mount, never re-checked) — matches V1's own
|
|
2314
|
+
// idempotency guard exactly, cheap insurance if this is ever called from
|
|
2315
|
+
// anywhere else in the future.
|
|
2316
|
+
function initSl(t) {
|
|
2317
|
+
if (t.hasClass('select2-hidden-accessible')) return;
|
|
2318
|
+
try {
|
|
2319
|
+
var plchldr = t.attr("placeholder") ? t.attr("placeholder") : "";
|
|
2320
|
+
var dir = $("body").hasClass("rtl") ? "rtl" : "ltr";
|
|
2321
|
+
var nr = t.attr("sl-nrmsg") ? t.attr("sl-nrmsg") : "No results found";
|
|
2322
|
+
var minResultsForSearch = t.attr("sl-mins") ? t.attr("sl-mins") : 10;
|
|
2323
|
+
var allowNewTags = t.attr("sl-ntgs") ? true : false;
|
|
2324
|
+
var dropdownParent = t.attr("sl-prt") ? t.attr("sl-prt") : "body";
|
|
2325
|
+
if (dropdownParent == "self") dropdownParent = t.parent();
|
|
2326
|
+
else dropdownParent = $(dropdownParent);
|
|
2327
|
+
|
|
2328
|
+
var query = t.attr("sl-query") ? t.attr("sl-query") : null;
|
|
2329
|
+
var uniquer = Date.now();
|
|
2330
|
+
|
|
2331
|
+
if (typeof window[query] === "function") {
|
|
2332
|
+
t.select2.amd.define(
|
|
2333
|
+
"adapt_" + uniquer,
|
|
2334
|
+
["select2/data/array", "select2/utils"],
|
|
2335
|
+
function (ArrayAdapter, Utils) {
|
|
2336
|
+
function CustomDataAdapter($element, options) {
|
|
2337
|
+
CustomDataAdapter.__super__.constructor.call(this, $element, options);
|
|
2338
|
+
}
|
|
2339
|
+
Utils.Extend(CustomDataAdapter, ArrayAdapter);
|
|
2340
|
+
CustomDataAdapter.prototype.query = function (params, callback) {
|
|
2341
|
+
clearTimeout(_dbcrs[uniquer]);
|
|
2342
|
+
let _t = t;
|
|
2343
|
+
_dbcrs[uniquer] = setTimeout(function () {
|
|
2344
|
+
window[query](params, callback, _t);
|
|
2345
|
+
}, !_dbcrs.hasOwnProperty(uniquer) ? 0 : _dbcrsTime);
|
|
2346
|
+
};
|
|
2347
|
+
return CustomDataAdapter;
|
|
2348
|
+
}
|
|
2349
|
+
);
|
|
2350
|
+
|
|
2351
|
+
t.select2({
|
|
2352
|
+
dropdownParent: dropdownParent,
|
|
2353
|
+
minimumResultsForSearch: minResultsForSearch,
|
|
2354
|
+
placeholder: plchldr,
|
|
2355
|
+
dir: dir,
|
|
2356
|
+
tags: allowNewTags,
|
|
2357
|
+
allowClear: true,
|
|
2358
|
+
language: { noResults: function () { return nr; } },
|
|
2359
|
+
...(t.select2.amd.require("adapt_" + uniquer)
|
|
2360
|
+
? { ajax: {}, dataAdapter: t.select2.amd.require("adapt_" + uniquer) }
|
|
2361
|
+
: {}),
|
|
2362
|
+
});
|
|
2363
|
+
} else {
|
|
2364
|
+
t.select2({
|
|
2365
|
+
dropdownParent: dropdownParent,
|
|
2366
|
+
minimumResultsForSearch: minResultsForSearch,
|
|
2367
|
+
placeholder: plchldr,
|
|
2368
|
+
dir: dir,
|
|
2369
|
+
tags: allowNewTags,
|
|
2370
|
+
allowClear: true,
|
|
2371
|
+
language: { noResults: function () { return nr; } },
|
|
2372
|
+
});
|
|
2373
|
+
}
|
|
2374
|
+
|
|
2375
|
+
if (t.attr("sl-nosrch"))
|
|
2376
|
+
t.on("select2:opening select2:closing", function (event) {
|
|
2377
|
+
$(this).parent().find(".select2-search__field").prop("disabled", true);
|
|
2378
|
+
});
|
|
2379
|
+
if (t.attr("sl-class")) {
|
|
2380
|
+
t.on("select2:opening", function (event) { dropdownParent.addClass(t.attr("sl-class")); });
|
|
2381
|
+
t.on("select2:closing", function (event) { dropdownParent.removeClass(t.attr("sl-class")); });
|
|
2382
|
+
}
|
|
2383
|
+
|
|
2384
|
+
if (t.attr("sl-id") || t.attr("sl-text")) {
|
|
2385
|
+
let text = t.attr("sl-text");
|
|
2386
|
+
let id = t.attr("sl-id");
|
|
2387
|
+
if (!text) text = id;
|
|
2388
|
+
if (!id) id = text;
|
|
2389
|
+
let newOption = new Option(text, id, true, true);
|
|
2390
|
+
t.append(newOption).trigger('select');
|
|
2391
|
+
} else {
|
|
2392
|
+
t.select2("val", "");
|
|
2393
|
+
}
|
|
2394
|
+
|
|
2395
|
+
if (t.attr("sl-value")) t.val(t.attr("sl-value")).trigger('change');
|
|
2396
|
+
} catch (e) { cl(e); }
|
|
2397
|
+
}
|
|
2398
|
+
var _dbcrs = {};
|
|
2399
|
+
var _dbcrsTime = 250;
|
|
2400
|
+
|
|
2401
|
+
// Real V1 source, ported as-is (SBEACDN core.js:3834-3891) — bootstrap-
|
|
2402
|
+
// datetimepicker option builder + optional start/end date-linking. Not
|
|
2403
|
+
// documented in lumenjs-spec.md (see PROVENANCE.md's --with-plugins
|
|
2404
|
+
// section); this exact option shape was confirmed directly against V1's
|
|
2405
|
+
// real source once it was located this session, not reverse-engineered.
|
|
2406
|
+
function dtp(el, t) {
|
|
2407
|
+
el.removeAttr(t);
|
|
2408
|
+
let opts = {
|
|
2409
|
+
format: t == "date" ? "yyyy-mm-dd" : t == "time" ? "hh:ii" : "yyyy-mm-dd hh:ii",
|
|
2410
|
+
weekStart: el.attr("date-week-start") ?? 1,
|
|
2411
|
+
startView: t == "time" ? 1 : (el.attr("startview") ? el.attr("startview") : 2),
|
|
2412
|
+
minView: el.attr("minview") ? el.attr("minview") : t == "time" ? 0 : t == "datetime" ? 0 : 2,
|
|
2413
|
+
maxView: el.attr("maxview") ? el.attr("maxview") : t == "time" ? 1 : 4,
|
|
2414
|
+
todayBtn: t == "time" ? 0 : el.attr("date-today") == "false" ? 0 : 1,
|
|
2415
|
+
todayHighlight: t == "time" ? 0 : el.attr("date-today") == "false" ? 0 : 1,
|
|
2416
|
+
language: el.attr("date-lang") ?? "en",
|
|
2417
|
+
minuteStep: el.attr("date-minute-step") ?? 5,
|
|
2418
|
+
pickerPosition: el.attr("date-position") ?? "top-right",
|
|
2419
|
+
autoclose: 1,
|
|
2420
|
+
showMeridian: false,
|
|
2421
|
+
};
|
|
2422
|
+
if (el.attr("date-start")) opts["startDate"] = el.attr("date-start");
|
|
2423
|
+
if (el.attr("date-end")) opts["endDate"] = el.attr("date-end");
|
|
2424
|
+
if (el.attr("date-value")) opts["date"] = el.attr("date-value");
|
|
2425
|
+
|
|
2426
|
+
el.datetimepicker(opts);
|
|
2427
|
+
|
|
2428
|
+
if (t == "time") {
|
|
2429
|
+
el.on("show", function (ev) {
|
|
2430
|
+
$(".table-condensed > thead, .table-condensed > thead > tr > th, .table-condensed > thead > tr > th > i")
|
|
2431
|
+
.attr("style", "visibility: hidden; font-size:0px !important; overflow: hidden; height: 0px;");
|
|
2432
|
+
}).on("hide", function (ev) {
|
|
2433
|
+
$(".table-condensed > thead, .table-condensed > thead > tr > th, .table-condensed > thead > tr > th > i")
|
|
2434
|
+
.attr("style", "visibility: visible;");
|
|
2435
|
+
});
|
|
2436
|
+
}
|
|
2437
|
+
if (el.attr("date-link-start")) {
|
|
2438
|
+
el.on("change", function (e) {
|
|
2439
|
+
let dp1 = el.data("datetimepicker");
|
|
2440
|
+
let dp2 = $(el.attr("date-link-start")).data("datetimepicker");
|
|
2441
|
+
dp2.setStartDate(dp1.getFormattedDate());
|
|
2442
|
+
if (dp2.getFormattedDate() < dp1.getFormattedDate() || dp2.getFormattedDate() == "")
|
|
2443
|
+
$(el.attr("date-link-start")).val(dp1.getFormattedDate());
|
|
2444
|
+
});
|
|
2445
|
+
} else if (el.attr("date-link-end")) {
|
|
2446
|
+
el.on("change", function (e) {
|
|
2447
|
+
let dp1 = $(el.attr("date-link-end")).data("datetimepicker");
|
|
2448
|
+
let dp2 = el.data("datetimepicker");
|
|
2449
|
+
dp1.setEndDate(dp2.getFormattedDate());
|
|
2450
|
+
});
|
|
2451
|
+
opts["useCurrent"] = false;
|
|
2452
|
+
}
|
|
2453
|
+
}
|
|
2454
|
+
|
|
2455
|
+
async function renderSection(tx, doc, par, k, sectionsData) {
|
|
2456
|
+
if (tx.node.isConnected) {
|
|
2457
|
+
if (k) {
|
|
2458
|
+
tx._re.update(k);
|
|
2459
|
+
}
|
|
2460
|
+
return tx._re;
|
|
2461
|
+
}
|
|
2462
|
+
|
|
2463
|
+
|
|
2464
|
+
var hst = doc.children;
|
|
2465
|
+
|
|
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);
|
|
2505
|
+
// if(par) {
|
|
2506
|
+
// _re.reactiveVariables = par.reactiveVariables;
|
|
2507
|
+
// }
|
|
2508
|
+
tx._re = _re;
|
|
2509
|
+
|
|
2510
|
+
// if (doc.type == "sections") {
|
|
2511
|
+
|
|
2512
|
+
// return _re;
|
|
2513
|
+
// }
|
|
2514
|
+
// cl(tx.key);
|
|
2515
|
+
// cl(par);
|
|
2516
|
+
// cl(hst);
|
|
2517
|
+
// cl(tx);
|
|
2518
|
+
// cl(doc);
|
|
2519
|
+
// cl(_re);
|
|
2520
|
+
|
|
2521
|
+
// cl("section", _re, par);
|
|
2522
|
+
|
|
2523
|
+
if (tx.node) {
|
|
2524
|
+
tx.replaceWith(tx.node);
|
|
2525
|
+
tx.node.innerHTML = "";
|
|
2526
|
+
tx.node.append(..._re._RealDOM);
|
|
2527
|
+
|
|
2528
|
+
_re.setEffects(doc, tx.node);
|
|
2529
|
+
|
|
2530
|
+
_re.renderAll('section');
|
|
2531
|
+
}
|
|
2532
|
+
return _re;
|
|
2533
|
+
}
|
|
2534
|
+
|
|
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"]) {
|
|
2544
|
+
let filePath = "src/views/" + n + ".view"
|
|
2545
|
+
if (type == 'layouts') filePath = "src/layouts/" + n + ".layout";
|
|
2546
|
+
|
|
2547
|
+
cl(arguments);
|
|
2548
|
+
let fileKey = btoa(filePath);
|
|
2549
|
+
if (!isSub) {
|
|
2550
|
+
View.props = d ?? {};
|
|
2551
|
+
var _queryParams = window.location.href.split("?");
|
|
2552
|
+
var nn = _queryParams.shift();
|
|
2553
|
+
View.params = paraToObj(_queryParams) ?? {};
|
|
2554
|
+
}
|
|
2555
|
+
n = prepareNode(n);
|
|
2556
|
+
var _queryParams = n.split("?");
|
|
2557
|
+
n = _queryParams.shift();
|
|
2558
|
+
|
|
2559
|
+
// 2026-09-15, real bug found implementing HST versioning: `_vcD` is
|
|
2560
|
+
// only ever assigned by ws.js's syncViewFiles() (dev-only, gated on
|
|
2561
|
+
// `!_beaTn`) — production never runs that, so `_vcD` itself stays
|
|
2562
|
+
// undefined forever in a real prod build, even though the SAME data
|
|
2563
|
+
// (views/layouts) is already sitting in the embedded `_vcData` global
|
|
2564
|
+
// (baked directly into the bundle at build time — see fcs.js's `vt`).
|
|
2565
|
+
// Resolved fresh on every call, not cached at module-load time: fcs.js
|
|
2566
|
+
// hands `vt` (which declares `_vcData`) to a remote endpoint that
|
|
2567
|
+
// weaves it into the final bundle at a position this code can't see or
|
|
2568
|
+
// control, so there's no reliable "by now it's definitely defined"
|
|
2569
|
+
// point earlier than an actual function call like this one — by the
|
|
2570
|
+
// time any event-driven code (this function) runs, all synchronous
|
|
2571
|
+
// top-level script content, wherever the remote endpoint placed it,
|
|
2572
|
+
// has already executed.
|
|
2573
|
+
let _payload = _vcD || (typeof _vcData !== 'undefined' ? _vcData : undefined);
|
|
2574
|
+
|
|
2575
|
+
// HST wire-format version check (2026-09-15) — see fcs.js's own
|
|
2576
|
+
// HST_FORMAT_VERSION comment for the full rationale. Checked once per
|
|
2577
|
+
// payload object (not once per call) via a property stamped directly
|
|
2578
|
+
// onto it, so a hot-reloaded payload during dev gets re-checked too.
|
|
2579
|
+
if (_payload && !_payload.__hstVersionChecked) {
|
|
2580
|
+
_payload.__hstVersionChecked = true;
|
|
2581
|
+
if (_payload.hstFormatVersion !== undefined && _payload.hstFormatVersion !== EXPECTED_HST_FORMAT_VERSION) {
|
|
2582
|
+
reportLumenError({
|
|
2583
|
+
stage: 'hst-version-mismatch',
|
|
2584
|
+
error: new Error('This project was compiled for HST format v' + _payload.hstFormatVersion + ', but this LumenJS runtime expects v' + EXPECTED_HST_FORMAT_VERSION + '. @lmjs/cli and @lmjs/core are out of sync — reinstall/upgrade both together.'),
|
|
2585
|
+
});
|
|
2586
|
+
return;
|
|
2587
|
+
}
|
|
2588
|
+
}
|
|
2589
|
+
|
|
2590
|
+
if ((_payload && _payload[type].hasOwnProperty(fileKey)) /*&& _jswacrk.isStarted()*/ && _csswrk.isStarted()) {
|
|
2591
|
+
if (isSub) cl("Rendering", n, fileKey);
|
|
2592
|
+
var hst = _payload[type][fileKey];
|
|
2593
|
+
// var mxes = hst.mxes;
|
|
2594
|
+
// hst = hst.hst;
|
|
2595
|
+
// cl("HHHSSSTTT", hst);
|
|
2596
|
+
// return;
|
|
2597
|
+
|
|
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;
|
|
2610
|
+
let els = [];
|
|
2611
|
+
for (let i = 0; i < searchArr.length; i++) {
|
|
2612
|
+
let _el = searchArr[i];
|
|
2613
|
+
if (_el.__isProxy) _el = _el.target;
|
|
2614
|
+
if (_el.subPath == n) els.push({ el: _el, viewsIndex: i });
|
|
2615
|
+
}
|
|
2616
|
+
if (els.length) {
|
|
2617
|
+
// cl(els);
|
|
2618
|
+
for (let i = 0; i < els.length; i++) {
|
|
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;
|
|
2629
|
+
el.innerHTML = "";
|
|
2630
|
+
// cl(el);
|
|
2631
|
+
el.append(..._re._RealDOM);
|
|
2632
|
+
_re.renderAll();
|
|
2633
|
+
}
|
|
2634
|
+
// _re._RealDOM.forEach(_el => el[0].appendChild(_el))
|
|
2635
|
+
// cl(_re);
|
|
2636
|
+
}
|
|
2637
|
+
} else {
|
|
2638
|
+
if (type == 'layouts') {
|
|
2639
|
+
let appSelector = appSettings.App ?? "[app]";
|
|
2640
|
+
var appContainer = $(appSelector);
|
|
2641
|
+
if (appContainer.length) {
|
|
2642
|
+
let _rel = await renderHST(hst, n, 'layout');
|
|
2643
|
+
appContainer.data('layout', n).html(_rel._RealDOM);
|
|
2644
|
+
_rel.renderAll();
|
|
2645
|
+
goToNode();
|
|
2646
|
+
}
|
|
2647
|
+
} else {
|
|
2648
|
+
let _re = await renderHST(hst, n, 'main');
|
|
2649
|
+
// cl("RRR", _re);
|
|
2650
|
+
let layout = _re.view.settings.layout;
|
|
2651
|
+
|
|
2652
|
+
let filePathL = "src/layouts/" + layout + ".layout";
|
|
2653
|
+
let fileKeyL = btoa(filePathL);
|
|
2654
|
+
let hstL = _payload['layouts'][fileKeyL];
|
|
2655
|
+
// cl(_rel);
|
|
2656
|
+
|
|
2657
|
+
|
|
2658
|
+
let appSelector = appSettings.App ?? "[app]";
|
|
2659
|
+
var appContainer = $(appSelector);
|
|
2660
|
+
if (appContainer.length) {
|
|
2661
|
+
let currentLayout = $(appSelector).data('layout');
|
|
2662
|
+
if (currentLayout != layout) {
|
|
2663
|
+
let _rel = await renderHST(hstL, layout, 'layout');
|
|
2664
|
+
appContainer.data('layout', layout).html(_rel._RealDOM);
|
|
2665
|
+
_rel.renderAll();
|
|
2666
|
+
} else {
|
|
2667
|
+
// cl("Same Layout");
|
|
2668
|
+
}
|
|
2669
|
+
} else {
|
|
2670
|
+
$("body").prepend("<div " + (appSelector) + "></div>");
|
|
2671
|
+
let _rel = await renderHST(hstL, layout, 'layout');
|
|
2672
|
+
appContainer = $(appSelector);
|
|
2673
|
+
appContainer.data('layout', layout).html(_rel._RealDOM);
|
|
2674
|
+
_rel.renderAll();
|
|
2675
|
+
}
|
|
2676
|
+
|
|
2677
|
+
// return;
|
|
2678
|
+
// setLayout();
|
|
2679
|
+
$('[body]').html(_re._RealDOM);
|
|
2680
|
+
|
|
2681
|
+
// Layout slots (2026-09-15) — see walk()'s "slot" branch
|
|
2682
|
+
// above for how _re.view._slots gets populated. Injected
|
|
2683
|
+
// the same way [body] is, right above — .html() moves the
|
|
2684
|
+
// already-walked real DOM nodes (doesn't clone them), so
|
|
2685
|
+
// this can run before or after renderAll() without
|
|
2686
|
+
// affecting which node each reactive effect targets;
|
|
2687
|
+
// matching [body]'s own ordering here just for consistency.
|
|
2688
|
+
// A view targeting a slot name no loaded layout declares
|
|
2689
|
+
// is a legitimate, silent no-op (the layout may simply not
|
|
2690
|
+
// have that optional region) — $('[slot="x"]') matching
|
|
2691
|
+
// nothing is real jQuery/dom-shim behavior already, not a
|
|
2692
|
+
// new case to handle.
|
|
2693
|
+
if (_re.view._slots) {
|
|
2694
|
+
for (const slotName in _re.view._slots) {
|
|
2695
|
+
if (Object.prototype.hasOwnProperty.call(_re.view._slots, slotName)) {
|
|
2696
|
+
$('[slot="' + slotName + '"]').html(_re.view._slots[slotName]);
|
|
2697
|
+
}
|
|
2698
|
+
}
|
|
2699
|
+
}
|
|
2700
|
+
|
|
2701
|
+
_re.renderAll();
|
|
2702
|
+
}
|
|
2703
|
+
}
|
|
2704
|
+
} else {
|
|
2705
|
+
setTimeout(() => {
|
|
2706
|
+
renderView(n, isSub, d);
|
|
2707
|
+
}, 10);
|
|
2708
|
+
}
|
|
2709
|
+
}
|