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.
- package/LICENSE +21 -0
- package/README.md +294 -0
- package/dist/a11y/aria.d.ts +7 -0
- package/dist/a11y/aria.d.ts.map +1 -0
- package/dist/a11y/focus.d.ts +9 -0
- package/dist/a11y/focus.d.ts.map +1 -0
- package/dist/a11y/keyboard.d.ts +14 -0
- package/dist/a11y/keyboard.d.ts.map +1 -0
- package/dist/core/CIHotspot.d.ts +48 -0
- package/dist/core/CIHotspot.d.ts.map +1 -0
- package/dist/core/ci-hotspot.d.ts +95 -0
- package/dist/core/ci-hotspot.d.ts.map +1 -0
- package/dist/core/config.d.ts +15 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/types.d.ts +227 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/editor/ci-hotspot-editor.d.ts +58 -0
- package/dist/editor/ci-hotspot-editor.d.ts.map +1 -0
- package/dist/editor/drag-manager.d.ts +18 -0
- package/dist/editor/drag-manager.d.ts.map +1 -0
- package/dist/editor/editor-toolbar.d.ts +26 -0
- package/dist/editor/editor-toolbar.d.ts.map +1 -0
- package/dist/editor/index.d.ts +3 -0
- package/dist/editor/index.d.ts.map +1 -0
- package/dist/editor/js-cloudimage-hotspot-editor.cjs.js +4 -0
- package/dist/editor/js-cloudimage-hotspot-editor.cjs.js.map +1 -0
- package/dist/editor/js-cloudimage-hotspot-editor.esm.js +1981 -0
- package/dist/editor/js-cloudimage-hotspot-editor.esm.js.map +1 -0
- package/dist/editor/js-cloudimage-hotspot-editor.min.js +4 -0
- package/dist/editor/js-cloudimage-hotspot-editor.min.js.map +1 -0
- package/dist/editor/property-panel.d.ts +17 -0
- package/dist/editor/property-panel.d.ts.map +1 -0
- package/dist/editor/selection-manager.d.ts +15 -0
- package/dist/editor/selection-manager.d.ts.map +1 -0
- package/dist/editor/types.d.ts +32 -0
- package/dist/editor/types.d.ts.map +1 -0
- package/dist/editor/undo-manager.d.ts +16 -0
- package/dist/editor/undo-manager.d.ts.map +1 -0
- package/dist/fullscreen/fullscreen.d.ts +14 -0
- package/dist/fullscreen/fullscreen.d.ts.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/js-cloudimage-hotspot.cjs.js +2 -0
- package/dist/js-cloudimage-hotspot.cjs.js.map +1 -0
- package/dist/js-cloudimage-hotspot.esm.js +1408 -0
- package/dist/js-cloudimage-hotspot.esm.js.map +1 -0
- package/dist/js-cloudimage-hotspot.min.js +2 -0
- package/dist/js-cloudimage-hotspot.min.js.map +1 -0
- package/dist/markers/Marker.d.ts +10 -0
- package/dist/markers/Marker.d.ts.map +1 -0
- package/dist/markers/pulse.d.ts +9 -0
- package/dist/markers/pulse.d.ts.map +1 -0
- package/dist/popover/Popover.d.ts +41 -0
- package/dist/popover/Popover.d.ts.map +1 -0
- package/dist/popover/position.d.ts +6 -0
- package/dist/popover/position.d.ts.map +1 -0
- package/dist/popover/sanitize.d.ts +6 -0
- package/dist/popover/sanitize.d.ts.map +1 -0
- package/dist/popover/template.d.ts +9 -0
- package/dist/popover/template.d.ts.map +1 -0
- package/dist/react/index.cjs +2 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.js +1617 -0
- package/dist/react/index.js.map +1 -0
- package/dist/utils/cloudimage.d.ts +16 -0
- package/dist/utils/cloudimage.d.ts.map +1 -0
- package/dist/utils/coordinates.d.ts +17 -0
- package/dist/utils/coordinates.d.ts.map +1 -0
- package/dist/utils/dom.d.ts +13 -0
- package/dist/utils/dom.d.ts.map +1 -0
- package/dist/utils/events.d.ts +15 -0
- package/dist/utils/events.d.ts.map +1 -0
- package/dist/zoom/ScrollHint.d.ts +8 -0
- package/dist/zoom/ScrollHint.d.ts.map +1 -0
- package/dist/zoom/ZoomPan.d.ts +51 -0
- package/dist/zoom/ZoomPan.d.ts.map +1 -0
- package/dist/zoom/controls.d.ts +14 -0
- package/dist/zoom/controls.d.ts.map +1 -0
- package/dist/zoom/gestures.d.ts +28 -0
- package/dist/zoom/gestures.d.ts.map +1 -0
- package/dist/zoom/scroll-hint.d.ts +8 -0
- package/dist/zoom/scroll-hint.d.ts.map +1 -0
- package/dist/zoom/zoom-pan.d.ts +53 -0
- package/dist/zoom/zoom-pan.d.ts.map +1 -0
- package/package.json +97 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"js-cloudimage-hotspot.cjs.js","sources":["../src/core/config.ts","../src/utils/dom.ts","../src/utils/coordinates.ts","../src/utils/events.ts","../src/markers/marker.ts","../src/popover/position.ts","../src/popover/sanitize.ts","../src/popover/template.ts","../src/popover/popover.ts","../src/zoom/gestures.ts","../src/zoom/zoom-pan.ts","../src/zoom/controls.ts","../src/zoom/scroll-hint.ts","../src/utils/cloudimage.ts","../src/a11y/keyboard.ts","../src/a11y/focus.ts","../src/a11y/aria.ts","../src/fullscreen/fullscreen.ts","../src/core/ci-hotspot.ts"],"sourcesContent":["import type { CIHotspotConfig, CloudimageConfig, ResolvedCIHotspotConfig } from './types';\n\nexport const DEFAULT_CONFIG: Omit<Required<CIHotspotConfig>, 'src' | 'hotspots' | 'cloudimage' | 'renderPopover' | 'onOpen' | 'onClose' | 'onZoom' | 'onClick' | 'scenes' | 'initialScene' | 'onSceneChange' | 'sceneAspectRatio' | 'onFullscreenChange'> = {\n alt: '',\n trigger: 'hover',\n zoom: false,\n zoomMax: 4,\n zoomMin: 1,\n theme: 'light',\n pulse: true,\n zoomControls: true,\n placement: 'top',\n lazyLoad: true,\n sceneTransition: 'fade',\n scrollHint: true,\n invertMarkerTheme: false,\n fullscreenButton: true,\n zoomControlsPosition: 'bottom-right',\n};\n\n/** Data attribute to config property mapping */\nexport const DATA_ATTR_MAP: Record<string, { key: string; type: 'string' | 'boolean' | 'number' | 'json'; nested?: string }> = {\n 'data-ci-hotspot-src': { key: 'src', type: 'string' },\n 'data-ci-hotspot-alt': { key: 'alt', type: 'string' },\n 'data-ci-hotspot-items': { key: 'hotspots', type: 'json' },\n 'data-ci-hotspot-trigger': { key: 'trigger', type: 'string' },\n 'data-ci-hotspot-zoom': { key: 'zoom', type: 'boolean' },\n 'data-ci-hotspot-zoom-max': { key: 'zoomMax', type: 'number' },\n 'data-ci-hotspot-zoom-min': { key: 'zoomMin', type: 'number' },\n 'data-ci-hotspot-theme': { key: 'theme', type: 'string' },\n 'data-ci-hotspot-pulse': { key: 'pulse', type: 'boolean' },\n 'data-ci-hotspot-placement': { key: 'placement', type: 'string' },\n 'data-ci-hotspot-lazy-load': { key: 'lazyLoad', type: 'boolean' },\n 'data-ci-hotspot-zoom-controls': { key: 'zoomControls', type: 'boolean' },\n 'data-ci-hotspot-scroll-hint': { key: 'scrollHint', type: 'boolean' },\n 'data-ci-hotspot-ci-token': { key: 'token', type: 'string', nested: 'cloudimage' },\n 'data-ci-hotspot-ci-api-version': { key: 'apiVersion', type: 'string', nested: 'cloudimage' },\n 'data-ci-hotspot-ci-domain': { key: 'domain', type: 'string', nested: 'cloudimage' },\n 'data-ci-hotspot-ci-limit-factor': { key: 'limitFactor', type: 'number', nested: 'cloudimage' },\n 'data-ci-hotspot-ci-params': { key: 'params', type: 'string', nested: 'cloudimage' },\n 'data-ci-hotspot-scenes': { key: 'scenes', type: 'json' },\n 'data-ci-hotspot-initial-scene': { key: 'initialScene', type: 'string' },\n 'data-ci-hotspot-scene-transition': { key: 'sceneTransition', type: 'string' },\n 'data-ci-hotspot-scene-aspect-ratio': { key: 'sceneAspectRatio', type: 'string' },\n 'data-ci-hotspot-invert-marker-theme': { key: 'invertMarkerTheme', type: 'boolean' },\n 'data-ci-hotspot-fullscreen-button': { key: 'fullscreenButton', type: 'boolean' },\n 'data-ci-hotspot-zoom-controls-position': { key: 'zoomControlsPosition', type: 'string' },\n};\n\n/** Parse data attributes from an element into a config object */\nexport function parseDataAttributes(element: HTMLElement): Partial<CIHotspotConfig> {\n const config: Record<string, unknown> = {};\n const cloudimage: Record<string, unknown> = {};\n\n for (const [attr, mapping] of Object.entries(DATA_ATTR_MAP)) {\n const value = element.getAttribute(attr);\n if (value === null) continue;\n\n const parsed = coerceValue(value, mapping.type);\n\n if (mapping.nested === 'cloudimage') {\n cloudimage[mapping.key] = parsed;\n } else {\n config[mapping.key] = parsed;\n }\n }\n\n if (Object.keys(cloudimage).length > 0) {\n config.cloudimage = cloudimage as unknown as CloudimageConfig;\n }\n\n return config as Partial<CIHotspotConfig>;\n}\n\nfunction coerceValue(value: string, type: string): unknown {\n switch (type) {\n case 'boolean':\n return value === 'true';\n case 'number': {\n const num = parseFloat(value);\n if (isNaN(num)) {\n console.warn(`CIHotspot: invalid number value \"${value}\"`);\n return undefined;\n }\n return num;\n }\n case 'json':\n try {\n return JSON.parse(value);\n } catch {\n console.warn(`CIHotspot: failed to parse JSON value \"${value}\"`);\n return undefined;\n }\n default:\n return value;\n }\n}\n\n/** Merge user config with defaults */\nexport function mergeConfig(userConfig: Partial<CIHotspotConfig>): ResolvedCIHotspotConfig {\n return {\n ...DEFAULT_CONFIG,\n ...userConfig,\n src: userConfig.src || '',\n hotspots: userConfig.hotspots || [],\n } as ResolvedCIHotspotConfig;\n}\n\n/** Validate config — throws on critical issues */\nexport function validateConfig(config: ResolvedCIHotspotConfig): void {\n if (config.scenes && config.scenes.length > 0) {\n const sceneIds = new Set<string>();\n for (const scene of config.scenes) {\n if (!scene.id) throw new Error('CIHotspot: each scene must have an \"id\"');\n if (sceneIds.has(scene.id)) {\n throw new Error(`CIHotspot: duplicate scene ID \"${scene.id}\"`);\n }\n sceneIds.add(scene.id);\n if (!scene.src) throw new Error(`CIHotspot: scene \"${scene.id}\" must have a \"src\"`);\n }\n for (const scene of config.scenes) {\n for (const hotspot of scene.hotspots || []) {\n if (hotspot.navigateTo && !sceneIds.has(hotspot.navigateTo)) {\n throw new Error(`CIHotspot: hotspot \"${hotspot.id}\" navigateTo \"${hotspot.navigateTo}\" is not a valid scene ID`);\n }\n }\n }\n if (config.initialScene) {\n if (!sceneIds.has(config.initialScene)) {\n throw new Error(`CIHotspot: initialScene \"${config.initialScene}\" not found in scenes`);\n }\n }\n } else if (!config.src) {\n throw new Error('CIHotspot: \"src\" is required');\n }\n}\n","const STYLE_ID = 'ci-hotspot-styles';\n\n/** Check if code is running in browser environment */\nexport function isBrowser(): boolean {\n return typeof window !== 'undefined' && typeof document !== 'undefined';\n}\n\n/** Resolve element from selector string or HTMLElement */\nexport function getElement(el: HTMLElement | string): HTMLElement {\n if (typeof el === 'string') {\n const found = document.querySelector<HTMLElement>(el);\n if (!found) throw new Error(`CIHotspot: element \"${el}\" not found`);\n return found;\n }\n return el;\n}\n\n/** Create an HTML element with optional class and attributes */\nexport function createElement<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n className?: string,\n attrs?: Record<string, string>,\n): HTMLElementTagNameMap[K] {\n const el = document.createElement(tag);\n if (className) el.className = className;\n if (attrs) {\n for (const [key, value] of Object.entries(attrs)) {\n el.setAttribute(key, value);\n }\n }\n return el;\n}\n\n/** Add CSS class to element */\nexport function addClass(el: HTMLElement, ...classNames: string[]): void {\n el.classList.add(...classNames);\n}\n\n/** Remove CSS class from element */\nexport function removeClass(el: HTMLElement, ...classNames: string[]): void {\n el.classList.remove(...classNames);\n}\n\n/** Idempotent CSS style injection — only injects once per page */\nexport function injectStyles(css: string): void {\n if (!isBrowser()) return;\n if (document.getElementById(STYLE_ID)) return;\n const style = document.createElement('style');\n style.id = STYLE_ID;\n style.textContent = css;\n document.head.appendChild(style);\n}\n","import type { Point } from '../core/types';\n\n/**\n * Parse a coordinate value.\n * - String ending in '%': return the numeric percent value\n * - Number: return as-is (pixel value)\n */\nexport function parseCoordinate(value: string | number): { value: number; isPercent: boolean } {\n if (typeof value === 'string') {\n const trimmed = value.trim();\n if (trimmed.endsWith('%')) {\n return { value: parseFloat(trimmed), isPercent: true };\n }\n return { value: parseFloat(trimmed), isPercent: false };\n }\n return { value, isPercent: false };\n}\n\n/**\n * Normalize x/y coordinates to percentages.\n * If already percentages, returns as-is.\n * If pixel values, converts using natural image dimensions.\n */\nexport function normalizeToPercent(\n x: string | number,\n y: string | number,\n naturalWidth: number,\n naturalHeight: number,\n): Point {\n const px = parseCoordinate(x);\n const py = parseCoordinate(y);\n\n return {\n x: px.isPercent ? px.value : (px.value / naturalWidth) * 100,\n y: py.isPercent ? py.value : (py.value / naturalHeight) * 100,\n };\n}\n\n","type EventHandler = (...args: unknown[]) => void;\n\n/** Minimal typed event emitter */\nexport class EventEmitter {\n private listeners = new Map<string, Set<EventHandler>>();\n\n on(event: string, handler: EventHandler): void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(handler);\n }\n\n off(event: string, handler: EventHandler): void {\n this.listeners.get(event)?.delete(handler);\n }\n\n emit(event: string, ...args: unknown[]): void {\n this.listeners.get(event)?.forEach((handler) => handler(...args));\n }\n\n removeAll(): void {\n this.listeners.clear();\n }\n}\n\n/**\n * Add a DOM event listener and return a cleanup function.\n */\nexport function addListener<K extends keyof HTMLElementEventMap>(\n el: EventTarget,\n event: K,\n handler: (e: HTMLElementEventMap[K]) => void,\n options?: boolean | AddEventListenerOptions,\n): () => void {\n el.addEventListener(event, handler as EventListener, options);\n return () => el.removeEventListener(event, handler as EventListener, options);\n}\n\n","import type { NormalizedHotspot } from '../core/types';\nimport { createElement, addClass, removeClass } from '../utils/dom';\n\n/** Create a marker button element for a hotspot */\nexport function createMarker(hotspot: NormalizedHotspot, pulse: boolean): HTMLButtonElement {\n const marker = createElement('button', 'ci-hotspot-marker', {\n 'aria-label': hotspot.label,\n 'aria-expanded': 'false',\n 'data-hotspot-id': hotspot.id,\n 'tabindex': '0',\n });\n\n marker.style.left = `${hotspot.x}%`;\n marker.style.top = `${hotspot.y}%`;\n\n if (hotspot.className) {\n const classes = hotspot.className.trim().split(/\\s+/).filter(Boolean);\n if (classes.length) addClass(marker, ...classes);\n }\n\n if (hotspot.hidden) {\n addClass(marker, 'ci-hotspot-marker--hidden');\n }\n\n if (pulse) {\n addClass(marker, 'ci-hotspot-marker--pulse');\n }\n\n if (hotspot.icon) {\n setMarkerIcon(marker, hotspot.icon);\n }\n\n return marker;\n}\n\n/** Sanitize SVG string to prevent XSS via injected scripts or event handlers */\nfunction sanitizeSVG(svg: string): string {\n if (typeof DOMParser === 'undefined') return '';\n const doc = new DOMParser().parseFromString(svg, 'image/svg+xml');\n const root = doc.documentElement;\n // If parsing failed, return empty\n if (root.querySelector('parsererror')) return '';\n cleanSVGNode(root);\n return new XMLSerializer().serializeToString(root);\n}\n\nconst SVG_BLOCKED_ELEMENTS = new Set([\n 'script', 'foreignobject', 'iframe', 'object', 'embed',\n 'animate', 'animatetransform', 'animatemotion', 'set',\n 'style', 'a', 'use', 'image', 'feimage',\n]);\n\nfunction cleanSVGNode(node: Element): void {\n for (const attr of Array.from(node.attributes)) {\n const name = attr.name.toLowerCase();\n if (name.startsWith('on') || name === 'style') {\n node.removeAttribute(attr.name);\n } else if (\n (name === 'href' || name === 'xlink:href') &&\n /^\\s*javascript\\s*:/i.test(attr.value)\n ) {\n node.removeAttribute(attr.name);\n }\n }\n for (const child of Array.from(node.children)) {\n if (SVG_BLOCKED_ELEMENTS.has(child.tagName.toLowerCase())) {\n child.remove();\n continue;\n }\n cleanSVGNode(child);\n }\n}\n\n/** Set marker icon content */\nfunction setMarkerIcon(marker: HTMLButtonElement, icon: string): void {\n const trimmed = icon.trim();\n if (/^<svg[\\s>]/i.test(trimmed) || /^<\\?xml/i.test(trimmed)) {\n marker.innerHTML = sanitizeSVG(icon);\n } else if (icon.match(/\\.(png|jpg|jpeg|gif|svg|webp)$/i) || icon.startsWith('http') || icon.startsWith('/')) {\n const img = createElement('img', undefined, {\n src: icon,\n alt: '',\n 'aria-hidden': 'true',\n });\n img.style.width = '100%';\n img.style.height = '100%';\n img.style.objectFit = 'contain';\n marker.appendChild(img);\n } else {\n // CSS class name\n const iconEl = createElement('span', icon, { 'aria-hidden': 'true' });\n marker.appendChild(iconEl);\n }\n}\n\n/** Set marker as active (popover open) */\nexport function setMarkerActive(marker: HTMLButtonElement, active: boolean): void {\n if (active) {\n addClass(marker, 'ci-hotspot-marker--active');\n marker.setAttribute('aria-expanded', 'true');\n } else {\n removeClass(marker, 'ci-hotspot-marker--active');\n marker.setAttribute('aria-expanded', 'false');\n }\n}\n\n/** Set marker as hidden */\nexport function setMarkerHidden(marker: HTMLButtonElement, hidden: boolean): void {\n if (hidden) {\n addClass(marker, 'ci-hotspot-marker--hidden');\n } else {\n removeClass(marker, 'ci-hotspot-marker--hidden');\n }\n}\n\n/** Destroy a marker element */\nexport function destroyMarker(marker: HTMLButtonElement): void {\n marker.remove();\n}\n","import type { Placement, PositionResult } from '../core/types';\n\ninterface AvailableSpace {\n top: number;\n bottom: number;\n left: number;\n right: number;\n}\n\n/** Compute the best position for a popover relative to a marker within a container */\nexport function computePosition(\n markerEl: HTMLElement,\n popoverEl: HTMLElement,\n containerEl: HTMLElement,\n options: { placement: Placement },\n): PositionResult {\n const markerRect = markerEl.getBoundingClientRect();\n const containerRect = containerEl.getBoundingClientRect();\n const popoverWidth = popoverEl.offsetWidth;\n const popoverHeight = popoverEl.offsetHeight;\n\n // Marker position relative to container\n const markerCenterX = markerRect.left + markerRect.width / 2 - containerRect.left;\n const markerCenterY = markerRect.top + markerRect.height / 2 - containerRect.top;\n const markerTop = markerRect.top - containerRect.top;\n const markerBottom = markerRect.bottom - containerRect.top;\n const markerLeft = markerRect.left - containerRect.left;\n const markerRight = markerRect.right - containerRect.left;\n\n const containerWidth = containerEl.offsetWidth;\n const containerHeight = containerEl.offsetHeight;\n const gap = 8; // gap between marker and popover\n\n const space: AvailableSpace = {\n top: markerTop - gap,\n bottom: containerHeight - markerBottom - gap,\n left: markerLeft - gap,\n right: containerWidth - markerRight - gap,\n };\n\n let placement = options.placement;\n if (placement === 'auto') {\n placement = getAutoPlacement(space);\n }\n\n // Flip if not enough space\n placement = flip(placement, popoverWidth, popoverHeight, space);\n\n // Calculate position\n let x: number;\n let y: number;\n let arrowOffset = 0;\n\n switch (placement) {\n case 'top':\n x = markerCenterX - popoverWidth / 2;\n y = markerTop - gap - popoverHeight;\n break;\n case 'bottom':\n x = markerCenterX - popoverWidth / 2;\n y = markerBottom + gap;\n break;\n case 'left':\n x = markerLeft - gap - popoverWidth;\n y = markerCenterY - popoverHeight / 2;\n break;\n case 'right':\n x = markerRight + gap;\n y = markerCenterY - popoverHeight / 2;\n break;\n default:\n x = markerCenterX - popoverWidth / 2;\n y = markerTop - gap - popoverHeight;\n }\n\n // Shift to stay within container\n const shifted = shift(x, y, popoverWidth, popoverHeight, containerWidth, containerHeight);\n arrowOffset = (placement === 'top' || placement === 'bottom')\n ? x - shifted.x\n : y - shifted.y;\n\n return {\n x: shifted.x,\n y: shifted.y,\n placement,\n arrowOffset,\n };\n}\n\nfunction getAutoPlacement(space: AvailableSpace): Exclude<Placement, 'auto'> {\n const max = Math.max(space.top, space.bottom, space.left, space.right);\n if (max === space.top) return 'top';\n if (max === space.bottom) return 'bottom';\n if (max === space.right) return 'right';\n return 'left';\n}\n\nfunction flip(\n placement: Placement,\n popoverWidth: number,\n popoverHeight: number,\n space: AvailableSpace,\n): Exclude<Placement, 'auto'> {\n const p = placement as Exclude<Placement, 'auto'>;\n switch (p) {\n case 'top':\n if (space.top < popoverHeight && space.bottom > space.top) return 'bottom';\n break;\n case 'bottom':\n if (space.bottom < popoverHeight && space.top > space.bottom) return 'top';\n break;\n case 'left':\n if (space.left < popoverWidth && space.right > space.left) return 'right';\n break;\n case 'right':\n if (space.right < popoverWidth && space.left > space.right) return 'left';\n break;\n }\n return p;\n}\n\nfunction shift(\n x: number,\n y: number,\n width: number,\n height: number,\n containerWidth: number,\n containerHeight: number,\n): { x: number; y: number } {\n const padding = 4;\n let sx = x;\n let sy = y;\n\n // Horizontal: center if popover wider than container, otherwise clamp\n if (width > containerWidth - 2 * padding) {\n sx = (containerWidth - width) / 2;\n } else {\n if (sx < padding) sx = padding;\n if (sx + width > containerWidth - padding) sx = containerWidth - padding - width;\n }\n\n // Vertical: center if popover taller than container, otherwise clamp\n if (height > containerHeight - 2 * padding) {\n sy = (containerHeight - height) / 2;\n } else {\n if (sy < padding) sy = padding;\n if (sy + height > containerHeight - padding) sy = containerHeight - padding - height;\n }\n\n return { x: sx, y: sy };\n}\n","const ALLOWED_TAGS = new Set([\n 'a', 'b', 'br', 'div', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',\n 'i', 'img', 'li', 'ol', 'p', 'span', 'strong', 'ul',\n]);\n\nconst ALLOWED_ATTRS = new Set([\n 'class', 'href', 'src', 'alt', 'title', 'target', 'rel',\n]);\n\nconst SAFE_HREF_PATTERN = /^(?:https?:|mailto:)/i;\nconst SAFE_SRC_PATTERN = /^(?:https?:|data:image\\/(?!svg[+%]))/i;\nconst SAFE_REL_VALUES = new Set([\n 'noopener', 'noreferrer', 'nofollow', 'external', 'author', 'help',\n 'license', 'next', 'prev', 'search', 'tag', 'bookmark',\n]);\n\n/**\n * Sanitize HTML string to prevent XSS.\n * Uses DOMParser for robust parsing.\n */\nexport function sanitizeHTML(html: string): string {\n const parser = new DOMParser();\n const doc = parser.parseFromString(`<body>${html}</body>`, 'text/html');\n const body = doc.body;\n\n sanitizeNode(body);\n\n return body.innerHTML;\n}\n\nfunction sanitizeNode(node: Node): void {\n const children = Array.from(node.childNodes);\n\n for (const child of children) {\n if (child.nodeType === Node.TEXT_NODE) {\n continue;\n }\n\n if (child.nodeType === Node.ELEMENT_NODE) {\n const el = child as Element;\n const tagName = el.tagName.toLowerCase();\n\n if (!ALLOWED_TAGS.has(tagName)) {\n // Remove disallowed element entirely (including its children)\n el.remove();\n continue;\n }\n\n // Remove disallowed attributes\n const attrs = Array.from(el.attributes);\n for (const attr of attrs) {\n const name = attr.name.toLowerCase();\n\n // Block event handlers\n if (name.startsWith('on')) {\n el.removeAttribute(attr.name);\n continue;\n }\n\n if (!ALLOWED_ATTRS.has(name)) {\n el.removeAttribute(attr.name);\n continue;\n }\n\n // Block unsafe URLs per attribute type\n if (name === 'href' && !SAFE_HREF_PATTERN.test(attr.value.trim())) {\n el.removeAttribute(attr.name);\n } else if (name === 'src' && !SAFE_SRC_PATTERN.test(attr.value.trim())) {\n el.removeAttribute(attr.name);\n } else if (name === 'rel') {\n const safeTokens = attr.value.trim().toLowerCase().split(/\\s+/).filter((t) => SAFE_REL_VALUES.has(t));\n if (safeTokens.length === 0) {\n el.removeAttribute(attr.name);\n } else {\n el.setAttribute(attr.name, safeTokens.join(' '));\n }\n }\n }\n\n // Recursively sanitize children\n sanitizeNode(el);\n } else {\n // Remove comment nodes and other non-element/text nodes\n child.remove();\n }\n }\n}\n","import type { HotspotItem, PopoverData } from '../core/types';\nimport { sanitizeHTML } from './sanitize';\n\n/** Render the built-in product template from data fields */\nexport function renderBuiltInTemplate(data: PopoverData): string {\n const parts: string[] = [];\n\n if (data.image) {\n parts.push(`<img class=\"ci-hotspot-popover-image\" src=\"${escapeAttr(data.image)}\" alt=\"${escapeAttr(data.title || '')}\">`);\n }\n\n const bodyParts: string[] = [];\n\n if (data.title) {\n bodyParts.push(`<h3 class=\"ci-hotspot-popover-title\">${escapeHtml(data.title)}</h3>`);\n }\n\n if (data.price) {\n bodyParts.push(`<span class=\"ci-hotspot-popover-price\">${escapeHtml(data.price)}</span>`);\n }\n\n if (data.description) {\n bodyParts.push(`<p class=\"ci-hotspot-popover-description\">${escapeHtml(data.description)}</p>`);\n }\n\n if (data.url && isSafeUrl(data.url)) {\n const ctaText = data.ctaText || 'View details';\n bodyParts.push(\n `<a class=\"ci-hotspot-popover-cta\" href=\"${escapeAttr(data.url)}\">${escapeHtml(String(ctaText))}</a>`,\n );\n }\n\n if (bodyParts.length > 0) {\n parts.push(`<div class=\"ci-hotspot-popover-body\">${bodyParts.join('')}</div>`);\n }\n\n return parts.join('');\n}\n\n/**\n * Render popover content with priority: renderFn > content string > data template.\n * Returns HTML string or DOM element.\n */\nexport function renderPopoverContent(\n hotspot: HotspotItem,\n renderFn?: (hotspot: HotspotItem) => string | HTMLElement,\n): string | HTMLElement {\n // Priority 1: Custom render function (bypasses sanitization)\n if (renderFn) {\n return renderFn(hotspot);\n }\n\n // Priority 2: HTML content string (sanitized)\n if (hotspot.content) {\n return sanitizeHTML(hotspot.content);\n }\n\n // Priority 3: Built-in template from data\n if (hotspot.data) {\n return renderBuiltInTemplate(hotspot.data);\n }\n\n return '';\n}\n\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"');\n}\n\n/** Check that a URL uses a safe protocol (allowlist approach) */\nfunction isSafeUrl(url: string): boolean {\n const normalized = url.replace(/[\\s\\x00-\\x1f]/g, '');\n return /^https?:\\/\\//i.test(normalized) || /^\\/(?!\\/)/.test(normalized) || /^#/.test(normalized);\n}\n\nfunction escapeAttr(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/\"/g, '"')\n .replace(/'/g, ''')\n .replace(/</g, '<')\n .replace(/>/g, '>');\n}\n","import type { HotspotItem, Placement } from '../core/types';\nimport { createElement, addClass, removeClass } from '../utils/dom';\nimport { computePosition } from './position';\nimport { renderPopoverContent } from './template';\n\nexport interface PopoverOptions {\n placement: Placement;\n triggerMode: 'hover' | 'click' | 'load';\n renderFn?: (hotspot: HotspotItem) => string | HTMLElement;\n onOpen?: (hotspot: HotspotItem) => void;\n onClose?: (hotspot: HotspotItem) => void;\n}\n\nexport class Popover {\n readonly element: HTMLElement;\n private arrowEl: HTMLElement;\n private contentEl: HTMLElement;\n private visible = false;\n private hideTimer: ReturnType<typeof setTimeout> | undefined;\n private hotspot: HotspotItem;\n private markerEl: HTMLElement | null = null;\n private containerEl: HTMLElement | null = null;\n private options: PopoverOptions;\n private hoverCleanups: (() => void)[] = [];\n\n constructor(hotspot: HotspotItem, options: PopoverOptions) {\n this.hotspot = hotspot;\n this.options = options;\n\n const isDialog = options.triggerMode === 'click' || options.triggerMode === 'load';\n this.element = createElement('div', 'ci-hotspot-popover', {\n 'role': isDialog ? 'dialog' : 'tooltip',\n 'id': `ci-hotspot-popover-${hotspot.id}`,\n 'aria-hidden': 'true',\n 'data-placement': options.placement === 'auto' ? 'top' : options.placement,\n ...(isDialog && hotspot.label ? { 'aria-label': hotspot.label } : {}),\n });\n\n this.arrowEl = createElement('div', 'ci-hotspot-popover-arrow');\n this.contentEl = createElement('div', 'ci-hotspot-popover-content');\n\n this.element.appendChild(this.arrowEl);\n this.element.appendChild(this.contentEl);\n\n // Render content\n const content = renderPopoverContent(hotspot, options.renderFn);\n if (typeof content === 'string') {\n this.contentEl.innerHTML = content;\n } else if (content instanceof HTMLElement) {\n this.contentEl.appendChild(content);\n }\n\n // Hover delay: clear hide timer when mouse enters popover\n if (options.triggerMode === 'hover') {\n const onEnter = () => this.clearHideTimer();\n const onLeave = () => this.scheduleHide();\n this.element.addEventListener('mouseenter', onEnter);\n this.element.addEventListener('mouseleave', onLeave);\n this.hoverCleanups.push(\n () => this.element.removeEventListener('mouseenter', onEnter),\n () => this.element.removeEventListener('mouseleave', onLeave),\n );\n }\n }\n\n /** Mount the popover to a container, associating it with a marker */\n mount(containerEl: HTMLElement, markerEl: HTMLElement): void {\n this.containerEl = containerEl;\n this.markerEl = markerEl;\n containerEl.appendChild(this.element);\n\n // Set appropriate ARIA relationship on marker\n if (this.options.triggerMode === 'click' || this.options.triggerMode === 'load') {\n markerEl.setAttribute('aria-haspopup', 'dialog');\n markerEl.setAttribute('aria-controls', this.element.id);\n } else {\n markerEl.setAttribute('aria-describedby', this.element.id);\n }\n }\n\n /** Show the popover */\n show(): void {\n this.clearHideTimer();\n if (this.visible) return;\n this.visible = true;\n\n addClass(this.element, 'ci-hotspot-popover--visible');\n this.element.setAttribute('aria-hidden', 'false');\n\n this.updatePosition();\n this.options.onOpen?.(this.hotspot);\n }\n\n /** Schedule hide with delay (for hover mode) */\n scheduleHide(delay: number = 200): void {\n this.clearHideTimer();\n this.hideTimer = setTimeout(() => {\n this.hide();\n }, delay);\n }\n\n /** Hide the popover immediately */\n hide(): void {\n this.clearHideTimer();\n if (!this.visible) return;\n this.visible = false;\n\n removeClass(this.element, 'ci-hotspot-popover--visible');\n this.element.setAttribute('aria-hidden', 'true');\n\n this.options.onClose?.(this.hotspot);\n }\n\n /** Clear any pending hide timer */\n clearHideTimer(): void {\n if (this.hideTimer !== undefined) {\n clearTimeout(this.hideTimer);\n this.hideTimer = undefined;\n }\n }\n\n /** Update popover position relative to marker */\n updatePosition(): void {\n if (!this.markerEl || !this.containerEl || !this.visible) return;\n\n const result = computePosition(\n this.markerEl,\n this.element,\n this.containerEl,\n { placement: this.options.placement },\n );\n\n this.element.style.left = `${result.x}px`;\n this.element.style.top = `${result.y}px`;\n this.element.setAttribute('data-placement', result.placement);\n\n // Position arrow\n this.positionArrow(result.placement, result.arrowOffset);\n }\n\n private positionArrow(placement: Placement, offset: number): void {\n const arrow = this.arrowEl;\n // Reset\n arrow.style.left = '';\n arrow.style.top = '';\n\n if (placement === 'top' || placement === 'bottom') {\n arrow.style.left = `calc(50% - var(--ci-hotspot-arrow-size) + ${offset}px)`;\n } else {\n arrow.style.top = `calc(50% - var(--ci-hotspot-arrow-size) + ${offset}px)`;\n }\n }\n\n /** Check if popover is currently visible */\n isVisible(): boolean {\n return this.visible;\n }\n\n /** Get the hotspot associated with this popover */\n getHotspot(): HotspotItem {\n return this.hotspot;\n }\n\n /** Destroy the popover and clean up */\n destroy(): void {\n this.clearHideTimer();\n this.hoverCleanups.forEach((fn) => fn());\n this.hoverCleanups = [];\n this.markerEl?.removeAttribute('aria-describedby');\n this.markerEl?.removeAttribute('aria-controls');\n this.markerEl?.removeAttribute('aria-haspopup');\n this.element.remove();\n this.markerEl = null;\n this.containerEl = null;\n }\n}\n","export interface GestureCallbacks {\n onPinch?: (scale: number, centerX: number, centerY: number) => void;\n onPanStart?: () => void;\n onPan?: (totalDx: number, totalDy: number) => void;\n onPanEnd?: () => void;\n onDoubleTap?: (x: number, y: number) => void;\n}\n\n/** Recognizes touch gestures: pinch, pan, double-tap */\nexport class GestureRecognizer {\n private el: HTMLElement;\n private callbacks: GestureCallbacks;\n private lastTouchEnd = 0;\n private initialPinchDistance = 0;\n private initialPinchScale = 1;\n private panStartX = 0;\n private panStartY = 0;\n private isPinching = false;\n private isPanning = false;\n private wasPinching = false;\n private cleanups: (() => void)[] = [];\n\n constructor(el: HTMLElement, callbacks: GestureCallbacks, getZoom: () => number) {\n this.el = el;\n this.callbacks = callbacks;\n this.initialPinchScale = getZoom();\n\n const onTouchStart = (e: TouchEvent) => this.handleTouchStart(e, getZoom);\n const onTouchMove = (e: TouchEvent) => this.handleTouchMove(e);\n const onTouchEnd = (e: TouchEvent) => this.handleTouchEnd(e);\n\n el.addEventListener('touchstart', onTouchStart, { passive: false });\n el.addEventListener('touchmove', onTouchMove, { passive: false });\n el.addEventListener('touchend', onTouchEnd, { passive: true });\n\n this.cleanups.push(\n () => el.removeEventListener('touchstart', onTouchStart),\n () => el.removeEventListener('touchmove', onTouchMove),\n () => el.removeEventListener('touchend', onTouchEnd),\n );\n }\n\n private handleTouchStart(e: TouchEvent, getZoom: () => number): void {\n if (e.touches.length === 2) {\n e.preventDefault();\n this.isPinching = true;\n this.wasPinching = false;\n this.initialPinchDistance = this.getTouchDistance(e.touches);\n this.initialPinchScale = getZoom();\n } else if (e.touches.length === 1) {\n this.panStartX = e.touches[0].clientX;\n this.panStartY = e.touches[0].clientY;\n this.wasPinching = false;\n }\n }\n\n private handleTouchMove(e: TouchEvent): void {\n if (e.touches.length === 2 && this.isPinching) {\n e.preventDefault();\n const currentDistance = this.getTouchDistance(e.touches);\n const scale = this.initialPinchScale * (currentDistance / this.initialPinchDistance);\n const centerX = (e.touches[0].clientX + e.touches[1].clientX) / 2;\n const centerY = (e.touches[0].clientY + e.touches[1].clientY) / 2;\n this.callbacks.onPinch?.(scale, centerX, centerY);\n } else if (e.touches.length === 1 && !this.isPinching && !this.wasPinching) {\n if (!this.isPanning) {\n this.isPanning = true;\n this.callbacks.onPanStart?.();\n }\n const totalDx = e.touches[0].clientX - this.panStartX;\n const totalDy = e.touches[0].clientY - this.panStartY;\n this.callbacks.onPan?.(totalDx, totalDy);\n }\n }\n\n private handleTouchEnd(e: TouchEvent): void {\n if (this.isPinching && e.touches.length < 2) {\n this.isPinching = false;\n this.wasPinching = true;\n // Update pan start to remaining finger position\n if (e.touches.length === 1) {\n this.panStartX = e.touches[0].clientX;\n this.panStartY = e.touches[0].clientY;\n }\n }\n\n if (this.isPanning) {\n this.isPanning = false;\n this.callbacks.onPanEnd?.();\n }\n\n // Double-tap detection (suppressed after pinch)\n if (e.changedTouches.length === 1 && e.touches.length === 0) {\n if (this.wasPinching) {\n this.wasPinching = false;\n this.lastTouchEnd = 0;\n return;\n }\n const now = Date.now();\n if (now - this.lastTouchEnd < 300) {\n const touch = e.changedTouches[0];\n this.callbacks.onDoubleTap?.(touch.clientX, touch.clientY);\n }\n this.lastTouchEnd = now;\n }\n }\n\n private getTouchDistance(touches: TouchList): number {\n const dx = touches[0].clientX - touches[1].clientX;\n const dy = touches[0].clientY - touches[1].clientY;\n return Math.sqrt(dx * dx + dy * dy);\n }\n\n destroy(): void {\n this.cleanups.forEach((fn) => fn());\n this.cleanups = [];\n }\n}\n","import { addClass, removeClass } from '../utils/dom';\nimport { GestureRecognizer } from './gestures';\n\n/** Safari's proprietary GestureEvent (not in standard TypeScript lib) */\ninterface SafariGestureEvent extends UIEvent {\n readonly scale: number;\n readonly rotation: number;\n}\n\ndeclare global {\n interface WindowEventMap {\n gesturestart: SafariGestureEvent;\n gesturechange: SafariGestureEvent;\n gestureend: SafariGestureEvent;\n }\n}\n\nexport interface ZoomPanOptions {\n zoomMin: number;\n zoomMax: number;\n onZoom?: (level: number) => void;\n onScrollWithoutZoom?: () => void;\n}\n\nexport class ZoomPan {\n private viewport: HTMLElement;\n private container: HTMLElement;\n private options: ZoomPanOptions;\n private zoom = 1;\n private panX = 0;\n private panY = 0;\n private enabled = true;\n private isDragging = false;\n private dragStartX = 0;\n private dragStartY = 0;\n private lastPanX = 0;\n private lastPanY = 0;\n private gestures: GestureRecognizer | null = null;\n private isGesturing = false;\n private gestureStartZoom = 1;\n private touchStartPanX = 0;\n private touchStartPanY = 0;\n private cleanups: (() => void)[] = [];\n\n constructor(viewport: HTMLElement, container: HTMLElement, options: ZoomPanOptions) {\n this.viewport = viewport;\n this.container = container;\n this.options = options;\n\n this.bindEvents();\n }\n\n private bindEvents(): void {\n // Mouse wheel zoom — only on pinch (ctrlKey) or Ctrl+scroll\n const onWheel = (e: WheelEvent) => {\n if (!this.enabled) return;\n\n // Safari fires ctrlKey wheel events during a gesture — skip if already handled\n if (this.isGesturing) {\n e.preventDefault();\n return;\n }\n\n if (!e.ctrlKey) {\n // Regular scroll — let it pass through to the page\n this.options.onScrollWithoutZoom?.();\n return;\n }\n\n e.preventDefault();\n\n const rect = this.container.getBoundingClientRect();\n const originX = e.clientX - rect.left;\n const originY = e.clientY - rect.top;\n\n // Normalize deltaY: Firefox line-mode (deltaMode=1) uses ~20px per line\n let deltaY = e.deltaY;\n if (e.deltaMode === 1) deltaY *= 20;\n\n const delta = -deltaY * 0.01;\n this.setZoom(this.zoom + delta, originX, originY);\n };\n this.container.addEventListener('wheel', onWheel, { passive: false });\n this.cleanups.push(() => this.container.removeEventListener('wheel', onWheel));\n\n // Safari GestureEvent support (proprietary pinch-to-zoom)\n this.bindSafariGestures();\n\n // Double-click toggle\n const onDblClick = (e: MouseEvent) => {\n if (!this.enabled) return;\n const rect = this.container.getBoundingClientRect();\n const x = e.clientX - rect.left;\n const y = e.clientY - rect.top;\n if (this.zoom > 1) {\n this.resetZoom();\n } else {\n this.setZoom(2, x, y);\n }\n };\n this.container.addEventListener('dblclick', onDblClick);\n this.cleanups.push(() => this.container.removeEventListener('dblclick', onDblClick));\n\n // Mouse drag for panning\n const onMouseDown = (e: MouseEvent) => {\n if (!this.enabled || this.zoom <= 1) return;\n // Only pan with left mouse button\n if (e.button !== 0) return;\n this.isDragging = true;\n this.dragStartX = e.clientX;\n this.dragStartY = e.clientY;\n this.lastPanX = this.panX;\n this.lastPanY = this.panY;\n addClass(this.viewport, 'ci-hotspot-viewport--dragging');\n this.container.style.cursor = 'grabbing';\n e.preventDefault();\n };\n\n const onMouseMove = (e: MouseEvent) => {\n if (!this.isDragging) return;\n const dx = (e.clientX - this.dragStartX) / this.zoom;\n const dy = (e.clientY - this.dragStartY) / this.zoom;\n this.panX = this.lastPanX + dx;\n this.panY = this.lastPanY + dy;\n this.clampPan();\n this.applyTransform();\n };\n\n const onMouseUp = () => {\n if (!this.isDragging) return;\n this.isDragging = false;\n removeClass(this.viewport, 'ci-hotspot-viewport--dragging');\n this.container.style.cursor = this.zoom > 1 ? 'grab' : '';\n };\n\n this.container.addEventListener('mousedown', onMouseDown);\n document.addEventListener('mousemove', onMouseMove);\n document.addEventListener('mouseup', onMouseUp);\n this.cleanups.push(\n () => this.container.removeEventListener('mousedown', onMouseDown),\n () => document.removeEventListener('mousemove', onMouseMove),\n () => document.removeEventListener('mouseup', onMouseUp),\n );\n\n // Touch gestures\n this.gestures = new GestureRecognizer(\n this.container,\n {\n onPinch: (scale, cx, cy) => {\n const rect = this.container.getBoundingClientRect();\n this.setZoom(scale, cx - rect.left, cy - rect.top);\n },\n onPanStart: () => {\n this.touchStartPanX = this.panX;\n this.touchStartPanY = this.panY;\n },\n onPan: (totalDx, totalDy) => {\n if (this.zoom <= 1) return;\n this.panX = this.touchStartPanX + totalDx / this.zoom;\n this.panY = this.touchStartPanY + totalDy / this.zoom;\n this.clampPan();\n this.applyTransform();\n },\n onDoubleTap: (cx, cy) => {\n const rect = this.container.getBoundingClientRect();\n if (this.zoom > 1) {\n this.resetZoom();\n } else {\n this.setZoom(2, cx - rect.left, cy - rect.top);\n }\n },\n },\n () => this.zoom,\n );\n }\n\n private bindSafariGestures(): void {\n // Feature-detect Safari's proprietary GestureEvent\n if (typeof window === 'undefined' || !('GestureEvent' in window)) return;\n\n const onGestureStart = (e: Event) => {\n e.preventDefault();\n this.isGesturing = true;\n this.gestureStartZoom = this.zoom;\n };\n\n const onGestureChange = (e: Event) => {\n e.preventDefault();\n if (!this.enabled) return;\n const ge = e as unknown as SafariGestureEvent;\n const rect = this.container.getBoundingClientRect();\n // Safari GestureEvent may carry cursor coordinates; fall back to center\n const ev = e as unknown as { clientX?: number; clientY?: number };\n const originX = ev.clientX != null ? ev.clientX - rect.left : rect.width / 2;\n const originY = ev.clientY != null ? ev.clientY - rect.top : rect.height / 2;\n this.setZoom(this.gestureStartZoom * ge.scale, originX, originY);\n };\n\n const onGestureEnd = (e: Event) => {\n e.preventDefault();\n this.isGesturing = false;\n };\n\n this.container.addEventListener('gesturestart', onGestureStart as EventListener);\n this.container.addEventListener('gesturechange', onGestureChange as EventListener);\n this.container.addEventListener('gestureend', onGestureEnd as EventListener);\n this.cleanups.push(\n () => this.container.removeEventListener('gesturestart', onGestureStart as EventListener),\n () => this.container.removeEventListener('gesturechange', onGestureChange as EventListener),\n () => this.container.removeEventListener('gestureend', onGestureEnd as EventListener),\n );\n }\n\n setZoom(level: number, originX?: number, originY?: number): void {\n const oldZoom = this.zoom;\n this.zoom = Math.max(this.options.zoomMin, Math.min(this.options.zoomMax, level));\n\n if (originX !== undefined && originY !== undefined && oldZoom !== this.zoom) {\n // Adjust pan to zoom toward the origin point\n const containerWidth = this.container.offsetWidth;\n const containerHeight = this.container.offsetHeight;\n const scaleChange = this.zoom / oldZoom;\n const ox = originX / containerWidth;\n const oy = originY / containerHeight;\n this.panX = this.panX - (containerWidth * ox * (scaleChange - 1)) / this.zoom;\n this.panY = this.panY - (containerHeight * oy * (scaleChange - 1)) / this.zoom;\n }\n\n this.clampPan();\n this.applyTransform();\n this.updateCursor();\n this.options.onZoom?.(this.zoom);\n }\n\n getZoom(): number {\n return this.zoom;\n }\n\n resetZoom(): void {\n this.zoom = 1;\n this.panX = 0;\n this.panY = 0;\n this.applyTransform();\n this.updateCursor();\n this.options.onZoom?.(1);\n }\n\n pan(dx: number, dy: number): void {\n if (this.zoom <= 1) return;\n this.panX += dx / this.zoom;\n this.panY += dy / this.zoom;\n this.clampPan();\n this.applyTransform();\n }\n\n enable(): void {\n this.enabled = true;\n }\n\n disable(): void {\n this.enabled = false;\n }\n\n private clampPan(): void {\n const containerWidth = this.container.offsetWidth;\n const containerHeight = this.container.offsetHeight;\n\n // Maximum pan so image edge stays at container edge.\n // With transform-origin 0 0 and scale() before translate(), valid range is [-max, 0]:\n // pan=0 → top-left visible, pan=-max → bottom-right visible.\n const maxPanX = (containerWidth * (this.zoom - 1)) / this.zoom;\n const maxPanY = (containerHeight * (this.zoom - 1)) / this.zoom;\n\n this.panX = Math.max(-maxPanX, Math.min(0, this.panX));\n this.panY = Math.max(-maxPanY, Math.min(0, this.panY));\n\n if (this.zoom <= 1) {\n this.panX = 0;\n this.panY = 0;\n }\n }\n\n private applyTransform(): void {\n this.viewport.style.transform = `scale(${this.zoom}) translate(${this.panX}px, ${this.panY}px)`;\n this.viewport.style.setProperty('--zoom', String(this.zoom));\n }\n\n private updateCursor(): void {\n if (!this.isDragging) {\n this.container.style.cursor = this.zoom > 1 ? 'grab' : '';\n }\n }\n\n destroy(): void {\n this.cleanups.forEach((fn) => fn());\n this.cleanups = [];\n this.gestures?.destroy();\n this.gestures = null;\n this.viewport.style.transform = '';\n this.viewport.style.removeProperty('--zoom');\n this.container.style.cursor = '';\n }\n}\n","import { createElement } from '../utils/dom';\nimport type { ZoomPan } from './zoom-pan';\n\nexport interface ZoomControlsOptions {\n zoomMin: number;\n zoomMax: number;\n zoomStep?: number;\n position?: string;\n}\n\n/** Create zoom controls UI (+/−/reset) */\nexport function createZoomControls(\n container: HTMLElement,\n zoomPan: ZoomPan,\n options: ZoomControlsOptions,\n): { element: HTMLElement; update: () => void; destroy: () => void } {\n const step = options.zoomStep || 0.5;\n const controls = createElement('div', 'ci-hotspot-zoom-controls');\n controls.dataset.position = options.position || 'bottom-right';\n\n const btnIn = createElement('button', 'ci-hotspot-zoom-in', {\n 'aria-label': 'Zoom in',\n 'type': 'button',\n });\n btnIn.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>';\n\n const btnOut = createElement('button', 'ci-hotspot-zoom-out', {\n 'aria-label': 'Zoom out',\n 'type': 'button',\n });\n btnOut.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>';\n\n const btnReset = createElement('button', 'ci-hotspot-zoom-reset', {\n 'aria-label': 'Reset zoom',\n 'type': 'button',\n });\n btnReset.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>';\n\n controls.appendChild(btnIn);\n controls.appendChild(btnOut);\n controls.appendChild(btnReset);\n\n btnIn.addEventListener('click', (e) => {\n e.stopPropagation();\n zoomPan.setZoom(zoomPan.getZoom() + step);\n updateState();\n });\n\n btnOut.addEventListener('click', (e) => {\n e.stopPropagation();\n zoomPan.setZoom(zoomPan.getZoom() - step);\n updateState();\n });\n\n btnReset.addEventListener('click', (e) => {\n e.stopPropagation();\n zoomPan.resetZoom();\n updateState();\n });\n\n function updateState(): void {\n const zoom = zoomPan.getZoom();\n btnIn.disabled = zoom >= options.zoomMax;\n btnOut.disabled = zoom <= options.zoomMin;\n btnReset.disabled = Math.abs(zoom - 1) < 0.001;\n }\n\n container.appendChild(controls);\n updateState();\n\n return {\n element: controls,\n update: updateState,\n destroy: () => controls.remove(),\n };\n}\n","const isMac =\n typeof navigator !== 'undefined' && /Mac|iPhone|iPad|iPod/i.test(navigator.userAgent);\n\nconst HINT_TEXT = isMac\n ? '\\u2318 Scroll or pinch to zoom'\n : 'Ctrl + scroll to zoom';\n\nconst HIDE_DELAY = 1500;\n\nexport class ScrollHint {\n private el: HTMLElement;\n private hideTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor(parent: HTMLElement) {\n this.el = document.createElement('div');\n this.el.className = 'ci-hotspot-scroll-hint';\n this.el.textContent = HINT_TEXT;\n this.el.setAttribute('aria-hidden', 'true');\n parent.appendChild(this.el);\n }\n\n show(): void {\n // Reset timer on repeated scrolls\n if (this.hideTimer !== null) {\n clearTimeout(this.hideTimer);\n }\n\n this.el.classList.add('ci-hotspot-scroll-hint--visible');\n\n this.hideTimer = setTimeout(() => {\n this.el.classList.remove('ci-hotspot-scroll-hint--visible');\n this.hideTimer = null;\n }, HIDE_DELAY);\n }\n\n destroy(): void {\n if (this.hideTimer !== null) {\n clearTimeout(this.hideTimer);\n this.hideTimer = null;\n }\n this.el.remove();\n }\n}\n","import type { CloudimageConfig } from '../core/types';\n\nconst DEFAULT_DOMAIN = 'cloudimg.io';\nconst DEFAULT_API_VERSION = 'v7';\nconst DEFAULT_LIMIT_FACTOR = 100;\n\n/** Round a width up to the nearest limitFactor */\nexport function roundToLimitFactor(width: number, limitFactor: number = DEFAULT_LIMIT_FACTOR): number {\n return Math.ceil(width / limitFactor) * limitFactor;\n}\n\n/** Get the optimal image width for the container */\nexport function getOptimalWidth(\n containerWidth: number,\n dpr: number = 1,\n zoomLevel: number = 1,\n limitFactor: number = DEFAULT_LIMIT_FACTOR,\n): number {\n const raw = containerWidth * dpr * zoomLevel;\n return roundToLimitFactor(raw, limitFactor);\n}\n\n/** Build a Cloudimage CDN URL */\nexport function buildCloudimageUrl(\n src: string,\n config: CloudimageConfig,\n containerWidth: number,\n zoomLevel: number = 1,\n dpr: number = 1,\n): string {\n const domain = config.domain || DEFAULT_DOMAIN;\n const apiVersion = config.apiVersion || DEFAULT_API_VERSION;\n const limitFactor = config.limitFactor || DEFAULT_LIMIT_FACTOR;\n\n const width = getOptimalWidth(containerWidth, dpr, zoomLevel, limitFactor);\n\n const encodedSrc = encodeURI(src);\n let url = `https://${config.token}.${domain}/${apiVersion}/${encodedSrc}?width=${width}`;\n if (config.params) {\n url += `&${config.params}`;\n }\n return url;\n}\n\n/**\n * Create a ResizeObserver-based handler that updates img src when\n * the container width crosses a limitFactor boundary.\n */\nexport function createResizeHandler(\n img: HTMLImageElement,\n src: string,\n config: CloudimageConfig,\n getZoomLevel: () => number,\n): { observer: ResizeObserver; destroy: () => void } {\n const limitFactor = config.limitFactor || DEFAULT_LIMIT_FACTOR;\n let lastRequestedWidth = 0;\n\n const observer = new ResizeObserver((entries) => {\n for (const entry of entries) {\n const containerWidth = entry.contentRect.width;\n if (containerWidth === 0) continue;\n\n const dpr = typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1;\n const newWidth = getOptimalWidth(containerWidth, dpr, getZoomLevel(), limitFactor);\n\n if (newWidth !== lastRequestedWidth) {\n lastRequestedWidth = newWidth;\n img.src = buildCloudimageUrl(src, config, containerWidth, getZoomLevel(), dpr);\n }\n }\n });\n\n return {\n observer,\n destroy: () => observer.disconnect(),\n };\n}\n","import type { ZoomPan } from '../zoom/zoom-pan';\nimport { addListener } from '../utils/events';\n\nexport interface KeyboardHandlerOptions {\n container: HTMLElement;\n getZoomPan: () => ZoomPan | null;\n onEscape?: () => void;\n onFullscreenToggle?: () => void;\n}\n\nconst PAN_STEP = 50;\nconst ZOOM_STEP = 0.5;\n\n/** Handles keyboard navigation within the hotspot container */\nexport class KeyboardHandler {\n private cleanups: (() => void)[] = [];\n\n constructor(options: KeyboardHandlerOptions) {\n const { container, getZoomPan, onEscape, onFullscreenToggle } = options;\n\n const cleanup = addListener(container, 'keydown', (e: KeyboardEvent) => {\n const target = e.target as HTMLElement;\n\n // Don't handle keys typed into form elements inside popovers\n if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.tagName === 'SELECT') {\n return;\n }\n\n const zoomPan = getZoomPan();\n\n switch (e.key) {\n case 'Escape':\n onEscape?.();\n break;\n\n case 'ArrowUp':\n if (zoomPan && zoomPan.getZoom() > 1) {\n e.preventDefault();\n zoomPan.pan(0, PAN_STEP);\n }\n break;\n case 'ArrowDown':\n if (zoomPan && zoomPan.getZoom() > 1) {\n e.preventDefault();\n zoomPan.pan(0, -PAN_STEP);\n }\n break;\n case 'ArrowLeft':\n if (zoomPan && zoomPan.getZoom() > 1) {\n e.preventDefault();\n zoomPan.pan(PAN_STEP, 0);\n }\n break;\n case 'ArrowRight':\n if (zoomPan && zoomPan.getZoom() > 1) {\n e.preventDefault();\n zoomPan.pan(-PAN_STEP, 0);\n }\n break;\n\n case '+':\n case '=':\n if (zoomPan) {\n e.preventDefault();\n zoomPan.setZoom(zoomPan.getZoom() + ZOOM_STEP);\n }\n break;\n case '-':\n if (zoomPan) {\n e.preventDefault();\n zoomPan.setZoom(zoomPan.getZoom() - ZOOM_STEP);\n }\n break;\n case '0':\n if (zoomPan) {\n e.preventDefault();\n zoomPan.resetZoom();\n }\n break;\n\n case 'f':\n if (onFullscreenToggle) {\n e.preventDefault();\n onFullscreenToggle();\n }\n break;\n }\n });\n\n this.cleanups.push(cleanup);\n }\n\n destroy(): void {\n this.cleanups.forEach((fn) => fn());\n this.cleanups = [];\n }\n}\n","import { addListener } from '../utils/events';\n\nconst FOCUSABLE_SELECTOR = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n\n/** Get all focusable elements within a container */\nexport function getFocusableElements(container: HTMLElement): HTMLElement[] {\n return Array.from(container.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR));\n}\n\n/** Create a focus trap within a popover element */\nexport function createFocusTrap(\n popover: HTMLElement,\n returnFocusTo: HTMLElement,\n): { activate: () => void; deactivate: () => void; destroy: () => void } {\n let active = false;\n let cleanup: (() => void) | null = null;\n\n function activate(): void {\n if (active) return;\n active = true;\n\n const focusable = getFocusableElements(popover);\n if (focusable.length === 0) return;\n\n // Focus first element\n focusable[0].focus();\n\n cleanup = addListener(popover, 'keydown', (e: KeyboardEvent) => {\n if (e.key !== 'Tab') return;\n\n const currentFocusable = getFocusableElements(popover);\n if (currentFocusable.length === 0) return;\n\n const first = currentFocusable[0];\n const last = currentFocusable[currentFocusable.length - 1];\n\n if (e.shiftKey && document.activeElement === first) {\n e.preventDefault();\n last.focus();\n } else if (!e.shiftKey && document.activeElement === last) {\n e.preventDefault();\n first.focus();\n }\n });\n }\n\n function deactivate(): void {\n if (!active) return;\n active = false;\n cleanup?.();\n cleanup = null;\n returnFocusTo.focus();\n }\n\n function destroy(): void {\n deactivate();\n }\n\n return { activate, deactivate, destroy };\n}\n\n","import { isBrowser, createElement } from '../utils/dom';\n\nlet liveRegion: HTMLElement | null = null;\nlet liveRegionRefCount = 0;\n\n/** Announce a message to screen readers via a live region */\nexport function announceToScreenReader(message: string): void {\n if (!isBrowser()) return;\n\n if (!liveRegion) {\n liveRegion = createElement('div', undefined, {\n 'aria-live': 'polite',\n 'aria-atomic': 'true',\n 'role': 'status',\n });\n liveRegion.style.cssText =\n 'position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0';\n document.body.appendChild(liveRegion);\n }\n\n // Clear then set to trigger announcement\n liveRegion.textContent = '';\n requestAnimationFrame(() => {\n if (liveRegion) liveRegion.textContent = message;\n });\n}\n\n/** Register an instance that uses the live region */\nexport function acquireLiveRegion(): void {\n liveRegionRefCount++;\n}\n\n/** Release an instance; removes the live region when the last one is released */\nexport function releaseLiveRegion(): void {\n liveRegionRefCount = Math.max(0, liveRegionRefCount - 1);\n if (liveRegionRefCount === 0 && liveRegion) {\n liveRegion.remove();\n liveRegion = null;\n }\n}\n","import { createElement, addClass, removeClass } from '../utils/dom';\nimport { addListener } from '../utils/events';\n\nexport interface FullscreenControlOptions {\n onChange?: (isFullscreen: boolean) => void;\n}\n\nexport interface FullscreenControl {\n element: HTMLElement;\n isFullscreen: () => boolean;\n toggle: () => void;\n enter: () => void;\n exit: () => void;\n destroy: () => void;\n}\n\n// Lucide Maximize2 SVG\nconst MAXIMIZE_SVG =\n '<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>';\n\n// Lucide Minimize2 SVG\nconst MINIMIZE_SVG =\n '<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>';\n\nfunction isFullscreenEnabled(): boolean {\n return !!(\n document.fullscreenEnabled ||\n (document as any).webkitFullscreenEnabled\n );\n}\n\nfunction getFullscreenElement(): Element | null {\n return (\n document.fullscreenElement ||\n (document as any).webkitFullscreenElement ||\n null\n );\n}\n\nfunction requestFullscreen(el: HTMLElement): Promise<void> {\n if (el.requestFullscreen) return el.requestFullscreen();\n if ((el as any).webkitRequestFullscreen) {\n (el as any).webkitRequestFullscreen();\n return Promise.resolve();\n }\n return Promise.reject(new Error('Fullscreen API not supported'));\n}\n\nfunction exitFullscreen(): Promise<void> {\n if (document.exitFullscreen) return document.exitFullscreen();\n if ((document as any).webkitExitFullscreen) {\n (document as any).webkitExitFullscreen();\n return Promise.resolve();\n }\n return Promise.reject(new Error('Fullscreen API not supported'));\n}\n\n/** Create a fullscreen toggle button for the container */\nexport function createFullscreenControl(\n container: HTMLElement,\n options: FullscreenControlOptions = {},\n): FullscreenControl | null {\n if (!isFullscreenEnabled()) return null;\n\n const btn = createElement('button', 'ci-hotspot-fullscreen-btn', {\n 'aria-label': 'Enter fullscreen',\n 'aria-pressed': 'false',\n 'type': 'button',\n });\n btn.innerHTML = MAXIMIZE_SVG;\n\n const cleanups: (() => void)[] = [];\n\n function isActive(): boolean {\n return getFullscreenElement() === container;\n }\n\n function syncState(): void {\n const fs = isActive();\n btn.innerHTML = fs ? MINIMIZE_SVG : MAXIMIZE_SVG;\n btn.setAttribute('aria-label', fs ? 'Exit fullscreen' : 'Enter fullscreen');\n btn.setAttribute('aria-pressed', String(fs));\n if (fs) {\n addClass(container, 'ci-hotspot-container--fullscreen');\n } else {\n removeClass(container, 'ci-hotspot-container--fullscreen');\n }\n options.onChange?.(fs);\n }\n\n function toggle(): void {\n if (isActive()) {\n exitFullscreen().catch(() => {});\n } else {\n requestFullscreen(container).catch(() => {});\n }\n }\n\n function enter(): void {\n if (!isActive()) {\n requestFullscreen(container).catch(() => {});\n }\n }\n\n function exit(): void {\n if (isActive()) {\n exitFullscreen().catch(() => {});\n }\n }\n\n // Listen to fullscreenchange (handles both standard and webkit)\n const changeCleanup = addListener(document, 'fullscreenchange', syncState);\n cleanups.push(changeCleanup);\n\n document.addEventListener('webkitfullscreenchange', syncState);\n cleanups.push(() => document.removeEventListener('webkitfullscreenchange', syncState));\n\n // Button click\n const clickCleanup = addListener(btn, 'click', (e) => {\n e.stopPropagation();\n toggle();\n });\n cleanups.push(clickCleanup);\n\n container.appendChild(btn);\n\n function destroy(): void {\n // Exit fullscreen if active before cleanup\n if (isActive()) {\n exitFullscreen().catch(() => {});\n }\n removeClass(container, 'ci-hotspot-container--fullscreen');\n cleanups.forEach((fn) => fn());\n cleanups.length = 0;\n btn.remove();\n }\n\n return {\n element: btn,\n isFullscreen: isActive,\n toggle,\n enter,\n exit,\n destroy,\n };\n}\n","import type { CIHotspotConfig, CIHotspotInstance, HotspotItem, NormalizedHotspot, ResolvedCIHotspotConfig, Scene, SceneTransition, TriggerMode } from './types';\nimport { mergeConfig, parseDataAttributes, validateConfig } from './config';\nimport { getElement, createElement, addClass, removeClass, injectStyles } from '../utils/dom';\nimport { normalizeToPercent } from '../utils/coordinates';\nimport { addListener } from '../utils/events';\nimport { createMarker, setMarkerActive, setMarkerHidden, destroyMarker } from '../markers/marker';\nimport { Popover } from '../popover/popover';\nimport { ZoomPan } from '../zoom/zoom-pan';\nimport { createZoomControls } from '../zoom/controls';\nimport { ScrollHint } from '../zoom/scroll-hint';\nimport { buildCloudimageUrl, createResizeHandler } from '../utils/cloudimage';\nimport { KeyboardHandler } from '../a11y/keyboard';\nimport { createFocusTrap } from '../a11y/focus';\nimport { announceToScreenReader, acquireLiveRegion, releaseLiveRegion } from '../a11y/aria';\nimport { createFullscreenControl, type FullscreenControl } from '../fullscreen/fullscreen';\nimport cssText from '../styles/index.css?inline';\n\nexport class CIHotspot implements CIHotspotInstance {\n private config: ResolvedCIHotspotConfig;\n private rootEl: HTMLElement;\n private containerEl!: HTMLElement;\n private viewportEl!: HTMLElement;\n private imgEl!: HTMLImageElement;\n private markersEl!: HTMLElement;\n private markers = new Map<string, HTMLButtonElement>();\n private popovers = new Map<string, Popover>();\n private normalizedHotspots = new Map<string, NormalizedHotspot>();\n private scrollHint: ScrollHint | null = null;\n private zoomPan: ZoomPan | null = null;\n private zoomControls: { element: HTMLElement; update: () => void; destroy: () => void } | null = null;\n private cloudimageHandler: { observer: ResizeObserver; destroy: () => void } | null = null;\n private resizeObserver: ResizeObserver | null = null;\n private keyboardHandler: KeyboardHandler | null = null;\n private fullscreenControl: FullscreenControl | null = null;\n private focusTraps = new Map<string, ReturnType<typeof createFocusTrap>>();\n private cleanups: (() => void)[] = [];\n private hotspotCleanups = new Map<string, (() => void)[]>();\n private imageLoaded = false;\n private destroyed = false;\n private currentSceneId: string | undefined;\n private scenesMap = new Map<string, Scene>();\n private isTransitioning = false;\n private transitionTimer: ReturnType<typeof setTimeout> | undefined;\n private activeTimers = new Set<ReturnType<typeof setTimeout>>();\n private sceneHotspotOverrides = new Map<string, HotspotItem[]>();\n\n constructor(element: HTMLElement | string, config: CIHotspotConfig) {\n this.rootEl = getElement(element);\n this.config = mergeConfig(config);\n validateConfig(this.config);\n\n if (this.config.scenes && this.config.scenes.length > 0) {\n this.initScenes();\n }\n\n acquireLiveRegion();\n injectStyles(cssText);\n this.buildDOM();\n this.applyTheme();\n this.setupImage();\n this.initHotspots();\n\n if (this.config.zoom) {\n this.initZoom();\n }\n\n this.initKeyboard();\n this.initFullscreen();\n this.setupResponsive();\n }\n\n /** Auto-initialize all elements with data-ci-hotspot-src or data-ci-hotspot-scenes attribute */\n static autoInit(root?: HTMLElement): CIHotspotInstance[] {\n const container = root || document;\n const elements = container.querySelectorAll<HTMLElement>(\n '[data-ci-hotspot-src], [data-ci-hotspot-scenes]',\n );\n const instances: CIHotspotInstance[] = [];\n\n elements.forEach((el) => {\n const config = parseDataAttributes(el);\n if (config.src || config.scenes) {\n instances.push(new CIHotspot(el, config as CIHotspotConfig));\n }\n });\n\n return instances;\n }\n\n private buildDOM(): void {\n this.containerEl = createElement('div', 'ci-hotspot-container');\n this.viewportEl = createElement('div', 'ci-hotspot-viewport');\n this.imgEl = createElement('img', 'ci-hotspot-image', {\n alt: this.config.alt || '',\n draggable: 'false',\n });\n this.markersEl = createElement('div', 'ci-hotspot-markers');\n\n this.viewportEl.appendChild(this.imgEl);\n this.viewportEl.appendChild(this.markersEl);\n this.containerEl.appendChild(this.viewportEl);\n\n // Set container role and aria-label for accessibility\n this.containerEl.setAttribute('role', 'group');\n this.containerEl.setAttribute('aria-label', this.config.alt || 'Image with hotspots');\n\n // Fixed aspect-ratio for scenes mode\n if (this.config.sceneAspectRatio) {\n addClass(this.containerEl, 'ci-hotspot-container--fixed-ratio');\n this.viewportEl.style.aspectRatio = this.config.sceneAspectRatio;\n }\n\n // Replace root element content\n this.rootEl.innerHTML = '';\n this.rootEl.appendChild(this.containerEl);\n\n // Loading state\n if (this.config.lazyLoad) {\n addClass(this.containerEl, 'ci-hotspot-loading');\n }\n }\n\n private applyTheme(): void {\n if (this.config.theme === 'dark') {\n addClass(this.containerEl, 'ci-hotspot-theme-dark');\n }\n if (this.config.invertMarkerTheme) {\n addClass(this.containerEl, 'ci-hotspot-marker-inverted');\n }\n }\n\n private setupImage(): void {\n const onLoad = () => {\n removeClass(this.containerEl, 'ci-hotspot-loading');\n this.imageLoaded = true;\n // Normalize pixel coordinates now that we know natural dimensions\n this.renormalizePixelCoordinates();\n // Position markers to match rendered image in fixed-ratio mode\n this.syncMarkersToImage();\n // Now that the image defines the container size, show load-trigger popovers\n this.showLoadTriggerPopovers();\n };\n\n this.imgEl.addEventListener('load', onLoad);\n this.cleanups.push(() => this.imgEl.removeEventListener('load', onLoad));\n\n if (this.config.lazyLoad && typeof IntersectionObserver !== 'undefined') {\n const observer = new IntersectionObserver(\n (entries) => {\n if (entries[0]?.isIntersecting) {\n this.loadImage();\n observer.disconnect();\n }\n },\n { threshold: 0.1 },\n );\n observer.observe(this.containerEl);\n this.cleanups.push(() => observer.disconnect());\n } else {\n this.loadImage();\n }\n }\n\n private loadImage(): void {\n if (this.config.cloudimage?.token) {\n const containerWidth = this.containerEl.offsetWidth || 300;\n const dpr = window.devicePixelRatio || 1;\n const zoomLevel = this.zoomPan?.getZoom() || 1;\n this.imgEl.src = buildCloudimageUrl(\n this.config.src,\n this.config.cloudimage,\n containerWidth,\n zoomLevel,\n dpr,\n );\n\n this.cloudimageHandler = createResizeHandler(\n this.imgEl,\n this.config.src,\n this.config.cloudimage,\n () => this.zoomPan?.getZoom() || 1,\n );\n this.cloudimageHandler.observer.observe(this.containerEl);\n this.cleanups.push(() => this.cloudimageHandler?.destroy());\n } else {\n this.imgEl.src = this.config.src;\n }\n }\n\n private initHotspots(): void {\n for (const hotspot of this.config.hotspots) {\n this.addHotspotInternal(hotspot);\n }\n }\n\n private addHotspotInternal(hotspot: HotspotItem): void {\n // Remove existing hotspot with same ID to prevent orphaned DOM\n if (this.markers.has(hotspot.id)) {\n const oldMarker = this.markers.get(hotspot.id)!;\n destroyMarker(oldMarker);\n this.markers.delete(hotspot.id);\n this.popovers.get(hotspot.id)?.destroy();\n this.popovers.delete(hotspot.id);\n const fns = this.hotspotCleanups.get(hotspot.id);\n if (fns) { fns.forEach((fn) => fn()); this.hotspotCleanups.delete(hotspot.id); }\n const trap = this.focusTraps.get(hotspot.id);\n if (trap) { trap.destroy(); this.focusTraps.delete(hotspot.id); }\n }\n\n // Normalize coordinates\n const { x, y } = normalizeToPercent(\n hotspot.x,\n hotspot.y,\n this.imgEl.naturalWidth || 1000,\n this.imgEl.naturalHeight || 1000,\n );\n\n const normalized: NormalizedHotspot = { ...hotspot, x, y };\n this.normalizedHotspots.set(hotspot.id, normalized);\n\n // Create marker\n const pulse = this.config.pulse !== false;\n const marker = createMarker(normalized, pulse);\n this.markers.set(hotspot.id, marker);\n this.markersEl.appendChild(marker);\n\n const triggerMode = hotspot.trigger || this.config.trigger || 'hover';\n\n // navigateTo hotspots: hover shows destination info, click navigates\n if (hotspot.navigateTo) {\n const popoverHotspot = this.enrichNavigateHotspot(hotspot);\n const hasContent = !!(popoverHotspot.data || popoverHotspot.content || this.config.renderPopover);\n\n if (hasContent) {\n const placement = hotspot.placement || this.config.placement || 'top';\n const popover = new Popover(popoverHotspot, {\n placement,\n triggerMode: 'hover',\n renderFn: this.config.renderPopover,\n onOpen: this.config.onOpen,\n onClose: this.config.onClose,\n });\n this.popovers.set(hotspot.id, popover);\n popover.mount(this.containerEl, marker);\n this.bindNavigateTrigger(hotspot, marker, popover);\n } else {\n this.bindNavigateTrigger(hotspot, marker);\n }\n return;\n }\n\n // Create popover for regular hotspots\n const placement = hotspot.placement || this.config.placement || 'top';\n\n const popover = new Popover(hotspot, {\n placement,\n triggerMode,\n renderFn: this.config.renderPopover,\n onOpen: this.config.onOpen,\n onClose: this.config.onClose,\n });\n this.popovers.set(hotspot.id, popover);\n popover.mount(this.containerEl, marker);\n\n // Bind triggers\n this.bindTrigger(hotspot, marker, popover, triggerMode);\n\n // Handle per-hotspot trigger override for 'load'\n if (triggerMode === 'load' && this.imageLoaded) {\n this.closeAll();\n popover.show();\n setMarkerActive(marker, true);\n this.ensureFocusTrap(hotspot.id, popover.element, marker);\n this.focusTraps.get(hotspot.id)?.activate();\n }\n }\n\n /** For navigateTo hotspots without explicit data/content, generate popover content from the destination scene */\n private enrichNavigateHotspot(hotspot: HotspotItem): HotspotItem {\n if (hotspot.data || hotspot.content) return hotspot;\n const destScene = this.scenesMap.get(hotspot.navigateTo!);\n if (!destScene) return hotspot;\n return {\n ...hotspot,\n data: { title: hotspot.label || destScene.alt || destScene.id },\n };\n }\n\n private static readonly NAVIGATE_ARROW_SVG =\n '<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>';\n\n private bindNavigateTrigger(hotspot: HotspotItem, marker: HTMLButtonElement, popover?: Popover): void {\n addClass(marker, 'ci-hotspot-marker--navigate');\n marker.innerHTML = CIHotspot.NAVIGATE_ARROW_SVG;\n if (hotspot.arrowDirection != null) {\n const svg = marker.querySelector('svg');\n if (svg) svg.style.transform = `rotate(${hotspot.arrowDirection}deg)`;\n }\n const sceneLabel = hotspot.label || hotspot.navigateTo!;\n marker.setAttribute('aria-label', `Navigate to ${sceneLabel}`);\n marker.setAttribute('aria-roledescription', 'navigation hotspot');\n\n // Hover: show/hide popover (only if popover exists) + preload target scene\n if (popover) {\n const enterCleanup = addListener(marker, 'mouseenter', () => {\n this.preloadSceneImage(hotspot.navigateTo!);\n popover.clearHideTimer();\n popover.show();\n setMarkerActive(marker, true);\n });\n\n const leaveCleanup = addListener(marker, 'mouseleave', () => {\n popover.scheduleHide(200);\n this.trackedTimeout(() => {\n if (!popover.isVisible()) {\n setMarkerActive(marker, false);\n }\n }, 250);\n });\n\n const focusCleanup = addListener(marker, 'focus', () => {\n this.preloadSceneImage(hotspot.navigateTo!);\n popover.clearHideTimer();\n popover.show();\n setMarkerActive(marker, true);\n });\n\n const blurCleanup = addListener(marker, 'blur', () => {\n popover.scheduleHide(200);\n this.trackedTimeout(() => {\n if (!popover.isVisible()) setMarkerActive(marker, false);\n }, 250);\n });\n\n this.addHotspotCleanups(hotspot.id, enterCleanup, leaveCleanup, focusCleanup, blurCleanup);\n } else {\n // No popover — still preload on hover/focus\n const preloadEnter = addListener(marker, 'mouseenter', () => {\n this.preloadSceneImage(hotspot.navigateTo!);\n });\n const preloadFocus = addListener(marker, 'focus', () => {\n this.preloadSceneImage(hotspot.navigateTo!);\n });\n this.addHotspotCleanups(hotspot.id, preloadEnter, preloadFocus);\n }\n\n // Click: hide popover and navigate\n const clickCleanup = addListener(marker, 'click', (e) => {\n e.stopPropagation();\n popover?.hide();\n setMarkerActive(marker, false);\n this.config.onClick?.(e, hotspot);\n hotspot.onClick?.(e, hotspot);\n this.goToScene(hotspot.navigateTo!);\n });\n\n // Keyboard: Enter/Space navigates (fire onClick for parity with click handler)\n const keyCleanup = addListener(marker, 'keydown', (e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n popover?.hide();\n setMarkerActive(marker, false);\n this.config.onClick?.(e, hotspot);\n hotspot.onClick?.(e, hotspot);\n this.goToScene(hotspot.navigateTo!);\n }\n });\n\n this.addHotspotCleanups(hotspot.id, clickCleanup, keyCleanup);\n }\n\n private bindTrigger(\n hotspot: HotspotItem,\n marker: HTMLButtonElement,\n popover: Popover,\n triggerMode: TriggerMode,\n ): void {\n if (triggerMode === 'hover') {\n this.bindHoverTrigger(hotspot, marker, popover);\n } else if (triggerMode === 'click' || triggerMode === 'load') {\n this.bindClickTrigger(hotspot, marker, popover);\n }\n this.bindKeyboardTrigger(hotspot, marker, popover, triggerMode);\n }\n\n private bindHoverTrigger(hotspot: HotspotItem, marker: HTMLButtonElement, popover: Popover): void {\n const enterCleanup = addListener(marker, 'mouseenter', () => {\n popover.clearHideTimer();\n popover.show();\n setMarkerActive(marker, true);\n });\n\n const leaveCleanup = addListener(marker, 'mouseleave', () => {\n popover.scheduleHide(200);\n this.trackedTimeout(() => {\n if (!popover.isVisible()) setMarkerActive(marker, false);\n }, 250);\n });\n\n this.addHotspotCleanups(hotspot.id, enterCleanup, leaveCleanup);\n }\n\n private bindClickTrigger(hotspot: HotspotItem, marker: HTMLButtonElement, popover: Popover): void {\n const trap = createFocusTrap(popover.element, marker);\n this.focusTraps.set(hotspot.id, trap);\n\n const clickCleanup = addListener(marker, 'click', (e) => {\n e.stopPropagation();\n this.config.onClick?.(e, hotspot);\n hotspot.onClick?.(e, hotspot);\n\n if (popover.isVisible()) {\n popover.hide();\n setMarkerActive(marker, false);\n trap.deactivate();\n } else {\n this.closeAll();\n popover.show();\n setMarkerActive(marker, true);\n trap.activate();\n }\n });\n\n const outsideCleanup = addListener(document, 'click', (e) => {\n if (\n popover.isVisible() &&\n !hotspot.keepOpen &&\n !popover.element.contains(e.target as Node) &&\n !marker.contains(e.target as Node)\n ) {\n popover.hide();\n setMarkerActive(marker, false);\n trap.deactivate();\n }\n });\n\n this.addHotspotCleanups(hotspot.id, clickCleanup, outsideCleanup);\n }\n\n private bindKeyboardTrigger(\n hotspot: HotspotItem,\n marker: HTMLButtonElement,\n popover: Popover,\n triggerMode: TriggerMode,\n ): void {\n const focusCleanup = addListener(marker, 'focus', () => {\n if (triggerMode === 'hover') {\n popover.clearHideTimer();\n popover.show();\n setMarkerActive(marker, true);\n }\n });\n\n const blurCleanup = addListener(marker, 'blur', () => {\n if (triggerMode === 'hover') {\n popover.scheduleHide(200);\n this.trackedTimeout(() => {\n if (!popover.isVisible()) setMarkerActive(marker, false);\n }, 250);\n }\n });\n\n const keyCleanup = addListener(marker, 'keydown', (e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n this.config.onClick?.(e, hotspot);\n if (popover.isVisible()) {\n popover.hide();\n setMarkerActive(marker, false);\n this.focusTraps.get(hotspot.id)?.deactivate();\n } else {\n this.closeAll();\n popover.show();\n setMarkerActive(marker, true);\n this.ensureFocusTrap(hotspot.id, popover.element, marker);\n this.focusTraps.get(hotspot.id)?.activate();\n }\n } else if (e.key === 'Escape') {\n if (popover.isVisible()) {\n popover.hide();\n setMarkerActive(marker, false);\n this.focusTraps.get(hotspot.id)?.deactivate();\n marker.focus();\n }\n }\n });\n\n this.addHotspotCleanups(hotspot.id, focusCleanup, blurCleanup, keyCleanup);\n }\n\n private renormalizePixelCoordinates(): void {\n const naturalWidth = this.imgEl.naturalWidth;\n const naturalHeight = this.imgEl.naturalHeight;\n if (!naturalWidth || !naturalHeight) return;\n\n for (const hotspot of this.config.hotspots) {\n if (typeof hotspot.x === 'number' || typeof hotspot.y === 'number') {\n const { x, y } = normalizeToPercent(hotspot.x, hotspot.y, naturalWidth, naturalHeight);\n const marker = this.markers.get(hotspot.id);\n if (marker) {\n marker.style.left = `${x}%`;\n marker.style.top = `${y}%`;\n }\n const normalized = this.normalizedHotspots.get(hotspot.id);\n if (normalized) {\n normalized.x = x;\n normalized.y = y;\n }\n }\n }\n }\n\n private showLoadTriggerPopovers(): void {\n for (const [id, popover] of this.popovers) {\n const hotspot = this.normalizedHotspots.get(id);\n const triggerMode = hotspot?.trigger || this.config.trigger || 'hover';\n if (triggerMode === 'load' && !popover.isVisible()) {\n popover.show();\n const marker = this.markers.get(id);\n if (marker) {\n setMarkerActive(marker, true);\n this.ensureFocusTrap(id, popover.element, marker);\n this.focusTraps.get(id)?.activate();\n }\n break;\n }\n }\n }\n\n private initZoom(): void {\n if (this.config.scrollHint !== false) {\n this.scrollHint = new ScrollHint(this.containerEl);\n }\n\n this.zoomPan = new ZoomPan(this.viewportEl, this.containerEl, {\n zoomMin: this.config.zoomMin || 1,\n zoomMax: this.config.zoomMax || 4,\n onZoom: (level) => {\n this.config.onZoom?.(level);\n this.zoomControls?.update();\n // Update all popover positions\n for (const [, popover] of this.popovers) {\n if (popover.isVisible()) {\n popover.updatePosition();\n }\n }\n },\n onScrollWithoutZoom: () => this.scrollHint?.show(),\n });\n\n if (this.config.zoomControls !== false) {\n this.zoomControls = createZoomControls(this.containerEl, this.zoomPan, {\n zoomMin: this.config.zoomMin || 1,\n zoomMax: this.config.zoomMax || 4,\n position: this.config.zoomControlsPosition,\n });\n }\n }\n\n private setupResponsive(): void {\n if (typeof ResizeObserver === 'undefined') return;\n\n let rafId = 0;\n this.resizeObserver = new ResizeObserver(() => {\n if (rafId) return;\n rafId = requestAnimationFrame(() => {\n rafId = 0;\n if (this.destroyed) return;\n // Re-sync markers position in fixed-ratio mode\n this.syncMarkersToImage();\n // Check responsive hotspot visibility\n const containerWidth = this.containerEl.offsetWidth;\n for (const [id, hotspot] of this.normalizedHotspots) {\n if (hotspot.responsive) {\n const marker = this.markers.get(id);\n if (!marker) continue;\n const shouldHide =\n (hotspot.responsive.maxWidth && containerWidth > hotspot.responsive.maxWidth) ||\n (hotspot.responsive.minWidth && containerWidth < hotspot.responsive.minWidth);\n setMarkerHidden(marker, !!shouldHide);\n }\n }\n });\n });\n this.cleanups.push(() => { if (rafId) cancelAnimationFrame(rafId); });\n\n this.resizeObserver.observe(this.containerEl);\n this.cleanups.push(() => this.resizeObserver?.disconnect());\n }\n\n /** Position the markers layer to match the rendered image area within a fixed-ratio viewport */\n private syncMarkersToImage(): void {\n if (!this.config.sceneAspectRatio) return;\n\n const vw = this.viewportEl.offsetWidth;\n const vh = this.viewportEl.offsetHeight;\n const nw = this.imgEl.naturalWidth;\n const nh = this.imgEl.naturalHeight;\n if (!vw || !vh || !nw || !nh) return;\n\n const viewportRatio = vw / vh;\n const imageRatio = nw / nh;\n\n let renderedWidth: number;\n let renderedHeight: number;\n let offsetX: number;\n let offsetY: number;\n\n if (imageRatio > viewportRatio) {\n // Image is wider than viewport — fills width, vertical padding\n renderedWidth = vw;\n renderedHeight = vw / imageRatio;\n offsetX = 0;\n offsetY = (vh - renderedHeight) / 2;\n } else {\n // Image is taller than viewport — fills height, horizontal padding\n renderedHeight = vh;\n renderedWidth = vh * imageRatio;\n offsetX = (vw - renderedWidth) / 2;\n offsetY = 0;\n }\n\n this.markersEl.style.left = `${offsetX}px`;\n this.markersEl.style.top = `${offsetY}px`;\n this.markersEl.style.width = `${renderedWidth}px`;\n this.markersEl.style.height = `${renderedHeight}px`;\n // Override inset:0 from base CSS\n this.markersEl.style.right = 'auto';\n this.markersEl.style.bottom = 'auto';\n }\n\n private initScenes(): void {\n for (const scene of this.config.scenes!) {\n this.scenesMap.set(scene.id, scene);\n }\n const initialId = this.config.initialScene || this.config.scenes![0].id;\n const initialScene = this.scenesMap.get(initialId)!;\n this.config.src = initialScene.src;\n this.config.alt = initialScene.alt || '';\n this.config.hotspots = [...initialScene.hotspots];\n this.currentSceneId = initialId;\n }\n\n private initKeyboard(): void {\n this.keyboardHandler = new KeyboardHandler({\n container: this.containerEl,\n getZoomPan: () => this.zoomPan,\n onEscape: () => {\n // If in fullscreen, exit fullscreen first; otherwise close popovers\n if (this.fullscreenControl?.isFullscreen()) {\n this.fullscreenControl.exit();\n return;\n }\n this.closeAll();\n },\n onFullscreenToggle: () => {\n this.fullscreenControl?.toggle();\n },\n });\n }\n\n private initFullscreen(): void {\n if (this.config.fullscreenButton === false) return;\n\n this.fullscreenControl = createFullscreenControl(this.containerEl, {\n onChange: (isFullscreen) => {\n // Reposition visible popovers after the layout shift\n requestAnimationFrame(() => {\n for (const [, popover] of this.popovers) {\n if (popover.isVisible()) {\n popover.updatePosition();\n }\n }\n });\n this.config.onFullscreenChange?.(isFullscreen);\n },\n });\n }\n\n private preloadedScenes = new Set<string>();\n\n private preloadSceneImage(sceneId: string): void {\n if (this.preloadedScenes.has(sceneId)) return;\n const scene = this.scenesMap.get(sceneId);\n if (!scene) return;\n this.preloadedScenes.add(sceneId);\n const img = new Image();\n if (this.config.cloudimage?.token) {\n const containerWidth = this.containerEl.offsetWidth || 300;\n const dpr = typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1;\n img.src = buildCloudimageUrl(scene.src, this.config.cloudimage, containerWidth, 1, dpr);\n } else {\n img.src = scene.src;\n }\n }\n\n private ensureFocusTrap(id: string, popoverEl: HTMLElement, markerEl: HTMLButtonElement): void {\n if (!this.focusTraps.has(id)) {\n this.focusTraps.set(id, createFocusTrap(popoverEl, markerEl));\n }\n }\n\n /** Create a setTimeout that is automatically cleared on destroy */\n private trackedTimeout(fn: () => void, delay: number): void {\n const id = setTimeout(() => {\n this.activeTimers.delete(id);\n fn();\n }, delay);\n this.activeTimers.add(id);\n }\n\n /** Sync current hotspots back to scene override map so navigating away and back preserves changes */\n private syncCurrentSceneHotspots(): void {\n if (!this.currentSceneId || this.isTransitioning) return;\n this.sceneHotspotOverrides.set(this.currentSceneId, [...this.config.hotspots]);\n }\n\n private addHotspotCleanups(id: string, ...fns: (() => void)[]): void {\n let arr = this.hotspotCleanups.get(id);\n if (!arr) {\n arr = [];\n this.hotspotCleanups.set(id, arr);\n }\n arr.push(...fns);\n }\n\n private clearHotspots(): void {\n for (const fns of this.hotspotCleanups.values()) {\n fns.forEach((fn) => fn());\n }\n this.hotspotCleanups.clear();\n\n for (const [, popover] of this.popovers) {\n popover.destroy();\n }\n this.popovers.clear();\n\n for (const [, marker] of this.markers) {\n destroyMarker(marker);\n }\n this.markers.clear();\n this.normalizedHotspots.clear();\n\n for (const [, trap] of this.focusTraps) {\n trap.destroy();\n }\n this.focusTraps.clear();\n }\n\n /** Read scene transition duration from CSS variable (handles both ms and s units) */\n private getSceneTransitionDuration(): number {\n if (typeof getComputedStyle === 'undefined') return 400;\n const val = getComputedStyle(this.containerEl).getPropertyValue('--ci-hotspot-scene-transition-duration').trim();\n const num = parseFloat(val);\n if (isNaN(num)) return 400;\n // If the value ends with 's' but not 'ms', it's in seconds\n if (val.endsWith('s') && !val.endsWith('ms')) return num * 1000;\n return num;\n }\n\n private performSceneTransition(\n scene: Scene,\n transition: SceneTransition,\n slideReverse: boolean,\n onComplete: () => void,\n ): void {\n if (transition === 'none') {\n this.clearHotspots();\n this.switchToScene(scene);\n onComplete();\n return;\n }\n\n const transitionDuration = this.getSceneTransitionDuration();\n\n addClass(this.containerEl, 'ci-hotspot-scene-transitioning');\n\n const incomingImg = createElement('img', 'ci-hotspot-scene-incoming', {\n alt: scene.alt || '',\n draggable: 'false',\n }) as HTMLImageElement;\n\n if (this.config.cloudimage?.token) {\n const containerWidth = this.containerEl.offsetWidth || 300;\n const dpr = typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1;\n incomingImg.src = buildCloudimageUrl(scene.src, this.config.cloudimage, containerWidth, 1, dpr);\n } else {\n incomingImg.src = scene.src;\n }\n\n const doTransition = () => {\n if (this.destroyed) return;\n\n if (transition === 'fade') {\n addClass(incomingImg, 'ci-hotspot-scene-fade-in');\n addClass(this.imgEl, 'ci-hotspot-scene-fade-out');\n } else if (transition === 'slide') {\n const suffix = slideReverse ? '-reverse' : '';\n addClass(incomingImg, `ci-hotspot-scene-slide-in${suffix}`);\n addClass(this.imgEl, `ci-hotspot-scene-slide-out${suffix}`);\n }\n\n this.viewportEl.insertBefore(incomingImg, this.markersEl);\n\n this.transitionTimer = setTimeout(() => {\n this.transitionTimer = undefined;\n if (this.destroyed) return;\n\n this.clearHotspots();\n this.switchToScene(scene);\n\n const finish = () => {\n incomingImg.remove();\n removeClass(this.imgEl, 'ci-hotspot-scene-fade-out');\n removeClass(this.imgEl, 'ci-hotspot-scene-slide-out');\n removeClass(this.imgEl, 'ci-hotspot-scene-slide-out-reverse');\n removeClass(this.containerEl, 'ci-hotspot-scene-transitioning');\n onComplete();\n };\n\n if (this.imgEl.complete && this.imgEl.naturalWidth > 0) {\n finish();\n } else {\n this.imgEl.addEventListener('load', finish, { once: true });\n this.imgEl.addEventListener('error', finish, { once: true });\n }\n }, transitionDuration);\n };\n\n if (incomingImg.complete) {\n doTransition();\n } else {\n addClass(this.containerEl, 'ci-hotspot-scene-loading');\n incomingImg.onload = () => {\n removeClass(this.containerEl, 'ci-hotspot-scene-loading');\n doTransition();\n };\n incomingImg.onerror = () => {\n if (this.destroyed) return;\n removeClass(this.containerEl, 'ci-hotspot-scene-loading');\n incomingImg.remove();\n removeClass(this.containerEl, 'ci-hotspot-scene-transitioning');\n this.clearHotspots();\n this.switchToScene(scene);\n onComplete();\n };\n }\n }\n\n private switchToScene(scene: Scene): void {\n this.config.src = scene.src;\n this.config.alt = scene.alt || '';\n this.config.hotspots = this.sceneHotspotOverrides.get(scene.id) ?? [...scene.hotspots];\n\n this.imgEl.alt = scene.alt || '';\n\n this.containerEl.setAttribute('aria-label', scene.alt || 'Image with hotspots');\n\n this.imageLoaded = false;\n const onLoad = () => {\n this.imageLoaded = true;\n this.renormalizePixelCoordinates();\n this.syncMarkersToImage();\n this.showLoadTriggerPopovers();\n };\n // Attach listener BEFORE setting src to avoid missing synchronous cache loads\n this.imgEl.addEventListener('load', onLoad, { once: true });\n\n if (this.config.cloudimage?.token) {\n const containerWidth = this.containerEl.offsetWidth || 300;\n const dpr = typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1;\n this.imgEl.src = buildCloudimageUrl(scene.src, this.config.cloudimage, containerWidth, 1, dpr);\n } else {\n this.imgEl.src = scene.src;\n }\n\n // If image loaded synchronously from cache, the listener may have already fired.\n // If not, trigger the handler manually.\n if (this.imgEl.complete && this.imgEl.naturalWidth > 0 && !this.imageLoaded) {\n this.imgEl.removeEventListener('load', onLoad);\n onLoad();\n }\n\n this.initHotspots();\n }\n\n // === Public API ===\n\n /** Get references to the internal DOM elements */\n getElements(): {\n container: HTMLElement;\n viewport: HTMLElement;\n image: HTMLImageElement;\n markers: HTMLElement;\n } {\n return {\n container: this.containerEl,\n viewport: this.viewportEl,\n image: this.imgEl,\n markers: this.markersEl,\n };\n }\n\n open(id: string): void {\n if (this.destroyed) return;\n const popover = this.popovers.get(id);\n const marker = this.markers.get(id);\n if (!popover || !marker) return;\n if (popover.isVisible()) return;\n this.closeAll();\n popover.show();\n setMarkerActive(marker, true);\n this.focusTraps.get(id)?.activate();\n }\n\n close(id: string): void {\n if (this.destroyed) return;\n const popover = this.popovers.get(id);\n const marker = this.markers.get(id);\n if (popover && marker) {\n popover.hide();\n setMarkerActive(marker, false);\n this.focusTraps.get(id)?.deactivate();\n }\n }\n\n closeAll(): void {\n if (this.destroyed) return;\n for (const [id, popover] of this.popovers) {\n if (popover.isVisible()) {\n popover.hide();\n const marker = this.markers.get(id);\n if (marker) setMarkerActive(marker, false);\n this.focusTraps.get(id)?.deactivate();\n }\n }\n }\n\n setZoom(level: number): void {\n if (this.destroyed) return;\n this.zoomPan?.setZoom(level);\n }\n\n getZoom(): number {\n return this.zoomPan?.getZoom() || 1;\n }\n\n resetZoom(): void {\n if (this.destroyed) return;\n this.zoomPan?.resetZoom();\n }\n\n goToScene(sceneId: string): void {\n if (this.destroyed) return;\n if (this.isTransitioning) return;\n if (!this.scenesMap.size) return;\n if (sceneId === this.currentSceneId) return;\n\n const scene = this.scenesMap.get(sceneId);\n if (!scene) return;\n\n const transition = this.config.sceneTransition || 'fade';\n this.isTransitioning = true;\n\n if (this.zoomPan && this.zoomPan.getZoom() > 1) {\n this.zoomPan.resetZoom();\n }\n\n // Determine slide direction from the triggering hotspot's position\n let slideReverse = false;\n if (transition === 'slide') {\n for (const hotspot of this.config.hotspots) {\n if (hotspot.navigateTo === sceneId) {\n const normalized = this.normalizedHotspots.get(hotspot.id);\n if (normalized && normalized.x <= 50) {\n slideReverse = true;\n }\n break;\n }\n }\n }\n\n // Sync old scene's hotspots before updating currentSceneId\n this.syncCurrentSceneHotspots();\n this.currentSceneId = sceneId;\n\n this.performSceneTransition(scene, transition, slideReverse, () => {\n this.isTransitioning = false;\n\n announceToScreenReader(`Navigated to ${scene.alt || sceneId}`);\n this.config.onSceneChange?.(sceneId, scene);\n\n // Focus first marker if keyboard user\n const firstHotspot = scene.hotspots[0];\n if (firstHotspot) {\n const firstMarker = this.markers.get(firstHotspot.id);\n if (firstMarker && document.activeElement && this.containerEl.contains(document.activeElement)) {\n firstMarker.focus();\n }\n }\n });\n }\n\n getCurrentScene(): string | undefined {\n return this.currentSceneId;\n }\n\n getScenes(): string[] {\n return Array.from(this.scenesMap.keys());\n }\n\n enterFullscreen(): void {\n if (this.destroyed) return;\n this.fullscreenControl?.enter();\n }\n\n exitFullscreen(): void {\n if (this.destroyed) return;\n this.fullscreenControl?.exit();\n }\n\n isFullscreen(): boolean {\n return this.fullscreenControl?.isFullscreen() ?? false;\n }\n\n addHotspot(hotspot: HotspotItem): void {\n if (this.destroyed) return;\n this.config.hotspots.push(hotspot);\n this.addHotspotInternal(hotspot);\n this.syncCurrentSceneHotspots();\n }\n\n removeHotspot(id: string): void {\n if (this.destroyed) return;\n\n // Clean up per-hotspot listeners\n const fns = this.hotspotCleanups.get(id);\n if (fns) {\n fns.forEach((fn) => fn());\n this.hotspotCleanups.delete(id);\n }\n\n // Clean up focus trap\n const trap = this.focusTraps.get(id);\n if (trap) {\n trap.destroy();\n this.focusTraps.delete(id);\n }\n\n const marker = this.markers.get(id);\n const popover = this.popovers.get(id);\n\n if (popover) {\n popover.destroy();\n this.popovers.delete(id);\n }\n if (marker) {\n destroyMarker(marker);\n this.markers.delete(id);\n }\n this.normalizedHotspots.delete(id);\n this.config.hotspots = this.config.hotspots.filter((h) => h.id !== id);\n this.syncCurrentSceneHotspots();\n }\n\n updateHotspot(id: string, updates: Partial<HotspotItem>): void {\n if (this.destroyed) return;\n const idx = this.config.hotspots.findIndex((h) => h.id === id);\n if (idx === -1) return;\n\n // Track marker's DOM position before removal\n const oldMarker = this.markers.get(id);\n const nextSibling = oldMarker?.nextElementSibling || null;\n\n // Remove and re-add with updated config\n const current = this.config.hotspots[idx];\n const updated = { ...current, ...updates };\n this.removeHotspot(id);\n this.config.hotspots.splice(idx, 0, updated);\n this.addHotspotInternal(updated);\n\n // Restore DOM order\n const newMarker = this.markers.get(id);\n if (newMarker && nextSibling && this.markersEl.contains(nextSibling)) {\n this.markersEl.insertBefore(newMarker, nextSibling);\n }\n this.syncCurrentSceneHotspots();\n }\n\n update(config: Partial<CIHotspotConfig>): void {\n if (this.destroyed) return;\n // Destroy current state and rebuild\n this.destroyInternal();\n acquireLiveRegion();\n this.config = mergeConfig({ ...this.config, ...config });\n validateConfig(this.config);\n\n if (this.config.scenes && this.config.scenes.length > 0) {\n this.initScenes();\n }\n\n this.buildDOM();\n this.applyTheme();\n this.setupImage();\n this.initHotspots();\n if (this.config.zoom) {\n this.initZoom();\n }\n this.initKeyboard();\n this.initFullscreen();\n this.setupResponsive();\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n this.destroyInternal();\n this.rootEl.innerHTML = '';\n }\n\n private destroyInternal(): void {\n this.imageLoaded = false;\n\n // Clear all tracked timers\n for (const id of this.activeTimers) clearTimeout(id);\n this.activeTimers.clear();\n\n // Run all per-hotspot cleanup functions\n for (const fns of this.hotspotCleanups.values()) {\n fns.forEach((fn) => fn());\n }\n this.hotspotCleanups.clear();\n this.cleanups.forEach((fn) => fn());\n this.cleanups = [];\n\n // Destroy popovers\n for (const [, popover] of this.popovers) {\n popover.destroy();\n }\n this.popovers.clear();\n\n // Destroy markers\n for (const [, marker] of this.markers) {\n destroyMarker(marker);\n }\n this.markers.clear();\n this.normalizedHotspots.clear();\n\n // Destroy focus traps\n for (const [, trap] of this.focusTraps) {\n trap.destroy();\n }\n this.focusTraps.clear();\n\n // Clear scenes state\n this.scenesMap.clear();\n this.preloadedScenes.clear();\n this.sceneHotspotOverrides.clear();\n this.currentSceneId = undefined;\n this.isTransitioning = false;\n if (this.transitionTimer !== undefined) {\n clearTimeout(this.transitionTimer);\n this.transitionTimer = undefined;\n }\n\n // Destroy fullscreen control\n this.fullscreenControl?.destroy();\n this.fullscreenControl = null;\n\n // Destroy keyboard handler\n this.keyboardHandler?.destroy();\n this.keyboardHandler = null;\n\n // Destroy zoom\n this.zoomPan?.destroy();\n this.zoomPan = null;\n this.zoomControls?.destroy();\n this.zoomControls = null;\n this.scrollHint?.destroy();\n this.scrollHint = null;\n\n // Destroy cloudimage handler\n this.cloudimageHandler?.destroy();\n this.cloudimageHandler = null;\n\n // Destroy resize observer\n this.resizeObserver?.disconnect();\n this.resizeObserver = null;\n\n // Release shared live region\n releaseLiveRegion();\n }\n}\n"],"names":["DEFAULT_CONFIG","DATA_ATTR_MAP","parseDataAttributes","element","config","cloudimage","attr","mapping","value","parsed","coerceValue","type","num","mergeConfig","userConfig","validateConfig","sceneIds","scene","hotspot","STYLE_ID","isBrowser","getElement","el","found","createElement","tag","className","attrs","key","addClass","classNames","removeClass","injectStyles","css","style","parseCoordinate","trimmed","normalizeToPercent","x","y","naturalWidth","naturalHeight","px","py","addListener","event","handler","options","createMarker","pulse","marker","classes","setMarkerIcon","sanitizeSVG","svg","root","cleanSVGNode","SVG_BLOCKED_ELEMENTS","node","name","child","icon","img","iconEl","setMarkerActive","active","setMarkerHidden","hidden","destroyMarker","computePosition","markerEl","popoverEl","containerEl","markerRect","containerRect","popoverWidth","popoverHeight","markerCenterX","markerCenterY","markerTop","markerBottom","markerLeft","markerRight","containerWidth","containerHeight","gap","space","placement","getAutoPlacement","flip","arrowOffset","shifted","shift","max","p","width","height","sx","sy","ALLOWED_TAGS","ALLOWED_ATTRS","SAFE_HREF_PATTERN","SAFE_SRC_PATTERN","SAFE_REL_VALUES","sanitizeHTML","html","body","sanitizeNode","children","tagName","safeTokens","t","renderBuiltInTemplate","data","parts","escapeAttr","bodyParts","escapeHtml","isSafeUrl","ctaText","renderPopoverContent","renderFn","str","url","normalized","Popover","isDialog","content","onEnter","onLeave","_b","_a","delay","result","offset","arrow","fn","_c","GestureRecognizer","callbacks","getZoom","onTouchStart","e","onTouchMove","onTouchEnd","currentDistance","scale","centerX","centerY","_d","totalDx","totalDy","_f","_e","now","touch","touches","dx","dy","ZoomPan","viewport","container","onWheel","rect","originX","originY","deltaY","delta","onDblClick","onMouseDown","onMouseMove","onMouseUp","cx","cy","onGestureStart","onGestureChange","ge","ev","onGestureEnd","level","oldZoom","scaleChange","ox","oy","maxPanX","maxPanY","createZoomControls","zoomPan","step","controls","btnIn","btnOut","btnReset","updateState","zoom","isMac","HINT_TEXT","HIDE_DELAY","ScrollHint","parent","DEFAULT_DOMAIN","DEFAULT_API_VERSION","DEFAULT_LIMIT_FACTOR","roundToLimitFactor","limitFactor","getOptimalWidth","dpr","zoomLevel","raw","buildCloudimageUrl","src","domain","apiVersion","encodedSrc","createResizeHandler","getZoomLevel","lastRequestedWidth","observer","entries","entry","newWidth","PAN_STEP","ZOOM_STEP","KeyboardHandler","getZoomPan","onEscape","onFullscreenToggle","cleanup","target","FOCUSABLE_SELECTOR","getFocusableElements","createFocusTrap","popover","returnFocusTo","activate","focusable","currentFocusable","first","last","deactivate","destroy","liveRegion","liveRegionRefCount","announceToScreenReader","message","acquireLiveRegion","releaseLiveRegion","MAXIMIZE_SVG","MINIMIZE_SVG","isFullscreenEnabled","getFullscreenElement","requestFullscreen","exitFullscreen","createFullscreenControl","btn","cleanups","isActive","syncState","fs","toggle","enter","exit","changeCleanup","clickCleanup","_CIHotspot","cssText","elements","instances","onLoad","oldMarker","fns","trap","triggerMode","popoverHotspot","destScene","sceneLabel","enterCleanup","leaveCleanup","focusCleanup","blurCleanup","preloadEnter","preloadFocus","keyCleanup","outsideCleanup","id","rafId","shouldHide","vw","vh","nw","nh","viewportRatio","imageRatio","renderedWidth","renderedHeight","offsetX","offsetY","initialId","initialScene","isFullscreen","sceneId","arr","val","transition","slideReverse","onComplete","transitionDuration","incomingImg","doTransition","suffix","finish","firstHotspot","firstMarker","h","updates","idx","nextSibling","updated","newMarker","_g","CIHotspot"],"mappings":"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"}
|