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