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