@vizor-vr/player 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. package/README.md +4 -0
  2. package/dist/__tests__/autoplay.test.d.ts +2 -0
  3. package/dist/__tests__/autoplay.test.d.ts.map +1 -0
  4. package/dist/__tests__/chapter-logic.test.d.ts +2 -0
  5. package/dist/__tests__/chapter-logic.test.d.ts.map +1 -0
  6. package/dist/__tests__/chapter-manager.test.d.ts +2 -0
  7. package/dist/__tests__/chapter-manager.test.d.ts.map +1 -0
  8. package/dist/__tests__/chapter-menu.test.d.ts +2 -0
  9. package/dist/__tests__/chapter-menu.test.d.ts.map +1 -0
  10. package/dist/__tests__/dom-drm.test.d.ts +2 -0
  11. package/dist/__tests__/dom-drm.test.d.ts.map +1 -0
  12. package/dist/__tests__/drm-gate.test.d.ts +2 -0
  13. package/dist/__tests__/drm-gate.test.d.ts.map +1 -0
  14. package/dist/__tests__/fairplay.test.d.ts +2 -0
  15. package/dist/__tests__/fairplay.test.d.ts.map +1 -0
  16. package/dist/__tests__/hub-controller.test.d.ts +2 -0
  17. package/dist/__tests__/hub-controller.test.d.ts.map +1 -0
  18. package/dist/__tests__/hub-layout.test.d.ts +2 -0
  19. package/dist/__tests__/hub-layout.test.d.ts.map +1 -0
  20. package/dist/__tests__/hub-model.test.d.ts +2 -0
  21. package/dist/__tests__/hub-model.test.d.ts.map +1 -0
  22. package/dist/__tests__/hub-panel-renderer.test.d.ts +2 -0
  23. package/dist/__tests__/hub-panel-renderer.test.d.ts.map +1 -0
  24. package/dist/__tests__/lens-angle.test.d.ts +2 -0
  25. package/dist/__tests__/lens-angle.test.d.ts.map +1 -0
  26. package/dist/__tests__/media-element-drm-gate.test.d.ts +2 -0
  27. package/dist/__tests__/media-element-drm-gate.test.d.ts.map +1 -0
  28. package/dist/__tests__/media-element-gating.test.d.ts +2 -0
  29. package/dist/__tests__/media-element-gating.test.d.ts.map +1 -0
  30. package/dist/__tests__/player-version-lock.test.d.ts +2 -0
  31. package/dist/__tests__/player-version-lock.test.d.ts.map +1 -0
  32. package/dist/__tests__/source-manager-drm.test.d.ts +2 -0
  33. package/dist/__tests__/source-manager-drm.test.d.ts.map +1 -0
  34. package/dist/__tests__/vz-chapter.test.d.ts +2 -0
  35. package/dist/__tests__/vz-chapter.test.d.ts.map +1 -0
  36. package/dist/__tests__/vz-cinema-gating.test.d.ts +2 -0
  37. package/dist/__tests__/vz-cinema-gating.test.d.ts.map +1 -0
  38. package/dist/__tests__/vz-hub-item.test.d.ts +2 -0
  39. package/dist/__tests__/vz-hub-item.test.d.ts.map +1 -0
  40. package/dist/__tests__/vz-tour-gating.test.d.ts +2 -0
  41. package/dist/__tests__/vz-tour-gating.test.d.ts.map +1 -0
  42. package/dist/__tests__/watch-time.test.d.ts +2 -0
  43. package/dist/__tests__/watch-time.test.d.ts.map +1 -0
  44. package/dist/analytics/index.d.ts.map +1 -1
  45. package/dist/annotations/annotation-popup.d.ts.map +1 -1
  46. package/dist/{annotations-C0ugfCn4.js → annotations-Du6ZWrqC.js} +82 -13
  47. package/dist/annotations-Du6ZWrqC.js.map +1 -0
  48. package/dist/captions/caption-renderer.d.ts.map +1 -1
  49. package/dist/{captions-BP0H2E03.js → captions-YvnHAwLY.js} +4 -4
  50. package/dist/captions-YvnHAwLY.js.map +1 -0
  51. package/dist/chapters/chapter-logic.d.ts +47 -0
  52. package/dist/chapters/chapter-logic.d.ts.map +1 -0
  53. package/dist/chapters/chapter-manager.d.ts +42 -0
  54. package/dist/chapters/chapter-manager.d.ts.map +1 -0
  55. package/dist/chapters/chapter-menu.d.ts +24 -0
  56. package/dist/chapters/chapter-menu.d.ts.map +1 -0
  57. package/dist/{collab-BXPomrS0.js → collab-BLpllHtb.js} +20 -14
  58. package/dist/collab-BLpllHtb.js.map +1 -0
  59. package/dist/collaborative/chat-button.d.ts.map +1 -1
  60. package/dist/collaborative/voice-button.d.ts.map +1 -1
  61. package/dist/controls/cc-button.d.ts.map +1 -1
  62. package/dist/controls/compass.d.ts.map +1 -1
  63. package/dist/controls/controls-overlay.d.ts.map +1 -1
  64. package/dist/controls/fullscreen-button.d.ts.map +1 -1
  65. package/dist/controls/icons.d.ts +1 -0
  66. package/dist/controls/icons.d.ts.map +1 -1
  67. package/dist/controls/live-indicator.d.ts.map +1 -1
  68. package/dist/controls/play-button.d.ts.map +1 -1
  69. package/dist/controls/playlist-controls.d.ts.map +1 -1
  70. package/dist/controls/quality-menu.d.ts.map +1 -1
  71. package/dist/controls/seek-bar.d.ts +9 -0
  72. package/dist/controls/seek-bar.d.ts.map +1 -1
  73. package/dist/controls/volume-control.d.ts.map +1 -1
  74. package/dist/controls/vr-button.d.ts.map +1 -1
  75. package/dist/core/base-element.d.ts +8 -0
  76. package/dist/core/base-element.d.ts.map +1 -1
  77. package/dist/core/media-element.d.ts +70 -1
  78. package/dist/core/media-element.d.ts.map +1 -1
  79. package/dist/{dash-adapter-tid9ZyhO.js → dash-adapter-BMpHWcBA.js} +9 -2
  80. package/dist/dash-adapter-BMpHWcBA.js.map +1 -0
  81. package/dist/delivery/__tests__/delivery-client.test.d.ts +2 -0
  82. package/dist/delivery/__tests__/delivery-client.test.d.ts.map +1 -0
  83. package/dist/delivery/__tests__/delivery-hydrate.test.d.ts +2 -0
  84. package/dist/delivery/__tests__/delivery-hydrate.test.d.ts.map +1 -0
  85. package/dist/delivery/delivery-client.d.ts +27 -0
  86. package/dist/delivery/delivery-client.d.ts.map +1 -0
  87. package/dist/delivery/delivery-hydrate.d.ts +18 -0
  88. package/dist/delivery/delivery-hydrate.d.ts.map +1 -0
  89. package/dist/delivery/dom-drm.d.ts +9 -0
  90. package/dist/delivery/dom-drm.d.ts.map +1 -0
  91. package/dist/elements/vz-chapter.d.ts +27 -0
  92. package/dist/elements/vz-chapter.d.ts.map +1 -0
  93. package/dist/elements/vz-cinema.d.ts +20 -0
  94. package/dist/elements/vz-cinema.d.ts.map +1 -1
  95. package/dist/elements/vz-hub-item.d.ts +23 -0
  96. package/dist/elements/vz-hub-item.d.ts.map +1 -0
  97. package/dist/elements/vz-hub.d.ts +57 -0
  98. package/dist/elements/vz-hub.d.ts.map +1 -0
  99. package/dist/elements/vz-playlist.d.ts +14 -0
  100. package/dist/elements/vz-playlist.d.ts.map +1 -1
  101. package/dist/elements/vz-tour.d.ts +16 -0
  102. package/dist/elements/vz-tour.d.ts.map +1 -1
  103. package/dist/hls-adapter-C_a65zia.js +161 -0
  104. package/dist/hls-adapter-C_a65zia.js.map +1 -0
  105. package/dist/hub/hub-controller.d.ts +31 -0
  106. package/dist/hub/hub-controller.d.ts.map +1 -0
  107. package/dist/hub/hub-layout.d.ts +23 -0
  108. package/dist/hub/hub-layout.d.ts.map +1 -0
  109. package/dist/hub/hub-model.d.ts +27 -0
  110. package/dist/hub/hub-model.d.ts.map +1 -0
  111. package/dist/hub/hub-panel-manager.d.ts +16 -0
  112. package/dist/hub/hub-panel-manager.d.ts.map +1 -0
  113. package/dist/hub/hub-panel-renderer.d.ts +26 -0
  114. package/dist/hub/hub-panel-renderer.d.ts.map +1 -0
  115. package/dist/license/__tests__/cache.test.d.ts +2 -0
  116. package/dist/license/__tests__/cache.test.d.ts.map +1 -0
  117. package/dist/license/__tests__/feature-gate.test.d.ts +2 -0
  118. package/dist/license/__tests__/feature-gate.test.d.ts.map +1 -0
  119. package/dist/license/__tests__/format-gate.test.d.ts +2 -0
  120. package/dist/license/__tests__/format-gate.test.d.ts.map +1 -0
  121. package/dist/license/__tests__/license-checker.test.d.ts.map +1 -0
  122. package/dist/license/__tests__/master-key.test.d.ts.map +1 -0
  123. package/dist/license/__tests__/verify-no-ed25519.test.d.ts +2 -0
  124. package/dist/license/__tests__/verify-no-ed25519.test.d.ts.map +1 -0
  125. package/dist/license/__tests__/verify.test.d.ts +2 -0
  126. package/dist/license/__tests__/verify.test.d.ts.map +1 -0
  127. package/dist/license/cache.d.ts +4 -0
  128. package/dist/license/cache.d.ts.map +1 -0
  129. package/dist/license/drm-gate.d.ts +15 -0
  130. package/dist/license/drm-gate.d.ts.map +1 -0
  131. package/dist/license/feature-gate.d.ts +27 -0
  132. package/dist/license/feature-gate.d.ts.map +1 -0
  133. package/dist/license/format-gate.d.ts +23 -0
  134. package/dist/license/format-gate.d.ts.map +1 -0
  135. package/dist/license/license-checker.d.ts.map +1 -1
  136. package/dist/license/master-key.d.ts +11 -7
  137. package/dist/license/master-key.d.ts.map +1 -1
  138. package/dist/license/trusted-key.d.ts +7 -0
  139. package/dist/license/trusted-key.d.ts.map +1 -0
  140. package/dist/license/verify.d.ts +7 -0
  141. package/dist/license/verify.d.ts.map +1 -0
  142. package/dist/{register-CwnmiGEs.js → register-BmZt_300.js} +1482 -163
  143. package/dist/register-BmZt_300.js.map +1 -0
  144. package/dist/register.d.ts.map +1 -1
  145. package/dist/register.js +1 -1
  146. package/dist/renderer/camera-controller.d.ts +1 -1
  147. package/dist/renderer/camera-controller.d.ts.map +1 -1
  148. package/dist/renderer/projections/fisheye-projection.d.ts +4 -0
  149. package/dist/renderer/projections/fisheye-projection.d.ts.map +1 -1
  150. package/dist/renderer/projections/spherical-projection.d.ts +26 -0
  151. package/dist/renderer/projections/spherical-projection.d.ts.map +1 -0
  152. package/dist/renderer/renderer.d.ts.map +1 -1
  153. package/dist/renderer/shaders/fisheye.glsl.d.ts +10 -1
  154. package/dist/renderer/shaders/fisheye.glsl.d.ts.map +1 -1
  155. package/dist/sri.json +8 -8
  156. package/dist/streaming/dash-adapter.d.ts +2 -2
  157. package/dist/streaming/dash-adapter.d.ts.map +1 -1
  158. package/dist/streaming/fairplay.d.ts +21 -0
  159. package/dist/streaming/fairplay.d.ts.map +1 -0
  160. package/dist/streaming/hls-adapter.d.ts +2 -2
  161. package/dist/streaming/hls-adapter.d.ts.map +1 -1
  162. package/dist/streaming/progressive-adapter.d.ts +2 -2
  163. package/dist/streaming/progressive-adapter.d.ts.map +1 -1
  164. package/dist/streaming/source-manager.d.ts +3 -1
  165. package/dist/streaming/source-manager.d.ts.map +1 -1
  166. package/dist/streaming/types.d.ts +9 -3
  167. package/dist/streaming/types.d.ts.map +1 -1
  168. package/dist/styles/controls.d.ts +1 -1
  169. package/dist/styles/controls.d.ts.map +1 -1
  170. package/dist/utils/static-html.d.ts +12 -0
  171. package/dist/utils/static-html.d.ts.map +1 -0
  172. package/dist/vizor.js +4 -4
  173. package/package.json +8 -5
  174. package/dist/__tests__/license-checker.test.d.ts.map +0 -1
  175. package/dist/__tests__/master-key.test.d.ts.map +0 -1
  176. package/dist/annotations-C0ugfCn4.js.map +0 -1
  177. package/dist/captions-BP0H2E03.js.map +0 -1
  178. package/dist/collab-BXPomrS0.js.map +0 -1
  179. package/dist/dash-adapter-tid9ZyhO.js.map +0 -1
  180. package/dist/hls-adapter-BivH46L_.js +0 -97
  181. package/dist/hls-adapter-BivH46L_.js.map +0 -1
  182. package/dist/register-CwnmiGEs.js.map +0 -1
  183. /package/dist/{__tests__ → license/__tests__}/license-checker.test.d.ts +0 -0
  184. /package/dist/{__tests__ → license/__tests__}/master-key.test.d.ts +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"file":"register-CwnmiGEs.js","sources":["../src/core/html-element.ts","../src/core/player-state.ts","../src/core/event-emitter.ts","../src/config/meta-config.ts","../src/license/master-key.ts","../src/license/license-checker.ts","../src/license/watermark-guard.ts","../src/license/watermark.ts","../src/license/integrity.ts","../src/core/base-element.ts","../src/renderer/projections/equirect-projection.ts","../src/renderer/projections/flat-projection.ts","../src/renderer/projections/cubemap-projection.ts","../src/renderer/shaders/eac.glsl.ts","../src/renderer/projections/eac-projection.ts","../src/renderer/shaders/fisheye.glsl.ts","../src/renderer/projections/fisheye-projection.ts","../src/renderer/camera-controller.ts","../src/renderer/renderer.ts","../src/streaming/progressive-adapter.ts","../src/streaming/adapter-factory.ts","../src/streaming/source-manager.ts","../src/utils/fullscreen.ts","../src/styles/controls.ts","../src/controls/controls-overlay.ts","../src/controls/play-button.ts","../src/controls/seek-bar.ts","../src/controls/volume-control.ts","../src/controls/fullscreen-button.ts","../src/controls/vr-button.ts","../src/controls/compass.ts","../src/controls/quality-menu.ts","../src/controls/keyboard-shortcuts.ts","../src/xr/xr-controller.ts","../src/xr/xr-manager.ts","../src/controls/live-indicator.ts","../src/analytics/analytics-collector.ts","../src/analytics/gaze-tracker.ts","../src/analytics/index.ts","../src/controls/cc-button.ts","../src/core/media-element.ts","../src/elements/vz-video.ts","../src/elements/vz-img.ts","../src/hotspots/hotspot-renderer.ts","../src/hotspots/hotspot-interaction.ts","../src/hotspots/hotspot-manager.ts","../src/elements/vz-tour.ts","../src/elements/vz-tour-img.ts","../src/elements/vz-tour-portal.ts","../src/renderer/projections/cinema-projection.ts","../src/elements/vz-cinema.ts","../src/elements/vz-live.ts","../src/controls/playlist-controls.ts","../src/controls/playlist-panel.ts","../src/elements/vz-playlist.ts","../src/elements/vz-annotation.ts","../src/register.ts"],"sourcesContent":["/**\n * SSR-safe HTMLElement base.\n *\n * On a server (no DOM) `HTMLElement` is undefined, so `class X extends\n * HTMLElement` throws a ReferenceError at module-evaluation time — which crashes\n * any framework that imports the player during SSR (Next.js RSC, Nuxt,\n * SvelteKit). Fall back to a no-op stand-in there so the package can be imported\n * without throwing. The classes are never instantiated on the server because\n * custom-element registration is separately guarded to a no-op when\n * `customElements` is unavailable.\n */\nexport const HTMLElementBase: typeof HTMLElement =\n typeof HTMLElement !== 'undefined'\n ? HTMLElement\n : (class {} as unknown as typeof HTMLElement);\n","import type { VzFormat, StereoMode, VzError, QualityLevel, LicenseResult } from '@vizor-vr/shared';\n\n/** Shape of all player state */\nexport interface PlayerStateShape {\n // Playback\n playing: boolean;\n paused: boolean;\n currentTime: number;\n duration: number;\n buffered: number;\n seeking: boolean;\n ended: boolean;\n volume: number;\n muted: boolean;\n playbackRate: number;\n\n // Loading\n loading: boolean;\n loadProgress: number;\n\n // Renderer\n format: VzFormat | null;\n stereoMode: StereoMode;\n\n // Quality\n currentQuality: string;\n availableQualities: QualityLevel[];\n autoQuality: boolean;\n\n // Camera\n lat: number;\n lon: number;\n fov: number;\n\n // XR\n xrSupported: boolean;\n xrActive: boolean;\n\n // Controls\n controlsVisible: boolean;\n fullscreen: boolean;\n\n // Live\n isLive: boolean;\n isAtLiveEdge: boolean;\n\n // License\n license: LicenseResult | null;\n\n // Tour\n currentProbeId: string | null;\n tourProbeCount: number;\n\n // Error\n error: VzError | null;\n}\n\ntype Listener<T> = (value: T, prev: T) => void;\ntype AnyListener = (key: string, value: unknown, prev: unknown) => void;\n\nconst INITIAL_STATE: PlayerStateShape = {\n playing: false,\n paused: true,\n currentTime: 0,\n duration: 0,\n buffered: 0,\n seeking: false,\n ended: false,\n volume: 1,\n muted: false,\n playbackRate: 1,\n loading: false,\n loadProgress: 0,\n format: null,\n stereoMode: 'mono',\n currentQuality: 'auto',\n availableQualities: [],\n autoQuality: true,\n lat: 0,\n lon: 0,\n fov: 75,\n xrSupported: false,\n xrActive: false,\n controlsVisible: true,\n fullscreen: false,\n isLive: false,\n isAtLiveEdge: false,\n license: null,\n currentProbeId: null,\n tourProbeCount: 0,\n error: null,\n};\n\n/**\n * Lightweight reactive state store for the player.\n * Supports per-key subscriptions and batch updates.\n */\nexport class PlayerState {\n private state: PlayerStateShape;\n private listeners = new Map<keyof PlayerStateShape, Set<Listener<unknown>>>();\n private globalListeners = new Set<AnyListener>();\n\n constructor(initial?: Partial<PlayerStateShape>) {\n this.state = { ...INITIAL_STATE, ...initial };\n }\n\n /** Get the current value of a state key */\n get<K extends keyof PlayerStateShape>(key: K): PlayerStateShape[K] {\n return this.state[key];\n }\n\n /** Get the entire state snapshot (read-only) */\n getSnapshot(): Readonly<PlayerStateShape> {\n return this.state;\n }\n\n /** Set a single state value. No-op if value is unchanged. */\n set<K extends keyof PlayerStateShape>(key: K, value: PlayerStateShape[K]): void {\n const prev = this.state[key];\n if (Object.is(prev, value)) return;\n this.state[key] = value;\n this.notify(key, value, prev);\n }\n\n /** Batch multiple state updates. Notifications fire after all values are set. */\n batch(updates: Partial<PlayerStateShape>): void {\n const changed: Array<[keyof PlayerStateShape, unknown, unknown]> = [];\n for (const key of Object.keys(updates) as Array<keyof PlayerStateShape>) {\n const value = updates[key];\n const prev = this.state[key];\n if (!Object.is(prev, value)) {\n (this.state as unknown as Record<string, unknown>)[key] = value;\n changed.push([key, value, prev]);\n }\n }\n for (const [key, value, prev] of changed) {\n this.notify(key, value, prev);\n }\n }\n\n /** Subscribe to changes on a specific state key. Returns unsubscribe function. */\n subscribe<K extends keyof PlayerStateShape>(\n key: K,\n listener: Listener<PlayerStateShape[K]>,\n ): () => void {\n if (!this.listeners.has(key)) {\n this.listeners.set(key, new Set());\n }\n const typedListener = listener as Listener<unknown>;\n this.listeners.get(key)!.add(typedListener);\n return () => {\n this.listeners.get(key)?.delete(typedListener);\n };\n }\n\n /** Subscribe to all state changes. Returns unsubscribe function. */\n subscribeAll(listener: AnyListener): () => void {\n this.globalListeners.add(listener);\n return () => {\n this.globalListeners.delete(listener);\n };\n }\n\n /** Reset state to initial values */\n reset(): void {\n const oldState = { ...this.state };\n this.state = { ...INITIAL_STATE };\n for (const key of Object.keys(oldState) as Array<keyof PlayerStateShape>) {\n if (!Object.is(oldState[key], this.state[key])) {\n this.notify(key, this.state[key], oldState[key]);\n }\n }\n }\n\n private notify(key: keyof PlayerStateShape, value: unknown, prev: unknown): void {\n this.listeners.get(key)?.forEach((fn) => fn(value, prev));\n this.globalListeners.forEach((fn) => fn(key as string, value, prev));\n }\n}\n","import type { VzEvent, VzEventDetailMap } from '@vizor-vr/shared';\n\ntype EventCallback = (event: CustomEvent) => void;\n\n/**\n * Typed event emitter that dispatches CustomEvents on the host element.\n * Supports both HTML5 media events and Vizor custom events.\n */\nexport class VzEventEmitter {\n private element: HTMLElement;\n private handlers = new Map<string, Set<EventCallback>>();\n\n constructor(element: HTMLElement) {\n this.element = element;\n }\n\n /** Emit a standard media event (no detail) */\n emitMedia(type: string): void {\n const event = new Event(type, { bubbles: true, composed: true });\n this.element.dispatchEvent(event);\n }\n\n /** Emit a Vizor custom event with typed detail */\n emit<E extends keyof VzEventDetailMap>(\n type: E,\n detail?: VzEventDetailMap[E],\n ): void {\n const event = new CustomEvent(type, {\n bubbles: true,\n composed: true,\n detail,\n });\n this.element.dispatchEvent(event);\n }\n\n /** Add an event listener */\n on(type: VzEvent | string, callback: EventCallback): () => void {\n if (!this.handlers.has(type)) {\n this.handlers.set(type, new Set());\n }\n this.handlers.get(type)!.add(callback);\n this.element.addEventListener(type, callback as EventListener);\n return () => this.off(type, callback);\n }\n\n /** Remove an event listener */\n off(type: VzEvent | string, callback: EventCallback): void {\n this.handlers.get(type)?.delete(callback);\n this.element.removeEventListener(type, callback as EventListener);\n }\n\n /** Remove all event listeners */\n removeAll(): void {\n for (const [type, callbacks] of this.handlers) {\n for (const callback of callbacks) {\n this.element.removeEventListener(type, callback as EventListener);\n }\n }\n this.handlers.clear();\n }\n}\n","import type { PlayerConfig } from '@vizor-vr/shared';\nimport { DEFAULT_CONFIG } from '@vizor-vr/shared';\n\n/** Meta tag name to config key mapping */\nconst META_MAP: Record<string, keyof PlayerConfig> = {\n 'api-key': 'apiKey',\n 'license-key': 'licenseKey',\n 'license-mode': 'licenseMode',\n 'api-endpoint': 'apiEndpoint',\n 'primary-color': 'primaryColor',\n 'secondary-color': 'secondaryColor',\n 'brand-name': 'brandName',\n 'brand-logo': 'brandLogo',\n 'brand-url': 'brandUrl',\n 'watermark': 'watermarkLogo',\n 'hide-compass': 'hideCompass',\n 'hide-vr-button': 'hideVrButton',\n 'hide-settings': 'hideSettings',\n 'hide-fullscreen': 'hideFullscreen',\n 'hide-controls': 'hideControls',\n 'hide-title': 'hideTitle',\n 'hide-brand-logo': 'hideBrandLogo',\n 'hide-watermark': 'hideWatermark',\n};\n\n/** Boolean meta tags (presence = true) */\nconst BOOLEAN_METAS = new Set([\n 'hideCompass',\n 'hideVrButton',\n 'hideSettings',\n 'hideFullscreen',\n 'hideControls',\n 'hideTitle',\n 'hideBrandLogo',\n 'hideWatermark',\n]);\n\n/**\n * Read all <meta name=\"vz:*\"> tags from the document head\n * and return partial config overrides.\n */\nexport function readMetaConfig(): Partial<PlayerConfig> {\n const config: Partial<Record<string, unknown>> = {};\n\n const metas = document.querySelectorAll('meta[name^=\"vz:\"]');\n metas.forEach((meta) => {\n const rawName = meta.getAttribute('name');\n if (!rawName) return;\n\n const metaKey = rawName.replace('vz:', '');\n const configKey = META_MAP[metaKey];\n if (!configKey) return;\n\n const content = meta.getAttribute('content') ?? '';\n\n if (BOOLEAN_METAS.has(configKey)) {\n config[configKey] = content === '' || content === 'true';\n } else {\n config[configKey] = content;\n }\n });\n\n return config as Partial<PlayerConfig>;\n}\n\n/**\n * Resolve the full player config by merging:\n * 1. Defaults\n * 2. Meta tag overrides\n * 3. Element attribute overrides\n */\nexport function resolveConfig(\n elementAttrs: Partial<PlayerConfig>,\n metaOverrides?: Partial<PlayerConfig>,\n): PlayerConfig {\n const meta = metaOverrides ?? readMetaConfig();\n return {\n ...DEFAULT_CONFIG,\n ...meta,\n ...elementAttrs,\n };\n}\n","import type { LicenseResult } from '@vizor-vr/shared';\nimport { getFeaturesForTier } from '@vizor-vr/shared';\n\n/**\n * Build-time constant injected via Vite `define`.\n * Contains the hex-encoded Ed25519 public key for the master key.\n * In source code this is a placeholder — the real value comes from env.\n */\ndeclare const __VZ_MASTER_PUB_KEY__: string;\n\n/**\n * Check if a standalone license result represents a master key.\n *\n * A master key is a specially-signed standalone key where:\n * 1. The payload tier is 'master'\n * 2. The payload publicKey matches the build-time master public key\n *\n * If both conditions are met, the result is upgraded to enterprise tier.\n *\n * @returns The original result (possibly upgraded to enterprise), or null if not a master key\n */\nexport function checkMasterKey(licenseKey: string): LicenseResult | null {\n try {\n // Check if build-time constant is available\n const masterPubKey = typeof __VZ_MASTER_PUB_KEY__ !== 'undefined' ? __VZ_MASTER_PUB_KEY__ : '';\n if (!masterPubKey) return null;\n\n // Decode the license key to check the payload\n const base64 = licenseKey.replace(/-/g, '+').replace(/_/g, '/');\n const decoded = atob(base64);\n const envelope = JSON.parse(decoded) as { payload: string; signature: string };\n const payload = JSON.parse(envelope.payload) as {\n tier: string;\n domains: string[];\n publicKey: string;\n expiresAt?: string;\n };\n\n // Check master key conditions\n if (payload.tier !== 'master') return null;\n if (payload.publicKey !== masterPubKey) return null;\n\n // Master key detected — grant enterprise features\n return {\n valid: true,\n tier: 'enterprise',\n features: getFeaturesForTier('enterprise'),\n domains: payload.domains,\n expiresAt: payload.expiresAt,\n };\n } catch {\n return null;\n }\n}\n","import type { LicenseResult, LicenseMode } from '@vizor-vr/shared';\nimport { getFeaturesForTier } from '@vizor-vr/shared';\nimport { checkMasterKey } from './master-key.js';\nimport { registerIntegrityCheck, verifyFunctionIntegrity } from './integrity.js';\n\nexport interface LicenseConfig {\n apiKey?: string;\n licenseKey?: string;\n licenseMode?: LicenseMode;\n apiEndpoint?: string;\n /** If true, skip phone-home for standalone keys (useful for testing) */\n skipPhoneHome?: boolean;\n}\n\nconst DEFAULT_API_ENDPOINT = 'https://api.vizor.io';\n\n/**\n * Validate a license and return the result.\n *\n * SaaS mode: POST to API with apiKey + domain.\n * Standalone mode: decode base64url key → verify Ed25519 signature → check expiry/domain.\n * No key: return free tier.\n */\nexport async function checkLicense(config: LicenseConfig): Promise<LicenseResult> {\n const mode = config.licenseMode ?? (config.licenseKey ? 'standalone' : 'saas');\n\n if (mode === 'standalone' && config.licenseKey) {\n // Check master key first (fast path, no async)\n const masterResult = checkMasterKey(config.licenseKey);\n if (masterResult) return masterResult;\n\n return checkStandaloneLicense(config.licenseKey, config.apiEndpoint, config.skipPhoneHome);\n }\n\n if (mode === 'saas' && config.apiKey) {\n return checkSaasLicense(config.apiKey, config.apiEndpoint);\n }\n\n // No valid key provided — free tier\n return freeTierResult('No license key provided');\n}\n\nasync function checkSaasLicense(apiKey: string, apiEndpoint?: string): Promise<LicenseResult> {\n const endpoint = apiEndpoint || DEFAULT_API_ENDPOINT;\n const domain = typeof window !== 'undefined' ? window.location.hostname : 'localhost';\n\n try {\n const res = await fetch(`${endpoint}/api/v1/license/validate`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ apiKey, domain }),\n });\n\n if (!res.ok) {\n console.warn('[Vizor] License validation failed:', res.status);\n return freeTierResult(`API returned ${res.status}`);\n }\n\n const result: LicenseResult = await res.json();\n return result;\n } catch (err) {\n console.warn('[Vizor] License validation network error:', err);\n return freeTierResult('Network error during license validation');\n }\n}\n\ninterface StandalonePayload {\n tier: string;\n domains: string[];\n expiresAt?: string;\n publicKey: string;\n}\n\nconst PHONE_HOME_CACHE_KEY = '__vz_lk_cache';\n\nasync function checkStandaloneLicense(\n licenseKey: string,\n apiEndpoint?: string,\n skipPhoneHome?: boolean,\n): Promise<LicenseResult> {\n try {\n // Decode base64url → JSON { payload, signature }\n const decoded = atob(licenseKey.replace(/-/g, '+').replace(/_/g, '/'));\n const envelope = JSON.parse(decoded) as {\n payload: string;\n signature: string;\n };\n\n const payload: StandalonePayload = JSON.parse(envelope.payload);\n\n // Verify Ed25519 signature (dynamic import to keep bundle light)\n try {\n // @ts-ignore — optional peer dependency, may not be installed\n const ed = await import('@noble/ed25519');\n const msgBytes = new TextEncoder().encode(envelope.payload);\n const sigBytes = hexToBytes(envelope.signature);\n const pubBytes = hexToBytes(payload.publicKey);\n const valid = await ed.verifyAsync(sigBytes, msgBytes, pubBytes);\n if (!valid) {\n return freeTierResult('Invalid license signature');\n }\n } catch {\n console.warn('[Vizor] Ed25519 verification unavailable, skipping signature check');\n }\n\n // Check expiry\n if (payload.expiresAt) {\n const expires = new Date(payload.expiresAt);\n if (expires < new Date()) {\n return freeTierResult('License expired');\n }\n }\n\n // Check domain\n const currentDomain = typeof window !== 'undefined' ? window.location.hostname : 'localhost';\n if (payload.domains.length > 0 && !matchesDomain(currentDomain, payload.domains)) {\n return freeTierResult('Domain not authorized');\n }\n\n // Local validation passed — build initial result\n const tier = payload.tier as LicenseResult['tier'];\n const localResult: LicenseResult = {\n valid: true,\n tier,\n features: getFeaturesForTier(tier),\n domains: payload.domains,\n expiresAt: payload.expiresAt,\n };\n\n // Phone home to refresh subscription status (async, non-blocking on first load per session)\n if (!skipPhoneHome && typeof window !== 'undefined') {\n phoneHome(licenseKey, apiEndpoint, localResult);\n }\n\n // Check if we have a cached server result that's fresher\n const cached = getCachedResult();\n if (cached) {\n return cached;\n }\n\n return localResult;\n } catch (err) {\n console.warn('[Vizor] Standalone license decode failed:', err);\n return freeTierResult('Invalid license key format');\n }\n}\n\n/**\n * Phone home to server on first load per session.\n * Updates localStorage cache with server result.\n */\nfunction phoneHome(licenseKey: string, apiEndpoint?: string, fallback?: LicenseResult): void {\n // Only phone home once per session\n const sessionKey = '__vz_ph_done';\n if (typeof sessionStorage !== 'undefined') {\n try {\n if (sessionStorage.getItem(sessionKey)) return;\n sessionStorage.setItem(sessionKey, '1');\n } catch {\n // sessionStorage unavailable\n }\n }\n\n const endpoint = apiEndpoint || DEFAULT_API_ENDPOINT;\n const domain = typeof window !== 'undefined' ? window.location.hostname : 'localhost';\n\n fetch(`${endpoint}/api/v1/license/validate-standalone`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ licenseKey, domain }),\n })\n .then((res) => {\n if (!res.ok) throw new Error(`Status ${res.status}`);\n return res.json();\n })\n .then((serverResult: LicenseResult) => {\n // Cache the server result\n try {\n localStorage.setItem(\n PHONE_HOME_CACHE_KEY,\n JSON.stringify({ result: serverResult, ts: Date.now() }),\n );\n } catch {\n // localStorage unavailable\n }\n })\n .catch(() => {\n // Network error — use local result (already returned)\n console.debug('[Vizor] Phone-home failed, using local license result');\n });\n}\n\n/**\n * Get cached phone-home result from localStorage.\n * Only returns results less than 24 hours old.\n */\nfunction getCachedResult(): LicenseResult | null {\n try {\n const raw = localStorage.getItem(PHONE_HOME_CACHE_KEY);\n if (!raw) return null;\n const { result, ts } = JSON.parse(raw) as { result: LicenseResult; ts: number };\n // Cache valid for 24 hours\n if (Date.now() - ts > 24 * 60 * 60 * 1000) {\n localStorage.removeItem(PHONE_HOME_CACHE_KEY);\n return null;\n }\n return result;\n } catch {\n return null;\n }\n}\n\nfunction freeTierResult(message: string): LicenseResult {\n return {\n valid: false,\n tier: 'free',\n features: getFeaturesForTier('free'),\n message,\n };\n}\n\nfunction matchesDomain(hostname: string, allowedDomains: string[]): boolean {\n return allowedDomains.some((pattern) => {\n if (pattern.startsWith('*.')) {\n const suffix = pattern.slice(1); // .example.com\n return hostname.endsWith(suffix) || hostname === pattern.slice(2);\n }\n return hostname === pattern;\n });\n}\n\nfunction hexToBytes(hex: string): Uint8Array {\n const bytes = new Uint8Array(hex.length / 2);\n for (let i = 0; i < bytes.length; i++) {\n bytes[i] = parseInt(hex.substring(i * 2, i * 2 + 2), 16);\n }\n return bytes;\n}\n","/**\n * Watermark Guard — MutationObserver that re-injects the watermark\n * if it's removed from the shadow DOM.\n *\n * Protection strategies:\n * 1. MutationObserver watches for childList changes\n * 2. Re-injects watermark with random delay (100-500ms)\n * 3. Creates a backup watermark with a different class name\n * 4. CSS uses !important to resist style overrides\n */\n\nimport { sanitizeUrl } from '../utils/sanitize.js';\n\nlet observer: MutationObserver | null = null;\nlet guardActive = false;\n\n/** CSS for hardened watermark styling */\nconst HARDENED_WATERMARK_CSS = `\n .vz-watermark, .vz-wm-backup {\n position: absolute !important;\n bottom: 48px !important;\n right: 12px !important;\n z-index: 100 !important;\n pointer-events: auto !important;\n display: block !important;\n visibility: visible !important;\n opacity: 1 !important;\n transform: none !important;\n clip: auto !important;\n clip-path: none !important;\n width: auto !important;\n height: auto !important;\n overflow: visible !important;\n }\n .vz-watermark a, .vz-wm-backup a {\n display: inline-block !important;\n padding: 4px 10px !important;\n background: rgba(0, 0, 0, 0.4) !important;\n border-radius: 4px !important;\n color: rgba(255, 255, 255, 0.6) !important;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important;\n font-size: 10px !important;\n text-decoration: none !important;\n letter-spacing: 0.02em !important;\n visibility: visible !important;\n opacity: 1 !important;\n }\n`;\n\n/**\n * Initialize the watermark guard on a shadow root.\n * Watches for removal of .vz-watermark and re-injects it.\n */\nexport function initWatermarkGuard(\n shadowRoot: ShadowRoot | HTMLElement,\n linkUrl: string = 'https://vizor.io',\n logoUrl?: string,\n): void {\n if (guardActive) return;\n guardActive = true;\n\n // Inject hardened CSS\n const guardStyle = document.createElement('style');\n guardStyle.setAttribute('data-vz-guard', '1');\n guardStyle.textContent = HARDENED_WATERMARK_CSS;\n shadowRoot.appendChild(guardStyle);\n\n // Create backup watermark (hidden initially, different class)\n const backup = createWatermarkEl('vz-wm-backup', linkUrl, logoUrl);\n backup.style.display = 'none';\n shadowRoot.appendChild(backup);\n\n observer = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n if (mutation.type !== 'childList') continue;\n\n // Check if primary watermark was removed\n const primary = shadowRoot.querySelector('.vz-watermark');\n if (!primary) {\n // Re-inject with random delay to make it harder to script removal\n const delay = 100 + Math.random() * 400;\n setTimeout(() => {\n if (!shadowRoot.querySelector('.vz-watermark')) {\n const newWatermark = createWatermarkEl('vz-watermark', linkUrl, logoUrl);\n shadowRoot.appendChild(newWatermark);\n }\n }, delay);\n\n // Show backup immediately\n const backupEl = shadowRoot.querySelector('.vz-wm-backup') as HTMLElement;\n if (backupEl) backupEl.style.display = 'block';\n }\n\n // Check if backup was removed\n const backupCheck = shadowRoot.querySelector('.vz-wm-backup');\n if (!backupCheck) {\n const newBackup = createWatermarkEl('vz-wm-backup', linkUrl, logoUrl);\n shadowRoot.appendChild(newBackup);\n }\n\n // Check if guard style was removed\n const guardStyleCheck = shadowRoot.querySelector('[data-vz-guard]');\n if (!guardStyleCheck) {\n const newStyle = document.createElement('style');\n newStyle.setAttribute('data-vz-guard', '1');\n newStyle.textContent = HARDENED_WATERMARK_CSS;\n shadowRoot.appendChild(newStyle);\n }\n }\n });\n\n observer.observe(shadowRoot, { childList: true, subtree: false });\n}\n\n/**\n * Destroy the watermark guard (for cleanup/testing).\n */\nexport function destroyWatermarkGuard(): void {\n if (observer) {\n observer.disconnect();\n observer = null;\n }\n guardActive = false;\n}\n\n/**\n * Check if the watermark guard is currently active.\n */\nexport function isWatermarkGuardActive(): boolean {\n return guardActive;\n}\n\nfunction createWatermarkEl(className: string, linkUrl: string, logoUrl?: string): HTMLElement {\n const el = document.createElement('div');\n el.className = className;\n\n // URLs are already sanitized by createWatermark; sanitize again defensively\n // and build via DOM APIs so author-supplied values can never inject markup.\n const safeLink = sanitizeUrl(linkUrl, 'https://vizor.io');\n const safeLogo = sanitizeUrl(logoUrl, '');\n\n const anchor = document.createElement('a');\n anchor.href = safeLink;\n anchor.target = '_blank';\n anchor.rel = 'noopener';\n\n if (safeLogo) {\n const img = document.createElement('img');\n img.src = safeLogo;\n img.alt = 'Watermark';\n img.style.cssText =\n 'max-height:24px;max-width:120px;width:auto;height:auto;object-fit:contain';\n anchor.appendChild(img);\n } else {\n anchor.textContent = 'Powered by Vizor';\n }\n\n el.appendChild(anchor);\n return el;\n}\n","import type { LicenseResult } from '@vizor-vr/shared';\nimport { initWatermarkGuard } from './watermark-guard.js';\nimport { sanitizeUrl } from '../utils/sanitize.js';\n\nexport interface WatermarkOptions {\n /** Custom logo image URL (replaces default text) */\n logoUrl?: string;\n /** Link URL when watermark is clicked */\n linkUrl?: string;\n}\n\n/**\n * Create a watermark element if the license doesn't allow removal.\n * Returns the element (appended to container) or null if no watermark needed.\n *\n * - Free tier (no custom branding): shows \"Powered by Vizor\" text\n * - Paid tier with custom branding + watermarkLogo: shows custom logo image\n * - Paid tier with removeWatermark: no watermark at all\n */\nexport function createWatermark(\n container: ShadowRoot | HTMLElement,\n license: LicenseResult,\n options?: WatermarkOptions,\n): HTMLElement | null {\n if (license.features.removeWatermark) return null;\n\n const el = document.createElement('div');\n el.className = 'vz-watermark';\n\n // Sanitize author-supplied URLs (brand-url / brand-logo attributes) to avoid\n // attribute-injection / javascript: URLs. Build via DOM APIs, never innerHTML.\n const linkUrl = sanitizeUrl(options?.linkUrl, 'https://vizor.io');\n const logoUrl = sanitizeUrl(options?.logoUrl, '');\n\n const anchor = document.createElement('a');\n anchor.href = linkUrl;\n anchor.target = '_blank';\n anchor.rel = 'noopener';\n\n if (logoUrl) {\n const img = document.createElement('img');\n img.src = logoUrl;\n img.alt = 'Watermark';\n img.className = 'vz-watermark-logo';\n anchor.appendChild(img);\n } else {\n anchor.textContent = 'Powered by Vizor';\n }\n el.appendChild(anchor);\n\n const style = document.createElement('style');\n style.textContent = `\n .vz-watermark {\n position: absolute;\n bottom: 48px;\n right: 12px;\n z-index: 100;\n pointer-events: auto;\n }\n .vz-watermark a {\n display: inline-block;\n padding: 4px 10px;\n background: rgba(0, 0, 0, 0.4);\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n border: 1px solid rgba(255, 255, 255, 0.06);\n border-radius: 6px;\n color: rgba(255, 255, 255, 0.6);\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n font-size: 10px;\n text-decoration: none;\n letter-spacing: 0.02em;\n transition: background 0.2s, color 0.2s, border-color 0.2s;\n }\n .vz-watermark a:hover {\n background: rgba(0, 0, 0, 0.55);\n border-color: rgba(255, 255, 255, 0.1);\n color: rgba(255, 255, 255, 0.85);\n }\n .vz-watermark-logo {\n display: block;\n max-height: 24px;\n max-width: 120px;\n width: auto;\n height: auto;\n object-fit: contain;\n }\n `;\n\n container.appendChild(style);\n container.appendChild(el);\n\n // Start watermark guard to prevent removal via DevTools (pass sanitized URLs)\n initWatermarkGuard(container, linkUrl, logoUrl || undefined);\n\n return el;\n}\n\n/** Remove watermark from container */\nexport function removeWatermark(container: ShadowRoot | HTMLElement): void {\n const el = container.querySelector('.vz-watermark');\n if (el) el.remove();\n}\n","import type { LicenseResult } from '@vizor-vr/shared';\nimport { getFeaturesForTier } from '@vizor-vr/shared';\n\n/**\n * Registry of functions to integrity-check.\n * Maps function name to its expected SHA-256 hash (computed at init time).\n */\nconst registeredChecks: Map<string, string> = new Map();\nlet initialized = false;\n\n/**\n * Compute a SHA-256 hash of a function's source code.\n * Uses the Web Crypto API (available in all modern browsers).\n */\nexport async function computeFunctionHash(fn: Function): Promise<string> {\n const source = fn.toString();\n const bytes = new TextEncoder().encode(source);\n\n if (typeof crypto !== 'undefined' && crypto.subtle) {\n const hash = await crypto.subtle.digest('SHA-256', bytes);\n return Array.from(new Uint8Array(hash))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n }\n\n // Fallback: simple hash for environments without Web Crypto\n let hash = 0;\n for (let i = 0; i < source.length; i++) {\n const char = source.charCodeAt(i);\n hash = ((hash << 5) - hash + char) | 0;\n }\n return Math.abs(hash).toString(16);\n}\n\n/**\n * Register a function for integrity checking.\n * Must be called during initialization, before any potential tampering.\n */\nexport async function registerIntegrityCheck(name: string, fn: Function): Promise<void> {\n const hash = await computeFunctionHash(fn);\n registeredChecks.set(name, hash);\n initialized = true;\n}\n\n/**\n * Verify integrity of all registered functions.\n *\n * If any function's source has changed since registration,\n * returns a free-tier result (tamper detected).\n * Otherwise returns null (integrity OK).\n */\nexport async function verifyIntegrity(): Promise<LicenseResult | null> {\n if (!initialized || registeredChecks.size === 0) {\n return null; // No checks registered — skip\n }\n\n // We need to re-import the functions to check their current state\n // But since we stored hashes at registration time, we compare against stored hashes\n // The caller should pass the same function references for verification\n\n return null; // Integrity checks happen via verifyFunctionIntegrity\n}\n\n/**\n * Verify a specific function's integrity against its registered hash.\n * Returns a free-tier result if tampered, null if OK.\n */\nexport async function verifyFunctionIntegrity(\n name: string,\n fn: Function,\n): Promise<LicenseResult | null> {\n const expectedHash = registeredChecks.get(name);\n if (!expectedHash) return null; // Not registered\n\n const currentHash = await computeFunctionHash(fn);\n\n if (currentHash !== expectedHash) {\n console.warn(`[Vizor] Integrity check failed for ${name}`);\n return {\n valid: false,\n tier: 'free',\n features: getFeaturesForTier('free'),\n message: 'License validation integrity check failed',\n };\n }\n\n return null; // OK\n}\n\n/**\n * Check if integrity system has been initialized.\n */\nexport function isIntegrityInitialized(): boolean {\n return initialized;\n}\n\n/**\n * Get count of registered checks.\n */\nexport function getRegisteredCheckCount(): number {\n return registeredChecks.size;\n}\n","import type { PlayerConfig, VzFormat, FeatureFlags, LicenseResult } from '@vizor-vr/shared';\nimport { HTMLElementBase } from './html-element.js';\nimport { parseFormat, getFeaturesForTier } from '@vizor-vr/shared';\nimport { PlayerState } from './player-state.js';\nimport { VzEventEmitter } from './event-emitter.js';\nimport { readMetaConfig, resolveConfig } from '../config/meta-config.js';\nimport { checkLicense } from '../license/license-checker.js';\nimport { createWatermark, removeWatermark } from '../license/watermark.js';\nimport { registerIntegrityCheck, verifyFunctionIntegrity } from '../license/integrity.js';\n\n/** Attribute name to config key mapping */\nconst ATTR_MAP: Record<string, keyof PlayerConfig> = {\n format: 'format',\n title: 'title',\n author: 'author',\n 'author-href': 'authorHref',\n poster: 'poster',\n loop: 'loop',\n muted: 'muted',\n preload: 'preload',\n fps: 'fps',\n crossorigin: 'crossorigin',\n 'cors-fallback-url': 'corsFallbackUrl',\n 'cors-fallback-redirect-confirmation': 'corsFallbackRedirectConfirmation',\n 'audio-format': 'audioFormat',\n seekable: 'seekable',\n 'display-mode': 'displayMode',\n width: 'width',\n height: 'height',\n aspect: 'aspect',\n 'start-lat': 'startLat',\n 'start-lon': 'startLon',\n 'focal-length': 'focalLength',\n 'min-focal-length': 'minFocalLength',\n 'max-focal-length': 'maxFocalLength',\n 'license-key': 'licenseKey',\n 'license-mode': 'licenseMode',\n 'api-endpoint': 'apiEndpoint',\n 'content-id': 'contentId',\n 'collab-server': 'collabServer',\n 'collab-room': 'collabRoom',\n 'collab-role': 'collabRole',\n 'collab-user-id': 'collabUserId',\n 'collab-display-name': 'collabDisplayName',\n 'collab-password': 'collabPassword',\n 'api-key': 'apiKey',\n 'primary-color': 'primaryColor',\n 'secondary-color': 'secondaryColor',\n 'brand-name': 'brandName',\n 'brand-logo': 'brandLogo',\n 'brand-url': 'brandUrl',\n 'hide-compass': 'hideCompass',\n 'hide-vr-button': 'hideVrButton',\n 'hide-settings': 'hideSettings',\n 'hide-fullscreen': 'hideFullscreen',\n 'hide-controls': 'hideControls',\n 'hide-title': 'hideTitle',\n 'hide-brand-logo': 'hideBrandLogo',\n 'hide-watermark': 'hideWatermark',\n};\n\n/** Boolean attributes (presence = true) */\nconst BOOLEAN_ATTRS = new Set([\n 'loop', 'muted', 'seekable', 'cors-fallback-redirect-confirmation',\n 'hide-compass', 'hide-vr-button', 'hide-settings', 'hide-fullscreen',\n 'hide-controls', 'hide-title', 'hide-brand-logo', 'hide-watermark',\n]);\n\n/** Numeric attributes */\nconst NUMBER_ATTRS = new Set([\n 'fps',\n 'start-lat',\n 'start-lon',\n 'focal-length',\n 'min-focal-length',\n 'max-focal-length',\n]);\n\n/**\n * Abstract base class for all Vizor custom elements.\n *\n * Responsibilities:\n * - Shadow DOM creation\n * - Attribute observation and reflection to config\n * - Meta tag configuration reading\n * - Responsive sizing via ResizeObserver\n * - Poster image display\n * - Event emission\n */\nexport abstract class VzBaseElement extends HTMLElementBase {\n static get observedAttributes(): string[] {\n return Object.keys(ATTR_MAP);\n }\n\n protected shadow: ShadowRoot;\n protected state: PlayerState;\n protected events: VzEventEmitter;\n protected config!: PlayerConfig;\n protected license: LicenseResult | null = null;\n private resizeObserver: ResizeObserver | null = null;\n private metaConfig: Partial<PlayerConfig> | null = null;\n private watermarkEl: HTMLElement | null = null;\n\n constructor() {\n super();\n this.shadow = this.attachShadow({ mode: 'open' });\n this.state = new PlayerState();\n this.events = new VzEventEmitter(this);\n }\n\n connectedCallback(): void {\n // Read meta tags once (cached per page)\n if (!this.metaConfig) {\n this.metaConfig = readMetaConfig();\n }\n\n // Resolve full config\n this.config = this.buildConfig();\n\n // Apply sizing\n this.applySizing();\n\n // Set up resize observer\n this.resizeObserver = new ResizeObserver(() => this.onResize());\n this.resizeObserver.observe(this);\n\n // Inject base styles\n this.injectBaseStyles();\n\n // Subclass initialization\n this.onConnected();\n\n // License check (fire-and-forget, non-blocking)\n this.checkLicenseAsync();\n }\n\n disconnectedCallback(): void {\n this.resizeObserver?.disconnect();\n this.resizeObserver = null;\n this.events.removeAll();\n removeWatermark(this.shadow);\n this.watermarkEl = null;\n this.onDisconnected();\n }\n\n attributeChangedCallback(name: string, oldVal: string | null, newVal: string | null): void {\n if (oldVal === newVal) return;\n this.config = this.buildConfig();\n this.onAttributeChanged(name, oldVal, newVal);\n }\n\n // --- Public API ---\n\n /** Get the resolved player configuration */\n getConfig(): Readonly<PlayerConfig> {\n return this.config;\n }\n\n /** Get the reactive state store */\n getState(): PlayerState {\n return this.state;\n }\n\n /** Get the current license result (null until validated) */\n getLicense(): Readonly<LicenseResult> | null {\n return this.license;\n }\n\n /** Check if a feature is enabled by the current license */\n protected hasFeature(feature: keyof FeatureFlags): boolean {\n if (!this.license) return false;\n return this.license.features[feature] === true;\n }\n\n // --- Protected hooks for subclasses ---\n\n protected abstract onConnected(): void;\n protected abstract onDisconnected(): void;\n protected abstract onAttributeChanged(\n name: string,\n oldVal: string | null,\n newVal: string | null,\n ): void;\n protected abstract onResize(): void;\n\n /** Called after license validation completes (override for post-license setup) */\n protected onLicenseResolved(): void {\n // Default no-op — subclasses can override\n }\n\n // --- Private helpers ---\n\n /** Async license validation (non-blocking) */\n private async checkLicenseAsync(): Promise<void> {\n try {\n // Register integrity checks on first run\n await registerIntegrityCheck('checkLicense', checkLicense);\n\n this.license = await checkLicense({\n apiKey: this.config.apiKey,\n licenseKey: this.config.licenseKey,\n licenseMode: this.config.licenseMode,\n apiEndpoint: this.config.apiEndpoint,\n });\n\n // Verify integrity of critical license functions\n const integrityFail = await verifyFunctionIntegrity('checkLicense', checkLicense);\n if (integrityFail) {\n this.license = integrityFail;\n }\n\n // Create watermark if needed\n // Only use custom brand logo if the license allows it\n const logoUrl = this.license.features.brandLogoColors\n ? this.config.brandLogo ?? this.config.watermarkLogo\n : this.config.watermarkLogo;\n this.watermarkEl = createWatermark(this.shadow, this.license, {\n logoUrl,\n linkUrl: this.config.brandUrl,\n });\n\n // Notify subclass that license has resolved (for theme gating etc.)\n this.onLicenseResolved();\n\n // Emit license event\n this.events.emit('vz-license', {\n valid: this.license.valid,\n tier: this.license.tier,\n message: this.license.message,\n });\n } catch (err) {\n console.warn('[Vizor] License check failed:', err);\n this.license = {\n valid: false,\n tier: 'free',\n features: getFeaturesForTier('free'),\n message: 'License check error',\n };\n this.watermarkEl = createWatermark(this.shadow, this.license, {\n logoUrl: this.config.watermarkLogo,\n linkUrl: this.config.brandUrl,\n });\n this.onLicenseResolved();\n this.events.emit('vz-license', {\n valid: false,\n tier: 'free',\n message: 'License check error',\n });\n }\n }\n\n /** Build config from defaults + meta tags + element attributes */\n private buildConfig(): PlayerConfig {\n const attrs: Partial<PlayerConfig> = {};\n\n for (const [attrName, configKey] of Object.entries(ATTR_MAP)) {\n const rawVal = this.getAttribute(attrName);\n if (rawVal === null && !BOOLEAN_ATTRS.has(attrName)) continue;\n\n if (BOOLEAN_ATTRS.has(attrName)) {\n (attrs as Record<string, unknown>)[configKey] = this.hasAttribute(attrName);\n } else if (NUMBER_ATTRS.has(attrName)) {\n const num = parseFloat(rawVal!);\n if (!isNaN(num)) {\n (attrs as Record<string, unknown>)[configKey] = num;\n }\n } else if (configKey === 'format') {\n const fmt = parseFormat(rawVal!);\n if (fmt) attrs.format = fmt;\n } else if (configKey === 'preload') {\n if (['auto', 'metadata', 'none'].includes(rawVal!)) {\n attrs.preload = rawVal as 'auto' | 'metadata' | 'none';\n }\n } else if (configKey === 'audioFormat') {\n if (rawVal === 'ambix') attrs.audioFormat = 'ambix';\n } else if (configKey === 'collabRole') {\n if (rawVal === 'host' || rawVal === 'viewer') {\n attrs.collabRole = rawVal;\n }\n } else {\n (attrs as Record<string, unknown>)[configKey] = rawVal;\n }\n }\n\n return resolveConfig(attrs, this.metaConfig ?? undefined);\n }\n\n /** Apply width/height/aspect styling to the host element */\n private applySizing(): void {\n if (!this.style.display || this.style.display === '') {\n this.style.display = 'block';\n }\n\n if (this.config.width) {\n this.style.width = this.config.width.includes('%') || this.config.width.includes('px')\n ? this.config.width\n : `${this.config.width}px`;\n } else if (!this.style.width) {\n this.style.width = '100%';\n }\n\n if (this.config.height) {\n this.style.height = this.config.height.includes('%') || this.config.height.includes('px')\n ? this.config.height\n : `${this.config.height}px`;\n } else if (this.config.aspect) {\n this.style.aspectRatio = this.config.aspect.replace(':', '/');\n } else if (!this.style.height) {\n this.style.aspectRatio = '16/9';\n }\n\n this.style.position = 'relative';\n this.style.overflow = 'hidden';\n this.style.backgroundColor = '#000';\n }\n\n /** Inject minimal base styles into shadow DOM */\n private injectBaseStyles(): void {\n const style = document.createElement('style');\n style.textContent = `\n :host {\n display: block;\n position: relative;\n overflow: hidden;\n background: #000;\n user-select: none;\n -webkit-user-select: none;\n touch-action: none;\n }\n :host([hidden]) {\n display: none;\n }\n .vz-container {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n canvas {\n display: block;\n width: 100%;\n height: 100%;\n }\n .vz-poster {\n position: absolute;\n inset: 0;\n background-size: cover;\n background-position: center;\n z-index: 1;\n cursor: pointer;\n }\n .vz-poster.hidden {\n display: none;\n }\n `;\n this.shadow.appendChild(style);\n }\n}\n","import {\n SphereGeometry,\n MeshBasicMaterial,\n Mesh,\n} from 'three';\nimport type { PerspectiveCamera, Texture } from 'three';\nimport type { StereoMode } from '@vizor-vr/shared';\nimport { SPHERE_DEFAULTS, CAMERA_DEFAULTS } from '@vizor-vr/shared';\nimport type { IProjection } from './projection.js';\n\n/**\n * Equirectangular projection for 360 and 180 content.\n * Creates an inverted sphere with the texture mapped on the inside.\n * For stereo content, creates two spheres on different Three.js layers\n * with UV remapping for side-by-side or top-bottom layouts.\n */\nexport class EquirectProjection implements IProjection {\n private meshes: Mesh[] = [];\n private fovH: number;\n\n constructor(fovH: number = 360) {\n this.fovH = fovH;\n }\n\n createMeshes(texture: Texture, stereoMode: StereoMode): Mesh[] {\n this.dispose();\n\n if (stereoMode === 'mono') {\n const mesh = this.createSphere(texture);\n mesh.layers.set(0);\n this.meshes = [mesh];\n return this.meshes;\n }\n\n // Stereo: create two spheres on layers 1 (left) and 2 (right)\n const meshL = this.createSphere(texture);\n const meshR = this.createSphere(texture);\n\n this.remapUVs(meshL.geometry as SphereGeometry, stereoMode, 'left');\n this.remapUVs(meshR.geometry as SphereGeometry, stereoMode, 'right');\n\n meshL.layers.set(1);\n meshR.layers.set(2);\n\n this.meshes = [meshL, meshR];\n return this.meshes;\n }\n\n configureCamera(camera: PerspectiveCamera): void {\n camera.fov = CAMERA_DEFAULTS.fov;\n camera.near = CAMERA_DEFAULTS.near;\n camera.far = CAMERA_DEFAULTS.far;\n camera.position.set(0, 0, 0);\n camera.updateProjectionMatrix();\n }\n\n update(_camera: PerspectiveCamera, _time: number): void {\n // No per-frame updates needed for equirectangular\n }\n\n dispose(): void {\n for (const mesh of this.meshes) {\n mesh.geometry.dispose();\n (mesh.material as MeshBasicMaterial).dispose();\n }\n this.meshes = [];\n }\n\n private createSphere(texture: Texture): Mesh {\n const { radius, widthSegments, heightSegments } = SPHERE_DEFAULTS;\n\n // For 180 content, only create a half-sphere\n const phiLength = (this.fovH / 360) * Math.PI * 2;\n const phiStart = this.fovH === 180 ? Math.PI / 2 : 0;\n\n const geometry = new SphereGeometry(\n radius,\n widthSegments,\n heightSegments,\n phiStart,\n phiLength,\n );\n\n // Invert the sphere so texture renders on the inside\n geometry.scale(-1, 1, 1);\n\n const material = new MeshBasicMaterial({\n map: texture,\n });\n\n return new Mesh(geometry, material);\n }\n\n /**\n * Remap UV coordinates for stereo content.\n * SBS: left eye uses left half (u: 0-0.5), right eye uses right half (u: 0.5-1)\n * TB: left eye uses top half (v: 0.5-1), right eye uses bottom half (v: 0-0.5)\n */\n private remapUVs(\n geometry: SphereGeometry,\n stereoMode: StereoMode,\n eye: 'left' | 'right',\n ): void {\n const uvAttr = geometry.attributes.uv!;\n\n for (let i = 0; i < uvAttr.count; i++) {\n let u = uvAttr.getX(i);\n let v = uvAttr.getY(i);\n\n if (stereoMode === 'sbs') {\n // Side-by-side: compress U to half, offset for right eye\n u = eye === 'left' ? u * 0.5 : u * 0.5 + 0.5;\n } else if (stereoMode === 'tb') {\n // Top-bottom: compress V to half, offset for bottom (right) eye\n v = eye === 'left' ? v * 0.5 + 0.5 : v * 0.5;\n }\n\n uvAttr.setXY(i, u, v);\n }\n\n uvAttr.needsUpdate = true;\n }\n}\n","import { PlaneGeometry, MeshBasicMaterial, Mesh, MathUtils } from 'three';\nimport type { PerspectiveCamera, Texture } from 'three';\nimport type { StereoMode } from '@vizor-vr/shared';\nimport type { IProjection } from './projection.js';\n\n/** Distance of the camera from the plane (z-axis). */\nconst CAM_Z = 3;\n\n/**\n * Flat projection for standard 2D and 3D (SBS/TB) video.\n *\n * Behaves like `object-fit: contain` — the plane preserves the video's\n * native aspect ratio and scales to fit as large as possible within the\n * viewport. Letterbox / pillarbox bars appear only when the video and\n * container aspect ratios differ.\n */\nexport class FlatProjection implements IProjection {\n private meshes: Mesh[] = [];\n /** Video aspect ratio (accounts for stereo layout). */\n private videoAspect = 16 / 9;\n private stereoMode: StereoMode = 'mono';\n\n createMeshes(texture: Texture, stereoMode: StereoMode): Mesh[] {\n this.dispose();\n this.stereoMode = stereoMode;\n\n // Determine video aspect ratio from texture (accounting for stereo layout)\n let aspect = texture.image\n ? (texture.image as HTMLVideoElement).videoWidth /\n (texture.image as HTMLVideoElement).videoHeight\n : 16 / 9;\n\n // For SBS stereo, each eye sees half the width\n if (stereoMode === 'sbs') aspect /= 2;\n // For TB stereo, each eye sees half the height\n if (stereoMode === 'tb') aspect *= 2;\n\n this.videoAspect = aspect || 16 / 9;\n\n // Start with a 1×1 unit plane — real size is set each frame in update()\n if (stereoMode === 'mono') {\n const mesh = this.createPlane(texture, 1, 1);\n mesh.layers.set(0);\n this.meshes = [mesh];\n return this.meshes;\n }\n\n const meshL = this.createPlane(texture, 1, 1);\n const meshR = this.createPlane(texture, 1, 1);\n\n this.remapUVs(meshL, stereoMode, 'left');\n this.remapUVs(meshR, stereoMode, 'right');\n\n meshL.layers.set(1);\n meshR.layers.set(2);\n\n this.meshes = [meshL, meshR];\n return this.meshes;\n }\n\n configureCamera(camera: PerspectiveCamera): void {\n camera.fov = 60;\n camera.near = 0.1;\n camera.far = 100;\n camera.position.set(0, 0, CAM_Z);\n camera.lookAt(0, 0, 0);\n camera.updateProjectionMatrix();\n }\n\n /**\n * Scale the plane every frame to fit inside the camera frustum\n * while preserving the video's native aspect ratio (`object-fit: contain`).\n *\n * Also re-reads the video dimensions so the aspect ratio updates if\n * the source changes (e.g. adaptive streaming resolution switch).\n */\n update(camera: PerspectiveCamera, _time: number): void {\n if (this.meshes.length === 0) return;\n\n // Re-read video dimensions in case the source resolution changed\n const tex = (this.meshes[0]!.material as MeshBasicMaterial).map;\n if (tex?.image) {\n const img = tex.image as HTMLVideoElement;\n if (img.videoWidth && img.videoHeight) {\n let a = img.videoWidth / img.videoHeight;\n if (this.stereoMode === 'sbs') a /= 2;\n if (this.stereoMode === 'tb') a *= 2;\n this.videoAspect = a;\n }\n }\n\n // Visible rectangle at z = 0 given current FOV and aspect\n const vFov = MathUtils.degToRad(camera.fov);\n const visH = 2 * CAM_Z * Math.tan(vFov / 2);\n const visW = visH * camera.aspect;\n\n // object-fit: contain — fit inside viewport preserving video aspect\n let planeW: number;\n let planeH: number;\n\n if (this.videoAspect > camera.aspect) {\n // Video is wider than viewport → fit to width, letterbox top/bottom\n planeW = visW;\n planeH = visW / this.videoAspect;\n } else {\n // Video is taller than viewport → fit to height, pillarbox left/right\n planeH = visH;\n planeW = visH * this.videoAspect;\n }\n\n for (const mesh of this.meshes) {\n mesh.scale.set(planeW, planeH, 1);\n }\n }\n\n dispose(): void {\n for (const mesh of this.meshes) {\n mesh.geometry.dispose();\n (mesh.material as MeshBasicMaterial).dispose();\n }\n this.meshes = [];\n }\n\n private createPlane(texture: Texture, width: number, height: number): Mesh {\n const geometry = new PlaneGeometry(width, height);\n const material = new MeshBasicMaterial({ map: texture });\n const mesh = new Mesh(geometry, material);\n mesh.position.set(0, 0, 0);\n return mesh;\n }\n\n private remapUVs(mesh: Mesh, stereoMode: StereoMode, eye: 'left' | 'right'): void {\n const geometry = mesh.geometry as PlaneGeometry;\n const uvAttr = geometry.attributes.uv!;\n\n for (let i = 0; i < uvAttr.count; i++) {\n let u = uvAttr.getX(i);\n let v = uvAttr.getY(i);\n\n if (stereoMode === 'sbs') {\n u = eye === 'left' ? u * 0.5 : u * 0.5 + 0.5;\n } else if (stereoMode === 'tb') {\n v = eye === 'left' ? v * 0.5 + 0.5 : v * 0.5;\n }\n\n uvAttr.setXY(i, u, v);\n }\n\n uvAttr.needsUpdate = true;\n }\n}\n","import {\n BoxGeometry,\n MeshBasicMaterial,\n Mesh,\n} from 'three';\nimport type { PerspectiveCamera, Texture, BufferAttribute } from 'three';\nimport type { StereoMode } from '@vizor-vr/shared';\nimport { CAMERA_DEFAULTS } from '@vizor-vr/shared';\nimport type { IProjection } from './projection.js';\n\n/**\n * Standard 3x2 cubemap atlas layout.\n *\n * Three.js BoxGeometry face order (by group index):\n * 0: +X 1: -X 2: +Y 3: -Y 4: +Z 5: -Z\n *\n * Atlas tile positions (col, row) for each face:\n * Row 0 (top): +X(0,0) -X(1,0) +Y(2,0)\n * Row 1 (bottom): -Y(0,1) +Z(1,1) -Z(2,1)\n */\nconst FACE_MAP: ReadonlyArray<{ col: number; row: number }> = [\n { col: 0, row: 0 }, // +X\n { col: 1, row: 0 }, // -X\n { col: 2, row: 0 }, // +Y\n { col: 0, row: 1 }, // -Y\n { col: 1, row: 1 }, // +Z\n { col: 2, row: 1 }, // -Z\n];\n\nconst COLS = 3;\nconst ROWS = 2;\n\n/**\n * Cubemap projection for standard 3x2 atlas cubemap content.\n * Creates an inverted box with each face's UVs remapped to the\n * corresponding tile in the atlas texture.\n * For stereo content, creates two boxes on different Three.js layers\n * with UV remapping for side-by-side or top-bottom layouts.\n */\nexport class CubemapProjection implements IProjection {\n private meshes: Mesh[] = [];\n\n createMeshes(texture: Texture, stereoMode: StereoMode): Mesh[] {\n this.dispose();\n\n if (stereoMode === 'mono') {\n const mesh = this.createBox(texture);\n this.remapFaceUVs(mesh.geometry as BoxGeometry, 'mono', 'left');\n mesh.layers.set(0);\n this.meshes = [mesh];\n return this.meshes;\n }\n\n // Stereo: create two boxes on layers 1 (left) and 2 (right)\n const meshL = this.createBox(texture);\n const meshR = this.createBox(texture);\n\n const effectiveMode = stereoMode === 'separate' ? 'tb' : stereoMode;\n\n this.remapFaceUVs(meshL.geometry as BoxGeometry, effectiveMode, 'left');\n this.remapFaceUVs(meshR.geometry as BoxGeometry, effectiveMode, 'right');\n\n meshL.layers.set(1);\n meshR.layers.set(2);\n\n this.meshes = [meshL, meshR];\n return this.meshes;\n }\n\n configureCamera(camera: PerspectiveCamera): void {\n camera.fov = CAMERA_DEFAULTS.fov;\n camera.near = CAMERA_DEFAULTS.near;\n camera.far = CAMERA_DEFAULTS.far;\n camera.position.set(0, 0, 0);\n camera.updateProjectionMatrix();\n }\n\n update(_camera: PerspectiveCamera, _time: number): void {\n // No per-frame updates needed for cubemap\n }\n\n dispose(): void {\n for (const mesh of this.meshes) {\n mesh.geometry.dispose();\n (mesh.material as MeshBasicMaterial).dispose();\n }\n this.meshes = [];\n }\n\n private createBox(texture: Texture): Mesh {\n const geometry = new BoxGeometry(1, 1, 1);\n\n // Invert the box so texture renders on the inside\n geometry.scale(-1, 1, 1);\n\n const material = new MeshBasicMaterial({\n map: texture,\n });\n\n return new Mesh(geometry, material);\n }\n\n /**\n * Remap UV coordinates for cubemap atlas layout, with optional stereo offset.\n *\n * BoxGeometry has 6 faces, each with 4 vertices (indices faceIndex*4 .. faceIndex*4+3).\n * Default UVs for each face span [0,1] in both U and V.\n * We remap each face to its tile in the 3x2 atlas, then apply stereo compression\n * if needed.\n *\n * For TB stereo the entire atlas occupies either the top or bottom half of the\n * texture, so V coordinates are compressed to half and offset accordingly.\n * For SBS stereo the atlas occupies either the left or right half, so U\n * coordinates are compressed to half and offset accordingly.\n */\n private remapFaceUVs(\n geometry: BoxGeometry,\n stereoMode: StereoMode,\n eye: 'left' | 'right',\n ): void {\n const uvAttr = geometry.attributes.uv as BufferAttribute;\n\n for (let face = 0; face < 6; face++) {\n const { col, row } = FACE_MAP[face]!;\n\n // Base tile region in the atlas (full texture, no stereo offset)\n let uMin = col / COLS;\n let uMax = (col + 1) / COLS;\n // Row 0 is the top of the image (v: 0.5 to 1.0), row 1 is bottom (v: 0 to 0.5)\n let vMin = 1 - (row + 1) / ROWS;\n let vMax = 1 - row / ROWS;\n\n // Apply stereo compression\n if (stereoMode === 'tb') {\n // Top-bottom: compress V range to half, offset for eye\n const vRange = vMax - vMin;\n const halfRange = vRange * 0.5;\n if (eye === 'left') {\n // Left eye uses top half of texture: shift V up\n vMin = vMin * 0.5 + 0.5;\n vMax = vMin + halfRange;\n } else {\n // Right eye uses bottom half of texture\n vMin = vMin * 0.5;\n vMax = vMin + halfRange;\n }\n } else if (stereoMode === 'sbs') {\n // Side-by-side: compress U range to half, offset for eye\n const uRange = uMax - uMin;\n const halfRange = uRange * 0.5;\n if (eye === 'left') {\n // Left eye uses left half of texture\n uMin = uMin * 0.5;\n uMax = uMin + halfRange;\n } else {\n // Right eye uses right half of texture: shift U right\n uMin = uMin * 0.5 + 0.5;\n uMax = uMin + halfRange;\n }\n }\n\n // Each face has 4 vertices starting at face * 4\n const baseIndex = face * 4;\n for (let v = 0; v < 4; v++) {\n const idx = baseIndex + v;\n // Map the default [0,1] UVs to the tile region\n const defaultU = uvAttr.getX(idx);\n const defaultV = uvAttr.getY(idx);\n\n const mappedU = uMin + defaultU * (uMax - uMin);\n const mappedV = vMin + defaultV * (vMax - vMin);\n\n uvAttr.setXY(idx, mappedU, mappedV);\n }\n }\n\n uvAttr.needsUpdate = true;\n }\n}\n","/** EAC vertex shader — passes the face-local UV and the face's atlas tile region. */\nexport const eacVertexShader = `\nattribute vec4 faceRegion;\nvarying vec2 vUv;\nvarying vec4 vFaceRegion;\n\nvoid main() {\n vUv = uv;\n vFaceRegion = faceRegion;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n}\n`;\n\n/**\n * EAC fragment shader. The equi-angular (tan) correction must be applied in each\n * cube face's LOCAL 0..1 space, not in atlas space — so `vUv` is the face-local\n * coordinate and `vFaceRegion` (xy = tile origin, zw = tile size) maps the\n * corrected coordinate into the face's tile in the 3x2 atlas. The face edges\n * (uv 0 and 1) map to themselves and the centre stays centred, so face\n * orientation is preserved while interior samples are redistributed correctly.\n */\nexport const eacFragmentShader = `\nuniform sampler2D map;\nvarying vec2 vUv;\nvarying vec4 vFaceRegion;\n\nconst float PI = 3.14159265359;\n\nvoid main() {\n // Equi-angular correction in face-local space: 0..1 -> -1..1 -> tan -> 0..1.\n vec2 ndc = vUv * 2.0 - 1.0;\n ndc = tan(ndc * PI / 4.0);\n vec2 faceUv = clamp(ndc * 0.5 + 0.5, 0.0, 1.0);\n\n // Map the corrected face-local UV into this face's atlas tile.\n vec2 atlasUv = vFaceRegion.xy + faceUv * vFaceRegion.zw;\n\n gl_FragColor = texture2D(map, atlasUv);\n}\n`;\n","import {\n BoxGeometry,\n ShaderMaterial,\n Mesh,\n FrontSide,\n BufferAttribute,\n} from 'three';\nimport type { PerspectiveCamera, Texture } from 'three';\nimport type { StereoMode } from '@vizor-vr/shared';\nimport { CAMERA_DEFAULTS } from '@vizor-vr/shared';\nimport type { IProjection } from './projection.js';\nimport { eacVertexShader, eacFragmentShader } from '../shaders/eac.glsl.js';\n\n/**\n * Standard 3x2 cubemap atlas layout.\n *\n * Three.js BoxGeometry face order (by group index):\n * 0: +X 1: -X 2: +Y 3: -Y 4: +Z 5: -Z\n *\n * Atlas tile positions (col, row) for each face:\n * Row 0 (top): +X(0,0) -X(1,0) +Y(2,0)\n * Row 1 (bottom): -Y(0,1) +Z(1,1) -Z(2,1)\n */\nconst FACE_MAP: ReadonlyArray<{ col: number; row: number }> = [\n { col: 0, row: 0 }, // +X\n { col: 1, row: 0 }, // -X\n { col: 2, row: 0 }, // +Y\n { col: 0, row: 1 }, // -Y\n { col: 1, row: 1 }, // +Z\n { col: 2, row: 1 }, // -Z\n];\n\nconst COLS = 3;\nconst ROWS = 2;\n\n/**\n * Equi-Angular Cubemap (EAC) projection for 3x2 atlas cubemap content.\n * Uses the same box geometry and UV atlas layout as CubemapProjection,\n * but applies a ShaderMaterial with tan() angular correction in the\n * fragment shader to correct for the equi-angular sampling distortion.\n *\n * For stereo content, creates two boxes on different Three.js layers\n * with UV remapping for side-by-side or top-bottom layouts.\n */\nexport class EacProjection implements IProjection {\n private meshes: Mesh[] = [];\n\n createMeshes(texture: Texture, stereoMode: StereoMode): Mesh[] {\n this.dispose();\n\n if (stereoMode === 'mono') {\n const mesh = this.createBox(texture);\n this.setupFaceAtlas(mesh.geometry as BoxGeometry, 'mono', 'left');\n mesh.layers.set(0);\n this.meshes = [mesh];\n return this.meshes;\n }\n\n // Stereo: create two boxes on layers 1 (left) and 2 (right)\n const meshL = this.createBox(texture);\n const meshR = this.createBox(texture);\n\n const effectiveMode = stereoMode === 'separate' ? 'tb' : stereoMode;\n\n this.setupFaceAtlas(meshL.geometry as BoxGeometry, effectiveMode, 'left');\n this.setupFaceAtlas(meshR.geometry as BoxGeometry, effectiveMode, 'right');\n\n meshL.layers.set(1);\n meshR.layers.set(2);\n\n this.meshes = [meshL, meshR];\n return this.meshes;\n }\n\n configureCamera(camera: PerspectiveCamera): void {\n camera.fov = CAMERA_DEFAULTS.fov;\n camera.near = CAMERA_DEFAULTS.near;\n camera.far = CAMERA_DEFAULTS.far;\n camera.position.set(0, 0, 0);\n camera.updateProjectionMatrix();\n }\n\n update(_camera: PerspectiveCamera, _time: number): void {\n // No per-frame updates needed for EAC\n }\n\n dispose(): void {\n for (const mesh of this.meshes) {\n mesh.geometry.dispose();\n (mesh.material as ShaderMaterial).dispose();\n }\n this.meshes = [];\n }\n\n private createBox(texture: Texture): Mesh {\n const geometry = new BoxGeometry(1, 1, 1);\n\n // Invert the box so texture renders on the inside\n geometry.scale(-1, 1, 1);\n\n const material = new ShaderMaterial({\n uniforms: { map: { value: texture } },\n vertexShader: eacVertexShader,\n fragmentShader: eacFragmentShader,\n side: FrontSide,\n });\n\n return new Mesh(geometry, material);\n }\n\n /**\n * Provide each face's atlas tile as a per-vertex `faceRegion` attribute\n * (xy = tile origin, zw = tile size), leaving geometry.uv as the default\n * face-local [0,1] coordinates. The fragment shader applies the equi-angular\n * correction in that face-local space and then maps the result into the tile\n * — applying the correction in atlas space (the previous approach) is wrong\n * because the tan() distortion is per-face.\n *\n * BoxGeometry has 6 faces, each with 4 vertices (indices face*4 .. face*4+3).\n * For TB stereo the atlas occupies the top or bottom half of the texture (V\n * compressed/offset); for SBS, the left or right half (U compressed/offset).\n */\n private setupFaceAtlas(\n geometry: BoxGeometry,\n stereoMode: StereoMode,\n eye: 'left' | 'right',\n ): void {\n const vertexCount = geometry.attributes.uv!.count;\n const regions = new Float32Array(vertexCount * 4);\n\n for (let face = 0; face < 6; face++) {\n const { col, row } = FACE_MAP[face]!;\n\n // Base tile region in the atlas (full texture, no stereo offset).\n let uMin = col / COLS;\n let uMax = (col + 1) / COLS;\n // Row 0 is the top of the image (v: 0.5 to 1.0), row 1 is bottom (v: 0 to 0.5).\n let vMin = 1 - (row + 1) / ROWS;\n let vMax = 1 - row / ROWS;\n\n if (stereoMode === 'tb') {\n const halfRange = (vMax - vMin) * 0.5;\n if (eye === 'left') {\n vMin = vMin * 0.5 + 0.5; // top half of the texture\n vMax = vMin + halfRange;\n } else {\n vMin = vMin * 0.5; // bottom half\n vMax = vMin + halfRange;\n }\n } else if (stereoMode === 'sbs') {\n const halfRange = (uMax - uMin) * 0.5;\n if (eye === 'left') {\n uMin = uMin * 0.5; // left half of the texture\n uMax = uMin + halfRange;\n } else {\n uMin = uMin * 0.5 + 0.5; // right half\n uMax = uMin + halfRange;\n }\n }\n\n const baseIndex = face * 4;\n for (let v = 0; v < 4; v++) {\n const idx = (baseIndex + v) * 4;\n regions[idx] = uMin;\n regions[idx + 1] = vMin;\n regions[idx + 2] = uMax - uMin;\n regions[idx + 3] = vMax - vMin;\n }\n }\n\n geometry.setAttribute('faceRegion', new BufferAttribute(regions, 4));\n }\n}\n","/**\n * GLSL shaders for dual fisheye projection.\n *\n * A dual fisheye texture contains two circular fisheye images side by side:\n * - Left circle: front hemisphere (z >= 0)\n * - Right circle: back hemisphere (z < 0)\n *\n * For stereo content (SBS or TB), the entire dual-fisheye pair is packed\n * into a sub-region of the texture. Uniforms uScaleX/uScaleY/uOffsetX/uOffsetY\n * select the appropriate region per eye.\n */\n\n/** Fisheye vertex shader -- passes world position to fragment shader */\nexport const fisheyeVertexShader = /* glsl */ `\nvarying vec3 vWorldPosition;\n\nvoid main() {\n vec4 worldPos = modelMatrix * vec4(position, 1.0);\n vWorldPosition = worldPos.xyz;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n}\n`;\n\n/**\n * Dual fisheye fragment shader -- maps sphere surface to circular fisheye UV.\n *\n * Stereo uniforms allow selecting a sub-region of the texture:\n * - uScaleX / uScaleY: scale factor applied to the final UV (1.0 = full, 0.5 = half)\n * - uOffsetX / uOffsetY: offset added after scaling (0.0 or 0.5 for stereo halves)\n */\nexport const fisheyeFragmentShader = /* glsl */ `\nuniform sampler2D map;\nuniform float uScaleX;\nuniform float uScaleY;\nuniform float uOffsetX;\nuniform float uOffsetY;\n\nvarying vec3 vWorldPosition;\n\nconst float PI = 3.14159265359;\n\nvoid main() {\n vec3 dir = normalize(vWorldPosition);\n\n // Determine which hemisphere: front (z >= 0) or back (z < 0)\n bool isFront = dir.z >= 0.0;\n float faceSign = isFront ? 1.0 : -1.0;\n\n // Polar angle from the hemisphere pole (0 at pole, PI/2 at equator)\n float theta = acos(clamp(abs(dir.z), -1.0, 1.0));\n\n // Azimuthal angle around the hemisphere pole\n float phi = atan(dir.y, dir.x * faceSign);\n\n // Map to circular fisheye UV using equidistant projection\n // r ranges from 0 (pole) to 1 (equator)\n float r = theta / (PI * 0.5);\n\n // Convert polar (r, phi) to cartesian circle coordinates, centered at (0.5, 0.5)\n vec2 circleUV = vec2(r * cos(phi), r * sin(phi)) * 0.5 + 0.5;\n\n // Place front hemisphere in left half of the dual-fisheye region,\n // back hemisphere in right half\n float xOffset = isFront ? 0.0 : 0.5;\n vec2 uv = vec2(circleUV.x * 0.5 + xOffset, circleUV.y);\n\n // Apply stereo region selection\n uv = vec2(uv.x * uScaleX + uOffsetX, uv.y * uScaleY + uOffsetY);\n\n gl_FragColor = texture2D(map, uv);\n}\n`;\n","import {\n SphereGeometry,\n ShaderMaterial,\n Mesh,\n FrontSide,\n} from 'three';\nimport type { PerspectiveCamera, Texture } from 'three';\nimport type { StereoMode } from '@vizor-vr/shared';\nimport { SPHERE_DEFAULTS, CAMERA_DEFAULTS } from '@vizor-vr/shared';\nimport type { IProjection } from './projection.js';\nimport { fisheyeVertexShader, fisheyeFragmentShader } from '../shaders/fisheye.glsl.js';\n\n/**\n * Stereo uniform values for each eye / stereo mode combination.\n *\n * For mono: the shader sees the full texture (scale 1, offset 0).\n * For SBS: each eye's dual-fisheye pair occupies a horizontal half.\n * For TB: each eye's dual-fisheye pair occupies a vertical half.\n */\ninterface StereoUniforms {\n uScaleX: number;\n uScaleY: number;\n uOffsetX: number;\n uOffsetY: number;\n}\n\nfunction stereoUniforms(\n stereoMode: StereoMode,\n eye: 'left' | 'right',\n): StereoUniforms {\n switch (stereoMode) {\n case 'sbs':\n return {\n uScaleX: 0.5,\n uScaleY: 1.0,\n uOffsetX: eye === 'left' ? 0.0 : 0.5,\n uOffsetY: 0.0,\n };\n case 'tb':\n return {\n uScaleX: 1.0,\n uScaleY: 0.5,\n uOffsetX: 0.0,\n // TB: left eye in top half (offset 0.5), right eye in bottom half (offset 0.0)\n uOffsetY: eye === 'left' ? 0.5 : 0.0,\n };\n case 'mono':\n default:\n return {\n uScaleX: 1.0,\n uScaleY: 1.0,\n uOffsetX: 0.0,\n uOffsetY: 0.0,\n };\n }\n}\n\n/**\n * Dual fisheye projection for 360 content captured with dual-fisheye cameras\n * (e.g. Ricoh Theta, Insta360).\n *\n * The texture contains two circular fisheye images side by side:\n * - Left circle: front hemisphere (z >= 0)\n * - Right circle: back hemisphere (z < 0)\n *\n * Uses a full inverted SphereGeometry (like equirectangular) but with a\n * ShaderMaterial that computes fisheye UV mapping from world-space direction\n * vectors rather than relying on geometry UVs.\n *\n * For stereo content, two spheres are created on separate Three.js layers\n * with shader uniforms selecting the appropriate texture sub-region per eye.\n */\nexport class FisheyeProjection implements IProjection {\n private meshes: Mesh[] = [];\n\n createMeshes(texture: Texture, stereoMode: StereoMode): Mesh[] {\n this.dispose();\n\n if (stereoMode === 'mono') {\n const mesh = this.createSphere(texture, 'mono', 'left');\n mesh.layers.set(0);\n this.meshes = [mesh];\n return this.meshes;\n }\n\n // Stereo: create two spheres on layers 1 (left eye) and 2 (right eye)\n const effectiveMode = stereoMode === 'separate' ? 'tb' : stereoMode;\n\n const meshL = this.createSphere(texture, effectiveMode, 'left');\n const meshR = this.createSphere(texture, effectiveMode, 'right');\n\n meshL.layers.set(1);\n meshR.layers.set(2);\n\n this.meshes = [meshL, meshR];\n return this.meshes;\n }\n\n configureCamera(camera: PerspectiveCamera): void {\n camera.fov = CAMERA_DEFAULTS.fov;\n camera.near = CAMERA_DEFAULTS.near;\n camera.far = CAMERA_DEFAULTS.far;\n camera.position.set(0, 0, 0);\n camera.updateProjectionMatrix();\n }\n\n update(_camera: PerspectiveCamera, _time: number): void {\n // No per-frame updates needed for dual fisheye\n }\n\n dispose(): void {\n for (const mesh of this.meshes) {\n mesh.geometry.dispose();\n (mesh.material as ShaderMaterial).dispose();\n }\n this.meshes = [];\n }\n\n private createSphere(\n texture: Texture,\n stereoMode: StereoMode,\n eye: 'left' | 'right',\n ): Mesh {\n const { radius, widthSegments, heightSegments } = SPHERE_DEFAULTS;\n\n const geometry = new SphereGeometry(radius, widthSegments, heightSegments);\n\n // Invert the sphere so texture renders on the inside.\n // With scale(-1,1,1) the winding order flips, so use FrontSide (not BackSide).\n geometry.scale(-1, 1, 1);\n\n const uniforms = stereoUniforms(stereoMode, eye);\n\n const material = new ShaderMaterial({\n uniforms: {\n map: { value: texture },\n uScaleX: { value: uniforms.uScaleX },\n uScaleY: { value: uniforms.uScaleY },\n uOffsetX: { value: uniforms.uOffsetX },\n uOffsetY: { value: uniforms.uOffsetY },\n },\n vertexShader: fisheyeVertexShader,\n fragmentShader: fisheyeFragmentShader,\n side: FrontSide,\n });\n\n return new Mesh(geometry, material);\n }\n}\n","import { PerspectiveCamera, Euler, Quaternion, Vector2 } from 'three';\nimport { clamp, degToRad, radToDeg, normalizeAngle } from '@vizor-vr/shared';\nimport { CAMERA_DEFAULTS } from '@vizor-vr/shared';\nimport type { PlayerState } from '../core/player-state.js';\n\ninterface CameraConfig {\n startLat: number;\n startLon: number;\n focalLength: number;\n minFocalLength: number;\n maxFocalLength: number;\n isPanoramic: boolean;\n fovH?: number; // Horizontal FOV in degrees (360 for equirect, 180 for half-sphere)\n}\n\n/**\n * Unified camera controller for the VR player.\n *\n * Handles:\n * - Mouse drag (desktop)\n * - Touch drag (mobile)\n * - Device orientation / gyroscope (mobile magic window)\n * - Keyboard arrow keys\n * - FOV zoom (scroll wheel / pinch)\n *\n * WebXR head tracking is handled by Three.js WebXRManager directly.\n */\nexport class CameraController {\n private camera: PerspectiveCamera;\n private state: PlayerState;\n private domElement: HTMLElement;\n\n // Current orientation in degrees\n private lat: number;\n private lon: number;\n private targetLat: number;\n private targetLon: number;\n private fov: number;\n\n // Config\n private isPanoramic: boolean;\n private minFov: number;\n private maxFov: number;\n private fovH: number; // Horizontal FOV of content (360 or 180)\n\n // Enable/disable (disabled during WebXR when headset drives camera)\n private enabled = true;\n\n // Drag state\n private isDragging = false;\n private lastPointer = new Vector2();\n\n // Device orientation\n private deviceOrientationEnabled = false;\n private deviceAlpha = 0;\n private deviceBeta = 0;\n private deviceGamma = 0;\n\n // Bound handlers for cleanup\n private boundHandlers: Array<[string, EventListener, EventTarget]> = [];\n\n constructor(camera: PerspectiveCamera, domElement: HTMLElement, state: PlayerState, config: CameraConfig) {\n this.camera = camera;\n this.domElement = domElement;\n this.state = state;\n\n this.lat = config.startLat;\n this.lon = config.startLon;\n this.targetLat = config.startLat;\n this.targetLon = config.startLon;\n this.fov = config.focalLength;\n this.isPanoramic = config.isPanoramic;\n this.minFov = config.minFocalLength;\n this.maxFov = config.maxFocalLength;\n this.fovH = config.fovH ?? 360;\n\n this.camera.fov = this.fov;\n this.camera.updateProjectionMatrix();\n\n if (this.isPanoramic) {\n this.attachListeners();\n }\n\n this.updateCamera();\n }\n\n /** Enable or disable input processing (disabled during WebXR) */\n setEnabled(enabled: boolean): void {\n this.enabled = enabled;\n if (!enabled) {\n this.isDragging = false;\n }\n }\n\n /** Switch panoramic mode at runtime (e.g. cinema overrides flat → panoramic) */\n setPanoramic(panoramic: boolean): void {\n if (this.isPanoramic === panoramic) return;\n this.isPanoramic = panoramic;\n if (panoramic) {\n this.fovH = 360; // Allow full horizontal rotation\n this.attachListeners();\n }\n }\n\n /** Call per frame in animation loop */\n update(): void {\n if (!this.isPanoramic || !this.enabled) return;\n\n // Smooth damping toward target\n const d = CAMERA_DEFAULTS.dampingFactor;\n this.lat += (this.targetLat - this.lat) * d;\n this.lon += (this.targetLon - this.lon) * d;\n\n // Clamp latitude\n this.lat = clamp(this.lat, -85, 85);\n\n // Clamp longitude for 180-degree content\n if (this.fovH < 360) {\n const halfFovH = this.fovH / 2;\n this.lon = clamp(this.lon, -halfFovH, halfFovH);\n this.targetLon = clamp(this.targetLon, -halfFovH, halfFovH);\n }\n\n // Update state\n this.state.batch({\n lat: this.lat,\n lon: normalizeAngle(this.lon),\n fov: this.fov,\n });\n\n this.updateCamera();\n }\n\n /** Get current orientation */\n getOrientation(): { lat: number; lon: number; fov: number } {\n return {\n lat: this.lat,\n lon: normalizeAngle(this.lon),\n fov: this.fov,\n };\n }\n\n /** Set orientation programmatically */\n setOrientation(lat: number, lon: number, snap = false): void {\n this.targetLat = lat;\n this.targetLon = lon;\n if (snap) {\n this.lat = lat;\n this.lon = lon;\n this.updateCamera();\n }\n }\n\n /** Enable device orientation (gyroscope) */\n enableDeviceOrientation(): void {\n if (this.deviceOrientationEnabled) return;\n this.deviceOrientationEnabled = true;\n\n const handler = ((e: DeviceOrientationEvent) => {\n this.deviceAlpha = e.alpha ?? 0;\n this.deviceBeta = e.beta ?? 0;\n this.deviceGamma = e.gamma ?? 0;\n this.applyDeviceOrientation();\n }) as EventListener;\n\n window.addEventListener('deviceorientation', handler);\n this.boundHandlers.push(['deviceorientation', handler, window]);\n }\n\n /** Dispose all event listeners */\n dispose(): void {\n for (const [event, handler, target] of this.boundHandlers) {\n target.removeEventListener(event, handler);\n }\n this.boundHandlers = [];\n }\n\n private attachListeners(): void {\n // Mouse\n this.addHandler(this.domElement, 'pointerdown', this.onPointerDown.bind(this));\n this.addHandler(this.domElement, 'pointermove', this.onPointerMove.bind(this));\n this.addHandler(this.domElement, 'pointerup', this.onPointerUp.bind(this));\n this.addHandler(this.domElement, 'pointerleave', this.onPointerUp.bind(this));\n\n // Scroll zoom\n this.addHandler(this.domElement, 'wheel', this.onWheel.bind(this));\n\n // Keyboard\n this.addHandler(this.domElement, 'keydown', this.onKeyDown.bind(this));\n\n // Make focusable for keyboard events\n if (!this.domElement.hasAttribute('tabindex')) {\n this.domElement.setAttribute('tabindex', '0');\n }\n }\n\n private addHandler(target: EventTarget, event: string, handler: EventListener): void {\n target.addEventListener(event, handler, { passive: event === 'wheel' ? false : true });\n this.boundHandlers.push([event, handler, target]);\n }\n\n private onPointerDown(e: Event): void {\n if (!this.enabled) return;\n const pe = e as PointerEvent;\n this.isDragging = true;\n this.lastPointer.set(pe.clientX, pe.clientY);\n this.domElement.setPointerCapture(pe.pointerId);\n }\n\n private onPointerMove(e: Event): void {\n if (!this.isDragging) return;\n const pe = e as PointerEvent;\n const dx = pe.clientX - this.lastPointer.x;\n const dy = pe.clientY - this.lastPointer.y;\n this.lastPointer.set(pe.clientX, pe.clientY);\n\n const sensitivity = CAMERA_DEFAULTS.dragSensitivity;\n this.targetLon -= dx * sensitivity;\n this.targetLat += dy * sensitivity;\n }\n\n private onPointerUp(e: Event): void {\n const pe = e as PointerEvent;\n this.isDragging = false;\n this.domElement.releasePointerCapture(pe.pointerId);\n }\n\n private onWheel(e: Event): void {\n if (!this.enabled) return;\n const we = e as WheelEvent;\n we.preventDefault();\n this.fov = clamp(\n this.fov + we.deltaY * CAMERA_DEFAULTS.zoomSpeed,\n this.minFov,\n this.maxFov,\n );\n this.camera.fov = this.fov;\n this.camera.updateProjectionMatrix();\n }\n\n private onKeyDown(e: Event): void {\n if (!this.enabled) return;\n const ke = e as KeyboardEvent;\n const speed = CAMERA_DEFAULTS.keyboardPanSpeed;\n\n switch (ke.code) {\n case 'ArrowLeft':\n this.targetLon += speed;\n break;\n case 'ArrowRight':\n this.targetLon -= speed;\n break;\n case 'ArrowUp':\n this.targetLat += speed;\n break;\n case 'ArrowDown':\n this.targetLat -= speed;\n break;\n default:\n return; // Don't prevent default for unhandled keys\n }\n }\n\n private applyDeviceOrientation(): void {\n // Convert device orientation to look direction\n // Beta (x): front-to-back tilt (-180 to 180)\n // Gamma (y): left-to-right tilt (-90 to 90)\n // Alpha (z): compass direction (0 to 360)\n this.targetLat = clamp(this.deviceBeta - 90, -85, 85);\n this.targetLon = -this.deviceAlpha;\n }\n\n private updateCamera(): void {\n if (!this.isPanoramic) return;\n\n const phi = degToRad(90 - this.lat);\n const theta = degToRad(this.lon);\n\n // Convert spherical to cartesian for lookAt target\n const x = Math.sin(phi) * Math.cos(theta);\n const y = Math.cos(phi);\n const z = Math.sin(phi) * Math.sin(theta);\n\n this.camera.lookAt(x * 500, y * 500, z * 500);\n }\n}\n","import {\n Scene,\n WebGLRenderer,\n PerspectiveCamera,\n VideoTexture,\n SRGBColorSpace,\n LinearFilter,\n Color,\n Mesh,\n} from 'three';\nimport type { Texture, Material } from 'three';\nimport type { VzFormat, FormatMeta } from '@vizor-vr/shared';\nimport { getFormatMeta } from '@vizor-vr/shared';\nimport type { PlayerConfig } from '@vizor-vr/shared';\nimport type { PlayerState } from '../core/player-state.js';\nimport type { StereoMode } from '@vizor-vr/shared';\nimport type { IProjection } from './projections/projection.js';\nimport { EquirectProjection } from './projections/equirect-projection.js';\nimport { FlatProjection } from './projections/flat-projection.js';\nimport { CubemapProjection } from './projections/cubemap-projection.js';\nimport { EacProjection } from './projections/eac-projection.js';\nimport { FisheyeProjection } from './projections/fisheye-projection.js';\nimport { CameraController } from './camera-controller.js';\nimport type { AudioManager } from '../audio/audio-manager.js';\n\n/**\n * Main renderer facade.\n * Manages the Three.js scene, camera, projection, and animation loop.\n */\nexport class VzRenderer {\n private scene: Scene;\n private renderer: WebGLRenderer;\n private camera: PerspectiveCamera;\n private cameraController: CameraController;\n private projection: IProjection | null = null;\n private videoTexture: VideoTexture | null = null;\n private audioManager: AudioManager | null = null;\n private state: PlayerState;\n private config: PlayerConfig;\n private canvas: HTMLCanvasElement;\n private isRunning = false;\n private containerEl: HTMLElement;\n private intersectionObserver: IntersectionObserver | null = null;\n private isVisible = true;\n private fpsHistory: number[] = [];\n private lastFrameTime = 0;\n\n constructor(shadowRoot: ShadowRoot, containerEl: HTMLElement, state: PlayerState, config: PlayerConfig) {\n this.state = state;\n this.config = config;\n this.containerEl = containerEl;\n\n // Create Three.js scene\n this.scene = new Scene();\n this.scene.background = new Color(0x000000);\n\n // Create camera\n this.camera = new PerspectiveCamera(75, 1, 0.1, 1000);\n this.camera.position.set(0, 0, 0);\n\n // Enable left eye layer for 2D mode (stereo fallback)\n this.camera.layers.enable(1);\n\n // Create renderer\n this.canvas = document.createElement('canvas');\n this.renderer = new WebGLRenderer({\n canvas: this.canvas,\n antialias: true,\n alpha: false,\n });\n this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));\n this.renderer.xr.enabled = true;\n\n // Add canvas to shadow DOM container\n containerEl.appendChild(this.canvas);\n\n // IntersectionObserver: pause render loop when off-screen\n if (typeof IntersectionObserver !== 'undefined') {\n this.intersectionObserver = new IntersectionObserver(\n (entries) => {\n this.isVisible = entries[0]?.isIntersecting ?? true;\n },\n { threshold: 0.1 },\n );\n this.intersectionObserver.observe(containerEl);\n }\n\n // Create camera controller\n const formatMeta = config.format ? getFormatMeta(config.format) : null;\n const isPanoramic = formatMeta ? !formatMeta.isFlat : true;\n\n this.cameraController = new CameraController(this.camera, containerEl, state, {\n startLat: config.startLat,\n startLon: config.startLon,\n focalLength: config.focalLength,\n minFocalLength: config.minFocalLength,\n maxFocalLength: config.maxFocalLength,\n isPanoramic,\n fovH: formatMeta?.fovH,\n });\n\n // Initial resize\n this.resize();\n }\n\n /** Set up the projection and texture for a video element */\n setupVideo(videoElement: HTMLVideoElement, format: VzFormat): void {\n const formatMeta = getFormatMeta(format);\n if (!formatMeta) {\n console.error(`[Vizor] Unknown format: ${format}`);\n return;\n }\n\n // Create video texture\n this.videoTexture = new VideoTexture(videoElement);\n this.videoTexture.colorSpace = SRGBColorSpace;\n this.videoTexture.minFilter = LinearFilter;\n this.videoTexture.magFilter = LinearFilter;\n this.videoTexture.generateMipmaps = false;\n\n // Create projection based on format\n this.setProjection(formatMeta, this.videoTexture);\n }\n\n /** Set up the projection and texture for an image */\n setupImage(texture: Texture, format: VzFormat): void {\n const formatMeta = getFormatMeta(format);\n if (!formatMeta) {\n console.error(`[Vizor] Unknown format: ${format}`);\n return;\n }\n\n this.setProjection(formatMeta, texture);\n }\n\n /**\n * Load a compressed KTX2 texture (optional, GPU-compressed).\n * Falls back gracefully if KTX2Loader is not available.\n */\n async loadCompressedTexture(url: string, format: VzFormat): Promise<void> {\n const formatMeta = getFormatMeta(format);\n if (!formatMeta) {\n console.error(`[Vizor] Unknown format: ${format}`);\n return;\n }\n\n try {\n const { KTX2Loader } = await import('three/examples/jsm/loaders/KTX2Loader.js');\n const loader = new KTX2Loader();\n loader.setTranscoderPath('https://cdn.jsdelivr.net/npm/three@0.170.0/examples/jsm/libs/basis/');\n loader.detectSupport(this.renderer);\n\n const texture = await loader.loadAsync(url);\n this.setProjection(formatMeta, texture);\n loader.dispose();\n } catch (err) {\n console.warn('[Vizor] KTX2 loading failed, falling back to standard texture:', err);\n }\n }\n\n /** Start the animation loop */\n start(): void {\n if (this.isRunning) return;\n this.isRunning = true;\n this.renderer.setAnimationLoop(this.tick.bind(this));\n }\n\n /** Stop the animation loop */\n stop(): void {\n this.isRunning = false;\n this.renderer.setAnimationLoop(null);\n }\n\n /** Handle container resize */\n resize(): void {\n const rect = this.containerEl.getBoundingClientRect();\n const width = rect.width;\n const height = rect.height;\n\n if (width === 0 || height === 0) return;\n\n this.camera.aspect = width / height;\n this.camera.updateProjectionMatrix();\n this.renderer.setSize(width, height, false);\n }\n\n /** Get the WebGL renderer (for WebXR integration) */\n getThreeRenderer(): WebGLRenderer {\n return this.renderer;\n }\n\n /** Get the camera (for WebXR integration) */\n getCamera(): PerspectiveCamera {\n return this.camera;\n }\n\n /** Get the camera controller */\n getCameraController(): CameraController {\n return this.cameraController;\n }\n\n /** Get the Three.js scene (for hotspot integration) */\n getScene(): Scene {\n return this.scene;\n }\n\n /** Set the audio manager for spatial audio orientation updates */\n setAudioManager(manager: AudioManager): void {\n this.audioManager = manager;\n }\n\n /** Clean up all GPU resources */\n dispose(): void {\n this.stop();\n this.intersectionObserver?.disconnect();\n this.intersectionObserver = null;\n this.cameraController.dispose();\n this.projection?.dispose();\n this.disposeSceneChildren();\n this.videoTexture?.dispose();\n this.renderer.dispose();\n this.renderer.forceContextLoss();\n\n // Remove canvas from DOM\n this.canvas.remove();\n }\n\n /** Set a custom projection (for cinema mode) */\n setCustomProjection(projection: IProjection, texture: Texture, stereoMode: StereoMode): void {\n this.projection?.dispose();\n this.disposeSceneChildren();\n\n this.projection = projection;\n projection.configureCamera(this.camera);\n const meshes = projection.createMeshes(texture, stereoMode);\n for (const mesh of meshes) {\n this.scene.add(mesh);\n }\n\n this.state.set('stereoMode', stereoMode);\n }\n\n /** Dispose all meshes, materials, and textures in the scene */\n private disposeSceneChildren(): void {\n this.scene.traverse((obj) => {\n if (obj instanceof Mesh) {\n obj.geometry?.dispose();\n const materials = Array.isArray(obj.material) ? obj.material : [obj.material];\n for (const mat of materials as Material[]) {\n // Dispose textures attached to a standard material's `map`.\n if ('map' in mat && (mat as Record<string, unknown>).map) {\n ((mat as Record<string, unknown>).map as Texture).dispose();\n }\n // Dispose textures stored in ShaderMaterial uniforms (EAC/fisheye\n // projections keep the texture in `uniforms`, not `.map`) — otherwise\n // those textures leak GPU memory on projection swap or teardown.\n const uniforms = (mat as unknown as Record<string, unknown>).uniforms as\n | Record<string, { value?: unknown }>\n | undefined;\n if (uniforms) {\n for (const key of Object.keys(uniforms)) {\n const value = uniforms[key]?.value as { isTexture?: boolean; dispose?: () => void } | undefined;\n if (value && value.isTexture && typeof value.dispose === 'function') {\n value.dispose();\n }\n }\n }\n mat.dispose();\n }\n }\n });\n while (this.scene.children.length > 0) {\n this.scene.remove(this.scene.children[0]!);\n }\n }\n\n private setProjection(formatMeta: FormatMeta, texture: Texture): void {\n // Clean up previous projection\n this.projection?.dispose();\n this.disposeSceneChildren();\n\n // Create new projection\n this.projection = this.createProjection(formatMeta);\n this.projection.configureCamera(this.camera);\n\n // Create meshes and add to scene\n const meshes = this.projection.createMeshes(texture, formatMeta.stereoMode);\n for (const mesh of meshes) {\n this.scene.add(mesh);\n }\n\n // Update state\n this.state.batch({\n format: formatMeta.format,\n stereoMode: formatMeta.stereoMode,\n });\n }\n\n private createProjection(formatMeta: FormatMeta): IProjection {\n switch (formatMeta.projection) {\n case 'equirect':\n case 'spherical':\n return new EquirectProjection(formatMeta.fovH);\n case 'flat':\n return new FlatProjection();\n case 'cubemap':\n return new CubemapProjection();\n case 'eac':\n return new EacProjection();\n case 'fisheye':\n return new FisheyeProjection();\n default:\n return new EquirectProjection(360);\n }\n }\n\n private tick(_timestamp: number, _xrFrame?: unknown): void {\n if (!this.isRunning) return;\n\n // Skip rendering when off-screen (IntersectionObserver)\n if (!this.isVisible) return;\n\n // Adaptive quality: track FPS and adjust pixel ratio\n this.adaptPixelRatio(_timestamp);\n\n // Update camera orientation (damped)\n this.cameraController.update();\n\n // Update projection\n this.projection?.update(this.camera, _timestamp);\n\n // Update spatial audio orientation from camera\n this.audioManager?.updateOrientation(this.camera);\n\n // Update video texture only when playing\n if (this.videoTexture) {\n const video = this.videoTexture.image as HTMLVideoElement;\n if (video && !video.paused && video.readyState >= 2) {\n this.videoTexture.needsUpdate = true;\n }\n }\n\n // Render\n this.renderer.render(this.scene, this.camera);\n }\n\n /**\n * Adaptive quality: adjust pixel ratio based on recent FPS.\n * Drop at <30fps, increase at >55fps. Checked every 60 frames.\n */\n private adaptPixelRatio(timestamp: number): void {\n if (this.lastFrameTime > 0) {\n const delta = timestamp - this.lastFrameTime;\n if (delta > 0) {\n this.fpsHistory.push(1000 / delta);\n }\n }\n this.lastFrameTime = timestamp;\n\n if (this.fpsHistory.length < 60) return;\n\n const avgFps = this.fpsHistory.reduce((a, b) => a + b, 0) / this.fpsHistory.length;\n this.fpsHistory.length = 0;\n\n const current = this.renderer.getPixelRatio();\n const maxRatio = Math.min(window.devicePixelRatio, 2);\n\n if (avgFps < 30 && current > 0.75) {\n this.renderer.setPixelRatio(Math.max(0.75, current - 0.25));\n } else if (avgFps > 55 && current < maxRatio) {\n this.renderer.setPixelRatio(Math.min(maxRatio, current + 0.25));\n }\n }\n}\n","import type { QualityLevel } from '@vizor-vr/shared';\nimport type { IStreamingAdapter } from './types.js';\n\n/**\n * Progressive MP4/WebM adapter.\n * Sets the video source directly — no adaptive bitrate.\n */\nexport class ProgressiveAdapter implements IStreamingAdapter {\n private videoElement: HTMLVideoElement | null = null;\n private _isAttached = false;\n\n get supportsABR(): boolean {\n return false;\n }\n\n get isAttached(): boolean {\n return this._isAttached;\n }\n\n attach(videoElement: HTMLVideoElement): void {\n this.videoElement = videoElement;\n this._isAttached = true;\n }\n\n load(url: string): void {\n if (!this.videoElement) return;\n this.videoElement.src = url;\n }\n\n getQualityLevels(): QualityLevel[] {\n return [];\n }\n\n setQualityLevel(_index: number): void {\n // No-op for progressive\n }\n\n destroy(): void {\n if (this.videoElement) {\n this.videoElement.removeAttribute('src');\n }\n this.videoElement = null;\n this._isAttached = false;\n }\n}\n","import type { IStreamingAdapter, AdapterType } from './types.js';\nimport { ProgressiveAdapter } from './progressive-adapter.js';\n\n/**\n * Create a streaming adapter by type.\n * HLS and DASH adapters are lazy-loaded via dynamic import\n * to keep the core bundle small.\n */\nexport async function createAdapter(type: AdapterType): Promise<IStreamingAdapter> {\n switch (type) {\n case 'hls': {\n const { HlsAdapter } = await import('./hls-adapter.js');\n return new HlsAdapter();\n }\n case 'dash': {\n const { DashAdapter } = await import('./dash-adapter.js');\n return new DashAdapter();\n }\n case 'progressive':\n default:\n return new ProgressiveAdapter();\n }\n}\n","import type { ParsedSource, QualityLevel } from '@vizor-vr/shared';\nimport type { PlayerState } from '../core/player-state.js';\nimport type { VzEventEmitter } from '../core/event-emitter.js';\nimport type { IStreamingAdapter, AdapterType } from './types.js';\nimport { createAdapter } from './adapter-factory.js';\n\n/**\n * Manages source selection and streaming adapter lifecycle.\n *\n * Responsibilities:\n * - Detect MIME type to determine adapter type (progressive/hls/dash)\n * - Instantiate the correct adapter via lazy-loaded factory\n * - Coordinate quality switching with PlayerState\n * - Handle multi-source progressive switching (preserves currentTime)\n */\nexport class SourceManager {\n private adapter: IStreamingAdapter | null = null;\n private videoElement: HTMLVideoElement | null = null;\n private state: PlayerState | null = null;\n private events: VzEventEmitter | null = null;\n private sources: ParsedSource[] = [];\n\n /**\n * Load sources into the video element using the appropriate adapter.\n */\n async loadSources(\n sources: ParsedSource[],\n videoElement: HTMLVideoElement,\n state: PlayerState,\n events: VzEventEmitter,\n ): Promise<void> {\n this.sources = sources;\n this.videoElement = videoElement;\n this.state = state;\n this.events = events;\n\n if (sources.length === 0) return;\n\n // Destroy previous adapter\n this.adapter?.destroy();\n\n // Determine the best adapter type from available sources\n const { adapterType, source } = this.selectSource(sources);\n\n // Create and attach adapter\n this.adapter = await createAdapter(adapterType);\n this.adapter.attach(videoElement);\n this.adapter.load(source.url);\n\n // Update quality levels in state once adapter is loaded\n this.pollQualityLevels();\n }\n\n /** Get available quality levels from the current adapter */\n getQualityLevels(): QualityLevel[] {\n return this.adapter?.getQualityLevels() ?? [];\n }\n\n /** Set quality level (-1 for auto) */\n setQualityLevel(index: number): void {\n if (!this.adapter) return;\n this.adapter.setQualityLevel(index);\n\n const levels = this.adapter.getQualityLevels();\n this.state?.batch({\n availableQualities: levels,\n currentQuality: index === -1 ? 'auto' : (levels[index]?.label ?? 'auto'),\n autoQuality: index === -1,\n });\n\n this.events?.emit('vz-quality-change', {\n quality: index === -1 ? 'auto' : (levels[index]?.label ?? 'unknown'),\n auto: index === -1,\n });\n }\n\n /** Whether the current adapter supports ABR */\n get supportsABR(): boolean {\n return this.adapter?.supportsABR ?? false;\n }\n\n /** Switch to a different progressive source by index (preserves currentTime) */\n switchSource(index: number): void {\n if (!this.videoElement || !this.adapter || index >= this.sources.length) return;\n\n const currentTime = this.videoElement.currentTime;\n const wasPlaying = !this.videoElement.paused;\n\n this.adapter.load(this.sources[index]!.url);\n\n // Restore playback position after the new source loads\n const onLoaded = () => {\n this.videoElement!.currentTime = currentTime;\n if (wasPlaying) this.videoElement!.play().catch(() => {});\n this.videoElement!.removeEventListener('loadeddata', onLoaded);\n };\n this.videoElement.addEventListener('loadeddata', onLoaded);\n }\n\n /** Dispose the adapter and release resources */\n dispose(): void {\n this.adapter?.destroy();\n this.adapter = null;\n this.videoElement = null;\n this.state = null;\n this.events = null;\n this.sources = [];\n }\n\n /**\n * Select the best source and adapter type.\n * Prefers adaptive (HLS/DASH) over progressive when available.\n */\n private selectSource(sources: ParsedSource[]): { adapterType: AdapterType; source: ParsedSource } {\n // Look for HLS first\n const hlsSource = sources.find((s) => this.detectAdapterType(s) === 'hls');\n if (hlsSource) return { adapterType: 'hls', source: hlsSource };\n\n // Then DASH\n const dashSource = sources.find((s) => this.detectAdapterType(s) === 'dash');\n if (dashSource) return { adapterType: 'dash', source: dashSource };\n\n // Fall back to first progressive source\n return { adapterType: 'progressive', source: sources[0]! };\n }\n\n /** Detect adapter type from source MIME type or URL extension */\n private detectAdapterType(source: ParsedSource): AdapterType {\n const type = source.type.toLowerCase();\n const url = source.url.toLowerCase();\n\n if (\n type === 'application/x-mpegurl' ||\n type === 'application/vnd.apple.mpegurl' ||\n url.endsWith('.m3u8')\n ) {\n return 'hls';\n }\n\n if (type === 'application/dash+xml' || url.endsWith('.mpd')) {\n return 'dash';\n }\n\n return 'progressive';\n }\n\n /**\n * Poll for quality levels after a short delay.\n * Adaptive adapters need time to parse the manifest before levels are available.\n */\n private pollQualityLevels(): void {\n if (!this.adapter || !this.state) return;\n\n const check = () => {\n if (!this.adapter || !this.state) return;\n const levels = this.adapter.getQualityLevels();\n if (levels.length > 0) {\n this.state.batch({\n availableQualities: levels,\n currentQuality: 'auto',\n autoQuality: true,\n });\n }\n };\n\n // Check immediately and again after manifest parsing\n check();\n setTimeout(check, 1000);\n setTimeout(check, 3000);\n }\n}\n","/** Cross-browser fullscreen API wrapper */\nexport function requestFullscreen(element: HTMLElement): Promise<void> {\n if (element.requestFullscreen) {\n return element.requestFullscreen();\n }\n // Webkit prefix (Safari)\n const el = element as HTMLElement & { webkitRequestFullscreen?: () => Promise<void> };\n if (el.webkitRequestFullscreen) {\n return el.webkitRequestFullscreen();\n }\n return Promise.reject(new Error('Fullscreen not supported'));\n}\n\nexport function exitFullscreen(): Promise<void> {\n if (document.exitFullscreen) {\n return document.exitFullscreen();\n }\n const doc = document as Document & { webkitExitFullscreen?: () => Promise<void> };\n if (doc.webkitExitFullscreen) {\n return doc.webkitExitFullscreen();\n }\n return Promise.reject(new Error('Fullscreen not supported'));\n}\n\nexport function isFullscreen(): boolean {\n return !!document.fullscreenElement;\n}\n\nexport function onFullscreenChange(callback: () => void): () => void {\n document.addEventListener('fullscreenchange', callback);\n document.addEventListener('webkitfullscreenchange', callback);\n return () => {\n document.removeEventListener('fullscreenchange', callback);\n document.removeEventListener('webkitfullscreenchange', callback);\n };\n}\n","/**\n * Controls CSS injected into the shadow DOM.\n * Vimeo-inspired design: thin progress bar, subtle gradient,\n * frosted glass big play, smooth transitions.\n *\n * CSS custom properties for theming:\n * --vz-primary Accent color (default: #f43f5e)\n * --vz-controls-bg Control bar gradient end (default: rgba(0,0,0,0.6))\n * --vz-progress-height Progress bar resting height (default: 3px)\n * --vz-progress-color Progress fill color (default: --vz-primary)\n * --vz-controls-transition Fade duration (default: 0.3s)\n * --vz-button-size Button hit-area size (default: 36px)\n */\nexport const CONTROLS_CSS = `\n /* ------------------------------------------------------------------ */\n /* Layout — wrapper + seek above button row */\n /* ------------------------------------------------------------------ */\n\n .vz-controls-wrapper {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n z-index: 10;\n user-select: none;\n -webkit-user-select: none;\n transition: opacity var(--vz-controls-transition, 0.3s) ease,\n transform 0.2s ease;\n }\n\n .vz-controls-wrapper.hidden {\n opacity: 0;\n pointer-events: none;\n transform: translateY(4px);\n }\n\n /* Progress bar sits above the button row */\n .vz-seek-row {\n position: relative;\n width: 100%;\n padding: 0 12px;\n cursor: pointer;\n z-index: 11;\n }\n\n /* Button row */\n .vz-controls {\n display: flex;\n align-items: center;\n gap: 2px;\n padding: 4px 8px 6px;\n background: linear-gradient(transparent, var(--vz-controls-bg, rgba(0, 0, 0, 0.6)));\n backdrop-filter: blur(4px);\n -webkit-backdrop-filter: blur(4px);\n color: #fff;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n font-size: 13px;\n }\n\n .vz-controls-left {\n display: flex;\n align-items: center;\n gap: 2px;\n flex-shrink: 0;\n }\n\n .vz-controls-center {\n flex: 1;\n min-width: 0;\n }\n\n .vz-controls-right {\n display: flex;\n align-items: center;\n gap: 2px;\n flex-shrink: 0;\n }\n\n /* ------------------------------------------------------------------ */\n /* Buttons */\n /* ------------------------------------------------------------------ */\n\n .vz-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--vz-button-size, 36px);\n height: var(--vz-button-size, 36px);\n padding: 0;\n border: none;\n border-radius: 50%;\n background: transparent;\n color: #fff;\n cursor: pointer;\n flex-shrink: 0;\n transition: opacity 0.15s, transform 0.15s;\n opacity: 0.9;\n }\n\n .vz-btn:hover {\n opacity: 1;\n transform: scale(1.1);\n }\n\n .vz-btn:active {\n transform: scale(0.95);\n }\n\n .vz-btn:focus-visible {\n outline: 2px solid var(--vz-primary, #f43f5e);\n outline-offset: 2px;\n }\n\n .vz-btn svg {\n width: 20px;\n height: 20px;\n fill: currentColor;\n }\n\n /* ------------------------------------------------------------------ */\n /* Seek / Progress bar */\n /* ------------------------------------------------------------------ */\n\n .vz-seek-container {\n position: relative;\n display: flex;\n align-items: center;\n width: 100%;\n height: 18px;\n cursor: pointer;\n }\n\n .vz-seek-track {\n position: relative;\n width: 100%;\n height: var(--vz-progress-height, 3px);\n background: rgba(255, 255, 255, 0.2);\n border-radius: 99px;\n overflow: visible;\n transition: height 0.15s ease;\n }\n\n .vz-seek-container:hover .vz-seek-track {\n height: 5px;\n }\n\n .vz-seek-buffered {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n background: linear-gradient(\n 90deg,\n rgba(255, 255, 255, 0.25),\n rgba(255, 255, 255, 0.35)\n );\n border-radius: 99px;\n }\n\n .vz-seek-played {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n background: var(--vz-progress-color, var(--vz-primary, #f43f5e));\n border-radius: 99px;\n box-shadow: 0 0 8px rgba(244, 63, 94, 0.4);\n box-shadow: 0 0 8px color-mix(in srgb, var(--vz-primary, #f43f5e) 40%, transparent);\n }\n\n .vz-seek-thumb {\n position: absolute;\n top: 50%;\n width: 13px;\n height: 13px;\n border-radius: 50%;\n background: #fff;\n box-shadow: 0 0 4px rgba(0, 0, 0, 0.3);\n transform: translate(-50%, -50%) scale(0);\n transition: transform 0.15s ease, box-shadow 0.15s ease;\n pointer-events: none;\n }\n\n .vz-seek-container:hover .vz-seek-thumb {\n transform: translate(-50%, -50%) scale(1);\n box-shadow: 0 0 6px rgba(244, 63, 94, 0.5);\n box-shadow: 0 0 6px color-mix(in srgb, var(--vz-primary, #f43f5e) 50%, transparent);\n }\n\n .vz-seek-input {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n margin: 0;\n padding: 0;\n opacity: 0;\n cursor: pointer;\n -webkit-appearance: none;\n appearance: none;\n }\n\n /* Hover time tooltip */\n .vz-seek-tooltip {\n position: absolute;\n bottom: 100%;\n margin-bottom: 6px;\n padding: 3px 7px;\n background: var(--vz-secondary, rgba(0, 0, 0, 0.85));\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n color: #fff;\n font-size: 12px;\n font-variant-numeric: tabular-nums;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n border-radius: 4px;\n white-space: nowrap;\n pointer-events: none;\n transform: translateX(-50%);\n opacity: 0;\n transition: opacity 0.12s;\n }\n\n .vz-seek-container:hover .vz-seek-tooltip {\n opacity: 1;\n }\n\n .vz-time {\n font-size: 12px;\n font-weight: 400;\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n padding: 0 6px;\n flex-shrink: 0;\n opacity: 0.9;\n letter-spacing: 0.02em;\n }\n\n /* ------------------------------------------------------------------ */\n /* Volume — horizontal slide-out on hover */\n /* ------------------------------------------------------------------ */\n\n .vz-volume {\n display: flex;\n align-items: center;\n gap: 0;\n overflow: hidden;\n }\n\n .vz-volume-slider-wrap {\n width: 0;\n overflow: hidden;\n transition: width 0.2s ease;\n }\n\n .vz-volume:hover .vz-volume-slider-wrap {\n width: 64px;\n }\n\n .vz-volume-slider {\n width: 60px;\n height: 4px;\n margin: 0 4px;\n -webkit-appearance: none;\n appearance: none;\n background: rgba(255, 255, 255, 0.3);\n border-radius: 99px;\n outline: none;\n cursor: pointer;\n }\n\n .vz-volume-slider::-webkit-slider-thumb {\n -webkit-appearance: none;\n width: 12px;\n height: 12px;\n border-radius: 50%;\n background: #fff;\n cursor: pointer;\n box-shadow: 0 0 3px rgba(0, 0, 0, 0.2);\n }\n\n .vz-volume-slider::-moz-range-thumb {\n width: 12px;\n height: 12px;\n border: none;\n border-radius: 50%;\n background: #fff;\n cursor: pointer;\n box-shadow: 0 0 3px rgba(0, 0, 0, 0.2);\n }\n\n /* ------------------------------------------------------------------ */\n /* Quality menu */\n /* ------------------------------------------------------------------ */\n\n .vz-quality-menu {\n position: relative;\n }\n\n .vz-quality-dropdown {\n position: absolute;\n bottom: 100%;\n right: 0;\n margin-bottom: 8px;\n background: var(--vz-secondary, rgba(20, 20, 30, 0.95));\n backdrop-filter: blur(16px) saturate(1.3);\n -webkit-backdrop-filter: blur(16px) saturate(1.3);\n border: 1px solid rgba(255, 255, 255, 0.06);\n border-radius: 8px;\n padding: 4px 0;\n min-width: 120px;\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.04);\n display: none;\n z-index: 20;\n }\n\n .vz-quality-dropdown.open {\n display: block;\n }\n\n .vz-quality-option {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 16px;\n cursor: pointer;\n font-size: 13px;\n color: rgba(255, 255, 255, 0.7);\n white-space: nowrap;\n transition: color 0.12s, background 0.12s;\n }\n\n .vz-quality-option:hover {\n background: rgba(244, 63, 94, 0.08);\n background: color-mix(in srgb, var(--vz-primary, #f43f5e) 8%, transparent);\n color: #fff;\n }\n\n .vz-quality-option.active {\n color: var(--vz-primary, #f43f5e);\n }\n\n .vz-quality-check {\n width: 16px;\n font-size: 14px;\n }\n\n /* ------------------------------------------------------------------ */\n /* Compass */\n /* ------------------------------------------------------------------ */\n\n .vz-compass {\n position: relative;\n }\n\n .vz-compass svg {\n fill: none;\n stroke: currentColor;\n transition: transform 0.1s ease-out;\n }\n\n /* ------------------------------------------------------------------ */\n /* Loading spinner — CSS ring */\n /* ------------------------------------------------------------------ */\n\n .vz-loading {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 44px;\n height: 44px;\n z-index: 5;\n pointer-events: none;\n }\n\n .vz-loading-spinner {\n width: 100%;\n height: 100%;\n border: 2.5px solid rgba(255, 255, 255, 0.15);\n border-top-color: var(--vz-primary, #f43f5e);\n border-radius: 50%;\n animation: vz-spin 0.75s linear infinite;\n }\n\n .vz-loading.hidden {\n display: none;\n }\n\n @keyframes vz-spin {\n to { transform: rotate(360deg); }\n }\n\n /* ------------------------------------------------------------------ */\n /* Big play button — frosted glass */\n /* ------------------------------------------------------------------ */\n\n .vz-big-play {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 68px;\n height: 68px;\n border: 1px solid rgba(255, 255, 255, 0.08);\n border-radius: 50%;\n background: rgba(15, 10, 20, 0.4);\n backdrop-filter: blur(12px) saturate(1.2);\n -webkit-backdrop-filter: blur(12px) saturate(1.2);\n color: #fff;\n cursor: pointer;\n z-index: 5;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background-color 0.25s ease, transform 0.25s ease, opacity 0.25s ease, box-shadow 0.25s ease;\n opacity: 1;\n box-shadow: 0 0 20px rgba(244, 63, 94, 0.15);\n box-shadow: 0 0 20px color-mix(in srgb, var(--vz-primary, #f43f5e) 15%, transparent);\n }\n\n .vz-big-play:hover {\n background: rgba(15, 10, 20, 0.55);\n transform: translate(-50%, -50%) scale(1.08);\n box-shadow: 0 0 30px rgba(244, 63, 94, 0.25);\n box-shadow: 0 0 30px color-mix(in srgb, var(--vz-primary, #f43f5e) 25%, transparent);\n }\n\n .vz-big-play.hidden {\n opacity: 0;\n pointer-events: none;\n transform: translate(-50%, -50%) scale(0.85);\n }\n\n .vz-big-play svg {\n width: 30px;\n height: 30px;\n fill: currentColor;\n margin-left: 3px; /* visual centering for play triangle */\n }\n\n /* ------------------------------------------------------------------ */\n /* Watermark */\n /* ------------------------------------------------------------------ */\n\n .vz-watermark {\n position: absolute;\n bottom: 48px;\n right: 12px;\n z-index: 15;\n pointer-events: auto;\n }\n\n /* ------------------------------------------------------------------ */\n /* Controls behavior variants */\n /* ------------------------------------------------------------------ */\n\n /* \"always\" mode — never hide */\n :host([controls-behavior=\"always\"]) .vz-controls-wrapper {\n opacity: 1 !important;\n pointer-events: auto !important;\n transform: none !important;\n }\n\n /* \"minimal\" mode — only thin progress bar always visible */\n :host([controls-behavior=\"minimal\"]) .vz-controls-wrapper.hidden .vz-controls {\n opacity: 0;\n pointer-events: none;\n }\n\n :host([controls-behavior=\"minimal\"]) .vz-controls-wrapper.hidden .vz-seek-row {\n opacity: 1;\n pointer-events: auto;\n }\n\n :host([controls-behavior=\"minimal\"]) .vz-controls-wrapper.hidden {\n opacity: 1;\n pointer-events: auto;\n transform: none;\n }\n\n /* ------------------------------------------------------------------ */\n /* Live indicator */\n /* ------------------------------------------------------------------ */\n\n .vz-live-indicator {\n display: flex;\n align-items: center;\n gap: 4px;\n padding: 0 8px;\n cursor: pointer;\n flex-shrink: 0;\n transition: opacity 0.2s;\n user-select: none;\n }\n\n .vz-live-indicator:hover {\n opacity: 1;\n }\n\n .vz-live-dot {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 12px;\n height: 12px;\n }\n\n .vz-live-dot svg {\n width: 10px;\n height: 10px;\n }\n\n .vz-live-dot.at-edge {\n color: #ef4444;\n animation: vz-live-pulse 2s ease-in-out infinite;\n }\n\n .vz-live-dot.behind {\n color: #6b7280;\n animation: none;\n }\n\n .vz-live-label {\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n color: rgba(255, 255, 255, 0.9);\n }\n\n @keyframes vz-live-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n }\n\n /* ------------------------------------------------------------------ */\n /* Caption overlay (HUD) */\n /* ------------------------------------------------------------------ */\n\n .vz-caption-overlay {\n position: absolute;\n bottom: 60px;\n left: 50%;\n transform: translateX(-50%);\n max-width: 80%;\n padding: 4px 12px;\n background: rgba(0, 0, 0, 0.75);\n border-radius: 4px;\n color: #fff;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n font-size: 16px;\n line-height: 1.4;\n text-align: center;\n z-index: 12;\n pointer-events: none;\n opacity: 0;\n transition: opacity 0.15s ease;\n white-space: pre-wrap;\n word-wrap: break-word;\n }\n\n .vz-caption-overlay.visible {\n opacity: 1;\n }\n\n /* ------------------------------------------------------------------ */\n /* CC button & dropdown */\n /* ------------------------------------------------------------------ */\n\n .vz-cc-menu {\n position: relative;\n }\n\n .vz-cc-btn.active {\n color: var(--vz-primary, #f43f5e);\n opacity: 1;\n }\n\n .vz-cc-dropdown {\n position: absolute;\n bottom: 100%;\n right: 0;\n margin-bottom: 8px;\n background: var(--vz-secondary, rgba(20, 20, 30, 0.95));\n backdrop-filter: blur(16px) saturate(1.3);\n -webkit-backdrop-filter: blur(16px) saturate(1.3);\n border: 1px solid rgba(255, 255, 255, 0.06);\n border-radius: 8px;\n padding: 4px 0;\n min-width: 120px;\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.04);\n display: none;\n z-index: 20;\n }\n\n .vz-cc-dropdown.open {\n display: block;\n }\n\n .vz-cc-option {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 16px;\n cursor: pointer;\n font-size: 13px;\n color: rgba(255, 255, 255, 0.7);\n white-space: nowrap;\n transition: color 0.12s, background 0.12s;\n }\n\n .vz-cc-option:hover {\n background: rgba(244, 63, 94, 0.08);\n background: color-mix(in srgb, var(--vz-primary, #f43f5e) 8%, transparent);\n color: #fff;\n }\n\n .vz-cc-option.active {\n color: var(--vz-primary, #f43f5e);\n }\n\n .vz-cc-check {\n width: 16px;\n font-size: 14px;\n }\n\n /* ------------------------------------------------------------------ */\n /* Chat badge */\n /* ------------------------------------------------------------------ */\n\n .vz-chat-badge {\n position: absolute;\n top: 2px;\n right: 2px;\n min-width: 16px;\n height: 16px;\n border-radius: 8px;\n background: #ef4444;\n color: #fff;\n font-size: 10px;\n font-weight: 700;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 0 4px;\n pointer-events: none;\n line-height: 1;\n }\n\n /* ------------------------------------------------------------------ */\n /* Responsive */\n /* ------------------------------------------------------------------ */\n\n @media (max-width: 400px) {\n .vz-volume-slider-wrap,\n .vz-volume-slider {\n display: none;\n }\n }\n`;\n","import type { PlayerConfig, LicenseResult } from '@vizor-vr/shared';\nimport { DEFAULT_CONFIG } from '@vizor-vr/shared';\nimport type { PlayerState } from '../core/player-state.js';\nimport type { VzEventEmitter } from '../core/event-emitter.js';\nimport { CONTROLS_CSS } from '../styles/controls.js';\n\nconst CONTROLS_HIDE_TIMEOUT = 3000;\n\n/**\n * Controls overlay container rendered in shadow DOM.\n * Vimeo-inspired layout: seek bar above button row, smooth slide-up.\n *\n * Layout:\n * .vz-controls-wrapper (outer — animated show/hide)\n * .vz-seek-row (progress bar — full width)\n * .vz-controls (button bar — gradient background)\n * .vz-controls-left (play, time)\n * .vz-controls-center (spacer)\n * .vz-controls-right (volume, quality, compass, vr, fullscreen)\n */\nexport class ControlsOverlay {\n private wrapper: HTMLDivElement;\n private seekRow: HTMLDivElement;\n private container: HTMLDivElement;\n private leftSection: HTMLDivElement;\n private centerSection: HTMLDivElement;\n private rightSection: HTMLDivElement;\n private loadingEl: HTMLDivElement;\n private hideTimer: ReturnType<typeof setTimeout> | null = null;\n private state: PlayerState;\n private cleanups: Array<() => void> = [];\n\n constructor(\n shadowRoot: ShadowRoot,\n containerEl: HTMLElement,\n state: PlayerState,\n config: PlayerConfig,\n _events: VzEventEmitter,\n ) {\n this.state = state;\n\n // Inject controls CSS\n const style = document.createElement('style');\n style.textContent = CONTROLS_CSS;\n shadowRoot.appendChild(style);\n\n // Create outer wrapper (handles show/hide animation)\n this.wrapper = document.createElement('div');\n this.wrapper.className = 'vz-controls-wrapper';\n\n // Apply theme colors\n if (config.primaryColor) {\n this.wrapper.style.setProperty('--vz-primary', config.primaryColor);\n }\n if (config.secondaryColor) {\n this.wrapper.style.setProperty('--vz-secondary', config.secondaryColor);\n }\n\n // Seek row — full-width progress bar above buttons\n this.seekRow = document.createElement('div');\n this.seekRow.className = 'vz-seek-row';\n\n // Button row\n this.container = document.createElement('div');\n this.container.className = 'vz-controls';\n\n // Create sections\n this.leftSection = document.createElement('div');\n this.leftSection.className = 'vz-controls-left';\n this.centerSection = document.createElement('div');\n this.centerSection.className = 'vz-controls-center';\n this.rightSection = document.createElement('div');\n this.rightSection.className = 'vz-controls-right';\n\n this.container.appendChild(this.leftSection);\n this.container.appendChild(this.centerSection);\n this.container.appendChild(this.rightSection);\n\n this.wrapper.appendChild(this.seekRow);\n this.wrapper.appendChild(this.container);\n containerEl.appendChild(this.wrapper);\n\n // Create loading spinner\n this.loadingEl = document.createElement('div');\n this.loadingEl.className = 'vz-loading hidden';\n this.loadingEl.innerHTML = '<div class=\"vz-loading-spinner\"></div>';\n containerEl.appendChild(this.loadingEl);\n\n // Auto-hide: show on pointer activity over the container parent\n const onActivity = () => this.show();\n containerEl.addEventListener('pointermove', onActivity);\n containerEl.addEventListener('pointerenter', onActivity);\n this.cleanups.push(\n () => containerEl.removeEventListener('pointermove', onActivity),\n () => containerEl.removeEventListener('pointerenter', onActivity),\n );\n\n // Stop pointer events from reaching the CameraController on the parent container.\n this.wrapper.addEventListener('pointerdown', (e) => e.stopPropagation());\n\n // Keep visible while interacting with controls\n this.wrapper.addEventListener('pointerenter', () => {\n if (this.hideTimer) {\n clearTimeout(this.hideTimer);\n this.hideTimer = null;\n }\n });\n this.wrapper.addEventListener('pointerleave', () => {\n this.scheduleHide();\n });\n\n // Subscribe to loading state\n const unsubLoading = state.subscribe('loading', (loading) => {\n this.loadingEl.classList.toggle('hidden', !loading);\n });\n this.cleanups.push(unsubLoading);\n\n // Subscribe to playing state — auto-hide when playing\n const unsubPlaying = state.subscribe('playing', (playing) => {\n if (playing) {\n this.scheduleHide();\n } else {\n this.show();\n if (this.hideTimer) {\n clearTimeout(this.hideTimer);\n this.hideTimer = null;\n }\n }\n });\n this.cleanups.push(unsubPlaying);\n\n // Start visible\n this.show();\n }\n\n /** Show controls and reset the auto-hide timer */\n show(): void {\n this.wrapper.classList.remove('hidden');\n this.state.set('controlsVisible', true);\n this.scheduleHide();\n }\n\n /** Hide controls */\n hide(): void {\n if (!this.state.get('playing')) return; // Don't hide when paused\n this.wrapper.classList.add('hidden');\n this.state.set('controlsVisible', false);\n }\n\n /** Add a control element to the overlay */\n addControl(el: HTMLElement, section: 'left' | 'center' | 'right' | 'seek'): void {\n switch (section) {\n case 'left':\n this.leftSection.appendChild(el);\n break;\n case 'center':\n this.centerSection.appendChild(el);\n break;\n case 'right':\n this.rightSection.appendChild(el);\n break;\n case 'seek':\n this.seekRow.appendChild(el);\n break;\n }\n }\n\n /**\n * Update theme colors after license validation.\n *\n * Color customisation is always respected — the `primary-color` attribute\n * is the element's accent color regardless of license tier. Tier gating\n * for color pickers lives in the dashboard embed builder UI, not here.\n */\n updateTheme(config: PlayerConfig, _license: LicenseResult): void {\n this.wrapper.style.setProperty(\n '--vz-primary',\n config.primaryColor ?? DEFAULT_CONFIG.primaryColor,\n );\n this.wrapper.style.setProperty(\n '--vz-secondary',\n config.secondaryColor ?? DEFAULT_CONFIG.secondaryColor,\n );\n }\n\n /** Clean up all event listeners and timers */\n dispose(): void {\n if (this.hideTimer) clearTimeout(this.hideTimer);\n for (const cleanup of this.cleanups) cleanup();\n this.cleanups = [];\n this.wrapper.remove();\n this.loadingEl.remove();\n }\n\n private scheduleHide(): void {\n if (this.hideTimer) clearTimeout(this.hideTimer);\n this.hideTimer = setTimeout(() => this.hide(), CONTROLS_HIDE_TIMEOUT);\n }\n}\n","import type { PlayerState } from '../core/player-state.js';\nimport type { VzMediaElement } from '../core/media-element.js';\nimport { ICON_PLAY, ICON_PAUSE } from './icons.js';\n\n/**\n * Play/pause toggle button.\n */\nexport class PlayButton {\n private button: HTMLButtonElement;\n private bigPlay: HTMLButtonElement;\n private unsubscribes: Array<() => void> = [];\n\n constructor(state: PlayerState, mediaElement: VzMediaElement, containerEl: HTMLElement) {\n // Small control bar button\n this.button = document.createElement('button');\n this.button.className = 'vz-btn vz-play-btn';\n this.button.setAttribute('aria-label', 'Play');\n this.button.innerHTML = ICON_PLAY;\n\n // Big center play button\n this.bigPlay = document.createElement('button');\n this.bigPlay.className = 'vz-big-play';\n this.bigPlay.setAttribute('aria-label', 'Play');\n this.bigPlay.innerHTML = ICON_PLAY;\n containerEl.appendChild(this.bigPlay);\n\n const toggle = () => {\n if (state.get('paused') || state.get('ended')) {\n mediaElement.play().catch(() => {});\n } else {\n mediaElement.pause();\n }\n };\n\n this.button.addEventListener('click', toggle);\n this.bigPlay.addEventListener('click', toggle);\n\n // Stop pointer events from reaching the CameraController\n this.bigPlay.addEventListener('pointerdown', (e) => e.stopPropagation());\n\n // Subscribe to state changes\n const unsubPlaying = state.subscribe('playing', (playing) => {\n this.button.innerHTML = playing ? ICON_PAUSE : ICON_PLAY;\n this.button.setAttribute('aria-label', playing ? 'Pause' : 'Play');\n this.bigPlay.classList.toggle('hidden', playing);\n });\n this.unsubscribes.push(unsubPlaying);\n\n const unsubEnded = state.subscribe('ended', (ended) => {\n if (ended) {\n this.button.innerHTML = ICON_PLAY;\n this.button.setAttribute('aria-label', 'Play');\n this.bigPlay.classList.remove('hidden');\n }\n });\n this.unsubscribes.push(unsubEnded);\n }\n\n getElement(): HTMLButtonElement {\n return this.button;\n }\n\n dispose(): void {\n for (const unsub of this.unsubscribes) unsub();\n this.bigPlay.remove();\n }\n}\n","import type { PlayerState } from '../core/player-state.js';\nimport type { VzMediaElement } from '../core/media-element.js';\n\n/**\n * Seek bar with played/buffered progress, time display, and hover tooltip.\n */\nexport class SeekBar {\n private container: HTMLDivElement;\n private timeDisplay: HTMLSpanElement;\n private playedBar: HTMLDivElement;\n private bufferedBar: HTMLDivElement;\n private thumb: HTMLDivElement;\n private tooltip: HTMLDivElement;\n private input: HTMLInputElement;\n private track: HTMLDivElement;\n private unsubscribes: Array<() => void> = [];\n\n constructor(state: PlayerState, mediaElement: VzMediaElement) {\n // Time display\n this.timeDisplay = document.createElement('span');\n this.timeDisplay.className = 'vz-time';\n this.timeDisplay.textContent = '0:00 / 0:00';\n\n // Seek container\n this.container = document.createElement('div');\n this.container.className = 'vz-seek-container';\n\n // Hide seek bar and time display in live mode (replaced by live indicator)\n const unsubLive = state.subscribe('isLive', (isLive) => {\n this.container.style.display = isLive ? 'none' : '';\n this.timeDisplay.style.display = isLive ? 'none' : '';\n });\n this.unsubscribes.push(unsubLive);\n\n this.track = document.createElement('div');\n this.track.className = 'vz-seek-track';\n\n this.bufferedBar = document.createElement('div');\n this.bufferedBar.className = 'vz-seek-buffered';\n\n this.playedBar = document.createElement('div');\n this.playedBar.className = 'vz-seek-played';\n\n this.thumb = document.createElement('div');\n this.thumb.className = 'vz-seek-thumb';\n\n // Hover time tooltip\n this.tooltip = document.createElement('div');\n this.tooltip.className = 'vz-seek-tooltip';\n this.tooltip.textContent = '0:00';\n\n this.input = document.createElement('input');\n this.input.className = 'vz-seek-input';\n this.input.type = 'range';\n this.input.min = '0';\n this.input.max = '1000';\n this.input.value = '0';\n this.input.setAttribute('aria-label', 'Seek');\n\n this.track.appendChild(this.bufferedBar);\n this.track.appendChild(this.playedBar);\n this.track.appendChild(this.thumb);\n this.track.appendChild(this.tooltip);\n this.container.appendChild(this.track);\n this.container.appendChild(this.input);\n\n // Input events — seeking\n this.input.addEventListener('input', () => {\n const duration = state.get('duration');\n if (duration > 0) {\n const time = (parseInt(this.input.value) / 1000) * duration;\n mediaElement.seek(time);\n }\n });\n\n // Hover tooltip — show time at cursor position\n this.container.addEventListener('pointermove', (e) => {\n const duration = state.get('duration');\n if (duration <= 0) return;\n\n const rect = this.track.getBoundingClientRect();\n const pct = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));\n const hoverTime = pct * duration;\n\n this.tooltip.textContent = this.formatTime(hoverTime);\n this.tooltip.style.left = `${pct * 100}%`;\n });\n\n // Subscribe to state\n const unsubTime = state.subscribe('currentTime', (time) => {\n const duration = state.get('duration');\n this.updateProgress(time, duration);\n });\n this.unsubscribes.push(unsubTime);\n\n const unsubDuration = state.subscribe('duration', (duration) => {\n const time = state.get('currentTime');\n this.updateProgress(time, duration);\n });\n this.unsubscribes.push(unsubDuration);\n\n const unsubBuffered = state.subscribe('buffered', (buffered) => {\n const duration = state.get('duration');\n if (duration > 0) {\n this.bufferedBar.style.width = `${(buffered / duration) * 100}%`;\n }\n });\n this.unsubscribes.push(unsubBuffered);\n }\n\n getElement(): HTMLDivElement {\n return this.container;\n }\n\n getTimeDisplay(): HTMLSpanElement {\n return this.timeDisplay;\n }\n\n dispose(): void {\n for (const unsub of this.unsubscribes) unsub();\n }\n\n private updateProgress(time: number, duration: number): void {\n if (duration > 0) {\n const pct = (time / duration) * 100;\n this.playedBar.style.width = `${pct}%`;\n this.thumb.style.left = `${pct}%`;\n this.input.value = String(Math.round((time / duration) * 1000));\n }\n this.timeDisplay.textContent = `${this.formatTime(time)} / ${this.formatTime(duration)}`;\n }\n\n private formatTime(seconds: number): string {\n if (isNaN(seconds) || !isFinite(seconds)) return '0:00';\n const s = Math.floor(seconds);\n const h = Math.floor(s / 3600);\n const m = Math.floor((s % 3600) / 60);\n const sec = s % 60;\n if (h > 0) {\n return `${h}:${String(m).padStart(2, '0')}:${String(sec).padStart(2, '0')}`;\n }\n return `${m}:${String(sec).padStart(2, '0')}`;\n }\n}\n","import type { PlayerState } from '../core/player-state.js';\nimport type { VzMediaElement } from '../core/media-element.js';\nimport { ICON_VOLUME_HIGH, ICON_VOLUME_LOW, ICON_VOLUME_MUTED } from './icons.js';\n\n/**\n * Volume slider and mute toggle button.\n * Slider slides out horizontally on hover.\n */\nexport class VolumeControl {\n private container: HTMLDivElement;\n private unsubscribes: Array<() => void> = [];\n\n constructor(state: PlayerState, mediaElement: VzMediaElement) {\n this.container = document.createElement('div');\n this.container.className = 'vz-volume';\n\n // Mute button\n const muteBtn = document.createElement('button');\n muteBtn.className = 'vz-btn vz-mute-btn';\n muteBtn.setAttribute('aria-label', 'Mute');\n muteBtn.innerHTML = ICON_VOLUME_HIGH;\n\n muteBtn.addEventListener('click', () => {\n mediaElement.muted = !mediaElement.muted;\n });\n\n // Volume slider wrapped for slide-out animation\n const sliderWrap = document.createElement('div');\n sliderWrap.className = 'vz-volume-slider-wrap';\n\n const slider = document.createElement('input');\n slider.className = 'vz-volume-slider';\n slider.type = 'range';\n slider.min = '0';\n slider.max = '100';\n slider.value = String(Math.round(state.get('volume') * 100));\n slider.setAttribute('aria-label', 'Volume');\n\n const updateSliderTrack = (pct: number) => {\n slider.style.background = `linear-gradient(to right, var(--vz-primary, #f43f5e) ${pct}%, rgba(255,255,255,0.3) ${pct}%)`;\n };\n\n // Set initial track fill\n updateSliderTrack(Math.round(state.get('volume') * 100));\n\n slider.addEventListener('input', () => {\n const vol = parseInt(slider.value) / 100;\n mediaElement.volume = vol;\n if (vol > 0 && mediaElement.muted) {\n mediaElement.muted = false;\n }\n updateSliderTrack(parseInt(slider.value));\n });\n\n sliderWrap.appendChild(slider);\n this.container.appendChild(muteBtn);\n this.container.appendChild(sliderWrap);\n\n // Subscribe to state\n const unsubVolume = state.subscribe('volume', (volume) => {\n const pct = Math.round(volume * 100);\n slider.value = String(pct);\n updateSliderTrack(pct);\n if (!state.get('muted')) {\n muteBtn.innerHTML = volume > 0.5 ? ICON_VOLUME_HIGH : (volume > 0 ? ICON_VOLUME_LOW : ICON_VOLUME_MUTED);\n }\n });\n this.unsubscribes.push(unsubVolume);\n\n const unsubMuted = state.subscribe('muted', (muted) => {\n muteBtn.innerHTML = muted ? ICON_VOLUME_MUTED : (state.get('volume') > 0.5 ? ICON_VOLUME_HIGH : ICON_VOLUME_LOW);\n muteBtn.setAttribute('aria-label', muted ? 'Unmute' : 'Mute');\n });\n this.unsubscribes.push(unsubMuted);\n }\n\n getElement(): HTMLDivElement {\n return this.container;\n }\n\n dispose(): void {\n for (const unsub of this.unsubscribes) unsub();\n }\n}\n","import type { PlayerState } from '../core/player-state.js';\nimport { ICON_FULLSCREEN, ICON_FULLSCREEN_EXIT } from './icons.js';\n\ninterface FullscreenTarget {\n toggleFullscreen(): void;\n}\n\n/**\n * Fullscreen toggle button.\n */\nexport class FullscreenButton {\n private button: HTMLButtonElement;\n private unsubscribes: Array<() => void> = [];\n\n constructor(state: PlayerState, mediaElement: FullscreenTarget) {\n this.button = document.createElement('button');\n this.button.className = 'vz-btn vz-fullscreen-btn';\n this.button.setAttribute('aria-label', 'Enter fullscreen');\n this.button.innerHTML = ICON_FULLSCREEN;\n\n this.button.addEventListener('click', () => {\n mediaElement.toggleFullscreen();\n });\n\n const unsubFs = state.subscribe('fullscreen', (fs) => {\n this.button.innerHTML = fs ? ICON_FULLSCREEN_EXIT : ICON_FULLSCREEN;\n this.button.setAttribute('aria-label', fs ? 'Exit fullscreen' : 'Enter fullscreen');\n });\n this.unsubscribes.push(unsubFs);\n }\n\n getElement(): HTMLButtonElement {\n return this.button;\n }\n\n dispose(): void {\n for (const unsub of this.unsubscribes) unsub();\n }\n}\n","import type { PlayerState } from '../core/player-state.js';\nimport { ICON_VR } from './icons.js';\n\n/**\n * Enter VR button.\n * Checks WebXR support and hides itself if unavailable.\n * WebXR session management is Phase 2 — currently a placeholder.\n */\nexport class VrButton {\n private button: HTMLButtonElement;\n private unsubscribes: Array<() => void> = [];\n\n constructor(state: PlayerState, onToggleXR?: () => void) {\n this.button = document.createElement('button');\n this.button.className = 'vz-btn vz-vr-btn';\n this.button.setAttribute('aria-label', 'Enter VR');\n this.button.innerHTML = ICON_VR;\n this.button.style.display = 'none'; // Hidden until XR is confirmed supported\n\n this.button.addEventListener('click', () => {\n onToggleXR?.();\n });\n\n // Check for WebXR support\n if (navigator.xr) {\n navigator.xr.isSessionSupported('immersive-vr').then((supported) => {\n state.set('xrSupported', supported);\n if (supported) {\n this.button.style.display = '';\n }\n }).catch(() => {\n // WebXR not available\n });\n }\n\n const unsubXr = state.subscribe('xrActive', (active) => {\n this.button.setAttribute('aria-label', active ? 'Exit VR' : 'Enter VR');\n });\n this.unsubscribes.push(unsubXr);\n }\n\n getElement(): HTMLButtonElement {\n return this.button;\n }\n\n dispose(): void {\n for (const unsub of this.unsubscribes) unsub();\n }\n}\n","import type { PlayerState } from '../core/player-state.js';\nimport type { VzRenderer } from '../renderer/renderer.js';\nimport { ICON_COMPASS } from './icons.js';\n\n/**\n * Compass indicator for 360 content.\n * Rotates to show the current viewing direction.\n * Click to reset to north (lon=0).\n */\nexport class Compass {\n private button: HTMLButtonElement;\n private unsubscribes: Array<() => void> = [];\n\n constructor(state: PlayerState, renderer: VzRenderer) {\n this.button = document.createElement('button');\n this.button.className = 'vz-btn vz-compass';\n this.button.setAttribute('aria-label', 'Reset to north');\n this.button.innerHTML = ICON_COMPASS;\n\n this.button.addEventListener('click', () => {\n const controller = renderer.getCameraController();\n controller.setOrientation(state.get('lat'), 0);\n });\n\n const unsubLon = state.subscribe('lon', (lon) => {\n const svg = this.button.querySelector('svg');\n if (svg) {\n svg.style.transform = `rotate(${-lon}deg)`;\n }\n });\n this.unsubscribes.push(unsubLon);\n }\n\n getElement(): HTMLButtonElement {\n return this.button;\n }\n\n dispose(): void {\n for (const unsub of this.unsubscribes) unsub();\n }\n}\n","import type { PlayerState } from '../core/player-state.js';\nimport type { SourceManager } from '../streaming/source-manager.js';\nimport { ICON_SETTINGS } from './icons.js';\n\n/**\n * Quality level selector dropdown.\n * Hidden when only one quality level is available (progressive MP4).\n */\nexport class QualityMenu {\n private container: HTMLDivElement;\n private dropdown: HTMLDivElement;\n private unsubscribes: Array<() => void> = [];\n private isOpen = false;\n\n constructor(state: PlayerState, sourceManager: SourceManager) {\n this.container = document.createElement('div');\n this.container.className = 'vz-quality-menu';\n\n const btn = document.createElement('button');\n btn.className = 'vz-btn vz-quality-btn';\n btn.setAttribute('aria-label', 'Quality');\n btn.innerHTML = ICON_SETTINGS;\n\n this.dropdown = document.createElement('div');\n this.dropdown.className = 'vz-quality-dropdown';\n\n this.container.appendChild(btn);\n this.container.appendChild(this.dropdown);\n\n // Toggle dropdown\n btn.addEventListener('click', (e) => {\n e.stopPropagation();\n this.isOpen = !this.isOpen;\n this.dropdown.classList.toggle('open', this.isOpen);\n });\n\n // Close on outside click\n const onDocClick = () => {\n if (this.isOpen) {\n this.isOpen = false;\n this.dropdown.classList.remove('open');\n }\n };\n document.addEventListener('click', onDocClick);\n this.unsubscribes.push(() => document.removeEventListener('click', onDocClick));\n\n // Subscribe to quality levels\n const unsubLevels = state.subscribe('availableQualities', (levels) => {\n // Hide if no quality options\n this.container.style.display = levels.length <= 1 ? 'none' : '';\n this.renderOptions(state, sourceManager);\n });\n this.unsubscribes.push(unsubLevels);\n\n // Initially hidden\n this.container.style.display = 'none';\n }\n\n getElement(): HTMLDivElement {\n return this.container;\n }\n\n dispose(): void {\n for (const unsub of this.unsubscribes) unsub();\n }\n\n private renderOptions(state: PlayerState, sourceManager: SourceManager): void {\n const levels = state.get('availableQualities');\n const currentQuality = state.get('currentQuality');\n this.dropdown.innerHTML = '';\n\n // Auto option\n const autoOption = document.createElement('div');\n autoOption.className = `vz-quality-option ${currentQuality === 'auto' ? 'active' : ''}`;\n autoOption.innerHTML = `<span class=\"vz-quality-check\">${currentQuality === 'auto' ? '✓' : ''}</span>Auto`;\n autoOption.addEventListener('click', () => {\n sourceManager.setQualityLevel(-1);\n this.isOpen = false;\n this.dropdown.classList.remove('open');\n });\n this.dropdown.appendChild(autoOption);\n\n // Quality levels\n levels.forEach((level, index) => {\n const option = document.createElement('div');\n const isActive = level.label === currentQuality;\n option.className = `vz-quality-option ${isActive ? 'active' : ''}`;\n option.innerHTML = `<span class=\"vz-quality-check\">${isActive ? '✓' : ''}</span>${level.label}`;\n option.addEventListener('click', () => {\n sourceManager.setQualityLevel(index);\n this.isOpen = false;\n this.dropdown.classList.remove('open');\n });\n this.dropdown.appendChild(option);\n });\n }\n}\n","import type { PlayerState } from '../core/player-state.js';\nimport type { VzMediaElement } from '../core/media-element.js';\nimport type { CaptionRenderer } from '../captions/caption-renderer.js';\nimport type { ChatPanel } from '../collaborative/chat-panel.js';\nimport type { VoiceButton } from '../collaborative/voice-button.js';\nimport { SEEK_STEP, VOLUME_STEP } from '@vizor-vr/shared';\n\n/**\n * Keyboard shortcut handler for the player.\n * Listens on the host element to avoid conflicts with\n * CameraController's arrow key handling in the container.\n */\nexport class KeyboardShortcuts {\n private hostElement: HTMLElement;\n private handler: (e: KeyboardEvent) => void;\n\n private captionRenderer: CaptionRenderer | null;\n private chatPanel: ChatPanel | null;\n private voiceButton: VoiceButton | null;\n\n constructor(\n hostElement: HTMLElement,\n state: PlayerState,\n mediaElement: VzMediaElement,\n captionRenderer?: CaptionRenderer | null,\n chatPanel?: ChatPanel | null,\n voiceButton?: VoiceButton | null,\n ) {\n this.captionRenderer = captionRenderer ?? null;\n this.chatPanel = chatPanel ?? null;\n this.voiceButton = voiceButton ?? null;\n this.hostElement = hostElement;\n\n // Make the element focusable\n if (!hostElement.hasAttribute('tabindex')) {\n hostElement.setAttribute('tabindex', '0');\n }\n\n this.handler = (e: KeyboardEvent) => {\n // Don't handle if a modifier key is held\n if (e.ctrlKey || e.metaKey || e.altKey) return;\n\n switch (e.code) {\n case 'Space':\n case 'KeyK':\n e.preventDefault();\n if (state.get('paused') || state.get('ended')) {\n mediaElement.play().catch(() => {});\n } else {\n mediaElement.pause();\n }\n break;\n\n case 'KeyM':\n e.preventDefault();\n mediaElement.muted = !mediaElement.muted;\n break;\n\n case 'KeyF':\n e.preventDefault();\n mediaElement.toggleFullscreen();\n break;\n\n case 'Home':\n e.preventDefault();\n mediaElement.seek(0);\n break;\n\n case 'End':\n e.preventDefault();\n mediaElement.seek(state.get('duration'));\n break;\n\n case 'ArrowLeft':\n // Only handle for seeking in flat (non-panoramic) mode\n // Panoramic mode arrow keys are handled by CameraController\n if (!this.isPanoramic(state)) {\n e.preventDefault();\n mediaElement.seek(Math.max(0, mediaElement.currentTime - SEEK_STEP));\n }\n break;\n\n case 'ArrowRight':\n if (!this.isPanoramic(state)) {\n e.preventDefault();\n mediaElement.seek(Math.min(state.get('duration'), mediaElement.currentTime + SEEK_STEP));\n }\n break;\n\n case 'ArrowUp':\n if (!this.isPanoramic(state)) {\n e.preventDefault();\n mediaElement.volume = Math.min(1, mediaElement.volume + VOLUME_STEP);\n }\n break;\n\n case 'ArrowDown':\n if (!this.isPanoramic(state)) {\n e.preventDefault();\n mediaElement.volume = Math.max(0, mediaElement.volume - VOLUME_STEP);\n }\n break;\n\n case 'Comma':\n // Previous frame (when paused)\n if (state.get('paused')) {\n e.preventDefault();\n mediaElement.seek(Math.max(0, mediaElement.currentTime - (1 / 30)));\n }\n break;\n\n case 'Period':\n // Next frame (when paused)\n if (state.get('paused')) {\n e.preventDefault();\n mediaElement.seek(mediaElement.currentTime + (1 / 30));\n }\n break;\n\n case 'KeyC':\n e.preventDefault();\n this.captionRenderer?.toggle();\n break;\n\n case 'KeyT':\n e.preventDefault();\n this.chatPanel?.toggle();\n break;\n\n case 'KeyV':\n e.preventDefault();\n this.voiceButton?.toggle();\n break;\n\n default:\n return; // Don't prevent default for unhandled keys\n }\n };\n\n hostElement.addEventListener('keydown', this.handler as EventListener);\n }\n\n dispose(): void {\n this.hostElement.removeEventListener('keydown', this.handler as EventListener);\n }\n\n private isPanoramic(state: PlayerState): boolean {\n const format = state.get('format');\n if (!format) return false;\n return format.includes('360') || format.includes('180');\n }\n}\n","import type { WebGLRenderer, Group } from 'three';\n\n/**\n * Handles VR controller input during WebXR sessions.\n *\n * Listens for `select` events on controllers (trigger press).\n * Default behavior: toggle play/pause. Tours can override via setSelectHandler.\n *\n * Three.js dispatches XR controller events (select, selectstart, selectend,\n * squeeze, etc.) on the Group returned by renderer.xr.getController().\n * These aren't in the typed Object3DEventMap, so we use EventDispatcher directly.\n */\nexport class XRControllerInput {\n private renderer: WebGLRenderer;\n private onSelect: (() => void) | null = null;\n private cleanups: Array<() => void> = [];\n private controller0: Group | null = null;\n private controller1: Group | null = null;\n\n constructor(renderer: WebGLRenderer) {\n this.renderer = renderer;\n }\n\n /** Set custom handler for controller select (trigger press) */\n setSelectHandler(handler: () => void): void {\n this.onSelect = handler;\n }\n\n /** Start listening for controller events */\n attach(): void {\n this.detach();\n\n this.controller0 = this.renderer.xr.getController(0);\n this.controller1 = this.renderer.xr.getController(1);\n\n const handler = () => {\n this.onSelect?.();\n };\n\n // Three.js XR controller events (select, squeeze, etc.) are dispatched\n // via EventDispatcher but aren't in the typed event map.\n // Use type assertion to bypass the strict event map check.\n const c0 = this.controller0 as Group & { addEventListener(type: string, listener: () => void): void; removeEventListener(type: string, listener: () => void): void };\n const c1 = this.controller1 as Group & { addEventListener(type: string, listener: () => void): void; removeEventListener(type: string, listener: () => void): void };\n\n c0.addEventListener('select', handler);\n c1.addEventListener('select', handler);\n\n this.cleanups.push(\n () => c0.removeEventListener('select', handler),\n () => c1.removeEventListener('select', handler),\n );\n }\n\n /** Stop listening for controller events */\n detach(): void {\n for (const cleanup of this.cleanups) cleanup();\n this.cleanups = [];\n this.controller0 = null;\n this.controller1 = null;\n }\n\n dispose(): void {\n this.detach();\n this.onSelect = null;\n }\n}\n","import type { WebGLRenderer } from 'three';\nimport type { PlayerState } from '../core/player-state.js';\nimport type { VzEventEmitter } from '../core/event-emitter.js';\nimport type { CameraController } from '../renderer/camera-controller.js';\nimport { XRControllerInput } from './xr-controller.js';\n\n/**\n * Manages the WebXR session lifecycle.\n *\n * When entering XR:\n * - Requests an immersive-vr session\n * - Connects it to Three.js via renderer.xr.setSession()\n * - Three.js swaps to XRSession.requestAnimationFrame and renders per-eye\n * - Our layer-based stereo (Layer 1=left, Layer 2=right) maps directly\n * - CameraController is disabled (headset drives camera)\n *\n * When exiting XR:\n * - Three.js reverts to window.requestAnimationFrame\n * - CameraController is re-enabled\n */\nexport class XRManager {\n private renderer: WebGLRenderer;\n private state: PlayerState;\n private events: VzEventEmitter;\n private cameraController: CameraController;\n private session: XRSession | null = null;\n private controllerInput: XRControllerInput;\n\n constructor(\n renderer: WebGLRenderer,\n state: PlayerState,\n events: VzEventEmitter,\n cameraController: CameraController,\n ) {\n this.renderer = renderer;\n this.state = state;\n this.events = events;\n this.cameraController = cameraController;\n this.controllerInput = new XRControllerInput(renderer);\n }\n\n /** Check if immersive-vr sessions are supported */\n static async isSupported(): Promise<boolean> {\n if (!navigator.xr) return false;\n try {\n return await navigator.xr.isSessionSupported('immersive-vr');\n } catch {\n return false;\n }\n }\n\n /** Request and start an immersive-vr session */\n async enterXR(): Promise<void> {\n if (this.session) return;\n if (!navigator.xr) throw new Error('WebXR not available');\n\n const session = await navigator.xr.requestSession('immersive-vr', {\n optionalFeatures: ['local-floor', 'hand-tracking'],\n });\n\n this.session = session;\n\n // Connect session to Three.js renderer — this makes Three.js use\n // XRSession.requestAnimationFrame and render to the XR framebuffer\n await this.renderer.xr.setSession(session);\n\n // Get reference space (prefer local-floor for standing experiences)\n let referenceSpace: XRReferenceSpace;\n try {\n referenceSpace = await session.requestReferenceSpace('local-floor');\n } catch {\n referenceSpace = await session.requestReferenceSpace('local');\n }\n this.renderer.xr.setReferenceSpace(referenceSpace);\n\n // Disable mouse/touch camera — headset drives camera\n this.cameraController.setEnabled(false);\n\n // Attach controller input (trigger = play/pause by default)\n this.controllerInput.attach();\n\n // Update state and emit event\n this.state.set('xrActive', true);\n this.events.emit('vz-xr-enter');\n\n // Listen for session end (user exits via headset button)\n session.addEventListener('end', this.onSessionEnd);\n }\n\n /** End the current XR session */\n async exitXR(): Promise<void> {\n if (!this.session) return;\n await this.session.end();\n // onSessionEnd will handle cleanup\n }\n\n /** Toggle XR on/off */\n async toggleXR(): Promise<void> {\n if (this.session) {\n await this.exitXR();\n } else {\n await this.enterXR();\n }\n }\n\n /** Whether an XR session is currently active */\n get isActive(): boolean {\n return this.session !== null;\n }\n\n /** Set handler for controller select (trigger press) */\n setSelectHandler(handler: () => void): void {\n this.controllerInput.setSelectHandler(handler);\n }\n\n /** Clean up */\n dispose(): void {\n this.controllerInput.dispose();\n if (this.session) {\n this.session.removeEventListener('end', this.onSessionEnd);\n this.session.end().catch(() => {});\n this.session = null;\n }\n }\n\n private onSessionEnd = (): void => {\n this.session?.removeEventListener('end', this.onSessionEnd);\n this.session = null;\n\n // Detach controller input\n this.controllerInput.detach();\n\n // Re-enable mouse/touch camera controller\n this.cameraController.setEnabled(true);\n\n // Update state and emit event\n this.state.set('xrActive', false);\n this.events.emit('vz-xr-exit');\n };\n}\n","import type { PlayerState } from '../core/player-state.js';\nimport { ICON_LIVE_DOT } from './icons.js';\n\n/**\n * Live indicator control.\n * Shows a red pulsing dot + \"LIVE\" text when at the live edge.\n * Grey dot when behind the live edge. Clicking seeks back to live.\n */\nexport class LiveIndicator {\n private container: HTMLDivElement;\n private dotEl: HTMLSpanElement;\n private labelEl: HTMLSpanElement;\n private unsubscribes: Array<() => void> = [];\n\n constructor(state: PlayerState, private onSeekToLive: () => void) {\n this.container = document.createElement('div');\n this.container.className = 'vz-live-indicator';\n this.container.title = 'Jump to live';\n\n this.dotEl = document.createElement('span');\n this.dotEl.className = 'vz-live-dot at-edge';\n this.dotEl.innerHTML = ICON_LIVE_DOT;\n\n this.labelEl = document.createElement('span');\n this.labelEl.className = 'vz-live-label';\n this.labelEl.textContent = 'LIVE';\n\n this.container.appendChild(this.dotEl);\n this.container.appendChild(this.labelEl);\n\n // Click → seek to live edge\n this.container.addEventListener('click', () => {\n this.onSeekToLive();\n });\n\n // Subscribe to live edge state\n const unsubEdge = state.subscribe('isAtLiveEdge', (atEdge) => {\n if (atEdge) {\n this.dotEl.classList.add('at-edge');\n this.dotEl.classList.remove('behind');\n this.container.title = 'Live';\n } else {\n this.dotEl.classList.remove('at-edge');\n this.dotEl.classList.add('behind');\n this.container.title = 'Click to jump to live';\n }\n });\n this.unsubscribes.push(unsubEdge);\n\n // Only show when live\n const unsubLive = state.subscribe('isLive', (isLive) => {\n this.container.style.display = isLive ? '' : 'none';\n });\n this.unsubscribes.push(unsubLive);\n\n // Start hidden (will be shown when isLive becomes true)\n this.container.style.display = 'none';\n }\n\n getElement(): HTMLDivElement {\n return this.container;\n }\n\n dispose(): void {\n for (const unsub of this.unsubscribes) unsub();\n this.unsubscribes = [];\n }\n}\n","/**\n * Analytics event collector.\n *\n * Captures player events (play, pause, seek, etc.), batches them,\n * and POSTs to the analytics API endpoint at regular intervals.\n *\n * Feature-gated: only active when `license.features.analytics === true`.\n */\n\nimport type { AnalyticsEventType, AnalyticsEvent } from '@vizor-vr/shared';\nimport { ANALYTICS_BATCH_INTERVAL } from '@vizor-vr/shared';\n\nexport interface AnalyticsCollectorConfig {\n contentId: string;\n sessionId: string;\n apiEndpoint: string;\n apiKey?: string;\n}\n\nexport class AnalyticsCollector {\n private queue: AnalyticsEvent[] = [];\n private flushTimer: ReturnType<typeof setInterval> | null = null;\n private config: AnalyticsCollectorConfig;\n private visibilityHandler: (() => void) | null = null;\n\n constructor(config: AnalyticsCollectorConfig) {\n this.config = config;\n\n // Start periodic flush\n this.flushTimer = setInterval(() => {\n this.flush();\n }, ANALYTICS_BATCH_INTERVAL);\n\n // Flush on page unload (store reference for cleanup)\n if (typeof window !== 'undefined') {\n this.visibilityHandler = () => {\n if (document.visibilityState === 'hidden') {\n this.flush();\n }\n };\n window.addEventListener('visibilitychange', this.visibilityHandler);\n }\n }\n\n /** Record an analytics event */\n track(type: AnalyticsEventType, data?: Record<string, unknown>): void {\n this.queue.push({\n type,\n contentId: this.config.contentId,\n sessionId: this.config.sessionId,\n timestamp: Date.now(),\n data,\n userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : undefined,\n });\n }\n\n /** Flush the event queue to the API */\n async flush(): Promise<void> {\n if (this.queue.length === 0) return;\n\n const events = [...this.queue];\n this.queue = [];\n\n try {\n const url = `${this.config.apiEndpoint}/api/v1/analytics/events`;\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n if (this.config.apiKey) {\n headers['x-api-key'] = this.config.apiKey;\n }\n\n // Use sendBeacon if available (for page unload), otherwise fetch\n if (document.visibilityState === 'hidden' && navigator.sendBeacon) {\n const blob = new Blob([JSON.stringify({ events })], {\n type: 'application/json',\n });\n navigator.sendBeacon(url, blob);\n } else {\n await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify({ events }),\n keepalive: true,\n });\n }\n } catch {\n // Re-add failed events to queue (up to 100 max to prevent memory leak)\n this.queue.unshift(...events.slice(0, Math.max(0, 100 - this.queue.length)));\n }\n }\n\n /** Stop the collector and flush remaining events */\n dispose(): void {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n if (this.visibilityHandler) {\n window.removeEventListener('visibilitychange', this.visibilityHandler);\n this.visibilityHandler = null;\n }\n this.flush();\n }\n}\n","/**\n * Gaze tracker for heatmap analytics.\n *\n * Samples camera orientation (lat, lon, fov) at ~3Hz and batches the\n * samples before POSTing to the analytics API.\n *\n * Feature-gated: only active when `license.features.analyticsHeatmaps === true`.\n */\n\nimport type { GazeSample } from '@vizor-vr/shared';\nimport { GAZE_SAMPLE_INTERVAL, GAZE_BATCH_INTERVAL } from '@vizor-vr/shared';\n\nexport interface GazeTrackerConfig {\n contentId: string;\n sessionId: string;\n apiEndpoint: string;\n apiKey?: string;\n}\n\n/** Function returning the current camera orientation */\nexport type OrientationProvider = () => { lat: number; lon: number; fov: number };\n\n/** Function returning the current video time */\nexport type ContentTimeProvider = () => number;\n\nexport class GazeTracker {\n private queue: GazeSample[] = [];\n private sampleTimer: ReturnType<typeof setInterval> | null = null;\n private flushTimer: ReturnType<typeof setInterval> | null = null;\n private config: GazeTrackerConfig;\n private getOrientation: OrientationProvider;\n private getContentTime: ContentTimeProvider;\n private active = false;\n private visibilityHandler: (() => void) | null = null;\n\n constructor(\n config: GazeTrackerConfig,\n getOrientation: OrientationProvider,\n getContentTime: ContentTimeProvider,\n ) {\n this.config = config;\n this.getOrientation = getOrientation;\n this.getContentTime = getContentTime;\n\n // Flush on page hide (store reference for cleanup)\n if (typeof window !== 'undefined') {\n this.visibilityHandler = () => {\n if (document.visibilityState === 'hidden') {\n this.flush();\n }\n };\n window.addEventListener('visibilitychange', this.visibilityHandler);\n }\n }\n\n /** Start sampling gaze data (call when video is playing) */\n start(): void {\n if (this.active) return;\n this.active = true;\n\n // Sample at ~3Hz\n this.sampleTimer = setInterval(() => {\n this.sample();\n }, GAZE_SAMPLE_INTERVAL);\n\n // Flush at batch interval\n this.flushTimer = setInterval(() => {\n this.flush();\n }, GAZE_BATCH_INTERVAL);\n }\n\n /** Stop sampling (call when video is paused) */\n stop(): void {\n if (!this.active) return;\n this.active = false;\n\n if (this.sampleTimer) {\n clearInterval(this.sampleTimer);\n this.sampleTimer = null;\n }\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n }\n\n /** Take a single gaze sample */\n private sample(): void {\n const orientation = this.getOrientation();\n const contentTime = this.getContentTime();\n\n this.queue.push({\n contentId: this.config.contentId,\n sessionId: this.config.sessionId,\n contentTime,\n lat: Math.round(orientation.lat * 100) / 100,\n lon: Math.round(orientation.lon * 100) / 100,\n fov: Math.round(orientation.fov * 10) / 10,\n });\n }\n\n /** Flush gaze samples to the API */\n async flush(): Promise<void> {\n if (this.queue.length === 0) return;\n\n const samples = [...this.queue];\n this.queue = [];\n\n try {\n const url = `${this.config.apiEndpoint}/api/v1/analytics/gaze`;\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n if (this.config.apiKey) {\n headers['x-api-key'] = this.config.apiKey;\n }\n\n if (document.visibilityState === 'hidden' && navigator.sendBeacon) {\n const blob = new Blob([JSON.stringify({ samples })], {\n type: 'application/json',\n });\n navigator.sendBeacon(url, blob);\n } else {\n await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify({ samples }),\n keepalive: true,\n });\n }\n } catch {\n // Re-add failed samples (cap at 300 to limit memory)\n this.queue.unshift(...samples.slice(0, Math.max(0, 300 - this.queue.length)));\n }\n }\n\n /** Stop tracking and flush remaining data */\n dispose(): void {\n this.stop();\n this.flush();\n if (this.visibilityHandler) {\n window.removeEventListener('visibilitychange', this.visibilityHandler);\n this.visibilityHandler = null;\n }\n }\n}\n","/**\n * Analytics initialization.\n *\n * Creates the AnalyticsCollector and GazeTracker based on license features,\n * attaches event listeners to the player, and returns a cleanup function.\n */\n\nimport type { FeatureFlags } from '@vizor-vr/shared';\nimport type { PlayerState } from '../core/player-state.js';\nimport type { VzEventEmitter } from '../core/event-emitter.js';\nimport { AnalyticsCollector } from './analytics-collector.js';\nimport { GazeTracker, type OrientationProvider, type ContentTimeProvider } from './gaze-tracker.js';\n\nexport interface AnalyticsOptions {\n contentId: string;\n apiEndpoint: string;\n apiKey?: string;\n features: FeatureFlags;\n getOrientation: OrientationProvider;\n getContentTime: ContentTimeProvider;\n}\n\nexport interface AnalyticsHandle {\n collector: AnalyticsCollector | null;\n gazeTracker: GazeTracker | null;\n dispose: () => void;\n}\n\n/**\n * Initialize analytics subsystem.\n *\n * Attaches event listeners to state changes and player events.\n * Returns a handle with collector, gazeTracker, and dispose function.\n */\nexport function initAnalytics(\n state: PlayerState,\n events: VzEventEmitter,\n options: AnalyticsOptions,\n): AnalyticsHandle {\n const sessionId = crypto.randomUUID();\n const cleanups: Array<() => void> = [];\n\n let collector: AnalyticsCollector | null = null;\n let gazeTracker: GazeTracker | null = null;\n\n // --- Analytics event collection ---\n if (options.features.analytics) {\n collector = new AnalyticsCollector({\n contentId: options.contentId,\n sessionId,\n apiEndpoint: options.apiEndpoint,\n apiKey: options.apiKey,\n });\n\n // Track play/pause/ended from state changes\n cleanups.push(\n state.subscribe('playing', (playing) => {\n collector!.track(playing ? 'play' : 'pause');\n }),\n );\n\n cleanups.push(\n state.subscribe('ended', (ended) => {\n if (ended) collector!.track('ended');\n }),\n );\n\n cleanups.push(\n state.subscribe('seeking', (seeking) => {\n if (seeking) collector!.track('seek', { time: state.get('currentTime') });\n }),\n );\n\n cleanups.push(\n state.subscribe('loading', (loading) => {\n if (loading && state.get('playing')) {\n collector!.track('buffering');\n }\n }),\n );\n\n cleanups.push(\n state.subscribe('fullscreen', (fs) => {\n collector!.track('fullscreen', { active: fs });\n }),\n );\n\n // Track custom events\n const trackVzEvent = (type: string, detail?: unknown) => {\n switch (type) {\n case 'vz-quality-change':\n collector!.track('quality_change', detail as Record<string, unknown>);\n break;\n case 'vz-xr-enter':\n collector!.track('vr_enter');\n break;\n case 'vz-xr-exit':\n collector!.track('vr_exit');\n break;\n case 'vz-tour-navigate':\n collector!.track('tour_navigate', detail as Record<string, unknown>);\n break;\n case 'vz-error':\n collector!.track('error', detail as Record<string, unknown>);\n break;\n }\n };\n\n // Listen for custom events via the event emitter\n const eventTypes = [\n 'vz-quality-change',\n 'vz-xr-enter',\n 'vz-xr-exit',\n 'vz-tour-navigate',\n 'vz-error',\n ];\n\n for (const eventType of eventTypes) {\n const cleanup = events.on(eventType, (e) => {\n trackVzEvent(eventType, (e as CustomEvent).detail);\n });\n cleanups.push(cleanup);\n }\n }\n\n // --- Gaze tracking ---\n if (options.features.analyticsHeatmaps) {\n gazeTracker = new GazeTracker(\n {\n contentId: options.contentId,\n sessionId,\n apiEndpoint: options.apiEndpoint,\n apiKey: options.apiKey,\n },\n options.getOrientation,\n options.getContentTime,\n );\n\n // Start/stop gaze tracking based on play state\n cleanups.push(\n state.subscribe('playing', (playing) => {\n if (playing) {\n gazeTracker!.start();\n } else {\n gazeTracker!.stop();\n }\n }),\n );\n }\n\n return {\n collector,\n gazeTracker,\n dispose: () => {\n for (const cleanup of cleanups) cleanup();\n collector?.dispose();\n gazeTracker?.dispose();\n },\n };\n}\n","import type { PlayerState } from '../core/player-state.js';\nimport type { CaptionRenderer } from '../captions/caption-renderer.js';\nimport { ICON_CC } from './icons.js';\n\n/**\n * Closed-caption toggle button with language dropdown.\n *\n * UI pattern matches QualityMenu: a button that toggles a\n * dropdown of options (Off + one per loaded track).\n */\nexport class CcButton {\n private container: HTMLDivElement;\n private dropdown: HTMLDivElement;\n private btn: HTMLButtonElement;\n private captionRenderer: CaptionRenderer;\n private unsubscribes: Array<() => void> = [];\n private isOpen = false;\n\n constructor(state: PlayerState, captionRenderer: CaptionRenderer) {\n this.captionRenderer = captionRenderer;\n\n this.container = document.createElement('div');\n this.container.className = 'vz-cc-menu';\n\n this.btn = document.createElement('button');\n this.btn.className = 'vz-btn vz-cc-btn';\n this.btn.setAttribute('aria-label', 'Captions');\n this.btn.innerHTML = ICON_CC;\n\n this.dropdown = document.createElement('div');\n this.dropdown.className = 'vz-cc-dropdown';\n\n this.container.appendChild(this.btn);\n this.container.appendChild(this.dropdown);\n\n // Toggle dropdown on click\n this.btn.addEventListener('click', (e) => {\n e.stopPropagation();\n this.isOpen = !this.isOpen;\n this.dropdown.classList.toggle('open', this.isOpen);\n if (this.isOpen) this.renderOptions();\n });\n\n // Close on outside click\n const onDocClick = () => {\n if (this.isOpen) {\n this.isOpen = false;\n this.dropdown.classList.remove('open');\n }\n };\n document.addEventListener('click', onDocClick);\n this.unsubscribes.push(() => document.removeEventListener('click', onDocClick));\n\n // Update active indicator when time changes (cue might switch track appearance)\n this.updateButtonState();\n }\n\n getElement(): HTMLDivElement {\n return this.container;\n }\n\n /** Show or hide the CC button based on whether tracks are loaded */\n updateVisibility(): void {\n this.container.style.display = this.captionRenderer.hasTracks() ? '' : 'none';\n this.updateButtonState();\n }\n\n dispose(): void {\n for (const unsub of this.unsubscribes) unsub();\n this.unsubscribes = [];\n }\n\n private updateButtonState(): void {\n if (this.captionRenderer.isActive()) {\n this.btn.classList.add('active');\n } else {\n this.btn.classList.remove('active');\n }\n }\n\n private renderOptions(): void {\n this.dropdown.innerHTML = '';\n const tracks = this.captionRenderer.getTracks();\n const activeIndex = this.captionRenderer.getActiveTrackIndex();\n\n // \"Off\" option\n const offOption = document.createElement('div');\n offOption.className = `vz-cc-option ${activeIndex === -1 ? 'active' : ''}`;\n offOption.innerHTML = `<span class=\"vz-cc-check\">${activeIndex === -1 ? '✓' : ''}</span>Off`;\n offOption.addEventListener('click', () => {\n this.captionRenderer.setActiveTrack(-1);\n this.isOpen = false;\n this.dropdown.classList.remove('open');\n this.updateButtonState();\n });\n this.dropdown.appendChild(offOption);\n\n // Track options\n tracks.forEach((track, index) => {\n const option = document.createElement('div');\n const isActive = index === activeIndex;\n option.className = `vz-cc-option ${isActive ? 'active' : ''}`;\n // Build with DOM APIs, not innerHTML: track.label is untrusted (it comes\n // from a <track label> attribute / the captions API, which only\n // length-validates it) and must render as inert text, never parsed HTML.\n const check = document.createElement('span');\n check.className = 'vz-cc-check';\n check.textContent = isActive ? '✓' : '';\n option.appendChild(check);\n option.appendChild(document.createTextNode(track.label));\n option.addEventListener('click', () => {\n this.captionRenderer.setActiveTrack(index);\n this.isOpen = false;\n this.dropdown.classList.remove('open');\n this.updateButtonState();\n });\n this.dropdown.appendChild(option);\n });\n }\n}\n","import type { VzFormat, ParsedSource } from '@vizor-vr/shared';\nimport { parseFormat, MEDIA_EVENTS } from '@vizor-vr/shared';\nimport { VzBaseElement } from './base-element.js';\nimport { VzRenderer } from '../renderer/renderer.js';\nimport { SourceManager } from '../streaming/source-manager.js';\nimport { requestFullscreen, exitFullscreen, isFullscreen, onFullscreenChange } from '../utils/fullscreen.js';\nimport { ControlsOverlay } from '../controls/controls-overlay.js';\nimport { PlayButton } from '../controls/play-button.js';\nimport { SeekBar } from '../controls/seek-bar.js';\nimport { VolumeControl } from '../controls/volume-control.js';\nimport { FullscreenButton } from '../controls/fullscreen-button.js';\nimport { VrButton } from '../controls/vr-button.js';\nimport { Compass } from '../controls/compass.js';\nimport { QualityMenu } from '../controls/quality-menu.js';\nimport { KeyboardShortcuts } from '../controls/keyboard-shortcuts.js';\nimport { XRManager } from '../xr/xr-manager.js';\nimport { AudioManager } from '../audio/audio-manager.js';\nimport { LiveIndicator } from '../controls/live-indicator.js';\nimport { initAnalytics, type AnalyticsHandle } from '../analytics/index.js';\nimport { AnnotationManager } from '../annotations/annotation-manager.js';\nimport type { VzAnnotationElement } from '../elements/vz-annotation.js';\nimport type { AnnotationData } from '@vizor-vr/shared';\nimport { CaptionRenderer } from '../captions/caption-renderer.js';\nimport { CcButton } from '../controls/cc-button.js';\nimport { CollaborativeClient } from '../collaborative/collab-client.js';\nimport { GazeCursorManager } from '../collaborative/gaze-cursor-manager.js';\nimport { GazeSender } from '../collaborative/gaze-sender.js';\nimport { ChatPanel } from '../collaborative/chat-panel.js';\nimport { ChatButton } from '../collaborative/chat-button.js';\nimport { VoiceManager } from '../collaborative/voice-manager.js';\nimport { VoiceButton } from '../collaborative/voice-button.js';\n\n/**\n * Abstract base class for all media-playing elements (video, live-video, cinema).\n *\n * Extends VzBaseElement with:\n * - Three.js renderer initialization\n * - Hidden <video> element creation and management\n * - Source parsing from <source> children\n * - HTML5 Media API mirroring\n * - Controls overlay rendering\n */\nexport abstract class VzMediaElement extends VzBaseElement {\n protected renderer!: VzRenderer;\n protected videoElement!: HTMLVideoElement;\n protected containerEl!: HTMLDivElement;\n protected sourceManager!: SourceManager;\n /** When true, base class skips auto-setupVideo in loadeddata (used by cinema) */\n protected suppressAutoSetup = false;\n private cleanupFullscreen: (() => void) | null = null;\n private timeUpdateTimer: ReturnType<typeof setInterval> | null = null;\n private controlsOverlay: ControlsOverlay | null = null;\n private playButton: PlayButton | null = null;\n private seekBar: SeekBar | null = null;\n private volumeControl: VolumeControl | null = null;\n private fullscreenButton: FullscreenButton | null = null;\n private vrButton: VrButton | null = null;\n private compass: Compass | null = null;\n private qualityMenu: QualityMenu | null = null;\n private keyboardShortcuts: KeyboardShortcuts | null = null;\n private xrManager: XRManager | null = null;\n private audioManager: AudioManager | null = null;\n private liveIndicator: LiveIndicator | null = null;\n private analyticsHandle: AnalyticsHandle | null = null;\n private annotationManager: AnnotationManager | null = null;\n private captionRenderer: CaptionRenderer | null = null;\n private ccButton: CcButton | null = null;\n private collabClient: CollaborativeClient | null = null;\n private gazeCursorManager: GazeCursorManager | null = null;\n private gazeSender: GazeSender | null = null;\n private chatPanel: ChatPanel | null = null;\n private chatButton: ChatButton | null = null;\n private voiceManager: VoiceManager | null = null;\n private voiceButton: VoiceButton | null = null;\n\n protected onConnected(): void {\n // Create container div\n this.containerEl = document.createElement('div');\n this.containerEl.className = 'vz-container';\n this.shadow.appendChild(this.containerEl);\n\n // Create poster overlay\n if (this.config.poster) {\n const posterEl = document.createElement('div');\n posterEl.className = 'vz-poster';\n posterEl.style.backgroundImage = `url(${this.config.poster})`;\n posterEl.addEventListener('click', () => {\n this.play().catch(() => {});\n posterEl.classList.add('hidden');\n });\n this.shadow.appendChild(posterEl);\n }\n\n // Create hidden video element\n this.videoElement = this.createVideoElement();\n\n // Initialize Three.js renderer\n this.renderer = new VzRenderer(\n this.shadow,\n this.containerEl,\n this.state,\n this.config,\n );\n\n // Mirror HTML5 media events\n this.mirrorMediaEvents();\n\n // Set up video texture once the video has data\n this.videoElement.addEventListener('loadeddata', () => {\n if (this.config.format && !this.suppressAutoSetup) {\n this.renderer.setupVideo(this.videoElement, this.config.format);\n }\n });\n\n // Initialize source manager\n this.sourceManager = new SourceManager();\n\n // Parse and load sources (must happen after event listeners are attached)\n this.loadSources();\n\n // Initialize spatial audio if configured\n if (this.config.audioFormat === 'ambix') {\n this.audioManager = new AudioManager(this.state, this.events);\n this.audioManager.init(this.videoElement, 'ambix').catch((err) => {\n console.warn('[Vizor] Audio init error:', err);\n });\n this.renderer.setAudioManager(this.audioManager);\n }\n\n // Listen for fullscreen changes\n this.cleanupFullscreen = onFullscreenChange(() => {\n const fs = isFullscreen();\n this.state.set('fullscreen', fs);\n if (fs) {\n this.events.emit('vz-fullscreen-enter');\n } else {\n this.events.emit('vz-fullscreen-exit');\n }\n this.renderer.resize();\n });\n\n // Initialize WebXR manager\n this.xrManager = new XRManager(\n this.renderer.getThreeRenderer(),\n this.state,\n this.events,\n this.renderer.getCameraController(),\n );\n\n // Default XR controller action: toggle play/pause\n this.xrManager.setSelectHandler(() => {\n if (this.state.get('paused') || this.state.get('ended')) {\n this.play().catch(() => {});\n } else {\n this.pause();\n }\n });\n\n // Initialize controls overlay\n if (!this.config.hideControls) {\n this.controlsOverlay = new ControlsOverlay(\n this.shadow, this.containerEl, this.state, this.config, this.events,\n );\n\n this.playButton = new PlayButton(this.state, this, this.containerEl);\n this.controlsOverlay.addControl(this.playButton.getElement(), 'left');\n\n this.seekBar = new SeekBar(this.state, this);\n this.controlsOverlay.addControl(this.seekBar.getElement(), 'seek');\n this.controlsOverlay.addControl(this.seekBar.getTimeDisplay(), 'left');\n\n // Live indicator — shown only when isLive is true (replaces time display)\n this.liveIndicator = new LiveIndicator(this.state, () => {\n // Seek to live edge — subclass (VzLiveElement) provides seekToLiveEdge\n const seekable = this.videoElement.seekable;\n if (seekable.length > 0) {\n this.videoElement.currentTime = seekable.end(seekable.length - 1);\n this.state.set('isAtLiveEdge', true);\n }\n });\n this.controlsOverlay.addControl(this.liveIndicator.getElement(), 'left');\n\n this.volumeControl = new VolumeControl(this.state, this);\n this.controlsOverlay.addControl(this.volumeControl.getElement(), 'right');\n\n this.qualityMenu = new QualityMenu(this.state, this.sourceManager);\n this.controlsOverlay.addControl(this.qualityMenu.getElement(), 'right');\n\n this.compass = new Compass(this.state, this.renderer);\n this.controlsOverlay.addControl(this.compass.getElement(), 'right');\n\n this.vrButton = new VrButton(this.state, () => {\n this.xrManager?.toggleXR().catch((err) => {\n console.error('[Vizor] XR toggle failed:', err);\n this.events.emit('vz-error', { code: 'XR_ERROR', message: String(err) });\n });\n });\n this.controlsOverlay.addControl(this.vrButton.getElement(), 'right');\n\n this.fullscreenButton = new FullscreenButton(this.state, this);\n this.controlsOverlay.addControl(this.fullscreenButton.getElement(), 'right');\n }\n\n // Initialize analytics after license check (feature-gated)\n if (this.config.contentId) {\n this.events.on('vz-license', () => {\n if (this.license && (this.license.features.analytics || this.license.features.analyticsHeatmaps)) {\n this.analyticsHandle = initAnalytics(this.state, this.events, {\n contentId: this.config.contentId!,\n apiEndpoint: this.config.apiEndpoint ?? window.location.origin,\n apiKey: this.config.apiKey,\n features: this.license.features,\n getOrientation: () => this.renderer.getCameraController().getOrientation(),\n getContentTime: () => this.videoElement.currentTime,\n });\n }\n });\n }\n\n // Initialize annotation system if <vz-annotation> children are present\n this.initAnnotations();\n\n // Initialize captions if <track> children are present\n this.initCaptions();\n\n // Initialize collaborative viewing if configured\n this.initCollab();\n\n // Keyboard shortcuts always active (even without visible controls)\n this.keyboardShortcuts = new KeyboardShortcuts(this, this.state, this, this.captionRenderer, this.chatPanel, this.voiceButton);\n\n // Start rendering\n this.renderer.start();\n\n // Emit ready\n this.events.emit('vz-ready');\n\n // Subclass hook\n this.onMediaConnected();\n }\n\n protected onDisconnected(): void {\n this.cleanupFullscreen?.();\n if (this.timeUpdateTimer) {\n clearInterval(this.timeUpdateTimer);\n }\n this.keyboardShortcuts?.dispose();\n this.playButton?.dispose();\n this.seekBar?.dispose();\n this.volumeControl?.dispose();\n this.fullscreenButton?.dispose();\n this.vrButton?.dispose();\n this.liveIndicator?.dispose();\n this.compass?.dispose();\n this.qualityMenu?.dispose();\n this.controlsOverlay?.dispose();\n this.xrManager?.dispose();\n this.audioManager?.dispose();\n this.audioManager = null;\n this.analyticsHandle?.dispose();\n this.analyticsHandle = null;\n this.annotationManager?.dispose();\n this.annotationManager = null;\n this.ccButton?.dispose();\n this.ccButton = null;\n this.captionRenderer?.dispose();\n this.captionRenderer = null;\n this.gazeSender?.dispose();\n this.gazeSender = null;\n this.gazeCursorManager?.dispose();\n this.gazeCursorManager = null;\n this.voiceButton?.dispose();\n this.voiceButton = null;\n this.voiceManager?.dispose();\n this.voiceManager = null;\n this.chatButton?.dispose();\n this.chatButton = null;\n this.chatPanel?.dispose();\n this.chatPanel = null;\n this.collabClient?.dispose();\n this.collabClient = null;\n this.sourceManager.dispose();\n this.videoElement.pause();\n this.videoElement.removeAttribute('src');\n this.videoElement.load();\n this.renderer.dispose();\n this.onMediaDisconnected();\n }\n\n protected onResize(): void {\n this.renderer?.resize();\n }\n\n /** After license resolves, enforce theme color gating */\n protected override onLicenseResolved(): void {\n if (this.controlsOverlay && this.license) {\n this.controlsOverlay.updateTheme(this.config, this.license);\n }\n }\n\n protected onAttributeChanged(name: string, _oldVal: string | null, _newVal: string | null): void {\n if (name === 'format' && this.config.format && this.renderer) {\n this.renderer.setupVideo(this.videoElement, this.config.format);\n }\n }\n\n // --- Public HTML5 Media API ---\n\n play(): Promise<void> {\n // Hide poster on play\n const poster = this.shadow.querySelector('.vz-poster');\n if (poster) poster.classList.add('hidden');\n // Resume AudioContext on user gesture (browsers require this)\n this.audioManager?.resume().catch(() => {});\n return this.videoElement.play();\n }\n\n pause(): void {\n this.videoElement.pause();\n }\n\n seek(time: number): void {\n this.videoElement.currentTime = time;\n }\n\n get duration(): number {\n return this.videoElement.duration;\n }\n\n get currentTime(): number {\n return this.videoElement.currentTime;\n }\n\n set currentTime(t: number) {\n this.videoElement.currentTime = t;\n }\n\n get paused(): boolean {\n return this.videoElement.paused;\n }\n\n get volume(): number {\n return this.videoElement.volume;\n }\n\n set volume(v: number) {\n this.videoElement.volume = v;\n }\n\n get muted(): boolean {\n return this.videoElement.muted;\n }\n\n set muted(m: boolean) {\n this.videoElement.muted = m;\n }\n\n get playbackRate(): number {\n return this.videoElement.playbackRate;\n }\n\n set playbackRate(r: number) {\n this.videoElement.playbackRate = r;\n }\n\n toggleFullscreen(): void {\n if (isFullscreen()) {\n exitFullscreen();\n } else {\n requestFullscreen(this);\n }\n }\n\n /** Get the Three.js renderer (for advanced use / WebXR) */\n getRenderer(): VzRenderer {\n return this.renderer;\n }\n\n // --- Protected hooks for subclasses ---\n\n protected abstract onMediaConnected(): void;\n protected abstract onMediaDisconnected(): void;\n\n // --- Private helpers ---\n\n /** Parse <vz-annotation> children and create annotation manager if any exist */\n private initAnnotations(): void {\n const annotationEls = this.querySelectorAll('vz-annotation');\n if (annotationEls.length === 0) return;\n\n const annotations: AnnotationData[] = [];\n annotationEls.forEach((el) => {\n const vzEl = el as VzAnnotationElement;\n if (typeof vzEl.getData === 'function') {\n annotations.push(vzEl.getData());\n }\n });\n\n if (annotations.length === 0) return;\n\n this.annotationManager = new AnnotationManager(\n this.renderer.getScene(),\n this.renderer.getCamera(),\n this.containerEl,\n this.shadow,\n );\n this.annotationManager.setAnnotations(annotations);\n\n // Update annotation visibility based on current playback time\n this.state.subscribe('currentTime', (time) => {\n this.annotationManager?.update(time);\n });\n }\n\n /** Initialize collaborative viewing if collab attributes are set */\n private initCollab(): void {\n const { collabServer, collabRoom, collabRole, collabDisplayName } = this.config;\n if (!collabServer || !collabRoom) return;\n\n const userId = this.config.collabUserId ?? crypto.randomUUID();\n const role = collabRole ?? 'viewer';\n const displayName = collabDisplayName ?? `User-${userId.slice(0, 6)}`;\n\n // Create gaze cursor manager for peer cursors\n this.gazeCursorManager = new GazeCursorManager(this.renderer.getScene());\n\n // Create chat panel and button\n this.chatPanel = new ChatPanel(this.shadow, (text) => {\n // Add the local message to the panel immediately\n this.chatPanel?.addMessage({\n userId,\n displayName,\n text,\n timestamp: Date.now(),\n isLocal: true,\n });\n // Send via WebSocket\n this.collabClient?.sendChat(text);\n });\n\n if (!this.config.hideControls && this.controlsOverlay) {\n this.chatButton = new ChatButton(this.chatPanel);\n this.controlsOverlay.addControl(this.chatButton.getElement(), 'right');\n }\n\n // Create voice manager (signaling relay via WS)\n this.voiceManager = new VoiceManager(\n (targetUserId, sdp, candidate) => {\n this.collabClient?.sendVoiceSignal(targetUserId, sdp, candidate);\n },\n );\n\n // Create voice button (first click = start, subsequent = toggle mute)\n if (!this.config.hideControls && this.controlsOverlay) {\n this.voiceButton = new VoiceButton(this.voiceManager, () => {\n // When voice starts, connect to all existing peers\n // (peers who join later will be auto-connected via onUserJoined)\n });\n this.controlsOverlay.addControl(this.voiceButton.getElement(), 'right');\n }\n\n this.collabClient = new CollaborativeClient({\n serverUrl: collabServer,\n roomCode: collabRoom,\n role,\n userId,\n displayName,\n state: this.state,\n mediaElement: this,\n password: this.config.collabPassword,\n onPeerGaze: (peerUserId, lat, lon) => {\n this.gazeCursorManager?.updateGaze(peerUserId, lat, lon);\n },\n onUserJoined: (user) => {\n if (user.userId !== userId) {\n this.gazeCursorManager?.addPeer(user.userId, user.displayName);\n // Auto-connect voice to new peer if voice is active\n if (this.voiceManager?.isActive()) {\n this.voiceManager.connectToPeer(user.userId).catch(() => {});\n }\n }\n },\n onUserLeft: (leftUserId) => {\n this.gazeCursorManager?.removePeer(leftUserId);\n // Clean up voice connection to departed peer\n this.voiceManager?.removePeer(leftUserId);\n },\n onChatMessage: (msgUserId, msgDisplayName, text, timestamp) => {\n // Add remote message to chat panel\n this.chatPanel?.addMessage({\n userId: msgUserId,\n displayName: msgDisplayName,\n text,\n timestamp,\n isLocal: false,\n });\n // Increment unread badge if panel is closed\n if (!this.chatPanel?.isVisible()) {\n this.chatButton?.incrementUnread();\n }\n },\n onVoiceSignal: (fromUserId, sdp, candidate) => {\n this.voiceManager?.handleSignal(fromUserId, sdp, candidate).catch(() => {});\n },\n onParticipantsChange: (users) => {\n // On initial room_state, add cursors for all existing peers\n for (const user of users) {\n if (user.userId !== userId) {\n this.gazeCursorManager?.addPeer(user.userId, user.displayName);\n // Set initial gaze if provided\n if (user.lat !== undefined && user.lon !== undefined) {\n this.gazeCursorManager?.updateGaze(user.userId, user.lat, user.lon);\n }\n }\n }\n },\n });\n this.collabClient.connect();\n\n // Create gaze sender to broadcast local camera orientation\n this.gazeSender = new GazeSender(\n this.renderer.getCameraController(),\n (msg) => this.collabClient?.send(msg),\n );\n this.gazeSender.start();\n\n // Update gaze cursor interpolation each frame via currentTime subscription\n // (currentTime updates at display refresh rate via timeupdate)\n const unsubGaze = this.state.subscribe('currentTime', () => {\n this.gazeCursorManager?.update();\n });\n this.collabClient.addCleanup(unsubGaze);\n }\n\n /** Initialize caption system from <track> children */\n private initCaptions(): void {\n const trackElements = this.querySelectorAll('track');\n if (trackElements.length === 0) return;\n\n this.captionRenderer = new CaptionRenderer(this.shadow, this.state);\n\n // Load tracks asynchronously; once done, wire up the CC button\n this.captionRenderer.loadTracks(this).then(() => {\n if (!this.captionRenderer?.hasTracks()) return;\n\n // Create CC button if controls are visible\n if (!this.config.hideControls && this.controlsOverlay) {\n this.ccButton = new CcButton(this.state, this.captionRenderer);\n this.controlsOverlay.addControl(this.ccButton.getElement(), 'right');\n this.ccButton.updateVisibility();\n }\n }).catch((err) => {\n console.warn('[Vizor] Caption init error:', err);\n });\n }\n\n private createVideoElement(): HTMLVideoElement {\n const video = document.createElement('video');\n // crossorigin is required for WebGL texture use\n video.crossOrigin = this.config.crossorigin ?? 'anonymous';\n video.playsInline = true;\n video.preload = this.config.preload === 'none' ? 'metadata' : this.config.preload;\n video.loop = this.config.loop;\n video.muted = this.config.muted;\n\n // Hidden - used only as texture source\n video.style.display = 'none';\n\n // Log errors for debugging\n video.addEventListener('error', () => {\n const err = video.error;\n console.error('[Vizor] Video error:', err?.code, err?.message);\n this.events.emit('vz-error', {\n code: `MEDIA_ERROR_${err?.code ?? 'UNKNOWN'}`,\n message: err?.message ?? 'Unknown video error',\n });\n });\n\n this.shadow.appendChild(video);\n\n return video;\n }\n\n /** Get the source manager (for quality switching) */\n getSourceManager(): SourceManager {\n return this.sourceManager;\n }\n\n /** Parse <source> children and set up video sources */\n private loadSources(): void {\n const sourceElements = this.querySelectorAll('source');\n const sources: ParsedSource[] = [];\n\n sourceElements.forEach((el) => {\n const src = el.getAttribute('src');\n const type = el.getAttribute('type') ?? 'video/mp4';\n if (!src) return;\n\n sources.push({\n url: src,\n type,\n quality: el.getAttribute('quality') ?? undefined,\n fps: el.hasAttribute('fps') ? parseFloat(el.getAttribute('fps')!) : undefined,\n format: el.hasAttribute('format') ? parseFormat(el.getAttribute('format')!) : undefined,\n audioFormat: el.getAttribute('audio-format') === 'ambix' ? 'ambix' : undefined,\n });\n });\n\n if (sources.length === 0) {\n // Check for direct src attribute\n const src = this.getAttribute('src');\n if (src) {\n sources.push({ url: src, type: 'video/mp4' });\n }\n }\n\n if (sources.length === 0) return;\n\n // Use SourceManager to select the best adapter and load\n this.sourceManager.loadSources(sources, this.videoElement, this.state, this.events).catch((err) => {\n console.error('[Vizor] Source loading error:', err);\n this.events.emit('vz-error', { code: 'SOURCE_LOAD_ERROR', message: String(err) });\n });\n }\n\n /** Mirror HTML5 media events from the hidden video to the custom element */\n private mirrorMediaEvents(): void {\n for (const eventName of MEDIA_EVENTS) {\n this.videoElement.addEventListener(eventName, () => {\n // Update state\n this.syncStateFromVideo();\n\n // Re-dispatch event on the custom element\n this.events.emitMedia(eventName);\n });\n }\n\n // High-frequency timeupdate polling for smoother seek bar\n this.timeUpdateTimer = setInterval(() => {\n if (!this.videoElement.paused) {\n this.state.set('currentTime', this.videoElement.currentTime);\n }\n }, 250);\n }\n\n /** Sync the reactive state from the current video element state */\n private syncStateFromVideo(): void {\n const v = this.videoElement;\n this.state.batch({\n playing: !v.paused && !v.ended,\n paused: v.paused,\n currentTime: v.currentTime,\n duration: isNaN(v.duration) ? 0 : v.duration,\n buffered: v.buffered.length > 0 ? v.buffered.end(v.buffered.length - 1) : 0,\n seeking: v.seeking,\n ended: v.ended,\n volume: v.volume,\n muted: v.muted,\n playbackRate: v.playbackRate,\n loading: v.readyState < 3,\n });\n }\n}\n","import { VzMediaElement } from '../core/media-element.js';\n\n/**\n * <vz-video> - VR-capable video player element.\n *\n * Supports all VzFormat types (mono/stereo, 180/360, flat, cubemap).\n * Mirrors the HTML5 <video> API with added VR capabilities.\n *\n * @example\n * ```html\n * <vz-video title=\"My Video\" format=\"STEREO_360_LR\" poster=\"thumb.jpg\">\n * <source src=\"video.mp4\" type=\"video/mp4\" />\n * </vz-video>\n * ```\n */\nexport class VzVideoElement extends VzMediaElement {\n protected onMediaConnected(): void {\n // Video-specific initialization\n // Controls, analytics, etc. will be added in later phases\n }\n\n protected onMediaDisconnected(): void {\n // Video-specific cleanup\n }\n}\n\n/** Register the <vz-video> custom element */\nexport function registerVideo(): void {\n if (!customElements.get('vz-video')) {\n customElements.define('vz-video', VzVideoElement);\n }\n}\n","import { TextureLoader, SRGBColorSpace } from 'three';\nimport type { VzFormat } from '@vizor-vr/shared';\nimport { getFormatMeta } from '@vizor-vr/shared';\nimport { VzBaseElement } from '../core/base-element.js';\nimport { VzRenderer } from '../renderer/renderer.js';\nimport { ControlsOverlay } from '../controls/controls-overlay.js';\nimport { FullscreenButton } from '../controls/fullscreen-button.js';\nimport { Compass } from '../controls/compass.js';\nimport { requestFullscreen, exitFullscreen, isFullscreen, onFullscreenChange } from '../utils/fullscreen.js';\n\n/**\n * <vz-img> - VR-capable panoramic image viewer element.\n *\n * @example\n * ```html\n * <vz-img src=\"panorama.jpg\" format=\"MONO_360\" title=\"My Panorama\" />\n * ```\n */\nexport class VzImgElement extends VzBaseElement {\n // `src` is not part of the shared attribute map, so observe it explicitly —\n // otherwise runtime `src` changes never reach onAttributeChanged (dead code).\n static get observedAttributes(): string[] {\n return [...super.observedAttributes, 'src'];\n }\n\n private renderer!: VzRenderer;\n private containerEl!: HTMLDivElement;\n private controlsOverlay: ControlsOverlay | null = null;\n private compass: Compass | null = null;\n private fullscreenButton: FullscreenButton | null = null;\n private cleanupFullscreen: (() => void) | null = null;\n\n protected onConnected(): void {\n // Create container\n this.containerEl = document.createElement('div');\n this.containerEl.className = 'vz-container';\n this.shadow.appendChild(this.containerEl);\n\n // Initialize renderer\n this.renderer = new VzRenderer(\n this.shadow,\n this.containerEl,\n this.state,\n this.config,\n );\n\n // Load image and set up projection\n const src = this.getAttribute('src');\n if (src && this.config.format) {\n this.loadImage(src, this.config.format);\n }\n\n // Add controls (compass + fullscreen only for images)\n if (!this.config.hideControls) {\n this.controlsOverlay = new ControlsOverlay(\n this.shadow, this.containerEl, this.state, this.config, this.events,\n );\n\n this.compass = new Compass(this.state, this.renderer);\n this.controlsOverlay.addControl(this.compass.getElement(), 'right');\n\n this.fullscreenButton = new FullscreenButton(this.state, this);\n this.controlsOverlay.addControl(this.fullscreenButton.getElement(), 'right');\n }\n\n // Fullscreen support\n this.cleanupFullscreen = onFullscreenChange(() => {\n const fs = isFullscreen();\n this.state.set('fullscreen', fs);\n this.renderer.resize();\n });\n\n this.renderer.start();\n this.events.emit('vz-ready');\n }\n\n protected onDisconnected(): void {\n this.cleanupFullscreen?.();\n this.compass?.dispose();\n this.fullscreenButton?.dispose();\n this.controlsOverlay?.dispose();\n this.renderer.dispose();\n }\n\n toggleFullscreen(): void {\n if (isFullscreen()) {\n exitFullscreen();\n } else {\n requestFullscreen(this);\n }\n }\n\n protected onAttributeChanged(name: string): void {\n if (name === 'src' || name === 'format') {\n const src = this.getAttribute('src');\n if (src && this.config.format) {\n this.loadImage(src, this.config.format);\n }\n }\n }\n\n protected onResize(): void {\n this.renderer?.resize();\n }\n\n private loadImage(src: string, format: VzFormat): void {\n const loader = new TextureLoader();\n loader.load(\n src,\n (texture) => {\n texture.colorSpace = SRGBColorSpace;\n this.renderer.setupImage(texture, format);\n },\n undefined,\n (err) => {\n console.error('[Vizor] Failed to load image:', err);\n this.events.emit('vz-error', { code: 'IMAGE_LOAD_ERROR', message: String(err) });\n },\n );\n }\n}\n\n/** Register the <vz-img> custom element */\nexport function registerImg(): void {\n if (!customElements.get('vz-img')) {\n customElements.define('vz-img', VzImgElement);\n }\n}\n","import {\n Sprite,\n SpriteMaterial,\n CanvasTexture,\n LinearFilter,\n} from 'three';\nimport type { Scene } from 'three';\nimport type { TourPortal } from '@vizor-vr/shared';\nimport { latLonToSpherical } from '@vizor-vr/shared';\n\nexport interface HotspotSprite {\n portal: TourPortal;\n sprite: Sprite;\n}\n\nconst HOTSPOT_DISTANCE = 200;\nconst HOTSPOT_SCALE_X = 30;\nconst HOTSPOT_SCALE_Y = 15;\nconst CANVAS_W = 256;\nconst CANVAS_H = 128;\n\n/**\n * Renders 3D sprite labels for tour portal hotspots.\n * Positioned on the inside of the panoramic sphere at each portal's lat/lon.\n */\nexport class HotspotRenderer {\n private sprites: HotspotSprite[] = [];\n private scene: Scene;\n\n constructor(scene: Scene) {\n this.scene = scene;\n }\n\n /** Create sprite meshes for all portals and add to scene */\n setPortals(portals: TourPortal[]): void {\n this.clear();\n\n for (const portal of portals) {\n const sprite = this.createSprite(portal);\n sprite.userData.portalTo = portal.to;\n this.sprites.push({ portal, sprite });\n this.scene.add(sprite);\n }\n }\n\n /** Remove all hotspot sprites from scene */\n clear(): void {\n for (const { sprite } of this.sprites) {\n this.scene.remove(sprite);\n sprite.material.map?.dispose();\n sprite.material.dispose();\n }\n this.sprites = [];\n }\n\n /** Get all current sprites (for raycasting) */\n getSprites(): Sprite[] {\n return this.sprites.map((h) => h.sprite);\n }\n\n dispose(): void {\n this.clear();\n }\n\n private createSprite(portal: TourPortal): Sprite {\n // Draw label using Canvas2D\n const canvas = document.createElement('canvas');\n canvas.width = CANVAS_W;\n canvas.height = CANVAS_H;\n const ctx = canvas.getContext('2d')!;\n\n // Rounded rectangle background\n const pad = 8;\n ctx.fillStyle = 'rgba(0, 160, 255, 0.85)';\n ctx.beginPath();\n this.roundRect(ctx, pad, pad, CANVAS_W - pad * 2, CANVAS_H - pad * 2, 16);\n ctx.fill();\n\n // Label text\n const label = portal.title ?? portal.to;\n ctx.fillStyle = '#fff';\n ctx.font = 'bold 24px sans-serif';\n ctx.textAlign = 'center';\n ctx.textBaseline = 'middle';\n ctx.fillText(label, CANVAS_W / 2, CANVAS_H / 2 - 4, CANVAS_W - 32);\n\n // Small arrow indicator\n ctx.font = '16px sans-serif';\n ctx.fillText('\\u2192', CANVAS_W / 2, CANVAS_H / 2 + 20);\n\n const texture = new CanvasTexture(canvas);\n texture.minFilter = LinearFilter;\n texture.magFilter = LinearFilter;\n\n const material = new SpriteMaterial({\n map: texture,\n sizeAttenuation: true,\n depthTest: false,\n });\n const sprite = new Sprite(material);\n\n // Position on sphere interior at the portal's lat/lon\n const distance = portal.distance ?? HOTSPOT_DISTANCE;\n const lat = portal.lat ?? 0;\n const lon = portal.lon ?? 0;\n const { phi, theta } = latLonToSpherical(lat, lon);\n\n sprite.position.set(\n distance * Math.sin(phi) * Math.cos(theta),\n distance * Math.cos(phi),\n distance * Math.sin(phi) * Math.sin(theta),\n );\n sprite.scale.set(HOTSPOT_SCALE_X, HOTSPOT_SCALE_Y, 1);\n\n return sprite;\n }\n\n private roundRect(\n ctx: CanvasRenderingContext2D,\n x: number,\n y: number,\n w: number,\n h: number,\n r: number,\n ): void {\n ctx.moveTo(x + r, y);\n ctx.arcTo(x + w, y, x + w, y + h, r);\n ctx.arcTo(x + w, y + h, x, y + h, r);\n ctx.arcTo(x, y + h, x, y, r);\n ctx.arcTo(x, y, x + w, y, r);\n ctx.closePath();\n }\n}\n","import { Raycaster, Vector2 } from 'three';\nimport type { PerspectiveCamera, Sprite } from 'three';\n\nexport type HotspotClickHandler = (portalTo: string) => void;\n\n/**\n * Handles click detection on hotspot sprites via raycasting.\n * Converts mouse/touch clicks to normalized device coordinates,\n * raycasts through the scene, and triggers navigation on hit.\n */\nexport class HotspotInteraction {\n private raycaster = new Raycaster();\n private pointer = new Vector2();\n private camera: PerspectiveCamera;\n private getSprites: () => Sprite[];\n private onClick: HotspotClickHandler;\n private domElement: HTMLElement;\n private cleanups: Array<() => void> = [];\n\n constructor(\n camera: PerspectiveCamera,\n domElement: HTMLElement,\n getSprites: () => Sprite[],\n onClick: HotspotClickHandler,\n ) {\n this.camera = camera;\n this.domElement = domElement;\n this.getSprites = getSprites;\n this.onClick = onClick;\n }\n\n /** Start listening for click events */\n attach(): void {\n const handler = (e: MouseEvent) => {\n const rect = this.domElement.getBoundingClientRect();\n this.pointer.x = ((e.clientX - rect.left) / rect.width) * 2 - 1;\n this.pointer.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;\n\n this.raycaster.setFromCamera(this.pointer, this.camera);\n const sprites = this.getSprites();\n const intersects = this.raycaster.intersectObjects(sprites);\n\n if (intersects.length > 0) {\n const portalTo = intersects[0]!.object.userData.portalTo as string;\n if (portalTo) {\n e.stopPropagation();\n this.onClick(portalTo);\n }\n }\n };\n\n this.domElement.addEventListener('click', handler);\n this.cleanups.push(() => this.domElement.removeEventListener('click', handler));\n }\n\n /** Stop listening */\n detach(): void {\n for (const cleanup of this.cleanups) cleanup();\n this.cleanups = [];\n }\n\n dispose(): void {\n this.detach();\n }\n}\n","import type { Scene, PerspectiveCamera } from 'three';\nimport type { TourPortal } from '@vizor-vr/shared';\nimport { HotspotRenderer } from './hotspot-renderer.js';\nimport { HotspotInteraction, type HotspotClickHandler } from './hotspot-interaction.js';\n\n/**\n * Orchestrates hotspot rendering and interaction.\n * Used by VzTourElement to manage portal sprites and click detection.\n */\nexport class HotspotManager {\n private renderer: HotspotRenderer;\n private interaction: HotspotInteraction;\n\n constructor(\n scene: Scene,\n camera: PerspectiveCamera,\n domElement: HTMLElement,\n onNavigate: HotspotClickHandler,\n ) {\n this.renderer = new HotspotRenderer(scene);\n this.interaction = new HotspotInteraction(\n camera,\n domElement,\n () => this.renderer.getSprites(),\n onNavigate,\n );\n this.interaction.attach();\n }\n\n /** Set portals for the current probe */\n setPortals(portals: TourPortal[]): void {\n this.renderer.setPortals(portals);\n }\n\n /** Clear all hotspots */\n clear(): void {\n this.renderer.clear();\n }\n\n dispose(): void {\n this.interaction.dispose();\n this.renderer.dispose();\n }\n}\n","import { TextureLoader, SRGBColorSpace } from 'three';\nimport type { Texture } from 'three';\nimport type { TourProbe } from '@vizor-vr/shared';\nimport { VzBaseElement } from '../core/base-element.js';\nimport { VzRenderer } from '../renderer/renderer.js';\nimport { HotspotManager } from '../hotspots/hotspot-manager.js';\nimport { ControlsOverlay } from '../controls/controls-overlay.js';\nimport { FullscreenButton } from '../controls/fullscreen-button.js';\nimport { Compass } from '../controls/compass.js';\nimport { VrButton } from '../controls/vr-button.js';\nimport { XRManager } from '../xr/xr-manager.js';\nimport { requestFullscreen, exitFullscreen, isFullscreen, onFullscreenChange } from '../utils/fullscreen.js';\nimport type { VzTourImgElement } from './vz-tour-img.js';\n\n/**\n * <vz-tour> - Interactive panoramic tour with navigable probes.\n *\n * @example\n * ```html\n * <vz-tour start-probe-id=\"lobby\" style=\"width:100%; aspect-ratio:16/9\">\n * <vz-tour-img probe-id=\"lobby\" format=\"MONO_360\" src=\"lobby.jpg\" title=\"Lobby\">\n * <vz-tour-portal to=\"office\" lat=\"10\" lon=\"45\" title=\"Go to Office\" />\n * </vz-tour-img>\n * <vz-tour-img probe-id=\"office\" format=\"MONO_360\" src=\"office.jpg\" title=\"Office\">\n * <vz-tour-portal to=\"lobby\" lat=\"0\" lon=\"-135\" title=\"Back to Lobby\" />\n * </vz-tour-img>\n * </vz-tour>\n * ```\n */\nexport class VzTourElement extends VzBaseElement {\n private renderer!: VzRenderer;\n private containerEl!: HTMLDivElement;\n private probes = new Map<string, TourProbe>();\n private currentProbeId: string | null = null;\n private hotspotManager: HotspotManager | null = null;\n private textureCache = new Map<string, Texture>();\n private controlsOverlay: ControlsOverlay | null = null;\n private compass: Compass | null = null;\n private fullscreenButton: FullscreenButton | null = null;\n private vrButton: VrButton | null = null;\n private xrManager: XRManager | null = null;\n private cleanupFullscreen: (() => void) | null = null;\n private loader = new TextureLoader();\n\n protected onConnected(): void {\n // Create container\n this.containerEl = document.createElement('div');\n this.containerEl.className = 'vz-container';\n this.shadow.appendChild(this.containerEl);\n\n // Initialize renderer\n this.renderer = new VzRenderer(\n this.shadow,\n this.containerEl,\n this.state,\n this.config,\n );\n\n // Parse child elements into probes map\n this.parseProbes();\n\n // Create hotspot manager for portal interaction\n this.hotspotManager = new HotspotManager(\n this.renderer.getScene(),\n this.renderer.getCamera(),\n this.containerEl,\n (probeId) => this.navigateTo(probeId),\n );\n\n // Initialize WebXR manager\n this.xrManager = new XRManager(\n this.renderer.getThreeRenderer(),\n this.state,\n this.events,\n this.renderer.getCameraController(),\n );\n\n // Add controls (compass + fullscreen + VR)\n if (!this.config.hideControls) {\n this.controlsOverlay = new ControlsOverlay(\n this.shadow, this.containerEl, this.state, this.config, this.events,\n );\n\n this.compass = new Compass(this.state, this.renderer);\n this.controlsOverlay.addControl(this.compass.getElement(), 'right');\n\n this.vrButton = new VrButton(this.state, () => {\n this.xrManager?.toggleXR().catch((err) => {\n console.error('[Vizor] XR toggle failed:', err);\n this.events.emit('vz-error', { code: 'XR_ERROR', message: String(err) });\n });\n });\n this.controlsOverlay.addControl(this.vrButton.getElement(), 'right');\n\n this.fullscreenButton = new FullscreenButton(this.state, this);\n this.controlsOverlay.addControl(this.fullscreenButton.getElement(), 'right');\n }\n\n // Fullscreen support\n this.cleanupFullscreen = onFullscreenChange(() => {\n const fs = isFullscreen();\n this.state.set('fullscreen', fs);\n this.renderer.resize();\n });\n\n // Load initial probe\n const startId = this.getAttribute('start-probe-id') ?? this.probes.keys().next().value;\n if (startId) {\n this.navigateTo(startId);\n }\n\n this.renderer.start();\n this.events.emit('vz-ready');\n }\n\n protected onDisconnected(): void {\n this.cleanupFullscreen?.();\n this.compass?.dispose();\n this.fullscreenButton?.dispose();\n this.vrButton?.dispose();\n this.controlsOverlay?.dispose();\n this.xrManager?.dispose();\n this.hotspotManager?.dispose();\n\n // Dispose cached textures\n for (const texture of this.textureCache.values()) {\n texture.dispose();\n }\n this.textureCache.clear();\n\n this.renderer.dispose();\n }\n\n protected onAttributeChanged(_name: string): void {\n // Re-parse if children changed\n }\n\n protected onResize(): void {\n this.renderer?.resize();\n }\n\n /** Navigate to a specific probe by ID */\n async navigateTo(probeId: string): Promise<void> {\n const probe = this.probes.get(probeId);\n if (!probe) {\n console.error(`[Vizor] Tour probe not found: ${probeId}`);\n return;\n }\n\n const prevProbeId = this.currentProbeId;\n this.currentProbeId = probeId;\n\n // Clear existing hotspots during transition\n this.hotspotManager?.clear();\n\n // Load texture (from cache or network)\n this.state.set('loading', true);\n try {\n const texture = await this.loadOrGetTexture(probe.src);\n this.renderer.setupImage(texture, probe.format);\n } catch (err) {\n console.error('[Vizor] Failed to load probe image:', err);\n this.events.emit('vz-error', { code: 'TOUR_LOAD_ERROR', message: String(err) });\n this.state.set('loading', false);\n return;\n }\n this.state.set('loading', false);\n\n // Apply initial camera orientation if specified\n if (probe.rotationY !== undefined) {\n this.renderer.getCameraController().setOrientation(0, probe.rotationY);\n }\n\n // Set new hotspots for this probe's portals\n this.hotspotManager?.setPortals(probe.portals);\n\n // Update state\n this.state.batch({\n currentProbeId: probeId,\n });\n\n // Emit navigation event\n if (prevProbeId) {\n this.events.emit('vz-tour-navigate', {\n fromProbeId: prevProbeId,\n toProbeId: probeId,\n });\n }\n\n // Preload neighboring probes\n this.preloadNeighbors(probe);\n }\n\n /** Get current probe ID */\n get currentProbe(): string | null {\n return this.currentProbeId;\n }\n\n toggleFullscreen(): void {\n if (isFullscreen()) {\n exitFullscreen();\n } else {\n requestFullscreen(this);\n }\n }\n\n /** Parse all <vz-tour-img> children into the probes map */\n private parseProbes(): void {\n this.probes.clear();\n const tourImgs = this.querySelectorAll('vz-tour-img') as NodeListOf<VzTourImgElement>;\n\n for (const img of tourImgs) {\n const probe = img.getProbeData();\n if (probe.probeId) {\n this.probes.set(probe.probeId, probe);\n }\n }\n\n this.state.set('tourProbeCount', this.probes.size);\n }\n\n /** Load a texture, using cache if available */\n private loadOrGetTexture(src: string): Promise<Texture> {\n const cached = this.textureCache.get(src);\n if (cached) return Promise.resolve(cached);\n\n return new Promise((resolve, reject) => {\n this.loader.load(\n src,\n (texture) => {\n texture.colorSpace = SRGBColorSpace;\n this.textureCache.set(src, texture);\n resolve(texture);\n },\n undefined,\n reject,\n );\n });\n }\n\n /** Preload textures for neighboring probes (fire-and-forget) */\n private preloadNeighbors(probe: TourProbe): void {\n for (const portal of probe.portals) {\n const target = this.probes.get(portal.to);\n if (target && !this.textureCache.has(target.src)) {\n this.loader.load(\n target.src,\n (texture) => {\n texture.colorSpace = SRGBColorSpace;\n this.textureCache.set(target.src, texture);\n },\n );\n }\n }\n }\n}\n\n/** Register the <vz-tour> custom element */\nexport function registerTour(): void {\n if (!customElements.get('vz-tour')) {\n customElements.define('vz-tour', VzTourElement);\n }\n}\n","import type { TourProbe, VzFormat } from '@vizor-vr/shared';\nimport { parseFormat } from '@vizor-vr/shared';\nimport { HTMLElementBase } from '../core/html-element.js';\nimport type { VzTourPortalElement } from './vz-tour-portal.js';\n\n/**\n * <vz-tour-img> - A single panoramic probe in a tour.\n * Parsed by the parent <vz-tour> to build the navigable data structure.\n *\n * @example\n * ```html\n * <vz-tour-img probe-id=\"lobby\" format=\"MONO_360\" src=\"lobby.jpg\" title=\"Lobby\">\n * <vz-tour-portal to=\"office\" lat=\"10\" lon=\"45\" title=\"Go to Office\" />\n * </vz-tour-img>\n * ```\n */\nexport class VzTourImgElement extends HTMLElementBase {\n static get observedAttributes(): string[] {\n return ['probe-id', 'format', 'src', 'src-left', 'src-right', 'title', 'rotation-y'];\n }\n\n /** Parse this element and its portal children into a TourProbe data object */\n getProbeData(): TourProbe {\n const formatStr = this.getAttribute('format');\n const format = formatStr ? parseFormat(formatStr) : null;\n\n const portals = Array.from(\n this.querySelectorAll('vz-tour-portal') as NodeListOf<VzTourPortalElement>,\n ).map((el) => el.getPortalData());\n\n return {\n probeId: this.getAttribute('probe-id') ?? '',\n title: this.getAttribute('title') ?? undefined,\n format: format as VzFormat,\n src: this.getAttribute('src') ?? '',\n srcLeft: this.getAttribute('src-left') ?? undefined,\n srcRight: this.getAttribute('src-right') ?? undefined,\n rotationY: this.hasAttribute('rotation-y')\n ? parseFloat(this.getAttribute('rotation-y')!)\n : undefined,\n portals,\n };\n }\n}\n\n/** Register the <vz-tour-img> custom element */\nexport function registerTourImg(): void {\n if (!customElements.get('vz-tour-img')) {\n customElements.define('vz-tour-img', VzTourImgElement);\n }\n}\n","import type { TourPortal } from '@vizor-vr/shared';\nimport { HTMLElementBase } from '../core/html-element.js';\n\n/**\n * <vz-tour-portal> - Declarative portal (navigation hotspot) definition.\n * Must be a child of <vz-tour-img>.\n *\n * @example\n * ```html\n * <vz-tour-portal to=\"office\" lat=\"10\" lon=\"45\" title=\"Go to Office\" />\n * ```\n */\nexport class VzTourPortalElement extends HTMLElementBase {\n static get observedAttributes(): string[] {\n return ['to', 'lat', 'lon', 'title', 'distance'];\n }\n\n /** Parse this element's attributes into a TourPortal data object */\n getPortalData(): TourPortal {\n return {\n to: this.getAttribute('to') ?? '',\n title: this.getAttribute('title') ?? undefined,\n lat: this.hasAttribute('lat') ? parseFloat(this.getAttribute('lat')!) : undefined,\n lon: this.hasAttribute('lon') ? parseFloat(this.getAttribute('lon')!) : undefined,\n distance: this.hasAttribute('distance') ? parseFloat(this.getAttribute('distance')!) : undefined,\n };\n }\n}\n\n/** Register the <vz-tour-portal> custom element */\nexport function registerTourPortal(): void {\n if (!customElements.get('vz-tour-portal')) {\n customElements.define('vz-tour-portal', VzTourPortalElement);\n }\n}\n","import {\n SphereGeometry,\n PlaneGeometry,\n MeshBasicMaterial,\n Mesh,\n} from 'three';\nimport type { PerspectiveCamera, Texture } from 'three';\nimport type { StereoMode } from '@vizor-vr/shared';\nimport { SPHERE_DEFAULTS, CAMERA_DEFAULTS } from '@vizor-vr/shared';\nimport type { IProjection } from './projection.js';\n\nexport interface CinemaConfig {\n screenWidth: number;\n screenHeight: number;\n screenDistance: number;\n screenOffsetY: number;\n}\n\nconst DEFAULT_CINEMA: CinemaConfig = {\n screenWidth: 3,\n screenHeight: 1.8,\n screenDistance: 2.5,\n screenOffsetY: 0,\n};\n\n/**\n * Cinema projection: flat video screen inside a panoramic room.\n *\n * Composes two geometries:\n * - Inverted sphere for the room environment (equirectangular)\n * - Flat plane for the video screen positioned at configurable distance/size\n */\nexport class CinemaProjection implements IProjection {\n private meshes: Mesh[] = [];\n private roomMesh: Mesh | null = null;\n private config: CinemaConfig;\n\n constructor(config?: Partial<CinemaConfig>) {\n this.config = { ...DEFAULT_CINEMA, ...config };\n }\n\n createMeshes(videoTexture: Texture, stereoMode: StereoMode): Mesh[] {\n this.dispose();\n\n // Room sphere (dark until room texture is set)\n const roomGeo = new SphereGeometry(\n SPHERE_DEFAULTS.radius,\n SPHERE_DEFAULTS.widthSegments,\n SPHERE_DEFAULTS.heightSegments,\n );\n roomGeo.scale(-1, 1, 1);\n const roomMat = new MeshBasicMaterial({ color: 0x111111 });\n this.roomMesh = new Mesh(roomGeo, roomMat);\n this.roomMesh.layers.set(0); // Visible to both eyes\n\n // Screen plane with video texture\n // Position along +X axis (CameraController lon=0 faces +X)\n // Rotate -90° around Y so plane faces back toward camera at origin\n const screenGeo = new PlaneGeometry(this.config.screenWidth, this.config.screenHeight);\n const screenMat = new MeshBasicMaterial({ map: videoTexture });\n const screenMesh = new Mesh(screenGeo, screenMat);\n screenMesh.position.set(this.config.screenDistance, this.config.screenOffsetY, 0);\n screenMesh.rotation.y = -Math.PI / 2;\n\n if (stereoMode === 'mono') {\n screenMesh.layers.set(0);\n this.meshes = [this.roomMesh, screenMesh];\n } else {\n // Stereo: create two screen meshes with UV remapping\n const screenGeoR = new PlaneGeometry(this.config.screenWidth, this.config.screenHeight);\n const screenMatR = new MeshBasicMaterial({ map: videoTexture });\n const screenMeshR = new Mesh(screenGeoR, screenMatR);\n screenMeshR.position.copy(screenMesh.position);\n screenMeshR.rotation.copy(screenMesh.rotation);\n\n if (stereoMode === 'sbs') {\n this.remapUVsHorizontal(screenGeo, 0, 0.5);\n this.remapUVsHorizontal(screenGeoR, 0.5, 1);\n } else if (stereoMode === 'tb') {\n this.remapUVsVertical(screenGeo, 0.5, 1);\n this.remapUVsVertical(screenGeoR, 0, 0.5);\n }\n\n screenMesh.layers.set(1); // Left eye\n screenMeshR.layers.set(2); // Right eye\n this.meshes = [this.roomMesh, screenMesh, screenMeshR];\n }\n\n return this.meshes;\n }\n\n /** Set the room equirectangular texture */\n setRoomTexture(texture: Texture): void {\n if (this.roomMesh) {\n (this.roomMesh.material as MeshBasicMaterial).map = texture;\n (this.roomMesh.material as MeshBasicMaterial).color.set(0xffffff);\n (this.roomMesh.material as MeshBasicMaterial).needsUpdate = true;\n }\n }\n\n configureCamera(camera: PerspectiveCamera): void {\n camera.fov = CAMERA_DEFAULTS.fov;\n camera.near = 0.1;\n camera.far = SPHERE_DEFAULTS.radius * 2;\n camera.position.set(0, 0, 0);\n camera.updateProjectionMatrix();\n }\n\n update(_camera: PerspectiveCamera, _time: number): void {\n // No per-frame updates needed\n }\n\n dispose(): void {\n for (const mesh of this.meshes) {\n mesh.geometry.dispose();\n (mesh.material as MeshBasicMaterial).dispose();\n }\n this.meshes = [];\n this.roomMesh = null;\n }\n\n private remapUVsHorizontal(geo: PlaneGeometry, uMin: number, uMax: number): void {\n const uv = geo.attributes.uv!;\n for (let i = 0; i < uv.count; i++) {\n const u = uv.getX(i);\n uv.setX(i, uMin + u * (uMax - uMin));\n }\n uv.needsUpdate = true;\n }\n\n private remapUVsVertical(geo: PlaneGeometry, vMin: number, vMax: number): void {\n const uv = geo.attributes.uv!;\n for (let i = 0; i < uv.count; i++) {\n const v = uv.getY(i);\n uv.setY(i, vMin + v * (vMax - vMin));\n }\n uv.needsUpdate = true;\n }\n}\n","import { TextureLoader, VideoTexture, SRGBColorSpace, LinearFilter } from 'three';\nimport { VzMediaElement } from '../core/media-element.js';\nimport { CinemaProjection } from '../renderer/projections/cinema-projection.js';\n\n/**\n * <vz-cinema> - Virtual cinema: flat video on a screen inside a panoramic room.\n *\n * @example\n * ```html\n * <vz-cinema\n * room-src=\"cinema-room.jpg\"\n * screen-width=\"3\"\n * screen-height=\"1.8\"\n * screen-distance=\"2.5\"\n * style=\"width:100%; aspect-ratio:16/9\"\n * >\n * <source src=\"movie.mp4\" type=\"video/mp4\" />\n * </vz-cinema>\n * ```\n */\nexport class VzCinemaElement extends VzMediaElement {\n private cinemaProjection: CinemaProjection | null = null;\n\n static get observedAttributes(): string[] {\n return [\n ...super.observedAttributes,\n 'room-src', 'room-format',\n 'screen-width', 'screen-height', 'screen-distance', 'screen-offset-y',\n ];\n }\n\n protected onMediaConnected(): void {\n // Suppress the base class auto-setupVideo — cinema handles its own projection\n this.suppressAutoSetup = true;\n\n // Set up cinema projection once video data is available\n if (this.videoElement.readyState >= 2) {\n this.setupCinemaProjection();\n } else {\n this.videoElement.addEventListener('loadeddata', () => {\n this.setupCinemaProjection();\n }, { once: true });\n }\n }\n\n protected onMediaDisconnected(): void {\n this.cinemaProjection = null;\n }\n\n private setupCinemaProjection(): void {\n this.cinemaProjection = new CinemaProjection({\n screenWidth: this.getNumAttr('screen-width', 3),\n screenHeight: this.getNumAttr('screen-height', 1.8),\n screenDistance: this.getNumAttr('screen-distance', 2.5),\n screenOffsetY: this.getNumAttr('screen-offset-y', 0),\n });\n\n // Create a VideoTexture for the screen\n const videoTexture = new VideoTexture(this.videoElement);\n videoTexture.colorSpace = SRGBColorSpace;\n videoTexture.minFilter = LinearFilter;\n videoTexture.magFilter = LinearFilter;\n videoTexture.generateMipmaps = false;\n\n const stereoMode = this.state.get('stereoMode');\n this.renderer.setCustomProjection(this.cinemaProjection, videoTexture, stereoMode);\n\n // Cinema is always panoramic (user looks around inside the room sphere)\n const cc = this.renderer.getCameraController();\n cc.setPanoramic(true);\n\n // Load room texture if specified\n const roomSrc = this.getAttribute('room-src');\n if (roomSrc) {\n const loader = new TextureLoader();\n loader.load(roomSrc, (texture) => {\n texture.colorSpace = SRGBColorSpace;\n this.cinemaProjection?.setRoomTexture(texture);\n });\n }\n }\n\n private getNumAttr(name: string, defaultVal: number): number {\n const val = this.getAttribute(name);\n if (val === null) return defaultVal;\n const num = parseFloat(val);\n return isNaN(num) ? defaultVal : num;\n }\n}\n\n/** Register the <vz-cinema> custom element */\nexport function registerCinema(): void {\n if (!customElements.get('vz-cinema')) {\n customElements.define('vz-cinema', VzCinemaElement);\n }\n}\n","import { VzMediaElement } from '../core/media-element.js';\n\n/**\n * <vz-live> - Live streaming VR video element.\n *\n * Supports flat + 360° live HLS/DASH streams.\n * Automatically configures the player for live mode:\n * - Sets `isLive = true` state\n * - Shows LIVE indicator instead of time display\n * - Disables seek bar scrubbing (or shows DVR window)\n * - Auto-seeks to live edge on load\n *\n * @example\n * ```html\n * <vz-live title=\"Live Concert\" format=\"MONO_360\" style=\"width: 100%; aspect-ratio: 16/9;\">\n * <source src=\"https://stream.example.com/live.m3u8\" type=\"application/x-mpegURL\" />\n * </vz-live>\n * ```\n */\nexport class VzLiveElement extends VzMediaElement {\n private liveEdgeInterval: ReturnType<typeof setInterval> | null = null;\n\n /** Live edge threshold in seconds — within this range = \"at live edge\" */\n private static readonly LIVE_EDGE_THRESHOLD = 10;\n\n protected onMediaConnected(): void {\n // Mark as live in state\n this.state.batch({\n isLive: true,\n isAtLiveEdge: true,\n });\n\n // Auto-play on load for live streams\n this.videoElement.addEventListener('canplay', () => {\n this.seekToLiveEdge();\n }, { once: true });\n\n // Monitor live edge position\n this.liveEdgeInterval = setInterval(() => {\n this.checkLiveEdge();\n }, 2000);\n }\n\n protected onMediaDisconnected(): void {\n if (this.liveEdgeInterval) {\n clearInterval(this.liveEdgeInterval);\n this.liveEdgeInterval = null;\n }\n }\n\n /**\n * Seek to the live edge of the stream.\n */\n seekToLiveEdge(): void {\n const duration = this.videoElement.duration;\n if (duration && isFinite(duration)) {\n // For DVR-enabled streams, duration is available\n this.videoElement.currentTime = duration;\n } else if (this.videoElement.seekable.length > 0) {\n // Seek to the end of the seekable range\n const end = this.videoElement.seekable.end(this.videoElement.seekable.length - 1);\n this.videoElement.currentTime = end;\n }\n this.state.set('isAtLiveEdge', true);\n }\n\n /**\n * Check if we're at the live edge by comparing currentTime to seekable end.\n */\n private checkLiveEdge(): void {\n if (!this.videoElement || this.videoElement.paused) return;\n\n const seekable = this.videoElement.seekable;\n if (seekable.length === 0) {\n this.state.set('isAtLiveEdge', true);\n return;\n }\n\n const end = seekable.end(seekable.length - 1);\n const current = this.videoElement.currentTime;\n const behindBy = end - current;\n\n this.state.set('isAtLiveEdge', behindBy <= VzLiveElement.LIVE_EDGE_THRESHOLD);\n }\n}\n\n/** Register the <vz-live> custom element */\nexport function registerLive(): void {\n if (!customElements.get('vz-live')) {\n customElements.define('vz-live', VzLiveElement);\n }\n}\n","/**\n * Playlist prev/next controls injected into the active player's control bar.\n * Also shows an item counter \"2 / 5\".\n */\n\nimport { ICON_PLAYLIST_PREV, ICON_PLAYLIST_NEXT } from './icons.js';\n\nconst ICON_PLAYLIST_LIST = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"8\" y1=\"6\" x2=\"21\" y2=\"6\"/><line x1=\"8\" y1=\"12\" x2=\"21\" y2=\"12\"/><line x1=\"8\" y1=\"18\" x2=\"21\" y2=\"18\"/><line x1=\"3\" y1=\"6\" x2=\"3.01\" y2=\"6\"/><line x1=\"3\" y1=\"12\" x2=\"3.01\" y2=\"12\"/><line x1=\"3\" y1=\"18\" x2=\"3.01\" y2=\"18\"/></svg>`;\n\nexport class PlaylistControls {\n private prevBtn: HTMLButtonElement;\n private nextBtn: HTMLButtonElement;\n private counterEl: HTMLSpanElement;\n private panelBtn: HTMLButtonElement | null = null;\n private injectedInto: HTMLElement;\n private injectedRight: HTMLElement | null;\n\n constructor(\n controlsLeft: HTMLElement,\n controlsRight: HTMLElement | null,\n currentIndex: number,\n totalItems: number,\n onPrev: () => void,\n onNext: () => void,\n onTogglePanel: (() => void) | null,\n ) {\n this.injectedInto = controlsLeft;\n this.injectedRight = controlsRight;\n\n // Previous button\n this.prevBtn = document.createElement('button');\n this.prevBtn.className = 'vz-btn vz-playlist-prev';\n this.prevBtn.innerHTML = ICON_PLAYLIST_PREV;\n this.prevBtn.title = 'Previous';\n this.prevBtn.addEventListener('click', (e) => {\n e.stopPropagation();\n onPrev();\n });\n\n // Next button\n this.nextBtn = document.createElement('button');\n this.nextBtn.className = 'vz-btn vz-playlist-next';\n this.nextBtn.innerHTML = ICON_PLAYLIST_NEXT;\n this.nextBtn.title = 'Next';\n this.nextBtn.addEventListener('click', (e) => {\n e.stopPropagation();\n onNext();\n });\n\n // Counter display: \"2 / 5\"\n this.counterEl = document.createElement('span');\n this.counterEl.className = 'vz-time vz-playlist-counter';\n this.counterEl.textContent = `${currentIndex + 1} / ${totalItems}`;\n\n // Insert after the play button (first child)\n const firstChild = controlsLeft.children[0];\n if (firstChild?.nextSibling) {\n controlsLeft.insertBefore(this.prevBtn, firstChild.nextSibling);\n controlsLeft.insertBefore(this.nextBtn, this.prevBtn.nextSibling);\n controlsLeft.insertBefore(this.counterEl, this.nextBtn.nextSibling);\n } else {\n controlsLeft.appendChild(this.prevBtn);\n controlsLeft.appendChild(this.nextBtn);\n controlsLeft.appendChild(this.counterEl);\n }\n\n // Panel toggle button (right side, only if panel mode is expandable)\n if (onTogglePanel && controlsRight) {\n this.panelBtn = document.createElement('button');\n this.panelBtn.className = 'vz-btn vz-playlist-panel-toggle';\n this.panelBtn.innerHTML = ICON_PLAYLIST_LIST;\n this.panelBtn.title = 'Playlist';\n this.panelBtn.addEventListener('click', (e) => {\n e.stopPropagation();\n onTogglePanel();\n });\n // Insert before the first right-side control\n const firstRight = controlsRight.children[0];\n if (firstRight) {\n controlsRight.insertBefore(this.panelBtn, firstRight);\n } else {\n controlsRight.appendChild(this.panelBtn);\n }\n }\n }\n\n /** Update the counter display */\n update(currentIndex: number, totalItems: number): void {\n this.counterEl.textContent = `${currentIndex + 1} / ${totalItems}`;\n }\n\n /** Remove injected controls */\n dispose(): void {\n this.prevBtn.remove();\n this.nextBtn.remove();\n this.counterEl.remove();\n this.panelBtn?.remove();\n }\n}\n","/**\n * Expandable sidebar panel showing all playlist items.\n * Slides in from the right, overlaid on top of the player.\n */\n\nimport type { PlaylistItem } from '../elements/vz-playlist.js';\n\nexport class PlaylistPanel {\n private panel: HTMLDivElement;\n private listEl: HTMLDivElement;\n private isOpen = false;\n private onSelect: (index: number) => void;\n private shadowRoot: ShadowRoot;\n\n constructor(\n shadowRoot: ShadowRoot,\n items: PlaylistItem[],\n activeIndex: number,\n onSelect: (index: number) => void,\n ) {\n this.shadowRoot = shadowRoot;\n this.onSelect = onSelect;\n\n // Inject panel CSS\n const style = document.createElement('style');\n style.textContent = PANEL_CSS;\n shadowRoot.appendChild(style);\n\n // Create panel container\n this.panel = document.createElement('div');\n this.panel.className = 'vz-playlist-panel';\n\n // Header\n const header = document.createElement('div');\n header.className = 'vz-playlist-panel-header';\n\n const title = document.createElement('span');\n title.className = 'vz-playlist-panel-title';\n title.textContent = 'Playlist';\n\n const closeBtn = document.createElement('button');\n closeBtn.className = 'vz-playlist-panel-close';\n closeBtn.innerHTML = '&times;';\n closeBtn.title = 'Close';\n closeBtn.addEventListener('click', (e) => {\n e.stopPropagation();\n this.close();\n });\n\n header.appendChild(title);\n header.appendChild(closeBtn);\n this.panel.appendChild(header);\n\n // List\n this.listEl = document.createElement('div');\n this.listEl.className = 'vz-playlist-panel-list';\n this.panel.appendChild(this.listEl);\n\n // Render items\n this.renderItems(items, activeIndex);\n\n // Add to shadow DOM\n shadowRoot.appendChild(this.panel);\n\n // Prevent pointer events from reaching the player container\n this.panel.addEventListener('pointerdown', (e) => e.stopPropagation());\n this.panel.addEventListener('pointermove', (e) => e.stopPropagation());\n }\n\n /** Update items and active index */\n update(items: PlaylistItem[], activeIndex: number): void {\n this.renderItems(items, activeIndex);\n }\n\n /** Set the active item highlight */\n setActive(index: number): void {\n const children = this.listEl.children;\n for (let i = 0; i < children.length; i++) {\n children[i]?.classList.toggle('active', i === index);\n }\n }\n\n /** Toggle panel open/closed */\n toggle(): void {\n if (this.isOpen) {\n this.close();\n } else {\n this.open();\n }\n }\n\n /** Open the panel */\n open(): void {\n this.panel.classList.add('open');\n this.isOpen = true;\n }\n\n /** Close the panel */\n close(): void {\n this.panel.classList.remove('open');\n this.isOpen = false;\n }\n\n /** Clean up */\n dispose(): void {\n this.panel.remove();\n }\n\n private renderItems(items: PlaylistItem[], activeIndex: number): void {\n this.listEl.innerHTML = '';\n\n for (let i = 0; i < items.length; i++) {\n const item = items[i]!;\n const row = document.createElement('div');\n row.className = 'vz-playlist-panel-item';\n if (i === activeIndex) row.classList.add('active');\n\n // Index number\n const num = document.createElement('span');\n num.className = 'vz-playlist-panel-num';\n num.textContent = String(i + 1);\n\n // Thumbnail placeholder (uses poster or format icon)\n const thumb = document.createElement('div');\n thumb.className = 'vz-playlist-panel-thumb';\n if (item.poster) {\n thumb.style.backgroundImage = `url(${item.poster})`;\n } else {\n // Show format text if no poster\n thumb.textContent = item.format ? item.format.replace('MONO_', '').replace('STEREO_', 'S:') : '360';\n }\n\n // Title\n const title = document.createElement('span');\n title.className = 'vz-playlist-panel-item-title';\n title.textContent = item.title;\n\n row.appendChild(num);\n row.appendChild(thumb);\n row.appendChild(title);\n\n row.addEventListener('click', (e) => {\n e.stopPropagation();\n this.onSelect(i);\n });\n\n this.listEl.appendChild(row);\n }\n }\n}\n\nconst PANEL_CSS = `\n .vz-playlist-panel {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n width: 280px;\n max-width: 70%;\n background: rgba(15, 15, 25, 0.95);\n backdrop-filter: blur(16px);\n -webkit-backdrop-filter: blur(16px);\n z-index: 25;\n transform: translateX(100%);\n transition: transform 0.3s ease;\n display: flex;\n flex-direction: column;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n color: #fff;\n user-select: none;\n -webkit-user-select: none;\n }\n\n .vz-playlist-panel.open {\n transform: translateX(0);\n }\n\n .vz-playlist-panel-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 16px;\n border-bottom: 1px solid rgba(255, 255, 255, 0.1);\n flex-shrink: 0;\n }\n\n .vz-playlist-panel-title {\n font-size: 14px;\n font-weight: 600;\n letter-spacing: 0.02em;\n }\n\n .vz-playlist-panel-close {\n background: none;\n border: none;\n color: rgba(255, 255, 255, 0.6);\n font-size: 22px;\n cursor: pointer;\n padding: 0 4px;\n line-height: 1;\n transition: color 0.15s;\n }\n\n .vz-playlist-panel-close:hover {\n color: #fff;\n }\n\n .vz-playlist-panel-list {\n flex: 1;\n overflow-y: auto;\n padding: 4px 0;\n }\n\n .vz-playlist-panel-list::-webkit-scrollbar {\n width: 4px;\n }\n\n .vz-playlist-panel-list::-webkit-scrollbar-track {\n background: transparent;\n }\n\n .vz-playlist-panel-list::-webkit-scrollbar-thumb {\n background: rgba(255, 255, 255, 0.15);\n border-radius: 2px;\n }\n\n .vz-playlist-panel-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px 16px;\n cursor: pointer;\n transition: background 0.12s;\n }\n\n .vz-playlist-panel-item:hover {\n background: rgba(255, 255, 255, 0.08);\n }\n\n .vz-playlist-panel-item.active {\n background: rgba(255, 255, 255, 0.12);\n }\n\n .vz-playlist-panel-item.active .vz-playlist-panel-num {\n color: var(--vz-primary, #f43f5e);\n }\n\n .vz-playlist-panel-num {\n width: 20px;\n text-align: center;\n font-size: 12px;\n color: rgba(255, 255, 255, 0.4);\n font-variant-numeric: tabular-nums;\n flex-shrink: 0;\n }\n\n .vz-playlist-panel-thumb {\n width: 48px;\n height: 28px;\n border-radius: 3px;\n background: rgba(255, 255, 255, 0.06);\n background-size: cover;\n background-position: center;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 9px;\n color: rgba(255, 255, 255, 0.3);\n letter-spacing: 0.05em;\n text-transform: uppercase;\n flex-shrink: 0;\n overflow: hidden;\n }\n\n .vz-playlist-panel-item-title {\n font-size: 13px;\n color: rgba(255, 255, 255, 0.85);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n min-width: 0;\n }\n\n .vz-playlist-panel-item.active .vz-playlist-panel-item-title {\n color: #fff;\n font-weight: 500;\n }\n`;\n","/**\n * <vz-playlist> - Container element managing a queue of <vz-video> players.\n *\n * Only one child video is active (visible/playing) at a time.\n * Auto-advances when the current video ends.\n *\n * @example\n * ```html\n * <vz-playlist autoplay loop-playlist>\n * <vz-video title=\"Video 1\" format=\"MONO_360\">\n * <source src=\"video1.mp4\" type=\"video/mp4\" />\n * </vz-video>\n * <vz-video title=\"Video 2\" format=\"MONO_FLAT\">\n * <source src=\"video2.mp4\" type=\"video/mp4\" />\n * </vz-video>\n * </vz-playlist>\n *\n * <!-- With expandable sidebar -->\n * <vz-playlist panel=\"expandable\" autoplay>\n * ...\n * </vz-playlist>\n * ```\n */\n\nimport { PlaylistControls } from '../controls/playlist-controls.js';\nimport { PlaylistPanel } from '../controls/playlist-panel.js';\nimport { HTMLElementBase } from '../core/html-element.js';\n\nexport interface PlaylistItem {\n index: number;\n element: HTMLElement;\n title: string;\n format: string;\n poster: string;\n}\n\nexport class VzPlaylistElement extends HTMLElementBase {\n private shadow: ShadowRoot;\n private items: PlaylistItem[] = [];\n private currentIndex = -1;\n private playlistControls: PlaylistControls | null = null;\n private playlistPanel: PlaylistPanel | null = null;\n private endedHandler: (() => void) | null = null;\n private connected = false;\n\n constructor() {\n super();\n this.shadow = this.attachShadow({ mode: 'open' });\n }\n\n static get observedAttributes(): string[] {\n return ['autoplay', 'loop-playlist', 'panel'];\n }\n\n // --- Attribute accessors ---\n\n get autoplay(): boolean {\n return this.hasAttribute('autoplay');\n }\n\n set autoplay(val: boolean) {\n if (val) {\n this.setAttribute('autoplay', '');\n } else {\n this.removeAttribute('autoplay');\n }\n }\n\n get loopPlaylist(): boolean {\n return this.hasAttribute('loop-playlist');\n }\n\n set loopPlaylist(val: boolean) {\n if (val) {\n this.setAttribute('loop-playlist', '');\n } else {\n this.removeAttribute('loop-playlist');\n }\n }\n\n get panelMode(): 'none' | 'expandable' {\n const raw = this.getAttribute('panel');\n return raw === 'expandable' ? 'expandable' : 'none';\n }\n\n connectedCallback(): void {\n this.connected = true;\n\n // Apply host styling\n this.style.display = 'block';\n this.style.position = 'relative';\n this.style.overflow = 'hidden';\n if (!this.style.width) this.style.width = '100%';\n\n // Inject styles\n this.injectStyles();\n\n // Create wrapper\n const wrapper = document.createElement('div');\n wrapper.className = 'vz-playlist-wrapper';\n this.shadow.appendChild(wrapper);\n\n // Slot for light DOM children\n const slot = document.createElement('slot');\n wrapper.appendChild(slot);\n\n // Parse children\n this.parseItems();\n\n // Create playlist panel if configured\n if (this.panelMode === 'expandable') {\n this.playlistPanel = new PlaylistPanel(\n this.shadow,\n this.items,\n this.currentIndex,\n (index) => this.goTo(index),\n );\n }\n\n // Observe slot changes for dynamic children\n slot.addEventListener('slotchange', () => {\n this.parseItems();\n if (this.currentIndex < 0 && this.items.length > 0) {\n this.goTo(0);\n }\n this.playlistPanel?.update(this.items, this.currentIndex);\n });\n\n // Show first item\n if (this.items.length > 0) {\n this.goTo(0);\n }\n }\n\n disconnectedCallback(): void {\n this.connected = false;\n this.detachEndedListener();\n this.playlistControls?.dispose();\n this.playlistControls = null;\n this.playlistPanel?.dispose();\n this.playlistPanel = null;\n }\n\n attributeChangedCallback(_name: string, _oldVal: string | null, _newVal: string | null): void {\n // Panel change would require re-render (unusual at runtime, ignore for now)\n }\n\n // --- Public API ---\n\n /** Navigate to item at index */\n goTo(index: number): void {\n if (index < 0 || index >= this.items.length) return;\n\n const prevIndex = this.currentIndex;\n this.currentIndex = index;\n\n // Hide all items, show current\n for (let i = 0; i < this.items.length; i++) {\n const el = this.items[i]!.element;\n if (i === index) {\n el.style.display = '';\n el.removeAttribute('hidden');\n } else {\n el.style.display = 'none';\n el.setAttribute('hidden', '');\n // Pause hidden videos\n if (typeof (el as any).pause === 'function') {\n (el as any).pause();\n }\n }\n }\n\n // Attach ended listener to the new active element\n this.detachEndedListener();\n this.attachEndedListener();\n\n // Inject/update playlist controls into the active player\n this.injectControls();\n\n // Update panel highlight\n this.playlistPanel?.setActive(index);\n\n // Dispatch change event\n const item = this.items[index]!;\n this.dispatchEvent(\n new CustomEvent('vz-playlist-change', {\n bubbles: true,\n composed: true,\n detail: {\n index,\n title: item.title,\n total: this.items.length,\n },\n }),\n );\n\n // Auto-play if configured and not the initial load\n if (this.autoplay && prevIndex >= 0) {\n const activeEl = this.items[index]!.element;\n if (typeof (activeEl as any).play === 'function') {\n // Small delay to let the element connect/render\n requestAnimationFrame(() => {\n (activeEl as any).play().catch(() => {\n // Autoplay may be blocked by browser policy\n });\n });\n }\n }\n }\n\n /** Go to next item */\n next(): void {\n if (this.currentIndex < this.items.length - 1) {\n this.goTo(this.currentIndex + 1);\n } else if (this.loopPlaylist) {\n this.goTo(0);\n }\n }\n\n /** Go to previous item */\n previous(): void {\n if (this.currentIndex > 0) {\n this.goTo(this.currentIndex - 1);\n } else if (this.loopPlaylist) {\n this.goTo(this.items.length - 1);\n }\n }\n\n /** Shuffle the playlist order */\n shuffle(): void {\n // Fisher-Yates shuffle on the items array\n for (let i = this.items.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [this.items[i], this.items[j]] = [this.items[j]!, this.items[i]!];\n this.items[i]!.index = i;\n this.items[j]!.index = j;\n }\n\n // Update panel if exists\n this.playlistPanel?.update(this.items, this.currentIndex);\n\n // Find the new index of the currently active element\n const activeEl = this.currentIndex >= 0 ? this.items.find(\n (item) => item.element.style.display !== 'none',\n ) : null;\n if (activeEl) {\n this.currentIndex = this.items.indexOf(activeEl);\n }\n }\n\n /** Get current item index */\n get currentItemIndex(): number {\n return this.currentIndex;\n }\n\n /** Get total item count */\n get itemCount(): number {\n return this.items.length;\n }\n\n /** Get the active player element */\n get activeElement(): HTMLElement | null {\n return this.currentIndex >= 0 ? this.items[this.currentIndex]?.element ?? null : null;\n }\n\n /** Toggle the playlist panel open/closed */\n togglePanel(): void {\n this.playlistPanel?.toggle();\n }\n\n // --- Private ---\n\n private parseItems(): void {\n this.items = [];\n const children = this.querySelectorAll('vz-video, vz-cinema, vz-live');\n let i = 0;\n for (const child of children) {\n this.items.push({\n index: i,\n element: child as HTMLElement,\n title: child.getAttribute('title') ?? `Item ${i + 1}`,\n format: child.getAttribute('format') ?? '',\n poster: child.getAttribute('poster') ?? '',\n });\n i++;\n }\n }\n\n private attachEndedListener(): void {\n if (this.currentIndex < 0) return;\n const activeEl = this.items[this.currentIndex]?.element;\n if (!activeEl) return;\n\n this.endedHandler = () => {\n if (this.currentIndex < this.items.length - 1) {\n this.next();\n } else if (this.loopPlaylist) {\n this.goTo(0);\n } else {\n // Playlist ended\n this.dispatchEvent(\n new CustomEvent('vz-playlist-end', {\n bubbles: true,\n composed: true,\n }),\n );\n }\n };\n\n activeEl.addEventListener('ended', this.endedHandler);\n }\n\n private detachEndedListener(): void {\n if (this.endedHandler && this.currentIndex >= 0) {\n const el = this.items[this.currentIndex]?.element;\n el?.removeEventListener('ended', this.endedHandler);\n }\n this.endedHandler = null;\n }\n\n private injectControls(): void {\n // Clean up previous controls\n this.playlistControls?.dispose();\n\n if (this.items.length <= 1) {\n this.playlistControls = null;\n return;\n }\n\n const activeEl = this.items[this.currentIndex]?.element;\n if (!activeEl) return;\n\n // Wait for the element to be ready before injecting controls\n const inject = () => {\n // Access the player's shadow root to find the controls overlay\n const shadow = activeEl.shadowRoot;\n if (!shadow) return;\n\n const controlsLeft = shadow.querySelector('.vz-controls-left');\n const controlsRight = shadow.querySelector('.vz-controls-right');\n if (!controlsLeft) return;\n\n this.playlistControls = new PlaylistControls(\n controlsLeft as HTMLElement,\n controlsRight as HTMLElement | null,\n this.currentIndex,\n this.items.length,\n () => this.previous(),\n () => this.next(),\n this.panelMode === 'expandable' ? () => this.togglePanel() : null,\n );\n };\n\n // The player may not have its shadow DOM ready yet\n if (activeEl.shadowRoot?.querySelector('.vz-controls-left')) {\n inject();\n } else {\n // Wait for vz-ready event\n activeEl.addEventListener('vz-ready', () => inject(), { once: true });\n }\n }\n\n private injectStyles(): void {\n const style = document.createElement('style');\n style.textContent = `\n :host {\n display: block;\n position: relative;\n overflow: hidden;\n }\n\n .vz-playlist-wrapper {\n position: relative;\n width: 100%;\n height: 100%;\n }\n\n ::slotted(vz-video),\n ::slotted(vz-cinema),\n ::slotted(vz-live) {\n width: 100% !important;\n }\n\n ::slotted([hidden]) {\n display: none !important;\n }\n `;\n this.shadow.appendChild(style);\n }\n}\n\n/** Register the <vz-playlist> custom element */\nexport function registerPlaylist(): void {\n if (!customElements.get('vz-playlist')) {\n customElements.define('vz-playlist', VzPlaylistElement);\n }\n}\n","import type { AnnotationData } from '@vizor-vr/shared';\nimport { sanitizeAnnotationHtml } from '../utils/sanitize.js';\nimport { HTMLElementBase } from '../core/html-element.js';\n\n/**\n * <vz-annotation> — child element of vz-video/vz-img for interactive annotations.\n *\n * This is a data-holder element with no shadow DOM. The parent media element's\n * AnnotationManager reads these elements to create 3D sprite markers.\n *\n * @example\n * ```html\n * <vz-video format=\"MONO_360\">\n * <source src=\"video.mp4\" type=\"video/mp4\" />\n * <vz-annotation lat=\"10\" lon=\"45\" time-start=\"5\" time-end=\"15\" title=\"Look here\">\n * <p>Rich HTML content</p>\n * </vz-annotation>\n * </vz-video>\n * ```\n */\nexport class VzAnnotationElement extends HTMLElementBase {\n static get observedAttributes(): string[] {\n return ['lat', 'lon', 'time-start', 'time-end', 'title', 'icon', 'sort-order'];\n }\n\n /** Parse this element's attributes into an AnnotationData object */\n getData(): AnnotationData {\n const lat = this.hasAttribute('lat') ? parseFloat(this.getAttribute('lat')!) : 0;\n const lon = this.hasAttribute('lon') ? parseFloat(this.getAttribute('lon')!) : 0;\n const title = this.getAttribute('title') ?? '';\n const icon = this.getAttribute('icon') ?? undefined;\n const timeStart = this.hasAttribute('time-start')\n ? parseFloat(this.getAttribute('time-start')!)\n : undefined;\n const timeEnd = this.hasAttribute('time-end')\n ? parseFloat(this.getAttribute('time-end')!)\n : undefined;\n const sortOrder = this.hasAttribute('sort-order')\n ? parseInt(this.getAttribute('sort-order')!, 10)\n : undefined;\n\n // Use innerHTML as rich content, sanitized at the source with DOMPurify so\n // stored htmlContent is never an XSS vector regardless of which consumer\n // reads it (the render sink sanitizes again as defense in depth).\n const rawHtml = this.innerHTML.trim();\n const htmlContent = rawHtml ? sanitizeAnnotationHtml(rawHtml) : undefined;\n\n return { lat, lon, timeStart, timeEnd, title, icon, htmlContent, sortOrder };\n }\n}\n\n/** Register the <vz-annotation> custom element */\nexport function registerAnnotation(): void {\n if (!customElements.get('vz-annotation')) {\n customElements.define('vz-annotation', VzAnnotationElement);\n }\n}\n","import { registerVideo } from './elements/vz-video.js';\nimport { registerImg } from './elements/vz-img.js';\nimport { registerTour } from './elements/vz-tour.js';\nimport { registerTourImg } from './elements/vz-tour-img.js';\nimport { registerTourPortal } from './elements/vz-tour-portal.js';\nimport { registerCinema } from './elements/vz-cinema.js';\nimport { registerPlaylist } from './elements/vz-playlist.js';\nimport { registerLive } from './elements/vz-live.js';\nimport { registerAnnotation } from './elements/vz-annotation.js';\n\n/**\n * Auto-register all Vizor custom elements.\n * Used by the CDN script tag build.\n */\nexport function registerAll(): void {\n // No-op outside the browser (SSR/Node) — custom elements need a DOM.\n if (typeof customElements === 'undefined') return;\n registerVideo();\n registerImg();\n registerTourPortal();\n registerTourImg();\n registerTour();\n registerCinema();\n registerPlaylist();\n registerLive();\n registerAnnotation();\n}\n\n// Auto-register when loaded via script tag\nregisterAll();\n"],"names":["hash","FACE_MAP","COLS","ROWS"],"mappings":";;;;;AAWO,MAAM,kBACX,OAAO,gBAAgB,cACnB,cACC,MAAM;AAAC;AC8Cd,MAAM,gBAAkC;AAAA,EACtC,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,UAAU;AAAA,EACV,UAAU;AAAA,EACV,SAAS;AAAA,EACT,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,cAAc;AAAA,EACd,SAAS;AAAA,EACT,cAAc;AAAA,EACd,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,oBAAoB,CAAA;AAAA,EACpB,aAAa;AAAA,EACb,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,aAAa;AAAA,EACb,UAAU;AAAA,EACV,iBAAiB;AAAA,EACjB,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR,cAAc;AAAA,EACd,SAAS;AAAA,EACT,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,OAAO;AACT;AAMO,MAAM,YAAY;AAAA,EACf;AAAA,EACA,gCAAgB,IAAA;AAAA,EAChB,sCAAsB,IAAA;AAAA,EAE9B,YAAY,SAAqC;AAC/C,SAAK,QAAQ,EAAE,GAAG,eAAe,GAAG,QAAA;AAAA,EACtC;AAAA;AAAA,EAGA,IAAsC,KAA6B;AACjE,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB;AAAA;AAAA,EAGA,cAA0C;AACxC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,IAAsC,KAAQ,OAAkC;AAC9E,UAAM,OAAO,KAAK,MAAM,GAAG;AAC3B,QAAI,OAAO,GAAG,MAAM,KAAK,EAAG;AAC5B,SAAK,MAAM,GAAG,IAAI;AAClB,SAAK,OAAO,KAAK,OAAO,IAAI;AAAA,EAC9B;AAAA;AAAA,EAGA,MAAM,SAA0C;AAC9C,UAAM,UAA6D,CAAA;AACnE,eAAW,OAAO,OAAO,KAAK,OAAO,GAAoC;AACvE,YAAM,QAAQ,QAAQ,GAAG;AACzB,YAAM,OAAO,KAAK,MAAM,GAAG;AAC3B,UAAI,CAAC,OAAO,GAAG,MAAM,KAAK,GAAG;AAC1B,aAAK,MAA6C,GAAG,IAAI;AAC1D,gBAAQ,KAAK,CAAC,KAAK,OAAO,IAAI,CAAC;AAAA,MACjC;AAAA,IACF;AACA,eAAW,CAAC,KAAK,OAAO,IAAI,KAAK,SAAS;AACxC,WAAK,OAAO,KAAK,OAAO,IAAI;AAAA,IAC9B;AAAA,EACF;AAAA;AAAA,EAGA,UACE,KACA,UACY;AACZ,QAAI,CAAC,KAAK,UAAU,IAAI,GAAG,GAAG;AAC5B,WAAK,UAAU,IAAI,KAAK,oBAAI,KAAK;AAAA,IACnC;AACA,UAAM,gBAAgB;AACtB,SAAK,UAAU,IAAI,GAAG,EAAG,IAAI,aAAa;AAC1C,WAAO,MAAM;AACX,WAAK,UAAU,IAAI,GAAG,GAAG,OAAO,aAAa;AAAA,IAC/C;AAAA,EACF;AAAA;AAAA,EAGA,aAAa,UAAmC;AAC9C,SAAK,gBAAgB,IAAI,QAAQ;AACjC,WAAO,MAAM;AACX,WAAK,gBAAgB,OAAO,QAAQ;AAAA,IACtC;AAAA,EACF;AAAA;AAAA,EAGA,QAAc;AACZ,UAAM,WAAW,EAAE,GAAG,KAAK,MAAA;AAC3B,SAAK,QAAQ,EAAE,GAAG,cAAA;AAClB,eAAW,OAAO,OAAO,KAAK,QAAQ,GAAoC;AACxE,UAAI,CAAC,OAAO,GAAG,SAAS,GAAG,GAAG,KAAK,MAAM,GAAG,CAAC,GAAG;AAC9C,aAAK,OAAO,KAAK,KAAK,MAAM,GAAG,GAAG,SAAS,GAAG,CAAC;AAAA,MACjD;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,OAAO,KAA6B,OAAgB,MAAqB;AAC/E,SAAK,UAAU,IAAI,GAAG,GAAG,QAAQ,CAAC,OAAO,GAAG,OAAO,IAAI,CAAC;AACxD,SAAK,gBAAgB,QAAQ,CAAC,OAAO,GAAG,KAAe,OAAO,IAAI,CAAC;AAAA,EACrE;AACF;AC1KO,MAAM,eAAe;AAAA,EAClB;AAAA,EACA,+BAAe,IAAA;AAAA,EAEvB,YAAY,SAAsB;AAChC,SAAK,UAAU;AAAA,EACjB;AAAA;AAAA,EAGA,UAAU,MAAoB;AAC5B,UAAM,QAAQ,IAAI,MAAM,MAAM,EAAE,SAAS,MAAM,UAAU,MAAM;AAC/D,SAAK,QAAQ,cAAc,KAAK;AAAA,EAClC;AAAA;AAAA,EAGA,KACE,MACA,QACM;AACN,UAAM,QAAQ,IAAI,YAAY,MAAM;AAAA,MAClC,SAAS;AAAA,MACT,UAAU;AAAA,MACV;AAAA,IAAA,CACD;AACD,SAAK,QAAQ,cAAc,KAAK;AAAA,EAClC;AAAA;AAAA,EAGA,GAAG,MAAwB,UAAqC;AAC9D,QAAI,CAAC,KAAK,SAAS,IAAI,IAAI,GAAG;AAC5B,WAAK,SAAS,IAAI,MAAM,oBAAI,KAAK;AAAA,IACnC;AACA,SAAK,SAAS,IAAI,IAAI,EAAG,IAAI,QAAQ;AACrC,SAAK,QAAQ,iBAAiB,MAAM,QAAyB;AAC7D,WAAO,MAAM,KAAK,IAAI,MAAM,QAAQ;AAAA,EACtC;AAAA;AAAA,EAGA,IAAI,MAAwB,UAA+B;AACzD,SAAK,SAAS,IAAI,IAAI,GAAG,OAAO,QAAQ;AACxC,SAAK,QAAQ,oBAAoB,MAAM,QAAyB;AAAA,EAClE;AAAA;AAAA,EAGA,YAAkB;AAChB,eAAW,CAAC,MAAM,SAAS,KAAK,KAAK,UAAU;AAC7C,iBAAW,YAAY,WAAW;AAChC,aAAK,QAAQ,oBAAoB,MAAM,QAAyB;AAAA,MAClE;AAAA,IACF;AACA,SAAK,SAAS,MAAA;AAAA,EAChB;AACF;ACxDA,MAAM,WAA+C;AAAA,EACnD,WAAW;AAAA,EACX,eAAe;AAAA,EACf,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,mBAAmB;AAAA,EACnB,cAAc;AAAA,EACd,cAAc;AAAA,EACd,aAAa;AAAA,EACb,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,kBAAkB;AAAA,EAClB,iBAAiB;AAAA,EACjB,mBAAmB;AAAA,EACnB,iBAAiB;AAAA,EACjB,cAAc;AAAA,EACd,mBAAmB;AAAA,EACnB,kBAAkB;AACpB;AAGA,MAAM,oCAAoB,IAAI;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAMM,SAAS,iBAAwC;AACtD,QAAM,SAA2C,CAAA;AAEjD,QAAM,QAAQ,SAAS,iBAAiB,mBAAmB;AAC3D,QAAM,QAAQ,CAAC,SAAS;AACtB,UAAM,UAAU,KAAK,aAAa,MAAM;AACxC,QAAI,CAAC,QAAS;AAEd,UAAM,UAAU,QAAQ,QAAQ,OAAO,EAAE;AACzC,UAAM,YAAY,SAAS,OAAO;AAClC,QAAI,CAAC,UAAW;AAEhB,UAAM,UAAU,KAAK,aAAa,SAAS,KAAK;AAEhD,QAAI,cAAc,IAAI,SAAS,GAAG;AAChC,aAAO,SAAS,IAAI,YAAY,MAAM,YAAY;AAAA,IACpD,OAAO;AACL,aAAO,SAAS,IAAI;AAAA,IACtB;AAAA,EACF,CAAC;AAED,SAAO;AACT;AAQO,SAAS,cACd,cACA,eACc;AACd,QAAM,OAAO,iBAAiB,eAAA;AAC9B,SAAO;AAAA,IACL,GAAG;AAAA,IACH,GAAG;AAAA,IACH,GAAG;AAAA,EAAA;AAEP;AC5DO,SAAS,eAAe,YAA0C;AACvE,MAAI;AAEF,UAAM,eAAe,OAA+C,KAAwB;AAC5F,QAAI,CAAC,aAAc,QAAO;AAG1B,UAAM,SAAS,WAAW,QAAQ,MAAM,GAAG,EAAE,QAAQ,MAAM,GAAG;AAC9D,UAAM,UAAU,KAAK,MAAM;AAC3B,UAAM,WAAW,KAAK,MAAM,OAAO;AACnC,UAAM,UAAU,KAAK,MAAM,SAAS,OAAO;AAQ3C,QAAI,QAAQ,SAAS,SAAU,QAAO;AACtC,QAAI,QAAQ,cAAc,aAAc,QAAO;AAG/C,WAAO;AAAA,MACL,OAAO;AAAA,MACP,MAAM;AAAA,MACN,UAAU,mBAAmB,YAAY;AAAA,MACzC,SAAS,QAAQ;AAAA,MACjB,WAAW,QAAQ;AAAA,IAAA;AAAA,EAEvB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;ACvCA,MAAM,uBAAuB;AAS7B,eAAsB,aAAa,QAA+C;AAChF,QAAM,OAAO,OAAO,gBAAgB,OAAO,aAAa,eAAe;AAEvE,MAAI,SAAS,gBAAgB,OAAO,YAAY;AAE9C,UAAM,eAAe,eAAe,OAAO,UAAU;AACrD,QAAI,aAAc,QAAO;AAEzB,WAAO,uBAAuB,OAAO,YAAY,OAAO,aAAa,OAAO,aAAa;AAAA,EAC3F;AAEA,MAAI,SAAS,UAAU,OAAO,QAAQ;AACpC,WAAO,iBAAiB,OAAO,QAAQ,OAAO,WAAW;AAAA,EAC3D;AAGA,SAAO,eAAe,yBAAyB;AACjD;AAEA,eAAe,iBAAiB,QAAgB,aAA8C;AAC5F,QAAM,WAAW,eAAe;AAChC,QAAM,SAAS,OAAO,WAAW,cAAc,OAAO,SAAS,WAAW;AAE1E,MAAI;AACF,UAAM,MAAM,MAAM,MAAM,GAAG,QAAQ,4BAA4B;AAAA,MAC7D,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAA;AAAA,MAC3B,MAAM,KAAK,UAAU,EAAE,QAAQ,QAAQ;AAAA,IAAA,CACxC;AAED,QAAI,CAAC,IAAI,IAAI;AACX,cAAQ,KAAK,sCAAsC,IAAI,MAAM;AAC7D,aAAO,eAAe,gBAAgB,IAAI,MAAM,EAAE;AAAA,IACpD;AAEA,UAAM,SAAwB,MAAM,IAAI,KAAA;AACxC,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,YAAQ,KAAK,6CAA6C,GAAG;AAC7D,WAAO,eAAe,yCAAyC;AAAA,EACjE;AACF;AASA,MAAM,uBAAuB;AAE7B,eAAe,uBACb,YACA,aACA,eACwB;AACxB,MAAI;AAEF,UAAM,UAAU,KAAK,WAAW,QAAQ,MAAM,GAAG,EAAE,QAAQ,MAAM,GAAG,CAAC;AACrE,UAAM,WAAW,KAAK,MAAM,OAAO;AAKnC,UAAM,UAA6B,KAAK,MAAM,SAAS,OAAO;AAG9D,QAAI;AAEF,YAAM,KAAK,MAAM,OAAO,gBAAgB;AACxC,YAAM,WAAW,IAAI,YAAA,EAAc,OAAO,SAAS,OAAO;AAC1D,YAAM,WAAW,WAAW,SAAS,SAAS;AAC9C,YAAM,WAAW,WAAW,QAAQ,SAAS;AAC7C,YAAM,QAAQ,MAAM,GAAG,YAAY,UAAU,UAAU,QAAQ;AAC/D,UAAI,CAAC,OAAO;AACV,eAAO,eAAe,2BAA2B;AAAA,MACnD;AAAA,IACF,QAAQ;AACN,cAAQ,KAAK,oEAAoE;AAAA,IACnF;AAGA,QAAI,QAAQ,WAAW;AACrB,YAAM,UAAU,IAAI,KAAK,QAAQ,SAAS;AAC1C,UAAI,UAAU,oBAAI,QAAQ;AACxB,eAAO,eAAe,iBAAiB;AAAA,MACzC;AAAA,IACF;AAGA,UAAM,gBAAgB,OAAO,WAAW,cAAc,OAAO,SAAS,WAAW;AACjF,QAAI,QAAQ,QAAQ,SAAS,KAAK,CAAC,cAAc,eAAe,QAAQ,OAAO,GAAG;AAChF,aAAO,eAAe,uBAAuB;AAAA,IAC/C;AAGA,UAAM,OAAO,QAAQ;AACrB,UAAM,cAA6B;AAAA,MACjC,OAAO;AAAA,MACP;AAAA,MACA,UAAU,mBAAmB,IAAI;AAAA,MACjC,SAAS,QAAQ;AAAA,MACjB,WAAW,QAAQ;AAAA,IAAA;AAIrB,QAAI,CAAC,iBAAiB,OAAO,WAAW,aAAa;AACnD,gBAAU,YAAY,aAAa,WAAW;AAAA,IAChD;AAGA,UAAM,SAAS,gBAAA;AACf,QAAI,QAAQ;AACV,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,YAAQ,KAAK,6CAA6C,GAAG;AAC7D,WAAO,eAAe,4BAA4B;AAAA,EACpD;AACF;AAMA,SAAS,UAAU,YAAoB,aAAsB,UAAgC;AAE3F,QAAM,aAAa;AACnB,MAAI,OAAO,mBAAmB,aAAa;AACzC,QAAI;AACF,UAAI,eAAe,QAAQ,UAAU,EAAG;AACxC,qBAAe,QAAQ,YAAY,GAAG;AAAA,IACxC,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,QAAM,WAAW,eAAe;AAChC,QAAM,SAAS,OAAO,WAAW,cAAc,OAAO,SAAS,WAAW;AAE1E,QAAM,GAAG,QAAQ,uCAAuC;AAAA,IACtD,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,mBAAA;AAAA,IAC3B,MAAM,KAAK,UAAU,EAAE,YAAY,QAAQ;AAAA,EAAA,CAC5C,EACE,KAAK,CAAC,QAAQ;AACb,QAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,UAAU,IAAI,MAAM,EAAE;AACnD,WAAO,IAAI,KAAA;AAAA,EACb,CAAC,EACA,KAAK,CAAC,iBAAgC;AAErC,QAAI;AACF,mBAAa;AAAA,QACX;AAAA,QACA,KAAK,UAAU,EAAE,QAAQ,cAAc,IAAI,KAAK,MAAI,CAAG;AAAA,MAAA;AAAA,IAE3D,QAAQ;AAAA,IAER;AAAA,EACF,CAAC,EACA,MAAM,MAAM;AAEX,YAAQ,MAAM,uDAAuD;AAAA,EACvE,CAAC;AACL;AAMA,SAAS,kBAAwC;AAC/C,MAAI;AACF,UAAM,MAAM,aAAa,QAAQ,oBAAoB;AACrD,QAAI,CAAC,IAAK,QAAO;AACjB,UAAM,EAAE,QAAQ,GAAA,IAAO,KAAK,MAAM,GAAG;AAErC,QAAI,KAAK,QAAQ,KAAK,KAAK,KAAK,KAAK,KAAM;AACzC,mBAAa,WAAW,oBAAoB;AAC5C,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,eAAe,SAAgC;AACtD,SAAO;AAAA,IACL,OAAO;AAAA,IACP,MAAM;AAAA,IACN,UAAU,mBAAmB,MAAM;AAAA,IACnC;AAAA,EAAA;AAEJ;AAEA,SAAS,cAAc,UAAkB,gBAAmC;AAC1E,SAAO,eAAe,KAAK,CAAC,YAAY;AACtC,QAAI,QAAQ,WAAW,IAAI,GAAG;AAC5B,YAAM,SAAS,QAAQ,MAAM,CAAC;AAC9B,aAAO,SAAS,SAAS,MAAM,KAAK,aAAa,QAAQ,MAAM,CAAC;AAAA,IAClE;AACA,WAAO,aAAa;AAAA,EACtB,CAAC;AACH;AAEA,SAAS,WAAW,KAAyB;AAC3C,QAAM,QAAQ,IAAI,WAAW,IAAI,SAAS,CAAC;AAC3C,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAM,CAAC,IAAI,SAAS,IAAI,UAAU,IAAI,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE;AAAA,EACzD;AACA,SAAO;AACT;AChOA,IAAI,WAAoC;AACxC,IAAI,cAAc;AAGlB,MAAM,yBAAyB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoCxB,SAAS,mBACd,YACA,UAAkB,oBAClB,SACM;AACN,MAAI,YAAa;AACjB,gBAAc;AAGd,QAAM,aAAa,SAAS,cAAc,OAAO;AACjD,aAAW,aAAa,iBAAiB,GAAG;AAC5C,aAAW,cAAc;AACzB,aAAW,YAAY,UAAU;AAGjC,QAAM,SAAS,kBAAkB,gBAAgB,SAAS,OAAO;AACjE,SAAO,MAAM,UAAU;AACvB,aAAW,YAAY,MAAM;AAE7B,aAAW,IAAI,iBAAiB,CAAC,cAAc;AAC7C,eAAW,YAAY,WAAW;AAChC,UAAI,SAAS,SAAS,YAAa;AAGnC,YAAM,UAAU,WAAW,cAAc,eAAe;AACxD,UAAI,CAAC,SAAS;AAEZ,cAAM,QAAQ,MAAM,KAAK,OAAA,IAAW;AACpC,mBAAW,MAAM;AACf,cAAI,CAAC,WAAW,cAAc,eAAe,GAAG;AAC9C,kBAAM,eAAe,kBAAkB,gBAAgB,SAAS,OAAO;AACvE,uBAAW,YAAY,YAAY;AAAA,UACrC;AAAA,QACF,GAAG,KAAK;AAGR,cAAM,WAAW,WAAW,cAAc,eAAe;AACzD,YAAI,SAAU,UAAS,MAAM,UAAU;AAAA,MACzC;AAGA,YAAM,cAAc,WAAW,cAAc,eAAe;AAC5D,UAAI,CAAC,aAAa;AAChB,cAAM,YAAY,kBAAkB,gBAAgB,SAAS,OAAO;AACpE,mBAAW,YAAY,SAAS;AAAA,MAClC;AAGA,YAAM,kBAAkB,WAAW,cAAc,iBAAiB;AAClE,UAAI,CAAC,iBAAiB;AACpB,cAAM,WAAW,SAAS,cAAc,OAAO;AAC/C,iBAAS,aAAa,iBAAiB,GAAG;AAC1C,iBAAS,cAAc;AACvB,mBAAW,YAAY,QAAQ;AAAA,MACjC;AAAA,IACF;AAAA,EACF,CAAC;AAED,WAAS,QAAQ,YAAY,EAAE,WAAW,MAAM,SAAS,OAAO;AAClE;AAoBA,SAAS,kBAAkB,WAAmB,SAAiB,SAA+B;AAC5F,QAAM,KAAK,SAAS,cAAc,KAAK;AACvC,KAAG,YAAY;AAIf,QAAM,WAAW,YAAY,SAAS,kBAAkB;AACxD,QAAM,WAAW,YAAY,SAAS,EAAE;AAExC,QAAM,SAAS,SAAS,cAAc,GAAG;AACzC,SAAO,OAAO;AACd,SAAO,SAAS;AAChB,SAAO,MAAM;AAEb,MAAI,UAAU;AACZ,UAAM,MAAM,SAAS,cAAc,KAAK;AACxC,QAAI,MAAM;AACV,QAAI,MAAM;AACV,QAAI,MAAM,UACR;AACF,WAAO,YAAY,GAAG;AAAA,EACxB,OAAO;AACL,WAAO,cAAc;AAAA,EACvB;AAEA,KAAG,YAAY,MAAM;AACrB,SAAO;AACT;AC5IO,SAAS,gBACd,WACA,SACA,SACoB;AACpB,MAAI,QAAQ,SAAS,gBAAiB,QAAO;AAE7C,QAAM,KAAK,SAAS,cAAc,KAAK;AACvC,KAAG,YAAY;AAIf,QAAM,UAAU,YAAY,SAAS,SAAS,kBAAkB;AAChE,QAAM,UAAU,YAAY,SAAS,SAAS,EAAE;AAEhD,QAAM,SAAS,SAAS,cAAc,GAAG;AACzC,SAAO,OAAO;AACd,SAAO,SAAS;AAChB,SAAO,MAAM;AAEb,MAAI,SAAS;AACX,UAAM,MAAM,SAAS,cAAc,KAAK;AACxC,QAAI,MAAM;AACV,QAAI,MAAM;AACV,QAAI,YAAY;AAChB,WAAO,YAAY,GAAG;AAAA,EACxB,OAAO;AACL,WAAO,cAAc;AAAA,EACvB;AACA,KAAG,YAAY,MAAM;AAErB,QAAM,QAAQ,SAAS,cAAc,OAAO;AAC5C,QAAM,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAsCpB,YAAU,YAAY,KAAK;AAC3B,YAAU,YAAY,EAAE;AAGxB,qBAAmB,WAAW,SAAS,WAAW,MAAS;AAE3D,SAAO;AACT;AAGO,SAAS,gBAAgB,WAA2C;AACzE,QAAM,KAAK,UAAU,cAAc,eAAe;AAClD,MAAI,OAAO,OAAA;AACb;AC/FA,MAAM,uCAA4C,IAAA;AAOlD,eAAsB,oBAAoB,IAA+B;AACvE,QAAM,SAAS,GAAG,SAAA;AAClB,QAAM,QAAQ,IAAI,cAAc,OAAO,MAAM;AAE7C,MAAI,OAAO,WAAW,eAAe,OAAO,QAAQ;AAClD,UAAMA,QAAO,MAAM,OAAO,OAAO,OAAO,WAAW,KAAK;AACxD,WAAO,MAAM,KAAK,IAAI,WAAWA,KAAI,CAAC,EACnC,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAC1C,KAAK,EAAE;AAAA,EACZ;AAGA,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,UAAM,OAAO,OAAO,WAAW,CAAC;AAChC,YAAS,QAAQ,KAAK,OAAO,OAAQ;AAAA,EACvC;AACA,SAAO,KAAK,IAAI,IAAI,EAAE,SAAS,EAAE;AACnC;AAMA,eAAsB,uBAAuB,MAAc,IAA6B;AACtF,QAAM,OAAO,MAAM,oBAAoB,EAAE;AACzC,mBAAiB,IAAI,MAAM,IAAI;AAEjC;AAyBA,eAAsB,wBACpB,MACA,IAC+B;AAC/B,QAAM,eAAe,iBAAiB,IAAI,IAAI;AAC9C,MAAI,CAAC,aAAc,QAAO;AAE1B,QAAM,cAAc,MAAM,oBAAoB,EAAE;AAEhD,MAAI,gBAAgB,cAAc;AAChC,YAAQ,KAAK,sCAAsC,IAAI,EAAE;AACzD,WAAO;AAAA,MACL,OAAO;AAAA,MACP,MAAM;AAAA,MACN,UAAU,mBAAmB,MAAM;AAAA,MACnC,SAAS;AAAA,IAAA;AAAA,EAEb;AAEA,SAAO;AACT;AC5EA,MAAM,WAA+C;AAAA,EACnD,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,eAAe;AAAA,EACf,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,OAAO;AAAA,EACP,SAAS;AAAA,EACT,KAAK;AAAA,EACL,aAAa;AAAA,EACb,qBAAqB;AAAA,EACrB,uCAAuC;AAAA,EACvC,gBAAgB;AAAA,EAChB,UAAU;AAAA,EACV,gBAAgB;AAAA,EAChB,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,eAAe;AAAA,EACf,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,cAAc;AAAA,EACd,iBAAiB;AAAA,EACjB,eAAe;AAAA,EACf,eAAe;AAAA,EACf,kBAAkB;AAAA,EAClB,uBAAuB;AAAA,EACvB,mBAAmB;AAAA,EACnB,WAAW;AAAA,EACX,iBAAiB;AAAA,EACjB,mBAAmB;AAAA,EACnB,cAAc;AAAA,EACd,cAAc;AAAA,EACd,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,kBAAkB;AAAA,EAClB,iBAAiB;AAAA,EACjB,mBAAmB;AAAA,EACnB,iBAAiB;AAAA,EACjB,cAAc;AAAA,EACd,mBAAmB;AAAA,EACnB,kBAAkB;AACpB;AAGA,MAAM,oCAAoB,IAAI;AAAA,EAC5B;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAY;AAAA,EAC7B;AAAA,EAAgB;AAAA,EAAkB;AAAA,EAAiB;AAAA,EACnD;AAAA,EAAiB;AAAA,EAAc;AAAA,EAAmB;AACpD,CAAC;AAGD,MAAM,mCAAmB,IAAI;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAaM,MAAe,sBAAsB,gBAAgB;AAAA,EAC1D,WAAW,qBAA+B;AACxC,WAAO,OAAO,KAAK,QAAQ;AAAA,EAC7B;AAAA,EAEU;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAgC;AAAA,EAClC,iBAAwC;AAAA,EACxC,aAA2C;AAAA,EAC3C,cAAkC;AAAA,EAE1C,cAAc;AACZ,UAAA;AACA,SAAK,SAAS,KAAK,aAAa,EAAE,MAAM,QAAQ;AAChD,SAAK,QAAQ,IAAI,YAAA;AACjB,SAAK,SAAS,IAAI,eAAe,IAAI;AAAA,EACvC;AAAA,EAEA,oBAA0B;AAExB,QAAI,CAAC,KAAK,YAAY;AACpB,WAAK,aAAa,eAAA;AAAA,IACpB;AAGA,SAAK,SAAS,KAAK,YAAA;AAGnB,SAAK,YAAA;AAGL,SAAK,iBAAiB,IAAI,eAAe,MAAM,KAAK,UAAU;AAC9D,SAAK,eAAe,QAAQ,IAAI;AAGhC,SAAK,iBAAA;AAGL,SAAK,YAAA;AAGL,SAAK,kBAAA;AAAA,EACP;AAAA,EAEA,uBAA6B;AAC3B,SAAK,gBAAgB,WAAA;AACrB,SAAK,iBAAiB;AACtB,SAAK,OAAO,UAAA;AACZ,oBAAgB,KAAK,MAAM;AAC3B,SAAK,cAAc;AACnB,SAAK,eAAA;AAAA,EACP;AAAA,EAEA,yBAAyB,MAAc,QAAuB,QAA6B;AACzF,QAAI,WAAW,OAAQ;AACvB,SAAK,SAAS,KAAK,YAAA;AACnB,SAAK,mBAAmB,MAAM,QAAQ,MAAM;AAAA,EAC9C;AAAA;AAAA;AAAA,EAKA,YAAoC;AAClC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,WAAwB;AACtB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,aAA6C;AAC3C,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGU,WAAW,SAAsC;AACzD,QAAI,CAAC,KAAK,QAAS,QAAO;AAC1B,WAAO,KAAK,QAAQ,SAAS,OAAO,MAAM;AAAA,EAC5C;AAAA;AAAA,EAcU,oBAA0B;AAAA,EAEpC;AAAA;AAAA;AAAA,EAKA,MAAc,oBAAmC;AAC/C,QAAI;AAEF,YAAM,uBAAuB,gBAAgB,YAAY;AAEzD,WAAK,UAAU,MAAM,aAAa;AAAA,QAChC,QAAQ,KAAK,OAAO;AAAA,QACpB,YAAY,KAAK,OAAO;AAAA,QACxB,aAAa,KAAK,OAAO;AAAA,QACzB,aAAa,KAAK,OAAO;AAAA,MAAA,CAC1B;AAGD,YAAM,gBAAgB,MAAM,wBAAwB,gBAAgB,YAAY;AAChF,UAAI,eAAe;AACjB,aAAK,UAAU;AAAA,MACjB;AAIA,YAAM,UAAU,KAAK,QAAQ,SAAS,kBAClC,KAAK,OAAO,aAAa,KAAK,OAAO,gBACrC,KAAK,OAAO;AAChB,WAAK,cAAc,gBAAgB,KAAK,QAAQ,KAAK,SAAS;AAAA,QAC5D;AAAA,QACA,SAAS,KAAK,OAAO;AAAA,MAAA,CACtB;AAGD,WAAK,kBAAA;AAGL,WAAK,OAAO,KAAK,cAAc;AAAA,QAC7B,OAAO,KAAK,QAAQ;AAAA,QACpB,MAAM,KAAK,QAAQ;AAAA,QACnB,SAAS,KAAK,QAAQ;AAAA,MAAA,CACvB;AAAA,IACH,SAAS,KAAK;AACZ,cAAQ,KAAK,iCAAiC,GAAG;AACjD,WAAK,UAAU;AAAA,QACb,OAAO;AAAA,QACP,MAAM;AAAA,QACN,UAAU,mBAAmB,MAAM;AAAA,QACnC,SAAS;AAAA,MAAA;AAEX,WAAK,cAAc,gBAAgB,KAAK,QAAQ,KAAK,SAAS;AAAA,QAC5D,SAAS,KAAK,OAAO;AAAA,QACrB,SAAS,KAAK,OAAO;AAAA,MAAA,CACtB;AACD,WAAK,kBAAA;AACL,WAAK,OAAO,KAAK,cAAc;AAAA,QAC7B,OAAO;AAAA,QACP,MAAM;AAAA,QACN,SAAS;AAAA,MAAA,CACV;AAAA,IACH;AAAA,EACF;AAAA;AAAA,EAGQ,cAA4B;AAClC,UAAM,QAA+B,CAAA;AAErC,eAAW,CAAC,UAAU,SAAS,KAAK,OAAO,QAAQ,QAAQ,GAAG;AAC5D,YAAM,SAAS,KAAK,aAAa,QAAQ;AACzC,UAAI,WAAW,QAAQ,CAAC,cAAc,IAAI,QAAQ,EAAG;AAErD,UAAI,cAAc,IAAI,QAAQ,GAAG;AAC9B,cAAkC,SAAS,IAAI,KAAK,aAAa,QAAQ;AAAA,MAC5E,WAAW,aAAa,IAAI,QAAQ,GAAG;AACrC,cAAM,MAAM,WAAW,MAAO;AAC9B,YAAI,CAAC,MAAM,GAAG,GAAG;AACd,gBAAkC,SAAS,IAAI;AAAA,QAClD;AAAA,MACF,WAAW,cAAc,UAAU;AACjC,cAAM,MAAM,YAAY,MAAO;AAC/B,YAAI,WAAW,SAAS;AAAA,MAC1B,WAAW,cAAc,WAAW;AAClC,YAAI,CAAC,QAAQ,YAAY,MAAM,EAAE,SAAS,MAAO,GAAG;AAClD,gBAAM,UAAU;AAAA,QAClB;AAAA,MACF,WAAW,cAAc,eAAe;AACtC,YAAI,WAAW,QAAS,OAAM,cAAc;AAAA,MAC9C,WAAW,cAAc,cAAc;AACrC,YAAI,WAAW,UAAU,WAAW,UAAU;AAC5C,gBAAM,aAAa;AAAA,QACrB;AAAA,MACF,OAAO;AACJ,cAAkC,SAAS,IAAI;AAAA,MAClD;AAAA,IACF;AAEA,WAAO,cAAc,OAAO,KAAK,cAAc,MAAS;AAAA,EAC1D;AAAA;AAAA,EAGQ,cAAoB;AAC1B,QAAI,CAAC,KAAK,MAAM,WAAW,KAAK,MAAM,YAAY,IAAI;AACpD,WAAK,MAAM,UAAU;AAAA,IACvB;AAEA,QAAI,KAAK,OAAO,OAAO;AACrB,WAAK,MAAM,QAAQ,KAAK,OAAO,MAAM,SAAS,GAAG,KAAK,KAAK,OAAO,MAAM,SAAS,IAAI,IACjF,KAAK,OAAO,QACZ,GAAG,KAAK,OAAO,KAAK;AAAA,IAC1B,WAAW,CAAC,KAAK,MAAM,OAAO;AAC5B,WAAK,MAAM,QAAQ;AAAA,IACrB;AAEA,QAAI,KAAK,OAAO,QAAQ;AACtB,WAAK,MAAM,SAAS,KAAK,OAAO,OAAO,SAAS,GAAG,KAAK,KAAK,OAAO,OAAO,SAAS,IAAI,IACpF,KAAK,OAAO,SACZ,GAAG,KAAK,OAAO,MAAM;AAAA,IAC3B,WAAW,KAAK,OAAO,QAAQ;AAC7B,WAAK,MAAM,cAAc,KAAK,OAAO,OAAO,QAAQ,KAAK,GAAG;AAAA,IAC9D,WAAW,CAAC,KAAK,MAAM,QAAQ;AAC7B,WAAK,MAAM,cAAc;AAAA,IAC3B;AAEA,SAAK,MAAM,WAAW;AACtB,SAAK,MAAM,WAAW;AACtB,SAAK,MAAM,kBAAkB;AAAA,EAC/B;AAAA;AAAA,EAGQ,mBAAyB;AAC/B,UAAM,QAAQ,SAAS,cAAc,OAAO;AAC5C,UAAM,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqCpB,SAAK,OAAO,YAAY,KAAK;AAAA,EAC/B;AACF;ACtVO,MAAM,mBAA0C;AAAA,EAC7C,SAAiB,CAAA;AAAA,EACjB;AAAA,EAER,YAAY,OAAe,KAAK;AAC9B,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,aAAa,SAAkB,YAAgC;AAC7D,SAAK,QAAA;AAEL,QAAI,eAAe,QAAQ;AACzB,YAAM,OAAO,KAAK,aAAa,OAAO;AACtC,WAAK,OAAO,IAAI,CAAC;AACjB,WAAK,SAAS,CAAC,IAAI;AACnB,aAAO,KAAK;AAAA,IACd;AAGA,UAAM,QAAQ,KAAK,aAAa,OAAO;AACvC,UAAM,QAAQ,KAAK,aAAa,OAAO;AAEvC,SAAK,SAAS,MAAM,UAA4B,YAAY,MAAM;AAClE,SAAK,SAAS,MAAM,UAA4B,YAAY,OAAO;AAEnE,UAAM,OAAO,IAAI,CAAC;AAClB,UAAM,OAAO,IAAI,CAAC;AAElB,SAAK,SAAS,CAAC,OAAO,KAAK;AAC3B,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,gBAAgB,QAAiC;AAC/C,WAAO,MAAM,gBAAgB;AAC7B,WAAO,OAAO,gBAAgB;AAC9B,WAAO,MAAM,gBAAgB;AAC7B,WAAO,SAAS,IAAI,GAAG,GAAG,CAAC;AAC3B,WAAO,uBAAA;AAAA,EACT;AAAA,EAEA,OAAO,SAA4B,OAAqB;AAAA,EAExD;AAAA,EAEA,UAAgB;AACd,eAAW,QAAQ,KAAK,QAAQ;AAC9B,WAAK,SAAS,QAAA;AACb,WAAK,SAA+B,QAAA;AAAA,IACvC;AACA,SAAK,SAAS,CAAA;AAAA,EAChB;AAAA,EAEQ,aAAa,SAAwB;AAC3C,UAAM,EAAE,QAAQ,eAAe,eAAA,IAAmB;AAGlD,UAAM,YAAa,KAAK,OAAO,MAAO,KAAK,KAAK;AAChD,UAAM,WAAW,KAAK,SAAS,MAAM,KAAK,KAAK,IAAI;AAEnD,UAAM,WAAW,IAAI;AAAA,MACnB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAIF,aAAS,MAAM,IAAI,GAAG,CAAC;AAEvB,UAAM,WAAW,IAAI,kBAAkB;AAAA,MACrC,KAAK;AAAA,IAAA,CACN;AAED,WAAO,IAAI,KAAK,UAAU,QAAQ;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,SACN,UACA,YACA,KACM;AACN,UAAM,SAAS,SAAS,WAAW;AAEnC,aAAS,IAAI,GAAG,IAAI,OAAO,OAAO,KAAK;AACrC,UAAI,IAAI,OAAO,KAAK,CAAC;AACrB,UAAI,IAAI,OAAO,KAAK,CAAC;AAErB,UAAI,eAAe,OAAO;AAExB,YAAI,QAAQ,SAAS,IAAI,MAAM,IAAI,MAAM;AAAA,MAC3C,WAAW,eAAe,MAAM;AAE9B,YAAI,QAAQ,SAAS,IAAI,MAAM,MAAM,IAAI;AAAA,MAC3C;AAEA,aAAO,MAAM,GAAG,GAAG,CAAC;AAAA,IACtB;AAEA,WAAO,cAAc;AAAA,EACvB;AACF;ACpHA,MAAM,QAAQ;AAUP,MAAM,eAAsC;AAAA,EACzC,SAAiB,CAAA;AAAA;AAAA,EAEjB,cAAc,KAAK;AAAA,EACnB,aAAyB;AAAA,EAEjC,aAAa,SAAkB,YAAgC;AAC7D,SAAK,QAAA;AACL,SAAK,aAAa;AAGlB,QAAI,SAAS,QAAQ,QAChB,QAAQ,MAA2B,aACnC,QAAQ,MAA2B,cACpC,KAAK;AAGT,QAAI,eAAe,MAAO,WAAU;AAEpC,QAAI,eAAe,KAAM,WAAU;AAEnC,SAAK,cAAc,UAAU,KAAK;AAGlC,QAAI,eAAe,QAAQ;AACzB,YAAM,OAAO,KAAK,YAAY,SAAS,GAAG,CAAC;AAC3C,WAAK,OAAO,IAAI,CAAC;AACjB,WAAK,SAAS,CAAC,IAAI;AACnB,aAAO,KAAK;AAAA,IACd;AAEA,UAAM,QAAQ,KAAK,YAAY,SAAS,GAAG,CAAC;AAC5C,UAAM,QAAQ,KAAK,YAAY,SAAS,GAAG,CAAC;AAE5C,SAAK,SAAS,OAAO,YAAY,MAAM;AACvC,SAAK,SAAS,OAAO,YAAY,OAAO;AAExC,UAAM,OAAO,IAAI,CAAC;AAClB,UAAM,OAAO,IAAI,CAAC;AAElB,SAAK,SAAS,CAAC,OAAO,KAAK;AAC3B,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,gBAAgB,QAAiC;AAC/C,WAAO,MAAM;AACb,WAAO,OAAO;AACd,WAAO,MAAM;AACb,WAAO,SAAS,IAAI,GAAG,GAAG,KAAK;AAC/B,WAAO,OAAO,GAAG,GAAG,CAAC;AACrB,WAAO,uBAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,OAAO,QAA2B,OAAqB;AACrD,QAAI,KAAK,OAAO,WAAW,EAAG;AAG9B,UAAM,MAAO,KAAK,OAAO,CAAC,EAAG,SAA+B;AAC5D,QAAI,KAAK,OAAO;AACd,YAAM,MAAM,IAAI;AAChB,UAAI,IAAI,cAAc,IAAI,aAAa;AACrC,YAAI,IAAI,IAAI,aAAa,IAAI;AAC7B,YAAI,KAAK,eAAe,MAAO,MAAK;AACpC,YAAI,KAAK,eAAe,KAAM,MAAK;AACnC,aAAK,cAAc;AAAA,MACrB;AAAA,IACF;AAGA,UAAM,OAAO,UAAU,SAAS,OAAO,GAAG;AAC1C,UAAM,OAAO,IAAI,QAAQ,KAAK,IAAI,OAAO,CAAC;AAC1C,UAAM,OAAO,OAAO,OAAO;AAG3B,QAAI;AACJ,QAAI;AAEJ,QAAI,KAAK,cAAc,OAAO,QAAQ;AAEpC,eAAS;AACT,eAAS,OAAO,KAAK;AAAA,IACvB,OAAO;AAEL,eAAS;AACT,eAAS,OAAO,KAAK;AAAA,IACvB;AAEA,eAAW,QAAQ,KAAK,QAAQ;AAC9B,WAAK,MAAM,IAAI,QAAQ,QAAQ,CAAC;AAAA,IAClC;AAAA,EACF;AAAA,EAEA,UAAgB;AACd,eAAW,QAAQ,KAAK,QAAQ;AAC9B,WAAK,SAAS,QAAA;AACb,WAAK,SAA+B,QAAA;AAAA,IACvC;AACA,SAAK,SAAS,CAAA;AAAA,EAChB;AAAA,EAEQ,YAAY,SAAkB,OAAe,QAAsB;AACzE,UAAM,WAAW,IAAI,cAAc,OAAO,MAAM;AAChD,UAAM,WAAW,IAAI,kBAAkB,EAAE,KAAK,SAAS;AACvD,UAAM,OAAO,IAAI,KAAK,UAAU,QAAQ;AACxC,SAAK,SAAS,IAAI,GAAG,GAAG,CAAC;AACzB,WAAO;AAAA,EACT;AAAA,EAEQ,SAAS,MAAY,YAAwB,KAA6B;AAChF,UAAM,WAAW,KAAK;AACtB,UAAM,SAAS,SAAS,WAAW;AAEnC,aAAS,IAAI,GAAG,IAAI,OAAO,OAAO,KAAK;AACrC,UAAI,IAAI,OAAO,KAAK,CAAC;AACrB,UAAI,IAAI,OAAO,KAAK,CAAC;AAErB,UAAI,eAAe,OAAO;AACxB,YAAI,QAAQ,SAAS,IAAI,MAAM,IAAI,MAAM;AAAA,MAC3C,WAAW,eAAe,MAAM;AAC9B,YAAI,QAAQ,SAAS,IAAI,MAAM,MAAM,IAAI;AAAA,MAC3C;AAEA,aAAO,MAAM,GAAG,GAAG,CAAC;AAAA,IACtB;AAEA,WAAO,cAAc;AAAA,EACvB;AACF;AClIA,MAAMC,aAAwD;AAAA,EAC5D,EAAE,KAAK,GAAG,KAAK,EAAA;AAAA;AAAA,EACf,EAAE,KAAK,GAAG,KAAK,EAAA;AAAA;AAAA,EACf,EAAE,KAAK,GAAG,KAAK,EAAA;AAAA;AAAA,EACf,EAAE,KAAK,GAAG,KAAK,EAAA;AAAA;AAAA,EACf,EAAE,KAAK,GAAG,KAAK,EAAA;AAAA;AAAA,EACf,EAAE,KAAK,GAAG,KAAK,EAAA;AAAA;AACjB;AAEA,MAAMC,SAAO;AACb,MAAMC,SAAO;AASN,MAAM,kBAAyC;AAAA,EAC5C,SAAiB,CAAA;AAAA,EAEzB,aAAa,SAAkB,YAAgC;AAC7D,SAAK,QAAA;AAEL,QAAI,eAAe,QAAQ;AACzB,YAAM,OAAO,KAAK,UAAU,OAAO;AACnC,WAAK,aAAa,KAAK,UAAyB,QAAQ,MAAM;AAC9D,WAAK,OAAO,IAAI,CAAC;AACjB,WAAK,SAAS,CAAC,IAAI;AACnB,aAAO,KAAK;AAAA,IACd;AAGA,UAAM,QAAQ,KAAK,UAAU,OAAO;AACpC,UAAM,QAAQ,KAAK,UAAU,OAAO;AAEpC,UAAM,gBAAgB,eAAe,aAAa,OAAO;AAEzD,SAAK,aAAa,MAAM,UAAyB,eAAe,MAAM;AACtE,SAAK,aAAa,MAAM,UAAyB,eAAe,OAAO;AAEvE,UAAM,OAAO,IAAI,CAAC;AAClB,UAAM,OAAO,IAAI,CAAC;AAElB,SAAK,SAAS,CAAC,OAAO,KAAK;AAC3B,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,gBAAgB,QAAiC;AAC/C,WAAO,MAAM,gBAAgB;AAC7B,WAAO,OAAO,gBAAgB;AAC9B,WAAO,MAAM,gBAAgB;AAC7B,WAAO,SAAS,IAAI,GAAG,GAAG,CAAC;AAC3B,WAAO,uBAAA;AAAA,EACT;AAAA,EAEA,OAAO,SAA4B,OAAqB;AAAA,EAExD;AAAA,EAEA,UAAgB;AACd,eAAW,QAAQ,KAAK,QAAQ;AAC9B,WAAK,SAAS,QAAA;AACb,WAAK,SAA+B,QAAA;AAAA,IACvC;AACA,SAAK,SAAS,CAAA;AAAA,EAChB;AAAA,EAEQ,UAAU,SAAwB;AACxC,UAAM,WAAW,IAAI,YAAY,GAAG,GAAG,CAAC;AAGxC,aAAS,MAAM,IAAI,GAAG,CAAC;AAEvB,UAAM,WAAW,IAAI,kBAAkB;AAAA,MACrC,KAAK;AAAA,IAAA,CACN;AAED,WAAO,IAAI,KAAK,UAAU,QAAQ;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeQ,aACN,UACA,YACA,KACM;AACN,UAAM,SAAS,SAAS,WAAW;AAEnC,aAAS,OAAO,GAAG,OAAO,GAAG,QAAQ;AACnC,YAAM,EAAE,KAAK,QAAQF,WAAS,IAAI;AAGlC,UAAI,OAAO,MAAMC;AACjB,UAAI,QAAQ,MAAM,KAAKA;AAEvB,UAAI,OAAO,KAAK,MAAM,KAAKC;AAC3B,UAAI,OAAO,IAAI,MAAMA;AAGrB,UAAI,eAAe,MAAM;AAEvB,cAAM,SAAS,OAAO;AACtB,cAAM,YAAY,SAAS;AAC3B,YAAI,QAAQ,QAAQ;AAElB,iBAAO,OAAO,MAAM;AACpB,iBAAO,OAAO;AAAA,QAChB,OAAO;AAEL,iBAAO,OAAO;AACd,iBAAO,OAAO;AAAA,QAChB;AAAA,MACF,WAAW,eAAe,OAAO;AAE/B,cAAM,SAAS,OAAO;AACtB,cAAM,YAAY,SAAS;AAC3B,YAAI,QAAQ,QAAQ;AAElB,iBAAO,OAAO;AACd,iBAAO,OAAO;AAAA,QAChB,OAAO;AAEL,iBAAO,OAAO,MAAM;AACpB,iBAAO,OAAO;AAAA,QAChB;AAAA,MACF;AAGA,YAAM,YAAY,OAAO;AACzB,eAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,cAAM,MAAM,YAAY;AAExB,cAAM,WAAW,OAAO,KAAK,GAAG;AAChC,cAAM,WAAW,OAAO,KAAK,GAAG;AAEhC,cAAM,UAAU,OAAO,YAAY,OAAO;AAC1C,cAAM,UAAU,OAAO,YAAY,OAAO;AAE1C,eAAO,MAAM,KAAK,SAAS,OAAO;AAAA,MACpC;AAAA,IACF;AAEA,WAAO,cAAc;AAAA,EACvB;AACF;ACjLO,MAAM,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoBxB,MAAM,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACEjC,MAAM,WAAwD;AAAA,EAC5D,EAAE,KAAK,GAAG,KAAK,EAAA;AAAA;AAAA,EACf,EAAE,KAAK,GAAG,KAAK,EAAA;AAAA;AAAA,EACf,EAAE,KAAK,GAAG,KAAK,EAAA;AAAA;AAAA,EACf,EAAE,KAAK,GAAG,KAAK,EAAA;AAAA;AAAA,EACf,EAAE,KAAK,GAAG,KAAK,EAAA;AAAA;AAAA,EACf,EAAE,KAAK,GAAG,KAAK,EAAA;AAAA;AACjB;AAEA,MAAM,OAAO;AACb,MAAM,OAAO;AAWN,MAAM,cAAqC;AAAA,EACxC,SAAiB,CAAA;AAAA,EAEzB,aAAa,SAAkB,YAAgC;AAC7D,SAAK,QAAA;AAEL,QAAI,eAAe,QAAQ;AACzB,YAAM,OAAO,KAAK,UAAU,OAAO;AACnC,WAAK,eAAe,KAAK,UAAyB,QAAQ,MAAM;AAChE,WAAK,OAAO,IAAI,CAAC;AACjB,WAAK,SAAS,CAAC,IAAI;AACnB,aAAO,KAAK;AAAA,IACd;AAGA,UAAM,QAAQ,KAAK,UAAU,OAAO;AACpC,UAAM,QAAQ,KAAK,UAAU,OAAO;AAEpC,UAAM,gBAAgB,eAAe,aAAa,OAAO;AAEzD,SAAK,eAAe,MAAM,UAAyB,eAAe,MAAM;AACxE,SAAK,eAAe,MAAM,UAAyB,eAAe,OAAO;AAEzE,UAAM,OAAO,IAAI,CAAC;AAClB,UAAM,OAAO,IAAI,CAAC;AAElB,SAAK,SAAS,CAAC,OAAO,KAAK;AAC3B,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,gBAAgB,QAAiC;AAC/C,WAAO,MAAM,gBAAgB;AAC7B,WAAO,OAAO,gBAAgB;AAC9B,WAAO,MAAM,gBAAgB;AAC7B,WAAO,SAAS,IAAI,GAAG,GAAG,CAAC;AAC3B,WAAO,uBAAA;AAAA,EACT;AAAA,EAEA,OAAO,SAA4B,OAAqB;AAAA,EAExD;AAAA,EAEA,UAAgB;AACd,eAAW,QAAQ,KAAK,QAAQ;AAC9B,WAAK,SAAS,QAAA;AACb,WAAK,SAA4B,QAAA;AAAA,IACpC;AACA,SAAK,SAAS,CAAA;AAAA,EAChB;AAAA,EAEQ,UAAU,SAAwB;AACxC,UAAM,WAAW,IAAI,YAAY,GAAG,GAAG,CAAC;AAGxC,aAAS,MAAM,IAAI,GAAG,CAAC;AAEvB,UAAM,WAAW,IAAI,eAAe;AAAA,MAClC,UAAU,EAAE,KAAK,EAAE,OAAO,UAAQ;AAAA,MAClC,cAAc;AAAA,MACd,gBAAgB;AAAA,MAChB,MAAM;AAAA,IAAA,CACP;AAED,WAAO,IAAI,KAAK,UAAU,QAAQ;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcQ,eACN,UACA,YACA,KACM;AACN,UAAM,cAAc,SAAS,WAAW,GAAI;AAC5C,UAAM,UAAU,IAAI,aAAa,cAAc,CAAC;AAEhD,aAAS,OAAO,GAAG,OAAO,GAAG,QAAQ;AACnC,YAAM,EAAE,KAAK,QAAQ,SAAS,IAAI;AAGlC,UAAI,OAAO,MAAM;AACjB,UAAI,QAAQ,MAAM,KAAK;AAEvB,UAAI,OAAO,KAAK,MAAM,KAAK;AAC3B,UAAI,OAAO,IAAI,MAAM;AAErB,UAAI,eAAe,MAAM;AACvB,cAAM,aAAa,OAAO,QAAQ;AAClC,YAAI,QAAQ,QAAQ;AAClB,iBAAO,OAAO,MAAM;AACpB,iBAAO,OAAO;AAAA,QAChB,OAAO;AACL,iBAAO,OAAO;AACd,iBAAO,OAAO;AAAA,QAChB;AAAA,MACF,WAAW,eAAe,OAAO;AAC/B,cAAM,aAAa,OAAO,QAAQ;AAClC,YAAI,QAAQ,QAAQ;AAClB,iBAAO,OAAO;AACd,iBAAO,OAAO;AAAA,QAChB,OAAO;AACL,iBAAO,OAAO,MAAM;AACpB,iBAAO,OAAO;AAAA,QAChB;AAAA,MACF;AAEA,YAAM,YAAY,OAAO;AACzB,eAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,cAAM,OAAO,YAAY,KAAK;AAC9B,gBAAQ,GAAG,IAAI;AACf,gBAAQ,MAAM,CAAC,IAAI;AACnB,gBAAQ,MAAM,CAAC,IAAI,OAAO;AAC1B,gBAAQ,MAAM,CAAC,IAAI,OAAO;AAAA,MAC5B;AAAA,IACF;AAEA,aAAS,aAAa,cAAc,IAAI,gBAAgB,SAAS,CAAC,CAAC;AAAA,EACrE;AACF;AC/JO,MAAM;AAAA;AAAA,EAAiC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiBvC,MAAM;AAAA;AAAA,EAAmC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACJhD,SAAS,eACP,YACA,KACgB;AAChB,UAAQ,YAAA;AAAA,IACN,KAAK;AACH,aAAO;AAAA,QACL,SAAS;AAAA,QACT,SAAS;AAAA,QACT,UAAU,QAAQ,SAAS,IAAM;AAAA,QACjC,UAAU;AAAA,MAAA;AAAA,IAEd,KAAK;AACH,aAAO;AAAA,QACL,SAAS;AAAA,QACT,SAAS;AAAA,QACT,UAAU;AAAA;AAAA,QAEV,UAAU,QAAQ,SAAS,MAAM;AAAA,MAAA;AAAA,IAErC,KAAK;AAAA,IACL;AACE,aAAO;AAAA,QACL,SAAS;AAAA,QACT,SAAS;AAAA,QACT,UAAU;AAAA,QACV,UAAU;AAAA,MAAA;AAAA,EACZ;AAEN;AAiBO,MAAM,kBAAyC;AAAA,EAC5C,SAAiB,CAAA;AAAA,EAEzB,aAAa,SAAkB,YAAgC;AAC7D,SAAK,QAAA;AAEL,QAAI,eAAe,QAAQ;AACzB,YAAM,OAAO,KAAK,aAAa,SAAS,QAAQ,MAAM;AACtD,WAAK,OAAO,IAAI,CAAC;AACjB,WAAK,SAAS,CAAC,IAAI;AACnB,aAAO,KAAK;AAAA,IACd;AAGA,UAAM,gBAAgB,eAAe,aAAa,OAAO;AAEzD,UAAM,QAAQ,KAAK,aAAa,SAAS,eAAe,MAAM;AAC9D,UAAM,QAAQ,KAAK,aAAa,SAAS,eAAe,OAAO;AAE/D,UAAM,OAAO,IAAI,CAAC;AAClB,UAAM,OAAO,IAAI,CAAC;AAElB,SAAK,SAAS,CAAC,OAAO,KAAK;AAC3B,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,gBAAgB,QAAiC;AAC/C,WAAO,MAAM,gBAAgB;AAC7B,WAAO,OAAO,gBAAgB;AAC9B,WAAO,MAAM,gBAAgB;AAC7B,WAAO,SAAS,IAAI,GAAG,GAAG,CAAC;AAC3B,WAAO,uBAAA;AAAA,EACT;AAAA,EAEA,OAAO,SAA4B,OAAqB;AAAA,EAExD;AAAA,EAEA,UAAgB;AACd,eAAW,QAAQ,KAAK,QAAQ;AAC9B,WAAK,SAAS,QAAA;AACb,WAAK,SAA4B,QAAA;AAAA,IACpC;AACA,SAAK,SAAS,CAAA;AAAA,EAChB;AAAA,EAEQ,aACN,SACA,YACA,KACM;AACN,UAAM,EAAE,QAAQ,eAAe,eAAA,IAAmB;AAElD,UAAM,WAAW,IAAI,eAAe,QAAQ,eAAe,cAAc;AAIzE,aAAS,MAAM,IAAI,GAAG,CAAC;AAEvB,UAAM,WAAW,eAAe,YAAY,GAAG;AAE/C,UAAM,WAAW,IAAI,eAAe;AAAA,MAClC,UAAU;AAAA,QACR,KAAK,EAAE,OAAO,QAAA;AAAA,QACd,SAAS,EAAE,OAAO,SAAS,QAAA;AAAA,QAC3B,SAAS,EAAE,OAAO,SAAS,QAAA;AAAA,QAC3B,UAAU,EAAE,OAAO,SAAS,SAAA;AAAA,QAC5B,UAAU,EAAE,OAAO,SAAS,SAAA;AAAA,MAAS;AAAA,MAEvC,cAAc;AAAA,MACd,gBAAgB;AAAA,MAChB,MAAM;AAAA,IAAA,CACP;AAED,WAAO,IAAI,KAAK,UAAU,QAAQ;AAAA,EACpC;AACF;ACzHO,MAAM,iBAAiB;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA,EAGA,UAAU;AAAA;AAAA,EAGV,aAAa;AAAA,EACb,cAAc,IAAI,QAAA;AAAA;AAAA,EAGlB,2BAA2B;AAAA,EAC3B,cAAc;AAAA,EACd,aAAa;AAAA,EACb,cAAc;AAAA;AAAA,EAGd,gBAA6D,CAAA;AAAA,EAErE,YAAY,QAA2B,YAAyB,OAAoB,QAAsB;AACxG,SAAK,SAAS;AACd,SAAK,aAAa;AAClB,SAAK,QAAQ;AAEb,SAAK,MAAM,OAAO;AAClB,SAAK,MAAM,OAAO;AAClB,SAAK,YAAY,OAAO;AACxB,SAAK,YAAY,OAAO;AACxB,SAAK,MAAM,OAAO;AAClB,SAAK,cAAc,OAAO;AAC1B,SAAK,SAAS,OAAO;AACrB,SAAK,SAAS,OAAO;AACrB,SAAK,OAAO,OAAO,QAAQ;AAE3B,SAAK,OAAO,MAAM,KAAK;AACvB,SAAK,OAAO,uBAAA;AAEZ,QAAI,KAAK,aAAa;AACpB,WAAK,gBAAA;AAAA,IACP;AAEA,SAAK,aAAA;AAAA,EACP;AAAA;AAAA,EAGA,WAAW,SAAwB;AACjC,SAAK,UAAU;AACf,QAAI,CAAC,SAAS;AACZ,WAAK,aAAa;AAAA,IACpB;AAAA,EACF;AAAA;AAAA,EAGA,aAAa,WAA0B;AACrC,QAAI,KAAK,gBAAgB,UAAW;AACpC,SAAK,cAAc;AACnB,QAAI,WAAW;AACb,WAAK,OAAO;AACZ,WAAK,gBAAA;AAAA,IACP;AAAA,EACF;AAAA;AAAA,EAGA,SAAe;AACb,QAAI,CAAC,KAAK,eAAe,CAAC,KAAK,QAAS;AAGxC,UAAM,IAAI,gBAAgB;AAC1B,SAAK,QAAQ,KAAK,YAAY,KAAK,OAAO;AAC1C,SAAK,QAAQ,KAAK,YAAY,KAAK,OAAO;AAG1C,SAAK,MAAM,MAAM,KAAK,KAAK,KAAK,EAAE;AAGlC,QAAI,KAAK,OAAO,KAAK;AACnB,YAAM,WAAW,KAAK,OAAO;AAC7B,WAAK,MAAM,MAAM,KAAK,KAAK,CAAC,UAAU,QAAQ;AAC9C,WAAK,YAAY,MAAM,KAAK,WAAW,CAAC,UAAU,QAAQ;AAAA,IAC5D;AAGA,SAAK,MAAM,MAAM;AAAA,MACf,KAAK,KAAK;AAAA,MACV,KAAK,eAAe,KAAK,GAAG;AAAA,MAC5B,KAAK,KAAK;AAAA,IAAA,CACX;AAED,SAAK,aAAA;AAAA,EACP;AAAA;AAAA,EAGA,iBAA4D;AAC1D,WAAO;AAAA,MACL,KAAK,KAAK;AAAA,MACV,KAAK,eAAe,KAAK,GAAG;AAAA,MAC5B,KAAK,KAAK;AAAA,IAAA;AAAA,EAEd;AAAA;AAAA,EAGA,eAAe,KAAa,KAAa,OAAO,OAAa;AAC3D,SAAK,YAAY;AACjB,SAAK,YAAY;AACjB,QAAI,MAAM;AACR,WAAK,MAAM;AACX,WAAK,MAAM;AACX,WAAK,aAAA;AAAA,IACP;AAAA,EACF;AAAA;AAAA,EAGA,0BAAgC;AAC9B,QAAI,KAAK,yBAA0B;AACnC,SAAK,2BAA2B;AAEhC,UAAM,WAAW,CAAC,MAA8B;AAC9C,WAAK,cAAc,EAAE,SAAS;AAC9B,WAAK,aAAa,EAAE,QAAQ;AAC5B,WAAK,cAAc,EAAE,SAAS;AAC9B,WAAK,uBAAA;AAAA,IACP;AAEA,WAAO,iBAAiB,qBAAqB,OAAO;AACpD,SAAK,cAAc,KAAK,CAAC,qBAAqB,SAAS,MAAM,CAAC;AAAA,EAChE;AAAA;AAAA,EAGA,UAAgB;AACd,eAAW,CAAC,OAAO,SAAS,MAAM,KAAK,KAAK,eAAe;AACzD,aAAO,oBAAoB,OAAO,OAAO;AAAA,IAC3C;AACA,SAAK,gBAAgB,CAAA;AAAA,EACvB;AAAA,EAEQ,kBAAwB;AAE9B,SAAK,WAAW,KAAK,YAAY,eAAe,KAAK,cAAc,KAAK,IAAI,CAAC;AAC7E,SAAK,WAAW,KAAK,YAAY,eAAe,KAAK,cAAc,KAAK,IAAI,CAAC;AAC7E,SAAK,WAAW,KAAK,YAAY,aAAa,KAAK,YAAY,KAAK,IAAI,CAAC;AACzE,SAAK,WAAW,KAAK,YAAY,gBAAgB,KAAK,YAAY,KAAK,IAAI,CAAC;AAG5E,SAAK,WAAW,KAAK,YAAY,SAAS,KAAK,QAAQ,KAAK,IAAI,CAAC;AAGjE,SAAK,WAAW,KAAK,YAAY,WAAW,KAAK,UAAU,KAAK,IAAI,CAAC;AAGrE,QAAI,CAAC,KAAK,WAAW,aAAa,UAAU,GAAG;AAC7C,WAAK,WAAW,aAAa,YAAY,GAAG;AAAA,IAC9C;AAAA,EACF;AAAA,EAEQ,WAAW,QAAqB,OAAe,SAA8B;AACnF,WAAO,iBAAiB,OAAO,SAAS,EAAE,SAAS,UAAU,UAAU,QAAQ,MAAM;AACrF,SAAK,cAAc,KAAK,CAAC,OAAO,SAAS,MAAM,CAAC;AAAA,EAClD;AAAA,EAEQ,cAAc,GAAgB;AACpC,QAAI,CAAC,KAAK,QAAS;AACnB,UAAM,KAAK;AACX,SAAK,aAAa;AAClB,SAAK,YAAY,IAAI,GAAG,SAAS,GAAG,OAAO;AAC3C,SAAK,WAAW,kBAAkB,GAAG,SAAS;AAAA,EAChD;AAAA,EAEQ,cAAc,GAAgB;AACpC,QAAI,CAAC,KAAK,WAAY;AACtB,UAAM,KAAK;AACX,UAAM,KAAK,GAAG,UAAU,KAAK,YAAY;AACzC,UAAM,KAAK,GAAG,UAAU,KAAK,YAAY;AACzC,SAAK,YAAY,IAAI,GAAG,SAAS,GAAG,OAAO;AAE3C,UAAM,cAAc,gBAAgB;AACpC,SAAK,aAAa,KAAK;AACvB,SAAK,aAAa,KAAK;AAAA,EACzB;AAAA,EAEQ,YAAY,GAAgB;AAClC,UAAM,KAAK;AACX,SAAK,aAAa;AAClB,SAAK,WAAW,sBAAsB,GAAG,SAAS;AAAA,EACpD;AAAA,EAEQ,QAAQ,GAAgB;AAC9B,QAAI,CAAC,KAAK,QAAS;AACnB,UAAM,KAAK;AACX,OAAG,eAAA;AACH,SAAK,MAAM;AAAA,MACT,KAAK,MAAM,GAAG,SAAS,gBAAgB;AAAA,MACvC,KAAK;AAAA,MACL,KAAK;AAAA,IAAA;AAEP,SAAK,OAAO,MAAM,KAAK;AACvB,SAAK,OAAO,uBAAA;AAAA,EACd;AAAA,EAEQ,UAAU,GAAgB;AAChC,QAAI,CAAC,KAAK,QAAS;AACnB,UAAM,KAAK;AACX,UAAM,QAAQ,gBAAgB;AAE9B,YAAQ,GAAG,MAAA;AAAA,MACT,KAAK;AACH,aAAK,aAAa;AAClB;AAAA,MACF,KAAK;AACH,aAAK,aAAa;AAClB;AAAA,MACF,KAAK;AACH,aAAK,aAAa;AAClB;AAAA,MACF,KAAK;AACH,aAAK,aAAa;AAClB;AAAA,MACF;AACE;AAAA,IAAA;AAAA,EAEN;AAAA,EAEQ,yBAA+B;AAKrC,SAAK,YAAY,MAAM,KAAK,aAAa,IAAI,KAAK,EAAE;AACpD,SAAK,YAAY,CAAC,KAAK;AAAA,EACzB;AAAA,EAEQ,eAAqB;AAC3B,QAAI,CAAC,KAAK,YAAa;AAEvB,UAAM,MAAM,SAAS,KAAK,KAAK,GAAG;AAClC,UAAM,QAAQ,SAAS,KAAK,GAAG;AAG/B,UAAM,IAAI,KAAK,IAAI,GAAG,IAAI,KAAK,IAAI,KAAK;AACxC,UAAM,IAAI,KAAK,IAAI,GAAG;AACtB,UAAM,IAAI,KAAK,IAAI,GAAG,IAAI,KAAK,IAAI,KAAK;AAExC,SAAK,OAAO,OAAO,IAAI,KAAK,IAAI,KAAK,IAAI,GAAG;AAAA,EAC9C;AACF;AChQO,MAAM,WAAW;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAiC;AAAA,EACjC,eAAoC;AAAA,EACpC,eAAoC;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,EACA,uBAAoD;AAAA,EACpD,YAAY;AAAA,EACZ,aAAuB,CAAA;AAAA,EACvB,gBAAgB;AAAA,EAExB,YAAY,YAAwB,aAA0B,OAAoB,QAAsB;AACtG,SAAK,QAAQ;AACb,SAAK,SAAS;AACd,SAAK,cAAc;AAGnB,SAAK,QAAQ,IAAI,MAAA;AACjB,SAAK,MAAM,aAAa,IAAI,MAAM,CAAQ;AAG1C,SAAK,SAAS,IAAI,kBAAkB,IAAI,GAAG,KAAK,GAAI;AACpD,SAAK,OAAO,SAAS,IAAI,GAAG,GAAG,CAAC;AAGhC,SAAK,OAAO,OAAO,OAAO,CAAC;AAG3B,SAAK,SAAS,SAAS,cAAc,QAAQ;AAC7C,SAAK,WAAW,IAAI,cAAc;AAAA,MAChC,QAAQ,KAAK;AAAA,MACb,WAAW;AAAA,MACX,OAAO;AAAA,IAAA,CACR;AACD,SAAK,SAAS,cAAc,KAAK,IAAI,OAAO,kBAAkB,CAAC,CAAC;AAChE,SAAK,SAAS,GAAG,UAAU;AAG3B,gBAAY,YAAY,KAAK,MAAM;AAGnC,QAAI,OAAO,yBAAyB,aAAa;AAC/C,WAAK,uBAAuB,IAAI;AAAA,QAC9B,CAAC,YAAY;AACX,eAAK,YAAY,QAAQ,CAAC,GAAG,kBAAkB;AAAA,QACjD;AAAA,QACA,EAAE,WAAW,IAAA;AAAA,MAAI;AAEnB,WAAK,qBAAqB,QAAQ,WAAW;AAAA,IAC/C;AAGA,UAAM,aAAa,OAAO,SAAS,cAAc,OAAO,MAAM,IAAI;AAClE,UAAM,cAAc,aAAa,CAAC,WAAW,SAAS;AAEtD,SAAK,mBAAmB,IAAI,iBAAiB,KAAK,QAAQ,aAAa,OAAO;AAAA,MAC5E,UAAU,OAAO;AAAA,MACjB,UAAU,OAAO;AAAA,MACjB,aAAa,OAAO;AAAA,MACpB,gBAAgB,OAAO;AAAA,MACvB,gBAAgB,OAAO;AAAA,MACvB;AAAA,MACA,MAAM,YAAY;AAAA,IAAA,CACnB;AAGD,SAAK,OAAA;AAAA,EACP;AAAA;AAAA,EAGA,WAAW,cAAgC,QAAwB;AACjE,UAAM,aAAa,cAAc,MAAM;AACvC,QAAI,CAAC,YAAY;AACf,cAAQ,MAAM,2BAA2B,MAAM,EAAE;AACjD;AAAA,IACF;AAGA,SAAK,eAAe,IAAI,aAAa,YAAY;AACjD,SAAK,aAAa,aAAa;AAC/B,SAAK,aAAa,YAAY;AAC9B,SAAK,aAAa,YAAY;AAC9B,SAAK,aAAa,kBAAkB;AAGpC,SAAK,cAAc,YAAY,KAAK,YAAY;AAAA,EAClD;AAAA;AAAA,EAGA,WAAW,SAAkB,QAAwB;AACnD,UAAM,aAAa,cAAc,MAAM;AACvC,QAAI,CAAC,YAAY;AACf,cAAQ,MAAM,2BAA2B,MAAM,EAAE;AACjD;AAAA,IACF;AAEA,SAAK,cAAc,YAAY,OAAO;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,sBAAsB,KAAa,QAAiC;AACxE,UAAM,aAAa,cAAc,MAAM;AACvC,QAAI,CAAC,YAAY;AACf,cAAQ,MAAM,2BAA2B,MAAM,EAAE;AACjD;AAAA,IACF;AAEA,QAAI;AACF,YAAM,EAAE,WAAA,IAAe,MAAM,OAAO,qBAA0C,EAAA,KAAA,OAAA,EAAA,CAAA;AAC9E,YAAM,SAAS,IAAI,WAAA;AACnB,aAAO,kBAAkB,qEAAqE;AAC9F,aAAO,cAAc,KAAK,QAAQ;AAElC,YAAM,UAAU,MAAM,OAAO,UAAU,GAAG;AAC1C,WAAK,cAAc,YAAY,OAAO;AACtC,aAAO,QAAA;AAAA,IACT,SAAS,KAAK;AACZ,cAAQ,KAAK,kEAAkE,GAAG;AAAA,IACpF;AAAA,EACF;AAAA;AAAA,EAGA,QAAc;AACZ,QAAI,KAAK,UAAW;AACpB,SAAK,YAAY;AACjB,SAAK,SAAS,iBAAiB,KAAK,KAAK,KAAK,IAAI,CAAC;AAAA,EACrD;AAAA;AAAA,EAGA,OAAa;AACX,SAAK,YAAY;AACjB,SAAK,SAAS,iBAAiB,IAAI;AAAA,EACrC;AAAA;AAAA,EAGA,SAAe;AACb,UAAM,OAAO,KAAK,YAAY,sBAAA;AAC9B,UAAM,QAAQ,KAAK;AACnB,UAAM,SAAS,KAAK;AAEpB,QAAI,UAAU,KAAK,WAAW,EAAG;AAEjC,SAAK,OAAO,SAAS,QAAQ;AAC7B,SAAK,OAAO,uBAAA;AACZ,SAAK,SAAS,QAAQ,OAAO,QAAQ,KAAK;AAAA,EAC5C;AAAA;AAAA,EAGA,mBAAkC;AAChC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,YAA+B;AAC7B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,sBAAwC;AACtC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,WAAkB;AAChB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,gBAAgB,SAA6B;AAC3C,SAAK,eAAe;AAAA,EACtB;AAAA;AAAA,EAGA,UAAgB;AACd,SAAK,KAAA;AACL,SAAK,sBAAsB,WAAA;AAC3B,SAAK,uBAAuB;AAC5B,SAAK,iBAAiB,QAAA;AACtB,SAAK,YAAY,QAAA;AACjB,SAAK,qBAAA;AACL,SAAK,cAAc,QAAA;AACnB,SAAK,SAAS,QAAA;AACd,SAAK,SAAS,iBAAA;AAGd,SAAK,OAAO,OAAA;AAAA,EACd;AAAA;AAAA,EAGA,oBAAoB,YAAyB,SAAkB,YAA8B;AAC3F,SAAK,YAAY,QAAA;AACjB,SAAK,qBAAA;AAEL,SAAK,aAAa;AAClB,eAAW,gBAAgB,KAAK,MAAM;AACtC,UAAM,SAAS,WAAW,aAAa,SAAS,UAAU;AAC1D,eAAW,QAAQ,QAAQ;AACzB,WAAK,MAAM,IAAI,IAAI;AAAA,IACrB;AAEA,SAAK,MAAM,IAAI,cAAc,UAAU;AAAA,EACzC;AAAA;AAAA,EAGQ,uBAA6B;AACnC,SAAK,MAAM,SAAS,CAAC,QAAQ;AAC3B,UAAI,eAAe,MAAM;AACvB,YAAI,UAAU,QAAA;AACd,cAAM,YAAY,MAAM,QAAQ,IAAI,QAAQ,IAAI,IAAI,WAAW,CAAC,IAAI,QAAQ;AAC5E,mBAAW,OAAO,WAAyB;AAEzC,cAAI,SAAS,OAAQ,IAAgC,KAAK;AACtD,gBAAgC,IAAgB,QAAA;AAAA,UACpD;AAIA,gBAAM,WAAY,IAA2C;AAG7D,cAAI,UAAU;AACZ,uBAAW,OAAO,OAAO,KAAK,QAAQ,GAAG;AACvC,oBAAM,QAAQ,SAAS,GAAG,GAAG;AAC7B,kBAAI,SAAS,MAAM,aAAa,OAAO,MAAM,YAAY,YAAY;AACnE,sBAAM,QAAA;AAAA,cACR;AAAA,YACF;AAAA,UACF;AACA,cAAI,QAAA;AAAA,QACN;AAAA,MACF;AAAA,IACF,CAAC;AACD,WAAO,KAAK,MAAM,SAAS,SAAS,GAAG;AACrC,WAAK,MAAM,OAAO,KAAK,MAAM,SAAS,CAAC,CAAE;AAAA,IAC3C;AAAA,EACF;AAAA,EAEQ,cAAc,YAAwB,SAAwB;AAEpE,SAAK,YAAY,QAAA;AACjB,SAAK,qBAAA;AAGL,SAAK,aAAa,KAAK,iBAAiB,UAAU;AAClD,SAAK,WAAW,gBAAgB,KAAK,MAAM;AAG3C,UAAM,SAAS,KAAK,WAAW,aAAa,SAAS,WAAW,UAAU;AAC1E,eAAW,QAAQ,QAAQ;AACzB,WAAK,MAAM,IAAI,IAAI;AAAA,IACrB;AAGA,SAAK,MAAM,MAAM;AAAA,MACf,QAAQ,WAAW;AAAA,MACnB,YAAY,WAAW;AAAA,IAAA,CACxB;AAAA,EACH;AAAA,EAEQ,iBAAiB,YAAqC;AAC5D,YAAQ,WAAW,YAAA;AAAA,MACjB,KAAK;AAAA,MACL,KAAK;AACH,eAAO,IAAI,mBAAmB,WAAW,IAAI;AAAA,MAC/C,KAAK;AACH,eAAO,IAAI,eAAA;AAAA,MACb,KAAK;AACH,eAAO,IAAI,kBAAA;AAAA,MACb,KAAK;AACH,eAAO,IAAI,cAAA;AAAA,MACb,KAAK;AACH,eAAO,IAAI,kBAAA;AAAA,MACb;AACE,eAAO,IAAI,mBAAmB,GAAG;AAAA,IAAA;AAAA,EAEvC;AAAA,EAEQ,KAAK,YAAoB,UAA0B;AACzD,QAAI,CAAC,KAAK,UAAW;AAGrB,QAAI,CAAC,KAAK,UAAW;AAGrB,SAAK,gBAAgB,UAAU;AAG/B,SAAK,iBAAiB,OAAA;AAGtB,SAAK,YAAY,OAAO,KAAK,QAAQ,UAAU;AAG/C,SAAK,cAAc,kBAAkB,KAAK,MAAM;AAGhD,QAAI,KAAK,cAAc;AACrB,YAAM,QAAQ,KAAK,aAAa;AAChC,UAAI,SAAS,CAAC,MAAM,UAAU,MAAM,cAAc,GAAG;AACnD,aAAK,aAAa,cAAc;AAAA,MAClC;AAAA,IACF;AAGA,SAAK,SAAS,OAAO,KAAK,OAAO,KAAK,MAAM;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,gBAAgB,WAAyB;AAC/C,QAAI,KAAK,gBAAgB,GAAG;AAC1B,YAAM,QAAQ,YAAY,KAAK;AAC/B,UAAI,QAAQ,GAAG;AACb,aAAK,WAAW,KAAK,MAAO,KAAK;AAAA,MACnC;AAAA,IACF;AACA,SAAK,gBAAgB;AAErB,QAAI,KAAK,WAAW,SAAS,GAAI;AAEjC,UAAM,SAAS,KAAK,WAAW,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC,IAAI,KAAK,WAAW;AAC5E,SAAK,WAAW,SAAS;AAEzB,UAAM,UAAU,KAAK,SAAS,cAAA;AAC9B,UAAM,WAAW,KAAK,IAAI,OAAO,kBAAkB,CAAC;AAEpD,QAAI,SAAS,MAAM,UAAU,MAAM;AACjC,WAAK,SAAS,cAAc,KAAK,IAAI,MAAM,UAAU,IAAI,CAAC;AAAA,IAC5D,WAAW,SAAS,MAAM,UAAU,UAAU;AAC5C,WAAK,SAAS,cAAc,KAAK,IAAI,UAAU,UAAU,IAAI,CAAC;AAAA,IAChE;AAAA,EACF;AACF;AC9WO,MAAM,mBAAgD;AAAA,EACnD,eAAwC;AAAA,EACxC,cAAc;AAAA,EAEtB,IAAI,cAAuB;AACzB,WAAO;AAAA,EACT;AAAA,EAEA,IAAI,aAAsB;AACxB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,OAAO,cAAsC;AAC3C,SAAK,eAAe;AACpB,SAAK,cAAc;AAAA,EACrB;AAAA,EAEA,KAAK,KAAmB;AACtB,QAAI,CAAC,KAAK,aAAc;AACxB,SAAK,aAAa,MAAM;AAAA,EAC1B;AAAA,EAEA,mBAAmC;AACjC,WAAO,CAAA;AAAA,EACT;AAAA,EAEA,gBAAgB,QAAsB;AAAA,EAEtC;AAAA,EAEA,UAAgB;AACd,QAAI,KAAK,cAAc;AACrB,WAAK,aAAa,gBAAgB,KAAK;AAAA,IACzC;AACA,SAAK,eAAe;AACpB,SAAK,cAAc;AAAA,EACrB;AACF;ACpCA,eAAsB,cAAc,MAA+C;AACjF,UAAQ,MAAA;AAAA,IACN,KAAK,OAAO;AACV,YAAM,EAAE,WAAA,IAAe,MAAM,OAAO,2BAAkB;AACtD,aAAO,IAAI,WAAA;AAAA,IACb;AAAA,IACA,KAAK,QAAQ;AACX,YAAM,EAAE,YAAA,IAAgB,MAAM,OAAO,4BAAmB;AACxD,aAAO,IAAI,YAAA;AAAA,IACb;AAAA,IACA,KAAK;AAAA,IACL;AACE,aAAO,IAAI,mBAAA;AAAA,EAAmB;AAEpC;ACPO,MAAM,cAAc;AAAA,EACjB,UAAoC;AAAA,EACpC,eAAwC;AAAA,EACxC,QAA4B;AAAA,EAC5B,SAAgC;AAAA,EAChC,UAA0B,CAAA;AAAA;AAAA;AAAA;AAAA,EAKlC,MAAM,YACJ,SACA,cACA,OACA,QACe;AACf,SAAK,UAAU;AACf,SAAK,eAAe;AACpB,SAAK,QAAQ;AACb,SAAK,SAAS;AAEd,QAAI,QAAQ,WAAW,EAAG;AAG1B,SAAK,SAAS,QAAA;AAGd,UAAM,EAAE,aAAa,OAAA,IAAW,KAAK,aAAa,OAAO;AAGzD,SAAK,UAAU,MAAM,cAAc,WAAW;AAC9C,SAAK,QAAQ,OAAO,YAAY;AAChC,SAAK,QAAQ,KAAK,OAAO,GAAG;AAG5B,SAAK,kBAAA;AAAA,EACP;AAAA;AAAA,EAGA,mBAAmC;AACjC,WAAO,KAAK,SAAS,iBAAA,KAAsB,CAAA;AAAA,EAC7C;AAAA;AAAA,EAGA,gBAAgB,OAAqB;AACnC,QAAI,CAAC,KAAK,QAAS;AACnB,SAAK,QAAQ,gBAAgB,KAAK;AAElC,UAAM,SAAS,KAAK,QAAQ,iBAAA;AAC5B,SAAK,OAAO,MAAM;AAAA,MAChB,oBAAoB;AAAA,MACpB,gBAAgB,UAAU,KAAK,SAAU,OAAO,KAAK,GAAG,SAAS;AAAA,MACjE,aAAa,UAAU;AAAA,IAAA,CACxB;AAED,SAAK,QAAQ,KAAK,qBAAqB;AAAA,MACrC,SAAS,UAAU,KAAK,SAAU,OAAO,KAAK,GAAG,SAAS;AAAA,MAC1D,MAAM,UAAU;AAAA,IAAA,CACjB;AAAA,EACH;AAAA;AAAA,EAGA,IAAI,cAAuB;AACzB,WAAO,KAAK,SAAS,eAAe;AAAA,EACtC;AAAA;AAAA,EAGA,aAAa,OAAqB;AAChC,QAAI,CAAC,KAAK,gBAAgB,CAAC,KAAK,WAAW,SAAS,KAAK,QAAQ,OAAQ;AAEzE,UAAM,cAAc,KAAK,aAAa;AACtC,UAAM,aAAa,CAAC,KAAK,aAAa;AAEtC,SAAK,QAAQ,KAAK,KAAK,QAAQ,KAAK,EAAG,GAAG;AAG1C,UAAM,WAAW,MAAM;AACrB,WAAK,aAAc,cAAc;AACjC,UAAI,WAAY,MAAK,aAAc,KAAA,EAAO,MAAM,MAAM;AAAA,MAAC,CAAC;AACxD,WAAK,aAAc,oBAAoB,cAAc,QAAQ;AAAA,IAC/D;AACA,SAAK,aAAa,iBAAiB,cAAc,QAAQ;AAAA,EAC3D;AAAA;AAAA,EAGA,UAAgB;AACd,SAAK,SAAS,QAAA;AACd,SAAK,UAAU;AACf,SAAK,eAAe;AACpB,SAAK,QAAQ;AACb,SAAK,SAAS;AACd,SAAK,UAAU,CAAA;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,aAAa,SAA6E;AAEhG,UAAM,YAAY,QAAQ,KAAK,CAAC,MAAM,KAAK,kBAAkB,CAAC,MAAM,KAAK;AACzE,QAAI,UAAW,QAAO,EAAE,aAAa,OAAO,QAAQ,UAAA;AAGpD,UAAM,aAAa,QAAQ,KAAK,CAAC,MAAM,KAAK,kBAAkB,CAAC,MAAM,MAAM;AAC3E,QAAI,WAAY,QAAO,EAAE,aAAa,QAAQ,QAAQ,WAAA;AAGtD,WAAO,EAAE,aAAa,eAAe,QAAQ,QAAQ,CAAC,EAAA;AAAA,EACxD;AAAA;AAAA,EAGQ,kBAAkB,QAAmC;AAC3D,UAAM,OAAO,OAAO,KAAK,YAAA;AACzB,UAAM,MAAM,OAAO,IAAI,YAAA;AAEvB,QACE,SAAS,2BACT,SAAS,mCACT,IAAI,SAAS,OAAO,GACpB;AACA,aAAO;AAAA,IACT;AAEA,QAAI,SAAS,0BAA0B,IAAI,SAAS,MAAM,GAAG;AAC3D,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,oBAA0B;AAChC,QAAI,CAAC,KAAK,WAAW,CAAC,KAAK,MAAO;AAElC,UAAM,QAAQ,MAAM;AAClB,UAAI,CAAC,KAAK,WAAW,CAAC,KAAK,MAAO;AAClC,YAAM,SAAS,KAAK,QAAQ,iBAAA;AAC5B,UAAI,OAAO,SAAS,GAAG;AACrB,aAAK,MAAM,MAAM;AAAA,UACf,oBAAoB;AAAA,UACpB,gBAAgB;AAAA,UAChB,aAAa;AAAA,QAAA,CACd;AAAA,MACH;AAAA,IACF;AAGA,UAAA;AACA,eAAW,OAAO,GAAI;AACtB,eAAW,OAAO,GAAI;AAAA,EACxB;AACF;ACzKO,SAAS,kBAAkB,SAAqC;AACrE,MAAI,QAAQ,mBAAmB;AAC7B,WAAO,QAAQ,kBAAA;AAAA,EACjB;AAEA,QAAM,KAAK;AACX,MAAI,GAAG,yBAAyB;AAC9B,WAAO,GAAG,wBAAA;AAAA,EACZ;AACA,SAAO,QAAQ,OAAO,IAAI,MAAM,0BAA0B,CAAC;AAC7D;AAEO,SAAS,iBAAgC;AAC9C,MAAI,SAAS,gBAAgB;AAC3B,WAAO,SAAS,eAAA;AAAA,EAClB;AACA,QAAM,MAAM;AACZ,MAAI,IAAI,sBAAsB;AAC5B,WAAO,IAAI,qBAAA;AAAA,EACb;AACA,SAAO,QAAQ,OAAO,IAAI,MAAM,0BAA0B,CAAC;AAC7D;AAEO,SAAS,eAAwB;AACtC,SAAO,CAAC,CAAC,SAAS;AACpB;AAEO,SAAS,mBAAmB,UAAkC;AACnE,WAAS,iBAAiB,oBAAoB,QAAQ;AACtD,WAAS,iBAAiB,0BAA0B,QAAQ;AAC5D,SAAO,MAAM;AACX,aAAS,oBAAoB,oBAAoB,QAAQ;AACzD,aAAS,oBAAoB,0BAA0B,QAAQ;AAAA,EACjE;AACF;ACtBO,MAAM,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAA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"}