bitwrench 2.0.16 → 2.0.17

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.
Files changed (42) hide show
  1. package/dist/bitwrench-bccl.cjs.js +6 -2
  2. package/dist/bitwrench-bccl.cjs.min.js +3 -3
  3. package/dist/bitwrench-bccl.esm.js +6 -2
  4. package/dist/bitwrench-bccl.esm.min.js +3 -3
  5. package/dist/bitwrench-bccl.umd.js +6 -2
  6. package/dist/bitwrench-bccl.umd.min.js +2 -2
  7. package/dist/bitwrench-code-edit.cjs.js +1 -1
  8. package/dist/bitwrench-code-edit.cjs.min.js +1 -1
  9. package/dist/bitwrench-code-edit.es5.js +1 -1
  10. package/dist/bitwrench-code-edit.es5.min.js +1 -1
  11. package/dist/bitwrench-code-edit.esm.js +1 -1
  12. package/dist/bitwrench-code-edit.esm.min.js +1 -1
  13. package/dist/bitwrench-code-edit.umd.js +1 -1
  14. package/dist/bitwrench-code-edit.umd.min.js +1 -1
  15. package/dist/bitwrench-lean.cjs.js +506 -154
  16. package/dist/bitwrench-lean.cjs.min.js +7 -7
  17. package/dist/bitwrench-lean.es5.js +517 -155
  18. package/dist/bitwrench-lean.es5.min.js +5 -5
  19. package/dist/bitwrench-lean.esm.js +505 -154
  20. package/dist/bitwrench-lean.esm.min.js +6 -6
  21. package/dist/bitwrench-lean.umd.js +506 -154
  22. package/dist/bitwrench-lean.umd.min.js +7 -7
  23. package/dist/bitwrench.cjs.js +511 -155
  24. package/dist/bitwrench.cjs.min.js +8 -8
  25. package/dist/bitwrench.es5.js +525 -156
  26. package/dist/bitwrench.es5.min.js +6 -6
  27. package/dist/bitwrench.esm.js +510 -155
  28. package/dist/bitwrench.esm.min.js +8 -8
  29. package/dist/bitwrench.umd.js +511 -155
  30. package/dist/bitwrench.umd.min.js +8 -8
  31. package/dist/builds.json +82 -82
  32. package/dist/bwserve.cjs.js +16 -2
  33. package/dist/bwserve.esm.js +16 -2
  34. package/dist/sri.json +34 -34
  35. package/package.json +4 -2
  36. package/readme.html +1 -1
  37. package/src/bitwrench-bccl.js +5 -1
  38. package/src/bitwrench.js +502 -151
  39. package/src/bwserve/index.js +12 -1
  40. package/src/bwserve/shell.js +3 -0
  41. package/src/cli/layout-default.js +47 -32
  42. package/src/version.js +3 -3
@@ -1,24 +1,25 @@
1
- /*! bitwrench-lean v2.0.16 | BSD-2-Clause | https://deftio.github.com/bitwrench/pages */
1
+ /*! bitwrench-lean v2.0.17 | BSD-2-Clause | https://deftio.github.com/bitwrench/pages */
2
2
  (function (global, factory) {
3
3
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
4
4
  typeof define === 'function' && define.amd ? define(factory) :
5
5
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.bw = factory());
6
6
  })(this, (function () { 'use strict';
7
7
 
8
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
8
9
  /**
9
10
  * Auto-generated version file from package.json
10
11
  * DO NOT EDIT DIRECTLY - Use npm run generate-version
11
12
  */
12
13
 
13
14
  const VERSION_INFO = {
14
- version: '2.0.16',
15
+ version: '2.0.17',
15
16
  name: 'bitwrench',
16
17
  description: 'A library for javascript UI functions.',
17
18
  license: 'BSD-2-Clause',
18
19
  homepage: 'https://deftio.github.com/bitwrench/pages',
19
20
  repository: 'git+https://github.com/deftio/bitwrench.git',
20
21
  author: 'manu a. chatterjee <deftio@deftio.com> (https://deftio.com/)',
21
- buildDate: '2026-03-12T08:05:52.043Z'
22
+ buildDate: '2026-03-13T23:15:10.823Z'
22
23
  };
23
24
 
24
25
  /**
@@ -3349,7 +3350,7 @@
3349
3350
  __monkey_patch_is_nodejs__: {
3350
3351
  _value: 'ignore',
3351
3352
  set: function(x) {
3352
- this._value = (typeof x === 'boolean') ? x : 'ignore';
3353
+ this._value = _is(x, 'boolean') ? x : 'ignore';
3353
3354
  },
3354
3355
  get: function() {
3355
3356
  return this._value;
@@ -3397,6 +3398,67 @@
3397
3398
  configurable: true
3398
3399
  });
3399
3400
 
3401
+ // ── Internal aliases ─────────────────────────────────────────────────────
3402
+ // Short names for frequently-used builtins and internal methods.
3403
+ // Same pattern as v1 (_to = bw.typeOf, etc.).
3404
+ //
3405
+ // Why: Terser can't shorten global property chains (console.warn,
3406
+ // Object.prototype.hasOwnProperty, Array.isArray, document.createElement)
3407
+ // because it can't prove they're side-effect-free. We can, so we alias
3408
+ // them here. Each alias saves bytes in the minified output, and the short
3409
+ // names also reduce visual noise in the hot paths (binding pipeline,
3410
+ // createDOM, etc.).
3411
+ //
3412
+ // Alias Target Sites
3413
+ // ───────── ────────────────────────────────────── ─────
3414
+ // _hop Object.prototype.hasOwnProperty 15
3415
+ // _isA Array.isArray 25
3416
+ // _keys Object.keys 7
3417
+ // _to bw.typeOf (type string) 26
3418
+ // _is type check boolean: _is(x,'string') ~50
3419
+ // _cw console.warn 8
3420
+ // _cl console.log 11
3421
+ // _ce console.error 4
3422
+ // _chp ComponentHandle.prototype 28 (defined after constructor)
3423
+ //
3424
+ // Note: document.createElement etc. are NOT aliased because they require
3425
+ // `this === document` and .bind() would add overhead on every call.
3426
+ // Console aliases use thin wrappers (not direct refs) so test monkey-
3427
+ // patching of console.warn/log/error continues to work.
3428
+ //
3429
+ // `typeof x` for UNDECLARED globals (window, document, process, require,
3430
+ // EventSource, navigator, Promise, __filename, import.meta) MUST stay as
3431
+ // raw `typeof` — calling _to(x) when x doesn't exist throws ReferenceError.
3432
+ //
3433
+ // ── v1 functional type helpers (kept for reference, not currently used) ──
3434
+ // _toa(x, type, trueVal, falseVal) — bw.typeAssign:
3435
+ // returns trueVal if _to(x)===type, else falseVal.
3436
+ // Replaces: (typeof x === 'string') ? A : B → _toa(x,'string',A,B)
3437
+ // _toc(x, type, trueVal, falseVal) — bw.typeConvert:
3438
+ // same as _toa but if trueVal/falseVal are functions, calls them with x.
3439
+ // Replaces: typeof x === 'string' ? fn(x) : default → _toc(x,'string',fn,default)
3440
+ // Uncomment if pattern frequency justifies them:
3441
+ // var _toa = function(x, t, y, n) { return _to(x) === t ? y : n; };
3442
+ // var _toc = function(x, t, y, n) { var r = _to(x)===t; return r ? (_to(y)==='function'?y(x):y) : (_to(n)==='function'?n(x):n); };
3443
+ // ─────────────────────────────────────────────────────────────────────────
3444
+ var _hop = Object.prototype.hasOwnProperty;
3445
+ var _isA = Array.isArray;
3446
+ var _keys = Object.keys;
3447
+ var _to = typeOf; // imported from bitwrench-utils.js
3448
+ var _is = function(x, t) { var r = _to(x); return r === t || r.toLowerCase() === t; };
3449
+ // Console aliases use thin wrappers (not direct references) so that test
3450
+ // code can monkey-patch console.warn/log/error and the patches take effect.
3451
+ var _cw = function() { console.warn.apply(console, arguments); };
3452
+ var _cl = function() { console.log.apply(console, arguments); };
3453
+ var _ce = function() { console.error.apply(console, arguments); };
3454
+
3455
+ /**
3456
+ * Debug flag. When true, emits console.warn for silent binding failures
3457
+ * (missing paths, null refs, auto-created intermediate objects).
3458
+ * @type {boolean}
3459
+ */
3460
+ bw.debug = false;
3461
+
3400
3462
  /**
3401
3463
  * Lazy-resolve Node.js `fs` module.
3402
3464
  * Tries require('fs') first (available in CJS/UMD Node.js builds),
@@ -3544,7 +3606,7 @@
3544
3606
  */
3545
3607
  bw._el = function(id) {
3546
3608
  // Pass-through for DOM elements
3547
- if (typeof id !== 'string') return id || null;
3609
+ if (!_is(id, 'string')) return id || null;
3548
3610
  if (!id) return null;
3549
3611
  if (!bw._isBrowser) return null;
3550
3612
 
@@ -3640,7 +3702,7 @@
3640
3702
  * // => '&lt;b&gt;Hello&lt;&#x2F;b&gt; &amp; &quot;world&quot;'
3641
3703
  */
3642
3704
  bw.escapeHTML = function(str) {
3643
- if (typeof str !== 'string') return '';
3705
+ if (!_is(str, 'string')) return '';
3644
3706
 
3645
3707
  const escapeMap = {
3646
3708
  '&': '&amp;',
@@ -3713,7 +3775,7 @@
3713
3775
  }
3714
3776
 
3715
3777
  // Handle arrays of TACOs
3716
- if (Array.isArray(taco)) {
3778
+ if (_isA(taco)) {
3717
3779
  return taco.map(t => bw.html(t, options)).join('');
3718
3780
  }
3719
3781
 
@@ -3736,15 +3798,15 @@
3736
3798
  if (taco && taco._bwEach && options.state) {
3737
3799
  var eachExpr = taco.expr.replace(/^\$\{|\}$/g, '');
3738
3800
  var arr = bw._evaluatePath(options.state, eachExpr);
3739
- if (!Array.isArray(arr)) return '';
3801
+ if (!_isA(arr)) return '';
3740
3802
  return arr.map(function(item, idx) { return bw.html(taco.factory(item, idx), options); }).join('');
3741
3803
  }
3742
3804
 
3743
3805
  // Handle primitives and non-TACO objects
3744
- if (typeof taco !== 'object' || !taco.t) {
3806
+ if (!_is(taco, 'object') || !taco.t) {
3745
3807
  var str = options.raw ? String(taco) : bw.escapeHTML(String(taco));
3746
3808
  // Resolve template bindings if state provided
3747
- if (options.state && typeof str === 'string' && str.indexOf('${') >= 0) {
3809
+ if (options.state && _is(str, 'string') && str.indexOf('${') >= 0) {
3748
3810
  str = bw._resolveTemplate(str, options.state, !!options.compile);
3749
3811
  }
3750
3812
  return str;
@@ -3764,10 +3826,18 @@
3764
3826
  // Skip null, undefined, false
3765
3827
  if (value == null || value === false) continue;
3766
3828
 
3767
- // Skip event handlers (they're for DOM only)
3768
- if (key.startsWith('on')) continue;
3829
+ // Serialize event handlers via funcRegister
3830
+ if (key.startsWith('on')) {
3831
+ if (_is(value, 'function')) {
3832
+ var fnId = bw.funcRegister(value);
3833
+ attrStr += ' ' + key + '="' + bw.funcGetDispatchStr(fnId, 'event') + '"';
3834
+ } else if (_is(value, 'string')) {
3835
+ attrStr += ' ' + key + '="' + bw.escapeHTML(value) + '"';
3836
+ }
3837
+ continue;
3838
+ }
3769
3839
 
3770
- if (key === 'style' && typeof value === 'object') {
3840
+ if (key === 'style' && _is(value, 'object')) {
3771
3841
  // Convert style object to string
3772
3842
  const styleStr = Object.entries(value)
3773
3843
  .filter(([, v]) => v != null)
@@ -3778,7 +3848,7 @@
3778
3848
  }
3779
3849
  } else if (key === 'class') {
3780
3850
  // Handle class as array or string
3781
- const classStr = Array.isArray(value) ? value.filter(Boolean).join(' ') : String(value);
3851
+ const classStr = _isA(value) ? value.filter(Boolean).join(' ') : String(value);
3782
3852
  if (classStr) {
3783
3853
  attrStr += ` class="${bw.escapeHTML(classStr)}"`;
3784
3854
  }
@@ -3814,13 +3884,184 @@
3814
3884
  // Process content recursively
3815
3885
  let contentStr = content != null ? bw.html(content, options) : '';
3816
3886
  // Resolve template bindings in content if state provided
3817
- if (options.state && typeof contentStr === 'string' && contentStr.indexOf('${') >= 0) {
3887
+ if (options.state && _is(contentStr, 'string') && contentStr.indexOf('${') >= 0) {
3818
3888
  contentStr = bw._resolveTemplate(contentStr, options.state, !!options.compile);
3819
3889
  }
3820
3890
 
3821
3891
  return `<${tag}${attrStr}>${contentStr}</${tag}>`;
3822
3892
  };
3823
3893
 
3894
+ /**
3895
+ * Generate a complete, self-contained HTML document from TACO content.
3896
+ *
3897
+ * Produces a full `<!DOCTYPE html>` page with configurable runtime injection,
3898
+ * func registry emission (so serialized event handlers work), optional theme,
3899
+ * and extra head elements. Designed for static site generation, offline/airgapped
3900
+ * use, and the "static site that isn't static" workflow.
3901
+ *
3902
+ * @param {Object} [opts={}] - Page options
3903
+ * @param {Object|string|Array} [opts.body=''] - Body content: TACO, string, or array
3904
+ * @param {string} [opts.title='bitwrench'] - Page title
3905
+ * @param {Object} [opts.state] - State for ${expr} resolution in bw.html()
3906
+ * @param {string} [opts.runtime='shim'] - Runtime level: 'inline'|'cdn'|'shim'|'none'
3907
+ * @param {string} [opts.css=''] - Additional CSS for <style> block
3908
+ * @param {string|Object} [opts.theme=null] - Theme preset name or config object
3909
+ * @param {Array} [opts.head=[]] - Extra TACO elements rendered into <head>
3910
+ * @param {string} [opts.favicon=''] - Favicon URL
3911
+ * @param {string} [opts.lang='en'] - HTML lang attribute
3912
+ * @returns {string} Complete HTML document string
3913
+ * @category DOM Generation
3914
+ * @see bw.html
3915
+ * @example
3916
+ * bw.htmlPage({
3917
+ * title: 'My App',
3918
+ * body: { t: 'h1', c: 'Hello World' },
3919
+ * runtime: 'shim'
3920
+ * })
3921
+ */
3922
+ bw.htmlPage = function(opts) {
3923
+ opts = opts || {};
3924
+ var title = opts.title || 'bitwrench';
3925
+ var body = opts.body || '';
3926
+ var state = opts.state || undefined;
3927
+ var runtime = opts.runtime || 'shim';
3928
+ var css = opts.css || '';
3929
+ var theme = opts.theme || null;
3930
+ var headExtra = opts.head || [];
3931
+ var favicon = opts.favicon || '';
3932
+ var lang = opts.lang || 'en';
3933
+
3934
+ // Snapshot funcRegistry counter before rendering
3935
+ var fnCounterBefore = bw._fnIDCounter;
3936
+
3937
+ // Render body content
3938
+ var bodyHTML = '';
3939
+ if (_is(body, 'string')) {
3940
+ bodyHTML = body;
3941
+ } else {
3942
+ var htmlOpts = {};
3943
+ if (state) htmlOpts.state = state;
3944
+ bodyHTML = bw.html(body, htmlOpts);
3945
+ }
3946
+
3947
+ // Collect functions registered during this render
3948
+ var fnCounterAfter = bw._fnIDCounter;
3949
+ var registryEntries = '';
3950
+ for (var i = fnCounterBefore; i < fnCounterAfter; i++) {
3951
+ var fnKey = 'bw_fn_' + i;
3952
+ if (bw._fnRegistry[fnKey]) {
3953
+ registryEntries += 'bw._fnRegistry[\'' + fnKey + '\']=' +
3954
+ bw._fnRegistry[fnKey].toString() + ';\n';
3955
+ }
3956
+ }
3957
+
3958
+ // Build runtime script for <head>
3959
+ var runtimeHead = '';
3960
+ if (runtime === 'inline') {
3961
+ // Read UMD bundle synchronously if in Node.js
3962
+ var umdSource = null;
3963
+ if (bw._isNode) {
3964
+ try {
3965
+ var fs = (typeof require === 'function') ? require('fs') : null;
3966
+ var pathMod = (typeof require === 'function') ? require('path') : null;
3967
+ if (fs && pathMod) {
3968
+ // Resolve dist/ relative to this source file
3969
+ var srcDir = '';
3970
+ try { srcDir = pathMod.dirname((typeof __filename !== 'undefined') ? __filename : ''); }
3971
+ catch(e2) { /* ESM: __filename not available */ }
3972
+ if (!srcDir && typeof ({ url: (typeof document === 'undefined' && typeof location === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : typeof document === 'undefined' ? location.href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('bitwrench-lean.umd.js', document.baseURI).href)) }) !== 'undefined' && (typeof document === 'undefined' && typeof location === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : typeof document === 'undefined' ? location.href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('bitwrench-lean.umd.js', document.baseURI).href))) {
3973
+ var url = (typeof require === 'function') ? require('url') : null;
3974
+ if (url && url.fileURLToPath) srcDir = pathMod.dirname(url.fileURLToPath((typeof document === 'undefined' && typeof location === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : typeof document === 'undefined' ? location.href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('bitwrench-lean.umd.js', document.baseURI).href))));
3975
+ }
3976
+ if (srcDir) {
3977
+ var distPath = pathMod.resolve(srcDir, '../dist/bitwrench.umd.min.js');
3978
+ umdSource = fs.readFileSync(distPath, 'utf8');
3979
+ }
3980
+ }
3981
+ } catch(e) { /* fall through */ }
3982
+ }
3983
+ if (umdSource) {
3984
+ runtimeHead = '<script>' + umdSource + '</script>';
3985
+ } else {
3986
+ // Fallback to shim in browser or if dist not available
3987
+ runtimeHead = '<script>' + bw._FUNC_REGISTRY_SHIM + '</script>';
3988
+ }
3989
+ } else if (runtime === 'cdn') {
3990
+ runtimeHead = '<script src="https://cdn.jsdelivr.net/npm/bitwrench@2/dist/bitwrench.umd.min.js"></script>';
3991
+ } else if (runtime === 'shim') {
3992
+ runtimeHead = '<script>' + bw._FUNC_REGISTRY_SHIM + '</script>';
3993
+ }
3994
+ // runtime === 'none' → empty
3995
+
3996
+ // Theme CSS
3997
+ var themeCSS = '';
3998
+ if (theme) {
3999
+ var themeConfig = _is(theme, 'string')
4000
+ ? (THEME_PRESETS[theme.toLowerCase()] || null)
4001
+ : theme;
4002
+ if (themeConfig) {
4003
+ var themeResult = bw.generateTheme('', Object.assign({}, themeConfig, { inject: false }));
4004
+ themeCSS = themeResult.css;
4005
+ }
4006
+ }
4007
+
4008
+ // Extra <head> elements
4009
+ var headHTML = '';
4010
+ if (_isA(headExtra) && headExtra.length > 0) {
4011
+ headHTML = headExtra.map(function(el) { return bw.html(el); }).join('\n');
4012
+ }
4013
+
4014
+ // Favicon
4015
+ var faviconTag = '';
4016
+ if (favicon) {
4017
+ var safeFavicon = favicon.replace(/[&<>"']/g, function(c) {
4018
+ return ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' })[c];
4019
+ });
4020
+ faviconTag = '<link rel="icon" href="' + safeFavicon + '">';
4021
+ }
4022
+
4023
+ // Escaped title
4024
+ var safeTitle = bw.escapeHTML(title);
4025
+
4026
+ // Combine all CSS
4027
+ var allCSS = (themeCSS ? themeCSS + '\n' : '') + css;
4028
+
4029
+ // Body-end script: registry entries + optional loadDefaultStyles
4030
+ var bodyEndScript = '';
4031
+ var bodyEndParts = [];
4032
+ if (registryEntries) {
4033
+ bodyEndParts.push(registryEntries);
4034
+ }
4035
+ if (runtime === 'inline' || runtime === 'cdn') {
4036
+ bodyEndParts.push('if(typeof bw!=="undefined"){bw.loadDefaultStyles();}');
4037
+ }
4038
+ if (bodyEndParts.length > 0) {
4039
+ bodyEndScript = '<script>\n' + bodyEndParts.join('\n') + '\n</script>';
4040
+ }
4041
+
4042
+ // Assemble document
4043
+ var parts = [
4044
+ '<!DOCTYPE html>',
4045
+ '<html lang="' + lang + '">',
4046
+ '<head>',
4047
+ '<meta charset="UTF-8">',
4048
+ '<meta name="viewport" content="width=device-width, initial-scale=1">'
4049
+ ];
4050
+ parts.push('<title>' + safeTitle + '</title>');
4051
+ if (faviconTag) parts.push(faviconTag);
4052
+ if (runtimeHead) parts.push(runtimeHead);
4053
+ if (headHTML) parts.push(headHTML);
4054
+ if (allCSS) parts.push('<style>' + allCSS + '</style>');
4055
+ parts.push('</head>');
4056
+ parts.push('<body>');
4057
+ parts.push(bodyHTML);
4058
+ if (bodyEndScript) parts.push(bodyEndScript);
4059
+ parts.push('</body>');
4060
+ parts.push('</html>');
4061
+
4062
+ return parts.join('\n');
4063
+ };
4064
+
3824
4065
  /**
3825
4066
  * Create a live DOM element from a TACO object (browser only).
3826
4067
  *
@@ -3865,7 +4106,7 @@
3865
4106
  }
3866
4107
 
3867
4108
  // Handle text nodes
3868
- if (typeof taco !== 'object' || !taco.t) {
4109
+ if (!_is(taco, 'object') || !taco.t) {
3869
4110
  return document.createTextNode(String(taco));
3870
4111
  }
3871
4112
 
@@ -3878,16 +4119,16 @@
3878
4119
  for (const [key, value] of Object.entries(attrs)) {
3879
4120
  if (value == null || value === false) continue;
3880
4121
 
3881
- if (key === 'style' && typeof value === 'object') {
4122
+ if (key === 'style' && _is(value, 'object')) {
3882
4123
  // Apply styles directly
3883
4124
  Object.assign(el.style, value);
3884
4125
  } else if (key === 'class') {
3885
4126
  // Handle class as array or string
3886
- const classStr = Array.isArray(value) ? value.filter(Boolean).join(' ') : String(value);
4127
+ const classStr = _isA(value) ? value.filter(Boolean).join(' ') : String(value);
3887
4128
  if (classStr) {
3888
4129
  el.className = classStr;
3889
4130
  }
3890
- } else if (key.startsWith('on') && typeof value === 'function') {
4131
+ } else if (key.startsWith('on') && _is(value, 'function')) {
3891
4132
  // Event handlers
3892
4133
  const eventName = key.slice(2).toLowerCase();
3893
4134
  el.addEventListener(eventName, value);
@@ -3907,7 +4148,7 @@
3907
4148
  // Children with data-bw_id or id attributes get local refs on the parent,
3908
4149
  // so o.render functions can access them without any DOM lookup.
3909
4150
  if (content != null) {
3910
- if (Array.isArray(content)) {
4151
+ if (_isA(content)) {
3911
4152
  content.forEach(child => {
3912
4153
  if (child != null) {
3913
4154
  // Handle ComponentHandle in content arrays (Level 2 children)
@@ -3927,20 +4168,20 @@
3927
4168
  if (childEl._bw_refs) {
3928
4169
  if (!el._bw_refs) el._bw_refs = {};
3929
4170
  for (var rk in childEl._bw_refs) {
3930
- if (Object.prototype.hasOwnProperty.call(childEl._bw_refs, rk)) {
4171
+ if (_hop.call(childEl._bw_refs, rk)) {
3931
4172
  el._bw_refs[rk] = childEl._bw_refs[rk];
3932
4173
  }
3933
4174
  }
3934
4175
  }
3935
4176
  }
3936
4177
  });
3937
- } else if (typeof content === 'object' && content.__bw_raw) {
4178
+ } else if (_is(content, 'object') && content.__bw_raw) {
3938
4179
  // Raw HTML content — inject via innerHTML
3939
4180
  el.innerHTML = content.v;
3940
4181
  } else if (content._bwComponent === true) {
3941
4182
  // Single ComponentHandle as content
3942
4183
  content.mount(el);
3943
- } else if (typeof content === 'object' && content.t) {
4184
+ } else if (_is(content, 'object') && content.t) {
3944
4185
  var childEl = bw.createDOM(content, options);
3945
4186
  el.appendChild(childEl);
3946
4187
  var childBwId = content.a ? (content.a['data-bw_id'] || content.a.id) : null;
@@ -3951,7 +4192,7 @@
3951
4192
  if (childEl._bw_refs) {
3952
4193
  if (!el._bw_refs) el._bw_refs = {};
3953
4194
  for (var rk in childEl._bw_refs) {
3954
- if (Object.prototype.hasOwnProperty.call(childEl._bw_refs, rk)) {
4195
+ if (_hop.call(childEl._bw_refs, rk)) {
3955
4196
  el._bw_refs[rk] = childEl._bw_refs[rk];
3956
4197
  }
3957
4198
  }
@@ -3984,7 +4225,7 @@
3984
4225
  el._bw_render = opts.render;
3985
4226
 
3986
4227
  if (opts.mounted) {
3987
- console.warn('bw.createDOM: o.render and o.mounted are mutually exclusive. o.render wins.');
4228
+ _cw('bw.createDOM: o.render and o.mounted are mutually exclusive. o.render wins.');
3988
4229
  }
3989
4230
 
3990
4231
  // Queue initial render (same timing as mounted)
@@ -4057,7 +4298,7 @@
4057
4298
  const targetEl = bw._el(target);
4058
4299
 
4059
4300
  if (!targetEl) {
4060
- console.error('bw.DOM: Target element not found:', target);
4301
+ _ce('bw.DOM: Target element not found:', target);
4061
4302
  return null;
4062
4303
  }
4063
4304
 
@@ -4097,7 +4338,7 @@
4097
4338
  targetEl.appendChild(taco.element);
4098
4339
  }
4099
4340
  // Handle arrays
4100
- else if (Array.isArray(taco)) {
4341
+ else if (_isA(taco)) {
4101
4342
  taco.forEach(t => {
4102
4343
  if (t != null) {
4103
4344
  if (t._bwComponent === true) {
@@ -4133,7 +4374,7 @@
4133
4374
  bw.compileProps = function(handle, props = {}) {
4134
4375
  const compiledProps = {};
4135
4376
 
4136
- Object.keys(props).forEach(key => {
4377
+ _keys(props).forEach(key => {
4137
4378
  // Create getter/setter for each prop
4138
4379
  Object.defineProperty(compiledProps, key, {
4139
4380
  get() {
@@ -4451,17 +4692,17 @@
4451
4692
  if (attr) {
4452
4693
  // Patch an attribute
4453
4694
  el.setAttribute(attr, String(content));
4454
- } else if (Array.isArray(content)) {
4695
+ } else if (_isA(content)) {
4455
4696
  // Patch with array of children (strings and/or TACOs)
4456
4697
  el.innerHTML = '';
4457
4698
  content.forEach(function(item) {
4458
- if (typeof item === 'string' || typeof item === 'number') {
4699
+ if (_is(item, 'string') || _is(item, 'number')) {
4459
4700
  el.appendChild(document.createTextNode(String(item)));
4460
4701
  } else if (item && item.t) {
4461
4702
  el.appendChild(bw.createDOM(item));
4462
4703
  }
4463
4704
  });
4464
- } else if (typeof content === 'object' && content !== null && content.t) {
4705
+ } else if (_is(content, 'object') && content.t) {
4465
4706
  // Patch with a TACO — replace children
4466
4707
  el.innerHTML = '';
4467
4708
  el.appendChild(bw.createDOM(content));
@@ -4492,7 +4733,7 @@
4492
4733
  bw.patchAll = function(patches) {
4493
4734
  var results = {};
4494
4735
  for (var id in patches) {
4495
- if (Object.prototype.hasOwnProperty.call(patches, id)) {
4736
+ if (_hop.call(patches, id)) {
4496
4737
  results[id] = bw.patch(id, patches[id]);
4497
4738
  }
4498
4739
  }
@@ -4589,7 +4830,7 @@
4589
4830
  snapshot[i].handler(detail);
4590
4831
  called++;
4591
4832
  } catch (err) {
4592
- console.warn('bw.pub: subscriber error on topic "' + topic + '":', err);
4833
+ _cw('bw.pub: subscriber error on topic "' + topic + '":', err);
4593
4834
  }
4594
4835
  }
4595
4836
  return called;
@@ -4685,8 +4926,8 @@
4685
4926
  * @see bw.funcGetDispatchStr
4686
4927
  */
4687
4928
  bw.funcRegister = function(fn, name) {
4688
- if (typeof fn !== 'function') return '';
4689
- var fnID = (typeof name === 'string' && name.length > 0) ? name : ('bw_fn_' + bw._fnIDCounter++);
4929
+ if (!_is(fn, 'function')) return '';
4930
+ var fnID = (_is(name, 'string') && name.length > 0) ? name : ('bw_fn_' + bw._fnIDCounter++);
4690
4931
  bw._fnRegistry[fnID] = fn;
4691
4932
  return fnID;
4692
4933
  };
@@ -4705,7 +4946,7 @@
4705
4946
  bw.funcGetById = function(name, errFn) {
4706
4947
  name = String(name);
4707
4948
  if (name in bw._fnRegistry) return bw._fnRegistry[name];
4708
- return (typeof errFn === 'function') ? errFn : function() { console.warn('bw.funcGetById: unregistered fn "' + name + '"'); };
4949
+ return _is(errFn, 'function') ? errFn : function() { _cw('bw.funcGetById: unregistered fn "' + name + '"'); };
4709
4950
  };
4710
4951
 
4711
4952
  /**
@@ -4746,13 +4987,30 @@
4746
4987
  bw.funcGetRegistry = function() {
4747
4988
  var copy = {};
4748
4989
  for (var k in bw._fnRegistry) {
4749
- if (Object.prototype.hasOwnProperty.call(bw._fnRegistry, k)) {
4990
+ if (_hop.call(bw._fnRegistry, k)) {
4750
4991
  copy[k] = bw._fnRegistry[k];
4751
4992
  }
4752
4993
  }
4753
4994
  return copy;
4754
4995
  };
4755
4996
 
4997
+ /**
4998
+ * Minimal runtime shim for funcRegister dispatch in static HTML.
4999
+ * When embedded in a `<script>` tag, provides just enough infrastructure
5000
+ * for `bw.funcGetById()` calls to resolve. The actual function bodies
5001
+ * are emitted separately as `bw._fnRegistry['bw_fn_X'] = ...;` assignments.
5002
+ * @type {string}
5003
+ * @category Function Registry
5004
+ */
5005
+ bw._FUNC_REGISTRY_SHIM = '(function(){var bw=window.bw||(window.bw={});' +
5006
+ 'if(!bw._fnRegistry)bw._fnRegistry={};' +
5007
+ 'bw.funcGetById=function(n){return bw._fnRegistry[n]||function(){' +
5008
+ 'console.warn("bw: unregistered fn "+n)};};' +
5009
+ 'bw.funcRegister=function(fn,name){' +
5010
+ 'var id=name||("bw_fn_"+(bw._fnIDCounter=(bw._fnIDCounter||0)+1));' +
5011
+ 'bw._fnRegistry[id]=fn;return id;};' +
5012
+ 'window.bw=bw;})();';
5013
+
4756
5014
  // ===================================================================================
4757
5015
  // Template Binding Utilities
4758
5016
  // ===================================================================================
@@ -4780,7 +5038,10 @@
4780
5038
  var parts = path.split('.');
4781
5039
  var val = state;
4782
5040
  for (var i = 0; i < parts.length; i++) {
4783
- if (val == null) return '';
5041
+ if (val == null) {
5042
+ if (bw.debug) _cw('bw.debug: _evaluatePath — null at key "' + parts[i] + '" in path "' + path + '"');
5043
+ return '';
5044
+ }
4784
5045
  val = val[parts[i]];
4785
5046
  }
4786
5047
  return (val == null) ? '' : val;
@@ -4800,7 +5061,7 @@
4800
5061
  */
4801
5062
  bw._compiledExprs = {};
4802
5063
  bw._resolveTemplate = function(str, state, compile) {
4803
- if (typeof str !== 'string' || str.indexOf('${') < 0) return str;
5064
+ if (!_is(str, 'string') || str.indexOf('${') < 0) return str;
4804
5065
  var bindings = bw._parseBindings(str);
4805
5066
  if (bindings.length === 0) return str;
4806
5067
 
@@ -4822,6 +5083,7 @@
4822
5083
  try {
4823
5084
  val = bw._compiledExprs[b.expr](state);
4824
5085
  } catch (e) {
5086
+ if (bw.debug) _cw('bw.debug: _resolveTemplate — Tier 2 eval failed for "${' + b.expr + '}":', e.message);
4825
5087
  val = '';
4826
5088
  }
4827
5089
  } else {
@@ -4930,7 +5192,7 @@
4930
5192
  this._state = {};
4931
5193
  if (o.state) {
4932
5194
  for (var k in o.state) {
4933
- if (Object.prototype.hasOwnProperty.call(o.state, k)) {
5195
+ if (_hop.call(o.state, k)) {
4934
5196
  this._state[k] = o.state[k];
4935
5197
  }
4936
5198
  }
@@ -4939,7 +5201,7 @@
4939
5201
  this._actions = {};
4940
5202
  if (o.actions) {
4941
5203
  for (var k2 in o.actions) {
4942
- if (Object.prototype.hasOwnProperty.call(o.actions, k2)) {
5204
+ if (_hop.call(o.actions, k2)) {
4943
5205
  this._actions[k2] = o.actions[k2];
4944
5206
  }
4945
5207
  }
@@ -4949,7 +5211,7 @@
4949
5211
  if (o.methods) {
4950
5212
  var self = this;
4951
5213
  for (var k3 in o.methods) {
4952
- if (Object.prototype.hasOwnProperty.call(o.methods, k3)) {
5214
+ if (_hop.call(o.methods, k3)) {
4953
5215
  this._methods[k3] = o.methods[k3];
4954
5216
  (function(methodName, methodFn) {
4955
5217
  self[methodName] = function() {
@@ -4982,14 +5244,23 @@
4982
5244
  this._compile = !!o.compile;
4983
5245
  this._bw_refs = {};
4984
5246
  this._refCounter = 0;
5247
+ // Child component ownership (Bug #5)
5248
+ this._children = [];
5249
+ this._parent = null;
5250
+ // Factory metadata for BCCL rebuild (Bug #6)
5251
+ this._factory = taco._bwFactory || null;
4985
5252
  }
4986
5253
 
5254
+ // Short alias for ComponentHandle.prototype (see alias block at top of file).
5255
+ // 28 method definitions × 25 chars = ~700B raw savings in minified output.
5256
+ var _chp = ComponentHandle.prototype;
5257
+
4987
5258
  // ── State Methods ──
4988
5259
 
4989
5260
  /**
4990
5261
  * Get a state value. Dot-path supported: `get('user.name')`
4991
5262
  */
4992
- ComponentHandle.prototype.get = function(key) {
5263
+ _chp.get = function(key) {
4993
5264
  return bw._evaluatePath(this._state, key);
4994
5265
  };
4995
5266
 
@@ -4999,12 +5270,13 @@
4999
5270
  * @param {*} value - New value
5000
5271
  * @param {Object} [opts] - Options. `{sync: true}` for immediate flush.
5001
5272
  */
5002
- ComponentHandle.prototype.set = function(key, value, opts) {
5273
+ _chp.set = function(key, value, opts) {
5003
5274
  // Dot-path set
5004
5275
  var parts = key.split('.');
5005
5276
  var obj = this._state;
5006
5277
  for (var i = 0; i < parts.length - 1; i++) {
5007
- if (obj[parts[i]] == null || typeof obj[parts[i]] !== 'object') {
5278
+ if (!_is(obj[parts[i]], 'object')) {
5279
+ if (bw.debug) _cw('bw.debug: set() — auto-creating intermediate "' + parts[i] + '" in path "' + key + '"');
5008
5280
  obj[parts[i]] = {};
5009
5281
  }
5010
5282
  obj = obj[parts[i]];
@@ -5024,10 +5296,10 @@
5024
5296
  /**
5025
5297
  * Get a shallow clone of the full state.
5026
5298
  */
5027
- ComponentHandle.prototype.getState = function() {
5299
+ _chp.getState = function() {
5028
5300
  var clone = {};
5029
5301
  for (var k in this._state) {
5030
- if (Object.prototype.hasOwnProperty.call(this._state, k)) {
5302
+ if (_hop.call(this._state, k)) {
5031
5303
  clone[k] = this._state[k];
5032
5304
  }
5033
5305
  }
@@ -5039,9 +5311,9 @@
5039
5311
  * @param {Object} updates - Key-value pairs to merge
5040
5312
  * @param {Object} [opts] - Options. `{sync: true}` for immediate flush.
5041
5313
  */
5042
- ComponentHandle.prototype.setState = function(updates, opts) {
5314
+ _chp.setState = function(updates, opts) {
5043
5315
  for (var k in updates) {
5044
- if (Object.prototype.hasOwnProperty.call(updates, k)) {
5316
+ if (_hop.call(updates, k)) {
5045
5317
  this._state[k] = updates[k];
5046
5318
  this._dirtyKeys[k] = true;
5047
5319
  }
@@ -5058,9 +5330,9 @@
5058
5330
  /**
5059
5331
  * Push a value onto an array in state. Clones the array.
5060
5332
  */
5061
- ComponentHandle.prototype.push = function(key, val) {
5333
+ _chp.push = function(key, val) {
5062
5334
  var arr = this.get(key);
5063
- var newArr = Array.isArray(arr) ? arr.slice() : [];
5335
+ var newArr = _isA(arr) ? arr.slice() : [];
5064
5336
  newArr.push(val);
5065
5337
  this.set(key, newArr);
5066
5338
  };
@@ -5068,9 +5340,9 @@
5068
5340
  /**
5069
5341
  * Splice an array in state. Clones the array.
5070
5342
  */
5071
- ComponentHandle.prototype.splice = function(key, start, deleteCount) {
5343
+ _chp.splice = function(key, start, deleteCount) {
5072
5344
  var arr = this.get(key);
5073
- var newArr = Array.isArray(arr) ? arr.slice() : [];
5345
+ var newArr = _isA(arr) ? arr.slice() : [];
5074
5346
  var args = [start, deleteCount].concat(Array.prototype.slice.call(arguments, 3));
5075
5347
  Array.prototype.splice.apply(newArr, args);
5076
5348
  this.set(key, newArr);
@@ -5078,7 +5350,7 @@
5078
5350
 
5079
5351
  // ── Scheduling ──
5080
5352
 
5081
- ComponentHandle.prototype._scheduleDirty = function() {
5353
+ _chp._scheduleDirty = function() {
5082
5354
  if (!this._scheduled) {
5083
5355
  this._scheduled = true;
5084
5356
  bw._dirtyComponents.push(this);
@@ -5093,17 +5365,17 @@
5093
5365
  * Creates binding descriptors with refIds for targeted DOM updates.
5094
5366
  * @private
5095
5367
  */
5096
- ComponentHandle.prototype._compileBindings = function() {
5368
+ _chp._compileBindings = function() {
5097
5369
  this._bindings = [];
5098
5370
  this._refCounter = 0;
5099
- var stateKeys = Object.keys(this._state);
5371
+ var stateKeys = _keys(this._state);
5100
5372
  var self = this;
5101
5373
 
5102
5374
  function walkTaco(taco, path) {
5103
- if (taco == null || typeof taco !== 'object' || !taco.t) return taco;
5375
+ if (!_is(taco, 'object') || !taco.t) return taco;
5104
5376
 
5105
5377
  // Check content for bindings
5106
- if (typeof taco.c === 'string' && taco.c.indexOf('${') >= 0) {
5378
+ if (_is(taco.c, 'string') && taco.c.indexOf('${') >= 0) {
5107
5379
  var refId = 'bw_ref_' + self._refCounter++;
5108
5380
  var parsed = bw._parseBindings(taco.c);
5109
5381
  var deps = [];
@@ -5125,10 +5397,10 @@
5125
5397
  // Check attributes for bindings
5126
5398
  if (taco.a) {
5127
5399
  for (var attrName in taco.a) {
5128
- if (!Object.prototype.hasOwnProperty.call(taco.a, attrName)) continue;
5400
+ if (!_hop.call(taco.a, attrName)) continue;
5129
5401
  if (attrName === 'data-bw_ref') continue;
5130
5402
  var attrVal = taco.a[attrName];
5131
- if (typeof attrVal === 'string' && attrVal.indexOf('${') >= 0) {
5403
+ if (_is(attrVal, 'string') && attrVal.indexOf('${') >= 0) {
5132
5404
  var refId2 = 'bw_ref_' + self._refCounter++;
5133
5405
  var parsed2 = bw._parseBindings(attrVal);
5134
5406
  var deps2 = [];
@@ -5154,9 +5426,27 @@
5154
5426
  }
5155
5427
 
5156
5428
  // Recurse into children
5157
- if (Array.isArray(taco.c)) {
5429
+ if (_isA(taco.c)) {
5158
5430
  for (var i = 0; i < taco.c.length; i++) {
5159
- if (taco.c[i] && typeof taco.c[i] === 'object' && taco.c[i].t) {
5431
+ // Wrap string children with ${expr} in a span so patches target the span, not the parent
5432
+ if (_is(taco.c[i], 'string') && taco.c[i].indexOf('${') >= 0) {
5433
+ var mixedRefId = 'bw_ref_' + self._refCounter++;
5434
+ var mixedParsed = bw._parseBindings(taco.c[i]);
5435
+ var mixedDeps = [];
5436
+ for (var mi = 0; mi < mixedParsed.length; mi++) {
5437
+ mixedDeps = mixedDeps.concat(bw._extractDeps(mixedParsed[mi].expr, stateKeys));
5438
+ }
5439
+ self._bindings.push({
5440
+ expr: taco.c[i],
5441
+ type: 'content',
5442
+ refId: mixedRefId,
5443
+ deps: mixedDeps,
5444
+ template: taco.c[i]
5445
+ });
5446
+ // Replace string with a span wrapper so textContent targets the span only
5447
+ taco.c[i] = { t: 'span', a: { 'data-bw_ref': mixedRefId, style: 'display:contents' }, c: taco.c[i] };
5448
+ }
5449
+ if (_is(taco.c[i], 'object') && taco.c[i].t) {
5160
5450
  walkTaco(taco.c[i], path.concat(i));
5161
5451
  }
5162
5452
  // Handle bw.when/bw.each markers
@@ -5191,7 +5481,7 @@
5191
5481
  taco.c[i]._refId = eachRefId;
5192
5482
  }
5193
5483
  }
5194
- } else if (taco.c && typeof taco.c === 'object' && taco.c.t) {
5484
+ } else if (_is(taco.c, 'object') && taco.c.t) {
5195
5485
  walkTaco(taco.c, path.concat(0));
5196
5486
  }
5197
5487
 
@@ -5207,7 +5497,7 @@
5207
5497
  * Build ref map from the live DOM after createDOM.
5208
5498
  * @private
5209
5499
  */
5210
- ComponentHandle.prototype._collectRefs = function() {
5500
+ _chp._collectRefs = function() {
5211
5501
  this._bw_refs = {};
5212
5502
  if (!this.element) return;
5213
5503
  var els = this.element.querySelectorAll('[data-bw_ref]');
@@ -5228,7 +5518,7 @@
5228
5518
  * Creates DOM, compiles bindings, registers actions, and calls lifecycle hooks.
5229
5519
  * @param {Element} parentEl - DOM element to mount into
5230
5520
  */
5231
- ComponentHandle.prototype.mount = function(parentEl) {
5521
+ _chp.mount = function(parentEl) {
5232
5522
  // willMount hook
5233
5523
  if (this._hooks.willMount) this._hooks.willMount(this);
5234
5524
 
@@ -5250,7 +5540,7 @@
5250
5540
  // Register named actions in function registry
5251
5541
  var self = this;
5252
5542
  for (var actionName in this._actions) {
5253
- if (Object.prototype.hasOwnProperty.call(this._actions, actionName)) {
5543
+ if (_hop.call(this._actions, actionName)) {
5254
5544
  var registeredName = this._bwId + '_' + actionName;
5255
5545
  (function(aName) {
5256
5546
  bw.funcRegister(function(evt) {
@@ -5269,6 +5559,11 @@
5269
5559
  this.element = bw.createDOM(tacoForDOM);
5270
5560
  this.element._bwComponentHandle = this;
5271
5561
  this.element.setAttribute('data-bw_comp_id', this._bwId);
5562
+
5563
+ // Restore o.render from original TACO (stripped by _tacoForDOM)
5564
+ if (this.taco.o && this.taco.o.render) {
5565
+ this.element._bw_render = this.taco.o.render;
5566
+ }
5272
5567
  if (this._userTag) {
5273
5568
  this.element.classList.add(this._userTag);
5274
5569
  }
@@ -5284,6 +5579,16 @@
5284
5579
 
5285
5580
  this.mounted = true;
5286
5581
 
5582
+ // Scan for child ComponentHandles and link parent/child (Bug #5)
5583
+ var childEls = this.element.querySelectorAll('[data-bw_comp_id]');
5584
+ for (var ci = 0; ci < childEls.length; ci++) {
5585
+ var ch = childEls[ci]._bwComponentHandle;
5586
+ if (ch && ch !== this && !ch._parent) {
5587
+ ch._parent = this;
5588
+ this._children.push(ch);
5589
+ }
5590
+ }
5591
+
5287
5592
  // mounted hook (backward compat: fn.length === 2 wraps (el, state))
5288
5593
  if (this._hooks.mounted) {
5289
5594
  if (this._hooks.mounted.length === 2) {
@@ -5292,16 +5597,21 @@
5292
5597
  this._hooks.mounted(this);
5293
5598
  }
5294
5599
  }
5600
+
5601
+ // Invoke o.render on initial mount (if present)
5602
+ if (this.element._bw_render) {
5603
+ this.element._bw_render(this.element, this._state);
5604
+ }
5295
5605
  };
5296
5606
 
5297
5607
  /**
5298
5608
  * Prepare TACO for initial render: resolve when/each markers.
5299
5609
  * @private
5300
5610
  */
5301
- ComponentHandle.prototype._prepareTaco = function(taco) {
5302
- if (!taco || typeof taco !== 'object') return;
5611
+ _chp._prepareTaco = function(taco) {
5612
+ if (!_is(taco, 'object')) return;
5303
5613
 
5304
- if (Array.isArray(taco.c)) {
5614
+ if (_isA(taco.c)) {
5305
5615
  for (var i = taco.c.length - 1; i >= 0; i--) {
5306
5616
  var child = taco.c[i];
5307
5617
  if (child && child._bwWhen) {
@@ -5326,18 +5636,18 @@
5326
5636
  var eachExprStr = child.expr.replace(/^\$\{|\}$/g, '');
5327
5637
  var arr = bw._evaluatePath(this._state, eachExprStr);
5328
5638
  var items = [];
5329
- if (Array.isArray(arr)) {
5639
+ if (_isA(arr)) {
5330
5640
  for (var j = 0; j < arr.length; j++) {
5331
5641
  items.push(child.factory(arr[j], j));
5332
5642
  }
5333
5643
  }
5334
5644
  taco.c[i] = { t: 'span', a: { 'data-bw_each': child._refId, style: 'display:contents' }, c: items };
5335
5645
  }
5336
- if (taco.c[i] && typeof taco.c[i] === 'object' && taco.c[i].t) {
5646
+ if (_is(taco.c[i], 'object') && taco.c[i].t) {
5337
5647
  this._prepareTaco(taco.c[i]);
5338
5648
  }
5339
5649
  }
5340
- } else if (taco.c && typeof taco.c === 'object' && taco.c.t) {
5650
+ } else if (_is(taco.c, 'object') && taco.c.t) {
5341
5651
  this._prepareTaco(taco.c);
5342
5652
  }
5343
5653
  };
@@ -5346,12 +5656,12 @@
5346
5656
  * Wire action name strings (in onclick etc.) to dispatch function calls.
5347
5657
  * @private
5348
5658
  */
5349
- ComponentHandle.prototype._wireActions = function(taco) {
5350
- if (!taco || typeof taco !== 'object' || !taco.t) return;
5659
+ _chp._wireActions = function(taco) {
5660
+ if (!_is(taco, 'object') || !taco.t) return;
5351
5661
  if (taco.a) {
5352
5662
  for (var key in taco.a) {
5353
- if (!Object.prototype.hasOwnProperty.call(taco.a, key)) continue;
5354
- if (key.startsWith('on') && typeof taco.a[key] === 'string') {
5663
+ if (!_hop.call(taco.a, key)) continue;
5664
+ if (key.startsWith('on') && _is(taco.a[key], 'string')) {
5355
5665
  var actionName = taco.a[key];
5356
5666
  if (actionName in this._actions) {
5357
5667
  var registeredName = this._bwId + '_' + actionName;
@@ -5365,11 +5675,11 @@
5365
5675
  }
5366
5676
  }
5367
5677
  }
5368
- if (Array.isArray(taco.c)) {
5678
+ if (_isA(taco.c)) {
5369
5679
  for (var i = 0; i < taco.c.length; i++) {
5370
5680
  this._wireActions(taco.c[i]);
5371
5681
  }
5372
- } else if (taco.c && typeof taco.c === 'object' && taco.c.t) {
5682
+ } else if (_is(taco.c, 'object') && taco.c.t) {
5373
5683
  this._wireActions(taco.c);
5374
5684
  }
5375
5685
  };
@@ -5378,7 +5688,7 @@
5378
5688
  * Deep-clone a TACO tree, preserving _bwWhen/_bwEach markers and their factories.
5379
5689
  * @private
5380
5690
  */
5381
- ComponentHandle.prototype._deepCloneTaco = function(taco) {
5691
+ _chp._deepCloneTaco = function(taco) {
5382
5692
  if (taco == null) return taco;
5383
5693
  // Preserve _bwWhen / _bwEach markers (contain functions)
5384
5694
  if (taco._bwWhen) {
@@ -5390,18 +5700,18 @@
5390
5700
  if (taco._bwEach) {
5391
5701
  return { _bwEach: true, expr: taco.expr, factory: taco.factory, _refId: taco._refId };
5392
5702
  }
5393
- if (typeof taco !== 'object' || !taco.t) return taco;
5703
+ if (!_is(taco, 'object') || !taco.t) return taco;
5394
5704
  var result = { t: taco.t };
5395
5705
  if (taco.a) {
5396
5706
  result.a = {};
5397
5707
  for (var k in taco.a) {
5398
- if (Object.prototype.hasOwnProperty.call(taco.a, k)) result.a[k] = taco.a[k];
5708
+ if (_hop.call(taco.a, k)) result.a[k] = taco.a[k];
5399
5709
  }
5400
5710
  }
5401
5711
  if (taco.c != null) {
5402
- if (Array.isArray(taco.c)) {
5712
+ if (_isA(taco.c)) {
5403
5713
  result.c = taco.c.map(function(child) { return this._deepCloneTaco(child); }.bind(this));
5404
- } else if (typeof taco.c === 'object') {
5714
+ } else if (_is(taco.c, 'object')) {
5405
5715
  result.c = this._deepCloneTaco(taco.c);
5406
5716
  } else {
5407
5717
  result.c = taco.c;
@@ -5415,27 +5725,31 @@
5415
5725
  * Create a copy of TACO suitable for createDOM (strips o to prevent double lifecycle).
5416
5726
  * @private
5417
5727
  */
5418
- ComponentHandle.prototype._tacoForDOM = function(taco) {
5419
- if (!taco || typeof taco !== 'object' || !taco.t) return taco;
5728
+ _chp._tacoForDOM = function(taco) {
5729
+ if (!_is(taco, 'object') || !taco.t) return taco;
5420
5730
  var result = { t: taco.t };
5421
5731
  if (taco.a) result.a = taco.a;
5422
5732
  if (taco.c != null) {
5423
- if (Array.isArray(taco.c)) {
5733
+ if (_isA(taco.c)) {
5424
5734
  result.c = taco.c.map(function(child) { return this._tacoForDOM(child); }.bind(this));
5425
- } else if (typeof taco.c === 'object' && taco.c.t) {
5735
+ } else if (_is(taco.c, 'object') && taco.c.t) {
5426
5736
  result.c = this._tacoForDOM(taco.c);
5427
5737
  } else {
5428
5738
  result.c = taco.c;
5429
5739
  }
5430
5740
  }
5431
5741
  // Intentionally strip o (no mounted/unmount/state/render on sub-elements)
5742
+ if (taco.o && (taco.o.mounted || taco.o.render || taco.o.unmount)) {
5743
+ _cw('bw: _tacoForDOM stripped o.mounted/render/unmount from child <' + taco.t +
5744
+ '>. Use onclick attribute or bw.component() for child interactivity.');
5745
+ }
5432
5746
  return result;
5433
5747
  };
5434
5748
 
5435
5749
  /**
5436
5750
  * Unmount: remove from DOM, deactivate, preserve state for re-mount.
5437
5751
  */
5438
- ComponentHandle.prototype.unmount = function() {
5752
+ _chp.unmount = function() {
5439
5753
  if (!this.mounted) return;
5440
5754
 
5441
5755
  // unmount hook
@@ -5470,12 +5784,23 @@
5470
5784
  /**
5471
5785
  * Destroy: unmount + clear state + unregister actions.
5472
5786
  */
5473
- ComponentHandle.prototype.destroy = function() {
5787
+ _chp.destroy = function() {
5474
5788
  // willDestroy hook
5475
5789
  if (this._hooks.willDestroy) {
5476
5790
  this._hooks.willDestroy(this);
5477
5791
  }
5478
5792
 
5793
+ // Cascade destroy to children depth-first (Bug #5)
5794
+ for (var ci = this._children.length - 1; ci >= 0; ci--) {
5795
+ this._children[ci].destroy();
5796
+ }
5797
+ this._children = [];
5798
+ if (this._parent) {
5799
+ var idx = this._parent._children.indexOf(this);
5800
+ if (idx >= 0) this._parent._children.splice(idx, 1);
5801
+ this._parent = null;
5802
+ }
5803
+
5479
5804
  this.unmount();
5480
5805
 
5481
5806
  // Unregister actions from function registry
@@ -5502,12 +5827,36 @@
5502
5827
  * Flush dirty state: resolve changed bindings and apply to DOM.
5503
5828
  * @private
5504
5829
  */
5505
- ComponentHandle.prototype._flush = function() {
5830
+ _chp._flush = function() {
5506
5831
  this._scheduled = false;
5507
- var changedKeys = Object.keys(this._dirtyKeys);
5832
+ var changedKeys = _keys(this._dirtyKeys);
5508
5833
  this._dirtyKeys = {};
5509
5834
  if (changedKeys.length === 0 || !this.mounted) return;
5510
5835
 
5836
+ // Factory rebuild: if a BCCL factory exists and changed keys overlap factory props,
5837
+ // rebuild the TACO from the factory with merged state (Bug #6)
5838
+ if (this._factory) {
5839
+ var rebuildNeeded = false;
5840
+ for (var fi = 0; fi < changedKeys.length; fi++) {
5841
+ if (_hop.call(this._factory.props, changedKeys[fi])) {
5842
+ rebuildNeeded = true; break;
5843
+ }
5844
+ }
5845
+ if (rebuildNeeded) {
5846
+ var merged = {};
5847
+ for (var mk in this._factory.props) if (_hop.call(this._factory.props, mk)) merged[mk] = this._factory.props[mk];
5848
+ for (var sk in this._state) if (_hop.call(this._state, sk)) merged[sk] = this._state[sk];
5849
+ this._factory.props = merged;
5850
+ var newTaco = bw.make(this._factory.type, merged);
5851
+ newTaco._bwFactory = this._factory;
5852
+ this.taco = newTaco;
5853
+ this._originalTaco = this._deepCloneTaco(newTaco);
5854
+ this._render();
5855
+ if (this._hooks.onUpdate) this._hooks.onUpdate(this, changedKeys);
5856
+ return;
5857
+ }
5858
+ }
5859
+
5511
5860
  // willUpdate hook
5512
5861
  if (this._hooks.willUpdate) {
5513
5862
  this._hooks.willUpdate(this, changedKeys);
@@ -5546,7 +5895,7 @@
5546
5895
  * Returns list of patches to apply.
5547
5896
  * @private
5548
5897
  */
5549
- ComponentHandle.prototype._resolveBindings = function(changedKeys) {
5898
+ _chp._resolveBindings = function(changedKeys) {
5550
5899
  var patches = [];
5551
5900
  for (var i = 0; i < this._bindings.length; i++) {
5552
5901
  var b = this._bindings[i];
@@ -5582,11 +5931,14 @@
5582
5931
  * Apply patches to DOM.
5583
5932
  * @private
5584
5933
  */
5585
- ComponentHandle.prototype._applyPatches = function(patches) {
5934
+ _chp._applyPatches = function(patches) {
5586
5935
  for (var i = 0; i < patches.length; i++) {
5587
5936
  var p = patches[i];
5588
5937
  var el = this._bw_refs[p.refId];
5589
- if (!el) continue;
5938
+ if (!el) {
5939
+ if (bw.debug) _cw('bw.debug: _applyPatches — ref "' + p.refId + '" not found in DOM');
5940
+ continue;
5941
+ }
5590
5942
  if (p.type === 'content') {
5591
5943
  el.textContent = p.value;
5592
5944
  } else if (p.type === 'attribute') {
@@ -5603,7 +5955,7 @@
5603
5955
  * Resolve all bindings and apply (used for initial render).
5604
5956
  * @private
5605
5957
  */
5606
- ComponentHandle.prototype._resolveAndApplyAll = function() {
5958
+ _chp._resolveAndApplyAll = function() {
5607
5959
  var patches = [];
5608
5960
  for (var i = 0; i < this._bindings.length; i++) {
5609
5961
  var b = this._bindings[i];
@@ -5626,7 +5978,7 @@
5626
5978
  * Full re-render for structural changes (when/each branch switches).
5627
5979
  * @private
5628
5980
  */
5629
- ComponentHandle.prototype._render = function() {
5981
+ _chp._render = function() {
5630
5982
  if (!this.element || !this.element.parentNode) return;
5631
5983
  var parent = this.element.parentNode;
5632
5984
  var nextSibling = this.element.nextSibling;
@@ -5666,7 +6018,7 @@
5666
6018
  * @param {string} event - Event name (e.g., 'click')
5667
6019
  * @param {Function} handler - Event handler
5668
6020
  */
5669
- ComponentHandle.prototype.on = function(event, handler) {
6021
+ _chp.on = function(event, handler) {
5670
6022
  if (this.element) {
5671
6023
  this.element.addEventListener(event, handler);
5672
6024
  }
@@ -5678,7 +6030,7 @@
5678
6030
  * @param {string} event - Event name
5679
6031
  * @param {Function} handler - Handler to remove
5680
6032
  */
5681
- ComponentHandle.prototype.off = function(event, handler) {
6033
+ _chp.off = function(event, handler) {
5682
6034
  if (this.element) {
5683
6035
  this.element.removeEventListener(event, handler);
5684
6036
  }
@@ -5693,7 +6045,7 @@
5693
6045
  * @param {Function} handler - Handler function
5694
6046
  * @returns {Function} Unsubscribe function
5695
6047
  */
5696
- ComponentHandle.prototype.sub = function(topic, handler) {
6048
+ _chp.sub = function(topic, handler) {
5697
6049
  var unsub = bw.sub(topic, handler);
5698
6050
  this._subs.push(unsub);
5699
6051
  return unsub;
@@ -5704,10 +6056,10 @@
5704
6056
  * @param {string} name - Action name
5705
6057
  * @param {...*} args - Arguments passed after comp
5706
6058
  */
5707
- ComponentHandle.prototype.action = function(name) {
6059
+ _chp.action = function(name) {
5708
6060
  var fn = this._actions[name];
5709
6061
  if (!fn) {
5710
- console.warn('ComponentHandle.action: unknown action "' + name + '"');
6062
+ _cw('ComponentHandle.action: unknown action "' + name + '"');
5711
6063
  return;
5712
6064
  }
5713
6065
  var args = [this].concat(Array.prototype.slice.call(arguments, 1));
@@ -5719,7 +6071,7 @@
5719
6071
  * @param {string} sel - CSS selector
5720
6072
  * @returns {Element|null}
5721
6073
  */
5722
- ComponentHandle.prototype.select = function(sel) {
6074
+ _chp.select = function(sel) {
5723
6075
  return this.element ? this.element.querySelector(sel) : null;
5724
6076
  };
5725
6077
 
@@ -5728,7 +6080,7 @@
5728
6080
  * @param {string} sel - CSS selector
5729
6081
  * @returns {Element[]}
5730
6082
  */
5731
- ComponentHandle.prototype.selectAll = function(sel) {
6083
+ _chp.selectAll = function(sel) {
5732
6084
  if (!this.element) return [];
5733
6085
  return Array.prototype.slice.call(this.element.querySelectorAll(sel));
5734
6086
  };
@@ -5739,7 +6091,7 @@
5739
6091
  * @param {string} tag - User-defined identifier (e.g. 'dashboard_prod_east')
5740
6092
  * @returns {ComponentHandle} this (for chaining)
5741
6093
  */
5742
- ComponentHandle.prototype.userTag = function(tag) {
6094
+ _chp.userTag = function(tag) {
5743
6095
  this._userTag = tag;
5744
6096
  if (this.element) {
5745
6097
  this.element.classList.add(tag);
@@ -5840,8 +6192,8 @@
5840
6192
  }
5841
6193
  if (!el || !el._bwComponentHandle) return false;
5842
6194
  var comp = el._bwComponentHandle;
5843
- if (typeof comp[action] !== 'function') {
5844
- console.warn('bw.message: unknown action "' + action + '" on component ' + target);
6195
+ if (!_is(comp[action], 'function')) {
6196
+ _cw('bw.message: unknown action "' + action + '" on component ' + target);
5845
6197
  return false;
5846
6198
  }
5847
6199
  comp[action](data);
@@ -5878,7 +6230,7 @@
5878
6230
  },
5879
6231
  focus: function(selector) {
5880
6232
  var el = bw._el(selector);
5881
- if (el && typeof el.focus === 'function') el.focus();
6233
+ if (el && _is(el.focus, 'function')) el.focus();
5882
6234
  },
5883
6235
  download: function(filename, content, mimeType) {
5884
6236
  if (typeof document === 'undefined') return;
@@ -6043,12 +6395,12 @@
6043
6395
  } else if (type === 'remove') {
6044
6396
  var toRemove = bw._el(target);
6045
6397
  if (!toRemove) return false;
6046
- if (typeof bw.cleanup === 'function') bw.cleanup(toRemove);
6398
+ if (_is(bw.cleanup, 'function')) bw.cleanup(toRemove);
6047
6399
  toRemove.remove();
6048
6400
  return true;
6049
6401
 
6050
6402
  } else if (type === 'batch') {
6051
- if (!Array.isArray(msg.ops)) return false;
6403
+ if (!_isA(msg.ops)) return false;
6052
6404
  var allOk = true;
6053
6405
  msg.ops.forEach(function(op) {
6054
6406
  if (!bw.clientApply(op)) allOk = false;
@@ -6064,26 +6416,26 @@
6064
6416
  bw._clientFunctions[msg.name] = new Function('return ' + msg.body)();
6065
6417
  return true;
6066
6418
  } catch (e) {
6067
- console.error('[bw] register error:', msg.name, e);
6419
+ _ce('[bw] register error:', msg.name, e);
6068
6420
  return false;
6069
6421
  }
6070
6422
 
6071
6423
  } else if (type === 'call') {
6072
6424
  if (!msg.name) return false;
6073
6425
  var fn = bw._clientFunctions[msg.name] || bw._builtinClientFunctions[msg.name];
6074
- if (typeof fn !== 'function') return false;
6426
+ if (!_is(fn, 'function')) return false;
6075
6427
  try {
6076
- var args = Array.isArray(msg.args) ? msg.args : [];
6428
+ var args = _isA(msg.args) ? msg.args : [];
6077
6429
  fn.apply(null, args);
6078
6430
  return true;
6079
6431
  } catch (e) {
6080
- console.error('[bw] call error:', msg.name, e);
6432
+ _ce('[bw] call error:', msg.name, e);
6081
6433
  return false;
6082
6434
  }
6083
6435
 
6084
6436
  } else if (type === 'exec') {
6085
6437
  if (!bw._allowExec) {
6086
- console.warn('[bw] exec rejected: allowExec is not enabled');
6438
+ _cw('[bw] exec rejected: allowExec is not enabled');
6087
6439
  return false;
6088
6440
  }
6089
6441
  if (!msg.code) return false;
@@ -6091,7 +6443,7 @@
6091
6443
  new Function(msg.code)();
6092
6444
  return true;
6093
6445
  } catch (e) {
6094
- console.error('[bw] exec error:', e);
6446
+ _ce('[bw] exec error:', e);
6095
6447
  return false;
6096
6448
  }
6097
6449
  }
@@ -6139,7 +6491,7 @@
6139
6491
 
6140
6492
  function handleMessage(data) {
6141
6493
  try {
6142
- var msg = typeof data === 'string' ? bw.clientParse(data) : data;
6494
+ var msg = _is(data, 'string') ? bw.clientParse(data) : data;
6143
6495
  if (onMessage) onMessage(msg);
6144
6496
  if (handlers.message) handlers.message(msg);
6145
6497
  bw.clientApply(msg);
@@ -6177,7 +6529,7 @@
6177
6529
  setStatus('connected');
6178
6530
  conn._pollTimer = setInterval(function() {
6179
6531
  fetch(url).then(function(r) { return r.json(); }).then(function(msgs) {
6180
- if (Array.isArray(msgs)) {
6532
+ if (_isA(msgs)) {
6181
6533
  msgs.forEach(handleMessage);
6182
6534
  } else if (msgs && msgs.type) {
6183
6535
  handleMessage(msgs);
@@ -6259,33 +6611,33 @@
6259
6611
  el = target.element;
6260
6612
  comp = target;
6261
6613
  } else {
6262
- if (typeof target === 'string') {
6614
+ if (_is(target, 'string')) {
6263
6615
  el = bw.$(target)[0];
6264
6616
  }
6265
6617
  if (!el) {
6266
- console.warn('bw.inspect: element not found');
6618
+ _cw('bw.inspect: element not found');
6267
6619
  return null;
6268
6620
  }
6269
6621
  comp = el._bwComponentHandle;
6270
6622
  }
6271
6623
  if (!comp) {
6272
- console.log('bw.inspect: no ComponentHandle on this element');
6273
- console.log(' Tag:', el.tagName);
6274
- console.log(' Classes:', el.className);
6275
- console.log(' _bw_state:', el._bw_state || '(none)');
6624
+ _cl('bw.inspect: no ComponentHandle on this element');
6625
+ _cl(' Tag:', el.tagName);
6626
+ _cl(' Classes:', el.className);
6627
+ _cl(' _bw_state:', el._bw_state || '(none)');
6276
6628
  return null;
6277
6629
  }
6278
6630
  var deps = comp._bindings.reduce(function(s, b) {
6279
6631
  return s.concat(b.deps || []);
6280
6632
  }, []).filter(function(v, i, a) { return a.indexOf(v) === i; });
6281
6633
  console.group('Component: ' + comp._bwId);
6282
- console.log('State:', comp._state);
6283
- console.log('Bindings:', comp._bindings.length, '(deps:', deps, ')');
6284
- console.log('Methods:', Object.keys(comp._methods));
6285
- console.log('Actions:', Object.keys(comp._actions));
6286
- console.log('User tag:', comp._userTag || '(none)');
6287
- console.log('Mounted:', comp.mounted);
6288
- console.log('Element:', comp.element);
6634
+ _cl('State:', comp._state);
6635
+ _cl('Bindings:', comp._bindings.length, '(deps:', deps, ')');
6636
+ _cl('Methods:', _keys(comp._methods));
6637
+ _cl('Actions:', _keys(comp._actions));
6638
+ _cl('User tag:', comp._userTag || '(none)');
6639
+ _cl('Mounted:', comp.mounted);
6640
+ _cl('Element:', comp.element);
6289
6641
  console.groupEnd();
6290
6642
  return comp;
6291
6643
  };
@@ -6308,8 +6660,8 @@
6308
6660
  // Pre-extract all binding expressions
6309
6661
  var precompiled = [];
6310
6662
  function walkExpressions(node) {
6311
- if (!node || typeof node !== 'object') return;
6312
- if (typeof node.c === 'string' && node.c.indexOf('${') >= 0) {
6663
+ if (!_is(node, 'object')) return;
6664
+ if (_is(node.c, 'string') && node.c.indexOf('${') >= 0) {
6313
6665
  var parsed = bw._parseBindings(node.c);
6314
6666
  for (var i = 0; i < parsed.length; i++) {
6315
6667
  try {
@@ -6324,9 +6676,9 @@
6324
6676
  }
6325
6677
  if (node.a) {
6326
6678
  for (var key in node.a) {
6327
- if (Object.prototype.hasOwnProperty.call(node.a, key)) {
6679
+ if (_hop.call(node.a, key)) {
6328
6680
  var v = node.a[key];
6329
- if (typeof v === 'string' && v.indexOf('${') >= 0) {
6681
+ if (_is(v, 'string') && v.indexOf('${') >= 0) {
6330
6682
  var parsed2 = bw._parseBindings(v);
6331
6683
  for (var j = 0; j < parsed2.length; j++) {
6332
6684
  try {
@@ -6342,9 +6694,9 @@
6342
6694
  }
6343
6695
  }
6344
6696
  }
6345
- if (Array.isArray(node.c)) {
6697
+ if (_isA(node.c)) {
6346
6698
  for (var k = 0; k < node.c.length; k++) walkExpressions(node.c[k]);
6347
- } else if (node.c && typeof node.c === 'object' && node.c.t) {
6699
+ } else if (_is(node.c, 'object') && node.c.t) {
6348
6700
  walkExpressions(node.c);
6349
6701
  }
6350
6702
  }
@@ -6356,7 +6708,7 @@
6356
6708
  handle._precompiledBindings = precompiled;
6357
6709
  if (initialState) {
6358
6710
  for (var k in initialState) {
6359
- if (Object.prototype.hasOwnProperty.call(initialState, k)) {
6711
+ if (_hop.call(initialState, k)) {
6360
6712
  handle._state[k] = initialState[k];
6361
6713
  }
6362
6714
  }
@@ -6387,18 +6739,18 @@
6387
6739
  bw.css = function(rules, options = {}) {
6388
6740
  const { minify = false, pretty = !minify } = options;
6389
6741
 
6390
- if (typeof rules === 'string') return rules;
6742
+ if (_is(rules, 'string')) return rules;
6391
6743
 
6392
6744
  let css = '';
6393
6745
  const indent = pretty ? ' ' : '';
6394
6746
  const newline = pretty ? '\n' : '';
6395
6747
  const space = pretty ? ' ' : '';
6396
6748
 
6397
- if (Array.isArray(rules)) {
6749
+ if (_isA(rules)) {
6398
6750
  css = rules.map(rule => bw.css(rule, options)).join(newline);
6399
- } else if (typeof rules === 'object') {
6751
+ } else if (_is(rules, 'object')) {
6400
6752
  Object.entries(rules).forEach(([selector, styles]) => {
6401
- if (typeof styles === 'object' && !Array.isArray(styles)) {
6753
+ if (_is(styles, 'object')) {
6402
6754
  // Handle @media, @keyframes, @supports — recurse into nested block
6403
6755
  if (selector.charAt(0) === '@') {
6404
6756
  const inner = bw.css(styles, options);
@@ -6447,7 +6799,7 @@
6447
6799
  */
6448
6800
  bw.injectCSS = function(css, options = {}) {
6449
6801
  if (!bw._isBrowser) {
6450
- console.warn('bw.injectCSS requires a DOM environment');
6802
+ _cw('bw.injectCSS requires a DOM environment');
6451
6803
  return null;
6452
6804
  }
6453
6805
 
@@ -6464,7 +6816,7 @@
6464
6816
  }
6465
6817
 
6466
6818
  // Convert CSS if needed
6467
- const cssStr = typeof css === 'string' ? css : bw.css(css, options);
6819
+ const cssStr = _is(css, 'string') ? css : bw.css(css, options);
6468
6820
 
6469
6821
  // Set or append CSS
6470
6822
  if (append && styleEl.textContent) {
@@ -6494,7 +6846,7 @@
6494
6846
  var result = {};
6495
6847
  for (var i = 0; i < arguments.length; i++) {
6496
6848
  var arg = arguments[i];
6497
- if (arg && typeof arg === 'object') Object.assign(result, arg);
6849
+ if (_is(arg, 'object')) Object.assign(result, arg);
6498
6850
  }
6499
6851
  return result;
6500
6852
  };
@@ -6617,7 +6969,7 @@
6617
6969
  bw.responsive = function(selector, breakpoints) {
6618
6970
  var sizes = { sm: '576px', md: '768px', lg: '992px', xl: '1200px' };
6619
6971
  var parts = [];
6620
- Object.keys(breakpoints).forEach(function(key) {
6972
+ _keys(breakpoints).forEach(function(key) {
6621
6973
  var rules = {};
6622
6974
  if (key === 'base') {
6623
6975
  rules[selector] = breakpoints[key];
@@ -6689,18 +7041,18 @@
6689
7041
  if (!selector) return [];
6690
7042
 
6691
7043
  // Already an array
6692
- if (Array.isArray(selector)) return selector;
7044
+ if (_isA(selector)) return selector;
6693
7045
 
6694
7046
  // Single element
6695
7047
  if (selector.nodeType) return [selector];
6696
7048
 
6697
7049
  // NodeList or HTMLCollection
6698
- if (selector.length !== undefined && typeof selector !== 'string') {
7050
+ if (selector.length !== undefined && !_is(selector, 'string')) {
6699
7051
  return Array.from(selector);
6700
7052
  }
6701
7053
 
6702
7054
  // CSS selector string
6703
- if (typeof selector === 'string') {
7055
+ if (_is(selector, 'string')) {
6704
7056
  return Array.from(document.querySelectorAll(selector));
6705
7057
  }
6706
7058
 
@@ -7204,7 +7556,7 @@
7204
7556
 
7205
7557
  // Auto-detect columns if not provided
7206
7558
  const cols = columns || (data.length > 0
7207
- ? Object.keys(data[0]).map(key => ({ key, label: key }))
7559
+ ? _keys(data[0]).map(key => ({ key, label: key }))
7208
7560
  : []);
7209
7561
 
7210
7562
  // Current sort state
@@ -7219,7 +7571,7 @@
7219
7571
  const bVal = b[currentSortColumn];
7220
7572
 
7221
7573
  // Handle different types
7222
- if (typeof aVal === 'number' && typeof bVal === 'number') {
7574
+ if (_is(aVal, 'number') && _is(bVal, 'number')) {
7223
7575
  return currentSortDirection === 'asc' ? aVal - bVal : bVal - aVal;
7224
7576
  }
7225
7577
 
@@ -7329,7 +7681,7 @@
7329
7681
  bw.makeTableFromArray = function(config) {
7330
7682
  const { data = [], headerRow = true, columns, ...rest } = config;
7331
7683
 
7332
- if (!Array.isArray(data) || data.length === 0) {
7684
+ if (!_isA(data) || data.length === 0) {
7333
7685
  return bw.makeTable({ data: [], columns: columns || [], ...rest });
7334
7686
  }
7335
7687
 
@@ -7411,7 +7763,7 @@
7411
7763
  className = ''
7412
7764
  } = config;
7413
7765
 
7414
- if (!Array.isArray(data) || data.length === 0) {
7766
+ if (!_isA(data) || data.length === 0) {
7415
7767
  return { t: 'div', a: { class: ('bw_bar_chart_container ' + className).trim() }, c: '' };
7416
7768
  }
7417
7769
 
@@ -7560,7 +7912,7 @@
7560
7912
  */
7561
7913
  bw.render = function(element, position, taco) {
7562
7914
  // Get target element
7563
- const targetEl = typeof element === 'string'
7915
+ const targetEl = _is(element, 'string')
7564
7916
  ? document.querySelector(element)
7565
7917
  : element;
7566
7918
 
@@ -7710,7 +8062,7 @@
7710
8062
  setContent(content) {
7711
8063
  this._taco.c = content;
7712
8064
  if (this.element) {
7713
- if (typeof content === 'string') {
8065
+ if (_is(content, 'string')) {
7714
8066
  this.element.textContent = content;
7715
8067
  } else {
7716
8068
  // Re-render for complex content