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
package/dist/bitwrench.umd.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! bitwrench v2.0.
|
|
1
|
+
/*! bitwrench 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) :
|
|
@@ -12,14 +12,14 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
const VERSION_INFO = {
|
|
15
|
-
version: '2.0.
|
|
15
|
+
version: '2.0.20',
|
|
16
16
|
name: 'bitwrench',
|
|
17
17
|
description: 'A library for javascript UI functions.',
|
|
18
18
|
license: 'BSD-2-Clause',
|
|
19
19
|
homepage: 'https://deftio.github.com/bitwrench/pages',
|
|
20
20
|
repository: 'git+https://github.com/deftio/bitwrench.git',
|
|
21
21
|
author: 'manu a. chatterjee <deftio@deftio.com> (https://deftio.com/)',
|
|
22
|
-
buildDate: '2026-03-
|
|
22
|
+
buildDate: '2026-03-23T05:19:31.951Z'
|
|
23
23
|
};
|
|
24
24
|
|
|
25
25
|
/**
|
|
@@ -2176,10 +2176,10 @@
|
|
|
2176
2176
|
'position': 'relative', 'display': 'flex', 'flex-direction': 'column', 'pointer-events': 'auto',
|
|
2177
2177
|
'background-clip': 'padding-box', 'border': '1px solid transparent', 'outline': '0'
|
|
2178
2178
|
},
|
|
2179
|
-
'.bw_modal_header': { 'display': 'flex', 'align-items': 'center', 'justify-content': 'space-between' },
|
|
2179
|
+
'.bw_modal_header': { 'display': 'flex', 'align-items': 'center', 'justify-content': 'space-between', 'padding': '1rem 1.25rem', 'border-bottom': '1px solid transparent' },
|
|
2180
2180
|
'.bw_modal_title': { 'margin': '0', 'font-size': '1.25rem', 'font-weight': '600', 'line-height': '1.3' },
|
|
2181
|
-
'.bw_modal_body': { 'position': 'relative', 'flex': '1 1 auto' },
|
|
2182
|
-
'.bw_modal_footer': { 'display': 'flex', 'flex-wrap': 'wrap', 'align-items': 'center', 'justify-content': 'flex-end', 'gap': '0.5rem' }
|
|
2181
|
+
'.bw_modal_body': { 'position': 'relative', 'flex': '1 1 auto', 'padding': '1rem 1.25rem' },
|
|
2182
|
+
'.bw_modal_footer': { 'display': 'flex', 'flex-wrap': 'wrap', 'align-items': 'center', 'justify-content': 'flex-end', 'gap': '0.5rem', 'padding': '0.75rem 1.25rem', 'border-top': '1px solid transparent' }
|
|
2183
2183
|
},
|
|
2184
2184
|
|
|
2185
2185
|
// ---- Toast ----
|
|
@@ -2200,8 +2200,8 @@
|
|
|
2200
2200
|
},
|
|
2201
2201
|
'.bw_toast.bw_toast_show': { 'opacity': '1', 'transform': 'translateY(0)' },
|
|
2202
2202
|
'.bw_toast.bw_toast_hiding': { 'opacity': '0' },
|
|
2203
|
-
'.bw_toast_header': { 'display': 'flex', 'align-items': 'center', 'justify-content': 'space-between', 'font-size': '0.875rem' },
|
|
2204
|
-
'.bw_toast_body': { 'font-size': '0.9375rem' }
|
|
2203
|
+
'.bw_toast_header': { 'display': 'flex', 'align-items': 'center', 'justify-content': 'space-between', 'padding': '0.5rem 0.75rem', 'font-size': '0.875rem', 'border-bottom': '1px solid transparent' },
|
|
2204
|
+
'.bw_toast_body': { 'padding': '0.5rem 0.75rem', 'font-size': '0.9375rem' }
|
|
2205
2205
|
},
|
|
2206
2206
|
|
|
2207
2207
|
// ---- Dropdown ----
|
|
@@ -2215,15 +2215,15 @@
|
|
|
2215
2215
|
'.bw_dropdown_menu': {
|
|
2216
2216
|
'position': 'absolute', 'top': '100%', 'left': '0', 'z-index': '1000', 'display': 'block',
|
|
2217
2217
|
'min-width': '10rem', 'padding': '0.5rem 0', 'margin': '0.125rem 0 0',
|
|
2218
|
-
'background-clip': 'padding-box',
|
|
2218
|
+
'background-clip': 'padding-box', 'border': '1px solid transparent',
|
|
2219
2219
|
'opacity': '0', 'visibility': 'hidden', 'pointer-events': 'none'
|
|
2220
2220
|
},
|
|
2221
2221
|
'.bw_dropdown_menu.bw_dropdown_show': { 'opacity': '1', 'visibility': 'visible', 'pointer-events': 'auto' },
|
|
2222
2222
|
'.bw_dropdown_menu_end': { 'left': 'auto', 'right': '0' },
|
|
2223
2223
|
'.bw_dropdown_item': {
|
|
2224
|
-
'display': 'block', 'width': '100%', 'clear': 'both',
|
|
2224
|
+
'display': 'block', 'width': '100%', 'padding': '0.4rem 1rem', 'clear': 'both',
|
|
2225
2225
|
'font-weight': '400', 'text-align': 'inherit', 'text-decoration': 'none', 'white-space': 'nowrap',
|
|
2226
|
-
'background-color': 'transparent', 'border': '0', 'font-size': '0.9375rem'
|
|
2226
|
+
'background-color': 'transparent', 'border': '0', 'font-size': '0.9375rem', 'cursor': 'pointer'
|
|
2227
2227
|
},
|
|
2228
2228
|
'.bw_dropdown_item:focus-visible': { 'outline': '2px solid currentColor', 'outline-offset': '-2px' },
|
|
2229
2229
|
'.bw_dropdown_divider': { 'height': '0', 'margin': '0.5rem 0', 'overflow': 'hidden', 'opacity': '1' }
|
|
@@ -2545,6 +2545,33 @@
|
|
|
2545
2545
|
rules['.bw_text_left'] = { 'text-align': 'left' };
|
|
2546
2546
|
rules['.bw_text_right'] = { 'text-align': 'right' };
|
|
2547
2547
|
rules['.bw_text_center'] = { 'text-align': 'center' };
|
|
2548
|
+
rules['.bw_text_justify'] = { 'text-align': 'justify' };
|
|
2549
|
+
|
|
2550
|
+
// Font weight
|
|
2551
|
+
rules['.bw_fw_bold'] = { 'font-weight': '700' };
|
|
2552
|
+
rules['.bw_fw_semibold'] = { 'font-weight': '600' };
|
|
2553
|
+
rules['.bw_fw_normal'] = { 'font-weight': '400' };
|
|
2554
|
+
rules['.bw_fw_light'] = { 'font-weight': '300' };
|
|
2555
|
+
|
|
2556
|
+
// Font style
|
|
2557
|
+
rules['.bw_fst_italic'] = { 'font-style': 'italic' };
|
|
2558
|
+
rules['.bw_fst_normal'] = { 'font-style': 'normal' };
|
|
2559
|
+
|
|
2560
|
+
// Text decoration
|
|
2561
|
+
rules['.bw_text_underline'] = { 'text-decoration': 'underline' };
|
|
2562
|
+
rules['.bw_text_line_through'] = { 'text-decoration': 'line-through' };
|
|
2563
|
+
rules['.bw_text_decoration_none'] = { 'text-decoration': 'none' };
|
|
2564
|
+
|
|
2565
|
+
// Text transform
|
|
2566
|
+
rules['.bw_text_uppercase'] = { 'text-transform': 'uppercase' };
|
|
2567
|
+
rules['.bw_text_lowercase'] = { 'text-transform': 'lowercase' };
|
|
2568
|
+
rules['.bw_text_capitalize'] = { 'text-transform': 'capitalize' };
|
|
2569
|
+
|
|
2570
|
+
// Font size
|
|
2571
|
+
rules['.bw_fs_sm'] = { 'font-size': '0.875rem' };
|
|
2572
|
+
rules['.bw_fs_base'] = { 'font-size': '1rem' };
|
|
2573
|
+
rules['.bw_fs_lg'] = { 'font-size': '1.25rem' };
|
|
2574
|
+
rules['.bw_fs_xl'] = { 'font-size': '1.5rem' };
|
|
2548
2575
|
|
|
2549
2576
|
// Flexbox
|
|
2550
2577
|
var jc = { start: 'flex-start', end: 'flex-end', center: 'center', between: 'space-between', around: 'space-around' };
|
|
@@ -3611,7 +3638,12 @@
|
|
|
3611
3638
|
},
|
|
3612
3639
|
o: {
|
|
3613
3640
|
type: 'card',
|
|
3614
|
-
state: props.state || {}
|
|
3641
|
+
state: props.state || {},
|
|
3642
|
+
slots: {
|
|
3643
|
+
title: '.bw_card_title',
|
|
3644
|
+
content: '.bw_card_body',
|
|
3645
|
+
footer: '.bw_card_footer'
|
|
3646
|
+
}
|
|
3615
3647
|
}
|
|
3616
3648
|
};
|
|
3617
3649
|
}
|
|
@@ -3622,7 +3654,12 @@
|
|
|
3622
3654
|
c: cardContent,
|
|
3623
3655
|
o: {
|
|
3624
3656
|
type: 'card',
|
|
3625
|
-
state: props.state || {}
|
|
3657
|
+
state: props.state || {},
|
|
3658
|
+
slots: {
|
|
3659
|
+
title: '.bw_card_title',
|
|
3660
|
+
content: '.bw_card_body',
|
|
3661
|
+
footer: '.bw_card_footer'
|
|
3662
|
+
}
|
|
3626
3663
|
}
|
|
3627
3664
|
};
|
|
3628
3665
|
}
|
|
@@ -3940,6 +3977,24 @@
|
|
|
3940
3977
|
}
|
|
3941
3978
|
});
|
|
3942
3979
|
|
|
3980
|
+
// Shared tab switching logic
|
|
3981
|
+
function switchTab(el, index) {
|
|
3982
|
+
var allTabs = el.querySelectorAll('.bw_nav_link');
|
|
3983
|
+
var allPanes = el.querySelectorAll('.bw_tab_pane');
|
|
3984
|
+
if (index < 0 || index >= allTabs.length) return;
|
|
3985
|
+
allTabs.forEach(function(t) {
|
|
3986
|
+
t.classList.remove('active');
|
|
3987
|
+
t.setAttribute('aria-selected', 'false');
|
|
3988
|
+
t.setAttribute('tabindex', '-1');
|
|
3989
|
+
});
|
|
3990
|
+
allPanes.forEach(function(p) { p.classList.remove('active'); });
|
|
3991
|
+
allTabs[index].classList.add('active');
|
|
3992
|
+
allTabs[index].setAttribute('aria-selected', 'true');
|
|
3993
|
+
allTabs[index].setAttribute('tabindex', '0');
|
|
3994
|
+
allPanes[index].classList.add('active');
|
|
3995
|
+
if (el._bw_state) el._bw_state.activeIndex = index;
|
|
3996
|
+
}
|
|
3997
|
+
|
|
3943
3998
|
return {
|
|
3944
3999
|
t: 'div',
|
|
3945
4000
|
a: { class: 'bw_tabs' },
|
|
@@ -3958,24 +4013,8 @@
|
|
|
3958
4013
|
role: 'tab',
|
|
3959
4014
|
tabindex: index === actualActiveIndex ? '0' : '-1',
|
|
3960
4015
|
'aria-selected': index === actualActiveIndex ? 'true' : 'false',
|
|
3961
|
-
'data-tab-index': index,
|
|
3962
4016
|
onclick: (e) => {
|
|
3963
|
-
|
|
3964
|
-
const allTabs = tabsContainer.querySelectorAll('.bw_nav_link');
|
|
3965
|
-
const allPanes = tabsContainer.querySelectorAll('.bw_tab_pane');
|
|
3966
|
-
|
|
3967
|
-
allTabs.forEach(t => {
|
|
3968
|
-
t.classList.remove('active');
|
|
3969
|
-
t.setAttribute('aria-selected', 'false');
|
|
3970
|
-
t.setAttribute('tabindex', '-1');
|
|
3971
|
-
});
|
|
3972
|
-
allPanes.forEach(p => p.classList.remove('active'));
|
|
3973
|
-
|
|
3974
|
-
e.target.classList.add('active');
|
|
3975
|
-
e.target.setAttribute('aria-selected', 'true');
|
|
3976
|
-
e.target.setAttribute('tabindex', '0');
|
|
3977
|
-
const targetIndex = parseInt(e.target.getAttribute('data-tab-index'));
|
|
3978
|
-
allPanes[targetIndex].classList.add('active');
|
|
4017
|
+
switchTab(e.target.closest('.bw_tabs'), index);
|
|
3979
4018
|
}
|
|
3980
4019
|
},
|
|
3981
4020
|
c: tab.label
|
|
@@ -3998,6 +4037,10 @@
|
|
|
3998
4037
|
o: {
|
|
3999
4038
|
type: 'tabs',
|
|
4000
4039
|
state: { activeIndex: actualActiveIndex },
|
|
4040
|
+
handle: {
|
|
4041
|
+
setActiveTab: switchTab,
|
|
4042
|
+
getActiveTab: function(el) { return (el._bw_state && el._bw_state.activeIndex) || 0; }
|
|
4043
|
+
},
|
|
4001
4044
|
mounted: function(el) {
|
|
4002
4045
|
var tablist = el.querySelector('[role="tablist"]');
|
|
4003
4046
|
if (!tablist) return;
|
|
@@ -4083,7 +4126,13 @@
|
|
|
4083
4126
|
},
|
|
4084
4127
|
c: '×'
|
|
4085
4128
|
}
|
|
4086
|
-
].filter(Boolean)
|
|
4129
|
+
].filter(Boolean),
|
|
4130
|
+
o: {
|
|
4131
|
+
type: 'alert',
|
|
4132
|
+
handle: {
|
|
4133
|
+
dismiss: function(el) { if (el && el.parentNode) el.parentNode.removeChild(el); }
|
|
4134
|
+
}
|
|
4135
|
+
}
|
|
4087
4136
|
};
|
|
4088
4137
|
}
|
|
4089
4138
|
|
|
@@ -4181,6 +4230,24 @@
|
|
|
4181
4230
|
'aria-valuemax': max
|
|
4182
4231
|
},
|
|
4183
4232
|
c: label || `${percentage}%`
|
|
4233
|
+
},
|
|
4234
|
+
o: {
|
|
4235
|
+
type: 'progress',
|
|
4236
|
+
handle: {
|
|
4237
|
+
setValue: function(el, n) {
|
|
4238
|
+
var bar = el.querySelector('.bw_progress_bar');
|
|
4239
|
+
if (!bar) return;
|
|
4240
|
+
var maxVal = parseInt(bar.getAttribute('aria-valuemax')) || 100;
|
|
4241
|
+
var pct = Math.round((n / maxVal) * 100);
|
|
4242
|
+
bar.style.width = pct + '%';
|
|
4243
|
+
bar.setAttribute('aria-valuenow', n);
|
|
4244
|
+
bar.textContent = pct + '%';
|
|
4245
|
+
},
|
|
4246
|
+
getValue: function(el) {
|
|
4247
|
+
var bar = el.querySelector('.bw_progress_bar');
|
|
4248
|
+
return bar ? parseInt(bar.getAttribute('aria-valuenow')) || 0 : 0;
|
|
4249
|
+
}
|
|
4250
|
+
}
|
|
4184
4251
|
}
|
|
4185
4252
|
};
|
|
4186
4253
|
}
|
|
@@ -5190,6 +5257,26 @@
|
|
|
5190
5257
|
class: `bw_pagination ${size ? 'bw_pagination_' + size : ''} ${className}`.trim()
|
|
5191
5258
|
},
|
|
5192
5259
|
c: items
|
|
5260
|
+
},
|
|
5261
|
+
o: {
|
|
5262
|
+
type: 'pagination',
|
|
5263
|
+
state: { currentPage: currentPage, pages: pages },
|
|
5264
|
+
handle: {
|
|
5265
|
+
setPage: function(el, n) {
|
|
5266
|
+
if (n < 1 || n > pages) return;
|
|
5267
|
+
var allItems = el.querySelectorAll('.bw_page_item');
|
|
5268
|
+
for (var i = 0; i < allItems.length; i++) {
|
|
5269
|
+
allItems[i].classList.remove('bw_active');
|
|
5270
|
+
}
|
|
5271
|
+
// +1 offset: first item is prev arrow
|
|
5272
|
+
if (allItems[n]) allItems[n].classList.add('bw_active');
|
|
5273
|
+
if (el._bw_state) el._bw_state.currentPage = n;
|
|
5274
|
+
if (onPageChange) onPageChange(n);
|
|
5275
|
+
},
|
|
5276
|
+
getPage: function(el) {
|
|
5277
|
+
return (el._bw_state && el._bw_state.currentPage) || 1;
|
|
5278
|
+
}
|
|
5279
|
+
}
|
|
5193
5280
|
}
|
|
5194
5281
|
};
|
|
5195
5282
|
}
|
|
@@ -5338,7 +5425,6 @@
|
|
|
5338
5425
|
class: `bw_accordion_button ${item.open ? '' : 'bw_collapsed'}`.trim(),
|
|
5339
5426
|
type: 'button',
|
|
5340
5427
|
'aria-expanded': item.open ? 'true' : 'false',
|
|
5341
|
-
'data-accordion-index': index,
|
|
5342
5428
|
onclick: function(e) {
|
|
5343
5429
|
var btn = e.target.closest('.bw_accordion_button');
|
|
5344
5430
|
var accordionEl = btn.closest('.bw_accordion');
|
|
@@ -5413,7 +5499,43 @@
|
|
|
5413
5499
|
}),
|
|
5414
5500
|
o: {
|
|
5415
5501
|
type: 'accordion',
|
|
5416
|
-
state: { multiOpen: multiOpen }
|
|
5502
|
+
state: { multiOpen: multiOpen },
|
|
5503
|
+
handle: {
|
|
5504
|
+
toggle: function(el, index) {
|
|
5505
|
+
var items = el.querySelectorAll('.bw_accordion_item');
|
|
5506
|
+
if (index < 0 || index >= items.length) return;
|
|
5507
|
+
var btn = items[index].querySelector('.bw_accordion_button');
|
|
5508
|
+
if (btn) btn.click();
|
|
5509
|
+
},
|
|
5510
|
+
openAll: function(el) {
|
|
5511
|
+
var items = el.querySelectorAll('.bw_accordion_item');
|
|
5512
|
+
for (var i = 0; i < items.length; i++) {
|
|
5513
|
+
var collapse = items[i].querySelector('.bw_accordion_collapse');
|
|
5514
|
+
var btn = items[i].querySelector('.bw_accordion_button');
|
|
5515
|
+
if (!collapse.classList.contains('bw_collapse_show')) {
|
|
5516
|
+
collapse.classList.add('bw_collapse_show');
|
|
5517
|
+
collapse.style.maxHeight = 'none';
|
|
5518
|
+
btn.classList.remove('bw_collapsed');
|
|
5519
|
+
btn.setAttribute('aria-expanded', 'true');
|
|
5520
|
+
}
|
|
5521
|
+
}
|
|
5522
|
+
},
|
|
5523
|
+
closeAll: function(el) {
|
|
5524
|
+
var items = el.querySelectorAll('.bw_accordion_item');
|
|
5525
|
+
for (var i = 0; i < items.length; i++) {
|
|
5526
|
+
var collapse = items[i].querySelector('.bw_accordion_collapse');
|
|
5527
|
+
var btn = items[i].querySelector('.bw_accordion_button');
|
|
5528
|
+
if (collapse.classList.contains('bw_collapse_show')) {
|
|
5529
|
+
collapse.style.maxHeight = collapse.scrollHeight + 'px';
|
|
5530
|
+
collapse.offsetHeight;
|
|
5531
|
+
collapse.style.maxHeight = '0px';
|
|
5532
|
+
collapse.classList.remove('bw_collapse_show');
|
|
5533
|
+
btn.classList.add('bw_collapsed');
|
|
5534
|
+
btn.setAttribute('aria-expanded', 'false');
|
|
5535
|
+
}
|
|
5536
|
+
}
|
|
5537
|
+
}
|
|
5538
|
+
}
|
|
5417
5539
|
}
|
|
5418
5540
|
};
|
|
5419
5541
|
}
|
|
@@ -5503,6 +5625,14 @@
|
|
|
5503
5625
|
},
|
|
5504
5626
|
o: {
|
|
5505
5627
|
type: 'modal',
|
|
5628
|
+
handle: {
|
|
5629
|
+
open: function(el) {
|
|
5630
|
+
el.classList.add('bw_modal_show');
|
|
5631
|
+
el.style.display = 'flex';
|
|
5632
|
+
document.body.style.overflow = 'hidden';
|
|
5633
|
+
},
|
|
5634
|
+
close: function(el) { closeModal(el); }
|
|
5635
|
+
},
|
|
5506
5636
|
mounted: function(el) {
|
|
5507
5637
|
// Click backdrop to close
|
|
5508
5638
|
el.addEventListener('click', function(e) {
|
|
@@ -5561,9 +5691,8 @@
|
|
|
5561
5691
|
return {
|
|
5562
5692
|
t: 'div',
|
|
5563
5693
|
a: {
|
|
5564
|
-
class: `bw_toast ${variantClass(variant)} ${className}`.trim(),
|
|
5565
|
-
role: 'alert'
|
|
5566
|
-
'data-position': position
|
|
5694
|
+
class: `bw_toast ${variantClass(variant)} bw_toast_${position.replace(/-/g, '_')} ${className}`.trim(),
|
|
5695
|
+
role: 'alert'
|
|
5567
5696
|
},
|
|
5568
5697
|
c: [
|
|
5569
5698
|
(title) && {
|
|
@@ -5597,6 +5726,12 @@
|
|
|
5597
5726
|
].filter(Boolean),
|
|
5598
5727
|
o: {
|
|
5599
5728
|
type: 'toast',
|
|
5729
|
+
handle: {
|
|
5730
|
+
dismiss: function(el) {
|
|
5731
|
+
el.classList.add('bw_toast_hiding');
|
|
5732
|
+
setTimeout(function() { if (el.parentNode) el.parentNode.removeChild(el); }, 300);
|
|
5733
|
+
}
|
|
5734
|
+
},
|
|
5600
5735
|
mounted: function(el) {
|
|
5601
5736
|
// Trigger show animation
|
|
5602
5737
|
requestAnimationFrame(function() {
|
|
@@ -5954,7 +6089,7 @@
|
|
|
5954
6089
|
var total = carouselEl.querySelectorAll('.bw_carousel_slide').length;
|
|
5955
6090
|
if (index < 0) index = total - 1;
|
|
5956
6091
|
if (index >= total) index = 0;
|
|
5957
|
-
carouselEl.
|
|
6092
|
+
carouselEl._bw_carouselIndex = index;
|
|
5958
6093
|
var track = carouselEl.querySelector('.bw_carousel_track');
|
|
5959
6094
|
track.style.transform = 'translateX(-' + (index * 100) + '%)';
|
|
5960
6095
|
// Update indicators
|
|
@@ -6011,7 +6146,7 @@
|
|
|
6011
6146
|
'aria-label': 'Previous slide',
|
|
6012
6147
|
onclick: function(e) {
|
|
6013
6148
|
var carousel = e.target.closest('.bw_carousel');
|
|
6014
|
-
var idx =
|
|
6149
|
+
var idx = carousel._bw_carouselIndex || 0;
|
|
6015
6150
|
goToSlide(carousel, idx - 1);
|
|
6016
6151
|
}
|
|
6017
6152
|
},
|
|
@@ -6025,7 +6160,7 @@
|
|
|
6025
6160
|
'aria-label': 'Next slide',
|
|
6026
6161
|
onclick: function(e) {
|
|
6027
6162
|
var carousel = e.target.closest('.bw_carousel');
|
|
6028
|
-
var idx =
|
|
6163
|
+
var idx = carousel._bw_carouselIndex || 0;
|
|
6029
6164
|
goToSlide(carousel, idx + 1);
|
|
6030
6165
|
}
|
|
6031
6166
|
},
|
|
@@ -6045,11 +6180,9 @@
|
|
|
6045
6180
|
class: 'bw_carousel_indicator' + (i === startIndex ? ' active' : ''),
|
|
6046
6181
|
type: 'button',
|
|
6047
6182
|
'aria-label': 'Go to slide ' + (i + 1),
|
|
6048
|
-
'data-slide-index': i,
|
|
6049
6183
|
onclick: function(e) {
|
|
6050
6184
|
var carousel = e.target.closest('.bw_carousel');
|
|
6051
|
-
|
|
6052
|
-
goToSlide(carousel, idx);
|
|
6185
|
+
goToSlide(carousel, i);
|
|
6053
6186
|
}
|
|
6054
6187
|
}
|
|
6055
6188
|
};
|
|
@@ -6063,17 +6196,37 @@
|
|
|
6063
6196
|
class: ('bw_carousel ' + className).trim(),
|
|
6064
6197
|
style: 'height: ' + height,
|
|
6065
6198
|
tabindex: '0',
|
|
6066
|
-
'aria-roledescription': 'carousel'
|
|
6067
|
-
'data-carousel-index': startIndex
|
|
6199
|
+
'aria-roledescription': 'carousel'
|
|
6068
6200
|
},
|
|
6069
6201
|
c: children,
|
|
6070
6202
|
o: {
|
|
6071
6203
|
type: 'carousel',
|
|
6072
6204
|
state: { activeIndex: startIndex, autoPlay: autoPlay, interval: interval },
|
|
6205
|
+
handle: {
|
|
6206
|
+
goToSlide: function(el, index) { goToSlide(el, index); },
|
|
6207
|
+
next: function(el) { goToSlide(el, (el._bw_carouselIndex || 0) + 1); },
|
|
6208
|
+
prev: function(el) { goToSlide(el, (el._bw_carouselIndex || 0) - 1); },
|
|
6209
|
+
getActiveIndex: function(el) { return el._bw_carouselIndex || 0; },
|
|
6210
|
+
pause: function(el) {
|
|
6211
|
+
if (el._bw_carouselInterval) {
|
|
6212
|
+
clearInterval(el._bw_carouselInterval);
|
|
6213
|
+
el._bw_carouselInterval = null;
|
|
6214
|
+
}
|
|
6215
|
+
},
|
|
6216
|
+
play: function(el) {
|
|
6217
|
+
if (!el._bw_carouselInterval && el._bw_state) {
|
|
6218
|
+
var ms = el._bw_state.interval || 5000;
|
|
6219
|
+
el._bw_carouselInterval = setInterval(function() {
|
|
6220
|
+
goToSlide(el, (el._bw_carouselIndex || 0) + 1);
|
|
6221
|
+
}, ms);
|
|
6222
|
+
}
|
|
6223
|
+
}
|
|
6224
|
+
},
|
|
6073
6225
|
mounted: function(el) {
|
|
6226
|
+
el._bw_carouselIndex = startIndex;
|
|
6074
6227
|
// Keyboard navigation
|
|
6075
6228
|
el.addEventListener('keydown', function(e) {
|
|
6076
|
-
var idx =
|
|
6229
|
+
var idx = el._bw_carouselIndex || 0;
|
|
6077
6230
|
if (e.key === 'ArrowLeft') {
|
|
6078
6231
|
e.preventDefault();
|
|
6079
6232
|
goToSlide(el, idx - 1);
|
|
@@ -6085,7 +6238,7 @@
|
|
|
6085
6238
|
// Auto-play
|
|
6086
6239
|
if (autoPlay) {
|
|
6087
6240
|
var intervalId = setInterval(function() {
|
|
6088
|
-
var idx =
|
|
6241
|
+
var idx = el._bw_carouselIndex || 0;
|
|
6089
6242
|
goToSlide(el, idx + 1);
|
|
6090
6243
|
}, interval);
|
|
6091
6244
|
el._bw_carouselInterval = intervalId;
|
|
@@ -6095,7 +6248,7 @@
|
|
|
6095
6248
|
});
|
|
6096
6249
|
el.addEventListener('mouseleave', function() {
|
|
6097
6250
|
el._bw_carouselInterval = setInterval(function() {
|
|
6098
|
-
var idx =
|
|
6251
|
+
var idx = el._bw_carouselIndex || 0;
|
|
6099
6252
|
goToSlide(el, idx + 1);
|
|
6100
6253
|
}, interval);
|
|
6101
6254
|
});
|
|
@@ -6211,7 +6364,13 @@
|
|
|
6211
6364
|
t: 'div',
|
|
6212
6365
|
a: { class: classes, style: style },
|
|
6213
6366
|
c: children,
|
|
6214
|
-
o: {
|
|
6367
|
+
o: {
|
|
6368
|
+
type: 'stat-card',
|
|
6369
|
+
slots: {
|
|
6370
|
+
value: '.bw_stat_value',
|
|
6371
|
+
label: '.bw_stat_label'
|
|
6372
|
+
}
|
|
6373
|
+
}
|
|
6215
6374
|
};
|
|
6216
6375
|
}
|
|
6217
6376
|
|
|
@@ -6890,7 +7049,7 @@
|
|
|
6890
7049
|
function makeChipEl(text) {
|
|
6891
7050
|
return {
|
|
6892
7051
|
t: 'span',
|
|
6893
|
-
a: { class: 'bw_chip'
|
|
7052
|
+
a: { class: 'bw_chip' },
|
|
6894
7053
|
c: [
|
|
6895
7054
|
text,
|
|
6896
7055
|
{
|
|
@@ -6901,9 +7060,8 @@
|
|
|
6901
7060
|
'aria-label': 'Remove ' + text,
|
|
6902
7061
|
onclick: function(e) {
|
|
6903
7062
|
var chip = e.target.closest('.bw_chip');
|
|
6904
|
-
var val = chip.getAttribute('data-chip-value');
|
|
6905
7063
|
chip.parentNode.removeChild(chip);
|
|
6906
|
-
if (onRemove) onRemove(
|
|
7064
|
+
if (onRemove) onRemove(text);
|
|
6907
7065
|
}
|
|
6908
7066
|
},
|
|
6909
7067
|
c: '\u00D7'
|
|
@@ -6931,7 +7089,7 @@
|
|
|
6931
7089
|
// Insert chip before the input
|
|
6932
7090
|
var chipEl = document.createElement('span');
|
|
6933
7091
|
chipEl.className = 'bw_chip';
|
|
6934
|
-
chipEl.
|
|
7092
|
+
chipEl._bw_chipValue = val;
|
|
6935
7093
|
chipEl.innerHTML = '';
|
|
6936
7094
|
chipEl.textContent = val;
|
|
6937
7095
|
var removeBtn = document.createElement('button');
|
|
@@ -6954,7 +7112,7 @@
|
|
|
6954
7112
|
var chipEls = wrapper.querySelectorAll('.bw_chip');
|
|
6955
7113
|
if (chipEls.length) {
|
|
6956
7114
|
var last = chipEls[chipEls.length - 1];
|
|
6957
|
-
var removedVal = last.
|
|
7115
|
+
var removedVal = last._bw_chipValue || last.firstChild.textContent;
|
|
6958
7116
|
last.parentNode.removeChild(last);
|
|
6959
7117
|
if (onRemove) onRemove(removedVal);
|
|
6960
7118
|
}
|
|
@@ -6963,7 +7121,50 @@
|
|
|
6963
7121
|
}
|
|
6964
7122
|
}
|
|
6965
7123
|
],
|
|
6966
|
-
o: {
|
|
7124
|
+
o: {
|
|
7125
|
+
type: 'chip-input',
|
|
7126
|
+
handle: {
|
|
7127
|
+
addChip: function(el, text) {
|
|
7128
|
+
if (!text) return;
|
|
7129
|
+
var input = el.querySelector('.bw_chip_field');
|
|
7130
|
+
var chipEl = document.createElement('span');
|
|
7131
|
+
chipEl.className = 'bw_chip';
|
|
7132
|
+
chipEl._bw_chipValue = text;
|
|
7133
|
+
chipEl.textContent = text;
|
|
7134
|
+
var removeBtn = document.createElement('button');
|
|
7135
|
+
removeBtn.type = 'button';
|
|
7136
|
+
removeBtn.className = 'bw_chip_remove';
|
|
7137
|
+
removeBtn.setAttribute('aria-label', 'Remove ' + text);
|
|
7138
|
+
removeBtn.textContent = '\u00D7';
|
|
7139
|
+
removeBtn.onclick = function() { chipEl.parentNode.removeChild(chipEl); };
|
|
7140
|
+
chipEl.appendChild(removeBtn);
|
|
7141
|
+
el.insertBefore(chipEl, input);
|
|
7142
|
+
},
|
|
7143
|
+
removeChip: function(el, text) {
|
|
7144
|
+
var chips = el.querySelectorAll('.bw_chip');
|
|
7145
|
+
for (var i = 0; i < chips.length; i++) {
|
|
7146
|
+
if ((chips[i]._bw_chipValue || chips[i].firstChild.textContent) === text) {
|
|
7147
|
+
chips[i].parentNode.removeChild(chips[i]);
|
|
7148
|
+
return;
|
|
7149
|
+
}
|
|
7150
|
+
}
|
|
7151
|
+
},
|
|
7152
|
+
getChips: function(el) {
|
|
7153
|
+
var chips = el.querySelectorAll('.bw_chip');
|
|
7154
|
+
var values = [];
|
|
7155
|
+
for (var i = 0; i < chips.length; i++) {
|
|
7156
|
+
values.push(chips[i]._bw_chipValue || chips[i].firstChild.textContent);
|
|
7157
|
+
}
|
|
7158
|
+
return values;
|
|
7159
|
+
},
|
|
7160
|
+
clear: function(el) {
|
|
7161
|
+
var chips = el.querySelectorAll('.bw_chip');
|
|
7162
|
+
for (var i = chips.length - 1; i >= 0; i--) {
|
|
7163
|
+
chips[i].parentNode.removeChild(chips[i]);
|
|
7164
|
+
}
|
|
7165
|
+
}
|
|
7166
|
+
}
|
|
7167
|
+
}
|
|
6967
7168
|
};
|
|
6968
7169
|
}
|
|
6969
7170
|
|
|
@@ -7150,12 +7351,11 @@
|
|
|
7150
7351
|
_subIdCounter: 0, // monotonic ID for subscriptions
|
|
7151
7352
|
|
|
7152
7353
|
// ── Node reference cache ──────────────────────────────────────────────
|
|
7153
|
-
// Fast O(1) lookup for elements by
|
|
7354
|
+
// Fast O(1) lookup for elements by id attribute or bw_uuid_* class.
|
|
7154
7355
|
//
|
|
7155
7356
|
// Populated by bw.createDOM() when elements have:
|
|
7156
|
-
// - data-bw_id attribute (user-declared addressable elements)
|
|
7157
7357
|
// - id attribute (standard HTML id)
|
|
7158
|
-
// -
|
|
7358
|
+
// - bw_uuid_* class (lifecycle-managed or explicitly addressed elements)
|
|
7159
7359
|
//
|
|
7160
7360
|
// Cleaned up by bw.cleanup() when elements are destroyed via bitwrench APIs.
|
|
7161
7361
|
// On cache miss, falls back to querySelector/getElementById — never fails,
|
|
@@ -7163,7 +7363,7 @@
|
|
|
7163
7363
|
// via parentNode === null check (IE11-safe, unlike el.isConnected).
|
|
7164
7364
|
//
|
|
7165
7365
|
// Elements created via bw.createDOM() also get el._bw_refs — a local map of
|
|
7166
|
-
// child
|
|
7366
|
+
// child id/UUID -> DOM node ref for fast parent->child access in o.render.
|
|
7167
7367
|
// This is the bitwrench equivalent of React's compiled template "holes".
|
|
7168
7368
|
//
|
|
7169
7369
|
// Contract: if you remove elements outside of bitwrench APIs (raw el.remove()),
|
|
@@ -7243,7 +7443,6 @@
|
|
|
7243
7443
|
// _cw console.warn 8
|
|
7244
7444
|
// _cl console.log 11
|
|
7245
7445
|
// _ce console.error 4
|
|
7246
|
-
// _chp ComponentHandle.prototype 28 (defined after constructor)
|
|
7247
7446
|
//
|
|
7248
7447
|
// Note: document.createElement etc. are NOT aliased because they require
|
|
7249
7448
|
// `this === document` and .bind() would add overhead on every call.
|
|
@@ -7416,15 +7615,15 @@
|
|
|
7416
7615
|
* 1. Check `bw._nodeMap[id]` — if found and still attached (parentNode !== null), return it
|
|
7417
7616
|
* 2. If cached ref is detached (parentNode === null), remove stale entry
|
|
7418
7617
|
* 3. Fall back to `document.getElementById(id)` then `document.querySelector(...)`
|
|
7419
|
-
* 4.
|
|
7420
|
-
* 5.
|
|
7618
|
+
* 4. Try class-based lookup for bw_uuid_* tokens (UUID addressing)
|
|
7619
|
+
* 5. Cache the result for next time
|
|
7421
7620
|
*
|
|
7422
7621
|
* Accepts a DOM element directly (pass-through) or a string identifier.
|
|
7423
7622
|
* String identifiers are tried as: direct map key, getElementById,
|
|
7424
7623
|
* querySelector (for CSS selectors starting with . or #), and
|
|
7425
|
-
*
|
|
7624
|
+
* bw_uuid_* class selector.
|
|
7426
7625
|
*
|
|
7427
|
-
* @param {string|Element} id - Element ID, CSS selector,
|
|
7626
|
+
* @param {string|Element} id - Element ID, CSS selector, bw_uuid_* class, or DOM element
|
|
7428
7627
|
* @returns {Element|null} The DOM element, or null if not found
|
|
7429
7628
|
* @category Internal
|
|
7430
7629
|
*/
|
|
@@ -7453,17 +7652,12 @@
|
|
|
7453
7652
|
el = document.querySelector(id);
|
|
7454
7653
|
}
|
|
7455
7654
|
|
|
7456
|
-
// 4. Try
|
|
7457
|
-
if (!el) {
|
|
7458
|
-
el = document.querySelector('[data-bw_id="' + id + '"]');
|
|
7459
|
-
}
|
|
7460
|
-
|
|
7461
|
-
// 5. Try class-based lookup for bw_uuid_* tokens (UUID addressing)
|
|
7655
|
+
// 4. Try class-based lookup for bw_uuid_* tokens (UUID addressing)
|
|
7462
7656
|
if (!el && id.indexOf('bw_uuid_') === 0) {
|
|
7463
7657
|
el = document.querySelector('.' + id);
|
|
7464
7658
|
}
|
|
7465
7659
|
|
|
7466
|
-
//
|
|
7660
|
+
// 5. Cache the result for next time
|
|
7467
7661
|
if (el) {
|
|
7468
7662
|
bw._nodeMap[id] = el;
|
|
7469
7663
|
}
|
|
@@ -7475,17 +7669,17 @@
|
|
|
7475
7669
|
* Register a DOM element in the node cache under one or more keys.
|
|
7476
7670
|
*
|
|
7477
7671
|
* Called internally by `bw.createDOM()`. Registers elements that have
|
|
7478
|
-
* id attributes,
|
|
7672
|
+
* id attributes, UUID classes, or both.
|
|
7479
7673
|
*
|
|
7480
7674
|
* @param {Element} el - DOM element to register
|
|
7481
|
-
* @param {string} [
|
|
7675
|
+
* @param {string} [uuid] - bw_uuid_* class token to register under
|
|
7482
7676
|
* @category Internal
|
|
7483
7677
|
*/
|
|
7484
|
-
bw._registerNode = function(el,
|
|
7678
|
+
bw._registerNode = function(el, uuid) {
|
|
7485
7679
|
if (!el) return;
|
|
7486
|
-
// Register under
|
|
7487
|
-
if (
|
|
7488
|
-
bw._nodeMap[
|
|
7680
|
+
// Register under UUID class token
|
|
7681
|
+
if (uuid) {
|
|
7682
|
+
bw._nodeMap[uuid] = el;
|
|
7489
7683
|
}
|
|
7490
7684
|
// Register under id attribute
|
|
7491
7685
|
var htmlId = el.getAttribute ? el.getAttribute('id') : null;
|
|
@@ -7501,13 +7695,13 @@
|
|
|
7501
7695
|
* through bitwrench APIs.
|
|
7502
7696
|
*
|
|
7503
7697
|
* @param {Element} el - DOM element to deregister
|
|
7504
|
-
* @param {string} [
|
|
7698
|
+
* @param {string} [uuid] - bw_uuid_* class token to remove
|
|
7505
7699
|
* @category Internal
|
|
7506
7700
|
*/
|
|
7507
|
-
bw._deregisterNode = function(el,
|
|
7508
|
-
// Remove
|
|
7509
|
-
if (
|
|
7510
|
-
delete bw._nodeMap[
|
|
7701
|
+
bw._deregisterNode = function(el, uuid) {
|
|
7702
|
+
// Remove UUID class entry
|
|
7703
|
+
if (uuid) {
|
|
7704
|
+
delete bw._nodeMap[uuid];
|
|
7511
7705
|
}
|
|
7512
7706
|
// Remove id attribute entry
|
|
7513
7707
|
var htmlId = el && el.getAttribute ? el.getAttribute('id') : null;
|
|
@@ -7520,6 +7714,13 @@
|
|
|
7520
7714
|
// bw.assignUUID() / bw.getUUID() — Explicit UUID addressing for TACO objects
|
|
7521
7715
|
// ===================================================================================
|
|
7522
7716
|
|
|
7717
|
+
/**
|
|
7718
|
+
* Marker class for elements with lifecycle hooks (mounted/unmount/render/state).
|
|
7719
|
+
* Used by cleanup() to find lifecycle-managed elements via querySelectorAll('.bw_lc').
|
|
7720
|
+
* @private
|
|
7721
|
+
*/
|
|
7722
|
+
var _BW_LC = 'bw_lc';
|
|
7723
|
+
|
|
7523
7724
|
/**
|
|
7524
7725
|
* Regex to match a bw_uuid_* token in a class string.
|
|
7525
7726
|
* @private
|
|
@@ -7708,15 +7909,6 @@
|
|
|
7708
7909
|
// Handle null/undefined
|
|
7709
7910
|
if (taco == null) return '';
|
|
7710
7911
|
|
|
7711
|
-
// Handle ComponentHandle — use its .taco
|
|
7712
|
-
if (taco && taco._bwComponent === true) {
|
|
7713
|
-
var compOptions = Object.assign({}, options);
|
|
7714
|
-
if (!compOptions.state && taco._state) {
|
|
7715
|
-
compOptions.state = taco._state;
|
|
7716
|
-
}
|
|
7717
|
-
return bw.html(taco.taco, compOptions);
|
|
7718
|
-
}
|
|
7719
|
-
|
|
7720
7912
|
// Handle arrays of TACOs
|
|
7721
7913
|
if (_isA(taco)) {
|
|
7722
7914
|
return taco.map(t => bw.html(t, options)).join('');
|
|
@@ -7727,24 +7919,6 @@
|
|
|
7727
7919
|
return taco.v;
|
|
7728
7920
|
}
|
|
7729
7921
|
|
|
7730
|
-
// Handle bw.when() markers
|
|
7731
|
-
if (taco && taco._bwWhen && options.state) {
|
|
7732
|
-
var whenExpr = taco.expr.replace(/^\$\{|\}$/g, '');
|
|
7733
|
-
var whenVal = options.compile
|
|
7734
|
-
? bw._resolveTemplate('${' + whenExpr + '}', options.state, true)
|
|
7735
|
-
: bw._evaluatePath(options.state, whenExpr);
|
|
7736
|
-
var branch = whenVal ? taco.branches[0] : (taco.branches[1] || null);
|
|
7737
|
-
return branch ? bw.html(branch, options) : '';
|
|
7738
|
-
}
|
|
7739
|
-
|
|
7740
|
-
// Handle bw.each() markers
|
|
7741
|
-
if (taco && taco._bwEach && options.state) {
|
|
7742
|
-
var eachExpr = taco.expr.replace(/^\$\{|\}$/g, '');
|
|
7743
|
-
var arr = bw._evaluatePath(options.state, eachExpr);
|
|
7744
|
-
if (!_isA(arr)) return '';
|
|
7745
|
-
return arr.map(function(item, idx) { return bw.html(taco.factory(item, idx), options); }).join('');
|
|
7746
|
-
}
|
|
7747
|
-
|
|
7748
7922
|
// Handle primitives and non-TACO objects
|
|
7749
7923
|
if (!_is(taco, 'object') || !taco.t) {
|
|
7750
7924
|
var str = options.raw ? String(taco) : bw.escapeHTML(String(taco));
|
|
@@ -7808,14 +7982,14 @@
|
|
|
7808
7982
|
}
|
|
7809
7983
|
}
|
|
7810
7984
|
|
|
7811
|
-
// Add
|
|
7812
|
-
if ((opts.mounted || opts.unmount) && !attrs.class
|
|
7813
|
-
const
|
|
7985
|
+
// Add bw_uuid + bw_lc classes if lifecycle hooks present
|
|
7986
|
+
if ((opts.mounted || opts.unmount) && !_UUID_RE.test(attrs.class || '')) {
|
|
7987
|
+
const uuid = bw.uuid('uuid');
|
|
7814
7988
|
attrStr = attrStr.replace(/class="([^"]*)"/, (_match, classes) => {
|
|
7815
|
-
return `class="${classes}
|
|
7989
|
+
return `class="${classes} ${uuid} ${_BW_LC}"`.trim();
|
|
7816
7990
|
});
|
|
7817
7991
|
if (!attrStr.includes('class=')) {
|
|
7818
|
-
attrStr += ` class="
|
|
7992
|
+
attrStr += ` class="${uuid} ${_BW_LC}"`;
|
|
7819
7993
|
}
|
|
7820
7994
|
}
|
|
7821
7995
|
|
|
@@ -8043,11 +8217,6 @@
|
|
|
8043
8217
|
return frag;
|
|
8044
8218
|
}
|
|
8045
8219
|
|
|
8046
|
-
// Handle ComponentHandle — extract .taco for DOM creation
|
|
8047
|
-
if (taco && taco._bwComponent === true) {
|
|
8048
|
-
return bw.createDOM(taco.taco, options);
|
|
8049
|
-
}
|
|
8050
|
-
|
|
8051
8220
|
// Handle text nodes
|
|
8052
8221
|
if (!_is(taco, 'object') || !taco.t) {
|
|
8053
8222
|
return document.createTextNode(String(taco));
|
|
@@ -8088,24 +8257,19 @@
|
|
|
8088
8257
|
}
|
|
8089
8258
|
|
|
8090
8259
|
// Add children, building _bw_refs for fast parent→child access.
|
|
8091
|
-
// Children with
|
|
8260
|
+
// Children with id attributes or bw_uuid_* classes get local refs on the parent,
|
|
8092
8261
|
// so o.render functions can access them without any DOM lookup.
|
|
8093
8262
|
if (content != null) {
|
|
8094
8263
|
if (_isA(content)) {
|
|
8095
8264
|
content.forEach(child => {
|
|
8096
8265
|
if (child != null) {
|
|
8097
|
-
// Handle ComponentHandle in content arrays (Level 2 children)
|
|
8098
|
-
if (child._bwComponent === true) {
|
|
8099
|
-
child.mount(el);
|
|
8100
|
-
return;
|
|
8101
|
-
}
|
|
8102
8266
|
var childEl = bw.createDOM(child, options);
|
|
8103
8267
|
el.appendChild(childEl);
|
|
8104
8268
|
// Build local refs for addressable children
|
|
8105
|
-
var
|
|
8106
|
-
if (
|
|
8269
|
+
var childRefId = (child && child.a) ? (child.a.id || bw.getUUID(child)) : null;
|
|
8270
|
+
if (childRefId) {
|
|
8107
8271
|
if (!el._bw_refs) el._bw_refs = {};
|
|
8108
|
-
el._bw_refs[
|
|
8272
|
+
el._bw_refs[childRefId] = childEl;
|
|
8109
8273
|
}
|
|
8110
8274
|
// Bubble up grandchild refs (flatten one level)
|
|
8111
8275
|
if (childEl._bw_refs) {
|
|
@@ -8121,16 +8285,13 @@
|
|
|
8121
8285
|
} else if (_is(content, 'object') && content.__bw_raw) {
|
|
8122
8286
|
// Raw HTML content — inject via innerHTML
|
|
8123
8287
|
el.innerHTML = content.v;
|
|
8124
|
-
} else if (content._bwComponent === true) {
|
|
8125
|
-
// Single ComponentHandle as content
|
|
8126
|
-
content.mount(el);
|
|
8127
8288
|
} else if (_is(content, 'object') && content.t) {
|
|
8128
8289
|
var childEl = bw.createDOM(content, options);
|
|
8129
8290
|
el.appendChild(childEl);
|
|
8130
|
-
var
|
|
8131
|
-
if (
|
|
8291
|
+
var childRefId = content.a ? (content.a.id || bw.getUUID(content)) : null;
|
|
8292
|
+
if (childRefId) {
|
|
8132
8293
|
if (!el._bw_refs) el._bw_refs = {};
|
|
8133
|
-
el._bw_refs[
|
|
8294
|
+
el._bw_refs[childRefId] = childEl;
|
|
8134
8295
|
}
|
|
8135
8296
|
if (childEl._bw_refs) {
|
|
8136
8297
|
if (!el._bw_refs) el._bw_refs = {};
|
|
@@ -8160,57 +8321,88 @@
|
|
|
8160
8321
|
|
|
8161
8322
|
// Handle lifecycle hooks and state
|
|
8162
8323
|
if (opts.mounted || opts.unmount || opts.render || opts.state) {
|
|
8163
|
-
|
|
8164
|
-
el.
|
|
8324
|
+
// Ensure element has a UUID class for identity
|
|
8325
|
+
var uuid = bw.getUUID(el) || bw.uuid('uuid');
|
|
8326
|
+
el.classList.add(uuid);
|
|
8327
|
+
el.classList.add(_BW_LC);
|
|
8165
8328
|
|
|
8166
|
-
// Register in node cache under
|
|
8167
|
-
bw._registerNode(el,
|
|
8329
|
+
// Register in node cache under UUID class
|
|
8330
|
+
bw._registerNode(el, uuid);
|
|
8168
8331
|
|
|
8169
8332
|
// Store state
|
|
8170
8333
|
if (opts.state) {
|
|
8171
8334
|
el._bw_state = opts.state;
|
|
8172
8335
|
}
|
|
8173
8336
|
|
|
8174
|
-
// o.render —
|
|
8337
|
+
// o.render — store the render function for bw.update()
|
|
8175
8338
|
if (opts.render) {
|
|
8176
8339
|
el._bw_render = opts.render;
|
|
8340
|
+
}
|
|
8177
8341
|
|
|
8178
|
-
|
|
8179
|
-
|
|
8180
|
-
|
|
8342
|
+
// Determine what to call on mount:
|
|
8343
|
+
// - If o.mounted exists, call it (it can call el._bw_render() for initial render)
|
|
8344
|
+
// - Otherwise if o.render exists, auto-call it as a convenience shorthand
|
|
8345
|
+
var mountFn = opts.mounted || (opts.render ? function(mountEl) {
|
|
8346
|
+
opts.render(mountEl, mountEl._bw_state || {});
|
|
8347
|
+
} : null);
|
|
8181
8348
|
|
|
8182
|
-
|
|
8183
|
-
if (document.body.contains(el)) {
|
|
8184
|
-
opts.render(el, el._bw_state || {});
|
|
8185
|
-
} else {
|
|
8186
|
-
requestAnimationFrame(() => {
|
|
8187
|
-
if (document.body.contains(el)) {
|
|
8188
|
-
opts.render(el, el._bw_state || {});
|
|
8189
|
-
}
|
|
8190
|
-
});
|
|
8191
|
-
}
|
|
8192
|
-
} else if (opts.mounted) {
|
|
8193
|
-
// Queue mounted callback (legacy pattern)
|
|
8349
|
+
if (mountFn) {
|
|
8194
8350
|
if (document.body.contains(el)) {
|
|
8195
|
-
|
|
8351
|
+
mountFn(el, el._bw_state || {});
|
|
8196
8352
|
} else {
|
|
8197
8353
|
requestAnimationFrame(() => {
|
|
8198
8354
|
if (document.body.contains(el)) {
|
|
8199
|
-
|
|
8355
|
+
mountFn(el, el._bw_state || {});
|
|
8200
8356
|
}
|
|
8201
8357
|
});
|
|
8202
8358
|
}
|
|
8203
8359
|
}
|
|
8204
8360
|
|
|
8205
|
-
// Store unmount callback
|
|
8361
|
+
// Store unmount callback keyed by UUID class
|
|
8206
8362
|
if (opts.unmount) {
|
|
8207
|
-
bw._unmountCallbacks.set(
|
|
8363
|
+
bw._unmountCallbacks.set(uuid, () => {
|
|
8208
8364
|
opts.unmount(el, el._bw_state || {});
|
|
8209
8365
|
});
|
|
8210
8366
|
}
|
|
8211
|
-
}
|
|
8212
|
-
|
|
8213
|
-
|
|
8367
|
+
}
|
|
8368
|
+
|
|
8369
|
+
// Component handle: attach methods to el.bw namespace
|
|
8370
|
+
if (opts.handle || opts.slots) {
|
|
8371
|
+
if (!el.bw) el.bw = {};
|
|
8372
|
+
|
|
8373
|
+
// Explicit handle methods: fn(el, ...args) -> el.bw.method(...args)
|
|
8374
|
+
if (opts.handle) {
|
|
8375
|
+
for (var hk in opts.handle) {
|
|
8376
|
+
if (_hop.call(opts.handle, hk)) {
|
|
8377
|
+
el.bw[hk] = opts.handle[hk].bind(null, el);
|
|
8378
|
+
}
|
|
8379
|
+
}
|
|
8380
|
+
}
|
|
8381
|
+
|
|
8382
|
+
// Slot declarations: auto-generate setX/getX pairs
|
|
8383
|
+
if (opts.slots) {
|
|
8384
|
+
for (var sk in opts.slots) {
|
|
8385
|
+
if (_hop.call(opts.slots, sk)) {
|
|
8386
|
+
(function(name, selector) {
|
|
8387
|
+
var cap = name.charAt(0).toUpperCase() + name.slice(1);
|
|
8388
|
+
el.bw['set' + cap] = function(value) {
|
|
8389
|
+
var t = el.querySelector(selector);
|
|
8390
|
+
if (!t) return;
|
|
8391
|
+
if (value != null && typeof value === 'object' && value.t) {
|
|
8392
|
+
t.innerHTML = '';
|
|
8393
|
+
t.appendChild(bw.createDOM(value));
|
|
8394
|
+
} else {
|
|
8395
|
+
t.textContent = (value != null) ? String(value) : '';
|
|
8396
|
+
}
|
|
8397
|
+
};
|
|
8398
|
+
el.bw['get' + cap] = function() {
|
|
8399
|
+
var t = el.querySelector(selector);
|
|
8400
|
+
return t ? t.textContent : '';
|
|
8401
|
+
};
|
|
8402
|
+
})(sk, opts.slots[sk]);
|
|
8403
|
+
}
|
|
8404
|
+
}
|
|
8405
|
+
}
|
|
8214
8406
|
}
|
|
8215
8407
|
|
|
8216
8408
|
return el;
|
|
@@ -8257,7 +8449,7 @@
|
|
|
8257
8449
|
// the target is the mount point, not the content being replaced)
|
|
8258
8450
|
const savedState = targetEl._bw_state;
|
|
8259
8451
|
const savedRender = targetEl._bw_render;
|
|
8260
|
-
const
|
|
8452
|
+
const savedUuid = bw.getUUID(targetEl);
|
|
8261
8453
|
const savedSubs = targetEl._bw_subs;
|
|
8262
8454
|
|
|
8263
8455
|
// Temporarily remove _bw_subs so cleanup doesn't call them
|
|
@@ -8269,10 +8461,9 @@
|
|
|
8269
8461
|
// Restore the target's own state/render/subs after cleanup
|
|
8270
8462
|
if (savedState !== undefined) targetEl._bw_state = savedState;
|
|
8271
8463
|
if (savedRender) targetEl._bw_render = savedRender;
|
|
8272
|
-
if (
|
|
8273
|
-
|
|
8274
|
-
|
|
8275
|
-
bw._registerNode(targetEl, savedBwId);
|
|
8464
|
+
if (savedUuid) {
|
|
8465
|
+
// UUID class stays on element through cleanup; re-register in cache
|
|
8466
|
+
bw._registerNode(targetEl, savedUuid);
|
|
8276
8467
|
}
|
|
8277
8468
|
if (savedSubs) targetEl._bw_subs = savedSubs;
|
|
8278
8469
|
|
|
@@ -8280,25 +8471,11 @@
|
|
|
8280
8471
|
targetEl.innerHTML = '';
|
|
8281
8472
|
|
|
8282
8473
|
if (taco != null) {
|
|
8283
|
-
// Handle ComponentHandle (reactive components from bw.component())
|
|
8284
|
-
if (taco._bwComponent === true) {
|
|
8285
|
-
taco.mount(targetEl);
|
|
8286
|
-
}
|
|
8287
|
-
// Handle component handles (objects with element property)
|
|
8288
|
-
else if (taco.element instanceof Element) {
|
|
8289
|
-
targetEl.appendChild(taco.element);
|
|
8290
|
-
}
|
|
8291
8474
|
// Handle arrays
|
|
8292
|
-
|
|
8475
|
+
if (_isA(taco)) {
|
|
8293
8476
|
taco.forEach(t => {
|
|
8294
8477
|
if (t != null) {
|
|
8295
|
-
|
|
8296
|
-
t.mount(targetEl);
|
|
8297
|
-
} else if (t.element instanceof Element) {
|
|
8298
|
-
targetEl.appendChild(t.element);
|
|
8299
|
-
} else {
|
|
8300
|
-
targetEl.appendChild(bw.createDOM(t, options));
|
|
8301
|
-
}
|
|
8478
|
+
targetEl.appendChild(bw.createDOM(t, options));
|
|
8302
8479
|
}
|
|
8303
8480
|
});
|
|
8304
8481
|
}
|
|
@@ -8311,205 +8488,36 @@
|
|
|
8311
8488
|
return targetEl;
|
|
8312
8489
|
};
|
|
8313
8490
|
|
|
8314
|
-
|
|
8315
|
-
|
|
8316
|
-
|
|
8317
|
-
* Used internally by `bw.renderComponent()`. Creates a proxy-like object
|
|
8318
|
-
* where setting a property triggers `handle.onPropChange()`.
|
|
8319
|
-
*
|
|
8320
|
-
* @param {Object} handle - Component handle
|
|
8321
|
-
* @param {Object} props - Initial props
|
|
8322
|
-
* @returns {Object} Compiled props object with getters/setters
|
|
8323
|
-
* @category DOM Generation
|
|
8324
|
-
*/
|
|
8325
|
-
bw.compileProps = function(handle, props = {}) {
|
|
8326
|
-
const compiledProps = {};
|
|
8327
|
-
|
|
8328
|
-
_keys(props).forEach(key => {
|
|
8329
|
-
// Create getter/setter for each prop
|
|
8330
|
-
Object.defineProperty(compiledProps, key, {
|
|
8331
|
-
get() {
|
|
8332
|
-
return handle._props[key];
|
|
8333
|
-
},
|
|
8334
|
-
set(value) {
|
|
8335
|
-
const oldValue = handle._props[key];
|
|
8336
|
-
if (oldValue !== value) {
|
|
8337
|
-
handle._props[key] = value;
|
|
8338
|
-
// Trigger update if prop changed
|
|
8339
|
-
if (handle.onPropChange) {
|
|
8340
|
-
handle.onPropChange(key, value, oldValue);
|
|
8341
|
-
}
|
|
8342
|
-
}
|
|
8343
|
-
},
|
|
8344
|
-
enumerable: true,
|
|
8345
|
-
configurable: true
|
|
8346
|
-
});
|
|
8347
|
-
});
|
|
8348
|
-
|
|
8349
|
-
return compiledProps;
|
|
8350
|
-
};
|
|
8491
|
+
// Deprecation stubs for removed ComponentHandle APIs
|
|
8492
|
+
bw.compileProps = function() { throw new Error('bw.compileProps() removed in v2.0.19. Use o.handle/o.slots instead.'); };
|
|
8493
|
+
bw.renderComponent = function() { throw new Error('bw.renderComponent() removed in v2.0.19. Use bw.mount() with o.handle/o.slots instead.'); };
|
|
8351
8494
|
|
|
8352
8495
|
/**
|
|
8353
|
-
*
|
|
8354
|
-
*
|
|
8355
|
-
*
|
|
8356
|
-
* and a destroy method. Used internally by `bw.createCard()`, `bw.createTable()`, etc.
|
|
8496
|
+
* Mount a TACO into a target element and return the created root element.
|
|
8497
|
+
* Like bw.DOM() but returns the root element of the TACO (not the container),
|
|
8498
|
+
* giving direct access to el.bw handle methods.
|
|
8357
8499
|
*
|
|
8358
|
-
* @param {
|
|
8359
|
-
* @param {Object}
|
|
8360
|
-
* @
|
|
8500
|
+
* @param {string|Element} target - CSS selector or DOM element
|
|
8501
|
+
* @param {Object} taco - TACO to render
|
|
8502
|
+
* @param {Object} [options] - Mount options
|
|
8503
|
+
* @returns {Element} The created root element
|
|
8361
8504
|
* @category DOM Generation
|
|
8362
|
-
|
|
8363
|
-
|
|
8364
|
-
|
|
8365
|
-
|
|
8366
|
-
|
|
8367
|
-
|
|
8368
|
-
|
|
8369
|
-
|
|
8370
|
-
|
|
8371
|
-
|
|
8372
|
-
|
|
8373
|
-
|
|
8374
|
-
|
|
8375
|
-
|
|
8376
|
-
|
|
8377
|
-
|
|
8378
|
-
}
|
|
8379
|
-
return this._compiledProps;
|
|
8380
|
-
},
|
|
8381
|
-
|
|
8382
|
-
/**
|
|
8383
|
-
* Query all matching elements within this component
|
|
8384
|
-
* @param {string} selector - CSS selector
|
|
8385
|
-
* @returns {NodeList} Matching elements
|
|
8386
|
-
*/
|
|
8387
|
-
$(selector) {
|
|
8388
|
-
return this.element.querySelectorAll(selector);
|
|
8389
|
-
},
|
|
8390
|
-
|
|
8391
|
-
/**
|
|
8392
|
-
* Query the first matching element within this component
|
|
8393
|
-
* @param {string} selector - CSS selector
|
|
8394
|
-
* @returns {Element|null} First matching element or null
|
|
8395
|
-
*/
|
|
8396
|
-
$first(selector) {
|
|
8397
|
-
return this.element.querySelector(selector);
|
|
8398
|
-
},
|
|
8399
|
-
|
|
8400
|
-
/**
|
|
8401
|
-
* Update component with new props and re-render in place
|
|
8402
|
-
* @param {Object} newProps - Properties to merge into current props
|
|
8403
|
-
* @returns {Object} this handle (for chaining)
|
|
8404
|
-
*/
|
|
8405
|
-
update(newProps) {
|
|
8406
|
-
// Update internal props
|
|
8407
|
-
Object.assign(this._props, newProps);
|
|
8408
|
-
|
|
8409
|
-
// Rebuild TACO with new props
|
|
8410
|
-
const newTaco = { ...this.taco, a: { ...this.taco.a, ...newProps } };
|
|
8411
|
-
const newElement = bw.createDOM(newTaco, options);
|
|
8412
|
-
|
|
8413
|
-
// Replace in DOM
|
|
8414
|
-
this.element.replaceWith(newElement);
|
|
8415
|
-
this.element = newElement;
|
|
8416
|
-
this.taco = newTaco;
|
|
8417
|
-
|
|
8418
|
-
return this;
|
|
8419
|
-
},
|
|
8420
|
-
|
|
8421
|
-
/**
|
|
8422
|
-
* Re-render the component from its current TACO, replacing the DOM element
|
|
8423
|
-
* @returns {Object} this handle (for chaining)
|
|
8424
|
-
*/
|
|
8425
|
-
render() {
|
|
8426
|
-
const newElement = bw.createDOM(this.taco, options);
|
|
8427
|
-
this.element.replaceWith(newElement);
|
|
8428
|
-
this.element = newElement;
|
|
8429
|
-
return this;
|
|
8430
|
-
},
|
|
8431
|
-
|
|
8432
|
-
/**
|
|
8433
|
-
* Called when a compiled prop value changes. Override to customize behavior.
|
|
8434
|
-
* Default implementation triggers a full re-render.
|
|
8435
|
-
* @param {string} key - Property name that changed
|
|
8436
|
-
* @param {*} newValue - New property value
|
|
8437
|
-
* @param {*} oldValue - Previous property value
|
|
8438
|
-
*/
|
|
8439
|
-
onPropChange(_key, _newValue, _oldValue) {
|
|
8440
|
-
// Auto re-render on prop change by default
|
|
8441
|
-
this.render();
|
|
8442
|
-
},
|
|
8443
|
-
|
|
8444
|
-
// State management
|
|
8445
|
-
get state() {
|
|
8446
|
-
return this._state;
|
|
8447
|
-
},
|
|
8448
|
-
|
|
8449
|
-
set state(newState) {
|
|
8450
|
-
this._state = newState;
|
|
8451
|
-
this.render();
|
|
8452
|
-
},
|
|
8453
|
-
|
|
8454
|
-
/**
|
|
8455
|
-
* Merge state updates and re-render the component
|
|
8456
|
-
* @param {Object} updates - State properties to merge
|
|
8457
|
-
* @returns {Object} this handle (for chaining)
|
|
8458
|
-
*/
|
|
8459
|
-
setState(updates) {
|
|
8460
|
-
Object.assign(this._state, updates);
|
|
8461
|
-
this.render();
|
|
8462
|
-
return this;
|
|
8463
|
-
},
|
|
8464
|
-
|
|
8465
|
-
/**
|
|
8466
|
-
* Register a child component under a name for later retrieval
|
|
8467
|
-
* @param {string} name - Child name key
|
|
8468
|
-
* @param {Object} component - Child component handle
|
|
8469
|
-
* @returns {Object} this handle (for chaining)
|
|
8470
|
-
*/
|
|
8471
|
-
addChild(name, component) {
|
|
8472
|
-
this._children[name] = component;
|
|
8473
|
-
return this;
|
|
8474
|
-
},
|
|
8475
|
-
|
|
8476
|
-
/**
|
|
8477
|
-
* Retrieve a registered child component by name
|
|
8478
|
-
* @param {string} name - Child name key
|
|
8479
|
-
* @returns {Object|undefined} Child component handle
|
|
8480
|
-
*/
|
|
8481
|
-
getChild(name) {
|
|
8482
|
-
return this._children[name];
|
|
8483
|
-
},
|
|
8484
|
-
|
|
8485
|
-
/**
|
|
8486
|
-
* Destroy this component and all registered children
|
|
8487
|
-
*
|
|
8488
|
-
* Calls destroy() recursively on children, runs bw.cleanup(),
|
|
8489
|
-
* removes the element from DOM, and clears all internal references.
|
|
8490
|
-
*/
|
|
8491
|
-
destroy() {
|
|
8492
|
-
// Destroy children first
|
|
8493
|
-
Object.values(this._children).forEach(child => {
|
|
8494
|
-
if (child && child.destroy) child.destroy();
|
|
8495
|
-
});
|
|
8496
|
-
|
|
8497
|
-
// Clean up this component
|
|
8498
|
-
bw.cleanup(this.element);
|
|
8499
|
-
this.element.remove();
|
|
8500
|
-
|
|
8501
|
-
// Clear references
|
|
8502
|
-
this._children = {};
|
|
8503
|
-
this._props = {};
|
|
8504
|
-
this._state = {};
|
|
8505
|
-
this._compiledProps = null;
|
|
8506
|
-
}
|
|
8507
|
-
};
|
|
8508
|
-
|
|
8509
|
-
// Store handle reference on element
|
|
8510
|
-
element._bwHandle = handle;
|
|
8511
|
-
|
|
8512
|
-
return handle;
|
|
8505
|
+
* @example
|
|
8506
|
+
* var el = bw.mount('#app', bw.makeCarousel({ items: slides }));
|
|
8507
|
+
* el.bw.goToSlide(2);
|
|
8508
|
+
* el.bw.next();
|
|
8509
|
+
*/
|
|
8510
|
+
bw.mount = function(target, taco, options) {
|
|
8511
|
+
var container = _is(target, 'string') ? bw.$(target)[0] : target;
|
|
8512
|
+
if (!container) {
|
|
8513
|
+
_cw('bw.mount: target not found');
|
|
8514
|
+
return null;
|
|
8515
|
+
}
|
|
8516
|
+
bw.cleanup(container);
|
|
8517
|
+
container.innerHTML = '';
|
|
8518
|
+
var el = bw.createDOM(taco, options || {});
|
|
8519
|
+
container.appendChild(el);
|
|
8520
|
+
return el;
|
|
8513
8521
|
};
|
|
8514
8522
|
|
|
8515
8523
|
/**
|
|
@@ -8530,34 +8538,29 @@
|
|
|
8530
8538
|
bw.cleanup = function(element) {
|
|
8531
8539
|
if (!bw._isBrowser || !element) return;
|
|
8532
8540
|
|
|
8533
|
-
// Deregister UUID classes from node cache
|
|
8534
|
-
// Covers elements that have UUID but no data-bw_id
|
|
8535
|
-
var selfUuidMatch = element.className && element.className.match(_UUID_RE);
|
|
8536
|
-
if (selfUuidMatch) delete bw._nodeMap[selfUuidMatch[0]];
|
|
8541
|
+
// Deregister UUID classes from node cache for non-lifecycle UUID elements
|
|
8537
8542
|
var uuidEls = element.querySelectorAll('[class*="bw_uuid_"]');
|
|
8538
8543
|
uuidEls.forEach(function(uel) {
|
|
8539
8544
|
var m = uel.className && uel.className.match(_UUID_RE);
|
|
8540
8545
|
if (m) delete bw._nodeMap[m[0]];
|
|
8541
8546
|
});
|
|
8542
8547
|
|
|
8543
|
-
// Find all elements
|
|
8544
|
-
const elements = element.querySelectorAll('
|
|
8548
|
+
// Find all lifecycle-managed elements (have bw_lc marker class)
|
|
8549
|
+
const elements = element.querySelectorAll('.' + _BW_LC);
|
|
8545
8550
|
|
|
8546
8551
|
elements.forEach(el => {
|
|
8547
|
-
|
|
8548
|
-
const callback = bw._unmountCallbacks.get(id);
|
|
8549
|
-
|
|
8550
|
-
if (callback) {
|
|
8551
|
-
callback();
|
|
8552
|
-
bw._unmountCallbacks.delete(id);
|
|
8553
|
-
}
|
|
8552
|
+
var uuid = bw.getUUID(el);
|
|
8554
8553
|
|
|
8555
|
-
|
|
8556
|
-
|
|
8554
|
+
if (uuid) {
|
|
8555
|
+
const callback = bw._unmountCallbacks.get(uuid);
|
|
8556
|
+
if (callback) {
|
|
8557
|
+
callback();
|
|
8558
|
+
bw._unmountCallbacks.delete(uuid);
|
|
8559
|
+
}
|
|
8557
8560
|
|
|
8558
|
-
|
|
8559
|
-
|
|
8560
|
-
|
|
8561
|
+
// Deregister from node cache
|
|
8562
|
+
bw._deregisterNode(el, uuid);
|
|
8563
|
+
}
|
|
8561
8564
|
|
|
8562
8565
|
// Clean up pub/sub subscriptions tied to this element
|
|
8563
8566
|
if (el._bw_subs) {
|
|
@@ -8572,20 +8575,18 @@
|
|
|
8572
8575
|
});
|
|
8573
8576
|
|
|
8574
8577
|
// Check element itself
|
|
8575
|
-
|
|
8576
|
-
if (
|
|
8577
|
-
|
|
8578
|
+
var selfUuid = bw.getUUID(element);
|
|
8579
|
+
if (selfUuid) {
|
|
8580
|
+
delete bw._nodeMap[selfUuid];
|
|
8581
|
+
|
|
8582
|
+
const callback = bw._unmountCallbacks.get(selfUuid);
|
|
8578
8583
|
if (callback) {
|
|
8579
8584
|
callback();
|
|
8580
|
-
bw._unmountCallbacks.delete(
|
|
8585
|
+
bw._unmountCallbacks.delete(selfUuid);
|
|
8581
8586
|
}
|
|
8582
8587
|
|
|
8583
8588
|
// Deregister from node cache
|
|
8584
|
-
bw._deregisterNode(element,
|
|
8585
|
-
|
|
8586
|
-
// Deregister UUID class from node cache
|
|
8587
|
-
var elemUuidMatch = element.className && element.className.match(_UUID_RE);
|
|
8588
|
-
if (elemUuidMatch) delete bw._nodeMap[elemUuidMatch[0]];
|
|
8589
|
+
bw._deregisterNode(element, selfUuid);
|
|
8589
8590
|
|
|
8590
8591
|
// Clean up pub/sub subscriptions tied to element itself
|
|
8591
8592
|
if (element._bw_subs) {
|
|
@@ -8596,11 +8597,11 @@
|
|
|
8596
8597
|
delete element._bw_render;
|
|
8597
8598
|
delete element._bw_refs;
|
|
8598
8599
|
|
|
8599
|
-
|
|
8600
|
-
|
|
8601
|
-
|
|
8602
|
-
element.
|
|
8603
|
-
delete element.
|
|
8600
|
+
} else {
|
|
8601
|
+
// No UUID on element itself, but still check for _bw_subs (from bw.sub())
|
|
8602
|
+
if (element._bw_subs) {
|
|
8603
|
+
element._bw_subs.forEach(function(unsub) { unsub(); });
|
|
8604
|
+
delete element._bw_subs;
|
|
8604
8605
|
}
|
|
8605
8606
|
}
|
|
8606
8607
|
};
|
|
@@ -8616,7 +8617,7 @@
|
|
|
8616
8617
|
* Calls `el._bw_render(el, state)` and emits `bw:statechange` so other
|
|
8617
8618
|
* components can react without tight coupling.
|
|
8618
8619
|
*
|
|
8619
|
-
* @param {string|Element} target - Element ID,
|
|
8620
|
+
* @param {string|Element} target - Element ID, bw_uuid_* class, CSS selector, or DOM element
|
|
8620
8621
|
* @returns {Element|null} The element, or null if not found / no render function
|
|
8621
8622
|
* @category State Management
|
|
8622
8623
|
* @see bw.patch
|
|
@@ -8641,7 +8642,7 @@
|
|
|
8641
8642
|
* Use `bw.patch()` for lightweight value updates (scores, labels, counters)
|
|
8642
8643
|
* and `bw.update()` for full structural re-renders.
|
|
8643
8644
|
*
|
|
8644
|
-
* @param {string|Element} id - Element ID,
|
|
8645
|
+
* @param {string|Element} id - Element ID, bw_uuid_* class, CSS selector, or DOM element.
|
|
8645
8646
|
* Uses node cache for O(1) lookup; falls back to DOM query on cache miss.
|
|
8646
8647
|
* @param {string|Object} content - New text content, or TACO object to replace children
|
|
8647
8648
|
* @param {string} [attr] - If provided, sets this attribute instead of content
|
|
@@ -8716,7 +8717,7 @@
|
|
|
8716
8717
|
* bubble by default so ancestor elements can listen. Use with `bw.on()` for
|
|
8717
8718
|
* DOM-scoped communication between components.
|
|
8718
8719
|
*
|
|
8719
|
-
* @param {string|Element} target - Element ID,
|
|
8720
|
+
* @param {string|Element} target - Element ID, bw_uuid_* class, CSS selector, or DOM element.
|
|
8720
8721
|
* Uses node cache for O(1) lookup; falls back to DOM query on cache miss.
|
|
8721
8722
|
* @param {string} eventName - Event name (will be prefixed with 'bw:')
|
|
8722
8723
|
* @param {*} [detail] - Data to pass with the event
|
|
@@ -8743,7 +8744,7 @@
|
|
|
8743
8744
|
* is the first argument so you don't need to destructure `e.detail`.
|
|
8744
8745
|
* Events bubble, so you can listen on an ancestor element.
|
|
8745
8746
|
*
|
|
8746
|
-
* @param {string|Element} target - Element ID,
|
|
8747
|
+
* @param {string|Element} target - Element ID, bw_uuid_* class, CSS selector, or DOM element.
|
|
8747
8748
|
* Uses node cache for O(1) lookup; falls back to DOM query on cache miss.
|
|
8748
8749
|
* @param {string} eventName - Event name (will be prefixed with 'bw:')
|
|
8749
8750
|
* @param {Function} handler - Called with (detail, event)
|
|
@@ -8841,10 +8842,12 @@
|
|
|
8841
8842
|
if (el) {
|
|
8842
8843
|
if (!el._bw_subs) el._bw_subs = [];
|
|
8843
8844
|
el._bw_subs.push(unsub);
|
|
8844
|
-
// Ensure element has
|
|
8845
|
-
if (!
|
|
8846
|
-
|
|
8847
|
-
|
|
8845
|
+
// Ensure element has UUID + bw_lc so bw.cleanup() finds it
|
|
8846
|
+
if (!bw.getUUID(el)) {
|
|
8847
|
+
el.classList.add(bw.uuid('uuid'));
|
|
8848
|
+
}
|
|
8849
|
+
if (!el.classList.contains(_BW_LC)) {
|
|
8850
|
+
el.classList.add(_BW_LC);
|
|
8848
8851
|
}
|
|
8849
8852
|
}
|
|
8850
8853
|
|
|
@@ -9066,1148 +9069,97 @@
|
|
|
9066
9069
|
return result;
|
|
9067
9070
|
};
|
|
9068
9071
|
|
|
9069
|
-
/**
|
|
9070
|
-
* Extract top-level state keys that an expression depends on.
|
|
9071
|
-
* @param {string} expr - Expression string
|
|
9072
|
-
* @param {string[]} stateKeys - Declared state keys
|
|
9073
|
-
* @returns {string[]} Matching dependency keys
|
|
9074
|
-
* @private
|
|
9075
|
-
*/
|
|
9076
|
-
bw._extractDeps = function(expr, stateKeys) {
|
|
9077
|
-
var deps = [];
|
|
9078
|
-
for (var i = 0; i < stateKeys.length; i++) {
|
|
9079
|
-
var key = stateKeys[i];
|
|
9080
|
-
// Match word boundary: key must be preceded by start/non-word and followed by non-word/end
|
|
9081
|
-
var re = new RegExp('(?:^|[^\\w$.])' + key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '(?:[^\\w$]|$)');
|
|
9082
|
-
if (re.test(expr) || expr === key || expr.indexOf(key + '.') === 0) {
|
|
9083
|
-
deps.push(key);
|
|
9084
|
-
}
|
|
9085
|
-
}
|
|
9086
|
-
return deps;
|
|
9087
|
-
};
|
|
9088
|
-
|
|
9089
9072
|
// ===================================================================================
|
|
9090
|
-
//
|
|
9073
|
+
// Deprecation stubs for removed ComponentHandle APIs (v2.0.19)
|
|
9091
9074
|
// ===================================================================================
|
|
9092
9075
|
|
|
9093
|
-
bw.
|
|
9094
|
-
bw.
|
|
9076
|
+
bw._extractDeps = undefined;
|
|
9077
|
+
bw._dirtyComponents = undefined;
|
|
9078
|
+
bw._flushScheduled = undefined;
|
|
9079
|
+
bw._scheduleFlush = undefined;
|
|
9080
|
+
bw._doFlush = undefined;
|
|
9081
|
+
bw._ComponentHandle = undefined;
|
|
9095
9082
|
|
|
9096
9083
|
/**
|
|
9097
|
-
*
|
|
9098
|
-
*
|
|
9084
|
+
* No-op flush (ComponentHandle removed in v2.0.19).
|
|
9085
|
+
* Kept as no-op for backward compatibility.
|
|
9086
|
+
* @category Component
|
|
9099
9087
|
*/
|
|
9100
|
-
bw.
|
|
9101
|
-
if (bw._flushScheduled) return;
|
|
9102
|
-
bw._flushScheduled = true;
|
|
9103
|
-
if (typeof Promise !== 'undefined') {
|
|
9104
|
-
Promise.resolve().then(bw._doFlush);
|
|
9105
|
-
} else {
|
|
9106
|
-
setTimeout(bw._doFlush, 0);
|
|
9107
|
-
}
|
|
9108
|
-
};
|
|
9088
|
+
bw.flush = function() {};
|
|
9109
9089
|
|
|
9110
|
-
|
|
9111
|
-
|
|
9112
|
-
|
|
9113
|
-
|
|
9114
|
-
|
|
9115
|
-
|
|
9116
|
-
|
|
9117
|
-
|
|
9118
|
-
|
|
9119
|
-
var seen = {};
|
|
9120
|
-
for (var i = 0; i < queue.length; i++) {
|
|
9121
|
-
var comp = queue[i];
|
|
9122
|
-
if (!seen[comp._bwId]) {
|
|
9123
|
-
seen[comp._bwId] = true;
|
|
9124
|
-
comp._flush();
|
|
9125
|
-
}
|
|
9126
|
-
}
|
|
9127
|
-
};
|
|
9090
|
+
|
|
9091
|
+
bw.when = function() { throw new Error('bw.when() removed in v2.0.19. Use conditional logic in o.render instead.'); };
|
|
9092
|
+
bw.each = function() { throw new Error('bw.each() removed in v2.0.19. Use array mapping in o.render instead.'); };
|
|
9093
|
+
bw.component = function() { throw new Error('bw.component() removed in v2.0.19. Use o.handle/o.slots on TACO options instead.'); };
|
|
9094
|
+
|
|
9095
|
+
|
|
9096
|
+
// ===================================================================================
|
|
9097
|
+
// bw.message() — SendMessage() for the web
|
|
9098
|
+
// ===================================================================================
|
|
9128
9099
|
|
|
9129
9100
|
/**
|
|
9130
|
-
*
|
|
9131
|
-
*
|
|
9101
|
+
* Dispatch a message to a component by UUID, CSS class, or selector.
|
|
9102
|
+
* Finds the element, looks up el.bw, and calls the named method.
|
|
9103
|
+
* This is the bitwrench equivalent of Win32 SendMessage(hwnd, msg, wParam, lParam).
|
|
9132
9104
|
*
|
|
9105
|
+
* @param {string} target - Component UUID (bw_uuid_*), CSS class, or selector
|
|
9106
|
+
* @param {string} action - Method name to call on el.bw
|
|
9107
|
+
* @param {*} data - Data to pass to the method
|
|
9108
|
+
* @returns {boolean} True if message was dispatched successfully
|
|
9133
9109
|
* @category Component
|
|
9110
|
+
* @example
|
|
9111
|
+
* bw.message('my_carousel', 'goToSlide', 2);
|
|
9112
|
+
* // Or from SSE handler:
|
|
9113
|
+
* es.onmessage = function(e) {
|
|
9114
|
+
* var msg = JSON.parse(e.data);
|
|
9115
|
+
* bw.message(msg.target, msg.action, msg.data);
|
|
9116
|
+
* };
|
|
9134
9117
|
*/
|
|
9135
|
-
bw.
|
|
9136
|
-
bw.
|
|
9118
|
+
bw.message = function(target, action, data) {
|
|
9119
|
+
var el = bw._el(target);
|
|
9120
|
+
if (!el) el = bw.$('.' + target)[0];
|
|
9121
|
+
if (!el || !el.bw || typeof el.bw[action] !== 'function') {
|
|
9122
|
+
_cw('bw.message: no handle method "' + action + '" on ' + target);
|
|
9123
|
+
return false;
|
|
9124
|
+
}
|
|
9125
|
+
el.bw[action](data);
|
|
9126
|
+
return true;
|
|
9137
9127
|
};
|
|
9138
9128
|
|
|
9139
9129
|
// ===================================================================================
|
|
9140
|
-
//
|
|
9130
|
+
// bw.apply() / bw.parseJSONFlex() — Server-driven UI protocol
|
|
9141
9131
|
// ===================================================================================
|
|
9142
9132
|
|
|
9143
9133
|
/**
|
|
9144
|
-
*
|
|
9145
|
-
*
|
|
9146
|
-
*
|
|
9147
|
-
*
|
|
9148
|
-
* @param {Object} taco - TACO definition {t, a, c, o}
|
|
9149
|
-
* @constructor
|
|
9134
|
+
* Registry of named functions sent via register messages.
|
|
9135
|
+
* Populated by bw.apply({ type: 'register', name, body }).
|
|
9136
|
+
* Invoked by bw.apply({ type: 'call', name, args }).
|
|
9150
9137
|
* @private
|
|
9151
9138
|
*/
|
|
9152
|
-
|
|
9153
|
-
this._bwComponent = true; // duck-type marker
|
|
9154
|
-
this._bwId = bw.uuid('comp');
|
|
9155
|
-
this.taco = taco;
|
|
9156
|
-
this.element = null;
|
|
9157
|
-
this.mounted = false;
|
|
9158
|
-
|
|
9159
|
-
var o = taco.o || {};
|
|
9160
|
-
// Copy initial state
|
|
9161
|
-
this._state = {};
|
|
9162
|
-
if (o.state) {
|
|
9163
|
-
for (var k in o.state) {
|
|
9164
|
-
if (_hop.call(o.state, k)) {
|
|
9165
|
-
this._state[k] = o.state[k];
|
|
9166
|
-
}
|
|
9167
|
-
}
|
|
9168
|
-
}
|
|
9169
|
-
// Copy actions
|
|
9170
|
-
this._actions = {};
|
|
9171
|
-
if (o.actions) {
|
|
9172
|
-
for (var k2 in o.actions) {
|
|
9173
|
-
if (_hop.call(o.actions, k2)) {
|
|
9174
|
-
this._actions[k2] = o.actions[k2];
|
|
9175
|
-
}
|
|
9176
|
-
}
|
|
9177
|
-
}
|
|
9178
|
-
// Promote o.methods to handle API (MFC/Qt pattern: component owns its methods)
|
|
9179
|
-
this._methods = {};
|
|
9180
|
-
if (o.methods) {
|
|
9181
|
-
var self = this;
|
|
9182
|
-
for (var k3 in o.methods) {
|
|
9183
|
-
if (_hop.call(o.methods, k3)) {
|
|
9184
|
-
this._methods[k3] = o.methods[k3];
|
|
9185
|
-
(function(methodName, methodFn) {
|
|
9186
|
-
self[methodName] = function() {
|
|
9187
|
-
var args = [self].concat(Array.prototype.slice.call(arguments));
|
|
9188
|
-
return methodFn.apply(null, args);
|
|
9189
|
-
};
|
|
9190
|
-
})(k3, o.methods[k3]);
|
|
9191
|
-
}
|
|
9192
|
-
}
|
|
9193
|
-
}
|
|
9194
|
-
// User tag for addressing via bw.message()
|
|
9195
|
-
this._userTag = null;
|
|
9196
|
-
// Lifecycle hooks
|
|
9197
|
-
this._hooks = {
|
|
9198
|
-
willMount: o.willMount || null,
|
|
9199
|
-
mounted: o.mounted || null,
|
|
9200
|
-
willUpdate: o.willUpdate || null,
|
|
9201
|
-
onUpdate: o.onUpdate || o.updated || null,
|
|
9202
|
-
unmount: o.unmount || null,
|
|
9203
|
-
willDestroy: o.willDestroy || null
|
|
9204
|
-
};
|
|
9205
|
-
// Binding tracking
|
|
9206
|
-
this._bindings = [];
|
|
9207
|
-
this._dirtyKeys = {};
|
|
9208
|
-
this._scheduled = false;
|
|
9209
|
-
this._subs = [];
|
|
9210
|
-
this._eventListeners = [];
|
|
9211
|
-
this._registeredActions = [];
|
|
9212
|
-
this._prevValues = {};
|
|
9213
|
-
this._compile = !!o.compile;
|
|
9214
|
-
this._bw_refs = {};
|
|
9215
|
-
this._refCounter = 0;
|
|
9216
|
-
// Child component ownership (Bug #5)
|
|
9217
|
-
this._children = [];
|
|
9218
|
-
this._parent = null;
|
|
9219
|
-
// Factory metadata for BCCL rebuild (Bug #6)
|
|
9220
|
-
this._factory = taco._bwFactory || null;
|
|
9221
|
-
}
|
|
9222
|
-
|
|
9223
|
-
// Short alias for ComponentHandle.prototype (see alias block at top of file).
|
|
9224
|
-
// 28 method definitions × 25 chars = ~700B raw savings in minified output.
|
|
9225
|
-
var _chp = ComponentHandle.prototype;
|
|
9226
|
-
|
|
9227
|
-
// ── State Methods ──
|
|
9139
|
+
bw._clientFunctions = {};
|
|
9228
9140
|
|
|
9229
9141
|
/**
|
|
9230
|
-
*
|
|
9142
|
+
* Whether exec messages are allowed. Set by bwclient connect opts.allowExec.
|
|
9143
|
+
* Default false — exec messages are rejected unless explicitly opted in.
|
|
9144
|
+
* @private
|
|
9231
9145
|
*/
|
|
9232
|
-
|
|
9233
|
-
return bw._evaluatePath(this._state, key);
|
|
9234
|
-
};
|
|
9146
|
+
bw._allowExec = false;
|
|
9235
9147
|
|
|
9236
9148
|
/**
|
|
9237
|
-
*
|
|
9238
|
-
*
|
|
9239
|
-
*
|
|
9240
|
-
*
|
|
9241
|
-
|
|
9242
|
-
|
|
9243
|
-
|
|
9244
|
-
|
|
9245
|
-
|
|
9246
|
-
|
|
9247
|
-
|
|
9248
|
-
|
|
9249
|
-
|
|
9250
|
-
|
|
9251
|
-
obj = obj[parts[i]];
|
|
9252
|
-
}
|
|
9253
|
-
obj[parts[parts.length - 1]] = value;
|
|
9254
|
-
// Mark top-level key dirty
|
|
9255
|
-
this._dirtyKeys[parts[0]] = true;
|
|
9256
|
-
if (this.mounted) {
|
|
9257
|
-
if (opts && opts.sync) {
|
|
9258
|
-
this._flush();
|
|
9259
|
-
} else {
|
|
9260
|
-
this._scheduleDirty();
|
|
9261
|
-
}
|
|
9262
|
-
}
|
|
9263
|
-
};
|
|
9264
|
-
|
|
9265
|
-
/**
|
|
9266
|
-
* Get a shallow clone of the full state.
|
|
9267
|
-
*/
|
|
9268
|
-
_chp.getState = function() {
|
|
9269
|
-
var clone = {};
|
|
9270
|
-
for (var k in this._state) {
|
|
9271
|
-
if (_hop.call(this._state, k)) {
|
|
9272
|
-
clone[k] = this._state[k];
|
|
9273
|
-
}
|
|
9274
|
-
}
|
|
9275
|
-
return clone;
|
|
9276
|
-
};
|
|
9277
|
-
|
|
9278
|
-
/**
|
|
9279
|
-
* Merge multiple state keys. Schedules re-render.
|
|
9280
|
-
* @param {Object} updates - Key-value pairs to merge
|
|
9281
|
-
* @param {Object} [opts] - Options. `{sync: true}` for immediate flush.
|
|
9282
|
-
*/
|
|
9283
|
-
_chp.setState = function(updates, opts) {
|
|
9284
|
-
for (var k in updates) {
|
|
9285
|
-
if (_hop.call(updates, k)) {
|
|
9286
|
-
this._state[k] = updates[k];
|
|
9287
|
-
this._dirtyKeys[k] = true;
|
|
9288
|
-
}
|
|
9289
|
-
}
|
|
9290
|
-
if (this.mounted) {
|
|
9291
|
-
if (opts && opts.sync) {
|
|
9292
|
-
this._flush();
|
|
9293
|
-
} else {
|
|
9294
|
-
this._scheduleDirty();
|
|
9295
|
-
}
|
|
9296
|
-
}
|
|
9297
|
-
};
|
|
9298
|
-
|
|
9299
|
-
/**
|
|
9300
|
-
* Push a value onto an array in state. Clones the array.
|
|
9301
|
-
*/
|
|
9302
|
-
_chp.push = function(key, val) {
|
|
9303
|
-
var arr = this.get(key);
|
|
9304
|
-
var newArr = _isA(arr) ? arr.slice() : [];
|
|
9305
|
-
newArr.push(val);
|
|
9306
|
-
this.set(key, newArr);
|
|
9307
|
-
};
|
|
9308
|
-
|
|
9309
|
-
/**
|
|
9310
|
-
* Splice an array in state. Clones the array.
|
|
9311
|
-
*/
|
|
9312
|
-
_chp.splice = function(key, start, deleteCount) {
|
|
9313
|
-
var arr = this.get(key);
|
|
9314
|
-
var newArr = _isA(arr) ? arr.slice() : [];
|
|
9315
|
-
var args = [start, deleteCount].concat(Array.prototype.slice.call(arguments, 3));
|
|
9316
|
-
Array.prototype.splice.apply(newArr, args);
|
|
9317
|
-
this.set(key, newArr);
|
|
9318
|
-
};
|
|
9319
|
-
|
|
9320
|
-
// ── Scheduling ──
|
|
9321
|
-
|
|
9322
|
-
_chp._scheduleDirty = function() {
|
|
9323
|
-
if (!this._scheduled) {
|
|
9324
|
-
this._scheduled = true;
|
|
9325
|
-
bw._dirtyComponents.push(this);
|
|
9326
|
-
bw._scheduleFlush();
|
|
9327
|
-
}
|
|
9328
|
-
};
|
|
9329
|
-
|
|
9330
|
-
// ── Binding Compilation ──
|
|
9331
|
-
|
|
9332
|
-
/**
|
|
9333
|
-
* Walk the TACO tree and extract ${expr} bindings.
|
|
9334
|
-
* Creates binding descriptors with refIds for targeted DOM updates.
|
|
9335
|
-
* @private
|
|
9336
|
-
*/
|
|
9337
|
-
_chp._compileBindings = function() {
|
|
9338
|
-
this._bindings = [];
|
|
9339
|
-
this._refCounter = 0;
|
|
9340
|
-
var stateKeys = _keys(this._state);
|
|
9341
|
-
var self = this;
|
|
9342
|
-
|
|
9343
|
-
function walkTaco(taco, path) {
|
|
9344
|
-
if (!_is(taco, 'object') || !taco.t) return taco;
|
|
9345
|
-
|
|
9346
|
-
// Check content for bindings
|
|
9347
|
-
if (_is(taco.c, 'string') && taco.c.indexOf('${') >= 0) {
|
|
9348
|
-
var refId = 'bw_ref_' + self._refCounter++;
|
|
9349
|
-
var parsed = bw._parseBindings(taco.c);
|
|
9350
|
-
var deps = [];
|
|
9351
|
-
for (var j = 0; j < parsed.length; j++) {
|
|
9352
|
-
deps = deps.concat(bw._extractDeps(parsed[j].expr, stateKeys));
|
|
9353
|
-
}
|
|
9354
|
-
self._bindings.push({
|
|
9355
|
-
expr: taco.c,
|
|
9356
|
-
type: 'content',
|
|
9357
|
-
refId: refId,
|
|
9358
|
-
deps: deps,
|
|
9359
|
-
template: taco.c
|
|
9360
|
-
});
|
|
9361
|
-
// Inject data-bw_ref on the TACO for createDOM to pick up
|
|
9362
|
-
if (!taco.a) taco.a = {};
|
|
9363
|
-
taco.a['data-bw_ref'] = refId;
|
|
9364
|
-
}
|
|
9365
|
-
|
|
9366
|
-
// Check attributes for bindings
|
|
9367
|
-
if (taco.a) {
|
|
9368
|
-
for (var attrName in taco.a) {
|
|
9369
|
-
if (!_hop.call(taco.a, attrName)) continue;
|
|
9370
|
-
if (attrName === 'data-bw_ref') continue;
|
|
9371
|
-
var attrVal = taco.a[attrName];
|
|
9372
|
-
if (_is(attrVal, 'string') && attrVal.indexOf('${') >= 0) {
|
|
9373
|
-
var refId2 = 'bw_ref_' + self._refCounter++;
|
|
9374
|
-
var parsed2 = bw._parseBindings(attrVal);
|
|
9375
|
-
var deps2 = [];
|
|
9376
|
-
for (var j2 = 0; j2 < parsed2.length; j2++) {
|
|
9377
|
-
deps2 = deps2.concat(bw._extractDeps(parsed2[j2].expr, stateKeys));
|
|
9378
|
-
}
|
|
9379
|
-
self._bindings.push({
|
|
9380
|
-
expr: attrVal,
|
|
9381
|
-
type: 'attribute',
|
|
9382
|
-
attrName: attrName,
|
|
9383
|
-
refId: refId2,
|
|
9384
|
-
deps: deps2,
|
|
9385
|
-
template: attrVal
|
|
9386
|
-
});
|
|
9387
|
-
if (!taco.a) taco.a = {};
|
|
9388
|
-
taco.a['data-bw_ref'] = taco.a['data-bw_ref'] || refId2;
|
|
9389
|
-
// If multiple attribute bindings on same element, store additional marker
|
|
9390
|
-
if (taco.a['data-bw_ref'] !== refId2) {
|
|
9391
|
-
taco.a['data-bw_ref_' + attrName] = refId2;
|
|
9392
|
-
}
|
|
9393
|
-
}
|
|
9394
|
-
}
|
|
9395
|
-
}
|
|
9396
|
-
|
|
9397
|
-
// Recurse into children
|
|
9398
|
-
if (_isA(taco.c)) {
|
|
9399
|
-
for (var i = 0; i < taco.c.length; i++) {
|
|
9400
|
-
// Wrap string children with ${expr} in a span so patches target the span, not the parent
|
|
9401
|
-
if (_is(taco.c[i], 'string') && taco.c[i].indexOf('${') >= 0) {
|
|
9402
|
-
var mixedRefId = 'bw_ref_' + self._refCounter++;
|
|
9403
|
-
var mixedParsed = bw._parseBindings(taco.c[i]);
|
|
9404
|
-
var mixedDeps = [];
|
|
9405
|
-
for (var mi = 0; mi < mixedParsed.length; mi++) {
|
|
9406
|
-
mixedDeps = mixedDeps.concat(bw._extractDeps(mixedParsed[mi].expr, stateKeys));
|
|
9407
|
-
}
|
|
9408
|
-
self._bindings.push({
|
|
9409
|
-
expr: taco.c[i],
|
|
9410
|
-
type: 'content',
|
|
9411
|
-
refId: mixedRefId,
|
|
9412
|
-
deps: mixedDeps,
|
|
9413
|
-
template: taco.c[i]
|
|
9414
|
-
});
|
|
9415
|
-
// Replace string with a span wrapper so textContent targets the span only
|
|
9416
|
-
taco.c[i] = { t: 'span', a: { 'data-bw_ref': mixedRefId, style: 'display:contents' }, c: taco.c[i] };
|
|
9417
|
-
}
|
|
9418
|
-
if (_is(taco.c[i], 'object') && taco.c[i].t) {
|
|
9419
|
-
walkTaco(taco.c[i], path.concat(i));
|
|
9420
|
-
}
|
|
9421
|
-
// Handle bw.when/bw.each markers
|
|
9422
|
-
if (taco.c[i] && taco.c[i]._bwWhen) {
|
|
9423
|
-
var whenRefId = 'bw_ref_' + self._refCounter++;
|
|
9424
|
-
var whenDeps = bw._extractDeps(taco.c[i].expr.replace(/^\$\{|\}$/g, ''), stateKeys);
|
|
9425
|
-
self._bindings.push({
|
|
9426
|
-
expr: taco.c[i].expr,
|
|
9427
|
-
type: 'structural',
|
|
9428
|
-
subtype: 'when',
|
|
9429
|
-
refId: whenRefId,
|
|
9430
|
-
deps: whenDeps,
|
|
9431
|
-
branches: taco.c[i].branches,
|
|
9432
|
-
index: i,
|
|
9433
|
-
parentPath: path
|
|
9434
|
-
});
|
|
9435
|
-
taco.c[i]._refId = whenRefId;
|
|
9436
|
-
}
|
|
9437
|
-
if (taco.c[i] && taco.c[i]._bwEach) {
|
|
9438
|
-
var eachRefId = 'bw_ref_' + self._refCounter++;
|
|
9439
|
-
var eachDeps = bw._extractDeps(taco.c[i].expr.replace(/^\$\{|\}$/g, ''), stateKeys);
|
|
9440
|
-
self._bindings.push({
|
|
9441
|
-
expr: taco.c[i].expr,
|
|
9442
|
-
type: 'structural',
|
|
9443
|
-
subtype: 'each',
|
|
9444
|
-
refId: eachRefId,
|
|
9445
|
-
deps: eachDeps,
|
|
9446
|
-
factory: taco.c[i].factory,
|
|
9447
|
-
index: i,
|
|
9448
|
-
parentPath: path
|
|
9449
|
-
});
|
|
9450
|
-
taco.c[i]._refId = eachRefId;
|
|
9451
|
-
}
|
|
9452
|
-
}
|
|
9453
|
-
} else if (_is(taco.c, 'object') && taco.c.t) {
|
|
9454
|
-
walkTaco(taco.c, path.concat(0));
|
|
9455
|
-
}
|
|
9456
|
-
|
|
9457
|
-
return taco;
|
|
9458
|
-
}
|
|
9459
|
-
|
|
9460
|
-
walkTaco(this.taco, []);
|
|
9461
|
-
};
|
|
9462
|
-
|
|
9463
|
-
// ── DOM Reference Collection ──
|
|
9464
|
-
|
|
9465
|
-
/**
|
|
9466
|
-
* Build ref map from the live DOM after createDOM.
|
|
9467
|
-
* @private
|
|
9468
|
-
*/
|
|
9469
|
-
_chp._collectRefs = function() {
|
|
9470
|
-
this._bw_refs = {};
|
|
9471
|
-
if (!this.element) return;
|
|
9472
|
-
var els = this.element.querySelectorAll('[data-bw_ref]');
|
|
9473
|
-
for (var i = 0; i < els.length; i++) {
|
|
9474
|
-
this._bw_refs[els[i].getAttribute('data-bw_ref')] = els[i];
|
|
9475
|
-
}
|
|
9476
|
-
// Also check root element
|
|
9477
|
-
var rootRef = this.element.getAttribute && this.element.getAttribute('data-bw_ref');
|
|
9478
|
-
if (rootRef) {
|
|
9479
|
-
this._bw_refs[rootRef] = this.element;
|
|
9480
|
-
}
|
|
9481
|
-
};
|
|
9482
|
-
|
|
9483
|
-
// ── Lifecycle ──
|
|
9484
|
-
|
|
9485
|
-
/**
|
|
9486
|
-
* Mount the component into a parent DOM element.
|
|
9487
|
-
* Creates DOM, compiles bindings, registers actions, and calls lifecycle hooks.
|
|
9488
|
-
* @param {Element} parentEl - DOM element to mount into
|
|
9489
|
-
*/
|
|
9490
|
-
_chp.mount = function(parentEl) {
|
|
9491
|
-
// willMount hook
|
|
9492
|
-
if (this._hooks.willMount) this._hooks.willMount(this);
|
|
9493
|
-
|
|
9494
|
-
// Save original TACO for re-renders (structural changes clone from this)
|
|
9495
|
-
if (!this._originalTaco) {
|
|
9496
|
-
this._originalTaco = this.taco;
|
|
9497
|
-
}
|
|
9498
|
-
|
|
9499
|
-
// Deep-clone TACO so binding annotations don't mutate original.
|
|
9500
|
-
// Custom clone to preserve _bwWhen/_bwEach markers and their factory functions.
|
|
9501
|
-
this.taco = this._deepCloneTaco(this._originalTaco);
|
|
9502
|
-
|
|
9503
|
-
// Compile bindings (annotates TACO with data-bw_ref attributes)
|
|
9504
|
-
this._compileBindings();
|
|
9505
|
-
|
|
9506
|
-
// Prepare TACO: resolve initial binding values, evaluate when/each
|
|
9507
|
-
this._prepareTaco(this.taco);
|
|
9508
|
-
|
|
9509
|
-
// Register named actions in function registry
|
|
9510
|
-
var self = this;
|
|
9511
|
-
for (var actionName in this._actions) {
|
|
9512
|
-
if (_hop.call(this._actions, actionName)) {
|
|
9513
|
-
var registeredName = this._bwId + '_' + actionName;
|
|
9514
|
-
(function(aName) {
|
|
9515
|
-
bw.funcRegister(function(evt) {
|
|
9516
|
-
self._actions[aName](self, evt);
|
|
9517
|
-
}, registeredName);
|
|
9518
|
-
})(actionName);
|
|
9519
|
-
this._registeredActions.push(registeredName);
|
|
9520
|
-
}
|
|
9521
|
-
}
|
|
9522
|
-
|
|
9523
|
-
// Wire action names in onclick etc. to dispatch strings
|
|
9524
|
-
this._wireActions(this.taco);
|
|
9525
|
-
|
|
9526
|
-
// Create DOM (strip o before createDOM to prevent double lifecycle)
|
|
9527
|
-
var tacoForDOM = this._tacoForDOM(this.taco);
|
|
9528
|
-
this.element = bw.createDOM(tacoForDOM);
|
|
9529
|
-
this.element._bwComponentHandle = this;
|
|
9530
|
-
this.element.setAttribute('data-bw_comp_id', this._bwId);
|
|
9531
|
-
|
|
9532
|
-
// Restore o.render from original TACO (stripped by _tacoForDOM)
|
|
9533
|
-
if (this.taco.o && this.taco.o.render) {
|
|
9534
|
-
this.element._bw_render = this.taco.o.render;
|
|
9535
|
-
}
|
|
9536
|
-
if (this._userTag) {
|
|
9537
|
-
this.element.classList.add(this._userTag);
|
|
9538
|
-
}
|
|
9539
|
-
|
|
9540
|
-
// Append to parent
|
|
9541
|
-
parentEl.appendChild(this.element);
|
|
9542
|
-
|
|
9543
|
-
// Collect refs from live DOM
|
|
9544
|
-
this._collectRefs();
|
|
9545
|
-
|
|
9546
|
-
// Resolve initial bindings and apply to DOM
|
|
9547
|
-
this._resolveAndApplyAll();
|
|
9548
|
-
|
|
9549
|
-
this.mounted = true;
|
|
9550
|
-
|
|
9551
|
-
// Scan for child ComponentHandles and link parent/child (Bug #5)
|
|
9552
|
-
var childEls = this.element.querySelectorAll('[data-bw_comp_id]');
|
|
9553
|
-
for (var ci = 0; ci < childEls.length; ci++) {
|
|
9554
|
-
var ch = childEls[ci]._bwComponentHandle;
|
|
9555
|
-
if (ch && ch !== this && !ch._parent) {
|
|
9556
|
-
ch._parent = this;
|
|
9557
|
-
this._children.push(ch);
|
|
9558
|
-
}
|
|
9559
|
-
}
|
|
9560
|
-
|
|
9561
|
-
// mounted hook (backward compat: fn.length === 2 wraps (el, state))
|
|
9562
|
-
if (this._hooks.mounted) {
|
|
9563
|
-
if (this._hooks.mounted.length === 2) {
|
|
9564
|
-
this._hooks.mounted(this.element, this.getState());
|
|
9565
|
-
} else {
|
|
9566
|
-
this._hooks.mounted(this);
|
|
9567
|
-
}
|
|
9568
|
-
}
|
|
9569
|
-
|
|
9570
|
-
// Invoke o.render on initial mount (if present)
|
|
9571
|
-
if (this.element._bw_render) {
|
|
9572
|
-
this.element._bw_render(this.element, this._state);
|
|
9573
|
-
}
|
|
9574
|
-
};
|
|
9575
|
-
|
|
9576
|
-
/**
|
|
9577
|
-
* Prepare TACO for initial render: resolve when/each markers.
|
|
9578
|
-
* @private
|
|
9579
|
-
*/
|
|
9580
|
-
_chp._prepareTaco = function(taco) {
|
|
9581
|
-
if (!_is(taco, 'object')) return;
|
|
9582
|
-
|
|
9583
|
-
if (_isA(taco.c)) {
|
|
9584
|
-
for (var i = taco.c.length - 1; i >= 0; i--) {
|
|
9585
|
-
var child = taco.c[i];
|
|
9586
|
-
if (child && child._bwWhen) {
|
|
9587
|
-
var exprStr = child.expr.replace(/^\$\{|\}$/g, '');
|
|
9588
|
-
var val;
|
|
9589
|
-
if (this._compile) {
|
|
9590
|
-
try {
|
|
9591
|
-
val = (new Function('state', 'with(state){return (' + exprStr + ');}'))(this._state);
|
|
9592
|
-
} catch(e) { val = false; }
|
|
9593
|
-
} else {
|
|
9594
|
-
val = bw._evaluatePath(this._state, exprStr);
|
|
9595
|
-
}
|
|
9596
|
-
var branch = val ? child.branches[0] : (child.branches[1] || null);
|
|
9597
|
-
if (branch) {
|
|
9598
|
-
// Wrap in a container so we can track it
|
|
9599
|
-
taco.c[i] = { t: 'span', a: { 'data-bw_when': child._refId, style: 'display:contents' }, c: branch };
|
|
9600
|
-
} else {
|
|
9601
|
-
taco.c[i] = { t: 'span', a: { 'data-bw_when': child._refId, style: 'display:contents' }, c: '' };
|
|
9602
|
-
}
|
|
9603
|
-
}
|
|
9604
|
-
if (child && child._bwEach) {
|
|
9605
|
-
var eachExprStr = child.expr.replace(/^\$\{|\}$/g, '');
|
|
9606
|
-
var arr = bw._evaluatePath(this._state, eachExprStr);
|
|
9607
|
-
var items = [];
|
|
9608
|
-
if (_isA(arr)) {
|
|
9609
|
-
for (var j = 0; j < arr.length; j++) {
|
|
9610
|
-
items.push(child.factory(arr[j], j));
|
|
9611
|
-
}
|
|
9612
|
-
}
|
|
9613
|
-
taco.c[i] = { t: 'span', a: { 'data-bw_each': child._refId, style: 'display:contents' }, c: items };
|
|
9614
|
-
}
|
|
9615
|
-
if (_is(taco.c[i], 'object') && taco.c[i].t) {
|
|
9616
|
-
this._prepareTaco(taco.c[i]);
|
|
9617
|
-
}
|
|
9618
|
-
}
|
|
9619
|
-
} else if (_is(taco.c, 'object') && taco.c.t) {
|
|
9620
|
-
this._prepareTaco(taco.c);
|
|
9621
|
-
}
|
|
9622
|
-
};
|
|
9623
|
-
|
|
9624
|
-
/**
|
|
9625
|
-
* Wire action name strings (in onclick etc.) to dispatch function calls.
|
|
9626
|
-
* @private
|
|
9627
|
-
*/
|
|
9628
|
-
_chp._wireActions = function(taco) {
|
|
9629
|
-
if (!_is(taco, 'object') || !taco.t) return;
|
|
9630
|
-
if (taco.a) {
|
|
9631
|
-
for (var key in taco.a) {
|
|
9632
|
-
if (!_hop.call(taco.a, key)) continue;
|
|
9633
|
-
if (key.startsWith('on') && _is(taco.a[key], 'string')) {
|
|
9634
|
-
var actionName = taco.a[key];
|
|
9635
|
-
if (actionName in this._actions) {
|
|
9636
|
-
var registeredName = this._bwId + '_' + actionName;
|
|
9637
|
-
// Replace string with actual function for createDOM event binding
|
|
9638
|
-
(function(rName) {
|
|
9639
|
-
taco.a[key] = function(evt) {
|
|
9640
|
-
bw.funcGetById(rName)(evt);
|
|
9641
|
-
};
|
|
9642
|
-
})(registeredName);
|
|
9643
|
-
}
|
|
9644
|
-
}
|
|
9645
|
-
}
|
|
9646
|
-
}
|
|
9647
|
-
if (_isA(taco.c)) {
|
|
9648
|
-
for (var i = 0; i < taco.c.length; i++) {
|
|
9649
|
-
this._wireActions(taco.c[i]);
|
|
9650
|
-
}
|
|
9651
|
-
} else if (_is(taco.c, 'object') && taco.c.t) {
|
|
9652
|
-
this._wireActions(taco.c);
|
|
9653
|
-
}
|
|
9654
|
-
};
|
|
9655
|
-
|
|
9656
|
-
/**
|
|
9657
|
-
* Deep-clone a TACO tree, preserving _bwWhen/_bwEach markers and their factories.
|
|
9658
|
-
* @private
|
|
9659
|
-
*/
|
|
9660
|
-
_chp._deepCloneTaco = function(taco) {
|
|
9661
|
-
if (taco == null) return taco;
|
|
9662
|
-
// Preserve _bwWhen / _bwEach markers (contain functions)
|
|
9663
|
-
if (taco._bwWhen) {
|
|
9664
|
-
return { _bwWhen: true, expr: taco.expr, branches: [
|
|
9665
|
-
this._deepCloneTaco(taco.branches[0]),
|
|
9666
|
-
taco.branches[1] ? this._deepCloneTaco(taco.branches[1]) : null
|
|
9667
|
-
], _refId: taco._refId };
|
|
9668
|
-
}
|
|
9669
|
-
if (taco._bwEach) {
|
|
9670
|
-
return { _bwEach: true, expr: taco.expr, factory: taco.factory, _refId: taco._refId };
|
|
9671
|
-
}
|
|
9672
|
-
if (!_is(taco, 'object') || !taco.t) return taco;
|
|
9673
|
-
var result = { t: taco.t };
|
|
9674
|
-
if (taco.a) {
|
|
9675
|
-
result.a = {};
|
|
9676
|
-
for (var k in taco.a) {
|
|
9677
|
-
if (_hop.call(taco.a, k)) result.a[k] = taco.a[k];
|
|
9678
|
-
}
|
|
9679
|
-
}
|
|
9680
|
-
if (taco.c != null) {
|
|
9681
|
-
if (_isA(taco.c)) {
|
|
9682
|
-
result.c = taco.c.map(function(child) { return this._deepCloneTaco(child); }.bind(this));
|
|
9683
|
-
} else if (_is(taco.c, 'object')) {
|
|
9684
|
-
result.c = this._deepCloneTaco(taco.c);
|
|
9685
|
-
} else {
|
|
9686
|
-
result.c = taco.c;
|
|
9687
|
-
}
|
|
9688
|
-
}
|
|
9689
|
-
if (taco.o) result.o = taco.o; // Keep o reference (not deep-cloned; hooks are functions)
|
|
9690
|
-
return result;
|
|
9691
|
-
};
|
|
9692
|
-
|
|
9693
|
-
/**
|
|
9694
|
-
* Create a copy of TACO suitable for createDOM (strips o to prevent double lifecycle).
|
|
9695
|
-
* @private
|
|
9696
|
-
*/
|
|
9697
|
-
_chp._tacoForDOM = function(taco) {
|
|
9698
|
-
if (!_is(taco, 'object') || !taco.t) return taco;
|
|
9699
|
-
var result = { t: taco.t };
|
|
9700
|
-
if (taco.a) result.a = taco.a;
|
|
9701
|
-
if (taco.c != null) {
|
|
9702
|
-
if (_isA(taco.c)) {
|
|
9703
|
-
result.c = taco.c.map(function(child) { return this._tacoForDOM(child); }.bind(this));
|
|
9704
|
-
} else if (_is(taco.c, 'object') && taco.c.t) {
|
|
9705
|
-
result.c = this._tacoForDOM(taco.c);
|
|
9706
|
-
} else {
|
|
9707
|
-
result.c = taco.c;
|
|
9708
|
-
}
|
|
9709
|
-
}
|
|
9710
|
-
// Intentionally strip o (no mounted/unmount/state/render on sub-elements)
|
|
9711
|
-
if (taco.o && (taco.o.mounted || taco.o.render || taco.o.unmount)) {
|
|
9712
|
-
_cw('bw: _tacoForDOM stripped o.mounted/render/unmount from child <' + taco.t +
|
|
9713
|
-
'>. Use onclick attribute or bw.component() for child interactivity.');
|
|
9714
|
-
}
|
|
9715
|
-
return result;
|
|
9716
|
-
};
|
|
9717
|
-
|
|
9718
|
-
/**
|
|
9719
|
-
* Unmount: remove from DOM, deactivate, preserve state for re-mount.
|
|
9720
|
-
*/
|
|
9721
|
-
_chp.unmount = function() {
|
|
9722
|
-
if (!this.mounted) return;
|
|
9723
|
-
|
|
9724
|
-
// unmount hook
|
|
9725
|
-
if (this._hooks.unmount) {
|
|
9726
|
-
this._hooks.unmount(this);
|
|
9727
|
-
}
|
|
9728
|
-
|
|
9729
|
-
// Remove DOM event listeners
|
|
9730
|
-
for (var i = 0; i < this._eventListeners.length; i++) {
|
|
9731
|
-
var l = this._eventListeners[i];
|
|
9732
|
-
if (this.element) {
|
|
9733
|
-
this.element.removeEventListener(l.event, l.handler);
|
|
9734
|
-
}
|
|
9735
|
-
}
|
|
9736
|
-
this._eventListeners = [];
|
|
9737
|
-
|
|
9738
|
-
// Unsubscribe pub/sub
|
|
9739
|
-
for (var j = 0; j < this._subs.length; j++) {
|
|
9740
|
-
this._subs[j]();
|
|
9741
|
-
}
|
|
9742
|
-
this._subs = [];
|
|
9743
|
-
|
|
9744
|
-
// Remove from DOM
|
|
9745
|
-
if (this.element && this.element.parentNode) {
|
|
9746
|
-
this.element.parentNode.removeChild(this.element);
|
|
9747
|
-
}
|
|
9748
|
-
|
|
9749
|
-
this.mounted = false;
|
|
9750
|
-
// State preserved — can re-mount
|
|
9751
|
-
};
|
|
9752
|
-
|
|
9753
|
-
/**
|
|
9754
|
-
* Destroy: unmount + clear state + unregister actions.
|
|
9755
|
-
*/
|
|
9756
|
-
_chp.destroy = function() {
|
|
9757
|
-
// willDestroy hook
|
|
9758
|
-
if (this._hooks.willDestroy) {
|
|
9759
|
-
this._hooks.willDestroy(this);
|
|
9760
|
-
}
|
|
9761
|
-
|
|
9762
|
-
// Cascade destroy to children depth-first (Bug #5)
|
|
9763
|
-
for (var ci = this._children.length - 1; ci >= 0; ci--) {
|
|
9764
|
-
this._children[ci].destroy();
|
|
9765
|
-
}
|
|
9766
|
-
this._children = [];
|
|
9767
|
-
if (this._parent) {
|
|
9768
|
-
var idx = this._parent._children.indexOf(this);
|
|
9769
|
-
if (idx >= 0) this._parent._children.splice(idx, 1);
|
|
9770
|
-
this._parent = null;
|
|
9771
|
-
}
|
|
9772
|
-
|
|
9773
|
-
this.unmount();
|
|
9774
|
-
|
|
9775
|
-
// Unregister actions from function registry
|
|
9776
|
-
for (var i = 0; i < this._registeredActions.length; i++) {
|
|
9777
|
-
bw.funcUnregister(this._registeredActions[i]);
|
|
9778
|
-
}
|
|
9779
|
-
this._registeredActions = [];
|
|
9780
|
-
|
|
9781
|
-
// Clear state
|
|
9782
|
-
this._state = {};
|
|
9783
|
-
this._bindings = [];
|
|
9784
|
-
this._bw_refs = {};
|
|
9785
|
-
this._prevValues = {};
|
|
9786
|
-
this._dirtyKeys = {};
|
|
9787
|
-
if (this.element) {
|
|
9788
|
-
delete this.element._bwComponentHandle;
|
|
9789
|
-
this.element = null;
|
|
9790
|
-
}
|
|
9791
|
-
};
|
|
9792
|
-
|
|
9793
|
-
// ── Flush & Binding Resolution ──
|
|
9794
|
-
|
|
9795
|
-
/**
|
|
9796
|
-
* Flush dirty state: resolve changed bindings and apply to DOM.
|
|
9797
|
-
* @private
|
|
9798
|
-
*/
|
|
9799
|
-
_chp._flush = function() {
|
|
9800
|
-
this._scheduled = false;
|
|
9801
|
-
var changedKeys = _keys(this._dirtyKeys);
|
|
9802
|
-
this._dirtyKeys = {};
|
|
9803
|
-
if (changedKeys.length === 0 || !this.mounted) return;
|
|
9804
|
-
|
|
9805
|
-
// Factory rebuild: if a BCCL factory exists and changed keys overlap factory props,
|
|
9806
|
-
// rebuild the TACO from the factory with merged state (Bug #6)
|
|
9807
|
-
if (this._factory) {
|
|
9808
|
-
var rebuildNeeded = false;
|
|
9809
|
-
for (var fi = 0; fi < changedKeys.length; fi++) {
|
|
9810
|
-
if (_hop.call(this._factory.props, changedKeys[fi])) {
|
|
9811
|
-
rebuildNeeded = true; break;
|
|
9812
|
-
}
|
|
9813
|
-
}
|
|
9814
|
-
if (rebuildNeeded) {
|
|
9815
|
-
var merged = {};
|
|
9816
|
-
for (var mk in this._factory.props) if (_hop.call(this._factory.props, mk)) merged[mk] = this._factory.props[mk];
|
|
9817
|
-
for (var sk in this._state) if (_hop.call(this._state, sk)) merged[sk] = this._state[sk];
|
|
9818
|
-
this._factory.props = merged;
|
|
9819
|
-
var newTaco = bw.make(this._factory.type, merged);
|
|
9820
|
-
newTaco._bwFactory = this._factory;
|
|
9821
|
-
this.taco = newTaco;
|
|
9822
|
-
this._originalTaco = this._deepCloneTaco(newTaco);
|
|
9823
|
-
this._render();
|
|
9824
|
-
if (this._hooks.onUpdate) this._hooks.onUpdate(this, changedKeys);
|
|
9825
|
-
return;
|
|
9826
|
-
}
|
|
9827
|
-
}
|
|
9828
|
-
|
|
9829
|
-
// willUpdate hook
|
|
9830
|
-
if (this._hooks.willUpdate) {
|
|
9831
|
-
this._hooks.willUpdate(this, changedKeys);
|
|
9832
|
-
}
|
|
9833
|
-
|
|
9834
|
-
// Check if any structural bindings are affected
|
|
9835
|
-
var needsFullRender = false;
|
|
9836
|
-
for (var i = 0; i < this._bindings.length; i++) {
|
|
9837
|
-
var b = this._bindings[i];
|
|
9838
|
-
if (b.type === 'structural') {
|
|
9839
|
-
for (var j = 0; j < b.deps.length; j++) {
|
|
9840
|
-
if (changedKeys.indexOf(b.deps[j]) >= 0) {
|
|
9841
|
-
needsFullRender = true;
|
|
9842
|
-
break;
|
|
9843
|
-
}
|
|
9844
|
-
}
|
|
9845
|
-
if (needsFullRender) break;
|
|
9846
|
-
}
|
|
9847
|
-
}
|
|
9848
|
-
|
|
9849
|
-
if (needsFullRender) {
|
|
9850
|
-
this._render();
|
|
9851
|
-
} else {
|
|
9852
|
-
var patches = this._resolveBindings(changedKeys);
|
|
9853
|
-
this._applyPatches(patches);
|
|
9854
|
-
}
|
|
9855
|
-
|
|
9856
|
-
// onUpdate hook
|
|
9857
|
-
if (this._hooks.onUpdate) {
|
|
9858
|
-
this._hooks.onUpdate(this, changedKeys);
|
|
9859
|
-
}
|
|
9860
|
-
};
|
|
9861
|
-
|
|
9862
|
-
/**
|
|
9863
|
-
* Resolve bindings whose deps intersect with changedKeys.
|
|
9864
|
-
* Returns list of patches to apply.
|
|
9865
|
-
* @private
|
|
9866
|
-
*/
|
|
9867
|
-
_chp._resolveBindings = function(changedKeys) {
|
|
9868
|
-
var patches = [];
|
|
9869
|
-
for (var i = 0; i < this._bindings.length; i++) {
|
|
9870
|
-
var b = this._bindings[i];
|
|
9871
|
-
if (b.type === 'structural') continue;
|
|
9872
|
-
|
|
9873
|
-
// Check if any dep matches
|
|
9874
|
-
var affected = false;
|
|
9875
|
-
for (var j = 0; j < b.deps.length; j++) {
|
|
9876
|
-
if (changedKeys.indexOf(b.deps[j]) >= 0) {
|
|
9877
|
-
affected = true;
|
|
9878
|
-
break;
|
|
9879
|
-
}
|
|
9880
|
-
}
|
|
9881
|
-
if (!affected) continue;
|
|
9882
|
-
|
|
9883
|
-
// Evaluate
|
|
9884
|
-
var newVal = bw._resolveTemplate(b.template, this._state, this._compile);
|
|
9885
|
-
var prevKey = b.refId + '_' + (b.attrName || 'content');
|
|
9886
|
-
if (this._prevValues[prevKey] !== newVal) {
|
|
9887
|
-
this._prevValues[prevKey] = newVal;
|
|
9888
|
-
patches.push({
|
|
9889
|
-
refId: b.refId,
|
|
9890
|
-
type: b.type,
|
|
9891
|
-
attrName: b.attrName,
|
|
9892
|
-
value: newVal
|
|
9893
|
-
});
|
|
9894
|
-
}
|
|
9895
|
-
}
|
|
9896
|
-
return patches;
|
|
9897
|
-
};
|
|
9898
|
-
|
|
9899
|
-
/**
|
|
9900
|
-
* Apply patches to DOM.
|
|
9901
|
-
* @private
|
|
9902
|
-
*/
|
|
9903
|
-
_chp._applyPatches = function(patches) {
|
|
9904
|
-
for (var i = 0; i < patches.length; i++) {
|
|
9905
|
-
var p = patches[i];
|
|
9906
|
-
var el = this._bw_refs[p.refId];
|
|
9907
|
-
if (!el) {
|
|
9908
|
-
if (bw.debug) _cw('bw.debug: _applyPatches — ref "' + p.refId + '" not found in DOM');
|
|
9909
|
-
continue;
|
|
9910
|
-
}
|
|
9911
|
-
if (p.type === 'content') {
|
|
9912
|
-
el.textContent = p.value;
|
|
9913
|
-
} else if (p.type === 'attribute') {
|
|
9914
|
-
if (p.attrName === 'class') {
|
|
9915
|
-
el.className = p.value;
|
|
9916
|
-
} else {
|
|
9917
|
-
el.setAttribute(p.attrName, p.value);
|
|
9918
|
-
}
|
|
9919
|
-
}
|
|
9920
|
-
}
|
|
9921
|
-
};
|
|
9922
|
-
|
|
9923
|
-
/**
|
|
9924
|
-
* Resolve all bindings and apply (used for initial render).
|
|
9925
|
-
* @private
|
|
9926
|
-
*/
|
|
9927
|
-
_chp._resolveAndApplyAll = function() {
|
|
9928
|
-
var patches = [];
|
|
9929
|
-
for (var i = 0; i < this._bindings.length; i++) {
|
|
9930
|
-
var b = this._bindings[i];
|
|
9931
|
-
if (b.type === 'structural') continue;
|
|
9932
|
-
|
|
9933
|
-
var newVal = bw._resolveTemplate(b.template, this._state, this._compile);
|
|
9934
|
-
var prevKey = b.refId + '_' + (b.attrName || 'content');
|
|
9935
|
-
this._prevValues[prevKey] = newVal;
|
|
9936
|
-
patches.push({
|
|
9937
|
-
refId: b.refId,
|
|
9938
|
-
type: b.type,
|
|
9939
|
-
attrName: b.attrName,
|
|
9940
|
-
value: newVal
|
|
9941
|
-
});
|
|
9942
|
-
}
|
|
9943
|
-
this._applyPatches(patches);
|
|
9944
|
-
};
|
|
9945
|
-
|
|
9946
|
-
/**
|
|
9947
|
-
* Full re-render for structural changes (when/each branch switches).
|
|
9948
|
-
* @private
|
|
9949
|
-
*/
|
|
9950
|
-
_chp._render = function() {
|
|
9951
|
-
if (!this.element || !this.element.parentNode) return;
|
|
9952
|
-
var parent = this.element.parentNode;
|
|
9953
|
-
var nextSibling = this.element.nextSibling;
|
|
9954
|
-
|
|
9955
|
-
// Remove old DOM
|
|
9956
|
-
parent.removeChild(this.element);
|
|
9957
|
-
|
|
9958
|
-
// Re-prepare TACO with current state (deep clone preserving functions)
|
|
9959
|
-
this.taco = this._deepCloneTaco(this._originalTaco || this.taco);
|
|
9960
|
-
|
|
9961
|
-
// Re-compile bindings and prepare
|
|
9962
|
-
this._compileBindings();
|
|
9963
|
-
this._prepareTaco(this.taco);
|
|
9964
|
-
this._wireActions(this.taco);
|
|
9965
|
-
|
|
9966
|
-
var tacoForDOM = this._tacoForDOM(this.taco);
|
|
9967
|
-
this.element = bw.createDOM(tacoForDOM);
|
|
9968
|
-
this.element._bwComponentHandle = this;
|
|
9969
|
-
this.element.setAttribute('data-bw_comp_id', this._bwId);
|
|
9970
|
-
|
|
9971
|
-
// Re-insert at same position
|
|
9972
|
-
if (nextSibling) {
|
|
9973
|
-
parent.insertBefore(this.element, nextSibling);
|
|
9974
|
-
} else {
|
|
9975
|
-
parent.appendChild(this.element);
|
|
9976
|
-
}
|
|
9977
|
-
|
|
9978
|
-
// Re-collect refs and apply all bindings
|
|
9979
|
-
this._collectRefs();
|
|
9980
|
-
this._resolveAndApplyAll();
|
|
9981
|
-
};
|
|
9982
|
-
|
|
9983
|
-
// ── Event & Pub/Sub Methods ──
|
|
9984
|
-
|
|
9985
|
-
/**
|
|
9986
|
-
* Add a DOM event listener on the component's root element.
|
|
9987
|
-
* @param {string} event - Event name (e.g., 'click')
|
|
9988
|
-
* @param {Function} handler - Event handler
|
|
9989
|
-
*/
|
|
9990
|
-
_chp.on = function(event, handler) {
|
|
9991
|
-
if (this.element) {
|
|
9992
|
-
this.element.addEventListener(event, handler);
|
|
9993
|
-
}
|
|
9994
|
-
this._eventListeners.push({ event: event, handler: handler });
|
|
9995
|
-
};
|
|
9996
|
-
|
|
9997
|
-
/**
|
|
9998
|
-
* Remove a DOM event listener.
|
|
9999
|
-
* @param {string} event - Event name
|
|
10000
|
-
* @param {Function} handler - Handler to remove
|
|
10001
|
-
*/
|
|
10002
|
-
_chp.off = function(event, handler) {
|
|
10003
|
-
if (this.element) {
|
|
10004
|
-
this.element.removeEventListener(event, handler);
|
|
10005
|
-
}
|
|
10006
|
-
this._eventListeners = this._eventListeners.filter(function(l) {
|
|
10007
|
-
return !(l.event === event && l.handler === handler);
|
|
10008
|
-
});
|
|
10009
|
-
};
|
|
10010
|
-
|
|
10011
|
-
/**
|
|
10012
|
-
* Subscribe to a pub/sub topic. Lifecycle-tied: auto-unsubs on destroy.
|
|
10013
|
-
* @param {string} topic - Topic name
|
|
10014
|
-
* @param {Function} handler - Handler function
|
|
10015
|
-
* @returns {Function} Unsubscribe function
|
|
10016
|
-
*/
|
|
10017
|
-
_chp.sub = function(topic, handler) {
|
|
10018
|
-
var unsub = bw.sub(topic, handler);
|
|
10019
|
-
this._subs.push(unsub);
|
|
10020
|
-
return unsub;
|
|
10021
|
-
};
|
|
10022
|
-
|
|
10023
|
-
/**
|
|
10024
|
-
* Call a named action.
|
|
10025
|
-
* @param {string} name - Action name
|
|
10026
|
-
* @param {...*} args - Arguments passed after comp
|
|
10027
|
-
*/
|
|
10028
|
-
_chp.action = function(name) {
|
|
10029
|
-
var fn = this._actions[name];
|
|
10030
|
-
if (!fn) {
|
|
10031
|
-
_cw('ComponentHandle.action: unknown action "' + name + '"');
|
|
10032
|
-
return;
|
|
10033
|
-
}
|
|
10034
|
-
var args = [this].concat(Array.prototype.slice.call(arguments, 1));
|
|
10035
|
-
return fn.apply(null, args);
|
|
10036
|
-
};
|
|
10037
|
-
|
|
10038
|
-
/**
|
|
10039
|
-
* querySelector within the component's DOM.
|
|
10040
|
-
* @param {string} sel - CSS selector
|
|
10041
|
-
* @returns {Element|null}
|
|
10042
|
-
*/
|
|
10043
|
-
_chp.select = function(sel) {
|
|
10044
|
-
return this.element ? this.element.querySelector(sel) : null;
|
|
10045
|
-
};
|
|
10046
|
-
|
|
10047
|
-
/**
|
|
10048
|
-
* querySelectorAll within the component's DOM.
|
|
10049
|
-
* @param {string} sel - CSS selector
|
|
10050
|
-
* @returns {Element[]}
|
|
10051
|
-
*/
|
|
10052
|
-
_chp.selectAll = function(sel) {
|
|
10053
|
-
if (!this.element) return [];
|
|
10054
|
-
return Array.prototype.slice.call(this.element.querySelectorAll(sel));
|
|
10055
|
-
};
|
|
10056
|
-
|
|
10057
|
-
/**
|
|
10058
|
-
* Tag this component with a user-defined ID for addressing via bw.message().
|
|
10059
|
-
* The tag is added as a CSS class on the root element (DOM IS the registry).
|
|
10060
|
-
* @param {string} tag - User-defined identifier (e.g. 'dashboard_prod_east')
|
|
10061
|
-
* @returns {ComponentHandle} this (for chaining)
|
|
10062
|
-
*/
|
|
10063
|
-
_chp.userTag = function(tag) {
|
|
10064
|
-
this._userTag = tag;
|
|
10065
|
-
if (this.element) {
|
|
10066
|
-
this.element.classList.add(tag);
|
|
10067
|
-
}
|
|
10068
|
-
return this;
|
|
10069
|
-
};
|
|
10070
|
-
|
|
10071
|
-
// Expose ComponentHandle on bw (for testing and advanced use)
|
|
10072
|
-
bw._ComponentHandle = ComponentHandle;
|
|
10073
|
-
|
|
10074
|
-
// ===================================================================================
|
|
10075
|
-
// Control Flow Helpers
|
|
10076
|
-
// ===================================================================================
|
|
10077
|
-
|
|
10078
|
-
/**
|
|
10079
|
-
* Conditional rendering helper.
|
|
10080
|
-
* Returns a marker object that ComponentHandle detects during binding compilation.
|
|
10081
|
-
* In static contexts (bw.html with state), evaluates immediately.
|
|
10082
|
-
*
|
|
10083
|
-
* @param {string} expr - Expression string like '${loggedIn}'
|
|
10084
|
-
* @param {Object} tacoTrue - TACO to render when truthy
|
|
10085
|
-
* @param {Object} [tacoFalse] - TACO to render when falsy
|
|
10086
|
-
* @returns {Object} Marker object with _bwWhen flag
|
|
10087
|
-
* @category Component
|
|
10088
|
-
*/
|
|
10089
|
-
bw.when = function(expr, tacoTrue, tacoFalse) {
|
|
10090
|
-
return { _bwWhen: true, expr: expr, branches: [tacoTrue, tacoFalse || null] };
|
|
10091
|
-
};
|
|
10092
|
-
|
|
10093
|
-
/**
|
|
10094
|
-
* List rendering helper.
|
|
10095
|
-
* Returns a marker object that ComponentHandle detects during binding compilation.
|
|
10096
|
-
*
|
|
10097
|
-
* @param {string} expr - Expression string like '${items}'
|
|
10098
|
-
* @param {Function} fn - Factory function(item, index) returning TACO
|
|
10099
|
-
* @returns {Object} Marker object with _bwEach flag
|
|
10100
|
-
* @category Component
|
|
10101
|
-
*/
|
|
10102
|
-
bw.each = function(expr, fn) {
|
|
10103
|
-
return { _bwEach: true, expr: expr, factory: fn };
|
|
10104
|
-
};
|
|
10105
|
-
|
|
10106
|
-
// ===================================================================================
|
|
10107
|
-
// bw.component() — Factory for ComponentHandle
|
|
10108
|
-
// ===================================================================================
|
|
10109
|
-
|
|
10110
|
-
/**
|
|
10111
|
-
* Create a ComponentHandle from a TACO definition.
|
|
10112
|
-
* The returned handle has .get(), .set(), .mount(), .destroy(), etc.
|
|
10113
|
-
*
|
|
10114
|
-
* @param {Object} taco - TACO definition with {t, a, c, o}
|
|
10115
|
-
* @returns {ComponentHandle} Reactive component handle
|
|
10116
|
-
* @category Component
|
|
10117
|
-
* @see bw.DOM
|
|
10118
|
-
* @example
|
|
10119
|
-
* var counter = bw.component({
|
|
10120
|
-
* t: 'div', c: [{ t: 'h3', c: 'Count: ${count}' }],
|
|
10121
|
-
* o: { state: { count: 0 } }
|
|
10122
|
-
* });
|
|
10123
|
-
* bw.DOM('#app', counter);
|
|
10124
|
-
* counter.set('count', 42); // DOM auto-updates
|
|
10125
|
-
*/
|
|
10126
|
-
bw.component = function(taco) {
|
|
10127
|
-
return new ComponentHandle(taco);
|
|
10128
|
-
};
|
|
10129
|
-
|
|
10130
|
-
// ===================================================================================
|
|
10131
|
-
// bw.message() — SendMessage() for the web
|
|
10132
|
-
// ===================================================================================
|
|
10133
|
-
|
|
10134
|
-
/**
|
|
10135
|
-
* Dispatch a message to a component by UUID or user tag.
|
|
10136
|
-
* Finds the component's DOM element, looks up its ComponentHandle,
|
|
10137
|
-
* and calls the named method. This is the bitwrench equivalent of
|
|
10138
|
-
* Win32 SendMessage(hwnd, msg, wParam, lParam).
|
|
10139
|
-
*
|
|
10140
|
-
* @param {string} target - Component UUID (bw_uuid_*), comp ID (data-bw_comp_id), or user tag (CSS class)
|
|
10141
|
-
* @param {string} action - Method name to call on the component
|
|
10142
|
-
* @param {*} data - Data to pass to the method
|
|
10143
|
-
* @returns {boolean} True if message was dispatched successfully
|
|
10144
|
-
* @category Component
|
|
10145
|
-
* @example
|
|
10146
|
-
* // Tag a component
|
|
10147
|
-
* myDash.userTag('dashboard_prod');
|
|
10148
|
-
* // Dispatch locally
|
|
10149
|
-
* bw.message('dashboard_prod', 'addAlert', { severity: 'warning', text: 'CPU spike' });
|
|
10150
|
-
* // Or from SSE handler:
|
|
10151
|
-
* es.onmessage = function(e) {
|
|
10152
|
-
* var msg = JSON.parse(e.data);
|
|
10153
|
-
* bw.message(msg.target, msg.action, msg.data);
|
|
10154
|
-
* };
|
|
10155
|
-
*/
|
|
10156
|
-
bw.message = function(target, action, data) {
|
|
10157
|
-
// Try bw._el() first (handles UUID class, nodeMap cache, getElementById)
|
|
10158
|
-
var el = bw._el(target);
|
|
10159
|
-
// Then try data-bw_comp_id attribute
|
|
10160
|
-
if (!el || !el._bwComponentHandle) {
|
|
10161
|
-
el = bw.$('[data-bw_comp_id="' + target + '"]')[0];
|
|
10162
|
-
}
|
|
10163
|
-
// Then try CSS class (user tag)
|
|
10164
|
-
if (!el || !el._bwComponentHandle) {
|
|
10165
|
-
el = bw.$('.' + target)[0];
|
|
10166
|
-
}
|
|
10167
|
-
if (!el || !el._bwComponentHandle) return false;
|
|
10168
|
-
var comp = el._bwComponentHandle;
|
|
10169
|
-
if (!_is(comp[action], 'function')) {
|
|
10170
|
-
_cw('bw.message: unknown action "' + action + '" on component ' + target);
|
|
10171
|
-
return false;
|
|
10172
|
-
}
|
|
10173
|
-
comp[action](data);
|
|
10174
|
-
return true;
|
|
10175
|
-
};
|
|
10176
|
-
|
|
10177
|
-
// ===================================================================================
|
|
10178
|
-
// bw.apply() / bw.parseJSONFlex() — Server-driven UI protocol
|
|
10179
|
-
// ===================================================================================
|
|
10180
|
-
|
|
10181
|
-
/**
|
|
10182
|
-
* Registry of named functions sent via register messages.
|
|
10183
|
-
* Populated by bw.apply({ type: 'register', name, body }).
|
|
10184
|
-
* Invoked by bw.apply({ type: 'call', name, args }).
|
|
10185
|
-
* @private
|
|
10186
|
-
*/
|
|
10187
|
-
bw._clientFunctions = {};
|
|
10188
|
-
|
|
10189
|
-
/**
|
|
10190
|
-
* Whether exec messages are allowed. Set by bwclient connect opts.allowExec.
|
|
10191
|
-
* Default false — exec messages are rejected unless explicitly opted in.
|
|
10192
|
-
* @private
|
|
10193
|
-
*/
|
|
10194
|
-
bw._allowExec = false;
|
|
10195
|
-
|
|
10196
|
-
/**
|
|
10197
|
-
* Parse a bwserve protocol message string, supporting both strict JSON
|
|
10198
|
-
* and r-prefixed relaxed JSON (single-quoted strings, trailing commas).
|
|
10199
|
-
*
|
|
10200
|
-
* The r-prefix format is designed for C/C++ string literals where
|
|
10201
|
-
* double-quote escaping is painful. The parser is a state machine
|
|
10202
|
-
* that walks character by character — not a regex replace.
|
|
10203
|
-
*
|
|
10204
|
-
* Escaping: apostrophes inside single-quoted values must be escaped
|
|
10205
|
-
* with backslash: r{'name':'Barry\'s room'}
|
|
10206
|
-
*
|
|
10207
|
-
* @param {string} str - JSON or r-prefixed relaxed JSON string
|
|
10208
|
-
* @returns {Object} Parsed message object
|
|
10209
|
-
* @throws {SyntaxError} If the string is not valid JSON or relaxed JSON
|
|
10210
|
-
* @category Core
|
|
9149
|
+
* Parse a bwserve protocol message string, supporting both strict JSON
|
|
9150
|
+
* and r-prefixed relaxed JSON (single-quoted strings, trailing commas).
|
|
9151
|
+
*
|
|
9152
|
+
* The r-prefix format is designed for C/C++ string literals where
|
|
9153
|
+
* double-quote escaping is painful. The parser is a state machine
|
|
9154
|
+
* that walks character by character — not a regex replace.
|
|
9155
|
+
*
|
|
9156
|
+
* Escaping: apostrophes inside single-quoted values must be escaped
|
|
9157
|
+
* with backslash: r{'name':'Barry\'s room'}
|
|
9158
|
+
*
|
|
9159
|
+
* @param {string} str - JSON or r-prefixed relaxed JSON string
|
|
9160
|
+
* @returns {Object} Parsed message object
|
|
9161
|
+
* @throws {SyntaxError} If the string is not valid JSON or relaxed JSON
|
|
9162
|
+
* @category Core
|
|
10211
9163
|
*/
|
|
10212
9164
|
bw.parseJSONFlex = function(str) {
|
|
10213
9165
|
str = (str || '').trim();
|
|
@@ -10396,132 +9348,29 @@
|
|
|
10396
9348
|
// ===================================================================================
|
|
10397
9349
|
|
|
10398
9350
|
/**
|
|
10399
|
-
* Inspect a
|
|
10400
|
-
* Works with DOM elements
|
|
10401
|
-
* Returns the ComponentHandle for console chaining.
|
|
9351
|
+
* Inspect a DOM element's bitwrench state, handle methods, and metadata.
|
|
9352
|
+
* Works with DOM elements or CSS selectors.
|
|
10402
9353
|
*
|
|
10403
|
-
* @param {string|Element
|
|
10404
|
-
* @returns {
|
|
9354
|
+
* @param {string|Element} target - Selector or DOM element
|
|
9355
|
+
* @returns {Element|null} The element, or null if not found
|
|
10405
9356
|
* @category Component
|
|
10406
9357
|
* @example
|
|
10407
|
-
*
|
|
9358
|
+
* bw.inspect('#my-carousel');
|
|
10408
9359
|
* bw.inspect($0);
|
|
10409
|
-
* // Or by selector:
|
|
10410
|
-
* var h = bw.inspect('#my-dashboard');
|
|
10411
|
-
* h.set('count', 99); // chain from returned handle
|
|
10412
9360
|
*/
|
|
10413
9361
|
bw.inspect = function(target) {
|
|
10414
|
-
var el = target;
|
|
10415
|
-
|
|
10416
|
-
|
|
10417
|
-
|
|
10418
|
-
|
|
10419
|
-
|
|
10420
|
-
|
|
10421
|
-
el = bw.$(target)[0];
|
|
10422
|
-
}
|
|
10423
|
-
if (!el) {
|
|
10424
|
-
_cw('bw.inspect: element not found');
|
|
10425
|
-
return null;
|
|
10426
|
-
}
|
|
10427
|
-
comp = el._bwComponentHandle;
|
|
10428
|
-
}
|
|
10429
|
-
if (!comp) {
|
|
10430
|
-
_cl('bw.inspect: no ComponentHandle on this element');
|
|
10431
|
-
_cl(' Tag:', el.tagName);
|
|
10432
|
-
_cl(' Classes:', el.className);
|
|
10433
|
-
_cl(' _bw_state:', el._bw_state || '(none)');
|
|
10434
|
-
return null;
|
|
10435
|
-
}
|
|
10436
|
-
var deps = comp._bindings.reduce(function(s, b) {
|
|
10437
|
-
return s.concat(b.deps || []);
|
|
10438
|
-
}, []).filter(function(v, i, a) { return a.indexOf(v) === i; });
|
|
10439
|
-
console.group('Component: ' + comp._bwId);
|
|
10440
|
-
_cl('State:', comp._state);
|
|
10441
|
-
_cl('Bindings:', comp._bindings.length, '(deps:', deps, ')');
|
|
10442
|
-
_cl('Methods:', _keys(comp._methods));
|
|
10443
|
-
_cl('Actions:', _keys(comp._actions));
|
|
10444
|
-
_cl('User tag:', comp._userTag || '(none)');
|
|
10445
|
-
_cl('Mounted:', comp.mounted);
|
|
10446
|
-
_cl('Element:', comp.element);
|
|
9362
|
+
var el = _is(target, 'string') ? bw.$(target)[0] : target;
|
|
9363
|
+
if (!el) { _cw('bw.inspect: element not found'); return null; }
|
|
9364
|
+
console.group('Element: ' + (bw.getUUID(el) || el.id || el.tagName));
|
|
9365
|
+
_cl('State:', el._bw_state || '(none)');
|
|
9366
|
+
_cl('Handle:', el.bw ? _keys(el.bw) : '(none)');
|
|
9367
|
+
_cl('Classes:', el.className);
|
|
9368
|
+
_cl('Refs:', el._bw_refs || '(none)');
|
|
10447
9369
|
console.groupEnd();
|
|
10448
|
-
return
|
|
9370
|
+
return el;
|
|
10449
9371
|
};
|
|
10450
9372
|
|
|
10451
|
-
|
|
10452
|
-
// bw.compile() — Pre-compile TACO into optimized factory
|
|
10453
|
-
// ===================================================================================
|
|
10454
|
-
|
|
10455
|
-
/**
|
|
10456
|
-
* Pre-compile a TACO definition into a factory function.
|
|
10457
|
-
* The factory produces ComponentHandles with pre-compiled binding evaluators.
|
|
10458
|
-
*
|
|
10459
|
-
* Phase 1: validates API surface. Template cloning optimization deferred.
|
|
10460
|
-
*
|
|
10461
|
-
* @param {Object} taco - TACO definition
|
|
10462
|
-
* @returns {Function} Factory function(initialState?) → ComponentHandle
|
|
10463
|
-
* @category Component
|
|
10464
|
-
*/
|
|
10465
|
-
bw.compile = function(taco) {
|
|
10466
|
-
// Pre-extract all binding expressions
|
|
10467
|
-
var precompiled = [];
|
|
10468
|
-
function walkExpressions(node) {
|
|
10469
|
-
if (!_is(node, 'object')) return;
|
|
10470
|
-
if (_is(node.c, 'string') && node.c.indexOf('${') >= 0) {
|
|
10471
|
-
var parsed = bw._parseBindings(node.c);
|
|
10472
|
-
for (var i = 0; i < parsed.length; i++) {
|
|
10473
|
-
try {
|
|
10474
|
-
precompiled.push({
|
|
10475
|
-
expr: parsed[i].expr,
|
|
10476
|
-
fn: new Function('state', 'with(state){return (' + parsed[i].expr + ');}')
|
|
10477
|
-
});
|
|
10478
|
-
} catch(e) {
|
|
10479
|
-
precompiled.push({ expr: parsed[i].expr, fn: function() { return ''; } });
|
|
10480
|
-
}
|
|
10481
|
-
}
|
|
10482
|
-
}
|
|
10483
|
-
if (node.a) {
|
|
10484
|
-
for (var key in node.a) {
|
|
10485
|
-
if (_hop.call(node.a, key)) {
|
|
10486
|
-
var v = node.a[key];
|
|
10487
|
-
if (_is(v, 'string') && v.indexOf('${') >= 0) {
|
|
10488
|
-
var parsed2 = bw._parseBindings(v);
|
|
10489
|
-
for (var j = 0; j < parsed2.length; j++) {
|
|
10490
|
-
try {
|
|
10491
|
-
precompiled.push({
|
|
10492
|
-
expr: parsed2[j].expr,
|
|
10493
|
-
fn: new Function('state', 'with(state){return (' + parsed2[j].expr + ');}')
|
|
10494
|
-
});
|
|
10495
|
-
} catch(e2) {
|
|
10496
|
-
precompiled.push({ expr: parsed2[j].expr, fn: function() { return ''; } });
|
|
10497
|
-
}
|
|
10498
|
-
}
|
|
10499
|
-
}
|
|
10500
|
-
}
|
|
10501
|
-
}
|
|
10502
|
-
}
|
|
10503
|
-
if (_isA(node.c)) {
|
|
10504
|
-
for (var k = 0; k < node.c.length; k++) walkExpressions(node.c[k]);
|
|
10505
|
-
} else if (_is(node.c, 'object') && node.c.t) {
|
|
10506
|
-
walkExpressions(node.c);
|
|
10507
|
-
}
|
|
10508
|
-
}
|
|
10509
|
-
walkExpressions(taco);
|
|
10510
|
-
|
|
10511
|
-
return function(initialState) {
|
|
10512
|
-
var handle = new ComponentHandle(taco);
|
|
10513
|
-
handle._compile = true;
|
|
10514
|
-
handle._precompiledBindings = precompiled;
|
|
10515
|
-
if (initialState) {
|
|
10516
|
-
for (var k in initialState) {
|
|
10517
|
-
if (_hop.call(initialState, k)) {
|
|
10518
|
-
handle._state[k] = initialState[k];
|
|
10519
|
-
}
|
|
10520
|
-
}
|
|
10521
|
-
}
|
|
10522
|
-
return handle;
|
|
10523
|
-
};
|
|
10524
|
-
};
|
|
9373
|
+
bw.compile = function() { throw new Error('bw.compile() removed in v2.0.19. Use o.handle/o.slots on TACO options instead.'); };
|
|
10525
9374
|
|
|
10526
9375
|
/**
|
|
10527
9376
|
* Generate CSS from JavaScript objects.
|
|
@@ -11747,8 +10596,8 @@
|
|
|
11747
10596
|
};
|
|
11748
10597
|
}
|
|
11749
10598
|
|
|
11750
|
-
// Generate unique
|
|
11751
|
-
const componentId = taco.o?.id || bw.uuid();
|
|
10599
|
+
// Generate unique UUID class if not provided
|
|
10600
|
+
const componentId = taco.o?.id || bw.uuid('uuid');
|
|
11752
10601
|
|
|
11753
10602
|
// Create DOM element
|
|
11754
10603
|
let domElement;
|
|
@@ -11763,9 +10612,10 @@
|
|
|
11763
10612
|
};
|
|
11764
10613
|
}
|
|
11765
10614
|
|
|
11766
|
-
// Add component ID
|
|
11767
|
-
domElement.
|
|
11768
|
-
|
|
10615
|
+
// Add component ID as class + lifecycle marker
|
|
10616
|
+
domElement.classList.add(componentId);
|
|
10617
|
+
domElement.classList.add(_BW_LC);
|
|
10618
|
+
|
|
11769
10619
|
// Insert into DOM based on position
|
|
11770
10620
|
try {
|
|
11771
10621
|
switch(position) {
|
|
@@ -11839,7 +10689,8 @@
|
|
|
11839
10689
|
|
|
11840
10690
|
// Re-render
|
|
11841
10691
|
const newElement = bw.createDOM(this._taco);
|
|
11842
|
-
newElement.
|
|
10692
|
+
newElement.classList.add(componentId);
|
|
10693
|
+
newElement.classList.add(_BW_LC);
|
|
11843
10694
|
|
|
11844
10695
|
// Replace in DOM
|
|
11845
10696
|
parent.replaceChild(newElement, this.element);
|
|
@@ -12026,13 +10877,12 @@
|
|
|
12026
10877
|
// Variant class helper: bw.variantClass('primary') → 'bw_primary'
|
|
12027
10878
|
bw.variantClass = variantClass;
|
|
12028
10879
|
|
|
12029
|
-
// Create functions that return
|
|
10880
|
+
// Create functions that return DOM elements (createCard, createTable, etc.)
|
|
12030
10881
|
Object.entries(components).forEach(([name, fn]) => {
|
|
12031
10882
|
if (name.startsWith('make')) {
|
|
12032
|
-
const createName = 'create' + name.substring(4);
|
|
10883
|
+
const createName = 'create' + name.substring(4);
|
|
12033
10884
|
bw[createName] = function(props) {
|
|
12034
|
-
|
|
12035
|
-
return bw.renderComponent(taco);
|
|
10885
|
+
return bw.createDOM(fn(props));
|
|
12036
10886
|
};
|
|
12037
10887
|
}
|
|
12038
10888
|
});
|