@jqhtml/core 2.3.50 → 2.3.54
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cache-key-serializer.d.ts +47 -0
- package/dist/cache-key-serializer.d.ts.map +1 -0
- package/dist/component-cache.d.ts +1 -0
- package/dist/component-cache.d.ts.map +1 -1
- package/dist/component-events.d.ts.map +1 -1
- package/dist/component.d.ts +18 -0
- package/dist/component.d.ts.map +1 -1
- package/dist/config.d.ts +57 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/index.cjs +486 -40
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +485 -41
- package/dist/index.js.map +1 -1
- package/dist/instruction-processor.d.ts.map +1 -1
- package/dist/jqhtml-core.esm.js +486 -42
- package/dist/jqhtml-core.esm.js.map +1 -1
- package/dist/jquery-plugin.d.ts.map +1 -1
- package/dist/load-coordinator.d.ts +18 -1
- package/dist/load-coordinator.d.ts.map +1 -1
- package/dist/local-storage.d.ts +2 -0
- package/dist/local-storage.d.ts.map +1 -1
- package/dist/viewport.d.ts +36 -0
- package/dist/viewport.d.ts.map +1 -0
- package/package.json +7 -2
package/dist/index.js
CHANGED
|
@@ -496,6 +496,70 @@ function register(source) {
|
|
|
496
496
|
'class name mangling, define static component_name or use register_component().');
|
|
497
497
|
}
|
|
498
498
|
|
|
499
|
+
/**
|
|
500
|
+
* JQHTML Runtime Configuration
|
|
501
|
+
*
|
|
502
|
+
* Integration-level settings supplied by the host application (RSpade, Laravel,
|
|
503
|
+
* a bundler entry point) when jqhtml is loaded. Distinct from `jqhtml.debug`,
|
|
504
|
+
* which is a developer's interactive tracing switch: config here describes the
|
|
505
|
+
* ENVIRONMENT the app is running in, and jqhtml derives behaviour from it.
|
|
506
|
+
*
|
|
507
|
+
* jqhtml.init($, { mode: 'production' });
|
|
508
|
+
* jqhtml.configure({ mode: 'development' });
|
|
509
|
+
*
|
|
510
|
+
* Default mode is 'development', so an integration that passes nothing keeps
|
|
511
|
+
* every diagnostic. Production is always opt-in.
|
|
512
|
+
*/
|
|
513
|
+
/** Flags implied by each mode. Add future conventions here, not at call sites. */
|
|
514
|
+
const MODE_DEFAULTS = {
|
|
515
|
+
development: {
|
|
516
|
+
warn_uncacheable_args: true,
|
|
517
|
+
debug_attributes: true,
|
|
518
|
+
},
|
|
519
|
+
production: {
|
|
520
|
+
warn_uncacheable_args: false,
|
|
521
|
+
debug_attributes: false,
|
|
522
|
+
},
|
|
523
|
+
};
|
|
524
|
+
let _mode = 'development';
|
|
525
|
+
let _flags = { ...MODE_DEFAULTS.development };
|
|
526
|
+
/**
|
|
527
|
+
* Apply integration configuration.
|
|
528
|
+
*
|
|
529
|
+
* `mode` resets every flag to that mode's defaults; any explicit flag in the
|
|
530
|
+
* same call then overrides it. Calling with only flags leaves the mode alone.
|
|
531
|
+
* Repeated calls merge.
|
|
532
|
+
*/
|
|
533
|
+
function configure(config) {
|
|
534
|
+
if (!config)
|
|
535
|
+
return;
|
|
536
|
+
if (config.mode) {
|
|
537
|
+
if (!MODE_DEFAULTS[config.mode]) {
|
|
538
|
+
throw new Error(`[JQHTML] Unknown mode '${config.mode}'. Expected 'development' or 'production'.`);
|
|
539
|
+
}
|
|
540
|
+
_mode = config.mode;
|
|
541
|
+
_flags = { ...MODE_DEFAULTS[config.mode] };
|
|
542
|
+
}
|
|
543
|
+
if (config.warn_uncacheable_args !== undefined) {
|
|
544
|
+
_flags.warn_uncacheable_args = !!config.warn_uncacheable_args;
|
|
545
|
+
}
|
|
546
|
+
if (config.debug_attributes !== undefined) {
|
|
547
|
+
_flags.debug_attributes = !!config.debug_attributes;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
/** Current mode plus every resolved flag. */
|
|
551
|
+
function get_config() {
|
|
552
|
+
return { mode: _mode, ..._flags };
|
|
553
|
+
}
|
|
554
|
+
/** True when debug-only DOM attributes (data-sid) should be emitted. */
|
|
555
|
+
function debug_attributes_enabled() {
|
|
556
|
+
return _flags.debug_attributes;
|
|
557
|
+
}
|
|
558
|
+
/** True when uncacheable-args warnings should be printed. */
|
|
559
|
+
function warn_uncacheable_args_enabled() {
|
|
560
|
+
return _flags.warn_uncacheable_args;
|
|
561
|
+
}
|
|
562
|
+
|
|
499
563
|
/**
|
|
500
564
|
* JQHTML v2 Instruction Processor
|
|
501
565
|
*
|
|
@@ -633,6 +697,9 @@ function process_tag_to_html(instruction, html, tagElements, components, context
|
|
|
633
697
|
}
|
|
634
698
|
// Add simple string/numeric attributes directly
|
|
635
699
|
for (const [key, value] of Object.entries(attrs)) {
|
|
700
|
+
if (key === 'data-sid' && !debug_attributes_enabled()) {
|
|
701
|
+
continue; // debug-only attribute; the scoped id is what $sid() resolves
|
|
702
|
+
}
|
|
636
703
|
if (!key.startsWith('$') && !key.startsWith('on') && !key.startsWith('@') &&
|
|
637
704
|
!key.startsWith('data-bind-') && !key.startsWith('data-__-on-') &&
|
|
638
705
|
(typeof value === 'string' || typeof value === 'number')) {
|
|
@@ -722,7 +789,10 @@ function process_component_to_html(instruction, html, components, context) {
|
|
|
722
789
|
const baseId = props['data-sid'];
|
|
723
790
|
// The compiled code ALWAYS sets props['id'] with the correct scoped value
|
|
724
791
|
// Just use it directly - it already has the correct parent _cid baked in
|
|
725
|
-
|
|
792
|
+
// data-sid is debug-only: $sid() resolves via the scoped id, never this.
|
|
793
|
+
html.push(debug_attributes_enabled()
|
|
794
|
+
? ` id="${props['id']}" data-sid="${baseId}"`
|
|
795
|
+
: ` id="${props['id']}"`);
|
|
726
796
|
}
|
|
727
797
|
// Regular id passes through unchanged
|
|
728
798
|
else if (props['id']) {
|
|
@@ -864,6 +934,10 @@ function apply_attributes(element, attrs, context) {
|
|
|
864
934
|
}
|
|
865
935
|
}
|
|
866
936
|
else if (key.startsWith('data-')) {
|
|
937
|
+
// data-sid is debug-only - suppressed entirely in production
|
|
938
|
+
if (key === 'data-sid' && !debug_attributes_enabled()) {
|
|
939
|
+
continue;
|
|
940
|
+
}
|
|
867
941
|
// Data attributes - go into DOM
|
|
868
942
|
const attrValue = typeof value === "string" ? value.trim() : value;
|
|
869
943
|
element.attr(key, attrValue);
|
|
@@ -1226,6 +1300,146 @@ function handleComponentError(component, phase, error) {
|
|
|
1226
1300
|
// 9. Performance Budgets - Warn when components exceed size/time limits
|
|
1227
1301
|
// 10. Accessibility Auditing - Check for missing ARIA attributes
|
|
1228
1302
|
|
|
1303
|
+
/**
|
|
1304
|
+
* JQHTML Cache Key Serializer
|
|
1305
|
+
*
|
|
1306
|
+
* Produces a deterministic string for a plain-data argument value, so that two
|
|
1307
|
+
* structurally equal objects built on separate renders yield the SAME cache key.
|
|
1308
|
+
* Templates rebuild `{contact_id: 12}` on every render, so identity-based keying
|
|
1309
|
+
* can never match and permanently opts the receiving component out of caching.
|
|
1310
|
+
*
|
|
1311
|
+
* ---------------------------------------------------------------------------
|
|
1312
|
+
* THIS IS NOT `process_for_serialization()` IN local-storage.ts. DO NOT MERGE THEM.
|
|
1313
|
+
*
|
|
1314
|
+
* That serializer stores cache VALUES and deliberately degrades: a function, a
|
|
1315
|
+
* symbol or a circular reference is dropped and serialization continues, because
|
|
1316
|
+
* losing a method while keeping the data is the right trade for a payload.
|
|
1317
|
+
*
|
|
1318
|
+
* For a cache KEY that policy manufactures false hits. Two argument objects that
|
|
1319
|
+
* differ ONLY by a callback would serialize identically, and a component would
|
|
1320
|
+
* render with another component's cached content - strictly worse than not
|
|
1321
|
+
* caching at all. So this serializer DECLINES on the first value it cannot
|
|
1322
|
+
* express exactly. It never drops anything.
|
|
1323
|
+
* ---------------------------------------------------------------------------
|
|
1324
|
+
*
|
|
1325
|
+
* Allow-list, and nothing else: null, undefined, boolean, number, string, Date,
|
|
1326
|
+
* arrays, and plain objects (prototype Object.prototype or null), in any nesting.
|
|
1327
|
+
* Everything else declines with a reason that reaches `data-nocache`.
|
|
1328
|
+
*/
|
|
1329
|
+
/**
|
|
1330
|
+
* Values larger than this are declined rather than keyed. The supported case is a
|
|
1331
|
+
* handful of key/value pairs or a list of ids - not a payload. Declining loudly
|
|
1332
|
+
* beats spending the budget on a key nobody benefits from.
|
|
1333
|
+
*/
|
|
1334
|
+
const MAX_KEY_BYTES = 500;
|
|
1335
|
+
class Decline extends Error {
|
|
1336
|
+
constructor(reason) {
|
|
1337
|
+
super(reason);
|
|
1338
|
+
this.reason = reason;
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
function push(state, text) {
|
|
1342
|
+
state.size += text.length;
|
|
1343
|
+
if (state.size > MAX_KEY_BYTES) {
|
|
1344
|
+
throw new Decline('too-large');
|
|
1345
|
+
}
|
|
1346
|
+
return text;
|
|
1347
|
+
}
|
|
1348
|
+
/**
|
|
1349
|
+
* Length-prefix strings so that no combination of separators can make two
|
|
1350
|
+
* different structures produce the same text.
|
|
1351
|
+
*/
|
|
1352
|
+
function encode_string(state, prefix, value) {
|
|
1353
|
+
return push(state, `${prefix}${value.length}:${value}`);
|
|
1354
|
+
}
|
|
1355
|
+
function walk(value, state) {
|
|
1356
|
+
// --- primitives, each tagged so shapes cannot collide ---
|
|
1357
|
+
if (value === null)
|
|
1358
|
+
return push(state, 'n');
|
|
1359
|
+
if (value === undefined)
|
|
1360
|
+
return push(state, 'u');
|
|
1361
|
+
const type = typeof value;
|
|
1362
|
+
if (type === 'boolean')
|
|
1363
|
+
return push(state, value ? 'b1' : 'b0');
|
|
1364
|
+
if (type === 'number')
|
|
1365
|
+
return push(state, `d${String(value)}`);
|
|
1366
|
+
if (type === 'string')
|
|
1367
|
+
return encode_string(state, 's', value);
|
|
1368
|
+
if (type === 'function')
|
|
1369
|
+
throw new Decline('function');
|
|
1370
|
+
if (type === 'symbol')
|
|
1371
|
+
throw new Decline('symbol');
|
|
1372
|
+
if (type === 'bigint')
|
|
1373
|
+
throw new Decline('bigint');
|
|
1374
|
+
if (type !== 'object')
|
|
1375
|
+
throw new Decline(type);
|
|
1376
|
+
// --- objects: reject the impostors before treating anything as data ---
|
|
1377
|
+
if (typeof value.nodeType === 'number')
|
|
1378
|
+
throw new Decline('dom-node');
|
|
1379
|
+
if (value.jquery)
|
|
1380
|
+
throw new Decline('jquery');
|
|
1381
|
+
if (state.seen.has(value))
|
|
1382
|
+
throw new Decline('circular');
|
|
1383
|
+
state.seen.add(value);
|
|
1384
|
+
try {
|
|
1385
|
+
if (value instanceof Date) {
|
|
1386
|
+
const time = value.getTime();
|
|
1387
|
+
if (Number.isNaN(time))
|
|
1388
|
+
throw new Decline('invalid-date');
|
|
1389
|
+
return push(state, `t${value.toISOString()}`);
|
|
1390
|
+
}
|
|
1391
|
+
if (Array.isArray(value)) {
|
|
1392
|
+
// Order is meaningful - never sorted.
|
|
1393
|
+
const parts = [];
|
|
1394
|
+
push(state, 'a[');
|
|
1395
|
+
for (let i = 0; i < value.length; i++) {
|
|
1396
|
+
parts.push(walk(value[i], state));
|
|
1397
|
+
}
|
|
1398
|
+
push(state, ']');
|
|
1399
|
+
return `a[${parts.join(',')}]`;
|
|
1400
|
+
}
|
|
1401
|
+
// Plain object ONLY. A class instance, Map, Set, RegExp, Error, etc. is
|
|
1402
|
+
// declined: two different classes with identical fields would otherwise
|
|
1403
|
+
// serialize the same and collide.
|
|
1404
|
+
const proto = Object.getPrototypeOf(value);
|
|
1405
|
+
if (proto !== Object.prototype && proto !== null) {
|
|
1406
|
+
throw new Decline('non-plain-object');
|
|
1407
|
+
}
|
|
1408
|
+
// Recursive key sort - the existing top-level sort is not enough for nesting.
|
|
1409
|
+
const keys = Object.keys(value).sort();
|
|
1410
|
+
const parts = [];
|
|
1411
|
+
push(state, 'o{');
|
|
1412
|
+
for (const key of keys) {
|
|
1413
|
+
const encoded_key = encode_string(state, 'k', key);
|
|
1414
|
+
parts.push(`${encoded_key}=${walk(value[key], state)}`);
|
|
1415
|
+
}
|
|
1416
|
+
push(state, '}');
|
|
1417
|
+
return `o{${parts.join(',')}}`;
|
|
1418
|
+
}
|
|
1419
|
+
finally {
|
|
1420
|
+
state.seen.delete(value);
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
/**
|
|
1424
|
+
* Serialize a value for use in a cache key.
|
|
1425
|
+
*
|
|
1426
|
+
* @returns `{ok: true, text}` when the value is exactly expressible, or
|
|
1427
|
+
* `{ok: false, reason}` naming why it is not. Never throws.
|
|
1428
|
+
*/
|
|
1429
|
+
function serialize_for_cache_key(value) {
|
|
1430
|
+
const state = { seen: new WeakSet(), size: 0 };
|
|
1431
|
+
try {
|
|
1432
|
+
return { ok: true, text: walk(value, state) };
|
|
1433
|
+
}
|
|
1434
|
+
catch (error) {
|
|
1435
|
+
if (error instanceof Decline) {
|
|
1436
|
+
return { ok: false, reason: error.reason };
|
|
1437
|
+
}
|
|
1438
|
+
// A throwing getter or exotic proxy trap - decline rather than guess.
|
|
1439
|
+
return { ok: false, reason: 'unserializable' };
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1229
1443
|
/**
|
|
1230
1444
|
* Load Coordinator - Request deduplication for component on_load() calls
|
|
1231
1445
|
*
|
|
@@ -1262,8 +1476,9 @@ class Load_Coordinator {
|
|
|
1262
1476
|
* - key: Cache key string, or null if uncacheable
|
|
1263
1477
|
* - uncacheable_property: Name of first property that prevented caching (for debugging)
|
|
1264
1478
|
*/
|
|
1265
|
-
static generate_invocation_key(component_name, args) {
|
|
1479
|
+
static generate_invocation_key(component_name, args, options = {}) {
|
|
1266
1480
|
let uncacheable_property;
|
|
1481
|
+
let uncacheable_reason;
|
|
1267
1482
|
// Filter out internal properties (starting with _) and serialize args
|
|
1268
1483
|
const serializable_args = {};
|
|
1269
1484
|
for (const key of Object.keys(args).sort()) {
|
|
@@ -1302,21 +1517,37 @@ class Load_Coordinator {
|
|
|
1302
1517
|
// Method threw error - treat as uncacheable
|
|
1303
1518
|
if (!uncacheable_property) {
|
|
1304
1519
|
uncacheable_property = key;
|
|
1520
|
+
uncacheable_reason = 'cache-id-threw';
|
|
1305
1521
|
}
|
|
1306
|
-
return { key: null, uncacheable_property };
|
|
1522
|
+
return { key: null, uncacheable_property, uncacheable_reason };
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
// No author-supplied id. The cache may fall back to keying by
|
|
1526
|
+
// deterministic content; deduplication deliberately may not.
|
|
1527
|
+
if (options.allow_content_serialization) {
|
|
1528
|
+
const serialized = serialize_for_cache_key(value);
|
|
1529
|
+
if (serialized.ok) {
|
|
1530
|
+
serializable_args[key] = `__JQHTML_CONTENT__:${serialized.text}`;
|
|
1531
|
+
continue;
|
|
1307
1532
|
}
|
|
1533
|
+
if (!uncacheable_property) {
|
|
1534
|
+
uncacheable_property = key;
|
|
1535
|
+
uncacheable_reason = serialized.reason;
|
|
1536
|
+
}
|
|
1537
|
+
return { key: null, uncacheable_property, uncacheable_reason };
|
|
1308
1538
|
}
|
|
1309
|
-
// No cache ID available - this property is uncacheable
|
|
1310
1539
|
if (!uncacheable_property) {
|
|
1311
1540
|
uncacheable_property = key;
|
|
1541
|
+
uncacheable_reason = value_type === 'function' ? 'function' : 'object';
|
|
1312
1542
|
}
|
|
1313
|
-
return { key: null, uncacheable_property };
|
|
1543
|
+
return { key: null, uncacheable_property, uncacheable_reason };
|
|
1314
1544
|
}
|
|
1315
1545
|
// Unknown type (symbol, bigint, etc.) - uncacheable
|
|
1316
1546
|
if (!uncacheable_property) {
|
|
1317
1547
|
uncacheable_property = key;
|
|
1548
|
+
uncacheable_reason = value_type;
|
|
1318
1549
|
}
|
|
1319
|
-
return { key: null, uncacheable_property };
|
|
1550
|
+
return { key: null, uncacheable_property, uncacheable_reason };
|
|
1320
1551
|
}
|
|
1321
1552
|
// Try to serialize - if it fails (shouldn't happen now, but safety net), return null
|
|
1322
1553
|
try {
|
|
@@ -1334,6 +1565,12 @@ class Load_Coordinator {
|
|
|
1334
1565
|
*/
|
|
1335
1566
|
static should_execute_on_load(component) {
|
|
1336
1567
|
const { key } = this.generate_invocation_key(component.component_name(), component.args);
|
|
1568
|
+
// No identity means no coordination. Without this, every un-keyable component
|
|
1569
|
+
// would share the single `null` registry entry and adopt an unrelated
|
|
1570
|
+
// component's data - the worst failure this system can produce.
|
|
1571
|
+
if (key === null) {
|
|
1572
|
+
return true;
|
|
1573
|
+
}
|
|
1337
1574
|
const entry = this._registry.get(key);
|
|
1338
1575
|
if (!entry) {
|
|
1339
1576
|
// No existing request - this component becomes the leader
|
|
@@ -1356,6 +1593,10 @@ class Load_Coordinator {
|
|
|
1356
1593
|
*/
|
|
1357
1594
|
static register_leader(component, on_load_promise) {
|
|
1358
1595
|
const { key } = this.generate_invocation_key(component.component_name(), component.args);
|
|
1596
|
+
// Un-keyable component: run alone, register nothing. See should_execute_on_load().
|
|
1597
|
+
if (key === null) {
|
|
1598
|
+
return () => { };
|
|
1599
|
+
}
|
|
1359
1600
|
// Create a promise that we control - it resolves when complete_coordination is called
|
|
1360
1601
|
let resolve_promise;
|
|
1361
1602
|
const coordination_promise = new Promise((resolve) => {
|
|
@@ -1577,6 +1818,8 @@ Load_Coordinator._registry = new Map();
|
|
|
1577
1818
|
// Registry mapping class names to constructors
|
|
1578
1819
|
const class_registry = Object.create(null);
|
|
1579
1820
|
// Markers for serialized class instances (unique to avoid collisions with user data)
|
|
1821
|
+
// Replaced at build time by rollup (see rollup.config.js).
|
|
1822
|
+
const CORE_VERSION = '2.3.54';
|
|
1580
1823
|
const CLASS_MARKER = '__jqhtml_class__';
|
|
1581
1824
|
const PROPS_MARKER = '__jqhtml_props__';
|
|
1582
1825
|
/**
|
|
@@ -1866,6 +2109,12 @@ class Jqhtml_Local_Storage {
|
|
|
1866
2109
|
*/
|
|
1867
2110
|
static set_cache_key(cache_key, cache_mode = 'data') {
|
|
1868
2111
|
this._cache_key = cache_key;
|
|
2112
|
+
// Key SHAPE is a function of the library (see cache-key-serializer.ts), so an
|
|
2113
|
+
// upgrade that changes it must invalidate everything persisted under the old shape.
|
|
2114
|
+
// Version the SCOPE MARKER rather than the key itself: _validate_scope() already
|
|
2115
|
+
// clears every jqhtml key when this value changes, and leaving the per-entry key
|
|
2116
|
+
// format alone keeps stored keys readable and avoids repeating the version in each.
|
|
2117
|
+
this._scope_marker = `${CORE_VERSION}::${cache_key}`;
|
|
1869
2118
|
this._cache_mode = cache_mode;
|
|
1870
2119
|
this._init();
|
|
1871
2120
|
}
|
|
@@ -1935,21 +2184,21 @@ class Jqhtml_Local_Storage {
|
|
|
1935
2184
|
}
|
|
1936
2185
|
try {
|
|
1937
2186
|
const stored_key = localStorage.getItem('_jqhtml_cache_key');
|
|
1938
|
-
// If
|
|
1939
|
-
if (stored_key !== null && stored_key !== this.
|
|
1940
|
-
console.log('[JQHTML Local Storage] Cache
|
|
2187
|
+
// If the scope marker (core version + developer key) changed, clear only JQHTML keys
|
|
2188
|
+
if (stored_key !== null && stored_key !== this._scope_marker) {
|
|
2189
|
+
console.log('[JQHTML Local Storage] Cache scope changed, clearing JQHTML keys:', {
|
|
1941
2190
|
old_key: stored_key,
|
|
1942
|
-
new_key: this.
|
|
2191
|
+
new_key: this._scope_marker,
|
|
1943
2192
|
});
|
|
1944
2193
|
this._clear_jqhtml_keys();
|
|
1945
|
-
localStorage.setItem('_jqhtml_cache_key', this.
|
|
2194
|
+
localStorage.setItem('_jqhtml_cache_key', this._scope_marker);
|
|
1946
2195
|
}
|
|
1947
2196
|
else if (stored_key === null) {
|
|
1948
2197
|
// First time JQHTML is using this storage - just set the key, don't clear
|
|
1949
|
-
console.log('[JQHTML Local Storage] Initializing cache
|
|
1950
|
-
new_key: this.
|
|
2198
|
+
console.log('[JQHTML Local Storage] Initializing cache scope (first use):', {
|
|
2199
|
+
new_key: this._scope_marker,
|
|
1951
2200
|
});
|
|
1952
|
-
localStorage.setItem('_jqhtml_cache_key', this.
|
|
2201
|
+
localStorage.setItem('_jqhtml_cache_key', this._scope_marker);
|
|
1953
2202
|
}
|
|
1954
2203
|
}
|
|
1955
2204
|
catch (e) {
|
|
@@ -2181,6 +2430,8 @@ class Jqhtml_Local_Storage {
|
|
|
2181
2430
|
}
|
|
2182
2431
|
}
|
|
2183
2432
|
Jqhtml_Local_Storage._cache_key = null;
|
|
2433
|
+
/** Versioned scope identity - compared in _validate_scope() to clear on a core upgrade. */
|
|
2434
|
+
Jqhtml_Local_Storage._scope_marker = null;
|
|
2184
2435
|
Jqhtml_Local_Storage._cache_mode = 'data';
|
|
2185
2436
|
Jqhtml_Local_Storage._storage_available = null;
|
|
2186
2437
|
Jqhtml_Local_Storage._initialized = false;
|
|
@@ -2278,10 +2529,15 @@ function event_once(component, event_name, callback) {
|
|
|
2278
2529
|
function event_trigger(component, event_name, data) {
|
|
2279
2530
|
// Mark this event as occurred and store the data for late subscribers
|
|
2280
2531
|
component._lifecycle_states.set(event_name, data);
|
|
2281
|
-
// Fire all registered callbacks for this event
|
|
2532
|
+
// Fire all registered callbacks for this event.
|
|
2533
|
+
// Iterate a SNAPSHOT: handlers registered via once() splice themselves out of the
|
|
2534
|
+
// live array while it is being iterated, which would shift later handlers into
|
|
2535
|
+
// already-visited slots and skip them. The snapshot also means a handler
|
|
2536
|
+
// registered during dispatch does not fire in this dispatch (event_on's sticky
|
|
2537
|
+
// immediate-fire already delivered it).
|
|
2282
2538
|
const callbacks = component._lifecycle_callbacks.get(event_name);
|
|
2283
2539
|
if (callbacks) {
|
|
2284
|
-
for (const callback of callbacks) {
|
|
2540
|
+
for (const callback of [...callbacks]) {
|
|
2285
2541
|
try {
|
|
2286
2542
|
callback.bind(component)(component, data);
|
|
2287
2543
|
}
|
|
@@ -2566,12 +2822,73 @@ function generate_cache_key(component) {
|
|
|
2566
2822
|
return { cache_key: `${component.component_name()}::${String(custom_cache_id)}` };
|
|
2567
2823
|
}
|
|
2568
2824
|
catch (error) {
|
|
2569
|
-
return { cache_key: null, uncacheable_property: 'cache_id()' };
|
|
2825
|
+
return { cache_key: null, uncacheable_property: 'cache_id()', uncacheable_reason: 'cache-id-threw' };
|
|
2570
2826
|
}
|
|
2571
2827
|
}
|
|
2572
|
-
//
|
|
2573
|
-
|
|
2574
|
-
|
|
2828
|
+
// Standard args-based key. The CACHE opts into content serialization for plain-data
|
|
2829
|
+
// object args; deduplication does not - see InvocationKeyOptions for why.
|
|
2830
|
+
const result = Load_Coordinator.generate_invocation_key(component.component_name(), component.args, { allow_content_serialization: true });
|
|
2831
|
+
return {
|
|
2832
|
+
cache_key: result.key,
|
|
2833
|
+
uncacheable_property: result.uncacheable_property,
|
|
2834
|
+
uncacheable_reason: result.uncacheable_reason,
|
|
2835
|
+
};
|
|
2836
|
+
}
|
|
2837
|
+
// One warning per component+property. A list of 50 rows must not print 50 warnings.
|
|
2838
|
+
const _warned_uncacheable = new Set();
|
|
2839
|
+
/**
|
|
2840
|
+
* Development warning: this component fetches data but cannot be cached, because
|
|
2841
|
+
* its args do not reduce to a stable key and it defines no cache_id() to say what
|
|
2842
|
+
* its identity is. Nothing is broken - the component works - but it silently opts
|
|
2843
|
+
* out of cache reuse and of load deduplication.
|
|
2844
|
+
*
|
|
2845
|
+
* Controlled by the `warn_uncacheable_args` integration flag (see config.ts).
|
|
2846
|
+
*/
|
|
2847
|
+
function warn_uncacheable_component(component, uncacheable_property, uncacheable_reason) {
|
|
2848
|
+
if (!warn_uncacheable_args_enabled())
|
|
2849
|
+
return;
|
|
2850
|
+
let name = 'unknown';
|
|
2851
|
+
try {
|
|
2852
|
+
name = component.component_name();
|
|
2853
|
+
}
|
|
2854
|
+
catch { /* name unavailable */ }
|
|
2855
|
+
const dedupe_key = `${name}::${uncacheable_property || '?'}::${uncacheable_reason || '?'}`;
|
|
2856
|
+
if (_warned_uncacheable.has(dedupe_key))
|
|
2857
|
+
return;
|
|
2858
|
+
_warned_uncacheable.add(dedupe_key);
|
|
2859
|
+
// cache_id() exists but threw - a different problem with a different fix
|
|
2860
|
+
if (uncacheable_property === 'cache_id()' || uncacheable_reason === 'cache-id-threw') {
|
|
2861
|
+
console.warn(`[JQHTML] <${name}> defines cache_id() but it threw, so this component is not cached ` +
|
|
2862
|
+
`and its loads are not deduplicated. Fix cache_id() so it returns a stable string.`, { component: name, cid: component._cid });
|
|
2863
|
+
return;
|
|
2864
|
+
}
|
|
2865
|
+
const REASONS = {
|
|
2866
|
+
'function': 'it contains a function - a callback is real identity that content cannot express',
|
|
2867
|
+
'symbol': 'it contains a symbol',
|
|
2868
|
+
'bigint': 'it contains a bigint',
|
|
2869
|
+
'dom-node': 'it contains a DOM node',
|
|
2870
|
+
'jquery': 'it contains a jQuery object',
|
|
2871
|
+
'circular': 'it contains a circular reference',
|
|
2872
|
+
'non-plain-object': 'it contains a class instance, Map, Set or similar - only plain objects and arrays can be keyed by content',
|
|
2873
|
+
'too-large': 'it serializes to more than 500 bytes - args are meant to identify data, not carry it',
|
|
2874
|
+
'invalid-date': 'it contains an invalid Date',
|
|
2875
|
+
};
|
|
2876
|
+
const why = REASONS[uncacheable_reason || ''] || 'it cannot be serialized deterministically';
|
|
2877
|
+
console.warn(`[JQHTML] <${name}> cannot be cached: arg $${uncacheable_property} is not keyable because ` +
|
|
2878
|
+
`${why}.\n` +
|
|
2879
|
+
` The component works, but it will not be restored from cache when reused.\n` +
|
|
2880
|
+
` Fix: define cache_id() on the component to state its identity explicitly, e.g.\n` +
|
|
2881
|
+
` cache_id() { return \`${name.toLowerCase()}_\${this.args.some_id}\`; }\n` +
|
|
2882
|
+
` Or pass plain data (objects/arrays of primitives are keyed automatically) instead of ` +
|
|
2883
|
+
`$${uncacheable_property}.\n` +
|
|
2884
|
+
` Find it in DevTools with the selector ` +
|
|
2885
|
+
`[data-nocache="${uncacheable_property}:${uncacheable_reason || ''}"].`, {
|
|
2886
|
+
component: name,
|
|
2887
|
+
cid: component._cid,
|
|
2888
|
+
uncacheable_arg: uncacheable_property,
|
|
2889
|
+
reason: uncacheable_reason,
|
|
2890
|
+
all_args: component.args ? Object.keys(component.args) : [],
|
|
2891
|
+
});
|
|
2575
2892
|
}
|
|
2576
2893
|
/**
|
|
2577
2894
|
* Read cache during create() phase.
|
|
@@ -2580,13 +2897,15 @@ function generate_cache_key(component) {
|
|
|
2580
2897
|
* @param component - The component instance
|
|
2581
2898
|
*/
|
|
2582
2899
|
function read_cache_in_create(component) {
|
|
2583
|
-
const { cache_key, uncacheable_property } = generate_cache_key(component);
|
|
2900
|
+
const { cache_key, uncacheable_property, uncacheable_reason } = generate_cache_key(component);
|
|
2584
2901
|
// If cache_key is null, caching disabled
|
|
2585
2902
|
if (cache_key === null) {
|
|
2586
2903
|
// Set data-nocache attribute for debugging
|
|
2587
2904
|
if (uncacheable_property) {
|
|
2588
|
-
|
|
2905
|
+
// "<arg>:<reason>" - a bare property name left authors reverse-engineering why
|
|
2906
|
+
component.$.attr('data-nocache', uncacheable_reason ? `${uncacheable_property}:${uncacheable_reason}` : uncacheable_property);
|
|
2589
2907
|
}
|
|
2908
|
+
warn_uncacheable_component(component, uncacheable_property, uncacheable_reason);
|
|
2590
2909
|
if (window.jqhtml?.debug?.verbose) {
|
|
2591
2910
|
console.log(`[Cache] Component ${component._cid} (${component.component_name()}) has non-serializable args - caching disabled`, { uncacheable_property });
|
|
2592
2911
|
}
|
|
@@ -3000,6 +3319,75 @@ function has_preload_data() {
|
|
|
3000
3319
|
return _preload_cache.size > 0;
|
|
3001
3320
|
}
|
|
3002
3321
|
|
|
3322
|
+
/**
|
|
3323
|
+
* JQHTML Viewport Resize Dispatch
|
|
3324
|
+
*
|
|
3325
|
+
* One framework-owned, debounced window 'resize' listener fans out to every live
|
|
3326
|
+
* component in the document by calling on_viewport_resize(viewport_width).
|
|
3327
|
+
*
|
|
3328
|
+
* Components never bind their own window listener, so there is nothing to unbind
|
|
3329
|
+
* in on_stop() - a component that is stopped or removed from the document simply
|
|
3330
|
+
* stops being found by the dispatcher.
|
|
3331
|
+
*/
|
|
3332
|
+
/** Trailing-edge debounce applied to the window resize listener. */
|
|
3333
|
+
const VIEWPORT_RESIZE_DEBOUNCE_MS = 30;
|
|
3334
|
+
let debounce_timer = null;
|
|
3335
|
+
let listener_installed = false;
|
|
3336
|
+
/**
|
|
3337
|
+
* Current viewport width in CSS pixels.
|
|
3338
|
+
*
|
|
3339
|
+
* window.innerWidth is used (not $(window).width()) because it matches the width
|
|
3340
|
+
* CSS media queries resolve against. It includes the scrollbar gutter.
|
|
3341
|
+
*
|
|
3342
|
+
* Returns 0 outside a browser (SSR).
|
|
3343
|
+
*/
|
|
3344
|
+
function get_viewport_width() {
|
|
3345
|
+
return typeof window !== 'undefined' ? window.innerWidth : 0;
|
|
3346
|
+
}
|
|
3347
|
+
/**
|
|
3348
|
+
* Call on_viewport_resize(width) on every component currently in the document.
|
|
3349
|
+
*
|
|
3350
|
+
* Walks $('.Component') in document order (parents before children). Elements
|
|
3351
|
+
* with no component instance and stopped components are skipped. A component
|
|
3352
|
+
* that throws is logged and the walk continues - one bad handler must not stop
|
|
3353
|
+
* the rest of the page from resizing.
|
|
3354
|
+
*/
|
|
3355
|
+
function dispatch_viewport_resize(jQuery) {
|
|
3356
|
+
const viewport_width = get_viewport_width();
|
|
3357
|
+
jQuery('.Component').each(function () {
|
|
3358
|
+
const component = jQuery(this).data('_component');
|
|
3359
|
+
if (!component || component._stopped)
|
|
3360
|
+
return;
|
|
3361
|
+
if (typeof component.on_viewport_resize !== 'function')
|
|
3362
|
+
return;
|
|
3363
|
+
try {
|
|
3364
|
+
component.on_viewport_resize(viewport_width);
|
|
3365
|
+
}
|
|
3366
|
+
catch (error) {
|
|
3367
|
+
console.error(`[JQHTML] Error in on_viewport_resize() for ${component.component_name?.() || 'component'}:`, error);
|
|
3368
|
+
}
|
|
3369
|
+
});
|
|
3370
|
+
}
|
|
3371
|
+
/**
|
|
3372
|
+
* Install the single window resize listener. Idempotent - repeated init() calls
|
|
3373
|
+
* and non-browser environments are no-ops.
|
|
3374
|
+
*/
|
|
3375
|
+
function init_viewport_resize(jQuery) {
|
|
3376
|
+
if (typeof window === 'undefined')
|
|
3377
|
+
return;
|
|
3378
|
+
if (listener_installed)
|
|
3379
|
+
return;
|
|
3380
|
+
listener_installed = true;
|
|
3381
|
+
jQuery(window).on('resize.jqhtml_viewport', function () {
|
|
3382
|
+
if (debounce_timer !== null)
|
|
3383
|
+
clearTimeout(debounce_timer);
|
|
3384
|
+
debounce_timer = setTimeout(function () {
|
|
3385
|
+
debounce_timer = null;
|
|
3386
|
+
dispatch_viewport_resize(jQuery);
|
|
3387
|
+
}, VIEWPORT_RESIZE_DEBOUNCE_MS);
|
|
3388
|
+
});
|
|
3389
|
+
}
|
|
3390
|
+
|
|
3003
3391
|
/**
|
|
3004
3392
|
* JQHTML v2 Component Base Class
|
|
3005
3393
|
*
|
|
@@ -3194,6 +3582,21 @@ class Jqhtml_Component {
|
|
|
3194
3582
|
// Call original directly - no flag needed since we bypass the wrapper
|
|
3195
3583
|
return impl.call(this);
|
|
3196
3584
|
}
|
|
3585
|
+
/**
|
|
3586
|
+
* Fire on_viewport_resize() with the current viewport width.
|
|
3587
|
+
* Called after every on_render() and on_ready(); the debounced window
|
|
3588
|
+
* listener in viewport.ts drives it from then on. Errors are isolated so a
|
|
3589
|
+
* bad handler cannot break the lifecycle.
|
|
3590
|
+
* @private
|
|
3591
|
+
*/
|
|
3592
|
+
_fire_viewport_resize() {
|
|
3593
|
+
try {
|
|
3594
|
+
this.on_viewport_resize(get_viewport_width());
|
|
3595
|
+
}
|
|
3596
|
+
catch (error) {
|
|
3597
|
+
console.error(`[JQHTML] Error in on_viewport_resize() for ${this.component_name()}:`, error);
|
|
3598
|
+
}
|
|
3599
|
+
}
|
|
3197
3600
|
/**
|
|
3198
3601
|
* Check if on_load() is overridden in a subclass
|
|
3199
3602
|
* Used to skip the load phase entirely for components that don't fetch data
|
|
@@ -3278,6 +3681,7 @@ class Jqhtml_Component {
|
|
|
3278
3681
|
console.warn(`[JQHTML] Component "${this.component_name()}" returned a Promise from on_render(). ` +
|
|
3279
3682
|
`on_render() must be synchronous code. Remove 'async' from the function declaration.`);
|
|
3280
3683
|
}
|
|
3684
|
+
this._fire_viewport_resize();
|
|
3281
3685
|
}
|
|
3282
3686
|
// Emit lifecycle event
|
|
3283
3687
|
this.trigger('render');
|
|
@@ -3469,6 +3873,7 @@ class Jqhtml_Component {
|
|
|
3469
3873
|
console.warn(`[JQHTML] Component "${this.component_name()}" returned a Promise from on_render(). ` +
|
|
3470
3874
|
`on_render() must be synchronous code. Remove 'async' from the function declaration.`);
|
|
3471
3875
|
}
|
|
3876
|
+
this._fire_viewport_resize();
|
|
3472
3877
|
}
|
|
3473
3878
|
// Emit lifecycle event
|
|
3474
3879
|
this.trigger('render');
|
|
@@ -3543,6 +3948,7 @@ class Jqhtml_Component {
|
|
|
3543
3948
|
}
|
|
3544
3949
|
// Call on_ready hook with authorization
|
|
3545
3950
|
await this._call_lifecycle('on_ready');
|
|
3951
|
+
this._fire_viewport_resize();
|
|
3546
3952
|
// Trigger ready event
|
|
3547
3953
|
this.trigger('ready');
|
|
3548
3954
|
});
|
|
@@ -3769,9 +4175,18 @@ class Jqhtml_Component {
|
|
|
3769
4175
|
}
|
|
3770
4176
|
return;
|
|
3771
4177
|
}
|
|
3772
|
-
//
|
|
4178
|
+
// TWO identities, deliberately computed separately.
|
|
4179
|
+
//
|
|
4180
|
+
// cache_key - may key a plain-data object arg by deterministic CONTENT, and may come
|
|
4181
|
+
// from the author's cache_id(). A wrong key here is corrected by
|
|
4182
|
+
// stale-while-revalidate.
|
|
4183
|
+
// dedup_key - args only, no content serialization, no cache_id(). A deduplicated
|
|
4184
|
+
// follower never runs on_load() and adopts the leader's data with NO
|
|
4185
|
+
// revalidation, so a wrong key here is permanently wrong data. Redundant
|
|
4186
|
+
// concurrent requests are the cheaper failure.
|
|
3773
4187
|
let cache_key = null;
|
|
3774
4188
|
let uncacheable_property;
|
|
4189
|
+
let uncacheable_reason;
|
|
3775
4190
|
if (typeof this.cache_id === 'function') {
|
|
3776
4191
|
try {
|
|
3777
4192
|
const custom_cache_id = this.cache_id();
|
|
@@ -3780,14 +4195,16 @@ class Jqhtml_Component {
|
|
|
3780
4195
|
catch (error) {
|
|
3781
4196
|
// cache_id() threw error - disable caching
|
|
3782
4197
|
uncacheable_property = 'cache_id()';
|
|
4198
|
+
uncacheable_reason = 'cache-id-threw';
|
|
3783
4199
|
}
|
|
3784
4200
|
}
|
|
3785
4201
|
else {
|
|
3786
|
-
|
|
3787
|
-
const result = Load_Coordinator.generate_invocation_key(this.component_name(), this.args);
|
|
4202
|
+
const result = Load_Coordinator.generate_invocation_key(this.component_name(), this.args, { allow_content_serialization: true });
|
|
3788
4203
|
cache_key = result.key;
|
|
3789
4204
|
uncacheable_property = result.uncacheable_property;
|
|
4205
|
+
uncacheable_reason = result.uncacheable_reason;
|
|
3790
4206
|
}
|
|
4207
|
+
const dedup_key = Load_Coordinator.generate_invocation_key(this.component_name(), this.args).key;
|
|
3791
4208
|
// SSR preload check: if preloaded data exists for this component+args, use it
|
|
3792
4209
|
if (cache_key !== null && has_preload_data()) {
|
|
3793
4210
|
const preloaded_data = consume_preload_data(cache_key);
|
|
@@ -3803,23 +4220,24 @@ class Jqhtml_Component {
|
|
|
3803
4220
|
}
|
|
3804
4221
|
// Store cache key for later use
|
|
3805
4222
|
this._cache_key = cache_key;
|
|
3806
|
-
//
|
|
3807
|
-
if (cache_key === null) {
|
|
3808
|
-
|
|
3809
|
-
if (
|
|
3810
|
-
this
|
|
4223
|
+
// Caching disabled - mark the element with the offending arg AND why
|
|
4224
|
+
if (cache_key === null && uncacheable_property) {
|
|
4225
|
+
this.$.attr('data-nocache', uncacheable_reason ? `${uncacheable_property}:${uncacheable_reason}` : uncacheable_property);
|
|
4226
|
+
if (window.jqhtml?.debug?.verbose) {
|
|
4227
|
+
console.log(`[Cache] Component ${this._cid} (${this.component_name()}) has non-keyable args - caching disabled`, { uncacheable_property, uncacheable_reason });
|
|
3811
4228
|
}
|
|
4229
|
+
}
|
|
4230
|
+
// Store "before" snapshot for comparison after on_load()
|
|
4231
|
+
const data_before_load = JSON.stringify(this.data);
|
|
4232
|
+
// No dedup identity - run on_load() alone rather than risk sharing a load
|
|
4233
|
+
if (dedup_key === null) {
|
|
3812
4234
|
if (window.jqhtml?.debug?.verbose) {
|
|
3813
|
-
console.log(`[
|
|
4235
|
+
console.log(`[Load Deduplication] Component ${this._cid} (${this.component_name()}) has no dedup key - loading independently`);
|
|
3814
4236
|
}
|
|
3815
|
-
// Execute on_load on detached proxy without deduplication
|
|
3816
4237
|
const { data: result_data } = await this._execute_on_load_detached();
|
|
3817
|
-
|
|
3818
|
-
await this._apply_load_result(result_data, null);
|
|
4238
|
+
await this._apply_load_result(result_data, data_before_load);
|
|
3819
4239
|
return;
|
|
3820
4240
|
}
|
|
3821
|
-
// Store "before" snapshot for comparison after on_load()
|
|
3822
|
-
const data_before_load = JSON.stringify(this.data);
|
|
3823
4241
|
// Check if this component should execute on_load() or wait for existing request
|
|
3824
4242
|
const should_execute = Load_Coordinator.should_execute_on_load(this);
|
|
3825
4243
|
if (!should_execute) {
|
|
@@ -3968,6 +4386,7 @@ class Jqhtml_Component {
|
|
|
3968
4386
|
// Wait for all children to reach ready state (bottom-up execution)
|
|
3969
4387
|
await this._wait_for_children_ready();
|
|
3970
4388
|
await this._call_lifecycle('on_ready');
|
|
4389
|
+
this._fire_viewport_resize();
|
|
3971
4390
|
this._ready_state = 4;
|
|
3972
4391
|
this._update_debug_attrs();
|
|
3973
4392
|
this._log_lifecycle('ready', 'complete');
|
|
@@ -4212,6 +4631,7 @@ class Jqhtml_Component {
|
|
|
4212
4631
|
this._render();
|
|
4213
4632
|
await this._wait_for_children_ready();
|
|
4214
4633
|
await this._call_lifecycle('on_ready');
|
|
4634
|
+
this._fire_viewport_resize();
|
|
4215
4635
|
this.trigger('ready');
|
|
4216
4636
|
this._log_lifecycle('reload', 'complete (no on_load)');
|
|
4217
4637
|
return;
|
|
@@ -4286,6 +4706,7 @@ class Jqhtml_Component {
|
|
|
4286
4706
|
if (rendered_from_cache || should_render) {
|
|
4287
4707
|
await this._wait_for_children_ready();
|
|
4288
4708
|
await this._call_lifecycle('on_ready');
|
|
4709
|
+
this._fire_viewport_resize();
|
|
4289
4710
|
// Trigger ready event so parent callbacks fire on subsequent reloads
|
|
4290
4711
|
this.trigger('ready');
|
|
4291
4712
|
}
|
|
@@ -4368,6 +4789,16 @@ class Jqhtml_Component {
|
|
|
4368
4789
|
on_loaded() { } // Override to process loaded data (e.g., clone this.data to this.state)
|
|
4369
4790
|
async on_ready() { }
|
|
4370
4791
|
on_stop() { }
|
|
4792
|
+
/**
|
|
4793
|
+
* Called with the viewport width in CSS pixels:
|
|
4794
|
+
* - after every on_render()
|
|
4795
|
+
* - after every on_ready()
|
|
4796
|
+
* - on window resize, debounced 30ms, for every component in the document
|
|
4797
|
+
*
|
|
4798
|
+
* MUST be synchronous. Prefer CSS media/container queries for pure styling -
|
|
4799
|
+
* override this only when layout requires imperative measurement.
|
|
4800
|
+
*/
|
|
4801
|
+
on_viewport_resize(viewport_width) { }
|
|
4371
4802
|
/**
|
|
4372
4803
|
* Should component re-render after load?
|
|
4373
4804
|
* By default, only re-renders if data has changed
|
|
@@ -4742,8 +5173,13 @@ class Jqhtml_Component {
|
|
|
4742
5173
|
}
|
|
4743
5174
|
}
|
|
4744
5175
|
_set_attributes() {
|
|
4745
|
-
//
|
|
4746
|
-
|
|
5176
|
+
// data-cid on the rendered element is debug-only: it mirrors this._cid so component
|
|
5177
|
+
// boundaries are legible in DevTools. The runtime never reads it - scoping uses the
|
|
5178
|
+
// _cid property, and the transient data-cid placeholder the instruction processor
|
|
5179
|
+
// matches on is written and removed before this ever runs.
|
|
5180
|
+
if (debug_attributes_enabled()) {
|
|
5181
|
+
this.$.attr('data-cid', this._cid);
|
|
5182
|
+
}
|
|
4747
5183
|
// Only set lifecycle state attribute if verbose mode enabled
|
|
4748
5184
|
if (window.jqhtml?.debug?.verbose) {
|
|
4749
5185
|
this.$.attr('data-_lifecycle-state', this._ready_state.toString());
|
|
@@ -5682,6 +6118,9 @@ function init_jquery_plugin(jQuery) {
|
|
|
5682
6118
|
PatchedInit.prototype = jQuery.fn;
|
|
5683
6119
|
jQuery.fn.init = PatchedInit;
|
|
5684
6120
|
}
|
|
6121
|
+
// Install the single debounced window resize listener that drives
|
|
6122
|
+
// on_viewport_resize() on every component. Idempotent.
|
|
6123
|
+
init_viewport_resize(jQuery);
|
|
5685
6124
|
}
|
|
5686
6125
|
// Try to auto-initialize if global jQuery exists
|
|
5687
6126
|
if (typeof window !== 'undefined' && window.jQuery) {
|
|
@@ -5695,7 +6134,9 @@ if (typeof window !== 'undefined' && window.jQuery) {
|
|
|
5695
6134
|
*/
|
|
5696
6135
|
// Core exports
|
|
5697
6136
|
// Main initialization function
|
|
5698
|
-
function init(jQuery) {
|
|
6137
|
+
function init(jQuery, config) {
|
|
6138
|
+
// Integration config first - it governs behaviour during the very first render
|
|
6139
|
+
configure(config);
|
|
5699
6140
|
// If jQuery is provided, initialize the plugin
|
|
5700
6141
|
if (jQuery) {
|
|
5701
6142
|
init_jquery_plugin(jQuery);
|
|
@@ -5709,7 +6150,7 @@ function init(jQuery) {
|
|
|
5709
6150
|
}
|
|
5710
6151
|
}
|
|
5711
6152
|
// Version - will be replaced during build with actual version from package.json
|
|
5712
|
-
const version = '2.3.
|
|
6153
|
+
const version = '2.3.54';
|
|
5713
6154
|
// Default export with all functionality
|
|
5714
6155
|
const jqhtml = {
|
|
5715
6156
|
// Core
|
|
@@ -5737,6 +6178,9 @@ const jqhtml = {
|
|
|
5737
6178
|
__version: version,
|
|
5738
6179
|
// state facts
|
|
5739
6180
|
tombstone: 'pepperoni and cheese',
|
|
6181
|
+
// Integration configuration - see config.ts
|
|
6182
|
+
configure,
|
|
6183
|
+
get_config,
|
|
5740
6184
|
// Debug settings
|
|
5741
6185
|
debug: {
|
|
5742
6186
|
enabled: false,
|
|
@@ -5842,5 +6286,5 @@ if (typeof window !== 'undefined' && !window.jqhtml) {
|
|
|
5842
6286
|
}
|
|
5843
6287
|
}
|
|
5844
6288
|
|
|
5845
|
-
export { Jqhtml_Component, LifecycleManager as Jqhtml_LifecycleManager, Jqhtml_Local_Storage, LifecycleManager, Load_Coordinator, applyDebugDelay, boot, clear_preload_data, create_component, jqhtml as default, devWarn, escape_html, escape_html_nl2br, extract_slots, get_captured_data, get_component_class, get_component_names, get_registered_templates, get_template, get_template_by_class, handleComponentError, has_component, init, init_jquery_plugin, isSequentialProcessing, list_components, logDataChange, logDispatch, logInstruction, logLifecycle, process_instructions, register, register_cache_class, register_component, register_template, render_template, set_preload_data, start_data_capture, stop_data_capture, version };
|
|
6289
|
+
export { Jqhtml_Component, LifecycleManager as Jqhtml_LifecycleManager, Jqhtml_Local_Storage, LifecycleManager, Load_Coordinator, applyDebugDelay, boot, clear_preload_data, configure, create_component, jqhtml as default, devWarn, escape_html, escape_html_nl2br, extract_slots, get_captured_data, get_component_class, get_component_names, get_config, get_registered_templates, get_template, get_template_by_class, handleComponentError, has_component, init, init_jquery_plugin, isSequentialProcessing, list_components, logDataChange, logDispatch, logInstruction, logLifecycle, process_instructions, register, register_cache_class, register_component, register_template, render_template, set_preload_data, start_data_capture, stop_data_capture, version };
|
|
5846
6290
|
//# sourceMappingURL=index.js.map
|