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
package/dist/bitwrench.esm.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
/*! bitwrench v2.0.
|
|
1
|
+
/*! bitwrench 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
|
/**
|
|
@@ -6874,7 +6874,11 @@ var BCCL = {
|
|
|
6874
6874
|
function make(type, props) {
|
|
6875
6875
|
var def = BCCL[type];
|
|
6876
6876
|
if (!def) throw new Error('bw.make: unknown component type "' + type + '". Available: ' + Object.keys(BCCL).join(', '));
|
|
6877
|
-
|
|
6877
|
+
var taco = def.make(props || {});
|
|
6878
|
+
if (taco && typeof taco === 'object') {
|
|
6879
|
+
taco._bwFactory = { type: type, props: props || {} };
|
|
6880
|
+
}
|
|
6881
|
+
return taco;
|
|
6878
6882
|
}
|
|
6879
6883
|
|
|
6880
6884
|
var components = /*#__PURE__*/Object.freeze({
|
|
@@ -6995,7 +6999,7 @@ const bw = {
|
|
|
6995
6999
|
__monkey_patch_is_nodejs__: {
|
|
6996
7000
|
_value: 'ignore',
|
|
6997
7001
|
set: function(x) {
|
|
6998
|
-
this._value = (
|
|
7002
|
+
this._value = _is(x, 'boolean') ? x : 'ignore';
|
|
6999
7003
|
},
|
|
7000
7004
|
get: function() {
|
|
7001
7005
|
return this._value;
|
|
@@ -7043,6 +7047,67 @@ Object.defineProperty(bw, '_isBrowser', {
|
|
|
7043
7047
|
configurable: true
|
|
7044
7048
|
});
|
|
7045
7049
|
|
|
7050
|
+
// ── Internal aliases ─────────────────────────────────────────────────────
|
|
7051
|
+
// Short names for frequently-used builtins and internal methods.
|
|
7052
|
+
// Same pattern as v1 (_to = bw.typeOf, etc.).
|
|
7053
|
+
//
|
|
7054
|
+
// Why: Terser can't shorten global property chains (console.warn,
|
|
7055
|
+
// Object.prototype.hasOwnProperty, Array.isArray, document.createElement)
|
|
7056
|
+
// because it can't prove they're side-effect-free. We can, so we alias
|
|
7057
|
+
// them here. Each alias saves bytes in the minified output, and the short
|
|
7058
|
+
// names also reduce visual noise in the hot paths (binding pipeline,
|
|
7059
|
+
// createDOM, etc.).
|
|
7060
|
+
//
|
|
7061
|
+
// Alias Target Sites
|
|
7062
|
+
// ───────── ────────────────────────────────────── ─────
|
|
7063
|
+
// _hop Object.prototype.hasOwnProperty 15
|
|
7064
|
+
// _isA Array.isArray 25
|
|
7065
|
+
// _keys Object.keys 7
|
|
7066
|
+
// _to bw.typeOf (type string) 26
|
|
7067
|
+
// _is type check boolean: _is(x,'string') ~50
|
|
7068
|
+
// _cw console.warn 8
|
|
7069
|
+
// _cl console.log 11
|
|
7070
|
+
// _ce console.error 4
|
|
7071
|
+
// _chp ComponentHandle.prototype 28 (defined after constructor)
|
|
7072
|
+
//
|
|
7073
|
+
// Note: document.createElement etc. are NOT aliased because they require
|
|
7074
|
+
// `this === document` and .bind() would add overhead on every call.
|
|
7075
|
+
// Console aliases use thin wrappers (not direct refs) so test monkey-
|
|
7076
|
+
// patching of console.warn/log/error continues to work.
|
|
7077
|
+
//
|
|
7078
|
+
// `typeof x` for UNDECLARED globals (window, document, process, require,
|
|
7079
|
+
// EventSource, navigator, Promise, __filename, import.meta) MUST stay as
|
|
7080
|
+
// raw `typeof` — calling _to(x) when x doesn't exist throws ReferenceError.
|
|
7081
|
+
//
|
|
7082
|
+
// ── v1 functional type helpers (kept for reference, not currently used) ──
|
|
7083
|
+
// _toa(x, type, trueVal, falseVal) — bw.typeAssign:
|
|
7084
|
+
// returns trueVal if _to(x)===type, else falseVal.
|
|
7085
|
+
// Replaces: (typeof x === 'string') ? A : B → _toa(x,'string',A,B)
|
|
7086
|
+
// _toc(x, type, trueVal, falseVal) — bw.typeConvert:
|
|
7087
|
+
// same as _toa but if trueVal/falseVal are functions, calls them with x.
|
|
7088
|
+
// Replaces: typeof x === 'string' ? fn(x) : default → _toc(x,'string',fn,default)
|
|
7089
|
+
// Uncomment if pattern frequency justifies them:
|
|
7090
|
+
// var _toa = function(x, t, y, n) { return _to(x) === t ? y : n; };
|
|
7091
|
+
// 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); };
|
|
7092
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
7093
|
+
var _hop = Object.prototype.hasOwnProperty;
|
|
7094
|
+
var _isA = Array.isArray;
|
|
7095
|
+
var _keys = Object.keys;
|
|
7096
|
+
var _to = typeOf; // imported from bitwrench-utils.js
|
|
7097
|
+
var _is = function(x, t) { var r = _to(x); return r === t || r.toLowerCase() === t; };
|
|
7098
|
+
// Console aliases use thin wrappers (not direct references) so that test
|
|
7099
|
+
// code can monkey-patch console.warn/log/error and the patches take effect.
|
|
7100
|
+
var _cw = function() { console.warn.apply(console, arguments); };
|
|
7101
|
+
var _cl = function() { console.log.apply(console, arguments); };
|
|
7102
|
+
var _ce = function() { console.error.apply(console, arguments); };
|
|
7103
|
+
|
|
7104
|
+
/**
|
|
7105
|
+
* Debug flag. When true, emits console.warn for silent binding failures
|
|
7106
|
+
* (missing paths, null refs, auto-created intermediate objects).
|
|
7107
|
+
* @type {boolean}
|
|
7108
|
+
*/
|
|
7109
|
+
bw.debug = false;
|
|
7110
|
+
|
|
7046
7111
|
/**
|
|
7047
7112
|
* Lazy-resolve Node.js `fs` module.
|
|
7048
7113
|
* Tries require('fs') first (available in CJS/UMD Node.js builds),
|
|
@@ -7190,7 +7255,7 @@ bw.uuid = function(prefix) {
|
|
|
7190
7255
|
*/
|
|
7191
7256
|
bw._el = function(id) {
|
|
7192
7257
|
// Pass-through for DOM elements
|
|
7193
|
-
if (
|
|
7258
|
+
if (!_is(id, 'string')) return id || null;
|
|
7194
7259
|
if (!id) return null;
|
|
7195
7260
|
if (!bw._isBrowser) return null;
|
|
7196
7261
|
|
|
@@ -7286,7 +7351,7 @@ bw._deregisterNode = function(el, bwId) {
|
|
|
7286
7351
|
* // => '<b>Hello</b> & "world"'
|
|
7287
7352
|
*/
|
|
7288
7353
|
bw.escapeHTML = function(str) {
|
|
7289
|
-
if (
|
|
7354
|
+
if (!_is(str, 'string')) return '';
|
|
7290
7355
|
|
|
7291
7356
|
const escapeMap = {
|
|
7292
7357
|
'&': '&',
|
|
@@ -7359,7 +7424,7 @@ bw.html = function(taco, options = {}) {
|
|
|
7359
7424
|
}
|
|
7360
7425
|
|
|
7361
7426
|
// Handle arrays of TACOs
|
|
7362
|
-
if (
|
|
7427
|
+
if (_isA(taco)) {
|
|
7363
7428
|
return taco.map(t => bw.html(t, options)).join('');
|
|
7364
7429
|
}
|
|
7365
7430
|
|
|
@@ -7382,15 +7447,15 @@ bw.html = function(taco, options = {}) {
|
|
|
7382
7447
|
if (taco && taco._bwEach && options.state) {
|
|
7383
7448
|
var eachExpr = taco.expr.replace(/^\$\{|\}$/g, '');
|
|
7384
7449
|
var arr = bw._evaluatePath(options.state, eachExpr);
|
|
7385
|
-
if (!
|
|
7450
|
+
if (!_isA(arr)) return '';
|
|
7386
7451
|
return arr.map(function(item, idx) { return bw.html(taco.factory(item, idx), options); }).join('');
|
|
7387
7452
|
}
|
|
7388
7453
|
|
|
7389
7454
|
// Handle primitives and non-TACO objects
|
|
7390
|
-
if (
|
|
7455
|
+
if (!_is(taco, 'object') || !taco.t) {
|
|
7391
7456
|
var str = options.raw ? String(taco) : bw.escapeHTML(String(taco));
|
|
7392
7457
|
// Resolve template bindings if state provided
|
|
7393
|
-
if (options.state &&
|
|
7458
|
+
if (options.state && _is(str, 'string') && str.indexOf('${') >= 0) {
|
|
7394
7459
|
str = bw._resolveTemplate(str, options.state, !!options.compile);
|
|
7395
7460
|
}
|
|
7396
7461
|
return str;
|
|
@@ -7410,10 +7475,18 @@ bw.html = function(taco, options = {}) {
|
|
|
7410
7475
|
// Skip null, undefined, false
|
|
7411
7476
|
if (value == null || value === false) continue;
|
|
7412
7477
|
|
|
7413
|
-
//
|
|
7414
|
-
if (key.startsWith('on'))
|
|
7478
|
+
// Serialize event handlers via funcRegister
|
|
7479
|
+
if (key.startsWith('on')) {
|
|
7480
|
+
if (_is(value, 'function')) {
|
|
7481
|
+
var fnId = bw.funcRegister(value);
|
|
7482
|
+
attrStr += ' ' + key + '="' + bw.funcGetDispatchStr(fnId, 'event') + '"';
|
|
7483
|
+
} else if (_is(value, 'string')) {
|
|
7484
|
+
attrStr += ' ' + key + '="' + bw.escapeHTML(value) + '"';
|
|
7485
|
+
}
|
|
7486
|
+
continue;
|
|
7487
|
+
}
|
|
7415
7488
|
|
|
7416
|
-
if (key === 'style' &&
|
|
7489
|
+
if (key === 'style' && _is(value, 'object')) {
|
|
7417
7490
|
// Convert style object to string
|
|
7418
7491
|
const styleStr = Object.entries(value)
|
|
7419
7492
|
.filter(([, v]) => v != null)
|
|
@@ -7424,7 +7497,7 @@ bw.html = function(taco, options = {}) {
|
|
|
7424
7497
|
}
|
|
7425
7498
|
} else if (key === 'class') {
|
|
7426
7499
|
// Handle class as array or string
|
|
7427
|
-
const classStr =
|
|
7500
|
+
const classStr = _isA(value) ? value.filter(Boolean).join(' ') : String(value);
|
|
7428
7501
|
if (classStr) {
|
|
7429
7502
|
attrStr += ` class="${bw.escapeHTML(classStr)}"`;
|
|
7430
7503
|
}
|
|
@@ -7460,13 +7533,184 @@ bw.html = function(taco, options = {}) {
|
|
|
7460
7533
|
// Process content recursively
|
|
7461
7534
|
let contentStr = content != null ? bw.html(content, options) : '';
|
|
7462
7535
|
// Resolve template bindings in content if state provided
|
|
7463
|
-
if (options.state &&
|
|
7536
|
+
if (options.state && _is(contentStr, 'string') && contentStr.indexOf('${') >= 0) {
|
|
7464
7537
|
contentStr = bw._resolveTemplate(contentStr, options.state, !!options.compile);
|
|
7465
7538
|
}
|
|
7466
7539
|
|
|
7467
7540
|
return `<${tag}${attrStr}>${contentStr}</${tag}>`;
|
|
7468
7541
|
};
|
|
7469
7542
|
|
|
7543
|
+
/**
|
|
7544
|
+
* Generate a complete, self-contained HTML document from TACO content.
|
|
7545
|
+
*
|
|
7546
|
+
* Produces a full `<!DOCTYPE html>` page with configurable runtime injection,
|
|
7547
|
+
* func registry emission (so serialized event handlers work), optional theme,
|
|
7548
|
+
* and extra head elements. Designed for static site generation, offline/airgapped
|
|
7549
|
+
* use, and the "static site that isn't static" workflow.
|
|
7550
|
+
*
|
|
7551
|
+
* @param {Object} [opts={}] - Page options
|
|
7552
|
+
* @param {Object|string|Array} [opts.body=''] - Body content: TACO, string, or array
|
|
7553
|
+
* @param {string} [opts.title='bitwrench'] - Page title
|
|
7554
|
+
* @param {Object} [opts.state] - State for ${expr} resolution in bw.html()
|
|
7555
|
+
* @param {string} [opts.runtime='shim'] - Runtime level: 'inline'|'cdn'|'shim'|'none'
|
|
7556
|
+
* @param {string} [opts.css=''] - Additional CSS for <style> block
|
|
7557
|
+
* @param {string|Object} [opts.theme=null] - Theme preset name or config object
|
|
7558
|
+
* @param {Array} [opts.head=[]] - Extra TACO elements rendered into <head>
|
|
7559
|
+
* @param {string} [opts.favicon=''] - Favicon URL
|
|
7560
|
+
* @param {string} [opts.lang='en'] - HTML lang attribute
|
|
7561
|
+
* @returns {string} Complete HTML document string
|
|
7562
|
+
* @category DOM Generation
|
|
7563
|
+
* @see bw.html
|
|
7564
|
+
* @example
|
|
7565
|
+
* bw.htmlPage({
|
|
7566
|
+
* title: 'My App',
|
|
7567
|
+
* body: { t: 'h1', c: 'Hello World' },
|
|
7568
|
+
* runtime: 'shim'
|
|
7569
|
+
* })
|
|
7570
|
+
*/
|
|
7571
|
+
bw.htmlPage = function(opts) {
|
|
7572
|
+
opts = opts || {};
|
|
7573
|
+
var title = opts.title || 'bitwrench';
|
|
7574
|
+
var body = opts.body || '';
|
|
7575
|
+
var state = opts.state || undefined;
|
|
7576
|
+
var runtime = opts.runtime || 'shim';
|
|
7577
|
+
var css = opts.css || '';
|
|
7578
|
+
var theme = opts.theme || null;
|
|
7579
|
+
var headExtra = opts.head || [];
|
|
7580
|
+
var favicon = opts.favicon || '';
|
|
7581
|
+
var lang = opts.lang || 'en';
|
|
7582
|
+
|
|
7583
|
+
// Snapshot funcRegistry counter before rendering
|
|
7584
|
+
var fnCounterBefore = bw._fnIDCounter;
|
|
7585
|
+
|
|
7586
|
+
// Render body content
|
|
7587
|
+
var bodyHTML = '';
|
|
7588
|
+
if (_is(body, 'string')) {
|
|
7589
|
+
bodyHTML = body;
|
|
7590
|
+
} else {
|
|
7591
|
+
var htmlOpts = {};
|
|
7592
|
+
if (state) htmlOpts.state = state;
|
|
7593
|
+
bodyHTML = bw.html(body, htmlOpts);
|
|
7594
|
+
}
|
|
7595
|
+
|
|
7596
|
+
// Collect functions registered during this render
|
|
7597
|
+
var fnCounterAfter = bw._fnIDCounter;
|
|
7598
|
+
var registryEntries = '';
|
|
7599
|
+
for (var i = fnCounterBefore; i < fnCounterAfter; i++) {
|
|
7600
|
+
var fnKey = 'bw_fn_' + i;
|
|
7601
|
+
if (bw._fnRegistry[fnKey]) {
|
|
7602
|
+
registryEntries += 'bw._fnRegistry[\'' + fnKey + '\']=' +
|
|
7603
|
+
bw._fnRegistry[fnKey].toString() + ';\n';
|
|
7604
|
+
}
|
|
7605
|
+
}
|
|
7606
|
+
|
|
7607
|
+
// Build runtime script for <head>
|
|
7608
|
+
var runtimeHead = '';
|
|
7609
|
+
if (runtime === 'inline') {
|
|
7610
|
+
// Read UMD bundle synchronously if in Node.js
|
|
7611
|
+
var umdSource = null;
|
|
7612
|
+
if (bw._isNode) {
|
|
7613
|
+
try {
|
|
7614
|
+
var fs = (typeof require === 'function') ? require('fs') : null;
|
|
7615
|
+
var pathMod = (typeof require === 'function') ? require('path') : null;
|
|
7616
|
+
if (fs && pathMod) {
|
|
7617
|
+
// Resolve dist/ relative to this source file
|
|
7618
|
+
var srcDir = '';
|
|
7619
|
+
try { srcDir = pathMod.dirname((typeof __filename !== 'undefined') ? __filename : ''); }
|
|
7620
|
+
catch(e2) { /* ESM: __filename not available */ }
|
|
7621
|
+
if (!srcDir && typeof import.meta !== 'undefined' && import.meta.url) {
|
|
7622
|
+
var url = (typeof require === 'function') ? require('url') : null;
|
|
7623
|
+
if (url && url.fileURLToPath) srcDir = pathMod.dirname(url.fileURLToPath(import.meta.url));
|
|
7624
|
+
}
|
|
7625
|
+
if (srcDir) {
|
|
7626
|
+
var distPath = pathMod.resolve(srcDir, '../dist/bitwrench.umd.min.js');
|
|
7627
|
+
umdSource = fs.readFileSync(distPath, 'utf8');
|
|
7628
|
+
}
|
|
7629
|
+
}
|
|
7630
|
+
} catch(e) { /* fall through */ }
|
|
7631
|
+
}
|
|
7632
|
+
if (umdSource) {
|
|
7633
|
+
runtimeHead = '<script>' + umdSource + '</script>';
|
|
7634
|
+
} else {
|
|
7635
|
+
// Fallback to shim in browser or if dist not available
|
|
7636
|
+
runtimeHead = '<script>' + bw._FUNC_REGISTRY_SHIM + '</script>';
|
|
7637
|
+
}
|
|
7638
|
+
} else if (runtime === 'cdn') {
|
|
7639
|
+
runtimeHead = '<script src="https://cdn.jsdelivr.net/npm/bitwrench@2/dist/bitwrench.umd.min.js"></script>';
|
|
7640
|
+
} else if (runtime === 'shim') {
|
|
7641
|
+
runtimeHead = '<script>' + bw._FUNC_REGISTRY_SHIM + '</script>';
|
|
7642
|
+
}
|
|
7643
|
+
// runtime === 'none' → empty
|
|
7644
|
+
|
|
7645
|
+
// Theme CSS
|
|
7646
|
+
var themeCSS = '';
|
|
7647
|
+
if (theme) {
|
|
7648
|
+
var themeConfig = _is(theme, 'string')
|
|
7649
|
+
? (THEME_PRESETS[theme.toLowerCase()] || null)
|
|
7650
|
+
: theme;
|
|
7651
|
+
if (themeConfig) {
|
|
7652
|
+
var themeResult = bw.generateTheme('', Object.assign({}, themeConfig, { inject: false }));
|
|
7653
|
+
themeCSS = themeResult.css;
|
|
7654
|
+
}
|
|
7655
|
+
}
|
|
7656
|
+
|
|
7657
|
+
// Extra <head> elements
|
|
7658
|
+
var headHTML = '';
|
|
7659
|
+
if (_isA(headExtra) && headExtra.length > 0) {
|
|
7660
|
+
headHTML = headExtra.map(function(el) { return bw.html(el); }).join('\n');
|
|
7661
|
+
}
|
|
7662
|
+
|
|
7663
|
+
// Favicon
|
|
7664
|
+
var faviconTag = '';
|
|
7665
|
+
if (favicon) {
|
|
7666
|
+
var safeFavicon = favicon.replace(/[&<>"']/g, function(c) {
|
|
7667
|
+
return ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[c];
|
|
7668
|
+
});
|
|
7669
|
+
faviconTag = '<link rel="icon" href="' + safeFavicon + '">';
|
|
7670
|
+
}
|
|
7671
|
+
|
|
7672
|
+
// Escaped title
|
|
7673
|
+
var safeTitle = bw.escapeHTML(title);
|
|
7674
|
+
|
|
7675
|
+
// Combine all CSS
|
|
7676
|
+
var allCSS = (themeCSS ? themeCSS + '\n' : '') + css;
|
|
7677
|
+
|
|
7678
|
+
// Body-end script: registry entries + optional loadDefaultStyles
|
|
7679
|
+
var bodyEndScript = '';
|
|
7680
|
+
var bodyEndParts = [];
|
|
7681
|
+
if (registryEntries) {
|
|
7682
|
+
bodyEndParts.push(registryEntries);
|
|
7683
|
+
}
|
|
7684
|
+
if (runtime === 'inline' || runtime === 'cdn') {
|
|
7685
|
+
bodyEndParts.push('if(typeof bw!=="undefined"){bw.loadDefaultStyles();}');
|
|
7686
|
+
}
|
|
7687
|
+
if (bodyEndParts.length > 0) {
|
|
7688
|
+
bodyEndScript = '<script>\n' + bodyEndParts.join('\n') + '\n</script>';
|
|
7689
|
+
}
|
|
7690
|
+
|
|
7691
|
+
// Assemble document
|
|
7692
|
+
var parts = [
|
|
7693
|
+
'<!DOCTYPE html>',
|
|
7694
|
+
'<html lang="' + lang + '">',
|
|
7695
|
+
'<head>',
|
|
7696
|
+
'<meta charset="UTF-8">',
|
|
7697
|
+
'<meta name="viewport" content="width=device-width, initial-scale=1">'
|
|
7698
|
+
];
|
|
7699
|
+
parts.push('<title>' + safeTitle + '</title>');
|
|
7700
|
+
if (faviconTag) parts.push(faviconTag);
|
|
7701
|
+
if (runtimeHead) parts.push(runtimeHead);
|
|
7702
|
+
if (headHTML) parts.push(headHTML);
|
|
7703
|
+
if (allCSS) parts.push('<style>' + allCSS + '</style>');
|
|
7704
|
+
parts.push('</head>');
|
|
7705
|
+
parts.push('<body>');
|
|
7706
|
+
parts.push(bodyHTML);
|
|
7707
|
+
if (bodyEndScript) parts.push(bodyEndScript);
|
|
7708
|
+
parts.push('</body>');
|
|
7709
|
+
parts.push('</html>');
|
|
7710
|
+
|
|
7711
|
+
return parts.join('\n');
|
|
7712
|
+
};
|
|
7713
|
+
|
|
7470
7714
|
/**
|
|
7471
7715
|
* Create a live DOM element from a TACO object (browser only).
|
|
7472
7716
|
*
|
|
@@ -7511,7 +7755,7 @@ bw.createDOM = function(taco, options = {}) {
|
|
|
7511
7755
|
}
|
|
7512
7756
|
|
|
7513
7757
|
// Handle text nodes
|
|
7514
|
-
if (
|
|
7758
|
+
if (!_is(taco, 'object') || !taco.t) {
|
|
7515
7759
|
return document.createTextNode(String(taco));
|
|
7516
7760
|
}
|
|
7517
7761
|
|
|
@@ -7524,16 +7768,16 @@ bw.createDOM = function(taco, options = {}) {
|
|
|
7524
7768
|
for (const [key, value] of Object.entries(attrs)) {
|
|
7525
7769
|
if (value == null || value === false) continue;
|
|
7526
7770
|
|
|
7527
|
-
if (key === 'style' &&
|
|
7771
|
+
if (key === 'style' && _is(value, 'object')) {
|
|
7528
7772
|
// Apply styles directly
|
|
7529
7773
|
Object.assign(el.style, value);
|
|
7530
7774
|
} else if (key === 'class') {
|
|
7531
7775
|
// Handle class as array or string
|
|
7532
|
-
const classStr =
|
|
7776
|
+
const classStr = _isA(value) ? value.filter(Boolean).join(' ') : String(value);
|
|
7533
7777
|
if (classStr) {
|
|
7534
7778
|
el.className = classStr;
|
|
7535
7779
|
}
|
|
7536
|
-
} else if (key.startsWith('on') &&
|
|
7780
|
+
} else if (key.startsWith('on') && _is(value, 'function')) {
|
|
7537
7781
|
// Event handlers
|
|
7538
7782
|
const eventName = key.slice(2).toLowerCase();
|
|
7539
7783
|
el.addEventListener(eventName, value);
|
|
@@ -7553,7 +7797,7 @@ bw.createDOM = function(taco, options = {}) {
|
|
|
7553
7797
|
// Children with data-bw_id or id attributes get local refs on the parent,
|
|
7554
7798
|
// so o.render functions can access them without any DOM lookup.
|
|
7555
7799
|
if (content != null) {
|
|
7556
|
-
if (
|
|
7800
|
+
if (_isA(content)) {
|
|
7557
7801
|
content.forEach(child => {
|
|
7558
7802
|
if (child != null) {
|
|
7559
7803
|
// Handle ComponentHandle in content arrays (Level 2 children)
|
|
@@ -7573,20 +7817,20 @@ bw.createDOM = function(taco, options = {}) {
|
|
|
7573
7817
|
if (childEl._bw_refs) {
|
|
7574
7818
|
if (!el._bw_refs) el._bw_refs = {};
|
|
7575
7819
|
for (var rk in childEl._bw_refs) {
|
|
7576
|
-
if (
|
|
7820
|
+
if (_hop.call(childEl._bw_refs, rk)) {
|
|
7577
7821
|
el._bw_refs[rk] = childEl._bw_refs[rk];
|
|
7578
7822
|
}
|
|
7579
7823
|
}
|
|
7580
7824
|
}
|
|
7581
7825
|
}
|
|
7582
7826
|
});
|
|
7583
|
-
} else if (
|
|
7827
|
+
} else if (_is(content, 'object') && content.__bw_raw) {
|
|
7584
7828
|
// Raw HTML content — inject via innerHTML
|
|
7585
7829
|
el.innerHTML = content.v;
|
|
7586
7830
|
} else if (content._bwComponent === true) {
|
|
7587
7831
|
// Single ComponentHandle as content
|
|
7588
7832
|
content.mount(el);
|
|
7589
|
-
} else if (
|
|
7833
|
+
} else if (_is(content, 'object') && content.t) {
|
|
7590
7834
|
var childEl = bw.createDOM(content, options);
|
|
7591
7835
|
el.appendChild(childEl);
|
|
7592
7836
|
var childBwId = content.a ? (content.a['data-bw_id'] || content.a.id) : null;
|
|
@@ -7597,7 +7841,7 @@ bw.createDOM = function(taco, options = {}) {
|
|
|
7597
7841
|
if (childEl._bw_refs) {
|
|
7598
7842
|
if (!el._bw_refs) el._bw_refs = {};
|
|
7599
7843
|
for (var rk in childEl._bw_refs) {
|
|
7600
|
-
if (
|
|
7844
|
+
if (_hop.call(childEl._bw_refs, rk)) {
|
|
7601
7845
|
el._bw_refs[rk] = childEl._bw_refs[rk];
|
|
7602
7846
|
}
|
|
7603
7847
|
}
|
|
@@ -7630,7 +7874,7 @@ bw.createDOM = function(taco, options = {}) {
|
|
|
7630
7874
|
el._bw_render = opts.render;
|
|
7631
7875
|
|
|
7632
7876
|
if (opts.mounted) {
|
|
7633
|
-
|
|
7877
|
+
_cw('bw.createDOM: o.render and o.mounted are mutually exclusive. o.render wins.');
|
|
7634
7878
|
}
|
|
7635
7879
|
|
|
7636
7880
|
// Queue initial render (same timing as mounted)
|
|
@@ -7703,7 +7947,7 @@ bw.DOM = function(target, taco, options = {}) {
|
|
|
7703
7947
|
const targetEl = bw._el(target);
|
|
7704
7948
|
|
|
7705
7949
|
if (!targetEl) {
|
|
7706
|
-
|
|
7950
|
+
_ce('bw.DOM: Target element not found:', target);
|
|
7707
7951
|
return null;
|
|
7708
7952
|
}
|
|
7709
7953
|
|
|
@@ -7743,7 +7987,7 @@ bw.DOM = function(target, taco, options = {}) {
|
|
|
7743
7987
|
targetEl.appendChild(taco.element);
|
|
7744
7988
|
}
|
|
7745
7989
|
// Handle arrays
|
|
7746
|
-
else if (
|
|
7990
|
+
else if (_isA(taco)) {
|
|
7747
7991
|
taco.forEach(t => {
|
|
7748
7992
|
if (t != null) {
|
|
7749
7993
|
if (t._bwComponent === true) {
|
|
@@ -7779,7 +8023,7 @@ bw.DOM = function(target, taco, options = {}) {
|
|
|
7779
8023
|
bw.compileProps = function(handle, props = {}) {
|
|
7780
8024
|
const compiledProps = {};
|
|
7781
8025
|
|
|
7782
|
-
|
|
8026
|
+
_keys(props).forEach(key => {
|
|
7783
8027
|
// Create getter/setter for each prop
|
|
7784
8028
|
Object.defineProperty(compiledProps, key, {
|
|
7785
8029
|
get() {
|
|
@@ -8097,17 +8341,17 @@ bw.patch = function(id, content, attr) {
|
|
|
8097
8341
|
if (attr) {
|
|
8098
8342
|
// Patch an attribute
|
|
8099
8343
|
el.setAttribute(attr, String(content));
|
|
8100
|
-
} else if (
|
|
8344
|
+
} else if (_isA(content)) {
|
|
8101
8345
|
// Patch with array of children (strings and/or TACOs)
|
|
8102
8346
|
el.innerHTML = '';
|
|
8103
8347
|
content.forEach(function(item) {
|
|
8104
|
-
if (
|
|
8348
|
+
if (_is(item, 'string') || _is(item, 'number')) {
|
|
8105
8349
|
el.appendChild(document.createTextNode(String(item)));
|
|
8106
8350
|
} else if (item && item.t) {
|
|
8107
8351
|
el.appendChild(bw.createDOM(item));
|
|
8108
8352
|
}
|
|
8109
8353
|
});
|
|
8110
|
-
} else if (
|
|
8354
|
+
} else if (_is(content, 'object') && content.t) {
|
|
8111
8355
|
// Patch with a TACO — replace children
|
|
8112
8356
|
el.innerHTML = '';
|
|
8113
8357
|
el.appendChild(bw.createDOM(content));
|
|
@@ -8138,7 +8382,7 @@ bw.patch = function(id, content, attr) {
|
|
|
8138
8382
|
bw.patchAll = function(patches) {
|
|
8139
8383
|
var results = {};
|
|
8140
8384
|
for (var id in patches) {
|
|
8141
|
-
if (
|
|
8385
|
+
if (_hop.call(patches, id)) {
|
|
8142
8386
|
results[id] = bw.patch(id, patches[id]);
|
|
8143
8387
|
}
|
|
8144
8388
|
}
|
|
@@ -8235,7 +8479,7 @@ bw.pub = function(topic, detail) {
|
|
|
8235
8479
|
snapshot[i].handler(detail);
|
|
8236
8480
|
called++;
|
|
8237
8481
|
} catch (err) {
|
|
8238
|
-
|
|
8482
|
+
_cw('bw.pub: subscriber error on topic "' + topic + '":', err);
|
|
8239
8483
|
}
|
|
8240
8484
|
}
|
|
8241
8485
|
return called;
|
|
@@ -8331,8 +8575,8 @@ bw._fnIDCounter = 0;
|
|
|
8331
8575
|
* @see bw.funcGetDispatchStr
|
|
8332
8576
|
*/
|
|
8333
8577
|
bw.funcRegister = function(fn, name) {
|
|
8334
|
-
if (
|
|
8335
|
-
var fnID = (
|
|
8578
|
+
if (!_is(fn, 'function')) return '';
|
|
8579
|
+
var fnID = (_is(name, 'string') && name.length > 0) ? name : ('bw_fn_' + bw._fnIDCounter++);
|
|
8336
8580
|
bw._fnRegistry[fnID] = fn;
|
|
8337
8581
|
return fnID;
|
|
8338
8582
|
};
|
|
@@ -8351,7 +8595,7 @@ bw.funcRegister = function(fn, name) {
|
|
|
8351
8595
|
bw.funcGetById = function(name, errFn) {
|
|
8352
8596
|
name = String(name);
|
|
8353
8597
|
if (name in bw._fnRegistry) return bw._fnRegistry[name];
|
|
8354
|
-
return (
|
|
8598
|
+
return _is(errFn, 'function') ? errFn : function() { _cw('bw.funcGetById: unregistered fn "' + name + '"'); };
|
|
8355
8599
|
};
|
|
8356
8600
|
|
|
8357
8601
|
/**
|
|
@@ -8392,13 +8636,30 @@ bw.funcUnregister = function(name) {
|
|
|
8392
8636
|
bw.funcGetRegistry = function() {
|
|
8393
8637
|
var copy = {};
|
|
8394
8638
|
for (var k in bw._fnRegistry) {
|
|
8395
|
-
if (
|
|
8639
|
+
if (_hop.call(bw._fnRegistry, k)) {
|
|
8396
8640
|
copy[k] = bw._fnRegistry[k];
|
|
8397
8641
|
}
|
|
8398
8642
|
}
|
|
8399
8643
|
return copy;
|
|
8400
8644
|
};
|
|
8401
8645
|
|
|
8646
|
+
/**
|
|
8647
|
+
* Minimal runtime shim for funcRegister dispatch in static HTML.
|
|
8648
|
+
* When embedded in a `<script>` tag, provides just enough infrastructure
|
|
8649
|
+
* for `bw.funcGetById()` calls to resolve. The actual function bodies
|
|
8650
|
+
* are emitted separately as `bw._fnRegistry['bw_fn_X'] = ...;` assignments.
|
|
8651
|
+
* @type {string}
|
|
8652
|
+
* @category Function Registry
|
|
8653
|
+
*/
|
|
8654
|
+
bw._FUNC_REGISTRY_SHIM = '(function(){var bw=window.bw||(window.bw={});' +
|
|
8655
|
+
'if(!bw._fnRegistry)bw._fnRegistry={};' +
|
|
8656
|
+
'bw.funcGetById=function(n){return bw._fnRegistry[n]||function(){' +
|
|
8657
|
+
'console.warn("bw: unregistered fn "+n)};};' +
|
|
8658
|
+
'bw.funcRegister=function(fn,name){' +
|
|
8659
|
+
'var id=name||("bw_fn_"+(bw._fnIDCounter=(bw._fnIDCounter||0)+1));' +
|
|
8660
|
+
'bw._fnRegistry[id]=fn;return id;};' +
|
|
8661
|
+
'window.bw=bw;})();';
|
|
8662
|
+
|
|
8402
8663
|
// ===================================================================================
|
|
8403
8664
|
// Template Binding Utilities
|
|
8404
8665
|
// ===================================================================================
|
|
@@ -8426,7 +8687,10 @@ bw._evaluatePath = function(state, path) {
|
|
|
8426
8687
|
var parts = path.split('.');
|
|
8427
8688
|
var val = state;
|
|
8428
8689
|
for (var i = 0; i < parts.length; i++) {
|
|
8429
|
-
if (val == null)
|
|
8690
|
+
if (val == null) {
|
|
8691
|
+
if (bw.debug) _cw('bw.debug: _evaluatePath — null at key "' + parts[i] + '" in path "' + path + '"');
|
|
8692
|
+
return '';
|
|
8693
|
+
}
|
|
8430
8694
|
val = val[parts[i]];
|
|
8431
8695
|
}
|
|
8432
8696
|
return (val == null) ? '' : val;
|
|
@@ -8446,7 +8710,7 @@ bw._evaluatePath = function(state, path) {
|
|
|
8446
8710
|
*/
|
|
8447
8711
|
bw._compiledExprs = {};
|
|
8448
8712
|
bw._resolveTemplate = function(str, state, compile) {
|
|
8449
|
-
if (
|
|
8713
|
+
if (!_is(str, 'string') || str.indexOf('${') < 0) return str;
|
|
8450
8714
|
var bindings = bw._parseBindings(str);
|
|
8451
8715
|
if (bindings.length === 0) return str;
|
|
8452
8716
|
|
|
@@ -8468,6 +8732,7 @@ bw._resolveTemplate = function(str, state, compile) {
|
|
|
8468
8732
|
try {
|
|
8469
8733
|
val = bw._compiledExprs[b.expr](state);
|
|
8470
8734
|
} catch (e) {
|
|
8735
|
+
if (bw.debug) _cw('bw.debug: _resolveTemplate — Tier 2 eval failed for "${' + b.expr + '}":', e.message);
|
|
8471
8736
|
val = '';
|
|
8472
8737
|
}
|
|
8473
8738
|
} else {
|
|
@@ -8576,7 +8841,7 @@ function ComponentHandle(taco) {
|
|
|
8576
8841
|
this._state = {};
|
|
8577
8842
|
if (o.state) {
|
|
8578
8843
|
for (var k in o.state) {
|
|
8579
|
-
if (
|
|
8844
|
+
if (_hop.call(o.state, k)) {
|
|
8580
8845
|
this._state[k] = o.state[k];
|
|
8581
8846
|
}
|
|
8582
8847
|
}
|
|
@@ -8585,7 +8850,7 @@ function ComponentHandle(taco) {
|
|
|
8585
8850
|
this._actions = {};
|
|
8586
8851
|
if (o.actions) {
|
|
8587
8852
|
for (var k2 in o.actions) {
|
|
8588
|
-
if (
|
|
8853
|
+
if (_hop.call(o.actions, k2)) {
|
|
8589
8854
|
this._actions[k2] = o.actions[k2];
|
|
8590
8855
|
}
|
|
8591
8856
|
}
|
|
@@ -8595,7 +8860,7 @@ function ComponentHandle(taco) {
|
|
|
8595
8860
|
if (o.methods) {
|
|
8596
8861
|
var self = this;
|
|
8597
8862
|
for (var k3 in o.methods) {
|
|
8598
|
-
if (
|
|
8863
|
+
if (_hop.call(o.methods, k3)) {
|
|
8599
8864
|
this._methods[k3] = o.methods[k3];
|
|
8600
8865
|
(function(methodName, methodFn) {
|
|
8601
8866
|
self[methodName] = function() {
|
|
@@ -8628,14 +8893,23 @@ function ComponentHandle(taco) {
|
|
|
8628
8893
|
this._compile = !!o.compile;
|
|
8629
8894
|
this._bw_refs = {};
|
|
8630
8895
|
this._refCounter = 0;
|
|
8896
|
+
// Child component ownership (Bug #5)
|
|
8897
|
+
this._children = [];
|
|
8898
|
+
this._parent = null;
|
|
8899
|
+
// Factory metadata for BCCL rebuild (Bug #6)
|
|
8900
|
+
this._factory = taco._bwFactory || null;
|
|
8631
8901
|
}
|
|
8632
8902
|
|
|
8903
|
+
// Short alias for ComponentHandle.prototype (see alias block at top of file).
|
|
8904
|
+
// 28 method definitions × 25 chars = ~700B raw savings in minified output.
|
|
8905
|
+
var _chp = ComponentHandle.prototype;
|
|
8906
|
+
|
|
8633
8907
|
// ── State Methods ──
|
|
8634
8908
|
|
|
8635
8909
|
/**
|
|
8636
8910
|
* Get a state value. Dot-path supported: `get('user.name')`
|
|
8637
8911
|
*/
|
|
8638
|
-
|
|
8912
|
+
_chp.get = function(key) {
|
|
8639
8913
|
return bw._evaluatePath(this._state, key);
|
|
8640
8914
|
};
|
|
8641
8915
|
|
|
@@ -8645,12 +8919,13 @@ ComponentHandle.prototype.get = function(key) {
|
|
|
8645
8919
|
* @param {*} value - New value
|
|
8646
8920
|
* @param {Object} [opts] - Options. `{sync: true}` for immediate flush.
|
|
8647
8921
|
*/
|
|
8648
|
-
|
|
8922
|
+
_chp.set = function(key, value, opts) {
|
|
8649
8923
|
// Dot-path set
|
|
8650
8924
|
var parts = key.split('.');
|
|
8651
8925
|
var obj = this._state;
|
|
8652
8926
|
for (var i = 0; i < parts.length - 1; i++) {
|
|
8653
|
-
if (obj[parts[i]]
|
|
8927
|
+
if (!_is(obj[parts[i]], 'object')) {
|
|
8928
|
+
if (bw.debug) _cw('bw.debug: set() — auto-creating intermediate "' + parts[i] + '" in path "' + key + '"');
|
|
8654
8929
|
obj[parts[i]] = {};
|
|
8655
8930
|
}
|
|
8656
8931
|
obj = obj[parts[i]];
|
|
@@ -8670,10 +8945,10 @@ ComponentHandle.prototype.set = function(key, value, opts) {
|
|
|
8670
8945
|
/**
|
|
8671
8946
|
* Get a shallow clone of the full state.
|
|
8672
8947
|
*/
|
|
8673
|
-
|
|
8948
|
+
_chp.getState = function() {
|
|
8674
8949
|
var clone = {};
|
|
8675
8950
|
for (var k in this._state) {
|
|
8676
|
-
if (
|
|
8951
|
+
if (_hop.call(this._state, k)) {
|
|
8677
8952
|
clone[k] = this._state[k];
|
|
8678
8953
|
}
|
|
8679
8954
|
}
|
|
@@ -8685,9 +8960,9 @@ ComponentHandle.prototype.getState = function() {
|
|
|
8685
8960
|
* @param {Object} updates - Key-value pairs to merge
|
|
8686
8961
|
* @param {Object} [opts] - Options. `{sync: true}` for immediate flush.
|
|
8687
8962
|
*/
|
|
8688
|
-
|
|
8963
|
+
_chp.setState = function(updates, opts) {
|
|
8689
8964
|
for (var k in updates) {
|
|
8690
|
-
if (
|
|
8965
|
+
if (_hop.call(updates, k)) {
|
|
8691
8966
|
this._state[k] = updates[k];
|
|
8692
8967
|
this._dirtyKeys[k] = true;
|
|
8693
8968
|
}
|
|
@@ -8704,9 +8979,9 @@ ComponentHandle.prototype.setState = function(updates, opts) {
|
|
|
8704
8979
|
/**
|
|
8705
8980
|
* Push a value onto an array in state. Clones the array.
|
|
8706
8981
|
*/
|
|
8707
|
-
|
|
8982
|
+
_chp.push = function(key, val) {
|
|
8708
8983
|
var arr = this.get(key);
|
|
8709
|
-
var newArr =
|
|
8984
|
+
var newArr = _isA(arr) ? arr.slice() : [];
|
|
8710
8985
|
newArr.push(val);
|
|
8711
8986
|
this.set(key, newArr);
|
|
8712
8987
|
};
|
|
@@ -8714,9 +8989,9 @@ ComponentHandle.prototype.push = function(key, val) {
|
|
|
8714
8989
|
/**
|
|
8715
8990
|
* Splice an array in state. Clones the array.
|
|
8716
8991
|
*/
|
|
8717
|
-
|
|
8992
|
+
_chp.splice = function(key, start, deleteCount) {
|
|
8718
8993
|
var arr = this.get(key);
|
|
8719
|
-
var newArr =
|
|
8994
|
+
var newArr = _isA(arr) ? arr.slice() : [];
|
|
8720
8995
|
var args = [start, deleteCount].concat(Array.prototype.slice.call(arguments, 3));
|
|
8721
8996
|
Array.prototype.splice.apply(newArr, args);
|
|
8722
8997
|
this.set(key, newArr);
|
|
@@ -8724,7 +8999,7 @@ ComponentHandle.prototype.splice = function(key, start, deleteCount) {
|
|
|
8724
8999
|
|
|
8725
9000
|
// ── Scheduling ──
|
|
8726
9001
|
|
|
8727
|
-
|
|
9002
|
+
_chp._scheduleDirty = function() {
|
|
8728
9003
|
if (!this._scheduled) {
|
|
8729
9004
|
this._scheduled = true;
|
|
8730
9005
|
bw._dirtyComponents.push(this);
|
|
@@ -8739,17 +9014,17 @@ ComponentHandle.prototype._scheduleDirty = function() {
|
|
|
8739
9014
|
* Creates binding descriptors with refIds for targeted DOM updates.
|
|
8740
9015
|
* @private
|
|
8741
9016
|
*/
|
|
8742
|
-
|
|
9017
|
+
_chp._compileBindings = function() {
|
|
8743
9018
|
this._bindings = [];
|
|
8744
9019
|
this._refCounter = 0;
|
|
8745
|
-
var stateKeys =
|
|
9020
|
+
var stateKeys = _keys(this._state);
|
|
8746
9021
|
var self = this;
|
|
8747
9022
|
|
|
8748
9023
|
function walkTaco(taco, path) {
|
|
8749
|
-
if (taco
|
|
9024
|
+
if (!_is(taco, 'object') || !taco.t) return taco;
|
|
8750
9025
|
|
|
8751
9026
|
// Check content for bindings
|
|
8752
|
-
if (
|
|
9027
|
+
if (_is(taco.c, 'string') && taco.c.indexOf('${') >= 0) {
|
|
8753
9028
|
var refId = 'bw_ref_' + self._refCounter++;
|
|
8754
9029
|
var parsed = bw._parseBindings(taco.c);
|
|
8755
9030
|
var deps = [];
|
|
@@ -8771,10 +9046,10 @@ ComponentHandle.prototype._compileBindings = function() {
|
|
|
8771
9046
|
// Check attributes for bindings
|
|
8772
9047
|
if (taco.a) {
|
|
8773
9048
|
for (var attrName in taco.a) {
|
|
8774
|
-
if (!
|
|
9049
|
+
if (!_hop.call(taco.a, attrName)) continue;
|
|
8775
9050
|
if (attrName === 'data-bw_ref') continue;
|
|
8776
9051
|
var attrVal = taco.a[attrName];
|
|
8777
|
-
if (
|
|
9052
|
+
if (_is(attrVal, 'string') && attrVal.indexOf('${') >= 0) {
|
|
8778
9053
|
var refId2 = 'bw_ref_' + self._refCounter++;
|
|
8779
9054
|
var parsed2 = bw._parseBindings(attrVal);
|
|
8780
9055
|
var deps2 = [];
|
|
@@ -8800,9 +9075,27 @@ ComponentHandle.prototype._compileBindings = function() {
|
|
|
8800
9075
|
}
|
|
8801
9076
|
|
|
8802
9077
|
// Recurse into children
|
|
8803
|
-
if (
|
|
9078
|
+
if (_isA(taco.c)) {
|
|
8804
9079
|
for (var i = 0; i < taco.c.length; i++) {
|
|
8805
|
-
|
|
9080
|
+
// Wrap string children with ${expr} in a span so patches target the span, not the parent
|
|
9081
|
+
if (_is(taco.c[i], 'string') && taco.c[i].indexOf('${') >= 0) {
|
|
9082
|
+
var mixedRefId = 'bw_ref_' + self._refCounter++;
|
|
9083
|
+
var mixedParsed = bw._parseBindings(taco.c[i]);
|
|
9084
|
+
var mixedDeps = [];
|
|
9085
|
+
for (var mi = 0; mi < mixedParsed.length; mi++) {
|
|
9086
|
+
mixedDeps = mixedDeps.concat(bw._extractDeps(mixedParsed[mi].expr, stateKeys));
|
|
9087
|
+
}
|
|
9088
|
+
self._bindings.push({
|
|
9089
|
+
expr: taco.c[i],
|
|
9090
|
+
type: 'content',
|
|
9091
|
+
refId: mixedRefId,
|
|
9092
|
+
deps: mixedDeps,
|
|
9093
|
+
template: taco.c[i]
|
|
9094
|
+
});
|
|
9095
|
+
// Replace string with a span wrapper so textContent targets the span only
|
|
9096
|
+
taco.c[i] = { t: 'span', a: { 'data-bw_ref': mixedRefId, style: 'display:contents' }, c: taco.c[i] };
|
|
9097
|
+
}
|
|
9098
|
+
if (_is(taco.c[i], 'object') && taco.c[i].t) {
|
|
8806
9099
|
walkTaco(taco.c[i], path.concat(i));
|
|
8807
9100
|
}
|
|
8808
9101
|
// Handle bw.when/bw.each markers
|
|
@@ -8837,7 +9130,7 @@ ComponentHandle.prototype._compileBindings = function() {
|
|
|
8837
9130
|
taco.c[i]._refId = eachRefId;
|
|
8838
9131
|
}
|
|
8839
9132
|
}
|
|
8840
|
-
} else if (taco.c
|
|
9133
|
+
} else if (_is(taco.c, 'object') && taco.c.t) {
|
|
8841
9134
|
walkTaco(taco.c, path.concat(0));
|
|
8842
9135
|
}
|
|
8843
9136
|
|
|
@@ -8853,7 +9146,7 @@ ComponentHandle.prototype._compileBindings = function() {
|
|
|
8853
9146
|
* Build ref map from the live DOM after createDOM.
|
|
8854
9147
|
* @private
|
|
8855
9148
|
*/
|
|
8856
|
-
|
|
9149
|
+
_chp._collectRefs = function() {
|
|
8857
9150
|
this._bw_refs = {};
|
|
8858
9151
|
if (!this.element) return;
|
|
8859
9152
|
var els = this.element.querySelectorAll('[data-bw_ref]');
|
|
@@ -8874,7 +9167,7 @@ ComponentHandle.prototype._collectRefs = function() {
|
|
|
8874
9167
|
* Creates DOM, compiles bindings, registers actions, and calls lifecycle hooks.
|
|
8875
9168
|
* @param {Element} parentEl - DOM element to mount into
|
|
8876
9169
|
*/
|
|
8877
|
-
|
|
9170
|
+
_chp.mount = function(parentEl) {
|
|
8878
9171
|
// willMount hook
|
|
8879
9172
|
if (this._hooks.willMount) this._hooks.willMount(this);
|
|
8880
9173
|
|
|
@@ -8896,7 +9189,7 @@ ComponentHandle.prototype.mount = function(parentEl) {
|
|
|
8896
9189
|
// Register named actions in function registry
|
|
8897
9190
|
var self = this;
|
|
8898
9191
|
for (var actionName in this._actions) {
|
|
8899
|
-
if (
|
|
9192
|
+
if (_hop.call(this._actions, actionName)) {
|
|
8900
9193
|
var registeredName = this._bwId + '_' + actionName;
|
|
8901
9194
|
(function(aName) {
|
|
8902
9195
|
bw.funcRegister(function(evt) {
|
|
@@ -8915,6 +9208,11 @@ ComponentHandle.prototype.mount = function(parentEl) {
|
|
|
8915
9208
|
this.element = bw.createDOM(tacoForDOM);
|
|
8916
9209
|
this.element._bwComponentHandle = this;
|
|
8917
9210
|
this.element.setAttribute('data-bw_comp_id', this._bwId);
|
|
9211
|
+
|
|
9212
|
+
// Restore o.render from original TACO (stripped by _tacoForDOM)
|
|
9213
|
+
if (this.taco.o && this.taco.o.render) {
|
|
9214
|
+
this.element._bw_render = this.taco.o.render;
|
|
9215
|
+
}
|
|
8918
9216
|
if (this._userTag) {
|
|
8919
9217
|
this.element.classList.add(this._userTag);
|
|
8920
9218
|
}
|
|
@@ -8930,6 +9228,16 @@ ComponentHandle.prototype.mount = function(parentEl) {
|
|
|
8930
9228
|
|
|
8931
9229
|
this.mounted = true;
|
|
8932
9230
|
|
|
9231
|
+
// Scan for child ComponentHandles and link parent/child (Bug #5)
|
|
9232
|
+
var childEls = this.element.querySelectorAll('[data-bw_comp_id]');
|
|
9233
|
+
for (var ci = 0; ci < childEls.length; ci++) {
|
|
9234
|
+
var ch = childEls[ci]._bwComponentHandle;
|
|
9235
|
+
if (ch && ch !== this && !ch._parent) {
|
|
9236
|
+
ch._parent = this;
|
|
9237
|
+
this._children.push(ch);
|
|
9238
|
+
}
|
|
9239
|
+
}
|
|
9240
|
+
|
|
8933
9241
|
// mounted hook (backward compat: fn.length === 2 wraps (el, state))
|
|
8934
9242
|
if (this._hooks.mounted) {
|
|
8935
9243
|
if (this._hooks.mounted.length === 2) {
|
|
@@ -8938,16 +9246,21 @@ ComponentHandle.prototype.mount = function(parentEl) {
|
|
|
8938
9246
|
this._hooks.mounted(this);
|
|
8939
9247
|
}
|
|
8940
9248
|
}
|
|
9249
|
+
|
|
9250
|
+
// Invoke o.render on initial mount (if present)
|
|
9251
|
+
if (this.element._bw_render) {
|
|
9252
|
+
this.element._bw_render(this.element, this._state);
|
|
9253
|
+
}
|
|
8941
9254
|
};
|
|
8942
9255
|
|
|
8943
9256
|
/**
|
|
8944
9257
|
* Prepare TACO for initial render: resolve when/each markers.
|
|
8945
9258
|
* @private
|
|
8946
9259
|
*/
|
|
8947
|
-
|
|
8948
|
-
if (!taco
|
|
9260
|
+
_chp._prepareTaco = function(taco) {
|
|
9261
|
+
if (!_is(taco, 'object')) return;
|
|
8949
9262
|
|
|
8950
|
-
if (
|
|
9263
|
+
if (_isA(taco.c)) {
|
|
8951
9264
|
for (var i = taco.c.length - 1; i >= 0; i--) {
|
|
8952
9265
|
var child = taco.c[i];
|
|
8953
9266
|
if (child && child._bwWhen) {
|
|
@@ -8972,18 +9285,18 @@ ComponentHandle.prototype._prepareTaco = function(taco) {
|
|
|
8972
9285
|
var eachExprStr = child.expr.replace(/^\$\{|\}$/g, '');
|
|
8973
9286
|
var arr = bw._evaluatePath(this._state, eachExprStr);
|
|
8974
9287
|
var items = [];
|
|
8975
|
-
if (
|
|
9288
|
+
if (_isA(arr)) {
|
|
8976
9289
|
for (var j = 0; j < arr.length; j++) {
|
|
8977
9290
|
items.push(child.factory(arr[j], j));
|
|
8978
9291
|
}
|
|
8979
9292
|
}
|
|
8980
9293
|
taco.c[i] = { t: 'span', a: { 'data-bw_each': child._refId, style: 'display:contents' }, c: items };
|
|
8981
9294
|
}
|
|
8982
|
-
if (taco.c[i]
|
|
9295
|
+
if (_is(taco.c[i], 'object') && taco.c[i].t) {
|
|
8983
9296
|
this._prepareTaco(taco.c[i]);
|
|
8984
9297
|
}
|
|
8985
9298
|
}
|
|
8986
|
-
} else if (taco.c
|
|
9299
|
+
} else if (_is(taco.c, 'object') && taco.c.t) {
|
|
8987
9300
|
this._prepareTaco(taco.c);
|
|
8988
9301
|
}
|
|
8989
9302
|
};
|
|
@@ -8992,12 +9305,12 @@ ComponentHandle.prototype._prepareTaco = function(taco) {
|
|
|
8992
9305
|
* Wire action name strings (in onclick etc.) to dispatch function calls.
|
|
8993
9306
|
* @private
|
|
8994
9307
|
*/
|
|
8995
|
-
|
|
8996
|
-
if (!taco
|
|
9308
|
+
_chp._wireActions = function(taco) {
|
|
9309
|
+
if (!_is(taco, 'object') || !taco.t) return;
|
|
8997
9310
|
if (taco.a) {
|
|
8998
9311
|
for (var key in taco.a) {
|
|
8999
|
-
if (!
|
|
9000
|
-
if (key.startsWith('on') &&
|
|
9312
|
+
if (!_hop.call(taco.a, key)) continue;
|
|
9313
|
+
if (key.startsWith('on') && _is(taco.a[key], 'string')) {
|
|
9001
9314
|
var actionName = taco.a[key];
|
|
9002
9315
|
if (actionName in this._actions) {
|
|
9003
9316
|
var registeredName = this._bwId + '_' + actionName;
|
|
@@ -9011,11 +9324,11 @@ ComponentHandle.prototype._wireActions = function(taco) {
|
|
|
9011
9324
|
}
|
|
9012
9325
|
}
|
|
9013
9326
|
}
|
|
9014
|
-
if (
|
|
9327
|
+
if (_isA(taco.c)) {
|
|
9015
9328
|
for (var i = 0; i < taco.c.length; i++) {
|
|
9016
9329
|
this._wireActions(taco.c[i]);
|
|
9017
9330
|
}
|
|
9018
|
-
} else if (taco.c
|
|
9331
|
+
} else if (_is(taco.c, 'object') && taco.c.t) {
|
|
9019
9332
|
this._wireActions(taco.c);
|
|
9020
9333
|
}
|
|
9021
9334
|
};
|
|
@@ -9024,7 +9337,7 @@ ComponentHandle.prototype._wireActions = function(taco) {
|
|
|
9024
9337
|
* Deep-clone a TACO tree, preserving _bwWhen/_bwEach markers and their factories.
|
|
9025
9338
|
* @private
|
|
9026
9339
|
*/
|
|
9027
|
-
|
|
9340
|
+
_chp._deepCloneTaco = function(taco) {
|
|
9028
9341
|
if (taco == null) return taco;
|
|
9029
9342
|
// Preserve _bwWhen / _bwEach markers (contain functions)
|
|
9030
9343
|
if (taco._bwWhen) {
|
|
@@ -9036,18 +9349,18 @@ ComponentHandle.prototype._deepCloneTaco = function(taco) {
|
|
|
9036
9349
|
if (taco._bwEach) {
|
|
9037
9350
|
return { _bwEach: true, expr: taco.expr, factory: taco.factory, _refId: taco._refId };
|
|
9038
9351
|
}
|
|
9039
|
-
if (
|
|
9352
|
+
if (!_is(taco, 'object') || !taco.t) return taco;
|
|
9040
9353
|
var result = { t: taco.t };
|
|
9041
9354
|
if (taco.a) {
|
|
9042
9355
|
result.a = {};
|
|
9043
9356
|
for (var k in taco.a) {
|
|
9044
|
-
if (
|
|
9357
|
+
if (_hop.call(taco.a, k)) result.a[k] = taco.a[k];
|
|
9045
9358
|
}
|
|
9046
9359
|
}
|
|
9047
9360
|
if (taco.c != null) {
|
|
9048
|
-
if (
|
|
9361
|
+
if (_isA(taco.c)) {
|
|
9049
9362
|
result.c = taco.c.map(function(child) { return this._deepCloneTaco(child); }.bind(this));
|
|
9050
|
-
} else if (
|
|
9363
|
+
} else if (_is(taco.c, 'object')) {
|
|
9051
9364
|
result.c = this._deepCloneTaco(taco.c);
|
|
9052
9365
|
} else {
|
|
9053
9366
|
result.c = taco.c;
|
|
@@ -9061,27 +9374,31 @@ ComponentHandle.prototype._deepCloneTaco = function(taco) {
|
|
|
9061
9374
|
* Create a copy of TACO suitable for createDOM (strips o to prevent double lifecycle).
|
|
9062
9375
|
* @private
|
|
9063
9376
|
*/
|
|
9064
|
-
|
|
9065
|
-
if (!taco
|
|
9377
|
+
_chp._tacoForDOM = function(taco) {
|
|
9378
|
+
if (!_is(taco, 'object') || !taco.t) return taco;
|
|
9066
9379
|
var result = { t: taco.t };
|
|
9067
9380
|
if (taco.a) result.a = taco.a;
|
|
9068
9381
|
if (taco.c != null) {
|
|
9069
|
-
if (
|
|
9382
|
+
if (_isA(taco.c)) {
|
|
9070
9383
|
result.c = taco.c.map(function(child) { return this._tacoForDOM(child); }.bind(this));
|
|
9071
|
-
} else if (
|
|
9384
|
+
} else if (_is(taco.c, 'object') && taco.c.t) {
|
|
9072
9385
|
result.c = this._tacoForDOM(taco.c);
|
|
9073
9386
|
} else {
|
|
9074
9387
|
result.c = taco.c;
|
|
9075
9388
|
}
|
|
9076
9389
|
}
|
|
9077
9390
|
// Intentionally strip o (no mounted/unmount/state/render on sub-elements)
|
|
9391
|
+
if (taco.o && (taco.o.mounted || taco.o.render || taco.o.unmount)) {
|
|
9392
|
+
_cw('bw: _tacoForDOM stripped o.mounted/render/unmount from child <' + taco.t +
|
|
9393
|
+
'>. Use onclick attribute or bw.component() for child interactivity.');
|
|
9394
|
+
}
|
|
9078
9395
|
return result;
|
|
9079
9396
|
};
|
|
9080
9397
|
|
|
9081
9398
|
/**
|
|
9082
9399
|
* Unmount: remove from DOM, deactivate, preserve state for re-mount.
|
|
9083
9400
|
*/
|
|
9084
|
-
|
|
9401
|
+
_chp.unmount = function() {
|
|
9085
9402
|
if (!this.mounted) return;
|
|
9086
9403
|
|
|
9087
9404
|
// unmount hook
|
|
@@ -9116,12 +9433,23 @@ ComponentHandle.prototype.unmount = function() {
|
|
|
9116
9433
|
/**
|
|
9117
9434
|
* Destroy: unmount + clear state + unregister actions.
|
|
9118
9435
|
*/
|
|
9119
|
-
|
|
9436
|
+
_chp.destroy = function() {
|
|
9120
9437
|
// willDestroy hook
|
|
9121
9438
|
if (this._hooks.willDestroy) {
|
|
9122
9439
|
this._hooks.willDestroy(this);
|
|
9123
9440
|
}
|
|
9124
9441
|
|
|
9442
|
+
// Cascade destroy to children depth-first (Bug #5)
|
|
9443
|
+
for (var ci = this._children.length - 1; ci >= 0; ci--) {
|
|
9444
|
+
this._children[ci].destroy();
|
|
9445
|
+
}
|
|
9446
|
+
this._children = [];
|
|
9447
|
+
if (this._parent) {
|
|
9448
|
+
var idx = this._parent._children.indexOf(this);
|
|
9449
|
+
if (idx >= 0) this._parent._children.splice(idx, 1);
|
|
9450
|
+
this._parent = null;
|
|
9451
|
+
}
|
|
9452
|
+
|
|
9125
9453
|
this.unmount();
|
|
9126
9454
|
|
|
9127
9455
|
// Unregister actions from function registry
|
|
@@ -9148,12 +9476,36 @@ ComponentHandle.prototype.destroy = function() {
|
|
|
9148
9476
|
* Flush dirty state: resolve changed bindings and apply to DOM.
|
|
9149
9477
|
* @private
|
|
9150
9478
|
*/
|
|
9151
|
-
|
|
9479
|
+
_chp._flush = function() {
|
|
9152
9480
|
this._scheduled = false;
|
|
9153
|
-
var changedKeys =
|
|
9481
|
+
var changedKeys = _keys(this._dirtyKeys);
|
|
9154
9482
|
this._dirtyKeys = {};
|
|
9155
9483
|
if (changedKeys.length === 0 || !this.mounted) return;
|
|
9156
9484
|
|
|
9485
|
+
// Factory rebuild: if a BCCL factory exists and changed keys overlap factory props,
|
|
9486
|
+
// rebuild the TACO from the factory with merged state (Bug #6)
|
|
9487
|
+
if (this._factory) {
|
|
9488
|
+
var rebuildNeeded = false;
|
|
9489
|
+
for (var fi = 0; fi < changedKeys.length; fi++) {
|
|
9490
|
+
if (_hop.call(this._factory.props, changedKeys[fi])) {
|
|
9491
|
+
rebuildNeeded = true; break;
|
|
9492
|
+
}
|
|
9493
|
+
}
|
|
9494
|
+
if (rebuildNeeded) {
|
|
9495
|
+
var merged = {};
|
|
9496
|
+
for (var mk in this._factory.props) if (_hop.call(this._factory.props, mk)) merged[mk] = this._factory.props[mk];
|
|
9497
|
+
for (var sk in this._state) if (_hop.call(this._state, sk)) merged[sk] = this._state[sk];
|
|
9498
|
+
this._factory.props = merged;
|
|
9499
|
+
var newTaco = bw.make(this._factory.type, merged);
|
|
9500
|
+
newTaco._bwFactory = this._factory;
|
|
9501
|
+
this.taco = newTaco;
|
|
9502
|
+
this._originalTaco = this._deepCloneTaco(newTaco);
|
|
9503
|
+
this._render();
|
|
9504
|
+
if (this._hooks.onUpdate) this._hooks.onUpdate(this, changedKeys);
|
|
9505
|
+
return;
|
|
9506
|
+
}
|
|
9507
|
+
}
|
|
9508
|
+
|
|
9157
9509
|
// willUpdate hook
|
|
9158
9510
|
if (this._hooks.willUpdate) {
|
|
9159
9511
|
this._hooks.willUpdate(this, changedKeys);
|
|
@@ -9192,7 +9544,7 @@ ComponentHandle.prototype._flush = function() {
|
|
|
9192
9544
|
* Returns list of patches to apply.
|
|
9193
9545
|
* @private
|
|
9194
9546
|
*/
|
|
9195
|
-
|
|
9547
|
+
_chp._resolveBindings = function(changedKeys) {
|
|
9196
9548
|
var patches = [];
|
|
9197
9549
|
for (var i = 0; i < this._bindings.length; i++) {
|
|
9198
9550
|
var b = this._bindings[i];
|
|
@@ -9228,11 +9580,14 @@ ComponentHandle.prototype._resolveBindings = function(changedKeys) {
|
|
|
9228
9580
|
* Apply patches to DOM.
|
|
9229
9581
|
* @private
|
|
9230
9582
|
*/
|
|
9231
|
-
|
|
9583
|
+
_chp._applyPatches = function(patches) {
|
|
9232
9584
|
for (var i = 0; i < patches.length; i++) {
|
|
9233
9585
|
var p = patches[i];
|
|
9234
9586
|
var el = this._bw_refs[p.refId];
|
|
9235
|
-
if (!el)
|
|
9587
|
+
if (!el) {
|
|
9588
|
+
if (bw.debug) _cw('bw.debug: _applyPatches — ref "' + p.refId + '" not found in DOM');
|
|
9589
|
+
continue;
|
|
9590
|
+
}
|
|
9236
9591
|
if (p.type === 'content') {
|
|
9237
9592
|
el.textContent = p.value;
|
|
9238
9593
|
} else if (p.type === 'attribute') {
|
|
@@ -9249,7 +9604,7 @@ ComponentHandle.prototype._applyPatches = function(patches) {
|
|
|
9249
9604
|
* Resolve all bindings and apply (used for initial render).
|
|
9250
9605
|
* @private
|
|
9251
9606
|
*/
|
|
9252
|
-
|
|
9607
|
+
_chp._resolveAndApplyAll = function() {
|
|
9253
9608
|
var patches = [];
|
|
9254
9609
|
for (var i = 0; i < this._bindings.length; i++) {
|
|
9255
9610
|
var b = this._bindings[i];
|
|
@@ -9272,7 +9627,7 @@ ComponentHandle.prototype._resolveAndApplyAll = function() {
|
|
|
9272
9627
|
* Full re-render for structural changes (when/each branch switches).
|
|
9273
9628
|
* @private
|
|
9274
9629
|
*/
|
|
9275
|
-
|
|
9630
|
+
_chp._render = function() {
|
|
9276
9631
|
if (!this.element || !this.element.parentNode) return;
|
|
9277
9632
|
var parent = this.element.parentNode;
|
|
9278
9633
|
var nextSibling = this.element.nextSibling;
|
|
@@ -9312,7 +9667,7 @@ ComponentHandle.prototype._render = function() {
|
|
|
9312
9667
|
* @param {string} event - Event name (e.g., 'click')
|
|
9313
9668
|
* @param {Function} handler - Event handler
|
|
9314
9669
|
*/
|
|
9315
|
-
|
|
9670
|
+
_chp.on = function(event, handler) {
|
|
9316
9671
|
if (this.element) {
|
|
9317
9672
|
this.element.addEventListener(event, handler);
|
|
9318
9673
|
}
|
|
@@ -9324,7 +9679,7 @@ ComponentHandle.prototype.on = function(event, handler) {
|
|
|
9324
9679
|
* @param {string} event - Event name
|
|
9325
9680
|
* @param {Function} handler - Handler to remove
|
|
9326
9681
|
*/
|
|
9327
|
-
|
|
9682
|
+
_chp.off = function(event, handler) {
|
|
9328
9683
|
if (this.element) {
|
|
9329
9684
|
this.element.removeEventListener(event, handler);
|
|
9330
9685
|
}
|
|
@@ -9339,7 +9694,7 @@ ComponentHandle.prototype.off = function(event, handler) {
|
|
|
9339
9694
|
* @param {Function} handler - Handler function
|
|
9340
9695
|
* @returns {Function} Unsubscribe function
|
|
9341
9696
|
*/
|
|
9342
|
-
|
|
9697
|
+
_chp.sub = function(topic, handler) {
|
|
9343
9698
|
var unsub = bw.sub(topic, handler);
|
|
9344
9699
|
this._subs.push(unsub);
|
|
9345
9700
|
return unsub;
|
|
@@ -9350,10 +9705,10 @@ ComponentHandle.prototype.sub = function(topic, handler) {
|
|
|
9350
9705
|
* @param {string} name - Action name
|
|
9351
9706
|
* @param {...*} args - Arguments passed after comp
|
|
9352
9707
|
*/
|
|
9353
|
-
|
|
9708
|
+
_chp.action = function(name) {
|
|
9354
9709
|
var fn = this._actions[name];
|
|
9355
9710
|
if (!fn) {
|
|
9356
|
-
|
|
9711
|
+
_cw('ComponentHandle.action: unknown action "' + name + '"');
|
|
9357
9712
|
return;
|
|
9358
9713
|
}
|
|
9359
9714
|
var args = [this].concat(Array.prototype.slice.call(arguments, 1));
|
|
@@ -9365,7 +9720,7 @@ ComponentHandle.prototype.action = function(name) {
|
|
|
9365
9720
|
* @param {string} sel - CSS selector
|
|
9366
9721
|
* @returns {Element|null}
|
|
9367
9722
|
*/
|
|
9368
|
-
|
|
9723
|
+
_chp.select = function(sel) {
|
|
9369
9724
|
return this.element ? this.element.querySelector(sel) : null;
|
|
9370
9725
|
};
|
|
9371
9726
|
|
|
@@ -9374,7 +9729,7 @@ ComponentHandle.prototype.select = function(sel) {
|
|
|
9374
9729
|
* @param {string} sel - CSS selector
|
|
9375
9730
|
* @returns {Element[]}
|
|
9376
9731
|
*/
|
|
9377
|
-
|
|
9732
|
+
_chp.selectAll = function(sel) {
|
|
9378
9733
|
if (!this.element) return [];
|
|
9379
9734
|
return Array.prototype.slice.call(this.element.querySelectorAll(sel));
|
|
9380
9735
|
};
|
|
@@ -9385,7 +9740,7 @@ ComponentHandle.prototype.selectAll = function(sel) {
|
|
|
9385
9740
|
* @param {string} tag - User-defined identifier (e.g. 'dashboard_prod_east')
|
|
9386
9741
|
* @returns {ComponentHandle} this (for chaining)
|
|
9387
9742
|
*/
|
|
9388
|
-
|
|
9743
|
+
_chp.userTag = function(tag) {
|
|
9389
9744
|
this._userTag = tag;
|
|
9390
9745
|
if (this.element) {
|
|
9391
9746
|
this.element.classList.add(tag);
|
|
@@ -9486,8 +9841,8 @@ bw.message = function(target, action, data) {
|
|
|
9486
9841
|
}
|
|
9487
9842
|
if (!el || !el._bwComponentHandle) return false;
|
|
9488
9843
|
var comp = el._bwComponentHandle;
|
|
9489
|
-
if (
|
|
9490
|
-
|
|
9844
|
+
if (!_is(comp[action], 'function')) {
|
|
9845
|
+
_cw('bw.message: unknown action "' + action + '" on component ' + target);
|
|
9491
9846
|
return false;
|
|
9492
9847
|
}
|
|
9493
9848
|
comp[action](data);
|
|
@@ -9524,7 +9879,7 @@ bw._builtinClientFunctions = {
|
|
|
9524
9879
|
},
|
|
9525
9880
|
focus: function(selector) {
|
|
9526
9881
|
var el = bw._el(selector);
|
|
9527
|
-
if (el &&
|
|
9882
|
+
if (el && _is(el.focus, 'function')) el.focus();
|
|
9528
9883
|
},
|
|
9529
9884
|
download: function(filename, content, mimeType) {
|
|
9530
9885
|
if (typeof document === 'undefined') return;
|
|
@@ -9689,12 +10044,12 @@ bw.clientApply = function(msg) {
|
|
|
9689
10044
|
} else if (type === 'remove') {
|
|
9690
10045
|
var toRemove = bw._el(target);
|
|
9691
10046
|
if (!toRemove) return false;
|
|
9692
|
-
if (
|
|
10047
|
+
if (_is(bw.cleanup, 'function')) bw.cleanup(toRemove);
|
|
9693
10048
|
toRemove.remove();
|
|
9694
10049
|
return true;
|
|
9695
10050
|
|
|
9696
10051
|
} else if (type === 'batch') {
|
|
9697
|
-
if (!
|
|
10052
|
+
if (!_isA(msg.ops)) return false;
|
|
9698
10053
|
var allOk = true;
|
|
9699
10054
|
msg.ops.forEach(function(op) {
|
|
9700
10055
|
if (!bw.clientApply(op)) allOk = false;
|
|
@@ -9710,26 +10065,26 @@ bw.clientApply = function(msg) {
|
|
|
9710
10065
|
bw._clientFunctions[msg.name] = new Function('return ' + msg.body)();
|
|
9711
10066
|
return true;
|
|
9712
10067
|
} catch (e) {
|
|
9713
|
-
|
|
10068
|
+
_ce('[bw] register error:', msg.name, e);
|
|
9714
10069
|
return false;
|
|
9715
10070
|
}
|
|
9716
10071
|
|
|
9717
10072
|
} else if (type === 'call') {
|
|
9718
10073
|
if (!msg.name) return false;
|
|
9719
10074
|
var fn = bw._clientFunctions[msg.name] || bw._builtinClientFunctions[msg.name];
|
|
9720
|
-
if (
|
|
10075
|
+
if (!_is(fn, 'function')) return false;
|
|
9721
10076
|
try {
|
|
9722
|
-
var args =
|
|
10077
|
+
var args = _isA(msg.args) ? msg.args : [];
|
|
9723
10078
|
fn.apply(null, args);
|
|
9724
10079
|
return true;
|
|
9725
10080
|
} catch (e) {
|
|
9726
|
-
|
|
10081
|
+
_ce('[bw] call error:', msg.name, e);
|
|
9727
10082
|
return false;
|
|
9728
10083
|
}
|
|
9729
10084
|
|
|
9730
10085
|
} else if (type === 'exec') {
|
|
9731
10086
|
if (!bw._allowExec) {
|
|
9732
|
-
|
|
10087
|
+
_cw('[bw] exec rejected: allowExec is not enabled');
|
|
9733
10088
|
return false;
|
|
9734
10089
|
}
|
|
9735
10090
|
if (!msg.code) return false;
|
|
@@ -9737,7 +10092,7 @@ bw.clientApply = function(msg) {
|
|
|
9737
10092
|
new Function(msg.code)();
|
|
9738
10093
|
return true;
|
|
9739
10094
|
} catch (e) {
|
|
9740
|
-
|
|
10095
|
+
_ce('[bw] exec error:', e);
|
|
9741
10096
|
return false;
|
|
9742
10097
|
}
|
|
9743
10098
|
}
|
|
@@ -9785,7 +10140,7 @@ bw.clientConnect = function(url, opts) {
|
|
|
9785
10140
|
|
|
9786
10141
|
function handleMessage(data) {
|
|
9787
10142
|
try {
|
|
9788
|
-
var msg =
|
|
10143
|
+
var msg = _is(data, 'string') ? bw.clientParse(data) : data;
|
|
9789
10144
|
if (onMessage) onMessage(msg);
|
|
9790
10145
|
if (handlers.message) handlers.message(msg);
|
|
9791
10146
|
bw.clientApply(msg);
|
|
@@ -9823,7 +10178,7 @@ bw.clientConnect = function(url, opts) {
|
|
|
9823
10178
|
setStatus('connected');
|
|
9824
10179
|
conn._pollTimer = setInterval(function() {
|
|
9825
10180
|
fetch(url).then(function(r) { return r.json(); }).then(function(msgs) {
|
|
9826
|
-
if (
|
|
10181
|
+
if (_isA(msgs)) {
|
|
9827
10182
|
msgs.forEach(handleMessage);
|
|
9828
10183
|
} else if (msgs && msgs.type) {
|
|
9829
10184
|
handleMessage(msgs);
|
|
@@ -9905,33 +10260,33 @@ bw.inspect = function(target) {
|
|
|
9905
10260
|
el = target.element;
|
|
9906
10261
|
comp = target;
|
|
9907
10262
|
} else {
|
|
9908
|
-
if (
|
|
10263
|
+
if (_is(target, 'string')) {
|
|
9909
10264
|
el = bw.$(target)[0];
|
|
9910
10265
|
}
|
|
9911
10266
|
if (!el) {
|
|
9912
|
-
|
|
10267
|
+
_cw('bw.inspect: element not found');
|
|
9913
10268
|
return null;
|
|
9914
10269
|
}
|
|
9915
10270
|
comp = el._bwComponentHandle;
|
|
9916
10271
|
}
|
|
9917
10272
|
if (!comp) {
|
|
9918
|
-
|
|
9919
|
-
|
|
9920
|
-
|
|
9921
|
-
|
|
10273
|
+
_cl('bw.inspect: no ComponentHandle on this element');
|
|
10274
|
+
_cl(' Tag:', el.tagName);
|
|
10275
|
+
_cl(' Classes:', el.className);
|
|
10276
|
+
_cl(' _bw_state:', el._bw_state || '(none)');
|
|
9922
10277
|
return null;
|
|
9923
10278
|
}
|
|
9924
10279
|
var deps = comp._bindings.reduce(function(s, b) {
|
|
9925
10280
|
return s.concat(b.deps || []);
|
|
9926
10281
|
}, []).filter(function(v, i, a) { return a.indexOf(v) === i; });
|
|
9927
10282
|
console.group('Component: ' + comp._bwId);
|
|
9928
|
-
|
|
9929
|
-
|
|
9930
|
-
|
|
9931
|
-
|
|
9932
|
-
|
|
9933
|
-
|
|
9934
|
-
|
|
10283
|
+
_cl('State:', comp._state);
|
|
10284
|
+
_cl('Bindings:', comp._bindings.length, '(deps:', deps, ')');
|
|
10285
|
+
_cl('Methods:', _keys(comp._methods));
|
|
10286
|
+
_cl('Actions:', _keys(comp._actions));
|
|
10287
|
+
_cl('User tag:', comp._userTag || '(none)');
|
|
10288
|
+
_cl('Mounted:', comp.mounted);
|
|
10289
|
+
_cl('Element:', comp.element);
|
|
9935
10290
|
console.groupEnd();
|
|
9936
10291
|
return comp;
|
|
9937
10292
|
};
|
|
@@ -9954,8 +10309,8 @@ bw.compile = function(taco) {
|
|
|
9954
10309
|
// Pre-extract all binding expressions
|
|
9955
10310
|
var precompiled = [];
|
|
9956
10311
|
function walkExpressions(node) {
|
|
9957
|
-
if (!node
|
|
9958
|
-
if (
|
|
10312
|
+
if (!_is(node, 'object')) return;
|
|
10313
|
+
if (_is(node.c, 'string') && node.c.indexOf('${') >= 0) {
|
|
9959
10314
|
var parsed = bw._parseBindings(node.c);
|
|
9960
10315
|
for (var i = 0; i < parsed.length; i++) {
|
|
9961
10316
|
try {
|
|
@@ -9970,9 +10325,9 @@ bw.compile = function(taco) {
|
|
|
9970
10325
|
}
|
|
9971
10326
|
if (node.a) {
|
|
9972
10327
|
for (var key in node.a) {
|
|
9973
|
-
if (
|
|
10328
|
+
if (_hop.call(node.a, key)) {
|
|
9974
10329
|
var v = node.a[key];
|
|
9975
|
-
if (
|
|
10330
|
+
if (_is(v, 'string') && v.indexOf('${') >= 0) {
|
|
9976
10331
|
var parsed2 = bw._parseBindings(v);
|
|
9977
10332
|
for (var j = 0; j < parsed2.length; j++) {
|
|
9978
10333
|
try {
|
|
@@ -9988,9 +10343,9 @@ bw.compile = function(taco) {
|
|
|
9988
10343
|
}
|
|
9989
10344
|
}
|
|
9990
10345
|
}
|
|
9991
|
-
if (
|
|
10346
|
+
if (_isA(node.c)) {
|
|
9992
10347
|
for (var k = 0; k < node.c.length; k++) walkExpressions(node.c[k]);
|
|
9993
|
-
} else if (node.c
|
|
10348
|
+
} else if (_is(node.c, 'object') && node.c.t) {
|
|
9994
10349
|
walkExpressions(node.c);
|
|
9995
10350
|
}
|
|
9996
10351
|
}
|
|
@@ -10002,7 +10357,7 @@ bw.compile = function(taco) {
|
|
|
10002
10357
|
handle._precompiledBindings = precompiled;
|
|
10003
10358
|
if (initialState) {
|
|
10004
10359
|
for (var k in initialState) {
|
|
10005
|
-
if (
|
|
10360
|
+
if (_hop.call(initialState, k)) {
|
|
10006
10361
|
handle._state[k] = initialState[k];
|
|
10007
10362
|
}
|
|
10008
10363
|
}
|
|
@@ -10033,18 +10388,18 @@ bw.compile = function(taco) {
|
|
|
10033
10388
|
bw.css = function(rules, options = {}) {
|
|
10034
10389
|
const { minify = false, pretty = !minify } = options;
|
|
10035
10390
|
|
|
10036
|
-
if (
|
|
10391
|
+
if (_is(rules, 'string')) return rules;
|
|
10037
10392
|
|
|
10038
10393
|
let css = '';
|
|
10039
10394
|
const indent = pretty ? ' ' : '';
|
|
10040
10395
|
const newline = pretty ? '\n' : '';
|
|
10041
10396
|
const space = pretty ? ' ' : '';
|
|
10042
10397
|
|
|
10043
|
-
if (
|
|
10398
|
+
if (_isA(rules)) {
|
|
10044
10399
|
css = rules.map(rule => bw.css(rule, options)).join(newline);
|
|
10045
|
-
} else if (
|
|
10400
|
+
} else if (_is(rules, 'object')) {
|
|
10046
10401
|
Object.entries(rules).forEach(([selector, styles]) => {
|
|
10047
|
-
if (
|
|
10402
|
+
if (_is(styles, 'object')) {
|
|
10048
10403
|
// Handle @media, @keyframes, @supports — recurse into nested block
|
|
10049
10404
|
if (selector.charAt(0) === '@') {
|
|
10050
10405
|
const inner = bw.css(styles, options);
|
|
@@ -10093,7 +10448,7 @@ bw.css = function(rules, options = {}) {
|
|
|
10093
10448
|
*/
|
|
10094
10449
|
bw.injectCSS = function(css, options = {}) {
|
|
10095
10450
|
if (!bw._isBrowser) {
|
|
10096
|
-
|
|
10451
|
+
_cw('bw.injectCSS requires a DOM environment');
|
|
10097
10452
|
return null;
|
|
10098
10453
|
}
|
|
10099
10454
|
|
|
@@ -10110,7 +10465,7 @@ bw.injectCSS = function(css, options = {}) {
|
|
|
10110
10465
|
}
|
|
10111
10466
|
|
|
10112
10467
|
// Convert CSS if needed
|
|
10113
|
-
const cssStr =
|
|
10468
|
+
const cssStr = _is(css, 'string') ? css : bw.css(css, options);
|
|
10114
10469
|
|
|
10115
10470
|
// Set or append CSS
|
|
10116
10471
|
if (append && styleEl.textContent) {
|
|
@@ -10140,7 +10495,7 @@ bw.s = function() {
|
|
|
10140
10495
|
var result = {};
|
|
10141
10496
|
for (var i = 0; i < arguments.length; i++) {
|
|
10142
10497
|
var arg = arguments[i];
|
|
10143
|
-
if (arg
|
|
10498
|
+
if (_is(arg, 'object')) Object.assign(result, arg);
|
|
10144
10499
|
}
|
|
10145
10500
|
return result;
|
|
10146
10501
|
};
|
|
@@ -10263,7 +10618,7 @@ bw.u = {
|
|
|
10263
10618
|
bw.responsive = function(selector, breakpoints) {
|
|
10264
10619
|
var sizes = { sm: '576px', md: '768px', lg: '992px', xl: '1200px' };
|
|
10265
10620
|
var parts = [];
|
|
10266
|
-
|
|
10621
|
+
_keys(breakpoints).forEach(function(key) {
|
|
10267
10622
|
var rules = {};
|
|
10268
10623
|
if (key === 'base') {
|
|
10269
10624
|
rules[selector] = breakpoints[key];
|
|
@@ -10335,18 +10690,18 @@ if (bw._isBrowser) {
|
|
|
10335
10690
|
if (!selector) return [];
|
|
10336
10691
|
|
|
10337
10692
|
// Already an array
|
|
10338
|
-
if (
|
|
10693
|
+
if (_isA(selector)) return selector;
|
|
10339
10694
|
|
|
10340
10695
|
// Single element
|
|
10341
10696
|
if (selector.nodeType) return [selector];
|
|
10342
10697
|
|
|
10343
10698
|
// NodeList or HTMLCollection
|
|
10344
|
-
if (selector.length !== undefined &&
|
|
10699
|
+
if (selector.length !== undefined && !_is(selector, 'string')) {
|
|
10345
10700
|
return Array.from(selector);
|
|
10346
10701
|
}
|
|
10347
10702
|
|
|
10348
10703
|
// CSS selector string
|
|
10349
|
-
if (
|
|
10704
|
+
if (_is(selector, 'string')) {
|
|
10350
10705
|
return Array.from(document.querySelectorAll(selector));
|
|
10351
10706
|
}
|
|
10352
10707
|
|
|
@@ -10850,7 +11205,7 @@ bw.makeTable = function(config) {
|
|
|
10850
11205
|
|
|
10851
11206
|
// Auto-detect columns if not provided
|
|
10852
11207
|
const cols = columns || (data.length > 0
|
|
10853
|
-
?
|
|
11208
|
+
? _keys(data[0]).map(key => ({ key, label: key }))
|
|
10854
11209
|
: []);
|
|
10855
11210
|
|
|
10856
11211
|
// Current sort state
|
|
@@ -10865,7 +11220,7 @@ bw.makeTable = function(config) {
|
|
|
10865
11220
|
const bVal = b[currentSortColumn];
|
|
10866
11221
|
|
|
10867
11222
|
// Handle different types
|
|
10868
|
-
if (
|
|
11223
|
+
if (_is(aVal, 'number') && _is(bVal, 'number')) {
|
|
10869
11224
|
return currentSortDirection === 'asc' ? aVal - bVal : bVal - aVal;
|
|
10870
11225
|
}
|
|
10871
11226
|
|
|
@@ -10975,7 +11330,7 @@ bw.makeTable = function(config) {
|
|
|
10975
11330
|
bw.makeTableFromArray = function(config) {
|
|
10976
11331
|
const { data = [], headerRow = true, columns, ...rest } = config;
|
|
10977
11332
|
|
|
10978
|
-
if (!
|
|
11333
|
+
if (!_isA(data) || data.length === 0) {
|
|
10979
11334
|
return bw.makeTable({ data: [], columns: columns || [], ...rest });
|
|
10980
11335
|
}
|
|
10981
11336
|
|
|
@@ -11057,7 +11412,7 @@ bw.makeBarChart = function(config) {
|
|
|
11057
11412
|
className = ''
|
|
11058
11413
|
} = config;
|
|
11059
11414
|
|
|
11060
|
-
if (!
|
|
11415
|
+
if (!_isA(data) || data.length === 0) {
|
|
11061
11416
|
return { t: 'div', a: { class: ('bw_bar_chart_container ' + className).trim() }, c: '' };
|
|
11062
11417
|
}
|
|
11063
11418
|
|
|
@@ -11206,7 +11561,7 @@ bw._componentRegistry = new Map();
|
|
|
11206
11561
|
*/
|
|
11207
11562
|
bw.render = function(element, position, taco) {
|
|
11208
11563
|
// Get target element
|
|
11209
|
-
const targetEl =
|
|
11564
|
+
const targetEl = _is(element, 'string')
|
|
11210
11565
|
? document.querySelector(element)
|
|
11211
11566
|
: element;
|
|
11212
11567
|
|
|
@@ -11356,7 +11711,7 @@ bw.render = function(element, position, taco) {
|
|
|
11356
11711
|
setContent(content) {
|
|
11357
11712
|
this._taco.c = content;
|
|
11358
11713
|
if (this.element) {
|
|
11359
|
-
if (
|
|
11714
|
+
if (_is(content, 'string')) {
|
|
11360
11715
|
this.element.textContent = content;
|
|
11361
11716
|
} else {
|
|
11362
11717
|
// Re-render for complex content
|