bitwrench 2.0.20 → 2.0.22

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.
Files changed (53) hide show
  1. package/README.md +0 -4
  2. package/dist/bitwrench-bccl.cjs.js +1 -1
  3. package/dist/bitwrench-bccl.cjs.min.js +1 -1
  4. package/dist/bitwrench-bccl.esm.js +1 -1
  5. package/dist/bitwrench-bccl.esm.min.js +1 -1
  6. package/dist/bitwrench-bccl.umd.js +1 -1
  7. package/dist/bitwrench-bccl.umd.min.js +1 -1
  8. package/dist/bitwrench-code-edit.cjs.js +1 -1
  9. package/dist/bitwrench-code-edit.cjs.min.js +1 -1
  10. package/dist/bitwrench-code-edit.es5.js +1 -1
  11. package/dist/bitwrench-code-edit.es5.min.js +1 -1
  12. package/dist/bitwrench-code-edit.esm.js +1 -1
  13. package/dist/bitwrench-code-edit.esm.min.js +1 -1
  14. package/dist/bitwrench-code-edit.umd.js +1 -1
  15. package/dist/bitwrench-code-edit.umd.min.js +1 -1
  16. package/dist/bitwrench-debug.js +1 -1
  17. package/dist/bitwrench-debug.min.js +1 -1
  18. package/dist/bitwrench-lean.cjs.js +344 -30
  19. package/dist/bitwrench-lean.cjs.min.js +14 -6
  20. package/dist/bitwrench-lean.es5.js +379 -29
  21. package/dist/bitwrench-lean.es5.min.js +12 -4
  22. package/dist/bitwrench-lean.esm.js +344 -30
  23. package/dist/bitwrench-lean.esm.min.js +14 -6
  24. package/dist/bitwrench-lean.umd.js +344 -30
  25. package/dist/bitwrench-lean.umd.min.js +14 -6
  26. package/dist/bitwrench-util-css.cjs.js +1 -1
  27. package/dist/bitwrench-util-css.cjs.min.js +1 -1
  28. package/dist/bitwrench-util-css.es5.js +1 -1
  29. package/dist/bitwrench-util-css.es5.min.js +1 -1
  30. package/dist/bitwrench-util-css.esm.js +1 -1
  31. package/dist/bitwrench-util-css.esm.min.js +1 -1
  32. package/dist/bitwrench-util-css.umd.js +1 -1
  33. package/dist/bitwrench-util-css.umd.min.js +1 -1
  34. package/dist/bitwrench.cjs.js +344 -30
  35. package/dist/bitwrench.cjs.min.js +14 -6
  36. package/dist/bitwrench.css +65 -14
  37. package/dist/bitwrench.es5.js +379 -29
  38. package/dist/bitwrench.es5.min.js +13 -5
  39. package/dist/bitwrench.esm.js +344 -30
  40. package/dist/bitwrench.esm.min.js +15 -7
  41. package/dist/bitwrench.min.css +1 -1
  42. package/dist/bitwrench.umd.js +344 -30
  43. package/dist/bitwrench.umd.min.js +14 -6
  44. package/dist/builds.json +87 -87
  45. package/dist/bwserve.cjs.js +2 -2
  46. package/dist/bwserve.esm.js +2 -2
  47. package/dist/sri.json +46 -46
  48. package/package.json +5 -6
  49. package/readme.html +3 -3
  50. package/src/bitwrench-router.js +282 -0
  51. package/src/bitwrench-styles.js +59 -27
  52. package/src/bitwrench.js +6 -0
  53. package/src/version.js +3 -3
@@ -1,4 +1,4 @@
1
- /*! bitwrench v2.0.20 | BSD-2-Clause | https://deftio.github.com/bitwrench/pages */
1
+ /*! bitwrench v2.0.22 | BSD-2-Clause | https://deftio.github.com/bitwrench/pages */
2
2
  (function (global, factory) {
3
3
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
4
4
  typeof define === 'function' && define.amd ? define(factory) :
@@ -190,14 +190,14 @@
190
190
  */
191
191
 
192
192
  var VERSION_INFO = {
193
- version: '2.0.20',
193
+ version: '2.0.22',
194
194
  name: 'bitwrench',
195
195
  description: 'A library for javascript UI functions.',
196
196
  license: 'BSD-2-Clause',
197
197
  homepage: 'https://deftio.github.com/bitwrench/pages',
198
198
  repository: 'git+https://github.com/deftio/bitwrench.git',
199
199
  author: 'manu a. chatterjee <deftio@deftio.com> (https://deftio.com/)',
200
- buildDate: '2026-03-23T05:19:31.951Z'
200
+ buildDate: '2026-03-24T05:38:12.852Z'
201
201
  };
202
202
 
203
203
  /**
@@ -777,10 +777,10 @@
777
777
  xl: '0 4px 12px rgba(0,0,0,0.12)'
778
778
  },
779
779
  md: {
780
- sm: '0 1px 3px rgba(0,0,0,0.08)',
781
- md: '0 2px 6px rgba(0,0,0,0.12)',
782
- lg: '0 4px 12px rgba(0,0,0,0.16)',
783
- xl: '0 8px 24px rgba(0,0,0,0.20)'
780
+ sm: '0 1px 3px rgba(0,0,0,0.10), 0 1px 2px rgba(0,0,0,0.06)',
781
+ md: '0 4px 6px rgba(0,0,0,0.10), 0 2px 4px rgba(0,0,0,0.06)',
782
+ lg: '0 10px 15px rgba(0,0,0,0.12), 0 4px 6px rgba(0,0,0,0.08)',
783
+ xl: '0 20px 25px rgba(0,0,0,0.15), 0 8px 10px rgba(0,0,0,0.10)'
784
784
  },
785
785
  lg: {
786
786
  sm: '0 2px 4px rgba(0,0,0,0.10)',
@@ -969,6 +969,9 @@
969
969
  rules[_sx(scope, '.bw_card:hover')] = {
970
970
  'box-shadow': elev.md
971
971
  };
972
+ rules[_sx(scope, '.bw_card_hoverable')] = {
973
+ 'transition': 'box-shadow ' + motion.slow + ' ' + motion.easing + ', transform ' + motion.slow + ' ' + motion.easing
974
+ };
972
975
  rules[_sx(scope, '.bw_card_hoverable:hover')] = {
973
976
  'box-shadow': elev.lg
974
977
  };
@@ -1071,7 +1074,8 @@
1071
1074
  };
1072
1075
  rules[_sx(scope, '.bw_navbar_nav .bw_nav_link')] = {
1073
1076
  'color': palette.secondary.base,
1074
- 'border-radius': layout.radius.btn
1077
+ 'border-radius': layout.radius.btn,
1078
+ 'transition': 'color ' + layout.motion.fast + ' ' + layout.motion.easing + ', background-color ' + layout.motion.fast + ' ' + layout.motion.easing
1075
1079
  };
1076
1080
  rules[_sx(scope, '.bw_navbar_nav .bw_nav_link:hover')] = {
1077
1081
  'color': palette.dark.base,
@@ -1228,15 +1232,20 @@
1228
1232
  };
1229
1233
  return rules;
1230
1234
  }
1231
- function generateProgress(scope, palette) {
1235
+ function generateProgress(scope, palette, layout) {
1232
1236
  var rules = {};
1237
+ var rd = layout ? layout.radius : {
1238
+ badge: '.375rem'
1239
+ };
1233
1240
  rules[_sx(scope, '.bw_progress')] = {
1234
1241
  'background-color': palette.surfaceAlt,
1242
+ 'border-radius': rd.badge,
1235
1243
  'box-shadow': 'inset 0 1px 2px rgba(0,0,0,.1)'
1236
1244
  };
1237
1245
  rules[_sx(scope, '.bw_progress_bar')] = {
1238
1246
  'color': palette.primary.textOn,
1239
1247
  'background-color': palette.primary.base,
1248
+ 'border-radius': 'inherit',
1240
1249
  'box-shadow': 'inset 0 -1px 0 rgba(0,0,0,.15)'
1241
1250
  };
1242
1251
  // Variant progress bar colors handled by palette class
@@ -1359,7 +1368,8 @@
1359
1368
  };
1360
1369
  rules[_sx(scope, '.bw_carousel_control')] = {
1361
1370
  'background-color': palette.dark.base,
1362
- 'color': palette.dark.textOn
1371
+ 'color': palette.dark.textOn,
1372
+ 'transition': 'background-color 0.15s ease-out'
1363
1373
  };
1364
1374
  rules[_sx(scope, '.bw_carousel_control:hover')] = {
1365
1375
  'background-color': palette.dark.hover
@@ -1372,9 +1382,13 @@
1372
1382
  }
1373
1383
  function generateModalThemed(scope, palette, layout) {
1374
1384
  var rules = {};
1385
+ var rd = layout ? layout.radius : {
1386
+ card: '8px'
1387
+ };
1375
1388
  rules[_sx(scope, '.bw_modal_content')] = {
1376
1389
  'background-color': palette.surface || '#fff',
1377
1390
  'border-color': palette.light.border,
1391
+ 'border-radius': rd.card,
1378
1392
  'box-shadow': layout.elevation.lg
1379
1393
  };
1380
1394
  rules[_sx(scope, '.bw_modal_header')] = {
@@ -1390,9 +1404,13 @@
1390
1404
  }
1391
1405
  function generateToastThemed(scope, palette, layout) {
1392
1406
  var rules = {};
1407
+ var rd = layout ? layout.radius : {
1408
+ card: '8px'
1409
+ };
1393
1410
  rules[_sx(scope, '.bw_toast')] = {
1394
1411
  'background-color': palette.surface || '#fff',
1395
1412
  'border-color': palette.light.border,
1413
+ 'border-radius': rd.card,
1396
1414
  'box-shadow': layout.elevation.lg
1397
1415
  };
1398
1416
  rules[_sx(scope, '.bw_toast_header')] = {
@@ -1403,9 +1421,13 @@
1403
1421
  }
1404
1422
  function generateDropdownThemed(scope, palette, layout) {
1405
1423
  var rules = {};
1424
+ var rd = layout ? layout.radius : {
1425
+ card: '8px'
1426
+ };
1406
1427
  rules[_sx(scope, '.bw_dropdown_menu')] = {
1407
1428
  'background-color': palette.surface || '#fff',
1408
1429
  'border-color': palette.light.border,
1430
+ 'border-radius': rd.card,
1409
1431
  'box-shadow': layout.elevation.md
1410
1432
  };
1411
1433
  rules[_sx(scope, '.bw_dropdown_item')] = {
@@ -1437,6 +1459,10 @@
1437
1459
  rules[_sx(scope, '.bw_form_switch .bw_switch_input:focus')] = {
1438
1460
  'box-shadow': '0 0 0 0.25rem ' + palette.primary.focus
1439
1461
  };
1462
+ rules[_sx(scope, '.bw_form_switch .bw_switch_input:focus-visible')] = {
1463
+ 'box-shadow': '0 0 0 0.25rem ' + palette.primary.focus,
1464
+ 'outline': 'none'
1465
+ };
1440
1466
  return rules;
1441
1467
  }
1442
1468
  function generateSkeletonThemed(scope, palette) {
@@ -1515,12 +1541,16 @@
1515
1541
  };
1516
1542
  return rules;
1517
1543
  }
1518
- function generateChipInputThemed(scope, palette) {
1544
+ function generateChipInputThemed(scope, palette, layout) {
1519
1545
  var rules = {};
1546
+ var rd = layout ? layout.radius : {
1547
+ input: '6px'
1548
+ };
1520
1549
  rules[_sx(scope, '.bw_chip_input')] = {
1521
1550
  'border-color': palette.light.border,
1522
1551
  'background-color': palette.surface || '#fff',
1523
- 'color': palette.dark.base
1552
+ 'color': palette.dark.base,
1553
+ 'border-radius': rd.input
1524
1554
  };
1525
1555
  rules[_sx(scope, '.bw_chip_input:focus-within')] = {
1526
1556
  'border-color': palette.primary.base,
@@ -1824,7 +1854,7 @@
1824
1854
  * @returns {Object} CSS rules object
1825
1855
  */
1826
1856
  function generateThemedCSS(scopeName, palette, layout) {
1827
- return Object.assign({}, generateResetThemed(scopeName, palette), generateTypographyThemed(scopeName, palette, layout), generateButtons(scopeName, palette, layout), generateAlerts(scopeName, palette, layout), generateCards(scopeName, palette, layout), generateForms(scopeName, palette, layout), generateNavigation(scopeName, palette, layout), generateTables(scopeName, palette, layout), generateTabs(scopeName, palette, layout), generateListGroups(scopeName, palette, layout), generatePagination(scopeName, palette, layout), generateProgress(scopeName, palette), generateBreadcrumbThemed(scopeName, palette, layout), generateCloseButtonThemed(scopeName, palette), generateSectionsThemed(scopeName, palette), generateAccordionThemed(scopeName, palette, layout), generateCarouselThemed(scopeName, palette), generateModalThemed(scopeName, palette, layout), generateToastThemed(scopeName, palette, layout), generateDropdownThemed(scopeName, palette, layout), generateSwitchThemed(scopeName, palette), generateSkeletonThemed(scopeName, palette), generateStatCardThemed(scopeName, palette, layout), generateTimelineThemed(scopeName, palette), generateStepperThemed(scopeName, palette), generateChipInputThemed(scopeName, palette), generateFileUploadThemed(scopeName, palette, layout), generateRangeThemed(scopeName, palette), generateSearchThemed(scopeName, palette, layout), generateTooltipThemed(scopeName, palette, layout), generatePopoverThemed(scopeName, palette, layout), generateCodeDemoThemed(scopeName, palette, layout), generateNavPillsThemed(scopeName, palette, layout), generatePaletteClasses(scopeName, palette));
1857
+ return Object.assign({}, generateResetThemed(scopeName, palette), generateTypographyThemed(scopeName, palette, layout), generateButtons(scopeName, palette, layout), generateAlerts(scopeName, palette, layout), generateCards(scopeName, palette, layout), generateForms(scopeName, palette, layout), generateNavigation(scopeName, palette, layout), generateTables(scopeName, palette, layout), generateTabs(scopeName, palette, layout), generateListGroups(scopeName, palette, layout), generatePagination(scopeName, palette, layout), generateProgress(scopeName, palette, layout), generateBreadcrumbThemed(scopeName, palette, layout), generateCloseButtonThemed(scopeName, palette), generateSectionsThemed(scopeName, palette), generateAccordionThemed(scopeName, palette, layout), generateCarouselThemed(scopeName, palette), generateModalThemed(scopeName, palette, layout), generateToastThemed(scopeName, palette, layout), generateDropdownThemed(scopeName, palette, layout), generateSwitchThemed(scopeName, palette), generateSkeletonThemed(scopeName, palette), generateStatCardThemed(scopeName, palette, layout), generateTimelineThemed(scopeName, palette), generateStepperThemed(scopeName, palette), generateChipInputThemed(scopeName, palette, layout), generateFileUploadThemed(scopeName, palette, layout), generateRangeThemed(scopeName, palette), generateSearchThemed(scopeName, palette, layout), generateTooltipThemed(scopeName, palette, layout), generatePopoverThemed(scopeName, palette, layout), generateCodeDemoThemed(scopeName, palette, layout), generateNavPillsThemed(scopeName, palette, layout), generatePaletteClasses(scopeName, palette));
1828
1858
  }
1829
1859
 
1830
1860
  // =========================================================================
@@ -2130,9 +2160,7 @@
2130
2160
  '.bw_card_footer': {
2131
2161
  'font-size': '0.875rem'
2132
2162
  },
2133
- '.bw_card_hoverable': {
2134
- 'transition': 'all 0.3s ease-out'
2135
- },
2163
+ '.bw_card_hoverable': {},
2136
2164
  '.bw_card_hoverable:hover': {
2137
2165
  'transform': 'translateY(-4px)'
2138
2166
  },
@@ -2167,7 +2195,8 @@
2167
2195
  'background-clip': 'padding-box',
2168
2196
  'appearance': 'none',
2169
2197
  'border': '1px solid transparent',
2170
- 'font-family': 'inherit'
2198
+ 'font-family': 'inherit',
2199
+ 'transition': 'border-color 0.15s ease-out, box-shadow 0.15s ease-out'
2171
2200
  },
2172
2201
  '.bw_form_control:focus': {
2173
2202
  'outline': '2px solid currentColor',
@@ -2445,6 +2474,10 @@
2445
2474
  'background': 'transparent',
2446
2475
  'font-family': 'inherit'
2447
2476
  },
2477
+ '.bw_nav_link:focus-visible': {
2478
+ 'outline': '2px solid currentColor',
2479
+ 'outline-offset': '-2px'
2480
+ },
2448
2481
  '.bw_nav_tabs .bw_nav_link': {
2449
2482
  'border': 'none',
2450
2483
  'border-bottom': '2px solid transparent',
@@ -2752,10 +2785,15 @@
2752
2785
  'background': 'transparent',
2753
2786
  'border': '0',
2754
2787
  'border-radius': '0.25rem',
2755
- 'cursor': 'pointer'
2788
+ 'cursor': 'pointer',
2789
+ 'transition': 'opacity 0.15s ease-out'
2756
2790
  },
2757
2791
  '.bw_close:hover': {
2758
2792
  'opacity': '0.75'
2793
+ },
2794
+ '.bw_close:focus-visible': {
2795
+ 'outline': '2px solid currentColor',
2796
+ 'outline-offset': '2px'
2759
2797
  }
2760
2798
  },
2761
2799
  // ---- Stacks ----
@@ -2938,7 +2976,8 @@
2938
2976
  'content': '""',
2939
2977
  'background-image': "url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23212529'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e\")",
2940
2978
  'background-repeat': 'no-repeat',
2941
- 'background-size': '1.25rem'
2979
+ 'background-size': '1.25rem',
2980
+ 'transition': 'transform 0.2s ease-out'
2942
2981
  },
2943
2982
  '.bw_accordion_button:not(.bw_collapsed)::after': {
2944
2983
  'transform': 'rotate(-180deg)'
@@ -3007,6 +3046,10 @@
3007
3046
  'height': '20px',
3008
3047
  'pointer-events': 'none'
3009
3048
  },
3049
+ '.bw_carousel_control:focus-visible': {
3050
+ 'outline': '2px solid currentColor',
3051
+ 'outline-offset': '2px'
3052
+ },
3010
3053
  '.bw_carousel_control_prev': {
3011
3054
  'left': '10px'
3012
3055
  },
@@ -3050,7 +3093,8 @@
3050
3093
  'overflow-y': 'auto',
3051
3094
  'opacity': '0',
3052
3095
  'visibility': 'hidden',
3053
- 'pointer-events': 'none'
3096
+ 'pointer-events': 'none',
3097
+ 'transition': 'opacity 0.2s ease-out, visibility 0.2s ease-out'
3054
3098
  },
3055
3099
  '.bw_modal.bw_modal_show': {
3056
3100
  'opacity': '1',
@@ -3062,7 +3106,9 @@
3062
3106
  'width': 'calc(100% - 1rem)',
3063
3107
  'max-width': '500px',
3064
3108
  'margin': '1.75rem auto',
3065
- 'pointer-events': 'none'
3109
+ 'pointer-events': 'none',
3110
+ 'transform': 'translateY(-16px)',
3111
+ 'transition': 'transform 0.2s ease-out'
3066
3112
  },
3067
3113
  '.bw_modal.bw_modal_show .bw_modal_dialog': {
3068
3114
  'transform': 'translateY(0)'
@@ -3155,14 +3201,17 @@
3155
3201
  'width': '350px',
3156
3202
  'max-width': 'calc(100vw - 2rem)',
3157
3203
  'background-clip': 'padding-box',
3158
- 'opacity': '0'
3204
+ 'opacity': '0',
3205
+ 'transform': 'translateY(-8px)',
3206
+ 'transition': 'opacity 0.2s ease-out, transform 0.2s ease-out'
3159
3207
  },
3160
3208
  '.bw_toast.bw_toast_show': {
3161
3209
  'opacity': '1',
3162
3210
  'transform': 'translateY(0)'
3163
3211
  },
3164
3212
  '.bw_toast.bw_toast_hiding': {
3165
- 'opacity': '0'
3213
+ 'opacity': '0',
3214
+ 'transform': 'translateY(-8px)'
3166
3215
  },
3167
3216
  '.bw_toast_header': {
3168
3217
  'display': 'flex',
@@ -3206,12 +3255,15 @@
3206
3255
  'border': '1px solid transparent',
3207
3256
  'opacity': '0',
3208
3257
  'visibility': 'hidden',
3209
- 'pointer-events': 'none'
3258
+ 'pointer-events': 'none',
3259
+ 'transform': 'translateY(-4px)',
3260
+ 'transition': 'opacity 0.15s ease-out, transform 0.15s ease-out, visibility 0.15s ease-out'
3210
3261
  },
3211
3262
  '.bw_dropdown_menu.bw_dropdown_show': {
3212
3263
  'opacity': '1',
3213
3264
  'visibility': 'visible',
3214
- 'pointer-events': 'auto'
3265
+ 'pointer-events': 'auto',
3266
+ 'transform': 'translateY(0)'
3215
3267
  },
3216
3268
  '.bw_dropdown_menu_end': {
3217
3269
  'left': 'auto',
@@ -3257,7 +3309,8 @@
3257
3309
  'background-position': 'left center',
3258
3310
  'background-repeat': 'no-repeat',
3259
3311
  'background-size': 'contain',
3260
- 'cursor': 'pointer'
3312
+ 'cursor': 'pointer',
3313
+ 'transition': 'background-color 0.15s ease-out, background-position 0.15s ease-out, border-color 0.15s ease-out'
3261
3314
  },
3262
3315
  '.bw_form_switch .bw_switch_input:checked': {
3263
3316
  'background-position': 'right center'
@@ -3333,9 +3386,7 @@
3333
3386
  '.bw_stat_card': {
3334
3387
  'padding': '1.25rem',
3335
3388
  'border-left': '4px solid transparent',
3336
- 'border-radius': '0.375rem',
3337
- 'background-color': 'inherit',
3338
- 'transition': 'transform 0.15s ease'
3389
+ 'background-color': 'inherit'
3339
3390
  },
3340
3391
  '.bw_stat_card:hover': {
3341
3392
  'transform': 'translateY(-1px)'
@@ -3508,6 +3559,10 @@
3508
3559
  'cursor': 'pointer',
3509
3560
  'padding': '0',
3510
3561
  'border-radius': '50%'
3562
+ },
3563
+ '.bw_search_clear:focus-visible': {
3564
+ 'outline': '2px solid currentColor',
3565
+ 'outline-offset': '2px'
3511
3566
  }
3512
3567
  },
3513
3568
  // ---- Range ----
@@ -3750,6 +3805,10 @@
3750
3805
  'padding': '0',
3751
3806
  'border-radius': '50%'
3752
3807
  },
3808
+ '.bw_chip_remove:focus-visible': {
3809
+ 'outline': '2px solid currentColor',
3810
+ 'outline-offset': '1px'
3811
+ },
3753
3812
  '.bw_chip_field': {
3754
3813
  'flex': '1',
3755
3814
  'min-width': '80px',
@@ -5302,6 +5361,296 @@
5302
5361
  return intervalID;
5303
5362
  }
5304
5363
 
5364
+ /**
5365
+ * Bitwrench Router -- client-side URL routing for SPAs
5366
+ *
5367
+ * Single export: initRouter(bw) attaches bw.router(), bw.navigate(), bw.link()
5368
+ *
5369
+ * @license BSD-2-Clause
5370
+ */
5371
+
5372
+ // -- internal helpers --
5373
+
5374
+ function normalizePath(p) {
5375
+ // strip query string (handled separately)
5376
+ var qi = p.indexOf('?');
5377
+ if (qi >= 0) p = p.substring(0, qi);
5378
+ // collapse double slashes, strip trailing slash
5379
+ p = p.replace(/\/\/+/g, '/');
5380
+ if (p.length > 1 && p.charAt(p.length - 1) === '/') p = p.substring(0, p.length - 1);
5381
+ return p || '/';
5382
+ }
5383
+ function parseQuery(fullPath) {
5384
+ var qi = fullPath.indexOf('?');
5385
+ if (qi < 0) return {};
5386
+ var qs = fullPath.substring(qi + 1);
5387
+ var result = {};
5388
+ var pairs = qs.split('&');
5389
+ for (var i = 0; i < pairs.length; i++) {
5390
+ var kv = pairs[i].split('=');
5391
+ if (kv[0]) result[decodeURIComponent(kv[0])] = kv.length > 1 ? decodeURIComponent(kv[1]) : '';
5392
+ }
5393
+ return result;
5394
+ }
5395
+ function matchRoute(routes, rawPath) {
5396
+ var query = parseQuery(rawPath);
5397
+ var path = normalizePath(rawPath);
5398
+ var segs = path === '/' ? [''] : path.split('/');
5399
+ var globalWild = null;
5400
+ for (var i = 0; i < routes.length; i++) {
5401
+ var r = routes[i];
5402
+ var pattern = r.pattern;
5403
+
5404
+ // global wildcard -- save for last
5405
+ if (pattern === '*') {
5406
+ globalWild = r;
5407
+ continue;
5408
+ }
5409
+
5410
+ // catch-all: ends with /*
5411
+ if (pattern.length > 1 && pattern.substring(pattern.length - 2) === '/*') {
5412
+ var prefix = pattern.substring(0, pattern.length - 2);
5413
+ var prefixSegs = prefix === '' ? [''] : prefix.split('/');
5414
+ if (segs.length < prefixSegs.length) continue;
5415
+ var params = {};
5416
+ var ok = true;
5417
+ for (var j = 0; j < prefixSegs.length; j++) {
5418
+ if (prefixSegs[j].charAt(0) === ':') {
5419
+ params[prefixSegs[j].substring(1)] = segs[j];
5420
+ } else if (prefixSegs[j] !== segs[j]) {
5421
+ ok = false;
5422
+ break;
5423
+ }
5424
+ }
5425
+ if (ok) {
5426
+ params._rest = segs.slice(prefixSegs.length).join('/');
5427
+ params._query = query;
5428
+ return {
5429
+ handler: r.handler,
5430
+ params: params
5431
+ };
5432
+ }
5433
+ continue;
5434
+ }
5435
+
5436
+ // exact / parameterized match
5437
+ var rSegs = pattern === '/' ? [''] : pattern.split('/');
5438
+ if (rSegs.length !== segs.length) continue;
5439
+ var params2 = {};
5440
+ var match = true;
5441
+ for (var k = 0; k < rSegs.length; k++) {
5442
+ if (rSegs[k].charAt(0) === ':') {
5443
+ params2[rSegs[k].substring(1)] = segs[k];
5444
+ } else if (rSegs[k] !== segs[k]) {
5445
+ match = false;
5446
+ break;
5447
+ }
5448
+ }
5449
+ if (match) {
5450
+ params2._query = query;
5451
+ return {
5452
+ handler: r.handler,
5453
+ params: params2
5454
+ };
5455
+ }
5456
+ }
5457
+
5458
+ // global wildcard fallback
5459
+ if (globalWild) {
5460
+ return {
5461
+ handler: globalWild.handler,
5462
+ params: {
5463
+ _query: query
5464
+ }
5465
+ };
5466
+ }
5467
+ return null;
5468
+ }
5469
+
5470
+ // -- public API factory --
5471
+
5472
+ function initRouter(bw) {
5473
+ var _activeRouter = null;
5474
+ bw.router = function (config) {
5475
+ if (!config || !config.routes) throw new Error('bw.router: config.routes is required');
5476
+ if (!bw._isBrowser) throw new Error('bw.router: requires a browser environment');
5477
+ var mode = config.mode || 'hash';
5478
+ var base = config.base || '/';
5479
+ if (base.length > 1 && base.charAt(base.length - 1) === '/') base = base.substring(0, base.length - 1);
5480
+ var target = config.target || null;
5481
+
5482
+ // compile routes (preserve registration order)
5483
+ var routes = [];
5484
+ var keys = Object.keys(config.routes);
5485
+ for (var i = 0; i < keys.length; i++) {
5486
+ routes.push({
5487
+ pattern: keys[i],
5488
+ handler: config.routes[keys[i]]
5489
+ });
5490
+ }
5491
+ var currentPath = '/';
5492
+ var destroyed = false;
5493
+ function getPath() {
5494
+ if (mode === 'hash') {
5495
+ var h = window.location.hash.replace(/^#/, '');
5496
+ return h || '/';
5497
+ }
5498
+ var p = window.location.pathname;
5499
+ if (base !== '/' && p.indexOf(base) === 0) {
5500
+ p = p.substring(base.length) || '/';
5501
+ }
5502
+ var s = window.location.search || '';
5503
+ return p + s;
5504
+ }
5505
+ function handleRoute(toRaw, opts) {
5506
+ if (destroyed) return;
5507
+ var fromPath = currentPath;
5508
+ var toPath = normalizePath(toRaw);
5509
+
5510
+ // before guard
5511
+ if (config.before) {
5512
+ var result = config.before(toPath, fromPath);
5513
+ if (result === false) return;
5514
+ if (typeof result === 'string') {
5515
+ toPath = normalizePath(result);
5516
+ toRaw = result;
5517
+ }
5518
+ }
5519
+ currentPath = toPath;
5520
+
5521
+ // match route
5522
+ var m = matchRoute(routes, toRaw);
5523
+ if (m) {
5524
+ var rendered = m.handler(m.params);
5525
+ if (rendered != null && target) {
5526
+ bw.DOM(target, rendered);
5527
+ }
5528
+ }
5529
+
5530
+ // pub/sub
5531
+ var query = parseQuery(toRaw);
5532
+ bw.pub('bw:route', {
5533
+ path: toPath,
5534
+ params: m ? m.params : {},
5535
+ query: query,
5536
+ from: fromPath
5537
+ });
5538
+
5539
+ // after hook
5540
+ if (config.after) config.after(toPath, fromPath);
5541
+ }
5542
+ function navigate(path, opts) {
5543
+ if (destroyed) return;
5544
+ opts = opts || {};
5545
+ if (mode === 'hash') {
5546
+ if (opts.replace) {
5547
+ var loc = window.location;
5548
+ loc.replace(loc.pathname + loc.search + '#' + path);
5549
+ } else {
5550
+ window.location.hash = path;
5551
+ }
5552
+ // hashchange listener will fire handleRoute; but if same hash, trigger manually
5553
+ var currentHash = window.location.hash.replace(/^#/, '') || '/';
5554
+ if (normalizePath(currentHash) === normalizePath(path)) {
5555
+ handleRoute(path);
5556
+ }
5557
+ } else {
5558
+ var url = (base === '/' ? '' : base) + path;
5559
+ if (opts.replace) {
5560
+ window.history.replaceState(null, '', url);
5561
+ } else {
5562
+ window.history.pushState(null, '', url);
5563
+ }
5564
+ handleRoute(path);
5565
+ }
5566
+ }
5567
+ function onHashChange() {
5568
+ if (destroyed) return;
5569
+ handleRoute(getPath());
5570
+ }
5571
+ function onPopState() {
5572
+ if (destroyed) return;
5573
+ handleRoute(getPath());
5574
+ }
5575
+
5576
+ // listen
5577
+ if (mode === 'hash') {
5578
+ window.addEventListener('hashchange', onHashChange);
5579
+ } else {
5580
+ window.addEventListener('popstate', onPopState);
5581
+ }
5582
+
5583
+ // initial render
5584
+ handleRoute(getPath());
5585
+ var routerObj = {
5586
+ navigate: navigate,
5587
+ current: function current() {
5588
+ var raw = getPath();
5589
+ var m = matchRoute(routes, raw);
5590
+ return {
5591
+ path: currentPath,
5592
+ params: m ? m.params : {},
5593
+ query: parseQuery(raw)
5594
+ };
5595
+ },
5596
+ destroy: function destroy() {
5597
+ destroyed = true;
5598
+ if (mode === 'hash') {
5599
+ window.removeEventListener('hashchange', onHashChange);
5600
+ } else {
5601
+ window.removeEventListener('popstate', onPopState);
5602
+ }
5603
+ if (_activeRouter === routerObj) _activeRouter = null;
5604
+ }
5605
+ };
5606
+ _activeRouter = routerObj;
5607
+ return routerObj;
5608
+ };
5609
+ bw.navigate = function (path, opts) {
5610
+ if (_activeRouter) {
5611
+ _activeRouter.navigate(path, opts);
5612
+ } else {
5613
+ if (typeof console !== 'undefined' && console.warn) {
5614
+ console.warn('bw.navigate: no active router');
5615
+ }
5616
+ }
5617
+ };
5618
+ bw.link = function (path, content, attrs) {
5619
+ var a = {};
5620
+ if (attrs) {
5621
+ var keys = Object.keys(attrs);
5622
+ for (var i = 0; i < keys.length; i++) a[keys[i]] = attrs[keys[i]];
5623
+ }
5624
+ if (_activeRouter) {
5625
+ a.href = '#' + path;
5626
+ } else {
5627
+ a.href = path;
5628
+ }
5629
+ a.onclick = function (e) {
5630
+ e.preventDefault();
5631
+ bw.navigate(path);
5632
+ };
5633
+ return {
5634
+ t: 'a',
5635
+ a: a,
5636
+ c: content
5637
+ };
5638
+ };
5639
+
5640
+ // expose for testing: internal helpers
5641
+ bw._router = {
5642
+ matchRoute: matchRoute,
5643
+ normalizePath: normalizePath,
5644
+ parseQuery: parseQuery,
5645
+ getActiveRouter: function getActiveRouter() {
5646
+ return _activeRouter;
5647
+ },
5648
+ resetActiveRouter: function resetActiveRouter() {
5649
+ _activeRouter = null;
5650
+ }
5651
+ };
5652
+ }
5653
+
5305
5654
  var _excluded$1 = ["type", "placeholder", "value", "id", "name", "disabled", "readonly", "required", "className", "style"],
5306
5655
  _excluded2$1 = ["placeholder", "value", "rows", "id", "name", "disabled", "readonly", "required", "className"],
5307
5656
  _excluded3 = ["options", "value", "id", "name", "disabled", "required", "className"],
@@ -13227,6 +13576,7 @@
13227
13576
  bw.getAllComponents = function () {
13228
13577
  return new Map(bw._componentRegistry);
13229
13578
  };
13579
+ initRouter(bw);
13230
13580
 
13231
13581
  // Register all make functions
13232
13582
  Object.entries(components).forEach(function (_ref1) {