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