bitwrench 2.0.15 → 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/README.md +57 -21
- package/dist/bitwrench-bccl.cjs.js +3750 -0
- package/dist/bitwrench-bccl.cjs.min.js +40 -0
- package/dist/bitwrench-bccl.esm.js +3745 -0
- package/dist/bitwrench-bccl.esm.min.js +40 -0
- package/dist/bitwrench-bccl.umd.js +3756 -0
- package/dist/bitwrench-bccl.umd.min.js +40 -0
- package/dist/bitwrench-code-edit.cjs.js +57 -7
- package/dist/bitwrench-code-edit.cjs.min.js +9 -2
- package/dist/bitwrench-code-edit.es5.js +74 -11
- package/dist/bitwrench-code-edit.es5.min.js +9 -2
- package/dist/bitwrench-code-edit.esm.js +57 -7
- package/dist/bitwrench-code-edit.esm.min.js +9 -2
- package/dist/bitwrench-code-edit.umd.js +57 -7
- package/dist/bitwrench-code-edit.umd.min.js +9 -2
- package/dist/bitwrench-lean.cjs.js +905 -157
- package/dist/bitwrench-lean.cjs.min.js +7 -7
- package/dist/bitwrench-lean.es5.js +931 -157
- package/dist/bitwrench-lean.es5.min.js +5 -5
- package/dist/bitwrench-lean.esm.js +904 -157
- package/dist/bitwrench-lean.esm.min.js +7 -7
- package/dist/bitwrench-lean.umd.js +905 -157
- package/dist/bitwrench-lean.umd.min.js +7 -7
- package/dist/bitwrench.cjs.js +910 -158
- package/dist/bitwrench.cjs.min.js +8 -8
- package/dist/bitwrench.css +60 -17
- package/dist/bitwrench.es5.js +939 -158
- package/dist/bitwrench.es5.min.js +6 -6
- package/dist/bitwrench.esm.js +909 -158
- package/dist/bitwrench.esm.min.js +8 -8
- package/dist/bitwrench.min.css +1 -1
- package/dist/bitwrench.umd.js +910 -158
- package/dist/bitwrench.umd.min.js +8 -8
- package/dist/builds.json +168 -80
- package/dist/bwserve.cjs.js +660 -0
- package/dist/bwserve.esm.js +652 -0
- package/dist/sri.json +36 -28
- package/package.json +20 -3
- package/readme.html +62 -23
- package/src/bitwrench-bccl-entry.js +72 -0
- package/src/bitwrench-bccl.js +5 -1
- package/src/bitwrench-code-edit.js +56 -6
- package/src/bitwrench-color-utils.js +5 -6
- package/src/bitwrench-styles.js +20 -8
- package/src/bitwrench.js +876 -140
- package/src/bwserve/client.js +182 -0
- package/src/bwserve/index.js +363 -0
- package/src/bwserve/shell.js +106 -0
- package/src/cli/index.js +36 -15
- package/src/cli/layout-default.js +47 -32
- package/src/cli/serve.js +325 -0
- package/src/version.js +3 -3
- /package/bin/{bitwrench.js → bwcli.js} +0 -0
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
/*! bitwrench-lean v2.0.
|
|
1
|
+
/*! bitwrench-lean v2.0.17 | BSD-2-Clause | https://deftio.github.com/bitwrench/pages */
|
|
2
2
|
(function (global, factory) {
|
|
3
3
|
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
|
4
4
|
typeof define === 'function' && define.amd ? define(factory) :
|
|
5
5
|
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.bw = factory());
|
|
6
6
|
})(this, (function () { 'use strict';
|
|
7
7
|
|
|
8
|
+
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
8
9
|
function _arrayLikeToArray(r, a) {
|
|
9
10
|
(null == a || a > r.length) && (a = r.length);
|
|
10
11
|
for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
|
|
@@ -189,14 +190,14 @@
|
|
|
189
190
|
*/
|
|
190
191
|
|
|
191
192
|
var VERSION_INFO = {
|
|
192
|
-
version: '2.0.
|
|
193
|
+
version: '2.0.17',
|
|
193
194
|
name: 'bitwrench',
|
|
194
195
|
description: 'A library for javascript UI functions.',
|
|
195
196
|
license: 'BSD-2-Clause',
|
|
196
197
|
homepage: 'https://deftio.github.com/bitwrench/pages',
|
|
197
198
|
repository: 'git+https://github.com/deftio/bitwrench.git',
|
|
198
199
|
author: 'manu a. chatterjee <deftio@deftio.com> (https://deftio.com/)',
|
|
199
|
-
buildDate: '2026-03-
|
|
200
|
+
buildDate: '2026-03-13T23:15:10.823Z'
|
|
200
201
|
};
|
|
201
202
|
|
|
202
203
|
/**
|
|
@@ -609,12 +610,11 @@
|
|
|
609
610
|
var lightBase = config.light || hslToHex([h, 8, 97]);
|
|
610
611
|
var darkBase = config.dark || hslToHex([h, 10, 13]);
|
|
611
612
|
|
|
612
|
-
// Background & surface tokens —
|
|
613
|
-
//
|
|
614
|
-
//
|
|
615
|
-
|
|
616
|
-
var
|
|
617
|
-
var surfBase = config.surface || '#f8f9fa';
|
|
613
|
+
// Background & surface tokens — tinted with primary hue for theme personality.
|
|
614
|
+
// Very subtle: bg at L=98/S=6, surface at L=96/S=8.
|
|
615
|
+
// User can override with config.background / config.surface.
|
|
616
|
+
var bgBase = config.background || hslToHex([h, 6, 98]);
|
|
617
|
+
var surfBase = config.surface || hslToHex([h, 8, 96]);
|
|
618
618
|
var palette = {
|
|
619
619
|
primary: deriveShades(config.primary),
|
|
620
620
|
secondary: deriveShades(config.secondary),
|
|
@@ -1857,8 +1857,8 @@
|
|
|
1857
1857
|
},
|
|
1858
1858
|
'.bw_container_fluid': {
|
|
1859
1859
|
'width': '100%',
|
|
1860
|
-
'padding-right': '
|
|
1861
|
-
'padding-left': '
|
|
1860
|
+
'padding-right': '0.75rem',
|
|
1861
|
+
'padding-left': '0.75rem',
|
|
1862
1862
|
'margin-right': 'auto',
|
|
1863
1863
|
'margin-left': 'auto'
|
|
1864
1864
|
},
|
|
@@ -2234,7 +2234,9 @@
|
|
|
2234
2234
|
'line-height': '1.3',
|
|
2235
2235
|
'text-align': 'center',
|
|
2236
2236
|
'white-space': 'nowrap',
|
|
2237
|
-
'vertical-align': 'baseline'
|
|
2237
|
+
'vertical-align': 'baseline',
|
|
2238
|
+
'padding': '0.35rem 0.65rem',
|
|
2239
|
+
'border-radius': '0.25rem'
|
|
2238
2240
|
},
|
|
2239
2241
|
'.bw_badge:empty': {
|
|
2240
2242
|
'display': 'none'
|
|
@@ -2699,7 +2701,8 @@
|
|
|
2699
2701
|
'.bw_code_pre': {
|
|
2700
2702
|
'margin': '0',
|
|
2701
2703
|
'border': 'none',
|
|
2702
|
-
'overflow-x': 'auto'
|
|
2704
|
+
'overflow-x': 'auto',
|
|
2705
|
+
'max-width': '100%'
|
|
2703
2706
|
},
|
|
2704
2707
|
'.bw_code_block': {
|
|
2705
2708
|
'display': 'block',
|
|
@@ -2928,7 +2931,7 @@
|
|
|
2928
2931
|
},
|
|
2929
2932
|
'.bw_modal_dialog': {
|
|
2930
2933
|
'position': 'relative',
|
|
2931
|
-
'width': '100%',
|
|
2934
|
+
'width': 'calc(100% - 1rem)',
|
|
2932
2935
|
'max-width': '500px',
|
|
2933
2936
|
'margin': '1.75rem auto',
|
|
2934
2937
|
'pointer-events': 'none'
|
|
@@ -3017,7 +3020,7 @@
|
|
|
3017
3020
|
'.bw_toast': {
|
|
3018
3021
|
'pointer-events': 'auto',
|
|
3019
3022
|
'width': '350px',
|
|
3020
|
-
'max-width': '
|
|
3023
|
+
'max-width': 'calc(100vw - 2rem)',
|
|
3021
3024
|
'background-clip': 'padding-box',
|
|
3022
3025
|
'opacity': '0'
|
|
3023
3026
|
},
|
|
@@ -3224,6 +3227,7 @@
|
|
|
3224
3227
|
'z-index': '999',
|
|
3225
3228
|
'font-size': '0.875rem',
|
|
3226
3229
|
'white-space': 'nowrap',
|
|
3230
|
+
'max-width': 'min(300px, calc(100vw - 1rem))',
|
|
3227
3231
|
'pointer-events': 'none',
|
|
3228
3232
|
'opacity': '0',
|
|
3229
3233
|
'visibility': 'hidden'
|
|
@@ -3282,7 +3286,7 @@
|
|
|
3282
3286
|
'position': 'absolute',
|
|
3283
3287
|
'z-index': '1000',
|
|
3284
3288
|
'min-width': '200px',
|
|
3285
|
-
'max-width': '320px',
|
|
3289
|
+
'max-width': 'min(320px, calc(100vw - 2rem))',
|
|
3286
3290
|
'pointer-events': 'none',
|
|
3287
3291
|
'opacity': '0',
|
|
3288
3292
|
'visibility': 'hidden'
|
|
@@ -3828,6 +3832,45 @@
|
|
|
3828
3832
|
},
|
|
3829
3833
|
'.bw_feature_grid, .bw_feature-grid': {
|
|
3830
3834
|
'grid-template-columns': '1fr'
|
|
3835
|
+
},
|
|
3836
|
+
'.bw_modal_dialog': {
|
|
3837
|
+
'margin': '0.5rem auto'
|
|
3838
|
+
},
|
|
3839
|
+
'.bw_modal_lg': {
|
|
3840
|
+
'max-width': 'calc(100% - 1rem)'
|
|
3841
|
+
},
|
|
3842
|
+
'.bw_modal_xl': {
|
|
3843
|
+
'max-width': 'calc(100% - 1rem)'
|
|
3844
|
+
},
|
|
3845
|
+
'.bw_navbar': {
|
|
3846
|
+
'padding': '0.5rem 0.75rem'
|
|
3847
|
+
},
|
|
3848
|
+
'.bw_navbar_brand': {
|
|
3849
|
+
'margin-right': '0.5rem',
|
|
3850
|
+
'font-size': '1rem'
|
|
3851
|
+
},
|
|
3852
|
+
'.bw_navbar_nav': {
|
|
3853
|
+
'flex-wrap': 'wrap'
|
|
3854
|
+
},
|
|
3855
|
+
'.bw_tooltip': {
|
|
3856
|
+
'white-space': 'normal'
|
|
3857
|
+
},
|
|
3858
|
+
'.bw_table': {
|
|
3859
|
+
'display': 'block',
|
|
3860
|
+
'overflow-x': 'auto',
|
|
3861
|
+
'-webkit-overflow-scrolling': 'touch'
|
|
3862
|
+
},
|
|
3863
|
+
'.bw_col, .bw_col_1, .bw_col_2, .bw_col_3, .bw_col_4, .bw_col_5, .bw_col_6, .bw_col_7, .bw_col_8, .bw_col_9, .bw_col_10, .bw_col_11, .bw_col_12': {
|
|
3864
|
+
'flex': '0 0 100%',
|
|
3865
|
+
'max-width': '100%'
|
|
3866
|
+
},
|
|
3867
|
+
'.bw_container': {
|
|
3868
|
+
'padding-right': '0.5rem',
|
|
3869
|
+
'padding-left': '0.5rem'
|
|
3870
|
+
},
|
|
3871
|
+
'.bw_container_fluid': {
|
|
3872
|
+
'padding-right': '0.5rem',
|
|
3873
|
+
'padding-left': '0.5rem'
|
|
3831
3874
|
}
|
|
3832
3875
|
}
|
|
3833
3876
|
}
|
|
@@ -5083,7 +5126,7 @@
|
|
|
5083
5126
|
__monkey_patch_is_nodejs__: {
|
|
5084
5127
|
_value: 'ignore',
|
|
5085
5128
|
set: function set(x) {
|
|
5086
|
-
this._value =
|
|
5129
|
+
this._value = _is(x, 'boolean') ? x : 'ignore';
|
|
5087
5130
|
},
|
|
5088
5131
|
get: function get() {
|
|
5089
5132
|
return this._value;
|
|
@@ -5131,6 +5174,76 @@
|
|
|
5131
5174
|
configurable: true
|
|
5132
5175
|
});
|
|
5133
5176
|
|
|
5177
|
+
// ── Internal aliases ─────────────────────────────────────────────────────
|
|
5178
|
+
// Short names for frequently-used builtins and internal methods.
|
|
5179
|
+
// Same pattern as v1 (_to = bw.typeOf, etc.).
|
|
5180
|
+
//
|
|
5181
|
+
// Why: Terser can't shorten global property chains (console.warn,
|
|
5182
|
+
// Object.prototype.hasOwnProperty, Array.isArray, document.createElement)
|
|
5183
|
+
// because it can't prove they're side-effect-free. We can, so we alias
|
|
5184
|
+
// them here. Each alias saves bytes in the minified output, and the short
|
|
5185
|
+
// names also reduce visual noise in the hot paths (binding pipeline,
|
|
5186
|
+
// createDOM, etc.).
|
|
5187
|
+
//
|
|
5188
|
+
// Alias Target Sites
|
|
5189
|
+
// ───────── ────────────────────────────────────── ─────
|
|
5190
|
+
// _hop Object.prototype.hasOwnProperty 15
|
|
5191
|
+
// _isA Array.isArray 25
|
|
5192
|
+
// _keys Object.keys 7
|
|
5193
|
+
// _to bw.typeOf (type string) 26
|
|
5194
|
+
// _is type check boolean: _is(x,'string') ~50
|
|
5195
|
+
// _cw console.warn 8
|
|
5196
|
+
// _cl console.log 11
|
|
5197
|
+
// _ce console.error 4
|
|
5198
|
+
// _chp ComponentHandle.prototype 28 (defined after constructor)
|
|
5199
|
+
//
|
|
5200
|
+
// Note: document.createElement etc. are NOT aliased because they require
|
|
5201
|
+
// `this === document` and .bind() would add overhead on every call.
|
|
5202
|
+
// Console aliases use thin wrappers (not direct refs) so test monkey-
|
|
5203
|
+
// patching of console.warn/log/error continues to work.
|
|
5204
|
+
//
|
|
5205
|
+
// `typeof x` for UNDECLARED globals (window, document, process, require,
|
|
5206
|
+
// EventSource, navigator, Promise, __filename, import.meta) MUST stay as
|
|
5207
|
+
// raw `typeof` — calling _to(x) when x doesn't exist throws ReferenceError.
|
|
5208
|
+
//
|
|
5209
|
+
// ── v1 functional type helpers (kept for reference, not currently used) ──
|
|
5210
|
+
// _toa(x, type, trueVal, falseVal) — bw.typeAssign:
|
|
5211
|
+
// returns trueVal if _to(x)===type, else falseVal.
|
|
5212
|
+
// Replaces: (typeof x === 'string') ? A : B → _toa(x,'string',A,B)
|
|
5213
|
+
// _toc(x, type, trueVal, falseVal) — bw.typeConvert:
|
|
5214
|
+
// same as _toa but if trueVal/falseVal are functions, calls them with x.
|
|
5215
|
+
// Replaces: typeof x === 'string' ? fn(x) : default → _toc(x,'string',fn,default)
|
|
5216
|
+
// Uncomment if pattern frequency justifies them:
|
|
5217
|
+
// var _toa = function(x, t, y, n) { return _to(x) === t ? y : n; };
|
|
5218
|
+
// 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); };
|
|
5219
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
5220
|
+
var _hop = Object.prototype.hasOwnProperty;
|
|
5221
|
+
var _isA = Array.isArray;
|
|
5222
|
+
var _keys = Object.keys;
|
|
5223
|
+
var _to = typeOf; // imported from bitwrench-utils.js
|
|
5224
|
+
var _is = function _is(x, t) {
|
|
5225
|
+
var r = _to(x);
|
|
5226
|
+
return r === t || r.toLowerCase() === t;
|
|
5227
|
+
};
|
|
5228
|
+
// Console aliases use thin wrappers (not direct references) so that test
|
|
5229
|
+
// code can monkey-patch console.warn/log/error and the patches take effect.
|
|
5230
|
+
var _cw = function _cw() {
|
|
5231
|
+
console.warn.apply(console, arguments);
|
|
5232
|
+
};
|
|
5233
|
+
var _cl = function _cl() {
|
|
5234
|
+
console.log.apply(console, arguments);
|
|
5235
|
+
};
|
|
5236
|
+
var _ce = function _ce() {
|
|
5237
|
+
console.error.apply(console, arguments);
|
|
5238
|
+
};
|
|
5239
|
+
|
|
5240
|
+
/**
|
|
5241
|
+
* Debug flag. When true, emits console.warn for silent binding failures
|
|
5242
|
+
* (missing paths, null refs, auto-created intermediate objects).
|
|
5243
|
+
* @type {boolean}
|
|
5244
|
+
*/
|
|
5245
|
+
bw.debug = false;
|
|
5246
|
+
|
|
5134
5247
|
/**
|
|
5135
5248
|
* Lazy-resolve Node.js `fs` module.
|
|
5136
5249
|
* Tries require('fs') first (available in CJS/UMD Node.js builds),
|
|
@@ -5280,7 +5393,7 @@
|
|
|
5280
5393
|
*/
|
|
5281
5394
|
bw._el = function (id) {
|
|
5282
5395
|
// Pass-through for DOM elements
|
|
5283
|
-
if (
|
|
5396
|
+
if (!_is(id, 'string')) return id || null;
|
|
5284
5397
|
if (!id) return null;
|
|
5285
5398
|
if (!bw._isBrowser) return null;
|
|
5286
5399
|
|
|
@@ -5375,7 +5488,7 @@
|
|
|
5375
5488
|
* // => '<b>Hello</b> & "world"'
|
|
5376
5489
|
*/
|
|
5377
5490
|
bw.escapeHTML = function (str) {
|
|
5378
|
-
if (
|
|
5491
|
+
if (!_is(str, 'string')) return '';
|
|
5379
5492
|
var escapeMap = {
|
|
5380
5493
|
'&': '&',
|
|
5381
5494
|
'<': '<',
|
|
@@ -5452,7 +5565,7 @@
|
|
|
5452
5565
|
}
|
|
5453
5566
|
|
|
5454
5567
|
// Handle arrays of TACOs
|
|
5455
|
-
if (
|
|
5568
|
+
if (_isA(taco)) {
|
|
5456
5569
|
return taco.map(function (t) {
|
|
5457
5570
|
return bw.html(t, options);
|
|
5458
5571
|
}).join('');
|
|
@@ -5475,17 +5588,17 @@
|
|
|
5475
5588
|
if (taco && taco._bwEach && options.state) {
|
|
5476
5589
|
var eachExpr = taco.expr.replace(/^\$\{|\}$/g, '');
|
|
5477
5590
|
var arr = bw._evaluatePath(options.state, eachExpr);
|
|
5478
|
-
if (!
|
|
5591
|
+
if (!_isA(arr)) return '';
|
|
5479
5592
|
return arr.map(function (item, idx) {
|
|
5480
5593
|
return bw.html(taco.factory(item, idx), options);
|
|
5481
5594
|
}).join('');
|
|
5482
5595
|
}
|
|
5483
5596
|
|
|
5484
5597
|
// Handle primitives and non-TACO objects
|
|
5485
|
-
if (
|
|
5598
|
+
if (!_is(taco, 'object') || !taco.t) {
|
|
5486
5599
|
var str = options.raw ? String(taco) : bw.escapeHTML(String(taco));
|
|
5487
5600
|
// Resolve template bindings if state provided
|
|
5488
|
-
if (options.state &&
|
|
5601
|
+
if (options.state && _is(str, 'string') && str.indexOf('${') >= 0) {
|
|
5489
5602
|
str = bw._resolveTemplate(str, options.state, !!options.compile);
|
|
5490
5603
|
}
|
|
5491
5604
|
return str;
|
|
@@ -5510,9 +5623,17 @@
|
|
|
5510
5623
|
// Skip null, undefined, false
|
|
5511
5624
|
if (value == null || value === false) continue;
|
|
5512
5625
|
|
|
5513
|
-
//
|
|
5514
|
-
if (key.startsWith('on'))
|
|
5515
|
-
|
|
5626
|
+
// Serialize event handlers via funcRegister
|
|
5627
|
+
if (key.startsWith('on')) {
|
|
5628
|
+
if (_is(value, 'function')) {
|
|
5629
|
+
var fnId = bw.funcRegister(value);
|
|
5630
|
+
attrStr += ' ' + key + '="' + bw.funcGetDispatchStr(fnId, 'event') + '"';
|
|
5631
|
+
} else if (_is(value, 'string')) {
|
|
5632
|
+
attrStr += ' ' + key + '="' + bw.escapeHTML(value) + '"';
|
|
5633
|
+
}
|
|
5634
|
+
continue;
|
|
5635
|
+
}
|
|
5636
|
+
if (key === 'style' && _is(value, 'object')) {
|
|
5516
5637
|
// Convert style object to string
|
|
5517
5638
|
var styleStr = Object.entries(value).filter(function (_ref) {
|
|
5518
5639
|
var _ref2 = _slicedToArray(_ref, 2),
|
|
@@ -5529,7 +5650,7 @@
|
|
|
5529
5650
|
}
|
|
5530
5651
|
} else if (key === 'class') {
|
|
5531
5652
|
// Handle class as array or string
|
|
5532
|
-
var classStr =
|
|
5653
|
+
var classStr = _isA(value) ? value.filter(Boolean).join(' ') : String(value);
|
|
5533
5654
|
if (classStr) {
|
|
5534
5655
|
attrStr += " class=\"".concat(bw.escapeHTML(classStr), "\"");
|
|
5535
5656
|
}
|
|
@@ -5565,12 +5686,184 @@
|
|
|
5565
5686
|
// Process content recursively
|
|
5566
5687
|
var contentStr = content != null ? bw.html(content, options) : '';
|
|
5567
5688
|
// Resolve template bindings in content if state provided
|
|
5568
|
-
if (options.state &&
|
|
5689
|
+
if (options.state && _is(contentStr, 'string') && contentStr.indexOf('${') >= 0) {
|
|
5569
5690
|
contentStr = bw._resolveTemplate(contentStr, options.state, !!options.compile);
|
|
5570
5691
|
}
|
|
5571
5692
|
return "<".concat(tag).concat(attrStr, ">").concat(contentStr, "</").concat(tag, ">");
|
|
5572
5693
|
};
|
|
5573
5694
|
|
|
5695
|
+
/**
|
|
5696
|
+
* Generate a complete, self-contained HTML document from TACO content.
|
|
5697
|
+
*
|
|
5698
|
+
* Produces a full `<!DOCTYPE html>` page with configurable runtime injection,
|
|
5699
|
+
* func registry emission (so serialized event handlers work), optional theme,
|
|
5700
|
+
* and extra head elements. Designed for static site generation, offline/airgapped
|
|
5701
|
+
* use, and the "static site that isn't static" workflow.
|
|
5702
|
+
*
|
|
5703
|
+
* @param {Object} [opts={}] - Page options
|
|
5704
|
+
* @param {Object|string|Array} [opts.body=''] - Body content: TACO, string, or array
|
|
5705
|
+
* @param {string} [opts.title='bitwrench'] - Page title
|
|
5706
|
+
* @param {Object} [opts.state] - State for ${expr} resolution in bw.html()
|
|
5707
|
+
* @param {string} [opts.runtime='shim'] - Runtime level: 'inline'|'cdn'|'shim'|'none'
|
|
5708
|
+
* @param {string} [opts.css=''] - Additional CSS for <style> block
|
|
5709
|
+
* @param {string|Object} [opts.theme=null] - Theme preset name or config object
|
|
5710
|
+
* @param {Array} [opts.head=[]] - Extra TACO elements rendered into <head>
|
|
5711
|
+
* @param {string} [opts.favicon=''] - Favicon URL
|
|
5712
|
+
* @param {string} [opts.lang='en'] - HTML lang attribute
|
|
5713
|
+
* @returns {string} Complete HTML document string
|
|
5714
|
+
* @category DOM Generation
|
|
5715
|
+
* @see bw.html
|
|
5716
|
+
* @example
|
|
5717
|
+
* bw.htmlPage({
|
|
5718
|
+
* title: 'My App',
|
|
5719
|
+
* body: { t: 'h1', c: 'Hello World' },
|
|
5720
|
+
* runtime: 'shim'
|
|
5721
|
+
* })
|
|
5722
|
+
*/
|
|
5723
|
+
bw.htmlPage = function (opts) {
|
|
5724
|
+
opts = opts || {};
|
|
5725
|
+
var title = opts.title || 'bitwrench';
|
|
5726
|
+
var body = opts.body || '';
|
|
5727
|
+
var state = opts.state || undefined;
|
|
5728
|
+
var runtime = opts.runtime || 'shim';
|
|
5729
|
+
var css = opts.css || '';
|
|
5730
|
+
var theme = opts.theme || null;
|
|
5731
|
+
var headExtra = opts.head || [];
|
|
5732
|
+
var favicon = opts.favicon || '';
|
|
5733
|
+
var lang = opts.lang || 'en';
|
|
5734
|
+
|
|
5735
|
+
// Snapshot funcRegistry counter before rendering
|
|
5736
|
+
var fnCounterBefore = bw._fnIDCounter;
|
|
5737
|
+
|
|
5738
|
+
// Render body content
|
|
5739
|
+
var bodyHTML = '';
|
|
5740
|
+
if (_is(body, 'string')) {
|
|
5741
|
+
bodyHTML = body;
|
|
5742
|
+
} else {
|
|
5743
|
+
var htmlOpts = {};
|
|
5744
|
+
if (state) htmlOpts.state = state;
|
|
5745
|
+
bodyHTML = bw.html(body, htmlOpts);
|
|
5746
|
+
}
|
|
5747
|
+
|
|
5748
|
+
// Collect functions registered during this render
|
|
5749
|
+
var fnCounterAfter = bw._fnIDCounter;
|
|
5750
|
+
var registryEntries = '';
|
|
5751
|
+
for (var i = fnCounterBefore; i < fnCounterAfter; i++) {
|
|
5752
|
+
var fnKey = 'bw_fn_' + i;
|
|
5753
|
+
if (bw._fnRegistry[fnKey]) {
|
|
5754
|
+
registryEntries += 'bw._fnRegistry[\'' + fnKey + '\']=' + bw._fnRegistry[fnKey].toString() + ';\n';
|
|
5755
|
+
}
|
|
5756
|
+
}
|
|
5757
|
+
|
|
5758
|
+
// Build runtime script for <head>
|
|
5759
|
+
var runtimeHead = '';
|
|
5760
|
+
if (runtime === 'inline') {
|
|
5761
|
+
// Read UMD bundle synchronously if in Node.js
|
|
5762
|
+
var umdSource = null;
|
|
5763
|
+
if (bw._isNode) {
|
|
5764
|
+
try {
|
|
5765
|
+
var fs = typeof require === 'function' ? require('fs') : null;
|
|
5766
|
+
var pathMod = typeof require === 'function' ? require('path') : null;
|
|
5767
|
+
if (fs && pathMod) {
|
|
5768
|
+
// Resolve dist/ relative to this source file
|
|
5769
|
+
var srcDir = '';
|
|
5770
|
+
try {
|
|
5771
|
+
srcDir = pathMod.dirname(typeof __filename !== 'undefined' ? __filename : '');
|
|
5772
|
+
} catch (e2) {/* ESM: __filename not available */}
|
|
5773
|
+
if (!srcDir && typeof ({ url: (typeof document === 'undefined' && typeof location === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : typeof document === 'undefined' ? location.href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('bitwrench-lean.es5.js', document.baseURI).href)) }) !== 'undefined' && (typeof document === 'undefined' && typeof location === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : typeof document === 'undefined' ? location.href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('bitwrench-lean.es5.js', document.baseURI).href))) {
|
|
5774
|
+
var url = typeof require === 'function' ? require('url') : null;
|
|
5775
|
+
if (url && url.fileURLToPath) srcDir = pathMod.dirname(url.fileURLToPath((typeof document === 'undefined' && typeof location === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : typeof document === 'undefined' ? location.href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('bitwrench-lean.es5.js', document.baseURI).href))));
|
|
5776
|
+
}
|
|
5777
|
+
if (srcDir) {
|
|
5778
|
+
var distPath = pathMod.resolve(srcDir, '../dist/bitwrench.umd.min.js');
|
|
5779
|
+
umdSource = fs.readFileSync(distPath, 'utf8');
|
|
5780
|
+
}
|
|
5781
|
+
}
|
|
5782
|
+
} catch (e) {/* fall through */}
|
|
5783
|
+
}
|
|
5784
|
+
if (umdSource) {
|
|
5785
|
+
runtimeHead = '<script>' + umdSource + '</script>';
|
|
5786
|
+
} else {
|
|
5787
|
+
// Fallback to shim in browser or if dist not available
|
|
5788
|
+
runtimeHead = '<script>' + bw._FUNC_REGISTRY_SHIM + '</script>';
|
|
5789
|
+
}
|
|
5790
|
+
} else if (runtime === 'cdn') {
|
|
5791
|
+
runtimeHead = '<script src="https://cdn.jsdelivr.net/npm/bitwrench@2/dist/bitwrench.umd.min.js"></script>';
|
|
5792
|
+
} else if (runtime === 'shim') {
|
|
5793
|
+
runtimeHead = '<script>' + bw._FUNC_REGISTRY_SHIM + '</script>';
|
|
5794
|
+
}
|
|
5795
|
+
// runtime === 'none' → empty
|
|
5796
|
+
|
|
5797
|
+
// Theme CSS
|
|
5798
|
+
var themeCSS = '';
|
|
5799
|
+
if (theme) {
|
|
5800
|
+
var themeConfig = _is(theme, 'string') ? THEME_PRESETS[theme.toLowerCase()] || null : theme;
|
|
5801
|
+
if (themeConfig) {
|
|
5802
|
+
var themeResult = bw.generateTheme('', Object.assign({}, themeConfig, {
|
|
5803
|
+
inject: false
|
|
5804
|
+
}));
|
|
5805
|
+
themeCSS = themeResult.css;
|
|
5806
|
+
}
|
|
5807
|
+
}
|
|
5808
|
+
|
|
5809
|
+
// Extra <head> elements
|
|
5810
|
+
var headHTML = '';
|
|
5811
|
+
if (_isA(headExtra) && headExtra.length > 0) {
|
|
5812
|
+
headHTML = headExtra.map(function (el) {
|
|
5813
|
+
return bw.html(el);
|
|
5814
|
+
}).join('\n');
|
|
5815
|
+
}
|
|
5816
|
+
|
|
5817
|
+
// Favicon
|
|
5818
|
+
var faviconTag = '';
|
|
5819
|
+
if (favicon) {
|
|
5820
|
+
var safeFavicon = favicon.replace(/[&<>"']/g, function (c) {
|
|
5821
|
+
return {
|
|
5822
|
+
'&': '&',
|
|
5823
|
+
'<': '<',
|
|
5824
|
+
'>': '>',
|
|
5825
|
+
'"': '"',
|
|
5826
|
+
"'": '''
|
|
5827
|
+
}[c];
|
|
5828
|
+
});
|
|
5829
|
+
faviconTag = '<link rel="icon" href="' + safeFavicon + '">';
|
|
5830
|
+
}
|
|
5831
|
+
|
|
5832
|
+
// Escaped title
|
|
5833
|
+
var safeTitle = bw.escapeHTML(title);
|
|
5834
|
+
|
|
5835
|
+
// Combine all CSS
|
|
5836
|
+
var allCSS = (themeCSS ? themeCSS + '\n' : '') + css;
|
|
5837
|
+
|
|
5838
|
+
// Body-end script: registry entries + optional loadDefaultStyles
|
|
5839
|
+
var bodyEndScript = '';
|
|
5840
|
+
var bodyEndParts = [];
|
|
5841
|
+
if (registryEntries) {
|
|
5842
|
+
bodyEndParts.push(registryEntries);
|
|
5843
|
+
}
|
|
5844
|
+
if (runtime === 'inline' || runtime === 'cdn') {
|
|
5845
|
+
bodyEndParts.push('if(typeof bw!=="undefined"){bw.loadDefaultStyles();}');
|
|
5846
|
+
}
|
|
5847
|
+
if (bodyEndParts.length > 0) {
|
|
5848
|
+
bodyEndScript = '<script>\n' + bodyEndParts.join('\n') + '\n</script>';
|
|
5849
|
+
}
|
|
5850
|
+
|
|
5851
|
+
// Assemble document
|
|
5852
|
+
var parts = ['<!DOCTYPE html>', '<html lang="' + lang + '">', '<head>', '<meta charset="UTF-8">', '<meta name="viewport" content="width=device-width, initial-scale=1">'];
|
|
5853
|
+
parts.push('<title>' + safeTitle + '</title>');
|
|
5854
|
+
if (faviconTag) parts.push(faviconTag);
|
|
5855
|
+
if (runtimeHead) parts.push(runtimeHead);
|
|
5856
|
+
if (headHTML) parts.push(headHTML);
|
|
5857
|
+
if (allCSS) parts.push('<style>' + allCSS + '</style>');
|
|
5858
|
+
parts.push('</head>');
|
|
5859
|
+
parts.push('<body>');
|
|
5860
|
+
parts.push(bodyHTML);
|
|
5861
|
+
if (bodyEndScript) parts.push(bodyEndScript);
|
|
5862
|
+
parts.push('</body>');
|
|
5863
|
+
parts.push('</html>');
|
|
5864
|
+
return parts.join('\n');
|
|
5865
|
+
};
|
|
5866
|
+
|
|
5574
5867
|
/**
|
|
5575
5868
|
* Create a live DOM element from a TACO object (browser only).
|
|
5576
5869
|
*
|
|
@@ -5616,7 +5909,7 @@
|
|
|
5616
5909
|
}
|
|
5617
5910
|
|
|
5618
5911
|
// Handle text nodes
|
|
5619
|
-
if (
|
|
5912
|
+
if (!_is(taco, 'object') || !taco.t) {
|
|
5620
5913
|
return document.createTextNode(String(taco));
|
|
5621
5914
|
}
|
|
5622
5915
|
var tag = taco.t,
|
|
@@ -5635,16 +5928,16 @@
|
|
|
5635
5928
|
key = _Object$entries2$_i[0],
|
|
5636
5929
|
value = _Object$entries2$_i[1];
|
|
5637
5930
|
if (value == null || value === false) continue;
|
|
5638
|
-
if (key === 'style' &&
|
|
5931
|
+
if (key === 'style' && _is(value, 'object')) {
|
|
5639
5932
|
// Apply styles directly
|
|
5640
5933
|
Object.assign(el.style, value);
|
|
5641
5934
|
} else if (key === 'class') {
|
|
5642
5935
|
// Handle class as array or string
|
|
5643
|
-
var classStr =
|
|
5936
|
+
var classStr = _isA(value) ? value.filter(Boolean).join(' ') : String(value);
|
|
5644
5937
|
if (classStr) {
|
|
5645
5938
|
el.className = classStr;
|
|
5646
5939
|
}
|
|
5647
|
-
} else if (key.startsWith('on') &&
|
|
5940
|
+
} else if (key.startsWith('on') && _is(value, 'function')) {
|
|
5648
5941
|
// Event handlers
|
|
5649
5942
|
var eventName = key.slice(2).toLowerCase();
|
|
5650
5943
|
el.addEventListener(eventName, value);
|
|
@@ -5664,7 +5957,7 @@
|
|
|
5664
5957
|
// Children with data-bw_id or id attributes get local refs on the parent,
|
|
5665
5958
|
// so o.render functions can access them without any DOM lookup.
|
|
5666
5959
|
if (content != null) {
|
|
5667
|
-
if (
|
|
5960
|
+
if (_isA(content)) {
|
|
5668
5961
|
content.forEach(function (child) {
|
|
5669
5962
|
if (child != null) {
|
|
5670
5963
|
// Handle ComponentHandle in content arrays (Level 2 children)
|
|
@@ -5684,20 +5977,20 @@
|
|
|
5684
5977
|
if (childEl._bw_refs) {
|
|
5685
5978
|
if (!el._bw_refs) el._bw_refs = {};
|
|
5686
5979
|
for (var rk in childEl._bw_refs) {
|
|
5687
|
-
if (
|
|
5980
|
+
if (_hop.call(childEl._bw_refs, rk)) {
|
|
5688
5981
|
el._bw_refs[rk] = childEl._bw_refs[rk];
|
|
5689
5982
|
}
|
|
5690
5983
|
}
|
|
5691
5984
|
}
|
|
5692
5985
|
}
|
|
5693
5986
|
});
|
|
5694
|
-
} else if (
|
|
5987
|
+
} else if (_is(content, 'object') && content.__bw_raw) {
|
|
5695
5988
|
// Raw HTML content — inject via innerHTML
|
|
5696
5989
|
el.innerHTML = content.v;
|
|
5697
5990
|
} else if (content._bwComponent === true) {
|
|
5698
5991
|
// Single ComponentHandle as content
|
|
5699
5992
|
content.mount(el);
|
|
5700
|
-
} else if (
|
|
5993
|
+
} else if (_is(content, 'object') && content.t) {
|
|
5701
5994
|
var childEl = bw.createDOM(content, options);
|
|
5702
5995
|
el.appendChild(childEl);
|
|
5703
5996
|
var childBwId = content.a ? content.a['data-bw_id'] || content.a.id : null;
|
|
@@ -5708,7 +6001,7 @@
|
|
|
5708
6001
|
if (childEl._bw_refs) {
|
|
5709
6002
|
if (!el._bw_refs) el._bw_refs = {};
|
|
5710
6003
|
for (var rk in childEl._bw_refs) {
|
|
5711
|
-
if (
|
|
6004
|
+
if (_hop.call(childEl._bw_refs, rk)) {
|
|
5712
6005
|
el._bw_refs[rk] = childEl._bw_refs[rk];
|
|
5713
6006
|
}
|
|
5714
6007
|
}
|
|
@@ -5740,7 +6033,7 @@
|
|
|
5740
6033
|
if (opts.render) {
|
|
5741
6034
|
el._bw_render = opts.render;
|
|
5742
6035
|
if (opts.mounted) {
|
|
5743
|
-
|
|
6036
|
+
_cw('bw.createDOM: o.render and o.mounted are mutually exclusive. o.render wins.');
|
|
5744
6037
|
}
|
|
5745
6038
|
|
|
5746
6039
|
// Queue initial render (same timing as mounted)
|
|
@@ -5812,7 +6105,7 @@
|
|
|
5812
6105
|
// Get target element (use cache-backed lookup)
|
|
5813
6106
|
var targetEl = bw._el(target);
|
|
5814
6107
|
if (!targetEl) {
|
|
5815
|
-
|
|
6108
|
+
_ce('bw.DOM: Target element not found:', target);
|
|
5816
6109
|
return null;
|
|
5817
6110
|
}
|
|
5818
6111
|
|
|
@@ -5850,7 +6143,7 @@
|
|
|
5850
6143
|
targetEl.appendChild(taco.element);
|
|
5851
6144
|
}
|
|
5852
6145
|
// Handle arrays
|
|
5853
|
-
else if (
|
|
6146
|
+
else if (_isA(taco)) {
|
|
5854
6147
|
taco.forEach(function (t) {
|
|
5855
6148
|
if (t != null) {
|
|
5856
6149
|
if (t._bwComponent === true) {
|
|
@@ -5885,7 +6178,7 @@
|
|
|
5885
6178
|
bw.compileProps = function (handle) {
|
|
5886
6179
|
var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
5887
6180
|
var compiledProps = {};
|
|
5888
|
-
|
|
6181
|
+
_keys(props).forEach(function (key) {
|
|
5889
6182
|
// Create getter/setter for each prop
|
|
5890
6183
|
Object.defineProperty(compiledProps, key, {
|
|
5891
6184
|
get: function get() {
|
|
@@ -6196,17 +6489,17 @@
|
|
|
6196
6489
|
if (attr) {
|
|
6197
6490
|
// Patch an attribute
|
|
6198
6491
|
el.setAttribute(attr, String(content));
|
|
6199
|
-
} else if (
|
|
6492
|
+
} else if (_isA(content)) {
|
|
6200
6493
|
// Patch with array of children (strings and/or TACOs)
|
|
6201
6494
|
el.innerHTML = '';
|
|
6202
6495
|
content.forEach(function (item) {
|
|
6203
|
-
if (
|
|
6496
|
+
if (_is(item, 'string') || _is(item, 'number')) {
|
|
6204
6497
|
el.appendChild(document.createTextNode(String(item)));
|
|
6205
6498
|
} else if (item && item.t) {
|
|
6206
6499
|
el.appendChild(bw.createDOM(item));
|
|
6207
6500
|
}
|
|
6208
6501
|
});
|
|
6209
|
-
} else if (
|
|
6502
|
+
} else if (_is(content, 'object') && content.t) {
|
|
6210
6503
|
// Patch with a TACO — replace children
|
|
6211
6504
|
el.innerHTML = '';
|
|
6212
6505
|
el.appendChild(bw.createDOM(content));
|
|
@@ -6237,7 +6530,7 @@
|
|
|
6237
6530
|
bw.patchAll = function (patches) {
|
|
6238
6531
|
var results = {};
|
|
6239
6532
|
for (var id in patches) {
|
|
6240
|
-
if (
|
|
6533
|
+
if (_hop.call(patches, id)) {
|
|
6241
6534
|
results[id] = bw.patch(id, patches[id]);
|
|
6242
6535
|
}
|
|
6243
6536
|
}
|
|
@@ -6334,7 +6627,7 @@
|
|
|
6334
6627
|
snapshot[i].handler(detail);
|
|
6335
6628
|
called++;
|
|
6336
6629
|
} catch (err) {
|
|
6337
|
-
|
|
6630
|
+
_cw('bw.pub: subscriber error on topic "' + topic + '":', err);
|
|
6338
6631
|
}
|
|
6339
6632
|
}
|
|
6340
6633
|
return called;
|
|
@@ -6435,8 +6728,8 @@
|
|
|
6435
6728
|
* @see bw.funcGetDispatchStr
|
|
6436
6729
|
*/
|
|
6437
6730
|
bw.funcRegister = function (fn, name) {
|
|
6438
|
-
if (
|
|
6439
|
-
var fnID =
|
|
6731
|
+
if (!_is(fn, 'function')) return '';
|
|
6732
|
+
var fnID = _is(name, 'string') && name.length > 0 ? name : 'bw_fn_' + bw._fnIDCounter++;
|
|
6440
6733
|
bw._fnRegistry[fnID] = fn;
|
|
6441
6734
|
return fnID;
|
|
6442
6735
|
};
|
|
@@ -6455,8 +6748,8 @@
|
|
|
6455
6748
|
bw.funcGetById = function (name, errFn) {
|
|
6456
6749
|
name = String(name);
|
|
6457
6750
|
if (name in bw._fnRegistry) return bw._fnRegistry[name];
|
|
6458
|
-
return
|
|
6459
|
-
|
|
6751
|
+
return _is(errFn, 'function') ? errFn : function () {
|
|
6752
|
+
_cw('bw.funcGetById: unregistered fn "' + name + '"');
|
|
6460
6753
|
};
|
|
6461
6754
|
};
|
|
6462
6755
|
|
|
@@ -6498,13 +6791,23 @@
|
|
|
6498
6791
|
bw.funcGetRegistry = function () {
|
|
6499
6792
|
var copy = {};
|
|
6500
6793
|
for (var k in bw._fnRegistry) {
|
|
6501
|
-
if (
|
|
6794
|
+
if (_hop.call(bw._fnRegistry, k)) {
|
|
6502
6795
|
copy[k] = bw._fnRegistry[k];
|
|
6503
6796
|
}
|
|
6504
6797
|
}
|
|
6505
6798
|
return copy;
|
|
6506
6799
|
};
|
|
6507
6800
|
|
|
6801
|
+
/**
|
|
6802
|
+
* Minimal runtime shim for funcRegister dispatch in static HTML.
|
|
6803
|
+
* When embedded in a `<script>` tag, provides just enough infrastructure
|
|
6804
|
+
* for `bw.funcGetById()` calls to resolve. The actual function bodies
|
|
6805
|
+
* are emitted separately as `bw._fnRegistry['bw_fn_X'] = ...;` assignments.
|
|
6806
|
+
* @type {string}
|
|
6807
|
+
* @category Function Registry
|
|
6808
|
+
*/
|
|
6809
|
+
bw._FUNC_REGISTRY_SHIM = '(function(){var bw=window.bw||(window.bw={});' + 'if(!bw._fnRegistry)bw._fnRegistry={};' + 'bw.funcGetById=function(n){return bw._fnRegistry[n]||function(){' + 'console.warn("bw: unregistered fn "+n)};};' + 'bw.funcRegister=function(fn,name){' + 'var id=name||("bw_fn_"+(bw._fnIDCounter=(bw._fnIDCounter||0)+1));' + 'bw._fnRegistry[id]=fn;return id;};' + 'window.bw=bw;})();';
|
|
6810
|
+
|
|
6508
6811
|
// ===================================================================================
|
|
6509
6812
|
// Template Binding Utilities
|
|
6510
6813
|
// ===================================================================================
|
|
@@ -6536,7 +6839,10 @@
|
|
|
6536
6839
|
var parts = path.split('.');
|
|
6537
6840
|
var val = state;
|
|
6538
6841
|
for (var i = 0; i < parts.length; i++) {
|
|
6539
|
-
if (val == null)
|
|
6842
|
+
if (val == null) {
|
|
6843
|
+
if (bw.debug) _cw('bw.debug: _evaluatePath — null at key "' + parts[i] + '" in path "' + path + '"');
|
|
6844
|
+
return '';
|
|
6845
|
+
}
|
|
6540
6846
|
val = val[parts[i]];
|
|
6541
6847
|
}
|
|
6542
6848
|
return val == null ? '' : val;
|
|
@@ -6556,7 +6862,7 @@
|
|
|
6556
6862
|
*/
|
|
6557
6863
|
bw._compiledExprs = {};
|
|
6558
6864
|
bw._resolveTemplate = function (str, state, compile) {
|
|
6559
|
-
if (
|
|
6865
|
+
if (!_is(str, 'string') || str.indexOf('${') < 0) return str;
|
|
6560
6866
|
var bindings = bw._parseBindings(str);
|
|
6561
6867
|
if (bindings.length === 0) return str;
|
|
6562
6868
|
var result = '';
|
|
@@ -6579,6 +6885,7 @@
|
|
|
6579
6885
|
try {
|
|
6580
6886
|
val = bw._compiledExprs[b.expr](state);
|
|
6581
6887
|
} catch (e) {
|
|
6888
|
+
if (bw.debug) _cw('bw.debug: _resolveTemplate — Tier 2 eval failed for "${' + b.expr + '}":', e.message);
|
|
6582
6889
|
val = '';
|
|
6583
6890
|
}
|
|
6584
6891
|
} else {
|
|
@@ -6686,7 +6993,7 @@
|
|
|
6686
6993
|
this._state = {};
|
|
6687
6994
|
if (o.state) {
|
|
6688
6995
|
for (var k in o.state) {
|
|
6689
|
-
if (
|
|
6996
|
+
if (_hop.call(o.state, k)) {
|
|
6690
6997
|
this._state[k] = o.state[k];
|
|
6691
6998
|
}
|
|
6692
6999
|
}
|
|
@@ -6695,7 +7002,7 @@
|
|
|
6695
7002
|
this._actions = {};
|
|
6696
7003
|
if (o.actions) {
|
|
6697
7004
|
for (var k2 in o.actions) {
|
|
6698
|
-
if (
|
|
7005
|
+
if (_hop.call(o.actions, k2)) {
|
|
6699
7006
|
this._actions[k2] = o.actions[k2];
|
|
6700
7007
|
}
|
|
6701
7008
|
}
|
|
@@ -6705,7 +7012,7 @@
|
|
|
6705
7012
|
if (o.methods) {
|
|
6706
7013
|
var self = this;
|
|
6707
7014
|
for (var k3 in o.methods) {
|
|
6708
|
-
if (
|
|
7015
|
+
if (_hop.call(o.methods, k3)) {
|
|
6709
7016
|
this._methods[k3] = o.methods[k3];
|
|
6710
7017
|
(function (methodName, methodFn) {
|
|
6711
7018
|
self[methodName] = function () {
|
|
@@ -6738,14 +7045,23 @@
|
|
|
6738
7045
|
this._compile = !!o.compile;
|
|
6739
7046
|
this._bw_refs = {};
|
|
6740
7047
|
this._refCounter = 0;
|
|
7048
|
+
// Child component ownership (Bug #5)
|
|
7049
|
+
this._children = [];
|
|
7050
|
+
this._parent = null;
|
|
7051
|
+
// Factory metadata for BCCL rebuild (Bug #6)
|
|
7052
|
+
this._factory = taco._bwFactory || null;
|
|
6741
7053
|
}
|
|
6742
7054
|
|
|
7055
|
+
// Short alias for ComponentHandle.prototype (see alias block at top of file).
|
|
7056
|
+
// 28 method definitions × 25 chars = ~700B raw savings in minified output.
|
|
7057
|
+
var _chp = ComponentHandle.prototype;
|
|
7058
|
+
|
|
6743
7059
|
// ── State Methods ──
|
|
6744
7060
|
|
|
6745
7061
|
/**
|
|
6746
7062
|
* Get a state value. Dot-path supported: `get('user.name')`
|
|
6747
7063
|
*/
|
|
6748
|
-
|
|
7064
|
+
_chp.get = function (key) {
|
|
6749
7065
|
return bw._evaluatePath(this._state, key);
|
|
6750
7066
|
};
|
|
6751
7067
|
|
|
@@ -6755,12 +7071,13 @@
|
|
|
6755
7071
|
* @param {*} value - New value
|
|
6756
7072
|
* @param {Object} [opts] - Options. `{sync: true}` for immediate flush.
|
|
6757
7073
|
*/
|
|
6758
|
-
|
|
7074
|
+
_chp.set = function (key, value, opts) {
|
|
6759
7075
|
// Dot-path set
|
|
6760
7076
|
var parts = key.split('.');
|
|
6761
7077
|
var obj = this._state;
|
|
6762
7078
|
for (var i = 0; i < parts.length - 1; i++) {
|
|
6763
|
-
if (
|
|
7079
|
+
if (!_is(obj[parts[i]], 'object')) {
|
|
7080
|
+
if (bw.debug) _cw('bw.debug: set() — auto-creating intermediate "' + parts[i] + '" in path "' + key + '"');
|
|
6764
7081
|
obj[parts[i]] = {};
|
|
6765
7082
|
}
|
|
6766
7083
|
obj = obj[parts[i]];
|
|
@@ -6780,10 +7097,10 @@
|
|
|
6780
7097
|
/**
|
|
6781
7098
|
* Get a shallow clone of the full state.
|
|
6782
7099
|
*/
|
|
6783
|
-
|
|
7100
|
+
_chp.getState = function () {
|
|
6784
7101
|
var clone = {};
|
|
6785
7102
|
for (var k in this._state) {
|
|
6786
|
-
if (
|
|
7103
|
+
if (_hop.call(this._state, k)) {
|
|
6787
7104
|
clone[k] = this._state[k];
|
|
6788
7105
|
}
|
|
6789
7106
|
}
|
|
@@ -6795,9 +7112,9 @@
|
|
|
6795
7112
|
* @param {Object} updates - Key-value pairs to merge
|
|
6796
7113
|
* @param {Object} [opts] - Options. `{sync: true}` for immediate flush.
|
|
6797
7114
|
*/
|
|
6798
|
-
|
|
7115
|
+
_chp.setState = function (updates, opts) {
|
|
6799
7116
|
for (var k in updates) {
|
|
6800
|
-
if (
|
|
7117
|
+
if (_hop.call(updates, k)) {
|
|
6801
7118
|
this._state[k] = updates[k];
|
|
6802
7119
|
this._dirtyKeys[k] = true;
|
|
6803
7120
|
}
|
|
@@ -6814,9 +7131,9 @@
|
|
|
6814
7131
|
/**
|
|
6815
7132
|
* Push a value onto an array in state. Clones the array.
|
|
6816
7133
|
*/
|
|
6817
|
-
|
|
7134
|
+
_chp.push = function (key, val) {
|
|
6818
7135
|
var arr = this.get(key);
|
|
6819
|
-
var newArr =
|
|
7136
|
+
var newArr = _isA(arr) ? arr.slice() : [];
|
|
6820
7137
|
newArr.push(val);
|
|
6821
7138
|
this.set(key, newArr);
|
|
6822
7139
|
};
|
|
@@ -6824,9 +7141,9 @@
|
|
|
6824
7141
|
/**
|
|
6825
7142
|
* Splice an array in state. Clones the array.
|
|
6826
7143
|
*/
|
|
6827
|
-
|
|
7144
|
+
_chp.splice = function (key, start, deleteCount) {
|
|
6828
7145
|
var arr = this.get(key);
|
|
6829
|
-
var newArr =
|
|
7146
|
+
var newArr = _isA(arr) ? arr.slice() : [];
|
|
6830
7147
|
var args = [start, deleteCount].concat(Array.prototype.slice.call(arguments, 3));
|
|
6831
7148
|
Array.prototype.splice.apply(newArr, args);
|
|
6832
7149
|
this.set(key, newArr);
|
|
@@ -6834,7 +7151,7 @@
|
|
|
6834
7151
|
|
|
6835
7152
|
// ── Scheduling ──
|
|
6836
7153
|
|
|
6837
|
-
|
|
7154
|
+
_chp._scheduleDirty = function () {
|
|
6838
7155
|
if (!this._scheduled) {
|
|
6839
7156
|
this._scheduled = true;
|
|
6840
7157
|
bw._dirtyComponents.push(this);
|
|
@@ -6849,16 +7166,16 @@
|
|
|
6849
7166
|
* Creates binding descriptors with refIds for targeted DOM updates.
|
|
6850
7167
|
* @private
|
|
6851
7168
|
*/
|
|
6852
|
-
|
|
7169
|
+
_chp._compileBindings = function () {
|
|
6853
7170
|
this._bindings = [];
|
|
6854
7171
|
this._refCounter = 0;
|
|
6855
|
-
var stateKeys =
|
|
7172
|
+
var stateKeys = _keys(this._state);
|
|
6856
7173
|
var self = this;
|
|
6857
7174
|
function walkTaco(taco, path) {
|
|
6858
|
-
if (
|
|
7175
|
+
if (!_is(taco, 'object') || !taco.t) return taco;
|
|
6859
7176
|
|
|
6860
7177
|
// Check content for bindings
|
|
6861
|
-
if (
|
|
7178
|
+
if (_is(taco.c, 'string') && taco.c.indexOf('${') >= 0) {
|
|
6862
7179
|
var refId = 'bw_ref_' + self._refCounter++;
|
|
6863
7180
|
var parsed = bw._parseBindings(taco.c);
|
|
6864
7181
|
var deps = [];
|
|
@@ -6880,10 +7197,10 @@
|
|
|
6880
7197
|
// Check attributes for bindings
|
|
6881
7198
|
if (taco.a) {
|
|
6882
7199
|
for (var attrName in taco.a) {
|
|
6883
|
-
if (!
|
|
7200
|
+
if (!_hop.call(taco.a, attrName)) continue;
|
|
6884
7201
|
if (attrName === 'data-bw_ref') continue;
|
|
6885
7202
|
var attrVal = taco.a[attrName];
|
|
6886
|
-
if (
|
|
7203
|
+
if (_is(attrVal, 'string') && attrVal.indexOf('${') >= 0) {
|
|
6887
7204
|
var refId2 = 'bw_ref_' + self._refCounter++;
|
|
6888
7205
|
var parsed2 = bw._parseBindings(attrVal);
|
|
6889
7206
|
var deps2 = [];
|
|
@@ -6909,9 +7226,34 @@
|
|
|
6909
7226
|
}
|
|
6910
7227
|
|
|
6911
7228
|
// Recurse into children
|
|
6912
|
-
if (
|
|
7229
|
+
if (_isA(taco.c)) {
|
|
6913
7230
|
for (var i = 0; i < taco.c.length; i++) {
|
|
6914
|
-
|
|
7231
|
+
// Wrap string children with ${expr} in a span so patches target the span, not the parent
|
|
7232
|
+
if (_is(taco.c[i], 'string') && taco.c[i].indexOf('${') >= 0) {
|
|
7233
|
+
var mixedRefId = 'bw_ref_' + self._refCounter++;
|
|
7234
|
+
var mixedParsed = bw._parseBindings(taco.c[i]);
|
|
7235
|
+
var mixedDeps = [];
|
|
7236
|
+
for (var mi = 0; mi < mixedParsed.length; mi++) {
|
|
7237
|
+
mixedDeps = mixedDeps.concat(bw._extractDeps(mixedParsed[mi].expr, stateKeys));
|
|
7238
|
+
}
|
|
7239
|
+
self._bindings.push({
|
|
7240
|
+
expr: taco.c[i],
|
|
7241
|
+
type: 'content',
|
|
7242
|
+
refId: mixedRefId,
|
|
7243
|
+
deps: mixedDeps,
|
|
7244
|
+
template: taco.c[i]
|
|
7245
|
+
});
|
|
7246
|
+
// Replace string with a span wrapper so textContent targets the span only
|
|
7247
|
+
taco.c[i] = {
|
|
7248
|
+
t: 'span',
|
|
7249
|
+
a: {
|
|
7250
|
+
'data-bw_ref': mixedRefId,
|
|
7251
|
+
style: 'display:contents'
|
|
7252
|
+
},
|
|
7253
|
+
c: taco.c[i]
|
|
7254
|
+
};
|
|
7255
|
+
}
|
|
7256
|
+
if (_is(taco.c[i], 'object') && taco.c[i].t) {
|
|
6915
7257
|
walkTaco(taco.c[i], path.concat(i));
|
|
6916
7258
|
}
|
|
6917
7259
|
// Handle bw.when/bw.each markers
|
|
@@ -6946,7 +7288,7 @@
|
|
|
6946
7288
|
taco.c[i]._refId = eachRefId;
|
|
6947
7289
|
}
|
|
6948
7290
|
}
|
|
6949
|
-
} else if (
|
|
7291
|
+
} else if (_is(taco.c, 'object') && taco.c.t) {
|
|
6950
7292
|
walkTaco(taco.c, path.concat(0));
|
|
6951
7293
|
}
|
|
6952
7294
|
return taco;
|
|
@@ -6960,7 +7302,7 @@
|
|
|
6960
7302
|
* Build ref map from the live DOM after createDOM.
|
|
6961
7303
|
* @private
|
|
6962
7304
|
*/
|
|
6963
|
-
|
|
7305
|
+
_chp._collectRefs = function () {
|
|
6964
7306
|
this._bw_refs = {};
|
|
6965
7307
|
if (!this.element) return;
|
|
6966
7308
|
var els = this.element.querySelectorAll('[data-bw_ref]');
|
|
@@ -6981,7 +7323,7 @@
|
|
|
6981
7323
|
* Creates DOM, compiles bindings, registers actions, and calls lifecycle hooks.
|
|
6982
7324
|
* @param {Element} parentEl - DOM element to mount into
|
|
6983
7325
|
*/
|
|
6984
|
-
|
|
7326
|
+
_chp.mount = function (parentEl) {
|
|
6985
7327
|
// willMount hook
|
|
6986
7328
|
if (this._hooks.willMount) this._hooks.willMount(this);
|
|
6987
7329
|
|
|
@@ -7003,7 +7345,7 @@
|
|
|
7003
7345
|
// Register named actions in function registry
|
|
7004
7346
|
var self = this;
|
|
7005
7347
|
for (var actionName in this._actions) {
|
|
7006
|
-
if (
|
|
7348
|
+
if (_hop.call(this._actions, actionName)) {
|
|
7007
7349
|
var registeredName = this._bwId + '_' + actionName;
|
|
7008
7350
|
(function (aName) {
|
|
7009
7351
|
bw.funcRegister(function (evt) {
|
|
@@ -7022,6 +7364,11 @@
|
|
|
7022
7364
|
this.element = bw.createDOM(tacoForDOM);
|
|
7023
7365
|
this.element._bwComponentHandle = this;
|
|
7024
7366
|
this.element.setAttribute('data-bw_comp_id', this._bwId);
|
|
7367
|
+
|
|
7368
|
+
// Restore o.render from original TACO (stripped by _tacoForDOM)
|
|
7369
|
+
if (this.taco.o && this.taco.o.render) {
|
|
7370
|
+
this.element._bw_render = this.taco.o.render;
|
|
7371
|
+
}
|
|
7025
7372
|
if (this._userTag) {
|
|
7026
7373
|
this.element.classList.add(this._userTag);
|
|
7027
7374
|
}
|
|
@@ -7036,6 +7383,16 @@
|
|
|
7036
7383
|
this._resolveAndApplyAll();
|
|
7037
7384
|
this.mounted = true;
|
|
7038
7385
|
|
|
7386
|
+
// Scan for child ComponentHandles and link parent/child (Bug #5)
|
|
7387
|
+
var childEls = this.element.querySelectorAll('[data-bw_comp_id]');
|
|
7388
|
+
for (var ci = 0; ci < childEls.length; ci++) {
|
|
7389
|
+
var ch = childEls[ci]._bwComponentHandle;
|
|
7390
|
+
if (ch && ch !== this && !ch._parent) {
|
|
7391
|
+
ch._parent = this;
|
|
7392
|
+
this._children.push(ch);
|
|
7393
|
+
}
|
|
7394
|
+
}
|
|
7395
|
+
|
|
7039
7396
|
// mounted hook (backward compat: fn.length === 2 wraps (el, state))
|
|
7040
7397
|
if (this._hooks.mounted) {
|
|
7041
7398
|
if (this._hooks.mounted.length === 2) {
|
|
@@ -7044,15 +7401,20 @@
|
|
|
7044
7401
|
this._hooks.mounted(this);
|
|
7045
7402
|
}
|
|
7046
7403
|
}
|
|
7404
|
+
|
|
7405
|
+
// Invoke o.render on initial mount (if present)
|
|
7406
|
+
if (this.element._bw_render) {
|
|
7407
|
+
this.element._bw_render(this.element, this._state);
|
|
7408
|
+
}
|
|
7047
7409
|
};
|
|
7048
7410
|
|
|
7049
7411
|
/**
|
|
7050
7412
|
* Prepare TACO for initial render: resolve when/each markers.
|
|
7051
7413
|
* @private
|
|
7052
7414
|
*/
|
|
7053
|
-
|
|
7054
|
-
if (!
|
|
7055
|
-
if (
|
|
7415
|
+
_chp._prepareTaco = function (taco) {
|
|
7416
|
+
if (!_is(taco, 'object')) return;
|
|
7417
|
+
if (_isA(taco.c)) {
|
|
7056
7418
|
for (var i = taco.c.length - 1; i >= 0; i--) {
|
|
7057
7419
|
var child = taco.c[i];
|
|
7058
7420
|
if (child && child._bwWhen) {
|
|
@@ -7093,7 +7455,7 @@
|
|
|
7093
7455
|
var eachExprStr = child.expr.replace(/^\$\{|\}$/g, '');
|
|
7094
7456
|
var arr = bw._evaluatePath(this._state, eachExprStr);
|
|
7095
7457
|
var items = [];
|
|
7096
|
-
if (
|
|
7458
|
+
if (_isA(arr)) {
|
|
7097
7459
|
for (var j = 0; j < arr.length; j++) {
|
|
7098
7460
|
items.push(child.factory(arr[j], j));
|
|
7099
7461
|
}
|
|
@@ -7107,11 +7469,11 @@
|
|
|
7107
7469
|
c: items
|
|
7108
7470
|
};
|
|
7109
7471
|
}
|
|
7110
|
-
if (
|
|
7472
|
+
if (_is(taco.c[i], 'object') && taco.c[i].t) {
|
|
7111
7473
|
this._prepareTaco(taco.c[i]);
|
|
7112
7474
|
}
|
|
7113
7475
|
}
|
|
7114
|
-
} else if (
|
|
7476
|
+
} else if (_is(taco.c, 'object') && taco.c.t) {
|
|
7115
7477
|
this._prepareTaco(taco.c);
|
|
7116
7478
|
}
|
|
7117
7479
|
};
|
|
@@ -7120,12 +7482,12 @@
|
|
|
7120
7482
|
* Wire action name strings (in onclick etc.) to dispatch function calls.
|
|
7121
7483
|
* @private
|
|
7122
7484
|
*/
|
|
7123
|
-
|
|
7124
|
-
if (!
|
|
7485
|
+
_chp._wireActions = function (taco) {
|
|
7486
|
+
if (!_is(taco, 'object') || !taco.t) return;
|
|
7125
7487
|
if (taco.a) {
|
|
7126
7488
|
for (var key in taco.a) {
|
|
7127
|
-
if (!
|
|
7128
|
-
if (key.startsWith('on') &&
|
|
7489
|
+
if (!_hop.call(taco.a, key)) continue;
|
|
7490
|
+
if (key.startsWith('on') && _is(taco.a[key], 'string')) {
|
|
7129
7491
|
var actionName = taco.a[key];
|
|
7130
7492
|
if (actionName in this._actions) {
|
|
7131
7493
|
var registeredName = this._bwId + '_' + actionName;
|
|
@@ -7139,11 +7501,11 @@
|
|
|
7139
7501
|
}
|
|
7140
7502
|
}
|
|
7141
7503
|
}
|
|
7142
|
-
if (
|
|
7504
|
+
if (_isA(taco.c)) {
|
|
7143
7505
|
for (var i = 0; i < taco.c.length; i++) {
|
|
7144
7506
|
this._wireActions(taco.c[i]);
|
|
7145
7507
|
}
|
|
7146
|
-
} else if (
|
|
7508
|
+
} else if (_is(taco.c, 'object') && taco.c.t) {
|
|
7147
7509
|
this._wireActions(taco.c);
|
|
7148
7510
|
}
|
|
7149
7511
|
};
|
|
@@ -7152,7 +7514,7 @@
|
|
|
7152
7514
|
* Deep-clone a TACO tree, preserving _bwWhen/_bwEach markers and their factories.
|
|
7153
7515
|
* @private
|
|
7154
7516
|
*/
|
|
7155
|
-
|
|
7517
|
+
_chp._deepCloneTaco = function (taco) {
|
|
7156
7518
|
if (taco == null) return taco;
|
|
7157
7519
|
// Preserve _bwWhen / _bwEach markers (contain functions)
|
|
7158
7520
|
if (taco._bwWhen) {
|
|
@@ -7171,22 +7533,22 @@
|
|
|
7171
7533
|
_refId: taco._refId
|
|
7172
7534
|
};
|
|
7173
7535
|
}
|
|
7174
|
-
if (
|
|
7536
|
+
if (!_is(taco, 'object') || !taco.t) return taco;
|
|
7175
7537
|
var result = {
|
|
7176
7538
|
t: taco.t
|
|
7177
7539
|
};
|
|
7178
7540
|
if (taco.a) {
|
|
7179
7541
|
result.a = {};
|
|
7180
7542
|
for (var k in taco.a) {
|
|
7181
|
-
if (
|
|
7543
|
+
if (_hop.call(taco.a, k)) result.a[k] = taco.a[k];
|
|
7182
7544
|
}
|
|
7183
7545
|
}
|
|
7184
7546
|
if (taco.c != null) {
|
|
7185
|
-
if (
|
|
7547
|
+
if (_isA(taco.c)) {
|
|
7186
7548
|
result.c = taco.c.map(function (child) {
|
|
7187
7549
|
return this._deepCloneTaco(child);
|
|
7188
7550
|
}.bind(this));
|
|
7189
|
-
} else if (
|
|
7551
|
+
} else if (_is(taco.c, 'object')) {
|
|
7190
7552
|
result.c = this._deepCloneTaco(taco.c);
|
|
7191
7553
|
} else {
|
|
7192
7554
|
result.c = taco.c;
|
|
@@ -7200,31 +7562,34 @@
|
|
|
7200
7562
|
* Create a copy of TACO suitable for createDOM (strips o to prevent double lifecycle).
|
|
7201
7563
|
* @private
|
|
7202
7564
|
*/
|
|
7203
|
-
|
|
7204
|
-
if (!
|
|
7565
|
+
_chp._tacoForDOM = function (taco) {
|
|
7566
|
+
if (!_is(taco, 'object') || !taco.t) return taco;
|
|
7205
7567
|
var result = {
|
|
7206
7568
|
t: taco.t
|
|
7207
7569
|
};
|
|
7208
7570
|
if (taco.a) result.a = taco.a;
|
|
7209
7571
|
if (taco.c != null) {
|
|
7210
|
-
if (
|
|
7572
|
+
if (_isA(taco.c)) {
|
|
7211
7573
|
result.c = taco.c.map(function (child) {
|
|
7212
7574
|
return this._tacoForDOM(child);
|
|
7213
7575
|
}.bind(this));
|
|
7214
|
-
} else if (
|
|
7576
|
+
} else if (_is(taco.c, 'object') && taco.c.t) {
|
|
7215
7577
|
result.c = this._tacoForDOM(taco.c);
|
|
7216
7578
|
} else {
|
|
7217
7579
|
result.c = taco.c;
|
|
7218
7580
|
}
|
|
7219
7581
|
}
|
|
7220
7582
|
// Intentionally strip o (no mounted/unmount/state/render on sub-elements)
|
|
7583
|
+
if (taco.o && (taco.o.mounted || taco.o.render || taco.o.unmount)) {
|
|
7584
|
+
_cw('bw: _tacoForDOM stripped o.mounted/render/unmount from child <' + taco.t + '>. Use onclick attribute or bw.component() for child interactivity.');
|
|
7585
|
+
}
|
|
7221
7586
|
return result;
|
|
7222
7587
|
};
|
|
7223
7588
|
|
|
7224
7589
|
/**
|
|
7225
7590
|
* Unmount: remove from DOM, deactivate, preserve state for re-mount.
|
|
7226
7591
|
*/
|
|
7227
|
-
|
|
7592
|
+
_chp.unmount = function () {
|
|
7228
7593
|
if (!this.mounted) return;
|
|
7229
7594
|
|
|
7230
7595
|
// unmount hook
|
|
@@ -7258,11 +7623,22 @@
|
|
|
7258
7623
|
/**
|
|
7259
7624
|
* Destroy: unmount + clear state + unregister actions.
|
|
7260
7625
|
*/
|
|
7261
|
-
|
|
7626
|
+
_chp.destroy = function () {
|
|
7262
7627
|
// willDestroy hook
|
|
7263
7628
|
if (this._hooks.willDestroy) {
|
|
7264
7629
|
this._hooks.willDestroy(this);
|
|
7265
7630
|
}
|
|
7631
|
+
|
|
7632
|
+
// Cascade destroy to children depth-first (Bug #5)
|
|
7633
|
+
for (var ci = this._children.length - 1; ci >= 0; ci--) {
|
|
7634
|
+
this._children[ci].destroy();
|
|
7635
|
+
}
|
|
7636
|
+
this._children = [];
|
|
7637
|
+
if (this._parent) {
|
|
7638
|
+
var idx = this._parent._children.indexOf(this);
|
|
7639
|
+
if (idx >= 0) this._parent._children.splice(idx, 1);
|
|
7640
|
+
this._parent = null;
|
|
7641
|
+
}
|
|
7266
7642
|
this.unmount();
|
|
7267
7643
|
|
|
7268
7644
|
// Unregister actions from function registry
|
|
@@ -7289,12 +7665,37 @@
|
|
|
7289
7665
|
* Flush dirty state: resolve changed bindings and apply to DOM.
|
|
7290
7666
|
* @private
|
|
7291
7667
|
*/
|
|
7292
|
-
|
|
7668
|
+
_chp._flush = function () {
|
|
7293
7669
|
this._scheduled = false;
|
|
7294
|
-
var changedKeys =
|
|
7670
|
+
var changedKeys = _keys(this._dirtyKeys);
|
|
7295
7671
|
this._dirtyKeys = {};
|
|
7296
7672
|
if (changedKeys.length === 0 || !this.mounted) return;
|
|
7297
7673
|
|
|
7674
|
+
// Factory rebuild: if a BCCL factory exists and changed keys overlap factory props,
|
|
7675
|
+
// rebuild the TACO from the factory with merged state (Bug #6)
|
|
7676
|
+
if (this._factory) {
|
|
7677
|
+
var rebuildNeeded = false;
|
|
7678
|
+
for (var fi = 0; fi < changedKeys.length; fi++) {
|
|
7679
|
+
if (_hop.call(this._factory.props, changedKeys[fi])) {
|
|
7680
|
+
rebuildNeeded = true;
|
|
7681
|
+
break;
|
|
7682
|
+
}
|
|
7683
|
+
}
|
|
7684
|
+
if (rebuildNeeded) {
|
|
7685
|
+
var merged = {};
|
|
7686
|
+
for (var mk in this._factory.props) if (_hop.call(this._factory.props, mk)) merged[mk] = this._factory.props[mk];
|
|
7687
|
+
for (var sk in this._state) if (_hop.call(this._state, sk)) merged[sk] = this._state[sk];
|
|
7688
|
+
this._factory.props = merged;
|
|
7689
|
+
var newTaco = bw.make(this._factory.type, merged);
|
|
7690
|
+
newTaco._bwFactory = this._factory;
|
|
7691
|
+
this.taco = newTaco;
|
|
7692
|
+
this._originalTaco = this._deepCloneTaco(newTaco);
|
|
7693
|
+
this._render();
|
|
7694
|
+
if (this._hooks.onUpdate) this._hooks.onUpdate(this, changedKeys);
|
|
7695
|
+
return;
|
|
7696
|
+
}
|
|
7697
|
+
}
|
|
7698
|
+
|
|
7298
7699
|
// willUpdate hook
|
|
7299
7700
|
if (this._hooks.willUpdate) {
|
|
7300
7701
|
this._hooks.willUpdate(this, changedKeys);
|
|
@@ -7332,7 +7733,7 @@
|
|
|
7332
7733
|
* Returns list of patches to apply.
|
|
7333
7734
|
* @private
|
|
7334
7735
|
*/
|
|
7335
|
-
|
|
7736
|
+
_chp._resolveBindings = function (changedKeys) {
|
|
7336
7737
|
var patches = [];
|
|
7337
7738
|
for (var i = 0; i < this._bindings.length; i++) {
|
|
7338
7739
|
var b = this._bindings[i];
|
|
@@ -7368,11 +7769,14 @@
|
|
|
7368
7769
|
* Apply patches to DOM.
|
|
7369
7770
|
* @private
|
|
7370
7771
|
*/
|
|
7371
|
-
|
|
7772
|
+
_chp._applyPatches = function (patches) {
|
|
7372
7773
|
for (var i = 0; i < patches.length; i++) {
|
|
7373
7774
|
var p = patches[i];
|
|
7374
7775
|
var el = this._bw_refs[p.refId];
|
|
7375
|
-
if (!el)
|
|
7776
|
+
if (!el) {
|
|
7777
|
+
if (bw.debug) _cw('bw.debug: _applyPatches — ref "' + p.refId + '" not found in DOM');
|
|
7778
|
+
continue;
|
|
7779
|
+
}
|
|
7376
7780
|
if (p.type === 'content') {
|
|
7377
7781
|
el.textContent = p.value;
|
|
7378
7782
|
} else if (p.type === 'attribute') {
|
|
@@ -7389,7 +7793,7 @@
|
|
|
7389
7793
|
* Resolve all bindings and apply (used for initial render).
|
|
7390
7794
|
* @private
|
|
7391
7795
|
*/
|
|
7392
|
-
|
|
7796
|
+
_chp._resolveAndApplyAll = function () {
|
|
7393
7797
|
var patches = [];
|
|
7394
7798
|
for (var i = 0; i < this._bindings.length; i++) {
|
|
7395
7799
|
var b = this._bindings[i];
|
|
@@ -7411,7 +7815,7 @@
|
|
|
7411
7815
|
* Full re-render for structural changes (when/each branch switches).
|
|
7412
7816
|
* @private
|
|
7413
7817
|
*/
|
|
7414
|
-
|
|
7818
|
+
_chp._render = function () {
|
|
7415
7819
|
if (!this.element || !this.element.parentNode) return;
|
|
7416
7820
|
var parent = this.element.parentNode;
|
|
7417
7821
|
var nextSibling = this.element.nextSibling;
|
|
@@ -7450,7 +7854,7 @@
|
|
|
7450
7854
|
* @param {string} event - Event name (e.g., 'click')
|
|
7451
7855
|
* @param {Function} handler - Event handler
|
|
7452
7856
|
*/
|
|
7453
|
-
|
|
7857
|
+
_chp.on = function (event, handler) {
|
|
7454
7858
|
if (this.element) {
|
|
7455
7859
|
this.element.addEventListener(event, handler);
|
|
7456
7860
|
}
|
|
@@ -7465,7 +7869,7 @@
|
|
|
7465
7869
|
* @param {string} event - Event name
|
|
7466
7870
|
* @param {Function} handler - Handler to remove
|
|
7467
7871
|
*/
|
|
7468
|
-
|
|
7872
|
+
_chp.off = function (event, handler) {
|
|
7469
7873
|
if (this.element) {
|
|
7470
7874
|
this.element.removeEventListener(event, handler);
|
|
7471
7875
|
}
|
|
@@ -7480,7 +7884,7 @@
|
|
|
7480
7884
|
* @param {Function} handler - Handler function
|
|
7481
7885
|
* @returns {Function} Unsubscribe function
|
|
7482
7886
|
*/
|
|
7483
|
-
|
|
7887
|
+
_chp.sub = function (topic, handler) {
|
|
7484
7888
|
var unsub = bw.sub(topic, handler);
|
|
7485
7889
|
this._subs.push(unsub);
|
|
7486
7890
|
return unsub;
|
|
@@ -7491,10 +7895,10 @@
|
|
|
7491
7895
|
* @param {string} name - Action name
|
|
7492
7896
|
* @param {...*} args - Arguments passed after comp
|
|
7493
7897
|
*/
|
|
7494
|
-
|
|
7898
|
+
_chp.action = function (name) {
|
|
7495
7899
|
var fn = this._actions[name];
|
|
7496
7900
|
if (!fn) {
|
|
7497
|
-
|
|
7901
|
+
_cw('ComponentHandle.action: unknown action "' + name + '"');
|
|
7498
7902
|
return;
|
|
7499
7903
|
}
|
|
7500
7904
|
var args = [this].concat(Array.prototype.slice.call(arguments, 1));
|
|
@@ -7506,7 +7910,7 @@
|
|
|
7506
7910
|
* @param {string} sel - CSS selector
|
|
7507
7911
|
* @returns {Element|null}
|
|
7508
7912
|
*/
|
|
7509
|
-
|
|
7913
|
+
_chp.select = function (sel) {
|
|
7510
7914
|
return this.element ? this.element.querySelector(sel) : null;
|
|
7511
7915
|
};
|
|
7512
7916
|
|
|
@@ -7515,7 +7919,7 @@
|
|
|
7515
7919
|
* @param {string} sel - CSS selector
|
|
7516
7920
|
* @returns {Element[]}
|
|
7517
7921
|
*/
|
|
7518
|
-
|
|
7922
|
+
_chp.selectAll = function (sel) {
|
|
7519
7923
|
if (!this.element) return [];
|
|
7520
7924
|
return Array.prototype.slice.call(this.element.querySelectorAll(sel));
|
|
7521
7925
|
};
|
|
@@ -7526,7 +7930,7 @@
|
|
|
7526
7930
|
* @param {string} tag - User-defined identifier (e.g. 'dashboard_prod_east')
|
|
7527
7931
|
* @returns {ComponentHandle} this (for chaining)
|
|
7528
7932
|
*/
|
|
7529
|
-
|
|
7933
|
+
_chp.userTag = function (tag) {
|
|
7530
7934
|
this._userTag = tag;
|
|
7531
7935
|
if (this.element) {
|
|
7532
7936
|
this.element.classList.add(tag);
|
|
@@ -7635,14 +8039,384 @@
|
|
|
7635
8039
|
}
|
|
7636
8040
|
if (!el || !el._bwComponentHandle) return false;
|
|
7637
8041
|
var comp = el._bwComponentHandle;
|
|
7638
|
-
if (
|
|
7639
|
-
|
|
8042
|
+
if (!_is(comp[action], 'function')) {
|
|
8043
|
+
_cw('bw.message: unknown action "' + action + '" on component ' + target);
|
|
7640
8044
|
return false;
|
|
7641
8045
|
}
|
|
7642
8046
|
comp[action](data);
|
|
7643
8047
|
return true;
|
|
7644
8048
|
};
|
|
7645
8049
|
|
|
8050
|
+
// ===================================================================================
|
|
8051
|
+
// bw.clientApply() / bw.clientConnect() — Server-driven UI protocol
|
|
8052
|
+
// ===================================================================================
|
|
8053
|
+
|
|
8054
|
+
/**
|
|
8055
|
+
* Registry of named functions sent via register messages.
|
|
8056
|
+
* Populated by clientApply({ type: 'register', name, body }).
|
|
8057
|
+
* Invoked by clientApply({ type: 'call', name, args }).
|
|
8058
|
+
* @private
|
|
8059
|
+
*/
|
|
8060
|
+
bw._clientFunctions = {};
|
|
8061
|
+
|
|
8062
|
+
/**
|
|
8063
|
+
* Whether exec messages are allowed. Set by clientConnect opts.allowExec.
|
|
8064
|
+
* Default false — exec messages are rejected unless explicitly opted in.
|
|
8065
|
+
* @private
|
|
8066
|
+
*/
|
|
8067
|
+
bw._allowExec = false;
|
|
8068
|
+
|
|
8069
|
+
/**
|
|
8070
|
+
* Built-in client functions available via call() without registration.
|
|
8071
|
+
* @private
|
|
8072
|
+
*/
|
|
8073
|
+
bw._builtinClientFunctions = {
|
|
8074
|
+
scrollTo: function scrollTo(selector) {
|
|
8075
|
+
var el = bw._el(selector);
|
|
8076
|
+
if (el) el.scrollTop = el.scrollHeight;
|
|
8077
|
+
},
|
|
8078
|
+
focus: function focus(selector) {
|
|
8079
|
+
var el = bw._el(selector);
|
|
8080
|
+
if (el && _is(el.focus, 'function')) el.focus();
|
|
8081
|
+
},
|
|
8082
|
+
download: function download(filename, content, mimeType) {
|
|
8083
|
+
if (typeof document === 'undefined') return;
|
|
8084
|
+
var blob = new Blob([content], {
|
|
8085
|
+
type: mimeType || 'text/plain'
|
|
8086
|
+
});
|
|
8087
|
+
var a = document.createElement('a');
|
|
8088
|
+
a.href = URL.createObjectURL(blob);
|
|
8089
|
+
a.download = filename;
|
|
8090
|
+
a.click();
|
|
8091
|
+
URL.revokeObjectURL(a.href);
|
|
8092
|
+
},
|
|
8093
|
+
clipboard: function clipboard(text) {
|
|
8094
|
+
if (typeof navigator !== 'undefined' && navigator.clipboard) {
|
|
8095
|
+
navigator.clipboard.writeText(text);
|
|
8096
|
+
}
|
|
8097
|
+
},
|
|
8098
|
+
redirect: function redirect(url) {
|
|
8099
|
+
if (typeof window !== 'undefined') window.location.href = url;
|
|
8100
|
+
},
|
|
8101
|
+
log: function log() {
|
|
8102
|
+
console.log.apply(console, arguments);
|
|
8103
|
+
}
|
|
8104
|
+
};
|
|
8105
|
+
|
|
8106
|
+
/**
|
|
8107
|
+
* Parse a bwserve protocol message string, supporting both strict JSON
|
|
8108
|
+
* and r-prefixed relaxed JSON (single-quoted strings, trailing commas).
|
|
8109
|
+
*
|
|
8110
|
+
* The r-prefix format is designed for C/C++ string literals where
|
|
8111
|
+
* double-quote escaping is painful. The parser is a state machine
|
|
8112
|
+
* that walks character by character — not a regex replace.
|
|
8113
|
+
*
|
|
8114
|
+
* Escaping: apostrophes inside single-quoted values must be escaped
|
|
8115
|
+
* with backslash: r{'name':'Barry\'s room'}
|
|
8116
|
+
*
|
|
8117
|
+
* @param {string} str - JSON or r-prefixed relaxed JSON string
|
|
8118
|
+
* @returns {Object} Parsed message object
|
|
8119
|
+
* @throws {SyntaxError} If the string is not valid JSON or relaxed JSON
|
|
8120
|
+
* @category Server
|
|
8121
|
+
*/
|
|
8122
|
+
bw.clientParse = function (str) {
|
|
8123
|
+
str = (str || '').trim();
|
|
8124
|
+
if (str.charAt(0) !== 'r') return JSON.parse(str);
|
|
8125
|
+
str = str.slice(1);
|
|
8126
|
+
var out = [];
|
|
8127
|
+
var i = 0;
|
|
8128
|
+
var len = str.length;
|
|
8129
|
+
while (i < len) {
|
|
8130
|
+
var ch = str[i];
|
|
8131
|
+
if (ch === "'") {
|
|
8132
|
+
// Single-quoted string → emit as double-quoted
|
|
8133
|
+
out.push('"');
|
|
8134
|
+
i++;
|
|
8135
|
+
while (i < len) {
|
|
8136
|
+
var c = str[i];
|
|
8137
|
+
if (c === '\\' && i + 1 < len) {
|
|
8138
|
+
var next = str[i + 1];
|
|
8139
|
+
if (next === "'") {
|
|
8140
|
+
out.push("'"); // \' in input → ' in output
|
|
8141
|
+
} else {
|
|
8142
|
+
out.push('\\');
|
|
8143
|
+
out.push(next);
|
|
8144
|
+
}
|
|
8145
|
+
i += 2;
|
|
8146
|
+
} else if (c === '"') {
|
|
8147
|
+
out.push('\\"');
|
|
8148
|
+
i++;
|
|
8149
|
+
} else if (c === "'") {
|
|
8150
|
+
break;
|
|
8151
|
+
} else {
|
|
8152
|
+
out.push(c);
|
|
8153
|
+
i++;
|
|
8154
|
+
}
|
|
8155
|
+
}
|
|
8156
|
+
out.push('"');
|
|
8157
|
+
i++; // skip closing '
|
|
8158
|
+
} else if (ch === '"') {
|
|
8159
|
+
// Double-quoted string — pass through verbatim
|
|
8160
|
+
out.push(ch);
|
|
8161
|
+
i++;
|
|
8162
|
+
while (i < len) {
|
|
8163
|
+
var c2 = str[i];
|
|
8164
|
+
if (c2 === '\\' && i + 1 < len) {
|
|
8165
|
+
out.push(c2);
|
|
8166
|
+
out.push(str[i + 1]);
|
|
8167
|
+
i += 2;
|
|
8168
|
+
} else {
|
|
8169
|
+
out.push(c2);
|
|
8170
|
+
i++;
|
|
8171
|
+
if (c2 === '"') break;
|
|
8172
|
+
}
|
|
8173
|
+
}
|
|
8174
|
+
} else if (ch === ',') {
|
|
8175
|
+
// Trailing comma check: skip comma if next non-whitespace is } or ]
|
|
8176
|
+
var j = i + 1;
|
|
8177
|
+
while (j < len && (str[j] === ' ' || str[j] === '\t' || str[j] === '\n' || str[j] === '\r')) j++;
|
|
8178
|
+
if (j < len && (str[j] === '}' || str[j] === ']')) {
|
|
8179
|
+
i++; // skip trailing comma
|
|
8180
|
+
} else {
|
|
8181
|
+
out.push(ch);
|
|
8182
|
+
i++;
|
|
8183
|
+
}
|
|
8184
|
+
} else {
|
|
8185
|
+
out.push(ch);
|
|
8186
|
+
i++;
|
|
8187
|
+
}
|
|
8188
|
+
}
|
|
8189
|
+
return JSON.parse(out.join(''));
|
|
8190
|
+
};
|
|
8191
|
+
|
|
8192
|
+
/**
|
|
8193
|
+
* Apply a bwserve protocol message to the DOM.
|
|
8194
|
+
*
|
|
8195
|
+
* Dispatches one of 9 message types:
|
|
8196
|
+
* replace — bw.DOM(target, node)
|
|
8197
|
+
* append — target.appendChild(bw.createDOM(node))
|
|
8198
|
+
* remove — bw.cleanup(target); target.remove()
|
|
8199
|
+
* patch — bw.patch(target, content, attr)
|
|
8200
|
+
* batch — iterate ops, call clientApply for each
|
|
8201
|
+
* message — bw.message(target, action, data)
|
|
8202
|
+
* register — store a named function for later call()
|
|
8203
|
+
* call — invoke a registered or built-in function
|
|
8204
|
+
* exec — execute arbitrary JS (requires allowExec)
|
|
8205
|
+
*
|
|
8206
|
+
* Target resolution:
|
|
8207
|
+
* Starts with '#' or '.' → CSS selector (querySelector)
|
|
8208
|
+
* Otherwise → getElementById, then bw._el fallback
|
|
8209
|
+
*
|
|
8210
|
+
* @param {Object} msg - Protocol message
|
|
8211
|
+
* @returns {boolean} true if the message was applied successfully
|
|
8212
|
+
* @category Server
|
|
8213
|
+
*/
|
|
8214
|
+
bw.clientApply = function (msg) {
|
|
8215
|
+
if (!msg || !msg.type) return false;
|
|
8216
|
+
var type = msg.type;
|
|
8217
|
+
var target = msg.target;
|
|
8218
|
+
if (type === 'replace') {
|
|
8219
|
+
var el = bw._el(target);
|
|
8220
|
+
if (!el) return false;
|
|
8221
|
+
bw.DOM(el, msg.node);
|
|
8222
|
+
return true;
|
|
8223
|
+
} else if (type === 'patch') {
|
|
8224
|
+
var patched = bw.patch(target, msg.content, msg.attr);
|
|
8225
|
+
return patched !== null;
|
|
8226
|
+
} else if (type === 'append') {
|
|
8227
|
+
var parent = bw._el(target);
|
|
8228
|
+
if (!parent) return false;
|
|
8229
|
+
var child = bw.createDOM(msg.node);
|
|
8230
|
+
parent.appendChild(child);
|
|
8231
|
+
return true;
|
|
8232
|
+
} else if (type === 'remove') {
|
|
8233
|
+
var toRemove = bw._el(target);
|
|
8234
|
+
if (!toRemove) return false;
|
|
8235
|
+
if (_is(bw.cleanup, 'function')) bw.cleanup(toRemove);
|
|
8236
|
+
toRemove.remove();
|
|
8237
|
+
return true;
|
|
8238
|
+
} else if (type === 'batch') {
|
|
8239
|
+
if (!_isA(msg.ops)) return false;
|
|
8240
|
+
var allOk = true;
|
|
8241
|
+
msg.ops.forEach(function (op) {
|
|
8242
|
+
if (!bw.clientApply(op)) allOk = false;
|
|
8243
|
+
});
|
|
8244
|
+
return allOk;
|
|
8245
|
+
} else if (type === 'message') {
|
|
8246
|
+
return bw.message(msg.target, msg.action, msg.data);
|
|
8247
|
+
} else if (type === 'register') {
|
|
8248
|
+
if (!msg.name || !msg.body) return false;
|
|
8249
|
+
try {
|
|
8250
|
+
bw._clientFunctions[msg.name] = new Function('return ' + msg.body)();
|
|
8251
|
+
return true;
|
|
8252
|
+
} catch (e) {
|
|
8253
|
+
_ce('[bw] register error:', msg.name, e);
|
|
8254
|
+
return false;
|
|
8255
|
+
}
|
|
8256
|
+
} else if (type === 'call') {
|
|
8257
|
+
if (!msg.name) return false;
|
|
8258
|
+
var fn = bw._clientFunctions[msg.name] || bw._builtinClientFunctions[msg.name];
|
|
8259
|
+
if (!_is(fn, 'function')) return false;
|
|
8260
|
+
try {
|
|
8261
|
+
var args = _isA(msg.args) ? msg.args : [];
|
|
8262
|
+
fn.apply(null, args);
|
|
8263
|
+
return true;
|
|
8264
|
+
} catch (e) {
|
|
8265
|
+
_ce('[bw] call error:', msg.name, e);
|
|
8266
|
+
return false;
|
|
8267
|
+
}
|
|
8268
|
+
} else if (type === 'exec') {
|
|
8269
|
+
if (!bw._allowExec) {
|
|
8270
|
+
_cw('[bw] exec rejected: allowExec is not enabled');
|
|
8271
|
+
return false;
|
|
8272
|
+
}
|
|
8273
|
+
if (!msg.code) return false;
|
|
8274
|
+
try {
|
|
8275
|
+
new Function(msg.code)();
|
|
8276
|
+
return true;
|
|
8277
|
+
} catch (e) {
|
|
8278
|
+
_ce('[bw] exec error:', e);
|
|
8279
|
+
return false;
|
|
8280
|
+
}
|
|
8281
|
+
}
|
|
8282
|
+
return false;
|
|
8283
|
+
};
|
|
8284
|
+
|
|
8285
|
+
/**
|
|
8286
|
+
* Connect to a bwserve SSE endpoint and apply protocol messages automatically.
|
|
8287
|
+
*
|
|
8288
|
+
* Returns a connection object with sendAction(), on(), and close() methods.
|
|
8289
|
+
*
|
|
8290
|
+
* @param {string} url - SSE endpoint URL (e.g., '/__bw/events/client-1')
|
|
8291
|
+
* @param {Object} [opts] - Connection options
|
|
8292
|
+
* @param {string} [opts.transport='sse'] - Transport type: 'sse' (default) or 'poll'
|
|
8293
|
+
* @param {number} [opts.interval=2000] - Poll interval in ms (only for 'poll' transport)
|
|
8294
|
+
* @param {string} [opts.actionUrl] - POST endpoint for actions (default: derived from url)
|
|
8295
|
+
* @param {boolean} [opts.reconnect=true] - Auto-reconnect on disconnect
|
|
8296
|
+
* @param {boolean} [opts.allowExec=false] - Enable exec message type (arbitrary JS execution)
|
|
8297
|
+
* @param {Function} [opts.onStatus] - Status callback: 'connecting'|'connected'|'disconnected'
|
|
8298
|
+
* @param {Function} [opts.onMessage] - Raw message callback (before clientApply)
|
|
8299
|
+
* @returns {Object} Connection object { sendAction, on, close, status }
|
|
8300
|
+
* @category Server
|
|
8301
|
+
*/
|
|
8302
|
+
bw.clientConnect = function (url, opts) {
|
|
8303
|
+
opts = opts || {};
|
|
8304
|
+
var transport = opts.transport || 'sse';
|
|
8305
|
+
var actionUrl = opts.actionUrl || url.replace(/\/events\//, '/action/');
|
|
8306
|
+
var reconnect = opts.reconnect !== false;
|
|
8307
|
+
var onStatus = opts.onStatus || function () {};
|
|
8308
|
+
var onMessage = opts.onMessage || null;
|
|
8309
|
+
var handlers = {};
|
|
8310
|
+
// Set the global allowExec flag from connection options
|
|
8311
|
+
bw._allowExec = !!opts.allowExec;
|
|
8312
|
+
var conn = {
|
|
8313
|
+
status: 'connecting',
|
|
8314
|
+
_es: null,
|
|
8315
|
+
_pollTimer: null
|
|
8316
|
+
};
|
|
8317
|
+
function setStatus(s) {
|
|
8318
|
+
conn.status = s;
|
|
8319
|
+
onStatus(s);
|
|
8320
|
+
}
|
|
8321
|
+
function handleMessage(data) {
|
|
8322
|
+
try {
|
|
8323
|
+
var msg = _is(data, 'string') ? bw.clientParse(data) : data;
|
|
8324
|
+
if (onMessage) onMessage(msg);
|
|
8325
|
+
if (handlers.message) handlers.message(msg);
|
|
8326
|
+
bw.clientApply(msg);
|
|
8327
|
+
} catch (e) {
|
|
8328
|
+
if (handlers.error) handlers.error(e);
|
|
8329
|
+
}
|
|
8330
|
+
}
|
|
8331
|
+
if (transport === 'sse' && typeof EventSource !== 'undefined') {
|
|
8332
|
+
setStatus('connecting');
|
|
8333
|
+
var es = new EventSource(url);
|
|
8334
|
+
conn._es = es;
|
|
8335
|
+
es.onopen = function () {
|
|
8336
|
+
setStatus('connected');
|
|
8337
|
+
if (handlers.open) handlers.open();
|
|
8338
|
+
};
|
|
8339
|
+
es.onmessage = function (e) {
|
|
8340
|
+
handleMessage(e.data);
|
|
8341
|
+
};
|
|
8342
|
+
es.onerror = function () {
|
|
8343
|
+
if (conn.status === 'connected') {
|
|
8344
|
+
setStatus('disconnected');
|
|
8345
|
+
}
|
|
8346
|
+
if (handlers.error) handlers.error(new Error('SSE connection error'));
|
|
8347
|
+
if (!reconnect) {
|
|
8348
|
+
es.close();
|
|
8349
|
+
}
|
|
8350
|
+
// EventSource auto-reconnects by default when reconnect=true
|
|
8351
|
+
};
|
|
8352
|
+
} else if (transport === 'poll') {
|
|
8353
|
+
var interval = opts.interval || 2000;
|
|
8354
|
+
setStatus('connected');
|
|
8355
|
+
conn._pollTimer = setInterval(function () {
|
|
8356
|
+
fetch(url).then(function (r) {
|
|
8357
|
+
return r.json();
|
|
8358
|
+
}).then(function (msgs) {
|
|
8359
|
+
if (_isA(msgs)) {
|
|
8360
|
+
msgs.forEach(handleMessage);
|
|
8361
|
+
} else if (msgs && msgs.type) {
|
|
8362
|
+
handleMessage(msgs);
|
|
8363
|
+
}
|
|
8364
|
+
})["catch"](function (e) {
|
|
8365
|
+
if (handlers.error) handlers.error(e);
|
|
8366
|
+
});
|
|
8367
|
+
}, interval);
|
|
8368
|
+
}
|
|
8369
|
+
|
|
8370
|
+
/**
|
|
8371
|
+
* Send an action to the server via POST.
|
|
8372
|
+
* @param {string} action - Action name
|
|
8373
|
+
* @param {Object} [data] - Action payload
|
|
8374
|
+
*/
|
|
8375
|
+
conn.sendAction = function (action, data) {
|
|
8376
|
+
var body = JSON.stringify({
|
|
8377
|
+
type: 'action',
|
|
8378
|
+
action: action,
|
|
8379
|
+
data: data || {}
|
|
8380
|
+
});
|
|
8381
|
+
fetch(actionUrl, {
|
|
8382
|
+
method: 'POST',
|
|
8383
|
+
headers: {
|
|
8384
|
+
'Content-Type': 'application/json'
|
|
8385
|
+
},
|
|
8386
|
+
body: body
|
|
8387
|
+
})["catch"](function (e) {
|
|
8388
|
+
if (handlers.error) handlers.error(e);
|
|
8389
|
+
});
|
|
8390
|
+
};
|
|
8391
|
+
|
|
8392
|
+
/**
|
|
8393
|
+
* Register an event handler.
|
|
8394
|
+
* @param {string} event - 'open'|'message'|'error'|'close'
|
|
8395
|
+
* @param {Function} handler
|
|
8396
|
+
*/
|
|
8397
|
+
conn.on = function (event, handler) {
|
|
8398
|
+
handlers[event] = handler;
|
|
8399
|
+
return conn;
|
|
8400
|
+
};
|
|
8401
|
+
|
|
8402
|
+
/**
|
|
8403
|
+
* Close the connection.
|
|
8404
|
+
*/
|
|
8405
|
+
conn.close = function () {
|
|
8406
|
+
if (conn._es) {
|
|
8407
|
+
conn._es.close();
|
|
8408
|
+
conn._es = null;
|
|
8409
|
+
}
|
|
8410
|
+
if (conn._pollTimer) {
|
|
8411
|
+
clearInterval(conn._pollTimer);
|
|
8412
|
+
conn._pollTimer = null;
|
|
8413
|
+
}
|
|
8414
|
+
setStatus('disconnected');
|
|
8415
|
+
if (handlers.close) handlers.close();
|
|
8416
|
+
};
|
|
8417
|
+
return conn;
|
|
8418
|
+
};
|
|
8419
|
+
|
|
7646
8420
|
// ===================================================================================
|
|
7647
8421
|
// bw.inspect() — Debug utility
|
|
7648
8422
|
// ===================================================================================
|
|
@@ -7669,20 +8443,20 @@
|
|
|
7669
8443
|
el = target.element;
|
|
7670
8444
|
comp = target;
|
|
7671
8445
|
} else {
|
|
7672
|
-
if (
|
|
8446
|
+
if (_is(target, 'string')) {
|
|
7673
8447
|
el = bw.$(target)[0];
|
|
7674
8448
|
}
|
|
7675
8449
|
if (!el) {
|
|
7676
|
-
|
|
8450
|
+
_cw('bw.inspect: element not found');
|
|
7677
8451
|
return null;
|
|
7678
8452
|
}
|
|
7679
8453
|
comp = el._bwComponentHandle;
|
|
7680
8454
|
}
|
|
7681
8455
|
if (!comp) {
|
|
7682
|
-
|
|
7683
|
-
|
|
7684
|
-
|
|
7685
|
-
|
|
8456
|
+
_cl('bw.inspect: no ComponentHandle on this element');
|
|
8457
|
+
_cl(' Tag:', el.tagName);
|
|
8458
|
+
_cl(' Classes:', el.className);
|
|
8459
|
+
_cl(' _bw_state:', el._bw_state || '(none)');
|
|
7686
8460
|
return null;
|
|
7687
8461
|
}
|
|
7688
8462
|
var deps = comp._bindings.reduce(function (s, b) {
|
|
@@ -7691,13 +8465,13 @@
|
|
|
7691
8465
|
return a.indexOf(v) === i;
|
|
7692
8466
|
});
|
|
7693
8467
|
console.group('Component: ' + comp._bwId);
|
|
7694
|
-
|
|
7695
|
-
|
|
7696
|
-
|
|
7697
|
-
|
|
7698
|
-
|
|
7699
|
-
|
|
7700
|
-
|
|
8468
|
+
_cl('State:', comp._state);
|
|
8469
|
+
_cl('Bindings:', comp._bindings.length, '(deps:', deps, ')');
|
|
8470
|
+
_cl('Methods:', _keys(comp._methods));
|
|
8471
|
+
_cl('Actions:', _keys(comp._actions));
|
|
8472
|
+
_cl('User tag:', comp._userTag || '(none)');
|
|
8473
|
+
_cl('Mounted:', comp.mounted);
|
|
8474
|
+
_cl('Element:', comp.element);
|
|
7701
8475
|
console.groupEnd();
|
|
7702
8476
|
return comp;
|
|
7703
8477
|
};
|
|
@@ -7720,8 +8494,8 @@
|
|
|
7720
8494
|
// Pre-extract all binding expressions
|
|
7721
8495
|
var precompiled = [];
|
|
7722
8496
|
function walkExpressions(node) {
|
|
7723
|
-
if (!
|
|
7724
|
-
if (
|
|
8497
|
+
if (!_is(node, 'object')) return;
|
|
8498
|
+
if (_is(node.c, 'string') && node.c.indexOf('${') >= 0) {
|
|
7725
8499
|
var parsed = bw._parseBindings(node.c);
|
|
7726
8500
|
for (var i = 0; i < parsed.length; i++) {
|
|
7727
8501
|
try {
|
|
@@ -7741,9 +8515,9 @@
|
|
|
7741
8515
|
}
|
|
7742
8516
|
if (node.a) {
|
|
7743
8517
|
for (var key in node.a) {
|
|
7744
|
-
if (
|
|
8518
|
+
if (_hop.call(node.a, key)) {
|
|
7745
8519
|
var v = node.a[key];
|
|
7746
|
-
if (
|
|
8520
|
+
if (_is(v, 'string') && v.indexOf('${') >= 0) {
|
|
7747
8521
|
var parsed2 = bw._parseBindings(v);
|
|
7748
8522
|
for (var j = 0; j < parsed2.length; j++) {
|
|
7749
8523
|
try {
|
|
@@ -7764,9 +8538,9 @@
|
|
|
7764
8538
|
}
|
|
7765
8539
|
}
|
|
7766
8540
|
}
|
|
7767
|
-
if (
|
|
8541
|
+
if (_isA(node.c)) {
|
|
7768
8542
|
for (var k = 0; k < node.c.length; k++) walkExpressions(node.c[k]);
|
|
7769
|
-
} else if (
|
|
8543
|
+
} else if (_is(node.c, 'object') && node.c.t) {
|
|
7770
8544
|
walkExpressions(node.c);
|
|
7771
8545
|
}
|
|
7772
8546
|
}
|
|
@@ -7777,7 +8551,7 @@
|
|
|
7777
8551
|
handle._precompiledBindings = precompiled;
|
|
7778
8552
|
if (initialState) {
|
|
7779
8553
|
for (var k in initialState) {
|
|
7780
|
-
if (
|
|
8554
|
+
if (_hop.call(initialState, k)) {
|
|
7781
8555
|
handle._state[k] = initialState[k];
|
|
7782
8556
|
}
|
|
7783
8557
|
}
|
|
@@ -7811,21 +8585,21 @@
|
|
|
7811
8585
|
minify = _options$minify === void 0 ? false : _options$minify,
|
|
7812
8586
|
_options$pretty = options.pretty,
|
|
7813
8587
|
pretty = _options$pretty === void 0 ? !minify : _options$pretty;
|
|
7814
|
-
if (
|
|
8588
|
+
if (_is(rules, 'string')) return rules;
|
|
7815
8589
|
var css = '';
|
|
7816
8590
|
var indent = pretty ? ' ' : '';
|
|
7817
8591
|
var newline = pretty ? '\n' : '';
|
|
7818
8592
|
var space = pretty ? ' ' : '';
|
|
7819
|
-
if (
|
|
8593
|
+
if (_isA(rules)) {
|
|
7820
8594
|
css = rules.map(function (rule) {
|
|
7821
8595
|
return bw.css(rule, options);
|
|
7822
8596
|
}).join(newline);
|
|
7823
|
-
} else if (
|
|
8597
|
+
} else if (_is(rules, 'object')) {
|
|
7824
8598
|
Object.entries(rules).forEach(function (_ref5) {
|
|
7825
8599
|
var _ref6 = _slicedToArray(_ref5, 2),
|
|
7826
8600
|
selector = _ref6[0],
|
|
7827
8601
|
styles = _ref6[1];
|
|
7828
|
-
if (
|
|
8602
|
+
if (_is(styles, 'object')) {
|
|
7829
8603
|
// Handle @media, @keyframes, @supports — recurse into nested block
|
|
7830
8604
|
if (selector.charAt(0) === '@') {
|
|
7831
8605
|
var inner = bw.css(styles, options);
|
|
@@ -7879,7 +8653,7 @@
|
|
|
7879
8653
|
bw.injectCSS = function (css) {
|
|
7880
8654
|
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
7881
8655
|
if (!bw._isBrowser) {
|
|
7882
|
-
|
|
8656
|
+
_cw('bw.injectCSS requires a DOM environment');
|
|
7883
8657
|
return null;
|
|
7884
8658
|
}
|
|
7885
8659
|
var _options$id = options.id,
|
|
@@ -7897,7 +8671,7 @@
|
|
|
7897
8671
|
}
|
|
7898
8672
|
|
|
7899
8673
|
// Convert CSS if needed
|
|
7900
|
-
var cssStr =
|
|
8674
|
+
var cssStr = _is(css, 'string') ? css : bw.css(css, options);
|
|
7901
8675
|
|
|
7902
8676
|
// Set or append CSS
|
|
7903
8677
|
if (append && styleEl.textContent) {
|
|
@@ -7926,7 +8700,7 @@
|
|
|
7926
8700
|
var result = {};
|
|
7927
8701
|
for (var i = 0; i < arguments.length; i++) {
|
|
7928
8702
|
var arg = arguments[i];
|
|
7929
|
-
if (
|
|
8703
|
+
if (_is(arg, 'object')) Object.assign(result, arg);
|
|
7930
8704
|
}
|
|
7931
8705
|
return result;
|
|
7932
8706
|
};
|
|
@@ -8171,7 +8945,7 @@
|
|
|
8171
8945
|
xl: '1200px'
|
|
8172
8946
|
};
|
|
8173
8947
|
var parts = [];
|
|
8174
|
-
|
|
8948
|
+
_keys(breakpoints).forEach(function (key) {
|
|
8175
8949
|
var rules = {};
|
|
8176
8950
|
if (key === 'base') {
|
|
8177
8951
|
rules[selector] = breakpoints[key];
|
|
@@ -8243,18 +9017,18 @@
|
|
|
8243
9017
|
if (!selector) return [];
|
|
8244
9018
|
|
|
8245
9019
|
// Already an array
|
|
8246
|
-
if (
|
|
9020
|
+
if (_isA(selector)) return selector;
|
|
8247
9021
|
|
|
8248
9022
|
// Single element
|
|
8249
9023
|
if (selector.nodeType) return [selector];
|
|
8250
9024
|
|
|
8251
9025
|
// NodeList or HTMLCollection
|
|
8252
|
-
if (selector.length !== undefined &&
|
|
9026
|
+
if (selector.length !== undefined && !_is(selector, 'string')) {
|
|
8253
9027
|
return Array.from(selector);
|
|
8254
9028
|
}
|
|
8255
9029
|
|
|
8256
9030
|
// CSS selector string
|
|
8257
|
-
if (
|
|
9031
|
+
if (_is(selector, 'string')) {
|
|
8258
9032
|
return Array.from(document.querySelectorAll(selector));
|
|
8259
9033
|
}
|
|
8260
9034
|
return [];
|
|
@@ -8772,7 +9546,7 @@
|
|
|
8772
9546
|
cls = cls.trim();
|
|
8773
9547
|
|
|
8774
9548
|
// Auto-detect columns if not provided
|
|
8775
|
-
var cols = columns || (data.length > 0 ?
|
|
9549
|
+
var cols = columns || (data.length > 0 ? _keys(data[0]).map(function (key) {
|
|
8776
9550
|
return {
|
|
8777
9551
|
key: key,
|
|
8778
9552
|
label: key
|
|
@@ -8791,7 +9565,7 @@
|
|
|
8791
9565
|
var bVal = b[currentSortColumn];
|
|
8792
9566
|
|
|
8793
9567
|
// Handle different types
|
|
8794
|
-
if (
|
|
9568
|
+
if (_is(aVal, 'number') && _is(bVal, 'number')) {
|
|
8795
9569
|
return currentSortDirection === 'asc' ? aVal - bVal : bVal - aVal;
|
|
8796
9570
|
}
|
|
8797
9571
|
|
|
@@ -8915,7 +9689,7 @@
|
|
|
8915
9689
|
headerRow = _config$headerRow === void 0 ? true : _config$headerRow,
|
|
8916
9690
|
columns = config.columns,
|
|
8917
9691
|
rest = _objectWithoutProperties(config, _excluded);
|
|
8918
|
-
if (!
|
|
9692
|
+
if (!_isA(data) || data.length === 0) {
|
|
8919
9693
|
return bw.makeTable(_objectSpread2({
|
|
8920
9694
|
data: [],
|
|
8921
9695
|
columns: columns || []
|
|
@@ -9012,7 +9786,7 @@
|
|
|
9012
9786
|
showLabels = _config$showLabels === void 0 ? true : _config$showLabels,
|
|
9013
9787
|
_config$className2 = config.className,
|
|
9014
9788
|
className = _config$className2 === void 0 ? '' : _config$className2;
|
|
9015
|
-
if (!
|
|
9789
|
+
if (!_isA(data) || data.length === 0) {
|
|
9016
9790
|
return {
|
|
9017
9791
|
t: 'div',
|
|
9018
9792
|
a: {
|
|
@@ -9195,7 +9969,7 @@
|
|
|
9195
9969
|
bw.render = function (element, position, taco) {
|
|
9196
9970
|
var _taco$o4, _taco$o5, _taco$o6;
|
|
9197
9971
|
// Get target element
|
|
9198
|
-
var targetEl =
|
|
9972
|
+
var targetEl = _is(element, 'string') ? document.querySelector(element) : element;
|
|
9199
9973
|
if (!targetEl) {
|
|
9200
9974
|
return {
|
|
9201
9975
|
object_type: 'error',
|
|
@@ -9333,7 +10107,7 @@
|
|
|
9333
10107
|
setContent: function setContent(content) {
|
|
9334
10108
|
this._taco.c = content;
|
|
9335
10109
|
if (this.element) {
|
|
9336
|
-
if (
|
|
10110
|
+
if (_is(content, 'string')) {
|
|
9337
10111
|
this.element.textContent = content;
|
|
9338
10112
|
} else {
|
|
9339
10113
|
// Re-render for complex content
|