vidply 1.2.1 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/README.md +30 -2
  2. package/dist/dev/{vidply.AudioDescriptionManager-6RFVF27Z.js → vidply.AudioDescriptionManager-JYYLK5KB.js} +6 -6
  3. package/dist/dev/{vidply.CaptionStyleMenu-R2SCQ6VP.js → vidply.CaptionStyleMenu-47RCKQF5.js} +4 -4
  4. package/dist/dev/{vidply.DASHRenderer-EOVEW73H.js → vidply.DASHRenderer-VEJ4HDHO.js} +3 -3
  5. package/dist/dev/{vidply.FloatingPlayerManager-V5XQWS75.js → vidply.FloatingPlayerManager-C2UVRPZY.js} +5 -5
  6. package/dist/dev/{vidply.HLSRenderer-ZBEFVH5W.js → vidply.HLSRenderer-ZVBK2TNQ.js} +4 -4
  7. package/dist/dev/vidply.HTML5Renderer-HLRQUOCU.js +12 -0
  8. package/dist/dev/{vidply.SignLanguageManager-7IAJOVF6.js → vidply.SignLanguageManager-FLUCBW5H.js} +8 -8
  9. package/dist/dev/{vidply.SoundCloudRenderer-OA7QQYGN.js → vidply.SoundCloudRenderer-MGMAJ7NK.js} +3 -3
  10. package/dist/dev/{vidply.TranscriptManager-BPM3T23L.js → vidply.TranscriptManager-Y2RYXHJW.js} +10 -10
  11. package/dist/dev/{vidply.VimeoRenderer-BRHFYPNJ.js → vidply.VimeoRenderer-RQFQD7OE.js} +3 -3
  12. package/dist/dev/{vidply.YouTubeRenderer-HRTN4EJ5.js → vidply.YouTubeRenderer-EU2BARZF.js} +3 -3
  13. package/dist/dev/{vidply.chunk-UHMVDBDB.js → vidply.chunk-6LYZYI5G.js} +3 -3
  14. package/dist/dev/{vidply.chunk-W5BR65JY.js → vidply.chunk-A4ESEOQ2.js} +6 -6
  15. package/dist/dev/{vidply.chunk-XAK3DPTO.js → vidply.chunk-CGYIED2I.js} +2 -2
  16. package/dist/dev/{vidply.chunk-YVCYQZJI.js → vidply.chunk-HZ7UOKOU.js} +3 -3
  17. package/dist/dev/{vidply.chunk-NSMAZCMB.js → vidply.chunk-I6EDRYGJ.js} +33 -6
  18. package/dist/dev/{vidply.chunk-NSMAZCMB.js.map → vidply.chunk-I6EDRYGJ.js.map} +2 -2
  19. package/dist/dev/{vidply.chunk-LDVFAFGC.js → vidply.chunk-JKMQGLCJ.js} +5 -5
  20. package/dist/dev/{vidply.chunk-HOYEMJSD.js → vidply.chunk-JZNZX32Z.js} +2 -2
  21. package/dist/dev/{vidply.chunk-JAKZNGOR.js → vidply.chunk-P25MRJ6O.js} +2 -2
  22. package/dist/dev/{vidply.chunk-5IK3IC5V.js → vidply.chunk-PRFNESYA.js} +2 -2
  23. package/dist/dev/{vidply.chunk-RQG3MA7W.js → vidply.chunk-UI3RYCVF.js} +4 -3
  24. package/dist/dev/{vidply.chunk-RQG3MA7W.js.map → vidply.chunk-UI3RYCVF.js.map} +2 -2
  25. package/dist/dev/{vidply.chunk-C55OBT5H.js → vidply.chunk-UNFGMABP.js} +2 -2
  26. package/dist/dev/{vidply.de-5W5LBFSJ.js → vidply.de-VAUVSQXC.js} +29 -2
  27. package/dist/dev/{vidply.de-5W5LBFSJ.js.map → vidply.de-VAUVSQXC.js.map} +2 -2
  28. package/dist/dev/{vidply.es-SHDAT6L6.js → vidply.es-G65HFWO6.js} +29 -2
  29. package/dist/dev/{vidply.es-SHDAT6L6.js.map → vidply.es-G65HFWO6.js.map} +2 -2
  30. package/dist/dev/vidply.esm.js +594 -20
  31. package/dist/dev/vidply.esm.js.map +3 -3
  32. package/dist/dev/{vidply.fr-2JANOMNB.js → vidply.fr-MJNOZELM.js} +29 -2
  33. package/dist/dev/{vidply.fr-2JANOMNB.js.map → vidply.fr-MJNOZELM.js.map} +2 -2
  34. package/dist/dev/{vidply.ja-WWMSTEJH.js → vidply.ja-INMDHIYJ.js} +29 -2
  35. package/dist/dev/{vidply.ja-WWMSTEJH.js.map → vidply.ja-INMDHIYJ.js.map} +2 -2
  36. package/dist/legacy/vidply.js +718 -3
  37. package/dist/legacy/vidply.js.map +3 -3
  38. package/dist/legacy/vidply.min.js +2 -2
  39. package/dist/legacy/vidply.min.meta.json +72 -19
  40. package/dist/prod/{vidply.AudioDescriptionManager-YTDVDQBO.min.js → vidply.AudioDescriptionManager-EKWUNOPE.min.js} +2 -2
  41. package/dist/prod/{vidply.CaptionStyleMenu-2AFF3HF2.min.js → vidply.CaptionStyleMenu-JTWSNHRQ.min.js} +2 -2
  42. package/dist/prod/{vidply.DASHRenderer-URDOTOKS.min.js → vidply.DASHRenderer-CIDAHFWM.min.js} +2 -2
  43. package/dist/prod/{vidply.FloatingPlayerManager-QUBFK7CQ.min.js → vidply.FloatingPlayerManager-VZXHPCPM.min.js} +2 -2
  44. package/dist/prod/{vidply.HLSRenderer-46KNUVAB.min.js → vidply.HLSRenderer-Q6EFYKID.min.js} +2 -2
  45. package/dist/prod/vidply.HTML5Renderer-XRDJUTOO.min.js +6 -0
  46. package/dist/prod/{vidply.SignLanguageManager-H4C36EVL.min.js → vidply.SignLanguageManager-JUOSA5AM.min.js} +2 -2
  47. package/dist/prod/{vidply.SoundCloudRenderer-OYSWMW7D.min.js → vidply.SoundCloudRenderer-7ODD46PO.min.js} +2 -2
  48. package/dist/prod/{vidply.TranscriptManager-BA3DAS63.min.js → vidply.TranscriptManager-4B2QKYGQ.min.js} +2 -2
  49. package/dist/prod/{vidply.VimeoRenderer-NF3COTHU.min.js → vidply.VimeoRenderer-MR3UYLY7.min.js} +2 -2
  50. package/dist/prod/{vidply.YouTubeRenderer-UCQKYIJ4.min.js → vidply.YouTubeRenderer-ITRTC7NR.min.js} +2 -2
  51. package/dist/prod/{vidply.chunk-7S5AFTST.min.js → vidply.chunk-3EEDXU4T.min.js} +2 -2
  52. package/dist/prod/{vidply.chunk-EUOA2DJK.min.js → vidply.chunk-5HTX5QXW.min.js} +1 -1
  53. package/dist/prod/{vidply.chunk-JPMAK3NS.min.js → vidply.chunk-6OGHHIYP.min.js} +2 -2
  54. package/dist/prod/{vidply.chunk-XXUH7ADU.min.js → vidply.chunk-GUPGMFUZ.min.js} +2 -2
  55. package/dist/prod/{vidply.chunk-M76ABMKB.min.js → vidply.chunk-MWEBITCP.min.js} +1 -1
  56. package/dist/prod/{vidply.chunk-G6KGXNEW.min.js → vidply.chunk-PL3P3JHR.min.js} +1 -1
  57. package/dist/prod/{vidply.chunk-6ECBIYVI.min.js → vidply.chunk-QQZGV7TD.min.js} +2 -2
  58. package/dist/prod/{vidply.chunk-2HNCP64N.min.js → vidply.chunk-U5IRRYG5.min.js} +1 -1
  59. package/dist/prod/{vidply.chunk-WRVDVD33.min.js → vidply.chunk-VKHV4QHA.min.js} +2 -2
  60. package/dist/prod/{vidply.chunk-L4O44K63.min.js → vidply.chunk-XA4M574A.min.js} +1 -1
  61. package/dist/prod/{vidply.chunk-X2FQEQYR.min.js → vidply.chunk-ZPTV4JEP.min.js} +2 -2
  62. package/dist/prod/{vidply.de-JMCATTKZ.min.js → vidply.de-UZHFCEZU.min.js} +2 -2
  63. package/dist/prod/{vidply.es-YVG37V7Z.min.js → vidply.es-BOISUV2S.min.js} +2 -2
  64. package/dist/prod/vidply.esm.min.js +2 -2
  65. package/dist/prod/{vidply.fr-OGKGN2R4.min.js → vidply.fr-ICSMKPFM.min.js} +2 -2
  66. package/dist/prod/{vidply.ja-Z6DS7QLS.min.js → vidply.ja-VQ2HKGAY.min.js} +2 -2
  67. package/dist/vidply.css +56 -0
  68. package/dist/vidply.esm.min.meta.json +154 -112
  69. package/dist/vidply.min.css +1 -1
  70. package/package.json +7 -7
  71. package/src/controls/ControlBar.ts +32 -0
  72. package/src/controls/KeyboardHelp.ts +326 -0
  73. package/src/controls/KeyboardManager.ts +4 -0
  74. package/src/core/MediaSessionManager.ts +341 -0
  75. package/src/core/Player.ts +59 -1
  76. package/src/i18n/languages/de.ts +27 -0
  77. package/src/i18n/languages/en.ts +27 -0
  78. package/src/i18n/languages/es.ts +27 -0
  79. package/src/i18n/languages/fr.ts +27 -0
  80. package/src/i18n/languages/ja.ts +27 -0
  81. package/src/icons/Icons.ts +2 -1
  82. package/src/styles/vidply.css +56 -0
  83. package/src/types/events.ts +4 -0
  84. package/src/types/options.ts +14 -0
  85. package/dist/dev/vidply.HTML5Renderer-P7ICKYU4.js +0 -12
  86. package/dist/prod/vidply.HTML5Renderer-7TQDMIGY.min.js +0 -6
  87. /package/dist/dev/{vidply.AudioDescriptionManager-6RFVF27Z.js.map → vidply.AudioDescriptionManager-JYYLK5KB.js.map} +0 -0
  88. /package/dist/dev/{vidply.CaptionStyleMenu-R2SCQ6VP.js.map → vidply.CaptionStyleMenu-47RCKQF5.js.map} +0 -0
  89. /package/dist/dev/{vidply.DASHRenderer-EOVEW73H.js.map → vidply.DASHRenderer-VEJ4HDHO.js.map} +0 -0
  90. /package/dist/dev/{vidply.FloatingPlayerManager-V5XQWS75.js.map → vidply.FloatingPlayerManager-C2UVRPZY.js.map} +0 -0
  91. /package/dist/dev/{vidply.HLSRenderer-ZBEFVH5W.js.map → vidply.HLSRenderer-ZVBK2TNQ.js.map} +0 -0
  92. /package/dist/dev/{vidply.HTML5Renderer-P7ICKYU4.js.map → vidply.HTML5Renderer-HLRQUOCU.js.map} +0 -0
  93. /package/dist/dev/{vidply.SignLanguageManager-7IAJOVF6.js.map → vidply.SignLanguageManager-FLUCBW5H.js.map} +0 -0
  94. /package/dist/dev/{vidply.SoundCloudRenderer-OA7QQYGN.js.map → vidply.SoundCloudRenderer-MGMAJ7NK.js.map} +0 -0
  95. /package/dist/dev/{vidply.TranscriptManager-BPM3T23L.js.map → vidply.TranscriptManager-Y2RYXHJW.js.map} +0 -0
  96. /package/dist/dev/{vidply.VimeoRenderer-BRHFYPNJ.js.map → vidply.VimeoRenderer-RQFQD7OE.js.map} +0 -0
  97. /package/dist/dev/{vidply.YouTubeRenderer-HRTN4EJ5.js.map → vidply.YouTubeRenderer-EU2BARZF.js.map} +0 -0
  98. /package/dist/dev/{vidply.chunk-UHMVDBDB.js.map → vidply.chunk-6LYZYI5G.js.map} +0 -0
  99. /package/dist/dev/{vidply.chunk-W5BR65JY.js.map → vidply.chunk-A4ESEOQ2.js.map} +0 -0
  100. /package/dist/dev/{vidply.chunk-XAK3DPTO.js.map → vidply.chunk-CGYIED2I.js.map} +0 -0
  101. /package/dist/dev/{vidply.chunk-YVCYQZJI.js.map → vidply.chunk-HZ7UOKOU.js.map} +0 -0
  102. /package/dist/dev/{vidply.chunk-LDVFAFGC.js.map → vidply.chunk-JKMQGLCJ.js.map} +0 -0
  103. /package/dist/dev/{vidply.chunk-HOYEMJSD.js.map → vidply.chunk-JZNZX32Z.js.map} +0 -0
  104. /package/dist/dev/{vidply.chunk-JAKZNGOR.js.map → vidply.chunk-P25MRJ6O.js.map} +0 -0
  105. /package/dist/dev/{vidply.chunk-5IK3IC5V.js.map → vidply.chunk-PRFNESYA.js.map} +0 -0
  106. /package/dist/dev/{vidply.chunk-C55OBT5H.js.map → vidply.chunk-UNFGMABP.js.map} +0 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../src/utils/EventEmitter.ts", "../../src/utils/VideoFrameCapture.ts", "../../src/utils/DownloadInfo.ts", "../../src/controls/ControlBar.ts", "../../src/controls/KeyboardManager.ts", "../../src/utils/UrlSafe.ts", "../../src/core/LazyInit.ts", "../../src/core/PseudoFullscreen.ts", "../../src/core/ThemeManager.ts", "../../src/core/PosterManager.ts", "../../src/core/ResumeManager.ts", "../../src/core/ResponsiveManager.ts", "../../src/core/MetadataAlertsManager.ts", "../../src/core/Player.ts", "../../src/features/PlaylistManager.ts", "../../src/index.ts"],
4
- "sourcesContent": ["type DefaultEventMap = Record<string, unknown>;\ntype Listener<T> = T extends void ? () => void : (data: T) => void;\n\n/**\n * Generic typed event emitter. The `TEvents` parameter is constrained\n * via the self-referential `Record<keyof TEvents, unknown>` so concrete\n * event maps (like `PlayerEventMap`) can be used directly without\n * adding a noisy `[key: string]: unknown` index signature, which would\n * defeat the typo-protection that strongly typed event maps provide.\n */\nexport class EventEmitter<TEvents extends Record<keyof TEvents, unknown> = DefaultEventMap> {\n private events: Partial<Record<keyof TEvents, Array<(...args: unknown[]) => void>>> = {};\n\n on<K extends keyof TEvents>(event: K, listener: Listener<TEvents[K]>): this {\n const listeners = this.events[event] ?? [];\n listeners.push(listener as (...args: unknown[]) => void);\n this.events[event] = listeners;\n return this;\n }\n\n once<K extends keyof TEvents>(event: K, listener: Listener<TEvents[K]>): this {\n const onceListener = ((...args: unknown[]) => {\n (listener as (...a: unknown[]) => void)(...args);\n this.off(event, onceListener as Listener<TEvents[K]>);\n }) as unknown as Listener<TEvents[K]>;\n return this.on(event, onceListener);\n }\n\n off<K extends keyof TEvents>(event: K, listener?: Listener<TEvents[K]>): this {\n const listeners = this.events[event];\n if (!listeners) return this;\n\n if (!listener) {\n delete this.events[event];\n } else {\n this.events[event] = listeners.filter(\n l => l !== (listener as unknown)\n );\n }\n\n return this;\n }\n\n emit<K extends keyof TEvents>(event: K, ...args: unknown[]): this {\n const listeners = this.events[event];\n if (!listeners) return this;\n\n listeners.forEach(listener => {\n listener(...args);\n });\n\n return this;\n }\n\n removeAllListeners(): this {\n this.events = {};\n return this;\n }\n}\n", "export interface CaptureFrameOptions {\n restoreState?: boolean;\n quality?: number;\n maxWidth?: number;\n maxHeight?: number;\n}\n\nexport async function captureVideoFrame(\n video: HTMLVideoElement | null,\n time: number,\n options: CaptureFrameOptions = {}\n): Promise<string | null> {\n if (!video || video.tagName !== 'VIDEO') {\n return null;\n }\n\n const { restoreState = true, quality = 0.9, maxWidth, maxHeight } = options;\n\n const wasPlaying = !video.paused;\n const originalTime = video.currentTime;\n const originalMuted = video.muted;\n\n if (restoreState) {\n video.muted = true;\n }\n\n return new Promise<string | null>((resolve) => {\n const captureFrame = () => {\n try {\n let width = video.videoWidth || 640;\n let height = video.videoHeight || 360;\n\n if (maxWidth && width > maxWidth) {\n const ratio = maxWidth / width;\n width = maxWidth;\n height = Math.round(height * ratio);\n }\n if (maxHeight && height > maxHeight) {\n const ratio = maxHeight / height;\n height = maxHeight;\n width = Math.round(width * ratio);\n }\n\n const canvas = document.createElement('canvas');\n canvas.width = width;\n canvas.height = height;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n resolve(null);\n return;\n }\n ctx.drawImage(video, 0, 0, width, height);\n\n const dataURL = canvas.toDataURL('image/jpeg', quality);\n\n if (restoreState) {\n video.currentTime = originalTime;\n video.muted = originalMuted;\n if (wasPlaying && !video.paused) {\n // Autoplay can be rejected by browser policy. We log at debug\n // level so the noise doesn't reach production consoles, but\n // unexpected errors are still observable.\n video.play().catch((e: unknown) => {\n if (typeof console !== 'undefined' && console.debug) {\n console.debug('[VidPly] preview play() rejected:', e);\n }\n });\n }\n }\n\n resolve(dataURL);\n } catch (e) {\n if (typeof console !== 'undefined' && console.debug) {\n console.debug('[VidPly] frame capture failed:', e);\n }\n if (restoreState) {\n video.currentTime = originalTime;\n video.muted = originalMuted;\n if (wasPlaying && !video.paused) {\n video.play().catch((err: unknown) => {\n if (typeof console !== 'undefined' && console.debug) {\n console.debug('[VidPly] preview play() rejected:', err);\n }\n });\n }\n }\n resolve(null);\n }\n };\n\n const onSeeked = () => {\n video.removeEventListener('seeked', onSeeked);\n requestAnimationFrame(() => {\n requestAnimationFrame(captureFrame);\n });\n };\n\n const timeDiff = Math.abs(video.currentTime - time);\n if (timeDiff < 0.1 && video.readyState >= 2) {\n captureFrame();\n } else if (video.readyState >= 1) {\n video.addEventListener('seeked', onSeeked);\n video.currentTime = time;\n } else {\n const onLoadedMetadata = () => {\n video.removeEventListener('loadedmetadata', onLoadedMetadata);\n video.addEventListener('seeked', onSeeked);\n video.currentTime = time;\n };\n video.addEventListener('loadedmetadata', onLoadedMetadata);\n }\n });\n}\n", "/**\n * Download metadata helpers\n *\n * Used by the download button to resolve a human-readable file format\n * (e.g. MP4, MP3, WebM) and size string (e.g. 12.4 MB) for the button's\n * aria-label and tooltip.\n */\n\nconst MIME_TO_FORMAT: Record<string, string> = {\n 'video/mp4': 'MP4',\n 'video/webm': 'WebM',\n 'video/ogg': 'Ogg',\n 'video/quicktime': 'MOV',\n 'video/x-matroska': 'MKV',\n 'video/x-msvideo': 'AVI',\n 'audio/mpeg': 'MP3',\n 'audio/mp3': 'MP3',\n 'audio/mp4': 'M4A',\n 'audio/x-m4a': 'M4A',\n 'audio/aac': 'AAC',\n 'audio/ogg': 'Ogg',\n 'audio/opus': 'Opus',\n 'audio/wav': 'WAV',\n 'audio/x-wav': 'WAV',\n 'audio/wave': 'WAV',\n 'audio/flac': 'FLAC',\n 'audio/x-flac': 'FLAC',\n 'audio/webm': 'WebM'\n};\n\nconst EXT_TO_FORMAT: Record<string, string> = {\n mp4: 'MP4',\n m4v: 'MP4',\n mov: 'MOV',\n webm: 'WebM',\n mkv: 'MKV',\n avi: 'AVI',\n ogv: 'Ogg',\n ogg: 'Ogg',\n oga: 'Ogg',\n mp3: 'MP3',\n m4a: 'M4A',\n aac: 'AAC',\n opus: 'Opus',\n wav: 'WAV',\n flac: 'FLAC'\n};\n\nexport function inferFormatFromMime(mime: string | null | undefined): string | null {\n if (!mime) return null;\n const trimmed = (mime.split(';')[0] ?? '').trim().toLowerCase();\n return MIME_TO_FORMAT[trimmed] || null;\n}\n\nexport function inferFormatFromUrl(url: string | null | undefined): string | null {\n if (!url) return null;\n try {\n const cleaned = url.split('?')[0]?.split('#')[0] ?? '';\n const lastSegment = cleaned.split('/').pop() || '';\n const dotIndex = lastSegment.lastIndexOf('.');\n if (dotIndex < 0 || dotIndex === lastSegment.length - 1) return null;\n const ext = lastSegment.slice(dotIndex + 1).toLowerCase();\n return EXT_TO_FORMAT[ext] || null;\n } catch {\n return null;\n }\n}\n\nexport function formatBytes(bytes: number, locale: string = 'en'): string | null {\n if (!isFinite(bytes) || bytes < 0) return null;\n\n const units = ['B', 'KB', 'MB', 'GB', 'TB'];\n let value = bytes;\n let unitIndex = 0;\n while (value >= 1024 && unitIndex < units.length - 1) {\n value /= 1024;\n unitIndex++;\n }\n\n // Bytes/KB get no decimals; MB and up get one decimal.\n const fractionDigits = unitIndex < 2 ? 0 : 1;\n\n let formatted: string;\n try {\n formatted = new Intl.NumberFormat(locale, {\n minimumFractionDigits: fractionDigits,\n maximumFractionDigits: fractionDigits\n }).format(value);\n } catch {\n formatted = value.toFixed(fractionDigits);\n }\n\n return `${formatted} ${units[unitIndex]}`;\n}\n\nexport interface FetchContentLengthOptions {\n signal?: AbortSignal;\n timeoutMs?: number;\n}\n\nexport async function fetchContentLength(\n url: string,\n options: FetchContentLengthOptions = {}\n): Promise<number | null> {\n if (!url || typeof fetch !== 'function') return null;\n\n // Compose abort signals: caller's lifecycle signal + a per-call timeout.\n const signals: AbortSignal[] = [];\n if (options.signal) signals.push(options.signal);\n if (typeof AbortSignal !== 'undefined' && typeof AbortSignal.timeout === 'function') {\n signals.push(AbortSignal.timeout(options.timeoutMs ?? 8000));\n }\n let combinedSignal: AbortSignal | undefined;\n if (signals.length === 1) combinedSignal = signals[0];\n else if (signals.length > 1) {\n const anyFn = (AbortSignal as { any?: (s: AbortSignal[]) => AbortSignal }).any;\n combinedSignal = anyFn ? anyFn(signals) : signals[0];\n }\n\n try {\n const response = await fetch(url, {\n method: 'HEAD',\n credentials: 'omit',\n cache: 'no-store',\n signal: combinedSignal\n });\n if (!response.ok) return null;\n const header = response.headers.get('Content-Length');\n if (!header) return null;\n const size = Number(header);\n return Number.isFinite(size) && size > 0 ? size : null;\n } catch (error) {\n // CORS, network errors, abort, or HEAD not allowed: silently give up.\n if (typeof console !== 'undefined' && console.debug) {\n console.debug('[vidply] HEAD request for download size failed:', error);\n }\n return null;\n }\n}\n\nexport interface DownloadLabelParts {\n baseLabel: string;\n format?: string | null;\n sizeBytes?: number | null;\n locale?: string;\n withFormatSizeTemplate: string;\n withFormatTemplate: string;\n withSizeTemplate: string;\n}\n\n/**\n * Compose the localized download label.\n * Templates are i18n strings already containing {format} / {size} placeholders.\n */\nexport function buildDownloadLabel(parts: DownloadLabelParts): string {\n const { baseLabel, format, sizeBytes, locale = 'en' } = parts;\n const sizeStr = sizeBytes != null ? formatBytes(sizeBytes, locale) : null;\n\n if (format && sizeStr) {\n return parts.withFormatSizeTemplate\n .replace('{format}', format)\n .replace('{size}', sizeStr);\n }\n if (format) {\n return parts.withFormatTemplate.replace('{format}', format);\n }\n if (sizeStr) {\n return parts.withSizeTemplate.replace('{size}', sizeStr);\n }\n return baseLabel;\n}\n", "/**\n * Control Bar Component\n */\n\nimport {DOMUtils} from '../utils/DOMUtils.js';\nimport {TimeUtils} from '../utils/TimeUtils.js';\nimport {createIconElement} from '../icons/Icons.js';\nimport {i18n} from '../i18n/i18n.js';\nimport {focusElement} from '../utils/FocusUtils.js';\nimport {isMobile, reducedMotionScrollOptions} from '../utils/PerformanceUtils.js';\nimport {captureVideoFrame} from '../utils/VideoFrameCapture.js';\nimport {\n buildDownloadLabel,\n fetchContentLength,\n inferFormatFromMime,\n inferFormatFromUrl\n} from '../utils/DownloadInfo.js';\nimport type { Player } from '../core/Player.js';\nimport type { CaptionManager } from './CaptionManager.js';\nimport type { QualityLevel } from '../types/renderer.js';\n\ntype TimerHandle = ReturnType<typeof setTimeout>;\n\n/**\n * Stores per-button event handlers added by `attachMenuCloseHandler` so they\n * can be removed when a different menu opens. Using a WeakMap keeps the\n * tagging out of the DOM element itself (which both removes the previous\n * `(button as any)._vidplyXHandler` casts and lets the GC reclaim the entry\n * automatically once the button is detached).\n */\ntype MenuButtonHandlers = {\n blur?: (e: FocusEvent) => void;\n mousedown?: () => void;\n};\nconst menuButtonHandlers: WeakMap<HTMLElement, MenuButtonHandlers> = new WeakMap();\n\nfunction getMenuButtonHandlers(button: HTMLElement): MenuButtonHandlers {\n let entry = menuButtonHandlers.get(button);\n if (!entry) {\n entry = {};\n menuButtonHandlers.set(button, entry);\n }\n return entry;\n}\n\nexport class ControlBar {\n player: Player;\n _overflowMenuItemRef: HTMLElement | null = null;\n controls: Record<string, HTMLElement>;\n currentPreviewTime: number | null;\n element!: HTMLElement;\n hideTimeout: TimerHandle | undefined;\n isDraggingProgress: boolean;\n isDraggingVolume: boolean;\n openMenu: HTMLElement | null;\n openMenuButton: HTMLElement | null;\n overflowResizeObserver: ResizeObserver | null = null;\n previewSupported: boolean = false;\n previewThumbnailCache: Map<number, string> = new Map();\n previewThumbnailTimeout: TimerHandle | null = null;\n previewUsingMainVideo: boolean = false;\n previewVideo: HTMLVideoElement | null = null;\n previewVideoInitialized: boolean = false;\n previewVideoReady: boolean = false;\n rightButtons!: HTMLElement;\n overflowMenuButton: HTMLElement | null = null;\n /** Track of the currently open volume slider so a single pair of\n * document listeners (installed once in {@link init}) can update the\n * right element while dragging without being re-registered per open. */\n _activeVolumeTrack: HTMLElement | null = null;\n /** Track of the currently rendered progress bar so document-level\n * mousemove/mouseup handlers installed once in {@link init} can resolve\n * the geometry without re-registering per drag. */\n _progressBarRect: DOMRect | null = null;\n\n constructor(player: Player) {\n this.player = player;\n this.controls = {};\n this.hideTimeout = undefined;\n this.isDraggingProgress = false;\n this.isDraggingVolume = false;\n this.currentPreviewTime = null;\n this.openMenu = null; // Track currently open menu\n this.openMenuButton = null; // Track button that opened the menu\n\n this.init();\n }\n\n init() {\n this.createElement();\n this.createControls();\n // Ensure time UI reflects any prefilled state (e.g. initialDuration)\n // even when media metadata is deferred and 'loadedmetadata' won't fire yet.\n this.updateDuration();\n this.updateProgress();\n this.attachEvents();\n this.setupAutoHide();\n this.setupOverflowDetection();\n this.setupGlobalDragListeners();\n }\n\n /**\n * Install a single pair of document-level mousemove/mouseup handlers\n * that both the progress bar drag and the volume slider drag reuse.\n *\n * This replaces the previous pattern where {@link showVolumeSlider}\n * and {@link setupProgressBarEvents} each attached their own\n * `document.addEventListener` calls on every call — the volume variant\n * in particular accumulated two extra document listeners on every menu\n * open for the life of the page. All listeners here are tied to the\n * Player's lifecycle AbortController so `destroy()` removes them.\n */\n setupGlobalDragListeners() {\n const signal = this.player.lifecycleSignal;\n\n document.addEventListener('mousemove', (e: MouseEvent) => {\n if (this.isDraggingProgress && this._progressBarRect) {\n const rect = this._progressBarRect;\n const percent = rect.width > 0\n ? Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width))\n : 0;\n const duration = this.player.state.duration || 0;\n this.player.seek(percent * duration);\n return;\n }\n if (this.isDraggingVolume && this._activeVolumeTrack) {\n const rect = this._activeVolumeTrack.getBoundingClientRect();\n const percent = Math.max(0, Math.min(1, 1 - ((e.clientY - rect.top) / rect.height)));\n this.player.setVolume(percent);\n }\n }, { signal });\n\n document.addEventListener('mouseup', () => {\n this.isDraggingProgress = false;\n this._progressBarRect = null;\n this.isDraggingVolume = false;\n this._activeVolumeTrack = null;\n }, { signal });\n }\n\n // Helper method to detect touch devices\n isTouchDevice() {\n return (\n ('ontouchstart' in window) ||\n (navigator.maxTouchPoints > 0) ||\n ((navigator.msMaxTouchPoints ?? 0) > 0)\n );\n }\n\n // Smart menu positioning to avoid overflow\n positionMenu(menu: HTMLElement, button: HTMLElement, immediate = false) {\n const mobile = isMobile();\n const isOverflowMenu = menu.classList.contains(`${this.player.options.classPrefix}-overflow-menu-list`);\n const isFullscreen = this.player.state.fullscreen;\n \n // In fullscreen, menu is appended to player container, so use fixed positioning\n if (isFullscreen && menu.parentElement === this.player.container) {\n const doFullscreenPositioning = () => {\n const buttonRect = button.getBoundingClientRect();\n const menuRect = menu.getBoundingClientRect();\n const containerRect = this.player.container.getBoundingClientRect();\n \n // Position relative to player container\n const buttonCenterX = buttonRect.left + buttonRect.width / 2 - containerRect.left;\n const buttonTop = buttonRect.top - containerRect.top;\n const buttonBottom = buttonRect.bottom - containerRect.top;\n \n const spaceAbove = buttonRect.top - containerRect.top;\n const spaceBelow = containerRect.bottom - buttonRect.bottom;\n \n // Position menu above button by default\n if (spaceAbove >= menuRect.height + 20 || spaceAbove > spaceBelow) {\n menu.style.bottom = `${containerRect.height - buttonTop + 8}px`;\n menu.style.top = 'auto';\n menu.classList.remove('vidply-menu-below');\n } else {\n // Position below if more space there\n menu.style.top = `${buttonBottom + 8}px`;\n menu.style.bottom = 'auto';\n menu.classList.add('vidply-menu-below');\n }\n \n // Horizontal positioning\n if (isOverflowMenu) {\n // Overflow menu aligns to right\n const buttonRight = buttonRect.right - containerRect.left;\n menu.style.right = `${containerRect.width - buttonRight}px`;\n menu.style.left = 'auto';\n menu.style.transform = 'none';\n } else {\n // Other menus center on button\n menu.style.left = `${buttonCenterX}px`;\n menu.style.right = 'auto';\n menu.style.transform = 'translateX(-50%)';\n }\n };\n \n if (immediate) {\n doFullscreenPositioning();\n } else {\n requestAnimationFrame(doFullscreenPositioning);\n }\n return;\n }\n \n if (mobile) {\n // On mobile, ensure menus stay within viewport\n const isVolumeMenu = menu.classList.contains(`${this.player.options.classPrefix}-volume-menu`);\n \n const doMobilePositioning = () => {\n // Get the button's parent container (controls-left or controls-right)\n const parentContainer = button.parentElement;\n if (!parentContainer) return;\n \n const buttonRect = button.getBoundingClientRect();\n const parentRect = parentContainer.getBoundingClientRect();\n const menuRect = menu.getBoundingClientRect();\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n \n // Volume menu should be centered on its button\n if (isVolumeMenu) {\n // Calculate button position relative to parent\n const buttonCenterX = buttonRect.left + buttonRect.width / 2 - parentRect.left;\n \n // Center menu on button\n menu.style.left = `${buttonCenterX}px`;\n menu.style.right = 'auto';\n menu.style.transform = 'translateX(-50%)';\n return;\n }\n \n // Check if menu overflows viewport\n if (menuRect.right > viewportWidth) {\n menu.style.left = 'auto';\n menu.style.right = '10px';\n menu.style.transform = 'none';\n }\n \n if (menuRect.left < 0) {\n menu.style.left = '10px';\n menu.style.right = 'auto';\n menu.style.transform = 'none';\n }\n \n // Ensure menu doesn't go off top or bottom\n if (menuRect.top < 10) {\n menu.style.top = '10px';\n // Important: clear bottom constraint, otherwise top+bottom will squeeze the menu height\n // into a tiny strip on some mobile layouts.\n menu.style.bottom = 'auto';\n }\n \n if (menuRect.bottom > viewportHeight - 10) {\n menu.style.bottom = '10px';\n menu.style.top = 'auto';\n }\n };\n \n if (immediate) {\n doMobilePositioning();\n } else {\n requestAnimationFrame(doMobilePositioning);\n }\n return;\n }\n\n // Desktop: Smart positioning\n // Menu is now a sibling of the button, within controls-left or controls-right container\n // These containers have position: relative, so menus position relative to them\n \n const doPositioning = () => {\n const buttonRect = button.getBoundingClientRect();\n const menuRect = menu.getBoundingClientRect();\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n \n // Get the button's parent container (controls-left or controls-right)\n const parentContainer = button.parentElement;\n if (!parentContainer) return;\n \n const parentRect = parentContainer.getBoundingClientRect();\n \n // Calculate position relative to parent container\n const buttonCenterX = buttonRect.left + buttonRect.width / 2 - parentRect.left;\n const buttonBottom = buttonRect.bottom - parentRect.top;\n const buttonTop = buttonRect.top - parentRect.top;\n \n const spaceAbove = buttonRect.top;\n const spaceBelow = viewportHeight - buttonRect.bottom;\n \n // Position menu above button by default\n let menuTop: number | null = buttonTop - menuRect.height - 8;\n let menuBottom: number | null = null;\n \n // Prefer above, but switch to below if not enough space\n if (spaceAbove < menuRect.height + 20 && spaceBelow > spaceAbove) {\n menuTop = null;\n // Calculate bottom position relative to parent container\n // bottom: X means X pixels from the bottom of the positioned parent\n // We want the menu to be 8px below the button\n const parentHeight = parentRect.bottom - parentRect.top;\n menuBottom = parentHeight - buttonBottom + 8;\n menu.classList.add('vidply-menu-below');\n } else {\n menu.classList.remove('vidply-menu-below');\n }\n \n let menuLeft: number | string = 'auto';\n let menuRight: number | string = 'auto';\n let transformX = 'translateX(0)';\n \n // For overflow menu, always align to the right edge\n if (isOverflowMenu) {\n menuLeft = 'auto';\n menuRight = 0;\n transformX = 'translateX(0)';\n } else {\n // For other menus, center on button by default\n menuLeft = buttonCenterX - menuRect.width / 2;\n \n // Check horizontal overflow\n const menuLeftAbsolute = buttonRect.left + buttonRect.width / 2 - menuRect.width / 2;\n if (menuLeftAbsolute < 10) {\n // Too far left, align to left edge of parent\n menuLeft = 0;\n transformX = 'translateX(0)';\n } else if (menuLeftAbsolute + menuRect.width > viewportWidth - 10) {\n // Too far right, align to right edge of parent\n menuLeft = 'auto';\n menuRight = 0;\n transformX = 'translateX(0)';\n } else {\n // Center on button\n menuLeft = buttonCenterX;\n transformX = 'translateX(-50%)';\n }\n }\n \n // Apply calculated positions\n if (menuTop !== null) {\n menu.style.top = `${menuTop}px`;\n menu.style.bottom = 'auto';\n } else if (menuBottom !== null) {\n menu.style.top = 'auto';\n menu.style.bottom = `${menuBottom}px`;\n }\n \n if (menuLeft !== 'auto') {\n menu.style.left = `${menuLeft}px`;\n menu.style.right = 'auto';\n } else {\n menu.style.left = 'auto';\n menu.style.right = `${menuRight}px`;\n }\n \n menu.style.transform = transformX;\n };\n \n if (immediate) {\n // Position immediately (synchronously) - used when menu is first shown\n doPositioning();\n } else {\n // Use requestAnimationFrame to ensure layout is stable before positioning\n requestAnimationFrame(() => {\n setTimeout(doPositioning, 10); // Small delay to ensure layout is stable\n });\n }\n }\n\n // Helper method to insert menu into DOM (handles fullscreen vs normal mode)\n insertMenuIntoDOM(menu: HTMLElement, button: HTMLElement) {\n // Ensure menu has an ID for aria-controls relationship\n if (!menu.id) {\n menu.id = `vidply-menu-${Math.random().toString(36).substr(2, 9)}`;\n }\n \n // Set ARIA attributes for screen reader users (WCAG 4.1.2)\n button.setAttribute('aria-controls', menu.id);\n button.setAttribute('aria-haspopup', 'true');\n \n // In fullscreen, append menu to player container to escape video-wrapper stacking context\n // This allows menus to appear above the playlist panel\n // Otherwise, keep it after the button for WCAG compliance\n const isFullscreen = this.player.state.fullscreen;\n if (isFullscreen) {\n // Append to player container as sibling to playlist panel\n this.player.container.appendChild(menu);\n // Store reference to button for positioning\n menu.dataset.triggerButton = button.getAttribute('aria-label') || 'button';\n } else {\n // Insert menu right after the button in the DOM for keyboard navigation\n button.insertAdjacentElement('afterend', menu);\n }\n }\n\n // Helper method to attach close-on-outside-click behavior to menus\n attachMenuCloseHandler(menu: HTMLElement, button: HTMLElement, preventCloseOnInteraction = false) {\n // Close any previously open menu and clean up its handlers\n if (this.openMenu && this.openMenu !== menu && this.openMenuButton) {\n // Remove previously-attached handlers for this button if any.\n const previousHandlers = menuButtonHandlers.get(this.openMenuButton);\n if (previousHandlers?.blur) {\n this.openMenuButton.removeEventListener('blur', previousHandlers.blur);\n delete previousHandlers.blur;\n }\n if (previousHandlers?.mousedown) {\n this.openMenuButton.removeEventListener('mousedown', previousHandlers.mousedown);\n delete previousHandlers.mousedown;\n }\n \n // Close previous menu without returning focus\n if (this.openMenu && document.contains(this.openMenu)) {\n this.openMenu.remove();\n } else if (this.openMenu && this.openMenu.parentNode) {\n this.openMenu.parentNode.removeChild(this.openMenu);\n }\n if (this.openMenuButton) {\n this.openMenuButton.setAttribute('aria-expanded', 'false');\n }\n }\n \n // Track this menu as open\n this.openMenu = menu;\n this.openMenuButton = button;\n \n // Position menu smartly\n this.positionMenu(menu, button);\n \n // Set aria-expanded to true when menu opens\n if (button) {\n button.setAttribute('aria-expanded', 'true');\n }\n \n // Add focus handler to button to close menu when focus leaves to another control bar button\n // But only if it's a keyboard navigation (Tab), not a click\n let isClickingButton = false;\n let blurHandlerActive = true;\n \n const handleButtonMousedown = () => {\n isClickingButton = true;\n // Temporarily disable blur handler when clicking\n blurHandlerActive = false;\n setTimeout(() => {\n isClickingButton = false;\n blurHandlerActive = true;\n }, 200);\n };\n button.addEventListener('mousedown', handleButtonMousedown);\n getMenuButtonHandlers(button).mousedown = handleButtonMousedown;\n \n const handleButtonBlur = (e: FocusEvent) => {\n // If blur handler is disabled (during click) or this is a click, don't close the menu\n if (!blurHandlerActive || isClickingButton) {\n return;\n }\n \n // Check if this menu is still the open menu (might have been replaced)\n if (this.openMenu !== menu) {\n return;\n }\n \n // Store the related target (where focus is going) before async operations\n const relatedTarget = e.relatedTarget;\n \n // Use requestAnimationFrame to check where focus went after browser handles focus\n requestAnimationFrame(() => {\n setTimeout(() => {\n // Check again if blur handler is still active and menu is still open\n if (!blurHandlerActive || this.openMenu !== menu) {\n return;\n }\n \n const activeElement = document.activeElement;\n \n // Don't close if focus is still within the menu\n if (menu.contains(activeElement)) {\n return;\n }\n \n // Don't close if focus moved to sign language or transcript windows\n const signLanguageWrapper = this.player.signLanguageWrapper;\n const transcriptWindow = this.player.transcriptManager?.transcriptWindow;\n if ((signLanguageWrapper && signLanguageWrapper.contains(activeElement)) ||\n (transcriptWindow && transcriptWindow.contains(activeElement))) {\n return;\n }\n \n // If focus moved to another button in the control bar, close the menu\n const controlBarButtons = Array.from(this.element.querySelectorAll('button')) as Element[];\n const isFocusOnAnotherButton = activeElement !== null\n && controlBarButtons.includes(activeElement)\n && activeElement !== button;\n\n // Also check relatedTarget in case activeElement hasn't updated yet\n const isRelatedTargetAnotherButton = relatedTarget !== null\n && relatedTarget instanceof Element\n && controlBarButtons.includes(relatedTarget)\n && relatedTarget !== button;\n \n // Close menu if focus moved to another control bar button (without returning focus)\n if (isFocusOnAnotherButton || isRelatedTargetAnotherButton) {\n // Double-check this menu is still the open menu\n if (this.openMenu !== menu) {\n return;\n }\n \n // Just remove the menu, don't touch focus at all\n if (menu && document.contains(menu)) {\n menu.remove();\n } else if (menu && menu.parentNode) {\n menu.parentNode.removeChild(menu);\n }\n if (button) {\n button.setAttribute('aria-expanded', 'false');\n }\n // Clear tracking\n if (this.openMenu === menu) {\n this.openMenu = null;\n this.openMenuButton = null;\n }\n button.removeEventListener('blur', handleButtonBlur);\n button.removeEventListener('mousedown', handleButtonMousedown);\n const handlers = menuButtonHandlers.get(button);\n if (handlers) {\n delete handlers.blur;\n delete handlers.mousedown;\n }\n }\n }, 10); // Small delay to ensure focus has fully moved\n });\n };\n button.addEventListener('blur', handleButtonBlur);\n getMenuButtonHandlers(button).blur = handleButtonBlur;\n \n const closeMenuAndUpdateAria = () => {\n this.closeMenuAndReturnFocus(menu, button);\n };\n \n // Store document handlers for cleanup\n let documentClickHandler: ((e: MouseEvent) => void) | null = null;\n let documentEscapeHandler: ((e: KeyboardEvent) => void) | null = null;\n \n setTimeout(() => {\n documentClickHandler = (e: MouseEvent) => {\n // If this menu has form controls, don't close when clicking inside\n if (preventCloseOnInteraction && menu.contains(e.target as Node)) {\n return;\n }\n\n // Check if click is outside menu and button\n // Also check if this menu is still the open menu\n if (this.openMenu === menu && !menu.contains(e.target as Node) && !button.contains(e.target as Node)) {\n closeMenuAndUpdateAria();\n if (documentClickHandler) {\n document.removeEventListener('click', documentClickHandler);\n }\n if (documentEscapeHandler) {\n document.removeEventListener('keydown', documentEscapeHandler);\n }\n }\n };\n\n documentEscapeHandler = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && this.openMenu === menu) {\n e.preventDefault();\n e.stopPropagation();\n // Close menu and return focus to the button that opened it\n this.closeMenuAndReturnFocus(menu, button, true);\n if (documentClickHandler) {\n document.removeEventListener('click', documentClickHandler);\n }\n if (documentEscapeHandler) {\n document.removeEventListener('keydown', documentEscapeHandler);\n }\n }\n };\n\n const signal = this.player.lifecycleSignal;\n document.addEventListener('click', documentClickHandler, { signal });\n document.addEventListener('keydown', documentEscapeHandler, { signal });\n }, 100);\n }\n\n // Helper method to close menu and return focus to button\n closeMenuAndReturnFocus(menu: HTMLElement | null, button: HTMLElement | null, returnFocus = true) {\n if (menu) {\n // Remove menu from DOM - use remove() which works reliably\n if (document.contains(menu)) {\n menu.remove();\n } else if (menu.parentNode) {\n // Fallback if menu is not in document but has parent\n menu.parentNode.removeChild(menu);\n }\n }\n if (button) {\n button.setAttribute('aria-expanded', 'false');\n // Clean up aria-controls since menu is removed (optional, but cleaner)\n // Note: Keeping it doesn't cause issues, but removing it is semantically correct\n // when the referenced element no longer exists\n if (menu && menu.id) {\n button.removeAttribute('aria-controls');\n }\n // Only return focus if explicitly requested (not when tabbing away)\n if (returnFocus) {\n // Use requestAnimationFrame to ensure DOM updates are complete before focusing\n // This prevents focus from jumping to next/previous button\n requestAnimationFrame(() => {\n setTimeout(() => {\n if (button && document.contains(button)) {\n button.focus({ preventScroll: true });\n }\n }, 0);\n });\n }\n }\n // Clear tracking\n if (this.openMenu === menu) {\n this.openMenu = null;\n this.openMenuButton = null;\n }\n }\n \n // Close any open menu when tabbing to another button or clicking another button\n closeOpenMenu() {\n if (this.openMenu && this.openMenuButton) {\n // Close without returning focus (user is tabbing to next element or clicking another button)\n if (this.openMenu && document.contains(this.openMenu)) {\n this.openMenu.remove();\n } else if (this.openMenu && this.openMenu.parentNode) {\n this.openMenu.parentNode.removeChild(this.openMenu);\n }\n if (this.openMenuButton) {\n this.openMenuButton.setAttribute('aria-expanded', 'false');\n }\n // Clear tracking\n this.openMenu = null;\n this.openMenuButton = null;\n }\n }\n\n // Helper method to add keyboard navigation to menus (arrow keys)\n attachMenuKeyboardNavigation(menu: HTMLElement, button: HTMLElement) {\n // Exclude aria-disabled empty-state entries so arrow keys never land\n // on a non-operable item (WCAG 2.1.1, 4.1.2).\n const menuItems: HTMLElement[] = Array.from(\n menu.querySelectorAll<HTMLElement>(`.${this.player.options.classPrefix}-menu-item`)\n ).filter((item) => item.getAttribute('aria-disabled') !== 'true');\n \n if (menuItems.length === 0) return;\n\n const handleKeyDown = (e: KeyboardEvent) => {\n const currentIndex = menuItems.indexOf(document.activeElement as HTMLElement);\n \n switch (e.key) {\n case 'ArrowDown': {\n e.preventDefault();\n e.stopPropagation(); // Prevent volume/seek actions\n const nextIndex = (currentIndex + 1) % menuItems.length;\n const nextItem = menuItems[nextIndex];\n if (nextItem) {\n nextItem.focus({ preventScroll: false });\n nextItem.scrollIntoView(reducedMotionScrollOptions('nearest'));\n }\n break;\n }\n \n case 'ArrowUp': {\n e.preventDefault();\n e.stopPropagation(); // Prevent volume/seek actions\n const prevIndex = (currentIndex - 1 + menuItems.length) % menuItems.length;\n const prevItem = menuItems[prevIndex];\n if (prevItem) {\n prevItem.focus({ preventScroll: false });\n prevItem.scrollIntoView(reducedMotionScrollOptions('nearest'));\n }\n break;\n }\n \n case 'ArrowLeft':\n case 'ArrowRight':\n // Prevent seeking when in menu (but allow menu to handle if needed)\n e.preventDefault();\n e.stopPropagation();\n break;\n \n case 'Home': {\n e.preventDefault();\n e.stopPropagation();\n const homeItem = menuItems[0];\n if (homeItem) {\n homeItem.focus({ preventScroll: false });\n homeItem.scrollIntoView(reducedMotionScrollOptions('nearest'));\n }\n break;\n }\n \n case 'End': {\n e.preventDefault();\n e.stopPropagation();\n const endItem = menuItems[menuItems.length - 1];\n if (endItem) {\n endItem.focus({ preventScroll: false });\n endItem.scrollIntoView(reducedMotionScrollOptions('nearest'));\n }\n break;\n }\n \n case 'Enter':\n case ' ':\n e.preventDefault();\n e.stopPropagation(); // Prevent event from reaching KeyboardManager\n if (document.activeElement && menuItems.includes(document.activeElement as HTMLElement)) {\n (document.activeElement as HTMLElement).click();\n // Menu will be closed by the click handler, but ensure focus returns\n focusElement(button, { delay: 0 });\n }\n break;\n \n case 'Escape':\n e.preventDefault();\n e.stopPropagation(); // Prevent event from reaching KeyboardManager\n // Close menu and return focus to the button that opened it\n this.closeMenuAndReturnFocus(menu, button, true);\n break;\n }\n };\n\n menu.addEventListener('keydown', handleKeyDown);\n }\n\n createElement() {\n this.element = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-controls`,\n attributes: {\n 'role': 'region',\n 'aria-label': i18n.t('player.label') + ' controls'\n }\n });\n }\n\n createControls() {\n // Progress bar and time display wrapper\n const progressTimeWrapper = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-progress-time-wrapper`\n });\n\n // Progress bar container\n if (this.player.options.progressBar) {\n this.createProgressBar();\n if (this.controls.progress) {\n progressTimeWrapper.appendChild(this.controls.progress);\n }\n }\n\n // Time display (right beside progress bar)\n if (this.player.options.currentTime || this.player.options.duration) {\n progressTimeWrapper.appendChild(this.createTimeDisplay());\n }\n\n this.element.appendChild(progressTimeWrapper);\n\n // Button container\n const buttonContainer = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-controls-buttons`\n });\n\n // Left buttons\n const leftButtons = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-controls-left`\n });\n\n // Previous track button (if playlist)\n if (this.player.playlistManager) {\n leftButtons.appendChild(this.createPreviousButton());\n }\n\n // Play/Pause button\n if (this.player.options.playPauseButton) {\n leftButtons.appendChild(this.createPlayPauseButton());\n }\n\n // Restart button (right beside play button)\n leftButtons.appendChild(this.createRestartButton());\n\n // Next track button (if playlist)\n if (this.player.playlistManager) {\n leftButtons.appendChild(this.createNextButton());\n }\n\n // Rewind button (not shown in playlist mode)\n if (!this.player.playlistManager) {\n leftButtons.appendChild(this.createRewindButton());\n }\n\n // Forward button (not shown in playlist mode)\n if (!this.player.playlistManager) {\n leftButtons.appendChild(this.createForwardButton());\n }\n\n // Volume control\n if (this.player.options.volumeControl) {\n // On touch devices: simple mute button (hardware buttons control volume)\n // On desktop: full volume control with slider\n if (this.isTouchDevice()) {\n leftButtons.appendChild(this.createMuteButton());\n } else {\n leftButtons.appendChild(this.createVolumeControl());\n }\n }\n\n // Right buttons\n this.rightButtons = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-controls-right`\n });\n\n // Check for available features\n const hasChapters = this.hasChapterTracks();\n const hasCaptions = this.hasCaptionTracks();\n const hasQualityLevels = this.hasQualityLevels();\n const hasAudioDescription = this.hasAudioDescription();\n\n // Priority order (lower number = higher priority, stays visible longer)\n // Desktop (>768px):\n // Priority 1: Play, Volume, Captions, Speed, Fullscreen\n // Priority 2: Audio Description, Quality \n // Priority 3: Chapters, Caption Style, Transcript, Sign Language, PiP\n // Smaller screens (<768px):\n // Priority 1: Play, Volume, Progress (only left controls visible)\n // Priority 3: ALL right-side buttons go to overflow menu\n\n // 1. Chapters button\n if (this.player.options.chaptersButton && hasChapters) {\n const btn = this.createChaptersButton();\n btn.dataset.overflowPriority = '3';\n btn.dataset.overflowPriorityMobile = '3';\n this.rightButtons.appendChild(btn);\n }\n\n // 2. Captions button\n if (this.player.options.captionsButton && hasCaptions) {\n const btn = this.createCaptionsButton();\n btn.dataset.overflowPriority = '1';\n btn.dataset.overflowPriorityMobile = '3';\n this.rightButtons.appendChild(btn);\n }\n\n // 3. Caption styling button\n if (this.player.options.captionStyleButton && hasCaptions) {\n const btn = this.createCaptionStyleButton();\n btn.dataset.overflowPriority = '3';\n btn.dataset.overflowPriorityMobile = '3';\n this.rightButtons.appendChild(btn);\n }\n\n // 4. Playback speed button\n // IMPORTANT: Don't rely on renderer.constructor.name here.\n // In production builds, class names are minified (e.g. \"class s\"), which would break the check.\n // Instead, detect HLS by the current source URL.\n const src = this.player.currentSource\n || this.player.element?.getAttribute?.('src')\n || this.player.element?.currentSrc\n || this.player.element?.src\n || this.player.element?.querySelector?.('source')?.getAttribute?.('src')\n || this.player.element?.querySelector?.('source')?.src\n || '';\n const isHlsSource = typeof src === 'string' && src.includes('.m3u8');\n const isDashSource = typeof src === 'string' && src.includes('.mpd');\n const isVideoElement = this.player.element?.tagName?.toLowerCase() === 'video';\n const hideSpeedForThisPlayer =\n (Boolean(this.player.options.hideSpeedForHls) && isHlsSource)\n || (Boolean(this.player.options.hideSpeedForHlsVideo) && isHlsSource && isVideoElement)\n || (Boolean(this.player.options.hideSpeedForDash) && isDashSource)\n || (Boolean(this.player.options.hideSpeedForDashVideo) && isDashSource && isVideoElement);\n if (this.player.options.speedButton && !hideSpeedForThisPlayer) {\n const btn = this.createSpeedButton();\n btn.dataset.overflowPriority = '1';\n btn.dataset.overflowPriorityMobile = '3';\n this.rightButtons.appendChild(btn);\n }\n\n // 5. Audio Description button\n if (this.player.options.audioDescriptionButton && hasAudioDescription) {\n const btn = this.createAudioDescriptionButton();\n btn.dataset.overflowPriority = '2';\n btn.dataset.overflowPriorityMobile = '3';\n this.rightButtons.appendChild(btn);\n }\n\n // 6. Transcript button\n if (this.player.options.transcriptButton && hasCaptions) {\n const btn = this.createTranscriptButton();\n btn.dataset.overflowPriority = '3';\n btn.dataset.overflowPriorityMobile = '3';\n this.rightButtons.appendChild(btn);\n }\n\n // 6.5 Playlist toggle button (for playlists)\n if (this.player.playlistManager && this.player.options.playlistToggleButton !== false) {\n const btn = this.createPlaylistToggleButton();\n btn.dataset.overflowPriority = '2';\n btn.dataset.overflowPriorityMobile = '3';\n this.rightButtons.appendChild(btn);\n }\n\n // 7. Sign Language buttons (PiP overlay and/or main view src swap)\n const hasSignLanguage = this.hasSignLanguage();\n const showSignLanguageButtons = this.player.options.signLanguageButton !== false && hasSignLanguage;\n const signLanguageDisplayMode = this.player.options.signLanguageDisplayMode || 'both';\n if (showSignLanguageButtons) {\n // PiP overlay button (show if mode is 'pip' or 'both')\n if (['pip', 'both'].includes(signLanguageDisplayMode)) {\n const pipBtn = this.createSignLanguageButton();\n pipBtn.dataset.overflowPriority = '3';\n pipBtn.dataset.overflowPriorityMobile = '3';\n this.rightButtons.appendChild(pipBtn);\n }\n \n // Main view button (show if mode is 'main' or 'both')\n if (['main', 'both'].includes(signLanguageDisplayMode)) {\n const mainViewBtn = this.createSignLanguageInMainViewButton();\n mainViewBtn.dataset.overflowPriority = '3';\n mainViewBtn.dataset.overflowPriorityMobile = '3';\n this.rightButtons.appendChild(mainViewBtn);\n }\n }\n\n // Quality button (before fullscreen)\n if (this.player.options.qualityButton && hasQualityLevels) {\n const btn = this.createQualityButton();\n btn.dataset.overflowPriority = '2';\n btn.dataset.overflowPriorityMobile = '3';\n this.rightButtons.appendChild(btn);\n }\n\n // Download button\n if (this.player.options.downloadButton) {\n const downloadUrl = this.player.options.downloadUrl\n || this.player.element?.dataset?.vidplyDownloadUrl;\n if (downloadUrl) {\n const btn = this.createDownloadButton(downloadUrl);\n btn.dataset.overflowPriority = '2';\n btn.dataset.overflowPriorityMobile = '3';\n this.rightButtons.appendChild(btn);\n }\n }\n\n // PiP button (before fullscreen)\n // When options.floating is enabled, the same button drives our custom\n // in-page floating player; the native PiP capability check is skipped\n // because requestPictureInPicture is not used in that mode.\n const pipEnabled = this.player.options.pipButton &&\n (this.player.options.floating || 'pictureInPictureEnabled' in document);\n const isAudio = this.player.element.tagName.toLowerCase() === 'audio';\n if (pipEnabled && !(this.player.options.floating && isAudio)) {\n const btn = this.createPipButton();\n if (this.player.options.floating) {\n // The floating feature has a hard viewport minimum (default\n // 768px). Below that, the feature is disabled by the\n // FloatingPlayerManager, so the button must neither sit in\n // the main control bar (hidden via CSS media query) nor be\n // shuffled into the overflow menu. Mark it so the overflow\n // detector leaves it alone, regardless of the screen size.\n btn.dataset.skipOverflow = 'true';\n btn.dataset.overflowPriority = '1';\n btn.dataset.overflowPriorityMobile = '1';\n } else {\n btn.dataset.overflowPriority = '3';\n btn.dataset.overflowPriorityMobile = '3';\n }\n this.rightButtons.appendChild(btn);\n }\n\n // Create overflow menu button (initially hidden). Added before the\n // fullscreen button so that, on mobile viewports where the overflow\n // menu is visible, fullscreen sits immediately to its right as the\n // last control in the bar.\n this.overflowMenuButton = this.createOverflowMenuButton();\n this.overflowMenuButton.style.display = 'none';\n this.rightButtons.appendChild(this.overflowMenuButton);\n\n // 8. Fullscreen button (very last)\n // Don't show fullscreen button for audio players. Fullscreen is a\n // core action users reach for often, so it stays in the main bar\n // on every viewport (priority 1 on desktop AND mobile) rather than\n // being shuffled into the overflow menu.\n const isAudioPlayer = this.player.element.tagName.toLowerCase() === 'audio';\n if (this.player.options.fullscreenButton && !isAudioPlayer) {\n const btn = this.createFullscreenButton();\n btn.dataset.overflowPriority = '1';\n btn.dataset.overflowPriorityMobile = '1';\n this.rightButtons.appendChild(btn);\n }\n\n buttonContainer.appendChild(leftButtons);\n buttonContainer.appendChild(this.rightButtons);\n this.element.appendChild(buttonContainer);\n \n // Ensure all buttons have title attributes\n this.ensureButtonTooltips(buttonContainer);\n }\n \n /**\n * Ensure all buttons in the controls have title attributes\n * Uses aria-label as title if title is not present\n */\n ensureButtonTooltips(container: HTMLElement) {\n const buttons = container.querySelectorAll('button');\n buttons.forEach((button: HTMLButtonElement) => {\n // Skip if tooltip already exists\n if (button.querySelector(`.${this.player.options.classPrefix}-tooltip`)) {\n return;\n }\n \n // Skip if button text already exists\n if (button.querySelector(`.${this.player.options.classPrefix}-button-text`)) {\n return;\n }\n \n // Skip menu items - they already have visible text\n if (button.getAttribute('role') === 'menuitem' || \n button.classList.contains(`${this.player.options.classPrefix}-settings-item`) ||\n button.classList.contains(`${this.player.options.classPrefix}-menu-item`) ||\n button.classList.contains(`${this.player.options.classPrefix}-transcript-settings-item`) ||\n button.classList.contains(`${this.player.options.classPrefix}-sign-language-settings-item`) ||\n button.classList.contains(`${this.player.options.classPrefix}-popup-settings-item`)) {\n return;\n }\n \n const ariaLabel = button.getAttribute('aria-label');\n if (ariaLabel) {\n // Add tooltip (aria-hidden popover)\n DOMUtils.attachTooltip(button, ariaLabel, this.player.options.classPrefix);\n }\n });\n }\n\n // Helper methods to check for available features\n hasChapterTracks() {\n // 1) Prefer already-loaded TextTracks (fast + accurate)\n const textTracks = this.player.element.textTracks;\n for (let i = 0; i < textTracks.length; i++) {\n if (textTracks[i]?.kind === 'chapters') return true;\n }\n\n // 2) Fallback to DOM <track> elements (works before tracks are fully loaded)\n const trackEls = Array.from(this.player.element.querySelectorAll('track[kind=\"chapters\"]'));\n if (trackEls.length > 0) return true;\n\n // 3) Playlist metadata fallback (works even when we intentionally defer loading)\n const current = this.player.playlistManager?.getCurrentTrack?.();\n if (current?.tracks && Array.isArray(current.tracks)) {\n const tracks = current.tracks as Array<{ kind?: string }>;\n return tracks.some(t => t?.kind === 'chapters');\n }\n\n return false;\n }\n\n hasCaptionTracks() {\n // 1) Prefer already-loaded TextTracks (skip stale tracks from previous dash.js)\n const textTracks = this.player.element.textTracks;\n for (let i = 0; i < textTracks.length; i++) {\n const tt = textTracks[i];\n if (tt && (tt.kind === 'captions' || tt.kind === 'subtitles') && !tt._vidplyStale) {\n return true;\n }\n }\n\n // 2) Fallback to DOM <track> elements\n const trackEls: HTMLElement[] = Array.from(this.player.element.querySelectorAll('track'));\n if (trackEls.some(el => el.getAttribute('kind') === 'captions' || el.getAttribute('kind') === 'subtitles')) {\n return true;\n }\n\n // 3) Playlist metadata fallback\n const current = this.player.playlistManager?.getCurrentTrack?.();\n const playlistTracks = (current?.tracks ?? []) as Array<{ kind?: string }>;\n if (playlistTracks.some(t => t?.kind === 'captions' || t?.kind === 'subtitles')) {\n return true;\n }\n\n return false;\n }\n\n hasQualityLevels() {\n // Check if renderer supports quality selection\n if (this.player.renderer && this.player.renderer.getQualities) {\n const qualities = this.player.renderer.getQualities();\n return qualities && qualities.length > 1;\n }\n return false;\n }\n\n hasAudioDescription() {\n // Check for audio-described video source OR description tracks\n if (this.player.audioDescriptionSrc && this.player.audioDescriptionSrc.length > 0) {\n return true;\n }\n \n // Check for description tracks\n const textTracks: TextTrack[] = Array.from(this.player.element.textTracks || []);\n return textTracks.some(track => track.kind === 'descriptions');\n }\n\n hasSignLanguage() {\n // Check for single source or multiple sources\n const hasSingleSource = this.player.signLanguageSrc && this.player.signLanguageSrc.length > 0;\n const hasMultipleSources = this.player.signLanguageSources && Object.keys(this.player.signLanguageSources).length > 0;\n return hasSingleSource || hasMultipleSources;\n }\n\n createProgressBar() {\n const progressContainer = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-progress-container`,\n attributes: {\n 'role': 'slider',\n 'aria-label': i18n.t('player.progress'),\n 'aria-valuemin': '0',\n 'aria-valuemax': '100',\n 'aria-valuenow': '0',\n 'tabindex': '0'\n }\n });\n\n // Buffered progress\n this.controls.buffered = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-progress-buffered`\n });\n\n // Played progress\n this.controls.played = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-progress-played`\n });\n\n // Progress handle\n this.controls.progressHandle = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-progress-handle`\n });\n\n // Tooltip\n this.controls.progressTooltip = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-progress-tooltip`\n });\n\n // Preview thumbnail (for video only)\n this.controls.progressPreview = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-progress-preview`,\n attributes: {\n 'aria-hidden': 'true'\n }\n });\n this.controls.progressTooltip.appendChild(this.controls.progressPreview);\n\n // Time text\n this.controls.progressTooltipTime = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-progress-tooltip-time`\n });\n this.controls.progressTooltip.appendChild(this.controls.progressTooltipTime);\n\n progressContainer.appendChild(this.controls.buffered);\n progressContainer.appendChild(this.controls.played);\n this.controls.played.appendChild(this.controls.progressHandle);\n progressContainer.appendChild(this.controls.progressTooltip);\n\n this.controls.progress = progressContainer;\n\n // Initialize preview functionality\n this.initPreviewThumbnail();\n\n // Progress bar events\n this.setupProgressBarEvents();\n }\n\n /**\n * Initialize preview thumbnail functionality for HTML5 video\n */\n initPreviewThumbnail() {\n this.previewThumbnailCache = new Map();\n this.previewVideo = null;\n this.currentPreviewTime = null;\n this.previewThumbnailTimeout = null;\n this.previewSupported = false;\n this.previewVideoReady = false;\n this.previewVideoInitialized = false;\n this.previewUsingMainVideo = false; // Flag for HLS mode (uses main video instead of separate element)\n\n // Check if preview is supported (HTML5 video or HLS stream)\n // Check if element is a video\n const isVideo = this.player.element && this.player.element.tagName === 'VIDEO';\n \n if (!isVideo) {\n return;\n }\n\n // IMPORTANT: do NOT create/load the preview video until the user has started playback at least once.\n // Otherwise we'd trigger heavy MP4 network traffic just by hovering the progress bar.\n }\n\n /**\n * Lazily create the hidden preview video (only after playback started once)\n * Supports HTML5, HLS, and DASH renderers\n */\n ensurePreviewVideoInitialized() {\n if (this.previewVideoInitialized) return;\n if (!this.player?.state?.hasStartedPlayback) return;\n\n // Check if thumbnail preview is disabled via options\n if (this.player.options.thumbnailPreview === false) {\n this.previewSupported = false;\n this.previewVideoInitialized = true;\n return;\n }\n\n const renderer = this.player.renderer;\n const hasVideoMedia = renderer && renderer.media && renderer.media.tagName === 'VIDEO';\n \n if (!hasVideoMedia) {\n this.previewSupported = false;\n this.previewVideoInitialized = true;\n return;\n }\n\n // Check if this is a streaming renderer (HLS/DASH handle seeking internally).\n // Prefer the renderer's own `isStreaming` flag (stable across dash.js /\n // hls.js API changes). Fall back to probing instance methods for\n // backwards compatibility with older renderers. For DASH we probe\n // multiple methods because dash.js 5.x removed getQualityFor /\n // setQualityFor in favour of getRepresentationsByType /\n // getCurrentRepresentationForType. Missing this check causes the\n // preview thumbnail path to copy the dash.js MediaSource blob URL\n // onto a second <video> element, which Firefox rejects with\n // \"Security Error: Content at … may not load data from blob:…\".\n const isStreamingRenderer = renderer.isStreaming === true\n || (renderer.hls && typeof renderer.hls.loadLevel !== 'undefined')\n || (renderer.dash && (\n typeof renderer.dash.getQualityFor === 'function' ||\n typeof renderer.dash.getCurrentRepresentationForType === 'function' ||\n typeof renderer.dash.getRepresentationsByType === 'function' ||\n typeof renderer.dash.attachSource === 'function'\n ));\n const isHTML5Renderer = hasVideoMedia &&\n renderer.media === this.player.element &&\n !isStreamingRenderer &&\n typeof renderer.seek === 'function';\n\n // For streaming renderers (HLS/DASH), we use the main video element directly\n // For HTML5, we create a separate hidden video element\n if (isStreamingRenderer) {\n // Streaming: Preview thumbnails are disabled because seeking the main video\n // for thumbnail generation would cause visible playback jumps.\n // A separate instance for preview would be too resource-intensive.\n this.previewVideo = null;\n this.previewVideoReady = false;\n this.previewSupported = false;\n this.previewUsingMainVideo = false;\n this.previewVideoInitialized = true;\n this.player.log('Preview thumbnails disabled for streaming sources', 'info');\n return;\n }\n\n this.previewSupported = isHTML5Renderer && hasVideoMedia;\n if (!this.previewSupported) {\n this.previewVideoInitialized = true;\n return;\n }\n\n const mainVideo = renderer.media || this.player.element;\n let videoSrc = null;\n if (mainVideo.src) {\n videoSrc = mainVideo.src;\n } else {\n const source = mainVideo.querySelector('source');\n if (source) {\n videoSrc = source.src;\n }\n }\n\n if (!videoSrc) {\n this.player.log('No video source found for preview', 'warn');\n this.previewSupported = false;\n this.previewVideoInitialized = true;\n return;\n }\n\n // Create a hidden video element for capturing frames\n this.previewVideo = document.createElement('video');\n this.previewVideo.muted = true;\n this.previewVideo.preload = 'auto'; // Need more than metadata to capture frames\n this.previewVideo.playsInline = true;\n this.previewVideo.style.position = 'absolute';\n this.previewVideo.style.visibility = 'hidden';\n this.previewVideo.style.width = '1px';\n this.previewVideo.style.height = '1px';\n this.previewVideo.style.top = '-9999px';\n\n if (mainVideo.crossOrigin) {\n this.previewVideo.crossOrigin = mainVideo.crossOrigin;\n }\n\n this.previewVideo.addEventListener('error', (e: Event) => {\n this.player.log('Preview video failed to load:', e, 'warn');\n this.previewSupported = false;\n });\n\n this.previewVideo.addEventListener('loadedmetadata', () => {\n this.previewVideoReady = true;\n \n // Start pregeneration if enabled\n if (this.player.options.thumbnailPregenerate) {\n this.pregenerateThumbnails();\n }\n }, { once: true });\n\n if (this.player.container) {\n this.player.container.appendChild(this.previewVideo);\n }\n\n this.previewVideo.src = videoSrc;\n this.previewVideoReady = false;\n this.previewUsingMainVideo = false;\n this.previewVideoInitialized = true;\n }\n\n /**\n * Pre-generate thumbnails during browser idle time\n * Uses requestIdleCallback to avoid impacting UI performance\n */\n pregenerateThumbnails() {\n if (!this.previewSupported || !this.previewVideo) return;\n if (!window.requestIdleCallback) return; // Not supported in older browsers\n \n const duration = this.player.state.duration;\n if (!duration || duration <= 0) return;\n \n const interval = this.player.options.thumbnailInterval || 10;\n const times: number[] = [];\n \n // Generate list of times to pre-generate\n for (let t = 0; t < duration; t += interval) {\n // Skip times already in cache\n const cacheKey = Math.floor(t);\n if (!this.previewThumbnailCache.has(cacheKey)) {\n times.push(t);\n }\n }\n \n if (times.length === 0) return;\n \n this.player.log(`Pre-generating ${times.length} thumbnails`, 'debug');\n \n const generateNext = (deadline: IdleDeadline) => {\n // Generate thumbnails while we have idle time (at least 50ms)\n while (deadline.timeRemaining() > 50 && times.length > 0) {\n const time = times.shift();\n if (time === undefined) {\n break;\n }\n // Fire-and-forget thumbnail generation\n this.generatePreviewThumbnail(time).catch(() => {\n // Silently ignore errors during pregeneration\n });\n }\n \n // Schedule next batch if there are more to generate\n if (times.length > 0 && this.previewSupported) {\n requestIdleCallback(generateNext, { timeout: 5000 });\n }\n };\n \n // Start pre-generation with a timeout to ensure it eventually runs\n requestIdleCallback(generateNext, { timeout: 5000 });\n }\n\n /**\n * Generate preview thumbnail for a specific time\n * @param {number} time - Time in seconds\n * @returns {Promise<string>} Data URL of the thumbnail\n */\n async generatePreviewThumbnail(time: number) {\n if (!this.previewSupported || !this.previewVideo) {\n return null;\n }\n\n // Stash a non-null local: TypeScript loses the `previewVideo !== null`\n // narrowing across awaits and inner closures.\n const previewVideo = this.previewVideo;\n\n // Wait for preview video to be ready if not yet loaded\n if (!this.previewVideoReady) {\n if (previewVideo.readyState < 2) {\n // Wait for at least HAVE_CURRENT_DATA (2) to ensure we can capture frames\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error('Preview video data load timeout'));\n }, 10000);\n\n const cleanup = () => {\n clearTimeout(timeout);\n previewVideo.removeEventListener('loadeddata', checkReady);\n previewVideo.removeEventListener('canplay', checkReady);\n previewVideo.removeEventListener('error', onError);\n };\n\n const checkReady = () => {\n if (previewVideo.readyState >= 2) {\n cleanup();\n this.previewVideoReady = true;\n resolve();\n }\n };\n\n const onError = () => {\n cleanup();\n reject(new Error('Preview video failed to load'));\n };\n\n // Try loadeddata first (faster), fallback to canplay\n if (previewVideo.readyState >= 1) {\n previewVideo.addEventListener('loadeddata', checkReady);\n }\n previewVideo.addEventListener('canplay', checkReady);\n previewVideo.addEventListener('error', onError);\n\n // If already ready, resolve immediately\n if (previewVideo.readyState >= 2) {\n checkReady();\n }\n }).catch((): null => {\n this.previewSupported = false;\n return null;\n });\n } else {\n this.previewVideoReady = true;\n }\n }\n\n // Check cache first\n const cacheKey = Math.floor(time);\n if (this.previewThumbnailCache.has(cacheKey)) {\n return this.previewThumbnailCache.get(cacheKey);\n }\n\n // Use shared frame capture utility\n // For main video (HLS), we need to restore state; for preview video, we don't\n const restoreState = this.previewUsingMainVideo;\n const quality = this.player.options.thumbnailQuality || 0.8;\n const maxWidth = this.player.options.thumbnailWidth || 160;\n const maxHeight = this.player.options.thumbnailHeight || 90;\n\n const dataURL = await captureVideoFrame(previewVideo, time, {\n restoreState,\n quality,\n maxWidth,\n maxHeight\n });\n\n if (dataURL) {\n // Cache the thumbnail (configurable cache size with LRU-like behavior)\n const maxCacheSize = this.player.options.thumbnailCacheSize || 50;\n if (this.previewThumbnailCache.size >= maxCacheSize) {\n // Delete oldest entry (first key in insertion order)\n const firstKey = this.previewThumbnailCache.keys().next().value;\n if (firstKey !== undefined) {\n this.previewThumbnailCache.delete(firstKey);\n }\n }\n this.previewThumbnailCache.set(cacheKey, dataURL);\n }\n\n return dataURL;\n }\n\n /**\n * Update preview thumbnail display\n * @param {number} time - Time in seconds\n */\n async updatePreviewThumbnail(time: number) {\n if (!this.previewSupported || !this.controls.progressPreview) {\n return;\n }\n\n // Clear any pending updates\n if (this.previewThumbnailTimeout) {\n clearTimeout(this.previewThumbnailTimeout);\n }\n\n // Debounce thumbnail generation to avoid excessive seeking\n this.previewThumbnailTimeout = setTimeout(async () => {\n try {\n const thumbnail = await this.generatePreviewThumbnail(time);\n if (thumbnail && this.controls.progressPreview) {\n // Set background image and make visible\n this.controls.progressPreview.style.backgroundImage = `url(\"${thumbnail}\")`;\n this.controls.progressPreview.style.display = 'block';\n this.controls.progressPreview.style.backgroundRepeat = 'no-repeat';\n this.controls.progressPreview.style.backgroundPosition = 'center';\n } else {\n // Hide if thumbnail generation failed\n if (this.controls.progressPreview) {\n this.controls.progressPreview.style.display = 'none';\n }\n }\n this.currentPreviewTime = time;\n } catch (error) {\n this.player.log('Preview thumbnail update failed:', error, 'warn');\n if (this.controls.progressPreview) {\n this.controls.progressPreview.style.display = 'none';\n }\n }\n }, 100);\n }\n\n setupProgressBarEvents() {\n const progress = this.controls.progress;\n if (!progress) return;\n\n const updateProgress = (clientX: number) => {\n const rect = progress.getBoundingClientRect();\n // Guard against division by zero if progress bar has no width\n const percent = rect.width > 0 ? Math.max(0, Math.min(1, (clientX - rect.left) / rect.width)) : 0;\n const duration = this.player.state.duration || 0;\n const time = percent * duration;\n return {percent, time};\n };\n\n // Mouse events. Document-wide mousemove/mouseup are installed once\n // in {@link setupGlobalDragListeners}; here we only cache the\n // progress rect on mousedown so the global handler can resolve\n // positions without re-querying layout on every pointer move.\n progress.addEventListener('mousedown', (e: MouseEvent) => {\n this.isDraggingProgress = true;\n this._progressBarRect = progress.getBoundingClientRect();\n const {time} = updateProgress(e.clientX);\n this.player.seek(time);\n });\n\n // Hover tooltip\n progress.addEventListener('mousemove', (e: MouseEvent) => {\n if (!this.isDraggingProgress) {\n const {time} = updateProgress(e.clientX);\n const rect = progress.getBoundingClientRect();\n const left = e.clientX - rect.left;\n \n // Update tooltip time text + position\n const tooltip = this.controls.progressTooltip;\n const tooltipTime = this.controls.progressTooltipTime;\n if (tooltip && tooltipTime) {\n tooltipTime.textContent = TimeUtils.formatTime(time);\n tooltip.style.left = `${left}px`;\n tooltip.style.display = 'block';\n }\n\n // Only show preview thumbnails after the user has started playback at least once.\n // Before that, show just the timestamp (no empty preview box).\n if (!this.player?.state?.hasStartedPlayback) {\n if (this.controls.progressPreview) {\n this.controls.progressPreview.style.display = 'none';\n }\n return;\n }\n\n this.ensurePreviewVideoInitialized();\n if (this.previewSupported) {\n this.updatePreviewThumbnail(time);\n } else if (this.controls.progressPreview) {\n this.controls.progressPreview.style.display = 'none';\n }\n }\n });\n\n progress.addEventListener('mouseleave', () => {\n if (this.controls.progressTooltip) {\n this.controls.progressTooltip.style.display = 'none';\n }\n if (this.previewThumbnailTimeout) {\n clearTimeout(this.previewThumbnailTimeout);\n }\n });\n\n // Keyboard navigation. Mirrors WAI-ARIA Authoring Practices for\n // sliders: PageUp/PageDown step by `seekIntervalLarge` (default 30s),\n // Home/End jump to the start/end of the media.\n progress.addEventListener('keydown', (e: KeyboardEvent) => {\n const smallStep = (this.player.options.seekInterval as number | undefined) || 5;\n const largeStep = (this.player.options.seekIntervalLarge as number | undefined) || 30;\n const duration = Number(this.player.state.duration);\n\n switch (e.key) {\n case 'ArrowLeft':\n e.preventDefault();\n this.player.seekBackward(smallStep);\n break;\n case 'ArrowRight':\n e.preventDefault();\n this.player.seekForward(smallStep);\n break;\n case 'PageUp':\n e.preventDefault();\n this.player.seekForward(largeStep);\n break;\n case 'PageDown':\n e.preventDefault();\n this.player.seekBackward(largeStep);\n break;\n case 'Home':\n e.preventDefault();\n this.player.seek(0);\n break;\n case 'End':\n e.preventDefault();\n if (Number.isFinite(duration) && duration > 0) {\n this.player.seek(Math.max(0, duration - 0.1));\n }\n break;\n default:\n break;\n }\n });\n\n // Touch events\n progress.addEventListener('touchstart', (e: TouchEvent) => {\n this.isDraggingProgress = true;\n const touch = e.touches[0];\n if (!touch) return;\n const {time} = updateProgress(touch.clientX);\n this.player.seek(time);\n });\n\n progress.addEventListener('touchmove', (e: TouchEvent) => {\n if (this.isDraggingProgress) {\n e.preventDefault();\n const touch = e.touches[0];\n if (!touch) return;\n const {time} = updateProgress(touch.clientX);\n this.player.seek(time);\n }\n });\n\n progress.addEventListener('touchend', () => {\n this.isDraggingProgress = false;\n });\n }\n\n createPlayPauseButton() {\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-play-pause`,\n attributes: {\n 'type': 'button',\n 'aria-label': i18n.t('player.play')\n }\n });\n\n button.appendChild(createIconElement('play'));\n\n button.addEventListener('click', () => {\n this.player.toggle();\n });\n\n this.controls.playPause = button;\n return button;\n }\n\n createRestartButton() {\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-restart`,\n attributes: {\n 'type': 'button',\n 'aria-label': i18n.t('player.restart')\n }\n });\n\n button.appendChild(createIconElement('restart'));\n\n button.addEventListener('click', () => {\n this.player.seek(0);\n this.player.play();\n });\n\n return button;\n }\n\n createPreviousButton() {\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-previous`,\n attributes: {\n 'type': 'button',\n 'aria-label': i18n.t('player.previous')\n }\n });\n\n button.appendChild(createIconElement('skipPrevious'));\n\n button.addEventListener('click', () => {\n if (this.player.playlistManager) {\n this.player.playlistManager.previous();\n }\n });\n\n // Update button state\n const updateState = () => {\n if (this.player.playlistManager) {\n (button as HTMLButtonElement).disabled = !this.player.playlistManager.hasPrevious() && !this.player.playlistManager.options.loop;\n }\n };\n this.player.on('playlisttrackchange', updateState);\n updateState();\n\n this.controls.previous = button;\n return button;\n }\n\n createNextButton() {\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-next`,\n attributes: {\n 'type': 'button',\n 'aria-label': i18n.t('player.next')\n }\n });\n\n button.appendChild(createIconElement('skipNext'));\n\n button.addEventListener('click', () => {\n if (this.player.playlistManager) {\n this.player.playlistManager.next();\n }\n });\n\n // Update button state\n const updateState = () => {\n if (this.player.playlistManager) {\n (button as HTMLButtonElement).disabled = !this.player.playlistManager.hasNext() && !this.player.playlistManager.options.loop;\n }\n };\n this.player.on('playlisttrackchange', updateState);\n updateState();\n\n this.controls.next = button;\n return button;\n }\n\n createPlaylistToggleButton() {\n // Get unique panel ID from playlist manager\n const panelId = this.player.playlistManager ? `${this.player.playlistManager.uniqueId}-panel` : 'vidply-playlist-panel';\n \n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-playlist-toggle`,\n attributes: {\n 'type': 'button',\n 'aria-label': i18n.t('player.playlist'),\n 'aria-expanded': 'false',\n 'aria-pressed': 'false',\n 'aria-controls': panelId\n }\n });\n\n button.appendChild(createIconElement('playlist'));\n\n button.addEventListener('click', () => {\n if (this.player.playlistManager) {\n this.player.playlistManager.togglePanel();\n }\n });\n\n this.controls.playlistToggle = button;\n return button;\n }\n\n createRewindButton() {\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-rewind`,\n attributes: {\n 'type': 'button',\n 'aria-label': i18n.t('player.rewindSeconds', { seconds: 15 })\n }\n });\n\n button.appendChild(createIconElement('rewind'));\n\n button.addEventListener('click', () => {\n this.player.seekBackward(15);\n });\n\n return button;\n }\n\n createForwardButton() {\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-forward`,\n attributes: {\n 'type': 'button',\n 'aria-label': i18n.t('player.forwardSeconds', { seconds: 15 })\n }\n });\n\n button.appendChild(createIconElement('forward'));\n\n button.addEventListener('click', () => {\n this.player.seekForward(15);\n });\n\n return button;\n }\n\n createMuteButton() {\n // Simple mute/unmute button for touch devices\n const muteButton = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-mute`,\n attributes: {\n 'type': 'button',\n 'aria-label': i18n.t('player.mute')\n }\n });\n\n muteButton.appendChild(createIconElement('volumeHigh'));\n\n // Simply toggle mute on click/touch\n muteButton.addEventListener('click', () => {\n this.player.toggleMute();\n });\n\n this.controls.mute = muteButton;\n\n return muteButton;\n }\n\n createVolumeControl() {\n // Mute/Volume button with slider (desktop)\n const muteButton = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-mute`,\n attributes: {\n 'type': 'button',\n 'aria-label': i18n.t('player.volume'),\n 'aria-expanded': 'false'\n }\n });\n\n muteButton.appendChild(createIconElement('volumeHigh'));\n\n // Toggle mute on right click, show volume slider on left click\n muteButton.addEventListener('contextmenu', (e) => {\n e.preventDefault();\n this.player.toggleMute();\n });\n\n muteButton.addEventListener('click', () => {\n this.showVolumeSlider(muteButton);\n });\n\n this.controls.mute = muteButton;\n\n return muteButton;\n }\n\n showVolumeSlider(button: HTMLElement) {\n // Remove existing slider if any\n const existingSlider = document.querySelector(`.${this.player.options.classPrefix}-volume-menu`);\n if (existingSlider) {\n existingSlider.remove();\n button.setAttribute('aria-expanded', 'false');\n return;\n }\n\n // Volume menu container\n const volumeMenu = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-volume-menu ${this.player.options.classPrefix}-menu`\n });\n\n const initialPercent = Math.round(this.player.state.volume * 100);\n const volumeSlider = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-volume-slider`,\n attributes: {\n 'role': 'slider',\n 'aria-label': i18n.t('player.volume'),\n 'aria-valuemin': '0',\n 'aria-valuemax': '100',\n 'aria-valuenow': String(initialPercent),\n 'aria-valuetext': this.player.state.muted\n ? i18n.t('player.muted')\n : i18n.t('player.volumePercent', { percent: initialPercent }),\n 'tabindex': '0'\n }\n });\n\n const volumeTrack = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-volume-track`\n });\n\n const volumeFill = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-volume-fill`\n });\n \n // Set initial fill height based on current volume\n const initialVolumePercent = this.player.state.volume * 100;\n volumeFill.style.height = `${initialVolumePercent}%`;\n\n const volumeHandle = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-volume-handle`\n });\n\n volumeTrack.appendChild(volumeFill);\n volumeFill.appendChild(volumeHandle);\n volumeSlider.appendChild(volumeTrack);\n volumeMenu.appendChild(volumeSlider);\n\n // Volume slider events. Document-level mousemove/mouseup are\n // installed once in {@link setupGlobalDragListeners}; here we only\n // mark the active track + drag state so the global handler can\n // translate cursor position into a volume value.\n const updateVolume = (clientY: number) => {\n const rect = volumeTrack.getBoundingClientRect();\n const percent = Math.max(0, Math.min(1, 1 - ((clientY - rect.top) / rect.height)));\n this.player.setVolume(percent);\n };\n\n volumeSlider.addEventListener('mousedown', (e) => {\n e.stopPropagation();\n e.preventDefault();\n this.isDraggingVolume = true;\n this._activeVolumeTrack = volumeTrack;\n updateVolume(e.clientY);\n });\n\n // Touch events for iOS and mobile devices\n volumeSlider.addEventListener('touchstart', (e) => {\n e.stopPropagation();\n e.preventDefault();\n this.isDraggingVolume = true;\n this._activeVolumeTrack = volumeTrack;\n const touch = e.touches[0];\n if (!touch) return;\n updateVolume(touch.clientY);\n }, { passive: false });\n\n volumeSlider.addEventListener('touchmove', (e: TouchEvent) => {\n if (this.isDraggingVolume) {\n e.preventDefault();\n const touch = e.touches[0];\n if (!touch) return;\n updateVolume(touch.clientY);\n }\n }, { passive: false });\n\n volumeSlider.addEventListener('touchend', (e) => {\n e.stopPropagation();\n e.preventDefault();\n this.isDraggingVolume = false;\n this._activeVolumeTrack = null;\n }, { passive: false });\n\n volumeSlider.addEventListener('touchcancel', () => {\n this.isDraggingVolume = false;\n this._activeVolumeTrack = null;\n });\n\n // Keyboard volume control\n volumeSlider.addEventListener('keydown', (e) => {\n if (e.key === 'ArrowUp') {\n e.preventDefault();\n this.player.setVolume(Math.min(1, this.player.state.volume + 0.1));\n } else if (e.key === 'ArrowDown') {\n e.preventDefault();\n this.player.setVolume(Math.max(0, this.player.state.volume - 0.1));\n } else if (e.key === 'Escape') {\n e.preventDefault();\n e.stopPropagation();\n // Close menu and return focus to button\n this.closeMenuAndReturnFocus(volumeMenu, button, true);\n }\n });\n\n // Prevent menu from closing when interacting with slider\n volumeMenu.addEventListener('click', (e) => {\n e.stopPropagation();\n });\n \n // Prevent menu from closing on touch events\n volumeMenu.addEventListener('touchstart', (e) => {\n e.stopPropagation();\n });\n \n volumeMenu.addEventListener('touchmove', (e) => {\n e.stopPropagation();\n });\n \n volumeMenu.addEventListener('touchend', (e) => {\n e.stopPropagation();\n });\n\n // Position menu first (before it's visible) to prevent jumping\n volumeMenu.style.visibility = 'hidden';\n volumeMenu.style.display = 'block';\n \n // Insert menu into DOM (handles fullscreen positioning)\n this.insertMenuIntoDOM(volumeMenu, button);\n \n // Position immediately (synchronously) while hidden\n this.positionMenu(volumeMenu, button, true);\n \n // Make menu visible after positioning\n requestAnimationFrame(() => {\n volumeMenu.style.visibility = 'visible';\n });\n\n this.controls.volumeSlider = volumeSlider;\n this.controls.volumeFill = volumeFill;\n\n // Focus the volume slider for keyboard accessibility\n focusElement(volumeSlider, { delay: 50 });\n\n // Close menu on outside click\n this.attachMenuCloseHandler(volumeMenu, button, true);\n }\n\n createTimeDisplay() {\n const container = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-time`,\n attributes: {\n 'role': 'group',\n 'aria-label': i18n.t('time.display')\n }\n });\n\n // Current time - visual text hidden, accessible text provided via sr-only span\n this.controls.currentTimeDisplay = DOMUtils.createElement('span', {\n className: `${this.player.options.classPrefix}-current-time`\n });\n \n // Create visual text inside, hidden from screen readers\n const currentTimeVisual = DOMUtils.createElement('span', {\n textContent: '00:00',\n attributes: {\n 'aria-hidden': 'true'\n }\n });\n const currentTimeAccessible = DOMUtils.createElement('span', {\n className: 'vidply-sr-only',\n textContent: i18n.t('time.seconds', { count: 0 })\n });\n\n this.controls.currentTimeDisplay.appendChild(currentTimeVisual);\n this.controls.currentTimeDisplay.appendChild(currentTimeAccessible);\n this.controls.currentTimeVisual = currentTimeVisual;\n this.controls.currentTimeAccessible = currentTimeAccessible;\n\n const separator = DOMUtils.createElement('span', {\n textContent: ' / ',\n attributes: {\n 'aria-hidden': 'true'\n }\n });\n\n // Duration - visual text hidden, accessible text provided via sr-only span\n this.controls.durationDisplay = DOMUtils.createElement('span', {\n className: `${this.player.options.classPrefix}-duration`\n });\n \n // Create visual text inside, hidden from screen readers\n const durationVisual = DOMUtils.createElement('span', {\n textContent: '00:00',\n attributes: {\n 'aria-hidden': 'true'\n }\n });\n const durationAccessible = DOMUtils.createElement('span', {\n className: 'vidply-sr-only',\n textContent: i18n.t('time.durationPrefix') + i18n.t('time.seconds', { count: 0 })\n });\n\n this.controls.durationDisplay.appendChild(durationVisual);\n this.controls.durationDisplay.appendChild(durationAccessible);\n this.controls.durationVisual = durationVisual;\n this.controls.durationAccessible = durationAccessible;\n\n container.appendChild(this.controls.currentTimeDisplay);\n container.appendChild(separator);\n container.appendChild(this.controls.durationDisplay);\n\n return container;\n }\n\n createChaptersButton() {\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-chapters`,\n attributes: {\n 'type': 'button',\n 'aria-label': i18n.t('player.chapters'),\n 'aria-expanded': 'false'\n }\n });\n\n button.appendChild(createIconElement('chapters'));\n\n button.addEventListener('click', () => {\n this.showChaptersMenu(button);\n });\n\n this.controls.chapters = button;\n return button;\n }\n\n showChaptersMenu(button: HTMLElement) {\n // Remove existing menu if any (toggle behavior)\n const existingMenu = document.querySelector(`.${this.player.options.classPrefix}-chapters-menu`);\n if (existingMenu) {\n existingMenu.remove();\n button.setAttribute('aria-expanded', 'false');\n // Clear tracking if this was the open menu\n if (this.openMenu === existingMenu) {\n this.openMenu = null;\n this.openMenuButton = null;\n }\n return;\n }\n\n const menu = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-chapters-menu ${this.player.options.classPrefix}-menu`,\n attributes: {\n 'role': 'menu',\n 'aria-label': i18n.t('player.chapters')\n }\n });\n\n // Get chapter tracks\n const chapterTracks: TextTrack[] = (Array.from(this.player.element.textTracks) as TextTrack[]).filter(\n track => track.kind === 'chapters'\n );\n\n if (chapterTracks.length === 0) {\n // No chapters available — non-operable, so keep it out of keyboard\n // roving navigation via aria-disabled (WCAG 2.1.1, 4.1.2).\n const noChaptersItem = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-menu-item ${this.player.options.classPrefix}-menu-item-disabled`,\n textContent: i18n.t('player.noChapters'),\n attributes: {\n 'role': 'menuitem',\n 'aria-disabled': 'true',\n 'tabindex': '-1'\n },\n style: {opacity: '0.5', cursor: 'default'}\n });\n menu.appendChild(noChaptersItem);\n } else if (chapterTracks[0]) {\n const chapterTrack = chapterTracks[0];\n\n // Ensure track is in 'hidden' mode to load cues\n if (chapterTrack.mode === 'disabled') {\n chapterTrack.mode = 'hidden';\n }\n\n if (!chapterTrack.cues || chapterTrack.cues.length === 0) {\n // Cues not loaded yet - wait for them to load. Non-operable\n // placeholder, kept out of roving navigation via aria-disabled.\n const loadingItem = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-menu-item ${this.player.options.classPrefix}-menu-item-disabled`,\n textContent: i18n.t('player.loadingChapters'),\n attributes: {\n 'role': 'menuitem',\n 'aria-disabled': 'true',\n 'tabindex': '-1'\n },\n style: {opacity: '0.5', cursor: 'default'}\n });\n menu.appendChild(loadingItem);\n\n // Listen for track load event\n const onTrackLoad = () => {\n // Remove loading message and rebuild menu\n menu.remove();\n this.showChaptersMenu(button);\n };\n\n chapterTrack.addEventListener('load', onTrackLoad, {once: true});\n\n // Also try again after a short delay as fallback\n setTimeout(() => {\n if (chapterTrack.cues && chapterTrack.cues.length > 0 && document.contains(menu)) {\n menu.remove();\n this.showChaptersMenu(button);\n }\n }, 500);\n } else {\n // Display chapters\n const cues = chapterTrack.cues;\n for (let i = 0; i < cues.length; i++) {\n const cue = cues[i] as VTTCue;\n const item = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-menu-item`,\n attributes: {\n 'type': 'button',\n 'role': 'menuitem',\n 'tabindex': '-1'\n }\n });\n\n const timeLabel = DOMUtils.createElement('span', {\n className: `${this.player.options.classPrefix}-chapter-time`,\n textContent: TimeUtils.formatTime(cue.startTime),\n attributes: {\n 'aria-label': TimeUtils.formatDuration(cue.startTime)\n }\n });\n\n const titleLabel = DOMUtils.createElement('span', {\n className: `${this.player.options.classPrefix}-chapter-title`,\n textContent: cue.text\n });\n\n item.appendChild(timeLabel);\n item.appendChild(document.createTextNode(' '));\n item.appendChild(titleLabel);\n\n item.addEventListener('click', () => {\n this.player.seek(cue.startTime);\n this.closeMenuAndReturnFocus(menu, button);\n });\n\n menu.appendChild(item);\n }\n \n // Add keyboard navigation\n this.attachMenuKeyboardNavigation(menu, button);\n \n // Focus first item\n setTimeout(() => {\n const firstItem = menu.querySelector(`.${this.player.options.classPrefix}-menu-item`) as HTMLElement | null;\n if (firstItem) {\n firstItem.focus({ preventScroll: true });\n }\n }, 0);\n }\n }\n\n // Position menu first (before it's visible) to prevent jumping\n // Set menu to invisible temporarily\n menu.style.visibility = 'hidden';\n menu.style.display = 'block';\n \n // Insert menu into DOM (handles fullscreen positioning)\n this.insertMenuIntoDOM(menu, button);\n \n // Position immediately (synchronously) while hidden\n this.positionMenu(menu, button, true);\n \n // Make menu visible after positioning\n requestAnimationFrame(() => {\n menu.style.visibility = 'visible';\n });\n\n // Close menu on outside click\n this.attachMenuCloseHandler(menu, button);\n }\n\n createQualityButton() {\n const ariaLabel = i18n.t('player.quality');\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-quality`,\n attributes: {\n 'type': 'button',\n 'aria-label': ariaLabel,\n 'aria-expanded': 'false'\n }\n });\n\n button.appendChild(createIconElement('hd'));\n DOMUtils.attachTooltip(button, ariaLabel, this.player.options.classPrefix);\n\n // Add quality indicator text\n const qualityText = DOMUtils.createElement('span', {\n className: `${this.player.options.classPrefix}-quality-text`,\n textContent: ''\n });\n button.appendChild(qualityText);\n\n button.addEventListener('click', () => {\n this.showQualityMenu(button);\n });\n\n this.controls.quality = button;\n this.controls.qualityText = qualityText;\n \n // Update quality indicator after a short delay to ensure renderer is ready\n setTimeout(() => this.updateQualityIndicator(), 500);\n \n return button;\n }\n\n showQualityMenu(button: HTMLElement) {\n // Remove existing menu if any (toggle behavior)\n const existingMenu = document.querySelector(`.${this.player.options.classPrefix}-quality-menu`);\n if (existingMenu) {\n existingMenu.remove();\n button.setAttribute('aria-expanded', 'false');\n // Clear tracking if this was the open menu\n if (this.openMenu === existingMenu) {\n this.openMenu = null;\n this.openMenuButton = null;\n }\n return;\n }\n\n const menu = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-quality-menu ${this.player.options.classPrefix}-menu`,\n attributes: {\n 'role': 'menu',\n 'aria-label': i18n.t('player.quality')\n }\n });\n\n // Check if renderer supports quality selection\n if (this.player.renderer && this.player.renderer.getQualities) {\n const qualities = this.player.renderer.getQualities();\n const currentQuality = this.player.renderer.getCurrentQuality ? this.player.renderer.getCurrentQuality() : -1;\n const hasAutoQuality = typeof this.player.renderer.supportsAutoQuality === 'function'\n && this.player.renderer.supportsAutoQuality();\n\n if (qualities.length === 0) {\n // No qualities available\n const noQualityItem = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-menu-item`,\n textContent: i18n.t('player.autoQuality'),\n attributes: {\n 'role': 'menuitem'\n },\n style: {opacity: '0.5', cursor: 'default'}\n });\n menu.appendChild(noQualityItem);\n } else {\n let activeItem: HTMLElement | null = null;\n \n // Auto quality option (for renderers that support it, e.g. HLS, DASH)\n if (hasAutoQuality) {\n const autoItem = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-menu-item`,\n textContent: i18n.t('player.auto'),\n attributes: {\n 'type': 'button',\n 'role': 'menuitem',\n 'tabindex': '-1'\n }\n });\n\n // Check if auto is currently selected\n const isAuto = typeof this.player.renderer.isAutoQuality === 'function'\n && this.player.renderer.isAutoQuality();\n if (isAuto) {\n autoItem.classList.add(`${this.player.options.classPrefix}-menu-item-active`);\n autoItem.appendChild(createIconElement('check'));\n activeItem = autoItem;\n }\n\n autoItem.addEventListener('click', () => {\n if (this.player.renderer?.switchQuality) {\n this.player.renderer.switchQuality(-1);\n }\n this.closeMenuAndReturnFocus(menu, button);\n });\n\n menu.appendChild(autoItem);\n }\n\n // Quality options\n (qualities as QualityLevel[]).forEach((quality) => {\n const item = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-menu-item`,\n textContent: quality.name || `${quality.height}p`,\n attributes: {\n 'type': 'button',\n 'role': 'menuitem',\n 'tabindex': '-1'\n }\n });\n\n // Highlight current quality\n if (quality.index === currentQuality) {\n item.classList.add(`${this.player.options.classPrefix}-menu-item-active`);\n item.appendChild(createIconElement('check'));\n activeItem = item;\n }\n\n item.addEventListener('click', () => {\n if (this.player.renderer?.switchQuality && quality.index !== undefined) {\n this.player.renderer.switchQuality(quality.index);\n }\n this.closeMenuAndReturnFocus(menu, button);\n });\n\n menu.appendChild(item);\n });\n \n // Add keyboard navigation\n this.attachMenuKeyboardNavigation(menu, button);\n \n // Focus active item or first item\n setTimeout(() => {\n const focusTarget = activeItem || menu.querySelector(`.${this.player.options.classPrefix}-menu-item`);\n if (focusTarget) {\n focusTarget.focus({ preventScroll: true });\n }\n }, 0);\n }\n } else {\n // No quality support\n const noSupportItem = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-menu-item`,\n textContent: i18n.t('player.noQuality'),\n style: {opacity: '0.5', cursor: 'default'}\n });\n menu.appendChild(noSupportItem);\n }\n\n // Position menu first (before it's visible) to prevent jumping\n // Set menu to invisible temporarily\n menu.style.visibility = 'hidden';\n menu.style.display = 'block';\n \n // Insert menu into DOM (handles fullscreen positioning)\n this.insertMenuIntoDOM(menu, button);\n \n // Position immediately (synchronously) while hidden\n this.positionMenu(menu, button, true);\n \n // Make menu visible after positioning\n requestAnimationFrame(() => {\n menu.style.visibility = 'visible';\n });\n\n // Close menu on outside click\n this.attachMenuCloseHandler(menu, button);\n }\n\n createCaptionStyleButton() {\n const ariaLabel = i18n.t('player.captionStyling');\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-caption-style`,\n attributes: {\n 'type': 'button',\n 'aria-label': ariaLabel,\n 'aria-expanded': 'false'\n }\n });\n\n // Create \"Aa\" text icon for styling\n const textIcon = DOMUtils.createElement('span', {\n textContent: 'Aa',\n style: {\n fontSize: '14px',\n fontWeight: 'bold'\n }\n });\n button.appendChild(textIcon);\n DOMUtils.attachTooltip(button, ariaLabel, this.player.options.classPrefix);\n\n button.addEventListener('click', () => {\n this.showCaptionStyleMenu(button);\n });\n\n this.controls.captionStyle = button;\n return button;\n }\n\n showCaptionStyleMenu(button: HTMLElement) {\n // The caption-style panel builder (font/color/opacity controls) lives\n // in a lazily imported module so it stays out of the always-loaded\n // core bundle and is only fetched the first time the panel is opened.\n import('./CaptionStyleMenu.js')\n .then(({ showCaptionStyleMenu }) => showCaptionStyleMenu(this, button))\n .catch((error: unknown) => this.player.log('Failed to load caption style menu:', error, 'error'));\n }\n\n createSpeedButton() {\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-speed`,\n attributes: {\n 'type': 'button',\n 'aria-label': i18n.t('player.speed'),\n 'aria-expanded': 'false'\n }\n });\n\n button.appendChild(createIconElement('speed'));\n\n const speedText = DOMUtils.createElement('span', {\n className: `${this.player.options.classPrefix}-speed-text`,\n textContent: '1x'\n });\n button.appendChild(speedText);\n\n button.addEventListener('click', () => {\n this.showSpeedMenu(button);\n });\n\n this.controls.speed = button;\n this.controls.speedText = speedText;\n return button;\n }\n\n formatSpeedLabel(speed: number) {\n // Special case: 1x is \"Normal\" (translated)\n if (speed === 1) {\n return i18n.t('speeds.normal');\n }\n \n // For other speeds, format with locale-specific decimal separator\n const speedStr = speed.toLocaleString(i18n.getLanguage(), {\n minimumFractionDigits: 0,\n maximumFractionDigits: 2\n });\n \n return `${speedStr}×`;\n }\n\n showSpeedMenu(button: HTMLElement) {\n // Remove existing menu if any (toggle behavior)\n const existingMenu = document.querySelector(`.${this.player.options.classPrefix}-speed-menu`);\n if (existingMenu) {\n existingMenu.remove();\n button.setAttribute('aria-expanded', 'false');\n // Clear tracking if this was the open menu\n if (this.openMenu === existingMenu) {\n this.openMenu = null;\n this.openMenuButton = null;\n }\n return;\n }\n\n const menu = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-speed-menu ${this.player.options.classPrefix}-menu`,\n attributes: {\n 'role': 'menu'\n }\n });\n\n const speeds = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2];\n let activeItem: HTMLElement | null = null;\n\n speeds.forEach(speed => {\n const item = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-menu-item`,\n textContent: this.formatSpeedLabel(speed),\n attributes: {\n 'type': 'button',\n 'role': 'menuitem',\n 'tabindex': '-1'\n }\n });\n\n if (speed === this.player.state.playbackSpeed) {\n item.classList.add(`${this.player.options.classPrefix}-menu-item-active`);\n item.appendChild(createIconElement('check'));\n activeItem = item;\n }\n\n item.addEventListener('click', () => {\n this.player.setPlaybackSpeed(speed);\n this.closeMenuAndReturnFocus(menu, button);\n });\n\n menu.appendChild(item);\n });\n\n // Position menu first (before it's visible) to prevent jumping\n // Set menu to invisible temporarily\n menu.style.visibility = 'hidden';\n menu.style.display = 'block';\n \n // Insert menu into DOM (handles fullscreen positioning)\n this.insertMenuIntoDOM(menu, button);\n \n // Position immediately (synchronously) while hidden\n this.positionMenu(menu, button, true);\n \n // Make menu visible after positioning\n requestAnimationFrame(() => {\n menu.style.visibility = 'visible';\n });\n\n // Add keyboard navigation\n this.attachMenuKeyboardNavigation(menu, button);\n\n // Close menu on outside click\n this.attachMenuCloseHandler(menu, button);\n\n // Focus the active item or first item\n setTimeout(() => {\n const focusTarget = activeItem || menu.querySelector(`.${this.player.options.classPrefix}-menu-item`);\n if (focusTarget) {\n focusTarget.focus({ preventScroll: true });\n }\n }, 0);\n }\n\n createCaptionsButton() {\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-captions-button`,\n attributes: {\n 'type': 'button',\n 'aria-label': i18n.t('player.captions'),\n 'aria-expanded': 'false'\n }\n });\n\n button.appendChild(createIconElement('captionsOff'));\n\n button.addEventListener('click', () => {\n this.showCaptionsMenu(button);\n });\n\n this.controls.captions = button;\n return button;\n }\n\n showCaptionsMenu(button: HTMLElement) {\n // Remove existing menu if any (toggle behavior)\n const existingMenu = document.querySelector(`.${this.player.options.classPrefix}-captions-menu`);\n if (existingMenu) {\n existingMenu.remove();\n button.setAttribute('aria-expanded', 'false');\n // Clear tracking if this was the open menu\n if (this.openMenu === existingMenu) {\n this.openMenu = null;\n this.openMenuButton = null;\n }\n return;\n }\n\n const menu = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-captions-menu ${this.player.options.classPrefix}-menu`,\n attributes: {\n 'role': 'menu',\n 'aria-label': i18n.t('captions.select')\n }\n });\n\n // Check if there are any caption tracks\n if (!this.player.captionManager || this.player.captionManager.tracks.length === 0) {\n // Show \"No captions available\" message\n const noTracksItem = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-menu-item`,\n textContent: i18n.t('player.noCaptions'),\n attributes: {\n 'role': 'menuitem'\n },\n style: {opacity: '0.5', cursor: 'default'}\n });\n menu.appendChild(noTracksItem);\n\n // Insert menu into DOM (handles fullscreen positioning)\n this.insertMenuIntoDOM(menu, button);\n\n // Close menu on outside click\n this.attachMenuCloseHandler(menu, button);\n return;\n }\n\n let activeItem: HTMLElement | null = null;\n\n // Off option\n const offItem = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-menu-item`,\n textContent: i18n.t('captions.off'),\n attributes: {\n 'type': 'button',\n 'role': 'menuitem',\n 'tabindex': '-1'\n }\n });\n\n if (!this.player.state.captionsEnabled) {\n offItem.classList.add(`${this.player.options.classPrefix}-menu-item-active`);\n offItem.appendChild(createIconElement('check'));\n activeItem = offItem;\n }\n\n offItem.addEventListener('click', () => {\n this.player.disableCaptions();\n this.updateCaptionsButton();\n this.closeMenuAndReturnFocus(menu, button);\n });\n\n menu.appendChild(offItem);\n\n // Available tracks. `this.player.captionManager` is loosely typed\n // (any) on Player today, so re-anchor the return type via the real\n // CaptionManager class to keep `track` strongly typed below.\n const tracks: ReturnType<CaptionManager['getAvailableTracks']> =\n this.player.captionManager.getAvailableTracks();\n tracks.forEach((track) => {\n const item = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-menu-item`,\n textContent: track.label,\n attributes: {\n 'type': 'button',\n 'role': 'menuitem',\n 'lang': track.language,\n 'tabindex': '-1'\n }\n });\n\n const captionManager = this.player.captionManager;\n if (captionManager && this.player.state.captionsEnabled &&\n captionManager.currentTrack === captionManager.tracks[track.index]) {\n item.classList.add(`${this.player.options.classPrefix}-menu-item-active`);\n item.appendChild(createIconElement('check'));\n activeItem = item;\n }\n\n item.addEventListener('click', () => {\n this.player.captionManager?.switchTrack(track.index);\n this.updateCaptionsButton();\n this.closeMenuAndReturnFocus(menu, button);\n });\n\n menu.appendChild(item);\n });\n\n // Insert menu into DOM (handles fullscreen positioning)\n this.insertMenuIntoDOM(menu, button);\n\n // Add keyboard navigation for the menu\n this.attachMenuKeyboardNavigation(menu, button);\n\n // Close menu on outside click and Escape key\n this.attachMenuCloseHandler(menu, button);\n\n // Focus the active item or the first item\n setTimeout(() => {\n const focusTarget = activeItem || menu.querySelector(`.${this.player.options.classPrefix}-menu-item`);\n if (focusTarget) {\n focusTarget.focus({ preventScroll: true });\n }\n }, 0);\n }\n\n updateCaptionsButton() {\n if (!this.controls.captions) return;\n\n const icon = this.controls.captions.querySelector('.vidply-icon');\n if (!icon) return;\n const isEnabled = this.player.state.captionsEnabled;\n\n icon.innerHTML = isEnabled ?\n createIconElement('captions').innerHTML :\n createIconElement('captionsOff').innerHTML;\n\n }\n\n createTranscriptButton() {\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-transcript`,\n attributes: {\n 'type': 'button',\n 'aria-label': i18n.t('player.transcript'),\n 'aria-expanded': 'false'\n }\n });\n\n button.appendChild(createIconElement('transcript'));\n\n button.addEventListener('click', async () => {\n await this.player.toggleTranscript();\n this.updateTranscriptButton();\n });\n\n this.controls.transcript = button;\n return button;\n }\n\n updateTranscriptButton() {\n if (!this.controls.transcript) return;\n\n const isVisible = this.player.transcriptManager && this.player.transcriptManager.isVisible;\n this.controls.transcript.setAttribute('aria-expanded', isVisible ? 'true' : 'false');\n }\n\n createAudioDescriptionButton() {\n const ariaLabel = i18n.t('player.audioDescription');\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-audio-description`,\n attributes: {\n 'type': 'button',\n 'aria-label': ariaLabel,\n 'role': 'switch',\n 'aria-checked': 'false'\n }\n });\n\n button.appendChild(createIconElement('audioDescription'));\n DOMUtils.attachTooltip(button, ariaLabel, this.player.options.classPrefix);\n\n button.addEventListener('click', async () => {\n await this.player.toggleAudioDescription();\n this.updateAudioDescriptionButton();\n });\n\n this.controls.audioDescription = button;\n return button;\n }\n\n updateAudioDescriptionButton() {\n if (!this.controls.audioDescription) return;\n\n const icon = this.controls.audioDescription.querySelector('.vidply-icon');\n const isEnabled = this.player.state.audioDescriptionEnabled;\n\n if (icon) {\n icon.innerHTML = isEnabled ?\n createIconElement('audioDescriptionOn').innerHTML :\n createIconElement('audioDescription').innerHTML;\n }\n\n this.controls.audioDescription.setAttribute('aria-checked', isEnabled ? 'true' : 'false');\n // Keep aria-label static - let aria-checked convey the state for switch role\n // The label describes what the control is, not what action it performs\n }\n\n createSignLanguageButton() {\n const ariaLabel = i18n.t('player.signLanguage');\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-sign-language`,\n attributes: {\n 'type': 'button',\n 'aria-label': ariaLabel,\n 'aria-expanded': 'false'\n }\n });\n\n button.appendChild(createIconElement('signLanguagePip'));\n DOMUtils.attachTooltip(button, ariaLabel, this.player.options.classPrefix);\n\n button.addEventListener('click', () => {\n this.player.toggleSignLanguage();\n this.updateSignLanguageButton();\n });\n\n this.controls.signLanguage = button;\n return button;\n }\n\n updateSignLanguageButton() {\n if (!this.controls.signLanguage) return;\n\n const icon = this.controls.signLanguage.querySelector('.vidply-icon');\n const isEnabled = this.player.state.signLanguageEnabled;\n\n if (icon) {\n icon.innerHTML = isEnabled ?\n createIconElement('signLanguagePipOn').innerHTML :\n createIconElement('signLanguagePip').innerHTML;\n }\n\n this.controls.signLanguage.setAttribute('aria-expanded', isEnabled ? 'true' : 'false');\n this.controls.signLanguage.setAttribute('aria-label',\n isEnabled ? i18n.t('signLanguage.hide') : i18n.t('signLanguage.show')\n );\n }\n\n /**\n * Create sign language in main view button (src swap, like audio description)\n */\n createSignLanguageInMainViewButton() {\n const ariaLabel = i18n.t('signLanguage.showInMainView');\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-sign-language-main-view`,\n attributes: {\n 'type': 'button',\n 'aria-label': ariaLabel,\n 'aria-pressed': 'false'\n }\n });\n\n button.appendChild(createIconElement('signLanguage'));\n DOMUtils.attachTooltip(button, ariaLabel, this.player.options.classPrefix);\n\n button.addEventListener('click', () => {\n if (this.player.signLanguageManager) {\n this.player.signLanguageManager.toggleInMainView();\n }\n });\n\n this.controls.signLanguageMainView = button;\n return button;\n }\n\n /**\n * Update sign language in main view button state\n */\n updateSignLanguageInMainViewButton() {\n const btn = this.controls.signLanguageMainView;\n if (!btn) return;\n\n const isEnabled = this.player.state.signLanguageInMainView;\n const newLabel = isEnabled ? i18n.t('signLanguage.hideInMainView') : i18n.t('signLanguage.showInMainView');\n const iconName = isEnabled ? 'signLanguageOn' : 'signLanguage';\n\n const icon = btn.querySelector('.vidply-icon');\n if (icon) {\n icon.innerHTML = createIconElement(iconName).innerHTML;\n }\n btn.setAttribute('aria-pressed', String(isEnabled));\n btn.setAttribute('aria-label', newLabel);\n \n const tooltip = btn.querySelector(`.${this.player.options.classPrefix}-tooltip`);\n if (tooltip) tooltip.textContent = newLabel;\n }\n\n /**\n * Update accessibility buttons visibility based on current track data.\n * Called when loading a new playlist track to show/hide buttons accordingly.\n */\n updateAccessibilityButtons() {\n const hasAudioDescription = this.hasAudioDescription();\n const hasSignLanguage = this.hasSignLanguage();\n \n // Handle Audio Description button\n if (hasAudioDescription) {\n // Create button if it doesn't exist\n if (!this.controls.audioDescription && this.player.options.audioDescriptionButton !== false) {\n const btn = this.createAudioDescriptionButton();\n btn.dataset.overflowPriority = '2';\n btn.dataset.overflowPriorityMobile = '3';\n // Insert before transcript or playlist toggle button\n const transcriptBtn = this.rightButtons.querySelector(`.${this.player.options.classPrefix}-transcript`);\n const playlistBtn = this.rightButtons.querySelector(`.${this.player.options.classPrefix}-playlist-toggle`);\n const insertBefore = transcriptBtn || playlistBtn || null;\n if (insertBefore) {\n this.rightButtons.insertBefore(btn, insertBefore);\n } else {\n this.rightButtons.appendChild(btn);\n }\n // Re-evaluate overflow now that a new button was added.\n this.checkOverflow();\n }\n // Show button\n if (this.controls.audioDescription) {\n this.controls.audioDescription.style.display = '';\n }\n } else {\n // Hide button if no audio description available\n if (this.controls.audioDescription) {\n this.controls.audioDescription.style.display = 'none';\n }\n }\n \n // Handle Sign Language buttons (PiP overlay and/or main view based on displayMode)\n const showSignLanguage = hasSignLanguage && this.player.options.signLanguageButton !== false;\n const classPrefix = this.player.options.classPrefix;\n const displayMode = this.player.options.signLanguageDisplayMode || 'both';\n const showPip = ['pip', 'both'].includes(displayMode);\n const showMain = ['main', 'both'].includes(displayMode);\n \n if (showSignLanguage) {\n // Find insertion point once for both buttons\n const qualityBtn = this.rightButtons.querySelector(`.${classPrefix}-quality`);\n const fullscreenBtn = this.rightButtons.querySelector(`.${classPrefix}-fullscreen`);\n const insertBeforeRef = qualityBtn || fullscreenBtn || null;\n let needsOverflowSetup = false;\n \n // Create PiP button if needed and doesn't exist\n if (showPip && !this.controls.signLanguage) {\n const btn = this.createSignLanguageButton();\n btn.dataset.overflowPriority = '3';\n btn.dataset.overflowPriorityMobile = '3';\n if (insertBeforeRef) {\n this.rightButtons.insertBefore(btn, insertBeforeRef);\n } else {\n this.rightButtons.appendChild(btn);\n }\n needsOverflowSetup = true;\n }\n \n // Create main view button if needed and doesn't exist\n if (showMain && !this.controls.signLanguageMainView) {\n const btn = this.createSignLanguageInMainViewButton();\n btn.dataset.overflowPriority = '3';\n btn.dataset.overflowPriorityMobile = '3';\n // Insert after PiP button or at the reference point\n const afterPip = this.controls.signLanguage?.nextSibling;\n if (afterPip) {\n this.rightButtons.insertBefore(btn, afterPip);\n } else if (insertBeforeRef) {\n this.rightButtons.insertBefore(btn, insertBeforeRef);\n } else {\n this.rightButtons.appendChild(btn);\n }\n needsOverflowSetup = true;\n }\n \n if (needsOverflowSetup) {\n this.checkOverflow();\n }\n \n // Show/hide buttons based on displayMode\n if (this.controls.signLanguage) {\n this.controls.signLanguage.style.display = showPip ? '' : 'none';\n }\n if (this.controls.signLanguageMainView) {\n this.controls.signLanguageMainView.style.display = showMain ? '' : 'none';\n }\n } else {\n // Hide both buttons if no sign language available\n if (this.controls.signLanguage) this.controls.signLanguage.style.display = 'none';\n if (this.controls.signLanguageMainView) this.controls.signLanguageMainView.style.display = 'none';\n }\n }\n\n createSettingsButton() {\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-settings`,\n attributes: {\n 'type': 'button',\n 'aria-label': i18n.t('player.settings')\n }\n });\n\n button.appendChild(createIconElement('settings'));\n\n button.addEventListener('click', () => {\n this.player.showSettings();\n });\n\n return button;\n }\n\n createPipButton() {\n const floating = this.player.options.floating === true;\n const labelKey = floating ? 'player.floatingPlayer' : 'player.pip';\n const prefix = this.player.options.classPrefix;\n // When floating is enabled, add an extra marker class so the CSS\n // media query can hide the button below the feature's minimum\n // viewport width without affecting the native-PiP variant.\n const className = floating\n ? `${prefix}-button ${prefix}-pip ${prefix}-pip-floating`\n : `${prefix}-button ${prefix}-pip`;\n const button = DOMUtils.createElement('button', {\n className,\n attributes: {\n 'type': 'button',\n 'aria-label': i18n.t(labelKey),\n 'aria-pressed': 'false'\n }\n }) as HTMLButtonElement;\n\n button.appendChild(createIconElement('pip'));\n\n button.addEventListener('click', () => {\n if (floating) {\n if (this.player.floatingPlayerManager) {\n this.player.floatingPlayerManager.togglePinned(button);\n }\n } else {\n this.player.togglePiP();\n }\n });\n\n if (floating) {\n // Keep aria-pressed in sync with the floating state so screen\n // readers announce the toggled state correctly.\n this.player.on('floatingchange', (state: 'pinned' | 'auto' | null) => {\n button.setAttribute('aria-pressed', state === 'pinned' ? 'true' : 'false');\n button.classList.toggle(`${this.player.options.classPrefix}-pip-active`, Boolean(state));\n });\n }\n\n return button;\n }\n\n createDownloadButton(downloadUrl: string) {\n const dataset = this.player.element?.dataset || ({} as DOMStringMap);\n\n const format = this.resolveDownloadFormat(downloadUrl);\n const initialSize = this.resolveInitialDownloadSize();\n\n const baseLabel = i18n.t('player.download');\n const initialLabel = buildDownloadLabel({\n baseLabel,\n format,\n sizeBytes: initialSize,\n locale: i18n.getLanguage(),\n withFormatSizeTemplate: i18n.t('player.downloadWithFormatSize'),\n withFormatTemplate: i18n.t('player.downloadWithFormat'),\n withSizeTemplate: i18n.t('player.downloadWithSize')\n });\n\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-download`,\n attributes: {\n 'type': 'button',\n 'aria-label': initialLabel\n }\n }) as HTMLButtonElement;\n\n if (format) button.dataset.vidplyDownloadFormat = format;\n if (initialSize != null) button.dataset.vidplyDownloadSize = String(initialSize);\n\n button.appendChild(createIconElement('download'));\n\n button.addEventListener('click', () => {\n const url = this.player.options.downloadUrl\n || dataset.vidplyDownloadUrl\n || downloadUrl;\n if (!url) return;\n\n const a = document.createElement('a');\n a.href = url;\n a.download = url.split('/').pop()?.split('?')[0] || 'download';\n a.rel = 'noopener';\n a.style.display = 'none';\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n });\n\n // If size wasn't provided, try a HEAD request and update the label/tooltip when it resolves.\n const shouldFetchSize = this.player.options.downloadFetchSize !== false && initialSize == null;\n if (shouldFetchSize) {\n fetchContentLength(downloadUrl).then(sizeBytes => {\n if (sizeBytes == null) return;\n const newLabel = buildDownloadLabel({\n baseLabel,\n format,\n sizeBytes,\n locale: i18n.getLanguage(),\n withFormatSizeTemplate: i18n.t('player.downloadWithFormatSize'),\n withFormatTemplate: i18n.t('player.downloadWithFormat'),\n withSizeTemplate: i18n.t('player.downloadWithSize')\n });\n button.dataset.vidplyDownloadSize = String(sizeBytes);\n this.updateDownloadButtonLabel(button, newLabel);\n });\n }\n\n return button;\n }\n\n /**\n * Resolve the human-readable file format (e.g. \"MP4\") for the download\n * button from options, data attributes, the matching <source type>, or\n * the URL extension. Returns null when nothing can be determined.\n */\n resolveDownloadFormat(downloadUrl: string): string | null {\n const dataset = this.player.element?.dataset || ({} as DOMStringMap);\n\n const explicit = this.player.options.downloadFormat\n || dataset.vidplyDownloadFormat\n || null;\n if (explicit) return explicit;\n\n const sourceEls = this.player.element?.querySelectorAll\n ? Array.from(this.player.element.querySelectorAll('source')) as HTMLSourceElement[]\n : [];\n const matching = sourceEls.find(s => (s.getAttribute('src') || s.src || '') === downloadUrl);\n const candidate = matching || sourceEls[0];\n if (candidate) {\n const fromMime = inferFormatFromMime(candidate.getAttribute('type'));\n if (fromMime) return fromMime;\n }\n\n return inferFormatFromUrl(downloadUrl);\n }\n\n /**\n * Resolve a known file size from options or data attributes (in bytes).\n * Returns null if no value was provided and a HEAD request should run.\n */\n resolveInitialDownloadSize(): number | null {\n const dataset = this.player.element?.dataset || ({} as DOMStringMap);\n const optionSize = this.player.options.downloadFileSize;\n if (typeof optionSize === 'number' && Number.isFinite(optionSize) && optionSize > 0) {\n return optionSize;\n }\n const datasetSize = dataset.vidplyDownloadSize;\n if (datasetSize) {\n const n = Number(datasetSize);\n if (Number.isFinite(n) && n > 0) return n;\n }\n return null;\n }\n\n /**\n * Update both aria-label and the visible tooltip text for the download button.\n */\n updateDownloadButtonLabel(button: HTMLButtonElement, label: string): void {\n if (!button || !label) return;\n button.setAttribute('aria-label', label);\n const tooltip = button.querySelector(`.${this.player.options.classPrefix}-tooltip`);\n if (tooltip) {\n tooltip.textContent = label;\n }\n }\n\n createFullscreenButton() {\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-fullscreen`,\n attributes: {\n 'type': 'button',\n 'aria-label': i18n.t('player.fullscreen')\n }\n });\n\n button.appendChild(createIconElement('fullscreen'));\n\n button.addEventListener('click', () => {\n this.player.toggleFullscreen();\n });\n\n this.controls.fullscreen = button;\n return button;\n }\n\n attachEvents() {\n // Update controls based on player state\n this.player.on('play', () => this.updatePlayPauseButton());\n this.player.on('pause', () => this.updatePlayPauseButton());\n this.player.on('timeupdate', () => this.updateProgress());\n this.player.on('loadedmetadata', () => {\n this.updateDuration();\n this.ensureQualityButton();\n this.updateQualityIndicator();\n // Update preview video source when metadata loads (for playlists)\n this.updatePreviewVideoSource();\n });\n this.player.on('durationchange', () => {\n this.updateDuration();\n });\n this.player.on('sourcechange', () => {\n // Update preview video source when source changes (for playlists)\n this.updatePreviewVideoSource();\n });\n this.player.on('volumechange', () => this.updateVolumeDisplay());\n this.player.on('progress', () => this.updateBuffered());\n this.player.on('playbackspeedchange', () => this.updateSpeedDisplay());\n this.player.on('fullscreenchange', () => this.updateFullscreenButton());\n this.player.on('captionsenabled', () => this.updateCaptionsButton());\n this.player.on('captionsdisabled', () => this.updateCaptionsButton());\n this.player.on('audiodescriptionenabled', () => this.updateAudioDescriptionButton());\n this.player.on('audiodescriptiondisabled', () => this.updateAudioDescriptionButton());\n this.player.on('signlanguageenabled', () => this.updateSignLanguageButton());\n this.player.on('signlanguagedisabled', () => this.updateSignLanguageButton());\n this.player.on('signlanguageinmainviewenabled', () => this.updateSignLanguageInMainViewButton());\n this.player.on('signlanguageinmainviewdisabled', () => this.updateSignLanguageInMainViewButton());\n this.player.on('qualitychange', () => this.updateQualityIndicator());\n this.player.on('hlslevelswitched', () => this.updateQualityIndicator());\n this.player.on('hlsmanifestparsed', () => {\n this.ensureQualityButton();\n this.updateQualityIndicator();\n });\n this.player.on('dashqualitychanged', () => this.updateQualityIndicator());\n this.player.on('dashmanifestparsed', () => {\n this.ensureQualityButton();\n this.updateQualityIndicator();\n });\n }\n\n updatePlayPauseButton() {\n if (!this.controls.playPause) return;\n\n const icon = this.controls.playPause.querySelector('.vidply-icon');\n const isPlaying = this.player.state.playing;\n\n if (icon) {\n icon.innerHTML = isPlaying ?\n createIconElement('pause').innerHTML :\n createIconElement('play').innerHTML;\n }\n\n const newAriaLabel = isPlaying ? i18n.t('player.pause') : i18n.t('player.play');\n this.controls.playPause.setAttribute('aria-label', newAriaLabel);\n // Update tooltip to match aria-label\n DOMUtils.attachTooltip(this.controls.playPause, newAriaLabel, this.player.options.classPrefix);\n }\n\n updateProgress() {\n if (!this.controls.played) return;\n\n const currentTime = this.player.state.currentTime || 0;\n const duration = this.player.state.duration || 0;\n \n // Guard against division by zero and ensure valid percentage\n const percent = duration > 0 ? Math.min(100, Math.max(0, (currentTime / duration) * 100)) : 0;\n \n this.controls.played.style.width = `${percent}%`;\n if (this.controls.progress) {\n this.controls.progress.setAttribute('aria-valuenow', String(Math.round(percent)));\n\n // Set aria-valuetext to announce both percentage and time for screen readers\n const currentTimeText = TimeUtils.formatDuration(this.player.state.currentTime);\n const durationText = TimeUtils.formatDuration(this.player.state.duration);\n this.controls.progress.setAttribute('aria-valuetext',\n `${Math.round(percent)}%, ${currentTimeText} ${i18n.t('time.of')} ${durationText}`);\n }\n\n if (this.controls.currentTimeVisual) {\n const currentTime = this.player.state.currentTime;\n // Update visual text (hidden from screen readers)\n this.controls.currentTimeVisual.textContent = TimeUtils.formatTime(currentTime);\n if (this.controls.currentTimeAccessible) {\n this.controls.currentTimeAccessible.textContent = TimeUtils.formatDuration(currentTime);\n }\n }\n }\n\n updateDuration() {\n if (this.controls.durationVisual) {\n const duration = this.player.state.duration;\n // Update visual text (hidden from screen readers)\n this.controls.durationVisual.textContent = TimeUtils.formatTime(duration);\n if (this.controls.durationAccessible) {\n this.controls.durationAccessible.textContent = i18n.t('time.durationPrefix') + TimeUtils.formatDuration(duration);\n }\n }\n }\n\n updateVolumeDisplay() {\n const percent = this.player.state.volume * 100;\n\n // Update volume fill bar if it exists\n if (this.controls.volumeFill) {\n this.controls.volumeFill.style.height = `${percent}%`;\n }\n \n // Update volume slider aria-valuenow + aria-valuetext if it exists.\n if (this.controls.volumeSlider) {\n const rounded = Math.round(percent);\n this.controls.volumeSlider.setAttribute('aria-valuenow', String(rounded));\n this.controls.volumeSlider.setAttribute(\n 'aria-valuetext',\n this.player.state.muted\n ? i18n.t('player.muted')\n : i18n.t('player.volumePercent', { percent: rounded })\n );\n }\n\n // Update mute button icon (should always work even if slider not shown)\n if (this.controls.mute) {\n const icon = this.controls.mute.querySelector('.vidply-icon');\n if (icon) {\n let iconName;\n\n if (this.player.state.muted || this.player.state.volume === 0) {\n iconName = 'volumeMuted';\n } else if (this.player.state.volume < 0.3) {\n iconName = 'volumeLow';\n } else if (this.player.state.volume < 0.7) {\n iconName = 'volumeMedium';\n } else {\n iconName = 'volumeHigh';\n }\n\n icon.innerHTML = createIconElement(iconName).innerHTML;\n\n // On touch devices the button toggles mute directly, so label reflects that action.\n // On desktop the button opens the volume slider, so the label stays \"Volume\"\n // and we append the current percentage for quick visual feedback.\n const volumePercent = this.player.state.muted ? 0 : Math.round(percent);\n const newMuteAriaLabel = this.isTouchDevice()\n ? (this.player.state.muted ? i18n.t('player.unmute') : i18n.t('player.mute'))\n : `${i18n.t('player.volume')} ${volumePercent}%`;\n this.controls.mute.setAttribute('aria-label', newMuteAriaLabel);\n // Update tooltip to match aria-label\n DOMUtils.attachTooltip(this.controls.mute, newMuteAriaLabel, this.player.options.classPrefix);\n }\n }\n\n }\n\n updateBuffered() {\n if (!this.controls.buffered || !this.player.element.buffered || this.player.element.buffered.length === 0) return;\n\n const buffered = this.player.element.buffered.end(this.player.element.buffered.length - 1);\n const percent = (buffered / this.player.state.duration) * 100;\n this.controls.buffered.style.width = `${percent}%`;\n }\n\n updateSpeedDisplay() {\n if (this.controls.speedText) {\n this.controls.speedText.textContent = `${this.player.state.playbackSpeed}x`;\n }\n }\n\n updateFullscreenButton() {\n if (!this.controls.fullscreen) return;\n\n const icon = this.controls.fullscreen.querySelector('.vidply-icon');\n const isFullscreen = this.player.state.fullscreen;\n\n if (icon) {\n icon.innerHTML = isFullscreen ?\n createIconElement('fullscreenExit').innerHTML :\n createIconElement('fullscreen').innerHTML;\n }\n\n this.controls.fullscreen.setAttribute('aria-label',\n isFullscreen ? i18n.t('player.exitFullscreen') : i18n.t('player.fullscreen')\n );\n }\n\n /**\n * Ensure quality button exists if qualities are available\n * This is called after renderer initialization to dynamically add the button\n */\n ensureQualityButton() {\n // Skip if quality button is disabled\n if (!this.player.options.qualityButton) return;\n \n // Skip if button already exists\n if (this.controls.quality) return;\n \n // Check if qualities are now available\n if (!this.hasQualityLevels()) return;\n \n // Create and insert the quality button before the speed button\n const qualityButton = this.createQualityButton();\n \n // Find the speed button or caption style button to insert before\n const speedButton = this.rightButtons.querySelector(`.${this.player.options.classPrefix}-speed`);\n const captionStyleButton = this.rightButtons.querySelector(`.${this.player.options.classPrefix}-caption-style`);\n const insertBefore = captionStyleButton || speedButton;\n \n if (insertBefore) {\n this.rightButtons.insertBefore(qualityButton, insertBefore);\n } else {\n // If no reference button, add it at the beginning of right buttons\n this.rightButtons.insertBefore(qualityButton, this.rightButtons.firstChild);\n }\n \n this.player.log('Quality button added dynamically', 'info');\n }\n\n /**\n * Dynamically add captions button if HLS subtitle tracks become available\n * Button order: Chapters, Captions, Caption Style, Speed, AD, Transcript, Playlist, Sign, Quality, PiP, Fullscreen\n */\n ensureCaptionsButton() {\n // Skip if captions button is disabled\n if (!this.player.options.captionsButton) return;\n \n // Skip if button already exists\n if (this.controls.captions) return;\n \n // Create the captions button\n const btn = this.createCaptionsButton();\n btn.dataset.overflowPriority = '1';\n btn.dataset.overflowPriorityMobile = '3';\n \n // Insert after chapters button, or at the start\n const chaptersButton = this.rightButtons.querySelector(`.${this.player.options.classPrefix}-chapters`);\n if (chaptersButton && chaptersButton.nextSibling) {\n this.rightButtons.insertBefore(btn, chaptersButton.nextSibling);\n } else if (chaptersButton) {\n chaptersButton.after(btn);\n } else {\n this.rightButtons.insertBefore(btn, this.rightButtons.firstChild);\n }\n \n this.player.log('Captions button added dynamically for HLS subtitles', 'info');\n }\n\n /**\n * Dynamically add caption style button if HLS subtitle tracks become available\n */\n ensureCaptionStyleButton() {\n // Skip if caption style button is disabled\n if (!this.player.options.captionStyleButton) return;\n \n // Skip if button already exists\n if (this.controls.captionStyle) return;\n \n // Create the caption style button\n const btn = this.createCaptionStyleButton();\n btn.dataset.overflowPriority = '3';\n btn.dataset.overflowPriorityMobile = '3';\n \n // Insert after captions button\n const captionsButton = this.rightButtons.querySelector(`.${this.player.options.classPrefix}-captions-button`);\n if (captionsButton) {\n captionsButton.after(btn);\n } else {\n // Insert at start if no captions button\n this.rightButtons.insertBefore(btn, this.rightButtons.firstChild);\n }\n \n this.player.log('Caption style button added dynamically for HLS subtitles', 'info');\n }\n\n /**\n * Dynamically add transcript button if HLS subtitle tracks become available\n */\n ensureTranscriptButton() {\n // Skip if transcript button is disabled\n if (!this.player.options.transcriptButton) return;\n \n // Skip if button already exists\n if (this.controls.transcript) return;\n \n // Create the transcript button\n const btn = this.createTranscriptButton();\n btn.dataset.overflowPriority = '3';\n btn.dataset.overflowPriorityMobile = '3';\n \n // Insert after AD button, or after speed button\n const adButton = this.rightButtons.querySelector(`.${this.player.options.classPrefix}-audio-description`);\n const speedButton = this.rightButtons.querySelector(`.${this.player.options.classPrefix}-speed`);\n const captionStyleButton = this.rightButtons.querySelector(`.${this.player.options.classPrefix}-caption-style`);\n \n if (adButton) {\n adButton.after(btn);\n } else if (speedButton) {\n speedButton.after(btn);\n } else if (captionStyleButton) {\n captionStyleButton.after(btn);\n } else {\n // Insert before quality/pip/fullscreen buttons\n const qualityButton = this.rightButtons.querySelector(`.${this.player.options.classPrefix}-quality`);\n const pipButton = this.rightButtons.querySelector(`.${this.player.options.classPrefix}-pip`);\n const fullscreenButton = this.rightButtons.querySelector(`.${this.player.options.classPrefix}-fullscreen`);\n const insertBefore = qualityButton || pipButton || fullscreenButton;\n \n if (insertBefore) {\n this.rightButtons.insertBefore(btn, insertBefore);\n } else {\n this.rightButtons.appendChild(btn);\n }\n }\n \n this.player.log('Transcript button added dynamically for HLS subtitles', 'info');\n }\n\n /**\n * Remove caption-related buttons if no HLS subtitle tracks are available\n * and no native caption tracks exist. Called when switching to a stream\n * without subtitles.\n * @param {boolean} force - If true, skip the native captions check and force removal\n */\n removeHlsCaptionButtons(force = false) {\n if (!force) {\n // Check if there are native caption tracks (from <track> elements in the HTML)\n // Note: For HLS streams, we pass force=true because HLS manages its own text tracks\n const trackElements = this.player.element.querySelectorAll('track[kind=\"captions\"], track[kind=\"subtitles\"]');\n if (trackElements.length > 0) {\n this.player.log('Keeping caption buttons - native track elements exist', 'info');\n return;\n }\n }\n \n // Disable all text tracks and clear captions display\n this.disableAllCaptions();\n \n // Remove captions button if it exists\n if (this.controls.captions) {\n this.controls.captions.remove();\n delete this.controls.captions;\n this.player.log('Captions button removed - no subtitle tracks', 'info');\n }\n\n // Remove caption style button if it exists\n if (this.controls.captionStyle) {\n this.controls.captionStyle.remove();\n delete this.controls.captionStyle;\n this.player.log('Caption style button removed - no subtitle tracks', 'info');\n }\n\n // Remove transcript button if it exists\n if (this.controls.transcript) {\n this.controls.transcript.remove();\n delete this.controls.transcript;\n this.player.log('Transcript button removed - no subtitle tracks', 'info');\n }\n }\n\n /**\n * Disable all caption/subtitle tracks and clear the captions display\n */\n disableAllCaptions() {\n // Disable all text tracks\n const textTracks = this.player.element.textTracks;\n for (let i = 0; i < textTracks.length; i++) {\n const tt = textTracks[i];\n if (tt) tt.mode = 'disabled';\n }\n\n // NOTE: A previous (`captionsManager`) cast lived here. The property\n // does not exist on Player and the only related manager (`captionManager`)\n // has no `hide()` method, so the block was unreachable dead code. The\n // captions container is cleared below, which was the actual intent.\n\n // Clear the captions container if it exists\n const captionsContainer = this.player.container?.querySelector(`.${this.player.options.classPrefix}-captions`);\n if (captionsContainer) {\n captionsContainer.textContent = '';\n (captionsContainer as HTMLElement).style.display = 'none';\n }\n \n // Update player state\n this.player.state.captionsEnabled = false;\n \n this.player.log('All captions disabled and cleared', 'info');\n }\n\n updateQualityIndicator() {\n if (!this.controls.qualityText) return;\n if (!this.player.renderer || !this.player.renderer.getQualities) return;\n\n const qualities = this.player.renderer.getQualities();\n if (qualities.length === 0) {\n this.controls.qualityText.textContent = '';\n return;\n }\n\n // Get current quality\n let currentQualityText = '';\n \n // Check if renderer is in auto quality mode (HLS, DASH, etc.)\n if (typeof this.player.renderer.isAutoQuality === 'function' && this.player.renderer.isAutoQuality()) {\n currentQualityText = 'Auto';\n } else if (this.player.renderer.getCurrentQuality) {\n const currentIndex = this.player.renderer.getCurrentQuality();\n const currentQuality = (qualities as QualityLevel[]).find((q) => q.index === currentIndex);\n if (currentQuality) {\n currentQualityText = currentQuality.height ? `${currentQuality.height}p` : '';\n }\n }\n\n this.controls.qualityText.textContent = currentQualityText;\n }\n\n setupAutoHide() {\n if (this.player.element.tagName !== 'VIDEO') return;\n\n const showControls = () => {\n this.element.classList.add(`${this.player.options.classPrefix}-controls-visible`);\n this.player.container.classList.add(`${this.player.options.classPrefix}-controls-visible`);\n this.player.state.controlsVisible = true;\n\n clearTimeout(this.hideTimeout);\n\n if (this.player.state.playing) {\n // Use longer delay in fullscreen mode for better mobile UX\n const delay = this.player.state.fullscreen \n ? this.player.options.hideControlsDelay * 1.5 \n : this.player.options.hideControlsDelay;\n \n this.hideTimeout = setTimeout(() => {\n this.element.classList.remove(`${this.player.options.classPrefix}-controls-visible`);\n this.player.container.classList.remove(`${this.player.options.classPrefix}-controls-visible`);\n this.player.state.controlsVisible = false;\n }, delay);\n }\n };\n\n // Mouse and touch events to show controls\n this.player.container.addEventListener('mousemove', showControls);\n this.player.container.addEventListener('touchstart', showControls);\n this.player.container.addEventListener('touchmove', showControls); // Also show on touch drag/swipe\n this.player.container.addEventListener('click', showControls);\n this.player.container.addEventListener('tap', showControls); // Some mobile browsers use tap event\n\n // Show controls on focus\n this.element.addEventListener('focusin', showControls);\n\n // Always show when paused\n this.player.on('pause', () => {\n showControls();\n clearTimeout(this.hideTimeout);\n });\n\n this.player.on('play', () => {\n showControls();\n });\n\n // Show controls when entering fullscreen (especially important for mobile landscape)\n this.player.on('enterfullscreen', () => {\n showControls();\n // In fullscreen, keep controls visible longer initially\n if (this.player.state.fullscreen) {\n clearTimeout(this.hideTimeout);\n this.hideTimeout = setTimeout(() => {\n if (this.player.state.playing) {\n this.element.classList.remove(`${this.player.options.classPrefix}-controls-visible`);\n this.player.container.classList.remove(`${this.player.options.classPrefix}-controls-visible`);\n this.player.state.controlsVisible = false;\n }\n }, this.player.options.hideControlsDelay * 2); // Double the delay in fullscreen\n }\n });\n\n // Initial state\n showControls();\n }\n\n createOverflowMenuButton() {\n const ariaLabel = i18n.t('player.moreOptions');\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-overflow-menu`,\n attributes: {\n 'type': 'button',\n 'aria-label': ariaLabel,\n 'aria-expanded': 'false'\n }\n });\n\n button.appendChild(createIconElement('moreVertical'));\n DOMUtils.attachTooltip(button, ariaLabel, this.player.options.classPrefix);\n\n button.addEventListener('click', () => {\n this.showOverflowMenu(button);\n });\n\n this.controls.overflowMenu = button;\n return button;\n }\n\n showOverflowMenu(button: HTMLElement) {\n // Remove existing menu if any (toggle behavior)\n const existingMenu = document.querySelector(`.${this.player.options.classPrefix}-overflow-menu-list`);\n if (existingMenu) {\n existingMenu.remove();\n button.setAttribute('aria-expanded', 'false');\n if (this.openMenu === existingMenu) {\n this.openMenu = null;\n this.openMenuButton = null;\n }\n return;\n }\n\n const menu = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-overflow-menu-list ${this.player.options.classPrefix}-menu`,\n attributes: {\n 'role': 'menu',\n 'aria-label': i18n.t('player.moreOptions')\n }\n });\n\n // Get all overflow buttons (those currently hidden)\n const overflowButtons: HTMLElement[] = Array.from(this.rightButtons.querySelectorAll('button[data-in-overflow=\"true\"]'));\n\n if (overflowButtons.length === 0) {\n // No overflow items — non-operable, kept out of roving navigation\n // via aria-disabled (WCAG 2.1.1, 4.1.2).\n const noItemsText = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-menu-item ${this.player.options.classPrefix}-menu-item-disabled`,\n textContent: i18n.t('player.noMoreOptions'),\n attributes: {\n 'role': 'menuitem',\n 'aria-disabled': 'true',\n 'tabindex': '-1'\n },\n style: {opacity: '0.5', cursor: 'default'}\n });\n menu.appendChild(noItemsText);\n } else {\n // Create menu items for each overflow button\n overflowButtons.forEach((btn: HTMLElement) => {\n const item = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-menu-item`,\n attributes: {\n 'type': 'button',\n 'role': 'menuitem',\n 'tabindex': '-1'\n }\n });\n\n // Get button label\n const label = btn.getAttribute('aria-label') || btn.getAttribute('title') || '';\n \n // Copy icon if present (SVG icon or text icon like \"Aa\")\n const icon = btn.querySelector('.vidply-icon');\n if (icon) {\n const iconClone = icon.cloneNode(true);\n item.appendChild(iconClone);\n } else {\n // Check for text icon (like caption styling \"Aa\")\n const firstChild = btn.querySelector('span');\n if (firstChild && firstChild.textContent && firstChild.textContent.length <= 3) {\n const iconClone = firstChild.cloneNode(true) as HTMLElement;\n iconClone.classList.add('vidply-icon');\n item.appendChild(iconClone);\n }\n }\n \n const labelSpan = DOMUtils.createElement('span', {\n textContent: label\n });\n item.appendChild(labelSpan);\n\n // When clicked, trigger the original button's click\n item.addEventListener('click', () => {\n // Store the overflow menu item as the positioning reference\n // This allows submenus to position relative to the visible menu item\n this._overflowMenuItemRef = item;\n \n // Temporarily make the original button visible for positioning\n const originalDisplay = btn.style.display;\n btn.style.display = '';\n btn.style.visibility = 'hidden'; // Keep it invisible but in layout\n \n // Trigger the button's menu\n btn.click();\n \n // Restore original state after menu is positioned\n setTimeout(() => {\n btn.style.display = originalDisplay;\n btn.style.visibility = '';\n this._overflowMenuItemRef = null;\n }, 100);\n \n // Close overflow menu\n this.closeMenuAndReturnFocus(menu, button);\n });\n\n menu.appendChild(item);\n });\n\n // Add keyboard navigation\n this.attachMenuKeyboardNavigation(menu, button);\n\n // Focus first item\n setTimeout(() => {\n const firstItem = menu.querySelector(`.${this.player.options.classPrefix}-menu-item`) as HTMLElement | null;\n if (firstItem && firstItem.tagName === 'BUTTON') {\n firstItem.focus({ preventScroll: true });\n }\n }, 0);\n }\n\n // Position menu first (before it's visible) to prevent jumping\n menu.style.visibility = 'hidden';\n menu.style.display = 'block';\n \n // Insert menu into DOM (handles fullscreen positioning)\n this.insertMenuIntoDOM(menu, button);\n \n // Position immediately (synchronously) while hidden\n this.positionMenu(menu, button, true);\n \n // Make menu visible after positioning\n requestAnimationFrame(() => {\n menu.style.visibility = 'visible';\n });\n\n // Close menu on outside click\n this.attachMenuCloseHandler(menu, button);\n }\n\n /**\n * Re-evaluate which buttons fit in the right-side area and which need to\n * be moved into the overflow (\"more options\") menu. Safe to call any\n * number of times — extracted from `setupOverflowDetection` so dynamic\n * button insertions (audio-description / sign-language) can request a\n * recheck without re-attaching observers.\n */\n checkOverflow() {\n const isDesktop = window.innerWidth >= 768;\n const isLandscape = window.innerHeight < window.innerWidth;\n const isFullscreen = this.player.state.fullscreen;\n const isLandscapeFullscreen = isLandscape && isFullscreen;\n\n if (!this.rightButtons || this.rightButtons.children.length === 0) {\n if (this.overflowMenuButton) {\n this.overflowMenuButton.style.display = 'none';\n }\n return;\n }\n\n // Get all buttons (except the overflow menu button itself and\n // buttons that opt out via data-skip-overflow=\"true\", e.g. the\n // floating-player PiP button which has its own viewport media\n // query and must never appear in the overflow menu list).\n const allButtons = (Array.from(this.rightButtons.children) as HTMLElement[]).filter(\n btn => !btn.classList.contains(`${this.player.options.classPrefix}-overflow-menu`)\n && btn.dataset.skipOverflow !== 'true'\n );\n\n if (allButtons.length === 0) {\n if (this.overflowMenuButton) {\n this.overflowMenuButton.style.display = 'none';\n }\n return;\n }\n\n // Only use overflow on mobile portrait (width < 768px and not in landscape).\n // In landscape, always show all buttons so the fullscreen button stays reachable.\n const shouldUseOverflow = !isDesktop && !isLandscape;\n\n if (this.player.options.debug) {\n console.log('Overflow detection:', {\n isDesktop,\n isFullscreen,\n isLandscape,\n isLandscapeFullscreen,\n shouldUseOverflow,\n width: window.innerWidth,\n height: window.innerHeight\n });\n }\n\n if (!shouldUseOverflow) {\n allButtons.forEach(btn => {\n btn.dataset.inOverflow = 'false';\n btn.style.display = '';\n });\n if (this.overflowMenuButton) {\n this.overflowMenuButton.style.display = 'none';\n }\n if (this.player.options.debug) {\n console.log('No overflow menu needed - all buttons visible, overflow button hidden');\n }\n return;\n }\n\n if (this.player.options.debug) {\n console.log('Mobile portrait - checking for overflow...');\n }\n\n allButtons.forEach(btn => {\n btn.style.display = '';\n });\n\n const containerWidth = this.rightButtons.offsetWidth;\n const overflowButtonWidth = 50;\n const availableWidth = containerWidth - overflowButtonWidth;\n\n let totalWidth = 0;\n const buttonWidths = allButtons.map(btn => {\n const style = getComputedStyle(btn);\n const width = btn.offsetWidth +\n parseInt(style.marginLeft || '0') +\n parseInt(style.marginRight || '0');\n totalWidth += width;\n return {btn, width};\n });\n\n const gapWidth = 8;\n totalWidth += (allButtons.length - 1) * gapWidth;\n\n const isSmallScreen = window.innerWidth < 768;\n const needsOverflow = totalWidth > availableWidth || isSmallScreen || (isLandscapeFullscreen && !isDesktop);\n\n if (this.player.options.debug) {\n console.log('Overflow detection:', {\n containerWidth,\n availableWidth,\n totalWidth,\n needsOverflow,\n isSmallScreen,\n reason: isSmallScreen ? 'mobile screen' : (totalWidth > availableWidth ? 'not enough space' : 'enough space'),\n buttonCount: allButtons.length\n });\n }\n\n if (needsOverflow) {\n const priorityAttr = isSmallScreen ? 'overflowPriorityMobile' : 'overflowPriority';\n\n if (this.player.options.debug) {\n console.log(`Using ${isSmallScreen ? 'mobile' : 'desktop'} priorities (width: ${window.innerWidth}px)`);\n }\n\n const sortedButtons = buttonWidths.sort((a, b) => {\n const priorityA = parseInt(a.btn.dataset[priorityAttr] || a.btn.dataset.overflowPriority || '1');\n const priorityB = parseInt(b.btn.dataset[priorityAttr] || b.btn.dataset.overflowPriority || '1');\n return priorityB - priorityA;\n });\n\n let currentWidth = totalWidth;\n let movedToOverflow = 0;\n\n for (const {btn, width} of sortedButtons) {\n const priority = parseInt(btn.dataset[priorityAttr] || btn.dataset.overflowPriority || '1');\n const buttonLabel = btn.getAttribute('aria-label') || 'unknown';\n\n if (priority === 1) {\n btn.dataset.inOverflow = 'false';\n btn.style.display = '';\n continue;\n }\n\n const shouldHide = isSmallScreen ? (priority > 1) : (currentWidth > availableWidth);\n\n if (shouldHide) {\n btn.dataset.inOverflow = 'true';\n btn.style.display = 'none';\n currentWidth -= width;\n movedToOverflow++;\n if (this.player.options.debug) {\n console.log(` → Hiding button: ${buttonLabel} (priority ${priority}, ${isSmallScreen ? 'mobile' : 'desktop'})`);\n }\n } else {\n btn.dataset.inOverflow = 'false';\n btn.style.display = '';\n }\n }\n\n if (this.player.options.debug) {\n console.log('Overflow button exists?', Boolean(this.overflowMenuButton));\n }\n\n if (!this.overflowMenuButton) {\n console.error('Overflow menu button not found!');\n return;\n }\n\n if (movedToOverflow > 0) {\n this.overflowMenuButton.style.display = '';\n if (this.player.options.debug) {\n console.log('Showing overflow menu button -', movedToOverflow, 'buttons moved');\n }\n } else {\n this.overflowMenuButton.style.display = 'none';\n if (this.player.options.debug) {\n console.log('Hiding overflow menu button - all buttons fit');\n }\n }\n } else {\n allButtons.forEach(btn => {\n btn.dataset.inOverflow = 'false';\n btn.style.display = '';\n });\n if (this.overflowMenuButton) {\n this.overflowMenuButton.style.display = 'none';\n }\n }\n }\n\n setupOverflowDetection() {\n const signal = this.player.lifecycleSignal;\n // Every deferred callback checks the lifecycle signal first so a\n // player destroyed within the 500ms initial-check window does not\n // poke at a torn-down ControlBar.\n const checkOverflow = () => {\n if (signal.aborted) return;\n this.checkOverflow();\n };\n\n // Check on resize\n const resizeObserver = new ResizeObserver(() => {\n requestAnimationFrame(checkOverflow);\n });\n resizeObserver.observe(this.rightButtons);\n\n // Check on window resize. Tied to the player's lifecycle so the\n // listener is removed when the player is destroyed.\n window.addEventListener('resize', () => {\n requestAnimationFrame(checkOverflow);\n }, { signal });\n\n // Check on fullscreen changes (important for desktop/tablet fullscreen)\n this.player.on('fullscreenchange', () => {\n // Use setTimeout to ensure fullscreen state is fully updated\n setTimeout(() => {\n requestAnimationFrame(checkOverflow);\n }, 50);\n });\n\n // Initial checks at multiple intervals to ensure layout is stable.\n // Some browsers need more time for font loading, CSS rendering, etc.\n requestAnimationFrame(() => {\n checkOverflow();\n setTimeout(() => checkOverflow(), 100);\n setTimeout(() => checkOverflow(), 300);\n setTimeout(() => checkOverflow(), 500);\n });\n\n // Also check when fonts are loaded\n if (document.fonts && document.fonts.ready) {\n document.fonts.ready.then(() => {\n requestAnimationFrame(checkOverflow);\n });\n }\n\n this.overflowResizeObserver = resizeObserver;\n }\n\n show() {\n this.element.style.display = '';\n }\n\n hide() {\n this.element.style.display = 'none';\n }\n\n /**\n * Update preview video source when player source changes (for playlists)\n * Also re-initializes if preview wasn't set up initially\n */\n updatePreviewVideoSource() {\n const renderer = this.player.renderer;\n if (!renderer || !renderer.media || renderer.media.tagName !== 'VIDEO') {\n return;\n }\n\n // If preview wasn't initialized yet, try to initialize it now\n if (!this.previewSupported && !this.previewVideo) {\n this.initPreviewThumbnail();\n }\n\n if (!this.previewSupported || !this.previewVideo) {\n return;\n }\n\n const mainVideo = renderer.media;\n const newSrc = mainVideo.src || mainVideo.querySelector('source')?.src;\n \n if (newSrc && this.previewVideo.src !== newSrc) {\n // Clear cache when source changes\n this.previewThumbnailCache.clear();\n this.previewVideoReady = false;\n this.previewVideo.src = newSrc;\n \n // Copy crossOrigin if set\n if (mainVideo.crossOrigin) {\n this.previewVideo.crossOrigin = mainVideo.crossOrigin;\n }\n \n // Wait for new source to load\n this.previewVideo.addEventListener('loadedmetadata', () => {\n this.previewVideoReady = true;\n }, { once: true });\n } else if (newSrc && !this.previewVideoReady && this.previewVideo.readyState >= 1) {\n // If source is the same but video is ready, mark as ready\n this.previewVideoReady = true;\n }\n }\n\n /**\n * Cleanup preview thumbnail resources\n */\n cleanupPreviewThumbnail() {\n if (this.previewThumbnailTimeout) {\n clearTimeout(this.previewThumbnailTimeout);\n this.previewThumbnailTimeout = null;\n }\n \n if (this.previewVideo && this.previewVideo.parentNode) {\n this.previewVideo.parentNode.removeChild(this.previewVideo);\n this.previewVideo = null;\n }\n \n this.previewThumbnailCache.clear();\n }\n\n destroy() {\n if (this.hideTimeout) {\n clearTimeout(this.hideTimeout);\n }\n\n if (this.overflowResizeObserver) {\n this.overflowResizeObserver.disconnect();\n }\n\n // Cleanup preview thumbnail resources\n this.cleanupPreviewThumbnail();\n\n if (this.element && this.element.parentNode) {\n this.element.parentNode.removeChild(this.element);\n }\n }\n}\n\n", "/**\n * Keyboard Accessibility Manager\n */\n\nimport { i18n } from '../i18n/i18n.js';\nimport { debounce } from '../utils/PerformanceUtils.js';\nimport type { Player } from '../core/Player.js';\nimport type { KeyboardShortcuts } from '../types/options.js';\n\nexport class KeyboardManager {\n player: Player;\n shortcuts: KeyboardShortcuts;\n private announcer: HTMLElement | null = null;\n // Announcements are driven by player state-change events so they fire for\n // mouse/touch control use too, not only keyboard shortcuts (WCAG 4.1.3).\n // Gated until 'ready' so initial volume/mute/source setup stays silent.\n private _announceReady = false;\n private _prevMuted: boolean;\n private _stateAnnouncers: Array<{ event: string; handler: (...args: unknown[]) => void }> = [];\n private _announceVolume: () => void;\n\n constructor(player: Player) {\n this.player = player;\n this.shortcuts = player.options.keyboardShortcuts;\n this._prevMuted = player.state.muted;\n // Volume slider drags emit a stream of volumechange events; debounce the\n // level announcement so AT hears the final value instead of every step.\n this._announceVolume = debounce(() => {\n const percent = Math.round(this.player.state.volume * 100);\n this.announce(i18n.t('player.volumePercent', { percent }));\n }, 500);\n\n this.init();\n }\n\n init() {\n this.attachEvents();\n this.attachStateAnnouncements();\n }\n\n /**\n * Subscribe to player state-change events so play/pause, mute, volume,\n * captions, fullscreen and speed changes are announced to assistive tech\n * regardless of whether the user used the keyboard, mouse or touch\n * (WCAG 4.1.3 Status Messages).\n */\n attachStateAnnouncements(): void {\n // Defensive: the player exposes an EventEmitter `on`/`off` API at runtime.\n if (typeof this.player.on !== 'function') return;\n\n const onReady = () => { this._announceReady = true; };\n this.player.on('ready', onReady);\n\n const register = (event: string, handler: () => void) => {\n const wrapped = () => { if (this._announceReady) handler(); };\n this.player.on(event as never, wrapped as never);\n this._stateAnnouncers.push({ event, handler: wrapped as (...args: unknown[]) => void });\n };\n\n // Track the 'ready' handler too so destroy() detaches it.\n this._stateAnnouncers.push({ event: 'ready', handler: onReady as (...args: unknown[]) => void });\n\n register('play', () => this.announce(i18n.t('player.playing')));\n register('pause', () => this.announce(i18n.t('player.paused')));\n register('captionsenabled', () => this.announce(i18n.t('player.captionsOn')));\n register('captionsdisabled', () => this.announce(i18n.t('player.captionsOff')));\n register('fullscreenchange', () => {\n this.announce(this.player.state.fullscreen\n ? i18n.t('player.fullscreen')\n : i18n.t('player.exitFullscreen'));\n });\n register('ratechange', () => {\n const rate = this.player.state.playbackSpeed;\n this.announce(i18n.t('player.speedRate', { rate: String(rate) }));\n });\n register('volumechange', () => {\n // Mute toggles are discrete and announced immediately; continuous\n // level changes are debounced via _announceVolume.\n if (this.player.state.muted !== this._prevMuted) {\n this._prevMuted = this.player.state.muted;\n this.announce(this.player.state.muted\n ? i18n.t('player.muted')\n : i18n.t('player.unmuted'));\n return;\n }\n if (!this.player.state.muted) {\n this._announceVolume();\n }\n });\n }\n\n attachEvents() {\n // Listen for keyboard events on the player container using CAPTURE phase\n // This ensures we intercept events before they reach child elements\n this.player.container.addEventListener('keydown', (e: KeyboardEvent) => {\n this.handleKeydown(e);\n }, true); // Use capture phase\n\n // Make player container focusable\n if (!this.player.container.hasAttribute('tabindex')) {\n this.player.container.setAttribute('tabindex', '0');\n }\n }\n\n handleKeydown(e: KeyboardEvent) {\n // Don't handle if target is an input element\n const target = e.target as HTMLElement;\n if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.tagName === 'SELECT') {\n return;\n }\n \n // Don't handle if focus is inside a menu (let menu handle its own keyboard navigation)\n const activeElement = document.activeElement;\n if (activeElement) {\n const menu = activeElement.closest('.vidply-menu, [role=\"menu\"]');\n if (menu) {\n return; // Let the menu handle keyboard events\n }\n \n // Don't handle if focus is on a playlist button (let playlist handle navigation)\n const playlistButton = activeElement.closest('.vidply-playlist-item-button');\n if (playlistButton) {\n return; // Let the playlist handle keyboard events\n }\n\n // Don't steal arrow keys when the sign-language overlay is in keyboard drag/resize mode.\n // DraggableResizable listens on the overlay itself, but we run in CAPTURE phase, so we must opt out here.\n const signWrapper = activeElement.closest('.vidply-sign-language-wrapper');\n if (signWrapper) {\n const draggable = this.player.signLanguageManager?.draggable;\n if (draggable?.keyboardDragMode || draggable?.keyboardResizeMode) {\n return;\n }\n }\n\n // Same idea for the transcript floating window (it also uses DraggableResizable).\n const transcriptWindow = activeElement.closest('.vidply-transcript-window');\n if (transcriptWindow) {\n const draggable = this.player.transcriptManager?.draggableResizable;\n if (draggable?.keyboardDragMode || draggable?.keyboardResizeMode) {\n return;\n }\n }\n }\n\n const key = e.key;\n let handled = false;\n\n // Special handling for ESC key - exit fullscreen (especially for iOS pseudo-fullscreen)\n if (key === 'Escape' && this.player.state.fullscreen) {\n this.player.exitFullscreen();\n e.preventDefault();\n e.stopPropagation();\n return;\n }\n\n // Check each shortcut category\n for (const [action, keys] of Object.entries(this.shortcuts)) {\n if ((keys as string[]).includes(key)) {\n handled = this.executeAction(action, e);\n if (handled) {\n e.preventDefault();\n e.stopPropagation();\n // Announcements are handled centrally by attachStateAnnouncements()\n // via player events, so they fire for pointer interactions too.\n break;\n }\n }\n }\n \n // Log unhandled keys for debugging (in development)\n if (!handled && this.player.options.debug) {\n console.log('[VidPly] Unhandled key:', e.key, 'code:', e.code, 'shiftKey:', e.shiftKey);\n }\n }\n\n executeAction(action: string, _event: KeyboardEvent): boolean {\n switch (action) {\n case 'play-pause':\n this.player.toggle();\n return true;\n\n case 'volume-up':\n this.player.setVolume(Math.min(1, this.player.state.volume + 0.1));\n return true;\n\n case 'volume-down':\n this.player.setVolume(Math.max(0, this.player.state.volume - 0.1));\n return true;\n\n case 'seek-forward':\n this.player.seekForward();\n return true;\n\n case 'seek-backward':\n this.player.seekBackward();\n return true;\n\n case 'mute':\n this.player.toggleMute();\n return true;\n\n case 'fullscreen':\n this.player.toggleFullscreen();\n return true;\n\n case 'captions':\n if (this.player.captionManager && this.player.captionManager.tracks.length > 1) {\n const captionsButton = this.player.controlBar?.controls.captions;\n if (captionsButton && this.player.controlBar) {\n this.player.controlBar.showCaptionsMenu(captionsButton);\n } else {\n this.player.toggleCaptions();\n }\n } else {\n this.player.toggleCaptions();\n }\n return true;\n\n case 'caption-style-menu':\n // Open caption style menu\n if (this.player.controlBar && this.player.controlBar.controls.captionStyle) {\n this.player.controlBar.showCaptionStyleMenu(this.player.controlBar.controls.captionStyle);\n return true;\n }\n return false;\n\n case 'speed-up':\n this.player.setPlaybackSpeed(\n Math.min(2, this.player.state.playbackSpeed + 0.25)\n );\n return true;\n\n case 'speed-down':\n this.player.setPlaybackSpeed(\n Math.max(0.25, this.player.state.playbackSpeed - 0.25)\n );\n return true;\n\n case 'speed-menu':\n // Open speed menu\n if (this.player.controlBar && this.player.controlBar.controls.speed) {\n this.player.controlBar.showSpeedMenu(this.player.controlBar.controls.speed);\n return true;\n }\n return false;\n\n case 'quality-menu':\n // Open quality menu\n if (this.player.controlBar && this.player.controlBar.controls.quality) {\n this.player.controlBar.showQualityMenu(this.player.controlBar.controls.quality);\n return true;\n }\n return false;\n\n case 'chapters-menu':\n // Open chapters menu\n if (this.player.controlBar && this.player.controlBar.controls.chapters) {\n this.player.controlBar.showChaptersMenu(this.player.controlBar.controls.chapters);\n return true;\n }\n return false;\n\n case 'transcript-toggle':\n // Toggle transcript\n if (this.player.transcriptManager) {\n this.player.transcriptManager.toggleTranscript();\n return true;\n }\n return false;\n\n default:\n return false;\n }\n }\n\n announceAction(action: string): void {\n if (!this.player.options.screenReaderAnnouncements) return;\n\n // Every announcement now goes through i18n.t so the screen-reader\n // hears the player's configured language instead of always English.\n // Missing keys fall back to the same English literal used previously,\n // so behavior is unchanged for unconfigured locales.\n let message = '';\n\n switch (action) {\n case 'play-pause':\n message = this.player.state.playing\n ? i18n.t('player.playing')\n : i18n.t('player.paused');\n break;\n case 'volume-up':\n case 'volume-down': {\n const percent = Math.round(this.player.state.volume * 100);\n message = i18n.t('player.volumePercent', { percent });\n break;\n }\n case 'mute':\n message = this.player.state.muted\n ? i18n.t('player.muted')\n : i18n.t('player.unmuted');\n break;\n case 'fullscreen':\n message = this.player.state.fullscreen\n ? i18n.t('player.fullscreen')\n : i18n.t('player.exitFullscreen');\n break;\n case 'captions':\n message = this.player.state.captionsEnabled\n ? i18n.t('player.captionsOn')\n : i18n.t('player.captionsOff');\n break;\n case 'speed-up':\n case 'speed-down': {\n const rate = this.player.state.playbackSpeed;\n message = i18n.t('player.speedRate', { rate: String(rate) });\n break;\n }\n }\n\n if (message) {\n this.announce(message);\n }\n }\n\n /**\n * Live-region announcer scoped to *this* player instance so multi-player\n * pages do not cross-talk through a shared `#vidply-announcer` id. The\n * region is appended to `document.body` so it is reachable regardless of\n * the embedding container's stacking / overflow context.\n */\n announce(message: string, priority: 'polite' | 'assertive' = 'polite'): void {\n if (!this.announcer) {\n const id = `vidply-announcer-${this.player.instanceId}`;\n this.announcer = document.createElement('div');\n this.announcer.id = id;\n this.announcer.className = 'vidply-sr-only';\n this.announcer.setAttribute('aria-live', priority);\n this.announcer.setAttribute('aria-atomic', 'true');\n this.announcer.style.cssText = `\n position: absolute;\n left: -10000px;\n width: 1px;\n height: 1px;\n overflow: hidden;\n `;\n document.body.appendChild(this.announcer);\n } else {\n this.announcer.setAttribute('aria-live', priority);\n }\n\n this.announcer.textContent = '';\n const announcer = this.announcer;\n setTimeout(() => {\n if (announcer) announcer.textContent = message;\n }, 100);\n }\n\n updateShortcut(action: string, keys: string[]): void {\n if (Array.isArray(keys)) {\n (this.shortcuts as Record<string, string[]>)[action] = keys;\n }\n }\n\n destroy(): void {\n // Detach the player state-change listeners that drive announcements.\n if (typeof this.player.off === 'function') {\n for (const { event, handler } of this._stateAnnouncers) {\n this.player.off(event as never, handler as never);\n }\n }\n this._stateAnnouncers = [];\n\n // Container-attached keydown listener is removed when the container is\n // destroyed by Player.destroy(). The live-region announcer, however,\n // lives on `document.body` so we must remove it explicitly.\n if (this.announcer && this.announcer.parentNode) {\n this.announcer.parentNode.removeChild(this.announcer);\n }\n this.announcer = null;\n }\n}\n\n", "/**\n * URL sanitisation helpers for values that flow into `<video>.poster`\n * attributes or CSS `url(...)` declarations.\n *\n * Both helpers used to live as file-local functions inside `Player.ts`,\n * which meant `PlaylistManager` interpolated raw `track.poster` values\n * into `backgroundImage` without any validation. They now live here so\n * every consumer of untrusted poster/artwork URLs can share the same\n * allow-list + CSS escape.\n */\n\n/**\n * Validate a poster/artwork URL before interpolating it into a CSS\n * `url(...)` value or assigning it to `<video>.poster`. Allows `https:`,\n * `http:`, `data:image/<png|jpeg|webp|gif|svg+xml>;...`, root-relative\n * paths starting with `/`, and same-origin relative paths.\n *\n * Returns `null` for anything suspicious — callers must check for null\n * and fall back to a safe default (e.g. skip setting `backgroundImage`).\n */\nexport function sanitizePosterUrl(input: unknown): string | null {\n if (typeof input !== 'string' || input.length === 0 || input.length > 4096) {\n return null;\n }\n const trimmed = input.trim();\n if (!trimmed) return null;\n // Whitespace, quotes, angle brackets and backslashes would let a\n // crafted value break out of a CSS declaration or HTML attribute.\n if (/[\\s\"'<>\\\\]/.test(trimmed)) return null;\n\n if (trimmed.startsWith('/') || trimmed.startsWith('./') || trimmed.startsWith('../')) {\n return trimmed;\n }\n try {\n const url = new URL(trimmed, typeof window !== 'undefined' ? window.location.href : 'http://localhost/');\n if (url.protocol === 'https:' || url.protocol === 'http:') {\n return url.href;\n }\n if (url.protocol === 'data:' && /^data:image\\/(png|jpeg|jpg|webp|gif|svg\\+xml);/i.test(trimmed)) {\n return trimmed;\n }\n } catch {\n return null;\n }\n return null;\n}\n\n/**\n * CSS-escape an already-validated URL for safe interpolation into a\n * `url(...)` value. Defense in depth alongside {@link sanitizePosterUrl}:\n * even if the URL slipped through, the CSS parser cannot leave the\n * `url()` context because `\"`, `(`, `)` and `\\` are all escaped.\n */\nexport function cssEscapeUrl(url: string): string {\n return url.replace(/[\"()\\\\]/g, (m) => `\\\\${m}`);\n}\n\n/**\n * Convenience wrapper: given an unknown input, return a CSS\n * `url(\"escaped-safe-url\")` string ready to assign to\n * `element.style.backgroundImage`, or `null` if the input is unsafe\n * (so the caller can leave the current background untouched instead\n * of clearing it).\n */\nexport function toCssBackgroundImage(input: unknown): string | null {\n const safe = sanitizePosterUrl(input);\n if (!safe) return null;\n return `url(\"${cssEscapeUrl(safe)}\")`;\n}\n", "/**\n * Lazy instantiation helpers for {@link Player}.\n *\n * Extracted from `index.ts` so that:\n *\n * 1. The `Player.observeLazy` public API is a real static method on the\n * Player class rather than a side-effectful assignment done from\n * `index.ts` at module load time.\n * 2. Consumers that want the lazy-init API without the auto-init\n * entry point can import this module explicitly.\n * 3. Both the auto-init flow (`initializePlayers`) and the manual flow\n * (`Player.observeLazy`) share the same `IntersectionObserver`\n * bookkeeping, so cancellation and rect-based eager fallbacks stay\n * consistent.\n */\n\nexport type LazyHandle = { cancel: () => void } | null;\n\nexport interface LazyInitEntry<TOptions> {\n observer: IntersectionObserver;\n options: TOptions;\n}\n\n/** Factory the observer invokes once an element is ready to promote. */\nexport type LazyPlayerFactory<TOptions> = (\n element: HTMLElement,\n options: TOptions\n) => void;\n\nconst pendingByElement = new WeakMap<HTMLElement, LazyInitEntry<unknown>>();\n\n/**\n * Begin observing `element` and construct a player when it scrolls into\n * view (or immediately if its rect is too small to be a meaningful\n * intersection target). Safe to call multiple times — a duplicate call\n * cancels the previous observer first.\n */\nexport function observeForLazyInit<TOptions>(\n element: HTMLElement,\n options: TOptions,\n margin: string,\n factory: LazyPlayerFactory<TOptions>\n): void {\n const existing = pendingByElement.get(element);\n if (existing) {\n existing.observer.unobserve(element);\n pendingByElement.delete(element);\n }\n\n const rect = element.getBoundingClientRect();\n if (rect.height < 20) {\n factory(element, options);\n return;\n }\n\n const observer = new IntersectionObserver(\n (entries) => {\n entries.forEach((entry) => {\n if (entry.isIntersecting) {\n observer.unobserve(entry.target);\n pendingByElement.delete(entry.target as HTMLElement);\n factory(entry.target as HTMLElement, options);\n }\n });\n },\n { rootMargin: margin, threshold: 0 }\n );\n\n observer.observe(element);\n pendingByElement.set(element, { observer, options } as LazyInitEntry<unknown>);\n}\n\n/** Cancel a scheduled lazy init for `element`, if any. */\nexport function cancelLazyInit(element: HTMLElement): void {\n const pending = pendingByElement.get(element);\n if (pending) {\n pending.observer.unobserve(element);\n pendingByElement.delete(element);\n }\n}\n", "/**\n * Pseudo-fullscreen fallback for browsers / platforms (chiefly iOS\n * Safari) where the real Fullscreen API is unavailable for video\n * elements.\n *\n * Extracted from the Player class so the scroll-lock, background-inert\n * and viewport-hijack logic lives in one focussed place. The Player\n * still owns `state.fullscreen` and emits the `fullscreenchange`\n * events — this controller only drives the DOM side effects.\n */\n\nimport type { Player } from './Player.js';\n\nexport class PseudoFullscreenController {\n private readonly player: Player;\n\n // All of the \"remember current style / scroll / viewport\" slots used\n // to restore state on exit. Kept private so the rest of the code\n // base cannot poke into them.\n private originalScrollX?: number;\n private originalScrollY?: number;\n private originalBodyOverflow?: string;\n private originalBodyPosition?: string;\n private originalBodyWidth?: string;\n private originalBodyHeight?: string;\n private originalHtmlOverflow?: string;\n private originalBodyBackground?: string;\n private originalHtmlBackground?: string;\n private originalViewport?: string | null;\n private inertElements: Element[] = [];\n\n constructor(player: Player) {\n this.player = player;\n }\n\n enable(): void {\n const { player } = this;\n player.state.fullscreen = true;\n player.container.classList.add(`${player.options.classPrefix}-fullscreen`);\n\n // CSS fallback for browsers without :has() support.\n document.body.classList.add('vidply-fullscreen-active');\n\n // Stash the current scroll position so we can restore it on exit.\n this.originalScrollX = window.scrollX || window.pageXOffset;\n this.originalScrollY = window.scrollY || window.pageYOffset;\n\n // Prevent body/html from scrolling while the player covers the viewport.\n this.originalBodyOverflow = document.body.style.overflow;\n this.originalBodyPosition = document.body.style.position;\n this.originalBodyWidth = document.body.style.width;\n this.originalBodyHeight = document.body.style.height;\n this.originalHtmlOverflow = document.documentElement.style.overflow;\n this.originalBodyBackground = document.body.style.background;\n this.originalHtmlBackground = document.documentElement.style.background;\n\n document.body.style.overflow = 'hidden';\n document.body.style.width = '100%';\n document.body.style.height = '100%';\n document.body.style.background = '#000';\n document.documentElement.style.overflow = 'hidden';\n document.documentElement.style.background = '#000';\n\n // iOS: reset the viewport scale so the player fills the screen, but never\n // disable user scaling. Setting maximum-scale=1.0 / user-scalable=no would\n // block pinch-zoom and fail WCAG 1.4.4 (Resize Text) and 1.4.10 (Reflow).\n this.originalViewport = document.querySelector('meta[name=\"viewport\"]')?.getAttribute('content');\n const viewport = document.querySelector('meta[name=\"viewport\"]');\n if (viewport) {\n viewport.setAttribute('content', 'width=device-width, initial-scale=1.0');\n }\n\n // Scroll to top on iOS to prevent positioning issues.\n window.scrollTo(0, 0);\n\n // Make the rest of the page inert so keyboard focus cannot escape\n // to background content that is visually covered but still alive.\n this.makeBackgroundInert();\n\n player.emit('fullscreenchange', true);\n player.emit('enterfullscreen');\n }\n\n /**\n * Make every sibling of the player container (walking up to the body)\n * `inert`. Scripts/styles are skipped so layout-time mutations still\n * work. Elements that were already inert are left alone so we don't\n * accidentally clear another author's inert marker on exit.\n *\n * Public because the real Fullscreen API handler also calls it — we\n * need the same inert treatment when the browser grants real\n * fullscreen, not only in the pseudo-fallback path.\n */\n makeBackgroundInert(): void {\n this.inertElements = [];\n let current: HTMLElement | null = this.player.container;\n while (current && current !== document.body && current !== document.documentElement) {\n const parentElement: HTMLElement | null = current.parentElement;\n if (parentElement) {\n Array.from(parentElement.children).forEach((sibling: Element) => {\n if (\n sibling !== current &&\n sibling.nodeType === Node.ELEMENT_NODE &&\n !sibling.hasAttribute('inert') &&\n sibling.tagName !== 'SCRIPT' &&\n sibling.tagName !== 'STYLE' &&\n sibling.tagName !== 'LINK' &&\n sibling.tagName !== 'META'\n ) {\n sibling.setAttribute('inert', '');\n this.inertElements.push(sibling);\n }\n });\n }\n current = parentElement;\n }\n }\n\n /** Public counterpart of {@link makeBackgroundInert}. */\n restoreBackgroundInteractivity(): void {\n if (this.inertElements.length > 0) {\n for (const el of this.inertElements) {\n el.removeAttribute('inert');\n }\n this.inertElements = [];\n }\n }\n\n disable(): void {\n document.body.classList.remove('vidply-fullscreen-active');\n\n this.restoreBackgroundInteractivity();\n\n if (this.originalBodyOverflow !== undefined) {\n document.body.style.overflow = this.originalBodyOverflow;\n this.originalBodyOverflow = undefined;\n }\n if (this.originalBodyPosition !== undefined) {\n document.body.style.position = this.originalBodyPosition;\n this.originalBodyPosition = undefined;\n }\n if (this.originalBodyWidth !== undefined) {\n document.body.style.width = this.originalBodyWidth;\n this.originalBodyWidth = undefined;\n }\n if (this.originalBodyHeight !== undefined) {\n document.body.style.height = this.originalBodyHeight;\n this.originalBodyHeight = undefined;\n }\n if (this.originalHtmlOverflow !== undefined) {\n document.documentElement.style.overflow = this.originalHtmlOverflow;\n this.originalHtmlOverflow = undefined;\n }\n if (this.originalBodyBackground !== undefined) {\n document.body.style.background = this.originalBodyBackground;\n this.originalBodyBackground = undefined;\n }\n if (this.originalHtmlBackground !== undefined) {\n document.documentElement.style.background = this.originalHtmlBackground;\n this.originalHtmlBackground = undefined;\n }\n\n if (this.originalViewport !== undefined) {\n const viewport = document.querySelector('meta[name=\"viewport\"]');\n if (viewport && this.originalViewport !== null) {\n viewport.setAttribute('content', this.originalViewport);\n }\n this.originalViewport = undefined;\n }\n\n if (this.originalScrollX !== undefined && this.originalScrollY !== undefined) {\n window.scrollTo(this.originalScrollX, this.originalScrollY);\n this.originalScrollX = undefined;\n this.originalScrollY = undefined;\n }\n\n this.player.emit('exitfullscreen');\n }\n}\n", "/**\n * Theme + CSS custom-property support extracted from Player.\n *\n * Every path that used to live on `Player.applyTheme` /\n * `Player.setTheme` / `Player.setThemeVariable` / `Player.resetTheme`\n * now routes through this module. Player keeps thin public delegates\n * so existing consumers (`player.setTheme('light')`, etc.) keep\n * working unchanged.\n *\n * The validation rules are deliberately strict: names must start with\n * `--vidply-` followed by `[A-Za-z0-9_-]+`, values must be short and\n * must not contain characters that would let a malicious override\n * escape the declaration or rule. That way a consumer that passes\n * user-supplied text through `setThemeVariable` cannot accidentally\n * turn the player into a CSS-injection sink.\n */\n\nimport { isForbiddenKey } from '../utils/Sanitize.js';\nimport type { Player } from './Player.js';\n\nexport type ThemeName = 'dark' | 'light' | 'minimal' | 'high-contrast';\n\nexport const PLAYER_THEMES: readonly ThemeName[] = ['dark', 'light', 'minimal', 'high-contrast'];\n\n/**\n * Validate a CSS variable name before it reaches `setProperty`. The\n * browser would silently drop unparseable names; the bigger risk is an\n * attacker smuggling `;` / `:` to escape into another declaration, so\n * the allow-list is intentionally narrow.\n */\nexport function isValidThemeVariableName(name: string): boolean {\n return /^--vidply-[A-Za-z0-9_-]{1,64}$/.test(name);\n}\n\nexport function isValidThemeVariableValue(value: unknown): value is string {\n if (typeof value !== 'string') return false;\n if (value.length > 200) return false;\n return !/[<>{};@\\\\]/.test(value);\n}\n\nexport class ThemeManager {\n private readonly player: Player;\n\n constructor(player: Player) {\n this.player = player;\n }\n\n /**\n * Apply `options.theme` and validate-and-apply every entry in\n * `options.themeVariables` to the container. Bad entries are logged\n * and skipped so a single malformed override cannot poison siblings.\n */\n apply(): void {\n const player = this.player;\n if (!player.container) return;\n\n // Clear any previous theme class so switching themes at runtime\n // does not leave two active theme classes on the element.\n const themeClasses = PLAYER_THEMES.map(t => `${player.options.classPrefix}-theme-${t}`);\n player.container.classList.remove(...themeClasses);\n\n const theme = player.options.theme as ThemeName | undefined;\n if (theme && (PLAYER_THEMES as readonly string[]).includes(theme)) {\n player.container.classList.add(`${player.options.classPrefix}-theme-${theme}`);\n }\n\n if (player.options.themeVariables && typeof player.options.themeVariables === 'object') {\n for (const [rawKey, rawValue] of Object.entries(player.options.themeVariables)) {\n if (isForbiddenKey(rawKey)) continue;\n const cssVar = rawKey.startsWith('--vidply-') ? rawKey : `--vidply-${rawKey}`;\n if (!isValidThemeVariableName(cssVar)) {\n player.log(`[VidPly] Ignoring invalid theme variable name: ${rawKey}`, 'warn');\n continue;\n }\n if (!isValidThemeVariableValue(rawValue)) {\n player.log(`[VidPly] Ignoring invalid theme variable value for ${cssVar}`, 'warn');\n continue;\n }\n player.container.style.setProperty(cssVar, rawValue);\n }\n }\n }\n\n /**\n * Swap the active theme at runtime. Emits `themechange` with the old\n * and new names so external consumers (e.g. telemetry) can react.\n */\n set(themeName: ThemeName, customVariables: Record<string, string> = {}): void {\n const player = this.player;\n const previousTheme = player.options.theme;\n\n player.options.theme = themeName;\n\n if (customVariables && Object.keys(customVariables).length > 0) {\n player.options.themeVariables = {\n ...player.options.themeVariables,\n ...customVariables\n };\n }\n\n this.apply();\n\n player.emit('themechange', {\n theme: themeName,\n previousTheme,\n customVariables: player.options.themeVariables\n });\n }\n\n get(): ThemeName | undefined {\n return this.player.options.theme as ThemeName | undefined;\n }\n\n /** Set a single CSS variable override, validating the (name, value)\n * pair before it reaches the DOM. Callers must pass a string value. */\n setVariable(variableName: string, value: string): void {\n const player = this.player;\n if (!player.container) return;\n\n const cssVar = variableName.startsWith('--vidply-')\n ? variableName\n : `--vidply-${variableName}`;\n\n if (!isValidThemeVariableName(cssVar) || !isValidThemeVariableValue(value)) {\n player.log(`[VidPly] Ignoring unsafe setThemeVariable(${variableName})`, 'warn');\n return;\n }\n\n player.container.style.setProperty(cssVar, value);\n\n if (!player.options.themeVariables) {\n player.options.themeVariables = {};\n }\n player.options.themeVariables[variableName] = value;\n }\n\n /**\n * Reset to the default theme (dark) and clear every override that was\n * applied through `options.themeVariables`.\n */\n reset(): void {\n const player = this.player;\n if (player.container && player.options.themeVariables) {\n Object.keys(player.options.themeVariables).forEach(key => {\n const cssVar = key.startsWith('--vidply-') ? key : `--vidply-${key}`;\n player.container.style.removeProperty(cssVar);\n });\n }\n\n const previousTheme = player.options.theme;\n player.options.theme = 'dark';\n player.options.themeVariables = {};\n\n this.apply();\n player.emit('themechange', { theme: 'dark', previousTheme });\n }\n}\n", "/**\n * Poster / artwork helpers lifted out of Player.\n *\n * The manager owns:\n * - relative -> absolute URL resolution;\n * - the canvas-based \"grab a frame from the video as poster\" logic;\n * - the `.vidply-forced-poster` overlay that shows the poster while\n * the underlying `<video>` is in a paused / not-yet-loaded state.\n *\n * Player keeps public delegating methods (`resolvePosterPath`,\n * `showPosterOverlay`, etc.) so the existing external API is\n * unchanged — only the implementation has moved.\n */\n\nimport { captureVideoFrame } from '../utils/VideoFrameCapture.js';\nimport type { Player } from './Player.js';\n\nexport class PosterManager {\n private readonly player: Player;\n\n constructor(player: Player) {\n this.player = player;\n }\n\n /**\n * Convert a relative poster path into an absolute URL. Absolute URLs\n * (http/https) and root-relative paths (`/foo`) are returned as-is.\n * Falls back to the raw string on any parse error — a malformed URL\n * is still better than throwing and breaking the caller.\n */\n resolvePath(posterPath: string | null | undefined): string {\n if (!posterPath) return '';\n\n if (posterPath.match(/^(https?:|\\/)/)) {\n return posterPath;\n }\n\n try {\n const posterUrl = new URL(posterPath, window.location.href);\n return posterUrl.href;\n } catch {\n return posterPath;\n }\n }\n\n /**\n * Capture a frame from the underlying video as a data URL suitable\n * for use as `<video>.poster`. Returns `null` when the element is\n * not a video, the renderer isn't ready, or the capture fails.\n *\n * When the control bar has a hidden \"preview video\" element (used\n * for the seek hover thumbnail), we prefer that so we don't disturb\n * the user's current playback position.\n */\n async generateFromVideo(time = 10): Promise<string | null> {\n const player = this.player;\n if (player.element.tagName !== 'VIDEO') return null;\n\n const renderer = player.renderer;\n if (!renderer || !renderer.media || renderer.media.tagName !== 'VIDEO') {\n return null;\n }\n\n const video = renderer.media as HTMLVideoElement;\n\n if (!video.duration || video.duration < time) {\n time = Math.min(time, Math.max(1, video.duration * 0.1));\n }\n\n let videoToUse: HTMLVideoElement = video;\n if (player.controlBar && player.controlBar.previewVideo && player.controlBar.previewSupported) {\n videoToUse = player.controlBar.previewVideo as HTMLVideoElement;\n }\n\n // If we're using the main video we have to put its currentTime /\n // paused state back; for the preview video nobody is watching so\n // we can skip the restore.\n const restoreState = videoToUse === video;\n return await captureVideoFrame(videoToUse, time, {\n restoreState,\n quality: 0.9\n });\n }\n\n /**\n * Auto-generate a poster from the video at the 10-second mark if the\n * content doesn't already have one. No-op for audio elements and for\n * media that ships with a poster attribute or option.\n */\n async autoGenerate(): Promise<void> {\n const player = this.player;\n\n const hasPoster =\n player.element.getAttribute('poster') ||\n (player.element as HTMLVideoElement).poster ||\n player.options.poster;\n\n if (hasPoster) return;\n if (player.element.tagName !== 'VIDEO') return;\n\n // Wait for metadata so `duration` is known before we seek.\n if (!player.state.duration || player.state.duration === 0) {\n await new Promise<void>((resolve) => {\n const onLoadedMetadata = () => {\n player.element.removeEventListener('loadedmetadata', onLoadedMetadata);\n resolve();\n };\n\n if (player.element.readyState >= 1) {\n resolve();\n } else {\n player.element.addEventListener('loadedmetadata', onLoadedMetadata);\n }\n });\n }\n\n const posterDataURL = await this.generateFromVideo(10);\n\n if (posterDataURL) {\n (player.element as HTMLVideoElement).poster = posterDataURL;\n player.log('Auto-generated poster from video frame at 10 seconds', 'info');\n this.showOverlay();\n }\n }\n\n /**\n * Apply the poster as a CSS background on the video wrapper. This is\n * used to keep the poster visible behind the play button when the\n * browser wouldn't render `<video>.poster` itself (e.g. during\n * fallback / transitional states).\n */\n showOverlay(): void {\n const player = this.player;\n if (!player.videoWrapper || player.element.tagName !== 'VIDEO') return;\n\n const poster =\n player.element.getAttribute('poster') ||\n (player.element as HTMLVideoElement).poster ||\n player.options.poster;\n\n if (!poster) return;\n\n // Data URLs are already absolute and opaque; only normal URLs need\n // resolving into absolute form for the CSS `url(...)` value.\n const resolvedPoster = poster.startsWith('data:') ? poster : this.resolvePath(poster);\n player.videoWrapper.style.setProperty('--vidply-poster-image', `url(\"${resolvedPoster}\")`);\n player.videoWrapper.classList.add('vidply-forced-poster');\n\n // Audio-as-video content uses a wider aspect ratio so the poster\n // doesn't get stretched into a tall letter-boxed frame.\n if (player._isAudioContent && player.container) {\n player.container.classList.add('vidply-audio-content');\n } else if (player.container) {\n player.container.classList.remove('vidply-audio-content');\n }\n }\n\n hideOverlay(): void {\n const player = this.player;\n if (!player.videoWrapper) return;\n\n player.videoWrapper.classList.remove('vidply-forced-poster');\n player.videoWrapper.style.removeProperty('--vidply-poster-image');\n\n // NOTE: `vidply-audio-content` intentionally stays applied — it\n // drives the aspect ratio for the entire duration of audio-in-a-\n // video-element playback, not just while the poster is visible.\n }\n}\n", "/**\n * Resume-playback feature extracted from Player.\n *\n * Owns:\n * - Throttled progress persistence during playback.\n * - \"Where were we?\" detection on `loadedmetadata`.\n * - Clearing persisted progress on `ended`.\n * - The optional resume prompt (accessible modal with two buttons).\n *\n * Because `resumePlayback` is opt-in, the Player only instantiates\n * this manager when the option is enabled — so pages without the\n * feature don't pay for the extra state / event handlers.\n */\n\nimport { DOMUtils } from '../utils/DOMUtils.js';\nimport { i18n } from '../i18n/i18n.js';\nimport { throttle } from '../utils/PerformanceUtils.js';\nimport type { Player } from './Player.js';\n\nexport class ResumeManager {\n private readonly player: Player;\n private saveProgressThrottled: (() => void) | null = null;\n private resumeChecked = false;\n private listenersAttached = false;\n\n constructor(player: Player) {\n this.player = player;\n }\n\n /**\n * Wire up the progress-save + resume-check listeners. Safe to call\n * multiple times: repeat calls are no-ops so a re-init path during\n * source switching doesn't stack duplicate listeners.\n */\n init(): void {\n if (this.listenersAttached) return;\n this.listenersAttached = true;\n\n this.saveProgressThrottled = throttle(() => this.saveProgress(), 5000);\n\n this.player.on('timeupdate', () => {\n if (this.player.state.playing && this.player.state.duration > 0) {\n this.saveProgressThrottled?.();\n }\n });\n\n // First `loadedmetadata` after init is the only place we should\n // offer to resume — after that the user is in charge of seeking.\n this.player.on('loadedmetadata', () => {\n if (!this.resumeChecked) {\n this.resumeChecked = true;\n this.checkForResume();\n }\n });\n\n this.player.on('ended', () => {\n const videoId = this.player.getVideoId();\n if (videoId) {\n this.player.storage.clearWatchProgress(videoId);\n }\n });\n }\n\n /**\n * Persist current playback progress to storage. No-op when the\n * feature is disabled, when the video is too short / at the very\n * start, or when playback is effectively complete.\n */\n saveProgress(): void {\n const player = this.player;\n if (!player.options.resumePlayback) return;\n\n const videoId = player.getVideoId();\n if (!videoId) return;\n\n const currentTime = player.state.currentTime;\n const duration = player.state.duration;\n\n // Resume for tiny clips is more annoying than useful.\n if (duration < 30 || currentTime < (player.options.resumeThreshold as number)) {\n return;\n }\n\n // Near the end? Don't persist — the user effectively finished.\n const percentage = (currentTime / duration) * 100;\n if (percentage > 95) return;\n\n player.storage.saveWatchProgress(videoId, currentTime, duration);\n }\n\n /**\n * Check for a previously-saved resume point for the current video\n * and either auto-resume or show the prompt depending on\n * `options.resumePrompt`. Safe to call manually, e.g. after an\n * external source change.\n */\n checkForResume(): void {\n const player = this.player;\n if (!player.options.resumePlayback) return;\n\n const videoId = player.getVideoId();\n if (!videoId) return;\n\n const progress = player.storage.getWatchProgress(videoId);\n if (!progress) return;\n\n const { currentTime, duration, percentage } = progress;\n const threshold = player.options.resumeThreshold as number;\n\n // Stale / trivial entries get purged instead of offered.\n if (currentTime < threshold || percentage > 95) {\n player.storage.clearWatchProgress(videoId);\n return;\n }\n\n // Duration mismatch usually means the source URL points to a new\n // cut of the same video — better to start fresh than resume at a\n // time that's outside the new clip.\n if (player.state.duration > 0 && Math.abs(player.state.duration - duration) > 5) {\n player.storage.clearWatchProgress(videoId);\n return;\n }\n\n if (player.options.resumePrompt) {\n this.showPrompt(currentTime);\n } else {\n player.seek(currentTime);\n }\n }\n\n /**\n * Format a time value as `mm:ss` (or `hh:mm:ss` once we cross an\n * hour) for display in the resume prompt label. No localisation is\n * needed because the surrounding prompt text is already localised\n * by i18n.\n */\n private formatTime(seconds: number): string {\n const h = Math.floor(seconds / 3600);\n const m = Math.floor((seconds % 3600) / 60);\n const s = Math.floor(seconds % 60);\n\n if (h > 0) {\n return `${h}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;\n }\n return `${m}:${s.toString().padStart(2, '0')}`;\n }\n\n showPrompt(savedTime: number): void {\n const player = this.player;\n if (player.state.resumePromptVisible || !player.container) return;\n\n const formattedTime = this.formatTime(savedTime);\n const promptText = i18n.t('resume.prompt', { time: formattedTime });\n\n player.resumePromptElement = DOMUtils.createElement('div', {\n className: `${player.options.classPrefix}-resume-prompt`,\n attributes: {\n role: 'dialog',\n 'aria-label': promptText,\n 'aria-modal': 'true'\n }\n });\n\n const promptContent = DOMUtils.createElement('div', {\n className: `${player.options.classPrefix}-resume-prompt-content`\n });\n\n const promptMessage = DOMUtils.createElement('p', {\n className: `${player.options.classPrefix}-resume-prompt-message`,\n textContent: promptText\n });\n\n const buttonContainer = DOMUtils.createElement('div', {\n className: `${player.options.classPrefix}-resume-prompt-buttons`\n });\n\n const resumeButton = DOMUtils.createElement('button', {\n className: `${player.options.classPrefix}-resume-prompt-button ${player.options.classPrefix}-resume-prompt-button-primary`,\n textContent: i18n.t('resume.resume'),\n attributes: { type: 'button' }\n });\n resumeButton.addEventListener('click', () => {\n this.hidePrompt();\n player.seek(savedTime);\n player.play();\n });\n\n const startOverButton = DOMUtils.createElement('button', {\n className: `${player.options.classPrefix}-resume-prompt-button`,\n textContent: i18n.t('resume.startOver'),\n attributes: { type: 'button' }\n });\n startOverButton.addEventListener('click', () => {\n this.hidePrompt();\n const videoId = player.getVideoId();\n if (videoId) player.storage.clearWatchProgress(videoId);\n player.seek(0);\n player.play();\n });\n\n const handleKeydown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n e.preventDefault();\n e.stopPropagation();\n this.hidePrompt();\n }\n };\n player.resumePromptElement.addEventListener('keydown', handleKeydown);\n\n buttonContainer.appendChild(resumeButton);\n buttonContainer.appendChild(startOverButton);\n promptContent.appendChild(promptMessage);\n promptContent.appendChild(buttonContainer);\n player.resumePromptElement.appendChild(promptContent);\n\n player.container.appendChild(player.resumePromptElement);\n player.state.resumePromptVisible = true;\n\n // Focus the primary button after the element is attached to the DOM\n // so the dialog is immediately keyboard-navigable.\n requestAnimationFrame(() => { resumeButton.focus(); });\n\n player.emit('resumepromptshow', { savedTime });\n }\n\n hidePrompt(): void {\n const player = this.player;\n if (!player.resumePromptElement) return;\n\n player.resumePromptElement.remove();\n player.resumePromptElement = null;\n player.state.resumePromptVisible = false;\n\n player.emit('resumeprompthide');\n }\n}\n", "/**\n * Resize / orientation / fullscreen handling extracted from Player.\n *\n * Three responsibilities live here:\n *\n * 1. Reacting to container size changes (ResizeObserver when\n * available, `window.resize` fallback otherwise) so the control\n * bar and the transcript panel stay correctly laid out.\n * 2. Listening for orientation changes on mobile so the transcript\n * panel re-positions after the viewport rotates.\n * 3. Tracking native fullscreen changes across vendor prefixes so\n * `state.fullscreen` stays in sync with reality — including the\n * inert/overlay bookkeeping that's shared with the pseudo-\n * fullscreen fallback.\n *\n * Every listener is attached with the Player's `lifecycleSignal`\n * (or, for the ResizeObserver / older matchMedia listeners that\n * don't accept AbortSignal, cleaned up explicitly in `cleanup()`).\n */\n\nimport { PseudoFullscreenController } from './PseudoFullscreen.js';\nimport type { ControlBar } from '../controls/ControlBar.js';\nimport type { Player } from './Player.js';\n\nexport class ResponsiveManager {\n private readonly player: Player;\n private orientationQuery: MediaQueryList | null = null;\n private orientationHandler: ((e: MediaQueryListEvent) => void) | null = null;\n\n constructor(player: Player) {\n this.player = player;\n }\n\n setup(): void {\n this.setupResizeTracking();\n this.setupOrientationTracking();\n this.setupFullscreenTracking();\n }\n\n private setupResizeTracking(): void {\n const player = this.player;\n\n if (typeof ResizeObserver !== 'undefined') {\n // Preferred path: ResizeObserver fires per-container, not per-viewport,\n // so multiple players on one page don't stomp on each other.\n player.resizeObserver = new ResizeObserver((entries) => {\n for (const entry of entries) {\n const width = entry.contentRect.width;\n\n const controlBar = player.controlBar as (ControlBar & {\n updateControlsForViewport?: (w: number) => void\n }) | null;\n if (controlBar && typeof controlBar.updateControlsForViewport === 'function') {\n controlBar.updateControlsForViewport(width);\n }\n\n if (player.transcriptManager && player.transcriptManager.isVisible) {\n player.transcriptManager.positionTranscript();\n }\n }\n });\n\n player.resizeObserver.observe(player.container);\n return;\n }\n\n // Fallback: `window.resize`. We still respect\n // `draggableResizable.manuallyPositioned` so we don't stomp on a\n // user-chosen transcript location.\n player.resizeHandler = () => {\n const width = player.container.clientWidth;\n\n const controlBar = player.controlBar as (ControlBar & {\n updateControlsForViewport?: (w: number) => void\n }) | null;\n if (controlBar && typeof controlBar.updateControlsForViewport === 'function') {\n controlBar.updateControlsForViewport(width);\n }\n\n if (player.transcriptManager && player.transcriptManager.isVisible) {\n if (!player.transcriptManager.draggableResizable\n || !player.transcriptManager.draggableResizable.manuallyPositioned) {\n player.transcriptManager.positionTranscript();\n }\n }\n };\n\n window.addEventListener('resize', player.resizeHandler, { signal: player.lifecycleSignal });\n }\n\n private setupOrientationTracking(): void {\n const player = this.player;\n if (!window.matchMedia) return;\n\n this.orientationHandler = () => {\n // Wait for layout to settle before we recompute positions —\n // otherwise `getBoundingClientRect` still reflects the\n // pre-rotation viewport.\n setTimeout(() => {\n if (player.transcriptManager && player.transcriptManager.isVisible) {\n if (!player.transcriptManager.draggableResizable\n || !player.transcriptManager.draggableResizable.manuallyPositioned) {\n player.transcriptManager.positionTranscript();\n }\n }\n }, 100);\n };\n\n const orientationQuery = window.matchMedia('(orientation: portrait)');\n if (orientationQuery.addEventListener) {\n // Modern MediaQueryList supports AddEventListenerOptions including\n // `signal`, so teardown happens automatically via the player's\n // lifecycle AbortController.\n orientationQuery.addEventListener('change', this.orientationHandler, {\n signal: player.lifecycleSignal\n });\n } else if ((orientationQuery as MediaQueryList).addListener) {\n // Older Safari: no signal support; `cleanup()` will remove this\n // explicitly below.\n (orientationQuery as MediaQueryList).addListener(this.orientationHandler);\n }\n\n this.orientationQuery = orientationQuery;\n // Mirror the field onto the player too; some older call sites\n // reach for `player.orientationQuery` directly.\n player.orientationQuery = orientationQuery;\n player.orientationHandler = this.orientationHandler;\n }\n\n private setupFullscreenTracking(): void {\n const player = this.player;\n\n player.fullscreenChangeHandler = () => {\n const doc = document as Document & {\n webkitFullscreenElement?: Element | null;\n mozFullScreenElement?: Element | null;\n msFullscreenElement?: Element | null;\n };\n const isFullscreen = Boolean(\n document.fullscreenElement ||\n doc.webkitFullscreenElement ||\n doc.mozFullScreenElement ||\n doc.msFullscreenElement\n );\n\n if (player.state.fullscreen === isFullscreen) return;\n\n player.state.fullscreen = isFullscreen;\n\n if (!player.pseudoFullscreen) {\n player.pseudoFullscreen = new PseudoFullscreenController(player);\n }\n\n if (isFullscreen) {\n player.container.classList.add(`${player.options.classPrefix}-fullscreen`);\n // CSS fallback for browsers without :has() support.\n document.body.classList.add('vidply-fullscreen-active');\n // Make background content inert even in real-fullscreen mode —\n // the overlay still leaves DOM focus reachable on some browsers.\n player.pseudoFullscreen.makeBackgroundInert();\n } else {\n player.container.classList.remove(`${player.options.classPrefix}-fullscreen`);\n document.body.classList.remove('vidply-fullscreen-active');\n player.pseudoFullscreen.restoreBackgroundInteractivity();\n player._disablePseudoFullscreen();\n }\n\n player.emit('fullscreenchange', isFullscreen);\n\n if (player.controlBar) {\n player.controlBar.updateFullscreenButton();\n }\n\n // Reposition the sign-language overlay after the transition\n // completes; drag/resize affordances differ between windowed\n // and fullscreen on mobile so we re-install them.\n if (player.signLanguageWrapper && player.signLanguageWrapper.style.display !== 'none') {\n const isMobileDevice = window.innerWidth < 768;\n if (isMobileDevice) {\n player.setupSignLanguageInteraction();\n }\n\n player.setManagedTimeout(() => {\n requestAnimationFrame(() => {\n player.storage.saveSignLanguagePreferences({ size: null });\n if (player.signLanguageWrapper) {\n player.signLanguageWrapper.style.width = isFullscreen ? '400px' : '280px';\n }\n player.constrainSignLanguagePosition();\n });\n }, 500);\n }\n };\n\n const opts = { signal: player.lifecycleSignal };\n document.addEventListener('fullscreenchange', player.fullscreenChangeHandler, opts);\n document.addEventListener('webkitfullscreenchange', player.fullscreenChangeHandler, opts);\n document.addEventListener('mozfullscreenchange', player.fullscreenChangeHandler, opts);\n document.addEventListener('MSFullscreenChange', player.fullscreenChangeHandler, opts);\n }\n\n /**\n * Tear down listeners that aren't covered by the Player's\n * lifecycle AbortController. The `window.resize` and\n * `document.fullscreenchange` listeners are already cleaned up\n * via `{signal}`; only the ResizeObserver and old-Safari\n * matchMedia listener need an explicit removal.\n */\n cleanup(): void {\n const player = this.player;\n\n if (player.resizeObserver) {\n player.resizeObserver.disconnect();\n player.resizeObserver = null;\n }\n player.resizeHandler = null;\n player.fullscreenChangeHandler = null;\n\n if (this.orientationQuery && this.orientationHandler) {\n if (this.orientationQuery.removeEventListener) {\n this.orientationQuery.removeEventListener('change', this.orientationHandler);\n } else if ((this.orientationQuery as MediaQueryList).removeListener) {\n (this.orientationQuery as MediaQueryList).removeListener(this.orientationHandler);\n }\n this.orientationQuery = null;\n this.orientationHandler = null;\n }\n player.orientationQuery = null;\n player.orientationHandler = null;\n }\n}\n", "/**\n * Metadata-track alert handling extracted from Player.\n *\n * The alert system lets a `kind=metadata` text track drive\n * accessibility affordances on the page — pausing the video,\n * focusing an element, showing or hiding a marked-up alert region,\n * wiring a \"Continue\" button. All of that lives here rather than on\n * Player so the code can be skipped entirely (via tree-shaking) when\n * the `metadataAlerts`/`metadataDirectives` options are disabled.\n *\n * Public-ish entry points (Player exposes them through thin\n * delegates that keep the same names):\n *\n * - `setupHandling()` — wire the `cuechange` listener\n * - `handleCue(cue)` — parse a single cue and dispatch directives\n * - `handleAlert(selector, options)` — manually drive the alert UI\n * - `handleHashtags(tags)` — resolve a list of cue hashtags\n * - `cleanup()` — called from Player.destroy()\n */\n\nimport { reducedMotionScrollOptions } from '../utils/PerformanceUtils.js';\nimport type { Player } from './Player.js';\n\n/** Per-selector metadata alert configuration. */\nexport interface MetadataAlertConfig {\n titleSelector?: string;\n messageSelector?: string;\n title?: string;\n message?: string;\n focus?: boolean;\n focusOnShow?: boolean;\n focusTarget?: string;\n focusDelay?: number;\n label?: string;\n role?: string;\n show?: boolean;\n display?: string;\n hideDisplay?: string;\n autoScroll?: boolean;\n selector?: string;\n alert?: string;\n target?: string;\n continueButton?: string;\n hideOnContinue?: boolean;\n resume?: boolean;\n resetContent?: boolean;\n notification?: string;\n persist?: boolean;\n\n [key: string]: unknown;\n}\n\n/** Options accepted by `handleAlert`. */\nexport interface MetadataAlertOptions {\n element?: HTMLElement | null;\n reason?: string;\n cue?: VTTCue | null;\n show?: boolean;\n focus?: boolean;\n autoScroll?: boolean;\n}\n\ninterface AlertHandlerEntry {\n button: HTMLElement | null;\n handler: EventListener | null;\n}\n\nexport class MetadataAlertsManager {\n private readonly player: Player;\n private cueChangeHandler: (() => void) | null = null;\n private readonly alertHandlers: Map<string, AlertHandlerEntry> = new Map();\n\n constructor(player: Player) {\n this.player = player;\n }\n\n /** The `cuechange` handler this manager installed on the metadata\n * track. Exposed so Player can mirror it onto itself for legacy\n * access (some tests poke at `player.metadataCueChangeHandler`). */\n get cuechangeListener(): (() => void) | null {\n return this.cueChangeHandler;\n }\n\n setupHandling(): void {\n const player = this.player;\n\n const setupMetadata = () => {\n const textTracks = player.textTracks;\n const metadataTrack = textTracks.find((track) => track.kind === 'metadata');\n\n if (!metadataTrack) {\n if (player.options.debug) player.log('[Metadata] No metadata track found');\n return;\n }\n\n // `disabled` suppresses cuechange entirely; `hidden` still fires\n // events without rendering the track.\n if (metadataTrack.mode === 'disabled') {\n metadataTrack.mode = 'hidden';\n }\n\n if (this.cueChangeHandler) {\n metadataTrack.removeEventListener('cuechange', this.cueChangeHandler);\n }\n\n this.cueChangeHandler = () => {\n const activeCues = Array.from(metadataTrack.activeCues || []) as VTTCue[];\n if (activeCues.length > 0 && player.options.debug) {\n player.log('[Metadata] Active cues:', activeCues.map((c) => ({\n start: c.startTime,\n end: c.endTime,\n text: c.text\n })));\n }\n activeCues.forEach((cue) => this.handleCue(cue));\n };\n\n metadataTrack.addEventListener('cuechange', this.cueChangeHandler);\n // Mirror onto player for existing destroy() cleanup path.\n player.metadataCueChangeHandler = this.cueChangeHandler;\n\n if (player.options.debug) {\n const cueCount = metadataTrack.cues ? metadataTrack.cues.length : 0;\n player.log('[Metadata] Track enabled,', cueCount, 'cues available');\n }\n };\n\n setupMetadata();\n player.on('loadedmetadata', setupMetadata);\n }\n\n /**\n * Sanitise a user-supplied selector string. Returns `null` for\n * anything that isn't obviously safe: non-string input, empty\n * after trimming, or too long to bound selector-engine cost.\n */\n normalizeSelector(selector: unknown): string | null {\n if (typeof selector !== 'string') return null;\n const trimmed = selector.trim();\n if (!trimmed) return null;\n if (trimmed.length > 200) return null;\n if (trimmed.startsWith('#') || trimmed.startsWith('.') || trimmed.startsWith('[')) {\n return trimmed;\n }\n return `#${trimmed}`;\n }\n\n resolveConfig(\n map: Record<string, unknown> | null | undefined,\n key: string | null | undefined\n ): MetadataAlertConfig | null {\n if (!map || !key) return null;\n if (Object.prototype.hasOwnProperty.call(map, key)) {\n return map[key] as MetadataAlertConfig;\n }\n const withoutHash = key.replace(/^#/, '');\n if (Object.prototype.hasOwnProperty.call(map, withoutHash)) {\n return map[withoutHash] as MetadataAlertConfig;\n }\n return null;\n }\n\n /**\n * Remember the original title/message text before a hashtag cue\n * overwrites them, so `restoreContent` can roll back on the next\n * cue boundary. Idempotent — a second call for the same element\n * does not overwrite the already-cached value.\n */\n cacheContent(element: HTMLElement | null | undefined, config: MetadataAlertConfig = {}): void {\n if (!element) return;\n const titleSelector = config.titleSelector || '[data-vidply-alert-title], h3, header';\n const messageSelector = config.messageSelector || '[data-vidply-alert-message], p';\n\n const titleEl = element.querySelector<HTMLElement>(titleSelector);\n if (titleEl && !titleEl.dataset.vidplyAlertTitleOriginal) {\n titleEl.dataset.vidplyAlertTitleOriginal = titleEl.textContent?.trim() ?? '';\n }\n\n const messageEl = element.querySelector<HTMLElement>(messageSelector);\n if (messageEl && !messageEl.dataset.vidplyAlertMessageOriginal) {\n messageEl.dataset.vidplyAlertMessageOriginal = messageEl.textContent?.trim() ?? '';\n }\n }\n\n restoreContent(element: HTMLElement | null | undefined, config: MetadataAlertConfig = {}): void {\n if (!element) return;\n const titleSelector = config.titleSelector || '[data-vidply-alert-title], h3, header';\n const messageSelector = config.messageSelector || '[data-vidply-alert-message], p';\n\n const titleEl = element.querySelector<HTMLElement>(titleSelector);\n if (titleEl && titleEl.dataset.vidplyAlertTitleOriginal) {\n titleEl.textContent = titleEl.dataset.vidplyAlertTitleOriginal;\n }\n\n const messageEl = element.querySelector<HTMLElement>(messageSelector);\n if (messageEl && messageEl.dataset.vidplyAlertMessageOriginal) {\n messageEl.textContent = messageEl.dataset.vidplyAlertMessageOriginal;\n }\n }\n\n /**\n * Move focus to one of the well-known targets understood by the\n * alert system, or to a named selector. Never silently errors — an\n * unresolved target is simply a no-op.\n */\n focusTarget(target: string | null | undefined, fallbackElement: HTMLElement | null = null): void {\n if (!target || target === 'none') return;\n\n if (target === 'alert' && fallbackElement) {\n fallbackElement.focus({ preventScroll: true });\n return;\n }\n\n const player = this.player;\n\n if (target === 'player') {\n player.container?.focus({ preventScroll: true });\n return;\n }\n\n if (target === 'media') {\n player.element.focus({ preventScroll: true });\n return;\n }\n\n if (target === 'playButton') {\n const playButton = player.controlBar?.controls?.playPause;\n playButton?.focus({ preventScroll: true });\n return;\n }\n\n if (typeof target === 'string') {\n const targetElement = document.querySelector(target) as HTMLElement | null;\n if (targetElement) {\n if (targetElement.tabIndex === -1 && !targetElement.hasAttribute('tabindex')) {\n targetElement.setAttribute('tabindex', '-1');\n }\n targetElement.focus({ preventScroll: true });\n }\n }\n }\n\n /**\n * The public alert entry point. Pulls config out of\n * `options.metadataAlerts`, locates the DOM element, and applies\n * show/focus/continue logic per configuration.\n */\n handleAlert(selector: string, options: MetadataAlertOptions = {}): HTMLElement | undefined {\n const player = this.player;\n if (!selector) return undefined;\n\n const config: MetadataAlertConfig =\n this.resolveConfig(player.options.metadataAlerts as Record<string, unknown> | null | undefined, selector) || {};\n\n // Container-scoped resolution by default; only fall back to a\n // global lookup when `metadataDirectives === 'global' | true`.\n const element = options.element || this.resolveElement(selector);\n\n if (!element) {\n if (player.options.debug) player.log('[Metadata] Alert element not found:', selector);\n return undefined;\n }\n\n if (player.options.debug) {\n player.log('[Metadata] Handling alert', selector, { reason: options.reason, config });\n }\n\n this.cacheContent(element, config);\n\n if (!element.dataset.vidplyAlertOriginalDisplay) {\n element.dataset.vidplyAlertOriginalDisplay = element.style.display || '';\n }\n\n if (!element.dataset.vidplyAlertDisplay) {\n element.dataset.vidplyAlertDisplay = config.display || 'block';\n }\n\n const shouldShow = options.show !== undefined ? options.show : (config.show !== false);\n if (shouldShow) {\n const displayValue = config.display || element.dataset.vidplyAlertDisplay || 'block';\n element.style.display = displayValue;\n element.hidden = false;\n element.removeAttribute('hidden');\n element.setAttribute('aria-hidden', 'false');\n element.setAttribute('data-vidply-alert-active', 'true');\n }\n\n const shouldReset = config.resetContent !== false && options.reason === 'focus';\n if (shouldReset) this.restoreContent(element, config);\n\n const shouldFocus = options.focus !== undefined\n ? options.focus\n : (config.focusOnShow ?? (options.reason !== 'focus'));\n\n if (shouldShow && shouldFocus) {\n if (element.tabIndex === -1 && !element.hasAttribute('tabindex')) {\n element.setAttribute('tabindex', '-1');\n }\n element.focus({ preventScroll: true });\n }\n\n if (shouldShow && config.autoScroll !== false && options.autoScroll !== false) {\n element.scrollIntoView(reducedMotionScrollOptions('nearest'));\n }\n\n const continueSelector = config.continueButton;\n if (continueSelector) {\n let continueButton: HTMLElement | null = null;\n if (continueSelector === 'self') {\n continueButton = element;\n } else if (element.matches(continueSelector)) {\n continueButton = element;\n } else {\n continueButton =\n element.querySelector<HTMLElement>(continueSelector) ||\n document.querySelector<HTMLElement>(continueSelector);\n }\n\n if (continueButton && !this.alertHandlers.has(selector)) {\n const handler = () => {\n const hideOnContinue = config.hideOnContinue !== false;\n if (hideOnContinue) {\n const originalDisplay = element.dataset.vidplyAlertOriginalDisplay || '';\n element.style.display = config.hideDisplay || originalDisplay || 'none';\n element.setAttribute('aria-hidden', 'true');\n element.removeAttribute('data-vidply-alert-active');\n }\n\n if (config.resume !== false && player.state.paused) {\n player.play();\n }\n\n const focusTarget = config.focusTarget || 'playButton';\n player.setManagedTimeout(() => {\n this.focusTarget(focusTarget, element);\n }, config.focusDelay ?? 100);\n };\n\n continueButton.addEventListener('click', handler);\n this.alertHandlers.set(selector, { button: continueButton, handler });\n }\n }\n\n return element;\n }\n\n handleHashtags(hashtags: string[] | null | undefined): void {\n if (!Array.isArray(hashtags) || hashtags.length === 0) return;\n\n const player = this.player;\n const configMap = player.options.metadataHashtags as Record<string, unknown> | null | undefined;\n if (!configMap) return;\n\n hashtags.forEach((tag) => {\n const config = this.resolveConfig(configMap, tag);\n if (!config) return;\n\n const selector = this.normalizeSelector(config.alert || config.selector || config.target);\n if (!selector) return;\n\n const element = this.resolveElement(selector);\n if (!element) {\n if (player.options.debug) player.log('[Metadata] Hashtag target not found:', selector);\n return;\n }\n\n if (player.options.debug) {\n player.log('[Metadata] Handling hashtag', tag, { selector, config });\n }\n\n this.cacheContent(element, config);\n\n if (config.title) {\n const titleSelector = config.titleSelector || '[data-vidply-alert-title], h3, header';\n const titleEl = element.querySelector<HTMLElement>(titleSelector);\n if (titleEl) titleEl.textContent = config.title;\n }\n\n if (config.message) {\n const messageSelector = config.messageSelector || '[data-vidply-alert-message], p';\n const messageEl = element.querySelector<HTMLElement>(messageSelector);\n if (messageEl) messageEl.textContent = config.message;\n }\n\n const show = config.show !== false;\n const focus = config.focus !== undefined ? config.focus : false;\n\n this.handleAlert(selector, {\n element,\n show,\n focus,\n autoScroll: config.autoScroll,\n reason: 'hashtag'\n });\n });\n }\n\n /**\n * Parse a single metadata cue for directives (`PAUSE`, `FOCUS:x`,\n * `#hashtag`), emit the corresponding public events, and execute\n * DOM side-effects only when `options.metadataDirectives` is set.\n */\n handleCue(cue: VTTCue | TextTrackCue): void {\n const player = this.player;\n const text = (cue as VTTCue).text.trim();\n\n if (player.options.debug) {\n player.log('[Metadata] Processing cue:', { time: cue.startTime, text });\n }\n\n // Public event fires regardless of directive opt-in so consumers\n // can always observe metadata without granting DOM side-effects.\n player.emit('metadata', {\n time: cue.startTime,\n endTime: cue.endTime,\n text,\n cue\n });\n\n if (text.includes('PAUSE')) {\n if (!player.state.paused) {\n if (player.options.debug) player.log('[Metadata] Pausing video at', cue.startTime);\n player.pause();\n }\n player.emit('metadata:pause', { time: cue.startTime, text });\n }\n\n const focusMatch = text.match(/FOCUS:([\\w#-]{1,128})/);\n if (focusMatch) {\n const targetSelector = focusMatch[1];\n const normalizedSelector = this.normalizeSelector(targetSelector);\n const targetElement = this.resolveElement(normalizedSelector);\n if (targetElement) {\n if (player.options.debug) player.log('[Metadata] Focusing element:', normalizedSelector);\n if (targetElement.tabIndex === -1 && !targetElement.hasAttribute('tabindex')) {\n targetElement.setAttribute('tabindex', '-1');\n }\n player.setManagedTimeout(() => {\n targetElement.focus({ preventScroll: true });\n }, 10);\n } else if (player.options.debug && player.options.metadataDirectives) {\n player.log('[Metadata] Element not found:', normalizedSelector || targetSelector);\n }\n player.emit('metadata:focus', {\n time: cue.startTime,\n target: targetSelector,\n selector: normalizedSelector,\n element: targetElement,\n text\n });\n\n if (player.options.metadataDirectives && normalizedSelector) {\n this.handleAlert(normalizedSelector, {\n element: targetElement,\n reason: 'focus'\n });\n }\n }\n\n const hashtags = text.match(/#[\\w-]{1,64}/g);\n if (hashtags && hashtags.length > 0) {\n // Cap at 32 hashtags per cue to bound subsequent work.\n const safeTags = hashtags.slice(0, 32);\n if (player.options.debug) player.log('[Metadata] Hashtags found:', safeTags);\n player.emit('metadata:hashtags', {\n time: cue.startTime,\n hashtags: safeTags,\n text\n });\n\n if (player.options.metadataDirectives) this.handleHashtags(safeTags);\n }\n }\n\n /**\n * Resolve a metadata-cue selector inside the configured directive\n * scope. Returns `null` when directives are disabled or the\n * selector doesn't resolve. Container-scoped resolution is the\n * default so a malicious caption cannot focus a login-form input\n * or trigger a dialog elsewhere on the page.\n */\n private resolveElement(selector: string | null): HTMLElement | null {\n const player = this.player;\n const mode = player.options.metadataDirectives;\n if (!mode) return null;\n if (!selector) return null;\n try {\n if (mode === true || mode === 'global') {\n return document.querySelector(selector) as HTMLElement | null;\n }\n const root = player.container || player.element.parentElement || document;\n return (root as ParentNode).querySelector(selector) as HTMLElement | null;\n } catch {\n // Bad selector — never surface to the page.\n return null;\n }\n }\n\n /** Tear down the per-alert click handlers and the cuechange\n * listener. Called from Player.destroy(). */\n cleanup(): void {\n // The cuechange listener is removed by the Player destroy path\n // because the underlying TextTrack still exists and Player\n // already knows how to walk textTracks. We only need to release\n // the per-alert click handlers we installed.\n if (this.alertHandlers.size > 0) {\n this.alertHandlers.forEach(({ button, handler }) => {\n if (button && handler) button.removeEventListener('click', handler);\n });\n this.alertHandlers.clear();\n }\n this.cueChangeHandler = null;\n }\n}\n", "/**\n * VidPly - Universal Video Player\n * Main Player Class\n */\n\nimport {EventEmitter} from '../utils/EventEmitter.js';\nimport {DOMUtils} from '../utils/DOMUtils.js';\nimport {ControlBar} from '../controls/ControlBar.js';\nimport {CaptionManager} from '../controls/CaptionManager.js';\nimport {KeyboardManager} from '../controls/KeyboardManager.js';\nimport {HTML5Renderer} from '../renderers/HTML5Renderer.js';\nimport {createPlayOverlay} from '../icons/Icons.js';\nimport {i18n} from '../i18n/i18n.js';\nimport {StorageManager} from '../utils/StorageManager.js';\nimport {DraggableResizable} from '../utils/DraggableResizable.js';\nimport {debounce, isMobile, rafWithTimeout} from '../utils/PerformanceUtils.js';\nimport {sanitizePosterUrl, cssEscapeUrl} from '../utils/UrlSafe.js';\nimport {observeForLazyInit, cancelLazyInit, type LazyHandle} from './LazyInit.js';\nimport {PseudoFullscreenController} from './PseudoFullscreen.js';\nimport {ThemeManager, PLAYER_THEMES, type ThemeName} from './ThemeManager.js';\nimport {PosterManager} from './PosterManager.js';\nimport {ResumeManager} from './ResumeManager.js';\nimport {ResponsiveManager} from './ResponsiveManager.js';\nimport {\n MetadataAlertsManager,\n type MetadataAlertConfig as _MetadataAlertConfig,\n type MetadataAlertOptions as _MetadataAlertOptions\n} from './MetadataAlertsManager.js';\n\n// Re-export the interfaces so external users who imported them from\n// `core/Player.js` keep working.\nexport type MetadataAlertConfig = _MetadataAlertConfig;\nexport type MetadataAlertOptions = _MetadataAlertOptions;\nimport type {PlayerEventMap} from '../types/events.js';\nimport type {PlayerOptions} from '../types/options.js';\nimport type {PlayerState} from '../types/state.js';\nimport type {Renderer} from '../types/renderer.js';\nimport type {AudioDescriptionManager} from './AudioDescriptionManager.js';\nimport type {SignLanguageManager} from './SignLanguageManager.js';\nimport type {FloatingPlayerManager} from './FloatingPlayerManager.js';\nimport type {TranscriptManager} from '../controls/TranscriptManager.js';\nimport type {PlaylistManager} from '../features/PlaylistManager.js';\nimport type {SettingsDialog} from '../controls/SettingsDialog.js';\n\n// Typed dynamic loaders. Each loader returns the constructor for the\n// lazily imported manager, so call sites get full IntelliSense and\n// `noImplicitAny` is satisfied.\ntype AudioDescriptionManagerCtor = typeof AudioDescriptionManager;\ntype SignLanguageManagerCtor = typeof SignLanguageManager;\ntype FloatingPlayerManagerCtor = typeof FloatingPlayerManager;\n\nlet AudioDescriptionManagerModule: AudioDescriptionManagerCtor | null = null;\nlet SignLanguageManagerModule: SignLanguageManagerCtor | null = null;\nlet FloatingPlayerManagerModule: FloatingPlayerManagerCtor | null = null;\n\nasync function loadAudioDescriptionManager(): Promise<AudioDescriptionManagerCtor> {\n if (!AudioDescriptionManagerModule) {\n const module = await import('./AudioDescriptionManager.js');\n AudioDescriptionManagerModule = module.AudioDescriptionManager;\n }\n return AudioDescriptionManagerModule;\n}\n\nasync function loadSignLanguageManager(): Promise<SignLanguageManagerCtor> {\n if (!SignLanguageManagerModule) {\n const module = await import('./SignLanguageManager.js');\n SignLanguageManagerModule = module.SignLanguageManager;\n }\n return SignLanguageManagerModule;\n}\n\nasync function loadFloatingPlayerManager(): Promise<FloatingPlayerManagerCtor> {\n if (!FloatingPlayerManagerModule) {\n const module = await import('./FloatingPlayerManager.js');\n FloatingPlayerManagerModule = module.FloatingPlayerManager;\n }\n return FloatingPlayerManagerModule;\n}\n\nconst ALLOWED_MEDIA_TYPES = ['video', 'audio'] as const;\ntype AllowedMediaType = (typeof ALLOWED_MEDIA_TYPES)[number];\n\n/** Configuration accepted by Player.load() when switching to new media. */\nexport interface PlayerLoadConfig {\n src: string;\n type?: string;\n poster?: string | null;\n tracks?: Array<{\n kind?: string;\n label?: string;\n src?: string;\n srclang?: string;\n default?: boolean;\n [key: string]: unknown;\n }>;\n audioDescriptionSrc?: string | null;\n signLanguageSrc?: string | null;\n signLanguageSources?: Record<string, string>;\n\n [key: string]: unknown;\n}\n\n// Static counter for unique player instances\nlet playerInstanceCounter = 0;\n\nexport class Player extends EventEmitter<PlayerEventMap> {\n static instances: Player[] = [];\n /**\n * Available theme names. Kept as a static field for backward\n * compatibility with external callers that used\n * `Player.THEMES.includes(x)`; the canonical source is\n * `PLAYER_THEMES` in `./ThemeManager.ts`.\n */\n static readonly THEMES: readonly ThemeName[] = PLAYER_THEMES;\n\n /**\n * Manually schedule a lazy-initialised player for `selector` /\n * `element`. The player is constructed the first time the element\n * scrolls within `margin` of the viewport; if `IntersectionObserver`\n * is unavailable the player is constructed immediately.\n *\n * Returns a handle whose `cancel()` method removes the pending\n * observation, or `null` if no observation was scheduled (element\n * missing or eager fallback took effect).\n *\n * Implemented as a real static method (rather than a post-construction\n * assignment from `index.ts`) so the API belongs to the `Player`\n * symbol itself — which makes it easier to tree-shake and reason about.\n */\n static observeLazy(\n selector: string | HTMLElement,\n options: Partial<PlayerOptions> = {},\n margin = '200px'\n ): LazyHandle {\n const element =\n typeof selector === 'string'\n ? (document.querySelector(selector) as HTMLElement | null)\n : selector;\n\n if (!element) {\n console.warn('VidPly: Element not found for lazy observation');\n return null;\n }\n\n if ('IntersectionObserver' in window) {\n observeForLazyInit<Partial<PlayerOptions>>(\n element,\n options,\n margin,\n (target, opts) => { new Player(target, opts); }\n );\n return { cancel: () => cancelLazyInit(element) };\n }\n new Player(element, options);\n return null;\n }\n element: HTMLMediaElement;\n container!: HTMLElement;\n /**\n * Runtime options. Includes a `[key: string]: unknown` index for\n * internal-only dynamic keys that have not yet been promoted into\n * the public {@link PlayerOptions} interface.\n */\n options: PlayerOptions & Record<string, unknown>;\n state: PlayerState & Record<string, unknown>;\n renderer: Renderer | null = null;\n controlBar: ControlBar | null = null;\n captionManager: CaptionManager | null = null;\n keyboardManager: KeyboardManager | null = null;\n transcriptManager: TranscriptManager | null = null;\n playlistManager: PlaylistManager | null = null;\n settingsDialog: SettingsDialog | null = null;\n audioDescriptionManager: AudioDescriptionManager | null = null;\n signLanguageManager: SignLanguageManager | null = null;\n floatingPlayerManager: FloatingPlayerManager | null = null;\n storage: StorageManager;\n instanceId: number;\n _audioDescriptionDesiredState: boolean | undefined;\n _fallbackSources: Array<{ src: string; type?: string; [key: string]: unknown }> | null = null;\n _isAudioContent: boolean | undefined;\n _isFallingBack: boolean | undefined;\n _managersLoading: Promise<unknown> | null = null;\n _originalElement!: HTMLElement;\n /** Lazily-created on first pseudo-fullscreen entry. Owns the scroll /\n * inert / viewport bookkeeping that used to live as `_original*`\n * fields directly on the player. */\n pseudoFullscreen: PseudoFullscreenController | null = null;\n /** Owns `applyTheme`/`setTheme`/`setThemeVariable`/`resetTheme`. Player\n * keeps delegating public methods so the existing API is unchanged. */\n themeManager!: ThemeManager;\n /** Owns poster resolution, canvas-capture, and overlay show/hide. */\n posterManager!: PosterManager;\n /** Owns resume-playback prompt + progress persistence. Lazily\n * created the first time `initResumePlayback` is called so sites\n * that don't enable the feature don't pay the DOM / listener cost. */\n resumeManager: ResumeManager | null = null;\n /** Owns resize-observer, orientation matchMedia, and the\n * cross-vendor fullscreenchange listeners. */\n responsiveManager!: ResponsiveManager;\n /** Owns `kind=metadata` text-track directives (PAUSE, FOCUS,\n * #hashtag) + the per-selector alert UI. Lazily created on first\n * `setupMetadataHandling()` call. */\n metadataAlertsManager: MetadataAlertsManager | null = null;\n _pendingSource: string | null = null;\n _sourceElementsCache: HTMLSourceElement[] | null = null;\n _sourceElementsDirty: boolean = true;\n _switchingRenderer: boolean | undefined;\n _trackElementsCache: HTMLTrackElement[] | null = null;\n _trackElementsDirty: boolean = true;\n _textTracksCache: TextTrack[] | null = null;\n _textTracksDirty: boolean | undefined;\n audioDescriptionCaptionTracks: unknown[] = [];\n audioDescriptionSourceElement: HTMLSourceElement | null = null;\n audioDescriptionSrc: string | null = null;\n currentSignLanguage: string | null = null;\n currentSource: string | null = null;\n debouncedPositionPlayOverlay: ((...args: unknown[]) => void) | null = null;\n fullscreenChangeHandler: (() => void) | null = null;\n /** Mirrored from `MetadataAlertsManager` so the TextTrack cleanup\n * path in `destroy()` can still find it by a fixed field name. */\n metadataCueChangeHandler: (() => void) | null = null;\n noticeElement: HTMLElement | null = null;\n noticeTimeout: ReturnType<typeof setTimeout> | null = null;\n orientationHandler: ((e: MediaQueryListEvent) => void) | null = null;\n orientationQuery: MediaQueryList | null = null;\n originalAudioDescriptionSource: string | null = null;\n originalSrc: string | null = null;\n playButtonOverlay: SVGSVGElement | null = null;\n resizeHandler: (() => void) | null = null;\n resizeObserver: ResizeObserver | null = null;\n resumePromptElement: HTMLElement | null = null;\n signLanguageDraggable: DraggableResizable | null = null;\n signLanguageHeader: HTMLElement | null = null;\n signLanguageSettingsButton: HTMLButtonElement | null = null;\n signLanguageSettingsMenu: HTMLElement | null = null;\n signLanguageSettingsMenuVisible: boolean = false;\n signLanguageSources: Record<string, string> = {};\n signLanguageSrc: string | null = null;\n signLanguageVideo: HTMLVideoElement | null = null;\n signLanguageWrapper: HTMLElement | null = null;\n timeouts: Set<ReturnType<typeof setTimeout>> = new Set();\n trackArtworkElement: HTMLElement | null = null;\n videoWrapper: HTMLElement | null = null;\n /** Centered buffering spinner (see `.vidply-loading` / `.vidply-buffering` in CSS) */\n loadingOverlayElement: HTMLElement | null = null;\n /** Native `playing` listener — must be removed in destroy() */\n _bufferingHideOnMediaPlaying: (() => void) | null = null;\n /** AbortController, whose signal feeds every window/document listener and\n * every user-influenced fetch the Player creates. `destroy()` calls\n * `abort()` so a torn-down player can never leak listeners or pending\n * network calls. */\n private _lifecycleController: AbortController = new AbortController();\n\n constructor(element: string | HTMLElement, options: Record<string, unknown> = {}) {\n super();\n\n this.element = (typeof element === 'string' ? document.querySelector(element) : element) as HTMLMediaElement;\n if (!this.element) {\n throw new Error('VidPly: Element not found');\n }\n\n // Assign unique instance ID\n playerInstanceCounter++;\n this.instanceId = playerInstanceCounter;\n\n // Auto-creates a media element if a non-media element is provided.\n // mediaType is restricted to a hard-coded allowlist so an attacker\n // cannot smuggle, say, `script` or `object` via a `data-vidply-options`\n // attribute they control.\n if (this.element.tagName !== 'VIDEO' && this.element.tagName !== 'AUDIO') {\n const requested = typeof options.mediaType === 'string' ? options.mediaType.toLowerCase() : 'video';\n const mediaType: AllowedMediaType = (ALLOWED_MEDIA_TYPES as readonly string[]).includes(requested)\n ? (requested as AllowedMediaType)\n : 'video';\n if (mediaType !== requested) {\n console.warn(`[VidPly] Ignoring unsafe mediaType \"${requested}\", falling back to \"video\"`);\n }\n const mediaElement = document.createElement(mediaType);\n\n // Copy attributes from the div to the media element\n Array.from(this.element.attributes).forEach(attr => {\n if (attr.name !== 'id' && attr.name !== 'class' && !attr.name.startsWith('data-')) {\n mediaElement.setAttribute(attr.name, attr.value);\n }\n });\n\n // Copy any track elements from the div\n const tracks = this.element.querySelectorAll('track');\n tracks.forEach((track: HTMLTrackElement) => {\n mediaElement.appendChild(track.cloneNode(true));\n });\n\n // Clear the div and insert the media element\n this.element.replaceChildren(mediaElement);\n\n // Update element reference to the actual media element\n this.element = mediaElement as HTMLMediaElement;\n }\n\n // Store original element reference for mixed media swapping\n // This allows us to swap between video and audio elements in mixed playlists\n this._originalElement = this.element;\n\n // Default options\n this.options = {\n // Display\n width: null,\n height: null,\n poster: null,\n responsive: true,\n fillContainer: false,\n\n // Playback\n autoplay: false,\n loop: false,\n muted: false,\n volume: 0.8,\n playbackSpeed: 1.0,\n preload: 'metadata',\n // Optional initial duration (seconds) so UI can show duration\n // before media metadata is loaded (useful with deferLoad/preload=none).\n initialDuration: 0,\n // When enabled, VidPly will not start network loading during init().\n // - HTML5: does not call element.load() until the first user-initiated play()\n // - HLS (hls.js): does not load manifest/segments until the first play()\n // - DASH (dash.js): does not attach a source until the first play()\n // This is useful for pages with many players to avoid high initial bandwidth.\n deferLoad: false,\n // When enabled, clicking Audio Description / Sign Language before playback will show\n // a notice instead of implicitly starting playback/loading.\n requirePlaybackForAccessibilityToggles: false,\n startTime: 0,\n playsInline: true, // Enable inline playback on iOS (prevents native fullscreen)\n\n // Controls\n controls: true,\n hideControlsDelay: 3000,\n playPauseButton: true,\n progressBar: true,\n currentTime: true,\n duration: true,\n volumeControl: true,\n muteButton: true,\n chaptersButton: true,\n qualityButton: true,\n captionStyleButton: true,\n speedButton: true,\n // When enabled, the playback speed UI is suppressed for ALL HLS streams (audio + video).\n hideSpeedForHls: false,\n // When enabled, the playback speed UI is suppressed for HLS *video* streams only.\n // This is useful for live streams where speed controls don't make sense.\n hideSpeedForHlsVideo: false,\n // When enabled, the playback speed UI is suppressed for ALL DASH streams (audio + video).\n hideSpeedForDash: false,\n // When enabled, the playback speed UI is suppressed for DASH *video* streams only.\n hideSpeedForDashVideo: false,\n captionsButton: true,\n transcriptButton: true,\n fullscreenButton: true,\n pipButton: false,\n floating: false,\n floatingPosition: 'bottom-right',\n floatingMinViewportWidth: 768,\n downloadButton: false,\n downloadUrl: null,\n downloadFormat: null,\n downloadFileSize: null,\n downloadFetchSize: true,\n\n // Seeking\n seekInterval: 10,\n seekIntervalLarge: 30,\n\n // Captions\n captions: true,\n captionsDefault: false,\n captionsFontSize: '100%',\n captionsFontFamily: 'sans-serif',\n captionsColor: '#FFFFFF',\n captionsBackgroundColor: '#000000',\n captionsOpacity: 0.8,\n\n // Audio Description\n audioDescription: true,\n audioDescriptionSrc: null, // URL to audio-described version\n audioDescriptionButton: true,\n audioDescriptionMode: 'auto',\n audioDescriptionSpeech: true,\n audioDescriptionExtended: true,\n\n // Sign Language\n signLanguage: true,\n signLanguageSrc: null, // URL to sign language video\n signLanguageButton: true,\n signLanguagePosition: 'bottom-right', // Position: 'bottom-right', 'bottom-left', 'top-right', 'top-left'\n signLanguageDisplayMode: 'both', // Display mode: 'pip' (overlay), 'main' (source swap), 'both'\n\n // Transcripts\n transcript: false,\n transcriptPosition: 'external',\n transcriptContainer: null,\n\n // Keyboard\n keyboard: true,\n keyboardShortcuts: {\n 'play-pause': [' ', 'p', 'k'],\n 'volume-up': ['ArrowUp'],\n 'volume-down': ['ArrowDown'],\n 'seek-forward': ['ArrowRight'],\n 'seek-backward': ['ArrowLeft'],\n 'mute': ['m'],\n 'fullscreen': ['f'],\n 'captions': ['c'],\n 'caption-style-menu': ['a'],\n 'speed-up': ['>'],\n 'speed-down': ['<'],\n 'speed-menu': ['s'],\n 'quality-menu': ['q'],\n 'chapters-menu': ['j'],\n 'transcript-toggle': ['t']\n },\n\n // Accessibility\n ariaLabels: {},\n screenReaderAnnouncements: true,\n highContrast: false,\n focusHighlight: true,\n metadataAlerts: {},\n metadataHashtags: {},\n\n // Languages\n language: 'en',\n languages: ['en'],\n\n // Resume Playback\n resumePlayback: true, // Enable saving and resuming playback position\n resumeThreshold: 10, // Don't resume if < threshold seconds watched\n resumePrompt: true, // Show prompt to resume (false = auto-resume silently)\n\n // Thumbnail Preview\n thumbnailPreview: true, // Enable/disable thumbnail preview on seek bar\n thumbnailCacheSize: 50, // Max cached thumbnails (default increased from 20)\n thumbnailPregenerate: true, // Pre-generate thumbnails during idle time\n thumbnailInterval: 10, // Pre-generation interval in seconds\n thumbnailWidth: 160, // Thumbnail width\n thumbnailHeight: 90, // Thumbnail height\n thumbnailQuality: 0.8, // Thumbnail JPEG quality\n\n // Lazy Loading (primarily used by index.js auto-init)\n lazyInit: true, // Enable lazy initialization via IntersectionObserver\n lazyMargin: '200px', // Root margin for IntersectionObserver\n\n // Theming\n theme: 'dark', // Theme: 'dark', 'light', 'minimal', 'high-contrast'\n themeVariables: {}, // Custom CSS variable overrides (e.g., { 'primary': '#ff0000' })\n\n // Advanced\n debug: false,\n classPrefix: 'vidply',\n iconType: 'svg',\n pauseOthersOnPlay: true,\n\n // Callbacks\n onReady: null,\n onPlay: null,\n onPause: null,\n onEnded: null,\n onTimeUpdate: null,\n onVolumeChange: null,\n onError: null,\n\n ...options\n };\n\n this.options.metadataAlerts = this.options.metadataAlerts || {};\n this.options.metadataHashtags = this.options.metadataHashtags || {};\n\n // Notice UI\n this.noticeElement = null;\n this.noticeTimeout = null;\n\n // Storage manager\n this.storage = new StorageManager('vidply');\n\n // Theme manager — cheap to construct (no DOM work in ctor) so we\n // wire it up immediately. `applyTheme()` is called later once the\n // container exists.\n this.themeManager = new ThemeManager(this);\n this.posterManager = new PosterManager(this);\n this.responsiveManager = new ResponsiveManager(this);\n\n // Load saved player preferences\n const savedPrefs = this.storage.getPlayerPreferences();\n if (savedPrefs) {\n if (typeof savedPrefs.volume === 'number') this.options.volume = savedPrefs.volume;\n if (typeof savedPrefs.playbackSpeed === 'number') this.options.playbackSpeed = savedPrefs.playbackSpeed;\n if (typeof savedPrefs.muted === 'boolean') this.options.muted = savedPrefs.muted;\n }\n\n // State\n this.state = {\n ready: false,\n playing: false,\n paused: true,\n ended: false,\n buffering: false,\n seeking: false,\n hasStartedPlayback: false,\n muted: this.options.muted,\n volume: this.options.volume,\n currentTime: 0,\n duration: Number(this.options.initialDuration) > 0 ? Number(this.options.initialDuration) : 0,\n playbackSpeed: this.options.playbackSpeed,\n fullscreen: false,\n pip: false,\n floating: null,\n captionsEnabled: this.options.captionsDefault,\n currentCaption: null,\n controlsVisible: true,\n audioDescriptionEnabled: false,\n signLanguageEnabled: false,\n signLanguageInMainView: false,\n resumePromptVisible: false\n };\n\n // Resume playback: prompt DOM element lives here so\n // `ResumeManager` can attach/detach it without leaking container\n // state back into the manager.\n this.resumePromptElement = null;\n\n // Store original source for toggling\n this.originalSrc = null;\n this.audioDescriptionSrc = this.options.audioDescriptionSrc;\n this.signLanguageSrc = this.options.signLanguageSrc;\n this.signLanguageSources = this.options.signLanguageSources || {}; // Map of lang codes to video URLs\n this.currentSignLanguage = null; // Current selected sign language code\n this.signLanguageVideo = null;\n // Store references to source elements with audio description attributes\n this.audioDescriptionSourceElement = null;\n this.originalAudioDescriptionSource = null;\n // Store caption tracks that should be swapped for audio description\n this.audioDescriptionCaptionTracks = [];\n this._audioDescriptionDesiredState = false;\n\n // DOM query cache (for performance optimization)\n this._textTracksCache = null;\n this._textTracksDirty = true;\n this._sourceElementsCache = null;\n this._sourceElementsDirty = true;\n this._trackElementsCache = null;\n this._trackElementsDirty = true;\n\n // Timeout management (for cleanup)\n this.timeouts = new Set();\n\n // Components\n this.container = document.createElement('div');\n this.renderer = null;\n this.controlBar = null;\n this.captionManager = null;\n this.keyboardManager = null;\n this.settingsDialog = null;\n\n // Metadata handling\n this.metadataCueChangeHandler = null;\n\n // Feature managers (lazy-loaded)\n this.audioDescriptionManager = null;\n this.signLanguageManager = null;\n this._managersLoading = null; // Promise for loading managers\n\n // Backward-compatible property aliases for SignLanguageManager\n // These allow existing code to reference this.signLanguageWrapper, etc.\n Object.defineProperties(this, {\n signLanguageWrapper: {\n get: () => this.signLanguageManager?.wrapper,\n set: (v) => {\n if (this.signLanguageManager) this.signLanguageManager.wrapper = v;\n }\n },\n signLanguageVideo: {\n get: () => this.signLanguageManager?.video,\n set: (v) => {\n if (this.signLanguageManager) this.signLanguageManager.video = v;\n }\n },\n signLanguageHeader: {\n get: () => this.signLanguageManager?.header,\n set: (v) => {\n if (this.signLanguageManager) this.signLanguageManager.header = v;\n }\n },\n signLanguageSettingsButton: {\n get: () => this.signLanguageManager?.settingsButton,\n set: (v) => {\n if (this.signLanguageManager) this.signLanguageManager.settingsButton = v;\n }\n },\n signLanguageSettingsMenu: {\n get: () => this.signLanguageManager?.settingsMenu,\n set: (v) => {\n if (this.signLanguageManager) this.signLanguageManager.settingsMenu = v;\n }\n },\n signLanguageSettingsMenuVisible: {\n get: () => this.signLanguageManager?.settingsMenuVisible,\n set: (v) => {\n if (this.signLanguageManager) this.signLanguageManager.settingsMenuVisible = v;\n }\n },\n signLanguageDraggable: {\n get: () => this.signLanguageManager?.draggable,\n set: (v) => {\n if (this.signLanguageManager) this.signLanguageManager.draggable = v;\n }\n },\n currentSignLanguage: {\n get: () => this.signLanguageManager?.currentLanguage,\n set: (v) => {\n if (this.signLanguageManager) this.signLanguageManager.currentLanguage = v;\n }\n }\n });\n\n // Initialize\n this.init();\n }\n\n /** Convenience getter for subsystems that take an AbortSignal. */\n get lifecycleSignal(): AbortSignal {\n return this._lifecycleController.signal;\n }\n\n /**\n * Get cached text tracks array\n * @returns {Array} Array of text tracks\n */\n get textTracks() {\n if (!this._textTracksCache || this._textTracksDirty) {\n this._textTracksCache = Array.from(this.element.textTracks || []);\n this._textTracksDirty = false;\n }\n return this._textTracksCache;\n }\n\n /**\n * Get cached source elements array\n * @returns {Array} Array of source elements\n */\n get sourceElements() {\n if (!this._sourceElementsCache || this._sourceElementsDirty) {\n this._sourceElementsCache = Array.from(this.element.querySelectorAll('source'));\n this._sourceElementsDirty = false;\n }\n return this._sourceElementsCache;\n }\n\n /**\n * Get cached track elements array\n * @returns {Array} Array of track elements\n */\n get trackElements() {\n if (!this._trackElementsCache || this._trackElementsDirty) {\n this._trackElementsCache = Array.from(this.element.querySelectorAll('track'));\n this._trackElementsDirty = false;\n }\n return this._trackElementsCache;\n }\n\n /**\n * Show a small in-player notice (non-blocking), also announced to screen readers.\n */\n showNotice(message: string, {timeout = 2500, priority = 'polite' as 'polite' | 'assertive'} = {}) {\n try {\n if (!message) return;\n if (!this.container) return;\n\n if (this.keyboardManager?.announce) {\n this.keyboardManager.announce(message, priority);\n }\n\n if (!this.noticeElement) {\n const el = document.createElement('div');\n el.className = `${this.options.classPrefix}-notice`;\n el.setAttribute('role', 'status');\n el.setAttribute('aria-live', priority);\n el.setAttribute('aria-atomic', 'true');\n el.style.position = 'absolute';\n el.style.left = '0.75rem';\n el.style.right = '0.75rem';\n el.style.top = '0.75rem';\n el.style.zIndex = '9999';\n el.style.padding = '0.5rem 0.75rem';\n el.style.borderRadius = '0.5rem';\n el.style.background = 'rgba(0, 0, 0, 0.75)';\n el.style.color = '#fff';\n el.style.fontSize = '0.875rem';\n el.style.lineHeight = '1.3';\n el.style.pointerEvents = 'none';\n this.noticeElement = el;\n this.container.appendChild(el);\n }\n\n const noticeElement = this.noticeElement;\n noticeElement.textContent = message;\n noticeElement.style.display = 'block';\n\n if (this.noticeTimeout) {\n clearTimeout(this.noticeTimeout);\n this.noticeTimeout = null;\n }\n this.noticeTimeout = setTimeout(() => {\n if (this.noticeElement) {\n this.noticeElement.style.display = 'none';\n }\n }, timeout);\n } catch {\n // ignore\n }\n }\n\n async init() {\n try {\n this.log('Initializing VidPly player');\n\n // Load custom language files if specified\n if (this.options.languageFiles) {\n try {\n await i18n.loadLanguagesFromUrls(this.options.languageFiles);\n } catch (error: unknown) {\n console.warn('Failed to load some language files:', error);\n }\n }\n\n if (this.options.languageFile && this.options.languageFileUrl) {\n try {\n await i18n.loadLanguageFromUrl(this.options.languageFile, this.options.languageFileUrl);\n this.log(`Custom language file loaded for ${this.options.languageFile}`);\n } catch (error: unknown) {\n console.warn(`Failed to load language file for ${this.options.languageFile}:`, error);\n }\n }\n\n // Auto-detect language from HTML lang attribute if not explicitly set\n if (!this.options.language || this.options.language === 'en') {\n const htmlLang = this.detectHtmlLanguage();\n if (htmlLang) {\n this.options.language = htmlLang;\n this.log(`Auto-detected language from HTML: ${htmlLang}`);\n }\n }\n\n // Ensure we have a language set (default to 'en' if not set)\n if (!this.options.language) {\n this.options.language = 'en';\n }\n\n // Ensure requested language is available (loads built-ins on demand)\n await i18n.ensureLanguage(this.options.language);\n\n // Set language\n i18n.setLanguage(this.options.language);\n\n // Create container\n this.createContainer();\n\n // Suppress native Picture-in-Picture when the custom floating\n // player is enabled. This removes Chrome's hover PiP button and\n // the \"Picture in Picture\" entry in the video context menu and\n // prevents the remote playback indicator from hijacking playback.\n if (this.options.floating && this.element && this.element.tagName === 'VIDEO') {\n try {\n const mediaEl = this.element as HTMLMediaElement & {\n disablePictureInPicture?: boolean;\n disableRemotePlayback?: boolean\n };\n mediaEl.disablePictureInPicture = true;\n mediaEl.disableRemotePlayback = true;\n this.element.setAttribute('disablepictureinpicture', '');\n this.element.setAttribute('disableremoteplayback', '');\n } catch (err) {\n this.log(`Failed to disable native PiP: ${err}`, 'warn');\n }\n }\n\n const src: string | undefined = this.element.src || this.element.querySelector('source')?.src;\n if (src) {\n await this.initializeRenderer();\n } else {\n this.log('No initial source - waiting for playlist or manual load');\n }\n\n // Initialize feature managers (lazy-loaded based on options)\n await this.initFeatureManagers();\n\n // Create controls\n if (this.options.controls) {\n this.controlBar = new ControlBar(this);\n this.videoWrapper?.appendChild(this.controlBar.element);\n }\n\n // Initialize captions\n if (this.options.captions) {\n this.captionManager = new CaptionManager(this);\n }\n\n // Initialize transcript lazily unless explicitly requested\n if (this.options.transcript) {\n await this.ensureTranscriptManager();\n }\n\n // Always set up metadata track handling (independent of transcript)\n this.setupMetadataHandling();\n\n // Initialize keyboard controls\n if (this.options.keyboard) {\n this.keyboardManager = new KeyboardManager(this);\n }\n\n // Set up responsive handlers\n this.setupResponsiveHandlers();\n\n // Set initial state\n if (this.options.startTime > 0) {\n this.seek(this.options.startTime);\n }\n\n // Apply volume and mute settings after renderer is initialized\n // Use requestAnimationFrame to ensure renderer is fully ready\n requestAnimationFrame(() => {\n if (this.options.muted) {\n this.mute();\n } else if (this.renderer && this.renderer.media) {\n // Ensure media element is not muted if options say it shouldn't be\n this.renderer.setMuted(false);\n }\n\n if (this.options.volume !== 0.8) {\n this.setVolume(this.options.volume);\n } else if (this.renderer && this.renderer.media) {\n // Ensure volume is set even if it's the default\n this.renderer.setVolume(this.options.volume);\n }\n });\n\n // Initialize resume playback feature\n if (this.options.resumePlayback) {\n this.initResumePlayback();\n }\n\n // Mark as ready\n this.state.ready = true;\n this._originalElement.classList.add('vidply-initialized');\n this.emit('ready');\n\n if (this.options.onReady) {\n this.options.onReady.call(this);\n }\n\n // Autoplay if enabled\n if (this.options.autoplay) {\n this.play();\n }\n\n this.log('Player initialized successfully');\n } catch (error: unknown) {\n this.handleError(error);\n }\n }\n\n /**\n * Ensure the transcript manager is available, creating it on demand.\n * This keeps the initial load fast when transcripts are not needed.\n */\n async ensureTranscriptManager() {\n if (this.transcriptManager) {\n return this.transcriptManager;\n }\n\n if (!this.options.transcript && !this.options.transcriptButton) {\n return null;\n }\n\n const module = await import('../controls/TranscriptManager.js');\n const fallbackDefault = (module as { default?: typeof TranscriptManager }).default;\n const Manager = module.TranscriptManager || fallbackDefault;\n\n if (!Manager) {\n return null;\n }\n\n this.transcriptManager = new Manager(this);\n return this.transcriptManager;\n }\n\n /**\n * Toggle transcript visibility, lazily creating the manager if necessary.\n */\n async toggleTranscript() {\n const manager = await this.ensureTranscriptManager();\n if (!manager) return;\n\n manager.toggleTranscript();\n if (this.controlBar) {\n this.controlBar.updateTranscriptButton();\n }\n }\n\n /**\n * Ensure the audio description manager is available, creating it on demand.\n * This keeps the initial load fast when an audio description is not needed.\n */\n async ensureAudioDescriptionManager() {\n if (this.audioDescriptionManager) {\n return this.audioDescriptionManager;\n }\n\n // Only load the chunk when there is actual audio-description content to\n // drive it. The button option alone (default `true`) is not enough — the\n // control bar already hides the button when no content is present, so a\n // content-less player never needs this manager.\n if (!this.hasAudioDescriptionContent()) {\n return null;\n }\n\n const AudioDescManager = await loadAudioDescriptionManager();\n this.audioDescriptionManager = new AudioDescManager(this);\n return this.audioDescriptionManager;\n }\n\n /**\n * True when the current media actually exposes audio-description content:\n * an explicit described-audio source, `<source>` elements carrying\n * `data-desc-src` / `data-orig-src`, or a `descriptions` text track.\n * Mirrors `ControlBar.hasAudioDescription()` so the chunk load and the\n * button visibility stay in lock-step.\n */\n hasAudioDescriptionContent(): boolean {\n if (this.options.audioDescriptionSrc || this.audioDescriptionSrc) {\n return true;\n }\n\n const hasSourceElementsWithDesc = this.sourceElements.some(\n (el: HTMLSourceElement) => el.getAttribute('data-desc-src') || el.getAttribute('data-orig-src')\n );\n if (hasSourceElementsWithDesc) {\n return true;\n }\n\n const textTracks = this.element ? Array.from(this.element.textTracks || []) : [];\n return textTracks.some((track) => track.kind === 'descriptions');\n }\n\n // ============================================\n // Resume Playback Methods\n // ============================================\n\n /**\n * Ensure the sign language manager is available, creating it on demand.\n * This keeps the initial load fast when sign language is not needed.\n */\n async ensureSignLanguageManager() {\n if (this.signLanguageManager) {\n return this.signLanguageManager;\n }\n\n // Only load the chunk when an actual sign-language source is configured.\n // As with audio description, the button alone (default `true`) is not a\n // reason to pull in the manager on content-less players.\n if (!this.hasSignLanguageContent()) {\n return null;\n }\n\n const SignLangManager = await loadSignLanguageManager();\n this.signLanguageManager = new SignLangManager(this);\n return this.signLanguageManager;\n }\n\n /**\n * True when a sign-language video source (single `signLanguageSrc` or a\n * `signLanguageSources` map) is configured. Mirrors\n * `ControlBar.hasSignLanguage()`.\n */\n hasSignLanguageContent(): boolean {\n if (this.options.signLanguageSrc || this.signLanguageSrc) {\n return true;\n }\n return !!(\n this.options.signLanguageSources &&\n Object.keys(this.options.signLanguageSources).length > 0\n );\n }\n\n /**\n * Lazy-load and instantiate the floating (in-page PiP) manager. Only\n * created when `options.floating === true` and the media element is a\n * <video>. Audio-only players never float.\n */\n async ensureFloatingPlayerManager() {\n if (this.floatingPlayerManager) {\n return this.floatingPlayerManager;\n }\n if (!this.options.floating) {\n return null;\n }\n if (!this.element || this.element.tagName !== 'VIDEO') {\n return null;\n }\n const FloatingManager = await loadFloatingPlayerManager();\n this.floatingPlayerManager = new FloatingManager(this);\n return this.floatingPlayerManager;\n }\n\n /**\n * Initialize feature managers if needed (called during init)\n */\n async initFeatureManagers() {\n const promises = [];\n\n // Load the audio-description manager only when the media actually carries\n // description content (described-audio source, `data-desc-src` sources, or\n // a `descriptions` text track). The button option alone no longer pulls in\n // the chunk — content-less players stay lean. Late-arriving content (e.g.\n // streaming description tracks) is handled by `updateAccessibilityButtons`\n // + the lazy `ensureAudioDescriptionManager()` on first toggle.\n if (this.hasAudioDescriptionContent()) {\n promises.push(this.ensureAudioDescriptionManager());\n }\n\n // Load the sign-language manager only when a sign-language source exists.\n if (this.hasSignLanguageContent()) {\n promises.push(this.ensureSignLanguageManager());\n }\n\n // Load floating (custom in-page PiP) manager if enabled\n if (this.options.floating && this.element && this.element.tagName === 'VIDEO') {\n promises.push(this.ensureFloatingPlayerManager());\n }\n\n if (promises.length > 0) {\n await Promise.all(promises);\n }\n\n if (this.audioDescriptionManager) {\n this.audioDescriptionManager.initFromSourceElements(this.sourceElements, this.trackElements);\n }\n }\n\n /**\n * Detect language from HTML lang attribute\n * @returns {string|null} Language code if available in translations or as built-in, null otherwise\n */\n detectHtmlLanguage() {\n // Try to get lang from html element\n const htmlLang = document.documentElement.lang || document.documentElement.getAttribute('lang');\n\n if (!htmlLang) {\n return null;\n }\n\n // Normalize the language code (e.g., \"en-US\" -> \"en\", \"de-DE\" -> \"de\")\n const normalizedLang = htmlLang.toLowerCase().split('-')[0];\n if (!normalizedLang) {\n return null;\n }\n\n // Check if this language is available in our translations (already loaded)\n if (i18n.translations[normalizedLang]) {\n return normalizedLang;\n }\n\n // Check if this language is available as a built-in that can be loaded on demand\n const i18nWithLoaders = i18n as unknown as { builtInLanguageLoaders?: Record<string, unknown> };\n if (i18nWithLoaders.builtInLanguageLoaders && i18nWithLoaders.builtInLanguageLoaders[normalizedLang]) {\n return normalizedLang;\n }\n\n // Language not available, will fall back to English\n this.log(`Language \"${htmlLang}\" not available, using English as fallback`);\n return null; // Return null instead of 'en' to let the default language handling work\n }\n\n /**\n * Initialise the resume-playback feature. Lazily constructs a\n * `ResumeManager` on first use so disabled pages don't pay the DOM\n * / listener cost. Repeat calls are safe — the manager's own\n * `init()` is idempotent.\n */\n initResumePlayback(): void {\n if (!this.resumeManager) {\n this.resumeManager = new ResumeManager(this);\n }\n this.resumeManager.init();\n }\n\n /**\n * Get a unique identifier for the current video\n * Uses data-video-id attribute if available, otherwise hashes the source URL\n * @returns {string|null} Video ID or null if not available\n */\n getVideoId() {\n // First check for explicit video ID attribute\n const explicitId = this.element.getAttribute('data-video-id') ||\n this.element.dataset.videoId ||\n this._originalElement?.getAttribute('data-video-id') ||\n this._originalElement?.dataset?.videoId;\n\n if (explicitId) {\n return explicitId;\n }\n\n // Get source URL\n let src: string | null | undefined = this.element.src;\n if (!src) {\n const sourceEl = this.element.querySelector<HTMLSourceElement>('source');\n src = sourceEl?.src;\n }\n\n if (!src) {\n return null;\n }\n\n return this._hashString(src);\n }\n\n /**\n * Simple string hash function\n * @param {string} str - String to hash\n * @returns {string} Hash string\n */\n _hashString(str: string) {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash = hash & hash;\n }\n return 'v_' + Math.abs(hash).toString(36);\n }\n\n // Resume-playback delegates. Implementations live in\n // `core/ResumeManager.ts`; these stubs keep the public API.\n saveProgress(): void { this.resumeManager?.saveProgress(); }\n\n checkForResume(): void { this.resumeManager?.checkForResume(); }\n\n showResumePrompt(savedTime: number): void { this.resumeManager?.showPrompt(savedTime); }\n\n hideResumePrompt(): void { this.resumeManager?.hidePrompt(); }\n\n // Theme delegates. All four keep their original names so external\n // callers keep working; the real work is in `core/ThemeManager.ts`.\n applyTheme(): void { this.themeManager.apply(); }\n\n setTheme(themeName: ThemeName, customVariables: Record<string, string> = {}): void {\n this.themeManager.set(themeName, customVariables);\n }\n\n getTheme(): ThemeName | undefined { return this.themeManager.get(); }\n\n setThemeVariable(variableName: string, value: string): void {\n this.themeManager.setVariable(variableName, value);\n }\n\n resetTheme(): void { this.themeManager.reset(); }\n\n createContainer() {\n // Create main container with unique label for multiple players on same page\n const playerLabel = this.instanceId > 1\n ? `${i18n.t('player.label')} ${this.instanceId}`\n : i18n.t('player.label');\n\n // Use role=\"region\" (a labelled perceivable section) rather than\n // role=\"application\". role=\"application\" forces most screen readers into\n // application mode, suppressing their virtual-cursor/browse mode and\n // standard reading shortcuts for everything inside the player. A media\n // player built from native <button> controls and a custom slider does not\n // need that; a labelled region keeps normal AT navigation while the\n // control bar (role=\"region\"/toolbar) and keyboard shortcuts still work\n // when the container is focused (WCAG 4.1.2, 2.1.1).\n this.container = DOMUtils.createElement('div', {\n className: `${this.options.classPrefix}-player`,\n attributes: {\n 'role': 'region',\n 'aria-label': playerLabel,\n 'tabindex': '0'\n }\n });\n\n // Add media type class\n const mediaType = this.element.tagName.toLowerCase();\n this.container.classList.add(`${this.options.classPrefix}-${mediaType}`);\n\n // Add responsive class\n if (this.options.responsive) {\n this.container.classList.add(`${this.options.classPrefix}-responsive`);\n }\n\n // Create video wrapper (for proper positioning of controls)\n this.videoWrapper = DOMUtils.createElement('div', {\n className: `${this.options.classPrefix}-video-wrapper`\n });\n\n // Wrap original element\n this.element.parentNode?.insertBefore(this.container, this.element);\n\n // Create a track artwork element for single audio files (before video wrapper)\n // This shows the poster/artwork above the audio player (similar to playlists).\n // Poster URL is validated and CSS-escaped before interpolation.\n if (this.element.tagName === 'AUDIO' && this.options.poster) {\n const safePoster = sanitizePosterUrl(this.options.poster);\n if (safePoster) {\n this.trackArtworkElement = DOMUtils.createElement('div', {\n className: `${this.options.classPrefix}-track-artwork`,\n attributes: {\n 'aria-hidden': 'true'\n }\n });\n this.trackArtworkElement.style.backgroundImage = `url(\"${cssEscapeUrl(safePoster)}\")`;\n this.container.appendChild(this.trackArtworkElement);\n } else {\n this.log(`[VidPly] Ignored unsafe poster URL`, 'warn');\n }\n }\n\n this.container.appendChild(this.videoWrapper);\n this.videoWrapper.appendChild(this.element);\n\n // Hide native controls and set dimensions\n this.element.controls = false;\n this.element.removeAttribute('controls');\n this.element.setAttribute('tabindex', '-1'); // Remove from tab order\n this.element.style.width = '100%';\n this.element.style.height = '100%';\n\n // Enable inline playback on iOS (prevents native fullscreen)\n // This allows custom controls to work on iOS devices\n if (this.element.tagName === 'VIDEO' && this.options.playsInline) {\n this.element.setAttribute('playsinline', '');\n (this.element as HTMLVideoElement).playsInline = true; // Property version\n }\n\n // Set dimensions\n if (this.options.width) {\n this.container.style.width = typeof this.options.width === 'number'\n ? `${this.options.width}px`\n : this.options.width;\n }\n\n if (this.options.height) {\n this.container.style.height = typeof this.options.height === 'number'\n ? `${this.options.height}px`\n : this.options.height;\n }\n\n // Set a poster (convert relative paths to absolute URLs). The\n // resolvePosterPath() output is then re-validated to ensure no\n // disallowed scheme survived.\n if (this.options.poster && this.element.tagName === 'VIDEO') {\n const resolvedPoster = sanitizePosterUrl(this.resolvePosterPath(this.options.poster));\n if (resolvedPoster) {\n (this.element as HTMLVideoElement).poster = resolvedPoster;\n }\n }\n\n // Create centered play button overlay (only for video)\n if (this.element.tagName === 'VIDEO') {\n this.createPlayButtonOverlay();\n }\n\n this.createBufferingLoadingOverlay();\n\n (this.element as HTMLMediaElement & { vidply?: Player }).vidply = this;\n\n // Add to static instances array\n Player.instances.push(this);\n\n // Make video/audio element clickable to toggle play/pause. The\n // listener is wired to the Player's lifecycle AbortController so\n // destroy() removes it without an explicit removeEventListener\n // pair.\n this.element.style.cursor = 'pointer';\n this.element.addEventListener('click', (e) => {\n if (e.target === this.element) {\n this.toggle();\n }\n }, {signal: this.lifecycleSignal});\n\n this.on('play', () => {\n this.state.hasStartedPlayback = true;\n // Hide poster immediately when playing\n this.hidePosterOverlay();\n });\n\n this.on('timeupdate', () => {\n // Hide the poster when the video has started playing (currentTime > 0).\n // We additionally gate on `hasStartedPlayback` to ignore\n // background `currentTime` movements that the user did not\n // initiate — most notably dash.js startup gap-jump, which\n // synthesizes a small seek (often ~0.08s) right after MSE\n // attach to skip a manifest gap. Without this guard, that\n // synthetic seek hides the poster before the user has ever\n // pressed play, defeating the showPosterOverlay() call we\n // make from DASHRenderer.init() to keep the artwork visible\n // on top of the (transparent) <video> element.\n if (this.state.hasStartedPlayback && this.state.currentTime > 0) {\n this.hidePosterOverlay();\n }\n });\n\n // Also hide poster on loadeddata event (when first frame is loaded).\n // The same hasStartedPlayback gate as above: dash.js loads init+startup\n // segments at attachSource time, which triggers loadeddata before\n // the user clicks play. Hiding the poster there reveals the first\n // decoded frame instead of the artwork the publisher chose.\n this.element.addEventListener('loadeddata', () => {\n if (this.state.hasStartedPlayback && (this.state.playing || this.state.currentTime > 0)) {\n this.hidePosterOverlay();\n }\n }, {once: true});\n\n // Apply theme\n this.applyTheme();\n }\n\n createPlayButtonOverlay() {\n const overlay = createPlayOverlay();\n this.playButtonOverlay = overlay;\n\n overlay.addEventListener('click', () => {\n this.toggle();\n });\n\n this.videoWrapper?.appendChild(overlay);\n\n this.on('play', () => {\n overlay.style.opacity = '0';\n overlay.style.pointerEvents = 'none';\n });\n\n this.on('pause', () => {\n overlay.style.opacity = '1';\n overlay.style.pointerEvents = 'auto';\n this.positionPlayOverlayOnMobile();\n });\n\n this.on('ended', () => {\n overlay.style.opacity = '1';\n overlay.style.pointerEvents = 'auto';\n this.positionPlayOverlayOnMobile();\n });\n\n const debouncedPosition = debounce(() => {\n this.positionPlayOverlayOnMobile();\n }, 150);\n this.debouncedPositionPlayOverlay = debouncedPosition;\n\n window.addEventListener('resize', debouncedPosition, {signal: this.lifecycleSignal});\n\n this.on('loadedmetadata', () => {\n this.positionPlayOverlayOnMobile();\n });\n\n // Recalculate on fullscreen change with RAF\n this.on('enterfullscreen', () => {\n rafWithTimeout(() => this.positionPlayOverlayOnMobile(), 100);\n });\n\n this.on('exitfullscreen', () => {\n rafWithTimeout(() => this.positionPlayOverlayOnMobile(), 100);\n });\n }\n\n /**\n * Purely additive buffering spinner. Never touches play overlay or any other UI —\n * only toggles `vidply-buffering` on the container and manages its own `.vidply-loading` node.\n * Skipped for external providers (YouTube, Vimeo, SoundCloud) which have native loading UI.\n */\n createBufferingLoadingOverlay() {\n if (!this.videoWrapper) {\n return;\n }\n\n const prefix = this.options.classPrefix as string;\n const bufferingLabel = i18n.t('player.buffering');\n\n const loading = DOMUtils.createElement('div', {\n className: `${prefix}-loading`,\n attributes: {\n 'aria-busy': 'false'\n }\n });\n\n const srAnnouncer = DOMUtils.createElement('span', {\n className: `${prefix}-sr-only`,\n attributes: {\n id: `${prefix}-buffering-live-${this.instanceId}`,\n 'aria-live': 'polite',\n 'aria-atomic': 'true'\n }\n });\n loading.appendChild(srAnnouncer);\n\n this.loadingOverlayElement = loading;\n this.videoWrapper.appendChild(loading);\n\n const isExternalControls = () =>\n this.container?.classList.contains(`${prefix}-external-controls`);\n\n const showBuffering = () => {\n // Skip for external providers (YouTube, Vimeo, SoundCloud — they own\n // their own loading UI). Otherwise, allow the spinner whenever:\n // - the user has already started playback (normal mid-playback buffering), OR\n // - the user is currently seeking (e.g. scrubbed the seekbar before\n // pressing play, or after stop()) — they explicitly asked the\n // player to fetch data, so giving feedback is the right thing.\n if (isExternalControls()) {\n return;\n }\n if (!this.state.hasStartedPlayback && !this.state.seeking) {\n return;\n }\n this.container.classList.add(`${prefix}-buffering`);\n loading.setAttribute('aria-busy', 'true');\n srAnnouncer.textContent = bufferingLabel;\n };\n\n const hideBuffering = () => {\n if (!this.container.classList.contains(`${prefix}-buffering`)) {\n return;\n }\n this.container.classList.remove(`${prefix}-buffering`);\n loading.setAttribute('aria-busy', 'false');\n srAnnouncer.textContent = '';\n };\n\n this.on('waiting', showBuffering);\n this.on('canplay', hideBuffering);\n this.on('pause', hideBuffering);\n this.on('ended', hideBuffering);\n\n // Native `playing` — fires on the media element for all renderer types (HTML5, HLS, DASH).\n this._bufferingHideOnMediaPlaying = hideBuffering;\n this.element.addEventListener('playing', this._bufferingHideOnMediaPlaying);\n\n // Safety net: if timeupdate is ticking, media is clearly playing — clear any stale spinner.\n this.on('timeupdate', () => {\n if (this.container.classList.contains(`${prefix}-buffering`)) {\n hideBuffering();\n }\n });\n }\n\n positionPlayOverlayOnMobile() {\n if (!this.playButtonOverlay || this.element.tagName !== 'VIDEO') {\n return;\n }\n\n const mobile = isMobile();\n\n if (!mobile) {\n // Reset to CSS defaults on desktop\n this.playButtonOverlay.style.top = '';\n return;\n }\n\n const videoRect = this.element.getBoundingClientRect();\n const wrapperRect = this.videoWrapper?.getBoundingClientRect();\n if (!wrapperRect) return;\n const videoCenter = (videoRect.top - wrapperRect.top) + (videoRect.height / 2);\n\n this.playButtonOverlay.style.top = `${videoCenter}px`;\n }\n\n async initializeRenderer() {\n let src: string | null | undefined = this._pendingSource;\n let rendererClass: (new (player: Player) => Renderer) | null = null;\n\n if (!src) {\n const sourceElements = Array.from(this.element.querySelectorAll('source'));\n if (sourceElements.length > 1) {\n const negotiated = this._selectBestSource(sourceElements);\n src = negotiated.src;\n this._fallbackSources = negotiated.fallbacks;\n } else {\n src = this.element.src || sourceElements[0]?.src;\n this._fallbackSources = [];\n }\n } else {\n this._fallbackSources = [];\n }\n\n if (!src) {\n throw new Error('No media source found');\n }\n\n this.currentSource = src;\n this._pendingSource = null;\n\n // Pull in the audio-description manager on demand when this source carries\n // description content (it is no longer eagerly loaded for content-less\n // players). Then (re)scan the current source/track elements.\n if (this.hasAudioDescriptionContent()) {\n await this.ensureAudioDescriptionManager();\n }\n this.audioDescriptionManager?.initFromSourceElements(this.sourceElements, this.trackElements);\n\n if (!this.originalSrc) {\n this.originalSrc = src;\n }\n\n rendererClass = await this._detectRendererClass(src);\n\n this.log(`Using ${rendererClass?.name || 'HTML5Renderer'} renderer`);\n this.renderer = new rendererClass(this);\n\n const initTimeout = (this._fallbackSources?.length ?? 0) > 0 ? 10000 : 0;\n if (initTimeout > 0) {\n let timer: ReturnType<typeof setTimeout> | undefined;\n await Promise.race([\n this.renderer.init(),\n new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new Error(`Renderer init timed out after ${initTimeout}ms`)), initTimeout);\n }),\n ]).finally(() => {\n if (timer !== undefined) clearTimeout(timer);\n });\n } else {\n await this.renderer.init();\n }\n\n this.invalidateTrackCache();\n }\n\n async _detectRendererClass(src: string): Promise<new (player: Player) => Renderer> {\n type RendererCtor = new (player: Player) => Renderer;\n type RendererModule = Record<string, RendererCtor | undefined> & { default?: RendererCtor };\n if (src.includes('youtube.com') || src.includes('youtu.be')) {\n const module = await import('../renderers/YouTubeRenderer.js') as RendererModule;\n return (module.YouTubeRenderer ?? module.default) as RendererCtor;\n } else if (src.includes('vimeo.com')) {\n const module = await import('../renderers/VimeoRenderer.js') as RendererModule;\n return (module.VimeoRenderer ?? module.default) as RendererCtor;\n } else if (src.includes('.m3u8')) {\n const module = await import('../renderers/HLSRenderer.js') as RendererModule;\n return (module.HLSRenderer ?? module.default) as RendererCtor;\n } else if (src.includes('.mpd')) {\n const module = await import('../renderers/DASHRenderer.js') as RendererModule;\n return (module.DASHRenderer ?? module.default) as RendererCtor;\n } else if (src.includes('soundcloud.com') || src.includes('api.soundcloud.com')) {\n const module = await import('../renderers/SoundCloudRenderer.js') as RendererModule;\n return (module.SoundCloudRenderer ?? module.default) as RendererCtor;\n }\n return HTML5Renderer as unknown as RendererCtor;\n }\n\n _selectBestSource(sourceElements: HTMLSourceElement[]): {\n src: string;\n fallbacks: Array<{ src: string; type: string }>\n } {\n const hasMSE = typeof MediaSource !== 'undefined';\n type SourceInfo = { src: string; type: string; el: HTMLSourceElement };\n const sources: SourceInfo[] = sourceElements.map((el) => ({\n src: el.src || el.getAttribute('src') || '',\n type: el.type || el.getAttribute('type') || '',\n el,\n }));\n\n const canPlayNativeHLS = (() => {\n const v = document.createElement('video');\n return v.canPlayType('application/vnd.apple.mpegurl') !== '';\n })();\n\n let chosen: SourceInfo | undefined;\n\n if (hasMSE) {\n chosen = sources.find((s) => s.src.includes('.mpd'));\n }\n\n if (!chosen) {\n const hlsSource = sources.find((s) => s.src.includes('.m3u8'));\n if (hlsSource && (hasMSE || canPlayNativeHLS)) {\n chosen = hlsSource;\n }\n }\n\n if (!chosen) {\n chosen = sources.find((s) => !s.src.includes('.mpd') && !s.src.includes('.m3u8')) || sources[0];\n }\n\n const fallbacks = sources\n .filter((s) => s !== chosen)\n .map((s) => ({src: s.src, type: s.type}));\n\n return {src: chosen?.src ?? '', fallbacks};\n }\n\n async _fallbackToNextSource(): Promise<boolean> {\n if (!this._fallbackSources || this._fallbackSources.length === 0) {\n return false;\n }\n\n const next = this._fallbackSources.shift();\n if (!next) return false;\n this.log(`Falling back to next source: ${next.src}`);\n\n try {\n if (this.renderer && typeof this.renderer.destroy === 'function') {\n this.renderer.destroy();\n this.renderer = null;\n }\n\n this.currentSource = next.src;\n this._pendingSource = next.src;\n this._isFallingBack = true;\n await this.initializeRenderer();\n this._isFallingBack = false;\n return true;\n } catch {\n this.log(`Fallback source failed: ${next.src}`, 'warn');\n this._isFallingBack = false;\n return this._fallbackToNextSource();\n }\n }\n\n /**\n * Invalidate DOM query cache (call when tracks/sources change)\n */\n invalidateTrackCache() {\n this._textTracksDirty = true;\n this._trackElementsDirty = true;\n this._sourceElementsDirty = true;\n }\n\n /**\n * Find a text track by kind and optionally language\n * @param {string} kind - Track kind (captions, subtitles, descriptions, chapters, metadata)\n * @param {string} [language] - Optional language code\n * @returns {TextTrack|null} Found track or null\n */\n findTextTrack(kind: string, language: string | null = null) {\n const tracks = this.textTracks;\n if (language) {\n return tracks.find((t) => t.kind === kind && t.language === language);\n }\n return tracks.find((t) => t.kind === kind);\n }\n\n /**\n * Find a source element by attribute\n * @param {string} attribute - Attribute name (e.g., 'data-desc-src')\n * @param {string} [value] - Optional attribute value\n * @returns {Element|null} Found source element or null\n */\n findSourceElement(attribute: string, value: string | null = null) {\n const sources = this.sourceElements;\n if (value) {\n return sources.find((el) => el.getAttribute(attribute) === value);\n }\n return sources.find((el) => el.hasAttribute(attribute));\n }\n\n /**\n * Find a track element by its associated TextTrack\n * @param {TextTrack} track - The TextTrack object\n * @returns {Element|null} Found track element or null\n */\n findTrackElement(track: TextTrack) {\n return this.trackElements.find((el) => el.track === track);\n }\n\n // Poster delegates. Implementations live in `core/PosterManager.ts`.\n resolvePosterPath(posterPath: string | null | undefined): string {\n return this.posterManager.resolvePath(posterPath);\n }\n\n async generatePosterFromVideo(time = 10): Promise<string | null> {\n return this.posterManager.generateFromVideo(time);\n }\n\n async autoGeneratePoster(): Promise<void> {\n return this.posterManager.autoGenerate();\n }\n\n showPosterOverlay(): void { this.posterManager.showOverlay(); }\n\n hidePosterOverlay(): void { this.posterManager.hideOverlay(); }\n\n /**\n * Set a managed timeout that will be cleaned up on destroy\n * @param {Function} callback - Callback function\n * @param {number} delay - Delay in milliseconds\n * @returns {number} Timeout ID\n */\n setManagedTimeout(callback: () => void, delay: number): ReturnType<typeof setTimeout> {\n const timeoutId: ReturnType<typeof setTimeout> = setTimeout(() => {\n this.timeouts.delete(timeoutId);\n callback();\n }, delay);\n this.timeouts.add(timeoutId);\n return timeoutId;\n }\n\n /**\n * Clear a managed timeout\n * @param {number} timeoutId - Timeout ID to clear\n */\n clearManagedTimeout(timeoutId: ReturnType<typeof setTimeout> | null | undefined) {\n if (timeoutId) {\n clearTimeout(timeoutId);\n this.timeouts.delete(timeoutId);\n }\n }\n\n /**\n * Load new media source (for playlists)\n * @param {Object} config - Media configuration\n * @param {string} config.src - Media source URL\n * @param {string} config.type - Media MIME type\n * @param {string} [config.poster] - Poster image URL\n * @param {Array} [config.tracks] - Text tracks (captions, chapters, etc.)\n * @param {string} [config.audioDescriptionSrc] - Audio description video URL\n * @param {string} [config.signLanguageSrc] - Sign language video URL\n */\n /**\n * Check if a source URL requires an external renderer (YouTube, Vimeo, SoundCloud, HLS, DASH)\n * @param {string} src - Source URL\n * @returns {boolean}\n */\n isExternalRendererUrl(src: string | null | undefined) {\n if (!src) return false;\n return src.includes('youtube.com') ||\n src.includes('youtu.be') ||\n src.includes('vimeo.com') ||\n src.includes('soundcloud.com') ||\n src.includes('api.soundcloud.com') ||\n src.includes('.m3u8') ||\n src.includes('.mpd');\n }\n\n async load(config: PlayerLoadConfig) {\n try {\n this.log('Loading new media:', config.src);\n\n // Pause current playback\n if (this.renderer) {\n this.pause();\n }\n\n // Save scroll position to prevent browser from auto-scrolling when loading new media\n const scrollX = window.scrollX || window.pageXOffset;\n const scrollY = window.scrollY || window.pageYOffset;\n\n // Clear existing text tracks\n const existingTracks = this.trackElements;\n existingTracks.forEach((track) => track.remove());\n this.invalidateTrackCache();\n\n // Check if this is an external renderer URL\n const isExternalRenderer = this.isExternalRendererUrl(config.src);\n\n // Set flag early to suppress any errors that might fire during the transition\n // This prevents HTML5 element errors from triggering playlist auto-advance\n if (isExternalRenderer) {\n this._switchingRenderer = true;\n }\n\n // Only set src on HTML5 element for non-external sources\n // External renderers (YouTube, Vimeo, SoundCloud, HLS, DASH) handle their own media loading\n if (!isExternalRenderer) {\n this.element.src = config.src;\n\n if (config.type) {\n (this.element as HTMLMediaElement & { type?: string }).type = config.type;\n }\n } else {\n // For external renderers, clear the src to prevent HTML5 element errors\n // but store the URL for the renderer to use\n // DO NOT call load() here - it will trigger an error event on an element without a valid source\n this.element.removeAttribute('src');\n // Also clear any source elements to prevent errors\n const sources = this.element.querySelectorAll('source');\n sources.forEach(s => s.removeAttribute('src'));\n }\n\n // Store the source URL for external renderers to access\n this._pendingSource = config.src;\n\n this._isAudioContent = Boolean(config.type && config.type.startsWith('audio/'));\n\n // Apply or remove audio content class (16:3 aspect ratio for audio in video player)\n if (this.container) {\n if (this._isAudioContent) {\n this.container.classList.add('vidply-audio-content');\n } else {\n this.container.classList.remove('vidply-audio-content');\n }\n }\n\n // Handle poster display based on content type\n if (config.poster && this.element.tagName === 'VIDEO') {\n if (this._isAudioContent) {\n // For audio in video player: use CSS poster overlay with 16:3 aspect ratio\n this.element.removeAttribute('poster');\n if (this.videoWrapper) {\n const resolvedPoster = this.resolvePosterPath(config.poster);\n this.videoWrapper.style.setProperty('--vidply-poster-image', `url(\"${resolvedPoster}\")`);\n this.videoWrapper.classList.add('vidply-forced-poster');\n }\n } else {\n // For video: use normal poster and remove overlay\n (this.element as HTMLVideoElement).poster = this.resolvePosterPath(config.poster);\n if (this.videoWrapper) {\n this.videoWrapper.classList.remove('vidply-forced-poster');\n this.videoWrapper.style.removeProperty('--vidply-poster-image');\n }\n }\n }\n\n if (config.tracks && config.tracks.length > 0) {\n config.tracks.forEach((trackConfig) => {\n const track = document.createElement('track');\n track.src = trackConfig.src ?? '';\n track.kind = trackConfig.kind || 'captions';\n track.srclang = trackConfig.srclang || 'en';\n track.label = trackConfig.label || trackConfig.srclang || '';\n\n if (trackConfig.default) {\n track.default = true;\n }\n\n if (typeof trackConfig.describedSrc === 'string') {\n track.setAttribute('data-desc-src', trackConfig.describedSrc);\n }\n\n // Insert tracks at the beginning (before any flow content) for HTML5 validity\n const firstChild = this.element.firstChild;\n if (firstChild && firstChild.nodeType === Node.ELEMENT_NODE && (firstChild as Element).tagName !== 'TRACK') {\n this.element.insertBefore(track, firstChild);\n } else {\n this.element.appendChild(track);\n }\n });\n this.invalidateTrackCache();\n }\n\n // Remember accessibility feature states before switching tracks\n const wasSignLanguageEnabled = this.state.signLanguageEnabled;\n const wasAudioDescriptionEnabled = this.state.audioDescriptionEnabled;\n\n // Update sources from config FIRST (before hiding features)\n this.audioDescriptionSrc = config.audioDescriptionSrc || null;\n this.signLanguageSrc = config.signLanguageSrc || null;\n\n // Update original source for toggling\n this.originalSrc = config.src;\n\n // Update manager sources for playlist changes\n if (this.audioDescriptionManager) {\n this.audioDescriptionManager.updateSources(config.audioDescriptionSrc);\n // Reinitialize to pick up new track elements with data-desc-src attributes\n this.audioDescriptionManager.reinitialize();\n }\n if (this.signLanguageManager) {\n this.signLanguageManager.updateSources(config.signLanguageSrc, config.signLanguageSources);\n }\n\n // Hide accessibility features that were enabled (must happen AFTER updating sources)\n if (wasAudioDescriptionEnabled) {\n this.disableAudioDescription();\n }\n if (wasSignLanguageEnabled) {\n this.disableSignLanguage();\n }\n\n // Check if we need to change renderer type\n const shouldChangeRenderer = this.shouldChangeRenderer(config.src);\n\n // DASH and HLS renderers manage their own source loading via MSE/hls.js\n // and must be fully destroyed+reinitialized when switching sources,\n // even when the renderer type stays the same.\n const needsFullReinit = !shouldChangeRenderer && this.renderer &&\n (this.renderer.dash || this.renderer.hls);\n\n // Destroy old renderer if changing types or if MSE-based renderer needs reinit\n if ((shouldChangeRenderer || needsFullReinit) && this.renderer) {\n this.renderer.destroy();\n this.renderer = null;\n\n // Clean up dynamically added caption/transcript buttons from the\n // previous renderer. The new renderer will re-add them if its\n // stream contains text tracks.\n if (this.controlBar) {\n this.controlBar.removeHlsCaptionButtons(true);\n }\n if (this.transcriptManager?.isVisible) {\n this.transcriptManager.hideTranscript();\n }\n }\n\n // Initialize or reinitialize renderer\n if (!this.renderer || shouldChangeRenderer || needsFullReinit) {\n await this.initializeRenderer();\n } else {\n // Just reload the current renderer with the updated element\n this.renderer.media = this.element; // Update media reference\n if (this.options.deferLoad) {\n try {\n this.element.preload = this.options.preload || 'metadata';\n } catch {\n // ignore\n }\n // Reset renderer-level deferred flags if present (HTML5/HLS/DASH\n // renderers). These are renderer-private implementation details, not\n // part of the public Renderer contract, so reach them via a\n // structural cast rather than the interface.\n if (this.renderer) {\n const deferState = this.renderer as Partial<{\n _didDeferredLoad: boolean;\n _hlsSourceLoaded: boolean;\n _dashSourceLoaded: boolean;\n _pendingSrc: string | null;\n }>;\n if (typeof deferState._didDeferredLoad === 'boolean') {\n deferState._didDeferredLoad = false;\n }\n if (typeof deferState._hlsSourceLoaded === 'boolean') {\n deferState._hlsSourceLoaded = false;\n }\n if (typeof deferState._dashSourceLoaded === 'boolean') {\n deferState._dashSourceLoaded = false;\n }\n if ('_pendingSrc' in this.renderer) {\n // For HLS, store pending src for the first play() call\n deferState._pendingSrc = this._pendingSource || this.currentSource || null;\n }\n }\n } else {\n this.element.load();\n }\n }\n\n // Clear the renderer switching flag after a delay to catch async errors\n // This prevents errors from the old renderer's event queue from causing issues\n if (isExternalRenderer) {\n setTimeout(() => {\n this._switchingRenderer = false;\n }, 500);\n } else {\n this._switchingRenderer = false;\n }\n\n // Restore scroll position immediately after loading to prevent auto-scroll\n window.scrollTo(scrollX, scrollY);\n\n // For MSE-based renderers (DASH/HLS), skip CaptionManager/TranscriptManager\n // re-creation here. dash.js/hls.js create programmatic TextTrack objects\n // that persist on the <video> element after destroy() and cannot be removed\n // via standard APIs. Re-scanning element.textTracks would pick up these stale\n // tracks. Instead, the renderer's events (TEXT_TRACKS_ADDED etc.) will call\n // refreshTracks()/ensureCaptionsButton() once real tracks are available.\n if (needsFullReinit) {\n if (this.captionManager) {\n this.captionManager.disable();\n this.captionManager.tracks = [];\n }\n if (this.transcriptManager?.isVisible) {\n this.transcriptManager.hideTranscript();\n }\n } else {\n if (this.captionManager) {\n this.captionManager.destroy();\n this.captionManager = new CaptionManager(this);\n }\n\n if (this.transcriptManager) {\n const wasTranscriptVisible = this.transcriptManager.isVisible;\n this.transcriptManager.destroy();\n this.transcriptManager = null;\n\n const newManager = await this.ensureTranscriptManager();\n\n if (wasTranscriptVisible && this.controlBar && this.controlBar.hasCaptionTracks()) {\n newManager?.showTranscript();\n }\n }\n\n if (this.controlBar) {\n this.updateControlBar();\n }\n }\n\n // Restore scroll position after control bar update (may have caused micro-scrolls)\n window.scrollTo(scrollX, scrollY);\n\n // Restore accessibility features if they were enabled and available in new track\n if (wasSignLanguageEnabled && this.signLanguageSrc) {\n // Small delay to ensure player and control bar are ready\n setTimeout(() => {\n this.enableSignLanguage();\n // Restore scroll after sign language is shown\n window.scrollTo(scrollX, scrollY);\n }, 150);\n }\n\n if (wasAudioDescriptionEnabled && this.audioDescriptionSrc) {\n // Small delay to ensure player is ready\n setTimeout(() => {\n this.enableAudioDescription();\n // Restore scroll after audio description is enabled\n window.scrollTo(scrollX, scrollY);\n }, 150);\n }\n\n this.emit('sourcechange', config);\n this.log('Media loaded successfully');\n\n } catch (error: unknown) {\n this.handleError(error);\n }\n }\n\n /**\n * Ensure the current renderer has started its initial load (metadata/manifest)\n * without starting playback. This is useful for playlists to behave like\n * single videos on selection, while still keeping autoplay off.\n */\n ensureLoaded() {\n try {\n if (!this.renderer) return;\n if (typeof this.renderer.ensureLoaded === 'function') {\n this.renderer.ensureLoaded();\n }\n } catch {\n // ignore\n }\n }\n\n /**\n * Check if we need to change renderer type\n * @param {string} src - New source URL\n * @returns {boolean}\n */\n /**\n * Update the control bar to refresh button visibility based on available features\n */\n updateControlBar() {\n if (!this.controlBar) return;\n\n const controlBar = this.controlBar;\n\n // Clear existing controls content\n controlBar.element.innerHTML = '';\n\n // Recreate controls with updated feature detection\n controlBar.createControls();\n\n // Reattach events for the new controls\n controlBar.attachEvents();\n controlBar.setupAutoHide();\n controlBar.setupOverflowDetection();\n }\n\n shouldChangeRenderer(src: string) {\n if (!this.renderer) return true;\n\n const isYouTube = src.includes('youtube.com') || src.includes('youtu.be');\n const isVimeo = src.includes('vimeo.com');\n const isHLS = src.includes('.m3u8');\n const isDASH = src.includes('.mpd');\n const isSoundCloud = src.includes('soundcloud.com') || src.includes('api.soundcloud.com');\n\n const currentRendererName = this.renderer.constructor.name;\n\n if (isYouTube && currentRendererName !== 'YouTubeRenderer') return true;\n if (isVimeo && currentRendererName !== 'VimeoRenderer') return true;\n if (isHLS && currentRendererName !== 'HLSRenderer') return true;\n if (isDASH && currentRendererName !== 'DASHRenderer') return true;\n if (isSoundCloud && currentRendererName !== 'SoundCloudRenderer') return true;\n if (!isYouTube && !isVimeo && !isHLS && !isDASH && !isSoundCloud && currentRendererName !== 'HTML5Renderer') return true;\n\n return false;\n }\n\n // Playback controls\n play() {\n if (this.renderer) {\n this.renderer.play();\n return;\n }\n\n // Playlist support: if no renderer exists yet (no initial src),\n // start playback via playlist selection.\n if (this.playlistManager && Array.isArray(this.playlistManager.tracks) && this.playlistManager.tracks.length > 0) {\n const index = this.playlistManager.currentIndex >= 0 ? this.playlistManager.currentIndex : 0;\n this.playlistManager.play(index, true);\n }\n }\n\n pause() {\n if (this.renderer) {\n this.renderer.pause();\n }\n }\n\n stop() {\n this.pause();\n this.seek(0);\n }\n\n toggle() {\n if (this.state.playing) {\n this.pause();\n } else {\n this.play();\n }\n }\n\n /**\n * Seek to a non-negative finite second offset. Non-finite or non-numeric\n * inputs are silently dropped instead of being forwarded to the renderer\n * where they would set `currentTime = NaN` on an HTMLMediaElement.\n */\n seek(time: number): void {\n if (typeof time !== 'number' || !Number.isFinite(time)) return;\n const safeTime = time < 0 ? 0 : time;\n // Any user-initiated seek (seekbar drag/click, keyboard arrow,\n // skip buttons) flows through this method, so it's the right place\n // to drop the poster overlay: scrubbing communicates \"I want to see\n // a different frame\", and keeping the artwork on top would hide\n // exactly the frame the user is asking for. dash.js internal\n // startup gap-jump bypasses this method and sets\n // media.currentTime directly, so the poster stays put for that\n // case (which is what we want — the user hasn't engaged yet).\n this.hidePosterOverlay();\n if (this.renderer) {\n this.renderer.seek(safeTime);\n }\n }\n\n seekForward(interval: number = (this.options.seekInterval as number)): void {\n const step = Number.isFinite(interval) ? interval : 5;\n const targetTime = this.state.currentTime + step;\n // Only cap to duration if duration is known (> 0), otherwise let the media element handle it\n const seekTime = this.state.duration > 0 ? Math.min(targetTime, this.state.duration) : targetTime;\n this.seek(seekTime);\n }\n\n seekBackward(interval: number = (this.options.seekInterval as number)): void {\n const step = Number.isFinite(interval) ? interval : 5;\n this.seek(Math.max(this.state.currentTime - step, 0));\n }\n\n // Volume controls\n /**\n * Set the volume to a finite number in [0, 1]. Non-numeric or NaN\n * input is silently ignored.\n */\n setVolume(volume: number): void {\n if (typeof volume !== 'number' || !Number.isFinite(volume)) return;\n const newVolume = Math.max(0, Math.min(1, volume));\n if (this.renderer) {\n this.renderer.setVolume(newVolume);\n }\n this.state.volume = newVolume;\n\n // If volume is increased above 0 and currently muted, unmute\n if (newVolume > 0 && this.state.muted) {\n this.state.muted = false;\n if (this.renderer) {\n this.renderer.setMuted(false);\n }\n this.emit('volumechange');\n }\n\n this.savePlayerPreferences();\n }\n\n getVolume() {\n return this.state.volume;\n }\n\n mute() {\n if (this.renderer) {\n this.renderer.setMuted(true);\n }\n this.state.muted = true;\n this.savePlayerPreferences();\n this.emit('volumechange');\n }\n\n unmute() {\n if (this.renderer) {\n this.renderer.setMuted(false);\n }\n this.state.muted = false;\n this.savePlayerPreferences();\n this.emit('volumechange');\n }\n\n toggleMute() {\n if (this.state.muted) {\n this.unmute();\n } else {\n this.mute();\n }\n }\n\n // Playback speed\n /**\n * Set playback speed in [0.25, 2]. Silently rejects non-finite input.\n */\n setPlaybackSpeed(speed: number): void {\n if (typeof speed !== 'number' || !Number.isFinite(speed)) return;\n const newSpeed = Math.max(0.25, Math.min(2, speed));\n if (this.renderer) {\n this.renderer.setPlaybackSpeed(newSpeed);\n }\n this.state.playbackSpeed = newSpeed;\n this.savePlayerPreferences();\n this.emit('playbackspeedchange', newSpeed);\n }\n\n getPlaybackSpeed() {\n return this.state.playbackSpeed;\n }\n\n // Save player preferences to localStorage\n savePlayerPreferences() {\n this.storage.savePlayerPreferences({\n volume: this.state.volume,\n muted: this.state.muted,\n playbackSpeed: this.state.playbackSpeed\n });\n }\n\n // Fullscreen\n enterFullscreen() {\n const elem = this.container as HTMLElement & {\n webkitRequestFullscreen?: () => Promise<void> | void;\n mozRequestFullScreen?: () => Promise<void> | void;\n msRequestFullscreen?: () => Promise<void> | void;\n };\n let fullscreenPromise: Promise<void> | void | null = null;\n\n const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) ||\n (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);\n\n if (isIOS) {\n this._enablePseudoFullscreen();\n return;\n }\n\n if (elem.requestFullscreen) {\n fullscreenPromise = elem.requestFullscreen();\n } else if (elem.webkitRequestFullscreen) {\n fullscreenPromise = elem.webkitRequestFullscreen();\n } else if (elem.mozRequestFullScreen) {\n fullscreenPromise = elem.mozRequestFullScreen();\n } else if (elem.msRequestFullscreen) {\n fullscreenPromise = elem.msRequestFullscreen();\n }\n\n if (fullscreenPromise && typeof (fullscreenPromise as Promise<void>).catch === 'function') {\n (fullscreenPromise as Promise<void>).catch((err: Error) => {\n this.log('Fullscreen API failed, using pseudo-fullscreen:', err.message);\n this._enablePseudoFullscreen();\n });\n }\n\n if (!elem.requestFullscreen && !elem.webkitRequestFullscreen &&\n !elem.mozRequestFullScreen && !elem.msRequestFullscreen) {\n this._enablePseudoFullscreen();\n } else {\n // Optimistically set state (will be corrected by fullscreenChangeHandler if it fails)\n this.state.fullscreen = true;\n this.container.classList.add(`${this.options.classPrefix}-fullscreen`);\n this.emit('fullscreenchange', true);\n }\n }\n\n exitFullscreen() {\n const doc = document as Document & {\n webkitFullscreenElement?: Element | null;\n mozFullScreenElement?: Element | null;\n msFullscreenElement?: Element | null;\n webkitExitFullscreen?: () => Promise<void> | void;\n mozCancelFullScreen?: () => Promise<void> | void;\n msExitFullscreen?: () => Promise<void> | void;\n };\n const isInNativeFullscreen = Boolean(\n document.fullscreenElement ||\n doc.webkitFullscreenElement ||\n doc.mozFullScreenElement ||\n doc.msFullscreenElement\n );\n\n if (isInNativeFullscreen) {\n if (document.exitFullscreen) {\n document.exitFullscreen();\n } else if (doc.webkitExitFullscreen) {\n doc.webkitExitFullscreen();\n } else if (doc.mozCancelFullScreen) {\n doc.mozCancelFullScreen();\n } else if (doc.msExitFullscreen) {\n doc.msExitFullscreen();\n }\n } else {\n // We're in pseudo-fullscreen, exit it manually\n this._disablePseudoFullscreen();\n }\n\n this.state.fullscreen = false;\n this.container.classList.remove(`${this.options.classPrefix}-fullscreen`);\n this.emit('fullscreenchange', false);\n }\n\n toggleFullscreen() {\n if (this.state.fullscreen) {\n this.exitFullscreen();\n } else {\n this.enterFullscreen();\n }\n }\n\n // Pseudo-fullscreen fallback for iOS and browsers without Fullscreen API.\n // All of the real DOM + scroll + inert bookkeeping lives in\n // `PseudoFullscreenController`; Player keeps these thin delegates so\n // call sites elsewhere in the class stay readable.\n _enablePseudoFullscreen() {\n if (!this.pseudoFullscreen) {\n this.pseudoFullscreen = new PseudoFullscreenController(this);\n }\n this.pseudoFullscreen.enable();\n }\n\n _disablePseudoFullscreen() {\n this.pseudoFullscreen?.disable();\n }\n\n // Picture-in-Picture\n enterPiP() {\n // When the custom floating player is enabled, the control bar PiP\n // button is rebound to floatingPlayerManager.togglePinned(). This\n // direct API is redirected too so external callers don't accidentally\n // trigger native PiP after we disabled it.\n if (this.options.floating) {\n if (this.floatingPlayerManager) {\n this.floatingPlayerManager.togglePinned();\n }\n return;\n }\n const pipElement = this.element as HTMLVideoElement & {\n requestPictureInPicture?: () => Promise<PictureInPictureWindow>\n };\n if (typeof pipElement.requestPictureInPicture === 'function') {\n pipElement.requestPictureInPicture();\n this.state.pip = true;\n this.emit('pipchange', true);\n }\n }\n\n exitPiP() {\n if (this.options.floating) {\n if (this.floatingPlayerManager && this.state.floating) {\n this.floatingPlayerManager.exit('manual');\n }\n return;\n }\n if (document.pictureInPictureElement) {\n document.exitPictureInPicture();\n this.state.pip = false;\n this.emit('pipchange', false);\n }\n }\n\n togglePiP() {\n if (this.options.floating) {\n if (this.floatingPlayerManager) {\n this.floatingPlayerManager.togglePinned();\n }\n return;\n }\n if (this.state.pip) {\n this.exitPiP();\n } else {\n this.enterPiP();\n }\n }\n\n // Captions\n enableCaptions() {\n if (this.captionManager) {\n this.captionManager.enable();\n this.state.captionsEnabled = true;\n }\n }\n\n disableCaptions() {\n if (this.captionManager) {\n this.captionManager.disable();\n this.state.captionsEnabled = false;\n }\n }\n\n toggleCaptions() {\n if (this.state.captionsEnabled) {\n this.disableCaptions();\n } else {\n this.enableCaptions();\n }\n }\n\n /**\n * Check if a track file exists. Bounded by a 8s `AbortSignal.timeout`\n * and the player's lifecycle controller, so a slow / hung server cannot\n * keep a request alive past `destroy()`.\n */\n async validateTrackExists(url: string): Promise<boolean> {\n if (typeof url !== 'string' || !url) return false;\n const signals: AbortSignal[] = [this.lifecycleSignal];\n if (typeof AbortSignal !== 'undefined' && typeof AbortSignal.timeout === 'function') {\n signals.push(AbortSignal.timeout(8000));\n }\n const signal = signals.length === 1 ? signals[0] : (AbortSignal as {\n any?: (signals: AbortSignal[]) => AbortSignal\n }).any?.(signals) ?? signals[0];\n try {\n const response = await fetch(url, {method: 'HEAD', cache: 'no-cache', signal});\n return response.ok;\n } catch (error: unknown) {\n if (this.options.debug) {\n this.log(`validateTrackExists(\"${url}\") failed: ${(error as Error)?.message ?? error}`, 'warn');\n }\n return false;\n }\n }\n\n /**\n * Strip VTT formatting tags from caption text\n * @param {string} text - Caption text with VTT formatting\n * @returns {string} Plain text without formatting\n */\n stripVTTFormatting(text: string | null | undefined): string {\n if (!text) return '';\n return text\n .replace(/<[^>]+>/g, '')\n .replace(/\\n/g, ' ')\n .trim()\n .toLowerCase();\n }\n\n /**\n * Find matching caption time based on text content\n * Useful for syncing between videos of different lengths (e.g., with/without audio description)\n * @param {string} targetText - Caption text to search for\n * @param {Array} tracks - Array of caption tracks to search in\n * @returns {number|null} Start time of matching caption, or null if not found\n */\n findMatchingCaptionTime(targetText: string | null | undefined, tracks: Array<{\n kind?: string;\n track?: TextTrack | null\n }> | null | undefined) {\n if (!targetText || !tracks || tracks.length === 0) {\n return null;\n }\n\n const normalizedTarget = this.stripVTTFormatting(targetText);\n\n for (const trackInfo of tracks) {\n if (trackInfo.kind !== 'captions' && trackInfo.kind !== 'subtitles') {\n continue;\n }\n\n const track = trackInfo.track;\n if (!track || !track.cues) {\n continue;\n }\n\n for (let i = 0; i < track.cues.length; i++) {\n const cue = track.cues[i] as VTTCue;\n const cueText = this.stripVTTFormatting(cue.text);\n\n if (cueText === normalizedTarget) {\n return cue.startTime;\n }\n\n const targetWords = normalizedTarget.split(/\\s+/).filter((w) => w.length > 2);\n const cueWords = cueText.split(/\\s+/).filter((w) => w.length > 2);\n\n if (targetWords.length > 0 && cueWords.length > 0) {\n const matchingWords = targetWords.filter((word) => cueWords.includes(word));\n const matchRatio = matchingWords.length / targetWords.length;\n\n if (matchRatio >= 0.8) {\n return cue.startTime;\n }\n }\n }\n }\n\n return null;\n }\n\n // Audio Description (delegated to AudioDescriptionManager)\n async enableAudioDescription() {\n const manager = await this.ensureAudioDescriptionManager();\n return manager?.enable();\n }\n\n async disableAudioDescription() {\n const manager = await this.ensureAudioDescriptionManager();\n return manager?.disable();\n }\n\n async toggleAudioDescription() {\n if (this.options.requirePlaybackForAccessibilityToggles && !this.renderer && this.playlistManager?.tracks?.length) {\n this.showNotice(i18n.t('player.startPlaybackForAudioDescription'));\n return;\n }\n\n const manager = await this.ensureAudioDescriptionManager();\n if (!manager) return;\n\n // If user toggles audio description before the first track has been loaded,\n // remember desired state and start playback so the described source is loaded.\n if (!this.renderer && this.playlistManager && this.playlistManager.tracks?.length) {\n manager.desiredState = !manager.desiredState;\n this.state.audioDescriptionEnabled = manager.desiredState;\n this.emit(manager.desiredState ? 'audiodescriptionenabled' : 'audiodescriptiondisabled');\n // Start playback (PlaylistManager.play() will honor desiredState and load described src)\n this.play();\n return;\n }\n\n return manager.toggle();\n }\n\n // Sign Language (delegated to SignLanguageManager)\n async enableSignLanguage() {\n const manager = await this.ensureSignLanguageManager();\n return manager?.enable();\n }\n\n async disableSignLanguage() {\n const manager = await this.ensureSignLanguageManager();\n return manager?.disable();\n }\n\n async toggleSignLanguage() {\n if (this.options.requirePlaybackForAccessibilityToggles && !this.renderer && this.playlistManager?.tracks?.length) {\n this.showNotice(i18n.t('player.startPlaybackForSignLanguage'));\n return;\n }\n\n const manager = await this.ensureSignLanguageManager();\n if (!manager) return;\n\n // If user toggles sign language before the first track has been loaded,\n // enable the overlay and start playback so sign language video sync begins.\n if (!this.renderer && this.playlistManager && this.playlistManager.tracks?.length) {\n const wasEnabled = manager.enabled;\n const result = manager.toggle();\n if (!wasEnabled && manager.enabled) {\n this.play();\n }\n return result;\n }\n\n return manager.toggle();\n }\n\n setupSignLanguageInteraction() {\n return this.signLanguageManager?._setupInteraction();\n }\n\n switchSignLanguage(langCode: string) {\n return this.signLanguageManager?.switchLanguage(langCode);\n }\n\n showSignLanguageSettingsMenu() {\n return this.signLanguageManager?.showSettingsMenu();\n }\n\n hideSignLanguageSettingsMenu({focusButton = true} = {}) {\n return this.signLanguageManager?.hideSettingsMenu({focusButton});\n }\n\n constrainSignLanguagePosition() {\n return this.signLanguageManager?.constrainPosition();\n }\n\n saveSignLanguagePreferences() {\n return this.signLanguageManager?.savePreferences();\n }\n\n cleanupSignLanguage() {\n return this.signLanguageManager?.cleanup();\n }\n\n // Settings dialog removed - using individual control buttons instead\n showSettings() {\n console.warn('[VidPly] Settings dialog has been removed. Use individual control buttons (speed, captions, etc.)');\n }\n\n hideSettings() {\n // No-op - settings dialog removed\n }\n\n // Utility methods\n getCurrentTime() {\n return this.state.currentTime;\n }\n\n getDuration() {\n return this.state.duration;\n }\n\n isPlaying() {\n return this.state.playing;\n }\n\n isPaused() {\n return this.state.paused;\n }\n\n isEnded() {\n return this.state.ended;\n }\n\n isMuted() {\n return this.state.muted;\n }\n\n isFullscreen() {\n return this.state.fullscreen;\n }\n\n handleError(error: unknown) {\n if (this._switchingRenderer || this._isFallingBack) {\n this.log('Suppressing error during renderer switch:', error, 'debug');\n return;\n }\n\n if (this._fallbackSources && this._fallbackSources.length > 0) {\n this.log('Renderer error, attempting fallback:', error, 'warn');\n this._fallbackToNextSource().then((success: boolean) => {\n if (!success) {\n this.log('All fallback sources exhausted', 'error');\n this.emit('error', error);\n if (this.options.onError) {\n this.options.onError.call(this, error);\n }\n }\n });\n return;\n }\n\n this.log('Error:', error, 'error');\n this.emit('error', error);\n\n if (this.options.onError) {\n this.options.onError.call(this, error);\n }\n }\n\n // Logging\n log(...messages: unknown[]) {\n if (!this.options.debug) {\n return;\n }\n\n let type: 'log' | 'warn' | 'error' | 'debug' | 'info' = 'log';\n const consoleObj = console as unknown as Record<string, (...a: unknown[]) => void>;\n if (messages.length > 0) {\n const potentialType = messages[messages.length - 1];\n if (typeof potentialType === 'string' && typeof consoleObj[potentialType] === 'function') {\n type = potentialType as typeof type;\n messages = messages.slice(0, -1);\n }\n }\n\n if (messages.length === 0) {\n messages = [''];\n }\n\n const consoleFn = consoleObj[type];\n if (typeof consoleFn === 'function') {\n consoleFn('[VidPly]', ...messages);\n } else {\n console.log('[VidPly]', ...messages);\n }\n }\n\n /**\n * Wire up resize / orientation / fullscreen listeners. Delegates to\n * `ResponsiveManager`; Player keeps the method name for backward\n * compatibility with external callers that start the feature\n * manually after swapping the container.\n */\n setupResponsiveHandlers(): void {\n this.responsiveManager.setup();\n }\n\n // Cleanup. Aborts the lifecycle controller (which removes every\n // window/document listener wired with `{ signal }` plus every\n // user-influenced fetch we threaded the signal into), cascade-destroys\n // every manager we own, and finally removes this instance from the\n // global `Player.instances` registry.\n destroy(): void {\n this.log('Destroying player');\n\n // Abort all listeners + in-flight fetches first so callbacks\n // running concurrently with destroy() see a torn-down player.\n try {\n this._lifecycleController.abort();\n } catch (err) {\n this.log(`AbortController.abort failed: ${err}`, 'warn');\n }\n\n if (this.renderer) {\n this.renderer.destroy();\n }\n\n if (this.controlBar) {\n this.controlBar.destroy();\n }\n\n if (this.captionManager) {\n this.captionManager.destroy();\n }\n\n if (this.keyboardManager) {\n this.keyboardManager.destroy();\n }\n\n if (this.transcriptManager) {\n this.transcriptManager.destroy();\n }\n\n // Clean up sign-language video and listeners\n this.cleanupSignLanguage();\n\n // Cascade-destroy lazy-loaded managers that own their own listeners.\n if (this.audioDescriptionManager && typeof this.audioDescriptionManager.destroy === 'function') {\n try {\n this.audioDescriptionManager.destroy();\n } catch (err) {\n this.log(`AudioDescriptionManager.destroy failed: ${err}`, 'warn');\n }\n this.audioDescriptionManager = null;\n }\n\n if (this.signLanguageManager && typeof this.signLanguageManager.destroy === 'function') {\n try {\n this.signLanguageManager.destroy();\n } catch (err) {\n this.log(`SignLanguageManager.destroy failed: ${err}`, 'warn');\n }\n this.signLanguageManager = null;\n }\n\n if (this.playlistManager && typeof this.playlistManager.destroy === 'function') {\n try {\n this.playlistManager.destroy();\n } catch (err) {\n this.log(`PlaylistManager.destroy failed: ${err}`, 'warn');\n }\n this.playlistManager = null;\n }\n\n if (this.settingsDialog && typeof this.settingsDialog.destroy === 'function') {\n try {\n this.settingsDialog.destroy();\n } catch (err) {\n this.log(`SettingsDialog.destroy failed: ${err}`, 'warn');\n }\n this.settingsDialog = null;\n }\n\n // Clean up floating player manager (disconnects IntersectionObserver,\n // returns the container to its original parent if still floating)\n if (this.floatingPlayerManager) {\n try {\n this.floatingPlayerManager.destroy();\n } catch (err) {\n this.log(`FloatingPlayerManager.destroy failed: ${err}`, 'warn');\n }\n this.floatingPlayerManager = null;\n }\n\n // Cleanup play overlay button\n if (this.playButtonOverlay && this.playButtonOverlay.parentNode) {\n this.playButtonOverlay.remove();\n this.playButtonOverlay = null;\n }\n\n // The buffering listener is attached to `this.element` directly and\n // therefore not covered by the lifecycle controller (which only\n // covers window/document/element listeners that opted in via\n // `{ signal }`); it is removed explicitly here.\n if (this._bufferingHideOnMediaPlaying) {\n this.element.removeEventListener('playing', this._bufferingHideOnMediaPlaying);\n this._bufferingHideOnMediaPlaying = null;\n }\n\n if (this.loadingOverlayElement && this.loadingOverlayElement.parentNode) {\n this.loadingOverlayElement.remove();\n this.loadingOverlayElement = null;\n }\n\n // Responsive tracking owns the resize-observer + orientation\n // matchMedia + fullscreenchange listeners. The window/document\n // handlers attached with `{signal}` have already been torn down\n // by `this._lifecycleController.abort()` above; `cleanup()`\n // handles the two exceptions (ResizeObserver + legacy Safari\n // matchMedia listener).\n this.responsiveManager?.cleanup();\n\n // Clean up all managed timeouts\n this.timeouts.forEach((timeoutId: ReturnType<typeof setTimeout>) => clearTimeout(timeoutId));\n this.timeouts.clear();\n\n // Cleanup metadata handling. The manager owns the alert button\n // click listeners; the `cuechange` listener is also tracked on\n // `player.metadataCueChangeHandler` for the TextTrack teardown\n // path below.\n if (this.metadataCueChangeHandler) {\n const textTracks = this.textTracks;\n const metadataTrack = textTracks.find((track: TextTrack) => track.kind === 'metadata');\n if (metadataTrack) {\n metadataTrack.removeEventListener('cuechange', this.metadataCueChangeHandler);\n }\n this.metadataCueChangeHandler = null;\n }\n this.metadataAlertsManager?.cleanup();\n\n // Drop ourselves from the global registry so multi-player pages can\n // detect leaks via Player.instances.length === expected.\n const idx = Player.instances.indexOf(this);\n if (idx >= 0) {\n Player.instances.splice(idx, 1);\n }\n\n // Remove container\n if (this.container && this.container.parentNode) {\n this.container.parentNode.insertBefore(this.element, this.container);\n this.container.parentNode.removeChild(this.container);\n }\n\n this.removeAllListeners();\n }\n\n /**\n * Set up metadata track handling. Delegates to\n * `MetadataAlertsManager` — Player lazily constructs it so pages\n * without metadata tracks pay no cost.\n */\n setupMetadataHandling(): void {\n if (!this.metadataAlertsManager) {\n this.metadataAlertsManager = new MetadataAlertsManager(this);\n }\n this.metadataAlertsManager.setupHandling();\n }\n\n // Thin delegates for the metadata-alert system. Implementations\n // live in `core/MetadataAlertsManager.ts`; Player keeps the names\n // so call sites inside `handleMetadataCue` and external callers\n // (e.g. TranscriptManager integration tests) keep working.\n normalizeMetadataSelector(selector: unknown): string | null {\n return (this.metadataAlertsManager ?? this._ensureMetadataManager()).normalizeSelector(selector);\n }\n\n resolveMetadataConfig(\n map: Record<string, unknown> | null | undefined,\n key: string | null | undefined\n ): MetadataAlertConfig | null {\n return (this.metadataAlertsManager ?? this._ensureMetadataManager()).resolveConfig(map, key);\n }\n\n cacheMetadataAlertContent(element: HTMLElement | null | undefined, config: MetadataAlertConfig = {}): void {\n (this.metadataAlertsManager ?? this._ensureMetadataManager()).cacheContent(element, config);\n }\n\n restoreMetadataAlertContent(element: HTMLElement | null | undefined, config: MetadataAlertConfig = {}): void {\n (this.metadataAlertsManager ?? this._ensureMetadataManager()).restoreContent(element, config);\n }\n\n focusMetadataTarget(target: string | null | undefined, fallbackElement: HTMLElement | null = null): void {\n (this.metadataAlertsManager ?? this._ensureMetadataManager()).focusTarget(target, fallbackElement);\n }\n\n /** Internal helper: lazily creates the manager for external\n * entry points that didn't come via `setupMetadataHandling`. */\n private _ensureMetadataManager(): MetadataAlertsManager {\n if (!this.metadataAlertsManager) {\n this.metadataAlertsManager = new MetadataAlertsManager(this);\n }\n return this.metadataAlertsManager;\n }\n\n handleMetadataAlert(selector: string, options: MetadataAlertOptions = {}): HTMLElement | undefined {\n return (this.metadataAlertsManager ?? this._ensureMetadataManager()).handleAlert(selector, options);\n }\n\n handleMetadataHashtags(hashtags: string[] | null | undefined): void {\n (this.metadataAlertsManager ?? this._ensureMetadataManager()).handleHashtags(hashtags);\n }\n\n handleMetadataCue(cue: VTTCue | TextTrackCue): void {\n (this.metadataAlertsManager ?? this._ensureMetadataManager()).handleCue(cue);\n }\n\n}\n\n", "/**\n * VidPly Playlist Manager\n * Manages playlists for audio and video content\n */\n\nimport { DOMUtils } from '../utils/DOMUtils.js';\nimport { createIconElement } from '../icons/Icons.js';\nimport { i18n } from '../i18n/i18n.js';\nimport { TimeUtils } from '../utils/TimeUtils.js';\nimport { sanitizePosterUrl, toCssBackgroundImage } from '../utils/UrlSafe.js';\nimport { reducedMotionScrollOptions } from '../utils/PerformanceUtils.js';\nimport type { Player } from '../core/Player.js';\n\ntype PlaylistTextTrack = {\n src?: string;\n kind?: string;\n srclang?: string;\n label?: string;\n default?: boolean;\n describedSrc?: string;\n [key: string]: unknown;\n};\n\ntype PlaylistTrack = {\n src?: string;\n type?: string;\n poster?: string;\n tracks?: PlaylistTextTrack[];\n audioDescriptionSrc?: string | null;\n audioDescriptionDuration?: number | string | null;\n signLanguageSrc?: string | null;\n signLanguageSources?: Record<string, string>;\n duration?: number | string | null;\n title?: string;\n artist?: string;\n description?: string;\n [key: string]: unknown;\n};\n\n/**\n * Construction signature for the host Player class. The playlist\n * manager receives this via options so it can recreate players when\n * tracks of incompatible media types (e.g. audio after video) load.\n */\ntype PlayerConstructor = new (\n element: string | HTMLElement,\n options?: Record<string, unknown>\n) => Player;\n\n/**\n * Internal options bag for the manager. Extra keys from the caller\n * (we accept `Record<string, unknown>`) are merged in via the\n * `[key: string]: unknown` index so consumers can pass through\n * additional player options without losing typing on the known ones.\n */\ninterface PlaylistManagerOptions {\n autoAdvance: boolean;\n autoPlayFirst: boolean;\n loop: boolean;\n showPanel: boolean;\n recreatePlayers: boolean;\n hostElement?: HTMLElement | null;\n PlayerClass?: PlayerConstructor | null;\n tracks?: PlaylistTrack[];\n [key: string]: unknown;\n}\n\n// Static counter for unique IDs\nlet playlistInstanceCounter = 0;\n\nexport class PlaylistManager {\n player: Player;\n container: HTMLElement | null;\n currentIndex: number;\n hostElement: HTMLElement | null;\n initialTracks: PlaylistTrack[];\n instanceId: number;\n isChangingTrack: boolean;\n isPanelVisible: boolean;\n navigationFeedback: HTMLElement | null;\n options: PlaylistManagerOptions;\n PlayerClass: PlayerConstructor | null;\n playlistPanel: HTMLElement | null;\n trackArtworkElement: HTMLElement | null;\n trackInfoElement: HTMLElement | null;\n tracks: PlaylistTrack[];\n uniqueId: string;\n\n constructor(player: Player, options: Record<string, unknown> = {}) {\n this.player = player;\n this.tracks = [];\n this.initialTracks = Array.isArray(options.tracks) ? (options.tracks as PlaylistTrack[]) : [];\n this.currentIndex = -1;\n\n // Generate unique instance ID for this playlist\n this.instanceId = ++playlistInstanceCounter;\n this.uniqueId = `vidply-playlist-${this.instanceId}`;\n\n // Options. Spread the caller's bag last so we still support arbitrary\n // pass-through keys, but defaults stay typed.\n this.options = {\n ...options,\n autoAdvance: options.autoAdvance !== false, // Default true\n autoPlayFirst: options.autoPlayFirst !== false, // Default true - auto-play first track on load\n loop: Boolean(options.loop) || false,\n showPanel: options.showPanel !== false, // Default true\n recreatePlayers: Boolean(options.recreatePlayers) || false,\n };\n\n // UI elements\n this.container = null;\n this.playlistPanel = null;\n this.trackInfoElement = null;\n this.trackArtworkElement = null;\n this.navigationFeedback = null; // Live region for keyboard navigation feedback\n this.isPanelVisible = this.options.showPanel !== false;\n\n // Track change guard to prevent cascade of next() calls\n this.isChangingTrack = false;\n\n // Store the host element for player recreation\n this.hostElement = (options.hostElement as HTMLElement | null | undefined) ?? null;\n this.PlayerClass = (options.PlayerClass as PlayerConstructor | null | undefined) ?? null;\n \n // Bind methods once so the same reference is used for both on() and off().\n // Binding inline at registration time (e.g. `.bind(this)` in init()) would\n // create a fresh function each call that off() can never match, leaking the\n // listener after destroy().\n this.handleTrackEnd = this.handleTrackEnd.bind(this);\n this.handleTrackError = this.handleTrackError.bind(this);\n this.handlePlaybackStateChange = this.handlePlaybackStateChange.bind(this);\n this.handleFullscreenChange = this.handleFullscreenChange.bind(this);\n this.handleAudioDescriptionChange = this.handleAudioDescriptionChange.bind(this);\n \n // Register this playlist manager with the player\n this.player.playlistManager = this;\n \n // Initialize\n this.init();\n \n // Update controls to add playlist buttons\n this.updatePlayerControls();\n \n // Load tracks if provided in options (after UI is ready)\n if (this.initialTracks.length > 0) {\n this.loadPlaylist(this.initialTracks);\n }\n }\n \n /**\n * Determine the media type for a track\n * @param {Object} track - Track object\n * @returns {string} - 'audio', 'video', 'youtube', 'vimeo', 'soundcloud', 'hls', 'dash'\n */\n getTrackMediaType(track: PlaylistTrack) {\n const src = track.src || '';\n \n if (src.includes('youtube.com') || src.includes('youtu.be')) {\n return 'youtube';\n }\n if (src.includes('vimeo.com')) {\n return 'vimeo';\n }\n if (src.includes('soundcloud.com') || src.includes('api.soundcloud.com')) {\n return 'soundcloud';\n }\n if (src.includes('.m3u8')) {\n return 'hls';\n }\n if (src.includes('.mpd')) {\n return 'dash';\n }\n if (track.type && track.type.startsWith('audio/')) {\n return 'audio';\n }\n // Default to video for video types or unknown\n return 'video';\n }\n \n /**\n * Recreate the player with the appropriate element type for the track\n * @param {Object} track - Track to load\n * @param {boolean} autoPlay - Whether to auto-play after creation\n */\n async recreatePlayerForTrack(track: PlaylistTrack, autoPlay = false) {\n if (!this.hostElement || !this.PlayerClass) {\n console.warn('VidPly Playlist: Cannot recreate player - missing hostElement or PlayerClass');\n return false;\n }\n \n const mediaType = this.getTrackMediaType(track);\n // SoundCloud uses an iframe widget, so it doesn't need an audio element\n // Only local audio files need an actual <audio> element\n const elementType = (mediaType === 'audio') ? 'audio' : 'video';\n \n // Store playlist panel state\n const wasVisible = this.isPanelVisible;\n const savedTracks = [...this.tracks]; // Keep track data\n const savedIndex = this.currentIndex;\n \n // Detach all playlist UI elements from DOM (keep references)\n // These will be reattached to the new player container\n if (this.trackArtworkElement && this.trackArtworkElement.parentNode) {\n this.trackArtworkElement.parentNode.removeChild(this.trackArtworkElement);\n }\n if (this.trackInfoElement && this.trackInfoElement.parentNode) {\n this.trackInfoElement.parentNode.removeChild(this.trackInfoElement);\n }\n if (this.navigationFeedback && this.navigationFeedback.parentNode) {\n this.navigationFeedback.parentNode.removeChild(this.navigationFeedback);\n }\n if (this.playlistPanel && this.playlistPanel.parentNode) {\n this.playlistPanel.parentNode.removeChild(this.playlistPanel);\n }\n \n // Preserve existing player options so recreated players behave\n // consistently. We use a loose `Record<string, unknown>` because\n // PlayerOptions includes optional fields and an open string-indexed\n // bag of pass-through values that the spread flattens.\n const preservedPlayerOptions: Record<string, unknown> =\n this.player?.options ? { ...this.player.options } : {};\n\n // Remove event listeners before destroying\n if (this.player) {\n this.player.off('ended', this.handleTrackEnd);\n this.player.off('error', this.handleTrackError);\n // Detach the back-reference *before* destroying the player.\n // Player.destroy() cascade-destroys its `playlistManager`, but here\n // we're swapping the player out and the PlaylistManager owns a UI\n // (panel, track info, artwork) that must survive the swap. Without\n // this detach, the cascade would clear() the panel and the playlist\n // visually disappears when the user clicks an audio track in a\n // mixed playlist.\n this.player.playlistManager = null;\n this.player.destroy();\n }\n \n // Clear the host element\n this.hostElement.innerHTML = '';\n \n // Create new media element with appropriate type\n const mediaElement = document.createElement(elementType);\n // Respect configured preload (playlists should behave like single videos even with deferLoad)\n const preloadValue = (preservedPlayerOptions.preload as string | undefined) || 'metadata';\n mediaElement.setAttribute('preload', preloadValue);\n \n // For video elements with local media, set poster\n if (elementType === 'video' && track.poster && \n (mediaType === 'video' || mediaType === 'hls' || mediaType === 'dash')) {\n mediaElement.setAttribute('poster', track.poster);\n }\n \n // For external renderers (YouTube, Vimeo, SoundCloud, HLS, DASH), don't add source\n // The renderer will handle the source directly\n const isExternalRenderer = ['youtube', 'vimeo', 'soundcloud', 'hls', 'dash'].includes(mediaType);\n \n if (!isExternalRenderer) {\n // Add source for HTML5 media\n const source = document.createElement('source');\n source.src = track.src || '';\n if (track.type) {\n source.type = track.type;\n }\n mediaElement.appendChild(source);\n \n // Add tracks (captions, chapters, etc.)\n if (track.tracks && track.tracks.length > 0) {\n track.tracks.forEach((trackConfig: PlaylistTextTrack) => {\n const trackEl = document.createElement('track');\n trackEl.src = trackConfig.src || '';\n trackEl.kind = trackConfig.kind || 'captions';\n trackEl.srclang = trackConfig.srclang || 'en';\n trackEl.label = trackConfig.label || trackConfig.srclang || '';\n if (trackConfig.default) {\n trackEl.default = true;\n }\n mediaElement.appendChild(trackEl);\n });\n }\n }\n \n this.hostElement.appendChild(mediaElement);\n \n // Create new player with the media element\n // Pass the source for external renderers via options\n const playerOptions = {\n mediaType: elementType,\n poster: track.poster,\n audioDescriptionSrc: track.audioDescriptionSrc || null,\n audioDescriptionDuration: track.audioDescriptionDuration || null,\n signLanguageSrc: track.signLanguageSrc || null\n };\n // Merge back preserved options (so deferLoad/preload/etc remain active)\n Object.assign(playerOptions, preservedPlayerOptions);\n \n this.player = new this.PlayerClass(mediaElement, playerOptions);\n \n // Re-register playlist manager\n this.player.playlistManager = this;\n \n // Wait for player to be ready\n await new Promise<void>(resolve => {\n this.player.on('ready', () => resolve());\n });\n \n // Re-attach event listeners\n this.player.on('ended', this.handleTrackEnd);\n this.player.on('error', this.handleTrackError);\n \n // Re-attach all playlist UI elements to the new player's container\n if (this.player.container) {\n // Track artwork goes before video wrapper\n if (this.trackArtworkElement) {\n const videoWrapper = this.player.container.querySelector('.vidply-video-wrapper');\n if (videoWrapper) {\n this.player.container.insertBefore(this.trackArtworkElement, videoWrapper);\n } else {\n this.player.container.appendChild(this.trackArtworkElement);\n }\n }\n // Track info\n if (this.trackInfoElement) {\n this.player.container.appendChild(this.trackInfoElement);\n }\n // Navigation feedback (screen reader only)\n if (this.navigationFeedback) {\n this.player.container.appendChild(this.navigationFeedback);\n }\n // Playlist panel\n if (this.playlistPanel) {\n this.player.container.appendChild(this.playlistPanel);\n }\n }\n \n // Update container reference\n this.container = this.player.container;\n \n // Update controls (adds playlist prev/next buttons)\n this.updatePlayerControls();\n \n // Restore tracks data (we kept it during recreation)\n this.tracks = savedTracks;\n this.currentIndex = savedIndex;\n \n // Update playlist UI to reflect current state\n this.updatePlaylistUI();\n \n // Restore playlist panel visibility\n this.isPanelVisible = wasVisible;\n if (this.playlistPanel) {\n this.playlistPanel.style.display = wasVisible ? '' : 'none';\n }\n \n // For external renderers, load the track via player.load()\n // For HTML5, the source is already set on the element\n if (isExternalRenderer) {\n this.player.load({\n src: track.src ?? '',\n type: track.type,\n poster: track.poster,\n tracks: track.tracks || [],\n audioDescriptionSrc: track.audioDescriptionSrc || null,\n signLanguageSrc: track.signLanguageSrc || null\n });\n } else {\n this.player.load({\n src: track.src ?? '',\n type: track.type,\n poster: track.poster,\n tracks: track.tracks || [],\n audioDescriptionSrc: track.audioDescriptionSrc || null,\n signLanguageSrc: track.signLanguageSrc || null\n });\n }\n \n // Auto-play if requested\n if (autoPlay) {\n setTimeout(() => {\n this.player.play();\n }, 100);\n }\n \n return true;\n }\n \n init() {\n // Listen for track end\n this.player.on('ended', this.handleTrackEnd);\n this.player.on('error', this.handleTrackError);\n \n // Listen for playback state changes to show/hide playlist in fullscreen.\n // Handlers are pre-bound in the constructor so destroy() can detach them.\n this.player.on('play', this.handlePlaybackStateChange);\n this.player.on('pause', this.handlePlaybackStateChange);\n this.player.on('ended', this.handlePlaybackStateChange);\n // Use fullscreenchange event which is what the player actually emits\n this.player.on('fullscreenchange', this.handleFullscreenChange);\n \n // Listen for audio description state changes to update duration displays\n this.player.on('audiodescriptionenabled', this.handleAudioDescriptionChange);\n this.player.on('audiodescriptiondisabled', this.handleAudioDescriptionChange);\n \n // Create UI if needed\n if (this.options.showPanel) {\n this.createUI();\n }\n \n // Check for data-playlist attribute on player container (only if tracks weren't provided in options)\n if (this.tracks.length === 0 && this.initialTracks.length === 0) {\n this.loadPlaylistFromAttribute();\n }\n }\n \n /**\n * Load playlist from data-playlist attribute if present\n */\n loadPlaylistFromAttribute() {\n // Check the original wrapper element for data-playlist\n // Structure: #audio-player -> .vidply-player -> .vidply-video-wrapper -> <audio>\n // So we need to go up 3 levels\n if (!this.player.element || !this.player.element.parentElement) {\n return;\n }\n \n const videoWrapper = this.player.element.parentElement; // .vidply-video-wrapper\n const playerContainer = videoWrapper.parentElement; // .vidply-player\n const originalElement = playerContainer ? playerContainer.parentElement : null; // #audio-player (original div)\n \n if (!originalElement) {\n return;\n }\n \n // Load playlist options from data attributes\n this.loadOptionsFromAttributes(originalElement);\n \n const playlistData = originalElement.getAttribute('data-playlist');\n if (!playlistData) {\n return;\n }\n \n try {\n const tracks = JSON.parse(playlistData);\n if (Array.isArray(tracks) && tracks.length > 0) {\n this.loadPlaylist(tracks);\n } else {\n console.warn('VidPly Playlist: data-playlist is not a valid array or is empty');\n }\n } catch (error) {\n console.error('VidPly Playlist: Failed to parse data-playlist attribute', error);\n }\n }\n \n /**\n * Load playlist options from data attributes\n * @param {HTMLElement} element - Element to read attributes from\n */\n loadOptionsFromAttributes(element: HTMLElement) {\n // data-playlist-auto-advance\n const autoAdvance = element.getAttribute('data-playlist-auto-advance');\n if (autoAdvance !== null) {\n this.options.autoAdvance = autoAdvance === 'true';\n }\n \n // data-playlist-auto-play-first\n const autoPlayFirst = element.getAttribute('data-playlist-auto-play-first');\n if (autoPlayFirst !== null) {\n this.options.autoPlayFirst = autoPlayFirst === 'true';\n }\n \n // data-playlist-loop\n const loop = element.getAttribute('data-playlist-loop');\n if (loop !== null) {\n this.options.loop = loop === 'true';\n }\n \n // data-playlist-show-panel\n const showPanel = element.getAttribute('data-playlist-show-panel');\n if (showPanel !== null) {\n this.options.showPanel = showPanel === 'true';\n }\n }\n \n /**\n * Update player controls to add playlist navigation buttons\n */\n updatePlayerControls() {\n if (!this.player.controlBar) return;\n \n const controlBar = this.player.controlBar;\n \n // Clear existing controls content (except the element itself)\n controlBar.element.innerHTML = '';\n \n // Recreate controls with playlist buttons now available\n controlBar.createControls();\n \n // Reattach events for the new controls\n controlBar.attachEvents();\n controlBar.setupAutoHide();\n }\n \n /**\n * Load a playlist\n * @param {Array} tracks - Array of track objects\n */\n loadPlaylist(tracks: PlaylistTrack[]) {\n this.tracks = tracks;\n this.currentIndex = -1;\n \n // Add playlist class to container\n if (this.container) {\n this.container.classList.add('vidply-has-playlist');\n }\n \n // Update UI\n if (this.playlistPanel) {\n this.renderPlaylist();\n }\n \n // Auto-play first track (if enabled)\n if (tracks.length > 0) {\n if (this.options.autoPlayFirst) {\n this.play(0);\n } else {\n // Behave like a single video: load the first track (metadata/manifest)\n // but do not start playback.\n void this.loadTrack(0).catch(() => {\n // ignore\n });\n }\n }\n \n // Update visibility based on current state\n this.updatePlaylistVisibilityInFullscreen();\n }\n \n /**\n * Load a track without playing\n * This is the playlist equivalent of a \"single video initialized but not started yet\":\n * it updates UI selection and loads the media into the player so metadata/manifests\n * and feature managers can be ready, but it does not start playback.\n * @param {number} index - Track index\n */\n async loadTrack(index: number) {\n if (index < 0 || index >= this.tracks.length) {\n console.warn('VidPly Playlist: Invalid track index', index);\n return;\n }\n \n const track = this.tracks[index];\n if (!track) return;\n \n // Always update UI immediately (poster, buttons, duration, etc.).\n // Note: this is UI-only; actual media loading is performed by player.load() below.\n this.selectTrack(index);\n \n // Set guard flag to prevent cascade of next() calls during track change\n this.isChangingTrack = true;\n \n // Check if we should recreate the player for this track type\n if (this.options.recreatePlayers && this.hostElement && this.PlayerClass) {\n const currentMediaType = this.player ? \n (this.player.element.tagName === 'AUDIO' ? 'audio' : 'video') : null;\n const newMediaType = this.getTrackMediaType(track);\n const newElementType = (newMediaType === 'audio' || newMediaType === 'soundcloud') ? 'audio' : 'video';\n \n // Recreate if element type is different\n if (currentMediaType !== newElementType) {\n await this.recreatePlayerForTrack(track, false);\n // Re-apply selection to the newly created player (poster/tracks/buttons)\n this.selectTrack(index);\n \n // Emit event\n this.player.emit('playlisttrackchange', {\n index: index,\n item: track,\n total: this.tracks.length\n });\n \n // Clear guard flag\n setTimeout(() => {\n this.isChangingTrack = false;\n }, 150);\n return;\n }\n }\n \n const loadPromise = this.player.load({\n src: track.src ?? '',\n type: track.type,\n poster: track.poster,\n tracks: track.tracks || [],\n audioDescriptionSrc: track.audioDescriptionSrc || null,\n signLanguageSrc: track.signLanguageSrc || null,\n signLanguageSources: track.signLanguageSources || {}\n });\n\n // For playlist UX parity with single videos: fetch metadata/manifest now,\n // but do not start playback.\n if (this.player?.options?.deferLoad && typeof this.player.ensureLoaded === 'function') {\n Promise.resolve(loadPromise)\n .then(() => this.player?.ensureLoaded?.())\n .catch(() => {\n // ignore\n });\n }\n \n // Emit event\n this.player.emit('playlisttrackchange', {\n index: index,\n item: track,\n total: this.tracks.length\n });\n \n // Clear guard flag after a short delay to ensure track is loaded\n setTimeout(() => {\n this.isChangingTrack = false;\n }, 150);\n }\n\n /**\n * Select a track (UI/selection only; does NOT set the media src / does NOT initialize renderer)\n *\n * In \"B always\" playlist mode, you typically want `loadTrack()` on selection so the\n * selected item behaves like a single video (metadata/manifest loaded, features ready)\n * without auto-playing.\n * @param {number} index - Track index\n */\n selectTrack(index: number) {\n if (index < 0 || index >= this.tracks.length) {\n console.warn('VidPly Playlist: Invalid track index', index);\n return;\n }\n\n const track = this.tracks[index];\n if (!track) return;\n this.currentIndex = index;\n\n // Apply per-track metadata without touching the media source.\n // This ensures poster + feature buttons (chapters/captions/transcript/sign-language)\n // can be updated instantly even before any media network activity happens.\n try {\n // Poster for video element — validate the URL from the manifest\n // before we hand it to `<video>.poster`. Without the allow-list an\n // attacker-controlled `javascript:` or `data:text/html` value could\n // reach the attribute.\n if (this.player?.element?.tagName === 'VIDEO') {\n if (track.poster) {\n const resolved: string = typeof this.player.resolvePosterPath === 'function'\n ? this.player.resolvePosterPath(track.poster)\n : track.poster;\n const posterUrl = sanitizePosterUrl(resolved);\n if (posterUrl) {\n (this.player.element as HTMLVideoElement).poster = posterUrl;\n (this.player as Player & { applyPosterAspectRatio?: (url: string) => void }).applyPosterAspectRatio?.(posterUrl);\n } else {\n this.player.element.removeAttribute('poster');\n }\n } else {\n this.player.element.removeAttribute('poster');\n }\n }\n\n // Update sign language / audio description sources (used for button visibility)\n this.player.audioDescriptionSrc = track.audioDescriptionSrc || null;\n this.player.signLanguageSrc = track.signLanguageSrc || null;\n this.player.signLanguageSources = track.signLanguageSources || {};\n\n // Fill duration early for UI (progress/time display) without loading media\n if (track.duration && Number(track.duration) > 0) {\n this.player.state.duration = Number(track.duration);\n }\n // Also sync feature managers (they keep their own copy of sources)\n if (this.player.audioDescriptionManager) {\n this.player.audioDescriptionManager.src = track.audioDescriptionSrc || null;\n // Remember original (non-described) source for switching back later\n this.player.audioDescriptionManager.originalSource = track.src || this.player.originalSrc || null;\n }\n if (this.player.signLanguageManager) {\n this.player.signLanguageManager.src = track.signLanguageSrc || null;\n this.player.signLanguageManager.sources = track.signLanguageSources || {};\n this.player.signLanguageManager.currentLanguage = null;\n }\n\n // For audio description switching, remember original source even before first play\n if (track.src && !this.player.originalSrc) {\n this.player.originalSrc = track.src;\n }\n\n // Replace <track> elements so captions/chapters/transcript can be detected/loaded\n const existing: HTMLElement[] = Array.from(this.player.element.querySelectorAll('track'));\n existing.forEach(t => t.remove());\n\n if (Array.isArray(track.tracks)) {\n track.tracks.forEach((tc: PlaylistTextTrack) => {\n if (!tc?.src) return;\n const el = document.createElement('track');\n el.src = tc.src;\n el.kind = tc.kind || 'captions';\n el.srclang = tc.srclang || 'en';\n el.label = tc.label || tc.srclang || 'Track';\n if (tc.default) el.default = true;\n if (tc.describedSrc) {\n el.setAttribute('data-desc-src', tc.describedSrc);\n }\n this.player.element.appendChild(el);\n });\n }\n\n if (typeof this.player.invalidateTrackCache === 'function') {\n this.player.invalidateTrackCache();\n }\n\n // Re-scan described-track metadata for the AudioDescriptionManager. The\n // manager is no longer eagerly loaded on content-less players, so if this\n // track is the first to introduce described content, pull the chunk in\n // lazily before re-scanning.\n const reinitAudioDescription = (adm: Player['audioDescriptionManager']) => {\n if (!adm || typeof adm.initFromSourceElements !== 'function') return;\n try {\n adm.captionTracks = [];\n adm.initFromSourceElements(this.player.sourceElements, this.player.trackElements);\n } catch {\n // ignore\n }\n };\n if (this.player.audioDescriptionManager) {\n reinitAudioDescription(this.player.audioDescriptionManager);\n } else if (this.player.hasAudioDescriptionContent?.()) {\n void this.player.ensureAudioDescriptionManager()\n .then(reinitAudioDescription)\n .catch(() => { /* ignore */ });\n }\n\n // Refresh caption/transcript managers so menus reflect newly injected <track> elements\n // (important when we defer MP4/MP3 loading but still want VTT-based UI to work).\n if (this.player.captionManager && typeof this.player.captionManager.loadTracks === 'function') {\n try {\n this.player.captionManager.tracks = [];\n this.player.captionManager.currentTrack = null;\n this.player.captionManager.loadTracks();\n } catch {\n // ignore\n }\n }\n\n // TranscriptManager reads from TextTracks too; it will be correct after media starts.\n // For now, we just ensure control bar is rebuilt so the button is present.\n\n // Rebuild controls so feature buttons appear immediately\n if (typeof this.player.updateControlBar === 'function') {\n this.player.updateControlBar();\n }\n } catch {\n // ignore preview errors; selection should still work\n }\n\n this.updateTrackInfo(track);\n this.updatePlaylistUI();\n\n this.player.emit('playlisttrackselect', {\n index,\n item: track,\n total: this.tracks.length\n });\n }\n \n /**\n * Play a specific track\n * @param {number} index - Track index\n * @param {boolean} userInitiated - Whether this was triggered by user action (default: false)\n */\n async play(index: number, _userInitiated = false) {\n if (index < 0 || index >= this.tracks.length) {\n console.warn('VidPly Playlist: Invalid track index', index);\n return;\n }\n \n const track = this.tracks[index];\n if (!track) return;\n \n // Set guard flag to prevent cascade of next() calls during track change\n this.isChangingTrack = true;\n \n // Update current index\n this.currentIndex = index;\n \n // Check if we should recreate the player for this track type\n if (this.options.recreatePlayers && this.hostElement && this.PlayerClass) {\n const currentMediaType = this.player ? \n (this.player.element.tagName === 'AUDIO' ? 'audio' : 'video') : null;\n const newMediaType = this.getTrackMediaType(track);\n const newElementType = (newMediaType === 'audio' || newMediaType === 'soundcloud') ? 'audio' : 'video';\n \n // Recreate if element type is different\n if (currentMediaType !== newElementType) {\n await this.recreatePlayerForTrack(track, true); // true = autoPlay\n // Update UI after recreation\n this.updateTrackInfo(track);\n this.updatePlaylistUI();\n \n // Emit event\n this.player.emit('playlisttrackchange', {\n index: index,\n item: track,\n total: this.tracks.length\n });\n \n // Clear guard flag\n setTimeout(() => {\n this.isChangingTrack = false;\n }, 150);\n return;\n }\n }\n \n // Load track into player (normal path)\n // If audio description was toggled before the first play, load the described source directly.\n let srcToLoad = track.src;\n if (this.player?.audioDescriptionManager?.desiredState && track.audioDescriptionSrc) {\n this.player.originalSrc = track.src ?? null;\n this.player.audioDescriptionManager.originalSource = track.src ?? null;\n this.player.audioDescriptionManager.src = track.audioDescriptionSrc;\n srcToLoad = track.audioDescriptionSrc;\n }\n\n this.player.load({\n src: srcToLoad ?? '',\n type: track.type,\n poster: track.poster,\n tracks: track.tracks || [],\n audioDescriptionSrc: track.audioDescriptionSrc || null,\n signLanguageSrc: track.signLanguageSrc || null,\n signLanguageSources: track.signLanguageSources || {}\n });\n \n // Update UI\n this.updateTrackInfo(track);\n this.updatePlaylistUI();\n \n // Emit event\n this.player.emit('playlisttrackchange', {\n index: index,\n item: track,\n total: this.tracks.length\n });\n \n // Auto-play and clear guard flag after playback starts\n setTimeout(() => {\n this.player.play();\n // Clear guard flag after a short delay to ensure track has started\n setTimeout(() => {\n this.isChangingTrack = false;\n }, 50);\n }, 100);\n }\n \n /**\n * Play next track\n */\n next() {\n let nextIndex = this.currentIndex + 1;\n \n if (nextIndex >= this.tracks.length) {\n if (this.options.loop) {\n nextIndex = 0;\n } else {\n return;\n }\n }\n \n this.play(nextIndex);\n }\n \n /**\n * Play previous track\n */\n previous() {\n let prevIndex = this.currentIndex - 1;\n \n if (prevIndex < 0) {\n if (this.options.loop) {\n prevIndex = this.tracks.length - 1;\n } else {\n return;\n }\n }\n \n this.play(prevIndex);\n }\n \n /**\n * Handle track end\n */\n handleTrackEnd() {\n // Don't auto-advance if we're already in the process of changing tracks\n // This prevents a cascade of next() calls when loading a new track triggers an 'ended' event\n if (this.isChangingTrack) {\n return;\n }\n \n if (this.options.autoAdvance) {\n this.next();\n }\n }\n \n /**\n * Check if a source URL requires an external renderer\n * @param {string} src - Source URL\n * @returns {boolean}\n */\n isExternalRendererUrl(src: string | null | undefined) {\n if (!src) return false;\n return src.includes('youtube.com') || \n src.includes('youtu.be') || \n src.includes('vimeo.com') || \n src.includes('soundcloud.com') || \n src.includes('api.soundcloud.com') ||\n src.includes('.m3u8') ||\n src.includes('.mpd');\n }\n\n /**\n * Handle track error\n */\n handleTrackError(e: unknown) {\n // Don't auto-advance for external renderer tracks\n // External renderers (YouTube, Vimeo, SoundCloud, HLS, DASH) may trigger HTML5 errors\n // that should be ignored since the external renderer handles playback\n const currentTrack = this.getCurrentTrack();\n if (currentTrack && currentTrack.src && this.isExternalRendererUrl(currentTrack.src)) {\n // Silently ignore errors for external renderer tracks\n return;\n }\n \n // Don't auto-advance if we're in the process of changing tracks\n // This prevents a cascade of next() calls when switching between renderer types\n if (this.isChangingTrack) {\n return;\n }\n \n console.error('VidPly Playlist: Track error', e);\n \n // Try next track\n if (this.options.autoAdvance) {\n setTimeout(() => {\n this.next();\n }, 1000);\n }\n }\n \n /**\n * Handle playback state changes (for fullscreen playlist visibility)\n */\n handlePlaybackStateChange() {\n this.updatePlaylistVisibilityInFullscreen();\n }\n \n /**\n * Handle fullscreen state changes\n */\n handleFullscreenChange() {\n // Use a small delay to ensure fullscreen state is fully applied\n setTimeout(() => {\n this.updatePlaylistVisibilityInFullscreen();\n }, 50);\n }\n \n /**\n * Handle audio description state changes\n * Updates duration displays to show audio-described version duration when AD is enabled\n */\n handleAudioDescriptionChange() {\n const currentTrack = this.getCurrentTrack();\n if (!currentTrack) return;\n \n // Update the track info display with the appropriate duration\n this.updateTrackInfo(currentTrack);\n \n // Update the playlist UI to reflect duration changes (aria-labels)\n this.updatePlaylistUI();\n \n // Update visual duration elements in playlist panel\n this.updatePlaylistDurations();\n }\n \n /**\n * Update the visual duration displays in the playlist panel\n * Called when audio description state changes\n */\n updatePlaylistDurations() {\n if (!this.playlistPanel) return;\n \n const items = this.playlistPanel.querySelectorAll('.vidply-playlist-item');\n \n items.forEach((item: Element, index: number) => {\n const track = this.tracks[index];\n if (!track) return;\n \n const effectiveDuration = this.getEffectiveDuration(track);\n const trackDuration = effectiveDuration ? TimeUtils.formatTime(effectiveDuration as number) : '';\n \n // Update duration badge on thumbnail (if exists)\n const durationBadge = item.querySelector('.vidply-playlist-duration-badge');\n if (durationBadge) {\n durationBadge.textContent = trackDuration;\n }\n \n // Update inline duration (if exists)\n const inlineDuration = item.querySelector('.vidply-playlist-item-duration');\n if (inlineDuration) {\n inlineDuration.textContent = trackDuration;\n }\n });\n }\n \n /**\n * Get the effective duration for a track based on audio description state\n * @param {Object} track - Track object\n * @returns {number|null} - Duration in seconds or null if not available\n */\n getEffectiveDuration(track: PlaylistTrack) {\n if (!track) return null;\n \n const isAudioDescriptionEnabled = this.player.state.audioDescriptionEnabled;\n \n // If audio description is enabled and track has audioDescriptionDuration, use it\n if (isAudioDescriptionEnabled && track.audioDescriptionDuration) {\n return track.audioDescriptionDuration;\n }\n \n // Otherwise use regular duration\n return track.duration || null;\n }\n \n /**\n * Update playlist visibility based on fullscreen and playback state\n * In fullscreen: show when paused/not started, hide when playing\n * Outside fullscreen: respect original panel visibility setting\n */\n updatePlaylistVisibilityInFullscreen() {\n const playlistPanel = this.playlistPanel;\n if (!playlistPanel || !this.tracks.length) return;\n\n const isFullscreen = this.player.state.fullscreen;\n const isPlaying = this.player.state.playing;\n\n if (isFullscreen) {\n // In fullscreen: show only when not playing (paused or not started)\n // Check playing state explicitly since paused might not be set initially\n if (!isPlaying) {\n playlistPanel.classList.add('vidply-playlist-fullscreen-visible');\n playlistPanel.style.display = 'block';\n } else {\n playlistPanel.classList.remove('vidply-playlist-fullscreen-visible');\n // Add a smooth fade out with delay to match CSS transition\n setTimeout(() => {\n // Double-check state hasn't changed before hiding\n if (this.player.state.playing && this.player.state.fullscreen) {\n playlistPanel.style.display = 'none';\n }\n }, 300); // Match CSS transition duration\n }\n } else {\n // Outside fullscreen: restore original behavior\n playlistPanel.classList.remove('vidply-playlist-fullscreen-visible');\n if (this.isPanelVisible && this.tracks.length > 0) {\n playlistPanel.style.display = 'block';\n } else {\n playlistPanel.style.display = 'none';\n }\n }\n }\n \n /**\n * Create playlist UI\n */\n createUI() {\n // Find player container\n this.container = this.player.container;\n \n if (!this.container) {\n console.warn('VidPly Playlist: No container found');\n return;\n }\n \n // Track artwork element (shows album art/poster for audio tracks).\n // Important: in mixed playlists the player may start as <video> and later recreate to <audio>,\n // so we create this lazily in `updateTrackArtwork()` when we actually have an audio element.\n \n // Create track info element (shows current track)\n this.trackInfoElement = DOMUtils.createElement('div', {\n className: 'vidply-track-info',\n attributes: {\n role: 'status'\n }\n });\n this.trackInfoElement.style.display = 'none';\n \n this.container.appendChild(this.trackInfoElement);\n \n // Create navigation feedback live region\n this.navigationFeedback = DOMUtils.createElement('div', {\n className: 'vidply-sr-only',\n attributes: {\n role: 'status',\n 'aria-live': 'polite',\n 'aria-atomic': 'true'\n }\n });\n this.container.appendChild(this.navigationFeedback);\n \n // Create playlist panel with proper landmark\n this.playlistPanel = DOMUtils.createElement('div', {\n className: 'vidply-playlist-panel',\n attributes: {\n id: `${this.uniqueId}-panel`,\n role: 'region',\n 'aria-label': i18n.t('playlist.title'),\n 'aria-labelledby': `${this.uniqueId}-heading`\n }\n });\n this.playlistPanel.style.display = this.isPanelVisible ? 'none' : 'none'; // Will be shown when playlist is loaded\n \n this.container.appendChild(this.playlistPanel);\n }\n \n /**\n * Update track info display\n */\n updateTrackInfo(track: PlaylistTrack) {\n if (!this.trackInfoElement) return;\n \n const trackNumber = this.currentIndex + 1;\n const totalTracks = this.tracks.length;\n const trackTitle = track.title || i18n.t('playlist.untitled');\n const trackArtist = track.artist || '';\n \n // Use effective duration (audio description duration when AD is enabled)\n const effectiveDuration = this.getEffectiveDuration(track);\n const trackDuration = effectiveDuration ? TimeUtils.formatTime(effectiveDuration as number) : '';\n const trackDurationReadable = effectiveDuration ? TimeUtils.formatDuration(effectiveDuration as number) : '';\n \n // Screen reader announcement - include duration if available\n const artistPart = trackArtist ? i18n.t('playlist.by') + trackArtist : '';\n const durationPart = trackDurationReadable ? `. ${trackDurationReadable}` : '';\n const announcement = i18n.t('playlist.nowPlaying', {\n current: trackNumber,\n total: totalTracks,\n title: trackTitle,\n artist: artistPart\n }) + durationPart;\n \n const trackOfText = i18n.t('playlist.trackOf', {\n current: trackNumber,\n total: totalTracks\n });\n \n // Build duration HTML if available\n const durationHtml = trackDuration \n ? `<span class=\"vidply-track-duration\" aria-hidden=\"true\">${DOMUtils.escapeHTML(trackDuration)}</span>` \n : '';\n \n // Get description if available\n const trackDescription = track.description || '';\n \n this.trackInfoElement.innerHTML = `\n <span class=\"vidply-sr-only\">${DOMUtils.escapeHTML(announcement)}</span>\n <div class=\"vidply-track-header\" aria-hidden=\"true\">\n <span class=\"vidply-track-number\">${DOMUtils.escapeHTML(trackOfText)}</span>\n ${durationHtml}\n </div>\n <div class=\"vidply-track-title\" aria-hidden=\"true\">${DOMUtils.escapeHTML(trackTitle)}</div>\n ${trackArtist ? `<div class=\"vidply-track-artist\" aria-hidden=\"true\">${DOMUtils.escapeHTML(trackArtist)}</div>` : ''}\n ${trackDescription ? `<div class=\"vidply-track-description\" aria-hidden=\"true\">${DOMUtils.escapeHTML(trackDescription)}</div>` : ''}\n `;\n \n this.trackInfoElement.style.display = 'block';\n \n // Update track artwork if available (for audio playlists)\n this.updateTrackArtwork(track);\n }\n \n /**\n * Update track artwork display (for audio playlists)\n */\n updateTrackArtwork(track: PlaylistTrack) {\n // Only show artwork for audio players.\n // In mixed playlists we may recreate from <video> -> <audio> later, so ensure the element exists lazily.\n if (this.player?.element?.tagName !== 'AUDIO') {\n if (this.trackArtworkElement) {\n this.trackArtworkElement.style.display = 'none';\n }\n return;\n }\n\n // Lazily create artwork element once we have an audio element/container.\n if (!this.trackArtworkElement && this.container) {\n this.trackArtworkElement = DOMUtils.createElement('div', {\n className: 'vidply-track-artwork',\n attributes: {\n 'aria-hidden': 'true'\n }\n });\n this.trackArtworkElement.style.display = 'none';\n\n // Insert before video wrapper (if present) for consistent layout.\n const videoWrapper = this.container.querySelector('.vidply-video-wrapper');\n if (videoWrapper) {\n this.container.insertBefore(this.trackArtworkElement, videoWrapper);\n } else {\n this.container.appendChild(this.trackArtworkElement);\n }\n }\n\n if (!this.trackArtworkElement) return;\n \n // A track manifest is attacker-influenced data — a value like\n // `x); background: url(evil.svg` must not be allowed to break out\n // of the declaration. `toCssBackgroundImage` returns null when the\n // URL fails validation, so we leave the element hidden instead of\n // assigning a dangerous value.\n const safeBackground = track.poster ? toCssBackgroundImage(track.poster) : null;\n if (safeBackground) {\n this.trackArtworkElement.style.backgroundImage = safeBackground;\n this.trackArtworkElement.style.display = 'block';\n } else {\n this.trackArtworkElement.style.backgroundImage = '';\n this.trackArtworkElement.style.display = 'none';\n }\n }\n \n /**\n * Render playlist\n */\n renderPlaylist() {\n if (!this.playlistPanel) return;\n \n // Clear existing\n this.playlistPanel.innerHTML = '';\n \n // Create header\n const header = DOMUtils.createElement('h2', {\n className: 'vidply-playlist-header',\n attributes: {\n id: `${this.uniqueId}-heading`\n }\n });\n header.textContent = `${i18n.t('playlist.title')} (${this.tracks.length})`;\n this.playlistPanel.appendChild(header);\n \n // Add keyboard instructions (visually hidden)\n const instructions = DOMUtils.createElement('div', {\n className: 'vidply-sr-only',\n attributes: {\n id: `${this.uniqueId}-keyboard-instructions`\n }\n });\n instructions.textContent = i18n.t('playlist.keyboardInstructions');\n this.playlistPanel.appendChild(instructions);\n \n // Create list (proper ul element)\n // Note: role=\"listbox\" requires option children. We use role=\"none\" on <li> elements\n // so the buttons with role=\"option\" become the semantic children of the listbox.\n const list = DOMUtils.createElement('ul', {\n className: 'vidply-playlist-list',\n attributes: {\n role: 'listbox',\n 'aria-labelledby': `${this.uniqueId}-heading`,\n 'aria-describedby': `${this.uniqueId}-keyboard-instructions`\n }\n });\n \n this.tracks.forEach((track, index) => {\n const item = this.createPlaylistItem(track, index);\n list.appendChild(item);\n });\n \n this.playlistPanel.appendChild(list);\n \n // Show panel if it should be visible\n if (this.isPanelVisible) {\n this.playlistPanel.style.display = 'block';\n }\n }\n \n /**\n * Create playlist item element\n */\n createPlaylistItem(track: PlaylistTrack, index: number) {\n const trackTitle = track.title || i18n.t('playlist.trackUntitled', { number: index + 1 });\n const trackArtist = track.artist ? i18n.t('playlist.by') + track.artist : '';\n \n // Use effective duration (audio description duration when AD is enabled)\n const effectiveDuration = this.getEffectiveDuration(track);\n const trackDuration = effectiveDuration ? TimeUtils.formatTime(effectiveDuration as number) : '';\n const trackDurationReadable = effectiveDuration ? TimeUtils.formatDuration(effectiveDuration as number) : '';\n const isActive = index === this.currentIndex;\n \n // Build accessible label for screen readers\n // With role=\"option\" and aria-checked, screen reader will announce selection state\n // Position is already announced via aria-posinset/aria-setsize\n // Format: \"Title by Artist. 3 minutes, 45 seconds.\"\n let ariaLabel = `${trackTitle}${trackArtist}`;\n if (trackDurationReadable) {\n ariaLabel += `. ${trackDurationReadable}`;\n }\n \n // Create list item container\n // role=\"none\" removes the <li> from the accessibility tree so the button with\n // role=\"option\" becomes the direct semantic child of the listbox (required by ARIA).\n const item = DOMUtils.createElement('li', {\n className: isActive ? 'vidply-playlist-item vidply-playlist-item-active' : 'vidply-playlist-item',\n attributes: {\n 'data-playlist-index': String(index),\n role: 'none'\n }\n });\n \n // Create button wrapper for interactive content\n const button = DOMUtils.createElement('button', {\n className: 'vidply-playlist-item-button',\n attributes: {\n type: 'button',\n role: 'option',\n tabIndex: index === 0 ? '0' : '-1',\n 'aria-label': ariaLabel,\n 'aria-posinset': String(index + 1),\n 'aria-setsize': String(this.tracks.length),\n 'aria-checked': isActive ? 'true' : 'false'\n }\n });\n \n // Add aria-current if active\n if (isActive) {\n button.setAttribute('aria-current', 'true');\n button.setAttribute('tabIndex', '0'); // Active item should always be tabbable\n }\n \n // Thumbnail container with optional duration badge\n const thumbnailContainer = DOMUtils.createElement('span', {\n className: 'vidply-playlist-thumbnail-container',\n attributes: {\n 'aria-hidden': 'true'\n }\n });\n \n // Thumbnail or icon\n const thumbnail = DOMUtils.createElement('span', {\n className: 'vidply-playlist-thumbnail'\n });\n \n // Same rule as trackArtworkElement below: only set backgroundImage\n // when the manifest URL passes the allow-list so a hostile feed\n // cannot break out of the CSS `url(...)` declaration.\n const safeThumbnail = track.poster ? toCssBackgroundImage(track.poster) : null;\n if (safeThumbnail) {\n thumbnail.style.backgroundImage = safeThumbnail;\n } else {\n const icon = createIconElement('music');\n icon.classList.add('vidply-playlist-thumbnail-icon');\n thumbnail.appendChild(icon);\n }\n \n thumbnailContainer.appendChild(thumbnail);\n \n // Duration badge on thumbnail (like YouTube) - only show if there's a poster\n if (trackDuration && track.poster) {\n const durationBadge = DOMUtils.createElement('span', {\n className: 'vidply-playlist-duration-badge'\n });\n durationBadge.textContent = trackDuration;\n thumbnailContainer.appendChild(durationBadge);\n }\n \n button.appendChild(thumbnailContainer);\n \n // Info section (title, artist, description)\n const info = DOMUtils.createElement('span', {\n className: 'vidply-playlist-item-info',\n attributes: {\n 'aria-hidden': 'true'\n }\n });\n \n // Title row with optional inline duration (for when no thumbnail)\n const titleRow = DOMUtils.createElement('span', {\n className: 'vidply-playlist-item-title-row'\n });\n \n const title = DOMUtils.createElement('span', {\n className: 'vidply-playlist-item-title'\n });\n title.textContent = trackTitle;\n titleRow.appendChild(title);\n \n // Inline duration (shown when no poster/thumbnail)\n if (trackDuration && !track.poster) {\n const inlineDuration = DOMUtils.createElement('span', {\n className: 'vidply-playlist-item-duration'\n });\n inlineDuration.textContent = trackDuration;\n titleRow.appendChild(inlineDuration);\n }\n \n info.appendChild(titleRow);\n \n // Artist\n if (track.artist) {\n const artist = DOMUtils.createElement('span', {\n className: 'vidply-playlist-item-artist'\n });\n artist.textContent = track.artist;\n info.appendChild(artist);\n }\n \n // Description (truncated)\n if (track.description) {\n const description = DOMUtils.createElement('span', {\n className: 'vidply-playlist-item-description'\n });\n description.textContent = track.description;\n info.appendChild(description);\n }\n \n button.appendChild(info);\n \n // Play icon\n const playIcon = createIconElement('play');\n playIcon.classList.add('vidply-playlist-item-icon');\n playIcon.setAttribute('aria-hidden', 'true');\n button.appendChild(playIcon);\n \n // Click handler\n button.addEventListener('click', () => {\n const track = this.tracks[index];\n const isExternalRenderer = this.isExternalRendererUrl(track?.src);\n \n // Exit fullscreen for external renderer tracks (YouTube, Vimeo, SoundCloud)\n // These have their own native controls that work better outside vidply's fullscreen\n if (isExternalRenderer && this.player.state.fullscreen) {\n this.player.exitFullscreen();\n // Small delay to let fullscreen exit before loading\n setTimeout(() => {\n this.play(index, true);\n }, 100);\n } else {\n this.play(index, true); // User-initiated\n }\n });\n \n // Keyboard handler\n button.addEventListener('keydown', (e) => {\n this.handlePlaylistItemKeydown(e, index);\n });\n \n // Append button to list item\n item.appendChild(button);\n \n return item;\n }\n \n /**\n * Handle keyboard navigation in playlist items\n */\n handlePlaylistItemKeydown(e: KeyboardEvent, index: number) {\n if (!this.playlistPanel) return;\n const buttons: HTMLElement[] = Array.from(this.playlistPanel.querySelectorAll('.vidply-playlist-item-button'));\n let newIndex = -1;\n let announcement = '';\n \n switch(e.key) {\n case 'Enter':\n case ' ':\n e.preventDefault();\n e.stopPropagation();\n {\n const track = this.tracks[index];\n const isExternalRenderer = this.isExternalRendererUrl(track?.src);\n \n // Exit fullscreen for external renderer tracks (YouTube, Vimeo, SoundCloud)\n if (isExternalRenderer && this.player.state.fullscreen) {\n this.player.exitFullscreen();\n setTimeout(() => {\n this.play(index, true);\n }, 100);\n } else {\n this.play(index, true); // User-initiated\n }\n }\n return; // No need to move focus\n \n case 'ArrowDown':\n e.preventDefault();\n e.stopPropagation();\n // Move to next item\n if (index < buttons.length - 1) {\n newIndex = index + 1;\n } else {\n // At the end, announce boundary\n announcement = i18n.t('playlist.endOfPlaylist', { current: buttons.length, total: buttons.length });\n }\n break;\n\n case 'ArrowUp':\n e.preventDefault();\n e.stopPropagation();\n // Move to previous item\n if (index > 0) {\n newIndex = index - 1;\n } else {\n // At the beginning, announce boundary\n announcement = i18n.t('playlist.beginningOfPlaylist', { total: buttons.length });\n }\n break;\n\n case 'PageDown':\n e.preventDefault();\n e.stopPropagation();\n // Move 5 items down (or to end)\n newIndex = Math.min(index + 5, buttons.length - 1);\n if (newIndex === buttons.length - 1 && index !== newIndex) {\n announcement = i18n.t('playlist.jumpedToLastTrack', { current: newIndex + 1, total: buttons.length });\n }\n break;\n\n case 'PageUp':\n e.preventDefault();\n e.stopPropagation();\n // Move 5 items up (or to beginning)\n newIndex = Math.max(index - 5, 0);\n if (newIndex === 0 && index !== newIndex) {\n announcement = i18n.t('playlist.jumpedToFirstTrack', { total: buttons.length });\n }\n break;\n\n case 'Home':\n e.preventDefault();\n e.stopPropagation();\n // Move to first item\n newIndex = 0;\n if (index !== 0) {\n announcement = i18n.t('playlist.firstTrack', { total: buttons.length });\n }\n break;\n\n case 'End':\n e.preventDefault();\n e.stopPropagation();\n // Move to last item\n newIndex = buttons.length - 1;\n if (index !== buttons.length - 1) {\n announcement = i18n.t('playlist.lastTrack', { current: buttons.length, total: buttons.length });\n }\n break;\n }\n \n // Update tab indices for roving tabindex pattern\n if (newIndex !== -1 && newIndex !== index) {\n const currentButton = buttons[index];\n const newButton = buttons[newIndex];\n if (currentButton && newButton) {\n currentButton.setAttribute('tabIndex', '-1');\n newButton.setAttribute('tabIndex', '0');\n newButton.focus({ preventScroll: false });\n\n // Scroll the focused item into view (same behavior as mouse interaction)\n const item = newButton.closest('.vidply-playlist-item');\n if (item) {\n item.scrollIntoView(reducedMotionScrollOptions('nearest'));\n }\n }\n }\n \n // Announce navigation feedback\n if (announcement && this.navigationFeedback) {\n this.navigationFeedback.textContent = announcement;\n // Clear after a short delay to allow for repeated announcements\n setTimeout(() => {\n if (this.navigationFeedback) {\n this.navigationFeedback.textContent = '';\n }\n }, 1000);\n }\n }\n \n /**\n * Update playlist UI (highlight current track)\n */\n updatePlaylistUI() {\n if (!this.playlistPanel) return;\n \n const items = this.playlistPanel.querySelectorAll('.vidply-playlist-item');\n const buttons = this.playlistPanel.querySelectorAll('.vidply-playlist-item-button');\n \n items.forEach((item: Element, index: number) => {\n const button = buttons[index];\n if (!button) return;\n \n const track = this.tracks[index];\n if (!track) return;\n const trackTitle = track.title || i18n.t('playlist.trackUntitled', { number: index + 1 });\n const trackArtist = track.artist ? i18n.t('playlist.by') + track.artist : '';\n \n // Use effective duration (audio description duration when AD is enabled)\n const effectiveDuration = this.getEffectiveDuration(track);\n const trackDurationReadable = effectiveDuration ? TimeUtils.formatDuration(effectiveDuration as number) : '';\n \n if (index === this.currentIndex) {\n // Update list item styling\n item.classList.add('vidply-playlist-item-active');\n \n // Update button ARIA attributes\n button.setAttribute('aria-current', 'true');\n button.setAttribute('aria-checked', 'true');\n button.setAttribute('tabIndex', '0'); // Active item should be tabbable\n \n // Simplified aria-label - status and actions are announced via ARIA roles\n let ariaLabel = `${trackTitle}${trackArtist}`;\n if (trackDurationReadable) {\n ariaLabel += `. ${trackDurationReadable}`;\n }\n button.setAttribute('aria-label', ariaLabel);\n \n // Scroll into view within playlist panel (uses 'nearest' to minimize page scroll)\n item.scrollIntoView(reducedMotionScrollOptions('nearest'));\n } else {\n // Update list item styling\n item.classList.remove('vidply-playlist-item-active');\n \n // Update button ARIA attributes\n button.removeAttribute('aria-current');\n button.setAttribute('aria-checked', 'false');\n button.setAttribute('tabIndex', '-1'); // Remove from tab order (use arrow keys)\n \n // Simplified aria-label - status and actions are announced via ARIA roles\n let ariaLabel = `${trackTitle}${trackArtist}`;\n if (trackDurationReadable) {\n ariaLabel += `. ${trackDurationReadable}`;\n }\n button.setAttribute('aria-label', ariaLabel);\n }\n });\n }\n \n /**\n * Get current track\n */\n getCurrentTrack() {\n return this.tracks[this.currentIndex] || null;\n }\n \n /**\n * Get playlist info\n */\n getPlaylistInfo() {\n return {\n currentIndex: this.currentIndex,\n totalTracks: this.tracks.length,\n currentTrack: this.getCurrentTrack(),\n hasNext: this.hasNext(),\n hasPrevious: this.hasPrevious()\n };\n }\n \n /**\n * Check if there is a next track\n */\n hasNext() {\n if (this.options.loop) return true;\n return this.currentIndex < this.tracks.length - 1;\n }\n \n /**\n * Check if there is a previous track\n */\n hasPrevious() {\n if (this.options.loop) return true;\n return this.currentIndex > 0;\n }\n \n /**\n * Add track to playlist\n */\n addTrack(track: PlaylistTrack) {\n this.tracks.push(track);\n \n if (this.playlistPanel) {\n this.renderPlaylist();\n }\n }\n \n /**\n * Remove track from playlist\n */\n removeTrack(index: number) {\n if (index < 0 || index >= this.tracks.length) return;\n \n this.tracks.splice(index, 1);\n \n // Adjust current index if needed\n if (index < this.currentIndex) {\n this.currentIndex--;\n } else if (index === this.currentIndex) {\n // Current track was removed, play next or stop\n if (this.currentIndex >= this.tracks.length) {\n this.currentIndex = this.tracks.length - 1;\n }\n \n if (this.currentIndex >= 0) {\n this.play(this.currentIndex);\n }\n }\n \n if (this.playlistPanel) {\n this.renderPlaylist();\n }\n }\n \n /**\n * Clear playlist\n */\n clear() {\n this.tracks = [];\n this.currentIndex = -1;\n \n if (this.playlistPanel) {\n this.playlistPanel.innerHTML = '';\n this.playlistPanel.style.display = 'none';\n }\n \n if (this.trackInfoElement) {\n this.trackInfoElement.innerHTML = '';\n this.trackInfoElement.style.display = 'none';\n }\n \n if (this.trackArtworkElement) {\n this.trackArtworkElement.style.backgroundImage = '';\n this.trackArtworkElement.style.display = 'none';\n }\n }\n \n /**\n * Toggle playlist panel visibility\n * @param {boolean} show - Optional: force show (true) or hide (false)\n * @returns {boolean} - New visibility state\n */\n togglePanel(show?: boolean) {\n const playlistPanel = this.playlistPanel;\n if (!playlistPanel) return false;\n\n // Determine new state\n const shouldShow = show !== undefined ? show : playlistPanel.style.display === 'none';\n\n if (shouldShow) {\n playlistPanel.style.display = 'block';\n this.isPanelVisible = true;\n\n // Focus first item if playlist has tracks\n if (this.tracks.length > 0) {\n setTimeout(() => {\n const firstItem = playlistPanel.querySelector<HTMLElement>('.vidply-playlist-item[tabindex=\"0\"]');\n if (firstItem) {\n firstItem.focus({ preventScroll: true });\n }\n }, 100);\n }\n \n // Update toggle button state if it exists\n if (this.player.controlBar && this.player.controlBar.controls.playlistToggle) {\n this.player.controlBar.controls.playlistToggle.setAttribute('aria-expanded', 'true');\n this.player.controlBar.controls.playlistToggle.setAttribute('aria-pressed', 'true');\n }\n } else {\n playlistPanel.style.display = 'none';\n this.isPanelVisible = false;\n \n // Update toggle button state if it exists\n if (this.player.controlBar && this.player.controlBar.controls.playlistToggle) {\n this.player.controlBar.controls.playlistToggle.setAttribute('aria-expanded', 'false');\n this.player.controlBar.controls.playlistToggle.setAttribute('aria-pressed', 'false');\n \n // Return focus to toggle button\n this.player.controlBar.controls.playlistToggle.focus({ preventScroll: true });\n }\n }\n \n return this.isPanelVisible;\n }\n \n /**\n * Show playlist panel\n */\n showPanel() {\n return this.togglePanel(true);\n }\n \n /**\n * Hide playlist panel\n */\n hidePanel() {\n return this.togglePanel(false);\n }\n \n /**\n * Destroy playlist manager\n */\n destroy() {\n // Remove every listener registered in init(). All handlers are pre-bound\n // in the constructor, so these references match what on() registered.\n this.player.off('ended', this.handleTrackEnd);\n this.player.off('error', this.handleTrackError);\n this.player.off('play', this.handlePlaybackStateChange);\n this.player.off('pause', this.handlePlaybackStateChange);\n this.player.off('ended', this.handlePlaybackStateChange);\n this.player.off('fullscreenchange', this.handleFullscreenChange);\n this.player.off('audiodescriptionenabled', this.handleAudioDescriptionChange);\n this.player.off('audiodescriptiondisabled', this.handleAudioDescriptionChange);\n \n // Remove UI\n if (this.trackArtworkElement) {\n this.trackArtworkElement.remove();\n }\n \n if (this.trackInfoElement) {\n this.trackInfoElement.remove();\n }\n \n if (this.playlistPanel) {\n this.playlistPanel.remove();\n }\n \n // Clear data\n this.clear();\n }\n}\n\nexport default PlaylistManager;\n", "/**\n * VidPly - Universal Video Player\n * Main Entry Point\n */\n\nimport { Player } from './core/Player.js';\nimport { observeForLazyInit } from './core/LazyInit.js';\nimport { PlaylistManager } from './features/PlaylistManager.js';\nimport { isForbiddenKey } from './utils/Sanitize.js';\nimport type { PlayerOptions } from './types/options.js';\n\n// Re-export so existing consumers of `import { LazyHandle } from 'vidply'`\n// keep working; the canonical definition now lives in core/LazyInit.\nexport type { LazyHandle } from './core/LazyInit.js';\n\n/**\n * Filter `__proto__` / `constructor` / `prototype` keys from an attacker-\n * influenced JSON object before merging it into the player options. This\n * avoids prototype-pollution turning a malformed `data-vidply-options`\n * attribute into a global gadget.\n */\nfunction sanitizeOptionsObject(input: unknown): Partial<PlayerOptions> {\n if (!input || typeof input !== 'object' || Array.isArray(input)) {\n return {};\n }\n const out: Record<string, unknown> = Object.create(null);\n for (const [key, value] of Object.entries(input as Record<string, unknown>)) {\n if (isForbiddenKey(key)) continue;\n out[key] = value;\n }\n return out as Partial<PlayerOptions>;\n}\n\n/**\n * Parse a `data-vidply-options` JSON string. Robust against malformed input\n * — a single broken attribute on one element no longer aborts the auto-init\n * loop for every other player on the page.\n */\nfunction parseInlineOptions(element: HTMLElement): Partial<PlayerOptions> {\n const raw = element.dataset.vidplyOptions;\n if (!raw) return {};\n try {\n const parsed: unknown = JSON.parse(raw);\n return sanitizeOptionsObject(parsed);\n } catch (err) {\n console.warn('[VidPly] Ignored malformed data-vidply-options:', err);\n return {};\n }\n}\n\nfunction initializePlayers(): void {\n const elements = document.querySelectorAll<HTMLElement>('[data-vidply]');\n\n elements.forEach((element) => {\n const options = parseInlineOptions(element);\n const dataOptions = parseDataAttributes(element.dataset);\n const mergedOptions: Partial<PlayerOptions> = { ...dataOptions, ...options };\n\n const lazyInit =\n element.dataset.vidplyLazy !== 'false' && mergedOptions.lazyInit !== false;\n const lazyMargin =\n element.dataset.vidplyLazyMargin || (mergedOptions.lazyMargin as string) || '500px';\n\n if (lazyInit && 'IntersectionObserver' in window) {\n observeForLazyInit<Partial<PlayerOptions>>(\n element,\n mergedOptions,\n lazyMargin,\n (target, opts) => { new Player(target, opts); }\n );\n } else {\n new Player(element, mergedOptions);\n }\n });\n}\n\nfunction parseDataAttributes(dataset: DOMStringMap): Partial<PlayerOptions> {\n const options: Record<string, unknown> = Object.create(null);\n\n const attributeMap: Record<string, keyof PlayerOptions> = {\n signLanguageSrc: 'signLanguageSrc',\n signLanguageButton: 'signLanguageButton',\n signLanguagePosition: 'signLanguagePosition',\n signLanguageDisplayMode: 'signLanguageDisplayMode',\n audioDescriptionSrc: 'audioDescriptionSrc',\n audioDescriptionButton: 'audioDescriptionButton',\n audioDescriptionMode: 'audioDescriptionMode',\n audioDescriptionSpeech: 'audioDescriptionSpeech',\n audioDescriptionExtended: 'audioDescriptionExtended',\n autoplay: 'autoplay',\n loop: 'loop',\n muted: 'muted',\n controls: 'controls',\n poster: 'poster',\n width: 'width',\n height: 'height',\n language: 'language',\n captions: 'captions',\n captionsDefault: 'captionsDefault',\n transcript: 'transcript',\n transcriptButton: 'transcriptButton',\n keyboard: 'keyboard',\n responsive: 'responsive',\n pipButton: 'pipButton',\n fullscreenButton: 'fullscreenButton',\n floating: 'floating',\n floatingPosition: 'floatingPosition',\n floatingMinViewportWidth: 'floatingMinViewportWidth',\n lazyInit: 'lazyInit',\n lazyMargin: 'lazyMargin',\n theme: 'theme'\n };\n\n for (const [dataKey, optionKey] of Object.entries(attributeMap)) {\n if (isForbiddenKey(optionKey)) continue;\n const value = dataset[dataKey];\n if (value === undefined) continue;\n if (value === 'true') {\n options[optionKey] = true;\n } else if (value === 'false') {\n options[optionKey] = false;\n } else if (value !== '' && !Number.isNaN(Number(value))) {\n options[optionKey] = Number(value);\n } else {\n options[optionKey] = value;\n }\n }\n\n const signLanguageSources: Record<string, string> = Object.create(null);\n for (const key of Object.keys(dataset)) {\n if (key.startsWith('signLanguageSrc') && key !== 'signLanguageSrc') {\n const langMatch = key.match(/^signLanguageSrc([A-Z][a-z]*)$/);\n if (langMatch && langMatch[1]) {\n const langCode = langMatch[1].toLowerCase();\n const value = dataset[key];\n if (value !== undefined) {\n signLanguageSources[langCode] = value;\n }\n }\n }\n }\n\n if (Object.keys(signLanguageSources).length > 0) {\n options.signLanguageSources = signLanguageSources;\n if (dataset.signLanguageSrc && !options.signLanguageSrc) {\n options.signLanguageSrc = dataset.signLanguageSrc;\n }\n }\n\n if (dataset.vidplyLanguageFiles) {\n try {\n const parsed = JSON.parse(dataset.vidplyLanguageFiles);\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n options.languageFiles = sanitizeOptionsObject(parsed) as Record<string, string>;\n }\n } catch (e) {\n console.warn('Invalid JSON in data-vidply-language-files:', e);\n }\n }\n\n if (dataset.vidplyLanguageFile) {\n try {\n const parsed = JSON.parse(dataset.vidplyLanguageFile);\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n options.languageFiles = sanitizeOptionsObject(parsed) as Record<string, string>;\n }\n } catch {\n if (dataset.vidplyLanguageFileCode && dataset.vidplyLanguageFileUrl) {\n options.languageFile = dataset.vidplyLanguageFileCode;\n options.languageFileUrl = dataset.vidplyLanguageFileUrl;\n }\n }\n } else if (dataset.vidplyLanguageFileCode && dataset.vidplyLanguageFileUrl) {\n options.languageFile = dataset.vidplyLanguageFileCode;\n options.languageFileUrl = dataset.vidplyLanguageFileUrl;\n }\n\n return options as Partial<PlayerOptions>;\n}\n\nif (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', initializePlayers);\n} else {\n initializePlayers();\n}\n\nexport { Player, PlaylistManager };\nexport type { PlayerOptions } from './types/options.js';\nexport type {\n PlayerEventMap,\n PlaylistTrack,\n FloatingChangeDetail\n} from './types/events.js';\nexport type { PlayerState } from './types/state.js';\nexport type { Renderer, QualityLevel } from './types/renderer.js';\nexport default Player;\n"],
5
- "mappings": "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",
3
+ "sources": ["../../src/utils/EventEmitter.ts", "../../src/utils/VideoFrameCapture.ts", "../../src/utils/DownloadInfo.ts", "../../src/controls/ControlBar.ts", "../../src/controls/KeyboardManager.ts", "../../src/core/MediaSessionManager.ts", "../../src/utils/UrlSafe.ts", "../../src/core/LazyInit.ts", "../../src/core/PseudoFullscreen.ts", "../../src/core/ThemeManager.ts", "../../src/core/PosterManager.ts", "../../src/core/ResumeManager.ts", "../../src/core/ResponsiveManager.ts", "../../src/core/MetadataAlertsManager.ts", "../../src/controls/KeyboardHelp.ts", "../../src/core/Player.ts", "../../src/features/PlaylistManager.ts", "../../src/index.ts"],
4
+ "sourcesContent": ["type DefaultEventMap = Record<string, unknown>;\ntype Listener<T> = T extends void ? () => void : (data: T) => void;\n\n/**\n * Generic typed event emitter. The `TEvents` parameter is constrained\n * via the self-referential `Record<keyof TEvents, unknown>` so concrete\n * event maps (like `PlayerEventMap`) can be used directly without\n * adding a noisy `[key: string]: unknown` index signature, which would\n * defeat the typo-protection that strongly typed event maps provide.\n */\nexport class EventEmitter<TEvents extends Record<keyof TEvents, unknown> = DefaultEventMap> {\n private events: Partial<Record<keyof TEvents, Array<(...args: unknown[]) => void>>> = {};\n\n on<K extends keyof TEvents>(event: K, listener: Listener<TEvents[K]>): this {\n const listeners = this.events[event] ?? [];\n listeners.push(listener as (...args: unknown[]) => void);\n this.events[event] = listeners;\n return this;\n }\n\n once<K extends keyof TEvents>(event: K, listener: Listener<TEvents[K]>): this {\n const onceListener = ((...args: unknown[]) => {\n (listener as (...a: unknown[]) => void)(...args);\n this.off(event, onceListener as Listener<TEvents[K]>);\n }) as unknown as Listener<TEvents[K]>;\n return this.on(event, onceListener);\n }\n\n off<K extends keyof TEvents>(event: K, listener?: Listener<TEvents[K]>): this {\n const listeners = this.events[event];\n if (!listeners) return this;\n\n if (!listener) {\n delete this.events[event];\n } else {\n this.events[event] = listeners.filter(\n l => l !== (listener as unknown)\n );\n }\n\n return this;\n }\n\n emit<K extends keyof TEvents>(event: K, ...args: unknown[]): this {\n const listeners = this.events[event];\n if (!listeners) return this;\n\n listeners.forEach(listener => {\n listener(...args);\n });\n\n return this;\n }\n\n removeAllListeners(): this {\n this.events = {};\n return this;\n }\n}\n", "export interface CaptureFrameOptions {\n restoreState?: boolean;\n quality?: number;\n maxWidth?: number;\n maxHeight?: number;\n}\n\nexport async function captureVideoFrame(\n video: HTMLVideoElement | null,\n time: number,\n options: CaptureFrameOptions = {}\n): Promise<string | null> {\n if (!video || video.tagName !== 'VIDEO') {\n return null;\n }\n\n const { restoreState = true, quality = 0.9, maxWidth, maxHeight } = options;\n\n const wasPlaying = !video.paused;\n const originalTime = video.currentTime;\n const originalMuted = video.muted;\n\n if (restoreState) {\n video.muted = true;\n }\n\n return new Promise<string | null>((resolve) => {\n const captureFrame = () => {\n try {\n let width = video.videoWidth || 640;\n let height = video.videoHeight || 360;\n\n if (maxWidth && width > maxWidth) {\n const ratio = maxWidth / width;\n width = maxWidth;\n height = Math.round(height * ratio);\n }\n if (maxHeight && height > maxHeight) {\n const ratio = maxHeight / height;\n height = maxHeight;\n width = Math.round(width * ratio);\n }\n\n const canvas = document.createElement('canvas');\n canvas.width = width;\n canvas.height = height;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n resolve(null);\n return;\n }\n ctx.drawImage(video, 0, 0, width, height);\n\n const dataURL = canvas.toDataURL('image/jpeg', quality);\n\n if (restoreState) {\n video.currentTime = originalTime;\n video.muted = originalMuted;\n if (wasPlaying && !video.paused) {\n // Autoplay can be rejected by browser policy. We log at debug\n // level so the noise doesn't reach production consoles, but\n // unexpected errors are still observable.\n video.play().catch((e: unknown) => {\n if (typeof console !== 'undefined' && console.debug) {\n console.debug('[VidPly] preview play() rejected:', e);\n }\n });\n }\n }\n\n resolve(dataURL);\n } catch (e) {\n if (typeof console !== 'undefined' && console.debug) {\n console.debug('[VidPly] frame capture failed:', e);\n }\n if (restoreState) {\n video.currentTime = originalTime;\n video.muted = originalMuted;\n if (wasPlaying && !video.paused) {\n video.play().catch((err: unknown) => {\n if (typeof console !== 'undefined' && console.debug) {\n console.debug('[VidPly] preview play() rejected:', err);\n }\n });\n }\n }\n resolve(null);\n }\n };\n\n const onSeeked = () => {\n video.removeEventListener('seeked', onSeeked);\n requestAnimationFrame(() => {\n requestAnimationFrame(captureFrame);\n });\n };\n\n const timeDiff = Math.abs(video.currentTime - time);\n if (timeDiff < 0.1 && video.readyState >= 2) {\n captureFrame();\n } else if (video.readyState >= 1) {\n video.addEventListener('seeked', onSeeked);\n video.currentTime = time;\n } else {\n const onLoadedMetadata = () => {\n video.removeEventListener('loadedmetadata', onLoadedMetadata);\n video.addEventListener('seeked', onSeeked);\n video.currentTime = time;\n };\n video.addEventListener('loadedmetadata', onLoadedMetadata);\n }\n });\n}\n", "/**\n * Download metadata helpers\n *\n * Used by the download button to resolve a human-readable file format\n * (e.g. MP4, MP3, WebM) and size string (e.g. 12.4 MB) for the button's\n * aria-label and tooltip.\n */\n\nconst MIME_TO_FORMAT: Record<string, string> = {\n 'video/mp4': 'MP4',\n 'video/webm': 'WebM',\n 'video/ogg': 'Ogg',\n 'video/quicktime': 'MOV',\n 'video/x-matroska': 'MKV',\n 'video/x-msvideo': 'AVI',\n 'audio/mpeg': 'MP3',\n 'audio/mp3': 'MP3',\n 'audio/mp4': 'M4A',\n 'audio/x-m4a': 'M4A',\n 'audio/aac': 'AAC',\n 'audio/ogg': 'Ogg',\n 'audio/opus': 'Opus',\n 'audio/wav': 'WAV',\n 'audio/x-wav': 'WAV',\n 'audio/wave': 'WAV',\n 'audio/flac': 'FLAC',\n 'audio/x-flac': 'FLAC',\n 'audio/webm': 'WebM'\n};\n\nconst EXT_TO_FORMAT: Record<string, string> = {\n mp4: 'MP4',\n m4v: 'MP4',\n mov: 'MOV',\n webm: 'WebM',\n mkv: 'MKV',\n avi: 'AVI',\n ogv: 'Ogg',\n ogg: 'Ogg',\n oga: 'Ogg',\n mp3: 'MP3',\n m4a: 'M4A',\n aac: 'AAC',\n opus: 'Opus',\n wav: 'WAV',\n flac: 'FLAC'\n};\n\nexport function inferFormatFromMime(mime: string | null | undefined): string | null {\n if (!mime) return null;\n const trimmed = (mime.split(';')[0] ?? '').trim().toLowerCase();\n return MIME_TO_FORMAT[trimmed] || null;\n}\n\nexport function inferFormatFromUrl(url: string | null | undefined): string | null {\n if (!url) return null;\n try {\n const cleaned = url.split('?')[0]?.split('#')[0] ?? '';\n const lastSegment = cleaned.split('/').pop() || '';\n const dotIndex = lastSegment.lastIndexOf('.');\n if (dotIndex < 0 || dotIndex === lastSegment.length - 1) return null;\n const ext = lastSegment.slice(dotIndex + 1).toLowerCase();\n return EXT_TO_FORMAT[ext] || null;\n } catch {\n return null;\n }\n}\n\nexport function formatBytes(bytes: number, locale: string = 'en'): string | null {\n if (!isFinite(bytes) || bytes < 0) return null;\n\n const units = ['B', 'KB', 'MB', 'GB', 'TB'];\n let value = bytes;\n let unitIndex = 0;\n while (value >= 1024 && unitIndex < units.length - 1) {\n value /= 1024;\n unitIndex++;\n }\n\n // Bytes/KB get no decimals; MB and up get one decimal.\n const fractionDigits = unitIndex < 2 ? 0 : 1;\n\n let formatted: string;\n try {\n formatted = new Intl.NumberFormat(locale, {\n minimumFractionDigits: fractionDigits,\n maximumFractionDigits: fractionDigits\n }).format(value);\n } catch {\n formatted = value.toFixed(fractionDigits);\n }\n\n return `${formatted} ${units[unitIndex]}`;\n}\n\nexport interface FetchContentLengthOptions {\n signal?: AbortSignal;\n timeoutMs?: number;\n}\n\nexport async function fetchContentLength(\n url: string,\n options: FetchContentLengthOptions = {}\n): Promise<number | null> {\n if (!url || typeof fetch !== 'function') return null;\n\n // Compose abort signals: caller's lifecycle signal + a per-call timeout.\n const signals: AbortSignal[] = [];\n if (options.signal) signals.push(options.signal);\n if (typeof AbortSignal !== 'undefined' && typeof AbortSignal.timeout === 'function') {\n signals.push(AbortSignal.timeout(options.timeoutMs ?? 8000));\n }\n let combinedSignal: AbortSignal | undefined;\n if (signals.length === 1) combinedSignal = signals[0];\n else if (signals.length > 1) {\n const anyFn = (AbortSignal as { any?: (s: AbortSignal[]) => AbortSignal }).any;\n combinedSignal = anyFn ? anyFn(signals) : signals[0];\n }\n\n try {\n const response = await fetch(url, {\n method: 'HEAD',\n credentials: 'omit',\n cache: 'no-store',\n signal: combinedSignal\n });\n if (!response.ok) return null;\n const header = response.headers.get('Content-Length');\n if (!header) return null;\n const size = Number(header);\n return Number.isFinite(size) && size > 0 ? size : null;\n } catch (error) {\n // CORS, network errors, abort, or HEAD not allowed: silently give up.\n if (typeof console !== 'undefined' && console.debug) {\n console.debug('[vidply] HEAD request for download size failed:', error);\n }\n return null;\n }\n}\n\nexport interface DownloadLabelParts {\n baseLabel: string;\n format?: string | null;\n sizeBytes?: number | null;\n locale?: string;\n withFormatSizeTemplate: string;\n withFormatTemplate: string;\n withSizeTemplate: string;\n}\n\n/**\n * Compose the localized download label.\n * Templates are i18n strings already containing {format} / {size} placeholders.\n */\nexport function buildDownloadLabel(parts: DownloadLabelParts): string {\n const { baseLabel, format, sizeBytes, locale = 'en' } = parts;\n const sizeStr = sizeBytes != null ? formatBytes(sizeBytes, locale) : null;\n\n if (format && sizeStr) {\n return parts.withFormatSizeTemplate\n .replace('{format}', format)\n .replace('{size}', sizeStr);\n }\n if (format) {\n return parts.withFormatTemplate.replace('{format}', format);\n }\n if (sizeStr) {\n return parts.withSizeTemplate.replace('{size}', sizeStr);\n }\n return baseLabel;\n}\n", "/**\n * Control Bar Component\n */\n\nimport {DOMUtils} from '../utils/DOMUtils.js';\nimport {TimeUtils} from '../utils/TimeUtils.js';\nimport {createIconElement} from '../icons/Icons.js';\nimport {i18n} from '../i18n/i18n.js';\nimport {focusElement} from '../utils/FocusUtils.js';\nimport {isMobile, reducedMotionScrollOptions} from '../utils/PerformanceUtils.js';\nimport {captureVideoFrame} from '../utils/VideoFrameCapture.js';\nimport {\n buildDownloadLabel,\n fetchContentLength,\n inferFormatFromMime,\n inferFormatFromUrl\n} from '../utils/DownloadInfo.js';\nimport type { Player } from '../core/Player.js';\nimport type { CaptionManager } from './CaptionManager.js';\nimport type { QualityLevel } from '../types/renderer.js';\n\ntype TimerHandle = ReturnType<typeof setTimeout>;\n\n/**\n * Stores per-button event handlers added by `attachMenuCloseHandler` so they\n * can be removed when a different menu opens. Using a WeakMap keeps the\n * tagging out of the DOM element itself (which both removes the previous\n * `(button as any)._vidplyXHandler` casts and lets the GC reclaim the entry\n * automatically once the button is detached).\n */\ntype MenuButtonHandlers = {\n blur?: (e: FocusEvent) => void;\n mousedown?: () => void;\n};\nconst menuButtonHandlers: WeakMap<HTMLElement, MenuButtonHandlers> = new WeakMap();\n\nfunction getMenuButtonHandlers(button: HTMLElement): MenuButtonHandlers {\n let entry = menuButtonHandlers.get(button);\n if (!entry) {\n entry = {};\n menuButtonHandlers.set(button, entry);\n }\n return entry;\n}\n\nexport class ControlBar {\n player: Player;\n _overflowMenuItemRef: HTMLElement | null = null;\n controls: Record<string, HTMLElement>;\n currentPreviewTime: number | null;\n element!: HTMLElement;\n hideTimeout: TimerHandle | undefined;\n isDraggingProgress: boolean;\n isDraggingVolume: boolean;\n openMenu: HTMLElement | null;\n openMenuButton: HTMLElement | null;\n overflowResizeObserver: ResizeObserver | null = null;\n previewSupported: boolean = false;\n previewThumbnailCache: Map<number, string> = new Map();\n previewThumbnailTimeout: TimerHandle | null = null;\n previewUsingMainVideo: boolean = false;\n previewVideo: HTMLVideoElement | null = null;\n previewVideoInitialized: boolean = false;\n previewVideoReady: boolean = false;\n rightButtons!: HTMLElement;\n overflowMenuButton: HTMLElement | null = null;\n /** Track of the currently open volume slider so a single pair of\n * document listeners (installed once in {@link init}) can update the\n * right element while dragging without being re-registered per open. */\n _activeVolumeTrack: HTMLElement | null = null;\n /** Track of the currently rendered progress bar so document-level\n * mousemove/mouseup handlers installed once in {@link init} can resolve\n * the geometry without re-registering per drag. */\n _progressBarRect: DOMRect | null = null;\n\n constructor(player: Player) {\n this.player = player;\n this.controls = {};\n this.hideTimeout = undefined;\n this.isDraggingProgress = false;\n this.isDraggingVolume = false;\n this.currentPreviewTime = null;\n this.openMenu = null; // Track currently open menu\n this.openMenuButton = null; // Track button that opened the menu\n\n this.init();\n }\n\n init() {\n this.createElement();\n this.createControls();\n // Ensure time UI reflects any prefilled state (e.g. initialDuration)\n // even when media metadata is deferred and 'loadedmetadata' won't fire yet.\n this.updateDuration();\n this.updateProgress();\n this.attachEvents();\n this.setupAutoHide();\n this.setupOverflowDetection();\n this.setupGlobalDragListeners();\n }\n\n /**\n * Install a single pair of document-level mousemove/mouseup handlers\n * that both the progress bar drag and the volume slider drag reuse.\n *\n * This replaces the previous pattern where {@link showVolumeSlider}\n * and {@link setupProgressBarEvents} each attached their own\n * `document.addEventListener` calls on every call — the volume variant\n * in particular accumulated two extra document listeners on every menu\n * open for the life of the page. All listeners here are tied to the\n * Player's lifecycle AbortController so `destroy()` removes them.\n */\n setupGlobalDragListeners() {\n const signal = this.player.lifecycleSignal;\n\n document.addEventListener('mousemove', (e: MouseEvent) => {\n if (this.isDraggingProgress && this._progressBarRect) {\n const rect = this._progressBarRect;\n const percent = rect.width > 0\n ? Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width))\n : 0;\n const duration = this.player.state.duration || 0;\n this.player.seek(percent * duration);\n return;\n }\n if (this.isDraggingVolume && this._activeVolumeTrack) {\n const rect = this._activeVolumeTrack.getBoundingClientRect();\n const percent = Math.max(0, Math.min(1, 1 - ((e.clientY - rect.top) / rect.height)));\n this.player.setVolume(percent);\n }\n }, { signal });\n\n document.addEventListener('mouseup', () => {\n this.isDraggingProgress = false;\n this._progressBarRect = null;\n this.isDraggingVolume = false;\n this._activeVolumeTrack = null;\n }, { signal });\n }\n\n // Helper method to detect touch devices\n isTouchDevice() {\n return (\n ('ontouchstart' in window) ||\n (navigator.maxTouchPoints > 0) ||\n ((navigator.msMaxTouchPoints ?? 0) > 0)\n );\n }\n\n // Smart menu positioning to avoid overflow\n positionMenu(menu: HTMLElement, button: HTMLElement, immediate = false) {\n const mobile = isMobile();\n const isOverflowMenu = menu.classList.contains(`${this.player.options.classPrefix}-overflow-menu-list`);\n const isFullscreen = this.player.state.fullscreen;\n \n // In fullscreen, menu is appended to player container, so use fixed positioning\n if (isFullscreen && menu.parentElement === this.player.container) {\n const doFullscreenPositioning = () => {\n const buttonRect = button.getBoundingClientRect();\n const menuRect = menu.getBoundingClientRect();\n const containerRect = this.player.container.getBoundingClientRect();\n \n // Position relative to player container\n const buttonCenterX = buttonRect.left + buttonRect.width / 2 - containerRect.left;\n const buttonTop = buttonRect.top - containerRect.top;\n const buttonBottom = buttonRect.bottom - containerRect.top;\n \n const spaceAbove = buttonRect.top - containerRect.top;\n const spaceBelow = containerRect.bottom - buttonRect.bottom;\n \n // Position menu above button by default\n if (spaceAbove >= menuRect.height + 20 || spaceAbove > spaceBelow) {\n menu.style.bottom = `${containerRect.height - buttonTop + 8}px`;\n menu.style.top = 'auto';\n menu.classList.remove('vidply-menu-below');\n } else {\n // Position below if more space there\n menu.style.top = `${buttonBottom + 8}px`;\n menu.style.bottom = 'auto';\n menu.classList.add('vidply-menu-below');\n }\n \n // Horizontal positioning\n if (isOverflowMenu) {\n // Overflow menu aligns to right\n const buttonRight = buttonRect.right - containerRect.left;\n menu.style.right = `${containerRect.width - buttonRight}px`;\n menu.style.left = 'auto';\n menu.style.transform = 'none';\n } else {\n // Other menus center on button\n menu.style.left = `${buttonCenterX}px`;\n menu.style.right = 'auto';\n menu.style.transform = 'translateX(-50%)';\n }\n };\n \n if (immediate) {\n doFullscreenPositioning();\n } else {\n requestAnimationFrame(doFullscreenPositioning);\n }\n return;\n }\n \n if (mobile) {\n // On mobile, ensure menus stay within viewport\n const isVolumeMenu = menu.classList.contains(`${this.player.options.classPrefix}-volume-menu`);\n \n const doMobilePositioning = () => {\n // Get the button's parent container (controls-left or controls-right)\n const parentContainer = button.parentElement;\n if (!parentContainer) return;\n \n const buttonRect = button.getBoundingClientRect();\n const parentRect = parentContainer.getBoundingClientRect();\n const menuRect = menu.getBoundingClientRect();\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n \n // Volume menu should be centered on its button\n if (isVolumeMenu) {\n // Calculate button position relative to parent\n const buttonCenterX = buttonRect.left + buttonRect.width / 2 - parentRect.left;\n \n // Center menu on button\n menu.style.left = `${buttonCenterX}px`;\n menu.style.right = 'auto';\n menu.style.transform = 'translateX(-50%)';\n return;\n }\n \n // Check if menu overflows viewport\n if (menuRect.right > viewportWidth) {\n menu.style.left = 'auto';\n menu.style.right = '10px';\n menu.style.transform = 'none';\n }\n \n if (menuRect.left < 0) {\n menu.style.left = '10px';\n menu.style.right = 'auto';\n menu.style.transform = 'none';\n }\n \n // Ensure menu doesn't go off top or bottom\n if (menuRect.top < 10) {\n menu.style.top = '10px';\n // Important: clear bottom constraint, otherwise top+bottom will squeeze the menu height\n // into a tiny strip on some mobile layouts.\n menu.style.bottom = 'auto';\n }\n \n if (menuRect.bottom > viewportHeight - 10) {\n menu.style.bottom = '10px';\n menu.style.top = 'auto';\n }\n };\n \n if (immediate) {\n doMobilePositioning();\n } else {\n requestAnimationFrame(doMobilePositioning);\n }\n return;\n }\n\n // Desktop: Smart positioning\n // Menu is now a sibling of the button, within controls-left or controls-right container\n // These containers have position: relative, so menus position relative to them\n \n const doPositioning = () => {\n const buttonRect = button.getBoundingClientRect();\n const menuRect = menu.getBoundingClientRect();\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n \n // Get the button's parent container (controls-left or controls-right)\n const parentContainer = button.parentElement;\n if (!parentContainer) return;\n \n const parentRect = parentContainer.getBoundingClientRect();\n \n // Calculate position relative to parent container\n const buttonCenterX = buttonRect.left + buttonRect.width / 2 - parentRect.left;\n const buttonBottom = buttonRect.bottom - parentRect.top;\n const buttonTop = buttonRect.top - parentRect.top;\n \n const spaceAbove = buttonRect.top;\n const spaceBelow = viewportHeight - buttonRect.bottom;\n \n // Position menu above button by default\n let menuTop: number | null = buttonTop - menuRect.height - 8;\n let menuBottom: number | null = null;\n \n // Prefer above, but switch to below if not enough space\n if (spaceAbove < menuRect.height + 20 && spaceBelow > spaceAbove) {\n menuTop = null;\n // Calculate bottom position relative to parent container\n // bottom: X means X pixels from the bottom of the positioned parent\n // We want the menu to be 8px below the button\n const parentHeight = parentRect.bottom - parentRect.top;\n menuBottom = parentHeight - buttonBottom + 8;\n menu.classList.add('vidply-menu-below');\n } else {\n menu.classList.remove('vidply-menu-below');\n }\n \n let menuLeft: number | string = 'auto';\n let menuRight: number | string = 'auto';\n let transformX = 'translateX(0)';\n \n // For overflow menu, always align to the right edge\n if (isOverflowMenu) {\n menuLeft = 'auto';\n menuRight = 0;\n transformX = 'translateX(0)';\n } else {\n // For other menus, center on button by default\n menuLeft = buttonCenterX - menuRect.width / 2;\n \n // Check horizontal overflow\n const menuLeftAbsolute = buttonRect.left + buttonRect.width / 2 - menuRect.width / 2;\n if (menuLeftAbsolute < 10) {\n // Too far left, align to left edge of parent\n menuLeft = 0;\n transformX = 'translateX(0)';\n } else if (menuLeftAbsolute + menuRect.width > viewportWidth - 10) {\n // Too far right, align to right edge of parent\n menuLeft = 'auto';\n menuRight = 0;\n transformX = 'translateX(0)';\n } else {\n // Center on button\n menuLeft = buttonCenterX;\n transformX = 'translateX(-50%)';\n }\n }\n \n // Apply calculated positions\n if (menuTop !== null) {\n menu.style.top = `${menuTop}px`;\n menu.style.bottom = 'auto';\n } else if (menuBottom !== null) {\n menu.style.top = 'auto';\n menu.style.bottom = `${menuBottom}px`;\n }\n \n if (menuLeft !== 'auto') {\n menu.style.left = `${menuLeft}px`;\n menu.style.right = 'auto';\n } else {\n menu.style.left = 'auto';\n menu.style.right = `${menuRight}px`;\n }\n \n menu.style.transform = transformX;\n };\n \n if (immediate) {\n // Position immediately (synchronously) - used when menu is first shown\n doPositioning();\n } else {\n // Use requestAnimationFrame to ensure layout is stable before positioning\n requestAnimationFrame(() => {\n setTimeout(doPositioning, 10); // Small delay to ensure layout is stable\n });\n }\n }\n\n // Helper method to insert menu into DOM (handles fullscreen vs normal mode)\n insertMenuIntoDOM(menu: HTMLElement, button: HTMLElement) {\n // Ensure menu has an ID for aria-controls relationship\n if (!menu.id) {\n menu.id = `vidply-menu-${Math.random().toString(36).substr(2, 9)}`;\n }\n \n // Set ARIA attributes for screen reader users (WCAG 4.1.2)\n button.setAttribute('aria-controls', menu.id);\n button.setAttribute('aria-haspopup', 'true');\n \n // In fullscreen, append menu to player container to escape video-wrapper stacking context\n // This allows menus to appear above the playlist panel\n // Otherwise, keep it after the button for WCAG compliance\n const isFullscreen = this.player.state.fullscreen;\n if (isFullscreen) {\n // Append to player container as sibling to playlist panel\n this.player.container.appendChild(menu);\n // Store reference to button for positioning\n menu.dataset.triggerButton = button.getAttribute('aria-label') || 'button';\n } else {\n // Insert menu right after the button in the DOM for keyboard navigation\n button.insertAdjacentElement('afterend', menu);\n }\n }\n\n // Helper method to attach close-on-outside-click behavior to menus\n attachMenuCloseHandler(menu: HTMLElement, button: HTMLElement, preventCloseOnInteraction = false) {\n // Close any previously open menu and clean up its handlers\n if (this.openMenu && this.openMenu !== menu && this.openMenuButton) {\n // Remove previously-attached handlers for this button if any.\n const previousHandlers = menuButtonHandlers.get(this.openMenuButton);\n if (previousHandlers?.blur) {\n this.openMenuButton.removeEventListener('blur', previousHandlers.blur);\n delete previousHandlers.blur;\n }\n if (previousHandlers?.mousedown) {\n this.openMenuButton.removeEventListener('mousedown', previousHandlers.mousedown);\n delete previousHandlers.mousedown;\n }\n \n // Close previous menu without returning focus\n if (this.openMenu && document.contains(this.openMenu)) {\n this.openMenu.remove();\n } else if (this.openMenu && this.openMenu.parentNode) {\n this.openMenu.parentNode.removeChild(this.openMenu);\n }\n if (this.openMenuButton) {\n this.openMenuButton.setAttribute('aria-expanded', 'false');\n }\n }\n \n // Track this menu as open\n this.openMenu = menu;\n this.openMenuButton = button;\n \n // Position menu smartly\n this.positionMenu(menu, button);\n \n // Set aria-expanded to true when menu opens\n if (button) {\n button.setAttribute('aria-expanded', 'true');\n }\n \n // Add focus handler to button to close menu when focus leaves to another control bar button\n // But only if it's a keyboard navigation (Tab), not a click\n let isClickingButton = false;\n let blurHandlerActive = true;\n \n const handleButtonMousedown = () => {\n isClickingButton = true;\n // Temporarily disable blur handler when clicking\n blurHandlerActive = false;\n setTimeout(() => {\n isClickingButton = false;\n blurHandlerActive = true;\n }, 200);\n };\n button.addEventListener('mousedown', handleButtonMousedown);\n getMenuButtonHandlers(button).mousedown = handleButtonMousedown;\n \n const handleButtonBlur = (e: FocusEvent) => {\n // If blur handler is disabled (during click) or this is a click, don't close the menu\n if (!blurHandlerActive || isClickingButton) {\n return;\n }\n \n // Check if this menu is still the open menu (might have been replaced)\n if (this.openMenu !== menu) {\n return;\n }\n \n // Store the related target (where focus is going) before async operations\n const relatedTarget = e.relatedTarget;\n \n // Use requestAnimationFrame to check where focus went after browser handles focus\n requestAnimationFrame(() => {\n setTimeout(() => {\n // Check again if blur handler is still active and menu is still open\n if (!blurHandlerActive || this.openMenu !== menu) {\n return;\n }\n \n const activeElement = document.activeElement;\n \n // Don't close if focus is still within the menu\n if (menu.contains(activeElement)) {\n return;\n }\n \n // Don't close if focus moved to sign language or transcript windows\n const signLanguageWrapper = this.player.signLanguageWrapper;\n const transcriptWindow = this.player.transcriptManager?.transcriptWindow;\n if ((signLanguageWrapper && signLanguageWrapper.contains(activeElement)) ||\n (transcriptWindow && transcriptWindow.contains(activeElement))) {\n return;\n }\n \n // If focus moved to another button in the control bar, close the menu\n const controlBarButtons = Array.from(this.element.querySelectorAll('button')) as Element[];\n const isFocusOnAnotherButton = activeElement !== null\n && controlBarButtons.includes(activeElement)\n && activeElement !== button;\n\n // Also check relatedTarget in case activeElement hasn't updated yet\n const isRelatedTargetAnotherButton = relatedTarget !== null\n && relatedTarget instanceof Element\n && controlBarButtons.includes(relatedTarget)\n && relatedTarget !== button;\n \n // Close menu if focus moved to another control bar button (without returning focus)\n if (isFocusOnAnotherButton || isRelatedTargetAnotherButton) {\n // Double-check this menu is still the open menu\n if (this.openMenu !== menu) {\n return;\n }\n \n // Just remove the menu, don't touch focus at all\n if (menu && document.contains(menu)) {\n menu.remove();\n } else if (menu && menu.parentNode) {\n menu.parentNode.removeChild(menu);\n }\n if (button) {\n button.setAttribute('aria-expanded', 'false');\n }\n // Clear tracking\n if (this.openMenu === menu) {\n this.openMenu = null;\n this.openMenuButton = null;\n }\n button.removeEventListener('blur', handleButtonBlur);\n button.removeEventListener('mousedown', handleButtonMousedown);\n const handlers = menuButtonHandlers.get(button);\n if (handlers) {\n delete handlers.blur;\n delete handlers.mousedown;\n }\n }\n }, 10); // Small delay to ensure focus has fully moved\n });\n };\n button.addEventListener('blur', handleButtonBlur);\n getMenuButtonHandlers(button).blur = handleButtonBlur;\n \n const closeMenuAndUpdateAria = () => {\n this.closeMenuAndReturnFocus(menu, button);\n };\n \n // Store document handlers for cleanup\n let documentClickHandler: ((e: MouseEvent) => void) | null = null;\n let documentEscapeHandler: ((e: KeyboardEvent) => void) | null = null;\n \n setTimeout(() => {\n documentClickHandler = (e: MouseEvent) => {\n // If this menu has form controls, don't close when clicking inside\n if (preventCloseOnInteraction && menu.contains(e.target as Node)) {\n return;\n }\n\n // Check if click is outside menu and button\n // Also check if this menu is still the open menu\n if (this.openMenu === menu && !menu.contains(e.target as Node) && !button.contains(e.target as Node)) {\n closeMenuAndUpdateAria();\n if (documentClickHandler) {\n document.removeEventListener('click', documentClickHandler);\n }\n if (documentEscapeHandler) {\n document.removeEventListener('keydown', documentEscapeHandler);\n }\n }\n };\n\n documentEscapeHandler = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && this.openMenu === menu) {\n e.preventDefault();\n e.stopPropagation();\n // Close menu and return focus to the button that opened it\n this.closeMenuAndReturnFocus(menu, button, true);\n if (documentClickHandler) {\n document.removeEventListener('click', documentClickHandler);\n }\n if (documentEscapeHandler) {\n document.removeEventListener('keydown', documentEscapeHandler);\n }\n }\n };\n\n const signal = this.player.lifecycleSignal;\n document.addEventListener('click', documentClickHandler, { signal });\n document.addEventListener('keydown', documentEscapeHandler, { signal });\n }, 100);\n }\n\n // Helper method to close menu and return focus to button\n closeMenuAndReturnFocus(menu: HTMLElement | null, button: HTMLElement | null, returnFocus = true) {\n if (menu) {\n // Remove menu from DOM - use remove() which works reliably\n if (document.contains(menu)) {\n menu.remove();\n } else if (menu.parentNode) {\n // Fallback if menu is not in document but has parent\n menu.parentNode.removeChild(menu);\n }\n }\n if (button) {\n button.setAttribute('aria-expanded', 'false');\n // Clean up aria-controls since menu is removed (optional, but cleaner)\n // Note: Keeping it doesn't cause issues, but removing it is semantically correct\n // when the referenced element no longer exists\n if (menu && menu.id) {\n button.removeAttribute('aria-controls');\n }\n // Only return focus if explicitly requested (not when tabbing away)\n if (returnFocus) {\n // Use requestAnimationFrame to ensure DOM updates are complete before focusing\n // This prevents focus from jumping to next/previous button\n requestAnimationFrame(() => {\n setTimeout(() => {\n if (button && document.contains(button)) {\n button.focus({ preventScroll: true });\n }\n }, 0);\n });\n }\n }\n // Clear tracking\n if (this.openMenu === menu) {\n this.openMenu = null;\n this.openMenuButton = null;\n }\n }\n \n // Close any open menu when tabbing to another button or clicking another button\n closeOpenMenu() {\n if (this.openMenu && this.openMenuButton) {\n // Close without returning focus (user is tabbing to next element or clicking another button)\n if (this.openMenu && document.contains(this.openMenu)) {\n this.openMenu.remove();\n } else if (this.openMenu && this.openMenu.parentNode) {\n this.openMenu.parentNode.removeChild(this.openMenu);\n }\n if (this.openMenuButton) {\n this.openMenuButton.setAttribute('aria-expanded', 'false');\n }\n // Clear tracking\n this.openMenu = null;\n this.openMenuButton = null;\n }\n }\n\n // Helper method to add keyboard navigation to menus (arrow keys)\n attachMenuKeyboardNavigation(menu: HTMLElement, button: HTMLElement) {\n // Exclude aria-disabled empty-state entries so arrow keys never land\n // on a non-operable item (WCAG 2.1.1, 4.1.2).\n const menuItems: HTMLElement[] = Array.from(\n menu.querySelectorAll<HTMLElement>(`.${this.player.options.classPrefix}-menu-item`)\n ).filter((item) => item.getAttribute('aria-disabled') !== 'true');\n \n if (menuItems.length === 0) return;\n\n const handleKeyDown = (e: KeyboardEvent) => {\n const currentIndex = menuItems.indexOf(document.activeElement as HTMLElement);\n \n switch (e.key) {\n case 'ArrowDown': {\n e.preventDefault();\n e.stopPropagation(); // Prevent volume/seek actions\n const nextIndex = (currentIndex + 1) % menuItems.length;\n const nextItem = menuItems[nextIndex];\n if (nextItem) {\n nextItem.focus({ preventScroll: false });\n nextItem.scrollIntoView(reducedMotionScrollOptions('nearest'));\n }\n break;\n }\n \n case 'ArrowUp': {\n e.preventDefault();\n e.stopPropagation(); // Prevent volume/seek actions\n const prevIndex = (currentIndex - 1 + menuItems.length) % menuItems.length;\n const prevItem = menuItems[prevIndex];\n if (prevItem) {\n prevItem.focus({ preventScroll: false });\n prevItem.scrollIntoView(reducedMotionScrollOptions('nearest'));\n }\n break;\n }\n \n case 'ArrowLeft':\n case 'ArrowRight':\n // Prevent seeking when in menu (but allow menu to handle if needed)\n e.preventDefault();\n e.stopPropagation();\n break;\n \n case 'Home': {\n e.preventDefault();\n e.stopPropagation();\n const homeItem = menuItems[0];\n if (homeItem) {\n homeItem.focus({ preventScroll: false });\n homeItem.scrollIntoView(reducedMotionScrollOptions('nearest'));\n }\n break;\n }\n \n case 'End': {\n e.preventDefault();\n e.stopPropagation();\n const endItem = menuItems[menuItems.length - 1];\n if (endItem) {\n endItem.focus({ preventScroll: false });\n endItem.scrollIntoView(reducedMotionScrollOptions('nearest'));\n }\n break;\n }\n \n case 'Enter':\n case ' ':\n e.preventDefault();\n e.stopPropagation(); // Prevent event from reaching KeyboardManager\n if (document.activeElement && menuItems.includes(document.activeElement as HTMLElement)) {\n (document.activeElement as HTMLElement).click();\n // Menu will be closed by the click handler, but ensure focus returns\n focusElement(button, { delay: 0 });\n }\n break;\n \n case 'Escape':\n e.preventDefault();\n e.stopPropagation(); // Prevent event from reaching KeyboardManager\n // Close menu and return focus to the button that opened it\n this.closeMenuAndReturnFocus(menu, button, true);\n break;\n }\n };\n\n menu.addEventListener('keydown', handleKeyDown);\n }\n\n createElement() {\n this.element = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-controls`,\n attributes: {\n 'role': 'region',\n 'aria-label': i18n.t('player.label') + ' controls'\n }\n });\n }\n\n createControls() {\n // Progress bar and time display wrapper\n const progressTimeWrapper = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-progress-time-wrapper`\n });\n\n // Progress bar container\n if (this.player.options.progressBar) {\n this.createProgressBar();\n if (this.controls.progress) {\n progressTimeWrapper.appendChild(this.controls.progress);\n }\n }\n\n // Time display (right beside progress bar)\n if (this.player.options.currentTime || this.player.options.duration) {\n progressTimeWrapper.appendChild(this.createTimeDisplay());\n }\n\n this.element.appendChild(progressTimeWrapper);\n\n // Button container\n const buttonContainer = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-controls-buttons`\n });\n\n // Left buttons\n const leftButtons = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-controls-left`\n });\n\n // Previous track button (if playlist)\n if (this.player.playlistManager) {\n leftButtons.appendChild(this.createPreviousButton());\n }\n\n // Play/Pause button\n if (this.player.options.playPauseButton) {\n leftButtons.appendChild(this.createPlayPauseButton());\n }\n\n // Restart button (right beside play button)\n leftButtons.appendChild(this.createRestartButton());\n\n // Next track button (if playlist)\n if (this.player.playlistManager) {\n leftButtons.appendChild(this.createNextButton());\n }\n\n // Rewind button (not shown in playlist mode)\n if (!this.player.playlistManager) {\n leftButtons.appendChild(this.createRewindButton());\n }\n\n // Forward button (not shown in playlist mode)\n if (!this.player.playlistManager) {\n leftButtons.appendChild(this.createForwardButton());\n }\n\n // Volume control\n if (this.player.options.volumeControl) {\n // On touch devices: simple mute button (hardware buttons control volume)\n // On desktop: full volume control with slider\n if (this.isTouchDevice()) {\n leftButtons.appendChild(this.createMuteButton());\n } else {\n leftButtons.appendChild(this.createVolumeControl());\n }\n }\n\n // Right buttons\n this.rightButtons = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-controls-right`\n });\n\n // Check for available features\n const hasChapters = this.hasChapterTracks();\n const hasCaptions = this.hasCaptionTracks();\n const hasQualityLevels = this.hasQualityLevels();\n const hasAudioDescription = this.hasAudioDescription();\n\n // Priority order (lower number = higher priority, stays visible longer)\n // Desktop (>768px):\n // Priority 1: Play, Volume, Captions, Speed, Fullscreen\n // Priority 2: Audio Description, Quality \n // Priority 3: Chapters, Caption Style, Transcript, Sign Language, PiP\n // Smaller screens (<768px):\n // Priority 1: Play, Volume, Progress (only left controls visible)\n // Priority 3: ALL right-side buttons go to overflow menu\n\n // 1. Chapters button\n if (this.player.options.chaptersButton && hasChapters) {\n const btn = this.createChaptersButton();\n btn.dataset.overflowPriority = '3';\n btn.dataset.overflowPriorityMobile = '3';\n this.rightButtons.appendChild(btn);\n }\n\n // 2. Captions button\n if (this.player.options.captionsButton && hasCaptions) {\n const btn = this.createCaptionsButton();\n btn.dataset.overflowPriority = '1';\n btn.dataset.overflowPriorityMobile = '3';\n this.rightButtons.appendChild(btn);\n }\n\n // 3. Caption styling button\n if (this.player.options.captionStyleButton && hasCaptions) {\n const btn = this.createCaptionStyleButton();\n btn.dataset.overflowPriority = '3';\n btn.dataset.overflowPriorityMobile = '3';\n this.rightButtons.appendChild(btn);\n }\n\n // 4. Playback speed button\n // IMPORTANT: Don't rely on renderer.constructor.name here.\n // In production builds, class names are minified (e.g. \"class s\"), which would break the check.\n // Instead, detect HLS by the current source URL.\n const src = this.player.currentSource\n || this.player.element?.getAttribute?.('src')\n || this.player.element?.currentSrc\n || this.player.element?.src\n || this.player.element?.querySelector?.('source')?.getAttribute?.('src')\n || this.player.element?.querySelector?.('source')?.src\n || '';\n const isHlsSource = typeof src === 'string' && src.includes('.m3u8');\n const isDashSource = typeof src === 'string' && src.includes('.mpd');\n const isVideoElement = this.player.element?.tagName?.toLowerCase() === 'video';\n const hideSpeedForThisPlayer =\n (Boolean(this.player.options.hideSpeedForHls) && isHlsSource)\n || (Boolean(this.player.options.hideSpeedForHlsVideo) && isHlsSource && isVideoElement)\n || (Boolean(this.player.options.hideSpeedForDash) && isDashSource)\n || (Boolean(this.player.options.hideSpeedForDashVideo) && isDashSource && isVideoElement);\n if (this.player.options.speedButton && !hideSpeedForThisPlayer) {\n const btn = this.createSpeedButton();\n btn.dataset.overflowPriority = '1';\n btn.dataset.overflowPriorityMobile = '3';\n this.rightButtons.appendChild(btn);\n }\n\n // 5. Audio Description button\n if (this.player.options.audioDescriptionButton && hasAudioDescription) {\n const btn = this.createAudioDescriptionButton();\n btn.dataset.overflowPriority = '2';\n btn.dataset.overflowPriorityMobile = '3';\n this.rightButtons.appendChild(btn);\n }\n\n // 6. Transcript button\n if (this.player.options.transcriptButton && hasCaptions) {\n const btn = this.createTranscriptButton();\n btn.dataset.overflowPriority = '3';\n btn.dataset.overflowPriorityMobile = '3';\n this.rightButtons.appendChild(btn);\n }\n\n // 6.5 Playlist toggle button (for playlists)\n if (this.player.playlistManager && this.player.options.playlistToggleButton !== false) {\n const btn = this.createPlaylistToggleButton();\n btn.dataset.overflowPriority = '2';\n btn.dataset.overflowPriorityMobile = '3';\n this.rightButtons.appendChild(btn);\n }\n\n // 7. Sign Language buttons (PiP overlay and/or main view src swap)\n const hasSignLanguage = this.hasSignLanguage();\n const showSignLanguageButtons = this.player.options.signLanguageButton !== false && hasSignLanguage;\n const signLanguageDisplayMode = this.player.options.signLanguageDisplayMode || 'both';\n if (showSignLanguageButtons) {\n // PiP overlay button (show if mode is 'pip' or 'both')\n if (['pip', 'both'].includes(signLanguageDisplayMode)) {\n const pipBtn = this.createSignLanguageButton();\n pipBtn.dataset.overflowPriority = '3';\n pipBtn.dataset.overflowPriorityMobile = '3';\n this.rightButtons.appendChild(pipBtn);\n }\n \n // Main view button (show if mode is 'main' or 'both')\n if (['main', 'both'].includes(signLanguageDisplayMode)) {\n const mainViewBtn = this.createSignLanguageInMainViewButton();\n mainViewBtn.dataset.overflowPriority = '3';\n mainViewBtn.dataset.overflowPriorityMobile = '3';\n this.rightButtons.appendChild(mainViewBtn);\n }\n }\n\n // Quality button (before fullscreen)\n if (this.player.options.qualityButton && hasQualityLevels) {\n const btn = this.createQualityButton();\n btn.dataset.overflowPriority = '2';\n btn.dataset.overflowPriorityMobile = '3';\n this.rightButtons.appendChild(btn);\n }\n\n // Download button\n if (this.player.options.downloadButton) {\n const downloadUrl = this.player.options.downloadUrl\n || this.player.element?.dataset?.vidplyDownloadUrl;\n if (downloadUrl) {\n const btn = this.createDownloadButton(downloadUrl);\n btn.dataset.overflowPriority = '2';\n btn.dataset.overflowPriorityMobile = '3';\n this.rightButtons.appendChild(btn);\n }\n }\n\n // PiP button (before fullscreen)\n // When options.floating is enabled, the same button drives our custom\n // in-page floating player; the native PiP capability check is skipped\n // because requestPictureInPicture is not used in that mode.\n const pipEnabled = this.player.options.pipButton &&\n (this.player.options.floating || 'pictureInPictureEnabled' in document);\n const isAudio = this.player.element.tagName.toLowerCase() === 'audio';\n if (pipEnabled && !(this.player.options.floating && isAudio)) {\n const btn = this.createPipButton();\n if (this.player.options.floating) {\n // The floating feature has a hard viewport minimum (default\n // 768px). Below that, the feature is disabled by the\n // FloatingPlayerManager, so the button must neither sit in\n // the main control bar (hidden via CSS media query) nor be\n // shuffled into the overflow menu. Mark it so the overflow\n // detector leaves it alone, regardless of the screen size.\n btn.dataset.skipOverflow = 'true';\n btn.dataset.overflowPriority = '1';\n btn.dataset.overflowPriorityMobile = '1';\n } else {\n btn.dataset.overflowPriority = '3';\n btn.dataset.overflowPriorityMobile = '3';\n }\n this.rightButtons.appendChild(btn);\n }\n\n // Keyboard-shortcuts help button (only meaningful when keyboard\n // shortcuts are active). Low priority: collapses into the overflow\n // menu first on narrow viewports.\n if (this.player.options.helpButton && this.player.options.keyboard) {\n const btn = this.createHelpButton();\n btn.dataset.overflowPriority = '3';\n btn.dataset.overflowPriorityMobile = '3';\n this.rightButtons.appendChild(btn);\n }\n\n // Create overflow menu button (initially hidden). Added before the\n // fullscreen button so that, on mobile viewports where the overflow\n // menu is visible, fullscreen sits immediately to its right as the\n // last control in the bar.\n this.overflowMenuButton = this.createOverflowMenuButton();\n this.overflowMenuButton.style.display = 'none';\n this.rightButtons.appendChild(this.overflowMenuButton);\n\n // 8. Fullscreen button (very last)\n // Don't show fullscreen button for audio players. Fullscreen is a\n // core action users reach for often, so it stays in the main bar\n // on every viewport (priority 1 on desktop AND mobile) rather than\n // being shuffled into the overflow menu.\n const isAudioPlayer = this.player.element.tagName.toLowerCase() === 'audio';\n if (this.player.options.fullscreenButton && !isAudioPlayer) {\n const btn = this.createFullscreenButton();\n btn.dataset.overflowPriority = '1';\n btn.dataset.overflowPriorityMobile = '1';\n this.rightButtons.appendChild(btn);\n }\n\n buttonContainer.appendChild(leftButtons);\n buttonContainer.appendChild(this.rightButtons);\n this.element.appendChild(buttonContainer);\n \n // Ensure all buttons have title attributes\n this.ensureButtonTooltips(buttonContainer);\n }\n \n /**\n * Ensure all buttons in the controls have title attributes\n * Uses aria-label as title if title is not present\n */\n ensureButtonTooltips(container: HTMLElement) {\n const buttons = container.querySelectorAll('button');\n buttons.forEach((button: HTMLButtonElement) => {\n // Skip if tooltip already exists\n if (button.querySelector(`.${this.player.options.classPrefix}-tooltip`)) {\n return;\n }\n \n // Skip if button text already exists\n if (button.querySelector(`.${this.player.options.classPrefix}-button-text`)) {\n return;\n }\n \n // Skip menu items - they already have visible text\n if (button.getAttribute('role') === 'menuitem' || \n button.classList.contains(`${this.player.options.classPrefix}-settings-item`) ||\n button.classList.contains(`${this.player.options.classPrefix}-menu-item`) ||\n button.classList.contains(`${this.player.options.classPrefix}-transcript-settings-item`) ||\n button.classList.contains(`${this.player.options.classPrefix}-sign-language-settings-item`) ||\n button.classList.contains(`${this.player.options.classPrefix}-popup-settings-item`)) {\n return;\n }\n \n const ariaLabel = button.getAttribute('aria-label');\n if (ariaLabel) {\n // Add tooltip (aria-hidden popover)\n DOMUtils.attachTooltip(button, ariaLabel, this.player.options.classPrefix);\n }\n });\n }\n\n // Helper methods to check for available features\n hasChapterTracks() {\n // 1) Prefer already-loaded TextTracks (fast + accurate)\n const textTracks = this.player.element.textTracks;\n for (let i = 0; i < textTracks.length; i++) {\n if (textTracks[i]?.kind === 'chapters') return true;\n }\n\n // 2) Fallback to DOM <track> elements (works before tracks are fully loaded)\n const trackEls = Array.from(this.player.element.querySelectorAll('track[kind=\"chapters\"]'));\n if (trackEls.length > 0) return true;\n\n // 3) Playlist metadata fallback (works even when we intentionally defer loading)\n const current = this.player.playlistManager?.getCurrentTrack?.();\n if (current?.tracks && Array.isArray(current.tracks)) {\n const tracks = current.tracks as Array<{ kind?: string }>;\n return tracks.some(t => t?.kind === 'chapters');\n }\n\n return false;\n }\n\n hasCaptionTracks() {\n // 1) Prefer already-loaded TextTracks (skip stale tracks from previous dash.js)\n const textTracks = this.player.element.textTracks;\n for (let i = 0; i < textTracks.length; i++) {\n const tt = textTracks[i];\n if (tt && (tt.kind === 'captions' || tt.kind === 'subtitles') && !tt._vidplyStale) {\n return true;\n }\n }\n\n // 2) Fallback to DOM <track> elements\n const trackEls: HTMLElement[] = Array.from(this.player.element.querySelectorAll('track'));\n if (trackEls.some(el => el.getAttribute('kind') === 'captions' || el.getAttribute('kind') === 'subtitles')) {\n return true;\n }\n\n // 3) Playlist metadata fallback\n const current = this.player.playlistManager?.getCurrentTrack?.();\n const playlistTracks = (current?.tracks ?? []) as Array<{ kind?: string }>;\n if (playlistTracks.some(t => t?.kind === 'captions' || t?.kind === 'subtitles')) {\n return true;\n }\n\n return false;\n }\n\n hasQualityLevels() {\n // Check if renderer supports quality selection\n if (this.player.renderer && this.player.renderer.getQualities) {\n const qualities = this.player.renderer.getQualities();\n return qualities && qualities.length > 1;\n }\n return false;\n }\n\n hasAudioDescription() {\n // Check for audio-described video source OR description tracks\n if (this.player.audioDescriptionSrc && this.player.audioDescriptionSrc.length > 0) {\n return true;\n }\n \n // Check for description tracks\n const textTracks: TextTrack[] = Array.from(this.player.element.textTracks || []);\n return textTracks.some(track => track.kind === 'descriptions');\n }\n\n hasSignLanguage() {\n // Check for single source or multiple sources\n const hasSingleSource = this.player.signLanguageSrc && this.player.signLanguageSrc.length > 0;\n const hasMultipleSources = this.player.signLanguageSources && Object.keys(this.player.signLanguageSources).length > 0;\n return hasSingleSource || hasMultipleSources;\n }\n\n createProgressBar() {\n const progressContainer = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-progress-container`,\n attributes: {\n 'role': 'slider',\n 'aria-label': i18n.t('player.progress'),\n 'aria-valuemin': '0',\n 'aria-valuemax': '100',\n 'aria-valuenow': '0',\n 'tabindex': '0'\n }\n });\n\n // Buffered progress\n this.controls.buffered = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-progress-buffered`\n });\n\n // Played progress\n this.controls.played = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-progress-played`\n });\n\n // Progress handle\n this.controls.progressHandle = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-progress-handle`\n });\n\n // Tooltip\n this.controls.progressTooltip = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-progress-tooltip`\n });\n\n // Preview thumbnail (for video only)\n this.controls.progressPreview = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-progress-preview`,\n attributes: {\n 'aria-hidden': 'true'\n }\n });\n this.controls.progressTooltip.appendChild(this.controls.progressPreview);\n\n // Time text\n this.controls.progressTooltipTime = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-progress-tooltip-time`\n });\n this.controls.progressTooltip.appendChild(this.controls.progressTooltipTime);\n\n progressContainer.appendChild(this.controls.buffered);\n progressContainer.appendChild(this.controls.played);\n this.controls.played.appendChild(this.controls.progressHandle);\n progressContainer.appendChild(this.controls.progressTooltip);\n\n this.controls.progress = progressContainer;\n\n // Initialize preview functionality\n this.initPreviewThumbnail();\n\n // Progress bar events\n this.setupProgressBarEvents();\n }\n\n /**\n * Initialize preview thumbnail functionality for HTML5 video\n */\n initPreviewThumbnail() {\n this.previewThumbnailCache = new Map();\n this.previewVideo = null;\n this.currentPreviewTime = null;\n this.previewThumbnailTimeout = null;\n this.previewSupported = false;\n this.previewVideoReady = false;\n this.previewVideoInitialized = false;\n this.previewUsingMainVideo = false; // Flag for HLS mode (uses main video instead of separate element)\n\n // Check if preview is supported (HTML5 video or HLS stream)\n // Check if element is a video\n const isVideo = this.player.element && this.player.element.tagName === 'VIDEO';\n \n if (!isVideo) {\n return;\n }\n\n // IMPORTANT: do NOT create/load the preview video until the user has started playback at least once.\n // Otherwise we'd trigger heavy MP4 network traffic just by hovering the progress bar.\n }\n\n /**\n * Lazily create the hidden preview video (only after playback started once)\n * Supports HTML5, HLS, and DASH renderers\n */\n ensurePreviewVideoInitialized() {\n if (this.previewVideoInitialized) return;\n if (!this.player?.state?.hasStartedPlayback) return;\n\n // Check if thumbnail preview is disabled via options\n if (this.player.options.thumbnailPreview === false) {\n this.previewSupported = false;\n this.previewVideoInitialized = true;\n return;\n }\n\n const renderer = this.player.renderer;\n const hasVideoMedia = renderer && renderer.media && renderer.media.tagName === 'VIDEO';\n \n if (!hasVideoMedia) {\n this.previewSupported = false;\n this.previewVideoInitialized = true;\n return;\n }\n\n // Check if this is a streaming renderer (HLS/DASH handle seeking internally).\n // Prefer the renderer's own `isStreaming` flag (stable across dash.js /\n // hls.js API changes). Fall back to probing instance methods for\n // backwards compatibility with older renderers. For DASH we probe\n // multiple methods because dash.js 5.x removed getQualityFor /\n // setQualityFor in favour of getRepresentationsByType /\n // getCurrentRepresentationForType. Missing this check causes the\n // preview thumbnail path to copy the dash.js MediaSource blob URL\n // onto a second <video> element, which Firefox rejects with\n // \"Security Error: Content at … may not load data from blob:…\".\n const isStreamingRenderer = renderer.isStreaming === true\n || (renderer.hls && typeof renderer.hls.loadLevel !== 'undefined')\n || (renderer.dash && (\n typeof renderer.dash.getQualityFor === 'function' ||\n typeof renderer.dash.getCurrentRepresentationForType === 'function' ||\n typeof renderer.dash.getRepresentationsByType === 'function' ||\n typeof renderer.dash.attachSource === 'function'\n ));\n const isHTML5Renderer = hasVideoMedia &&\n renderer.media === this.player.element &&\n !isStreamingRenderer &&\n typeof renderer.seek === 'function';\n\n // For streaming renderers (HLS/DASH), we use the main video element directly\n // For HTML5, we create a separate hidden video element\n if (isStreamingRenderer) {\n // Streaming: Preview thumbnails are disabled because seeking the main video\n // for thumbnail generation would cause visible playback jumps.\n // A separate instance for preview would be too resource-intensive.\n this.previewVideo = null;\n this.previewVideoReady = false;\n this.previewSupported = false;\n this.previewUsingMainVideo = false;\n this.previewVideoInitialized = true;\n this.player.log('Preview thumbnails disabled for streaming sources', 'info');\n return;\n }\n\n this.previewSupported = isHTML5Renderer && hasVideoMedia;\n if (!this.previewSupported) {\n this.previewVideoInitialized = true;\n return;\n }\n\n const mainVideo = renderer.media || this.player.element;\n let videoSrc = null;\n if (mainVideo.src) {\n videoSrc = mainVideo.src;\n } else {\n const source = mainVideo.querySelector('source');\n if (source) {\n videoSrc = source.src;\n }\n }\n\n if (!videoSrc) {\n this.player.log('No video source found for preview', 'warn');\n this.previewSupported = false;\n this.previewVideoInitialized = true;\n return;\n }\n\n // Create a hidden video element for capturing frames\n this.previewVideo = document.createElement('video');\n this.previewVideo.muted = true;\n this.previewVideo.preload = 'auto'; // Need more than metadata to capture frames\n this.previewVideo.playsInline = true;\n this.previewVideo.style.position = 'absolute';\n this.previewVideo.style.visibility = 'hidden';\n this.previewVideo.style.width = '1px';\n this.previewVideo.style.height = '1px';\n this.previewVideo.style.top = '-9999px';\n\n if (mainVideo.crossOrigin) {\n this.previewVideo.crossOrigin = mainVideo.crossOrigin;\n }\n\n this.previewVideo.addEventListener('error', (e: Event) => {\n this.player.log('Preview video failed to load:', e, 'warn');\n this.previewSupported = false;\n });\n\n this.previewVideo.addEventListener('loadedmetadata', () => {\n this.previewVideoReady = true;\n \n // Start pregeneration if enabled\n if (this.player.options.thumbnailPregenerate) {\n this.pregenerateThumbnails();\n }\n }, { once: true });\n\n if (this.player.container) {\n this.player.container.appendChild(this.previewVideo);\n }\n\n this.previewVideo.src = videoSrc;\n this.previewVideoReady = false;\n this.previewUsingMainVideo = false;\n this.previewVideoInitialized = true;\n }\n\n /**\n * Pre-generate thumbnails during browser idle time\n * Uses requestIdleCallback to avoid impacting UI performance\n */\n pregenerateThumbnails() {\n if (!this.previewSupported || !this.previewVideo) return;\n if (!window.requestIdleCallback) return; // Not supported in older browsers\n \n const duration = this.player.state.duration;\n if (!duration || duration <= 0) return;\n \n const interval = this.player.options.thumbnailInterval || 10;\n const times: number[] = [];\n \n // Generate list of times to pre-generate\n for (let t = 0; t < duration; t += interval) {\n // Skip times already in cache\n const cacheKey = Math.floor(t);\n if (!this.previewThumbnailCache.has(cacheKey)) {\n times.push(t);\n }\n }\n \n if (times.length === 0) return;\n \n this.player.log(`Pre-generating ${times.length} thumbnails`, 'debug');\n \n const generateNext = (deadline: IdleDeadline) => {\n // Generate thumbnails while we have idle time (at least 50ms)\n while (deadline.timeRemaining() > 50 && times.length > 0) {\n const time = times.shift();\n if (time === undefined) {\n break;\n }\n // Fire-and-forget thumbnail generation\n this.generatePreviewThumbnail(time).catch(() => {\n // Silently ignore errors during pregeneration\n });\n }\n \n // Schedule next batch if there are more to generate\n if (times.length > 0 && this.previewSupported) {\n requestIdleCallback(generateNext, { timeout: 5000 });\n }\n };\n \n // Start pre-generation with a timeout to ensure it eventually runs\n requestIdleCallback(generateNext, { timeout: 5000 });\n }\n\n /**\n * Generate preview thumbnail for a specific time\n * @param {number} time - Time in seconds\n * @returns {Promise<string>} Data URL of the thumbnail\n */\n async generatePreviewThumbnail(time: number) {\n if (!this.previewSupported || !this.previewVideo) {\n return null;\n }\n\n // Stash a non-null local: TypeScript loses the `previewVideo !== null`\n // narrowing across awaits and inner closures.\n const previewVideo = this.previewVideo;\n\n // Wait for preview video to be ready if not yet loaded\n if (!this.previewVideoReady) {\n if (previewVideo.readyState < 2) {\n // Wait for at least HAVE_CURRENT_DATA (2) to ensure we can capture frames\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error('Preview video data load timeout'));\n }, 10000);\n\n const cleanup = () => {\n clearTimeout(timeout);\n previewVideo.removeEventListener('loadeddata', checkReady);\n previewVideo.removeEventListener('canplay', checkReady);\n previewVideo.removeEventListener('error', onError);\n };\n\n const checkReady = () => {\n if (previewVideo.readyState >= 2) {\n cleanup();\n this.previewVideoReady = true;\n resolve();\n }\n };\n\n const onError = () => {\n cleanup();\n reject(new Error('Preview video failed to load'));\n };\n\n // Try loadeddata first (faster), fallback to canplay\n if (previewVideo.readyState >= 1) {\n previewVideo.addEventListener('loadeddata', checkReady);\n }\n previewVideo.addEventListener('canplay', checkReady);\n previewVideo.addEventListener('error', onError);\n\n // If already ready, resolve immediately\n if (previewVideo.readyState >= 2) {\n checkReady();\n }\n }).catch((): null => {\n this.previewSupported = false;\n return null;\n });\n } else {\n this.previewVideoReady = true;\n }\n }\n\n // Check cache first\n const cacheKey = Math.floor(time);\n if (this.previewThumbnailCache.has(cacheKey)) {\n return this.previewThumbnailCache.get(cacheKey);\n }\n\n // Use shared frame capture utility\n // For main video (HLS), we need to restore state; for preview video, we don't\n const restoreState = this.previewUsingMainVideo;\n const quality = this.player.options.thumbnailQuality || 0.8;\n const maxWidth = this.player.options.thumbnailWidth || 160;\n const maxHeight = this.player.options.thumbnailHeight || 90;\n\n const dataURL = await captureVideoFrame(previewVideo, time, {\n restoreState,\n quality,\n maxWidth,\n maxHeight\n });\n\n if (dataURL) {\n // Cache the thumbnail (configurable cache size with LRU-like behavior)\n const maxCacheSize = this.player.options.thumbnailCacheSize || 50;\n if (this.previewThumbnailCache.size >= maxCacheSize) {\n // Delete oldest entry (first key in insertion order)\n const firstKey = this.previewThumbnailCache.keys().next().value;\n if (firstKey !== undefined) {\n this.previewThumbnailCache.delete(firstKey);\n }\n }\n this.previewThumbnailCache.set(cacheKey, dataURL);\n }\n\n return dataURL;\n }\n\n /**\n * Update preview thumbnail display\n * @param {number} time - Time in seconds\n */\n async updatePreviewThumbnail(time: number) {\n if (!this.previewSupported || !this.controls.progressPreview) {\n return;\n }\n\n // Clear any pending updates\n if (this.previewThumbnailTimeout) {\n clearTimeout(this.previewThumbnailTimeout);\n }\n\n // Debounce thumbnail generation to avoid excessive seeking\n this.previewThumbnailTimeout = setTimeout(async () => {\n try {\n const thumbnail = await this.generatePreviewThumbnail(time);\n if (thumbnail && this.controls.progressPreview) {\n // Set background image and make visible\n this.controls.progressPreview.style.backgroundImage = `url(\"${thumbnail}\")`;\n this.controls.progressPreview.style.display = 'block';\n this.controls.progressPreview.style.backgroundRepeat = 'no-repeat';\n this.controls.progressPreview.style.backgroundPosition = 'center';\n } else {\n // Hide if thumbnail generation failed\n if (this.controls.progressPreview) {\n this.controls.progressPreview.style.display = 'none';\n }\n }\n this.currentPreviewTime = time;\n } catch (error) {\n this.player.log('Preview thumbnail update failed:', error, 'warn');\n if (this.controls.progressPreview) {\n this.controls.progressPreview.style.display = 'none';\n }\n }\n }, 100);\n }\n\n setupProgressBarEvents() {\n const progress = this.controls.progress;\n if (!progress) return;\n\n const updateProgress = (clientX: number) => {\n const rect = progress.getBoundingClientRect();\n // Guard against division by zero if progress bar has no width\n const percent = rect.width > 0 ? Math.max(0, Math.min(1, (clientX - rect.left) / rect.width)) : 0;\n const duration = this.player.state.duration || 0;\n const time = percent * duration;\n return {percent, time};\n };\n\n // Mouse events. Document-wide mousemove/mouseup are installed once\n // in {@link setupGlobalDragListeners}; here we only cache the\n // progress rect on mousedown so the global handler can resolve\n // positions without re-querying layout on every pointer move.\n progress.addEventListener('mousedown', (e: MouseEvent) => {\n this.isDraggingProgress = true;\n this._progressBarRect = progress.getBoundingClientRect();\n const {time} = updateProgress(e.clientX);\n this.player.seek(time);\n });\n\n // Hover tooltip\n progress.addEventListener('mousemove', (e: MouseEvent) => {\n if (!this.isDraggingProgress) {\n const {time} = updateProgress(e.clientX);\n const rect = progress.getBoundingClientRect();\n const left = e.clientX - rect.left;\n \n // Update tooltip time text + position\n const tooltip = this.controls.progressTooltip;\n const tooltipTime = this.controls.progressTooltipTime;\n if (tooltip && tooltipTime) {\n tooltipTime.textContent = TimeUtils.formatTime(time);\n tooltip.style.left = `${left}px`;\n tooltip.style.display = 'block';\n }\n\n // Only show preview thumbnails after the user has started playback at least once.\n // Before that, show just the timestamp (no empty preview box).\n if (!this.player?.state?.hasStartedPlayback) {\n if (this.controls.progressPreview) {\n this.controls.progressPreview.style.display = 'none';\n }\n return;\n }\n\n this.ensurePreviewVideoInitialized();\n if (this.previewSupported) {\n this.updatePreviewThumbnail(time);\n } else if (this.controls.progressPreview) {\n this.controls.progressPreview.style.display = 'none';\n }\n }\n });\n\n progress.addEventListener('mouseleave', () => {\n if (this.controls.progressTooltip) {\n this.controls.progressTooltip.style.display = 'none';\n }\n if (this.previewThumbnailTimeout) {\n clearTimeout(this.previewThumbnailTimeout);\n }\n });\n\n // Keyboard navigation. Mirrors WAI-ARIA Authoring Practices for\n // sliders: PageUp/PageDown step by `seekIntervalLarge` (default 30s),\n // Home/End jump to the start/end of the media.\n progress.addEventListener('keydown', (e: KeyboardEvent) => {\n const smallStep = (this.player.options.seekInterval as number | undefined) || 5;\n const largeStep = (this.player.options.seekIntervalLarge as number | undefined) || 30;\n const duration = Number(this.player.state.duration);\n\n switch (e.key) {\n case 'ArrowLeft':\n e.preventDefault();\n this.player.seekBackward(smallStep);\n break;\n case 'ArrowRight':\n e.preventDefault();\n this.player.seekForward(smallStep);\n break;\n case 'PageUp':\n e.preventDefault();\n this.player.seekForward(largeStep);\n break;\n case 'PageDown':\n e.preventDefault();\n this.player.seekBackward(largeStep);\n break;\n case 'Home':\n e.preventDefault();\n this.player.seek(0);\n break;\n case 'End':\n e.preventDefault();\n if (Number.isFinite(duration) && duration > 0) {\n this.player.seek(Math.max(0, duration - 0.1));\n }\n break;\n default:\n break;\n }\n });\n\n // Touch events\n progress.addEventListener('touchstart', (e: TouchEvent) => {\n this.isDraggingProgress = true;\n const touch = e.touches[0];\n if (!touch) return;\n const {time} = updateProgress(touch.clientX);\n this.player.seek(time);\n });\n\n progress.addEventListener('touchmove', (e: TouchEvent) => {\n if (this.isDraggingProgress) {\n e.preventDefault();\n const touch = e.touches[0];\n if (!touch) return;\n const {time} = updateProgress(touch.clientX);\n this.player.seek(time);\n }\n });\n\n progress.addEventListener('touchend', () => {\n this.isDraggingProgress = false;\n });\n }\n\n createPlayPauseButton() {\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-play-pause`,\n attributes: {\n 'type': 'button',\n 'aria-label': i18n.t('player.play')\n }\n });\n\n button.appendChild(createIconElement('play'));\n\n button.addEventListener('click', () => {\n this.player.toggle();\n });\n\n this.controls.playPause = button;\n return button;\n }\n\n createRestartButton() {\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-restart`,\n attributes: {\n 'type': 'button',\n 'aria-label': i18n.t('player.restart')\n }\n });\n\n button.appendChild(createIconElement('restart'));\n\n button.addEventListener('click', () => {\n this.player.seek(0);\n this.player.play();\n });\n\n return button;\n }\n\n createPreviousButton() {\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-previous`,\n attributes: {\n 'type': 'button',\n 'aria-label': i18n.t('player.previous')\n }\n });\n\n button.appendChild(createIconElement('skipPrevious'));\n\n button.addEventListener('click', () => {\n if (this.player.playlistManager) {\n this.player.playlistManager.previous();\n }\n });\n\n // Update button state\n const updateState = () => {\n if (this.player.playlistManager) {\n (button as HTMLButtonElement).disabled = !this.player.playlistManager.hasPrevious() && !this.player.playlistManager.options.loop;\n }\n };\n this.player.on('playlisttrackchange', updateState);\n updateState();\n\n this.controls.previous = button;\n return button;\n }\n\n createNextButton() {\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-next`,\n attributes: {\n 'type': 'button',\n 'aria-label': i18n.t('player.next')\n }\n });\n\n button.appendChild(createIconElement('skipNext'));\n\n button.addEventListener('click', () => {\n if (this.player.playlistManager) {\n this.player.playlistManager.next();\n }\n });\n\n // Update button state\n const updateState = () => {\n if (this.player.playlistManager) {\n (button as HTMLButtonElement).disabled = !this.player.playlistManager.hasNext() && !this.player.playlistManager.options.loop;\n }\n };\n this.player.on('playlisttrackchange', updateState);\n updateState();\n\n this.controls.next = button;\n return button;\n }\n\n createPlaylistToggleButton() {\n // Get unique panel ID from playlist manager\n const panelId = this.player.playlistManager ? `${this.player.playlistManager.uniqueId}-panel` : 'vidply-playlist-panel';\n \n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-playlist-toggle`,\n attributes: {\n 'type': 'button',\n 'aria-label': i18n.t('player.playlist'),\n 'aria-expanded': 'false',\n 'aria-pressed': 'false',\n 'aria-controls': panelId\n }\n });\n\n button.appendChild(createIconElement('playlist'));\n\n button.addEventListener('click', () => {\n if (this.player.playlistManager) {\n this.player.playlistManager.togglePanel();\n }\n });\n\n this.controls.playlistToggle = button;\n return button;\n }\n\n createRewindButton() {\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-rewind`,\n attributes: {\n 'type': 'button',\n 'aria-label': i18n.t('player.rewindSeconds', { seconds: 15 })\n }\n });\n\n button.appendChild(createIconElement('rewind'));\n\n button.addEventListener('click', () => {\n this.player.seekBackward(15);\n });\n\n return button;\n }\n\n createForwardButton() {\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-forward`,\n attributes: {\n 'type': 'button',\n 'aria-label': i18n.t('player.forwardSeconds', { seconds: 15 })\n }\n });\n\n button.appendChild(createIconElement('forward'));\n\n button.addEventListener('click', () => {\n this.player.seekForward(15);\n });\n\n return button;\n }\n\n createMuteButton() {\n // Simple mute/unmute button for touch devices\n const muteButton = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-mute`,\n attributes: {\n 'type': 'button',\n 'aria-label': i18n.t('player.mute')\n }\n });\n\n muteButton.appendChild(createIconElement('volumeHigh'));\n\n // Simply toggle mute on click/touch\n muteButton.addEventListener('click', () => {\n this.player.toggleMute();\n });\n\n this.controls.mute = muteButton;\n\n return muteButton;\n }\n\n createVolumeControl() {\n // Mute/Volume button with slider (desktop)\n const muteButton = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-mute`,\n attributes: {\n 'type': 'button',\n 'aria-label': i18n.t('player.volume'),\n 'aria-expanded': 'false'\n }\n });\n\n muteButton.appendChild(createIconElement('volumeHigh'));\n\n // Toggle mute on right click, show volume slider on left click\n muteButton.addEventListener('contextmenu', (e) => {\n e.preventDefault();\n this.player.toggleMute();\n });\n\n muteButton.addEventListener('click', () => {\n this.showVolumeSlider(muteButton);\n });\n\n this.controls.mute = muteButton;\n\n return muteButton;\n }\n\n showVolumeSlider(button: HTMLElement) {\n // Remove existing slider if any\n const existingSlider = document.querySelector(`.${this.player.options.classPrefix}-volume-menu`);\n if (existingSlider) {\n existingSlider.remove();\n button.setAttribute('aria-expanded', 'false');\n return;\n }\n\n // Volume menu container\n const volumeMenu = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-volume-menu ${this.player.options.classPrefix}-menu`\n });\n\n const initialPercent = Math.round(this.player.state.volume * 100);\n const volumeSlider = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-volume-slider`,\n attributes: {\n 'role': 'slider',\n 'aria-label': i18n.t('player.volume'),\n 'aria-valuemin': '0',\n 'aria-valuemax': '100',\n 'aria-valuenow': String(initialPercent),\n 'aria-valuetext': this.player.state.muted\n ? i18n.t('player.muted')\n : i18n.t('player.volumePercent', { percent: initialPercent }),\n 'tabindex': '0'\n }\n });\n\n const volumeTrack = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-volume-track`\n });\n\n const volumeFill = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-volume-fill`\n });\n \n // Set initial fill height based on current volume\n const initialVolumePercent = this.player.state.volume * 100;\n volumeFill.style.height = `${initialVolumePercent}%`;\n\n const volumeHandle = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-volume-handle`\n });\n\n volumeTrack.appendChild(volumeFill);\n volumeFill.appendChild(volumeHandle);\n volumeSlider.appendChild(volumeTrack);\n volumeMenu.appendChild(volumeSlider);\n\n // Volume slider events. Document-level mousemove/mouseup are\n // installed once in {@link setupGlobalDragListeners}; here we only\n // mark the active track + drag state so the global handler can\n // translate cursor position into a volume value.\n const updateVolume = (clientY: number) => {\n const rect = volumeTrack.getBoundingClientRect();\n const percent = Math.max(0, Math.min(1, 1 - ((clientY - rect.top) / rect.height)));\n this.player.setVolume(percent);\n };\n\n volumeSlider.addEventListener('mousedown', (e) => {\n e.stopPropagation();\n e.preventDefault();\n this.isDraggingVolume = true;\n this._activeVolumeTrack = volumeTrack;\n updateVolume(e.clientY);\n });\n\n // Touch events for iOS and mobile devices\n volumeSlider.addEventListener('touchstart', (e) => {\n e.stopPropagation();\n e.preventDefault();\n this.isDraggingVolume = true;\n this._activeVolumeTrack = volumeTrack;\n const touch = e.touches[0];\n if (!touch) return;\n updateVolume(touch.clientY);\n }, { passive: false });\n\n volumeSlider.addEventListener('touchmove', (e: TouchEvent) => {\n if (this.isDraggingVolume) {\n e.preventDefault();\n const touch = e.touches[0];\n if (!touch) return;\n updateVolume(touch.clientY);\n }\n }, { passive: false });\n\n volumeSlider.addEventListener('touchend', (e) => {\n e.stopPropagation();\n e.preventDefault();\n this.isDraggingVolume = false;\n this._activeVolumeTrack = null;\n }, { passive: false });\n\n volumeSlider.addEventListener('touchcancel', () => {\n this.isDraggingVolume = false;\n this._activeVolumeTrack = null;\n });\n\n // Keyboard volume control\n volumeSlider.addEventListener('keydown', (e) => {\n if (e.key === 'ArrowUp') {\n e.preventDefault();\n this.player.setVolume(Math.min(1, this.player.state.volume + 0.1));\n } else if (e.key === 'ArrowDown') {\n e.preventDefault();\n this.player.setVolume(Math.max(0, this.player.state.volume - 0.1));\n } else if (e.key === 'Escape') {\n e.preventDefault();\n e.stopPropagation();\n // Close menu and return focus to button\n this.closeMenuAndReturnFocus(volumeMenu, button, true);\n }\n });\n\n // Prevent menu from closing when interacting with slider\n volumeMenu.addEventListener('click', (e) => {\n e.stopPropagation();\n });\n \n // Prevent menu from closing on touch events\n volumeMenu.addEventListener('touchstart', (e) => {\n e.stopPropagation();\n });\n \n volumeMenu.addEventListener('touchmove', (e) => {\n e.stopPropagation();\n });\n \n volumeMenu.addEventListener('touchend', (e) => {\n e.stopPropagation();\n });\n\n // Position menu first (before it's visible) to prevent jumping\n volumeMenu.style.visibility = 'hidden';\n volumeMenu.style.display = 'block';\n \n // Insert menu into DOM (handles fullscreen positioning)\n this.insertMenuIntoDOM(volumeMenu, button);\n \n // Position immediately (synchronously) while hidden\n this.positionMenu(volumeMenu, button, true);\n \n // Make menu visible after positioning\n requestAnimationFrame(() => {\n volumeMenu.style.visibility = 'visible';\n });\n\n this.controls.volumeSlider = volumeSlider;\n this.controls.volumeFill = volumeFill;\n\n // Focus the volume slider for keyboard accessibility\n focusElement(volumeSlider, { delay: 50 });\n\n // Close menu on outside click\n this.attachMenuCloseHandler(volumeMenu, button, true);\n }\n\n createTimeDisplay() {\n const container = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-time`,\n attributes: {\n 'role': 'group',\n 'aria-label': i18n.t('time.display')\n }\n });\n\n // Current time - visual text hidden, accessible text provided via sr-only span\n this.controls.currentTimeDisplay = DOMUtils.createElement('span', {\n className: `${this.player.options.classPrefix}-current-time`\n });\n \n // Create visual text inside, hidden from screen readers\n const currentTimeVisual = DOMUtils.createElement('span', {\n textContent: '00:00',\n attributes: {\n 'aria-hidden': 'true'\n }\n });\n const currentTimeAccessible = DOMUtils.createElement('span', {\n className: 'vidply-sr-only',\n textContent: i18n.t('time.seconds', { count: 0 })\n });\n\n this.controls.currentTimeDisplay.appendChild(currentTimeVisual);\n this.controls.currentTimeDisplay.appendChild(currentTimeAccessible);\n this.controls.currentTimeVisual = currentTimeVisual;\n this.controls.currentTimeAccessible = currentTimeAccessible;\n\n const separator = DOMUtils.createElement('span', {\n textContent: ' / ',\n attributes: {\n 'aria-hidden': 'true'\n }\n });\n\n // Duration - visual text hidden, accessible text provided via sr-only span\n this.controls.durationDisplay = DOMUtils.createElement('span', {\n className: `${this.player.options.classPrefix}-duration`\n });\n \n // Create visual text inside, hidden from screen readers\n const durationVisual = DOMUtils.createElement('span', {\n textContent: '00:00',\n attributes: {\n 'aria-hidden': 'true'\n }\n });\n const durationAccessible = DOMUtils.createElement('span', {\n className: 'vidply-sr-only',\n textContent: i18n.t('time.durationPrefix') + i18n.t('time.seconds', { count: 0 })\n });\n\n this.controls.durationDisplay.appendChild(durationVisual);\n this.controls.durationDisplay.appendChild(durationAccessible);\n this.controls.durationVisual = durationVisual;\n this.controls.durationAccessible = durationAccessible;\n\n container.appendChild(this.controls.currentTimeDisplay);\n container.appendChild(separator);\n container.appendChild(this.controls.durationDisplay);\n\n return container;\n }\n\n createChaptersButton() {\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-chapters`,\n attributes: {\n 'type': 'button',\n 'aria-label': i18n.t('player.chapters'),\n 'aria-expanded': 'false'\n }\n });\n\n button.appendChild(createIconElement('chapters'));\n\n button.addEventListener('click', () => {\n this.showChaptersMenu(button);\n });\n\n this.controls.chapters = button;\n return button;\n }\n\n showChaptersMenu(button: HTMLElement) {\n // Remove existing menu if any (toggle behavior)\n const existingMenu = document.querySelector(`.${this.player.options.classPrefix}-chapters-menu`);\n if (existingMenu) {\n existingMenu.remove();\n button.setAttribute('aria-expanded', 'false');\n // Clear tracking if this was the open menu\n if (this.openMenu === existingMenu) {\n this.openMenu = null;\n this.openMenuButton = null;\n }\n return;\n }\n\n const menu = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-chapters-menu ${this.player.options.classPrefix}-menu`,\n attributes: {\n 'role': 'menu',\n 'aria-label': i18n.t('player.chapters')\n }\n });\n\n // Get chapter tracks\n const chapterTracks: TextTrack[] = (Array.from(this.player.element.textTracks) as TextTrack[]).filter(\n track => track.kind === 'chapters'\n );\n\n if (chapterTracks.length === 0) {\n // No chapters available — non-operable, so keep it out of keyboard\n // roving navigation via aria-disabled (WCAG 2.1.1, 4.1.2).\n const noChaptersItem = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-menu-item ${this.player.options.classPrefix}-menu-item-disabled`,\n textContent: i18n.t('player.noChapters'),\n attributes: {\n 'role': 'menuitem',\n 'aria-disabled': 'true',\n 'tabindex': '-1'\n },\n style: {opacity: '0.5', cursor: 'default'}\n });\n menu.appendChild(noChaptersItem);\n } else if (chapterTracks[0]) {\n const chapterTrack = chapterTracks[0];\n\n // Ensure track is in 'hidden' mode to load cues\n if (chapterTrack.mode === 'disabled') {\n chapterTrack.mode = 'hidden';\n }\n\n if (!chapterTrack.cues || chapterTrack.cues.length === 0) {\n // Cues not loaded yet - wait for them to load. Non-operable\n // placeholder, kept out of roving navigation via aria-disabled.\n const loadingItem = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-menu-item ${this.player.options.classPrefix}-menu-item-disabled`,\n textContent: i18n.t('player.loadingChapters'),\n attributes: {\n 'role': 'menuitem',\n 'aria-disabled': 'true',\n 'tabindex': '-1'\n },\n style: {opacity: '0.5', cursor: 'default'}\n });\n menu.appendChild(loadingItem);\n\n // Listen for track load event\n const onTrackLoad = () => {\n // Remove loading message and rebuild menu\n menu.remove();\n this.showChaptersMenu(button);\n };\n\n chapterTrack.addEventListener('load', onTrackLoad, {once: true});\n\n // Also try again after a short delay as fallback\n setTimeout(() => {\n if (chapterTrack.cues && chapterTrack.cues.length > 0 && document.contains(menu)) {\n menu.remove();\n this.showChaptersMenu(button);\n }\n }, 500);\n } else {\n // Display chapters\n const cues = chapterTrack.cues;\n for (let i = 0; i < cues.length; i++) {\n const cue = cues[i] as VTTCue;\n const item = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-menu-item`,\n attributes: {\n 'type': 'button',\n 'role': 'menuitem',\n 'tabindex': '-1'\n }\n });\n\n const timeLabel = DOMUtils.createElement('span', {\n className: `${this.player.options.classPrefix}-chapter-time`,\n textContent: TimeUtils.formatTime(cue.startTime),\n attributes: {\n 'aria-label': TimeUtils.formatDuration(cue.startTime)\n }\n });\n\n const titleLabel = DOMUtils.createElement('span', {\n className: `${this.player.options.classPrefix}-chapter-title`,\n textContent: cue.text\n });\n\n item.appendChild(timeLabel);\n item.appendChild(document.createTextNode(' '));\n item.appendChild(titleLabel);\n\n item.addEventListener('click', () => {\n this.player.seek(cue.startTime);\n this.closeMenuAndReturnFocus(menu, button);\n });\n\n menu.appendChild(item);\n }\n \n // Add keyboard navigation\n this.attachMenuKeyboardNavigation(menu, button);\n \n // Focus first item\n setTimeout(() => {\n const firstItem = menu.querySelector(`.${this.player.options.classPrefix}-menu-item`) as HTMLElement | null;\n if (firstItem) {\n firstItem.focus({ preventScroll: true });\n }\n }, 0);\n }\n }\n\n // Position menu first (before it's visible) to prevent jumping\n // Set menu to invisible temporarily\n menu.style.visibility = 'hidden';\n menu.style.display = 'block';\n \n // Insert menu into DOM (handles fullscreen positioning)\n this.insertMenuIntoDOM(menu, button);\n \n // Position immediately (synchronously) while hidden\n this.positionMenu(menu, button, true);\n \n // Make menu visible after positioning\n requestAnimationFrame(() => {\n menu.style.visibility = 'visible';\n });\n\n // Close menu on outside click\n this.attachMenuCloseHandler(menu, button);\n }\n\n createQualityButton() {\n const ariaLabel = i18n.t('player.quality');\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-quality`,\n attributes: {\n 'type': 'button',\n 'aria-label': ariaLabel,\n 'aria-expanded': 'false'\n }\n });\n\n button.appendChild(createIconElement('hd'));\n DOMUtils.attachTooltip(button, ariaLabel, this.player.options.classPrefix);\n\n // Add quality indicator text\n const qualityText = DOMUtils.createElement('span', {\n className: `${this.player.options.classPrefix}-quality-text`,\n textContent: ''\n });\n button.appendChild(qualityText);\n\n button.addEventListener('click', () => {\n this.showQualityMenu(button);\n });\n\n this.controls.quality = button;\n this.controls.qualityText = qualityText;\n \n // Update quality indicator after a short delay to ensure renderer is ready\n setTimeout(() => this.updateQualityIndicator(), 500);\n \n return button;\n }\n\n showQualityMenu(button: HTMLElement) {\n // Remove existing menu if any (toggle behavior)\n const existingMenu = document.querySelector(`.${this.player.options.classPrefix}-quality-menu`);\n if (existingMenu) {\n existingMenu.remove();\n button.setAttribute('aria-expanded', 'false');\n // Clear tracking if this was the open menu\n if (this.openMenu === existingMenu) {\n this.openMenu = null;\n this.openMenuButton = null;\n }\n return;\n }\n\n const menu = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-quality-menu ${this.player.options.classPrefix}-menu`,\n attributes: {\n 'role': 'menu',\n 'aria-label': i18n.t('player.quality')\n }\n });\n\n // Check if renderer supports quality selection\n if (this.player.renderer && this.player.renderer.getQualities) {\n const qualities = this.player.renderer.getQualities();\n const currentQuality = this.player.renderer.getCurrentQuality ? this.player.renderer.getCurrentQuality() : -1;\n const hasAutoQuality = typeof this.player.renderer.supportsAutoQuality === 'function'\n && this.player.renderer.supportsAutoQuality();\n\n if (qualities.length === 0) {\n // No qualities available\n const noQualityItem = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-menu-item`,\n textContent: i18n.t('player.autoQuality'),\n attributes: {\n 'role': 'menuitem'\n },\n style: {opacity: '0.5', cursor: 'default'}\n });\n menu.appendChild(noQualityItem);\n } else {\n let activeItem: HTMLElement | null = null;\n \n // Auto quality option (for renderers that support it, e.g. HLS, DASH)\n if (hasAutoQuality) {\n const autoItem = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-menu-item`,\n textContent: i18n.t('player.auto'),\n attributes: {\n 'type': 'button',\n 'role': 'menuitem',\n 'tabindex': '-1'\n }\n });\n\n // Check if auto is currently selected\n const isAuto = typeof this.player.renderer.isAutoQuality === 'function'\n && this.player.renderer.isAutoQuality();\n if (isAuto) {\n autoItem.classList.add(`${this.player.options.classPrefix}-menu-item-active`);\n autoItem.appendChild(createIconElement('check'));\n activeItem = autoItem;\n }\n\n autoItem.addEventListener('click', () => {\n if (this.player.renderer?.switchQuality) {\n this.player.renderer.switchQuality(-1);\n }\n this.closeMenuAndReturnFocus(menu, button);\n });\n\n menu.appendChild(autoItem);\n }\n\n // Quality options\n (qualities as QualityLevel[]).forEach((quality) => {\n const item = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-menu-item`,\n textContent: quality.name || `${quality.height}p`,\n attributes: {\n 'type': 'button',\n 'role': 'menuitem',\n 'tabindex': '-1'\n }\n });\n\n // Highlight current quality\n if (quality.index === currentQuality) {\n item.classList.add(`${this.player.options.classPrefix}-menu-item-active`);\n item.appendChild(createIconElement('check'));\n activeItem = item;\n }\n\n item.addEventListener('click', () => {\n if (this.player.renderer?.switchQuality && quality.index !== undefined) {\n this.player.renderer.switchQuality(quality.index);\n }\n this.closeMenuAndReturnFocus(menu, button);\n });\n\n menu.appendChild(item);\n });\n \n // Add keyboard navigation\n this.attachMenuKeyboardNavigation(menu, button);\n \n // Focus active item or first item\n setTimeout(() => {\n const focusTarget = activeItem || menu.querySelector(`.${this.player.options.classPrefix}-menu-item`);\n if (focusTarget) {\n focusTarget.focus({ preventScroll: true });\n }\n }, 0);\n }\n } else {\n // No quality support\n const noSupportItem = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-menu-item`,\n textContent: i18n.t('player.noQuality'),\n style: {opacity: '0.5', cursor: 'default'}\n });\n menu.appendChild(noSupportItem);\n }\n\n // Position menu first (before it's visible) to prevent jumping\n // Set menu to invisible temporarily\n menu.style.visibility = 'hidden';\n menu.style.display = 'block';\n \n // Insert menu into DOM (handles fullscreen positioning)\n this.insertMenuIntoDOM(menu, button);\n \n // Position immediately (synchronously) while hidden\n this.positionMenu(menu, button, true);\n \n // Make menu visible after positioning\n requestAnimationFrame(() => {\n menu.style.visibility = 'visible';\n });\n\n // Close menu on outside click\n this.attachMenuCloseHandler(menu, button);\n }\n\n createCaptionStyleButton() {\n const ariaLabel = i18n.t('player.captionStyling');\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-caption-style`,\n attributes: {\n 'type': 'button',\n 'aria-label': ariaLabel,\n 'aria-expanded': 'false'\n }\n });\n\n // Create \"Aa\" text icon for styling\n const textIcon = DOMUtils.createElement('span', {\n textContent: 'Aa',\n style: {\n fontSize: '14px',\n fontWeight: 'bold'\n }\n });\n button.appendChild(textIcon);\n DOMUtils.attachTooltip(button, ariaLabel, this.player.options.classPrefix);\n\n button.addEventListener('click', () => {\n this.showCaptionStyleMenu(button);\n });\n\n this.controls.captionStyle = button;\n return button;\n }\n\n showCaptionStyleMenu(button: HTMLElement) {\n // The caption-style panel builder (font/color/opacity controls) lives\n // in a lazily imported module so it stays out of the always-loaded\n // core bundle and is only fetched the first time the panel is opened.\n import('./CaptionStyleMenu.js')\n .then(({ showCaptionStyleMenu }) => showCaptionStyleMenu(this, button))\n .catch((error: unknown) => this.player.log('Failed to load caption style menu:', error, 'error'));\n }\n\n createSpeedButton() {\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-speed`,\n attributes: {\n 'type': 'button',\n 'aria-label': i18n.t('player.speed'),\n 'aria-expanded': 'false'\n }\n });\n\n button.appendChild(createIconElement('speed'));\n\n const speedText = DOMUtils.createElement('span', {\n className: `${this.player.options.classPrefix}-speed-text`,\n textContent: '1x'\n });\n button.appendChild(speedText);\n\n button.addEventListener('click', () => {\n this.showSpeedMenu(button);\n });\n\n this.controls.speed = button;\n this.controls.speedText = speedText;\n return button;\n }\n\n formatSpeedLabel(speed: number) {\n // Special case: 1x is \"Normal\" (translated)\n if (speed === 1) {\n return i18n.t('speeds.normal');\n }\n \n // For other speeds, format with locale-specific decimal separator\n const speedStr = speed.toLocaleString(i18n.getLanguage(), {\n minimumFractionDigits: 0,\n maximumFractionDigits: 2\n });\n \n return `${speedStr}×`;\n }\n\n showSpeedMenu(button: HTMLElement) {\n // Remove existing menu if any (toggle behavior)\n const existingMenu = document.querySelector(`.${this.player.options.classPrefix}-speed-menu`);\n if (existingMenu) {\n existingMenu.remove();\n button.setAttribute('aria-expanded', 'false');\n // Clear tracking if this was the open menu\n if (this.openMenu === existingMenu) {\n this.openMenu = null;\n this.openMenuButton = null;\n }\n return;\n }\n\n const menu = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-speed-menu ${this.player.options.classPrefix}-menu`,\n attributes: {\n 'role': 'menu'\n }\n });\n\n const speeds = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2];\n let activeItem: HTMLElement | null = null;\n\n speeds.forEach(speed => {\n const item = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-menu-item`,\n textContent: this.formatSpeedLabel(speed),\n attributes: {\n 'type': 'button',\n 'role': 'menuitem',\n 'tabindex': '-1'\n }\n });\n\n if (speed === this.player.state.playbackSpeed) {\n item.classList.add(`${this.player.options.classPrefix}-menu-item-active`);\n item.appendChild(createIconElement('check'));\n activeItem = item;\n }\n\n item.addEventListener('click', () => {\n this.player.setPlaybackSpeed(speed);\n this.closeMenuAndReturnFocus(menu, button);\n });\n\n menu.appendChild(item);\n });\n\n // Position menu first (before it's visible) to prevent jumping\n // Set menu to invisible temporarily\n menu.style.visibility = 'hidden';\n menu.style.display = 'block';\n \n // Insert menu into DOM (handles fullscreen positioning)\n this.insertMenuIntoDOM(menu, button);\n \n // Position immediately (synchronously) while hidden\n this.positionMenu(menu, button, true);\n \n // Make menu visible after positioning\n requestAnimationFrame(() => {\n menu.style.visibility = 'visible';\n });\n\n // Add keyboard navigation\n this.attachMenuKeyboardNavigation(menu, button);\n\n // Close menu on outside click\n this.attachMenuCloseHandler(menu, button);\n\n // Focus the active item or first item\n setTimeout(() => {\n const focusTarget = activeItem || menu.querySelector(`.${this.player.options.classPrefix}-menu-item`);\n if (focusTarget) {\n focusTarget.focus({ preventScroll: true });\n }\n }, 0);\n }\n\n createCaptionsButton() {\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-captions-button`,\n attributes: {\n 'type': 'button',\n 'aria-label': i18n.t('player.captions'),\n 'aria-expanded': 'false'\n }\n });\n\n button.appendChild(createIconElement('captionsOff'));\n\n button.addEventListener('click', () => {\n this.showCaptionsMenu(button);\n });\n\n this.controls.captions = button;\n return button;\n }\n\n showCaptionsMenu(button: HTMLElement) {\n // Remove existing menu if any (toggle behavior)\n const existingMenu = document.querySelector(`.${this.player.options.classPrefix}-captions-menu`);\n if (existingMenu) {\n existingMenu.remove();\n button.setAttribute('aria-expanded', 'false');\n // Clear tracking if this was the open menu\n if (this.openMenu === existingMenu) {\n this.openMenu = null;\n this.openMenuButton = null;\n }\n return;\n }\n\n const menu = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-captions-menu ${this.player.options.classPrefix}-menu`,\n attributes: {\n 'role': 'menu',\n 'aria-label': i18n.t('captions.select')\n }\n });\n\n // Check if there are any caption tracks\n if (!this.player.captionManager || this.player.captionManager.tracks.length === 0) {\n // Show \"No captions available\" message\n const noTracksItem = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-menu-item`,\n textContent: i18n.t('player.noCaptions'),\n attributes: {\n 'role': 'menuitem'\n },\n style: {opacity: '0.5', cursor: 'default'}\n });\n menu.appendChild(noTracksItem);\n\n // Insert menu into DOM (handles fullscreen positioning)\n this.insertMenuIntoDOM(menu, button);\n\n // Close menu on outside click\n this.attachMenuCloseHandler(menu, button);\n return;\n }\n\n let activeItem: HTMLElement | null = null;\n\n // Off option\n const offItem = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-menu-item`,\n textContent: i18n.t('captions.off'),\n attributes: {\n 'type': 'button',\n 'role': 'menuitem',\n 'tabindex': '-1'\n }\n });\n\n if (!this.player.state.captionsEnabled) {\n offItem.classList.add(`${this.player.options.classPrefix}-menu-item-active`);\n offItem.appendChild(createIconElement('check'));\n activeItem = offItem;\n }\n\n offItem.addEventListener('click', () => {\n this.player.disableCaptions();\n this.updateCaptionsButton();\n this.closeMenuAndReturnFocus(menu, button);\n });\n\n menu.appendChild(offItem);\n\n // Available tracks. `this.player.captionManager` is loosely typed\n // (any) on Player today, so re-anchor the return type via the real\n // CaptionManager class to keep `track` strongly typed below.\n const tracks: ReturnType<CaptionManager['getAvailableTracks']> =\n this.player.captionManager.getAvailableTracks();\n tracks.forEach((track) => {\n const item = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-menu-item`,\n textContent: track.label,\n attributes: {\n 'type': 'button',\n 'role': 'menuitem',\n 'lang': track.language,\n 'tabindex': '-1'\n }\n });\n\n const captionManager = this.player.captionManager;\n if (captionManager && this.player.state.captionsEnabled &&\n captionManager.currentTrack === captionManager.tracks[track.index]) {\n item.classList.add(`${this.player.options.classPrefix}-menu-item-active`);\n item.appendChild(createIconElement('check'));\n activeItem = item;\n }\n\n item.addEventListener('click', () => {\n this.player.captionManager?.switchTrack(track.index);\n this.updateCaptionsButton();\n this.closeMenuAndReturnFocus(menu, button);\n });\n\n menu.appendChild(item);\n });\n\n // Insert menu into DOM (handles fullscreen positioning)\n this.insertMenuIntoDOM(menu, button);\n\n // Add keyboard navigation for the menu\n this.attachMenuKeyboardNavigation(menu, button);\n\n // Close menu on outside click and Escape key\n this.attachMenuCloseHandler(menu, button);\n\n // Focus the active item or the first item\n setTimeout(() => {\n const focusTarget = activeItem || menu.querySelector(`.${this.player.options.classPrefix}-menu-item`);\n if (focusTarget) {\n focusTarget.focus({ preventScroll: true });\n }\n }, 0);\n }\n\n updateCaptionsButton() {\n if (!this.controls.captions) return;\n\n const icon = this.controls.captions.querySelector('.vidply-icon');\n if (!icon) return;\n const isEnabled = this.player.state.captionsEnabled;\n\n icon.innerHTML = isEnabled ?\n createIconElement('captions').innerHTML :\n createIconElement('captionsOff').innerHTML;\n\n }\n\n createTranscriptButton() {\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-transcript`,\n attributes: {\n 'type': 'button',\n 'aria-label': i18n.t('player.transcript'),\n 'aria-expanded': 'false'\n }\n });\n\n button.appendChild(createIconElement('transcript'));\n\n button.addEventListener('click', async () => {\n await this.player.toggleTranscript();\n this.updateTranscriptButton();\n });\n\n this.controls.transcript = button;\n return button;\n }\n\n updateTranscriptButton() {\n if (!this.controls.transcript) return;\n\n const isVisible = this.player.transcriptManager && this.player.transcriptManager.isVisible;\n this.controls.transcript.setAttribute('aria-expanded', isVisible ? 'true' : 'false');\n }\n\n createHelpButton() {\n const ariaLabel = i18n.t('help.button');\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-help`,\n attributes: {\n 'type': 'button',\n 'aria-label': ariaLabel,\n 'aria-haspopup': 'dialog'\n }\n });\n\n button.appendChild(createIconElement('help'));\n DOMUtils.attachTooltip(button, ariaLabel, this.player.options.classPrefix);\n\n button.addEventListener('click', () => {\n this.player.toggleKeyboardHelp();\n });\n\n this.controls.help = button;\n return button;\n }\n\n createAudioDescriptionButton() {\n const ariaLabel = i18n.t('player.audioDescription');\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-audio-description`,\n attributes: {\n 'type': 'button',\n 'aria-label': ariaLabel,\n 'role': 'switch',\n 'aria-checked': 'false'\n }\n });\n\n button.appendChild(createIconElement('audioDescription'));\n DOMUtils.attachTooltip(button, ariaLabel, this.player.options.classPrefix);\n\n button.addEventListener('click', async () => {\n await this.player.toggleAudioDescription();\n this.updateAudioDescriptionButton();\n });\n\n this.controls.audioDescription = button;\n return button;\n }\n\n updateAudioDescriptionButton() {\n if (!this.controls.audioDescription) return;\n\n const icon = this.controls.audioDescription.querySelector('.vidply-icon');\n const isEnabled = this.player.state.audioDescriptionEnabled;\n\n if (icon) {\n icon.innerHTML = isEnabled ?\n createIconElement('audioDescriptionOn').innerHTML :\n createIconElement('audioDescription').innerHTML;\n }\n\n this.controls.audioDescription.setAttribute('aria-checked', isEnabled ? 'true' : 'false');\n // Keep aria-label static - let aria-checked convey the state for switch role\n // The label describes what the control is, not what action it performs\n }\n\n createSignLanguageButton() {\n const ariaLabel = i18n.t('player.signLanguage');\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-sign-language`,\n attributes: {\n 'type': 'button',\n 'aria-label': ariaLabel,\n 'aria-expanded': 'false'\n }\n });\n\n button.appendChild(createIconElement('signLanguagePip'));\n DOMUtils.attachTooltip(button, ariaLabel, this.player.options.classPrefix);\n\n button.addEventListener('click', () => {\n this.player.toggleSignLanguage();\n this.updateSignLanguageButton();\n });\n\n this.controls.signLanguage = button;\n return button;\n }\n\n updateSignLanguageButton() {\n if (!this.controls.signLanguage) return;\n\n const icon = this.controls.signLanguage.querySelector('.vidply-icon');\n const isEnabled = this.player.state.signLanguageEnabled;\n\n if (icon) {\n icon.innerHTML = isEnabled ?\n createIconElement('signLanguagePipOn').innerHTML :\n createIconElement('signLanguagePip').innerHTML;\n }\n\n this.controls.signLanguage.setAttribute('aria-expanded', isEnabled ? 'true' : 'false');\n this.controls.signLanguage.setAttribute('aria-label',\n isEnabled ? i18n.t('signLanguage.hide') : i18n.t('signLanguage.show')\n );\n }\n\n /**\n * Create sign language in main view button (src swap, like audio description)\n */\n createSignLanguageInMainViewButton() {\n const ariaLabel = i18n.t('signLanguage.showInMainView');\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-sign-language-main-view`,\n attributes: {\n 'type': 'button',\n 'aria-label': ariaLabel,\n 'aria-pressed': 'false'\n }\n });\n\n button.appendChild(createIconElement('signLanguage'));\n DOMUtils.attachTooltip(button, ariaLabel, this.player.options.classPrefix);\n\n button.addEventListener('click', () => {\n if (this.player.signLanguageManager) {\n this.player.signLanguageManager.toggleInMainView();\n }\n });\n\n this.controls.signLanguageMainView = button;\n return button;\n }\n\n /**\n * Update sign language in main view button state\n */\n updateSignLanguageInMainViewButton() {\n const btn = this.controls.signLanguageMainView;\n if (!btn) return;\n\n const isEnabled = this.player.state.signLanguageInMainView;\n const newLabel = isEnabled ? i18n.t('signLanguage.hideInMainView') : i18n.t('signLanguage.showInMainView');\n const iconName = isEnabled ? 'signLanguageOn' : 'signLanguage';\n\n const icon = btn.querySelector('.vidply-icon');\n if (icon) {\n icon.innerHTML = createIconElement(iconName).innerHTML;\n }\n btn.setAttribute('aria-pressed', String(isEnabled));\n btn.setAttribute('aria-label', newLabel);\n \n const tooltip = btn.querySelector(`.${this.player.options.classPrefix}-tooltip`);\n if (tooltip) tooltip.textContent = newLabel;\n }\n\n /**\n * Update accessibility buttons visibility based on current track data.\n * Called when loading a new playlist track to show/hide buttons accordingly.\n */\n updateAccessibilityButtons() {\n const hasAudioDescription = this.hasAudioDescription();\n const hasSignLanguage = this.hasSignLanguage();\n \n // Handle Audio Description button\n if (hasAudioDescription) {\n // Create button if it doesn't exist\n if (!this.controls.audioDescription && this.player.options.audioDescriptionButton !== false) {\n const btn = this.createAudioDescriptionButton();\n btn.dataset.overflowPriority = '2';\n btn.dataset.overflowPriorityMobile = '3';\n // Insert before transcript or playlist toggle button\n const transcriptBtn = this.rightButtons.querySelector(`.${this.player.options.classPrefix}-transcript`);\n const playlistBtn = this.rightButtons.querySelector(`.${this.player.options.classPrefix}-playlist-toggle`);\n const insertBefore = transcriptBtn || playlistBtn || null;\n if (insertBefore) {\n this.rightButtons.insertBefore(btn, insertBefore);\n } else {\n this.rightButtons.appendChild(btn);\n }\n // Re-evaluate overflow now that a new button was added.\n this.checkOverflow();\n }\n // Show button\n if (this.controls.audioDescription) {\n this.controls.audioDescription.style.display = '';\n }\n } else {\n // Hide button if no audio description available\n if (this.controls.audioDescription) {\n this.controls.audioDescription.style.display = 'none';\n }\n }\n \n // Handle Sign Language buttons (PiP overlay and/or main view based on displayMode)\n const showSignLanguage = hasSignLanguage && this.player.options.signLanguageButton !== false;\n const classPrefix = this.player.options.classPrefix;\n const displayMode = this.player.options.signLanguageDisplayMode || 'both';\n const showPip = ['pip', 'both'].includes(displayMode);\n const showMain = ['main', 'both'].includes(displayMode);\n \n if (showSignLanguage) {\n // Find insertion point once for both buttons\n const qualityBtn = this.rightButtons.querySelector(`.${classPrefix}-quality`);\n const fullscreenBtn = this.rightButtons.querySelector(`.${classPrefix}-fullscreen`);\n const insertBeforeRef = qualityBtn || fullscreenBtn || null;\n let needsOverflowSetup = false;\n \n // Create PiP button if needed and doesn't exist\n if (showPip && !this.controls.signLanguage) {\n const btn = this.createSignLanguageButton();\n btn.dataset.overflowPriority = '3';\n btn.dataset.overflowPriorityMobile = '3';\n if (insertBeforeRef) {\n this.rightButtons.insertBefore(btn, insertBeforeRef);\n } else {\n this.rightButtons.appendChild(btn);\n }\n needsOverflowSetup = true;\n }\n \n // Create main view button if needed and doesn't exist\n if (showMain && !this.controls.signLanguageMainView) {\n const btn = this.createSignLanguageInMainViewButton();\n btn.dataset.overflowPriority = '3';\n btn.dataset.overflowPriorityMobile = '3';\n // Insert after PiP button or at the reference point\n const afterPip = this.controls.signLanguage?.nextSibling;\n if (afterPip) {\n this.rightButtons.insertBefore(btn, afterPip);\n } else if (insertBeforeRef) {\n this.rightButtons.insertBefore(btn, insertBeforeRef);\n } else {\n this.rightButtons.appendChild(btn);\n }\n needsOverflowSetup = true;\n }\n \n if (needsOverflowSetup) {\n this.checkOverflow();\n }\n \n // Show/hide buttons based on displayMode\n if (this.controls.signLanguage) {\n this.controls.signLanguage.style.display = showPip ? '' : 'none';\n }\n if (this.controls.signLanguageMainView) {\n this.controls.signLanguageMainView.style.display = showMain ? '' : 'none';\n }\n } else {\n // Hide both buttons if no sign language available\n if (this.controls.signLanguage) this.controls.signLanguage.style.display = 'none';\n if (this.controls.signLanguageMainView) this.controls.signLanguageMainView.style.display = 'none';\n }\n }\n\n createSettingsButton() {\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-settings`,\n attributes: {\n 'type': 'button',\n 'aria-label': i18n.t('player.settings')\n }\n });\n\n button.appendChild(createIconElement('settings'));\n\n button.addEventListener('click', () => {\n this.player.showSettings();\n });\n\n return button;\n }\n\n createPipButton() {\n const floating = this.player.options.floating === true;\n const labelKey = floating ? 'player.floatingPlayer' : 'player.pip';\n const prefix = this.player.options.classPrefix;\n // When floating is enabled, add an extra marker class so the CSS\n // media query can hide the button below the feature's minimum\n // viewport width without affecting the native-PiP variant.\n const className = floating\n ? `${prefix}-button ${prefix}-pip ${prefix}-pip-floating`\n : `${prefix}-button ${prefix}-pip`;\n const button = DOMUtils.createElement('button', {\n className,\n attributes: {\n 'type': 'button',\n 'aria-label': i18n.t(labelKey),\n 'aria-pressed': 'false'\n }\n }) as HTMLButtonElement;\n\n button.appendChild(createIconElement('pip'));\n\n button.addEventListener('click', () => {\n if (floating) {\n if (this.player.floatingPlayerManager) {\n this.player.floatingPlayerManager.togglePinned(button);\n }\n } else {\n this.player.togglePiP();\n }\n });\n\n if (floating) {\n // Keep aria-pressed in sync with the floating state so screen\n // readers announce the toggled state correctly.\n this.player.on('floatingchange', (state: 'pinned' | 'auto' | null) => {\n button.setAttribute('aria-pressed', state === 'pinned' ? 'true' : 'false');\n button.classList.toggle(`${this.player.options.classPrefix}-pip-active`, Boolean(state));\n });\n }\n\n return button;\n }\n\n createDownloadButton(downloadUrl: string) {\n const dataset = this.player.element?.dataset || ({} as DOMStringMap);\n\n const format = this.resolveDownloadFormat(downloadUrl);\n const initialSize = this.resolveInitialDownloadSize();\n\n const baseLabel = i18n.t('player.download');\n const initialLabel = buildDownloadLabel({\n baseLabel,\n format,\n sizeBytes: initialSize,\n locale: i18n.getLanguage(),\n withFormatSizeTemplate: i18n.t('player.downloadWithFormatSize'),\n withFormatTemplate: i18n.t('player.downloadWithFormat'),\n withSizeTemplate: i18n.t('player.downloadWithSize')\n });\n\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-download`,\n attributes: {\n 'type': 'button',\n 'aria-label': initialLabel\n }\n }) as HTMLButtonElement;\n\n if (format) button.dataset.vidplyDownloadFormat = format;\n if (initialSize != null) button.dataset.vidplyDownloadSize = String(initialSize);\n\n button.appendChild(createIconElement('download'));\n\n button.addEventListener('click', () => {\n const url = this.player.options.downloadUrl\n || dataset.vidplyDownloadUrl\n || downloadUrl;\n if (!url) return;\n\n const a = document.createElement('a');\n a.href = url;\n a.download = url.split('/').pop()?.split('?')[0] || 'download';\n a.rel = 'noopener';\n a.style.display = 'none';\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n });\n\n // If size wasn't provided, try a HEAD request and update the label/tooltip when it resolves.\n const shouldFetchSize = this.player.options.downloadFetchSize !== false && initialSize == null;\n if (shouldFetchSize) {\n fetchContentLength(downloadUrl).then(sizeBytes => {\n if (sizeBytes == null) return;\n const newLabel = buildDownloadLabel({\n baseLabel,\n format,\n sizeBytes,\n locale: i18n.getLanguage(),\n withFormatSizeTemplate: i18n.t('player.downloadWithFormatSize'),\n withFormatTemplate: i18n.t('player.downloadWithFormat'),\n withSizeTemplate: i18n.t('player.downloadWithSize')\n });\n button.dataset.vidplyDownloadSize = String(sizeBytes);\n this.updateDownloadButtonLabel(button, newLabel);\n });\n }\n\n return button;\n }\n\n /**\n * Resolve the human-readable file format (e.g. \"MP4\") for the download\n * button from options, data attributes, the matching <source type>, or\n * the URL extension. Returns null when nothing can be determined.\n */\n resolveDownloadFormat(downloadUrl: string): string | null {\n const dataset = this.player.element?.dataset || ({} as DOMStringMap);\n\n const explicit = this.player.options.downloadFormat\n || dataset.vidplyDownloadFormat\n || null;\n if (explicit) return explicit;\n\n const sourceEls = this.player.element?.querySelectorAll\n ? Array.from(this.player.element.querySelectorAll('source')) as HTMLSourceElement[]\n : [];\n const matching = sourceEls.find(s => (s.getAttribute('src') || s.src || '') === downloadUrl);\n const candidate = matching || sourceEls[0];\n if (candidate) {\n const fromMime = inferFormatFromMime(candidate.getAttribute('type'));\n if (fromMime) return fromMime;\n }\n\n return inferFormatFromUrl(downloadUrl);\n }\n\n /**\n * Resolve a known file size from options or data attributes (in bytes).\n * Returns null if no value was provided and a HEAD request should run.\n */\n resolveInitialDownloadSize(): number | null {\n const dataset = this.player.element?.dataset || ({} as DOMStringMap);\n const optionSize = this.player.options.downloadFileSize;\n if (typeof optionSize === 'number' && Number.isFinite(optionSize) && optionSize > 0) {\n return optionSize;\n }\n const datasetSize = dataset.vidplyDownloadSize;\n if (datasetSize) {\n const n = Number(datasetSize);\n if (Number.isFinite(n) && n > 0) return n;\n }\n return null;\n }\n\n /**\n * Update both aria-label and the visible tooltip text for the download button.\n */\n updateDownloadButtonLabel(button: HTMLButtonElement, label: string): void {\n if (!button || !label) return;\n button.setAttribute('aria-label', label);\n const tooltip = button.querySelector(`.${this.player.options.classPrefix}-tooltip`);\n if (tooltip) {\n tooltip.textContent = label;\n }\n }\n\n createFullscreenButton() {\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-fullscreen`,\n attributes: {\n 'type': 'button',\n 'aria-label': i18n.t('player.fullscreen')\n }\n });\n\n button.appendChild(createIconElement('fullscreen'));\n\n button.addEventListener('click', () => {\n this.player.toggleFullscreen();\n });\n\n this.controls.fullscreen = button;\n return button;\n }\n\n attachEvents() {\n // Update controls based on player state\n this.player.on('play', () => this.updatePlayPauseButton());\n this.player.on('pause', () => this.updatePlayPauseButton());\n this.player.on('timeupdate', () => this.updateProgress());\n this.player.on('loadedmetadata', () => {\n this.updateDuration();\n this.ensureQualityButton();\n this.updateQualityIndicator();\n // Update preview video source when metadata loads (for playlists)\n this.updatePreviewVideoSource();\n });\n this.player.on('durationchange', () => {\n this.updateDuration();\n });\n this.player.on('sourcechange', () => {\n // Update preview video source when source changes (for playlists)\n this.updatePreviewVideoSource();\n });\n this.player.on('volumechange', () => this.updateVolumeDisplay());\n this.player.on('progress', () => this.updateBuffered());\n this.player.on('playbackspeedchange', () => this.updateSpeedDisplay());\n this.player.on('fullscreenchange', () => this.updateFullscreenButton());\n this.player.on('captionsenabled', () => this.updateCaptionsButton());\n this.player.on('captionsdisabled', () => this.updateCaptionsButton());\n this.player.on('audiodescriptionenabled', () => this.updateAudioDescriptionButton());\n this.player.on('audiodescriptiondisabled', () => this.updateAudioDescriptionButton());\n this.player.on('signlanguageenabled', () => this.updateSignLanguageButton());\n this.player.on('signlanguagedisabled', () => this.updateSignLanguageButton());\n this.player.on('signlanguageinmainviewenabled', () => this.updateSignLanguageInMainViewButton());\n this.player.on('signlanguageinmainviewdisabled', () => this.updateSignLanguageInMainViewButton());\n this.player.on('qualitychange', () => this.updateQualityIndicator());\n this.player.on('hlslevelswitched', () => this.updateQualityIndicator());\n this.player.on('hlsmanifestparsed', () => {\n this.ensureQualityButton();\n this.updateQualityIndicator();\n });\n this.player.on('dashqualitychanged', () => this.updateQualityIndicator());\n this.player.on('dashmanifestparsed', () => {\n this.ensureQualityButton();\n this.updateQualityIndicator();\n });\n }\n\n updatePlayPauseButton() {\n if (!this.controls.playPause) return;\n\n const icon = this.controls.playPause.querySelector('.vidply-icon');\n const isPlaying = this.player.state.playing;\n\n if (icon) {\n icon.innerHTML = isPlaying ?\n createIconElement('pause').innerHTML :\n createIconElement('play').innerHTML;\n }\n\n const newAriaLabel = isPlaying ? i18n.t('player.pause') : i18n.t('player.play');\n this.controls.playPause.setAttribute('aria-label', newAriaLabel);\n // Update tooltip to match aria-label\n DOMUtils.attachTooltip(this.controls.playPause, newAriaLabel, this.player.options.classPrefix);\n }\n\n updateProgress() {\n if (!this.controls.played) return;\n\n const currentTime = this.player.state.currentTime || 0;\n const duration = this.player.state.duration || 0;\n \n // Guard against division by zero and ensure valid percentage\n const percent = duration > 0 ? Math.min(100, Math.max(0, (currentTime / duration) * 100)) : 0;\n \n this.controls.played.style.width = `${percent}%`;\n if (this.controls.progress) {\n this.controls.progress.setAttribute('aria-valuenow', String(Math.round(percent)));\n\n // Set aria-valuetext to announce both percentage and time for screen readers\n const currentTimeText = TimeUtils.formatDuration(this.player.state.currentTime);\n const durationText = TimeUtils.formatDuration(this.player.state.duration);\n this.controls.progress.setAttribute('aria-valuetext',\n `${Math.round(percent)}%, ${currentTimeText} ${i18n.t('time.of')} ${durationText}`);\n }\n\n if (this.controls.currentTimeVisual) {\n const currentTime = this.player.state.currentTime;\n // Update visual text (hidden from screen readers)\n this.controls.currentTimeVisual.textContent = TimeUtils.formatTime(currentTime);\n if (this.controls.currentTimeAccessible) {\n this.controls.currentTimeAccessible.textContent = TimeUtils.formatDuration(currentTime);\n }\n }\n }\n\n updateDuration() {\n if (this.controls.durationVisual) {\n const duration = this.player.state.duration;\n // Update visual text (hidden from screen readers)\n this.controls.durationVisual.textContent = TimeUtils.formatTime(duration);\n if (this.controls.durationAccessible) {\n this.controls.durationAccessible.textContent = i18n.t('time.durationPrefix') + TimeUtils.formatDuration(duration);\n }\n }\n }\n\n updateVolumeDisplay() {\n const percent = this.player.state.volume * 100;\n\n // Update volume fill bar if it exists\n if (this.controls.volumeFill) {\n this.controls.volumeFill.style.height = `${percent}%`;\n }\n \n // Update volume slider aria-valuenow + aria-valuetext if it exists.\n if (this.controls.volumeSlider) {\n const rounded = Math.round(percent);\n this.controls.volumeSlider.setAttribute('aria-valuenow', String(rounded));\n this.controls.volumeSlider.setAttribute(\n 'aria-valuetext',\n this.player.state.muted\n ? i18n.t('player.muted')\n : i18n.t('player.volumePercent', { percent: rounded })\n );\n }\n\n // Update mute button icon (should always work even if slider not shown)\n if (this.controls.mute) {\n const icon = this.controls.mute.querySelector('.vidply-icon');\n if (icon) {\n let iconName;\n\n if (this.player.state.muted || this.player.state.volume === 0) {\n iconName = 'volumeMuted';\n } else if (this.player.state.volume < 0.3) {\n iconName = 'volumeLow';\n } else if (this.player.state.volume < 0.7) {\n iconName = 'volumeMedium';\n } else {\n iconName = 'volumeHigh';\n }\n\n icon.innerHTML = createIconElement(iconName).innerHTML;\n\n // On touch devices the button toggles mute directly, so label reflects that action.\n // On desktop the button opens the volume slider, so the label stays \"Volume\"\n // and we append the current percentage for quick visual feedback.\n const volumePercent = this.player.state.muted ? 0 : Math.round(percent);\n const newMuteAriaLabel = this.isTouchDevice()\n ? (this.player.state.muted ? i18n.t('player.unmute') : i18n.t('player.mute'))\n : `${i18n.t('player.volume')} ${volumePercent}%`;\n this.controls.mute.setAttribute('aria-label', newMuteAriaLabel);\n // Update tooltip to match aria-label\n DOMUtils.attachTooltip(this.controls.mute, newMuteAriaLabel, this.player.options.classPrefix);\n }\n }\n\n }\n\n updateBuffered() {\n if (!this.controls.buffered || !this.player.element.buffered || this.player.element.buffered.length === 0) return;\n\n const buffered = this.player.element.buffered.end(this.player.element.buffered.length - 1);\n const percent = (buffered / this.player.state.duration) * 100;\n this.controls.buffered.style.width = `${percent}%`;\n }\n\n updateSpeedDisplay() {\n if (this.controls.speedText) {\n this.controls.speedText.textContent = `${this.player.state.playbackSpeed}x`;\n }\n }\n\n updateFullscreenButton() {\n if (!this.controls.fullscreen) return;\n\n const icon = this.controls.fullscreen.querySelector('.vidply-icon');\n const isFullscreen = this.player.state.fullscreen;\n\n if (icon) {\n icon.innerHTML = isFullscreen ?\n createIconElement('fullscreenExit').innerHTML :\n createIconElement('fullscreen').innerHTML;\n }\n\n this.controls.fullscreen.setAttribute('aria-label',\n isFullscreen ? i18n.t('player.exitFullscreen') : i18n.t('player.fullscreen')\n );\n }\n\n /**\n * Ensure quality button exists if qualities are available\n * This is called after renderer initialization to dynamically add the button\n */\n ensureQualityButton() {\n // Skip if quality button is disabled\n if (!this.player.options.qualityButton) return;\n \n // Skip if button already exists\n if (this.controls.quality) return;\n \n // Check if qualities are now available\n if (!this.hasQualityLevels()) return;\n \n // Create and insert the quality button before the speed button\n const qualityButton = this.createQualityButton();\n \n // Find the speed button or caption style button to insert before\n const speedButton = this.rightButtons.querySelector(`.${this.player.options.classPrefix}-speed`);\n const captionStyleButton = this.rightButtons.querySelector(`.${this.player.options.classPrefix}-caption-style`);\n const insertBefore = captionStyleButton || speedButton;\n \n if (insertBefore) {\n this.rightButtons.insertBefore(qualityButton, insertBefore);\n } else {\n // If no reference button, add it at the beginning of right buttons\n this.rightButtons.insertBefore(qualityButton, this.rightButtons.firstChild);\n }\n \n this.player.log('Quality button added dynamically', 'info');\n }\n\n /**\n * Dynamically add captions button if HLS subtitle tracks become available\n * Button order: Chapters, Captions, Caption Style, Speed, AD, Transcript, Playlist, Sign, Quality, PiP, Fullscreen\n */\n ensureCaptionsButton() {\n // Skip if captions button is disabled\n if (!this.player.options.captionsButton) return;\n \n // Skip if button already exists\n if (this.controls.captions) return;\n \n // Create the captions button\n const btn = this.createCaptionsButton();\n btn.dataset.overflowPriority = '1';\n btn.dataset.overflowPriorityMobile = '3';\n \n // Insert after chapters button, or at the start\n const chaptersButton = this.rightButtons.querySelector(`.${this.player.options.classPrefix}-chapters`);\n if (chaptersButton && chaptersButton.nextSibling) {\n this.rightButtons.insertBefore(btn, chaptersButton.nextSibling);\n } else if (chaptersButton) {\n chaptersButton.after(btn);\n } else {\n this.rightButtons.insertBefore(btn, this.rightButtons.firstChild);\n }\n \n this.player.log('Captions button added dynamically for HLS subtitles', 'info');\n }\n\n /**\n * Dynamically add caption style button if HLS subtitle tracks become available\n */\n ensureCaptionStyleButton() {\n // Skip if caption style button is disabled\n if (!this.player.options.captionStyleButton) return;\n \n // Skip if button already exists\n if (this.controls.captionStyle) return;\n \n // Create the caption style button\n const btn = this.createCaptionStyleButton();\n btn.dataset.overflowPriority = '3';\n btn.dataset.overflowPriorityMobile = '3';\n \n // Insert after captions button\n const captionsButton = this.rightButtons.querySelector(`.${this.player.options.classPrefix}-captions-button`);\n if (captionsButton) {\n captionsButton.after(btn);\n } else {\n // Insert at start if no captions button\n this.rightButtons.insertBefore(btn, this.rightButtons.firstChild);\n }\n \n this.player.log('Caption style button added dynamically for HLS subtitles', 'info');\n }\n\n /**\n * Dynamically add transcript button if HLS subtitle tracks become available\n */\n ensureTranscriptButton() {\n // Skip if transcript button is disabled\n if (!this.player.options.transcriptButton) return;\n \n // Skip if button already exists\n if (this.controls.transcript) return;\n \n // Create the transcript button\n const btn = this.createTranscriptButton();\n btn.dataset.overflowPriority = '3';\n btn.dataset.overflowPriorityMobile = '3';\n \n // Insert after AD button, or after speed button\n const adButton = this.rightButtons.querySelector(`.${this.player.options.classPrefix}-audio-description`);\n const speedButton = this.rightButtons.querySelector(`.${this.player.options.classPrefix}-speed`);\n const captionStyleButton = this.rightButtons.querySelector(`.${this.player.options.classPrefix}-caption-style`);\n \n if (adButton) {\n adButton.after(btn);\n } else if (speedButton) {\n speedButton.after(btn);\n } else if (captionStyleButton) {\n captionStyleButton.after(btn);\n } else {\n // Insert before quality/pip/fullscreen buttons\n const qualityButton = this.rightButtons.querySelector(`.${this.player.options.classPrefix}-quality`);\n const pipButton = this.rightButtons.querySelector(`.${this.player.options.classPrefix}-pip`);\n const fullscreenButton = this.rightButtons.querySelector(`.${this.player.options.classPrefix}-fullscreen`);\n const insertBefore = qualityButton || pipButton || fullscreenButton;\n \n if (insertBefore) {\n this.rightButtons.insertBefore(btn, insertBefore);\n } else {\n this.rightButtons.appendChild(btn);\n }\n }\n \n this.player.log('Transcript button added dynamically for HLS subtitles', 'info');\n }\n\n /**\n * Remove caption-related buttons if no HLS subtitle tracks are available\n * and no native caption tracks exist. Called when switching to a stream\n * without subtitles.\n * @param {boolean} force - If true, skip the native captions check and force removal\n */\n removeHlsCaptionButtons(force = false) {\n if (!force) {\n // Check if there are native caption tracks (from <track> elements in the HTML)\n // Note: For HLS streams, we pass force=true because HLS manages its own text tracks\n const trackElements = this.player.element.querySelectorAll('track[kind=\"captions\"], track[kind=\"subtitles\"]');\n if (trackElements.length > 0) {\n this.player.log('Keeping caption buttons - native track elements exist', 'info');\n return;\n }\n }\n \n // Disable all text tracks and clear captions display\n this.disableAllCaptions();\n \n // Remove captions button if it exists\n if (this.controls.captions) {\n this.controls.captions.remove();\n delete this.controls.captions;\n this.player.log('Captions button removed - no subtitle tracks', 'info');\n }\n\n // Remove caption style button if it exists\n if (this.controls.captionStyle) {\n this.controls.captionStyle.remove();\n delete this.controls.captionStyle;\n this.player.log('Caption style button removed - no subtitle tracks', 'info');\n }\n\n // Remove transcript button if it exists\n if (this.controls.transcript) {\n this.controls.transcript.remove();\n delete this.controls.transcript;\n this.player.log('Transcript button removed - no subtitle tracks', 'info');\n }\n }\n\n /**\n * Disable all caption/subtitle tracks and clear the captions display\n */\n disableAllCaptions() {\n // Disable all text tracks\n const textTracks = this.player.element.textTracks;\n for (let i = 0; i < textTracks.length; i++) {\n const tt = textTracks[i];\n if (tt) tt.mode = 'disabled';\n }\n\n // NOTE: A previous (`captionsManager`) cast lived here. The property\n // does not exist on Player and the only related manager (`captionManager`)\n // has no `hide()` method, so the block was unreachable dead code. The\n // captions container is cleared below, which was the actual intent.\n\n // Clear the captions container if it exists\n const captionsContainer = this.player.container?.querySelector(`.${this.player.options.classPrefix}-captions`);\n if (captionsContainer) {\n captionsContainer.textContent = '';\n (captionsContainer as HTMLElement).style.display = 'none';\n }\n \n // Update player state\n this.player.state.captionsEnabled = false;\n \n this.player.log('All captions disabled and cleared', 'info');\n }\n\n updateQualityIndicator() {\n if (!this.controls.qualityText) return;\n if (!this.player.renderer || !this.player.renderer.getQualities) return;\n\n const qualities = this.player.renderer.getQualities();\n if (qualities.length === 0) {\n this.controls.qualityText.textContent = '';\n return;\n }\n\n // Get current quality\n let currentQualityText = '';\n \n // Check if renderer is in auto quality mode (HLS, DASH, etc.)\n if (typeof this.player.renderer.isAutoQuality === 'function' && this.player.renderer.isAutoQuality()) {\n currentQualityText = 'Auto';\n } else if (this.player.renderer.getCurrentQuality) {\n const currentIndex = this.player.renderer.getCurrentQuality();\n const currentQuality = (qualities as QualityLevel[]).find((q) => q.index === currentIndex);\n if (currentQuality) {\n currentQualityText = currentQuality.height ? `${currentQuality.height}p` : '';\n }\n }\n\n this.controls.qualityText.textContent = currentQualityText;\n }\n\n setupAutoHide() {\n if (this.player.element.tagName !== 'VIDEO') return;\n\n const showControls = () => {\n this.element.classList.add(`${this.player.options.classPrefix}-controls-visible`);\n this.player.container.classList.add(`${this.player.options.classPrefix}-controls-visible`);\n this.player.state.controlsVisible = true;\n\n clearTimeout(this.hideTimeout);\n\n if (this.player.state.playing) {\n // Use longer delay in fullscreen mode for better mobile UX\n const delay = this.player.state.fullscreen \n ? this.player.options.hideControlsDelay * 1.5 \n : this.player.options.hideControlsDelay;\n \n this.hideTimeout = setTimeout(() => {\n this.element.classList.remove(`${this.player.options.classPrefix}-controls-visible`);\n this.player.container.classList.remove(`${this.player.options.classPrefix}-controls-visible`);\n this.player.state.controlsVisible = false;\n }, delay);\n }\n };\n\n // Mouse and touch events to show controls\n this.player.container.addEventListener('mousemove', showControls);\n this.player.container.addEventListener('touchstart', showControls);\n this.player.container.addEventListener('touchmove', showControls); // Also show on touch drag/swipe\n this.player.container.addEventListener('click', showControls);\n this.player.container.addEventListener('tap', showControls); // Some mobile browsers use tap event\n\n // Show controls on focus\n this.element.addEventListener('focusin', showControls);\n\n // Always show when paused\n this.player.on('pause', () => {\n showControls();\n clearTimeout(this.hideTimeout);\n });\n\n this.player.on('play', () => {\n showControls();\n });\n\n // Show controls when entering fullscreen (especially important for mobile landscape)\n this.player.on('enterfullscreen', () => {\n showControls();\n // In fullscreen, keep controls visible longer initially\n if (this.player.state.fullscreen) {\n clearTimeout(this.hideTimeout);\n this.hideTimeout = setTimeout(() => {\n if (this.player.state.playing) {\n this.element.classList.remove(`${this.player.options.classPrefix}-controls-visible`);\n this.player.container.classList.remove(`${this.player.options.classPrefix}-controls-visible`);\n this.player.state.controlsVisible = false;\n }\n }, this.player.options.hideControlsDelay * 2); // Double the delay in fullscreen\n }\n });\n\n // Initial state\n showControls();\n }\n\n createOverflowMenuButton() {\n const ariaLabel = i18n.t('player.moreOptions');\n const button = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-button ${this.player.options.classPrefix}-overflow-menu`,\n attributes: {\n 'type': 'button',\n 'aria-label': ariaLabel,\n 'aria-expanded': 'false'\n }\n });\n\n button.appendChild(createIconElement('moreVertical'));\n DOMUtils.attachTooltip(button, ariaLabel, this.player.options.classPrefix);\n\n button.addEventListener('click', () => {\n this.showOverflowMenu(button);\n });\n\n this.controls.overflowMenu = button;\n return button;\n }\n\n showOverflowMenu(button: HTMLElement) {\n // Remove existing menu if any (toggle behavior)\n const existingMenu = document.querySelector(`.${this.player.options.classPrefix}-overflow-menu-list`);\n if (existingMenu) {\n existingMenu.remove();\n button.setAttribute('aria-expanded', 'false');\n if (this.openMenu === existingMenu) {\n this.openMenu = null;\n this.openMenuButton = null;\n }\n return;\n }\n\n const menu = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-overflow-menu-list ${this.player.options.classPrefix}-menu`,\n attributes: {\n 'role': 'menu',\n 'aria-label': i18n.t('player.moreOptions')\n }\n });\n\n // Get all overflow buttons (those currently hidden)\n const overflowButtons: HTMLElement[] = Array.from(this.rightButtons.querySelectorAll('button[data-in-overflow=\"true\"]'));\n\n if (overflowButtons.length === 0) {\n // No overflow items — non-operable, kept out of roving navigation\n // via aria-disabled (WCAG 2.1.1, 4.1.2).\n const noItemsText = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-menu-item ${this.player.options.classPrefix}-menu-item-disabled`,\n textContent: i18n.t('player.noMoreOptions'),\n attributes: {\n 'role': 'menuitem',\n 'aria-disabled': 'true',\n 'tabindex': '-1'\n },\n style: {opacity: '0.5', cursor: 'default'}\n });\n menu.appendChild(noItemsText);\n } else {\n // Create menu items for each overflow button\n overflowButtons.forEach((btn: HTMLElement) => {\n const item = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-menu-item`,\n attributes: {\n 'type': 'button',\n 'role': 'menuitem',\n 'tabindex': '-1'\n }\n });\n\n // Get button label\n const label = btn.getAttribute('aria-label') || btn.getAttribute('title') || '';\n \n // Copy icon if present (SVG icon or text icon like \"Aa\")\n const icon = btn.querySelector('.vidply-icon');\n if (icon) {\n const iconClone = icon.cloneNode(true);\n item.appendChild(iconClone);\n } else {\n // Check for text icon (like caption styling \"Aa\")\n const firstChild = btn.querySelector('span');\n if (firstChild && firstChild.textContent && firstChild.textContent.length <= 3) {\n const iconClone = firstChild.cloneNode(true) as HTMLElement;\n iconClone.classList.add('vidply-icon');\n item.appendChild(iconClone);\n }\n }\n \n const labelSpan = DOMUtils.createElement('span', {\n textContent: label\n });\n item.appendChild(labelSpan);\n\n // When clicked, trigger the original button's click\n item.addEventListener('click', () => {\n // Store the overflow menu item as the positioning reference\n // This allows submenus to position relative to the visible menu item\n this._overflowMenuItemRef = item;\n \n // Temporarily make the original button visible for positioning\n const originalDisplay = btn.style.display;\n btn.style.display = '';\n btn.style.visibility = 'hidden'; // Keep it invisible but in layout\n \n // Trigger the button's menu\n btn.click();\n \n // Restore original state after menu is positioned\n setTimeout(() => {\n btn.style.display = originalDisplay;\n btn.style.visibility = '';\n this._overflowMenuItemRef = null;\n }, 100);\n \n // Close overflow menu\n this.closeMenuAndReturnFocus(menu, button);\n });\n\n menu.appendChild(item);\n });\n\n // Add keyboard navigation\n this.attachMenuKeyboardNavigation(menu, button);\n\n // Focus first item\n setTimeout(() => {\n const firstItem = menu.querySelector(`.${this.player.options.classPrefix}-menu-item`) as HTMLElement | null;\n if (firstItem && firstItem.tagName === 'BUTTON') {\n firstItem.focus({ preventScroll: true });\n }\n }, 0);\n }\n\n // Position menu first (before it's visible) to prevent jumping\n menu.style.visibility = 'hidden';\n menu.style.display = 'block';\n \n // Insert menu into DOM (handles fullscreen positioning)\n this.insertMenuIntoDOM(menu, button);\n \n // Position immediately (synchronously) while hidden\n this.positionMenu(menu, button, true);\n \n // Make menu visible after positioning\n requestAnimationFrame(() => {\n menu.style.visibility = 'visible';\n });\n\n // Close menu on outside click\n this.attachMenuCloseHandler(menu, button);\n }\n\n /**\n * Re-evaluate which buttons fit in the right-side area and which need to\n * be moved into the overflow (\"more options\") menu. Safe to call any\n * number of times — extracted from `setupOverflowDetection` so dynamic\n * button insertions (audio-description / sign-language) can request a\n * recheck without re-attaching observers.\n */\n checkOverflow() {\n const isDesktop = window.innerWidth >= 768;\n const isLandscape = window.innerHeight < window.innerWidth;\n const isFullscreen = this.player.state.fullscreen;\n const isLandscapeFullscreen = isLandscape && isFullscreen;\n\n if (!this.rightButtons || this.rightButtons.children.length === 0) {\n if (this.overflowMenuButton) {\n this.overflowMenuButton.style.display = 'none';\n }\n return;\n }\n\n // Get all buttons (except the overflow menu button itself and\n // buttons that opt out via data-skip-overflow=\"true\", e.g. the\n // floating-player PiP button which has its own viewport media\n // query and must never appear in the overflow menu list).\n const allButtons = (Array.from(this.rightButtons.children) as HTMLElement[]).filter(\n btn => !btn.classList.contains(`${this.player.options.classPrefix}-overflow-menu`)\n && btn.dataset.skipOverflow !== 'true'\n );\n\n if (allButtons.length === 0) {\n if (this.overflowMenuButton) {\n this.overflowMenuButton.style.display = 'none';\n }\n return;\n }\n\n // Only use overflow on mobile portrait (width < 768px and not in landscape).\n // In landscape, always show all buttons so the fullscreen button stays reachable.\n const shouldUseOverflow = !isDesktop && !isLandscape;\n\n if (this.player.options.debug) {\n console.log('Overflow detection:', {\n isDesktop,\n isFullscreen,\n isLandscape,\n isLandscapeFullscreen,\n shouldUseOverflow,\n width: window.innerWidth,\n height: window.innerHeight\n });\n }\n\n if (!shouldUseOverflow) {\n allButtons.forEach(btn => {\n btn.dataset.inOverflow = 'false';\n btn.style.display = '';\n });\n if (this.overflowMenuButton) {\n this.overflowMenuButton.style.display = 'none';\n }\n if (this.player.options.debug) {\n console.log('No overflow menu needed - all buttons visible, overflow button hidden');\n }\n return;\n }\n\n if (this.player.options.debug) {\n console.log('Mobile portrait - checking for overflow...');\n }\n\n allButtons.forEach(btn => {\n btn.style.display = '';\n });\n\n const containerWidth = this.rightButtons.offsetWidth;\n const overflowButtonWidth = 50;\n const availableWidth = containerWidth - overflowButtonWidth;\n\n let totalWidth = 0;\n const buttonWidths = allButtons.map(btn => {\n const style = getComputedStyle(btn);\n const width = btn.offsetWidth +\n parseInt(style.marginLeft || '0') +\n parseInt(style.marginRight || '0');\n totalWidth += width;\n return {btn, width};\n });\n\n const gapWidth = 8;\n totalWidth += (allButtons.length - 1) * gapWidth;\n\n const isSmallScreen = window.innerWidth < 768;\n const needsOverflow = totalWidth > availableWidth || isSmallScreen || (isLandscapeFullscreen && !isDesktop);\n\n if (this.player.options.debug) {\n console.log('Overflow detection:', {\n containerWidth,\n availableWidth,\n totalWidth,\n needsOverflow,\n isSmallScreen,\n reason: isSmallScreen ? 'mobile screen' : (totalWidth > availableWidth ? 'not enough space' : 'enough space'),\n buttonCount: allButtons.length\n });\n }\n\n if (needsOverflow) {\n const priorityAttr = isSmallScreen ? 'overflowPriorityMobile' : 'overflowPriority';\n\n if (this.player.options.debug) {\n console.log(`Using ${isSmallScreen ? 'mobile' : 'desktop'} priorities (width: ${window.innerWidth}px)`);\n }\n\n const sortedButtons = buttonWidths.sort((a, b) => {\n const priorityA = parseInt(a.btn.dataset[priorityAttr] || a.btn.dataset.overflowPriority || '1');\n const priorityB = parseInt(b.btn.dataset[priorityAttr] || b.btn.dataset.overflowPriority || '1');\n return priorityB - priorityA;\n });\n\n let currentWidth = totalWidth;\n let movedToOverflow = 0;\n\n for (const {btn, width} of sortedButtons) {\n const priority = parseInt(btn.dataset[priorityAttr] || btn.dataset.overflowPriority || '1');\n const buttonLabel = btn.getAttribute('aria-label') || 'unknown';\n\n if (priority === 1) {\n btn.dataset.inOverflow = 'false';\n btn.style.display = '';\n continue;\n }\n\n const shouldHide = isSmallScreen ? (priority > 1) : (currentWidth > availableWidth);\n\n if (shouldHide) {\n btn.dataset.inOverflow = 'true';\n btn.style.display = 'none';\n currentWidth -= width;\n movedToOverflow++;\n if (this.player.options.debug) {\n console.log(` → Hiding button: ${buttonLabel} (priority ${priority}, ${isSmallScreen ? 'mobile' : 'desktop'})`);\n }\n } else {\n btn.dataset.inOverflow = 'false';\n btn.style.display = '';\n }\n }\n\n if (this.player.options.debug) {\n console.log('Overflow button exists?', Boolean(this.overflowMenuButton));\n }\n\n if (!this.overflowMenuButton) {\n console.error('Overflow menu button not found!');\n return;\n }\n\n if (movedToOverflow > 0) {\n this.overflowMenuButton.style.display = '';\n if (this.player.options.debug) {\n console.log('Showing overflow menu button -', movedToOverflow, 'buttons moved');\n }\n } else {\n this.overflowMenuButton.style.display = 'none';\n if (this.player.options.debug) {\n console.log('Hiding overflow menu button - all buttons fit');\n }\n }\n } else {\n allButtons.forEach(btn => {\n btn.dataset.inOverflow = 'false';\n btn.style.display = '';\n });\n if (this.overflowMenuButton) {\n this.overflowMenuButton.style.display = 'none';\n }\n }\n }\n\n setupOverflowDetection() {\n const signal = this.player.lifecycleSignal;\n // Every deferred callback checks the lifecycle signal first so a\n // player destroyed within the 500ms initial-check window does not\n // poke at a torn-down ControlBar.\n const checkOverflow = () => {\n if (signal.aborted) return;\n this.checkOverflow();\n };\n\n // Check on resize\n const resizeObserver = new ResizeObserver(() => {\n requestAnimationFrame(checkOverflow);\n });\n resizeObserver.observe(this.rightButtons);\n\n // Check on window resize. Tied to the player's lifecycle so the\n // listener is removed when the player is destroyed.\n window.addEventListener('resize', () => {\n requestAnimationFrame(checkOverflow);\n }, { signal });\n\n // Check on fullscreen changes (important for desktop/tablet fullscreen)\n this.player.on('fullscreenchange', () => {\n // Use setTimeout to ensure fullscreen state is fully updated\n setTimeout(() => {\n requestAnimationFrame(checkOverflow);\n }, 50);\n });\n\n // Initial checks at multiple intervals to ensure layout is stable.\n // Some browsers need more time for font loading, CSS rendering, etc.\n requestAnimationFrame(() => {\n checkOverflow();\n setTimeout(() => checkOverflow(), 100);\n setTimeout(() => checkOverflow(), 300);\n setTimeout(() => checkOverflow(), 500);\n });\n\n // Also check when fonts are loaded\n if (document.fonts && document.fonts.ready) {\n document.fonts.ready.then(() => {\n requestAnimationFrame(checkOverflow);\n });\n }\n\n this.overflowResizeObserver = resizeObserver;\n }\n\n show() {\n this.element.style.display = '';\n }\n\n hide() {\n this.element.style.display = 'none';\n }\n\n /**\n * Update preview video source when player source changes (for playlists)\n * Also re-initializes if preview wasn't set up initially\n */\n updatePreviewVideoSource() {\n const renderer = this.player.renderer;\n if (!renderer || !renderer.media || renderer.media.tagName !== 'VIDEO') {\n return;\n }\n\n // If preview wasn't initialized yet, try to initialize it now\n if (!this.previewSupported && !this.previewVideo) {\n this.initPreviewThumbnail();\n }\n\n if (!this.previewSupported || !this.previewVideo) {\n return;\n }\n\n const mainVideo = renderer.media;\n const newSrc = mainVideo.src || mainVideo.querySelector('source')?.src;\n \n if (newSrc && this.previewVideo.src !== newSrc) {\n // Clear cache when source changes\n this.previewThumbnailCache.clear();\n this.previewVideoReady = false;\n this.previewVideo.src = newSrc;\n \n // Copy crossOrigin if set\n if (mainVideo.crossOrigin) {\n this.previewVideo.crossOrigin = mainVideo.crossOrigin;\n }\n \n // Wait for new source to load\n this.previewVideo.addEventListener('loadedmetadata', () => {\n this.previewVideoReady = true;\n }, { once: true });\n } else if (newSrc && !this.previewVideoReady && this.previewVideo.readyState >= 1) {\n // If source is the same but video is ready, mark as ready\n this.previewVideoReady = true;\n }\n }\n\n /**\n * Cleanup preview thumbnail resources\n */\n cleanupPreviewThumbnail() {\n if (this.previewThumbnailTimeout) {\n clearTimeout(this.previewThumbnailTimeout);\n this.previewThumbnailTimeout = null;\n }\n \n if (this.previewVideo && this.previewVideo.parentNode) {\n this.previewVideo.parentNode.removeChild(this.previewVideo);\n this.previewVideo = null;\n }\n \n this.previewThumbnailCache.clear();\n }\n\n destroy() {\n if (this.hideTimeout) {\n clearTimeout(this.hideTimeout);\n }\n\n if (this.overflowResizeObserver) {\n this.overflowResizeObserver.disconnect();\n }\n\n // Cleanup preview thumbnail resources\n this.cleanupPreviewThumbnail();\n\n if (this.element && this.element.parentNode) {\n this.element.parentNode.removeChild(this.element);\n }\n }\n}\n\n", "/**\n * Keyboard Accessibility Manager\n */\n\nimport { i18n } from '../i18n/i18n.js';\nimport { debounce } from '../utils/PerformanceUtils.js';\nimport type { Player } from '../core/Player.js';\nimport type { KeyboardShortcuts } from '../types/options.js';\n\nexport class KeyboardManager {\n player: Player;\n shortcuts: KeyboardShortcuts;\n private announcer: HTMLElement | null = null;\n // Announcements are driven by player state-change events so they fire for\n // mouse/touch control use too, not only keyboard shortcuts (WCAG 4.1.3).\n // Gated until 'ready' so initial volume/mute/source setup stays silent.\n private _announceReady = false;\n private _prevMuted: boolean;\n private _stateAnnouncers: Array<{ event: string; handler: (...args: unknown[]) => void }> = [];\n private _announceVolume: () => void;\n\n constructor(player: Player) {\n this.player = player;\n this.shortcuts = player.options.keyboardShortcuts;\n this._prevMuted = player.state.muted;\n // Volume slider drags emit a stream of volumechange events; debounce the\n // level announcement so AT hears the final value instead of every step.\n this._announceVolume = debounce(() => {\n const percent = Math.round(this.player.state.volume * 100);\n this.announce(i18n.t('player.volumePercent', { percent }));\n }, 500);\n\n this.init();\n }\n\n init() {\n this.attachEvents();\n this.attachStateAnnouncements();\n }\n\n /**\n * Subscribe to player state-change events so play/pause, mute, volume,\n * captions, fullscreen and speed changes are announced to assistive tech\n * regardless of whether the user used the keyboard, mouse or touch\n * (WCAG 4.1.3 Status Messages).\n */\n attachStateAnnouncements(): void {\n // Defensive: the player exposes an EventEmitter `on`/`off` API at runtime.\n if (typeof this.player.on !== 'function') return;\n\n const onReady = () => { this._announceReady = true; };\n this.player.on('ready', onReady);\n\n const register = (event: string, handler: () => void) => {\n const wrapped = () => { if (this._announceReady) handler(); };\n this.player.on(event as never, wrapped as never);\n this._stateAnnouncers.push({ event, handler: wrapped as (...args: unknown[]) => void });\n };\n\n // Track the 'ready' handler too so destroy() detaches it.\n this._stateAnnouncers.push({ event: 'ready', handler: onReady as (...args: unknown[]) => void });\n\n register('play', () => this.announce(i18n.t('player.playing')));\n register('pause', () => this.announce(i18n.t('player.paused')));\n register('captionsenabled', () => this.announce(i18n.t('player.captionsOn')));\n register('captionsdisabled', () => this.announce(i18n.t('player.captionsOff')));\n register('fullscreenchange', () => {\n this.announce(this.player.state.fullscreen\n ? i18n.t('player.fullscreen')\n : i18n.t('player.exitFullscreen'));\n });\n register('ratechange', () => {\n const rate = this.player.state.playbackSpeed;\n this.announce(i18n.t('player.speedRate', { rate: String(rate) }));\n });\n register('volumechange', () => {\n // Mute toggles are discrete and announced immediately; continuous\n // level changes are debounced via _announceVolume.\n if (this.player.state.muted !== this._prevMuted) {\n this._prevMuted = this.player.state.muted;\n this.announce(this.player.state.muted\n ? i18n.t('player.muted')\n : i18n.t('player.unmuted'));\n return;\n }\n if (!this.player.state.muted) {\n this._announceVolume();\n }\n });\n }\n\n attachEvents() {\n // Listen for keyboard events on the player container using CAPTURE phase\n // This ensures we intercept events before they reach child elements\n this.player.container.addEventListener('keydown', (e: KeyboardEvent) => {\n this.handleKeydown(e);\n }, true); // Use capture phase\n\n // Make player container focusable\n if (!this.player.container.hasAttribute('tabindex')) {\n this.player.container.setAttribute('tabindex', '0');\n }\n }\n\n handleKeydown(e: KeyboardEvent) {\n // Don't handle if target is an input element\n const target = e.target as HTMLElement;\n if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.tagName === 'SELECT') {\n return;\n }\n \n // Don't handle if focus is inside a menu (let menu handle its own keyboard navigation)\n const activeElement = document.activeElement;\n if (activeElement) {\n const menu = activeElement.closest('.vidply-menu, [role=\"menu\"]');\n if (menu) {\n return; // Let the menu handle keyboard events\n }\n \n // Don't handle if focus is on a playlist button (let playlist handle navigation)\n const playlistButton = activeElement.closest('.vidply-playlist-item-button');\n if (playlistButton) {\n return; // Let the playlist handle keyboard events\n }\n\n // Don't steal arrow keys when the sign-language overlay is in keyboard drag/resize mode.\n // DraggableResizable listens on the overlay itself, but we run in CAPTURE phase, so we must opt out here.\n const signWrapper = activeElement.closest('.vidply-sign-language-wrapper');\n if (signWrapper) {\n const draggable = this.player.signLanguageManager?.draggable;\n if (draggable?.keyboardDragMode || draggable?.keyboardResizeMode) {\n return;\n }\n }\n\n // Same idea for the transcript floating window (it also uses DraggableResizable).\n const transcriptWindow = activeElement.closest('.vidply-transcript-window');\n if (transcriptWindow) {\n const draggable = this.player.transcriptManager?.draggableResizable;\n if (draggable?.keyboardDragMode || draggable?.keyboardResizeMode) {\n return;\n }\n }\n }\n\n const key = e.key;\n let handled = false;\n\n // Special handling for ESC key - exit fullscreen (especially for iOS pseudo-fullscreen)\n if (key === 'Escape' && this.player.state.fullscreen) {\n this.player.exitFullscreen();\n e.preventDefault();\n e.stopPropagation();\n return;\n }\n\n // Check each shortcut category\n for (const [action, keys] of Object.entries(this.shortcuts)) {\n if ((keys as string[]).includes(key)) {\n handled = this.executeAction(action, e);\n if (handled) {\n e.preventDefault();\n e.stopPropagation();\n // Announcements are handled centrally by attachStateAnnouncements()\n // via player events, so they fire for pointer interactions too.\n break;\n }\n }\n }\n \n // Log unhandled keys for debugging (in development)\n if (!handled && this.player.options.debug) {\n console.log('[VidPly] Unhandled key:', e.key, 'code:', e.code, 'shiftKey:', e.shiftKey);\n }\n }\n\n executeAction(action: string, _event: KeyboardEvent): boolean {\n switch (action) {\n case 'play-pause':\n this.player.toggle();\n return true;\n\n case 'volume-up':\n this.player.setVolume(Math.min(1, this.player.state.volume + 0.1));\n return true;\n\n case 'volume-down':\n this.player.setVolume(Math.max(0, this.player.state.volume - 0.1));\n return true;\n\n case 'seek-forward':\n this.player.seekForward();\n return true;\n\n case 'seek-backward':\n this.player.seekBackward();\n return true;\n\n case 'mute':\n this.player.toggleMute();\n return true;\n\n case 'fullscreen':\n this.player.toggleFullscreen();\n return true;\n\n case 'captions':\n if (this.player.captionManager && this.player.captionManager.tracks.length > 1) {\n const captionsButton = this.player.controlBar?.controls.captions;\n if (captionsButton && this.player.controlBar) {\n this.player.controlBar.showCaptionsMenu(captionsButton);\n } else {\n this.player.toggleCaptions();\n }\n } else {\n this.player.toggleCaptions();\n }\n return true;\n\n case 'caption-style-menu':\n // Open caption style menu\n if (this.player.controlBar && this.player.controlBar.controls.captionStyle) {\n this.player.controlBar.showCaptionStyleMenu(this.player.controlBar.controls.captionStyle);\n return true;\n }\n return false;\n\n case 'speed-up':\n this.player.setPlaybackSpeed(\n Math.min(2, this.player.state.playbackSpeed + 0.25)\n );\n return true;\n\n case 'speed-down':\n this.player.setPlaybackSpeed(\n Math.max(0.25, this.player.state.playbackSpeed - 0.25)\n );\n return true;\n\n case 'speed-menu':\n // Open speed menu\n if (this.player.controlBar && this.player.controlBar.controls.speed) {\n this.player.controlBar.showSpeedMenu(this.player.controlBar.controls.speed);\n return true;\n }\n return false;\n\n case 'quality-menu':\n // Open quality menu\n if (this.player.controlBar && this.player.controlBar.controls.quality) {\n this.player.controlBar.showQualityMenu(this.player.controlBar.controls.quality);\n return true;\n }\n return false;\n\n case 'chapters-menu':\n // Open chapters menu\n if (this.player.controlBar && this.player.controlBar.controls.chapters) {\n this.player.controlBar.showChaptersMenu(this.player.controlBar.controls.chapters);\n return true;\n }\n return false;\n\n case 'transcript-toggle':\n // Toggle transcript\n if (this.player.transcriptManager) {\n this.player.transcriptManager.toggleTranscript();\n return true;\n }\n return false;\n\n case 'help':\n this.player.toggleKeyboardHelp();\n return true;\n\n default:\n return false;\n }\n }\n\n announceAction(action: string): void {\n if (!this.player.options.screenReaderAnnouncements) return;\n\n // Every announcement now goes through i18n.t so the screen-reader\n // hears the player's configured language instead of always English.\n // Missing keys fall back to the same English literal used previously,\n // so behavior is unchanged for unconfigured locales.\n let message = '';\n\n switch (action) {\n case 'play-pause':\n message = this.player.state.playing\n ? i18n.t('player.playing')\n : i18n.t('player.paused');\n break;\n case 'volume-up':\n case 'volume-down': {\n const percent = Math.round(this.player.state.volume * 100);\n message = i18n.t('player.volumePercent', { percent });\n break;\n }\n case 'mute':\n message = this.player.state.muted\n ? i18n.t('player.muted')\n : i18n.t('player.unmuted');\n break;\n case 'fullscreen':\n message = this.player.state.fullscreen\n ? i18n.t('player.fullscreen')\n : i18n.t('player.exitFullscreen');\n break;\n case 'captions':\n message = this.player.state.captionsEnabled\n ? i18n.t('player.captionsOn')\n : i18n.t('player.captionsOff');\n break;\n case 'speed-up':\n case 'speed-down': {\n const rate = this.player.state.playbackSpeed;\n message = i18n.t('player.speedRate', { rate: String(rate) });\n break;\n }\n }\n\n if (message) {\n this.announce(message);\n }\n }\n\n /**\n * Live-region announcer scoped to *this* player instance so multi-player\n * pages do not cross-talk through a shared `#vidply-announcer` id. The\n * region is appended to `document.body` so it is reachable regardless of\n * the embedding container's stacking / overflow context.\n */\n announce(message: string, priority: 'polite' | 'assertive' = 'polite'): void {\n if (!this.announcer) {\n const id = `vidply-announcer-${this.player.instanceId}`;\n this.announcer = document.createElement('div');\n this.announcer.id = id;\n this.announcer.className = 'vidply-sr-only';\n this.announcer.setAttribute('aria-live', priority);\n this.announcer.setAttribute('aria-atomic', 'true');\n this.announcer.style.cssText = `\n position: absolute;\n left: -10000px;\n width: 1px;\n height: 1px;\n overflow: hidden;\n `;\n document.body.appendChild(this.announcer);\n } else {\n this.announcer.setAttribute('aria-live', priority);\n }\n\n this.announcer.textContent = '';\n const announcer = this.announcer;\n setTimeout(() => {\n if (announcer) announcer.textContent = message;\n }, 100);\n }\n\n updateShortcut(action: string, keys: string[]): void {\n if (Array.isArray(keys)) {\n (this.shortcuts as Record<string, string[]>)[action] = keys;\n }\n }\n\n destroy(): void {\n // Detach the player state-change listeners that drive announcements.\n if (typeof this.player.off === 'function') {\n for (const { event, handler } of this._stateAnnouncers) {\n this.player.off(event as never, handler as never);\n }\n }\n this._stateAnnouncers = [];\n\n // Container-attached keydown listener is removed when the container is\n // destroyed by Player.destroy(). The live-region announcer, however,\n // lives on `document.body` so we must remove it explicitly.\n if (this.announcer && this.announcer.parentNode) {\n this.announcer.parentNode.removeChild(this.announcer);\n }\n this.announcer = null;\n }\n}\n\n", "/**\n * Media Session API integration.\n *\n * Wires the player into `navigator.mediaSession` so the OS shows\n * now-playing metadata (title / artist / album / artwork) and routes\n * hardware/lock-screen/notification controls (play, pause, stop, seek,\n * and previous/next track for playlists) back into the player.\n *\n * Everything is feature-detected and wrapped in try/catch: unsupported\n * browsers and unsupported individual actions degrade silently.\n */\n\nimport type { Player } from './Player.js';\n\ninterface PlaylistLikeTrack {\n title?: string;\n artist?: string;\n poster?: string;\n}\n\nconst POSITION_THROTTLE_MS = 1000;\n\n/**\n * `navigator.mediaSession` is a single, global object shared by the whole\n * document, but a page can host several VidPly players at once (common in a\n * CMS). Only one of them may own the session at a time, otherwise the OS\n * controls would be wired to a player the user isn't actually listening to\n * (clicking play/pause/next then appears to \"do nothing\").\n *\n * We track the owning manager here and follow the browser convention: the\n * player that most recently started playing claims the session. Background\n * players never touch the global session, and a player only releases it on\n * destroy if it still owns it.\n */\nlet activeManager: MediaSessionManager | null = null;\n\nexport class MediaSessionManager {\n player: Player;\n private supported: boolean;\n private handlers: Partial<Record<string, () => void>> = {};\n private boundActions: string[] = [];\n private lastPositionUpdate = 0;\n\n constructor(player: Player) {\n this.player = player;\n this.supported =\n typeof navigator !== 'undefined' && 'mediaSession' in navigator;\n\n if (!this.supported) return;\n\n this.attachEvents();\n\n // Claim the session up front only when no other player owns it yet (i.e.\n // we're the first/only player on the page). When other players already\n // exist we wait until this one actually starts playing before taking\n // over, so we never hijack a player the user is currently using.\n if (activeManager === null) {\n this.claimSession();\n }\n }\n\n /** Does this manager currently own the global media session? */\n private isActive(): boolean {\n return activeManager === this;\n }\n\n /**\n * Take ownership of the global session: (re)register the action handlers so\n * the OS controls drive this player, and refresh metadata/state/position.\n */\n private claimSession(): void {\n if (!this.supported) return;\n activeManager = this;\n this.setupActionHandlers();\n this.updateMetadata();\n this.updatePlaybackState();\n this.updatePositionState(true);\n }\n\n private get session(): MediaSession {\n return navigator.mediaSession;\n }\n\n private setActionHandler(\n action: MediaSessionAction,\n handler: MediaSessionActionHandler | null\n ): void {\n try {\n this.session.setActionHandler(action, handler);\n if (handler && !this.boundActions.includes(action)) {\n this.boundActions.push(action);\n }\n } catch {\n // Action not supported by this browser - ignore.\n }\n }\n\n private setupActionHandlers(): void {\n this.setActionHandler('play', () => this.player.play());\n this.setActionHandler('pause', () => this.player.pause());\n this.setActionHandler('stop', () => this.player.stop());\n\n this.setActionHandler('seekbackward', (details) => {\n this.player.seekBackward(this.offsetFrom(details));\n });\n this.setActionHandler('seekforward', (details) => {\n this.player.seekForward(this.offsetFrom(details));\n });\n this.setActionHandler('seekto', (details) => {\n if (details && typeof details.seekTime === 'number') {\n this.player.seek(details.seekTime);\n }\n });\n\n this.updateTrackHandlers();\n }\n\n private offsetFrom(details?: MediaSessionActionDetails): number | undefined {\n const offset = details && typeof details.seekOffset === 'number' ? details.seekOffset : undefined;\n return typeof offset === 'number' && offset > 0 ? offset : undefined;\n }\n\n /**\n * previous/next track only make sense with a multi-item playlist; bind\n * or clear them whenever the playlist state changes so the OS shows the\n * correct affordances.\n */\n private updateTrackHandlers(): void {\n const pm = this.player.playlistManager;\n const hasPlaylist = Boolean(pm && Array.isArray(pm.tracks) && pm.tracks.length > 1);\n\n if (hasPlaylist && pm) {\n this.setActionHandler('previoustrack', () => pm.previous());\n this.setActionHandler('nexttrack', () => pm.next());\n } else {\n this.setActionHandler('previoustrack', null);\n this.setActionHandler('nexttrack', null);\n }\n }\n\n private attachEvents(): void {\n this.handlers = {\n // Starting playback makes this player the session owner, taking over\n // the OS controls from any other player on the page.\n play: () => this.claimSession(),\n pause: () => {\n if (!this.isActive()) return;\n this.updatePlaybackState();\n this.updatePositionState(true);\n },\n ended: () => {\n if (!this.isActive()) return;\n this.updatePlaybackState();\n },\n timeupdate: () => {\n if (!this.isActive()) return;\n this.updatePositionState();\n },\n durationchange: () => {\n if (!this.isActive()) return;\n this.updatePositionState(true);\n },\n ratechange: () => {\n if (!this.isActive()) return;\n this.updatePositionState(true);\n },\n loadedmetadata: () => {\n if (!this.isActive()) return;\n this.updateMetadata();\n this.updatePositionState(true);\n },\n playlisttrackchange: () => {\n if (!this.isActive()) return;\n this.updateMetadata();\n this.updateTrackHandlers();\n this.updatePositionState(true);\n }\n };\n\n for (const [event, handler] of Object.entries(this.handlers)) {\n if (handler) {\n this.player.on(event as never, handler as never);\n }\n }\n }\n\n private resolveMetadata(): {\n title: string;\n artist: string;\n album: string;\n poster: string | null;\n } {\n const opts = this.player.options;\n let title = opts.title || '';\n let artist = opts.artist || '';\n const album = opts.album || '';\n let poster = opts.poster || null;\n\n const pm = this.player.playlistManager;\n if (pm && Array.isArray(pm.tracks) && pm.currentIndex >= 0) {\n const track = pm.tracks[pm.currentIndex] as PlaylistLikeTrack | undefined;\n if (track) {\n if (track.title) title = track.title;\n if (track.artist) artist = track.artist;\n if (track.poster) poster = track.poster;\n }\n }\n\n if (!title && typeof document !== 'undefined') {\n title = document.title || 'VidPly';\n }\n\n return { title, artist, album, poster };\n }\n\n updateMetadata(): void {\n if (!this.supported || typeof window === 'undefined' || typeof window.MediaMetadata === 'undefined') {\n return;\n }\n\n const { title, artist, album, poster } = this.resolveMetadata();\n const artwork: MediaImage[] = [];\n\n if (poster) {\n try {\n const src = this.player.resolvePosterPath(poster);\n if (src) {\n artwork.push({ src });\n }\n } catch {\n // Ignore poster resolution failures.\n }\n }\n\n try {\n this.session.metadata = new window.MediaMetadata({\n title,\n artist,\n album,\n artwork\n });\n } catch {\n // Ignore metadata construction failures.\n }\n }\n\n updatePlaybackState(): void {\n if (!this.supported) return;\n try {\n this.session.playbackState = this.player.state.playing ? 'playing' : 'paused';\n } catch {\n // Ignore.\n }\n }\n\n /**\n * Push the current position to the OS scrubber. `timeupdate` fires\n * several times a second, so non-forced updates are throttled.\n */\n updatePositionState(force = false): void {\n if (!this.supported || typeof this.session.setPositionState !== 'function') {\n return;\n }\n\n const now = Date.now();\n if (!force && now - this.lastPositionUpdate < POSITION_THROTTLE_MS) {\n return;\n }\n this.lastPositionUpdate = now;\n\n const duration = this.player.state.duration;\n if (!Number.isFinite(duration) || duration <= 0) {\n // A finite, positive duration is required; clear any stale state\n // (e.g. live streams or before metadata is known).\n try {\n this.session.setPositionState();\n } catch {\n // Ignore.\n }\n return;\n }\n\n const playbackRate = this.player.state.playbackSpeed || 1;\n const position = Math.min(Math.max(0, this.player.state.currentTime || 0), duration);\n\n try {\n this.session.setPositionState({\n duration,\n playbackRate: playbackRate > 0 ? playbackRate : 1,\n position\n });\n } catch {\n // Ignore (e.g. position > duration race).\n }\n }\n\n destroy(): void {\n if (!this.supported) return;\n\n for (const [event, handler] of Object.entries(this.handlers)) {\n if (handler) {\n this.player.off(event as never, handler as never);\n }\n }\n this.handlers = {};\n\n // Only tear down the global session if we currently own it; otherwise we\n // would wipe the controls of whichever player is actually playing.\n if (this.isActive()) {\n for (const action of this.boundActions) {\n try {\n this.session.setActionHandler(action as MediaSessionAction, null);\n } catch {\n // Ignore.\n }\n }\n\n try {\n this.session.metadata = null;\n } catch {\n // Ignore.\n }\n try {\n if (typeof this.session.setPositionState === 'function') {\n this.session.setPositionState();\n }\n } catch {\n // Ignore.\n }\n try {\n this.session.playbackState = 'none';\n } catch {\n // Ignore.\n }\n\n activeManager = null;\n }\n\n this.boundActions = [];\n }\n}\n", "/**\n * URL sanitisation helpers for values that flow into `<video>.poster`\n * attributes or CSS `url(...)` declarations.\n *\n * Both helpers used to live as file-local functions inside `Player.ts`,\n * which meant `PlaylistManager` interpolated raw `track.poster` values\n * into `backgroundImage` without any validation. They now live here so\n * every consumer of untrusted poster/artwork URLs can share the same\n * allow-list + CSS escape.\n */\n\n/**\n * Validate a poster/artwork URL before interpolating it into a CSS\n * `url(...)` value or assigning it to `<video>.poster`. Allows `https:`,\n * `http:`, `data:image/<png|jpeg|webp|gif|svg+xml>;...`, root-relative\n * paths starting with `/`, and same-origin relative paths.\n *\n * Returns `null` for anything suspicious — callers must check for null\n * and fall back to a safe default (e.g. skip setting `backgroundImage`).\n */\nexport function sanitizePosterUrl(input: unknown): string | null {\n if (typeof input !== 'string' || input.length === 0 || input.length > 4096) {\n return null;\n }\n const trimmed = input.trim();\n if (!trimmed) return null;\n // Whitespace, quotes, angle brackets and backslashes would let a\n // crafted value break out of a CSS declaration or HTML attribute.\n if (/[\\s\"'<>\\\\]/.test(trimmed)) return null;\n\n if (trimmed.startsWith('/') || trimmed.startsWith('./') || trimmed.startsWith('../')) {\n return trimmed;\n }\n try {\n const url = new URL(trimmed, typeof window !== 'undefined' ? window.location.href : 'http://localhost/');\n if (url.protocol === 'https:' || url.protocol === 'http:') {\n return url.href;\n }\n if (url.protocol === 'data:' && /^data:image\\/(png|jpeg|jpg|webp|gif|svg\\+xml);/i.test(trimmed)) {\n return trimmed;\n }\n } catch {\n return null;\n }\n return null;\n}\n\n/**\n * CSS-escape an already-validated URL for safe interpolation into a\n * `url(...)` value. Defense in depth alongside {@link sanitizePosterUrl}:\n * even if the URL slipped through, the CSS parser cannot leave the\n * `url()` context because `\"`, `(`, `)` and `\\` are all escaped.\n */\nexport function cssEscapeUrl(url: string): string {\n return url.replace(/[\"()\\\\]/g, (m) => `\\\\${m}`);\n}\n\n/**\n * Convenience wrapper: given an unknown input, return a CSS\n * `url(\"escaped-safe-url\")` string ready to assign to\n * `element.style.backgroundImage`, or `null` if the input is unsafe\n * (so the caller can leave the current background untouched instead\n * of clearing it).\n */\nexport function toCssBackgroundImage(input: unknown): string | null {\n const safe = sanitizePosterUrl(input);\n if (!safe) return null;\n return `url(\"${cssEscapeUrl(safe)}\")`;\n}\n", "/**\n * Lazy instantiation helpers for {@link Player}.\n *\n * Extracted from `index.ts` so that:\n *\n * 1. The `Player.observeLazy` public API is a real static method on the\n * Player class rather than a side-effectful assignment done from\n * `index.ts` at module load time.\n * 2. Consumers that want the lazy-init API without the auto-init\n * entry point can import this module explicitly.\n * 3. Both the auto-init flow (`initializePlayers`) and the manual flow\n * (`Player.observeLazy`) share the same `IntersectionObserver`\n * bookkeeping, so cancellation and rect-based eager fallbacks stay\n * consistent.\n */\n\nexport type LazyHandle = { cancel: () => void } | null;\n\nexport interface LazyInitEntry<TOptions> {\n observer: IntersectionObserver;\n options: TOptions;\n}\n\n/** Factory the observer invokes once an element is ready to promote. */\nexport type LazyPlayerFactory<TOptions> = (\n element: HTMLElement,\n options: TOptions\n) => void;\n\nconst pendingByElement = new WeakMap<HTMLElement, LazyInitEntry<unknown>>();\n\n/**\n * Begin observing `element` and construct a player when it scrolls into\n * view (or immediately if its rect is too small to be a meaningful\n * intersection target). Safe to call multiple times — a duplicate call\n * cancels the previous observer first.\n */\nexport function observeForLazyInit<TOptions>(\n element: HTMLElement,\n options: TOptions,\n margin: string,\n factory: LazyPlayerFactory<TOptions>\n): void {\n const existing = pendingByElement.get(element);\n if (existing) {\n existing.observer.unobserve(element);\n pendingByElement.delete(element);\n }\n\n const rect = element.getBoundingClientRect();\n if (rect.height < 20) {\n factory(element, options);\n return;\n }\n\n const observer = new IntersectionObserver(\n (entries) => {\n entries.forEach((entry) => {\n if (entry.isIntersecting) {\n observer.unobserve(entry.target);\n pendingByElement.delete(entry.target as HTMLElement);\n factory(entry.target as HTMLElement, options);\n }\n });\n },\n { rootMargin: margin, threshold: 0 }\n );\n\n observer.observe(element);\n pendingByElement.set(element, { observer, options } as LazyInitEntry<unknown>);\n}\n\n/** Cancel a scheduled lazy init for `element`, if any. */\nexport function cancelLazyInit(element: HTMLElement): void {\n const pending = pendingByElement.get(element);\n if (pending) {\n pending.observer.unobserve(element);\n pendingByElement.delete(element);\n }\n}\n", "/**\n * Pseudo-fullscreen fallback for browsers / platforms (chiefly iOS\n * Safari) where the real Fullscreen API is unavailable for video\n * elements.\n *\n * Extracted from the Player class so the scroll-lock, background-inert\n * and viewport-hijack logic lives in one focussed place. The Player\n * still owns `state.fullscreen` and emits the `fullscreenchange`\n * events — this controller only drives the DOM side effects.\n */\n\nimport type { Player } from './Player.js';\n\nexport class PseudoFullscreenController {\n private readonly player: Player;\n\n // All of the \"remember current style / scroll / viewport\" slots used\n // to restore state on exit. Kept private so the rest of the code\n // base cannot poke into them.\n private originalScrollX?: number;\n private originalScrollY?: number;\n private originalBodyOverflow?: string;\n private originalBodyPosition?: string;\n private originalBodyWidth?: string;\n private originalBodyHeight?: string;\n private originalHtmlOverflow?: string;\n private originalBodyBackground?: string;\n private originalHtmlBackground?: string;\n private originalViewport?: string | null;\n private inertElements: Element[] = [];\n\n constructor(player: Player) {\n this.player = player;\n }\n\n enable(): void {\n const { player } = this;\n player.state.fullscreen = true;\n player.container.classList.add(`${player.options.classPrefix}-fullscreen`);\n\n // CSS fallback for browsers without :has() support.\n document.body.classList.add('vidply-fullscreen-active');\n\n // Stash the current scroll position so we can restore it on exit.\n this.originalScrollX = window.scrollX || window.pageXOffset;\n this.originalScrollY = window.scrollY || window.pageYOffset;\n\n // Prevent body/html from scrolling while the player covers the viewport.\n this.originalBodyOverflow = document.body.style.overflow;\n this.originalBodyPosition = document.body.style.position;\n this.originalBodyWidth = document.body.style.width;\n this.originalBodyHeight = document.body.style.height;\n this.originalHtmlOverflow = document.documentElement.style.overflow;\n this.originalBodyBackground = document.body.style.background;\n this.originalHtmlBackground = document.documentElement.style.background;\n\n document.body.style.overflow = 'hidden';\n document.body.style.width = '100%';\n document.body.style.height = '100%';\n document.body.style.background = '#000';\n document.documentElement.style.overflow = 'hidden';\n document.documentElement.style.background = '#000';\n\n // iOS: reset the viewport scale so the player fills the screen, but never\n // disable user scaling. Setting maximum-scale=1.0 / user-scalable=no would\n // block pinch-zoom and fail WCAG 1.4.4 (Resize Text) and 1.4.10 (Reflow).\n this.originalViewport = document.querySelector('meta[name=\"viewport\"]')?.getAttribute('content');\n const viewport = document.querySelector('meta[name=\"viewport\"]');\n if (viewport) {\n viewport.setAttribute('content', 'width=device-width, initial-scale=1.0');\n }\n\n // Scroll to top on iOS to prevent positioning issues.\n window.scrollTo(0, 0);\n\n // Make the rest of the page inert so keyboard focus cannot escape\n // to background content that is visually covered but still alive.\n this.makeBackgroundInert();\n\n player.emit('fullscreenchange', true);\n player.emit('enterfullscreen');\n }\n\n /**\n * Make every sibling of the player container (walking up to the body)\n * `inert`. Scripts/styles are skipped so layout-time mutations still\n * work. Elements that were already inert are left alone so we don't\n * accidentally clear another author's inert marker on exit.\n *\n * Public because the real Fullscreen API handler also calls it — we\n * need the same inert treatment when the browser grants real\n * fullscreen, not only in the pseudo-fallback path.\n */\n makeBackgroundInert(): void {\n this.inertElements = [];\n let current: HTMLElement | null = this.player.container;\n while (current && current !== document.body && current !== document.documentElement) {\n const parentElement: HTMLElement | null = current.parentElement;\n if (parentElement) {\n Array.from(parentElement.children).forEach((sibling: Element) => {\n if (\n sibling !== current &&\n sibling.nodeType === Node.ELEMENT_NODE &&\n !sibling.hasAttribute('inert') &&\n sibling.tagName !== 'SCRIPT' &&\n sibling.tagName !== 'STYLE' &&\n sibling.tagName !== 'LINK' &&\n sibling.tagName !== 'META'\n ) {\n sibling.setAttribute('inert', '');\n this.inertElements.push(sibling);\n }\n });\n }\n current = parentElement;\n }\n }\n\n /** Public counterpart of {@link makeBackgroundInert}. */\n restoreBackgroundInteractivity(): void {\n if (this.inertElements.length > 0) {\n for (const el of this.inertElements) {\n el.removeAttribute('inert');\n }\n this.inertElements = [];\n }\n }\n\n disable(): void {\n document.body.classList.remove('vidply-fullscreen-active');\n\n this.restoreBackgroundInteractivity();\n\n if (this.originalBodyOverflow !== undefined) {\n document.body.style.overflow = this.originalBodyOverflow;\n this.originalBodyOverflow = undefined;\n }\n if (this.originalBodyPosition !== undefined) {\n document.body.style.position = this.originalBodyPosition;\n this.originalBodyPosition = undefined;\n }\n if (this.originalBodyWidth !== undefined) {\n document.body.style.width = this.originalBodyWidth;\n this.originalBodyWidth = undefined;\n }\n if (this.originalBodyHeight !== undefined) {\n document.body.style.height = this.originalBodyHeight;\n this.originalBodyHeight = undefined;\n }\n if (this.originalHtmlOverflow !== undefined) {\n document.documentElement.style.overflow = this.originalHtmlOverflow;\n this.originalHtmlOverflow = undefined;\n }\n if (this.originalBodyBackground !== undefined) {\n document.body.style.background = this.originalBodyBackground;\n this.originalBodyBackground = undefined;\n }\n if (this.originalHtmlBackground !== undefined) {\n document.documentElement.style.background = this.originalHtmlBackground;\n this.originalHtmlBackground = undefined;\n }\n\n if (this.originalViewport !== undefined) {\n const viewport = document.querySelector('meta[name=\"viewport\"]');\n if (viewport && this.originalViewport !== null) {\n viewport.setAttribute('content', this.originalViewport);\n }\n this.originalViewport = undefined;\n }\n\n if (this.originalScrollX !== undefined && this.originalScrollY !== undefined) {\n window.scrollTo(this.originalScrollX, this.originalScrollY);\n this.originalScrollX = undefined;\n this.originalScrollY = undefined;\n }\n\n this.player.emit('exitfullscreen');\n }\n}\n", "/**\n * Theme + CSS custom-property support extracted from Player.\n *\n * Every path that used to live on `Player.applyTheme` /\n * `Player.setTheme` / `Player.setThemeVariable` / `Player.resetTheme`\n * now routes through this module. Player keeps thin public delegates\n * so existing consumers (`player.setTheme('light')`, etc.) keep\n * working unchanged.\n *\n * The validation rules are deliberately strict: names must start with\n * `--vidply-` followed by `[A-Za-z0-9_-]+`, values must be short and\n * must not contain characters that would let a malicious override\n * escape the declaration or rule. That way a consumer that passes\n * user-supplied text through `setThemeVariable` cannot accidentally\n * turn the player into a CSS-injection sink.\n */\n\nimport { isForbiddenKey } from '../utils/Sanitize.js';\nimport type { Player } from './Player.js';\n\nexport type ThemeName = 'dark' | 'light' | 'minimal' | 'high-contrast';\n\nexport const PLAYER_THEMES: readonly ThemeName[] = ['dark', 'light', 'minimal', 'high-contrast'];\n\n/**\n * Validate a CSS variable name before it reaches `setProperty`. The\n * browser would silently drop unparseable names; the bigger risk is an\n * attacker smuggling `;` / `:` to escape into another declaration, so\n * the allow-list is intentionally narrow.\n */\nexport function isValidThemeVariableName(name: string): boolean {\n return /^--vidply-[A-Za-z0-9_-]{1,64}$/.test(name);\n}\n\nexport function isValidThemeVariableValue(value: unknown): value is string {\n if (typeof value !== 'string') return false;\n if (value.length > 200) return false;\n return !/[<>{};@\\\\]/.test(value);\n}\n\nexport class ThemeManager {\n private readonly player: Player;\n\n constructor(player: Player) {\n this.player = player;\n }\n\n /**\n * Apply `options.theme` and validate-and-apply every entry in\n * `options.themeVariables` to the container. Bad entries are logged\n * and skipped so a single malformed override cannot poison siblings.\n */\n apply(): void {\n const player = this.player;\n if (!player.container) return;\n\n // Clear any previous theme class so switching themes at runtime\n // does not leave two active theme classes on the element.\n const themeClasses = PLAYER_THEMES.map(t => `${player.options.classPrefix}-theme-${t}`);\n player.container.classList.remove(...themeClasses);\n\n const theme = player.options.theme as ThemeName | undefined;\n if (theme && (PLAYER_THEMES as readonly string[]).includes(theme)) {\n player.container.classList.add(`${player.options.classPrefix}-theme-${theme}`);\n }\n\n if (player.options.themeVariables && typeof player.options.themeVariables === 'object') {\n for (const [rawKey, rawValue] of Object.entries(player.options.themeVariables)) {\n if (isForbiddenKey(rawKey)) continue;\n const cssVar = rawKey.startsWith('--vidply-') ? rawKey : `--vidply-${rawKey}`;\n if (!isValidThemeVariableName(cssVar)) {\n player.log(`[VidPly] Ignoring invalid theme variable name: ${rawKey}`, 'warn');\n continue;\n }\n if (!isValidThemeVariableValue(rawValue)) {\n player.log(`[VidPly] Ignoring invalid theme variable value for ${cssVar}`, 'warn');\n continue;\n }\n player.container.style.setProperty(cssVar, rawValue);\n }\n }\n }\n\n /**\n * Swap the active theme at runtime. Emits `themechange` with the old\n * and new names so external consumers (e.g. telemetry) can react.\n */\n set(themeName: ThemeName, customVariables: Record<string, string> = {}): void {\n const player = this.player;\n const previousTheme = player.options.theme;\n\n player.options.theme = themeName;\n\n if (customVariables && Object.keys(customVariables).length > 0) {\n player.options.themeVariables = {\n ...player.options.themeVariables,\n ...customVariables\n };\n }\n\n this.apply();\n\n player.emit('themechange', {\n theme: themeName,\n previousTheme,\n customVariables: player.options.themeVariables\n });\n }\n\n get(): ThemeName | undefined {\n return this.player.options.theme as ThemeName | undefined;\n }\n\n /** Set a single CSS variable override, validating the (name, value)\n * pair before it reaches the DOM. Callers must pass a string value. */\n setVariable(variableName: string, value: string): void {\n const player = this.player;\n if (!player.container) return;\n\n const cssVar = variableName.startsWith('--vidply-')\n ? variableName\n : `--vidply-${variableName}`;\n\n if (!isValidThemeVariableName(cssVar) || !isValidThemeVariableValue(value)) {\n player.log(`[VidPly] Ignoring unsafe setThemeVariable(${variableName})`, 'warn');\n return;\n }\n\n player.container.style.setProperty(cssVar, value);\n\n if (!player.options.themeVariables) {\n player.options.themeVariables = {};\n }\n player.options.themeVariables[variableName] = value;\n }\n\n /**\n * Reset to the default theme (dark) and clear every override that was\n * applied through `options.themeVariables`.\n */\n reset(): void {\n const player = this.player;\n if (player.container && player.options.themeVariables) {\n Object.keys(player.options.themeVariables).forEach(key => {\n const cssVar = key.startsWith('--vidply-') ? key : `--vidply-${key}`;\n player.container.style.removeProperty(cssVar);\n });\n }\n\n const previousTheme = player.options.theme;\n player.options.theme = 'dark';\n player.options.themeVariables = {};\n\n this.apply();\n player.emit('themechange', { theme: 'dark', previousTheme });\n }\n}\n", "/**\n * Poster / artwork helpers lifted out of Player.\n *\n * The manager owns:\n * - relative -> absolute URL resolution;\n * - the canvas-based \"grab a frame from the video as poster\" logic;\n * - the `.vidply-forced-poster` overlay that shows the poster while\n * the underlying `<video>` is in a paused / not-yet-loaded state.\n *\n * Player keeps public delegating methods (`resolvePosterPath`,\n * `showPosterOverlay`, etc.) so the existing external API is\n * unchanged — only the implementation has moved.\n */\n\nimport { captureVideoFrame } from '../utils/VideoFrameCapture.js';\nimport type { Player } from './Player.js';\n\nexport class PosterManager {\n private readonly player: Player;\n\n constructor(player: Player) {\n this.player = player;\n }\n\n /**\n * Convert a relative poster path into an absolute URL. Absolute URLs\n * (http/https) and root-relative paths (`/foo`) are returned as-is.\n * Falls back to the raw string on any parse error — a malformed URL\n * is still better than throwing and breaking the caller.\n */\n resolvePath(posterPath: string | null | undefined): string {\n if (!posterPath) return '';\n\n if (posterPath.match(/^(https?:|\\/)/)) {\n return posterPath;\n }\n\n try {\n const posterUrl = new URL(posterPath, window.location.href);\n return posterUrl.href;\n } catch {\n return posterPath;\n }\n }\n\n /**\n * Capture a frame from the underlying video as a data URL suitable\n * for use as `<video>.poster`. Returns `null` when the element is\n * not a video, the renderer isn't ready, or the capture fails.\n *\n * When the control bar has a hidden \"preview video\" element (used\n * for the seek hover thumbnail), we prefer that so we don't disturb\n * the user's current playback position.\n */\n async generateFromVideo(time = 10): Promise<string | null> {\n const player = this.player;\n if (player.element.tagName !== 'VIDEO') return null;\n\n const renderer = player.renderer;\n if (!renderer || !renderer.media || renderer.media.tagName !== 'VIDEO') {\n return null;\n }\n\n const video = renderer.media as HTMLVideoElement;\n\n if (!video.duration || video.duration < time) {\n time = Math.min(time, Math.max(1, video.duration * 0.1));\n }\n\n let videoToUse: HTMLVideoElement = video;\n if (player.controlBar && player.controlBar.previewVideo && player.controlBar.previewSupported) {\n videoToUse = player.controlBar.previewVideo as HTMLVideoElement;\n }\n\n // If we're using the main video we have to put its currentTime /\n // paused state back; for the preview video nobody is watching so\n // we can skip the restore.\n const restoreState = videoToUse === video;\n return await captureVideoFrame(videoToUse, time, {\n restoreState,\n quality: 0.9\n });\n }\n\n /**\n * Auto-generate a poster from the video at the 10-second mark if the\n * content doesn't already have one. No-op for audio elements and for\n * media that ships with a poster attribute or option.\n */\n async autoGenerate(): Promise<void> {\n const player = this.player;\n\n const hasPoster =\n player.element.getAttribute('poster') ||\n (player.element as HTMLVideoElement).poster ||\n player.options.poster;\n\n if (hasPoster) return;\n if (player.element.tagName !== 'VIDEO') return;\n\n // Wait for metadata so `duration` is known before we seek.\n if (!player.state.duration || player.state.duration === 0) {\n await new Promise<void>((resolve) => {\n const onLoadedMetadata = () => {\n player.element.removeEventListener('loadedmetadata', onLoadedMetadata);\n resolve();\n };\n\n if (player.element.readyState >= 1) {\n resolve();\n } else {\n player.element.addEventListener('loadedmetadata', onLoadedMetadata);\n }\n });\n }\n\n const posterDataURL = await this.generateFromVideo(10);\n\n if (posterDataURL) {\n (player.element as HTMLVideoElement).poster = posterDataURL;\n player.log('Auto-generated poster from video frame at 10 seconds', 'info');\n this.showOverlay();\n }\n }\n\n /**\n * Apply the poster as a CSS background on the video wrapper. This is\n * used to keep the poster visible behind the play button when the\n * browser wouldn't render `<video>.poster` itself (e.g. during\n * fallback / transitional states).\n */\n showOverlay(): void {\n const player = this.player;\n if (!player.videoWrapper || player.element.tagName !== 'VIDEO') return;\n\n const poster =\n player.element.getAttribute('poster') ||\n (player.element as HTMLVideoElement).poster ||\n player.options.poster;\n\n if (!poster) return;\n\n // Data URLs are already absolute and opaque; only normal URLs need\n // resolving into absolute form for the CSS `url(...)` value.\n const resolvedPoster = poster.startsWith('data:') ? poster : this.resolvePath(poster);\n player.videoWrapper.style.setProperty('--vidply-poster-image', `url(\"${resolvedPoster}\")`);\n player.videoWrapper.classList.add('vidply-forced-poster');\n\n // Audio-as-video content uses a wider aspect ratio so the poster\n // doesn't get stretched into a tall letter-boxed frame.\n if (player._isAudioContent && player.container) {\n player.container.classList.add('vidply-audio-content');\n } else if (player.container) {\n player.container.classList.remove('vidply-audio-content');\n }\n }\n\n hideOverlay(): void {\n const player = this.player;\n if (!player.videoWrapper) return;\n\n player.videoWrapper.classList.remove('vidply-forced-poster');\n player.videoWrapper.style.removeProperty('--vidply-poster-image');\n\n // NOTE: `vidply-audio-content` intentionally stays applied — it\n // drives the aspect ratio for the entire duration of audio-in-a-\n // video-element playback, not just while the poster is visible.\n }\n}\n", "/**\n * Resume-playback feature extracted from Player.\n *\n * Owns:\n * - Throttled progress persistence during playback.\n * - \"Where were we?\" detection on `loadedmetadata`.\n * - Clearing persisted progress on `ended`.\n * - The optional resume prompt (accessible modal with two buttons).\n *\n * Because `resumePlayback` is opt-in, the Player only instantiates\n * this manager when the option is enabled — so pages without the\n * feature don't pay for the extra state / event handlers.\n */\n\nimport { DOMUtils } from '../utils/DOMUtils.js';\nimport { i18n } from '../i18n/i18n.js';\nimport { throttle } from '../utils/PerformanceUtils.js';\nimport type { Player } from './Player.js';\n\nexport class ResumeManager {\n private readonly player: Player;\n private saveProgressThrottled: (() => void) | null = null;\n private resumeChecked = false;\n private listenersAttached = false;\n\n constructor(player: Player) {\n this.player = player;\n }\n\n /**\n * Wire up the progress-save + resume-check listeners. Safe to call\n * multiple times: repeat calls are no-ops so a re-init path during\n * source switching doesn't stack duplicate listeners.\n */\n init(): void {\n if (this.listenersAttached) return;\n this.listenersAttached = true;\n\n this.saveProgressThrottled = throttle(() => this.saveProgress(), 5000);\n\n this.player.on('timeupdate', () => {\n if (this.player.state.playing && this.player.state.duration > 0) {\n this.saveProgressThrottled?.();\n }\n });\n\n // First `loadedmetadata` after init is the only place we should\n // offer to resume — after that the user is in charge of seeking.\n this.player.on('loadedmetadata', () => {\n if (!this.resumeChecked) {\n this.resumeChecked = true;\n this.checkForResume();\n }\n });\n\n this.player.on('ended', () => {\n const videoId = this.player.getVideoId();\n if (videoId) {\n this.player.storage.clearWatchProgress(videoId);\n }\n });\n }\n\n /**\n * Persist current playback progress to storage. No-op when the\n * feature is disabled, when the video is too short / at the very\n * start, or when playback is effectively complete.\n */\n saveProgress(): void {\n const player = this.player;\n if (!player.options.resumePlayback) return;\n\n const videoId = player.getVideoId();\n if (!videoId) return;\n\n const currentTime = player.state.currentTime;\n const duration = player.state.duration;\n\n // Resume for tiny clips is more annoying than useful.\n if (duration < 30 || currentTime < (player.options.resumeThreshold as number)) {\n return;\n }\n\n // Near the end? Don't persist — the user effectively finished.\n const percentage = (currentTime / duration) * 100;\n if (percentage > 95) return;\n\n player.storage.saveWatchProgress(videoId, currentTime, duration);\n }\n\n /**\n * Check for a previously-saved resume point for the current video\n * and either auto-resume or show the prompt depending on\n * `options.resumePrompt`. Safe to call manually, e.g. after an\n * external source change.\n */\n checkForResume(): void {\n const player = this.player;\n if (!player.options.resumePlayback) return;\n\n const videoId = player.getVideoId();\n if (!videoId) return;\n\n const progress = player.storage.getWatchProgress(videoId);\n if (!progress) return;\n\n const { currentTime, duration, percentage } = progress;\n const threshold = player.options.resumeThreshold as number;\n\n // Stale / trivial entries get purged instead of offered.\n if (currentTime < threshold || percentage > 95) {\n player.storage.clearWatchProgress(videoId);\n return;\n }\n\n // Duration mismatch usually means the source URL points to a new\n // cut of the same video — better to start fresh than resume at a\n // time that's outside the new clip.\n if (player.state.duration > 0 && Math.abs(player.state.duration - duration) > 5) {\n player.storage.clearWatchProgress(videoId);\n return;\n }\n\n if (player.options.resumePrompt) {\n this.showPrompt(currentTime);\n } else {\n player.seek(currentTime);\n }\n }\n\n /**\n * Format a time value as `mm:ss` (or `hh:mm:ss` once we cross an\n * hour) for display in the resume prompt label. No localisation is\n * needed because the surrounding prompt text is already localised\n * by i18n.\n */\n private formatTime(seconds: number): string {\n const h = Math.floor(seconds / 3600);\n const m = Math.floor((seconds % 3600) / 60);\n const s = Math.floor(seconds % 60);\n\n if (h > 0) {\n return `${h}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;\n }\n return `${m}:${s.toString().padStart(2, '0')}`;\n }\n\n showPrompt(savedTime: number): void {\n const player = this.player;\n if (player.state.resumePromptVisible || !player.container) return;\n\n const formattedTime = this.formatTime(savedTime);\n const promptText = i18n.t('resume.prompt', { time: formattedTime });\n\n player.resumePromptElement = DOMUtils.createElement('div', {\n className: `${player.options.classPrefix}-resume-prompt`,\n attributes: {\n role: 'dialog',\n 'aria-label': promptText,\n 'aria-modal': 'true'\n }\n });\n\n const promptContent = DOMUtils.createElement('div', {\n className: `${player.options.classPrefix}-resume-prompt-content`\n });\n\n const promptMessage = DOMUtils.createElement('p', {\n className: `${player.options.classPrefix}-resume-prompt-message`,\n textContent: promptText\n });\n\n const buttonContainer = DOMUtils.createElement('div', {\n className: `${player.options.classPrefix}-resume-prompt-buttons`\n });\n\n const resumeButton = DOMUtils.createElement('button', {\n className: `${player.options.classPrefix}-resume-prompt-button ${player.options.classPrefix}-resume-prompt-button-primary`,\n textContent: i18n.t('resume.resume'),\n attributes: { type: 'button' }\n });\n resumeButton.addEventListener('click', () => {\n this.hidePrompt();\n player.seek(savedTime);\n player.play();\n });\n\n const startOverButton = DOMUtils.createElement('button', {\n className: `${player.options.classPrefix}-resume-prompt-button`,\n textContent: i18n.t('resume.startOver'),\n attributes: { type: 'button' }\n });\n startOverButton.addEventListener('click', () => {\n this.hidePrompt();\n const videoId = player.getVideoId();\n if (videoId) player.storage.clearWatchProgress(videoId);\n player.seek(0);\n player.play();\n });\n\n const handleKeydown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n e.preventDefault();\n e.stopPropagation();\n this.hidePrompt();\n }\n };\n player.resumePromptElement.addEventListener('keydown', handleKeydown);\n\n buttonContainer.appendChild(resumeButton);\n buttonContainer.appendChild(startOverButton);\n promptContent.appendChild(promptMessage);\n promptContent.appendChild(buttonContainer);\n player.resumePromptElement.appendChild(promptContent);\n\n player.container.appendChild(player.resumePromptElement);\n player.state.resumePromptVisible = true;\n\n // Focus the primary button after the element is attached to the DOM\n // so the dialog is immediately keyboard-navigable.\n requestAnimationFrame(() => { resumeButton.focus(); });\n\n player.emit('resumepromptshow', { savedTime });\n }\n\n hidePrompt(): void {\n const player = this.player;\n if (!player.resumePromptElement) return;\n\n player.resumePromptElement.remove();\n player.resumePromptElement = null;\n player.state.resumePromptVisible = false;\n\n player.emit('resumeprompthide');\n }\n}\n", "/**\n * Resize / orientation / fullscreen handling extracted from Player.\n *\n * Three responsibilities live here:\n *\n * 1. Reacting to container size changes (ResizeObserver when\n * available, `window.resize` fallback otherwise) so the control\n * bar and the transcript panel stay correctly laid out.\n * 2. Listening for orientation changes on mobile so the transcript\n * panel re-positions after the viewport rotates.\n * 3. Tracking native fullscreen changes across vendor prefixes so\n * `state.fullscreen` stays in sync with reality — including the\n * inert/overlay bookkeeping that's shared with the pseudo-\n * fullscreen fallback.\n *\n * Every listener is attached with the Player's `lifecycleSignal`\n * (or, for the ResizeObserver / older matchMedia listeners that\n * don't accept AbortSignal, cleaned up explicitly in `cleanup()`).\n */\n\nimport { PseudoFullscreenController } from './PseudoFullscreen.js';\nimport type { ControlBar } from '../controls/ControlBar.js';\nimport type { Player } from './Player.js';\n\nexport class ResponsiveManager {\n private readonly player: Player;\n private orientationQuery: MediaQueryList | null = null;\n private orientationHandler: ((e: MediaQueryListEvent) => void) | null = null;\n\n constructor(player: Player) {\n this.player = player;\n }\n\n setup(): void {\n this.setupResizeTracking();\n this.setupOrientationTracking();\n this.setupFullscreenTracking();\n }\n\n private setupResizeTracking(): void {\n const player = this.player;\n\n if (typeof ResizeObserver !== 'undefined') {\n // Preferred path: ResizeObserver fires per-container, not per-viewport,\n // so multiple players on one page don't stomp on each other.\n player.resizeObserver = new ResizeObserver((entries) => {\n for (const entry of entries) {\n const width = entry.contentRect.width;\n\n const controlBar = player.controlBar as (ControlBar & {\n updateControlsForViewport?: (w: number) => void\n }) | null;\n if (controlBar && typeof controlBar.updateControlsForViewport === 'function') {\n controlBar.updateControlsForViewport(width);\n }\n\n if (player.transcriptManager && player.transcriptManager.isVisible) {\n player.transcriptManager.positionTranscript();\n }\n }\n });\n\n player.resizeObserver.observe(player.container);\n return;\n }\n\n // Fallback: `window.resize`. We still respect\n // `draggableResizable.manuallyPositioned` so we don't stomp on a\n // user-chosen transcript location.\n player.resizeHandler = () => {\n const width = player.container.clientWidth;\n\n const controlBar = player.controlBar as (ControlBar & {\n updateControlsForViewport?: (w: number) => void\n }) | null;\n if (controlBar && typeof controlBar.updateControlsForViewport === 'function') {\n controlBar.updateControlsForViewport(width);\n }\n\n if (player.transcriptManager && player.transcriptManager.isVisible) {\n if (!player.transcriptManager.draggableResizable\n || !player.transcriptManager.draggableResizable.manuallyPositioned) {\n player.transcriptManager.positionTranscript();\n }\n }\n };\n\n window.addEventListener('resize', player.resizeHandler, { signal: player.lifecycleSignal });\n }\n\n private setupOrientationTracking(): void {\n const player = this.player;\n if (!window.matchMedia) return;\n\n this.orientationHandler = () => {\n // Wait for layout to settle before we recompute positions —\n // otherwise `getBoundingClientRect` still reflects the\n // pre-rotation viewport.\n setTimeout(() => {\n if (player.transcriptManager && player.transcriptManager.isVisible) {\n if (!player.transcriptManager.draggableResizable\n || !player.transcriptManager.draggableResizable.manuallyPositioned) {\n player.transcriptManager.positionTranscript();\n }\n }\n }, 100);\n };\n\n const orientationQuery = window.matchMedia('(orientation: portrait)');\n if (orientationQuery.addEventListener) {\n // Modern MediaQueryList supports AddEventListenerOptions including\n // `signal`, so teardown happens automatically via the player's\n // lifecycle AbortController.\n orientationQuery.addEventListener('change', this.orientationHandler, {\n signal: player.lifecycleSignal\n });\n } else if ((orientationQuery as MediaQueryList).addListener) {\n // Older Safari: no signal support; `cleanup()` will remove this\n // explicitly below.\n (orientationQuery as MediaQueryList).addListener(this.orientationHandler);\n }\n\n this.orientationQuery = orientationQuery;\n // Mirror the field onto the player too; some older call sites\n // reach for `player.orientationQuery` directly.\n player.orientationQuery = orientationQuery;\n player.orientationHandler = this.orientationHandler;\n }\n\n private setupFullscreenTracking(): void {\n const player = this.player;\n\n player.fullscreenChangeHandler = () => {\n const doc = document as Document & {\n webkitFullscreenElement?: Element | null;\n mozFullScreenElement?: Element | null;\n msFullscreenElement?: Element | null;\n };\n const isFullscreen = Boolean(\n document.fullscreenElement ||\n doc.webkitFullscreenElement ||\n doc.mozFullScreenElement ||\n doc.msFullscreenElement\n );\n\n if (player.state.fullscreen === isFullscreen) return;\n\n player.state.fullscreen = isFullscreen;\n\n if (!player.pseudoFullscreen) {\n player.pseudoFullscreen = new PseudoFullscreenController(player);\n }\n\n if (isFullscreen) {\n player.container.classList.add(`${player.options.classPrefix}-fullscreen`);\n // CSS fallback for browsers without :has() support.\n document.body.classList.add('vidply-fullscreen-active');\n // Make background content inert even in real-fullscreen mode —\n // the overlay still leaves DOM focus reachable on some browsers.\n player.pseudoFullscreen.makeBackgroundInert();\n } else {\n player.container.classList.remove(`${player.options.classPrefix}-fullscreen`);\n document.body.classList.remove('vidply-fullscreen-active');\n player.pseudoFullscreen.restoreBackgroundInteractivity();\n player._disablePseudoFullscreen();\n }\n\n player.emit('fullscreenchange', isFullscreen);\n\n if (player.controlBar) {\n player.controlBar.updateFullscreenButton();\n }\n\n // Reposition the sign-language overlay after the transition\n // completes; drag/resize affordances differ between windowed\n // and fullscreen on mobile so we re-install them.\n if (player.signLanguageWrapper && player.signLanguageWrapper.style.display !== 'none') {\n const isMobileDevice = window.innerWidth < 768;\n if (isMobileDevice) {\n player.setupSignLanguageInteraction();\n }\n\n player.setManagedTimeout(() => {\n requestAnimationFrame(() => {\n player.storage.saveSignLanguagePreferences({ size: null });\n if (player.signLanguageWrapper) {\n player.signLanguageWrapper.style.width = isFullscreen ? '400px' : '280px';\n }\n player.constrainSignLanguagePosition();\n });\n }, 500);\n }\n };\n\n const opts = { signal: player.lifecycleSignal };\n document.addEventListener('fullscreenchange', player.fullscreenChangeHandler, opts);\n document.addEventListener('webkitfullscreenchange', player.fullscreenChangeHandler, opts);\n document.addEventListener('mozfullscreenchange', player.fullscreenChangeHandler, opts);\n document.addEventListener('MSFullscreenChange', player.fullscreenChangeHandler, opts);\n }\n\n /**\n * Tear down listeners that aren't covered by the Player's\n * lifecycle AbortController. The `window.resize` and\n * `document.fullscreenchange` listeners are already cleaned up\n * via `{signal}`; only the ResizeObserver and old-Safari\n * matchMedia listener need an explicit removal.\n */\n cleanup(): void {\n const player = this.player;\n\n if (player.resizeObserver) {\n player.resizeObserver.disconnect();\n player.resizeObserver = null;\n }\n player.resizeHandler = null;\n player.fullscreenChangeHandler = null;\n\n if (this.orientationQuery && this.orientationHandler) {\n if (this.orientationQuery.removeEventListener) {\n this.orientationQuery.removeEventListener('change', this.orientationHandler);\n } else if ((this.orientationQuery as MediaQueryList).removeListener) {\n (this.orientationQuery as MediaQueryList).removeListener(this.orientationHandler);\n }\n this.orientationQuery = null;\n this.orientationHandler = null;\n }\n player.orientationQuery = null;\n player.orientationHandler = null;\n }\n}\n", "/**\n * Metadata-track alert handling extracted from Player.\n *\n * The alert system lets a `kind=metadata` text track drive\n * accessibility affordances on the page — pausing the video,\n * focusing an element, showing or hiding a marked-up alert region,\n * wiring a \"Continue\" button. All of that lives here rather than on\n * Player so the code can be skipped entirely (via tree-shaking) when\n * the `metadataAlerts`/`metadataDirectives` options are disabled.\n *\n * Public-ish entry points (Player exposes them through thin\n * delegates that keep the same names):\n *\n * - `setupHandling()` — wire the `cuechange` listener\n * - `handleCue(cue)` — parse a single cue and dispatch directives\n * - `handleAlert(selector, options)` — manually drive the alert UI\n * - `handleHashtags(tags)` — resolve a list of cue hashtags\n * - `cleanup()` — called from Player.destroy()\n */\n\nimport { reducedMotionScrollOptions } from '../utils/PerformanceUtils.js';\nimport type { Player } from './Player.js';\n\n/** Per-selector metadata alert configuration. */\nexport interface MetadataAlertConfig {\n titleSelector?: string;\n messageSelector?: string;\n title?: string;\n message?: string;\n focus?: boolean;\n focusOnShow?: boolean;\n focusTarget?: string;\n focusDelay?: number;\n label?: string;\n role?: string;\n show?: boolean;\n display?: string;\n hideDisplay?: string;\n autoScroll?: boolean;\n selector?: string;\n alert?: string;\n target?: string;\n continueButton?: string;\n hideOnContinue?: boolean;\n resume?: boolean;\n resetContent?: boolean;\n notification?: string;\n persist?: boolean;\n\n [key: string]: unknown;\n}\n\n/** Options accepted by `handleAlert`. */\nexport interface MetadataAlertOptions {\n element?: HTMLElement | null;\n reason?: string;\n cue?: VTTCue | null;\n show?: boolean;\n focus?: boolean;\n autoScroll?: boolean;\n}\n\ninterface AlertHandlerEntry {\n button: HTMLElement | null;\n handler: EventListener | null;\n}\n\nexport class MetadataAlertsManager {\n private readonly player: Player;\n private cueChangeHandler: (() => void) | null = null;\n private readonly alertHandlers: Map<string, AlertHandlerEntry> = new Map();\n\n constructor(player: Player) {\n this.player = player;\n }\n\n /** The `cuechange` handler this manager installed on the metadata\n * track. Exposed so Player can mirror it onto itself for legacy\n * access (some tests poke at `player.metadataCueChangeHandler`). */\n get cuechangeListener(): (() => void) | null {\n return this.cueChangeHandler;\n }\n\n setupHandling(): void {\n const player = this.player;\n\n const setupMetadata = () => {\n const textTracks = player.textTracks;\n const metadataTrack = textTracks.find((track) => track.kind === 'metadata');\n\n if (!metadataTrack) {\n if (player.options.debug) player.log('[Metadata] No metadata track found');\n return;\n }\n\n // `disabled` suppresses cuechange entirely; `hidden` still fires\n // events without rendering the track.\n if (metadataTrack.mode === 'disabled') {\n metadataTrack.mode = 'hidden';\n }\n\n if (this.cueChangeHandler) {\n metadataTrack.removeEventListener('cuechange', this.cueChangeHandler);\n }\n\n this.cueChangeHandler = () => {\n const activeCues = Array.from(metadataTrack.activeCues || []) as VTTCue[];\n if (activeCues.length > 0 && player.options.debug) {\n player.log('[Metadata] Active cues:', activeCues.map((c) => ({\n start: c.startTime,\n end: c.endTime,\n text: c.text\n })));\n }\n activeCues.forEach((cue) => this.handleCue(cue));\n };\n\n metadataTrack.addEventListener('cuechange', this.cueChangeHandler);\n // Mirror onto player for existing destroy() cleanup path.\n player.metadataCueChangeHandler = this.cueChangeHandler;\n\n if (player.options.debug) {\n const cueCount = metadataTrack.cues ? metadataTrack.cues.length : 0;\n player.log('[Metadata] Track enabled,', cueCount, 'cues available');\n }\n };\n\n setupMetadata();\n player.on('loadedmetadata', setupMetadata);\n }\n\n /**\n * Sanitise a user-supplied selector string. Returns `null` for\n * anything that isn't obviously safe: non-string input, empty\n * after trimming, or too long to bound selector-engine cost.\n */\n normalizeSelector(selector: unknown): string | null {\n if (typeof selector !== 'string') return null;\n const trimmed = selector.trim();\n if (!trimmed) return null;\n if (trimmed.length > 200) return null;\n if (trimmed.startsWith('#') || trimmed.startsWith('.') || trimmed.startsWith('[')) {\n return trimmed;\n }\n return `#${trimmed}`;\n }\n\n resolveConfig(\n map: Record<string, unknown> | null | undefined,\n key: string | null | undefined\n ): MetadataAlertConfig | null {\n if (!map || !key) return null;\n if (Object.prototype.hasOwnProperty.call(map, key)) {\n return map[key] as MetadataAlertConfig;\n }\n const withoutHash = key.replace(/^#/, '');\n if (Object.prototype.hasOwnProperty.call(map, withoutHash)) {\n return map[withoutHash] as MetadataAlertConfig;\n }\n return null;\n }\n\n /**\n * Remember the original title/message text before a hashtag cue\n * overwrites them, so `restoreContent` can roll back on the next\n * cue boundary. Idempotent — a second call for the same element\n * does not overwrite the already-cached value.\n */\n cacheContent(element: HTMLElement | null | undefined, config: MetadataAlertConfig = {}): void {\n if (!element) return;\n const titleSelector = config.titleSelector || '[data-vidply-alert-title], h3, header';\n const messageSelector = config.messageSelector || '[data-vidply-alert-message], p';\n\n const titleEl = element.querySelector<HTMLElement>(titleSelector);\n if (titleEl && !titleEl.dataset.vidplyAlertTitleOriginal) {\n titleEl.dataset.vidplyAlertTitleOriginal = titleEl.textContent?.trim() ?? '';\n }\n\n const messageEl = element.querySelector<HTMLElement>(messageSelector);\n if (messageEl && !messageEl.dataset.vidplyAlertMessageOriginal) {\n messageEl.dataset.vidplyAlertMessageOriginal = messageEl.textContent?.trim() ?? '';\n }\n }\n\n restoreContent(element: HTMLElement | null | undefined, config: MetadataAlertConfig = {}): void {\n if (!element) return;\n const titleSelector = config.titleSelector || '[data-vidply-alert-title], h3, header';\n const messageSelector = config.messageSelector || '[data-vidply-alert-message], p';\n\n const titleEl = element.querySelector<HTMLElement>(titleSelector);\n if (titleEl && titleEl.dataset.vidplyAlertTitleOriginal) {\n titleEl.textContent = titleEl.dataset.vidplyAlertTitleOriginal;\n }\n\n const messageEl = element.querySelector<HTMLElement>(messageSelector);\n if (messageEl && messageEl.dataset.vidplyAlertMessageOriginal) {\n messageEl.textContent = messageEl.dataset.vidplyAlertMessageOriginal;\n }\n }\n\n /**\n * Move focus to one of the well-known targets understood by the\n * alert system, or to a named selector. Never silently errors — an\n * unresolved target is simply a no-op.\n */\n focusTarget(target: string | null | undefined, fallbackElement: HTMLElement | null = null): void {\n if (!target || target === 'none') return;\n\n if (target === 'alert' && fallbackElement) {\n fallbackElement.focus({ preventScroll: true });\n return;\n }\n\n const player = this.player;\n\n if (target === 'player') {\n player.container?.focus({ preventScroll: true });\n return;\n }\n\n if (target === 'media') {\n player.element.focus({ preventScroll: true });\n return;\n }\n\n if (target === 'playButton') {\n const playButton = player.controlBar?.controls?.playPause;\n playButton?.focus({ preventScroll: true });\n return;\n }\n\n if (typeof target === 'string') {\n const targetElement = document.querySelector(target) as HTMLElement | null;\n if (targetElement) {\n if (targetElement.tabIndex === -1 && !targetElement.hasAttribute('tabindex')) {\n targetElement.setAttribute('tabindex', '-1');\n }\n targetElement.focus({ preventScroll: true });\n }\n }\n }\n\n /**\n * The public alert entry point. Pulls config out of\n * `options.metadataAlerts`, locates the DOM element, and applies\n * show/focus/continue logic per configuration.\n */\n handleAlert(selector: string, options: MetadataAlertOptions = {}): HTMLElement | undefined {\n const player = this.player;\n if (!selector) return undefined;\n\n const config: MetadataAlertConfig =\n this.resolveConfig(player.options.metadataAlerts as Record<string, unknown> | null | undefined, selector) || {};\n\n // Container-scoped resolution by default; only fall back to a\n // global lookup when `metadataDirectives === 'global' | true`.\n const element = options.element || this.resolveElement(selector);\n\n if (!element) {\n if (player.options.debug) player.log('[Metadata] Alert element not found:', selector);\n return undefined;\n }\n\n if (player.options.debug) {\n player.log('[Metadata] Handling alert', selector, { reason: options.reason, config });\n }\n\n this.cacheContent(element, config);\n\n if (!element.dataset.vidplyAlertOriginalDisplay) {\n element.dataset.vidplyAlertOriginalDisplay = element.style.display || '';\n }\n\n if (!element.dataset.vidplyAlertDisplay) {\n element.dataset.vidplyAlertDisplay = config.display || 'block';\n }\n\n const shouldShow = options.show !== undefined ? options.show : (config.show !== false);\n if (shouldShow) {\n const displayValue = config.display || element.dataset.vidplyAlertDisplay || 'block';\n element.style.display = displayValue;\n element.hidden = false;\n element.removeAttribute('hidden');\n element.setAttribute('aria-hidden', 'false');\n element.setAttribute('data-vidply-alert-active', 'true');\n }\n\n const shouldReset = config.resetContent !== false && options.reason === 'focus';\n if (shouldReset) this.restoreContent(element, config);\n\n const shouldFocus = options.focus !== undefined\n ? options.focus\n : (config.focusOnShow ?? (options.reason !== 'focus'));\n\n if (shouldShow && shouldFocus) {\n if (element.tabIndex === -1 && !element.hasAttribute('tabindex')) {\n element.setAttribute('tabindex', '-1');\n }\n element.focus({ preventScroll: true });\n }\n\n if (shouldShow && config.autoScroll !== false && options.autoScroll !== false) {\n element.scrollIntoView(reducedMotionScrollOptions('nearest'));\n }\n\n const continueSelector = config.continueButton;\n if (continueSelector) {\n let continueButton: HTMLElement | null = null;\n if (continueSelector === 'self') {\n continueButton = element;\n } else if (element.matches(continueSelector)) {\n continueButton = element;\n } else {\n continueButton =\n element.querySelector<HTMLElement>(continueSelector) ||\n document.querySelector<HTMLElement>(continueSelector);\n }\n\n if (continueButton && !this.alertHandlers.has(selector)) {\n const handler = () => {\n const hideOnContinue = config.hideOnContinue !== false;\n if (hideOnContinue) {\n const originalDisplay = element.dataset.vidplyAlertOriginalDisplay || '';\n element.style.display = config.hideDisplay || originalDisplay || 'none';\n element.setAttribute('aria-hidden', 'true');\n element.removeAttribute('data-vidply-alert-active');\n }\n\n if (config.resume !== false && player.state.paused) {\n player.play();\n }\n\n const focusTarget = config.focusTarget || 'playButton';\n player.setManagedTimeout(() => {\n this.focusTarget(focusTarget, element);\n }, config.focusDelay ?? 100);\n };\n\n continueButton.addEventListener('click', handler);\n this.alertHandlers.set(selector, { button: continueButton, handler });\n }\n }\n\n return element;\n }\n\n handleHashtags(hashtags: string[] | null | undefined): void {\n if (!Array.isArray(hashtags) || hashtags.length === 0) return;\n\n const player = this.player;\n const configMap = player.options.metadataHashtags as Record<string, unknown> | null | undefined;\n if (!configMap) return;\n\n hashtags.forEach((tag) => {\n const config = this.resolveConfig(configMap, tag);\n if (!config) return;\n\n const selector = this.normalizeSelector(config.alert || config.selector || config.target);\n if (!selector) return;\n\n const element = this.resolveElement(selector);\n if (!element) {\n if (player.options.debug) player.log('[Metadata] Hashtag target not found:', selector);\n return;\n }\n\n if (player.options.debug) {\n player.log('[Metadata] Handling hashtag', tag, { selector, config });\n }\n\n this.cacheContent(element, config);\n\n if (config.title) {\n const titleSelector = config.titleSelector || '[data-vidply-alert-title], h3, header';\n const titleEl = element.querySelector<HTMLElement>(titleSelector);\n if (titleEl) titleEl.textContent = config.title;\n }\n\n if (config.message) {\n const messageSelector = config.messageSelector || '[data-vidply-alert-message], p';\n const messageEl = element.querySelector<HTMLElement>(messageSelector);\n if (messageEl) messageEl.textContent = config.message;\n }\n\n const show = config.show !== false;\n const focus = config.focus !== undefined ? config.focus : false;\n\n this.handleAlert(selector, {\n element,\n show,\n focus,\n autoScroll: config.autoScroll,\n reason: 'hashtag'\n });\n });\n }\n\n /**\n * Parse a single metadata cue for directives (`PAUSE`, `FOCUS:x`,\n * `#hashtag`), emit the corresponding public events, and execute\n * DOM side-effects only when `options.metadataDirectives` is set.\n */\n handleCue(cue: VTTCue | TextTrackCue): void {\n const player = this.player;\n const text = (cue as VTTCue).text.trim();\n\n if (player.options.debug) {\n player.log('[Metadata] Processing cue:', { time: cue.startTime, text });\n }\n\n // Public event fires regardless of directive opt-in so consumers\n // can always observe metadata without granting DOM side-effects.\n player.emit('metadata', {\n time: cue.startTime,\n endTime: cue.endTime,\n text,\n cue\n });\n\n if (text.includes('PAUSE')) {\n if (!player.state.paused) {\n if (player.options.debug) player.log('[Metadata] Pausing video at', cue.startTime);\n player.pause();\n }\n player.emit('metadata:pause', { time: cue.startTime, text });\n }\n\n const focusMatch = text.match(/FOCUS:([\\w#-]{1,128})/);\n if (focusMatch) {\n const targetSelector = focusMatch[1];\n const normalizedSelector = this.normalizeSelector(targetSelector);\n const targetElement = this.resolveElement(normalizedSelector);\n if (targetElement) {\n if (player.options.debug) player.log('[Metadata] Focusing element:', normalizedSelector);\n if (targetElement.tabIndex === -1 && !targetElement.hasAttribute('tabindex')) {\n targetElement.setAttribute('tabindex', '-1');\n }\n player.setManagedTimeout(() => {\n targetElement.focus({ preventScroll: true });\n }, 10);\n } else if (player.options.debug && player.options.metadataDirectives) {\n player.log('[Metadata] Element not found:', normalizedSelector || targetSelector);\n }\n player.emit('metadata:focus', {\n time: cue.startTime,\n target: targetSelector,\n selector: normalizedSelector,\n element: targetElement,\n text\n });\n\n if (player.options.metadataDirectives && normalizedSelector) {\n this.handleAlert(normalizedSelector, {\n element: targetElement,\n reason: 'focus'\n });\n }\n }\n\n const hashtags = text.match(/#[\\w-]{1,64}/g);\n if (hashtags && hashtags.length > 0) {\n // Cap at 32 hashtags per cue to bound subsequent work.\n const safeTags = hashtags.slice(0, 32);\n if (player.options.debug) player.log('[Metadata] Hashtags found:', safeTags);\n player.emit('metadata:hashtags', {\n time: cue.startTime,\n hashtags: safeTags,\n text\n });\n\n if (player.options.metadataDirectives) this.handleHashtags(safeTags);\n }\n }\n\n /**\n * Resolve a metadata-cue selector inside the configured directive\n * scope. Returns `null` when directives are disabled or the\n * selector doesn't resolve. Container-scoped resolution is the\n * default so a malicious caption cannot focus a login-form input\n * or trigger a dialog elsewhere on the page.\n */\n private resolveElement(selector: string | null): HTMLElement | null {\n const player = this.player;\n const mode = player.options.metadataDirectives;\n if (!mode) return null;\n if (!selector) return null;\n try {\n if (mode === true || mode === 'global') {\n return document.querySelector(selector) as HTMLElement | null;\n }\n const root = player.container || player.element.parentElement || document;\n return (root as ParentNode).querySelector(selector) as HTMLElement | null;\n } catch {\n // Bad selector — never surface to the page.\n return null;\n }\n }\n\n /** Tear down the per-alert click handlers and the cuechange\n * listener. Called from Player.destroy(). */\n cleanup(): void {\n // The cuechange listener is removed by the Player destroy path\n // because the underlying TextTrack still exists and Player\n // already knows how to walk textTracks. We only need to release\n // the per-alert click handlers we installed.\n if (this.alertHandlers.size > 0) {\n this.alertHandlers.forEach(({ button, handler }) => {\n if (button && handler) button.removeEventListener('click', handler);\n });\n this.alertHandlers.clear();\n }\n this.cueChangeHandler = null;\n }\n}\n", "/**\n * Keyboard Shortcuts Help Dialog\n *\n * A focus-trapped modal listing the player's active keyboard shortcuts.\n * Built lazily from `player.options.keyboardShortcuts` so it always reflects\n * the live bindings (including consumer overrides). Mirrors the focus, escape\n * and return-focus behaviour of SettingsDialog for a consistent, accessible UX.\n */\n\nimport { DOMUtils } from '../utils/DOMUtils.js';\nimport { createIconElement } from '../icons/Icons.js';\nimport { i18n } from '../i18n/i18n.js';\nimport type { Player } from '../core/Player.js';\nimport type { KeyboardShortcuts } from '../types/options.js';\n\n/**\n * Display order for the shortcut rows. Only actions that are both listed here\n * and have at least one bound key are rendered, so removing a binding hides\n * its row automatically.\n */\nconst ACTION_ORDER: Array<keyof KeyboardShortcuts> = [\n 'play-pause',\n 'seek-backward',\n 'seek-forward',\n 'volume-up',\n 'volume-down',\n 'mute',\n 'captions',\n 'caption-style-menu',\n 'speed-down',\n 'speed-up',\n 'speed-menu',\n 'quality-menu',\n 'chapters-menu',\n 'transcript-toggle',\n 'fullscreen',\n 'help'\n];\n\n/**\n * Feature-specific actions only make sense when the matching feature actually\n * exists on this player (e.g. there's no point listing the captions shortcut\n * for a video without captions). Each value is the key under\n * `player.controlBar.controls` whose presence proves the feature is available\n * and enabled for this player. Actions not listed here (play/pause, seek,\n * volume, mute, help) are always relevant for any audio/video player.\n */\nconst ACTION_REQUIRES_CONTROL: Partial<Record<keyof KeyboardShortcuts, string>> = {\n captions: 'captions',\n 'caption-style-menu': 'captionStyle',\n 'speed-down': 'speed',\n 'speed-up': 'speed',\n 'speed-menu': 'speed',\n 'quality-menu': 'quality',\n 'chapters-menu': 'chapters',\n 'transcript-toggle': 'transcript',\n fullscreen: 'fullscreen'\n};\n\nexport class KeyboardHelp {\n player: Player;\n isOpen = false;\n overlay: HTMLElement | null = null;\n private _triggerElement: HTMLElement | null = null;\n private _keydownHandler: ((e: KeyboardEvent) => void) | null = null;\n private _content: HTMLElement | null = null;\n\n constructor(player: Player) {\n this.player = player;\n }\n\n private get prefix(): string {\n return this.player.options.classPrefix;\n }\n\n /**\n * Turn a raw KeyboardEvent.key value into a human-readable label. Arrow\n * keys become universally understood glyphs; the space bar and single\n * letters are normalised for legibility.\n */\n private formatKey(key: string): string {\n switch (key) {\n case ' ':\n return i18n.t('help.keys.space');\n case 'ArrowUp':\n return '\\u2191';\n case 'ArrowDown':\n return '\\u2193';\n case 'ArrowLeft':\n return '\\u2190';\n case 'ArrowRight':\n return '\\u2192';\n case 'Escape':\n return 'Esc';\n default:\n return key.length === 1 ? key.toUpperCase() : key;\n }\n }\n\n private createElement(): HTMLElement {\n const titleId = `${this.prefix}-help-title-${this.player.instanceId}`;\n\n const overlay = DOMUtils.createElement('div', {\n className: `${this.prefix}-settings-overlay ${this.prefix}-help-overlay`,\n attributes: {\n role: 'dialog',\n 'aria-modal': 'true',\n 'aria-labelledby': titleId\n }\n });\n overlay.style.display = 'none';\n\n const dialog = DOMUtils.createElement('div', {\n className: `${this.prefix}-settings-dialog ${this.prefix}-help-dialog`\n });\n\n const header = DOMUtils.createElement('div', {\n className: `${this.prefix}-settings-header`\n });\n\n const title = DOMUtils.createElement('h2', {\n textContent: i18n.t('help.title'),\n attributes: { id: titleId }\n });\n\n const closeButton = DOMUtils.createElement('button', {\n className: `${this.prefix}-button ${this.prefix}-settings-close`,\n attributes: {\n type: 'button',\n 'aria-label': i18n.t('help.close')\n }\n });\n closeButton.appendChild(createIconElement('close'));\n closeButton.addEventListener('click', () => this.hide());\n\n header.appendChild(title);\n header.appendChild(closeButton);\n\n const content = DOMUtils.createElement('div', {\n className: `${this.prefix}-settings-content`\n });\n this._content = content;\n content.appendChild(this.buildShortcutList());\n\n dialog.appendChild(header);\n dialog.appendChild(content);\n overlay.appendChild(dialog);\n\n overlay.addEventListener('click', (e: MouseEvent) => {\n if (e.target === overlay) {\n this.hide();\n }\n });\n\n this._keydownHandler = (e: KeyboardEvent) => {\n if (!this.isOpen || !this.overlay) return;\n\n if (e.key === 'Escape') {\n e.preventDefault();\n this.hide();\n return;\n }\n\n // Trap Tab within the dialog (WCAG 2.1.2, 2.4.3).\n if (e.key === 'Tab') {\n const focusable = Array.from(\n this.overlay.querySelectorAll<HTMLElement>(\n 'button, [href], select, input, textarea, [tabindex]:not([tabindex=\"-1\"])'\n )\n ).filter((el) => !el.hasAttribute('disabled') && el.offsetParent !== null);\n if (focusable.length === 0) return;\n\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n if (!first || !last) return;\n const active = document.activeElement as HTMLElement | null;\n\n if (e.shiftKey && (active === first || !this.overlay.contains(active))) {\n e.preventDefault();\n last.focus({ preventScroll: true });\n } else if (!e.shiftKey && active === last) {\n e.preventDefault();\n first.focus({ preventScroll: true });\n }\n }\n };\n const lifecycleSignal = (this.player as { lifecycleSignal?: AbortSignal }).lifecycleSignal;\n document.addEventListener(\n 'keydown',\n this._keydownHandler,\n lifecycleSignal ? { signal: lifecycleSignal } : undefined\n );\n\n return overlay;\n }\n\n /**\n * Whether a shortcut row is worth showing for *this* player. Feature actions\n * are hidden when their control isn't present (e.g. no captions track, an\n * audio-only player with no fullscreen). Core actions are always relevant.\n *\n * When the player has no control bar we can't infer availability, so nothing\n * is hidden — the shortcuts still work and we'd rather over-show than mislead.\n */\n private isActionRelevant(action: keyof KeyboardShortcuts): boolean {\n const requiredControl = ACTION_REQUIRES_CONTROL[action];\n if (!requiredControl) return true;\n\n const controlBar = (this.player as { controlBar?: { controls?: Record<string, unknown> } }).controlBar;\n if (!controlBar || !controlBar.controls) return true;\n\n return Boolean(controlBar.controls[requiredControl]);\n }\n\n private buildShortcutList(): HTMLElement {\n const list = DOMUtils.createElement('dl', {\n className: `${this.prefix}-help-list`\n });\n\n const shortcuts = this.player.options.keyboardShortcuts as unknown as Record<string, string[] | undefined>;\n\n for (const action of ACTION_ORDER) {\n const keys = shortcuts[action];\n if (!Array.isArray(keys) || keys.length === 0) continue;\n if (!this.isActionRelevant(action)) continue;\n\n const term = DOMUtils.createElement('dt', {\n className: `${this.prefix}-help-action`,\n textContent: i18n.t(`help.actions.${action}`)\n });\n\n const desc = DOMUtils.createElement('dd', {\n className: `${this.prefix}-help-keys`\n });\n\n keys.forEach((key, index) => {\n if (index > 0) {\n desc.appendChild(\n DOMUtils.createElement('span', {\n className: `${this.prefix}-help-key-sep`,\n textContent: i18n.t('help.or')\n })\n );\n }\n desc.appendChild(\n DOMUtils.createElement('kbd', {\n className: `${this.prefix}-help-key`,\n textContent: this.formatKey(key)\n })\n );\n });\n\n list.appendChild(term);\n list.appendChild(desc);\n }\n\n return list;\n }\n\n show(): void {\n if (this.isOpen) return;\n\n if (!this.overlay) {\n this.overlay = this.createElement();\n this.player.container.appendChild(this.overlay);\n } else if (this._content) {\n // Rebuild so feature availability that changed since last open\n // (e.g. HLS captions/qualities loaded later) is reflected.\n this._content.replaceChildren(this.buildShortcutList());\n }\n\n const active = (typeof document !== 'undefined' ? document.activeElement : null) as HTMLElement | null;\n this._triggerElement = active && typeof active.focus === 'function' ? active : null;\n\n this.overlay.style.display = 'flex';\n this.isOpen = true;\n\n const closeButton = this.overlay.querySelector<HTMLElement>(`.${this.prefix}-settings-close`);\n closeButton?.focus({ preventScroll: true });\n\n this.player.emit('keyboardhelpopen');\n }\n\n hide(): void {\n if (!this.overlay) return;\n\n this.overlay.style.display = 'none';\n this.isOpen = false;\n\n const trigger = this._triggerElement;\n this._triggerElement = null;\n if (trigger && document.contains(trigger)) {\n try {\n trigger.focus({ preventScroll: true });\n } catch {\n this.player.container?.focus();\n }\n } else {\n this.player.container?.focus();\n }\n\n this.player.emit('keyboardhelpclose');\n }\n\n toggle(): void {\n if (this.isOpen) {\n this.hide();\n } else {\n this.show();\n }\n }\n\n destroy(): void {\n if (this._keydownHandler) {\n document.removeEventListener('keydown', this._keydownHandler);\n this._keydownHandler = null;\n }\n if (this.overlay && this.overlay.parentNode) {\n this.overlay.parentNode.removeChild(this.overlay);\n }\n this.overlay = null;\n this._content = null;\n this._triggerElement = null;\n this.isOpen = false;\n }\n}\n", "/**\n * VidPly - Universal Video Player\n * Main Player Class\n */\n\nimport {EventEmitter} from '../utils/EventEmitter.js';\nimport {DOMUtils} from '../utils/DOMUtils.js';\nimport {ControlBar} from '../controls/ControlBar.js';\nimport {CaptionManager} from '../controls/CaptionManager.js';\nimport {KeyboardManager} from '../controls/KeyboardManager.js';\nimport {MediaSessionManager} from './MediaSessionManager.js';\nimport {HTML5Renderer} from '../renderers/HTML5Renderer.js';\nimport {createPlayOverlay} from '../icons/Icons.js';\nimport {i18n} from '../i18n/i18n.js';\nimport {StorageManager} from '../utils/StorageManager.js';\nimport {DraggableResizable} from '../utils/DraggableResizable.js';\nimport {debounce, isMobile, rafWithTimeout} from '../utils/PerformanceUtils.js';\nimport {sanitizePosterUrl, cssEscapeUrl} from '../utils/UrlSafe.js';\nimport {observeForLazyInit, cancelLazyInit, type LazyHandle} from './LazyInit.js';\nimport {PseudoFullscreenController} from './PseudoFullscreen.js';\nimport {ThemeManager, PLAYER_THEMES, type ThemeName} from './ThemeManager.js';\nimport {PosterManager} from './PosterManager.js';\nimport {ResumeManager} from './ResumeManager.js';\nimport {ResponsiveManager} from './ResponsiveManager.js';\nimport {\n MetadataAlertsManager,\n type MetadataAlertConfig as _MetadataAlertConfig,\n type MetadataAlertOptions as _MetadataAlertOptions\n} from './MetadataAlertsManager.js';\n\n// Re-export the interfaces so external users who imported them from\n// `core/Player.js` keep working.\nexport type MetadataAlertConfig = _MetadataAlertConfig;\nexport type MetadataAlertOptions = _MetadataAlertOptions;\nimport type {PlayerEventMap} from '../types/events.js';\nimport type {PlayerOptions} from '../types/options.js';\nimport type {PlayerState} from '../types/state.js';\nimport type {Renderer} from '../types/renderer.js';\nimport type {AudioDescriptionManager} from './AudioDescriptionManager.js';\nimport type {SignLanguageManager} from './SignLanguageManager.js';\nimport type {FloatingPlayerManager} from './FloatingPlayerManager.js';\nimport type {TranscriptManager} from '../controls/TranscriptManager.js';\nimport type {PlaylistManager} from '../features/PlaylistManager.js';\nimport type {SettingsDialog} from '../controls/SettingsDialog.js';\nimport {KeyboardHelp} from '../controls/KeyboardHelp.js';\n\n// Typed dynamic loaders. Each loader returns the constructor for the\n// lazily imported manager, so call sites get full IntelliSense and\n// `noImplicitAny` is satisfied.\ntype AudioDescriptionManagerCtor = typeof AudioDescriptionManager;\ntype SignLanguageManagerCtor = typeof SignLanguageManager;\ntype FloatingPlayerManagerCtor = typeof FloatingPlayerManager;\n\nlet AudioDescriptionManagerModule: AudioDescriptionManagerCtor | null = null;\nlet SignLanguageManagerModule: SignLanguageManagerCtor | null = null;\nlet FloatingPlayerManagerModule: FloatingPlayerManagerCtor | null = null;\n\nasync function loadAudioDescriptionManager(): Promise<AudioDescriptionManagerCtor> {\n if (!AudioDescriptionManagerModule) {\n const module = await import('./AudioDescriptionManager.js');\n AudioDescriptionManagerModule = module.AudioDescriptionManager;\n }\n return AudioDescriptionManagerModule;\n}\n\nasync function loadSignLanguageManager(): Promise<SignLanguageManagerCtor> {\n if (!SignLanguageManagerModule) {\n const module = await import('./SignLanguageManager.js');\n SignLanguageManagerModule = module.SignLanguageManager;\n }\n return SignLanguageManagerModule;\n}\n\nasync function loadFloatingPlayerManager(): Promise<FloatingPlayerManagerCtor> {\n if (!FloatingPlayerManagerModule) {\n const module = await import('./FloatingPlayerManager.js');\n FloatingPlayerManagerModule = module.FloatingPlayerManager;\n }\n return FloatingPlayerManagerModule;\n}\n\nconst ALLOWED_MEDIA_TYPES = ['video', 'audio'] as const;\ntype AllowedMediaType = (typeof ALLOWED_MEDIA_TYPES)[number];\n\n/** Configuration accepted by Player.load() when switching to new media. */\nexport interface PlayerLoadConfig {\n src: string;\n type?: string;\n poster?: string | null;\n tracks?: Array<{\n kind?: string;\n label?: string;\n src?: string;\n srclang?: string;\n default?: boolean;\n [key: string]: unknown;\n }>;\n audioDescriptionSrc?: string | null;\n signLanguageSrc?: string | null;\n signLanguageSources?: Record<string, string>;\n\n [key: string]: unknown;\n}\n\n// Static counter for unique player instances\nlet playerInstanceCounter = 0;\n\nexport class Player extends EventEmitter<PlayerEventMap> {\n static instances: Player[] = [];\n /**\n * Available theme names. Kept as a static field for backward\n * compatibility with external callers that used\n * `Player.THEMES.includes(x)`; the canonical source is\n * `PLAYER_THEMES` in `./ThemeManager.ts`.\n */\n static readonly THEMES: readonly ThemeName[] = PLAYER_THEMES;\n\n /**\n * Manually schedule a lazy-initialised player for `selector` /\n * `element`. The player is constructed the first time the element\n * scrolls within `margin` of the viewport; if `IntersectionObserver`\n * is unavailable the player is constructed immediately.\n *\n * Returns a handle whose `cancel()` method removes the pending\n * observation, or `null` if no observation was scheduled (element\n * missing or eager fallback took effect).\n *\n * Implemented as a real static method (rather than a post-construction\n * assignment from `index.ts`) so the API belongs to the `Player`\n * symbol itself — which makes it easier to tree-shake and reason about.\n */\n static observeLazy(\n selector: string | HTMLElement,\n options: Partial<PlayerOptions> = {},\n margin = '200px'\n ): LazyHandle {\n const element =\n typeof selector === 'string'\n ? (document.querySelector(selector) as HTMLElement | null)\n : selector;\n\n if (!element) {\n console.warn('VidPly: Element not found for lazy observation');\n return null;\n }\n\n if ('IntersectionObserver' in window) {\n observeForLazyInit<Partial<PlayerOptions>>(\n element,\n options,\n margin,\n (target, opts) => { new Player(target, opts); }\n );\n return { cancel: () => cancelLazyInit(element) };\n }\n new Player(element, options);\n return null;\n }\n element: HTMLMediaElement;\n container!: HTMLElement;\n /**\n * Runtime options. Includes a `[key: string]: unknown` index for\n * internal-only dynamic keys that have not yet been promoted into\n * the public {@link PlayerOptions} interface.\n */\n options: PlayerOptions & Record<string, unknown>;\n state: PlayerState & Record<string, unknown>;\n renderer: Renderer | null = null;\n controlBar: ControlBar | null = null;\n captionManager: CaptionManager | null = null;\n keyboardManager: KeyboardManager | null = null;\n mediaSessionManager: MediaSessionManager | null = null;\n transcriptManager: TranscriptManager | null = null;\n playlistManager: PlaylistManager | null = null;\n settingsDialog: SettingsDialog | null = null;\n keyboardHelp: KeyboardHelp | null = null;\n audioDescriptionManager: AudioDescriptionManager | null = null;\n signLanguageManager: SignLanguageManager | null = null;\n floatingPlayerManager: FloatingPlayerManager | null = null;\n storage: StorageManager;\n instanceId: number;\n _audioDescriptionDesiredState: boolean | undefined;\n _fallbackSources: Array<{ src: string; type?: string; [key: string]: unknown }> | null = null;\n _isAudioContent: boolean | undefined;\n _isFallingBack: boolean | undefined;\n _managersLoading: Promise<unknown> | null = null;\n _originalElement!: HTMLElement;\n /** Lazily-created on first pseudo-fullscreen entry. Owns the scroll /\n * inert / viewport bookkeeping that used to live as `_original*`\n * fields directly on the player. */\n pseudoFullscreen: PseudoFullscreenController | null = null;\n /** Owns `applyTheme`/`setTheme`/`setThemeVariable`/`resetTheme`. Player\n * keeps delegating public methods so the existing API is unchanged. */\n themeManager!: ThemeManager;\n /** Owns poster resolution, canvas-capture, and overlay show/hide. */\n posterManager!: PosterManager;\n /** Owns resume-playback prompt + progress persistence. Lazily\n * created the first time `initResumePlayback` is called so sites\n * that don't enable the feature don't pay the DOM / listener cost. */\n resumeManager: ResumeManager | null = null;\n /** Owns resize-observer, orientation matchMedia, and the\n * cross-vendor fullscreenchange listeners. */\n responsiveManager!: ResponsiveManager;\n /** Owns `kind=metadata` text-track directives (PAUSE, FOCUS,\n * #hashtag) + the per-selector alert UI. Lazily created on first\n * `setupMetadataHandling()` call. */\n metadataAlertsManager: MetadataAlertsManager | null = null;\n _pendingSource: string | null = null;\n _sourceElementsCache: HTMLSourceElement[] | null = null;\n _sourceElementsDirty: boolean = true;\n _switchingRenderer: boolean | undefined;\n _trackElementsCache: HTMLTrackElement[] | null = null;\n _trackElementsDirty: boolean = true;\n _textTracksCache: TextTrack[] | null = null;\n _textTracksDirty: boolean | undefined;\n audioDescriptionCaptionTracks: unknown[] = [];\n audioDescriptionSourceElement: HTMLSourceElement | null = null;\n audioDescriptionSrc: string | null = null;\n currentSignLanguage: string | null = null;\n currentSource: string | null = null;\n debouncedPositionPlayOverlay: ((...args: unknown[]) => void) | null = null;\n fullscreenChangeHandler: (() => void) | null = null;\n /** Mirrored from `MetadataAlertsManager` so the TextTrack cleanup\n * path in `destroy()` can still find it by a fixed field name. */\n metadataCueChangeHandler: (() => void) | null = null;\n noticeElement: HTMLElement | null = null;\n noticeTimeout: ReturnType<typeof setTimeout> | null = null;\n orientationHandler: ((e: MediaQueryListEvent) => void) | null = null;\n orientationQuery: MediaQueryList | null = null;\n originalAudioDescriptionSource: string | null = null;\n originalSrc: string | null = null;\n playButtonOverlay: SVGSVGElement | null = null;\n resizeHandler: (() => void) | null = null;\n resizeObserver: ResizeObserver | null = null;\n resumePromptElement: HTMLElement | null = null;\n signLanguageDraggable: DraggableResizable | null = null;\n signLanguageHeader: HTMLElement | null = null;\n signLanguageSettingsButton: HTMLButtonElement | null = null;\n signLanguageSettingsMenu: HTMLElement | null = null;\n signLanguageSettingsMenuVisible: boolean = false;\n signLanguageSources: Record<string, string> = {};\n signLanguageSrc: string | null = null;\n signLanguageVideo: HTMLVideoElement | null = null;\n signLanguageWrapper: HTMLElement | null = null;\n timeouts: Set<ReturnType<typeof setTimeout>> = new Set();\n trackArtworkElement: HTMLElement | null = null;\n videoWrapper: HTMLElement | null = null;\n /** Centered buffering spinner (see `.vidply-loading` / `.vidply-buffering` in CSS) */\n loadingOverlayElement: HTMLElement | null = null;\n /** Native `playing` listener — must be removed in destroy() */\n _bufferingHideOnMediaPlaying: (() => void) | null = null;\n /** AbortController, whose signal feeds every window/document listener and\n * every user-influenced fetch the Player creates. `destroy()` calls\n * `abort()` so a torn-down player can never leak listeners or pending\n * network calls. */\n private _lifecycleController: AbortController = new AbortController();\n\n constructor(element: string | HTMLElement, options: Record<string, unknown> = {}) {\n super();\n\n this.element = (typeof element === 'string' ? document.querySelector(element) : element) as HTMLMediaElement;\n if (!this.element) {\n throw new Error('VidPly: Element not found');\n }\n\n // Assign unique instance ID\n playerInstanceCounter++;\n this.instanceId = playerInstanceCounter;\n\n // Auto-creates a media element if a non-media element is provided.\n // mediaType is restricted to a hard-coded allowlist so an attacker\n // cannot smuggle, say, `script` or `object` via a `data-vidply-options`\n // attribute they control.\n if (this.element.tagName !== 'VIDEO' && this.element.tagName !== 'AUDIO') {\n const requested = typeof options.mediaType === 'string' ? options.mediaType.toLowerCase() : 'video';\n const mediaType: AllowedMediaType = (ALLOWED_MEDIA_TYPES as readonly string[]).includes(requested)\n ? (requested as AllowedMediaType)\n : 'video';\n if (mediaType !== requested) {\n console.warn(`[VidPly] Ignoring unsafe mediaType \"${requested}\", falling back to \"video\"`);\n }\n const mediaElement = document.createElement(mediaType);\n\n // Copy attributes from the div to the media element\n Array.from(this.element.attributes).forEach(attr => {\n if (attr.name !== 'id' && attr.name !== 'class' && !attr.name.startsWith('data-')) {\n mediaElement.setAttribute(attr.name, attr.value);\n }\n });\n\n // Copy any track elements from the div\n const tracks = this.element.querySelectorAll('track');\n tracks.forEach((track: HTMLTrackElement) => {\n mediaElement.appendChild(track.cloneNode(true));\n });\n\n // Clear the div and insert the media element\n this.element.replaceChildren(mediaElement);\n\n // Update element reference to the actual media element\n this.element = mediaElement as HTMLMediaElement;\n }\n\n // Store original element reference for mixed media swapping\n // This allows us to swap between video and audio elements in mixed playlists\n this._originalElement = this.element;\n\n // Default options\n this.options = {\n // Display\n width: null,\n height: null,\n poster: null,\n responsive: true,\n fillContainer: false,\n\n // Media metadata + OS media controls (Media Session API)\n title: null,\n artist: null,\n album: null,\n mediaSession: true,\n\n // Playback\n autoplay: false,\n loop: false,\n muted: false,\n volume: 0.8,\n playbackSpeed: 1.0,\n preload: 'metadata',\n // Optional initial duration (seconds) so UI can show duration\n // before media metadata is loaded (useful with deferLoad/preload=none).\n initialDuration: 0,\n // When enabled, VidPly will not start network loading during init().\n // - HTML5: does not call element.load() until the first user-initiated play()\n // - HLS (hls.js): does not load manifest/segments until the first play()\n // - DASH (dash.js): does not attach a source until the first play()\n // This is useful for pages with many players to avoid high initial bandwidth.\n deferLoad: false,\n // When enabled, clicking Audio Description / Sign Language before playback will show\n // a notice instead of implicitly starting playback/loading.\n requirePlaybackForAccessibilityToggles: false,\n startTime: 0,\n playsInline: true, // Enable inline playback on iOS (prevents native fullscreen)\n\n // Controls\n controls: true,\n hideControlsDelay: 3000,\n playPauseButton: true,\n progressBar: true,\n currentTime: true,\n duration: true,\n volumeControl: true,\n muteButton: true,\n chaptersButton: true,\n qualityButton: true,\n captionStyleButton: true,\n speedButton: true,\n // When enabled, the playback speed UI is suppressed for ALL HLS streams (audio + video).\n hideSpeedForHls: false,\n // When enabled, the playback speed UI is suppressed for HLS *video* streams only.\n // This is useful for live streams where speed controls don't make sense.\n hideSpeedForHlsVideo: false,\n // When enabled, the playback speed UI is suppressed for ALL DASH streams (audio + video).\n hideSpeedForDash: false,\n // When enabled, the playback speed UI is suppressed for DASH *video* streams only.\n hideSpeedForDashVideo: false,\n captionsButton: true,\n transcriptButton: true,\n fullscreenButton: true,\n helpButton: true,\n pipButton: false,\n floating: false,\n floatingPosition: 'bottom-right',\n floatingMinViewportWidth: 768,\n downloadButton: false,\n downloadUrl: null,\n downloadFormat: null,\n downloadFileSize: null,\n downloadFetchSize: true,\n\n // Seeking\n seekInterval: 10,\n seekIntervalLarge: 30,\n\n // Captions\n captions: true,\n captionsDefault: false,\n captionsFontSize: '100%',\n captionsFontFamily: 'sans-serif',\n captionsColor: '#FFFFFF',\n captionsBackgroundColor: '#000000',\n captionsOpacity: 0.8,\n\n // Audio Description\n audioDescription: true,\n audioDescriptionSrc: null, // URL to audio-described version\n audioDescriptionButton: true,\n audioDescriptionMode: 'auto',\n audioDescriptionSpeech: true,\n audioDescriptionExtended: true,\n\n // Sign Language\n signLanguage: true,\n signLanguageSrc: null, // URL to sign language video\n signLanguageButton: true,\n signLanguagePosition: 'bottom-right', // Position: 'bottom-right', 'bottom-left', 'top-right', 'top-left'\n signLanguageDisplayMode: 'both', // Display mode: 'pip' (overlay), 'main' (source swap), 'both'\n\n // Transcripts\n transcript: false,\n transcriptPosition: 'external',\n transcriptContainer: null,\n\n // Keyboard\n keyboard: true,\n keyboardShortcuts: {\n 'play-pause': [' ', 'p', 'k'],\n 'volume-up': ['ArrowUp'],\n 'volume-down': ['ArrowDown'],\n 'seek-forward': ['ArrowRight'],\n 'seek-backward': ['ArrowLeft'],\n 'mute': ['m'],\n 'fullscreen': ['f'],\n 'captions': ['c'],\n 'caption-style-menu': ['a'],\n 'speed-up': ['>'],\n 'speed-down': ['<'],\n 'speed-menu': ['s'],\n 'quality-menu': ['q'],\n 'chapters-menu': ['j'],\n 'transcript-toggle': ['t'],\n 'help': ['?']\n },\n\n // Accessibility\n ariaLabels: {},\n screenReaderAnnouncements: true,\n highContrast: false,\n focusHighlight: true,\n metadataAlerts: {},\n metadataHashtags: {},\n\n // Languages\n language: 'en',\n languages: ['en'],\n\n // Resume Playback\n resumePlayback: true, // Enable saving and resuming playback position\n resumeThreshold: 10, // Don't resume if < threshold seconds watched\n resumePrompt: true, // Show prompt to resume (false = auto-resume silently)\n\n // Thumbnail Preview\n thumbnailPreview: true, // Enable/disable thumbnail preview on seek bar\n thumbnailCacheSize: 50, // Max cached thumbnails (default increased from 20)\n thumbnailPregenerate: true, // Pre-generate thumbnails during idle time\n thumbnailInterval: 10, // Pre-generation interval in seconds\n thumbnailWidth: 160, // Thumbnail width\n thumbnailHeight: 90, // Thumbnail height\n thumbnailQuality: 0.8, // Thumbnail JPEG quality\n\n // Lazy Loading (primarily used by index.js auto-init)\n lazyInit: true, // Enable lazy initialization via IntersectionObserver\n lazyMargin: '200px', // Root margin for IntersectionObserver\n\n // Theming\n theme: 'dark', // Theme: 'dark', 'light', 'minimal', 'high-contrast'\n themeVariables: {}, // Custom CSS variable overrides (e.g., { 'primary': '#ff0000' })\n\n // Advanced\n debug: false,\n classPrefix: 'vidply',\n iconType: 'svg',\n pauseOthersOnPlay: true,\n\n // Callbacks\n onReady: null,\n onPlay: null,\n onPause: null,\n onEnded: null,\n onTimeUpdate: null,\n onVolumeChange: null,\n onError: null,\n\n ...options\n };\n\n this.options.metadataAlerts = this.options.metadataAlerts || {};\n this.options.metadataHashtags = this.options.metadataHashtags || {};\n\n // Notice UI\n this.noticeElement = null;\n this.noticeTimeout = null;\n\n // Storage manager\n this.storage = new StorageManager('vidply');\n\n // Theme manager — cheap to construct (no DOM work in ctor) so we\n // wire it up immediately. `applyTheme()` is called later once the\n // container exists.\n this.themeManager = new ThemeManager(this);\n this.posterManager = new PosterManager(this);\n this.responsiveManager = new ResponsiveManager(this);\n\n // Load saved player preferences\n const savedPrefs = this.storage.getPlayerPreferences();\n if (savedPrefs) {\n if (typeof savedPrefs.volume === 'number') this.options.volume = savedPrefs.volume;\n if (typeof savedPrefs.playbackSpeed === 'number') this.options.playbackSpeed = savedPrefs.playbackSpeed;\n if (typeof savedPrefs.muted === 'boolean') this.options.muted = savedPrefs.muted;\n }\n\n // State\n this.state = {\n ready: false,\n playing: false,\n paused: true,\n ended: false,\n buffering: false,\n seeking: false,\n hasStartedPlayback: false,\n muted: this.options.muted,\n volume: this.options.volume,\n currentTime: 0,\n duration: Number(this.options.initialDuration) > 0 ? Number(this.options.initialDuration) : 0,\n playbackSpeed: this.options.playbackSpeed,\n fullscreen: false,\n pip: false,\n floating: null,\n captionsEnabled: this.options.captionsDefault,\n currentCaption: null,\n controlsVisible: true,\n audioDescriptionEnabled: false,\n signLanguageEnabled: false,\n signLanguageInMainView: false,\n resumePromptVisible: false\n };\n\n // Resume playback: prompt DOM element lives here so\n // `ResumeManager` can attach/detach it without leaking container\n // state back into the manager.\n this.resumePromptElement = null;\n\n // Store original source for toggling\n this.originalSrc = null;\n this.audioDescriptionSrc = this.options.audioDescriptionSrc;\n this.signLanguageSrc = this.options.signLanguageSrc;\n this.signLanguageSources = this.options.signLanguageSources || {}; // Map of lang codes to video URLs\n this.currentSignLanguage = null; // Current selected sign language code\n this.signLanguageVideo = null;\n // Store references to source elements with audio description attributes\n this.audioDescriptionSourceElement = null;\n this.originalAudioDescriptionSource = null;\n // Store caption tracks that should be swapped for audio description\n this.audioDescriptionCaptionTracks = [];\n this._audioDescriptionDesiredState = false;\n\n // DOM query cache (for performance optimization)\n this._textTracksCache = null;\n this._textTracksDirty = true;\n this._sourceElementsCache = null;\n this._sourceElementsDirty = true;\n this._trackElementsCache = null;\n this._trackElementsDirty = true;\n\n // Timeout management (for cleanup)\n this.timeouts = new Set();\n\n // Components\n this.container = document.createElement('div');\n this.renderer = null;\n this.controlBar = null;\n this.captionManager = null;\n this.keyboardManager = null;\n this.settingsDialog = null;\n\n // Metadata handling\n this.metadataCueChangeHandler = null;\n\n // Feature managers (lazy-loaded)\n this.audioDescriptionManager = null;\n this.signLanguageManager = null;\n this._managersLoading = null; // Promise for loading managers\n\n // Backward-compatible property aliases for SignLanguageManager\n // These allow existing code to reference this.signLanguageWrapper, etc.\n Object.defineProperties(this, {\n signLanguageWrapper: {\n get: () => this.signLanguageManager?.wrapper,\n set: (v) => {\n if (this.signLanguageManager) this.signLanguageManager.wrapper = v;\n }\n },\n signLanguageVideo: {\n get: () => this.signLanguageManager?.video,\n set: (v) => {\n if (this.signLanguageManager) this.signLanguageManager.video = v;\n }\n },\n signLanguageHeader: {\n get: () => this.signLanguageManager?.header,\n set: (v) => {\n if (this.signLanguageManager) this.signLanguageManager.header = v;\n }\n },\n signLanguageSettingsButton: {\n get: () => this.signLanguageManager?.settingsButton,\n set: (v) => {\n if (this.signLanguageManager) this.signLanguageManager.settingsButton = v;\n }\n },\n signLanguageSettingsMenu: {\n get: () => this.signLanguageManager?.settingsMenu,\n set: (v) => {\n if (this.signLanguageManager) this.signLanguageManager.settingsMenu = v;\n }\n },\n signLanguageSettingsMenuVisible: {\n get: () => this.signLanguageManager?.settingsMenuVisible,\n set: (v) => {\n if (this.signLanguageManager) this.signLanguageManager.settingsMenuVisible = v;\n }\n },\n signLanguageDraggable: {\n get: () => this.signLanguageManager?.draggable,\n set: (v) => {\n if (this.signLanguageManager) this.signLanguageManager.draggable = v;\n }\n },\n currentSignLanguage: {\n get: () => this.signLanguageManager?.currentLanguage,\n set: (v) => {\n if (this.signLanguageManager) this.signLanguageManager.currentLanguage = v;\n }\n }\n });\n\n // Initialize\n this.init();\n }\n\n /** Convenience getter for subsystems that take an AbortSignal. */\n get lifecycleSignal(): AbortSignal {\n return this._lifecycleController.signal;\n }\n\n /**\n * Get cached text tracks array\n * @returns {Array} Array of text tracks\n */\n get textTracks() {\n if (!this._textTracksCache || this._textTracksDirty) {\n this._textTracksCache = Array.from(this.element.textTracks || []);\n this._textTracksDirty = false;\n }\n return this._textTracksCache;\n }\n\n /**\n * Get cached source elements array\n * @returns {Array} Array of source elements\n */\n get sourceElements() {\n if (!this._sourceElementsCache || this._sourceElementsDirty) {\n this._sourceElementsCache = Array.from(this.element.querySelectorAll('source'));\n this._sourceElementsDirty = false;\n }\n return this._sourceElementsCache;\n }\n\n /**\n * Get cached track elements array\n * @returns {Array} Array of track elements\n */\n get trackElements() {\n if (!this._trackElementsCache || this._trackElementsDirty) {\n this._trackElementsCache = Array.from(this.element.querySelectorAll('track'));\n this._trackElementsDirty = false;\n }\n return this._trackElementsCache;\n }\n\n /**\n * Show a small in-player notice (non-blocking), also announced to screen readers.\n */\n showNotice(message: string, {timeout = 2500, priority = 'polite' as 'polite' | 'assertive'} = {}) {\n try {\n if (!message) return;\n if (!this.container) return;\n\n if (this.keyboardManager?.announce) {\n this.keyboardManager.announce(message, priority);\n }\n\n if (!this.noticeElement) {\n const el = document.createElement('div');\n el.className = `${this.options.classPrefix}-notice`;\n el.setAttribute('role', 'status');\n el.setAttribute('aria-live', priority);\n el.setAttribute('aria-atomic', 'true');\n el.style.position = 'absolute';\n el.style.left = '0.75rem';\n el.style.right = '0.75rem';\n el.style.top = '0.75rem';\n el.style.zIndex = '9999';\n el.style.padding = '0.5rem 0.75rem';\n el.style.borderRadius = '0.5rem';\n el.style.background = 'rgba(0, 0, 0, 0.75)';\n el.style.color = '#fff';\n el.style.fontSize = '0.875rem';\n el.style.lineHeight = '1.3';\n el.style.pointerEvents = 'none';\n this.noticeElement = el;\n this.container.appendChild(el);\n }\n\n const noticeElement = this.noticeElement;\n noticeElement.textContent = message;\n noticeElement.style.display = 'block';\n\n if (this.noticeTimeout) {\n clearTimeout(this.noticeTimeout);\n this.noticeTimeout = null;\n }\n this.noticeTimeout = setTimeout(() => {\n if (this.noticeElement) {\n this.noticeElement.style.display = 'none';\n }\n }, timeout);\n } catch {\n // ignore\n }\n }\n\n async init() {\n try {\n this.log('Initializing VidPly player');\n\n // Load custom language files if specified\n if (this.options.languageFiles) {\n try {\n await i18n.loadLanguagesFromUrls(this.options.languageFiles);\n } catch (error: unknown) {\n console.warn('Failed to load some language files:', error);\n }\n }\n\n if (this.options.languageFile && this.options.languageFileUrl) {\n try {\n await i18n.loadLanguageFromUrl(this.options.languageFile, this.options.languageFileUrl);\n this.log(`Custom language file loaded for ${this.options.languageFile}`);\n } catch (error: unknown) {\n console.warn(`Failed to load language file for ${this.options.languageFile}:`, error);\n }\n }\n\n // Auto-detect language from HTML lang attribute if not explicitly set\n if (!this.options.language || this.options.language === 'en') {\n const htmlLang = this.detectHtmlLanguage();\n if (htmlLang) {\n this.options.language = htmlLang;\n this.log(`Auto-detected language from HTML: ${htmlLang}`);\n }\n }\n\n // Ensure we have a language set (default to 'en' if not set)\n if (!this.options.language) {\n this.options.language = 'en';\n }\n\n // Ensure requested language is available (loads built-ins on demand)\n await i18n.ensureLanguage(this.options.language);\n\n // Set language\n i18n.setLanguage(this.options.language);\n\n // Create container\n this.createContainer();\n\n // Suppress native Picture-in-Picture when the custom floating\n // player is enabled. This removes Chrome's hover PiP button and\n // the \"Picture in Picture\" entry in the video context menu and\n // prevents the remote playback indicator from hijacking playback.\n if (this.options.floating && this.element && this.element.tagName === 'VIDEO') {\n try {\n const mediaEl = this.element as HTMLMediaElement & {\n disablePictureInPicture?: boolean;\n disableRemotePlayback?: boolean\n };\n mediaEl.disablePictureInPicture = true;\n mediaEl.disableRemotePlayback = true;\n this.element.setAttribute('disablepictureinpicture', '');\n this.element.setAttribute('disableremoteplayback', '');\n } catch (err) {\n this.log(`Failed to disable native PiP: ${err}`, 'warn');\n }\n }\n\n const src: string | undefined = this.element.src || this.element.querySelector('source')?.src;\n if (src) {\n await this.initializeRenderer();\n } else {\n this.log('No initial source - waiting for playlist or manual load');\n }\n\n // Initialize feature managers (lazy-loaded based on options)\n await this.initFeatureManagers();\n\n // Create controls\n if (this.options.controls) {\n this.controlBar = new ControlBar(this);\n this.videoWrapper?.appendChild(this.controlBar.element);\n }\n\n // Initialize captions\n if (this.options.captions) {\n this.captionManager = new CaptionManager(this);\n }\n\n // Initialize transcript lazily unless explicitly requested\n if (this.options.transcript) {\n await this.ensureTranscriptManager();\n }\n\n // Always set up metadata track handling (independent of transcript)\n this.setupMetadataHandling();\n\n // Initialize keyboard controls\n if (this.options.keyboard) {\n this.keyboardManager = new KeyboardManager(this);\n }\n\n // Integrate with OS media controls (Media Session API)\n if (this.options.mediaSession) {\n this.mediaSessionManager = new MediaSessionManager(this);\n }\n\n // Set up responsive handlers\n this.setupResponsiveHandlers();\n\n // Set initial state\n if (this.options.startTime > 0) {\n this.seek(this.options.startTime);\n }\n\n // Apply volume and mute settings after renderer is initialized\n // Use requestAnimationFrame to ensure renderer is fully ready\n requestAnimationFrame(() => {\n if (this.options.muted) {\n this.mute();\n } else if (this.renderer && this.renderer.media) {\n // Ensure media element is not muted if options say it shouldn't be\n this.renderer.setMuted(false);\n }\n\n if (this.options.volume !== 0.8) {\n this.setVolume(this.options.volume);\n } else if (this.renderer && this.renderer.media) {\n // Ensure volume is set even if it's the default\n this.renderer.setVolume(this.options.volume);\n }\n });\n\n // Initialize resume playback feature\n if (this.options.resumePlayback) {\n this.initResumePlayback();\n }\n\n // Mark as ready\n this.state.ready = true;\n this._originalElement.classList.add('vidply-initialized');\n this.emit('ready');\n\n if (this.options.onReady) {\n this.options.onReady.call(this);\n }\n\n // Autoplay if enabled\n if (this.options.autoplay) {\n this.play();\n }\n\n this.log('Player initialized successfully');\n } catch (error: unknown) {\n this.handleError(error);\n }\n }\n\n /**\n * Ensure the transcript manager is available, creating it on demand.\n * This keeps the initial load fast when transcripts are not needed.\n */\n async ensureTranscriptManager() {\n if (this.transcriptManager) {\n return this.transcriptManager;\n }\n\n if (!this.options.transcript && !this.options.transcriptButton) {\n return null;\n }\n\n const module = await import('../controls/TranscriptManager.js');\n const fallbackDefault = (module as { default?: typeof TranscriptManager }).default;\n const Manager = module.TranscriptManager || fallbackDefault;\n\n if (!Manager) {\n return null;\n }\n\n this.transcriptManager = new Manager(this);\n return this.transcriptManager;\n }\n\n /**\n * Toggle transcript visibility, lazily creating the manager if necessary.\n */\n async toggleTranscript() {\n const manager = await this.ensureTranscriptManager();\n if (!manager) return;\n\n manager.toggleTranscript();\n if (this.controlBar) {\n this.controlBar.updateTranscriptButton();\n }\n }\n\n /**\n * Ensure the audio description manager is available, creating it on demand.\n * This keeps the initial load fast when an audio description is not needed.\n */\n async ensureAudioDescriptionManager() {\n if (this.audioDescriptionManager) {\n return this.audioDescriptionManager;\n }\n\n // Only load the chunk when there is actual audio-description content to\n // drive it. The button option alone (default `true`) is not enough — the\n // control bar already hides the button when no content is present, so a\n // content-less player never needs this manager.\n if (!this.hasAudioDescriptionContent()) {\n return null;\n }\n\n const AudioDescManager = await loadAudioDescriptionManager();\n this.audioDescriptionManager = new AudioDescManager(this);\n return this.audioDescriptionManager;\n }\n\n /**\n * True when the current media actually exposes audio-description content:\n * an explicit described-audio source, `<source>` elements carrying\n * `data-desc-src` / `data-orig-src`, or a `descriptions` text track.\n * Mirrors `ControlBar.hasAudioDescription()` so the chunk load and the\n * button visibility stay in lock-step.\n */\n hasAudioDescriptionContent(): boolean {\n if (this.options.audioDescriptionSrc || this.audioDescriptionSrc) {\n return true;\n }\n\n const hasSourceElementsWithDesc = this.sourceElements.some(\n (el: HTMLSourceElement) => el.getAttribute('data-desc-src') || el.getAttribute('data-orig-src')\n );\n if (hasSourceElementsWithDesc) {\n return true;\n }\n\n const textTracks = this.element ? Array.from(this.element.textTracks || []) : [];\n return textTracks.some((track) => track.kind === 'descriptions');\n }\n\n // ============================================\n // Resume Playback Methods\n // ============================================\n\n /**\n * Ensure the sign language manager is available, creating it on demand.\n * This keeps the initial load fast when sign language is not needed.\n */\n async ensureSignLanguageManager() {\n if (this.signLanguageManager) {\n return this.signLanguageManager;\n }\n\n // Only load the chunk when an actual sign-language source is configured.\n // As with audio description, the button alone (default `true`) is not a\n // reason to pull in the manager on content-less players.\n if (!this.hasSignLanguageContent()) {\n return null;\n }\n\n const SignLangManager = await loadSignLanguageManager();\n this.signLanguageManager = new SignLangManager(this);\n return this.signLanguageManager;\n }\n\n /**\n * True when a sign-language video source (single `signLanguageSrc` or a\n * `signLanguageSources` map) is configured. Mirrors\n * `ControlBar.hasSignLanguage()`.\n */\n hasSignLanguageContent(): boolean {\n if (this.options.signLanguageSrc || this.signLanguageSrc) {\n return true;\n }\n return !!(\n this.options.signLanguageSources &&\n Object.keys(this.options.signLanguageSources).length > 0\n );\n }\n\n /**\n * Lazy-load and instantiate the floating (in-page PiP) manager. Only\n * created when `options.floating === true` and the media element is a\n * <video>. Audio-only players never float.\n */\n async ensureFloatingPlayerManager() {\n if (this.floatingPlayerManager) {\n return this.floatingPlayerManager;\n }\n if (!this.options.floating) {\n return null;\n }\n if (!this.element || this.element.tagName !== 'VIDEO') {\n return null;\n }\n const FloatingManager = await loadFloatingPlayerManager();\n this.floatingPlayerManager = new FloatingManager(this);\n return this.floatingPlayerManager;\n }\n\n /**\n * Initialize feature managers if needed (called during init)\n */\n async initFeatureManagers() {\n const promises = [];\n\n // Load the audio-description manager only when the media actually carries\n // description content (described-audio source, `data-desc-src` sources, or\n // a `descriptions` text track). The button option alone no longer pulls in\n // the chunk — content-less players stay lean. Late-arriving content (e.g.\n // streaming description tracks) is handled by `updateAccessibilityButtons`\n // + the lazy `ensureAudioDescriptionManager()` on first toggle.\n if (this.hasAudioDescriptionContent()) {\n promises.push(this.ensureAudioDescriptionManager());\n }\n\n // Load the sign-language manager only when a sign-language source exists.\n if (this.hasSignLanguageContent()) {\n promises.push(this.ensureSignLanguageManager());\n }\n\n // Load floating (custom in-page PiP) manager if enabled\n if (this.options.floating && this.element && this.element.tagName === 'VIDEO') {\n promises.push(this.ensureFloatingPlayerManager());\n }\n\n if (promises.length > 0) {\n await Promise.all(promises);\n }\n\n if (this.audioDescriptionManager) {\n this.audioDescriptionManager.initFromSourceElements(this.sourceElements, this.trackElements);\n }\n }\n\n /**\n * Detect language from HTML lang attribute\n * @returns {string|null} Language code if available in translations or as built-in, null otherwise\n */\n detectHtmlLanguage() {\n // Try to get lang from html element\n const htmlLang = document.documentElement.lang || document.documentElement.getAttribute('lang');\n\n if (!htmlLang) {\n return null;\n }\n\n // Normalize the language code (e.g., \"en-US\" -> \"en\", \"de-DE\" -> \"de\")\n const normalizedLang = htmlLang.toLowerCase().split('-')[0];\n if (!normalizedLang) {\n return null;\n }\n\n // Check if this language is available in our translations (already loaded)\n if (i18n.translations[normalizedLang]) {\n return normalizedLang;\n }\n\n // Check if this language is available as a built-in that can be loaded on demand\n const i18nWithLoaders = i18n as unknown as { builtInLanguageLoaders?: Record<string, unknown> };\n if (i18nWithLoaders.builtInLanguageLoaders && i18nWithLoaders.builtInLanguageLoaders[normalizedLang]) {\n return normalizedLang;\n }\n\n // Language not available, will fall back to English\n this.log(`Language \"${htmlLang}\" not available, using English as fallback`);\n return null; // Return null instead of 'en' to let the default language handling work\n }\n\n /**\n * Initialise the resume-playback feature. Lazily constructs a\n * `ResumeManager` on first use so disabled pages don't pay the DOM\n * / listener cost. Repeat calls are safe — the manager's own\n * `init()` is idempotent.\n */\n initResumePlayback(): void {\n if (!this.resumeManager) {\n this.resumeManager = new ResumeManager(this);\n }\n this.resumeManager.init();\n }\n\n /**\n * Get a unique identifier for the current video\n * Uses data-video-id attribute if available, otherwise hashes the source URL\n * @returns {string|null} Video ID or null if not available\n */\n getVideoId() {\n // First check for explicit video ID attribute\n const explicitId = this.element.getAttribute('data-video-id') ||\n this.element.dataset.videoId ||\n this._originalElement?.getAttribute('data-video-id') ||\n this._originalElement?.dataset?.videoId;\n\n if (explicitId) {\n return explicitId;\n }\n\n // Get source URL\n let src: string | null | undefined = this.element.src;\n if (!src) {\n const sourceEl = this.element.querySelector<HTMLSourceElement>('source');\n src = sourceEl?.src;\n }\n\n if (!src) {\n return null;\n }\n\n return this._hashString(src);\n }\n\n /**\n * Simple string hash function\n * @param {string} str - String to hash\n * @returns {string} Hash string\n */\n _hashString(str: string) {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash = hash & hash;\n }\n return 'v_' + Math.abs(hash).toString(36);\n }\n\n // Resume-playback delegates. Implementations live in\n // `core/ResumeManager.ts`; these stubs keep the public API.\n saveProgress(): void { this.resumeManager?.saveProgress(); }\n\n checkForResume(): void { this.resumeManager?.checkForResume(); }\n\n showResumePrompt(savedTime: number): void { this.resumeManager?.showPrompt(savedTime); }\n\n hideResumePrompt(): void { this.resumeManager?.hidePrompt(); }\n\n // Theme delegates. All four keep their original names so external\n // callers keep working; the real work is in `core/ThemeManager.ts`.\n applyTheme(): void { this.themeManager.apply(); }\n\n setTheme(themeName: ThemeName, customVariables: Record<string, string> = {}): void {\n this.themeManager.set(themeName, customVariables);\n }\n\n getTheme(): ThemeName | undefined { return this.themeManager.get(); }\n\n setThemeVariable(variableName: string, value: string): void {\n this.themeManager.setVariable(variableName, value);\n }\n\n resetTheme(): void { this.themeManager.reset(); }\n\n createContainer() {\n // Create main container with unique label for multiple players on same page\n const playerLabel = this.instanceId > 1\n ? `${i18n.t('player.label')} ${this.instanceId}`\n : i18n.t('player.label');\n\n // Use role=\"region\" (a labelled perceivable section) rather than\n // role=\"application\". role=\"application\" forces most screen readers into\n // application mode, suppressing their virtual-cursor/browse mode and\n // standard reading shortcuts for everything inside the player. A media\n // player built from native <button> controls and a custom slider does not\n // need that; a labelled region keeps normal AT navigation while the\n // control bar (role=\"region\"/toolbar) and keyboard shortcuts still work\n // when the container is focused (WCAG 4.1.2, 2.1.1).\n this.container = DOMUtils.createElement('div', {\n className: `${this.options.classPrefix}-player`,\n attributes: {\n 'role': 'region',\n 'aria-label': playerLabel,\n 'tabindex': '0'\n }\n });\n\n // Add media type class\n const mediaType = this.element.tagName.toLowerCase();\n this.container.classList.add(`${this.options.classPrefix}-${mediaType}`);\n\n // Add responsive class\n if (this.options.responsive) {\n this.container.classList.add(`${this.options.classPrefix}-responsive`);\n }\n\n // Create video wrapper (for proper positioning of controls)\n this.videoWrapper = DOMUtils.createElement('div', {\n className: `${this.options.classPrefix}-video-wrapper`\n });\n\n // Wrap original element\n this.element.parentNode?.insertBefore(this.container, this.element);\n\n // Create a track artwork element for single audio files (before video wrapper)\n // This shows the poster/artwork above the audio player (similar to playlists).\n // Poster URL is validated and CSS-escaped before interpolation.\n if (this.element.tagName === 'AUDIO' && this.options.poster) {\n const safePoster = sanitizePosterUrl(this.options.poster);\n if (safePoster) {\n this.trackArtworkElement = DOMUtils.createElement('div', {\n className: `${this.options.classPrefix}-track-artwork`,\n attributes: {\n 'aria-hidden': 'true'\n }\n });\n this.trackArtworkElement.style.backgroundImage = `url(\"${cssEscapeUrl(safePoster)}\")`;\n this.container.appendChild(this.trackArtworkElement);\n } else {\n this.log(`[VidPly] Ignored unsafe poster URL`, 'warn');\n }\n }\n\n this.container.appendChild(this.videoWrapper);\n this.videoWrapper.appendChild(this.element);\n\n // Hide native controls and set dimensions\n this.element.controls = false;\n this.element.removeAttribute('controls');\n this.element.setAttribute('tabindex', '-1'); // Remove from tab order\n this.element.style.width = '100%';\n this.element.style.height = '100%';\n\n // Enable inline playback on iOS (prevents native fullscreen)\n // This allows custom controls to work on iOS devices\n if (this.element.tagName === 'VIDEO' && this.options.playsInline) {\n this.element.setAttribute('playsinline', '');\n (this.element as HTMLVideoElement).playsInline = true; // Property version\n }\n\n // Set dimensions\n if (this.options.width) {\n this.container.style.width = typeof this.options.width === 'number'\n ? `${this.options.width}px`\n : this.options.width;\n }\n\n if (this.options.height) {\n this.container.style.height = typeof this.options.height === 'number'\n ? `${this.options.height}px`\n : this.options.height;\n }\n\n // Set a poster (convert relative paths to absolute URLs). The\n // resolvePosterPath() output is then re-validated to ensure no\n // disallowed scheme survived.\n if (this.options.poster && this.element.tagName === 'VIDEO') {\n const resolvedPoster = sanitizePosterUrl(this.resolvePosterPath(this.options.poster));\n if (resolvedPoster) {\n (this.element as HTMLVideoElement).poster = resolvedPoster;\n }\n }\n\n // Create centered play button overlay (only for video)\n if (this.element.tagName === 'VIDEO') {\n this.createPlayButtonOverlay();\n }\n\n this.createBufferingLoadingOverlay();\n\n (this.element as HTMLMediaElement & { vidply?: Player }).vidply = this;\n\n // Add to static instances array\n Player.instances.push(this);\n\n // Make video/audio element clickable to toggle play/pause. The\n // listener is wired to the Player's lifecycle AbortController so\n // destroy() removes it without an explicit removeEventListener\n // pair.\n this.element.style.cursor = 'pointer';\n this.element.addEventListener('click', (e) => {\n if (e.target === this.element) {\n this.toggle();\n }\n }, {signal: this.lifecycleSignal});\n\n this.on('play', () => {\n this.state.hasStartedPlayback = true;\n // Hide poster immediately when playing\n this.hidePosterOverlay();\n });\n\n this.on('timeupdate', () => {\n // Hide the poster when the video has started playing (currentTime > 0).\n // We additionally gate on `hasStartedPlayback` to ignore\n // background `currentTime` movements that the user did not\n // initiate — most notably dash.js startup gap-jump, which\n // synthesizes a small seek (often ~0.08s) right after MSE\n // attach to skip a manifest gap. Without this guard, that\n // synthetic seek hides the poster before the user has ever\n // pressed play, defeating the showPosterOverlay() call we\n // make from DASHRenderer.init() to keep the artwork visible\n // on top of the (transparent) <video> element.\n if (this.state.hasStartedPlayback && this.state.currentTime > 0) {\n this.hidePosterOverlay();\n }\n });\n\n // Also hide poster on loadeddata event (when first frame is loaded).\n // The same hasStartedPlayback gate as above: dash.js loads init+startup\n // segments at attachSource time, which triggers loadeddata before\n // the user clicks play. Hiding the poster there reveals the first\n // decoded frame instead of the artwork the publisher chose.\n this.element.addEventListener('loadeddata', () => {\n if (this.state.hasStartedPlayback && (this.state.playing || this.state.currentTime > 0)) {\n this.hidePosterOverlay();\n }\n }, {once: true});\n\n // Apply theme\n this.applyTheme();\n }\n\n createPlayButtonOverlay() {\n const overlay = createPlayOverlay();\n this.playButtonOverlay = overlay;\n\n overlay.addEventListener('click', () => {\n this.toggle();\n });\n\n this.videoWrapper?.appendChild(overlay);\n\n this.on('play', () => {\n overlay.style.opacity = '0';\n overlay.style.pointerEvents = 'none';\n });\n\n this.on('pause', () => {\n overlay.style.opacity = '1';\n overlay.style.pointerEvents = 'auto';\n this.positionPlayOverlayOnMobile();\n });\n\n this.on('ended', () => {\n overlay.style.opacity = '1';\n overlay.style.pointerEvents = 'auto';\n this.positionPlayOverlayOnMobile();\n });\n\n const debouncedPosition = debounce(() => {\n this.positionPlayOverlayOnMobile();\n }, 150);\n this.debouncedPositionPlayOverlay = debouncedPosition;\n\n window.addEventListener('resize', debouncedPosition, {signal: this.lifecycleSignal});\n\n this.on('loadedmetadata', () => {\n this.positionPlayOverlayOnMobile();\n });\n\n // Recalculate on fullscreen change with RAF\n this.on('enterfullscreen', () => {\n rafWithTimeout(() => this.positionPlayOverlayOnMobile(), 100);\n });\n\n this.on('exitfullscreen', () => {\n rafWithTimeout(() => this.positionPlayOverlayOnMobile(), 100);\n });\n }\n\n /**\n * Purely additive buffering spinner. Never touches play overlay or any other UI —\n * only toggles `vidply-buffering` on the container and manages its own `.vidply-loading` node.\n * Skipped for external providers (YouTube, Vimeo, SoundCloud) which have native loading UI.\n */\n createBufferingLoadingOverlay() {\n if (!this.videoWrapper) {\n return;\n }\n\n const prefix = this.options.classPrefix as string;\n const bufferingLabel = i18n.t('player.buffering');\n\n const loading = DOMUtils.createElement('div', {\n className: `${prefix}-loading`,\n attributes: {\n 'aria-busy': 'false'\n }\n });\n\n const srAnnouncer = DOMUtils.createElement('span', {\n className: `${prefix}-sr-only`,\n attributes: {\n id: `${prefix}-buffering-live-${this.instanceId}`,\n 'aria-live': 'polite',\n 'aria-atomic': 'true'\n }\n });\n loading.appendChild(srAnnouncer);\n\n this.loadingOverlayElement = loading;\n this.videoWrapper.appendChild(loading);\n\n const isExternalControls = () =>\n this.container?.classList.contains(`${prefix}-external-controls`);\n\n const showBuffering = () => {\n // Skip for external providers (YouTube, Vimeo, SoundCloud — they own\n // their own loading UI). Otherwise, allow the spinner whenever:\n // - the user has already started playback (normal mid-playback buffering), OR\n // - the user is currently seeking (e.g. scrubbed the seekbar before\n // pressing play, or after stop()) — they explicitly asked the\n // player to fetch data, so giving feedback is the right thing.\n if (isExternalControls()) {\n return;\n }\n if (!this.state.hasStartedPlayback && !this.state.seeking) {\n return;\n }\n this.container.classList.add(`${prefix}-buffering`);\n loading.setAttribute('aria-busy', 'true');\n srAnnouncer.textContent = bufferingLabel;\n };\n\n const hideBuffering = () => {\n if (!this.container.classList.contains(`${prefix}-buffering`)) {\n return;\n }\n this.container.classList.remove(`${prefix}-buffering`);\n loading.setAttribute('aria-busy', 'false');\n srAnnouncer.textContent = '';\n };\n\n this.on('waiting', showBuffering);\n this.on('canplay', hideBuffering);\n this.on('pause', hideBuffering);\n this.on('ended', hideBuffering);\n\n // Native `playing` — fires on the media element for all renderer types (HTML5, HLS, DASH).\n this._bufferingHideOnMediaPlaying = hideBuffering;\n this.element.addEventListener('playing', this._bufferingHideOnMediaPlaying);\n\n // Safety net: if timeupdate is ticking, media is clearly playing — clear any stale spinner.\n this.on('timeupdate', () => {\n if (this.container.classList.contains(`${prefix}-buffering`)) {\n hideBuffering();\n }\n });\n }\n\n positionPlayOverlayOnMobile() {\n if (!this.playButtonOverlay || this.element.tagName !== 'VIDEO') {\n return;\n }\n\n const mobile = isMobile();\n\n if (!mobile) {\n // Reset to CSS defaults on desktop\n this.playButtonOverlay.style.top = '';\n return;\n }\n\n const videoRect = this.element.getBoundingClientRect();\n const wrapperRect = this.videoWrapper?.getBoundingClientRect();\n if (!wrapperRect) return;\n const videoCenter = (videoRect.top - wrapperRect.top) + (videoRect.height / 2);\n\n this.playButtonOverlay.style.top = `${videoCenter}px`;\n }\n\n async initializeRenderer() {\n let src: string | null | undefined = this._pendingSource;\n let rendererClass: (new (player: Player) => Renderer) | null = null;\n\n if (!src) {\n const sourceElements = Array.from(this.element.querySelectorAll('source'));\n if (sourceElements.length > 1) {\n const negotiated = this._selectBestSource(sourceElements);\n src = negotiated.src;\n this._fallbackSources = negotiated.fallbacks;\n } else {\n src = this.element.src || sourceElements[0]?.src;\n this._fallbackSources = [];\n }\n } else {\n this._fallbackSources = [];\n }\n\n if (!src) {\n throw new Error('No media source found');\n }\n\n this.currentSource = src;\n this._pendingSource = null;\n\n // Pull in the audio-description manager on demand when this source carries\n // description content (it is no longer eagerly loaded for content-less\n // players). Then (re)scan the current source/track elements.\n if (this.hasAudioDescriptionContent()) {\n await this.ensureAudioDescriptionManager();\n }\n this.audioDescriptionManager?.initFromSourceElements(this.sourceElements, this.trackElements);\n\n if (!this.originalSrc) {\n this.originalSrc = src;\n }\n\n rendererClass = await this._detectRendererClass(src);\n\n this.log(`Using ${rendererClass?.name || 'HTML5Renderer'} renderer`);\n this.renderer = new rendererClass(this);\n\n const initTimeout = (this._fallbackSources?.length ?? 0) > 0 ? 10000 : 0;\n if (initTimeout > 0) {\n let timer: ReturnType<typeof setTimeout> | undefined;\n await Promise.race([\n this.renderer.init(),\n new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new Error(`Renderer init timed out after ${initTimeout}ms`)), initTimeout);\n }),\n ]).finally(() => {\n if (timer !== undefined) clearTimeout(timer);\n });\n } else {\n await this.renderer.init();\n }\n\n this.invalidateTrackCache();\n }\n\n async _detectRendererClass(src: string): Promise<new (player: Player) => Renderer> {\n type RendererCtor = new (player: Player) => Renderer;\n type RendererModule = Record<string, RendererCtor | undefined> & { default?: RendererCtor };\n if (src.includes('youtube.com') || src.includes('youtu.be')) {\n const module = await import('../renderers/YouTubeRenderer.js') as RendererModule;\n return (module.YouTubeRenderer ?? module.default) as RendererCtor;\n } else if (src.includes('vimeo.com')) {\n const module = await import('../renderers/VimeoRenderer.js') as RendererModule;\n return (module.VimeoRenderer ?? module.default) as RendererCtor;\n } else if (src.includes('.m3u8')) {\n const module = await import('../renderers/HLSRenderer.js') as RendererModule;\n return (module.HLSRenderer ?? module.default) as RendererCtor;\n } else if (src.includes('.mpd')) {\n const module = await import('../renderers/DASHRenderer.js') as RendererModule;\n return (module.DASHRenderer ?? module.default) as RendererCtor;\n } else if (src.includes('soundcloud.com') || src.includes('api.soundcloud.com')) {\n const module = await import('../renderers/SoundCloudRenderer.js') as RendererModule;\n return (module.SoundCloudRenderer ?? module.default) as RendererCtor;\n }\n return HTML5Renderer as unknown as RendererCtor;\n }\n\n _selectBestSource(sourceElements: HTMLSourceElement[]): {\n src: string;\n fallbacks: Array<{ src: string; type: string }>\n } {\n const hasMSE = typeof MediaSource !== 'undefined';\n type SourceInfo = { src: string; type: string; el: HTMLSourceElement };\n const sources: SourceInfo[] = sourceElements.map((el) => ({\n src: el.src || el.getAttribute('src') || '',\n type: el.type || el.getAttribute('type') || '',\n el,\n }));\n\n const canPlayNativeHLS = (() => {\n const v = document.createElement('video');\n return v.canPlayType('application/vnd.apple.mpegurl') !== '';\n })();\n\n let chosen: SourceInfo | undefined;\n\n if (hasMSE) {\n chosen = sources.find((s) => s.src.includes('.mpd'));\n }\n\n if (!chosen) {\n const hlsSource = sources.find((s) => s.src.includes('.m3u8'));\n if (hlsSource && (hasMSE || canPlayNativeHLS)) {\n chosen = hlsSource;\n }\n }\n\n if (!chosen) {\n chosen = sources.find((s) => !s.src.includes('.mpd') && !s.src.includes('.m3u8')) || sources[0];\n }\n\n const fallbacks = sources\n .filter((s) => s !== chosen)\n .map((s) => ({src: s.src, type: s.type}));\n\n return {src: chosen?.src ?? '', fallbacks};\n }\n\n async _fallbackToNextSource(): Promise<boolean> {\n if (!this._fallbackSources || this._fallbackSources.length === 0) {\n return false;\n }\n\n const next = this._fallbackSources.shift();\n if (!next) return false;\n this.log(`Falling back to next source: ${next.src}`);\n\n try {\n if (this.renderer && typeof this.renderer.destroy === 'function') {\n this.renderer.destroy();\n this.renderer = null;\n }\n\n this.currentSource = next.src;\n this._pendingSource = next.src;\n this._isFallingBack = true;\n await this.initializeRenderer();\n this._isFallingBack = false;\n return true;\n } catch {\n this.log(`Fallback source failed: ${next.src}`, 'warn');\n this._isFallingBack = false;\n return this._fallbackToNextSource();\n }\n }\n\n /**\n * Invalidate DOM query cache (call when tracks/sources change)\n */\n invalidateTrackCache() {\n this._textTracksDirty = true;\n this._trackElementsDirty = true;\n this._sourceElementsDirty = true;\n }\n\n /**\n * Find a text track by kind and optionally language\n * @param {string} kind - Track kind (captions, subtitles, descriptions, chapters, metadata)\n * @param {string} [language] - Optional language code\n * @returns {TextTrack|null} Found track or null\n */\n findTextTrack(kind: string, language: string | null = null) {\n const tracks = this.textTracks;\n if (language) {\n return tracks.find((t) => t.kind === kind && t.language === language);\n }\n return tracks.find((t) => t.kind === kind);\n }\n\n /**\n * Find a source element by attribute\n * @param {string} attribute - Attribute name (e.g., 'data-desc-src')\n * @param {string} [value] - Optional attribute value\n * @returns {Element|null} Found source element or null\n */\n findSourceElement(attribute: string, value: string | null = null) {\n const sources = this.sourceElements;\n if (value) {\n return sources.find((el) => el.getAttribute(attribute) === value);\n }\n return sources.find((el) => el.hasAttribute(attribute));\n }\n\n /**\n * Find a track element by its associated TextTrack\n * @param {TextTrack} track - The TextTrack object\n * @returns {Element|null} Found track element or null\n */\n findTrackElement(track: TextTrack) {\n return this.trackElements.find((el) => el.track === track);\n }\n\n // Poster delegates. Implementations live in `core/PosterManager.ts`.\n resolvePosterPath(posterPath: string | null | undefined): string {\n return this.posterManager.resolvePath(posterPath);\n }\n\n async generatePosterFromVideo(time = 10): Promise<string | null> {\n return this.posterManager.generateFromVideo(time);\n }\n\n async autoGeneratePoster(): Promise<void> {\n return this.posterManager.autoGenerate();\n }\n\n showPosterOverlay(): void { this.posterManager.showOverlay(); }\n\n hidePosterOverlay(): void { this.posterManager.hideOverlay(); }\n\n /**\n * Set a managed timeout that will be cleaned up on destroy\n * @param {Function} callback - Callback function\n * @param {number} delay - Delay in milliseconds\n * @returns {number} Timeout ID\n */\n setManagedTimeout(callback: () => void, delay: number): ReturnType<typeof setTimeout> {\n const timeoutId: ReturnType<typeof setTimeout> = setTimeout(() => {\n this.timeouts.delete(timeoutId);\n callback();\n }, delay);\n this.timeouts.add(timeoutId);\n return timeoutId;\n }\n\n /**\n * Clear a managed timeout\n * @param {number} timeoutId - Timeout ID to clear\n */\n clearManagedTimeout(timeoutId: ReturnType<typeof setTimeout> | null | undefined) {\n if (timeoutId) {\n clearTimeout(timeoutId);\n this.timeouts.delete(timeoutId);\n }\n }\n\n /**\n * Load new media source (for playlists)\n * @param {Object} config - Media configuration\n * @param {string} config.src - Media source URL\n * @param {string} config.type - Media MIME type\n * @param {string} [config.poster] - Poster image URL\n * @param {Array} [config.tracks] - Text tracks (captions, chapters, etc.)\n * @param {string} [config.audioDescriptionSrc] - Audio description video URL\n * @param {string} [config.signLanguageSrc] - Sign language video URL\n */\n /**\n * Check if a source URL requires an external renderer (YouTube, Vimeo, SoundCloud, HLS, DASH)\n * @param {string} src - Source URL\n * @returns {boolean}\n */\n isExternalRendererUrl(src: string | null | undefined) {\n if (!src) return false;\n return src.includes('youtube.com') ||\n src.includes('youtu.be') ||\n src.includes('vimeo.com') ||\n src.includes('soundcloud.com') ||\n src.includes('api.soundcloud.com') ||\n src.includes('.m3u8') ||\n src.includes('.mpd');\n }\n\n async load(config: PlayerLoadConfig) {\n try {\n this.log('Loading new media:', config.src);\n\n // Pause current playback\n if (this.renderer) {\n this.pause();\n }\n\n // Save scroll position to prevent browser from auto-scrolling when loading new media\n const scrollX = window.scrollX || window.pageXOffset;\n const scrollY = window.scrollY || window.pageYOffset;\n\n // Clear existing text tracks\n const existingTracks = this.trackElements;\n existingTracks.forEach((track) => track.remove());\n this.invalidateTrackCache();\n\n // Check if this is an external renderer URL\n const isExternalRenderer = this.isExternalRendererUrl(config.src);\n\n // Set flag early to suppress any errors that might fire during the transition\n // This prevents HTML5 element errors from triggering playlist auto-advance\n if (isExternalRenderer) {\n this._switchingRenderer = true;\n }\n\n // Only set src on HTML5 element for non-external sources\n // External renderers (YouTube, Vimeo, SoundCloud, HLS, DASH) handle their own media loading\n if (!isExternalRenderer) {\n this.element.src = config.src;\n\n if (config.type) {\n (this.element as HTMLMediaElement & { type?: string }).type = config.type;\n }\n } else {\n // For external renderers, clear the src to prevent HTML5 element errors\n // but store the URL for the renderer to use\n // DO NOT call load() here - it will trigger an error event on an element without a valid source\n this.element.removeAttribute('src');\n // Also clear any source elements to prevent errors\n const sources = this.element.querySelectorAll('source');\n sources.forEach(s => s.removeAttribute('src'));\n }\n\n // Store the source URL for external renderers to access\n this._pendingSource = config.src;\n\n this._isAudioContent = Boolean(config.type && config.type.startsWith('audio/'));\n\n // Apply or remove audio content class (16:3 aspect ratio for audio in video player)\n if (this.container) {\n if (this._isAudioContent) {\n this.container.classList.add('vidply-audio-content');\n } else {\n this.container.classList.remove('vidply-audio-content');\n }\n }\n\n // Handle poster display based on content type\n if (config.poster && this.element.tagName === 'VIDEO') {\n if (this._isAudioContent) {\n // For audio in video player: use CSS poster overlay with 16:3 aspect ratio\n this.element.removeAttribute('poster');\n if (this.videoWrapper) {\n const resolvedPoster = this.resolvePosterPath(config.poster);\n this.videoWrapper.style.setProperty('--vidply-poster-image', `url(\"${resolvedPoster}\")`);\n this.videoWrapper.classList.add('vidply-forced-poster');\n }\n } else {\n // For video: use normal poster and remove overlay\n (this.element as HTMLVideoElement).poster = this.resolvePosterPath(config.poster);\n if (this.videoWrapper) {\n this.videoWrapper.classList.remove('vidply-forced-poster');\n this.videoWrapper.style.removeProperty('--vidply-poster-image');\n }\n }\n }\n\n if (config.tracks && config.tracks.length > 0) {\n config.tracks.forEach((trackConfig) => {\n const track = document.createElement('track');\n track.src = trackConfig.src ?? '';\n track.kind = trackConfig.kind || 'captions';\n track.srclang = trackConfig.srclang || 'en';\n track.label = trackConfig.label || trackConfig.srclang || '';\n\n if (trackConfig.default) {\n track.default = true;\n }\n\n if (typeof trackConfig.describedSrc === 'string') {\n track.setAttribute('data-desc-src', trackConfig.describedSrc);\n }\n\n // Insert tracks at the beginning (before any flow content) for HTML5 validity\n const firstChild = this.element.firstChild;\n if (firstChild && firstChild.nodeType === Node.ELEMENT_NODE && (firstChild as Element).tagName !== 'TRACK') {\n this.element.insertBefore(track, firstChild);\n } else {\n this.element.appendChild(track);\n }\n });\n this.invalidateTrackCache();\n }\n\n // Remember accessibility feature states before switching tracks\n const wasSignLanguageEnabled = this.state.signLanguageEnabled;\n const wasAudioDescriptionEnabled = this.state.audioDescriptionEnabled;\n\n // Update sources from config FIRST (before hiding features)\n this.audioDescriptionSrc = config.audioDescriptionSrc || null;\n this.signLanguageSrc = config.signLanguageSrc || null;\n\n // Update original source for toggling\n this.originalSrc = config.src;\n\n // Update manager sources for playlist changes\n if (this.audioDescriptionManager) {\n this.audioDescriptionManager.updateSources(config.audioDescriptionSrc);\n // Reinitialize to pick up new track elements with data-desc-src attributes\n this.audioDescriptionManager.reinitialize();\n }\n if (this.signLanguageManager) {\n this.signLanguageManager.updateSources(config.signLanguageSrc, config.signLanguageSources);\n }\n\n // Hide accessibility features that were enabled (must happen AFTER updating sources)\n if (wasAudioDescriptionEnabled) {\n this.disableAudioDescription();\n }\n if (wasSignLanguageEnabled) {\n this.disableSignLanguage();\n }\n\n // Check if we need to change renderer type\n const shouldChangeRenderer = this.shouldChangeRenderer(config.src);\n\n // DASH and HLS renderers manage their own source loading via MSE/hls.js\n // and must be fully destroyed+reinitialized when switching sources,\n // even when the renderer type stays the same.\n const needsFullReinit = !shouldChangeRenderer && this.renderer &&\n (this.renderer.dash || this.renderer.hls);\n\n // Destroy old renderer if changing types or if MSE-based renderer needs reinit\n if ((shouldChangeRenderer || needsFullReinit) && this.renderer) {\n this.renderer.destroy();\n this.renderer = null;\n\n // Clean up dynamically added caption/transcript buttons from the\n // previous renderer. The new renderer will re-add them if its\n // stream contains text tracks.\n if (this.controlBar) {\n this.controlBar.removeHlsCaptionButtons(true);\n }\n if (this.transcriptManager?.isVisible) {\n this.transcriptManager.hideTranscript();\n }\n }\n\n // Initialize or reinitialize renderer\n if (!this.renderer || shouldChangeRenderer || needsFullReinit) {\n await this.initializeRenderer();\n } else {\n // Just reload the current renderer with the updated element\n this.renderer.media = this.element; // Update media reference\n if (this.options.deferLoad) {\n try {\n this.element.preload = this.options.preload || 'metadata';\n } catch {\n // ignore\n }\n // Reset renderer-level deferred flags if present (HTML5/HLS/DASH\n // renderers). These are renderer-private implementation details, not\n // part of the public Renderer contract, so reach them via a\n // structural cast rather than the interface.\n if (this.renderer) {\n const deferState = this.renderer as Partial<{\n _didDeferredLoad: boolean;\n _hlsSourceLoaded: boolean;\n _dashSourceLoaded: boolean;\n _pendingSrc: string | null;\n }>;\n if (typeof deferState._didDeferredLoad === 'boolean') {\n deferState._didDeferredLoad = false;\n }\n if (typeof deferState._hlsSourceLoaded === 'boolean') {\n deferState._hlsSourceLoaded = false;\n }\n if (typeof deferState._dashSourceLoaded === 'boolean') {\n deferState._dashSourceLoaded = false;\n }\n if ('_pendingSrc' in this.renderer) {\n // For HLS, store pending src for the first play() call\n deferState._pendingSrc = this._pendingSource || this.currentSource || null;\n }\n }\n } else {\n this.element.load();\n }\n }\n\n // Clear the renderer switching flag after a delay to catch async errors\n // This prevents errors from the old renderer's event queue from causing issues\n if (isExternalRenderer) {\n setTimeout(() => {\n this._switchingRenderer = false;\n }, 500);\n } else {\n this._switchingRenderer = false;\n }\n\n // Restore scroll position immediately after loading to prevent auto-scroll\n window.scrollTo(scrollX, scrollY);\n\n // For MSE-based renderers (DASH/HLS), skip CaptionManager/TranscriptManager\n // re-creation here. dash.js/hls.js create programmatic TextTrack objects\n // that persist on the <video> element after destroy() and cannot be removed\n // via standard APIs. Re-scanning element.textTracks would pick up these stale\n // tracks. Instead, the renderer's events (TEXT_TRACKS_ADDED etc.) will call\n // refreshTracks()/ensureCaptionsButton() once real tracks are available.\n if (needsFullReinit) {\n if (this.captionManager) {\n this.captionManager.disable();\n this.captionManager.tracks = [];\n }\n if (this.transcriptManager?.isVisible) {\n this.transcriptManager.hideTranscript();\n }\n } else {\n if (this.captionManager) {\n this.captionManager.destroy();\n this.captionManager = new CaptionManager(this);\n }\n\n if (this.transcriptManager) {\n const wasTranscriptVisible = this.transcriptManager.isVisible;\n this.transcriptManager.destroy();\n this.transcriptManager = null;\n\n const newManager = await this.ensureTranscriptManager();\n\n if (wasTranscriptVisible && this.controlBar && this.controlBar.hasCaptionTracks()) {\n newManager?.showTranscript();\n }\n }\n\n if (this.controlBar) {\n this.updateControlBar();\n }\n }\n\n // Restore scroll position after control bar update (may have caused micro-scrolls)\n window.scrollTo(scrollX, scrollY);\n\n // Restore accessibility features if they were enabled and available in new track\n if (wasSignLanguageEnabled && this.signLanguageSrc) {\n // Small delay to ensure player and control bar are ready\n setTimeout(() => {\n this.enableSignLanguage();\n // Restore scroll after sign language is shown\n window.scrollTo(scrollX, scrollY);\n }, 150);\n }\n\n if (wasAudioDescriptionEnabled && this.audioDescriptionSrc) {\n // Small delay to ensure player is ready\n setTimeout(() => {\n this.enableAudioDescription();\n // Restore scroll after audio description is enabled\n window.scrollTo(scrollX, scrollY);\n }, 150);\n }\n\n this.emit('sourcechange', config);\n this.log('Media loaded successfully');\n\n } catch (error: unknown) {\n this.handleError(error);\n }\n }\n\n /**\n * Ensure the current renderer has started its initial load (metadata/manifest)\n * without starting playback. This is useful for playlists to behave like\n * single videos on selection, while still keeping autoplay off.\n */\n ensureLoaded() {\n try {\n if (!this.renderer) return;\n if (typeof this.renderer.ensureLoaded === 'function') {\n this.renderer.ensureLoaded();\n }\n } catch {\n // ignore\n }\n }\n\n /**\n * Check if we need to change renderer type\n * @param {string} src - New source URL\n * @returns {boolean}\n */\n /**\n * Update the control bar to refresh button visibility based on available features\n */\n updateControlBar() {\n if (!this.controlBar) return;\n\n const controlBar = this.controlBar;\n\n // Clear existing controls content\n controlBar.element.innerHTML = '';\n\n // Recreate controls with updated feature detection\n controlBar.createControls();\n\n // Reattach events for the new controls\n controlBar.attachEvents();\n controlBar.setupAutoHide();\n controlBar.setupOverflowDetection();\n }\n\n shouldChangeRenderer(src: string) {\n if (!this.renderer) return true;\n\n const isYouTube = src.includes('youtube.com') || src.includes('youtu.be');\n const isVimeo = src.includes('vimeo.com');\n const isHLS = src.includes('.m3u8');\n const isDASH = src.includes('.mpd');\n const isSoundCloud = src.includes('soundcloud.com') || src.includes('api.soundcloud.com');\n\n const currentRendererName = this.renderer.constructor.name;\n\n if (isYouTube && currentRendererName !== 'YouTubeRenderer') return true;\n if (isVimeo && currentRendererName !== 'VimeoRenderer') return true;\n if (isHLS && currentRendererName !== 'HLSRenderer') return true;\n if (isDASH && currentRendererName !== 'DASHRenderer') return true;\n if (isSoundCloud && currentRendererName !== 'SoundCloudRenderer') return true;\n if (!isYouTube && !isVimeo && !isHLS && !isDASH && !isSoundCloud && currentRendererName !== 'HTML5Renderer') return true;\n\n return false;\n }\n\n // Playback controls\n play() {\n if (this.renderer) {\n this.renderer.play();\n return;\n }\n\n // Playlist support: if no renderer exists yet (no initial src),\n // start playback via playlist selection.\n if (this.playlistManager && Array.isArray(this.playlistManager.tracks) && this.playlistManager.tracks.length > 0) {\n const index = this.playlistManager.currentIndex >= 0 ? this.playlistManager.currentIndex : 0;\n this.playlistManager.play(index, true);\n }\n }\n\n pause() {\n if (this.renderer) {\n this.renderer.pause();\n }\n }\n\n stop() {\n this.pause();\n this.seek(0);\n }\n\n toggle() {\n if (this.state.playing) {\n this.pause();\n } else {\n this.play();\n }\n }\n\n /**\n * Seek to a non-negative finite second offset. Non-finite or non-numeric\n * inputs are silently dropped instead of being forwarded to the renderer\n * where they would set `currentTime = NaN` on an HTMLMediaElement.\n */\n seek(time: number): void {\n if (typeof time !== 'number' || !Number.isFinite(time)) return;\n const safeTime = time < 0 ? 0 : time;\n // Any user-initiated seek (seekbar drag/click, keyboard arrow,\n // skip buttons) flows through this method, so it's the right place\n // to drop the poster overlay: scrubbing communicates \"I want to see\n // a different frame\", and keeping the artwork on top would hide\n // exactly the frame the user is asking for. dash.js internal\n // startup gap-jump bypasses this method and sets\n // media.currentTime directly, so the poster stays put for that\n // case (which is what we want — the user hasn't engaged yet).\n this.hidePosterOverlay();\n if (this.renderer) {\n this.renderer.seek(safeTime);\n }\n }\n\n seekForward(interval: number = (this.options.seekInterval as number)): void {\n const step = Number.isFinite(interval) ? interval : 5;\n const targetTime = this.state.currentTime + step;\n // Only cap to duration if duration is known (> 0), otherwise let the media element handle it\n const seekTime = this.state.duration > 0 ? Math.min(targetTime, this.state.duration) : targetTime;\n this.seek(seekTime);\n }\n\n seekBackward(interval: number = (this.options.seekInterval as number)): void {\n const step = Number.isFinite(interval) ? interval : 5;\n this.seek(Math.max(this.state.currentTime - step, 0));\n }\n\n // Volume controls\n /**\n * Set the volume to a finite number in [0, 1]. Non-numeric or NaN\n * input is silently ignored.\n */\n setVolume(volume: number): void {\n if (typeof volume !== 'number' || !Number.isFinite(volume)) return;\n const newVolume = Math.max(0, Math.min(1, volume));\n if (this.renderer) {\n this.renderer.setVolume(newVolume);\n }\n this.state.volume = newVolume;\n\n // If volume is increased above 0 and currently muted, unmute\n if (newVolume > 0 && this.state.muted) {\n this.state.muted = false;\n if (this.renderer) {\n this.renderer.setMuted(false);\n }\n this.emit('volumechange');\n }\n\n this.savePlayerPreferences();\n }\n\n getVolume() {\n return this.state.volume;\n }\n\n mute() {\n if (this.renderer) {\n this.renderer.setMuted(true);\n }\n this.state.muted = true;\n this.savePlayerPreferences();\n this.emit('volumechange');\n }\n\n unmute() {\n if (this.renderer) {\n this.renderer.setMuted(false);\n }\n this.state.muted = false;\n this.savePlayerPreferences();\n this.emit('volumechange');\n }\n\n toggleMute() {\n if (this.state.muted) {\n this.unmute();\n } else {\n this.mute();\n }\n }\n\n // Playback speed\n /**\n * Set playback speed in [0.25, 2]. Silently rejects non-finite input.\n */\n setPlaybackSpeed(speed: number): void {\n if (typeof speed !== 'number' || !Number.isFinite(speed)) return;\n const newSpeed = Math.max(0.25, Math.min(2, speed));\n if (this.renderer) {\n this.renderer.setPlaybackSpeed(newSpeed);\n }\n this.state.playbackSpeed = newSpeed;\n this.savePlayerPreferences();\n this.emit('playbackspeedchange', newSpeed);\n }\n\n getPlaybackSpeed() {\n return this.state.playbackSpeed;\n }\n\n // Save player preferences to localStorage\n savePlayerPreferences() {\n this.storage.savePlayerPreferences({\n volume: this.state.volume,\n muted: this.state.muted,\n playbackSpeed: this.state.playbackSpeed\n });\n }\n\n // Fullscreen\n enterFullscreen() {\n const elem = this.container as HTMLElement & {\n webkitRequestFullscreen?: () => Promise<void> | void;\n mozRequestFullScreen?: () => Promise<void> | void;\n msRequestFullscreen?: () => Promise<void> | void;\n };\n let fullscreenPromise: Promise<void> | void | null = null;\n\n const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) ||\n (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);\n\n if (isIOS) {\n this._enablePseudoFullscreen();\n return;\n }\n\n if (elem.requestFullscreen) {\n fullscreenPromise = elem.requestFullscreen();\n } else if (elem.webkitRequestFullscreen) {\n fullscreenPromise = elem.webkitRequestFullscreen();\n } else if (elem.mozRequestFullScreen) {\n fullscreenPromise = elem.mozRequestFullScreen();\n } else if (elem.msRequestFullscreen) {\n fullscreenPromise = elem.msRequestFullscreen();\n }\n\n if (fullscreenPromise && typeof (fullscreenPromise as Promise<void>).catch === 'function') {\n (fullscreenPromise as Promise<void>).catch((err: Error) => {\n this.log('Fullscreen API failed, using pseudo-fullscreen:', err.message);\n this._enablePseudoFullscreen();\n });\n }\n\n if (!elem.requestFullscreen && !elem.webkitRequestFullscreen &&\n !elem.mozRequestFullScreen && !elem.msRequestFullscreen) {\n this._enablePseudoFullscreen();\n } else {\n // Optimistically set state (will be corrected by fullscreenChangeHandler if it fails)\n this.state.fullscreen = true;\n this.container.classList.add(`${this.options.classPrefix}-fullscreen`);\n this.emit('fullscreenchange', true);\n }\n }\n\n exitFullscreen() {\n const doc = document as Document & {\n webkitFullscreenElement?: Element | null;\n mozFullScreenElement?: Element | null;\n msFullscreenElement?: Element | null;\n webkitExitFullscreen?: () => Promise<void> | void;\n mozCancelFullScreen?: () => Promise<void> | void;\n msExitFullscreen?: () => Promise<void> | void;\n };\n const isInNativeFullscreen = Boolean(\n document.fullscreenElement ||\n doc.webkitFullscreenElement ||\n doc.mozFullScreenElement ||\n doc.msFullscreenElement\n );\n\n if (isInNativeFullscreen) {\n if (document.exitFullscreen) {\n document.exitFullscreen();\n } else if (doc.webkitExitFullscreen) {\n doc.webkitExitFullscreen();\n } else if (doc.mozCancelFullScreen) {\n doc.mozCancelFullScreen();\n } else if (doc.msExitFullscreen) {\n doc.msExitFullscreen();\n }\n } else {\n // We're in pseudo-fullscreen, exit it manually\n this._disablePseudoFullscreen();\n }\n\n this.state.fullscreen = false;\n this.container.classList.remove(`${this.options.classPrefix}-fullscreen`);\n this.emit('fullscreenchange', false);\n }\n\n toggleFullscreen() {\n if (this.state.fullscreen) {\n this.exitFullscreen();\n } else {\n this.enterFullscreen();\n }\n }\n\n // Pseudo-fullscreen fallback for iOS and browsers without Fullscreen API.\n // All of the real DOM + scroll + inert bookkeeping lives in\n // `PseudoFullscreenController`; Player keeps these thin delegates so\n // call sites elsewhere in the class stay readable.\n _enablePseudoFullscreen() {\n if (!this.pseudoFullscreen) {\n this.pseudoFullscreen = new PseudoFullscreenController(this);\n }\n this.pseudoFullscreen.enable();\n }\n\n _disablePseudoFullscreen() {\n this.pseudoFullscreen?.disable();\n }\n\n // Picture-in-Picture\n enterPiP() {\n // When the custom floating player is enabled, the control bar PiP\n // button is rebound to floatingPlayerManager.togglePinned(). This\n // direct API is redirected too so external callers don't accidentally\n // trigger native PiP after we disabled it.\n if (this.options.floating) {\n if (this.floatingPlayerManager) {\n this.floatingPlayerManager.togglePinned();\n }\n return;\n }\n const pipElement = this.element as HTMLVideoElement & {\n requestPictureInPicture?: () => Promise<PictureInPictureWindow>\n };\n if (typeof pipElement.requestPictureInPicture === 'function') {\n pipElement.requestPictureInPicture();\n this.state.pip = true;\n this.emit('pipchange', true);\n }\n }\n\n exitPiP() {\n if (this.options.floating) {\n if (this.floatingPlayerManager && this.state.floating) {\n this.floatingPlayerManager.exit('manual');\n }\n return;\n }\n if (document.pictureInPictureElement) {\n document.exitPictureInPicture();\n this.state.pip = false;\n this.emit('pipchange', false);\n }\n }\n\n togglePiP() {\n if (this.options.floating) {\n if (this.floatingPlayerManager) {\n this.floatingPlayerManager.togglePinned();\n }\n return;\n }\n if (this.state.pip) {\n this.exitPiP();\n } else {\n this.enterPiP();\n }\n }\n\n // Captions\n enableCaptions() {\n if (this.captionManager) {\n this.captionManager.enable();\n this.state.captionsEnabled = true;\n }\n }\n\n disableCaptions() {\n if (this.captionManager) {\n this.captionManager.disable();\n this.state.captionsEnabled = false;\n }\n }\n\n toggleCaptions() {\n if (this.state.captionsEnabled) {\n this.disableCaptions();\n } else {\n this.enableCaptions();\n }\n }\n\n /**\n * Check if a track file exists. Bounded by a 8s `AbortSignal.timeout`\n * and the player's lifecycle controller, so a slow / hung server cannot\n * keep a request alive past `destroy()`.\n */\n async validateTrackExists(url: string): Promise<boolean> {\n if (typeof url !== 'string' || !url) return false;\n const signals: AbortSignal[] = [this.lifecycleSignal];\n if (typeof AbortSignal !== 'undefined' && typeof AbortSignal.timeout === 'function') {\n signals.push(AbortSignal.timeout(8000));\n }\n const signal = signals.length === 1 ? signals[0] : (AbortSignal as {\n any?: (signals: AbortSignal[]) => AbortSignal\n }).any?.(signals) ?? signals[0];\n try {\n const response = await fetch(url, {method: 'HEAD', cache: 'no-cache', signal});\n return response.ok;\n } catch (error: unknown) {\n if (this.options.debug) {\n this.log(`validateTrackExists(\"${url}\") failed: ${(error as Error)?.message ?? error}`, 'warn');\n }\n return false;\n }\n }\n\n /**\n * Strip VTT formatting tags from caption text\n * @param {string} text - Caption text with VTT formatting\n * @returns {string} Plain text without formatting\n */\n stripVTTFormatting(text: string | null | undefined): string {\n if (!text) return '';\n return text\n .replace(/<[^>]+>/g, '')\n .replace(/\\n/g, ' ')\n .trim()\n .toLowerCase();\n }\n\n /**\n * Find matching caption time based on text content\n * Useful for syncing between videos of different lengths (e.g., with/without audio description)\n * @param {string} targetText - Caption text to search for\n * @param {Array} tracks - Array of caption tracks to search in\n * @returns {number|null} Start time of matching caption, or null if not found\n */\n findMatchingCaptionTime(targetText: string | null | undefined, tracks: Array<{\n kind?: string;\n track?: TextTrack | null\n }> | null | undefined) {\n if (!targetText || !tracks || tracks.length === 0) {\n return null;\n }\n\n const normalizedTarget = this.stripVTTFormatting(targetText);\n\n for (const trackInfo of tracks) {\n if (trackInfo.kind !== 'captions' && trackInfo.kind !== 'subtitles') {\n continue;\n }\n\n const track = trackInfo.track;\n if (!track || !track.cues) {\n continue;\n }\n\n for (let i = 0; i < track.cues.length; i++) {\n const cue = track.cues[i] as VTTCue;\n const cueText = this.stripVTTFormatting(cue.text);\n\n if (cueText === normalizedTarget) {\n return cue.startTime;\n }\n\n const targetWords = normalizedTarget.split(/\\s+/).filter((w) => w.length > 2);\n const cueWords = cueText.split(/\\s+/).filter((w) => w.length > 2);\n\n if (targetWords.length > 0 && cueWords.length > 0) {\n const matchingWords = targetWords.filter((word) => cueWords.includes(word));\n const matchRatio = matchingWords.length / targetWords.length;\n\n if (matchRatio >= 0.8) {\n return cue.startTime;\n }\n }\n }\n }\n\n return null;\n }\n\n // Audio Description (delegated to AudioDescriptionManager)\n async enableAudioDescription() {\n const manager = await this.ensureAudioDescriptionManager();\n return manager?.enable();\n }\n\n async disableAudioDescription() {\n const manager = await this.ensureAudioDescriptionManager();\n return manager?.disable();\n }\n\n async toggleAudioDescription() {\n if (this.options.requirePlaybackForAccessibilityToggles && !this.renderer && this.playlistManager?.tracks?.length) {\n this.showNotice(i18n.t('player.startPlaybackForAudioDescription'));\n return;\n }\n\n const manager = await this.ensureAudioDescriptionManager();\n if (!manager) return;\n\n // If user toggles audio description before the first track has been loaded,\n // remember desired state and start playback so the described source is loaded.\n if (!this.renderer && this.playlistManager && this.playlistManager.tracks?.length) {\n manager.desiredState = !manager.desiredState;\n this.state.audioDescriptionEnabled = manager.desiredState;\n this.emit(manager.desiredState ? 'audiodescriptionenabled' : 'audiodescriptiondisabled');\n // Start playback (PlaylistManager.play() will honor desiredState and load described src)\n this.play();\n return;\n }\n\n return manager.toggle();\n }\n\n // Sign Language (delegated to SignLanguageManager)\n async enableSignLanguage() {\n const manager = await this.ensureSignLanguageManager();\n return manager?.enable();\n }\n\n async disableSignLanguage() {\n const manager = await this.ensureSignLanguageManager();\n return manager?.disable();\n }\n\n async toggleSignLanguage() {\n if (this.options.requirePlaybackForAccessibilityToggles && !this.renderer && this.playlistManager?.tracks?.length) {\n this.showNotice(i18n.t('player.startPlaybackForSignLanguage'));\n return;\n }\n\n const manager = await this.ensureSignLanguageManager();\n if (!manager) return;\n\n // If user toggles sign language before the first track has been loaded,\n // enable the overlay and start playback so sign language video sync begins.\n if (!this.renderer && this.playlistManager && this.playlistManager.tracks?.length) {\n const wasEnabled = manager.enabled;\n const result = manager.toggle();\n if (!wasEnabled && manager.enabled) {\n this.play();\n }\n return result;\n }\n\n return manager.toggle();\n }\n\n setupSignLanguageInteraction() {\n return this.signLanguageManager?._setupInteraction();\n }\n\n switchSignLanguage(langCode: string) {\n return this.signLanguageManager?.switchLanguage(langCode);\n }\n\n showSignLanguageSettingsMenu() {\n return this.signLanguageManager?.showSettingsMenu();\n }\n\n hideSignLanguageSettingsMenu({focusButton = true} = {}) {\n return this.signLanguageManager?.hideSettingsMenu({focusButton});\n }\n\n constrainSignLanguagePosition() {\n return this.signLanguageManager?.constrainPosition();\n }\n\n saveSignLanguagePreferences() {\n return this.signLanguageManager?.savePreferences();\n }\n\n cleanupSignLanguage() {\n return this.signLanguageManager?.cleanup();\n }\n\n // Settings dialog removed - using individual control buttons instead\n showSettings() {\n console.warn('[VidPly] Settings dialog has been removed. Use individual control buttons (speed, captions, etc.)');\n }\n\n hideSettings() {\n // No-op - settings dialog removed\n }\n\n /**\n * Lazily build (on first use) and toggle the keyboard-shortcuts help\n * dialog. Reflects the live `keyboardShortcuts` bindings, including any\n * consumer overrides.\n */\n toggleKeyboardHelp() {\n if (!this.keyboardHelp) {\n this.keyboardHelp = new KeyboardHelp(this);\n }\n this.keyboardHelp.toggle();\n }\n\n showKeyboardHelp() {\n if (!this.keyboardHelp) {\n this.keyboardHelp = new KeyboardHelp(this);\n }\n this.keyboardHelp.show();\n }\n\n hideKeyboardHelp() {\n this.keyboardHelp?.hide();\n }\n\n // Utility methods\n getCurrentTime() {\n return this.state.currentTime;\n }\n\n getDuration() {\n return this.state.duration;\n }\n\n isPlaying() {\n return this.state.playing;\n }\n\n isPaused() {\n return this.state.paused;\n }\n\n isEnded() {\n return this.state.ended;\n }\n\n isMuted() {\n return this.state.muted;\n }\n\n isFullscreen() {\n return this.state.fullscreen;\n }\n\n handleError(error: unknown) {\n if (this._switchingRenderer || this._isFallingBack) {\n this.log('Suppressing error during renderer switch:', error, 'debug');\n return;\n }\n\n if (this._fallbackSources && this._fallbackSources.length > 0) {\n this.log('Renderer error, attempting fallback:', error, 'warn');\n this._fallbackToNextSource().then((success: boolean) => {\n if (!success) {\n this.log('All fallback sources exhausted', 'error');\n this.emit('error', error);\n if (this.options.onError) {\n this.options.onError.call(this, error);\n }\n }\n });\n return;\n }\n\n this.log('Error:', error, 'error');\n this.emit('error', error);\n\n if (this.options.onError) {\n this.options.onError.call(this, error);\n }\n }\n\n // Logging\n log(...messages: unknown[]) {\n if (!this.options.debug) {\n return;\n }\n\n let type: 'log' | 'warn' | 'error' | 'debug' | 'info' = 'log';\n const consoleObj = console as unknown as Record<string, (...a: unknown[]) => void>;\n if (messages.length > 0) {\n const potentialType = messages[messages.length - 1];\n if (typeof potentialType === 'string' && typeof consoleObj[potentialType] === 'function') {\n type = potentialType as typeof type;\n messages = messages.slice(0, -1);\n }\n }\n\n if (messages.length === 0) {\n messages = [''];\n }\n\n const consoleFn = consoleObj[type];\n if (typeof consoleFn === 'function') {\n consoleFn('[VidPly]', ...messages);\n } else {\n console.log('[VidPly]', ...messages);\n }\n }\n\n /**\n * Wire up resize / orientation / fullscreen listeners. Delegates to\n * `ResponsiveManager`; Player keeps the method name for backward\n * compatibility with external callers that start the feature\n * manually after swapping the container.\n */\n setupResponsiveHandlers(): void {\n this.responsiveManager.setup();\n }\n\n // Cleanup. Aborts the lifecycle controller (which removes every\n // window/document listener wired with `{ signal }` plus every\n // user-influenced fetch we threaded the signal into), cascade-destroys\n // every manager we own, and finally removes this instance from the\n // global `Player.instances` registry.\n destroy(): void {\n this.log('Destroying player');\n\n // Abort all listeners + in-flight fetches first so callbacks\n // running concurrently with destroy() see a torn-down player.\n try {\n this._lifecycleController.abort();\n } catch (err) {\n this.log(`AbortController.abort failed: ${err}`, 'warn');\n }\n\n if (this.renderer) {\n this.renderer.destroy();\n }\n\n if (this.controlBar) {\n this.controlBar.destroy();\n }\n\n if (this.captionManager) {\n this.captionManager.destroy();\n }\n\n if (this.keyboardManager) {\n this.keyboardManager.destroy();\n }\n\n if (this.transcriptManager) {\n this.transcriptManager.destroy();\n }\n\n // Clean up sign-language video and listeners\n this.cleanupSignLanguage();\n\n // Cascade-destroy lazy-loaded managers that own their own listeners.\n if (this.audioDescriptionManager && typeof this.audioDescriptionManager.destroy === 'function') {\n try {\n this.audioDescriptionManager.destroy();\n } catch (err) {\n this.log(`AudioDescriptionManager.destroy failed: ${err}`, 'warn');\n }\n this.audioDescriptionManager = null;\n }\n\n if (this.signLanguageManager && typeof this.signLanguageManager.destroy === 'function') {\n try {\n this.signLanguageManager.destroy();\n } catch (err) {\n this.log(`SignLanguageManager.destroy failed: ${err}`, 'warn');\n }\n this.signLanguageManager = null;\n }\n\n if (this.playlistManager && typeof this.playlistManager.destroy === 'function') {\n try {\n this.playlistManager.destroy();\n } catch (err) {\n this.log(`PlaylistManager.destroy failed: ${err}`, 'warn');\n }\n this.playlistManager = null;\n }\n\n if (this.settingsDialog && typeof this.settingsDialog.destroy === 'function') {\n try {\n this.settingsDialog.destroy();\n } catch (err) {\n this.log(`SettingsDialog.destroy failed: ${err}`, 'warn');\n }\n this.settingsDialog = null;\n }\n\n if (this.keyboardHelp && typeof this.keyboardHelp.destroy === 'function') {\n try {\n this.keyboardHelp.destroy();\n } catch (err) {\n this.log(`KeyboardHelp.destroy failed: ${err}`, 'warn');\n }\n this.keyboardHelp = null;\n }\n\n if (this.mediaSessionManager && typeof this.mediaSessionManager.destroy === 'function') {\n try {\n this.mediaSessionManager.destroy();\n } catch (err) {\n this.log(`MediaSessionManager.destroy failed: ${err}`, 'warn');\n }\n this.mediaSessionManager = null;\n }\n\n // Clean up floating player manager (disconnects IntersectionObserver,\n // returns the container to its original parent if still floating)\n if (this.floatingPlayerManager) {\n try {\n this.floatingPlayerManager.destroy();\n } catch (err) {\n this.log(`FloatingPlayerManager.destroy failed: ${err}`, 'warn');\n }\n this.floatingPlayerManager = null;\n }\n\n // Cleanup play overlay button\n if (this.playButtonOverlay && this.playButtonOverlay.parentNode) {\n this.playButtonOverlay.remove();\n this.playButtonOverlay = null;\n }\n\n // The buffering listener is attached to `this.element` directly and\n // therefore not covered by the lifecycle controller (which only\n // covers window/document/element listeners that opted in via\n // `{ signal }`); it is removed explicitly here.\n if (this._bufferingHideOnMediaPlaying) {\n this.element.removeEventListener('playing', this._bufferingHideOnMediaPlaying);\n this._bufferingHideOnMediaPlaying = null;\n }\n\n if (this.loadingOverlayElement && this.loadingOverlayElement.parentNode) {\n this.loadingOverlayElement.remove();\n this.loadingOverlayElement = null;\n }\n\n // Responsive tracking owns the resize-observer + orientation\n // matchMedia + fullscreenchange listeners. The window/document\n // handlers attached with `{signal}` have already been torn down\n // by `this._lifecycleController.abort()` above; `cleanup()`\n // handles the two exceptions (ResizeObserver + legacy Safari\n // matchMedia listener).\n this.responsiveManager?.cleanup();\n\n // Clean up all managed timeouts\n this.timeouts.forEach((timeoutId: ReturnType<typeof setTimeout>) => clearTimeout(timeoutId));\n this.timeouts.clear();\n\n // Cleanup metadata handling. The manager owns the alert button\n // click listeners; the `cuechange` listener is also tracked on\n // `player.metadataCueChangeHandler` for the TextTrack teardown\n // path below.\n if (this.metadataCueChangeHandler) {\n const textTracks = this.textTracks;\n const metadataTrack = textTracks.find((track: TextTrack) => track.kind === 'metadata');\n if (metadataTrack) {\n metadataTrack.removeEventListener('cuechange', this.metadataCueChangeHandler);\n }\n this.metadataCueChangeHandler = null;\n }\n this.metadataAlertsManager?.cleanup();\n\n // Drop ourselves from the global registry so multi-player pages can\n // detect leaks via Player.instances.length === expected.\n const idx = Player.instances.indexOf(this);\n if (idx >= 0) {\n Player.instances.splice(idx, 1);\n }\n\n // Remove container\n if (this.container && this.container.parentNode) {\n this.container.parentNode.insertBefore(this.element, this.container);\n this.container.parentNode.removeChild(this.container);\n }\n\n this.removeAllListeners();\n }\n\n /**\n * Set up metadata track handling. Delegates to\n * `MetadataAlertsManager` — Player lazily constructs it so pages\n * without metadata tracks pay no cost.\n */\n setupMetadataHandling(): void {\n if (!this.metadataAlertsManager) {\n this.metadataAlertsManager = new MetadataAlertsManager(this);\n }\n this.metadataAlertsManager.setupHandling();\n }\n\n // Thin delegates for the metadata-alert system. Implementations\n // live in `core/MetadataAlertsManager.ts`; Player keeps the names\n // so call sites inside `handleMetadataCue` and external callers\n // (e.g. TranscriptManager integration tests) keep working.\n normalizeMetadataSelector(selector: unknown): string | null {\n return (this.metadataAlertsManager ?? this._ensureMetadataManager()).normalizeSelector(selector);\n }\n\n resolveMetadataConfig(\n map: Record<string, unknown> | null | undefined,\n key: string | null | undefined\n ): MetadataAlertConfig | null {\n return (this.metadataAlertsManager ?? this._ensureMetadataManager()).resolveConfig(map, key);\n }\n\n cacheMetadataAlertContent(element: HTMLElement | null | undefined, config: MetadataAlertConfig = {}): void {\n (this.metadataAlertsManager ?? this._ensureMetadataManager()).cacheContent(element, config);\n }\n\n restoreMetadataAlertContent(element: HTMLElement | null | undefined, config: MetadataAlertConfig = {}): void {\n (this.metadataAlertsManager ?? this._ensureMetadataManager()).restoreContent(element, config);\n }\n\n focusMetadataTarget(target: string | null | undefined, fallbackElement: HTMLElement | null = null): void {\n (this.metadataAlertsManager ?? this._ensureMetadataManager()).focusTarget(target, fallbackElement);\n }\n\n /** Internal helper: lazily creates the manager for external\n * entry points that didn't come via `setupMetadataHandling`. */\n private _ensureMetadataManager(): MetadataAlertsManager {\n if (!this.metadataAlertsManager) {\n this.metadataAlertsManager = new MetadataAlertsManager(this);\n }\n return this.metadataAlertsManager;\n }\n\n handleMetadataAlert(selector: string, options: MetadataAlertOptions = {}): HTMLElement | undefined {\n return (this.metadataAlertsManager ?? this._ensureMetadataManager()).handleAlert(selector, options);\n }\n\n handleMetadataHashtags(hashtags: string[] | null | undefined): void {\n (this.metadataAlertsManager ?? this._ensureMetadataManager()).handleHashtags(hashtags);\n }\n\n handleMetadataCue(cue: VTTCue | TextTrackCue): void {\n (this.metadataAlertsManager ?? this._ensureMetadataManager()).handleCue(cue);\n }\n\n}\n\n", "/**\n * VidPly Playlist Manager\n * Manages playlists for audio and video content\n */\n\nimport { DOMUtils } from '../utils/DOMUtils.js';\nimport { createIconElement } from '../icons/Icons.js';\nimport { i18n } from '../i18n/i18n.js';\nimport { TimeUtils } from '../utils/TimeUtils.js';\nimport { sanitizePosterUrl, toCssBackgroundImage } from '../utils/UrlSafe.js';\nimport { reducedMotionScrollOptions } from '../utils/PerformanceUtils.js';\nimport type { Player } from '../core/Player.js';\n\ntype PlaylistTextTrack = {\n src?: string;\n kind?: string;\n srclang?: string;\n label?: string;\n default?: boolean;\n describedSrc?: string;\n [key: string]: unknown;\n};\n\ntype PlaylistTrack = {\n src?: string;\n type?: string;\n poster?: string;\n tracks?: PlaylistTextTrack[];\n audioDescriptionSrc?: string | null;\n audioDescriptionDuration?: number | string | null;\n signLanguageSrc?: string | null;\n signLanguageSources?: Record<string, string>;\n duration?: number | string | null;\n title?: string;\n artist?: string;\n description?: string;\n [key: string]: unknown;\n};\n\n/**\n * Construction signature for the host Player class. The playlist\n * manager receives this via options so it can recreate players when\n * tracks of incompatible media types (e.g. audio after video) load.\n */\ntype PlayerConstructor = new (\n element: string | HTMLElement,\n options?: Record<string, unknown>\n) => Player;\n\n/**\n * Internal options bag for the manager. Extra keys from the caller\n * (we accept `Record<string, unknown>`) are merged in via the\n * `[key: string]: unknown` index so consumers can pass through\n * additional player options without losing typing on the known ones.\n */\ninterface PlaylistManagerOptions {\n autoAdvance: boolean;\n autoPlayFirst: boolean;\n loop: boolean;\n showPanel: boolean;\n recreatePlayers: boolean;\n hostElement?: HTMLElement | null;\n PlayerClass?: PlayerConstructor | null;\n tracks?: PlaylistTrack[];\n [key: string]: unknown;\n}\n\n// Static counter for unique IDs\nlet playlistInstanceCounter = 0;\n\nexport class PlaylistManager {\n player: Player;\n container: HTMLElement | null;\n currentIndex: number;\n hostElement: HTMLElement | null;\n initialTracks: PlaylistTrack[];\n instanceId: number;\n isChangingTrack: boolean;\n isPanelVisible: boolean;\n navigationFeedback: HTMLElement | null;\n options: PlaylistManagerOptions;\n PlayerClass: PlayerConstructor | null;\n playlistPanel: HTMLElement | null;\n trackArtworkElement: HTMLElement | null;\n trackInfoElement: HTMLElement | null;\n tracks: PlaylistTrack[];\n uniqueId: string;\n\n constructor(player: Player, options: Record<string, unknown> = {}) {\n this.player = player;\n this.tracks = [];\n this.initialTracks = Array.isArray(options.tracks) ? (options.tracks as PlaylistTrack[]) : [];\n this.currentIndex = -1;\n\n // Generate unique instance ID for this playlist\n this.instanceId = ++playlistInstanceCounter;\n this.uniqueId = `vidply-playlist-${this.instanceId}`;\n\n // Options. Spread the caller's bag last so we still support arbitrary\n // pass-through keys, but defaults stay typed.\n this.options = {\n ...options,\n autoAdvance: options.autoAdvance !== false, // Default true\n autoPlayFirst: options.autoPlayFirst !== false, // Default true - auto-play first track on load\n loop: Boolean(options.loop) || false,\n showPanel: options.showPanel !== false, // Default true\n recreatePlayers: Boolean(options.recreatePlayers) || false,\n };\n\n // UI elements\n this.container = null;\n this.playlistPanel = null;\n this.trackInfoElement = null;\n this.trackArtworkElement = null;\n this.navigationFeedback = null; // Live region for keyboard navigation feedback\n this.isPanelVisible = this.options.showPanel !== false;\n\n // Track change guard to prevent cascade of next() calls\n this.isChangingTrack = false;\n\n // Store the host element for player recreation\n this.hostElement = (options.hostElement as HTMLElement | null | undefined) ?? null;\n this.PlayerClass = (options.PlayerClass as PlayerConstructor | null | undefined) ?? null;\n \n // Bind methods once so the same reference is used for both on() and off().\n // Binding inline at registration time (e.g. `.bind(this)` in init()) would\n // create a fresh function each call that off() can never match, leaking the\n // listener after destroy().\n this.handleTrackEnd = this.handleTrackEnd.bind(this);\n this.handleTrackError = this.handleTrackError.bind(this);\n this.handlePlaybackStateChange = this.handlePlaybackStateChange.bind(this);\n this.handleFullscreenChange = this.handleFullscreenChange.bind(this);\n this.handleAudioDescriptionChange = this.handleAudioDescriptionChange.bind(this);\n \n // Register this playlist manager with the player\n this.player.playlistManager = this;\n \n // Initialize\n this.init();\n \n // Update controls to add playlist buttons\n this.updatePlayerControls();\n \n // Load tracks if provided in options (after UI is ready)\n if (this.initialTracks.length > 0) {\n this.loadPlaylist(this.initialTracks);\n }\n }\n \n /**\n * Determine the media type for a track\n * @param {Object} track - Track object\n * @returns {string} - 'audio', 'video', 'youtube', 'vimeo', 'soundcloud', 'hls', 'dash'\n */\n getTrackMediaType(track: PlaylistTrack) {\n const src = track.src || '';\n \n if (src.includes('youtube.com') || src.includes('youtu.be')) {\n return 'youtube';\n }\n if (src.includes('vimeo.com')) {\n return 'vimeo';\n }\n if (src.includes('soundcloud.com') || src.includes('api.soundcloud.com')) {\n return 'soundcloud';\n }\n if (src.includes('.m3u8')) {\n return 'hls';\n }\n if (src.includes('.mpd')) {\n return 'dash';\n }\n if (track.type && track.type.startsWith('audio/')) {\n return 'audio';\n }\n // Default to video for video types or unknown\n return 'video';\n }\n \n /**\n * Recreate the player with the appropriate element type for the track\n * @param {Object} track - Track to load\n * @param {boolean} autoPlay - Whether to auto-play after creation\n */\n async recreatePlayerForTrack(track: PlaylistTrack, autoPlay = false) {\n if (!this.hostElement || !this.PlayerClass) {\n console.warn('VidPly Playlist: Cannot recreate player - missing hostElement or PlayerClass');\n return false;\n }\n \n const mediaType = this.getTrackMediaType(track);\n // SoundCloud uses an iframe widget, so it doesn't need an audio element\n // Only local audio files need an actual <audio> element\n const elementType = (mediaType === 'audio') ? 'audio' : 'video';\n \n // Store playlist panel state\n const wasVisible = this.isPanelVisible;\n const savedTracks = [...this.tracks]; // Keep track data\n const savedIndex = this.currentIndex;\n \n // Detach all playlist UI elements from DOM (keep references)\n // These will be reattached to the new player container\n if (this.trackArtworkElement && this.trackArtworkElement.parentNode) {\n this.trackArtworkElement.parentNode.removeChild(this.trackArtworkElement);\n }\n if (this.trackInfoElement && this.trackInfoElement.parentNode) {\n this.trackInfoElement.parentNode.removeChild(this.trackInfoElement);\n }\n if (this.navigationFeedback && this.navigationFeedback.parentNode) {\n this.navigationFeedback.parentNode.removeChild(this.navigationFeedback);\n }\n if (this.playlistPanel && this.playlistPanel.parentNode) {\n this.playlistPanel.parentNode.removeChild(this.playlistPanel);\n }\n \n // Preserve existing player options so recreated players behave\n // consistently. We use a loose `Record<string, unknown>` because\n // PlayerOptions includes optional fields and an open string-indexed\n // bag of pass-through values that the spread flattens.\n const preservedPlayerOptions: Record<string, unknown> =\n this.player?.options ? { ...this.player.options } : {};\n\n // Remove event listeners before destroying\n if (this.player) {\n this.player.off('ended', this.handleTrackEnd);\n this.player.off('error', this.handleTrackError);\n // Detach the back-reference *before* destroying the player.\n // Player.destroy() cascade-destroys its `playlistManager`, but here\n // we're swapping the player out and the PlaylistManager owns a UI\n // (panel, track info, artwork) that must survive the swap. Without\n // this detach, the cascade would clear() the panel and the playlist\n // visually disappears when the user clicks an audio track in a\n // mixed playlist.\n this.player.playlistManager = null;\n this.player.destroy();\n }\n \n // Clear the host element\n this.hostElement.innerHTML = '';\n \n // Create new media element with appropriate type\n const mediaElement = document.createElement(elementType);\n // Respect configured preload (playlists should behave like single videos even with deferLoad)\n const preloadValue = (preservedPlayerOptions.preload as string | undefined) || 'metadata';\n mediaElement.setAttribute('preload', preloadValue);\n \n // For video elements with local media, set poster\n if (elementType === 'video' && track.poster && \n (mediaType === 'video' || mediaType === 'hls' || mediaType === 'dash')) {\n mediaElement.setAttribute('poster', track.poster);\n }\n \n // For external renderers (YouTube, Vimeo, SoundCloud, HLS, DASH), don't add source\n // The renderer will handle the source directly\n const isExternalRenderer = ['youtube', 'vimeo', 'soundcloud', 'hls', 'dash'].includes(mediaType);\n \n if (!isExternalRenderer) {\n // Add source for HTML5 media\n const source = document.createElement('source');\n source.src = track.src || '';\n if (track.type) {\n source.type = track.type;\n }\n mediaElement.appendChild(source);\n \n // Add tracks (captions, chapters, etc.)\n if (track.tracks && track.tracks.length > 0) {\n track.tracks.forEach((trackConfig: PlaylistTextTrack) => {\n const trackEl = document.createElement('track');\n trackEl.src = trackConfig.src || '';\n trackEl.kind = trackConfig.kind || 'captions';\n trackEl.srclang = trackConfig.srclang || 'en';\n trackEl.label = trackConfig.label || trackConfig.srclang || '';\n if (trackConfig.default) {\n trackEl.default = true;\n }\n mediaElement.appendChild(trackEl);\n });\n }\n }\n \n this.hostElement.appendChild(mediaElement);\n \n // Create new player with the media element\n // Pass the source for external renderers via options\n const playerOptions = {\n mediaType: elementType,\n poster: track.poster,\n audioDescriptionSrc: track.audioDescriptionSrc || null,\n audioDescriptionDuration: track.audioDescriptionDuration || null,\n signLanguageSrc: track.signLanguageSrc || null\n };\n // Merge back preserved options (so deferLoad/preload/etc remain active)\n Object.assign(playerOptions, preservedPlayerOptions);\n \n this.player = new this.PlayerClass(mediaElement, playerOptions);\n \n // Re-register playlist manager\n this.player.playlistManager = this;\n \n // Wait for player to be ready\n await new Promise<void>(resolve => {\n this.player.on('ready', () => resolve());\n });\n \n // Re-attach event listeners\n this.player.on('ended', this.handleTrackEnd);\n this.player.on('error', this.handleTrackError);\n \n // Re-attach all playlist UI elements to the new player's container\n if (this.player.container) {\n // Track artwork goes before video wrapper\n if (this.trackArtworkElement) {\n const videoWrapper = this.player.container.querySelector('.vidply-video-wrapper');\n if (videoWrapper) {\n this.player.container.insertBefore(this.trackArtworkElement, videoWrapper);\n } else {\n this.player.container.appendChild(this.trackArtworkElement);\n }\n }\n // Track info\n if (this.trackInfoElement) {\n this.player.container.appendChild(this.trackInfoElement);\n }\n // Navigation feedback (screen reader only)\n if (this.navigationFeedback) {\n this.player.container.appendChild(this.navigationFeedback);\n }\n // Playlist panel\n if (this.playlistPanel) {\n this.player.container.appendChild(this.playlistPanel);\n }\n }\n \n // Update container reference\n this.container = this.player.container;\n \n // Update controls (adds playlist prev/next buttons)\n this.updatePlayerControls();\n \n // Restore tracks data (we kept it during recreation)\n this.tracks = savedTracks;\n this.currentIndex = savedIndex;\n \n // Update playlist UI to reflect current state\n this.updatePlaylistUI();\n \n // Restore playlist panel visibility\n this.isPanelVisible = wasVisible;\n if (this.playlistPanel) {\n this.playlistPanel.style.display = wasVisible ? '' : 'none';\n }\n \n // For external renderers, load the track via player.load()\n // For HTML5, the source is already set on the element\n if (isExternalRenderer) {\n this.player.load({\n src: track.src ?? '',\n type: track.type,\n poster: track.poster,\n tracks: track.tracks || [],\n audioDescriptionSrc: track.audioDescriptionSrc || null,\n signLanguageSrc: track.signLanguageSrc || null\n });\n } else {\n this.player.load({\n src: track.src ?? '',\n type: track.type,\n poster: track.poster,\n tracks: track.tracks || [],\n audioDescriptionSrc: track.audioDescriptionSrc || null,\n signLanguageSrc: track.signLanguageSrc || null\n });\n }\n \n // Auto-play if requested\n if (autoPlay) {\n setTimeout(() => {\n this.player.play();\n }, 100);\n }\n \n return true;\n }\n \n init() {\n // Listen for track end\n this.player.on('ended', this.handleTrackEnd);\n this.player.on('error', this.handleTrackError);\n \n // Listen for playback state changes to show/hide playlist in fullscreen.\n // Handlers are pre-bound in the constructor so destroy() can detach them.\n this.player.on('play', this.handlePlaybackStateChange);\n this.player.on('pause', this.handlePlaybackStateChange);\n this.player.on('ended', this.handlePlaybackStateChange);\n // Use fullscreenchange event which is what the player actually emits\n this.player.on('fullscreenchange', this.handleFullscreenChange);\n \n // Listen for audio description state changes to update duration displays\n this.player.on('audiodescriptionenabled', this.handleAudioDescriptionChange);\n this.player.on('audiodescriptiondisabled', this.handleAudioDescriptionChange);\n \n // Create UI if needed\n if (this.options.showPanel) {\n this.createUI();\n }\n \n // Check for data-playlist attribute on player container (only if tracks weren't provided in options)\n if (this.tracks.length === 0 && this.initialTracks.length === 0) {\n this.loadPlaylistFromAttribute();\n }\n }\n \n /**\n * Load playlist from data-playlist attribute if present\n */\n loadPlaylistFromAttribute() {\n // Check the original wrapper element for data-playlist\n // Structure: #audio-player -> .vidply-player -> .vidply-video-wrapper -> <audio>\n // So we need to go up 3 levels\n if (!this.player.element || !this.player.element.parentElement) {\n return;\n }\n \n const videoWrapper = this.player.element.parentElement; // .vidply-video-wrapper\n const playerContainer = videoWrapper.parentElement; // .vidply-player\n const originalElement = playerContainer ? playerContainer.parentElement : null; // #audio-player (original div)\n \n if (!originalElement) {\n return;\n }\n \n // Load playlist options from data attributes\n this.loadOptionsFromAttributes(originalElement);\n \n const playlistData = originalElement.getAttribute('data-playlist');\n if (!playlistData) {\n return;\n }\n \n try {\n const tracks = JSON.parse(playlistData);\n if (Array.isArray(tracks) && tracks.length > 0) {\n this.loadPlaylist(tracks);\n } else {\n console.warn('VidPly Playlist: data-playlist is not a valid array or is empty');\n }\n } catch (error) {\n console.error('VidPly Playlist: Failed to parse data-playlist attribute', error);\n }\n }\n \n /**\n * Load playlist options from data attributes\n * @param {HTMLElement} element - Element to read attributes from\n */\n loadOptionsFromAttributes(element: HTMLElement) {\n // data-playlist-auto-advance\n const autoAdvance = element.getAttribute('data-playlist-auto-advance');\n if (autoAdvance !== null) {\n this.options.autoAdvance = autoAdvance === 'true';\n }\n \n // data-playlist-auto-play-first\n const autoPlayFirst = element.getAttribute('data-playlist-auto-play-first');\n if (autoPlayFirst !== null) {\n this.options.autoPlayFirst = autoPlayFirst === 'true';\n }\n \n // data-playlist-loop\n const loop = element.getAttribute('data-playlist-loop');\n if (loop !== null) {\n this.options.loop = loop === 'true';\n }\n \n // data-playlist-show-panel\n const showPanel = element.getAttribute('data-playlist-show-panel');\n if (showPanel !== null) {\n this.options.showPanel = showPanel === 'true';\n }\n }\n \n /**\n * Update player controls to add playlist navigation buttons\n */\n updatePlayerControls() {\n if (!this.player.controlBar) return;\n \n const controlBar = this.player.controlBar;\n \n // Clear existing controls content (except the element itself)\n controlBar.element.innerHTML = '';\n \n // Recreate controls with playlist buttons now available\n controlBar.createControls();\n \n // Reattach events for the new controls\n controlBar.attachEvents();\n controlBar.setupAutoHide();\n }\n \n /**\n * Load a playlist\n * @param {Array} tracks - Array of track objects\n */\n loadPlaylist(tracks: PlaylistTrack[]) {\n this.tracks = tracks;\n this.currentIndex = -1;\n \n // Add playlist class to container\n if (this.container) {\n this.container.classList.add('vidply-has-playlist');\n }\n \n // Update UI\n if (this.playlistPanel) {\n this.renderPlaylist();\n }\n \n // Auto-play first track (if enabled)\n if (tracks.length > 0) {\n if (this.options.autoPlayFirst) {\n this.play(0);\n } else {\n // Behave like a single video: load the first track (metadata/manifest)\n // but do not start playback.\n void this.loadTrack(0).catch(() => {\n // ignore\n });\n }\n }\n \n // Update visibility based on current state\n this.updatePlaylistVisibilityInFullscreen();\n }\n \n /**\n * Load a track without playing\n * This is the playlist equivalent of a \"single video initialized but not started yet\":\n * it updates UI selection and loads the media into the player so metadata/manifests\n * and feature managers can be ready, but it does not start playback.\n * @param {number} index - Track index\n */\n async loadTrack(index: number) {\n if (index < 0 || index >= this.tracks.length) {\n console.warn('VidPly Playlist: Invalid track index', index);\n return;\n }\n \n const track = this.tracks[index];\n if (!track) return;\n \n // Always update UI immediately (poster, buttons, duration, etc.).\n // Note: this is UI-only; actual media loading is performed by player.load() below.\n this.selectTrack(index);\n \n // Set guard flag to prevent cascade of next() calls during track change\n this.isChangingTrack = true;\n \n // Check if we should recreate the player for this track type\n if (this.options.recreatePlayers && this.hostElement && this.PlayerClass) {\n const currentMediaType = this.player ? \n (this.player.element.tagName === 'AUDIO' ? 'audio' : 'video') : null;\n const newMediaType = this.getTrackMediaType(track);\n const newElementType = (newMediaType === 'audio' || newMediaType === 'soundcloud') ? 'audio' : 'video';\n \n // Recreate if element type is different\n if (currentMediaType !== newElementType) {\n await this.recreatePlayerForTrack(track, false);\n // Re-apply selection to the newly created player (poster/tracks/buttons)\n this.selectTrack(index);\n \n // Emit event\n this.player.emit('playlisttrackchange', {\n index: index,\n item: track,\n total: this.tracks.length\n });\n \n // Clear guard flag\n setTimeout(() => {\n this.isChangingTrack = false;\n }, 150);\n return;\n }\n }\n \n const loadPromise = this.player.load({\n src: track.src ?? '',\n type: track.type,\n poster: track.poster,\n tracks: track.tracks || [],\n audioDescriptionSrc: track.audioDescriptionSrc || null,\n signLanguageSrc: track.signLanguageSrc || null,\n signLanguageSources: track.signLanguageSources || {}\n });\n\n // For playlist UX parity with single videos: fetch metadata/manifest now,\n // but do not start playback.\n if (this.player?.options?.deferLoad && typeof this.player.ensureLoaded === 'function') {\n Promise.resolve(loadPromise)\n .then(() => this.player?.ensureLoaded?.())\n .catch(() => {\n // ignore\n });\n }\n \n // Emit event\n this.player.emit('playlisttrackchange', {\n index: index,\n item: track,\n total: this.tracks.length\n });\n \n // Clear guard flag after a short delay to ensure track is loaded\n setTimeout(() => {\n this.isChangingTrack = false;\n }, 150);\n }\n\n /**\n * Select a track (UI/selection only; does NOT set the media src / does NOT initialize renderer)\n *\n * In \"B always\" playlist mode, you typically want `loadTrack()` on selection so the\n * selected item behaves like a single video (metadata/manifest loaded, features ready)\n * without auto-playing.\n * @param {number} index - Track index\n */\n selectTrack(index: number) {\n if (index < 0 || index >= this.tracks.length) {\n console.warn('VidPly Playlist: Invalid track index', index);\n return;\n }\n\n const track = this.tracks[index];\n if (!track) return;\n this.currentIndex = index;\n\n // Apply per-track metadata without touching the media source.\n // This ensures poster + feature buttons (chapters/captions/transcript/sign-language)\n // can be updated instantly even before any media network activity happens.\n try {\n // Poster for video element — validate the URL from the manifest\n // before we hand it to `<video>.poster`. Without the allow-list an\n // attacker-controlled `javascript:` or `data:text/html` value could\n // reach the attribute.\n if (this.player?.element?.tagName === 'VIDEO') {\n if (track.poster) {\n const resolved: string = typeof this.player.resolvePosterPath === 'function'\n ? this.player.resolvePosterPath(track.poster)\n : track.poster;\n const posterUrl = sanitizePosterUrl(resolved);\n if (posterUrl) {\n (this.player.element as HTMLVideoElement).poster = posterUrl;\n (this.player as Player & { applyPosterAspectRatio?: (url: string) => void }).applyPosterAspectRatio?.(posterUrl);\n } else {\n this.player.element.removeAttribute('poster');\n }\n } else {\n this.player.element.removeAttribute('poster');\n }\n }\n\n // Update sign language / audio description sources (used for button visibility)\n this.player.audioDescriptionSrc = track.audioDescriptionSrc || null;\n this.player.signLanguageSrc = track.signLanguageSrc || null;\n this.player.signLanguageSources = track.signLanguageSources || {};\n\n // Fill duration early for UI (progress/time display) without loading media\n if (track.duration && Number(track.duration) > 0) {\n this.player.state.duration = Number(track.duration);\n }\n // Also sync feature managers (they keep their own copy of sources)\n if (this.player.audioDescriptionManager) {\n this.player.audioDescriptionManager.src = track.audioDescriptionSrc || null;\n // Remember original (non-described) source for switching back later\n this.player.audioDescriptionManager.originalSource = track.src || this.player.originalSrc || null;\n }\n if (this.player.signLanguageManager) {\n this.player.signLanguageManager.src = track.signLanguageSrc || null;\n this.player.signLanguageManager.sources = track.signLanguageSources || {};\n this.player.signLanguageManager.currentLanguage = null;\n }\n\n // For audio description switching, remember original source even before first play\n if (track.src && !this.player.originalSrc) {\n this.player.originalSrc = track.src;\n }\n\n // Replace <track> elements so captions/chapters/transcript can be detected/loaded\n const existing: HTMLElement[] = Array.from(this.player.element.querySelectorAll('track'));\n existing.forEach(t => t.remove());\n\n if (Array.isArray(track.tracks)) {\n track.tracks.forEach((tc: PlaylistTextTrack) => {\n if (!tc?.src) return;\n const el = document.createElement('track');\n el.src = tc.src;\n el.kind = tc.kind || 'captions';\n el.srclang = tc.srclang || 'en';\n el.label = tc.label || tc.srclang || 'Track';\n if (tc.default) el.default = true;\n if (tc.describedSrc) {\n el.setAttribute('data-desc-src', tc.describedSrc);\n }\n this.player.element.appendChild(el);\n });\n }\n\n if (typeof this.player.invalidateTrackCache === 'function') {\n this.player.invalidateTrackCache();\n }\n\n // Re-scan described-track metadata for the AudioDescriptionManager. The\n // manager is no longer eagerly loaded on content-less players, so if this\n // track is the first to introduce described content, pull the chunk in\n // lazily before re-scanning.\n const reinitAudioDescription = (adm: Player['audioDescriptionManager']) => {\n if (!adm || typeof adm.initFromSourceElements !== 'function') return;\n try {\n adm.captionTracks = [];\n adm.initFromSourceElements(this.player.sourceElements, this.player.trackElements);\n } catch {\n // ignore\n }\n };\n if (this.player.audioDescriptionManager) {\n reinitAudioDescription(this.player.audioDescriptionManager);\n } else if (this.player.hasAudioDescriptionContent?.()) {\n void this.player.ensureAudioDescriptionManager()\n .then(reinitAudioDescription)\n .catch(() => { /* ignore */ });\n }\n\n // Refresh caption/transcript managers so menus reflect newly injected <track> elements\n // (important when we defer MP4/MP3 loading but still want VTT-based UI to work).\n if (this.player.captionManager && typeof this.player.captionManager.loadTracks === 'function') {\n try {\n this.player.captionManager.tracks = [];\n this.player.captionManager.currentTrack = null;\n this.player.captionManager.loadTracks();\n } catch {\n // ignore\n }\n }\n\n // TranscriptManager reads from TextTracks too; it will be correct after media starts.\n // For now, we just ensure control bar is rebuilt so the button is present.\n\n // Rebuild controls so feature buttons appear immediately\n if (typeof this.player.updateControlBar === 'function') {\n this.player.updateControlBar();\n }\n } catch {\n // ignore preview errors; selection should still work\n }\n\n this.updateTrackInfo(track);\n this.updatePlaylistUI();\n\n this.player.emit('playlisttrackselect', {\n index,\n item: track,\n total: this.tracks.length\n });\n }\n \n /**\n * Play a specific track\n * @param {number} index - Track index\n * @param {boolean} userInitiated - Whether this was triggered by user action (default: false)\n */\n async play(index: number, _userInitiated = false) {\n if (index < 0 || index >= this.tracks.length) {\n console.warn('VidPly Playlist: Invalid track index', index);\n return;\n }\n \n const track = this.tracks[index];\n if (!track) return;\n \n // Set guard flag to prevent cascade of next() calls during track change\n this.isChangingTrack = true;\n \n // Update current index\n this.currentIndex = index;\n \n // Check if we should recreate the player for this track type\n if (this.options.recreatePlayers && this.hostElement && this.PlayerClass) {\n const currentMediaType = this.player ? \n (this.player.element.tagName === 'AUDIO' ? 'audio' : 'video') : null;\n const newMediaType = this.getTrackMediaType(track);\n const newElementType = (newMediaType === 'audio' || newMediaType === 'soundcloud') ? 'audio' : 'video';\n \n // Recreate if element type is different\n if (currentMediaType !== newElementType) {\n await this.recreatePlayerForTrack(track, true); // true = autoPlay\n // Update UI after recreation\n this.updateTrackInfo(track);\n this.updatePlaylistUI();\n \n // Emit event\n this.player.emit('playlisttrackchange', {\n index: index,\n item: track,\n total: this.tracks.length\n });\n \n // Clear guard flag\n setTimeout(() => {\n this.isChangingTrack = false;\n }, 150);\n return;\n }\n }\n \n // Load track into player (normal path)\n // If audio description was toggled before the first play, load the described source directly.\n let srcToLoad = track.src;\n if (this.player?.audioDescriptionManager?.desiredState && track.audioDescriptionSrc) {\n this.player.originalSrc = track.src ?? null;\n this.player.audioDescriptionManager.originalSource = track.src ?? null;\n this.player.audioDescriptionManager.src = track.audioDescriptionSrc;\n srcToLoad = track.audioDescriptionSrc;\n }\n\n this.player.load({\n src: srcToLoad ?? '',\n type: track.type,\n poster: track.poster,\n tracks: track.tracks || [],\n audioDescriptionSrc: track.audioDescriptionSrc || null,\n signLanguageSrc: track.signLanguageSrc || null,\n signLanguageSources: track.signLanguageSources || {}\n });\n \n // Update UI\n this.updateTrackInfo(track);\n this.updatePlaylistUI();\n \n // Emit event\n this.player.emit('playlisttrackchange', {\n index: index,\n item: track,\n total: this.tracks.length\n });\n \n // Auto-play and clear guard flag after playback starts\n setTimeout(() => {\n this.player.play();\n // Clear guard flag after a short delay to ensure track has started\n setTimeout(() => {\n this.isChangingTrack = false;\n }, 50);\n }, 100);\n }\n \n /**\n * Play next track\n */\n next() {\n let nextIndex = this.currentIndex + 1;\n \n if (nextIndex >= this.tracks.length) {\n if (this.options.loop) {\n nextIndex = 0;\n } else {\n return;\n }\n }\n \n this.play(nextIndex);\n }\n \n /**\n * Play previous track\n */\n previous() {\n let prevIndex = this.currentIndex - 1;\n \n if (prevIndex < 0) {\n if (this.options.loop) {\n prevIndex = this.tracks.length - 1;\n } else {\n return;\n }\n }\n \n this.play(prevIndex);\n }\n \n /**\n * Handle track end\n */\n handleTrackEnd() {\n // Don't auto-advance if we're already in the process of changing tracks\n // This prevents a cascade of next() calls when loading a new track triggers an 'ended' event\n if (this.isChangingTrack) {\n return;\n }\n \n if (this.options.autoAdvance) {\n this.next();\n }\n }\n \n /**\n * Check if a source URL requires an external renderer\n * @param {string} src - Source URL\n * @returns {boolean}\n */\n isExternalRendererUrl(src: string | null | undefined) {\n if (!src) return false;\n return src.includes('youtube.com') || \n src.includes('youtu.be') || \n src.includes('vimeo.com') || \n src.includes('soundcloud.com') || \n src.includes('api.soundcloud.com') ||\n src.includes('.m3u8') ||\n src.includes('.mpd');\n }\n\n /**\n * Handle track error\n */\n handleTrackError(e: unknown) {\n // Don't auto-advance for external renderer tracks\n // External renderers (YouTube, Vimeo, SoundCloud, HLS, DASH) may trigger HTML5 errors\n // that should be ignored since the external renderer handles playback\n const currentTrack = this.getCurrentTrack();\n if (currentTrack && currentTrack.src && this.isExternalRendererUrl(currentTrack.src)) {\n // Silently ignore errors for external renderer tracks\n return;\n }\n \n // Don't auto-advance if we're in the process of changing tracks\n // This prevents a cascade of next() calls when switching between renderer types\n if (this.isChangingTrack) {\n return;\n }\n \n console.error('VidPly Playlist: Track error', e);\n \n // Try next track\n if (this.options.autoAdvance) {\n setTimeout(() => {\n this.next();\n }, 1000);\n }\n }\n \n /**\n * Handle playback state changes (for fullscreen playlist visibility)\n */\n handlePlaybackStateChange() {\n this.updatePlaylistVisibilityInFullscreen();\n }\n \n /**\n * Handle fullscreen state changes\n */\n handleFullscreenChange() {\n // Use a small delay to ensure fullscreen state is fully applied\n setTimeout(() => {\n this.updatePlaylistVisibilityInFullscreen();\n }, 50);\n }\n \n /**\n * Handle audio description state changes\n * Updates duration displays to show audio-described version duration when AD is enabled\n */\n handleAudioDescriptionChange() {\n const currentTrack = this.getCurrentTrack();\n if (!currentTrack) return;\n \n // Update the track info display with the appropriate duration\n this.updateTrackInfo(currentTrack);\n \n // Update the playlist UI to reflect duration changes (aria-labels)\n this.updatePlaylistUI();\n \n // Update visual duration elements in playlist panel\n this.updatePlaylistDurations();\n }\n \n /**\n * Update the visual duration displays in the playlist panel\n * Called when audio description state changes\n */\n updatePlaylistDurations() {\n if (!this.playlistPanel) return;\n \n const items = this.playlistPanel.querySelectorAll('.vidply-playlist-item');\n \n items.forEach((item: Element, index: number) => {\n const track = this.tracks[index];\n if (!track) return;\n \n const effectiveDuration = this.getEffectiveDuration(track);\n const trackDuration = effectiveDuration ? TimeUtils.formatTime(effectiveDuration as number) : '';\n \n // Update duration badge on thumbnail (if exists)\n const durationBadge = item.querySelector('.vidply-playlist-duration-badge');\n if (durationBadge) {\n durationBadge.textContent = trackDuration;\n }\n \n // Update inline duration (if exists)\n const inlineDuration = item.querySelector('.vidply-playlist-item-duration');\n if (inlineDuration) {\n inlineDuration.textContent = trackDuration;\n }\n });\n }\n \n /**\n * Get the effective duration for a track based on audio description state\n * @param {Object} track - Track object\n * @returns {number|null} - Duration in seconds or null if not available\n */\n getEffectiveDuration(track: PlaylistTrack) {\n if (!track) return null;\n \n const isAudioDescriptionEnabled = this.player.state.audioDescriptionEnabled;\n \n // If audio description is enabled and track has audioDescriptionDuration, use it\n if (isAudioDescriptionEnabled && track.audioDescriptionDuration) {\n return track.audioDescriptionDuration;\n }\n \n // Otherwise use regular duration\n return track.duration || null;\n }\n \n /**\n * Update playlist visibility based on fullscreen and playback state\n * In fullscreen: show when paused/not started, hide when playing\n * Outside fullscreen: respect original panel visibility setting\n */\n updatePlaylistVisibilityInFullscreen() {\n const playlistPanel = this.playlistPanel;\n if (!playlistPanel || !this.tracks.length) return;\n\n const isFullscreen = this.player.state.fullscreen;\n const isPlaying = this.player.state.playing;\n\n if (isFullscreen) {\n // In fullscreen: show only when not playing (paused or not started)\n // Check playing state explicitly since paused might not be set initially\n if (!isPlaying) {\n playlistPanel.classList.add('vidply-playlist-fullscreen-visible');\n playlistPanel.style.display = 'block';\n } else {\n playlistPanel.classList.remove('vidply-playlist-fullscreen-visible');\n // Add a smooth fade out with delay to match CSS transition\n setTimeout(() => {\n // Double-check state hasn't changed before hiding\n if (this.player.state.playing && this.player.state.fullscreen) {\n playlistPanel.style.display = 'none';\n }\n }, 300); // Match CSS transition duration\n }\n } else {\n // Outside fullscreen: restore original behavior\n playlistPanel.classList.remove('vidply-playlist-fullscreen-visible');\n if (this.isPanelVisible && this.tracks.length > 0) {\n playlistPanel.style.display = 'block';\n } else {\n playlistPanel.style.display = 'none';\n }\n }\n }\n \n /**\n * Create playlist UI\n */\n createUI() {\n // Find player container\n this.container = this.player.container;\n \n if (!this.container) {\n console.warn('VidPly Playlist: No container found');\n return;\n }\n \n // Track artwork element (shows album art/poster for audio tracks).\n // Important: in mixed playlists the player may start as <video> and later recreate to <audio>,\n // so we create this lazily in `updateTrackArtwork()` when we actually have an audio element.\n \n // Create track info element (shows current track)\n this.trackInfoElement = DOMUtils.createElement('div', {\n className: 'vidply-track-info',\n attributes: {\n role: 'status'\n }\n });\n this.trackInfoElement.style.display = 'none';\n \n this.container.appendChild(this.trackInfoElement);\n \n // Create navigation feedback live region\n this.navigationFeedback = DOMUtils.createElement('div', {\n className: 'vidply-sr-only',\n attributes: {\n role: 'status',\n 'aria-live': 'polite',\n 'aria-atomic': 'true'\n }\n });\n this.container.appendChild(this.navigationFeedback);\n \n // Create playlist panel with proper landmark\n this.playlistPanel = DOMUtils.createElement('div', {\n className: 'vidply-playlist-panel',\n attributes: {\n id: `${this.uniqueId}-panel`,\n role: 'region',\n 'aria-label': i18n.t('playlist.title'),\n 'aria-labelledby': `${this.uniqueId}-heading`\n }\n });\n this.playlistPanel.style.display = this.isPanelVisible ? 'none' : 'none'; // Will be shown when playlist is loaded\n \n this.container.appendChild(this.playlistPanel);\n }\n \n /**\n * Update track info display\n */\n updateTrackInfo(track: PlaylistTrack) {\n if (!this.trackInfoElement) return;\n \n const trackNumber = this.currentIndex + 1;\n const totalTracks = this.tracks.length;\n const trackTitle = track.title || i18n.t('playlist.untitled');\n const trackArtist = track.artist || '';\n \n // Use effective duration (audio description duration when AD is enabled)\n const effectiveDuration = this.getEffectiveDuration(track);\n const trackDuration = effectiveDuration ? TimeUtils.formatTime(effectiveDuration as number) : '';\n const trackDurationReadable = effectiveDuration ? TimeUtils.formatDuration(effectiveDuration as number) : '';\n \n // Screen reader announcement - include duration if available\n const artistPart = trackArtist ? i18n.t('playlist.by') + trackArtist : '';\n const durationPart = trackDurationReadable ? `. ${trackDurationReadable}` : '';\n const announcement = i18n.t('playlist.nowPlaying', {\n current: trackNumber,\n total: totalTracks,\n title: trackTitle,\n artist: artistPart\n }) + durationPart;\n \n const trackOfText = i18n.t('playlist.trackOf', {\n current: trackNumber,\n total: totalTracks\n });\n \n // Build duration HTML if available\n const durationHtml = trackDuration \n ? `<span class=\"vidply-track-duration\" aria-hidden=\"true\">${DOMUtils.escapeHTML(trackDuration)}</span>` \n : '';\n \n // Get description if available\n const trackDescription = track.description || '';\n \n this.trackInfoElement.innerHTML = `\n <span class=\"vidply-sr-only\">${DOMUtils.escapeHTML(announcement)}</span>\n <div class=\"vidply-track-header\" aria-hidden=\"true\">\n <span class=\"vidply-track-number\">${DOMUtils.escapeHTML(trackOfText)}</span>\n ${durationHtml}\n </div>\n <div class=\"vidply-track-title\" aria-hidden=\"true\">${DOMUtils.escapeHTML(trackTitle)}</div>\n ${trackArtist ? `<div class=\"vidply-track-artist\" aria-hidden=\"true\">${DOMUtils.escapeHTML(trackArtist)}</div>` : ''}\n ${trackDescription ? `<div class=\"vidply-track-description\" aria-hidden=\"true\">${DOMUtils.escapeHTML(trackDescription)}</div>` : ''}\n `;\n \n this.trackInfoElement.style.display = 'block';\n \n // Update track artwork if available (for audio playlists)\n this.updateTrackArtwork(track);\n }\n \n /**\n * Update track artwork display (for audio playlists)\n */\n updateTrackArtwork(track: PlaylistTrack) {\n // Only show artwork for audio players.\n // In mixed playlists we may recreate from <video> -> <audio> later, so ensure the element exists lazily.\n if (this.player?.element?.tagName !== 'AUDIO') {\n if (this.trackArtworkElement) {\n this.trackArtworkElement.style.display = 'none';\n }\n return;\n }\n\n // Lazily create artwork element once we have an audio element/container.\n if (!this.trackArtworkElement && this.container) {\n this.trackArtworkElement = DOMUtils.createElement('div', {\n className: 'vidply-track-artwork',\n attributes: {\n 'aria-hidden': 'true'\n }\n });\n this.trackArtworkElement.style.display = 'none';\n\n // Insert before video wrapper (if present) for consistent layout.\n const videoWrapper = this.container.querySelector('.vidply-video-wrapper');\n if (videoWrapper) {\n this.container.insertBefore(this.trackArtworkElement, videoWrapper);\n } else {\n this.container.appendChild(this.trackArtworkElement);\n }\n }\n\n if (!this.trackArtworkElement) return;\n \n // A track manifest is attacker-influenced data — a value like\n // `x); background: url(evil.svg` must not be allowed to break out\n // of the declaration. `toCssBackgroundImage` returns null when the\n // URL fails validation, so we leave the element hidden instead of\n // assigning a dangerous value.\n const safeBackground = track.poster ? toCssBackgroundImage(track.poster) : null;\n if (safeBackground) {\n this.trackArtworkElement.style.backgroundImage = safeBackground;\n this.trackArtworkElement.style.display = 'block';\n } else {\n this.trackArtworkElement.style.backgroundImage = '';\n this.trackArtworkElement.style.display = 'none';\n }\n }\n \n /**\n * Render playlist\n */\n renderPlaylist() {\n if (!this.playlistPanel) return;\n \n // Clear existing\n this.playlistPanel.innerHTML = '';\n \n // Create header\n const header = DOMUtils.createElement('h2', {\n className: 'vidply-playlist-header',\n attributes: {\n id: `${this.uniqueId}-heading`\n }\n });\n header.textContent = `${i18n.t('playlist.title')} (${this.tracks.length})`;\n this.playlistPanel.appendChild(header);\n \n // Add keyboard instructions (visually hidden)\n const instructions = DOMUtils.createElement('div', {\n className: 'vidply-sr-only',\n attributes: {\n id: `${this.uniqueId}-keyboard-instructions`\n }\n });\n instructions.textContent = i18n.t('playlist.keyboardInstructions');\n this.playlistPanel.appendChild(instructions);\n \n // Create list (proper ul element)\n // Note: role=\"listbox\" requires option children. We use role=\"none\" on <li> elements\n // so the buttons with role=\"option\" become the semantic children of the listbox.\n const list = DOMUtils.createElement('ul', {\n className: 'vidply-playlist-list',\n attributes: {\n role: 'listbox',\n 'aria-labelledby': `${this.uniqueId}-heading`,\n 'aria-describedby': `${this.uniqueId}-keyboard-instructions`\n }\n });\n \n this.tracks.forEach((track, index) => {\n const item = this.createPlaylistItem(track, index);\n list.appendChild(item);\n });\n \n this.playlistPanel.appendChild(list);\n \n // Show panel if it should be visible\n if (this.isPanelVisible) {\n this.playlistPanel.style.display = 'block';\n }\n }\n \n /**\n * Create playlist item element\n */\n createPlaylistItem(track: PlaylistTrack, index: number) {\n const trackTitle = track.title || i18n.t('playlist.trackUntitled', { number: index + 1 });\n const trackArtist = track.artist ? i18n.t('playlist.by') + track.artist : '';\n \n // Use effective duration (audio description duration when AD is enabled)\n const effectiveDuration = this.getEffectiveDuration(track);\n const trackDuration = effectiveDuration ? TimeUtils.formatTime(effectiveDuration as number) : '';\n const trackDurationReadable = effectiveDuration ? TimeUtils.formatDuration(effectiveDuration as number) : '';\n const isActive = index === this.currentIndex;\n \n // Build accessible label for screen readers\n // With role=\"option\" and aria-checked, screen reader will announce selection state\n // Position is already announced via aria-posinset/aria-setsize\n // Format: \"Title by Artist. 3 minutes, 45 seconds.\"\n let ariaLabel = `${trackTitle}${trackArtist}`;\n if (trackDurationReadable) {\n ariaLabel += `. ${trackDurationReadable}`;\n }\n \n // Create list item container\n // role=\"none\" removes the <li> from the accessibility tree so the button with\n // role=\"option\" becomes the direct semantic child of the listbox (required by ARIA).\n const item = DOMUtils.createElement('li', {\n className: isActive ? 'vidply-playlist-item vidply-playlist-item-active' : 'vidply-playlist-item',\n attributes: {\n 'data-playlist-index': String(index),\n role: 'none'\n }\n });\n \n // Create button wrapper for interactive content\n const button = DOMUtils.createElement('button', {\n className: 'vidply-playlist-item-button',\n attributes: {\n type: 'button',\n role: 'option',\n tabIndex: index === 0 ? '0' : '-1',\n 'aria-label': ariaLabel,\n 'aria-posinset': String(index + 1),\n 'aria-setsize': String(this.tracks.length),\n 'aria-checked': isActive ? 'true' : 'false'\n }\n });\n \n // Add aria-current if active\n if (isActive) {\n button.setAttribute('aria-current', 'true');\n button.setAttribute('tabIndex', '0'); // Active item should always be tabbable\n }\n \n // Thumbnail container with optional duration badge\n const thumbnailContainer = DOMUtils.createElement('span', {\n className: 'vidply-playlist-thumbnail-container',\n attributes: {\n 'aria-hidden': 'true'\n }\n });\n \n // Thumbnail or icon\n const thumbnail = DOMUtils.createElement('span', {\n className: 'vidply-playlist-thumbnail'\n });\n \n // Same rule as trackArtworkElement below: only set backgroundImage\n // when the manifest URL passes the allow-list so a hostile feed\n // cannot break out of the CSS `url(...)` declaration.\n const safeThumbnail = track.poster ? toCssBackgroundImage(track.poster) : null;\n if (safeThumbnail) {\n thumbnail.style.backgroundImage = safeThumbnail;\n } else {\n const icon = createIconElement('music');\n icon.classList.add('vidply-playlist-thumbnail-icon');\n thumbnail.appendChild(icon);\n }\n \n thumbnailContainer.appendChild(thumbnail);\n \n // Duration badge on thumbnail (like YouTube) - only show if there's a poster\n if (trackDuration && track.poster) {\n const durationBadge = DOMUtils.createElement('span', {\n className: 'vidply-playlist-duration-badge'\n });\n durationBadge.textContent = trackDuration;\n thumbnailContainer.appendChild(durationBadge);\n }\n \n button.appendChild(thumbnailContainer);\n \n // Info section (title, artist, description)\n const info = DOMUtils.createElement('span', {\n className: 'vidply-playlist-item-info',\n attributes: {\n 'aria-hidden': 'true'\n }\n });\n \n // Title row with optional inline duration (for when no thumbnail)\n const titleRow = DOMUtils.createElement('span', {\n className: 'vidply-playlist-item-title-row'\n });\n \n const title = DOMUtils.createElement('span', {\n className: 'vidply-playlist-item-title'\n });\n title.textContent = trackTitle;\n titleRow.appendChild(title);\n \n // Inline duration (shown when no poster/thumbnail)\n if (trackDuration && !track.poster) {\n const inlineDuration = DOMUtils.createElement('span', {\n className: 'vidply-playlist-item-duration'\n });\n inlineDuration.textContent = trackDuration;\n titleRow.appendChild(inlineDuration);\n }\n \n info.appendChild(titleRow);\n \n // Artist\n if (track.artist) {\n const artist = DOMUtils.createElement('span', {\n className: 'vidply-playlist-item-artist'\n });\n artist.textContent = track.artist;\n info.appendChild(artist);\n }\n \n // Description (truncated)\n if (track.description) {\n const description = DOMUtils.createElement('span', {\n className: 'vidply-playlist-item-description'\n });\n description.textContent = track.description;\n info.appendChild(description);\n }\n \n button.appendChild(info);\n \n // Play icon\n const playIcon = createIconElement('play');\n playIcon.classList.add('vidply-playlist-item-icon');\n playIcon.setAttribute('aria-hidden', 'true');\n button.appendChild(playIcon);\n \n // Click handler\n button.addEventListener('click', () => {\n const track = this.tracks[index];\n const isExternalRenderer = this.isExternalRendererUrl(track?.src);\n \n // Exit fullscreen for external renderer tracks (YouTube, Vimeo, SoundCloud)\n // These have their own native controls that work better outside vidply's fullscreen\n if (isExternalRenderer && this.player.state.fullscreen) {\n this.player.exitFullscreen();\n // Small delay to let fullscreen exit before loading\n setTimeout(() => {\n this.play(index, true);\n }, 100);\n } else {\n this.play(index, true); // User-initiated\n }\n });\n \n // Keyboard handler\n button.addEventListener('keydown', (e) => {\n this.handlePlaylistItemKeydown(e, index);\n });\n \n // Append button to list item\n item.appendChild(button);\n \n return item;\n }\n \n /**\n * Handle keyboard navigation in playlist items\n */\n handlePlaylistItemKeydown(e: KeyboardEvent, index: number) {\n if (!this.playlistPanel) return;\n const buttons: HTMLElement[] = Array.from(this.playlistPanel.querySelectorAll('.vidply-playlist-item-button'));\n let newIndex = -1;\n let announcement = '';\n \n switch(e.key) {\n case 'Enter':\n case ' ':\n e.preventDefault();\n e.stopPropagation();\n {\n const track = this.tracks[index];\n const isExternalRenderer = this.isExternalRendererUrl(track?.src);\n \n // Exit fullscreen for external renderer tracks (YouTube, Vimeo, SoundCloud)\n if (isExternalRenderer && this.player.state.fullscreen) {\n this.player.exitFullscreen();\n setTimeout(() => {\n this.play(index, true);\n }, 100);\n } else {\n this.play(index, true); // User-initiated\n }\n }\n return; // No need to move focus\n \n case 'ArrowDown':\n e.preventDefault();\n e.stopPropagation();\n // Move to next item\n if (index < buttons.length - 1) {\n newIndex = index + 1;\n } else {\n // At the end, announce boundary\n announcement = i18n.t('playlist.endOfPlaylist', { current: buttons.length, total: buttons.length });\n }\n break;\n\n case 'ArrowUp':\n e.preventDefault();\n e.stopPropagation();\n // Move to previous item\n if (index > 0) {\n newIndex = index - 1;\n } else {\n // At the beginning, announce boundary\n announcement = i18n.t('playlist.beginningOfPlaylist', { total: buttons.length });\n }\n break;\n\n case 'PageDown':\n e.preventDefault();\n e.stopPropagation();\n // Move 5 items down (or to end)\n newIndex = Math.min(index + 5, buttons.length - 1);\n if (newIndex === buttons.length - 1 && index !== newIndex) {\n announcement = i18n.t('playlist.jumpedToLastTrack', { current: newIndex + 1, total: buttons.length });\n }\n break;\n\n case 'PageUp':\n e.preventDefault();\n e.stopPropagation();\n // Move 5 items up (or to beginning)\n newIndex = Math.max(index - 5, 0);\n if (newIndex === 0 && index !== newIndex) {\n announcement = i18n.t('playlist.jumpedToFirstTrack', { total: buttons.length });\n }\n break;\n\n case 'Home':\n e.preventDefault();\n e.stopPropagation();\n // Move to first item\n newIndex = 0;\n if (index !== 0) {\n announcement = i18n.t('playlist.firstTrack', { total: buttons.length });\n }\n break;\n\n case 'End':\n e.preventDefault();\n e.stopPropagation();\n // Move to last item\n newIndex = buttons.length - 1;\n if (index !== buttons.length - 1) {\n announcement = i18n.t('playlist.lastTrack', { current: buttons.length, total: buttons.length });\n }\n break;\n }\n \n // Update tab indices for roving tabindex pattern\n if (newIndex !== -1 && newIndex !== index) {\n const currentButton = buttons[index];\n const newButton = buttons[newIndex];\n if (currentButton && newButton) {\n currentButton.setAttribute('tabIndex', '-1');\n newButton.setAttribute('tabIndex', '0');\n newButton.focus({ preventScroll: false });\n\n // Scroll the focused item into view (same behavior as mouse interaction)\n const item = newButton.closest('.vidply-playlist-item');\n if (item) {\n item.scrollIntoView(reducedMotionScrollOptions('nearest'));\n }\n }\n }\n \n // Announce navigation feedback\n if (announcement && this.navigationFeedback) {\n this.navigationFeedback.textContent = announcement;\n // Clear after a short delay to allow for repeated announcements\n setTimeout(() => {\n if (this.navigationFeedback) {\n this.navigationFeedback.textContent = '';\n }\n }, 1000);\n }\n }\n \n /**\n * Update playlist UI (highlight current track)\n */\n updatePlaylistUI() {\n if (!this.playlistPanel) return;\n \n const items = this.playlistPanel.querySelectorAll('.vidply-playlist-item');\n const buttons = this.playlistPanel.querySelectorAll('.vidply-playlist-item-button');\n \n items.forEach((item: Element, index: number) => {\n const button = buttons[index];\n if (!button) return;\n \n const track = this.tracks[index];\n if (!track) return;\n const trackTitle = track.title || i18n.t('playlist.trackUntitled', { number: index + 1 });\n const trackArtist = track.artist ? i18n.t('playlist.by') + track.artist : '';\n \n // Use effective duration (audio description duration when AD is enabled)\n const effectiveDuration = this.getEffectiveDuration(track);\n const trackDurationReadable = effectiveDuration ? TimeUtils.formatDuration(effectiveDuration as number) : '';\n \n if (index === this.currentIndex) {\n // Update list item styling\n item.classList.add('vidply-playlist-item-active');\n \n // Update button ARIA attributes\n button.setAttribute('aria-current', 'true');\n button.setAttribute('aria-checked', 'true');\n button.setAttribute('tabIndex', '0'); // Active item should be tabbable\n \n // Simplified aria-label - status and actions are announced via ARIA roles\n let ariaLabel = `${trackTitle}${trackArtist}`;\n if (trackDurationReadable) {\n ariaLabel += `. ${trackDurationReadable}`;\n }\n button.setAttribute('aria-label', ariaLabel);\n \n // Scroll into view within playlist panel (uses 'nearest' to minimize page scroll)\n item.scrollIntoView(reducedMotionScrollOptions('nearest'));\n } else {\n // Update list item styling\n item.classList.remove('vidply-playlist-item-active');\n \n // Update button ARIA attributes\n button.removeAttribute('aria-current');\n button.setAttribute('aria-checked', 'false');\n button.setAttribute('tabIndex', '-1'); // Remove from tab order (use arrow keys)\n \n // Simplified aria-label - status and actions are announced via ARIA roles\n let ariaLabel = `${trackTitle}${trackArtist}`;\n if (trackDurationReadable) {\n ariaLabel += `. ${trackDurationReadable}`;\n }\n button.setAttribute('aria-label', ariaLabel);\n }\n });\n }\n \n /**\n * Get current track\n */\n getCurrentTrack() {\n return this.tracks[this.currentIndex] || null;\n }\n \n /**\n * Get playlist info\n */\n getPlaylistInfo() {\n return {\n currentIndex: this.currentIndex,\n totalTracks: this.tracks.length,\n currentTrack: this.getCurrentTrack(),\n hasNext: this.hasNext(),\n hasPrevious: this.hasPrevious()\n };\n }\n \n /**\n * Check if there is a next track\n */\n hasNext() {\n if (this.options.loop) return true;\n return this.currentIndex < this.tracks.length - 1;\n }\n \n /**\n * Check if there is a previous track\n */\n hasPrevious() {\n if (this.options.loop) return true;\n return this.currentIndex > 0;\n }\n \n /**\n * Add track to playlist\n */\n addTrack(track: PlaylistTrack) {\n this.tracks.push(track);\n \n if (this.playlistPanel) {\n this.renderPlaylist();\n }\n }\n \n /**\n * Remove track from playlist\n */\n removeTrack(index: number) {\n if (index < 0 || index >= this.tracks.length) return;\n \n this.tracks.splice(index, 1);\n \n // Adjust current index if needed\n if (index < this.currentIndex) {\n this.currentIndex--;\n } else if (index === this.currentIndex) {\n // Current track was removed, play next or stop\n if (this.currentIndex >= this.tracks.length) {\n this.currentIndex = this.tracks.length - 1;\n }\n \n if (this.currentIndex >= 0) {\n this.play(this.currentIndex);\n }\n }\n \n if (this.playlistPanel) {\n this.renderPlaylist();\n }\n }\n \n /**\n * Clear playlist\n */\n clear() {\n this.tracks = [];\n this.currentIndex = -1;\n \n if (this.playlistPanel) {\n this.playlistPanel.innerHTML = '';\n this.playlistPanel.style.display = 'none';\n }\n \n if (this.trackInfoElement) {\n this.trackInfoElement.innerHTML = '';\n this.trackInfoElement.style.display = 'none';\n }\n \n if (this.trackArtworkElement) {\n this.trackArtworkElement.style.backgroundImage = '';\n this.trackArtworkElement.style.display = 'none';\n }\n }\n \n /**\n * Toggle playlist panel visibility\n * @param {boolean} show - Optional: force show (true) or hide (false)\n * @returns {boolean} - New visibility state\n */\n togglePanel(show?: boolean) {\n const playlistPanel = this.playlistPanel;\n if (!playlistPanel) return false;\n\n // Determine new state\n const shouldShow = show !== undefined ? show : playlistPanel.style.display === 'none';\n\n if (shouldShow) {\n playlistPanel.style.display = 'block';\n this.isPanelVisible = true;\n\n // Focus first item if playlist has tracks\n if (this.tracks.length > 0) {\n setTimeout(() => {\n const firstItem = playlistPanel.querySelector<HTMLElement>('.vidply-playlist-item[tabindex=\"0\"]');\n if (firstItem) {\n firstItem.focus({ preventScroll: true });\n }\n }, 100);\n }\n \n // Update toggle button state if it exists\n if (this.player.controlBar && this.player.controlBar.controls.playlistToggle) {\n this.player.controlBar.controls.playlistToggle.setAttribute('aria-expanded', 'true');\n this.player.controlBar.controls.playlistToggle.setAttribute('aria-pressed', 'true');\n }\n } else {\n playlistPanel.style.display = 'none';\n this.isPanelVisible = false;\n \n // Update toggle button state if it exists\n if (this.player.controlBar && this.player.controlBar.controls.playlistToggle) {\n this.player.controlBar.controls.playlistToggle.setAttribute('aria-expanded', 'false');\n this.player.controlBar.controls.playlistToggle.setAttribute('aria-pressed', 'false');\n \n // Return focus to toggle button\n this.player.controlBar.controls.playlistToggle.focus({ preventScroll: true });\n }\n }\n \n return this.isPanelVisible;\n }\n \n /**\n * Show playlist panel\n */\n showPanel() {\n return this.togglePanel(true);\n }\n \n /**\n * Hide playlist panel\n */\n hidePanel() {\n return this.togglePanel(false);\n }\n \n /**\n * Destroy playlist manager\n */\n destroy() {\n // Remove every listener registered in init(). All handlers are pre-bound\n // in the constructor, so these references match what on() registered.\n this.player.off('ended', this.handleTrackEnd);\n this.player.off('error', this.handleTrackError);\n this.player.off('play', this.handlePlaybackStateChange);\n this.player.off('pause', this.handlePlaybackStateChange);\n this.player.off('ended', this.handlePlaybackStateChange);\n this.player.off('fullscreenchange', this.handleFullscreenChange);\n this.player.off('audiodescriptionenabled', this.handleAudioDescriptionChange);\n this.player.off('audiodescriptiondisabled', this.handleAudioDescriptionChange);\n \n // Remove UI\n if (this.trackArtworkElement) {\n this.trackArtworkElement.remove();\n }\n \n if (this.trackInfoElement) {\n this.trackInfoElement.remove();\n }\n \n if (this.playlistPanel) {\n this.playlistPanel.remove();\n }\n \n // Clear data\n this.clear();\n }\n}\n\nexport default PlaylistManager;\n", "/**\n * VidPly - Universal Video Player\n * Main Entry Point\n */\n\nimport { Player } from './core/Player.js';\nimport { observeForLazyInit } from './core/LazyInit.js';\nimport { PlaylistManager } from './features/PlaylistManager.js';\nimport { isForbiddenKey } from './utils/Sanitize.js';\nimport type { PlayerOptions } from './types/options.js';\n\n// Re-export so existing consumers of `import { LazyHandle } from 'vidply'`\n// keep working; the canonical definition now lives in core/LazyInit.\nexport type { LazyHandle } from './core/LazyInit.js';\n\n/**\n * Filter `__proto__` / `constructor` / `prototype` keys from an attacker-\n * influenced JSON object before merging it into the player options. This\n * avoids prototype-pollution turning a malformed `data-vidply-options`\n * attribute into a global gadget.\n */\nfunction sanitizeOptionsObject(input: unknown): Partial<PlayerOptions> {\n if (!input || typeof input !== 'object' || Array.isArray(input)) {\n return {};\n }\n const out: Record<string, unknown> = Object.create(null);\n for (const [key, value] of Object.entries(input as Record<string, unknown>)) {\n if (isForbiddenKey(key)) continue;\n out[key] = value;\n }\n return out as Partial<PlayerOptions>;\n}\n\n/**\n * Parse a `data-vidply-options` JSON string. Robust against malformed input\n * — a single broken attribute on one element no longer aborts the auto-init\n * loop for every other player on the page.\n */\nfunction parseInlineOptions(element: HTMLElement): Partial<PlayerOptions> {\n const raw = element.dataset.vidplyOptions;\n if (!raw) return {};\n try {\n const parsed: unknown = JSON.parse(raw);\n return sanitizeOptionsObject(parsed);\n } catch (err) {\n console.warn('[VidPly] Ignored malformed data-vidply-options:', err);\n return {};\n }\n}\n\nfunction initializePlayers(): void {\n const elements = document.querySelectorAll<HTMLElement>('[data-vidply]');\n\n elements.forEach((element) => {\n const options = parseInlineOptions(element);\n const dataOptions = parseDataAttributes(element.dataset);\n const mergedOptions: Partial<PlayerOptions> = { ...dataOptions, ...options };\n\n const lazyInit =\n element.dataset.vidplyLazy !== 'false' && mergedOptions.lazyInit !== false;\n const lazyMargin =\n element.dataset.vidplyLazyMargin || (mergedOptions.lazyMargin as string) || '500px';\n\n if (lazyInit && 'IntersectionObserver' in window) {\n observeForLazyInit<Partial<PlayerOptions>>(\n element,\n mergedOptions,\n lazyMargin,\n (target, opts) => { new Player(target, opts); }\n );\n } else {\n new Player(element, mergedOptions);\n }\n });\n}\n\nfunction parseDataAttributes(dataset: DOMStringMap): Partial<PlayerOptions> {\n const options: Record<string, unknown> = Object.create(null);\n\n const attributeMap: Record<string, keyof PlayerOptions> = {\n signLanguageSrc: 'signLanguageSrc',\n signLanguageButton: 'signLanguageButton',\n signLanguagePosition: 'signLanguagePosition',\n signLanguageDisplayMode: 'signLanguageDisplayMode',\n audioDescriptionSrc: 'audioDescriptionSrc',\n audioDescriptionButton: 'audioDescriptionButton',\n audioDescriptionMode: 'audioDescriptionMode',\n audioDescriptionSpeech: 'audioDescriptionSpeech',\n audioDescriptionExtended: 'audioDescriptionExtended',\n autoplay: 'autoplay',\n loop: 'loop',\n muted: 'muted',\n controls: 'controls',\n poster: 'poster',\n width: 'width',\n height: 'height',\n language: 'language',\n captions: 'captions',\n captionsDefault: 'captionsDefault',\n transcript: 'transcript',\n transcriptButton: 'transcriptButton',\n keyboard: 'keyboard',\n responsive: 'responsive',\n pipButton: 'pipButton',\n fullscreenButton: 'fullscreenButton',\n floating: 'floating',\n floatingPosition: 'floatingPosition',\n floatingMinViewportWidth: 'floatingMinViewportWidth',\n lazyInit: 'lazyInit',\n lazyMargin: 'lazyMargin',\n theme: 'theme'\n };\n\n for (const [dataKey, optionKey] of Object.entries(attributeMap)) {\n if (isForbiddenKey(optionKey)) continue;\n const value = dataset[dataKey];\n if (value === undefined) continue;\n if (value === 'true') {\n options[optionKey] = true;\n } else if (value === 'false') {\n options[optionKey] = false;\n } else if (value !== '' && !Number.isNaN(Number(value))) {\n options[optionKey] = Number(value);\n } else {\n options[optionKey] = value;\n }\n }\n\n const signLanguageSources: Record<string, string> = Object.create(null);\n for (const key of Object.keys(dataset)) {\n if (key.startsWith('signLanguageSrc') && key !== 'signLanguageSrc') {\n const langMatch = key.match(/^signLanguageSrc([A-Z][a-z]*)$/);\n if (langMatch && langMatch[1]) {\n const langCode = langMatch[1].toLowerCase();\n const value = dataset[key];\n if (value !== undefined) {\n signLanguageSources[langCode] = value;\n }\n }\n }\n }\n\n if (Object.keys(signLanguageSources).length > 0) {\n options.signLanguageSources = signLanguageSources;\n if (dataset.signLanguageSrc && !options.signLanguageSrc) {\n options.signLanguageSrc = dataset.signLanguageSrc;\n }\n }\n\n if (dataset.vidplyLanguageFiles) {\n try {\n const parsed = JSON.parse(dataset.vidplyLanguageFiles);\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n options.languageFiles = sanitizeOptionsObject(parsed) as Record<string, string>;\n }\n } catch (e) {\n console.warn('Invalid JSON in data-vidply-language-files:', e);\n }\n }\n\n if (dataset.vidplyLanguageFile) {\n try {\n const parsed = JSON.parse(dataset.vidplyLanguageFile);\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n options.languageFiles = sanitizeOptionsObject(parsed) as Record<string, string>;\n }\n } catch {\n if (dataset.vidplyLanguageFileCode && dataset.vidplyLanguageFileUrl) {\n options.languageFile = dataset.vidplyLanguageFileCode;\n options.languageFileUrl = dataset.vidplyLanguageFileUrl;\n }\n }\n } else if (dataset.vidplyLanguageFileCode && dataset.vidplyLanguageFileUrl) {\n options.languageFile = dataset.vidplyLanguageFileCode;\n options.languageFileUrl = dataset.vidplyLanguageFileUrl;\n }\n\n return options as Partial<PlayerOptions>;\n}\n\nif (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', initializePlayers);\n} else {\n initializePlayers();\n}\n\nexport { Player, PlaylistManager };\nexport type { PlayerOptions } from './types/options.js';\nexport type {\n PlayerEventMap,\n PlaylistTrack,\n FloatingChangeDetail\n} from './types/events.js';\nexport type { PlayerState } from './types/state.js';\nexport type { Renderer, QualityLevel } from './types/renderer.js';\nexport default Player;\n"],
5
+ "mappings": "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",
6
6
  "names": ["currentTime", "track"]
7
7
  }