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