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-editor.cjs.js","sources":["../../src/utils/dom.ts","../../src/utils/events.ts","../../src/core/config.ts","../../src/utils/coordinates.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","../../src/editor/editor-toolbar.ts","../../src/editor/selection-manager.ts","../../src/editor/property-panel.ts","../../src/editor/drag-manager.ts","../../src/editor/undo-manager.ts","../../src/editor/ci-hotspot-editor.ts"],"sourcesContent":["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","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 { 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","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","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","import { createElement } from '../utils/dom';\nimport { createFocusTrap } from '../a11y/focus';\nimport type { CIHotspotEditor } from './ci-hotspot-editor';\n\nconst ICONS = {\n cursor: '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12.586 12.586 19 19\"/><path d=\"M3.688 3.037a.497.497 0 0 0-.651.651l6.5 15.999a.501.501 0 0 0 .947-.062l1.569-6.083a2 2 0 0 1 1.448-1.479l6.124-1.579a.5.5 0 0 0 .063-.947z\"/></svg>',\n plus: '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M5 12h14\"/><path d=\"M12 5v14\"/></svg>',\n undo: '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M9 14 4 9l5-5\"/><path d=\"M4 9h10.5a5.5 5.5 0 0 1 5.5 5.5a5.5 5.5 0 0 1-5.5 5.5H11\"/></svg>',\n redo: '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m15 14 5-5-5-5\"/><path d=\"M20 9H9.5A5.5 5.5 0 0 0 4 14.5A5.5 5.5 0 0 0 9.5 20H13\"/></svg>',\n trash: '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M10 11v6\"/><path d=\"M14 11v6\"/><path d=\"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6\"/><path d=\"M3 6h18\"/><path d=\"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\"/></svg>',\n download: '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 15V3\"/><path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"/><path d=\"m7 10 5 5 5-5\"/></svg>',\n upload: '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 3v12\"/><path d=\"m17 8-5-5-5 5\"/><path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"/></svg>',\n copy: '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg>',\n image: '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect width=\"18\" height=\"18\" x=\"3\" y=\"3\" rx=\"2\" ry=\"2\"/><circle cx=\"9\" cy=\"9\" r=\"2\"/><path d=\"m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21\"/></svg>',\n};\n\nexport class EditorToolbar {\n private toolbarEl: HTMLElement;\n private urlBarEl: HTMLElement;\n private selectBtn!: HTMLButtonElement;\n private addBtn!: HTMLButtonElement;\n private undoBtn!: HTMLButtonElement;\n private redoBtn!: HTMLButtonElement;\n private deleteBtn!: HTMLButtonElement;\n private exportBtn!: HTMLButtonElement;\n private importBtn!: HTMLButtonElement;\n private copyBtn!: HTMLButtonElement;\n\n constructor(\n private parentEl: HTMLElement,\n private editor: CIHotspotEditor,\n ) {\n this.toolbarEl = createElement('div', 'ci-editor-toolbar');\n this.urlBarEl = createElement('div', 'ci-editor-url-bar');\n this.build();\n // Insert toolbar and URL bar before the body\n const firstChild = this.parentEl.firstChild;\n this.parentEl.insertBefore(this.toolbarEl, firstChild);\n this.parentEl.insertBefore(this.urlBarEl, firstChild);\n }\n\n private build(): void {\n // Mode buttons\n const modeGroup = createElement('div', 'ci-editor-toolbar-group');\n this.selectBtn = this.createBtn(ICONS.cursor, 'Select', () => this.editor.setMode('select'));\n this.addBtn = this.createBtn(ICONS.plus, 'Add', () => this.editor.setMode('add'));\n modeGroup.appendChild(this.selectBtn);\n modeGroup.appendChild(this.addBtn);\n this.toolbarEl.appendChild(modeGroup);\n\n this.toolbarEl.appendChild(this.createSeparator());\n\n // History buttons\n const historyGroup = createElement('div', 'ci-editor-toolbar-group');\n this.undoBtn = this.createBtn(ICONS.undo, 'Undo', () => this.editor.getUndoManager().undo());\n this.redoBtn = this.createBtn(ICONS.redo, 'Redo', () => this.editor.getUndoManager().redo());\n this.deleteBtn = this.createBtn(ICONS.trash, 'Delete', () => {\n const id = this.editor.getSelection().getSelectedId();\n if (id) this.editor.removeHotspot(id);\n });\n historyGroup.appendChild(this.undoBtn);\n historyGroup.appendChild(this.redoBtn);\n historyGroup.appendChild(this.deleteBtn);\n this.toolbarEl.appendChild(historyGroup);\n\n this.toolbarEl.appendChild(this.createSeparator());\n\n // Import/Export buttons\n const ioGroup = createElement('div', 'ci-editor-toolbar-group');\n this.exportBtn = this.createBtn(ICONS.download, 'Export', () => this.showExportJSON());\n this.importBtn = this.createBtn(ICONS.upload, 'Import', () => this.showImportModal());\n this.copyBtn = this.createBtn(ICONS.copy, 'Copy JSON', () => this.copyJSON());\n ioGroup.appendChild(this.exportBtn);\n ioGroup.appendChild(this.importBtn);\n ioGroup.appendChild(this.copyBtn);\n this.toolbarEl.appendChild(ioGroup);\n\n // Image URL bar (separate row)\n const urlIcon = createElement('span', 'ci-editor-btn-icon');\n urlIcon.innerHTML = ICONS.image;\n const urlInput = createElement('input', 'ci-editor-toolbar-url') as HTMLInputElement;\n urlInput.type = 'url';\n urlInput.placeholder = 'Image URL...';\n urlInput.value = this.editor.getSrc();\n const loadBtn = this.createBtn('', 'Load', () => this.loadImageUrl(urlInput));\n urlInput.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n this.loadImageUrl(urlInput);\n }\n });\n this.urlBarEl.appendChild(urlIcon);\n this.urlBarEl.appendChild(urlInput);\n this.urlBarEl.appendChild(loadBtn);\n\n this.updateState();\n }\n\n private createBtn(icon: string, label: string, onClick: () => void): HTMLButtonElement {\n const btn = createElement('button', 'ci-editor-btn');\n btn.innerHTML = icon;\n btn.appendChild(document.createTextNode(` ${label}`));\n btn.title = label;\n btn.setAttribute('aria-label', label);\n btn.addEventListener('click', (e) => {\n e.stopPropagation();\n onClick();\n });\n return btn;\n }\n\n private createSeparator(): HTMLElement {\n return createElement('div', 'ci-editor-toolbar-separator');\n }\n\n updateState(): void {\n const mode = this.editor.getMode();\n this.selectBtn.classList.toggle('ci-editor-btn--active', mode === 'select');\n this.addBtn.classList.toggle('ci-editor-btn--active', mode === 'add');\n this.selectBtn.setAttribute('aria-pressed', String(mode === 'select'));\n this.addBtn.setAttribute('aria-pressed', String(mode === 'add'));\n\n const undo = this.editor.getUndoManager();\n this.undoBtn.disabled = !undo.canUndo();\n this.redoBtn.disabled = !undo.canRedo();\n\n this.deleteBtn.disabled = !this.editor.getSelection().getSelectedId();\n }\n\n private showExportJSON(): void {\n const json = this.editor.exportJSON();\n const blob = new Blob([json], { type: 'application/json' });\n const url = URL.createObjectURL(blob);\n const a = createElement('a');\n a.href = url;\n a.download = 'hotspots.json';\n a.click();\n URL.revokeObjectURL(url);\n this.editor.showToast('Exported hotspots.json');\n }\n\n private async copyJSON(): Promise<void> {\n try {\n await navigator.clipboard.writeText(this.editor.exportJSON());\n this.editor.showToast('Copied JSON to clipboard');\n } catch {\n this.editor.showToast('Failed to copy');\n }\n }\n\n private showImportModal(): void {\n const overlay = createElement('div', 'ci-editor-modal-overlay');\n const modal = createElement('div', 'ci-editor-modal');\n modal.setAttribute('role', 'dialog');\n modal.setAttribute('aria-modal', 'true');\n\n const titleId = 'ci-editor-import-title';\n modal.setAttribute('aria-labelledby', titleId);\n\n const title = createElement('h3');\n title.id = titleId;\n title.textContent = 'Import Hotspots JSON';\n\n const textarea = createElement('textarea');\n textarea.placeholder = '[\\n { \"id\": \"h1\", \"x\": \"50%\", \"y\": \"50%\", \"label\": \"My Hotspot\" }\\n]';\n\n const errorEl = createElement('div', 'ci-editor-modal-error');\n errorEl.style.display = 'none';\n\n const actions = createElement('div', 'ci-editor-modal-actions');\n const cancelBtn = createElement('button', 'ci-editor-btn');\n cancelBtn.textContent = 'Cancel';\n const importBtn = createElement('button', 'ci-editor-btn ci-editor-btn--primary');\n importBtn.textContent = 'Import';\n\n actions.appendChild(cancelBtn);\n actions.appendChild(importBtn);\n\n modal.appendChild(title);\n modal.appendChild(textarea);\n modal.appendChild(errorEl);\n modal.appendChild(actions);\n overlay.appendChild(modal);\n document.body.appendChild(overlay);\n\n const trap = createFocusTrap(modal, this.importBtn);\n const close = () => {\n trap.destroy();\n overlay.remove();\n };\n\n cancelBtn.addEventListener('click', close);\n overlay.addEventListener('click', (e) => {\n if (e.target === overlay) close();\n });\n overlay.addEventListener('keydown', (e) => {\n if (e.key === 'Escape') close();\n });\n\n importBtn.addEventListener('click', () => {\n try {\n this.editor.importJSON(textarea.value);\n close();\n this.editor.showToast('Imported hotspots');\n } catch (err) {\n errorEl.style.display = 'block';\n errorEl.textContent = err instanceof Error ? err.message : 'Invalid JSON';\n }\n });\n\n textarea.focus();\n trap.activate();\n }\n\n private loadImageUrl(input: HTMLInputElement): void {\n const src = input.value.trim();\n if (!src) return;\n this.editor.setSrc(src);\n this.editor.showToast('Image updated');\n }\n\n destroy(): void {\n this.toolbarEl.remove();\n this.urlBarEl.remove();\n }\n}\n","import { addListener } from '../utils/events';\nimport type { CIHotspotEditor } from './ci-hotspot-editor';\n\nexport class SelectionManager {\n private selectedId: string | null = null;\n private cleanups: (() => void)[] = [];\n\n constructor(private editor: CIHotspotEditor) {\n this.bindCanvasClicks();\n }\n\n private bindCanvasClicks(): void {\n const cleanup = addListener(this.editor.getCanvasEl(), 'click', (e) => {\n if (this.editor.getMode() !== 'select') return;\n\n const target = e.target as HTMLElement;\n const markerEl = target.closest<HTMLElement>('[data-hotspot-id]');\n\n if (markerEl) {\n e.stopPropagation();\n const id = markerEl.getAttribute('data-hotspot-id');\n if (id) this.select(id);\n } else {\n // Clicked on canvas background — deselect\n const isToolbar = target.closest('.ci-editor-toolbar');\n const isSidebar = target.closest('.ci-editor-sidebar');\n if (!isToolbar && !isSidebar) {\n this.deselect();\n }\n }\n });\n this.cleanups.push(cleanup);\n }\n\n select(id: string): void {\n if (this.selectedId === id) return;\n this.deselect();\n this.selectedId = id;\n this.applySelectedClass(id, true);\n this.editor.events.emit('hotspot:select', id);\n this.editor.getToolbar().updateState();\n // Property panel updates via editor notifyChange or explicit refresh\n }\n\n deselect(): void {\n if (!this.selectedId) return;\n const prevId = this.selectedId;\n this.applySelectedClass(prevId, false);\n this.selectedId = null;\n this.editor.events.emit('hotspot:deselect', prevId);\n this.editor.getToolbar().updateState();\n }\n\n getSelectedId(): string | null {\n return this.selectedId;\n }\n\n refreshMarkerVisuals(): void {\n if (!this.selectedId) return;\n this.applySelectedClass(this.selectedId, true);\n }\n\n private applySelectedClass(id: string, add: boolean): void {\n const canvas = this.editor.getCanvasEl();\n const marker = canvas.querySelector<HTMLElement>(`[data-hotspot-id=\"${id}\"]`);\n if (marker) {\n marker.classList.toggle('ci-hotspot-marker--editor-selected', add);\n }\n }\n\n destroy(): void {\n this.cleanups.forEach((fn) => fn());\n this.cleanups = [];\n }\n}\n","import type { HotspotItem } from '../core/types';\nimport { createElement } from '../utils/dom';\nimport type { CIHotspotEditor } from './ci-hotspot-editor';\n\nexport class PropertyPanel {\n private panelEl: HTMLElement;\n\n constructor(\n private parentEl: HTMLElement,\n private editor: CIHotspotEditor,\n ) {\n this.panelEl = createElement('div', 'ci-editor-panel');\n this.parentEl.appendChild(this.panelEl);\n\n // Listen for selection changes\n this.editor.events.on('hotspot:select', () => this.refresh());\n this.editor.events.on('hotspot:deselect', () => this.refresh());\n\n this.refresh();\n }\n\n refresh(): void {\n const selectedId = this.editor.getSelection().getSelectedId();\n\n if (!selectedId) {\n this.renderHotspotList();\n return;\n }\n\n const hotspot = this.editor.getHotspot(selectedId);\n if (!hotspot) {\n this.renderHotspotList();\n return;\n }\n\n this.renderForm(hotspot);\n }\n\n private renderHotspotList(): void {\n this.panelEl.innerHTML = '';\n\n const title = createElement('div', 'ci-editor-panel-title');\n title.textContent = 'Hotspots';\n this.panelEl.appendChild(title);\n\n const hotspots = this.editor.getHotspots();\n\n if (hotspots.length === 0) {\n const empty = createElement('div', 'ci-editor-panel-empty');\n empty.textContent = 'No hotspots yet. Use the Add tool to place hotspots on the image.';\n this.panelEl.appendChild(empty);\n return;\n }\n\n const list = createElement('ul', 'ci-editor-hotspot-list');\n list.setAttribute('role', 'listbox');\n list.setAttribute('aria-label', 'Hotspot list');\n for (const h of hotspots) {\n const item = createElement('li', 'ci-editor-hotspot-item');\n item.setAttribute('data-list-id', h.id);\n item.setAttribute('role', 'option');\n item.setAttribute('tabindex', '0');\n\n const label = createElement('span', 'ci-editor-hotspot-item-label');\n label.textContent = h.label || h.id;\n\n const coords = createElement('span', 'ci-editor-hotspot-item-coords');\n coords.textContent = `${this.fmtCoord(h.x)}, ${this.fmtCoord(h.y)}`;\n\n item.appendChild(label);\n item.appendChild(coords);\n\n const selectHotspot = () => this.editor.getSelection().select(h.id);\n item.addEventListener('click', selectHotspot);\n item.addEventListener('keydown', (e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n selectHotspot();\n }\n });\n\n list.appendChild(item);\n }\n this.panelEl.appendChild(list);\n }\n\n private renderForm(hotspot: HotspotItem): void {\n this.panelEl.innerHTML = '';\n this.fieldCounter = 0;\n\n const title = createElement('div', 'ci-editor-panel-title');\n title.textContent = `Edit: ${hotspot.label || hotspot.id}`;\n this.panelEl.appendChild(title);\n\n // Label\n this.panelEl.appendChild(\n this.createTextField('Label', hotspot.label || '', (val) => {\n this.editor.updateHotspot(hotspot.id, { label: val });\n }),\n );\n\n // Coordinates\n const coordRow = createElement('div', 'ci-editor-field-row');\n coordRow.appendChild(\n this.createTextField('X', String(hotspot.x), (val) => {\n this.editor.updateHotspot(hotspot.id, { x: val });\n }),\n );\n coordRow.appendChild(\n this.createTextField('Y', String(hotspot.y), (val) => {\n this.editor.updateHotspot(hotspot.id, { y: val });\n }),\n );\n this.panelEl.appendChild(coordRow);\n\n // Trigger\n this.panelEl.appendChild(\n this.createSelect('Trigger', hotspot.trigger || 'click', ['hover', 'click', 'load'], (val) => {\n this.editor.updateHotspot(hotspot.id, { trigger: val as HotspotItem['trigger'] });\n }),\n );\n\n // Placement\n this.panelEl.appendChild(\n this.createSelect(\n 'Placement',\n hotspot.placement || 'top',\n ['top', 'bottom', 'left', 'right', 'auto'],\n (val) => {\n this.editor.updateHotspot(hotspot.id, { placement: val as HotspotItem['placement'] });\n },\n ),\n );\n\n // Data fields\n const data = hotspot.data || {};\n\n this.panelEl.appendChild(\n this.createTextField('Title', data.title || '', (val) => {\n this.editor.updateHotspot(hotspot.id, {\n data: { ...hotspot.data, title: val },\n });\n }),\n );\n\n this.panelEl.appendChild(\n this.createTextField('Price', data.price || '', (val) => {\n this.editor.updateHotspot(hotspot.id, {\n data: { ...hotspot.data, price: val },\n });\n }),\n );\n\n this.panelEl.appendChild(\n this.createTextArea('Description', data.description || '', (val) => {\n this.editor.updateHotspot(hotspot.id, {\n data: { ...hotspot.data, description: val },\n });\n }),\n );\n\n this.panelEl.appendChild(\n this.createTextField('Image URL', data.image || '', (val) => {\n this.editor.updateHotspot(hotspot.id, {\n data: { ...hotspot.data, image: val },\n });\n }),\n );\n\n this.panelEl.appendChild(\n this.createTextField('Link URL', data.url || '', (val) => {\n this.editor.updateHotspot(hotspot.id, {\n data: { ...hotspot.data, url: val },\n });\n }),\n );\n\n this.panelEl.appendChild(\n this.createTextField('CTA Text', data.ctaText || '', (val) => {\n this.editor.updateHotspot(hotspot.id, {\n data: { ...hotspot.data, ctaText: val },\n });\n }),\n );\n\n // Actions\n const actions = createElement('div', 'ci-editor-panel-actions');\n const deleteBtn = createElement('button', 'ci-editor-btn ci-editor-btn--danger');\n deleteBtn.textContent = 'Delete Hotspot';\n deleteBtn.addEventListener('click', () => this.editor.removeHotspot(hotspot.id));\n\n const backBtn = createElement('button', 'ci-editor-btn');\n backBtn.textContent = 'Back to List';\n backBtn.addEventListener('click', () => this.editor.getSelection().deselect());\n\n actions.appendChild(backBtn);\n actions.appendChild(deleteBtn);\n this.panelEl.appendChild(actions);\n }\n\n // === Field Builders ===\n\n private fieldCounter = 0;\n\n private nextFieldId(label: string): string {\n return `ci-editor-field-${label.toLowerCase().replace(/\\s+/g, '-')}-${++this.fieldCounter}`;\n }\n\n private createTextField(\n label: string,\n value: string,\n onChange: (val: string) => void,\n ): HTMLElement {\n const id = this.nextFieldId(label);\n const field = createElement('div', 'ci-editor-field');\n const labelEl = createElement('label', '', { for: id });\n labelEl.textContent = label;\n const input = createElement('input', '', { id });\n input.type = 'text';\n input.value = value;\n input.addEventListener('change', () => onChange(input.value));\n field.appendChild(labelEl);\n field.appendChild(input);\n return field;\n }\n\n private createTextArea(\n label: string,\n value: string,\n onChange: (val: string) => void,\n ): HTMLElement {\n const id = this.nextFieldId(label);\n const field = createElement('div', 'ci-editor-field');\n const labelEl = createElement('label', '', { for: id });\n labelEl.textContent = label;\n const textarea = createElement('textarea', '', { id });\n textarea.value = value;\n textarea.addEventListener('change', () => onChange(textarea.value));\n field.appendChild(labelEl);\n field.appendChild(textarea);\n return field;\n }\n\n private createSelect(\n label: string,\n value: string,\n options: string[],\n onChange: (val: string) => void,\n ): HTMLElement {\n const id = this.nextFieldId(label);\n const field = createElement('div', 'ci-editor-field');\n const labelEl = createElement('label', '', { for: id });\n labelEl.textContent = label;\n const select = createElement('select', '', { id });\n for (const opt of options) {\n const option = createElement('option');\n option.value = opt;\n option.textContent = opt;\n if (opt === value) option.selected = true;\n select.appendChild(option);\n }\n select.addEventListener('change', () => onChange(select.value));\n field.appendChild(labelEl);\n field.appendChild(select);\n return field;\n }\n\n private fmtCoord(v: string | number): string {\n if (typeof v === 'string') return v;\n return `${Math.round(v * 100) / 100}%`;\n }\n}\n","import { addListener } from '../utils/events';\nimport type { CIHotspotEditor } from './ci-hotspot-editor';\n\nexport class DragManager {\n private cleanups: (() => void)[] = [];\n private dragging = false;\n private dragId: string | null = null;\n private startX = 0;\n private startY = 0;\n private markerEl: HTMLElement | null = null;\n private origLeft = '';\n private origTop = '';\n private rafId: number | null = null;\n\n constructor(private editor: CIHotspotEditor) {\n this.bind();\n }\n\n bind(): void {\n this.unbind();\n\n const canvas = this.editor.getCanvasEl();\n\n const onPointerDown = (e: PointerEvent) => {\n if (this.editor.getMode() !== 'select') return;\n const target = e.target as HTMLElement;\n const marker = target.closest<HTMLElement>('[data-hotspot-id]');\n if (!marker) return;\n\n const id = marker.getAttribute('data-hotspot-id');\n if (!id) return;\n\n e.preventDefault();\n this.dragging = false;\n this.dragId = id;\n this.startX = e.clientX;\n this.startY = e.clientY;\n this.markerEl = marker;\n this.origLeft = marker.style.left;\n this.origTop = marker.style.top;\n\n marker.setPointerCapture(e.pointerId);\n };\n\n const onPointerMove = (e: PointerEvent) => {\n if (!this.dragId || !this.markerEl) return;\n\n const dx = e.clientX - this.startX;\n const dy = e.clientY - this.startY;\n\n // Start dragging after 3px threshold\n if (!this.dragging && Math.abs(dx) + Math.abs(dy) < 3) return;\n this.dragging = true;\n\n // Throttle DOM updates to one per animation frame\n if (this.rafId !== null) return;\n const clientX = e.clientX;\n const clientY = e.clientY;\n this.rafId = requestAnimationFrame(() => {\n this.rafId = null;\n if (!this.markerEl) return;\n\n const imgEl = canvas.querySelector<HTMLImageElement>('.ci-hotspot-image');\n if (!imgEl) return;\n\n const rect = imgEl.getBoundingClientRect();\n const x = ((clientX - rect.left) / rect.width) * 100;\n const y = ((clientY - rect.top) / rect.height) * 100;\n\n const cx = Math.max(0, Math.min(100, x));\n const cy = Math.max(0, Math.min(100, y));\n\n this.markerEl.style.left = `${cx}%`;\n this.markerEl.style.top = `${cy}%`;\n });\n };\n\n const onPointerUp = (e: PointerEvent) => {\n if (this.rafId !== null) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n if (!this.dragId) return;\n\n if (this.dragging && this.markerEl) {\n const imgEl = canvas.querySelector<HTMLImageElement>('.ci-hotspot-image');\n if (imgEl) {\n const rect = imgEl.getBoundingClientRect();\n const x = ((e.clientX - rect.left) / rect.width) * 100;\n const y = ((e.clientY - rect.top) / rect.height) * 100;\n const cx = Math.max(0, Math.min(100, Math.round(x * 100) / 100));\n const cy = Math.max(0, Math.min(100, Math.round(y * 100) / 100));\n\n this.editor.updateHotspot(this.dragId, {\n x: `${cx}%`,\n y: `${cy}%`,\n });\n }\n }\n\n this.dragId = null;\n this.dragging = false;\n this.markerEl = null;\n };\n\n const downCleanup = addListener(canvas, 'pointerdown', onPointerDown);\n const moveCleanup = addListener(canvas, 'pointermove', onPointerMove);\n const upCleanup = addListener(canvas, 'pointerup', onPointerUp);\n\n this.cleanups.push(downCleanup, moveCleanup, upCleanup);\n }\n\n private unbind(): void {\n this.cleanups.forEach((fn) => fn());\n this.cleanups = [];\n }\n\n destroy(): void {\n if (this.rafId !== null) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n this.unbind();\n }\n}\n","import type { EditorSnapshot } from './types';\nimport type { CIHotspotEditor } from './ci-hotspot-editor';\n\nexport class UndoManager {\n private undoStack: EditorSnapshot[] = [];\n private redoStack: EditorSnapshot[] = [];\n private maxHistory: number;\n\n constructor(\n private editor: CIHotspotEditor,\n maxHistory: number,\n ) {\n this.maxHistory = maxHistory;\n }\n\n saveInitial(): void {\n this.undoStack = [this.editor.createSnapshot()];\n this.redoStack = [];\n this.notifyChange();\n }\n\n save(): void {\n this.undoStack.push(this.editor.createSnapshot());\n if (this.undoStack.length > this.maxHistory) {\n this.undoStack.shift();\n }\n this.redoStack = [];\n this.notifyChange();\n }\n\n undo(): void {\n if (this.undoStack.length <= 1) return;\n const current = this.undoStack.pop()!;\n this.redoStack.push(current);\n const prev = this.undoStack[this.undoStack.length - 1];\n this.editor.restoreSnapshot(prev);\n this.notifyChange();\n }\n\n redo(): void {\n if (this.redoStack.length === 0) return;\n const next = this.redoStack.pop()!;\n this.undoStack.push(next);\n this.editor.restoreSnapshot(next);\n this.notifyChange();\n }\n\n canUndo(): boolean {\n return this.undoStack.length > 1;\n }\n\n canRedo(): boolean {\n return this.redoStack.length > 0;\n }\n\n private notifyChange(): void {\n this.editor.events.emit('history:change');\n this.editor.getToolbar().updateState();\n }\n}\n","import type { HotspotItem, Placement, TriggerMode } from '../core/types';\nimport type { EditorConfig, EditorMode, EditorSnapshot } from './types';\nimport { createElement, injectStyles } from '../utils/dom';\nimport { EventEmitter, addListener } from '../utils/events';\nimport { CIHotspot } from '../core/ci-hotspot';\nimport { EditorToolbar } from './editor-toolbar';\nimport { SelectionManager } from './selection-manager';\nimport { PropertyPanel } from './property-panel';\nimport { DragManager } from './drag-manager';\nimport { UndoManager } from './undo-manager';\nimport editorCss from './editor.css?inline';\nimport viewerCss from '../styles/index.css?inline';\n\nexport class CIHotspotEditor {\n private config: Required<\n Pick<EditorConfig, 'src' | 'defaultTrigger' | 'defaultPlacement' | 'maxHistory'>\n > &\n EditorConfig;\n private rootEl: HTMLElement;\n private editorEl!: HTMLElement;\n private canvasEl!: HTMLElement;\n private sidebarEl!: HTMLElement;\n private statusEl!: HTMLElement;\n\n private viewer: CIHotspot | null = null;\n private hotspots: HotspotItem[] = [];\n private mode: EditorMode = 'select';\n private nextId = 1;\n\n private toolbar!: EditorToolbar;\n private selection!: SelectionManager;\n private propertyPanel!: PropertyPanel;\n private dragManager!: DragManager;\n private undoManager!: UndoManager;\n\n readonly events = new EventEmitter();\n private cleanups: (() => void)[] = [];\n private toastEl: HTMLElement | null = null;\n private toastTimer: ReturnType<typeof setTimeout> | null = null;\n private destroyed = false;\n\n constructor(element: HTMLElement | string, config: EditorConfig) {\n this.rootEl =\n typeof element === 'string'\n ? document.querySelector<HTMLElement>(element)!\n : element;\n if (!this.rootEl) throw new Error('CIHotspotEditor: element not found');\n\n this.config = {\n defaultTrigger: 'click' as TriggerMode,\n defaultPlacement: 'top' as Placement,\n maxHistory: 50,\n ...config,\n };\n\n this.hotspots = config.hotspots ? structuredClone(config.hotspots) : [];\n // Ensure unique IDs and track next ID\n for (const h of this.hotspots) {\n const num = parseInt(h.id.replace(/\\D/g, ''), 10);\n if (!isNaN(num) && num >= this.nextId) this.nextId = num + 1;\n }\n\n injectStyles(viewerCss);\n this.injectEditorStyles();\n this.buildDOM();\n this.initModules();\n this.rebuildViewer();\n this.updateStatus();\n }\n\n // === DOM ===\n\n private injectEditorStyles(): void {\n const id = 'ci-editor-styles';\n if (document.getElementById(id)) return;\n const style = document.createElement('style');\n style.id = id;\n style.textContent = editorCss;\n document.head.appendChild(style);\n }\n\n private buildDOM(): void {\n this.editorEl = createElement('div', 'ci-editor');\n if (this.config.theme === 'dark') {\n this.editorEl.classList.add('ci-editor--dark');\n }\n this.canvasEl = createElement('div', 'ci-editor-canvas');\n this.sidebarEl = createElement('div', 'ci-editor-sidebar');\n this.statusEl = createElement('div', 'ci-editor-status');\n\n const bodyEl = createElement('div', 'ci-editor-body');\n bodyEl.appendChild(this.canvasEl);\n bodyEl.appendChild(this.sidebarEl);\n\n this.editorEl.appendChild(bodyEl);\n this.editorEl.appendChild(this.statusEl);\n\n this.rootEl.innerHTML = '';\n this.rootEl.appendChild(this.editorEl);\n }\n\n private initModules(): void {\n // Undo manager (must be before toolbar, which reads undo state)\n this.undoManager = new UndoManager(this, this.config.maxHistory);\n // Selection\n this.selection = new SelectionManager(this);\n // Toolbar (reads undo + selection state)\n this.toolbar = new EditorToolbar(this.editorEl, this);\n // Property panel\n this.propertyPanel = new PropertyPanel(this.sidebarEl, this);\n // Drag manager\n this.dragManager = new DragManager(this);\n\n // Click on canvas to add hotspot\n const clickCleanup = addListener(this.canvasEl, 'click', (e) => {\n if (this.mode !== 'add') return;\n const target = e.target as HTMLElement;\n // Don't place if clicking a marker\n if (target.closest('.ci-hotspot-marker')) return;\n\n const rect = this.canvasEl\n .querySelector('.ci-hotspot-image')\n ?.getBoundingClientRect();\n if (!rect) return;\n\n const x = ((e.clientX - rect.left) / rect.width) * 100;\n const y = ((e.clientY - rect.top) / rect.height) * 100;\n\n // Clamp to image bounds\n if (x < 0 || x > 100 || y < 0 || y > 100) return;\n\n this.addHotspot({\n x: `${Math.round(x * 100) / 100}%`,\n y: `${Math.round(y * 100) / 100}%`,\n });\n });\n this.cleanups.push(clickCleanup);\n\n // Keyboard shortcuts (scoped to editor)\n const keyCleanup = addListener(document, 'keydown', (e) => {\n // Don't handle if focus is in an input/textarea/select\n const tag = (e.target as HTMLElement).tagName;\n if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return;\n\n const mod = e.metaKey || e.ctrlKey;\n // Non-modifier shortcuts only fire when focus is within this editor\n const focusInEditor = this.editorEl.contains(document.activeElement) ||\n this.editorEl.contains(e.target as Node);\n\n if (mod && e.key === 'z' && !e.shiftKey && focusInEditor) {\n e.preventDefault();\n this.undoManager.undo();\n } else if (mod && e.key === 'z' && e.shiftKey && focusInEditor) {\n e.preventDefault();\n this.undoManager.redo();\n } else if (mod && e.key === 'y' && focusInEditor) {\n e.preventDefault();\n this.undoManager.redo();\n } else if ((e.key === 'Delete' || e.key === 'Backspace') && focusInEditor) {\n const id = this.selection.getSelectedId();\n if (id) {\n e.preventDefault();\n this.removeHotspot(id);\n }\n } else if (e.key === 'Escape' && focusInEditor) {\n if (this.mode === 'add') {\n this.setMode('select');\n } else {\n this.selection.deselect();\n }\n } else if (e.key === 'a' && !mod && focusInEditor) {\n this.setMode(this.mode === 'add' ? 'select' : 'add');\n } else if (e.key === 'v' && !mod && focusInEditor) {\n this.setMode('select');\n }\n });\n this.cleanups.push(keyCleanup);\n\n // Save initial state\n this.undoManager.saveInitial();\n }\n\n // === Viewer ===\n\n rebuildViewer(): void {\n if (this.destroyed) return;\n if (this.viewer) {\n this.viewer.destroy();\n this.viewer = null;\n }\n\n // Don't pass empty hotspots array if there are no hotspots — init with what we have\n this.viewer = new CIHotspot(this.canvasEl, {\n src: this.config.src,\n alt: this.config.alt || 'Editor image',\n hotspots: this.hotspots,\n trigger: 'click',\n pulse: false,\n lazyLoad: false,\n ...(this.config.cloudimage ? { cloudimage: this.config.cloudimage } : {}),\n });\n\n // Re-apply selection visuals\n this.selection?.refreshMarkerVisuals();\n // Re-bind drag listeners\n this.dragManager?.bind();\n }\n\n // === Hotspot CRUD ===\n\n addHotspot(partial: Partial<HotspotItem> = {}): HotspotItem | undefined {\n if (this.destroyed) return undefined;\n const id = `hotspot-${this.nextId++}`;\n const hotspot: HotspotItem = {\n ...partial,\n id,\n x: partial.x ?? '50%',\n y: partial.y ?? '50%',\n label: partial.label || `Hotspot ${this.hotspots.length + 1}`,\n trigger: partial.trigger || this.config.defaultTrigger,\n placement: partial.placement || this.config.defaultPlacement,\n data: partial.data || {},\n };\n\n this.hotspots.push(hotspot);\n this.rebuildViewer();\n this.selection.select(id);\n this.undoManager.save();\n this.notifyChange('hotspot:add');\n this.updateStatus();\n return hotspot;\n }\n\n removeHotspot(id: string): void {\n if (this.destroyed) return;\n const idx = this.hotspots.findIndex((h) => h.id === id);\n if (idx === -1) return;\n\n this.hotspots.splice(idx, 1);\n if (this.selection.getSelectedId() === id) {\n this.selection.deselect();\n }\n this.rebuildViewer();\n this.undoManager.save();\n this.notifyChange('hotspot:remove');\n this.updateStatus();\n }\n\n updateHotspot(id: string, updates: Partial<HotspotItem>): void {\n if (this.destroyed) return;\n const idx = this.hotspots.findIndex((h) => h.id === id);\n if (idx === -1) return;\n\n this.hotspots[idx] = { ...this.hotspots[idx], ...updates };\n this.rebuildViewer();\n this.undoManager.save();\n this.notifyChange('hotspot:update');\n this.updateStatus();\n }\n\n // === State Access ===\n\n getHotspots(): HotspotItem[] {\n return structuredClone(this.hotspots);\n }\n\n getHotspot(id: string): HotspotItem | undefined {\n const hotspot = this.hotspots.find((h) => h.id === id);\n return hotspot ? structuredClone(hotspot) : undefined;\n }\n\n setHotspots(hotspots: HotspotItem[]): void {\n if (this.destroyed) return;\n this.hotspots = structuredClone(hotspots);\n this.selection.deselect();\n this.rebuildViewer();\n this.updateStatus();\n }\n\n getMode(): EditorMode {\n return this.mode;\n }\n\n setMode(mode: EditorMode): void {\n if (this.destroyed) return;\n this.mode = mode;\n this.canvasEl.classList.toggle('ci-editor-canvas--add-mode', mode === 'add');\n this.events.emit('mode:change', mode);\n this.toolbar.updateState();\n this.updateStatus();\n }\n\n getCanvasEl(): HTMLElement {\n return this.canvasEl;\n }\n\n getViewer(): CIHotspot | null {\n return this.viewer;\n }\n\n getSrc(): string {\n return this.config.src;\n }\n\n setSrc(src: string): void {\n if (this.destroyed) return;\n this.config.src = src;\n this.rebuildViewer();\n }\n\n getSelection(): SelectionManager {\n return this.selection;\n }\n\n getToolbar(): EditorToolbar {\n return this.toolbar;\n }\n\n getUndoManager(): UndoManager {\n return this.undoManager;\n }\n\n // === Snapshot (for undo/redo) ===\n\n createSnapshot(): EditorSnapshot {\n return {\n hotspots: structuredClone(this.hotspots),\n selectedId: this.selection.getSelectedId(),\n };\n }\n\n restoreSnapshot(snapshot: EditorSnapshot): void {\n if (this.destroyed) return;\n this.hotspots = structuredClone(snapshot.hotspots);\n this.rebuildViewer();\n if (snapshot.selectedId && this.hotspots.find((h) => h.id === snapshot.selectedId)) {\n this.selection.select(snapshot.selectedId);\n } else {\n this.selection.deselect();\n }\n this.notifyChange('change');\n this.updateStatus();\n }\n\n // === Export ===\n\n exportJSON(): string {\n return JSON.stringify(this.hotspots, null, 2);\n }\n\n importJSON(json: string): void {\n if (this.destroyed) return;\n const parsed = JSON.parse(json);\n if (!Array.isArray(parsed)) throw new Error('Expected an array of hotspots');\n for (const h of parsed) {\n if (!h.id || h.x == null || h.y == null) {\n throw new Error('Each hotspot must have id, x, and y');\n }\n }\n this.hotspots = parsed;\n // Update nextId\n for (const h of this.hotspots) {\n const num = parseInt(h.id.replace(/\\D/g, ''), 10);\n if (!isNaN(num) && num >= this.nextId) this.nextId = num + 1;\n }\n this.selection.deselect();\n this.rebuildViewer();\n this.undoManager.save();\n this.notifyChange('change');\n this.updateStatus();\n }\n\n // === Notifications ===\n\n private notifyChange(event: string): void {\n this.events.emit(event);\n this.events.emit('change');\n this.config.onChange?.(this.getHotspots());\n this.propertyPanel?.refresh();\n }\n\n private updateStatus(): void {\n const count = this.hotspots.length;\n const selected = this.selection?.getSelectedId();\n const modeLabel = this.mode === 'add' ? 'Add mode' : 'Select mode';\n const parts = [`${count} hotspot${count !== 1 ? 's' : ''}`, modeLabel];\n if (selected) parts.push(`Selected: ${selected}`);\n this.statusEl.textContent = parts.join(' | ');\n }\n\n // === Toast ===\n\n showToast(message: string, duration = 2000): void {\n if (this.destroyed) return;\n if (!this.toastEl) {\n this.toastEl = createElement('div', 'ci-editor-toast');\n this.editorEl.appendChild(this.toastEl);\n }\n this.toastEl.textContent = message;\n this.toastEl.classList.add('ci-editor-toast--visible');\n if (this.toastTimer) clearTimeout(this.toastTimer);\n this.toastTimer = setTimeout(() => {\n this.toastEl?.classList.remove('ci-editor-toast--visible');\n this.toastTimer = null;\n }, duration);\n }\n\n // === Lifecycle ===\n\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n this.cleanups.forEach((fn) => fn());\n this.cleanups = [];\n this.dragManager.destroy();\n this.selection.destroy();\n this.toolbar.destroy();\n this.viewer?.destroy();\n this.events.removeAll();\n if (this.toastTimer) {\n clearTimeout(this.toastTimer);\n this.toastTimer = null;\n }\n this.toastEl = null;\n this.rootEl.innerHTML = '';\n }\n}\n"],"names":["STYLE_ID","isBrowser","getElement","el","found","createElement","tag","className","attrs","key","value","addClass","classNames","removeClass","injectStyles","css","style","EventEmitter","event","handler","_a","args","addListener","options","DEFAULT_CONFIG","DATA_ATTR_MAP","parseDataAttributes","element","config","cloudimage","attr","mapping","parsed","coerceValue","type","num","mergeConfig","userConfig","validateConfig","sceneIds","scene","hotspot","parseCoordinate","trimmed","normalizeToPercent","x","y","naturalWidth","naturalHeight","px","py","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","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","ICONS","EditorToolbar","parentEl","editor","firstChild","modeGroup","historyGroup","ioGroup","urlIcon","urlInput","loadBtn","label","onClick","mode","undo","json","blob","a","overlay","modal","titleId","title","textarea","errorEl","actions","cancelBtn","importBtn","close","err","input","SelectionManager","isToolbar","isSidebar","prevId","add","PropertyPanel","selectedId","hotspots","empty","list","item","coords","selectHotspot","coordRow","deleteBtn","backBtn","onChange","field","labelEl","select","opt","option","v","DragManager","canvas","onPointerDown","onPointerMove","clientX","clientY","imgEl","onPointerUp","downCleanup","moveCleanup","upCleanup","UndoManager","maxHistory","current","prev","next","CIHotspotEditor","viewerCss","editorCss","bodyEl","mod","focusInEditor","partial","snapshot","count","selected","modeLabel","duration"],"mappings":"gFAAA,MAAMA,EAAW,oBAGV,SAASC,GAAqB,CACnC,OAAO,OAAO,OAAW,KAAe,OAAO,SAAa,GAC9D,CAGO,SAASC,GAAWC,EAAuC,CAChE,GAAI,OAAOA,GAAO,SAAU,CAC1B,MAAMC,EAAQ,SAAS,cAA2BD,CAAE,EACpD,GAAI,CAACC,EAAO,MAAM,IAAI,MAAM,uBAAuBD,CAAE,aAAa,EAClE,OAAOC,CACT,CACA,OAAOD,CACT,CAGO,SAASE,EACdC,EACAC,EACAC,EAC0B,CAC1B,MAAML,EAAK,SAAS,cAAcG,CAAG,EAErC,GADIC,MAAc,UAAYA,GAC1BC,EACF,SAAW,CAACC,EAAKC,CAAK,IAAK,OAAO,QAAQF,CAAK,EAC7CL,EAAG,aAAaM,EAAKC,CAAK,EAG9B,OAAOP,CACT,CAGO,SAASQ,EAASR,KAAoBS,EAA4B,CACvET,EAAG,UAAU,IAAI,GAAGS,CAAU,CAChC,CAGO,SAASC,EAAYV,KAAoBS,EAA4B,CAC1ET,EAAG,UAAU,OAAO,GAAGS,CAAU,CACnC,CAGO,SAASE,EAAaC,EAAmB,CAE9C,GADI,CAACd,KACD,SAAS,eAAeD,CAAQ,EAAG,OACvC,MAAMgB,EAAQ,SAAS,cAAc,OAAO,EAC5CA,EAAM,GAAKhB,EACXgB,EAAM,YAAcD,EACpB,SAAS,KAAK,YAAYC,CAAK,CACjC,CChDO,MAAMC,EAAa,CAAnB,aAAA,CACL,KAAQ,cAAgB,GAA+B,CAEvD,GAAGC,EAAeC,EAA6B,CACxC,KAAK,UAAU,IAAID,CAAK,GAC3B,KAAK,UAAU,IAAIA,EAAO,IAAI,GAAK,EAErC,KAAK,UAAU,IAAIA,CAAK,EAAG,IAAIC,CAAO,CACxC,CAEA,IAAID,EAAeC,EAA6B,QAC9CC,EAAA,KAAK,UAAU,IAAIF,CAAK,IAAxB,MAAAE,EAA2B,OAAOD,EACpC,CAEA,KAAKD,KAAkBG,EAAuB,QAC5CD,EAAA,KAAK,UAAU,IAAIF,CAAK,IAAxB,MAAAE,EAA2B,QAASD,GAAYA,EAAQ,GAAGE,CAAI,EACjE,CAEA,WAAkB,CAChB,KAAK,UAAU,MAAA,CACjB,CACF,CAKO,SAASC,EACdnB,EACAe,EACAC,EACAI,EACY,CACZ,OAAApB,EAAG,iBAAiBe,EAAOC,EAA0BI,CAAO,EACrD,IAAMpB,EAAG,oBAAoBe,EAAOC,EAA0BI,CAAO,CAC9E,CCnCO,MAAMC,GAA+O,CAC1P,IAAK,GACL,QAAS,QACT,KAAM,GACN,QAAS,EACT,QAAS,EACT,MAAO,QACP,MAAO,GACP,aAAc,GACd,UAAW,MACX,SAAU,GACV,gBAAiB,OACjB,WAAY,GACZ,kBAAmB,GACnB,iBAAkB,GAClB,qBAAsB,cACxB,EAGaC,GAAkH,CAC7H,sBAAuB,CAAE,IAAK,MAAO,KAAM,QAAA,EAC3C,sBAAuB,CAAE,IAAK,MAAO,KAAM,QAAA,EAC3C,wBAAyB,CAAE,IAAK,WAAY,KAAM,MAAA,EAClD,0BAA2B,CAAE,IAAK,UAAW,KAAM,QAAA,EACnD,uBAAwB,CAAE,IAAK,OAAQ,KAAM,SAAA,EAC7C,2BAA4B,CAAE,IAAK,UAAW,KAAM,QAAA,EACpD,2BAA4B,CAAE,IAAK,UAAW,KAAM,QAAA,EACpD,wBAAyB,CAAE,IAAK,QAAS,KAAM,QAAA,EAC/C,wBAAyB,CAAE,IAAK,QAAS,KAAM,SAAA,EAC/C,4BAA6B,CAAE,IAAK,YAAa,KAAM,QAAA,EACvD,4BAA6B,CAAE,IAAK,WAAY,KAAM,SAAA,EACtD,gCAAiC,CAAE,IAAK,eAAgB,KAAM,SAAA,EAC9D,8BAA+B,CAAE,IAAK,aAAc,KAAM,SAAA,EAC1D,2BAA4B,CAAE,IAAK,QAAS,KAAM,SAAU,OAAQ,YAAA,EACpE,iCAAkC,CAAE,IAAK,aAAc,KAAM,SAAU,OAAQ,YAAA,EAC/E,4BAA6B,CAAE,IAAK,SAAU,KAAM,SAAU,OAAQ,YAAA,EACtE,kCAAmC,CAAE,IAAK,cAAe,KAAM,SAAU,OAAQ,YAAA,EACjF,4BAA6B,CAAE,IAAK,SAAU,KAAM,SAAU,OAAQ,YAAA,EACtE,yBAA0B,CAAE,IAAK,SAAU,KAAM,MAAA,EACjD,gCAAiC,CAAE,IAAK,eAAgB,KAAM,QAAA,EAC9D,mCAAoC,CAAE,IAAK,kBAAmB,KAAM,QAAA,EACpE,qCAAsC,CAAE,IAAK,mBAAoB,KAAM,QAAA,EACvE,sCAAuC,CAAE,IAAK,oBAAqB,KAAM,SAAA,EACzE,oCAAqC,CAAE,IAAK,mBAAoB,KAAM,SAAA,EACtE,yCAA0C,CAAE,IAAK,uBAAwB,KAAM,QAAA,CACjF,EAGO,SAASC,GAAoBC,EAAgD,CAClF,MAAMC,EAAkC,CAAA,EAClCC,EAAsC,CAAA,EAE5C,SAAW,CAACC,EAAMC,CAAO,IAAK,OAAO,QAAQN,EAAa,EAAG,CAC3D,MAAMf,EAAQiB,EAAQ,aAAaG,CAAI,EACvC,GAAIpB,IAAU,KAAM,SAEpB,MAAMsB,EAASC,GAAYvB,EAAOqB,EAAQ,IAAI,EAE1CA,EAAQ,SAAW,aACrBF,EAAWE,EAAQ,GAAG,EAAIC,EAE1BJ,EAAOG,EAAQ,GAAG,EAAIC,CAE1B,CAEA,OAAI,OAAO,KAAKH,CAAU,EAAE,OAAS,IACnCD,EAAO,WAAaC,GAGfD,CACT,CAEA,SAASK,GAAYvB,EAAewB,EAAuB,CACzD,OAAQA,EAAA,CACN,IAAK,UACH,OAAOxB,IAAU,OACnB,IAAK,SAAU,CACb,MAAMyB,EAAM,WAAWzB,CAAK,EAC5B,GAAI,MAAMyB,CAAG,EAAG,CACd,QAAQ,KAAK,oCAAoCzB,CAAK,GAAG,EACzD,MACF,CACA,OAAOyB,CACT,CACA,IAAK,OACH,GAAI,CACF,OAAO,KAAK,MAAMzB,CAAK,CACzB,MAAQ,CACN,QAAQ,KAAK,0CAA0CA,CAAK,GAAG,EAC/D,MACF,CACF,QACE,OAAOA,CAAA,CAEb,CAGO,SAAS0B,EAAYC,EAA+D,CACzF,MAAO,CACL,GAAGb,GACH,GAAGa,EACH,IAAKA,EAAW,KAAO,GACvB,SAAUA,EAAW,UAAY,CAAA,CAAC,CAEtC,CAGO,SAASC,EAAeV,EAAuC,CACpE,GAAIA,EAAO,QAAUA,EAAO,OAAO,OAAS,EAAG,CAC7C,MAAMW,MAAe,IACrB,UAAWC,KAASZ,EAAO,OAAQ,CACjC,GAAI,CAACY,EAAM,GAAI,MAAM,IAAI,MAAM,yCAAyC,EACxE,GAAID,EAAS,IAAIC,EAAM,EAAE,EACvB,MAAM,IAAI,MAAM,kCAAkCA,EAAM,EAAE,GAAG,EAG/D,GADAD,EAAS,IAAIC,EAAM,EAAE,EACjB,CAACA,EAAM,IAAK,MAAM,IAAI,MAAM,qBAAqBA,EAAM,EAAE,qBAAqB,CACpF,CACA,UAAWA,KAASZ,EAAO,OACzB,UAAWa,KAAWD,EAAM,UAAY,CAAA,EACtC,GAAIC,EAAQ,YAAc,CAACF,EAAS,IAAIE,EAAQ,UAAU,EACxD,MAAM,IAAI,MAAM,uBAAuBA,EAAQ,EAAE,iBAAiBA,EAAQ,UAAU,2BAA2B,EAIrH,GAAIb,EAAO,cACL,CAACW,EAAS,IAAIX,EAAO,YAAY,EACnC,MAAM,IAAI,MAAM,4BAA4BA,EAAO,YAAY,uBAAuB,CAG5F,SAAW,CAACA,EAAO,IACjB,MAAM,IAAI,MAAM,8BAA8B,CAElD,CChIO,SAASc,EAAgBhC,EAA+D,CAC7F,GAAI,OAAOA,GAAU,SAAU,CAC7B,MAAMiC,EAAUjC,EAAM,KAAA,EACtB,OAAIiC,EAAQ,SAAS,GAAG,EACf,CAAE,MAAO,WAAWA,CAAO,EAAG,UAAW,EAAA,EAE3C,CAAE,MAAO,WAAWA,CAAO,EAAG,UAAW,EAAA,CAClD,CACA,MAAO,CAAE,MAAAjC,EAAO,UAAW,EAAA,CAC7B,CAOO,SAASkC,EACdC,EACAC,EACAC,EACAC,EACO,CACP,MAAMC,EAAKP,EAAgBG,CAAC,EACtBK,EAAKR,EAAgBI,CAAC,EAE5B,MAAO,CACL,EAAGG,EAAG,UAAYA,EAAG,MAASA,EAAG,MAAQF,EAAgB,IACzD,EAAGG,EAAG,UAAYA,EAAG,MAASA,EAAG,MAAQF,EAAiB,GAAA,CAE9D,CChCO,SAASG,GAAaV,EAA4BW,EAAmC,CAC1F,MAAMC,EAAShD,EAAc,SAAU,oBAAqB,CAC1D,aAAcoC,EAAQ,MACtB,gBAAiB,QACjB,kBAAmBA,EAAQ,GAC3B,SAAY,GAAA,CACb,EAKD,GAHAY,EAAO,MAAM,KAAO,GAAGZ,EAAQ,CAAC,IAChCY,EAAO,MAAM,IAAM,GAAGZ,EAAQ,CAAC,IAE3BA,EAAQ,UAAW,CACrB,MAAMa,EAAUb,EAAQ,UAAU,KAAA,EAAO,MAAM,KAAK,EAAE,OAAO,OAAO,EAChEa,EAAQ,QAAQ3C,EAAS0C,EAAQ,GAAGC,CAAO,CACjD,CAEA,OAAIb,EAAQ,QACV9B,EAAS0C,EAAQ,2BAA2B,EAG1CD,GACFzC,EAAS0C,EAAQ,0BAA0B,EAGzCZ,EAAQ,MACVc,GAAcF,EAAQZ,EAAQ,IAAI,EAG7BY,CACT,CAGA,SAASG,GAAYC,EAAqB,CACxC,GAAI,OAAO,UAAc,IAAa,MAAO,GAE7C,MAAMC,EADM,IAAI,UAAA,EAAY,gBAAgBD,EAAK,eAAe,EAC/C,gBAEjB,OAAIC,EAAK,cAAc,aAAa,EAAU,IAC9CC,GAAaD,CAAI,EACV,IAAI,cAAA,EAAgB,kBAAkBA,CAAI,EACnD,CAEA,MAAME,OAA2B,IAAI,CACnC,SAAU,gBAAiB,SAAU,SAAU,QAC/C,UAAW,mBAAoB,gBAAiB,MAChD,QAAS,IAAK,MAAO,QAAS,SAChC,CAAC,EAED,SAASD,GAAaE,EAAqB,CACzC,UAAW/B,KAAQ,MAAM,KAAK+B,EAAK,UAAU,EAAG,CAC9C,MAAMC,EAAOhC,EAAK,KAAK,YAAA,GACnBgC,EAAK,WAAW,IAAI,GAAKA,IAAS,UAGnCA,IAAS,QAAUA,IAAS,eAC7B,sBAAsB,KAAKhC,EAAK,KAAK,IAErC+B,EAAK,gBAAgB/B,EAAK,IAAI,CAElC,CACA,UAAWiC,KAAS,MAAM,KAAKF,EAAK,QAAQ,EAAG,CAC7C,GAAID,GAAqB,IAAIG,EAAM,QAAQ,YAAA,CAAa,EAAG,CACzDA,EAAM,OAAA,EACN,QACF,CACAJ,GAAaI,CAAK,CACpB,CACF,CAGA,SAASR,GAAcF,EAA2BW,EAAoB,CACpE,MAAMrB,EAAUqB,EAAK,KAAA,EACrB,GAAI,cAAc,KAAKrB,CAAO,GAAK,WAAW,KAAKA,CAAO,EACxDU,EAAO,UAAYG,GAAYQ,CAAI,UAC1BA,EAAK,MAAM,iCAAiC,GAAKA,EAAK,WAAW,MAAM,GAAKA,EAAK,WAAW,GAAG,EAAG,CAC3G,MAAMC,EAAM5D,EAAc,MAAO,OAAW,CAC1C,IAAK2D,EACL,IAAK,GACL,cAAe,MAAA,CAChB,EACDC,EAAI,MAAM,MAAQ,OAClBA,EAAI,MAAM,OAAS,OACnBA,EAAI,MAAM,UAAY,UACtBZ,EAAO,YAAYY,CAAG,CACxB,KAAO,CAEL,MAAMC,EAAS7D,EAAc,OAAQ2D,EAAM,CAAE,cAAe,OAAQ,EACpEX,EAAO,YAAYa,CAAM,CAC3B,CACF,CAGO,SAASC,EAAgBd,EAA2Be,EAAuB,CAC5EA,GACFzD,EAAS0C,EAAQ,2BAA2B,EAC5CA,EAAO,aAAa,gBAAiB,MAAM,IAE3CxC,EAAYwC,EAAQ,2BAA2B,EAC/CA,EAAO,aAAa,gBAAiB,OAAO,EAEhD,CAGO,SAASgB,GAAgBhB,EAA2BiB,EAAuB,CAC5EA,EACF3D,EAAS0C,EAAQ,2BAA2B,EAE5CxC,EAAYwC,EAAQ,2BAA2B,CAEnD,CAGO,SAASkB,EAAclB,EAAiC,CAC7DA,EAAO,OAAA,CACT,CC5GO,SAASmB,GACdC,EACAC,EACAC,EACApD,EACgB,CAChB,MAAMqD,EAAaH,EAAS,sBAAA,EACtBI,EAAgBF,EAAY,sBAAA,EAC5BG,EAAeJ,EAAU,YACzBK,EAAgBL,EAAU,aAG1BM,EAAgBJ,EAAW,KAAOA,EAAW,MAAQ,EAAIC,EAAc,KACvEI,EAAgBL,EAAW,IAAMA,EAAW,OAAS,EAAIC,EAAc,IACvEK,EAAYN,EAAW,IAAMC,EAAc,IAC3CM,EAAeP,EAAW,OAASC,EAAc,IACjDO,EAAaR,EAAW,KAAOC,EAAc,KAC7CQ,EAAcT,EAAW,MAAQC,EAAc,KAE/CS,EAAiBX,EAAY,YAC7BY,EAAkBZ,EAAY,aAC9Ba,EAAM,EAENC,EAAwB,CAC5B,IAAKP,EAAYM,EACjB,OAAQD,EAAkBJ,EAAeK,EACzC,KAAMJ,EAAaI,EACnB,MAAOF,EAAiBD,EAAcG,CAAA,EAGxC,IAAIE,EAAYnE,EAAQ,UACpBmE,IAAc,SAChBA,EAAYC,GAAiBF,CAAK,GAIpCC,EAAYE,GAAKF,EAAWZ,EAAcC,EAAeU,CAAK,EAG9D,IAAI5C,EACAC,EACA+C,EAAc,EAElB,OAAQH,EAAA,CACN,IAAK,MACH7C,EAAImC,EAAgBF,EAAe,EACnChC,EAAIoC,EAAYM,EAAMT,EACtB,MACF,IAAK,SACHlC,EAAImC,EAAgBF,EAAe,EACnChC,EAAIqC,EAAeK,EACnB,MACF,IAAK,OACH3C,EAAIuC,EAAaI,EAAMV,EACvBhC,EAAImC,EAAgBF,EAAgB,EACpC,MACF,IAAK,QACHlC,EAAIwC,EAAcG,EAClB1C,EAAImC,EAAgBF,EAAgB,EACpC,MACF,QACElC,EAAImC,EAAgBF,EAAe,EACnChC,EAAIoC,EAAYM,EAAMT,CAAA,CAI1B,MAAMe,EAAUC,GAAMlD,EAAGC,EAAGgC,EAAcC,EAAeO,EAAgBC,CAAe,EACxF,OAAAM,EAAeH,IAAc,OAASA,IAAc,SAChD7C,EAAIiD,EAAQ,EACZhD,EAAIgD,EAAQ,EAET,CACL,EAAGA,EAAQ,EACX,EAAGA,EAAQ,EACX,UAAAJ,EACA,YAAAG,CAAA,CAEJ,CAEA,SAASF,GAAiBF,EAAmD,CAC3E,MAAMO,EAAM,KAAK,IAAIP,EAAM,IAAKA,EAAM,OAAQA,EAAM,KAAMA,EAAM,KAAK,EACrE,OAAIO,IAAQP,EAAM,IAAY,MAC1BO,IAAQP,EAAM,OAAe,SAC7BO,IAAQP,EAAM,MAAc,QACzB,MACT,CAEA,SAASG,GACPF,EACAZ,EACAC,EACAU,EAC4B,CAC5B,MAAMQ,EAAIP,EACV,OAAQO,EAAA,CACN,IAAK,MACH,GAAIR,EAAM,IAAMV,GAAiBU,EAAM,OAASA,EAAM,IAAK,MAAO,SAClE,MACF,IAAK,SACH,GAAIA,EAAM,OAASV,GAAiBU,EAAM,IAAMA,EAAM,OAAQ,MAAO,MACrE,MACF,IAAK,OACH,GAAIA,EAAM,KAAOX,GAAgBW,EAAM,MAAQA,EAAM,KAAM,MAAO,QAClE,MACF,IAAK,QACH,GAAIA,EAAM,MAAQX,GAAgBW,EAAM,KAAOA,EAAM,MAAO,MAAO,OACnE,KAAA,CAEJ,OAAOQ,CACT,CAEA,SAASF,GACPlD,EACAC,EACAoD,EACAC,EACAb,EACAC,EAC0B,CAE1B,IAAIa,EAAKvD,EACLwD,EAAKvD,EAGT,OAAIoD,EAAQZ,EAAiB,EAAI,EAC/Bc,GAAMd,EAAiBY,GAAS,GAE5BE,EAAK,IAASA,EAAK,GACnBA,EAAKF,EAAQZ,EAAiB,IAASc,EAAKd,EAAiB,EAAUY,IAIzEC,EAASZ,EAAkB,EAAI,EACjCc,GAAMd,EAAkBY,GAAU,GAE9BE,EAAK,IAASA,EAAK,GACnBA,EAAKF,EAASZ,EAAkB,IAASc,EAAKd,EAAkB,EAAUY,IAGzE,CAAE,EAAGC,EAAI,EAAGC,CAAA,CACrB,CCtJA,MAAMC,OAAmB,IAAI,CAC3B,IAAK,IAAK,KAAM,MAAO,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,KAC3D,IAAK,MAAO,KAAM,KAAM,IAAK,OAAQ,SAAU,IACjD,CAAC,EAEKC,OAAoB,IAAI,CAC5B,QAAS,OAAQ,MAAO,MAAO,QAAS,SAAU,KACpD,CAAC,EAEKC,GAAoB,wBACpBC,GAAmB,wCACnBC,OAAsB,IAAI,CAC9B,WAAY,aAAc,WAAY,WAAY,SAAU,OAC5D,UAAW,OAAQ,OAAQ,SAAU,MAAO,UAC9C,CAAC,EAMM,SAASC,GAAaC,EAAsB,CAGjD,MAAMC,EAFS,IAAI,UAAA,EACA,gBAAgB,SAASD,CAAI,UAAW,WAAW,EACrD,KAEjB,OAAAE,GAAaD,CAAI,EAEVA,EAAK,SACd,CAEA,SAASC,GAAajD,EAAkB,CACtC,MAAMkD,EAAW,MAAM,KAAKlD,EAAK,UAAU,EAE3C,UAAWE,KAASgD,EAClB,GAAIhD,EAAM,WAAa,KAAK,UAI5B,GAAIA,EAAM,WAAa,KAAK,aAAc,CACxC,MAAM5D,EAAK4D,EACLiD,EAAU7G,EAAG,QAAQ,YAAA,EAE3B,GAAI,CAACmG,GAAa,IAAIU,CAAO,EAAG,CAE9B7G,EAAG,OAAA,EACH,QACF,CAGA,MAAMK,EAAQ,MAAM,KAAKL,EAAG,UAAU,EACtC,UAAW2B,KAAQtB,EAAO,CACxB,MAAMsD,EAAOhC,EAAK,KAAK,YAAA,EAGvB,GAAIgC,EAAK,WAAW,IAAI,EAAG,CACzB3D,EAAG,gBAAgB2B,EAAK,IAAI,EAC5B,QACF,CAEA,GAAI,CAACyE,GAAc,IAAIzC,CAAI,EAAG,CAC5B3D,EAAG,gBAAgB2B,EAAK,IAAI,EAC5B,QACF,CAGA,GAAIgC,IAAS,QAAU,CAAC0C,GAAkB,KAAK1E,EAAK,MAAM,KAAA,CAAM,EAC9D3B,EAAG,gBAAgB2B,EAAK,IAAI,UACnBgC,IAAS,OAAS,CAAC2C,GAAiB,KAAK3E,EAAK,MAAM,KAAA,CAAM,EACnE3B,EAAG,gBAAgB2B,EAAK,IAAI,UACnBgC,IAAS,MAAO,CACzB,MAAMmD,EAAanF,EAAK,MAAM,KAAA,EAAO,cAAc,MAAM,KAAK,EAAE,OAAQoF,GAAMR,GAAgB,IAAIQ,CAAC,CAAC,EAChGD,EAAW,SAAW,EACxB9G,EAAG,gBAAgB2B,EAAK,IAAI,EAE5B3B,EAAG,aAAa2B,EAAK,KAAMmF,EAAW,KAAK,GAAG,CAAC,CAEnD,CACF,CAGAH,GAAa3G,CAAE,CACjB,MAEE4D,EAAM,OAAA,CAGZ,CClFO,SAASoD,GAAsBC,EAA2B,CAC/D,MAAMC,EAAkB,CAAA,EAEpBD,EAAK,OACPC,EAAM,KAAK,8CAA8CC,EAAWF,EAAK,KAAK,CAAC,UAAUE,EAAWF,EAAK,OAAS,EAAE,CAAC,IAAI,EAG3H,MAAMG,EAAsB,CAAA,EAc5B,GAZIH,EAAK,OACPG,EAAU,KAAK,wCAAwCC,EAAWJ,EAAK,KAAK,CAAC,OAAO,EAGlFA,EAAK,OACPG,EAAU,KAAK,0CAA0CC,EAAWJ,EAAK,KAAK,CAAC,SAAS,EAGtFA,EAAK,aACPG,EAAU,KAAK,6CAA6CC,EAAWJ,EAAK,WAAW,CAAC,MAAM,EAG5FA,EAAK,KAAOK,GAAUL,EAAK,GAAG,EAAG,CACnC,MAAMM,EAAUN,EAAK,SAAW,eAChCG,EAAU,KACR,2CAA2CD,EAAWF,EAAK,GAAG,CAAC,KAAKI,EAAW,OAAOE,CAAO,CAAC,CAAC,MAAA,CAEnG,CAEA,OAAIH,EAAU,OAAS,GACrBF,EAAM,KAAK,wCAAwCE,EAAU,KAAK,EAAE,CAAC,QAAQ,EAGxEF,EAAM,KAAK,EAAE,CACtB,CAMO,SAASM,GACdlF,EACAmF,EACsB,CAEtB,OAAIA,EACKA,EAASnF,CAAO,EAIrBA,EAAQ,QACHkE,GAAalE,EAAQ,OAAO,EAIjCA,EAAQ,KACH0E,GAAsB1E,EAAQ,IAAI,EAGpC,EACT,CAEA,SAAS+E,EAAWK,EAAqB,CACvC,OAAOA,EACJ,QAAQ,KAAM,OAAO,EACrB,QAAQ,KAAM,MAAM,EACpB,QAAQ,KAAM,MAAM,EACpB,QAAQ,KAAM,QAAQ,CAC3B,CAGA,SAASJ,GAAUK,EAAsB,CACvC,MAAMC,EAAaD,EAAI,QAAQ,iBAAkB,EAAE,EACnD,MAAO,gBAAgB,KAAKC,CAAU,GAAK,YAAY,KAAKA,CAAU,GAAK,KAAK,KAAKA,CAAU,CACjG,CAEA,SAAST,EAAWO,EAAqB,CACvC,OAAOA,EACJ,QAAQ,KAAM,OAAO,EACrB,QAAQ,KAAM,QAAQ,EACtB,QAAQ,KAAM,OAAO,EACrB,QAAQ,KAAM,MAAM,EACpB,QAAQ,KAAM,MAAM,CACzB,CCzEO,MAAMG,CAAQ,CAYnB,YAAYvF,EAAsBlB,EAAyB,CAR3D,KAAQ,QAAU,GAGlB,KAAQ,SAA+B,KACvC,KAAQ,YAAkC,KAE1C,KAAQ,cAAgC,CAAA,EAGtC,KAAK,QAAUkB,EACf,KAAK,QAAUlB,EAEf,MAAM0G,EAAW1G,EAAQ,cAAgB,SAAWA,EAAQ,cAAgB,OAC5E,KAAK,QAAUlB,EAAc,MAAO,qBAAsB,CACxD,KAAQ4H,EAAW,SAAW,UAC9B,GAAM,sBAAsBxF,EAAQ,EAAE,GACtC,cAAe,OACf,iBAAkBlB,EAAQ,YAAc,OAAS,MAAQA,EAAQ,UACjE,GAAI0G,GAAYxF,EAAQ,MAAQ,CAAE,aAAcA,EAAQ,OAAU,CAAA,CAAC,CACpE,EAED,KAAK,QAAUpC,EAAc,MAAO,0BAA0B,EAC9D,KAAK,UAAYA,EAAc,MAAO,4BAA4B,EAElE,KAAK,QAAQ,YAAY,KAAK,OAAO,EACrC,KAAK,QAAQ,YAAY,KAAK,SAAS,EAGvC,MAAM6H,EAAUP,GAAqBlF,EAASlB,EAAQ,QAAQ,EAQ9D,GAPI,OAAO2G,GAAY,SACrB,KAAK,UAAU,UAAYA,EAClBA,aAAmB,aAC5B,KAAK,UAAU,YAAYA,CAAO,EAIhC3G,EAAQ,cAAgB,QAAS,CACnC,MAAM4G,EAAU,IAAM,KAAK,eAAA,EACrBC,EAAU,IAAM,KAAK,aAAA,EAC3B,KAAK,QAAQ,iBAAiB,aAAcD,CAAO,EACnD,KAAK,QAAQ,iBAAiB,aAAcC,CAAO,EACnD,KAAK,cAAc,KACjB,IAAM,KAAK,QAAQ,oBAAoB,aAAcD,CAAO,EAC5D,IAAM,KAAK,QAAQ,oBAAoB,aAAcC,CAAO,CAAA,CAEhE,CACF,CAGA,MAAMzD,EAA0BF,EAA6B,CAC3D,KAAK,YAAcE,EACnB,KAAK,SAAWF,EAChBE,EAAY,YAAY,KAAK,OAAO,EAGhC,KAAK,QAAQ,cAAgB,SAAW,KAAK,QAAQ,cAAgB,QACvEF,EAAS,aAAa,gBAAiB,QAAQ,EAC/CA,EAAS,aAAa,gBAAiB,KAAK,QAAQ,EAAE,GAEtDA,EAAS,aAAa,mBAAoB,KAAK,QAAQ,EAAE,CAE7D,CAGA,MAAa,SACX,KAAK,eAAA,EACD,MAAK,UACT,KAAK,QAAU,GAEf9D,EAAS,KAAK,QAAS,6BAA6B,EACpD,KAAK,QAAQ,aAAa,cAAe,OAAO,EAEhD,KAAK,eAAA,GACL0H,GAAAjH,EAAA,KAAK,SAAQ,SAAb,MAAAiH,EAAA,KAAAjH,EAAsB,KAAK,SAC7B,CAGA,aAAakH,EAAgB,IAAW,CACtC,KAAK,eAAA,EACL,KAAK,UAAY,WAAW,IAAM,CAChC,KAAK,KAAA,CACP,EAAGA,CAAK,CACV,CAGA,MAAa,SACX,KAAK,eAAA,EACA,KAAK,UACV,KAAK,QAAU,GAEfzH,EAAY,KAAK,QAAS,6BAA6B,EACvD,KAAK,QAAQ,aAAa,cAAe,MAAM,GAE/CwH,GAAAjH,EAAA,KAAK,SAAQ,UAAb,MAAAiH,EAAA,KAAAjH,EAAuB,KAAK,SAC9B,CAGA,gBAAuB,CACjB,KAAK,YAAc,SACrB,aAAa,KAAK,SAAS,EAC3B,KAAK,UAAY,OAErB,CAGA,gBAAuB,CACrB,GAAI,CAAC,KAAK,UAAY,CAAC,KAAK,aAAe,CAAC,KAAK,QAAS,OAE1D,MAAMmH,EAAS/D,GACb,KAAK,SACL,KAAK,QACL,KAAK,YACL,CAAE,UAAW,KAAK,QAAQ,SAAA,CAAU,EAGtC,KAAK,QAAQ,MAAM,KAAO,GAAG+D,EAAO,CAAC,KACrC,KAAK,QAAQ,MAAM,IAAM,GAAGA,EAAO,CAAC,KACpC,KAAK,QAAQ,aAAa,iBAAkBA,EAAO,SAAS,EAG5D,KAAK,cAAcA,EAAO,UAAWA,EAAO,WAAW,CACzD,CAEQ,cAAc7C,EAAsB8C,EAAsB,CAChE,MAAMC,EAAQ,KAAK,QAEnBA,EAAM,MAAM,KAAO,GACnBA,EAAM,MAAM,IAAM,GAEd/C,IAAc,OAASA,IAAc,SACvC+C,EAAM,MAAM,KAAO,6CAA6CD,CAAM,MAEtEC,EAAM,MAAM,IAAM,6CAA6CD,CAAM,KAEzE,CAGA,WAAqB,CACnB,OAAO,KAAK,OACd,CAGA,YAA0B,CACxB,OAAO,KAAK,OACd,CAGA,SAAgB,WACd,KAAK,eAAA,EACL,KAAK,cAAc,QAASE,GAAOA,GAAI,EACvC,KAAK,cAAgB,CAAA,GACrBtH,EAAA,KAAK,WAAL,MAAAA,EAAe,gBAAgB,qBAC/BiH,EAAA,KAAK,WAAL,MAAAA,EAAe,gBAAgB,kBAC/BM,EAAA,KAAK,WAAL,MAAAA,EAAe,gBAAgB,iBAC/B,KAAK,QAAQ,OAAA,EACb,KAAK,SAAW,KAChB,KAAK,YAAc,IACrB,CACF,CCtKO,MAAMC,EAAkB,CAa7B,YAAYzI,EAAiB0I,EAA6BC,EAAuB,CAVjF,KAAQ,aAAe,EACvB,KAAQ,qBAAuB,EAC/B,KAAQ,kBAAoB,EAC5B,KAAQ,UAAY,EACpB,KAAQ,UAAY,EACpB,KAAQ,WAAa,GACrB,KAAQ,UAAY,GACpB,KAAQ,YAAc,GACtB,KAAQ,SAA2B,CAAA,EAGjC,KAAK,GAAK3I,EACV,KAAK,UAAY0I,EACjB,KAAK,kBAAoBC,EAAA,EAEzB,MAAMC,EAAgBC,GAAkB,KAAK,iBAAiBA,EAAGF,CAAO,EAClEG,EAAeD,GAAkB,KAAK,gBAAgBA,CAAC,EACvDE,EAAcF,GAAkB,KAAK,eAAeA,CAAC,EAE3D7I,EAAG,iBAAiB,aAAc4I,EAAc,CAAE,QAAS,GAAO,EAClE5I,EAAG,iBAAiB,YAAa8I,EAAa,CAAE,QAAS,GAAO,EAChE9I,EAAG,iBAAiB,WAAY+I,EAAY,CAAE,QAAS,GAAM,EAE7D,KAAK,SAAS,KACZ,IAAM/I,EAAG,oBAAoB,aAAc4I,CAAY,EACvD,IAAM5I,EAAG,oBAAoB,YAAa8I,CAAW,EACrD,IAAM9I,EAAG,oBAAoB,WAAY+I,CAAU,CAAA,CAEvD,CAEQ,iBAAiBF,EAAeF,EAA6B,CAC/DE,EAAE,QAAQ,SAAW,GACvBA,EAAE,eAAA,EACF,KAAK,WAAa,GAClB,KAAK,YAAc,GACnB,KAAK,qBAAuB,KAAK,iBAAiBA,EAAE,OAAO,EAC3D,KAAK,kBAAoBF,EAAA,GAChBE,EAAE,QAAQ,SAAW,IAC9B,KAAK,UAAYA,EAAE,QAAQ,CAAC,EAAE,QAC9B,KAAK,UAAYA,EAAE,QAAQ,CAAC,EAAE,QAC9B,KAAK,YAAc,GAEvB,CAEQ,gBAAgBA,EAAqB,iBAC3C,GAAIA,EAAE,QAAQ,SAAW,GAAK,KAAK,WAAY,CAC7CA,EAAE,eAAA,EACF,MAAMG,EAAkB,KAAK,iBAAiBH,EAAE,OAAO,EACjDI,EAAQ,KAAK,mBAAqBD,EAAkB,KAAK,sBACzDE,GAAWL,EAAE,QAAQ,CAAC,EAAE,QAAUA,EAAE,QAAQ,CAAC,EAAE,SAAW,EAC1DM,GAAWN,EAAE,QAAQ,CAAC,EAAE,QAAUA,EAAE,QAAQ,CAAC,EAAE,SAAW,GAChEX,GAAAjH,EAAA,KAAK,WAAU,UAAf,MAAAiH,EAAA,KAAAjH,EAAyBgI,EAAOC,EAASC,EAC3C,SAAWN,EAAE,QAAQ,SAAW,GAAK,CAAC,KAAK,YAAc,CAAC,KAAK,YAAa,CACrE,KAAK,YACR,KAAK,UAAY,IACjBO,GAAAZ,EAAA,KAAK,WAAU,aAAf,MAAAY,EAAA,KAAAZ,IAEF,MAAMa,EAAUR,EAAE,QAAQ,CAAC,EAAE,QAAU,KAAK,UACtCS,EAAUT,EAAE,QAAQ,CAAC,EAAE,QAAU,KAAK,WAC5CU,GAAAC,EAAA,KAAK,WAAU,QAAf,MAAAD,EAAA,KAAAC,EAAuBH,EAASC,EAClC,CACF,CAEQ,eAAeT,EAAqB,aAiB1C,GAhBI,KAAK,YAAcA,EAAE,QAAQ,OAAS,IACxC,KAAK,WAAa,GAClB,KAAK,YAAc,GAEfA,EAAE,QAAQ,SAAW,IACvB,KAAK,UAAYA,EAAE,QAAQ,CAAC,EAAE,QAC9B,KAAK,UAAYA,EAAE,QAAQ,CAAC,EAAE,UAI9B,KAAK,YACP,KAAK,UAAY,IACjBX,GAAAjH,EAAA,KAAK,WAAU,WAAf,MAAAiH,EAAA,KAAAjH,IAIE4H,EAAE,eAAe,SAAW,GAAKA,EAAE,QAAQ,SAAW,EAAG,CAC3D,GAAI,KAAK,YAAa,CACpB,KAAK,YAAc,GACnB,KAAK,aAAe,EACpB,MACF,CACA,MAAMY,EAAM,KAAK,IAAA,EACjB,GAAIA,EAAM,KAAK,aAAe,IAAK,CACjC,MAAMC,EAAQb,EAAE,eAAe,CAAC,GAChCO,GAAAZ,EAAA,KAAK,WAAU,cAAf,MAAAY,EAAA,KAAAZ,EAA6BkB,EAAM,QAASA,EAAM,QACpD,CACA,KAAK,aAAeD,CACtB,CACF,CAEQ,iBAAiBE,EAA4B,CACnD,MAAMC,EAAKD,EAAQ,CAAC,EAAE,QAAUA,EAAQ,CAAC,EAAE,QACrCE,EAAKF,EAAQ,CAAC,EAAE,QAAUA,EAAQ,CAAC,EAAE,QAC3C,OAAO,KAAK,KAAKC,EAAKA,EAAKC,EAAKA,CAAE,CACpC,CAEA,SAAgB,CACd,KAAK,SAAS,QAAStB,GAAOA,GAAI,EAClC,KAAK,SAAW,CAAA,CAClB,CACF,CC7FO,MAAMuB,EAAQ,CAoBnB,YAAYC,EAAuBC,EAAwB5I,EAAyB,CAhBpF,KAAQ,KAAO,EACf,KAAQ,KAAO,EACf,KAAQ,KAAO,EACf,KAAQ,QAAU,GAClB,KAAQ,WAAa,GACrB,KAAQ,WAAa,EACrB,KAAQ,WAAa,EACrB,KAAQ,SAAW,EACnB,KAAQ,SAAW,EACnB,KAAQ,SAAqC,KAC7C,KAAQ,YAAc,GACtB,KAAQ,iBAAmB,EAC3B,KAAQ,eAAiB,EACzB,KAAQ,eAAiB,EACzB,KAAQ,SAA2B,CAAA,EAGjC,KAAK,SAAW2I,EAChB,KAAK,UAAYC,EACjB,KAAK,QAAU5I,EAEf,KAAK,WAAA,CACP,CAEQ,YAAmB,CAEzB,MAAM6I,EAAWpB,GAAkB,SACjC,GAAI,CAAC,KAAK,QAAS,OAGnB,GAAI,KAAK,YAAa,CACpBA,EAAE,eAAA,EACF,MACF,CAEA,GAAI,CAACA,EAAE,QAAS,EAEdX,GAAAjH,EAAA,KAAK,SAAQ,sBAAb,MAAAiH,EAAA,KAAAjH,GACA,MACF,CAEA4H,EAAE,eAAA,EAEF,MAAMqB,EAAO,KAAK,UAAU,sBAAA,EACtBC,EAAUtB,EAAE,QAAUqB,EAAK,KAC3BE,EAAUvB,EAAE,QAAUqB,EAAK,IAGjC,IAAIG,EAASxB,EAAE,OACXA,EAAE,YAAc,IAAGwB,GAAU,IAEjC,MAAMC,EAAQ,CAACD,EAAS,IACxB,KAAK,QAAQ,KAAK,KAAOC,EAAOH,EAASC,CAAO,CAClD,EACA,KAAK,UAAU,iBAAiB,QAASH,EAAS,CAAE,QAAS,GAAO,EACpE,KAAK,SAAS,KAAK,IAAM,KAAK,UAAU,oBAAoB,QAASA,CAAO,CAAC,EAG7E,KAAK,mBAAA,EAGL,MAAMM,EAAc1B,GAAkB,CACpC,GAAI,CAAC,KAAK,QAAS,OACnB,MAAMqB,EAAO,KAAK,UAAU,sBAAA,EACtBxH,EAAImG,EAAE,QAAUqB,EAAK,KACrBvH,EAAIkG,EAAE,QAAUqB,EAAK,IACvB,KAAK,KAAO,EACd,KAAK,UAAA,EAEL,KAAK,QAAQ,EAAGxH,EAAGC,CAAC,CAExB,EACA,KAAK,UAAU,iBAAiB,WAAY4H,CAAU,EACtD,KAAK,SAAS,KAAK,IAAM,KAAK,UAAU,oBAAoB,WAAYA,CAAU,CAAC,EAGnF,MAAMC,EAAe3B,GAAkB,CACjC,CAAC,KAAK,SAAW,KAAK,MAAQ,GAE9BA,EAAE,SAAW,IACjB,KAAK,WAAa,GAClB,KAAK,WAAaA,EAAE,QACpB,KAAK,WAAaA,EAAE,QACpB,KAAK,SAAW,KAAK,KACrB,KAAK,SAAW,KAAK,KACrBrI,EAAS,KAAK,SAAU,+BAA+B,EACvD,KAAK,UAAU,MAAM,OAAS,WAC9BqI,EAAE,eAAA,EACJ,EAEM4B,EAAe5B,GAAkB,CACrC,GAAI,CAAC,KAAK,WAAY,OACtB,MAAMe,GAAMf,EAAE,QAAU,KAAK,YAAc,KAAK,KAC1CgB,GAAMhB,EAAE,QAAU,KAAK,YAAc,KAAK,KAChD,KAAK,KAAO,KAAK,SAAWe,EAC5B,KAAK,KAAO,KAAK,SAAWC,EAC5B,KAAK,SAAA,EACL,KAAK,eAAA,CACP,EAEMa,EAAY,IAAM,CACjB,KAAK,aACV,KAAK,WAAa,GAClBhK,EAAY,KAAK,SAAU,+BAA+B,EAC1D,KAAK,UAAU,MAAM,OAAS,KAAK,KAAO,EAAI,OAAS,GACzD,EAEA,KAAK,UAAU,iBAAiB,YAAa8J,CAAW,EACxD,SAAS,iBAAiB,YAAaC,CAAW,EAClD,SAAS,iBAAiB,UAAWC,CAAS,EAC9C,KAAK,SAAS,KACZ,IAAM,KAAK,UAAU,oBAAoB,YAAaF,CAAW,EACjE,IAAM,SAAS,oBAAoB,YAAaC,CAAW,EAC3D,IAAM,SAAS,oBAAoB,UAAWC,CAAS,CAAA,EAIzD,KAAK,SAAW,IAAIjC,GAClB,KAAK,UACL,CACE,QAAS,CAACQ,EAAO0B,EAAIC,IAAO,CAC1B,MAAMV,EAAO,KAAK,UAAU,sBAAA,EAC5B,KAAK,QAAQjB,EAAO0B,EAAKT,EAAK,KAAMU,EAAKV,EAAK,GAAG,CACnD,EACA,WAAY,IAAM,CAChB,KAAK,eAAiB,KAAK,KAC3B,KAAK,eAAiB,KAAK,IAC7B,EACA,MAAO,CAACb,EAASC,IAAY,CACvB,KAAK,MAAQ,IACjB,KAAK,KAAO,KAAK,eAAiBD,EAAU,KAAK,KACjD,KAAK,KAAO,KAAK,eAAiBC,EAAU,KAAK,KACjD,KAAK,SAAA,EACL,KAAK,eAAA,EACP,EACA,YAAa,CAACqB,EAAIC,IAAO,CACvB,MAAMV,EAAO,KAAK,UAAU,sBAAA,EACxB,KAAK,KAAO,EACd,KAAK,UAAA,EAEL,KAAK,QAAQ,EAAGS,EAAKT,EAAK,KAAMU,EAAKV,EAAK,GAAG,CAEjD,CAAA,EAEF,IAAM,KAAK,IAAA,CAEf,CAEQ,oBAA2B,CAEjC,GAAI,OAAO,OAAW,KAAe,EAAE,iBAAkB,QAAS,OAElE,MAAMW,EAAkBhC,GAAa,CACnCA,EAAE,eAAA,EACF,KAAK,YAAc,GACnB,KAAK,iBAAmB,KAAK,IAC/B,EAEMiC,EAAmBjC,GAAa,CAEpC,GADAA,EAAE,eAAA,EACE,CAAC,KAAK,QAAS,OACnB,MAAMkC,EAAKlC,EACLqB,EAAO,KAAK,UAAU,sBAAA,EAEtBc,EAAKnC,EACLsB,EAAUa,EAAG,SAAW,KAAOA,EAAG,QAAUd,EAAK,KAAOA,EAAK,MAAQ,EACrEE,EAAUY,EAAG,SAAW,KAAOA,EAAG,QAAUd,EAAK,IAAMA,EAAK,OAAS,EAC3E,KAAK,QAAQ,KAAK,iBAAmBa,EAAG,MAAOZ,EAASC,CAAO,CACjE,EAEMa,EAAgBpC,GAAa,CACjCA,EAAE,eAAA,EACF,KAAK,YAAc,EACrB,EAEA,KAAK,UAAU,iBAAiB,eAAgBgC,CAA+B,EAC/E,KAAK,UAAU,iBAAiB,gBAAiBC,CAAgC,EACjF,KAAK,UAAU,iBAAiB,aAAcG,CAA6B,EAC3E,KAAK,SAAS,KACZ,IAAM,KAAK,UAAU,oBAAoB,eAAgBJ,CAA+B,EACxF,IAAM,KAAK,UAAU,oBAAoB,gBAAiBC,CAAgC,EAC1F,IAAM,KAAK,UAAU,oBAAoB,aAAcG,CAA6B,CAAA,CAExF,CAEA,QAAQC,EAAef,EAAkBC,EAAwB,SAC/D,MAAMe,EAAU,KAAK,KAGrB,GAFA,KAAK,KAAO,KAAK,IAAI,KAAK,QAAQ,QAAS,KAAK,IAAI,KAAK,QAAQ,QAASD,CAAK,CAAC,EAE5Ef,IAAY,QAAaC,IAAY,QAAae,IAAY,KAAK,KAAM,CAE3E,MAAMhG,EAAiB,KAAK,UAAU,YAChCC,EAAkB,KAAK,UAAU,aACjCgG,EAAc,KAAK,KAAOD,EAC1BE,EAAKlB,EAAUhF,EACfmG,EAAKlB,EAAUhF,EACrB,KAAK,KAAO,KAAK,KAAQD,EAAiBkG,GAAMD,EAAc,GAAM,KAAK,KACzE,KAAK,KAAO,KAAK,KAAQhG,EAAkBkG,GAAMF,EAAc,GAAM,KAAK,IAC5E,CAEA,KAAK,SAAA,EACL,KAAK,eAAA,EACL,KAAK,aAAA,GACLlD,GAAAjH,EAAA,KAAK,SAAQ,SAAb,MAAAiH,EAAA,KAAAjH,EAAsB,KAAK,KAC7B,CAEA,SAAkB,CAChB,OAAO,KAAK,IACd,CAEA,WAAkB,SAChB,KAAK,KAAO,EACZ,KAAK,KAAO,EACZ,KAAK,KAAO,EACZ,KAAK,eAAA,EACL,KAAK,aAAA,GACLiH,GAAAjH,EAAA,KAAK,SAAQ,SAAb,MAAAiH,EAAA,KAAAjH,EAAsB,EACxB,CAEA,IAAI2I,EAAYC,EAAkB,CAC5B,KAAK,MAAQ,IACjB,KAAK,MAAQD,EAAK,KAAK,KACvB,KAAK,MAAQC,EAAK,KAAK,KACvB,KAAK,SAAA,EACL,KAAK,eAAA,EACP,CAEA,QAAe,CACb,KAAK,QAAU,EACjB,CAEA,SAAgB,CACd,KAAK,QAAU,EACjB,CAEQ,UAAiB,CACvB,MAAM1E,EAAiB,KAAK,UAAU,YAChCC,EAAkB,KAAK,UAAU,aAKjCmG,EAAWpG,GAAkB,KAAK,KAAO,GAAM,KAAK,KACpDqG,EAAWpG,GAAmB,KAAK,KAAO,GAAM,KAAK,KAE3D,KAAK,KAAO,KAAK,IAAI,CAACmG,EAAS,KAAK,IAAI,EAAG,KAAK,IAAI,CAAC,EACrD,KAAK,KAAO,KAAK,IAAI,CAACC,EAAS,KAAK,IAAI,EAAG,KAAK,IAAI,CAAC,EAEjD,KAAK,MAAQ,IACf,KAAK,KAAO,EACZ,KAAK,KAAO,EAEhB,CAEQ,gBAAuB,CAC7B,KAAK,SAAS,MAAM,UAAY,SAAS,KAAK,IAAI,eAAe,KAAK,IAAI,OAAO,KAAK,IAAI,MAC1F,KAAK,SAAS,MAAM,YAAY,SAAU,OAAO,KAAK,IAAI,CAAC,CAC7D,CAEQ,cAAqB,CACtB,KAAK,aACR,KAAK,UAAU,MAAM,OAAS,KAAK,KAAO,EAAI,OAAS,GAE3D,CAEA,SAAgB,OACd,KAAK,SAAS,QAASjD,GAAOA,GAAI,EAClC,KAAK,SAAW,CAAA,GAChBtH,EAAA,KAAK,WAAL,MAAAA,EAAe,UACf,KAAK,SAAW,KAChB,KAAK,SAAS,MAAM,UAAY,GAChC,KAAK,SAAS,MAAM,eAAe,QAAQ,EAC3C,KAAK,UAAU,MAAM,OAAS,EAChC,CACF,CCnSO,SAASwK,GACdzB,EACA0B,EACAtK,EACmE,CACnE,MAAMuK,EAAOvK,EAAQ,UAAY,GAC3BwK,EAAW1L,EAAc,MAAO,0BAA0B,EAChE0L,EAAS,QAAQ,SAAWxK,EAAQ,UAAY,eAEhD,MAAMyK,EAAQ3L,EAAc,SAAU,qBAAsB,CAC1D,aAAc,UACd,KAAQ,QAAA,CACT,EACD2L,EAAM,UAAY,4RAElB,MAAMC,EAAS5L,EAAc,SAAU,sBAAuB,CAC5D,aAAc,WACd,KAAQ,QAAA,CACT,EACD4L,EAAO,UAAY,sPAEnB,MAAMC,EAAW7L,EAAc,SAAU,wBAAyB,CAChE,aAAc,aACd,KAAQ,QAAA,CACT,EACD6L,EAAS,UAAY,qNAErBH,EAAS,YAAYC,CAAK,EAC1BD,EAAS,YAAYE,CAAM,EAC3BF,EAAS,YAAYG,CAAQ,EAE7BF,EAAM,iBAAiB,QAAUhD,GAAM,CACrCA,EAAE,gBAAA,EACF6C,EAAQ,QAAQA,EAAQ,QAAA,EAAYC,CAAI,EACxCK,EAAA,CACF,CAAC,EAEDF,EAAO,iBAAiB,QAAUjD,GAAM,CACtCA,EAAE,gBAAA,EACF6C,EAAQ,QAAQA,EAAQ,QAAA,EAAYC,CAAI,EACxCK,EAAA,CACF,CAAC,EAEDD,EAAS,iBAAiB,QAAUlD,GAAM,CACxCA,EAAE,gBAAA,EACF6C,EAAQ,UAAA,EACRM,EAAA,CACF,CAAC,EAED,SAASA,GAAoB,CAC3B,MAAMC,EAAOP,EAAQ,QAAA,EACrBG,EAAM,SAAWI,GAAQ7K,EAAQ,QACjC0K,EAAO,SAAWG,GAAQ7K,EAAQ,QAClC2K,EAAS,SAAW,KAAK,IAAIE,EAAO,CAAC,EAAI,IAC3C,CAEA,OAAAjC,EAAU,YAAY4B,CAAQ,EAC9BI,EAAA,EAEO,CACL,QAASJ,EACT,OAAQI,EACR,QAAS,IAAMJ,EAAS,OAAA,CAAO,CAEnC,CC3EA,MAAMM,GACJ,OAAO,UAAc,KAAe,wBAAwB,KAAK,UAAU,SAAS,EAEhFC,GAAYD,GACd,4BACA,wBAEEE,GAAa,KAEZ,MAAMC,EAAW,CAItB,YAAYC,EAAqB,CAFjC,KAAQ,UAAkD,KAGxD,KAAK,GAAK,SAAS,cAAc,KAAK,EACtC,KAAK,GAAG,UAAY,yBACpB,KAAK,GAAG,YAAcH,GACtB,KAAK,GAAG,aAAa,cAAe,MAAM,EAC1CG,EAAO,YAAY,KAAK,EAAE,CAC5B,CAEA,MAAa,CAEP,KAAK,YAAc,MACrB,aAAa,KAAK,SAAS,EAG7B,KAAK,GAAG,UAAU,IAAI,iCAAiC,EAEvD,KAAK,UAAY,WAAW,IAAM,CAChC,KAAK,GAAG,UAAU,OAAO,iCAAiC,EAC1D,KAAK,UAAY,IACnB,EAAGF,EAAU,CACf,CAEA,SAAgB,CACV,KAAK,YAAc,OACrB,aAAa,KAAK,SAAS,EAC3B,KAAK,UAAY,MAEnB,KAAK,GAAG,OAAA,CACV,CACF,CCxCA,MAAMG,GAAiB,cACjBC,GAAsB,KACtBC,EAAuB,IAGtB,SAASC,GAAmB3G,EAAe4G,EAAsBF,EAA8B,CACpG,OAAO,KAAK,KAAK1G,EAAQ4G,CAAW,EAAIA,CAC1C,CAGO,SAASC,GACdzH,EACA0H,EAAc,EACdC,EAAoB,EACpBH,EAAsBF,EACd,CACR,MAAMM,EAAM5H,EAAiB0H,EAAMC,EACnC,OAAOJ,GAAmBK,EAAKJ,CAAW,CAC5C,CAGO,SAASK,EACdC,EACAxL,EACA0D,EACA2H,EAAoB,EACpBD,EAAc,EACN,CACR,MAAMK,EAASzL,EAAO,QAAU8K,GAC1BY,EAAa1L,EAAO,YAAc+K,GAClCG,EAAclL,EAAO,aAAegL,EAEpC1G,EAAQ6G,GAAgBzH,EAAgB0H,EAAKC,EAAWH,CAAW,EAEnES,EAAa,UAAUH,CAAG,EAChC,IAAItF,EAAM,WAAWlG,EAAO,KAAK,IAAIyL,CAAM,IAAIC,CAAU,IAAIC,CAAU,UAAUrH,CAAK,GACtF,OAAItE,EAAO,SACTkG,GAAO,IAAIlG,EAAO,MAAM,IAEnBkG,CACT,CAMO,SAAS0F,GACdvJ,EACAmJ,EACAxL,EACA6L,EACmD,CACnD,MAAMX,EAAclL,EAAO,aAAegL,EAC1C,IAAIc,EAAqB,EAEzB,MAAMC,EAAW,IAAI,eAAgBC,GAAY,CAC/C,UAAWC,KAASD,EAAS,CAC3B,MAAMtI,EAAiBuI,EAAM,YAAY,MACzC,GAAIvI,IAAmB,EAAG,SAE1B,MAAM0H,EAAM,OAAO,OAAW,KAAc,OAAO,kBAAoB,EACjEc,EAAWf,GAAgBzH,EAAgB0H,EAAKS,EAAA,EAAgBX,CAAW,EAE7EgB,IAAaJ,IACfA,EAAqBI,EACrB7J,EAAI,IAAMkJ,EAAmBC,EAAKxL,EAAQ0D,EAAgBmI,EAAA,EAAgBT,CAAG,EAEjF,CACF,CAAC,EAED,MAAO,CACL,SAAAW,EACA,QAAS,IAAMA,EAAS,WAAA,CAAW,CAEvC,CClEA,MAAMI,EAAW,GACXC,EAAY,GAGX,MAAMC,EAAgB,CAG3B,YAAY1M,EAAiC,CAF7C,KAAQ,SAA2B,CAAA,EAGjC,KAAM,CAAE,UAAA4I,EAAW,WAAA+D,EAAY,SAAAC,EAAU,mBAAAC,GAAuB7M,EAE1D8M,EAAU/M,EAAY6I,EAAW,UAAYnB,GAAqB,CACtE,MAAMsF,EAAStF,EAAE,OAGjB,GAAIsF,EAAO,UAAY,SAAWA,EAAO,UAAY,YAAcA,EAAO,UAAY,SACpF,OAGF,MAAMzC,EAAUqC,EAAA,EAEhB,OAAQlF,EAAE,IAAA,CACR,IAAK,SACHmF,GAAA,MAAAA,IACA,MAEF,IAAK,UACCtC,GAAWA,EAAQ,QAAA,EAAY,IACjC7C,EAAE,eAAA,EACF6C,EAAQ,IAAI,EAAGkC,CAAQ,GAEzB,MACF,IAAK,YACClC,GAAWA,EAAQ,QAAA,EAAY,IACjC7C,EAAE,eAAA,EACF6C,EAAQ,IAAI,EAAG,CAACkC,CAAQ,GAE1B,MACF,IAAK,YACClC,GAAWA,EAAQ,QAAA,EAAY,IACjC7C,EAAE,eAAA,EACF6C,EAAQ,IAAIkC,EAAU,CAAC,GAEzB,MACF,IAAK,aACClC,GAAWA,EAAQ,QAAA,EAAY,IACjC7C,EAAE,eAAA,EACF6C,EAAQ,IAAI,CAACkC,EAAU,CAAC,GAE1B,MAEF,IAAK,IACL,IAAK,IACClC,IACF7C,EAAE,eAAA,EACF6C,EAAQ,QAAQA,EAAQ,QAAA,EAAYmC,CAAS,GAE/C,MACF,IAAK,IACCnC,IACF7C,EAAE,eAAA,EACF6C,EAAQ,QAAQA,EAAQ,QAAA,EAAYmC,CAAS,GAE/C,MACF,IAAK,IACCnC,IACF7C,EAAE,eAAA,EACF6C,EAAQ,UAAA,GAEV,MAEF,IAAK,IACCuC,IACFpF,EAAE,eAAA,EACFoF,EAAA,GAEF,KAAA,CAEN,CAAC,EAED,KAAK,SAAS,KAAKC,CAAO,CAC5B,CAEA,SAAgB,CACd,KAAK,SAAS,QAAS3F,GAAOA,GAAI,EAClC,KAAK,SAAW,CAAA,CAClB,CACF,CC9FA,MAAM6F,GAAqB,4IAGpB,SAASC,EAAqBrE,EAAuC,CAC1E,OAAO,MAAM,KAAKA,EAAU,iBAA8BoE,EAAkB,CAAC,CAC/E,CAGO,SAASE,EACdC,EACAC,EACuE,CACvE,IAAIvK,EAAS,GACTiK,EAA+B,KAEnC,SAASO,GAAiB,CACxB,GAAIxK,EAAQ,OACZA,EAAS,GAET,MAAMyK,EAAYL,EAAqBE,CAAO,EAC1CG,EAAU,SAAW,IAGzBA,EAAU,CAAC,EAAE,MAAA,EAEbR,EAAU/M,EAAYoN,EAAS,UAAY1F,GAAqB,CAC9D,GAAIA,EAAE,MAAQ,MAAO,OAErB,MAAM8F,EAAmBN,EAAqBE,CAAO,EACrD,GAAII,EAAiB,SAAW,EAAG,OAEnC,MAAMC,EAAQD,EAAiB,CAAC,EAC1BE,EAAOF,EAAiBA,EAAiB,OAAS,CAAC,EAErD9F,EAAE,UAAY,SAAS,gBAAkB+F,GAC3C/F,EAAE,eAAA,EACFgG,EAAK,MAAA,GACI,CAAChG,EAAE,UAAY,SAAS,gBAAkBgG,IACnDhG,EAAE,eAAA,EACF+F,EAAM,MAAA,EAEV,CAAC,EACH,CAEA,SAASE,GAAmB,CACrB7K,IACLA,EAAS,GACTiK,GAAA,MAAAA,IACAA,EAAU,KACVM,EAAc,MAAA,EAChB,CAEA,SAASO,GAAgB,CACvBD,EAAA,CACF,CAEA,MAAO,CAAE,SAAAL,EAAU,WAAAK,EAAY,QAAAC,CAAA,CACjC,CCzDA,IAAIC,EAAiC,KACjCC,EAAqB,EAGlB,SAASC,GAAuBC,EAAuB,CACvDrP,MAEAkP,IACHA,EAAa9O,EAAc,MAAO,OAAW,CAC3C,YAAa,SACb,cAAe,OACf,KAAQ,QAAA,CACT,EACD8O,EAAW,MAAM,QACf,8HACF,SAAS,KAAK,YAAYA,CAAU,GAItCA,EAAW,YAAc,GACzB,sBAAsB,IAAM,CACtBA,MAAuB,YAAcG,EAC3C,CAAC,EACH,CAGO,SAASC,GAA0B,CACxCH,GACF,CAGO,SAASI,IAA0B,CACxCJ,EAAqB,KAAK,IAAI,EAAGA,EAAqB,CAAC,EACnDA,IAAuB,GAAKD,IAC9BA,EAAW,OAAA,EACXA,EAAa,KAEjB,CCtBA,MAAMM,EACJ,sRAGIC,GACJ,0RAEF,SAASC,IAA+B,CACtC,MAAO,CAAC,EACN,SAAS,mBACR,SAAiB,wBAEtB,CAEA,SAASC,IAAuC,CAC9C,OACE,SAAS,mBACR,SAAiB,yBAClB,IAEJ,CAEA,SAASC,EAAkB1P,EAAgC,CACzD,OAAIA,EAAG,kBAA0BA,EAAG,kBAAA,EAC/BA,EAAW,yBACbA,EAAW,wBAAA,EACL,QAAQ,QAAA,GAEV,QAAQ,OAAO,IAAI,MAAM,8BAA8B,CAAC,CACjE,CAEA,SAAS2P,GAAgC,CACvC,OAAI,SAAS,eAAuB,SAAS,eAAA,EACxC,SAAiB,sBACnB,SAAiB,qBAAA,EACX,QAAQ,QAAA,GAEV,QAAQ,OAAO,IAAI,MAAM,8BAA8B,CAAC,CACjE,CAGO,SAASC,GACd5F,EACA5I,EAAoC,GACV,CAC1B,GAAI,CAACoO,GAAA,EAAuB,OAAO,KAEnC,MAAMK,EAAM3P,EAAc,SAAU,4BAA6B,CAC/D,aAAc,mBACd,eAAgB,QAChB,KAAQ,QAAA,CACT,EACD2P,EAAI,UAAYP,EAEhB,MAAMQ,EAA2B,CAAA,EAEjC,SAASC,GAAoB,CAC3B,OAAON,OAA2BzF,CACpC,CAEA,SAASgG,GAAkB,OACzB,MAAMC,EAAKF,EAAA,EACXF,EAAI,UAAYI,EAAKV,GAAeD,EACpCO,EAAI,aAAa,aAAcI,EAAK,kBAAoB,kBAAkB,EAC1EJ,EAAI,aAAa,eAAgB,OAAOI,CAAE,CAAC,EACvCA,EACFzP,EAASwJ,EAAW,kCAAkC,EAEtDtJ,EAAYsJ,EAAW,kCAAkC,GAE3D/I,EAAAG,EAAQ,WAAR,MAAAH,EAAA,KAAAG,EAAmB6O,EACrB,CAEA,SAASC,GAAe,CAClBH,IACFJ,EAAA,EAAiB,MAAM,IAAM,CAAC,CAAC,EAE/BD,EAAkB1F,CAAS,EAAE,MAAM,IAAM,CAAC,CAAC,CAE/C,CAEA,SAASmG,GAAc,CAChBJ,KACHL,EAAkB1F,CAAS,EAAE,MAAM,IAAM,CAAC,CAAC,CAE/C,CAEA,SAASoG,GAAa,CAChBL,KACFJ,EAAA,EAAiB,MAAM,IAAM,CAAC,CAAC,CAEnC,CAGA,MAAMU,EAAgBlP,EAAY,SAAU,mBAAoB6O,CAAS,EACzEF,EAAS,KAAKO,CAAa,EAE3B,SAAS,iBAAiB,yBAA0BL,CAAS,EAC7DF,EAAS,KAAK,IAAM,SAAS,oBAAoB,yBAA0BE,CAAS,CAAC,EAGrF,MAAMM,EAAenP,EAAY0O,EAAK,QAAUhH,GAAM,CACpDA,EAAE,gBAAA,EACFqH,EAAA,CACF,CAAC,EACDJ,EAAS,KAAKQ,CAAY,EAE1BtG,EAAU,YAAY6F,CAAG,EAEzB,SAASd,GAAgB,CAEnBgB,KACFJ,EAAA,EAAiB,MAAM,IAAM,CAAC,CAAC,EAEjCjP,EAAYsJ,EAAW,kCAAkC,EACzD8F,EAAS,QAASvH,GAAOA,EAAA,CAAI,EAC7BuH,EAAS,OAAS,EAClBD,EAAI,OAAA,CACN,CAEA,MAAO,CACL,QAASA,EACT,aAAcE,EACd,OAAAG,EACA,MAAAC,EACA,KAAAC,EACA,QAAArB,CAAA,CAEJ,mkeChIawB,EAAN,MAAMA,CAAuC,CA6BlD,YAAY/O,EAA+BC,EAAyB,CAtBpE,KAAQ,YAAc,IACtB,KAAQ,aAAe,IACvB,KAAQ,uBAAyB,IACjC,KAAQ,WAAgC,KACxC,KAAQ,QAA0B,KAClC,KAAQ,aAAyF,KACjG,KAAQ,kBAA8E,KACtF,KAAQ,eAAwC,KAChD,KAAQ,gBAA0C,KAClD,KAAQ,kBAA8C,KACtD,KAAQ,eAAiB,IACzB,KAAQ,SAA2B,CAAA,EACnC,KAAQ,oBAAsB,IAC9B,KAAQ,YAAc,GACtB,KAAQ,UAAY,GAEpB,KAAQ,cAAgB,IACxB,KAAQ,gBAAkB,GAE1B,KAAQ,iBAAmB,IAC3B,KAAQ,0BAA4B,IA2nBpC,KAAQ,oBAAsB,IAxnB5B,KAAK,OAAS1B,GAAWyB,CAAO,EAChC,KAAK,OAASS,EAAYR,CAAM,EAChCU,EAAe,KAAK,MAAM,EAEtB,KAAK,OAAO,QAAU,KAAK,OAAO,OAAO,OAAS,GACpD,KAAK,WAAA,EAGPiN,EAAA,EACAzO,EAAa6P,EAAO,EACpB,KAAK,SAAA,EACL,KAAK,WAAA,EACL,KAAK,WAAA,EACL,KAAK,aAAA,EAED,KAAK,OAAO,MACd,KAAK,SAAA,EAGP,KAAK,aAAA,EACL,KAAK,eAAA,EACL,KAAK,gBAAA,CACP,CAGA,OAAO,SAASjN,EAAyC,CAEvD,MAAMkN,GADYlN,GAAQ,UACC,iBACzB,iDAAA,EAEImN,EAAiC,CAAA,EAEvC,OAAAD,EAAS,QAASzQ,GAAO,CACvB,MAAMyB,EAASF,GAAoBvB,CAAE,GACjCyB,EAAO,KAAOA,EAAO,SACvBiP,EAAU,KAAK,IAAIH,EAAUvQ,EAAIyB,CAAyB,CAAC,CAE/D,CAAC,EAEMiP,CACT,CAEQ,UAAiB,CACvB,KAAK,YAAcxQ,EAAc,MAAO,sBAAsB,EAC9D,KAAK,WAAaA,EAAc,MAAO,qBAAqB,EAC5D,KAAK,MAAQA,EAAc,MAAO,mBAAoB,CACpD,IAAK,KAAK,OAAO,KAAO,GACxB,UAAW,OAAA,CACZ,EACD,KAAK,UAAYA,EAAc,MAAO,oBAAoB,EAE1D,KAAK,WAAW,YAAY,KAAK,KAAK,EACtC,KAAK,WAAW,YAAY,KAAK,SAAS,EAC1C,KAAK,YAAY,YAAY,KAAK,UAAU,EAG5C,KAAK,YAAY,aAAa,OAAQ,OAAO,EAC7C,KAAK,YAAY,aAAa,aAAc,KAAK,OAAO,KAAO,qBAAqB,EAGhF,KAAK,OAAO,mBACdM,EAAS,KAAK,YAAa,mCAAmC,EAC9D,KAAK,WAAW,MAAM,YAAc,KAAK,OAAO,kBAIlD,KAAK,OAAO,UAAY,GACxB,KAAK,OAAO,YAAY,KAAK,WAAW,EAGpC,KAAK,OAAO,UACdA,EAAS,KAAK,YAAa,oBAAoB,CAEnD,CAEQ,YAAmB,CACrB,KAAK,OAAO,QAAU,QACxBA,EAAS,KAAK,YAAa,uBAAuB,EAEhD,KAAK,OAAO,mBACdA,EAAS,KAAK,YAAa,4BAA4B,CAE3D,CAEQ,YAAmB,CACzB,MAAMmQ,EAAS,IAAM,CACnBjQ,EAAY,KAAK,YAAa,oBAAoB,EAClD,KAAK,YAAc,GAEnB,KAAK,4BAAA,EAEL,KAAK,mBAAA,EAEL,KAAK,wBAAA,CACP,EAKA,GAHA,KAAK,MAAM,iBAAiB,OAAQiQ,CAAM,EAC1C,KAAK,SAAS,KAAK,IAAM,KAAK,MAAM,oBAAoB,OAAQA,CAAM,CAAC,EAEnE,KAAK,OAAO,UAAY,OAAO,qBAAyB,IAAa,CACvE,MAAMnD,EAAW,IAAI,qBAClBC,GAAY,QACPxM,EAAAwM,EAAQ,CAAC,IAAT,MAAAxM,EAAY,iBACd,KAAK,UAAA,EACLuM,EAAS,WAAA,EAEb,EACA,CAAE,UAAW,EAAA,CAAI,EAEnBA,EAAS,QAAQ,KAAK,WAAW,EACjC,KAAK,SAAS,KAAK,IAAMA,EAAS,YAAY,CAChD,MACE,KAAK,UAAA,CAET,CAEQ,WAAkB,SACxB,IAAIvM,EAAA,KAAK,OAAO,aAAZ,MAAAA,EAAwB,MAAO,CACjC,MAAMkE,EAAiB,KAAK,YAAY,aAAe,IACjD0H,EAAM,OAAO,kBAAoB,EACjCC,IAAY5E,EAAA,KAAK,UAAL,YAAAA,EAAc,YAAa,EAC7C,KAAK,MAAM,IAAM8E,EACf,KAAK,OAAO,IACZ,KAAK,OAAO,WACZ7H,EACA2H,EACAD,CAAA,EAGF,KAAK,kBAAoBQ,GACvB,KAAK,MACL,KAAK,OAAO,IACZ,KAAK,OAAO,WACZ,WAAM,QAAApM,EAAA,KAAK,UAAL,YAAAA,EAAc,YAAa,EAAA,EAEnC,KAAK,kBAAkB,SAAS,QAAQ,KAAK,WAAW,EACxD,KAAK,SAAS,KAAK,IAAA,OAAM,OAAAA,EAAA,KAAK,oBAAL,YAAAA,EAAwB,UAAS,CAC5D,MACE,KAAK,MAAM,IAAM,KAAK,OAAO,GAEjC,CAEQ,cAAqB,CAC3B,UAAWqB,KAAW,KAAK,OAAO,SAChC,KAAK,mBAAmBA,CAAO,CAEnC,CAEQ,mBAAmBA,EAA4B,SAErD,GAAI,KAAK,QAAQ,IAAIA,EAAQ,EAAE,EAAG,CAChC,MAAMsO,EAAY,KAAK,QAAQ,IAAItO,EAAQ,EAAE,EAC7C8B,EAAcwM,CAAS,EACvB,KAAK,QAAQ,OAAOtO,EAAQ,EAAE,GAC9BrB,EAAA,KAAK,SAAS,IAAIqB,EAAQ,EAAE,IAA5B,MAAArB,EAA+B,UAC/B,KAAK,SAAS,OAAOqB,EAAQ,EAAE,EAC/B,MAAMuO,EAAM,KAAK,gBAAgB,IAAIvO,EAAQ,EAAE,EAC3CuO,IAAOA,EAAI,QAAStI,GAAOA,EAAA,CAAI,EAAG,KAAK,gBAAgB,OAAOjG,EAAQ,EAAE,GAC5E,MAAMwO,EAAO,KAAK,WAAW,IAAIxO,EAAQ,EAAE,EACvCwO,IAAQA,EAAK,QAAA,EAAW,KAAK,WAAW,OAAOxO,EAAQ,EAAE,EAC/D,CAGA,KAAM,CAAE,EAAAI,EAAG,EAAAC,CAAA,EAAMF,EACfH,EAAQ,EACRA,EAAQ,EACR,KAAK,MAAM,cAAgB,IAC3B,KAAK,MAAM,eAAiB,GAAA,EAGxBsF,EAAgC,CAAE,GAAGtF,EAAS,EAAAI,EAAG,EAAAC,CAAA,EACvD,KAAK,mBAAmB,IAAIL,EAAQ,GAAIsF,CAAU,EAGlD,MAAM3E,EAAQ,KAAK,OAAO,QAAU,GAC9BC,EAASF,GAAa4E,EAAY3E,CAAK,EAC7C,KAAK,QAAQ,IAAIX,EAAQ,GAAIY,CAAM,EACnC,KAAK,UAAU,YAAYA,CAAM,EAEjC,MAAM6N,EAAczO,EAAQ,SAAW,KAAK,OAAO,SAAW,QAG9D,GAAIA,EAAQ,WAAY,CACtB,MAAM0O,EAAiB,KAAK,sBAAsB1O,CAAO,EAGzD,GAFmB,CAAC,EAAE0O,EAAe,MAAQA,EAAe,SAAW,KAAK,OAAO,eAEnE,CACd,MAAMzL,EAAYjD,EAAQ,WAAa,KAAK,OAAO,WAAa,MAC1DiM,EAAU,IAAI1G,EAAQmJ,EAAgB,CAC1C,UAAAzL,EACA,YAAa,QACb,SAAU,KAAK,OAAO,cACtB,OAAQ,KAAK,OAAO,OACpB,QAAS,KAAK,OAAO,OAAA,CACtB,EACD,KAAK,SAAS,IAAIjD,EAAQ,GAAIiM,CAAO,EACrCA,EAAQ,MAAM,KAAK,YAAarL,CAAM,EACtC,KAAK,oBAAoBZ,EAASY,EAAQqL,CAAO,CACnD,MACE,KAAK,oBAAoBjM,EAASY,CAAM,EAE1C,MACF,CAGA,MAAMqC,EAAYjD,EAAQ,WAAa,KAAK,OAAO,WAAa,MAE1DiM,EAAU,IAAI1G,EAAQvF,EAAS,CACnC,UAAAiD,EACA,YAAAwL,EACA,SAAU,KAAK,OAAO,cACtB,OAAQ,KAAK,OAAO,OACpB,QAAS,KAAK,OAAO,OAAA,CACtB,EACD,KAAK,SAAS,IAAIzO,EAAQ,GAAIiM,CAAO,EACrCA,EAAQ,MAAM,KAAK,YAAarL,CAAM,EAGtC,KAAK,YAAYZ,EAASY,EAAQqL,EAASwC,CAAW,EAGlDA,IAAgB,QAAU,KAAK,cACjC,KAAK,SAAA,EACLxC,EAAQ,KAAA,EACRvK,EAAgBd,EAAQ,EAAI,EAC5B,KAAK,gBAAgBZ,EAAQ,GAAIiM,EAAQ,QAASrL,CAAM,GACxDgF,EAAA,KAAK,WAAW,IAAI5F,EAAQ,EAAE,IAA9B,MAAA4F,EAAiC,WAErC,CAGQ,sBAAsB5F,EAAmC,CAC/D,GAAIA,EAAQ,MAAQA,EAAQ,QAAS,OAAOA,EAC5C,MAAM2O,EAAY,KAAK,UAAU,IAAI3O,EAAQ,UAAW,EACxD,OAAK2O,EACE,CACL,GAAG3O,EACH,KAAM,CAAE,MAAOA,EAAQ,OAAS2O,EAAU,KAAOA,EAAU,EAAA,CAAG,EAHzC3O,CAKzB,CAKQ,oBAAoBA,EAAsBY,EAA2BqL,EAAyB,CAGpG,GAFA/N,EAAS0C,EAAQ,6BAA6B,EAC9CA,EAAO,UAAYqN,EAAU,mBACzBjO,EAAQ,gBAAkB,KAAM,CAClC,MAAMgB,EAAMJ,EAAO,cAAc,KAAK,EAClCI,IAAKA,EAAI,MAAM,UAAY,UAAUhB,EAAQ,cAAc,OACjE,CACA,MAAM4O,EAAa5O,EAAQ,OAASA,EAAQ,WAK5C,GAJAY,EAAO,aAAa,aAAc,eAAegO,CAAU,EAAE,EAC7DhO,EAAO,aAAa,uBAAwB,oBAAoB,EAG5DqL,EAAS,CACX,MAAM4C,EAAehQ,EAAY+B,EAAQ,aAAc,IAAM,CAC3D,KAAK,kBAAkBZ,EAAQ,UAAW,EAC1CiM,EAAQ,eAAA,EACRA,EAAQ,KAAA,EACRvK,EAAgBd,EAAQ,EAAI,CAC9B,CAAC,EAEKkO,EAAejQ,EAAY+B,EAAQ,aAAc,IAAM,CAC3DqL,EAAQ,aAAa,GAAG,EACxB,KAAK,eAAe,IAAM,CACnBA,EAAQ,aACXvK,EAAgBd,EAAQ,EAAK,CAEjC,EAAG,GAAG,CACR,CAAC,EAEKmO,EAAelQ,EAAY+B,EAAQ,QAAS,IAAM,CACtD,KAAK,kBAAkBZ,EAAQ,UAAW,EAC1CiM,EAAQ,eAAA,EACRA,EAAQ,KAAA,EACRvK,EAAgBd,EAAQ,EAAI,CAC9B,CAAC,EAEKoO,EAAcnQ,EAAY+B,EAAQ,OAAQ,IAAM,CACpDqL,EAAQ,aAAa,GAAG,EACxB,KAAK,eAAe,IAAM,CACnBA,EAAQ,UAAA,GAAavK,EAAgBd,EAAQ,EAAK,CACzD,EAAG,GAAG,CACR,CAAC,EAED,KAAK,mBAAmBZ,EAAQ,GAAI6O,EAAcC,EAAcC,EAAcC,CAAW,CAC3F,KAAO,CAEL,MAAMC,EAAepQ,EAAY+B,EAAQ,aAAc,IAAM,CAC3D,KAAK,kBAAkBZ,EAAQ,UAAW,CAC5C,CAAC,EACKkP,EAAerQ,EAAY+B,EAAQ,QAAS,IAAM,CACtD,KAAK,kBAAkBZ,EAAQ,UAAW,CAC5C,CAAC,EACD,KAAK,mBAAmBA,EAAQ,GAAIiP,EAAcC,CAAY,CAChE,CAGA,MAAMlB,EAAenP,EAAY+B,EAAQ,QAAU2F,GAAM,WACvDA,EAAE,gBAAA,EACF0F,GAAA,MAAAA,EAAS,OACTvK,EAAgBd,EAAQ,EAAK,GAC7BgF,GAAAjH,EAAA,KAAK,QAAO,UAAZ,MAAAiH,EAAA,KAAAjH,EAAsB4H,EAAGvG,IACzBkG,EAAAlG,EAAQ,UAAR,MAAAkG,EAAA,KAAAlG,EAAkBuG,EAAGvG,GACrB,KAAK,UAAUA,EAAQ,UAAW,CACpC,CAAC,EAGKmP,EAAatQ,EAAY+B,EAAQ,UAAY2F,GAAM,YACnDA,EAAE,MAAQ,SAAWA,EAAE,MAAQ,OACjCA,EAAE,eAAA,EACF0F,GAAA,MAAAA,EAAS,OACTvK,EAAgBd,EAAQ,EAAK,GAC7BgF,GAAAjH,EAAA,KAAK,QAAO,UAAZ,MAAAiH,EAAA,KAAAjH,EAAsB4H,EAAGvG,IACzBkG,EAAAlG,EAAQ,UAAR,MAAAkG,EAAA,KAAAlG,EAAkBuG,EAAGvG,GACrB,KAAK,UAAUA,EAAQ,UAAW,EAEtC,CAAC,EAED,KAAK,mBAAmBA,EAAQ,GAAIgO,EAAcmB,CAAU,CAC9D,CAEQ,YACNnP,EACAY,EACAqL,EACAwC,EACM,CACFA,IAAgB,QAClB,KAAK,iBAAiBzO,EAASY,EAAQqL,CAAO,GACrCwC,IAAgB,SAAWA,IAAgB,SACpD,KAAK,iBAAiBzO,EAASY,EAAQqL,CAAO,EAEhD,KAAK,oBAAoBjM,EAASY,EAAQqL,EAASwC,CAAW,CAChE,CAEQ,iBAAiBzO,EAAsBY,EAA2BqL,EAAwB,CAChG,MAAM4C,EAAehQ,EAAY+B,EAAQ,aAAc,IAAM,CAC3DqL,EAAQ,eAAA,EACRA,EAAQ,KAAA,EACRvK,EAAgBd,EAAQ,EAAI,CAC9B,CAAC,EAEKkO,EAAejQ,EAAY+B,EAAQ,aAAc,IAAM,CAC3DqL,EAAQ,aAAa,GAAG,EACxB,KAAK,eAAe,IAAM,CACnBA,EAAQ,UAAA,GAAavK,EAAgBd,EAAQ,EAAK,CACzD,EAAG,GAAG,CACR,CAAC,EAED,KAAK,mBAAmBZ,EAAQ,GAAI6O,EAAcC,CAAY,CAChE,CAEQ,iBAAiB9O,EAAsBY,EAA2BqL,EAAwB,CAChG,MAAMuC,EAAOxC,EAAgBC,EAAQ,QAASrL,CAAM,EACpD,KAAK,WAAW,IAAIZ,EAAQ,GAAIwO,CAAI,EAEpC,MAAMR,EAAenP,EAAY+B,EAAQ,QAAU2F,GAAM,WACvDA,EAAE,gBAAA,GACFX,GAAAjH,EAAA,KAAK,QAAO,UAAZ,MAAAiH,EAAA,KAAAjH,EAAsB4H,EAAGvG,IACzBkG,EAAAlG,EAAQ,UAAR,MAAAkG,EAAA,KAAAlG,EAAkBuG,EAAGvG,GAEjBiM,EAAQ,aACVA,EAAQ,KAAA,EACRvK,EAAgBd,EAAQ,EAAK,EAC7B4N,EAAK,WAAA,IAEL,KAAK,SAAA,EACLvC,EAAQ,KAAA,EACRvK,EAAgBd,EAAQ,EAAI,EAC5B4N,EAAK,SAAA,EAET,CAAC,EAEKY,EAAiBvQ,EAAY,SAAU,QAAU0H,GAAM,CAEzD0F,EAAQ,aACR,CAACjM,EAAQ,UACT,CAACiM,EAAQ,QAAQ,SAAS1F,EAAE,MAAc,GAC1C,CAAC3F,EAAO,SAAS2F,EAAE,MAAc,IAEjC0F,EAAQ,KAAA,EACRvK,EAAgBd,EAAQ,EAAK,EAC7B4N,EAAK,WAAA,EAET,CAAC,EAED,KAAK,mBAAmBxO,EAAQ,GAAIgO,EAAcoB,CAAc,CAClE,CAEQ,oBACNpP,EACAY,EACAqL,EACAwC,EACM,CACN,MAAMM,EAAelQ,EAAY+B,EAAQ,QAAS,IAAM,CAClD6N,IAAgB,UAClBxC,EAAQ,eAAA,EACRA,EAAQ,KAAA,EACRvK,EAAgBd,EAAQ,EAAI,EAEhC,CAAC,EAEKoO,EAAcnQ,EAAY+B,EAAQ,OAAQ,IAAM,CAChD6N,IAAgB,UAClBxC,EAAQ,aAAa,GAAG,EACxB,KAAK,eAAe,IAAM,CACnBA,EAAQ,UAAA,GAAavK,EAAgBd,EAAQ,EAAK,CACzD,EAAG,GAAG,EAEV,CAAC,EAEKuO,EAAatQ,EAAY+B,EAAQ,UAAY2F,GAAM,eACnDA,EAAE,MAAQ,SAAWA,EAAE,MAAQ,KACjCA,EAAE,eAAA,GACFX,GAAAjH,EAAA,KAAK,QAAO,UAAZ,MAAAiH,EAAA,KAAAjH,EAAsB4H,EAAGvG,GACrBiM,EAAQ,aACVA,EAAQ,KAAA,EACRvK,EAAgBd,EAAQ,EAAK,GAC7BsF,EAAA,KAAK,WAAW,IAAIlG,EAAQ,EAAE,IAA9B,MAAAkG,EAAiC,eAEjC,KAAK,SAAA,EACL+F,EAAQ,KAAA,EACRvK,EAAgBd,EAAQ,EAAI,EAC5B,KAAK,gBAAgBZ,EAAQ,GAAIiM,EAAQ,QAASrL,CAAM,GACxDkG,EAAA,KAAK,WAAW,IAAI9G,EAAQ,EAAE,IAA9B,MAAA8G,EAAiC,aAE1BP,EAAE,MAAQ,UACf0F,EAAQ,cACVA,EAAQ,KAAA,EACRvK,EAAgBd,EAAQ,EAAK,GAC7BsG,EAAA,KAAK,WAAW,IAAIlH,EAAQ,EAAE,IAA9B,MAAAkH,EAAiC,aACjCtG,EAAO,MAAA,EAGb,CAAC,EAED,KAAK,mBAAmBZ,EAAQ,GAAI+O,EAAcC,EAAaG,CAAU,CAC3E,CAEQ,6BAAoC,CAC1C,MAAM7O,EAAe,KAAK,MAAM,aAC1BC,EAAgB,KAAK,MAAM,cACjC,GAAI,GAACD,GAAgB,CAACC,IAEtB,UAAWP,KAAW,KAAK,OAAO,SAChC,GAAI,OAAOA,EAAQ,GAAM,UAAY,OAAOA,EAAQ,GAAM,SAAU,CAClE,KAAM,CAAE,EAAAI,EAAG,EAAAC,CAAA,EAAMF,EAAmBH,EAAQ,EAAGA,EAAQ,EAAGM,EAAcC,CAAa,EAC/EK,EAAS,KAAK,QAAQ,IAAIZ,EAAQ,EAAE,EACtCY,IACFA,EAAO,MAAM,KAAO,GAAGR,CAAC,IACxBQ,EAAO,MAAM,IAAM,GAAGP,CAAC,KAEzB,MAAMiF,EAAa,KAAK,mBAAmB,IAAItF,EAAQ,EAAE,EACrDsF,IACFA,EAAW,EAAIlF,EACfkF,EAAW,EAAIjF,EAEnB,EAEJ,CAEQ,yBAAgC,OACtC,SAAW,CAACgP,EAAIpD,CAAO,IAAK,KAAK,SAAU,CACzC,MAAMjM,EAAU,KAAK,mBAAmB,IAAIqP,CAAE,EAE9C,KADoBrP,GAAA,YAAAA,EAAS,UAAW,KAAK,OAAO,SAAW,WAC3C,QAAU,CAACiM,EAAQ,YAAa,CAClDA,EAAQ,KAAA,EACR,MAAMrL,EAAS,KAAK,QAAQ,IAAIyO,CAAE,EAC9BzO,IACFc,EAAgBd,EAAQ,EAAI,EAC5B,KAAK,gBAAgByO,EAAIpD,EAAQ,QAASrL,CAAM,GAChDjC,EAAA,KAAK,WAAW,IAAI0Q,CAAE,IAAtB,MAAA1Q,EAAyB,YAE3B,KACF,CACF,CACF,CAEQ,UAAiB,CACnB,KAAK,OAAO,aAAe,KAC7B,KAAK,WAAa,IAAIoL,GAAW,KAAK,WAAW,GAGnD,KAAK,QAAU,IAAIvC,GAAQ,KAAK,WAAY,KAAK,YAAa,CAC5D,QAAS,KAAK,OAAO,SAAW,EAChC,QAAS,KAAK,OAAO,SAAW,EAChC,OAASoB,GAAU,YACjBhD,GAAAjH,EAAA,KAAK,QAAO,SAAZ,MAAAiH,EAAA,KAAAjH,EAAqBiK,IACrB1C,EAAA,KAAK,eAAL,MAAAA,EAAmB,SAEnB,SAAW,CAAA,CAAG+F,CAAO,IAAK,KAAK,SACzBA,EAAQ,aACVA,EAAQ,eAAA,CAGd,EACA,oBAAqB,IAAA,OAAM,OAAAtN,EAAA,KAAK,aAAL,YAAAA,EAAiB,OAAK,CAClD,EAEG,KAAK,OAAO,eAAiB,KAC/B,KAAK,aAAewK,GAAmB,KAAK,YAAa,KAAK,QAAS,CACrE,QAAS,KAAK,OAAO,SAAW,EAChC,QAAS,KAAK,OAAO,SAAW,EAChC,SAAU,KAAK,OAAO,oBAAA,CACvB,EAEL,CAEQ,iBAAwB,CAC9B,GAAI,OAAO,eAAmB,IAAa,OAE3C,IAAImG,EAAQ,EACZ,KAAK,eAAiB,IAAI,eAAe,IAAM,CACzCA,IACJA,EAAQ,sBAAsB,IAAM,CAElC,GADAA,EAAQ,EACJ,KAAK,UAAW,OAEpB,KAAK,mBAAA,EAEL,MAAMzM,EAAiB,KAAK,YAAY,YACxC,SAAW,CAACwM,EAAIrP,CAAO,IAAK,KAAK,mBAC/B,GAAIA,EAAQ,WAAY,CACtB,MAAMY,EAAS,KAAK,QAAQ,IAAIyO,CAAE,EAClC,GAAI,CAACzO,EAAQ,SACb,MAAM2O,EACHvP,EAAQ,WAAW,UAAY6C,EAAiB7C,EAAQ,WAAW,UACnEA,EAAQ,WAAW,UAAY6C,EAAiB7C,EAAQ,WAAW,SACtE4B,GAAgBhB,EAAQ,CAAC,CAAC2O,CAAU,CACtC,CAEJ,CAAC,EACH,CAAC,EACD,KAAK,SAAS,KAAK,IAAM,CAAMD,wBAA4BA,CAAK,CAAG,CAAC,EAEpE,KAAK,eAAe,QAAQ,KAAK,WAAW,EAC5C,KAAK,SAAS,KAAK,IAAA,OAAM,OAAA3Q,EAAA,KAAK,iBAAL,YAAAA,EAAqB,aAAY,CAC5D,CAGQ,oBAA2B,CACjC,GAAI,CAAC,KAAK,OAAO,iBAAkB,OAEnC,MAAM6Q,EAAK,KAAK,WAAW,YACrBC,EAAK,KAAK,WAAW,aACrBC,EAAK,KAAK,MAAM,aAChBC,EAAK,KAAK,MAAM,cACtB,GAAI,CAACH,GAAM,CAACC,GAAM,CAACC,GAAM,CAACC,EAAI,OAE9B,MAAMC,EAAgBJ,EAAKC,EACrBI,EAAaH,EAAKC,EAExB,IAAIG,EACAC,EACAC,EACAC,EAEAJ,EAAaD,GAEfE,EAAgBN,EAChBO,EAAiBP,EAAKK,EACtBG,EAAU,EACVC,GAAWR,EAAKM,GAAkB,IAGlCA,EAAiBN,EACjBK,EAAgBL,EAAKI,EACrBG,GAAWR,EAAKM,GAAiB,EACjCG,EAAU,GAGZ,KAAK,UAAU,MAAM,KAAO,GAAGD,CAAO,KACtC,KAAK,UAAU,MAAM,IAAM,GAAGC,CAAO,KACrC,KAAK,UAAU,MAAM,MAAQ,GAAGH,CAAa,KAC7C,KAAK,UAAU,MAAM,OAAS,GAAGC,CAAc,KAE/C,KAAK,UAAU,MAAM,MAAQ,OAC7B,KAAK,UAAU,MAAM,OAAS,MAChC,CAEQ,YAAmB,CACzB,UAAWhQ,KAAS,KAAK,OAAO,OAC9B,KAAK,UAAU,IAAIA,EAAM,GAAIA,CAAK,EAEpC,MAAMmQ,EAAY,KAAK,OAAO,cAAgB,KAAK,OAAO,OAAQ,CAAC,EAAE,GAC/DC,EAAe,KAAK,UAAU,IAAID,CAAS,EACjD,KAAK,OAAO,IAAMC,EAAa,IAC/B,KAAK,OAAO,IAAMA,EAAa,KAAO,GACtC,KAAK,OAAO,SAAW,CAAC,GAAGA,EAAa,QAAQ,EAChD,KAAK,eAAiBD,CACxB,CAEQ,cAAqB,CAC3B,KAAK,gBAAkB,IAAI1E,GAAgB,CACzC,UAAW,KAAK,YAChB,WAAY,IAAM,KAAK,QACvB,SAAU,IAAM,OAEd,IAAI7M,EAAA,KAAK,oBAAL,MAAAA,EAAwB,eAAgB,CAC1C,KAAK,kBAAkB,KAAA,EACvB,MACF,CACA,KAAK,SAAA,CACP,EACA,mBAAoB,IAAM,QACxBA,EAAA,KAAK,oBAAL,MAAAA,EAAwB,QAC1B,CAAA,CACD,CACH,CAEQ,gBAAuB,CACzB,KAAK,OAAO,mBAAqB,KAErC,KAAK,kBAAoB2O,GAAwB,KAAK,YAAa,CACjE,SAAW8C,GAAiB,SAE1B,sBAAsB,IAAM,CAC1B,SAAW,CAAA,CAAGnE,CAAO,IAAK,KAAK,SACzBA,EAAQ,aACVA,EAAQ,eAAA,CAGd,CAAC,GACDrG,GAAAjH,EAAA,KAAK,QAAO,qBAAZ,MAAAiH,EAAA,KAAAjH,EAAiCyR,EACnC,CAAA,CACD,EACH,CAIQ,kBAAkBC,EAAuB,OAC/C,GAAI,KAAK,gBAAgB,IAAIA,CAAO,EAAG,OACvC,MAAMtQ,EAAQ,KAAK,UAAU,IAAIsQ,CAAO,EACxC,GAAI,CAACtQ,EAAO,OACZ,KAAK,gBAAgB,IAAIsQ,CAAO,EAChC,MAAM7O,EAAM,IAAI,MAChB,IAAI7C,EAAA,KAAK,OAAO,aAAZ,MAAAA,EAAwB,MAAO,CACjC,MAAMkE,EAAiB,KAAK,YAAY,aAAe,IACjD0H,EAAM,OAAO,OAAW,KAAc,OAAO,kBAAoB,EACvE/I,EAAI,IAAMkJ,EAAmB3K,EAAM,IAAK,KAAK,OAAO,WAAY8C,EAAgB,EAAG0H,CAAG,CACxF,MACE/I,EAAI,IAAMzB,EAAM,GAEpB,CAEQ,gBAAgBsP,EAAYpN,EAAwBD,EAAmC,CACxF,KAAK,WAAW,IAAIqN,CAAE,GACzB,KAAK,WAAW,IAAIA,EAAIrD,EAAgB/J,EAAWD,CAAQ,CAAC,CAEhE,CAGQ,eAAeiE,EAAgBJ,EAAqB,CAC1D,MAAMwJ,EAAK,WAAW,IAAM,CAC1B,KAAK,aAAa,OAAOA,CAAE,EAC3BpJ,EAAA,CACF,EAAGJ,CAAK,EACR,KAAK,aAAa,IAAIwJ,CAAE,CAC1B,CAGQ,0BAAiC,CACnC,CAAC,KAAK,gBAAkB,KAAK,iBACjC,KAAK,sBAAsB,IAAI,KAAK,eAAgB,CAAC,GAAG,KAAK,OAAO,QAAQ,CAAC,CAC/E,CAEQ,mBAAmBA,KAAed,EAA2B,CACnE,IAAI+B,EAAM,KAAK,gBAAgB,IAAIjB,CAAE,EAChCiB,IACHA,EAAM,CAAA,EACN,KAAK,gBAAgB,IAAIjB,EAAIiB,CAAG,GAElCA,EAAI,KAAK,GAAG/B,CAAG,CACjB,CAEQ,eAAsB,CAC5B,UAAWA,KAAO,KAAK,gBAAgB,OAAA,EACrCA,EAAI,QAAStI,GAAOA,EAAA,CAAI,EAE1B,KAAK,gBAAgB,MAAA,EAErB,SAAW,CAAA,CAAGgG,CAAO,IAAK,KAAK,SAC7BA,EAAQ,QAAA,EAEV,KAAK,SAAS,MAAA,EAEd,SAAW,CAAA,CAAGrL,CAAM,IAAK,KAAK,QAC5BkB,EAAclB,CAAM,EAEtB,KAAK,QAAQ,MAAA,EACb,KAAK,mBAAmB,MAAA,EAExB,SAAW,CAAA,CAAG4N,CAAI,IAAK,KAAK,WAC1BA,EAAK,QAAA,EAEP,KAAK,WAAW,MAAA,CAClB,CAGQ,4BAAqC,CAC3C,GAAI,OAAO,iBAAqB,IAAa,MAAO,KACpD,MAAM+B,EAAM,iBAAiB,KAAK,WAAW,EAAE,iBAAiB,wCAAwC,EAAE,KAAA,EACpG7Q,EAAM,WAAW6Q,CAAG,EAC1B,OAAI,MAAM7Q,CAAG,EAAU,IAEnB6Q,EAAI,SAAS,GAAG,GAAK,CAACA,EAAI,SAAS,IAAI,EAAU7Q,EAAM,IACpDA,CACT,CAEQ,uBACNK,EACAyQ,EACAC,EACAC,EACM,OACN,GAAIF,IAAe,OAAQ,CACzB,KAAK,cAAA,EACL,KAAK,cAAczQ,CAAK,EACxB2Q,EAAA,EACA,MACF,CAEA,MAAMC,EAAqB,KAAK,2BAAA,EAEhCzS,EAAS,KAAK,YAAa,gCAAgC,EAE3D,MAAM0S,EAAchT,EAAc,MAAO,4BAA6B,CACpE,IAAKmC,EAAM,KAAO,GAClB,UAAW,OAAA,CACZ,EAED,IAAIpB,EAAA,KAAK,OAAO,aAAZ,MAAAA,EAAwB,MAAO,CACjC,MAAMkE,EAAiB,KAAK,YAAY,aAAe,IACjD0H,EAAM,OAAO,OAAW,KAAc,OAAO,kBAAoB,EACvEqG,EAAY,IAAMlG,EAAmB3K,EAAM,IAAK,KAAK,OAAO,WAAY8C,EAAgB,EAAG0H,CAAG,CAChG,MACEqG,EAAY,IAAM7Q,EAAM,IAG1B,MAAM8Q,EAAe,IAAM,CACzB,GAAI,MAAK,UAET,IAAIL,IAAe,OACjBtS,EAAS0S,EAAa,0BAA0B,EAChD1S,EAAS,KAAK,MAAO,2BAA2B,UACvCsS,IAAe,QAAS,CACjC,MAAMM,EAASL,EAAe,WAAa,GAC3CvS,EAAS0S,EAAa,4BAA4BE,CAAM,EAAE,EAC1D5S,EAAS,KAAK,MAAO,6BAA6B4S,CAAM,EAAE,CAC5D,CAEA,KAAK,WAAW,aAAaF,EAAa,KAAK,SAAS,EAExD,KAAK,gBAAkB,WAAW,IAAM,CAEtC,GADA,KAAK,gBAAkB,OACnB,KAAK,UAAW,OAEpB,KAAK,cAAA,EACL,KAAK,cAAc7Q,CAAK,EAExB,MAAMgR,EAAS,IAAM,CACnBH,EAAY,OAAA,EACZxS,EAAY,KAAK,MAAO,2BAA2B,EACnDA,EAAY,KAAK,MAAO,4BAA4B,EACpDA,EAAY,KAAK,MAAO,oCAAoC,EAC5DA,EAAY,KAAK,YAAa,gCAAgC,EAC9DsS,EAAA,CACF,EAEI,KAAK,MAAM,UAAY,KAAK,MAAM,aAAe,EACnDK,EAAA,GAEA,KAAK,MAAM,iBAAiB,OAAQA,EAAQ,CAAE,KAAM,GAAM,EAC1D,KAAK,MAAM,iBAAiB,QAASA,EAAQ,CAAE,KAAM,GAAM,EAE/D,EAAGJ,CAAkB,EACvB,EAEIC,EAAY,SACdC,EAAA,GAEA3S,EAAS,KAAK,YAAa,0BAA0B,EACrD0S,EAAY,OAAS,IAAM,CACzBxS,EAAY,KAAK,YAAa,0BAA0B,EACxDyS,EAAA,CACF,EACAD,EAAY,QAAU,IAAM,CACtB,KAAK,YACTxS,EAAY,KAAK,YAAa,0BAA0B,EACxDwS,EAAY,OAAA,EACZxS,EAAY,KAAK,YAAa,gCAAgC,EAC9D,KAAK,cAAA,EACL,KAAK,cAAc2B,CAAK,EACxB2Q,EAAA,EACF,EAEJ,CAEQ,cAAc3Q,EAAoB,OACxC,KAAK,OAAO,IAAMA,EAAM,IACxB,KAAK,OAAO,IAAMA,EAAM,KAAO,GAC/B,KAAK,OAAO,SAAW,KAAK,sBAAsB,IAAIA,EAAM,EAAE,GAAK,CAAC,GAAGA,EAAM,QAAQ,EAErF,KAAK,MAAM,IAAMA,EAAM,KAAO,GAE9B,KAAK,YAAY,aAAa,aAAcA,EAAM,KAAO,qBAAqB,EAE9E,KAAK,YAAc,GACnB,MAAMsO,EAAS,IAAM,CACnB,KAAK,YAAc,GACnB,KAAK,4BAAA,EACL,KAAK,mBAAA,EACL,KAAK,wBAAA,CACP,EAIA,GAFA,KAAK,MAAM,iBAAiB,OAAQA,EAAQ,CAAE,KAAM,GAAM,GAEtD1P,EAAA,KAAK,OAAO,aAAZ,MAAAA,EAAwB,MAAO,CACjC,MAAMkE,EAAiB,KAAK,YAAY,aAAe,IACjD0H,EAAM,OAAO,OAAW,KAAc,OAAO,kBAAoB,EACvE,KAAK,MAAM,IAAMG,EAAmB3K,EAAM,IAAK,KAAK,OAAO,WAAY8C,EAAgB,EAAG0H,CAAG,CAC/F,MACE,KAAK,MAAM,IAAMxK,EAAM,IAKrB,KAAK,MAAM,UAAY,KAAK,MAAM,aAAe,GAAK,CAAC,KAAK,cAC9D,KAAK,MAAM,oBAAoB,OAAQsO,CAAM,EAC7CA,EAAA,GAGF,KAAK,aAAA,CACP,CAKA,aAKE,CACA,MAAO,CACL,UAAW,KAAK,YAChB,SAAU,KAAK,WACf,MAAO,KAAK,MACZ,QAAS,KAAK,SAAA,CAElB,CAEA,KAAKgB,EAAkB,OACrB,GAAI,KAAK,UAAW,OACpB,MAAMpD,EAAU,KAAK,SAAS,IAAIoD,CAAE,EAC9BzO,EAAS,KAAK,QAAQ,IAAIyO,CAAE,EAC9B,CAACpD,GAAW,CAACrL,GACbqL,EAAQ,cACZ,KAAK,SAAA,EACLA,EAAQ,KAAA,EACRvK,EAAgBd,EAAQ,EAAI,GAC5BjC,EAAA,KAAK,WAAW,IAAI0Q,CAAE,IAAtB,MAAA1Q,EAAyB,WAC3B,CAEA,MAAM0Q,EAAkB,OACtB,GAAI,KAAK,UAAW,OACpB,MAAMpD,EAAU,KAAK,SAAS,IAAIoD,CAAE,EAC9BzO,EAAS,KAAK,QAAQ,IAAIyO,CAAE,EAC9BpD,GAAWrL,IACbqL,EAAQ,KAAA,EACRvK,EAAgBd,EAAQ,EAAK,GAC7BjC,EAAA,KAAK,WAAW,IAAI0Q,CAAE,IAAtB,MAAA1Q,EAAyB,aAE7B,CAEA,UAAiB,OACf,GAAI,MAAK,WACT,SAAW,CAAC0Q,EAAIpD,CAAO,IAAK,KAAK,SAC/B,GAAIA,EAAQ,YAAa,CACvBA,EAAQ,KAAA,EACR,MAAMrL,EAAS,KAAK,QAAQ,IAAIyO,CAAE,EAC9BzO,GAAQc,EAAgBd,EAAQ,EAAK,GACzCjC,EAAA,KAAK,WAAW,IAAI0Q,CAAE,IAAtB,MAAA1Q,EAAyB,YAC3B,EAEJ,CAEA,QAAQiK,EAAqB,OACvB,KAAK,YACTjK,EAAA,KAAK,UAAL,MAAAA,EAAc,QAAQiK,EACxB,CAEA,SAAkB,OAChB,QAAOjK,EAAA,KAAK,UAAL,YAAAA,EAAc,YAAa,CACpC,CAEA,WAAkB,OACZ,KAAK,YACTA,EAAA,KAAK,UAAL,MAAAA,EAAc,WAChB,CAEA,UAAU0R,EAAuB,CAI/B,GAHI,KAAK,WACL,KAAK,iBACL,CAAC,KAAK,UAAU,MAChBA,IAAY,KAAK,eAAgB,OAErC,MAAMtQ,EAAQ,KAAK,UAAU,IAAIsQ,CAAO,EACxC,GAAI,CAACtQ,EAAO,OAEZ,MAAMyQ,EAAa,KAAK,OAAO,iBAAmB,OAClD,KAAK,gBAAkB,GAEnB,KAAK,SAAW,KAAK,QAAQ,QAAA,EAAY,GAC3C,KAAK,QAAQ,UAAA,EAIf,IAAIC,EAAe,GACnB,GAAID,IAAe,SACjB,UAAWxQ,KAAW,KAAK,OAAO,SAChC,GAAIA,EAAQ,aAAeqQ,EAAS,CAClC,MAAM/K,EAAa,KAAK,mBAAmB,IAAItF,EAAQ,EAAE,EACrDsF,GAAcA,EAAW,GAAK,KAChCmL,EAAe,IAEjB,KACF,EAKJ,KAAK,yBAAA,EACL,KAAK,eAAiBJ,EAEtB,KAAK,uBAAuBtQ,EAAOyQ,EAAYC,EAAc,IAAM,SACjE,KAAK,gBAAkB,GAEvB7D,GAAuB,gBAAgB7M,EAAM,KAAOsQ,CAAO,EAAE,GAC7DzK,GAAAjH,EAAA,KAAK,QAAO,gBAAZ,MAAAiH,EAAA,KAAAjH,EAA4B0R,EAAStQ,GAGrC,MAAMiR,EAAejR,EAAM,SAAS,CAAC,EACrC,GAAIiR,EAAc,CAChB,MAAMC,EAAc,KAAK,QAAQ,IAAID,EAAa,EAAE,EAChDC,GAAe,SAAS,eAAiB,KAAK,YAAY,SAAS,SAAS,aAAa,GAC3FA,EAAY,MAAA,CAEhB,CACF,CAAC,CACH,CAEA,iBAAsC,CACpC,OAAO,KAAK,cACd,CAEA,WAAsB,CACpB,OAAO,MAAM,KAAK,KAAK,UAAU,MAAM,CACzC,CAEA,iBAAwB,OAClB,KAAK,YACTtS,EAAA,KAAK,oBAAL,MAAAA,EAAwB,OAC1B,CAEA,gBAAuB,OACjB,KAAK,YACTA,EAAA,KAAK,oBAAL,MAAAA,EAAwB,MAC1B,CAEA,cAAwB,OACtB,QAAOA,EAAA,KAAK,oBAAL,YAAAA,EAAwB,iBAAkB,EACnD,CAEA,WAAWqB,EAA4B,CACjC,KAAK,YACT,KAAK,OAAO,SAAS,KAAKA,CAAO,EACjC,KAAK,mBAAmBA,CAAO,EAC/B,KAAK,yBAAA,EACP,CAEA,cAAcqP,EAAkB,CAC9B,GAAI,KAAK,UAAW,OAGpB,MAAMd,EAAM,KAAK,gBAAgB,IAAIc,CAAE,EACnCd,IACFA,EAAI,QAAStI,GAAOA,EAAA,CAAI,EACxB,KAAK,gBAAgB,OAAOoJ,CAAE,GAIhC,MAAMb,EAAO,KAAK,WAAW,IAAIa,CAAE,EAC/Bb,IACFA,EAAK,QAAA,EACL,KAAK,WAAW,OAAOa,CAAE,GAG3B,MAAMzO,EAAS,KAAK,QAAQ,IAAIyO,CAAE,EAC5BpD,EAAU,KAAK,SAAS,IAAIoD,CAAE,EAEhCpD,IACFA,EAAQ,QAAA,EACR,KAAK,SAAS,OAAOoD,CAAE,GAErBzO,IACFkB,EAAclB,CAAM,EACpB,KAAK,QAAQ,OAAOyO,CAAE,GAExB,KAAK,mBAAmB,OAAOA,CAAE,EACjC,KAAK,OAAO,SAAW,KAAK,OAAO,SAAS,OAAQ6B,GAAMA,EAAE,KAAO7B,CAAE,EACrE,KAAK,yBAAA,CACP,CAEA,cAAcA,EAAY8B,EAAqC,CAC7D,GAAI,KAAK,UAAW,OACpB,MAAMC,EAAM,KAAK,OAAO,SAAS,UAAWF,GAAMA,EAAE,KAAO7B,CAAE,EAC7D,GAAI+B,IAAQ,GAAI,OAGhB,MAAM9C,EAAY,KAAK,QAAQ,IAAIe,CAAE,EAC/BgC,GAAc/C,GAAA,YAAAA,EAAW,qBAAsB,KAI/CgD,EAAU,CAAE,GADF,KAAK,OAAO,SAASF,CAAG,EACV,GAAGD,CAAA,EACjC,KAAK,cAAc9B,CAAE,EACrB,KAAK,OAAO,SAAS,OAAO+B,EAAK,EAAGE,CAAO,EAC3C,KAAK,mBAAmBA,CAAO,EAG/B,MAAMC,EAAY,KAAK,QAAQ,IAAIlC,CAAE,EACjCkC,GAAaF,GAAe,KAAK,UAAU,SAASA,CAAW,GACjE,KAAK,UAAU,aAAaE,EAAWF,CAAW,EAEpD,KAAK,yBAAA,CACP,CAEA,OAAOlS,EAAwC,CACzC,KAAK,YAET,KAAK,gBAAA,EACL2N,EAAA,EACA,KAAK,OAASnN,EAAY,CAAE,GAAG,KAAK,OAAQ,GAAGR,EAAQ,EACvDU,EAAe,KAAK,MAAM,EAEtB,KAAK,OAAO,QAAU,KAAK,OAAO,OAAO,OAAS,GACpD,KAAK,WAAA,EAGP,KAAK,SAAA,EACL,KAAK,WAAA,EACL,KAAK,WAAA,EACL,KAAK,aAAA,EACD,KAAK,OAAO,MACd,KAAK,SAAA,EAEP,KAAK,aAAA,EACL,KAAK,eAAA,EACL,KAAK,gBAAA,EACP,CAEA,SAAgB,CACV,KAAK,YACT,KAAK,UAAY,GACjB,KAAK,gBAAA,EACL,KAAK,OAAO,UAAY,GAC1B,CAEQ,iBAAwB,mBAC9B,KAAK,YAAc,GAGnB,UAAWwP,KAAM,KAAK,aAAc,aAAaA,CAAE,EACnD,KAAK,aAAa,MAAA,EAGlB,UAAWd,KAAO,KAAK,gBAAgB,OAAA,EACrCA,EAAI,QAAStI,GAAOA,EAAA,CAAI,EAE1B,KAAK,gBAAgB,MAAA,EACrB,KAAK,SAAS,QAASA,GAAOA,GAAI,EAClC,KAAK,SAAW,CAAA,EAGhB,SAAW,CAAA,CAAGgG,CAAO,IAAK,KAAK,SAC7BA,EAAQ,QAAA,EAEV,KAAK,SAAS,MAAA,EAGd,SAAW,CAAA,CAAGrL,CAAM,IAAK,KAAK,QAC5BkB,EAAclB,CAAM,EAEtB,KAAK,QAAQ,MAAA,EACb,KAAK,mBAAmB,MAAA,EAGxB,SAAW,CAAA,CAAG4N,CAAI,IAAK,KAAK,WAC1BA,EAAK,QAAA,EAEP,KAAK,WAAW,MAAA,EAGhB,KAAK,UAAU,MAAA,EACf,KAAK,gBAAgB,MAAA,EACrB,KAAK,sBAAsB,MAAA,EAC3B,KAAK,eAAiB,OACtB,KAAK,gBAAkB,GACnB,KAAK,kBAAoB,SAC3B,aAAa,KAAK,eAAe,EACjC,KAAK,gBAAkB,SAIzB7P,EAAA,KAAK,oBAAL,MAAAA,EAAwB,UACxB,KAAK,kBAAoB,MAGzBiH,EAAA,KAAK,kBAAL,MAAAA,EAAsB,UACtB,KAAK,gBAAkB,MAGvBM,EAAA,KAAK,UAAL,MAAAA,EAAc,UACd,KAAK,QAAU,MACfY,EAAA,KAAK,eAAL,MAAAA,EAAmB,UACnB,KAAK,aAAe,MACpBI,EAAA,KAAK,aAAL,MAAAA,EAAiB,UACjB,KAAK,WAAa,MAGlBD,EAAA,KAAK,oBAAL,MAAAA,EAAwB,UACxB,KAAK,kBAAoB,MAGzBuK,EAAA,KAAK,iBAAL,MAAAA,EAAqB,aACrB,KAAK,eAAiB,KAGtBzE,GAAA,CACF,CACF,EAx4BEkB,EAAwB,mBACtB,8LAhRG,IAAMwD,EAANxD,ECbP,MAAMyD,EAAQ,CACZ,OAAQ,4TACR,KAAM,4KACN,KAAM,iOACN,KAAM,gOACN,MAAO,qSACP,SAAU,sOACV,OAAQ,sOACR,KAAM,+PACN,MAAO,6QACT,EAEO,MAAMC,EAAc,CAYzB,YACUC,EACAC,EACR,CAFQ,KAAA,SAAAD,EACA,KAAA,OAAAC,EAER,KAAK,UAAYjU,EAAc,MAAO,mBAAmB,EACzD,KAAK,SAAWA,EAAc,MAAO,mBAAmB,EACxD,KAAK,MAAA,EAEL,MAAMkU,EAAa,KAAK,SAAS,WACjC,KAAK,SAAS,aAAa,KAAK,UAAWA,CAAU,EACrD,KAAK,SAAS,aAAa,KAAK,SAAUA,CAAU,CACtD,CAEQ,OAAc,CAEpB,MAAMC,EAAYnU,EAAc,MAAO,yBAAyB,EAChE,KAAK,UAAY,KAAK,UAAU8T,EAAM,OAAQ,SAAU,IAAM,KAAK,OAAO,QAAQ,QAAQ,CAAC,EAC3F,KAAK,OAAS,KAAK,UAAUA,EAAM,KAAM,MAAO,IAAM,KAAK,OAAO,QAAQ,KAAK,CAAC,EAChFK,EAAU,YAAY,KAAK,SAAS,EACpCA,EAAU,YAAY,KAAK,MAAM,EACjC,KAAK,UAAU,YAAYA,CAAS,EAEpC,KAAK,UAAU,YAAY,KAAK,gBAAA,CAAiB,EAGjD,MAAMC,EAAepU,EAAc,MAAO,yBAAyB,EACnE,KAAK,QAAU,KAAK,UAAU8T,EAAM,KAAM,OAAQ,IAAM,KAAK,OAAO,eAAA,EAAiB,KAAA,CAAM,EAC3F,KAAK,QAAU,KAAK,UAAUA,EAAM,KAAM,OAAQ,IAAM,KAAK,OAAO,eAAA,EAAiB,KAAA,CAAM,EAC3F,KAAK,UAAY,KAAK,UAAUA,EAAM,MAAO,SAAU,IAAM,CAC3D,MAAMrC,EAAK,KAAK,OAAO,aAAA,EAAe,cAAA,EAClCA,GAAI,KAAK,OAAO,cAAcA,CAAE,CACtC,CAAC,EACD2C,EAAa,YAAY,KAAK,OAAO,EACrCA,EAAa,YAAY,KAAK,OAAO,EACrCA,EAAa,YAAY,KAAK,SAAS,EACvC,KAAK,UAAU,YAAYA,CAAY,EAEvC,KAAK,UAAU,YAAY,KAAK,gBAAA,CAAiB,EAGjD,MAAMC,EAAUrU,EAAc,MAAO,yBAAyB,EAC9D,KAAK,UAAY,KAAK,UAAU8T,EAAM,SAAU,SAAU,IAAM,KAAK,gBAAgB,EACrF,KAAK,UAAY,KAAK,UAAUA,EAAM,OAAQ,SAAU,IAAM,KAAK,iBAAiB,EACpF,KAAK,QAAU,KAAK,UAAUA,EAAM,KAAM,YAAa,IAAM,KAAK,UAAU,EAC5EO,EAAQ,YAAY,KAAK,SAAS,EAClCA,EAAQ,YAAY,KAAK,SAAS,EAClCA,EAAQ,YAAY,KAAK,OAAO,EAChC,KAAK,UAAU,YAAYA,CAAO,EAGlC,MAAMC,EAAUtU,EAAc,OAAQ,oBAAoB,EAC1DsU,EAAQ,UAAYR,EAAM,MAC1B,MAAMS,EAAWvU,EAAc,QAAS,uBAAuB,EAC/DuU,EAAS,KAAO,MAChBA,EAAS,YAAc,eACvBA,EAAS,MAAQ,KAAK,OAAO,OAAA,EAC7B,MAAMC,EAAU,KAAK,UAAU,GAAI,OAAQ,IAAM,KAAK,aAAaD,CAAQ,CAAC,EAC5EA,EAAS,iBAAiB,UAAY5L,GAAM,CACtCA,EAAE,MAAQ,UACZA,EAAE,eAAA,EACF,KAAK,aAAa4L,CAAQ,EAE9B,CAAC,EACD,KAAK,SAAS,YAAYD,CAAO,EACjC,KAAK,SAAS,YAAYC,CAAQ,EAClC,KAAK,SAAS,YAAYC,CAAO,EAEjC,KAAK,YAAA,CACP,CAEQ,UAAU7Q,EAAc8Q,EAAeC,EAAwC,CACrF,MAAM/E,EAAM3P,EAAc,SAAU,eAAe,EACnD,OAAA2P,EAAI,UAAYhM,EAChBgM,EAAI,YAAY,SAAS,eAAe,IAAI8E,CAAK,EAAE,CAAC,EACpD9E,EAAI,MAAQ8E,EACZ9E,EAAI,aAAa,aAAc8E,CAAK,EACpC9E,EAAI,iBAAiB,QAAUhH,GAAM,CACnCA,EAAE,gBAAA,EACF+L,EAAA,CACF,CAAC,EACM/E,CACT,CAEQ,iBAA+B,CACrC,OAAO3P,EAAc,MAAO,6BAA6B,CAC3D,CAEA,aAAoB,CAClB,MAAM2U,EAAO,KAAK,OAAO,QAAA,EACzB,KAAK,UAAU,UAAU,OAAO,wBAAyBA,IAAS,QAAQ,EAC1E,KAAK,OAAO,UAAU,OAAO,wBAAyBA,IAAS,KAAK,EACpE,KAAK,UAAU,aAAa,eAAgB,OAAOA,IAAS,QAAQ,CAAC,EACrE,KAAK,OAAO,aAAa,eAAgB,OAAOA,IAAS,KAAK,CAAC,EAE/D,MAAMC,EAAO,KAAK,OAAO,eAAA,EACzB,KAAK,QAAQ,SAAW,CAACA,EAAK,QAAA,EAC9B,KAAK,QAAQ,SAAW,CAACA,EAAK,QAAA,EAE9B,KAAK,UAAU,SAAW,CAAC,KAAK,OAAO,aAAA,EAAe,cAAA,CACxD,CAEQ,gBAAuB,CAC7B,MAAMC,EAAO,KAAK,OAAO,WAAA,EACnBC,EAAO,IAAI,KAAK,CAACD,CAAI,EAAG,CAAE,KAAM,mBAAoB,EACpDpN,EAAM,IAAI,gBAAgBqN,CAAI,EAC9BC,EAAI/U,EAAc,GAAG,EAC3B+U,EAAE,KAAOtN,EACTsN,EAAE,SAAW,gBACbA,EAAE,MAAA,EACF,IAAI,gBAAgBtN,CAAG,EACvB,KAAK,OAAO,UAAU,wBAAwB,CAChD,CAEA,MAAc,UAA0B,CACtC,GAAI,CACF,MAAM,UAAU,UAAU,UAAU,KAAK,OAAO,YAAY,EAC5D,KAAK,OAAO,UAAU,0BAA0B,CAClD,MAAQ,CACN,KAAK,OAAO,UAAU,gBAAgB,CACxC,CACF,CAEQ,iBAAwB,CAC9B,MAAMuN,EAAUhV,EAAc,MAAO,yBAAyB,EACxDiV,EAAQjV,EAAc,MAAO,iBAAiB,EACpDiV,EAAM,aAAa,OAAQ,QAAQ,EACnCA,EAAM,aAAa,aAAc,MAAM,EAEvC,MAAMC,EAAU,yBAChBD,EAAM,aAAa,kBAAmBC,CAAO,EAE7C,MAAMC,EAAQnV,EAAc,IAAI,EAChCmV,EAAM,GAAKD,EACXC,EAAM,YAAc,uBAEpB,MAAMC,EAAWpV,EAAc,UAAU,EACzCoV,EAAS,YAAc;AAAA;AAAA,GAEvB,MAAMC,EAAUrV,EAAc,MAAO,uBAAuB,EAC5DqV,EAAQ,MAAM,QAAU,OAExB,MAAMC,EAAUtV,EAAc,MAAO,yBAAyB,EACxDuV,EAAYvV,EAAc,SAAU,eAAe,EACzDuV,EAAU,YAAc,SACxB,MAAMC,EAAYxV,EAAc,SAAU,sCAAsC,EAChFwV,EAAU,YAAc,SAExBF,EAAQ,YAAYC,CAAS,EAC7BD,EAAQ,YAAYE,CAAS,EAE7BP,EAAM,YAAYE,CAAK,EACvBF,EAAM,YAAYG,CAAQ,EAC1BH,EAAM,YAAYI,CAAO,EACzBJ,EAAM,YAAYK,CAAO,EACzBN,EAAQ,YAAYC,CAAK,EACzB,SAAS,KAAK,YAAYD,CAAO,EAEjC,MAAMpE,EAAOxC,EAAgB6G,EAAO,KAAK,SAAS,EAC5CQ,EAAQ,IAAM,CAClB7E,EAAK,QAAA,EACLoE,EAAQ,OAAA,CACV,EAEAO,EAAU,iBAAiB,QAASE,CAAK,EACzCT,EAAQ,iBAAiB,QAAUrM,GAAM,CACnCA,EAAE,SAAWqM,GAASS,EAAA,CAC5B,CAAC,EACDT,EAAQ,iBAAiB,UAAYrM,GAAM,CACrCA,EAAE,MAAQ,UAAU8M,EAAA,CAC1B,CAAC,EAEDD,EAAU,iBAAiB,QAAS,IAAM,CACxC,GAAI,CACF,KAAK,OAAO,WAAWJ,EAAS,KAAK,EACrCK,EAAA,EACA,KAAK,OAAO,UAAU,mBAAmB,CAC3C,OAASC,EAAK,CACZL,EAAQ,MAAM,QAAU,QACxBA,EAAQ,YAAcK,aAAe,MAAQA,EAAI,QAAU,cAC7D,CACF,CAAC,EAEDN,EAAS,MAAA,EACTxE,EAAK,SAAA,CACP,CAEQ,aAAa+E,EAA+B,CAClD,MAAM5I,EAAM4I,EAAM,MAAM,KAAA,EACnB5I,IACL,KAAK,OAAO,OAAOA,CAAG,EACtB,KAAK,OAAO,UAAU,eAAe,EACvC,CAEA,SAAgB,CACd,KAAK,UAAU,OAAA,EACf,KAAK,SAAS,OAAA,CAChB,CACF,CC9NO,MAAM6I,EAAiB,CAI5B,YAAoB3B,EAAyB,CAAzB,KAAA,OAAAA,EAHpB,KAAQ,WAA4B,KACpC,KAAQ,SAA2B,CAAA,EAGjC,KAAK,iBAAA,CACP,CAEQ,kBAAyB,CAC/B,MAAMjG,EAAU/M,EAAY,KAAK,OAAO,cAAe,QAAU,GAAM,CACrE,GAAI,KAAK,OAAO,QAAA,IAAc,SAAU,OAExC,MAAMgN,EAAS,EAAE,OACX7J,EAAW6J,EAAO,QAAqB,mBAAmB,EAEhE,GAAI7J,EAAU,CACZ,EAAE,gBAAA,EACF,MAAMqN,EAAKrN,EAAS,aAAa,iBAAiB,EAC9CqN,GAAI,KAAK,OAAOA,CAAE,CACxB,KAAO,CAEL,MAAMoE,EAAY5H,EAAO,QAAQ,oBAAoB,EAC/C6H,EAAY7H,EAAO,QAAQ,oBAAoB,EACjD,CAAC4H,GAAa,CAACC,GACjB,KAAK,SAAA,CAET,CACF,CAAC,EACD,KAAK,SAAS,KAAK9H,CAAO,CAC5B,CAEA,OAAOyD,EAAkB,CACnB,KAAK,aAAeA,IACxB,KAAK,SAAA,EACL,KAAK,WAAaA,EAClB,KAAK,mBAAmBA,EAAI,EAAI,EAChC,KAAK,OAAO,OAAO,KAAK,iBAAkBA,CAAE,EAC5C,KAAK,OAAO,WAAA,EAAa,YAAA,EAE3B,CAEA,UAAiB,CACf,GAAI,CAAC,KAAK,WAAY,OACtB,MAAMsE,EAAS,KAAK,WACpB,KAAK,mBAAmBA,EAAQ,EAAK,EACrC,KAAK,WAAa,KAClB,KAAK,OAAO,OAAO,KAAK,mBAAoBA,CAAM,EAClD,KAAK,OAAO,WAAA,EAAa,YAAA,CAC3B,CAEA,eAA+B,CAC7B,OAAO,KAAK,UACd,CAEA,sBAA6B,CACtB,KAAK,YACV,KAAK,mBAAmB,KAAK,WAAY,EAAI,CAC/C,CAEQ,mBAAmBtE,EAAYuE,EAAoB,CAEzD,MAAMhT,EADS,KAAK,OAAO,YAAA,EACL,cAA2B,qBAAqByO,CAAE,IAAI,EACxEzO,GACFA,EAAO,UAAU,OAAO,qCAAsCgT,CAAG,CAErE,CAEA,SAAgB,CACd,KAAK,SAAS,QAAS3N,GAAOA,GAAI,EAClC,KAAK,SAAW,CAAA,CAClB,CACF,CCtEO,MAAM4N,EAAc,CAGzB,YACUjC,EACAC,EACR,CAFQ,KAAA,SAAAD,EACA,KAAA,OAAAC,EAiMV,KAAQ,aAAe,EA/LrB,KAAK,QAAUjU,EAAc,MAAO,iBAAiB,EACrD,KAAK,SAAS,YAAY,KAAK,OAAO,EAGtC,KAAK,OAAO,OAAO,GAAG,iBAAkB,IAAM,KAAK,SAAS,EAC5D,KAAK,OAAO,OAAO,GAAG,mBAAoB,IAAM,KAAK,SAAS,EAE9D,KAAK,QAAA,CACP,CAEA,SAAgB,CACd,MAAMkW,EAAa,KAAK,OAAO,aAAA,EAAe,cAAA,EAE9C,GAAI,CAACA,EAAY,CACf,KAAK,kBAAA,EACL,MACF,CAEA,MAAM9T,EAAU,KAAK,OAAO,WAAW8T,CAAU,EACjD,GAAI,CAAC9T,EAAS,CACZ,KAAK,kBAAA,EACL,MACF,CAEA,KAAK,WAAWA,CAAO,CACzB,CAEQ,mBAA0B,CAChC,KAAK,QAAQ,UAAY,GAEzB,MAAM+S,EAAQnV,EAAc,MAAO,uBAAuB,EAC1DmV,EAAM,YAAc,WACpB,KAAK,QAAQ,YAAYA,CAAK,EAE9B,MAAMgB,EAAW,KAAK,OAAO,YAAA,EAE7B,GAAIA,EAAS,SAAW,EAAG,CACzB,MAAMC,EAAQpW,EAAc,MAAO,uBAAuB,EAC1DoW,EAAM,YAAc,oEACpB,KAAK,QAAQ,YAAYA,CAAK,EAC9B,MACF,CAEA,MAAMC,EAAOrW,EAAc,KAAM,wBAAwB,EACzDqW,EAAK,aAAa,OAAQ,SAAS,EACnCA,EAAK,aAAa,aAAc,cAAc,EAC9C,UAAW/C,KAAK6C,EAAU,CACxB,MAAMG,EAAOtW,EAAc,KAAM,wBAAwB,EACzDsW,EAAK,aAAa,eAAgBhD,EAAE,EAAE,EACtCgD,EAAK,aAAa,OAAQ,QAAQ,EAClCA,EAAK,aAAa,WAAY,GAAG,EAEjC,MAAM7B,EAAQzU,EAAc,OAAQ,8BAA8B,EAClEyU,EAAM,YAAcnB,EAAE,OAASA,EAAE,GAEjC,MAAMiD,EAASvW,EAAc,OAAQ,+BAA+B,EACpEuW,EAAO,YAAc,GAAG,KAAK,SAASjD,EAAE,CAAC,CAAC,KAAK,KAAK,SAASA,EAAE,CAAC,CAAC,GAEjEgD,EAAK,YAAY7B,CAAK,EACtB6B,EAAK,YAAYC,CAAM,EAEvB,MAAMC,EAAgB,IAAM,KAAK,OAAO,eAAe,OAAOlD,EAAE,EAAE,EAClEgD,EAAK,iBAAiB,QAASE,CAAa,EAC5CF,EAAK,iBAAiB,UAAY3N,GAAM,EAClCA,EAAE,MAAQ,SAAWA,EAAE,MAAQ,OACjCA,EAAE,eAAA,EACF6N,EAAA,EAEJ,CAAC,EAEDH,EAAK,YAAYC,CAAI,CACvB,CACA,KAAK,QAAQ,YAAYD,CAAI,CAC/B,CAEQ,WAAWjU,EAA4B,CAC7C,KAAK,QAAQ,UAAY,GACzB,KAAK,aAAe,EAEpB,MAAM+S,EAAQnV,EAAc,MAAO,uBAAuB,EAC1DmV,EAAM,YAAc,SAAS/S,EAAQ,OAASA,EAAQ,EAAE,GACxD,KAAK,QAAQ,YAAY+S,CAAK,EAG9B,KAAK,QAAQ,YACX,KAAK,gBAAgB,QAAS/S,EAAQ,OAAS,GAAKuQ,GAAQ,CAC1D,KAAK,OAAO,cAAcvQ,EAAQ,GAAI,CAAE,MAAOuQ,EAAK,CACtD,CAAC,CAAA,EAIH,MAAM8D,EAAWzW,EAAc,MAAO,qBAAqB,EAC3DyW,EAAS,YACP,KAAK,gBAAgB,IAAK,OAAOrU,EAAQ,CAAC,EAAIuQ,GAAQ,CACpD,KAAK,OAAO,cAAcvQ,EAAQ,GAAI,CAAE,EAAGuQ,EAAK,CAClD,CAAC,CAAA,EAEH8D,EAAS,YACP,KAAK,gBAAgB,IAAK,OAAOrU,EAAQ,CAAC,EAAIuQ,GAAQ,CACpD,KAAK,OAAO,cAAcvQ,EAAQ,GAAI,CAAE,EAAGuQ,EAAK,CAClD,CAAC,CAAA,EAEH,KAAK,QAAQ,YAAY8D,CAAQ,EAGjC,KAAK,QAAQ,YACX,KAAK,aAAa,UAAWrU,EAAQ,SAAW,QAAS,CAAC,QAAS,QAAS,MAAM,EAAIuQ,GAAQ,CAC5F,KAAK,OAAO,cAAcvQ,EAAQ,GAAI,CAAE,QAASuQ,EAA+B,CAClF,CAAC,CAAA,EAIH,KAAK,QAAQ,YACX,KAAK,aACH,YACAvQ,EAAQ,WAAa,MACrB,CAAC,MAAO,SAAU,OAAQ,QAAS,MAAM,EACxCuQ,GAAQ,CACP,KAAK,OAAO,cAAcvQ,EAAQ,GAAI,CAAE,UAAWuQ,EAAiC,CACtF,CAAA,CACF,EAIF,MAAM5L,EAAO3E,EAAQ,MAAQ,CAAA,EAE7B,KAAK,QAAQ,YACX,KAAK,gBAAgB,QAAS2E,EAAK,OAAS,GAAK4L,GAAQ,CACvD,KAAK,OAAO,cAAcvQ,EAAQ,GAAI,CACpC,KAAM,CAAE,GAAGA,EAAQ,KAAM,MAAOuQ,CAAA,CAAI,CACrC,CACH,CAAC,CAAA,EAGH,KAAK,QAAQ,YACX,KAAK,gBAAgB,QAAS5L,EAAK,OAAS,GAAK4L,GAAQ,CACvD,KAAK,OAAO,cAAcvQ,EAAQ,GAAI,CACpC,KAAM,CAAE,GAAGA,EAAQ,KAAM,MAAOuQ,CAAA,CAAI,CACrC,CACH,CAAC,CAAA,EAGH,KAAK,QAAQ,YACX,KAAK,eAAe,cAAe5L,EAAK,aAAe,GAAK4L,GAAQ,CAClE,KAAK,OAAO,cAAcvQ,EAAQ,GAAI,CACpC,KAAM,CAAE,GAAGA,EAAQ,KAAM,YAAauQ,CAAA,CAAI,CAC3C,CACH,CAAC,CAAA,EAGH,KAAK,QAAQ,YACX,KAAK,gBAAgB,YAAa5L,EAAK,OAAS,GAAK4L,GAAQ,CAC3D,KAAK,OAAO,cAAcvQ,EAAQ,GAAI,CACpC,KAAM,CAAE,GAAGA,EAAQ,KAAM,MAAOuQ,CAAA,CAAI,CACrC,CACH,CAAC,CAAA,EAGH,KAAK,QAAQ,YACX,KAAK,gBAAgB,WAAY5L,EAAK,KAAO,GAAK4L,GAAQ,CACxD,KAAK,OAAO,cAAcvQ,EAAQ,GAAI,CACpC,KAAM,CAAE,GAAGA,EAAQ,KAAM,IAAKuQ,CAAA,CAAI,CACnC,CACH,CAAC,CAAA,EAGH,KAAK,QAAQ,YACX,KAAK,gBAAgB,WAAY5L,EAAK,SAAW,GAAK4L,GAAQ,CAC5D,KAAK,OAAO,cAAcvQ,EAAQ,GAAI,CACpC,KAAM,CAAE,GAAGA,EAAQ,KAAM,QAASuQ,CAAA,CAAI,CACvC,CACH,CAAC,CAAA,EAIH,MAAM2C,EAAUtV,EAAc,MAAO,yBAAyB,EACxD0W,EAAY1W,EAAc,SAAU,qCAAqC,EAC/E0W,EAAU,YAAc,iBACxBA,EAAU,iBAAiB,QAAS,IAAM,KAAK,OAAO,cAActU,EAAQ,EAAE,CAAC,EAE/E,MAAMuU,EAAU3W,EAAc,SAAU,eAAe,EACvD2W,EAAQ,YAAc,eACtBA,EAAQ,iBAAiB,QAAS,IAAM,KAAK,OAAO,aAAA,EAAe,UAAU,EAE7ErB,EAAQ,YAAYqB,CAAO,EAC3BrB,EAAQ,YAAYoB,CAAS,EAC7B,KAAK,QAAQ,YAAYpB,CAAO,CAClC,CAMQ,YAAYb,EAAuB,CACzC,MAAO,mBAAmBA,EAAM,YAAA,EAAc,QAAQ,OAAQ,GAAG,CAAC,IAAI,EAAE,KAAK,YAAY,EAC3F,CAEQ,gBACNA,EACApU,EACAuW,EACa,CACb,MAAMnF,EAAK,KAAK,YAAYgD,CAAK,EAC3BoC,EAAQ7W,EAAc,MAAO,iBAAiB,EAC9C8W,EAAU9W,EAAc,QAAS,GAAI,CAAE,IAAKyR,EAAI,EACtDqF,EAAQ,YAAcrC,EACtB,MAAMkB,EAAQ3V,EAAc,QAAS,GAAI,CAAE,GAAAyR,EAAI,EAC/C,OAAAkE,EAAM,KAAO,OACbA,EAAM,MAAQtV,EACdsV,EAAM,iBAAiB,SAAU,IAAMiB,EAASjB,EAAM,KAAK,CAAC,EAC5DkB,EAAM,YAAYC,CAAO,EACzBD,EAAM,YAAYlB,CAAK,EAChBkB,CACT,CAEQ,eACNpC,EACApU,EACAuW,EACa,CACb,MAAMnF,EAAK,KAAK,YAAYgD,CAAK,EAC3BoC,EAAQ7W,EAAc,MAAO,iBAAiB,EAC9C8W,EAAU9W,EAAc,QAAS,GAAI,CAAE,IAAKyR,EAAI,EACtDqF,EAAQ,YAAcrC,EACtB,MAAMW,EAAWpV,EAAc,WAAY,GAAI,CAAE,GAAAyR,EAAI,EACrD,OAAA2D,EAAS,MAAQ/U,EACjB+U,EAAS,iBAAiB,SAAU,IAAMwB,EAASxB,EAAS,KAAK,CAAC,EAClEyB,EAAM,YAAYC,CAAO,EACzBD,EAAM,YAAYzB,CAAQ,EACnByB,CACT,CAEQ,aACNpC,EACApU,EACAa,EACA0V,EACa,CACb,MAAMnF,EAAK,KAAK,YAAYgD,CAAK,EAC3BoC,EAAQ7W,EAAc,MAAO,iBAAiB,EAC9C8W,EAAU9W,EAAc,QAAS,GAAI,CAAE,IAAKyR,EAAI,EACtDqF,EAAQ,YAAcrC,EACtB,MAAMsC,EAAS/W,EAAc,SAAU,GAAI,CAAE,GAAAyR,EAAI,EACjD,UAAWuF,KAAO9V,EAAS,CACzB,MAAM+V,EAASjX,EAAc,QAAQ,EACrCiX,EAAO,MAAQD,EACfC,EAAO,YAAcD,EACjBA,IAAQ3W,IAAO4W,EAAO,SAAW,IACrCF,EAAO,YAAYE,CAAM,CAC3B,CACA,OAAAF,EAAO,iBAAiB,SAAU,IAAMH,EAASG,EAAO,KAAK,CAAC,EAC9DF,EAAM,YAAYC,CAAO,EACzBD,EAAM,YAAYE,CAAM,EACjBF,CACT,CAEQ,SAASK,EAA4B,CAC3C,OAAI,OAAOA,GAAM,SAAiBA,EAC3B,GAAG,KAAK,MAAMA,EAAI,GAAG,EAAI,GAAG,GACrC,CACF,CC5QO,MAAMC,EAAY,CAWvB,YAAoBlD,EAAyB,CAAzB,KAAA,OAAAA,EAVpB,KAAQ,SAA2B,CAAA,EACnC,KAAQ,SAAW,GACnB,KAAQ,OAAwB,KAChC,KAAQ,OAAS,EACjB,KAAQ,OAAS,EACjB,KAAQ,SAA+B,KACvC,KAAQ,SAAW,GACnB,KAAQ,QAAU,GAClB,KAAQ,MAAuB,KAG7B,KAAK,KAAA,CACP,CAEA,MAAa,CACX,KAAK,OAAA,EAEL,MAAMmD,EAAS,KAAK,OAAO,YAAA,EAErBC,EAAiB1O,GAAoB,CACzC,GAAI,KAAK,OAAO,QAAA,IAAc,SAAU,OAExC,MAAM3F,EADS2F,EAAE,OACK,QAAqB,mBAAmB,EAC9D,GAAI,CAAC3F,EAAQ,OAEb,MAAMyO,EAAKzO,EAAO,aAAa,iBAAiB,EAC3CyO,IAEL9I,EAAE,eAAA,EACF,KAAK,SAAW,GAChB,KAAK,OAAS8I,EACd,KAAK,OAAS9I,EAAE,QAChB,KAAK,OAASA,EAAE,QAChB,KAAK,SAAW3F,EAChB,KAAK,SAAWA,EAAO,MAAM,KAC7B,KAAK,QAAUA,EAAO,MAAM,IAE5BA,EAAO,kBAAkB2F,EAAE,SAAS,EACtC,EAEM2O,EAAiB3O,GAAoB,CACzC,GAAI,CAAC,KAAK,QAAU,CAAC,KAAK,SAAU,OAEpC,MAAMe,EAAKf,EAAE,QAAU,KAAK,OACtBgB,EAAKhB,EAAE,QAAU,KAAK,OAO5B,GAJI,CAAC,KAAK,UAAY,KAAK,IAAIe,CAAE,EAAI,KAAK,IAAIC,CAAE,EAAI,IACpD,KAAK,SAAW,GAGZ,KAAK,QAAU,MAAM,OACzB,MAAM4N,EAAU5O,EAAE,QACZ6O,EAAU7O,EAAE,QAClB,KAAK,MAAQ,sBAAsB,IAAM,CAEvC,GADA,KAAK,MAAQ,KACT,CAAC,KAAK,SAAU,OAEpB,MAAM8O,EAAQL,EAAO,cAAgC,mBAAmB,EACxE,GAAI,CAACK,EAAO,OAEZ,MAAMzN,EAAOyN,EAAM,sBAAA,EACbjV,GAAM+U,EAAUvN,EAAK,MAAQA,EAAK,MAAS,IAC3CvH,GAAM+U,EAAUxN,EAAK,KAAOA,EAAK,OAAU,IAE3CS,EAAK,KAAK,IAAI,EAAG,KAAK,IAAI,IAAKjI,CAAC,CAAC,EACjCkI,EAAK,KAAK,IAAI,EAAG,KAAK,IAAI,IAAKjI,CAAC,CAAC,EAEvC,KAAK,SAAS,MAAM,KAAO,GAAGgI,CAAE,IAChC,KAAK,SAAS,MAAM,IAAM,GAAGC,CAAE,GACjC,CAAC,CACH,EAEMgN,EAAe/O,GAAoB,CAKvC,GAJI,KAAK,QAAU,OACjB,qBAAqB,KAAK,KAAK,EAC/B,KAAK,MAAQ,MAEX,EAAC,KAAK,OAEV,IAAI,KAAK,UAAY,KAAK,SAAU,CAClC,MAAM8O,EAAQL,EAAO,cAAgC,mBAAmB,EACxE,GAAIK,EAAO,CACT,MAAMzN,EAAOyN,EAAM,sBAAA,EACbjV,GAAMmG,EAAE,QAAUqB,EAAK,MAAQA,EAAK,MAAS,IAC7CvH,GAAMkG,EAAE,QAAUqB,EAAK,KAAOA,EAAK,OAAU,IAC7CS,EAAK,KAAK,IAAI,EAAG,KAAK,IAAI,IAAK,KAAK,MAAMjI,EAAI,GAAG,EAAI,GAAG,CAAC,EACzDkI,EAAK,KAAK,IAAI,EAAG,KAAK,IAAI,IAAK,KAAK,MAAMjI,EAAI,GAAG,EAAI,GAAG,CAAC,EAE/D,KAAK,OAAO,cAAc,KAAK,OAAQ,CACrC,EAAG,GAAGgI,CAAE,IACR,EAAG,GAAGC,CAAE,GAAA,CACT,CACH,CACF,CAEA,KAAK,OAAS,KACd,KAAK,SAAW,GAChB,KAAK,SAAW,KAClB,EAEMiN,EAAc1W,EAAYmW,EAAQ,cAAeC,CAAa,EAC9DO,EAAc3W,EAAYmW,EAAQ,cAAeE,CAAa,EAC9DO,EAAY5W,EAAYmW,EAAQ,YAAaM,CAAW,EAE9D,KAAK,SAAS,KAAKC,EAAaC,EAAaC,CAAS,CACxD,CAEQ,QAAe,CACrB,KAAK,SAAS,QAASxP,GAAOA,GAAI,EAClC,KAAK,SAAW,CAAA,CAClB,CAEA,SAAgB,CACV,KAAK,QAAU,OACjB,qBAAqB,KAAK,KAAK,EAC/B,KAAK,MAAQ,MAEf,KAAK,OAAA,CACP,CACF,CCzHO,MAAMyP,EAAY,CAKvB,YACU7D,EACR8D,EACA,CAFQ,KAAA,OAAA9D,EALV,KAAQ,UAA8B,CAAA,EACtC,KAAQ,UAA8B,CAAA,EAOpC,KAAK,WAAa8D,CACpB,CAEA,aAAoB,CAClB,KAAK,UAAY,CAAC,KAAK,OAAO,gBAAgB,EAC9C,KAAK,UAAY,CAAA,EACjB,KAAK,aAAA,CACP,CAEA,MAAa,CACX,KAAK,UAAU,KAAK,KAAK,OAAO,gBAAgB,EAC5C,KAAK,UAAU,OAAS,KAAK,YAC/B,KAAK,UAAU,MAAA,EAEjB,KAAK,UAAY,CAAA,EACjB,KAAK,aAAA,CACP,CAEA,MAAa,CACX,GAAI,KAAK,UAAU,QAAU,EAAG,OAChC,MAAMC,EAAU,KAAK,UAAU,IAAA,EAC/B,KAAK,UAAU,KAAKA,CAAO,EAC3B,MAAMC,EAAO,KAAK,UAAU,KAAK,UAAU,OAAS,CAAC,EACrD,KAAK,OAAO,gBAAgBA,CAAI,EAChC,KAAK,aAAA,CACP,CAEA,MAAa,CACX,GAAI,KAAK,UAAU,SAAW,EAAG,OACjC,MAAMC,EAAO,KAAK,UAAU,IAAA,EAC5B,KAAK,UAAU,KAAKA,CAAI,EACxB,KAAK,OAAO,gBAAgBA,CAAI,EAChC,KAAK,aAAA,CACP,CAEA,SAAmB,CACjB,OAAO,KAAK,UAAU,OAAS,CACjC,CAEA,SAAmB,CACjB,OAAO,KAAK,UAAU,OAAS,CACjC,CAEQ,cAAqB,CAC3B,KAAK,OAAO,OAAO,KAAK,gBAAgB,EACxC,KAAK,OAAO,WAAA,EAAa,YAAA,CAC3B,CACF,m/SC9CO,MAAMC,EAAgB,CA4B3B,YAAY7W,EAA+BC,EAAsB,CAK/D,GAtBF,KAAQ,OAA2B,KACnC,KAAQ,SAA0B,CAAA,EAClC,KAAQ,KAAmB,SAC3B,KAAQ,OAAS,EAQjB,KAAS,OAAS,IAAIX,GACtB,KAAQ,SAA2B,CAAA,EACnC,KAAQ,QAA8B,KACtC,KAAQ,WAAmD,KAC3D,KAAQ,UAAY,GAGlB,KAAK,OACH,OAAOU,GAAY,SACf,SAAS,cAA2BA,CAAO,EAC3CA,EACF,CAAC,KAAK,OAAQ,MAAM,IAAI,MAAM,oCAAoC,EAEtE,KAAK,OAAS,CACZ,eAAgB,QAChB,iBAAkB,MAClB,WAAY,GACZ,GAAGC,CAAA,EAGL,KAAK,SAAWA,EAAO,SAAW,gBAAgBA,EAAO,QAAQ,EAAI,CAAA,EAErE,UAAW+R,KAAK,KAAK,SAAU,CAC7B,MAAMxR,EAAM,SAASwR,EAAE,GAAG,QAAQ,MAAO,EAAE,EAAG,EAAE,EAC5C,CAAC,MAAMxR,CAAG,GAAKA,GAAO,KAAK,SAAQ,KAAK,OAASA,EAAM,EAC7D,CAEArB,EAAa2X,EAAS,EACtB,KAAK,mBAAA,EACL,KAAK,SAAA,EACL,KAAK,YAAA,EACL,KAAK,cAAA,EACL,KAAK,aAAA,CACP,CAIQ,oBAA2B,CACjC,MAAM3G,EAAK,mBACX,GAAI,SAAS,eAAeA,CAAE,EAAG,OACjC,MAAM9Q,EAAQ,SAAS,cAAc,OAAO,EAC5CA,EAAM,GAAK8Q,EACX9Q,EAAM,YAAc0X,GACpB,SAAS,KAAK,YAAY1X,CAAK,CACjC,CAEQ,UAAiB,CACvB,KAAK,SAAWX,EAAc,MAAO,WAAW,EAC5C,KAAK,OAAO,QAAU,QACxB,KAAK,SAAS,UAAU,IAAI,iBAAiB,EAE/C,KAAK,SAAWA,EAAc,MAAO,kBAAkB,EACvD,KAAK,UAAYA,EAAc,MAAO,mBAAmB,EACzD,KAAK,SAAWA,EAAc,MAAO,kBAAkB,EAEvD,MAAMsY,EAAStY,EAAc,MAAO,gBAAgB,EACpDsY,EAAO,YAAY,KAAK,QAAQ,EAChCA,EAAO,YAAY,KAAK,SAAS,EAEjC,KAAK,SAAS,YAAYA,CAAM,EAChC,KAAK,SAAS,YAAY,KAAK,QAAQ,EAEvC,KAAK,OAAO,UAAY,GACxB,KAAK,OAAO,YAAY,KAAK,QAAQ,CACvC,CAEQ,aAAoB,CAE1B,KAAK,YAAc,IAAIR,GAAY,KAAM,KAAK,OAAO,UAAU,EAE/D,KAAK,UAAY,IAAIlC,GAAiB,IAAI,EAE1C,KAAK,QAAU,IAAI7B,GAAc,KAAK,SAAU,IAAI,EAEpD,KAAK,cAAgB,IAAIkC,GAAc,KAAK,UAAW,IAAI,EAE3D,KAAK,YAAc,IAAIkB,GAAY,IAAI,EAGvC,MAAM/G,EAAenP,EAAY,KAAK,SAAU,QAAU0H,GAAM,OAI9D,GAHI,KAAK,OAAS,OACHA,EAAE,OAEN,QAAQ,oBAAoB,EAAG,OAE1C,MAAMqB,GAAOjJ,EAAA,KAAK,SACf,cAAc,mBAAmB,IADvB,YAAAA,EAET,wBACJ,GAAI,CAACiJ,EAAM,OAEX,MAAMxH,GAAMmG,EAAE,QAAUqB,EAAK,MAAQA,EAAK,MAAS,IAC7CvH,GAAMkG,EAAE,QAAUqB,EAAK,KAAOA,EAAK,OAAU,IAG/CxH,EAAI,GAAKA,EAAI,KAAOC,EAAI,GAAKA,EAAI,KAErC,KAAK,WAAW,CACd,EAAG,GAAG,KAAK,MAAMD,EAAI,GAAG,EAAI,GAAG,IAC/B,EAAG,GAAG,KAAK,MAAMC,EAAI,GAAG,EAAI,GAAG,GAAA,CAChC,CACH,CAAC,EACD,KAAK,SAAS,KAAK2N,CAAY,EAG/B,MAAMmB,EAAatQ,EAAY,SAAU,UAAY0H,GAAM,CAEzD,MAAM1I,EAAO0I,EAAE,OAAuB,QACtC,GAAI1I,IAAQ,SAAWA,IAAQ,YAAcA,IAAQ,SAAU,OAE/D,MAAMsY,EAAM5P,EAAE,SAAWA,EAAE,QAErB6P,EAAgB,KAAK,SAAS,SAAS,SAAS,aAAa,GACjE,KAAK,SAAS,SAAS7P,EAAE,MAAc,EAEzC,GAAI4P,GAAO5P,EAAE,MAAQ,KAAO,CAACA,EAAE,UAAY6P,EACzC7P,EAAE,eAAA,EACF,KAAK,YAAY,KAAA,UACR4P,GAAO5P,EAAE,MAAQ,KAAOA,EAAE,UAAY6P,EAC/C7P,EAAE,eAAA,EACF,KAAK,YAAY,KAAA,UACR4P,GAAO5P,EAAE,MAAQ,KAAO6P,EACjC7P,EAAE,eAAA,EACF,KAAK,YAAY,KAAA,WACPA,EAAE,MAAQ,UAAYA,EAAE,MAAQ,cAAgB6P,EAAe,CACzE,MAAM/G,EAAK,KAAK,UAAU,cAAA,EACtBA,IACF9I,EAAE,eAAA,EACF,KAAK,cAAc8I,CAAE,EAEzB,MAAW9I,EAAE,MAAQ,UAAY6P,EAC3B,KAAK,OAAS,MAChB,KAAK,QAAQ,QAAQ,EAErB,KAAK,UAAU,SAAA,EAER7P,EAAE,MAAQ,KAAO,CAAC4P,GAAOC,EAClC,KAAK,QAAQ,KAAK,OAAS,MAAQ,SAAW,KAAK,EAC1C7P,EAAE,MAAQ,KAAO,CAAC4P,GAAOC,GAClC,KAAK,QAAQ,QAAQ,CAEzB,CAAC,EACD,KAAK,SAAS,KAAKjH,CAAU,EAG7B,KAAK,YAAY,YAAA,CACnB,CAIA,eAAsB,SAChB,KAAK,YACL,KAAK,SACP,KAAK,OAAO,QAAA,EACZ,KAAK,OAAS,MAIhB,KAAK,OAAS,IAAIsC,EAAU,KAAK,SAAU,CACzC,IAAK,KAAK,OAAO,IACjB,IAAK,KAAK,OAAO,KAAO,eACxB,SAAU,KAAK,SACf,QAAS,QACT,MAAO,GACP,SAAU,GACV,GAAI,KAAK,OAAO,WAAa,CAAE,WAAY,KAAK,OAAO,YAAe,CAAA,CAAC,CACxE,GAGD9S,EAAA,KAAK,YAAL,MAAAA,EAAgB,wBAEhBiH,EAAA,KAAK,cAAL,MAAAA,EAAkB,OACpB,CAIA,WAAWyQ,EAAgC,GAA6B,CACtE,GAAI,KAAK,UAAW,OACpB,MAAMhH,EAAK,WAAW,KAAK,QAAQ,GAC7BrP,EAAuB,CAC3B,GAAGqW,EACH,GAAAhH,EACA,EAAGgH,EAAQ,GAAK,MAChB,EAAGA,EAAQ,GAAK,MAChB,MAAOA,EAAQ,OAAS,WAAW,KAAK,SAAS,OAAS,CAAC,GAC3D,QAASA,EAAQ,SAAW,KAAK,OAAO,eACxC,UAAWA,EAAQ,WAAa,KAAK,OAAO,iBAC5C,KAAMA,EAAQ,MAAQ,CAAA,CAAC,EAGzB,YAAK,SAAS,KAAKrW,CAAO,EAC1B,KAAK,cAAA,EACL,KAAK,UAAU,OAAOqP,CAAE,EACxB,KAAK,YAAY,KAAA,EACjB,KAAK,aAAa,aAAa,EAC/B,KAAK,aAAA,EACErP,CACT,CAEA,cAAcqP,EAAkB,CAC9B,GAAI,KAAK,UAAW,OACpB,MAAM+B,EAAM,KAAK,SAAS,UAAWF,GAAMA,EAAE,KAAO7B,CAAE,EAClD+B,IAAQ,KAEZ,KAAK,SAAS,OAAOA,EAAK,CAAC,EACvB,KAAK,UAAU,cAAA,IAAoB/B,GACrC,KAAK,UAAU,SAAA,EAEjB,KAAK,cAAA,EACL,KAAK,YAAY,KAAA,EACjB,KAAK,aAAa,gBAAgB,EAClC,KAAK,aAAA,EACP,CAEA,cAAcA,EAAY8B,EAAqC,CAC7D,GAAI,KAAK,UAAW,OACpB,MAAMC,EAAM,KAAK,SAAS,UAAWF,GAAMA,EAAE,KAAO7B,CAAE,EAClD+B,IAAQ,KAEZ,KAAK,SAASA,CAAG,EAAI,CAAE,GAAG,KAAK,SAASA,CAAG,EAAG,GAAGD,CAAA,EACjD,KAAK,cAAA,EACL,KAAK,YAAY,KAAA,EACjB,KAAK,aAAa,gBAAgB,EAClC,KAAK,aAAA,EACP,CAIA,aAA6B,CAC3B,OAAO,gBAAgB,KAAK,QAAQ,CACtC,CAEA,WAAW9B,EAAqC,CAC9C,MAAMrP,EAAU,KAAK,SAAS,KAAMkR,GAAMA,EAAE,KAAO7B,CAAE,EACrD,OAAOrP,EAAU,gBAAgBA,CAAO,EAAI,MAC9C,CAEA,YAAY+T,EAA+B,CACrC,KAAK,YACT,KAAK,SAAW,gBAAgBA,CAAQ,EACxC,KAAK,UAAU,SAAA,EACf,KAAK,cAAA,EACL,KAAK,aAAA,EACP,CAEA,SAAsB,CACpB,OAAO,KAAK,IACd,CAEA,QAAQxB,EAAwB,CAC1B,KAAK,YACT,KAAK,KAAOA,EACZ,KAAK,SAAS,UAAU,OAAO,6BAA8BA,IAAS,KAAK,EAC3E,KAAK,OAAO,KAAK,cAAeA,CAAI,EACpC,KAAK,QAAQ,YAAA,EACb,KAAK,aAAA,EACP,CAEA,aAA2B,CACzB,OAAO,KAAK,QACd,CAEA,WAA8B,CAC5B,OAAO,KAAK,MACd,CAEA,QAAiB,CACf,OAAO,KAAK,OAAO,GACrB,CAEA,OAAO5H,EAAmB,CACpB,KAAK,YACT,KAAK,OAAO,IAAMA,EAClB,KAAK,cAAA,EACP,CAEA,cAAiC,CAC/B,OAAO,KAAK,SACd,CAEA,YAA4B,CAC1B,OAAO,KAAK,OACd,CAEA,gBAA8B,CAC5B,OAAO,KAAK,WACd,CAIA,gBAAiC,CAC/B,MAAO,CACL,SAAU,gBAAgB,KAAK,QAAQ,EACvC,WAAY,KAAK,UAAU,cAAA,CAAc,CAE7C,CAEA,gBAAgB2L,EAAgC,CAC1C,KAAK,YACT,KAAK,SAAW,gBAAgBA,EAAS,QAAQ,EACjD,KAAK,cAAA,EACDA,EAAS,YAAc,KAAK,SAAS,KAAMpF,GAAMA,EAAE,KAAOoF,EAAS,UAAU,EAC/E,KAAK,UAAU,OAAOA,EAAS,UAAU,EAEzC,KAAK,UAAU,SAAA,EAEjB,KAAK,aAAa,QAAQ,EAC1B,KAAK,aAAA,EACP,CAIA,YAAqB,CACnB,OAAO,KAAK,UAAU,KAAK,SAAU,KAAM,CAAC,CAC9C,CAEA,WAAW7D,EAAoB,CAC7B,GAAI,KAAK,UAAW,OACpB,MAAMlT,EAAS,KAAK,MAAMkT,CAAI,EAC9B,GAAI,CAAC,MAAM,QAAQlT,CAAM,EAAG,MAAM,IAAI,MAAM,+BAA+B,EAC3E,UAAW2R,KAAK3R,EACd,GAAI,CAAC2R,EAAE,IAAMA,EAAE,GAAK,MAAQA,EAAE,GAAK,KACjC,MAAM,IAAI,MAAM,qCAAqC,EAGzD,KAAK,SAAW3R,EAEhB,UAAW2R,KAAK,KAAK,SAAU,CAC7B,MAAMxR,EAAM,SAASwR,EAAE,GAAG,QAAQ,MAAO,EAAE,EAAG,EAAE,EAC5C,CAAC,MAAMxR,CAAG,GAAKA,GAAO,KAAK,SAAQ,KAAK,OAASA,EAAM,EAC7D,CACA,KAAK,UAAU,SAAA,EACf,KAAK,cAAA,EACL,KAAK,YAAY,KAAA,EACjB,KAAK,aAAa,QAAQ,EAC1B,KAAK,aAAA,CACP,CAIQ,aAAajB,EAAqB,WACxC,KAAK,OAAO,KAAKA,CAAK,EACtB,KAAK,OAAO,KAAK,QAAQ,GACzBmH,GAAAjH,EAAA,KAAK,QAAO,WAAZ,MAAAiH,EAAA,KAAAjH,EAAuB,KAAK,YAAA,IAC5BuH,EAAA,KAAK,gBAAL,MAAAA,EAAoB,SACtB,CAEQ,cAAqB,OAC3B,MAAMqQ,EAAQ,KAAK,SAAS,OACtBC,GAAW7X,EAAA,KAAK,YAAL,YAAAA,EAAgB,gBAC3B8X,EAAY,KAAK,OAAS,MAAQ,WAAa,cAC/C7R,EAAQ,CAAC,GAAG2R,CAAK,WAAWA,IAAU,EAAI,IAAM,EAAE,GAAIE,CAAS,EACjED,GAAU5R,EAAM,KAAK,aAAa4R,CAAQ,EAAE,EAChD,KAAK,SAAS,YAAc5R,EAAM,KAAK,OAAO,CAChD,CAIA,UAAUiI,EAAiB6J,EAAW,IAAY,CAC5C,KAAK,YACJ,KAAK,UACR,KAAK,QAAU9Y,EAAc,MAAO,iBAAiB,EACrD,KAAK,SAAS,YAAY,KAAK,OAAO,GAExC,KAAK,QAAQ,YAAciP,EAC3B,KAAK,QAAQ,UAAU,IAAI,0BAA0B,EACjD,KAAK,YAAY,aAAa,KAAK,UAAU,EACjD,KAAK,WAAa,WAAW,IAAM,QACjClO,EAAA,KAAK,UAAL,MAAAA,EAAc,UAAU,OAAO,4BAC/B,KAAK,WAAa,IACpB,EAAG+X,CAAQ,EACb,CAIA,SAAgB,OACV,KAAK,YACT,KAAK,UAAY,GACjB,KAAK,SAAS,QAASzQ,GAAOA,GAAI,EAClC,KAAK,SAAW,CAAA,EAChB,KAAK,YAAY,QAAA,EACjB,KAAK,UAAU,QAAA,EACf,KAAK,QAAQ,QAAA,GACbtH,EAAA,KAAK,SAAL,MAAAA,EAAa,UACb,KAAK,OAAO,UAAA,EACR,KAAK,aACP,aAAa,KAAK,UAAU,EAC5B,KAAK,WAAa,MAEpB,KAAK,QAAU,KACf,KAAK,OAAO,UAAY,GAC1B,CACF"}
|