@framesquared/core 0.1.0

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/index.js ADDED
@@ -0,0 +1,3108 @@
1
+ // src/util/index.ts
2
+ function identity(value) {
3
+ return value;
4
+ }
5
+ function emptyFn() {
6
+ }
7
+ function applyIf(target, source) {
8
+ const keys2 = Object.keys(source);
9
+ for (const key of keys2) {
10
+ if (!(key in target)) {
11
+ target[key] = source[key];
12
+ }
13
+ }
14
+ return target;
15
+ }
16
+ function apply(target, ...sources) {
17
+ for (const source of sources) {
18
+ const keys2 = Object.keys(source);
19
+ for (const key of keys2) {
20
+ target[key] = source[key];
21
+ }
22
+ }
23
+ return target;
24
+ }
25
+ function clone(value) {
26
+ return structuredClone(value);
27
+ }
28
+ function isObject(value) {
29
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
30
+ return false;
31
+ }
32
+ const proto = Object.getPrototypeOf(value);
33
+ return proto === Object.prototype || proto === null;
34
+ }
35
+ function isString(value) {
36
+ return typeof value === "string";
37
+ }
38
+ function isNumber(value) {
39
+ return typeof value === "number" && !Number.isNaN(value);
40
+ }
41
+ function isBoolean(value) {
42
+ return typeof value === "boolean";
43
+ }
44
+ function isFunction(value) {
45
+ return typeof value === "function";
46
+ }
47
+ function isArray(value) {
48
+ return Array.isArray(value);
49
+ }
50
+ function isDefined(value) {
51
+ return value !== void 0 && value !== null;
52
+ }
53
+ function isEmpty(value) {
54
+ if (value === void 0 || value === null) return true;
55
+ if (typeof value === "number") return Number.isNaN(value);
56
+ if (typeof value === "string") return value.length === 0;
57
+ if (Array.isArray(value)) return value.length === 0;
58
+ if (isObject(value)) return Object.keys(value).length === 0;
59
+ return false;
60
+ }
61
+ function isIterable(value) {
62
+ if (value === null || value === void 0) return false;
63
+ return typeof value[Symbol.iterator] === "function";
64
+ }
65
+ function isPrimitive(value) {
66
+ if (value === null) return true;
67
+ const t = typeof value;
68
+ return t !== "object" && t !== "function";
69
+ }
70
+ function namespace(root, path) {
71
+ const segments = path.split(".");
72
+ let current = root;
73
+ for (const segment of segments) {
74
+ if (current[segment] === void 0 || current[segment] === null) {
75
+ current[segment] = {};
76
+ }
77
+ current = current[segment];
78
+ }
79
+ return current;
80
+ }
81
+ function defer(fn, millis = 0) {
82
+ return +setTimeout(fn, millis);
83
+ }
84
+ function interval(fn, millis) {
85
+ return +setInterval(fn, millis);
86
+ }
87
+ function now() {
88
+ return performance.now();
89
+ }
90
+ var idCounter = 0;
91
+ function generateId(prefix = "framesquared") {
92
+ return `${prefix}-${++idCounter}`;
93
+ }
94
+
95
+ // src/util/String.ts
96
+ function capitalize(str) {
97
+ if (str.length === 0) return str;
98
+ return str[0].toUpperCase() + str.slice(1);
99
+ }
100
+ function uncapitalize(str) {
101
+ if (str.length === 0) return str;
102
+ return str[0].toLowerCase() + str.slice(1);
103
+ }
104
+ function ellipsis(value, length, word) {
105
+ if (value.length <= length) return value;
106
+ if (length <= 3) return "...";
107
+ const truncLen = length - 3;
108
+ if (word) {
109
+ const lastSpace = value.lastIndexOf(" ", truncLen);
110
+ if (lastSpace > 0) {
111
+ return value.slice(0, lastSpace) + "...";
112
+ }
113
+ }
114
+ return value.slice(0, truncLen) + "...";
115
+ }
116
+ var HTML_ESCAPE_MAP = {
117
+ "&": "&amp;",
118
+ "<": "&lt;",
119
+ ">": "&gt;",
120
+ '"': "&quot;",
121
+ "'": "&#39;"
122
+ };
123
+ var HTML_UNESCAPE_MAP = {
124
+ "&amp;": "&",
125
+ "&lt;": "<",
126
+ "&gt;": ">",
127
+ "&quot;": '"',
128
+ "&#39;": "'"
129
+ };
130
+ var HTML_ESCAPE_RE = /[&<>"']/g;
131
+ var HTML_UNESCAPE_RE = /&amp;|&lt;|&gt;|&quot;|&#39;/g;
132
+ function escapeHtml(str) {
133
+ return str.replace(HTML_ESCAPE_RE, (ch) => HTML_ESCAPE_MAP[ch]);
134
+ }
135
+ function unescapeHtml(str) {
136
+ return str.replace(HTML_UNESCAPE_RE, (entity) => HTML_UNESCAPE_MAP[entity]);
137
+ }
138
+ var htmlEncode = escapeHtml;
139
+ var htmlDecode = unescapeHtml;
140
+ var REGEX_SPECIAL_RE = /[.*+?^${}()|[\]\\]/g;
141
+ function escapeRegex(str) {
142
+ return str.replace(REGEX_SPECIAL_RE, "\\$&");
143
+ }
144
+ function format(template, ...values2) {
145
+ return template.replace(/\{(\d+)}/g, (match, index) => {
146
+ const i = Number(index);
147
+ return i < values2.length ? String(values2[i]) : match;
148
+ });
149
+ }
150
+ function repeat(str, count) {
151
+ return str.repeat(count);
152
+ }
153
+ function trim(str) {
154
+ return str.trim();
155
+ }
156
+ function leftPad(str, size, char = " ") {
157
+ const padChar = char[0];
158
+ const needed = size - str.length;
159
+ if (needed <= 0) return str;
160
+ return padChar.repeat(needed) + str;
161
+ }
162
+ function toggle(str, value1, value2) {
163
+ return str === value1 ? value2 : value1;
164
+ }
165
+ function splitWords(str) {
166
+ if (str.length === 0) return [];
167
+ const withBoundaries = str.replace(/([a-z0-9])([A-Z])/g, "$1\0$2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1\0$2");
168
+ return withBoundaries.split(/[\0_-]+/).filter(Boolean);
169
+ }
170
+ function camelCase(str) {
171
+ const words = splitWords(str);
172
+ if (words.length === 0) return "";
173
+ return words.map((w, i) => {
174
+ const lower = w.toLowerCase();
175
+ return i === 0 ? lower : capitalize(lower);
176
+ }).join("");
177
+ }
178
+ function kebabCase(str) {
179
+ const words = splitWords(str);
180
+ return words.map((w) => w.toLowerCase()).join("-");
181
+ }
182
+ function pascalCase(str) {
183
+ const words = splitWords(str);
184
+ return words.map((w) => capitalize(w.toLowerCase())).join("");
185
+ }
186
+
187
+ // src/util/Array.ts
188
+ function from(value) {
189
+ if (Array.isArray(value)) return value;
190
+ if (typeof value !== "string" && value != null && typeof value[Symbol.iterator] === "function") {
191
+ return [...value];
192
+ }
193
+ return [value];
194
+ }
195
+ function contains(array, item) {
196
+ return array.includes(item);
197
+ }
198
+ function include(array, item) {
199
+ if (!array.includes(item)) {
200
+ array.push(item);
201
+ }
202
+ return array;
203
+ }
204
+ function remove(array, item) {
205
+ const idx = array.indexOf(item);
206
+ if (idx !== -1) {
207
+ array.splice(idx, 1);
208
+ }
209
+ return array;
210
+ }
211
+ function clean(array) {
212
+ return array.filter((v) => v !== null && v !== void 0);
213
+ }
214
+ function unique(array) {
215
+ return [...new Set(array)];
216
+ }
217
+ function flatten(array) {
218
+ const result = [];
219
+ for (const item of array) {
220
+ if (Array.isArray(item)) {
221
+ result.push(...item);
222
+ } else {
223
+ result.push(item);
224
+ }
225
+ }
226
+ return result;
227
+ }
228
+ function pluck(array, key) {
229
+ return array.map((item) => item[key]);
230
+ }
231
+ function sum(array) {
232
+ let total = 0;
233
+ for (const n of array) total += n;
234
+ return total;
235
+ }
236
+ function mean(array) {
237
+ if (array.length === 0) return NaN;
238
+ return sum(array) / array.length;
239
+ }
240
+ function min(array, compareFn) {
241
+ if (array.length === 0) throw new Error("min() requires a non-empty array");
242
+ const cmp = compareFn ?? defaultCompare;
243
+ let result = array[0];
244
+ for (let i = 1; i < array.length; i++) {
245
+ if (cmp(array[i], result) < 0) result = array[i];
246
+ }
247
+ return result;
248
+ }
249
+ function max(array, compareFn) {
250
+ if (array.length === 0) throw new Error("max() requires a non-empty array");
251
+ const cmp = compareFn ?? defaultCompare;
252
+ let result = array[0];
253
+ for (let i = 1; i < array.length; i++) {
254
+ if (cmp(array[i], result) > 0) result = array[i];
255
+ }
256
+ return result;
257
+ }
258
+ function defaultCompare(a, b) {
259
+ if (a < b) return -1;
260
+ if (a > b) return 1;
261
+ return 0;
262
+ }
263
+ function groupBy(array, fn) {
264
+ const result = {};
265
+ for (const item of array) {
266
+ const key = fn(item);
267
+ if (result[key] === void 0) {
268
+ result[key] = [];
269
+ }
270
+ result[key].push(item);
271
+ }
272
+ return result;
273
+ }
274
+ function partition(array, predicate) {
275
+ const yes = [];
276
+ const no = [];
277
+ for (const item of array) {
278
+ (predicate(item) ? yes : no).push(item);
279
+ }
280
+ return [yes, no];
281
+ }
282
+ function chunk(array, size) {
283
+ if (size <= 0) throw new Error("chunk() requires a positive size");
284
+ const result = [];
285
+ for (let i = 0; i < array.length; i += size) {
286
+ result.push(array.slice(i, i + size));
287
+ }
288
+ return result;
289
+ }
290
+ function difference(array1, array2) {
291
+ const set = new Set(array2);
292
+ return array1.filter((item) => !set.has(item));
293
+ }
294
+ function intersection(array1, array2) {
295
+ const set = new Set(array2);
296
+ return unique(array1.filter((item) => set.has(item)));
297
+ }
298
+ function sortBy(array, key, direction = "ASC") {
299
+ const accessor = typeof key === "function" ? key : (item) => item[key];
300
+ const dir = direction === "DESC" ? -1 : 1;
301
+ return [...array].sort((a, b) => {
302
+ const va = accessor(a);
303
+ const vb = accessor(b);
304
+ if (va < vb) return -1 * dir;
305
+ if (va > vb) return 1 * dir;
306
+ return 0;
307
+ });
308
+ }
309
+ function findBy(array, fn) {
310
+ return array.find(fn);
311
+ }
312
+ function range(start, end, step = 1) {
313
+ if (step === 0) throw new Error("range() step must not be zero");
314
+ const result = [];
315
+ if (step > 0) {
316
+ for (let i = start; i < end; i += step) result.push(i);
317
+ } else {
318
+ for (let i = start; i > end; i += step) result.push(i);
319
+ }
320
+ return result;
321
+ }
322
+
323
+ // src/util/Object.ts
324
+ function keys(obj) {
325
+ return Object.keys(obj);
326
+ }
327
+ function values(obj) {
328
+ return Object.values(obj);
329
+ }
330
+ function entries(obj) {
331
+ return Object.entries(obj);
332
+ }
333
+ function fromEntries(items) {
334
+ return Object.fromEntries(items);
335
+ }
336
+ function isPlainObject(value) {
337
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
338
+ return false;
339
+ }
340
+ const proto = Object.getPrototypeOf(value);
341
+ return proto === Object.prototype || proto === null;
342
+ }
343
+ function merge(target, ...sources) {
344
+ for (const source of sources) {
345
+ for (const key of Object.keys(source)) {
346
+ const srcVal = source[key];
347
+ const tgtVal = target[key];
348
+ if (isPlainObject(srcVal) && isPlainObject(tgtVal)) {
349
+ merge(tgtVal, srcVal);
350
+ } else {
351
+ target[key] = srcVal;
352
+ }
353
+ }
354
+ }
355
+ return target;
356
+ }
357
+ function pick(obj, pickedKeys) {
358
+ const result = {};
359
+ for (const key of pickedKeys) {
360
+ if (key in obj) {
361
+ result[key] = obj[key];
362
+ }
363
+ }
364
+ return result;
365
+ }
366
+ function omit(obj, omittedKeys) {
367
+ const excluded = new Set(omittedKeys);
368
+ const result = {};
369
+ for (const key of Object.keys(obj)) {
370
+ if (!excluded.has(key)) {
371
+ result[key] = obj[key];
372
+ }
373
+ }
374
+ return result;
375
+ }
376
+ function mapValues(obj, fn) {
377
+ const result = {};
378
+ for (const key of Object.keys(obj)) {
379
+ result[key] = fn(obj[key], key);
380
+ }
381
+ return result;
382
+ }
383
+ function mapKeys(obj, fn) {
384
+ const result = {};
385
+ for (const key of Object.keys(obj)) {
386
+ result[fn(key)] = obj[key];
387
+ }
388
+ return result;
389
+ }
390
+ function freeze(obj) {
391
+ Object.freeze(obj);
392
+ for (const value of Object.values(obj)) {
393
+ if (value !== null && typeof value === "object" && !Object.isFrozen(value)) {
394
+ freeze(value);
395
+ }
396
+ }
397
+ return obj;
398
+ }
399
+ function equals(a, b) {
400
+ if (a === b) return true;
401
+ if (typeof a === "number" && typeof b === "number") {
402
+ return Number.isNaN(a) && Number.isNaN(b);
403
+ }
404
+ if (a === null || b === null || a === void 0 || b === void 0) {
405
+ return false;
406
+ }
407
+ if (typeof a !== typeof b) return false;
408
+ if (typeof a !== "object") return false;
409
+ if (a instanceof Date && b instanceof Date) {
410
+ return a.getTime() === b.getTime();
411
+ }
412
+ if (a instanceof RegExp && b instanceof RegExp) {
413
+ return a.source === b.source && a.flags === b.flags;
414
+ }
415
+ const aIsArr = Array.isArray(a);
416
+ const bIsArr = Array.isArray(b);
417
+ if (aIsArr !== bIsArr) return false;
418
+ if (aIsArr && bIsArr) {
419
+ const arrA = a;
420
+ const arrB = b;
421
+ if (arrA.length !== arrB.length) return false;
422
+ for (let i = 0; i < arrA.length; i++) {
423
+ if (!equals(arrA[i], arrB[i])) return false;
424
+ }
425
+ return true;
426
+ }
427
+ const objA = a;
428
+ const objB = b;
429
+ const keysA = Object.keys(objA);
430
+ const keysB = Object.keys(objB);
431
+ if (keysA.length !== keysB.length) return false;
432
+ for (const key of keysA) {
433
+ if (!Object.prototype.hasOwnProperty.call(objB, key)) return false;
434
+ if (!equals(objA[key], objB[key])) return false;
435
+ }
436
+ return true;
437
+ }
438
+ function getNestedValue(obj, path, defaultValue) {
439
+ const segments = path.split(".");
440
+ let current = obj;
441
+ for (let i = 0; i < segments.length; i++) {
442
+ if (current === null || current === void 0 || typeof current !== "object") {
443
+ return defaultValue;
444
+ }
445
+ const rec = current;
446
+ const seg = segments[i];
447
+ if (i === segments.length - 1) {
448
+ return seg in rec ? rec[seg] : defaultValue;
449
+ }
450
+ current = rec[seg];
451
+ }
452
+ return current;
453
+ }
454
+ function setNestedValue(obj, path, value) {
455
+ const segments = path.split(".");
456
+ let current = obj;
457
+ for (let i = 0; i < segments.length - 1; i++) {
458
+ const seg = segments[i];
459
+ if (current[seg] === void 0 || current[seg] === null || typeof current[seg] !== "object") {
460
+ current[seg] = {};
461
+ }
462
+ current = current[seg];
463
+ }
464
+ current[segments[segments.length - 1]] = value;
465
+ }
466
+ function flattenObject(obj, prefix) {
467
+ const result = {};
468
+ for (const [key, value] of Object.entries(obj)) {
469
+ const fullKey = prefix ? `${prefix}.${key}` : key;
470
+ if (isPlainObject(value)) {
471
+ Object.assign(result, flattenObject(value, fullKey));
472
+ } else {
473
+ result[fullKey] = value;
474
+ }
475
+ }
476
+ return result;
477
+ }
478
+ function unflattenObject(obj) {
479
+ const result = {};
480
+ for (const [path, value] of Object.entries(obj)) {
481
+ setNestedValue(result, path, value);
482
+ }
483
+ return result;
484
+ }
485
+
486
+ // src/util/Function.ts
487
+ function bind(fn, scope, ...args) {
488
+ return ((...callArgs) => fn.apply(scope, [...args, ...callArgs]));
489
+ }
490
+ function createBuffered(fn, buffer, scope) {
491
+ let timerId;
492
+ return function(...args) {
493
+ if (timerId !== void 0) clearTimeout(timerId);
494
+ const ctx = scope ?? this;
495
+ timerId = setTimeout(() => {
496
+ fn.apply(ctx, args);
497
+ timerId = void 0;
498
+ }, buffer);
499
+ };
500
+ }
501
+ function createDelayed(fn, delay, scope) {
502
+ return function(...args) {
503
+ const ctx = scope ?? this;
504
+ setTimeout(() => fn.apply(ctx, args), delay);
505
+ };
506
+ }
507
+ function createThrottled(fn, intervalMs, scope) {
508
+ let lastCall = 0;
509
+ return function(...args) {
510
+ const ctx = scope ?? this;
511
+ const elapsed = Date.now() - lastCall;
512
+ if (elapsed >= intervalMs) {
513
+ lastCall = Date.now();
514
+ fn.apply(ctx, args);
515
+ }
516
+ };
517
+ }
518
+ function createBarrier(count, fn, scope) {
519
+ let remaining = count;
520
+ let fired = false;
521
+ return function(...args) {
522
+ if (fired) return;
523
+ remaining--;
524
+ if (remaining <= 0) {
525
+ fired = true;
526
+ const ctx = scope ?? this;
527
+ fn.apply(ctx, args);
528
+ }
529
+ };
530
+ }
531
+ function interceptBefore(obj, methodName, fn) {
532
+ const rec = obj;
533
+ const original = rec[methodName];
534
+ rec[methodName] = function(...args) {
535
+ fn.apply(this, args);
536
+ return original.apply(this, args);
537
+ };
538
+ }
539
+ function interceptAfter(obj, methodName, fn) {
540
+ const rec = obj;
541
+ const original = rec[methodName];
542
+ rec[methodName] = function(...args) {
543
+ const result = original.apply(this, args);
544
+ fn.call(this, result);
545
+ return result;
546
+ };
547
+ }
548
+ function createSequence(...fns) {
549
+ return (...args) => {
550
+ for (const fn of fns) {
551
+ fn(...args);
552
+ }
553
+ };
554
+ }
555
+ function memoize(fn, hasher) {
556
+ const cache = /* @__PURE__ */ new Map();
557
+ return ((...args) => {
558
+ const key = hasher ? hasher(...args) : String(args[0]);
559
+ if (cache.has(key)) return cache.get(key);
560
+ const result = fn(...args);
561
+ cache.set(key, result);
562
+ return result;
563
+ });
564
+ }
565
+ function once(fn) {
566
+ let called = false;
567
+ let result;
568
+ return ((...args) => {
569
+ if (!called) {
570
+ called = true;
571
+ result = fn(...args);
572
+ }
573
+ return result;
574
+ });
575
+ }
576
+ function negate(fn) {
577
+ return ((...args) => !fn(...args));
578
+ }
579
+ function compose(...fns) {
580
+ return (...args) => {
581
+ let result = fns[fns.length - 1](...args);
582
+ for (let i = fns.length - 2; i >= 0; i--) {
583
+ result = fns[i](result);
584
+ }
585
+ return result;
586
+ };
587
+ }
588
+ function pipe(...fns) {
589
+ return (...args) => {
590
+ let result = fns[0](...args);
591
+ for (let i = 1; i < fns.length; i++) {
592
+ result = fns[i](result);
593
+ }
594
+ return result;
595
+ };
596
+ }
597
+
598
+ // src/class/Base.ts
599
+ var CONFIGS_PROCESSED = /* @__PURE__ */ Symbol("configsProcessed");
600
+ function processClassConfigs(Ctor) {
601
+ if (Object.hasOwn(Ctor, CONFIGS_PROCESSED)) return;
602
+ const chain = [];
603
+ let cur = Ctor;
604
+ while (cur && cur !== Function.prototype) {
605
+ chain.unshift(cur);
606
+ cur = Object.getPrototypeOf(cur);
607
+ }
608
+ const merged = {};
609
+ for (const cls of chain) {
610
+ if (cls.hasOwnProperty("$configDefs")) {
611
+ Object.assign(merged, cls.$configDefs);
612
+ }
613
+ }
614
+ Ctor.$configDefs = merged;
615
+ for (const configName of Object.keys(merged)) {
616
+ generateAccessors(Ctor.prototype, configName);
617
+ }
618
+ Ctor[CONFIGS_PROCESSED] = true;
619
+ }
620
+ function generateAccessors(proto, configName) {
621
+ const cap = capitalize(configName);
622
+ const getterName = `get${cap}`;
623
+ const setterName = `set${cap}`;
624
+ const privateProp = `_${configName}`;
625
+ const applyName = `apply${cap}`;
626
+ const updateName = `update${cap}`;
627
+ if (!(getterName in proto)) {
628
+ proto[getterName] = function() {
629
+ if (!this.$configInitialized.has(configName)) {
630
+ const defaults = this.constructor.$configDefs;
631
+ if (defaults && configName in defaults) {
632
+ this[setterName](defaults[configName]);
633
+ }
634
+ }
635
+ return this[privateProp];
636
+ };
637
+ }
638
+ if (!(setterName in proto)) {
639
+ proto[setterName] = function(value) {
640
+ const oldValue = this[privateProp];
641
+ if (typeof this[applyName] === "function") {
642
+ value = this[applyName](value, oldValue);
643
+ }
644
+ this[privateProp] = value;
645
+ this.$configInitialized.add(configName);
646
+ if (typeof this[updateName] === "function" && value !== oldValue) {
647
+ this[updateName](value, oldValue);
648
+ }
649
+ };
650
+ }
651
+ }
652
+ function wrapMethod(fn, methodName, ownerClass) {
653
+ const wrapped = function(...args) {
654
+ this.$callStack.push({ methodName, owner: ownerClass });
655
+ try {
656
+ return fn.apply(this, args);
657
+ } finally {
658
+ this.$callStack.pop();
659
+ }
660
+ };
661
+ wrapped.$wrapped = true;
662
+ wrapped.$original = fn;
663
+ wrapped.$owner = ownerClass;
664
+ return wrapped;
665
+ }
666
+ var Base = class {
667
+ // -- Class-level metadata ------------------------------------------------
668
+ /** Fully-qualified class name. */
669
+ static $className = "Base";
670
+ /** Merged config defaults for this class (populated by processClassConfigs). */
671
+ static $configDefs;
672
+ /** Set of mixin constructors applied to this class (populated by define). */
673
+ static $mixins;
674
+ // -- Instance state ------------------------------------------------------
675
+ /** Per-instance call-stack used by `callParent()`. */
676
+ $callStack = [];
677
+ /** Tracks which config properties have been explicitly initialized. */
678
+ $configInitialized = /* @__PURE__ */ new Set();
679
+ /**
680
+ * Raw config object passed to the constructor. Decorator-based configs
681
+ * are consumed from here by `addInitializer` callbacks (which run after
682
+ * auto-accessor field initialization).
683
+ */
684
+ $pendingConfig;
685
+ /** Whether `destroy()` has been called. */
686
+ isDestroyed = false;
687
+ /**
688
+ * Array of cleanup callbacks. Mixins (and user code) can push
689
+ * functions here; they all run during `destroy()`.
690
+ */
691
+ $destroyHooks = [];
692
+ // -- Getters -------------------------------------------------------------
693
+ /** The fully-qualified class name of this instance's constructor. */
694
+ get $className() {
695
+ return this.constructor.$className;
696
+ }
697
+ /** Reference to the constructor (like ExtJS's `this.self`). */
698
+ get self() {
699
+ return this.constructor;
700
+ }
701
+ // -- Constructor ---------------------------------------------------------
702
+ constructor(config2) {
703
+ this.$pendingConfig = config2 ? { ...config2 } : void 0;
704
+ if (config2) {
705
+ this.initConfig(config2);
706
+ }
707
+ }
708
+ // -- Factory -------------------------------------------------------------
709
+ /**
710
+ * Static factory — creates an instance of the class.
711
+ */
712
+ static create(...args) {
713
+ return new this(...args);
714
+ }
715
+ // -- Config initialisation -----------------------------------------------
716
+ /**
717
+ * Applies the given config properties to this instance. Ensures that
718
+ * config accessors have been generated for the class first.
719
+ */
720
+ initConfig(config2) {
721
+ const Ctor = this.constructor;
722
+ processClassConfigs(Ctor);
723
+ if (!config2) return;
724
+ const defs = Ctor.$configDefs;
725
+ for (const key of Object.keys(config2)) {
726
+ if (defs && key in defs) {
727
+ const setter = `set${capitalize(key)}`;
728
+ if (typeof this[setter] === "function") {
729
+ this[setter](config2[key]);
730
+ }
731
+ }
732
+ }
733
+ }
734
+ // -- callParent ----------------------------------------------------------
735
+ /**
736
+ * Calls the parent (super) class's implementation of the method that is
737
+ * currently executing. Only works for methods wrapped via `define()`.
738
+ */
739
+ callParent(args) {
740
+ const stack = this.$callStack;
741
+ if (stack.length === 0) {
742
+ return void 0;
743
+ }
744
+ const current = stack[stack.length - 1];
745
+ const parentProto = Object.getPrototypeOf(current.owner.prototype);
746
+ if (parentProto && typeof parentProto[current.methodName] === "function") {
747
+ return parentProto[current.methodName].apply(this, args ?? []);
748
+ }
749
+ return void 0;
750
+ }
751
+ // -- Mixin query ---------------------------------------------------------
752
+ /**
753
+ * Returns `true` if the given mixin has been applied to this class (or
754
+ * to any of its ancestors / other mixins).
755
+ */
756
+ hasMixin(mixin2) {
757
+ const Ctor = this.constructor;
758
+ return Ctor.$mixins?.has(mixin2) ?? false;
759
+ }
760
+ // -- Destroy lifecycle ---------------------------------------------------
761
+ /**
762
+ * Tears down this instance. Calls `onDestroy()` if defined.
763
+ * Idempotent — second and subsequent calls are no-ops.
764
+ */
765
+ destroy() {
766
+ if (this.isDestroyed) return;
767
+ this.isDestroyed = true;
768
+ for (const hook of this.$destroyHooks) {
769
+ hook.call(this);
770
+ }
771
+ if (typeof this.onDestroy === "function") {
772
+ this.onDestroy();
773
+ }
774
+ }
775
+ /**
776
+ * `Symbol.dispose` support — calling `[Symbol.dispose]()` is equivalent
777
+ * to `destroy()`, enabling the TC39 `using` keyword.
778
+ */
779
+ [Symbol.dispose]() {
780
+ this.destroy();
781
+ }
782
+ };
783
+
784
+ // src/class/ClassManager.ts
785
+ var RESERVED_DEF_KEYS = /* @__PURE__ */ new Set([
786
+ "extend",
787
+ "mixins",
788
+ "alias",
789
+ "config",
790
+ "statics"
791
+ ]);
792
+ var ClassManagerImpl = class {
793
+ /** className → constructor */
794
+ classes = /* @__PURE__ */ new Map();
795
+ /** alias → constructor */
796
+ aliases = /* @__PURE__ */ new Map();
797
+ // -- Registration -------------------------------------------------------
798
+ register(name, cls) {
799
+ this.classes.set(name, cls);
800
+ }
801
+ // -- Lookup -------------------------------------------------------------
802
+ get(name) {
803
+ return this.classes.get(name);
804
+ }
805
+ getByAlias(alias2) {
806
+ return this.aliases.get(alias2);
807
+ }
808
+ isRegistered(name) {
809
+ return this.classes.has(name);
810
+ }
811
+ /**
812
+ * Returns all registered class names that start with `prefix`.
813
+ */
814
+ getNamesByPrefix(prefix) {
815
+ const result = [];
816
+ for (const name of this.classes.keys()) {
817
+ if (name.startsWith(prefix)) result.push(name);
818
+ }
819
+ return result;
820
+ }
821
+ // -- Instantiation ------------------------------------------------------
822
+ /**
823
+ * Creates an instance by alias, passing remaining args to the constructor.
824
+ */
825
+ instantiateByAlias(alias2, ...args) {
826
+ const Cls = this.aliases.get(alias2);
827
+ if (!Cls) {
828
+ throw new Error(`ClassManager: no class registered for alias "${alias2}"`);
829
+ }
830
+ return new Cls(...args);
831
+ }
832
+ // -- Alias management (used by define) ----------------------------------
833
+ registerAlias(alias2, cls) {
834
+ this.aliases.set(alias2, cls);
835
+ }
836
+ };
837
+ var ClassManager = new ClassManagerImpl();
838
+ function applyMixins(targetClass, mixins) {
839
+ if (!targetClass.$mixins) {
840
+ targetClass.$mixins = /* @__PURE__ */ new Set();
841
+ }
842
+ for (const mixin2 of mixins) {
843
+ if (targetClass.$mixins.has(mixin2)) continue;
844
+ targetClass.$mixins.add(mixin2);
845
+ if (mixin2.$mixins) {
846
+ for (const transitive of mixin2.$mixins) {
847
+ targetClass.$mixins.add(transitive);
848
+ }
849
+ }
850
+ for (const name of Object.getOwnPropertyNames(mixin2.prototype)) {
851
+ if (name === "constructor") continue;
852
+ if (name in targetClass.prototype) continue;
853
+ const desc = Object.getOwnPropertyDescriptor(mixin2.prototype, name);
854
+ if (desc) {
855
+ Object.defineProperty(targetClass.prototype, name, desc);
856
+ }
857
+ }
858
+ if (mixin2.$configDefs) {
859
+ targetClass.$configDefs = {
860
+ ...mixin2.$configDefs,
861
+ ...targetClass.$configDefs || {}
862
+ };
863
+ }
864
+ }
865
+ }
866
+ function define(className, definition) {
867
+ const SuperClass = definition.extend || Base;
868
+ const NewClass = class extends SuperClass {
869
+ constructor(...args) {
870
+ super(...args);
871
+ }
872
+ };
873
+ NewClass.$className = className;
874
+ if (definition.config) {
875
+ NewClass.$configDefs = {
876
+ ...SuperClass.$configDefs || {},
877
+ ...definition.config
878
+ };
879
+ } else {
880
+ if (SuperClass.$configDefs) {
881
+ NewClass.$configDefs = { ...SuperClass.$configDefs };
882
+ }
883
+ }
884
+ for (const key of Object.keys(definition)) {
885
+ if (RESERVED_DEF_KEYS.has(key)) continue;
886
+ const value = definition[key];
887
+ if (typeof value === "function") {
888
+ NewClass.prototype[key] = wrapMethod(value, key, NewClass);
889
+ } else {
890
+ NewClass.prototype[key] = value;
891
+ }
892
+ }
893
+ if (definition.mixins && definition.mixins.length > 0) {
894
+ applyMixins(NewClass, definition.mixins);
895
+ }
896
+ if (!NewClass.$mixins) {
897
+ NewClass.$mixins = /* @__PURE__ */ new Set();
898
+ }
899
+ processClassConfigs(NewClass);
900
+ if (definition.statics) {
901
+ for (const [k, v] of Object.entries(definition.statics)) {
902
+ NewClass[k] = v;
903
+ }
904
+ }
905
+ ClassManager.register(className, NewClass);
906
+ if (definition.alias) {
907
+ const aliases = Array.isArray(definition.alias) ? definition.alias : [definition.alias];
908
+ for (const a of aliases) {
909
+ ClassManager.registerAlias(a, NewClass);
910
+ }
911
+ }
912
+ return NewClass;
913
+ }
914
+
915
+ // src/class/Configurator.ts
916
+ var CONFIG_META_KEY = /* @__PURE__ */ Symbol("framesquared:configMeta");
917
+ var METADATA_SYMBOL = Symbol.metadata ?? /* @__PURE__ */ Symbol.for("Symbol.metadata");
918
+ function getOrCreateMetaMap(metadata) {
919
+ let map = metadata[CONFIG_META_KEY];
920
+ if (!map) {
921
+ map = /* @__PURE__ */ new Map();
922
+ metadata[CONFIG_META_KEY] = map;
923
+ }
924
+ return map;
925
+ }
926
+ function getClassMetadata(Ctor) {
927
+ return Ctor[METADATA_SYMBOL];
928
+ }
929
+ var ConfiguratorImpl = class {
930
+ /**
931
+ * Returns the ConfigMeta for a single config on the given class,
932
+ * walking the prototype chain.
933
+ */
934
+ getConfigMeta(Ctor, configName) {
935
+ let cur = Ctor;
936
+ while (cur) {
937
+ const md = getClassMetadata(cur);
938
+ if (md) {
939
+ const map = md[CONFIG_META_KEY];
940
+ if (map?.has(configName)) return map.get(configName);
941
+ }
942
+ cur = Object.getPrototypeOf(cur);
943
+ }
944
+ return void 0;
945
+ }
946
+ /**
947
+ * Collects all ConfigMeta entries for `Ctor`, including inherited.
948
+ */
949
+ getAllConfigMeta(Ctor) {
950
+ const chain = [];
951
+ let cur = Ctor;
952
+ while (cur) {
953
+ chain.unshift(cur);
954
+ cur = Object.getPrototypeOf(cur);
955
+ }
956
+ const merged = /* @__PURE__ */ new Map();
957
+ for (const cls of chain) {
958
+ const md = getClassMetadata(cls);
959
+ if (md) {
960
+ const map = md[CONFIG_META_KEY];
961
+ if (map) {
962
+ for (const [name, meta] of map) merged.set(name, meta);
963
+ }
964
+ }
965
+ }
966
+ return [...merged.values()];
967
+ }
968
+ };
969
+ var Configurator = new ConfiguratorImpl();
970
+
971
+ // src/class/decorators.ts
972
+ function isPlainObject2(v) {
973
+ if (v === null || typeof v !== "object" || Array.isArray(v)) return false;
974
+ const proto = Object.getPrototypeOf(v);
975
+ return proto === Object.prototype || proto === null;
976
+ }
977
+ var cachedValues = /* @__PURE__ */ new WeakMap();
978
+ function getCacheEntry(inst) {
979
+ let m = cachedValues.get(inst);
980
+ if (!m) {
981
+ m = /* @__PURE__ */ new Map();
982
+ cachedValues.set(inst, m);
983
+ }
984
+ return m;
985
+ }
986
+ function ensureAccessors(Ctor, configName) {
987
+ const proto = Ctor.prototype;
988
+ const cap = capitalize(configName);
989
+ const getter = `get${cap}`;
990
+ const setter = `set${cap}`;
991
+ if (!(getter in proto)) {
992
+ proto[getter] = function() {
993
+ return this[configName];
994
+ };
995
+ }
996
+ if (!(setter in proto)) {
997
+ proto[setter] = function(value) {
998
+ this[configName] = value;
999
+ };
1000
+ }
1001
+ }
1002
+ function createConfigDecorator(metaOverrides = {}) {
1003
+ return function(target, context) {
1004
+ const name = String(context.name);
1005
+ const cap = capitalize(name);
1006
+ const applyName = `apply${cap}`;
1007
+ const updateName = `update${cap}`;
1008
+ const metaMap = getOrCreateMetaMap(
1009
+ context.metadata
1010
+ );
1011
+ let meta = metaMap.get(name);
1012
+ if (!meta) {
1013
+ meta = {
1014
+ name,
1015
+ required: false,
1016
+ cached: false,
1017
+ merge: false,
1018
+ observable: false
1019
+ };
1020
+ metaMap.set(name, meta);
1021
+ }
1022
+ Object.assign(meta, metaOverrides);
1023
+ context.addInitializer(function() {
1024
+ const inst = this;
1025
+ const Ctor = inst.constructor;
1026
+ ensureAccessors(Ctor, name);
1027
+ if (inst.$pendingConfig && name in inst.$pendingConfig) {
1028
+ inst[name] = inst.$pendingConfig[name];
1029
+ delete inst.$pendingConfig[name];
1030
+ inst.$configInitialized?.add(name);
1031
+ }
1032
+ if (meta.required && !inst.$configInitialized?.has(name)) {
1033
+ throw new Error(
1034
+ `Config "${name}" is required for ${Ctor.$className || Ctor.name}`
1035
+ );
1036
+ }
1037
+ });
1038
+ const initFn = function(initialValue) {
1039
+ if (this.$configInitialized?.has(name)) {
1040
+ return target.get.call(this);
1041
+ }
1042
+ return initialValue;
1043
+ };
1044
+ const getFn = function() {
1045
+ const inst = this;
1046
+ if (meta.lazyFactory && !inst.$configInitialized?.has(name)) {
1047
+ const computed = meta.lazyFactory.call(inst);
1048
+ target.set.call(this, computed);
1049
+ inst.$configInitialized?.add(name);
1050
+ return computed;
1051
+ }
1052
+ if (meta.cached) {
1053
+ const cacheMap = getCacheEntry(inst);
1054
+ const entry = cacheMap.get(name);
1055
+ if (entry?.valid) {
1056
+ return entry.value;
1057
+ }
1058
+ const value = target.get.call(this);
1059
+ cacheMap.set(name, { valid: true, value });
1060
+ return value;
1061
+ }
1062
+ return target.get.call(this);
1063
+ };
1064
+ const setFn = function(value) {
1065
+ const inst = this;
1066
+ const oldValue = target.get.call(this);
1067
+ if (meta.merge && isPlainObject2(value) && isPlainObject2(oldValue)) {
1068
+ value = merge(
1069
+ {},
1070
+ oldValue,
1071
+ value
1072
+ );
1073
+ }
1074
+ if (typeof inst[applyName] === "function") {
1075
+ value = inst[applyName](value, oldValue);
1076
+ }
1077
+ target.set.call(this, value);
1078
+ if (meta.cached) {
1079
+ const cacheMap = getCacheEntry(inst);
1080
+ cacheMap.set(name, { valid: false, value: void 0 });
1081
+ }
1082
+ inst.$configInitialized?.add(name);
1083
+ if (typeof inst[updateName] === "function" && value !== oldValue) {
1084
+ inst[updateName](value, oldValue);
1085
+ }
1086
+ };
1087
+ return { init: initFn, get: getFn, set: setFn };
1088
+ };
1089
+ }
1090
+ var config = Object.assign(
1091
+ createConfigDecorator(),
1092
+ {
1093
+ required: createConfigDecorator({ required: true }),
1094
+ lazy(factory) {
1095
+ return createConfigDecorator({ lazyFactory: factory });
1096
+ },
1097
+ cached: createConfigDecorator({ cached: true }),
1098
+ merge: createConfigDecorator({ merge: true })
1099
+ }
1100
+ );
1101
+ function observable(_target, context) {
1102
+ const name = String(context.name);
1103
+ const metaMap = getOrCreateMetaMap(
1104
+ context.metadata
1105
+ );
1106
+ let meta = metaMap.get(name);
1107
+ if (!meta) {
1108
+ meta = {
1109
+ name,
1110
+ required: false,
1111
+ cached: false,
1112
+ merge: false,
1113
+ observable: true
1114
+ };
1115
+ metaMap.set(name, meta);
1116
+ } else {
1117
+ meta.observable = true;
1118
+ }
1119
+ }
1120
+ function alias(aliasName) {
1121
+ return function(target, _context) {
1122
+ ClassManager.registerAlias(aliasName, target);
1123
+ return target;
1124
+ };
1125
+ }
1126
+ function mixin(mixinClass) {
1127
+ return function(target, _context) {
1128
+ const targetCls = target;
1129
+ if (!targetCls.$mixins) targetCls.$mixins = /* @__PURE__ */ new Set();
1130
+ if (targetCls.$mixins.has(mixinClass)) return target;
1131
+ targetCls.$mixins.add(mixinClass);
1132
+ if (mixinClass.$mixins) {
1133
+ for (const m of mixinClass.$mixins) targetCls.$mixins.add(m);
1134
+ }
1135
+ for (const name of Object.getOwnPropertyNames(mixinClass.prototype)) {
1136
+ if (name === "constructor") continue;
1137
+ if (name in targetCls.prototype) continue;
1138
+ const desc = Object.getOwnPropertyDescriptor(mixinClass.prototype, name);
1139
+ if (desc) Object.defineProperty(targetCls.prototype, name, desc);
1140
+ }
1141
+ return target;
1142
+ };
1143
+ }
1144
+ function override(targetClass) {
1145
+ return function(patchClass, _context) {
1146
+ const patchProto = patchClass.prototype;
1147
+ for (const name of Object.getOwnPropertyNames(patchProto)) {
1148
+ if (name === "constructor") continue;
1149
+ const desc = Object.getOwnPropertyDescriptor(patchProto, name);
1150
+ if (desc) Object.defineProperty(targetClass.prototype, name, desc);
1151
+ }
1152
+ return patchClass;
1153
+ };
1154
+ }
1155
+
1156
+ // src/mixin/Identifiable.ts
1157
+ var IdentityMapImpl = class {
1158
+ map = /* @__PURE__ */ new Map();
1159
+ register(id, instance) {
1160
+ this.map.set(id, instance);
1161
+ }
1162
+ unregister(id) {
1163
+ this.map.delete(id);
1164
+ }
1165
+ get(id) {
1166
+ return this.map.get(id);
1167
+ }
1168
+ has(id) {
1169
+ return this.map.has(id);
1170
+ }
1171
+ };
1172
+ var IdentityMap = new IdentityMapImpl();
1173
+ var Identifiable = define("Ext.mixin.Identifiable", {
1174
+ config: {
1175
+ id: ""
1176
+ },
1177
+ /**
1178
+ * applyId: auto-generates an id if none provided, and registers
1179
+ * in the global identity map.
1180
+ */
1181
+ applyId(id, oldId) {
1182
+ if (oldId) {
1183
+ IdentityMap.unregister(oldId);
1184
+ }
1185
+ if (!id) {
1186
+ id = generateId("framesquared");
1187
+ }
1188
+ IdentityMap.register(id, this);
1189
+ if (!this.$identifiableHooked) {
1190
+ this.$identifiableHooked = true;
1191
+ this.$destroyHooks.push(() => {
1192
+ const currentId = this.getId?.();
1193
+ if (currentId) {
1194
+ IdentityMap.unregister(currentId);
1195
+ }
1196
+ });
1197
+ }
1198
+ return id;
1199
+ }
1200
+ });
1201
+
1202
+ // src/mixin/Factoryable.ts
1203
+ var Factoryable = {
1204
+ /**
1205
+ * Creates a `Base` instance from a config descriptor.
1206
+ *
1207
+ * @param config An object with `xtype`/`type`, a string alias, or
1208
+ * an existing `Base` instance.
1209
+ */
1210
+ create(config2) {
1211
+ if (config2 instanceof Base) {
1212
+ return config2;
1213
+ }
1214
+ if (typeof config2 === "string") {
1215
+ return ClassManager.instantiateByAlias(config2);
1216
+ }
1217
+ const alias2 = config2.xtype ?? config2.type;
1218
+ if (!alias2 || typeof alias2 !== "string") {
1219
+ throw new Error(
1220
+ 'Factoryable.create(): config must have an "xtype" or "type" property, be a string alias, or be a Base instance'
1221
+ );
1222
+ }
1223
+ const { xtype: _x, type: _t, ...rest } = config2;
1224
+ return ClassManager.instantiateByAlias(alias2, rest);
1225
+ }
1226
+ };
1227
+
1228
+ // src/mixin/Inheritable.ts
1229
+ var ownState = /* @__PURE__ */ new WeakMap();
1230
+ var parentRef = /* @__PURE__ */ new WeakMap();
1231
+ var Inheritable = define("Ext.mixin.Inheritable", {
1232
+ /**
1233
+ * Sets the parent in the inherited-state chain.
1234
+ */
1235
+ setInheritedParent(parent) {
1236
+ parentRef.set(this, parent);
1237
+ },
1238
+ /**
1239
+ * Returns the parent in the inherited-state chain, or undefined.
1240
+ */
1241
+ getInheritedParent() {
1242
+ return parentRef.get(this);
1243
+ },
1244
+ /**
1245
+ * Stores this instance's own inherited state. This is typically
1246
+ * called during component setup.
1247
+ */
1248
+ initInheritedState(state) {
1249
+ ownState.set(this, state);
1250
+ },
1251
+ /**
1252
+ * Returns the fully merged inherited state, walking up the parent chain.
1253
+ * Own state overrides parent state on key collisions.
1254
+ */
1255
+ getInherited() {
1256
+ const parent = parentRef.get(this);
1257
+ const parentState = parent && typeof parent.getInherited === "function" ? parent.getInherited() : {};
1258
+ const own = ownState.get(this) ?? {};
1259
+ return { ...parentState, ...own };
1260
+ },
1261
+ /**
1262
+ * Resolves a single key by walking up the inherited chain.
1263
+ * Returns the first defined value found, or undefined.
1264
+ */
1265
+ getInheritedConfig(key) {
1266
+ const own = ownState.get(this);
1267
+ if (own && key in own) {
1268
+ return own[key];
1269
+ }
1270
+ const parent = parentRef.get(this);
1271
+ if (parent && typeof parent.getInheritedConfig === "function") {
1272
+ return parent.getInheritedConfig(key);
1273
+ }
1274
+ return void 0;
1275
+ }
1276
+ });
1277
+
1278
+ // src/mixin/Hookable.ts
1279
+ var hookStore = /* @__PURE__ */ new WeakMap();
1280
+ function getHookMap(inst) {
1281
+ let map = hookStore.get(inst);
1282
+ if (!map) {
1283
+ map = /* @__PURE__ */ new Map();
1284
+ hookStore.set(inst, map);
1285
+ }
1286
+ return map;
1287
+ }
1288
+ function ensureEntry(inst, methodName) {
1289
+ const map = getHookMap(inst);
1290
+ let entry = map.get(methodName);
1291
+ if (!entry) {
1292
+ const original = inst[methodName];
1293
+ if (typeof original !== "function") {
1294
+ throw new Error(`Hookable: "${methodName}" is not a method on this instance`);
1295
+ }
1296
+ entry = { before: [], after: [], original, installed: false };
1297
+ map.set(methodName, entry);
1298
+ }
1299
+ return entry;
1300
+ }
1301
+ function installWrapper(inst, methodName, entry) {
1302
+ if (entry.installed) return;
1303
+ entry.installed = true;
1304
+ inst[methodName] = function(...args) {
1305
+ for (const fn of entry.before) {
1306
+ fn.apply(this, args);
1307
+ }
1308
+ const result = entry.original.apply(this, args);
1309
+ for (const fn of entry.after) {
1310
+ fn.call(this, result);
1311
+ }
1312
+ return result;
1313
+ };
1314
+ }
1315
+ var Hookable = define("Ext.mixin.Hookable", {
1316
+ /**
1317
+ * Adds a function that runs BEFORE the named method.
1318
+ * The hook receives the same arguments as the method.
1319
+ */
1320
+ addBeforeHook(methodName, fn) {
1321
+ const entry = ensureEntry(this, methodName);
1322
+ entry.before.push(fn);
1323
+ installWrapper(this, methodName, entry);
1324
+ },
1325
+ /**
1326
+ * Adds a function that runs AFTER the named method.
1327
+ * The hook receives the method's return value as its single argument.
1328
+ */
1329
+ addAfterHook(methodName, fn) {
1330
+ const entry = ensureEntry(this, methodName);
1331
+ entry.after.push(fn);
1332
+ installWrapper(this, methodName, entry);
1333
+ },
1334
+ /**
1335
+ * Removes a previously registered hook (before or after).
1336
+ */
1337
+ removeHook(methodName, fn) {
1338
+ const map = hookStore.get(this);
1339
+ if (!map) return;
1340
+ const entry = map.get(methodName);
1341
+ if (!entry) return;
1342
+ entry.before = entry.before.filter((f) => f !== fn);
1343
+ entry.after = entry.after.filter((f) => f !== fn);
1344
+ }
1345
+ });
1346
+
1347
+ // src/Plugin.ts
1348
+ var Plugin = class extends Base {
1349
+ static $className = "Plugin";
1350
+ // $configDefs for the define()-style config system
1351
+ static $configDefs = {
1352
+ id: "",
1353
+ owner: null
1354
+ };
1355
+ /**
1356
+ * Initialises this plugin. Called by the Pluggable mixin after the
1357
+ * plugin is added to an owner.
1358
+ */
1359
+ init(owner) {
1360
+ this.setOwner(owner);
1361
+ }
1362
+ /**
1363
+ * Override of initConfig that ensures an auto-generated id.
1364
+ */
1365
+ initConfig(config2) {
1366
+ super.initConfig(config2);
1367
+ if (!this.$configInitialized.has("id") || !this._id) {
1368
+ const setter = `set${capitalize("id")}`;
1369
+ if (typeof this[setter] === "function") {
1370
+ this[setter](generateId("plugin"));
1371
+ }
1372
+ }
1373
+ }
1374
+ };
1375
+
1376
+ // src/mixin/Pluggable.ts
1377
+ var pluginStore = /* @__PURE__ */ new WeakMap();
1378
+ function getPlugins(inst) {
1379
+ let list = pluginStore.get(inst);
1380
+ if (!list) {
1381
+ list = [];
1382
+ pluginStore.set(inst, list);
1383
+ }
1384
+ return list;
1385
+ }
1386
+ var Pluggable = define("Ext.mixin.Pluggable", {
1387
+ config: {
1388
+ plugins: null
1389
+ },
1390
+ /**
1391
+ * applyPlugins: processes the raw plugins config array.
1392
+ * Each entry can be a Plugin instance, a config object with type/xtype,
1393
+ * or will be passed through Factoryable.
1394
+ */
1395
+ applyPlugins(plugins) {
1396
+ if (!plugins || !Array.isArray(plugins)) return null;
1397
+ const list = getPlugins(this);
1398
+ for (const pluginCfg of plugins) {
1399
+ let plugin;
1400
+ if (pluginCfg instanceof Plugin) {
1401
+ plugin = pluginCfg;
1402
+ } else if (typeof pluginCfg === "object") {
1403
+ if (pluginCfg.xtype || pluginCfg.type) {
1404
+ plugin = Factoryable.create(pluginCfg);
1405
+ } else {
1406
+ plugin = new Plugin(pluginCfg);
1407
+ }
1408
+ } else {
1409
+ continue;
1410
+ }
1411
+ plugin.init(this);
1412
+ list.push(plugin);
1413
+ }
1414
+ if (!this.$pluggableHooked) {
1415
+ this.$pluggableHooked = true;
1416
+ this.$destroyHooks.push(() => {
1417
+ const l = pluginStore.get(this);
1418
+ if (l) {
1419
+ for (const p of l) {
1420
+ if (!p.isDestroyed) p.destroy();
1421
+ }
1422
+ l.length = 0;
1423
+ }
1424
+ });
1425
+ }
1426
+ return plugins;
1427
+ },
1428
+ /**
1429
+ * Returns the plugin with the given id, or undefined.
1430
+ */
1431
+ getPlugin(id) {
1432
+ const list = pluginStore.get(this);
1433
+ if (!list) return void 0;
1434
+ return list.find((p) => !p.isDestroyed && p.getId() === id);
1435
+ },
1436
+ /**
1437
+ * Adds a plugin to this instance. Calls plugin.init(this).
1438
+ */
1439
+ addPlugin(plugin) {
1440
+ const list = getPlugins(this);
1441
+ plugin.init(this);
1442
+ list.push(plugin);
1443
+ if (!this.$pluggableHooked) {
1444
+ this.$pluggableHooked = true;
1445
+ this.$destroyHooks.push(() => {
1446
+ const l = pluginStore.get(this);
1447
+ if (l) {
1448
+ for (const p of l) {
1449
+ if (!p.isDestroyed) p.destroy();
1450
+ }
1451
+ l.length = 0;
1452
+ }
1453
+ });
1454
+ }
1455
+ },
1456
+ /**
1457
+ * Removes a plugin from this instance. Destroys the plugin.
1458
+ */
1459
+ removePlugin(plugin) {
1460
+ const list = pluginStore.get(this);
1461
+ if (!list) return;
1462
+ const idx = list.indexOf(plugin);
1463
+ if (idx !== -1) {
1464
+ list.splice(idx, 1);
1465
+ }
1466
+ plugin.destroy();
1467
+ }
1468
+ });
1469
+
1470
+ // src/event/Event.ts
1471
+ var ExtEvent = class {
1472
+ eventName;
1473
+ source;
1474
+ timestamp;
1475
+ defaultPrevented = false;
1476
+ propagationStopped = false;
1477
+ constructor(eventName, source) {
1478
+ this.eventName = eventName;
1479
+ this.source = source;
1480
+ this.timestamp = performance.now();
1481
+ }
1482
+ preventDefault() {
1483
+ this.defaultPrevented = true;
1484
+ }
1485
+ stopPropagation() {
1486
+ this.propagationStopped = true;
1487
+ }
1488
+ /** Convenience: prevents default AND stops propagation. */
1489
+ stopEvent() {
1490
+ this.defaultPrevented = true;
1491
+ this.propagationStopped = true;
1492
+ }
1493
+ };
1494
+
1495
+ // src/event/Observable.ts
1496
+ var stateMap = /* @__PURE__ */ new WeakMap();
1497
+ function getState(inst) {
1498
+ let state = stateMap.get(inst);
1499
+ if (!state) {
1500
+ state = {
1501
+ listeners: /* @__PURE__ */ new Map(),
1502
+ suspendCount: 0,
1503
+ queueSuspended: false,
1504
+ eventQueue: [],
1505
+ managedListeners: []
1506
+ };
1507
+ stateMap.set(inst, state);
1508
+ }
1509
+ return state;
1510
+ }
1511
+ function getListeners(state, eventName) {
1512
+ let list = state.listeners.get(eventName);
1513
+ if (!list) {
1514
+ list = [];
1515
+ state.listeners.set(eventName, list);
1516
+ }
1517
+ return list;
1518
+ }
1519
+ function insertListener(list, entry, prepend) {
1520
+ const p = entry.priority;
1521
+ let idx;
1522
+ if (prepend) {
1523
+ idx = list.findIndex((e) => e.priority < p);
1524
+ } else {
1525
+ idx = list.findIndex((e) => e.priority < p);
1526
+ }
1527
+ if (idx === -1) {
1528
+ if (prepend) {
1529
+ idx = list.findIndex((e) => e.priority <= p);
1530
+ if (idx === -1) list.push(entry);
1531
+ else list.splice(idx, 0, entry);
1532
+ } else {
1533
+ list.push(entry);
1534
+ }
1535
+ } else {
1536
+ if (prepend) {
1537
+ while (idx > 0 && list[idx - 1].priority === p) idx--;
1538
+ list.splice(idx, 0, entry);
1539
+ } else {
1540
+ list.splice(idx, 0, entry);
1541
+ }
1542
+ }
1543
+ }
1544
+ function doFireEvent(inst, eventName, args) {
1545
+ const state = getState(inst);
1546
+ if (state.suspendCount > 0) {
1547
+ if (state.queueSuspended) {
1548
+ state.eventQueue.push({ eventName, args });
1549
+ }
1550
+ return true;
1551
+ }
1552
+ const list = state.listeners.get(eventName);
1553
+ if (!list || list.length === 0) return true;
1554
+ const snapshot = [...list];
1555
+ let cancelled = false;
1556
+ for (const entry of snapshot) {
1557
+ if (!list.includes(entry)) continue;
1558
+ const callArgs = entry.args ? [...entry.args, ...args] : args;
1559
+ const scope = entry.scope;
1560
+ if (entry.delay > 0) {
1561
+ setTimeout(() => entry.fn.apply(scope, callArgs), entry.delay);
1562
+ if (entry.single) removeEntry(list, entry);
1563
+ continue;
1564
+ }
1565
+ if (entry.buffer > 0) {
1566
+ if (entry.bufferTimerId !== void 0) {
1567
+ clearTimeout(entry.bufferTimerId);
1568
+ }
1569
+ entry.bufferTimerId = setTimeout(() => {
1570
+ entry.bufferTimerId = void 0;
1571
+ entry.fn.apply(scope, callArgs);
1572
+ }, entry.buffer);
1573
+ if (entry.single) removeEntry(list, entry);
1574
+ continue;
1575
+ }
1576
+ const result = entry.fn.apply(scope, callArgs);
1577
+ if (entry.single) {
1578
+ removeEntry(list, entry);
1579
+ }
1580
+ if (result === false) {
1581
+ cancelled = true;
1582
+ }
1583
+ }
1584
+ return !cancelled;
1585
+ }
1586
+ function removeEntry(list, entry) {
1587
+ const idx = list.indexOf(entry);
1588
+ if (idx !== -1) list.splice(idx, 1);
1589
+ }
1590
+ function findEntry(list, fn, scope) {
1591
+ return list.find(
1592
+ (e) => e.fn === fn && e.scope === scope
1593
+ );
1594
+ }
1595
+ var Observable = define("Ext.mixin.Observable", {
1596
+ // ----- on (with overloads) -----
1597
+ on(eventNameOrMap, handlerOrScope, scope, options) {
1598
+ if (typeof eventNameOrMap === "object" && eventNameOrMap !== null) {
1599
+ const map = eventNameOrMap;
1600
+ const mapScope = handlerOrScope;
1601
+ for (const [evName, handler2] of Object.entries(map)) {
1602
+ this.on(evName, handler2, mapScope);
1603
+ }
1604
+ return;
1605
+ }
1606
+ const eventName = eventNameOrMap;
1607
+ const handler = handlerOrScope;
1608
+ const opts = options ?? {};
1609
+ const state = getState(this);
1610
+ const list = getListeners(state, eventName);
1611
+ const entry = {
1612
+ fn: handler,
1613
+ scope,
1614
+ priority: opts.priority ?? 0,
1615
+ single: opts.single ?? false,
1616
+ delay: opts.delay ?? 0,
1617
+ buffer: opts.buffer ?? 0,
1618
+ args: opts.args
1619
+ };
1620
+ insertListener(list, entry, opts.prepend ?? false);
1621
+ if (opts.destroyable) {
1622
+ return {
1623
+ destroy: () => {
1624
+ removeEntry(list, entry);
1625
+ }
1626
+ };
1627
+ }
1628
+ },
1629
+ // ----- un -----
1630
+ un(eventName, handler, scope) {
1631
+ const state = getState(this);
1632
+ const list = state.listeners.get(eventName);
1633
+ if (!list) return;
1634
+ const entry = findEntry(list, handler, scope);
1635
+ if (entry) removeEntry(list, entry);
1636
+ },
1637
+ // ----- aliases -----
1638
+ addListener(...args) {
1639
+ return this.on(...args);
1640
+ },
1641
+ removeListener(...args) {
1642
+ return this.un(...args);
1643
+ },
1644
+ // ----- fireEvent -----
1645
+ fireEvent(eventName, ...args) {
1646
+ return doFireEvent(this, eventName, args);
1647
+ },
1648
+ // ----- fireEventArgs -----
1649
+ fireEventArgs(eventName, args) {
1650
+ return doFireEvent(this, eventName, args);
1651
+ },
1652
+ // ----- fireEventedAction -----
1653
+ fireEventedAction(eventName, args, beforeFn, afterFn, scope) {
1654
+ const beforeResult = doFireEvent(this, `before${eventName}`, args);
1655
+ if (!beforeResult) return;
1656
+ beforeFn.apply(scope, args);
1657
+ doFireEvent(this, eventName, args);
1658
+ afterFn.apply(scope, args);
1659
+ },
1660
+ // ----- suspendEvents -----
1661
+ suspendEvents(queueSuspended) {
1662
+ const state = getState(this);
1663
+ state.suspendCount++;
1664
+ if (queueSuspended) {
1665
+ state.queueSuspended = true;
1666
+ }
1667
+ },
1668
+ // ----- resumeEvents -----
1669
+ resumeEvents(discardQueue) {
1670
+ const state = getState(this);
1671
+ if (state.suspendCount > 0) {
1672
+ state.suspendCount--;
1673
+ }
1674
+ if (state.suspendCount === 0) {
1675
+ if (!discardQueue && state.queueSuspended) {
1676
+ const queue = state.eventQueue.splice(0);
1677
+ state.queueSuspended = false;
1678
+ for (const qe of queue) {
1679
+ doFireEvent(this, qe.eventName, qe.args);
1680
+ }
1681
+ } else {
1682
+ state.eventQueue.length = 0;
1683
+ state.queueSuspended = false;
1684
+ }
1685
+ }
1686
+ },
1687
+ // ----- isSuspended -----
1688
+ isSuspended() {
1689
+ return getState(this).suspendCount > 0;
1690
+ },
1691
+ // ----- hasListener -----
1692
+ hasListener(eventName) {
1693
+ const state = stateMap.get(this);
1694
+ if (!state) return false;
1695
+ const list = state.listeners.get(eventName);
1696
+ return !!list && list.length > 0;
1697
+ },
1698
+ // ----- clearListeners -----
1699
+ clearListeners() {
1700
+ const state = stateMap.get(this);
1701
+ if (state) {
1702
+ state.listeners.clear();
1703
+ state.eventQueue.length = 0;
1704
+ }
1705
+ },
1706
+ // ----- relayEvents -----
1707
+ relayEvents(origin, events, prefix) {
1708
+ const handlers = [];
1709
+ const self = this;
1710
+ for (const eventName of events) {
1711
+ const targetName = (prefix ?? "") + eventName;
1712
+ const fn = (...args) => {
1713
+ doFireEvent(self, targetName, args);
1714
+ };
1715
+ origin.on(eventName, fn);
1716
+ handlers.push({ eventName, fn });
1717
+ }
1718
+ return {
1719
+ destroy() {
1720
+ for (const { eventName, fn } of handlers) {
1721
+ origin.un(eventName, fn);
1722
+ }
1723
+ handlers.length = 0;
1724
+ }
1725
+ };
1726
+ },
1727
+ // ----- mon (managed listener) -----
1728
+ mon(target, eventName, handler, scope) {
1729
+ const state = getState(this);
1730
+ target.on(eventName, handler, scope);
1731
+ state.managedListeners.push({ target, eventName, fn: handler, scope });
1732
+ },
1733
+ // ----- mun (remove managed listener) -----
1734
+ mun(target, eventName, handler) {
1735
+ const state = getState(this);
1736
+ target.un(eventName, handler);
1737
+ state.managedListeners = state.managedListeners.filter(
1738
+ (ml) => !(ml.target === target && ml.eventName === eventName && ml.fn === handler)
1739
+ );
1740
+ }
1741
+ });
1742
+ var hookedInstances = /* @__PURE__ */ new WeakSet();
1743
+ var obsProto = Observable.prototype;
1744
+ function installDestroyHook(inst) {
1745
+ if (hookedInstances.has(inst)) return;
1746
+ hookedInstances.add(inst);
1747
+ inst.$destroyHooks.push(() => {
1748
+ const state = stateMap.get(inst);
1749
+ if (state) {
1750
+ state.listeners.clear();
1751
+ state.eventQueue.length = 0;
1752
+ for (const ml of state.managedListeners) {
1753
+ ml.target.un(ml.eventName, ml.fn, ml.scope);
1754
+ }
1755
+ state.managedListeners.length = 0;
1756
+ }
1757
+ });
1758
+ }
1759
+ var origOn = obsProto.on;
1760
+ obsProto.on = function(...args) {
1761
+ installDestroyHook(this);
1762
+ return origOn.apply(this, args);
1763
+ };
1764
+ var origMon = obsProto.mon;
1765
+ obsProto.mon = function(...args) {
1766
+ installDestroyHook(this);
1767
+ return origMon.apply(this, args);
1768
+ };
1769
+
1770
+ // src/event/Destroyable.ts
1771
+ var DestroyableUtil = {
1772
+ /**
1773
+ * Returns a single {@link Destroyable} whose `destroy()` method calls
1774
+ * `destroy()` on every item. The combined handle is idempotent — second
1775
+ * and subsequent calls are no-ops.
1776
+ */
1777
+ combine(...items) {
1778
+ let destroyed = false;
1779
+ return {
1780
+ destroy() {
1781
+ if (destroyed) return;
1782
+ destroyed = true;
1783
+ for (const item of items) {
1784
+ item.destroy();
1785
+ }
1786
+ }
1787
+ };
1788
+ }
1789
+ };
1790
+
1791
+ // src/event/EventDomain.ts
1792
+ var domainRegistry = /* @__PURE__ */ new Map();
1793
+ var EventDomain = class {
1794
+ name;
1795
+ matchFn;
1796
+ registeredListeners = [];
1797
+ constructor(name, matchFn) {
1798
+ this.name = name;
1799
+ this.matchFn = matchFn;
1800
+ }
1801
+ // -- Static registry ----------------------------------------------------
1802
+ static register(name, domain) {
1803
+ domainRegistry.set(name, domain);
1804
+ }
1805
+ static get(name) {
1806
+ return domainRegistry.get(name);
1807
+ }
1808
+ // -- Instance API -------------------------------------------------------
1809
+ /**
1810
+ * Returns `true` if `target` belongs to this domain.
1811
+ */
1812
+ match(target) {
1813
+ return this.matchFn(target);
1814
+ }
1815
+ /**
1816
+ * Registers a controller-style listener config.
1817
+ *
1818
+ * ```ts
1819
+ * domain.listen({
1820
+ * '#myButton': { click: handler },
1821
+ * 'MyApp.view.Panel': { collapse: handler },
1822
+ * });
1823
+ * ```
1824
+ *
1825
+ * Returns a {@link Destroyable} that removes all registered listeners.
1826
+ */
1827
+ listen(config2) {
1828
+ const entries2 = [];
1829
+ for (const [selector, events] of Object.entries(config2)) {
1830
+ for (const [eventName, handler] of Object.entries(events)) {
1831
+ const entry = { selector, eventName, handler };
1832
+ entries2.push(entry);
1833
+ this.registeredListeners.push(entry);
1834
+ }
1835
+ }
1836
+ return {
1837
+ destroy: () => {
1838
+ for (const entry of entries2) {
1839
+ const idx = this.registeredListeners.indexOf(entry);
1840
+ if (idx !== -1) this.registeredListeners.splice(idx, 1);
1841
+ }
1842
+ entries2.length = 0;
1843
+ }
1844
+ };
1845
+ }
1846
+ /**
1847
+ * Dispatches an event from `source` through this domain.
1848
+ * All registered listeners whose selector matches are called.
1849
+ */
1850
+ dispatch(source, eventName, args) {
1851
+ if (!this.match(source)) return;
1852
+ for (const entry of this.registeredListeners) {
1853
+ if (entry.eventName !== eventName) continue;
1854
+ if (!this.matchesSelector(source, entry.selector)) continue;
1855
+ entry.handler.apply(void 0, args);
1856
+ }
1857
+ }
1858
+ // -- Selector matching --------------------------------------------------
1859
+ matchesSelector(target, selector) {
1860
+ if (selector.startsWith("#")) {
1861
+ const id = selector.slice(1);
1862
+ const targetId = typeof target.getItemId === "function" ? target.getItemId() : typeof target.getId === "function" ? target.getId() : void 0;
1863
+ return targetId === id;
1864
+ }
1865
+ return target.$className === selector;
1866
+ }
1867
+ };
1868
+
1869
+ // src/event/EventBus.ts
1870
+ function matchPattern(pattern, channel) {
1871
+ if (pattern === "**") return true;
1872
+ if (pattern.endsWith(".**")) {
1873
+ const prefix = pattern.slice(0, -3);
1874
+ return channel.startsWith(prefix + ".");
1875
+ }
1876
+ if (pattern.endsWith(".*")) {
1877
+ const prefix = pattern.slice(0, -2);
1878
+ if (!channel.startsWith(prefix + ".")) return false;
1879
+ const rest = channel.slice(prefix.length + 1);
1880
+ return rest.length > 0 && !rest.includes(".");
1881
+ }
1882
+ return pattern === channel;
1883
+ }
1884
+ var EventBusImpl = class {
1885
+ subscriptions = [];
1886
+ /**
1887
+ * Publishes a message to a channel. All matching subscribers are called.
1888
+ */
1889
+ publish(channel, ...args) {
1890
+ const snapshot = [...this.subscriptions];
1891
+ for (const sub of snapshot) {
1892
+ if (matchPattern(sub.pattern, channel)) {
1893
+ sub.handler.apply(sub.scope, args);
1894
+ }
1895
+ }
1896
+ }
1897
+ /**
1898
+ * Subscribes to a channel (exact or wildcard pattern).
1899
+ * Returns a {@link Destroyable} handle.
1900
+ */
1901
+ subscribe(channel, handler, scope) {
1902
+ const sub = { pattern: channel, handler, scope };
1903
+ this.subscriptions.push(sub);
1904
+ return {
1905
+ destroy: () => {
1906
+ const idx = this.subscriptions.indexOf(sub);
1907
+ if (idx !== -1) this.subscriptions.splice(idx, 1);
1908
+ }
1909
+ };
1910
+ }
1911
+ /**
1912
+ * Removes a specific subscription by handler reference.
1913
+ */
1914
+ unsubscribe(channel, handler) {
1915
+ const idx = this.subscriptions.findIndex(
1916
+ (s) => s.pattern === channel && s.handler === handler
1917
+ );
1918
+ if (idx !== -1) this.subscriptions.splice(idx, 1);
1919
+ }
1920
+ };
1921
+ var EventBus = new EventBusImpl();
1922
+
1923
+ // src/event/DomEvent.ts
1924
+ var tracked = [];
1925
+ var EventManager = {
1926
+ // ----- on -----
1927
+ /**
1928
+ * Registers a DOM event listener. The listener is tracked for
1929
+ * bulk removal via {@link removeAll}.
1930
+ *
1931
+ * Returns a {@link Destroyable} handle.
1932
+ */
1933
+ on(element, eventName, handler, options) {
1934
+ element.addEventListener(eventName, handler, options);
1935
+ const entry = { element, eventName, handler, options };
1936
+ tracked.push(entry);
1937
+ return {
1938
+ destroy() {
1939
+ element.removeEventListener(eventName, handler, options);
1940
+ const idx = tracked.indexOf(entry);
1941
+ if (idx !== -1) tracked.splice(idx, 1);
1942
+ }
1943
+ };
1944
+ },
1945
+ // ----- un -----
1946
+ /**
1947
+ * Removes a DOM event listener.
1948
+ */
1949
+ un(element, eventName, handler) {
1950
+ element.removeEventListener(eventName, handler);
1951
+ const idx = tracked.findIndex(
1952
+ (t) => t.element === element && t.eventName === eventName && t.handler === handler
1953
+ );
1954
+ if (idx !== -1) tracked.splice(idx, 1);
1955
+ },
1956
+ // ----- delegate -----
1957
+ /**
1958
+ * Event delegation: listens on `element` and fires `handler` only when
1959
+ * the event target (or an ancestor up to `element`) matches `selector`.
1960
+ *
1961
+ * Uses `Element.closest()` — no IE polyfill needed.
1962
+ */
1963
+ delegate(element, eventName, selector, handler) {
1964
+ const delegateHandler = (e) => {
1965
+ const target = e.target;
1966
+ if (!target) return;
1967
+ const matched = target.closest(selector);
1968
+ if (matched && element.contains(matched)) {
1969
+ handler(e, matched);
1970
+ }
1971
+ };
1972
+ return EventManager.on(element, eventName, delegateHandler);
1973
+ },
1974
+ // ----- onReady -----
1975
+ /**
1976
+ * Calls `fn` when the DOM is ready. If it's already ready (interactive
1977
+ * or complete), fires immediately.
1978
+ */
1979
+ onReady(fn) {
1980
+ if (typeof document !== "undefined" && (document.readyState === "interactive" || document.readyState === "complete")) {
1981
+ fn();
1982
+ } else if (typeof document !== "undefined") {
1983
+ document.addEventListener("DOMContentLoaded", fn, { once: true });
1984
+ }
1985
+ },
1986
+ // ----- utility helpers -----
1987
+ /** Prevents default and stops propagation. */
1988
+ stopEvent(e) {
1989
+ e.preventDefault();
1990
+ e.stopPropagation();
1991
+ },
1992
+ /** Returns `e.pageX` (or `e.clientX` as fallback). */
1993
+ getPageX(e) {
1994
+ return e.pageX ?? e.clientX;
1995
+ },
1996
+ /** Returns `e.pageY` (or `e.clientY` as fallback). */
1997
+ getPageY(e) {
1998
+ return e.pageY ?? e.clientY;
1999
+ },
2000
+ /** Returns `e.relatedTarget`. */
2001
+ getRelatedTarget(e) {
2002
+ return e.relatedTarget;
2003
+ },
2004
+ /** Returns `e.key` (modern key value). */
2005
+ getKeyCode(e) {
2006
+ return e.key;
2007
+ },
2008
+ // ----- bulk cleanup -----
2009
+ /**
2010
+ * Removes all listeners registered via {@link on} and {@link delegate}.
2011
+ */
2012
+ removeAll() {
2013
+ for (const entry of tracked) {
2014
+ entry.element.removeEventListener(entry.eventName, entry.handler, entry.options);
2015
+ }
2016
+ tracked.length = 0;
2017
+ }
2018
+ };
2019
+
2020
+ // src/event/GestureRecognizer.ts
2021
+ var TAP_MAX_DURATION = 400;
2022
+ var TAP_MAX_DISTANCE = 15;
2023
+ var DOUBLETAP_INTERVAL = 300;
2024
+ var LONGPRESS_DURATION = 500;
2025
+ var SWIPE_MIN_DISTANCE = 50;
2026
+ function distance(x1, y1, x2, y2) {
2027
+ return Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
2028
+ }
2029
+ function angle(x1, y1, x2, y2) {
2030
+ return Math.atan2(y2 - y1, x2 - x1) * (180 / Math.PI);
2031
+ }
2032
+ function dispatchGesture(el, name, detail) {
2033
+ el.dispatchEvent(new CustomEvent(name, {
2034
+ bubbles: true,
2035
+ cancelable: true,
2036
+ detail: detail ?? {}
2037
+ }));
2038
+ }
2039
+ var GestureRecognizer = class {
2040
+ el;
2041
+ pointers = /* @__PURE__ */ new Map();
2042
+ longpressTimer;
2043
+ lastTapTime = 0;
2044
+ destroyed = false;
2045
+ // Multi-touch initial state
2046
+ initialPinchDistance = 0;
2047
+ initialPinchAngle = 0;
2048
+ // Bound handlers for removal
2049
+ handleDown;
2050
+ handleMove;
2051
+ handleUp;
2052
+ handleCancel;
2053
+ constructor(element) {
2054
+ this.el = element;
2055
+ this.handleDown = (e) => this.onPointerDown(e);
2056
+ this.handleMove = (e) => this.onPointerMove(e);
2057
+ this.handleUp = (e) => this.onPointerUp(e);
2058
+ this.handleCancel = (e) => this.onPointerCancel(e);
2059
+ element.addEventListener("pointerdown", this.handleDown);
2060
+ element.addEventListener("pointermove", this.handleMove);
2061
+ element.addEventListener("pointerup", this.handleUp);
2062
+ element.addEventListener("pointercancel", this.handleCancel);
2063
+ }
2064
+ destroy() {
2065
+ if (this.destroyed) return;
2066
+ this.destroyed = true;
2067
+ this.el.removeEventListener("pointerdown", this.handleDown);
2068
+ this.el.removeEventListener("pointermove", this.handleMove);
2069
+ this.el.removeEventListener("pointerup", this.handleUp);
2070
+ this.el.removeEventListener("pointercancel", this.handleCancel);
2071
+ this.clearLongpress();
2072
+ this.pointers.clear();
2073
+ }
2074
+ // ----- pointer down -----
2075
+ onPointerDown(e) {
2076
+ if (this.destroyed) return;
2077
+ const state = {
2078
+ id: e.pointerId,
2079
+ startX: e.clientX,
2080
+ startY: e.clientY,
2081
+ currentX: e.clientX,
2082
+ currentY: e.clientY,
2083
+ startTime: Date.now()
2084
+ };
2085
+ this.pointers.set(e.pointerId, state);
2086
+ if (this.pointers.size === 1) {
2087
+ this.startLongpress(state);
2088
+ } else if (this.pointers.size === 2) {
2089
+ this.clearLongpress();
2090
+ this.recordTwoPointerBaseline();
2091
+ }
2092
+ }
2093
+ // ----- pointer move -----
2094
+ onPointerMove(e) {
2095
+ if (this.destroyed) return;
2096
+ const state = this.pointers.get(e.pointerId);
2097
+ if (!state) return;
2098
+ state.currentX = e.clientX;
2099
+ state.currentY = e.clientY;
2100
+ const dist = distance(state.startX, state.startY, state.currentX, state.currentY);
2101
+ if (dist > TAP_MAX_DISTANCE) {
2102
+ this.clearLongpress();
2103
+ }
2104
+ if (this.pointers.size === 2) {
2105
+ this.handleTwoPointerMove();
2106
+ }
2107
+ }
2108
+ // ----- pointer up -----
2109
+ onPointerUp(e) {
2110
+ if (this.destroyed) return;
2111
+ const state = this.pointers.get(e.pointerId);
2112
+ if (!state) return;
2113
+ state.currentX = e.clientX;
2114
+ state.currentY = e.clientY;
2115
+ this.clearLongpress();
2116
+ const dt = Date.now() - state.startTime;
2117
+ const dist = distance(state.startX, state.startY, state.currentX, state.currentY);
2118
+ this.pointers.delete(e.pointerId);
2119
+ if (this.pointers.size > 0) return;
2120
+ if (dist >= SWIPE_MIN_DISTANCE && dt < 1e3) {
2121
+ this.fireSwipe(state);
2122
+ return;
2123
+ }
2124
+ if (dt < TAP_MAX_DURATION && dist < TAP_MAX_DISTANCE) {
2125
+ const now2 = Date.now();
2126
+ if (now2 - this.lastTapTime < DOUBLETAP_INTERVAL) {
2127
+ dispatchGesture(this.el, "doubletap");
2128
+ this.lastTapTime = 0;
2129
+ } else {
2130
+ dispatchGesture(this.el, "tap");
2131
+ this.lastTapTime = now2;
2132
+ }
2133
+ }
2134
+ }
2135
+ // ----- pointer cancel -----
2136
+ onPointerCancel(e) {
2137
+ if (this.destroyed) return;
2138
+ this.pointers.delete(e.pointerId);
2139
+ this.clearLongpress();
2140
+ }
2141
+ // ----- longpress -----
2142
+ startLongpress(state) {
2143
+ this.clearLongpress();
2144
+ this.longpressTimer = setTimeout(() => {
2145
+ this.longpressTimer = void 0;
2146
+ const current = this.pointers.get(state.id);
2147
+ if (!current) return;
2148
+ const dist = distance(current.startX, current.startY, current.currentX, current.currentY);
2149
+ if (dist < TAP_MAX_DISTANCE) {
2150
+ dispatchGesture(this.el, "longpress");
2151
+ }
2152
+ }, LONGPRESS_DURATION);
2153
+ }
2154
+ clearLongpress() {
2155
+ if (this.longpressTimer !== void 0) {
2156
+ clearTimeout(this.longpressTimer);
2157
+ this.longpressTimer = void 0;
2158
+ }
2159
+ }
2160
+ // ----- swipe -----
2161
+ fireSwipe(state) {
2162
+ const dx = state.currentX - state.startX;
2163
+ const dy = state.currentY - state.startY;
2164
+ const absDx = Math.abs(dx);
2165
+ const absDy = Math.abs(dy);
2166
+ let direction;
2167
+ if (absDx > absDy) {
2168
+ direction = dx > 0 ? "right" : "left";
2169
+ } else {
2170
+ direction = dy > 0 ? "down" : "up";
2171
+ }
2172
+ dispatchGesture(this.el, "swipe", {
2173
+ direction,
2174
+ distance: Math.max(absDx, absDy),
2175
+ deltaX: dx,
2176
+ deltaY: dy
2177
+ });
2178
+ }
2179
+ // ----- two-pointer: pinch + rotate -----
2180
+ recordTwoPointerBaseline() {
2181
+ const pts = [...this.pointers.values()];
2182
+ if (pts.length < 2) return;
2183
+ this.initialPinchDistance = distance(
2184
+ pts[0].currentX,
2185
+ pts[0].currentY,
2186
+ pts[1].currentX,
2187
+ pts[1].currentY
2188
+ );
2189
+ this.initialPinchAngle = angle(
2190
+ pts[0].currentX,
2191
+ pts[0].currentY,
2192
+ pts[1].currentX,
2193
+ pts[1].currentY
2194
+ );
2195
+ }
2196
+ handleTwoPointerMove() {
2197
+ const pts = [...this.pointers.values()];
2198
+ if (pts.length < 2) return;
2199
+ const currentDist = distance(
2200
+ pts[0].currentX,
2201
+ pts[0].currentY,
2202
+ pts[1].currentX,
2203
+ pts[1].currentY
2204
+ );
2205
+ const currentAngle = angle(
2206
+ pts[0].currentX,
2207
+ pts[0].currentY,
2208
+ pts[1].currentX,
2209
+ pts[1].currentY
2210
+ );
2211
+ if (this.initialPinchDistance > 0) {
2212
+ const scale = currentDist / this.initialPinchDistance;
2213
+ dispatchGesture(this.el, "pinch", {
2214
+ scale,
2215
+ distance: currentDist
2216
+ });
2217
+ }
2218
+ const angleDelta = currentAngle - this.initialPinchAngle;
2219
+ dispatchGesture(this.el, "rotate", {
2220
+ angle: angleDelta,
2221
+ rotation: angleDelta
2222
+ });
2223
+ }
2224
+ };
2225
+
2226
+ // src/event/KeyMap.ts
2227
+ function parseBinding(binding) {
2228
+ const parts = binding.key.split("+").map((p) => p.trim());
2229
+ let ctrl = false;
2230
+ let alt = false;
2231
+ let shift = false;
2232
+ let meta = false;
2233
+ const keyPart = parts.pop();
2234
+ for (const mod of parts) {
2235
+ switch (mod.toLowerCase()) {
2236
+ case "ctrl":
2237
+ case "control":
2238
+ ctrl = true;
2239
+ break;
2240
+ case "alt":
2241
+ alt = true;
2242
+ break;
2243
+ case "shift":
2244
+ shift = true;
2245
+ break;
2246
+ case "meta":
2247
+ case "cmd":
2248
+ case "command":
2249
+ meta = true;
2250
+ break;
2251
+ }
2252
+ }
2253
+ return {
2254
+ raw: binding,
2255
+ keyLower: keyPart.toLowerCase(),
2256
+ ctrl,
2257
+ alt,
2258
+ shift,
2259
+ meta
2260
+ };
2261
+ }
2262
+ function matchesEvent(parsed, e) {
2263
+ if (e.key.toLowerCase() !== parsed.keyLower) return false;
2264
+ if (e.ctrlKey !== parsed.ctrl) return false;
2265
+ if (e.altKey !== parsed.alt) return false;
2266
+ if (e.shiftKey !== parsed.shift) return false;
2267
+ if (e.metaKey !== parsed.meta) return false;
2268
+ return true;
2269
+ }
2270
+ var KeyMap = class {
2271
+ target;
2272
+ parsedBindings = [];
2273
+ enabled = true;
2274
+ handleKeyDown;
2275
+ constructor(config2) {
2276
+ this.target = config2.target;
2277
+ for (const b of config2.bindings) {
2278
+ this.parsedBindings.push(parseBinding(b));
2279
+ }
2280
+ this.handleKeyDown = (e) => this.onKeyDown(e);
2281
+ this.target.addEventListener("keydown", this.handleKeyDown);
2282
+ }
2283
+ // ----- event handler -----
2284
+ onKeyDown(e) {
2285
+ if (!this.enabled) return;
2286
+ for (const parsed of this.parsedBindings) {
2287
+ if (matchesEvent(parsed, e)) {
2288
+ if (parsed.raw.preventDefault) {
2289
+ e.preventDefault();
2290
+ }
2291
+ parsed.raw.handler.call(parsed.raw.scope, e);
2292
+ }
2293
+ }
2294
+ }
2295
+ // ----- public API -----
2296
+ enable() {
2297
+ this.enabled = true;
2298
+ }
2299
+ disable() {
2300
+ this.enabled = false;
2301
+ }
2302
+ addBinding(binding) {
2303
+ this.parsedBindings.push(parseBinding(binding));
2304
+ }
2305
+ removeBinding(binding) {
2306
+ const idx = this.parsedBindings.findIndex((p) => p.raw === binding);
2307
+ if (idx !== -1) this.parsedBindings.splice(idx, 1);
2308
+ }
2309
+ destroy() {
2310
+ this.target.removeEventListener("keydown", this.handleKeyDown);
2311
+ this.parsedBindings.length = 0;
2312
+ this.enabled = false;
2313
+ }
2314
+ };
2315
+
2316
+ // src/aria/AriaManager.ts
2317
+ var descCounter = 0;
2318
+ var liveRegion = null;
2319
+ var assertiveRegion = null;
2320
+ function ensureLiveRegion(priority) {
2321
+ if (priority === "assertive") {
2322
+ if (!assertiveRegion) {
2323
+ assertiveRegion = document.createElement("div");
2324
+ assertiveRegion.setAttribute("aria-live", "assertive");
2325
+ assertiveRegion.setAttribute("aria-atomic", "true");
2326
+ assertiveRegion.setAttribute("role", "status");
2327
+ assertiveRegion.style.cssText = "position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;";
2328
+ document.body.appendChild(assertiveRegion);
2329
+ }
2330
+ return assertiveRegion;
2331
+ }
2332
+ if (!liveRegion) {
2333
+ liveRegion = document.createElement("div");
2334
+ liveRegion.setAttribute("aria-live", "polite");
2335
+ liveRegion.setAttribute("aria-atomic", "true");
2336
+ liveRegion.setAttribute("role", "status");
2337
+ liveRegion.style.cssText = "position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;";
2338
+ document.body.appendChild(liveRegion);
2339
+ }
2340
+ return liveRegion;
2341
+ }
2342
+ var AriaManager = {
2343
+ setRole(element, role) {
2344
+ element.setAttribute("role", role);
2345
+ },
2346
+ setLabel(element, label) {
2347
+ element.setAttribute("aria-label", label);
2348
+ },
2349
+ setLabelledBy(element, id) {
2350
+ element.setAttribute("aria-labelledby", id);
2351
+ },
2352
+ setDescription(element, description) {
2353
+ const id = `ext-aria-desc-${descCounter++}`;
2354
+ const descEl = document.createElement("span");
2355
+ descEl.id = id;
2356
+ descEl.textContent = description;
2357
+ descEl.style.cssText = "position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);";
2358
+ element.parentNode?.appendChild(descEl) ?? document.body.appendChild(descEl);
2359
+ element.setAttribute("aria-describedby", id);
2360
+ },
2361
+ setLive(element, mode) {
2362
+ element.setAttribute("aria-live", mode);
2363
+ },
2364
+ announce(message, priority = "polite") {
2365
+ const region = ensureLiveRegion(priority);
2366
+ region.textContent = message;
2367
+ },
2368
+ /** Generate a unique ID for ARIA linking. */
2369
+ generateId(prefix = "ext-aria") {
2370
+ return `${prefix}-${descCounter++}`;
2371
+ }
2372
+ };
2373
+
2374
+ // src/aria/FocusManager.ts
2375
+ var trapped = false;
2376
+ var trapContainer = null;
2377
+ var savedFocus = null;
2378
+ var keyHandler = null;
2379
+ var FOCUSABLE = 'a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex="-1"])';
2380
+ function getFocusableElements(container) {
2381
+ return Array.from(container.querySelectorAll(FOCUSABLE));
2382
+ }
2383
+ function onKeyDown(e) {
2384
+ if (e.key !== "Tab" || !trapContainer) return;
2385
+ const focusable = getFocusableElements(trapContainer);
2386
+ if (focusable.length === 0) return;
2387
+ const first = focusable[0];
2388
+ const last = focusable[focusable.length - 1];
2389
+ if (e.shiftKey) {
2390
+ if (document.activeElement === first) {
2391
+ e.preventDefault();
2392
+ last.focus();
2393
+ }
2394
+ } else {
2395
+ if (document.activeElement === last) {
2396
+ e.preventDefault();
2397
+ first.focus();
2398
+ }
2399
+ }
2400
+ }
2401
+ var FocusManager = {
2402
+ /**
2403
+ * Trap focus within a container. Tab/Shift+Tab cycle among
2404
+ * focusable elements inside the container.
2405
+ */
2406
+ trapFocus(container) {
2407
+ trapped = true;
2408
+ trapContainer = container;
2409
+ keyHandler = onKeyDown;
2410
+ document.addEventListener("keydown", keyHandler);
2411
+ const focusable = getFocusableElements(container);
2412
+ if (focusable.length > 0) focusable[0].focus();
2413
+ },
2414
+ /** Release the focus trap. */
2415
+ releaseFocus() {
2416
+ trapped = false;
2417
+ trapContainer = null;
2418
+ if (keyHandler) {
2419
+ document.removeEventListener("keydown", keyHandler);
2420
+ keyHandler = null;
2421
+ }
2422
+ },
2423
+ isTrapped() {
2424
+ return trapped;
2425
+ },
2426
+ /** Save the currently focused element for later restoration. */
2427
+ saveFocus() {
2428
+ savedFocus = document.activeElement;
2429
+ },
2430
+ /** Restore focus to the previously saved element. */
2431
+ restoreFocus() {
2432
+ if (savedFocus && typeof savedFocus.focus === "function") {
2433
+ savedFocus.focus();
2434
+ }
2435
+ savedFocus = null;
2436
+ },
2437
+ /** Reset all state (for tests). */
2438
+ reset() {
2439
+ if (keyHandler) {
2440
+ document.removeEventListener("keydown", keyHandler);
2441
+ keyHandler = null;
2442
+ }
2443
+ trapped = false;
2444
+ trapContainer = null;
2445
+ savedFocus = null;
2446
+ }
2447
+ };
2448
+
2449
+ // src/locale/Locale.ts
2450
+ var Locale = class {
2451
+ _language;
2452
+ _rtl;
2453
+ _messages;
2454
+ _numberFormat;
2455
+ _firstDayOfWeek;
2456
+ _pluralRules;
2457
+ _collator;
2458
+ _numberFormatter;
2459
+ _dateFormatter;
2460
+ _timeFormatter;
2461
+ _currencyFormatter = null;
2462
+ constructor(config2) {
2463
+ this._language = config2.language;
2464
+ this._rtl = config2.rtl ?? false;
2465
+ this._messages = { ...config2.messages };
2466
+ this._numberFormat = config2.numberFormat ?? {};
2467
+ this._firstDayOfWeek = config2.firstDayOfWeek ?? 0;
2468
+ this._pluralRules = config2.pluralRules ?? null;
2469
+ this._collator = new Intl.Collator(this._language);
2470
+ this._numberFormatter = new Intl.NumberFormat(this._language);
2471
+ this._dateFormatter = new Intl.DateTimeFormat(this._language, {
2472
+ year: "numeric",
2473
+ month: "numeric",
2474
+ day: "numeric"
2475
+ });
2476
+ this._timeFormatter = new Intl.DateTimeFormat(this._language, {
2477
+ hour: "numeric",
2478
+ minute: "numeric"
2479
+ });
2480
+ if (this._numberFormat.currency) {
2481
+ this._currencyFormatter = new Intl.NumberFormat(this._language, {
2482
+ style: "currency",
2483
+ currency: this._numberFormat.currency
2484
+ });
2485
+ }
2486
+ }
2487
+ // -----------------------------------------------------------------------
2488
+ // Accessors
2489
+ // -----------------------------------------------------------------------
2490
+ getLanguage() {
2491
+ return this._language;
2492
+ }
2493
+ isRtl() {
2494
+ return this._rtl;
2495
+ }
2496
+ getDirection() {
2497
+ return this._rtl ? "rtl" : "ltr";
2498
+ }
2499
+ getFirstDayOfWeek() {
2500
+ return this._firstDayOfWeek;
2501
+ }
2502
+ // -----------------------------------------------------------------------
2503
+ // Translation
2504
+ // -----------------------------------------------------------------------
2505
+ t(key, params) {
2506
+ let msg = this._messages[key];
2507
+ if (msg === void 0) return key;
2508
+ if (params) {
2509
+ for (const [k, v] of Object.entries(params)) {
2510
+ msg = msg.replaceAll(`{${k}}`, String(v ?? ""));
2511
+ }
2512
+ }
2513
+ return msg;
2514
+ }
2515
+ /**
2516
+ * Translate with plural support. Appends the plural category
2517
+ * to the key (e.g., 'items.one', 'items.other').
2518
+ */
2519
+ tp(key, count, params) {
2520
+ const category = this.getPlural(count);
2521
+ return this.t(`${key}.${category}`, params);
2522
+ }
2523
+ // -----------------------------------------------------------------------
2524
+ // Plural rules
2525
+ // -----------------------------------------------------------------------
2526
+ getPlural(count) {
2527
+ if (this._pluralRules) return this._pluralRules(count);
2528
+ try {
2529
+ return new Intl.PluralRules(this._language).select(count);
2530
+ } catch {
2531
+ return count === 1 ? "one" : "other";
2532
+ }
2533
+ }
2534
+ // -----------------------------------------------------------------------
2535
+ // Number formatting
2536
+ // -----------------------------------------------------------------------
2537
+ formatNumber(value) {
2538
+ return this._numberFormatter.format(value);
2539
+ }
2540
+ formatCurrency(value) {
2541
+ if (this._currencyFormatter) return this._currencyFormatter.format(value);
2542
+ return this._numberFormatter.format(value);
2543
+ }
2544
+ // -----------------------------------------------------------------------
2545
+ // Date / time formatting
2546
+ // -----------------------------------------------------------------------
2547
+ formatDate(date) {
2548
+ return this._dateFormatter.format(date);
2549
+ }
2550
+ formatTime(date) {
2551
+ return this._timeFormatter.format(date);
2552
+ }
2553
+ // -----------------------------------------------------------------------
2554
+ // Collation / sorting
2555
+ // -----------------------------------------------------------------------
2556
+ compare(a, b) {
2557
+ return this._collator.compare(a, b);
2558
+ }
2559
+ };
2560
+
2561
+ // src/locale/LocaleManager.ts
2562
+ var locales = /* @__PURE__ */ new Map();
2563
+ var activeLocale = null;
2564
+ var listeners = {};
2565
+ function fire(event, ...args) {
2566
+ (listeners[event] ?? []).forEach((fn) => fn(...args));
2567
+ }
2568
+ var LocaleManager = {
2569
+ register(locale) {
2570
+ locales.set(locale.getLanguage(), locale);
2571
+ },
2572
+ setLocale(language) {
2573
+ const locale = locales.get(language);
2574
+ if (!locale) return;
2575
+ activeLocale = locale;
2576
+ const root = document.documentElement;
2577
+ root.setAttribute("lang", language);
2578
+ root.setAttribute("dir", locale.getDirection());
2579
+ fire("localechange", locale, language);
2580
+ },
2581
+ getLocale() {
2582
+ return activeLocale;
2583
+ },
2584
+ t(key, params) {
2585
+ if (!activeLocale) return key;
2586
+ return activeLocale.t(key, params);
2587
+ },
2588
+ getDirection() {
2589
+ return activeLocale?.getDirection() ?? "ltr";
2590
+ },
2591
+ on(event, fn) {
2592
+ (listeners[event] ??= []).push(fn);
2593
+ },
2594
+ off(event, fn) {
2595
+ const list = listeners[event];
2596
+ if (!list) return;
2597
+ const idx = list.indexOf(fn);
2598
+ if (idx >= 0) list.splice(idx, 1);
2599
+ },
2600
+ reset() {
2601
+ activeLocale = null;
2602
+ locales.clear();
2603
+ for (const key of Object.keys(listeners)) listeners[key] = [];
2604
+ document.documentElement.removeAttribute("dir");
2605
+ document.documentElement.removeAttribute("lang");
2606
+ }
2607
+ };
2608
+
2609
+ // src/locale/bundles/en-US.ts
2610
+ var enUS = new Locale({
2611
+ language: "en-US",
2612
+ rtl: false,
2613
+ firstDayOfWeek: 0,
2614
+ numberFormat: { currency: "USD" },
2615
+ messages: {
2616
+ // Buttons
2617
+ "btn.ok": "OK",
2618
+ "btn.cancel": "Cancel",
2619
+ "btn.yes": "Yes",
2620
+ "btn.no": "No",
2621
+ "btn.save": "Save",
2622
+ "btn.close": "Close",
2623
+ "btn.apply": "Apply",
2624
+ "btn.reset": "Reset",
2625
+ // Grid
2626
+ "grid.noData": "No data to display",
2627
+ "grid.loading": "Loading...",
2628
+ "grid.page": "Page {page} of {total}",
2629
+ "grid.pageSize": "Items per page",
2630
+ // Form validation
2631
+ "validation.required": "This field is required",
2632
+ "validation.minLength": "Minimum length is {min} characters",
2633
+ "validation.maxLength": "Maximum length is {max} characters",
2634
+ "validation.email": "This is not a valid email address",
2635
+ "validation.range": "Value must be between {min} and {max}",
2636
+ "validation.format": "Invalid format",
2637
+ // Date picker — months
2638
+ "datepicker.months.0": "January",
2639
+ "datepicker.months.1": "February",
2640
+ "datepicker.months.2": "March",
2641
+ "datepicker.months.3": "April",
2642
+ "datepicker.months.4": "May",
2643
+ "datepicker.months.5": "June",
2644
+ "datepicker.months.6": "July",
2645
+ "datepicker.months.7": "August",
2646
+ "datepicker.months.8": "September",
2647
+ "datepicker.months.9": "October",
2648
+ "datepicker.months.10": "November",
2649
+ "datepicker.months.11": "December",
2650
+ // Date picker — days
2651
+ "datepicker.days.0": "Sunday",
2652
+ "datepicker.days.1": "Monday",
2653
+ "datepicker.days.2": "Tuesday",
2654
+ "datepicker.days.3": "Wednesday",
2655
+ "datepicker.days.4": "Thursday",
2656
+ "datepicker.days.5": "Friday",
2657
+ "datepicker.days.6": "Saturday",
2658
+ // Date picker — short days
2659
+ "datepicker.daysShort.0": "Sun",
2660
+ "datepicker.daysShort.1": "Mon",
2661
+ "datepicker.daysShort.2": "Tue",
2662
+ "datepicker.daysShort.3": "Wed",
2663
+ "datepicker.daysShort.4": "Thu",
2664
+ "datepicker.daysShort.5": "Fri",
2665
+ "datepicker.daysShort.6": "Sat",
2666
+ // MessageBox
2667
+ "messagebox.alert.title": "Alert",
2668
+ "messagebox.confirm.title": "Confirm",
2669
+ "messagebox.prompt.title": "Prompt",
2670
+ // Misc
2671
+ "loading": "Loading...",
2672
+ "error": "Error"
2673
+ }
2674
+ });
2675
+
2676
+ // src/locale/bundles/es-ES.ts
2677
+ var esES = new Locale({
2678
+ language: "es-ES",
2679
+ rtl: false,
2680
+ firstDayOfWeek: 1,
2681
+ numberFormat: { currency: "EUR" },
2682
+ messages: {
2683
+ "btn.ok": "Aceptar",
2684
+ "btn.cancel": "Cancelar",
2685
+ "btn.yes": "S\xED",
2686
+ "btn.no": "No",
2687
+ "btn.save": "Guardar",
2688
+ "btn.close": "Cerrar",
2689
+ "btn.apply": "Aplicar",
2690
+ "btn.reset": "Restablecer",
2691
+ "grid.noData": "No hay datos para mostrar",
2692
+ "grid.loading": "Cargando...",
2693
+ "grid.page": "P\xE1gina {page} de {total}",
2694
+ "grid.pageSize": "Elementos por p\xE1gina",
2695
+ "validation.required": "Este campo es obligatorio",
2696
+ "validation.minLength": "La longitud m\xEDnima es de {min} caracteres",
2697
+ "validation.maxLength": "La longitud m\xE1xima es de {max} caracteres",
2698
+ "validation.email": "No es una direcci\xF3n de correo v\xE1lida",
2699
+ "validation.range": "El valor debe estar entre {min} y {max}",
2700
+ "validation.format": "Formato inv\xE1lido",
2701
+ "datepicker.months.0": "Enero",
2702
+ "datepicker.months.1": "Febrero",
2703
+ "datepicker.months.2": "Marzo",
2704
+ "datepicker.months.3": "Abril",
2705
+ "datepicker.months.4": "Mayo",
2706
+ "datepicker.months.5": "Junio",
2707
+ "datepicker.months.6": "Julio",
2708
+ "datepicker.months.7": "Agosto",
2709
+ "datepicker.months.8": "Septiembre",
2710
+ "datepicker.months.9": "Octubre",
2711
+ "datepicker.months.10": "Noviembre",
2712
+ "datepicker.months.11": "Diciembre",
2713
+ "datepicker.days.0": "Domingo",
2714
+ "datepicker.days.1": "Lunes",
2715
+ "datepicker.days.2": "Martes",
2716
+ "datepicker.days.3": "Mi\xE9rcoles",
2717
+ "datepicker.days.4": "Jueves",
2718
+ "datepicker.days.5": "Viernes",
2719
+ "datepicker.days.6": "S\xE1bado",
2720
+ "datepicker.daysShort.0": "Dom",
2721
+ "datepicker.daysShort.1": "Lun",
2722
+ "datepicker.daysShort.2": "Mar",
2723
+ "datepicker.daysShort.3": "Mi\xE9",
2724
+ "datepicker.daysShort.4": "Jue",
2725
+ "datepicker.daysShort.5": "Vie",
2726
+ "datepicker.daysShort.6": "S\xE1b",
2727
+ "messagebox.alert.title": "Alerta",
2728
+ "messagebox.confirm.title": "Confirmar",
2729
+ "messagebox.prompt.title": "Aviso",
2730
+ "loading": "Cargando...",
2731
+ "error": "Error"
2732
+ }
2733
+ });
2734
+
2735
+ // src/locale/bundles/ar-SA.ts
2736
+ var arSA = new Locale({
2737
+ language: "ar-SA",
2738
+ rtl: true,
2739
+ firstDayOfWeek: 6,
2740
+ numberFormat: { currency: "SAR" },
2741
+ messages: {
2742
+ "btn.ok": "\u0645\u0648\u0627\u0641\u0642",
2743
+ "btn.cancel": "\u0625\u0644\u063A\u0627\u0621",
2744
+ "btn.yes": "\u0646\u0639\u0645",
2745
+ "btn.no": "\u0644\u0627",
2746
+ "btn.save": "\u062D\u0641\u0638",
2747
+ "btn.close": "\u0625\u063A\u0644\u0627\u0642",
2748
+ "btn.apply": "\u062A\u0637\u0628\u064A\u0642",
2749
+ "btn.reset": "\u0625\u0639\u0627\u062F\u0629 \u062A\u0639\u064A\u064A\u0646",
2750
+ "grid.noData": "\u0644\u0627 \u062A\u0648\u062C\u062F \u0628\u064A\u0627\u0646\u0627\u062A \u0644\u0644\u0639\u0631\u0636",
2751
+ "grid.loading": "\u062C\u0627\u0631\u064A \u0627\u0644\u062A\u062D\u0645\u064A\u0644...",
2752
+ "grid.page": "\u0635\u0641\u062D\u0629 {page} \u0645\u0646 {total}",
2753
+ "grid.pageSize": "\u0639\u0646\u0627\u0635\u0631 \u0641\u064A \u0627\u0644\u0635\u0641\u062D\u0629",
2754
+ "validation.required": "\u0647\u0630\u0627 \u0627\u0644\u062D\u0642\u0644 \u0645\u0637\u0644\u0648\u0628",
2755
+ "validation.minLength": "\u0627\u0644\u062D\u062F \u0627\u0644\u0623\u062F\u0646\u0649 \u0644\u0644\u0637\u0648\u0644 \u0647\u0648 {min} \u062D\u0631\u0641\u064B\u0627",
2756
+ "validation.maxLength": "\u0627\u0644\u062D\u062F \u0627\u0644\u0623\u0642\u0635\u0649 \u0644\u0644\u0637\u0648\u0644 \u0647\u0648 {max} \u062D\u0631\u0641\u064B\u0627",
2757
+ "validation.email": "\u0647\u0630\u0627 \u0644\u064A\u0633 \u0639\u0646\u0648\u0627\u0646 \u0628\u0631\u064A\u062F \u0625\u0644\u0643\u062A\u0631\u0648\u0646\u064A \u0635\u0627\u0644\u062D\u064B\u0627",
2758
+ "validation.range": "\u064A\u062C\u0628 \u0623\u0646 \u062A\u0643\u0648\u0646 \u0627\u0644\u0642\u064A\u0645\u0629 \u0628\u064A\u0646 {min} \u0648 {max}",
2759
+ "validation.format": "\u062A\u0646\u0633\u064A\u0642 \u063A\u064A\u0631 \u0635\u0627\u0644\u062D",
2760
+ "datepicker.months.0": "\u064A\u0646\u0627\u064A\u0631",
2761
+ "datepicker.months.1": "\u0641\u0628\u0631\u0627\u064A\u0631",
2762
+ "datepicker.months.2": "\u0645\u0627\u0631\u0633",
2763
+ "datepicker.months.3": "\u0623\u0628\u0631\u064A\u0644",
2764
+ "datepicker.months.4": "\u0645\u0627\u064A\u0648",
2765
+ "datepicker.months.5": "\u064A\u0648\u0646\u064A\u0648",
2766
+ "datepicker.months.6": "\u064A\u0648\u0644\u064A\u0648",
2767
+ "datepicker.months.7": "\u0623\u063A\u0633\u0637\u0633",
2768
+ "datepicker.months.8": "\u0633\u0628\u062A\u0645\u0628\u0631",
2769
+ "datepicker.months.9": "\u0623\u0643\u062A\u0648\u0628\u0631",
2770
+ "datepicker.months.10": "\u0646\u0648\u0641\u0645\u0628\u0631",
2771
+ "datepicker.months.11": "\u062F\u064A\u0633\u0645\u0628\u0631",
2772
+ "datepicker.days.0": "\u0627\u0644\u0623\u062D\u062F",
2773
+ "datepicker.days.1": "\u0627\u0644\u0625\u062B\u0646\u064A\u0646",
2774
+ "datepicker.days.2": "\u0627\u0644\u062B\u0644\u0627\u062B\u0627\u0621",
2775
+ "datepicker.days.3": "\u0627\u0644\u0623\u0631\u0628\u0639\u0627\u0621",
2776
+ "datepicker.days.4": "\u0627\u0644\u062E\u0645\u064A\u0633",
2777
+ "datepicker.days.5": "\u0627\u0644\u062C\u0645\u0639\u0629",
2778
+ "datepicker.days.6": "\u0627\u0644\u0633\u0628\u062A",
2779
+ "datepicker.daysShort.0": "\u0623\u062D\u062F",
2780
+ "datepicker.daysShort.1": "\u0625\u062B\u0646",
2781
+ "datepicker.daysShort.2": "\u062B\u0644\u0627",
2782
+ "datepicker.daysShort.3": "\u0623\u0631\u0628",
2783
+ "datepicker.daysShort.4": "\u062E\u0645\u064A",
2784
+ "datepicker.daysShort.5": "\u062C\u0645\u0639",
2785
+ "datepicker.daysShort.6": "\u0633\u0628\u062A",
2786
+ "messagebox.alert.title": "\u062A\u0646\u0628\u064A\u0647",
2787
+ "messagebox.confirm.title": "\u062A\u0623\u0643\u064A\u062F",
2788
+ "messagebox.prompt.title": "\u0625\u062F\u062E\u0627\u0644",
2789
+ "loading": "\u062C\u0627\u0631\u064A \u0627\u0644\u062A\u062D\u0645\u064A\u0644...",
2790
+ "error": "\u062E\u0637\u0623"
2791
+ }
2792
+ });
2793
+
2794
+ // src/util/Sanitizer.ts
2795
+ var SAFE_TAGS = /* @__PURE__ */ new Set([
2796
+ "a",
2797
+ "abbr",
2798
+ "b",
2799
+ "blockquote",
2800
+ "br",
2801
+ "caption",
2802
+ "cite",
2803
+ "code",
2804
+ "col",
2805
+ "colgroup",
2806
+ "dd",
2807
+ "del",
2808
+ "details",
2809
+ "div",
2810
+ "dl",
2811
+ "dt",
2812
+ "em",
2813
+ "figcaption",
2814
+ "figure",
2815
+ "h1",
2816
+ "h2",
2817
+ "h3",
2818
+ "h4",
2819
+ "h5",
2820
+ "h6",
2821
+ "hr",
2822
+ "i",
2823
+ "img",
2824
+ "ins",
2825
+ "kbd",
2826
+ "label",
2827
+ "li",
2828
+ "mark",
2829
+ "ol",
2830
+ "p",
2831
+ "pre",
2832
+ "q",
2833
+ "s",
2834
+ "samp",
2835
+ "small",
2836
+ "span",
2837
+ "strong",
2838
+ "sub",
2839
+ "summary",
2840
+ "sup",
2841
+ "table",
2842
+ "tbody",
2843
+ "td",
2844
+ "tfoot",
2845
+ "th",
2846
+ "thead",
2847
+ "time",
2848
+ "tr",
2849
+ "u",
2850
+ "ul",
2851
+ "var",
2852
+ "wbr"
2853
+ ]);
2854
+ var SAFE_ATTRS = /* @__PURE__ */ new Set([
2855
+ "align",
2856
+ "alt",
2857
+ "class",
2858
+ "colspan",
2859
+ "dir",
2860
+ "height",
2861
+ "href",
2862
+ "id",
2863
+ "lang",
2864
+ "name",
2865
+ "role",
2866
+ "rowspan",
2867
+ "scope",
2868
+ "src",
2869
+ "style",
2870
+ "tabindex",
2871
+ "target",
2872
+ "title",
2873
+ "type",
2874
+ "valign",
2875
+ "width",
2876
+ // ARIA
2877
+ "aria-controls",
2878
+ "aria-describedby",
2879
+ "aria-disabled",
2880
+ "aria-expanded",
2881
+ "aria-haspopup",
2882
+ "aria-hidden",
2883
+ "aria-label",
2884
+ "aria-labelledby",
2885
+ "aria-live",
2886
+ "aria-modal",
2887
+ "aria-pressed",
2888
+ "aria-selected",
2889
+ "aria-sort"
2890
+ ]);
2891
+ var SAFE_URL_PATTERN = /^(?:https?|mailto|tel|#):/i;
2892
+ var DATA_URL_PATTERN = /^data:image\/(?:png|gif|jpeg|webp|svg\+xml);base64,/i;
2893
+ function isUrlSafe(attr, value) {
2894
+ if (attr !== "href" && attr !== "src") return true;
2895
+ const trimmed = value.trim();
2896
+ if (trimmed.startsWith("#") || trimmed.startsWith("/")) return true;
2897
+ return SAFE_URL_PATTERN.test(trimmed) || DATA_URL_PATTERN.test(trimmed);
2898
+ }
2899
+ var DANGEROUS_TAGS = /* @__PURE__ */ new Set([
2900
+ "script",
2901
+ "style",
2902
+ "iframe",
2903
+ "object",
2904
+ "embed",
2905
+ "applet",
2906
+ "base",
2907
+ "link",
2908
+ "meta",
2909
+ "noscript",
2910
+ "template"
2911
+ ]);
2912
+ function sanitizeNode(node, doc) {
2913
+ if (node.nodeType === 3) {
2914
+ return doc.createTextNode(node.textContent ?? "");
2915
+ }
2916
+ if (node.nodeType !== 1) return null;
2917
+ const el = node;
2918
+ const tagName = el.tagName.toLowerCase();
2919
+ if (DANGEROUS_TAGS.has(tagName)) return null;
2920
+ if (!SAFE_TAGS.has(tagName)) {
2921
+ const frag = doc.createDocumentFragment();
2922
+ for (const child of Array.from(el.childNodes)) {
2923
+ const cleaned = sanitizeNode(child, doc);
2924
+ if (cleaned) frag.appendChild(cleaned);
2925
+ }
2926
+ return frag;
2927
+ }
2928
+ const cleanEl = doc.createElement(tagName);
2929
+ for (const attr of Array.from(el.attributes)) {
2930
+ const name = attr.name.toLowerCase();
2931
+ if (!SAFE_ATTRS.has(name)) continue;
2932
+ if (name.startsWith("on")) continue;
2933
+ if (!isUrlSafe(name, attr.value)) continue;
2934
+ cleanEl.setAttribute(name, attr.value);
2935
+ }
2936
+ for (const child of Array.from(el.childNodes)) {
2937
+ const cleaned = sanitizeNode(child, doc);
2938
+ if (cleaned) cleanEl.appendChild(cleaned);
2939
+ }
2940
+ return cleanEl;
2941
+ }
2942
+ var Sanitizer = {
2943
+ /**
2944
+ * Sanitize an HTML string by removing dangerous tags and attributes.
2945
+ * Uses a whitelist approach — only known-safe elements pass through.
2946
+ *
2947
+ * @param html - Raw HTML string
2948
+ * @returns Sanitized HTML string safe for innerHTML
2949
+ */
2950
+ sanitize(html) {
2951
+ if (!html) return "";
2952
+ if (!/</.test(html)) return html;
2953
+ const parser = new DOMParser();
2954
+ const doc = parser.parseFromString(`<body>${html}</body>`, "text/html");
2955
+ const body = doc.body;
2956
+ const frag = doc.createDocumentFragment();
2957
+ for (const child of Array.from(body.childNodes)) {
2958
+ const cleaned = sanitizeNode(child, doc);
2959
+ if (cleaned) frag.appendChild(cleaned);
2960
+ }
2961
+ const wrapper = doc.createElement("div");
2962
+ wrapper.appendChild(frag);
2963
+ return wrapper.innerHTML;
2964
+ },
2965
+ /**
2966
+ * Escape HTML special characters for safe text insertion.
2967
+ *
2968
+ * @param text - Raw text
2969
+ * @returns Escaped string safe for HTML context
2970
+ */
2971
+ escapeHtml(text) {
2972
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
2973
+ },
2974
+ /**
2975
+ * Check if an HTML string contains potentially dangerous content.
2976
+ *
2977
+ * @param html - HTML string to check
2978
+ * @returns true if the HTML appears safe
2979
+ */
2980
+ isSafe(html) {
2981
+ const dangerous = /<script|javascript:|on\w+\s*=/i;
2982
+ return !dangerous.test(html);
2983
+ }
2984
+ };
2985
+
2986
+ // src/index.ts
2987
+ var VERSION = "0.0.1";
2988
+ export {
2989
+ AriaManager,
2990
+ Base,
2991
+ ClassManager,
2992
+ Configurator,
2993
+ DestroyableUtil,
2994
+ EventBus,
2995
+ EventDomain,
2996
+ EventManager,
2997
+ ExtEvent,
2998
+ Factoryable,
2999
+ FocusManager,
3000
+ GestureRecognizer,
3001
+ Hookable,
3002
+ Identifiable,
3003
+ IdentityMap,
3004
+ Inheritable,
3005
+ KeyMap,
3006
+ Locale,
3007
+ LocaleManager,
3008
+ Observable,
3009
+ Pluggable,
3010
+ Plugin,
3011
+ Sanitizer,
3012
+ VERSION,
3013
+ alias,
3014
+ apply,
3015
+ applyIf,
3016
+ arSA,
3017
+ bind,
3018
+ camelCase,
3019
+ capitalize,
3020
+ chunk,
3021
+ clean,
3022
+ clone,
3023
+ compose,
3024
+ config,
3025
+ contains,
3026
+ createBarrier,
3027
+ createBuffered,
3028
+ createDelayed,
3029
+ createSequence,
3030
+ createThrottled,
3031
+ defer,
3032
+ define,
3033
+ difference,
3034
+ ellipsis,
3035
+ emptyFn,
3036
+ enUS,
3037
+ entries,
3038
+ equals,
3039
+ esES,
3040
+ escapeHtml,
3041
+ escapeRegex,
3042
+ findBy,
3043
+ flatten,
3044
+ flattenObject,
3045
+ format,
3046
+ freeze,
3047
+ from,
3048
+ fromEntries,
3049
+ generateId,
3050
+ getNestedValue,
3051
+ groupBy,
3052
+ htmlDecode,
3053
+ htmlEncode,
3054
+ identity,
3055
+ include,
3056
+ interceptAfter,
3057
+ interceptBefore,
3058
+ intersection,
3059
+ interval,
3060
+ isArray,
3061
+ isBoolean,
3062
+ isDefined,
3063
+ isEmpty,
3064
+ isFunction,
3065
+ isIterable,
3066
+ isNumber,
3067
+ isObject,
3068
+ isPrimitive,
3069
+ isString,
3070
+ kebabCase,
3071
+ keys,
3072
+ leftPad,
3073
+ mapKeys,
3074
+ mapValues,
3075
+ max,
3076
+ mean,
3077
+ memoize,
3078
+ merge,
3079
+ min,
3080
+ mixin,
3081
+ namespace,
3082
+ negate,
3083
+ now,
3084
+ observable,
3085
+ omit,
3086
+ once,
3087
+ override,
3088
+ partition,
3089
+ pascalCase,
3090
+ pick,
3091
+ pipe,
3092
+ pluck,
3093
+ range,
3094
+ remove,
3095
+ repeat,
3096
+ setNestedValue,
3097
+ sortBy,
3098
+ splitWords,
3099
+ sum,
3100
+ toggle,
3101
+ trim,
3102
+ uncapitalize,
3103
+ unescapeHtml,
3104
+ unflattenObject,
3105
+ unique,
3106
+ values
3107
+ };
3108
+ //# sourceMappingURL=index.js.map