js-cloudimage-hotspot 1.0.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 (85) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +294 -0
  3. package/dist/a11y/aria.d.ts +7 -0
  4. package/dist/a11y/aria.d.ts.map +1 -0
  5. package/dist/a11y/focus.d.ts +9 -0
  6. package/dist/a11y/focus.d.ts.map +1 -0
  7. package/dist/a11y/keyboard.d.ts +14 -0
  8. package/dist/a11y/keyboard.d.ts.map +1 -0
  9. package/dist/core/CIHotspot.d.ts +48 -0
  10. package/dist/core/CIHotspot.d.ts.map +1 -0
  11. package/dist/core/ci-hotspot.d.ts +95 -0
  12. package/dist/core/ci-hotspot.d.ts.map +1 -0
  13. package/dist/core/config.d.ts +15 -0
  14. package/dist/core/config.d.ts.map +1 -0
  15. package/dist/core/types.d.ts +227 -0
  16. package/dist/core/types.d.ts.map +1 -0
  17. package/dist/editor/ci-hotspot-editor.d.ts +58 -0
  18. package/dist/editor/ci-hotspot-editor.d.ts.map +1 -0
  19. package/dist/editor/drag-manager.d.ts +18 -0
  20. package/dist/editor/drag-manager.d.ts.map +1 -0
  21. package/dist/editor/editor-toolbar.d.ts +26 -0
  22. package/dist/editor/editor-toolbar.d.ts.map +1 -0
  23. package/dist/editor/index.d.ts +3 -0
  24. package/dist/editor/index.d.ts.map +1 -0
  25. package/dist/editor/js-cloudimage-hotspot-editor.cjs.js +4 -0
  26. package/dist/editor/js-cloudimage-hotspot-editor.cjs.js.map +1 -0
  27. package/dist/editor/js-cloudimage-hotspot-editor.esm.js +1981 -0
  28. package/dist/editor/js-cloudimage-hotspot-editor.esm.js.map +1 -0
  29. package/dist/editor/js-cloudimage-hotspot-editor.min.js +4 -0
  30. package/dist/editor/js-cloudimage-hotspot-editor.min.js.map +1 -0
  31. package/dist/editor/property-panel.d.ts +17 -0
  32. package/dist/editor/property-panel.d.ts.map +1 -0
  33. package/dist/editor/selection-manager.d.ts +15 -0
  34. package/dist/editor/selection-manager.d.ts.map +1 -0
  35. package/dist/editor/types.d.ts +32 -0
  36. package/dist/editor/types.d.ts.map +1 -0
  37. package/dist/editor/undo-manager.d.ts +16 -0
  38. package/dist/editor/undo-manager.d.ts.map +1 -0
  39. package/dist/fullscreen/fullscreen.d.ts +14 -0
  40. package/dist/fullscreen/fullscreen.d.ts.map +1 -0
  41. package/dist/index.d.ts +6 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/js-cloudimage-hotspot.cjs.js +2 -0
  44. package/dist/js-cloudimage-hotspot.cjs.js.map +1 -0
  45. package/dist/js-cloudimage-hotspot.esm.js +1408 -0
  46. package/dist/js-cloudimage-hotspot.esm.js.map +1 -0
  47. package/dist/js-cloudimage-hotspot.min.js +2 -0
  48. package/dist/js-cloudimage-hotspot.min.js.map +1 -0
  49. package/dist/markers/Marker.d.ts +10 -0
  50. package/dist/markers/Marker.d.ts.map +1 -0
  51. package/dist/markers/pulse.d.ts +9 -0
  52. package/dist/markers/pulse.d.ts.map +1 -0
  53. package/dist/popover/Popover.d.ts +41 -0
  54. package/dist/popover/Popover.d.ts.map +1 -0
  55. package/dist/popover/position.d.ts +6 -0
  56. package/dist/popover/position.d.ts.map +1 -0
  57. package/dist/popover/sanitize.d.ts +6 -0
  58. package/dist/popover/sanitize.d.ts.map +1 -0
  59. package/dist/popover/template.d.ts +9 -0
  60. package/dist/popover/template.d.ts.map +1 -0
  61. package/dist/react/index.cjs +2 -0
  62. package/dist/react/index.cjs.map +1 -0
  63. package/dist/react/index.js +1617 -0
  64. package/dist/react/index.js.map +1 -0
  65. package/dist/utils/cloudimage.d.ts +16 -0
  66. package/dist/utils/cloudimage.d.ts.map +1 -0
  67. package/dist/utils/coordinates.d.ts +17 -0
  68. package/dist/utils/coordinates.d.ts.map +1 -0
  69. package/dist/utils/dom.d.ts +13 -0
  70. package/dist/utils/dom.d.ts.map +1 -0
  71. package/dist/utils/events.d.ts +15 -0
  72. package/dist/utils/events.d.ts.map +1 -0
  73. package/dist/zoom/ScrollHint.d.ts +8 -0
  74. package/dist/zoom/ScrollHint.d.ts.map +1 -0
  75. package/dist/zoom/ZoomPan.d.ts +51 -0
  76. package/dist/zoom/ZoomPan.d.ts.map +1 -0
  77. package/dist/zoom/controls.d.ts +14 -0
  78. package/dist/zoom/controls.d.ts.map +1 -0
  79. package/dist/zoom/gestures.d.ts +28 -0
  80. package/dist/zoom/gestures.d.ts.map +1 -0
  81. package/dist/zoom/scroll-hint.d.ts +8 -0
  82. package/dist/zoom/scroll-hint.d.ts.map +1 -0
  83. package/dist/zoom/zoom-pan.d.ts +53 -0
  84. package/dist/zoom/zoom-pan.d.ts.map +1 -0
  85. package/package.json +97 -0
@@ -0,0 +1,1617 @@
1
+ import { jsxs as rt, Fragment as $, jsx as W } from "react/jsx-runtime";
2
+ import at, { useRef as C, useEffect as Y, forwardRef as ct, useImperativeHandle as lt } from "react";
3
+ import { createPortal as ht } from "react-dom";
4
+ const pt = {
5
+ alt: "",
6
+ trigger: "hover",
7
+ zoom: !1,
8
+ zoomMax: 4,
9
+ zoomMin: 1,
10
+ theme: "light",
11
+ pulse: !0,
12
+ zoomControls: !0,
13
+ placement: "top",
14
+ lazyLoad: !0,
15
+ sceneTransition: "fade",
16
+ scrollHint: !0,
17
+ invertMarkerTheme: !1,
18
+ fullscreenButton: !0,
19
+ zoomControlsPosition: "bottom-right"
20
+ }, dt = {
21
+ "data-ci-hotspot-src": { key: "src", type: "string" },
22
+ "data-ci-hotspot-alt": { key: "alt", type: "string" },
23
+ "data-ci-hotspot-items": { key: "hotspots", type: "json" },
24
+ "data-ci-hotspot-trigger": { key: "trigger", type: "string" },
25
+ "data-ci-hotspot-zoom": { key: "zoom", type: "boolean" },
26
+ "data-ci-hotspot-zoom-max": { key: "zoomMax", type: "number" },
27
+ "data-ci-hotspot-zoom-min": { key: "zoomMin", type: "number" },
28
+ "data-ci-hotspot-theme": { key: "theme", type: "string" },
29
+ "data-ci-hotspot-pulse": { key: "pulse", type: "boolean" },
30
+ "data-ci-hotspot-placement": { key: "placement", type: "string" },
31
+ "data-ci-hotspot-lazy-load": { key: "lazyLoad", type: "boolean" },
32
+ "data-ci-hotspot-zoom-controls": { key: "zoomControls", type: "boolean" },
33
+ "data-ci-hotspot-scroll-hint": { key: "scrollHint", type: "boolean" },
34
+ "data-ci-hotspot-ci-token": { key: "token", type: "string", nested: "cloudimage" },
35
+ "data-ci-hotspot-ci-api-version": { key: "apiVersion", type: "string", nested: "cloudimage" },
36
+ "data-ci-hotspot-ci-domain": { key: "domain", type: "string", nested: "cloudimage" },
37
+ "data-ci-hotspot-ci-limit-factor": { key: "limitFactor", type: "number", nested: "cloudimage" },
38
+ "data-ci-hotspot-ci-params": { key: "params", type: "string", nested: "cloudimage" },
39
+ "data-ci-hotspot-scenes": { key: "scenes", type: "json" },
40
+ "data-ci-hotspot-initial-scene": { key: "initialScene", type: "string" },
41
+ "data-ci-hotspot-scene-transition": { key: "sceneTransition", type: "string" },
42
+ "data-ci-hotspot-scene-aspect-ratio": { key: "sceneAspectRatio", type: "string" },
43
+ "data-ci-hotspot-invert-marker-theme": { key: "invertMarkerTheme", type: "boolean" },
44
+ "data-ci-hotspot-fullscreen-button": { key: "fullscreenButton", type: "boolean" },
45
+ "data-ci-hotspot-zoom-controls-position": { key: "zoomControlsPosition", type: "string" }
46
+ };
47
+ function ut(i) {
48
+ const t = {}, e = {};
49
+ for (const [o, s] of Object.entries(dt)) {
50
+ const c = i.getAttribute(o);
51
+ if (c === null) continue;
52
+ const n = ft(c, s.type);
53
+ s.nested === "cloudimage" ? e[s.key] = n : t[s.key] = n;
54
+ }
55
+ return Object.keys(e).length > 0 && (t.cloudimage = e), t;
56
+ }
57
+ function ft(i, t) {
58
+ switch (t) {
59
+ case "boolean":
60
+ return i === "true";
61
+ case "number": {
62
+ const e = parseFloat(i);
63
+ if (isNaN(e)) {
64
+ console.warn(`CIHotspot: invalid number value "${i}"`);
65
+ return;
66
+ }
67
+ return e;
68
+ }
69
+ case "json":
70
+ try {
71
+ return JSON.parse(i);
72
+ } catch {
73
+ console.warn(`CIHotspot: failed to parse JSON value "${i}"`);
74
+ return;
75
+ }
76
+ default:
77
+ return i;
78
+ }
79
+ }
80
+ function j(i) {
81
+ return {
82
+ ...pt,
83
+ ...i,
84
+ src: i.src || "",
85
+ hotspots: i.hotspots || []
86
+ };
87
+ }
88
+ function V(i) {
89
+ if (i.scenes && i.scenes.length > 0) {
90
+ const t = /* @__PURE__ */ new Set();
91
+ for (const e of i.scenes) {
92
+ if (!e.id) throw new Error('CIHotspot: each scene must have an "id"');
93
+ if (t.has(e.id))
94
+ throw new Error(`CIHotspot: duplicate scene ID "${e.id}"`);
95
+ if (t.add(e.id), !e.src) throw new Error(`CIHotspot: scene "${e.id}" must have a "src"`);
96
+ }
97
+ for (const e of i.scenes)
98
+ for (const o of e.hotspots || [])
99
+ if (o.navigateTo && !t.has(o.navigateTo))
100
+ throw new Error(`CIHotspot: hotspot "${o.id}" navigateTo "${o.navigateTo}" is not a valid scene ID`);
101
+ if (i.initialScene && !t.has(i.initialScene))
102
+ throw new Error(`CIHotspot: initialScene "${i.initialScene}" not found in scenes`);
103
+ } else if (!i.src)
104
+ throw new Error('CIHotspot: "src" is required');
105
+ }
106
+ const B = "ci-hotspot-styles";
107
+ function ot() {
108
+ return typeof window < "u" && typeof document < "u";
109
+ }
110
+ function mt(i) {
111
+ if (typeof i == "string") {
112
+ const t = document.querySelector(i);
113
+ if (!t) throw new Error(`CIHotspot: element "${i}" not found`);
114
+ return t;
115
+ }
116
+ return i;
117
+ }
118
+ function b(i, t, e) {
119
+ const o = document.createElement(i);
120
+ if (t && (o.className = t), e)
121
+ for (const [s, c] of Object.entries(e))
122
+ o.setAttribute(s, c);
123
+ return o;
124
+ }
125
+ function g(i, ...t) {
126
+ i.classList.add(...t);
127
+ }
128
+ function w(i, ...t) {
129
+ i.classList.remove(...t);
130
+ }
131
+ function gt(i) {
132
+ if (!ot() || document.getElementById(B)) return;
133
+ const t = document.createElement("style");
134
+ t.id = B, t.textContent = i, document.head.appendChild(t);
135
+ }
136
+ function _(i) {
137
+ if (typeof i == "string") {
138
+ const t = i.trim();
139
+ return t.endsWith("%") ? { value: parseFloat(t), isPercent: !0 } : { value: parseFloat(t), isPercent: !1 };
140
+ }
141
+ return { value: i, isPercent: !1 };
142
+ }
143
+ function G(i, t, e, o) {
144
+ const s = _(i), c = _(t);
145
+ return {
146
+ x: s.isPercent ? s.value : s.value / e * 100,
147
+ y: c.isPercent ? c.value : c.value / o * 100
148
+ };
149
+ }
150
+ function m(i, t, e, o) {
151
+ return i.addEventListener(t, e, o), () => i.removeEventListener(t, e, o);
152
+ }
153
+ function vt(i, t) {
154
+ const e = b("button", "ci-hotspot-marker", {
155
+ "aria-label": i.label,
156
+ "aria-expanded": "false",
157
+ "data-hotspot-id": i.id,
158
+ tabindex: "0"
159
+ });
160
+ if (e.style.left = `${i.x}%`, e.style.top = `${i.y}%`, i.className) {
161
+ const o = i.className.trim().split(/\s+/).filter(Boolean);
162
+ o.length && g(e, ...o);
163
+ }
164
+ return i.hidden && g(e, "ci-hotspot-marker--hidden"), t && g(e, "ci-hotspot-marker--pulse"), i.icon && wt(e, i.icon), e;
165
+ }
166
+ function bt(i) {
167
+ if (typeof DOMParser > "u") return "";
168
+ const e = new DOMParser().parseFromString(i, "image/svg+xml").documentElement;
169
+ return e.querySelector("parsererror") ? "" : (it(e), new XMLSerializer().serializeToString(e));
170
+ }
171
+ const yt = /* @__PURE__ */ new Set([
172
+ "script",
173
+ "foreignobject",
174
+ "iframe",
175
+ "object",
176
+ "embed",
177
+ "animate",
178
+ "animatetransform",
179
+ "animatemotion",
180
+ "set",
181
+ "style",
182
+ "a",
183
+ "use",
184
+ "image",
185
+ "feimage"
186
+ ]);
187
+ function it(i) {
188
+ for (const t of Array.from(i.attributes)) {
189
+ const e = t.name.toLowerCase();
190
+ (e.startsWith("on") || e === "style" || (e === "href" || e === "xlink:href") && /^\s*javascript\s*:/i.test(t.value)) && i.removeAttribute(t.name);
191
+ }
192
+ for (const t of Array.from(i.children)) {
193
+ if (yt.has(t.tagName.toLowerCase())) {
194
+ t.remove();
195
+ continue;
196
+ }
197
+ it(t);
198
+ }
199
+ }
200
+ function wt(i, t) {
201
+ const e = t.trim();
202
+ if (/^<svg[\s>]/i.test(e) || /^<\?xml/i.test(e))
203
+ i.innerHTML = bt(t);
204
+ else if (t.match(/\.(png|jpg|jpeg|gif|svg|webp)$/i) || t.startsWith("http") || t.startsWith("/")) {
205
+ const o = b("img", void 0, {
206
+ src: t,
207
+ alt: "",
208
+ "aria-hidden": "true"
209
+ });
210
+ o.style.width = "100%", o.style.height = "100%", o.style.objectFit = "contain", i.appendChild(o);
211
+ } else {
212
+ const o = b("span", t, { "aria-hidden": "true" });
213
+ i.appendChild(o);
214
+ }
215
+ }
216
+ function f(i, t) {
217
+ t ? (g(i, "ci-hotspot-marker--active"), i.setAttribute("aria-expanded", "true")) : (w(i, "ci-hotspot-marker--active"), i.setAttribute("aria-expanded", "false"));
218
+ }
219
+ function kt(i, t) {
220
+ t ? g(i, "ci-hotspot-marker--hidden") : w(i, "ci-hotspot-marker--hidden");
221
+ }
222
+ function L(i) {
223
+ i.remove();
224
+ }
225
+ function xt(i, t, e, o) {
226
+ const s = i.getBoundingClientRect(), c = e.getBoundingClientRect(), n = t.offsetWidth, r = t.offsetHeight, a = s.left + s.width / 2 - c.left, l = s.top + s.height / 2 - c.top, d = s.top - c.top, h = s.bottom - c.top, p = s.left - c.left, u = s.right - c.left, k = e.offsetWidth, v = e.offsetHeight, y = 8, H = {
227
+ top: d - y,
228
+ bottom: v - h - y,
229
+ left: p - y,
230
+ right: k - u - y
231
+ };
232
+ let E = o.placement;
233
+ E === "auto" && (E = Et(H)), E = zt(E, n, r, H);
234
+ let z, T, N = 0;
235
+ switch (E) {
236
+ case "top":
237
+ z = a - n / 2, T = d - y - r;
238
+ break;
239
+ case "bottom":
240
+ z = a - n / 2, T = h + y;
241
+ break;
242
+ case "left":
243
+ z = p - y - n, T = l - r / 2;
244
+ break;
245
+ case "right":
246
+ z = u + y, T = l - r / 2;
247
+ break;
248
+ default:
249
+ z = a - n / 2, T = d - y - r;
250
+ }
251
+ const M = Tt(z, T, n, r, k, v);
252
+ return N = E === "top" || E === "bottom" ? z - M.x : T - M.y, {
253
+ x: M.x,
254
+ y: M.y,
255
+ placement: E,
256
+ arrowOffset: N
257
+ };
258
+ }
259
+ function Et(i) {
260
+ const t = Math.max(i.top, i.bottom, i.left, i.right);
261
+ return t === i.top ? "top" : t === i.bottom ? "bottom" : t === i.right ? "right" : "left";
262
+ }
263
+ function zt(i, t, e, o) {
264
+ const s = i;
265
+ switch (s) {
266
+ case "top":
267
+ if (o.top < e && o.bottom > o.top) return "bottom";
268
+ break;
269
+ case "bottom":
270
+ if (o.bottom < e && o.top > o.bottom) return "top";
271
+ break;
272
+ case "left":
273
+ if (o.left < t && o.right > o.left) return "right";
274
+ break;
275
+ case "right":
276
+ if (o.right < t && o.left > o.right) return "left";
277
+ break;
278
+ }
279
+ return s;
280
+ }
281
+ function Tt(i, t, e, o, s, c) {
282
+ let r = i, a = t;
283
+ return e > s - 2 * 4 ? r = (s - e) / 2 : (r < 4 && (r = 4), r + e > s - 4 && (r = s - 4 - e)), o > c - 2 * 4 ? a = (c - o) / 2 : (a < 4 && (a = 4), a + o > c - 4 && (a = c - 4 - o)), { x: r, y: a };
284
+ }
285
+ const Ct = /* @__PURE__ */ new Set([
286
+ "a",
287
+ "b",
288
+ "br",
289
+ "div",
290
+ "em",
291
+ "h1",
292
+ "h2",
293
+ "h3",
294
+ "h4",
295
+ "h5",
296
+ "h6",
297
+ "i",
298
+ "img",
299
+ "li",
300
+ "ol",
301
+ "p",
302
+ "span",
303
+ "strong",
304
+ "ul"
305
+ ]), St = /* @__PURE__ */ new Set([
306
+ "class",
307
+ "href",
308
+ "src",
309
+ "alt",
310
+ "title",
311
+ "target",
312
+ "rel"
313
+ ]), Pt = /^(?:https?:|mailto:)/i, Ht = /^(?:https?:|data:image\/(?!svg[+%]))/i, Mt = /* @__PURE__ */ new Set([
314
+ "noopener",
315
+ "noreferrer",
316
+ "nofollow",
317
+ "external",
318
+ "author",
319
+ "help",
320
+ "license",
321
+ "next",
322
+ "prev",
323
+ "search",
324
+ "tag",
325
+ "bookmark"
326
+ ]);
327
+ function Lt(i) {
328
+ const o = new DOMParser().parseFromString(`<body>${i}</body>`, "text/html").body;
329
+ return st(o), o.innerHTML;
330
+ }
331
+ function st(i) {
332
+ const t = Array.from(i.childNodes);
333
+ for (const e of t)
334
+ if (e.nodeType !== Node.TEXT_NODE)
335
+ if (e.nodeType === Node.ELEMENT_NODE) {
336
+ const o = e, s = o.tagName.toLowerCase();
337
+ if (!Ct.has(s)) {
338
+ o.remove();
339
+ continue;
340
+ }
341
+ const c = Array.from(o.attributes);
342
+ for (const n of c) {
343
+ const r = n.name.toLowerCase();
344
+ if (r.startsWith("on")) {
345
+ o.removeAttribute(n.name);
346
+ continue;
347
+ }
348
+ if (!St.has(r)) {
349
+ o.removeAttribute(n.name);
350
+ continue;
351
+ }
352
+ if (r === "href" && !Pt.test(n.value.trim()))
353
+ o.removeAttribute(n.name);
354
+ else if (r === "src" && !Ht.test(n.value.trim()))
355
+ o.removeAttribute(n.name);
356
+ else if (r === "rel") {
357
+ const a = n.value.trim().toLowerCase().split(/\s+/).filter((l) => Mt.has(l));
358
+ a.length === 0 ? o.removeAttribute(n.name) : o.setAttribute(n.name, a.join(" "));
359
+ }
360
+ }
361
+ st(o);
362
+ } else
363
+ e.remove();
364
+ }
365
+ function At(i) {
366
+ const t = [];
367
+ i.image && t.push(`<img class="ci-hotspot-popover-image" src="${I(i.image)}" alt="${I(i.title || "")}">`);
368
+ const e = [];
369
+ if (i.title && e.push(`<h3 class="ci-hotspot-popover-title">${A(i.title)}</h3>`), i.price && e.push(`<span class="ci-hotspot-popover-price">${A(i.price)}</span>`), i.description && e.push(`<p class="ci-hotspot-popover-description">${A(i.description)}</p>`), i.url && Dt(i.url)) {
370
+ const o = i.ctaText || "View details";
371
+ e.push(
372
+ `<a class="ci-hotspot-popover-cta" href="${I(i.url)}">${A(String(o))}</a>`
373
+ );
374
+ }
375
+ return e.length > 0 && t.push(`<div class="ci-hotspot-popover-body">${e.join("")}</div>`), t.join("");
376
+ }
377
+ function Ft(i, t) {
378
+ return t ? t(i) : i.content ? Lt(i.content) : i.data ? At(i.data) : "";
379
+ }
380
+ function A(i) {
381
+ return i.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
382
+ }
383
+ function Dt(i) {
384
+ const t = i.replace(/[\s\x00-\x1f]/g, "");
385
+ return /^https?:\/\//i.test(t) || /^\/(?!\/)/.test(t) || /^#/.test(t);
386
+ }
387
+ function I(i) {
388
+ return i.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&#39;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
389
+ }
390
+ class q {
391
+ constructor(t, e) {
392
+ this.visible = !1, this.markerEl = null, this.containerEl = null, this.hoverCleanups = [], this.hotspot = t, this.options = e;
393
+ const o = e.triggerMode === "click" || e.triggerMode === "load";
394
+ this.element = b("div", "ci-hotspot-popover", {
395
+ role: o ? "dialog" : "tooltip",
396
+ id: `ci-hotspot-popover-${t.id}`,
397
+ "aria-hidden": "true",
398
+ "data-placement": e.placement === "auto" ? "top" : e.placement,
399
+ ...o && t.label ? { "aria-label": t.label } : {}
400
+ }), this.arrowEl = b("div", "ci-hotspot-popover-arrow"), this.contentEl = b("div", "ci-hotspot-popover-content"), this.element.appendChild(this.arrowEl), this.element.appendChild(this.contentEl);
401
+ const s = Ft(t, e.renderFn);
402
+ if (typeof s == "string" ? this.contentEl.innerHTML = s : s instanceof HTMLElement && this.contentEl.appendChild(s), e.triggerMode === "hover") {
403
+ const c = () => this.clearHideTimer(), n = () => this.scheduleHide();
404
+ this.element.addEventListener("mouseenter", c), this.element.addEventListener("mouseleave", n), this.hoverCleanups.push(
405
+ () => this.element.removeEventListener("mouseenter", c),
406
+ () => this.element.removeEventListener("mouseleave", n)
407
+ );
408
+ }
409
+ }
410
+ /** Mount the popover to a container, associating it with a marker */
411
+ mount(t, e) {
412
+ this.containerEl = t, this.markerEl = e, t.appendChild(this.element), this.options.triggerMode === "click" || this.options.triggerMode === "load" ? (e.setAttribute("aria-haspopup", "dialog"), e.setAttribute("aria-controls", this.element.id)) : e.setAttribute("aria-describedby", this.element.id);
413
+ }
414
+ /** Show the popover */
415
+ show() {
416
+ var t, e;
417
+ this.clearHideTimer(), !this.visible && (this.visible = !0, g(this.element, "ci-hotspot-popover--visible"), this.element.setAttribute("aria-hidden", "false"), this.updatePosition(), (e = (t = this.options).onOpen) == null || e.call(t, this.hotspot));
418
+ }
419
+ /** Schedule hide with delay (for hover mode) */
420
+ scheduleHide(t = 200) {
421
+ this.clearHideTimer(), this.hideTimer = setTimeout(() => {
422
+ this.hide();
423
+ }, t);
424
+ }
425
+ /** Hide the popover immediately */
426
+ hide() {
427
+ var t, e;
428
+ this.clearHideTimer(), this.visible && (this.visible = !1, w(this.element, "ci-hotspot-popover--visible"), this.element.setAttribute("aria-hidden", "true"), (e = (t = this.options).onClose) == null || e.call(t, this.hotspot));
429
+ }
430
+ /** Clear any pending hide timer */
431
+ clearHideTimer() {
432
+ this.hideTimer !== void 0 && (clearTimeout(this.hideTimer), this.hideTimer = void 0);
433
+ }
434
+ /** Update popover position relative to marker */
435
+ updatePosition() {
436
+ if (!this.markerEl || !this.containerEl || !this.visible) return;
437
+ const t = xt(
438
+ this.markerEl,
439
+ this.element,
440
+ this.containerEl,
441
+ { placement: this.options.placement }
442
+ );
443
+ this.element.style.left = `${t.x}px`, this.element.style.top = `${t.y}px`, this.element.setAttribute("data-placement", t.placement), this.positionArrow(t.placement, t.arrowOffset);
444
+ }
445
+ positionArrow(t, e) {
446
+ const o = this.arrowEl;
447
+ o.style.left = "", o.style.top = "", t === "top" || t === "bottom" ? o.style.left = `calc(50% - var(--ci-hotspot-arrow-size) + ${e}px)` : o.style.top = `calc(50% - var(--ci-hotspot-arrow-size) + ${e}px)`;
448
+ }
449
+ /** Check if popover is currently visible */
450
+ isVisible() {
451
+ return this.visible;
452
+ }
453
+ /** Get the hotspot associated with this popover */
454
+ getHotspot() {
455
+ return this.hotspot;
456
+ }
457
+ /** Destroy the popover and clean up */
458
+ destroy() {
459
+ var t, e, o;
460
+ this.clearHideTimer(), this.hoverCleanups.forEach((s) => s()), this.hoverCleanups = [], (t = this.markerEl) == null || t.removeAttribute("aria-describedby"), (e = this.markerEl) == null || e.removeAttribute("aria-controls"), (o = this.markerEl) == null || o.removeAttribute("aria-haspopup"), this.element.remove(), this.markerEl = null, this.containerEl = null;
461
+ }
462
+ }
463
+ class Rt {
464
+ constructor(t, e, o) {
465
+ this.lastTouchEnd = 0, this.initialPinchDistance = 0, this.initialPinchScale = 1, this.panStartX = 0, this.panStartY = 0, this.isPinching = !1, this.isPanning = !1, this.wasPinching = !1, this.cleanups = [], this.el = t, this.callbacks = e, this.initialPinchScale = o();
466
+ const s = (r) => this.handleTouchStart(r, o), c = (r) => this.handleTouchMove(r), n = (r) => this.handleTouchEnd(r);
467
+ t.addEventListener("touchstart", s, { passive: !1 }), t.addEventListener("touchmove", c, { passive: !1 }), t.addEventListener("touchend", n, { passive: !0 }), this.cleanups.push(
468
+ () => t.removeEventListener("touchstart", s),
469
+ () => t.removeEventListener("touchmove", c),
470
+ () => t.removeEventListener("touchend", n)
471
+ );
472
+ }
473
+ handleTouchStart(t, e) {
474
+ t.touches.length === 2 ? (t.preventDefault(), this.isPinching = !0, this.wasPinching = !1, this.initialPinchDistance = this.getTouchDistance(t.touches), this.initialPinchScale = e()) : t.touches.length === 1 && (this.panStartX = t.touches[0].clientX, this.panStartY = t.touches[0].clientY, this.wasPinching = !1);
475
+ }
476
+ handleTouchMove(t) {
477
+ var e, o, s, c, n, r;
478
+ if (t.touches.length === 2 && this.isPinching) {
479
+ t.preventDefault();
480
+ const a = this.getTouchDistance(t.touches), l = this.initialPinchScale * (a / this.initialPinchDistance), d = (t.touches[0].clientX + t.touches[1].clientX) / 2, h = (t.touches[0].clientY + t.touches[1].clientY) / 2;
481
+ (o = (e = this.callbacks).onPinch) == null || o.call(e, l, d, h);
482
+ } else if (t.touches.length === 1 && !this.isPinching && !this.wasPinching) {
483
+ this.isPanning || (this.isPanning = !0, (c = (s = this.callbacks).onPanStart) == null || c.call(s));
484
+ const a = t.touches[0].clientX - this.panStartX, l = t.touches[0].clientY - this.panStartY;
485
+ (r = (n = this.callbacks).onPan) == null || r.call(n, a, l);
486
+ }
487
+ }
488
+ handleTouchEnd(t) {
489
+ var e, o, s, c;
490
+ if (this.isPinching && t.touches.length < 2 && (this.isPinching = !1, this.wasPinching = !0, t.touches.length === 1 && (this.panStartX = t.touches[0].clientX, this.panStartY = t.touches[0].clientY)), this.isPanning && (this.isPanning = !1, (o = (e = this.callbacks).onPanEnd) == null || o.call(e)), t.changedTouches.length === 1 && t.touches.length === 0) {
491
+ if (this.wasPinching) {
492
+ this.wasPinching = !1, this.lastTouchEnd = 0;
493
+ return;
494
+ }
495
+ const n = Date.now();
496
+ if (n - this.lastTouchEnd < 300) {
497
+ const r = t.changedTouches[0];
498
+ (c = (s = this.callbacks).onDoubleTap) == null || c.call(s, r.clientX, r.clientY);
499
+ }
500
+ this.lastTouchEnd = n;
501
+ }
502
+ }
503
+ getTouchDistance(t) {
504
+ const e = t[0].clientX - t[1].clientX, o = t[0].clientY - t[1].clientY;
505
+ return Math.sqrt(e * e + o * o);
506
+ }
507
+ destroy() {
508
+ this.cleanups.forEach((t) => t()), this.cleanups = [];
509
+ }
510
+ }
511
+ class It {
512
+ constructor(t, e, o) {
513
+ this.zoom = 1, this.panX = 0, this.panY = 0, this.enabled = !0, this.isDragging = !1, this.dragStartX = 0, this.dragStartY = 0, this.lastPanX = 0, this.lastPanY = 0, this.gestures = null, this.isGesturing = !1, this.gestureStartZoom = 1, this.touchStartPanX = 0, this.touchStartPanY = 0, this.cleanups = [], this.viewport = t, this.container = e, this.options = o, this.bindEvents();
514
+ }
515
+ bindEvents() {
516
+ const t = (n) => {
517
+ var p, u;
518
+ if (!this.enabled) return;
519
+ if (this.isGesturing) {
520
+ n.preventDefault();
521
+ return;
522
+ }
523
+ if (!n.ctrlKey) {
524
+ (u = (p = this.options).onScrollWithoutZoom) == null || u.call(p);
525
+ return;
526
+ }
527
+ n.preventDefault();
528
+ const r = this.container.getBoundingClientRect(), a = n.clientX - r.left, l = n.clientY - r.top;
529
+ let d = n.deltaY;
530
+ n.deltaMode === 1 && (d *= 20);
531
+ const h = -d * 0.01;
532
+ this.setZoom(this.zoom + h, a, l);
533
+ };
534
+ this.container.addEventListener("wheel", t, { passive: !1 }), this.cleanups.push(() => this.container.removeEventListener("wheel", t)), this.bindSafariGestures();
535
+ const e = (n) => {
536
+ if (!this.enabled) return;
537
+ const r = this.container.getBoundingClientRect(), a = n.clientX - r.left, l = n.clientY - r.top;
538
+ this.zoom > 1 ? this.resetZoom() : this.setZoom(2, a, l);
539
+ };
540
+ this.container.addEventListener("dblclick", e), this.cleanups.push(() => this.container.removeEventListener("dblclick", e));
541
+ const o = (n) => {
542
+ !this.enabled || this.zoom <= 1 || n.button === 0 && (this.isDragging = !0, this.dragStartX = n.clientX, this.dragStartY = n.clientY, this.lastPanX = this.panX, this.lastPanY = this.panY, g(this.viewport, "ci-hotspot-viewport--dragging"), this.container.style.cursor = "grabbing", n.preventDefault());
543
+ }, s = (n) => {
544
+ if (!this.isDragging) return;
545
+ const r = (n.clientX - this.dragStartX) / this.zoom, a = (n.clientY - this.dragStartY) / this.zoom;
546
+ this.panX = this.lastPanX + r, this.panY = this.lastPanY + a, this.clampPan(), this.applyTransform();
547
+ }, c = () => {
548
+ this.isDragging && (this.isDragging = !1, w(this.viewport, "ci-hotspot-viewport--dragging"), this.container.style.cursor = this.zoom > 1 ? "grab" : "");
549
+ };
550
+ this.container.addEventListener("mousedown", o), document.addEventListener("mousemove", s), document.addEventListener("mouseup", c), this.cleanups.push(
551
+ () => this.container.removeEventListener("mousedown", o),
552
+ () => document.removeEventListener("mousemove", s),
553
+ () => document.removeEventListener("mouseup", c)
554
+ ), this.gestures = new Rt(
555
+ this.container,
556
+ {
557
+ onPinch: (n, r, a) => {
558
+ const l = this.container.getBoundingClientRect();
559
+ this.setZoom(n, r - l.left, a - l.top);
560
+ },
561
+ onPanStart: () => {
562
+ this.touchStartPanX = this.panX, this.touchStartPanY = this.panY;
563
+ },
564
+ onPan: (n, r) => {
565
+ this.zoom <= 1 || (this.panX = this.touchStartPanX + n / this.zoom, this.panY = this.touchStartPanY + r / this.zoom, this.clampPan(), this.applyTransform());
566
+ },
567
+ onDoubleTap: (n, r) => {
568
+ const a = this.container.getBoundingClientRect();
569
+ this.zoom > 1 ? this.resetZoom() : this.setZoom(2, n - a.left, r - a.top);
570
+ }
571
+ },
572
+ () => this.zoom
573
+ );
574
+ }
575
+ bindSafariGestures() {
576
+ if (typeof window > "u" || !("GestureEvent" in window)) return;
577
+ const t = (s) => {
578
+ s.preventDefault(), this.isGesturing = !0, this.gestureStartZoom = this.zoom;
579
+ }, e = (s) => {
580
+ if (s.preventDefault(), !this.enabled) return;
581
+ const c = s, n = this.container.getBoundingClientRect(), r = s, a = r.clientX != null ? r.clientX - n.left : n.width / 2, l = r.clientY != null ? r.clientY - n.top : n.height / 2;
582
+ this.setZoom(this.gestureStartZoom * c.scale, a, l);
583
+ }, o = (s) => {
584
+ s.preventDefault(), this.isGesturing = !1;
585
+ };
586
+ this.container.addEventListener("gesturestart", t), this.container.addEventListener("gesturechange", e), this.container.addEventListener("gestureend", o), this.cleanups.push(
587
+ () => this.container.removeEventListener("gesturestart", t),
588
+ () => this.container.removeEventListener("gesturechange", e),
589
+ () => this.container.removeEventListener("gestureend", o)
590
+ );
591
+ }
592
+ setZoom(t, e, o) {
593
+ var c, n;
594
+ const s = this.zoom;
595
+ if (this.zoom = Math.max(this.options.zoomMin, Math.min(this.options.zoomMax, t)), e !== void 0 && o !== void 0 && s !== this.zoom) {
596
+ const r = this.container.offsetWidth, a = this.container.offsetHeight, l = this.zoom / s, d = e / r, h = o / a;
597
+ this.panX = this.panX - r * d * (l - 1) / this.zoom, this.panY = this.panY - a * h * (l - 1) / this.zoom;
598
+ }
599
+ this.clampPan(), this.applyTransform(), this.updateCursor(), (n = (c = this.options).onZoom) == null || n.call(c, this.zoom);
600
+ }
601
+ getZoom() {
602
+ return this.zoom;
603
+ }
604
+ resetZoom() {
605
+ var t, e;
606
+ this.zoom = 1, this.panX = 0, this.panY = 0, this.applyTransform(), this.updateCursor(), (e = (t = this.options).onZoom) == null || e.call(t, 1);
607
+ }
608
+ pan(t, e) {
609
+ this.zoom <= 1 || (this.panX += t / this.zoom, this.panY += e / this.zoom, this.clampPan(), this.applyTransform());
610
+ }
611
+ enable() {
612
+ this.enabled = !0;
613
+ }
614
+ disable() {
615
+ this.enabled = !1;
616
+ }
617
+ clampPan() {
618
+ const t = this.container.offsetWidth, e = this.container.offsetHeight, o = t * (this.zoom - 1) / this.zoom, s = e * (this.zoom - 1) / this.zoom;
619
+ this.panX = Math.max(-o, Math.min(0, this.panX)), this.panY = Math.max(-s, Math.min(0, this.panY)), this.zoom <= 1 && (this.panX = 0, this.panY = 0);
620
+ }
621
+ applyTransform() {
622
+ this.viewport.style.transform = `scale(${this.zoom}) translate(${this.panX}px, ${this.panY}px)`, this.viewport.style.setProperty("--zoom", String(this.zoom));
623
+ }
624
+ updateCursor() {
625
+ this.isDragging || (this.container.style.cursor = this.zoom > 1 ? "grab" : "");
626
+ }
627
+ destroy() {
628
+ var t;
629
+ this.cleanups.forEach((e) => e()), this.cleanups = [], (t = this.gestures) == null || t.destroy(), this.gestures = null, this.viewport.style.transform = "", this.viewport.style.removeProperty("--zoom"), this.container.style.cursor = "";
630
+ }
631
+ }
632
+ function Zt(i, t, e) {
633
+ const o = e.zoomStep || 0.5, s = b("div", "ci-hotspot-zoom-controls");
634
+ s.dataset.position = e.position || "bottom-right";
635
+ const c = b("button", "ci-hotspot-zoom-in", {
636
+ "aria-label": "Zoom in",
637
+ type: "button"
638
+ });
639
+ c.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" x2="16.65" y1="21" y2="16.65"/><line x1="11" x2="11" y1="8" y2="14"/><line x1="8" x2="14" y1="11" y2="11"/></svg>';
640
+ const n = b("button", "ci-hotspot-zoom-out", {
641
+ "aria-label": "Zoom out",
642
+ type: "button"
643
+ });
644
+ n.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" x2="16.65" y1="21" y2="16.65"/><line x1="8" x2="14" y1="11" y2="11"/></svg>';
645
+ const r = b("button", "ci-hotspot-zoom-reset", {
646
+ "aria-label": "Reset zoom",
647
+ type: "button"
648
+ });
649
+ r.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg>', s.appendChild(c), s.appendChild(n), s.appendChild(r), c.addEventListener("click", (l) => {
650
+ l.stopPropagation(), t.setZoom(t.getZoom() + o), a();
651
+ }), n.addEventListener("click", (l) => {
652
+ l.stopPropagation(), t.setZoom(t.getZoom() - o), a();
653
+ }), r.addEventListener("click", (l) => {
654
+ l.stopPropagation(), t.resetZoom(), a();
655
+ });
656
+ function a() {
657
+ const l = t.getZoom();
658
+ c.disabled = l >= e.zoomMax, n.disabled = l <= e.zoomMin, r.disabled = Math.abs(l - 1) < 1e-3;
659
+ }
660
+ return i.appendChild(s), a(), {
661
+ element: s,
662
+ update: a,
663
+ destroy: () => s.remove()
664
+ };
665
+ }
666
+ const Ot = typeof navigator < "u" && /Mac|iPhone|iPad|iPod/i.test(navigator.userAgent), Yt = Ot ? "⌘ Scroll or pinch to zoom" : "Ctrl + scroll to zoom", Xt = 1500;
667
+ class Nt {
668
+ constructor(t) {
669
+ this.hideTimer = null, this.el = document.createElement("div"), this.el.className = "ci-hotspot-scroll-hint", this.el.textContent = Yt, this.el.setAttribute("aria-hidden", "true"), t.appendChild(this.el);
670
+ }
671
+ show() {
672
+ this.hideTimer !== null && clearTimeout(this.hideTimer), this.el.classList.add("ci-hotspot-scroll-hint--visible"), this.hideTimer = setTimeout(() => {
673
+ this.el.classList.remove("ci-hotspot-scroll-hint--visible"), this.hideTimer = null;
674
+ }, Xt);
675
+ }
676
+ destroy() {
677
+ this.hideTimer !== null && (clearTimeout(this.hideTimer), this.hideTimer = null), this.el.remove();
678
+ }
679
+ }
680
+ const $t = "cloudimg.io", Wt = "v7", R = 100;
681
+ function jt(i, t = R) {
682
+ return Math.ceil(i / t) * t;
683
+ }
684
+ function nt(i, t = 1, e = 1, o = R) {
685
+ const s = i * t * e;
686
+ return jt(s, o);
687
+ }
688
+ function S(i, t, e, o = 1, s = 1) {
689
+ const c = t.domain || $t, n = t.apiVersion || Wt, r = t.limitFactor || R, a = nt(e, s, o, r), l = encodeURI(i);
690
+ let d = `https://${t.token}.${c}/${n}/${l}?width=${a}`;
691
+ return t.params && (d += `&${t.params}`), d;
692
+ }
693
+ function Vt(i, t, e, o) {
694
+ const s = e.limitFactor || R;
695
+ let c = 0;
696
+ const n = new ResizeObserver((r) => {
697
+ for (const a of r) {
698
+ const l = a.contentRect.width;
699
+ if (l === 0) continue;
700
+ const d = typeof window < "u" && window.devicePixelRatio || 1, h = nt(l, d, o(), s);
701
+ h !== c && (c = h, i.src = S(t, e, l, o(), d));
702
+ }
703
+ });
704
+ return {
705
+ observer: n,
706
+ destroy: () => n.disconnect()
707
+ };
708
+ }
709
+ const F = 50, K = 0.5;
710
+ class Bt {
711
+ constructor(t) {
712
+ this.cleanups = [];
713
+ const { container: e, getZoomPan: o, onEscape: s, onFullscreenToggle: c } = t, n = m(e, "keydown", (r) => {
714
+ const a = r.target;
715
+ if (a.tagName === "INPUT" || a.tagName === "TEXTAREA" || a.tagName === "SELECT")
716
+ return;
717
+ const l = o();
718
+ switch (r.key) {
719
+ case "Escape":
720
+ s == null || s();
721
+ break;
722
+ case "ArrowUp":
723
+ l && l.getZoom() > 1 && (r.preventDefault(), l.pan(0, F));
724
+ break;
725
+ case "ArrowDown":
726
+ l && l.getZoom() > 1 && (r.preventDefault(), l.pan(0, -F));
727
+ break;
728
+ case "ArrowLeft":
729
+ l && l.getZoom() > 1 && (r.preventDefault(), l.pan(F, 0));
730
+ break;
731
+ case "ArrowRight":
732
+ l && l.getZoom() > 1 && (r.preventDefault(), l.pan(-F, 0));
733
+ break;
734
+ case "+":
735
+ case "=":
736
+ l && (r.preventDefault(), l.setZoom(l.getZoom() + K));
737
+ break;
738
+ case "-":
739
+ l && (r.preventDefault(), l.setZoom(l.getZoom() - K));
740
+ break;
741
+ case "0":
742
+ l && (r.preventDefault(), l.resetZoom());
743
+ break;
744
+ case "f":
745
+ c && (r.preventDefault(), c());
746
+ break;
747
+ }
748
+ });
749
+ this.cleanups.push(n);
750
+ }
751
+ destroy() {
752
+ this.cleanups.forEach((t) => t()), this.cleanups = [];
753
+ }
754
+ }
755
+ const _t = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
756
+ function U(i) {
757
+ return Array.from(i.querySelectorAll(_t));
758
+ }
759
+ function J(i, t) {
760
+ let e = !1, o = null;
761
+ function s() {
762
+ if (e) return;
763
+ e = !0;
764
+ const r = U(i);
765
+ r.length !== 0 && (r[0].focus(), o = m(i, "keydown", (a) => {
766
+ if (a.key !== "Tab") return;
767
+ const l = U(i);
768
+ if (l.length === 0) return;
769
+ const d = l[0], h = l[l.length - 1];
770
+ a.shiftKey && document.activeElement === d ? (a.preventDefault(), h.focus()) : !a.shiftKey && document.activeElement === h && (a.preventDefault(), d.focus());
771
+ }));
772
+ }
773
+ function c() {
774
+ e && (e = !1, o == null || o(), o = null, t.focus());
775
+ }
776
+ function n() {
777
+ c();
778
+ }
779
+ return { activate: s, deactivate: c, destroy: n };
780
+ }
781
+ let x = null, D = 0;
782
+ function Gt(i) {
783
+ ot() && (x || (x = b("div", void 0, {
784
+ "aria-live": "polite",
785
+ "aria-atomic": "true",
786
+ role: "status"
787
+ }), x.style.cssText = "position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0", document.body.appendChild(x)), x.textContent = "", requestAnimationFrame(() => {
788
+ x && (x.textContent = i);
789
+ }));
790
+ }
791
+ function Q() {
792
+ D++;
793
+ }
794
+ function qt() {
795
+ D = Math.max(0, D - 1), D === 0 && x && (x.remove(), x = null);
796
+ }
797
+ const tt = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 3 21 3 21 9"/><polyline points="9 21 3 21 3 15"/><line x1="21" x2="14" y1="3" y2="10"/><line x1="3" x2="10" y1="21" y2="14"/></svg>', Kt = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="4 14 10 14 10 20"/><polyline points="20 10 14 10 14 4"/><line x1="14" x2="21" y1="10" y2="3"/><line x1="3" x2="10" y1="21" y2="14"/></svg>';
798
+ function Ut() {
799
+ return !!(document.fullscreenEnabled || document.webkitFullscreenEnabled);
800
+ }
801
+ function Jt() {
802
+ return document.fullscreenElement || document.webkitFullscreenElement || null;
803
+ }
804
+ function et(i) {
805
+ return i.requestFullscreen ? i.requestFullscreen() : i.webkitRequestFullscreen ? (i.webkitRequestFullscreen(), Promise.resolve()) : Promise.reject(new Error("Fullscreen API not supported"));
806
+ }
807
+ function Z() {
808
+ return document.exitFullscreen ? document.exitFullscreen() : document.webkitExitFullscreen ? (document.webkitExitFullscreen(), Promise.resolve()) : Promise.reject(new Error("Fullscreen API not supported"));
809
+ }
810
+ function Qt(i, t = {}) {
811
+ if (!Ut()) return null;
812
+ const e = b("button", "ci-hotspot-fullscreen-btn", {
813
+ "aria-label": "Enter fullscreen",
814
+ "aria-pressed": "false",
815
+ type: "button"
816
+ });
817
+ e.innerHTML = tt;
818
+ const o = [];
819
+ function s() {
820
+ return Jt() === i;
821
+ }
822
+ function c() {
823
+ var u;
824
+ const p = s();
825
+ e.innerHTML = p ? Kt : tt, e.setAttribute("aria-label", p ? "Exit fullscreen" : "Enter fullscreen"), e.setAttribute("aria-pressed", String(p)), p ? g(i, "ci-hotspot-container--fullscreen") : w(i, "ci-hotspot-container--fullscreen"), (u = t.onChange) == null || u.call(t, p);
826
+ }
827
+ function n() {
828
+ s() ? Z().catch(() => {
829
+ }) : et(i).catch(() => {
830
+ });
831
+ }
832
+ function r() {
833
+ s() || et(i).catch(() => {
834
+ });
835
+ }
836
+ function a() {
837
+ s() && Z().catch(() => {
838
+ });
839
+ }
840
+ const l = m(document, "fullscreenchange", c);
841
+ o.push(l), document.addEventListener("webkitfullscreenchange", c), o.push(() => document.removeEventListener("webkitfullscreenchange", c));
842
+ const d = m(e, "click", (p) => {
843
+ p.stopPropagation(), n();
844
+ });
845
+ o.push(d), i.appendChild(e);
846
+ function h() {
847
+ s() && Z().catch(() => {
848
+ }), w(i, "ci-hotspot-container--fullscreen"), o.forEach((p) => p()), o.length = 0, e.remove();
849
+ }
850
+ return {
851
+ element: e,
852
+ isFullscreen: s,
853
+ toggle: n,
854
+ enter: r,
855
+ exit: a,
856
+ destroy: h
857
+ };
858
+ }
859
+ const te = '.ci-hotspot-container{--ci-hotspot-marker-size: 24px;--ci-hotspot-marker-color: #ffffff;--ci-hotspot-marker-bg: rgba(0, 0, 0, .6);--ci-hotspot-marker-border-width: 2px;--ci-hotspot-marker-border-color: rgba(255, 255, 255, .8);--ci-hotspot-marker-border: var(--ci-hotspot-marker-border-width) solid var(--ci-hotspot-marker-border-color);--ci-hotspot-marker-border-radius: 50%;--ci-hotspot-marker-shadow: 0 2px 8px rgba(0, 0, 0, .3);--ci-hotspot-pulse-color: rgba(0, 0, 0, .2);--ci-hotspot-pulse-size: 40px;--ci-hotspot-pulse-duration: 1.8s;--ci-hotspot-popover-bg: #ffffff;--ci-hotspot-popover-color: #1a1a1a;--ci-hotspot-popover-border: 1px solid rgba(0, 0, 0, .1);--ci-hotspot-popover-border-radius: 12px;--ci-hotspot-popover-shadow: 0 8px 32px rgba(0, 0, 0, .12);--ci-hotspot-popover-padding: 16px;--ci-hotspot-popover-max-width: 320px;--ci-hotspot-popover-max-height: 400px;--ci-hotspot-popover-font-family: inherit;--ci-hotspot-popover-font-size: 14px;--ci-hotspot-popover-line-height: 1.5;--ci-hotspot-popover-z-index: 1000;--ci-hotspot-arrow-size: 8px;--ci-hotspot-arrow-color: var(--ci-hotspot-popover-bg);--ci-hotspot-title-font-size: 16px;--ci-hotspot-title-font-weight: 600;--ci-hotspot-title-color: #1a1a1a;--ci-hotspot-price-color: #2d8c3c;--ci-hotspot-price-font-size: 18px;--ci-hotspot-price-font-weight: 700;--ci-hotspot-description-color: #666666;--ci-hotspot-cta-bg: #0058a3;--ci-hotspot-cta-color: #ffffff;--ci-hotspot-cta-border-radius: 8px;--ci-hotspot-cta-padding: 8px 16px;--ci-hotspot-hover-transition: .2s ease;--ci-hotspot-popover-transition: .3s ease;--ci-hotspot-scene-transition-duration: .4s;--ci-hotspot-zoom-controls-bg: rgba(255, 255, 255, .9);--ci-hotspot-zoom-controls-color: #333333;--ci-hotspot-zoom-controls-border-radius: 8px;--ci-hotspot-zoom-controls-shadow: 0 2px 8px rgba(0, 0, 0, .15)}.ci-hotspot-container *,.ci-hotspot-container *:before,.ci-hotspot-container *:after{box-sizing:border-box}.ci-hotspot-container{position:relative;overflow:hidden;width:100%;line-height:0;user-select:none;-webkit-user-select:none}.ci-hotspot-viewport{position:relative;width:100%;transform-origin:0 0;will-change:transform;transition:transform .3s ease}.ci-hotspot-viewport--dragging{transition:none}.ci-hotspot-image{display:block;width:100%;height:auto;pointer-events:none}.ci-hotspot-markers{position:absolute;top:0;right:0;bottom:0;left:0;pointer-events:none}.ci-hotspot-marker{position:absolute;width:var(--ci-hotspot-marker-size);height:var(--ci-hotspot-marker-size);padding:0;border:var(--ci-hotspot-marker-border);border-radius:var(--ci-hotspot-marker-border-radius);background:var(--ci-hotspot-marker-bg);color:var(--ci-hotspot-marker-color);box-shadow:var(--ci-hotspot-marker-shadow);cursor:pointer;pointer-events:auto;transform:translate(-50%,-50%) scale(calc(1 / var(--zoom, 1)));transition:transform var(--ci-hotspot-hover-transition),box-shadow var(--ci-hotspot-hover-transition);z-index:1;display:flex;align-items:center;justify-content:center;font-size:12px;line-height:1;outline:none}.ci-hotspot-marker:hover{transform:translate(-50%,-50%) scale(calc(1.2 / var(--zoom, 1)));box-shadow:0 4px 12px #0006}.ci-hotspot-marker:focus-visible{outline:3px solid var(--ci-hotspot-focus-ring, #4A90D9);outline-offset:2px}.ci-hotspot-marker--active{transform:translate(-50%,-50%) scale(calc(1.2 / var(--zoom, 1)));z-index:2}.ci-hotspot-marker--hidden{display:none}.ci-hotspot-marker--pulse:before{content:"";position:absolute;top:50%;left:50%;width:var(--ci-hotspot-pulse-size);height:var(--ci-hotspot-pulse-size);border-radius:50%;background:var(--ci-hotspot-pulse-color);transform:translate(-50%,-50%) scale(1);animation:ci-hotspot-pulse var(--ci-hotspot-pulse-duration) ease-out infinite;pointer-events:none}@keyframes ci-hotspot-pulse{0%{transform:translate(-50%,-50%) scale(1);opacity:1}to{transform:translate(-50%,-50%) scale(1.8);opacity:0}}.ci-hotspot-marker--pulse{animation:ci-hotspot-breathe 2.4s ease-in-out infinite}.ci-hotspot-marker--pulse:hover,.ci-hotspot-marker--pulse.ci-hotspot-marker--active{animation:none}@keyframes ci-hotspot-breathe{0%,to{transform:translate(-50%,-50%) scale(calc(1 / var(--zoom, 1)))}50%{transform:translate(-50%,-50%) scale(calc(1.15 / var(--zoom, 1)))}}.ci-hotspot-popover{position:absolute;z-index:var(--ci-hotspot-popover-z-index);max-width:var(--ci-hotspot-popover-max-width);background:var(--ci-hotspot-popover-bg);color:var(--ci-hotspot-popover-color);border:var(--ci-hotspot-popover-border);border-radius:var(--ci-hotspot-popover-border-radius);box-shadow:var(--ci-hotspot-popover-shadow);font-family:var(--ci-hotspot-popover-font-family);font-size:var(--ci-hotspot-popover-font-size);line-height:var(--ci-hotspot-popover-line-height);opacity:0;pointer-events:none;transform:translateY(4px);transition:opacity var(--ci-hotspot-popover-transition),transform var(--ci-hotspot-popover-transition)}.ci-hotspot-popover--visible{opacity:1;pointer-events:auto;transform:translateY(0);animation:ci-hotspot-popover-in var(--ci-hotspot-popover-transition) forwards}@keyframes ci-hotspot-popover-in{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.ci-hotspot-popover-arrow{position:absolute;width:calc(var(--ci-hotspot-arrow-size) * 2);height:calc(var(--ci-hotspot-arrow-size) * 2);background:var(--ci-hotspot-arrow-color);transform:rotate(45deg);pointer-events:none}.ci-hotspot-popover[data-placement=top] .ci-hotspot-popover-arrow{bottom:calc(var(--ci-hotspot-arrow-size) * -1)}.ci-hotspot-popover[data-placement=bottom] .ci-hotspot-popover-arrow{top:calc(var(--ci-hotspot-arrow-size) * -1)}.ci-hotspot-popover[data-placement=left] .ci-hotspot-popover-arrow{right:calc(var(--ci-hotspot-arrow-size) * -1)}.ci-hotspot-popover[data-placement=right] .ci-hotspot-popover-arrow{left:calc(var(--ci-hotspot-arrow-size) * -1)}.ci-hotspot-popover-content{padding:var(--ci-hotspot-popover-padding);max-height:var(--ci-hotspot-popover-max-height);overflow-y:auto;overflow-x:hidden;border-radius:var(--ci-hotspot-popover-border-radius)}.ci-hotspot-popover-image{display:block;width:100%;height:auto;border-radius:calc(var(--ci-hotspot-popover-border-radius) - 4px) calc(var(--ci-hotspot-popover-border-radius) - 4px) 0 0;margin:calc(var(--ci-hotspot-popover-padding) * -1);margin-bottom:12px;width:calc(100% + var(--ci-hotspot-popover-padding) * 2)}.ci-hotspot-popover-body{line-height:1.5}.ci-hotspot-popover-title{margin:0 0 4px;font-size:var(--ci-hotspot-title-font-size);font-weight:var(--ci-hotspot-title-font-weight);color:var(--ci-hotspot-title-color)}.ci-hotspot-popover-price{display:inline-block;margin-bottom:8px;font-size:var(--ci-hotspot-price-font-size);font-weight:var(--ci-hotspot-price-font-weight);color:var(--ci-hotspot-price-color)}.ci-hotspot-popover-description{margin:0 0 12px;color:var(--ci-hotspot-description-color)}.ci-hotspot-popover-cta{display:inline-block;padding:var(--ci-hotspot-cta-padding);background:var(--ci-hotspot-cta-bg);color:var(--ci-hotspot-cta-color);border-radius:var(--ci-hotspot-cta-border-radius);text-decoration:none;font-weight:600;font-size:14px;transition:opacity .2s ease}.ci-hotspot-popover-cta:hover{opacity:.9}.ci-hotspot-popover-cta:focus-visible{outline:3px solid var(--ci-hotspot-focus-ring, #4A90D9);outline-offset:2px}.ci-hotspot-zoom-controls{position:absolute;display:flex;gap:2px;background:var(--ci-hotspot-zoom-controls-bg);border-radius:var(--ci-hotspot-zoom-controls-border-radius);box-shadow:var(--ci-hotspot-zoom-controls-shadow);z-index:10;overflow:hidden}.ci-hotspot-zoom-controls button{display:flex;align-items:center;justify-content:center;width:36px;height:36px;border:none;background:transparent;color:var(--ci-hotspot-zoom-controls-color);cursor:pointer;font-size:18px;line-height:1;padding:0;transition:background .15s ease}.ci-hotspot-zoom-controls button:hover{background:#0000000d}.ci-hotspot-zoom-controls button:disabled{opacity:.3;cursor:default}.ci-hotspot-zoom-controls button:disabled:hover{background:transparent}.ci-hotspot-zoom-controls button svg{width:18px;height:18px}.ci-hotspot-zoom-controls button:focus-visible{outline:3px solid var(--ci-hotspot-focus-ring, #4A90D9);outline-offset:-3px}.ci-hotspot-zoom-controls[data-position=bottom-right]{bottom:16px;right:16px}.ci-hotspot-zoom-controls[data-position=bottom-left]{bottom:16px;left:16px}.ci-hotspot-zoom-controls[data-position=bottom-center]{bottom:16px;left:50%;transform:translate(-50%)}.ci-hotspot-zoom-controls[data-position=top-right]{top:16px;right:16px}.ci-hotspot-zoom-controls[data-position=top-left]{top:16px;left:16px}.ci-hotspot-zoom-controls[data-position=top-center]{top:16px;left:50%;transform:translate(-50%)}.ci-hotspot-cluster{position:absolute;display:flex;align-items:center;justify-content:center;min-width:32px;height:32px;padding:0 8px;border-radius:16px;border:2px solid rgba(255,255,255,.8);background:var(--ci-hotspot-marker-bg);color:var(--ci-hotspot-marker-color);font-size:13px;font-weight:700;cursor:pointer;pointer-events:auto;transform:translate(-50%,-50%) scale(calc(1 / var(--zoom, 1)));box-shadow:var(--ci-hotspot-marker-shadow)}.ci-hotspot-loading .ci-hotspot-image{opacity:0;transition:opacity .3s ease}.ci-hotspot-loading .ci-hotspot-markers{display:none}.ci-hotspot-theme-dark{--ci-hotspot-marker-bg: rgba(255, 255, 255, .8);--ci-hotspot-marker-color: #1a1a1a;--ci-hotspot-marker-border-color: rgba(255, 255, 255, .4);--ci-hotspot-pulse-color: rgba(255, 255, 255, .2);--ci-hotspot-popover-bg: #1a1a1a;--ci-hotspot-popover-color: #f0f0f0;--ci-hotspot-popover-border: 1px solid rgba(255, 255, 255, .1);--ci-hotspot-popover-shadow: 0 8px 32px rgba(0, 0, 0, .4);--ci-hotspot-title-color: #f0f0f0;--ci-hotspot-description-color: #aaaaaa;--ci-hotspot-zoom-controls-bg: rgba(30, 30, 30, .9);--ci-hotspot-zoom-controls-color: #f0f0f0}.ci-hotspot-marker-inverted{--ci-hotspot-marker-bg: rgba(255, 255, 255, .8);--ci-hotspot-marker-color: #1a1a1a;--ci-hotspot-marker-border-color: rgba(0, 0, 0, .3);--ci-hotspot-marker-shadow: 0 2px 8px rgba(0, 0, 0, .15);--ci-hotspot-pulse-color: rgba(0, 0, 0, .15)}.ci-hotspot-theme-dark.ci-hotspot-marker-inverted{--ci-hotspot-marker-bg: rgba(0, 0, 0, .6);--ci-hotspot-marker-color: #ffffff;--ci-hotspot-marker-border-color: rgba(255, 255, 255, .3);--ci-hotspot-pulse-color: rgba(255, 255, 255, .15)}.ci-hotspot-scroll-hint{position:absolute;bottom:16px;left:50%;transform:translate(-50%) translateY(4px);padding:8px 16px;border-radius:20px;background:#000000b3;color:#fff;font-size:13px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;line-height:1;white-space:nowrap;z-index:100;opacity:0;pointer-events:none;transition:opacity .3s ease,transform .3s ease}.ci-hotspot-scroll-hint--visible{opacity:1;transform:translate(-50%) translateY(0)}.ci-hotspot-container--fixed-ratio .ci-hotspot-viewport{overflow:hidden}.ci-hotspot-container--fixed-ratio .ci-hotspot-image{position:absolute;top:0;left:0;width:100%;height:100%;object-fit:contain}.ci-hotspot-container--fixed-ratio .ci-hotspot-markers{z-index:1}.ci-hotspot-scene-incoming{position:absolute;top:0;left:0;width:100%;height:100%;object-fit:contain;pointer-events:none;z-index:0}.ci-hotspot-scene-fade-in{animation:ci-hotspot-scene-fade-in var(--ci-hotspot-scene-transition-duration) ease forwards;z-index:1}.ci-hotspot-scene-fade-out{animation:ci-hotspot-scene-fade-out var(--ci-hotspot-scene-transition-duration) ease forwards}@keyframes ci-hotspot-scene-fade-in{0%{opacity:0}to{opacity:1}}@keyframes ci-hotspot-scene-fade-out{0%{opacity:1}to{opacity:0}}.ci-hotspot-scene-slide-in{animation:ci-hotspot-scene-slide-in var(--ci-hotspot-scene-transition-duration) ease forwards;z-index:1}.ci-hotspot-scene-slide-out{animation:ci-hotspot-scene-slide-out var(--ci-hotspot-scene-transition-duration) ease forwards}@keyframes ci-hotspot-scene-slide-in{0%{transform:translate(100%)}to{transform:translate(0)}}@keyframes ci-hotspot-scene-slide-out{0%{transform:translate(0)}to{transform:translate(-100%)}}.ci-hotspot-scene-slide-in-reverse{animation:ci-hotspot-scene-slide-in-reverse var(--ci-hotspot-scene-transition-duration) ease forwards;z-index:1}.ci-hotspot-scene-slide-out-reverse{animation:ci-hotspot-scene-slide-out-reverse var(--ci-hotspot-scene-transition-duration) ease forwards}@keyframes ci-hotspot-scene-slide-in-reverse{0%{transform:translate(-100%)}to{transform:translate(0)}}@keyframes ci-hotspot-scene-slide-out-reverse{0%{transform:translate(0)}to{transform:translate(100%)}}.ci-hotspot-marker--navigate{--ci-hotspot-marker-bg: rgba(0, 88, 163, .7)}.ci-hotspot-navigate-icon{width:75%;height:75%;display:block}.ci-hotspot-scene-transitioning .ci-hotspot-markers{opacity:0;pointer-events:none;transition:opacity .1s ease}.ci-hotspot-scene-loading:after{content:"";position:absolute;top:50%;left:50%;width:32px;height:32px;margin:-16px 0 0 -16px;border:3px solid rgba(0,0,0,.1);border-top-color:#00000080;border-radius:50%;animation:ci-hotspot-spin .6s linear infinite;z-index:10;pointer-events:none}.ci-hotspot-theme-dark .ci-hotspot-scene-loading:after,.ci-hotspot-scene-loading.ci-hotspot-theme-dark:after{border-color:#ffffff1a;border-top-color:#ffffff80}@keyframes ci-hotspot-spin{to{transform:rotate(360deg)}}.ci-hotspot-fullscreen-btn{position:absolute;top:16px;right:16px;display:flex;align-items:center;justify-content:center;width:36px;height:36px;border:none;border-radius:var(--ci-hotspot-zoom-controls-border-radius);background:var(--ci-hotspot-zoom-controls-bg);color:var(--ci-hotspot-zoom-controls-color);box-shadow:var(--ci-hotspot-zoom-controls-shadow);cursor:pointer;padding:0;z-index:10;transition:background .15s ease}.ci-hotspot-fullscreen-btn:hover{background:#0000000d}.ci-hotspot-fullscreen-btn svg{width:18px;height:18px}.ci-hotspot-fullscreen-btn:focus-visible{outline:3px solid var(--ci-hotspot-focus-ring, #4A90D9);outline-offset:2px}.ci-hotspot-container--fullscreen{background:#000;width:100vw;height:100vh;display:flex;align-items:center;justify-content:center}.ci-hotspot-container--fullscreen .ci-hotspot-viewport{width:fit-content;max-width:100%;max-height:100%}.ci-hotspot-container--fullscreen .ci-hotspot-image{max-height:100vh;width:auto;max-width:100%;object-fit:contain}.ci-hotspot-container--fullscreen.ci-hotspot-container--fixed-ratio .ci-hotspot-viewport{width:100%;height:100%}.ci-hotspot-container--fullscreen.ci-hotspot-container--fixed-ratio .ci-hotspot-image{width:100%;height:100%;max-height:none}.ci-hotspot-theme-dark .ci-hotspot-fullscreen-btn,.ci-hotspot-container--fullscreen .ci-hotspot-fullscreen-btn{background:#1e1e1ee6;color:#f0f0f0}.ci-hotspot-theme-dark .ci-hotspot-fullscreen-btn:hover,.ci-hotspot-container--fullscreen .ci-hotspot-fullscreen-btn:hover{background:#ffffff1a}@media (prefers-reduced-motion: reduce){.ci-hotspot-marker,.ci-hotspot-marker:before,.ci-hotspot-popover{animation:none!important;transition-duration:.01ms!important}.ci-hotspot-viewport,.ci-hotspot-scroll-hint{transition-duration:.01ms!important}.ci-hotspot-scene-fade-in,.ci-hotspot-scene-fade-out,.ci-hotspot-scene-slide-in,.ci-hotspot-scene-slide-out,.ci-hotspot-scene-slide-in-reverse,.ci-hotspot-scene-slide-out-reverse{animation-duration:.01ms!important}.ci-hotspot-scene-transitioning .ci-hotspot-markers{transition-duration:.01ms!important}.ci-hotspot-scene-loading:after{animation-duration:.01ms!important}.ci-hotspot-fullscreen-btn{transition-duration:.01ms!important}}', P = class P {
860
+ constructor(t, e) {
861
+ this.markers = /* @__PURE__ */ new Map(), this.popovers = /* @__PURE__ */ new Map(), this.normalizedHotspots = /* @__PURE__ */ new Map(), this.scrollHint = null, this.zoomPan = null, this.zoomControls = null, this.cloudimageHandler = null, this.resizeObserver = null, this.keyboardHandler = null, this.fullscreenControl = null, this.focusTraps = /* @__PURE__ */ new Map(), this.cleanups = [], this.hotspotCleanups = /* @__PURE__ */ new Map(), this.imageLoaded = !1, this.destroyed = !1, this.scenesMap = /* @__PURE__ */ new Map(), this.isTransitioning = !1, this.activeTimers = /* @__PURE__ */ new Set(), this.sceneHotspotOverrides = /* @__PURE__ */ new Map(), this.preloadedScenes = /* @__PURE__ */ new Set(), this.rootEl = mt(t), this.config = j(e), V(this.config), this.config.scenes && this.config.scenes.length > 0 && this.initScenes(), Q(), gt(te), this.buildDOM(), this.applyTheme(), this.setupImage(), this.initHotspots(), this.config.zoom && this.initZoom(), this.initKeyboard(), this.initFullscreen(), this.setupResponsive();
862
+ }
863
+ /** Auto-initialize all elements with data-ci-hotspot-src or data-ci-hotspot-scenes attribute */
864
+ static autoInit(t) {
865
+ const o = (t || document).querySelectorAll(
866
+ "[data-ci-hotspot-src], [data-ci-hotspot-scenes]"
867
+ ), s = [];
868
+ return o.forEach((c) => {
869
+ const n = ut(c);
870
+ (n.src || n.scenes) && s.push(new P(c, n));
871
+ }), s;
872
+ }
873
+ buildDOM() {
874
+ this.containerEl = b("div", "ci-hotspot-container"), this.viewportEl = b("div", "ci-hotspot-viewport"), this.imgEl = b("img", "ci-hotspot-image", {
875
+ alt: this.config.alt || "",
876
+ draggable: "false"
877
+ }), this.markersEl = b("div", "ci-hotspot-markers"), this.viewportEl.appendChild(this.imgEl), this.viewportEl.appendChild(this.markersEl), this.containerEl.appendChild(this.viewportEl), this.containerEl.setAttribute("role", "group"), this.containerEl.setAttribute("aria-label", this.config.alt || "Image with hotspots"), this.config.sceneAspectRatio && (g(this.containerEl, "ci-hotspot-container--fixed-ratio"), this.viewportEl.style.aspectRatio = this.config.sceneAspectRatio), this.rootEl.innerHTML = "", this.rootEl.appendChild(this.containerEl), this.config.lazyLoad && g(this.containerEl, "ci-hotspot-loading");
878
+ }
879
+ applyTheme() {
880
+ this.config.theme === "dark" && g(this.containerEl, "ci-hotspot-theme-dark"), this.config.invertMarkerTheme && g(this.containerEl, "ci-hotspot-marker-inverted");
881
+ }
882
+ setupImage() {
883
+ const t = () => {
884
+ w(this.containerEl, "ci-hotspot-loading"), this.imageLoaded = !0, this.renormalizePixelCoordinates(), this.syncMarkersToImage(), this.showLoadTriggerPopovers();
885
+ };
886
+ if (this.imgEl.addEventListener("load", t), this.cleanups.push(() => this.imgEl.removeEventListener("load", t)), this.config.lazyLoad && typeof IntersectionObserver < "u") {
887
+ const e = new IntersectionObserver(
888
+ (o) => {
889
+ var s;
890
+ (s = o[0]) != null && s.isIntersecting && (this.loadImage(), e.disconnect());
891
+ },
892
+ { threshold: 0.1 }
893
+ );
894
+ e.observe(this.containerEl), this.cleanups.push(() => e.disconnect());
895
+ } else
896
+ this.loadImage();
897
+ }
898
+ loadImage() {
899
+ var t, e;
900
+ if ((t = this.config.cloudimage) != null && t.token) {
901
+ const o = this.containerEl.offsetWidth || 300, s = window.devicePixelRatio || 1, c = ((e = this.zoomPan) == null ? void 0 : e.getZoom()) || 1;
902
+ this.imgEl.src = S(
903
+ this.config.src,
904
+ this.config.cloudimage,
905
+ o,
906
+ c,
907
+ s
908
+ ), this.cloudimageHandler = Vt(
909
+ this.imgEl,
910
+ this.config.src,
911
+ this.config.cloudimage,
912
+ () => {
913
+ var n;
914
+ return ((n = this.zoomPan) == null ? void 0 : n.getZoom()) || 1;
915
+ }
916
+ ), this.cloudimageHandler.observer.observe(this.containerEl), this.cleanups.push(() => {
917
+ var n;
918
+ return (n = this.cloudimageHandler) == null ? void 0 : n.destroy();
919
+ });
920
+ } else
921
+ this.imgEl.src = this.config.src;
922
+ }
923
+ initHotspots() {
924
+ for (const t of this.config.hotspots)
925
+ this.addHotspotInternal(t);
926
+ }
927
+ addHotspotInternal(t) {
928
+ var d, h;
929
+ if (this.markers.has(t.id)) {
930
+ const p = this.markers.get(t.id);
931
+ L(p), this.markers.delete(t.id), (d = this.popovers.get(t.id)) == null || d.destroy(), this.popovers.delete(t.id);
932
+ const u = this.hotspotCleanups.get(t.id);
933
+ u && (u.forEach((v) => v()), this.hotspotCleanups.delete(t.id));
934
+ const k = this.focusTraps.get(t.id);
935
+ k && (k.destroy(), this.focusTraps.delete(t.id));
936
+ }
937
+ const { x: e, y: o } = G(
938
+ t.x,
939
+ t.y,
940
+ this.imgEl.naturalWidth || 1e3,
941
+ this.imgEl.naturalHeight || 1e3
942
+ ), s = { ...t, x: e, y: o };
943
+ this.normalizedHotspots.set(t.id, s);
944
+ const c = this.config.pulse !== !1, n = vt(s, c);
945
+ this.markers.set(t.id, n), this.markersEl.appendChild(n);
946
+ const r = t.trigger || this.config.trigger || "hover";
947
+ if (t.navigateTo) {
948
+ const p = this.enrichNavigateHotspot(t);
949
+ if (!!(p.data || p.content || this.config.renderPopover)) {
950
+ const k = t.placement || this.config.placement || "top", v = new q(p, {
951
+ placement: k,
952
+ triggerMode: "hover",
953
+ renderFn: this.config.renderPopover,
954
+ onOpen: this.config.onOpen,
955
+ onClose: this.config.onClose
956
+ });
957
+ this.popovers.set(t.id, v), v.mount(this.containerEl, n), this.bindNavigateTrigger(t, n, v);
958
+ } else
959
+ this.bindNavigateTrigger(t, n);
960
+ return;
961
+ }
962
+ const a = t.placement || this.config.placement || "top", l = new q(t, {
963
+ placement: a,
964
+ triggerMode: r,
965
+ renderFn: this.config.renderPopover,
966
+ onOpen: this.config.onOpen,
967
+ onClose: this.config.onClose
968
+ });
969
+ this.popovers.set(t.id, l), l.mount(this.containerEl, n), this.bindTrigger(t, n, l, r), r === "load" && this.imageLoaded && (this.closeAll(), l.show(), f(n, !0), this.ensureFocusTrap(t.id, l.element, n), (h = this.focusTraps.get(t.id)) == null || h.activate());
970
+ }
971
+ /** For navigateTo hotspots without explicit data/content, generate popover content from the destination scene */
972
+ enrichNavigateHotspot(t) {
973
+ if (t.data || t.content) return t;
974
+ const e = this.scenesMap.get(t.navigateTo);
975
+ return e ? {
976
+ ...t,
977
+ data: { title: t.label || e.alt || e.id }
978
+ } : t;
979
+ }
980
+ bindNavigateTrigger(t, e, o) {
981
+ if (g(e, "ci-hotspot-marker--navigate"), e.innerHTML = P.NAVIGATE_ARROW_SVG, t.arrowDirection != null) {
982
+ const r = e.querySelector("svg");
983
+ r && (r.style.transform = `rotate(${t.arrowDirection}deg)`);
984
+ }
985
+ const s = t.label || t.navigateTo;
986
+ if (e.setAttribute("aria-label", `Navigate to ${s}`), e.setAttribute("aria-roledescription", "navigation hotspot"), o) {
987
+ const r = m(e, "mouseenter", () => {
988
+ this.preloadSceneImage(t.navigateTo), o.clearHideTimer(), o.show(), f(e, !0);
989
+ }), a = m(e, "mouseleave", () => {
990
+ o.scheduleHide(200), this.trackedTimeout(() => {
991
+ o.isVisible() || f(e, !1);
992
+ }, 250);
993
+ }), l = m(e, "focus", () => {
994
+ this.preloadSceneImage(t.navigateTo), o.clearHideTimer(), o.show(), f(e, !0);
995
+ }), d = m(e, "blur", () => {
996
+ o.scheduleHide(200), this.trackedTimeout(() => {
997
+ o.isVisible() || f(e, !1);
998
+ }, 250);
999
+ });
1000
+ this.addHotspotCleanups(t.id, r, a, l, d);
1001
+ } else {
1002
+ const r = m(e, "mouseenter", () => {
1003
+ this.preloadSceneImage(t.navigateTo);
1004
+ }), a = m(e, "focus", () => {
1005
+ this.preloadSceneImage(t.navigateTo);
1006
+ });
1007
+ this.addHotspotCleanups(t.id, r, a);
1008
+ }
1009
+ const c = m(e, "click", (r) => {
1010
+ var a, l, d;
1011
+ r.stopPropagation(), o == null || o.hide(), f(e, !1), (l = (a = this.config).onClick) == null || l.call(a, r, t), (d = t.onClick) == null || d.call(t, r, t), this.goToScene(t.navigateTo);
1012
+ }), n = m(e, "keydown", (r) => {
1013
+ var a, l, d;
1014
+ (r.key === "Enter" || r.key === " ") && (r.preventDefault(), o == null || o.hide(), f(e, !1), (l = (a = this.config).onClick) == null || l.call(a, r, t), (d = t.onClick) == null || d.call(t, r, t), this.goToScene(t.navigateTo));
1015
+ });
1016
+ this.addHotspotCleanups(t.id, c, n);
1017
+ }
1018
+ bindTrigger(t, e, o, s) {
1019
+ s === "hover" ? this.bindHoverTrigger(t, e, o) : (s === "click" || s === "load") && this.bindClickTrigger(t, e, o), this.bindKeyboardTrigger(t, e, o, s);
1020
+ }
1021
+ bindHoverTrigger(t, e, o) {
1022
+ const s = m(e, "mouseenter", () => {
1023
+ o.clearHideTimer(), o.show(), f(e, !0);
1024
+ }), c = m(e, "mouseleave", () => {
1025
+ o.scheduleHide(200), this.trackedTimeout(() => {
1026
+ o.isVisible() || f(e, !1);
1027
+ }, 250);
1028
+ });
1029
+ this.addHotspotCleanups(t.id, s, c);
1030
+ }
1031
+ bindClickTrigger(t, e, o) {
1032
+ const s = J(o.element, e);
1033
+ this.focusTraps.set(t.id, s);
1034
+ const c = m(e, "click", (r) => {
1035
+ var a, l, d;
1036
+ r.stopPropagation(), (l = (a = this.config).onClick) == null || l.call(a, r, t), (d = t.onClick) == null || d.call(t, r, t), o.isVisible() ? (o.hide(), f(e, !1), s.deactivate()) : (this.closeAll(), o.show(), f(e, !0), s.activate());
1037
+ }), n = m(document, "click", (r) => {
1038
+ o.isVisible() && !t.keepOpen && !o.element.contains(r.target) && !e.contains(r.target) && (o.hide(), f(e, !1), s.deactivate());
1039
+ });
1040
+ this.addHotspotCleanups(t.id, c, n);
1041
+ }
1042
+ bindKeyboardTrigger(t, e, o, s) {
1043
+ const c = m(e, "focus", () => {
1044
+ s === "hover" && (o.clearHideTimer(), o.show(), f(e, !0));
1045
+ }), n = m(e, "blur", () => {
1046
+ s === "hover" && (o.scheduleHide(200), this.trackedTimeout(() => {
1047
+ o.isVisible() || f(e, !1);
1048
+ }, 250));
1049
+ }), r = m(e, "keydown", (a) => {
1050
+ var l, d, h, p, u;
1051
+ a.key === "Enter" || a.key === " " ? (a.preventDefault(), (d = (l = this.config).onClick) == null || d.call(l, a, t), o.isVisible() ? (o.hide(), f(e, !1), (h = this.focusTraps.get(t.id)) == null || h.deactivate()) : (this.closeAll(), o.show(), f(e, !0), this.ensureFocusTrap(t.id, o.element, e), (p = this.focusTraps.get(t.id)) == null || p.activate())) : a.key === "Escape" && o.isVisible() && (o.hide(), f(e, !1), (u = this.focusTraps.get(t.id)) == null || u.deactivate(), e.focus());
1052
+ });
1053
+ this.addHotspotCleanups(t.id, c, n, r);
1054
+ }
1055
+ renormalizePixelCoordinates() {
1056
+ const t = this.imgEl.naturalWidth, e = this.imgEl.naturalHeight;
1057
+ if (!(!t || !e)) {
1058
+ for (const o of this.config.hotspots)
1059
+ if (typeof o.x == "number" || typeof o.y == "number") {
1060
+ const { x: s, y: c } = G(o.x, o.y, t, e), n = this.markers.get(o.id);
1061
+ n && (n.style.left = `${s}%`, n.style.top = `${c}%`);
1062
+ const r = this.normalizedHotspots.get(o.id);
1063
+ r && (r.x = s, r.y = c);
1064
+ }
1065
+ }
1066
+ }
1067
+ showLoadTriggerPopovers() {
1068
+ var t;
1069
+ for (const [e, o] of this.popovers) {
1070
+ const s = this.normalizedHotspots.get(e);
1071
+ if (((s == null ? void 0 : s.trigger) || this.config.trigger || "hover") === "load" && !o.isVisible()) {
1072
+ o.show();
1073
+ const n = this.markers.get(e);
1074
+ n && (f(n, !0), this.ensureFocusTrap(e, o.element, n), (t = this.focusTraps.get(e)) == null || t.activate());
1075
+ break;
1076
+ }
1077
+ }
1078
+ }
1079
+ initZoom() {
1080
+ this.config.scrollHint !== !1 && (this.scrollHint = new Nt(this.containerEl)), this.zoomPan = new It(this.viewportEl, this.containerEl, {
1081
+ zoomMin: this.config.zoomMin || 1,
1082
+ zoomMax: this.config.zoomMax || 4,
1083
+ onZoom: (t) => {
1084
+ var e, o, s;
1085
+ (o = (e = this.config).onZoom) == null || o.call(e, t), (s = this.zoomControls) == null || s.update();
1086
+ for (const [, c] of this.popovers)
1087
+ c.isVisible() && c.updatePosition();
1088
+ },
1089
+ onScrollWithoutZoom: () => {
1090
+ var t;
1091
+ return (t = this.scrollHint) == null ? void 0 : t.show();
1092
+ }
1093
+ }), this.config.zoomControls !== !1 && (this.zoomControls = Zt(this.containerEl, this.zoomPan, {
1094
+ zoomMin: this.config.zoomMin || 1,
1095
+ zoomMax: this.config.zoomMax || 4,
1096
+ position: this.config.zoomControlsPosition
1097
+ }));
1098
+ }
1099
+ setupResponsive() {
1100
+ if (typeof ResizeObserver > "u") return;
1101
+ let t = 0;
1102
+ this.resizeObserver = new ResizeObserver(() => {
1103
+ t || (t = requestAnimationFrame(() => {
1104
+ if (t = 0, this.destroyed) return;
1105
+ this.syncMarkersToImage();
1106
+ const e = this.containerEl.offsetWidth;
1107
+ for (const [o, s] of this.normalizedHotspots)
1108
+ if (s.responsive) {
1109
+ const c = this.markers.get(o);
1110
+ if (!c) continue;
1111
+ const n = s.responsive.maxWidth && e > s.responsive.maxWidth || s.responsive.minWidth && e < s.responsive.minWidth;
1112
+ kt(c, !!n);
1113
+ }
1114
+ }));
1115
+ }), this.cleanups.push(() => {
1116
+ t && cancelAnimationFrame(t);
1117
+ }), this.resizeObserver.observe(this.containerEl), this.cleanups.push(() => {
1118
+ var e;
1119
+ return (e = this.resizeObserver) == null ? void 0 : e.disconnect();
1120
+ });
1121
+ }
1122
+ /** Position the markers layer to match the rendered image area within a fixed-ratio viewport */
1123
+ syncMarkersToImage() {
1124
+ if (!this.config.sceneAspectRatio) return;
1125
+ const t = this.viewportEl.offsetWidth, e = this.viewportEl.offsetHeight, o = this.imgEl.naturalWidth, s = this.imgEl.naturalHeight;
1126
+ if (!t || !e || !o || !s) return;
1127
+ const c = t / e, n = o / s;
1128
+ let r, a, l, d;
1129
+ n > c ? (r = t, a = t / n, l = 0, d = (e - a) / 2) : (a = e, r = e * n, l = (t - r) / 2, d = 0), this.markersEl.style.left = `${l}px`, this.markersEl.style.top = `${d}px`, this.markersEl.style.width = `${r}px`, this.markersEl.style.height = `${a}px`, this.markersEl.style.right = "auto", this.markersEl.style.bottom = "auto";
1130
+ }
1131
+ initScenes() {
1132
+ for (const o of this.config.scenes)
1133
+ this.scenesMap.set(o.id, o);
1134
+ const t = this.config.initialScene || this.config.scenes[0].id, e = this.scenesMap.get(t);
1135
+ this.config.src = e.src, this.config.alt = e.alt || "", this.config.hotspots = [...e.hotspots], this.currentSceneId = t;
1136
+ }
1137
+ initKeyboard() {
1138
+ this.keyboardHandler = new Bt({
1139
+ container: this.containerEl,
1140
+ getZoomPan: () => this.zoomPan,
1141
+ onEscape: () => {
1142
+ var t;
1143
+ if ((t = this.fullscreenControl) != null && t.isFullscreen()) {
1144
+ this.fullscreenControl.exit();
1145
+ return;
1146
+ }
1147
+ this.closeAll();
1148
+ },
1149
+ onFullscreenToggle: () => {
1150
+ var t;
1151
+ (t = this.fullscreenControl) == null || t.toggle();
1152
+ }
1153
+ });
1154
+ }
1155
+ initFullscreen() {
1156
+ this.config.fullscreenButton !== !1 && (this.fullscreenControl = Qt(this.containerEl, {
1157
+ onChange: (t) => {
1158
+ var e, o;
1159
+ requestAnimationFrame(() => {
1160
+ for (const [, s] of this.popovers)
1161
+ s.isVisible() && s.updatePosition();
1162
+ }), (o = (e = this.config).onFullscreenChange) == null || o.call(e, t);
1163
+ }
1164
+ }));
1165
+ }
1166
+ preloadSceneImage(t) {
1167
+ var s;
1168
+ if (this.preloadedScenes.has(t)) return;
1169
+ const e = this.scenesMap.get(t);
1170
+ if (!e) return;
1171
+ this.preloadedScenes.add(t);
1172
+ const o = new Image();
1173
+ if ((s = this.config.cloudimage) != null && s.token) {
1174
+ const c = this.containerEl.offsetWidth || 300, n = typeof window < "u" && window.devicePixelRatio || 1;
1175
+ o.src = S(e.src, this.config.cloudimage, c, 1, n);
1176
+ } else
1177
+ o.src = e.src;
1178
+ }
1179
+ ensureFocusTrap(t, e, o) {
1180
+ this.focusTraps.has(t) || this.focusTraps.set(t, J(e, o));
1181
+ }
1182
+ /** Create a setTimeout that is automatically cleared on destroy */
1183
+ trackedTimeout(t, e) {
1184
+ const o = setTimeout(() => {
1185
+ this.activeTimers.delete(o), t();
1186
+ }, e);
1187
+ this.activeTimers.add(o);
1188
+ }
1189
+ /** Sync current hotspots back to scene override map so navigating away and back preserves changes */
1190
+ syncCurrentSceneHotspots() {
1191
+ !this.currentSceneId || this.isTransitioning || this.sceneHotspotOverrides.set(this.currentSceneId, [...this.config.hotspots]);
1192
+ }
1193
+ addHotspotCleanups(t, ...e) {
1194
+ let o = this.hotspotCleanups.get(t);
1195
+ o || (o = [], this.hotspotCleanups.set(t, o)), o.push(...e);
1196
+ }
1197
+ clearHotspots() {
1198
+ for (const t of this.hotspotCleanups.values())
1199
+ t.forEach((e) => e());
1200
+ this.hotspotCleanups.clear();
1201
+ for (const [, t] of this.popovers)
1202
+ t.destroy();
1203
+ this.popovers.clear();
1204
+ for (const [, t] of this.markers)
1205
+ L(t);
1206
+ this.markers.clear(), this.normalizedHotspots.clear();
1207
+ for (const [, t] of this.focusTraps)
1208
+ t.destroy();
1209
+ this.focusTraps.clear();
1210
+ }
1211
+ /** Read scene transition duration from CSS variable (handles both ms and s units) */
1212
+ getSceneTransitionDuration() {
1213
+ if (typeof getComputedStyle > "u") return 400;
1214
+ const t = getComputedStyle(this.containerEl).getPropertyValue("--ci-hotspot-scene-transition-duration").trim(), e = parseFloat(t);
1215
+ return isNaN(e) ? 400 : t.endsWith("s") && !t.endsWith("ms") ? e * 1e3 : e;
1216
+ }
1217
+ performSceneTransition(t, e, o, s) {
1218
+ var a;
1219
+ if (e === "none") {
1220
+ this.clearHotspots(), this.switchToScene(t), s();
1221
+ return;
1222
+ }
1223
+ const c = this.getSceneTransitionDuration();
1224
+ g(this.containerEl, "ci-hotspot-scene-transitioning");
1225
+ const n = b("img", "ci-hotspot-scene-incoming", {
1226
+ alt: t.alt || "",
1227
+ draggable: "false"
1228
+ });
1229
+ if ((a = this.config.cloudimage) != null && a.token) {
1230
+ const l = this.containerEl.offsetWidth || 300, d = typeof window < "u" && window.devicePixelRatio || 1;
1231
+ n.src = S(t.src, this.config.cloudimage, l, 1, d);
1232
+ } else
1233
+ n.src = t.src;
1234
+ const r = () => {
1235
+ if (!this.destroyed) {
1236
+ if (e === "fade")
1237
+ g(n, "ci-hotspot-scene-fade-in"), g(this.imgEl, "ci-hotspot-scene-fade-out");
1238
+ else if (e === "slide") {
1239
+ const l = o ? "-reverse" : "";
1240
+ g(n, `ci-hotspot-scene-slide-in${l}`), g(this.imgEl, `ci-hotspot-scene-slide-out${l}`);
1241
+ }
1242
+ this.viewportEl.insertBefore(n, this.markersEl), this.transitionTimer = setTimeout(() => {
1243
+ if (this.transitionTimer = void 0, this.destroyed) return;
1244
+ this.clearHotspots(), this.switchToScene(t);
1245
+ const l = () => {
1246
+ n.remove(), w(this.imgEl, "ci-hotspot-scene-fade-out"), w(this.imgEl, "ci-hotspot-scene-slide-out"), w(this.imgEl, "ci-hotspot-scene-slide-out-reverse"), w(this.containerEl, "ci-hotspot-scene-transitioning"), s();
1247
+ };
1248
+ this.imgEl.complete && this.imgEl.naturalWidth > 0 ? l() : (this.imgEl.addEventListener("load", l, { once: !0 }), this.imgEl.addEventListener("error", l, { once: !0 }));
1249
+ }, c);
1250
+ }
1251
+ };
1252
+ n.complete ? r() : (g(this.containerEl, "ci-hotspot-scene-loading"), n.onload = () => {
1253
+ w(this.containerEl, "ci-hotspot-scene-loading"), r();
1254
+ }, n.onerror = () => {
1255
+ this.destroyed || (w(this.containerEl, "ci-hotspot-scene-loading"), n.remove(), w(this.containerEl, "ci-hotspot-scene-transitioning"), this.clearHotspots(), this.switchToScene(t), s());
1256
+ });
1257
+ }
1258
+ switchToScene(t) {
1259
+ var o;
1260
+ this.config.src = t.src, this.config.alt = t.alt || "", this.config.hotspots = this.sceneHotspotOverrides.get(t.id) ?? [...t.hotspots], this.imgEl.alt = t.alt || "", this.containerEl.setAttribute("aria-label", t.alt || "Image with hotspots"), this.imageLoaded = !1;
1261
+ const e = () => {
1262
+ this.imageLoaded = !0, this.renormalizePixelCoordinates(), this.syncMarkersToImage(), this.showLoadTriggerPopovers();
1263
+ };
1264
+ if (this.imgEl.addEventListener("load", e, { once: !0 }), (o = this.config.cloudimage) != null && o.token) {
1265
+ const s = this.containerEl.offsetWidth || 300, c = typeof window < "u" && window.devicePixelRatio || 1;
1266
+ this.imgEl.src = S(t.src, this.config.cloudimage, s, 1, c);
1267
+ } else
1268
+ this.imgEl.src = t.src;
1269
+ this.imgEl.complete && this.imgEl.naturalWidth > 0 && !this.imageLoaded && (this.imgEl.removeEventListener("load", e), e()), this.initHotspots();
1270
+ }
1271
+ // === Public API ===
1272
+ /** Get references to the internal DOM elements */
1273
+ getElements() {
1274
+ return {
1275
+ container: this.containerEl,
1276
+ viewport: this.viewportEl,
1277
+ image: this.imgEl,
1278
+ markers: this.markersEl
1279
+ };
1280
+ }
1281
+ open(t) {
1282
+ var s;
1283
+ if (this.destroyed) return;
1284
+ const e = this.popovers.get(t), o = this.markers.get(t);
1285
+ !e || !o || e.isVisible() || (this.closeAll(), e.show(), f(o, !0), (s = this.focusTraps.get(t)) == null || s.activate());
1286
+ }
1287
+ close(t) {
1288
+ var s;
1289
+ if (this.destroyed) return;
1290
+ const e = this.popovers.get(t), o = this.markers.get(t);
1291
+ e && o && (e.hide(), f(o, !1), (s = this.focusTraps.get(t)) == null || s.deactivate());
1292
+ }
1293
+ closeAll() {
1294
+ var t;
1295
+ if (!this.destroyed) {
1296
+ for (const [e, o] of this.popovers)
1297
+ if (o.isVisible()) {
1298
+ o.hide();
1299
+ const s = this.markers.get(e);
1300
+ s && f(s, !1), (t = this.focusTraps.get(e)) == null || t.deactivate();
1301
+ }
1302
+ }
1303
+ }
1304
+ setZoom(t) {
1305
+ var e;
1306
+ this.destroyed || (e = this.zoomPan) == null || e.setZoom(t);
1307
+ }
1308
+ getZoom() {
1309
+ var t;
1310
+ return ((t = this.zoomPan) == null ? void 0 : t.getZoom()) || 1;
1311
+ }
1312
+ resetZoom() {
1313
+ var t;
1314
+ this.destroyed || (t = this.zoomPan) == null || t.resetZoom();
1315
+ }
1316
+ goToScene(t) {
1317
+ if (this.destroyed || this.isTransitioning || !this.scenesMap.size || t === this.currentSceneId) return;
1318
+ const e = this.scenesMap.get(t);
1319
+ if (!e) return;
1320
+ const o = this.config.sceneTransition || "fade";
1321
+ this.isTransitioning = !0, this.zoomPan && this.zoomPan.getZoom() > 1 && this.zoomPan.resetZoom();
1322
+ let s = !1;
1323
+ if (o === "slide") {
1324
+ for (const c of this.config.hotspots)
1325
+ if (c.navigateTo === t) {
1326
+ const n = this.normalizedHotspots.get(c.id);
1327
+ n && n.x <= 50 && (s = !0);
1328
+ break;
1329
+ }
1330
+ }
1331
+ this.syncCurrentSceneHotspots(), this.currentSceneId = t, this.performSceneTransition(e, o, s, () => {
1332
+ var n, r;
1333
+ this.isTransitioning = !1, Gt(`Navigated to ${e.alt || t}`), (r = (n = this.config).onSceneChange) == null || r.call(n, t, e);
1334
+ const c = e.hotspots[0];
1335
+ if (c) {
1336
+ const a = this.markers.get(c.id);
1337
+ a && document.activeElement && this.containerEl.contains(document.activeElement) && a.focus();
1338
+ }
1339
+ });
1340
+ }
1341
+ getCurrentScene() {
1342
+ return this.currentSceneId;
1343
+ }
1344
+ getScenes() {
1345
+ return Array.from(this.scenesMap.keys());
1346
+ }
1347
+ enterFullscreen() {
1348
+ var t;
1349
+ this.destroyed || (t = this.fullscreenControl) == null || t.enter();
1350
+ }
1351
+ exitFullscreen() {
1352
+ var t;
1353
+ this.destroyed || (t = this.fullscreenControl) == null || t.exit();
1354
+ }
1355
+ isFullscreen() {
1356
+ var t;
1357
+ return ((t = this.fullscreenControl) == null ? void 0 : t.isFullscreen()) ?? !1;
1358
+ }
1359
+ addHotspot(t) {
1360
+ this.destroyed || (this.config.hotspots.push(t), this.addHotspotInternal(t), this.syncCurrentSceneHotspots());
1361
+ }
1362
+ removeHotspot(t) {
1363
+ if (this.destroyed) return;
1364
+ const e = this.hotspotCleanups.get(t);
1365
+ e && (e.forEach((n) => n()), this.hotspotCleanups.delete(t));
1366
+ const o = this.focusTraps.get(t);
1367
+ o && (o.destroy(), this.focusTraps.delete(t));
1368
+ const s = this.markers.get(t), c = this.popovers.get(t);
1369
+ c && (c.destroy(), this.popovers.delete(t)), s && (L(s), this.markers.delete(t)), this.normalizedHotspots.delete(t), this.config.hotspots = this.config.hotspots.filter((n) => n.id !== t), this.syncCurrentSceneHotspots();
1370
+ }
1371
+ updateHotspot(t, e) {
1372
+ if (this.destroyed) return;
1373
+ const o = this.config.hotspots.findIndex((l) => l.id === t);
1374
+ if (o === -1) return;
1375
+ const s = this.markers.get(t), c = (s == null ? void 0 : s.nextElementSibling) || null, r = { ...this.config.hotspots[o], ...e };
1376
+ this.removeHotspot(t), this.config.hotspots.splice(o, 0, r), this.addHotspotInternal(r);
1377
+ const a = this.markers.get(t);
1378
+ a && c && this.markersEl.contains(c) && this.markersEl.insertBefore(a, c), this.syncCurrentSceneHotspots();
1379
+ }
1380
+ update(t) {
1381
+ this.destroyed || (this.destroyInternal(), Q(), this.config = j({ ...this.config, ...t }), V(this.config), this.config.scenes && this.config.scenes.length > 0 && this.initScenes(), this.buildDOM(), this.applyTheme(), this.setupImage(), this.initHotspots(), this.config.zoom && this.initZoom(), this.initKeyboard(), this.initFullscreen(), this.setupResponsive());
1382
+ }
1383
+ destroy() {
1384
+ this.destroyed || (this.destroyed = !0, this.destroyInternal(), this.rootEl.innerHTML = "");
1385
+ }
1386
+ destroyInternal() {
1387
+ var t, e, o, s, c, n, r;
1388
+ this.imageLoaded = !1;
1389
+ for (const a of this.activeTimers) clearTimeout(a);
1390
+ this.activeTimers.clear();
1391
+ for (const a of this.hotspotCleanups.values())
1392
+ a.forEach((l) => l());
1393
+ this.hotspotCleanups.clear(), this.cleanups.forEach((a) => a()), this.cleanups = [];
1394
+ for (const [, a] of this.popovers)
1395
+ a.destroy();
1396
+ this.popovers.clear();
1397
+ for (const [, a] of this.markers)
1398
+ L(a);
1399
+ this.markers.clear(), this.normalizedHotspots.clear();
1400
+ for (const [, a] of this.focusTraps)
1401
+ a.destroy();
1402
+ this.focusTraps.clear(), this.scenesMap.clear(), this.preloadedScenes.clear(), this.sceneHotspotOverrides.clear(), this.currentSceneId = void 0, this.isTransitioning = !1, this.transitionTimer !== void 0 && (clearTimeout(this.transitionTimer), this.transitionTimer = void 0), (t = this.fullscreenControl) == null || t.destroy(), this.fullscreenControl = null, (e = this.keyboardHandler) == null || e.destroy(), this.keyboardHandler = null, (o = this.zoomPan) == null || o.destroy(), this.zoomPan = null, (s = this.zoomControls) == null || s.destroy(), this.zoomControls = null, (c = this.scrollHint) == null || c.destroy(), this.scrollHint = null, (n = this.cloudimageHandler) == null || n.destroy(), this.cloudimageHandler = null, (r = this.resizeObserver) == null || r.disconnect(), this.resizeObserver = null, qt();
1403
+ }
1404
+ };
1405
+ P.NAVIGATE_ARROW_SVG = '<svg class="ci-hotspot-navigate-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>';
1406
+ let X = P;
1407
+ function O(i) {
1408
+ const t = C({ value: i, key: JSON.stringify(i) });
1409
+ if (i !== t.current.value) {
1410
+ const e = JSON.stringify(i);
1411
+ e !== t.current.key ? t.current = { value: i, key: e } : t.current.value = i;
1412
+ }
1413
+ return t.current.key;
1414
+ }
1415
+ function ee(i) {
1416
+ const t = C(null), e = C(null), o = C(!1), s = C(i);
1417
+ s.current = i, Y(() => {
1418
+ const a = t.current;
1419
+ if (!a) return;
1420
+ const l = {
1421
+ src: s.current.src || "",
1422
+ alt: s.current.alt,
1423
+ hotspots: s.current.hotspots || [],
1424
+ trigger: s.current.trigger,
1425
+ zoom: s.current.zoom,
1426
+ zoomMax: s.current.zoomMax,
1427
+ zoomMin: s.current.zoomMin,
1428
+ theme: s.current.theme,
1429
+ pulse: s.current.pulse,
1430
+ placement: s.current.placement,
1431
+ zoomControls: s.current.zoomControls,
1432
+ zoomControlsPosition: s.current.zoomControlsPosition,
1433
+ lazyLoad: s.current.lazyLoad,
1434
+ scrollHint: s.current.scrollHint,
1435
+ cloudimage: s.current.cloudimage,
1436
+ onOpen: s.current.onOpen,
1437
+ onClose: s.current.onClose,
1438
+ onZoom: s.current.onZoom,
1439
+ onClick: s.current.onClick,
1440
+ scenes: s.current.scenes,
1441
+ initialScene: s.current.initialScene,
1442
+ sceneTransition: s.current.sceneTransition,
1443
+ sceneAspectRatio: s.current.sceneAspectRatio,
1444
+ onSceneChange: s.current.onSceneChange,
1445
+ fullscreenButton: s.current.fullscreenButton,
1446
+ onFullscreenChange: s.current.onFullscreenChange
1447
+ };
1448
+ s.current.renderPopover && (l.renderPopover = (h) => {
1449
+ const p = document.createElement("div");
1450
+ return p.dataset.reactPortal = h.id, p;
1451
+ });
1452
+ const d = new X(a, l);
1453
+ return e.current = d, () => {
1454
+ d.destroy(), e.current = null;
1455
+ };
1456
+ }, []);
1457
+ const c = O(i.scenes), n = O(i.hotspots), r = O(i.cloudimage);
1458
+ return Y(() => {
1459
+ if (!o.current) {
1460
+ o.current = !0;
1461
+ return;
1462
+ }
1463
+ e.current && e.current.update({
1464
+ src: i.src || "",
1465
+ alt: i.alt,
1466
+ hotspots: i.hotspots || [],
1467
+ trigger: i.trigger,
1468
+ zoom: i.zoom,
1469
+ zoomMax: i.zoomMax,
1470
+ zoomMin: i.zoomMin,
1471
+ theme: i.theme,
1472
+ pulse: i.pulse,
1473
+ placement: i.placement,
1474
+ zoomControls: i.zoomControls,
1475
+ zoomControlsPosition: i.zoomControlsPosition,
1476
+ lazyLoad: i.lazyLoad,
1477
+ scrollHint: i.scrollHint,
1478
+ cloudimage: i.cloudimage,
1479
+ onOpen: i.onOpen,
1480
+ onClose: i.onClose,
1481
+ onZoom: i.onZoom,
1482
+ onClick: i.onClick,
1483
+ scenes: i.scenes,
1484
+ initialScene: i.initialScene,
1485
+ sceneTransition: i.sceneTransition,
1486
+ sceneAspectRatio: i.sceneAspectRatio,
1487
+ onSceneChange: i.onSceneChange,
1488
+ fullscreenButton: i.fullscreenButton,
1489
+ onFullscreenChange: i.onFullscreenChange
1490
+ });
1491
+ }, [
1492
+ i.src,
1493
+ i.alt,
1494
+ n,
1495
+ i.trigger,
1496
+ i.zoom,
1497
+ i.zoomMax,
1498
+ i.zoomMin,
1499
+ i.theme,
1500
+ i.pulse,
1501
+ i.placement,
1502
+ i.zoomControls,
1503
+ i.zoomControlsPosition,
1504
+ i.lazyLoad,
1505
+ i.scrollHint,
1506
+ r,
1507
+ c,
1508
+ i.initialScene,
1509
+ i.sceneTransition,
1510
+ i.sceneAspectRatio,
1511
+ i.fullscreenButton
1512
+ // Note: Callback props (onOpen, onClose, onZoom, onClick, onSceneChange, onFullscreenChange) are
1513
+ // intentionally excluded from this dependency array. They are read from optionsRef
1514
+ // at call time, so they always reflect the latest value without triggering re-init.
1515
+ ]), { containerRef: t, instance: e };
1516
+ }
1517
+ const ne = ct(
1518
+ function(t, e) {
1519
+ const { className: o, style: s, renderPopover: c, ...n } = t, { containerRef: r, instance: a } = ee(n), [l, d] = at.useState(/* @__PURE__ */ new Map());
1520
+ return lt(e, () => ({
1521
+ open: (h) => {
1522
+ var p;
1523
+ return (p = a.current) == null ? void 0 : p.open(h);
1524
+ },
1525
+ close: (h) => {
1526
+ var p;
1527
+ return (p = a.current) == null ? void 0 : p.close(h);
1528
+ },
1529
+ closeAll: () => {
1530
+ var h;
1531
+ return (h = a.current) == null ? void 0 : h.closeAll();
1532
+ },
1533
+ setZoom: (h) => {
1534
+ var p;
1535
+ return (p = a.current) == null ? void 0 : p.setZoom(h);
1536
+ },
1537
+ getZoom: () => {
1538
+ var h;
1539
+ return ((h = a.current) == null ? void 0 : h.getZoom()) ?? 1;
1540
+ },
1541
+ resetZoom: () => {
1542
+ var h;
1543
+ return (h = a.current) == null ? void 0 : h.resetZoom();
1544
+ },
1545
+ addHotspot: (h) => {
1546
+ var p;
1547
+ return (p = a.current) == null ? void 0 : p.addHotspot(h);
1548
+ },
1549
+ removeHotspot: (h) => {
1550
+ var p;
1551
+ return (p = a.current) == null ? void 0 : p.removeHotspot(h);
1552
+ },
1553
+ updateHotspot: (h, p) => {
1554
+ var u;
1555
+ return (u = a.current) == null ? void 0 : u.updateHotspot(h, p);
1556
+ },
1557
+ goToScene: (h) => {
1558
+ var p;
1559
+ return (p = a.current) == null ? void 0 : p.goToScene(h);
1560
+ },
1561
+ getCurrentScene: () => {
1562
+ var h;
1563
+ return (h = a.current) == null ? void 0 : h.getCurrentScene();
1564
+ },
1565
+ getScenes: () => {
1566
+ var h;
1567
+ return ((h = a.current) == null ? void 0 : h.getScenes()) ?? [];
1568
+ },
1569
+ enterFullscreen: () => {
1570
+ var h;
1571
+ return (h = a.current) == null ? void 0 : h.enterFullscreen();
1572
+ },
1573
+ exitFullscreen: () => {
1574
+ var h;
1575
+ return (h = a.current) == null ? void 0 : h.exitFullscreen();
1576
+ },
1577
+ isFullscreen: () => {
1578
+ var h;
1579
+ return ((h = a.current) == null ? void 0 : h.isFullscreen()) ?? !1;
1580
+ }
1581
+ })), Y(() => {
1582
+ if (!r.current || !c) return;
1583
+ const h = new MutationObserver(() => {
1584
+ var k;
1585
+ const p = (k = r.current) == null ? void 0 : k.querySelectorAll("[data-react-portal]");
1586
+ if (!p) return;
1587
+ const u = /* @__PURE__ */ new Map();
1588
+ p.forEach((v) => {
1589
+ const y = v.dataset.reactPortal;
1590
+ y && u.set(y, v);
1591
+ }), d((v) => {
1592
+ if (v.size !== u.size) return u;
1593
+ for (const [y, H] of u)
1594
+ if (v.get(y) !== H) return u;
1595
+ return v;
1596
+ });
1597
+ });
1598
+ return h.observe(r.current, { childList: !0, subtree: !0 }), () => h.disconnect();
1599
+ }, [c]), /* @__PURE__ */ rt($, { children: [
1600
+ /* @__PURE__ */ W("div", { ref: r, className: o, style: s }),
1601
+ c && Array.from(l.entries()).map(([h, p]) => {
1602
+ var k;
1603
+ const u = (k = t.hotspots) == null ? void 0 : k.find((v) => v.id === h);
1604
+ return u ? ht(
1605
+ /* @__PURE__ */ W($, { children: c(u) }),
1606
+ p,
1607
+ h
1608
+ ) : null;
1609
+ })
1610
+ ] });
1611
+ }
1612
+ );
1613
+ export {
1614
+ ne as CIHotspotViewer,
1615
+ ee as useCIHotspot
1616
+ };
1617
+ //# sourceMappingURL=index.js.map