bitwrench 2.0.8 → 2.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bitwrench-code-edit.cjs.js +1 -1
- package/dist/bitwrench-code-edit.es5.js +1 -1
- package/dist/bitwrench-code-edit.es5.min.js +3 -3
- package/dist/bitwrench-code-edit.esm.js +1 -1
- package/dist/bitwrench-code-edit.esm.min.js +2 -2
- package/dist/bitwrench-code-edit.umd.js +1 -1
- package/dist/bitwrench-code-edit.umd.min.js +3 -3
- package/dist/bitwrench.cjs.js +202 -22
- package/dist/bitwrench.cjs.min.js +6 -6
- package/dist/bitwrench.es5.js +227 -47
- package/dist/bitwrench.es5.min.js +4 -4
- package/dist/bitwrench.esm.js +202 -22
- package/dist/bitwrench.esm.min.js +6 -6
- package/dist/bitwrench.umd.js +202 -22
- package/dist/bitwrench.umd.min.js +6 -6
- package/dist/builds.json +87 -32
- package/dist/sri.json +17 -12
- package/package.json +11 -3
- package/readme.html +98 -121
- package/src/bitwrench.js +199 -19
- package/src/version.js +3 -3
package/dist/bitwrench.esm.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
/*! bitwrench v2.0.
|
|
1
|
+
/*! bitwrench v2.0.10 | BSD-2-Clause | http://deftio.com/bitwrench */
|
|
2
2
|
/**
|
|
3
3
|
* Auto-generated version file from package.json
|
|
4
4
|
* DO NOT EDIT DIRECTLY - Use npm run generate-version
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const VERSION_INFO = {
|
|
8
|
-
version: '2.0.
|
|
8
|
+
version: '2.0.10',
|
|
9
9
|
name: 'bitwrench',
|
|
10
10
|
description: 'A library for javascript UI functions.',
|
|
11
11
|
license: 'BSD-2-Clause',
|
|
12
12
|
homepage: 'http://deftio.com/bitwrench',
|
|
13
13
|
repository: 'git+https://github.com/deftio/bitwrench.git',
|
|
14
14
|
author: 'manu a. chatterjee <deftio@deftio.com> (https://deftio.com/)',
|
|
15
|
-
buildDate: '2026-03-
|
|
15
|
+
buildDate: '2026-03-07T03:14:16.606Z'
|
|
16
16
|
};
|
|
17
17
|
|
|
18
18
|
/**
|
|
@@ -3663,6 +3663,27 @@ const bw = {
|
|
|
3663
3663
|
_unmountCallbacks: new Map(),
|
|
3664
3664
|
_topics: {}, // topic → [{handler, id}] (plain object for IE11 compat)
|
|
3665
3665
|
_subIdCounter: 0, // monotonic ID for subscriptions
|
|
3666
|
+
|
|
3667
|
+
// ── Node reference cache ──────────────────────────────────────────────
|
|
3668
|
+
// Fast O(1) lookup for elements by bw_id, id attribute, or bw_uuid.
|
|
3669
|
+
//
|
|
3670
|
+
// Populated by bw.createDOM() when elements have:
|
|
3671
|
+
// - data-bw-id attribute (user-declared addressable elements)
|
|
3672
|
+
// - id attribute (standard HTML id)
|
|
3673
|
+
// - bw_uuid (internal, for lifecycle-managed elements)
|
|
3674
|
+
//
|
|
3675
|
+
// Cleaned up by bw.cleanup() when elements are destroyed via bitwrench APIs.
|
|
3676
|
+
// On cache miss, falls back to querySelector/getElementById — never fails,
|
|
3677
|
+
// just slower. Stale entries (refs to detached nodes) are removed on miss
|
|
3678
|
+
// via parentNode === null check (IE11-safe, unlike el.isConnected).
|
|
3679
|
+
//
|
|
3680
|
+
// Elements created via bw.createDOM() also get el._bw_refs — a local map of
|
|
3681
|
+
// child bw_id → DOM node ref for fast parent→child access in o.render.
|
|
3682
|
+
// This is the bitwrench equivalent of React's compiled template "holes".
|
|
3683
|
+
//
|
|
3684
|
+
// Contract: if you remove elements outside of bitwrench APIs (raw el.remove()),
|
|
3685
|
+
// map entries may linger until the next lookup attempt cleans them.
|
|
3686
|
+
_nodeMap: {},
|
|
3666
3687
|
|
|
3667
3688
|
// Monkey patch for testing (same as v1)
|
|
3668
3689
|
__monkey_patch_is_nodejs__: {
|
|
@@ -3893,6 +3914,108 @@ bw.uuid = function(prefix) {
|
|
|
3893
3914
|
return `${tag}${timestamp}_${counter}_${random}`;
|
|
3894
3915
|
};
|
|
3895
3916
|
|
|
3917
|
+
/**
|
|
3918
|
+
* Look up a DOM element by ID string, using the node cache for O(1) access.
|
|
3919
|
+
*
|
|
3920
|
+
* Resolution order:
|
|
3921
|
+
* 1. Check `bw._nodeMap[id]` — if found and still attached (parentNode !== null), return it
|
|
3922
|
+
* 2. If cached ref is detached (parentNode === null), remove stale entry
|
|
3923
|
+
* 3. Fall back to `document.getElementById(id)` then `document.querySelector(...)`
|
|
3924
|
+
* 4. If fallback finds the element, cache it for next time
|
|
3925
|
+
* 5. If not found anywhere, return null
|
|
3926
|
+
*
|
|
3927
|
+
* Accepts a DOM element directly (pass-through) or a string identifier.
|
|
3928
|
+
* String identifiers are tried as: direct map key, getElementById,
|
|
3929
|
+
* querySelector (for CSS selectors starting with . or #), and
|
|
3930
|
+
* data-bw-id attribute selector.
|
|
3931
|
+
*
|
|
3932
|
+
* @param {string|Element} id - Element ID, CSS selector, data-bw-id value, or DOM element
|
|
3933
|
+
* @returns {Element|null} The DOM element, or null if not found
|
|
3934
|
+
* @category Internal
|
|
3935
|
+
*/
|
|
3936
|
+
bw._el = function(id) {
|
|
3937
|
+
// Pass-through for DOM elements
|
|
3938
|
+
if (typeof id !== 'string') return id || null;
|
|
3939
|
+
if (!id) return null;
|
|
3940
|
+
if (!bw._isBrowser) return null;
|
|
3941
|
+
|
|
3942
|
+
// 1. Check cache
|
|
3943
|
+
var cached = bw._nodeMap[id];
|
|
3944
|
+
if (cached) {
|
|
3945
|
+
// Verify not detached (parentNode check is IE11-safe)
|
|
3946
|
+
if (cached.parentNode !== null) {
|
|
3947
|
+
return cached;
|
|
3948
|
+
}
|
|
3949
|
+
// Stale — remove and fall through
|
|
3950
|
+
delete bw._nodeMap[id];
|
|
3951
|
+
}
|
|
3952
|
+
|
|
3953
|
+
// 2. DOM fallback: try getElementById first (fastest native lookup)
|
|
3954
|
+
var el = document.getElementById(id);
|
|
3955
|
+
|
|
3956
|
+
// 3. Try querySelector for CSS selectors (starts with # or .)
|
|
3957
|
+
if (!el && (id.charAt(0) === '#' || id.charAt(0) === '.')) {
|
|
3958
|
+
el = document.querySelector(id);
|
|
3959
|
+
}
|
|
3960
|
+
|
|
3961
|
+
// 4. Try data-bw-id attribute (for bw.uuid-generated IDs)
|
|
3962
|
+
if (!el) {
|
|
3963
|
+
el = document.querySelector('[data-bw-id="' + id + '"]');
|
|
3964
|
+
}
|
|
3965
|
+
|
|
3966
|
+
// 5. Cache the result for next time
|
|
3967
|
+
if (el) {
|
|
3968
|
+
bw._nodeMap[id] = el;
|
|
3969
|
+
}
|
|
3970
|
+
|
|
3971
|
+
return el;
|
|
3972
|
+
};
|
|
3973
|
+
|
|
3974
|
+
/**
|
|
3975
|
+
* Register a DOM element in the node cache under one or more keys.
|
|
3976
|
+
*
|
|
3977
|
+
* Called internally by `bw.createDOM()`. Registers elements that have
|
|
3978
|
+
* id attributes, data-bw-id attributes, or both.
|
|
3979
|
+
*
|
|
3980
|
+
* @param {Element} el - DOM element to register
|
|
3981
|
+
* @param {string} [bwId] - data-bw-id value to register under
|
|
3982
|
+
* @category Internal
|
|
3983
|
+
*/
|
|
3984
|
+
bw._registerNode = function(el, bwId) {
|
|
3985
|
+
if (!el) return;
|
|
3986
|
+
// Register under data-bw-id
|
|
3987
|
+
if (bwId) {
|
|
3988
|
+
bw._nodeMap[bwId] = el;
|
|
3989
|
+
}
|
|
3990
|
+
// Register under id attribute
|
|
3991
|
+
var htmlId = el.getAttribute ? el.getAttribute('id') : null;
|
|
3992
|
+
if (htmlId) {
|
|
3993
|
+
bw._nodeMap[htmlId] = el;
|
|
3994
|
+
}
|
|
3995
|
+
};
|
|
3996
|
+
|
|
3997
|
+
/**
|
|
3998
|
+
* Remove a DOM element from the node cache.
|
|
3999
|
+
*
|
|
4000
|
+
* Called internally by `bw.cleanup()` when elements are destroyed
|
|
4001
|
+
* through bitwrench APIs.
|
|
4002
|
+
*
|
|
4003
|
+
* @param {Element} el - DOM element to deregister
|
|
4004
|
+
* @param {string} [bwId] - data-bw-id value to remove
|
|
4005
|
+
* @category Internal
|
|
4006
|
+
*/
|
|
4007
|
+
bw._deregisterNode = function(el, bwId) {
|
|
4008
|
+
// Remove data-bw-id entry
|
|
4009
|
+
if (bwId) {
|
|
4010
|
+
delete bw._nodeMap[bwId];
|
|
4011
|
+
}
|
|
4012
|
+
// Remove id attribute entry
|
|
4013
|
+
var htmlId = el && el.getAttribute ? el.getAttribute('id') : null;
|
|
4014
|
+
if (htmlId) {
|
|
4015
|
+
delete bw._nodeMap[htmlId];
|
|
4016
|
+
}
|
|
4017
|
+
};
|
|
4018
|
+
|
|
3896
4019
|
/**
|
|
3897
4020
|
* Escape HTML special characters to prevent XSS.
|
|
3898
4021
|
*
|
|
@@ -4117,26 +4240,66 @@ bw.createDOM = function(taco, options = {}) {
|
|
|
4117
4240
|
}
|
|
4118
4241
|
}
|
|
4119
4242
|
|
|
4120
|
-
// Add children
|
|
4243
|
+
// Add children, building _bw_refs for fast parent→child access.
|
|
4244
|
+
// Children with data-bw-id or id attributes get local refs on the parent,
|
|
4245
|
+
// so o.render functions can access them without any DOM lookup.
|
|
4121
4246
|
if (content != null) {
|
|
4122
4247
|
if (Array.isArray(content)) {
|
|
4123
4248
|
content.forEach(child => {
|
|
4124
4249
|
if (child != null) {
|
|
4125
|
-
|
|
4250
|
+
var childEl = bw.createDOM(child, options);
|
|
4251
|
+
el.appendChild(childEl);
|
|
4252
|
+
// Build local refs for addressable children
|
|
4253
|
+
var childBwId = (child && child.a) ? (child.a['data-bw-id'] || child.a.id) : null;
|
|
4254
|
+
if (childBwId) {
|
|
4255
|
+
if (!el._bw_refs) el._bw_refs = {};
|
|
4256
|
+
el._bw_refs[childBwId] = childEl;
|
|
4257
|
+
}
|
|
4258
|
+
// Bubble up grandchild refs (flatten one level)
|
|
4259
|
+
if (childEl._bw_refs) {
|
|
4260
|
+
if (!el._bw_refs) el._bw_refs = {};
|
|
4261
|
+
for (var rk in childEl._bw_refs) {
|
|
4262
|
+
if (Object.prototype.hasOwnProperty.call(childEl._bw_refs, rk)) {
|
|
4263
|
+
el._bw_refs[rk] = childEl._bw_refs[rk];
|
|
4264
|
+
}
|
|
4265
|
+
}
|
|
4266
|
+
}
|
|
4126
4267
|
}
|
|
4127
4268
|
});
|
|
4128
4269
|
} else if (typeof content === 'object' && content.t) {
|
|
4129
|
-
|
|
4270
|
+
var childEl = bw.createDOM(content, options);
|
|
4271
|
+
el.appendChild(childEl);
|
|
4272
|
+
var childBwId = content.a ? (content.a['data-bw-id'] || content.a.id) : null;
|
|
4273
|
+
if (childBwId) {
|
|
4274
|
+
if (!el._bw_refs) el._bw_refs = {};
|
|
4275
|
+
el._bw_refs[childBwId] = childEl;
|
|
4276
|
+
}
|
|
4277
|
+
if (childEl._bw_refs) {
|
|
4278
|
+
if (!el._bw_refs) el._bw_refs = {};
|
|
4279
|
+
for (var rk in childEl._bw_refs) {
|
|
4280
|
+
if (Object.prototype.hasOwnProperty.call(childEl._bw_refs, rk)) {
|
|
4281
|
+
el._bw_refs[rk] = childEl._bw_refs[rk];
|
|
4282
|
+
}
|
|
4283
|
+
}
|
|
4284
|
+
}
|
|
4130
4285
|
} else {
|
|
4131
4286
|
el.textContent = String(content);
|
|
4132
4287
|
}
|
|
4133
4288
|
}
|
|
4134
|
-
|
|
4289
|
+
|
|
4290
|
+
// Register element in node cache if it has an id attribute
|
|
4291
|
+
if (attrs.id) {
|
|
4292
|
+
bw._registerNode(el, null);
|
|
4293
|
+
}
|
|
4294
|
+
|
|
4135
4295
|
// Handle lifecycle hooks and state
|
|
4136
4296
|
if (opts.mounted || opts.unmount || opts.render || opts.state) {
|
|
4137
4297
|
const id = attrs['data-bw-id'] || bw.uuid();
|
|
4138
4298
|
el.setAttribute('data-bw-id', id);
|
|
4139
4299
|
|
|
4300
|
+
// Register in node cache under data-bw-id
|
|
4301
|
+
bw._registerNode(el, id);
|
|
4302
|
+
|
|
4140
4303
|
// Store state
|
|
4141
4304
|
if (opts.state) {
|
|
4142
4305
|
el._bw_state = opts.state;
|
|
@@ -4179,8 +4342,11 @@ bw.createDOM = function(taco, options = {}) {
|
|
|
4179
4342
|
opts.unmount(el, el._bw_state || {});
|
|
4180
4343
|
});
|
|
4181
4344
|
}
|
|
4345
|
+
} else if (attrs['data-bw-id']) {
|
|
4346
|
+
// Element has explicit data-bw-id but no lifecycle hooks — still register it
|
|
4347
|
+
bw._registerNode(el, attrs['data-bw-id']);
|
|
4182
4348
|
}
|
|
4183
|
-
|
|
4349
|
+
|
|
4184
4350
|
return el;
|
|
4185
4351
|
};
|
|
4186
4352
|
|
|
@@ -4213,10 +4379,8 @@ bw.DOM = function(target, taco, options = {}) {
|
|
|
4213
4379
|
throw new Error('bw.DOM requires a DOM environment (document/window). Use bw.html() instead.');
|
|
4214
4380
|
}
|
|
4215
4381
|
|
|
4216
|
-
// Get target element
|
|
4217
|
-
const targetEl =
|
|
4218
|
-
? document.querySelector(target)
|
|
4219
|
-
: target;
|
|
4382
|
+
// Get target element (use cache-backed lookup)
|
|
4383
|
+
const targetEl = bw._el(target);
|
|
4220
4384
|
|
|
4221
4385
|
if (!targetEl) {
|
|
4222
4386
|
console.error('bw.DOM: Target element not found:', target);
|
|
@@ -4239,7 +4403,11 @@ bw.DOM = function(target, taco, options = {}) {
|
|
|
4239
4403
|
// Restore the target's own state/render/subs after cleanup
|
|
4240
4404
|
if (savedState !== undefined) targetEl._bw_state = savedState;
|
|
4241
4405
|
if (savedRender) targetEl._bw_render = savedRender;
|
|
4242
|
-
if (savedBwId)
|
|
4406
|
+
if (savedBwId) {
|
|
4407
|
+
targetEl.setAttribute('data-bw-id', savedBwId);
|
|
4408
|
+
// Re-register mount point in node cache (cleanup deregistered it)
|
|
4409
|
+
bw._registerNode(targetEl, savedBwId);
|
|
4410
|
+
}
|
|
4243
4411
|
if (savedSubs) targetEl._bw_subs = savedSubs;
|
|
4244
4412
|
|
|
4245
4413
|
// Clear and mount new content
|
|
@@ -4502,15 +4670,19 @@ bw.cleanup = function(element) {
|
|
|
4502
4670
|
bw._unmountCallbacks.delete(id);
|
|
4503
4671
|
}
|
|
4504
4672
|
|
|
4673
|
+
// Deregister from node cache
|
|
4674
|
+
bw._deregisterNode(el, id);
|
|
4675
|
+
|
|
4505
4676
|
// Clean up pub/sub subscriptions tied to this element
|
|
4506
4677
|
if (el._bw_subs) {
|
|
4507
4678
|
el._bw_subs.forEach(function(unsub) { unsub(); });
|
|
4508
4679
|
delete el._bw_subs;
|
|
4509
4680
|
}
|
|
4510
4681
|
|
|
4511
|
-
// Clean up state and
|
|
4682
|
+
// Clean up state, render, and local refs
|
|
4512
4683
|
delete el._bw_state;
|
|
4513
4684
|
delete el._bw_render;
|
|
4685
|
+
delete el._bw_refs;
|
|
4514
4686
|
});
|
|
4515
4687
|
|
|
4516
4688
|
// Check element itself
|
|
@@ -4521,6 +4693,10 @@ bw.cleanup = function(element) {
|
|
|
4521
4693
|
callback();
|
|
4522
4694
|
bw._unmountCallbacks.delete(id);
|
|
4523
4695
|
}
|
|
4696
|
+
|
|
4697
|
+
// Deregister from node cache
|
|
4698
|
+
bw._deregisterNode(element, id);
|
|
4699
|
+
|
|
4524
4700
|
// Clean up pub/sub subscriptions tied to element itself
|
|
4525
4701
|
if (element._bw_subs) {
|
|
4526
4702
|
element._bw_subs.forEach(function(unsub) { unsub(); });
|
|
@@ -4528,6 +4704,7 @@ bw.cleanup = function(element) {
|
|
|
4528
4704
|
}
|
|
4529
4705
|
delete element._bw_state;
|
|
4530
4706
|
delete element._bw_render;
|
|
4707
|
+
delete element._bw_refs;
|
|
4531
4708
|
}
|
|
4532
4709
|
};
|
|
4533
4710
|
|
|
@@ -4542,7 +4719,7 @@ bw.cleanup = function(element) {
|
|
|
4542
4719
|
* Calls `el._bw_render(el, state)` and emits `bw:statechange` so other
|
|
4543
4720
|
* components can react without tight coupling.
|
|
4544
4721
|
*
|
|
4545
|
-
* @param {string|Element} target - CSS selector or DOM element
|
|
4722
|
+
* @param {string|Element} target - Element ID, data-bw-id, CSS selector, or DOM element
|
|
4546
4723
|
* @returns {Element|null} The element, or null if not found / no render function
|
|
4547
4724
|
* @category State Management
|
|
4548
4725
|
* @see bw.patch
|
|
@@ -4552,7 +4729,7 @@ bw.cleanup = function(element) {
|
|
|
4552
4729
|
* bw.update(el); // re-renders, emits bw:statechange
|
|
4553
4730
|
*/
|
|
4554
4731
|
bw.update = function(target) {
|
|
4555
|
-
var el =
|
|
4732
|
+
var el = bw._el(target);
|
|
4556
4733
|
if (el && el._bw_render) {
|
|
4557
4734
|
el._bw_render(el, el._bw_state || {});
|
|
4558
4735
|
bw.emit(el, 'statechange', el._bw_state);
|
|
@@ -4567,7 +4744,8 @@ bw.update = function(target) {
|
|
|
4567
4744
|
* Use `bw.patch()` for lightweight value updates (scores, labels, counters)
|
|
4568
4745
|
* and `bw.update()` for full structural re-renders.
|
|
4569
4746
|
*
|
|
4570
|
-
* @param {string|Element} id - Element ID
|
|
4747
|
+
* @param {string|Element} id - Element ID, data-bw-id, CSS selector, or DOM element.
|
|
4748
|
+
* Uses node cache for O(1) lookup; falls back to DOM query on cache miss.
|
|
4571
4749
|
* @param {string|Object} content - New text content, or TACO object to replace children
|
|
4572
4750
|
* @param {string} [attr] - If provided, sets this attribute instead of content
|
|
4573
4751
|
* @returns {Element|null} The patched element, or null if not found
|
|
@@ -4580,7 +4758,7 @@ bw.update = function(target) {
|
|
|
4580
4758
|
* bw.patch('info', { t: 'em', c: 'new' }); // replace children with TACO
|
|
4581
4759
|
*/
|
|
4582
4760
|
bw.patch = function(id, content, attr) {
|
|
4583
|
-
var el =
|
|
4761
|
+
var el = bw._el(id);
|
|
4584
4762
|
if (!el) return null;
|
|
4585
4763
|
|
|
4586
4764
|
if (attr) {
|
|
@@ -4631,7 +4809,8 @@ bw.patchAll = function(patches) {
|
|
|
4631
4809
|
* bubble by default so ancestor elements can listen. Use with `bw.on()` for
|
|
4632
4810
|
* DOM-scoped communication between components.
|
|
4633
4811
|
*
|
|
4634
|
-
* @param {string|Element} target - CSS selector or DOM element
|
|
4812
|
+
* @param {string|Element} target - Element ID, data-bw-id, CSS selector, or DOM element.
|
|
4813
|
+
* Uses node cache for O(1) lookup; falls back to DOM query on cache miss.
|
|
4635
4814
|
* @param {string} eventName - Event name (will be prefixed with 'bw:')
|
|
4636
4815
|
* @param {*} [detail] - Data to pass with the event
|
|
4637
4816
|
* @category Events (DOM)
|
|
@@ -4641,7 +4820,7 @@ bw.patchAll = function(patches) {
|
|
|
4641
4820
|
* // Dispatches CustomEvent 'bw:statechange' on the element
|
|
4642
4821
|
*/
|
|
4643
4822
|
bw.emit = function(target, eventName, detail) {
|
|
4644
|
-
var el =
|
|
4823
|
+
var el = bw._el(target);
|
|
4645
4824
|
if (el) {
|
|
4646
4825
|
el.dispatchEvent(new CustomEvent('bw:' + eventName, {
|
|
4647
4826
|
bubbles: true,
|
|
@@ -4657,7 +4836,8 @@ bw.emit = function(target, eventName, detail) {
|
|
|
4657
4836
|
* is the first argument so you don't need to destructure `e.detail`.
|
|
4658
4837
|
* Events bubble, so you can listen on an ancestor element.
|
|
4659
4838
|
*
|
|
4660
|
-
* @param {string|Element} target - CSS selector or DOM element
|
|
4839
|
+
* @param {string|Element} target - Element ID, data-bw-id, CSS selector, or DOM element.
|
|
4840
|
+
* Uses node cache for O(1) lookup; falls back to DOM query on cache miss.
|
|
4661
4841
|
* @param {string} eventName - Event name (will be prefixed with 'bw:')
|
|
4662
4842
|
* @param {Function} handler - Called with (detail, event)
|
|
4663
4843
|
* @returns {Element|null} The element (for chaining), or null if not found
|
|
@@ -4669,7 +4849,7 @@ bw.emit = function(target, eventName, detail) {
|
|
|
4669
4849
|
* });
|
|
4670
4850
|
*/
|
|
4671
4851
|
bw.on = function(target, eventName, handler) {
|
|
4672
|
-
var el =
|
|
4852
|
+
var el = bw._el(target);
|
|
4673
4853
|
if (el) {
|
|
4674
4854
|
el.addEventListener('bw:' + eventName, function(e) {
|
|
4675
4855
|
handler(e.detail, e);
|