bitwrench 2.0.18 → 2.0.20
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 +86 -81
- package/dist/bitwrench-bccl.cjs.js +221 -48
- package/dist/bitwrench-bccl.cjs.min.js +3 -3
- package/dist/bitwrench-bccl.esm.js +221 -48
- package/dist/bitwrench-bccl.esm.min.js +3 -3
- package/dist/bitwrench-bccl.umd.js +221 -48
- package/dist/bitwrench-bccl.umd.min.js +3 -3
- package/dist/bitwrench-code-edit.cjs.js +7 -9
- package/dist/bitwrench-code-edit.cjs.min.js +5 -7
- package/dist/bitwrench-code-edit.es5.js +6 -8
- package/dist/bitwrench-code-edit.es5.min.js +5 -7
- package/dist/bitwrench-code-edit.esm.js +7 -9
- package/dist/bitwrench-code-edit.esm.min.js +5 -7
- package/dist/bitwrench-code-edit.umd.js +7 -9
- package/dist/bitwrench-code-edit.umd.min.js +5 -7
- package/dist/bitwrench-debug.js +268 -0
- package/dist/bitwrench-debug.min.js +3 -0
- package/dist/bitwrench-lean.cjs.js +250 -1574
- package/dist/bitwrench-lean.cjs.min.js +6 -6
- package/dist/bitwrench-lean.es5.js +344 -1661
- package/dist/bitwrench-lean.es5.min.js +4 -4
- package/dist/bitwrench-lean.esm.js +250 -1574
- package/dist/bitwrench-lean.esm.min.js +6 -6
- package/dist/bitwrench-lean.umd.js +250 -1574
- package/dist/bitwrench-lean.umd.min.js +6 -6
- package/dist/bitwrench-util-css.cjs.js +1 -1
- package/dist/bitwrench-util-css.cjs.min.js +1 -1
- package/dist/bitwrench-util-css.es5.js +1 -1
- package/dist/bitwrench-util-css.es5.min.js +1 -1
- package/dist/bitwrench-util-css.esm.js +1 -1
- package/dist/bitwrench-util-css.esm.min.js +1 -1
- package/dist/bitwrench-util-css.umd.js +1 -1
- package/dist/bitwrench-util-css.umd.min.js +1 -1
- package/dist/bitwrench.cjs.js +510 -1660
- package/dist/bitwrench.cjs.min.js +7 -7
- package/dist/bitwrench.css +80 -33
- package/dist/bitwrench.es5.js +569 -1694
- package/dist/bitwrench.es5.min.js +5 -5
- package/dist/bitwrench.esm.js +510 -1660
- package/dist/bitwrench.esm.min.js +7 -7
- package/dist/bitwrench.min.css +1 -1
- package/dist/bitwrench.umd.js +510 -1660
- package/dist/bitwrench.umd.min.js +7 -7
- package/dist/builds.json +133 -111
- package/dist/bwserve.cjs.js +2 -2
- package/dist/bwserve.esm.js +2 -2
- package/dist/sri.json +46 -44
- package/package.json +5 -3
- package/readme.html +86 -75
- package/src/bitwrench-bccl-entry.js +3 -4
- package/src/bitwrench-bccl.js +217 -43
- package/src/bitwrench-code-edit.js +6 -8
- package/src/bitwrench-debug.js +245 -0
- package/src/bitwrench-styles.js +35 -8
- package/src/bitwrench.js +212 -1563
- package/src/cli/attach.js +53 -21
- package/src/cli/serve.js +179 -3
- package/src/version.js +3 -3
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! bitwrench-lean v2.0.
|
|
1
|
+
/*! bitwrench-lean v2.0.20 | 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) :
|
|
@@ -190,14 +190,14 @@
|
|
|
190
190
|
*/
|
|
191
191
|
|
|
192
192
|
var VERSION_INFO = {
|
|
193
|
-
version: '2.0.
|
|
193
|
+
version: '2.0.20',
|
|
194
194
|
name: 'bitwrench',
|
|
195
195
|
description: 'A library for javascript UI functions.',
|
|
196
196
|
license: 'BSD-2-Clause',
|
|
197
197
|
homepage: 'https://deftio.github.com/bitwrench/pages',
|
|
198
198
|
repository: 'git+https://github.com/deftio/bitwrench.git',
|
|
199
199
|
author: 'manu a. chatterjee <deftio@deftio.com> (https://deftio.com/)',
|
|
200
|
-
buildDate: '2026-03-
|
|
200
|
+
buildDate: '2026-03-23T05:19:31.951Z'
|
|
201
201
|
};
|
|
202
202
|
|
|
203
203
|
/**
|
|
@@ -3088,7 +3088,9 @@
|
|
|
3088
3088
|
'.bw_modal_header': {
|
|
3089
3089
|
'display': 'flex',
|
|
3090
3090
|
'align-items': 'center',
|
|
3091
|
-
'justify-content': 'space-between'
|
|
3091
|
+
'justify-content': 'space-between',
|
|
3092
|
+
'padding': '1rem 1.25rem',
|
|
3093
|
+
'border-bottom': '1px solid transparent'
|
|
3092
3094
|
},
|
|
3093
3095
|
'.bw_modal_title': {
|
|
3094
3096
|
'margin': '0',
|
|
@@ -3098,14 +3100,17 @@
|
|
|
3098
3100
|
},
|
|
3099
3101
|
'.bw_modal_body': {
|
|
3100
3102
|
'position': 'relative',
|
|
3101
|
-
'flex': '1 1 auto'
|
|
3103
|
+
'flex': '1 1 auto',
|
|
3104
|
+
'padding': '1rem 1.25rem'
|
|
3102
3105
|
},
|
|
3103
3106
|
'.bw_modal_footer': {
|
|
3104
3107
|
'display': 'flex',
|
|
3105
3108
|
'flex-wrap': 'wrap',
|
|
3106
3109
|
'align-items': 'center',
|
|
3107
3110
|
'justify-content': 'flex-end',
|
|
3108
|
-
'gap': '0.5rem'
|
|
3111
|
+
'gap': '0.5rem',
|
|
3112
|
+
'padding': '0.75rem 1.25rem',
|
|
3113
|
+
'border-top': '1px solid transparent'
|
|
3109
3114
|
}
|
|
3110
3115
|
},
|
|
3111
3116
|
// ---- Toast ----
|
|
@@ -3163,9 +3168,12 @@
|
|
|
3163
3168
|
'display': 'flex',
|
|
3164
3169
|
'align-items': 'center',
|
|
3165
3170
|
'justify-content': 'space-between',
|
|
3166
|
-
'
|
|
3171
|
+
'padding': '0.5rem 0.75rem',
|
|
3172
|
+
'font-size': '0.875rem',
|
|
3173
|
+
'border-bottom': '1px solid transparent'
|
|
3167
3174
|
},
|
|
3168
3175
|
'.bw_toast_body': {
|
|
3176
|
+
'padding': '0.5rem 0.75rem',
|
|
3169
3177
|
'font-size': '0.9375rem'
|
|
3170
3178
|
}
|
|
3171
3179
|
},
|
|
@@ -3195,6 +3203,7 @@
|
|
|
3195
3203
|
'padding': '0.5rem 0',
|
|
3196
3204
|
'margin': '0.125rem 0 0',
|
|
3197
3205
|
'background-clip': 'padding-box',
|
|
3206
|
+
'border': '1px solid transparent',
|
|
3198
3207
|
'opacity': '0',
|
|
3199
3208
|
'visibility': 'hidden',
|
|
3200
3209
|
'pointer-events': 'none'
|
|
@@ -3211,6 +3220,7 @@
|
|
|
3211
3220
|
'.bw_dropdown_item': {
|
|
3212
3221
|
'display': 'block',
|
|
3213
3222
|
'width': '100%',
|
|
3223
|
+
'padding': '0.4rem 1rem',
|
|
3214
3224
|
'clear': 'both',
|
|
3215
3225
|
'font-weight': '400',
|
|
3216
3226
|
'text-align': 'inherit',
|
|
@@ -3218,7 +3228,8 @@
|
|
|
3218
3228
|
'white-space': 'nowrap',
|
|
3219
3229
|
'background-color': 'transparent',
|
|
3220
3230
|
'border': '0',
|
|
3221
|
-
'font-size': '0.9375rem'
|
|
3231
|
+
'font-size': '0.9375rem',
|
|
3232
|
+
'cursor': 'pointer'
|
|
3222
3233
|
},
|
|
3223
3234
|
'.bw_dropdown_item:focus-visible': {
|
|
3224
3235
|
'outline': '2px solid currentColor',
|
|
@@ -4104,6 +4115,67 @@
|
|
|
4104
4115
|
rules['.bw_text_center'] = {
|
|
4105
4116
|
'text-align': 'center'
|
|
4106
4117
|
};
|
|
4118
|
+
rules['.bw_text_justify'] = {
|
|
4119
|
+
'text-align': 'justify'
|
|
4120
|
+
};
|
|
4121
|
+
|
|
4122
|
+
// Font weight
|
|
4123
|
+
rules['.bw_fw_bold'] = {
|
|
4124
|
+
'font-weight': '700'
|
|
4125
|
+
};
|
|
4126
|
+
rules['.bw_fw_semibold'] = {
|
|
4127
|
+
'font-weight': '600'
|
|
4128
|
+
};
|
|
4129
|
+
rules['.bw_fw_normal'] = {
|
|
4130
|
+
'font-weight': '400'
|
|
4131
|
+
};
|
|
4132
|
+
rules['.bw_fw_light'] = {
|
|
4133
|
+
'font-weight': '300'
|
|
4134
|
+
};
|
|
4135
|
+
|
|
4136
|
+
// Font style
|
|
4137
|
+
rules['.bw_fst_italic'] = {
|
|
4138
|
+
'font-style': 'italic'
|
|
4139
|
+
};
|
|
4140
|
+
rules['.bw_fst_normal'] = {
|
|
4141
|
+
'font-style': 'normal'
|
|
4142
|
+
};
|
|
4143
|
+
|
|
4144
|
+
// Text decoration
|
|
4145
|
+
rules['.bw_text_underline'] = {
|
|
4146
|
+
'text-decoration': 'underline'
|
|
4147
|
+
};
|
|
4148
|
+
rules['.bw_text_line_through'] = {
|
|
4149
|
+
'text-decoration': 'line-through'
|
|
4150
|
+
};
|
|
4151
|
+
rules['.bw_text_decoration_none'] = {
|
|
4152
|
+
'text-decoration': 'none'
|
|
4153
|
+
};
|
|
4154
|
+
|
|
4155
|
+
// Text transform
|
|
4156
|
+
rules['.bw_text_uppercase'] = {
|
|
4157
|
+
'text-transform': 'uppercase'
|
|
4158
|
+
};
|
|
4159
|
+
rules['.bw_text_lowercase'] = {
|
|
4160
|
+
'text-transform': 'lowercase'
|
|
4161
|
+
};
|
|
4162
|
+
rules['.bw_text_capitalize'] = {
|
|
4163
|
+
'text-transform': 'capitalize'
|
|
4164
|
+
};
|
|
4165
|
+
|
|
4166
|
+
// Font size
|
|
4167
|
+
rules['.bw_fs_sm'] = {
|
|
4168
|
+
'font-size': '0.875rem'
|
|
4169
|
+
};
|
|
4170
|
+
rules['.bw_fs_base'] = {
|
|
4171
|
+
'font-size': '1rem'
|
|
4172
|
+
};
|
|
4173
|
+
rules['.bw_fs_lg'] = {
|
|
4174
|
+
'font-size': '1.25rem'
|
|
4175
|
+
};
|
|
4176
|
+
rules['.bw_fs_xl'] = {
|
|
4177
|
+
'font-size': '1.5rem'
|
|
4178
|
+
};
|
|
4107
4179
|
|
|
4108
4180
|
// Flexbox
|
|
4109
4181
|
var jc = {
|
|
@@ -5284,12 +5356,11 @@
|
|
|
5284
5356
|
// monotonic ID for subscriptions
|
|
5285
5357
|
|
|
5286
5358
|
// ── Node reference cache ──────────────────────────────────────────────
|
|
5287
|
-
// Fast O(1) lookup for elements by
|
|
5359
|
+
// Fast O(1) lookup for elements by id attribute or bw_uuid_* class.
|
|
5288
5360
|
//
|
|
5289
5361
|
// Populated by bw.createDOM() when elements have:
|
|
5290
|
-
// - data-bw_id attribute (user-declared addressable elements)
|
|
5291
5362
|
// - id attribute (standard HTML id)
|
|
5292
|
-
// -
|
|
5363
|
+
// - bw_uuid_* class (lifecycle-managed or explicitly addressed elements)
|
|
5293
5364
|
//
|
|
5294
5365
|
// Cleaned up by bw.cleanup() when elements are destroyed via bitwrench APIs.
|
|
5295
5366
|
// On cache miss, falls back to querySelector/getElementById — never fails,
|
|
@@ -5297,7 +5368,7 @@
|
|
|
5297
5368
|
// via parentNode === null check (IE11-safe, unlike el.isConnected).
|
|
5298
5369
|
//
|
|
5299
5370
|
// Elements created via bw.createDOM() also get el._bw_refs — a local map of
|
|
5300
|
-
// child
|
|
5371
|
+
// child id/UUID -> DOM node ref for fast parent->child access in o.render.
|
|
5301
5372
|
// This is the bitwrench equivalent of React's compiled template "holes".
|
|
5302
5373
|
//
|
|
5303
5374
|
// Contract: if you remove elements outside of bitwrench APIs (raw el.remove()),
|
|
@@ -5376,7 +5447,6 @@
|
|
|
5376
5447
|
// _cw console.warn 8
|
|
5377
5448
|
// _cl console.log 11
|
|
5378
5449
|
// _ce console.error 4
|
|
5379
|
-
// _chp ComponentHandle.prototype 28 (defined after constructor)
|
|
5380
5450
|
//
|
|
5381
5451
|
// Note: document.createElement etc. are NOT aliased because they require
|
|
5382
5452
|
// `this === document` and .bind() would add overhead on every call.
|
|
@@ -5560,15 +5630,15 @@
|
|
|
5560
5630
|
* 1. Check `bw._nodeMap[id]` — if found and still attached (parentNode !== null), return it
|
|
5561
5631
|
* 2. If cached ref is detached (parentNode === null), remove stale entry
|
|
5562
5632
|
* 3. Fall back to `document.getElementById(id)` then `document.querySelector(...)`
|
|
5563
|
-
* 4.
|
|
5564
|
-
* 5.
|
|
5633
|
+
* 4. Try class-based lookup for bw_uuid_* tokens (UUID addressing)
|
|
5634
|
+
* 5. Cache the result for next time
|
|
5565
5635
|
*
|
|
5566
5636
|
* Accepts a DOM element directly (pass-through) or a string identifier.
|
|
5567
5637
|
* String identifiers are tried as: direct map key, getElementById,
|
|
5568
5638
|
* querySelector (for CSS selectors starting with . or #), and
|
|
5569
|
-
*
|
|
5639
|
+
* bw_uuid_* class selector.
|
|
5570
5640
|
*
|
|
5571
|
-
* @param {string|Element} id - Element ID, CSS selector,
|
|
5641
|
+
* @param {string|Element} id - Element ID, CSS selector, bw_uuid_* class, or DOM element
|
|
5572
5642
|
* @returns {Element|null} The DOM element, or null if not found
|
|
5573
5643
|
* @category Internal
|
|
5574
5644
|
*/
|
|
@@ -5597,17 +5667,12 @@
|
|
|
5597
5667
|
el = document.querySelector(id);
|
|
5598
5668
|
}
|
|
5599
5669
|
|
|
5600
|
-
// 4. Try
|
|
5601
|
-
if (!el) {
|
|
5602
|
-
el = document.querySelector('[data-bw_id="' + id + '"]');
|
|
5603
|
-
}
|
|
5604
|
-
|
|
5605
|
-
// 5. Try class-based lookup for bw_uuid_* tokens (UUID addressing)
|
|
5670
|
+
// 4. Try class-based lookup for bw_uuid_* tokens (UUID addressing)
|
|
5606
5671
|
if (!el && id.indexOf('bw_uuid_') === 0) {
|
|
5607
5672
|
el = document.querySelector('.' + id);
|
|
5608
5673
|
}
|
|
5609
5674
|
|
|
5610
|
-
//
|
|
5675
|
+
// 5. Cache the result for next time
|
|
5611
5676
|
if (el) {
|
|
5612
5677
|
bw._nodeMap[id] = el;
|
|
5613
5678
|
}
|
|
@@ -5618,17 +5683,17 @@
|
|
|
5618
5683
|
* Register a DOM element in the node cache under one or more keys.
|
|
5619
5684
|
*
|
|
5620
5685
|
* Called internally by `bw.createDOM()`. Registers elements that have
|
|
5621
|
-
* id attributes,
|
|
5686
|
+
* id attributes, UUID classes, or both.
|
|
5622
5687
|
*
|
|
5623
5688
|
* @param {Element} el - DOM element to register
|
|
5624
|
-
* @param {string} [
|
|
5689
|
+
* @param {string} [uuid] - bw_uuid_* class token to register under
|
|
5625
5690
|
* @category Internal
|
|
5626
5691
|
*/
|
|
5627
|
-
bw._registerNode = function (el,
|
|
5692
|
+
bw._registerNode = function (el, uuid) {
|
|
5628
5693
|
if (!el) return;
|
|
5629
|
-
// Register under
|
|
5630
|
-
if (
|
|
5631
|
-
bw._nodeMap[
|
|
5694
|
+
// Register under UUID class token
|
|
5695
|
+
if (uuid) {
|
|
5696
|
+
bw._nodeMap[uuid] = el;
|
|
5632
5697
|
}
|
|
5633
5698
|
// Register under id attribute
|
|
5634
5699
|
var htmlId = el.getAttribute ? el.getAttribute('id') : null;
|
|
@@ -5644,13 +5709,13 @@
|
|
|
5644
5709
|
* through bitwrench APIs.
|
|
5645
5710
|
*
|
|
5646
5711
|
* @param {Element} el - DOM element to deregister
|
|
5647
|
-
* @param {string} [
|
|
5712
|
+
* @param {string} [uuid] - bw_uuid_* class token to remove
|
|
5648
5713
|
* @category Internal
|
|
5649
5714
|
*/
|
|
5650
|
-
bw._deregisterNode = function (el,
|
|
5651
|
-
// Remove
|
|
5652
|
-
if (
|
|
5653
|
-
delete bw._nodeMap[
|
|
5715
|
+
bw._deregisterNode = function (el, uuid) {
|
|
5716
|
+
// Remove UUID class entry
|
|
5717
|
+
if (uuid) {
|
|
5718
|
+
delete bw._nodeMap[uuid];
|
|
5654
5719
|
}
|
|
5655
5720
|
// Remove id attribute entry
|
|
5656
5721
|
var htmlId = el && el.getAttribute ? el.getAttribute('id') : null;
|
|
@@ -5663,6 +5728,13 @@
|
|
|
5663
5728
|
// bw.assignUUID() / bw.getUUID() — Explicit UUID addressing for TACO objects
|
|
5664
5729
|
// ===================================================================================
|
|
5665
5730
|
|
|
5731
|
+
/**
|
|
5732
|
+
* Marker class for elements with lifecycle hooks (mounted/unmount/render/state).
|
|
5733
|
+
* Used by cleanup() to find lifecycle-managed elements via querySelectorAll('.bw_lc').
|
|
5734
|
+
* @private
|
|
5735
|
+
*/
|
|
5736
|
+
var _BW_LC = 'bw_lc';
|
|
5737
|
+
|
|
5666
5738
|
/**
|
|
5667
5739
|
* Regex to match a bw_uuid_* token in a class string.
|
|
5668
5740
|
* @private
|
|
@@ -5848,20 +5920,10 @@
|
|
|
5848
5920
|
* // => '<div class="card"><p>Content here</p></div>'
|
|
5849
5921
|
*/
|
|
5850
5922
|
bw.html = function (taco) {
|
|
5851
|
-
var _attrs$class;
|
|
5852
5923
|
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
5853
5924
|
// Handle null/undefined
|
|
5854
5925
|
if (taco == null) return '';
|
|
5855
5926
|
|
|
5856
|
-
// Handle ComponentHandle — use its .taco
|
|
5857
|
-
if (taco && taco._bwComponent === true) {
|
|
5858
|
-
var compOptions = Object.assign({}, options);
|
|
5859
|
-
if (!compOptions.state && taco._state) {
|
|
5860
|
-
compOptions.state = taco._state;
|
|
5861
|
-
}
|
|
5862
|
-
return bw.html(taco.taco, compOptions);
|
|
5863
|
-
}
|
|
5864
|
-
|
|
5865
5927
|
// Handle arrays of TACOs
|
|
5866
5928
|
if (_isA(taco)) {
|
|
5867
5929
|
return taco.map(function (t) {
|
|
@@ -5874,24 +5936,6 @@
|
|
|
5874
5936
|
return taco.v;
|
|
5875
5937
|
}
|
|
5876
5938
|
|
|
5877
|
-
// Handle bw.when() markers
|
|
5878
|
-
if (taco && taco._bwWhen && options.state) {
|
|
5879
|
-
var whenExpr = taco.expr.replace(/^\$\{|\}$/g, '');
|
|
5880
|
-
var whenVal = options.compile ? bw._resolveTemplate('${' + whenExpr + '}', options.state, true) : bw._evaluatePath(options.state, whenExpr);
|
|
5881
|
-
var branch = whenVal ? taco.branches[0] : taco.branches[1] || null;
|
|
5882
|
-
return branch ? bw.html(branch, options) : '';
|
|
5883
|
-
}
|
|
5884
|
-
|
|
5885
|
-
// Handle bw.each() markers
|
|
5886
|
-
if (taco && taco._bwEach && options.state) {
|
|
5887
|
-
var eachExpr = taco.expr.replace(/^\$\{|\}$/g, '');
|
|
5888
|
-
var arr = bw._evaluatePath(options.state, eachExpr);
|
|
5889
|
-
if (!_isA(arr)) return '';
|
|
5890
|
-
return arr.map(function (item, idx) {
|
|
5891
|
-
return bw.html(taco.factory(item, idx), options);
|
|
5892
|
-
}).join('');
|
|
5893
|
-
}
|
|
5894
|
-
|
|
5895
5939
|
// Handle primitives and non-TACO objects
|
|
5896
5940
|
if (!_is(taco, 'object') || !taco.t) {
|
|
5897
5941
|
var str = options.raw ? String(taco) : bw.escapeHTML(String(taco));
|
|
@@ -5965,14 +6009,14 @@
|
|
|
5965
6009
|
}
|
|
5966
6010
|
}
|
|
5967
6011
|
|
|
5968
|
-
// Add
|
|
5969
|
-
if ((opts.mounted || opts.unmount) && !(
|
|
5970
|
-
var
|
|
6012
|
+
// Add bw_uuid + bw_lc classes if lifecycle hooks present
|
|
6013
|
+
if ((opts.mounted || opts.unmount) && !_UUID_RE.test(attrs["class"] || '')) {
|
|
6014
|
+
var uuid = bw.uuid('uuid');
|
|
5971
6015
|
attrStr = attrStr.replace(/class="([^"]*)"/, function (_match, classes) {
|
|
5972
|
-
return "class=\"".concat(classes, "
|
|
6016
|
+
return "class=\"".concat(classes, " ").concat(uuid, " ").concat(_BW_LC, "\"").trim();
|
|
5973
6017
|
});
|
|
5974
6018
|
if (!attrStr.includes('class=')) {
|
|
5975
|
-
attrStr += " class=\"
|
|
6019
|
+
attrStr += " class=\"".concat(uuid, " ").concat(_BW_LC, "\"");
|
|
5976
6020
|
}
|
|
5977
6021
|
}
|
|
5978
6022
|
|
|
@@ -6199,11 +6243,6 @@
|
|
|
6199
6243
|
return frag;
|
|
6200
6244
|
}
|
|
6201
6245
|
|
|
6202
|
-
// Handle ComponentHandle — extract .taco for DOM creation
|
|
6203
|
-
if (taco && taco._bwComponent === true) {
|
|
6204
|
-
return bw.createDOM(taco.taco, options);
|
|
6205
|
-
}
|
|
6206
|
-
|
|
6207
6246
|
// Handle text nodes
|
|
6208
6247
|
if (!_is(taco, 'object') || !taco.t) {
|
|
6209
6248
|
return document.createTextNode(String(taco));
|
|
@@ -6250,24 +6289,19 @@
|
|
|
6250
6289
|
}
|
|
6251
6290
|
|
|
6252
6291
|
// Add children, building _bw_refs for fast parent→child access.
|
|
6253
|
-
// Children with
|
|
6292
|
+
// Children with id attributes or bw_uuid_* classes get local refs on the parent,
|
|
6254
6293
|
// so o.render functions can access them without any DOM lookup.
|
|
6255
6294
|
if (content != null) {
|
|
6256
6295
|
if (_isA(content)) {
|
|
6257
6296
|
content.forEach(function (child) {
|
|
6258
6297
|
if (child != null) {
|
|
6259
|
-
// Handle ComponentHandle in content arrays (Level 2 children)
|
|
6260
|
-
if (child._bwComponent === true) {
|
|
6261
|
-
child.mount(el);
|
|
6262
|
-
return;
|
|
6263
|
-
}
|
|
6264
6298
|
var childEl = bw.createDOM(child, options);
|
|
6265
6299
|
el.appendChild(childEl);
|
|
6266
6300
|
// Build local refs for addressable children
|
|
6267
|
-
var
|
|
6268
|
-
if (
|
|
6301
|
+
var childRefId = child && child.a ? child.a.id || bw.getUUID(child) : null;
|
|
6302
|
+
if (childRefId) {
|
|
6269
6303
|
if (!el._bw_refs) el._bw_refs = {};
|
|
6270
|
-
el._bw_refs[
|
|
6304
|
+
el._bw_refs[childRefId] = childEl;
|
|
6271
6305
|
}
|
|
6272
6306
|
// Bubble up grandchild refs (flatten one level)
|
|
6273
6307
|
if (childEl._bw_refs) {
|
|
@@ -6283,16 +6317,13 @@
|
|
|
6283
6317
|
} else if (_is(content, 'object') && content.__bw_raw) {
|
|
6284
6318
|
// Raw HTML content — inject via innerHTML
|
|
6285
6319
|
el.innerHTML = content.v;
|
|
6286
|
-
} else if (content._bwComponent === true) {
|
|
6287
|
-
// Single ComponentHandle as content
|
|
6288
|
-
content.mount(el);
|
|
6289
6320
|
} else if (_is(content, 'object') && content.t) {
|
|
6290
6321
|
var childEl = bw.createDOM(content, options);
|
|
6291
6322
|
el.appendChild(childEl);
|
|
6292
|
-
var
|
|
6293
|
-
if (
|
|
6323
|
+
var childRefId = content.a ? content.a.id || bw.getUUID(content) : null;
|
|
6324
|
+
if (childRefId) {
|
|
6294
6325
|
if (!el._bw_refs) el._bw_refs = {};
|
|
6295
|
-
el._bw_refs[
|
|
6326
|
+
el._bw_refs[childRefId] = childEl;
|
|
6296
6327
|
}
|
|
6297
6328
|
if (childEl._bw_refs) {
|
|
6298
6329
|
if (!el._bw_refs) el._bw_refs = {};
|
|
@@ -6322,56 +6353,87 @@
|
|
|
6322
6353
|
|
|
6323
6354
|
// Handle lifecycle hooks and state
|
|
6324
6355
|
if (opts.mounted || opts.unmount || opts.render || opts.state) {
|
|
6325
|
-
|
|
6326
|
-
el.
|
|
6356
|
+
// Ensure element has a UUID class for identity
|
|
6357
|
+
var uuid = bw.getUUID(el) || bw.uuid('uuid');
|
|
6358
|
+
el.classList.add(uuid);
|
|
6359
|
+
el.classList.add(_BW_LC);
|
|
6327
6360
|
|
|
6328
|
-
// Register in node cache under
|
|
6329
|
-
bw._registerNode(el,
|
|
6361
|
+
// Register in node cache under UUID class
|
|
6362
|
+
bw._registerNode(el, uuid);
|
|
6330
6363
|
|
|
6331
6364
|
// Store state
|
|
6332
6365
|
if (opts.state) {
|
|
6333
6366
|
el._bw_state = opts.state;
|
|
6334
6367
|
}
|
|
6335
6368
|
|
|
6336
|
-
// o.render —
|
|
6369
|
+
// o.render — store the render function for bw.update()
|
|
6337
6370
|
if (opts.render) {
|
|
6338
6371
|
el._bw_render = opts.render;
|
|
6339
|
-
|
|
6340
|
-
_cw('bw.createDOM: o.render and o.mounted are mutually exclusive. o.render wins.');
|
|
6341
|
-
}
|
|
6372
|
+
}
|
|
6342
6373
|
|
|
6343
|
-
|
|
6344
|
-
|
|
6345
|
-
|
|
6346
|
-
|
|
6347
|
-
|
|
6348
|
-
|
|
6349
|
-
|
|
6350
|
-
}
|
|
6351
|
-
});
|
|
6352
|
-
}
|
|
6353
|
-
} else if (opts.mounted) {
|
|
6354
|
-
// Queue mounted callback (legacy pattern)
|
|
6374
|
+
// Determine what to call on mount:
|
|
6375
|
+
// - If o.mounted exists, call it (it can call el._bw_render() for initial render)
|
|
6376
|
+
// - Otherwise if o.render exists, auto-call it as a convenience shorthand
|
|
6377
|
+
var mountFn = opts.mounted || (opts.render ? function (mountEl) {
|
|
6378
|
+
opts.render(mountEl, mountEl._bw_state || {});
|
|
6379
|
+
} : null);
|
|
6380
|
+
if (mountFn) {
|
|
6355
6381
|
if (document.body.contains(el)) {
|
|
6356
|
-
|
|
6382
|
+
mountFn(el, el._bw_state || {});
|
|
6357
6383
|
} else {
|
|
6358
6384
|
requestAnimationFrame(function () {
|
|
6359
6385
|
if (document.body.contains(el)) {
|
|
6360
|
-
|
|
6386
|
+
mountFn(el, el._bw_state || {});
|
|
6361
6387
|
}
|
|
6362
6388
|
});
|
|
6363
6389
|
}
|
|
6364
6390
|
}
|
|
6365
6391
|
|
|
6366
|
-
// Store unmount callback
|
|
6392
|
+
// Store unmount callback keyed by UUID class
|
|
6367
6393
|
if (opts.unmount) {
|
|
6368
|
-
bw._unmountCallbacks.set(
|
|
6394
|
+
bw._unmountCallbacks.set(uuid, function () {
|
|
6369
6395
|
opts.unmount(el, el._bw_state || {});
|
|
6370
6396
|
});
|
|
6371
6397
|
}
|
|
6372
|
-
}
|
|
6373
|
-
|
|
6374
|
-
|
|
6398
|
+
}
|
|
6399
|
+
|
|
6400
|
+
// Component handle: attach methods to el.bw namespace
|
|
6401
|
+
if (opts.handle || opts.slots) {
|
|
6402
|
+
if (!el.bw) el.bw = {};
|
|
6403
|
+
|
|
6404
|
+
// Explicit handle methods: fn(el, ...args) -> el.bw.method(...args)
|
|
6405
|
+
if (opts.handle) {
|
|
6406
|
+
for (var hk in opts.handle) {
|
|
6407
|
+
if (_hop.call(opts.handle, hk)) {
|
|
6408
|
+
el.bw[hk] = opts.handle[hk].bind(null, el);
|
|
6409
|
+
}
|
|
6410
|
+
}
|
|
6411
|
+
}
|
|
6412
|
+
|
|
6413
|
+
// Slot declarations: auto-generate setX/getX pairs
|
|
6414
|
+
if (opts.slots) {
|
|
6415
|
+
for (var sk in opts.slots) {
|
|
6416
|
+
if (_hop.call(opts.slots, sk)) {
|
|
6417
|
+
(function (name, selector) {
|
|
6418
|
+
var cap = name.charAt(0).toUpperCase() + name.slice(1);
|
|
6419
|
+
el.bw['set' + cap] = function (value) {
|
|
6420
|
+
var t = el.querySelector(selector);
|
|
6421
|
+
if (!t) return;
|
|
6422
|
+
if (value != null && _typeof(value) === 'object' && value.t) {
|
|
6423
|
+
t.innerHTML = '';
|
|
6424
|
+
t.appendChild(bw.createDOM(value));
|
|
6425
|
+
} else {
|
|
6426
|
+
t.textContent = value != null ? String(value) : '';
|
|
6427
|
+
}
|
|
6428
|
+
};
|
|
6429
|
+
el.bw['get' + cap] = function () {
|
|
6430
|
+
var t = el.querySelector(selector);
|
|
6431
|
+
return t ? t.textContent : '';
|
|
6432
|
+
};
|
|
6433
|
+
})(sk, opts.slots[sk]);
|
|
6434
|
+
}
|
|
6435
|
+
}
|
|
6436
|
+
}
|
|
6375
6437
|
}
|
|
6376
6438
|
return el;
|
|
6377
6439
|
};
|
|
@@ -6417,7 +6479,7 @@
|
|
|
6417
6479
|
// the target is the mount point, not the content being replaced)
|
|
6418
6480
|
var savedState = targetEl._bw_state;
|
|
6419
6481
|
var savedRender = targetEl._bw_render;
|
|
6420
|
-
var
|
|
6482
|
+
var savedUuid = bw.getUUID(targetEl);
|
|
6421
6483
|
var savedSubs = targetEl._bw_subs;
|
|
6422
6484
|
|
|
6423
6485
|
// Temporarily remove _bw_subs so cleanup doesn't call them
|
|
@@ -6428,35 +6490,20 @@
|
|
|
6428
6490
|
// Restore the target's own state/render/subs after cleanup
|
|
6429
6491
|
if (savedState !== undefined) targetEl._bw_state = savedState;
|
|
6430
6492
|
if (savedRender) targetEl._bw_render = savedRender;
|
|
6431
|
-
if (
|
|
6432
|
-
|
|
6433
|
-
|
|
6434
|
-
bw._registerNode(targetEl, savedBwId);
|
|
6493
|
+
if (savedUuid) {
|
|
6494
|
+
// UUID class stays on element through cleanup; re-register in cache
|
|
6495
|
+
bw._registerNode(targetEl, savedUuid);
|
|
6435
6496
|
}
|
|
6436
6497
|
if (savedSubs) targetEl._bw_subs = savedSubs;
|
|
6437
6498
|
|
|
6438
6499
|
// Clear and mount new content
|
|
6439
6500
|
targetEl.innerHTML = '';
|
|
6440
6501
|
if (taco != null) {
|
|
6441
|
-
// Handle ComponentHandle (reactive components from bw.component())
|
|
6442
|
-
if (taco._bwComponent === true) {
|
|
6443
|
-
taco.mount(targetEl);
|
|
6444
|
-
}
|
|
6445
|
-
// Handle component handles (objects with element property)
|
|
6446
|
-
else if (taco.element instanceof Element) {
|
|
6447
|
-
targetEl.appendChild(taco.element);
|
|
6448
|
-
}
|
|
6449
6502
|
// Handle arrays
|
|
6450
|
-
|
|
6503
|
+
if (_isA(taco)) {
|
|
6451
6504
|
taco.forEach(function (t) {
|
|
6452
6505
|
if (t != null) {
|
|
6453
|
-
|
|
6454
|
-
t.mount(targetEl);
|
|
6455
|
-
} else if (t.element instanceof Element) {
|
|
6456
|
-
targetEl.appendChild(t.element);
|
|
6457
|
-
} else {
|
|
6458
|
-
targetEl.appendChild(bw.createDOM(t, options));
|
|
6459
|
-
}
|
|
6506
|
+
targetEl.appendChild(bw.createDOM(t, options));
|
|
6460
6507
|
}
|
|
6461
6508
|
});
|
|
6462
6509
|
}
|
|
@@ -6468,197 +6515,40 @@
|
|
|
6468
6515
|
return targetEl;
|
|
6469
6516
|
};
|
|
6470
6517
|
|
|
6471
|
-
|
|
6472
|
-
|
|
6473
|
-
|
|
6474
|
-
|
|
6475
|
-
|
|
6476
|
-
|
|
6477
|
-
* @param {Object} handle - Component handle
|
|
6478
|
-
* @param {Object} props - Initial props
|
|
6479
|
-
* @returns {Object} Compiled props object with getters/setters
|
|
6480
|
-
* @category DOM Generation
|
|
6481
|
-
*/
|
|
6482
|
-
bw.compileProps = function (handle) {
|
|
6483
|
-
var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
6484
|
-
var compiledProps = {};
|
|
6485
|
-
_keys(props).forEach(function (key) {
|
|
6486
|
-
// Create getter/setter for each prop
|
|
6487
|
-
Object.defineProperty(compiledProps, key, {
|
|
6488
|
-
get: function get() {
|
|
6489
|
-
return handle._props[key];
|
|
6490
|
-
},
|
|
6491
|
-
set: function set(value) {
|
|
6492
|
-
var oldValue = handle._props[key];
|
|
6493
|
-
if (oldValue !== value) {
|
|
6494
|
-
handle._props[key] = value;
|
|
6495
|
-
// Trigger update if prop changed
|
|
6496
|
-
if (handle.onPropChange) {
|
|
6497
|
-
handle.onPropChange(key, value, oldValue);
|
|
6498
|
-
}
|
|
6499
|
-
}
|
|
6500
|
-
},
|
|
6501
|
-
enumerable: true,
|
|
6502
|
-
configurable: true
|
|
6503
|
-
});
|
|
6504
|
-
});
|
|
6505
|
-
return compiledProps;
|
|
6518
|
+
// Deprecation stubs for removed ComponentHandle APIs
|
|
6519
|
+
bw.compileProps = function () {
|
|
6520
|
+
throw new Error('bw.compileProps() removed in v2.0.19. Use o.handle/o.slots instead.');
|
|
6521
|
+
};
|
|
6522
|
+
bw.renderComponent = function () {
|
|
6523
|
+
throw new Error('bw.renderComponent() removed in v2.0.19. Use bw.mount() with o.handle/o.slots instead.');
|
|
6506
6524
|
};
|
|
6507
6525
|
|
|
6508
6526
|
/**
|
|
6509
|
-
*
|
|
6510
|
-
*
|
|
6511
|
-
*
|
|
6512
|
-
* and a destroy method. Used internally by `bw.createCard()`, `bw.createTable()`, etc.
|
|
6527
|
+
* Mount a TACO into a target element and return the created root element.
|
|
6528
|
+
* Like bw.DOM() but returns the root element of the TACO (not the container),
|
|
6529
|
+
* giving direct access to el.bw handle methods.
|
|
6513
6530
|
*
|
|
6514
|
-
* @param {
|
|
6515
|
-
* @param {Object}
|
|
6516
|
-
* @
|
|
6531
|
+
* @param {string|Element} target - CSS selector or DOM element
|
|
6532
|
+
* @param {Object} taco - TACO to render
|
|
6533
|
+
* @param {Object} [options] - Mount options
|
|
6534
|
+
* @returns {Element} The created root element
|
|
6517
6535
|
* @category DOM Generation
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
|
|
6528
|
-
|
|
6529
|
-
|
|
6530
|
-
|
|
6531
|
-
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
// Get compiled props with getters/setters
|
|
6535
|
-
get props() {
|
|
6536
|
-
if (!this._compiledProps) {
|
|
6537
|
-
this._compiledProps = bw.compileProps(this, this._props);
|
|
6538
|
-
}
|
|
6539
|
-
return this._compiledProps;
|
|
6540
|
-
},
|
|
6541
|
-
/**
|
|
6542
|
-
* Query all matching elements within this component
|
|
6543
|
-
* @param {string} selector - CSS selector
|
|
6544
|
-
* @returns {NodeList} Matching elements
|
|
6545
|
-
*/
|
|
6546
|
-
$: function $(selector) {
|
|
6547
|
-
return this.element.querySelectorAll(selector);
|
|
6548
|
-
},
|
|
6549
|
-
/**
|
|
6550
|
-
* Query the first matching element within this component
|
|
6551
|
-
* @param {string} selector - CSS selector
|
|
6552
|
-
* @returns {Element|null} First matching element or null
|
|
6553
|
-
*/
|
|
6554
|
-
$first: function $first(selector) {
|
|
6555
|
-
return this.element.querySelector(selector);
|
|
6556
|
-
},
|
|
6557
|
-
/**
|
|
6558
|
-
* Update component with new props and re-render in place
|
|
6559
|
-
* @param {Object} newProps - Properties to merge into current props
|
|
6560
|
-
* @returns {Object} this handle (for chaining)
|
|
6561
|
-
*/
|
|
6562
|
-
update: function update(newProps) {
|
|
6563
|
-
// Update internal props
|
|
6564
|
-
Object.assign(this._props, newProps);
|
|
6565
|
-
|
|
6566
|
-
// Rebuild TACO with new props
|
|
6567
|
-
var newTaco = _objectSpread2(_objectSpread2({}, this.taco), {}, {
|
|
6568
|
-
a: _objectSpread2(_objectSpread2({}, this.taco.a), newProps)
|
|
6569
|
-
});
|
|
6570
|
-
var newElement = bw.createDOM(newTaco, options);
|
|
6571
|
-
|
|
6572
|
-
// Replace in DOM
|
|
6573
|
-
this.element.replaceWith(newElement);
|
|
6574
|
-
this.element = newElement;
|
|
6575
|
-
this.taco = newTaco;
|
|
6576
|
-
return this;
|
|
6577
|
-
},
|
|
6578
|
-
/**
|
|
6579
|
-
* Re-render the component from its current TACO, replacing the DOM element
|
|
6580
|
-
* @returns {Object} this handle (for chaining)
|
|
6581
|
-
*/
|
|
6582
|
-
render: function render() {
|
|
6583
|
-
var newElement = bw.createDOM(this.taco, options);
|
|
6584
|
-
this.element.replaceWith(newElement);
|
|
6585
|
-
this.element = newElement;
|
|
6586
|
-
return this;
|
|
6587
|
-
},
|
|
6588
|
-
/**
|
|
6589
|
-
* Called when a compiled prop value changes. Override to customize behavior.
|
|
6590
|
-
* Default implementation triggers a full re-render.
|
|
6591
|
-
* @param {string} key - Property name that changed
|
|
6592
|
-
* @param {*} newValue - New property value
|
|
6593
|
-
* @param {*} oldValue - Previous property value
|
|
6594
|
-
*/
|
|
6595
|
-
onPropChange: function onPropChange(_key, _newValue, _oldValue) {
|
|
6596
|
-
// Auto re-render on prop change by default
|
|
6597
|
-
this.render();
|
|
6598
|
-
},
|
|
6599
|
-
// State management
|
|
6600
|
-
get state() {
|
|
6601
|
-
return this._state;
|
|
6602
|
-
},
|
|
6603
|
-
set state(newState) {
|
|
6604
|
-
this._state = newState;
|
|
6605
|
-
this.render();
|
|
6606
|
-
},
|
|
6607
|
-
/**
|
|
6608
|
-
* Merge state updates and re-render the component
|
|
6609
|
-
* @param {Object} updates - State properties to merge
|
|
6610
|
-
* @returns {Object} this handle (for chaining)
|
|
6611
|
-
*/
|
|
6612
|
-
setState: function setState(updates) {
|
|
6613
|
-
Object.assign(this._state, updates);
|
|
6614
|
-
this.render();
|
|
6615
|
-
return this;
|
|
6616
|
-
},
|
|
6617
|
-
/**
|
|
6618
|
-
* Register a child component under a name for later retrieval
|
|
6619
|
-
* @param {string} name - Child name key
|
|
6620
|
-
* @param {Object} component - Child component handle
|
|
6621
|
-
* @returns {Object} this handle (for chaining)
|
|
6622
|
-
*/
|
|
6623
|
-
addChild: function addChild(name, component) {
|
|
6624
|
-
this._children[name] = component;
|
|
6625
|
-
return this;
|
|
6626
|
-
},
|
|
6627
|
-
/**
|
|
6628
|
-
* Retrieve a registered child component by name
|
|
6629
|
-
* @param {string} name - Child name key
|
|
6630
|
-
* @returns {Object|undefined} Child component handle
|
|
6631
|
-
*/
|
|
6632
|
-
getChild: function getChild(name) {
|
|
6633
|
-
return this._children[name];
|
|
6634
|
-
},
|
|
6635
|
-
/**
|
|
6636
|
-
* Destroy this component and all registered children
|
|
6637
|
-
*
|
|
6638
|
-
* Calls destroy() recursively on children, runs bw.cleanup(),
|
|
6639
|
-
* removes the element from DOM, and clears all internal references.
|
|
6640
|
-
*/
|
|
6641
|
-
destroy: function destroy() {
|
|
6642
|
-
// Destroy children first
|
|
6643
|
-
Object.values(this._children).forEach(function (child) {
|
|
6644
|
-
if (child && child.destroy) child.destroy();
|
|
6645
|
-
});
|
|
6646
|
-
|
|
6647
|
-
// Clean up this component
|
|
6648
|
-
bw.cleanup(this.element);
|
|
6649
|
-
this.element.remove();
|
|
6650
|
-
|
|
6651
|
-
// Clear references
|
|
6652
|
-
this._children = {};
|
|
6653
|
-
this._props = {};
|
|
6654
|
-
this._state = {};
|
|
6655
|
-
this._compiledProps = null;
|
|
6656
|
-
}
|
|
6657
|
-
};
|
|
6658
|
-
|
|
6659
|
-
// Store handle reference on element
|
|
6660
|
-
element._bwHandle = handle;
|
|
6661
|
-
return handle;
|
|
6536
|
+
* @example
|
|
6537
|
+
* var el = bw.mount('#app', bw.makeCarousel({ items: slides }));
|
|
6538
|
+
* el.bw.goToSlide(2);
|
|
6539
|
+
* el.bw.next();
|
|
6540
|
+
*/
|
|
6541
|
+
bw.mount = function (target, taco, options) {
|
|
6542
|
+
var container = _is(target, 'string') ? bw.$(target)[0] : target;
|
|
6543
|
+
if (!container) {
|
|
6544
|
+
_cw('bw.mount: target not found');
|
|
6545
|
+
return null;
|
|
6546
|
+
}
|
|
6547
|
+
bw.cleanup(container);
|
|
6548
|
+
container.innerHTML = '';
|
|
6549
|
+
var el = bw.createDOM(taco, options || {});
|
|
6550
|
+
container.appendChild(el);
|
|
6551
|
+
return el;
|
|
6662
6552
|
};
|
|
6663
6553
|
|
|
6664
6554
|
/**
|
|
@@ -6679,32 +6569,27 @@
|
|
|
6679
6569
|
bw.cleanup = function (element) {
|
|
6680
6570
|
if (!bw._isBrowser || !element) return;
|
|
6681
6571
|
|
|
6682
|
-
// Deregister UUID classes from node cache
|
|
6683
|
-
// Covers elements that have UUID but no data-bw_id
|
|
6684
|
-
var selfUuidMatch = element.className && element.className.match(_UUID_RE);
|
|
6685
|
-
if (selfUuidMatch) delete bw._nodeMap[selfUuidMatch[0]];
|
|
6572
|
+
// Deregister UUID classes from node cache for non-lifecycle UUID elements
|
|
6686
6573
|
var uuidEls = element.querySelectorAll('[class*="bw_uuid_"]');
|
|
6687
6574
|
uuidEls.forEach(function (uel) {
|
|
6688
6575
|
var m = uel.className && uel.className.match(_UUID_RE);
|
|
6689
6576
|
if (m) delete bw._nodeMap[m[0]];
|
|
6690
6577
|
});
|
|
6691
6578
|
|
|
6692
|
-
// Find all elements
|
|
6693
|
-
var elements = element.querySelectorAll('
|
|
6579
|
+
// Find all lifecycle-managed elements (have bw_lc marker class)
|
|
6580
|
+
var elements = element.querySelectorAll('.' + _BW_LC);
|
|
6694
6581
|
elements.forEach(function (el) {
|
|
6695
|
-
var
|
|
6696
|
-
|
|
6697
|
-
|
|
6698
|
-
callback
|
|
6699
|
-
|
|
6700
|
-
|
|
6701
|
-
|
|
6702
|
-
// Deregister from node cache
|
|
6703
|
-
bw._deregisterNode(el, id);
|
|
6582
|
+
var uuid = bw.getUUID(el);
|
|
6583
|
+
if (uuid) {
|
|
6584
|
+
var callback = bw._unmountCallbacks.get(uuid);
|
|
6585
|
+
if (callback) {
|
|
6586
|
+
callback();
|
|
6587
|
+
bw._unmountCallbacks["delete"](uuid);
|
|
6588
|
+
}
|
|
6704
6589
|
|
|
6705
|
-
|
|
6706
|
-
|
|
6707
|
-
|
|
6590
|
+
// Deregister from node cache
|
|
6591
|
+
bw._deregisterNode(el, uuid);
|
|
6592
|
+
}
|
|
6708
6593
|
|
|
6709
6594
|
// Clean up pub/sub subscriptions tied to this element
|
|
6710
6595
|
if (el._bw_subs) {
|
|
@@ -6721,20 +6606,17 @@
|
|
|
6721
6606
|
});
|
|
6722
6607
|
|
|
6723
6608
|
// Check element itself
|
|
6724
|
-
var
|
|
6725
|
-
if (
|
|
6726
|
-
|
|
6609
|
+
var selfUuid = bw.getUUID(element);
|
|
6610
|
+
if (selfUuid) {
|
|
6611
|
+
delete bw._nodeMap[selfUuid];
|
|
6612
|
+
var callback = bw._unmountCallbacks.get(selfUuid);
|
|
6727
6613
|
if (callback) {
|
|
6728
6614
|
callback();
|
|
6729
|
-
bw._unmountCallbacks["delete"](
|
|
6615
|
+
bw._unmountCallbacks["delete"](selfUuid);
|
|
6730
6616
|
}
|
|
6731
6617
|
|
|
6732
6618
|
// Deregister from node cache
|
|
6733
|
-
bw._deregisterNode(element,
|
|
6734
|
-
|
|
6735
|
-
// Deregister UUID class from node cache
|
|
6736
|
-
var elemUuidMatch = element.className && element.className.match(_UUID_RE);
|
|
6737
|
-
if (elemUuidMatch) delete bw._nodeMap[elemUuidMatch[0]];
|
|
6619
|
+
bw._deregisterNode(element, selfUuid);
|
|
6738
6620
|
|
|
6739
6621
|
// Clean up pub/sub subscriptions tied to element itself
|
|
6740
6622
|
if (element._bw_subs) {
|
|
@@ -6746,12 +6628,13 @@
|
|
|
6746
6628
|
delete element._bw_state;
|
|
6747
6629
|
delete element._bw_render;
|
|
6748
6630
|
delete element._bw_refs;
|
|
6749
|
-
|
|
6750
|
-
//
|
|
6751
|
-
if (element.
|
|
6752
|
-
element.
|
|
6753
|
-
|
|
6754
|
-
|
|
6631
|
+
} else {
|
|
6632
|
+
// No UUID on element itself, but still check for _bw_subs (from bw.sub())
|
|
6633
|
+
if (element._bw_subs) {
|
|
6634
|
+
element._bw_subs.forEach(function (unsub) {
|
|
6635
|
+
unsub();
|
|
6636
|
+
});
|
|
6637
|
+
delete element._bw_subs;
|
|
6755
6638
|
}
|
|
6756
6639
|
}
|
|
6757
6640
|
};
|
|
@@ -6767,7 +6650,7 @@
|
|
|
6767
6650
|
* Calls `el._bw_render(el, state)` and emits `bw:statechange` so other
|
|
6768
6651
|
* components can react without tight coupling.
|
|
6769
6652
|
*
|
|
6770
|
-
* @param {string|Element} target - Element ID,
|
|
6653
|
+
* @param {string|Element} target - Element ID, bw_uuid_* class, CSS selector, or DOM element
|
|
6771
6654
|
* @returns {Element|null} The element, or null if not found / no render function
|
|
6772
6655
|
* @category State Management
|
|
6773
6656
|
* @see bw.patch
|
|
@@ -6792,7 +6675,7 @@
|
|
|
6792
6675
|
* Use `bw.patch()` for lightweight value updates (scores, labels, counters)
|
|
6793
6676
|
* and `bw.update()` for full structural re-renders.
|
|
6794
6677
|
*
|
|
6795
|
-
* @param {string|Element} id - Element ID,
|
|
6678
|
+
* @param {string|Element} id - Element ID, bw_uuid_* class, CSS selector, or DOM element.
|
|
6796
6679
|
* Uses node cache for O(1) lookup; falls back to DOM query on cache miss.
|
|
6797
6680
|
* @param {string|Object} content - New text content, or TACO object to replace children
|
|
6798
6681
|
* @param {string} [attr] - If provided, sets this attribute instead of content
|
|
@@ -6866,7 +6749,7 @@
|
|
|
6866
6749
|
* bubble by default so ancestor elements can listen. Use with `bw.on()` for
|
|
6867
6750
|
* DOM-scoped communication between components.
|
|
6868
6751
|
*
|
|
6869
|
-
* @param {string|Element} target - Element ID,
|
|
6752
|
+
* @param {string|Element} target - Element ID, bw_uuid_* class, CSS selector, or DOM element.
|
|
6870
6753
|
* Uses node cache for O(1) lookup; falls back to DOM query on cache miss.
|
|
6871
6754
|
* @param {string} eventName - Event name (will be prefixed with 'bw:')
|
|
6872
6755
|
* @param {*} [detail] - Data to pass with the event
|
|
@@ -6893,7 +6776,7 @@
|
|
|
6893
6776
|
* is the first argument so you don't need to destructure `e.detail`.
|
|
6894
6777
|
* Events bubble, so you can listen on an ancestor element.
|
|
6895
6778
|
*
|
|
6896
|
-
* @param {string|Element} target - Element ID,
|
|
6779
|
+
* @param {string|Element} target - Element ID, bw_uuid_* class, CSS selector, or DOM element.
|
|
6897
6780
|
* Uses node cache for O(1) lookup; falls back to DOM query on cache miss.
|
|
6898
6781
|
* @param {string} eventName - Event name (will be prefixed with 'bw:')
|
|
6899
6782
|
* @param {Function} handler - Called with (detail, event)
|
|
@@ -6995,10 +6878,12 @@
|
|
|
6995
6878
|
if (el) {
|
|
6996
6879
|
if (!el._bw_subs) el._bw_subs = [];
|
|
6997
6880
|
el._bw_subs.push(unsub);
|
|
6998
|
-
// Ensure element has
|
|
6999
|
-
if (!
|
|
7000
|
-
|
|
7001
|
-
|
|
6881
|
+
// Ensure element has UUID + bw_lc so bw.cleanup() finds it
|
|
6882
|
+
if (!bw.getUUID(el)) {
|
|
6883
|
+
el.classList.add(bw.uuid('uuid'));
|
|
6884
|
+
}
|
|
6885
|
+
if (!el.classList.contains(_BW_LC)) {
|
|
6886
|
+
el.classList.add(_BW_LC);
|
|
7002
6887
|
}
|
|
7003
6888
|
}
|
|
7004
6889
|
return unsub;
|
|
@@ -7221,1193 +7106,100 @@
|
|
|
7221
7106
|
return result;
|
|
7222
7107
|
};
|
|
7223
7108
|
|
|
7224
|
-
/**
|
|
7225
|
-
* Extract top-level state keys that an expression depends on.
|
|
7226
|
-
* @param {string} expr - Expression string
|
|
7227
|
-
* @param {string[]} stateKeys - Declared state keys
|
|
7228
|
-
* @returns {string[]} Matching dependency keys
|
|
7229
|
-
* @private
|
|
7230
|
-
*/
|
|
7231
|
-
bw._extractDeps = function (expr, stateKeys) {
|
|
7232
|
-
var deps = [];
|
|
7233
|
-
for (var i = 0; i < stateKeys.length; i++) {
|
|
7234
|
-
var key = stateKeys[i];
|
|
7235
|
-
// Match word boundary: key must be preceded by start/non-word and followed by non-word/end
|
|
7236
|
-
var re = new RegExp('(?:^|[^\\w$.])' + key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '(?:[^\\w$]|$)');
|
|
7237
|
-
if (re.test(expr) || expr === key || expr.indexOf(key + '.') === 0) {
|
|
7238
|
-
deps.push(key);
|
|
7239
|
-
}
|
|
7240
|
-
}
|
|
7241
|
-
return deps;
|
|
7242
|
-
};
|
|
7243
|
-
|
|
7244
7109
|
// ===================================================================================
|
|
7245
|
-
//
|
|
7110
|
+
// Deprecation stubs for removed ComponentHandle APIs (v2.0.19)
|
|
7246
7111
|
// ===================================================================================
|
|
7247
7112
|
|
|
7248
|
-
bw.
|
|
7249
|
-
bw.
|
|
7113
|
+
bw._extractDeps = undefined;
|
|
7114
|
+
bw._dirtyComponents = undefined;
|
|
7115
|
+
bw._flushScheduled = undefined;
|
|
7116
|
+
bw._scheduleFlush = undefined;
|
|
7117
|
+
bw._doFlush = undefined;
|
|
7118
|
+
bw._ComponentHandle = undefined;
|
|
7250
7119
|
|
|
7251
7120
|
/**
|
|
7252
|
-
*
|
|
7253
|
-
*
|
|
7121
|
+
* No-op flush (ComponentHandle removed in v2.0.19).
|
|
7122
|
+
* Kept as no-op for backward compatibility.
|
|
7123
|
+
* @category Component
|
|
7254
7124
|
*/
|
|
7255
|
-
bw.
|
|
7256
|
-
|
|
7257
|
-
bw.
|
|
7258
|
-
if (typeof Promise !== 'undefined') {
|
|
7259
|
-
Promise.resolve().then(bw._doFlush);
|
|
7260
|
-
} else {
|
|
7261
|
-
setTimeout(bw._doFlush, 0);
|
|
7262
|
-
}
|
|
7125
|
+
bw.flush = function () {};
|
|
7126
|
+
bw.when = function () {
|
|
7127
|
+
throw new Error('bw.when() removed in v2.0.19. Use conditional logic in o.render instead.');
|
|
7263
7128
|
};
|
|
7264
|
-
|
|
7265
|
-
|
|
7266
|
-
* Flush all dirty components. Deduplicates by _bwId.
|
|
7267
|
-
* @private
|
|
7268
|
-
*/
|
|
7269
|
-
bw._doFlush = function () {
|
|
7270
|
-
bw._flushScheduled = false;
|
|
7271
|
-
var queue = bw._dirtyComponents.slice();
|
|
7272
|
-
bw._dirtyComponents = [];
|
|
7273
|
-
// Deduplicate by _bwId
|
|
7274
|
-
var seen = {};
|
|
7275
|
-
for (var i = 0; i < queue.length; i++) {
|
|
7276
|
-
var comp = queue[i];
|
|
7277
|
-
if (!seen[comp._bwId]) {
|
|
7278
|
-
seen[comp._bwId] = true;
|
|
7279
|
-
comp._flush();
|
|
7280
|
-
}
|
|
7281
|
-
}
|
|
7129
|
+
bw.each = function () {
|
|
7130
|
+
throw new Error('bw.each() removed in v2.0.19. Use array mapping in o.render instead.');
|
|
7282
7131
|
};
|
|
7283
|
-
|
|
7284
|
-
|
|
7285
|
-
* Synchronous flush for testing and imperative code.
|
|
7286
|
-
* Forces immediate re-render of all dirty components.
|
|
7287
|
-
*
|
|
7288
|
-
* @category Component
|
|
7289
|
-
*/
|
|
7290
|
-
bw.flush = function () {
|
|
7291
|
-
bw._doFlush();
|
|
7132
|
+
bw.component = function () {
|
|
7133
|
+
throw new Error('bw.component() removed in v2.0.19. Use o.handle/o.slots on TACO options instead.');
|
|
7292
7134
|
};
|
|
7293
7135
|
|
|
7294
7136
|
// ===================================================================================
|
|
7295
|
-
//
|
|
7137
|
+
// bw.message() — SendMessage() for the web
|
|
7296
7138
|
// ===================================================================================
|
|
7297
7139
|
|
|
7298
7140
|
/**
|
|
7299
|
-
*
|
|
7300
|
-
*
|
|
7301
|
-
*
|
|
7141
|
+
* Dispatch a message to a component by UUID, CSS class, or selector.
|
|
7142
|
+
* Finds the element, looks up el.bw, and calls the named method.
|
|
7143
|
+
* This is the bitwrench equivalent of Win32 SendMessage(hwnd, msg, wParam, lParam).
|
|
7302
7144
|
*
|
|
7303
|
-
* @param {
|
|
7304
|
-
* @
|
|
7305
|
-
* @
|
|
7306
|
-
|
|
7307
|
-
|
|
7308
|
-
|
|
7309
|
-
|
|
7310
|
-
|
|
7311
|
-
|
|
7312
|
-
|
|
7313
|
-
|
|
7314
|
-
|
|
7315
|
-
this._state = {};
|
|
7316
|
-
if (o.state) {
|
|
7317
|
-
for (var k in o.state) {
|
|
7318
|
-
if (_hop.call(o.state, k)) {
|
|
7319
|
-
this._state[k] = o.state[k];
|
|
7320
|
-
}
|
|
7321
|
-
}
|
|
7322
|
-
}
|
|
7323
|
-
// Copy actions
|
|
7324
|
-
this._actions = {};
|
|
7325
|
-
if (o.actions) {
|
|
7326
|
-
for (var k2 in o.actions) {
|
|
7327
|
-
if (_hop.call(o.actions, k2)) {
|
|
7328
|
-
this._actions[k2] = o.actions[k2];
|
|
7329
|
-
}
|
|
7330
|
-
}
|
|
7331
|
-
}
|
|
7332
|
-
// Promote o.methods to handle API (MFC/Qt pattern: component owns its methods)
|
|
7333
|
-
this._methods = {};
|
|
7334
|
-
if (o.methods) {
|
|
7335
|
-
var self = this;
|
|
7336
|
-
for (var k3 in o.methods) {
|
|
7337
|
-
if (_hop.call(o.methods, k3)) {
|
|
7338
|
-
this._methods[k3] = o.methods[k3];
|
|
7339
|
-
(function (methodName, methodFn) {
|
|
7340
|
-
self[methodName] = function () {
|
|
7341
|
-
var args = [self].concat(Array.prototype.slice.call(arguments));
|
|
7342
|
-
return methodFn.apply(null, args);
|
|
7343
|
-
};
|
|
7344
|
-
})(k3, o.methods[k3]);
|
|
7345
|
-
}
|
|
7346
|
-
}
|
|
7347
|
-
}
|
|
7348
|
-
// User tag for addressing via bw.message()
|
|
7349
|
-
this._userTag = null;
|
|
7350
|
-
// Lifecycle hooks
|
|
7351
|
-
this._hooks = {
|
|
7352
|
-
willMount: o.willMount || null,
|
|
7353
|
-
mounted: o.mounted || null,
|
|
7354
|
-
willUpdate: o.willUpdate || null,
|
|
7355
|
-
onUpdate: o.onUpdate || o.updated || null,
|
|
7356
|
-
unmount: o.unmount || null,
|
|
7357
|
-
willDestroy: o.willDestroy || null
|
|
7358
|
-
};
|
|
7359
|
-
// Binding tracking
|
|
7360
|
-
this._bindings = [];
|
|
7361
|
-
this._dirtyKeys = {};
|
|
7362
|
-
this._scheduled = false;
|
|
7363
|
-
this._subs = [];
|
|
7364
|
-
this._eventListeners = [];
|
|
7365
|
-
this._registeredActions = [];
|
|
7366
|
-
this._prevValues = {};
|
|
7367
|
-
this._compile = !!o.compile;
|
|
7368
|
-
this._bw_refs = {};
|
|
7369
|
-
this._refCounter = 0;
|
|
7370
|
-
// Child component ownership (Bug #5)
|
|
7371
|
-
this._children = [];
|
|
7372
|
-
this._parent = null;
|
|
7373
|
-
// Factory metadata for BCCL rebuild (Bug #6)
|
|
7374
|
-
this._factory = taco._bwFactory || null;
|
|
7375
|
-
}
|
|
7376
|
-
|
|
7377
|
-
// Short alias for ComponentHandle.prototype (see alias block at top of file).
|
|
7378
|
-
// 28 method definitions × 25 chars = ~700B raw savings in minified output.
|
|
7379
|
-
var _chp = ComponentHandle.prototype;
|
|
7380
|
-
|
|
7381
|
-
// ── State Methods ──
|
|
7382
|
-
|
|
7383
|
-
/**
|
|
7384
|
-
* Get a state value. Dot-path supported: `get('user.name')`
|
|
7385
|
-
*/
|
|
7386
|
-
_chp.get = function (key) {
|
|
7387
|
-
return bw._evaluatePath(this._state, key);
|
|
7388
|
-
};
|
|
7389
|
-
|
|
7390
|
-
/**
|
|
7391
|
-
* Set a state value. Dot-path supported. Schedules re-render.
|
|
7392
|
-
* @param {string} key - State key (dot-path)
|
|
7393
|
-
* @param {*} value - New value
|
|
7394
|
-
* @param {Object} [opts] - Options. `{sync: true}` for immediate flush.
|
|
7395
|
-
*/
|
|
7396
|
-
_chp.set = function (key, value, opts) {
|
|
7397
|
-
// Dot-path set
|
|
7398
|
-
var parts = key.split('.');
|
|
7399
|
-
var obj = this._state;
|
|
7400
|
-
for (var i = 0; i < parts.length - 1; i++) {
|
|
7401
|
-
if (!_is(obj[parts[i]], 'object')) {
|
|
7402
|
-
if (bw.debug) _cw('bw.debug: set() — auto-creating intermediate "' + parts[i] + '" in path "' + key + '"');
|
|
7403
|
-
obj[parts[i]] = {};
|
|
7404
|
-
}
|
|
7405
|
-
obj = obj[parts[i]];
|
|
7406
|
-
}
|
|
7407
|
-
obj[parts[parts.length - 1]] = value;
|
|
7408
|
-
// Mark top-level key dirty
|
|
7409
|
-
this._dirtyKeys[parts[0]] = true;
|
|
7410
|
-
if (this.mounted) {
|
|
7411
|
-
if (opts && opts.sync) {
|
|
7412
|
-
this._flush();
|
|
7413
|
-
} else {
|
|
7414
|
-
this._scheduleDirty();
|
|
7415
|
-
}
|
|
7416
|
-
}
|
|
7417
|
-
};
|
|
7418
|
-
|
|
7419
|
-
/**
|
|
7420
|
-
* Get a shallow clone of the full state.
|
|
7145
|
+
* @param {string} target - Component UUID (bw_uuid_*), CSS class, or selector
|
|
7146
|
+
* @param {string} action - Method name to call on el.bw
|
|
7147
|
+
* @param {*} data - Data to pass to the method
|
|
7148
|
+
* @returns {boolean} True if message was dispatched successfully
|
|
7149
|
+
* @category Component
|
|
7150
|
+
* @example
|
|
7151
|
+
* bw.message('my_carousel', 'goToSlide', 2);
|
|
7152
|
+
* // Or from SSE handler:
|
|
7153
|
+
* es.onmessage = function(e) {
|
|
7154
|
+
* var msg = JSON.parse(e.data);
|
|
7155
|
+
* bw.message(msg.target, msg.action, msg.data);
|
|
7156
|
+
* };
|
|
7421
7157
|
*/
|
|
7422
|
-
|
|
7423
|
-
var
|
|
7424
|
-
|
|
7425
|
-
|
|
7426
|
-
|
|
7427
|
-
|
|
7158
|
+
bw.message = function (target, action, data) {
|
|
7159
|
+
var el = bw._el(target);
|
|
7160
|
+
if (!el) el = bw.$('.' + target)[0];
|
|
7161
|
+
if (!el || !el.bw || typeof el.bw[action] !== 'function') {
|
|
7162
|
+
_cw('bw.message: no handle method "' + action + '" on ' + target);
|
|
7163
|
+
return false;
|
|
7428
7164
|
}
|
|
7429
|
-
|
|
7165
|
+
el.bw[action](data);
|
|
7166
|
+
return true;
|
|
7430
7167
|
};
|
|
7431
7168
|
|
|
7432
|
-
|
|
7433
|
-
|
|
7434
|
-
|
|
7435
|
-
* @param {Object} [opts] - Options. `{sync: true}` for immediate flush.
|
|
7436
|
-
*/
|
|
7437
|
-
_chp.setState = function (updates, opts) {
|
|
7438
|
-
for (var k in updates) {
|
|
7439
|
-
if (_hop.call(updates, k)) {
|
|
7440
|
-
this._state[k] = updates[k];
|
|
7441
|
-
this._dirtyKeys[k] = true;
|
|
7442
|
-
}
|
|
7443
|
-
}
|
|
7444
|
-
if (this.mounted) {
|
|
7445
|
-
if (opts && opts.sync) {
|
|
7446
|
-
this._flush();
|
|
7447
|
-
} else {
|
|
7448
|
-
this._scheduleDirty();
|
|
7449
|
-
}
|
|
7450
|
-
}
|
|
7451
|
-
};
|
|
7169
|
+
// ===================================================================================
|
|
7170
|
+
// bw.apply() / bw.parseJSONFlex() — Server-driven UI protocol
|
|
7171
|
+
// ===================================================================================
|
|
7452
7172
|
|
|
7453
7173
|
/**
|
|
7454
|
-
*
|
|
7174
|
+
* Registry of named functions sent via register messages.
|
|
7175
|
+
* Populated by bw.apply({ type: 'register', name, body }).
|
|
7176
|
+
* Invoked by bw.apply({ type: 'call', name, args }).
|
|
7177
|
+
* @private
|
|
7455
7178
|
*/
|
|
7456
|
-
|
|
7457
|
-
var arr = this.get(key);
|
|
7458
|
-
var newArr = _isA(arr) ? arr.slice() : [];
|
|
7459
|
-
newArr.push(val);
|
|
7460
|
-
this.set(key, newArr);
|
|
7461
|
-
};
|
|
7179
|
+
bw._clientFunctions = {};
|
|
7462
7180
|
|
|
7463
7181
|
/**
|
|
7464
|
-
*
|
|
7182
|
+
* Whether exec messages are allowed. Set by bwclient connect opts.allowExec.
|
|
7183
|
+
* Default false — exec messages are rejected unless explicitly opted in.
|
|
7184
|
+
* @private
|
|
7465
7185
|
*/
|
|
7466
|
-
|
|
7467
|
-
var arr = this.get(key);
|
|
7468
|
-
var newArr = _isA(arr) ? arr.slice() : [];
|
|
7469
|
-
var args = [start, deleteCount].concat(Array.prototype.slice.call(arguments, 3));
|
|
7470
|
-
Array.prototype.splice.apply(newArr, args);
|
|
7471
|
-
this.set(key, newArr);
|
|
7472
|
-
};
|
|
7473
|
-
|
|
7474
|
-
// ── Scheduling ──
|
|
7475
|
-
|
|
7476
|
-
_chp._scheduleDirty = function () {
|
|
7477
|
-
if (!this._scheduled) {
|
|
7478
|
-
this._scheduled = true;
|
|
7479
|
-
bw._dirtyComponents.push(this);
|
|
7480
|
-
bw._scheduleFlush();
|
|
7481
|
-
}
|
|
7482
|
-
};
|
|
7483
|
-
|
|
7484
|
-
// ── Binding Compilation ──
|
|
7186
|
+
bw._allowExec = false;
|
|
7485
7187
|
|
|
7486
7188
|
/**
|
|
7487
|
-
*
|
|
7488
|
-
*
|
|
7489
|
-
*
|
|
7490
|
-
|
|
7491
|
-
|
|
7492
|
-
|
|
7493
|
-
|
|
7494
|
-
|
|
7495
|
-
|
|
7496
|
-
|
|
7497
|
-
|
|
7498
|
-
|
|
7499
|
-
|
|
7500
|
-
|
|
7501
|
-
var refId = 'bw_ref_' + self._refCounter++;
|
|
7502
|
-
var parsed = bw._parseBindings(taco.c);
|
|
7503
|
-
var deps = [];
|
|
7504
|
-
for (var j = 0; j < parsed.length; j++) {
|
|
7505
|
-
deps = deps.concat(bw._extractDeps(parsed[j].expr, stateKeys));
|
|
7506
|
-
}
|
|
7507
|
-
self._bindings.push({
|
|
7508
|
-
expr: taco.c,
|
|
7509
|
-
type: 'content',
|
|
7510
|
-
refId: refId,
|
|
7511
|
-
deps: deps,
|
|
7512
|
-
template: taco.c
|
|
7513
|
-
});
|
|
7514
|
-
// Inject data-bw_ref on the TACO for createDOM to pick up
|
|
7515
|
-
if (!taco.a) taco.a = {};
|
|
7516
|
-
taco.a['data-bw_ref'] = refId;
|
|
7517
|
-
}
|
|
7518
|
-
|
|
7519
|
-
// Check attributes for bindings
|
|
7520
|
-
if (taco.a) {
|
|
7521
|
-
for (var attrName in taco.a) {
|
|
7522
|
-
if (!_hop.call(taco.a, attrName)) continue;
|
|
7523
|
-
if (attrName === 'data-bw_ref') continue;
|
|
7524
|
-
var attrVal = taco.a[attrName];
|
|
7525
|
-
if (_is(attrVal, 'string') && attrVal.indexOf('${') >= 0) {
|
|
7526
|
-
var refId2 = 'bw_ref_' + self._refCounter++;
|
|
7527
|
-
var parsed2 = bw._parseBindings(attrVal);
|
|
7528
|
-
var deps2 = [];
|
|
7529
|
-
for (var j2 = 0; j2 < parsed2.length; j2++) {
|
|
7530
|
-
deps2 = deps2.concat(bw._extractDeps(parsed2[j2].expr, stateKeys));
|
|
7531
|
-
}
|
|
7532
|
-
self._bindings.push({
|
|
7533
|
-
expr: attrVal,
|
|
7534
|
-
type: 'attribute',
|
|
7535
|
-
attrName: attrName,
|
|
7536
|
-
refId: refId2,
|
|
7537
|
-
deps: deps2,
|
|
7538
|
-
template: attrVal
|
|
7539
|
-
});
|
|
7540
|
-
if (!taco.a) taco.a = {};
|
|
7541
|
-
taco.a['data-bw_ref'] = taco.a['data-bw_ref'] || refId2;
|
|
7542
|
-
// If multiple attribute bindings on same element, store additional marker
|
|
7543
|
-
if (taco.a['data-bw_ref'] !== refId2) {
|
|
7544
|
-
taco.a['data-bw_ref_' + attrName] = refId2;
|
|
7545
|
-
}
|
|
7546
|
-
}
|
|
7547
|
-
}
|
|
7548
|
-
}
|
|
7549
|
-
|
|
7550
|
-
// Recurse into children
|
|
7551
|
-
if (_isA(taco.c)) {
|
|
7552
|
-
for (var i = 0; i < taco.c.length; i++) {
|
|
7553
|
-
// Wrap string children with ${expr} in a span so patches target the span, not the parent
|
|
7554
|
-
if (_is(taco.c[i], 'string') && taco.c[i].indexOf('${') >= 0) {
|
|
7555
|
-
var mixedRefId = 'bw_ref_' + self._refCounter++;
|
|
7556
|
-
var mixedParsed = bw._parseBindings(taco.c[i]);
|
|
7557
|
-
var mixedDeps = [];
|
|
7558
|
-
for (var mi = 0; mi < mixedParsed.length; mi++) {
|
|
7559
|
-
mixedDeps = mixedDeps.concat(bw._extractDeps(mixedParsed[mi].expr, stateKeys));
|
|
7560
|
-
}
|
|
7561
|
-
self._bindings.push({
|
|
7562
|
-
expr: taco.c[i],
|
|
7563
|
-
type: 'content',
|
|
7564
|
-
refId: mixedRefId,
|
|
7565
|
-
deps: mixedDeps,
|
|
7566
|
-
template: taco.c[i]
|
|
7567
|
-
});
|
|
7568
|
-
// Replace string with a span wrapper so textContent targets the span only
|
|
7569
|
-
taco.c[i] = {
|
|
7570
|
-
t: 'span',
|
|
7571
|
-
a: {
|
|
7572
|
-
'data-bw_ref': mixedRefId,
|
|
7573
|
-
style: 'display:contents'
|
|
7574
|
-
},
|
|
7575
|
-
c: taco.c[i]
|
|
7576
|
-
};
|
|
7577
|
-
}
|
|
7578
|
-
if (_is(taco.c[i], 'object') && taco.c[i].t) {
|
|
7579
|
-
walkTaco(taco.c[i], path.concat(i));
|
|
7580
|
-
}
|
|
7581
|
-
// Handle bw.when/bw.each markers
|
|
7582
|
-
if (taco.c[i] && taco.c[i]._bwWhen) {
|
|
7583
|
-
var whenRefId = 'bw_ref_' + self._refCounter++;
|
|
7584
|
-
var whenDeps = bw._extractDeps(taco.c[i].expr.replace(/^\$\{|\}$/g, ''), stateKeys);
|
|
7585
|
-
self._bindings.push({
|
|
7586
|
-
expr: taco.c[i].expr,
|
|
7587
|
-
type: 'structural',
|
|
7588
|
-
subtype: 'when',
|
|
7589
|
-
refId: whenRefId,
|
|
7590
|
-
deps: whenDeps,
|
|
7591
|
-
branches: taco.c[i].branches,
|
|
7592
|
-
index: i,
|
|
7593
|
-
parentPath: path
|
|
7594
|
-
});
|
|
7595
|
-
taco.c[i]._refId = whenRefId;
|
|
7596
|
-
}
|
|
7597
|
-
if (taco.c[i] && taco.c[i]._bwEach) {
|
|
7598
|
-
var eachRefId = 'bw_ref_' + self._refCounter++;
|
|
7599
|
-
var eachDeps = bw._extractDeps(taco.c[i].expr.replace(/^\$\{|\}$/g, ''), stateKeys);
|
|
7600
|
-
self._bindings.push({
|
|
7601
|
-
expr: taco.c[i].expr,
|
|
7602
|
-
type: 'structural',
|
|
7603
|
-
subtype: 'each',
|
|
7604
|
-
refId: eachRefId,
|
|
7605
|
-
deps: eachDeps,
|
|
7606
|
-
factory: taco.c[i].factory,
|
|
7607
|
-
index: i,
|
|
7608
|
-
parentPath: path
|
|
7609
|
-
});
|
|
7610
|
-
taco.c[i]._refId = eachRefId;
|
|
7611
|
-
}
|
|
7612
|
-
}
|
|
7613
|
-
} else if (_is(taco.c, 'object') && taco.c.t) {
|
|
7614
|
-
walkTaco(taco.c, path.concat(0));
|
|
7615
|
-
}
|
|
7616
|
-
return taco;
|
|
7617
|
-
}
|
|
7618
|
-
walkTaco(this.taco, []);
|
|
7619
|
-
};
|
|
7620
|
-
|
|
7621
|
-
// ── DOM Reference Collection ──
|
|
7622
|
-
|
|
7623
|
-
/**
|
|
7624
|
-
* Build ref map from the live DOM after createDOM.
|
|
7625
|
-
* @private
|
|
7626
|
-
*/
|
|
7627
|
-
_chp._collectRefs = function () {
|
|
7628
|
-
this._bw_refs = {};
|
|
7629
|
-
if (!this.element) return;
|
|
7630
|
-
var els = this.element.querySelectorAll('[data-bw_ref]');
|
|
7631
|
-
for (var i = 0; i < els.length; i++) {
|
|
7632
|
-
this._bw_refs[els[i].getAttribute('data-bw_ref')] = els[i];
|
|
7633
|
-
}
|
|
7634
|
-
// Also check root element
|
|
7635
|
-
var rootRef = this.element.getAttribute && this.element.getAttribute('data-bw_ref');
|
|
7636
|
-
if (rootRef) {
|
|
7637
|
-
this._bw_refs[rootRef] = this.element;
|
|
7638
|
-
}
|
|
7639
|
-
};
|
|
7640
|
-
|
|
7641
|
-
// ── Lifecycle ──
|
|
7642
|
-
|
|
7643
|
-
/**
|
|
7644
|
-
* Mount the component into a parent DOM element.
|
|
7645
|
-
* Creates DOM, compiles bindings, registers actions, and calls lifecycle hooks.
|
|
7646
|
-
* @param {Element} parentEl - DOM element to mount into
|
|
7647
|
-
*/
|
|
7648
|
-
_chp.mount = function (parentEl) {
|
|
7649
|
-
// willMount hook
|
|
7650
|
-
if (this._hooks.willMount) this._hooks.willMount(this);
|
|
7651
|
-
|
|
7652
|
-
// Save original TACO for re-renders (structural changes clone from this)
|
|
7653
|
-
if (!this._originalTaco) {
|
|
7654
|
-
this._originalTaco = this.taco;
|
|
7655
|
-
}
|
|
7656
|
-
|
|
7657
|
-
// Deep-clone TACO so binding annotations don't mutate original.
|
|
7658
|
-
// Custom clone to preserve _bwWhen/_bwEach markers and their factory functions.
|
|
7659
|
-
this.taco = this._deepCloneTaco(this._originalTaco);
|
|
7660
|
-
|
|
7661
|
-
// Compile bindings (annotates TACO with data-bw_ref attributes)
|
|
7662
|
-
this._compileBindings();
|
|
7663
|
-
|
|
7664
|
-
// Prepare TACO: resolve initial binding values, evaluate when/each
|
|
7665
|
-
this._prepareTaco(this.taco);
|
|
7666
|
-
|
|
7667
|
-
// Register named actions in function registry
|
|
7668
|
-
var self = this;
|
|
7669
|
-
for (var actionName in this._actions) {
|
|
7670
|
-
if (_hop.call(this._actions, actionName)) {
|
|
7671
|
-
var registeredName = this._bwId + '_' + actionName;
|
|
7672
|
-
(function (aName) {
|
|
7673
|
-
bw.funcRegister(function (evt) {
|
|
7674
|
-
self._actions[aName](self, evt);
|
|
7675
|
-
}, registeredName);
|
|
7676
|
-
})(actionName);
|
|
7677
|
-
this._registeredActions.push(registeredName);
|
|
7678
|
-
}
|
|
7679
|
-
}
|
|
7680
|
-
|
|
7681
|
-
// Wire action names in onclick etc. to dispatch strings
|
|
7682
|
-
this._wireActions(this.taco);
|
|
7683
|
-
|
|
7684
|
-
// Create DOM (strip o before createDOM to prevent double lifecycle)
|
|
7685
|
-
var tacoForDOM = this._tacoForDOM(this.taco);
|
|
7686
|
-
this.element = bw.createDOM(tacoForDOM);
|
|
7687
|
-
this.element._bwComponentHandle = this;
|
|
7688
|
-
this.element.setAttribute('data-bw_comp_id', this._bwId);
|
|
7689
|
-
|
|
7690
|
-
// Restore o.render from original TACO (stripped by _tacoForDOM)
|
|
7691
|
-
if (this.taco.o && this.taco.o.render) {
|
|
7692
|
-
this.element._bw_render = this.taco.o.render;
|
|
7693
|
-
}
|
|
7694
|
-
if (this._userTag) {
|
|
7695
|
-
this.element.classList.add(this._userTag);
|
|
7696
|
-
}
|
|
7697
|
-
|
|
7698
|
-
// Append to parent
|
|
7699
|
-
parentEl.appendChild(this.element);
|
|
7700
|
-
|
|
7701
|
-
// Collect refs from live DOM
|
|
7702
|
-
this._collectRefs();
|
|
7703
|
-
|
|
7704
|
-
// Resolve initial bindings and apply to DOM
|
|
7705
|
-
this._resolveAndApplyAll();
|
|
7706
|
-
this.mounted = true;
|
|
7707
|
-
|
|
7708
|
-
// Scan for child ComponentHandles and link parent/child (Bug #5)
|
|
7709
|
-
var childEls = this.element.querySelectorAll('[data-bw_comp_id]');
|
|
7710
|
-
for (var ci = 0; ci < childEls.length; ci++) {
|
|
7711
|
-
var ch = childEls[ci]._bwComponentHandle;
|
|
7712
|
-
if (ch && ch !== this && !ch._parent) {
|
|
7713
|
-
ch._parent = this;
|
|
7714
|
-
this._children.push(ch);
|
|
7715
|
-
}
|
|
7716
|
-
}
|
|
7717
|
-
|
|
7718
|
-
// mounted hook (backward compat: fn.length === 2 wraps (el, state))
|
|
7719
|
-
if (this._hooks.mounted) {
|
|
7720
|
-
if (this._hooks.mounted.length === 2) {
|
|
7721
|
-
this._hooks.mounted(this.element, this.getState());
|
|
7722
|
-
} else {
|
|
7723
|
-
this._hooks.mounted(this);
|
|
7724
|
-
}
|
|
7725
|
-
}
|
|
7726
|
-
|
|
7727
|
-
// Invoke o.render on initial mount (if present)
|
|
7728
|
-
if (this.element._bw_render) {
|
|
7729
|
-
this.element._bw_render(this.element, this._state);
|
|
7730
|
-
}
|
|
7731
|
-
};
|
|
7732
|
-
|
|
7733
|
-
/**
|
|
7734
|
-
* Prepare TACO for initial render: resolve when/each markers.
|
|
7735
|
-
* @private
|
|
7736
|
-
*/
|
|
7737
|
-
_chp._prepareTaco = function (taco) {
|
|
7738
|
-
if (!_is(taco, 'object')) return;
|
|
7739
|
-
if (_isA(taco.c)) {
|
|
7740
|
-
for (var i = taco.c.length - 1; i >= 0; i--) {
|
|
7741
|
-
var child = taco.c[i];
|
|
7742
|
-
if (child && child._bwWhen) {
|
|
7743
|
-
var exprStr = child.expr.replace(/^\$\{|\}$/g, '');
|
|
7744
|
-
var val;
|
|
7745
|
-
if (this._compile) {
|
|
7746
|
-
try {
|
|
7747
|
-
val = new Function('state', 'with(state){return (' + exprStr + ');}')(this._state);
|
|
7748
|
-
} catch (e) {
|
|
7749
|
-
val = false;
|
|
7750
|
-
}
|
|
7751
|
-
} else {
|
|
7752
|
-
val = bw._evaluatePath(this._state, exprStr);
|
|
7753
|
-
}
|
|
7754
|
-
var branch = val ? child.branches[0] : child.branches[1] || null;
|
|
7755
|
-
if (branch) {
|
|
7756
|
-
// Wrap in a container so we can track it
|
|
7757
|
-
taco.c[i] = {
|
|
7758
|
-
t: 'span',
|
|
7759
|
-
a: {
|
|
7760
|
-
'data-bw_when': child._refId,
|
|
7761
|
-
style: 'display:contents'
|
|
7762
|
-
},
|
|
7763
|
-
c: branch
|
|
7764
|
-
};
|
|
7765
|
-
} else {
|
|
7766
|
-
taco.c[i] = {
|
|
7767
|
-
t: 'span',
|
|
7768
|
-
a: {
|
|
7769
|
-
'data-bw_when': child._refId,
|
|
7770
|
-
style: 'display:contents'
|
|
7771
|
-
},
|
|
7772
|
-
c: ''
|
|
7773
|
-
};
|
|
7774
|
-
}
|
|
7775
|
-
}
|
|
7776
|
-
if (child && child._bwEach) {
|
|
7777
|
-
var eachExprStr = child.expr.replace(/^\$\{|\}$/g, '');
|
|
7778
|
-
var arr = bw._evaluatePath(this._state, eachExprStr);
|
|
7779
|
-
var items = [];
|
|
7780
|
-
if (_isA(arr)) {
|
|
7781
|
-
for (var j = 0; j < arr.length; j++) {
|
|
7782
|
-
items.push(child.factory(arr[j], j));
|
|
7783
|
-
}
|
|
7784
|
-
}
|
|
7785
|
-
taco.c[i] = {
|
|
7786
|
-
t: 'span',
|
|
7787
|
-
a: {
|
|
7788
|
-
'data-bw_each': child._refId,
|
|
7789
|
-
style: 'display:contents'
|
|
7790
|
-
},
|
|
7791
|
-
c: items
|
|
7792
|
-
};
|
|
7793
|
-
}
|
|
7794
|
-
if (_is(taco.c[i], 'object') && taco.c[i].t) {
|
|
7795
|
-
this._prepareTaco(taco.c[i]);
|
|
7796
|
-
}
|
|
7797
|
-
}
|
|
7798
|
-
} else if (_is(taco.c, 'object') && taco.c.t) {
|
|
7799
|
-
this._prepareTaco(taco.c);
|
|
7800
|
-
}
|
|
7801
|
-
};
|
|
7802
|
-
|
|
7803
|
-
/**
|
|
7804
|
-
* Wire action name strings (in onclick etc.) to dispatch function calls.
|
|
7805
|
-
* @private
|
|
7806
|
-
*/
|
|
7807
|
-
_chp._wireActions = function (taco) {
|
|
7808
|
-
if (!_is(taco, 'object') || !taco.t) return;
|
|
7809
|
-
if (taco.a) {
|
|
7810
|
-
for (var key in taco.a) {
|
|
7811
|
-
if (!_hop.call(taco.a, key)) continue;
|
|
7812
|
-
if (key.startsWith('on') && _is(taco.a[key], 'string')) {
|
|
7813
|
-
var actionName = taco.a[key];
|
|
7814
|
-
if (actionName in this._actions) {
|
|
7815
|
-
var registeredName = this._bwId + '_' + actionName;
|
|
7816
|
-
// Replace string with actual function for createDOM event binding
|
|
7817
|
-
(function (rName) {
|
|
7818
|
-
taco.a[key] = function (evt) {
|
|
7819
|
-
bw.funcGetById(rName)(evt);
|
|
7820
|
-
};
|
|
7821
|
-
})(registeredName);
|
|
7822
|
-
}
|
|
7823
|
-
}
|
|
7824
|
-
}
|
|
7825
|
-
}
|
|
7826
|
-
if (_isA(taco.c)) {
|
|
7827
|
-
for (var i = 0; i < taco.c.length; i++) {
|
|
7828
|
-
this._wireActions(taco.c[i]);
|
|
7829
|
-
}
|
|
7830
|
-
} else if (_is(taco.c, 'object') && taco.c.t) {
|
|
7831
|
-
this._wireActions(taco.c);
|
|
7832
|
-
}
|
|
7833
|
-
};
|
|
7834
|
-
|
|
7835
|
-
/**
|
|
7836
|
-
* Deep-clone a TACO tree, preserving _bwWhen/_bwEach markers and their factories.
|
|
7837
|
-
* @private
|
|
7838
|
-
*/
|
|
7839
|
-
_chp._deepCloneTaco = function (taco) {
|
|
7840
|
-
if (taco == null) return taco;
|
|
7841
|
-
// Preserve _bwWhen / _bwEach markers (contain functions)
|
|
7842
|
-
if (taco._bwWhen) {
|
|
7843
|
-
return {
|
|
7844
|
-
_bwWhen: true,
|
|
7845
|
-
expr: taco.expr,
|
|
7846
|
-
branches: [this._deepCloneTaco(taco.branches[0]), taco.branches[1] ? this._deepCloneTaco(taco.branches[1]) : null],
|
|
7847
|
-
_refId: taco._refId
|
|
7848
|
-
};
|
|
7849
|
-
}
|
|
7850
|
-
if (taco._bwEach) {
|
|
7851
|
-
return {
|
|
7852
|
-
_bwEach: true,
|
|
7853
|
-
expr: taco.expr,
|
|
7854
|
-
factory: taco.factory,
|
|
7855
|
-
_refId: taco._refId
|
|
7856
|
-
};
|
|
7857
|
-
}
|
|
7858
|
-
if (!_is(taco, 'object') || !taco.t) return taco;
|
|
7859
|
-
var result = {
|
|
7860
|
-
t: taco.t
|
|
7861
|
-
};
|
|
7862
|
-
if (taco.a) {
|
|
7863
|
-
result.a = {};
|
|
7864
|
-
for (var k in taco.a) {
|
|
7865
|
-
if (_hop.call(taco.a, k)) result.a[k] = taco.a[k];
|
|
7866
|
-
}
|
|
7867
|
-
}
|
|
7868
|
-
if (taco.c != null) {
|
|
7869
|
-
if (_isA(taco.c)) {
|
|
7870
|
-
result.c = taco.c.map(function (child) {
|
|
7871
|
-
return this._deepCloneTaco(child);
|
|
7872
|
-
}.bind(this));
|
|
7873
|
-
} else if (_is(taco.c, 'object')) {
|
|
7874
|
-
result.c = this._deepCloneTaco(taco.c);
|
|
7875
|
-
} else {
|
|
7876
|
-
result.c = taco.c;
|
|
7877
|
-
}
|
|
7878
|
-
}
|
|
7879
|
-
if (taco.o) result.o = taco.o; // Keep o reference (not deep-cloned; hooks are functions)
|
|
7880
|
-
return result;
|
|
7881
|
-
};
|
|
7882
|
-
|
|
7883
|
-
/**
|
|
7884
|
-
* Create a copy of TACO suitable for createDOM (strips o to prevent double lifecycle).
|
|
7885
|
-
* @private
|
|
7886
|
-
*/
|
|
7887
|
-
_chp._tacoForDOM = function (taco) {
|
|
7888
|
-
if (!_is(taco, 'object') || !taco.t) return taco;
|
|
7889
|
-
var result = {
|
|
7890
|
-
t: taco.t
|
|
7891
|
-
};
|
|
7892
|
-
if (taco.a) result.a = taco.a;
|
|
7893
|
-
if (taco.c != null) {
|
|
7894
|
-
if (_isA(taco.c)) {
|
|
7895
|
-
result.c = taco.c.map(function (child) {
|
|
7896
|
-
return this._tacoForDOM(child);
|
|
7897
|
-
}.bind(this));
|
|
7898
|
-
} else if (_is(taco.c, 'object') && taco.c.t) {
|
|
7899
|
-
result.c = this._tacoForDOM(taco.c);
|
|
7900
|
-
} else {
|
|
7901
|
-
result.c = taco.c;
|
|
7902
|
-
}
|
|
7903
|
-
}
|
|
7904
|
-
// Intentionally strip o (no mounted/unmount/state/render on sub-elements)
|
|
7905
|
-
if (taco.o && (taco.o.mounted || taco.o.render || taco.o.unmount)) {
|
|
7906
|
-
_cw('bw: _tacoForDOM stripped o.mounted/render/unmount from child <' + taco.t + '>. Use onclick attribute or bw.component() for child interactivity.');
|
|
7907
|
-
}
|
|
7908
|
-
return result;
|
|
7909
|
-
};
|
|
7910
|
-
|
|
7911
|
-
/**
|
|
7912
|
-
* Unmount: remove from DOM, deactivate, preserve state for re-mount.
|
|
7913
|
-
*/
|
|
7914
|
-
_chp.unmount = function () {
|
|
7915
|
-
if (!this.mounted) return;
|
|
7916
|
-
|
|
7917
|
-
// unmount hook
|
|
7918
|
-
if (this._hooks.unmount) {
|
|
7919
|
-
this._hooks.unmount(this);
|
|
7920
|
-
}
|
|
7921
|
-
|
|
7922
|
-
// Remove DOM event listeners
|
|
7923
|
-
for (var i = 0; i < this._eventListeners.length; i++) {
|
|
7924
|
-
var l = this._eventListeners[i];
|
|
7925
|
-
if (this.element) {
|
|
7926
|
-
this.element.removeEventListener(l.event, l.handler);
|
|
7927
|
-
}
|
|
7928
|
-
}
|
|
7929
|
-
this._eventListeners = [];
|
|
7930
|
-
|
|
7931
|
-
// Unsubscribe pub/sub
|
|
7932
|
-
for (var j = 0; j < this._subs.length; j++) {
|
|
7933
|
-
this._subs[j]();
|
|
7934
|
-
}
|
|
7935
|
-
this._subs = [];
|
|
7936
|
-
|
|
7937
|
-
// Remove from DOM
|
|
7938
|
-
if (this.element && this.element.parentNode) {
|
|
7939
|
-
this.element.parentNode.removeChild(this.element);
|
|
7940
|
-
}
|
|
7941
|
-
this.mounted = false;
|
|
7942
|
-
// State preserved — can re-mount
|
|
7943
|
-
};
|
|
7944
|
-
|
|
7945
|
-
/**
|
|
7946
|
-
* Destroy: unmount + clear state + unregister actions.
|
|
7947
|
-
*/
|
|
7948
|
-
_chp.destroy = function () {
|
|
7949
|
-
// willDestroy hook
|
|
7950
|
-
if (this._hooks.willDestroy) {
|
|
7951
|
-
this._hooks.willDestroy(this);
|
|
7952
|
-
}
|
|
7953
|
-
|
|
7954
|
-
// Cascade destroy to children depth-first (Bug #5)
|
|
7955
|
-
for (var ci = this._children.length - 1; ci >= 0; ci--) {
|
|
7956
|
-
this._children[ci].destroy();
|
|
7957
|
-
}
|
|
7958
|
-
this._children = [];
|
|
7959
|
-
if (this._parent) {
|
|
7960
|
-
var idx = this._parent._children.indexOf(this);
|
|
7961
|
-
if (idx >= 0) this._parent._children.splice(idx, 1);
|
|
7962
|
-
this._parent = null;
|
|
7963
|
-
}
|
|
7964
|
-
this.unmount();
|
|
7965
|
-
|
|
7966
|
-
// Unregister actions from function registry
|
|
7967
|
-
for (var i = 0; i < this._registeredActions.length; i++) {
|
|
7968
|
-
bw.funcUnregister(this._registeredActions[i]);
|
|
7969
|
-
}
|
|
7970
|
-
this._registeredActions = [];
|
|
7971
|
-
|
|
7972
|
-
// Clear state
|
|
7973
|
-
this._state = {};
|
|
7974
|
-
this._bindings = [];
|
|
7975
|
-
this._bw_refs = {};
|
|
7976
|
-
this._prevValues = {};
|
|
7977
|
-
this._dirtyKeys = {};
|
|
7978
|
-
if (this.element) {
|
|
7979
|
-
delete this.element._bwComponentHandle;
|
|
7980
|
-
this.element = null;
|
|
7981
|
-
}
|
|
7982
|
-
};
|
|
7983
|
-
|
|
7984
|
-
// ── Flush & Binding Resolution ──
|
|
7985
|
-
|
|
7986
|
-
/**
|
|
7987
|
-
* Flush dirty state: resolve changed bindings and apply to DOM.
|
|
7988
|
-
* @private
|
|
7989
|
-
*/
|
|
7990
|
-
_chp._flush = function () {
|
|
7991
|
-
this._scheduled = false;
|
|
7992
|
-
var changedKeys = _keys(this._dirtyKeys);
|
|
7993
|
-
this._dirtyKeys = {};
|
|
7994
|
-
if (changedKeys.length === 0 || !this.mounted) return;
|
|
7995
|
-
|
|
7996
|
-
// Factory rebuild: if a BCCL factory exists and changed keys overlap factory props,
|
|
7997
|
-
// rebuild the TACO from the factory with merged state (Bug #6)
|
|
7998
|
-
if (this._factory) {
|
|
7999
|
-
var rebuildNeeded = false;
|
|
8000
|
-
for (var fi = 0; fi < changedKeys.length; fi++) {
|
|
8001
|
-
if (_hop.call(this._factory.props, changedKeys[fi])) {
|
|
8002
|
-
rebuildNeeded = true;
|
|
8003
|
-
break;
|
|
8004
|
-
}
|
|
8005
|
-
}
|
|
8006
|
-
if (rebuildNeeded) {
|
|
8007
|
-
var merged = {};
|
|
8008
|
-
for (var mk in this._factory.props) if (_hop.call(this._factory.props, mk)) merged[mk] = this._factory.props[mk];
|
|
8009
|
-
for (var sk in this._state) if (_hop.call(this._state, sk)) merged[sk] = this._state[sk];
|
|
8010
|
-
this._factory.props = merged;
|
|
8011
|
-
var newTaco = bw.make(this._factory.type, merged);
|
|
8012
|
-
newTaco._bwFactory = this._factory;
|
|
8013
|
-
this.taco = newTaco;
|
|
8014
|
-
this._originalTaco = this._deepCloneTaco(newTaco);
|
|
8015
|
-
this._render();
|
|
8016
|
-
if (this._hooks.onUpdate) this._hooks.onUpdate(this, changedKeys);
|
|
8017
|
-
return;
|
|
8018
|
-
}
|
|
8019
|
-
}
|
|
8020
|
-
|
|
8021
|
-
// willUpdate hook
|
|
8022
|
-
if (this._hooks.willUpdate) {
|
|
8023
|
-
this._hooks.willUpdate(this, changedKeys);
|
|
8024
|
-
}
|
|
8025
|
-
|
|
8026
|
-
// Check if any structural bindings are affected
|
|
8027
|
-
var needsFullRender = false;
|
|
8028
|
-
for (var i = 0; i < this._bindings.length; i++) {
|
|
8029
|
-
var b = this._bindings[i];
|
|
8030
|
-
if (b.type === 'structural') {
|
|
8031
|
-
for (var j = 0; j < b.deps.length; j++) {
|
|
8032
|
-
if (changedKeys.indexOf(b.deps[j]) >= 0) {
|
|
8033
|
-
needsFullRender = true;
|
|
8034
|
-
break;
|
|
8035
|
-
}
|
|
8036
|
-
}
|
|
8037
|
-
if (needsFullRender) break;
|
|
8038
|
-
}
|
|
8039
|
-
}
|
|
8040
|
-
if (needsFullRender) {
|
|
8041
|
-
this._render();
|
|
8042
|
-
} else {
|
|
8043
|
-
var patches = this._resolveBindings(changedKeys);
|
|
8044
|
-
this._applyPatches(patches);
|
|
8045
|
-
}
|
|
8046
|
-
|
|
8047
|
-
// onUpdate hook
|
|
8048
|
-
if (this._hooks.onUpdate) {
|
|
8049
|
-
this._hooks.onUpdate(this, changedKeys);
|
|
8050
|
-
}
|
|
8051
|
-
};
|
|
8052
|
-
|
|
8053
|
-
/**
|
|
8054
|
-
* Resolve bindings whose deps intersect with changedKeys.
|
|
8055
|
-
* Returns list of patches to apply.
|
|
8056
|
-
* @private
|
|
8057
|
-
*/
|
|
8058
|
-
_chp._resolveBindings = function (changedKeys) {
|
|
8059
|
-
var patches = [];
|
|
8060
|
-
for (var i = 0; i < this._bindings.length; i++) {
|
|
8061
|
-
var b = this._bindings[i];
|
|
8062
|
-
if (b.type === 'structural') continue;
|
|
8063
|
-
|
|
8064
|
-
// Check if any dep matches
|
|
8065
|
-
var affected = false;
|
|
8066
|
-
for (var j = 0; j < b.deps.length; j++) {
|
|
8067
|
-
if (changedKeys.indexOf(b.deps[j]) >= 0) {
|
|
8068
|
-
affected = true;
|
|
8069
|
-
break;
|
|
8070
|
-
}
|
|
8071
|
-
}
|
|
8072
|
-
if (!affected) continue;
|
|
8073
|
-
|
|
8074
|
-
// Evaluate
|
|
8075
|
-
var newVal = bw._resolveTemplate(b.template, this._state, this._compile);
|
|
8076
|
-
var prevKey = b.refId + '_' + (b.attrName || 'content');
|
|
8077
|
-
if (this._prevValues[prevKey] !== newVal) {
|
|
8078
|
-
this._prevValues[prevKey] = newVal;
|
|
8079
|
-
patches.push({
|
|
8080
|
-
refId: b.refId,
|
|
8081
|
-
type: b.type,
|
|
8082
|
-
attrName: b.attrName,
|
|
8083
|
-
value: newVal
|
|
8084
|
-
});
|
|
8085
|
-
}
|
|
8086
|
-
}
|
|
8087
|
-
return patches;
|
|
8088
|
-
};
|
|
8089
|
-
|
|
8090
|
-
/**
|
|
8091
|
-
* Apply patches to DOM.
|
|
8092
|
-
* @private
|
|
8093
|
-
*/
|
|
8094
|
-
_chp._applyPatches = function (patches) {
|
|
8095
|
-
for (var i = 0; i < patches.length; i++) {
|
|
8096
|
-
var p = patches[i];
|
|
8097
|
-
var el = this._bw_refs[p.refId];
|
|
8098
|
-
if (!el) {
|
|
8099
|
-
if (bw.debug) _cw('bw.debug: _applyPatches — ref "' + p.refId + '" not found in DOM');
|
|
8100
|
-
continue;
|
|
8101
|
-
}
|
|
8102
|
-
if (p.type === 'content') {
|
|
8103
|
-
el.textContent = p.value;
|
|
8104
|
-
} else if (p.type === 'attribute') {
|
|
8105
|
-
if (p.attrName === 'class') {
|
|
8106
|
-
el.className = p.value;
|
|
8107
|
-
} else {
|
|
8108
|
-
el.setAttribute(p.attrName, p.value);
|
|
8109
|
-
}
|
|
8110
|
-
}
|
|
8111
|
-
}
|
|
8112
|
-
};
|
|
8113
|
-
|
|
8114
|
-
/**
|
|
8115
|
-
* Resolve all bindings and apply (used for initial render).
|
|
8116
|
-
* @private
|
|
8117
|
-
*/
|
|
8118
|
-
_chp._resolveAndApplyAll = function () {
|
|
8119
|
-
var patches = [];
|
|
8120
|
-
for (var i = 0; i < this._bindings.length; i++) {
|
|
8121
|
-
var b = this._bindings[i];
|
|
8122
|
-
if (b.type === 'structural') continue;
|
|
8123
|
-
var newVal = bw._resolveTemplate(b.template, this._state, this._compile);
|
|
8124
|
-
var prevKey = b.refId + '_' + (b.attrName || 'content');
|
|
8125
|
-
this._prevValues[prevKey] = newVal;
|
|
8126
|
-
patches.push({
|
|
8127
|
-
refId: b.refId,
|
|
8128
|
-
type: b.type,
|
|
8129
|
-
attrName: b.attrName,
|
|
8130
|
-
value: newVal
|
|
8131
|
-
});
|
|
8132
|
-
}
|
|
8133
|
-
this._applyPatches(patches);
|
|
8134
|
-
};
|
|
8135
|
-
|
|
8136
|
-
/**
|
|
8137
|
-
* Full re-render for structural changes (when/each branch switches).
|
|
8138
|
-
* @private
|
|
8139
|
-
*/
|
|
8140
|
-
_chp._render = function () {
|
|
8141
|
-
if (!this.element || !this.element.parentNode) return;
|
|
8142
|
-
var parent = this.element.parentNode;
|
|
8143
|
-
var nextSibling = this.element.nextSibling;
|
|
8144
|
-
|
|
8145
|
-
// Remove old DOM
|
|
8146
|
-
parent.removeChild(this.element);
|
|
8147
|
-
|
|
8148
|
-
// Re-prepare TACO with current state (deep clone preserving functions)
|
|
8149
|
-
this.taco = this._deepCloneTaco(this._originalTaco || this.taco);
|
|
8150
|
-
|
|
8151
|
-
// Re-compile bindings and prepare
|
|
8152
|
-
this._compileBindings();
|
|
8153
|
-
this._prepareTaco(this.taco);
|
|
8154
|
-
this._wireActions(this.taco);
|
|
8155
|
-
var tacoForDOM = this._tacoForDOM(this.taco);
|
|
8156
|
-
this.element = bw.createDOM(tacoForDOM);
|
|
8157
|
-
this.element._bwComponentHandle = this;
|
|
8158
|
-
this.element.setAttribute('data-bw_comp_id', this._bwId);
|
|
8159
|
-
|
|
8160
|
-
// Re-insert at same position
|
|
8161
|
-
if (nextSibling) {
|
|
8162
|
-
parent.insertBefore(this.element, nextSibling);
|
|
8163
|
-
} else {
|
|
8164
|
-
parent.appendChild(this.element);
|
|
8165
|
-
}
|
|
8166
|
-
|
|
8167
|
-
// Re-collect refs and apply all bindings
|
|
8168
|
-
this._collectRefs();
|
|
8169
|
-
this._resolveAndApplyAll();
|
|
8170
|
-
};
|
|
8171
|
-
|
|
8172
|
-
// ── Event & Pub/Sub Methods ──
|
|
8173
|
-
|
|
8174
|
-
/**
|
|
8175
|
-
* Add a DOM event listener on the component's root element.
|
|
8176
|
-
* @param {string} event - Event name (e.g., 'click')
|
|
8177
|
-
* @param {Function} handler - Event handler
|
|
8178
|
-
*/
|
|
8179
|
-
_chp.on = function (event, handler) {
|
|
8180
|
-
if (this.element) {
|
|
8181
|
-
this.element.addEventListener(event, handler);
|
|
8182
|
-
}
|
|
8183
|
-
this._eventListeners.push({
|
|
8184
|
-
event: event,
|
|
8185
|
-
handler: handler
|
|
8186
|
-
});
|
|
8187
|
-
};
|
|
8188
|
-
|
|
8189
|
-
/**
|
|
8190
|
-
* Remove a DOM event listener.
|
|
8191
|
-
* @param {string} event - Event name
|
|
8192
|
-
* @param {Function} handler - Handler to remove
|
|
8193
|
-
*/
|
|
8194
|
-
_chp.off = function (event, handler) {
|
|
8195
|
-
if (this.element) {
|
|
8196
|
-
this.element.removeEventListener(event, handler);
|
|
8197
|
-
}
|
|
8198
|
-
this._eventListeners = this._eventListeners.filter(function (l) {
|
|
8199
|
-
return !(l.event === event && l.handler === handler);
|
|
8200
|
-
});
|
|
8201
|
-
};
|
|
8202
|
-
|
|
8203
|
-
/**
|
|
8204
|
-
* Subscribe to a pub/sub topic. Lifecycle-tied: auto-unsubs on destroy.
|
|
8205
|
-
* @param {string} topic - Topic name
|
|
8206
|
-
* @param {Function} handler - Handler function
|
|
8207
|
-
* @returns {Function} Unsubscribe function
|
|
8208
|
-
*/
|
|
8209
|
-
_chp.sub = function (topic, handler) {
|
|
8210
|
-
var unsub = bw.sub(topic, handler);
|
|
8211
|
-
this._subs.push(unsub);
|
|
8212
|
-
return unsub;
|
|
8213
|
-
};
|
|
8214
|
-
|
|
8215
|
-
/**
|
|
8216
|
-
* Call a named action.
|
|
8217
|
-
* @param {string} name - Action name
|
|
8218
|
-
* @param {...*} args - Arguments passed after comp
|
|
8219
|
-
*/
|
|
8220
|
-
_chp.action = function (name) {
|
|
8221
|
-
var fn = this._actions[name];
|
|
8222
|
-
if (!fn) {
|
|
8223
|
-
_cw('ComponentHandle.action: unknown action "' + name + '"');
|
|
8224
|
-
return;
|
|
8225
|
-
}
|
|
8226
|
-
var args = [this].concat(Array.prototype.slice.call(arguments, 1));
|
|
8227
|
-
return fn.apply(null, args);
|
|
8228
|
-
};
|
|
8229
|
-
|
|
8230
|
-
/**
|
|
8231
|
-
* querySelector within the component's DOM.
|
|
8232
|
-
* @param {string} sel - CSS selector
|
|
8233
|
-
* @returns {Element|null}
|
|
8234
|
-
*/
|
|
8235
|
-
_chp.select = function (sel) {
|
|
8236
|
-
return this.element ? this.element.querySelector(sel) : null;
|
|
8237
|
-
};
|
|
8238
|
-
|
|
8239
|
-
/**
|
|
8240
|
-
* querySelectorAll within the component's DOM.
|
|
8241
|
-
* @param {string} sel - CSS selector
|
|
8242
|
-
* @returns {Element[]}
|
|
8243
|
-
*/
|
|
8244
|
-
_chp.selectAll = function (sel) {
|
|
8245
|
-
if (!this.element) return [];
|
|
8246
|
-
return Array.prototype.slice.call(this.element.querySelectorAll(sel));
|
|
8247
|
-
};
|
|
8248
|
-
|
|
8249
|
-
/**
|
|
8250
|
-
* Tag this component with a user-defined ID for addressing via bw.message().
|
|
8251
|
-
* The tag is added as a CSS class on the root element (DOM IS the registry).
|
|
8252
|
-
* @param {string} tag - User-defined identifier (e.g. 'dashboard_prod_east')
|
|
8253
|
-
* @returns {ComponentHandle} this (for chaining)
|
|
8254
|
-
*/
|
|
8255
|
-
_chp.userTag = function (tag) {
|
|
8256
|
-
this._userTag = tag;
|
|
8257
|
-
if (this.element) {
|
|
8258
|
-
this.element.classList.add(tag);
|
|
8259
|
-
}
|
|
8260
|
-
return this;
|
|
8261
|
-
};
|
|
8262
|
-
|
|
8263
|
-
// Expose ComponentHandle on bw (for testing and advanced use)
|
|
8264
|
-
bw._ComponentHandle = ComponentHandle;
|
|
8265
|
-
|
|
8266
|
-
// ===================================================================================
|
|
8267
|
-
// Control Flow Helpers
|
|
8268
|
-
// ===================================================================================
|
|
8269
|
-
|
|
8270
|
-
/**
|
|
8271
|
-
* Conditional rendering helper.
|
|
8272
|
-
* Returns a marker object that ComponentHandle detects during binding compilation.
|
|
8273
|
-
* In static contexts (bw.html with state), evaluates immediately.
|
|
8274
|
-
*
|
|
8275
|
-
* @param {string} expr - Expression string like '${loggedIn}'
|
|
8276
|
-
* @param {Object} tacoTrue - TACO to render when truthy
|
|
8277
|
-
* @param {Object} [tacoFalse] - TACO to render when falsy
|
|
8278
|
-
* @returns {Object} Marker object with _bwWhen flag
|
|
8279
|
-
* @category Component
|
|
8280
|
-
*/
|
|
8281
|
-
bw.when = function (expr, tacoTrue, tacoFalse) {
|
|
8282
|
-
return {
|
|
8283
|
-
_bwWhen: true,
|
|
8284
|
-
expr: expr,
|
|
8285
|
-
branches: [tacoTrue, tacoFalse || null]
|
|
8286
|
-
};
|
|
8287
|
-
};
|
|
8288
|
-
|
|
8289
|
-
/**
|
|
8290
|
-
* List rendering helper.
|
|
8291
|
-
* Returns a marker object that ComponentHandle detects during binding compilation.
|
|
8292
|
-
*
|
|
8293
|
-
* @param {string} expr - Expression string like '${items}'
|
|
8294
|
-
* @param {Function} fn - Factory function(item, index) returning TACO
|
|
8295
|
-
* @returns {Object} Marker object with _bwEach flag
|
|
8296
|
-
* @category Component
|
|
8297
|
-
*/
|
|
8298
|
-
bw.each = function (expr, fn) {
|
|
8299
|
-
return {
|
|
8300
|
-
_bwEach: true,
|
|
8301
|
-
expr: expr,
|
|
8302
|
-
factory: fn
|
|
8303
|
-
};
|
|
8304
|
-
};
|
|
8305
|
-
|
|
8306
|
-
// ===================================================================================
|
|
8307
|
-
// bw.component() — Factory for ComponentHandle
|
|
8308
|
-
// ===================================================================================
|
|
8309
|
-
|
|
8310
|
-
/**
|
|
8311
|
-
* Create a ComponentHandle from a TACO definition.
|
|
8312
|
-
* The returned handle has .get(), .set(), .mount(), .destroy(), etc.
|
|
8313
|
-
*
|
|
8314
|
-
* @param {Object} taco - TACO definition with {t, a, c, o}
|
|
8315
|
-
* @returns {ComponentHandle} Reactive component handle
|
|
8316
|
-
* @category Component
|
|
8317
|
-
* @see bw.DOM
|
|
8318
|
-
* @example
|
|
8319
|
-
* var counter = bw.component({
|
|
8320
|
-
* t: 'div', c: [{ t: 'h3', c: 'Count: ${count}' }],
|
|
8321
|
-
* o: { state: { count: 0 } }
|
|
8322
|
-
* });
|
|
8323
|
-
* bw.DOM('#app', counter);
|
|
8324
|
-
* counter.set('count', 42); // DOM auto-updates
|
|
8325
|
-
*/
|
|
8326
|
-
bw.component = function (taco) {
|
|
8327
|
-
return new ComponentHandle(taco);
|
|
8328
|
-
};
|
|
8329
|
-
|
|
8330
|
-
// ===================================================================================
|
|
8331
|
-
// bw.message() — SendMessage() for the web
|
|
8332
|
-
// ===================================================================================
|
|
8333
|
-
|
|
8334
|
-
/**
|
|
8335
|
-
* Dispatch a message to a component by UUID or user tag.
|
|
8336
|
-
* Finds the component's DOM element, looks up its ComponentHandle,
|
|
8337
|
-
* and calls the named method. This is the bitwrench equivalent of
|
|
8338
|
-
* Win32 SendMessage(hwnd, msg, wParam, lParam).
|
|
8339
|
-
*
|
|
8340
|
-
* @param {string} target - Component UUID (bw_uuid_*), comp ID (data-bw_comp_id), or user tag (CSS class)
|
|
8341
|
-
* @param {string} action - Method name to call on the component
|
|
8342
|
-
* @param {*} data - Data to pass to the method
|
|
8343
|
-
* @returns {boolean} True if message was dispatched successfully
|
|
8344
|
-
* @category Component
|
|
8345
|
-
* @example
|
|
8346
|
-
* // Tag a component
|
|
8347
|
-
* myDash.userTag('dashboard_prod');
|
|
8348
|
-
* // Dispatch locally
|
|
8349
|
-
* bw.message('dashboard_prod', 'addAlert', { severity: 'warning', text: 'CPU spike' });
|
|
8350
|
-
* // Or from SSE handler:
|
|
8351
|
-
* es.onmessage = function(e) {
|
|
8352
|
-
* var msg = JSON.parse(e.data);
|
|
8353
|
-
* bw.message(msg.target, msg.action, msg.data);
|
|
8354
|
-
* };
|
|
8355
|
-
*/
|
|
8356
|
-
bw.message = function (target, action, data) {
|
|
8357
|
-
// Try bw._el() first (handles UUID class, nodeMap cache, getElementById)
|
|
8358
|
-
var el = bw._el(target);
|
|
8359
|
-
// Then try data-bw_comp_id attribute
|
|
8360
|
-
if (!el || !el._bwComponentHandle) {
|
|
8361
|
-
el = bw.$('[data-bw_comp_id="' + target + '"]')[0];
|
|
8362
|
-
}
|
|
8363
|
-
// Then try CSS class (user tag)
|
|
8364
|
-
if (!el || !el._bwComponentHandle) {
|
|
8365
|
-
el = bw.$('.' + target)[0];
|
|
8366
|
-
}
|
|
8367
|
-
if (!el || !el._bwComponentHandle) return false;
|
|
8368
|
-
var comp = el._bwComponentHandle;
|
|
8369
|
-
if (!_is(comp[action], 'function')) {
|
|
8370
|
-
_cw('bw.message: unknown action "' + action + '" on component ' + target);
|
|
8371
|
-
return false;
|
|
8372
|
-
}
|
|
8373
|
-
comp[action](data);
|
|
8374
|
-
return true;
|
|
8375
|
-
};
|
|
8376
|
-
|
|
8377
|
-
// ===================================================================================
|
|
8378
|
-
// bw.apply() / bw.parseJSONFlex() — Server-driven UI protocol
|
|
8379
|
-
// ===================================================================================
|
|
8380
|
-
|
|
8381
|
-
/**
|
|
8382
|
-
* Registry of named functions sent via register messages.
|
|
8383
|
-
* Populated by bw.apply({ type: 'register', name, body }).
|
|
8384
|
-
* Invoked by bw.apply({ type: 'call', name, args }).
|
|
8385
|
-
* @private
|
|
8386
|
-
*/
|
|
8387
|
-
bw._clientFunctions = {};
|
|
8388
|
-
|
|
8389
|
-
/**
|
|
8390
|
-
* Whether exec messages are allowed. Set by bwclient connect opts.allowExec.
|
|
8391
|
-
* Default false — exec messages are rejected unless explicitly opted in.
|
|
8392
|
-
* @private
|
|
8393
|
-
*/
|
|
8394
|
-
bw._allowExec = false;
|
|
8395
|
-
|
|
8396
|
-
/**
|
|
8397
|
-
* Parse a bwserve protocol message string, supporting both strict JSON
|
|
8398
|
-
* and r-prefixed relaxed JSON (single-quoted strings, trailing commas).
|
|
8399
|
-
*
|
|
8400
|
-
* The r-prefix format is designed for C/C++ string literals where
|
|
8401
|
-
* double-quote escaping is painful. The parser is a state machine
|
|
8402
|
-
* that walks character by character — not a regex replace.
|
|
8403
|
-
*
|
|
8404
|
-
* Escaping: apostrophes inside single-quoted values must be escaped
|
|
8405
|
-
* with backslash: r{'name':'Barry\'s room'}
|
|
8406
|
-
*
|
|
8407
|
-
* @param {string} str - JSON or r-prefixed relaxed JSON string
|
|
8408
|
-
* @returns {Object} Parsed message object
|
|
8409
|
-
* @throws {SyntaxError} If the string is not valid JSON or relaxed JSON
|
|
8410
|
-
* @category Core
|
|
7189
|
+
* Parse a bwserve protocol message string, supporting both strict JSON
|
|
7190
|
+
* and r-prefixed relaxed JSON (single-quoted strings, trailing commas).
|
|
7191
|
+
*
|
|
7192
|
+
* The r-prefix format is designed for C/C++ string literals where
|
|
7193
|
+
* double-quote escaping is painful. The parser is a state machine
|
|
7194
|
+
* that walks character by character — not a regex replace.
|
|
7195
|
+
*
|
|
7196
|
+
* Escaping: apostrophes inside single-quoted values must be escaped
|
|
7197
|
+
* with backslash: r{'name':'Barry\'s room'}
|
|
7198
|
+
*
|
|
7199
|
+
* @param {string} str - JSON or r-prefixed relaxed JSON string
|
|
7200
|
+
* @returns {Object} Parsed message object
|
|
7201
|
+
* @throws {SyntaxError} If the string is not valid JSON or relaxed JSON
|
|
7202
|
+
* @category Core
|
|
8411
7203
|
*/
|
|
8412
7204
|
bw.parseJSONFlex = function (str) {
|
|
8413
7205
|
str = (str || '').trim();
|
|
@@ -8577,142 +7369,32 @@
|
|
|
8577
7369
|
// ===================================================================================
|
|
8578
7370
|
|
|
8579
7371
|
/**
|
|
8580
|
-
* Inspect a
|
|
8581
|
-
* Works with DOM elements
|
|
8582
|
-
* Returns the ComponentHandle for console chaining.
|
|
7372
|
+
* Inspect a DOM element's bitwrench state, handle methods, and metadata.
|
|
7373
|
+
* Works with DOM elements or CSS selectors.
|
|
8583
7374
|
*
|
|
8584
|
-
* @param {string|Element
|
|
8585
|
-
* @returns {
|
|
7375
|
+
* @param {string|Element} target - Selector or DOM element
|
|
7376
|
+
* @returns {Element|null} The element, or null if not found
|
|
8586
7377
|
* @category Component
|
|
8587
7378
|
* @example
|
|
8588
|
-
*
|
|
7379
|
+
* bw.inspect('#my-carousel');
|
|
8589
7380
|
* bw.inspect($0);
|
|
8590
|
-
* // Or by selector:
|
|
8591
|
-
* var h = bw.inspect('#my-dashboard');
|
|
8592
|
-
* h.set('count', 99); // chain from returned handle
|
|
8593
7381
|
*/
|
|
8594
7382
|
bw.inspect = function (target) {
|
|
8595
|
-
var el = target;
|
|
8596
|
-
|
|
8597
|
-
|
|
8598
|
-
el = target.element;
|
|
8599
|
-
comp = target;
|
|
8600
|
-
} else {
|
|
8601
|
-
if (_is(target, 'string')) {
|
|
8602
|
-
el = bw.$(target)[0];
|
|
8603
|
-
}
|
|
8604
|
-
if (!el) {
|
|
8605
|
-
_cw('bw.inspect: element not found');
|
|
8606
|
-
return null;
|
|
8607
|
-
}
|
|
8608
|
-
comp = el._bwComponentHandle;
|
|
8609
|
-
}
|
|
8610
|
-
if (!comp) {
|
|
8611
|
-
_cl('bw.inspect: no ComponentHandle on this element');
|
|
8612
|
-
_cl(' Tag:', el.tagName);
|
|
8613
|
-
_cl(' Classes:', el.className);
|
|
8614
|
-
_cl(' _bw_state:', el._bw_state || '(none)');
|
|
7383
|
+
var el = _is(target, 'string') ? bw.$(target)[0] : target;
|
|
7384
|
+
if (!el) {
|
|
7385
|
+
_cw('bw.inspect: element not found');
|
|
8615
7386
|
return null;
|
|
8616
7387
|
}
|
|
8617
|
-
|
|
8618
|
-
|
|
8619
|
-
|
|
8620
|
-
|
|
8621
|
-
|
|
8622
|
-
console.group('Component: ' + comp._bwId);
|
|
8623
|
-
_cl('State:', comp._state);
|
|
8624
|
-
_cl('Bindings:', comp._bindings.length, '(deps:', deps, ')');
|
|
8625
|
-
_cl('Methods:', _keys(comp._methods));
|
|
8626
|
-
_cl('Actions:', _keys(comp._actions));
|
|
8627
|
-
_cl('User tag:', comp._userTag || '(none)');
|
|
8628
|
-
_cl('Mounted:', comp.mounted);
|
|
8629
|
-
_cl('Element:', comp.element);
|
|
7388
|
+
console.group('Element: ' + (bw.getUUID(el) || el.id || el.tagName));
|
|
7389
|
+
_cl('State:', el._bw_state || '(none)');
|
|
7390
|
+
_cl('Handle:', el.bw ? _keys(el.bw) : '(none)');
|
|
7391
|
+
_cl('Classes:', el.className);
|
|
7392
|
+
_cl('Refs:', el._bw_refs || '(none)');
|
|
8630
7393
|
console.groupEnd();
|
|
8631
|
-
return
|
|
7394
|
+
return el;
|
|
8632
7395
|
};
|
|
8633
|
-
|
|
8634
|
-
|
|
8635
|
-
// bw.compile() — Pre-compile TACO into optimized factory
|
|
8636
|
-
// ===================================================================================
|
|
8637
|
-
|
|
8638
|
-
/**
|
|
8639
|
-
* Pre-compile a TACO definition into a factory function.
|
|
8640
|
-
* The factory produces ComponentHandles with pre-compiled binding evaluators.
|
|
8641
|
-
*
|
|
8642
|
-
* Phase 1: validates API surface. Template cloning optimization deferred.
|
|
8643
|
-
*
|
|
8644
|
-
* @param {Object} taco - TACO definition
|
|
8645
|
-
* @returns {Function} Factory function(initialState?) → ComponentHandle
|
|
8646
|
-
* @category Component
|
|
8647
|
-
*/
|
|
8648
|
-
bw.compile = function (taco) {
|
|
8649
|
-
// Pre-extract all binding expressions
|
|
8650
|
-
var precompiled = [];
|
|
8651
|
-
function walkExpressions(node) {
|
|
8652
|
-
if (!_is(node, 'object')) return;
|
|
8653
|
-
if (_is(node.c, 'string') && node.c.indexOf('${') >= 0) {
|
|
8654
|
-
var parsed = bw._parseBindings(node.c);
|
|
8655
|
-
for (var i = 0; i < parsed.length; i++) {
|
|
8656
|
-
try {
|
|
8657
|
-
precompiled.push({
|
|
8658
|
-
expr: parsed[i].expr,
|
|
8659
|
-
fn: new Function('state', 'with(state){return (' + parsed[i].expr + ');}')
|
|
8660
|
-
});
|
|
8661
|
-
} catch (e) {
|
|
8662
|
-
precompiled.push({
|
|
8663
|
-
expr: parsed[i].expr,
|
|
8664
|
-
fn: function fn() {
|
|
8665
|
-
return '';
|
|
8666
|
-
}
|
|
8667
|
-
});
|
|
8668
|
-
}
|
|
8669
|
-
}
|
|
8670
|
-
}
|
|
8671
|
-
if (node.a) {
|
|
8672
|
-
for (var key in node.a) {
|
|
8673
|
-
if (_hop.call(node.a, key)) {
|
|
8674
|
-
var v = node.a[key];
|
|
8675
|
-
if (_is(v, 'string') && v.indexOf('${') >= 0) {
|
|
8676
|
-
var parsed2 = bw._parseBindings(v);
|
|
8677
|
-
for (var j = 0; j < parsed2.length; j++) {
|
|
8678
|
-
try {
|
|
8679
|
-
precompiled.push({
|
|
8680
|
-
expr: parsed2[j].expr,
|
|
8681
|
-
fn: new Function('state', 'with(state){return (' + parsed2[j].expr + ');}')
|
|
8682
|
-
});
|
|
8683
|
-
} catch (e2) {
|
|
8684
|
-
precompiled.push({
|
|
8685
|
-
expr: parsed2[j].expr,
|
|
8686
|
-
fn: function fn() {
|
|
8687
|
-
return '';
|
|
8688
|
-
}
|
|
8689
|
-
});
|
|
8690
|
-
}
|
|
8691
|
-
}
|
|
8692
|
-
}
|
|
8693
|
-
}
|
|
8694
|
-
}
|
|
8695
|
-
}
|
|
8696
|
-
if (_isA(node.c)) {
|
|
8697
|
-
for (var k = 0; k < node.c.length; k++) walkExpressions(node.c[k]);
|
|
8698
|
-
} else if (_is(node.c, 'object') && node.c.t) {
|
|
8699
|
-
walkExpressions(node.c);
|
|
8700
|
-
}
|
|
8701
|
-
}
|
|
8702
|
-
walkExpressions(taco);
|
|
8703
|
-
return function (initialState) {
|
|
8704
|
-
var handle = new ComponentHandle(taco);
|
|
8705
|
-
handle._compile = true;
|
|
8706
|
-
handle._precompiledBindings = precompiled;
|
|
8707
|
-
if (initialState) {
|
|
8708
|
-
for (var k in initialState) {
|
|
8709
|
-
if (_hop.call(initialState, k)) {
|
|
8710
|
-
handle._state[k] = initialState[k];
|
|
8711
|
-
}
|
|
8712
|
-
}
|
|
8713
|
-
}
|
|
8714
|
-
return handle;
|
|
8715
|
-
};
|
|
7396
|
+
bw.compile = function () {
|
|
7397
|
+
throw new Error('bw.compile() removed in v2.0.19. Use o.handle/o.slots on TACO options instead.');
|
|
8716
7398
|
};
|
|
8717
7399
|
|
|
8718
7400
|
/**
|
|
@@ -10018,7 +8700,7 @@
|
|
|
10018
8700
|
* handle.destroy();
|
|
10019
8701
|
*/
|
|
10020
8702
|
bw.render = function (element, position, taco) {
|
|
10021
|
-
var _taco$
|
|
8703
|
+
var _taco$o3, _taco$o4, _taco$o5;
|
|
10022
8704
|
// Get target element
|
|
10023
8705
|
var targetEl = _is(element, 'string') ? document.querySelector(element) : element;
|
|
10024
8706
|
if (!targetEl) {
|
|
@@ -10030,8 +8712,8 @@
|
|
|
10030
8712
|
};
|
|
10031
8713
|
}
|
|
10032
8714
|
|
|
10033
|
-
// Generate unique
|
|
10034
|
-
var componentId = ((_taco$
|
|
8715
|
+
// Generate unique UUID class if not provided
|
|
8716
|
+
var componentId = ((_taco$o3 = taco.o) === null || _taco$o3 === void 0 ? void 0 : _taco$o3.id) || bw.uuid('uuid');
|
|
10035
8717
|
|
|
10036
8718
|
// Create DOM element
|
|
10037
8719
|
var domElement;
|
|
@@ -10046,8 +8728,9 @@
|
|
|
10046
8728
|
};
|
|
10047
8729
|
}
|
|
10048
8730
|
|
|
10049
|
-
// Add component ID
|
|
10050
|
-
domElement.
|
|
8731
|
+
// Add component ID as class + lifecycle marker
|
|
8732
|
+
domElement.classList.add(componentId);
|
|
8733
|
+
domElement.classList.add(_BW_LC);
|
|
10051
8734
|
|
|
10052
8735
|
// Insert into DOM based on position
|
|
10053
8736
|
try {
|
|
@@ -10087,7 +8770,7 @@
|
|
|
10087
8770
|
status_code: 'success',
|
|
10088
8771
|
// Reference to original TACO
|
|
10089
8772
|
_taco: _objectSpread2({}, taco),
|
|
10090
|
-
_state: _objectSpread2({}, ((_taco$
|
|
8773
|
+
_state: _objectSpread2({}, ((_taco$o4 = taco.o) === null || _taco$o4 === void 0 ? void 0 : _taco$o4.state) || {}),
|
|
10091
8774
|
_mounted: true,
|
|
10092
8775
|
// Get DOM element
|
|
10093
8776
|
get element() {
|
|
@@ -10118,7 +8801,8 @@
|
|
|
10118
8801
|
|
|
10119
8802
|
// Re-render
|
|
10120
8803
|
var newElement = bw.createDOM(this._taco);
|
|
10121
|
-
newElement.
|
|
8804
|
+
newElement.classList.add(componentId);
|
|
8805
|
+
newElement.classList.add(_BW_LC);
|
|
10122
8806
|
|
|
10123
8807
|
// Replace in DOM
|
|
10124
8808
|
parent.replaceChild(newElement, this.element);
|
|
@@ -10245,7 +8929,7 @@
|
|
|
10245
8929
|
bw._componentRegistry.set(componentId, handle);
|
|
10246
8930
|
|
|
10247
8931
|
// Call mounted lifecycle
|
|
10248
|
-
if ((_taco$
|
|
8932
|
+
if ((_taco$o5 = taco.o) !== null && _taco$o5 !== void 0 && _taco$o5.mounted) {
|
|
10249
8933
|
taco.o.mounted(domElement, handle);
|
|
10250
8934
|
}
|
|
10251
8935
|
return handle;
|
|
@@ -10293,16 +8977,15 @@
|
|
|
10293
8977
|
// Variant class helper: bw.variantClass('primary') → 'bw_primary'
|
|
10294
8978
|
bw.variantClass = variantClass;
|
|
10295
8979
|
|
|
10296
|
-
// Create functions that return
|
|
8980
|
+
// Create functions that return DOM elements (createCard, createTable, etc.)
|
|
10297
8981
|
Object.entries(components).forEach(function (_ref11) {
|
|
10298
8982
|
var _ref12 = _slicedToArray(_ref11, 2),
|
|
10299
8983
|
name = _ref12[0],
|
|
10300
8984
|
fn = _ref12[1];
|
|
10301
8985
|
if (name.startsWith('make')) {
|
|
10302
|
-
var createName = 'create' + name.substring(4);
|
|
8986
|
+
var createName = 'create' + name.substring(4);
|
|
10303
8987
|
bw[createName] = function (props) {
|
|
10304
|
-
|
|
10305
|
-
return bw.renderComponent(taco);
|
|
8988
|
+
return bw.createDOM(fn(props));
|
|
10306
8989
|
};
|
|
10307
8990
|
}
|
|
10308
8991
|
});
|