@goboss/web-video-player-sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of @goboss/web-video-player-sdk might be problematic. Click here for more details.

@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../analytics/src/analytics.ts","../../analytics/src/firebase-provider.ts","../../chapters/src/chapters.ts","../../subtitles/src/subtitles.ts","../../drm/src/drm.ts","../../casting/src/casting.ts","../../thumbnails/src/thumbnails.ts","../../core-player/src/core-player.ts","../../ui-controls/src/WebVideoPlayer.tsx","../src/create-player.ts"],"sourcesContent":["import type { AnalyticsCallback, AnalyticsEvent } from '@goboss/types';\n\ntype MilestoneEvent = 'first_quartile' | 'midpoint' | 'third_quartile';\n\nexport class AnalyticsModule {\n private callback?: AnalyticsCallback;\n private heartbeatInterval?: number;\n private isBuffering: boolean = false;\n\n // Session state\n private sessionId: string;\n private videoId: string;\n private deviceType: string;\n private networkType: string;\n\n // Watch time accumulation\n private isFirstPlay: boolean = true;\n private watchStartTime: number | null = null;\n private totalWatchTime: number = 0;\n\n // Buffering metrics\n private bufferingCount: number = 0;\n private totalBufferingTime: number = 0;\n private bufferingStartTime: number | null = null;\n\n // Engagement milestones\n private milestonesHit = new Set<MilestoneEvent>();\n\n // Stall detection\n private lastHeartbeatPosition: number | null = null;\n private lastHeartbeatAt: number | null = null;\n\n constructor(callback?: AnalyticsCallback, src?: string) {\n this.callback = callback;\n this.sessionId = this.generateSessionId();\n this.videoId = src ? this.extractVideoId(src) : '';\n this.deviceType = this.getDeviceType();\n this.networkType = this.getNetworkType();\n }\n\n public registerCallback(callback: AnalyticsCallback) {\n this.callback = callback;\n }\n\n public track(\n eventType: AnalyticsEvent['eventType'],\n videoEl: HTMLVideoElement,\n extra: Partial<AnalyticsEvent> = {}\n ) {\n // Fire session_start before the very first play event\n if (eventType === 'play' && this.isFirstPlay) {\n this.isFirstPlay = false;\n this.dispatchRaw({\n eventType: 'session_start',\n timestamp: Date.now(),\n currentTime: videoEl.currentTime,\n duration: videoEl.duration || 0,\n sessionId: this.sessionId,\n videoId: this.videoId,\n deviceType: this.deviceType,\n networkType: this.networkType,\n watchPercentage: 0,\n totalWatchTime: 0,\n bufferingCount: 0,\n totalBufferingTime: 0,\n });\n }\n\n // Accumulate watch time on play/pause/ended\n if (eventType === 'play') {\n this.watchStartTime = Date.now();\n }\n if ((eventType === 'pause' || eventType === 'ended') && this.watchStartTime !== null) {\n this.totalWatchTime += (Date.now() - this.watchStartTime) / 1000;\n this.watchStartTime = null;\n }\n\n const watchPct = videoEl.duration\n ? Math.min(100, Math.round((videoEl.currentTime / videoEl.duration) * 100))\n : 0;\n\n const event: AnalyticsEvent = {\n eventType,\n timestamp: Date.now(),\n currentTime: videoEl.currentTime,\n duration: videoEl.duration || 0,\n volume: videoEl.volume,\n speed: videoEl.playbackRate,\n sessionId: this.sessionId,\n videoId: this.videoId,\n deviceType: this.deviceType,\n networkType: this.networkType,\n watchPercentage: watchPct,\n totalWatchTime: Math.round(this.totalWatchTime),\n bufferingCount: this.bufferingCount,\n totalBufferingTime: Math.round(this.totalBufferingTime),\n ...extra,\n };\n\n this.dispatchRaw(event);\n }\n\n public startHeartbeat(videoEl: HTMLVideoElement, getExtra: () => Partial<AnalyticsEvent>) {\n this.stopHeartbeat();\n this.heartbeatInterval = window.setInterval(() => {\n if (!videoEl.paused && !this.isBuffering) {\n this.checkMilestones(videoEl, getExtra);\n this.checkStall(videoEl, getExtra);\n this.track('heartbeat', videoEl, getExtra());\n }\n this.lastHeartbeatPosition = videoEl.currentTime;\n this.lastHeartbeatAt = Date.now();\n }, 5000);\n }\n\n public stopHeartbeat() {\n if (this.heartbeatInterval) {\n window.clearInterval(this.heartbeatInterval);\n this.heartbeatInterval = undefined;\n }\n }\n\n public setBuffering(\n isBuffering: boolean,\n videoEl: HTMLVideoElement,\n getExtra: () => Partial<AnalyticsEvent>\n ) {\n if (this.isBuffering === isBuffering) return;\n this.isBuffering = isBuffering;\n\n if (isBuffering) {\n this.bufferingCount++;\n this.bufferingStartTime = Date.now();\n } else if (this.bufferingStartTime !== null) {\n this.totalBufferingTime += (Date.now() - this.bufferingStartTime) / 1000;\n this.bufferingStartTime = null;\n }\n\n this.track(isBuffering ? 'buffering_start' : 'buffering_end', videoEl, getExtra());\n }\n\n // Checks 25/50/75% milestones — called every heartbeat tick\n private checkMilestones(videoEl: HTMLVideoElement, getExtra: () => Partial<AnalyticsEvent>) {\n const { currentTime, duration } = videoEl;\n if (!duration) return;\n const ratio = currentTime / duration;\n\n const thresholds: Array<[number, MilestoneEvent]> = [\n [0.25, 'first_quartile'],\n [0.5, 'midpoint'],\n [0.75, 'third_quartile'],\n ];\n\n for (const [threshold, eventType] of thresholds) {\n if (ratio >= threshold && !this.milestonesHit.has(eventType)) {\n this.milestonesHit.add(eventType);\n this.track(eventType, videoEl, {\n ...getExtra(),\n watchPercentage: threshold * 100,\n });\n }\n }\n }\n\n // Detects stalls: video position hasn't advanced between heartbeats while playing\n private checkStall(videoEl: HTMLVideoElement, getExtra: () => Partial<AnalyticsEvent>) {\n if (\n this.lastHeartbeatPosition !== null &&\n this.lastHeartbeatAt !== null &&\n videoEl.currentTime - this.lastHeartbeatPosition < 0.1 &&\n Date.now() - this.lastHeartbeatAt >= 4500\n ) {\n this.track('stall', videoEl, getExtra());\n }\n }\n\n private dispatchRaw(event: AnalyticsEvent) {\n if (this.callback) {\n try {\n this.callback(event);\n } catch (err) {\n console.error('[PlayerSDK Analytics] Callback failed:', err);\n }\n }\n }\n\n private generateSessionId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n return `sess-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;\n }\n\n private extractVideoId(src: string): string {\n try {\n const url = new URL(src);\n const parts = url.pathname.split('/').filter(Boolean);\n // Remove filename (last segment if it has an extension)\n if (parts.length > 0 && parts[parts.length - 1].includes('.')) {\n parts.pop();\n }\n return parts[parts.length - 1] || url.hostname;\n } catch {\n const parts = src.split('/').filter(Boolean);\n if (parts.length >= 2 && parts[parts.length - 1].includes('.')) {\n return parts[parts.length - 2];\n }\n return parts[parts.length - 1] || 'unknown';\n }\n }\n\n private getDeviceType(): string {\n const ua = navigator.userAgent;\n if (/mobile/i.test(ua)) return 'mobile';\n if (/tablet|ipad/i.test(ua)) return 'tablet';\n return 'desktop';\n }\n\n private getNetworkType(): string {\n const conn =\n (navigator as any).connection ||\n (navigator as any).mozConnection ||\n (navigator as any).webkitConnection;\n if (conn) return conn.effectiveType || conn.type || 'unknown';\n return 'unknown';\n }\n}\n","import { initializeApp, getApps } from 'firebase/app';\nimport { getAnalytics, logEvent, isSupported } from 'firebase/analytics';\nimport type { Analytics } from 'firebase/analytics';\nimport type { AnalyticsEvent, AnalyticsCallback } from '@goboss/types';\n\nexport interface FirebaseConfig {\n apiKey: string;\n authDomain: string;\n projectId: string;\n storageBucket: string;\n messagingSenderId: string;\n appId: string;\n measurementId?: string;\n}\n\n// Firebase Analytics reserves these names — remap them to avoid conflicts\nconst RESERVED_EVENT_NAMES: Record<string, string> = {\n session_start: 'player_session_start',\n error: 'player_error',\n};\n\n// Heartbeat is internal bookkeeping — too noisy for an analytics dashboard\nconst SKIP_EVENTS = new Set(['heartbeat']);\n\n/**\n * Sends all player analytics events to Firebase Analytics (GA4).\n *\n * Events appear in:\n * Firebase Console → Analytics → Events (24-48 hr aggregated view)\n * Firebase Console → Analytics → DebugView (real-time while testing)\n * Google Analytics 4 → Reports → Realtime (within a few minutes)\n *\n * No Firestore / database setup required — Firebase Analytics handles all storage.\n */\nexport class FirebaseAnalyticsProvider {\n private analytics: Analytics | null = null;\n\n constructor(config: FirebaseConfig) {\n this.init(config);\n }\n\n private async init(config: FirebaseConfig): Promise<void> {\n try {\n const supported = await isSupported();\n if (!supported) {\n console.warn('[FirebaseAnalytics] Not supported in this environment.');\n return;\n }\n const apps = getApps();\n const app = apps.length === 0 ? initializeApp(config) : apps[0];\n this.analytics = getAnalytics(app);\n } catch (err) {\n console.error('[FirebaseAnalytics] Initialization failed:', err);\n }\n }\n\n public createCallback(): AnalyticsCallback {\n return (event: AnalyticsEvent) => this.handleEvent(event);\n }\n\n private handleEvent(event: AnalyticsEvent): void {\n if (!this.analytics) return;\n if (SKIP_EVENTS.has(event.eventType)) return;\n\n const name = RESERVED_EVENT_NAMES[event.eventType] ?? event.eventType;\n const params = this.buildParams(event);\n\n try {\n logEvent(this.analytics, name, params);\n } catch (err) {\n console.error('[FirebaseAnalytics] logEvent failed:', err);\n }\n }\n\n private buildParams(event: AnalyticsEvent): Record<string, string | number> {\n // Firebase Analytics allows max 25 params per event.\n // All param names must be snake_case, max 40 chars.\n // String values max 100 chars. Numbers are fine as-is.\n const p: Record<string, string | number> = {\n // Session context\n session_id: (event.sessionId ?? '').slice(0, 100),\n video_id: (event.videoId ?? '').slice(0, 100),\n device_type: event.deviceType ?? 'unknown',\n network_type: event.networkType ?? 'unknown',\n\n // Timing\n current_time: Math.round(event.currentTime),\n duration: Math.round(event.duration),\n\n // Engagement\n watch_percentage: event.watchPercentage ?? 0,\n total_watch_time: event.totalWatchTime ?? 0,\n buffering_count: event.bufferingCount ?? 0,\n total_buffering_time: event.totalBufferingTime ?? 0,\n };\n\n // Quality info\n if (event.rendition) p.rendition = event.rendition.slice(0, 100);\n if (event.bitrate) p.bitrate = event.bitrate;\n if (event.fromRendition) p.from_rendition = event.fromRendition.slice(0, 100);\n\n // Playback state\n if (event.volume !== undefined) p.volume = Math.round(event.volume * 100);\n if (event.speed !== undefined) p.speed = event.speed;\n\n // Error details\n if (event.errorMessage) p.error_message = event.errorMessage.slice(0, 100);\n if (event.errorCode) p.error_code = event.errorCode;\n\n // Seek\n if (event.seekFrom !== undefined) p.seek_from = Math.round(event.seekFrom);\n\n // PiP\n if (event.pipState) p.pip_state = event.pipState;\n\n return p;\n }\n\n // No-op — Firebase Analytics is fire-and-forget, nothing to flush or close\n public destroy(): Promise<void> {\n return Promise.resolve();\n }\n}\n","import type { KeyMoment } from '@goboss/types';\r\n\r\nexport class KeyMomentsModule {\r\n private keyMoments: KeyMoment[] = [];\r\n private currentMomentIndex: number = -1;\r\n private onMomentChangeCallback?: (moment: KeyMoment | null, index: number) => void;\r\n\r\n constructor(keyMoments: KeyMoment[] = []) {\r\n this.setKeyMoments(keyMoments);\r\n }\r\n\r\n /**\r\n * Sorts and stores the key moments.\r\n */\r\n public setKeyMoments(keyMoments: KeyMoment[]) {\r\n this.keyMoments = [...keyMoments].sort((a, b) => a.startTime - b.startTime);\r\n this.currentMomentIndex = -1;\r\n }\r\n\r\n public getKeyMoments(): KeyMoment[] {\r\n return this.keyMoments;\r\n }\r\n\r\n /**\r\n * Locates the key moment corresponding to the active playback timestamp.\r\n */\r\n public getCurrentMoment(currentTime: number): { keyMoment: KeyMoment | null; index: number } {\r\n const index = this.keyMoments.findIndex(\r\n (c) => currentTime >= c.startTime && currentTime < c.endTime\r\n );\r\n if (index !== -1) {\r\n return { keyMoment: this.keyMoments[index], index };\r\n }\r\n return { keyMoment: null, index: -1 };\r\n }\r\n\r\n /**\r\n * Evaluates if playback has crossed a key moment boundary.\r\n */\r\n public updateTime(currentTime: number) {\r\n const { keyMoment, index } = this.getCurrentMoment(currentTime);\r\n if (index !== this.currentMomentIndex) {\r\n this.currentMomentIndex = index;\r\n if (this.onMomentChangeCallback) {\r\n this.onMomentChangeCallback(keyMoment, index);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Subscribes a callback to change events.\r\n */\r\n public onMomentChange(callback: (keyMoment: KeyMoment | null, index: number) => void) {\r\n this.onMomentChangeCallback = callback;\r\n }\r\n\r\n // --- Fallback/Deprecated compatibility wrappers for 'chapters' ---\r\n public setChapters(chapters: KeyMoment[]) {\r\n this.setKeyMoments(chapters);\r\n }\r\n public getChapters(): KeyMoment[] {\r\n return this.getKeyMoments();\r\n }\r\n public getCurrentChapter(currentTime: number): { chapter: KeyMoment | null; index: number } {\r\n const res = this.getCurrentMoment(currentTime);\r\n return { chapter: res.keyMoment, index: res.index };\r\n }\r\n public onChapterChange(callback: (chapter: KeyMoment | null, index: number) => void) {\r\n this.onMomentChange(callback);\r\n }\r\n}\r\n\r\n// Keep export alias for backward compatibility\r\nexport class ChaptersModule extends KeyMomentsModule {}\r\n","import type { SubtitleTrack } from '@goboss/types';\r\n\r\ninterface SubtitleCue {\r\n startTime: number;\r\n endTime: number;\r\n text: string;\r\n}\r\n\r\nexport class SubtitlesModule {\r\n private tracks: SubtitleTrack[] = [];\r\n private activeTrackId: string = 'off';\r\n private currentCues: SubtitleCue[] = [];\r\n private activeText: string = '';\r\n private onSubtitleChangeCallback?: (text: string) => void;\r\n private onTrackSwitchCallback?: (track: SubtitleTrack | null) => void;\r\n\r\n constructor(tracks: SubtitleTrack[] = []) {\r\n this.tracks = tracks;\r\n const defaultTrack = tracks.find((t) => t.isDefault);\r\n if (defaultTrack) {\r\n this.setActiveTrack(defaultTrack.id);\r\n }\r\n }\r\n\r\n public getTracks(): SubtitleTrack[] {\r\n return this.tracks;\r\n }\r\n\r\n public getActiveTrackId(): string {\r\n return this.activeTrackId;\r\n }\r\n\r\n public setHlsTracks(hlsTracks: SubtitleTrack[]): void {\r\n const externalTracks = this.tracks.filter((t) => !t.isHls && !t.isNative);\r\n this.tracks = [...externalTracks, ...hlsTracks];\r\n }\r\n\r\n public addTrack(track: SubtitleTrack): void {\r\n if (!this.tracks.find((t) => t.id === track.id)) {\r\n this.tracks.push(track);\r\n }\r\n }\r\n\r\n public onTrackSwitch(callback: (track: SubtitleTrack | null) => void) {\r\n this.onTrackSwitchCallback = callback;\r\n }\r\n\r\n public setCustomSubtitleText(text: string) {\r\n if (text !== this.activeText) {\r\n this.activeText = text;\r\n if (this.onSubtitleChangeCallback) {\r\n this.onSubtitleChangeCallback(text);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Switches the active track. Fetches external tracks if they need loading.\r\n */\r\n public async setActiveTrack(trackId: string): Promise<void> {\r\n this.activeTrackId = trackId;\r\n if (trackId === 'off') {\r\n this.currentCues = [];\r\n this.updateActiveText(0);\r\n if (this.onTrackSwitchCallback) {\r\n this.onTrackSwitchCallback(null);\r\n }\r\n return;\r\n }\r\n\r\n const track = this.tracks.find((t) => t.id === trackId);\r\n if (!track) {\r\n this.currentCues = [];\r\n this.updateActiveText(0);\r\n if (this.onTrackSwitchCallback) {\r\n this.onTrackSwitchCallback(null);\r\n }\r\n return;\r\n }\r\n\r\n if (track.isHls || track.isNative) {\r\n this.currentCues = [];\r\n this.updateActiveText(0);\r\n if (this.onTrackSwitchCallback) {\r\n this.onTrackSwitchCallback(track);\r\n }\r\n return;\r\n }\r\n\r\n if (this.onTrackSwitchCallback) {\r\n this.onTrackSwitchCallback(null);\r\n }\r\n\r\n // If pre-loaded cues exist, use them\r\n if (track.cues) {\r\n this.currentCues = track.cues;\r\n return;\r\n }\r\n\r\n // If we have a URL, fetch and parse it\r\n if (track.src) {\r\n try {\r\n const response = await fetch(track.src);\r\n if (!response.ok) throw new Error(`HTTP error ${response.status}`);\r\n const text = await response.text();\r\n const parsed = this.parseVttOrSrt(text);\r\n track.cues = parsed; // Cache it\r\n if (this.activeTrackId === trackId) {\r\n this.currentCues = parsed;\r\n }\r\n } catch (err) {\r\n console.error(`[PlayerSDK Subtitles] Failed to fetch track ${track.label}:`, err);\r\n this.currentCues = [];\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Scans the subtitle cues for the current playback position.\r\n */\r\n public updateTime(currentTime: number) {\r\n if (this.activeTrackId === 'off' || this.currentCues.length === 0) {\r\n // HLS tracks are updated externally via setCustomSubtitleText, so only clear if not in an HLS/native track\r\n const track = this.tracks.find((t) => t.id === this.activeTrackId);\r\n if (!track || (!track.isHls && !track.isNative)) {\r\n if (this.activeText !== '') {\r\n this.activeText = '';\r\n if (this.onSubtitleChangeCallback) this.onSubtitleChangeCallback('');\r\n }\r\n }\r\n return;\r\n }\r\n\r\n this.updateActiveText(currentTime);\r\n }\r\n\r\n /**\r\n * Triggers the text update callback if a change in caption is detected.\r\n */\r\n private updateActiveText(currentTime: number) {\r\n // Find matching cues\r\n const matchingCues = this.currentCues.filter(\r\n (cue) => currentTime >= cue.startTime && currentTime <= cue.endTime\r\n );\r\n\r\n const newText = matchingCues.map((c) => c.text).join('<br />');\r\n\r\n if (newText !== this.activeText) {\r\n this.activeText = newText;\r\n if (this.onSubtitleChangeCallback) {\r\n this.onSubtitleChangeCallback(newText);\r\n }\r\n }\r\n }\r\n\r\n public onSubtitleChange(callback: (text: string) => void) {\r\n this.onSubtitleChangeCallback = callback;\r\n }\r\n\r\n /**\r\n * Robust parser supporting WebVTT and SRT formats.\r\n */\r\n private parseVttOrSrt(rawText: string): SubtitleCue[] {\r\n // Strip UTF-8 BOM if present\r\n // eslint-disable-next-line no-irregular-whitespace\r\n const text = rawText.replace(/^/, '');\r\n\r\n // Bail out immediately if this is an HLS playlist, not a subtitle file\r\n if (text.trimStart().startsWith('#EXTM3U')) return [];\r\n\r\n const cues: SubtitleCue[] = [];\r\n const lines = text.split(/\\r?\\n/);\r\n\r\n let tempStartTime = 0;\r\n let tempEndTime = 0;\r\n let tempTextLines: string[] = [];\r\n let isParsingCue = false;\r\n let inStyleOrRegionBlock = false;\r\n\r\n // HH:MM:SS.mmm or HH:MM:SS,mmm (hours may be 1+ digits)\r\n const timeRegex = /(\\d+):(\\d{2}):(\\d{2})[.,](\\d{3})\\s*-->\\s*(\\d+):(\\d{2}):(\\d{2})[.,](\\d{3})/;\r\n // MM:SS.mmm (no hours)\r\n const shortTimeRegex = /^(\\d{2}):(\\d{2})[.,](\\d{3})\\s*-->\\s*(\\d{2}):(\\d{2})[.,](\\d{3})/;\r\n\r\n const toSec = (h: string, m: string, s: string, ms: string) =>\r\n parseInt(h, 10) * 3600 + parseInt(m, 10) * 60 + parseInt(s, 10) + parseInt(ms, 10) / 1000;\r\n\r\n const stripVttTags = (s: string) =>\r\n s.replace(/<\\d{2}[:.]\\d{2}[:.]\\d{2}\\.\\d{3}>/g, '').replace(/<[^>]+>/g, '');\r\n\r\n const flushCue = () => {\r\n if (isParsingCue && tempTextLines.length > 0) {\r\n cues.push({\r\n startTime: tempStartTime,\r\n endTime: tempEndTime,\r\n text: tempTextLines.map(stripVttTags).join('\\n'),\r\n });\r\n }\r\n tempTextLines = [];\r\n isParsingCue = false;\r\n };\r\n\r\n for (const rawLine of lines) {\r\n const line = rawLine.trim();\r\n\r\n // Blank line ends the current cue\r\n if (!line) {\r\n if (isParsingCue) flushCue();\r\n inStyleOrRegionBlock = false;\r\n continue;\r\n }\r\n\r\n // Skip WEBVTT header, NOTE blocks, X-TIMESTAMP-MAP, STYLE/REGION blocks\r\n const upper = line.toUpperCase();\r\n if (upper === 'WEBVTT' || upper.startsWith('NOTE') ||\r\n upper.startsWith('X-TIMESTAMP-MAP') || upper.startsWith('STYLE') ||\r\n upper.startsWith('REGION')) {\r\n inStyleOrRegionBlock = true;\r\n continue;\r\n }\r\n if (inStyleOrRegionBlock) continue;\r\n\r\n // Try HH:MM:SS timestamp\r\n const match = line.match(timeRegex);\r\n if (match) {\r\n flushCue();\r\n tempStartTime = toSec(match[1], match[2], match[3], match[4]);\r\n tempEndTime = toSec(match[5], match[6], match[7], match[8]);\r\n isParsingCue = true;\r\n continue;\r\n }\r\n\r\n // Try MM:SS timestamp (short form)\r\n const shortMatch = line.match(shortTimeRegex);\r\n if (shortMatch) {\r\n flushCue();\r\n tempStartTime = parseInt(shortMatch[1], 10) * 60 + parseInt(shortMatch[2], 10) + parseInt(shortMatch[3], 10) / 1000;\r\n tempEndTime = parseInt(shortMatch[4], 10) * 60 + parseInt(shortMatch[5], 10) + parseInt(shortMatch[6], 10) / 1000;\r\n isParsingCue = true;\r\n continue;\r\n }\r\n\r\n // Skip SRT sequence numbers and cue identifiers when not inside a cue\r\n if (!isParsingCue) continue;\r\n\r\n tempTextLines.push(line);\r\n }\r\n\r\n // Flush final cue if file lacks a trailing blank line\r\n flushCue();\r\n\r\n return cues;\r\n }\r\n}\r\n","import type { DRMConfig } from '@goboss/types';\r\n\r\nexport class DRMModule {\r\n private config?: DRMConfig;\r\n private watermarkElement?: HTMLDivElement;\r\n private floatInterval?: number;\r\n\r\n constructor(config?: DRMConfig) {\r\n this.config = config;\r\n }\r\n\r\n /**\r\n * Validates if the active origin domain is permitted to load this stream asset.\r\n */\r\n public validateDomain(currentDomain: string): boolean {\r\n if (!this.config || !this.config.allowedDomains || this.config.allowedDomains.length === 0) {\r\n return true;\r\n }\r\n return (\r\n this.config.allowedDomains.includes(currentDomain) ||\r\n this.config.allowedDomains.some((d) => currentDomain.endsWith(d))\r\n );\r\n }\r\n\r\n public getAuthToken(): string | undefined {\r\n return this.config?.authToken;\r\n }\r\n\r\n /**\r\n * Injects an animated floating watermark on top of the media container to obstruct piracy or recording.\r\n */\r\n public setupWatermark(container: HTMLElement) {\r\n // Clear old watermark if active\r\n this.removeWatermark();\r\n\r\n if (!this.config || !this.config.watermarkText) return;\r\n\r\n const watermarkText = this.config.watermarkText;\r\n\r\n const div = document.createElement('div');\r\n div.className = 'sdk-watermark-overlay';\r\n div.innerText = watermarkText;\r\n div.style.position = 'absolute';\r\n div.style.pointerEvents = 'none';\r\n div.style.userSelect = 'none';\r\n div.style.color = 'rgba(255, 255, 255, 0.4)';\r\n div.style.fontSize = '12px';\r\n div.style.fontFamily = 'monospace';\r\n div.style.fontWeight = 'bold';\r\n div.style.zIndex = '9';\r\n div.style.padding = '4px 8px';\r\n div.style.borderRadius = '4px';\r\n div.style.background = 'rgba(0, 0, 0, 0.4)';\r\n div.style.backdropFilter = 'blur(2px)';\r\n div.style.top = '20px';\r\n div.style.right = '20px';\r\n\r\n container.appendChild(div);\r\n this.watermarkElement = div;\r\n }\r\n\r\n /**\r\n * Cleans up active watermarks.\r\n */\r\n public removeWatermark() {\r\n if (this.floatInterval) {\r\n window.clearInterval(this.floatInterval);\r\n this.floatInterval = undefined;\r\n }\r\n if (this.watermarkElement && this.watermarkElement.parentElement) {\r\n this.watermarkElement.parentElement.removeChild(this.watermarkElement);\r\n }\r\n this.watermarkElement = undefined;\r\n }\r\n\r\n public isDRMProtected(): boolean {\r\n return !!(\r\n this.config?.widevineLicenseUrl ||\r\n this.config?.playreadyLicenseUrl ||\r\n this.config?.fairplayLicenseUrl\r\n );\r\n }\r\n\r\n public getDRMDetails() {\r\n return {\r\n widevine: this.config?.widevineLicenseUrl || null,\r\n playready: this.config?.playreadyLicenseUrl || null,\r\n fairplay: this.config?.fairplayLicenseUrl || null,\r\n authTokenPresent: !!this.config?.authToken,\r\n allowedDomains: this.config?.allowedDomains || [],\r\n };\r\n }\r\n}\r\n","// Declare globals so TS doesn't complain about Google Cast SDK scripts loaded in index.html\r\ndeclare global {\r\n interface Window {\r\n __onGCastApiAvailable: (isAvailable: boolean) => void;\r\n cast: any;\r\n chrome: any;\r\n }\r\n}\r\n\r\nexport class CastingModule {\r\n private isCasting: boolean = false;\r\n private castDeviceName: string = '';\r\n private onCastStateChangeCallback?: (isCasting: boolean, deviceName: string) => void;\r\n \r\n private castContext: any = null;\r\n private remotePlayer: any = null;\r\n private remotePlayerController: any = null;\r\n\r\n constructor() {\r\n this.initCastApi();\r\n }\r\n\r\n private initCastApi() {\r\n const init = () => {\r\n this.castContext = window.cast.framework.CastContext.getInstance();\r\n this.castContext.setOptions({\r\n receiverApplicationId: window.chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID,\r\n autoJoinPolicy: window.chrome.cast.AutoJoinPolicy.ORIGIN_SCOPED\r\n });\r\n\r\n this.remotePlayer = new window.cast.framework.RemotePlayer();\r\n this.remotePlayerController = new window.cast.framework.RemotePlayerController(this.remotePlayer);\r\n\r\n this.remotePlayerController.addEventListener(\r\n window.cast.framework.RemotePlayerEventType.IS_CONNECTED_CHANGED,\r\n () => {\r\n this.isCasting = this.remotePlayer.isConnected;\r\n const session = this.castContext.getCurrentSession();\r\n this.castDeviceName = session ? session.getCastDevice().friendlyName : '';\r\n \r\n if (this.onCastStateChangeCallback) {\r\n this.onCastStateChangeCallback(this.isCasting, this.castDeviceName);\r\n }\r\n }\r\n );\r\n };\r\n\r\n if (window.cast && window.cast.framework) {\r\n init();\r\n } else {\r\n window.__onGCastApiAvailable = (isAvailable: boolean) => {\r\n if (isAvailable) init();\r\n };\r\n }\r\n }\r\n\r\n public getCastingState() {\r\n return {\r\n isCasting: this.isCasting,\r\n deviceName: this.castDeviceName,\r\n };\r\n }\r\n\r\n /**\r\n * Prompts the native Google Cast device picker overlay.\r\n */\r\n public async requestSession(currentSrc: string, currentTime: number): Promise<void> {\r\n if (!this.castContext) {\r\n console.error('[PlayerSDK Cast] Google Cast Framework not loaded.');\r\n return;\r\n }\r\n\r\n try {\r\n await this.castContext.requestSession();\r\n // If session connects successfully, start playing the media\r\n const session = this.castContext.getCurrentSession();\r\n if (session && currentSrc) {\r\n const mediaInfo = new window.chrome.cast.media.MediaInfo(currentSrc, 'video/mp4');\r\n const request = new window.chrome.cast.media.LoadRequest(mediaInfo);\r\n request.currentTime = currentTime;\r\n request.autoplay = true;\r\n await session.loadMedia(request);\r\n }\r\n } catch (err) {\r\n console.warn('[PlayerSDK Cast] Session request failed or cancelled by user.', err);\r\n }\r\n }\r\n\r\n /**\r\n * Disconnects the current cast session.\r\n */\r\n public stopCasting() {\r\n if (this.castContext) {\r\n try {\r\n this.castContext.endCurrentSession(true);\r\n } catch (err) {\r\n console.warn('[PlayerSDK Cast] Failed to disconnect session', err);\r\n }\r\n }\r\n }\r\n\r\n public onCastStateChange(callback: (isCasting: boolean, deviceName: string) => void) {\r\n this.onCastStateChangeCallback = callback;\r\n }\r\n\r\n public requestAirPlaySession(videoElement: HTMLVideoElement): void {\r\n const anyVideo = videoElement as any;\r\n if (typeof anyVideo.webkitShowPlaybackTargetPicker === 'function') {\r\n anyVideo.webkitShowPlaybackTargetPicker();\r\n } else {\r\n console.warn('[PlayerSDK AirPlay] AirPlay request is not supported on this device.');\r\n }\r\n }\r\n\r\n public stopAirPlay(videoElement: HTMLVideoElement): void {\r\n const anyVideo = videoElement as any;\r\n if (typeof anyVideo.webkitShowPlaybackTargetPicker === 'function') {\r\n anyVideo.webkitShowPlaybackTargetPicker();\r\n } else {\r\n console.warn('[PlayerSDK AirPlay] AirPlay stop is not supported on this device.');\r\n }\r\n }\r\n\r\n public isAirPlayAvailable(): boolean {\r\n return (\r\n !!(window as any).WebKitPlaybackTargetAvailabilityEvent ||\r\n (navigator.userAgent.includes('Safari') && !navigator.userAgent.includes('Chrome'))\r\n );\r\n }\r\n}\r\n","import type { ThumbnailConfig } from '@goboss/types';\r\n\r\nexport class ThumbnailsModule {\r\n private config?: ThumbnailConfig;\r\n\r\n constructor(config?: ThumbnailConfig) {\r\n this.config = config;\r\n }\r\n\r\n /**\r\n * Calculates the position offset inside the sprite sheet for a given seek hover time.\r\n */\r\n public getThumbnail(time: number): {\r\n url: string;\r\n x: number;\r\n y: number;\r\n width: number;\r\n height: number;\r\n } | null {\r\n if (!this.config) return null;\r\n\r\n const { spriteUrl, tileWidth, tileHeight, columns, interval, totalCount } = this.config;\r\n\r\n // Find thumbnail index\r\n let index = Math.floor(time / interval);\r\n if (index < 0) index = 0;\r\n if (index >= totalCount) index = totalCount - 1;\r\n\r\n // Get column and row coordinate\r\n const col = index % columns;\r\n const row = Math.floor(index / columns);\r\n\r\n return {\r\n url: spriteUrl,\r\n x: col * tileWidth,\r\n y: row * tileHeight,\r\n width: tileWidth,\r\n height: tileHeight,\r\n };\r\n }\r\n\r\n public hasThumbnails(): boolean {\r\n return !!this.config;\r\n }\r\n}\r\n","import Hls from 'hls.js';\r\nimport * as dashjs from 'dashjs';\r\nimport type { PlayerSDKOptions, VideoQuality, AnalyticsEvent, KeyMoment } from '@goboss/types';\r\nimport { AnalyticsModule } from '@goboss/analytics';\r\nimport { ChaptersModule, KeyMomentsModule } from '@goboss/chapters';\r\nimport { SubtitlesModule } from '@goboss/subtitles';\r\nimport { DRMModule } from '@goboss/drm';\r\nimport { CastingModule } from '@goboss/casting';\r\nimport { ThumbnailsModule } from '@goboss/thumbnails';\r\n\r\nexport class VideoPlayerSDK {\r\n private options: PlayerSDKOptions;\r\n private videoElement!: HTMLVideoElement;\r\n private hlsInstance: Hls | null = null;\r\n private dashInstance: dashjs.MediaPlayerClass | null = null;\r\n private activeQuality: VideoQuality = { id: -1, height: 0, width: 0, bitrate: 0, label: 'Auto' };\r\n private qualityLevels: VideoQuality[] = [];\r\n\r\n // Modular sub-packages\r\n public analytics: AnalyticsModule;\r\n public keyMoments: KeyMomentsModule;\r\n public chapters: ChaptersModule;\r\n public subtitles: SubtitlesModule;\r\n public drm: DRMModule;\r\n public casting: CastingModule;\r\n public thumbnails: ThumbnailsModule;\r\n\r\n // HLS subtitle cue store (populated via CUES_PARSED when renderTextTracksNatively=false)\r\n private hlsSubtitleCues = new Map<string, Array<{ startTime: number; endTime: number; text: string }>>();\r\n private hlsActiveSubtitleKey: string | null = null;\r\n\r\n // Audio track list (populated from HLS.js AUDIO_TRACKS_UPDATED)\r\n private audioTrackList: Array<{ id: number; label: string; lang: string }> = [];\r\n\r\n // State\r\n private eventListeners: { [key: string]: Array<(...args: any[]) => void> } = {};\r\n\r\n constructor(options: PlayerSDKOptions) {\r\n this.options = {\r\n autoplay: false,\r\n muted: false,\r\n themeColor: '#6366f1',\r\n ...options,\r\n };\r\n\r\n // Instantiate modular components\r\n this.analytics = new AnalyticsModule(this.options.analyticsCallback, this.options.src);\r\n this.keyMoments = new KeyMomentsModule(this.options.keyMoments || this.options.chapters || []);\r\n this.chapters = this.keyMoments; // Link chapters module directly to keyMoments for backward compatibility\r\n this.subtitles = new SubtitlesModule(this.options.subtitles);\r\n this.drm = new DRMModule(this.options.drm);\r\n this.casting = new CastingModule();\r\n this.thumbnails = new ThumbnailsModule(this.options.thumbnails);\r\n\r\n // Initialize player foundation\r\n this.initializePlayer();\r\n }\r\n\r\n /**\r\n * Builds the video DOM hierarchy, binds events, validates domain restrictions, and mounts DRM watermarks.\r\n */\r\n private initializePlayer() {\r\n const currentDomain = window.location.hostname;\r\n if (!this.drm.validateDomain(currentDomain)) {\r\n const errorMsg = `Domain Blocked: Access to streaming content is restricted on ${currentDomain}.`;\r\n console.error(errorMsg);\r\n this.options.container.innerHTML = `\r\n <div style=\"background:#0f172a; color:#f43f5e; padding:20px; text-align:center; font-family:sans-serif; border-radius:12px; border:1px solid #e11d48; margin: 10px;\">\r\n <h4 style=\"margin:0 0 10px 0;\">Content Protection Alert</h4>\r\n <p style=\"font-size:13px; margin:0; opacity:0.8;\">${errorMsg}</p>\r\n </div>\r\n `;\r\n throw new Error(errorMsg);\r\n }\r\n\r\n // Build video element\r\n this.videoElement = document.createElement('video');\r\n this.videoElement.className = 'sdk-html5-video';\r\n this.videoElement.style.width = '100%';\r\n this.videoElement.style.height = '100%';\r\n this.videoElement.style.display = 'block';\r\n this.videoElement.style.backgroundColor = '#000000';\r\n this.videoElement.playsInline = true;\r\n this.videoElement.controls = false; // Never use default browser controls\r\n this.videoElement.muted = !!this.options.muted;\r\n\r\n this.options.container.appendChild(this.videoElement);\r\n\r\n // Mount secure digital watermark\r\n this.drm.setupWatermark(this.options.container);\r\n\r\n // Bind playback and telemetry event hooks\r\n this.bindMediaEvents();\r\n\r\n // Initialize Streaming source (HLS or standard mp4)\r\n this.loadSource(this.options.src);\r\n\r\n // Setup background lock-screen controls\r\n this.setupBackgroundControls();\r\n }\r\n\r\n /**\r\n * Binds direct event listeners to the native HTML5 Video tag, routing them to our sub-packages.\r\n */\r\n private bindMediaEvents() {\r\n const video = this.videoElement;\r\n\r\n video.addEventListener('play', () => {\r\n this.analytics.track('play', video, this.getAnalyticsPayload());\r\n this.analytics.startHeartbeat(video, () => this.getAnalyticsPayload());\r\n this.emit('play', null);\r\n });\r\n\r\n video.addEventListener('pause', () => {\r\n this.analytics.track('pause', video, this.getAnalyticsPayload());\r\n this.analytics.stopHeartbeat();\r\n this.emit('pause', null);\r\n });\r\n\r\n video.addEventListener('timeupdate', () => {\r\n const time = video.currentTime;\r\n this.chapters.updateTime(time);\r\n this.subtitles.updateTime(time);\r\n\r\n // HLS subtitle display via CUES_PARSED (no native textTrack mode conflicts)\r\n if (this.hlsActiveSubtitleKey !== null) {\r\n const cues = this.hlsSubtitleCues.get(this.hlsActiveSubtitleKey) ?? [];\r\n const active = cues.filter(c => time >= c.startTime && time < c.endTime);\r\n this.subtitles.setCustomSubtitleText(active.map(c => c.text).join('<br />'));\r\n }\r\n\r\n this.emit('timeupdate', time);\r\n });\r\n\r\n video.addEventListener('seeking', () => {\r\n this.analytics.track('seek', video, this.getAnalyticsPayload());\r\n this.emit('seeking', video.currentTime);\r\n });\r\n\r\n video.addEventListener('waiting', () => {\r\n this.analytics.setBuffering(true, video, () => this.getAnalyticsPayload());\r\n this.emit('buffering', true);\r\n });\r\n\r\n video.addEventListener('playing', () => {\r\n this.analytics.setBuffering(false, video, () => this.getAnalyticsPayload());\r\n this.emit('buffering', false);\r\n });\r\n\r\n video.addEventListener('volumechange', () => {\r\n this.analytics.track('volume_change', video, this.getAnalyticsPayload());\r\n this.emit('volumechange', { volume: video.volume, muted: video.muted });\r\n });\r\n\r\n video.addEventListener('ratechange', () => {\r\n this.analytics.track('speed_change', video, this.getAnalyticsPayload());\r\n this.emit('ratechange', video.playbackRate);\r\n });\r\n\r\n video.addEventListener('ended', () => {\r\n this.analytics.track('ended', video, this.getAnalyticsPayload());\r\n this.analytics.track('complete', video, { ...this.getAnalyticsPayload(), watchPercentage: 100 });\r\n this.analytics.stopHeartbeat();\r\n this.emit('ended', null);\r\n });\r\n\r\n video.addEventListener('enterpictureinpicture', () => {\r\n this.analytics.track('pip_toggle', video, { ...this.getAnalyticsPayload(), errorMessage: 'entered' });\r\n this.emit('pipchange', true);\r\n });\r\n\r\n video.addEventListener('leavepictureinpicture', () => {\r\n this.analytics.track('pip_toggle', video, { ...this.getAnalyticsPayload(), errorMessage: 'exited' });\r\n this.emit('pipchange', false);\r\n });\r\n\r\n video.addEventListener('error', () => {\r\n const err = video.error;\r\n this.analytics.track('error', video, {\r\n ...this.getAnalyticsPayload(),\r\n errorMessage: err ? `${err.code}: ${err.message}` : 'Unknown HTML5 video playback error',\r\n });\r\n this.emit('error', err);\r\n });\r\n\r\n // Discover native text tracks (embedded subs for MP4, CEA-608/708 for HLS)\r\n const textTracks = video.textTracks;\r\n if (textTracks) {\r\n const registerNativeTrack = (track: TextTrack, index: number) => {\r\n if (track.kind !== 'subtitles' && track.kind !== 'captions') return;\r\n const alreadyRegistered = this.subtitles.getTracks().some(\r\n (t) => t.isNative && t.nativeTrackIndex === index\r\n );\r\n if (alreadyRegistered) return;\r\n const isCaption = track.kind === 'captions';\r\n this.subtitles.addTrack({\r\n id: `native-${index}`,\r\n label: track.label || track.language || (isCaption ? `CC ${index + 1}` : `Track ${index + 1}`),\r\n lang: track.language || 'und',\r\n isDefault: !this.hlsInstance && ((track as any).default || track.mode === 'showing'),\r\n isNative: true,\r\n nativeTrackIndex: index,\r\n });\r\n };\r\n\r\n // Catch tracks present at metadata load (non-HLS embedded subs + early HLS CC tracks)\r\n video.addEventListener('loadedmetadata', () => {\r\n this.analytics.track('video_loaded', video, this.getAnalyticsPayload());\r\n let added = 0;\r\n const targetKinds = this.hlsInstance ? ['captions'] : ['subtitles', 'captions'];\r\n for (let i = 0; i < textTracks.length; i++) {\r\n if (!targetKinds.includes(textTracks[i].kind)) continue;\r\n const before = this.subtitles.getTracks().length;\r\n registerNativeTrack(textTracks[i], i);\r\n if (this.subtitles.getTracks().length > before) added++;\r\n }\r\n if (added > 0) this.emit('subtitlesloaded', this.subtitles.getTracks());\r\n });\r\n\r\n // Catch CEA-608/708 caption tracks HLS.js adds dynamically after load\r\n textTracks.addEventListener('addtrack', (event) => {\r\n const added = event.track;\r\n if (!added) return;\r\n for (let i = 0; i < textTracks.length; i++) {\r\n if (textTracks[i] !== added) continue;\r\n const before = this.subtitles.getTracks().length;\r\n registerNativeTrack(added, i);\r\n if (this.subtitles.getTracks().length > before) {\r\n this.emit('subtitlesloaded', this.subtitles.getTracks());\r\n }\r\n break;\r\n }\r\n });\r\n }\r\n\r\n // Connect subtitles track changes\r\n this.subtitles.onTrackSwitch((track) => {\r\n if (track === null) {\r\n if (this.hlsInstance) {\r\n // SUBTITLE_TRACK_SWITCH(-1) fires and clears hlsActiveSubtitleKey\r\n this.hlsInstance.subtitleTrack = -1;\r\n } else if (textTracks) {\r\n for (let i = 0; i < textTracks.length; i++) {\r\n textTracks[i].mode = 'disabled';\r\n textTracks[i].oncuechange = null;\r\n }\r\n this.subtitles.setCustomSubtitleText('');\r\n }\r\n } else if (track.isHls) {\r\n if (this.hlsInstance && track.hlsTrackId !== undefined) {\r\n // SUBTITLE_TRACK_SWITCH fires → sets hlsActiveSubtitleKey\r\n // CUES_PARSED fires → populates hlsSubtitleCues\r\n // timeupdate polls hlsSubtitleCues to display text (no textTrack mode changes)\r\n this.hlsInstance.subtitleTrack = track.hlsTrackId;\r\n }\r\n } else if (track.isNative) {\r\n if (textTracks && track.nativeTrackIndex !== undefined && textTracks[track.nativeTrackIndex]) {\r\n for (let i = 0; i < textTracks.length; i++) {\r\n textTracks[i].oncuechange = null;\r\n if (i === track.nativeTrackIndex) {\r\n textTracks[i].mode = 'hidden';\r\n const tt = textTracks[i];\r\n tt.oncuechange = () => {\r\n const activeCues = tt.activeCues;\r\n if (activeCues && activeCues.length > 0) {\r\n const text = Array.from(activeCues)\r\n .map((cue: any) => cue.text)\r\n .join('<br />');\r\n this.subtitles.setCustomSubtitleText(text);\r\n } else {\r\n this.subtitles.setCustomSubtitleText('');\r\n }\r\n };\r\n } else {\r\n textTracks[i].mode = 'disabled';\r\n }\r\n }\r\n }\r\n }\r\n });\r\n }\r\n\r\n private rewriteToProxy(url: string): string {\r\n if (url.includes('d3queh3es1i8u.cloudfront.net')) {\r\n try {\r\n const urlObj = new URL(url);\r\n return `/video-proxy${urlObj.pathname}${urlObj.search}`;\r\n } catch (e) {\r\n return url.replace('https://d3queh3es1i8u.cloudfront.net', '/video-proxy');\r\n }\r\n }\r\n return url;\r\n }\r\n\r\n /**\r\n * Initializes HLS adaptive streaming through Hls.js or falls back to native browser playback.\r\n */\r\n private loadSource(src: string) {\r\n this.extractKeyMomentsFromManifest(src);\r\n this.extractSubtitlesFromManifest(src);\r\n const proxiedSrc = this.rewriteToProxy(src);\r\n const isHls = proxiedSrc.endsWith('.m3u8') || proxiedSrc.includes('.m3u8');\r\n const isDash = proxiedSrc.endsWith('.mpd') || proxiedSrc.includes('.mpd');\r\n\r\n if (isHls) {\r\n if (Hls.isSupported()) {\r\n const hls = new Hls({\r\n enableWorker: true,\r\n lowLatencyMode: true,\r\n backBufferLength: 90,\r\n maxMaxBufferLength: 30,\r\n // Disable native textTrack mode management so HLS.js's onTextTracksChanged\r\n // handler is a no-op. Subtitle cues are delivered via CUES_PARSED instead,\r\n // which we accumulate and poll in timeupdate. This eliminates the mode-change\r\n // feedback loop that caused video stalls after subtitle selection.\r\n renderTextTracksNatively: false,\r\n xhrSetup: (xhr, url) => {\r\n let token = this.drm.getAuthToken();\r\n if (!token) {\r\n try {\r\n const streamUrlObj = new URL(src);\r\n const parsedToken = streamUrlObj.searchParams.get('token') || \r\n streamUrlObj.searchParams.get('key') || \r\n streamUrlObj.searchParams.get('auth') || \r\n streamUrlObj.searchParams.get('authToken');\r\n if (parsedToken) {\r\n token = parsedToken;\r\n }\r\n } catch (e) {}\r\n }\r\n\r\n try {\r\n const proxiedUrl = this.rewriteToProxy(url);\r\n const urlObj = new URL(proxiedUrl, window.location.origin);\r\n if (token) {\r\n urlObj.searchParams.set('token', token);\r\n }\r\n xhr.open('GET', urlObj.toString(), true);\r\n } catch (e) {\r\n console.error('[PlayerSDK Core] Error in xhrSetup URL parsing:', e);\r\n }\r\n }\r\n });\r\n\r\n this.hlsInstance = hls;\r\n\r\n hls.loadSource(proxiedSrc);\r\n hls.attachMedia(this.videoElement);\r\n\r\n // Track which cue-store key is active for the current subtitle track.\r\n // Key format mirrors what SubtitleStreamController emits in CUES_PARSED:\r\n // 'default' for the HLS default track, 'subtitlesN' for track index N.\r\n hls.on(Hls.Events.SUBTITLE_TRACK_SWITCH, (_, data) => {\r\n this.hlsActiveSubtitleKey = null;\r\n this.subtitles.setCustomSubtitleText('');\r\n if (data.id !== -1) {\r\n const hlsTrack = hls.subtitleTracks[data.id];\r\n this.hlsActiveSubtitleKey = hlsTrack?.default ? 'default' : `subtitles${data.id}`;\r\n }\r\n });\r\n\r\n // Accumulate parsed cues per track. timeupdate polls these to display text.\r\n // Deduplicate on startTime+endTime so seeks don't double-add the same cue.\r\n hls.on(Hls.Events.CUES_PARSED, (_, data: { type: string; cues: Array<{ startTime: number; endTime: number; text: string }>; track: string }) => {\r\n const existing = this.hlsSubtitleCues.get(data.track) ?? [];\r\n const incoming = data.cues;\r\n const deduped = incoming.filter(\r\n c => !existing.some(e => e.startTime === c.startTime && e.endTime === c.endTime)\r\n );\r\n if (deduped.length > 0) {\r\n this.hlsSubtitleCues.set(data.track, [...existing, ...deduped]);\r\n }\r\n });\r\n\r\n hls.on(Hls.Events.MANIFEST_PARSED, () => {\r\n // Parse resolutions\r\n this.qualityLevels = hls.levels.map((level, index) => ({\r\n id: index,\r\n height: level.height,\r\n width: level.width,\r\n bitrate: level.bitrate,\r\n label: level.height ? `${level.height}p` : `Option ${index + 1}`,\r\n }));\r\n\r\n // Prep auto quality\r\n const autoLvl: VideoQuality = { id: -1, height: 0, width: 0, bitrate: 0, label: 'Auto' };\r\n this.qualityLevels.unshift(autoLvl);\r\n this.activeQuality = autoLvl;\r\n\r\n this.emit('qualitylevels', this.qualityLevels);\r\n\r\n if (this.options.autoplay) {\r\n this.play();\r\n }\r\n });\r\n\r\n // Audio track discovery and switching\r\n hls.on(Hls.Events.AUDIO_TRACKS_UPDATED, () => {\r\n if (hls.audioTracks.length > 1) {\r\n this.audioTrackList = hls.audioTracks.map((track, idx) => ({\r\n id: idx,\r\n label: track.name || track.lang || `Audio ${idx + 1}`,\r\n lang: track.lang || '',\r\n }));\r\n this.emit('audiotracksloaded', this.audioTrackList);\r\n }\r\n });\r\n\r\n hls.on(Hls.Events.AUDIO_TRACK_SWITCHED, (_, data) => {\r\n this.analytics.track('audio_track_change', this.videoElement, this.getAnalyticsPayload());\r\n this.emit('audiotrackchange', data.id);\r\n });\r\n\r\n // Track active level switches\r\n hls.on(Hls.Events.LEVEL_SWITCHED, (_, data) => {\r\n const actualLvl = this.qualityLevels.find((q) => q.id === data.level);\r\n if (actualLvl) {\r\n this.analytics.track('quality_change', this.videoElement, {\r\n ...this.getAnalyticsPayload(),\r\n rendition: actualLvl.label,\r\n bitrate: actualLvl.bitrate,\r\n });\r\n this.emit('qualitychange', actualLvl);\r\n }\r\n });\r\n\r\n hls.on(Hls.Events.ERROR, (_, data) => {\r\n if (data.fatal) {\r\n if (data.type === Hls.ErrorTypes.NETWORK_ERROR) {\r\n this.analytics.track('network_error', this.videoElement, {\r\n ...this.getAnalyticsPayload(),\r\n errorMessage: data.details,\r\n errorCode: 0,\r\n });\r\n hls.startLoad();\r\n } else if (data.type === Hls.ErrorTypes.MEDIA_ERROR) {\r\n hls.recoverMediaError();\r\n } else {\r\n this.destroy();\r\n }\r\n }\r\n });\r\n } else if (this.videoElement.canPlayType('application/vnd.apple.mpegurl')) {\r\n // Fallback to iOS Safari Native HLS Player\r\n this.videoElement.src = src;\r\n this.qualityLevels = [{ id: 0, height: 720, width: 1280, bitrate: 2000000, label: '720p (Native)' }];\r\n this.activeQuality = this.qualityLevels[0];\r\n\r\n if (this.options.autoplay) {\r\n this.videoElement.play().catch(console.error);\r\n }\r\n }\r\n } else if (isDash) {\r\n // MPEG-DASH (.mpd) adaptive stream via dash.js\r\n const dash = dashjs.MediaPlayer().create();\r\n this.dashInstance = dash;\r\n dash.initialize(this.videoElement, src, !!this.options.autoplay);\r\n dash.updateSettings({\r\n streaming: {\r\n abr: { autoSwitchBitrate: { video: true, audio: true } },\r\n buffer: { bufferTimeAtTopQuality: 30 },\r\n },\r\n });\r\n\r\n // Expose a single synthetic quality entry\r\n this.qualityLevels = [{ id: 0, height: 0, width: 0, bitrate: 0, label: 'Auto (DASH)' }];\r\n this.activeQuality = this.qualityLevels[0];\r\n this.emit('qualitylevels', this.qualityLevels);\r\n } else {\r\n // Standard MP4/WebM/OGG progressive fallback — handled natively by browser\r\n this.videoElement.src = src;\r\n this.qualityLevels = [{ id: 0, height: 1080, width: 1920, bitrate: 4500000, label: '1080p (Source)' }];\r\n this.activeQuality = this.qualityLevels[0];\r\n\r\n if (this.options.autoplay) {\r\n this.videoElement.play().catch(console.error);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Sets up global lock-screen metadata and media control keys (Media Session API).\r\n */\r\n private setupBackgroundControls() {\r\n if ('mediaSession' in navigator) {\r\n navigator.mediaSession.metadata = new MediaMetadata({\r\n title: 'Video Player',\r\n album: 'Video Player',\r\n artwork: [\r\n { src: 'https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?w=512&q=80', sizes: '512x512', type: 'image/jpeg' },\r\n ],\r\n });\r\n\r\n try {\r\n navigator.mediaSession.setActionHandler('play', () => this.play());\r\n navigator.mediaSession.setActionHandler('pause', () => this.pause());\r\n navigator.mediaSession.setActionHandler('seekbackward', (details) => {\r\n const offset = details.seekOffset || 10;\r\n this.seek(this.videoElement.currentTime - offset);\r\n });\r\n navigator.mediaSession.setActionHandler('seekforward', (details) => {\r\n const offset = details.seekOffset || 10;\r\n this.seek(this.videoElement.currentTime + offset);\r\n });\r\n } catch {\r\n console.warn('[PlayerSDK Session] Action handler binding is unsupported by browser.');\r\n }\r\n }\r\n }\r\n\r\n // --- CORE CONTROL INTERFACES ---\r\n\r\n public play() {\r\n this.videoElement.play().catch((err) => {\r\n console.warn('[PlayerSDK Play] Autoplay blocked, wait for user interaction:', err);\r\n });\r\n }\r\n\r\n public pause() {\r\n this.videoElement.pause();\r\n }\r\n\r\n public seek(seconds: number) {\r\n const target = Math.max(0, Math.min(seconds, this.getDuration()));\r\n this.videoElement.currentTime = target;\r\n }\r\n\r\n public setVolume(volume: number) {\r\n const vol = Math.max(0, Math.min(volume, 1));\r\n this.videoElement.volume = vol;\r\n this.videoElement.muted = vol === 0;\r\n }\r\n\r\n public setMute(muted: boolean) {\r\n this.videoElement.muted = muted;\r\n }\r\n\r\n public setPlaybackRate(rate: number) {\r\n this.videoElement.playbackRate = rate;\r\n }\r\n\r\n /**\r\n * Sets manual video quality rendition or switches back to auto bitrate scaling.\r\n */\r\n public setQuality(qualityId: number) {\r\n const qLvl = this.qualityLevels.find((q) => q.id === qualityId);\r\n if (!qLvl) return;\r\n\r\n this.activeQuality = qLvl;\r\n\r\n if (this.hlsInstance) {\r\n this.hlsInstance.currentLevel = qualityId;\r\n } else {\r\n console.warn('[PlayerSDK Core] Manual rendition switching is not supported by standard native players.');\r\n }\r\n }\r\n\r\n /**\r\n * Activates native Picture-in-Picture floating viewport.\r\n */\r\n public async togglePictureInPicture(): Promise<boolean> {\r\n if (!document.pictureInPictureEnabled) {\r\n console.warn('[PlayerSDK Core] Picture-in-Picture is disabled or unsupported.');\r\n return false;\r\n }\r\n\r\n try {\r\n if (document.pictureInPictureElement) {\r\n await document.exitPictureInPicture();\r\n return false;\r\n } else {\r\n await this.videoElement.requestPictureInPicture();\r\n return true;\r\n }\r\n } catch (err) {\r\n console.error('[PlayerSDK Core] PiP toggle failed:', err);\r\n return false;\r\n }\r\n }\r\n\r\n /**\r\n * Requests element-level fullscreen.\r\n */\r\n public toggleFullscreen() {\r\n const container = this.options.container;\r\n if (!document.fullscreenElement) {\r\n container.requestFullscreen().catch((err) => {\r\n console.error('[PlayerSDK Fullscreen] Failed entering fullscreen:', err);\r\n });\r\n } else {\r\n document.exitFullscreen();\r\n }\r\n }\r\n\r\n // --- GETTER METHODS ---\r\n\r\n public getVideoElement(): HTMLVideoElement {\r\n return this.videoElement;\r\n }\r\n\r\n public getCurrentTime(): number {\r\n return this.videoElement.currentTime;\r\n }\r\n\r\n public getDuration(): number {\r\n return this.videoElement.duration || 0;\r\n }\r\n\r\n public getVolume(): number {\r\n return this.videoElement.volume;\r\n }\r\n\r\n public isMuted(): boolean {\r\n return this.videoElement.muted;\r\n }\r\n\r\n public getPlaybackRate(): number {\r\n return this.videoElement.playbackRate;\r\n }\r\n\r\n public getActiveQuality(): VideoQuality {\r\n return this.activeQuality;\r\n }\r\n\r\n public getQualityLevels(): VideoQuality[] {\r\n return this.qualityLevels;\r\n }\r\n\r\n public getBufferLength(): number {\r\n const buffered = this.videoElement.buffered;\r\n const time = this.videoElement.currentTime;\r\n for (let i = 0; i < buffered.length; i++) {\r\n if (time >= buffered.start(i) && time <= buffered.end(i)) {\r\n return buffered.end(i) - time;\r\n }\r\n }\r\n return 0;\r\n }\r\n\r\n public getBufferedRanges(): Array<{ start: number; end: number }> {\r\n const buffered = this.videoElement.buffered;\r\n const ranges = [];\r\n for (let i = 0; i < buffered.length; i++) {\r\n ranges.push({ start: buffered.start(i), end: buffered.end(i) });\r\n }\r\n return ranges;\r\n }\r\n\r\n private getAnalyticsPayload(): Partial<AnalyticsEvent> {\r\n return {\r\n rendition: this.activeQuality.label,\r\n bitrate: this.activeQuality.bitrate,\r\n volume: this.videoElement.volume,\r\n speed: this.videoElement.playbackRate,\r\n };\r\n }\r\n\r\n // --- SIMPLE EVENT SYSTEM ---\r\n\r\n public on(event: string, callback: (...args: any[]) => void) {\r\n if (!this.eventListeners[event]) {\r\n this.eventListeners[event] = [];\r\n }\r\n this.eventListeners[event].push(callback);\r\n }\r\n\r\n public off(event: string, callback: (...args: any[]) => void) {\r\n if (!this.eventListeners[event]) return;\r\n this.eventListeners[event] = this.eventListeners[event].filter((cb) => cb !== callback);\r\n }\r\n\r\n private emit(event: string, ...args: any[]) {\r\n if (!this.eventListeners[event]) return;\r\n this.eventListeners[event].forEach((cb) => cb(...args));\r\n }\r\n\r\n private async extractSubtitlesFromManifest(src: string) {\r\n try {\r\n const proxiedUrl = this.rewriteToProxy(src);\r\n const res = await fetch(proxiedUrl);\r\n if (!res.ok) return;\r\n const text = await res.text();\r\n const lines = text.split('\\n');\r\n\r\n let token: string | undefined = this.drm.getAuthToken() || undefined;\r\n if (!token) {\r\n try {\r\n const u = new URL(src);\r\n token = u.searchParams.get('token') || u.searchParams.get('key') ||\r\n u.searchParams.get('auth') || u.searchParams.get('authToken') || undefined;\r\n } catch { /* no-op */ }\r\n }\r\n\r\n const baseUrl = src.substring(0, src.lastIndexOf('/') + 1);\r\n const subtitleTracks: Array<{\r\n id: string; label: string; lang: string; isDefault?: boolean;\r\n src?: string; isHls?: boolean; hlsTrackId?: number;\r\n }> = [];\r\n\r\n for (const line of lines) {\r\n if (!line.startsWith('#EXT-X-MEDIA:TYPE=SUBTITLES')) continue;\r\n const nameMatch = line.match(/NAME=\"([^\"]+)\"/);\r\n const langMatch = line.match(/LANGUAGE=\"([^\"]+)\"/);\r\n const uriMatch = line.match(/URI=\"([^\"]+)\"/);\r\n const isDefault = /DEFAULT=YES/i.test(line);\r\n if (!uriMatch) continue;\r\n\r\n const uri = uriMatch[1];\r\n const idx = subtitleTracks.length;\r\n const label = nameMatch ? nameMatch[1] : langMatch ? langMatch[1] : `Track ${idx + 1}`;\r\n const lang = langMatch ? langMatch[1] : 'und';\r\n\r\n // Segment-based subtitle playlist — HLS.js streams and parses these internally.\r\n // Set isHls + hlsTrackId so the SUBTITLE_TRACK_SWITCH → CUES_PARSED path handles display.\r\n const isSegmentPlaylist = /\\.m3u8?(\\?|$)/i.test(uri);\r\n if (isSegmentPlaylist) {\r\n subtitleTracks.push({ id: `subtitle-${idx}`, label, lang, isDefault, isHls: true, hlsTrackId: idx });\r\n continue;\r\n }\r\n\r\n // Direct subtitle file (VTT, SRT, etc.) — fetch and parse client-side.\r\n const absoluteUri = uri.startsWith('http') ? uri : baseUrl + uri;\r\n let vttSrc = this.rewriteToProxy(absoluteUri);\r\n if (token) {\r\n try {\r\n const urlObj = new URL(vttSrc, window.location.origin);\r\n urlObj.searchParams.set('token', token);\r\n vttSrc = urlObj.toString();\r\n } catch { /* no-op */ }\r\n }\r\n subtitleTracks.push({ id: `subtitle-${idx}`, label, lang, isDefault, src: vttSrc });\r\n }\r\n\r\n if (subtitleTracks.length > 0) {\r\n this.subtitles.setHlsTracks(subtitleTracks);\r\n this.emit('subtitlesloaded', this.subtitles.getTracks());\r\n }\r\n } catch (e) {\r\n console.error('[PlayerSDK] Error extracting subtitles from manifest:', e);\r\n }\r\n }\r\n\r\n private async extractKeyMomentsFromManifest(src: string) {\r\n try {\r\n const proxiedUrl = this.rewriteToProxy(src);\r\n const res = await fetch(proxiedUrl);\r\n if (!res.ok) return;\r\n const text = await res.text();\r\n const lines = text.split('\\n');\r\n const rawMoments: Array<{ id: string; startDate: string; title: string }> = [];\r\n\r\n for (const line of lines) {\r\n if (line.startsWith('#EXT-X-DATERANGE:')) {\r\n const idMatch = line.match(/ID=\"([^\"]+)\"/);\r\n const startDateMatch = line.match(/START-DATE=\"([^\"]+)\"/);\r\n const titleMatch = line.match(/X-TITLE=\"([^\"]+)\"/);\r\n\r\n if (idMatch && startDateMatch && titleMatch) {\r\n if (!rawMoments.some(m => m.id === idMatch[1])) {\r\n rawMoments.push({\r\n id: idMatch[1],\r\n startDate: startDateMatch[1],\r\n title: titleMatch[1],\r\n });\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (rawMoments.length > 0) {\r\n rawMoments.sort((a, b) => a.startDate.localeCompare(b.startDate));\r\n const baseTime = new Date(rawMoments[0].startDate).getTime();\r\n\r\n const parsedKeyMoments: KeyMoment[] = rawMoments.map((m) => {\r\n const startTime = (new Date(m.startDate).getTime() - baseTime) / 1000;\r\n return {\r\n title: m.title,\r\n startTime: startTime,\r\n endTime: 0,\r\n };\r\n });\r\n\r\n for (let i = 0; i < parsedKeyMoments.length; i++) {\r\n if (i < parsedKeyMoments.length - 1) {\r\n parsedKeyMoments[i].endTime = parsedKeyMoments[i + 1].startTime;\r\n } else {\r\n parsedKeyMoments[i].endTime = 99999;\r\n }\r\n }\r\n \r\n this.keyMoments.setKeyMoments(parsedKeyMoments);\r\n this.emit('keymomentsloaded', parsedKeyMoments);\r\n this.emit('chaptersloaded', parsedKeyMoments);\r\n }\r\n } catch (e) {\r\n console.error('Error parsing manifest key moments:', e);\r\n }\r\n }\r\n\r\n // --- AUDIO TRACK CONTROLS ---\r\n\r\n public getAudioTracks(): Array<{ id: number; label: string; lang: string }> {\r\n return this.audioTrackList;\r\n }\r\n\r\n public setAudioTrack(id: number) {\r\n if (this.hlsInstance) {\r\n this.hlsInstance.audioTrack = id;\r\n }\r\n }\r\n\r\n // --- LIFECYCLE MANAGEMENT ---\r\n\r\n public destroy() {\r\n this.hlsSubtitleCues.clear();\r\n this.hlsActiveSubtitleKey = null;\r\n this.analytics.stopHeartbeat();\r\n this.drm.removeWatermark();\r\n\r\n if (this.hlsInstance) {\r\n this.hlsInstance.destroy();\r\n this.hlsInstance = null;\r\n }\r\n\r\n if (this.dashInstance) {\r\n this.dashInstance.reset();\r\n this.dashInstance = null;\r\n }\r\n\r\n if (this.videoElement) {\r\n this.videoElement.pause();\r\n this.videoElement.removeAttribute('src');\r\n this.videoElement.load();\r\n if (this.videoElement.parentElement) {\r\n this.videoElement.parentElement.removeChild(this.videoElement);\r\n }\r\n }\r\n\r\n this.eventListeners = {};\r\n }\r\n}\r\n","import React, { useRef, useState, useEffect } from 'react';\r\nimport Hls from 'hls.js';\r\nimport {\r\n Play,\r\n Pause,\r\n RotateCcw,\r\n RotateCw,\r\n Volume2,\r\n VolumeX,\r\n Volume1,\r\n Volume,\r\n Settings,\r\n Subtitles,\r\n Maximize,\r\n Minimize,\r\n Cast,\r\n Airplay,\r\n ListVideo,\r\n X,\r\n Check,\r\n ChevronRight,\r\n PictureInPicture,\r\n Languages,\r\n} from 'lucide-react';\r\nimport { VideoPlayerSDK } from '@goboss/core-player';\r\nimport { FirebaseAnalyticsProvider } from '@goboss/analytics';\r\nimport type { FirebaseConfig } from '@goboss/analytics';\r\nimport type { VideoQuality, Chapter, KeyMoment, SubtitleTrack, AnalyticsEvent } from '@goboss/types';\r\nimport './player.css';\r\n\r\nconst rewriteToProxy = (url: string): string => {\r\n if (url.includes('d3queh3es1i8u.cloudfront.net')) {\r\n try {\r\n const urlObj = new URL(url);\r\n return `/video-proxy${urlObj.pathname}${urlObj.search}`;\r\n } catch (e) {\r\n return url.replace('https://d3queh3es1i8u.cloudfront.net', '/video-proxy');\r\n }\r\n }\r\n return url;\r\n};\r\n\r\nconst hexToRgb = (hex: string): string => {\r\n const cleanHex = hex.replace('#', '');\r\n if (cleanHex.length === 6) {\r\n const r = parseInt(cleanHex.substring(0, 2), 16);\r\n const g = parseInt(cleanHex.substring(2, 4), 16);\r\n const b = parseInt(cleanHex.substring(4, 6), 16);\r\n return `${r}, ${g}, ${b}`;\r\n } else if (cleanHex.length === 3) {\r\n const r = parseInt(cleanHex[0] + cleanHex[0], 16);\r\n const g = parseInt(cleanHex[1] + cleanHex[1], 16);\r\n const b = parseInt(cleanHex[2] + cleanHex[2], 16);\r\n return `${r}, ${g}, ${b}`;\r\n }\r\n return '99, 102, 241';\r\n};\r\n\r\nconst languageNameMap: Record<string, string> = {\r\n en: 'English',\r\n eng: 'English',\r\n fr: 'French',\r\n fre: 'French',\r\n fra: 'French',\r\n es: 'Spanish',\r\n spa: 'Spanish',\r\n de: 'German',\r\n ger: 'German',\r\n deu: 'German',\r\n it: 'Italian',\r\n ita: 'Italian',\r\n pt: 'Portuguese',\r\n por: 'Portuguese',\r\n ja: 'Japanese',\r\n zh: 'Chinese',\r\n ko: 'Korean',\r\n ru: 'Russian',\r\n ar: 'Arabic',\r\n hi: 'Hindi',\r\n};\r\n\r\nconst formatLanguageName = (value: string): string | null => {\r\n if (!value) return null;\r\n const normalized = value.trim().toLowerCase();\r\n const prefixMatch = normalized.match(/^(en|eng|fr|fre|fra|es|spa|de|ger|deu|it|ita|pt|por|ja|zh|ko|ru|ar|hi)/);\r\n if (prefixMatch && languageNameMap[prefixMatch[0]]) {\r\n return languageNameMap[prefixMatch[0]];\r\n }\r\n for (const [key, name] of Object.entries(languageNameMap)) {\r\n if (normalized.includes(key)) {\r\n return name;\r\n }\r\n }\r\n if (/english|anglais|ingles/i.test(normalized)) return 'English';\r\n if (/french|français/i.test(normalized)) return 'French';\r\n if (/spanish|español/i.test(normalized)) return 'Spanish';\r\n if (/german|deutsch/i.test(normalized)) return 'German';\r\n if (/italian|italiano/i.test(normalized)) return 'Italian';\r\n if (/portuguese|português/i.test(normalized)) return 'Portuguese';\r\n if (/japanese|nihongo/i.test(normalized)) return 'Japanese';\r\n if (/chinese|中文/i.test(normalized)) return 'Chinese';\r\n if (/korean|한국어/i.test(normalized)) return 'Korean';\r\n if (/russian|русский/i.test(normalized)) return 'Russian';\r\n if (/arabic|العربية/i.test(normalized)) return 'Arabic';\r\n if (/hindi|हिन्दी/i.test(normalized)) return 'Hindi';\r\n return null;\r\n};\r\n\r\nconst getLanguageLabel = (track: { label: string; lang: string }): string => {\r\n if (track.lang) {\r\n const normalized = formatLanguageName(track.lang);\r\n if (normalized) return normalized;\r\n }\r\n\r\n if (track.label) {\r\n const label = track.label.trim();\r\n const parts = label.split(/[#\\d\\.\\s\\-_/|:]+/).filter(Boolean);\r\n for (let i = parts.length - 1; i >= 0; i -= 1) {\r\n const candidate = formatLanguageName(parts[i]);\r\n if (candidate) return candidate;\r\n }\r\n const cleaned = label.replace(/^[#\\d\\.\\s]+/, '').trim();\r\n return formatLanguageName(cleaned) || cleaned;\r\n }\r\n\r\n return 'Unknown';\r\n};\r\n\r\nconst dedupeSubtitleTracks = (tracks: SubtitleTrack[]): SubtitleTrack[] => {\r\n const seen = new Set<string>();\r\n return tracks.filter((track) => {\r\n const label = getLanguageLabel(track).toLowerCase();\r\n const lang = (track.lang || '').trim().toLowerCase();\r\n const key = `${label}|${lang}`;\r\n if (seen.has(key)) {\r\n return false;\r\n }\r\n seen.add(key);\r\n return true;\r\n });\r\n};\r\n\r\ninterface SubtitleStyle {\r\n size?: 'small' | 'medium' | 'large';\r\n background?: 'none' | 'dim' | 'solid';\r\n color?: string;\r\n}\r\n\r\ninterface WebVideoPlayerProps {\r\n src: string;\r\n chapters?: Chapter[];\r\n keyMoments?: KeyMoment[];\r\n subtitles?: SubtitleTrack[];\r\n watermark?: string;\r\n themeColor?: string;\r\n drmConfig?: any;\r\n firebaseConfig?: FirebaseConfig;\r\n onAnalyticsEvent?: (event: AnalyticsEvent) => void;\r\n onKeyMomentsLoaded?: (keyMoments: KeyMoment[]) => void;\r\n enableControls?: boolean;\r\n enableChromecast?: boolean;\r\n enableKeyMoments?: boolean;\r\n enableSubtitles?: boolean;\r\n enableQuality?: boolean;\r\n enablePlaybackSpeed?: boolean;\r\n enablePiP?: boolean;\r\n enableFullscreen?: boolean;\r\n subtitleStyle?: SubtitleStyle;\r\n}\r\n\r\nexport const WebVideoPlayer: React.FC<WebVideoPlayerProps> = ({\r\n src,\r\n chapters = [],\r\n keyMoments,\r\n subtitles = [],\r\n watermark = 'GoBOSS',\r\n themeColor = '#6366f1',\r\n drmConfig,\r\n firebaseConfig,\r\n onAnalyticsEvent,\r\n onKeyMomentsLoaded,\r\n enableControls = true,\r\n enableChromecast = true,\r\n enableKeyMoments = true,\r\n enableSubtitles = true,\r\n enableQuality = true,\r\n enablePlaybackSpeed = true,\r\n enablePiP = true,\r\n enableFullscreen = true,\r\n subtitleStyle = {},\r\n}) => {\r\n const containerRef = useRef<HTMLDivElement>(null);\r\n const firebaseProviderRef = useRef<FirebaseAnalyticsProvider | null>(null);\r\n const seekbarRef = useRef<HTMLDivElement>(null);\r\n const canvasRef = useRef<HTMLCanvasElement>(null);\r\n const hiddenVideoRef = useRef<HTMLVideoElement | null>(null);\r\n const hiddenHlsRef = useRef<Hls | null>(null);\r\n const hoverSeekTimer = useRef<number | null>(null);\r\n const isScrubbing = useRef(false);\r\n const isSeeking = useRef(false);\r\n const seekTargetTime = useRef<number | null>(null);\r\n const seekGuardTimeout = useRef<number | null>(null);\r\n const castButtonRef = useRef<HTMLDivElement>(null);\r\n\r\n const [player, setPlayer] = useState<VideoPlayerSDK | null>(null);\r\n const [isPlaying, setIsPlaying] = useState(false);\r\n const [currentTime, setCurrentTime] = useState(0);\r\n const [duration, setDuration] = useState(0);\r\n const [volume, setVolume] = useState(1);\r\n const [isMuted, setIsMuted] = useState(false);\r\n const [isBuffering, setIsBuffering] = useState(false);\r\n const [isFullscreen, setIsFullscreen] = useState(false);\r\n const [isPiP, setIsPiP] = useState(false);\r\n const [isEnded, setIsEnded] = useState(false);\r\n\r\n // Lists & Selections\r\n const [qualities, setQualities] = useState<VideoQuality[]>([]);\r\n const [activeQuality, setActiveQuality] = useState<VideoQuality>({\r\n id: -1,\r\n height: 0,\r\n width: 0,\r\n bitrate: 0,\r\n label: 'Auto',\r\n });\r\n const [subtitleText, setSubtitleText] = useState('');\r\n const [activeSubtitleTrack, setActiveSubtitleTrack] = useState('off');\r\n const [availableSubtitles, setAvailableSubtitles] = useState<SubtitleTrack[]>(dedupeSubtitleTracks(subtitles));\r\n const [audioTracks, setAudioTracks] = useState<Array<{ id: number; label: string; lang: string }>>([]);\r\n const [activeAudioTrack, setActiveAudioTrack] = useState<number>(0);\r\n\r\n // Sync external subtitles prop dynamically and merge with discovered tracks\r\n useEffect(() => {\r\n if (player) {\r\n const playerTracks = player.subtitles.getTracks();\r\n const merged = [...subtitles, ...playerTracks.filter(t => !subtitles.some(s => s.id === t.id))];\r\n setAvailableSubtitles(dedupeSubtitleTracks(merged));\r\n } else {\r\n setAvailableSubtitles(dedupeSubtitleTracks(subtitles));\r\n }\r\n }, [subtitles, player]);\r\n\r\n const [activeChapter, setActiveChapter] = useState<{ chapter: KeyMoment | null; index: number }>({\r\n chapter: null,\r\n index: -1,\r\n });\r\n\r\n const [localKeyMoments, setLocalKeyMoments] = useState<KeyMoment[]>(keyMoments || chapters || []);\r\n\r\n // UI Dropdowns\r\n const [activeMenu, setActiveMenu] = useState<'none' | 'quality' | 'speed' | 'subtitles' | 'chapters' | 'audio'>('none');\r\n const [playbackRate, setPlaybackRate] = useState(1);\r\n const [showChaptersSidebar, setShowChaptersSidebar] = useState(false);\r\n const [chapterThumbnails, setChapterThumbnails] = useState<Record<number, string>>({});\r\n\r\n const keyMomentsKey = JSON.stringify(localKeyMoments.map(c => ({ title: c.title, startTime: c.startTime })));\r\n\r\n // Sync props to state dynamically\r\n useEffect(() => {\r\n setLocalKeyMoments(keyMoments || chapters || []);\r\n }, [keyMoments, chapters]);\r\n\r\n // Dynamically extract actual video frames at chapter start times\r\n useEffect(() => {\r\n if (!src || !localKeyMoments || localKeyMoments.length === 0) return;\r\n\r\n setChapterThumbnails({});\r\n\r\n const tempVideo = document.createElement('video');\r\n tempVideo.muted = true;\r\n tempVideo.preload = 'metadata';\r\n tempVideo.crossOrigin = 'anonymous';\r\n // position:fixed with opacity:0.01 forces the browser compositor to decode frames, bypassing display:none optimizations\r\n tempVideo.style.cssText = 'position:fixed;top:0;left:0;width:10px;height:10px;opacity:0.01;pointer-events:none;z-index:-9999;';\r\n document.body.appendChild(tempVideo);\r\n\r\n let tempHls: Hls | null = null;\r\n const isHls = src.includes('.m3u8');\r\n\r\n const tempCanvas = document.createElement('canvas');\r\n tempCanvas.width = 160;\r\n tempCanvas.height = 90;\r\n const ctx = tempCanvas.getContext('2d');\r\n\r\n let currentIndex = 0;\r\n let isStarted = false;\r\n\r\n const captureNext = () => {\r\n if (currentIndex >= localKeyMoments.length) {\r\n cleanup();\r\n return;\r\n }\r\n const targetTime = localKeyMoments[currentIndex].startTime;\r\n // Seek to target. If 0, seek to 0.01 to ensure the browser registers a currentTime change and triggers 'seeked'\r\n tempVideo.currentTime = targetTime === 0 ? 0.01 : targetTime;\r\n };\r\n\r\n const onSeeked = () => {\r\n if (ctx && tempVideo.readyState >= 2) {\r\n ctx.drawImage(tempVideo, 0, 0, tempCanvas.width, tempCanvas.height);\r\n const dataUrl = tempCanvas.toDataURL('image/jpeg', 0.8);\r\n setChapterThumbnails((prev) => ({ ...prev, [currentIndex]: dataUrl }));\r\n currentIndex++;\r\n captureNext();\r\n } else {\r\n // Fallback retry if video frame buffer wasn't ready\r\n setTimeout(onSeeked, 100);\r\n }\r\n };\r\n\r\n const startCapture = () => {\r\n if (isStarted) return;\r\n isStarted = true;\r\n captureNext();\r\n };\r\n\r\n tempVideo.addEventListener('seeked', onSeeked);\r\n tempVideo.addEventListener('canplay', startCapture);\r\n\r\n if (isHls && Hls.isSupported()) {\r\n tempHls = new Hls({\r\n enableWorker: false,\r\n maxBufferLength: 5,\r\n xhrSetup: (xhr, url) => {\r\n let token = drmConfig?.authToken;\r\n if (!token) {\r\n try {\r\n const streamUrlObj = new URL(src);\r\n const parsedToken = streamUrlObj.searchParams.get('token') || \r\n streamUrlObj.searchParams.get('key') || \r\n streamUrlObj.searchParams.get('auth') || \r\n streamUrlObj.searchParams.get('authToken');\r\n if (parsedToken) {\r\n token = parsedToken;\r\n }\r\n } catch (e) {}\r\n }\r\n\r\n try {\r\n const proxiedUrl = rewriteToProxy(url);\r\n const urlObj = new URL(proxiedUrl, window.location.origin);\r\n if (token) {\r\n urlObj.searchParams.set('token', token);\r\n }\r\n xhr.open('GET', urlObj.toString(), true);\r\n } catch (e) {\r\n console.error('[WebVideoPlayer tempHls] xhrSetup error:', e);\r\n }\r\n }\r\n });\r\n tempHls.loadSource(rewriteToProxy(src));\r\n tempHls.attachMedia(tempVideo);\r\n } else {\r\n tempVideo.src = src;\r\n }\r\n\r\n // If readyState is already satisfied, start capturing immediately\r\n if (tempVideo.readyState >= 2) {\r\n startCapture();\r\n }\r\n\r\n const cleanup = () => {\r\n tempVideo.removeEventListener('seeked', onSeeked);\r\n tempVideo.removeEventListener('canplay', startCapture);\r\n if (tempHls) {\r\n tempHls.destroy();\r\n }\r\n if (document.body.contains(tempVideo)) {\r\n document.body.removeChild(tempVideo);\r\n }\r\n };\r\n\r\n return cleanup;\r\n }, [src, keyMomentsKey]);\r\n\r\n // Cast Overlay Simulation\r\n const [castDevice, setCastDevice] = useState<string | null>(null);\r\n const [isAirPlayAvailable, setIsAirPlayAvailable] = useState(false);\r\n const [isAirPlayConnected, setIsAirPlayConnected] = useState(false);\r\n const [airPlayTargetName, setAirPlayTargetName] = useState<string | null>(null);\r\n\r\n // Scrubber Hover\r\n const [hoverX, setHoverX] = useState<number | null>(null);\r\n const [hoverTime, setHoverTime] = useState<number>(0);\r\n const [hoverThumbnail, setHoverThumbnail] = useState<string | null>(null);\r\n const [hoverChapter, setHoverChapter] = useState<KeyMoment | null>(null);\r\n const [scrubbingTime, setScrubbingTime] = useState<number | null>(null);\r\n\r\n // Inject Native Google Cast Button (Invisible Overlay)\r\n useEffect(() => {\r\n if (castButtonRef.current && !castButtonRef.current.querySelector('google-cast-launcher')) {\r\n const launcher = document.createElement('google-cast-launcher');\r\n launcher.style.cursor = 'pointer';\r\n launcher.style.width = '100%';\r\n launcher.style.height = '100%';\r\n launcher.style.display = 'block';\r\n castButtonRef.current.appendChild(launcher);\r\n }\r\n }, []);\r\n\r\n useEffect(() => {\r\n if (!player) {\r\n setIsAirPlayAvailable(false);\r\n setIsAirPlayConnected(false);\r\n setAirPlayTargetName(null);\r\n return;\r\n }\r\n\r\n const updateAvailability = (available: boolean) => setIsAirPlayAvailable(available);\r\n const updateConnection = () => {\r\n const video = player.getVideoElement();\r\n const anyVideo = video as any;\r\n const connected = Boolean(anyVideo?.webkitCurrentPlaybackTargetIsWireless);\r\n setIsAirPlayConnected(connected);\r\n setAirPlayTargetName(\r\n connected\r\n ? anyVideo.webkitCurrentPlaybackTargetName || 'AirPlay device'\r\n : null\r\n );\r\n };\r\n\r\n updateAvailability(player.casting.isAirPlayAvailable());\r\n updateConnection();\r\n\r\n const video = player.getVideoElement();\r\n const availabilityHandler = (event: any) => {\r\n updateAvailability(event.availability === 'available');\r\n };\r\n const connectionHandler = () => {\r\n updateConnection();\r\n };\r\n\r\n if ('webkitShowPlaybackTargetPicker' in video) {\r\n video.addEventListener('webkitplaybacktargetavailabilitychanged', availabilityHandler as EventListener);\r\n video.addEventListener('webkitcurrentplaybacktargetiswirelesschanged', connectionHandler as EventListener);\r\n }\r\n\r\n return () => {\r\n if ('webkitShowPlaybackTargetPicker' in video) {\r\n video.removeEventListener('webkitplaybacktargetavailabilitychanged', availabilityHandler as EventListener);\r\n video.removeEventListener('webkitcurrentplaybacktargetiswirelesschanged', connectionHandler as EventListener);\r\n }\r\n };\r\n }, [player]);\r\n\r\n // Control UI visibility timeout\r\n const [controlsVisible, setControlsVisible] = useState(true);\r\n const controlsTimeoutRef = useRef<number | null>(null);\r\n // Ref so the timeout callback always reads the *current* isPlaying value (avoids stale closure)\r\n const isPlayingRef = useRef(false);\r\n const activeMenuRef = useRef<typeof activeMenu>('none');\r\n const availableSubtitlesRef = useRef<SubtitleTrack[]>(subtitles);\r\n\r\n useEffect(() => {\r\n if (!containerRef.current) return;\r\n\r\n // ── Hidden video for position-accurate thumbnail capture ──────────────\r\n const hiddenVideo = document.createElement('video');\r\n hiddenVideo.muted = true;\r\n hiddenVideo.preload = 'metadata';\r\n hiddenVideo.crossOrigin = 'anonymous';\r\n hiddenVideo.style.cssText = 'display:none;position:absolute;pointer-events:none;';\r\n document.body.appendChild(hiddenVideo);\r\n hiddenVideoRef.current = hiddenVideo;\r\n\r\n const isHls = src.includes('.m3u8');\r\n if (isHls && Hls.isSupported()) {\r\n const hls = new Hls({\r\n enableWorker: false,\r\n maxBufferLength: 5,\r\n xhrSetup: (xhr, url) => {\r\n let token = drmConfig?.authToken;\r\n if (!token) {\r\n try {\r\n const streamUrlObj = new URL(src);\r\n const parsedToken = streamUrlObj.searchParams.get('token') || \r\n streamUrlObj.searchParams.get('key') || \r\n streamUrlObj.searchParams.get('auth') || \r\n streamUrlObj.searchParams.get('authToken');\r\n if (parsedToken) {\r\n token = parsedToken;\r\n }\r\n } catch (e) {}\r\n }\r\n\r\n try {\r\n const proxiedUrl = rewriteToProxy(url);\r\n const urlObj = new URL(proxiedUrl, window.location.origin);\r\n if (token) {\r\n urlObj.searchParams.set('token', token);\r\n }\r\n xhr.open('GET', urlObj.toString(), true);\r\n } catch (e) {\r\n console.error('[WebVideoPlayer hiddenHls] xhrSetup error:', e);\r\n }\r\n }\r\n });\r\n hls.loadSource(rewriteToProxy(src));\r\n hls.attachMedia(hiddenVideo);\r\n hiddenHlsRef.current = hls;\r\n } else if (isHls && hiddenVideo.canPlayType('application/vnd.apple.mpegurl')) {\r\n hiddenVideo.src = src;\r\n } else {\r\n hiddenVideo.src = src;\r\n }\r\n\r\n const onSeeked = () => {\r\n const canvas = canvasRef.current;\r\n if (!canvas) return;\r\n const ctx = canvas.getContext('2d');\r\n if (ctx && hiddenVideo.readyState >= 2) {\r\n ctx.drawImage(hiddenVideo, 0, 0, canvas.width, canvas.height);\r\n const dataUrl = canvas.toDataURL('image/jpeg', 0.82);\r\n setHoverThumbnail(dataUrl);\r\n }\r\n };\r\n hiddenVideo.addEventListener('seeked', onSeeked);\r\n\r\n // ── Firebase Analytics Provider ───────────────────────────────────────\r\n let firebaseCallback: ((event: import('@goboss/types').AnalyticsEvent) => void) | undefined;\r\n if (firebaseConfig) {\r\n const provider = new FirebaseAnalyticsProvider(firebaseConfig);\r\n firebaseProviderRef.current = provider;\r\n firebaseCallback = provider.createCallback();\r\n }\r\n\r\n // ── Main SDK player ───────────────────────────────────────────────────\r\n const sdkInstance = new VideoPlayerSDK({\r\n container: containerRef.current,\r\n src,\r\n autoplay: false,\r\n muted: false,\r\n keyMoments: localKeyMoments,\r\n subtitles,\r\n drm: {\r\n watermarkText: watermark,\r\n allowedDomains: ['localhost', '127.0.0.1'],\r\n ...drmConfig,\r\n },\r\n analyticsCallback: (event) => {\r\n firebaseCallback?.(event);\r\n if (onAnalyticsEvent) onAnalyticsEvent(event);\r\n },\r\n themeColor,\r\n });\r\n\r\n sdkInstance.on('keymomentsloaded', (parsedKeyMoments: KeyMoment[]) => {\r\n setLocalKeyMoments(parsedKeyMoments);\r\n if (onKeyMomentsLoaded) {\r\n onKeyMomentsLoaded(parsedKeyMoments);\r\n }\r\n });\r\n\r\n sdkInstance.on('chaptersloaded', (parsedChapters: Chapter[]) => {\r\n setLocalKeyMoments(parsedChapters);\r\n if (onKeyMomentsLoaded) {\r\n onKeyMomentsLoaded(parsedChapters);\r\n }\r\n });\r\n\r\n // Subscriptions to core player events\r\n sdkInstance.on('play', () => {\r\n setIsPlaying(true);\r\n setIsEnded(false);\r\n });\r\n sdkInstance.on('pause', () => setIsPlaying(false));\r\n sdkInstance.on('ended', () => {\r\n setIsPlaying(false);\r\n setIsEnded(true);\r\n });\r\n sdkInstance.on('timeupdate', (time: number) => {\r\n // Guard against stale timeupdates while seeking is in progress.\r\n // Tolerance is 5s to handle HLS segment boundary offsets.\r\n if (isSeeking.current && seekTargetTime.current !== null) {\r\n if (Math.abs(time - seekTargetTime.current) < 5.0) {\r\n // Seek landed — clear flag and allow normal updates\r\n isSeeking.current = false;\r\n seekTargetTime.current = null;\r\n if (seekGuardTimeout.current) {\r\n window.clearTimeout(seekGuardTimeout.current);\r\n seekGuardTimeout.current = null;\r\n }\r\n } else {\r\n // Still seeking — skip this stale update\r\n return;\r\n }\r\n }\r\n\r\n setCurrentTime(time);\r\n setDuration(sdkInstance.getDuration());\r\n\r\n // Update chapter badge\r\n const activeCh = sdkInstance.keyMoments.getCurrentMoment(time);\r\n setActiveChapter({ chapter: activeCh.keyMoment, index: activeCh.index });\r\n });\r\n\r\n sdkInstance.on('qualitylevels', (levels: VideoQuality[]) => {\r\n setQualities(levels);\r\n });\r\n\r\n sdkInstance.on('qualitychange', (quality: VideoQuality) => {\r\n setActiveQuality(quality);\r\n });\r\n\r\n sdkInstance.on('buffering', (buffering: boolean) => {\r\n setIsBuffering(buffering);\r\n });\r\n\r\n sdkInstance.on('volumechange', (data: { volume: number; muted: boolean }) => {\r\n setVolume(data.volume);\r\n setIsMuted(data.muted);\r\n });\r\n\r\n sdkInstance.on('pipchange', (active: boolean) => {\r\n setIsPiP(active);\r\n });\r\n\r\n // Subtitles track rendering hook\r\n sdkInstance.subtitles.onSubtitleChange((text: string) => {\r\n setSubtitleText(text);\r\n });\r\n\r\n sdkInstance.on('subtitlesloaded', (tracks: SubtitleTrack[]) => {\r\n setAvailableSubtitles(dedupeSubtitleTracks([...tracks]));\r\n \r\n // Auto-select default subtitle track\r\n const defaultTrack = tracks.find((t) => t.isDefault);\r\n if (defaultTrack) {\r\n sdkInstance.subtitles.setActiveTrack(defaultTrack.id);\r\n setActiveSubtitleTrack(defaultTrack.id);\r\n }\r\n });\r\n\r\n // Audio track discovery\r\n sdkInstance.on('audiotracksloaded', (tracks: Array<{ id: number; label: string; lang: string }>) => {\r\n setAudioTracks(tracks);\r\n setActiveAudioTrack(tracks[0]?.id ?? 0);\r\n });\r\n\r\n sdkInstance.on('audiotrackchange', (id: number) => {\r\n setActiveAudioTrack(id);\r\n });\r\n\r\n // Cast state syncer\r\n sdkInstance.casting.onCastStateChange((isCasting: boolean, deviceName: string) => {\r\n setCastDevice(isCasting ? deviceName : null);\r\n });\r\n\r\n // Sync HTML5 video volume initially\r\n setVolume(sdkInstance.getVolume());\r\n setIsMuted(sdkInstance.isMuted());\r\n\r\n setPlayer(sdkInstance);\r\n\r\n // Watch for fullscreen change globally to sync state and track analytics\r\n const handleFullscreenChange = () => {\r\n const isFs = !!document.fullscreenElement;\r\n setIsFullscreen(isFs);\r\n const videoEl = sdkInstance.getVideoElement();\r\n sdkInstance.analytics.track(\r\n isFs ? 'fullscreen_enter' : 'fullscreen_exit',\r\n videoEl\r\n );\r\n };\r\n document.addEventListener('fullscreenchange', handleFullscreenChange);\r\n\r\n // Global keyboard controls handler\r\n const handleKeyDown = (e: KeyboardEvent) => {\r\n const activeEl = document.activeElement;\r\n if (activeEl && (activeEl.tagName === 'INPUT' || activeEl.tagName === 'TEXTAREA' || activeEl.getAttribute('contenteditable') === 'true')) {\r\n return;\r\n }\r\n\r\n switch (e.key.toLowerCase()) {\r\n case ' ': // Space bar\r\n case 'k':\r\n e.preventDefault();\r\n if (sdkInstance.getVideoElement().paused) {\r\n sdkInstance.play();\r\n } else {\r\n sdkInstance.pause();\r\n }\r\n break;\r\n case 'arrowleft': // Seek back 10s\r\n case 'j':\r\n e.preventDefault();\r\n sdkInstance.seek(sdkInstance.getCurrentTime() - 10);\r\n break;\r\n case 'arrowright': // Seek forward 10s\r\n case 'l':\r\n e.preventDefault();\r\n sdkInstance.seek(sdkInstance.getCurrentTime() + 10);\r\n break;\r\n case 'arrowup': // Volume up 10%\r\n e.preventDefault();\r\n sdkInstance.setVolume(sdkInstance.getVolume() + 0.1);\r\n break;\r\n case 'arrowdown': // Volume down 10%\r\n e.preventDefault();\r\n sdkInstance.setVolume(sdkInstance.getVolume() - 0.1);\r\n break;\r\n case 'm': // Toggle Mute\r\n e.preventDefault();\r\n sdkInstance.setMute(!sdkInstance.isMuted());\r\n break;\r\n case 'f': // Toggle Fullscreen\r\n e.preventDefault();\r\n sdkInstance.toggleFullscreen();\r\n break;\r\n case 'p': // Toggle Picture-in-Picture\r\n e.preventDefault();\r\n sdkInstance.togglePictureInPicture();\r\n break;\r\n case 'c': // Toggle Subtitles\r\n e.preventDefault();\r\n const activeTrackId = sdkInstance.subtitles.getActiveTrackId();\r\n if (activeTrackId !== 'off') {\r\n sdkInstance.subtitles.setActiveTrack('off');\r\n setActiveSubtitleTrack('off');\r\n } else {\r\n const tracks = availableSubtitlesRef.current;\r\n if (tracks && tracks.length > 0) {\r\n sdkInstance.subtitles.setActiveTrack(tracks[0].id);\r\n setActiveSubtitleTrack(tracks[0].id);\r\n }\r\n }\r\n break;\r\n default:\r\n return;\r\n }\r\n resetControlsTimer();\r\n };\r\n\r\n document.addEventListener('keydown', handleKeyDown);\r\n\r\n // Reset controls visibility timer\r\n resetControlsTimer();\r\n\r\n return () => {\r\n sdkInstance.destroy();\r\n firebaseProviderRef.current?.destroy();\r\n firebaseProviderRef.current = null;\r\n document.removeEventListener('fullscreenchange', handleFullscreenChange);\r\n document.removeEventListener('keydown', handleKeyDown);\r\n if (controlsTimeoutRef.current) {\r\n window.clearTimeout(controlsTimeoutRef.current);\r\n }\r\n if (hiddenHlsRef.current) {\r\n hiddenHlsRef.current.destroy();\r\n hiddenHlsRef.current = null;\r\n }\r\n if (hiddenVideoRef.current) {\r\n document.body.removeChild(hiddenVideoRef.current);\r\n hiddenVideoRef.current = null;\r\n }\r\n };\r\n }, [src, watermark]);\r\n\r\n // Synchronize key moments prop dynamically to the running player instance\r\n useEffect(() => {\r\n if (player) {\r\n player.keyMoments.setKeyMoments(localKeyMoments);\r\n \r\n const video = containerRef.current?.querySelector('video');\r\n if (video) {\r\n const activeCh = player.keyMoments.getCurrentMoment(video.currentTime);\r\n setActiveChapter({ chapter: activeCh.keyMoment, index: activeCh.index });\r\n }\r\n }\r\n }, [keyMomentsKey, player]);\r\n\r\n // Close any open control drop-up menu when clicking outside of it\r\n useEffect(() => {\r\n const handleDocumentClick = (e: MouseEvent) => {\r\n if (activeMenu === 'none') return;\r\n\r\n const target = e.target as HTMLElement;\r\n if (target.closest('.sdk-menu-container')) {\r\n return;\r\n }\r\n\r\n setActiveMenu('none');\r\n };\r\n\r\n document.addEventListener('click', handleDocumentClick);\r\n return () => {\r\n document.removeEventListener('click', handleDocumentClick);\r\n };\r\n }, [activeMenu]);\r\n\r\n // Keep refs in sync with latest state so timeout callbacks are never stale\r\n isPlayingRef.current = isPlaying;\r\n activeMenuRef.current = activeMenu;\r\n availableSubtitlesRef.current = availableSubtitles;\r\n\r\n const resetControlsTimer = () => {\r\n setControlsVisible(true);\r\n if (controlsTimeoutRef.current) {\r\n window.clearTimeout(controlsTimeoutRef.current);\r\n }\r\n controlsTimeoutRef.current = window.setTimeout(() => {\r\n // Use refs to read the *current* values — not the closure-captured stale values\r\n if (isPlayingRef.current && activeMenuRef.current === 'none') {\r\n setControlsVisible(false);\r\n }\r\n }, 10000);\r\n };\r\n\r\n const handleMouseMove = () => {\r\n resetControlsTimer();\r\n };\r\n\r\n const togglePlay = () => {\r\n if (!player) return;\r\n if (isEnded) {\r\n performSeek(0);\r\n player.play();\r\n setIsEnded(false);\r\n } else if (isPlaying) {\r\n player.pause();\r\n } else {\r\n player.play();\r\n }\r\n resetControlsTimer();\r\n };\r\n\r\n const seekFromEvent = (e: React.MouseEvent<HTMLDivElement> | MouseEvent) => {\r\n if (!seekbarRef.current || !player || duration === 0) return 0;\r\n const rect = seekbarRef.current.getBoundingClientRect();\r\n const percent = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));\r\n return percent * duration;\r\n };\r\n\r\n const handleSeekMouseDown = (e: React.MouseEvent<HTMLDivElement>) => {\r\n if (!player) return;\r\n isScrubbing.current = true;\r\n const initialTime = seekFromEvent(e);\r\n setScrubbingTime(initialTime);\r\n\r\n const onMouseMove = (ev: MouseEvent) => {\r\n if (!isScrubbing.current || !seekbarRef.current) return;\r\n const rect = seekbarRef.current.getBoundingClientRect();\r\n const percent = Math.max(0, Math.min(1, (ev.clientX - rect.left) / rect.width));\r\n const targetTime = percent * duration;\r\n \r\n setScrubbingTime(targetTime);\r\n setHoverX(ev.clientX - rect.left);\r\n setHoverTime(targetTime);\r\n\r\n if (hiddenVideoRef.current) {\r\n hiddenVideoRef.current.currentTime = targetTime;\r\n }\r\n resetControlsTimer();\r\n };\r\n\r\n const onMouseUp = (ev: MouseEvent) => {\r\n isScrubbing.current = false;\r\n if (seekbarRef.current) {\r\n const rect = seekbarRef.current.getBoundingClientRect();\r\n const percent = Math.max(0, Math.min(1, (ev.clientX - rect.left) / rect.width));\r\n const targetTime = percent * duration;\r\n performSeek(targetTime);\r\n }\r\n setScrubbingTime(null);\r\n document.removeEventListener('mousemove', onMouseMove);\r\n document.removeEventListener('mouseup', onMouseUp);\r\n };\r\n\r\n document.addEventListener('mousemove', onMouseMove);\r\n document.addEventListener('mouseup', onMouseUp);\r\n };\r\n\r\n const performSeek = (time: number) => {\r\n if (!player) return;\r\n // Clamp to valid range so time never goes negative or past duration\r\n const clampedTime = Math.max(0, duration > 0 ? Math.min(time, duration) : time);\r\n isSeeking.current = true;\r\n seekTargetTime.current = clampedTime;\r\n setCurrentTime(clampedTime);\r\n player.seek(clampedTime);\r\n\r\n // Safety fallback: if no matching timeupdate arrives within 3s, force-clear the\r\n // seeking flag so the timer never gets permanently stuck (e.g. slow HLS buffer).\r\n if (seekGuardTimeout.current) window.clearTimeout(seekGuardTimeout.current);\r\n seekGuardTimeout.current = window.setTimeout(() => {\r\n isSeeking.current = false;\r\n seekTargetTime.current = null;\r\n seekGuardTimeout.current = null;\r\n }, 3000);\r\n };\r\n\r\n const handleVolumeChange = (e: React.ChangeEvent<HTMLInputElement>) => {\r\n if (!player) return;\r\n const value = parseFloat(e.target.value);\r\n player.setVolume(value);\r\n setVolume(value);\r\n setIsMuted(value === 0);\r\n };\r\n\r\n const toggleMute = () => {\r\n if (!player) return;\r\n const nextMute = !isMuted;\r\n player.setMute(nextMute);\r\n setIsMuted(nextMute);\r\n };\r\n\r\n const handleSpeedChange = (rate: number) => {\r\n if (!player) return;\r\n player.setPlaybackRate(rate);\r\n setPlaybackRate(rate);\r\n setActiveMenu('none');\r\n };\r\n\r\n const handleQualityChange = (qualityId: number) => {\r\n if (!player) return;\r\n player.setQuality(qualityId);\r\n\r\n const selected = qualities.find((q) => q.id === qualityId);\r\n if (selected) {\r\n setActiveQuality(selected);\r\n }\r\n setActiveMenu('none');\r\n };\r\n\r\n const handleSubtitleChange = (trackId: string) => {\r\n if (!player) return;\r\n player.subtitles.setActiveTrack(trackId);\r\n setActiveSubtitleTrack(trackId);\r\n setActiveMenu('none');\r\n };\r\n\r\n const handleChapterClick = (startTime: number) => {\r\n performSeek(startTime);\r\n setActiveMenu('none');\r\n };\r\n\r\n const togglePiP = () => {\r\n if (!player) return;\r\n player.togglePictureInPicture();\r\n };\r\n\r\n const toggleFullscreen = () => {\r\n if (!player) return;\r\n player.toggleFullscreen();\r\n };\r\n\r\n const handleCastToggle = () => {\r\n if (!player) return;\r\n if (castDevice) {\r\n player.casting.stopCasting();\r\n } else {\r\n player.casting.requestSession(src, currentTime);\r\n }\r\n };\r\n\r\n const handleAirPlayToggle = () => {\r\n if (!player) return;\r\n const video = player.getVideoElement();\r\n\r\n if (isAirPlayConnected) {\r\n player.casting.stopAirPlay(video);\r\n return;\r\n }\r\n\r\n if (typeof (video as any).webkitShowPlaybackTargetPicker === 'function') {\r\n player.casting.requestAirPlaySession(video);\r\n } else if (player.casting.isAirPlayAvailable()) {\r\n player.casting.requestAirPlaySession(video);\r\n } else {\r\n console.warn('[PlayerSDK AirPlay] AirPlay is not available on this device.');\r\n }\r\n };\r\n\r\n const handleSeekbarHover = (e: React.MouseEvent<HTMLDivElement>) => {\r\n if (!seekbarRef.current || duration === 0 || !player) return;\r\n const rect = seekbarRef.current.getBoundingClientRect();\r\n const percent = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));\r\n const targetTime = percent * duration;\r\n\r\n setHoverX(e.clientX - rect.left);\r\n setHoverTime(targetTime);\r\n\r\n if (hoverSeekTimer.current) window.clearTimeout(hoverSeekTimer.current);\r\n hoverSeekTimer.current = window.setTimeout(() => {\r\n if (hiddenVideoRef.current) {\r\n hiddenVideoRef.current.currentTime = targetTime;\r\n }\r\n }, 100);\r\n\r\n const hoverCh = player.keyMoments.getCurrentMoment(targetTime);\r\n setHoverChapter(hoverCh.keyMoment);\r\n };\r\n\r\n const handleSeekbarLeave = () => {\r\n setHoverX(null);\r\n setHoverThumbnail(null);\r\n setHoverChapter(null);\r\n if (hoverSeekTimer.current) {\r\n window.clearTimeout(hoverSeekTimer.current);\r\n }\r\n };\r\n\r\n const formatTime = (seconds: number) => {\r\n if (isNaN(seconds) || seconds < 0) return '00:00';\r\n const s = Math.floor(seconds);\r\n const hrs = Math.floor(s / 3600);\r\n const mins = Math.floor((s % 3600) / 60);\r\n const secs = s % 60;\r\n\r\n const pad = (num: number) => String(num).padStart(2, '0');\r\n\r\n if (hrs > 0) {\r\n return `${pad(hrs)}:${pad(mins)}:${pad(secs)}`;\r\n }\r\n return `${pad(mins)}:${pad(secs)}`;\r\n };\r\n\r\n const getVolumeIcon = () => {\r\n if (isMuted || volume === 0) return <VolumeX size={18} />;\r\n if (volume < 0.3) return <Volume size={18} />;\r\n if (volume < 0.7) return <Volume1 size={18} />;\r\n return <Volume2 size={18} />;\r\n };\r\n\r\n const menuToggle = (menu: typeof activeMenu) => {\r\n setActiveMenu(activeMenu === menu ? 'none' : menu);\r\n };\r\n\r\n return (\r\n <div\r\n ref={containerRef}\r\n className={`sdk-player-container ${controlsVisible ? 'controls-active' : ''}`}\r\n onMouseMove={handleMouseMove}\r\n onMouseLeave={() => {\r\n // Don't snap-hide on mouse-leave; let the running 10s timer handle it naturally\r\n // This way controls stay visible briefly as the cursor exits the player\r\n }}\r\n style={{\r\n border: `1px solid rgba(255, 255, 255, 0.08)`,\r\n ...({\r\n '--sdk-theme-color': themeColor,\r\n '--sdk-theme-color-rgb': hexToRgb(themeColor),\r\n } as React.CSSProperties)\r\n }}\r\n >\r\n <canvas\r\n ref={canvasRef}\r\n width={160}\r\n height={90}\r\n style={{ display: 'none', position: 'absolute', pointerEvents: 'none' }}\r\n />\r\n\r\n <div className={`sdk-spinner-overlay ${isBuffering ? 'active' : ''}`}>\r\n <div className=\"sdk-spinner\"></div>\r\n </div>\r\n\r\n {subtitleText && activeSubtitleTrack !== 'off' && (\r\n <div className=\"sdk-subtitles-overlay\">\r\n <span\r\n className=\"sdk-subtitles-text\"\r\n style={{\r\n fontSize: subtitleStyle.size === 'small' ? '14px' : subtitleStyle.size === 'large' ? '24px' : '18px',\r\n color: subtitleStyle.color ?? '#ffffff',\r\n background: subtitleStyle.background === 'none'\r\n ? 'transparent'\r\n : subtitleStyle.background === 'solid'\r\n ? 'rgba(0, 0, 0, 0.92)'\r\n : 'rgba(15, 23, 42, 0.78)',\r\n border: subtitleStyle.background === 'none' ? 'none' : '1px solid rgba(255,255,255,0.08)',\r\n backdropFilter: subtitleStyle.background === 'none' ? 'none' : 'blur(8px)',\r\n }}\r\n dangerouslySetInnerHTML={{ __html: subtitleText }}\r\n />\r\n </div>\r\n )}\r\n\r\n {(castDevice || isAirPlayConnected) && (\r\n <div className=\"sdk-cast-screen\">\r\n <Cast size={64} className=\"sdk-cast-icon-glow\" />\r\n <div className=\"sdk-cast-title\">\r\n {castDevice ? `Casting to ${castDevice}` : `AirPlay to ${airPlayTargetName || 'device'}`}\r\n </div>\r\n <div className=\"sdk-cast-desc\">\r\n {castDevice\r\n ? 'This media asset is currently streaming on your external screen.'\r\n : `This media asset is currently streaming via AirPlay${airPlayTargetName ? ` to ${airPlayTargetName}` : ''}.`}\r\n </div>\r\n <button\r\n className=\"sdk-cast-disconnect-btn\"\r\n onClick={() => {\r\n if (castDevice) {\r\n player?.casting.stopCasting();\r\n } else if (isAirPlayConnected) {\r\n player?.casting.stopAirPlay(player.getVideoElement());\r\n }\r\n }}\r\n >\r\n Disconnect Streaming\r\n </button>\r\n </div>\r\n )}\r\n\r\n {!castDevice && !isPlaying && (\r\n <div\r\n className=\"sdk-center-action-overlay\"\r\n onClick={togglePlay}\r\n >\r\n <div className=\"sdk-center-action-btn\">\r\n {isEnded ? (\r\n <RotateCcw size={32} className=\"sdk-center-icon\" />\r\n ) : (\r\n <Play size={32} className=\"sdk-center-icon\" style={{ marginLeft: '4px' }} fill=\"currentColor\" />\r\n )}\r\n </div>\r\n </div>\r\n )}\r\n\r\n {!castDevice && (\r\n <div\r\n className=\"sdk-controls-overlay\"\r\n onClick={(e) => {\r\n if (e.target === e.currentTarget) {\r\n togglePlay();\r\n }\r\n }}\r\n >\r\n {enableControls && (\r\n <div className=\"sdk-control-bar\">\r\n <div\r\n ref={seekbarRef}\r\n className=\"sdk-seekbar-container\"\r\n onMouseDown={handleSeekMouseDown}\r\n onMouseMove={handleSeekbarHover}\r\n onMouseLeave={handleSeekbarLeave}\r\n >\r\n <div\r\n className=\"sdk-seekbar-buffered\"\r\n style={{\r\n width: `${duration ? (player ? (player.getBufferedRanges()[0]?.end || 0) : 0) / duration * 100 : 0}%`,\r\n }}\r\n />\r\n <div\r\n className=\"sdk-seekbar-progress\"\r\n style={{\r\n width: `${duration ? ((scrubbingTime !== null ? scrubbingTime : currentTime) / duration) * 100 : 0}%`,\r\n background: themeColor,\r\n }}\r\n >\r\n <div className=\"sdk-seekbar-handle\" />\r\n </div>\r\n\r\n {enableKeyMoments && localKeyMoments.map((ch, idx) => {\r\n if (duration === 0) return null;\r\n const pos = (ch.startTime / duration) * 100;\r\n return (\r\n <div\r\n key={idx}\r\n className=\"sdk-seekbar-chapter-marker\"\r\n style={{ left: `${pos}%` }}\r\n />\r\n );\r\n })}\r\n\r\n {hoverX !== null && hoverThumbnail && (\r\n <div className=\"sdk-thumbnail-tooltip\" style={{ left: `${hoverX}px` }}>\r\n <div className=\"sdk-thumbnail-preview\">\r\n <img\r\n src={hoverThumbnail}\r\n alt=\"preview\"\r\n style={{\r\n width: '100%',\r\n height: '100%',\r\n objectFit: 'cover',\r\n borderRadius: '6px 6px 0 0',\r\n display: 'block',\r\n }}\r\n />\r\n </div>\r\n <div className=\"sdk-thumbnail-text\">\r\n {formatTime(hoverTime)}\r\n {hoverChapter && (\r\n <div className=\"sdk-thumbnail-chapter\">{hoverChapter.title}</div>\r\n )}\r\n </div>\r\n </div>\r\n )}\r\n </div>\r\n\r\n <div className=\"sdk-control-row\">\r\n <div className=\"sdk-control-group\">\r\n <button className=\"sdk-control-btn\" onClick={togglePlay} title={isPlaying ? \"Pause\" : \"Play\"}>\r\n {isPlaying ? <Pause size={18} fill=\"currentColor\" /> : <Play size={18} fill=\"currentColor\" />}\r\n </button>\r\n <button\r\n className=\"sdk-control-btn\"\r\n onClick={() => {\r\n performSeek(0);\r\n if (!isPlaying) {\r\n player?.play();\r\n }\r\n }}\r\n title=\"Replay from beginning\"\r\n >\r\n <RotateCcw size={18} />\r\n </button>\r\n <button\r\n className=\"sdk-control-btn\"\r\n onClick={() => performSeek(currentTime - 10)}\r\n title=\"Rewind 10s\"\r\n >\r\n <div style={{ position: 'relative', display: 'flex', alignItems: 'center', justifyContent: 'center', width: '18px', height: '18px' }}>\r\n <RotateCcw size={18} />\r\n <span style={{\r\n position: 'absolute',\r\n fontSize: '6px',\r\n fontWeight: 'bold',\r\n top: '52%',\r\n left: '50%',\r\n transform: 'translate(-50%, -50%)',\r\n pointerEvents: 'none',\r\n fontFamily: 'sans-serif',\r\n lineHeight: 1\r\n }}>10</span>\r\n </div>\r\n </button>\r\n <button\r\n className=\"sdk-control-btn\"\r\n onClick={() => performSeek(currentTime + 10)}\r\n title=\"Forward 10s\"\r\n >\r\n <div style={{ position: 'relative', display: 'flex', alignItems: 'center', justifyContent: 'center', width: '18px', height: '18px' }}>\r\n <RotateCw size={18} />\r\n <span style={{\r\n position: 'absolute',\r\n fontSize: '6px',\r\n fontWeight: 'bold',\r\n top: '52%',\r\n left: '50%',\r\n transform: 'translate(-50%, -50%)',\r\n pointerEvents: 'none',\r\n fontFamily: 'sans-serif',\r\n lineHeight: 1\r\n }}>10</span>\r\n </div>\r\n </button>\r\n\r\n <div className=\"sdk-volume-group\">\r\n <button className=\"sdk-control-btn\" onClick={toggleMute}>\r\n {getVolumeIcon()}\r\n </button>\r\n <div className=\"sdk-volume-slider-container\">\r\n <input\r\n type=\"range\"\r\n min=\"0\"\r\n max=\"1\"\r\n step=\"0.05\"\r\n value={isMuted ? 0 : volume}\r\n onChange={handleVolumeChange}\r\n className=\"sdk-volume-slider\"\r\n style={{ '--volume-pct': `${(isMuted ? 0 : volume) * 100}%` } as React.CSSProperties}\r\n />\r\n </div>\r\n </div>\r\n\r\n <div className=\"sdk-time-display\">\r\n {formatTime(scrubbingTime !== null ? scrubbingTime : currentTime)} / {formatTime(duration)}\r\n </div>\r\n\r\n {enableKeyMoments && activeChapter.chapter && (\r\n <div\r\n className={`sdk-chapter-badge ${showChaptersSidebar ? 'active' : ''}`}\r\n title=\"Toggle Key Moments side panel\"\r\n onClick={() => setShowChaptersSidebar(!showChaptersSidebar)}\r\n style={{ cursor: 'pointer', display: 'flex', alignItems: 'center' }}\r\n >\r\n <ListVideo size={10} style={{ marginRight: '5px' }} />\r\n <span style={{ marginRight: '3px' }}>{activeChapter.chapter.title}</span>\r\n <ChevronRight size={10} style={{ opacity: 0.8, transform: showChaptersSidebar ? 'rotate(90deg)' : 'none', transition: 'transform 0.2s ease' }} />\r\n </div>\r\n )}\r\n </div>\r\n\r\n <div className=\"sdk-control-group\">\r\n {enableChromecast && (\r\n <button\r\n className=\"sdk-control-btn\"\r\n onClick={handleCastToggle}\r\n title=\"Cast Stream\"\r\n style={{ color: castDevice ? '#3b82f6' : '' }}\r\n >\r\n <Cast size={18} />\r\n </button>\r\n )}\r\n\r\n {isAirPlayAvailable && (\r\n <button\r\n className=\"sdk-control-btn\"\r\n onClick={handleAirPlayToggle}\r\n title=\"AirPlay\"\r\n style={{ color: themeColor }}\r\n >\r\n <Airplay size={18} />\r\n </button>\r\n )}\r\n\r\n {audioTracks.length > 1 && (\r\n <div className=\"sdk-menu-container\">\r\n <button\r\n className=\"sdk-control-btn\"\r\n onClick={() => menuToggle('audio')}\r\n title=\"Audio track\"\r\n style={{ color: activeMenu === 'audio' ? themeColor : '' }}\r\n >\r\n <Languages size={18} />\r\n </button>\r\n {activeMenu === 'audio' && (\r\n <div className=\"sdk-dropup-menu\">\r\n <div className=\"sdk-dropup-header\">Audio Tracks</div>\r\n {audioTracks.map((track) => (\r\n <div\r\n key={track.id}\r\n className={`sdk-dropup-item ${activeAudioTrack === track.id ? 'active' : ''}`}\r\n onClick={() => {\r\n player?.setAudioTrack(track.id);\r\n setActiveAudioTrack(track.id);\r\n setActiveMenu('none');\r\n }}\r\n >\r\n <span>{getLanguageLabel(track)}</span>\r\n {activeAudioTrack === track.id && <Check size={12} />}\r\n </div>\r\n ))}\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n\r\n {enableSubtitles && (\r\n <div className=\"sdk-menu-container\">\r\n <button\r\n className=\"sdk-control-btn\"\r\n onClick={() => menuToggle('subtitles')}\r\n title=\"Subtitles track selector\"\r\n style={{ color: activeSubtitleTrack !== 'off' ? themeColor : '' }}\r\n >\r\n <Subtitles size={18} />\r\n </button>\r\n {activeMenu === 'subtitles' && (\r\n <div className=\"sdk-dropup-menu\">\r\n <div className=\"sdk-dropup-header\">Subtitles</div>\r\n <div\r\n className={`sdk-dropup-item ${activeSubtitleTrack === 'off' ? 'active' : ''}`}\r\n onClick={() => handleSubtitleChange('off')}\r\n >\r\n <span>Off</span>\r\n {activeSubtitleTrack === 'off' && <Check size={12} />}\r\n </div>\r\n {availableSubtitles.map((track) => (\r\n <div\r\n key={track.id}\r\n className={`sdk-dropup-item ${activeSubtitleTrack === track.id ? 'active' : ''}`}\r\n onClick={() => handleSubtitleChange(track.id)}\r\n >\r\n <span>{getLanguageLabel(track)}</span>\r\n {activeSubtitleTrack === track.id && <Check size={12} />}\r\n </div>\r\n ))}\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n\r\n {enableQuality && (\r\n <div className=\"sdk-menu-container\">\r\n <button\r\n className=\"sdk-control-btn\"\r\n onClick={() => menuToggle('quality')}\r\n title=\"Stream Quality settings\"\r\n style={{ color: activeQuality.id !== -1 ? themeColor : '' }}\r\n >\r\n <Settings size={18} />\r\n </button>\r\n {activeMenu === 'quality' && (\r\n <div className=\"sdk-dropup-menu large\">\r\n <div className=\"sdk-dropup-header\">Quality Options</div>\r\n {qualities.map((q) => (\r\n <div\r\n key={q.id}\r\n className={`sdk-dropup-item ${activeQuality.id === q.id ? 'active' : ''}`}\r\n onClick={() => handleQualityChange(q.id)}\r\n >\r\n <span>{q.label}</span>\r\n {activeQuality.id === q.id && <Check size={12} />}\r\n </div>\r\n ))}\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n\r\n {enablePlaybackSpeed && (\r\n <div className=\"sdk-menu-container\">\r\n <button\r\n className=\"sdk-control-btn\"\r\n onClick={() => menuToggle('speed')}\r\n title=\"Playback Speed\"\r\n >\r\n <span style={{ fontSize: '11px', fontWeight: 'bold' }}>{playbackRate}x</span>\r\n </button>\r\n {activeMenu === 'speed' && (\r\n <div className=\"sdk-dropup-menu\">\r\n <div className=\"sdk-dropup-header\">Speed</div>\r\n {[0.5, 0.75, 1, 1.25, 1.5, 2].map((rate) => (\r\n <div\r\n key={rate}\r\n className={`sdk-dropup-item ${playbackRate === rate ? 'active' : ''}`}\r\n onClick={() => handleSpeedChange(rate)}\r\n >\r\n <span>{rate === 1 ? 'Normal' : `${rate}x`}</span>\r\n {playbackRate === rate && <Check size={12} />}\r\n </div>\r\n ))}\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n\r\n {enablePiP && (\r\n <button\r\n className=\"sdk-control-btn\"\r\n onClick={togglePiP}\r\n title=\"Picture-in-Picture\"\r\n style={{ color: isPiP ? themeColor : '' }}\r\n >\r\n <PictureInPicture size={18} />\r\n </button>\r\n )}\r\n\r\n {enableFullscreen && (\r\n <button className=\"sdk-control-btn\" onClick={toggleFullscreen} title=\"Fullscreen\">\r\n {isFullscreen ? <Minimize size={18} /> : <Maximize size={18} />}\r\n </button>\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n\r\n {enableKeyMoments && showChaptersSidebar && localKeyMoments.length > 0 && (\r\n <div className=\"sdk-sidebar-panel\">\r\n <div className=\"sdk-sidebar-header\">\r\n <div className=\"sdk-sidebar-title\">In this video</div>\r\n <button\r\n className=\"sdk-sidebar-close-btn\"\r\n onClick={() => setShowChaptersSidebar(false)}\r\n title=\"Close panel\"\r\n >\r\n <X size={16} />\r\n </button>\r\n </div>\r\n \r\n <div className=\"sdk-sidebar-tabs\">\r\n <button className=\"sdk-sidebar-tab active\">Key Moments</button>\r\n </div>\r\n\r\n <div className=\"sdk-sidebar-content\">\r\n {localKeyMoments.map((ch, idx) => {\r\n const isActive = activeChapter.index === idx;\r\n return (\r\n <div\r\n key={idx}\r\n className={`sdk-sidebar-chapter-row ${isActive ? 'active' : ''}`}\r\n onClick={() => handleChapterClick(ch.startTime)}\r\n >\r\n <div className=\"sdk-sidebar-chapter-thumbnail\" style={{ overflow: 'hidden' }}>\r\n {(chapterThumbnails[idx] || ch.thumbnail) ? (\r\n <img\r\n src={chapterThumbnails[idx] || ch.thumbnail}\r\n alt={ch.title}\r\n style={{\r\n width: '100%',\r\n height: '100%',\r\n objectFit: 'cover',\r\n display: 'block',\r\n }}\r\n />\r\n ) : (\r\n <ListVideo size={14} className=\"sdk-sidebar-chapter-icon\" style={{ color: isActive ? themeColor : 'rgba(255,255,255,0.4)' }} />\r\n )}\r\n <span className=\"sdk-sidebar-chapter-time\">{formatTime(ch.startTime)}</span>\r\n </div>\r\n <div className=\"sdk-sidebar-chapter-info\">\r\n <div className=\"sdk-sidebar-chapter-title\" style={{ color: isActive ? themeColor : '#ffffff' }}>\r\n {ch.title}\r\n </div>\r\n <div className=\"sdk-sidebar-chapter-duration\">\r\n Start at {formatTime(ch.startTime)}\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n })}\r\n </div>\r\n </div>\r\n )}\r\n </div>\r\n );\r\n};\r\n","import React, { createElement } from 'react';\nimport { createRoot } from 'react-dom/client';\nimport { WebVideoPlayer } from '@goboss/ui-controls';\nimport type { FirebaseConfig } from '@goboss/analytics';\nimport type { Chapter, KeyMoment, SubtitleTrack, AnalyticsEvent } from '@goboss/types';\n\n/**\n * Props accepted by createPlayer — mirrors WebVideoPlayerProps so all\n * framework wrappers (Vue, Angular, Vanilla JS) share the same API surface.\n */\nexport interface PlayerProps {\n src: string;\n chapters?: Chapter[];\n keyMoments?: KeyMoment[];\n subtitles?: SubtitleTrack[];\n watermark?: string;\n themeColor?: string;\n drmConfig?: any;\n firebaseConfig?: FirebaseConfig;\n onAnalyticsEvent?: (event: AnalyticsEvent) => void;\n onKeyMomentsLoaded?: (keyMoments: KeyMoment[]) => void;\n enableControls?: boolean;\n enableChromecast?: boolean;\n enableKeyMoments?: boolean;\n enableSubtitles?: boolean;\n enableQuality?: boolean;\n enablePlaybackSpeed?: boolean;\n enablePiP?: boolean;\n enableFullscreen?: boolean;\n}\n\nexport interface PlayerInstance {\n /** Re-render the player with updated props (src change, theme, etc.) */\n update(props: Partial<PlayerProps>): void;\n /** Unmounts the player and releases all resources */\n destroy(): void;\n}\n\n/**\n * Mounts the full video player (React UI + all controls) into any DOM element.\n * Use this in Vue, Angular, Vanilla JS, or any environment where JSX is unavailable.\n *\n * @example Vanilla JS\n * const player = createPlayer(document.getElementById('player'), { src: 'https://...' });\n * player.destroy(); // cleanup\n *\n * @example Vue 3\n * const player = createPlayer(containerRef.value, { src: props.src });\n * onUnmounted(() => player.destroy());\n *\n * @example Angular\n * const player = createPlayer(this.containerRef.nativeElement, { src: this.src });\n * ngOnDestroy() { this.player.destroy(); }\n */\nexport function createPlayer(container: HTMLElement, props: PlayerProps): PlayerInstance {\n const root = createRoot(container);\n let currentProps = props;\n\n root.render(createElement(WebVideoPlayer as React.FC<PlayerProps>, currentProps));\n\n return {\n update(newProps: Partial<PlayerProps>) {\n currentProps = { ...currentProps, ...newProps };\n root.render(createElement(WebVideoPlayer as React.FC<PlayerProps>, currentProps));\n },\n destroy() {\n root.unmount();\n },\n };\n}\n"],"mappings":";;;;;;;;;AAIA,IAAa,KAAb,MAA6B;CAC3B;CACA;CACA,cAA+B;CAG/B;CACA;CACA;CACA;CAGA,cAA+B;CAC/B,iBAAwC;CACxC,iBAAiC;CAGjC,iBAAiC;CACjC,qBAAqC;CACrC,qBAA4C;CAG5C,gCAAwB,IAAI,IAAoB;CAGhD,wBAA+C;CAC/C,kBAAyC;CAEzC,YAAY,GAA8B,GAAc;EAKtD,AAJA,KAAK,WAAW,GAChB,KAAK,YAAY,KAAK,kBAAkB,GACxC,KAAK,UAAU,IAAM,KAAK,eAAe,CAAG,IAAI,IAChD,KAAK,aAAa,KAAK,cAAc,GACrC,KAAK,cAAc,KAAK,eAAe;CACzC;CAEA,iBAAwB,GAA6B;EACnD,KAAK,WAAW;CAClB;CAEA,MACE,GACA,GACA,IAAiC,CAAC,GAClC;EAwBA,AAtBI,MAAc,UAAU,KAAK,gBAC/B,KAAK,cAAc,IACnB,KAAK,YAAY;GACf,WAAW;GACX,WAAW,KAAK,IAAI;GACpB,aAAa,EAAQ;GACrB,UAAU,EAAQ,YAAY;GAC9B,WAAW,KAAK;GAChB,SAAS,KAAK;GACd,YAAY,KAAK;GACjB,aAAa,KAAK;GAClB,iBAAiB;GACjB,gBAAgB;GAChB,gBAAgB;GAChB,oBAAoB;EACtB,CAAC,IAIC,MAAc,WAChB,KAAK,iBAAiB,KAAK,IAAI,KAE5B,MAAc,WAAW,MAAc,YAAY,KAAK,mBAAmB,SAC9E,KAAK,mBAAmB,KAAK,IAAI,IAAI,KAAK,kBAAkB,KAC5D,KAAK,iBAAiB;EAGxB,IAAM,IAAW,EAAQ,WACrB,KAAK,IAAI,KAAK,KAAK,MAAO,EAAQ,cAAc,EAAQ,WAAY,GAAG,CAAC,IACxE,GAEE,IAAwB;GAC5B;GACA,WAAW,KAAK,IAAI;GACpB,aAAa,EAAQ;GACrB,UAAU,EAAQ,YAAY;GAC9B,QAAQ,EAAQ;GAChB,OAAO,EAAQ;GACf,WAAW,KAAK;GAChB,SAAS,KAAK;GACd,YAAY,KAAK;GACjB,aAAa,KAAK;GAClB,iBAAiB;GACjB,gBAAgB,KAAK,MAAM,KAAK,cAAc;GAC9C,gBAAgB,KAAK;GACrB,oBAAoB,KAAK,MAAM,KAAK,kBAAkB;GACtD,GAAG;EACL;EAEA,KAAK,YAAY,CAAK;CACxB;CAEA,eAAsB,GAA2B,GAAyC;EAExF,AADA,KAAK,cAAc,GACnB,KAAK,oBAAoB,OAAO,kBAAkB;GAOhD,AANI,CAAC,EAAQ,UAAU,CAAC,KAAK,gBAC3B,KAAK,gBAAgB,GAAS,CAAQ,GACtC,KAAK,WAAW,GAAS,CAAQ,GACjC,KAAK,MAAM,aAAa,GAAS,EAAS,CAAC,IAE7C,KAAK,wBAAwB,EAAQ,aACrC,KAAK,kBAAkB,KAAK,IAAI;EAClC,GAAG,GAAI;CACT;CAEA,gBAAuB;EACrB,AAEE,KAAK,uBADL,OAAO,cAAc,KAAK,iBAAiB,GAClB,KAAA;CAE7B;CAEA,aACE,GACA,GACA,GACA;EACI,KAAK,gBAAgB,MACzB,KAAK,cAAc,GAEf,KACF,KAAK,kBACL,KAAK,qBAAqB,KAAK,IAAI,KAC1B,KAAK,uBAAuB,SACrC,KAAK,uBAAuB,KAAK,IAAI,IAAI,KAAK,sBAAsB,KACpE,KAAK,qBAAqB,OAG5B,KAAK,MAAM,IAAc,oBAAoB,iBAAiB,GAAS,EAAS,CAAC;CACnF;CAGA,gBAAwB,GAA2B,GAAyC;EAC1F,IAAM,EAAE,gBAAa,gBAAa;EAClC,IAAI,CAAC,GAAU;EACf,IAAM,IAAQ,IAAc;EAQ5B,KAAK,IAAM,CAAC,GAAW,MAAc;GALnC,CAAC,KAAM,gBAAgB;GACvB,CAAC,IAAK,UAAU;GAChB,CAAC,KAAM,gBAAgB;EAGY,GACnC,AAAI,KAAS,KAAa,CAAC,KAAK,cAAc,IAAI,CAAS,MACzD,KAAK,cAAc,IAAI,CAAS,GAChC,KAAK,MAAM,GAAW,GAAS;GAC7B,GAAG,EAAS;GACZ,iBAAiB,IAAY;EAC/B,CAAC;CAGP;CAGA,WAAmB,GAA2B,GAAyC;EACrF,AACE,KAAK,0BAA0B,QAC/B,KAAK,oBAAoB,QACzB,EAAQ,cAAc,KAAK,wBAAwB,MACnD,KAAK,IAAI,IAAI,KAAK,mBAAmB,QAErC,KAAK,MAAM,SAAS,GAAS,EAAS,CAAC;CAE3C;CAEA,YAAoB,GAAuB;EACzC,IAAI,KAAK,UACP,IAAI;GACF,KAAK,SAAS,CAAK;EACrB,SAAS,GAAK;GACZ,QAAQ,MAAM,0CAA0C,CAAG;EAC7D;CAEJ;CAEA,oBAAoC;EAIlC,OAHI,OAAO,SAAW,OAAe,OAAO,OAAO,cAAe,aACzD,OAAO,WAAW,IAEpB,QAAQ,KAAK,IAAI,EAAE,GAAG,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC;CACpE;CAEA,eAAuB,GAAqB;EAC1C,IAAI;GACF,IAAM,IAAM,IAAI,IAAI,CAAG,GACjB,IAAQ,EAAI,SAAS,MAAM,GAAG,EAAE,OAAO,OAAO;GAKpD,OAHI,EAAM,SAAS,KAAK,EAAM,EAAM,SAAS,GAAG,SAAS,GAAG,KAC1D,EAAM,IAAI,GAEL,EAAM,EAAM,SAAS,MAAM,EAAI;EACxC,QAAQ;GACN,IAAM,IAAQ,EAAI,MAAM,GAAG,EAAE,OAAO,OAAO;GAI3C,OAHI,EAAM,UAAU,KAAK,EAAM,EAAM,SAAS,GAAG,SAAS,GAAG,IACpD,EAAM,EAAM,SAAS,KAEvB,EAAM,EAAM,SAAS,MAAM;EACpC;CACF;CAEA,gBAAgC;EAC9B,IAAM,IAAK,UAAU;EAGrB,OAFI,UAAU,KAAK,CAAE,IAAU,WAC3B,eAAe,KAAK,CAAE,IAAU,WAC7B;CACT;CAEA,iBAAiC;EAC/B,IAAM,IACH,UAAkB,cAClB,UAAkB,iBAClB,UAAkB;EAErB,OADI,MAAa,EAAK,iBAAiB,EAAK,SACrC;CACT;AACF,GClNM,IAA+C;CACnD,eAAe;CACf,OAAO;AACT,GAGM,KAAc,IAAI,IAAI,CAAC,WAAW,CAAC,GAY5B,KAAb,MAAuC;CACrC,YAAsC;CAEtC,YAAY,GAAwB;EAClC,KAAK,KAAK,CAAM;CAClB;CAEA,MAAc,KAAK,GAAuC;EACxD,IAAI;GAEF,IAAI,CAAC,MADmB,EAAY,GACpB;IACd,QAAQ,KAAK,wDAAwD;IACrE;GACF;GACA,IAAM,IAAO,EAAQ,GACf,IAAM,EAAK,WAAW,IAAI,GAAc,CAAM,IAAI,EAAK;GAC7D,KAAK,YAAY,EAAa,CAAG;EACnC,SAAS,GAAK;GACZ,QAAQ,MAAM,8CAA8C,CAAG;EACjE;CACF;CAEA,iBAA2C;EACzC,QAAQ,MAA0B,KAAK,YAAY,CAAK;CAC1D;CAEA,YAAoB,GAA6B;EAE/C,IADI,CAAC,KAAK,aACN,GAAY,IAAI,EAAM,SAAS,GAAG;EAEtC,IAAM,IAAO,EAAqB,EAAM,cAAc,EAAM,WACtD,IAAS,KAAK,YAAY,CAAK;EAErC,IAAI;GACF,GAAS,KAAK,WAAW,GAAM,CAAM;EACvC,SAAS,GAAK;GACZ,QAAQ,MAAM,wCAAwC,CAAG;EAC3D;CACF;CAEA,YAAoB,GAAwD;EAI1E,IAAM,IAAqC;GAEzC,aAAsB,EAAM,aAAa,IAAI,MAAM,GAAG,GAAG;GACzD,WAAsB,EAAM,WAAW,IAAI,MAAM,GAAG,GAAG;GACvD,aAAqB,EAAM,cAAc;GACzC,cAAqB,EAAM,eAAe;GAG1C,cAAqB,KAAK,MAAM,EAAM,WAAW;GACjD,UAAqB,KAAK,MAAM,EAAM,QAAQ;GAG9C,kBAAqB,EAAM,mBAAmB;GAC9C,kBAAqB,EAAM,kBAAkB;GAC7C,iBAAqB,EAAM,kBAAkB;GAC7C,sBAAsB,EAAM,sBAAsB;EACpD;EAqBA,OAlBI,EAAM,cAAoB,EAAE,YAAgB,EAAM,UAAU,MAAM,GAAG,GAAG,IACxE,EAAM,YAAoB,EAAE,UAAiB,EAAM,UACnD,EAAM,kBAAoB,EAAE,iBAAiB,EAAM,cAAc,MAAM,GAAG,GAAG,IAG7E,EAAM,WAAa,KAAA,MAAW,EAAE,SAAS,KAAK,MAAM,EAAM,SAAS,GAAG,IACtE,EAAM,UAAa,KAAA,MAAW,EAAE,QAAS,EAAM,QAG/C,EAAM,iBAAc,EAAE,gBAAgB,EAAM,aAAa,MAAM,GAAG,GAAG,IACrE,EAAM,cAAc,EAAE,aAAgB,EAAM,YAG5C,EAAM,aAAa,KAAA,MAAW,EAAE,YAAY,KAAK,MAAM,EAAM,QAAQ,IAGrE,EAAM,aAAU,EAAE,YAAY,EAAM,WAEjC;CACT;CAGA,UAAgC;EAC9B,OAAO,QAAQ,QAAQ;CACzB;AACF,GCxHa,KAAb,MAA8B;CAC5B,aAAkC,CAAC;CACnC,qBAAqC;CACrC;CAEA,YAAY,IAA0B,CAAC,GAAG;EACxC,KAAK,cAAc,CAAU;CAC/B;CAKA,cAAqB,GAAyB;EAE5C,AADA,KAAK,aAAa,CAAC,GAAG,CAAU,EAAE,MAAM,GAAG,MAAM,EAAE,YAAY,EAAE,SAAS,GAC1E,KAAK,qBAAqB;CAC5B;CAEA,gBAAoC;EAClC,OAAO,KAAK;CACd;CAKA,iBAAwB,GAAqE;EAC3F,IAAM,IAAQ,KAAK,WAAW,WAC3B,MAAM,KAAe,EAAE,aAAa,IAAc,EAAE,OACvD;EAIA,OAHI,MAAU,KAGP;GAAE,WAAW;GAAM,OAAO;EAAG,IAF3B;GAAE,WAAW,KAAK,WAAW;GAAQ;EAAM;CAGtD;CAKA,WAAkB,GAAqB;EACrC,IAAM,EAAE,cAAW,aAAU,KAAK,iBAAiB,CAAW;EAC9D,AAAI,MAAU,KAAK,uBACjB,KAAK,qBAAqB,GACtB,KAAK,0BACP,KAAK,uBAAuB,GAAW,CAAK;CAGlD;CAKA,eAAsB,GAAgE;EACpF,KAAK,yBAAyB;CAChC;CAGA,YAAmB,GAAuB;EACxC,KAAK,cAAc,CAAQ;CAC7B;CACA,cAAkC;EAChC,OAAO,KAAK,cAAc;CAC5B;CACA,kBAAyB,GAAmE;EAC1F,IAAM,IAAM,KAAK,iBAAiB,CAAW;EAC7C,OAAO;GAAE,SAAS,EAAI;GAAW,OAAO,EAAI;EAAM;CACpD;CACA,gBAAuB,GAA8D;EACnF,KAAK,eAAe,CAAQ;CAC9B;AACF,GC9Da,IAAb,MAA6B;CAC3B,SAAkC,CAAC;CACnC,gBAAgC;CAChC,cAAqC,CAAC;CACtC,aAA6B;CAC7B;CACA;CAEA,YAAY,IAA0B,CAAC,GAAG;EACxC,KAAK,SAAS;EACd,IAAM,IAAe,EAAO,MAAM,MAAM,EAAE,SAAS;EACnD,AAAI,KACF,KAAK,eAAe,EAAa,EAAE;CAEvC;CAEA,YAAoC;EAClC,OAAO,KAAK;CACd;CAEA,mBAAkC;EAChC,OAAO,KAAK;CACd;CAEA,aAAoB,GAAkC;EACpD,IAAM,IAAiB,KAAK,OAAO,QAAQ,MAAM,CAAC,EAAE,SAAS,CAAC,EAAE,QAAQ;EACxE,KAAK,SAAS,CAAC,GAAG,GAAgB,GAAG,CAAS;CAChD;CAEA,SAAgB,GAA4B;EAC1C,AAAK,KAAK,OAAO,MAAM,MAAM,EAAE,OAAO,EAAM,EAAE,KAC5C,KAAK,OAAO,KAAK,CAAK;CAE1B;CAEA,cAAqB,GAAiD;EACpE,KAAK,wBAAwB;CAC/B;CAEA,sBAA6B,GAAc;EACzC,AAAI,MAAS,KAAK,eAChB,KAAK,aAAa,GACd,KAAK,4BACP,KAAK,yBAAyB,CAAI;CAGxC;CAKA,MAAa,eAAe,GAAgC;EAE1D,IADA,KAAK,gBAAgB,GACjB,MAAY,OAAO;GAGrB,AAFA,KAAK,cAAc,CAAC,GACpB,KAAK,iBAAiB,CAAC,GACnB,KAAK,yBACP,KAAK,sBAAsB,IAAI;GAEjC;EACF;EAEA,IAAM,IAAQ,KAAK,OAAO,MAAM,MAAM,EAAE,OAAO,CAAO;EACtD,IAAI,CAAC,GAAO;GAGV,AAFA,KAAK,cAAc,CAAC,GACpB,KAAK,iBAAiB,CAAC,GACnB,KAAK,yBACP,KAAK,sBAAsB,IAAI;GAEjC;EACF;EAEA,IAAI,EAAM,SAAS,EAAM,UAAU;GAGjC,AAFA,KAAK,cAAc,CAAC,GACpB,KAAK,iBAAiB,CAAC,GACnB,KAAK,yBACP,KAAK,sBAAsB,CAAK;GAElC;EACF;EAOA,IALI,KAAK,yBACP,KAAK,sBAAsB,IAAI,GAI7B,EAAM,MAAM;GACd,KAAK,cAAc,EAAM;GACzB;EACF;EAGA,IAAI,EAAM,KACR,IAAI;GACF,IAAM,IAAW,MAAM,MAAM,EAAM,GAAG;GACtC,IAAI,CAAC,EAAS,IAAI,MAAU,MAAM,cAAc,EAAS,QAAQ;GACjE,IAAM,IAAO,MAAM,EAAS,KAAK,GAC3B,IAAS,KAAK,cAAc,CAAI;GAEtC,AADA,EAAM,OAAO,GACT,KAAK,kBAAkB,MACzB,KAAK,cAAc;EAEvB,SAAS,GAAK;GAEZ,AADA,QAAQ,MAAM,+CAA+C,EAAM,MAAM,IAAI,CAAG,GAChF,KAAK,cAAc,CAAC;EACtB;CAEJ;CAKA,WAAkB,GAAqB;EACrC,IAAI,KAAK,kBAAkB,SAAS,KAAK,YAAY,WAAW,GAAG;GAEjE,IAAM,IAAQ,KAAK,OAAO,MAAM,MAAM,EAAE,OAAO,KAAK,aAAa;GACjE,CAAI,CAAC,KAAU,CAAC,EAAM,SAAS,CAAC,EAAM,aAChC,KAAK,eAAe,OACtB,KAAK,aAAa,IACd,KAAK,4BAA0B,KAAK,yBAAyB,EAAE;GAGvE;EACF;EAEA,KAAK,iBAAiB,CAAW;CACnC;CAKA,iBAAyB,GAAqB;EAM5C,IAAM,IAJe,KAAK,YAAY,QACnC,MAAQ,KAAe,EAAI,aAAa,KAAe,EAAI,OAG9C,EAAa,KAAK,MAAM,EAAE,IAAI,EAAE,KAAK,QAAQ;EAE7D,AAAI,MAAY,KAAK,eACnB,KAAK,aAAa,GACd,KAAK,4BACP,KAAK,yBAAyB,CAAO;CAG3C;CAEA,iBAAwB,GAAkC;EACxD,KAAK,2BAA2B;CAClC;CAKA,cAAsB,GAAgC;EAGpD,IAAM,IAAO,EAAQ,QAAQ,MAAM,EAAE;EAGrC,IAAI,EAAK,UAAU,EAAE,WAAW,SAAS,GAAG,OAAO,CAAC;EAEpD,IAAM,IAAsB,CAAC,GACvB,IAAQ,EAAK,MAAM,OAAO,GAE5B,IAAgB,GAChB,IAAc,GACd,IAA0B,CAAC,GAC3B,IAAe,IACf,IAAuB,IAGrB,IAAY,6EAEZ,IAAiB,kEAEjB,KAAS,GAAW,GAAW,GAAW,MAC9C,SAAS,GAAG,EAAE,IAAI,OAAO,SAAS,GAAG,EAAE,IAAI,KAAK,SAAS,GAAG,EAAE,IAAI,SAAS,GAAI,EAAE,IAAI,KAEjF,KAAgB,MACpB,EAAE,QAAQ,qCAAqC,EAAE,EAAE,QAAQ,YAAY,EAAE,GAErE,UAAiB;GASrB,AARI,KAAgB,EAAc,SAAS,KACzC,EAAK,KAAK;IACR,WAAW;IACX,SAAS;IACT,MAAM,EAAc,IAAI,CAAY,EAAE,KAAK,IAAI;GACjD,CAAC,GAEH,IAAgB,CAAC,GACjB,IAAe;EACjB;EAEA,KAAK,IAAM,KAAW,GAAO;GAC3B,IAAM,IAAO,EAAQ,KAAK;GAG1B,IAAI,CAAC,GAAM;IAET,AADI,KAAc,EAAS,GAC3B,IAAuB;IACvB;GACF;GAGA,IAAM,IAAQ,EAAK,YAAY;GAC/B,IAAI,MAAU,YAAY,EAAM,WAAW,MAAM,KAC7C,EAAM,WAAW,iBAAiB,KAAK,EAAM,WAAW,OAAO,KAC/D,EAAM,WAAW,QAAQ,GAAG;IAC9B,IAAuB;IACvB;GACF;GACA,IAAI,GAAsB;GAG1B,IAAM,IAAQ,EAAK,MAAM,CAAS;GAClC,IAAI,GAAO;IAIT,AAHA,EAAS,GACT,IAAgB,EAAM,EAAM,IAAI,EAAM,IAAI,EAAM,IAAI,EAAM,EAAE,GAC5D,IAAgB,EAAM,EAAM,IAAI,EAAM,IAAI,EAAM,IAAI,EAAM,EAAE,GAC5D,IAAe;IACf;GACF;GAGA,IAAM,IAAa,EAAK,MAAM,CAAc;GAC5C,IAAI,GAAY;IAId,AAHA,EAAS,GACT,IAAgB,SAAS,EAAW,IAAI,EAAE,IAAI,KAAK,SAAS,EAAW,IAAI,EAAE,IAAI,SAAS,EAAW,IAAI,EAAE,IAAI,KAC/G,IAAgB,SAAS,EAAW,IAAI,EAAE,IAAI,KAAK,SAAS,EAAW,IAAI,EAAE,IAAI,SAAS,EAAW,IAAI,EAAE,IAAI,KAC/G,IAAe;IACf;GACF;GAGK,KAEL,EAAc,KAAK,CAAI;EACzB;EAKA,OAFA,EAAS,GAEF;CACT;AACF,GC3Pa,KAAb,MAAuB;CACrB;CACA;CACA;CAEA,YAAY,GAAoB;EAC9B,KAAK,SAAS;CAChB;CAKA,eAAsB,GAAgC;EAIpD,OAHI,CAAC,KAAK,UAAU,CAAC,KAAK,OAAO,kBAAkB,KAAK,OAAO,eAAe,WAAW,IAChF,KAGP,KAAK,OAAO,eAAe,SAAS,CAAa,KACjD,KAAK,OAAO,eAAe,MAAM,MAAM,EAAc,SAAS,CAAC,CAAC;CAEpE;CAEA,eAA0C;EACxC,OAAO,KAAK,QAAQ;CACtB;CAKA,eAAsB,GAAwB;EAI5C,IAFA,KAAK,gBAAgB,GAEjB,CAAC,KAAK,UAAU,CAAC,KAAK,OAAO,eAAe;EAEhD,IAAM,IAAgB,KAAK,OAAO,eAE5B,IAAM,SAAS,cAAc,KAAK;EAmBxC,AAlBA,EAAI,YAAY,yBAChB,EAAI,YAAY,GAChB,EAAI,MAAM,WAAW,YACrB,EAAI,MAAM,gBAAgB,QAC1B,EAAI,MAAM,aAAa,QACvB,EAAI,MAAM,QAAQ,4BAClB,EAAI,MAAM,WAAW,QACrB,EAAI,MAAM,aAAa,aACvB,EAAI,MAAM,aAAa,QACvB,EAAI,MAAM,SAAS,KACnB,EAAI,MAAM,UAAU,WACpB,EAAI,MAAM,eAAe,OACzB,EAAI,MAAM,aAAa,sBACvB,EAAI,MAAM,iBAAiB,aAC3B,EAAI,MAAM,MAAM,QAChB,EAAI,MAAM,QAAQ,QAElB,EAAU,YAAY,CAAG,GACzB,KAAK,mBAAmB;CAC1B;CAKA,kBAAyB;EAQvB,AAPA,AAEE,KAAK,mBADL,OAAO,cAAc,KAAK,aAAa,GAClB,KAAA,IAEnB,KAAK,oBAAoB,KAAK,iBAAiB,iBACjD,KAAK,iBAAiB,cAAc,YAAY,KAAK,gBAAgB,GAEvE,KAAK,mBAAmB,KAAA;CAC1B;CAEA,iBAAiC;EAC/B,OAAO,CAAC,EACN,KAAK,QAAQ,sBACb,KAAK,QAAQ,uBACb,KAAK,QAAQ;CAEjB;CAEA,gBAAuB;EACrB,OAAO;GACL,UAAU,KAAK,QAAQ,sBAAsB;GAC7C,WAAW,KAAK,QAAQ,uBAAuB;GAC/C,UAAU,KAAK,QAAQ,sBAAsB;GAC7C,kBAAkB,CAAC,CAAC,KAAK,QAAQ;GACjC,gBAAgB,KAAK,QAAQ,kBAAkB,CAAC;EAClD;CACF;AACF,GCnFa,KAAb,MAA2B;CACzB,YAA6B;CAC7B,iBAAiC;CACjC;CAEA,cAA2B;CAC3B,eAA4B;CAC5B,yBAAsC;CAEtC,cAAc;EACZ,KAAK,YAAY;CACnB;CAEA,cAAsB;EACpB,IAAM,UAAa;GAUjB,AATA,KAAK,cAAc,OAAO,KAAK,UAAU,YAAY,YAAY,GACjE,KAAK,YAAY,WAAW;IAC1B,uBAAuB,OAAO,OAAO,KAAK,MAAM;IAChD,gBAAgB,OAAO,OAAO,KAAK,eAAe;GACpD,CAAC,GAED,KAAK,eAAe,IAAI,OAAO,KAAK,UAAU,aAAa,GAC3D,KAAK,yBAAyB,IAAI,OAAO,KAAK,UAAU,uBAAuB,KAAK,YAAY,GAEhG,KAAK,uBAAuB,iBAC1B,OAAO,KAAK,UAAU,sBAAsB,4BACtC;IACJ,KAAK,YAAY,KAAK,aAAa;IACnC,IAAM,IAAU,KAAK,YAAY,kBAAkB;IAGnD,AAFA,KAAK,iBAAiB,IAAU,EAAQ,cAAc,EAAE,eAAe,IAEnE,KAAK,6BACP,KAAK,0BAA0B,KAAK,WAAW,KAAK,cAAc;GAEtE,CACF;EACF;EAEA,AAAI,OAAO,QAAQ,OAAO,KAAK,YAC7B,EAAK,IAEL,OAAO,yBAAyB,MAAyB;GACvD,AAAI,KAAa,EAAK;EACxB;CAEJ;CAEA,kBAAyB;EACvB,OAAO;GACL,WAAW,KAAK;GAChB,YAAY,KAAK;EACnB;CACF;CAKA,MAAa,eAAe,GAAoB,GAAoC;EAClF,IAAI,CAAC,KAAK,aAAa;GACrB,QAAQ,MAAM,oDAAoD;GAClE;EACF;EAEA,IAAI;GACF,MAAM,KAAK,YAAY,eAAe;GAEtC,IAAM,IAAU,KAAK,YAAY,kBAAkB;GACnD,IAAI,KAAW,GAAY;IACzB,IAAM,IAAY,IAAI,OAAO,OAAO,KAAK,MAAM,UAAU,GAAY,WAAW,GAC1E,IAAU,IAAI,OAAO,OAAO,KAAK,MAAM,YAAY,CAAS;IAGlE,AAFA,EAAQ,cAAc,GACtB,EAAQ,WAAW,IACnB,MAAM,EAAQ,UAAU,CAAO;GACjC;EACF,SAAS,GAAK;GACZ,QAAQ,KAAK,iEAAiE,CAAG;EACnF;CACF;CAKA,cAAqB;EACnB,IAAI,KAAK,aACP,IAAI;GACF,KAAK,YAAY,kBAAkB,EAAI;EACzC,SAAS,GAAK;GACZ,QAAQ,KAAK,iDAAiD,CAAG;EACnE;CAEJ;CAEA,kBAAyB,GAA4D;EACnF,KAAK,4BAA4B;CACnC;CAEA,sBAA6B,GAAsC;EACjE,IAAM,IAAW;EACjB,AAAI,OAAO,EAAS,kCAAmC,aACrD,EAAS,+BAA+B,IAExC,QAAQ,KAAK,sEAAsE;CAEvF;CAEA,YAAmB,GAAsC;EACvD,IAAM,IAAW;EACjB,AAAI,OAAO,EAAS,kCAAmC,aACrD,EAAS,+BAA+B,IAExC,QAAQ,KAAK,mEAAmE;CAEpF;CAEA,qBAAqC;EACnC,OACE,CAAC,CAAE,OAAe,yCACjB,UAAU,UAAU,SAAS,QAAQ,KAAK,CAAC,UAAU,UAAU,SAAS,QAAQ;CAErF;AACF,GC/Ha,KAAb,MAA8B;CAC5B;CAEA,YAAY,GAA0B;EACpC,KAAK,SAAS;CAChB;CAKA,aAAoB,GAMX;EACP,IAAI,CAAC,KAAK,QAAQ,OAAO;EAEzB,IAAM,EAAE,cAAW,cAAW,eAAY,YAAS,aAAU,kBAAe,KAAK,QAG7E,IAAQ,KAAK,MAAM,IAAO,CAAQ;EAEtC,AADI,IAAQ,MAAG,IAAQ,IACnB,KAAS,MAAY,IAAQ,IAAa;EAG9C,IAAM,IAAM,IAAQ,GACd,IAAM,KAAK,MAAM,IAAQ,CAAO;EAEtC,OAAO;GACL,KAAK;GACL,GAAG,IAAM;GACT,GAAG,IAAM;GACT,OAAO;GACP,QAAQ;EACV;CACF;CAEA,gBAAgC;EAC9B,OAAO,CAAC,CAAC,KAAK;CAChB;AACF,GClCa,KAAb,MAA4B;CAC1B;CACA;CACA,cAAkC;CAClC,eAAuD;CACvD,gBAAsC;EAAE,IAAI;EAAI,QAAQ;EAAG,OAAO;EAAG,SAAS;EAAG,OAAO;CAAO;CAC/F,gBAAwC,CAAC;CAGzC;CACA;CACA;CACA;CACA;CACA;CACA;CAGA,kCAA0B,IAAI,IAAyE;CACvG,uBAA8C;CAG9C,iBAA6E,CAAC;CAG9E,iBAA6E,CAAC;CAE9E,YAAY,GAA2B;EAkBrC,AAjBA,KAAK,UAAU;GACb,UAAU;GACV,OAAO;GACP,YAAY;GACZ,GAAG;EACL,GAGA,KAAK,YAAY,IAAI,GAAgB,KAAK,QAAQ,mBAAmB,KAAK,QAAQ,GAAG,GACrF,KAAK,aAAa,IAAI,GAAiB,KAAK,QAAQ,cAAc,KAAK,QAAQ,YAAY,CAAC,CAAC,GAC7F,KAAK,WAAW,KAAK,YACrB,KAAK,YAAY,IAAI,EAAgB,KAAK,QAAQ,SAAS,GAC3D,KAAK,MAAM,IAAI,GAAU,KAAK,QAAQ,GAAG,GACzC,KAAK,UAAU,IAAI,GAAc,GACjC,KAAK,aAAa,IAAI,GAAiB,KAAK,QAAQ,UAAU,GAG9D,KAAK,iBAAiB;CACxB;CAKA,mBAA2B;EACzB,IAAM,IAAgB,OAAO,SAAS;EACtC,IAAI,CAAC,KAAK,IAAI,eAAe,CAAa,GAAG;GAC3C,IAAM,IAAW,gEAAgE,EAAc;GAQ/F,MAPA,QAAQ,MAAM,CAAQ,GACtB,KAAK,QAAQ,UAAU,YAAY;;;8DAGqB,EAAS;;SAGvD,MAAM,CAAQ;EAC1B;EAyBA,AAtBA,KAAK,eAAe,SAAS,cAAc,OAAO,GAClD,KAAK,aAAa,YAAY,mBAC9B,KAAK,aAAa,MAAM,QAAQ,QAChC,KAAK,aAAa,MAAM,SAAS,QACjC,KAAK,aAAa,MAAM,UAAU,SAClC,KAAK,aAAa,MAAM,kBAAkB,WAC1C,KAAK,aAAa,cAAc,IAChC,KAAK,aAAa,WAAW,IAC7B,KAAK,aAAa,QAAQ,CAAC,CAAC,KAAK,QAAQ,OAEzC,KAAK,QAAQ,UAAU,YAAY,KAAK,YAAY,GAGpD,KAAK,IAAI,eAAe,KAAK,QAAQ,SAAS,GAG9C,KAAK,gBAAgB,GAGrB,KAAK,WAAW,KAAK,QAAQ,GAAG,GAGhC,KAAK,wBAAwB;CAC/B;CAKA,kBAA0B;EACxB,IAAM,IAAQ,KAAK;EAuEnB,AArEA,EAAM,iBAAiB,cAAc;GAGnC,AAFA,KAAK,UAAU,MAAM,QAAQ,GAAO,KAAK,oBAAoB,CAAC,GAC9D,KAAK,UAAU,eAAe,SAAa,KAAK,oBAAoB,CAAC,GACrE,KAAK,KAAK,QAAQ,IAAI;EACxB,CAAC,GAED,EAAM,iBAAiB,eAAe;GAGpC,AAFA,KAAK,UAAU,MAAM,SAAS,GAAO,KAAK,oBAAoB,CAAC,GAC/D,KAAK,UAAU,cAAc,GAC7B,KAAK,KAAK,SAAS,IAAI;EACzB,CAAC,GAED,EAAM,iBAAiB,oBAAoB;GACzC,IAAM,IAAO,EAAM;GAKnB,IAJA,KAAK,SAAS,WAAW,CAAI,GAC7B,KAAK,UAAU,WAAW,CAAI,GAG1B,KAAK,yBAAyB,MAAM;IAEtC,IAAM,KADO,KAAK,gBAAgB,IAAI,KAAK,oBAAoB,KAAK,CAAC,GACjD,QAAO,MAAK,KAAQ,EAAE,aAAa,IAAO,EAAE,OAAO;IACvE,KAAK,UAAU,sBAAsB,EAAO,KAAI,MAAK,EAAE,IAAI,EAAE,KAAK,QAAQ,CAAC;GAC7E;GAEA,KAAK,KAAK,cAAc,CAAI;EAC9B,CAAC,GAED,EAAM,iBAAiB,iBAAiB;GAEtC,AADA,KAAK,UAAU,MAAM,QAAQ,GAAO,KAAK,oBAAoB,CAAC,GAC9D,KAAK,KAAK,WAAW,EAAM,WAAW;EACxC,CAAC,GAED,EAAM,iBAAiB,iBAAiB;GAEtC,AADA,KAAK,UAAU,aAAa,IAAM,SAAa,KAAK,oBAAoB,CAAC,GACzE,KAAK,KAAK,aAAa,EAAI;EAC7B,CAAC,GAED,EAAM,iBAAiB,iBAAiB;GAEtC,AADA,KAAK,UAAU,aAAa,IAAO,SAAa,KAAK,oBAAoB,CAAC,GAC1E,KAAK,KAAK,aAAa,EAAK;EAC9B,CAAC,GAED,EAAM,iBAAiB,sBAAsB;GAE3C,AADA,KAAK,UAAU,MAAM,iBAAiB,GAAO,KAAK,oBAAoB,CAAC,GACvE,KAAK,KAAK,gBAAgB;IAAE,QAAQ,EAAM;IAAQ,OAAO,EAAM;GAAM,CAAC;EACxE,CAAC,GAED,EAAM,iBAAiB,oBAAoB;GAEzC,AADA,KAAK,UAAU,MAAM,gBAAgB,GAAO,KAAK,oBAAoB,CAAC,GACtE,KAAK,KAAK,cAAc,EAAM,YAAY;EAC5C,CAAC,GAED,EAAM,iBAAiB,eAAe;GAIpC,AAHA,KAAK,UAAU,MAAM,SAAS,GAAO,KAAK,oBAAoB,CAAC,GAC/D,KAAK,UAAU,MAAM,YAAY,GAAO;IAAE,GAAG,KAAK,oBAAoB;IAAG,iBAAiB;GAAI,CAAC,GAC/F,KAAK,UAAU,cAAc,GAC7B,KAAK,KAAK,SAAS,IAAI;EACzB,CAAC,GAED,EAAM,iBAAiB,+BAA+B;GAEpD,AADA,KAAK,UAAU,MAAM,cAAc,GAAO;IAAE,GAAG,KAAK,oBAAoB;IAAG,cAAc;GAAU,CAAC,GACpG,KAAK,KAAK,aAAa,EAAI;EAC7B,CAAC,GAED,EAAM,iBAAiB,+BAA+B;GAEpD,AADA,KAAK,UAAU,MAAM,cAAc,GAAO;IAAE,GAAG,KAAK,oBAAoB;IAAG,cAAc;GAAS,CAAC,GACnG,KAAK,KAAK,aAAa,EAAK;EAC9B,CAAC,GAED,EAAM,iBAAiB,eAAe;GACpC,IAAM,IAAM,EAAM;GAKlB,AAJA,KAAK,UAAU,MAAM,SAAS,GAAO;IACnC,GAAG,KAAK,oBAAoB;IAC5B,cAAc,IAAM,GAAG,EAAI,KAAK,IAAI,EAAI,YAAY;GACtD,CAAC,GACD,KAAK,KAAK,SAAS,CAAG;EACxB,CAAC;EAGD,IAAM,IAAa,EAAM;EACzB,IAAI,GAAY;GACd,IAAM,KAAuB,GAAkB,MAAkB;IAK/D,IAJI,EAAM,SAAS,eAAe,EAAM,SAAS,cACvB,KAAK,UAAU,UAAU,EAAE,MAClD,MAAM,EAAE,YAAY,EAAE,qBAAqB,CAE1C,GAAmB;IACvB,IAAM,IAAY,EAAM,SAAS;IACjC,KAAK,UAAU,SAAS;KACtB,IAAI,UAAU;KACd,OAAO,EAAM,SAAS,EAAM,aAAa,IAAY,MAAM,IAAQ,MAAM,SAAS,IAAQ;KAC1F,MAAM,EAAM,YAAY;KACxB,WAAW,CAAC,KAAK,gBAAiB,EAAc,WAAW,EAAM,SAAS;KAC1E,UAAU;KACV,kBAAkB;IACpB,CAAC;GACH;GAiBA,AAdA,EAAM,iBAAiB,wBAAwB;IAC7C,KAAK,UAAU,MAAM,gBAAgB,GAAO,KAAK,oBAAoB,CAAC;IACtE,IAAI,IAAQ,GACN,IAAc,KAAK,cAAc,CAAC,UAAU,IAAI,CAAC,aAAa,UAAU;IAC9E,KAAK,IAAI,IAAI,GAAG,IAAI,EAAW,QAAQ,KAAK;KAC1C,IAAI,CAAC,EAAY,SAAS,EAAW,GAAG,IAAI,GAAG;KAC/C,IAAM,IAAS,KAAK,UAAU,UAAU,EAAE;KAE1C,AADA,EAAoB,EAAW,IAAI,CAAC,GAChC,KAAK,UAAU,UAAU,EAAE,SAAS,KAAQ;IAClD;IACA,AAAI,IAAQ,KAAG,KAAK,KAAK,mBAAmB,KAAK,UAAU,UAAU,CAAC;GACxE,CAAC,GAGD,EAAW,iBAAiB,aAAa,MAAU;IACjD,IAAM,IAAQ,EAAM;IACf,OACL,KAAK,IAAI,IAAI,GAAG,IAAI,EAAW,QAAQ,KAAK;KAC1C,IAAI,EAAW,OAAO,GAAO;KAC7B,IAAM,IAAS,KAAK,UAAU,UAAU,EAAE;KAE1C,AADA,EAAoB,GAAO,CAAC,GACxB,KAAK,UAAU,UAAU,EAAE,SAAS,KACtC,KAAK,KAAK,mBAAmB,KAAK,UAAU,UAAU,CAAC;KAEzD;IACF;GACF,CAAC;EACH;EAGA,KAAK,UAAU,eAAe,MAAU;GACtC,IAAI,MAAU;QACR,KAAK,aAEP,KAAK,YAAY,gBAAgB;SAC5B,IAAI,GAAY;KACrB,KAAK,IAAI,IAAI,GAAG,IAAI,EAAW,QAAQ,KAErC,AADA,EAAW,GAAG,OAAO,YACrB,EAAW,GAAG,cAAc;KAE9B,KAAK,UAAU,sBAAsB,EAAE;IACzC;UACK,IAAI,EAAM,OACX,KAAK,eAAe,EAAM,eAAe,KAAA,MAI3C,KAAK,YAAY,gBAAgB,EAAM;QAEpC,IAAI,EAAM,YACX,KAAc,EAAM,qBAAqB,KAAA,KAAa,EAAW,EAAM,mBACzE,KAAK,IAAI,IAAI,GAAG,IAAI,EAAW,QAAQ,KAErC,IADA,EAAW,GAAG,cAAc,MACxB,MAAM,EAAM,kBAAkB;IAChC,EAAW,GAAG,OAAO;IACrB,IAAM,IAAK,EAAW;IACtB,EAAG,oBAAoB;KACrB,IAAM,IAAa,EAAG;KACtB,IAAI,KAAc,EAAW,SAAS,GAAG;MACvC,IAAM,IAAO,MAAM,KAAK,CAAU,EAC/B,KAAK,MAAa,EAAI,IAAI,EAC1B,KAAK,QAAQ;MAChB,KAAK,UAAU,sBAAsB,CAAI;KAC3C,OACE,KAAK,UAAU,sBAAsB,EAAE;IAE3C;GACF,OACE,EAAW,GAAG,OAAO;EAK/B,CAAC;CACH;CAEA,eAAuB,GAAqB;EAC1C,IAAI,EAAI,SAAS,8BAA8B,GAC7C,IAAI;GACF,IAAM,IAAS,IAAI,IAAI,CAAG;GAC1B,OAAO,eAAe,EAAO,WAAW,EAAO;EACjD,QAAY;GACV,OAAO,EAAI,QAAQ,wCAAwC,cAAc;EAC3E;EAEF,OAAO;CACT;CAKA,WAAmB,GAAa;EAE9B,AADA,KAAK,8BAA8B,CAAG,GACtC,KAAK,6BAA6B,CAAG;EACrC,IAAM,IAAa,KAAK,eAAe,CAAG,GACpC,IAAQ,EAAW,SAAS,OAAO,KAAK,EAAW,SAAS,OAAO,GACnE,IAAS,EAAW,SAAS,MAAM,KAAK,EAAW,SAAS,MAAM;EAExE,IAAI,OACE,EAAI,YAAY,GAAG;GACrB,IAAM,IAAM,IAAI,EAAI;IAClB,cAAc;IACd,gBAAgB;IAChB,kBAAkB;IAClB,oBAAoB;IAKpB,0BAA0B;IAC1B,WAAW,GAAK,MAAQ;KACtB,IAAI,IAAQ,KAAK,IAAI,aAAa;KAClC,IAAI,CAAC,GACH,IAAI;MACF,IAAM,IAAe,IAAI,IAAI,CAAG,GAC1B,IAAc,EAAa,aAAa,IAAI,OAAO,KACrC,EAAa,aAAa,IAAI,KAAK,KACnC,EAAa,aAAa,IAAI,MAAM,KACpC,EAAa,aAAa,IAAI,WAAW;MAC7D,AAAI,MACF,IAAQ;KAEZ,QAAY,CAAC;KAGf,IAAI;MACF,IAAM,IAAa,KAAK,eAAe,CAAG,GACpC,IAAS,IAAI,IAAI,GAAY,OAAO,SAAS,MAAM;MAIzD,AAHI,KACF,EAAO,aAAa,IAAI,SAAS,CAAK,GAExC,EAAI,KAAK,OAAO,EAAO,SAAS,GAAG,EAAI;KACzC,SAAS,GAAG;MACV,QAAQ,MAAM,mDAAmD,CAAC;KACpE;IACF;GACF,CAAC;GAoFD,AAlFA,KAAK,cAAc,GAEnB,EAAI,WAAW,CAAU,GACzB,EAAI,YAAY,KAAK,YAAY,GAKjC,EAAI,GAAG,EAAI,OAAO,wBAAwB,GAAG,MAAS;IAGpD,IAFA,KAAK,uBAAuB,MAC5B,KAAK,UAAU,sBAAsB,EAAE,GACnC,EAAK,OAAO,IAAI;KAClB,IAAM,IAAW,EAAI,eAAe,EAAK;KACzC,KAAK,uBAAuB,GAAU,UAAU,YAAY,YAAY,EAAK;IAC/E;GACF,CAAC,GAID,EAAI,GAAG,EAAI,OAAO,cAAc,GAAG,MAA6G;IAC9I,IAAM,IAAW,KAAK,gBAAgB,IAAI,EAAK,KAAK,KAAK,CAAC,GAEpD,IADW,EAAK,KACG,QACvB,MAAK,CAAC,EAAS,MAAK,MAAK,EAAE,cAAc,EAAE,aAAa,EAAE,YAAY,EAAE,OAAO,CACjF;IACA,AAAI,EAAQ,SAAS,KACnB,KAAK,gBAAgB,IAAI,EAAK,OAAO,CAAC,GAAG,GAAU,GAAG,CAAO,CAAC;GAElE,CAAC,GAED,EAAI,GAAG,EAAI,OAAO,uBAAuB;IAEvC,KAAK,gBAAgB,EAAI,OAAO,KAAK,GAAO,OAAW;KACrD,IAAI;KACJ,QAAQ,EAAM;KACd,OAAO,EAAM;KACb,SAAS,EAAM;KACf,OAAO,EAAM,SAAS,GAAG,EAAM,OAAO,KAAK,UAAU,IAAQ;IAC/D,EAAE;IAGF,IAAM,IAAwB;KAAE,IAAI;KAAI,QAAQ;KAAG,OAAO;KAAG,SAAS;KAAG,OAAO;IAAO;IAMvF,AALA,KAAK,cAAc,QAAQ,CAAO,GAClC,KAAK,gBAAgB,GAErB,KAAK,KAAK,iBAAiB,KAAK,aAAa,GAEzC,KAAK,QAAQ,YACf,KAAK,KAAK;GAEd,CAAC,GAGD,EAAI,GAAG,EAAI,OAAO,4BAA4B;IAC5C,AAAI,EAAI,YAAY,SAAS,MAC3B,KAAK,iBAAiB,EAAI,YAAY,KAAK,GAAO,OAAS;KACzD,IAAI;KACJ,OAAO,EAAM,QAAQ,EAAM,QAAQ,SAAS,IAAM;KAClD,MAAM,EAAM,QAAQ;IACtB,EAAE,GACF,KAAK,KAAK,qBAAqB,KAAK,cAAc;GAEtD,CAAC,GAED,EAAI,GAAG,EAAI,OAAO,uBAAuB,GAAG,MAAS;IAEnD,AADA,KAAK,UAAU,MAAM,sBAAsB,KAAK,cAAc,KAAK,oBAAoB,CAAC,GACxF,KAAK,KAAK,oBAAoB,EAAK,EAAE;GACvC,CAAC,GAGD,EAAI,GAAG,EAAI,OAAO,iBAAiB,GAAG,MAAS;IAC7C,IAAM,IAAY,KAAK,cAAc,MAAM,MAAM,EAAE,OAAO,EAAK,KAAK;IACpE,AAAI,MACF,KAAK,UAAU,MAAM,kBAAkB,KAAK,cAAc;KACxD,GAAG,KAAK,oBAAoB;KAC5B,WAAW,EAAU;KACrB,SAAS,EAAU;IACrB,CAAC,GACD,KAAK,KAAK,iBAAiB,CAAS;GAExC,CAAC,GAED,EAAI,GAAG,EAAI,OAAO,QAAQ,GAAG,MAAS;IACpC,AAAI,EAAK,UACH,EAAK,SAAS,EAAI,WAAW,iBAC/B,KAAK,UAAU,MAAM,iBAAiB,KAAK,cAAc;KACvD,GAAG,KAAK,oBAAoB;KAC5B,cAAc,EAAK;KACnB,WAAW;IACb,CAAC,GACD,EAAI,UAAU,KACL,EAAK,SAAS,EAAI,WAAW,cACtC,EAAI,kBAAkB,IAEtB,KAAK,QAAQ;GAGnB,CAAC;EACH,OAAO,AAAI,KAAK,aAAa,YAAY,+BAA+B,MAEtE,KAAK,aAAa,MAAM,GACxB,KAAK,gBAAgB,CAAC;GAAE,IAAI;GAAG,QAAQ;GAAK,OAAO;GAAM,SAAS;GAAS,OAAO;EAAgB,CAAC,GACnG,KAAK,gBAAgB,KAAK,cAAc,IAEpC,KAAK,QAAQ,YACf,KAAK,aAAa,KAAK,EAAE,MAAM,QAAQ,KAAK;OAG3C,IAAI,GAAQ;GAEjB,IAAM,IAAO,EAAO,YAAY,EAAE,OAAO;GAazC,AAZA,KAAK,eAAe,GACpB,EAAK,WAAW,KAAK,cAAc,GAAK,CAAC,CAAC,KAAK,QAAQ,QAAQ,GAC/D,EAAK,eAAe,EAClB,WAAW;IACT,KAAK,EAAE,mBAAmB;KAAE,OAAO;KAAM,OAAO;IAAK,EAAE;IACvD,QAAQ,EAAE,wBAAwB,GAAG;GACvC,EACF,CAAC,GAGD,KAAK,gBAAgB,CAAC;IAAE,IAAI;IAAG,QAAQ;IAAG,OAAO;IAAG,SAAS;IAAG,OAAO;GAAc,CAAC,GACtF,KAAK,gBAAgB,KAAK,cAAc,IACxC,KAAK,KAAK,iBAAiB,KAAK,aAAa;EAC/C,OAME,AAJA,KAAK,aAAa,MAAM,GACxB,KAAK,gBAAgB,CAAC;GAAE,IAAI;GAAG,QAAQ;GAAM,OAAO;GAAM,SAAS;GAAS,OAAO;EAAiB,CAAC,GACrG,KAAK,gBAAgB,KAAK,cAAc,IAEpC,KAAK,QAAQ,YACf,KAAK,aAAa,KAAK,EAAE,MAAM,QAAQ,KAAK;CAGlD;CAKA,0BAAkC;EAChC,IAAI,kBAAkB,WAAW;GAC/B,UAAU,aAAa,WAAW,IAAI,cAAc;IAClD,OAAO;IACP,OAAO;IACP,SAAS,CACP;KAAE,KAAK;KAA2E,OAAO;KAAW,MAAM;IAAa,CACzH;GACF,CAAC;GAED,IAAI;IAOF,AANA,UAAU,aAAa,iBAAiB,cAAc,KAAK,KAAK,CAAC,GACjE,UAAU,aAAa,iBAAiB,eAAe,KAAK,MAAM,CAAC,GACnE,UAAU,aAAa,iBAAiB,iBAAiB,MAAY;KACnE,IAAM,IAAS,EAAQ,cAAc;KACrC,KAAK,KAAK,KAAK,aAAa,cAAc,CAAM;IAClD,CAAC,GACD,UAAU,aAAa,iBAAiB,gBAAgB,MAAY;KAClE,IAAM,IAAS,EAAQ,cAAc;KACrC,KAAK,KAAK,KAAK,aAAa,cAAc,CAAM;IAClD,CAAC;GACH,QAAQ;IACN,QAAQ,KAAK,uEAAuE;GACtF;EACF;CACF;CAIA,OAAc;EACZ,KAAK,aAAa,KAAK,EAAE,OAAO,MAAQ;GACtC,QAAQ,KAAK,iEAAiE,CAAG;EACnF,CAAC;CACH;CAEA,QAAe;EACb,KAAK,aAAa,MAAM;CAC1B;CAEA,KAAY,GAAiB;EAC3B,IAAM,IAAS,KAAK,IAAI,GAAG,KAAK,IAAI,GAAS,KAAK,YAAY,CAAC,CAAC;EAChE,KAAK,aAAa,cAAc;CAClC;CAEA,UAAiB,GAAgB;EAC/B,IAAM,IAAM,KAAK,IAAI,GAAG,KAAK,IAAI,GAAQ,CAAC,CAAC;EAE3C,AADA,KAAK,aAAa,SAAS,GAC3B,KAAK,aAAa,QAAQ,MAAQ;CACpC;CAEA,QAAe,GAAgB;EAC7B,KAAK,aAAa,QAAQ;CAC5B;CAEA,gBAAuB,GAAc;EACnC,KAAK,aAAa,eAAe;CACnC;CAKA,WAAkB,GAAmB;EACnC,IAAM,IAAO,KAAK,cAAc,MAAM,MAAM,EAAE,OAAO,CAAS;EACzD,MAEL,KAAK,gBAAgB,GAEjB,KAAK,cACP,KAAK,YAAY,eAAe,IAEhC,QAAQ,KAAK,0FAA0F;CAE3G;CAKA,MAAa,yBAA2C;EACtD,IAAI,CAAC,SAAS,yBAEZ,OADA,QAAQ,KAAK,iEAAiE,GACvE;EAGT,IAAI;GAMA,OALE,SAAS,2BACX,MAAM,SAAS,qBAAqB,GAC7B,OAEP,MAAM,KAAK,aAAa,wBAAwB,GACzC;EAEX,SAAS,GAAK;GAEZ,OADA,QAAQ,MAAM,uCAAuC,CAAG,GACjD;EACT;CACF;CAKA,mBAA0B;EACxB,IAAM,IAAY,KAAK,QAAQ;EAC/B,AAAK,SAAS,oBAKZ,SAAS,eAAe,IAJxB,EAAU,kBAAkB,EAAE,OAAO,MAAQ;GAC3C,QAAQ,MAAM,sDAAsD,CAAG;EACzE,CAAC;CAIL;CAIA,kBAA2C;EACzC,OAAO,KAAK;CACd;CAEA,iBAAgC;EAC9B,OAAO,KAAK,aAAa;CAC3B;CAEA,cAA6B;EAC3B,OAAO,KAAK,aAAa,YAAY;CACvC;CAEA,YAA2B;EACzB,OAAO,KAAK,aAAa;CAC3B;CAEA,UAA0B;EACxB,OAAO,KAAK,aAAa;CAC3B;CAEA,kBAAiC;EAC/B,OAAO,KAAK,aAAa;CAC3B;CAEA,mBAAwC;EACtC,OAAO,KAAK;CACd;CAEA,mBAA0C;EACxC,OAAO,KAAK;CACd;CAEA,kBAAiC;EAC/B,IAAM,IAAW,KAAK,aAAa,UAC7B,IAAO,KAAK,aAAa;EAC/B,KAAK,IAAI,IAAI,GAAG,IAAI,EAAS,QAAQ,KACnC,IAAI,KAAQ,EAAS,MAAM,CAAC,KAAK,KAAQ,EAAS,IAAI,CAAC,GACrD,OAAO,EAAS,IAAI,CAAC,IAAI;EAG7B,OAAO;CACT;CAEA,oBAAkE;EAChE,IAAM,IAAW,KAAK,aAAa,UAC7B,IAAS,CAAC;EAChB,KAAK,IAAI,IAAI,GAAG,IAAI,EAAS,QAAQ,KACnC,EAAO,KAAK;GAAE,OAAO,EAAS,MAAM,CAAC;GAAG,KAAK,EAAS,IAAI,CAAC;EAAE,CAAC;EAEhE,OAAO;CACT;CAEA,sBAAuD;EACrD,OAAO;GACL,WAAW,KAAK,cAAc;GAC9B,SAAS,KAAK,cAAc;GAC5B,QAAQ,KAAK,aAAa;GAC1B,OAAO,KAAK,aAAa;EAC3B;CACF;CAIA,GAAU,GAAe,GAAoC;EAI3D,AAHK,KAAK,eAAe,OACvB,KAAK,eAAe,KAAS,CAAC,IAEhC,KAAK,eAAe,GAAO,KAAK,CAAQ;CAC1C;CAEA,IAAW,GAAe,GAAoC;EACvD,KAAK,eAAe,OACzB,KAAK,eAAe,KAAS,KAAK,eAAe,GAAO,QAAQ,MAAO,MAAO,CAAQ;CACxF;CAEA,KAAa,GAAe,GAAG,GAAa;EACrC,KAAK,eAAe,MACzB,KAAK,eAAe,GAAO,SAAS,MAAO,EAAG,GAAG,CAAI,CAAC;CACxD;CAEA,MAAc,6BAA6B,GAAa;EACtD,IAAI;GACF,IAAM,IAAa,KAAK,eAAe,CAAG,GACpC,IAAM,MAAM,MAAM,CAAU;GAClC,IAAI,CAAC,EAAI,IAAI;GAEb,IAAM,KAAQ,MADK,EAAI,KAAK,GACT,MAAM,IAAI,GAEzB,IAA4B,KAAK,IAAI,aAAa,KAAK,KAAA;GAC3D,IAAI,CAAC,GACH,IAAI;IACF,IAAM,IAAI,IAAI,IAAI,CAAG;IACrB,IAAQ,EAAE,aAAa,IAAI,OAAO,KAAK,EAAE,aAAa,IAAI,KAAK,KACvD,EAAE,aAAa,IAAI,MAAM,KAAK,EAAE,aAAa,IAAI,WAAW,KAAK,KAAA;GAC3E,QAAQ,CAAc;GAGxB,IAAM,IAAU,EAAI,UAAU,GAAG,EAAI,YAAY,GAAG,IAAI,CAAC,GACnD,IAGD,CAAC;GAEN,KAAK,IAAM,KAAQ,GAAO;IACxB,IAAI,CAAC,EAAK,WAAW,6BAA6B,GAAG;IACrD,IAAM,IAAY,EAAK,MAAM,gBAAgB,GACvC,IAAY,EAAK,MAAM,oBAAoB,GAC3C,IAAW,EAAK,MAAM,eAAe,GACrC,IAAY,eAAe,KAAK,CAAI;IAC1C,IAAI,CAAC,GAAU;IAEf,IAAM,IAAM,EAAS,IACf,IAAM,EAAe,QACrB,IAAQ,IAAY,EAAU,KAAK,IAAY,EAAU,KAAK,SAAS,IAAM,KAC7E,IAAO,IAAY,EAAU,KAAK;IAKxC,IAD0B,iBAAiB,KAAK,CAC5C,GAAmB;KACrB,EAAe,KAAK;MAAE,IAAI,YAAY;MAAO;MAAO;MAAM;MAAW,OAAO;MAAM,YAAY;KAAI,CAAC;KACnG;IACF;IAGA,IAAM,IAAc,EAAI,WAAW,MAAM,IAAI,IAAM,IAAU,GACzD,IAAS,KAAK,eAAe,CAAW;IAC5C,IAAI,GACF,IAAI;KACF,IAAM,IAAS,IAAI,IAAI,GAAQ,OAAO,SAAS,MAAM;KAErD,AADA,EAAO,aAAa,IAAI,SAAS,CAAK,GACtC,IAAS,EAAO,SAAS;IAC3B,QAAQ,CAAc;IAExB,EAAe,KAAK;KAAE,IAAI,YAAY;KAAO;KAAO;KAAM;KAAW,KAAK;IAAO,CAAC;GACpF;GAEA,AAAI,EAAe,SAAS,MAC1B,KAAK,UAAU,aAAa,CAAc,GAC1C,KAAK,KAAK,mBAAmB,KAAK,UAAU,UAAU,CAAC;EAE3D,SAAS,GAAG;GACV,QAAQ,MAAM,yDAAyD,CAAC;EAC1E;CACF;CAEA,MAAc,8BAA8B,GAAa;EACvD,IAAI;GACF,IAAM,IAAa,KAAK,eAAe,CAAG,GACpC,IAAM,MAAM,MAAM,CAAU;GAClC,IAAI,CAAC,EAAI,IAAI;GAEb,IAAM,KAAQ,MADK,EAAI,KAAK,GACT,MAAM,IAAI,GACvB,IAAsE,CAAC;GAE7E,KAAK,IAAM,KAAQ,GACjB,IAAI,EAAK,WAAW,mBAAmB,GAAG;IACxC,IAAM,IAAU,EAAK,MAAM,cAAc,GACnC,IAAiB,EAAK,MAAM,sBAAsB,GAClD,IAAa,EAAK,MAAM,mBAAmB;IAEjD,AAAI,KAAW,KAAkB,MAC1B,EAAW,MAAK,MAAK,EAAE,OAAO,EAAQ,EAAE,KAC3C,EAAW,KAAK;KACd,IAAI,EAAQ;KACZ,WAAW,EAAe;KAC1B,OAAO,EAAW;IACpB,CAAC;GAGP;GAGF,IAAI,EAAW,SAAS,GAAG;IACzB,EAAW,MAAM,GAAG,MAAM,EAAE,UAAU,cAAc,EAAE,SAAS,CAAC;IAChE,IAAM,IAAW,IAAI,KAAK,EAAW,GAAG,SAAS,EAAE,QAAQ,GAErD,IAAgC,EAAW,KAAK,MAAM;KAC1D,IAAM,KAAa,IAAI,KAAK,EAAE,SAAS,EAAE,QAAQ,IAAI,KAAY;KACjE,OAAO;MACL,OAAO,EAAE;MACE;MACX,SAAS;KACX;IACF,CAAC;IAED,KAAK,IAAI,IAAI,GAAG,IAAI,EAAiB,QAAQ,KAC3C,AAAI,IAAI,EAAiB,SAAS,IAChC,EAAiB,GAAG,UAAU,EAAiB,IAAI,GAAG,YAEtD,EAAiB,GAAG,UAAU;IAMlC,AAFA,KAAK,WAAW,cAAc,CAAgB,GAC9C,KAAK,KAAK,oBAAoB,CAAgB,GAC9C,KAAK,KAAK,kBAAkB,CAAgB;GAC9C;EACF,SAAS,GAAG;GACV,QAAQ,MAAM,uCAAuC,CAAC;EACxD;CACF;CAIA,iBAA4E;EAC1E,OAAO,KAAK;CACd;CAEA,cAAqB,GAAY;EAC/B,AAAI,KAAK,gBACP,KAAK,YAAY,aAAa;CAElC;CAIA,UAAiB;EAyBf,AAxBA,KAAK,gBAAgB,MAAM,GAC3B,KAAK,uBAAuB,MAC5B,KAAK,UAAU,cAAc,GAC7B,KAAK,IAAI,gBAAgB,GAEzB,AAEE,KAAK,iBADL,KAAK,YAAY,QAAQ,GACN,OAGrB,AAEE,KAAK,kBADL,KAAK,aAAa,MAAM,GACJ,OAGlB,KAAK,iBACP,KAAK,aAAa,MAAM,GACxB,KAAK,aAAa,gBAAgB,KAAK,GACvC,KAAK,aAAa,KAAK,GACnB,KAAK,aAAa,iBACpB,KAAK,aAAa,cAAc,YAAY,KAAK,YAAY,IAIjE,KAAK,iBAAiB,CAAC;CACzB;AACF,GC1yBM,KAAkB,MAAwB;CAC9C,IAAI,EAAI,SAAS,8BAA8B,GAC7C,IAAI;EACF,IAAM,IAAS,IAAI,IAAI,CAAG;EAC1B,OAAO,eAAe,EAAO,WAAW,EAAO;CACjD,QAAY;EACV,OAAO,EAAI,QAAQ,wCAAwC,cAAc;CAC3E;CAEF,OAAO;AACT,GAEM,MAAY,MAAwB;CACxC,IAAM,IAAW,EAAI,QAAQ,KAAK,EAAE;CAYpC,OAXI,EAAS,WAAW,IAIf,GAHG,SAAS,EAAS,UAAU,GAAG,CAAC,GAAG,EAGnC,EAAE,IAFF,SAAS,EAAS,UAAU,GAAG,CAAC,GAAG,EAE7B,EAAE,IADR,SAAS,EAAS,UAAU,GAAG,CAAC,GAAG,EACvB,MACb,EAAS,WAAW,IAItB,GAHG,SAAS,EAAS,KAAK,EAAS,IAAI,EAGpC,EAAE,IAFF,SAAS,EAAS,KAAK,EAAS,IAAI,EAE9B,EAAE,IADR,SAAS,EAAS,KAAK,EAAS,IAAI,EACxB,MAEjB;AACT,GAEM,IAA0C;CAC9C,IAAI;CACJ,KAAK;CACL,IAAI;CACJ,KAAK;CACL,KAAK;CACL,IAAI;CACJ,KAAK;CACL,IAAI;CACJ,KAAK;CACL,KAAK;CACL,IAAI;CACJ,KAAK;CACL,IAAI;CACJ,KAAK;CACL,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AACN,GAEM,KAAsB,MAAiC;CAC3D,IAAI,CAAC,GAAO,OAAO;CACnB,IAAM,IAAa,EAAM,KAAK,EAAE,YAAY,GACtC,IAAc,EAAW,MAAM,wEAAwE;CAC7G,IAAI,KAAe,EAAgB,EAAY,KAC7C,OAAO,EAAgB,EAAY;CAErC,KAAK,IAAM,CAAC,GAAK,MAAS,OAAO,QAAQ,CAAe,GACtD,IAAI,EAAW,SAAS,CAAG,GACzB,OAAO;CAeX,OAZI,0BAA0B,KAAK,CAAU,IAAU,YACnD,mBAAmB,KAAK,CAAU,IAAU,WAC5C,mBAAmB,KAAK,CAAU,IAAU,YAC5C,kBAAkB,KAAK,CAAU,IAAU,WAC3C,oBAAoB,KAAK,CAAU,IAAU,YAC7C,wBAAwB,KAAK,CAAU,IAAU,eACjD,oBAAoB,KAAK,CAAU,IAAU,aAC7C,cAAc,KAAK,CAAU,IAAU,YACvC,cAAc,KAAK,CAAU,IAAU,WACvC,mBAAmB,KAAK,CAAU,IAAU,YAC5C,kBAAkB,KAAK,CAAU,IAAU,WAC3C,gBAAgB,KAAK,CAAU,IAAU,UACtC;AACT,GAEM,MAAoB,MAAmD;CAC3E,IAAI,EAAM,MAAM;EACd,IAAM,IAAa,EAAmB,EAAM,IAAI;EAChD,IAAI,GAAY,OAAO;CACzB;CAEA,IAAI,EAAM,OAAO;EACf,IAAM,IAAQ,EAAM,MAAM,KAAK,GACzB,IAAQ,EAAM,MAAM,kBAAkB,EAAE,OAAO,OAAO;EAC5D,KAAK,IAAI,IAAI,EAAM,SAAS,GAAG,KAAK,GAAG,KAAQ;GAC7C,IAAM,IAAY,EAAmB,EAAM,EAAE;GAC7C,IAAI,GAAW,OAAO;EACxB;EACA,IAAM,IAAU,EAAM,QAAQ,eAAe,EAAE,EAAE,KAAK;EACtD,OAAO,EAAmB,CAAO,KAAK;CACxC;CAEA,OAAO;AACT,GAEM,KAAwB,MAA6C;CACzE,IAAM,oBAAO,IAAI,IAAY;CAC7B,OAAO,EAAO,QAAQ,MAAU;EAG9B,IAAM,IAAM,GAFE,GAAiB,CAAK,EAAE,YAEvB,EAAM,IADP,EAAM,QAAQ,IAAI,KAAK,EAAE,YACf;EAKxB,OAJI,EAAK,IAAI,CAAG,IACP,MAET,EAAK,IAAI,CAAG,GACL;CACT,CAAC;AACH,GA8Ba,KAAiD,EAC5D,QACA,cAAW,CAAC,GACZ,eACA,eAAY,CAAC,GACb,gBAAY,UACZ,gBAAa,WACb,cACA,oBACA,sBACA,uBACA,qBAAiB,IACjB,uBAAmB,IACnB,sBAAmB,IACnB,sBAAkB,IAClB,oBAAgB,IAChB,0BAAsB,IACtB,eAAY,IACZ,sBAAmB,IACnB,mBAAgB,CAAC,QACb;CACJ,IAAM,IAAe,EAAuB,IAAI,GAC1C,KAAsB,EAAyC,IAAI,GACnE,IAAa,EAAuB,IAAI,GACxC,KAAY,EAA0B,IAAI,GAC1C,IAAiB,EAAgC,IAAI,GACrD,KAAe,EAAmB,IAAI,GACtC,IAAiB,EAAsB,IAAI,GAC3C,KAAc,EAAO,EAAK,GAC1B,IAAY,EAAO,EAAK,GACxB,IAAiB,EAAsB,IAAI,GAC3C,IAAmB,EAAsB,IAAI,GAC7C,KAAgB,EAAuB,IAAI,GAE3C,CAAC,GAAQ,MAAa,EAAgC,IAAI,GAC1D,CAAC,GAAW,MAAgB,EAAS,EAAK,GAC1C,CAAC,GAAa,MAAkB,EAAS,CAAC,GAC1C,CAAC,GAAU,MAAe,EAAS,CAAC,GACpC,CAAC,GAAQ,MAAa,EAAS,CAAC,GAChC,CAAC,GAAS,KAAc,EAAS,EAAK,GACtC,CAAC,IAAa,MAAkB,EAAS,EAAK,GAC9C,CAAC,IAAc,MAAmB,EAAS,EAAK,GAChD,CAAC,IAAO,MAAY,EAAS,EAAK,GAClC,CAAC,IAAS,MAAc,EAAS,EAAK,GAGtC,CAAC,IAAW,MAAgB,EAAyB,CAAC,CAAC,GACvD,CAAC,IAAe,MAAoB,EAAuB;EAC/D,IAAI;EACJ,QAAQ;EACR,OAAO;EACP,SAAS;EACT,OAAO;CACT,CAAC,GACK,CAAC,IAAc,MAAmB,EAAS,EAAE,GAC7C,CAAC,GAAqB,KAA0B,EAAS,KAAK,GAC9D,CAAC,IAAoB,MAAyB,EAA0B,EAAqB,CAAS,CAAC,GACvG,CAAC,IAAa,MAAkB,EAA6D,CAAC,CAAC,GAC/F,CAAC,IAAkB,MAAuB,EAAiB,CAAC;CAGlE,QAAgB;EACd,IAAI,GAAQ;GACV,IAAM,IAAe,EAAO,UAAU,UAAU;GAEhD,GAAsB,EAAqB,CAD3B,GAAG,GAAW,GAAG,EAAa,QAAO,MAAK,CAAC,EAAU,MAAK,MAAK,EAAE,OAAO,EAAE,EAAE,CAAC,CAClD,CAAM,CAAC;EACpD,OACE,GAAsB,EAAqB,CAAS,CAAC;CAEzD,GAAG,CAAC,GAAW,CAAM,CAAC;CAEtB,IAAM,CAAC,IAAe,MAAoB,EAAuD;EAC/F,SAAS;EACT,OAAO;CACT,CAAC,GAEK,CAAC,GAAiB,MAAsB,EAAsB,KAAc,KAAY,CAAC,CAAC,GAG1F,CAAC,GAAY,KAAiB,EAA4E,MAAM,GAChH,CAAC,IAAc,MAAmB,EAAS,CAAC,GAC5C,CAAC,GAAqB,MAA0B,EAAS,EAAK,GAC9D,CAAC,IAAmB,MAAwB,EAAiC,CAAC,CAAC,GAE/E,KAAgB,KAAK,UAAU,EAAgB,KAAI,OAAM;EAAE,OAAO,EAAE;EAAO,WAAW,EAAE;CAAU,EAAE,CAAC;CAQ3G,AALA,QAAgB;EACd,GAAmB,KAAc,KAAY,CAAC,CAAC;CACjD,GAAG,CAAC,GAAY,CAAQ,CAAC,GAGzB,QAAgB;EACd,IAAI,CAAC,KAAO,CAAC,KAAmB,EAAgB,WAAW,GAAG;EAE9D,GAAqB,CAAC,CAAC;EAEvB,IAAM,IAAY,SAAS,cAAc,OAAO;EAMhD,AALA,EAAU,QAAQ,IAClB,EAAU,UAAU,YACpB,EAAU,cAAc,aAExB,EAAU,MAAM,UAAU,sGAC1B,SAAS,KAAK,YAAY,CAAS;EAEnC,IAAI,IAAsB,MACpB,IAAQ,EAAI,SAAS,OAAO,GAE5B,IAAa,SAAS,cAAc,QAAQ;EAElD,AADA,EAAW,QAAQ,KACnB,EAAW,SAAS;EACpB,IAAM,IAAM,EAAW,WAAW,IAAI,GAElC,IAAe,GACf,IAAY,IAEV,UAAoB;GACxB,IAAI,KAAgB,EAAgB,QAAQ;IAC1C,EAAQ;IACR;GACF;GACA,IAAM,IAAa,EAAgB,GAAc;GAEjD,EAAU,cAAc,MAAe,IAAI,MAAO;EACpD,GAEM,UAAiB;GACrB,IAAI,KAAO,EAAU,cAAc,GAAG;IACpC,EAAI,UAAU,GAAW,GAAG,GAAG,EAAW,OAAO,EAAW,MAAM;IAClE,IAAM,IAAU,EAAW,UAAU,cAAc,EAAG;IAGtD,AAFA,IAAsB,OAAU;KAAE,GAAG;MAAO,IAAe;IAAQ,EAAE,GACrE,KACA,EAAY;GACd,OAEE,WAAW,GAAU,GAAG;EAE5B,GAEM,UAAqB;GACrB,MACJ,IAAY,IACZ,EAAY;EACd;EA2CA,AAzCA,EAAU,iBAAiB,UAAU,CAAQ,GAC7C,EAAU,iBAAiB,WAAW,CAAY,GAE9C,KAAS,EAAI,YAAY,KAC3B,IAAU,IAAI,EAAI;GAChB,cAAc;GACd,iBAAiB;GACjB,WAAW,GAAK,MAAQ;IACtB,IAAI,IAAQ,GAAW;IACvB,IAAI,CAAC,GACH,IAAI;KACF,IAAM,IAAe,IAAI,IAAI,CAAG,GAC1B,IAAc,EAAa,aAAa,IAAI,OAAO,KACrC,EAAa,aAAa,IAAI,KAAK,KACnC,EAAa,aAAa,IAAI,MAAM,KACpC,EAAa,aAAa,IAAI,WAAW;KAC7D,AAAI,MACF,IAAQ;IAEZ,QAAY,CAAC;IAGf,IAAI;KACF,IAAM,IAAa,EAAe,CAAG,GAC/B,IAAS,IAAI,IAAI,GAAY,OAAO,SAAS,MAAM;KAIzD,AAHI,KACF,EAAO,aAAa,IAAI,SAAS,CAAK,GAExC,EAAI,KAAK,OAAO,EAAO,SAAS,GAAG,EAAI;IACzC,SAAS,GAAG;KACV,QAAQ,MAAM,4CAA4C,CAAC;IAC7D;GACF;EACF,CAAC,GACD,EAAQ,WAAW,EAAe,CAAG,CAAC,GACtC,EAAQ,YAAY,CAAS,KAE7B,EAAU,MAAM,GAId,EAAU,cAAc,KAC1B,EAAa;EAGf,IAAM,UAAgB;GAMpB,AALA,EAAU,oBAAoB,UAAU,CAAQ,GAChD,EAAU,oBAAoB,WAAW,CAAY,GACjD,KACF,EAAQ,QAAQ,GAEd,SAAS,KAAK,SAAS,CAAS,KAClC,SAAS,KAAK,YAAY,CAAS;EAEvC;EAEA,OAAO;CACT,GAAG,CAAC,GAAK,EAAa,CAAC;CAGvB,IAAM,CAAC,GAAY,MAAiB,EAAwB,IAAI,GAC1D,CAAC,IAAoB,MAAyB,EAAS,EAAK,GAC5D,CAAC,IAAoB,MAAyB,EAAS,EAAK,GAC5D,CAAC,IAAmB,MAAwB,EAAwB,IAAI,GAGxE,CAAC,IAAQ,MAAa,EAAwB,IAAI,GAClD,CAAC,IAAW,MAAgB,EAAiB,CAAC,GAC9C,CAAC,IAAgB,MAAqB,EAAwB,IAAI,GAClE,CAAC,IAAc,MAAmB,EAA2B,IAAI,GACjE,CAAC,GAAe,MAAoB,EAAwB,IAAI;CActE,AAXA,QAAgB;EACd,IAAI,GAAc,WAAW,CAAC,GAAc,QAAQ,cAAc,sBAAsB,GAAG;GACzF,IAAM,IAAW,SAAS,cAAc,sBAAsB;GAK9D,AAJA,EAAS,MAAM,SAAS,WACxB,EAAS,MAAM,QAAQ,QACvB,EAAS,MAAM,SAAS,QACxB,EAAS,MAAM,UAAU,SACzB,GAAc,QAAQ,YAAY,CAAQ;EAC5C;CACF,GAAG,CAAC,CAAC,GAEL,QAAgB;EACd,IAAI,CAAC,GAAQ;GAGX,AAFA,GAAsB,EAAK,GAC3B,GAAsB,EAAK,GAC3B,GAAqB,IAAI;GACzB;EACF;EAEA,IAAM,KAAsB,MAAuB,GAAsB,CAAS,GAC5E,UAAyB;GAE7B,IAAM,IADQ,EAAO,gBACJ,GACX,IAAY,EAAQ,GAAU;GAEpC,AADA,GAAsB,CAAS,GAC/B,GACE,IACI,EAAS,mCAAmC,mBAC5C,IACN;EACF;EAGA,AADA,EAAmB,EAAO,QAAQ,mBAAmB,CAAC,GACtD,EAAiB;EAEjB,IAAM,IAAQ,EAAO,gBAAgB,GAC/B,KAAuB,MAAe;GAC1C,EAAmB,EAAM,iBAAiB,WAAW;EACvD,GACM,UAA0B;GAC9B,EAAiB;EACnB;EAOA,OALI,oCAAoC,MACtC,EAAM,iBAAiB,2CAA2C,CAAoC,GACtG,EAAM,iBAAiB,gDAAgD,CAAkC,UAG9F;GACX,AAAI,oCAAoC,MACtC,EAAM,oBAAoB,2CAA2C,CAAoC,GACzG,EAAM,oBAAoB,gDAAgD,CAAkC;EAEhH;CACF,GAAG,CAAC,CAAM,CAAC;CAGX,IAAM,CAAC,IAAiB,MAAsB,EAAS,EAAI,GACrD,IAAqB,EAAsB,IAAI,GAE/C,KAAe,EAAO,EAAK,GAC3B,KAAgB,EAA0B,MAAM,GAChD,KAAwB,EAAwB,CAAS;CAsV/D,AApVA,QAAgB;EACd,IAAI,CAAC,EAAa,SAAS;EAG3B,IAAM,IAAc,SAAS,cAAc,OAAO;EAMlD,AALA,EAAY,QAAQ,IACpB,EAAY,UAAU,YACtB,EAAY,cAAc,aAC1B,EAAY,MAAM,UAAU,uDAC5B,SAAS,KAAK,YAAY,CAAW,GACrC,EAAe,UAAU;EAEzB,IAAM,IAAQ,EAAI,SAAS,OAAO;EAClC,IAAI,KAAS,EAAI,YAAY,GAAG;GAC9B,IAAM,IAAM,IAAI,EAAI;IAClB,cAAc;IACd,iBAAiB;IACjB,WAAW,GAAK,MAAQ;KACtB,IAAI,IAAQ,GAAW;KACvB,IAAI,CAAC,GACH,IAAI;MACF,IAAM,IAAe,IAAI,IAAI,CAAG,GAC1B,IAAc,EAAa,aAAa,IAAI,OAAO,KACrC,EAAa,aAAa,IAAI,KAAK,KACnC,EAAa,aAAa,IAAI,MAAM,KACpC,EAAa,aAAa,IAAI,WAAW;MAC7D,AAAI,MACF,IAAQ;KAEZ,QAAY,CAAC;KAGf,IAAI;MACF,IAAM,IAAa,EAAe,CAAG,GAC/B,IAAS,IAAI,IAAI,GAAY,OAAO,SAAS,MAAM;MAIzD,AAHI,KACF,EAAO,aAAa,IAAI,SAAS,CAAK,GAExC,EAAI,KAAK,OAAO,EAAO,SAAS,GAAG,EAAI;KACzC,SAAS,GAAG;MACV,QAAQ,MAAM,8CAA8C,CAAC;KAC/D;IACF;GACF,CAAC;GAGD,AAFA,EAAI,WAAW,EAAe,CAAG,CAAC,GAClC,EAAI,YAAY,CAAW,GAC3B,GAAa,UAAU;EACzB,OAAO,AAAI,KAAS,EAAY,YAAY,+BAA+B,GACzE,EAAY,MAAM;EAepB,EAAY,iBAAiB,gBAVN;GACrB,IAAM,IAAS,GAAU;GACzB,IAAI,CAAC,GAAQ;GACb,IAAM,IAAM,EAAO,WAAW,IAAI;GAClC,AAAI,KAAO,EAAY,cAAc,MACnC,EAAI,UAAU,GAAa,GAAG,GAAG,EAAO,OAAO,EAAO,MAAM,GAE5D,GADgB,EAAO,UAAU,cAAc,GAC7B,CAAO;EAE7B,CAC+C;EAG/C,IAAI;EACJ,IAAI,IAAgB;GAClB,IAAM,IAAW,IAAI,GAA0B,EAAc;GAE7D,AADA,GAAoB,UAAU,GAC9B,IAAmB,EAAS,eAAe;EAC7C;EAGA,IAAM,IAAc,IAAI,GAAe;GACrC,WAAW,EAAa;GACxB;GACA,UAAU;GACV,OAAO;GACP,YAAY;GACZ;GACA,KAAK;IACH,eAAe;IACf,gBAAgB,CAAC,aAAa,WAAW;IACzC,GAAG;GACL;GACA,oBAAoB,MAAU;IAE5B,AADA,IAAmB,CAAK,GACpB,MAAkB,GAAiB,CAAK;GAC9C;GACA;EACF,CAAC;EA4GD,AA1GA,EAAY,GAAG,qBAAqB,MAAkC;GAEpE,AADA,GAAmB,CAAgB,GAC/B,KACF,EAAmB,CAAgB;EAEvC,CAAC,GAED,EAAY,GAAG,mBAAmB,MAA8B;GAE9D,AADA,GAAmB,CAAc,GAC7B,KACF,EAAmB,CAAc;EAErC,CAAC,GAGD,EAAY,GAAG,cAAc;GAE3B,AADA,GAAa,EAAI,GACjB,GAAW,EAAK;EAClB,CAAC,GACD,EAAY,GAAG,eAAe,GAAa,EAAK,CAAC,GACjD,EAAY,GAAG,eAAe;GAE5B,AADA,GAAa,EAAK,GAClB,GAAW,EAAI;EACjB,CAAC,GACD,EAAY,GAAG,eAAe,MAAiB;GAG7C,IAAI,EAAU,WAAW,EAAe,YAAY,MAClD,IAAI,KAAK,IAAI,IAAO,EAAe,OAAO,IAAI,GAI5C,AAFA,EAAU,UAAU,IACpB,EAAe,UAAU,MACzB,AAEE,EAAiB,aADjB,OAAO,aAAa,EAAiB,OAAO,GACjB;QAI7B;GAKJ,AADA,GAAe,CAAI,GACnB,GAAY,EAAY,YAAY,CAAC;GAGrC,IAAM,IAAW,EAAY,WAAW,iBAAiB,CAAI;GAC7D,GAAiB;IAAE,SAAS,EAAS;IAAW,OAAO,EAAS;GAAM,CAAC;EACzE,CAAC,GAED,EAAY,GAAG,kBAAkB,MAA2B;GAC1D,GAAa,CAAM;EACrB,CAAC,GAED,EAAY,GAAG,kBAAkB,MAA0B;GACzD,GAAiB,CAAO;EAC1B,CAAC,GAED,EAAY,GAAG,cAAc,MAAuB;GAClD,GAAe,CAAS;EAC1B,CAAC,GAED,EAAY,GAAG,iBAAiB,MAA6C;GAE3E,AADA,GAAU,EAAK,MAAM,GACrB,EAAW,EAAK,KAAK;EACvB,CAAC,GAED,EAAY,GAAG,cAAc,MAAoB;GAC/C,GAAS,CAAM;EACjB,CAAC,GAGD,EAAY,UAAU,kBAAkB,MAAiB;GACvD,GAAgB,CAAI;EACtB,CAAC,GAED,EAAY,GAAG,oBAAoB,MAA4B;GAC7D,GAAsB,EAAqB,CAAC,GAAG,CAAM,CAAC,CAAC;GAGvD,IAAM,IAAe,EAAO,MAAM,MAAM,EAAE,SAAS;GACnD,AAAI,MACF,EAAY,UAAU,eAAe,EAAa,EAAE,GACpD,EAAuB,EAAa,EAAE;EAE1C,CAAC,GAGD,EAAY,GAAG,sBAAsB,MAA+D;GAElG,AADA,GAAe,CAAM,GACrB,GAAoB,EAAO,IAAI,MAAM,CAAC;EACxC,CAAC,GAED,EAAY,GAAG,qBAAqB,MAAe;GACjD,GAAoB,CAAE;EACxB,CAAC,GAGD,EAAY,QAAQ,mBAAmB,GAAoB,MAAuB;GAChF,GAAc,IAAY,IAAa,IAAI;EAC7C,CAAC,GAGD,GAAU,EAAY,UAAU,CAAC,GACjC,EAAW,EAAY,QAAQ,CAAC,GAEhC,GAAU,CAAW;EAGrB,IAAM,UAA+B;GACnC,IAAM,IAAO,CAAC,CAAC,SAAS;GACxB,GAAgB,CAAI;GACpB,IAAM,IAAU,EAAY,gBAAgB;GAC5C,EAAY,UAAU,MACpB,IAAO,qBAAqB,mBAC5B,CACF;EACF;EACA,SAAS,iBAAiB,oBAAoB,CAAsB;EAGpE,IAAM,KAAiB,MAAqB;GAC1C,IAAM,IAAW,SAAS;GACtB,YAAa,EAAS,YAAY,WAAW,EAAS,YAAY,cAAc,EAAS,aAAa,iBAAiB,MAAM,UAIjI;YAAQ,EAAE,IAAI,YAAY,GAA1B;KACE,KAAK;KACL,KAAK;MAEH,AADA,EAAE,eAAe,GACb,EAAY,gBAAgB,EAAE,SAChC,EAAY,KAAK,IAEjB,EAAY,MAAM;MAEpB;KACF,KAAK;KACL,KAAK;MAEH,AADA,EAAE,eAAe,GACjB,EAAY,KAAK,EAAY,eAAe,IAAI,EAAE;MAClD;KACF,KAAK;KACL,KAAK;MAEH,AADA,EAAE,eAAe,GACjB,EAAY,KAAK,EAAY,eAAe,IAAI,EAAE;MAClD;KACF,KAAK;MAEH,AADA,EAAE,eAAe,GACjB,EAAY,UAAU,EAAY,UAAU,IAAI,EAAG;MACnD;KACF,KAAK;MAEH,AADA,EAAE,eAAe,GACjB,EAAY,UAAU,EAAY,UAAU,IAAI,EAAG;MACnD;KACF,KAAK;MAEH,AADA,EAAE,eAAe,GACjB,EAAY,QAAQ,CAAC,EAAY,QAAQ,CAAC;MAC1C;KACF,KAAK;MAEH,AADA,EAAE,eAAe,GACjB,EAAY,iBAAiB;MAC7B;KACF,KAAK;MAEH,AADA,EAAE,eAAe,GACjB,EAAY,uBAAuB;MACnC;KACF,KAAK;MAGH,IAFA,EAAE,eAAe,GACK,EAAY,UAAU,iBACxC,MAAkB,OAEpB,AADA,EAAY,UAAU,eAAe,KAAK,GAC1C,EAAuB,KAAK;WACvB;OACL,IAAM,IAAS,GAAsB;OACrC,AAAI,KAAU,EAAO,SAAS,MAC5B,EAAY,UAAU,eAAe,EAAO,GAAG,EAAE,GACjD,EAAuB,EAAO,GAAG,EAAE;MAEvC;MACA;KACF,SACE;IACJ;IACA,EAAmB;GADnB;EAEF;EAOA,OALA,SAAS,iBAAiB,WAAW,CAAa,GAGlD,EAAmB,SAEN;GAaX,AAZA,EAAY,QAAQ,GACpB,GAAoB,SAAS,QAAQ,GACrC,GAAoB,UAAU,MAC9B,SAAS,oBAAoB,oBAAoB,CAAsB,GACvE,SAAS,oBAAoB,WAAW,CAAa,GACjD,EAAmB,WACrB,OAAO,aAAa,EAAmB,OAAO,GAEhD,AAEE,GAAa,aADb,GAAa,QAAQ,QAAQ,GACN,OAEzB,AAEE,EAAe,aADf,SAAS,KAAK,YAAY,EAAe,OAAO,GACvB;EAE7B;CACF,GAAG,CAAC,GAAK,EAAS,CAAC,GAGnB,QAAgB;EACd,IAAI,GAAQ;GACV,EAAO,WAAW,cAAc,CAAe;GAE/C,IAAM,IAAQ,EAAa,SAAS,cAAc,OAAO;GACzD,IAAI,GAAO;IACT,IAAM,IAAW,EAAO,WAAW,iBAAiB,EAAM,WAAW;IACrE,GAAiB;KAAE,SAAS,EAAS;KAAW,OAAO,EAAS;IAAM,CAAC;GACzE;EACF;CACF,GAAG,CAAC,IAAe,CAAM,CAAC,GAG1B,QAAgB;EACd,IAAM,KAAuB,MAAkB;GACzC,MAAe,WAEJ,EAAE,OACN,QAAQ,qBAAqB,KAIxC,EAAc,MAAM;EACtB;EAGA,OADA,SAAS,iBAAiB,SAAS,CAAmB,SACzC;GACX,SAAS,oBAAoB,SAAS,CAAmB;EAC3D;CACF,GAAG,CAAC,CAAU,CAAC,GAGf,GAAa,UAAU,GACvB,GAAc,UAAU,GACxB,GAAsB,UAAU;CAEhC,IAAM,UAA2B;EAK/B,AAJA,GAAmB,EAAI,GACnB,EAAmB,WACrB,OAAO,aAAa,EAAmB,OAAO,GAEhD,EAAmB,UAAU,OAAO,iBAAiB;GAEnD,AAAI,GAAa,WAAW,GAAc,YAAY,UACpD,GAAmB,EAAK;EAE5B,GAAG,GAAK;CACV,GAEM,WAAwB;EAC5B,EAAmB;CACrB,GAEM,WAAmB;EAClB,MACD,MACF,EAAY,CAAC,GACb,EAAO,KAAK,GACZ,GAAW,EAAK,KACP,IACT,EAAO,MAAM,IAEb,EAAO,KAAK,GAEd,EAAmB;CACrB,GAEM,MAAiB,MAAqD;EAC1E,IAAI,CAAC,EAAW,WAAW,CAAC,KAAU,MAAa,GAAG,OAAO;EAC7D,IAAM,IAAO,EAAW,QAAQ,sBAAsB;EAEtD,OADgB,KAAK,IAAI,GAAG,KAAK,IAAI,IAAI,EAAE,UAAU,EAAK,QAAQ,EAAK,KAAK,CACrE,IAAU;CACnB,GAEM,MAAuB,MAAwC;EACnE,IAAI,CAAC,GAAQ;EAGb,AAFA,GAAY,UAAU,IAEtB,GADoB,GAAc,CACjB,CAAW;EAE5B,IAAM,KAAe,MAAmB;GACtC,IAAI,CAAC,GAAY,WAAW,CAAC,EAAW,SAAS;GACjD,IAAM,IAAO,EAAW,QAAQ,sBAAsB,GAEhD,IADU,KAAK,IAAI,GAAG,KAAK,IAAI,IAAI,EAAG,UAAU,EAAK,QAAQ,EAAK,KAAK,CAC1D,IAAU;GAS7B,AAPA,GAAiB,CAAU,GAC3B,GAAU,EAAG,UAAU,EAAK,IAAI,GAChC,GAAa,CAAU,GAEnB,EAAe,YACjB,EAAe,QAAQ,cAAc,IAEvC,EAAmB;EACrB,GAEM,KAAa,MAAmB;GAEpC,IADA,GAAY,UAAU,IAClB,EAAW,SAAS;IACtB,IAAM,IAAO,EAAW,QAAQ,sBAAsB;IAGtD,EAFgB,KAAK,IAAI,GAAG,KAAK,IAAI,IAAI,EAAG,UAAU,EAAK,QAAQ,EAAK,KAAK,CAC1D,IAAU,CACP;GACxB;GAGA,AAFA,GAAiB,IAAI,GACrB,SAAS,oBAAoB,aAAa,CAAW,GACrD,SAAS,oBAAoB,WAAW,CAAS;EACnD;EAGA,AADA,SAAS,iBAAiB,aAAa,CAAW,GAClD,SAAS,iBAAiB,WAAW,CAAS;CAChD,GAEM,KAAe,MAAiB;EACpC,IAAI,CAAC,GAAQ;EAEb,IAAM,IAAc,KAAK,IAAI,GAAG,IAAW,IAAI,KAAK,IAAI,GAAM,CAAQ,IAAI,CAAI;EAS9E,AARA,EAAU,UAAU,IACpB,EAAe,UAAU,GACzB,GAAe,CAAW,GAC1B,EAAO,KAAK,CAAW,GAInB,EAAiB,WAAS,OAAO,aAAa,EAAiB,OAAO,GAC1E,EAAiB,UAAU,OAAO,iBAAiB;GAGjD,AAFA,EAAU,UAAU,IACpB,EAAe,UAAU,MACzB,EAAiB,UAAU;EAC7B,GAAG,GAAI;CACT,GAEM,MAAsB,MAA2C;EACrE,IAAI,CAAC,GAAQ;EACb,IAAM,IAAQ,WAAW,EAAE,OAAO,KAAK;EAGvC,AAFA,EAAO,UAAU,CAAK,GACtB,GAAU,CAAK,GACf,EAAW,MAAU,CAAC;CACxB,GAEM,WAAmB;EACvB,IAAI,CAAC,GAAQ;EACb,IAAM,IAAW,CAAC;EAElB,AADA,EAAO,QAAQ,CAAQ,GACvB,EAAW,CAAQ;CACrB,GAEM,MAAqB,MAAiB;EACrC,MACL,EAAO,gBAAgB,CAAI,GAC3B,GAAgB,CAAI,GACpB,EAAc,MAAM;CACtB,GAEM,MAAuB,MAAsB;EACjD,IAAI,CAAC,GAAQ;EACb,EAAO,WAAW,CAAS;EAE3B,IAAM,IAAW,GAAU,MAAM,MAAM,EAAE,OAAO,CAAS;EAIzD,AAHI,KACF,GAAiB,CAAQ,GAE3B,EAAc,MAAM;CACtB,GAEM,MAAwB,MAAoB;EAC3C,MACL,EAAO,UAAU,eAAe,CAAO,GACvC,EAAuB,CAAO,GAC9B,EAAc,MAAM;CACtB,GAEM,MAAsB,MAAsB;EAEhD,AADA,EAAY,CAAS,GACrB,EAAc,MAAM;CACtB,GAEM,WAAkB;EACjB,KACL,EAAO,uBAAuB;CAChC,GAEM,WAAyB;EACxB,KACL,EAAO,iBAAiB;CAC1B,GAEM,WAAyB;EACxB,MACD,IACF,EAAO,QAAQ,YAAY,IAE3B,EAAO,QAAQ,eAAe,GAAK,CAAW;CAElD,GAEM,WAA4B;EAChC,IAAI,CAAC,GAAQ;EACb,IAAM,IAAQ,EAAO,gBAAgB;EAErC,IAAI,IAAoB;GACtB,EAAO,QAAQ,YAAY,CAAK;GAChC;EACF;EAEA,AAAI,OAAQ,EAAc,kCAAmC,cAElD,EAAO,QAAQ,mBAAmB,IAD3C,EAAO,QAAQ,sBAAsB,CAAK,IAI1C,QAAQ,KAAK,8DAA8D;CAE/E,GAEM,MAAsB,MAAwC;EAClE,IAAI,CAAC,EAAW,WAAW,MAAa,KAAK,CAAC,GAAQ;EACtD,IAAM,IAAO,EAAW,QAAQ,sBAAsB,GAEhD,IADU,KAAK,IAAI,GAAG,KAAK,IAAI,IAAI,EAAE,UAAU,EAAK,QAAQ,EAAK,KAAK,CACzD,IAAU;EAa7B,AAXA,GAAU,EAAE,UAAU,EAAK,IAAI,GAC/B,GAAa,CAAU,GAEnB,EAAe,WAAS,OAAO,aAAa,EAAe,OAAO,GACtE,EAAe,UAAU,OAAO,iBAAiB;GAC/C,AAAI,EAAe,YACjB,EAAe,QAAQ,cAAc;EAEzC,GAAG,GAAG,GAGN,GADgB,EAAO,WAAW,iBAAiB,CACnC,EAAQ,SAAS;CACnC,GAEM,WAA2B;EAI/B,AAHA,GAAU,IAAI,GACd,GAAkB,IAAI,GACtB,GAAgB,IAAI,GAChB,EAAe,WACjB,OAAO,aAAa,EAAe,OAAO;CAE9C,GAEM,KAAc,MAAoB;EACtC,IAAI,MAAM,CAAO,KAAK,IAAU,GAAG,OAAO;EAC1C,IAAM,IAAI,KAAK,MAAM,CAAO,GACtB,IAAM,KAAK,MAAM,IAAI,IAAI,GACzB,IAAO,KAAK,MAAO,IAAI,OAAQ,EAAE,GACjC,IAAO,IAAI,IAEX,KAAO,MAAgB,OAAO,CAAG,EAAE,SAAS,GAAG,GAAG;EAKxD,OAHI,IAAM,IACD,GAAG,EAAI,CAAG,EAAE,GAAG,EAAI,CAAI,EAAE,GAAG,EAAI,CAAI,MAEtC,GAAG,EAAI,CAAI,EAAE,GAAG,EAAI,CAAI;CACjC,GAEM,WACgC,EAAhC,KAAW,MAAW,IAAW,KACjC,IAAS,KAAa,KACtB,IAAS,KAAa,KAClB,IAH4B,EAAS,MAAM,GAAK,CAAA,GAMpD,KAAc,MAA4B;EAC9C,EAAc,MAAe,IAAO,SAAS,CAAI;CACnD;CAEA,OACE,kBAAC,OAAD;EACE,KAAK;EACL,WAAW,wBAAwB,KAAkB,oBAAoB;EACzE,aAAa;EACb,oBAAoB,CAGpB;EACA,OAAO;GACL,QAAQ;GAEN,qBAAqB;GACrB,yBAAyB,GAAS,CAAU;EAEhD;YAdF;GAgBE,kBAAC,UAAD;IACE,KAAK;IACL,OAAO;IACP,QAAQ;IACR,OAAO;KAAE,SAAS;KAAQ,UAAU;KAAY,eAAe;IAAO;GACvE,CAAA;GAED,kBAAC,OAAD;IAAK,WAAW,uBAAuB,KAAc,WAAW;cAC9D,kBAAC,OAAD,EAAK,WAAU,cAAmB,CAAA;GAC/B,CAAA;GAEJ,MAAgB,MAAwB,SACvC,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,QAAD;KACE,WAAU;KACV,OAAO;MACL,UAAU,EAAc,SAAS,UAAU,SAAS,EAAc,SAAS,UAAU,SAAS;MAC9F,OAAO,EAAc,SAAS;MAC9B,YAAY,EAAc,eAAe,SACrC,gBACA,EAAc,eAAe,UAC3B,wBACA;MACN,QAAQ,EAAc,eAAe,SAAS,SAAS;MACvD,gBAAgB,EAAc,eAAe,SAAS,SAAS;KACjE;KACA,yBAAyB,EAAE,QAAQ,GAAa;IACjD,CAAA;GACE,CAAA;IAGL,KAAc,OACd,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD;MAAM,MAAM;MAAI,WAAU;KAAsB,CAAA;KAChD,kBAAC,OAAD;MAAK,WAAU;gBACZ,IAAa,cAAc,MAAe,cAAc,MAAqB;KAC3E,CAAA;KACL,kBAAC,OAAD;MAAK,WAAU;gBACZ,IACG,qEACA,sDAAsD,KAAoB,OAAO,OAAsB,GAAG;KAC3G,CAAA;KACL,kBAAC,UAAD;MACE,WAAU;MACV,eAAe;OACb,AAAI,IACF,GAAQ,QAAQ,YAAY,IACnB,MACT,GAAQ,QAAQ,YAAY,EAAO,gBAAgB,CAAC;MAExD;gBACD;KAEO,CAAA;IACL;;GAGN,CAAC,KAAc,CAAC,KACf,kBAAC,OAAD;IACE,WAAU;IACV,SAAS;cAET,kBAAC,OAAD;KAAK,WAAU;eACZ,KACC,kBAAC,IAAD;MAAW,MAAM;MAAI,WAAU;KAAmB,CAAA,IAElD,kBAAC,IAAD;MAAM,MAAM;MAAI,WAAU;MAAkB,OAAO,EAAE,YAAY,MAAM;MAAG,MAAK;KAAgB,CAAA;IAE9F,CAAA;GACF,CAAA;GAGN,CAAC,KACA,kBAAC,OAAD;IACE,WAAU;IACV,UAAU,MAAM;KACd,AAAI,EAAE,WAAW,EAAE,iBACjB,GAAW;IAEf;cAEC,MACC,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MACE,KAAK;MACL,WAAU;MACV,aAAa;MACb,aAAa;MACb,cAAc;gBALhB;OAOE,kBAAC,OAAD;QACE,WAAU;QACV,OAAO,EACL,OAAO,GAAG,KAAY,KAAU,EAAO,kBAAkB,EAAE,IAAI,OAAY,KAAK,IAAW,MAAM,EAAE,GACrG;OACD,CAAA;OACD,kBAAC,OAAD;QACE,WAAU;QACV,OAAO;SACL,OAAO,GAAG,KAAa,MAAkB,OAAuB,IAAhB,KAA+B,IAAY,MAAM,EAAE;SACnG,YAAY;QACd;kBAEA,kBAAC,OAAD,EAAK,WAAU,qBAAsB,CAAA;OAClC,CAAA;OAEJ,KAAoB,EAAgB,KAAK,GAAI,MACxC,MAAa,IAAU,OAGzB,kBAAC,OAAD;QAEE,WAAU;QACV,OAAO,EAAE,MAAM,GALN,EAAG,YAAY,IAAY,IAKd,GAAG;OAC1B,GAHM,CAGN,CAEJ;OAEA,OAAW,QAAQ,MAClB,kBAAC,OAAD;QAAK,WAAU;QAAwB,OAAO,EAAE,MAAM,GAAG,GAAO,IAAI;kBAApE,CACE,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,OAAD;UACE,KAAK;UACL,KAAI;UACJ,OAAO;WACL,OAAO;WACP,QAAQ;WACR,WAAW;WACX,cAAc;WACd,SAAS;UACX;SACD,CAAA;QACE,CAAA,GACL,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACG,EAAW,EAAS,GACpB,MACC,kBAAC,OAAD;UAAK,WAAU;oBAAyB,GAAa;SAAW,CAAA,CAE/D;UACF;;MAEJ;SAEL,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,UAAD;SAAQ,WAAU;SAAkB,SAAS;SAAY,OAAO,IAAY,UAAU;mBACvE,EAAZ,IAAa,KAA0C,IAA3C;UAAO,MAAM;UAAI,MAAK;SAAgB,CAAyC;QACtF,CAAA;QACR,kBAAC,UAAD;SACE,WAAU;SACV,eAAe;UAEb,AADA,EAAY,CAAC,GACR,KACH,GAAQ,KAAK;SAEjB;SACA,OAAM;mBAEN,kBAAC,IAAD,EAAW,MAAM,GAAK,CAAA;QAChB,CAAA;QACR,kBAAC,UAAD;SACE,WAAU;SACV,eAAe,EAAY,IAAc,EAAE;SAC3C,OAAM;mBAEN,kBAAC,OAAD;UAAK,OAAO;WAAE,UAAU;WAAY,SAAS;WAAQ,YAAY;WAAU,gBAAgB;WAAU,OAAO;WAAQ,QAAQ;UAAO;oBAAnI,CACE,kBAAC,IAAD,EAAW,MAAM,GAAK,CAAA,GACtB,kBAAC,QAAD;WAAM,OAAO;YACX,UAAU;YACV,UAAU;YACV,YAAY;YACZ,KAAK;YACL,MAAM;YACN,WAAW;YACX,eAAe;YACf,YAAY;YACZ,YAAY;WACd;qBAAG;UAAQ,CAAA,CACR;;QACC,CAAA;QACR,kBAAC,UAAD;SACE,WAAU;SACV,eAAe,EAAY,IAAc,EAAE;SAC3C,OAAM;mBAEN,kBAAC,OAAD;UAAK,OAAO;WAAE,UAAU;WAAY,SAAS;WAAQ,YAAY;WAAU,gBAAgB;WAAU,OAAO;WAAQ,QAAQ;UAAO;oBAAnI,CACE,kBAAC,IAAD,EAAU,MAAM,GAAK,CAAA,GACrB,kBAAC,QAAD;WAAM,OAAO;YACX,UAAU;YACV,UAAU;YACV,YAAY;YACZ,KAAK;YACL,MAAM;YACN,WAAW;YACX,eAAe;YACf,YAAY;YACZ,YAAY;WACd;qBAAG;UAAQ,CAAA,CACR;;QACC,CAAA;QAER,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,UAAD;UAAQ,WAAU;UAAkB,SAAS;oBAC1C,GAAc;SACT,CAAA,GACR,kBAAC,OAAD;UAAK,WAAU;oBACb,kBAAC,SAAD;WACE,MAAK;WACL,KAAI;WACJ,KAAI;WACJ,MAAK;WACL,OAAO,IAAU,IAAI;WACrB,UAAU;WACV,WAAU;WACV,OAAO,EAAE,gBAAgB,IAAI,IAAU,IAAI,KAAU,IAAI,GAAG;UAC7D,CAAA;SACE,CAAA,CACF;;QAEL,kBAAC,OAAD;SAAK,WAAU;mBAAf;UACG,EAAW,MAAkB,OAAuB,IAAhB,CAA2B;UAAE;UAAI,EAAW,CAAQ;SACtF;;QAEJ,KAAoB,GAAc,WACjC,kBAAC,OAAD;SACE,WAAW,qBAAqB,IAAsB,WAAW;SACjE,OAAM;SACN,eAAe,GAAuB,CAAC,CAAmB;SAC1D,OAAO;UAAE,QAAQ;UAAW,SAAS;UAAQ,YAAY;SAAS;mBAJpE;UAME,kBAAC,GAAD;WAAW,MAAM;WAAI,OAAO,EAAE,aAAa,MAAM;UAAI,CAAA;UACrD,kBAAC,QAAD;WAAM,OAAO,EAAE,aAAa,MAAM;qBAAI,GAAc,QAAQ;UAAY,CAAA;UACxE,kBAAC,GAAD;WAAc,MAAM;WAAI,OAAO;YAAE,SAAS;YAAK,WAAW,IAAsB,kBAAkB;YAAQ,YAAY;WAAsB;UAAI,CAAA;SAC7I;;OAEJ;UAEL,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACG,MACC,kBAAC,UAAD;SACE,WAAU;SACV,SAAS;SACT,OAAM;SACN,OAAO,EAAE,OAAO,IAAa,YAAY,GAAG;mBAE5C,kBAAC,GAAD,EAAM,MAAM,GAAK,CAAA;QACX,CAAA;QAGT,MACC,kBAAC,UAAD;SACE,WAAU;SACV,SAAS;SACT,OAAM;SACN,OAAO,EAAE,OAAO,EAAW;mBAE3B,kBAAC,GAAD,EAAS,MAAM,GAAK,CAAA;QACd,CAAA;QAGT,GAAY,SAAS,KACpB,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,UAAD;UACE,WAAU;UACV,eAAe,EAAW,OAAO;UACjC,OAAM;UACN,OAAO,EAAE,OAAO,MAAe,UAAU,IAAa,GAAG;oBAEzD,kBAAC,GAAD,EAAW,MAAM,GAAK,CAAA;SAChB,CAAA,GACP,MAAe,WACd,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,OAAD;WAAK,WAAU;qBAAoB;UAAiB,CAAA,GACnD,GAAY,KAAK,MAChB,kBAAC,OAAD;WAEE,WAAW,mBAAmB,OAAqB,EAAM,KAAK,WAAW;WACzE,eAAe;YAGb,AAFA,GAAQ,cAAc,EAAM,EAAE,GAC9B,GAAoB,EAAM,EAAE,GAC5B,EAAc,MAAM;WACtB;qBAPF,CASE,kBAAC,QAAD,EAAA,UAAO,GAAiB,CAAK,EAAQ,CAAA,GACpC,OAAqB,EAAM,MAAM,kBAAC,GAAD,EAAO,MAAM,GAAK,CAAA,CACjD;aAVE,EAAM,EAUR,CACN,CACE;WAEJ;;QAGN,MACC,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,UAAD;UACE,WAAU;UACV,eAAe,EAAW,WAAW;UACrC,OAAM;UACN,OAAO,EAAE,OAAO,MAAwB,QAAqB,KAAb,EAAgB;oBAEhE,kBAAC,IAAD,EAAW,MAAM,GAAK,CAAA;SAChB,CAAA,GACP,MAAe,eACd,kBAAC,OAAD;UAAK,WAAU;oBAAf;WACE,kBAAC,OAAD;YAAK,WAAU;sBAAoB;WAAc,CAAA;WACjD,kBAAC,OAAD;YACE,WAAW,mBAAmB,MAAwB,QAAQ,WAAW;YACzE,eAAe,GAAqB,KAAK;sBAF3C,CAIE,kBAAC,QAAD,EAAA,UAAM,MAAS,CAAA,GACd,MAAwB,SAAS,kBAAC,GAAD,EAAO,MAAM,GAAK,CAAA,CACjD;;WACJ,GAAmB,KAAK,MACvB,kBAAC,OAAD;YAEE,WAAW,mBAAmB,MAAwB,EAAM,KAAK,WAAW;YAC5E,eAAe,GAAqB,EAAM,EAAE;sBAH9C,CAKE,kBAAC,QAAD,EAAA,UAAO,GAAiB,CAAK,EAAQ,CAAA,GACpC,MAAwB,EAAM,MAAM,kBAAC,GAAD,EAAO,MAAM,GAAK,CAAA,CACpD;cANE,EAAM,EAMR,CACN;UACE;WAEJ;;QAGN,MACC,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,UAAD;UACE,WAAU;UACV,eAAe,EAAW,SAAS;UACnC,OAAM;UACN,OAAO,EAAE,OAAO,GAAc,OAAO,KAAkB,KAAb,EAAgB;oBAE1D,kBAAC,IAAD,EAAU,MAAM,GAAK,CAAA;SACf,CAAA,GACP,MAAe,aACd,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,OAAD;WAAK,WAAU;qBAAoB;UAAoB,CAAA,GACtD,GAAU,KAAK,MACd,kBAAC,OAAD;WAEE,WAAW,mBAAmB,GAAc,OAAO,EAAE,KAAK,WAAW;WACrE,eAAe,GAAoB,EAAE,EAAE;qBAHzC,CAKE,kBAAC,QAAD,EAAA,UAAO,EAAE,MAAY,CAAA,GACpB,GAAc,OAAO,EAAE,MAAM,kBAAC,GAAD,EAAO,MAAM,GAAK,CAAA,CAC7C;aANE,EAAE,EAMJ,CACN,CACE;WAEJ;;QAGN,MACC,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,UAAD;UACE,WAAU;UACV,eAAe,EAAW,OAAO;UACjC,OAAM;oBAEN,kBAAC,QAAD;WAAM,OAAO;YAAE,UAAU;YAAQ,YAAY;WAAO;qBAApD,CAAwD,IAAa,GAAO;;SACtE,CAAA,GACP,MAAe,WACd,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,OAAD;WAAK,WAAU;qBAAoB;UAAU,CAAA,GAC5C;WAAC;WAAK;WAAM;WAAG;WAAM;WAAK;UAAC,EAAE,KAAK,MACjC,kBAAC,OAAD;WAEE,WAAW,mBAAmB,OAAiB,IAAO,WAAW;WACjE,eAAe,GAAkB,CAAI;qBAHvC,CAKE,kBAAC,QAAD,EAAA,UAAO,MAAS,IAAI,WAAW,GAAG,EAAK,GAAS,CAAA,GAC/C,OAAiB,KAAQ,kBAAC,GAAD,EAAO,MAAM,GAAK,CAAA,CACzC;aANE,CAMF,CACN,CACE;WAEJ;;QAGN,KACC,kBAAC,UAAD;SACE,WAAU;SACV,SAAS;SACT,OAAM;SACN,OAAO,EAAE,OAAO,KAAQ,IAAa,GAAG;mBAExC,kBAAC,IAAD,EAAkB,MAAM,GAAK,CAAA;QACvB,CAAA;QAGT,KACC,kBAAC,UAAD;SAAQ,WAAU;SAAkB,SAAS;SAAkB,OAAM;mBACnD,EAAf,KAAgB,IAAyB,GAA1B,EAAU,MAAM,GAAK,CAAyB;QACxD,CAAA;OAEP;QACF;OACF;;GAEJ,CAAA;GAGN,KAAoB,KAAuB,EAAgB,SAAS,KACnE,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAoB;MAAkB,CAAA,GACrD,kBAAC,UAAD;OACE,WAAU;OACV,eAAe,GAAuB,EAAK;OAC3C,OAAM;iBAEN,kBAAC,IAAD,EAAG,MAAM,GAAK,CAAA;MACR,CAAA,CACL;;KAEL,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,UAAD;OAAQ,WAAU;iBAAyB;MAAmB,CAAA;KAC3D,CAAA;KAEL,kBAAC,OAAD;MAAK,WAAU;gBACZ,EAAgB,KAAK,GAAI,MAAQ;OAChC,IAAM,IAAW,GAAc,UAAU;OACzC,OACE,kBAAC,OAAD;QAEE,WAAW,2BAA2B,IAAW,WAAW;QAC5D,eAAe,GAAmB,EAAG,SAAS;kBAHhD,CAKE,kBAAC,OAAD;SAAK,WAAU;SAAgC,OAAO,EAAE,UAAU,SAAS;mBAA3E,CACI,GAAkB,MAAQ,EAAG,YAC7B,kBAAC,OAAD;UACE,KAAK,GAAkB,MAAQ,EAAG;UAClC,KAAK,EAAG;UACR,OAAO;WACL,OAAO;WACP,QAAQ;WACR,WAAW;WACX,SAAS;UACX;SACD,CAAA,IAED,kBAAC,GAAD;UAAW,MAAM;UAAI,WAAU;UAA2B,OAAO,EAAE,OAAO,IAAW,IAAa,wBAAwB;SAAI,CAAA,GAEhI,kBAAC,QAAD;UAAM,WAAU;oBAA4B,EAAW,EAAG,SAAS;SAAQ,CAAA,CACxE;YACL,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,OAAD;UAAK,WAAU;UAA4B,OAAO,EAAE,OAAO,IAAW,IAAa,UAAU;oBAC1F,EAAG;SACD,CAAA,GACL,kBAAC,OAAD;UAAK,WAAU;oBAAf,CAA8C,aAClC,EAAW,EAAG,SAAS,CAC9B;WACF;UACF;UA7BE,CA6BF;MAET,CAAC;KACE,CAAA;IACF;;EAEJ;;AAET;;;AC96CA,SAAgB,EAAa,GAAwB,GAAoC;CACvF,IAAM,IAAO,EAAW,CAAS,GAC7B,IAAe;CAInB,OAFA,EAAK,OAAO,EAAc,GAAyC,CAAY,CAAC,GAEzE;EACL,OAAO,GAAgC;GAErC,AADA,IAAe;IAAE,GAAG;IAAc,GAAG;GAAS,GAC9C,EAAK,OAAO,EAAc,GAAyC,CAAY,CAAC;EAClF;EACA,UAAU;GACR,EAAK,QAAQ;EACf;CACF;AACF"}
@@ -0,0 +1,10 @@
1
+ (function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports,require("react"),require("react-dom/client"),require("hls.js"),require("lucide-react"),require("dashjs"),require("firebase/app"),require("firebase/analytics"),require("react/jsx-runtime")):typeof define==`function`&&define.amd?define([`exports`,`react`,`react-dom/client`,`hls.js`,`lucide-react`,`dashjs`,`firebase/app`,`firebase/analytics`,`react/jsx-runtime`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.WebVideoPlayerSDK={},e.React,e.ReactDOM,e.Hls,e.lucide,e.dashjs,e.firebase,e.firebase,e.ReactJsxRuntime))})(this,function(e,t,n,r,i,a,o,s,c){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var l=Object.create,u=Object.defineProperty,d=Object.getOwnPropertyDescriptor,f=Object.getOwnPropertyNames,p=Object.getPrototypeOf,m=Object.prototype.hasOwnProperty,ee=(e,t,n,r)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var i=f(t),a=0,o=i.length,s;a<o;a++)s=i[a],!m.call(e,s)&&s!==n&&u(e,s,{get:(e=>t[e]).bind(null,s),enumerable:!(r=d(t,s))||r.enumerable});return e},h=(e,t,n)=>(n=e==null?{}:l(p(e)),ee(t||!e||!e.__esModule?u(n,`default`,{value:e,enumerable:!0}):n,e));t=h(t,1),r=h(r,1),a=h(a,1);var te=class{callback;heartbeatInterval;isBuffering=!1;sessionId;videoId;deviceType;networkType;isFirstPlay=!0;watchStartTime=null;totalWatchTime=0;bufferingCount=0;totalBufferingTime=0;bufferingStartTime=null;milestonesHit=new Set;lastHeartbeatPosition=null;lastHeartbeatAt=null;constructor(e,t){this.callback=e,this.sessionId=this.generateSessionId(),this.videoId=t?this.extractVideoId(t):``,this.deviceType=this.getDeviceType(),this.networkType=this.getNetworkType()}registerCallback(e){this.callback=e}track(e,t,n={}){e===`play`&&this.isFirstPlay&&(this.isFirstPlay=!1,this.dispatchRaw({eventType:`session_start`,timestamp:Date.now(),currentTime:t.currentTime,duration:t.duration||0,sessionId:this.sessionId,videoId:this.videoId,deviceType:this.deviceType,networkType:this.networkType,watchPercentage:0,totalWatchTime:0,bufferingCount:0,totalBufferingTime:0})),e===`play`&&(this.watchStartTime=Date.now()),(e===`pause`||e===`ended`)&&this.watchStartTime!==null&&(this.totalWatchTime+=(Date.now()-this.watchStartTime)/1e3,this.watchStartTime=null);let r=t.duration?Math.min(100,Math.round(t.currentTime/t.duration*100)):0,i={eventType:e,timestamp:Date.now(),currentTime:t.currentTime,duration:t.duration||0,volume:t.volume,speed:t.playbackRate,sessionId:this.sessionId,videoId:this.videoId,deviceType:this.deviceType,networkType:this.networkType,watchPercentage:r,totalWatchTime:Math.round(this.totalWatchTime),bufferingCount:this.bufferingCount,totalBufferingTime:Math.round(this.totalBufferingTime),...n};this.dispatchRaw(i)}startHeartbeat(e,t){this.stopHeartbeat(),this.heartbeatInterval=window.setInterval(()=>{!e.paused&&!this.isBuffering&&(this.checkMilestones(e,t),this.checkStall(e,t),this.track(`heartbeat`,e,t())),this.lastHeartbeatPosition=e.currentTime,this.lastHeartbeatAt=Date.now()},5e3)}stopHeartbeat(){this.heartbeatInterval&&=(window.clearInterval(this.heartbeatInterval),void 0)}setBuffering(e,t,n){this.isBuffering!==e&&(this.isBuffering=e,e?(this.bufferingCount++,this.bufferingStartTime=Date.now()):this.bufferingStartTime!==null&&(this.totalBufferingTime+=(Date.now()-this.bufferingStartTime)/1e3,this.bufferingStartTime=null),this.track(e?`buffering_start`:`buffering_end`,t,n()))}checkMilestones(e,t){let{currentTime:n,duration:r}=e;if(!r)return;let i=n/r;for(let[n,r]of[[.25,`first_quartile`],[.5,`midpoint`],[.75,`third_quartile`]])i>=n&&!this.milestonesHit.has(r)&&(this.milestonesHit.add(r),this.track(r,e,{...t(),watchPercentage:n*100}))}checkStall(e,t){this.lastHeartbeatPosition!==null&&this.lastHeartbeatAt!==null&&e.currentTime-this.lastHeartbeatPosition<.1&&Date.now()-this.lastHeartbeatAt>=4500&&this.track(`stall`,e,t())}dispatchRaw(e){if(this.callback)try{this.callback(e)}catch(e){console.error(`[PlayerSDK Analytics] Callback failed:`,e)}}generateSessionId(){return typeof crypto<`u`&&typeof crypto.randomUUID==`function`?crypto.randomUUID():`sess-${Date.now()}-${Math.random().toString(36).slice(2,9)}`}extractVideoId(e){try{let t=new URL(e),n=t.pathname.split(`/`).filter(Boolean);return n.length>0&&n[n.length-1].includes(`.`)&&n.pop(),n[n.length-1]||t.hostname}catch{let t=e.split(`/`).filter(Boolean);return t.length>=2&&t[t.length-1].includes(`.`)?t[t.length-2]:t[t.length-1]||`unknown`}}getDeviceType(){let e=navigator.userAgent;return/mobile/i.test(e)?`mobile`:/tablet|ipad/i.test(e)?`tablet`:`desktop`}getNetworkType(){let e=navigator.connection||navigator.mozConnection||navigator.webkitConnection;return e&&(e.effectiveType||e.type)||`unknown`}},ne={session_start:`player_session_start`,error:`player_error`},re=new Set([`heartbeat`]),ie=class{analytics=null;constructor(e){this.init(e)}async init(e){try{if(!await(0,s.isSupported)()){console.warn(`[FirebaseAnalytics] Not supported in this environment.`);return}let t=(0,o.getApps)(),n=t.length===0?(0,o.initializeApp)(e):t[0];this.analytics=(0,s.getAnalytics)(n)}catch(e){console.error(`[FirebaseAnalytics] Initialization failed:`,e)}}createCallback(){return e=>this.handleEvent(e)}handleEvent(e){if(!this.analytics||re.has(e.eventType))return;let t=ne[e.eventType]??e.eventType,n=this.buildParams(e);try{(0,s.logEvent)(this.analytics,t,n)}catch(e){console.error(`[FirebaseAnalytics] logEvent failed:`,e)}}buildParams(e){let t={session_id:(e.sessionId??``).slice(0,100),video_id:(e.videoId??``).slice(0,100),device_type:e.deviceType??`unknown`,network_type:e.networkType??`unknown`,current_time:Math.round(e.currentTime),duration:Math.round(e.duration),watch_percentage:e.watchPercentage??0,total_watch_time:e.totalWatchTime??0,buffering_count:e.bufferingCount??0,total_buffering_time:e.totalBufferingTime??0};return e.rendition&&(t.rendition=e.rendition.slice(0,100)),e.bitrate&&(t.bitrate=e.bitrate),e.fromRendition&&(t.from_rendition=e.fromRendition.slice(0,100)),e.volume!==void 0&&(t.volume=Math.round(e.volume*100)),e.speed!==void 0&&(t.speed=e.speed),e.errorMessage&&(t.error_message=e.errorMessage.slice(0,100)),e.errorCode&&(t.error_code=e.errorCode),e.seekFrom!==void 0&&(t.seek_from=Math.round(e.seekFrom)),e.pipState&&(t.pip_state=e.pipState),t}destroy(){return Promise.resolve()}},ae=class{keyMoments=[];currentMomentIndex=-1;onMomentChangeCallback;constructor(e=[]){this.setKeyMoments(e)}setKeyMoments(e){this.keyMoments=[...e].sort((e,t)=>e.startTime-t.startTime),this.currentMomentIndex=-1}getKeyMoments(){return this.keyMoments}getCurrentMoment(e){let t=this.keyMoments.findIndex(t=>e>=t.startTime&&e<t.endTime);return t===-1?{keyMoment:null,index:-1}:{keyMoment:this.keyMoments[t],index:t}}updateTime(e){let{keyMoment:t,index:n}=this.getCurrentMoment(e);n!==this.currentMomentIndex&&(this.currentMomentIndex=n,this.onMomentChangeCallback&&this.onMomentChangeCallback(t,n))}onMomentChange(e){this.onMomentChangeCallback=e}setChapters(e){this.setKeyMoments(e)}getChapters(){return this.getKeyMoments()}getCurrentChapter(e){let t=this.getCurrentMoment(e);return{chapter:t.keyMoment,index:t.index}}onChapterChange(e){this.onMomentChange(e)}},oe=class{tracks=[];activeTrackId=`off`;currentCues=[];activeText=``;onSubtitleChangeCallback;onTrackSwitchCallback;constructor(e=[]){this.tracks=e;let t=e.find(e=>e.isDefault);t&&this.setActiveTrack(t.id)}getTracks(){return this.tracks}getActiveTrackId(){return this.activeTrackId}setHlsTracks(e){let t=this.tracks.filter(e=>!e.isHls&&!e.isNative);this.tracks=[...t,...e]}addTrack(e){this.tracks.find(t=>t.id===e.id)||this.tracks.push(e)}onTrackSwitch(e){this.onTrackSwitchCallback=e}setCustomSubtitleText(e){e!==this.activeText&&(this.activeText=e,this.onSubtitleChangeCallback&&this.onSubtitleChangeCallback(e))}async setActiveTrack(e){if(this.activeTrackId=e,e===`off`){this.currentCues=[],this.updateActiveText(0),this.onTrackSwitchCallback&&this.onTrackSwitchCallback(null);return}let t=this.tracks.find(t=>t.id===e);if(!t){this.currentCues=[],this.updateActiveText(0),this.onTrackSwitchCallback&&this.onTrackSwitchCallback(null);return}if(t.isHls||t.isNative){this.currentCues=[],this.updateActiveText(0),this.onTrackSwitchCallback&&this.onTrackSwitchCallback(t);return}if(this.onTrackSwitchCallback&&this.onTrackSwitchCallback(null),t.cues){this.currentCues=t.cues;return}if(t.src)try{let n=await fetch(t.src);if(!n.ok)throw Error(`HTTP error ${n.status}`);let r=await n.text(),i=this.parseVttOrSrt(r);t.cues=i,this.activeTrackId===e&&(this.currentCues=i)}catch(e){console.error(`[PlayerSDK Subtitles] Failed to fetch track ${t.label}:`,e),this.currentCues=[]}}updateTime(e){if(this.activeTrackId===`off`||this.currentCues.length===0){let e=this.tracks.find(e=>e.id===this.activeTrackId);(!e||!e.isHls&&!e.isNative)&&this.activeText!==``&&(this.activeText=``,this.onSubtitleChangeCallback&&this.onSubtitleChangeCallback(``));return}this.updateActiveText(e)}updateActiveText(e){let t=this.currentCues.filter(t=>e>=t.startTime&&e<=t.endTime).map(e=>e.text).join(`<br />`);t!==this.activeText&&(this.activeText=t,this.onSubtitleChangeCallback&&this.onSubtitleChangeCallback(t))}onSubtitleChange(e){this.onSubtitleChangeCallback=e}parseVttOrSrt(e){let t=e.replace(/^/,``);if(t.trimStart().startsWith(`#EXTM3U`))return[];let n=[],r=t.split(/\r?\n/),i=0,a=0,o=[],s=!1,c=!1,l=/(\d+):(\d{2}):(\d{2})[.,](\d{3})\s*-->\s*(\d+):(\d{2}):(\d{2})[.,](\d{3})/,u=/^(\d{2}):(\d{2})[.,](\d{3})\s*-->\s*(\d{2}):(\d{2})[.,](\d{3})/,d=(e,t,n,r)=>parseInt(e,10)*3600+parseInt(t,10)*60+parseInt(n,10)+parseInt(r,10)/1e3,f=e=>e.replace(/<\d{2}[:.]\d{2}[:.]\d{2}\.\d{3}>/g,``).replace(/<[^>]+>/g,``),p=()=>{s&&o.length>0&&n.push({startTime:i,endTime:a,text:o.map(f).join(`
2
+ `)}),o=[],s=!1};for(let e of r){let t=e.trim();if(!t){s&&p(),c=!1;continue}let n=t.toUpperCase();if(n===`WEBVTT`||n.startsWith(`NOTE`)||n.startsWith(`X-TIMESTAMP-MAP`)||n.startsWith(`STYLE`)||n.startsWith(`REGION`)){c=!0;continue}if(c)continue;let r=t.match(l);if(r){p(),i=d(r[1],r[2],r[3],r[4]),a=d(r[5],r[6],r[7],r[8]),s=!0;continue}let f=t.match(u);if(f){p(),i=parseInt(f[1],10)*60+parseInt(f[2],10)+parseInt(f[3],10)/1e3,a=parseInt(f[4],10)*60+parseInt(f[5],10)+parseInt(f[6],10)/1e3,s=!0;continue}s&&o.push(t)}return p(),n}},g=class{config;watermarkElement;floatInterval;constructor(e){this.config=e}validateDomain(e){return!this.config||!this.config.allowedDomains||this.config.allowedDomains.length===0?!0:this.config.allowedDomains.includes(e)||this.config.allowedDomains.some(t=>e.endsWith(t))}getAuthToken(){return this.config?.authToken}setupWatermark(e){if(this.removeWatermark(),!this.config||!this.config.watermarkText)return;let t=this.config.watermarkText,n=document.createElement(`div`);n.className=`sdk-watermark-overlay`,n.innerText=t,n.style.position=`absolute`,n.style.pointerEvents=`none`,n.style.userSelect=`none`,n.style.color=`rgba(255, 255, 255, 0.4)`,n.style.fontSize=`12px`,n.style.fontFamily=`monospace`,n.style.fontWeight=`bold`,n.style.zIndex=`9`,n.style.padding=`4px 8px`,n.style.borderRadius=`4px`,n.style.background=`rgba(0, 0, 0, 0.4)`,n.style.backdropFilter=`blur(2px)`,n.style.top=`20px`,n.style.right=`20px`,e.appendChild(n),this.watermarkElement=n}removeWatermark(){this.floatInterval&&=(window.clearInterval(this.floatInterval),void 0),this.watermarkElement&&this.watermarkElement.parentElement&&this.watermarkElement.parentElement.removeChild(this.watermarkElement),this.watermarkElement=void 0}isDRMProtected(){return!!(this.config?.widevineLicenseUrl||this.config?.playreadyLicenseUrl||this.config?.fairplayLicenseUrl)}getDRMDetails(){return{widevine:this.config?.widevineLicenseUrl||null,playready:this.config?.playreadyLicenseUrl||null,fairplay:this.config?.fairplayLicenseUrl||null,authTokenPresent:!!this.config?.authToken,allowedDomains:this.config?.allowedDomains||[]}}},_=class{isCasting=!1;castDeviceName=``;onCastStateChangeCallback;castContext=null;remotePlayer=null;remotePlayerController=null;constructor(){this.initCastApi()}initCastApi(){let e=()=>{this.castContext=window.cast.framework.CastContext.getInstance(),this.castContext.setOptions({receiverApplicationId:window.chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID,autoJoinPolicy:window.chrome.cast.AutoJoinPolicy.ORIGIN_SCOPED}),this.remotePlayer=new window.cast.framework.RemotePlayer,this.remotePlayerController=new window.cast.framework.RemotePlayerController(this.remotePlayer),this.remotePlayerController.addEventListener(window.cast.framework.RemotePlayerEventType.IS_CONNECTED_CHANGED,()=>{this.isCasting=this.remotePlayer.isConnected;let e=this.castContext.getCurrentSession();this.castDeviceName=e?e.getCastDevice().friendlyName:``,this.onCastStateChangeCallback&&this.onCastStateChangeCallback(this.isCasting,this.castDeviceName)})};window.cast&&window.cast.framework?e():window.__onGCastApiAvailable=t=>{t&&e()}}getCastingState(){return{isCasting:this.isCasting,deviceName:this.castDeviceName}}async requestSession(e,t){if(!this.castContext){console.error(`[PlayerSDK Cast] Google Cast Framework not loaded.`);return}try{await this.castContext.requestSession();let n=this.castContext.getCurrentSession();if(n&&e){let r=new window.chrome.cast.media.MediaInfo(e,`video/mp4`),i=new window.chrome.cast.media.LoadRequest(r);i.currentTime=t,i.autoplay=!0,await n.loadMedia(i)}}catch(e){console.warn(`[PlayerSDK Cast] Session request failed or cancelled by user.`,e)}}stopCasting(){if(this.castContext)try{this.castContext.endCurrentSession(!0)}catch(e){console.warn(`[PlayerSDK Cast] Failed to disconnect session`,e)}}onCastStateChange(e){this.onCastStateChangeCallback=e}requestAirPlaySession(e){let t=e;typeof t.webkitShowPlaybackTargetPicker==`function`?t.webkitShowPlaybackTargetPicker():console.warn(`[PlayerSDK AirPlay] AirPlay request is not supported on this device.`)}stopAirPlay(e){let t=e;typeof t.webkitShowPlaybackTargetPicker==`function`?t.webkitShowPlaybackTargetPicker():console.warn(`[PlayerSDK AirPlay] AirPlay stop is not supported on this device.`)}isAirPlayAvailable(){return!!window.WebKitPlaybackTargetAvailabilityEvent||navigator.userAgent.includes(`Safari`)&&!navigator.userAgent.includes(`Chrome`)}},v=class{config;constructor(e){this.config=e}getThumbnail(e){if(!this.config)return null;let{spriteUrl:t,tileWidth:n,tileHeight:r,columns:i,interval:a,totalCount:o}=this.config,s=Math.floor(e/a);s<0&&(s=0),s>=o&&(s=o-1);let c=s%i,l=Math.floor(s/i);return{url:t,x:c*n,y:l*r,width:n,height:r}}hasThumbnails(){return!!this.config}},se=class{options;videoElement;hlsInstance=null;dashInstance=null;activeQuality={id:-1,height:0,width:0,bitrate:0,label:`Auto`};qualityLevels=[];analytics;keyMoments;chapters;subtitles;drm;casting;thumbnails;hlsSubtitleCues=new Map;hlsActiveSubtitleKey=null;audioTrackList=[];eventListeners={};constructor(e){this.options={autoplay:!1,muted:!1,themeColor:`#6366f1`,...e},this.analytics=new te(this.options.analyticsCallback,this.options.src),this.keyMoments=new ae(this.options.keyMoments||this.options.chapters||[]),this.chapters=this.keyMoments,this.subtitles=new oe(this.options.subtitles),this.drm=new g(this.options.drm),this.casting=new _,this.thumbnails=new v(this.options.thumbnails),this.initializePlayer()}initializePlayer(){let e=window.location.hostname;if(!this.drm.validateDomain(e)){let t=`Domain Blocked: Access to streaming content is restricted on ${e}.`;throw console.error(t),this.options.container.innerHTML=`
3
+ <div style="background:#0f172a; color:#f43f5e; padding:20px; text-align:center; font-family:sans-serif; border-radius:12px; border:1px solid #e11d48; margin: 10px;">
4
+ <h4 style="margin:0 0 10px 0;">Content Protection Alert</h4>
5
+ <p style="font-size:13px; margin:0; opacity:0.8;">${t}</p>
6
+ </div>
7
+ `,Error(t)}this.videoElement=document.createElement(`video`),this.videoElement.className=`sdk-html5-video`,this.videoElement.style.width=`100%`,this.videoElement.style.height=`100%`,this.videoElement.style.display=`block`,this.videoElement.style.backgroundColor=`#000000`,this.videoElement.playsInline=!0,this.videoElement.controls=!1,this.videoElement.muted=!!this.options.muted,this.options.container.appendChild(this.videoElement),this.drm.setupWatermark(this.options.container),this.bindMediaEvents(),this.loadSource(this.options.src),this.setupBackgroundControls()}bindMediaEvents(){let e=this.videoElement;e.addEventListener(`play`,()=>{this.analytics.track(`play`,e,this.getAnalyticsPayload()),this.analytics.startHeartbeat(e,()=>this.getAnalyticsPayload()),this.emit(`play`,null)}),e.addEventListener(`pause`,()=>{this.analytics.track(`pause`,e,this.getAnalyticsPayload()),this.analytics.stopHeartbeat(),this.emit(`pause`,null)}),e.addEventListener(`timeupdate`,()=>{let t=e.currentTime;if(this.chapters.updateTime(t),this.subtitles.updateTime(t),this.hlsActiveSubtitleKey!==null){let e=(this.hlsSubtitleCues.get(this.hlsActiveSubtitleKey)??[]).filter(e=>t>=e.startTime&&t<e.endTime);this.subtitles.setCustomSubtitleText(e.map(e=>e.text).join(`<br />`))}this.emit(`timeupdate`,t)}),e.addEventListener(`seeking`,()=>{this.analytics.track(`seek`,e,this.getAnalyticsPayload()),this.emit(`seeking`,e.currentTime)}),e.addEventListener(`waiting`,()=>{this.analytics.setBuffering(!0,e,()=>this.getAnalyticsPayload()),this.emit(`buffering`,!0)}),e.addEventListener(`playing`,()=>{this.analytics.setBuffering(!1,e,()=>this.getAnalyticsPayload()),this.emit(`buffering`,!1)}),e.addEventListener(`volumechange`,()=>{this.analytics.track(`volume_change`,e,this.getAnalyticsPayload()),this.emit(`volumechange`,{volume:e.volume,muted:e.muted})}),e.addEventListener(`ratechange`,()=>{this.analytics.track(`speed_change`,e,this.getAnalyticsPayload()),this.emit(`ratechange`,e.playbackRate)}),e.addEventListener(`ended`,()=>{this.analytics.track(`ended`,e,this.getAnalyticsPayload()),this.analytics.track(`complete`,e,{...this.getAnalyticsPayload(),watchPercentage:100}),this.analytics.stopHeartbeat(),this.emit(`ended`,null)}),e.addEventListener(`enterpictureinpicture`,()=>{this.analytics.track(`pip_toggle`,e,{...this.getAnalyticsPayload(),errorMessage:`entered`}),this.emit(`pipchange`,!0)}),e.addEventListener(`leavepictureinpicture`,()=>{this.analytics.track(`pip_toggle`,e,{...this.getAnalyticsPayload(),errorMessage:`exited`}),this.emit(`pipchange`,!1)}),e.addEventListener(`error`,()=>{let t=e.error;this.analytics.track(`error`,e,{...this.getAnalyticsPayload(),errorMessage:t?`${t.code}: ${t.message}`:`Unknown HTML5 video playback error`}),this.emit(`error`,t)});let t=e.textTracks;if(t){let n=(e,t)=>{if(e.kind!==`subtitles`&&e.kind!==`captions`||this.subtitles.getTracks().some(e=>e.isNative&&e.nativeTrackIndex===t))return;let n=e.kind===`captions`;this.subtitles.addTrack({id:`native-${t}`,label:e.label||e.language||(n?`CC ${t+1}`:`Track ${t+1}`),lang:e.language||`und`,isDefault:!this.hlsInstance&&(e.default||e.mode===`showing`),isNative:!0,nativeTrackIndex:t})};e.addEventListener(`loadedmetadata`,()=>{this.analytics.track(`video_loaded`,e,this.getAnalyticsPayload());let r=0,i=this.hlsInstance?[`captions`]:[`subtitles`,`captions`];for(let e=0;e<t.length;e++){if(!i.includes(t[e].kind))continue;let a=this.subtitles.getTracks().length;n(t[e],e),this.subtitles.getTracks().length>a&&r++}r>0&&this.emit(`subtitlesloaded`,this.subtitles.getTracks())}),t.addEventListener(`addtrack`,e=>{let r=e.track;if(r)for(let e=0;e<t.length;e++){if(t[e]!==r)continue;let i=this.subtitles.getTracks().length;n(r,e),this.subtitles.getTracks().length>i&&this.emit(`subtitlesloaded`,this.subtitles.getTracks());break}})}this.subtitles.onTrackSwitch(e=>{if(e===null){if(this.hlsInstance)this.hlsInstance.subtitleTrack=-1;else if(t){for(let e=0;e<t.length;e++)t[e].mode=`disabled`,t[e].oncuechange=null;this.subtitles.setCustomSubtitleText(``)}}else if(e.isHls)this.hlsInstance&&e.hlsTrackId!==void 0&&(this.hlsInstance.subtitleTrack=e.hlsTrackId);else if(e.isNative&&t&&e.nativeTrackIndex!==void 0&&t[e.nativeTrackIndex])for(let n=0;n<t.length;n++)if(t[n].oncuechange=null,n===e.nativeTrackIndex){t[n].mode=`hidden`;let e=t[n];e.oncuechange=()=>{let t=e.activeCues;if(t&&t.length>0){let e=Array.from(t).map(e=>e.text).join(`<br />`);this.subtitles.setCustomSubtitleText(e)}else this.subtitles.setCustomSubtitleText(``)}}else t[n].mode=`disabled`})}rewriteToProxy(e){if(e.includes(`d3queh3es1i8u.cloudfront.net`))try{let t=new URL(e);return`/video-proxy${t.pathname}${t.search}`}catch{return e.replace(`https://d3queh3es1i8u.cloudfront.net`,`/video-proxy`)}return e}loadSource(e){this.extractKeyMomentsFromManifest(e),this.extractSubtitlesFromManifest(e);let t=this.rewriteToProxy(e),n=t.endsWith(`.m3u8`)||t.includes(`.m3u8`),i=t.endsWith(`.mpd`)||t.includes(`.mpd`);if(n)if(r.default.isSupported()){let n=new r.default({enableWorker:!0,lowLatencyMode:!0,backBufferLength:90,maxMaxBufferLength:30,renderTextTracksNatively:!1,xhrSetup:(t,n)=>{let r=this.drm.getAuthToken();if(!r)try{let t=new URL(e),n=t.searchParams.get(`token`)||t.searchParams.get(`key`)||t.searchParams.get(`auth`)||t.searchParams.get(`authToken`);n&&(r=n)}catch{}try{let e=this.rewriteToProxy(n),i=new URL(e,window.location.origin);r&&i.searchParams.set(`token`,r),t.open(`GET`,i.toString(),!0)}catch(e){console.error(`[PlayerSDK Core] Error in xhrSetup URL parsing:`,e)}}});this.hlsInstance=n,n.loadSource(t),n.attachMedia(this.videoElement),n.on(r.default.Events.SUBTITLE_TRACK_SWITCH,(e,t)=>{if(this.hlsActiveSubtitleKey=null,this.subtitles.setCustomSubtitleText(``),t.id!==-1){let e=n.subtitleTracks[t.id];this.hlsActiveSubtitleKey=e?.default?`default`:`subtitles${t.id}`}}),n.on(r.default.Events.CUES_PARSED,(e,t)=>{let n=this.hlsSubtitleCues.get(t.track)??[],r=t.cues.filter(e=>!n.some(t=>t.startTime===e.startTime&&t.endTime===e.endTime));r.length>0&&this.hlsSubtitleCues.set(t.track,[...n,...r])}),n.on(r.default.Events.MANIFEST_PARSED,()=>{this.qualityLevels=n.levels.map((e,t)=>({id:t,height:e.height,width:e.width,bitrate:e.bitrate,label:e.height?`${e.height}p`:`Option ${t+1}`}));let e={id:-1,height:0,width:0,bitrate:0,label:`Auto`};this.qualityLevels.unshift(e),this.activeQuality=e,this.emit(`qualitylevels`,this.qualityLevels),this.options.autoplay&&this.play()}),n.on(r.default.Events.AUDIO_TRACKS_UPDATED,()=>{n.audioTracks.length>1&&(this.audioTrackList=n.audioTracks.map((e,t)=>({id:t,label:e.name||e.lang||`Audio ${t+1}`,lang:e.lang||``})),this.emit(`audiotracksloaded`,this.audioTrackList))}),n.on(r.default.Events.AUDIO_TRACK_SWITCHED,(e,t)=>{this.analytics.track(`audio_track_change`,this.videoElement,this.getAnalyticsPayload()),this.emit(`audiotrackchange`,t.id)}),n.on(r.default.Events.LEVEL_SWITCHED,(e,t)=>{let n=this.qualityLevels.find(e=>e.id===t.level);n&&(this.analytics.track(`quality_change`,this.videoElement,{...this.getAnalyticsPayload(),rendition:n.label,bitrate:n.bitrate}),this.emit(`qualitychange`,n))}),n.on(r.default.Events.ERROR,(e,t)=>{t.fatal&&(t.type===r.default.ErrorTypes.NETWORK_ERROR?(this.analytics.track(`network_error`,this.videoElement,{...this.getAnalyticsPayload(),errorMessage:t.details,errorCode:0}),n.startLoad()):t.type===r.default.ErrorTypes.MEDIA_ERROR?n.recoverMediaError():this.destroy())})}else this.videoElement.canPlayType(`application/vnd.apple.mpegurl`)&&(this.videoElement.src=e,this.qualityLevels=[{id:0,height:720,width:1280,bitrate:2e6,label:`720p (Native)`}],this.activeQuality=this.qualityLevels[0],this.options.autoplay&&this.videoElement.play().catch(console.error));else if(i){let t=a.MediaPlayer().create();this.dashInstance=t,t.initialize(this.videoElement,e,!!this.options.autoplay),t.updateSettings({streaming:{abr:{autoSwitchBitrate:{video:!0,audio:!0}},buffer:{bufferTimeAtTopQuality:30}}}),this.qualityLevels=[{id:0,height:0,width:0,bitrate:0,label:`Auto (DASH)`}],this.activeQuality=this.qualityLevels[0],this.emit(`qualitylevels`,this.qualityLevels)}else this.videoElement.src=e,this.qualityLevels=[{id:0,height:1080,width:1920,bitrate:45e5,label:`1080p (Source)`}],this.activeQuality=this.qualityLevels[0],this.options.autoplay&&this.videoElement.play().catch(console.error)}setupBackgroundControls(){if(`mediaSession`in navigator){navigator.mediaSession.metadata=new MediaMetadata({title:`Video Player`,album:`Video Player`,artwork:[{src:`https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?w=512&q=80`,sizes:`512x512`,type:`image/jpeg`}]});try{navigator.mediaSession.setActionHandler(`play`,()=>this.play()),navigator.mediaSession.setActionHandler(`pause`,()=>this.pause()),navigator.mediaSession.setActionHandler(`seekbackward`,e=>{let t=e.seekOffset||10;this.seek(this.videoElement.currentTime-t)}),navigator.mediaSession.setActionHandler(`seekforward`,e=>{let t=e.seekOffset||10;this.seek(this.videoElement.currentTime+t)})}catch{console.warn(`[PlayerSDK Session] Action handler binding is unsupported by browser.`)}}}play(){this.videoElement.play().catch(e=>{console.warn(`[PlayerSDK Play] Autoplay blocked, wait for user interaction:`,e)})}pause(){this.videoElement.pause()}seek(e){let t=Math.max(0,Math.min(e,this.getDuration()));this.videoElement.currentTime=t}setVolume(e){let t=Math.max(0,Math.min(e,1));this.videoElement.volume=t,this.videoElement.muted=t===0}setMute(e){this.videoElement.muted=e}setPlaybackRate(e){this.videoElement.playbackRate=e}setQuality(e){let t=this.qualityLevels.find(t=>t.id===e);t&&(this.activeQuality=t,this.hlsInstance?this.hlsInstance.currentLevel=e:console.warn(`[PlayerSDK Core] Manual rendition switching is not supported by standard native players.`))}async togglePictureInPicture(){if(!document.pictureInPictureEnabled)return console.warn(`[PlayerSDK Core] Picture-in-Picture is disabled or unsupported.`),!1;try{return document.pictureInPictureElement?(await document.exitPictureInPicture(),!1):(await this.videoElement.requestPictureInPicture(),!0)}catch(e){return console.error(`[PlayerSDK Core] PiP toggle failed:`,e),!1}}toggleFullscreen(){let e=this.options.container;document.fullscreenElement?document.exitFullscreen():e.requestFullscreen().catch(e=>{console.error(`[PlayerSDK Fullscreen] Failed entering fullscreen:`,e)})}getVideoElement(){return this.videoElement}getCurrentTime(){return this.videoElement.currentTime}getDuration(){return this.videoElement.duration||0}getVolume(){return this.videoElement.volume}isMuted(){return this.videoElement.muted}getPlaybackRate(){return this.videoElement.playbackRate}getActiveQuality(){return this.activeQuality}getQualityLevels(){return this.qualityLevels}getBufferLength(){let e=this.videoElement.buffered,t=this.videoElement.currentTime;for(let n=0;n<e.length;n++)if(t>=e.start(n)&&t<=e.end(n))return e.end(n)-t;return 0}getBufferedRanges(){let e=this.videoElement.buffered,t=[];for(let n=0;n<e.length;n++)t.push({start:e.start(n),end:e.end(n)});return t}getAnalyticsPayload(){return{rendition:this.activeQuality.label,bitrate:this.activeQuality.bitrate,volume:this.videoElement.volume,speed:this.videoElement.playbackRate}}on(e,t){this.eventListeners[e]||(this.eventListeners[e]=[]),this.eventListeners[e].push(t)}off(e,t){this.eventListeners[e]&&(this.eventListeners[e]=this.eventListeners[e].filter(e=>e!==t))}emit(e,...t){this.eventListeners[e]&&this.eventListeners[e].forEach(e=>e(...t))}async extractSubtitlesFromManifest(e){try{let t=this.rewriteToProxy(e),n=await fetch(t);if(!n.ok)return;let r=(await n.text()).split(`
8
+ `),i=this.drm.getAuthToken()||void 0;if(!i)try{let t=new URL(e);i=t.searchParams.get(`token`)||t.searchParams.get(`key`)||t.searchParams.get(`auth`)||t.searchParams.get(`authToken`)||void 0}catch{}let a=e.substring(0,e.lastIndexOf(`/`)+1),o=[];for(let e of r){if(!e.startsWith(`#EXT-X-MEDIA:TYPE=SUBTITLES`))continue;let t=e.match(/NAME="([^"]+)"/),n=e.match(/LANGUAGE="([^"]+)"/),r=e.match(/URI="([^"]+)"/),s=/DEFAULT=YES/i.test(e);if(!r)continue;let c=r[1],l=o.length,u=t?t[1]:n?n[1]:`Track ${l+1}`,d=n?n[1]:`und`;if(/\.m3u8?(\?|$)/i.test(c)){o.push({id:`subtitle-${l}`,label:u,lang:d,isDefault:s,isHls:!0,hlsTrackId:l});continue}let f=c.startsWith(`http`)?c:a+c,p=this.rewriteToProxy(f);if(i)try{let e=new URL(p,window.location.origin);e.searchParams.set(`token`,i),p=e.toString()}catch{}o.push({id:`subtitle-${l}`,label:u,lang:d,isDefault:s,src:p})}o.length>0&&(this.subtitles.setHlsTracks(o),this.emit(`subtitlesloaded`,this.subtitles.getTracks()))}catch(e){console.error(`[PlayerSDK] Error extracting subtitles from manifest:`,e)}}async extractKeyMomentsFromManifest(e){try{let t=this.rewriteToProxy(e),n=await fetch(t);if(!n.ok)return;let r=(await n.text()).split(`
9
+ `),i=[];for(let e of r)if(e.startsWith(`#EXT-X-DATERANGE:`)){let t=e.match(/ID="([^"]+)"/),n=e.match(/START-DATE="([^"]+)"/),r=e.match(/X-TITLE="([^"]+)"/);t&&n&&r&&(i.some(e=>e.id===t[1])||i.push({id:t[1],startDate:n[1],title:r[1]}))}if(i.length>0){i.sort((e,t)=>e.startDate.localeCompare(t.startDate));let e=new Date(i[0].startDate).getTime(),t=i.map(t=>{let n=(new Date(t.startDate).getTime()-e)/1e3;return{title:t.title,startTime:n,endTime:0}});for(let e=0;e<t.length;e++)e<t.length-1?t[e].endTime=t[e+1].startTime:t[e].endTime=99999;this.keyMoments.setKeyMoments(t),this.emit(`keymomentsloaded`,t),this.emit(`chaptersloaded`,t)}}catch(e){console.error(`Error parsing manifest key moments:`,e)}}getAudioTracks(){return this.audioTrackList}setAudioTrack(e){this.hlsInstance&&(this.hlsInstance.audioTrack=e)}destroy(){this.hlsSubtitleCues.clear(),this.hlsActiveSubtitleKey=null,this.analytics.stopHeartbeat(),this.drm.removeWatermark(),this.hlsInstance&&=(this.hlsInstance.destroy(),null),this.dashInstance&&=(this.dashInstance.reset(),null),this.videoElement&&(this.videoElement.pause(),this.videoElement.removeAttribute(`src`),this.videoElement.load(),this.videoElement.parentElement&&this.videoElement.parentElement.removeChild(this.videoElement)),this.eventListeners={}}},y=e=>{if(e.includes(`d3queh3es1i8u.cloudfront.net`))try{let t=new URL(e);return`/video-proxy${t.pathname}${t.search}`}catch{return e.replace(`https://d3queh3es1i8u.cloudfront.net`,`/video-proxy`)}return e},ce=e=>{let t=e.replace(`#`,``);return t.length===6?`${parseInt(t.substring(0,2),16)}, ${parseInt(t.substring(2,4),16)}, ${parseInt(t.substring(4,6),16)}`:t.length===3?`${parseInt(t[0]+t[0],16)}, ${parseInt(t[1]+t[1],16)}, ${parseInt(t[2]+t[2],16)}`:`99, 102, 241`},b={en:`English`,eng:`English`,fr:`French`,fre:`French`,fra:`French`,es:`Spanish`,spa:`Spanish`,de:`German`,ger:`German`,deu:`German`,it:`Italian`,ita:`Italian`,pt:`Portuguese`,por:`Portuguese`,ja:`Japanese`,zh:`Chinese`,ko:`Korean`,ru:`Russian`,ar:`Arabic`,hi:`Hindi`},x=e=>{if(!e)return null;let t=e.trim().toLowerCase(),n=t.match(/^(en|eng|fr|fre|fra|es|spa|de|ger|deu|it|ita|pt|por|ja|zh|ko|ru|ar|hi)/);if(n&&b[n[0]])return b[n[0]];for(let[e,n]of Object.entries(b))if(t.includes(e))return n;return/english|anglais|ingles/i.test(t)?`English`:/french|français/i.test(t)?`French`:/spanish|español/i.test(t)?`Spanish`:/german|deutsch/i.test(t)?`German`:/italian|italiano/i.test(t)?`Italian`:/portuguese|português/i.test(t)?`Portuguese`:/japanese|nihongo/i.test(t)?`Japanese`:/chinese|中文/i.test(t)?`Chinese`:/korean|한국어/i.test(t)?`Korean`:/russian|русский/i.test(t)?`Russian`:/arabic|العربية/i.test(t)?`Arabic`:/hindi|हिन्दी/i.test(t)?`Hindi`:null},S=e=>{if(e.lang){let t=x(e.lang);if(t)return t}if(e.label){let t=e.label.trim(),n=t.split(/[#\d\.\s\-_/|:]+/).filter(Boolean);for(let e=n.length-1;e>=0;--e){let t=x(n[e]);if(t)return t}let r=t.replace(/^[#\d\.\s]+/,``).trim();return x(r)||r}return`Unknown`},C=e=>{let t=new Set;return e.filter(e=>{let n=`${S(e).toLowerCase()}|${(e.lang||``).trim().toLowerCase()}`;return t.has(n)?!1:(t.add(n),!0)})},w=({src:e,chapters:n=[],keyMoments:a,subtitles:o=[],watermark:s=`GoBOSS`,themeColor:l=`#6366f1`,drmConfig:u,firebaseConfig:d,onAnalyticsEvent:f,onKeyMomentsLoaded:p,enableControls:m=!0,enableChromecast:ee=!0,enableKeyMoments:h=!0,enableSubtitles:te=!0,enableQuality:ne=!0,enablePlaybackSpeed:re=!0,enablePiP:ae=!0,enableFullscreen:oe=!0,subtitleStyle:g={}})=>{let _=(0,t.useRef)(null),v=(0,t.useRef)(null),b=(0,t.useRef)(null),x=(0,t.useRef)(null),w=(0,t.useRef)(null),T=(0,t.useRef)(null),E=(0,t.useRef)(null),le=(0,t.useRef)(!1),D=(0,t.useRef)(!1),O=(0,t.useRef)(null),k=(0,t.useRef)(null),A=(0,t.useRef)(null),[j,ue]=(0,t.useState)(null),[M,N]=(0,t.useState)(!1),[P,de]=(0,t.useState)(0),[F,fe]=(0,t.useState)(0),[I,pe]=(0,t.useState)(1),[L,R]=(0,t.useState)(!1),[me,he]=(0,t.useState)(!1),[ge,_e]=(0,t.useState)(!1),[ve,ye]=(0,t.useState)(!1),[be,xe]=(0,t.useState)(!1),[Se,Ce]=(0,t.useState)([]),[we,Te]=(0,t.useState)({id:-1,height:0,width:0,bitrate:0,label:`Auto`}),[Ee,De]=(0,t.useState)(``),[z,B]=(0,t.useState)(`off`),[Oe,ke]=(0,t.useState)(C(o)),[Ae,je]=(0,t.useState)([]),[Me,Ne]=(0,t.useState)(0);(0,t.useEffect)(()=>{if(j){let e=j.subtitles.getTracks();ke(C([...o,...e.filter(e=>!o.some(t=>t.id===e.id))]))}else ke(C(o))},[o,j]);let[Pe,Fe]=(0,t.useState)({chapter:null,index:-1}),[V,Ie]=(0,t.useState)(a||n||[]),[H,U]=(0,t.useState)(`none`),[W,Le]=(0,t.useState)(1),[G,Re]=(0,t.useState)(!1),[ze,Be]=(0,t.useState)({}),Ve=JSON.stringify(V.map(e=>({title:e.title,startTime:e.startTime})));(0,t.useEffect)(()=>{Ie(a||n||[])},[a,n]),(0,t.useEffect)(()=>{if(!e||!V||V.length===0)return;Be({});let t=document.createElement(`video`);t.muted=!0,t.preload=`metadata`,t.crossOrigin=`anonymous`,t.style.cssText=`position:fixed;top:0;left:0;width:10px;height:10px;opacity:0.01;pointer-events:none;z-index:-9999;`,document.body.appendChild(t);let n=null,i=e.includes(`.m3u8`),a=document.createElement(`canvas`);a.width=160,a.height=90;let o=a.getContext(`2d`),s=0,c=!1,l=()=>{if(s>=V.length){p();return}let e=V[s].startTime;t.currentTime=e===0?.01:e},d=()=>{if(o&&t.readyState>=2){o.drawImage(t,0,0,a.width,a.height);let e=a.toDataURL(`image/jpeg`,.8);Be(t=>({...t,[s]:e})),s++,l()}else setTimeout(d,100)},f=()=>{c||(c=!0,l())};t.addEventListener(`seeked`,d),t.addEventListener(`canplay`,f),i&&r.default.isSupported()?(n=new r.default({enableWorker:!1,maxBufferLength:5,xhrSetup:(t,n)=>{let r=u?.authToken;if(!r)try{let t=new URL(e),n=t.searchParams.get(`token`)||t.searchParams.get(`key`)||t.searchParams.get(`auth`)||t.searchParams.get(`authToken`);n&&(r=n)}catch{}try{let e=y(n),i=new URL(e,window.location.origin);r&&i.searchParams.set(`token`,r),t.open(`GET`,i.toString(),!0)}catch(e){console.error(`[WebVideoPlayer tempHls] xhrSetup error:`,e)}}}),n.loadSource(y(e)),n.attachMedia(t)):t.src=e,t.readyState>=2&&f();let p=()=>{t.removeEventListener(`seeked`,d),t.removeEventListener(`canplay`,f),n&&n.destroy(),document.body.contains(t)&&document.body.removeChild(t)};return p},[e,Ve]);let[K,He]=(0,t.useState)(null),[Ue,We]=(0,t.useState)(!1),[Ge,Ke]=(0,t.useState)(!1),[qe,Je]=(0,t.useState)(null),[Ye,q]=(0,t.useState)(null),[Xe,Ze]=(0,t.useState)(0),[Qe,$e]=(0,t.useState)(null),[et,tt]=(0,t.useState)(null),[J,nt]=(0,t.useState)(null);(0,t.useEffect)(()=>{if(A.current&&!A.current.querySelector(`google-cast-launcher`)){let e=document.createElement(`google-cast-launcher`);e.style.cursor=`pointer`,e.style.width=`100%`,e.style.height=`100%`,e.style.display=`block`,A.current.appendChild(e)}},[]),(0,t.useEffect)(()=>{if(!j){We(!1),Ke(!1),Je(null);return}let e=e=>We(e),t=()=>{let e=j.getVideoElement(),t=!!e?.webkitCurrentPlaybackTargetIsWireless;Ke(t),Je(t?e.webkitCurrentPlaybackTargetName||`AirPlay device`:null)};e(j.casting.isAirPlayAvailable()),t();let n=j.getVideoElement(),r=t=>{e(t.availability===`available`)},i=()=>{t()};return`webkitShowPlaybackTargetPicker`in n&&(n.addEventListener(`webkitplaybacktargetavailabilitychanged`,r),n.addEventListener(`webkitcurrentplaybacktargetiswirelesschanged`,i)),()=>{`webkitShowPlaybackTargetPicker`in n&&(n.removeEventListener(`webkitplaybacktargetavailabilitychanged`,r),n.removeEventListener(`webkitcurrentplaybacktargetiswirelesschanged`,i))}},[j]);let[rt,it]=(0,t.useState)(!0),Y=(0,t.useRef)(null),at=(0,t.useRef)(!1),ot=(0,t.useRef)(`none`),st=(0,t.useRef)(o);(0,t.useEffect)(()=>{if(!_.current)return;let t=document.createElement(`video`);t.muted=!0,t.preload=`metadata`,t.crossOrigin=`anonymous`,t.style.cssText=`display:none;position:absolute;pointer-events:none;`,document.body.appendChild(t),w.current=t;let n=e.includes(`.m3u8`);if(n&&r.default.isSupported()){let n=new r.default({enableWorker:!1,maxBufferLength:5,xhrSetup:(t,n)=>{let r=u?.authToken;if(!r)try{let t=new URL(e),n=t.searchParams.get(`token`)||t.searchParams.get(`key`)||t.searchParams.get(`auth`)||t.searchParams.get(`authToken`);n&&(r=n)}catch{}try{let e=y(n),i=new URL(e,window.location.origin);r&&i.searchParams.set(`token`,r),t.open(`GET`,i.toString(),!0)}catch(e){console.error(`[WebVideoPlayer hiddenHls] xhrSetup error:`,e)}}});n.loadSource(y(e)),n.attachMedia(t),T.current=n}else n&&t.canPlayType(`application/vnd.apple.mpegurl`),t.src=e;t.addEventListener(`seeked`,()=>{let e=x.current;if(!e)return;let n=e.getContext(`2d`);n&&t.readyState>=2&&(n.drawImage(t,0,0,e.width,e.height),$e(e.toDataURL(`image/jpeg`,.82)))});let i;if(d){let e=new ie(d);v.current=e,i=e.createCallback()}let a=new se({container:_.current,src:e,autoplay:!1,muted:!1,keyMoments:V,subtitles:o,drm:{watermarkText:s,allowedDomains:[`localhost`,`127.0.0.1`],...u},analyticsCallback:e=>{i?.(e),f&&f(e)},themeColor:l});a.on(`keymomentsloaded`,e=>{Ie(e),p&&p(e)}),a.on(`chaptersloaded`,e=>{Ie(e),p&&p(e)}),a.on(`play`,()=>{N(!0),xe(!1)}),a.on(`pause`,()=>N(!1)),a.on(`ended`,()=>{N(!1),xe(!0)}),a.on(`timeupdate`,e=>{if(D.current&&O.current!==null)if(Math.abs(e-O.current)<5)D.current=!1,O.current=null,k.current&&=(window.clearTimeout(k.current),null);else return;de(e),fe(a.getDuration());let t=a.keyMoments.getCurrentMoment(e);Fe({chapter:t.keyMoment,index:t.index})}),a.on(`qualitylevels`,e=>{Ce(e)}),a.on(`qualitychange`,e=>{Te(e)}),a.on(`buffering`,e=>{he(e)}),a.on(`volumechange`,e=>{pe(e.volume),R(e.muted)}),a.on(`pipchange`,e=>{ye(e)}),a.subtitles.onSubtitleChange(e=>{De(e)}),a.on(`subtitlesloaded`,e=>{ke(C([...e]));let t=e.find(e=>e.isDefault);t&&(a.subtitles.setActiveTrack(t.id),B(t.id))}),a.on(`audiotracksloaded`,e=>{je(e),Ne(e[0]?.id??0)}),a.on(`audiotrackchange`,e=>{Ne(e)}),a.casting.onCastStateChange((e,t)=>{He(e?t:null)}),pe(a.getVolume()),R(a.isMuted()),ue(a);let c=()=>{let e=!!document.fullscreenElement;_e(e);let t=a.getVideoElement();a.analytics.track(e?`fullscreen_enter`:`fullscreen_exit`,t)};document.addEventListener(`fullscreenchange`,c);let m=e=>{let t=document.activeElement;if(!(t&&(t.tagName===`INPUT`||t.tagName===`TEXTAREA`||t.getAttribute(`contenteditable`)===`true`))){switch(e.key.toLowerCase()){case` `:case`k`:e.preventDefault(),a.getVideoElement().paused?a.play():a.pause();break;case`arrowleft`:case`j`:e.preventDefault(),a.seek(a.getCurrentTime()-10);break;case`arrowright`:case`l`:e.preventDefault(),a.seek(a.getCurrentTime()+10);break;case`arrowup`:e.preventDefault(),a.setVolume(a.getVolume()+.1);break;case`arrowdown`:e.preventDefault(),a.setVolume(a.getVolume()-.1);break;case`m`:e.preventDefault(),a.setMute(!a.isMuted());break;case`f`:e.preventDefault(),a.toggleFullscreen();break;case`p`:e.preventDefault(),a.togglePictureInPicture();break;case`c`:if(e.preventDefault(),a.subtitles.getActiveTrackId()!==`off`)a.subtitles.setActiveTrack(`off`),B(`off`);else{let e=st.current;e&&e.length>0&&(a.subtitles.setActiveTrack(e[0].id),B(e[0].id))}break;default:return}X()}};return document.addEventListener(`keydown`,m),X(),()=>{a.destroy(),v.current?.destroy(),v.current=null,document.removeEventListener(`fullscreenchange`,c),document.removeEventListener(`keydown`,m),Y.current&&window.clearTimeout(Y.current),T.current&&=(T.current.destroy(),null),w.current&&=(document.body.removeChild(w.current),null)}},[e,s]),(0,t.useEffect)(()=>{if(j){j.keyMoments.setKeyMoments(V);let e=_.current?.querySelector(`video`);if(e){let t=j.keyMoments.getCurrentMoment(e.currentTime);Fe({chapter:t.keyMoment,index:t.index})}}},[Ve,j]),(0,t.useEffect)(()=>{let e=e=>{H!==`none`&&(e.target.closest(`.sdk-menu-container`)||U(`none`))};return document.addEventListener(`click`,e),()=>{document.removeEventListener(`click`,e)}},[H]),at.current=M,ot.current=H,st.current=Oe;let X=()=>{it(!0),Y.current&&window.clearTimeout(Y.current),Y.current=window.setTimeout(()=>{at.current&&ot.current===`none`&&it(!1)},1e4)},ct=()=>{X()},lt=()=>{j&&(be?(Z(0),j.play(),xe(!1)):M?j.pause():j.play(),X())},ut=e=>{if(!b.current||!j||F===0)return 0;let t=b.current.getBoundingClientRect();return Math.max(0,Math.min(1,(e.clientX-t.left)/t.width))*F},dt=e=>{if(!j)return;le.current=!0,nt(ut(e));let t=e=>{if(!le.current||!b.current)return;let t=b.current.getBoundingClientRect(),n=Math.max(0,Math.min(1,(e.clientX-t.left)/t.width))*F;nt(n),q(e.clientX-t.left),Ze(n),w.current&&(w.current.currentTime=n),X()},n=e=>{if(le.current=!1,b.current){let t=b.current.getBoundingClientRect();Z(Math.max(0,Math.min(1,(e.clientX-t.left)/t.width))*F)}nt(null),document.removeEventListener(`mousemove`,t),document.removeEventListener(`mouseup`,n)};document.addEventListener(`mousemove`,t),document.addEventListener(`mouseup`,n)},Z=e=>{if(!j)return;let t=Math.max(0,F>0?Math.min(e,F):e);D.current=!0,O.current=t,de(t),j.seek(t),k.current&&window.clearTimeout(k.current),k.current=window.setTimeout(()=>{D.current=!1,O.current=null,k.current=null},3e3)},ft=e=>{if(!j)return;let t=parseFloat(e.target.value);j.setVolume(t),pe(t),R(t===0)},pt=()=>{if(!j)return;let e=!L;j.setMute(e),R(e)},mt=e=>{j&&(j.setPlaybackRate(e),Le(e),U(`none`))},ht=e=>{if(!j)return;j.setQuality(e);let t=Se.find(t=>t.id===e);t&&Te(t),U(`none`)},gt=e=>{j&&(j.subtitles.setActiveTrack(e),B(e),U(`none`))},_t=e=>{Z(e),U(`none`)},vt=()=>{j&&j.togglePictureInPicture()},yt=()=>{j&&j.toggleFullscreen()},bt=()=>{j&&(K?j.casting.stopCasting():j.casting.requestSession(e,P))},xt=()=>{if(!j)return;let e=j.getVideoElement();if(Ge){j.casting.stopAirPlay(e);return}typeof e.webkitShowPlaybackTargetPicker==`function`||j.casting.isAirPlayAvailable()?j.casting.requestAirPlaySession(e):console.warn(`[PlayerSDK AirPlay] AirPlay is not available on this device.`)},St=e=>{if(!b.current||F===0||!j)return;let t=b.current.getBoundingClientRect(),n=Math.max(0,Math.min(1,(e.clientX-t.left)/t.width))*F;q(e.clientX-t.left),Ze(n),E.current&&window.clearTimeout(E.current),E.current=window.setTimeout(()=>{w.current&&(w.current.currentTime=n)},100),tt(j.keyMoments.getCurrentMoment(n).keyMoment)},Ct=()=>{q(null),$e(null),tt(null),E.current&&window.clearTimeout(E.current)},Q=e=>{if(isNaN(e)||e<0)return`00:00`;let t=Math.floor(e),n=Math.floor(t/3600),r=Math.floor(t%3600/60),i=t%60,a=e=>String(e).padStart(2,`0`);return n>0?`${a(n)}:${a(r)}:${a(i)}`:`${a(r)}:${a(i)}`},wt=()=>L||I===0?(0,c.jsx)(i.VolumeX,{size:18}):I<.3?(0,c.jsx)(i.Volume,{size:18}):I<.7?(0,c.jsx)(i.Volume1,{size:18}):(0,c.jsx)(i.Volume2,{size:18}),$=e=>{U(H===e?`none`:e)};return(0,c.jsxs)(`div`,{ref:_,className:`sdk-player-container ${rt?`controls-active`:``}`,onMouseMove:ct,onMouseLeave:()=>{},style:{border:`1px solid rgba(255, 255, 255, 0.08)`,"--sdk-theme-color":l,"--sdk-theme-color-rgb":ce(l)},children:[(0,c.jsx)(`canvas`,{ref:x,width:160,height:90,style:{display:`none`,position:`absolute`,pointerEvents:`none`}}),(0,c.jsx)(`div`,{className:`sdk-spinner-overlay ${me?`active`:``}`,children:(0,c.jsx)(`div`,{className:`sdk-spinner`})}),Ee&&z!==`off`&&(0,c.jsx)(`div`,{className:`sdk-subtitles-overlay`,children:(0,c.jsx)(`span`,{className:`sdk-subtitles-text`,style:{fontSize:g.size===`small`?`14px`:g.size===`large`?`24px`:`18px`,color:g.color??`#ffffff`,background:g.background===`none`?`transparent`:g.background===`solid`?`rgba(0, 0, 0, 0.92)`:`rgba(15, 23, 42, 0.78)`,border:g.background===`none`?`none`:`1px solid rgba(255,255,255,0.08)`,backdropFilter:g.background===`none`?`none`:`blur(8px)`},dangerouslySetInnerHTML:{__html:Ee}})}),(K||Ge)&&(0,c.jsxs)(`div`,{className:`sdk-cast-screen`,children:[(0,c.jsx)(i.Cast,{size:64,className:`sdk-cast-icon-glow`}),(0,c.jsx)(`div`,{className:`sdk-cast-title`,children:K?`Casting to ${K}`:`AirPlay to ${qe||`device`}`}),(0,c.jsx)(`div`,{className:`sdk-cast-desc`,children:K?`This media asset is currently streaming on your external screen.`:`This media asset is currently streaming via AirPlay${qe?` to ${qe}`:``}.`}),(0,c.jsx)(`button`,{className:`sdk-cast-disconnect-btn`,onClick:()=>{K?j?.casting.stopCasting():Ge&&j?.casting.stopAirPlay(j.getVideoElement())},children:`Disconnect Streaming`})]}),!K&&!M&&(0,c.jsx)(`div`,{className:`sdk-center-action-overlay`,onClick:lt,children:(0,c.jsx)(`div`,{className:`sdk-center-action-btn`,children:be?(0,c.jsx)(i.RotateCcw,{size:32,className:`sdk-center-icon`}):(0,c.jsx)(i.Play,{size:32,className:`sdk-center-icon`,style:{marginLeft:`4px`},fill:`currentColor`})})}),!K&&(0,c.jsx)(`div`,{className:`sdk-controls-overlay`,onClick:e=>{e.target===e.currentTarget&&lt()},children:m&&(0,c.jsxs)(`div`,{className:`sdk-control-bar`,children:[(0,c.jsxs)(`div`,{ref:b,className:`sdk-seekbar-container`,onMouseDown:dt,onMouseMove:St,onMouseLeave:Ct,children:[(0,c.jsx)(`div`,{className:`sdk-seekbar-buffered`,style:{width:`${F?(j&&j.getBufferedRanges()[0]?.end||0)/F*100:0}%`}}),(0,c.jsx)(`div`,{className:`sdk-seekbar-progress`,style:{width:`${F?(J===null?P:J)/F*100:0}%`,background:l},children:(0,c.jsx)(`div`,{className:`sdk-seekbar-handle`})}),h&&V.map((e,t)=>F===0?null:(0,c.jsx)(`div`,{className:`sdk-seekbar-chapter-marker`,style:{left:`${e.startTime/F*100}%`}},t)),Ye!==null&&Qe&&(0,c.jsxs)(`div`,{className:`sdk-thumbnail-tooltip`,style:{left:`${Ye}px`},children:[(0,c.jsx)(`div`,{className:`sdk-thumbnail-preview`,children:(0,c.jsx)(`img`,{src:Qe,alt:`preview`,style:{width:`100%`,height:`100%`,objectFit:`cover`,borderRadius:`6px 6px 0 0`,display:`block`}})}),(0,c.jsxs)(`div`,{className:`sdk-thumbnail-text`,children:[Q(Xe),et&&(0,c.jsx)(`div`,{className:`sdk-thumbnail-chapter`,children:et.title})]})]})]}),(0,c.jsxs)(`div`,{className:`sdk-control-row`,children:[(0,c.jsxs)(`div`,{className:`sdk-control-group`,children:[(0,c.jsx)(`button`,{className:`sdk-control-btn`,onClick:lt,title:M?`Pause`:`Play`,children:M?(0,c.jsx)(i.Pause,{size:18,fill:`currentColor`}):(0,c.jsx)(i.Play,{size:18,fill:`currentColor`})}),(0,c.jsx)(`button`,{className:`sdk-control-btn`,onClick:()=>{Z(0),M||j?.play()},title:`Replay from beginning`,children:(0,c.jsx)(i.RotateCcw,{size:18})}),(0,c.jsx)(`button`,{className:`sdk-control-btn`,onClick:()=>Z(P-10),title:`Rewind 10s`,children:(0,c.jsxs)(`div`,{style:{position:`relative`,display:`flex`,alignItems:`center`,justifyContent:`center`,width:`18px`,height:`18px`},children:[(0,c.jsx)(i.RotateCcw,{size:18}),(0,c.jsx)(`span`,{style:{position:`absolute`,fontSize:`6px`,fontWeight:`bold`,top:`52%`,left:`50%`,transform:`translate(-50%, -50%)`,pointerEvents:`none`,fontFamily:`sans-serif`,lineHeight:1},children:`10`})]})}),(0,c.jsx)(`button`,{className:`sdk-control-btn`,onClick:()=>Z(P+10),title:`Forward 10s`,children:(0,c.jsxs)(`div`,{style:{position:`relative`,display:`flex`,alignItems:`center`,justifyContent:`center`,width:`18px`,height:`18px`},children:[(0,c.jsx)(i.RotateCw,{size:18}),(0,c.jsx)(`span`,{style:{position:`absolute`,fontSize:`6px`,fontWeight:`bold`,top:`52%`,left:`50%`,transform:`translate(-50%, -50%)`,pointerEvents:`none`,fontFamily:`sans-serif`,lineHeight:1},children:`10`})]})}),(0,c.jsxs)(`div`,{className:`sdk-volume-group`,children:[(0,c.jsx)(`button`,{className:`sdk-control-btn`,onClick:pt,children:wt()}),(0,c.jsx)(`div`,{className:`sdk-volume-slider-container`,children:(0,c.jsx)(`input`,{type:`range`,min:`0`,max:`1`,step:`0.05`,value:L?0:I,onChange:ft,className:`sdk-volume-slider`,style:{"--volume-pct":`${(L?0:I)*100}%`}})})]}),(0,c.jsxs)(`div`,{className:`sdk-time-display`,children:[Q(J===null?P:J),` / `,Q(F)]}),h&&Pe.chapter&&(0,c.jsxs)(`div`,{className:`sdk-chapter-badge ${G?`active`:``}`,title:`Toggle Key Moments side panel`,onClick:()=>Re(!G),style:{cursor:`pointer`,display:`flex`,alignItems:`center`},children:[(0,c.jsx)(i.ListVideo,{size:10,style:{marginRight:`5px`}}),(0,c.jsx)(`span`,{style:{marginRight:`3px`},children:Pe.chapter.title}),(0,c.jsx)(i.ChevronRight,{size:10,style:{opacity:.8,transform:G?`rotate(90deg)`:`none`,transition:`transform 0.2s ease`}})]})]}),(0,c.jsxs)(`div`,{className:`sdk-control-group`,children:[ee&&(0,c.jsx)(`button`,{className:`sdk-control-btn`,onClick:bt,title:`Cast Stream`,style:{color:K?`#3b82f6`:``},children:(0,c.jsx)(i.Cast,{size:18})}),Ue&&(0,c.jsx)(`button`,{className:`sdk-control-btn`,onClick:xt,title:`AirPlay`,style:{color:l},children:(0,c.jsx)(i.Airplay,{size:18})}),Ae.length>1&&(0,c.jsxs)(`div`,{className:`sdk-menu-container`,children:[(0,c.jsx)(`button`,{className:`sdk-control-btn`,onClick:()=>$(`audio`),title:`Audio track`,style:{color:H===`audio`?l:``},children:(0,c.jsx)(i.Languages,{size:18})}),H===`audio`&&(0,c.jsxs)(`div`,{className:`sdk-dropup-menu`,children:[(0,c.jsx)(`div`,{className:`sdk-dropup-header`,children:`Audio Tracks`}),Ae.map(e=>(0,c.jsxs)(`div`,{className:`sdk-dropup-item ${Me===e.id?`active`:``}`,onClick:()=>{j?.setAudioTrack(e.id),Ne(e.id),U(`none`)},children:[(0,c.jsx)(`span`,{children:S(e)}),Me===e.id&&(0,c.jsx)(i.Check,{size:12})]},e.id))]})]}),te&&(0,c.jsxs)(`div`,{className:`sdk-menu-container`,children:[(0,c.jsx)(`button`,{className:`sdk-control-btn`,onClick:()=>$(`subtitles`),title:`Subtitles track selector`,style:{color:z===`off`?``:l},children:(0,c.jsx)(i.Subtitles,{size:18})}),H===`subtitles`&&(0,c.jsxs)(`div`,{className:`sdk-dropup-menu`,children:[(0,c.jsx)(`div`,{className:`sdk-dropup-header`,children:`Subtitles`}),(0,c.jsxs)(`div`,{className:`sdk-dropup-item ${z===`off`?`active`:``}`,onClick:()=>gt(`off`),children:[(0,c.jsx)(`span`,{children:`Off`}),z===`off`&&(0,c.jsx)(i.Check,{size:12})]}),Oe.map(e=>(0,c.jsxs)(`div`,{className:`sdk-dropup-item ${z===e.id?`active`:``}`,onClick:()=>gt(e.id),children:[(0,c.jsx)(`span`,{children:S(e)}),z===e.id&&(0,c.jsx)(i.Check,{size:12})]},e.id))]})]}),ne&&(0,c.jsxs)(`div`,{className:`sdk-menu-container`,children:[(0,c.jsx)(`button`,{className:`sdk-control-btn`,onClick:()=>$(`quality`),title:`Stream Quality settings`,style:{color:we.id===-1?``:l},children:(0,c.jsx)(i.Settings,{size:18})}),H===`quality`&&(0,c.jsxs)(`div`,{className:`sdk-dropup-menu large`,children:[(0,c.jsx)(`div`,{className:`sdk-dropup-header`,children:`Quality Options`}),Se.map(e=>(0,c.jsxs)(`div`,{className:`sdk-dropup-item ${we.id===e.id?`active`:``}`,onClick:()=>ht(e.id),children:[(0,c.jsx)(`span`,{children:e.label}),we.id===e.id&&(0,c.jsx)(i.Check,{size:12})]},e.id))]})]}),re&&(0,c.jsxs)(`div`,{className:`sdk-menu-container`,children:[(0,c.jsx)(`button`,{className:`sdk-control-btn`,onClick:()=>$(`speed`),title:`Playback Speed`,children:(0,c.jsxs)(`span`,{style:{fontSize:`11px`,fontWeight:`bold`},children:[W,`x`]})}),H===`speed`&&(0,c.jsxs)(`div`,{className:`sdk-dropup-menu`,children:[(0,c.jsx)(`div`,{className:`sdk-dropup-header`,children:`Speed`}),[.5,.75,1,1.25,1.5,2].map(e=>(0,c.jsxs)(`div`,{className:`sdk-dropup-item ${W===e?`active`:``}`,onClick:()=>mt(e),children:[(0,c.jsx)(`span`,{children:e===1?`Normal`:`${e}x`}),W===e&&(0,c.jsx)(i.Check,{size:12})]},e))]})]}),ae&&(0,c.jsx)(`button`,{className:`sdk-control-btn`,onClick:vt,title:`Picture-in-Picture`,style:{color:ve?l:``},children:(0,c.jsx)(i.PictureInPicture,{size:18})}),oe&&(0,c.jsx)(`button`,{className:`sdk-control-btn`,onClick:yt,title:`Fullscreen`,children:ge?(0,c.jsx)(i.Minimize,{size:18}):(0,c.jsx)(i.Maximize,{size:18})})]})]})]})}),h&&G&&V.length>0&&(0,c.jsxs)(`div`,{className:`sdk-sidebar-panel`,children:[(0,c.jsxs)(`div`,{className:`sdk-sidebar-header`,children:[(0,c.jsx)(`div`,{className:`sdk-sidebar-title`,children:`In this video`}),(0,c.jsx)(`button`,{className:`sdk-sidebar-close-btn`,onClick:()=>Re(!1),title:`Close panel`,children:(0,c.jsx)(i.X,{size:16})})]}),(0,c.jsx)(`div`,{className:`sdk-sidebar-tabs`,children:(0,c.jsx)(`button`,{className:`sdk-sidebar-tab active`,children:`Key Moments`})}),(0,c.jsx)(`div`,{className:`sdk-sidebar-content`,children:V.map((e,t)=>{let n=Pe.index===t;return(0,c.jsxs)(`div`,{className:`sdk-sidebar-chapter-row ${n?`active`:``}`,onClick:()=>_t(e.startTime),children:[(0,c.jsxs)(`div`,{className:`sdk-sidebar-chapter-thumbnail`,style:{overflow:`hidden`},children:[ze[t]||e.thumbnail?(0,c.jsx)(`img`,{src:ze[t]||e.thumbnail,alt:e.title,style:{width:`100%`,height:`100%`,objectFit:`cover`,display:`block`}}):(0,c.jsx)(i.ListVideo,{size:14,className:`sdk-sidebar-chapter-icon`,style:{color:n?l:`rgba(255,255,255,0.4)`}}),(0,c.jsx)(`span`,{className:`sdk-sidebar-chapter-time`,children:Q(e.startTime)})]}),(0,c.jsxs)(`div`,{className:`sdk-sidebar-chapter-info`,children:[(0,c.jsx)(`div`,{className:`sdk-sidebar-chapter-title`,style:{color:n?l:`#ffffff`},children:e.title}),(0,c.jsxs)(`div`,{className:`sdk-sidebar-chapter-duration`,children:[`Start at `,Q(e.startTime)]})]})]},t)})})]})]})};function T(e,r){let i=(0,n.createRoot)(e),a=r;return i.render((0,t.createElement)(w,a)),{update(e){a={...a,...e},i.render((0,t.createElement)(w,a))},destroy(){i.unmount()}}}e.FirebaseAnalyticsProvider=ie,e.VideoPlayerSDK=se,e.WebVideoPlayer=w,e.createPlayer=T});
10
+ //# sourceMappingURL=index.umd.js.map