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
package/dist/bitwrench.es5.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
/*! bitwrench v2.0.
|
|
1
|
+
/*! bitwrench 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
|
}
|
|
@@ -9038,7 +9081,14 @@
|
|
|
9038
9081
|
function make(type, props) {
|
|
9039
9082
|
var def = BCCL[type];
|
|
9040
9083
|
if (!def) throw new Error('bw.make: unknown component type "' + type + '". Available: ' + Object.keys(BCCL).join(', '));
|
|
9041
|
-
|
|
9084
|
+
var taco = def.make(props || {});
|
|
9085
|
+
if (taco && _typeof(taco) === 'object') {
|
|
9086
|
+
taco._bwFactory = {
|
|
9087
|
+
type: type,
|
|
9088
|
+
props: props || {}
|
|
9089
|
+
};
|
|
9090
|
+
}
|
|
9091
|
+
return taco;
|
|
9042
9092
|
}
|
|
9043
9093
|
|
|
9044
9094
|
var components = /*#__PURE__*/Object.freeze({
|
|
@@ -9151,7 +9201,7 @@
|
|
|
9151
9201
|
__monkey_patch_is_nodejs__: {
|
|
9152
9202
|
_value: 'ignore',
|
|
9153
9203
|
set: function set(x) {
|
|
9154
|
-
this._value =
|
|
9204
|
+
this._value = _is(x, 'boolean') ? x : 'ignore';
|
|
9155
9205
|
},
|
|
9156
9206
|
get: function get() {
|
|
9157
9207
|
return this._value;
|
|
@@ -9199,6 +9249,76 @@
|
|
|
9199
9249
|
configurable: true
|
|
9200
9250
|
});
|
|
9201
9251
|
|
|
9252
|
+
// ── Internal aliases ─────────────────────────────────────────────────────
|
|
9253
|
+
// Short names for frequently-used builtins and internal methods.
|
|
9254
|
+
// Same pattern as v1 (_to = bw.typeOf, etc.).
|
|
9255
|
+
//
|
|
9256
|
+
// Why: Terser can't shorten global property chains (console.warn,
|
|
9257
|
+
// Object.prototype.hasOwnProperty, Array.isArray, document.createElement)
|
|
9258
|
+
// because it can't prove they're side-effect-free. We can, so we alias
|
|
9259
|
+
// them here. Each alias saves bytes in the minified output, and the short
|
|
9260
|
+
// names also reduce visual noise in the hot paths (binding pipeline,
|
|
9261
|
+
// createDOM, etc.).
|
|
9262
|
+
//
|
|
9263
|
+
// Alias Target Sites
|
|
9264
|
+
// ───────── ────────────────────────────────────── ─────
|
|
9265
|
+
// _hop Object.prototype.hasOwnProperty 15
|
|
9266
|
+
// _isA Array.isArray 25
|
|
9267
|
+
// _keys Object.keys 7
|
|
9268
|
+
// _to bw.typeOf (type string) 26
|
|
9269
|
+
// _is type check boolean: _is(x,'string') ~50
|
|
9270
|
+
// _cw console.warn 8
|
|
9271
|
+
// _cl console.log 11
|
|
9272
|
+
// _ce console.error 4
|
|
9273
|
+
// _chp ComponentHandle.prototype 28 (defined after constructor)
|
|
9274
|
+
//
|
|
9275
|
+
// Note: document.createElement etc. are NOT aliased because they require
|
|
9276
|
+
// `this === document` and .bind() would add overhead on every call.
|
|
9277
|
+
// Console aliases use thin wrappers (not direct refs) so test monkey-
|
|
9278
|
+
// patching of console.warn/log/error continues to work.
|
|
9279
|
+
//
|
|
9280
|
+
// `typeof x` for UNDECLARED globals (window, document, process, require,
|
|
9281
|
+
// EventSource, navigator, Promise, __filename, import.meta) MUST stay as
|
|
9282
|
+
// raw `typeof` — calling _to(x) when x doesn't exist throws ReferenceError.
|
|
9283
|
+
//
|
|
9284
|
+
// ── v1 functional type helpers (kept for reference, not currently used) ──
|
|
9285
|
+
// _toa(x, type, trueVal, falseVal) — bw.typeAssign:
|
|
9286
|
+
// returns trueVal if _to(x)===type, else falseVal.
|
|
9287
|
+
// Replaces: (typeof x === 'string') ? A : B → _toa(x,'string',A,B)
|
|
9288
|
+
// _toc(x, type, trueVal, falseVal) — bw.typeConvert:
|
|
9289
|
+
// same as _toa but if trueVal/falseVal are functions, calls them with x.
|
|
9290
|
+
// Replaces: typeof x === 'string' ? fn(x) : default → _toc(x,'string',fn,default)
|
|
9291
|
+
// Uncomment if pattern frequency justifies them:
|
|
9292
|
+
// var _toa = function(x, t, y, n) { return _to(x) === t ? y : n; };
|
|
9293
|
+
// 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); };
|
|
9294
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
9295
|
+
var _hop = Object.prototype.hasOwnProperty;
|
|
9296
|
+
var _isA = Array.isArray;
|
|
9297
|
+
var _keys = Object.keys;
|
|
9298
|
+
var _to = typeOf; // imported from bitwrench-utils.js
|
|
9299
|
+
var _is = function _is(x, t) {
|
|
9300
|
+
var r = _to(x);
|
|
9301
|
+
return r === t || r.toLowerCase() === t;
|
|
9302
|
+
};
|
|
9303
|
+
// Console aliases use thin wrappers (not direct references) so that test
|
|
9304
|
+
// code can monkey-patch console.warn/log/error and the patches take effect.
|
|
9305
|
+
var _cw = function _cw() {
|
|
9306
|
+
console.warn.apply(console, arguments);
|
|
9307
|
+
};
|
|
9308
|
+
var _cl = function _cl() {
|
|
9309
|
+
console.log.apply(console, arguments);
|
|
9310
|
+
};
|
|
9311
|
+
var _ce = function _ce() {
|
|
9312
|
+
console.error.apply(console, arguments);
|
|
9313
|
+
};
|
|
9314
|
+
|
|
9315
|
+
/**
|
|
9316
|
+
* Debug flag. When true, emits console.warn for silent binding failures
|
|
9317
|
+
* (missing paths, null refs, auto-created intermediate objects).
|
|
9318
|
+
* @type {boolean}
|
|
9319
|
+
*/
|
|
9320
|
+
bw.debug = false;
|
|
9321
|
+
|
|
9202
9322
|
/**
|
|
9203
9323
|
* Lazy-resolve Node.js `fs` module.
|
|
9204
9324
|
* Tries require('fs') first (available in CJS/UMD Node.js builds),
|
|
@@ -9348,7 +9468,7 @@
|
|
|
9348
9468
|
*/
|
|
9349
9469
|
bw._el = function (id) {
|
|
9350
9470
|
// Pass-through for DOM elements
|
|
9351
|
-
if (
|
|
9471
|
+
if (!_is(id, 'string')) return id || null;
|
|
9352
9472
|
if (!id) return null;
|
|
9353
9473
|
if (!bw._isBrowser) return null;
|
|
9354
9474
|
|
|
@@ -9443,7 +9563,7 @@
|
|
|
9443
9563
|
* // => '<b>Hello</b> & "world"'
|
|
9444
9564
|
*/
|
|
9445
9565
|
bw.escapeHTML = function (str) {
|
|
9446
|
-
if (
|
|
9566
|
+
if (!_is(str, 'string')) return '';
|
|
9447
9567
|
var escapeMap = {
|
|
9448
9568
|
'&': '&',
|
|
9449
9569
|
'<': '<',
|
|
@@ -9520,7 +9640,7 @@
|
|
|
9520
9640
|
}
|
|
9521
9641
|
|
|
9522
9642
|
// Handle arrays of TACOs
|
|
9523
|
-
if (
|
|
9643
|
+
if (_isA(taco)) {
|
|
9524
9644
|
return taco.map(function (t) {
|
|
9525
9645
|
return bw.html(t, options);
|
|
9526
9646
|
}).join('');
|
|
@@ -9543,17 +9663,17 @@
|
|
|
9543
9663
|
if (taco && taco._bwEach && options.state) {
|
|
9544
9664
|
var eachExpr = taco.expr.replace(/^\$\{|\}$/g, '');
|
|
9545
9665
|
var arr = bw._evaluatePath(options.state, eachExpr);
|
|
9546
|
-
if (!
|
|
9666
|
+
if (!_isA(arr)) return '';
|
|
9547
9667
|
return arr.map(function (item, idx) {
|
|
9548
9668
|
return bw.html(taco.factory(item, idx), options);
|
|
9549
9669
|
}).join('');
|
|
9550
9670
|
}
|
|
9551
9671
|
|
|
9552
9672
|
// Handle primitives and non-TACO objects
|
|
9553
|
-
if (
|
|
9673
|
+
if (!_is(taco, 'object') || !taco.t) {
|
|
9554
9674
|
var str = options.raw ? String(taco) : bw.escapeHTML(String(taco));
|
|
9555
9675
|
// Resolve template bindings if state provided
|
|
9556
|
-
if (options.state &&
|
|
9676
|
+
if (options.state && _is(str, 'string') && str.indexOf('${') >= 0) {
|
|
9557
9677
|
str = bw._resolveTemplate(str, options.state, !!options.compile);
|
|
9558
9678
|
}
|
|
9559
9679
|
return str;
|
|
@@ -9578,9 +9698,17 @@
|
|
|
9578
9698
|
// Skip null, undefined, false
|
|
9579
9699
|
if (value == null || value === false) continue;
|
|
9580
9700
|
|
|
9581
|
-
//
|
|
9582
|
-
if (key.startsWith('on'))
|
|
9583
|
-
|
|
9701
|
+
// Serialize event handlers via funcRegister
|
|
9702
|
+
if (key.startsWith('on')) {
|
|
9703
|
+
if (_is(value, 'function')) {
|
|
9704
|
+
var fnId = bw.funcRegister(value);
|
|
9705
|
+
attrStr += ' ' + key + '="' + bw.funcGetDispatchStr(fnId, 'event') + '"';
|
|
9706
|
+
} else if (_is(value, 'string')) {
|
|
9707
|
+
attrStr += ' ' + key + '="' + bw.escapeHTML(value) + '"';
|
|
9708
|
+
}
|
|
9709
|
+
continue;
|
|
9710
|
+
}
|
|
9711
|
+
if (key === 'style' && _is(value, 'object')) {
|
|
9584
9712
|
// Convert style object to string
|
|
9585
9713
|
var styleStr = Object.entries(value).filter(function (_ref) {
|
|
9586
9714
|
var _ref2 = _slicedToArray(_ref, 2),
|
|
@@ -9597,7 +9725,7 @@
|
|
|
9597
9725
|
}
|
|
9598
9726
|
} else if (key === 'class') {
|
|
9599
9727
|
// Handle class as array or string
|
|
9600
|
-
var classStr =
|
|
9728
|
+
var classStr = _isA(value) ? value.filter(Boolean).join(' ') : String(value);
|
|
9601
9729
|
if (classStr) {
|
|
9602
9730
|
attrStr += " class=\"".concat(bw.escapeHTML(classStr), "\"");
|
|
9603
9731
|
}
|
|
@@ -9633,12 +9761,184 @@
|
|
|
9633
9761
|
// Process content recursively
|
|
9634
9762
|
var contentStr = content != null ? bw.html(content, options) : '';
|
|
9635
9763
|
// Resolve template bindings in content if state provided
|
|
9636
|
-
if (options.state &&
|
|
9764
|
+
if (options.state && _is(contentStr, 'string') && contentStr.indexOf('${') >= 0) {
|
|
9637
9765
|
contentStr = bw._resolveTemplate(contentStr, options.state, !!options.compile);
|
|
9638
9766
|
}
|
|
9639
9767
|
return "<".concat(tag).concat(attrStr, ">").concat(contentStr, "</").concat(tag, ">");
|
|
9640
9768
|
};
|
|
9641
9769
|
|
|
9770
|
+
/**
|
|
9771
|
+
* Generate a complete, self-contained HTML document from TACO content.
|
|
9772
|
+
*
|
|
9773
|
+
* Produces a full `<!DOCTYPE html>` page with configurable runtime injection,
|
|
9774
|
+
* func registry emission (so serialized event handlers work), optional theme,
|
|
9775
|
+
* and extra head elements. Designed for static site generation, offline/airgapped
|
|
9776
|
+
* use, and the "static site that isn't static" workflow.
|
|
9777
|
+
*
|
|
9778
|
+
* @param {Object} [opts={}] - Page options
|
|
9779
|
+
* @param {Object|string|Array} [opts.body=''] - Body content: TACO, string, or array
|
|
9780
|
+
* @param {string} [opts.title='bitwrench'] - Page title
|
|
9781
|
+
* @param {Object} [opts.state] - State for ${expr} resolution in bw.html()
|
|
9782
|
+
* @param {string} [opts.runtime='shim'] - Runtime level: 'inline'|'cdn'|'shim'|'none'
|
|
9783
|
+
* @param {string} [opts.css=''] - Additional CSS for <style> block
|
|
9784
|
+
* @param {string|Object} [opts.theme=null] - Theme preset name or config object
|
|
9785
|
+
* @param {Array} [opts.head=[]] - Extra TACO elements rendered into <head>
|
|
9786
|
+
* @param {string} [opts.favicon=''] - Favicon URL
|
|
9787
|
+
* @param {string} [opts.lang='en'] - HTML lang attribute
|
|
9788
|
+
* @returns {string} Complete HTML document string
|
|
9789
|
+
* @category DOM Generation
|
|
9790
|
+
* @see bw.html
|
|
9791
|
+
* @example
|
|
9792
|
+
* bw.htmlPage({
|
|
9793
|
+
* title: 'My App',
|
|
9794
|
+
* body: { t: 'h1', c: 'Hello World' },
|
|
9795
|
+
* runtime: 'shim'
|
|
9796
|
+
* })
|
|
9797
|
+
*/
|
|
9798
|
+
bw.htmlPage = function (opts) {
|
|
9799
|
+
opts = opts || {};
|
|
9800
|
+
var title = opts.title || 'bitwrench';
|
|
9801
|
+
var body = opts.body || '';
|
|
9802
|
+
var state = opts.state || undefined;
|
|
9803
|
+
var runtime = opts.runtime || 'shim';
|
|
9804
|
+
var css = opts.css || '';
|
|
9805
|
+
var theme = opts.theme || null;
|
|
9806
|
+
var headExtra = opts.head || [];
|
|
9807
|
+
var favicon = opts.favicon || '';
|
|
9808
|
+
var lang = opts.lang || 'en';
|
|
9809
|
+
|
|
9810
|
+
// Snapshot funcRegistry counter before rendering
|
|
9811
|
+
var fnCounterBefore = bw._fnIDCounter;
|
|
9812
|
+
|
|
9813
|
+
// Render body content
|
|
9814
|
+
var bodyHTML = '';
|
|
9815
|
+
if (_is(body, 'string')) {
|
|
9816
|
+
bodyHTML = body;
|
|
9817
|
+
} else {
|
|
9818
|
+
var htmlOpts = {};
|
|
9819
|
+
if (state) htmlOpts.state = state;
|
|
9820
|
+
bodyHTML = bw.html(body, htmlOpts);
|
|
9821
|
+
}
|
|
9822
|
+
|
|
9823
|
+
// Collect functions registered during this render
|
|
9824
|
+
var fnCounterAfter = bw._fnIDCounter;
|
|
9825
|
+
var registryEntries = '';
|
|
9826
|
+
for (var i = fnCounterBefore; i < fnCounterAfter; i++) {
|
|
9827
|
+
var fnKey = 'bw_fn_' + i;
|
|
9828
|
+
if (bw._fnRegistry[fnKey]) {
|
|
9829
|
+
registryEntries += 'bw._fnRegistry[\'' + fnKey + '\']=' + bw._fnRegistry[fnKey].toString() + ';\n';
|
|
9830
|
+
}
|
|
9831
|
+
}
|
|
9832
|
+
|
|
9833
|
+
// Build runtime script for <head>
|
|
9834
|
+
var runtimeHead = '';
|
|
9835
|
+
if (runtime === 'inline') {
|
|
9836
|
+
// Read UMD bundle synchronously if in Node.js
|
|
9837
|
+
var umdSource = null;
|
|
9838
|
+
if (bw._isNode) {
|
|
9839
|
+
try {
|
|
9840
|
+
var fs = typeof require === 'function' ? require('fs') : null;
|
|
9841
|
+
var pathMod = typeof require === 'function' ? require('path') : null;
|
|
9842
|
+
if (fs && pathMod) {
|
|
9843
|
+
// Resolve dist/ relative to this source file
|
|
9844
|
+
var srcDir = '';
|
|
9845
|
+
try {
|
|
9846
|
+
srcDir = pathMod.dirname(typeof __filename !== 'undefined' ? __filename : '');
|
|
9847
|
+
} catch (e2) {/* ESM: __filename not available */}
|
|
9848
|
+
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.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.es5.js', document.baseURI).href))) {
|
|
9849
|
+
var url = typeof require === 'function' ? require('url') : null;
|
|
9850
|
+
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.es5.js', document.baseURI).href))));
|
|
9851
|
+
}
|
|
9852
|
+
if (srcDir) {
|
|
9853
|
+
var distPath = pathMod.resolve(srcDir, '../dist/bitwrench.umd.min.js');
|
|
9854
|
+
umdSource = fs.readFileSync(distPath, 'utf8');
|
|
9855
|
+
}
|
|
9856
|
+
}
|
|
9857
|
+
} catch (e) {/* fall through */}
|
|
9858
|
+
}
|
|
9859
|
+
if (umdSource) {
|
|
9860
|
+
runtimeHead = '<script>' + umdSource + '</script>';
|
|
9861
|
+
} else {
|
|
9862
|
+
// Fallback to shim in browser or if dist not available
|
|
9863
|
+
runtimeHead = '<script>' + bw._FUNC_REGISTRY_SHIM + '</script>';
|
|
9864
|
+
}
|
|
9865
|
+
} else if (runtime === 'cdn') {
|
|
9866
|
+
runtimeHead = '<script src="https://cdn.jsdelivr.net/npm/bitwrench@2/dist/bitwrench.umd.min.js"></script>';
|
|
9867
|
+
} else if (runtime === 'shim') {
|
|
9868
|
+
runtimeHead = '<script>' + bw._FUNC_REGISTRY_SHIM + '</script>';
|
|
9869
|
+
}
|
|
9870
|
+
// runtime === 'none' → empty
|
|
9871
|
+
|
|
9872
|
+
// Theme CSS
|
|
9873
|
+
var themeCSS = '';
|
|
9874
|
+
if (theme) {
|
|
9875
|
+
var themeConfig = _is(theme, 'string') ? THEME_PRESETS[theme.toLowerCase()] || null : theme;
|
|
9876
|
+
if (themeConfig) {
|
|
9877
|
+
var themeResult = bw.generateTheme('', Object.assign({}, themeConfig, {
|
|
9878
|
+
inject: false
|
|
9879
|
+
}));
|
|
9880
|
+
themeCSS = themeResult.css;
|
|
9881
|
+
}
|
|
9882
|
+
}
|
|
9883
|
+
|
|
9884
|
+
// Extra <head> elements
|
|
9885
|
+
var headHTML = '';
|
|
9886
|
+
if (_isA(headExtra) && headExtra.length > 0) {
|
|
9887
|
+
headHTML = headExtra.map(function (el) {
|
|
9888
|
+
return bw.html(el);
|
|
9889
|
+
}).join('\n');
|
|
9890
|
+
}
|
|
9891
|
+
|
|
9892
|
+
// Favicon
|
|
9893
|
+
var faviconTag = '';
|
|
9894
|
+
if (favicon) {
|
|
9895
|
+
var safeFavicon = favicon.replace(/[&<>"']/g, function (c) {
|
|
9896
|
+
return {
|
|
9897
|
+
'&': '&',
|
|
9898
|
+
'<': '<',
|
|
9899
|
+
'>': '>',
|
|
9900
|
+
'"': '"',
|
|
9901
|
+
"'": '''
|
|
9902
|
+
}[c];
|
|
9903
|
+
});
|
|
9904
|
+
faviconTag = '<link rel="icon" href="' + safeFavicon + '">';
|
|
9905
|
+
}
|
|
9906
|
+
|
|
9907
|
+
// Escaped title
|
|
9908
|
+
var safeTitle = bw.escapeHTML(title);
|
|
9909
|
+
|
|
9910
|
+
// Combine all CSS
|
|
9911
|
+
var allCSS = (themeCSS ? themeCSS + '\n' : '') + css;
|
|
9912
|
+
|
|
9913
|
+
// Body-end script: registry entries + optional loadDefaultStyles
|
|
9914
|
+
var bodyEndScript = '';
|
|
9915
|
+
var bodyEndParts = [];
|
|
9916
|
+
if (registryEntries) {
|
|
9917
|
+
bodyEndParts.push(registryEntries);
|
|
9918
|
+
}
|
|
9919
|
+
if (runtime === 'inline' || runtime === 'cdn') {
|
|
9920
|
+
bodyEndParts.push('if(typeof bw!=="undefined"){bw.loadDefaultStyles();}');
|
|
9921
|
+
}
|
|
9922
|
+
if (bodyEndParts.length > 0) {
|
|
9923
|
+
bodyEndScript = '<script>\n' + bodyEndParts.join('\n') + '\n</script>';
|
|
9924
|
+
}
|
|
9925
|
+
|
|
9926
|
+
// Assemble document
|
|
9927
|
+
var parts = ['<!DOCTYPE html>', '<html lang="' + lang + '">', '<head>', '<meta charset="UTF-8">', '<meta name="viewport" content="width=device-width, initial-scale=1">'];
|
|
9928
|
+
parts.push('<title>' + safeTitle + '</title>');
|
|
9929
|
+
if (faviconTag) parts.push(faviconTag);
|
|
9930
|
+
if (runtimeHead) parts.push(runtimeHead);
|
|
9931
|
+
if (headHTML) parts.push(headHTML);
|
|
9932
|
+
if (allCSS) parts.push('<style>' + allCSS + '</style>');
|
|
9933
|
+
parts.push('</head>');
|
|
9934
|
+
parts.push('<body>');
|
|
9935
|
+
parts.push(bodyHTML);
|
|
9936
|
+
if (bodyEndScript) parts.push(bodyEndScript);
|
|
9937
|
+
parts.push('</body>');
|
|
9938
|
+
parts.push('</html>');
|
|
9939
|
+
return parts.join('\n');
|
|
9940
|
+
};
|
|
9941
|
+
|
|
9642
9942
|
/**
|
|
9643
9943
|
* Create a live DOM element from a TACO object (browser only).
|
|
9644
9944
|
*
|
|
@@ -9684,7 +9984,7 @@
|
|
|
9684
9984
|
}
|
|
9685
9985
|
|
|
9686
9986
|
// Handle text nodes
|
|
9687
|
-
if (
|
|
9987
|
+
if (!_is(taco, 'object') || !taco.t) {
|
|
9688
9988
|
return document.createTextNode(String(taco));
|
|
9689
9989
|
}
|
|
9690
9990
|
var tag = taco.t,
|
|
@@ -9703,16 +10003,16 @@
|
|
|
9703
10003
|
key = _Object$entries2$_i[0],
|
|
9704
10004
|
value = _Object$entries2$_i[1];
|
|
9705
10005
|
if (value == null || value === false) continue;
|
|
9706
|
-
if (key === 'style' &&
|
|
10006
|
+
if (key === 'style' && _is(value, 'object')) {
|
|
9707
10007
|
// Apply styles directly
|
|
9708
10008
|
Object.assign(el.style, value);
|
|
9709
10009
|
} else if (key === 'class') {
|
|
9710
10010
|
// Handle class as array or string
|
|
9711
|
-
var classStr =
|
|
10011
|
+
var classStr = _isA(value) ? value.filter(Boolean).join(' ') : String(value);
|
|
9712
10012
|
if (classStr) {
|
|
9713
10013
|
el.className = classStr;
|
|
9714
10014
|
}
|
|
9715
|
-
} else if (key.startsWith('on') &&
|
|
10015
|
+
} else if (key.startsWith('on') && _is(value, 'function')) {
|
|
9716
10016
|
// Event handlers
|
|
9717
10017
|
var eventName = key.slice(2).toLowerCase();
|
|
9718
10018
|
el.addEventListener(eventName, value);
|
|
@@ -9732,7 +10032,7 @@
|
|
|
9732
10032
|
// Children with data-bw_id or id attributes get local refs on the parent,
|
|
9733
10033
|
// so o.render functions can access them without any DOM lookup.
|
|
9734
10034
|
if (content != null) {
|
|
9735
|
-
if (
|
|
10035
|
+
if (_isA(content)) {
|
|
9736
10036
|
content.forEach(function (child) {
|
|
9737
10037
|
if (child != null) {
|
|
9738
10038
|
// Handle ComponentHandle in content arrays (Level 2 children)
|
|
@@ -9752,20 +10052,20 @@
|
|
|
9752
10052
|
if (childEl._bw_refs) {
|
|
9753
10053
|
if (!el._bw_refs) el._bw_refs = {};
|
|
9754
10054
|
for (var rk in childEl._bw_refs) {
|
|
9755
|
-
if (
|
|
10055
|
+
if (_hop.call(childEl._bw_refs, rk)) {
|
|
9756
10056
|
el._bw_refs[rk] = childEl._bw_refs[rk];
|
|
9757
10057
|
}
|
|
9758
10058
|
}
|
|
9759
10059
|
}
|
|
9760
10060
|
}
|
|
9761
10061
|
});
|
|
9762
|
-
} else if (
|
|
10062
|
+
} else if (_is(content, 'object') && content.__bw_raw) {
|
|
9763
10063
|
// Raw HTML content — inject via innerHTML
|
|
9764
10064
|
el.innerHTML = content.v;
|
|
9765
10065
|
} else if (content._bwComponent === true) {
|
|
9766
10066
|
// Single ComponentHandle as content
|
|
9767
10067
|
content.mount(el);
|
|
9768
|
-
} else if (
|
|
10068
|
+
} else if (_is(content, 'object') && content.t) {
|
|
9769
10069
|
var childEl = bw.createDOM(content, options);
|
|
9770
10070
|
el.appendChild(childEl);
|
|
9771
10071
|
var childBwId = content.a ? content.a['data-bw_id'] || content.a.id : null;
|
|
@@ -9776,7 +10076,7 @@
|
|
|
9776
10076
|
if (childEl._bw_refs) {
|
|
9777
10077
|
if (!el._bw_refs) el._bw_refs = {};
|
|
9778
10078
|
for (var rk in childEl._bw_refs) {
|
|
9779
|
-
if (
|
|
10079
|
+
if (_hop.call(childEl._bw_refs, rk)) {
|
|
9780
10080
|
el._bw_refs[rk] = childEl._bw_refs[rk];
|
|
9781
10081
|
}
|
|
9782
10082
|
}
|
|
@@ -9808,7 +10108,7 @@
|
|
|
9808
10108
|
if (opts.render) {
|
|
9809
10109
|
el._bw_render = opts.render;
|
|
9810
10110
|
if (opts.mounted) {
|
|
9811
|
-
|
|
10111
|
+
_cw('bw.createDOM: o.render and o.mounted are mutually exclusive. o.render wins.');
|
|
9812
10112
|
}
|
|
9813
10113
|
|
|
9814
10114
|
// Queue initial render (same timing as mounted)
|
|
@@ -9880,7 +10180,7 @@
|
|
|
9880
10180
|
// Get target element (use cache-backed lookup)
|
|
9881
10181
|
var targetEl = bw._el(target);
|
|
9882
10182
|
if (!targetEl) {
|
|
9883
|
-
|
|
10183
|
+
_ce('bw.DOM: Target element not found:', target);
|
|
9884
10184
|
return null;
|
|
9885
10185
|
}
|
|
9886
10186
|
|
|
@@ -9918,7 +10218,7 @@
|
|
|
9918
10218
|
targetEl.appendChild(taco.element);
|
|
9919
10219
|
}
|
|
9920
10220
|
// Handle arrays
|
|
9921
|
-
else if (
|
|
10221
|
+
else if (_isA(taco)) {
|
|
9922
10222
|
taco.forEach(function (t) {
|
|
9923
10223
|
if (t != null) {
|
|
9924
10224
|
if (t._bwComponent === true) {
|
|
@@ -9953,7 +10253,7 @@
|
|
|
9953
10253
|
bw.compileProps = function (handle) {
|
|
9954
10254
|
var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
9955
10255
|
var compiledProps = {};
|
|
9956
|
-
|
|
10256
|
+
_keys(props).forEach(function (key) {
|
|
9957
10257
|
// Create getter/setter for each prop
|
|
9958
10258
|
Object.defineProperty(compiledProps, key, {
|
|
9959
10259
|
get: function get() {
|
|
@@ -10264,17 +10564,17 @@
|
|
|
10264
10564
|
if (attr) {
|
|
10265
10565
|
// Patch an attribute
|
|
10266
10566
|
el.setAttribute(attr, String(content));
|
|
10267
|
-
} else if (
|
|
10567
|
+
} else if (_isA(content)) {
|
|
10268
10568
|
// Patch with array of children (strings and/or TACOs)
|
|
10269
10569
|
el.innerHTML = '';
|
|
10270
10570
|
content.forEach(function (item) {
|
|
10271
|
-
if (
|
|
10571
|
+
if (_is(item, 'string') || _is(item, 'number')) {
|
|
10272
10572
|
el.appendChild(document.createTextNode(String(item)));
|
|
10273
10573
|
} else if (item && item.t) {
|
|
10274
10574
|
el.appendChild(bw.createDOM(item));
|
|
10275
10575
|
}
|
|
10276
10576
|
});
|
|
10277
|
-
} else if (
|
|
10577
|
+
} else if (_is(content, 'object') && content.t) {
|
|
10278
10578
|
// Patch with a TACO — replace children
|
|
10279
10579
|
el.innerHTML = '';
|
|
10280
10580
|
el.appendChild(bw.createDOM(content));
|
|
@@ -10305,7 +10605,7 @@
|
|
|
10305
10605
|
bw.patchAll = function (patches) {
|
|
10306
10606
|
var results = {};
|
|
10307
10607
|
for (var id in patches) {
|
|
10308
|
-
if (
|
|
10608
|
+
if (_hop.call(patches, id)) {
|
|
10309
10609
|
results[id] = bw.patch(id, patches[id]);
|
|
10310
10610
|
}
|
|
10311
10611
|
}
|
|
@@ -10402,7 +10702,7 @@
|
|
|
10402
10702
|
snapshot[i].handler(detail);
|
|
10403
10703
|
called++;
|
|
10404
10704
|
} catch (err) {
|
|
10405
|
-
|
|
10705
|
+
_cw('bw.pub: subscriber error on topic "' + topic + '":', err);
|
|
10406
10706
|
}
|
|
10407
10707
|
}
|
|
10408
10708
|
return called;
|
|
@@ -10503,8 +10803,8 @@
|
|
|
10503
10803
|
* @see bw.funcGetDispatchStr
|
|
10504
10804
|
*/
|
|
10505
10805
|
bw.funcRegister = function (fn, name) {
|
|
10506
|
-
if (
|
|
10507
|
-
var fnID =
|
|
10806
|
+
if (!_is(fn, 'function')) return '';
|
|
10807
|
+
var fnID = _is(name, 'string') && name.length > 0 ? name : 'bw_fn_' + bw._fnIDCounter++;
|
|
10508
10808
|
bw._fnRegistry[fnID] = fn;
|
|
10509
10809
|
return fnID;
|
|
10510
10810
|
};
|
|
@@ -10523,8 +10823,8 @@
|
|
|
10523
10823
|
bw.funcGetById = function (name, errFn) {
|
|
10524
10824
|
name = String(name);
|
|
10525
10825
|
if (name in bw._fnRegistry) return bw._fnRegistry[name];
|
|
10526
|
-
return
|
|
10527
|
-
|
|
10826
|
+
return _is(errFn, 'function') ? errFn : function () {
|
|
10827
|
+
_cw('bw.funcGetById: unregistered fn "' + name + '"');
|
|
10528
10828
|
};
|
|
10529
10829
|
};
|
|
10530
10830
|
|
|
@@ -10566,13 +10866,23 @@
|
|
|
10566
10866
|
bw.funcGetRegistry = function () {
|
|
10567
10867
|
var copy = {};
|
|
10568
10868
|
for (var k in bw._fnRegistry) {
|
|
10569
|
-
if (
|
|
10869
|
+
if (_hop.call(bw._fnRegistry, k)) {
|
|
10570
10870
|
copy[k] = bw._fnRegistry[k];
|
|
10571
10871
|
}
|
|
10572
10872
|
}
|
|
10573
10873
|
return copy;
|
|
10574
10874
|
};
|
|
10575
10875
|
|
|
10876
|
+
/**
|
|
10877
|
+
* Minimal runtime shim for funcRegister dispatch in static HTML.
|
|
10878
|
+
* When embedded in a `<script>` tag, provides just enough infrastructure
|
|
10879
|
+
* for `bw.funcGetById()` calls to resolve. The actual function bodies
|
|
10880
|
+
* are emitted separately as `bw._fnRegistry['bw_fn_X'] = ...;` assignments.
|
|
10881
|
+
* @type {string}
|
|
10882
|
+
* @category Function Registry
|
|
10883
|
+
*/
|
|
10884
|
+
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;})();';
|
|
10885
|
+
|
|
10576
10886
|
// ===================================================================================
|
|
10577
10887
|
// Template Binding Utilities
|
|
10578
10888
|
// ===================================================================================
|
|
@@ -10604,7 +10914,10 @@
|
|
|
10604
10914
|
var parts = path.split('.');
|
|
10605
10915
|
var val = state;
|
|
10606
10916
|
for (var i = 0; i < parts.length; i++) {
|
|
10607
|
-
if (val == null)
|
|
10917
|
+
if (val == null) {
|
|
10918
|
+
if (bw.debug) _cw('bw.debug: _evaluatePath — null at key "' + parts[i] + '" in path "' + path + '"');
|
|
10919
|
+
return '';
|
|
10920
|
+
}
|
|
10608
10921
|
val = val[parts[i]];
|
|
10609
10922
|
}
|
|
10610
10923
|
return val == null ? '' : val;
|
|
@@ -10624,7 +10937,7 @@
|
|
|
10624
10937
|
*/
|
|
10625
10938
|
bw._compiledExprs = {};
|
|
10626
10939
|
bw._resolveTemplate = function (str, state, compile) {
|
|
10627
|
-
if (
|
|
10940
|
+
if (!_is(str, 'string') || str.indexOf('${') < 0) return str;
|
|
10628
10941
|
var bindings = bw._parseBindings(str);
|
|
10629
10942
|
if (bindings.length === 0) return str;
|
|
10630
10943
|
var result = '';
|
|
@@ -10647,6 +10960,7 @@
|
|
|
10647
10960
|
try {
|
|
10648
10961
|
val = bw._compiledExprs[b.expr](state);
|
|
10649
10962
|
} catch (e) {
|
|
10963
|
+
if (bw.debug) _cw('bw.debug: _resolveTemplate — Tier 2 eval failed for "${' + b.expr + '}":', e.message);
|
|
10650
10964
|
val = '';
|
|
10651
10965
|
}
|
|
10652
10966
|
} else {
|
|
@@ -10754,7 +11068,7 @@
|
|
|
10754
11068
|
this._state = {};
|
|
10755
11069
|
if (o.state) {
|
|
10756
11070
|
for (var k in o.state) {
|
|
10757
|
-
if (
|
|
11071
|
+
if (_hop.call(o.state, k)) {
|
|
10758
11072
|
this._state[k] = o.state[k];
|
|
10759
11073
|
}
|
|
10760
11074
|
}
|
|
@@ -10763,7 +11077,7 @@
|
|
|
10763
11077
|
this._actions = {};
|
|
10764
11078
|
if (o.actions) {
|
|
10765
11079
|
for (var k2 in o.actions) {
|
|
10766
|
-
if (
|
|
11080
|
+
if (_hop.call(o.actions, k2)) {
|
|
10767
11081
|
this._actions[k2] = o.actions[k2];
|
|
10768
11082
|
}
|
|
10769
11083
|
}
|
|
@@ -10773,7 +11087,7 @@
|
|
|
10773
11087
|
if (o.methods) {
|
|
10774
11088
|
var self = this;
|
|
10775
11089
|
for (var k3 in o.methods) {
|
|
10776
|
-
if (
|
|
11090
|
+
if (_hop.call(o.methods, k3)) {
|
|
10777
11091
|
this._methods[k3] = o.methods[k3];
|
|
10778
11092
|
(function (methodName, methodFn) {
|
|
10779
11093
|
self[methodName] = function () {
|
|
@@ -10806,14 +11120,23 @@
|
|
|
10806
11120
|
this._compile = !!o.compile;
|
|
10807
11121
|
this._bw_refs = {};
|
|
10808
11122
|
this._refCounter = 0;
|
|
11123
|
+
// Child component ownership (Bug #5)
|
|
11124
|
+
this._children = [];
|
|
11125
|
+
this._parent = null;
|
|
11126
|
+
// Factory metadata for BCCL rebuild (Bug #6)
|
|
11127
|
+
this._factory = taco._bwFactory || null;
|
|
10809
11128
|
}
|
|
10810
11129
|
|
|
11130
|
+
// Short alias for ComponentHandle.prototype (see alias block at top of file).
|
|
11131
|
+
// 28 method definitions × 25 chars = ~700B raw savings in minified output.
|
|
11132
|
+
var _chp = ComponentHandle.prototype;
|
|
11133
|
+
|
|
10811
11134
|
// ── State Methods ──
|
|
10812
11135
|
|
|
10813
11136
|
/**
|
|
10814
11137
|
* Get a state value. Dot-path supported: `get('user.name')`
|
|
10815
11138
|
*/
|
|
10816
|
-
|
|
11139
|
+
_chp.get = function (key) {
|
|
10817
11140
|
return bw._evaluatePath(this._state, key);
|
|
10818
11141
|
};
|
|
10819
11142
|
|
|
@@ -10823,12 +11146,13 @@
|
|
|
10823
11146
|
* @param {*} value - New value
|
|
10824
11147
|
* @param {Object} [opts] - Options. `{sync: true}` for immediate flush.
|
|
10825
11148
|
*/
|
|
10826
|
-
|
|
11149
|
+
_chp.set = function (key, value, opts) {
|
|
10827
11150
|
// Dot-path set
|
|
10828
11151
|
var parts = key.split('.');
|
|
10829
11152
|
var obj = this._state;
|
|
10830
11153
|
for (var i = 0; i < parts.length - 1; i++) {
|
|
10831
|
-
if (
|
|
11154
|
+
if (!_is(obj[parts[i]], 'object')) {
|
|
11155
|
+
if (bw.debug) _cw('bw.debug: set() — auto-creating intermediate "' + parts[i] + '" in path "' + key + '"');
|
|
10832
11156
|
obj[parts[i]] = {};
|
|
10833
11157
|
}
|
|
10834
11158
|
obj = obj[parts[i]];
|
|
@@ -10848,10 +11172,10 @@
|
|
|
10848
11172
|
/**
|
|
10849
11173
|
* Get a shallow clone of the full state.
|
|
10850
11174
|
*/
|
|
10851
|
-
|
|
11175
|
+
_chp.getState = function () {
|
|
10852
11176
|
var clone = {};
|
|
10853
11177
|
for (var k in this._state) {
|
|
10854
|
-
if (
|
|
11178
|
+
if (_hop.call(this._state, k)) {
|
|
10855
11179
|
clone[k] = this._state[k];
|
|
10856
11180
|
}
|
|
10857
11181
|
}
|
|
@@ -10863,9 +11187,9 @@
|
|
|
10863
11187
|
* @param {Object} updates - Key-value pairs to merge
|
|
10864
11188
|
* @param {Object} [opts] - Options. `{sync: true}` for immediate flush.
|
|
10865
11189
|
*/
|
|
10866
|
-
|
|
11190
|
+
_chp.setState = function (updates, opts) {
|
|
10867
11191
|
for (var k in updates) {
|
|
10868
|
-
if (
|
|
11192
|
+
if (_hop.call(updates, k)) {
|
|
10869
11193
|
this._state[k] = updates[k];
|
|
10870
11194
|
this._dirtyKeys[k] = true;
|
|
10871
11195
|
}
|
|
@@ -10882,9 +11206,9 @@
|
|
|
10882
11206
|
/**
|
|
10883
11207
|
* Push a value onto an array in state. Clones the array.
|
|
10884
11208
|
*/
|
|
10885
|
-
|
|
11209
|
+
_chp.push = function (key, val) {
|
|
10886
11210
|
var arr = this.get(key);
|
|
10887
|
-
var newArr =
|
|
11211
|
+
var newArr = _isA(arr) ? arr.slice() : [];
|
|
10888
11212
|
newArr.push(val);
|
|
10889
11213
|
this.set(key, newArr);
|
|
10890
11214
|
};
|
|
@@ -10892,9 +11216,9 @@
|
|
|
10892
11216
|
/**
|
|
10893
11217
|
* Splice an array in state. Clones the array.
|
|
10894
11218
|
*/
|
|
10895
|
-
|
|
11219
|
+
_chp.splice = function (key, start, deleteCount) {
|
|
10896
11220
|
var arr = this.get(key);
|
|
10897
|
-
var newArr =
|
|
11221
|
+
var newArr = _isA(arr) ? arr.slice() : [];
|
|
10898
11222
|
var args = [start, deleteCount].concat(Array.prototype.slice.call(arguments, 3));
|
|
10899
11223
|
Array.prototype.splice.apply(newArr, args);
|
|
10900
11224
|
this.set(key, newArr);
|
|
@@ -10902,7 +11226,7 @@
|
|
|
10902
11226
|
|
|
10903
11227
|
// ── Scheduling ──
|
|
10904
11228
|
|
|
10905
|
-
|
|
11229
|
+
_chp._scheduleDirty = function () {
|
|
10906
11230
|
if (!this._scheduled) {
|
|
10907
11231
|
this._scheduled = true;
|
|
10908
11232
|
bw._dirtyComponents.push(this);
|
|
@@ -10917,16 +11241,16 @@
|
|
|
10917
11241
|
* Creates binding descriptors with refIds for targeted DOM updates.
|
|
10918
11242
|
* @private
|
|
10919
11243
|
*/
|
|
10920
|
-
|
|
11244
|
+
_chp._compileBindings = function () {
|
|
10921
11245
|
this._bindings = [];
|
|
10922
11246
|
this._refCounter = 0;
|
|
10923
|
-
var stateKeys =
|
|
11247
|
+
var stateKeys = _keys(this._state);
|
|
10924
11248
|
var self = this;
|
|
10925
11249
|
function walkTaco(taco, path) {
|
|
10926
|
-
if (
|
|
11250
|
+
if (!_is(taco, 'object') || !taco.t) return taco;
|
|
10927
11251
|
|
|
10928
11252
|
// Check content for bindings
|
|
10929
|
-
if (
|
|
11253
|
+
if (_is(taco.c, 'string') && taco.c.indexOf('${') >= 0) {
|
|
10930
11254
|
var refId = 'bw_ref_' + self._refCounter++;
|
|
10931
11255
|
var parsed = bw._parseBindings(taco.c);
|
|
10932
11256
|
var deps = [];
|
|
@@ -10948,10 +11272,10 @@
|
|
|
10948
11272
|
// Check attributes for bindings
|
|
10949
11273
|
if (taco.a) {
|
|
10950
11274
|
for (var attrName in taco.a) {
|
|
10951
|
-
if (!
|
|
11275
|
+
if (!_hop.call(taco.a, attrName)) continue;
|
|
10952
11276
|
if (attrName === 'data-bw_ref') continue;
|
|
10953
11277
|
var attrVal = taco.a[attrName];
|
|
10954
|
-
if (
|
|
11278
|
+
if (_is(attrVal, 'string') && attrVal.indexOf('${') >= 0) {
|
|
10955
11279
|
var refId2 = 'bw_ref_' + self._refCounter++;
|
|
10956
11280
|
var parsed2 = bw._parseBindings(attrVal);
|
|
10957
11281
|
var deps2 = [];
|
|
@@ -10977,9 +11301,34 @@
|
|
|
10977
11301
|
}
|
|
10978
11302
|
|
|
10979
11303
|
// Recurse into children
|
|
10980
|
-
if (
|
|
11304
|
+
if (_isA(taco.c)) {
|
|
10981
11305
|
for (var i = 0; i < taco.c.length; i++) {
|
|
10982
|
-
|
|
11306
|
+
// Wrap string children with ${expr} in a span so patches target the span, not the parent
|
|
11307
|
+
if (_is(taco.c[i], 'string') && taco.c[i].indexOf('${') >= 0) {
|
|
11308
|
+
var mixedRefId = 'bw_ref_' + self._refCounter++;
|
|
11309
|
+
var mixedParsed = bw._parseBindings(taco.c[i]);
|
|
11310
|
+
var mixedDeps = [];
|
|
11311
|
+
for (var mi = 0; mi < mixedParsed.length; mi++) {
|
|
11312
|
+
mixedDeps = mixedDeps.concat(bw._extractDeps(mixedParsed[mi].expr, stateKeys));
|
|
11313
|
+
}
|
|
11314
|
+
self._bindings.push({
|
|
11315
|
+
expr: taco.c[i],
|
|
11316
|
+
type: 'content',
|
|
11317
|
+
refId: mixedRefId,
|
|
11318
|
+
deps: mixedDeps,
|
|
11319
|
+
template: taco.c[i]
|
|
11320
|
+
});
|
|
11321
|
+
// Replace string with a span wrapper so textContent targets the span only
|
|
11322
|
+
taco.c[i] = {
|
|
11323
|
+
t: 'span',
|
|
11324
|
+
a: {
|
|
11325
|
+
'data-bw_ref': mixedRefId,
|
|
11326
|
+
style: 'display:contents'
|
|
11327
|
+
},
|
|
11328
|
+
c: taco.c[i]
|
|
11329
|
+
};
|
|
11330
|
+
}
|
|
11331
|
+
if (_is(taco.c[i], 'object') && taco.c[i].t) {
|
|
10983
11332
|
walkTaco(taco.c[i], path.concat(i));
|
|
10984
11333
|
}
|
|
10985
11334
|
// Handle bw.when/bw.each markers
|
|
@@ -11014,7 +11363,7 @@
|
|
|
11014
11363
|
taco.c[i]._refId = eachRefId;
|
|
11015
11364
|
}
|
|
11016
11365
|
}
|
|
11017
|
-
} else if (
|
|
11366
|
+
} else if (_is(taco.c, 'object') && taco.c.t) {
|
|
11018
11367
|
walkTaco(taco.c, path.concat(0));
|
|
11019
11368
|
}
|
|
11020
11369
|
return taco;
|
|
@@ -11028,7 +11377,7 @@
|
|
|
11028
11377
|
* Build ref map from the live DOM after createDOM.
|
|
11029
11378
|
* @private
|
|
11030
11379
|
*/
|
|
11031
|
-
|
|
11380
|
+
_chp._collectRefs = function () {
|
|
11032
11381
|
this._bw_refs = {};
|
|
11033
11382
|
if (!this.element) return;
|
|
11034
11383
|
var els = this.element.querySelectorAll('[data-bw_ref]');
|
|
@@ -11049,7 +11398,7 @@
|
|
|
11049
11398
|
* Creates DOM, compiles bindings, registers actions, and calls lifecycle hooks.
|
|
11050
11399
|
* @param {Element} parentEl - DOM element to mount into
|
|
11051
11400
|
*/
|
|
11052
|
-
|
|
11401
|
+
_chp.mount = function (parentEl) {
|
|
11053
11402
|
// willMount hook
|
|
11054
11403
|
if (this._hooks.willMount) this._hooks.willMount(this);
|
|
11055
11404
|
|
|
@@ -11071,7 +11420,7 @@
|
|
|
11071
11420
|
// Register named actions in function registry
|
|
11072
11421
|
var self = this;
|
|
11073
11422
|
for (var actionName in this._actions) {
|
|
11074
|
-
if (
|
|
11423
|
+
if (_hop.call(this._actions, actionName)) {
|
|
11075
11424
|
var registeredName = this._bwId + '_' + actionName;
|
|
11076
11425
|
(function (aName) {
|
|
11077
11426
|
bw.funcRegister(function (evt) {
|
|
@@ -11090,6 +11439,11 @@
|
|
|
11090
11439
|
this.element = bw.createDOM(tacoForDOM);
|
|
11091
11440
|
this.element._bwComponentHandle = this;
|
|
11092
11441
|
this.element.setAttribute('data-bw_comp_id', this._bwId);
|
|
11442
|
+
|
|
11443
|
+
// Restore o.render from original TACO (stripped by _tacoForDOM)
|
|
11444
|
+
if (this.taco.o && this.taco.o.render) {
|
|
11445
|
+
this.element._bw_render = this.taco.o.render;
|
|
11446
|
+
}
|
|
11093
11447
|
if (this._userTag) {
|
|
11094
11448
|
this.element.classList.add(this._userTag);
|
|
11095
11449
|
}
|
|
@@ -11104,6 +11458,16 @@
|
|
|
11104
11458
|
this._resolveAndApplyAll();
|
|
11105
11459
|
this.mounted = true;
|
|
11106
11460
|
|
|
11461
|
+
// Scan for child ComponentHandles and link parent/child (Bug #5)
|
|
11462
|
+
var childEls = this.element.querySelectorAll('[data-bw_comp_id]');
|
|
11463
|
+
for (var ci = 0; ci < childEls.length; ci++) {
|
|
11464
|
+
var ch = childEls[ci]._bwComponentHandle;
|
|
11465
|
+
if (ch && ch !== this && !ch._parent) {
|
|
11466
|
+
ch._parent = this;
|
|
11467
|
+
this._children.push(ch);
|
|
11468
|
+
}
|
|
11469
|
+
}
|
|
11470
|
+
|
|
11107
11471
|
// mounted hook (backward compat: fn.length === 2 wraps (el, state))
|
|
11108
11472
|
if (this._hooks.mounted) {
|
|
11109
11473
|
if (this._hooks.mounted.length === 2) {
|
|
@@ -11112,15 +11476,20 @@
|
|
|
11112
11476
|
this._hooks.mounted(this);
|
|
11113
11477
|
}
|
|
11114
11478
|
}
|
|
11479
|
+
|
|
11480
|
+
// Invoke o.render on initial mount (if present)
|
|
11481
|
+
if (this.element._bw_render) {
|
|
11482
|
+
this.element._bw_render(this.element, this._state);
|
|
11483
|
+
}
|
|
11115
11484
|
};
|
|
11116
11485
|
|
|
11117
11486
|
/**
|
|
11118
11487
|
* Prepare TACO for initial render: resolve when/each markers.
|
|
11119
11488
|
* @private
|
|
11120
11489
|
*/
|
|
11121
|
-
|
|
11122
|
-
if (!
|
|
11123
|
-
if (
|
|
11490
|
+
_chp._prepareTaco = function (taco) {
|
|
11491
|
+
if (!_is(taco, 'object')) return;
|
|
11492
|
+
if (_isA(taco.c)) {
|
|
11124
11493
|
for (var i = taco.c.length - 1; i >= 0; i--) {
|
|
11125
11494
|
var child = taco.c[i];
|
|
11126
11495
|
if (child && child._bwWhen) {
|
|
@@ -11161,7 +11530,7 @@
|
|
|
11161
11530
|
var eachExprStr = child.expr.replace(/^\$\{|\}$/g, '');
|
|
11162
11531
|
var arr = bw._evaluatePath(this._state, eachExprStr);
|
|
11163
11532
|
var items = [];
|
|
11164
|
-
if (
|
|
11533
|
+
if (_isA(arr)) {
|
|
11165
11534
|
for (var j = 0; j < arr.length; j++) {
|
|
11166
11535
|
items.push(child.factory(arr[j], j));
|
|
11167
11536
|
}
|
|
@@ -11175,11 +11544,11 @@
|
|
|
11175
11544
|
c: items
|
|
11176
11545
|
};
|
|
11177
11546
|
}
|
|
11178
|
-
if (
|
|
11547
|
+
if (_is(taco.c[i], 'object') && taco.c[i].t) {
|
|
11179
11548
|
this._prepareTaco(taco.c[i]);
|
|
11180
11549
|
}
|
|
11181
11550
|
}
|
|
11182
|
-
} else if (
|
|
11551
|
+
} else if (_is(taco.c, 'object') && taco.c.t) {
|
|
11183
11552
|
this._prepareTaco(taco.c);
|
|
11184
11553
|
}
|
|
11185
11554
|
};
|
|
@@ -11188,12 +11557,12 @@
|
|
|
11188
11557
|
* Wire action name strings (in onclick etc.) to dispatch function calls.
|
|
11189
11558
|
* @private
|
|
11190
11559
|
*/
|
|
11191
|
-
|
|
11192
|
-
if (!
|
|
11560
|
+
_chp._wireActions = function (taco) {
|
|
11561
|
+
if (!_is(taco, 'object') || !taco.t) return;
|
|
11193
11562
|
if (taco.a) {
|
|
11194
11563
|
for (var key in taco.a) {
|
|
11195
|
-
if (!
|
|
11196
|
-
if (key.startsWith('on') &&
|
|
11564
|
+
if (!_hop.call(taco.a, key)) continue;
|
|
11565
|
+
if (key.startsWith('on') && _is(taco.a[key], 'string')) {
|
|
11197
11566
|
var actionName = taco.a[key];
|
|
11198
11567
|
if (actionName in this._actions) {
|
|
11199
11568
|
var registeredName = this._bwId + '_' + actionName;
|
|
@@ -11207,11 +11576,11 @@
|
|
|
11207
11576
|
}
|
|
11208
11577
|
}
|
|
11209
11578
|
}
|
|
11210
|
-
if (
|
|
11579
|
+
if (_isA(taco.c)) {
|
|
11211
11580
|
for (var i = 0; i < taco.c.length; i++) {
|
|
11212
11581
|
this._wireActions(taco.c[i]);
|
|
11213
11582
|
}
|
|
11214
|
-
} else if (
|
|
11583
|
+
} else if (_is(taco.c, 'object') && taco.c.t) {
|
|
11215
11584
|
this._wireActions(taco.c);
|
|
11216
11585
|
}
|
|
11217
11586
|
};
|
|
@@ -11220,7 +11589,7 @@
|
|
|
11220
11589
|
* Deep-clone a TACO tree, preserving _bwWhen/_bwEach markers and their factories.
|
|
11221
11590
|
* @private
|
|
11222
11591
|
*/
|
|
11223
|
-
|
|
11592
|
+
_chp._deepCloneTaco = function (taco) {
|
|
11224
11593
|
if (taco == null) return taco;
|
|
11225
11594
|
// Preserve _bwWhen / _bwEach markers (contain functions)
|
|
11226
11595
|
if (taco._bwWhen) {
|
|
@@ -11239,22 +11608,22 @@
|
|
|
11239
11608
|
_refId: taco._refId
|
|
11240
11609
|
};
|
|
11241
11610
|
}
|
|
11242
|
-
if (
|
|
11611
|
+
if (!_is(taco, 'object') || !taco.t) return taco;
|
|
11243
11612
|
var result = {
|
|
11244
11613
|
t: taco.t
|
|
11245
11614
|
};
|
|
11246
11615
|
if (taco.a) {
|
|
11247
11616
|
result.a = {};
|
|
11248
11617
|
for (var k in taco.a) {
|
|
11249
|
-
if (
|
|
11618
|
+
if (_hop.call(taco.a, k)) result.a[k] = taco.a[k];
|
|
11250
11619
|
}
|
|
11251
11620
|
}
|
|
11252
11621
|
if (taco.c != null) {
|
|
11253
|
-
if (
|
|
11622
|
+
if (_isA(taco.c)) {
|
|
11254
11623
|
result.c = taco.c.map(function (child) {
|
|
11255
11624
|
return this._deepCloneTaco(child);
|
|
11256
11625
|
}.bind(this));
|
|
11257
|
-
} else if (
|
|
11626
|
+
} else if (_is(taco.c, 'object')) {
|
|
11258
11627
|
result.c = this._deepCloneTaco(taco.c);
|
|
11259
11628
|
} else {
|
|
11260
11629
|
result.c = taco.c;
|
|
@@ -11268,31 +11637,34 @@
|
|
|
11268
11637
|
* Create a copy of TACO suitable for createDOM (strips o to prevent double lifecycle).
|
|
11269
11638
|
* @private
|
|
11270
11639
|
*/
|
|
11271
|
-
|
|
11272
|
-
if (!
|
|
11640
|
+
_chp._tacoForDOM = function (taco) {
|
|
11641
|
+
if (!_is(taco, 'object') || !taco.t) return taco;
|
|
11273
11642
|
var result = {
|
|
11274
11643
|
t: taco.t
|
|
11275
11644
|
};
|
|
11276
11645
|
if (taco.a) result.a = taco.a;
|
|
11277
11646
|
if (taco.c != null) {
|
|
11278
|
-
if (
|
|
11647
|
+
if (_isA(taco.c)) {
|
|
11279
11648
|
result.c = taco.c.map(function (child) {
|
|
11280
11649
|
return this._tacoForDOM(child);
|
|
11281
11650
|
}.bind(this));
|
|
11282
|
-
} else if (
|
|
11651
|
+
} else if (_is(taco.c, 'object') && taco.c.t) {
|
|
11283
11652
|
result.c = this._tacoForDOM(taco.c);
|
|
11284
11653
|
} else {
|
|
11285
11654
|
result.c = taco.c;
|
|
11286
11655
|
}
|
|
11287
11656
|
}
|
|
11288
11657
|
// Intentionally strip o (no mounted/unmount/state/render on sub-elements)
|
|
11658
|
+
if (taco.o && (taco.o.mounted || taco.o.render || taco.o.unmount)) {
|
|
11659
|
+
_cw('bw: _tacoForDOM stripped o.mounted/render/unmount from child <' + taco.t + '>. Use onclick attribute or bw.component() for child interactivity.');
|
|
11660
|
+
}
|
|
11289
11661
|
return result;
|
|
11290
11662
|
};
|
|
11291
11663
|
|
|
11292
11664
|
/**
|
|
11293
11665
|
* Unmount: remove from DOM, deactivate, preserve state for re-mount.
|
|
11294
11666
|
*/
|
|
11295
|
-
|
|
11667
|
+
_chp.unmount = function () {
|
|
11296
11668
|
if (!this.mounted) return;
|
|
11297
11669
|
|
|
11298
11670
|
// unmount hook
|
|
@@ -11326,11 +11698,22 @@
|
|
|
11326
11698
|
/**
|
|
11327
11699
|
* Destroy: unmount + clear state + unregister actions.
|
|
11328
11700
|
*/
|
|
11329
|
-
|
|
11701
|
+
_chp.destroy = function () {
|
|
11330
11702
|
// willDestroy hook
|
|
11331
11703
|
if (this._hooks.willDestroy) {
|
|
11332
11704
|
this._hooks.willDestroy(this);
|
|
11333
11705
|
}
|
|
11706
|
+
|
|
11707
|
+
// Cascade destroy to children depth-first (Bug #5)
|
|
11708
|
+
for (var ci = this._children.length - 1; ci >= 0; ci--) {
|
|
11709
|
+
this._children[ci].destroy();
|
|
11710
|
+
}
|
|
11711
|
+
this._children = [];
|
|
11712
|
+
if (this._parent) {
|
|
11713
|
+
var idx = this._parent._children.indexOf(this);
|
|
11714
|
+
if (idx >= 0) this._parent._children.splice(idx, 1);
|
|
11715
|
+
this._parent = null;
|
|
11716
|
+
}
|
|
11334
11717
|
this.unmount();
|
|
11335
11718
|
|
|
11336
11719
|
// Unregister actions from function registry
|
|
@@ -11357,12 +11740,37 @@
|
|
|
11357
11740
|
* Flush dirty state: resolve changed bindings and apply to DOM.
|
|
11358
11741
|
* @private
|
|
11359
11742
|
*/
|
|
11360
|
-
|
|
11743
|
+
_chp._flush = function () {
|
|
11361
11744
|
this._scheduled = false;
|
|
11362
|
-
var changedKeys =
|
|
11745
|
+
var changedKeys = _keys(this._dirtyKeys);
|
|
11363
11746
|
this._dirtyKeys = {};
|
|
11364
11747
|
if (changedKeys.length === 0 || !this.mounted) return;
|
|
11365
11748
|
|
|
11749
|
+
// Factory rebuild: if a BCCL factory exists and changed keys overlap factory props,
|
|
11750
|
+
// rebuild the TACO from the factory with merged state (Bug #6)
|
|
11751
|
+
if (this._factory) {
|
|
11752
|
+
var rebuildNeeded = false;
|
|
11753
|
+
for (var fi = 0; fi < changedKeys.length; fi++) {
|
|
11754
|
+
if (_hop.call(this._factory.props, changedKeys[fi])) {
|
|
11755
|
+
rebuildNeeded = true;
|
|
11756
|
+
break;
|
|
11757
|
+
}
|
|
11758
|
+
}
|
|
11759
|
+
if (rebuildNeeded) {
|
|
11760
|
+
var merged = {};
|
|
11761
|
+
for (var mk in this._factory.props) if (_hop.call(this._factory.props, mk)) merged[mk] = this._factory.props[mk];
|
|
11762
|
+
for (var sk in this._state) if (_hop.call(this._state, sk)) merged[sk] = this._state[sk];
|
|
11763
|
+
this._factory.props = merged;
|
|
11764
|
+
var newTaco = bw.make(this._factory.type, merged);
|
|
11765
|
+
newTaco._bwFactory = this._factory;
|
|
11766
|
+
this.taco = newTaco;
|
|
11767
|
+
this._originalTaco = this._deepCloneTaco(newTaco);
|
|
11768
|
+
this._render();
|
|
11769
|
+
if (this._hooks.onUpdate) this._hooks.onUpdate(this, changedKeys);
|
|
11770
|
+
return;
|
|
11771
|
+
}
|
|
11772
|
+
}
|
|
11773
|
+
|
|
11366
11774
|
// willUpdate hook
|
|
11367
11775
|
if (this._hooks.willUpdate) {
|
|
11368
11776
|
this._hooks.willUpdate(this, changedKeys);
|
|
@@ -11400,7 +11808,7 @@
|
|
|
11400
11808
|
* Returns list of patches to apply.
|
|
11401
11809
|
* @private
|
|
11402
11810
|
*/
|
|
11403
|
-
|
|
11811
|
+
_chp._resolveBindings = function (changedKeys) {
|
|
11404
11812
|
var patches = [];
|
|
11405
11813
|
for (var i = 0; i < this._bindings.length; i++) {
|
|
11406
11814
|
var b = this._bindings[i];
|
|
@@ -11436,11 +11844,14 @@
|
|
|
11436
11844
|
* Apply patches to DOM.
|
|
11437
11845
|
* @private
|
|
11438
11846
|
*/
|
|
11439
|
-
|
|
11847
|
+
_chp._applyPatches = function (patches) {
|
|
11440
11848
|
for (var i = 0; i < patches.length; i++) {
|
|
11441
11849
|
var p = patches[i];
|
|
11442
11850
|
var el = this._bw_refs[p.refId];
|
|
11443
|
-
if (!el)
|
|
11851
|
+
if (!el) {
|
|
11852
|
+
if (bw.debug) _cw('bw.debug: _applyPatches — ref "' + p.refId + '" not found in DOM');
|
|
11853
|
+
continue;
|
|
11854
|
+
}
|
|
11444
11855
|
if (p.type === 'content') {
|
|
11445
11856
|
el.textContent = p.value;
|
|
11446
11857
|
} else if (p.type === 'attribute') {
|
|
@@ -11457,7 +11868,7 @@
|
|
|
11457
11868
|
* Resolve all bindings and apply (used for initial render).
|
|
11458
11869
|
* @private
|
|
11459
11870
|
*/
|
|
11460
|
-
|
|
11871
|
+
_chp._resolveAndApplyAll = function () {
|
|
11461
11872
|
var patches = [];
|
|
11462
11873
|
for (var i = 0; i < this._bindings.length; i++) {
|
|
11463
11874
|
var b = this._bindings[i];
|
|
@@ -11479,7 +11890,7 @@
|
|
|
11479
11890
|
* Full re-render for structural changes (when/each branch switches).
|
|
11480
11891
|
* @private
|
|
11481
11892
|
*/
|
|
11482
|
-
|
|
11893
|
+
_chp._render = function () {
|
|
11483
11894
|
if (!this.element || !this.element.parentNode) return;
|
|
11484
11895
|
var parent = this.element.parentNode;
|
|
11485
11896
|
var nextSibling = this.element.nextSibling;
|
|
@@ -11518,7 +11929,7 @@
|
|
|
11518
11929
|
* @param {string} event - Event name (e.g., 'click')
|
|
11519
11930
|
* @param {Function} handler - Event handler
|
|
11520
11931
|
*/
|
|
11521
|
-
|
|
11932
|
+
_chp.on = function (event, handler) {
|
|
11522
11933
|
if (this.element) {
|
|
11523
11934
|
this.element.addEventListener(event, handler);
|
|
11524
11935
|
}
|
|
@@ -11533,7 +11944,7 @@
|
|
|
11533
11944
|
* @param {string} event - Event name
|
|
11534
11945
|
* @param {Function} handler - Handler to remove
|
|
11535
11946
|
*/
|
|
11536
|
-
|
|
11947
|
+
_chp.off = function (event, handler) {
|
|
11537
11948
|
if (this.element) {
|
|
11538
11949
|
this.element.removeEventListener(event, handler);
|
|
11539
11950
|
}
|
|
@@ -11548,7 +11959,7 @@
|
|
|
11548
11959
|
* @param {Function} handler - Handler function
|
|
11549
11960
|
* @returns {Function} Unsubscribe function
|
|
11550
11961
|
*/
|
|
11551
|
-
|
|
11962
|
+
_chp.sub = function (topic, handler) {
|
|
11552
11963
|
var unsub = bw.sub(topic, handler);
|
|
11553
11964
|
this._subs.push(unsub);
|
|
11554
11965
|
return unsub;
|
|
@@ -11559,10 +11970,10 @@
|
|
|
11559
11970
|
* @param {string} name - Action name
|
|
11560
11971
|
* @param {...*} args - Arguments passed after comp
|
|
11561
11972
|
*/
|
|
11562
|
-
|
|
11973
|
+
_chp.action = function (name) {
|
|
11563
11974
|
var fn = this._actions[name];
|
|
11564
11975
|
if (!fn) {
|
|
11565
|
-
|
|
11976
|
+
_cw('ComponentHandle.action: unknown action "' + name + '"');
|
|
11566
11977
|
return;
|
|
11567
11978
|
}
|
|
11568
11979
|
var args = [this].concat(Array.prototype.slice.call(arguments, 1));
|
|
@@ -11574,7 +11985,7 @@
|
|
|
11574
11985
|
* @param {string} sel - CSS selector
|
|
11575
11986
|
* @returns {Element|null}
|
|
11576
11987
|
*/
|
|
11577
|
-
|
|
11988
|
+
_chp.select = function (sel) {
|
|
11578
11989
|
return this.element ? this.element.querySelector(sel) : null;
|
|
11579
11990
|
};
|
|
11580
11991
|
|
|
@@ -11583,7 +11994,7 @@
|
|
|
11583
11994
|
* @param {string} sel - CSS selector
|
|
11584
11995
|
* @returns {Element[]}
|
|
11585
11996
|
*/
|
|
11586
|
-
|
|
11997
|
+
_chp.selectAll = function (sel) {
|
|
11587
11998
|
if (!this.element) return [];
|
|
11588
11999
|
return Array.prototype.slice.call(this.element.querySelectorAll(sel));
|
|
11589
12000
|
};
|
|
@@ -11594,7 +12005,7 @@
|
|
|
11594
12005
|
* @param {string} tag - User-defined identifier (e.g. 'dashboard_prod_east')
|
|
11595
12006
|
* @returns {ComponentHandle} this (for chaining)
|
|
11596
12007
|
*/
|
|
11597
|
-
|
|
12008
|
+
_chp.userTag = function (tag) {
|
|
11598
12009
|
this._userTag = tag;
|
|
11599
12010
|
if (this.element) {
|
|
11600
12011
|
this.element.classList.add(tag);
|
|
@@ -11703,14 +12114,384 @@
|
|
|
11703
12114
|
}
|
|
11704
12115
|
if (!el || !el._bwComponentHandle) return false;
|
|
11705
12116
|
var comp = el._bwComponentHandle;
|
|
11706
|
-
if (
|
|
11707
|
-
|
|
12117
|
+
if (!_is(comp[action], 'function')) {
|
|
12118
|
+
_cw('bw.message: unknown action "' + action + '" on component ' + target);
|
|
11708
12119
|
return false;
|
|
11709
12120
|
}
|
|
11710
12121
|
comp[action](data);
|
|
11711
12122
|
return true;
|
|
11712
12123
|
};
|
|
11713
12124
|
|
|
12125
|
+
// ===================================================================================
|
|
12126
|
+
// bw.clientApply() / bw.clientConnect() — Server-driven UI protocol
|
|
12127
|
+
// ===================================================================================
|
|
12128
|
+
|
|
12129
|
+
/**
|
|
12130
|
+
* Registry of named functions sent via register messages.
|
|
12131
|
+
* Populated by clientApply({ type: 'register', name, body }).
|
|
12132
|
+
* Invoked by clientApply({ type: 'call', name, args }).
|
|
12133
|
+
* @private
|
|
12134
|
+
*/
|
|
12135
|
+
bw._clientFunctions = {};
|
|
12136
|
+
|
|
12137
|
+
/**
|
|
12138
|
+
* Whether exec messages are allowed. Set by clientConnect opts.allowExec.
|
|
12139
|
+
* Default false — exec messages are rejected unless explicitly opted in.
|
|
12140
|
+
* @private
|
|
12141
|
+
*/
|
|
12142
|
+
bw._allowExec = false;
|
|
12143
|
+
|
|
12144
|
+
/**
|
|
12145
|
+
* Built-in client functions available via call() without registration.
|
|
12146
|
+
* @private
|
|
12147
|
+
*/
|
|
12148
|
+
bw._builtinClientFunctions = {
|
|
12149
|
+
scrollTo: function scrollTo(selector) {
|
|
12150
|
+
var el = bw._el(selector);
|
|
12151
|
+
if (el) el.scrollTop = el.scrollHeight;
|
|
12152
|
+
},
|
|
12153
|
+
focus: function focus(selector) {
|
|
12154
|
+
var el = bw._el(selector);
|
|
12155
|
+
if (el && _is(el.focus, 'function')) el.focus();
|
|
12156
|
+
},
|
|
12157
|
+
download: function download(filename, content, mimeType) {
|
|
12158
|
+
if (typeof document === 'undefined') return;
|
|
12159
|
+
var blob = new Blob([content], {
|
|
12160
|
+
type: mimeType || 'text/plain'
|
|
12161
|
+
});
|
|
12162
|
+
var a = document.createElement('a');
|
|
12163
|
+
a.href = URL.createObjectURL(blob);
|
|
12164
|
+
a.download = filename;
|
|
12165
|
+
a.click();
|
|
12166
|
+
URL.revokeObjectURL(a.href);
|
|
12167
|
+
},
|
|
12168
|
+
clipboard: function clipboard(text) {
|
|
12169
|
+
if (typeof navigator !== 'undefined' && navigator.clipboard) {
|
|
12170
|
+
navigator.clipboard.writeText(text);
|
|
12171
|
+
}
|
|
12172
|
+
},
|
|
12173
|
+
redirect: function redirect(url) {
|
|
12174
|
+
if (typeof window !== 'undefined') window.location.href = url;
|
|
12175
|
+
},
|
|
12176
|
+
log: function log() {
|
|
12177
|
+
console.log.apply(console, arguments);
|
|
12178
|
+
}
|
|
12179
|
+
};
|
|
12180
|
+
|
|
12181
|
+
/**
|
|
12182
|
+
* Parse a bwserve protocol message string, supporting both strict JSON
|
|
12183
|
+
* and r-prefixed relaxed JSON (single-quoted strings, trailing commas).
|
|
12184
|
+
*
|
|
12185
|
+
* The r-prefix format is designed for C/C++ string literals where
|
|
12186
|
+
* double-quote escaping is painful. The parser is a state machine
|
|
12187
|
+
* that walks character by character — not a regex replace.
|
|
12188
|
+
*
|
|
12189
|
+
* Escaping: apostrophes inside single-quoted values must be escaped
|
|
12190
|
+
* with backslash: r{'name':'Barry\'s room'}
|
|
12191
|
+
*
|
|
12192
|
+
* @param {string} str - JSON or r-prefixed relaxed JSON string
|
|
12193
|
+
* @returns {Object} Parsed message object
|
|
12194
|
+
* @throws {SyntaxError} If the string is not valid JSON or relaxed JSON
|
|
12195
|
+
* @category Server
|
|
12196
|
+
*/
|
|
12197
|
+
bw.clientParse = function (str) {
|
|
12198
|
+
str = (str || '').trim();
|
|
12199
|
+
if (str.charAt(0) !== 'r') return JSON.parse(str);
|
|
12200
|
+
str = str.slice(1);
|
|
12201
|
+
var out = [];
|
|
12202
|
+
var i = 0;
|
|
12203
|
+
var len = str.length;
|
|
12204
|
+
while (i < len) {
|
|
12205
|
+
var ch = str[i];
|
|
12206
|
+
if (ch === "'") {
|
|
12207
|
+
// Single-quoted string → emit as double-quoted
|
|
12208
|
+
out.push('"');
|
|
12209
|
+
i++;
|
|
12210
|
+
while (i < len) {
|
|
12211
|
+
var c = str[i];
|
|
12212
|
+
if (c === '\\' && i + 1 < len) {
|
|
12213
|
+
var next = str[i + 1];
|
|
12214
|
+
if (next === "'") {
|
|
12215
|
+
out.push("'"); // \' in input → ' in output
|
|
12216
|
+
} else {
|
|
12217
|
+
out.push('\\');
|
|
12218
|
+
out.push(next);
|
|
12219
|
+
}
|
|
12220
|
+
i += 2;
|
|
12221
|
+
} else if (c === '"') {
|
|
12222
|
+
out.push('\\"');
|
|
12223
|
+
i++;
|
|
12224
|
+
} else if (c === "'") {
|
|
12225
|
+
break;
|
|
12226
|
+
} else {
|
|
12227
|
+
out.push(c);
|
|
12228
|
+
i++;
|
|
12229
|
+
}
|
|
12230
|
+
}
|
|
12231
|
+
out.push('"');
|
|
12232
|
+
i++; // skip closing '
|
|
12233
|
+
} else if (ch === '"') {
|
|
12234
|
+
// Double-quoted string — pass through verbatim
|
|
12235
|
+
out.push(ch);
|
|
12236
|
+
i++;
|
|
12237
|
+
while (i < len) {
|
|
12238
|
+
var c2 = str[i];
|
|
12239
|
+
if (c2 === '\\' && i + 1 < len) {
|
|
12240
|
+
out.push(c2);
|
|
12241
|
+
out.push(str[i + 1]);
|
|
12242
|
+
i += 2;
|
|
12243
|
+
} else {
|
|
12244
|
+
out.push(c2);
|
|
12245
|
+
i++;
|
|
12246
|
+
if (c2 === '"') break;
|
|
12247
|
+
}
|
|
12248
|
+
}
|
|
12249
|
+
} else if (ch === ',') {
|
|
12250
|
+
// Trailing comma check: skip comma if next non-whitespace is } or ]
|
|
12251
|
+
var j = i + 1;
|
|
12252
|
+
while (j < len && (str[j] === ' ' || str[j] === '\t' || str[j] === '\n' || str[j] === '\r')) j++;
|
|
12253
|
+
if (j < len && (str[j] === '}' || str[j] === ']')) {
|
|
12254
|
+
i++; // skip trailing comma
|
|
12255
|
+
} else {
|
|
12256
|
+
out.push(ch);
|
|
12257
|
+
i++;
|
|
12258
|
+
}
|
|
12259
|
+
} else {
|
|
12260
|
+
out.push(ch);
|
|
12261
|
+
i++;
|
|
12262
|
+
}
|
|
12263
|
+
}
|
|
12264
|
+
return JSON.parse(out.join(''));
|
|
12265
|
+
};
|
|
12266
|
+
|
|
12267
|
+
/**
|
|
12268
|
+
* Apply a bwserve protocol message to the DOM.
|
|
12269
|
+
*
|
|
12270
|
+
* Dispatches one of 9 message types:
|
|
12271
|
+
* replace — bw.DOM(target, node)
|
|
12272
|
+
* append — target.appendChild(bw.createDOM(node))
|
|
12273
|
+
* remove — bw.cleanup(target); target.remove()
|
|
12274
|
+
* patch — bw.patch(target, content, attr)
|
|
12275
|
+
* batch — iterate ops, call clientApply for each
|
|
12276
|
+
* message — bw.message(target, action, data)
|
|
12277
|
+
* register — store a named function for later call()
|
|
12278
|
+
* call — invoke a registered or built-in function
|
|
12279
|
+
* exec — execute arbitrary JS (requires allowExec)
|
|
12280
|
+
*
|
|
12281
|
+
* Target resolution:
|
|
12282
|
+
* Starts with '#' or '.' → CSS selector (querySelector)
|
|
12283
|
+
* Otherwise → getElementById, then bw._el fallback
|
|
12284
|
+
*
|
|
12285
|
+
* @param {Object} msg - Protocol message
|
|
12286
|
+
* @returns {boolean} true if the message was applied successfully
|
|
12287
|
+
* @category Server
|
|
12288
|
+
*/
|
|
12289
|
+
bw.clientApply = function (msg) {
|
|
12290
|
+
if (!msg || !msg.type) return false;
|
|
12291
|
+
var type = msg.type;
|
|
12292
|
+
var target = msg.target;
|
|
12293
|
+
if (type === 'replace') {
|
|
12294
|
+
var el = bw._el(target);
|
|
12295
|
+
if (!el) return false;
|
|
12296
|
+
bw.DOM(el, msg.node);
|
|
12297
|
+
return true;
|
|
12298
|
+
} else if (type === 'patch') {
|
|
12299
|
+
var patched = bw.patch(target, msg.content, msg.attr);
|
|
12300
|
+
return patched !== null;
|
|
12301
|
+
} else if (type === 'append') {
|
|
12302
|
+
var parent = bw._el(target);
|
|
12303
|
+
if (!parent) return false;
|
|
12304
|
+
var child = bw.createDOM(msg.node);
|
|
12305
|
+
parent.appendChild(child);
|
|
12306
|
+
return true;
|
|
12307
|
+
} else if (type === 'remove') {
|
|
12308
|
+
var toRemove = bw._el(target);
|
|
12309
|
+
if (!toRemove) return false;
|
|
12310
|
+
if (_is(bw.cleanup, 'function')) bw.cleanup(toRemove);
|
|
12311
|
+
toRemove.remove();
|
|
12312
|
+
return true;
|
|
12313
|
+
} else if (type === 'batch') {
|
|
12314
|
+
if (!_isA(msg.ops)) return false;
|
|
12315
|
+
var allOk = true;
|
|
12316
|
+
msg.ops.forEach(function (op) {
|
|
12317
|
+
if (!bw.clientApply(op)) allOk = false;
|
|
12318
|
+
});
|
|
12319
|
+
return allOk;
|
|
12320
|
+
} else if (type === 'message') {
|
|
12321
|
+
return bw.message(msg.target, msg.action, msg.data);
|
|
12322
|
+
} else if (type === 'register') {
|
|
12323
|
+
if (!msg.name || !msg.body) return false;
|
|
12324
|
+
try {
|
|
12325
|
+
bw._clientFunctions[msg.name] = new Function('return ' + msg.body)();
|
|
12326
|
+
return true;
|
|
12327
|
+
} catch (e) {
|
|
12328
|
+
_ce('[bw] register error:', msg.name, e);
|
|
12329
|
+
return false;
|
|
12330
|
+
}
|
|
12331
|
+
} else if (type === 'call') {
|
|
12332
|
+
if (!msg.name) return false;
|
|
12333
|
+
var fn = bw._clientFunctions[msg.name] || bw._builtinClientFunctions[msg.name];
|
|
12334
|
+
if (!_is(fn, 'function')) return false;
|
|
12335
|
+
try {
|
|
12336
|
+
var args = _isA(msg.args) ? msg.args : [];
|
|
12337
|
+
fn.apply(null, args);
|
|
12338
|
+
return true;
|
|
12339
|
+
} catch (e) {
|
|
12340
|
+
_ce('[bw] call error:', msg.name, e);
|
|
12341
|
+
return false;
|
|
12342
|
+
}
|
|
12343
|
+
} else if (type === 'exec') {
|
|
12344
|
+
if (!bw._allowExec) {
|
|
12345
|
+
_cw('[bw] exec rejected: allowExec is not enabled');
|
|
12346
|
+
return false;
|
|
12347
|
+
}
|
|
12348
|
+
if (!msg.code) return false;
|
|
12349
|
+
try {
|
|
12350
|
+
new Function(msg.code)();
|
|
12351
|
+
return true;
|
|
12352
|
+
} catch (e) {
|
|
12353
|
+
_ce('[bw] exec error:', e);
|
|
12354
|
+
return false;
|
|
12355
|
+
}
|
|
12356
|
+
}
|
|
12357
|
+
return false;
|
|
12358
|
+
};
|
|
12359
|
+
|
|
12360
|
+
/**
|
|
12361
|
+
* Connect to a bwserve SSE endpoint and apply protocol messages automatically.
|
|
12362
|
+
*
|
|
12363
|
+
* Returns a connection object with sendAction(), on(), and close() methods.
|
|
12364
|
+
*
|
|
12365
|
+
* @param {string} url - SSE endpoint URL (e.g., '/__bw/events/client-1')
|
|
12366
|
+
* @param {Object} [opts] - Connection options
|
|
12367
|
+
* @param {string} [opts.transport='sse'] - Transport type: 'sse' (default) or 'poll'
|
|
12368
|
+
* @param {number} [opts.interval=2000] - Poll interval in ms (only for 'poll' transport)
|
|
12369
|
+
* @param {string} [opts.actionUrl] - POST endpoint for actions (default: derived from url)
|
|
12370
|
+
* @param {boolean} [opts.reconnect=true] - Auto-reconnect on disconnect
|
|
12371
|
+
* @param {boolean} [opts.allowExec=false] - Enable exec message type (arbitrary JS execution)
|
|
12372
|
+
* @param {Function} [opts.onStatus] - Status callback: 'connecting'|'connected'|'disconnected'
|
|
12373
|
+
* @param {Function} [opts.onMessage] - Raw message callback (before clientApply)
|
|
12374
|
+
* @returns {Object} Connection object { sendAction, on, close, status }
|
|
12375
|
+
* @category Server
|
|
12376
|
+
*/
|
|
12377
|
+
bw.clientConnect = function (url, opts) {
|
|
12378
|
+
opts = opts || {};
|
|
12379
|
+
var transport = opts.transport || 'sse';
|
|
12380
|
+
var actionUrl = opts.actionUrl || url.replace(/\/events\//, '/action/');
|
|
12381
|
+
var reconnect = opts.reconnect !== false;
|
|
12382
|
+
var onStatus = opts.onStatus || function () {};
|
|
12383
|
+
var onMessage = opts.onMessage || null;
|
|
12384
|
+
var handlers = {};
|
|
12385
|
+
// Set the global allowExec flag from connection options
|
|
12386
|
+
bw._allowExec = !!opts.allowExec;
|
|
12387
|
+
var conn = {
|
|
12388
|
+
status: 'connecting',
|
|
12389
|
+
_es: null,
|
|
12390
|
+
_pollTimer: null
|
|
12391
|
+
};
|
|
12392
|
+
function setStatus(s) {
|
|
12393
|
+
conn.status = s;
|
|
12394
|
+
onStatus(s);
|
|
12395
|
+
}
|
|
12396
|
+
function handleMessage(data) {
|
|
12397
|
+
try {
|
|
12398
|
+
var msg = _is(data, 'string') ? bw.clientParse(data) : data;
|
|
12399
|
+
if (onMessage) onMessage(msg);
|
|
12400
|
+
if (handlers.message) handlers.message(msg);
|
|
12401
|
+
bw.clientApply(msg);
|
|
12402
|
+
} catch (e) {
|
|
12403
|
+
if (handlers.error) handlers.error(e);
|
|
12404
|
+
}
|
|
12405
|
+
}
|
|
12406
|
+
if (transport === 'sse' && typeof EventSource !== 'undefined') {
|
|
12407
|
+
setStatus('connecting');
|
|
12408
|
+
var es = new EventSource(url);
|
|
12409
|
+
conn._es = es;
|
|
12410
|
+
es.onopen = function () {
|
|
12411
|
+
setStatus('connected');
|
|
12412
|
+
if (handlers.open) handlers.open();
|
|
12413
|
+
};
|
|
12414
|
+
es.onmessage = function (e) {
|
|
12415
|
+
handleMessage(e.data);
|
|
12416
|
+
};
|
|
12417
|
+
es.onerror = function () {
|
|
12418
|
+
if (conn.status === 'connected') {
|
|
12419
|
+
setStatus('disconnected');
|
|
12420
|
+
}
|
|
12421
|
+
if (handlers.error) handlers.error(new Error('SSE connection error'));
|
|
12422
|
+
if (!reconnect) {
|
|
12423
|
+
es.close();
|
|
12424
|
+
}
|
|
12425
|
+
// EventSource auto-reconnects by default when reconnect=true
|
|
12426
|
+
};
|
|
12427
|
+
} else if (transport === 'poll') {
|
|
12428
|
+
var interval = opts.interval || 2000;
|
|
12429
|
+
setStatus('connected');
|
|
12430
|
+
conn._pollTimer = setInterval(function () {
|
|
12431
|
+
fetch(url).then(function (r) {
|
|
12432
|
+
return r.json();
|
|
12433
|
+
}).then(function (msgs) {
|
|
12434
|
+
if (_isA(msgs)) {
|
|
12435
|
+
msgs.forEach(handleMessage);
|
|
12436
|
+
} else if (msgs && msgs.type) {
|
|
12437
|
+
handleMessage(msgs);
|
|
12438
|
+
}
|
|
12439
|
+
})["catch"](function (e) {
|
|
12440
|
+
if (handlers.error) handlers.error(e);
|
|
12441
|
+
});
|
|
12442
|
+
}, interval);
|
|
12443
|
+
}
|
|
12444
|
+
|
|
12445
|
+
/**
|
|
12446
|
+
* Send an action to the server via POST.
|
|
12447
|
+
* @param {string} action - Action name
|
|
12448
|
+
* @param {Object} [data] - Action payload
|
|
12449
|
+
*/
|
|
12450
|
+
conn.sendAction = function (action, data) {
|
|
12451
|
+
var body = JSON.stringify({
|
|
12452
|
+
type: 'action',
|
|
12453
|
+
action: action,
|
|
12454
|
+
data: data || {}
|
|
12455
|
+
});
|
|
12456
|
+
fetch(actionUrl, {
|
|
12457
|
+
method: 'POST',
|
|
12458
|
+
headers: {
|
|
12459
|
+
'Content-Type': 'application/json'
|
|
12460
|
+
},
|
|
12461
|
+
body: body
|
|
12462
|
+
})["catch"](function (e) {
|
|
12463
|
+
if (handlers.error) handlers.error(e);
|
|
12464
|
+
});
|
|
12465
|
+
};
|
|
12466
|
+
|
|
12467
|
+
/**
|
|
12468
|
+
* Register an event handler.
|
|
12469
|
+
* @param {string} event - 'open'|'message'|'error'|'close'
|
|
12470
|
+
* @param {Function} handler
|
|
12471
|
+
*/
|
|
12472
|
+
conn.on = function (event, handler) {
|
|
12473
|
+
handlers[event] = handler;
|
|
12474
|
+
return conn;
|
|
12475
|
+
};
|
|
12476
|
+
|
|
12477
|
+
/**
|
|
12478
|
+
* Close the connection.
|
|
12479
|
+
*/
|
|
12480
|
+
conn.close = function () {
|
|
12481
|
+
if (conn._es) {
|
|
12482
|
+
conn._es.close();
|
|
12483
|
+
conn._es = null;
|
|
12484
|
+
}
|
|
12485
|
+
if (conn._pollTimer) {
|
|
12486
|
+
clearInterval(conn._pollTimer);
|
|
12487
|
+
conn._pollTimer = null;
|
|
12488
|
+
}
|
|
12489
|
+
setStatus('disconnected');
|
|
12490
|
+
if (handlers.close) handlers.close();
|
|
12491
|
+
};
|
|
12492
|
+
return conn;
|
|
12493
|
+
};
|
|
12494
|
+
|
|
11714
12495
|
// ===================================================================================
|
|
11715
12496
|
// bw.inspect() — Debug utility
|
|
11716
12497
|
// ===================================================================================
|
|
@@ -11737,20 +12518,20 @@
|
|
|
11737
12518
|
el = target.element;
|
|
11738
12519
|
comp = target;
|
|
11739
12520
|
} else {
|
|
11740
|
-
if (
|
|
12521
|
+
if (_is(target, 'string')) {
|
|
11741
12522
|
el = bw.$(target)[0];
|
|
11742
12523
|
}
|
|
11743
12524
|
if (!el) {
|
|
11744
|
-
|
|
12525
|
+
_cw('bw.inspect: element not found');
|
|
11745
12526
|
return null;
|
|
11746
12527
|
}
|
|
11747
12528
|
comp = el._bwComponentHandle;
|
|
11748
12529
|
}
|
|
11749
12530
|
if (!comp) {
|
|
11750
|
-
|
|
11751
|
-
|
|
11752
|
-
|
|
11753
|
-
|
|
12531
|
+
_cl('bw.inspect: no ComponentHandle on this element');
|
|
12532
|
+
_cl(' Tag:', el.tagName);
|
|
12533
|
+
_cl(' Classes:', el.className);
|
|
12534
|
+
_cl(' _bw_state:', el._bw_state || '(none)');
|
|
11754
12535
|
return null;
|
|
11755
12536
|
}
|
|
11756
12537
|
var deps = comp._bindings.reduce(function (s, b) {
|
|
@@ -11759,13 +12540,13 @@
|
|
|
11759
12540
|
return a.indexOf(v) === i;
|
|
11760
12541
|
});
|
|
11761
12542
|
console.group('Component: ' + comp._bwId);
|
|
11762
|
-
|
|
11763
|
-
|
|
11764
|
-
|
|
11765
|
-
|
|
11766
|
-
|
|
11767
|
-
|
|
11768
|
-
|
|
12543
|
+
_cl('State:', comp._state);
|
|
12544
|
+
_cl('Bindings:', comp._bindings.length, '(deps:', deps, ')');
|
|
12545
|
+
_cl('Methods:', _keys(comp._methods));
|
|
12546
|
+
_cl('Actions:', _keys(comp._actions));
|
|
12547
|
+
_cl('User tag:', comp._userTag || '(none)');
|
|
12548
|
+
_cl('Mounted:', comp.mounted);
|
|
12549
|
+
_cl('Element:', comp.element);
|
|
11769
12550
|
console.groupEnd();
|
|
11770
12551
|
return comp;
|
|
11771
12552
|
};
|
|
@@ -11788,8 +12569,8 @@
|
|
|
11788
12569
|
// Pre-extract all binding expressions
|
|
11789
12570
|
var precompiled = [];
|
|
11790
12571
|
function walkExpressions(node) {
|
|
11791
|
-
if (!
|
|
11792
|
-
if (
|
|
12572
|
+
if (!_is(node, 'object')) return;
|
|
12573
|
+
if (_is(node.c, 'string') && node.c.indexOf('${') >= 0) {
|
|
11793
12574
|
var parsed = bw._parseBindings(node.c);
|
|
11794
12575
|
for (var i = 0; i < parsed.length; i++) {
|
|
11795
12576
|
try {
|
|
@@ -11809,9 +12590,9 @@
|
|
|
11809
12590
|
}
|
|
11810
12591
|
if (node.a) {
|
|
11811
12592
|
for (var key in node.a) {
|
|
11812
|
-
if (
|
|
12593
|
+
if (_hop.call(node.a, key)) {
|
|
11813
12594
|
var v = node.a[key];
|
|
11814
|
-
if (
|
|
12595
|
+
if (_is(v, 'string') && v.indexOf('${') >= 0) {
|
|
11815
12596
|
var parsed2 = bw._parseBindings(v);
|
|
11816
12597
|
for (var j = 0; j < parsed2.length; j++) {
|
|
11817
12598
|
try {
|
|
@@ -11832,9 +12613,9 @@
|
|
|
11832
12613
|
}
|
|
11833
12614
|
}
|
|
11834
12615
|
}
|
|
11835
|
-
if (
|
|
12616
|
+
if (_isA(node.c)) {
|
|
11836
12617
|
for (var k = 0; k < node.c.length; k++) walkExpressions(node.c[k]);
|
|
11837
|
-
} else if (
|
|
12618
|
+
} else if (_is(node.c, 'object') && node.c.t) {
|
|
11838
12619
|
walkExpressions(node.c);
|
|
11839
12620
|
}
|
|
11840
12621
|
}
|
|
@@ -11845,7 +12626,7 @@
|
|
|
11845
12626
|
handle._precompiledBindings = precompiled;
|
|
11846
12627
|
if (initialState) {
|
|
11847
12628
|
for (var k in initialState) {
|
|
11848
|
-
if (
|
|
12629
|
+
if (_hop.call(initialState, k)) {
|
|
11849
12630
|
handle._state[k] = initialState[k];
|
|
11850
12631
|
}
|
|
11851
12632
|
}
|
|
@@ -11879,21 +12660,21 @@
|
|
|
11879
12660
|
minify = _options$minify === void 0 ? false : _options$minify,
|
|
11880
12661
|
_options$pretty = options.pretty,
|
|
11881
12662
|
pretty = _options$pretty === void 0 ? !minify : _options$pretty;
|
|
11882
|
-
if (
|
|
12663
|
+
if (_is(rules, 'string')) return rules;
|
|
11883
12664
|
var css = '';
|
|
11884
12665
|
var indent = pretty ? ' ' : '';
|
|
11885
12666
|
var newline = pretty ? '\n' : '';
|
|
11886
12667
|
var space = pretty ? ' ' : '';
|
|
11887
|
-
if (
|
|
12668
|
+
if (_isA(rules)) {
|
|
11888
12669
|
css = rules.map(function (rule) {
|
|
11889
12670
|
return bw.css(rule, options);
|
|
11890
12671
|
}).join(newline);
|
|
11891
|
-
} else if (
|
|
12672
|
+
} else if (_is(rules, 'object')) {
|
|
11892
12673
|
Object.entries(rules).forEach(function (_ref5) {
|
|
11893
12674
|
var _ref6 = _slicedToArray(_ref5, 2),
|
|
11894
12675
|
selector = _ref6[0],
|
|
11895
12676
|
styles = _ref6[1];
|
|
11896
|
-
if (
|
|
12677
|
+
if (_is(styles, 'object')) {
|
|
11897
12678
|
// Handle @media, @keyframes, @supports — recurse into nested block
|
|
11898
12679
|
if (selector.charAt(0) === '@') {
|
|
11899
12680
|
var inner = bw.css(styles, options);
|
|
@@ -11947,7 +12728,7 @@
|
|
|
11947
12728
|
bw.injectCSS = function (css) {
|
|
11948
12729
|
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
11949
12730
|
if (!bw._isBrowser) {
|
|
11950
|
-
|
|
12731
|
+
_cw('bw.injectCSS requires a DOM environment');
|
|
11951
12732
|
return null;
|
|
11952
12733
|
}
|
|
11953
12734
|
var _options$id = options.id,
|
|
@@ -11965,7 +12746,7 @@
|
|
|
11965
12746
|
}
|
|
11966
12747
|
|
|
11967
12748
|
// Convert CSS if needed
|
|
11968
|
-
var cssStr =
|
|
12749
|
+
var cssStr = _is(css, 'string') ? css : bw.css(css, options);
|
|
11969
12750
|
|
|
11970
12751
|
// Set or append CSS
|
|
11971
12752
|
if (append && styleEl.textContent) {
|
|
@@ -11994,7 +12775,7 @@
|
|
|
11994
12775
|
var result = {};
|
|
11995
12776
|
for (var i = 0; i < arguments.length; i++) {
|
|
11996
12777
|
var arg = arguments[i];
|
|
11997
|
-
if (
|
|
12778
|
+
if (_is(arg, 'object')) Object.assign(result, arg);
|
|
11998
12779
|
}
|
|
11999
12780
|
return result;
|
|
12000
12781
|
};
|
|
@@ -12239,7 +13020,7 @@
|
|
|
12239
13020
|
xl: '1200px'
|
|
12240
13021
|
};
|
|
12241
13022
|
var parts = [];
|
|
12242
|
-
|
|
13023
|
+
_keys(breakpoints).forEach(function (key) {
|
|
12243
13024
|
var rules = {};
|
|
12244
13025
|
if (key === 'base') {
|
|
12245
13026
|
rules[selector] = breakpoints[key];
|
|
@@ -12311,18 +13092,18 @@
|
|
|
12311
13092
|
if (!selector) return [];
|
|
12312
13093
|
|
|
12313
13094
|
// Already an array
|
|
12314
|
-
if (
|
|
13095
|
+
if (_isA(selector)) return selector;
|
|
12315
13096
|
|
|
12316
13097
|
// Single element
|
|
12317
13098
|
if (selector.nodeType) return [selector];
|
|
12318
13099
|
|
|
12319
13100
|
// NodeList or HTMLCollection
|
|
12320
|
-
if (selector.length !== undefined &&
|
|
13101
|
+
if (selector.length !== undefined && !_is(selector, 'string')) {
|
|
12321
13102
|
return Array.from(selector);
|
|
12322
13103
|
}
|
|
12323
13104
|
|
|
12324
13105
|
// CSS selector string
|
|
12325
|
-
if (
|
|
13106
|
+
if (_is(selector, 'string')) {
|
|
12326
13107
|
return Array.from(document.querySelectorAll(selector));
|
|
12327
13108
|
}
|
|
12328
13109
|
return [];
|
|
@@ -12840,7 +13621,7 @@
|
|
|
12840
13621
|
cls = cls.trim();
|
|
12841
13622
|
|
|
12842
13623
|
// Auto-detect columns if not provided
|
|
12843
|
-
var cols = columns || (data.length > 0 ?
|
|
13624
|
+
var cols = columns || (data.length > 0 ? _keys(data[0]).map(function (key) {
|
|
12844
13625
|
return {
|
|
12845
13626
|
key: key,
|
|
12846
13627
|
label: key
|
|
@@ -12859,7 +13640,7 @@
|
|
|
12859
13640
|
var bVal = b[currentSortColumn];
|
|
12860
13641
|
|
|
12861
13642
|
// Handle different types
|
|
12862
|
-
if (
|
|
13643
|
+
if (_is(aVal, 'number') && _is(bVal, 'number')) {
|
|
12863
13644
|
return currentSortDirection === 'asc' ? aVal - bVal : bVal - aVal;
|
|
12864
13645
|
}
|
|
12865
13646
|
|
|
@@ -12983,7 +13764,7 @@
|
|
|
12983
13764
|
headerRow = _config$headerRow === void 0 ? true : _config$headerRow,
|
|
12984
13765
|
columns = config.columns,
|
|
12985
13766
|
rest = _objectWithoutProperties(config, _excluded);
|
|
12986
|
-
if (!
|
|
13767
|
+
if (!_isA(data) || data.length === 0) {
|
|
12987
13768
|
return bw.makeTable(_objectSpread2({
|
|
12988
13769
|
data: [],
|
|
12989
13770
|
columns: columns || []
|
|
@@ -13080,7 +13861,7 @@
|
|
|
13080
13861
|
showLabels = _config$showLabels === void 0 ? true : _config$showLabels,
|
|
13081
13862
|
_config$className2 = config.className,
|
|
13082
13863
|
className = _config$className2 === void 0 ? '' : _config$className2;
|
|
13083
|
-
if (!
|
|
13864
|
+
if (!_isA(data) || data.length === 0) {
|
|
13084
13865
|
return {
|
|
13085
13866
|
t: 'div',
|
|
13086
13867
|
a: {
|
|
@@ -13263,7 +14044,7 @@
|
|
|
13263
14044
|
bw.render = function (element, position, taco) {
|
|
13264
14045
|
var _taco$o4, _taco$o5, _taco$o6;
|
|
13265
14046
|
// Get target element
|
|
13266
|
-
var targetEl =
|
|
14047
|
+
var targetEl = _is(element, 'string') ? document.querySelector(element) : element;
|
|
13267
14048
|
if (!targetEl) {
|
|
13268
14049
|
return {
|
|
13269
14050
|
object_type: 'error',
|
|
@@ -13401,7 +14182,7 @@
|
|
|
13401
14182
|
setContent: function setContent(content) {
|
|
13402
14183
|
this._taco.c = content;
|
|
13403
14184
|
if (this.element) {
|
|
13404
|
-
if (
|
|
14185
|
+
if (_is(content, 'string')) {
|
|
13405
14186
|
this.element.textContent = content;
|
|
13406
14187
|
} else {
|
|
13407
14188
|
// Re-render for complex content
|