tsgrid-ui 2.7.1 → 2.10.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.
Files changed (69) hide show
  1. package/CHANGELOG.md +217 -0
  2. package/dist/base.d.ts +148 -0
  3. package/dist/base.es6.js +11 -0
  4. package/dist/base.es6.js.map +1 -0
  5. package/dist/chunks/chunk-3NYH6545.js +2423 -0
  6. package/dist/chunks/chunk-3NYH6545.js.map +1 -0
  7. package/dist/chunks/chunk-6MOFFUV2.js +2305 -0
  8. package/dist/chunks/chunk-6MOFFUV2.js.map +1 -0
  9. package/dist/chunks/chunk-6UCGFWIQ.js +865 -0
  10. package/dist/chunks/chunk-6UCGFWIQ.js.map +1 -0
  11. package/dist/chunks/chunk-DXZJHS4M.js +1283 -0
  12. package/dist/chunks/chunk-DXZJHS4M.js.map +1 -0
  13. package/dist/chunks/chunk-DZSFZLV6.js +1638 -0
  14. package/dist/chunks/chunk-DZSFZLV6.js.map +1 -0
  15. package/dist/chunks/chunk-EQK6JAHT.js +33 -0
  16. package/dist/chunks/chunk-EQK6JAHT.js.map +1 -0
  17. package/dist/chunks/chunk-FAIRNXQR.js +3020 -0
  18. package/dist/chunks/chunk-FAIRNXQR.js.map +1 -0
  19. package/dist/chunks/chunk-GZFWK4LZ.js +677 -0
  20. package/dist/chunks/chunk-GZFWK4LZ.js.map +1 -0
  21. package/dist/chunks/chunk-IYF3Q7GX.js +127 -0
  22. package/dist/chunks/chunk-IYF3Q7GX.js.map +1 -0
  23. package/dist/chunks/chunk-KLJ35UAH.js +1795 -0
  24. package/dist/chunks/chunk-KLJ35UAH.js.map +1 -0
  25. package/dist/chunks/chunk-LUSNRF73.js +1212 -0
  26. package/dist/chunks/chunk-LUSNRF73.js.map +1 -0
  27. package/dist/chunks/chunk-N3GASHTI.js +1127 -0
  28. package/dist/chunks/chunk-N3GASHTI.js.map +1 -0
  29. package/dist/field.d.ts +329 -0
  30. package/dist/field.es6.js +12 -0
  31. package/dist/field.es6.js.map +1 -0
  32. package/dist/form.d.ts +162 -0
  33. package/dist/form.es6.js +15 -0
  34. package/dist/form.es6.js.map +1 -0
  35. package/dist/layout.d.ts +108 -0
  36. package/dist/layout.es6.js +14 -0
  37. package/dist/layout.es6.js.map +1 -0
  38. package/dist/locale.d.ts +30 -0
  39. package/dist/locale.es6.js +7 -0
  40. package/dist/locale.es6.js.map +1 -0
  41. package/dist/popup.d.ts +97 -0
  42. package/dist/popup.es6.js +21 -0
  43. package/dist/popup.es6.js.map +1 -0
  44. package/dist/query-CKGg5Ugv.d.ts +81 -0
  45. package/dist/sidebar.d.ts +138 -0
  46. package/dist/sidebar.es6.js +12 -0
  47. package/dist/sidebar.es6.js.map +1 -0
  48. package/dist/tabs.d.ts +63 -0
  49. package/dist/tabs.es6.js +12 -0
  50. package/dist/tabs.es6.js.map +1 -0
  51. package/dist/toolbar.d.ts +97 -0
  52. package/dist/toolbar.es6.js +12 -0
  53. package/dist/toolbar.es6.js.map +1 -0
  54. package/dist/tooltip.d.ts +330 -0
  55. package/dist/tooltip.es6.js +21 -0
  56. package/dist/tooltip.es6.js.map +1 -0
  57. package/dist/tsgrid-ui.css +2 -2
  58. package/dist/tsgrid-ui.d.ts +16 -2004
  59. package/dist/tsgrid-ui.es6.js +7751 -23830
  60. package/dist/tsgrid-ui.es6.js.map +1 -1
  61. package/dist/tsgrid-ui.es6.min.js +37 -37
  62. package/dist/tsgrid-ui.js +150 -22
  63. package/dist/tsgrid-ui.min.css +2 -2
  64. package/dist/tsgrid-ui.min.js +39 -39
  65. package/dist/tsutils-message-CogFtVtO.d.ts +82 -0
  66. package/dist/utils.d.ts +418 -0
  67. package/dist/utils.es6.js +14 -0
  68. package/dist/utils.es6.js.map +1 -0
  69. package/package.json +25 -5
@@ -0,0 +1,3020 @@
1
+ import {
2
+ lazySingleton
3
+ } from "./chunk-EQK6JAHT.js";
4
+ import {
5
+ TsUtils
6
+ } from "./chunk-3NYH6545.js";
7
+ import {
8
+ TsBase,
9
+ query
10
+ } from "./chunk-DXZJHS4M.js";
11
+
12
+ // src/tstooltip.ts
13
+ var query2 = query;
14
+ var Tooltip = class _Tooltip {
15
+ // no need to extend TsBase, as each individual tooltip extends it
16
+ static active = {};
17
+ defaults;
18
+ // any: setColor is assigned dynamically inside ColorTooltip.initControls closure
19
+ setColor;
20
+ // optional hook — overridden in subclasses; declared as method stub to allow subclass override
21
+ // any: callback parameter — caller signature varies; TsTooltip overlay options merge from multiple user sources at runtime
22
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
23
+ initControls(_overlay) {
24
+ }
25
+ constructor() {
26
+ this.defaults = {
27
+ name: null,
28
+ // name for the overlay, otherwise input id is used
29
+ html: "",
30
+ // text or html
31
+ style: "",
32
+ // additional style for the overlay
33
+ class: "",
34
+ // add class for tsg-tooltip-body
35
+ position: "top|bottom",
36
+ // can be left, right, top, bottom
37
+ draggable: false,
38
+ // if true, then tooltip can be move with mouse
39
+ align: "",
40
+ // can be: both, both:XX left, right, both, top, bottom
41
+ anchor: null,
42
+ // element it is attached to, if anchor is body, then it is context menu
43
+ contextMenu: false,
44
+ // if true, then it is context menu
45
+ anchorClass: "",
46
+ // add class for anchor when tooltip is shown
47
+ anchorStyle: "",
48
+ // add style for anchor when tooltip is shown
49
+ autoShow: false,
50
+ // if autoShow true, then tooltip will show on mouseEnter and hide on mouseLeave
51
+ autoShowOn: null,
52
+ // when options.autoShow = true, mouse event to show on
53
+ autoHideOn: null,
54
+ // when options.autoShow = true, mouse event to hide on
55
+ arrowSize: 8,
56
+ // size of the carret
57
+ screenMargin: 2,
58
+ // min margin from screen to tooltip
59
+ autoResize: true,
60
+ // auto resize based on content size and available size
61
+ margin: 1,
62
+ // distance from the anchor
63
+ offsetX: 0,
64
+ // delta for left coordinate
65
+ offsetY: 0,
66
+ // delta for top coordinate
67
+ maxWidth: null,
68
+ // max width
69
+ maxHeight: null,
70
+ // max height
71
+ hideOn: null,
72
+ // events when to hide tooltip, ['doc-click', 'tooltip-click', 'focus-change', 'select', 'item-remove', ... or any standard event on the anchor],
73
+ onThen: null,
74
+ // called when displayed
75
+ onShow: null,
76
+ // callBack when shown
77
+ onHide: null,
78
+ // callBack when hidden
79
+ onUpdate: null,
80
+ // callback when tooltip gets updated
81
+ onMove: null
82
+ // callback when tooltip is moved
83
+ };
84
+ }
85
+ static observeRemove = new MutationObserver((_mutations) => {
86
+ let cnt = 0;
87
+ Object.keys(_Tooltip.active).forEach((name) => {
88
+ const overlay = _Tooltip.active[name];
89
+ if (overlay?.displayed) {
90
+ if (!overlay.anchor || !overlay.anchor.isConnected) {
91
+ overlay.hide();
92
+ } else {
93
+ cnt++;
94
+ }
95
+ }
96
+ });
97
+ if (cnt === 0) {
98
+ _Tooltip.observeRemove.disconnect();
99
+ }
100
+ });
101
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
102
+ trigger(event, data) {
103
+ if (arguments.length == 2) {
104
+ const type = event;
105
+ event = data;
106
+ data.type = type;
107
+ }
108
+ if (event.overlay) {
109
+ return event.overlay.trigger(event);
110
+ } else {
111
+ console.log("ERROR: cannot find overlay where to trigger events");
112
+ return { finish: () => {
113
+ } };
114
+ }
115
+ }
116
+ get(name) {
117
+ if (arguments.length == 0) {
118
+ return Object.keys(_Tooltip.active);
119
+ } else if (name === true) {
120
+ return _Tooltip.active;
121
+ } else {
122
+ return _Tooltip.active[(name ?? "").replace(/[\s\.#]/g, "_")];
123
+ }
124
+ }
125
+ attach(anchorArg, textArg) {
126
+ let anchor = anchorArg;
127
+ let text = textArg;
128
+ let options;
129
+ let overlay;
130
+ const self = this;
131
+ if (arguments.length == 0) {
132
+ return;
133
+ } else if (arguments.length == 1 && anchor instanceof Object) {
134
+ options = anchor;
135
+ anchor = options.anchor;
136
+ } else if (arguments.length === 2 && typeof text === "string") {
137
+ options = { anchor, html: text };
138
+ text = options.html;
139
+ } else if (arguments.length === 2 && text != null && typeof text === "object") {
140
+ options = text;
141
+ text = options.html;
142
+ }
143
+ options = TsUtils.extend({}, this.defaults, options || {});
144
+ if (!text && options.text) text = options.text;
145
+ if (!text && options.html) text = options.html;
146
+ delete options.anchor;
147
+ let name = options.name ? options.name : anchor?.id;
148
+ if (anchor == document || anchor == null) {
149
+ anchor = document.body;
150
+ }
151
+ if (options.contextMenu) {
152
+ anchor = document.body;
153
+ name = name ?? "context-menu";
154
+ }
155
+ if (!name) {
156
+ name = "noname-" + Object.keys(_Tooltip.active).length;
157
+ console.log("NOTICE: name property is not defined for tooltip, could lead to too many instances");
158
+ }
159
+ name = name.replace(/[\s\.#]/g, "_");
160
+ if (_Tooltip.active[name]) {
161
+ overlay = _Tooltip.active[name];
162
+ overlay.prevOptions = overlay.options;
163
+ overlay.options = options;
164
+ overlay.anchor = anchor;
165
+ if (overlay.prevOptions.html != overlay.options.html || overlay.prevOptions.class != overlay.options.class || overlay.prevOptions.style != overlay.options.style) {
166
+ overlay.needsUpdate = true;
167
+ }
168
+ options = overlay.options;
169
+ Object.keys(overlay).forEach((key) => {
170
+ const val = overlay[key];
171
+ if (key.startsWith("on") && typeof val == "function") {
172
+ delete overlay[key];
173
+ }
174
+ });
175
+ } else {
176
+ overlay = new TsBase();
177
+ Object.assign(overlay, {
178
+ id: "w2overlay-" + name,
179
+ name,
180
+ options,
181
+ anchor,
182
+ self,
183
+ displayed: false,
184
+ tmp: {
185
+ observeTooltipResize: new ResizeObserver(() => {
186
+ this.resize(overlay.name);
187
+ }),
188
+ observeAnchorResize: new ResizeObserver(() => {
189
+ this.resize(overlay.name);
190
+ }),
191
+ observeAnchorMove: new MutationObserver((mutations) => {
192
+ const target = mutations[0].target;
193
+ const currRect = target.getBoundingClientRect();
194
+ const lastRect = target._lastBoundingRect;
195
+ if (!target._lastBoundingRect) {
196
+ target._lastBoundingRect = currRect;
197
+ } else if (currRect.left !== lastRect.left || currRect.top !== lastRect.top) {
198
+ this.resize(overlay.name);
199
+ target._lastBoundingRect = currRect;
200
+ }
201
+ })
202
+ },
203
+ hide() {
204
+ self.hide(name);
205
+ }
206
+ });
207
+ _Tooltip.active[name] = overlay;
208
+ }
209
+ Object.keys(overlay.options).forEach((key) => {
210
+ const val = overlay.options[key];
211
+ if (key.startsWith("on") && typeof val == "function") {
212
+ overlay[key] = val;
213
+ delete overlay.options[key];
214
+ }
215
+ });
216
+ if (options.autoShow === true) {
217
+ options.autoShowOn = options.autoShowOn ?? "mouseenter";
218
+ options.autoHideOn = options.autoHideOn ?? "mouseleave";
219
+ options.autoShow = false;
220
+ options._keep = true;
221
+ }
222
+ if (options.autoShowOn) {
223
+ const scope = "autoShow-" + overlay.name;
224
+ query2(anchor).off(`.${scope}`).on(`${options.autoShowOn}.${scope}`, (event) => {
225
+ self.show(overlay.name);
226
+ event.stopPropagation();
227
+ });
228
+ delete options.autoShowOn;
229
+ options._keep = true;
230
+ }
231
+ if (options.autoHideOn) {
232
+ const scope = "autoHide-" + overlay.name;
233
+ query2(anchor).off(`.${scope}`).on(`${options.autoHideOn}.${scope}`, (event) => {
234
+ self.hide(overlay.name);
235
+ event.stopPropagation();
236
+ });
237
+ delete options.autoHideOn;
238
+ options._keep = true;
239
+ }
240
+ overlay.off(".attach");
241
+ const ret = {
242
+ overlay,
243
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
244
+ then: (callback) => {
245
+ overlay.on("show:after.attach", (event) => {
246
+ callback(event);
247
+ });
248
+ return ret;
249
+ },
250
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
251
+ show: (callback) => {
252
+ overlay.on("show.attach", (event) => {
253
+ callback(event);
254
+ });
255
+ return ret;
256
+ },
257
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
258
+ hide: (callback) => {
259
+ overlay.on("hide.attach", (event) => {
260
+ callback(event);
261
+ });
262
+ return ret;
263
+ },
264
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
265
+ update: (callback) => {
266
+ overlay.on("update.attach", (event) => {
267
+ callback(event);
268
+ });
269
+ return ret;
270
+ },
271
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
272
+ move: (callback) => {
273
+ overlay.on("move.attach", (event) => {
274
+ callback(event);
275
+ });
276
+ return ret;
277
+ }
278
+ };
279
+ return ret;
280
+ }
281
+ update(name, html) {
282
+ const overlay = _Tooltip.active[name];
283
+ if (overlay) {
284
+ overlay.needsUpdate = true;
285
+ overlay.options.html = html;
286
+ this.show(name);
287
+ } else {
288
+ console.log(`Tooltip "${name}" is not displayed. Cannot update it.`);
289
+ }
290
+ }
291
+ show(name, extraOptions) {
292
+ if (name instanceof HTMLElement || name instanceof Object) {
293
+ let options2 = name;
294
+ if (name instanceof HTMLElement) {
295
+ options2 = extraOptions || {};
296
+ options2.anchor = name;
297
+ }
298
+ const ret = this.attach(options2);
299
+ ret.overlay.tmp.hidden = false;
300
+ query2(ret.overlay.anchor).off(".autoShow-" + ret.overlay.name).off(".autoHide-" + ret.overlay.name);
301
+ setTimeout(() => {
302
+ if (!ret.overlay.tmp.hidden) {
303
+ this.show(ret.overlay.name);
304
+ if (this.initControls) {
305
+ this.initControls(ret.overlay);
306
+ }
307
+ }
308
+ }, 1);
309
+ return ret;
310
+ }
311
+ let edata;
312
+ const self = this;
313
+ const overlay = _Tooltip.active[name.replace(/[\s\.#]/g, "_")];
314
+ if (!overlay) return;
315
+ const options = overlay.options;
316
+ if (!overlay || overlay.displayed && !overlay.needsUpdate) {
317
+ this.resize(overlay?.name);
318
+ return;
319
+ }
320
+ const position = options.position.split("|");
321
+ const isVertical = ["top", "bottom"].includes(position[0]);
322
+ let overlayStyles = options.align == "both" && isVertical ? "" : "white-space: nowrap;";
323
+ if (options.maxWidth == null && /^<div[^>]*style=["'][^"']*max-width[^"']*["'][^>]*>/i.test(options.html?.trim?.() ?? "")) {
324
+ options.maxWidth = TsUtils.getStrWidth(options.html, "", true);
325
+ }
326
+ if (options.maxWidth && TsUtils.getStrWidth(options.html, "", true) >= options.maxWidth) {
327
+ overlayStyles = "width: " + options.maxWidth + "px; white-space: inherit; overflow: auto;";
328
+ }
329
+ overlayStyles += " max-height: " + (options.maxHeight ? options.maxHeight : window.innerHeight - 4) + "px;";
330
+ if (options.html === "" || options.html == null) {
331
+ self.hide(name);
332
+ return;
333
+ } else if (overlay.box) {
334
+ edata = this.trigger("update", { target: name, overlay });
335
+ if (edata.isCancelled === true) {
336
+ if (overlay.prevOptions) {
337
+ overlay.options = overlay.prevOptions;
338
+ delete overlay.prevOptions;
339
+ }
340
+ return;
341
+ }
342
+ query2(overlay.box).find(".tsg-overlay-body").attr("style", (options.style || "") + "; " + overlayStyles).removeClass(null).addClass("tsg-overlay-body " + options.class + (options.draggable ? " tsg-draggable" : "")).html(options.html);
343
+ this.resize(overlay.name);
344
+ } else {
345
+ edata = this.trigger("show", { target: name, overlay });
346
+ if (edata.isCancelled === true) return;
347
+ query2("body").append(
348
+ // pointer-events will be re-enabled leter
349
+ `<div id="${overlay.id}" name="${name}" style="display: none; pointer-events: none" class="tsg-overlay"
350
+ data-click="stop" data-focusin="stop">
351
+ <style></style>
352
+ <div class="tsg-overlay-body tsg-eaction ${options.class} ${options.draggable ? "tsg-draggable" : ""}"
353
+ style="${options.style || ""}; ${overlayStyles}" ${options.draggable ? 'data-mousedown="startDrag|event"' : ""}>
354
+ ${options.html}
355
+ </div>
356
+ </div>`
357
+ );
358
+ overlay.box = query2("#" + TsUtils.escapeId(overlay.id))[0];
359
+ overlay.displayed = true;
360
+ const names = query2(overlay.anchor).data("tooltipName") ?? [];
361
+ names.push(name);
362
+ query2(overlay.anchor).data("tooltipName", names);
363
+ TsUtils.bindEvents(overlay.box, {});
364
+ overlay.tmp.originalCSS = "";
365
+ if (query2(overlay.anchor).length > 0) {
366
+ overlay.tmp.originalCSS = query2(overlay.anchor)[0].style.cssText;
367
+ }
368
+ this.resize(overlay.name);
369
+ }
370
+ if (options.anchorStyle) {
371
+ overlay.anchor.style.cssText += ";" + options.anchorStyle;
372
+ }
373
+ if (options.anchorClass) {
374
+ if (!(options.anchorClass == "tsg-focus" && overlay.anchor == document.body)) {
375
+ query2(overlay.anchor).addClass(options.anchorClass);
376
+ }
377
+ }
378
+ if (typeof options.hideOn == "string") options.hideOn = [options.hideOn];
379
+ if (!Array.isArray(options.hideOn)) options.hideOn = [];
380
+ Object.assign(overlay.tmp, {
381
+ scrollLeft: document.body.scrollLeft,
382
+ scrollTop: document.body.scrollTop
383
+ });
384
+ addHideEvents();
385
+ addWatchEvents(document.body);
386
+ query2(overlay.box).show();
387
+ overlay.tmp.observeTooltipResize.observe(overlay.box);
388
+ overlay.tmp.observeAnchorResize.observe(overlay.anchor);
389
+ overlay.tmp.observeAnchorMove.observe(overlay.anchor, { attributes: true });
390
+ _Tooltip.observeRemove.observe(document.body, { subtree: true, childList: true });
391
+ query2(overlay.box).css("opacity", 1).find(".tsg-overlay-body").html(options.html);
392
+ setTimeout(() => {
393
+ query2(overlay.box).css({ "pointer-events": "auto" }).data("ready", "yes");
394
+ }, 100);
395
+ TsUtils.bindEvents(query2(overlay.box).find(".tsg-eaction"), this);
396
+ delete overlay.needsUpdate;
397
+ overlay.box.overlay = overlay;
398
+ query2(overlay.box).off("mousedown.tsg-bringfront").on("mousedown.tsg-bringfront", () => {
399
+ self.bringOverlayToFront(overlay);
400
+ });
401
+ if (edata) edata.finish();
402
+ return { overlay };
403
+ function addWatchEvents(el) {
404
+ const scope = "tooltip-" + overlay.name;
405
+ let queryEl = el;
406
+ if (el.tagName == "BODY") {
407
+ queryEl = el.ownerDocument;
408
+ }
409
+ query2(queryEl).off(`.${scope}`).on(`scroll.${scope}`, (_event) => {
410
+ Object.assign(overlay.tmp, {
411
+ scrollLeft: el.scrollLeft,
412
+ scrollTop: el.scrollTop
413
+ });
414
+ self.resize(overlay.name);
415
+ });
416
+ }
417
+ function addHideEvents() {
418
+ const hide = (_event) => {
419
+ self.hide(overlay.name);
420
+ };
421
+ const $anchor = query2(overlay.anchor);
422
+ const scope = "tooltip-" + overlay.name;
423
+ query2("html").off(`.${scope}`);
424
+ if (options.hideOn.includes("doc-click")) {
425
+ if (["INPUT", "TEXTAREA"].includes(overlay.anchor.tagName)) {
426
+ $anchor.off(`.${scope}-doc`).on(`click.${scope}-doc`, (event) => {
427
+ event.stopPropagation();
428
+ });
429
+ }
430
+ query2("html").on(`click.${scope}`, hide);
431
+ }
432
+ if (options.hideOn.includes("tooltip-click")) {
433
+ query2(overlay.box).off(`click.${scope}`).on(`click.${scope}`, hide);
434
+ }
435
+ if (options.hideOn.includes("focus-change") || options.hideOn.includes("blur")) {
436
+ query2("html").on(`focusin.${scope}`, (_e) => {
437
+ if (document.activeElement != overlay.anchor) {
438
+ self.hide(overlay.name);
439
+ }
440
+ });
441
+ }
442
+ if (["INPUT", "TEXTAREA"].includes(overlay.anchor.tagName)) {
443
+ $anchor.off(`.${scope}`);
444
+ options.hideOn.forEach((event) => {
445
+ if (["doc-click", "focus-change", "blur"].indexOf(event) == -1) {
446
+ $anchor.on(`${event}.${scope}`, { once: true }, hide);
447
+ }
448
+ });
449
+ }
450
+ }
451
+ }
452
+ hide(name) {
453
+ let overlay;
454
+ if (arguments.length == 0) {
455
+ Object.keys(_Tooltip.active).forEach((name2) => {
456
+ this.hide(name2);
457
+ });
458
+ return;
459
+ }
460
+ if (name instanceof HTMLElement) {
461
+ const names2 = query2(name).data("tooltipName") ?? [];
462
+ names2.forEach((name2) => {
463
+ this.hide(name2);
464
+ });
465
+ return;
466
+ }
467
+ if (typeof name == "string") {
468
+ name = name.replace(/[\s\.#]/g, "_");
469
+ overlay = _Tooltip.active[name];
470
+ }
471
+ if (overlay?.tmp) overlay.tmp.hidden = true;
472
+ if (!overlay || !overlay.box) return;
473
+ const edata = this.trigger("hide", { target: name, overlay });
474
+ if (edata.isCancelled === true) return;
475
+ if (!overlay.options._keep) delete _Tooltip.active[name];
476
+ const scope = "tooltip-" + overlay.name;
477
+ overlay.tmp.observeTooltipResize?.disconnect();
478
+ overlay.tmp.observeAnchorResize?.disconnect();
479
+ overlay.tmp.observeAnchorMove?.disconnect();
480
+ let cnt = 0;
481
+ Object.keys(_Tooltip.active).forEach((key) => {
482
+ const overlay2 = _Tooltip.active[key];
483
+ if (overlay2?.displayed) {
484
+ cnt++;
485
+ }
486
+ });
487
+ if (cnt == 0) {
488
+ _Tooltip.observeRemove.disconnect();
489
+ }
490
+ query2("html").off(`.${scope}`);
491
+ query2(document).off(`.${scope}`);
492
+ overlay.box?.remove();
493
+ overlay.box = null;
494
+ overlay.displayed = false;
495
+ const names = query2(overlay.anchor).data("tooltipName") ?? [];
496
+ const ind = names.indexOf(overlay.name);
497
+ if (ind != -1) names.splice(names.indexOf(overlay.name), 1);
498
+ if (names.length == 0) {
499
+ query2(overlay.anchor).removeData("tooltipName");
500
+ } else {
501
+ query2(overlay.anchor).data("tooltipName", names);
502
+ }
503
+ if (overlay.options.anchorStyle) {
504
+ overlay.anchor.style.cssText = overlay.tmp.originalCSS;
505
+ }
506
+ query2(overlay.anchor).off(`.${scope}`).removeClass(overlay.options.anchorClass);
507
+ if (overlay.options.url) {
508
+ overlay.options.items.splice(0);
509
+ overlay.tmp.remote.hasMore = true;
510
+ overlay.tmp.remote.search = null;
511
+ }
512
+ edata.finish();
513
+ }
514
+ resize(name) {
515
+ const state = { moved: false, resize: false };
516
+ if (arguments.length == 0) {
517
+ Object.keys(_Tooltip.active).forEach((key) => {
518
+ const overlay2 = _Tooltip.active[key];
519
+ if (overlay2.displayed) this.resize(overlay2.name);
520
+ });
521
+ return { multiple: true };
522
+ }
523
+ const overlay = _Tooltip.active[(name ?? "").replace(/[\s\.#]/g, "_")];
524
+ const pos = this.getPosition(overlay.name);
525
+ const newPos = pos.left + "x" + pos.top;
526
+ const newSize = pos.width + "x" + pos.height;
527
+ let edata1, edata2;
528
+ if (overlay.tmp.lastPos != newPos) {
529
+ edata1 = this.trigger("move", { target: name, overlay, pos });
530
+ state.moved = true;
531
+ }
532
+ if (overlay.tmp.lastSize != newSize) {
533
+ edata2 = this.trigger("resize", { target: name, overlay, pos });
534
+ state.moved = true;
535
+ }
536
+ const qBox = query2(overlay.box).css({
537
+ left: pos.left + "px",
538
+ top: pos.top + "px"
539
+ });
540
+ qBox.then((q) => {
541
+ if (pos.width != null) {
542
+ q.css("width", pos.width + "px").find(".tsg-overlay-body").css("width", "100%");
543
+ }
544
+ if (pos.height != null) {
545
+ q.css("height", pos.height + "px").find(".tsg-overlay-body").css("height", "100%");
546
+ }
547
+ return q;
548
+ }).find(".tsg-overlay-body").removeClass("tsg-arrow-right tsg-arrow-left tsg-arrow-top tsg-arrow-bottom").addClass(pos.arrow.class).closest(".tsg-overlay").find("style:first-child").text(pos.arrow.style);
549
+ if (overlay.tmp.lastPos != newPos && edata1) {
550
+ overlay.tmp.lastPos = newPos;
551
+ edata1.finish();
552
+ }
553
+ if (overlay.tmp.lastSize != newSize && edata2) {
554
+ overlay.tmp.lastSize = newSize;
555
+ edata2.finish();
556
+ }
557
+ return state;
558
+ }
559
+ getPosition(name) {
560
+ const overlay = _Tooltip.active[name.replace(/[\s\.#]/g, "_")];
561
+ if (!overlay || !overlay.box) {
562
+ return;
563
+ }
564
+ const options = overlay.options;
565
+ if (overlay.tmp.resizedY || overlay.tmp.resizedX) {
566
+ query2(overlay.box).css({ width: "", height: "", scroll: "auto" });
567
+ }
568
+ const scrollSize = TsUtils.scrollBarSize();
569
+ const hasScrollBarX = !(document.body.scrollWidth == document.body.clientWidth);
570
+ const hasScrollBarY = !(document.body.scrollHeight == document.body.clientHeight);
571
+ const max = {
572
+ width: window.innerWidth - (hasScrollBarY ? scrollSize : 0),
573
+ height: window.innerHeight - (hasScrollBarX ? scrollSize : 0)
574
+ };
575
+ const position = options.position == "auto" ? "top|bottom|right|left".split("|") : Array.isArray(options.position) ? options.position : options.position.split("|");
576
+ const isVertical = ["top", "bottom"].includes(position[0]);
577
+ const content = overlay.box.getBoundingClientRect();
578
+ let anchor = overlay.anchor?.getBoundingClientRect?.();
579
+ const min_width = TsUtils.getStrWidth(options.html, "", true);
580
+ if (content.width < min_width) content.width = min_width;
581
+ if (overlay.anchor == document.body) {
582
+ let evt = options.originalEvent;
583
+ while (evt?.originalEvent) {
584
+ evt = evt.originalEvent;
585
+ }
586
+ const { x, y, width: width2, height: height2 } = evt ?? { x: options.x, y: options.y, width: 0, height: 10 };
587
+ anchor = {
588
+ left: x - (options.contextMenu ? 4 : 0),
589
+ top: y - (options.contextMenu ? 4 : 0),
590
+ width: width2 ?? 0,
591
+ height: height2 ?? 10,
592
+ arrow: options.contextMenu ? "none" : null
593
+ };
594
+ }
595
+ let arrowSize = options.arrowSize;
596
+ if (anchor.arrow == "none") arrowSize = 0;
597
+ if (isNaN(arrowSize)) arrowSize = this.defaults.arrowSize;
598
+ const available = {
599
+ // tipsize adjustment should be here, not in max.width/max.height
600
+ top: anchor.top - arrowSize,
601
+ bottom: max.height - arrowSize - (anchor.top + anchor.height) - (hasScrollBarX ? scrollSize : 0) - 2,
602
+ left: anchor.left,
603
+ right: max.width - (anchor.left + anchor.width) + (hasScrollBarY ? scrollSize : 0)
604
+ };
605
+ if (content.width < 22) content.width = 22;
606
+ if (content.height < 14) content.height = 14;
607
+ let left, top, width, height;
608
+ let found = "";
609
+ const arrow = {
610
+ offset: 0,
611
+ class: "",
612
+ style: `#${overlay.id} { --tip-size: ${arrowSize}px; }`
613
+ };
614
+ const adjust = { left: 0, top: 0 };
615
+ const bestFit = { posX: "", x: 0, posY: "", y: 0 };
616
+ position.forEach((pos) => {
617
+ const avail = available[pos] ?? 0;
618
+ if (["top", "bottom"].includes(pos)) {
619
+ if (!found && content.height + arrowSize / 1.893 < avail) {
620
+ found = pos;
621
+ }
622
+ if (avail > bestFit.y) {
623
+ Object.assign(bestFit, { posY: pos, y: avail });
624
+ }
625
+ }
626
+ if (["left", "right"].includes(pos)) {
627
+ if (!found && content.width + arrowSize / 1.893 < avail) {
628
+ found = pos;
629
+ }
630
+ if (avail > bestFit.x) {
631
+ Object.assign(bestFit, { posX: pos, x: avail });
632
+ }
633
+ }
634
+ });
635
+ if (!found) {
636
+ if (isVertical) {
637
+ found = bestFit.posY;
638
+ } else {
639
+ found = bestFit.posX;
640
+ }
641
+ }
642
+ if (options.autoResize) {
643
+ if (["top", "bottom"].includes(found)) {
644
+ const availFound = available[found] ?? 0;
645
+ if (content.height > availFound) {
646
+ height = availFound;
647
+ overlay.tmp.resizedY = true;
648
+ } else {
649
+ overlay.tmp.resizedY = false;
650
+ }
651
+ }
652
+ if (["left", "right"].includes(found)) {
653
+ const availFound = available[found] ?? 0;
654
+ if (content.width > availFound) {
655
+ width = availFound;
656
+ overlay.tmp.resizedX = true;
657
+ } else {
658
+ overlay.tmp.resizedX = false;
659
+ }
660
+ }
661
+ }
662
+ usePosition(found);
663
+ if (isVertical) anchorAlignment();
664
+ top = (top ?? 0) + parseFloat(String(options.offsetY * (found == "top" ? -1 : 1)));
665
+ left = (left ?? 0) + parseFloat(String(options.offsetX * (found == "left" ? -1 : 1)));
666
+ screenAdjust();
667
+ const extraTop = found == "top" ? -options.margin : found == "bottom" ? options.margin : 0;
668
+ const extraLeft = found == "left" ? -options.margin : found == "right" ? options.margin : 0;
669
+ top = Math.floor(((top ?? 0) + parseFloat(extraTop)) * 100) / 100;
670
+ left = Math.floor(((left ?? 0) + parseFloat(extraLeft)) * 100) / 100;
671
+ const retPos = { left: left ?? 0, top: top ?? 0, arrow, adjust, pos: found };
672
+ if (width != null) retPos.width = width;
673
+ if (height != null) retPos.height = height;
674
+ return retPos;
675
+ function usePosition(pos) {
676
+ arrow.class = anchor.arrow ? anchor.arrow : `tsg-arrow-${pos}`;
677
+ switch (pos) {
678
+ case "top": {
679
+ left = anchor.left + (anchor.width - (width ?? content.width)) / 2;
680
+ top = anchor.top - (height ?? content.height) - arrowSize / 1.5 + 1;
681
+ break;
682
+ }
683
+ case "bottom": {
684
+ left = anchor.left + (anchor.width - (width ?? content.width)) / 2;
685
+ top = anchor.top + anchor.height + arrowSize / 1.25 + 1;
686
+ break;
687
+ }
688
+ case "left": {
689
+ left = anchor.left - (width ?? content.width) - arrowSize / 1.2 - 1;
690
+ top = anchor.top + (anchor.height - (height ?? content.height)) / 2;
691
+ break;
692
+ }
693
+ case "right": {
694
+ left = anchor.left + anchor.width + arrowSize / 1.2 + 1;
695
+ top = anchor.top + (anchor.height - (height ?? content.height)) / 2;
696
+ break;
697
+ }
698
+ }
699
+ }
700
+ function anchorAlignment() {
701
+ if (options.align == "left") {
702
+ adjust.left = anchor.left - (left ?? 0);
703
+ left = anchor.left;
704
+ }
705
+ if (options.align == "right") {
706
+ adjust.left = anchor.left + anchor.width - (width ?? content.width) - (left ?? 0);
707
+ left = anchor.left + anchor.width - (width ?? content.width);
708
+ }
709
+ if (["top", "bottom"].includes(found) && options.align.startsWith("both")) {
710
+ const minWidth = options.align.split(":")[1] ?? 50;
711
+ if (anchor.width >= minWidth) {
712
+ left = anchor.left;
713
+ width = anchor.width;
714
+ }
715
+ }
716
+ if (options.align == "top") {
717
+ adjust.top = anchor.top - (top ?? 0);
718
+ top = anchor.top;
719
+ }
720
+ if (options.align == "bottom") {
721
+ adjust.top = anchor.top + anchor.height - (height ?? content.height) - (top ?? 0);
722
+ top = anchor.top + anchor.height - (height ?? content.height);
723
+ }
724
+ if (["left", "right"].includes(found) && options.align.startsWith("both")) {
725
+ const minHeight = options.align.split(":")[1] ?? 50;
726
+ if (anchor.height >= minHeight) {
727
+ top = anchor.top;
728
+ height = anchor.height;
729
+ }
730
+ }
731
+ }
732
+ function screenAdjust() {
733
+ let adjustArrow;
734
+ if (["left", "right"].includes(options.align) && anchor.width < (width ?? content.width) || ["top", "bottom"].includes(options.align) && anchor.height < (height ?? content.height)) {
735
+ adjustArrow = true;
736
+ }
737
+ const minLeft = found == "right" ? arrowSize : options.screenMargin;
738
+ const minTop = found == "bottom" ? arrowSize : options.screenMargin;
739
+ const maxLeft = max.width - (width ?? content.width) - (found == "left" ? arrowSize : options.screenMargin);
740
+ const maxTop = max.height - (height ?? content.height) - (found == "top" ? arrowSize : options.screenMargin) + 3;
741
+ if (["top", "bottom"].includes(found) || options.autoResize) {
742
+ if ((left ?? 0) < minLeft) {
743
+ adjustArrow = true;
744
+ adjust.left -= left ?? 0;
745
+ left = minLeft;
746
+ }
747
+ if ((left ?? 0) > maxLeft) {
748
+ adjustArrow = true;
749
+ adjust.left -= (left ?? 0) - maxLeft;
750
+ left = (left ?? 0) + maxLeft - (left ?? 0);
751
+ }
752
+ }
753
+ if (["left", "right"].includes(found) || options.autoResize) {
754
+ if ((top ?? 0) < minTop) {
755
+ adjustArrow = true;
756
+ adjust.top -= top ?? 0;
757
+ top = minTop;
758
+ }
759
+ if ((top ?? 0) > maxTop) {
760
+ adjustArrow = true;
761
+ adjust.top -= (top ?? 0) - maxTop;
762
+ top = (top ?? 0) + maxTop - (top ?? 0);
763
+ }
764
+ }
765
+ if (adjustArrow) {
766
+ const aType = isVertical ? "left" : "top";
767
+ const sType = isVertical ? "width" : "height";
768
+ arrow.offset = -adjust[aType];
769
+ const maxOffset = content[sType] / 2 - arrowSize;
770
+ if (Math.abs(arrow.offset) > maxOffset + arrowSize) {
771
+ arrow.class = "";
772
+ }
773
+ if (Math.abs(arrow.offset) > maxOffset) {
774
+ arrow.offset = arrow.offset < 0 ? -maxOffset : maxOffset;
775
+ }
776
+ arrow.style = TsUtils.stripSpaces(`#${overlay.id} .tsg-overlay-body:after,
777
+ #${overlay.id} .tsg-overlay-body:before {
778
+ --tip-size: ${arrowSize}px;
779
+ margin-${aType}: ${arrow.offset}px;
780
+ }`);
781
+ }
782
+ }
783
+ }
784
+ /**
785
+ * Move overlay node to the end of its parent (typically body) so it stacks above other .tsg-overlay siblings
786
+ * without relying on z-index. No-op if it is already the last element child.
787
+ */
788
+ bringOverlayToFront(overlay) {
789
+ if (!overlay || !overlay.box || !overlay.box.parentNode || !overlay.box.nextElementSibling) {
790
+ return;
791
+ }
792
+ overlay.box.parentNode.appendChild(overlay.box);
793
+ }
794
+ startDrag(event) {
795
+ if (event.preventDefault) {
796
+ event.preventDefault();
797
+ }
798
+ const el = query2(event.target).closest(".tsg-overlay");
799
+ const overlay = el[0]?.overlay;
800
+ if (overlay) {
801
+ this.bringOverlayToFront(overlay);
802
+ }
803
+ const initial = {
804
+ el,
805
+ x: parseFloat(el.css("left")),
806
+ // any: css(string) returns string when reading a property
807
+ y: parseFloat(el.css("top")),
808
+ pageX: event.pageX,
809
+ pageY: event.pageY,
810
+ moved: false,
811
+ _removed: false
812
+ };
813
+ query2(document).off(".tsg-drag").on("selectstart.tsg-drag, dragstart.tsg-drag", (e) => e.preventDefault()).find("body").addClass("tsg-overlay-dragging");
814
+ query2("html").off(".TsColor").on("mousemove.TsColor", mouseMove).on("mouseup.TsColor", mouseUp);
815
+ function mouseUp(_event) {
816
+ query2("html").off(".TsColor");
817
+ query2(document).off("selectstart.tsg-drag");
818
+ query2(document).off("dragstart.tsg-drag");
819
+ query2(document.body).removeClass("tsg-overlay-dragging");
820
+ if (initial["moved"]) {
821
+ const ov = initial.el[0] && initial.el[0].overlay;
822
+ if (ov) {
823
+ if (!ov.tmp) ov.tmp = {};
824
+ ov.tmp["moved"] = true;
825
+ clearTimeout(ov.tmp["_movedClearTimer"]);
826
+ ov.tmp["_movedClearTimer"] = setTimeout(() => {
827
+ if (ov.tmp) {
828
+ delete ov.tmp["moved"];
829
+ delete ov.tmp["_movedClearTimer"];
830
+ }
831
+ }, 400);
832
+ }
833
+ }
834
+ }
835
+ function mouseMove(event2) {
836
+ const divX = event2.pageX - initial.pageX;
837
+ const divY = event2.pageY - initial.pageY;
838
+ if (Math.abs(divX) > 3 || Math.abs(divY) > 3) {
839
+ initial.moved = true;
840
+ }
841
+ initial.el.css({
842
+ left: initial.x + divX + "px",
843
+ top: initial.y + divY + "px"
844
+ });
845
+ if (!initial._removed) {
846
+ initial._removed = true;
847
+ initial.el.find(":scope > .tsg-overlay-body").removeClass("tsg-arrow-right tsg-arrow-left tsg-arrow-top tsg-arrow-bottom");
848
+ }
849
+ }
850
+ }
851
+ };
852
+ var ColorTooltip = class _ColorTooltip extends Tooltip {
853
+ static custom_colors = [];
854
+ palette;
855
+ // any: index tracks keyboard position in the palette grid; tuple [row, col]
856
+ index;
857
+ constructor() {
858
+ super();
859
+ this.palette = [
860
+ ["000000", "333333", "555555", "777777", "888888", "999999", "AAAAAA", "CCCCCC", "DDDDDD", "EEEEEE", "F7F7F7", "FFFFFF"],
861
+ ["FF011B", "FF9838", "FFC300", "FFFD59", "86FF14", "14FF7A", "2EFFFC", "2693FF", "006CE7", "9B24F4", "FF21F5", "FF0099"],
862
+ ["FFEAEA", "FCEFE1", "FCF4DC", "FFFECF", "EBFFD9", "D9FFE9", "E0FFFF", "E8F4FF", "ECF4FC", "EAE6F4", "FFF5FE", "FCF0F7"],
863
+ ["F4CCCC", "FCE5CD", "FFF1C2", "FFFDA1", "D5FCB1", "B5F7D0", "BFFFFF", "D6ECFF", "CFE2F3", "D9D1E9", "FFE3FD", "FFD9F0"],
864
+ ["EA9899", "F9CB9C", "FFE48C", "F7F56F", "B9F77E", "84F0B1", "83F7F7", "B5DAFF", "9FC5E8", "B4A7D6", "FAB9F6", "FFADDE"],
865
+ ["E06666", "F6B26B", "DEB737", "E0DE51", "8FDB48", "52D189", "4EDEDB", "76ACE3", "6FA8DC", "8E7CC3", "E07EDA", "F26DBD"],
866
+ ["CC0814", "E69138", "AB8816", "B5B20E", "6BAB30", "27A85F", "1BA8A6", "3C81C7", "3D85C6", "674EA7", "A14F9D", "BF4990"],
867
+ ["99050C", "B45F17", "80650E", "737103", "395E14", "10783D", "13615E", "094785", "0A5394", "351C75", "780172", "782C5A"]
868
+ ];
869
+ this.defaults = TsUtils.extend({}, this.defaults, {
870
+ advanced: false,
871
+ transparent: true,
872
+ position: "top|bottom",
873
+ class: "tsg-white",
874
+ color: "",
875
+ updateInput: true,
876
+ arrowSize: 12,
877
+ autoResize: false,
878
+ anchorClass: "tsg-focus",
879
+ autoShowOn: "focus",
880
+ hideOn: ["doc-click", "focus-change"],
881
+ onSelect: null,
882
+ onLiveUpdate: null
883
+ });
884
+ }
885
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
886
+ attach(anchor, text) {
887
+ let options;
888
+ if (arguments.length == 1 && anchor instanceof Object) {
889
+ options = anchor;
890
+ anchor = options.anchor;
891
+ } else if (arguments.length === 2 && text != null && typeof text === "object") {
892
+ options = text;
893
+ options.anchor = anchor;
894
+ }
895
+ const prevHideOn = options.hideOn;
896
+ options = TsUtils.extend({}, this.defaults, options || {});
897
+ if (prevHideOn) {
898
+ options.hideOn = prevHideOn;
899
+ }
900
+ options.style += "; padding: 0;";
901
+ if (options.transparent && this.palette[0][1] == "333333") {
902
+ this.palette[0].splice(1, 1);
903
+ this.palette[0].push("TRANSPARENT");
904
+ }
905
+ if (!options.transparent && this.palette[0][1] != "333333") {
906
+ this.palette[0].splice(1, 0, "333333");
907
+ this.palette[0].pop();
908
+ }
909
+ if (options.color) options.color = String(options.color).toUpperCase();
910
+ if (typeof options.color === "string" && options.color.substr(0, 1) === "#") options.color = options.color.substr(1);
911
+ this.index = [-1, -1];
912
+ const ret = super.attach(options);
913
+ const overlay = ret.overlay;
914
+ overlay.options.html = this.getColorHTML(overlay.name, options);
915
+ overlay.on("show.attach", (event) => {
916
+ const overlay2 = event.detail.overlay;
917
+ const anchor2 = overlay2.anchor;
918
+ const options2 = overlay2.options;
919
+ if (["INPUT", "TEXTAREA"].includes(anchor2.tagName) && !options2.color && anchor2.value) {
920
+ overlay2.tmp["initColor"] = anchor2.value;
921
+ }
922
+ delete overlay2.newColor;
923
+ });
924
+ overlay.on("show:after.attach", (_event) => {
925
+ if (ret.overlay?.box) {
926
+ const actions = query2(ret.overlay.box).find(".tsg-eaction");
927
+ TsUtils.bindEvents(actions, this);
928
+ this.initControls(ret.overlay);
929
+ }
930
+ });
931
+ overlay.on("update:after.attach", (_event) => {
932
+ if (ret.overlay?.box) {
933
+ const actions = query2(ret.overlay.box).find(".tsg-eaction");
934
+ TsUtils.bindEvents(actions, this);
935
+ this.initControls(ret.overlay);
936
+ }
937
+ });
938
+ overlay.on("hide.attach", (event) => {
939
+ const overlay2 = event.detail.overlay;
940
+ const anchor2 = overlay2.anchor;
941
+ const color = overlay2.newColor ?? overlay2.options.color ?? "";
942
+ if (color !== "") {
943
+ if (["INPUT", "TEXTAREA"].includes(anchor2.tagName) && anchor2.value != color && overlay2.options.updateInput) {
944
+ anchor2.value = color;
945
+ }
946
+ const edata = this.trigger("select", { color, target: overlay2.name, overlay: overlay2 });
947
+ if (edata.isCancelled === true) return;
948
+ edata.finish();
949
+ }
950
+ });
951
+ ret.liveUpdate = (callback) => {
952
+ overlay.on("liveUpdate.attach", (event) => {
953
+ callback(event);
954
+ });
955
+ return ret;
956
+ };
957
+ ret.select = (callback) => {
958
+ overlay.on("select.attach", (event) => {
959
+ callback(event);
960
+ });
961
+ return ret;
962
+ };
963
+ return ret;
964
+ }
965
+ // regular panel handler, adds selection class
966
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
967
+ select(color, name) {
968
+ let target;
969
+ this.index = [-1, -1];
970
+ if (typeof name != "string") {
971
+ target = name.target;
972
+ this.index = (query2(target).attr("index") ?? "").split(":").map(Number);
973
+ name = query2(target).closest(".tsg-overlay").attr("name");
974
+ }
975
+ const overlay = this.get(name);
976
+ const edata = this.trigger("liveUpdate", { color, target: name, overlay, param: name });
977
+ if (edata.isCancelled === true) return;
978
+ if (["INPUT", "TEXTAREA"].includes(overlay.anchor.tagName) && overlay.options.updateInput) {
979
+ query2(overlay.anchor).val(color);
980
+ }
981
+ overlay.newColor = color;
982
+ query2(overlay.box).find(".tsg-color.tsg-selected").removeClass("tsg-selected");
983
+ if (target) {
984
+ query2(target).addClass("tsg-selected");
985
+ }
986
+ edata.finish();
987
+ }
988
+ // used for keyboard navigation, if any
989
+ nextColor(direction) {
990
+ const pal = this.palette;
991
+ switch (direction) {
992
+ case "up":
993
+ this.index[0]--;
994
+ break;
995
+ case "down":
996
+ this.index[0]++;
997
+ break;
998
+ case "right":
999
+ this.index[1]++;
1000
+ break;
1001
+ case "left":
1002
+ this.index[1]--;
1003
+ break;
1004
+ }
1005
+ if (this.index[0] < 0) this.index[0] = 0;
1006
+ if (this.index[0] > pal.length - 2) this.index[0] = pal.length - 2;
1007
+ if (this.index[1] < 0) this.index[1] = 0;
1008
+ if (this.index[1] > (pal[0]?.length ?? 0) - 1) this.index[1] = (pal[0]?.length ?? 1) - 1;
1009
+ return pal[this.index[0]]?.[this.index[1]];
1010
+ }
1011
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1012
+ tabClick(index, name) {
1013
+ if (typeof name != "string") {
1014
+ name = query2(name.target).closest(".tsg-overlay").attr("name");
1015
+ }
1016
+ const overlay = this.get(name);
1017
+ const tab = query2(overlay.box).find(`.tsg-color-tab:nth-child(${index})`);
1018
+ query2(overlay.box).find(".tsg-color-tab").removeClass("tsg-selected");
1019
+ query2(tab).addClass("tsg-selected");
1020
+ query2(overlay.box).find(".tsg-tab-content").hide().closest(".tsg-colors").find(".tab-" + index).show();
1021
+ }
1022
+ // generate HTML with color pallent and controls
1023
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1024
+ getColorHTML(name, options) {
1025
+ let html = `
1026
+ <div class="tsg-colors-header tsg-eaction" data-mousedown="startDrag|event">
1027
+ Colors
1028
+ </div>
1029
+ <div class="tsg-colors">
1030
+ <div class="tsg-tab-content tab-1">`;
1031
+ for (let i = 0; i < this.palette.length; i++) {
1032
+ html += '<div class="tsg-color-row">';
1033
+ for (let j = 0; j < (this.palette[i]?.length ?? 0); j++) {
1034
+ const color = this.palette[i][j];
1035
+ let border = "";
1036
+ if (color === "FFFFFF") border = "; border: 1px solid #efefef";
1037
+ html += `
1038
+ <div class="tsg-color tsg-eaction ${color === "TRANSPARENT" ? "tsg-no-color" : ""} ${options.color == color ? "tsg-selected" : ""}"
1039
+ style="background-color: #${color + border};" name="${color}" index="${i}:${j}"
1040
+ data-mousedown="select|'${color}'|event" data-mouseup="hide|${name}">&nbsp;
1041
+ </div>`;
1042
+ }
1043
+ html += "</div>";
1044
+ if (i < 2) html += '<div style="height: 8px"></div>';
1045
+ }
1046
+ html += `
1047
+ <div style="height: 8px"></div>
1048
+ <div class="tsg-colors-custom">
1049
+ ${this.getCustomColorsHTML(name)}
1050
+ </div>`;
1051
+ html += "</div>";
1052
+ html += `
1053
+ <div class="tsg-tab-content tab-2" style="display: none">
1054
+ <div class="color-info">
1055
+ <div class="color-preview-bg"><div class="color-preview"></div><div class="color-original"></div></div>
1056
+ <div class="color-part">
1057
+ <span>H</span> <input class="tsg-input" name="h" maxlength="3" max="360" tabindex="101">
1058
+ <span>R</span> <input class="tsg-input" name="r" maxlength="3" max="255" tabindex="104">
1059
+ </div>
1060
+ <div class="color-part">
1061
+ <span>S</span> <input class="tsg-input" name="s" maxlength="3" max="100" tabindex="102">
1062
+ <span>G</span> <input class="tsg-input" name="g" maxlength="3" max="255" tabindex="105">
1063
+ </div>
1064
+ <div class="color-part">
1065
+ <span>V</span> <input class="tsg-input" name="v" maxlength="3" max="100" tabindex="103">
1066
+ <span>B</span> <input class="tsg-input" name="b" maxlength="3" max="255" tabindex="106">
1067
+ </div>
1068
+ <div class="color-part opacity">
1069
+ <span>${TsUtils.lang("Opacity")}</span>
1070
+ <input class="tsg-input" name="a" maxlength="5" max="1" tabindex="107">
1071
+ </div>
1072
+ </div>
1073
+ <div class="palette" name="palette">
1074
+ <div class="palette-bg"></div>
1075
+ <div class="value1 move-x move-y"></div>
1076
+ </div>
1077
+ <div class="rainbow" name="rainbow">
1078
+ <div class="value2 move-x"></div>
1079
+ </div>
1080
+ <div class="alpha" name="alpha">
1081
+ <div class="alpha-bg"></div>
1082
+ <div class="value2 move-x"></div>
1083
+ </div>
1084
+ <div class="final" name="final">
1085
+ <span style="text-align: right"> Hex </span>
1086
+ <input class="tsg-input final" name="hex" tabindex="107" style="width: 70px" readonly>
1087
+ <div class="tsg-color tsg-color-picker tsg-eaction" data-click="pickAndUse|${name}">
1088
+ <span class="tsg-icon tsg-icon-eye-dropper"></span>
1089
+ </div>
1090
+ </div>
1091
+ </div>`;
1092
+ html += `
1093
+ <div class="tsg-color-tabs">
1094
+ <div class="tsg-color-tab tsg-selected tsg-eaction" data-click="tabClick|1|event|this"><span class="tsg-icon tsg-icon-colors"></span></div>
1095
+ <div class="tsg-color-tab tsg-eaction" data-click="tabClick|2|event|this"><span class="tsg-icon tsg-icon-settings"></span></div>
1096
+ <div style="padding: 5px; width: 100%; text-align: right;">
1097
+ ${typeof options.html == "string" ? options.html : ""}
1098
+ </div>
1099
+ </div>`;
1100
+ return html;
1101
+ }
1102
+ getCustomColorsHTML(name) {
1103
+ const options = this.get(name)?.options;
1104
+ let html = '<div class="tsg-color-row" style="min-height: 21px">';
1105
+ _ColorTooltip.custom_colors.forEach((color, i) => {
1106
+ let border = "";
1107
+ if (color === "FFFFFF") border = "; border: 1px solid #efefef";
1108
+ html += `
1109
+ <div class="tsg-color tsg-eaction ${color === "TRANSPARENT" ? "tsg-no-color" : ""} ${options.color == color ? "tsg-selected" : ""}"
1110
+ style="background-color: #${color + border};" name="${color}" index="c:${i}"
1111
+ data-mousedown="select|'${color}'|event" data-mouseup="hide|${name}">&nbsp;
1112
+ </div>`;
1113
+ });
1114
+ html += `
1115
+ <div class="tsg-color tsg-color-picker tsg-eaction" data-click="pickAndSelect|${name}|event">
1116
+ <span class="tsg-icon tsg-icon-eye-dropper"></span>
1117
+ </div>
1118
+ </div>`;
1119
+ return html;
1120
+ }
1121
+ // bind advanced tab controls
1122
+ initControls(overlay) {
1123
+ let initial;
1124
+ const self = this;
1125
+ const options = overlay.options;
1126
+ const color = options.color || overlay.tmp["initColor"];
1127
+ let rgb = TsUtils.parseColor(color);
1128
+ if (rgb == null) {
1129
+ rgb = { r: 140, g: 150, b: 160, a: 1 };
1130
+ }
1131
+ let hsv = TsUtils.rgb2hsv(rgb);
1132
+ if (options.advanced === true) {
1133
+ this.tabClick(2, overlay.name);
1134
+ }
1135
+ setColor(hsv, true, color ?? "");
1136
+ query2(overlay.box).off(".TsColor").on("contextmenu.TsColor", (event) => {
1137
+ event.preventDefault();
1138
+ }).find("input").off(".TsColor").on("change.TsColor", (event) => {
1139
+ const el = query2(event.target);
1140
+ let val = parseFloat(el.val());
1141
+ const max = parseFloat(el.attr("max"));
1142
+ if (isNaN(val)) {
1143
+ val = 0;
1144
+ el.val(0);
1145
+ }
1146
+ if (max > 1) val = parseInt(String(val));
1147
+ if (max > 0 && val > max) {
1148
+ el.val(max);
1149
+ val = max;
1150
+ }
1151
+ if (val < 0) {
1152
+ el.val(0);
1153
+ val = 0;
1154
+ }
1155
+ const name = el.attr("name");
1156
+ const color2 = {};
1157
+ if (["r", "g", "b", "a"].indexOf(name) !== -1) {
1158
+ rgb[name] = val;
1159
+ hsv = TsUtils.rgb2hsv(rgb);
1160
+ } else if (["h", "s", "v"].indexOf(name) !== -1) {
1161
+ color2[name] = val;
1162
+ }
1163
+ setColor(color2, true);
1164
+ });
1165
+ query2(overlay.box).find(".color-original").off(".TsColor").on("click.TsColor", (event) => {
1166
+ const tmp = TsUtils.parseColor(query2(event.target).css("background-color"));
1167
+ if (tmp != null) {
1168
+ rgb = tmp;
1169
+ hsv = TsUtils.rgb2hsv(rgb);
1170
+ setColor(hsv, true);
1171
+ }
1172
+ });
1173
+ const mDown = `${!TsUtils.isMobile ? "mousedown" : "touchstart"}.TsColor`;
1174
+ const mUp = `${!TsUtils.isMobile ? "mouseup" : "touchend"}.TsColor`;
1175
+ const mMove = `${!TsUtils.isMobile ? "mousemove" : "touchmove"}.TsColor`;
1176
+ query2(overlay.box).find(".palette, .rainbow, .alpha").off(".TsColor").on(`${mDown}.TsColor`, mouseDown);
1177
+ this.setColor = setColor;
1178
+ return;
1179
+ function setColor(color2, fullUpdate, initial2) {
1180
+ if (color2.h != null) hsv.h = color2.h;
1181
+ if (color2.s != null) hsv.s = color2.s;
1182
+ if (color2.v != null) hsv.v = color2.v;
1183
+ if (color2.a != null) {
1184
+ rgb.a = color2.a;
1185
+ hsv.a = color2.a;
1186
+ }
1187
+ rgb = TsUtils.hsv2rgb(hsv);
1188
+ const rgba = "rgba(" + rgb.r + "," + rgb.g + "," + rgb.b + "," + rgb.a + ")";
1189
+ const cl = [
1190
+ Number(rgb.r).toString(16).toUpperCase(),
1191
+ Number(rgb.g).toString(16).toUpperCase(),
1192
+ Number(rgb.b).toString(16).toUpperCase(),
1193
+ Math.round(Number(rgb.a) * 255).toString(16).toUpperCase()
1194
+ ];
1195
+ cl.forEach((item, ind) => {
1196
+ if (item.length === 1) cl[ind] = "0" + item;
1197
+ });
1198
+ let newColor = cl[0] + cl[1] + cl[2] + cl[3];
1199
+ if (rgb.a === 1) {
1200
+ newColor = cl[0] + cl[1] + cl[2];
1201
+ }
1202
+ query2(overlay.box).find(".color-preview").css("background-color", "#" + newColor);
1203
+ query2(overlay.box).find("input").each((el) => {
1204
+ if (el.name) {
1205
+ if (rgb[el.name] != null) el.value = String(rgb[el.name]);
1206
+ if (hsv[el.name] != null) el.value = String(hsv[el.name]);
1207
+ if (el.name === "a") el.value = String(rgb.a);
1208
+ if (el.name == "hex") el.value = newColor;
1209
+ if (el.name == "rgb") el.value = rgba;
1210
+ }
1211
+ });
1212
+ if (initial2 != null) {
1213
+ const color3 = overlay.tmp["initColor"] || newColor;
1214
+ query2(overlay.box).find(".color-original").css("background-color", "#" + color3);
1215
+ query2(overlay.box).find(".tsg-color.tsg-selected").removeClass("tsg-selected");
1216
+ query2(overlay.box).find(`.tsg-colors [name="${color3}"], .tsg-colors [name="${initial2}"]`).addClass("tsg-selected");
1217
+ if (newColor.length == 8) {
1218
+ self.tabClick(2, overlay.name);
1219
+ }
1220
+ } else {
1221
+ self.select(newColor, overlay.name);
1222
+ }
1223
+ if (fullUpdate) {
1224
+ updateSliders();
1225
+ refreshPalette();
1226
+ }
1227
+ }
1228
+ function updateSliders() {
1229
+ const el1 = query2(overlay.box).find(".palette .value1");
1230
+ const el2 = query2(overlay.box).find(".rainbow .value2");
1231
+ const el3 = query2(overlay.box).find(".alpha .value2");
1232
+ if (!el1[0] || !el2[0] || !el3[0]) return;
1233
+ const offset1 = el1[0].clientWidth / 2;
1234
+ const offset2 = el2[0].clientWidth / 2;
1235
+ el1.css({
1236
+ "left": hsv.s * 150 / 100 - offset1 + "px",
1237
+ "top": (100 - hsv.v) * 125 / 100 - offset1 + "px"
1238
+ });
1239
+ el2.css("left", hsv.h / (360 / 150) - offset2 + "px");
1240
+ el3.css("left", rgb.a * 150 - offset2 + "px");
1241
+ }
1242
+ function refreshPalette() {
1243
+ const cl = TsUtils.hsv2rgb(hsv.h, 100, 100);
1244
+ const rgb2 = `${cl.r},${cl.g},${cl.b}`;
1245
+ query2(overlay.box).find(".palette").css("background-image", `linear-gradient(90deg, rgba(${rgb2},0) 0%, rgba(${rgb2},1) 100%)`);
1246
+ }
1247
+ function mouseDown(event) {
1248
+ const el = query2(this).find(".value1, .value2");
1249
+ const offset = el.prop("clientWidth") / 2;
1250
+ if (el.hasClass("move-x")) el.css({ left: event.offsetX - offset + "px" });
1251
+ if (el.hasClass("move-y")) el.css({ top: event.offsetY - offset + "px" });
1252
+ initial = {
1253
+ el,
1254
+ x: event.pageX,
1255
+ y: event.pageY,
1256
+ width: el.prop("parentNode").clientWidth,
1257
+ height: el.prop("parentNode").clientHeight,
1258
+ left: parseInt(el.css("left")),
1259
+ top: parseInt(el.css("top"))
1260
+ };
1261
+ mouseMove(event);
1262
+ query2("html").off(".TsColor").on(mMove, mouseMove).on(mUp, mouseUp);
1263
+ }
1264
+ function mouseUp(_event) {
1265
+ query2("html").off(".TsColor");
1266
+ }
1267
+ function mouseMove(event) {
1268
+ const el = initial.el;
1269
+ const divX = event.pageX - initial.x;
1270
+ const divY = event.pageY - initial.y;
1271
+ let newX = initial.left + divX;
1272
+ let newY = initial.top + divY;
1273
+ const offset = el.prop("clientWidth") / 2;
1274
+ if (newX < -offset) newX = -offset;
1275
+ if (newY < -offset) newY = -offset;
1276
+ if (newX > initial.width - offset) newX = initial.width - offset;
1277
+ if (newY > initial.height - offset) newY = initial.height - offset;
1278
+ if (el.hasClass("move-x")) el.css({ left: newX + "px" });
1279
+ if (el.hasClass("move-y")) el.css({ top: newY + "px" });
1280
+ const name = query2(el.get(0).parentNode).attr("name");
1281
+ const x = parseInt(el.css("left")) + offset;
1282
+ const y = parseInt(el.css("top")) + offset;
1283
+ if (name === "palette") {
1284
+ setColor({
1285
+ s: Math.round(x / initial.width * 100),
1286
+ v: Math.round(100 - y / initial.height * 100)
1287
+ });
1288
+ }
1289
+ if (name === "rainbow") {
1290
+ const h = Math.round(360 / 150 * x);
1291
+ setColor({ h });
1292
+ refreshPalette();
1293
+ }
1294
+ if (name === "alpha") {
1295
+ setColor({ a: parseFloat(Number(x / 150).toFixed(2)) });
1296
+ }
1297
+ }
1298
+ }
1299
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1300
+ addCustomColor(color, _name) {
1301
+ if (typeof color == "string" && color.substr(0, 1) == "#" && [7, 9].includes(color.length)) {
1302
+ color = color.substr(1).toUpperCase();
1303
+ const custom = _ColorTooltip.custom_colors;
1304
+ if (custom.includes(color)) {
1305
+ custom.splice(custom.indexOf(color), 1);
1306
+ }
1307
+ if (custom.length >= 5) {
1308
+ custom.pop();
1309
+ }
1310
+ custom.unshift(color);
1311
+ }
1312
+ return color;
1313
+ }
1314
+ // any: callback parameter — caller signature varies; TsTooltip overlay options merge from multiple user sources at runtime
1315
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1316
+ async pickAndSelect(name, event) {
1317
+ const color = await this.pickColor();
1318
+ if (typeof color == "string" && color.substr(0, 1) == "#" && [7, 9].includes(color.length)) {
1319
+ this.addCustomColor(color, name);
1320
+ const cnt = query2(event.target).closest(".tsg-colors-custom");
1321
+ cnt.html(this.getCustomColorsHTML(name));
1322
+ TsUtils.bindEvents(cnt.find(".tsg-eaction"), this);
1323
+ this.select(color.substr(1), name);
1324
+ }
1325
+ }
1326
+ async pickAndUse(_name) {
1327
+ const color = await this.pickColor();
1328
+ if (typeof color == "string" && color.substr(0, 1) == "#" && [7, 9].includes(color.length)) {
1329
+ const hsv = TsUtils.rgb2hsv(TsUtils.parseColor(color));
1330
+ this.setColor(hsv, true);
1331
+ }
1332
+ }
1333
+ async pickColor() {
1334
+ const win = window;
1335
+ if (!win.EyeDropper) {
1336
+ console.error("EyeDropper API is not supported in this browser.");
1337
+ return;
1338
+ }
1339
+ const eyeDropper = new win.EyeDropper();
1340
+ try {
1341
+ const result = await eyeDropper.open();
1342
+ return result.sRGBHex;
1343
+ } catch (err) {
1344
+ console.error("Error picking color:", err);
1345
+ }
1346
+ return "";
1347
+ }
1348
+ };
1349
+ var MenuTooltip = class extends Tooltip {
1350
+ constructor() {
1351
+ super();
1352
+ this.defaults = TsUtils.extend({}, this.defaults, {
1353
+ type: "normal",
1354
+ // can be normal, radio, check
1355
+ items: [],
1356
+ selected: null,
1357
+ // current selected
1358
+ render: null,
1359
+ spinner: false,
1360
+ msgNoItems: TsUtils.lang("No items found"),
1361
+ topHTML: "",
1362
+ menuStyle: "",
1363
+ search: false,
1364
+ // search input inside tooltip
1365
+ filter: false,
1366
+ // will apply filter, if anchor is INPUT or TEXTAREA
1367
+ match: "contains",
1368
+ // is, begins, ends, contains, regexp
1369
+ markSearch: false,
1370
+ prefilter: false,
1371
+ altRows: false,
1372
+ arrowSize: 10,
1373
+ align: "left",
1374
+ position: "bottom|top",
1375
+ class: "tsg-white",
1376
+ anchorClass: "tsg-focus",
1377
+ autoShowOn: "focus",
1378
+ hideOn: ["doc-click", "focus-change", "select"],
1379
+ // also can 'item-remove'
1380
+ onSelect: null,
1381
+ onSubMenu: null,
1382
+ onRemove: null,
1383
+ onTooltip: null,
1384
+ onMouseEnter: null,
1385
+ onMouseLeave: null
1386
+ });
1387
+ }
1388
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1389
+ attach(anchor, text) {
1390
+ let options;
1391
+ if (arguments.length == 1 && anchor instanceof Object) {
1392
+ options = anchor;
1393
+ anchor = options.anchor;
1394
+ } else if (arguments.length === 2 && text != null && typeof text === "object") {
1395
+ options = text;
1396
+ options.anchor = anchor;
1397
+ }
1398
+ const prevHideOn = options.hideOn;
1399
+ options = TsUtils.extend({}, this.defaults, options || {});
1400
+ if (prevHideOn) {
1401
+ options.hideOn = prevHideOn;
1402
+ }
1403
+ options.style += "; padding: 0;";
1404
+ if (options.items == null) {
1405
+ options.items = [];
1406
+ }
1407
+ if (options.cacheMax <= 0) {
1408
+ console.log(`The option "cacheMax" is ${options.cacheMax} but should be more than 0`);
1409
+ }
1410
+ options.items = TsUtils.normMenu(options.items, options);
1411
+ options.html = this.getMenuHTML(options);
1412
+ const ret = super.attach(options);
1413
+ const overlay = ret.overlay;
1414
+ overlay.on("show:after.attach, update:after.attach", (_event) => {
1415
+ if (ret.overlay?.box) {
1416
+ let search = "";
1417
+ overlay.selected = overlay.options.selected;
1418
+ const index = overlay.anchor.dataset?.selectedIndex;
1419
+ if (overlay.options.selected !== false && overlay.options.selected !== -1 || index != null) {
1420
+ if (["INPUT", "TEXTAREA"].includes(overlay.anchor.tagName)) {
1421
+ search = overlay.anchor.value;
1422
+ overlay.selected = null;
1423
+ if (index != null) {
1424
+ overlay.selected = index;
1425
+ }
1426
+ }
1427
+ }
1428
+ const actions = query2(ret.overlay.box).find(".tsg-eaction");
1429
+ if (["INPUT", "TEXTAREA"].includes(overlay.anchor.tagName)) {
1430
+ overlay.tmp._new_search = false;
1431
+ query2(overlay.anchor).on("input.search-trigger", () => {
1432
+ overlay.tmp._new_search = true;
1433
+ query2(overlay.anchor).off("input.search-trigger");
1434
+ });
1435
+ }
1436
+ TsUtils.bindEvents(actions, this);
1437
+ this.applyFilter(overlay.name, null, search, void 0).then((data) => {
1438
+ if (!Tooltip.active[overlay.name]?.displayed) {
1439
+ return;
1440
+ }
1441
+ this.getActiveChain(overlay.name, options.items);
1442
+ overlay.tmp.searchCount = data.count;
1443
+ overlay.tmp.search = data.search;
1444
+ if (options.prefilter || search !== "") {
1445
+ if (data.count === 0 || !this.getActiveChain(overlay.name, options.items).includes(overlay.selected)) {
1446
+ overlay.selected = null;
1447
+ }
1448
+ this.refreshSearch(overlay.name);
1449
+ }
1450
+ this.initControls(ret.overlay);
1451
+ this.refreshIndex(overlay.name, true);
1452
+ });
1453
+ }
1454
+ });
1455
+ overlay.next = () => {
1456
+ const chain = this.getActiveChain(overlay.name);
1457
+ if (overlay.selected == null || String(overlay.selected).length == 0) {
1458
+ overlay.selected = chain[0];
1459
+ } else {
1460
+ const ind = chain.indexOf(String(overlay.selected));
1461
+ if (ind == -1) {
1462
+ overlay.selected = chain[0];
1463
+ }
1464
+ if (ind < chain.length - 1) {
1465
+ overlay.selected = chain[ind + 1];
1466
+ }
1467
+ }
1468
+ this.refreshIndex(overlay.name);
1469
+ this.showTooltip(overlay.name);
1470
+ };
1471
+ overlay.prev = () => {
1472
+ const chain = this.getActiveChain(overlay.name);
1473
+ if (overlay.selected == null || String(overlay.selected).length == 0) {
1474
+ overlay.selected = chain[chain.length - 1];
1475
+ } else {
1476
+ const ind = chain.indexOf(String(overlay.selected));
1477
+ if (ind == -1) {
1478
+ overlay.selected = chain[chain.length - 1];
1479
+ }
1480
+ if (ind > 0) {
1481
+ overlay.selected = chain[ind - 1];
1482
+ }
1483
+ }
1484
+ this.refreshIndex(overlay.name);
1485
+ this.showTooltip(overlay.name);
1486
+ };
1487
+ overlay.click = () => {
1488
+ query2(overlay.box).find(".tsg-selected").each((el) => {
1489
+ el.click();
1490
+ });
1491
+ };
1492
+ overlay.on("hide:after.attach", (_event) => {
1493
+ TsTooltip.hide(overlay.name + "-tooltip");
1494
+ });
1495
+ ret.select = (callback) => {
1496
+ overlay.on("select.attach", (event) => {
1497
+ callback(event);
1498
+ });
1499
+ return ret;
1500
+ };
1501
+ ret.remove = (callback) => {
1502
+ overlay.on("remove.attach", (event) => {
1503
+ callback(event);
1504
+ });
1505
+ return ret;
1506
+ };
1507
+ ret.subMenu = (callback) => {
1508
+ overlay.on("subMenu.attach", (event) => {
1509
+ callback(event);
1510
+ });
1511
+ return ret;
1512
+ };
1513
+ return ret;
1514
+ }
1515
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1516
+ update(name, items) {
1517
+ const overlay = Tooltip.active[name];
1518
+ if (overlay) {
1519
+ const options = overlay.options;
1520
+ if (options.items != items) {
1521
+ options.items = items;
1522
+ }
1523
+ const menuHTML = this.getMenuHTML(options);
1524
+ if (options.html != menuHTML) {
1525
+ options.html = menuHTML;
1526
+ overlay.needsUpdate = true;
1527
+ this.show(name);
1528
+ }
1529
+ } else {
1530
+ console.log(`Tooltip "${name}" is not displayed. Cannot update it.`);
1531
+ }
1532
+ }
1533
+ // any: callback parameter — caller signature varies; TsTooltip overlay options merge from multiple user sources at runtime
1534
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1535
+ initControls(overlay) {
1536
+ let mdown = "mousedown";
1537
+ let mclick = "click";
1538
+ if (TsUtils.isMobile) {
1539
+ mdown = "touchstart";
1540
+ mclick = "touchend";
1541
+ }
1542
+ query2(overlay.box).find(".tsg-menu:not(.tsg-sub-menu)").off(".TsMenu").on("contextmenu.TsMenu", (event) => {
1543
+ event.preventDefault();
1544
+ }).on(`${mdown}.TsMenu`, { delegate: ".tsg-menu-item" }, (event) => {
1545
+ const dt = event.delegate.dataset;
1546
+ const parents = query2(event.delegate).closest(".tsg-menu").data("parents");
1547
+ this.menuDown(overlay, event, dt.index, parents);
1548
+ if (TsUtils.isMobile) {
1549
+ event.preventDefault();
1550
+ }
1551
+ }).on(`${mclick}.TsMenu`, { delegate: ".tsg-menu-item" }, (event) => {
1552
+ const dt = event.delegate.dataset;
1553
+ const parents = query2(event.delegate).closest(".tsg-menu").data("parents");
1554
+ this.menuClick(overlay, event, parseInt(dt["index"] ?? "0"), parents);
1555
+ }).find(".tsg-menu-item").off(".TsMenu").on("mouseEnter.TsMenu", (event) => {
1556
+ const dt = event.target.dataset;
1557
+ const item = overlay.options.items[dt["index"] ?? ""];
1558
+ const edata = this.trigger("mouseEnter", { overlay, item, originalEvent: event });
1559
+ if (edata.isCancelled) {
1560
+ return;
1561
+ }
1562
+ const tooltip = item?.tooltip;
1563
+ if (tooltip && dt["hassubmenu"] != "yes") {
1564
+ this.showTooltip(overlay.name, { tooltip, anchor: event.target });
1565
+ }
1566
+ const _menu = query2(event.target).closest(".tsg-menu").get(0);
1567
+ if (_menu._evt && _menu._evt.target != event.target) {
1568
+ this.closeSubMenu(_menu._evt);
1569
+ }
1570
+ if (dt["hassubmenu"] == "yes") {
1571
+ const _evt = {
1572
+ index: parseInt(dt["index"] ?? "0"),
1573
+ parents: _menu.dataset.parents !== "" ? _menu.dataset.parents.split("-").map((ind) => parseInt(ind)) : [],
1574
+ target: event.target,
1575
+ originalEvent: event,
1576
+ overlay
1577
+ };
1578
+ _menu._evt = _evt;
1579
+ this.openSubMenu(_evt);
1580
+ }
1581
+ edata.finish();
1582
+ }).on("mouseLeave.TsMenu", (event) => {
1583
+ const dt = event.target.dataset;
1584
+ const item = overlay.options.items[dt["index"] ?? ""];
1585
+ const edata = this.trigger("mouseLeave", { overlay, item, originalEvent: event });
1586
+ if (edata.isCancelled) {
1587
+ return;
1588
+ }
1589
+ TsTooltip.hide(overlay.name + "-tooltip");
1590
+ edata.finish();
1591
+ }).find(".menu-help").off(".TsMenu").on("mouseEnter.TsMenu", (event) => {
1592
+ const target = event.target;
1593
+ const dt = target.parentNode?.parentNode;
1594
+ const tooltip = overlay.options.items[dt.dataset?.index]?.help;
1595
+ if (tooltip) {
1596
+ TsTooltip.show({
1597
+ name: overlay.name + "-help-tp",
1598
+ anchor: event.target,
1599
+ html: tooltip,
1600
+ position: "right|left",
1601
+ hideOn: ["doc-click"]
1602
+ });
1603
+ }
1604
+ }).on("mouseLeave.TsMenu", (_event) => {
1605
+ TsTooltip.hide(overlay.name + "-help-tp");
1606
+ });
1607
+ if (["INPUT", "TEXTAREA"].includes(overlay.anchor.tagName)) {
1608
+ query2(overlay.anchor).off(".TsMenu").on("input.TsMenu", (_event) => {
1609
+ }).on("keyup.TsMenu", (event) => {
1610
+ event._searchType = "filter";
1611
+ this.keyUp(overlay, event);
1612
+ });
1613
+ }
1614
+ if (overlay.options.search) {
1615
+ query2(overlay.box).find("#menu-search").off(".TsMenu").on("keyup.TsMenu", (event) => {
1616
+ event._searchType = "search";
1617
+ this.keyUp(overlay, event);
1618
+ });
1619
+ }
1620
+ }
1621
+ // any: callback parameter — caller signature varies; TsTooltip overlay options merge from multiple user sources at runtime
1622
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1623
+ getCurrent(name, id) {
1624
+ const overlay = Tooltip.active[name.replace(/[\s\.#]/g, "_")];
1625
+ const options = overlay.options;
1626
+ const selected = String(id ? id : overlay.selected || "").split("-");
1627
+ if (selected?.[0] === "") {
1628
+ selected.shift();
1629
+ }
1630
+ const last = selected.length - 1;
1631
+ let index = selected[last];
1632
+ const parents = selected.slice(0, selected.length - 1).join("-");
1633
+ index = TsUtils.isInt(index) ? parseInt(index) : 0;
1634
+ let items = options.items;
1635
+ selected.forEach((id2, ind) => {
1636
+ if (ind < selected.length - 1) {
1637
+ items = items[id2].items;
1638
+ }
1639
+ });
1640
+ return { last, index, items, item: items?.[index], parents };
1641
+ }
1642
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1643
+ getMenuHTML(options) {
1644
+ if (options.spinner) {
1645
+ return `
1646
+ <div class="tsg-menu">
1647
+ <div class="tsg-no-items">
1648
+ <div class="tsg-spinner"></div>
1649
+ ${TsUtils.lang("Loading...")}
1650
+ </div>
1651
+ </div>`;
1652
+ }
1653
+ const parents = options.parents ?? [];
1654
+ let items = options.items;
1655
+ if (!Array.isArray(items)) items = [];
1656
+ let count = 0;
1657
+ let icon = null;
1658
+ let topHTML = "";
1659
+ if (options.search) {
1660
+ topHTML += `
1661
+ <div class="tsg-menu-search">
1662
+ <span class="tsg-icon tsg-icon-search"></span>
1663
+ <input id="menu-search" class="tsg-input" type="text"/>
1664
+ </div>`;
1665
+ items.forEach((item) => item.hidden = false);
1666
+ }
1667
+ if (options.topHTML) {
1668
+ topHTML += `<div class="tsg-menu-top">${options.topHTML}</div>`;
1669
+ }
1670
+ let menu_html = `
1671
+ ${topHTML}
1672
+ <div class="tsg-menu" style="${options.menuStyle}" data-parents="${parents.join("-")}">
1673
+ `;
1674
+ items.forEach((mitem, f) => {
1675
+ icon = mitem.icon;
1676
+ const index = (parents.length > 0 ? parents.join("-") + "-" : "") + f;
1677
+ if (icon == null) icon = null;
1678
+ if (["radio", "check"].includes(options.type) && !Array.isArray(mitem.items) && mitem.group !== false) {
1679
+ if (mitem.checked === true) icon = "tsg-icon-check";
1680
+ else icon = "tsg-icon-empty";
1681
+ }
1682
+ if (mitem.hidden !== true) {
1683
+ let txt = mitem.text;
1684
+ let icon_dsp = "";
1685
+ if (typeof options.render === "function") txt = options.render(mitem, options);
1686
+ if (typeof txt == "function") txt = txt(mitem, options);
1687
+ if (icon) {
1688
+ const first = String(icon).trim().slice(0, 1);
1689
+ if (first == "#") {
1690
+ icon = `<span class="tsg-icon tsg-icon-empty" style="background-color: ${icon}"></span>`;
1691
+ } else if (first !== "<") {
1692
+ icon = `<span class="tsg-icon ${icon}"></span>`;
1693
+ }
1694
+ icon_dsp = `<div class="menu-icon">${icon}</div>`;
1695
+ }
1696
+ if (mitem.removable == null && mitem.remove != null) {
1697
+ mitem.removable = mitem.remove;
1698
+ }
1699
+ if (mitem.type !== "break" && txt != null && txt !== "" && String(txt).substr(0, 2) != "--") {
1700
+ const classes = ["tsg-menu-item"];
1701
+ if (options.altRows == true) {
1702
+ classes.push(count % 2 === 0 ? "tsg-even" : "tsg-odd");
1703
+ }
1704
+ let colspan = 1;
1705
+ if (icon_dsp === "") colspan++;
1706
+ if (mitem.count == null && mitem.hotkey == null && mitem.removable !== true && mitem.items == null) colspan++;
1707
+ if (mitem.tooltip == null && mitem.hint != null) mitem.tooltip = mitem.hint;
1708
+ let count_dsp = "";
1709
+ if (mitem.removable === true) {
1710
+ count_dsp = '<span class="menu-remove">x</span>';
1711
+ } else if (mitem.items != null) {
1712
+ classes.push("has-sub-menu");
1713
+ count_dsp = '<span style="background-color: transparent; border: transparent; box-shadow: none;"></span>';
1714
+ } else {
1715
+ if (mitem.count != null) count_dsp += "<span>" + mitem.count + "</span>";
1716
+ if (mitem.hotkey != null) count_dsp += '<span class="menu-hotkey">' + mitem.hotkey + "</span>";
1717
+ if (mitem.help != null) count_dsp += '<span class="menu-help">?</span>';
1718
+ }
1719
+ if (mitem.disabled === true) classes.push("tsg-disabled");
1720
+ if (mitem._noSearchInside === true) classes.push("tsg-no-search-inside");
1721
+ menu_html += `
1722
+ <div index="${index}" class="${classes.join(" ")}" style="${mitem.style ? mitem.style : ""}"
1723
+ data-index="${f}" data-hasSubmenu="${mitem.items != null ? "yes" : ""}">
1724
+ <div style="width: ${parseInt(mitem.indent ?? 0)}px"></div>
1725
+ ${icon_dsp}
1726
+ <div class="menu-text" colspan="${colspan}">${TsUtils.lang(txt)}</div>
1727
+ <div class="menu-extra">${mitem.extra ?? ""}${count_dsp}</div>
1728
+ </div>`;
1729
+ count++;
1730
+ } else {
1731
+ const divText = (txt ?? "").replace(/^-+/g, "");
1732
+ menu_html += `
1733
+ <div index="${index}" class="tsg-menu-divider ${divText != "" ? "has-text" : ""}">
1734
+ <div class="line"></div>
1735
+ ${divText ? `<div class="text">${divText}</div>` : ""}
1736
+ </div>`;
1737
+ }
1738
+ }
1739
+ items[f] = mitem;
1740
+ });
1741
+ if (count === 0 && options.msgNoItems) {
1742
+ const overlay = Tooltip.active[options.name.replace(/[\s\.#]/g, "_")];
1743
+ const remote = overlay?.tmp["remote"];
1744
+ let msg = options.msgNoItems;
1745
+ if (options.url) {
1746
+ if (count == 0 && remote?.hasMore === false) {
1747
+ msg = options.msgNoItems;
1748
+ } else {
1749
+ msg = options.msgSearch;
1750
+ }
1751
+ }
1752
+ menu_html += `
1753
+ <div class="tsg-no-items">
1754
+ ${TsUtils.lang(msg)}
1755
+ </div>`;
1756
+ }
1757
+ menu_html += "</div>";
1758
+ return menu_html;
1759
+ }
1760
+ // any: callback parameter — caller signature varies; TsTooltip overlay options merge from multiple user sources at runtime
1761
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1762
+ openSubMenu(event) {
1763
+ const anchor = query2(event.originalEvent.target).get(0);
1764
+ const { overlay } = event;
1765
+ const { items } = overlay.options;
1766
+ const mitem = items[event.index];
1767
+ let _items = [];
1768
+ if (typeof mitem.items == "function") {
1769
+ _items = mitem.items(mitem);
1770
+ } else if (Array.isArray(mitem.items)) {
1771
+ _items = mitem.items;
1772
+ }
1773
+ const prev = TsMenu.get(overlay.name + "-submenu");
1774
+ if (prev) {
1775
+ prev.hide();
1776
+ }
1777
+ query2(event.target).addClass("expanded");
1778
+ TsMenu.show({
1779
+ name: overlay.name + "-submenu",
1780
+ anchor,
1781
+ items: _items,
1782
+ class: overlay.options.class + " " + mitem.overlay?.class,
1783
+ offsetX: -7,
1784
+ arrowSize: 0,
1785
+ parentOverlay: overlay,
1786
+ parents: [...event.parents, event.index],
1787
+ position: "right|left",
1788
+ hideOn: ["doc-click", "select"]
1789
+ // any: cast-to-any for dynamic dispatch; TsTooltip overlay options merge from multiple user sources at runtime
1790
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1791
+ }).hide((_evt) => {
1792
+ query2(event.target).removeClass("expanded");
1793
+ });
1794
+ setTimeout(() => {
1795
+ query2("#w2overlay-" + overlay.name + "-submenu").on("mouseenter", (event2) => {
1796
+ event2.target._keepSubOpen = true;
1797
+ }).on("mouseleave", (event2) => {
1798
+ event2.target._keepSubOpen = false;
1799
+ });
1800
+ }, 10);
1801
+ }
1802
+ // any: callback parameter — caller signature varies; TsTooltip overlay options merge from multiple user sources at runtime
1803
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1804
+ closeSubMenu(event) {
1805
+ const { overlay } = event;
1806
+ if (event.target._keepSubOpen !== true) {
1807
+ const prev = TsMenu.get(overlay.name + "-submenu");
1808
+ if (prev) {
1809
+ prev.hide();
1810
+ }
1811
+ }
1812
+ }
1813
+ // Refreshed only selected item highligh, used in keyboard navigation
1814
+ refreshIndex(name, instant) {
1815
+ const overlay = Tooltip.active[name.replace(/[\s\.#]/g, "_")];
1816
+ if (!overlay) return;
1817
+ if (!overlay.displayed) {
1818
+ this.show(overlay.name);
1819
+ }
1820
+ const view = query2(overlay.box).find(".tsg-overlay-body").get(0);
1821
+ const search = query2(overlay.box).find(".tsg-menu-search, .tsg-menu-top").get(0);
1822
+ query2(overlay.box).find(".tsg-menu-item.tsg-selected").removeClass("tsg-selected");
1823
+ const el = query2(overlay.box).find(`.tsg-menu-item[index="${overlay.selected}"]`).addClass("tsg-selected").get(0);
1824
+ if (el) {
1825
+ if (el.offsetTop + el.clientHeight > view.clientHeight + view.scrollTop) {
1826
+ el.scrollIntoView({
1827
+ behavior: instant ? "instant" : "smooth",
1828
+ block: instant ? "center" : "start",
1829
+ inline: instant ? "center" : "start"
1830
+ });
1831
+ }
1832
+ if (el.offsetTop < view.scrollTop + (search ? search.clientHeight : 0)) {
1833
+ el.scrollIntoView({
1834
+ behavior: instant ? "instant" : "smooth",
1835
+ block: instant ? "center" : "end",
1836
+ inline: instant ? "center" : "end"
1837
+ });
1838
+ }
1839
+ }
1840
+ TsTooltip.hide(overlay.name + "-tooltip");
1841
+ }
1842
+ // any: callback parameter — caller signature varies; TsTooltip overlay options merge from multiple user sources at runtime
1843
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1844
+ showTooltip(name, options) {
1845
+ const overlay = Tooltip.active[name.replace(/[\s\.#]/g, "_")];
1846
+ if (!overlay || !overlay.displayed) return;
1847
+ const anchor = options?.anchor ?? query2(overlay.box).find(`.tsg-menu-item[index="${overlay.selected}"]`).get(0);
1848
+ const tooltip = options?.tooltip ?? (overlay.selected != null ? overlay.options.items?.[overlay.selected]?.tooltip : void 0);
1849
+ if (tooltip) {
1850
+ const html = tooltip.html ?? tooltip;
1851
+ TsTooltip.show(Object.assign({
1852
+ name: overlay.name + "-tooltip",
1853
+ anchor,
1854
+ html,
1855
+ position: "right|left",
1856
+ hideOn: ["doc-click"],
1857
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1858
+ onShow(event) {
1859
+ overlay.self.trigger("tooltip", { overlay, action: "show", originalEvent: event });
1860
+ },
1861
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1862
+ onHide(event) {
1863
+ overlay.self.trigger("tooltip", { overlay, action: "hide", originalEvent: event });
1864
+ }
1865
+ }, typeof tooltip == "object" && tooltip != null ? tooltip : {}));
1866
+ }
1867
+ }
1868
+ // show/hide searched items
1869
+ refreshSearch(name) {
1870
+ const overlay = Tooltip.active[name.replace(/[\s\.#]/g, "_")];
1871
+ if (!overlay) return;
1872
+ if (!overlay.displayed) {
1873
+ this.show(overlay.name);
1874
+ }
1875
+ TsTooltip.hide(overlay.name + "-tooltip");
1876
+ query2(overlay.box).find(".tsg-no-items").hide();
1877
+ query2(overlay.box).find(".tsg-menu-item, .tsg-menu-divider").each((el) => {
1878
+ const cur = this.getCurrent(name, el.getAttribute("index"));
1879
+ if (cur.item?.hidden) {
1880
+ query2(el).hide();
1881
+ } else {
1882
+ const search = overlay.tmp?.["search"];
1883
+ if (overlay.options.markSearch) {
1884
+ TsUtils.marker(el, search, { onlyFirst: overlay.options.match == "begins" });
1885
+ }
1886
+ query2(el).show();
1887
+ }
1888
+ });
1889
+ query2(overlay.box).find(".tsg-sub-menu").each((sub) => {
1890
+ const hasItems = query2(sub).find(".tsg-menu-item").get().some((el) => {
1891
+ return el.style.display != "none" ? true : false;
1892
+ });
1893
+ const parent = this.getCurrent(name, sub.dataset?.parent);
1894
+ if (parent.item.expanded) {
1895
+ if (!hasItems) {
1896
+ query2(sub).parent().hide();
1897
+ } else {
1898
+ query2(sub).parent().show();
1899
+ }
1900
+ }
1901
+ });
1902
+ if (overlay.tmp["searchCount"] == 0 || (overlay.options?.items?.length ?? 0) == 0) {
1903
+ if (query2(overlay.box).find(".tsg-no-items").length == 0) {
1904
+ query2(overlay.box).find(".tsg-menu:not(.tsg-sub-menu)").append(`
1905
+ <div class="tsg-no-items">
1906
+ ${TsUtils.lang(overlay.options.msgNoItems)}
1907
+ </div>`);
1908
+ }
1909
+ query2(overlay.box).find(".tsg-no-items").show();
1910
+ }
1911
+ }
1912
+ /**
1913
+ * Loops through the items and markes item.hidden = true for those that need to be hidden, and item.hidden = false
1914
+ * for those that are visible. Return a promise (since items can be on the server) with the number of visible items.
1915
+ */
1916
+ // any: callback parameter — caller signature varies; TsTooltip overlay options merge from multiple user sources at runtime
1917
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1918
+ applyFilter(name, items, search, debounce) {
1919
+ let count = 0;
1920
+ const overlay = Tooltip.active[name.replace(/[\s\.#]/g, "_")];
1921
+ const options = overlay.options;
1922
+ let resolve, reject;
1923
+ const prom = new Promise((res, rej) => {
1924
+ resolve = res;
1925
+ reject = rej;
1926
+ });
1927
+ if (overlay.tmp["_skip_filter"] === true) {
1928
+ return prom;
1929
+ }
1930
+ if (search == null) {
1931
+ if (["INPUT", "TEXTAREA"].includes(overlay.anchor.tagName)) {
1932
+ search = overlay.anchor.value;
1933
+ } else {
1934
+ search = "";
1935
+ }
1936
+ }
1937
+ if (overlay.tmp["_new_search"] === false) {
1938
+ search = "";
1939
+ }
1940
+ let selectedIds = [];
1941
+ if (options.selected) {
1942
+ if (Array.isArray(options.selected)) {
1943
+ selectedIds = options.selected.map((item) => {
1944
+ return item?.id ?? item;
1945
+ });
1946
+ } else if (options.selected?.id) {
1947
+ selectedIds = [options.selected.id];
1948
+ }
1949
+ }
1950
+ overlay.tmp["activeChain"] = null;
1951
+ const remote = overlay.tmp["remote"] ?? { hasMore: true, emptySet: false, search: null, cached: -1 };
1952
+ if (remote.hasMore == false) {
1953
+ const len = remote.hasMore_search.length;
1954
+ if (search.substr(0, len) != remote.hasMore_search) {
1955
+ remote.hasMore = true;
1956
+ }
1957
+ }
1958
+ if (items == null && options.url && remote.hasMore && remote.search !== search) {
1959
+ let proceed = true;
1960
+ let msg = TsUtils.lang("Loading...");
1961
+ if (search.length < (options.minLength ?? 0) && remote.emptySet !== true) {
1962
+ msg = TsUtils.lang("${count} letters or more...", { count: String(options.minLength) });
1963
+ proceed = false;
1964
+ if (search === "") {
1965
+ msg = TsUtils.lang(options.msgSearch);
1966
+ }
1967
+ if ((options.items?.length ?? 0) > 0) {
1968
+ this.update(name, []);
1969
+ this.applyFilter(name, null, search);
1970
+ }
1971
+ }
1972
+ query2(overlay.box).find(".tsg-no-items").html(msg);
1973
+ remote.search = search;
1974
+ options.items = [];
1975
+ overlay.tmp["remote"] = remote;
1976
+ if (proceed) {
1977
+ this.request(overlay, search, debounce).then((remoteItems) => {
1978
+ overlay.tmp["_skip_filter"] = true;
1979
+ this.update(name, remoteItems);
1980
+ delete overlay.tmp["_skip_filter"];
1981
+ overlay.tmp["_new_search"] = true;
1982
+ this.applyFilter(name, remoteItems, search).then((data) => {
1983
+ this.getActiveChain(overlay.name, options.items);
1984
+ overlay.tmp["searchCount"] = data.count;
1985
+ overlay.tmp["search"] = data.search;
1986
+ if (options.prefilter || search !== "") {
1987
+ if (data.count === 0 || !this.getActiveChain(overlay.name, options.items).includes(overlay.selected)) {
1988
+ overlay.selected = null;
1989
+ }
1990
+ this.refreshSearch(overlay.name);
1991
+ }
1992
+ this.initControls(overlay);
1993
+ this.refreshIndex(overlay.name, true);
1994
+ resolve(data);
1995
+ });
1996
+ }).catch((error) => {
1997
+ console.log("Server Request error", error);
1998
+ });
1999
+ }
2000
+ return prom;
2001
+ }
2002
+ let edata;
2003
+ if (items == null) {
2004
+ edata = this.trigger("search", { search, overlay, prom, resolve, reject });
2005
+ if (edata.isCancelled === true) {
2006
+ return prom;
2007
+ }
2008
+ }
2009
+ if (items == null) {
2010
+ items = overlay.options.items;
2011
+ }
2012
+ if (options.filter === false) {
2013
+ resolve({ count: -1, search });
2014
+ return prom;
2015
+ }
2016
+ items.forEach((item) => {
2017
+ if (options.match == "regex") {
2018
+ try {
2019
+ const re = new RegExp(search, "i");
2020
+ if (re.test(item.text) || item.text === "...") {
2021
+ item.hidden = false;
2022
+ } else {
2023
+ item.hidden = true;
2024
+ }
2025
+ } catch (e) {
2026
+ }
2027
+ } else {
2028
+ let prefix = "";
2029
+ let suffix = "";
2030
+ if (["is", "begins", "begins with"].indexOf(options.match ?? "") !== -1) prefix = "^";
2031
+ if (["is", "ends", "ends with"].indexOf(options.match ?? "") !== -1) suffix = "$";
2032
+ try {
2033
+ const re = new RegExp(prefix + search + suffix, "i");
2034
+ if (re.test(item.text) || item.text === "...") {
2035
+ item.hidden = false;
2036
+ } else {
2037
+ item.hidden = true;
2038
+ }
2039
+ } catch (e) {
2040
+ }
2041
+ }
2042
+ if (options.hideSelected && selectedIds.includes(item.id)) {
2043
+ item.hidden = true;
2044
+ }
2045
+ if (Array.isArray(item.items) && item.items.length > 0) {
2046
+ delete item._noSearchInside;
2047
+ this.applyFilter(name, item.items, search).then((data) => {
2048
+ const subCount = data.count;
2049
+ if (subCount > 0) {
2050
+ count += subCount;
2051
+ if (item.hidden) item._noSearchInside = true;
2052
+ if (search) item.expanded = true;
2053
+ item.hidden = false;
2054
+ }
2055
+ });
2056
+ }
2057
+ if (item.hidden !== true) count++;
2058
+ });
2059
+ resolve({ count, search });
2060
+ edata?.finish();
2061
+ return prom;
2062
+ }
2063
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2064
+ request(overlay, search, debounce) {
2065
+ const options = overlay.options;
2066
+ const remote = overlay.tmp["remote"];
2067
+ let resolve, reject;
2068
+ if (options.items.length === 0 && remote.cached !== 0 || remote.cached == options.cacheMax && search.length > remote.search.length || search.length >= remote.search.length && search.substr(0, remote.search.length) !== remote.search || search.length < remote.search.length) {
2069
+ if (remote.controller) {
2070
+ remote.controller.abort();
2071
+ }
2072
+ remote.loading = true;
2073
+ clearTimeout(remote.timeout);
2074
+ remote.timeout = setTimeout(() => {
2075
+ let url = options.url;
2076
+ const postData = { search, max: options.cacheMax };
2077
+ Object.assign(postData, options.postData);
2078
+ const edata = this.trigger("request", {
2079
+ search,
2080
+ overlay,
2081
+ url,
2082
+ postData,
2083
+ httpMethod: options.method ?? "GET",
2084
+ httpHeaders: {}
2085
+ });
2086
+ if (edata.isCancelled === true) return;
2087
+ const detail = edata.detail;
2088
+ url = new URL(detail["url"], location.href);
2089
+ const fetchOptions = TsUtils.prepareParams(url, {
2090
+ method: detail["httpMethod"],
2091
+ headers: detail["httpHeaders"],
2092
+ body: detail["postData"]
2093
+ }, { caller: this, overlay, search });
2094
+ remote.controller = new AbortController();
2095
+ fetchOptions["signal"] = remote.controller.signal;
2096
+ fetch(url, fetchOptions).then((resp) => resp.json()).then((data) => {
2097
+ remote.controller = null;
2098
+ const edata2 = overlay.trigger("load", { search: postData.search, overlay, data });
2099
+ if (edata2.isCancelled === true) return;
2100
+ data = edata2.detail.data;
2101
+ if (typeof data === "string") data = JSON.parse(data);
2102
+ if (Array.isArray(data)) {
2103
+ data = { records: data };
2104
+ }
2105
+ if (data.records == null && data.items != null) {
2106
+ data.records = data.items;
2107
+ delete data.items;
2108
+ }
2109
+ if (!data.error && data.records == null) {
2110
+ data.records = [];
2111
+ }
2112
+ if (!Array.isArray(data.records)) {
2113
+ console.error(
2114
+ "ERROR: server did not return proper JSON data structure",
2115
+ "\n",
2116
+ " - it should return",
2117
+ { records: [{ id: 1, text: "item" }] },
2118
+ "\n",
2119
+ " - or just an array ",
2120
+ [{ id: 1, text: "item" }],
2121
+ "\n",
2122
+ " - or if errorr ",
2123
+ { error: true, message: "error message" }
2124
+ );
2125
+ return;
2126
+ }
2127
+ if (data.records.length >= options.cacheMax) {
2128
+ data.records.splice(options.cacheMax, data.records.length);
2129
+ remote.hasMore = true;
2130
+ } else {
2131
+ remote.hasMore = false;
2132
+ remote.hasMore_search = search;
2133
+ }
2134
+ if (options.recId == null && options.recid != null) options.recId = options.recid;
2135
+ if (options.recId || options.recText) {
2136
+ data.records.forEach((item) => {
2137
+ if (typeof options.recId === "string") item.id = item[options.recId];
2138
+ if (typeof options.recId === "function") item.id = options.recId(item);
2139
+ if (typeof options.recText === "string") item.text = item[options.recText];
2140
+ if (typeof options.recText === "function") item.text = options.recText(item);
2141
+ });
2142
+ }
2143
+ remote.loading = false;
2144
+ remote.search = search;
2145
+ remote.cached = data.records.length == 0 ? -1 : data.records.length;
2146
+ remote.lastError = "";
2147
+ remote.emptySet = search === "" && data.records.length === 0 ? true : false;
2148
+ edata2.finish();
2149
+ resolve(TsUtils.normMenu(data.records, data));
2150
+ }).catch((error) => {
2151
+ const edata2 = this.trigger("error", { overlay, search, error });
2152
+ if (edata2.isCancelled === true) return;
2153
+ if (error?.name !== "AbortError") {
2154
+ console.error(
2155
+ "ERROR: Server communication failed.",
2156
+ "\n",
2157
+ " - it should return",
2158
+ { records: [{ id: 1, text: "item" }] },
2159
+ "\n",
2160
+ " - or just an array ",
2161
+ [{ id: 1, text: "item" }],
2162
+ "\n",
2163
+ " - or if errorr ",
2164
+ { error: true, message: "error message" }
2165
+ );
2166
+ }
2167
+ remote.loading = false;
2168
+ remote.search = "";
2169
+ remote.cached = -1;
2170
+ remote.emptySet = true;
2171
+ remote.lastError = edata2.detail["error"] || "Server communication failed";
2172
+ options.items = [];
2173
+ edata2.finish();
2174
+ reject();
2175
+ });
2176
+ edata.finish();
2177
+ }, debounce ? options.debounce ?? 350 : 0);
2178
+ }
2179
+ return new Promise((res, rej) => {
2180
+ resolve = res;
2181
+ reject = rej;
2182
+ });
2183
+ }
2184
+ /**
2185
+ * Builds an array of item ids that sequencial order for navigation with up/down keys. Skips hidden and disabled items
2186
+ * and goes into nested structures. It will remember last active chain in 'overlay.tmp.activeChain'
2187
+ */
2188
+ // any: parameter typed any — runtime dispatch by call site; TsTooltip overlay options merge from multiple user sources at runtime
2189
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2190
+ getActiveChain(name, items, parents = [], res = [], noSave) {
2191
+ const overlay = Tooltip.active[name.replace(/[\s\.#]/g, "_")];
2192
+ if (overlay.tmp["activeChain"] != null) {
2193
+ return overlay.tmp["activeChain"];
2194
+ }
2195
+ if (items == null) items = overlay.options.items;
2196
+ items.forEach((item, ind) => {
2197
+ if (!item.hidden && !item.disabled && !item?.text?.startsWith?.("--")) {
2198
+ res.push(parents.concat([ind]).join("-"));
2199
+ if (Array.isArray(item.items) && item.items.length > 0 && item.expanded) {
2200
+ parents.push(ind);
2201
+ this.getActiveChain(name, item.items, parents, res, true);
2202
+ parents.pop();
2203
+ }
2204
+ }
2205
+ });
2206
+ if (noSave == null) {
2207
+ overlay.tmp["activeChain"] = res;
2208
+ }
2209
+ return res;
2210
+ }
2211
+ // any: callback parameter — caller signature varies; TsTooltip overlay options merge from multiple user sources at runtime
2212
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2213
+ menuDown(overlay, event, index, parents) {
2214
+ const options = overlay.options;
2215
+ let items = options.items;
2216
+ const icon = query2(event.delegate).find(".tsg-icon");
2217
+ const menu = query2(event.target).closest(".tsg-menu:not(.tsg-sub-menu)");
2218
+ if (typeof items == "function") {
2219
+ items = items({ overlay, index, parents, event });
2220
+ }
2221
+ const item = items[index];
2222
+ if (item == null || item.disabled) {
2223
+ return;
2224
+ }
2225
+ const uncheck = (items2, parent) => {
2226
+ items2.forEach((other, ind) => {
2227
+ if (other.id == item.id) return;
2228
+ if (other.group === item.group && other.checked) {
2229
+ menu.find(`.tsg-menu-item[index="${(parent ? parent + "-" : "") + ind}"] .tsg-icon`).removeClass("tsg-icon-check").addClass("tsg-icon-empty");
2230
+ items2[ind].checked = false;
2231
+ }
2232
+ if (Array.isArray(other.items)) {
2233
+ uncheck(other.items, ind);
2234
+ }
2235
+ });
2236
+ };
2237
+ if ((options.type === "check" || options.type === "radio") && item.group !== false && !query2(event.target).hasClass("menu-remove") && !query2(event.target).hasClass("menu-help") && !query2(event.target).closest(".tsg-menu-item").hasClass("has-sub-menu")) {
2238
+ item.checked = options.type == "radio" ? true : !item.checked;
2239
+ if (item.checked) {
2240
+ if (options.type === "radio") {
2241
+ query2(event.target).closest(".tsg-menu").find(".tsg-icon").removeClass("tsg-icon-check").addClass("tsg-icon-empty");
2242
+ }
2243
+ if (options.type === "check" && item.group != null) {
2244
+ uncheck(options.items);
2245
+ }
2246
+ icon.removeClass("tsg-icon-empty").addClass("tsg-icon-check");
2247
+ } else if (options.type === "check") {
2248
+ icon.removeClass("tsg-icon-check").addClass("tsg-icon-empty");
2249
+ }
2250
+ }
2251
+ if (!query2(event.target).hasClass("menu-remove") && !query2(event.target).hasClass("menu-help")) {
2252
+ menu.find(".tsg-menu-item").removeClass("tsg-selected");
2253
+ if (!query2(event.delegate).hasClass("has-sub-menu")) {
2254
+ query2(event.delegate).addClass("tsg-selected");
2255
+ }
2256
+ }
2257
+ }
2258
+ // any: callback parameter — caller signature varies; TsTooltip overlay options merge from multiple user sources at runtime
2259
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2260
+ menuClick(overlay, event, index, parents) {
2261
+ const options = overlay.options;
2262
+ let items = options.items;
2263
+ const $item = query2(event.delegate).closest(".tsg-menu-item");
2264
+ let keepOpen = options.hideOn.includes("select") ? false : true;
2265
+ if (event.shiftKey || event.metaKey || event.ctrlKey) {
2266
+ keepOpen = true;
2267
+ }
2268
+ if (typeof items == "function") {
2269
+ items = items({ overlay, index, parents, event });
2270
+ }
2271
+ const item = items[index];
2272
+ if (!item || item.disabled && !query2(event.target).hasClass("menu-remove")) {
2273
+ return;
2274
+ }
2275
+ let edata;
2276
+ const overlays = [overlay];
2277
+ let topOverlay = overlay;
2278
+ let parentOverlay;
2279
+ while (topOverlay.options.parentOverlay) {
2280
+ topOverlay = topOverlay.options.parentOverlay;
2281
+ parentOverlay ??= topOverlay;
2282
+ overlays.push(topOverlay);
2283
+ }
2284
+ if (query2(event.target).hasClass("menu-remove")) {
2285
+ edata = topOverlay.trigger("remove", {
2286
+ originalEvent: event,
2287
+ target: overlay.name,
2288
+ overlay,
2289
+ topOverlay,
2290
+ parentOverlay,
2291
+ item,
2292
+ index,
2293
+ el: $item[0],
2294
+ parents
2295
+ });
2296
+ if (edata.isCancelled === true) {
2297
+ return;
2298
+ }
2299
+ const items2 = options.items;
2300
+ const ind = items2.findIndex((it) => it.id == item.id);
2301
+ if (ind != -1) {
2302
+ const tmp = items2.splice(ind, 1);
2303
+ if (overlay.options.parents) {
2304
+ const pind = overlay.options.parents[overlay.options.parents.length - 1];
2305
+ const pitems = parentOverlay.options.items[pind].items;
2306
+ if (pitems[ind].id == tmp[0].id) {
2307
+ pitems.splice(ind, 1);
2308
+ }
2309
+ }
2310
+ }
2311
+ keepOpen = !options.hideOn.includes("item-remove");
2312
+ const name = $item.closest(".tsg-overlay").attr("name");
2313
+ overlay.self.update(name, items2);
2314
+ } else if ($item.hasClass("has-sub-menu")) {
2315
+ edata = topOverlay.trigger("subMenu", {
2316
+ originalEvent: event,
2317
+ target: overlay.name,
2318
+ overlay,
2319
+ topOverlay,
2320
+ parentOverlay,
2321
+ item,
2322
+ index,
2323
+ el: $item[0],
2324
+ parents
2325
+ });
2326
+ if (edata.isCancelled === true) {
2327
+ return;
2328
+ }
2329
+ keepOpen = true;
2330
+ } else {
2331
+ const selected = this.findChecked(options.items);
2332
+ const a_index = $item.attr("index");
2333
+ overlay.selected = isNaN(Number(a_index)) ? a_index : parseInt(a_index);
2334
+ edata = topOverlay.trigger("select", {
2335
+ originalEvent: event,
2336
+ target: overlay.name,
2337
+ overlay,
2338
+ topOverlay,
2339
+ parentOverlay,
2340
+ item,
2341
+ index,
2342
+ selected,
2343
+ keepOpen,
2344
+ el: $item[0],
2345
+ parents
2346
+ });
2347
+ if (edata.isCancelled === true) {
2348
+ return;
2349
+ }
2350
+ if (item.keepOpen != null) {
2351
+ keepOpen = item.keepOpen;
2352
+ }
2353
+ if (["INPUT", "TEXTAREA"].includes(overlay.anchor.tagName)) {
2354
+ overlay.anchor.dataset.selected = item.id;
2355
+ overlay.anchor.dataset.selectedIndex = overlay.selected;
2356
+ }
2357
+ }
2358
+ if (!keepOpen) {
2359
+ overlays.forEach((overlay2) => this.hide(overlay2.name));
2360
+ }
2361
+ edata.finish();
2362
+ }
2363
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2364
+ findChecked(items) {
2365
+ let found = [];
2366
+ items.forEach((item) => {
2367
+ if (item.checked) found.push(item);
2368
+ if (Array.isArray(item.items)) {
2369
+ found = found.concat(this.findChecked(item.items));
2370
+ }
2371
+ });
2372
+ return found;
2373
+ }
2374
+ // any: callback parameter — caller signature varies; TsTooltip overlay options merge from multiple user sources at runtime
2375
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2376
+ keyUp(overlay, event) {
2377
+ const options = overlay.options;
2378
+ const search = event.target.value;
2379
+ let filter = true;
2380
+ let refreshIndex = false;
2381
+ switch (event.keyCode) {
2382
+ case 46:
2383
+ // delete
2384
+ case 8: {
2385
+ if (search === "" && !overlay.displayed) filter = false;
2386
+ break;
2387
+ }
2388
+ case 13: {
2389
+ if (!overlay.displayed || !overlay.selected) return;
2390
+ const { index, parents } = this.getCurrent(overlay.name);
2391
+ event.delegate = query2(overlay.box).find(".tsg-selected").get(0);
2392
+ this.menuClick(overlay, event, parseInt(String(index)), parents);
2393
+ filter = false;
2394
+ break;
2395
+ }
2396
+ case 27: {
2397
+ filter = false;
2398
+ if (overlay.displayed) {
2399
+ this.hide(overlay.name);
2400
+ } else {
2401
+ const el = overlay.anchor;
2402
+ if (["INPUT", "TEXTAREA"].includes(el.tagName)) {
2403
+ el.value = "";
2404
+ delete el.dataset.selected;
2405
+ delete el.dataset.selectedIndex;
2406
+ }
2407
+ }
2408
+ break;
2409
+ }
2410
+ case 37: {
2411
+ if (!overlay.displayed) return;
2412
+ let { item, index, parents } = this.getCurrent(overlay.name);
2413
+ if (parents) {
2414
+ item = options.items[parseInt(parents)];
2415
+ index = parseInt(parents);
2416
+ parents = "";
2417
+ refreshIndex = true;
2418
+ }
2419
+ if (Array.isArray(item?.items) && item.items.length > 0 && item.expanded) {
2420
+ event.delegate = query2(overlay.box).find(`.tsg-menu-item[index="${index}"]`).get(0);
2421
+ overlay.selected = index;
2422
+ this.menuClick(overlay, event, parseInt(String(index)), parents);
2423
+ }
2424
+ filter = false;
2425
+ break;
2426
+ }
2427
+ case 39: {
2428
+ if (!overlay.displayed) return;
2429
+ const { item, index, parents } = this.getCurrent(overlay.name);
2430
+ if (Array.isArray(item?.items) && item.items.length > 0 && !item.expanded) {
2431
+ event.delegate = query2(overlay.box).find(".tsg-selected").get(0);
2432
+ this.menuClick(overlay, event, parseInt(String(index)), parents);
2433
+ }
2434
+ filter = false;
2435
+ break;
2436
+ }
2437
+ case 38: {
2438
+ if (!overlay.displayed) {
2439
+ break;
2440
+ }
2441
+ overlay.prev();
2442
+ filter = false;
2443
+ event.preventDefault();
2444
+ break;
2445
+ }
2446
+ case 40: {
2447
+ if (!overlay.displayed) {
2448
+ break;
2449
+ }
2450
+ overlay.next();
2451
+ filter = false;
2452
+ event.preventDefault();
2453
+ break;
2454
+ }
2455
+ }
2456
+ if (filter && overlay.displayed && (options.filter && event._searchType == "filter" || options.search && event._searchType == "search")) {
2457
+ this.applyFilter(overlay.name, null, search, true).then((data) => {
2458
+ overlay.tmp.searchCount = data.count;
2459
+ overlay.tmp.search = data.search;
2460
+ if (data.count === 0 || !this.getActiveChain(overlay.name).includes(overlay.selected)) {
2461
+ overlay.selected = null;
2462
+ }
2463
+ this.refreshSearch(overlay.name);
2464
+ });
2465
+ }
2466
+ if (refreshIndex) {
2467
+ this.refreshIndex(overlay.name);
2468
+ }
2469
+ }
2470
+ };
2471
+ var DateTooltip = class extends Tooltip {
2472
+ daysCount;
2473
+ today;
2474
+ constructor() {
2475
+ super();
2476
+ const td = /* @__PURE__ */ new Date();
2477
+ this.daysCount = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
2478
+ this.today = td.getFullYear() + "/" + (Number(td.getMonth()) + 1) + "/" + td.getDate();
2479
+ this.defaults = TsUtils.extend({}, this.defaults, {
2480
+ position: "top|bottom",
2481
+ class: "tsg-calendar",
2482
+ type: "date",
2483
+ // can be date/time/datetime
2484
+ value: "",
2485
+ // initial date (in TsUtils.settings format)
2486
+ format: "",
2487
+ start: null,
2488
+ end: null,
2489
+ btnNow: false,
2490
+ blockDates: [],
2491
+ // array of blocked dates
2492
+ blockWeekdays: [],
2493
+ // blocked weekdays 0 - sunday, 1 - monday, etc
2494
+ colored: {},
2495
+ // ex: { '3/13/2022': 'bg-color|text-color' }
2496
+ arrowSize: 12,
2497
+ autoResize: false,
2498
+ anchorClass: "tsg-focus",
2499
+ autoShowOn: "focus",
2500
+ hideOn: ["doc-click", "focus-change"],
2501
+ onSelect: null
2502
+ });
2503
+ }
2504
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2505
+ attach(anchor, text) {
2506
+ let options;
2507
+ if (arguments.length == 1 && anchor instanceof Object) {
2508
+ options = anchor;
2509
+ anchor = options.anchor;
2510
+ } else if (arguments.length === 2 && text != null && typeof text === "object") {
2511
+ options = text;
2512
+ options.anchor = anchor;
2513
+ }
2514
+ const prevHideOn = options.hideOn;
2515
+ options = TsUtils.extend({}, this.defaults, options || {});
2516
+ if (prevHideOn) {
2517
+ options.hideOn = prevHideOn;
2518
+ }
2519
+ if (!options.format) {
2520
+ const df = TsUtils.settings.dateFormat;
2521
+ const tf = TsUtils.settings.timeFormat;
2522
+ if (options.type == "date") {
2523
+ options.format = df;
2524
+ } else if (options.type == "time") {
2525
+ options.format = tf;
2526
+ } else {
2527
+ options.format = df + "|" + tf;
2528
+ }
2529
+ }
2530
+ const cal = options.type == "time" ? this.getHourHTML(options) : this.getMonthHTML(options, void 0, void 0);
2531
+ options.style += "; padding: 0;";
2532
+ options.html = cal.html;
2533
+ const ret = super.attach(options);
2534
+ const overlay = ret.overlay;
2535
+ Object.assign(overlay.tmp, cal);
2536
+ overlay.on("show.attach", (event) => {
2537
+ const overlay2 = event.detail.overlay;
2538
+ const anchor2 = overlay2.anchor;
2539
+ const options2 = overlay2.options;
2540
+ if (["INPUT", "TEXTAREA"].includes(anchor2.tagName) && !options2.value && anchor2.value) {
2541
+ overlay2.tmp.initValue = anchor2.value;
2542
+ }
2543
+ delete overlay2.newValue;
2544
+ delete overlay2.newDate;
2545
+ });
2546
+ overlay.on("show:after.attach", (_event) => {
2547
+ if (ret.overlay?.box) {
2548
+ this.initControls(ret.overlay);
2549
+ }
2550
+ });
2551
+ overlay.on("update:after.attach", (_event) => {
2552
+ if (ret.overlay?.box) {
2553
+ this.initControls(ret.overlay);
2554
+ }
2555
+ });
2556
+ overlay.on("hide.attach", (event) => {
2557
+ const overlay2 = event.detail.overlay;
2558
+ const anchor2 = overlay2.anchor;
2559
+ if (overlay2.newValue != null) {
2560
+ if (overlay2.newDate) {
2561
+ overlay2.newValue = overlay2.newDate + " " + overlay2.newValue;
2562
+ }
2563
+ if (["INPUT", "TEXTAREA"].includes(anchor2.tagName) && anchor2.value != overlay2.newValue) {
2564
+ anchor2.value = overlay2.newValue;
2565
+ }
2566
+ const edata = this.trigger("select", { date: overlay2.newValue, target: overlay2.name, overlay: overlay2 });
2567
+ if (edata.isCancelled === true) return;
2568
+ edata.finish();
2569
+ }
2570
+ });
2571
+ ret.select = (callback) => {
2572
+ overlay.on("select.attach", (event) => {
2573
+ callback(event);
2574
+ });
2575
+ return ret;
2576
+ };
2577
+ return ret;
2578
+ }
2579
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2580
+ initControls(overlay) {
2581
+ const options = overlay.options;
2582
+ const moveMonth = (inc) => {
2583
+ let { month, year } = overlay.tmp;
2584
+ month += inc;
2585
+ if (month > 12) {
2586
+ month = 1;
2587
+ year++;
2588
+ }
2589
+ if (month < 1) {
2590
+ month = 12;
2591
+ year--;
2592
+ }
2593
+ const cal = this.getMonthHTML(options, month, year);
2594
+ Object.assign(overlay.tmp, cal);
2595
+ query2(overlay.box).find(".tsg-overlay-body").html(cal.html);
2596
+ this.initControls(overlay);
2597
+ };
2598
+ const checkJump = (event, dblclick) => {
2599
+ query2(event.target).parent().find(".tsg-jump-month, .tsg-jump-year").removeClass("tsg-selected");
2600
+ query2(event.target).addClass("tsg-selected");
2601
+ const dt = /* @__PURE__ */ new Date();
2602
+ let { jumpMonth, jumpYear } = overlay.tmp;
2603
+ if (dblclick) {
2604
+ if (jumpYear == null) jumpYear = dt.getFullYear();
2605
+ if (jumpMonth == null) jumpMonth = dt.getMonth() + 1;
2606
+ }
2607
+ if (jumpMonth && jumpYear) {
2608
+ const cal = this.getMonthHTML(options, jumpMonth, jumpYear);
2609
+ Object.assign(overlay.tmp, cal);
2610
+ query2(overlay.box).find(".tsg-overlay-body").html(cal.html);
2611
+ overlay.tmp.jump = false;
2612
+ this.initControls(overlay);
2613
+ }
2614
+ };
2615
+ query2(overlay.box).find(".tsg-cal-title").off(".calendar").on("click.calendar", (event) => {
2616
+ if (options.draggable && overlay.tmp?.moved) {
2617
+ event.stopPropagation();
2618
+ return;
2619
+ }
2620
+ Object.assign(overlay.tmp, { jumpYear: null, jumpMonth: null });
2621
+ if (overlay.tmp.jump) {
2622
+ const { month, year } = overlay.tmp;
2623
+ const cal = this.getMonthHTML(options, month, year);
2624
+ query2(overlay.box).find(".tsg-overlay-body").html(cal.html);
2625
+ overlay.tmp.jump = false;
2626
+ } else {
2627
+ query2(overlay.box).find(".tsg-overlay-body .tsg-cal-days").replace(this.getYearHTML());
2628
+ const el = query2(overlay.box).find(`[name="${overlay.tmp.year}"]`).get(0);
2629
+ if (el) el.scrollIntoView(true);
2630
+ overlay.tmp.jump = true;
2631
+ }
2632
+ this.initControls(overlay);
2633
+ event.stopPropagation();
2634
+ }).find(".tsg-cal-previous").off(".calendar").on("click.calendar", (event) => {
2635
+ moveMonth(-1);
2636
+ event.stopPropagation();
2637
+ }).parent().find(".tsg-cal-next").off(".calendar").on("click.calendar", (event) => {
2638
+ moveMonth(1);
2639
+ event.stopPropagation();
2640
+ });
2641
+ query2(overlay.box).find(".tsg-cal-now").off(".calendar").on("click.calendar", (_event) => {
2642
+ if (options.type == "datetime") {
2643
+ if (overlay.newDate) {
2644
+ overlay.newValue = TsUtils.formatTime(/* @__PURE__ */ new Date(), options.format.split("|")[1]);
2645
+ } else {
2646
+ overlay.newValue = TsUtils.formatDateTime(/* @__PURE__ */ new Date(), options.format);
2647
+ }
2648
+ } else if (options.type == "date") {
2649
+ overlay.newValue = TsUtils.formatDate(/* @__PURE__ */ new Date(), options.format);
2650
+ } else if (options.type == "time") {
2651
+ overlay.newValue = TsUtils.formatTime(/* @__PURE__ */ new Date(), options.format);
2652
+ }
2653
+ this.hide(overlay.name);
2654
+ });
2655
+ query2(overlay.box).off(".calendar").on("contextmenu.calendar", (event) => {
2656
+ event.preventDefault();
2657
+ }).on("click.calendar", { delegate: ".tsg-day.tsg-date" }, (event) => {
2658
+ if (options.type == "datetime") {
2659
+ overlay.newDate = query2(event.target).attr("date");
2660
+ query2(overlay.box).find(".tsg-overlay-body").html(this.getHourHTML(overlay.options).html);
2661
+ this.initControls(overlay);
2662
+ } else {
2663
+ overlay.newValue = query2(event.target).attr("date");
2664
+ this.hide(overlay.name);
2665
+ }
2666
+ }).on("click.calendar", { delegate: ".tsg-jump-month" }, (event) => {
2667
+ overlay.tmp.jumpMonth = parseInt(query2(event.target).attr("name") ?? "0");
2668
+ checkJump(event);
2669
+ }).on("dblclick.calendar", { delegate: ".tsg-jump-month" }, (event) => {
2670
+ overlay.tmp.jumpMonth = parseInt(query2(event.target).attr("name") ?? "0");
2671
+ checkJump(event, true);
2672
+ }).on("click.calendar", { delegate: ".tsg-jump-year" }, (event) => {
2673
+ overlay.tmp.jumpYear = parseInt(query2(event.target).attr("name") ?? "0");
2674
+ checkJump(event);
2675
+ }).on("dblclick.calendar", { delegate: ".tsg-jump-year" }, (event) => {
2676
+ overlay.tmp.jumpYear = parseInt(query2(event.target).attr("name") ?? "0");
2677
+ checkJump(event, true);
2678
+ }).on("click.calendar", { delegate: ".tsg-time.hour" }, (event) => {
2679
+ const hour = Number(query2(event.target).attr("hour"));
2680
+ let min = (this.str2min(options.value) ?? 0) % 60;
2681
+ if (overlay.tmp.initValue && !options.value) {
2682
+ min = (this.str2min(overlay.tmp.initValue) ?? 0) % 60;
2683
+ }
2684
+ if (options.noMinutes) {
2685
+ overlay.newValue = this.min2str(hour * 60, options.format);
2686
+ this.hide(overlay.name);
2687
+ } else {
2688
+ overlay.newValue = hour + ":" + min;
2689
+ const html = this.getMinHTML(hour, options).html;
2690
+ query2(overlay.box).find(".tsg-overlay-body").html(html);
2691
+ this.initControls(overlay);
2692
+ }
2693
+ }).on("click.calendar", { delegate: ".tsg-time.min" }, (event) => {
2694
+ const hour = Math.floor((this.str2min(overlay.newValue) ?? 0) / 60);
2695
+ const time = hour * 60 + parseInt(query2(event.target).attr("min"));
2696
+ overlay.newValue = this.min2str(time, options.format);
2697
+ this.hide(overlay.name);
2698
+ });
2699
+ TsUtils.bindEvents(query2(overlay.box).find(".tsg-eaction"), this);
2700
+ }
2701
+ // any: callback parameter — caller signature varies; TsTooltip overlay options merge from multiple user sources at runtime
2702
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2703
+ getMonthHTML(options, month, year) {
2704
+ const days = TsUtils.settings.fulldays.slice();
2705
+ const sdays = TsUtils.settings.shortdays.slice();
2706
+ if (TsUtils.settings.weekStarts !== "M") {
2707
+ days.unshift(days.pop());
2708
+ sdays.unshift(sdays.pop());
2709
+ }
2710
+ let DT = /* @__PURE__ */ new Date();
2711
+ const dayLengthMil = 1e3 * 60 * 60 * 24;
2712
+ const selected = options.type === "datetime" ? TsUtils.isDateTime(options.value, options.format, true) : TsUtils.isDate(options.value, options.format, true);
2713
+ const selected_dsp = TsUtils.formatDate(selected);
2714
+ if (month == null || year == null) {
2715
+ const selDate = selected instanceof Date ? selected : DT;
2716
+ year = selDate.getFullYear();
2717
+ month = selDate.getMonth() + 1;
2718
+ }
2719
+ if (month > 12) {
2720
+ month -= 12;
2721
+ year++;
2722
+ }
2723
+ if (month < 1 || month === 0) {
2724
+ month += 12;
2725
+ year--;
2726
+ }
2727
+ if (year / 4 == Math.floor(year / 4)) {
2728
+ this.daysCount[1] = 29;
2729
+ } else {
2730
+ this.daysCount[1] = 28;
2731
+ }
2732
+ options.current = month + "/" + year;
2733
+ let weekDaysHeaderHTML = "";
2734
+ const st = TsUtils.settings.weekStarts;
2735
+ for (let i = 0; i < sdays.length; i++) {
2736
+ const isSat = st == "M" && i == 5 || st != "M" && i == 6 ? true : false;
2737
+ const isSun = st == "M" && i == 6 || st != "M" && i == 0 ? true : false;
2738
+ weekDaysHeaderHTML += `<div class="tsg-day tsg-weekday ${isSat ? "tsg-sunday" : ""} ${isSun ? "tsg-saturday" : ""}">${sdays[i]}</div>`;
2739
+ }
2740
+ const calTitleClass = "tsg-cal-title" + (options.draggable ? " tsg-eaction tsg-draggable" : "");
2741
+ const calTitleData = options.draggable ? ' data-mousedown="startDrag|event"' : "";
2742
+ let html = `
2743
+ <div class="${calTitleClass}"${calTitleData}>
2744
+ <div class="tsg-cal-previous tsg-eaction" data-mousedown="stop">
2745
+ <div></div>
2746
+ </div>
2747
+ <div class="tsg-cal-next tsg-eaction" data-mousedown="stop">
2748
+ <div></div>
2749
+ </div>
2750
+ ${TsUtils.settings.fullmonths[month - 1]}, ${year}
2751
+ <span class="arrow-down"></span>
2752
+ </div>
2753
+ <div class="tsg-cal-days">
2754
+ ${weekDaysHeaderHTML}
2755
+ `;
2756
+ DT = new Date(year, month - 1, 1);
2757
+ DT = new Date(DT.getTime() + dayLengthMil * 0.5);
2758
+ let weekDayOffset = DT.getDay();
2759
+ if (TsUtils.settings.weekStarts == "M") {
2760
+ weekDayOffset = weekDayOffset > 0 ? weekDayOffset - 1 : 6;
2761
+ }
2762
+ DT = new Date(DT.getTime() - weekDayOffset * dayLengthMil);
2763
+ const DaySat = 6, DaySun = 0;
2764
+ for (let ci = 0; ci < 42; ci++) {
2765
+ const className = [];
2766
+ const dt = `${DT.getFullYear()}/${DT.getMonth() + 1}/${DT.getDate()}`;
2767
+ if (DT.getDay() === DaySat) className.push("tsg-saturday");
2768
+ if (DT.getDay() === DaySun) className.push("tsg-sunday");
2769
+ if (DT.getMonth() + 1 !== month) className.push("outside");
2770
+ if (dt == this.today) className.push("tsg-today");
2771
+ const dspDay = DT.getDate();
2772
+ let col = "";
2773
+ let bgcol = "";
2774
+ let tmp_dt, tmp_dt_fmt;
2775
+ if (options.type === "datetime") {
2776
+ tmp_dt = TsUtils.formatDateTime(dt, options.format);
2777
+ tmp_dt_fmt = TsUtils.formatDate(dt, TsUtils.settings.dateFormat);
2778
+ } else {
2779
+ tmp_dt = TsUtils.formatDate(dt, options.format);
2780
+ tmp_dt_fmt = tmp_dt;
2781
+ }
2782
+ if (options.colored && options.colored[tmp_dt_fmt] !== void 0) {
2783
+ const tmp = options.colored[tmp_dt_fmt].split("|");
2784
+ bgcol = "background-color: " + tmp[0] + ";";
2785
+ col = "color: " + tmp[1] + ";";
2786
+ }
2787
+ html += `<div class="tsg-day ${this.inRange(tmp_dt, options, true) ? "tsg-date " + (tmp_dt_fmt == selected_dsp ? "tsg-selected" : "") : "tsg-blocked"} ${className.join(" ")}"
2788
+ style="${col + bgcol}" date="${tmp_dt_fmt}" data-date="${DT.getTime()}">
2789
+ ${dspDay}
2790
+ </div>`;
2791
+ DT = new Date(DT.getTime() + dayLengthMil);
2792
+ }
2793
+ html += "</div>";
2794
+ if (options.btnNow) {
2795
+ const label = TsUtils.lang("Today" + (options.type == "datetime" ? " & Now" : ""));
2796
+ html += `<div class="tsg-cal-now">${label}</div>`;
2797
+ }
2798
+ return { html, month, year };
2799
+ }
2800
+ getYearHTML() {
2801
+ let mhtml = "";
2802
+ let yhtml = "";
2803
+ for (let m = 0; m < TsUtils.settings.fullmonths.length; m++) {
2804
+ mhtml += `<div class="tsg-jump-month" name="${m + 1}">${TsUtils.settings.shortmonths[m]}</div>`;
2805
+ }
2806
+ for (let y = TsUtils.settings.dateStartYear; y <= TsUtils.settings.dateEndYear; y++) {
2807
+ yhtml += `<div class="tsg-jump-year" name="${y}">${y}</div>`;
2808
+ }
2809
+ return `<div class="tsg-cal-jump">
2810
+ <div id="tsg-jump-month">${mhtml}</div>
2811
+ <div id="tsg-jump-year">${yhtml}</div>
2812
+ </div>`;
2813
+ }
2814
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2815
+ getHourHTML(options) {
2816
+ options = options ?? {};
2817
+ if (!options.format) options.format = TsUtils.settings.timeFormat;
2818
+ const h24 = options.format.indexOf("h24") > -1;
2819
+ const value = options.value ? options.value : options.anchor ? options.anchor.value : "";
2820
+ const tmp = [];
2821
+ for (let a = 0; a < 24; a++) {
2822
+ let time = (a >= 12 && !h24 ? a - 12 : a) + ":00" + (!h24 ? a < 12 ? " am" : " pm" : "");
2823
+ if (a == 12 && !h24) time = "12:00 pm";
2824
+ if (!tmp[Math.floor(a / 8)]) tmp[Math.floor(a / 8)] = "";
2825
+ let tm1 = this.min2str(this.str2min(time) ?? 0);
2826
+ let tm2 = this.min2str((this.str2min(time) ?? 0) + 59);
2827
+ if (options.type === "datetime") {
2828
+ const dt = TsUtils.isDateTime(value, options.format, true);
2829
+ const fm = options.format.split("|")[0].trim();
2830
+ tm1 = TsUtils.formatDate(dt, fm) + " " + tm1;
2831
+ tm2 = TsUtils.formatDate(dt, fm) + " " + tm2;
2832
+ }
2833
+ const valid = this.inRange(tm1, options) || this.inRange(tm2, options);
2834
+ tmp[Math.floor(a / 8)] += `<span hour="${a}"
2835
+ class="hour ${valid ? "tsg-time " : "tsg-blocked"}">${time}</span>`;
2836
+ }
2837
+ const timeTitleClass = "tsg-time-title" + (options.draggable ? " tsg-eaction tsg-draggable" : "");
2838
+ const timeTitleData = options.draggable ? ' data-mousedown="startDrag|event"' : "";
2839
+ const html = `<div class="tsg-calendar">
2840
+ <div class="${timeTitleClass}"${timeTitleData}>${TsUtils.lang("Select Hour")}</div>
2841
+ <div class="tsg-cal-time">
2842
+ <div class="tsg-cal-column">${tmp[0]}</div>
2843
+ <div class="tsg-cal-column">${tmp[1]}</div>
2844
+ <div class="tsg-cal-column">${tmp[2]}</div>
2845
+ </div>
2846
+ ${options.btnNow ? `<div class="tsg-cal-now">${TsUtils.lang("Now")}</div>` : ""}
2847
+ </div>`;
2848
+ return { html };
2849
+ }
2850
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2851
+ getMinHTML(hour, options) {
2852
+ if (hour == null) hour = 0;
2853
+ options = options ?? {};
2854
+ if (!options.format) options.format = TsUtils.settings.timeFormat;
2855
+ const h24 = options.format.indexOf("h24") > -1;
2856
+ const value = options.value ? options.value : options.anchor ? options.anchor.value : "";
2857
+ const tmp = [];
2858
+ for (let a = 0; a < 60; a += 5) {
2859
+ const time = (hour > 12 && !h24 ? hour - 12 : hour) + ":" + (a < 10 ? 0 : "") + a + " " + (!h24 ? hour < 12 ? "am" : "pm" : "");
2860
+ let tm = time;
2861
+ const ind = a < 20 ? 0 : a < 40 ? 1 : 2;
2862
+ if (!tmp[ind]) tmp[ind] = "";
2863
+ if (options.type === "datetime") {
2864
+ const dt = TsUtils.isDateTime(value, options.format, true);
2865
+ const fm = options.format.split("|")[0].trim();
2866
+ tm = TsUtils.formatDate(dt, fm) + " " + tm;
2867
+ }
2868
+ tmp[ind] += `<span min="${a}" class="min ${this.inRange(tm, options) ? "tsg-time " : "tsg-blocked"}">${time}</span>`;
2869
+ }
2870
+ const timeTitleClass = "tsg-time-title" + (options.draggable ? " tsg-eaction tsg-draggable" : "");
2871
+ const timeTitleData = options.draggable ? ' data-mousedown="startDrag|event"' : "";
2872
+ const html = `<div class="tsg-calendar">
2873
+ <div class="${timeTitleClass}"${timeTitleData}>${TsUtils.lang("Select Minute")}</div>
2874
+ <div class="tsg-cal-time">
2875
+ <div class="tsg-cal-column">${tmp[0]}</div>
2876
+ <div class="tsg-cal-column">${tmp[1]}</div>
2877
+ <div class="tsg-cal-column">${tmp[2]}</div>
2878
+ </div>
2879
+ ${options.btnNow ? `<div class="tsg-cal-now">${TsUtils.lang("Now")}</div>` : ""}
2880
+ </div>`;
2881
+ return { html };
2882
+ }
2883
+ // checks if date is in range (loost at start, end, blockDates, blockWeekdays)
2884
+ // any: parameter typed any — runtime dispatch by call site; TsTooltip overlay options merge from multiple user sources at runtime
2885
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2886
+ inRange(str, options, dateOnly) {
2887
+ let inRange = false;
2888
+ if (options.type === "date") {
2889
+ const dt = TsUtils.isDate(str, options.format, true);
2890
+ if (dt) {
2891
+ if (options.start || options.end) {
2892
+ const st = typeof options.start === "string" ? options.start : query2(options.start).val();
2893
+ const en = typeof options.end === "string" ? options.end : query2(options.end).val();
2894
+ let start = TsUtils.isDate(st, options.format, true);
2895
+ let end = TsUtils.isDate(en, options.format, true);
2896
+ const dtDate = dt instanceof Date ? dt : /* @__PURE__ */ new Date();
2897
+ const current = new Date(dtDate);
2898
+ if (!start) start = current;
2899
+ if (!end) end = current;
2900
+ if (current >= start && current <= end) inRange = true;
2901
+ } else {
2902
+ inRange = true;
2903
+ }
2904
+ if (Array.isArray(options.blockDates) && options.blockDates.includes(str)) inRange = false;
2905
+ if (Array.isArray(options.blockWeekdays) && options.blockWeekdays.includes((dt instanceof Date ? dt : /* @__PURE__ */ new Date()).getDay())) inRange = false;
2906
+ }
2907
+ } else if (options.type === "time") {
2908
+ if (options.start || options.end) {
2909
+ const tm = this.str2min(str) ?? 0;
2910
+ let tm1 = this.str2min(options.start) ?? tm;
2911
+ let tm2 = this.str2min(options.end) ?? tm;
2912
+ if (!tm1) tm1 = tm;
2913
+ if (!tm2) tm2 = tm;
2914
+ if (tm >= tm1 && tm <= tm2) inRange = true;
2915
+ } else {
2916
+ inRange = true;
2917
+ }
2918
+ } else if (options.type === "datetime") {
2919
+ const dt = TsUtils.isDateTime(str, options.format, true);
2920
+ if (dt) {
2921
+ const format = options.format.split("|").map((format2) => format2.trim());
2922
+ if (dateOnly) {
2923
+ const date = TsUtils.formatDate(dt, format[0]);
2924
+ const opts = TsUtils.extend({}, options, { type: "date", format: format[0] });
2925
+ if (this.inRange(date, opts)) inRange = true;
2926
+ } else {
2927
+ const time = TsUtils.formatTime(dt, format[1]);
2928
+ const opts = { type: "time", format: format[1], start: options.startTime, end: options.endTime };
2929
+ if (this.inRange(time, opts)) inRange = true;
2930
+ }
2931
+ }
2932
+ }
2933
+ return inRange;
2934
+ }
2935
+ // converts time into number of minutes since midnight -- '11:50am' => 710
2936
+ // any: callback parameter — caller signature varies; TsTooltip overlay options merge from multiple user sources at runtime
2937
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2938
+ str2min(str) {
2939
+ if (typeof str !== "string") return null;
2940
+ const tmp = str.split(":");
2941
+ if (tmp.length === 2) {
2942
+ tmp[0] = parseInt(tmp[0]);
2943
+ tmp[1] = parseInt(tmp[1]);
2944
+ if (str.indexOf("pm") !== -1 && tmp[0] !== 12) tmp[0] += 12;
2945
+ if (str.includes("am") && tmp[0] == 12) tmp[0] = 0;
2946
+ } else {
2947
+ return null;
2948
+ }
2949
+ return tmp[0] * 60 + tmp[1];
2950
+ }
2951
+ // converts minutes since midnight into time str -- 710 => '11:50am'
2952
+ // any: callback parameter — caller signature varies; TsTooltip overlay options merge from multiple user sources at runtime
2953
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2954
+ min2str(time, format) {
2955
+ let ret = "";
2956
+ if (time >= 24 * 60) time = time % (24 * 60);
2957
+ if (time < 0) time = 24 * 60 + time;
2958
+ const hour = Math.floor(time / 60);
2959
+ const min = (time % 60 < 10 ? "0" : "") + time % 60;
2960
+ if (!format) {
2961
+ format = TsUtils.settings.timeFormat;
2962
+ }
2963
+ if (format.indexOf("h24") !== -1) {
2964
+ ret = hour + ":" + min;
2965
+ } else {
2966
+ ret = (hour <= 12 ? hour : hour - 12) + ":" + min + " " + (hour >= 12 ? "pm" : "am");
2967
+ }
2968
+ return ret;
2969
+ }
2970
+ };
2971
+ var _tooltipCtorCount = 0;
2972
+ var _menuCtorCount = 0;
2973
+ var _colorCtorCount = 0;
2974
+ var _dateCtorCount = 0;
2975
+ var TsTooltip = lazySingleton(() => {
2976
+ _tooltipCtorCount++;
2977
+ return new Tooltip();
2978
+ }, Tooltip);
2979
+ var TsMenu = lazySingleton(() => {
2980
+ _menuCtorCount++;
2981
+ return new MenuTooltip();
2982
+ }, MenuTooltip);
2983
+ var TsColor = lazySingleton(() => {
2984
+ _colorCtorCount++;
2985
+ return new ColorTooltip();
2986
+ }, ColorTooltip);
2987
+ var TsDate = lazySingleton(() => {
2988
+ _dateCtorCount++;
2989
+ return new DateTooltip();
2990
+ }, DateTooltip);
2991
+ var __test_internals = {
2992
+ get tooltipCtorCount() {
2993
+ return _tooltipCtorCount;
2994
+ },
2995
+ get menuCtorCount() {
2996
+ return _menuCtorCount;
2997
+ },
2998
+ get colorCtorCount() {
2999
+ return _colorCtorCount;
3000
+ },
3001
+ get dateCtorCount() {
3002
+ return _dateCtorCount;
3003
+ },
3004
+ reset() {
3005
+ _tooltipCtorCount = 0;
3006
+ _menuCtorCount = 0;
3007
+ _colorCtorCount = 0;
3008
+ _dateCtorCount = 0;
3009
+ }
3010
+ };
3011
+
3012
+ export {
3013
+ Tooltip,
3014
+ TsTooltip,
3015
+ TsMenu,
3016
+ TsColor,
3017
+ TsDate,
3018
+ __test_internals
3019
+ };
3020
+ //# sourceMappingURL=chunk-FAIRNXQR.js.map