@jqhtml/core 2.3.4 → 2.3.9

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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * JQHTML Core v2.3.4
2
+ * JQHTML Core v2.3.9
3
3
  * (c) 2025 JQHTML Team
4
4
  * Released under the MIT License
5
5
  */
@@ -356,6 +356,50 @@ function list_components() {
356
356
  }
357
357
  return result;
358
358
  }
359
+ /**
360
+ * Unified registration function - auto-detects source type and delegates
361
+ *
362
+ * Accepts either:
363
+ * - A compiled JQHTML template (has __jqhtml_template: true)
364
+ * - A component class extending Jqhtml_Component (has static __jqhtml_component: true and static component_name)
365
+ *
366
+ * @param source - Compiled template or component class
367
+ */
368
+ function register(source) {
369
+ // Check for template (compiled .jqhtml file)
370
+ if (source && typeof source === 'object' && '__jqhtml_template' in source && source.__jqhtml_template === true) {
371
+ register_template(source);
372
+ return;
373
+ }
374
+ // Check for component class (extends Jqhtml_Component)
375
+ if (source && typeof source === 'function' && '__jqhtml_component' in source && source.__jqhtml_component === true) {
376
+ const component_name = source.component_name;
377
+ if (!component_name || typeof component_name !== 'string') {
378
+ throw new Error('[JQHTML] Component class must define static component_name property.\n\n' +
379
+ 'Example:\n' +
380
+ ' class My_Component extends Jqhtml_Component {\n' +
381
+ ' static component_name = "My_Component";\n' +
382
+ ' // ...\n' +
383
+ ' }\n\n' +
384
+ 'Alternatively, use register_component(name, class) to specify the name explicitly:\n' +
385
+ ' jqhtml.register_component("My_Component", My_Component);');
386
+ }
387
+ register_component(component_name, source);
388
+ return;
389
+ }
390
+ // Unknown type - provide helpful error
391
+ throw new Error('[JQHTML] register() requires a compiled JQHTML template or a component class.\n\n' +
392
+ 'For templates:\n' +
393
+ ' import My_Template from "./my_component.jqhtml";\n' +
394
+ ' jqhtml.register(My_Template);\n\n' +
395
+ 'For classes (with static component_name):\n' +
396
+ ' class My_Component extends Jqhtml_Component {\n' +
397
+ ' static component_name = "My_Component";\n' +
398
+ ' }\n' +
399
+ ' jqhtml.register(My_Component);\n\n' +
400
+ 'For classes (without static component_name):\n' +
401
+ ' jqhtml.register_component("My_Component", My_Component);');
402
+ }
359
403
 
360
404
  /**
361
405
  * JQHTML v2 Instruction Processor
@@ -884,7 +928,7 @@ function devWarn(message) {
884
928
  console.warn(`[JQHTML Dev Warning] ${message}`);
885
929
  }
886
930
  // Get global jqhtml object
887
- function getJqhtml$1() {
931
+ function getJqhtml() {
888
932
  if (typeof window !== 'undefined' && window.jqhtml) {
889
933
  return window.jqhtml;
890
934
  }
@@ -897,7 +941,7 @@ function getJqhtml$1() {
897
941
  }
898
942
  // Visual flash effect
899
943
  function flashComponent(component, eventType) {
900
- const jqhtml = getJqhtml$1();
944
+ const jqhtml = getJqhtml();
901
945
  if (!jqhtml?.debug?.flashComponents)
902
946
  return;
903
947
  const duration = jqhtml.debug.flashDuration || 500;
@@ -919,7 +963,7 @@ function flashComponent(component, eventType) {
919
963
  }
920
964
  // Log lifecycle event
921
965
  function logLifecycle(component, phase, status) {
922
- const jqhtml = getJqhtml$1();
966
+ const jqhtml = getJqhtml();
923
967
  if (!jqhtml?.debug)
924
968
  return;
925
969
  const shouldLog = jqhtml.debug.logFullLifecycle ||
@@ -965,7 +1009,7 @@ function logLifecycle(component, phase, status) {
965
1009
  }
966
1010
  // Apply delays based on lifecycle phase
967
1011
  function applyDebugDelay(phase) {
968
- const jqhtml = getJqhtml$1();
1012
+ const jqhtml = getJqhtml();
969
1013
  if (!jqhtml?.debug)
970
1014
  return;
971
1015
  let delayMs = 0;
@@ -986,14 +1030,14 @@ function applyDebugDelay(phase) {
986
1030
  }
987
1031
  // Log instruction processing
988
1032
  function logInstruction(type, data) {
989
- const jqhtml = getJqhtml$1();
1033
+ const jqhtml = getJqhtml();
990
1034
  if (!jqhtml?.debug?.logInstructionProcessing)
991
1035
  return;
992
1036
  console.log(`[JQHTML Instruction] ${type}:`, data);
993
1037
  }
994
1038
  // Log data changes
995
1039
  function logDataChange(component, property, oldValue, newValue) {
996
- const jqhtml = getJqhtml$1();
1040
+ const jqhtml = getJqhtml();
997
1041
  if (!jqhtml?.debug?.traceDataFlow)
998
1042
  return;
999
1043
  console.log(`[JQHTML Data] ${component.constructor.name}#${component._cid}.data.${property}:`, { old: oldValue, new: newValue });
@@ -1006,7 +1050,7 @@ function updateComponentTree() {
1006
1050
  }
1007
1051
  // Router dispatch logging
1008
1052
  function logDispatch(url, route, params, verbose = false) {
1009
- const jqhtml = getJqhtml$1();
1053
+ const jqhtml = getJqhtml();
1010
1054
  if (!jqhtml?.debug)
1011
1055
  return;
1012
1056
  const shouldLog = jqhtml.debug.logDispatch || jqhtml.debug.logDispatchVerbose;
@@ -1028,12 +1072,12 @@ function logDispatch(url, route, params, verbose = false) {
1028
1072
  }
1029
1073
  // Check if sequential processing is enabled
1030
1074
  function isSequentialProcessing() {
1031
- const jqhtml = getJqhtml$1();
1075
+ const jqhtml = getJqhtml();
1032
1076
  return jqhtml?.debug?.sequentialProcessing || false;
1033
1077
  }
1034
1078
  // Error handling with break on error
1035
1079
  function handleComponentError(component, phase, error) {
1036
- const jqhtml = getJqhtml$1();
1080
+ const jqhtml = getJqhtml();
1037
1081
  console.error(`[JQHTML Error] ${component.constructor.name}#${component._cid} failed in ${phase}:`, error);
1038
1082
  if (jqhtml?.debug?.breakOnError) {
1039
1083
  debugger; // This will pause execution in dev tools
@@ -1061,6 +1105,9 @@ function handleComponentError(component, phase, error) {
1061
1105
  * - Scoped IDs using _cid pattern
1062
1106
  * - Event emission and CSS class hierarchy
1063
1107
  */
1108
+ // WeakMap storage for protected lifecycle method implementations (Option 2)
1109
+ // See docs/internal/lifecycle-method-protection.md for design details
1110
+ const lifecycle_impls = new WeakMap();
1064
1111
  class Jqhtml_Component {
1065
1112
  constructor(element, args = {}) {
1066
1113
  this._ready_state = 0; // 0=created, 1=init, 2=loaded, 3=rendered, 4=ready
@@ -1081,6 +1128,7 @@ class Jqhtml_Component {
1081
1128
  this.__initial_data_snapshot = null; // Snapshot of this.data after on_create() for restoration before on_load()
1082
1129
  this.__data_frozen = false; // Track if this.data is currently frozen
1083
1130
  this.next_reload_force_refresh = null; // State machine for reload()/refresh() debounce precedence
1131
+ this.__lifecycle_authorized = false; // Flag for lifecycle method protection
1084
1132
  this._cid = this._generate_cid();
1085
1133
  this._lifecycle_manager = LifecycleManager.get_instance();
1086
1134
  // Create or wrap element
@@ -1194,6 +1242,63 @@ class Jqhtml_Component {
1194
1242
  this.state = {};
1195
1243
  this._log_lifecycle('construct', 'complete');
1196
1244
  }
1245
+ /**
1246
+ * Protect lifecycle methods from manual invocation
1247
+ * Stores original implementations in WeakMap, replaces with guarded wrappers
1248
+ * @private
1249
+ */
1250
+ _protect_lifecycle_methods() {
1251
+ const methods = {
1252
+ on_create: 'Called automatically during creation.',
1253
+ on_render: 'Use render() to trigger a re-render.',
1254
+ on_load: 'Use reload() to refresh data.',
1255
+ on_ready: 'Called automatically when ready.',
1256
+ on_stop: 'Use stop() to stop the component.'
1257
+ };
1258
+ const impls = {};
1259
+ const self = this;
1260
+ for (const [name, help] of Object.entries(methods)) {
1261
+ const original = this[name];
1262
+ // Skip if using base class default (empty method)
1263
+ if (original === Jqhtml_Component.prototype[name])
1264
+ continue;
1265
+ impls[name] = original;
1266
+ // Create wrapper with same function name (for stack traces)
1267
+ this[name] = {
1268
+ [name](...args) {
1269
+ if (!self.__lifecycle_authorized) {
1270
+ throw new Error(`[JQHTML] ${name}() cannot be called manually. ${help}\n` +
1271
+ `Component: ${self.component_name()} (_cid: ${self._cid})`);
1272
+ }
1273
+ return lifecycle_impls.get(self)[name].apply(self, args);
1274
+ }
1275
+ }[name];
1276
+ }
1277
+ lifecycle_impls.set(this, impls);
1278
+ }
1279
+ /**
1280
+ * Call a lifecycle method with authorization (async)
1281
+ * Framework calls this to invoke lifecycle methods, bypassing the protection wrapper.
1282
+ * The flag is set momentarily for the wrapper check, then reset BEFORE user code runs.
1283
+ * This ensures any nested lifecycle calls from user code will fail the flag check.
1284
+ * @private
1285
+ */
1286
+ async _call_lifecycle(name, context) {
1287
+ // Get the original implementation (bypasses wrapper entirely)
1288
+ const impl = lifecycle_impls.get(this)?.[name] || this[name];
1289
+ // Call original directly - no flag needed since we bypass the wrapper
1290
+ return await impl.call(context || this);
1291
+ }
1292
+ /**
1293
+ * Call a lifecycle method with authorization (sync version for sync methods like on_stop)
1294
+ * @private
1295
+ */
1296
+ _call_lifecycle_sync(name) {
1297
+ // Get the original implementation (bypasses wrapper entirely)
1298
+ const impl = lifecycle_impls.get(this)?.[name] || this[name];
1299
+ // Call original directly - no flag needed since we bypass the wrapper
1300
+ return impl.call(this);
1301
+ }
1197
1302
  /**
1198
1303
  * Boot - Start the full component lifecycle
1199
1304
  * Called immediately after construction by instruction processor
@@ -1207,6 +1312,8 @@ class Jqhtml_Component {
1207
1312
  if (this._booted)
1208
1313
  return;
1209
1314
  this._booted = true;
1315
+ // Protect lifecycle methods from manual invocation (must happen after subclass constructor)
1316
+ this._protect_lifecycle_methods();
1210
1317
  await this._lifecycle_manager.boot_component(this);
1211
1318
  }
1212
1319
  // -------------------------------------------------------------------------
@@ -1417,9 +1524,9 @@ class Jqhtml_Component {
1417
1524
  // Don't update ready state here - let phases complete in order
1418
1525
  this._update_debug_attrs();
1419
1526
  this._log_lifecycle('render', 'complete');
1420
- // Call on_render() immediately after render completes
1527
+ // Call on_render() with authorization (sync) immediately after render completes
1421
1528
  // This ensures event handlers are always re-attached after DOM updates
1422
- const renderResult = this.on_render();
1529
+ const renderResult = this._call_lifecycle_sync('on_render');
1423
1530
  if (renderResult && typeof renderResult.then === 'function') {
1424
1531
  console.warn(`[JQHTML] Component "${this.component_name()}" returned a Promise from on_render(). ` +
1425
1532
  `on_render() must be synchronous code. Remove 'async' from the function declaration.`);
@@ -1484,8 +1591,8 @@ class Jqhtml_Component {
1484
1591
  if (this._render_count !== render_id) {
1485
1592
  return; // Stale render, don't call on_ready
1486
1593
  }
1487
- // Call on_ready hook
1488
- await this.on_ready();
1594
+ // Call on_ready hook with authorization
1595
+ await this._call_lifecycle('on_ready');
1489
1596
  // Trigger ready event
1490
1597
  await this.trigger('ready');
1491
1598
  })();
@@ -1505,8 +1612,8 @@ class Jqhtml_Component {
1505
1612
  if (this._stopped || this._ready_state >= 1)
1506
1613
  return;
1507
1614
  this._log_lifecycle('create', 'start');
1508
- // Call on_create() and validate it's synchronous
1509
- const result = this.on_create();
1615
+ // Call on_create() with authorization and validate it's synchronous
1616
+ const result = await this._call_lifecycle('on_create');
1510
1617
  if (result && typeof result.then === 'function') {
1511
1618
  console.warn(`[JQHTML] Component "${this.component_name()}" returned a Promise from on_create(). ` +
1512
1619
  `on_create() must be synchronous code. Remove 'async' from the function declaration.`);
@@ -1621,8 +1728,8 @@ class Jqhtml_Component {
1621
1728
  if (window.jqhtml?.debug?.verbose) {
1622
1729
  console.log(`[Cache] Component ${this._cid} (${this.component_name()}) has non-serializable args - load deduplication and caching disabled`, { uncacheable_property });
1623
1730
  }
1624
- // Execute on_load() without deduplication or caching
1625
- await this.on_load();
1731
+ // Execute on_load() with authorization, without deduplication or caching
1732
+ await this._call_lifecycle('on_load');
1626
1733
  this.__data_frozen = true;
1627
1734
  return;
1628
1735
  }
@@ -1731,7 +1838,7 @@ class Jqhtml_Component {
1731
1838
  // - Should errors reset state machine flags (next_reload_force_refresh)?
1732
1839
  // - Should partial data be preserved or rolled back?
1733
1840
  // - Should followers be notified differently based on error type?
1734
- await this.on_load.call(restricted_this);
1841
+ await this._call_lifecycle('on_load', restricted_this);
1735
1842
  }
1736
1843
  catch (error) {
1737
1844
  // Handle error and notify coordinator
@@ -1803,7 +1910,7 @@ class Jqhtml_Component {
1803
1910
  this._log_lifecycle('ready', 'start');
1804
1911
  // Wait for all children to reach ready state (bottom-up execution)
1805
1912
  await this._wait_for_children_ready();
1806
- await this.on_ready();
1913
+ await this._call_lifecycle('on_ready');
1807
1914
  this._ready_state = 4;
1808
1915
  this._update_debug_attrs();
1809
1916
  this._log_lifecycle('ready', 'complete');
@@ -2019,7 +2126,7 @@ class Jqhtml_Component {
2019
2126
  this.data = JSON.parse(JSON.stringify(this.__initial_data_snapshot));
2020
2127
  }
2021
2128
  try {
2022
- await this.on_load();
2129
+ await this._call_lifecycle('on_load');
2023
2130
  }
2024
2131
  finally {
2025
2132
  // Freeze this.data after on_load() completes
@@ -2095,7 +2202,7 @@ class Jqhtml_Component {
2095
2202
  // STEP 3.5 & 4: Wait for children and call on_ready (only if we rendered)
2096
2203
  if (rendered_from_cache || should_render) {
2097
2204
  await this._wait_for_children_ready();
2098
- await this.on_ready();
2205
+ await this._call_lifecycle('on_ready');
2099
2206
  }
2100
2207
  this._log_lifecycle('reload', 'complete');
2101
2208
  }
@@ -2129,16 +2236,14 @@ class Jqhtml_Component {
2129
2236
  this.$.addClass('_Component_Stopped');
2130
2237
  // Unregister from lifecycle manager
2131
2238
  this._lifecycle_manager.unregister_component(this);
2132
- // Call user's on_stop() hook
2133
- const stopResult = this.on_stop();
2239
+ // Call user's on_stop() hook with authorization (sync)
2240
+ const stopResult = this._call_lifecycle_sync('on_stop');
2134
2241
  if (stopResult && typeof stopResult.then === 'function') {
2135
2242
  console.warn(`[JQHTML] Component "${this.component_name()}" returned a Promise from on_stop(). ` +
2136
2243
  `on_stop() must be synchronous code. Remove 'async' from the function declaration.`);
2137
2244
  }
2138
- // Fire registered destroy callbacks
2139
- this.trigger('destroy');
2140
- // Trigger jQuery destroy event
2141
- this.$.trigger('destroy');
2245
+ // Fire registered stop callbacks
2246
+ this.trigger('stop');
2142
2247
  // Remove from DOM parent's children
2143
2248
  if (this._dom_parent) {
2144
2249
  this._dom_parent._dom_children.delete(this);
@@ -2701,6 +2806,8 @@ class Jqhtml_Component {
2701
2806
  };
2702
2807
  }
2703
2808
  }
2809
+ // Static properties
2810
+ Jqhtml_Component.__jqhtml_component = true; // Marker for unified register() detection
2704
2811
 
2705
2812
  /**
2706
2813
  * JQHTML v2 Template Renderer
@@ -3096,388 +3203,6 @@ function hydrateElement($element, jQ) {
3096
3203
  }
3097
3204
  }
3098
3205
 
3099
- /**
3100
- * JQHTML Debug Overlay
3101
- *
3102
- * Independent debug controls using pure jQuery DOM manipulation.
3103
- * Does NOT use JQHTML components so it works even when components are broken.
3104
- */
3105
- // Get global jQuery
3106
- function getJQuery() {
3107
- if (typeof window !== 'undefined' && window.$) {
3108
- return window.$;
3109
- }
3110
- if (typeof window !== 'undefined' && window.jQuery) {
3111
- return window.jQuery;
3112
- }
3113
- throw new Error('FATAL: jQuery is not defined. jQuery must be loaded before using JQHTML. ' +
3114
- 'Add <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script> before loading JQHTML.');
3115
- }
3116
- // Get global jqhtml object
3117
- function getJqhtml() {
3118
- if (typeof window !== 'undefined' && window.jqhtml) {
3119
- return window.jqhtml;
3120
- }
3121
- if (typeof globalThis !== 'undefined' && globalThis.jqhtml) {
3122
- return globalThis.jqhtml;
3123
- }
3124
- throw new Error('FATAL: window.jqhtml is not defined. The JQHTML runtime must be loaded before using JQHTML components. ' +
3125
- 'Ensure @jqhtml/core is imported and initialized before attempting to use debug features.');
3126
- }
3127
- class DebugOverlay {
3128
- constructor(options = {}) {
3129
- this.$container = null;
3130
- this.$statusIndicator = null;
3131
- this.$ = getJQuery();
3132
- if (!this.$) {
3133
- throw new Error('jQuery is required for DebugOverlay');
3134
- }
3135
- this.options = {
3136
- position: 'bottom',
3137
- theme: 'dark',
3138
- compact: false,
3139
- showStatus: true,
3140
- autoHide: false,
3141
- ...options
3142
- };
3143
- }
3144
- /**
3145
- * Static method to show debug overlay (singleton pattern)
3146
- */
3147
- static show(options) {
3148
- if (!DebugOverlay.instance) {
3149
- DebugOverlay.instance = new DebugOverlay(options);
3150
- }
3151
- DebugOverlay.instance.display();
3152
- return DebugOverlay.instance;
3153
- }
3154
- /**
3155
- * Static method to hide debug overlay
3156
- */
3157
- static hide() {
3158
- if (DebugOverlay.instance) {
3159
- DebugOverlay.instance.hide();
3160
- }
3161
- }
3162
- /**
3163
- * Static method to toggle debug overlay visibility
3164
- */
3165
- static toggle() {
3166
- if (DebugOverlay.instance && DebugOverlay.instance.$container) {
3167
- if (DebugOverlay.instance.$container.is(':visible')) {
3168
- DebugOverlay.hide();
3169
- }
3170
- else {
3171
- DebugOverlay.instance.display();
3172
- }
3173
- }
3174
- else {
3175
- DebugOverlay.show();
3176
- }
3177
- }
3178
- /**
3179
- * Static method to destroy debug overlay
3180
- */
3181
- static destroy() {
3182
- if (DebugOverlay.instance) {
3183
- DebugOverlay.instance.destroy();
3184
- DebugOverlay.instance = null;
3185
- }
3186
- }
3187
- /**
3188
- * Display the debug overlay
3189
- */
3190
- display() {
3191
- if (this.$container) {
3192
- this.$container.show();
3193
- return;
3194
- }
3195
- this.createOverlay();
3196
- if (this.options.showStatus) {
3197
- this.createStatusIndicator();
3198
- }
3199
- }
3200
- /**
3201
- * Hide the debug overlay
3202
- */
3203
- hide() {
3204
- if (this.$container) {
3205
- this.$container.hide();
3206
- }
3207
- if (this.$statusIndicator) {
3208
- this.$statusIndicator.hide();
3209
- }
3210
- }
3211
- /**
3212
- * Remove the debug overlay completely
3213
- */
3214
- destroy() {
3215
- if (this.$container) {
3216
- this.$container.remove();
3217
- this.$container = null;
3218
- }
3219
- if (this.$statusIndicator) {
3220
- this.$statusIndicator.remove();
3221
- this.$statusIndicator = null;
3222
- }
3223
- }
3224
- /**
3225
- * Update the status indicator
3226
- */
3227
- updateStatus(mode) {
3228
- if (!this.$statusIndicator)
3229
- return;
3230
- this.$statusIndicator.text('Debug: ' + mode);
3231
- this.$statusIndicator.attr('class', 'jqhtml-debug-status' + (mode !== 'Off' ? ' active' : ''));
3232
- }
3233
- createOverlay() {
3234
- // Add styles first
3235
- this.addStyles();
3236
- // Create container using jQuery
3237
- this.$container = this.$('<div>')
3238
- .addClass(`jqhtml-debug-overlay ${this.options.theme} ${this.options.position}`);
3239
- // Create content structure
3240
- const $content = this.$('<div>').addClass('jqhtml-debug-content');
3241
- const $controls = this.$('<div>').addClass('jqhtml-debug-controls');
3242
- // Add title
3243
- const $title = this.$('<span>')
3244
- .addClass('jqhtml-debug-title')
3245
- .html('<strong>🐛 JQHTML Debug:</strong>');
3246
- $controls.append($title);
3247
- // Create buttons
3248
- const buttons = [
3249
- { text: 'Slow Motion + Flash', action: 'enableSlowMotionDebug', class: 'success' },
3250
- { text: 'Basic Debug', action: 'enableBasicDebug', class: '' },
3251
- { text: 'Full Debug', action: 'enableFullDebug', class: '' },
3252
- { text: 'Sequential', action: 'enableSequentialMode', class: '' },
3253
- { text: 'Clear Debug', action: 'clearAllDebug', class: 'danger' },
3254
- { text: 'Settings', action: 'showDebugInfo', class: '' }
3255
- ];
3256
- buttons.forEach(btn => {
3257
- const $button = this.$('<button>')
3258
- .text(btn.text)
3259
- .addClass('jqhtml-debug-btn' + (btn.class ? ` ${btn.class}` : ''))
3260
- .on('click', () => this.executeAction(btn.action));
3261
- $controls.append($button);
3262
- });
3263
- // Add minimize/close button
3264
- const $toggleBtn = this.$('<button>')
3265
- .text(this.options.compact ? '▼' : '▲')
3266
- .addClass('jqhtml-debug-toggle')
3267
- .on('click', () => this.toggle());
3268
- $controls.append($toggleBtn);
3269
- // Assemble and add to page
3270
- $content.append($controls);
3271
- this.$container.append($content);
3272
- this.$('body').append(this.$container);
3273
- }
3274
- createStatusIndicator() {
3275
- this.$statusIndicator = this.$('<div>')
3276
- .addClass('jqhtml-debug-status')
3277
- .text('Debug: Off')
3278
- .css({
3279
- position: 'fixed',
3280
- top: '10px',
3281
- right: '10px',
3282
- background: '#2c3e50',
3283
- color: 'white',
3284
- padding: '5px 10px',
3285
- borderRadius: '4px',
3286
- fontSize: '0.75rem',
3287
- zIndex: '10001',
3288
- opacity: '0.8',
3289
- fontFamily: 'monospace'
3290
- });
3291
- this.$('body').append(this.$statusIndicator);
3292
- }
3293
- addStyles() {
3294
- // Check if styles already exist
3295
- if (this.$('#jqhtml-debug-styles').length > 0)
3296
- return;
3297
- // Create and inject CSS using jQuery - concatenated strings for better minification
3298
- const $style = this.$('<style>')
3299
- .attr('id', 'jqhtml-debug-styles')
3300
- .text('.jqhtml-debug-overlay {' +
3301
- 'position: fixed;' +
3302
- 'left: 0;' +
3303
- 'right: 0;' +
3304
- 'z-index: 10000;' +
3305
- 'font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace;' +
3306
- 'font-size: 0.8rem;' +
3307
- 'box-shadow: 0 2px 10px rgba(0,0,0,0.2);' +
3308
- '}' +
3309
- '.jqhtml-debug-overlay.top {' +
3310
- 'top: 0;' +
3311
- '}' +
3312
- '.jqhtml-debug-overlay.bottom {' +
3313
- 'bottom: 0;' +
3314
- '}' +
3315
- '.jqhtml-debug-overlay.dark {' +
3316
- 'background: #34495e;' +
3317
- 'color: #ecf0f1;' +
3318
- '}' +
3319
- '.jqhtml-debug-overlay.light {' +
3320
- 'background: #f8f9fa;' +
3321
- 'color: #333;' +
3322
- 'border-bottom: 1px solid #dee2e6;' +
3323
- '}' +
3324
- '.jqhtml-debug-content {' +
3325
- 'padding: 0.5rem 1rem;' +
3326
- '}' +
3327
- '.jqhtml-debug-controls {' +
3328
- 'display: flex;' +
3329
- 'flex-wrap: wrap;' +
3330
- 'gap: 8px;' +
3331
- 'align-items: center;' +
3332
- '}' +
3333
- '.jqhtml-debug-title {' +
3334
- 'margin-right: 10px;' +
3335
- 'font-weight: bold;' +
3336
- '}' +
3337
- '.jqhtml-debug-btn {' +
3338
- 'padding: 4px 8px;' +
3339
- 'border: none;' +
3340
- 'border-radius: 3px;' +
3341
- 'background: #3498db;' +
3342
- 'color: white;' +
3343
- 'cursor: pointer;' +
3344
- 'font-size: 0.75rem;' +
3345
- 'transition: background 0.2s;' +
3346
- '}' +
3347
- '.jqhtml-debug-btn:hover {' +
3348
- 'background: #2980b9;' +
3349
- '}' +
3350
- '.jqhtml-debug-btn.success {' +
3351
- 'background: #27ae60;' +
3352
- '}' +
3353
- '.jqhtml-debug-btn.success:hover {' +
3354
- 'background: #229954;' +
3355
- '}' +
3356
- '.jqhtml-debug-btn.danger {' +
3357
- 'background: #e74c3c;' +
3358
- '}' +
3359
- '.jqhtml-debug-btn.danger:hover {' +
3360
- 'background: #c0392b;' +
3361
- '}' +
3362
- '.jqhtml-debug-toggle {' +
3363
- 'padding: 4px 8px;' +
3364
- 'border: none;' +
3365
- 'border-radius: 3px;' +
3366
- 'background: #7f8c8d;' +
3367
- 'color: white;' +
3368
- 'cursor: pointer;' +
3369
- 'font-size: 0.75rem;' +
3370
- 'margin-left: auto;' +
3371
- '}' +
3372
- '.jqhtml-debug-toggle:hover {' +
3373
- 'background: #6c7b7d;' +
3374
- '}' +
3375
- '.jqhtml-debug-status.active {' +
3376
- 'background: #27ae60 !important;' +
3377
- '}' +
3378
- '@media (max-width: 768px) {' +
3379
- '.jqhtml-debug-controls {' +
3380
- 'flex-direction: column;' +
3381
- 'align-items: flex-start;' +
3382
- '}' +
3383
- '.jqhtml-debug-title {' +
3384
- 'margin-bottom: 5px;' +
3385
- '}' +
3386
- '}');
3387
- this.$('head').append($style);
3388
- }
3389
- toggle() {
3390
- // Toggle between compact and full view
3391
- this.options.compact = !this.options.compact;
3392
- const $toggleBtn = this.$container.find('.jqhtml-debug-toggle');
3393
- $toggleBtn.text(this.options.compact ? '▼' : '▲');
3394
- const $buttons = this.$container.find('.jqhtml-debug-btn');
3395
- if (this.options.compact) {
3396
- $buttons.hide();
3397
- }
3398
- else {
3399
- $buttons.show();
3400
- }
3401
- }
3402
- executeAction(action) {
3403
- const jqhtml = getJqhtml();
3404
- if (!jqhtml) {
3405
- console.warn('JQHTML not available - make sure it\'s loaded and exposed globally');
3406
- return;
3407
- }
3408
- switch (action) {
3409
- case 'enableSlowMotionDebug':
3410
- jqhtml.setDebugSettings({
3411
- logFullLifecycle: true,
3412
- sequentialProcessing: true,
3413
- delayAfterComponent: 150,
3414
- delayAfterRender: 200,
3415
- delayAfterRerender: 250,
3416
- flashComponents: true,
3417
- flashDuration: 800,
3418
- flashColors: {
3419
- create: '#3498db',
3420
- render: '#27ae60',
3421
- ready: '#9b59b6'
3422
- },
3423
- profilePerformance: true,
3424
- highlightSlowRenders: 30,
3425
- logDispatch: true
3426
- });
3427
- this.updateStatus('Slow Motion');
3428
- console.log('🐛 Slow Motion Debug Mode Enabled');
3429
- break;
3430
- case 'enableBasicDebug':
3431
- jqhtml.enableDebugMode('basic');
3432
- this.updateStatus('Basic');
3433
- console.log('🐛 Basic Debug Mode Enabled');
3434
- break;
3435
- case 'enableFullDebug':
3436
- jqhtml.enableDebugMode('full');
3437
- this.updateStatus('Full');
3438
- console.log('🐛 Full Debug Mode Enabled');
3439
- break;
3440
- case 'enableSequentialMode':
3441
- jqhtml.setDebugSettings({
3442
- logCreationReady: true,
3443
- sequentialProcessing: true,
3444
- flashComponents: true,
3445
- profilePerformance: true
3446
- });
3447
- this.updateStatus('Sequential');
3448
- console.log('🐛 Sequential Processing Mode Enabled');
3449
- break;
3450
- case 'clearAllDebug':
3451
- jqhtml.clearDebugSettings();
3452
- this.updateStatus('Off');
3453
- console.log('🐛 All Debug Modes Disabled');
3454
- break;
3455
- case 'showDebugInfo':
3456
- const settings = JSON.stringify(jqhtml.debug, null, 2);
3457
- console.log('🐛 Current Debug Settings:', settings);
3458
- alert('Debug settings logged to console:\n\n' + (Object.keys(jqhtml.debug).length > 0 ? settings : 'No debug settings active'));
3459
- break;
3460
- }
3461
- }
3462
- }
3463
- DebugOverlay.instance = null;
3464
- // Simplified global convenience functions that use static methods
3465
- function showDebugOverlay(options) {
3466
- return DebugOverlay.show(options);
3467
- }
3468
- function hideDebugOverlay() {
3469
- DebugOverlay.hide();
3470
- }
3471
- // Auto-initialize if debug query parameter is present
3472
- if (typeof window !== 'undefined') {
3473
- const urlParams = new URLSearchParams(window.location.search);
3474
- if (urlParams.get('debug') === 'true' || urlParams.get('jqhtml-debug') === 'true') {
3475
- document.addEventListener('DOMContentLoaded', () => {
3476
- DebugOverlay.show();
3477
- });
3478
- }
3479
- }
3480
-
3481
3206
  /**
3482
3207
  * JQHTML v2 jQuery Plugin
3483
3208
  *
@@ -3674,47 +3399,6 @@ function init_jquery_plugin(jQuery) {
3674
3399
  // Return the jQuery element to enable chaining (fluent interface pattern)
3675
3400
  return targetElement;
3676
3401
  };
3677
- // Store original jQuery methods for overriding
3678
- const _jqhtml_jquery_overrides = {};
3679
- // EXPERIMENTAL: Override DOM manipulation methods to support component instances as arguments
3680
- // and to trigger ready() when components are added to the DOM
3681
- // NOTE: This feature needs thorough testing in production scenarios
3682
- const dom_insertion_methods = ['append', 'prepend', 'before', 'after', 'replaceWith'];
3683
- for (const fnname of dom_insertion_methods) {
3684
- _jqhtml_jquery_overrides[fnname] = jQuery.fn[fnname];
3685
- jQuery.fn[fnname] = function (...args) {
3686
- // Resolve all component instances into jQuery elements
3687
- const resolvedArgs = args.map(arg => {
3688
- if (arg && typeof arg === 'object' && arg instanceof Jqhtml_Component) {
3689
- return arg.$;
3690
- }
3691
- return arg;
3692
- });
3693
- // Make a list of all jQuery elements in the arguments
3694
- const $elements = resolvedArgs.filter((arg) => arg instanceof jQuery);
3695
- // Call the original jQuery method with resolved arguments
3696
- const ret = _jqhtml_jquery_overrides[fnname].apply(this, resolvedArgs);
3697
- // For each jQuery element that is now in the DOM and hasn't triggered ready yet,
3698
- // find any uninitialized components and boot them
3699
- for (const $e of $elements) {
3700
- // Check if element is in the DOM
3701
- if ($e.closest('html').length > 0) {
3702
- // Find any components that haven't been initialized yet
3703
- $e.find('.Component').addBack('.Component').each(function () {
3704
- const $comp = jQuery(this);
3705
- const component = $comp.data('_component');
3706
- // If component exists and hasn't been booted yet, boot it
3707
- if (component && !component._ready_state) {
3708
- component._boot();
3709
- }
3710
- });
3711
- }
3712
- }
3713
- return ret;
3714
- };
3715
- }
3716
- // Note: Component destruction is handled automatically by MutationObserver
3717
- // in lifecycle-manager.ts. No jQuery method overrides needed for cleanup.
3718
3402
  /**
3719
3403
  * shallowFind - Find nearest descendants matching selector
3720
3404
  *
@@ -4376,16 +4060,17 @@ function init(jQuery) {
4376
4060
  }
4377
4061
  }
4378
4062
  // Version - will be replaced during build with actual version from package.json
4379
- const version = '2.3.4';
4063
+ const version = '2.3.9';
4380
4064
  // Default export with all functionality
4381
4065
  const jqhtml = {
4382
4066
  // Core
4383
4067
  Jqhtml_Component,
4384
4068
  LifecycleManager,
4385
4069
  // Registry
4070
+ register,
4386
4071
  register_component,
4387
- get_component_class,
4388
4072
  register_template,
4073
+ get_component_class,
4389
4074
  get_template,
4390
4075
  get_template_by_class,
4391
4076
  create_component,
@@ -4400,6 +4085,8 @@ const jqhtml = {
4400
4085
  escape_html,
4401
4086
  // Version property - internal
4402
4087
  __version: version,
4088
+ // state facts
4089
+ tombstone: 'pepperoni and cheese',
4403
4090
  // Debug settings
4404
4091
  debug: {
4405
4092
  enabled: false,
@@ -4426,15 +4113,6 @@ const jqhtml = {
4426
4113
  clearDebugSettings() {
4427
4114
  this.debug = {};
4428
4115
  },
4429
- // Debug overlay methods
4430
- showDebugOverlay(options) {
4431
- return DebugOverlay.show(options);
4432
- },
4433
- hideDebugOverlay() {
4434
- return DebugOverlay.hide();
4435
- },
4436
- // Export DebugOverlay class for direct access
4437
- DebugOverlay,
4438
4116
  // Install globals function
4439
4117
  installGlobals() {
4440
4118
  if (typeof window !== 'undefined') {
@@ -4488,5 +4166,5 @@ if (typeof window !== 'undefined' && !window.jqhtml) {
4488
4166
  }
4489
4167
  }
4490
4168
 
4491
- export { DebugOverlay, Jqhtml_Component, LifecycleManager as Jqhtml_LifecycleManager, Jqhtml_Local_Storage, LifecycleManager, Load_Coordinator, applyDebugDelay, boot, create_component, jqhtml as default, devWarn, escape_html, extract_slots, get_component_class, get_component_names, get_registered_templates, get_template, get_template_by_class, handleComponentError, has_component, hideDebugOverlay, init, init_jquery_plugin, isSequentialProcessing, list_components, logDataChange, logDispatch, logInstruction, logLifecycle, process_instructions, register_component, register_template, render_template, showDebugOverlay, version };
4169
+ export { Jqhtml_Component, LifecycleManager as Jqhtml_LifecycleManager, Jqhtml_Local_Storage, LifecycleManager, Load_Coordinator, applyDebugDelay, boot, create_component, jqhtml as default, devWarn, escape_html, extract_slots, 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_component, register_template, render_template, version };
4492
4170
  //# sourceMappingURL=jqhtml-core.esm.js.map