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.
- package/dist/bitwrench-bccl.cjs.js +6 -2
- package/dist/bitwrench-bccl.cjs.min.js +3 -3
- package/dist/bitwrench-bccl.esm.js +6 -2
- package/dist/bitwrench-bccl.esm.min.js +3 -3
- package/dist/bitwrench-bccl.umd.js +6 -2
- package/dist/bitwrench-bccl.umd.min.js +2 -2
- package/dist/bitwrench-code-edit.cjs.js +1 -1
- package/dist/bitwrench-code-edit.cjs.min.js +1 -1
- package/dist/bitwrench-code-edit.es5.js +1 -1
- package/dist/bitwrench-code-edit.es5.min.js +1 -1
- package/dist/bitwrench-code-edit.esm.js +1 -1
- package/dist/bitwrench-code-edit.esm.min.js +1 -1
- package/dist/bitwrench-code-edit.umd.js +1 -1
- package/dist/bitwrench-code-edit.umd.min.js +1 -1
- package/dist/bitwrench-lean.cjs.js +506 -154
- package/dist/bitwrench-lean.cjs.min.js +7 -7
- package/dist/bitwrench-lean.es5.js +517 -155
- package/dist/bitwrench-lean.es5.min.js +5 -5
- package/dist/bitwrench-lean.esm.js +505 -154
- package/dist/bitwrench-lean.esm.min.js +6 -6
- package/dist/bitwrench-lean.umd.js +506 -154
- package/dist/bitwrench-lean.umd.min.js +7 -7
- package/dist/bitwrench.cjs.js +511 -155
- package/dist/bitwrench.cjs.min.js +8 -8
- package/dist/bitwrench.es5.js +525 -156
- package/dist/bitwrench.es5.min.js +6 -6
- package/dist/bitwrench.esm.js +510 -155
- package/dist/bitwrench.esm.min.js +8 -8
- package/dist/bitwrench.umd.js +511 -155
- package/dist/bitwrench.umd.min.js +8 -8
- package/dist/builds.json +82 -82
- package/dist/bwserve.cjs.js +16 -2
- package/dist/bwserve.esm.js +16 -2
- package/dist/sri.json +34 -34
- package/package.json +4 -2
- package/readme.html +1 -1
- package/src/bitwrench-bccl.js +5 -1
- package/src/bitwrench.js +502 -151
- package/src/bwserve/index.js +12 -1
- package/src/bwserve/shell.js +3 -0
- package/src/cli/layout-default.js +47 -32
- package/src/version.js +3 -3
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
/*! bitwrench-lean v2.0.
|
|
1
|
+
/*! bitwrench-lean v2.0.17 | BSD-2-Clause | https://deftio.github.com/bitwrench/pages */
|
|
2
2
|
/**
|
|
3
3
|
* Auto-generated version file from package.json
|
|
4
4
|
* DO NOT EDIT DIRECTLY - Use npm run generate-version
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const VERSION_INFO = {
|
|
8
|
-
version: '2.0.
|
|
8
|
+
version: '2.0.17',
|
|
9
9
|
name: 'bitwrench',
|
|
10
10
|
description: 'A library for javascript UI functions.',
|
|
11
11
|
license: 'BSD-2-Clause',
|
|
12
12
|
homepage: 'https://deftio.github.com/bitwrench/pages',
|
|
13
13
|
repository: 'git+https://github.com/deftio/bitwrench.git',
|
|
14
14
|
author: 'manu a. chatterjee <deftio@deftio.com> (https://deftio.com/)',
|
|
15
|
-
buildDate: '2026-03-
|
|
15
|
+
buildDate: '2026-03-13T23:15:10.823Z'
|
|
16
16
|
};
|
|
17
17
|
|
|
18
18
|
/**
|
|
@@ -3343,7 +3343,7 @@ const bw = {
|
|
|
3343
3343
|
__monkey_patch_is_nodejs__: {
|
|
3344
3344
|
_value: 'ignore',
|
|
3345
3345
|
set: function(x) {
|
|
3346
|
-
this._value = (
|
|
3346
|
+
this._value = _is(x, 'boolean') ? x : 'ignore';
|
|
3347
3347
|
},
|
|
3348
3348
|
get: function() {
|
|
3349
3349
|
return this._value;
|
|
@@ -3391,6 +3391,67 @@ Object.defineProperty(bw, '_isBrowser', {
|
|
|
3391
3391
|
configurable: true
|
|
3392
3392
|
});
|
|
3393
3393
|
|
|
3394
|
+
// ── Internal aliases ─────────────────────────────────────────────────────
|
|
3395
|
+
// Short names for frequently-used builtins and internal methods.
|
|
3396
|
+
// Same pattern as v1 (_to = bw.typeOf, etc.).
|
|
3397
|
+
//
|
|
3398
|
+
// Why: Terser can't shorten global property chains (console.warn,
|
|
3399
|
+
// Object.prototype.hasOwnProperty, Array.isArray, document.createElement)
|
|
3400
|
+
// because it can't prove they're side-effect-free. We can, so we alias
|
|
3401
|
+
// them here. Each alias saves bytes in the minified output, and the short
|
|
3402
|
+
// names also reduce visual noise in the hot paths (binding pipeline,
|
|
3403
|
+
// createDOM, etc.).
|
|
3404
|
+
//
|
|
3405
|
+
// Alias Target Sites
|
|
3406
|
+
// ───────── ────────────────────────────────────── ─────
|
|
3407
|
+
// _hop Object.prototype.hasOwnProperty 15
|
|
3408
|
+
// _isA Array.isArray 25
|
|
3409
|
+
// _keys Object.keys 7
|
|
3410
|
+
// _to bw.typeOf (type string) 26
|
|
3411
|
+
// _is type check boolean: _is(x,'string') ~50
|
|
3412
|
+
// _cw console.warn 8
|
|
3413
|
+
// _cl console.log 11
|
|
3414
|
+
// _ce console.error 4
|
|
3415
|
+
// _chp ComponentHandle.prototype 28 (defined after constructor)
|
|
3416
|
+
//
|
|
3417
|
+
// Note: document.createElement etc. are NOT aliased because they require
|
|
3418
|
+
// `this === document` and .bind() would add overhead on every call.
|
|
3419
|
+
// Console aliases use thin wrappers (not direct refs) so test monkey-
|
|
3420
|
+
// patching of console.warn/log/error continues to work.
|
|
3421
|
+
//
|
|
3422
|
+
// `typeof x` for UNDECLARED globals (window, document, process, require,
|
|
3423
|
+
// EventSource, navigator, Promise, __filename, import.meta) MUST stay as
|
|
3424
|
+
// raw `typeof` — calling _to(x) when x doesn't exist throws ReferenceError.
|
|
3425
|
+
//
|
|
3426
|
+
// ── v1 functional type helpers (kept for reference, not currently used) ──
|
|
3427
|
+
// _toa(x, type, trueVal, falseVal) — bw.typeAssign:
|
|
3428
|
+
// returns trueVal if _to(x)===type, else falseVal.
|
|
3429
|
+
// Replaces: (typeof x === 'string') ? A : B → _toa(x,'string',A,B)
|
|
3430
|
+
// _toc(x, type, trueVal, falseVal) — bw.typeConvert:
|
|
3431
|
+
// same as _toa but if trueVal/falseVal are functions, calls them with x.
|
|
3432
|
+
// Replaces: typeof x === 'string' ? fn(x) : default → _toc(x,'string',fn,default)
|
|
3433
|
+
// Uncomment if pattern frequency justifies them:
|
|
3434
|
+
// var _toa = function(x, t, y, n) { return _to(x) === t ? y : n; };
|
|
3435
|
+
// 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); };
|
|
3436
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
3437
|
+
var _hop = Object.prototype.hasOwnProperty;
|
|
3438
|
+
var _isA = Array.isArray;
|
|
3439
|
+
var _keys = Object.keys;
|
|
3440
|
+
var _to = typeOf; // imported from bitwrench-utils.js
|
|
3441
|
+
var _is = function(x, t) { var r = _to(x); return r === t || r.toLowerCase() === t; };
|
|
3442
|
+
// Console aliases use thin wrappers (not direct references) so that test
|
|
3443
|
+
// code can monkey-patch console.warn/log/error and the patches take effect.
|
|
3444
|
+
var _cw = function() { console.warn.apply(console, arguments); };
|
|
3445
|
+
var _cl = function() { console.log.apply(console, arguments); };
|
|
3446
|
+
var _ce = function() { console.error.apply(console, arguments); };
|
|
3447
|
+
|
|
3448
|
+
/**
|
|
3449
|
+
* Debug flag. When true, emits console.warn for silent binding failures
|
|
3450
|
+
* (missing paths, null refs, auto-created intermediate objects).
|
|
3451
|
+
* @type {boolean}
|
|
3452
|
+
*/
|
|
3453
|
+
bw.debug = false;
|
|
3454
|
+
|
|
3394
3455
|
/**
|
|
3395
3456
|
* Lazy-resolve Node.js `fs` module.
|
|
3396
3457
|
* Tries require('fs') first (available in CJS/UMD Node.js builds),
|
|
@@ -3538,7 +3599,7 @@ bw.uuid = function(prefix) {
|
|
|
3538
3599
|
*/
|
|
3539
3600
|
bw._el = function(id) {
|
|
3540
3601
|
// Pass-through for DOM elements
|
|
3541
|
-
if (
|
|
3602
|
+
if (!_is(id, 'string')) return id || null;
|
|
3542
3603
|
if (!id) return null;
|
|
3543
3604
|
if (!bw._isBrowser) return null;
|
|
3544
3605
|
|
|
@@ -3634,7 +3695,7 @@ bw._deregisterNode = function(el, bwId) {
|
|
|
3634
3695
|
* // => '<b>Hello</b> & "world"'
|
|
3635
3696
|
*/
|
|
3636
3697
|
bw.escapeHTML = function(str) {
|
|
3637
|
-
if (
|
|
3698
|
+
if (!_is(str, 'string')) return '';
|
|
3638
3699
|
|
|
3639
3700
|
const escapeMap = {
|
|
3640
3701
|
'&': '&',
|
|
@@ -3707,7 +3768,7 @@ bw.html = function(taco, options = {}) {
|
|
|
3707
3768
|
}
|
|
3708
3769
|
|
|
3709
3770
|
// Handle arrays of TACOs
|
|
3710
|
-
if (
|
|
3771
|
+
if (_isA(taco)) {
|
|
3711
3772
|
return taco.map(t => bw.html(t, options)).join('');
|
|
3712
3773
|
}
|
|
3713
3774
|
|
|
@@ -3730,15 +3791,15 @@ bw.html = function(taco, options = {}) {
|
|
|
3730
3791
|
if (taco && taco._bwEach && options.state) {
|
|
3731
3792
|
var eachExpr = taco.expr.replace(/^\$\{|\}$/g, '');
|
|
3732
3793
|
var arr = bw._evaluatePath(options.state, eachExpr);
|
|
3733
|
-
if (!
|
|
3794
|
+
if (!_isA(arr)) return '';
|
|
3734
3795
|
return arr.map(function(item, idx) { return bw.html(taco.factory(item, idx), options); }).join('');
|
|
3735
3796
|
}
|
|
3736
3797
|
|
|
3737
3798
|
// Handle primitives and non-TACO objects
|
|
3738
|
-
if (
|
|
3799
|
+
if (!_is(taco, 'object') || !taco.t) {
|
|
3739
3800
|
var str = options.raw ? String(taco) : bw.escapeHTML(String(taco));
|
|
3740
3801
|
// Resolve template bindings if state provided
|
|
3741
|
-
if (options.state &&
|
|
3802
|
+
if (options.state && _is(str, 'string') && str.indexOf('${') >= 0) {
|
|
3742
3803
|
str = bw._resolveTemplate(str, options.state, !!options.compile);
|
|
3743
3804
|
}
|
|
3744
3805
|
return str;
|
|
@@ -3758,10 +3819,18 @@ bw.html = function(taco, options = {}) {
|
|
|
3758
3819
|
// Skip null, undefined, false
|
|
3759
3820
|
if (value == null || value === false) continue;
|
|
3760
3821
|
|
|
3761
|
-
//
|
|
3762
|
-
if (key.startsWith('on'))
|
|
3822
|
+
// Serialize event handlers via funcRegister
|
|
3823
|
+
if (key.startsWith('on')) {
|
|
3824
|
+
if (_is(value, 'function')) {
|
|
3825
|
+
var fnId = bw.funcRegister(value);
|
|
3826
|
+
attrStr += ' ' + key + '="' + bw.funcGetDispatchStr(fnId, 'event') + '"';
|
|
3827
|
+
} else if (_is(value, 'string')) {
|
|
3828
|
+
attrStr += ' ' + key + '="' + bw.escapeHTML(value) + '"';
|
|
3829
|
+
}
|
|
3830
|
+
continue;
|
|
3831
|
+
}
|
|
3763
3832
|
|
|
3764
|
-
if (key === 'style' &&
|
|
3833
|
+
if (key === 'style' && _is(value, 'object')) {
|
|
3765
3834
|
// Convert style object to string
|
|
3766
3835
|
const styleStr = Object.entries(value)
|
|
3767
3836
|
.filter(([, v]) => v != null)
|
|
@@ -3772,7 +3841,7 @@ bw.html = function(taco, options = {}) {
|
|
|
3772
3841
|
}
|
|
3773
3842
|
} else if (key === 'class') {
|
|
3774
3843
|
// Handle class as array or string
|
|
3775
|
-
const classStr =
|
|
3844
|
+
const classStr = _isA(value) ? value.filter(Boolean).join(' ') : String(value);
|
|
3776
3845
|
if (classStr) {
|
|
3777
3846
|
attrStr += ` class="${bw.escapeHTML(classStr)}"`;
|
|
3778
3847
|
}
|
|
@@ -3808,13 +3877,184 @@ bw.html = function(taco, options = {}) {
|
|
|
3808
3877
|
// Process content recursively
|
|
3809
3878
|
let contentStr = content != null ? bw.html(content, options) : '';
|
|
3810
3879
|
// Resolve template bindings in content if state provided
|
|
3811
|
-
if (options.state &&
|
|
3880
|
+
if (options.state && _is(contentStr, 'string') && contentStr.indexOf('${') >= 0) {
|
|
3812
3881
|
contentStr = bw._resolveTemplate(contentStr, options.state, !!options.compile);
|
|
3813
3882
|
}
|
|
3814
3883
|
|
|
3815
3884
|
return `<${tag}${attrStr}>${contentStr}</${tag}>`;
|
|
3816
3885
|
};
|
|
3817
3886
|
|
|
3887
|
+
/**
|
|
3888
|
+
* Generate a complete, self-contained HTML document from TACO content.
|
|
3889
|
+
*
|
|
3890
|
+
* Produces a full `<!DOCTYPE html>` page with configurable runtime injection,
|
|
3891
|
+
* func registry emission (so serialized event handlers work), optional theme,
|
|
3892
|
+
* and extra head elements. Designed for static site generation, offline/airgapped
|
|
3893
|
+
* use, and the "static site that isn't static" workflow.
|
|
3894
|
+
*
|
|
3895
|
+
* @param {Object} [opts={}] - Page options
|
|
3896
|
+
* @param {Object|string|Array} [opts.body=''] - Body content: TACO, string, or array
|
|
3897
|
+
* @param {string} [opts.title='bitwrench'] - Page title
|
|
3898
|
+
* @param {Object} [opts.state] - State for ${expr} resolution in bw.html()
|
|
3899
|
+
* @param {string} [opts.runtime='shim'] - Runtime level: 'inline'|'cdn'|'shim'|'none'
|
|
3900
|
+
* @param {string} [opts.css=''] - Additional CSS for <style> block
|
|
3901
|
+
* @param {string|Object} [opts.theme=null] - Theme preset name or config object
|
|
3902
|
+
* @param {Array} [opts.head=[]] - Extra TACO elements rendered into <head>
|
|
3903
|
+
* @param {string} [opts.favicon=''] - Favicon URL
|
|
3904
|
+
* @param {string} [opts.lang='en'] - HTML lang attribute
|
|
3905
|
+
* @returns {string} Complete HTML document string
|
|
3906
|
+
* @category DOM Generation
|
|
3907
|
+
* @see bw.html
|
|
3908
|
+
* @example
|
|
3909
|
+
* bw.htmlPage({
|
|
3910
|
+
* title: 'My App',
|
|
3911
|
+
* body: { t: 'h1', c: 'Hello World' },
|
|
3912
|
+
* runtime: 'shim'
|
|
3913
|
+
* })
|
|
3914
|
+
*/
|
|
3915
|
+
bw.htmlPage = function(opts) {
|
|
3916
|
+
opts = opts || {};
|
|
3917
|
+
var title = opts.title || 'bitwrench';
|
|
3918
|
+
var body = opts.body || '';
|
|
3919
|
+
var state = opts.state || undefined;
|
|
3920
|
+
var runtime = opts.runtime || 'shim';
|
|
3921
|
+
var css = opts.css || '';
|
|
3922
|
+
var theme = opts.theme || null;
|
|
3923
|
+
var headExtra = opts.head || [];
|
|
3924
|
+
var favicon = opts.favicon || '';
|
|
3925
|
+
var lang = opts.lang || 'en';
|
|
3926
|
+
|
|
3927
|
+
// Snapshot funcRegistry counter before rendering
|
|
3928
|
+
var fnCounterBefore = bw._fnIDCounter;
|
|
3929
|
+
|
|
3930
|
+
// Render body content
|
|
3931
|
+
var bodyHTML = '';
|
|
3932
|
+
if (_is(body, 'string')) {
|
|
3933
|
+
bodyHTML = body;
|
|
3934
|
+
} else {
|
|
3935
|
+
var htmlOpts = {};
|
|
3936
|
+
if (state) htmlOpts.state = state;
|
|
3937
|
+
bodyHTML = bw.html(body, htmlOpts);
|
|
3938
|
+
}
|
|
3939
|
+
|
|
3940
|
+
// Collect functions registered during this render
|
|
3941
|
+
var fnCounterAfter = bw._fnIDCounter;
|
|
3942
|
+
var registryEntries = '';
|
|
3943
|
+
for (var i = fnCounterBefore; i < fnCounterAfter; i++) {
|
|
3944
|
+
var fnKey = 'bw_fn_' + i;
|
|
3945
|
+
if (bw._fnRegistry[fnKey]) {
|
|
3946
|
+
registryEntries += 'bw._fnRegistry[\'' + fnKey + '\']=' +
|
|
3947
|
+
bw._fnRegistry[fnKey].toString() + ';\n';
|
|
3948
|
+
}
|
|
3949
|
+
}
|
|
3950
|
+
|
|
3951
|
+
// Build runtime script for <head>
|
|
3952
|
+
var runtimeHead = '';
|
|
3953
|
+
if (runtime === 'inline') {
|
|
3954
|
+
// Read UMD bundle synchronously if in Node.js
|
|
3955
|
+
var umdSource = null;
|
|
3956
|
+
if (bw._isNode) {
|
|
3957
|
+
try {
|
|
3958
|
+
var fs = (typeof require === 'function') ? require('fs') : null;
|
|
3959
|
+
var pathMod = (typeof require === 'function') ? require('path') : null;
|
|
3960
|
+
if (fs && pathMod) {
|
|
3961
|
+
// Resolve dist/ relative to this source file
|
|
3962
|
+
var srcDir = '';
|
|
3963
|
+
try { srcDir = pathMod.dirname((typeof __filename !== 'undefined') ? __filename : ''); }
|
|
3964
|
+
catch(e2) { /* ESM: __filename not available */ }
|
|
3965
|
+
if (!srcDir && typeof import.meta !== 'undefined' && import.meta.url) {
|
|
3966
|
+
var url = (typeof require === 'function') ? require('url') : null;
|
|
3967
|
+
if (url && url.fileURLToPath) srcDir = pathMod.dirname(url.fileURLToPath(import.meta.url));
|
|
3968
|
+
}
|
|
3969
|
+
if (srcDir) {
|
|
3970
|
+
var distPath = pathMod.resolve(srcDir, '../dist/bitwrench.umd.min.js');
|
|
3971
|
+
umdSource = fs.readFileSync(distPath, 'utf8');
|
|
3972
|
+
}
|
|
3973
|
+
}
|
|
3974
|
+
} catch(e) { /* fall through */ }
|
|
3975
|
+
}
|
|
3976
|
+
if (umdSource) {
|
|
3977
|
+
runtimeHead = '<script>' + umdSource + '</script>';
|
|
3978
|
+
} else {
|
|
3979
|
+
// Fallback to shim in browser or if dist not available
|
|
3980
|
+
runtimeHead = '<script>' + bw._FUNC_REGISTRY_SHIM + '</script>';
|
|
3981
|
+
}
|
|
3982
|
+
} else if (runtime === 'cdn') {
|
|
3983
|
+
runtimeHead = '<script src="https://cdn.jsdelivr.net/npm/bitwrench@2/dist/bitwrench.umd.min.js"></script>';
|
|
3984
|
+
} else if (runtime === 'shim') {
|
|
3985
|
+
runtimeHead = '<script>' + bw._FUNC_REGISTRY_SHIM + '</script>';
|
|
3986
|
+
}
|
|
3987
|
+
// runtime === 'none' → empty
|
|
3988
|
+
|
|
3989
|
+
// Theme CSS
|
|
3990
|
+
var themeCSS = '';
|
|
3991
|
+
if (theme) {
|
|
3992
|
+
var themeConfig = _is(theme, 'string')
|
|
3993
|
+
? (THEME_PRESETS[theme.toLowerCase()] || null)
|
|
3994
|
+
: theme;
|
|
3995
|
+
if (themeConfig) {
|
|
3996
|
+
var themeResult = bw.generateTheme('', Object.assign({}, themeConfig, { inject: false }));
|
|
3997
|
+
themeCSS = themeResult.css;
|
|
3998
|
+
}
|
|
3999
|
+
}
|
|
4000
|
+
|
|
4001
|
+
// Extra <head> elements
|
|
4002
|
+
var headHTML = '';
|
|
4003
|
+
if (_isA(headExtra) && headExtra.length > 0) {
|
|
4004
|
+
headHTML = headExtra.map(function(el) { return bw.html(el); }).join('\n');
|
|
4005
|
+
}
|
|
4006
|
+
|
|
4007
|
+
// Favicon
|
|
4008
|
+
var faviconTag = '';
|
|
4009
|
+
if (favicon) {
|
|
4010
|
+
var safeFavicon = favicon.replace(/[&<>"']/g, function(c) {
|
|
4011
|
+
return ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[c];
|
|
4012
|
+
});
|
|
4013
|
+
faviconTag = '<link rel="icon" href="' + safeFavicon + '">';
|
|
4014
|
+
}
|
|
4015
|
+
|
|
4016
|
+
// Escaped title
|
|
4017
|
+
var safeTitle = bw.escapeHTML(title);
|
|
4018
|
+
|
|
4019
|
+
// Combine all CSS
|
|
4020
|
+
var allCSS = (themeCSS ? themeCSS + '\n' : '') + css;
|
|
4021
|
+
|
|
4022
|
+
// Body-end script: registry entries + optional loadDefaultStyles
|
|
4023
|
+
var bodyEndScript = '';
|
|
4024
|
+
var bodyEndParts = [];
|
|
4025
|
+
if (registryEntries) {
|
|
4026
|
+
bodyEndParts.push(registryEntries);
|
|
4027
|
+
}
|
|
4028
|
+
if (runtime === 'inline' || runtime === 'cdn') {
|
|
4029
|
+
bodyEndParts.push('if(typeof bw!=="undefined"){bw.loadDefaultStyles();}');
|
|
4030
|
+
}
|
|
4031
|
+
if (bodyEndParts.length > 0) {
|
|
4032
|
+
bodyEndScript = '<script>\n' + bodyEndParts.join('\n') + '\n</script>';
|
|
4033
|
+
}
|
|
4034
|
+
|
|
4035
|
+
// Assemble document
|
|
4036
|
+
var parts = [
|
|
4037
|
+
'<!DOCTYPE html>',
|
|
4038
|
+
'<html lang="' + lang + '">',
|
|
4039
|
+
'<head>',
|
|
4040
|
+
'<meta charset="UTF-8">',
|
|
4041
|
+
'<meta name="viewport" content="width=device-width, initial-scale=1">'
|
|
4042
|
+
];
|
|
4043
|
+
parts.push('<title>' + safeTitle + '</title>');
|
|
4044
|
+
if (faviconTag) parts.push(faviconTag);
|
|
4045
|
+
if (runtimeHead) parts.push(runtimeHead);
|
|
4046
|
+
if (headHTML) parts.push(headHTML);
|
|
4047
|
+
if (allCSS) parts.push('<style>' + allCSS + '</style>');
|
|
4048
|
+
parts.push('</head>');
|
|
4049
|
+
parts.push('<body>');
|
|
4050
|
+
parts.push(bodyHTML);
|
|
4051
|
+
if (bodyEndScript) parts.push(bodyEndScript);
|
|
4052
|
+
parts.push('</body>');
|
|
4053
|
+
parts.push('</html>');
|
|
4054
|
+
|
|
4055
|
+
return parts.join('\n');
|
|
4056
|
+
};
|
|
4057
|
+
|
|
3818
4058
|
/**
|
|
3819
4059
|
* Create a live DOM element from a TACO object (browser only).
|
|
3820
4060
|
*
|
|
@@ -3859,7 +4099,7 @@ bw.createDOM = function(taco, options = {}) {
|
|
|
3859
4099
|
}
|
|
3860
4100
|
|
|
3861
4101
|
// Handle text nodes
|
|
3862
|
-
if (
|
|
4102
|
+
if (!_is(taco, 'object') || !taco.t) {
|
|
3863
4103
|
return document.createTextNode(String(taco));
|
|
3864
4104
|
}
|
|
3865
4105
|
|
|
@@ -3872,16 +4112,16 @@ bw.createDOM = function(taco, options = {}) {
|
|
|
3872
4112
|
for (const [key, value] of Object.entries(attrs)) {
|
|
3873
4113
|
if (value == null || value === false) continue;
|
|
3874
4114
|
|
|
3875
|
-
if (key === 'style' &&
|
|
4115
|
+
if (key === 'style' && _is(value, 'object')) {
|
|
3876
4116
|
// Apply styles directly
|
|
3877
4117
|
Object.assign(el.style, value);
|
|
3878
4118
|
} else if (key === 'class') {
|
|
3879
4119
|
// Handle class as array or string
|
|
3880
|
-
const classStr =
|
|
4120
|
+
const classStr = _isA(value) ? value.filter(Boolean).join(' ') : String(value);
|
|
3881
4121
|
if (classStr) {
|
|
3882
4122
|
el.className = classStr;
|
|
3883
4123
|
}
|
|
3884
|
-
} else if (key.startsWith('on') &&
|
|
4124
|
+
} else if (key.startsWith('on') && _is(value, 'function')) {
|
|
3885
4125
|
// Event handlers
|
|
3886
4126
|
const eventName = key.slice(2).toLowerCase();
|
|
3887
4127
|
el.addEventListener(eventName, value);
|
|
@@ -3901,7 +4141,7 @@ bw.createDOM = function(taco, options = {}) {
|
|
|
3901
4141
|
// Children with data-bw_id or id attributes get local refs on the parent,
|
|
3902
4142
|
// so o.render functions can access them without any DOM lookup.
|
|
3903
4143
|
if (content != null) {
|
|
3904
|
-
if (
|
|
4144
|
+
if (_isA(content)) {
|
|
3905
4145
|
content.forEach(child => {
|
|
3906
4146
|
if (child != null) {
|
|
3907
4147
|
// Handle ComponentHandle in content arrays (Level 2 children)
|
|
@@ -3921,20 +4161,20 @@ bw.createDOM = function(taco, options = {}) {
|
|
|
3921
4161
|
if (childEl._bw_refs) {
|
|
3922
4162
|
if (!el._bw_refs) el._bw_refs = {};
|
|
3923
4163
|
for (var rk in childEl._bw_refs) {
|
|
3924
|
-
if (
|
|
4164
|
+
if (_hop.call(childEl._bw_refs, rk)) {
|
|
3925
4165
|
el._bw_refs[rk] = childEl._bw_refs[rk];
|
|
3926
4166
|
}
|
|
3927
4167
|
}
|
|
3928
4168
|
}
|
|
3929
4169
|
}
|
|
3930
4170
|
});
|
|
3931
|
-
} else if (
|
|
4171
|
+
} else if (_is(content, 'object') && content.__bw_raw) {
|
|
3932
4172
|
// Raw HTML content — inject via innerHTML
|
|
3933
4173
|
el.innerHTML = content.v;
|
|
3934
4174
|
} else if (content._bwComponent === true) {
|
|
3935
4175
|
// Single ComponentHandle as content
|
|
3936
4176
|
content.mount(el);
|
|
3937
|
-
} else if (
|
|
4177
|
+
} else if (_is(content, 'object') && content.t) {
|
|
3938
4178
|
var childEl = bw.createDOM(content, options);
|
|
3939
4179
|
el.appendChild(childEl);
|
|
3940
4180
|
var childBwId = content.a ? (content.a['data-bw_id'] || content.a.id) : null;
|
|
@@ -3945,7 +4185,7 @@ bw.createDOM = function(taco, options = {}) {
|
|
|
3945
4185
|
if (childEl._bw_refs) {
|
|
3946
4186
|
if (!el._bw_refs) el._bw_refs = {};
|
|
3947
4187
|
for (var rk in childEl._bw_refs) {
|
|
3948
|
-
if (
|
|
4188
|
+
if (_hop.call(childEl._bw_refs, rk)) {
|
|
3949
4189
|
el._bw_refs[rk] = childEl._bw_refs[rk];
|
|
3950
4190
|
}
|
|
3951
4191
|
}
|
|
@@ -3978,7 +4218,7 @@ bw.createDOM = function(taco, options = {}) {
|
|
|
3978
4218
|
el._bw_render = opts.render;
|
|
3979
4219
|
|
|
3980
4220
|
if (opts.mounted) {
|
|
3981
|
-
|
|
4221
|
+
_cw('bw.createDOM: o.render and o.mounted are mutually exclusive. o.render wins.');
|
|
3982
4222
|
}
|
|
3983
4223
|
|
|
3984
4224
|
// Queue initial render (same timing as mounted)
|
|
@@ -4051,7 +4291,7 @@ bw.DOM = function(target, taco, options = {}) {
|
|
|
4051
4291
|
const targetEl = bw._el(target);
|
|
4052
4292
|
|
|
4053
4293
|
if (!targetEl) {
|
|
4054
|
-
|
|
4294
|
+
_ce('bw.DOM: Target element not found:', target);
|
|
4055
4295
|
return null;
|
|
4056
4296
|
}
|
|
4057
4297
|
|
|
@@ -4091,7 +4331,7 @@ bw.DOM = function(target, taco, options = {}) {
|
|
|
4091
4331
|
targetEl.appendChild(taco.element);
|
|
4092
4332
|
}
|
|
4093
4333
|
// Handle arrays
|
|
4094
|
-
else if (
|
|
4334
|
+
else if (_isA(taco)) {
|
|
4095
4335
|
taco.forEach(t => {
|
|
4096
4336
|
if (t != null) {
|
|
4097
4337
|
if (t._bwComponent === true) {
|
|
@@ -4127,7 +4367,7 @@ bw.DOM = function(target, taco, options = {}) {
|
|
|
4127
4367
|
bw.compileProps = function(handle, props = {}) {
|
|
4128
4368
|
const compiledProps = {};
|
|
4129
4369
|
|
|
4130
|
-
|
|
4370
|
+
_keys(props).forEach(key => {
|
|
4131
4371
|
// Create getter/setter for each prop
|
|
4132
4372
|
Object.defineProperty(compiledProps, key, {
|
|
4133
4373
|
get() {
|
|
@@ -4445,17 +4685,17 @@ bw.patch = function(id, content, attr) {
|
|
|
4445
4685
|
if (attr) {
|
|
4446
4686
|
// Patch an attribute
|
|
4447
4687
|
el.setAttribute(attr, String(content));
|
|
4448
|
-
} else if (
|
|
4688
|
+
} else if (_isA(content)) {
|
|
4449
4689
|
// Patch with array of children (strings and/or TACOs)
|
|
4450
4690
|
el.innerHTML = '';
|
|
4451
4691
|
content.forEach(function(item) {
|
|
4452
|
-
if (
|
|
4692
|
+
if (_is(item, 'string') || _is(item, 'number')) {
|
|
4453
4693
|
el.appendChild(document.createTextNode(String(item)));
|
|
4454
4694
|
} else if (item && item.t) {
|
|
4455
4695
|
el.appendChild(bw.createDOM(item));
|
|
4456
4696
|
}
|
|
4457
4697
|
});
|
|
4458
|
-
} else if (
|
|
4698
|
+
} else if (_is(content, 'object') && content.t) {
|
|
4459
4699
|
// Patch with a TACO — replace children
|
|
4460
4700
|
el.innerHTML = '';
|
|
4461
4701
|
el.appendChild(bw.createDOM(content));
|
|
@@ -4486,7 +4726,7 @@ bw.patch = function(id, content, attr) {
|
|
|
4486
4726
|
bw.patchAll = function(patches) {
|
|
4487
4727
|
var results = {};
|
|
4488
4728
|
for (var id in patches) {
|
|
4489
|
-
if (
|
|
4729
|
+
if (_hop.call(patches, id)) {
|
|
4490
4730
|
results[id] = bw.patch(id, patches[id]);
|
|
4491
4731
|
}
|
|
4492
4732
|
}
|
|
@@ -4583,7 +4823,7 @@ bw.pub = function(topic, detail) {
|
|
|
4583
4823
|
snapshot[i].handler(detail);
|
|
4584
4824
|
called++;
|
|
4585
4825
|
} catch (err) {
|
|
4586
|
-
|
|
4826
|
+
_cw('bw.pub: subscriber error on topic "' + topic + '":', err);
|
|
4587
4827
|
}
|
|
4588
4828
|
}
|
|
4589
4829
|
return called;
|
|
@@ -4679,8 +4919,8 @@ bw._fnIDCounter = 0;
|
|
|
4679
4919
|
* @see bw.funcGetDispatchStr
|
|
4680
4920
|
*/
|
|
4681
4921
|
bw.funcRegister = function(fn, name) {
|
|
4682
|
-
if (
|
|
4683
|
-
var fnID = (
|
|
4922
|
+
if (!_is(fn, 'function')) return '';
|
|
4923
|
+
var fnID = (_is(name, 'string') && name.length > 0) ? name : ('bw_fn_' + bw._fnIDCounter++);
|
|
4684
4924
|
bw._fnRegistry[fnID] = fn;
|
|
4685
4925
|
return fnID;
|
|
4686
4926
|
};
|
|
@@ -4699,7 +4939,7 @@ bw.funcRegister = function(fn, name) {
|
|
|
4699
4939
|
bw.funcGetById = function(name, errFn) {
|
|
4700
4940
|
name = String(name);
|
|
4701
4941
|
if (name in bw._fnRegistry) return bw._fnRegistry[name];
|
|
4702
|
-
return (
|
|
4942
|
+
return _is(errFn, 'function') ? errFn : function() { _cw('bw.funcGetById: unregistered fn "' + name + '"'); };
|
|
4703
4943
|
};
|
|
4704
4944
|
|
|
4705
4945
|
/**
|
|
@@ -4740,13 +4980,30 @@ bw.funcUnregister = function(name) {
|
|
|
4740
4980
|
bw.funcGetRegistry = function() {
|
|
4741
4981
|
var copy = {};
|
|
4742
4982
|
for (var k in bw._fnRegistry) {
|
|
4743
|
-
if (
|
|
4983
|
+
if (_hop.call(bw._fnRegistry, k)) {
|
|
4744
4984
|
copy[k] = bw._fnRegistry[k];
|
|
4745
4985
|
}
|
|
4746
4986
|
}
|
|
4747
4987
|
return copy;
|
|
4748
4988
|
};
|
|
4749
4989
|
|
|
4990
|
+
/**
|
|
4991
|
+
* Minimal runtime shim for funcRegister dispatch in static HTML.
|
|
4992
|
+
* When embedded in a `<script>` tag, provides just enough infrastructure
|
|
4993
|
+
* for `bw.funcGetById()` calls to resolve. The actual function bodies
|
|
4994
|
+
* are emitted separately as `bw._fnRegistry['bw_fn_X'] = ...;` assignments.
|
|
4995
|
+
* @type {string}
|
|
4996
|
+
* @category Function Registry
|
|
4997
|
+
*/
|
|
4998
|
+
bw._FUNC_REGISTRY_SHIM = '(function(){var bw=window.bw||(window.bw={});' +
|
|
4999
|
+
'if(!bw._fnRegistry)bw._fnRegistry={};' +
|
|
5000
|
+
'bw.funcGetById=function(n){return bw._fnRegistry[n]||function(){' +
|
|
5001
|
+
'console.warn("bw: unregistered fn "+n)};};' +
|
|
5002
|
+
'bw.funcRegister=function(fn,name){' +
|
|
5003
|
+
'var id=name||("bw_fn_"+(bw._fnIDCounter=(bw._fnIDCounter||0)+1));' +
|
|
5004
|
+
'bw._fnRegistry[id]=fn;return id;};' +
|
|
5005
|
+
'window.bw=bw;})();';
|
|
5006
|
+
|
|
4750
5007
|
// ===================================================================================
|
|
4751
5008
|
// Template Binding Utilities
|
|
4752
5009
|
// ===================================================================================
|
|
@@ -4774,7 +5031,10 @@ bw._evaluatePath = function(state, path) {
|
|
|
4774
5031
|
var parts = path.split('.');
|
|
4775
5032
|
var val = state;
|
|
4776
5033
|
for (var i = 0; i < parts.length; i++) {
|
|
4777
|
-
if (val == null)
|
|
5034
|
+
if (val == null) {
|
|
5035
|
+
if (bw.debug) _cw('bw.debug: _evaluatePath — null at key "' + parts[i] + '" in path "' + path + '"');
|
|
5036
|
+
return '';
|
|
5037
|
+
}
|
|
4778
5038
|
val = val[parts[i]];
|
|
4779
5039
|
}
|
|
4780
5040
|
return (val == null) ? '' : val;
|
|
@@ -4794,7 +5054,7 @@ bw._evaluatePath = function(state, path) {
|
|
|
4794
5054
|
*/
|
|
4795
5055
|
bw._compiledExprs = {};
|
|
4796
5056
|
bw._resolveTemplate = function(str, state, compile) {
|
|
4797
|
-
if (
|
|
5057
|
+
if (!_is(str, 'string') || str.indexOf('${') < 0) return str;
|
|
4798
5058
|
var bindings = bw._parseBindings(str);
|
|
4799
5059
|
if (bindings.length === 0) return str;
|
|
4800
5060
|
|
|
@@ -4816,6 +5076,7 @@ bw._resolveTemplate = function(str, state, compile) {
|
|
|
4816
5076
|
try {
|
|
4817
5077
|
val = bw._compiledExprs[b.expr](state);
|
|
4818
5078
|
} catch (e) {
|
|
5079
|
+
if (bw.debug) _cw('bw.debug: _resolveTemplate — Tier 2 eval failed for "${' + b.expr + '}":', e.message);
|
|
4819
5080
|
val = '';
|
|
4820
5081
|
}
|
|
4821
5082
|
} else {
|
|
@@ -4924,7 +5185,7 @@ function ComponentHandle(taco) {
|
|
|
4924
5185
|
this._state = {};
|
|
4925
5186
|
if (o.state) {
|
|
4926
5187
|
for (var k in o.state) {
|
|
4927
|
-
if (
|
|
5188
|
+
if (_hop.call(o.state, k)) {
|
|
4928
5189
|
this._state[k] = o.state[k];
|
|
4929
5190
|
}
|
|
4930
5191
|
}
|
|
@@ -4933,7 +5194,7 @@ function ComponentHandle(taco) {
|
|
|
4933
5194
|
this._actions = {};
|
|
4934
5195
|
if (o.actions) {
|
|
4935
5196
|
for (var k2 in o.actions) {
|
|
4936
|
-
if (
|
|
5197
|
+
if (_hop.call(o.actions, k2)) {
|
|
4937
5198
|
this._actions[k2] = o.actions[k2];
|
|
4938
5199
|
}
|
|
4939
5200
|
}
|
|
@@ -4943,7 +5204,7 @@ function ComponentHandle(taco) {
|
|
|
4943
5204
|
if (o.methods) {
|
|
4944
5205
|
var self = this;
|
|
4945
5206
|
for (var k3 in o.methods) {
|
|
4946
|
-
if (
|
|
5207
|
+
if (_hop.call(o.methods, k3)) {
|
|
4947
5208
|
this._methods[k3] = o.methods[k3];
|
|
4948
5209
|
(function(methodName, methodFn) {
|
|
4949
5210
|
self[methodName] = function() {
|
|
@@ -4976,14 +5237,23 @@ function ComponentHandle(taco) {
|
|
|
4976
5237
|
this._compile = !!o.compile;
|
|
4977
5238
|
this._bw_refs = {};
|
|
4978
5239
|
this._refCounter = 0;
|
|
5240
|
+
// Child component ownership (Bug #5)
|
|
5241
|
+
this._children = [];
|
|
5242
|
+
this._parent = null;
|
|
5243
|
+
// Factory metadata for BCCL rebuild (Bug #6)
|
|
5244
|
+
this._factory = taco._bwFactory || null;
|
|
4979
5245
|
}
|
|
4980
5246
|
|
|
5247
|
+
// Short alias for ComponentHandle.prototype (see alias block at top of file).
|
|
5248
|
+
// 28 method definitions × 25 chars = ~700B raw savings in minified output.
|
|
5249
|
+
var _chp = ComponentHandle.prototype;
|
|
5250
|
+
|
|
4981
5251
|
// ── State Methods ──
|
|
4982
5252
|
|
|
4983
5253
|
/**
|
|
4984
5254
|
* Get a state value. Dot-path supported: `get('user.name')`
|
|
4985
5255
|
*/
|
|
4986
|
-
|
|
5256
|
+
_chp.get = function(key) {
|
|
4987
5257
|
return bw._evaluatePath(this._state, key);
|
|
4988
5258
|
};
|
|
4989
5259
|
|
|
@@ -4993,12 +5263,13 @@ ComponentHandle.prototype.get = function(key) {
|
|
|
4993
5263
|
* @param {*} value - New value
|
|
4994
5264
|
* @param {Object} [opts] - Options. `{sync: true}` for immediate flush.
|
|
4995
5265
|
*/
|
|
4996
|
-
|
|
5266
|
+
_chp.set = function(key, value, opts) {
|
|
4997
5267
|
// Dot-path set
|
|
4998
5268
|
var parts = key.split('.');
|
|
4999
5269
|
var obj = this._state;
|
|
5000
5270
|
for (var i = 0; i < parts.length - 1; i++) {
|
|
5001
|
-
if (obj[parts[i]]
|
|
5271
|
+
if (!_is(obj[parts[i]], 'object')) {
|
|
5272
|
+
if (bw.debug) _cw('bw.debug: set() — auto-creating intermediate "' + parts[i] + '" in path "' + key + '"');
|
|
5002
5273
|
obj[parts[i]] = {};
|
|
5003
5274
|
}
|
|
5004
5275
|
obj = obj[parts[i]];
|
|
@@ -5018,10 +5289,10 @@ ComponentHandle.prototype.set = function(key, value, opts) {
|
|
|
5018
5289
|
/**
|
|
5019
5290
|
* Get a shallow clone of the full state.
|
|
5020
5291
|
*/
|
|
5021
|
-
|
|
5292
|
+
_chp.getState = function() {
|
|
5022
5293
|
var clone = {};
|
|
5023
5294
|
for (var k in this._state) {
|
|
5024
|
-
if (
|
|
5295
|
+
if (_hop.call(this._state, k)) {
|
|
5025
5296
|
clone[k] = this._state[k];
|
|
5026
5297
|
}
|
|
5027
5298
|
}
|
|
@@ -5033,9 +5304,9 @@ ComponentHandle.prototype.getState = function() {
|
|
|
5033
5304
|
* @param {Object} updates - Key-value pairs to merge
|
|
5034
5305
|
* @param {Object} [opts] - Options. `{sync: true}` for immediate flush.
|
|
5035
5306
|
*/
|
|
5036
|
-
|
|
5307
|
+
_chp.setState = function(updates, opts) {
|
|
5037
5308
|
for (var k in updates) {
|
|
5038
|
-
if (
|
|
5309
|
+
if (_hop.call(updates, k)) {
|
|
5039
5310
|
this._state[k] = updates[k];
|
|
5040
5311
|
this._dirtyKeys[k] = true;
|
|
5041
5312
|
}
|
|
@@ -5052,9 +5323,9 @@ ComponentHandle.prototype.setState = function(updates, opts) {
|
|
|
5052
5323
|
/**
|
|
5053
5324
|
* Push a value onto an array in state. Clones the array.
|
|
5054
5325
|
*/
|
|
5055
|
-
|
|
5326
|
+
_chp.push = function(key, val) {
|
|
5056
5327
|
var arr = this.get(key);
|
|
5057
|
-
var newArr =
|
|
5328
|
+
var newArr = _isA(arr) ? arr.slice() : [];
|
|
5058
5329
|
newArr.push(val);
|
|
5059
5330
|
this.set(key, newArr);
|
|
5060
5331
|
};
|
|
@@ -5062,9 +5333,9 @@ ComponentHandle.prototype.push = function(key, val) {
|
|
|
5062
5333
|
/**
|
|
5063
5334
|
* Splice an array in state. Clones the array.
|
|
5064
5335
|
*/
|
|
5065
|
-
|
|
5336
|
+
_chp.splice = function(key, start, deleteCount) {
|
|
5066
5337
|
var arr = this.get(key);
|
|
5067
|
-
var newArr =
|
|
5338
|
+
var newArr = _isA(arr) ? arr.slice() : [];
|
|
5068
5339
|
var args = [start, deleteCount].concat(Array.prototype.slice.call(arguments, 3));
|
|
5069
5340
|
Array.prototype.splice.apply(newArr, args);
|
|
5070
5341
|
this.set(key, newArr);
|
|
@@ -5072,7 +5343,7 @@ ComponentHandle.prototype.splice = function(key, start, deleteCount) {
|
|
|
5072
5343
|
|
|
5073
5344
|
// ── Scheduling ──
|
|
5074
5345
|
|
|
5075
|
-
|
|
5346
|
+
_chp._scheduleDirty = function() {
|
|
5076
5347
|
if (!this._scheduled) {
|
|
5077
5348
|
this._scheduled = true;
|
|
5078
5349
|
bw._dirtyComponents.push(this);
|
|
@@ -5087,17 +5358,17 @@ ComponentHandle.prototype._scheduleDirty = function() {
|
|
|
5087
5358
|
* Creates binding descriptors with refIds for targeted DOM updates.
|
|
5088
5359
|
* @private
|
|
5089
5360
|
*/
|
|
5090
|
-
|
|
5361
|
+
_chp._compileBindings = function() {
|
|
5091
5362
|
this._bindings = [];
|
|
5092
5363
|
this._refCounter = 0;
|
|
5093
|
-
var stateKeys =
|
|
5364
|
+
var stateKeys = _keys(this._state);
|
|
5094
5365
|
var self = this;
|
|
5095
5366
|
|
|
5096
5367
|
function walkTaco(taco, path) {
|
|
5097
|
-
if (taco
|
|
5368
|
+
if (!_is(taco, 'object') || !taco.t) return taco;
|
|
5098
5369
|
|
|
5099
5370
|
// Check content for bindings
|
|
5100
|
-
if (
|
|
5371
|
+
if (_is(taco.c, 'string') && taco.c.indexOf('${') >= 0) {
|
|
5101
5372
|
var refId = 'bw_ref_' + self._refCounter++;
|
|
5102
5373
|
var parsed = bw._parseBindings(taco.c);
|
|
5103
5374
|
var deps = [];
|
|
@@ -5119,10 +5390,10 @@ ComponentHandle.prototype._compileBindings = function() {
|
|
|
5119
5390
|
// Check attributes for bindings
|
|
5120
5391
|
if (taco.a) {
|
|
5121
5392
|
for (var attrName in taco.a) {
|
|
5122
|
-
if (!
|
|
5393
|
+
if (!_hop.call(taco.a, attrName)) continue;
|
|
5123
5394
|
if (attrName === 'data-bw_ref') continue;
|
|
5124
5395
|
var attrVal = taco.a[attrName];
|
|
5125
|
-
if (
|
|
5396
|
+
if (_is(attrVal, 'string') && attrVal.indexOf('${') >= 0) {
|
|
5126
5397
|
var refId2 = 'bw_ref_' + self._refCounter++;
|
|
5127
5398
|
var parsed2 = bw._parseBindings(attrVal);
|
|
5128
5399
|
var deps2 = [];
|
|
@@ -5148,9 +5419,27 @@ ComponentHandle.prototype._compileBindings = function() {
|
|
|
5148
5419
|
}
|
|
5149
5420
|
|
|
5150
5421
|
// Recurse into children
|
|
5151
|
-
if (
|
|
5422
|
+
if (_isA(taco.c)) {
|
|
5152
5423
|
for (var i = 0; i < taco.c.length; i++) {
|
|
5153
|
-
|
|
5424
|
+
// Wrap string children with ${expr} in a span so patches target the span, not the parent
|
|
5425
|
+
if (_is(taco.c[i], 'string') && taco.c[i].indexOf('${') >= 0) {
|
|
5426
|
+
var mixedRefId = 'bw_ref_' + self._refCounter++;
|
|
5427
|
+
var mixedParsed = bw._parseBindings(taco.c[i]);
|
|
5428
|
+
var mixedDeps = [];
|
|
5429
|
+
for (var mi = 0; mi < mixedParsed.length; mi++) {
|
|
5430
|
+
mixedDeps = mixedDeps.concat(bw._extractDeps(mixedParsed[mi].expr, stateKeys));
|
|
5431
|
+
}
|
|
5432
|
+
self._bindings.push({
|
|
5433
|
+
expr: taco.c[i],
|
|
5434
|
+
type: 'content',
|
|
5435
|
+
refId: mixedRefId,
|
|
5436
|
+
deps: mixedDeps,
|
|
5437
|
+
template: taco.c[i]
|
|
5438
|
+
});
|
|
5439
|
+
// Replace string with a span wrapper so textContent targets the span only
|
|
5440
|
+
taco.c[i] = { t: 'span', a: { 'data-bw_ref': mixedRefId, style: 'display:contents' }, c: taco.c[i] };
|
|
5441
|
+
}
|
|
5442
|
+
if (_is(taco.c[i], 'object') && taco.c[i].t) {
|
|
5154
5443
|
walkTaco(taco.c[i], path.concat(i));
|
|
5155
5444
|
}
|
|
5156
5445
|
// Handle bw.when/bw.each markers
|
|
@@ -5185,7 +5474,7 @@ ComponentHandle.prototype._compileBindings = function() {
|
|
|
5185
5474
|
taco.c[i]._refId = eachRefId;
|
|
5186
5475
|
}
|
|
5187
5476
|
}
|
|
5188
|
-
} else if (taco.c
|
|
5477
|
+
} else if (_is(taco.c, 'object') && taco.c.t) {
|
|
5189
5478
|
walkTaco(taco.c, path.concat(0));
|
|
5190
5479
|
}
|
|
5191
5480
|
|
|
@@ -5201,7 +5490,7 @@ ComponentHandle.prototype._compileBindings = function() {
|
|
|
5201
5490
|
* Build ref map from the live DOM after createDOM.
|
|
5202
5491
|
* @private
|
|
5203
5492
|
*/
|
|
5204
|
-
|
|
5493
|
+
_chp._collectRefs = function() {
|
|
5205
5494
|
this._bw_refs = {};
|
|
5206
5495
|
if (!this.element) return;
|
|
5207
5496
|
var els = this.element.querySelectorAll('[data-bw_ref]');
|
|
@@ -5222,7 +5511,7 @@ ComponentHandle.prototype._collectRefs = function() {
|
|
|
5222
5511
|
* Creates DOM, compiles bindings, registers actions, and calls lifecycle hooks.
|
|
5223
5512
|
* @param {Element} parentEl - DOM element to mount into
|
|
5224
5513
|
*/
|
|
5225
|
-
|
|
5514
|
+
_chp.mount = function(parentEl) {
|
|
5226
5515
|
// willMount hook
|
|
5227
5516
|
if (this._hooks.willMount) this._hooks.willMount(this);
|
|
5228
5517
|
|
|
@@ -5244,7 +5533,7 @@ ComponentHandle.prototype.mount = function(parentEl) {
|
|
|
5244
5533
|
// Register named actions in function registry
|
|
5245
5534
|
var self = this;
|
|
5246
5535
|
for (var actionName in this._actions) {
|
|
5247
|
-
if (
|
|
5536
|
+
if (_hop.call(this._actions, actionName)) {
|
|
5248
5537
|
var registeredName = this._bwId + '_' + actionName;
|
|
5249
5538
|
(function(aName) {
|
|
5250
5539
|
bw.funcRegister(function(evt) {
|
|
@@ -5263,6 +5552,11 @@ ComponentHandle.prototype.mount = function(parentEl) {
|
|
|
5263
5552
|
this.element = bw.createDOM(tacoForDOM);
|
|
5264
5553
|
this.element._bwComponentHandle = this;
|
|
5265
5554
|
this.element.setAttribute('data-bw_comp_id', this._bwId);
|
|
5555
|
+
|
|
5556
|
+
// Restore o.render from original TACO (stripped by _tacoForDOM)
|
|
5557
|
+
if (this.taco.o && this.taco.o.render) {
|
|
5558
|
+
this.element._bw_render = this.taco.o.render;
|
|
5559
|
+
}
|
|
5266
5560
|
if (this._userTag) {
|
|
5267
5561
|
this.element.classList.add(this._userTag);
|
|
5268
5562
|
}
|
|
@@ -5278,6 +5572,16 @@ ComponentHandle.prototype.mount = function(parentEl) {
|
|
|
5278
5572
|
|
|
5279
5573
|
this.mounted = true;
|
|
5280
5574
|
|
|
5575
|
+
// Scan for child ComponentHandles and link parent/child (Bug #5)
|
|
5576
|
+
var childEls = this.element.querySelectorAll('[data-bw_comp_id]');
|
|
5577
|
+
for (var ci = 0; ci < childEls.length; ci++) {
|
|
5578
|
+
var ch = childEls[ci]._bwComponentHandle;
|
|
5579
|
+
if (ch && ch !== this && !ch._parent) {
|
|
5580
|
+
ch._parent = this;
|
|
5581
|
+
this._children.push(ch);
|
|
5582
|
+
}
|
|
5583
|
+
}
|
|
5584
|
+
|
|
5281
5585
|
// mounted hook (backward compat: fn.length === 2 wraps (el, state))
|
|
5282
5586
|
if (this._hooks.mounted) {
|
|
5283
5587
|
if (this._hooks.mounted.length === 2) {
|
|
@@ -5286,16 +5590,21 @@ ComponentHandle.prototype.mount = function(parentEl) {
|
|
|
5286
5590
|
this._hooks.mounted(this);
|
|
5287
5591
|
}
|
|
5288
5592
|
}
|
|
5593
|
+
|
|
5594
|
+
// Invoke o.render on initial mount (if present)
|
|
5595
|
+
if (this.element._bw_render) {
|
|
5596
|
+
this.element._bw_render(this.element, this._state);
|
|
5597
|
+
}
|
|
5289
5598
|
};
|
|
5290
5599
|
|
|
5291
5600
|
/**
|
|
5292
5601
|
* Prepare TACO for initial render: resolve when/each markers.
|
|
5293
5602
|
* @private
|
|
5294
5603
|
*/
|
|
5295
|
-
|
|
5296
|
-
if (!taco
|
|
5604
|
+
_chp._prepareTaco = function(taco) {
|
|
5605
|
+
if (!_is(taco, 'object')) return;
|
|
5297
5606
|
|
|
5298
|
-
if (
|
|
5607
|
+
if (_isA(taco.c)) {
|
|
5299
5608
|
for (var i = taco.c.length - 1; i >= 0; i--) {
|
|
5300
5609
|
var child = taco.c[i];
|
|
5301
5610
|
if (child && child._bwWhen) {
|
|
@@ -5320,18 +5629,18 @@ ComponentHandle.prototype._prepareTaco = function(taco) {
|
|
|
5320
5629
|
var eachExprStr = child.expr.replace(/^\$\{|\}$/g, '');
|
|
5321
5630
|
var arr = bw._evaluatePath(this._state, eachExprStr);
|
|
5322
5631
|
var items = [];
|
|
5323
|
-
if (
|
|
5632
|
+
if (_isA(arr)) {
|
|
5324
5633
|
for (var j = 0; j < arr.length; j++) {
|
|
5325
5634
|
items.push(child.factory(arr[j], j));
|
|
5326
5635
|
}
|
|
5327
5636
|
}
|
|
5328
5637
|
taco.c[i] = { t: 'span', a: { 'data-bw_each': child._refId, style: 'display:contents' }, c: items };
|
|
5329
5638
|
}
|
|
5330
|
-
if (taco.c[i]
|
|
5639
|
+
if (_is(taco.c[i], 'object') && taco.c[i].t) {
|
|
5331
5640
|
this._prepareTaco(taco.c[i]);
|
|
5332
5641
|
}
|
|
5333
5642
|
}
|
|
5334
|
-
} else if (taco.c
|
|
5643
|
+
} else if (_is(taco.c, 'object') && taco.c.t) {
|
|
5335
5644
|
this._prepareTaco(taco.c);
|
|
5336
5645
|
}
|
|
5337
5646
|
};
|
|
@@ -5340,12 +5649,12 @@ ComponentHandle.prototype._prepareTaco = function(taco) {
|
|
|
5340
5649
|
* Wire action name strings (in onclick etc.) to dispatch function calls.
|
|
5341
5650
|
* @private
|
|
5342
5651
|
*/
|
|
5343
|
-
|
|
5344
|
-
if (!taco
|
|
5652
|
+
_chp._wireActions = function(taco) {
|
|
5653
|
+
if (!_is(taco, 'object') || !taco.t) return;
|
|
5345
5654
|
if (taco.a) {
|
|
5346
5655
|
for (var key in taco.a) {
|
|
5347
|
-
if (!
|
|
5348
|
-
if (key.startsWith('on') &&
|
|
5656
|
+
if (!_hop.call(taco.a, key)) continue;
|
|
5657
|
+
if (key.startsWith('on') && _is(taco.a[key], 'string')) {
|
|
5349
5658
|
var actionName = taco.a[key];
|
|
5350
5659
|
if (actionName in this._actions) {
|
|
5351
5660
|
var registeredName = this._bwId + '_' + actionName;
|
|
@@ -5359,11 +5668,11 @@ ComponentHandle.prototype._wireActions = function(taco) {
|
|
|
5359
5668
|
}
|
|
5360
5669
|
}
|
|
5361
5670
|
}
|
|
5362
|
-
if (
|
|
5671
|
+
if (_isA(taco.c)) {
|
|
5363
5672
|
for (var i = 0; i < taco.c.length; i++) {
|
|
5364
5673
|
this._wireActions(taco.c[i]);
|
|
5365
5674
|
}
|
|
5366
|
-
} else if (taco.c
|
|
5675
|
+
} else if (_is(taco.c, 'object') && taco.c.t) {
|
|
5367
5676
|
this._wireActions(taco.c);
|
|
5368
5677
|
}
|
|
5369
5678
|
};
|
|
@@ -5372,7 +5681,7 @@ ComponentHandle.prototype._wireActions = function(taco) {
|
|
|
5372
5681
|
* Deep-clone a TACO tree, preserving _bwWhen/_bwEach markers and their factories.
|
|
5373
5682
|
* @private
|
|
5374
5683
|
*/
|
|
5375
|
-
|
|
5684
|
+
_chp._deepCloneTaco = function(taco) {
|
|
5376
5685
|
if (taco == null) return taco;
|
|
5377
5686
|
// Preserve _bwWhen / _bwEach markers (contain functions)
|
|
5378
5687
|
if (taco._bwWhen) {
|
|
@@ -5384,18 +5693,18 @@ ComponentHandle.prototype._deepCloneTaco = function(taco) {
|
|
|
5384
5693
|
if (taco._bwEach) {
|
|
5385
5694
|
return { _bwEach: true, expr: taco.expr, factory: taco.factory, _refId: taco._refId };
|
|
5386
5695
|
}
|
|
5387
|
-
if (
|
|
5696
|
+
if (!_is(taco, 'object') || !taco.t) return taco;
|
|
5388
5697
|
var result = { t: taco.t };
|
|
5389
5698
|
if (taco.a) {
|
|
5390
5699
|
result.a = {};
|
|
5391
5700
|
for (var k in taco.a) {
|
|
5392
|
-
if (
|
|
5701
|
+
if (_hop.call(taco.a, k)) result.a[k] = taco.a[k];
|
|
5393
5702
|
}
|
|
5394
5703
|
}
|
|
5395
5704
|
if (taco.c != null) {
|
|
5396
|
-
if (
|
|
5705
|
+
if (_isA(taco.c)) {
|
|
5397
5706
|
result.c = taco.c.map(function(child) { return this._deepCloneTaco(child); }.bind(this));
|
|
5398
|
-
} else if (
|
|
5707
|
+
} else if (_is(taco.c, 'object')) {
|
|
5399
5708
|
result.c = this._deepCloneTaco(taco.c);
|
|
5400
5709
|
} else {
|
|
5401
5710
|
result.c = taco.c;
|
|
@@ -5409,27 +5718,31 @@ ComponentHandle.prototype._deepCloneTaco = function(taco) {
|
|
|
5409
5718
|
* Create a copy of TACO suitable for createDOM (strips o to prevent double lifecycle).
|
|
5410
5719
|
* @private
|
|
5411
5720
|
*/
|
|
5412
|
-
|
|
5413
|
-
if (!taco
|
|
5721
|
+
_chp._tacoForDOM = function(taco) {
|
|
5722
|
+
if (!_is(taco, 'object') || !taco.t) return taco;
|
|
5414
5723
|
var result = { t: taco.t };
|
|
5415
5724
|
if (taco.a) result.a = taco.a;
|
|
5416
5725
|
if (taco.c != null) {
|
|
5417
|
-
if (
|
|
5726
|
+
if (_isA(taco.c)) {
|
|
5418
5727
|
result.c = taco.c.map(function(child) { return this._tacoForDOM(child); }.bind(this));
|
|
5419
|
-
} else if (
|
|
5728
|
+
} else if (_is(taco.c, 'object') && taco.c.t) {
|
|
5420
5729
|
result.c = this._tacoForDOM(taco.c);
|
|
5421
5730
|
} else {
|
|
5422
5731
|
result.c = taco.c;
|
|
5423
5732
|
}
|
|
5424
5733
|
}
|
|
5425
5734
|
// Intentionally strip o (no mounted/unmount/state/render on sub-elements)
|
|
5735
|
+
if (taco.o && (taco.o.mounted || taco.o.render || taco.o.unmount)) {
|
|
5736
|
+
_cw('bw: _tacoForDOM stripped o.mounted/render/unmount from child <' + taco.t +
|
|
5737
|
+
'>. Use onclick attribute or bw.component() for child interactivity.');
|
|
5738
|
+
}
|
|
5426
5739
|
return result;
|
|
5427
5740
|
};
|
|
5428
5741
|
|
|
5429
5742
|
/**
|
|
5430
5743
|
* Unmount: remove from DOM, deactivate, preserve state for re-mount.
|
|
5431
5744
|
*/
|
|
5432
|
-
|
|
5745
|
+
_chp.unmount = function() {
|
|
5433
5746
|
if (!this.mounted) return;
|
|
5434
5747
|
|
|
5435
5748
|
// unmount hook
|
|
@@ -5464,12 +5777,23 @@ ComponentHandle.prototype.unmount = function() {
|
|
|
5464
5777
|
/**
|
|
5465
5778
|
* Destroy: unmount + clear state + unregister actions.
|
|
5466
5779
|
*/
|
|
5467
|
-
|
|
5780
|
+
_chp.destroy = function() {
|
|
5468
5781
|
// willDestroy hook
|
|
5469
5782
|
if (this._hooks.willDestroy) {
|
|
5470
5783
|
this._hooks.willDestroy(this);
|
|
5471
5784
|
}
|
|
5472
5785
|
|
|
5786
|
+
// Cascade destroy to children depth-first (Bug #5)
|
|
5787
|
+
for (var ci = this._children.length - 1; ci >= 0; ci--) {
|
|
5788
|
+
this._children[ci].destroy();
|
|
5789
|
+
}
|
|
5790
|
+
this._children = [];
|
|
5791
|
+
if (this._parent) {
|
|
5792
|
+
var idx = this._parent._children.indexOf(this);
|
|
5793
|
+
if (idx >= 0) this._parent._children.splice(idx, 1);
|
|
5794
|
+
this._parent = null;
|
|
5795
|
+
}
|
|
5796
|
+
|
|
5473
5797
|
this.unmount();
|
|
5474
5798
|
|
|
5475
5799
|
// Unregister actions from function registry
|
|
@@ -5496,12 +5820,36 @@ ComponentHandle.prototype.destroy = function() {
|
|
|
5496
5820
|
* Flush dirty state: resolve changed bindings and apply to DOM.
|
|
5497
5821
|
* @private
|
|
5498
5822
|
*/
|
|
5499
|
-
|
|
5823
|
+
_chp._flush = function() {
|
|
5500
5824
|
this._scheduled = false;
|
|
5501
|
-
var changedKeys =
|
|
5825
|
+
var changedKeys = _keys(this._dirtyKeys);
|
|
5502
5826
|
this._dirtyKeys = {};
|
|
5503
5827
|
if (changedKeys.length === 0 || !this.mounted) return;
|
|
5504
5828
|
|
|
5829
|
+
// Factory rebuild: if a BCCL factory exists and changed keys overlap factory props,
|
|
5830
|
+
// rebuild the TACO from the factory with merged state (Bug #6)
|
|
5831
|
+
if (this._factory) {
|
|
5832
|
+
var rebuildNeeded = false;
|
|
5833
|
+
for (var fi = 0; fi < changedKeys.length; fi++) {
|
|
5834
|
+
if (_hop.call(this._factory.props, changedKeys[fi])) {
|
|
5835
|
+
rebuildNeeded = true; break;
|
|
5836
|
+
}
|
|
5837
|
+
}
|
|
5838
|
+
if (rebuildNeeded) {
|
|
5839
|
+
var merged = {};
|
|
5840
|
+
for (var mk in this._factory.props) if (_hop.call(this._factory.props, mk)) merged[mk] = this._factory.props[mk];
|
|
5841
|
+
for (var sk in this._state) if (_hop.call(this._state, sk)) merged[sk] = this._state[sk];
|
|
5842
|
+
this._factory.props = merged;
|
|
5843
|
+
var newTaco = bw.make(this._factory.type, merged);
|
|
5844
|
+
newTaco._bwFactory = this._factory;
|
|
5845
|
+
this.taco = newTaco;
|
|
5846
|
+
this._originalTaco = this._deepCloneTaco(newTaco);
|
|
5847
|
+
this._render();
|
|
5848
|
+
if (this._hooks.onUpdate) this._hooks.onUpdate(this, changedKeys);
|
|
5849
|
+
return;
|
|
5850
|
+
}
|
|
5851
|
+
}
|
|
5852
|
+
|
|
5505
5853
|
// willUpdate hook
|
|
5506
5854
|
if (this._hooks.willUpdate) {
|
|
5507
5855
|
this._hooks.willUpdate(this, changedKeys);
|
|
@@ -5540,7 +5888,7 @@ ComponentHandle.prototype._flush = function() {
|
|
|
5540
5888
|
* Returns list of patches to apply.
|
|
5541
5889
|
* @private
|
|
5542
5890
|
*/
|
|
5543
|
-
|
|
5891
|
+
_chp._resolveBindings = function(changedKeys) {
|
|
5544
5892
|
var patches = [];
|
|
5545
5893
|
for (var i = 0; i < this._bindings.length; i++) {
|
|
5546
5894
|
var b = this._bindings[i];
|
|
@@ -5576,11 +5924,14 @@ ComponentHandle.prototype._resolveBindings = function(changedKeys) {
|
|
|
5576
5924
|
* Apply patches to DOM.
|
|
5577
5925
|
* @private
|
|
5578
5926
|
*/
|
|
5579
|
-
|
|
5927
|
+
_chp._applyPatches = function(patches) {
|
|
5580
5928
|
for (var i = 0; i < patches.length; i++) {
|
|
5581
5929
|
var p = patches[i];
|
|
5582
5930
|
var el = this._bw_refs[p.refId];
|
|
5583
|
-
if (!el)
|
|
5931
|
+
if (!el) {
|
|
5932
|
+
if (bw.debug) _cw('bw.debug: _applyPatches — ref "' + p.refId + '" not found in DOM');
|
|
5933
|
+
continue;
|
|
5934
|
+
}
|
|
5584
5935
|
if (p.type === 'content') {
|
|
5585
5936
|
el.textContent = p.value;
|
|
5586
5937
|
} else if (p.type === 'attribute') {
|
|
@@ -5597,7 +5948,7 @@ ComponentHandle.prototype._applyPatches = function(patches) {
|
|
|
5597
5948
|
* Resolve all bindings and apply (used for initial render).
|
|
5598
5949
|
* @private
|
|
5599
5950
|
*/
|
|
5600
|
-
|
|
5951
|
+
_chp._resolveAndApplyAll = function() {
|
|
5601
5952
|
var patches = [];
|
|
5602
5953
|
for (var i = 0; i < this._bindings.length; i++) {
|
|
5603
5954
|
var b = this._bindings[i];
|
|
@@ -5620,7 +5971,7 @@ ComponentHandle.prototype._resolveAndApplyAll = function() {
|
|
|
5620
5971
|
* Full re-render for structural changes (when/each branch switches).
|
|
5621
5972
|
* @private
|
|
5622
5973
|
*/
|
|
5623
|
-
|
|
5974
|
+
_chp._render = function() {
|
|
5624
5975
|
if (!this.element || !this.element.parentNode) return;
|
|
5625
5976
|
var parent = this.element.parentNode;
|
|
5626
5977
|
var nextSibling = this.element.nextSibling;
|
|
@@ -5660,7 +6011,7 @@ ComponentHandle.prototype._render = function() {
|
|
|
5660
6011
|
* @param {string} event - Event name (e.g., 'click')
|
|
5661
6012
|
* @param {Function} handler - Event handler
|
|
5662
6013
|
*/
|
|
5663
|
-
|
|
6014
|
+
_chp.on = function(event, handler) {
|
|
5664
6015
|
if (this.element) {
|
|
5665
6016
|
this.element.addEventListener(event, handler);
|
|
5666
6017
|
}
|
|
@@ -5672,7 +6023,7 @@ ComponentHandle.prototype.on = function(event, handler) {
|
|
|
5672
6023
|
* @param {string} event - Event name
|
|
5673
6024
|
* @param {Function} handler - Handler to remove
|
|
5674
6025
|
*/
|
|
5675
|
-
|
|
6026
|
+
_chp.off = function(event, handler) {
|
|
5676
6027
|
if (this.element) {
|
|
5677
6028
|
this.element.removeEventListener(event, handler);
|
|
5678
6029
|
}
|
|
@@ -5687,7 +6038,7 @@ ComponentHandle.prototype.off = function(event, handler) {
|
|
|
5687
6038
|
* @param {Function} handler - Handler function
|
|
5688
6039
|
* @returns {Function} Unsubscribe function
|
|
5689
6040
|
*/
|
|
5690
|
-
|
|
6041
|
+
_chp.sub = function(topic, handler) {
|
|
5691
6042
|
var unsub = bw.sub(topic, handler);
|
|
5692
6043
|
this._subs.push(unsub);
|
|
5693
6044
|
return unsub;
|
|
@@ -5698,10 +6049,10 @@ ComponentHandle.prototype.sub = function(topic, handler) {
|
|
|
5698
6049
|
* @param {string} name - Action name
|
|
5699
6050
|
* @param {...*} args - Arguments passed after comp
|
|
5700
6051
|
*/
|
|
5701
|
-
|
|
6052
|
+
_chp.action = function(name) {
|
|
5702
6053
|
var fn = this._actions[name];
|
|
5703
6054
|
if (!fn) {
|
|
5704
|
-
|
|
6055
|
+
_cw('ComponentHandle.action: unknown action "' + name + '"');
|
|
5705
6056
|
return;
|
|
5706
6057
|
}
|
|
5707
6058
|
var args = [this].concat(Array.prototype.slice.call(arguments, 1));
|
|
@@ -5713,7 +6064,7 @@ ComponentHandle.prototype.action = function(name) {
|
|
|
5713
6064
|
* @param {string} sel - CSS selector
|
|
5714
6065
|
* @returns {Element|null}
|
|
5715
6066
|
*/
|
|
5716
|
-
|
|
6067
|
+
_chp.select = function(sel) {
|
|
5717
6068
|
return this.element ? this.element.querySelector(sel) : null;
|
|
5718
6069
|
};
|
|
5719
6070
|
|
|
@@ -5722,7 +6073,7 @@ ComponentHandle.prototype.select = function(sel) {
|
|
|
5722
6073
|
* @param {string} sel - CSS selector
|
|
5723
6074
|
* @returns {Element[]}
|
|
5724
6075
|
*/
|
|
5725
|
-
|
|
6076
|
+
_chp.selectAll = function(sel) {
|
|
5726
6077
|
if (!this.element) return [];
|
|
5727
6078
|
return Array.prototype.slice.call(this.element.querySelectorAll(sel));
|
|
5728
6079
|
};
|
|
@@ -5733,7 +6084,7 @@ ComponentHandle.prototype.selectAll = function(sel) {
|
|
|
5733
6084
|
* @param {string} tag - User-defined identifier (e.g. 'dashboard_prod_east')
|
|
5734
6085
|
* @returns {ComponentHandle} this (for chaining)
|
|
5735
6086
|
*/
|
|
5736
|
-
|
|
6087
|
+
_chp.userTag = function(tag) {
|
|
5737
6088
|
this._userTag = tag;
|
|
5738
6089
|
if (this.element) {
|
|
5739
6090
|
this.element.classList.add(tag);
|
|
@@ -5834,8 +6185,8 @@ bw.message = function(target, action, data) {
|
|
|
5834
6185
|
}
|
|
5835
6186
|
if (!el || !el._bwComponentHandle) return false;
|
|
5836
6187
|
var comp = el._bwComponentHandle;
|
|
5837
|
-
if (
|
|
5838
|
-
|
|
6188
|
+
if (!_is(comp[action], 'function')) {
|
|
6189
|
+
_cw('bw.message: unknown action "' + action + '" on component ' + target);
|
|
5839
6190
|
return false;
|
|
5840
6191
|
}
|
|
5841
6192
|
comp[action](data);
|
|
@@ -5872,7 +6223,7 @@ bw._builtinClientFunctions = {
|
|
|
5872
6223
|
},
|
|
5873
6224
|
focus: function(selector) {
|
|
5874
6225
|
var el = bw._el(selector);
|
|
5875
|
-
if (el &&
|
|
6226
|
+
if (el && _is(el.focus, 'function')) el.focus();
|
|
5876
6227
|
},
|
|
5877
6228
|
download: function(filename, content, mimeType) {
|
|
5878
6229
|
if (typeof document === 'undefined') return;
|
|
@@ -6037,12 +6388,12 @@ bw.clientApply = function(msg) {
|
|
|
6037
6388
|
} else if (type === 'remove') {
|
|
6038
6389
|
var toRemove = bw._el(target);
|
|
6039
6390
|
if (!toRemove) return false;
|
|
6040
|
-
if (
|
|
6391
|
+
if (_is(bw.cleanup, 'function')) bw.cleanup(toRemove);
|
|
6041
6392
|
toRemove.remove();
|
|
6042
6393
|
return true;
|
|
6043
6394
|
|
|
6044
6395
|
} else if (type === 'batch') {
|
|
6045
|
-
if (!
|
|
6396
|
+
if (!_isA(msg.ops)) return false;
|
|
6046
6397
|
var allOk = true;
|
|
6047
6398
|
msg.ops.forEach(function(op) {
|
|
6048
6399
|
if (!bw.clientApply(op)) allOk = false;
|
|
@@ -6058,26 +6409,26 @@ bw.clientApply = function(msg) {
|
|
|
6058
6409
|
bw._clientFunctions[msg.name] = new Function('return ' + msg.body)();
|
|
6059
6410
|
return true;
|
|
6060
6411
|
} catch (e) {
|
|
6061
|
-
|
|
6412
|
+
_ce('[bw] register error:', msg.name, e);
|
|
6062
6413
|
return false;
|
|
6063
6414
|
}
|
|
6064
6415
|
|
|
6065
6416
|
} else if (type === 'call') {
|
|
6066
6417
|
if (!msg.name) return false;
|
|
6067
6418
|
var fn = bw._clientFunctions[msg.name] || bw._builtinClientFunctions[msg.name];
|
|
6068
|
-
if (
|
|
6419
|
+
if (!_is(fn, 'function')) return false;
|
|
6069
6420
|
try {
|
|
6070
|
-
var args =
|
|
6421
|
+
var args = _isA(msg.args) ? msg.args : [];
|
|
6071
6422
|
fn.apply(null, args);
|
|
6072
6423
|
return true;
|
|
6073
6424
|
} catch (e) {
|
|
6074
|
-
|
|
6425
|
+
_ce('[bw] call error:', msg.name, e);
|
|
6075
6426
|
return false;
|
|
6076
6427
|
}
|
|
6077
6428
|
|
|
6078
6429
|
} else if (type === 'exec') {
|
|
6079
6430
|
if (!bw._allowExec) {
|
|
6080
|
-
|
|
6431
|
+
_cw('[bw] exec rejected: allowExec is not enabled');
|
|
6081
6432
|
return false;
|
|
6082
6433
|
}
|
|
6083
6434
|
if (!msg.code) return false;
|
|
@@ -6085,7 +6436,7 @@ bw.clientApply = function(msg) {
|
|
|
6085
6436
|
new Function(msg.code)();
|
|
6086
6437
|
return true;
|
|
6087
6438
|
} catch (e) {
|
|
6088
|
-
|
|
6439
|
+
_ce('[bw] exec error:', e);
|
|
6089
6440
|
return false;
|
|
6090
6441
|
}
|
|
6091
6442
|
}
|
|
@@ -6133,7 +6484,7 @@ bw.clientConnect = function(url, opts) {
|
|
|
6133
6484
|
|
|
6134
6485
|
function handleMessage(data) {
|
|
6135
6486
|
try {
|
|
6136
|
-
var msg =
|
|
6487
|
+
var msg = _is(data, 'string') ? bw.clientParse(data) : data;
|
|
6137
6488
|
if (onMessage) onMessage(msg);
|
|
6138
6489
|
if (handlers.message) handlers.message(msg);
|
|
6139
6490
|
bw.clientApply(msg);
|
|
@@ -6171,7 +6522,7 @@ bw.clientConnect = function(url, opts) {
|
|
|
6171
6522
|
setStatus('connected');
|
|
6172
6523
|
conn._pollTimer = setInterval(function() {
|
|
6173
6524
|
fetch(url).then(function(r) { return r.json(); }).then(function(msgs) {
|
|
6174
|
-
if (
|
|
6525
|
+
if (_isA(msgs)) {
|
|
6175
6526
|
msgs.forEach(handleMessage);
|
|
6176
6527
|
} else if (msgs && msgs.type) {
|
|
6177
6528
|
handleMessage(msgs);
|
|
@@ -6253,33 +6604,33 @@ bw.inspect = function(target) {
|
|
|
6253
6604
|
el = target.element;
|
|
6254
6605
|
comp = target;
|
|
6255
6606
|
} else {
|
|
6256
|
-
if (
|
|
6607
|
+
if (_is(target, 'string')) {
|
|
6257
6608
|
el = bw.$(target)[0];
|
|
6258
6609
|
}
|
|
6259
6610
|
if (!el) {
|
|
6260
|
-
|
|
6611
|
+
_cw('bw.inspect: element not found');
|
|
6261
6612
|
return null;
|
|
6262
6613
|
}
|
|
6263
6614
|
comp = el._bwComponentHandle;
|
|
6264
6615
|
}
|
|
6265
6616
|
if (!comp) {
|
|
6266
|
-
|
|
6267
|
-
|
|
6268
|
-
|
|
6269
|
-
|
|
6617
|
+
_cl('bw.inspect: no ComponentHandle on this element');
|
|
6618
|
+
_cl(' Tag:', el.tagName);
|
|
6619
|
+
_cl(' Classes:', el.className);
|
|
6620
|
+
_cl(' _bw_state:', el._bw_state || '(none)');
|
|
6270
6621
|
return null;
|
|
6271
6622
|
}
|
|
6272
6623
|
var deps = comp._bindings.reduce(function(s, b) {
|
|
6273
6624
|
return s.concat(b.deps || []);
|
|
6274
6625
|
}, []).filter(function(v, i, a) { return a.indexOf(v) === i; });
|
|
6275
6626
|
console.group('Component: ' + comp._bwId);
|
|
6276
|
-
|
|
6277
|
-
|
|
6278
|
-
|
|
6279
|
-
|
|
6280
|
-
|
|
6281
|
-
|
|
6282
|
-
|
|
6627
|
+
_cl('State:', comp._state);
|
|
6628
|
+
_cl('Bindings:', comp._bindings.length, '(deps:', deps, ')');
|
|
6629
|
+
_cl('Methods:', _keys(comp._methods));
|
|
6630
|
+
_cl('Actions:', _keys(comp._actions));
|
|
6631
|
+
_cl('User tag:', comp._userTag || '(none)');
|
|
6632
|
+
_cl('Mounted:', comp.mounted);
|
|
6633
|
+
_cl('Element:', comp.element);
|
|
6283
6634
|
console.groupEnd();
|
|
6284
6635
|
return comp;
|
|
6285
6636
|
};
|
|
@@ -6302,8 +6653,8 @@ bw.compile = function(taco) {
|
|
|
6302
6653
|
// Pre-extract all binding expressions
|
|
6303
6654
|
var precompiled = [];
|
|
6304
6655
|
function walkExpressions(node) {
|
|
6305
|
-
if (!node
|
|
6306
|
-
if (
|
|
6656
|
+
if (!_is(node, 'object')) return;
|
|
6657
|
+
if (_is(node.c, 'string') && node.c.indexOf('${') >= 0) {
|
|
6307
6658
|
var parsed = bw._parseBindings(node.c);
|
|
6308
6659
|
for (var i = 0; i < parsed.length; i++) {
|
|
6309
6660
|
try {
|
|
@@ -6318,9 +6669,9 @@ bw.compile = function(taco) {
|
|
|
6318
6669
|
}
|
|
6319
6670
|
if (node.a) {
|
|
6320
6671
|
for (var key in node.a) {
|
|
6321
|
-
if (
|
|
6672
|
+
if (_hop.call(node.a, key)) {
|
|
6322
6673
|
var v = node.a[key];
|
|
6323
|
-
if (
|
|
6674
|
+
if (_is(v, 'string') && v.indexOf('${') >= 0) {
|
|
6324
6675
|
var parsed2 = bw._parseBindings(v);
|
|
6325
6676
|
for (var j = 0; j < parsed2.length; j++) {
|
|
6326
6677
|
try {
|
|
@@ -6336,9 +6687,9 @@ bw.compile = function(taco) {
|
|
|
6336
6687
|
}
|
|
6337
6688
|
}
|
|
6338
6689
|
}
|
|
6339
|
-
if (
|
|
6690
|
+
if (_isA(node.c)) {
|
|
6340
6691
|
for (var k = 0; k < node.c.length; k++) walkExpressions(node.c[k]);
|
|
6341
|
-
} else if (node.c
|
|
6692
|
+
} else if (_is(node.c, 'object') && node.c.t) {
|
|
6342
6693
|
walkExpressions(node.c);
|
|
6343
6694
|
}
|
|
6344
6695
|
}
|
|
@@ -6350,7 +6701,7 @@ bw.compile = function(taco) {
|
|
|
6350
6701
|
handle._precompiledBindings = precompiled;
|
|
6351
6702
|
if (initialState) {
|
|
6352
6703
|
for (var k in initialState) {
|
|
6353
|
-
if (
|
|
6704
|
+
if (_hop.call(initialState, k)) {
|
|
6354
6705
|
handle._state[k] = initialState[k];
|
|
6355
6706
|
}
|
|
6356
6707
|
}
|
|
@@ -6381,18 +6732,18 @@ bw.compile = function(taco) {
|
|
|
6381
6732
|
bw.css = function(rules, options = {}) {
|
|
6382
6733
|
const { minify = false, pretty = !minify } = options;
|
|
6383
6734
|
|
|
6384
|
-
if (
|
|
6735
|
+
if (_is(rules, 'string')) return rules;
|
|
6385
6736
|
|
|
6386
6737
|
let css = '';
|
|
6387
6738
|
const indent = pretty ? ' ' : '';
|
|
6388
6739
|
const newline = pretty ? '\n' : '';
|
|
6389
6740
|
const space = pretty ? ' ' : '';
|
|
6390
6741
|
|
|
6391
|
-
if (
|
|
6742
|
+
if (_isA(rules)) {
|
|
6392
6743
|
css = rules.map(rule => bw.css(rule, options)).join(newline);
|
|
6393
|
-
} else if (
|
|
6744
|
+
} else if (_is(rules, 'object')) {
|
|
6394
6745
|
Object.entries(rules).forEach(([selector, styles]) => {
|
|
6395
|
-
if (
|
|
6746
|
+
if (_is(styles, 'object')) {
|
|
6396
6747
|
// Handle @media, @keyframes, @supports — recurse into nested block
|
|
6397
6748
|
if (selector.charAt(0) === '@') {
|
|
6398
6749
|
const inner = bw.css(styles, options);
|
|
@@ -6441,7 +6792,7 @@ bw.css = function(rules, options = {}) {
|
|
|
6441
6792
|
*/
|
|
6442
6793
|
bw.injectCSS = function(css, options = {}) {
|
|
6443
6794
|
if (!bw._isBrowser) {
|
|
6444
|
-
|
|
6795
|
+
_cw('bw.injectCSS requires a DOM environment');
|
|
6445
6796
|
return null;
|
|
6446
6797
|
}
|
|
6447
6798
|
|
|
@@ -6458,7 +6809,7 @@ bw.injectCSS = function(css, options = {}) {
|
|
|
6458
6809
|
}
|
|
6459
6810
|
|
|
6460
6811
|
// Convert CSS if needed
|
|
6461
|
-
const cssStr =
|
|
6812
|
+
const cssStr = _is(css, 'string') ? css : bw.css(css, options);
|
|
6462
6813
|
|
|
6463
6814
|
// Set or append CSS
|
|
6464
6815
|
if (append && styleEl.textContent) {
|
|
@@ -6488,7 +6839,7 @@ bw.s = function() {
|
|
|
6488
6839
|
var result = {};
|
|
6489
6840
|
for (var i = 0; i < arguments.length; i++) {
|
|
6490
6841
|
var arg = arguments[i];
|
|
6491
|
-
if (arg
|
|
6842
|
+
if (_is(arg, 'object')) Object.assign(result, arg);
|
|
6492
6843
|
}
|
|
6493
6844
|
return result;
|
|
6494
6845
|
};
|
|
@@ -6611,7 +6962,7 @@ bw.u = {
|
|
|
6611
6962
|
bw.responsive = function(selector, breakpoints) {
|
|
6612
6963
|
var sizes = { sm: '576px', md: '768px', lg: '992px', xl: '1200px' };
|
|
6613
6964
|
var parts = [];
|
|
6614
|
-
|
|
6965
|
+
_keys(breakpoints).forEach(function(key) {
|
|
6615
6966
|
var rules = {};
|
|
6616
6967
|
if (key === 'base') {
|
|
6617
6968
|
rules[selector] = breakpoints[key];
|
|
@@ -6683,18 +7034,18 @@ if (bw._isBrowser) {
|
|
|
6683
7034
|
if (!selector) return [];
|
|
6684
7035
|
|
|
6685
7036
|
// Already an array
|
|
6686
|
-
if (
|
|
7037
|
+
if (_isA(selector)) return selector;
|
|
6687
7038
|
|
|
6688
7039
|
// Single element
|
|
6689
7040
|
if (selector.nodeType) return [selector];
|
|
6690
7041
|
|
|
6691
7042
|
// NodeList or HTMLCollection
|
|
6692
|
-
if (selector.length !== undefined &&
|
|
7043
|
+
if (selector.length !== undefined && !_is(selector, 'string')) {
|
|
6693
7044
|
return Array.from(selector);
|
|
6694
7045
|
}
|
|
6695
7046
|
|
|
6696
7047
|
// CSS selector string
|
|
6697
|
-
if (
|
|
7048
|
+
if (_is(selector, 'string')) {
|
|
6698
7049
|
return Array.from(document.querySelectorAll(selector));
|
|
6699
7050
|
}
|
|
6700
7051
|
|
|
@@ -7198,7 +7549,7 @@ bw.makeTable = function(config) {
|
|
|
7198
7549
|
|
|
7199
7550
|
// Auto-detect columns if not provided
|
|
7200
7551
|
const cols = columns || (data.length > 0
|
|
7201
|
-
?
|
|
7552
|
+
? _keys(data[0]).map(key => ({ key, label: key }))
|
|
7202
7553
|
: []);
|
|
7203
7554
|
|
|
7204
7555
|
// Current sort state
|
|
@@ -7213,7 +7564,7 @@ bw.makeTable = function(config) {
|
|
|
7213
7564
|
const bVal = b[currentSortColumn];
|
|
7214
7565
|
|
|
7215
7566
|
// Handle different types
|
|
7216
|
-
if (
|
|
7567
|
+
if (_is(aVal, 'number') && _is(bVal, 'number')) {
|
|
7217
7568
|
return currentSortDirection === 'asc' ? aVal - bVal : bVal - aVal;
|
|
7218
7569
|
}
|
|
7219
7570
|
|
|
@@ -7323,7 +7674,7 @@ bw.makeTable = function(config) {
|
|
|
7323
7674
|
bw.makeTableFromArray = function(config) {
|
|
7324
7675
|
const { data = [], headerRow = true, columns, ...rest } = config;
|
|
7325
7676
|
|
|
7326
|
-
if (!
|
|
7677
|
+
if (!_isA(data) || data.length === 0) {
|
|
7327
7678
|
return bw.makeTable({ data: [], columns: columns || [], ...rest });
|
|
7328
7679
|
}
|
|
7329
7680
|
|
|
@@ -7405,7 +7756,7 @@ bw.makeBarChart = function(config) {
|
|
|
7405
7756
|
className = ''
|
|
7406
7757
|
} = config;
|
|
7407
7758
|
|
|
7408
|
-
if (!
|
|
7759
|
+
if (!_isA(data) || data.length === 0) {
|
|
7409
7760
|
return { t: 'div', a: { class: ('bw_bar_chart_container ' + className).trim() }, c: '' };
|
|
7410
7761
|
}
|
|
7411
7762
|
|
|
@@ -7554,7 +7905,7 @@ bw._componentRegistry = new Map();
|
|
|
7554
7905
|
*/
|
|
7555
7906
|
bw.render = function(element, position, taco) {
|
|
7556
7907
|
// Get target element
|
|
7557
|
-
const targetEl =
|
|
7908
|
+
const targetEl = _is(element, 'string')
|
|
7558
7909
|
? document.querySelector(element)
|
|
7559
7910
|
: element;
|
|
7560
7911
|
|
|
@@ -7704,7 +8055,7 @@ bw.render = function(element, position, taco) {
|
|
|
7704
8055
|
setContent(content) {
|
|
7705
8056
|
this._taco.c = content;
|
|
7706
8057
|
if (this.element) {
|
|
7707
|
-
if (
|
|
8058
|
+
if (_is(content, 'string')) {
|
|
7708
8059
|
this.element.textContent = content;
|
|
7709
8060
|
} else {
|
|
7710
8061
|
// Re-render for complex content
|