@kupola/kupola 1.4.2 → 1.4.3

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 (63) hide show
  1. package/README.md +3 -3
  2. package/css/kupola.css +3 -2
  3. package/css/table.css +74 -0
  4. package/dist/css/kupola.css +12 -1
  5. package/dist/css/kupola.min.css +1 -1
  6. package/dist/kupola.cjs.js +1800 -3609
  7. package/dist/kupola.cjs.js.map +1 -1
  8. package/dist/kupola.css +100 -3
  9. package/dist/kupola.esm.js +1791 -3610
  10. package/dist/kupola.esm.js.map +1 -1
  11. package/dist/kupola.min.css +1 -1
  12. package/dist/kupola.min.js +1 -1
  13. package/dist/kupola.min.js.map +1 -1
  14. package/dist/kupola.umd.js +1800 -3609
  15. package/dist/kupola.umd.js.map +1 -1
  16. package/dist/types/kupola.d.ts +41 -25
  17. package/js/calendar.js +3 -10
  18. package/js/carousel.js +3 -10
  19. package/js/collapse.js +3 -10
  20. package/js/color-picker.js +5 -16
  21. package/js/component.js +4 -18
  22. package/js/countdown.js +3 -10
  23. package/js/data-bind.js +56 -36
  24. package/js/datepicker.js +16 -24
  25. package/js/depends.js +27 -16
  26. package/js/dialog.js +1 -5
  27. package/js/drawer.js +3 -11
  28. package/js/dropdown.js +11 -18
  29. package/js/dynamic-tags.js +3 -10
  30. package/js/fileupload.js +10 -12
  31. package/js/form.js +26 -20
  32. package/js/global-events.js +1 -14
  33. package/js/heatmap.js +3 -12
  34. package/js/i18n.js +1 -14
  35. package/js/icons.js +2 -2
  36. package/js/image-preview.js +1 -7
  37. package/js/initializer.js +0 -43
  38. package/js/kupola-core.js +1 -13
  39. package/js/kupola-lifecycle.js +1 -9
  40. package/js/message.js +1 -6
  41. package/js/modal.js +22 -20
  42. package/js/notification.js +1 -6
  43. package/js/numberinput.js +3 -10
  44. package/js/pagination.js +0 -5
  45. package/js/registry.js +0 -4
  46. package/js/select.js +5 -16
  47. package/js/slide-captcha.js +8 -21
  48. package/js/slider.js +3 -10
  49. package/js/statcard.js +3 -11
  50. package/js/table.js +240 -262
  51. package/js/tag.js +3 -10
  52. package/js/theme.js +1 -12
  53. package/js/timepicker.js +6 -19
  54. package/js/tooltip.js +3 -10
  55. package/js/utils.js +281 -1439
  56. package/js/validation.js +2 -7
  57. package/js/virtual-list.js +13 -38
  58. package/js/web-components.js +0 -9
  59. package/package.json +10 -5
  60. package/scripts/build-css.cjs +3 -2
  61. package/types/kupola.d.ts +41 -25
  62. package/version.json +10 -0
  63. /package/css/{colors_and_type.css → colors-and-type.css} +0 -0
@@ -468,6 +468,18 @@ declare class KupolaUtils {
468
468
 
469
469
  declare const kupolaUtils: KupolaUtils;
470
470
 
471
+ declare const stringUtils: KupolaUtils['string'];
472
+ declare const arrayUtils: KupolaUtils['array'];
473
+ declare const objectUtils: KupolaUtils['object'];
474
+ declare const numberUtils: KupolaUtils['number'];
475
+ declare const dateUtils: KupolaUtils['date'];
476
+ declare const validatorUtils: KupolaUtils['validator'];
477
+ declare const cryptoUtils: KupolaUtils['crypto'];
478
+ declare const preloadUtils: KupolaUtils['preload'];
479
+
480
+ declare function debounce<T extends (...args: unknown[]) => void>(func: T, wait: number, options?: DebounceOptions): T;
481
+ declare function throttle<T extends (...args: unknown[]) => void>(func: T, limit: number, options?: ThrottleOptions): T;
482
+
471
483
  declare interface FormState {
472
484
  valid: boolean;
473
485
  errors: Record<string, string>;
@@ -681,14 +693,14 @@ declare class KupolaModalElement extends HTMLElement {
681
693
  close(): void;
682
694
  }
683
695
 
684
- declare function createModal(options?: Record<string, unknown>): any;
685
- declare function confirmModal(options: Record<string, unknown> | string): any;
686
- declare function alertModal(options: Record<string, unknown> | string): any;
696
+ declare function createModal(options?: Record<string, unknown>): Modal;
697
+ declare function confirmModal(options: Record<string, unknown> | string): Modal;
698
+ declare function alertModal(options: Record<string, unknown> | string): Modal;
687
699
 
688
700
  declare function cleanupAllDropdowns(): void;
689
701
  declare function cleanupAllSlideCaptchas(): void;
690
702
 
691
- declare function ref<T = unknown>(initialValue?: T): { value: T; _subscribers: Set<Function> };
703
+ declare function ref<T = unknown>(initialValue?: T): { value: T; _subscribers: Set<Function>; subscribe: (callback: Function) => { unsubscribe: () => void } };
692
704
 
693
705
  declare class Modal {
694
706
  constructor(element: HTMLElement, options?: Record<string, unknown>);
@@ -720,7 +732,7 @@ interface KupolaHttpResponse {
720
732
  statusText?: string;
721
733
  headers: Record<string, string>;
722
734
  url?: string;
723
- json(): Promise<any>;
735
+ json(): Promise<unknown>;
724
736
  text(): Promise<string>;
725
737
  }
726
738
 
@@ -816,16 +828,16 @@ declare function clearCache(): void;
816
828
  // Table & Pagination Components
817
829
  // ============================================================
818
830
 
819
- interface KupolaTableColumn {
831
+ interface KupolaTableColumn<T = Record<string, unknown>> {
820
832
  key: string;
821
833
  title: string;
822
834
  width?: string | number;
823
835
  minWidth?: string | number;
824
836
  align?: 'left' | 'center' | 'right';
825
837
  sortable?: boolean;
826
- sorter?: (a: unknown, b: unknown, order?: string) => number;
838
+ sorter?: (a: T, b: T, order?: string) => number;
827
839
  fixed?: 'left' | 'right';
828
- render?: (value: unknown, row: unknown, index?: number) => string | HTMLElement;
840
+ render?: (value: unknown, row: T, index?: number) => string | HTMLElement;
829
841
  editable?: boolean;
830
842
  editType?: string;
831
843
  editOptions?: Array<{ value: string; label: string }>;
@@ -842,8 +854,8 @@ interface KupolaTableVirtualScrollOptions {
842
854
  maxHeight?: string;
843
855
  }
844
856
 
845
- interface KupolaTableOptions {
846
- columns: KupolaTableColumn[];
857
+ interface KupolaTableOptions<T = Record<string, unknown>> {
858
+ columns: KupolaTableColumn<T>[];
847
859
  rowKey?: string;
848
860
  striped?: boolean;
849
861
  bordered?: boolean;
@@ -858,31 +870,31 @@ interface KupolaTableOptions {
858
870
  showExport?: boolean;
859
871
  showPageSize?: boolean;
860
872
  selection?: 'checkbox' | 'radio';
861
- expandable?: (row: unknown) => string | HTMLElement;
873
+ expandable?: (row: T) => string | HTMLElement;
862
874
  editable?: boolean;
863
875
  resizable?: boolean;
864
876
  draggable?: boolean;
865
877
  multiSort?: boolean;
866
878
  tree?: KupolaTableTreeOptions;
867
879
  virtualScroll?: KupolaTableVirtualScrollOptions;
868
- mergeCells?: (data: unknown[]) => Array<{ row: number; col: number; rowSpan: number; colSpan: number }>;
880
+ mergeCells?: (data: T[]) => Array<{ row: number; col: number; rowSpan: number; colSpan: number }>;
869
881
  onSort?: (sorts: Array<{ key: string; order: string }>) => void;
870
882
  onFilter?: (text: string) => void;
871
- onRowClick?: (row: unknown, index: number, event: Event) => void;
883
+ onRowClick?: (row: T, index: number, event: Event) => void;
872
884
  onPageChange?: (page: number, pageSize: number) => void;
873
- onSelect?: (selectedKeys: unknown[], selectedRows: unknown[]) => void;
885
+ onSelect?: (selectedKeys: unknown[], selectedRows: T[]) => void;
874
886
  onExpand?: (key: unknown, isExpanded: boolean) => void;
875
- onEditSave?: (row: unknown, colKey: string, newValue: unknown, allData: unknown[]) => void;
876
- onEditCancel?: (editingCell: unknown) => void;
887
+ onEditSave?: (row: T, colKey: string, newValue: unknown, allData: T[]) => void;
888
+ onEditCancel?: (editingCell: { row: T; colKey: string; value: unknown }) => void;
877
889
  onColumnResize?: (colKey: string, newWidth: number) => void;
878
- onRowDragEnd?: (movedRow: unknown, fromIndex: number, toIndex: number, newData: unknown[]) => void;
890
+ onRowDragEnd?: (movedRow: T, fromIndex: number, toIndex: number, newData: T[]) => void;
879
891
  }
880
892
 
881
- declare class KupolaTable {
882
- constructor(element: HTMLElement | string, options?: KupolaTableOptions);
883
- setData(data: unknown[] | { value: unknown[] | null }): void;
893
+ declare class KupolaTable<T = Record<string, unknown>> {
894
+ constructor(element: HTMLElement | string, options?: KupolaTableOptions<T>);
895
+ setData(data: T[] | { value: T[] | null }): void;
884
896
  setLoading(loading: boolean): void;
885
- setColumns(columns: KupolaTableColumn[]): void;
897
+ setColumns(columns: KupolaTableColumn<T>[]): void;
886
898
  setPageSize(size: number): void;
887
899
  getSortState(): { key: string | null; order: string | null } | Array<{ key: string; order: string }>;
888
900
  getPage(): { current: number; pageSize: number; total: number };
@@ -894,11 +906,11 @@ declare class KupolaTable {
894
906
  selectRow(key: unknown): void;
895
907
  deselectRow(key: unknown): void;
896
908
  getSelectedKeys(): unknown[];
897
- getSelectedRows(): unknown[];
909
+ getSelectedRows(): T[];
898
910
  destroy(): void;
899
911
  }
900
912
 
901
- declare function initTable(element: HTMLElement | string, options?: KupolaTableOptions): KupolaTable;
913
+ declare function initTable<T = Record<string, unknown>>(element: HTMLElement | string, options?: KupolaTableOptions<T>): KupolaTable<T>;
902
914
  declare function initAllTables(): KupolaTable[];
903
915
 
904
916
  interface KupolaPaginationOptions {
@@ -1334,7 +1346,7 @@ declare module 'kupola' {
1334
1346
  export function cleanupAllDropdowns(): void;
1335
1347
  export function cleanupAllSlideCaptchas(): void;
1336
1348
 
1337
- export function ref<T = unknown>(initialValue?: T): { value: T; _subscribers: Set<Function> };
1349
+ export function ref<T = unknown>(initialValue?: T): { value: T; _subscribers: Set<Function>; subscribe: (callback: Function) => { unsubscribe: () => void } };
1338
1350
 
1339
1351
  export class Modal {
1340
1352
  constructor(element: HTMLElement, options?: Record<string, unknown>);
@@ -1358,7 +1370,7 @@ declare module 'kupola' {
1358
1370
  statusText?: string;
1359
1371
  headers: Record<string, string>;
1360
1372
  url?: string;
1361
- json(): Promise<any>;
1373
+ json(): Promise<unknown>;
1362
1374
  text(): Promise<string>;
1363
1375
  }
1364
1376
 
@@ -1454,4 +1466,8 @@ declare module 'kupola' {
1454
1466
  export { KupolaTable, KupolaTableColumn, KupolaTableOptions, KupolaPagination, KupolaPaginationOptions };
1455
1467
  export { initTable, initAllTables, initPagination };
1456
1468
 
1469
+ // Utils (tree-shakeable namespace exports)
1470
+ export { KupolaUtils, stringUtils, arrayUtils, objectUtils, numberUtils, dateUtils };
1471
+ export { debounce, throttle, validatorUtils, cryptoUtils, preloadUtils };
1472
+
1457
1473
  }
package/js/calendar.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { kupolaInitializer } from './initializer.js';
2
+
1
3
  class Calendar {
2
4
  constructor(element, options = {}) {
3
5
  this.element = element;
@@ -481,13 +483,4 @@ function initCalendars() {
481
483
 
482
484
  export { Calendar, initCalendars, initCalendar, cleanupCalendar };
483
485
 
484
- if (typeof window !== 'undefined') {
485
- window.Calendar = Calendar;
486
- window.initCalendar = initCalendar;
487
- window.cleanupCalendar = cleanupCalendar;
488
- window.initCalendars = initCalendars;
489
-
490
- if (window.kupolaInitializer) {
491
- window.kupolaInitializer.register('calendar', initCalendar, cleanupCalendar);
492
- }
493
- }
486
+ kupolaInitializer.register('calendar', initCalendar, cleanupCalendar);
package/js/carousel.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { kupolaInitializer } from './initializer.js';
2
+
1
3
  class Carousel {
2
4
  constructor(element, options = {}) {
3
5
  this.element = element;
@@ -295,13 +297,4 @@ function cleanupCarousel(element) {
295
297
 
296
298
  export { Carousel, initCarousel, initCarousels, cleanupCarousel };
297
299
 
298
- if (typeof window !== 'undefined') {
299
- window.Carousel = Carousel;
300
- window.initCarousel = initCarousel;
301
- window.initCarousels = initCarousels;
302
-
303
- if (window.kupolaInitializer) {
304
- window.kupolaInitializer.register('carousel', initCarousel, cleanupCarousel);
305
- }
306
- }
307
-
300
+ kupolaInitializer.register('carousel', initCarousel, cleanupCarousel);
package/js/collapse.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { kupolaInitializer } from './initializer.js';
2
+
1
3
  class Collapse {
2
4
  constructor(element, options = {}) {
3
5
  this.element = element;
@@ -229,13 +231,4 @@ function initCollapses() {
229
231
 
230
232
  export { Collapse, initCollapses, initCollapse, cleanupCollapse };
231
233
 
232
- if (typeof window !== 'undefined') {
233
- window.Collapse = Collapse;
234
- window.initCollapse = initCollapse;
235
- window.cleanupCollapse = cleanupCollapse;
236
- window.initCollapses = initCollapses;
237
-
238
- if (window.kupolaInitializer) {
239
- window.kupolaInitializer.register('collapse', initCollapse, cleanupCollapse);
240
- }
241
- }
234
+ kupolaInitializer.register('collapse', initCollapse, cleanupCollapse);
@@ -1,3 +1,6 @@
1
+ import { globalEvents } from './global-events.js';
2
+ import { kupolaInitializer } from './initializer.js';
3
+
1
4
  class ColorPicker {
2
5
  constructor(element, options = {}) {
3
6
  this.element = element;
@@ -251,11 +254,7 @@ class ColorPicker {
251
254
  }
252
255
  });
253
256
 
254
- if (window.globalEvents) {
255
- this._documentClickListener = window.globalEvents.on(document, 'click', this._documentClickHandler, { scope: this.scope });
256
- } else {
257
- document.addEventListener('click', this._documentClickHandler);
258
- }
257
+ this._documentClickListener = globalEvents.on(document, 'click', this._documentClickHandler, { scope: this.scope });
259
258
 
260
259
  this._renderPreviousColors();
261
260
  this._renderColorPanel();
@@ -439,14 +438,4 @@ function cleanupAllColorPickers() {
439
438
 
440
439
  export { ColorPicker, initColorPicker, initColorPickers, cleanupColorPicker, cleanupAllColorPickers };
441
440
 
442
- if (typeof window !== 'undefined') {
443
- window.ColorPicker = ColorPicker;
444
- window.initColorPicker = initColorPicker;
445
- window.initColorPickers = initColorPickers;
446
- window.cleanupColorPicker = cleanupColorPicker;
447
- window.cleanupAllColorPickers = cleanupAllColorPickers;
448
-
449
- if (window.kupolaInitializer) {
450
- window.kupolaInitializer.register('color-picker', initColorPicker, cleanupColorPicker);
451
- }
452
- }
441
+ kupolaInitializer.register('color-picker', initColorPicker, cleanupColorPicker);
package/js/component.js CHANGED
@@ -120,15 +120,10 @@ export class KupolaComponent {
120
120
 
121
121
  await this.lifecycle.bootstrap();
122
122
 
123
- if (typeof window !== 'undefined' && window._setCurrentSetupContext && window.SetupContext) {
124
- this.setupContext = new window.SetupContext(this);
125
- window._setCurrentSetupContext(this.setupContext);
126
-
127
- if (typeof this.setup === 'function') {
128
- const result = this.setup();
129
- if (result instanceof Promise) {
130
- await result;
131
- }
123
+ if (typeof this.setup === 'function') {
124
+ const result = this.setup();
125
+ if (result instanceof Promise) {
126
+ await result;
132
127
  }
133
128
  }
134
129
 
@@ -136,10 +131,6 @@ export class KupolaComponent {
136
131
  await this.lifecycle.mount();
137
132
 
138
133
  this.setupContext?._executeMounted();
139
-
140
- if (typeof window !== 'undefined' && window._clearSetupContext) {
141
- window._clearSetupContext();
142
- }
143
134
  } catch (error) {
144
135
  console.error(`[KupolaComponent] Error mounting component "${this.constructor.name}":`, error);
145
136
 
@@ -257,9 +248,4 @@ export function applyMixin(componentClass, mixin) {
257
248
  componentClass.prototype[key] = mixin[key];
258
249
  }
259
250
  });
260
- }
261
-
262
- if (typeof window !== 'undefined') {
263
- window.KupolaComponent = KupolaComponent;
264
- window.applyMixin = applyMixin;
265
251
  }
package/js/countdown.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { kupolaInitializer } from './initializer.js';
2
+
1
3
  class Countdown {
2
4
  constructor(element) {
3
5
  this.element = element;
@@ -133,13 +135,4 @@ function initCountdowns() {
133
135
 
134
136
  export { Countdown, initCountdowns, initCountdown, cleanupCountdown };
135
137
 
136
- if (typeof window !== 'undefined') {
137
- window.Countdown = Countdown;
138
- window.initCountdown = initCountdown;
139
- window.cleanupCountdown = cleanupCountdown;
140
- window.initCountdowns = initCountdowns;
141
-
142
- if (window.kupolaInitializer) {
143
- window.kupolaInitializer.register('countdown', initCountdown, cleanupCountdown);
144
- }
145
- }
138
+ kupolaInitializer.register('countdown', initCountdown, cleanupCountdown);
package/js/data-bind.js CHANGED
@@ -11,13 +11,26 @@
11
11
  */
12
12
  function sanitizeHtml(html) {
13
13
  if (!html) return '';
14
- const str = String(html);
15
- return str
16
- .replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
17
- .replace(/<iframe\b[^<]*(?:(?!<\/iframe>)<[^<]*)*<\/iframe>/gi, '')
18
- .replace(/<object\b[^<]*(?:(?!<\/object>)<[^<]*)*<\/object>/gi, '')
19
- .replace(/<embed\b[^>]*\/?>/gi, '')
20
- .replace(/\bon\w+\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/gi, '');
14
+ let str = String(html);
15
+
16
+ // Iteratively remove dangerous tags (handles nested attempts like <scr<script>ipt>)
17
+ const dangerousTags = /<\s*(script|iframe|object|embed|applet|form|base|link|meta|style)\b[^>]*>[\s\S]*?<\s*\/\s*\1\s*>|<\s*(script|iframe|object|embed|applet|form|base|link|meta|style)\b[^>]*\/?>/gi;
18
+ let prev;
19
+ do {
20
+ prev = str;
21
+ str = str.replace(dangerousTags, '');
22
+ } while (str !== prev);
23
+
24
+ // Remove event handler attributes (on* = ...)
25
+ str = str.replace(/\bon\w+\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/gi, '');
26
+
27
+ // Remove javascript:, vbscript:, data: URIs in href/src/action attributes
28
+ str = str.replace(/(href|src|action|background)\s*=\s*(?:"[^"]*(?:javascript|vbscript|data)\s*:[^"]*"|'[^']*(?:javascript|vbscript|data)\s*:[^']*'|[^\s>]*(?:javascript|vbscript|data)\s*:[^\s>]*)/gi, '$1=""');
29
+
30
+ // Remove expression() in style attributes (IE CSS expression)
31
+ str = str.replace(/expression\s*\([^)]*\)/gi, '');
32
+
33
+ return str;
21
34
  }
22
35
 
23
36
  class TrieNode {
@@ -946,18 +959,26 @@ class KupolaDataBind {
946
959
  });
947
960
 
948
961
  if (!this._mutationObserver) {
962
+ this._isObserving = false;
949
963
  this._mutationObserver = new MutationObserver((mutations) => {
950
- mutations.forEach(mutation => {
951
- mutation.addedNodes.forEach(node => {
952
- if (node.nodeType === Node.ELEMENT_NODE) {
953
- const bindElements = node.querySelectorAll('[data-bind]');
954
- bindElements.forEach(element => this._bindElement(element));
955
- if (node.hasAttribute && node.hasAttribute('data-bind')) {
956
- this._bindElement(node);
964
+ // Re-entry guard: skip mutations caused by _bindElement itself
965
+ if (this._isObserving) return;
966
+ this._isObserving = true;
967
+ try {
968
+ mutations.forEach(mutation => {
969
+ mutation.addedNodes.forEach(node => {
970
+ if (node.nodeType === Node.ELEMENT_NODE) {
971
+ const bindElements = node.querySelectorAll('[data-bind]');
972
+ bindElements.forEach(element => this._bindElement(element));
973
+ if (node.hasAttribute && node.hasAttribute('data-bind')) {
974
+ this._bindElement(node);
975
+ }
957
976
  }
958
- }
977
+ });
959
978
  });
960
- });
979
+ } finally {
980
+ this._isObserving = false;
981
+ }
961
982
  });
962
983
 
963
984
  this._mutationObserver.observe(document.body, {
@@ -1052,12 +1073,12 @@ class KupolaStore {
1052
1073
 
1053
1074
  this.observers = {};
1054
1075
 
1055
- if (window.kupolaData) {
1056
- window.kupolaData.set(this._stateKey, initialState);
1057
- this.state = window.kupolaData.data?.[this._stateKey] ||
1058
- window.kupolaData.createReactive(initialState, this._stateKey);
1076
+ if (kupolaData) {
1077
+ kupolaData.set(this._stateKey, initialState);
1078
+ this.state = kupolaData.data?.[this._stateKey] ||
1079
+ kupolaData.createReactive(initialState, this._stateKey);
1059
1080
 
1060
- window.kupolaData.observe(this._stateKey, (newState) => {
1081
+ kupolaData.observe(this._stateKey, (newState) => {
1061
1082
  this.notify(newState);
1062
1083
  });
1063
1084
  } else {
@@ -1140,8 +1161,8 @@ class KupolaStore {
1140
1161
  });
1141
1162
  }
1142
1163
 
1143
- if (window.kupolaData) {
1144
- window.kupolaData.set(this.name, newState);
1164
+ if (kupolaData) {
1165
+ kupolaData.set(this.name, newState);
1145
1166
  }
1146
1167
  }
1147
1168
 
@@ -1303,6 +1324,18 @@ function ref(initialValue = null) {
1303
1324
  }
1304
1325
  });
1305
1326
 
1327
+ /**
1328
+ * Subscribe to value changes.
1329
+ * @param {Function} callback - Called with new value on change
1330
+ * @returns {{ unsubscribe: Function }} Unsubscribe handle
1331
+ */
1332
+ refObj.subscribe = (callback) => {
1333
+ refObj._subscribers.add(callback);
1334
+ return {
1335
+ unsubscribe() { refObj._subscribers.delete(callback); }
1336
+ };
1337
+ };
1338
+
1306
1339
  return refObj;
1307
1340
  }
1308
1341
 
@@ -1328,16 +1361,3 @@ export { KupolaDataBind, KupolaEventBus, KupolaStore, KupolaStoreManager };
1328
1361
  export { kupolaData, kupolaEvents, kupolaStoreManager };
1329
1362
  export { createStore, getStore };
1330
1363
  export { ref };
1331
-
1332
- if (typeof window !== 'undefined') {
1333
- window.KupolaDataBind = KupolaDataBind;
1334
- window.KupolaEventBus = KupolaEventBus;
1335
- window.KupolaStore = KupolaStore;
1336
- window.KupolaStoreManager = KupolaStoreManager;
1337
- window.kupolaData = kupolaData;
1338
- window.kupolaEvents = kupolaEvents;
1339
- window.kupolaStoreManager = kupolaStoreManager;
1340
- window.createStore = createStore;
1341
- window.getStore = getStore;
1342
- window.kupolaRef = ref;
1343
- }
package/js/datepicker.js CHANGED
@@ -1,3 +1,6 @@
1
+ import { globalEvents } from './global-events.js';
2
+ import { kupolaInitializer } from './initializer.js';
3
+
1
4
  class Datepicker {
2
5
  constructor(element, options = {}) {
3
6
  this.element = element;
@@ -35,6 +38,7 @@ class Datepicker {
35
38
 
36
39
  this._iconClickHandler = null;
37
40
  this._inputClickHandler = null;
41
+ this._endInputClickHandler = null;
38
42
  this._documentClickHandler = null;
39
43
  this._documentClickListener = null;
40
44
  this._resizeHandler = null;
@@ -70,21 +74,17 @@ class Datepicker {
70
74
 
71
75
  if (this.icon) this.icon.addEventListener('click', this._iconClickHandler);
72
76
  if (this.input) this.input.addEventListener('click', this._inputClickHandler);
73
- if (this.endInput) this.endInput.addEventListener('click', (e) => {
74
- this.isSelectingEnd = true;
75
- this.toggleCalendar(e);
76
- });
77
-
78
- if (window.globalEvents) {
79
- this._documentClickListener = window.globalEvents.on(document, 'click', (e) => this.hideCalendar(e), { scope: this.scope });
80
- this._resizeListener = window.globalEvents.on(window, 'resize', () => this.resizeHandler(), { scope: this.scope });
81
- } else {
82
- document.addEventListener('click', (e) => this.hideCalendar(e));
83
- window.addEventListener('resize', () => this.resizeHandler());
84
- this._documentClickHandler = (e) => this.hideCalendar(e);
85
- this._resizeHandler = () => this.resizeHandler();
77
+ if (this.endInput) {
78
+ this._endInputClickHandler = (e) => {
79
+ this.isSelectingEnd = true;
80
+ this.toggleCalendar(e);
81
+ };
82
+ this.endInput.addEventListener('click', this._endInputClickHandler);
86
83
  }
87
84
 
85
+ this._documentClickListener = globalEvents.on(document, 'click', (e) => this.hideCalendar(e), { scope: this.scope });
86
+ this._resizeListener = globalEvents.on(window, 'resize', () => this.resizeHandler(), { scope: this.scope });
87
+
88
88
  // Keyboard
89
89
  this._keydownHandler = (e) => {
90
90
  if (e.key === 'Escape' && this.calendarEl.style.display === 'block') {
@@ -577,6 +577,7 @@ class Datepicker {
577
577
 
578
578
  if (this.icon && this._iconClickHandler) this.icon.removeEventListener('click', this._iconClickHandler);
579
579
  if (this.input && this._inputClickHandler) this.input.removeEventListener('click', this._inputClickHandler);
580
+ if (this.endInput && this._endInputClickHandler) this.endInput.removeEventListener('click', this._endInputClickHandler);
580
581
  if (this._keydownHandler) document.removeEventListener('keydown', this._keydownHandler);
581
582
 
582
583
  if (this._documentClickListener && this._documentClickListener.unsubscribe) {
@@ -603,6 +604,7 @@ class Datepicker {
603
604
  this._resizeListener = null;
604
605
  this._iconClickHandler = null;
605
606
  this._inputClickHandler = null;
607
+ this._endInputClickHandler = null;
606
608
  this._keydownHandler = null;
607
609
  this.element.__kupolaInitialized = false;
608
610
  }
@@ -635,14 +637,4 @@ function cleanupAllDatepickers() {
635
637
 
636
638
  export { Datepicker, initDatepicker, initDatepickers, cleanupDatepicker, cleanupAllDatepickers };
637
639
 
638
- if (typeof window !== 'undefined') {
639
- window.Datepicker = Datepicker;
640
- window.initDatepicker = initDatepicker;
641
- window.initDatepickers = initDatepickers;
642
- window.cleanupDatepicker = cleanupDatepicker;
643
- window.cleanupAllDatepickers = cleanupAllDatepickers;
644
-
645
- if (window.kupolaInitializer) {
646
- window.kupolaInitializer.register('datepicker', initDatepicker, cleanupDatepicker);
647
- }
648
- }
640
+ kupolaInitializer.register('datepicker', initDatepicker, cleanupDatepicker);
package/js/depends.js CHANGED
@@ -8,6 +8,8 @@ class Scheduler {
8
8
  constructor() {
9
9
  this._queue = new Set();
10
10
  this._scheduled = false;
11
+ this._flushDepth = 0;
12
+ this._maxDepth = 10;
11
13
  }
12
14
 
13
15
  schedule(fn) {
@@ -19,9 +21,16 @@ class Scheduler {
19
21
  }
20
22
 
21
23
  _flush() {
24
+ if (this._flushDepth >= this._maxDepth) {
25
+ console.warn('[Kupola Scheduler] Max flush depth reached, possible infinite loop detected');
26
+ this._queue.clear();
27
+ this._scheduled = false;
28
+ return;
29
+ }
22
30
  const tasks = Array.from(this._queue);
23
31
  this._queue.clear();
24
32
  this._scheduled = false;
33
+ this._flushDepth++;
25
34
  // Deduplicate: same function only runs once
26
35
  const seen = new Set();
27
36
  for (const task of tasks) {
@@ -30,6 +39,7 @@ class Scheduler {
30
39
  try { task(); } catch (e) { console.error('[DependsScheduler]', e); }
31
40
  }
32
41
  }
42
+ this._flushDepth--;
33
43
  }
34
44
  }
35
45
 
@@ -216,7 +226,10 @@ class DependsSource {
216
226
  return result;
217
227
  } catch (error) {
218
228
  if (attempt < this.retryCount) {
219
- const delay = this.retryDelay * Math.pow(2, attempt);
229
+ // Exponential backoff + random jitter to prevent thundering herd
230
+ const baseDelay = this.retryDelay * Math.pow(2, attempt);
231
+ const jitter = Math.random() * baseDelay * 0.5;
232
+ const delay = baseDelay + jitter;
220
233
  await new Promise(resolve => setTimeout(resolve, delay));
221
234
  return this._fetchWithRetry(params, attempt + 1);
222
235
  }
@@ -400,8 +413,11 @@ class WebSocketSource extends DependsSource {
400
413
  this.ws = null;
401
414
  this.reconnect = config.reconnect !== false;
402
415
  this.reconnectDelay = config.reconnectDelay || 3000;
416
+ this._reconnectAttempt = 0;
417
+ this._maxReconnectDelay = config.maxReconnectDelay || 30000;
403
418
  this.messageHandler = null;
404
419
  this._connected = false;
420
+ this._destroyed = false;
405
421
  }
406
422
 
407
423
  async fetch() {
@@ -411,6 +427,7 @@ class WebSocketSource extends DependsSource {
411
427
 
412
428
  this.ws.onopen = () => {
413
429
  this._connected = true;
430
+ this._reconnectAttempt = 0; // Reset on successful connection
414
431
  // Resolve with current cached data or null
415
432
  resolve(this.cache.getStale(this.cacheKey));
416
433
  };
@@ -432,10 +449,15 @@ class WebSocketSource extends DependsSource {
432
449
 
433
450
  this.ws.onclose = () => {
434
451
  this._connected = false;
435
- if (this.reconnect) {
452
+ if (this.reconnect && !this._destroyed) {
453
+ // Exponential backoff + jitter for reconnection
454
+ const baseDelay = this.reconnectDelay * Math.pow(2, this._reconnectAttempt);
455
+ const jitter = Math.random() * baseDelay * 0.3;
456
+ const delay = Math.min(baseDelay + jitter, this._maxReconnectDelay);
457
+ this._reconnectAttempt++;
436
458
  setTimeout(() => {
437
- this.fetch().catch(() => {});
438
- }, this.reconnectDelay);
459
+ if (!this._destroyed) this.fetch().catch(() => {});
460
+ }, delay);
439
461
  }
440
462
  };
441
463
  } catch (e) {
@@ -451,6 +473,7 @@ class WebSocketSource extends DependsSource {
451
473
  }
452
474
 
453
475
  destroy() {
476
+ this._destroyed = true;
454
477
  super.destroy();
455
478
  if (this.ws) {
456
479
  this.ws.onmessage = null;
@@ -696,15 +719,3 @@ export {
696
719
  };
697
720
 
698
721
  // HTTP Client Plugin System exports are above (configureHttpClient, getHttpClient, resetHttpClient)
699
-
700
- if (typeof window !== 'undefined') {
701
- window.useDeps = useDeps;
702
- window.useQuery = useQuery;
703
- window.clearCache = clearCache;
704
- window.DependsError = DependsError;
705
- window.CacheManager = CacheManager;
706
- window.Scheduler = Scheduler;
707
- window.configureHttpClient = configureHttpClient;
708
- window.getHttpClient = getHttpClient;
709
- window.resetHttpClient = resetHttpClient;
710
- }
package/js/dialog.js CHANGED
@@ -126,8 +126,4 @@ class Dialog {
126
126
  }
127
127
  }
128
128
 
129
- export { Dialog };
130
-
131
- if (typeof window !== 'undefined') {
132
- window.Dialog = Dialog;
133
- }
129
+ export { Dialog };
package/js/drawer.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { kupolaInitializer } from './initializer.js';
2
+
1
3
  class Drawer {
2
4
  constructor(element, options = {}) {
3
5
  this.element = element;
@@ -269,14 +271,4 @@ function initDrawers() {
269
271
 
270
272
  export { Drawer, initDrawer, initDrawers, cleanupDrawer, createDrawer };
271
273
 
272
- if (typeof window !== 'undefined') {
273
- window.Drawer = Drawer;
274
- window.initDrawer = initDrawer;
275
- window.initDrawers = initDrawers;
276
- window.cleanupDrawer = cleanupDrawer;
277
- window.createDrawer = createDrawer;
278
-
279
- if (window.kupolaInitializer) {
280
- window.kupolaInitializer.register('drawer', initDrawer, cleanupDrawer);
281
- }
282
- }
274
+ kupolaInitializer.register('drawer', initDrawer, cleanupDrawer);