@lmjs/core 2.0.2 → 2.1.1
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 +18 -101
- package/build/plugins-registry.js +28 -26
- package/dist/lumenjs-core.js +2 -2
- package/package.json +1 -1
- package/src/_re.js +153 -26
- package/dist/lumenjs-core-with-plugins.js +0 -59258
- package/dist/lumenjs-plugins.css +0 -2822
package/src/_re.js
CHANGED
|
@@ -190,7 +190,17 @@ function _x(_x) {
|
|
|
190
190
|
// so a stale tail from a prior _vt.Global read (which never hit
|
|
191
191
|
// "View" to reset it) would otherwise get prepended to the next
|
|
192
192
|
// chain and corrupt which key set()'s update(key) fires for.
|
|
193
|
-
|
|
193
|
+
// 2026-09-18: generalized from a hardcoded `key == "View" ||
|
|
194
|
+
// key == "Global"` name list (which needed editing every time a
|
|
195
|
+
// new permanent root, e.g. "Widgets", got added) to the one
|
|
196
|
+
// thing that's actually invariant: `target === _x` is only ever
|
|
197
|
+
// true for THIS proxy's own outermost target (the raw root
|
|
198
|
+
// object this whole closure was built from) — any nested proxy
|
|
199
|
+
// in the tree wraps some OTHER object as its target, never `_x`
|
|
200
|
+
// itself. So this fires on any fresh top-level access
|
|
201
|
+
// (`_vt.View`, `_vt.Global`, `_vt.Widgets`, ...) regardless of
|
|
202
|
+
// the key name, with nothing new to maintain per root added.
|
|
203
|
+
if (target === _x) currPath = [];
|
|
194
204
|
currPath.push(key);
|
|
195
205
|
if (typeof target[key] === 'object' && target[key] !== null && key != "_re") {
|
|
196
206
|
// cl("salsaa", target, key)
|
|
@@ -287,11 +297,55 @@ function _x(_x) {
|
|
|
287
297
|
// an uncaught exception there aborts every remaining statement in the
|
|
288
298
|
// whole bundle file, breaking the entire app on every real production
|
|
289
299
|
// site with this pattern. Found deploying lumenjs.com's own real build.
|
|
300
|
+
// "Widgets" (2026-09-18): a third permanent root, sibling of Global/View,
|
|
301
|
+
// generalizing V1's hardcoded hasNav/hasHeader/hasFooter widget-loading past
|
|
302
|
+
// its fixed 3-name list — see renderView()'s layout branch below for the
|
|
303
|
+
// resolve/mount/diff logic, and fcs.js's annotateLayoutRegions() for how a
|
|
304
|
+
// layout's own bare attributes (e.g. `<nav nav>`) become the region names
|
|
305
|
+
// used to key this object. Deliberately NOT an ancestor of View in the
|
|
306
|
+
// scope tree (widget regions and [body] are DOM SIBLINGS under the layout
|
|
307
|
+
// root, never one containing the other — unlike a subview's real, singular
|
|
308
|
+
// parent, there's no single unambiguous "the" widget a view could shadow
|
|
309
|
+
// into) — each entry gets its own real `._re` instead (see
|
|
310
|
+
// _dispatchVarsUpdate's generic owner-walk below, which already handles
|
|
311
|
+
// this with zero special-casing once an entry has `._re` set, same as any
|
|
312
|
+
// subview). Starts empty; populated lazily, one entry per declared region
|
|
313
|
+
// name, the first time a layout using that region is mounted.
|
|
290
314
|
let _vt = _x({
|
|
291
315
|
"View": new _v({}),
|
|
292
|
-
"Global": { "vars": {}, "fns": {} }
|
|
316
|
+
"Global": { "vars": {}, "fns": {} },
|
|
317
|
+
"Widgets": {}
|
|
293
318
|
});
|
|
294
319
|
|
|
320
|
+
// 2026-09-18: shared by getVal()/evalExp()/concatVarsAtLevel() below, so the
|
|
321
|
+
// "first-declared widget wins on a name collision" rule is defined exactly
|
|
322
|
+
// once. Object key iteration order for string keys is real insertion order,
|
|
323
|
+
// so this naturally checks widgets in the order they were first mounted —
|
|
324
|
+
// which, in practice, is document order (renderView()'s layout branch walks
|
|
325
|
+
// `hstL.regions`, itself built in document order by fcs.js's
|
|
326
|
+
// annotateLayoutRegions()).
|
|
327
|
+
function _lookupInWidgets(name) {
|
|
328
|
+
for (const wname in _vt.Widgets) {
|
|
329
|
+
if (_vt.Widgets[wname].vars.hasOwnProperty(name)) return _vt.Widgets[wname].vars[name];
|
|
330
|
+
}
|
|
331
|
+
return undefined;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// concatVarsAtLevel() below merges via object spread, not the loop-with-
|
|
335
|
+
// break shape _lookupInWidgets() uses — spread's "later wins" needs the
|
|
336
|
+
// widgets merged in REVERSE declaration order so the first-declared widget
|
|
337
|
+
// still wins a name collision, matching _lookupInWidgets()'s own semantics
|
|
338
|
+
// exactly (both must agree, since getVal()/evalExp() use one and
|
|
339
|
+
// concatVarsAtLevel()/lookup()/scopedEval() use the other for the same read).
|
|
340
|
+
function _mergedWidgetsVars() {
|
|
341
|
+
let out = {};
|
|
342
|
+
let names = Object.keys(_vt.Widgets).reverse();
|
|
343
|
+
for (const wname of names) {
|
|
344
|
+
out = { ...out, ..._vt.Widgets[wname].vars };
|
|
345
|
+
}
|
|
346
|
+
return out;
|
|
347
|
+
}
|
|
348
|
+
|
|
295
349
|
class _lm {
|
|
296
350
|
_RealDOM = [];
|
|
297
351
|
_effects = {};
|
|
@@ -1073,10 +1127,17 @@ class _lm {
|
|
|
1073
1127
|
// lookup()/scopedEval(); getVal() (what mustache text nodes
|
|
1074
1128
|
// actually call) had its own separate, unmerged read here
|
|
1075
1129
|
// and was silently rendering these as empty.
|
|
1130
|
+
// 2026-09-18: inserted a middle tier — _lookupInWidgets()
|
|
1131
|
+
// (all currently-mounted widgets' vars, first-declared
|
|
1132
|
+
// wins) — between View and Global, per this session's
|
|
1133
|
+
// agreed read-fallback order.
|
|
1076
1134
|
let __name = this.reactiveVariables[i];
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1135
|
+
if (_vt.View.vars.hasOwnProperty(__name)) {
|
|
1136
|
+
vars[__name] = _vt.View.vars[__name];
|
|
1137
|
+
} else {
|
|
1138
|
+
let _wv = _lookupInWidgets(__name);
|
|
1139
|
+
vars[__name] = _wv !== undefined ? _wv : _vt.Global.vars[__name];
|
|
1140
|
+
}
|
|
1080
1141
|
}
|
|
1081
1142
|
for (const ky in this.vrs) {
|
|
1082
1143
|
if (Object.prototype.hasOwnProperty.call(this.vrs, ky)) {
|
|
@@ -1207,8 +1268,10 @@ class _lm {
|
|
|
1207
1268
|
// Global vars (index.js's top-level state, see _vt.Global
|
|
1208
1269
|
// above) sit at the lowest priority here — a view or a
|
|
1209
1270
|
// narrower scope declaring the same name shadows it, same
|
|
1210
|
-
// as real JS scoping would.
|
|
1211
|
-
|
|
1271
|
+
// as real JS scoping would. 2026-09-18: widgets' vars sit
|
|
1272
|
+
// one tier above Global, below the view's own — see
|
|
1273
|
+
// _mergedWidgetsVars()'s comment for the ordering rationale.
|
|
1274
|
+
concatenatedVars = { ..._vt.Global.vars, ..._mergedWidgetsVars(), ...parent.view.vars, ...concatenatedVars };
|
|
1212
1275
|
}
|
|
1213
1276
|
return concatenatedVars;
|
|
1214
1277
|
}
|
|
@@ -1462,12 +1525,11 @@ class _lm {
|
|
|
1462
1525
|
// minified code) — [sl]/[color]/[time]/[date]/[datetime] elements
|
|
1463
1526
|
// become their respective widgets on mount, no explicit script
|
|
1464
1527
|
// call needed, matching V1's documented behavior (lumenjs-
|
|
1465
|
-
// spec.md §5.6/§6.7). Built into core directly
|
|
1466
|
-
//
|
|
1467
|
-
//
|
|
1468
|
-
//
|
|
1469
|
-
//
|
|
1470
|
-
// throwing when it encounters one of these attributes.
|
|
1528
|
+
// spec.md §5.6/§6.7). Built into core directly, always available
|
|
1529
|
+
// (explicit choice, 2026-09-16) — autoInitPlugins() itself guards
|
|
1530
|
+
// every call on the matching $.fn.* method actually existing, so a
|
|
1531
|
+
// plain project with no plugins selected silently no-ops instead
|
|
1532
|
+
// of throwing when it encounters one of these attributes.
|
|
1471
1533
|
autoInitPlugins(_el, attrs);
|
|
1472
1534
|
|
|
1473
1535
|
return _el;
|
|
@@ -1520,10 +1582,15 @@ class _lm {
|
|
|
1520
1582
|
// value scopedEval() would otherwise have found in
|
|
1521
1583
|
// _vt.Global.vars — breaking :if/:for conditions on any
|
|
1522
1584
|
// index.js-declared var, not just mustache text (getVal()).
|
|
1585
|
+
// 2026-09-18: same middle tier as getVal() — see its
|
|
1586
|
+
// matching comment.
|
|
1523
1587
|
let __name = vars[i];
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1588
|
+
if (_vt.View.vars.hasOwnProperty(__name)) {
|
|
1589
|
+
rvars[__name] = _vt.View.vars[__name];
|
|
1590
|
+
} else {
|
|
1591
|
+
let _wv = _lookupInWidgets(__name);
|
|
1592
|
+
rvars[__name] = _wv !== undefined ? _wv : _vt.Global.vars[__name];
|
|
1593
|
+
}
|
|
1527
1594
|
}
|
|
1528
1595
|
// 2026-09-17, real bug found and fixed: getVal() (mustache
|
|
1529
1596
|
// text, e.g. {{f.name}}) has always merged this.vrs in after
|
|
@@ -2284,12 +2351,11 @@ async function fireRenderHook(cx, n, containerEl, extra) {
|
|
|
2284
2351
|
// (SBEACDN/s.beacdn.com/beajs/core.js:2846/3834, confirmed against the
|
|
2285
2352
|
// real V1 build root this session already located, not reverse-engineered
|
|
2286
2353
|
// from minified code). Called from createEl() on every element's mount.
|
|
2287
|
-
// Deliberately built into packages/core's own engine
|
|
2288
|
-
//
|
|
2289
|
-
//
|
|
2290
|
-
//
|
|
2291
|
-
//
|
|
2292
|
-
// attributes.
|
|
2354
|
+
// Deliberately built into packages/core's own engine, always available —
|
|
2355
|
+
// every call below is guarded on the matching $.fn.* method actually
|
|
2356
|
+
// existing, so a plain project (dom-shim-based, none of these libraries
|
|
2357
|
+
// loaded) safely no-ops instead of throwing when a view happens to use one
|
|
2358
|
+
// of these attributes.
|
|
2293
2359
|
function autoInitPlugins(el, attrs) {
|
|
2294
2360
|
if (!attrs) return;
|
|
2295
2361
|
try {
|
|
@@ -2400,9 +2466,9 @@ var _dbcrsTime = 250;
|
|
|
2400
2466
|
|
|
2401
2467
|
// Real V1 source, ported as-is (SBEACDN core.js:3834-3891) — bootstrap-
|
|
2402
2468
|
// datetimepicker option builder + optional start/end date-linking. Not
|
|
2403
|
-
// documented in lumenjs-spec.md (see PROVENANCE.md's
|
|
2404
|
-
//
|
|
2405
|
-
//
|
|
2469
|
+
// documented in lumenjs-spec.md (see PROVENANCE.md's plugins section);
|
|
2470
|
+
// this exact option shape was confirmed directly against V1's real source
|
|
2471
|
+
// once it was located this session, not reverse-engineered.
|
|
2406
2472
|
function dtp(el, t) {
|
|
2407
2473
|
el.removeAttr(t);
|
|
2408
2474
|
let opts = {
|
|
@@ -2544,7 +2610,6 @@ async function renderView(n, isSub, d, type = 'views', viewsArr, scopeBase = ["V
|
|
|
2544
2610
|
let filePath = "src/views/" + n + ".view"
|
|
2545
2611
|
if (type == 'layouts') filePath = "src/layouts/" + n + ".layout";
|
|
2546
2612
|
|
|
2547
|
-
cl(arguments);
|
|
2548
2613
|
let fileKey = btoa(filePath);
|
|
2549
2614
|
if (!isSub) {
|
|
2550
2615
|
View.props = d ?? {};
|
|
@@ -2657,12 +2722,21 @@ async function renderView(n, isSub, d, type = 'views', viewsArr, scopeBase = ["V
|
|
|
2657
2722
|
|
|
2658
2723
|
let appSelector = appSettings.App ?? "[app]";
|
|
2659
2724
|
var appContainer = $(appSelector);
|
|
2725
|
+
// 2026-09-18: whether THIS call just replaced the layout's
|
|
2726
|
+
// own DOM wholesale — needed below by the widget
|
|
2727
|
+
// resolve/mount/diff pass, since a widget whose resolution
|
|
2728
|
+
// hasn't changed still needs its already-rendered nodes
|
|
2729
|
+
// reattached to the fresh layout element (the old one they
|
|
2730
|
+
// were living in is gone), even though nothing about the
|
|
2731
|
+
// widget itself needs re-rendering.
|
|
2732
|
+
let layoutJustSwapped = false;
|
|
2660
2733
|
if (appContainer.length) {
|
|
2661
2734
|
let currentLayout = $(appSelector).data('layout');
|
|
2662
2735
|
if (currentLayout != layout) {
|
|
2663
2736
|
let _rel = await renderHST(hstL, layout, 'layout');
|
|
2664
2737
|
appContainer.data('layout', layout).html(_rel._RealDOM);
|
|
2665
2738
|
_rel.renderAll();
|
|
2739
|
+
layoutJustSwapped = true;
|
|
2666
2740
|
} else {
|
|
2667
2741
|
// cl("Same Layout");
|
|
2668
2742
|
}
|
|
@@ -2672,6 +2746,59 @@ async function renderView(n, isSub, d, type = 'views', viewsArr, scopeBase = ["V
|
|
|
2672
2746
|
appContainer = $(appSelector);
|
|
2673
2747
|
appContainer.data('layout', layout).html(_rel._RealDOM);
|
|
2674
2748
|
_rel.renderAll();
|
|
2749
|
+
layoutJustSwapped = true;
|
|
2750
|
+
}
|
|
2751
|
+
|
|
2752
|
+
// Persistent layout widgets (_vt.Widgets, 2026-09-18) — see
|
|
2753
|
+
// that object's own declaration comment for the design.
|
|
2754
|
+
// Runs on EVERY navigation, not gated on layoutJustSwapped:
|
|
2755
|
+
// a view's own settings (hasNav: false, hasNav: "nav2", ...)
|
|
2756
|
+
// can change what belongs in a region even when the layout
|
|
2757
|
+
// itself didn't change at all. hstL.regions is precomputed
|
|
2758
|
+
// build-time by fcs.js's annotateLayoutRegions() — the bare
|
|
2759
|
+
// (valueless) attribute names the CURRENTLY MOUNTED layout
|
|
2760
|
+
// itself declares (e.g. ["header","nav","footer"] for
|
|
2761
|
+
// `<header header>`/`<nav nav>`/`<footer footer>`).
|
|
2762
|
+
let declaredRegions = (hstL && hstL.regions) || [];
|
|
2763
|
+
for (const regionName of declaredRegions) {
|
|
2764
|
+
let settingsKey = "has" + regionName[0].toUpperCase() + regionName.slice(1);
|
|
2765
|
+
let want = _re.view.settings.hasOwnProperty(settingsKey) ? _re.view.settings[settingsKey] : true;
|
|
2766
|
+
let resolvedFile = want === false ? null : (want === true ? regionName : want);
|
|
2767
|
+
|
|
2768
|
+
let w = _vt.Widgets[regionName] || (_vt.Widgets[regionName] = { vars: {}, fns: {}, views: [], _re: null, _resolvedFile: undefined });
|
|
2769
|
+
|
|
2770
|
+
if (resolvedFile !== w._resolvedFile) {
|
|
2771
|
+
// Resolution genuinely changed since the last time
|
|
2772
|
+
// this region was resolved — mount fresh, or clear.
|
|
2773
|
+
if (resolvedFile === null) {
|
|
2774
|
+
$('[' + regionName + ']').html('');
|
|
2775
|
+
w._re = null;
|
|
2776
|
+
} else {
|
|
2777
|
+
let wFilePath = "src/views/widgets/" + resolvedFile + ".view";
|
|
2778
|
+
let wHst = _payload['views'][btoa(wFilePath)];
|
|
2779
|
+
if (wHst) {
|
|
2780
|
+
let _rew = await renderHST(wHst, resolvedFile, 'widget', undefined, null, ["Widgets", regionName], w.vars, w.fns, w.views);
|
|
2781
|
+
w._re = _rew;
|
|
2782
|
+
$('[' + regionName + ']').html(_rew._RealDOM);
|
|
2783
|
+
_rew.renderAll();
|
|
2784
|
+
}
|
|
2785
|
+
}
|
|
2786
|
+
w._resolvedFile = resolvedFile;
|
|
2787
|
+
} else if (layoutJustSwapped && w._re) {
|
|
2788
|
+
// Same widget as before, but the layout element it
|
|
2789
|
+
// lives in was just replaced wholesale — reattach
|
|
2790
|
+
// the EXISTING rendered nodes (.html() moves real
|
|
2791
|
+
// node references, same as [body]/[slot] below,
|
|
2792
|
+
// it doesn't clone) rather than re-rendering. The
|
|
2793
|
+
// widget's own state (w.vars/.fns/.views) never
|
|
2794
|
+
// lived on the old DOM to begin with, so nothing
|
|
2795
|
+
// was lost — only the attachment point needed
|
|
2796
|
+
// fixing up.
|
|
2797
|
+
$('[' + regionName + ']').html(w._re._RealDOM);
|
|
2798
|
+
}
|
|
2799
|
+
// else: nothing changed and the layout didn't just
|
|
2800
|
+
// swap — this region's DOM/state is left completely
|
|
2801
|
+
// untouched, on purpose (the whole point of this).
|
|
2675
2802
|
}
|
|
2676
2803
|
|
|
2677
2804
|
// return;
|