vidply 1.2.0 → 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.
- package/README.md +974 -946
- package/dist/dev/{vidply.AudioDescriptionManager-R47Q4YR4.js → vidply.AudioDescriptionManager-JYYLK5KB.js} +275 -19
- package/dist/dev/vidply.AudioDescriptionManager-JYYLK5KB.js.map +7 -0
- package/dist/dev/{vidply.CaptionStyleMenu-7XHRBWUM.js → vidply.CaptionStyleMenu-47RCKQF5.js} +4 -4
- package/dist/dev/{vidply.DASHRenderer-PWEMC3BT.js → vidply.DASHRenderer-VEJ4HDHO.js} +3 -3
- package/dist/dev/vidply.DASHRenderer-VEJ4HDHO.js.map +7 -0
- package/dist/dev/{vidply.FloatingPlayerManager-PKNNNAS2.js → vidply.FloatingPlayerManager-C2UVRPZY.js} +5 -5
- package/dist/dev/vidply.FloatingPlayerManager-C2UVRPZY.js.map +7 -0
- package/dist/dev/{vidply.HLSRenderer-E7RQ74HI.js → vidply.HLSRenderer-ZVBK2TNQ.js} +4 -4
- package/dist/dev/vidply.HLSRenderer-ZVBK2TNQ.js.map +7 -0
- package/dist/dev/vidply.HTML5Renderer-HLRQUOCU.js +12 -0
- package/dist/dev/{vidply.SignLanguageManager-FET5FQZ7.js → vidply.SignLanguageManager-FLUCBW5H.js} +8 -8
- package/dist/dev/vidply.SignLanguageManager-FLUCBW5H.js.map +7 -0
- package/dist/dev/{vidply.SoundCloudRenderer-RVSJZODO.js → vidply.SoundCloudRenderer-MGMAJ7NK.js} +3 -3
- package/dist/dev/vidply.SoundCloudRenderer-MGMAJ7NK.js.map +7 -0
- package/dist/dev/{vidply.TranscriptManager-5GXOQCLX.js → vidply.TranscriptManager-Y2RYXHJW.js} +10 -10
- package/dist/dev/vidply.TranscriptManager-Y2RYXHJW.js.map +7 -0
- package/dist/dev/{vidply.VimeoRenderer-ER2LSBBW.js → vidply.VimeoRenderer-RQFQD7OE.js} +3 -3
- package/dist/dev/vidply.VimeoRenderer-RQFQD7OE.js.map +7 -0
- package/dist/dev/{vidply.YouTubeRenderer-NFTOWJAB.js → vidply.YouTubeRenderer-EU2BARZF.js} +3 -3
- package/dist/dev/vidply.YouTubeRenderer-EU2BARZF.js.map +7 -0
- package/dist/dev/{vidply.chunk-BNFIUL2Z.js → vidply.chunk-6LYZYI5G.js} +3 -3
- package/dist/dev/vidply.chunk-6LYZYI5G.js.map +7 -0
- package/dist/dev/{vidply.chunk-NQPURMQY.js → vidply.chunk-A4ESEOQ2.js} +6 -6
- package/dist/dev/vidply.chunk-A4ESEOQ2.js.map +7 -0
- package/dist/dev/{vidply.chunk-W25HUCHS.js → vidply.chunk-CGYIED2I.js} +2 -2
- package/dist/dev/vidply.chunk-CGYIED2I.js.map +7 -0
- package/dist/dev/{vidply.chunk-SK4V2DAU.js → vidply.chunk-HZ7UOKOU.js} +3 -3
- package/dist/dev/{vidply.chunk-RZWZGDV7.js → vidply.chunk-I6EDRYGJ.js} +33 -6
- package/dist/dev/vidply.chunk-I6EDRYGJ.js.map +7 -0
- package/dist/dev/{vidply.chunk-64WXMTGL.js → vidply.chunk-JKMQGLCJ.js} +5 -5
- package/dist/dev/vidply.chunk-JKMQGLCJ.js.map +7 -0
- package/dist/dev/{vidply.chunk-DZSDO3B7.js → vidply.chunk-JZNZX32Z.js} +2 -2
- package/dist/dev/{vidply.chunk-VMSQAZM7.js → vidply.chunk-P25MRJ6O.js} +2 -2
- package/dist/dev/{vidply.chunk-6RQA4ROD.js → vidply.chunk-PRFNESYA.js} +2 -2
- package/dist/dev/{vidply.chunk-KI7C3QVL.js → vidply.chunk-UI3RYCVF.js} +4 -3
- package/dist/dev/vidply.chunk-UI3RYCVF.js.map +7 -0
- package/dist/dev/{vidply.chunk-JJ47NO24.js → vidply.chunk-UNFGMABP.js} +2 -2
- package/dist/dev/vidply.chunk-UNFGMABP.js.map +7 -0
- package/dist/dev/{vidply.de-4YAT7FFM.js → vidply.de-VAUVSQXC.js} +29 -2
- package/dist/dev/vidply.de-VAUVSQXC.js.map +7 -0
- package/dist/dev/{vidply.es-LEWIRUBH.js → vidply.es-G65HFWO6.js} +29 -2
- package/dist/dev/vidply.es-G65HFWO6.js.map +7 -0
- package/dist/dev/vidply.esm.js +648 -35
- package/dist/dev/vidply.esm.js.map +3 -3
- package/dist/dev/{vidply.fr-Q4GV2NOE.js → vidply.fr-MJNOZELM.js} +29 -2
- package/dist/dev/vidply.fr-MJNOZELM.js.map +7 -0
- package/dist/dev/{vidply.ja-S6ITA6WU.js → vidply.ja-INMDHIYJ.js} +29 -2
- package/dist/dev/vidply.ja-INMDHIYJ.js.map +7 -0
- package/dist/legacy/vidply.js +1062 -34
- package/dist/legacy/vidply.js.map +3 -3
- package/dist/legacy/vidply.min.js +2 -2
- package/dist/legacy/vidply.min.meta.json +127 -55
- package/dist/prod/vidply.AudioDescriptionManager-EKWUNOPE.min.js +6 -0
- package/dist/prod/{vidply.CaptionStyleMenu-M3KVQWB5.min.js → vidply.CaptionStyleMenu-JTWSNHRQ.min.js} +2 -2
- package/dist/prod/{vidply.DASHRenderer-DN3S27AV.min.js → vidply.DASHRenderer-CIDAHFWM.min.js} +2 -2
- package/dist/prod/{vidply.FloatingPlayerManager-SKAZ4J43.min.js → vidply.FloatingPlayerManager-VZXHPCPM.min.js} +2 -2
- package/dist/prod/{vidply.HLSRenderer-62HH4YTK.min.js → vidply.HLSRenderer-Q6EFYKID.min.js} +2 -2
- package/dist/prod/vidply.HTML5Renderer-XRDJUTOO.min.js +6 -0
- package/dist/prod/{vidply.SignLanguageManager-V3OBTSSF.min.js → vidply.SignLanguageManager-JUOSA5AM.min.js} +2 -2
- package/dist/prod/{vidply.SoundCloudRenderer-CXRVZIAJ.min.js → vidply.SoundCloudRenderer-7ODD46PO.min.js} +2 -2
- package/dist/prod/{vidply.TranscriptManager-SYM3DFUY.min.js → vidply.TranscriptManager-4B2QKYGQ.min.js} +2 -2
- package/dist/prod/{vidply.VimeoRenderer-72TWRYFQ.min.js → vidply.VimeoRenderer-MR3UYLY7.min.js} +2 -2
- package/dist/prod/{vidply.YouTubeRenderer-UMHEV3KD.min.js → vidply.YouTubeRenderer-ITRTC7NR.min.js} +2 -2
- package/dist/prod/{vidply.chunk-YEENJS3Q.min.js → vidply.chunk-3EEDXU4T.min.js} +2 -2
- package/dist/prod/{vidply.chunk-P4UXOHWA.min.js → vidply.chunk-5HTX5QXW.min.js} +1 -1
- package/dist/prod/{vidply.chunk-ABPXVDLZ.min.js → vidply.chunk-6OGHHIYP.min.js} +2 -2
- package/dist/prod/{vidply.chunk-UK7YGOB5.min.js → vidply.chunk-GUPGMFUZ.min.js} +2 -2
- package/dist/prod/{vidply.chunk-WX3MVEAL.min.js → vidply.chunk-MWEBITCP.min.js} +1 -1
- package/dist/prod/{vidply.chunk-2TB6MUHO.min.js → vidply.chunk-PL3P3JHR.min.js} +1 -1
- package/dist/prod/{vidply.chunk-EKEYJWMG.min.js → vidply.chunk-QQZGV7TD.min.js} +2 -2
- package/dist/prod/{vidply.chunk-Z3YDSA7O.min.js → vidply.chunk-U5IRRYG5.min.js} +1 -1
- package/dist/prod/{vidply.chunk-TM6AXJVD.min.js → vidply.chunk-VKHV4QHA.min.js} +2 -2
- package/dist/prod/{vidply.chunk-P3PQBY7N.min.js → vidply.chunk-XA4M574A.min.js} +1 -1
- package/dist/prod/{vidply.chunk-RI3X7WHK.min.js → vidply.chunk-ZPTV4JEP.min.js} +2 -2
- package/dist/prod/{vidply.de-BCHXRQ53.min.js → vidply.de-UZHFCEZU.min.js} +2 -2
- package/dist/prod/{vidply.es-PICAKMZD.min.js → vidply.es-BOISUV2S.min.js} +2 -2
- package/dist/prod/vidply.esm.min.js +2 -2
- package/dist/prod/{vidply.fr-ZILI7CDR.min.js → vidply.fr-ICSMKPFM.min.js} +2 -2
- package/dist/prod/{vidply.ja-5WQ4OK4J.min.js → vidply.ja-VQ2HKGAY.min.js} +2 -2
- package/dist/vidply.css +5852 -5796
- package/dist/vidply.esm.min.meta.json +204 -149
- package/dist/vidply.min.css +1 -1
- package/package.json +11 -11
- package/src/controls/CaptionManager.ts +598 -598
- package/src/controls/ControlBar.ts +4191 -4159
- package/src/controls/KeyboardHelp.ts +326 -0
- package/src/controls/KeyboardManager.ts +387 -383
- package/src/controls/SettingsDialog.ts +488 -488
- package/src/controls/TranscriptManager.ts +2426 -2426
- package/src/core/AudioDescriptionManager.ts +863 -774
- package/src/core/DescriptionSpeechManager.ts +232 -0
- package/src/core/FloatingPlayerManager.ts +674 -674
- package/src/core/LazyInit.ts +80 -80
- package/src/core/MediaSessionManager.ts +341 -0
- package/src/core/MetadataAlertsManager.ts +514 -514
- package/src/core/Player.ts +2973 -2872
- package/src/core/PosterManager.ts +169 -169
- package/src/core/PseudoFullscreen.ts +179 -179
- package/src/core/ResponsiveManager.ts +231 -231
- package/src/core/ResumeManager.ts +236 -236
- package/src/core/SignLanguageManager.ts +1308 -1308
- package/src/core/ThemeManager.ts +157 -157
- package/src/features/PlaylistManager.ts +1838 -1827
- package/src/i18n/i18n.ts +223 -223
- package/src/i18n/languages/de.ts +244 -217
- package/src/i18n/languages/en.ts +245 -218
- package/src/i18n/languages/es.ts +241 -214
- package/src/i18n/languages/fr.ts +241 -214
- package/src/i18n/languages/ja.ts +241 -214
- package/src/icons/Icons.ts +168 -167
- package/src/index.ts +196 -193
- package/src/renderers/DASHRenderer.ts +1077 -1077
- package/src/renderers/HLSRenderer.ts +944 -944
- package/src/renderers/HTML5Renderer.ts +389 -389
- package/src/renderers/SoundCloudRenderer.ts +437 -437
- package/src/renderers/VimeoRenderer.ts +298 -298
- package/src/renderers/YouTubeRenderer.ts +290 -290
- package/src/styles/vidply.css +5852 -5796
- package/src/types/events.ts +128 -122
- package/src/types/globals.d.ts +313 -313
- package/src/types/options.ts +199 -182
- package/src/types/renderer.ts +58 -58
- package/src/utils/DOMUtils.ts +281 -281
- package/src/utils/DownloadInfo.ts +171 -171
- package/src/utils/DraggablePanel.ts +539 -539
- package/src/utils/DraggablePanelMenu.ts +195 -195
- package/src/utils/DraggableResizable.ts +997 -997
- package/src/utils/EventEmitter.ts +59 -59
- package/src/utils/FormUtils.ts +95 -95
- package/src/utils/MenuFactory.ts +483 -483
- package/src/utils/Sanitize.ts +70 -70
- package/src/utils/SettingsMenuFactory.ts +289 -289
- package/src/utils/StorageManager.ts +287 -287
- package/src/utils/TrackLabelUtils.ts +52 -52
- package/src/utils/UrlSafe.ts +69 -69
- package/src/utils/VideoFrameCapture.ts +114 -114
- package/src/utils/WindowComponents.ts +250 -250
- package/dist/dev/vidply.AudioDescriptionManager-R47Q4YR4.js.map +0 -7
- package/dist/dev/vidply.DASHRenderer-PWEMC3BT.js.map +0 -7
- package/dist/dev/vidply.FloatingPlayerManager-PKNNNAS2.js.map +0 -7
- package/dist/dev/vidply.HLSRenderer-E7RQ74HI.js.map +0 -7
- package/dist/dev/vidply.HTML5Renderer-BU4QJJJY.js +0 -12
- package/dist/dev/vidply.SignLanguageManager-FET5FQZ7.js.map +0 -7
- package/dist/dev/vidply.SoundCloudRenderer-RVSJZODO.js.map +0 -7
- package/dist/dev/vidply.TranscriptManager-5GXOQCLX.js.map +0 -7
- package/dist/dev/vidply.VimeoRenderer-ER2LSBBW.js.map +0 -7
- package/dist/dev/vidply.YouTubeRenderer-NFTOWJAB.js.map +0 -7
- package/dist/dev/vidply.chunk-64WXMTGL.js.map +0 -7
- package/dist/dev/vidply.chunk-BNFIUL2Z.js.map +0 -7
- package/dist/dev/vidply.chunk-JJ47NO24.js.map +0 -7
- package/dist/dev/vidply.chunk-KI7C3QVL.js.map +0 -7
- package/dist/dev/vidply.chunk-NQPURMQY.js.map +0 -7
- package/dist/dev/vidply.chunk-RZWZGDV7.js.map +0 -7
- package/dist/dev/vidply.chunk-W25HUCHS.js.map +0 -7
- package/dist/dev/vidply.de-4YAT7FFM.js.map +0 -7
- package/dist/dev/vidply.es-LEWIRUBH.js.map +0 -7
- package/dist/dev/vidply.fr-Q4GV2NOE.js.map +0 -7
- package/dist/dev/vidply.ja-S6ITA6WU.js.map +0 -7
- package/dist/prod/vidply.AudioDescriptionManager-RHLIJW5C.min.js +0 -6
- package/dist/prod/vidply.HTML5Renderer-IU5T3WEW.min.js +0 -6
- /package/dist/dev/{vidply.CaptionStyleMenu-7XHRBWUM.js.map → vidply.CaptionStyleMenu-47RCKQF5.js.map} +0 -0
- /package/dist/dev/{vidply.HTML5Renderer-BU4QJJJY.js.map → vidply.HTML5Renderer-HLRQUOCU.js.map} +0 -0
- /package/dist/dev/{vidply.chunk-SK4V2DAU.js.map → vidply.chunk-HZ7UOKOU.js.map} +0 -0
- /package/dist/dev/{vidply.chunk-DZSDO3B7.js.map → vidply.chunk-JZNZX32Z.js.map} +0 -0
- /package/dist/dev/{vidply.chunk-VMSQAZM7.js.map → vidply.chunk-P25MRJ6O.js.map} +0 -0
- /package/dist/dev/{vidply.chunk-6RQA4ROD.js.map → vidply.chunk-PRFNESYA.js.map} +0 -0
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/renderers/SoundCloudRenderer.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Renderer } from '../types/renderer.js';\r\nimport type { Player } from '../core/Player.js';\r\nimport { loadScriptOnce } from '../utils/ScriptLoader.js';\r\n\r\n/**\r\n * Subset of the SoundCloud Widget event payloads we actually consume.\r\n * The Widget API is callback-shaped and does not export TS types, so we\r\n * narrow `unknown` arguments locally as they enter our handlers.\r\n */\r\ninterface SCSoundInfo {\r\n duration: number;\r\n title?: string;\r\n}\r\n\r\ninterface SCProgressData {\r\n currentPosition: number;\r\n loadedProgress?: number;\r\n}\r\n\r\ninterface SCWidgetError {\r\n message?: string;\r\n}\r\n\r\nexport class SoundCloudRenderer implements Renderer {\r\n player: Player;\r\n media: HTMLMediaElement;\r\n widget: SCWidget | null;\r\n trackUrl: string | null;\r\n isReady: boolean;\r\n iframe: HTMLIFrameElement | null;\r\n iframeId: string | null;\r\n _previousVolume: number;\r\n // Pending init timeout (rejects after 10s); cleared once READY fires or on\r\n // destroy so it can't reject / touch a torn-down player afterwards.\r\n private _initTimeoutId: ReturnType<typeof setTimeout> | null;\r\n // Detaches the iframe 'load' listener on destroy.\r\n private _initController: AbortController | null;\r\n\r\n constructor(player: Player) {\r\n this.player = player;\r\n this.media = player.element;\r\n this.widget = null;\r\n this.trackUrl = null;\r\n this.isReady = false;\r\n this.iframe = null;\r\n this.iframeId = null;\r\n this._previousVolume = 100;\r\n this._initTimeoutId = null;\r\n this._initController = null;\r\n }\r\n\r\n async init() {\r\n // Extract track URL - use currentSource which works for external renderers\r\n this.trackUrl = this.player.currentSource || this.player.element.src || this.player.element.querySelector('source')?.src || null;\r\n \r\n if (!this.trackUrl || !this.isValidSoundCloudUrl(this.trackUrl)) {\r\n throw new Error('Invalid SoundCloud URL');\r\n }\r\n\r\n // Load SoundCloud Widget API\r\n await this.loadSoundCloudAPI();\r\n\r\n // Create iframe\r\n this.createIframe();\r\n\r\n // Initialize widget\r\n await this.initializeWidget();\r\n }\r\n\r\n /**\r\n * Validate a SoundCloud URL by parsing it with the URL constructor and\r\n * checking the protocol + hostname against an explicit allow-list.\r\n * Substring checks like `url.includes('soundcloud.com')` accept things\r\n * like `https://evil.com/?leak=soundcloud.com` or\r\n * `https://soundcloud.com.attacker.example`.\r\n */\r\n isValidSoundCloudUrl(url: string): boolean {\r\n if (typeof url !== 'string' || !url) return false;\r\n let parsed: URL;\r\n try {\r\n parsed = new URL(url);\r\n } catch {\r\n return false;\r\n }\r\n if (parsed.protocol !== 'https:') return false;\r\n const allowedHosts = new Set([\r\n 'soundcloud.com',\r\n 'www.soundcloud.com',\r\n 'm.soundcloud.com',\r\n 'api.soundcloud.com',\r\n 'api-v2.soundcloud.com'\r\n ]);\r\n return allowedHosts.has(parsed.hostname.toLowerCase());\r\n }\r\n\r\n /**\r\n * Check if URL is a playlist/set\r\n */\r\n isPlaylist() {\r\n return this.trackUrl && this.trackUrl.includes('/sets/');\r\n }\r\n\r\n /**\r\n * Extract track/playlist info from URL for embed\r\n * SoundCloud URLs can be:\r\n * - https://soundcloud.com/artist/track\r\n * - https://soundcloud.com/artist/sets/playlist\r\n * - https://api.soundcloud.com/tracks/123456\r\n */\r\n getEmbedUrl() {\r\n const trackUrl = this.trackUrl;\r\n if (!trackUrl) {\r\n throw new Error('SoundCloudRenderer.getEmbedUrl(): trackUrl is not set');\r\n }\r\n \r\n // Build widget URL with parameters\r\n const params = new URLSearchParams({\r\n url: trackUrl,\r\n auto_play: this.player.options.autoplay ? 'true' : 'false',\r\n hide_related: 'true',\r\n show_comments: 'false',\r\n show_user: 'true',\r\n show_reposts: 'false',\r\n show_teaser: 'false',\r\n visual: 'false', // Use classic player for better control\r\n color: '%23007bff'\r\n });\r\n \r\n return `https://w.soundcloud.com/player/?${params.toString()}`;\r\n }\r\n\r\n async loadSoundCloudAPI() {\r\n // Check if API is already loaded\r\n if (typeof window.SC !== 'undefined') {\r\n return Promise.resolve();\r\n }\r\n\r\n // SC.Widget can become available a tick after the script's load event, so\r\n // resolve via the readiness predicate rather than load alone.\r\n return loadScriptOnce('https://w.soundcloud.com/player/api.js', {\r\n isReady: () => typeof window.SC !== 'undefined'\r\n });\r\n }\r\n\r\n createIframe() {\r\n // Hide original element and remove poster (SoundCloud has its own visual widget)\r\n this.player.element.style.display = 'none';\r\n this.player.element.removeAttribute('poster');\r\n \r\n // Remove poster overlay from video wrapper if present\r\n if (this.player.videoWrapper) {\r\n this.player.videoWrapper.classList.remove('vidply-forced-poster');\r\n this.player.videoWrapper.style.removeProperty('--vidply-poster-image');\r\n }\r\n\r\n // Generate unique ID for iframe\r\n this.iframeId = `soundcloud-player-${Math.random().toString(36).substr(2, 9)}`;\r\n\r\n // Create iframe for SoundCloud widget\r\n this.iframe = document.createElement('iframe');\r\n this.iframe.id = this.iframeId;\r\n this.iframe.scrolling = 'no';\r\n this.iframe.frameBorder = 'no';\r\n this.iframe.allow = 'autoplay';\r\n this.iframe.src = this.getEmbedUrl();\r\n this.iframe.style.width = '100%';\r\n this.iframe.style.display = 'block';\r\n \r\n // Use different aspect ratio for playlists vs single tracks\r\n // Playlists need more height to show the track list\r\n if (this.isPlaylist()) {\r\n this.iframe.classList.add('vidply-soundcloud-iframe', 'vidply-soundcloud-playlist');\r\n } else {\r\n this.iframe.classList.add('vidply-soundcloud-iframe');\r\n }\r\n this.iframe.style.maxHeight = '100%';\r\n \r\n this.player.element.parentNode?.insertBefore(this.iframe, this.player.element);\r\n }\r\n\r\n async initializeWidget() {\r\n return new Promise<void>((resolve, reject) => {\r\n const iframe = this.iframe;\r\n if (!iframe || typeof window.SC === 'undefined') {\r\n reject(new Error('SoundCloud widget cannot initialize'));\r\n return;\r\n }\r\n\r\n const SC = window.SC;\r\n if (!SC) {\r\n reject(new Error('SoundCloud widget cannot initialize'));\r\n return;\r\n }\r\n\r\n // Wait for iframe to load. Registered through an AbortController so\r\n // destroy() can detach it if init never completes.\r\n this._initController = new AbortController();\r\n iframe.addEventListener('load', () => {\r\n try {\r\n const widget = SC.Widget(iframe);\r\n this.widget = widget;\r\n\r\n widget.bind(SC.Widget.Events.READY, () => {\r\n this.isReady = true;\r\n // Init succeeded — cancel the pending timeout so it can't reject.\r\n if (this._initTimeoutId !== null) {\r\n clearTimeout(this._initTimeoutId);\r\n this._initTimeoutId = null;\r\n }\r\n this.attachEvents();\r\n\r\n // Hide VidPly controls - SoundCloud has its own\r\n if (this.player.container) {\r\n this.player.container.classList.add('vidply-external-controls');\r\n }\r\n\r\n // Get initial sound info\r\n widget.getCurrentSound((sound: unknown) => {\r\n const info = sound as SCSoundInfo | null;\r\n if (info) {\r\n this.player.state.duration = info.duration / 1000; // ms -> s\r\n this.player.emit('loadedmetadata');\r\n }\r\n });\r\n\r\n resolve();\r\n });\r\n\r\n widget.bind(SC.Widget.Events.ERROR, (...args: unknown[]) => {\r\n const error = args[0] as SCWidgetError | undefined;\r\n this.player.handleError(new Error(`SoundCloud error: ${error?.message || 'Unknown error'}`));\r\n });\r\n } catch (error) {\r\n reject(error);\r\n }\r\n }, { signal: this._initController.signal });\r\n \r\n // Timeout after 10 seconds\r\n this._initTimeoutId = setTimeout(() => {\r\n this._initTimeoutId = null;\r\n if (!this.isReady) {\r\n reject(new Error('SoundCloud widget initialization timeout'));\r\n }\r\n }, 10000);\r\n });\r\n }\r\n\r\n attachEvents() {\r\n const widget = this.widget;\r\n const SC = window.SC;\r\n if (!widget || !SC) return;\r\n\r\n const Events = SC.Widget.Events;\r\n\r\n widget.bind(Events.PLAY, () => {\r\n this.player.state.playing = true;\r\n this.player.state.paused = false;\r\n this.player.state.ended = false;\r\n this.player.emit('play');\r\n\r\n if (this.player.options.onPlay) {\r\n this.player.options.onPlay.call(this.player);\r\n }\r\n });\r\n\r\n widget.bind(Events.PAUSE, () => {\r\n this.player.state.playing = false;\r\n this.player.state.paused = true;\r\n this.player.emit('pause');\r\n\r\n if (this.player.options.onPause) {\r\n this.player.options.onPause.call(this.player);\r\n }\r\n });\r\n\r\n widget.bind(Events.FINISH, () => {\r\n this.player.state.playing = false;\r\n this.player.state.paused = true;\r\n this.player.state.ended = true;\r\n this.player.emit('ended');\r\n\r\n if (this.player.options.onEnded) {\r\n this.player.options.onEnded.call(this.player);\r\n }\r\n\r\n if (this.player.options.loop) {\r\n this.seek(0);\r\n this.play();\r\n }\r\n });\r\n\r\n widget.bind(Events.PLAY_PROGRESS, (...args: unknown[]) => {\r\n const data = args[0] as SCProgressData;\r\n // data.currentPosition is in milliseconds\r\n const currentTime = data.currentPosition / 1000;\r\n this.player.state.currentTime = currentTime;\r\n this.player.emit('timeupdate', currentTime);\r\n\r\n if (this.player.options.onTimeUpdate) {\r\n this.player.options.onTimeUpdate.call(this.player, currentTime);\r\n }\r\n });\r\n\r\n widget.bind(Events.SEEK, (...args: unknown[]) => {\r\n const data = args[0] as SCProgressData;\r\n this.player.state.currentTime = data.currentPosition / 1000;\r\n this.player.emit('seeked');\r\n });\r\n\r\n widget.bind(Events.LOAD_PROGRESS, (...args: unknown[]) => {\r\n const data = args[0] as SCProgressData;\r\n // data.loadedProgress is 0-1\r\n if (this.player.state.duration && data.loadedProgress !== undefined) {\r\n const buffered = data.loadedProgress * this.player.state.duration;\r\n this.player.emit('progress', buffered);\r\n }\r\n });\r\n }\r\n\r\n play() {\r\n if (this.isReady && this.widget) {\r\n // Save scroll position to prevent browser from scrolling\r\n const scrollX = window.scrollX;\r\n const scrollY = window.scrollY;\r\n \r\n this.widget.play();\r\n \r\n // Restore scroll position\r\n window.scrollTo(scrollX, scrollY);\r\n }\r\n }\r\n\r\n pause() {\r\n if (this.isReady && this.widget) {\r\n this.widget.pause();\r\n }\r\n }\r\n\r\n seek(time: number) {\r\n if (this.isReady && this.widget) {\r\n // SoundCloud seekTo uses milliseconds\r\n this.widget.seekTo(time * 1000);\r\n this.player.state.currentTime = time;\r\n }\r\n }\r\n\r\n setVolume(volume: number) {\r\n if (this.isReady && this.widget) {\r\n // SoundCloud setVolume expects 0-100\r\n this.widget.setVolume(volume * 100);\r\n this.player.state.volume = volume;\r\n }\r\n }\r\n\r\n setMuted(muted: boolean) {\r\n const widget = this.widget;\r\n if (this.isReady && widget) {\r\n // SoundCloud doesn't have a native mute, use volume instead\r\n if (muted) {\r\n // Store current volume before muting\r\n widget.getVolume((vol: number) => {\r\n this._previousVolume = vol;\r\n widget.setVolume(0);\r\n });\r\n } else {\r\n widget.setVolume(this._previousVolume || 100);\r\n }\r\n this.player.state.muted = muted;\r\n }\r\n }\r\n\r\n setPlaybackSpeed(_speed: number) {\r\n // SoundCloud Widget API doesn't support playback speed\r\n this.player.log('SoundCloud does not support playback speed control', 'warn');\r\n }\r\n\r\n /**\r\n * Get current track info. Returns the raw sound payload from the\r\n * SoundCloud Widget API (shape is best described as `unknown` since\r\n * the API exposes many optional fields we don't formally type).\r\n */\r\n getCurrentSound(): Promise<unknown | null> {\r\n return new Promise<unknown | null>((resolve) => {\r\n if (this.isReady && this.widget) {\r\n this.widget.getCurrentSound((sound: unknown) => {\r\n resolve(sound);\r\n });\r\n } else {\r\n resolve(null);\r\n }\r\n });\r\n }\r\n\r\n destroy() {\r\n // Cancel any pending init timeout and detach the iframe 'load' listener.\r\n if (this._initTimeoutId !== null) {\r\n clearTimeout(this._initTimeoutId);\r\n this._initTimeoutId = null;\r\n }\r\n if (this._initController) {\r\n this._initController.abort();\r\n this._initController = null;\r\n }\r\n\r\n // Unbind all events\r\n if (this.widget && window.SC) {\r\n const Events = window.SC.Widget.Events;\r\n try {\r\n this.widget.unbind(Events.READY);\r\n this.widget.unbind(Events.PLAY);\r\n this.widget.unbind(Events.PAUSE);\r\n this.widget.unbind(Events.FINISH);\r\n this.widget.unbind(Events.PLAY_PROGRESS);\r\n this.widget.unbind(Events.SEEK);\r\n this.widget.unbind(Events.LOAD_PROGRESS);\r\n this.widget.unbind(Events.ERROR);\r\n } catch {\r\n // Ignore unbind errors\r\n }\r\n }\r\n\r\n if (this.iframe && this.iframe.parentNode) {\r\n this.iframe.parentNode.removeChild(this.iframe);\r\n }\r\n\r\n // Show original element\r\n if (this.player.element) {\r\n this.player.element.style.display = '';\r\n }\r\n\r\n this.widget = null;\r\n this.isReady = false;\r\n }\r\n}\r\n\r\nexport default SoundCloudRenderer;\r\n\r\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/controls/TranscriptManager.ts"],
|
|
4
|
-
"sourcesContent": ["/**\r\n * Transcript Manager Component\r\n * Manages transcript display and interaction\r\n */\r\n\r\nimport { DOMUtils } from '../utils/DOMUtils.js';\r\nimport { TimeUtils } from '../utils/TimeUtils.js';\r\nimport { createIconElement } from '../icons/Icons.js';\r\nimport { i18n } from '../i18n/i18n.js';\r\nimport { StorageManager } from '../utils/StorageManager.js';\r\nimport { focusElement } from '../utils/FocusUtils.js';\r\nimport { createMenuItem } from '../utils/MenuUtils.js';\r\nimport { DraggableResizable } from '../utils/DraggableResizable.js';\r\nimport { createLabeledSelect, preventDragOnElement } from '../utils/FormUtils.js';\r\nimport { DraggablePanel } from '../utils/DraggablePanel.js';\r\nimport { deriveTrackLabel } from '../utils/TrackLabelUtils.js';\r\nimport type { Player } from '../core/Player.js';\r\nimport type { Renderer } from '../types/renderer.js';\r\n\r\ntype TranscriptCue = TextTrackCue;\r\ntype TranscriptTrack = TextTrack & { _vidplyStale?: boolean };\r\ntype TranscriptLanguageInfo = {\r\n language: string;\r\n label: string;\r\n track: TranscriptTrack;\r\n};\r\ntype TranscriptCueItem = {\r\n cue: TranscriptCue;\r\n type: 'caption' | 'description';\r\n};\r\ntype TranscriptEntry = {\r\n element: HTMLElement;\r\n cue: TranscriptCue;\r\n type: 'caption' | 'description';\r\n startTime: number;\r\n endTime: number;\r\n};\r\n\r\ninterface TranscriptStyleOptions {\r\n fontSize: string;\r\n fontFamily: string;\r\n color: string;\r\n backgroundColor: string;\r\n opacity: number;\r\n [key: string]: unknown;\r\n}\r\n\r\ninterface TranscriptHandlers {\r\n timeupdate: () => void;\r\n seeked: () => void;\r\n audiodescriptionenabled: () => void;\r\n audiodescriptiondisabled: () => void;\r\n textcuesupdate: (() => void) | null;\r\n resize: (() => void) | null;\r\n settingsClick: ((e: MouseEvent) => void) | null;\r\n settingsKeydown: ((e: KeyboardEvent) => void) | null;\r\n documentClick: ((e: MouseEvent) => void) | null;\r\n styleDialogKeydown: ((e: KeyboardEvent) => void) | null;\r\n floatingchange: ((state: 'pinned' | 'auto' | null) => void) | null;\r\n}\r\n\r\ntype TimerHandle = ReturnType<typeof setTimeout>;\r\n\r\nexport class TranscriptManager {\r\n player: Player;\r\n _cueUpdateTimeout: TimerHandle | null;\r\n autoscrollCheckbox: HTMLInputElement | null = null;\r\n autoscrollEnabled: boolean;\r\n availableTranscriptLanguages: TranscriptLanguageInfo[];\r\n currentActiveEntry: TranscriptEntry | null;\r\n currentTranscriptLanguage: string | null;\r\n customKeyHandler: ((e: KeyboardEvent) => void) | null = null;\r\n /**\r\n * True once the style-dialog's outside-click listener has been\r\n * attached. The settings-menu's outside-click listener is now\r\n * owned by {@link DraggablePanel} and tracked there; this flag\r\n * covers the dialog half of the shared `handlers.documentClick`.\r\n */\r\n documentClickHandlerAdded: boolean = false;\r\n draggableResizable: DraggableResizable | null;\r\n handlers: TranscriptHandlers = {} as TranscriptHandlers;\r\n headerLeft: HTMLElement | null = null;\r\n isVisible: boolean;\r\n languageLabel: HTMLElement | null;\r\n languageSelector: HTMLSelectElement | null;\r\n languageSelectorHandler: ((e: Event) => void) | null;\r\n languageSelectorWrapper: HTMLElement | null = null;\r\n liveRegion: HTMLElement | null;\r\n metadataCueChangeHandler: (() => void) | null = null;\r\n metadataCues: TextTrackCue[];\r\n settingsButton: HTMLButtonElement | null;\r\n showTimestamps: boolean;\r\n showTimestampsButton: HTMLElement | null = null;\r\n showTimestampsText: Element | null = null;\r\n storage: StorageManager;\r\n styleDialog: HTMLElement | null;\r\n styleDialogJustOpened: boolean;\r\n styleDialogVisible: boolean;\r\n timeouts: Set<TimerHandle>;\r\n transcriptContent: HTMLElement | null = null;\r\n transcriptEntries: TranscriptEntry[];\r\n transcriptHeader: HTMLElement | null = null;\r\n transcriptResizeHandles: HTMLElement[];\r\n transcriptStyle: TranscriptStyleOptions;\r\n transcriptWindow: HTMLElement | null;\r\n _dashActiveLang: string | null;\r\n _vttCache: Map<string, TranscriptCueItem[]>;\r\n\r\n /**\r\n * Owns the settings-menu DOM scaffold, its outside-click\r\n * dismissal, keyboard navigation, viewport-aware positioning,\r\n * and the drag-mode / resize-mode toggle items. Instantiated\r\n * lazily once the header is built in {@link createTranscriptHeader}.\r\n */\r\n private _panel: DraggablePanel | null = null;\r\n\r\n // Back-compat getters for panel-owned state. External callers and\r\n // internal reads (e.g. `this.settingsMenuVisible` inside other\r\n // transcript methods) keep reading the same properties; the\r\n // setters are no-ops because the panel is now the authoritative\r\n // owner of those values.\r\n get settingsMenu(): HTMLElement | null {\r\n return this._panel?.settingsMenu ?? null;\r\n }\r\n set settingsMenu(_v: HTMLElement | null) {\r\n // panel-owned.\r\n }\r\n\r\n get settingsMenuVisible(): boolean {\r\n return this._panel?.settingsMenuVisible ?? false;\r\n }\r\n set settingsMenuVisible(_v: boolean) {\r\n // panel-owned.\r\n }\r\n\r\n get settingsMenuJustOpened(): boolean {\r\n return this._panel?.justOpened ?? false;\r\n }\r\n set settingsMenuJustOpened(_v: boolean) {\r\n // panel-owned.\r\n }\r\n\r\n get dragOptionButton(): HTMLElement | null {\r\n return this._panel?.dragOptionButton ?? null;\r\n }\r\n get dragOptionText(): Element | null {\r\n return this._panel?.dragOptionText ?? null;\r\n }\r\n get resizeOptionButton(): HTMLElement | null {\r\n return this._panel?.resizeOptionButton ?? null;\r\n }\r\n get resizeOptionText(): Element | null {\r\n return this._panel?.resizeOptionText ?? null;\r\n }\r\n\r\n constructor(player: Player) {\r\n this.player = player;\r\n this.transcriptWindow = null;\r\n this.transcriptEntries = [];\r\n this.metadataCues = [];\r\n this.currentActiveEntry = null;\r\n this.isVisible = false;\r\n \r\n // Storage manager\r\n this.storage = new StorageManager('vidply');\r\n \r\n // Draggable/Resizable utility\r\n this.draggableResizable = null;\r\n this.settingsButton = null;\r\n\r\n // Resize mode state\r\n this.transcriptResizeHandles = [];\r\n this.liveRegion = null;\r\n \r\n // Style dialog state\r\n this.styleDialog = null;\r\n this.styleDialogVisible = false;\r\n this.styleDialogJustOpened = false;\r\n \r\n // Language selector state\r\n this.languageSelector = null;\r\n this.languageLabel = null;\r\n this.currentTranscriptLanguage = null;\r\n this.availableTranscriptLanguages = [];\r\n this.languageSelectorHandler = null;\r\n \r\n const savedPreferences = this.storage.getTranscriptPreferences();\r\n \r\n this.autoscrollEnabled = typeof savedPreferences?.autoscroll === 'boolean' ? savedPreferences.autoscroll : true;\r\n \r\n this.showTimestamps = typeof savedPreferences?.showTimestamps === 'boolean' ? savedPreferences.showTimestamps : false;\r\n \r\n const savedFontSize = typeof savedPreferences?.fontSize === 'string' ? savedPreferences.fontSize : undefined;\r\n const savedFontFamily = typeof savedPreferences?.fontFamily === 'string' ? savedPreferences.fontFamily : undefined;\r\n const savedColor = typeof savedPreferences?.color === 'string' ? savedPreferences.color : undefined;\r\n const savedBackgroundColor = typeof savedPreferences?.backgroundColor === 'string' ? savedPreferences.backgroundColor : undefined;\r\n const savedOpacity = typeof savedPreferences?.opacity === 'number' ? savedPreferences.opacity : undefined;\r\n const optFontSize = typeof this.player.options.transcriptFontSize === 'string' ? this.player.options.transcriptFontSize : undefined;\r\n const optFontFamily = typeof this.player.options.transcriptFontFamily === 'string' ? this.player.options.transcriptFontFamily : undefined;\r\n const optColor = typeof this.player.options.transcriptColor === 'string' ? this.player.options.transcriptColor : undefined;\r\n const optBackgroundColor = typeof this.player.options.transcriptBackgroundColor === 'string' ? this.player.options.transcriptBackgroundColor : undefined;\r\n const optOpacity = typeof this.player.options.transcriptOpacity === 'number' ? this.player.options.transcriptOpacity : undefined;\r\n this.transcriptStyle = {\r\n fontSize: savedFontSize || optFontSize || '100%',\r\n fontFamily: savedFontFamily || optFontFamily || 'sans-serif',\r\n color: savedColor || optColor || '#ffffff',\r\n backgroundColor: savedBackgroundColor || optBackgroundColor || '#1e1e1e',\r\n opacity: savedOpacity ?? optOpacity ?? 0.98\r\n };\r\n \r\n // Store event handlers for cleanup\r\n this.handlers = {\r\n timeupdate: () => this.updateActiveEntry(),\r\n seeked: () => this.updateActiveEntry(),\r\n audiodescriptionenabled: () => {\r\n if (this.isVisible) {\r\n this.loadTranscriptData();\r\n }\r\n },\r\n audiodescriptiondisabled: () => {\r\n if (this.isVisible) {\r\n this.loadTranscriptData();\r\n }\r\n },\r\n textcuesupdate: null,\r\n resize: null,\r\n settingsClick: null,\r\n settingsKeydown: null,\r\n documentClick: null,\r\n styleDialogKeydown: null,\r\n floatingchange: null\r\n };\r\n\r\n this._cueUpdateTimeout = null;\r\n this._dashActiveLang = null;\r\n this._vttCache = new Map<string, TranscriptCueItem[]>();\r\n \r\n // Timeout management (for cleanup)\r\n this.timeouts = new Set();\r\n \r\n this.init();\r\n }\r\n\r\n init() {\r\n // Set up metadata handling immediately (independent of transcript display)\r\n this.setupMetadataHandlingOnLoad();\r\n \r\n // Listen for time updates to highlight active transcript entry\r\n this.player.on('timeupdate', this.handlers.timeupdate);\r\n this.player.on('seeked', this.handlers.seeked);\r\n \r\n // Listen for audio description changes to reload transcript\r\n this.player.on('audiodescriptionenabled', this.handlers.audiodescriptionenabled);\r\n this.player.on('audiodescriptiondisabled', this.handlers.audiodescriptiondisabled);\r\n\r\n // Listen for text cue updates (cues arrive incrementally as segments load in HLS/DASH).\r\n // Skip re-render when we already fetched the full VTT for the active language.\r\n this.handlers.textcuesupdate = () => {\r\n if (!this.isVisible) return;\r\n if (this.currentTranscriptLanguage && this._vttCache.has(this.currentTranscriptLanguage)) return;\r\n if (this._cueUpdateTimeout) {\r\n this.clearManagedTimeout(this._cueUpdateTimeout);\r\n }\r\n this._cueUpdateTimeout = this.setManagedTimeout(() => {\r\n this._cueUpdateTimeout = null;\r\n this.loadTranscriptData();\r\n }, 400);\r\n };\r\n this.player.on('textcuesupdate', this.handlers.textcuesupdate);\r\n \r\n // Reposition transcript when entering/exiting fullscreen\r\n this.player.on('fullscreenchange', () => {\r\n if (this.isVisible) {\r\n // Re-setup drag/drop when entering/exiting fullscreen on mobile devices\r\n // This enables drag/resize when entering fullscreen on mobile\r\n const isMobile = window.innerWidth < 768;\r\n if (isMobile) {\r\n this.setupDragAndDrop();\r\n }\r\n \r\n // Only auto-position if user hasn't manually positioned it\r\n if (!this.draggableResizable || !this.draggableResizable.manuallyPositioned) {\r\n // Add a small delay to ensure DOM has updated after fullscreen transition\r\n this.setManagedTimeout(() => this.positionTranscript(), 100);\r\n }\r\n }\r\n });\r\n\r\n // Collapse the transcript whenever the player enters the floating\r\n // shell. The floating miniplayer is intentionally narrow and only\r\n // shows a whitelisted subset of controls, so a full-size transcript\r\n // popup would either overflow the shell or travel with it and break\r\n // the miniplayer UX. We redock the transcript on exit by simply\r\n // letting the user re-open it if desired.\r\n this.handlers.floatingchange = (state) => {\r\n if (state && this.isVisible) {\r\n this.hideTranscript();\r\n }\r\n };\r\n this.player.on('floatingchange', this.handlers.floatingchange);\r\n }\r\n\r\n /**\r\n * For streaming renderers (DASH), tell the renderer to activate the text\r\n * track for `lang` so dash.js starts downloading subtitle segments and\r\n * populating cues. Skips the call if the language is already active.\r\n */\r\n private _requestStreamingTrack(lang: string | null) {\r\n if (!lang) return;\r\n const renderer = this.player.renderer as Renderer | null | undefined;\r\n if (renderer?.isStreaming && typeof renderer.activateTextTrackForLanguage === 'function') {\r\n if (this._dashActiveLang !== lang) {\r\n this._dashActiveLang = lang;\r\n renderer.activateTextTrackForLanguage(lang);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Toggle transcript window visibility\r\n */\r\n toggleTranscript() {\r\n if (this.isVisible) {\r\n this.hideTranscript();\r\n } else {\r\n this.showTranscript();\r\n }\r\n }\r\n\r\n /**\r\n * Show transcript window\r\n */\r\n showTranscript() {\r\n // The floating miniplayer hides the transcript control button and is\r\n // too narrow to host the transcript window, so refuse to open while\r\n // floating. Covers both the control-bar click (already gated by CSS)\r\n // and the keyboard shortcut / public API paths.\r\n if (this.player.state?.floating) {\r\n return;\r\n }\r\n\r\n // Always invalidate track cache to get fresh HLS subtitle tracks\r\n this.player.invalidateTrackCache();\r\n \r\n if (this.transcriptWindow) {\r\n this.transcriptWindow.style.display = 'flex';\r\n this.isVisible = true;\r\n \r\n // Reload transcript data to get fresh HLS tracks\r\n this.loadTranscriptData();\r\n this._requestStreamingTrack(this.currentTranscriptLanguage);\r\n this.updateLanguageSelector();\r\n\r\n if (this.player.controlBar && typeof this.player.controlBar.updateTranscriptButton === 'function') {\r\n this.player.controlBar.updateTranscriptButton();\r\n }\r\n \r\n // Focus the settings button for keyboard accessibility\r\n focusElement(this.settingsButton, { delay: 150 });\r\n return;\r\n }\r\n\r\n this.createTranscriptWindow();\r\n this.loadTranscriptData();\r\n this._requestStreamingTrack(this.currentTranscriptLanguage);\r\n \r\n const transcriptWindow = this.transcriptWindow as HTMLElement | null;\r\n if (transcriptWindow) {\r\n transcriptWindow.style.display = 'flex';\r\n \r\n if (!this.draggableResizable || !this.draggableResizable.manuallyPositioned) {\r\n this.setManagedTimeout(() => this.positionTranscript(), 0);\r\n }\r\n \r\n focusElement(this.settingsButton, { delay: 150 });\r\n }\r\n this.isVisible = true;\r\n }\r\n\r\n /**\r\n * Hide transcript window\r\n */\r\n hideTranscript({ focusButton = false } = {}) {\r\n if (this.transcriptWindow) {\r\n this.transcriptWindow.style.display = 'none';\r\n this.isVisible = false;\r\n }\r\n if (this.draggableResizable && this.draggableResizable.pointerResizeMode) {\r\n this.draggableResizable.disablePointerResizeMode();\r\n this.updateResizeOptionState();\r\n }\r\n this.hideResizeModeIndicator();\r\n this.announceLive('');\r\n\r\n // Update transcript button state in control bar\r\n if (this.player.controlBar && typeof this.player.controlBar.updateTranscriptButton === 'function') {\r\n this.player.controlBar.updateTranscriptButton();\r\n }\r\n\r\n if (focusButton) {\r\n const transcriptButton = this.player.controlBar?.controls?.transcript;\r\n if (transcriptButton && typeof transcriptButton.focus === 'function') {\r\n transcriptButton.focus({ preventScroll: true });\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Create the transcript window UI\r\n */\r\n createTranscriptWindow() {\r\n this.transcriptWindow = DOMUtils.createElement('div', {\r\n className: `${this.player.options.classPrefix}-transcript-window`,\r\n attributes: {\r\n 'role': 'dialog',\r\n 'aria-label': i18n.t('transcript.ariaLabel'),\r\n 'tabindex': '-1'\r\n }\r\n });\r\n\r\n // Header (draggable)\r\n this.transcriptHeader = DOMUtils.createElement('div', {\r\n className: `${this.player.options.classPrefix}-transcript-header`,\r\n attributes: {\r\n 'tabindex': '0'\r\n }\r\n });\r\n\r\n // Header left side (settings button + title)\r\n this.headerLeft = DOMUtils.createElement('div', {\r\n className: `${this.player.options.classPrefix}-transcript-header-left`\r\n });\r\n\r\n // Settings button\r\n const settingsAriaLabel = i18n.t('transcript.settingsMenu');\r\n this.settingsButton = DOMUtils.createElement('button', {\r\n className: `${this.player.options.classPrefix}-transcript-settings`,\r\n attributes: {\r\n 'type': 'button',\r\n 'aria-label': settingsAriaLabel,\r\n 'aria-expanded': 'false'\r\n }\r\n });\r\n this.settingsButton.appendChild(createIconElement('settings'));\r\n DOMUtils.attachTooltip(this.settingsButton, settingsAriaLabel, this.player.options.classPrefix);\r\n\r\n // The panel owns the settings-menu DOM, lifecycle, and the drag\r\n // + resize toggle items. Transcript-specific extras (style dialog\r\n // opener and timestamps toggle) are appended via the extras\r\n // builder below. The manager keeps ownership of the toggle\r\n // semantics (announcements, focus target), which the panel\r\n // invokes via the click callbacks.\r\n this._panel = new DraggablePanel({\r\n player: this.player,\r\n namespace: 'transcript',\r\n settingsButton: this.settingsButton,\r\n getDraggable: () => this.draggableResizable,\r\n i18nKeys: {\r\n enableDrag: 'transcript.enableDragMode',\r\n disableDrag: 'transcript.disableDragMode',\r\n enableDragAria: 'transcript.enableDragModeAria',\r\n disableDragAria: 'transcript.disableDragModeAria',\r\n enableResize: 'transcript.enableResizeMode',\r\n disableResize: 'transcript.disableResizeMode',\r\n enableResizeAria: 'transcript.enableResizeModeAria',\r\n disableResizeAria: 'transcript.disableResizeModeAria',\r\n closeMenu: 'transcript.closeMenu',\r\n },\r\n menuAlign: 'left',\r\n getMenuParent: () =>\r\n this.headerLeft ?? this.transcriptHeader ?? this.transcriptWindow,\r\n // The transcript window itself is the anchor for the mode badge\r\n // so it sits above the panel regardless of where the settings\r\n // menu was opened from. The default class name is derived from\r\n // the namespace → `{classPrefix}-transcript-mode-badge`.\r\n getBadgeHost: () => this.transcriptWindow,\r\n onDragItemClick: (panel) => {\r\n this.toggleKeyboardDragMode();\r\n panel.hide();\r\n },\r\n onResizeItemClick: (panel) => {\r\n const enabled = this.toggleResizeMode({ focus: false });\r\n if (enabled) {\r\n panel.hide({ focusButton: false });\r\n setTimeout(() => {\r\n this.transcriptWindow?.focus({ preventScroll: true });\r\n }, 20);\r\n } else {\r\n panel.hide({ focusButton: true });\r\n }\r\n },\r\n buildExtraItems: ({ menu, itemClass, classPrefix, stripInlineTooltip }) => {\r\n const styleOption = createMenuItem({\r\n classPrefix,\r\n itemClass,\r\n icon: 'settings',\r\n label: 'transcript.styleTranscript',\r\n onClick: (e: Event) => {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n this._panel?.hide();\r\n setTimeout(() => {\r\n this.showStyleDialog();\r\n }, 50);\r\n },\r\n });\r\n stripInlineTooltip(styleOption);\r\n menu.appendChild(styleOption);\r\n\r\n const timestampsOption = createMenuItem({\r\n classPrefix,\r\n itemClass,\r\n icon: 'clock',\r\n label: 'transcript.showTimestamps',\r\n hasTextClass: true,\r\n onClick: () => {\r\n this.toggleShowTimestamps();\r\n },\r\n });\r\n timestampsOption.setAttribute('role', 'switch');\r\n timestampsOption.setAttribute(\r\n 'aria-checked',\r\n this.showTimestamps ? 'true' : 'false'\r\n );\r\n stripInlineTooltip(timestampsOption);\r\n this.showTimestampsButton = timestampsOption;\r\n this.showTimestampsText = timestampsOption.querySelector(\r\n `.${classPrefix}-settings-text`\r\n );\r\n this.updateShowTimestampsState();\r\n menu.appendChild(timestampsOption);\r\n },\r\n });\r\n\r\n this.handlers.settingsClick = (e: MouseEvent) => {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n this._panel?.markJustOpenedForClick();\r\n this._panel?.toggle();\r\n };\r\n this.settingsButton.addEventListener('click', this.handlers.settingsClick);\r\n \r\n // Keyboard handler for settings button\r\n this.handlers.settingsKeydown = (e: KeyboardEvent) => {\r\n // D key to toggle keyboard drag mode\r\n if (e.key === 'd' || e.key === 'D') {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n this.toggleKeyboardDragMode();\r\n }\r\n // R key to toggle resize mode\r\n else if (e.key === 'r' || e.key === 'R') {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n this.toggleResizeMode();\r\n }\r\n // Escape to close menu if open\r\n else if (e.key === 'Escape' && this.settingsMenuVisible) {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n this.hideSettingsMenu();\r\n }\r\n };\r\n this.settingsButton.addEventListener('keydown', this.handlers.settingsKeydown);\r\n\r\n const title = DOMUtils.createElement('h3', {\r\n textContent: `${i18n.t('transcript.title')}. ${i18n.t('transcript.dragResizePrompt')}`\r\n });\r\n\r\n // Autoscroll checkbox\r\n const autoscrollId = `${this.player.options.classPrefix}-transcript-autoscroll-${Date.now()}`;\r\n \r\n const autoscrollLabel = DOMUtils.createElement('label', {\r\n className: `${this.player.options.classPrefix}-transcript-autoscroll-label`,\r\n attributes: {\r\n 'for': autoscrollId\r\n }\r\n });\r\n \r\n this.autoscrollCheckbox = DOMUtils.createElement('input', {\r\n attributes: {\r\n 'id': autoscrollId,\r\n 'type': 'checkbox'\r\n }\r\n });\r\n // Set checked property directly (boolean attribute, not \"true\" string)\r\n if (this.autoscrollEnabled) {\r\n this.autoscrollCheckbox.checked = true;\r\n }\r\n \r\n const autoscrollText = DOMUtils.createElement('span', {\r\n textContent: i18n.t('transcript.autoscroll'),\r\n className: `${this.player.options.classPrefix}-transcript-autoscroll-text`\r\n });\r\n \r\n autoscrollLabel.appendChild(this.autoscrollCheckbox);\r\n autoscrollLabel.appendChild(autoscrollText);\r\n \r\n // Handle autoscroll checkbox change\r\n this.autoscrollCheckbox.addEventListener('change', (e: Event) => {\r\n this.autoscrollEnabled = (e.target as HTMLInputElement).checked;\r\n this.saveAutoscrollPreference();\r\n });\r\n\r\n preventDragOnElement(autoscrollLabel);\r\n\r\n this.transcriptHeader.appendChild(title);\r\n this.headerLeft.appendChild(this.settingsButton);\r\n this.headerLeft.appendChild(autoscrollLabel);\r\n \r\n // Language selector (will be populated after tracks are loaded)\r\n const selectId = `${this.player.options.classPrefix}-transcript-language-select-${Date.now()}`;\r\n const { label: languageLabel, select: languageSelector } = createLabeledSelect({\r\n classPrefix: this.player.options.classPrefix,\r\n labelClass: `${this.player.options.classPrefix}-transcript-language-label`,\r\n selectClass: `${this.player.options.classPrefix}-transcript-language-select`,\r\n labelText: 'settings.language',\r\n selectId: selectId,\r\n hidden: false // Don't hide individual elements, we'll hide the wrapper instead\r\n });\r\n \r\n this.languageLabel = languageLabel;\r\n this.languageSelector = languageSelector;\r\n \r\n const languageSelectorWrapper = DOMUtils.createElement('div', {\r\n className: `${this.player.options.classPrefix}-transcript-language-wrapper`,\r\n attributes: {\r\n 'style': 'display: none;'\r\n }\r\n });\r\n languageSelectorWrapper.appendChild(languageLabel);\r\n languageSelectorWrapper.appendChild(languageSelector);\r\n this.languageSelectorWrapper = languageSelectorWrapper;\r\n \r\n preventDragOnElement(languageSelectorWrapper);\r\n \r\n if (this.headerLeft) {\r\n this.headerLeft.appendChild(languageSelectorWrapper);\r\n }\r\n\r\n const closeAriaLabel = i18n.t('transcript.close');\r\n const closeButton = DOMUtils.createElement('button', {\r\n className: `${this.player.options.classPrefix}-transcript-close`,\r\n attributes: {\r\n 'type': 'button',\r\n 'aria-label': closeAriaLabel\r\n }\r\n });\r\n closeButton.appendChild(createIconElement('close'));\r\n DOMUtils.attachTooltip(closeButton, closeAriaLabel, this.player.options.classPrefix);\r\n closeButton.addEventListener('click', () => this.hideTranscript({ focusButton: true }));\r\n\r\n this.transcriptHeader.appendChild(this.headerLeft);\r\n this.transcriptHeader.appendChild(closeButton);\r\n\r\n // Content container\r\n this.transcriptContent = DOMUtils.createElement('div', {\r\n className: `${this.player.options.classPrefix}-transcript-content`\r\n });\r\n\r\n this.transcriptWindow.appendChild(this.transcriptHeader);\r\n this.transcriptWindow.appendChild(this.transcriptContent);\r\n\r\n this.createResizeHandles();\r\n\r\n // Live region for announcements (screen reader feedback)\r\n this.liveRegion = DOMUtils.createElement('div', {\r\n className: 'vidply-sr-only',\r\n attributes: {\r\n 'aria-live': 'polite',\r\n 'aria-atomic': 'true'\r\n }\r\n });\r\n this.transcriptWindow.appendChild(this.liveRegion);\r\n\r\n // Append to player container\r\n this.player.container.appendChild(this.transcriptWindow);\r\n \r\n // Setup drag functionality FIRST (this will restore saved position if it exists)\r\n this.setupDragAndDrop();\r\n \r\n // Then position it next to the video wrapper ONLY if user hasn't manually positioned it\r\n // This ensures we don't overwrite saved positions from localStorage\r\n if (!this.draggableResizable || !this.draggableResizable.manuallyPositioned) {\r\n this.positionTranscript();\r\n }\r\n \r\n // Outside-click dismissal for the *style dialog* only. The\r\n // settings-menu equivalent is owned by the DraggablePanel, which\r\n // attaches its own listener (tied to the player's lifecycle\r\n // signal) when the menu is first opened.\r\n this.handlers.documentClick = (e: MouseEvent) => {\r\n if (this.styleDialogJustOpened) {\r\n return;\r\n }\r\n const target = e.target as Node | null;\r\n if (this.styleDialogVisible && this.styleDialog &&\r\n target && !this.styleDialog.contains(target)) {\r\n this.hideStyleDialog();\r\n }\r\n };\r\n this.documentClickHandlerAdded = false;\r\n \r\n // Re-position on window resize (debounced) - but only if not manually positioned\r\n let resizeTimeout: ReturnType<typeof setTimeout> | null = null;\r\n this.handlers.resize = () => {\r\n if (resizeTimeout) {\r\n this.clearManagedTimeout(resizeTimeout);\r\n }\r\n resizeTimeout = this.setManagedTimeout(() => {\r\n // Only auto-position if user hasn't manually moved it\r\n if (!this.draggableResizable || !this.draggableResizable.manuallyPositioned) {\r\n this.positionTranscript();\r\n }\r\n }, 100);\r\n };\r\n window.addEventListener('resize', this.handlers.resize, {\r\n signal: this.player.lifecycleSignal\r\n });\r\n }\r\n \r\n createResizeHandles() {\r\n const transcriptWindow = this.transcriptWindow;\r\n if (!transcriptWindow) return;\r\n\r\n const directions: Array<'n' | 's' | 'e' | 'w' | 'ne' | 'nw' | 'se' | 'sw'> = ['n', 's', 'e', 'w', 'ne', 'nw', 'se', 'sw'];\r\n this.transcriptResizeHandles = directions.map(direction => {\r\n const handle = DOMUtils.createElement('div', {\r\n className: `${this.player.options.classPrefix}-transcript-resize-handle ${this.player.options.classPrefix}-transcript-resize-${direction}`,\r\n attributes: {\r\n 'data-direction': direction,\r\n 'data-vidply-managed-resize': 'true',\r\n 'aria-hidden': 'true'\r\n }\r\n });\r\n\r\n handle.style.display = 'none';\r\n transcriptWindow.appendChild(handle);\r\n return handle;\r\n });\r\n }\r\n\r\n /**\r\n * Position transcript window next to video\r\n */\r\n positionTranscript() {\r\n const playerWithVideoWrapper = this.player as Player & { videoWrapper?: HTMLElement | null };\r\n if (!this.transcriptWindow || !playerWithVideoWrapper.videoWrapper || !this.isVisible) return;\r\n \r\n // Don't auto-position if user has manually positioned it\r\n if (this.draggableResizable && this.draggableResizable.manuallyPositioned) {\r\n return;\r\n }\r\n \r\n const isMobile = window.innerWidth < 768;\r\n const videoRect = playerWithVideoWrapper.videoWrapper.getBoundingClientRect();\r\n \r\n // Check if player is in fullscreen mode\r\n const isFullscreen = this.player.state.fullscreen;\r\n \r\n if (isMobile && !isFullscreen) {\r\n // Mobile: Position directly underneath the video controls as part of the layout\r\n this.transcriptWindow.style.position = 'relative';\r\n this.transcriptWindow.style.left = '0';\r\n this.transcriptWindow.style.right = '0';\r\n this.transcriptWindow.style.bottom = 'auto';\r\n this.transcriptWindow.style.top = 'auto';\r\n this.transcriptWindow.style.width = '100%';\r\n this.transcriptWindow.style.maxWidth = '100%';\r\n this.transcriptWindow.style.maxHeight = '300px';\r\n this.transcriptWindow.style.height = 'auto';\r\n this.transcriptWindow.style.borderRadius = '0';\r\n this.transcriptWindow.style.transform = 'none';\r\n this.transcriptWindow.style.border = 'none';\r\n this.transcriptWindow.style.borderTop = '1px solid var(--vidply-border-light)';\r\n // Remove any empty border properties that might have been set\r\n this.transcriptWindow.style.removeProperty('border-right');\r\n this.transcriptWindow.style.removeProperty('border-bottom');\r\n this.transcriptWindow.style.removeProperty('border-left');\r\n // Remove border-image properties that can cause parse errors\r\n this.transcriptWindow.style.removeProperty('border-image');\r\n this.transcriptWindow.style.removeProperty('border-image-source');\r\n this.transcriptWindow.style.removeProperty('border-image-slice');\r\n this.transcriptWindow.style.removeProperty('border-image-width');\r\n this.transcriptWindow.style.removeProperty('border-image-outset');\r\n this.transcriptWindow.style.removeProperty('border-image-repeat');\r\n this.transcriptWindow.style.boxShadow = 'none';\r\n // Disable dragging on mobile\r\n if (this.transcriptHeader) {\r\n this.transcriptHeader.style.cursor = 'default';\r\n }\r\n \r\n // Insert directly after video wrapper to appear right under controls\r\n const videoWrapper = playerWithVideoWrapper.videoWrapper;\r\n if (videoWrapper && videoWrapper.parentNode && videoWrapper.nextSibling !== this.transcriptWindow) {\r\n videoWrapper.parentNode.insertBefore(this.transcriptWindow, videoWrapper.nextSibling);\r\n }\r\n } else if (isFullscreen) {\r\n // In fullscreen: position in bottom right corner inside the video\r\n this.transcriptWindow.style.position = 'fixed';\r\n this.transcriptWindow.style.left = 'auto';\r\n this.transcriptWindow.style.right = '20px';\r\n this.transcriptWindow.style.bottom = '80px'; // Above controls\r\n this.transcriptWindow.style.top = 'auto';\r\n this.transcriptWindow.style.maxHeight = 'calc(100vh - 180px)'; // Leave space for controls\r\n this.transcriptWindow.style.height = 'auto';\r\n const fullscreenMinWidth = 260;\r\n const fullscreenAvailable = Math.max(fullscreenMinWidth, window.innerWidth - 40);\r\n const fullscreenDesired = parseFloat(this.transcriptWindow.style.width) || 400;\r\n const fullscreenWidth = Math.max(fullscreenMinWidth, Math.min(fullscreenDesired, fullscreenAvailable));\r\n this.transcriptWindow.style.width = `${fullscreenWidth}px`;\r\n this.transcriptWindow.style.maxWidth = 'none';\r\n this.transcriptWindow.style.borderRadius = '8px';\r\n this.transcriptWindow.style.border = '1px solid var(--vidply-border)';\r\n // Remove borderTop and any other individual border properties to avoid empty values\r\n this.transcriptWindow.style.removeProperty('border-top');\r\n this.transcriptWindow.style.removeProperty('border-right');\r\n this.transcriptWindow.style.removeProperty('border-bottom');\r\n this.transcriptWindow.style.removeProperty('border-left');\r\n // Remove border-image properties that can cause parse errors\r\n this.transcriptWindow.style.removeProperty('border-image');\r\n this.transcriptWindow.style.removeProperty('border-image-source');\r\n this.transcriptWindow.style.removeProperty('border-image-slice');\r\n this.transcriptWindow.style.removeProperty('border-image-width');\r\n this.transcriptWindow.style.removeProperty('border-image-outset');\r\n this.transcriptWindow.style.removeProperty('border-image-repeat');\r\n // Enable dragging in fullscreen (including touch devices)\r\n if (this.transcriptHeader) {\r\n this.transcriptHeader.style.cursor = 'move';\r\n }\r\n \r\n // Move back to container for fullscreen\r\n if (this.transcriptWindow.parentNode !== this.player.container) {\r\n this.player.container.appendChild(this.transcriptWindow);\r\n }\r\n } else {\r\n // Desktop mode: position to the right of video, or overlay if insufficient space\r\n const transcriptWidth = parseFloat(this.transcriptWindow.style.width) || 400;\r\n const padding = 20;\r\n const minWidth = 260;\r\n const containerRect = this.player.container.getBoundingClientRect();\r\n\r\n const ensureContainerPositioned = () => {\r\n const computed = window.getComputedStyle(this.player.container);\r\n if (computed.position === 'static') {\r\n this.player.container.style.position = 'relative';\r\n }\r\n };\r\n\r\n ensureContainerPositioned();\r\n\r\n // Calculate available space to the right of the video\r\n // WCAG 1.4.10 Reflow: Avoid horizontal overflow and content being cut off\r\n const availableWidth = window.innerWidth - videoRect.right - padding;\r\n \r\n // Switch to overlay mode when:\r\n // 1. Available width is less than the desired transcript width (would cause squeezing/cutoff)\r\n // 2. OR available width is below minimum usable width\r\n // This triggers earlier - as soon as the transcript would be squeezed\r\n const wouldBeCutOff = availableWidth < transcriptWidth;\r\n const hasMinimumSpace = availableWidth >= minWidth;\r\n const useOverlay = wouldBeCutOff || !hasMinimumSpace;\r\n\r\n if (!useOverlay) {\r\n // Position to the right of the video (original behavior)\r\n const left = (videoRect.right - containerRect.left) + padding;\r\n const appliedWidth = Math.max(minWidth, Math.min(transcriptWidth, availableWidth));\r\n const appliedHeight = videoRect.height;\r\n\r\n this.transcriptWindow.style.position = 'absolute';\r\n this.transcriptWindow.style.left = `${left}px`;\r\n this.transcriptWindow.style.right = 'auto';\r\n this.transcriptWindow.style.bottom = 'auto';\r\n this.transcriptWindow.style.top = '0';\r\n this.transcriptWindow.style.height = `${appliedHeight}px`;\r\n this.transcriptWindow.style.maxHeight = 'none';\r\n this.transcriptWindow.style.width = `${appliedWidth}px`;\r\n this.transcriptWindow.style.maxWidth = 'none';\r\n this.transcriptWindow.style.boxShadow = '';\r\n } else {\r\n // Insufficient space: fall back to compact overlay mode inside video area\r\n // Positioned above controls to prevent horizontal overflow\r\n const overlayMaxWidth = 320;\r\n const overlayMaxHeight = 280;\r\n const overlayWidth = Math.min(overlayMaxWidth, videoRect.width - 40);\r\n const overlayHeight = Math.min(overlayMaxHeight, videoRect.height - 120);\r\n \r\n // Calculate position relative to container, placing overlay inside video area\r\n const videoWrapperRect = playerWithVideoWrapper.videoWrapper.getBoundingClientRect();\r\n const controlsHeight = 70; // Approximate height of controls\r\n \r\n // Position from top of container to bottom of video minus controls and overlay height\r\n const overlayActualHeight = Math.max(180, overlayHeight);\r\n const topPosition = (videoWrapperRect.bottom - containerRect.top) - controlsHeight - overlayActualHeight;\r\n const rightPosition = (containerRect.right - videoWrapperRect.right) + 12;\r\n\r\n this.transcriptWindow.style.position = 'absolute';\r\n this.transcriptWindow.style.left = 'auto';\r\n this.transcriptWindow.style.right = `${rightPosition}px`;\r\n this.transcriptWindow.style.top = `${topPosition}px`;\r\n this.transcriptWindow.style.bottom = 'auto';\r\n this.transcriptWindow.style.width = `${Math.max(minWidth, overlayWidth)}px`;\r\n this.transcriptWindow.style.maxWidth = `${overlayMaxWidth}px`;\r\n this.transcriptWindow.style.height = `${overlayActualHeight}px`;\r\n this.transcriptWindow.style.maxHeight = `${overlayMaxHeight}px`;\r\n this.transcriptWindow.style.boxShadow = '0 4px 16px rgba(0, 0, 0, 0.6)'; // Enhanced shadow for overlay visibility\r\n }\r\n\r\n this.transcriptWindow.style.borderRadius = '8px';\r\n this.transcriptWindow.style.border = '1px solid var(--vidply-border)';\r\n // Remove borderTop and any other individual border properties to avoid empty values\r\n this.transcriptWindow.style.removeProperty('border-top');\r\n this.transcriptWindow.style.removeProperty('border-right');\r\n this.transcriptWindow.style.removeProperty('border-bottom');\r\n this.transcriptWindow.style.removeProperty('border-left');\r\n // Remove border-image properties that can cause parse errors\r\n this.transcriptWindow.style.removeProperty('border-image');\r\n this.transcriptWindow.style.removeProperty('border-image-source');\r\n this.transcriptWindow.style.removeProperty('border-image-slice');\r\n this.transcriptWindow.style.removeProperty('border-image-width');\r\n this.transcriptWindow.style.removeProperty('border-image-outset');\r\n this.transcriptWindow.style.removeProperty('border-image-repeat');\r\n // Enable dragging on desktop\r\n if (this.transcriptHeader) {\r\n this.transcriptHeader.style.cursor = 'move';\r\n }\r\n \r\n // Move back to container for desktop\r\n if (this.transcriptWindow.parentNode !== this.player.container) {\r\n this.player.container.appendChild(this.transcriptWindow);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Get available transcript languages from tracks\r\n */\r\n getAvailableTranscriptLanguages() {\r\n const textTracks = this.player.textTracks as TranscriptTrack[];\r\n const languages = new Map<string, TranscriptLanguageInfo>();\r\n \r\n // Collect all caption/subtitle tracks with their languages.\r\n // For DASH/HLS, tracks may have placeholder labels (AdaptationSet IDs,\r\n // \"null\", etc.); deriveTrackLabel normalises them to human-readable names.\r\n textTracks.forEach((track: TranscriptTrack) => {\r\n if ((track.kind === 'captions' || track.kind === 'subtitles') && !track._vidplyStale) {\r\n const lang = (track.language ?? '').trim();\r\n const label = deriveTrackLabel(track.label, track.language, 'player.captions');\r\n const key = lang || label;\r\n if (key && !languages.has(key)) {\r\n languages.set(key, {\r\n language: lang,\r\n label,\r\n track: track\r\n });\r\n }\r\n }\r\n });\r\n \r\n return Array.from(languages.values());\r\n }\r\n\r\n /**\r\n * Update language selector dropdown\r\n */\r\n updateLanguageSelector() {\r\n const languageSelector = this.languageSelector;\r\n if (!languageSelector) return;\r\n \r\n this.availableTranscriptLanguages = this.getAvailableTranscriptLanguages();\r\n \r\n languageSelector.innerHTML = '';\r\n \r\n if (this.availableTranscriptLanguages.length < 2) {\r\n if (this.languageSelectorWrapper) {\r\n this.languageSelectorWrapper.style.display = 'none';\r\n }\r\n return;\r\n }\r\n \r\n if (this.languageSelectorWrapper) {\r\n this.languageSelectorWrapper.style.display = 'flex';\r\n }\r\n \r\n this.availableTranscriptLanguages.forEach((langInfo: TranscriptLanguageInfo) => {\r\n const attrs: Record<string, string> = {\r\n 'value': langInfo.language || langInfo.label\r\n };\r\n if (langInfo.language) {\r\n attrs['lang'] = langInfo.language;\r\n }\r\n const option = DOMUtils.createElement('option', {\r\n textContent: langInfo.label,\r\n attributes: attrs\r\n });\r\n languageSelector.appendChild(option);\r\n });\r\n \r\n if (this.currentTranscriptLanguage) {\r\n languageSelector.value = this.currentTranscriptLanguage;\r\n } else if (this.availableTranscriptLanguages.length > 0) {\r\n const activeTrack = this.player.textTracks.find(\r\n (track: TranscriptTrack) => (track.kind === 'captions' || track.kind === 'subtitles') && track.mode === 'showing'\r\n );\r\n const firstLang = this.availableTranscriptLanguages[0];\r\n const fallbackLang = firstLang ? firstLang.language : null;\r\n this.currentTranscriptLanguage = activeTrack ? activeTrack.language : fallbackLang;\r\n if (this.currentTranscriptLanguage) {\r\n languageSelector.value = this.currentTranscriptLanguage;\r\n }\r\n }\r\n \r\n if (this.languageSelectorHandler) {\r\n languageSelector.removeEventListener('change', this.languageSelectorHandler);\r\n }\r\n \r\n // Handle language change — tell the streaming renderer to switch tracks\r\n // first so dash.js starts downloading cues for the new language, then\r\n // render whatever is available (textcuesupdate will re-render later).\r\n const handler = (e: Event) => {\r\n this.currentTranscriptLanguage = (e.target as HTMLSelectElement).value;\r\n this._requestStreamingTrack(this.currentTranscriptLanguage);\r\n this.loadTranscriptData();\r\n \r\n if (this.transcriptContent && this.currentTranscriptLanguage) {\r\n this.transcriptContent.setAttribute('lang', this.currentTranscriptLanguage);\r\n }\r\n };\r\n this.languageSelectorHandler = handler;\r\n languageSelector.addEventListener('change', handler);\r\n }\r\n\r\n private _parseVTT(vttText: string): TranscriptCueItem[] {\r\n const cues: TranscriptCueItem[] = [];\r\n const blocks = vttText.replace(/\\r\\n/g, '\\n').split(/\\n\\n+/);\r\n for (const block of blocks) {\r\n const lines = block.trim().split('\\n');\r\n let tsLine = -1;\r\n for (let i = 0; i < lines.length; i++) {\r\n if (lines[i]?.includes('-->')) { tsLine = i; break; }\r\n }\r\n if (tsLine < 0) continue;\r\n const match = lines[tsLine]?.match(\r\n /(\\d{1,2}:)?(\\d{2}):(\\d{2})\\.(\\d{3})\\s*-->\\s*(\\d{1,2}:)?(\\d{2}):(\\d{2})\\.(\\d{3})/\r\n );\r\n if (!match) continue;\r\n const startTime =\r\n (match[1] ? parseInt(match[1]) * 3600 : 0) +\r\n parseInt(match[2] ?? '0') * 60 +\r\n parseInt(match[3] ?? '0') +\r\n parseInt(match[4] ?? '0') / 1000;\r\n const endTime =\r\n (match[5] ? parseInt(match[5]) * 3600 : 0) +\r\n parseInt(match[6] ?? '0') * 60 +\r\n parseInt(match[7] ?? '0') +\r\n parseInt(match[8] ?? '0') / 1000;\r\n const text = lines.slice(tsLine + 1).join('\\n').trim();\r\n if (!text) continue;\r\n cues.push({\r\n cue: { startTime, endTime, text, id: '' } as unknown as TranscriptCue,\r\n type: 'caption'\r\n });\r\n }\r\n return cues;\r\n }\r\n\r\n private async _loadVttTranscript(lang: string): Promise<TranscriptCueItem[] | null> {\r\n const cached = this._vttCache.get(lang);\r\n if (cached) return cached;\r\n\r\n const renderer = this.player.renderer as Renderer | null | undefined;\r\n if (!renderer?.isStreaming || typeof renderer.getTextTrackURLs !== 'function') return null;\r\n\r\n const urls: { lang: string; url: string }[] = renderer.getTextTrackURLs();\r\n const entry = urls.find(u =>\r\n u.lang === lang || u.lang.startsWith(lang) || lang.startsWith(u.lang)\r\n );\r\n if (!entry) return null;\r\n\r\n // Build a combined AbortSignal: the player's lifecycle controller plus\r\n // a 10s timeout per individual fetch.\r\n const signal = this._buildFetchSignal(10_000);\r\n\r\n try {\r\n const res = await fetch(entry.url, { signal });\r\n if (!res.ok) return null;\r\n let text = await res.text();\r\n\r\n // HLS subtitle playlists are m3u8 files that reference the actual VTT.\r\n // Resolve the VTT URI and fetch it instead.\r\n if (text.trimStart().startsWith('#EXTM3U')) {\r\n const vttUri = text.split('\\n')\r\n .map(l => l.trim())\r\n .find(l => l && !l.startsWith('#'));\r\n if (!vttUri) return null;\r\n const baseUrl = entry.url.substring(0, entry.url.lastIndexOf('/') + 1);\r\n const vttUrl = vttUri.startsWith('http') ? vttUri : new URL(vttUri, baseUrl).href;\r\n const vttRes = await fetch(vttUrl, { signal: this._buildFetchSignal(10_000) });\r\n if (!vttRes.ok) return null;\r\n text = await vttRes.text();\r\n }\r\n\r\n const cues = this._parseVTT(text);\r\n if (cues.length > 0) this._vttCache.set(lang, cues);\r\n return cues;\r\n } catch {\r\n return null;\r\n }\r\n }\r\n\r\n /**\r\n * Build an AbortSignal that fires when either the player is destroyed\r\n * or `timeoutMs` elapses, whichever happens first.\r\n */\r\n private _buildFetchSignal(timeoutMs: number): AbortSignal | undefined {\r\n const signals: AbortSignal[] = [];\r\n const lifecycle = (this.player as { lifecycleSignal?: AbortSignal }).lifecycleSignal;\r\n if (lifecycle) signals.push(lifecycle);\r\n if (typeof AbortSignal !== 'undefined' && typeof AbortSignal.timeout === 'function') {\r\n signals.push(AbortSignal.timeout(timeoutMs));\r\n }\r\n if (signals.length === 0) return undefined;\r\n if (signals.length === 1) return signals[0];\r\n const anyFn = (AbortSignal as { any?: (sigs: AbortSignal[]) => AbortSignal }).any;\r\n return anyFn ? anyFn(signals) : signals[0];\r\n }\r\n\r\n /**\r\n * Load transcript data from caption/subtitle tracks\r\n */\r\n loadTranscriptData() {\r\n this.transcriptEntries = [];\r\n this.currentActiveEntry = null;\r\n if (this.transcriptContent) {\r\n this.transcriptContent.innerHTML = '';\r\n }\r\n\r\n // Get all text tracks\r\n const textTracks = this.player.textTracks as TranscriptTrack[];\r\n\r\n // Find track for selected language, or default to first available.\r\n // Safari native HLS can expose duplicate TextTrack objects for the same\r\n // language (SUBTITLES vs CLOSED-CAPTIONS); prefer the one with cues loaded.\r\n let captionTrack: TranscriptTrack | null = null;\r\n if (this.currentTranscriptLanguage) {\r\n const candidates = textTracks.filter(\r\n (track: TranscriptTrack) => (track.kind === 'captions' || track.kind === 'subtitles') && \r\n track.language === this.currentTranscriptLanguage &&\r\n !track._vidplyStale\r\n );\r\n captionTrack = candidates.find((t: TranscriptTrack) => t.cues && t.cues.length > 0) || candidates[0] || null;\r\n }\r\n \r\n if (!captionTrack) {\r\n const candidates = textTracks.filter(\r\n (track: TranscriptTrack) => (track.kind === 'captions' || track.kind === 'subtitles') && !track._vidplyStale\r\n );\r\n captionTrack = candidates.find((t: TranscriptTrack) => t.cues && t.cues.length > 0) || candidates[0] || null;\r\n if (captionTrack) {\r\n this.currentTranscriptLanguage = captionTrack.language;\r\n }\r\n }\r\n \r\n // Find description track matching the selected language\r\n let descriptionTrack: TranscriptTrack | null = null;\r\n if (this.currentTranscriptLanguage) {\r\n descriptionTrack = textTracks.find(\r\n (track: TranscriptTrack) => track.kind === 'descriptions' && track.language === this.currentTranscriptLanguage\r\n ) || null;\r\n }\r\n // Fallback to first available description track if no match found\r\n if (!descriptionTrack) {\r\n descriptionTrack = textTracks.find((track: TranscriptTrack) => track.kind === 'descriptions') || null;\r\n }\r\n \r\n const metadataTrack = textTracks.find((track: TranscriptTrack) => track.kind === 'metadata');\r\n\r\n // We need at least one track type available for display\r\n // (captions, descriptions, or metadata - though metadata is not displayed)\r\n if (!captionTrack && !descriptionTrack && !metadataTrack) {\r\n this.showNoTranscriptMessage();\r\n return;\r\n }\r\n\r\n // Enable all tracks to load cues so they're available for the transcript\r\n const tracksToLoad: TranscriptTrack[] = [captionTrack, descriptionTrack, metadataTrack].filter(\r\n (track): track is TranscriptTrack => Boolean(track)\r\n );\r\n tracksToLoad.forEach((track: TranscriptTrack) => {\r\n if (track.mode === 'disabled') {\r\n track.mode = 'hidden';\r\n }\r\n });\r\n\r\n // For streaming renderers, fetch the complete VTT file directly rather\r\n // than relying on dash.js's partial segment-based cues.\r\n const renderer = this.player.renderer as Renderer | null | undefined;\r\n const isStreaming = renderer?.isStreaming && typeof renderer.getTextTrackURLs === 'function';\r\n const lang = this.currentTranscriptLanguage || (captionTrack?.language ?? '');\r\n\r\n if (isStreaming && lang) {\r\n const loadingMessage = DOMUtils.createElement('div', {\r\n className: `${this.player.options.classPrefix}-transcript-loading`,\r\n textContent: i18n.t('transcript.loading')\r\n });\r\n this.transcriptContent?.appendChild(loadingMessage);\r\n\r\n this._loadVttTranscript(lang).then(vttCues => {\r\n if (!this.isVisible) return;\r\n this._renderTranscriptCues(\r\n vttCues && vttCues.length > 0 ? vttCues : null,\r\n captionTrack,\r\n descriptionTrack,\r\n metadataTrack\r\n );\r\n });\r\n return;\r\n }\r\n\r\n // Non-streaming path: wait for sidecar <track> elements to load cues.\r\n const primaryTrack = captionTrack;\r\n const primaryNeedsCues = primaryTrack && (!primaryTrack.cues || primaryTrack.cues.length === 0);\r\n\r\n if (primaryNeedsCues) {\r\n const loadingMessage = DOMUtils.createElement('div', {\r\n className: `${this.player.options.classPrefix}-transcript-loading`,\r\n textContent: i18n.t('transcript.loading')\r\n });\r\n this.transcriptContent?.appendChild(loadingMessage);\r\n\r\n const hasSidecarElement = this.player.findTrackElement?.(primaryTrack);\r\n if (hasSidecarElement) {\r\n primaryTrack.addEventListener('load', () => {\r\n this.loadTranscriptData();\r\n }, { once: true });\r\n this.setManagedTimeout(() => { this.loadTranscriptData(); }, 1000);\r\n } else {\r\n let attempts = 0;\r\n const poll = () => {\r\n attempts++;\r\n if (!this.isVisible) return;\r\n if (primaryTrack.cues && primaryTrack.cues.length > 0) {\r\n this.loadTranscriptData();\r\n return;\r\n }\r\n if (attempts < 40) {\r\n this.setManagedTimeout(poll, 500);\r\n }\r\n };\r\n this.setManagedTimeout(poll, 300);\r\n }\r\n return;\r\n }\r\n\r\n this._renderTranscriptCues(null, captionTrack, descriptionTrack, metadataTrack);\r\n }\r\n\r\n private _renderTranscriptCues(\r\n vttCues: TranscriptCueItem[] | null,\r\n captionTrack: TranscriptTrack | null,\r\n descriptionTrack: TranscriptTrack | null,\r\n metadataTrack: TranscriptTrack | undefined | null\r\n ) {\r\n this.transcriptEntries = [];\r\n this.currentActiveEntry = null;\r\n const transcriptContent = this.transcriptContent;\r\n if (transcriptContent) {\r\n transcriptContent.innerHTML = '';\r\n }\r\n\r\n const allCues: TranscriptCueItem[] = [];\r\n\r\n if (vttCues && vttCues.length > 0) {\r\n allCues.push(...vttCues);\r\n } else if (captionTrack && captionTrack.cues) {\r\n Array.from(captionTrack.cues).forEach((cue: TranscriptCue) => {\r\n allCues.push({ cue, type: 'caption' });\r\n });\r\n }\r\n \r\n if (descriptionTrack && descriptionTrack.cues) {\r\n Array.from(descriptionTrack.cues).forEach((cue: TranscriptCue) => {\r\n allCues.push({ cue, type: 'description' });\r\n });\r\n }\r\n \r\n if (metadataTrack && metadataTrack.cues) {\r\n this.metadataCues = Array.from(metadataTrack.cues) as TranscriptCue[];\r\n this.setupMetadataHandling();\r\n }\r\n\r\n allCues.sort((a, b) => a.cue.startTime - b.cue.startTime);\r\n\r\n allCues.forEach((item, index) => {\r\n const entry = this.createTranscriptEntry(item.cue, index, item.type);\r\n this.transcriptEntries.push({\r\n element: entry,\r\n cue: item.cue,\r\n type: item.type,\r\n startTime: item.cue.startTime,\r\n endTime: item.cue.endTime\r\n });\r\n transcriptContent?.appendChild(entry);\r\n });\r\n \r\n this.applyTranscriptStyles();\r\n this.updateTimestampVisibility();\r\n \r\n if (this.transcriptContent && this.currentTranscriptLanguage) {\r\n this.transcriptContent.setAttribute('lang', this.currentTranscriptLanguage);\r\n }\r\n \r\n this.updateLanguageSelector();\r\n this.updateActiveEntry();\r\n }\r\n\r\n /**\r\n * Setup metadata handling on player load\r\n * This runs independently of transcript loading\r\n */\r\n setupMetadataHandlingOnLoad() {\r\n // Wait for metadata to be loaded\r\n const setupMetadata = () => {\r\n const textTracks = this.player.textTracks;\r\n const metadataTrack = (textTracks as TranscriptTrack[]).find((track: TranscriptTrack) => track.kind === 'metadata');\r\n \r\n if (metadataTrack) {\r\n // Enable the metadata track so cuechange events fire\r\n // Use 'hidden' mode so it doesn't display anything, but events still work\r\n if (metadataTrack.mode === 'disabled') {\r\n metadataTrack.mode = 'hidden';\r\n }\r\n \r\n // Check if we already added the listener\r\n if (this.metadataCueChangeHandler) {\r\n metadataTrack.removeEventListener('cuechange', this.metadataCueChangeHandler);\r\n }\r\n \r\n // Add event listener for cue changes\r\n this.metadataCueChangeHandler = () => {\r\n const activeCues = Array.from(metadataTrack.activeCues || []) as TranscriptCue[];\r\n if (activeCues.length > 0) {\r\n // Debug logging (can be removed in production)\r\n if (this.player.options.debug) {\r\n console.log('[VidPly Metadata] Active cues:', activeCues.map((c) => ({\r\n start: c.startTime,\r\n end: c.endTime,\r\n text: (c as VTTCue).text\r\n })));\r\n }\r\n }\r\n activeCues.forEach((cue: TranscriptCue) => {\r\n this.handleMetadataCue(cue);\r\n });\r\n };\r\n \r\n metadataTrack.addEventListener('cuechange', this.metadataCueChangeHandler);\r\n \r\n // Debug: Log metadata track setup\r\n if (this.player.options.debug) {\r\n const cueCount = metadataTrack.cues ? metadataTrack.cues.length : 0;\r\n console.log('[VidPly Metadata] Track enabled,', cueCount, 'cues available');\r\n }\r\n } else if (this.player.options.debug) {\r\n console.warn('[VidPly Metadata] No metadata track found');\r\n }\r\n };\r\n \r\n // Try immediately\r\n setupMetadata();\r\n \r\n // Also try after loadedmetadata event\r\n this.player.on('loadedmetadata', setupMetadata);\r\n }\r\n\r\n /**\r\n * Setup metadata handling\r\n * Metadata cues are not displayed but can be used programmatically\r\n * This is called when transcript data is loaded (for storing cues)\r\n */\r\n setupMetadataHandling() {\r\n if (!this.metadataCues || this.metadataCues.length === 0) {\r\n return;\r\n }\r\n\r\n // The actual event handling is set up in setupMetadataHandlingOnLoad()\r\n // This method just stores the cues for reference\r\n if (this.player.options.debug) {\r\n console.log('[VidPly Metadata]', this.metadataCues.length, 'cues stored from transcript load');\r\n }\r\n }\r\n\r\n /**\r\n * Handle individual metadata cues\r\n * Parses metadata text and emits events or triggers actions\r\n */\r\n handleMetadataCue(cue: TranscriptCue) {\r\n const cueText = (cue as TextTrackCue & { text?: string }).text || '';\r\n const text = cueText.trim();\r\n \r\n // Debug logging\r\n if (this.player.options.debug) {\r\n console.log('[VidPly Metadata] Processing cue:', {\r\n time: cue.startTime,\r\n text: text\r\n });\r\n }\r\n \r\n // Emit a generic metadata event that developers can listen to\r\n this.player.emit('metadata', {\r\n time: cue.startTime,\r\n endTime: cue.endTime,\r\n text: text,\r\n cue: cue\r\n });\r\n\r\n // Parse for specific commands (examples based on wwa_meta.vtt format)\r\n if (text.includes('PAUSE')) {\r\n // Automatically pause the video\r\n if (!this.player.state.paused) {\r\n if (this.player.options.debug) {\r\n console.log('[VidPly Metadata] Pausing video at', cue.startTime);\r\n }\r\n this.player.pause();\r\n }\r\n // Also emit event for developers who want to listen\r\n this.player.emit('metadata:pause', { time: cue.startTime, text: text });\r\n }\r\n\r\n // Parse for focus directives\r\n const focusMatch = text.match(/FOCUS:([\\w#-]+)/);\r\n if (focusMatch && focusMatch[1]) {\r\n const targetSelector = focusMatch[1];\r\n // Automatically focus the target element\r\n const targetElement = document.querySelector(targetSelector) as HTMLElement | null;\r\n if (targetElement) {\r\n if (this.player.options.debug) {\r\n console.log('[VidPly Metadata] Focusing element:', targetSelector);\r\n }\r\n // Use setTimeout to ensure DOM is ready\r\n this.setManagedTimeout(() => {\r\n targetElement.focus({ preventScroll: true });\r\n }, 10);\r\n } else if (this.player.options.debug) {\r\n console.warn('[VidPly Metadata] Element not found:', targetSelector);\r\n }\r\n // Also emit event for developers who want to listen\r\n this.player.emit('metadata:focus', { \r\n time: cue.startTime, \r\n target: targetSelector,\r\n element: targetElement,\r\n text: text \r\n });\r\n }\r\n\r\n // Parse for hashtag references\r\n const hashtags = text.match(/#[\\w-]+/g);\r\n if (hashtags) {\r\n if (this.player.options.debug) {\r\n console.log('[VidPly Metadata] Hashtags found:', hashtags);\r\n }\r\n this.player.emit('metadata:hashtags', {\r\n time: cue.startTime,\r\n hashtags: hashtags,\r\n text: text\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Create a single transcript entry element\r\n */\r\n createTranscriptEntry(cue: TranscriptCue, index: number, type: 'caption' | 'description' = 'caption') {\r\n const entryText = this.stripVTTFormatting(((cue as TextTrackCue & { text?: string }).text) || '');\r\n\r\n // Use a real <button> so screen readers/AT report \"button\", Enter and\r\n // Space activate it natively, and forms-mode users can find it.\r\n const seekLabelTemplate = i18n.t('transcript.seekTo') || 'Seek to {time}';\r\n const ariaLabel = seekLabelTemplate\r\n .replace('{time}', TimeUtils.formatTime(cue.startTime))\r\n .replace('{text}', entryText);\r\n\r\n const entry = DOMUtils.createElement('button', {\r\n className: `${this.player.options.classPrefix}-transcript-entry ${this.player.options.classPrefix}-transcript-${type}`,\r\n attributes: {\r\n 'type': 'button',\r\n 'data-start': String(cue.startTime),\r\n 'data-end': String(cue.endTime),\r\n 'data-type': type,\r\n 'aria-label': `${ariaLabel} — ${entryText}`\r\n }\r\n });\r\n\r\n const timestamp = DOMUtils.createElement('span', {\r\n className: `${this.player.options.classPrefix}-transcript-time`,\r\n textContent: TimeUtils.formatTime(cue.startTime),\r\n attributes: {\r\n 'aria-hidden': 'true' // Hide from screen readers - decorative timestamp\r\n }\r\n });\r\n\r\n const text = DOMUtils.createElement('span', {\r\n className: `${this.player.options.classPrefix}-transcript-text`,\r\n textContent: entryText\r\n });\r\n\r\n entry.appendChild(timestamp);\r\n entry.appendChild(text);\r\n\r\n // Click to seek. <button> elements activate on Enter/Space natively,\r\n // so a custom keydown handler is no longer needed.\r\n const seekToTime = () => {\r\n this.player.seek(cue.startTime);\r\n if (this.player.state.paused) {\r\n this.player.play();\r\n }\r\n };\r\n\r\n entry.addEventListener('click', seekToTime);\r\n\r\n return entry;\r\n }\r\n\r\n /**\r\n * Strip VTT formatting tags from text\r\n */\r\n stripVTTFormatting(text: string) {\r\n // Remove VTT tags like <v Speaker>, <c>, etc.\r\n return text\r\n .replace(/<[^>]+>/g, '')\r\n .replace(/\\n/g, ' ')\r\n .trim();\r\n }\r\n\r\n /**\r\n * Show message when no transcript is available\r\n */\r\n showNoTranscriptMessage() {\r\n const message = DOMUtils.createElement('div', {\r\n className: `${this.player.options.classPrefix}-transcript-empty`,\r\n textContent: i18n.t('transcript.noTranscript')\r\n });\r\n this.transcriptContent?.appendChild(message);\r\n }\r\n\r\n /**\r\n * Update active transcript entry based on current time\r\n */\r\n updateActiveEntry() {\r\n if (!this.isVisible || this.transcriptEntries.length === 0) return;\r\n\r\n const currentTime = this.player.state.currentTime;\r\n \r\n // Find the entry that matches current time\r\n const activeEntry = this.transcriptEntries.find(\r\n (entry: TranscriptEntry) => currentTime >= entry.startTime && currentTime < entry.endTime\r\n );\r\n\r\n if (activeEntry && activeEntry !== this.currentActiveEntry) {\r\n // Remove previous active class\r\n if (this.currentActiveEntry) {\r\n this.currentActiveEntry.element.classList.remove(\r\n `${this.player.options.classPrefix}-transcript-entry-active`\r\n );\r\n }\r\n\r\n // Add active class to current entry\r\n activeEntry.element.classList.add(\r\n `${this.player.options.classPrefix}-transcript-entry-active`\r\n );\r\n \r\n // Scroll to active entry\r\n this.scrollToEntry(activeEntry.element);\r\n \r\n this.currentActiveEntry = activeEntry;\r\n } else if (!activeEntry && this.currentActiveEntry) {\r\n // No active entry, remove active class\r\n this.currentActiveEntry.element.classList.remove(\r\n `${this.player.options.classPrefix}-transcript-entry-active`\r\n );\r\n this.currentActiveEntry = null;\r\n }\r\n }\r\n\r\n /**\r\n * Scroll transcript window to show active entry\r\n */\r\n scrollToEntry(entryElement: HTMLElement) {\r\n if (!this.transcriptContent || !this.autoscrollEnabled) return;\r\n\r\n const contentRect = this.transcriptContent.getBoundingClientRect();\r\n const entryRect = entryElement.getBoundingClientRect();\r\n\r\n // Check if entry is out of view\r\n if (entryRect.top < contentRect.top || entryRect.bottom > contentRect.bottom) {\r\n // Scroll to center the entry\r\n const scrollTop = entryElement.offsetTop - (this.transcriptContent.clientHeight / 2) + (entryElement.clientHeight / 2);\r\n this.transcriptContent.scrollTo({\r\n top: scrollTop,\r\n behavior: 'smooth'\r\n });\r\n }\r\n }\r\n \r\n /**\r\n * Save autoscroll preference to localStorage\r\n */\r\n saveAutoscrollPreference() {\r\n const savedPreferences = this.storage.getTranscriptPreferences() || {};\r\n savedPreferences.autoscroll = this.autoscrollEnabled;\r\n this.storage.saveTranscriptPreferences(savedPreferences);\r\n }\r\n\r\n /**\r\n * Setup drag and drop functionality\r\n */\r\n setupDragAndDrop() {\r\n if (!this.transcriptHeader || !this.transcriptWindow) return;\r\n\r\n // Check if we're on mobile and not in fullscreen\r\n const isMobile = window.innerWidth < 768;\r\n const isFullscreen = this.player.state.fullscreen;\r\n \r\n // On mobile devices (< 768px), only enable drag/resize in fullscreen\r\n // On desktop/tablets (>= 768px), always enable drag/resize\r\n if (isMobile && !isFullscreen) {\r\n // Destroy existing instance if exiting fullscreen on mobile\r\n if (this.draggableResizable) {\r\n this.draggableResizable.destroy();\r\n this.draggableResizable = null;\r\n }\r\n return; // No drag/resize on mobile when not in fullscreen\r\n }\r\n\r\n // If already initialized, don't re-initialize\r\n if (this.draggableResizable) {\r\n return;\r\n }\r\n\r\n // Create DraggableResizable utility with touch support\r\n this.draggableResizable = new DraggableResizable(this.transcriptWindow, {\r\n dragHandle: this.transcriptHeader,\r\n resizeHandles: this.transcriptResizeHandles,\r\n constrainToViewport: true,\r\n classPrefix: `${this.player.options.classPrefix}-transcript`,\r\n keyboardDragKey: 'd',\r\n keyboardResizeKey: 'r',\r\n keyboardStep: 10,\r\n keyboardStepLarge: 50,\r\n minWidth: 300,\r\n minHeight: 200,\r\n maxWidth: () => Math.max(320, window.innerWidth - 40),\r\n maxHeight: () => Math.max(200, window.innerHeight - 120),\r\n pointerResizeIndicatorText: i18n.t('transcript.resizeModeHint'),\r\n onPointerResizeToggle: (enabled) => {\r\n // Update resize handles visibility\r\n this.transcriptResizeHandles.forEach((handle: HTMLElement) => {\r\n handle.style.display = enabled ? 'block' : 'none';\r\n });\r\n // Call the state change handler\r\n this.onPointerResizeModeChange(enabled);\r\n },\r\n onDragStart: (e: Event) => {\r\n // Don't drag if clicking on certain elements\r\n const ignoreSelectors = [\r\n `.${this.player.options.classPrefix}-transcript-close`,\r\n `.${this.player.options.classPrefix}-transcript-settings`,\r\n `.${this.player.options.classPrefix}-transcript-language-select`,\r\n `.${this.player.options.classPrefix}-transcript-language-label`,\r\n `.${this.player.options.classPrefix}-transcript-settings-menu`,\r\n `.${this.player.options.classPrefix}-transcript-style-dialog`\r\n ];\r\n \r\n for (const selector of ignoreSelectors) {\r\n if ((e.target as HTMLElement).closest(selector)) {\r\n return false; // Prevent drag\r\n }\r\n }\r\n \r\n return true; // Allow drag\r\n }\r\n });\r\n\r\n // Add custom keyboard handler for special keys (Escape, Home)\r\n this.customKeyHandler = (e: KeyboardEvent) => {\r\n const key = e.key.toLowerCase();\r\n const alreadyPrevented = e.defaultPrevented;\r\n\r\n // Don't handle keys if settings menu or style dialog is open (let them handle keys)\r\n if (this.settingsMenuVisible || this.styleDialogVisible) {\r\n return;\r\n }\r\n\r\n if (key === 'home') {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n if (this.draggableResizable) {\r\n if (this.draggableResizable.pointerResizeMode) {\r\n this.draggableResizable.disablePointerResizeMode();\r\n }\r\n this.draggableResizable.manuallyPositioned = false;\r\n this.positionTranscript();\r\n this.updateResizeOptionState();\r\n this.announceLive(i18n.t('transcript.positionReset'));\r\n }\r\n return;\r\n }\r\n \r\n if (key === 'r') {\r\n if (alreadyPrevented) {\r\n return;\r\n }\r\n e.preventDefault();\r\n e.stopPropagation();\r\n const enabled = this.toggleResizeMode();\r\n if (enabled) {\r\n this.transcriptWindow?.focus({ preventScroll: true });\r\n }\r\n return;\r\n }\r\n \r\n if (key === 'escape') {\r\n // Check priority: resize mode > drag mode > close transcript\r\n // (settings menu and style dialog already handled by early return above)\r\n if (this.draggableResizable && this.draggableResizable.pointerResizeMode) {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n this.draggableResizable.disablePointerResizeMode();\r\n return;\r\n }\r\n if (this.draggableResizable && this.draggableResizable.keyboardDragMode) {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n this.draggableResizable.disableKeyboardDragMode();\r\n this.announceLive(i18n.t('transcript.dragModeDisabled'));\r\n return;\r\n }\r\n // Only close transcript if nothing else is open\r\n e.preventDefault();\r\n e.stopPropagation();\r\n this.hideTranscript({ focusButton: true });\r\n return;\r\n }\r\n };\r\n \r\n const customKeyHandler = this.customKeyHandler;\r\n if (this.transcriptWindow && customKeyHandler) {\r\n this.transcriptWindow.addEventListener('keydown', customKeyHandler, {\r\n signal: this.player.lifecycleSignal\r\n });\r\n }\r\n }\r\n\r\n\r\n /**\r\n * Toggle keyboard drag mode. Mirrors the sign-language flow: a\r\n * persistent badge is shown on the transcript window while the mode\r\n * is active, and a live-region announcement is made on each state\r\n * change.\r\n */\r\n toggleKeyboardDragMode() {\r\n if (this.draggableResizable) {\r\n const wasEnabled = this.draggableResizable.keyboardDragMode;\r\n this.draggableResizable.toggleKeyboardDragMode();\r\n const isEnabled = this.draggableResizable.keyboardDragMode;\r\n if (!wasEnabled && isEnabled) {\r\n this.enableMoveMode();\r\n this._panel?.showBadge(i18n.t('transcript.dragModeBadge'));\r\n this.announceLive(i18n.t('transcript.dragModeEnabled'));\r\n } else if (wasEnabled && !isEnabled) {\r\n this._panel?.hideBadge();\r\n this.announceLive(i18n.t('transcript.dragModeDisabled'));\r\n }\r\n\r\n this.updateDragOptionState();\r\n\r\n if (this.settingsMenuVisible) {\r\n this.hideSettingsMenu();\r\n }\r\n\r\n this.transcriptWindow?.focus({ preventScroll: true });\r\n }\r\n }\r\n\r\n /**\r\n * Toggle settings menu visibility\r\n */\r\n toggleSettingsMenu() {\r\n if (this.settingsMenuVisible) {\r\n this.hideSettingsMenu();\r\n } else {\r\n this.showSettingsMenu();\r\n }\r\n }\r\n\r\n /**\r\n * Show the settings menu. Delegates to the shared {@link DraggablePanel};\r\n * kept as a named method so external callers (tests, other managers)\r\n * that referenced the legacy API keep working.\r\n */\r\n showSettingsMenu() {\r\n this._panel?.show();\r\n }\r\n\r\n /** @see {@link showSettingsMenu} */\r\n positionSettingsMenu() {\r\n this._panel?.reposition();\r\n }\r\n\r\n /** @see {@link showSettingsMenu} */\r\n hideSettingsMenu({ focusButton = true } = {}) {\r\n this._panel?.hide({ focusButton });\r\n }\r\n\r\n /**\r\n * Enable move mode (gives visual feedback)\r\n */\r\n /**\r\n * Brief pulse animation on the transcript window to confirm entry\r\n * into keyboard drag mode. The textual hint that used to also flash\r\n * here has been replaced by a persistent {@link DraggablePanel}\r\n * badge (see `toggleKeyboardDragMode`), so this method only owns\r\n * the 1s visual cue now.\r\n */\r\n enableMoveMode() {\r\n this.transcriptWindow?.classList.add(\r\n `${this.player.options.classPrefix}-transcript-move-mode`\r\n );\r\n\r\n setTimeout(() => {\r\n this.transcriptWindow?.classList.remove(\r\n `${this.player.options.classPrefix}-transcript-move-mode`\r\n );\r\n }, 2000);\r\n }\r\n\r\n /**\r\n * Toggle resize mode\r\n */\r\n toggleResizeMode({ focus = true } = {}) {\r\n if (!this.draggableResizable) {\r\n return false;\r\n }\r\n\r\n if (this.draggableResizable.pointerResizeMode) {\r\n this.draggableResizable.disablePointerResizeMode({ focus });\r\n return false;\r\n }\r\n\r\n this.draggableResizable.enablePointerResizeMode({ focus });\r\n return true;\r\n }\r\n\r\n // Thin delegates to the panel's refreshState. Kept as named methods\r\n // so the existing internal call sites (e.g. `toggleKeyboardDragMode`\r\n // and `toggleResizeMode`) read naturally without having to chain\r\n // through the optional panel reference every time.\r\n updateDragOptionState() {\r\n this._panel?.refreshDragState();\r\n }\r\n\r\n updateResizeOptionState() {\r\n this._panel?.refreshResizeState();\r\n }\r\n\r\n toggleShowTimestamps() {\r\n this.showTimestamps = !this.showTimestamps;\r\n this.updateShowTimestampsState();\r\n this.updateTimestampVisibility();\r\n this.saveTimestampsPreference();\r\n }\r\n\r\n updateShowTimestampsState() {\r\n if (!this.showTimestampsButton) {\r\n return;\r\n }\r\n \r\n const text = this.showTimestamps\r\n ? i18n.t('transcript.hideTimestamps')\r\n : i18n.t('transcript.showTimestamps');\r\n const ariaLabel = this.showTimestamps\r\n ? i18n.t('transcript.hideTimestampsAria')\r\n : i18n.t('transcript.showTimestampsAria');\r\n\r\n this.showTimestampsButton.setAttribute('aria-checked', this.showTimestamps ? 'true' : 'false');\r\n this.showTimestampsButton.setAttribute('aria-label', ariaLabel);\r\n\r\n if (this.showTimestampsText) {\r\n this.showTimestampsText.textContent = text;\r\n }\r\n }\r\n\r\n updateTimestampVisibility() {\r\n if (!this.transcriptContent) return;\r\n \r\n const timestamps = this.transcriptContent.querySelectorAll<HTMLElement>(`.${this.player.options.classPrefix}-transcript-time`);\r\n timestamps.forEach((timestamp) => {\r\n timestamp.style.display = this.showTimestamps ? '' : 'none';\r\n });\r\n }\r\n\r\n saveTimestampsPreference() {\r\n const savedPreferences = this.storage.getTranscriptPreferences() || {};\r\n savedPreferences.showTimestamps = this.showTimestamps;\r\n this.storage.saveTranscriptPreferences(savedPreferences);\r\n }\r\n\r\n // Legacy shims kept for any external callers that still invoke the\r\n // old transient-tooltip API. The persistent badge owned by the\r\n // {@link DraggablePanel} now replaces the 3-second indicator, so\r\n // these simply forward to the panel. Safe to remove once the next\r\n // consumer sweep confirms no external references.\r\n showResizeModeIndicator() {\r\n this._panel?.showBadge(i18n.t('transcript.resizeModeBadge'));\r\n }\r\n\r\n hideResizeModeIndicator() {\r\n this._panel?.hideBadge();\r\n }\r\n\r\n onPointerResizeModeChange(enabled: boolean) {\r\n this.updateResizeOptionState();\r\n\r\n if (enabled) {\r\n this._panel?.showBadge(i18n.t('transcript.resizeModeBadge'));\r\n this.announceLive(i18n.t('transcript.resizeModeEnabled'));\r\n } else {\r\n this._panel?.hideBadge();\r\n this.announceLive(i18n.t('transcript.resizeModeDisabled'));\r\n }\r\n }\r\n\r\n /**\r\n * Show style dialog\r\n */\r\n showStyleDialog() {\r\n // The style dialog uses an outside-click listener to dismiss\r\n // itself. Previously that listener was armed the first time the\r\n // settings menu opened (the same handler also dismissed the\r\n // settings menu). Now that the settings menu owns its own\r\n // outside-click, we arm the style-dialog listener here on first\r\n // open. Tied to `lifecycleSignal` so teardown is automatic.\r\n if (!this.documentClickHandlerAdded) {\r\n setTimeout(() => {\r\n const documentClick = this.handlers.documentClick;\r\n if (documentClick) {\r\n document.addEventListener('click', documentClick, {\r\n signal: this.player.lifecycleSignal\r\n });\r\n }\r\n this.documentClickHandlerAdded = true;\r\n }, 300);\r\n }\r\n\r\n // If dialog already exists, just show it\r\n if (this.styleDialog) {\r\n this.styleDialog.style.display = 'block';\r\n this.styleDialogVisible = true;\r\n \r\n // Re-add keyboard handler\r\n if (this.handlers.styleDialogKeydown) {\r\n document.addEventListener('keydown', this.handlers.styleDialogKeydown, {\r\n signal: this.player.lifecycleSignal\r\n });\r\n }\r\n \r\n // Set flag to prevent immediate closing from document click\r\n this.styleDialogJustOpened = true;\r\n setTimeout(() => {\r\n this.styleDialogJustOpened = false;\r\n }, 350);\r\n \r\n setTimeout(() => {\r\n const dialog = this.styleDialog;\r\n if (!dialog) return;\r\n const firstSelect = dialog.querySelector<HTMLElement>('select, input');\r\n if (firstSelect) {\r\n firstSelect.focus({ preventScroll: true });\r\n }\r\n }, 0);\r\n return;\r\n }\r\n\r\n const styleDialog = DOMUtils.createElement('div', {\r\n className: `${this.player.options.classPrefix}-transcript-style-dialog`\r\n });\r\n this.styleDialog = styleDialog;\r\n\r\n const title = DOMUtils.createElement('h4', {\r\n textContent: i18n.t('transcript.styleTitle'),\r\n className: `${this.player.options.classPrefix}-transcript-style-title`\r\n });\r\n styleDialog.appendChild(title);\r\n\r\n const fontSizeControl = this.createStyleSelectControl(\r\n i18n.t('captions.fontSize'),\r\n 'fontSize',\r\n [\r\n { label: i18n.t('fontSizes.small'), value: '90%' },\r\n { label: i18n.t('fontSizes.normal'), value: '100%' },\r\n { label: i18n.t('fontSizes.large'), value: '110%' },\r\n { label: i18n.t('fontSizes.xlarge'), value: '120%' }\r\n ]\r\n );\r\n styleDialog.appendChild(fontSizeControl);\r\n\r\n const fontFamilyControl = this.createStyleSelectControl(\r\n i18n.t('captions.fontFamily'),\r\n 'fontFamily',\r\n [\r\n { label: i18n.t('fontFamilies.sansSerif'), value: 'sans-serif' },\r\n { label: i18n.t('fontFamilies.serif'), value: 'serif' },\r\n { label: i18n.t('fontFamilies.monospace'), value: 'monospace' }\r\n ]\r\n );\r\n styleDialog.appendChild(fontFamilyControl);\r\n\r\n const colorControl = this.createStyleColorControl(i18n.t('captions.color'), 'color');\r\n styleDialog.appendChild(colorControl);\r\n\r\n const bgColorControl = this.createStyleColorControl(i18n.t('captions.backgroundColor'), 'backgroundColor');\r\n styleDialog.appendChild(bgColorControl);\r\n\r\n const opacityControl = this.createStyleOpacityControl(i18n.t('captions.opacity'), 'opacity');\r\n styleDialog.appendChild(opacityControl);\r\n\r\n const closeBtn = DOMUtils.createElement('button', {\r\n className: `${this.player.options.classPrefix}-transcript-style-close`,\r\n textContent: i18n.t('settings.close'),\r\n attributes: {\r\n 'type': 'button'\r\n }\r\n });\r\n closeBtn.addEventListener('click', () => this.hideStyleDialog());\r\n styleDialog.appendChild(closeBtn);\r\n\r\n const styleKeyHandler = (e: KeyboardEvent) => {\r\n if (!this.styleDialogVisible) return;\r\n \r\n if (e.key === 'Escape') {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n this.hideStyleDialog();\r\n return;\r\n }\r\n \r\n if (e.key === 'Tab') {\r\n const focusableElements = styleDialog.querySelectorAll<HTMLElement>(\r\n 'select, input, button'\r\n );\r\n const firstElement = focusableElements[0];\r\n const lastElement = focusableElements[focusableElements.length - 1];\r\n if (!firstElement || !lastElement) return;\r\n \r\n if (e.shiftKey && document.activeElement === firstElement) {\r\n e.preventDefault();\r\n lastElement.focus({ preventScroll: true });\r\n } else if (!e.shiftKey && document.activeElement === lastElement) {\r\n e.preventDefault();\r\n firstElement.focus({ preventScroll: true });\r\n }\r\n }\r\n };\r\n this.handlers.styleDialogKeydown = styleKeyHandler;\r\n document.addEventListener('keydown', styleKeyHandler, {\r\n signal: this.player.lifecycleSignal\r\n });\r\n\r\n if (this.headerLeft) {\r\n this.headerLeft.appendChild(styleDialog);\r\n } else if (this.transcriptHeader) {\r\n this.transcriptHeader.appendChild(styleDialog);\r\n }\r\n \r\n this.applyTranscriptStyles();\r\n \r\n this.styleDialogVisible = true;\r\n styleDialog.style.display = 'block';\r\n \r\n this.styleDialogJustOpened = true;\r\n setTimeout(() => {\r\n this.styleDialogJustOpened = false;\r\n }, 350);\r\n \r\n setTimeout(() => {\r\n const firstSelect = styleDialog.querySelector<HTMLElement>('select, input');\r\n if (firstSelect) {\r\n firstSelect.focus({ preventScroll: true });\r\n }\r\n }, 0);\r\n }\r\n\r\n /**\r\n * Hide style dialog\r\n */\r\n hideStyleDialog() {\r\n if (this.styleDialog) {\r\n this.styleDialog.style.display = 'none';\r\n this.styleDialogVisible = false;\r\n \r\n // Remove keyboard handler\r\n if (this.handlers.styleDialogKeydown) {\r\n document.removeEventListener('keydown', this.handlers.styleDialogKeydown);\r\n }\r\n \r\n // Return focus to settings button\r\n if (this.settingsButton) {\r\n this.settingsButton.focus({ preventScroll: true });\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Create style select control\r\n */\r\n createStyleSelectControl(label: string, property: string, options: Array<{ label: string; value: string }>) {\r\n const group = DOMUtils.createElement('div', {\r\n className: `${this.player.options.classPrefix}-transcript-style-group`\r\n });\r\n\r\n // Generate unique ID for the control\r\n const controlId = `${this.player.options.classPrefix}-transcript-${property}-${Date.now()}`;\r\n\r\n const labelEl = DOMUtils.createElement('label', {\r\n textContent: label,\r\n attributes: {\r\n 'for': controlId\r\n }\r\n });\r\n group.appendChild(labelEl);\r\n\r\n const select = DOMUtils.createElement('select', {\r\n className: `${this.player.options.classPrefix}-transcript-style-select`,\r\n attributes: {\r\n 'id': controlId\r\n }\r\n });\r\n\r\n options.forEach((opt: { label: string; value: string }) => {\r\n const option = DOMUtils.createElement('option', {\r\n textContent: opt.label,\r\n attributes: {\r\n 'value': opt.value\r\n }\r\n });\r\n if (this.transcriptStyle[property as 'fontSize' | 'fontFamily'] === opt.value) {\r\n option.selected = true;\r\n }\r\n select.appendChild(option);\r\n });\r\n\r\n select.addEventListener('change', (e) => {\r\n this.transcriptStyle[property as 'fontSize' | 'fontFamily'] = (e.target as HTMLSelectElement).value;\r\n this.applyTranscriptStyles();\r\n this.savePreferences();\r\n });\r\n\r\n group.appendChild(select);\r\n return group;\r\n }\r\n\r\n /**\r\n * Create style color control\r\n */\r\n createStyleColorControl(label: string, property: string) {\r\n const group = DOMUtils.createElement('div', {\r\n className: `${this.player.options.classPrefix}-transcript-style-group`\r\n });\r\n\r\n // Generate unique ID for the control\r\n const controlId = `${this.player.options.classPrefix}-transcript-${property}-${Date.now()}`;\r\n\r\n const labelEl = DOMUtils.createElement('label', {\r\n textContent: label,\r\n attributes: {\r\n 'for': controlId\r\n }\r\n });\r\n group.appendChild(labelEl);\r\n\r\n const input = DOMUtils.createElement('input', {\r\n attributes: {\r\n 'id': controlId,\r\n 'type': 'color',\r\n 'value': this.transcriptStyle[property as 'color' | 'backgroundColor']\r\n },\r\n className: `${this.player.options.classPrefix}-transcript-style-color`\r\n });\r\n\r\n input.addEventListener('input', (e) => {\r\n this.transcriptStyle[property as 'color' | 'backgroundColor'] = (e.target as HTMLInputElement).value;\r\n this.applyTranscriptStyles();\r\n this.savePreferences();\r\n });\r\n\r\n group.appendChild(input);\r\n return group;\r\n }\r\n\r\n /**\r\n * Create style opacity control\r\n */\r\n createStyleOpacityControl(label: string, property: string) {\r\n const group = DOMUtils.createElement('div', {\r\n className: `${this.player.options.classPrefix}-transcript-style-group`\r\n });\r\n\r\n // Generate unique ID for the control\r\n const controlId = `${this.player.options.classPrefix}-transcript-${property}-${Date.now()}`;\r\n\r\n const labelEl = DOMUtils.createElement('label', {\r\n textContent: label,\r\n attributes: {\r\n 'for': controlId\r\n }\r\n });\r\n group.appendChild(labelEl);\r\n\r\n const opacityProperty = property as 'opacity';\r\n const valueDisplay = DOMUtils.createElement('span', {\r\n textContent: Math.round(this.transcriptStyle[opacityProperty] * 100) + '%',\r\n className: `${this.player.options.classPrefix}-transcript-style-value`\r\n });\r\n\r\n const input = DOMUtils.createElement('input', {\r\n attributes: {\r\n 'id': controlId,\r\n 'type': 'range',\r\n 'min': '0',\r\n 'max': '1',\r\n 'step': '0.1',\r\n 'value': String(this.transcriptStyle[opacityProperty])\r\n },\r\n className: `${this.player.options.classPrefix}-transcript-style-range`\r\n });\r\n\r\n input.addEventListener('input', (e) => {\r\n const value = parseFloat((e.target as HTMLInputElement).value);\r\n this.transcriptStyle[opacityProperty] = value;\r\n valueDisplay.textContent = Math.round(value * 100) + '%';\r\n this.applyTranscriptStyles();\r\n this.savePreferences();\r\n });\r\n\r\n const inputContainer = DOMUtils.createElement('div', {\r\n className: `${this.player.options.classPrefix}-transcript-style-range-container`\r\n });\r\n inputContainer.appendChild(input);\r\n inputContainer.appendChild(valueDisplay);\r\n\r\n group.appendChild(labelEl);\r\n group.appendChild(inputContainer);\r\n return group;\r\n }\r\n\r\n /**\r\n * Save transcript preferences to localStorage\r\n */\r\n savePreferences() {\r\n this.storage.saveTranscriptPreferences(this.transcriptStyle);\r\n }\r\n\r\n /**\r\n * Apply transcript styles\r\n */\r\n applyTranscriptStyles() {\r\n if (!this.transcriptWindow) return;\r\n\r\n // Apply to transcript window background\r\n this.transcriptWindow.style.backgroundColor = this.transcriptStyle.backgroundColor;\r\n this.transcriptWindow.style.opacity = String(this.transcriptStyle.opacity);\r\n\r\n // Apply to content area\r\n if (this.transcriptContent) {\r\n this.transcriptContent.style.fontSize = this.transcriptStyle.fontSize;\r\n this.transcriptContent.style.fontFamily = this.transcriptStyle.fontFamily;\r\n this.transcriptContent.style.color = this.transcriptStyle.color;\r\n }\r\n\r\n const textEntries = this.transcriptWindow.querySelectorAll<HTMLElement>(`.${this.player.options.classPrefix}-transcript-text`);\r\n textEntries.forEach((entry) => {\r\n entry.style.fontSize = this.transcriptStyle.fontSize;\r\n entry.style.fontFamily = this.transcriptStyle.fontFamily;\r\n entry.style.color = this.transcriptStyle.color;\r\n });\r\n \r\n const timeEntries = this.transcriptWindow.querySelectorAll<HTMLElement>(`.${this.player.options.classPrefix}-transcript-time`);\r\n timeEntries.forEach((entry) => {\r\n entry.style.fontFamily = this.transcriptStyle.fontFamily;\r\n });\r\n }\r\n\r\n /**\r\n * Set a managed timeout that will be cleaned up on destroy\r\n * @param {Function} callback - Callback function\r\n * @param {number} delay - Delay in milliseconds\r\n * @returns {number} Timeout ID\r\n */\r\n setManagedTimeout(callback: () => void, delay: number) {\r\n const timeoutId = setTimeout(() => {\r\n this.timeouts.delete(timeoutId);\r\n callback();\r\n }, delay);\r\n this.timeouts.add(timeoutId);\r\n return timeoutId;\r\n }\r\n\r\n /**\r\n * Clear a managed timeout\r\n * @param {number} timeoutId - Timeout ID to clear\r\n */\r\n clearManagedTimeout(timeoutId: ReturnType<typeof setTimeout> | null) {\r\n if (timeoutId) {\r\n clearTimeout(timeoutId);\r\n this.timeouts.delete(timeoutId);\r\n }\r\n }\r\n\r\n /**\r\n * Cleanup\r\n */\r\n destroy() {\r\n this.hideResizeModeIndicator();\r\n\r\n // Tear down the settings-menu panel (removes its DOM + keyboard\r\n // listeners). The outside-click listener was attached with the\r\n // player's lifecycleSignal, so it cleans itself up as the player\r\n // is destroyed.\r\n if (this._panel) {\r\n this._panel.destroy();\r\n this._panel = null;\r\n }\r\n\r\n // Destroy draggableResizable utility\r\n if (this.draggableResizable) {\r\n if (this.draggableResizable.pointerResizeMode) {\r\n this.draggableResizable.disablePointerResizeMode();\r\n this.updateResizeOptionState();\r\n }\r\n this.draggableResizable.destroy();\r\n this.draggableResizable = null;\r\n }\r\n \r\n // Remove custom key handler\r\n if (this.transcriptWindow && this.customKeyHandler) {\r\n this.transcriptWindow.removeEventListener('keydown', this.customKeyHandler);\r\n this.customKeyHandler = null;\r\n }\r\n\r\n // Remove timeupdate and seeked listeners from player\r\n if (this.handlers.timeupdate) {\r\n this.player.off('timeupdate', this.handlers.timeupdate);\r\n }\r\n if (this.handlers.seeked) {\r\n this.player.off('seeked', this.handlers.seeked);\r\n }\r\n \r\n // Remove audio description listeners from player\r\n if (this.handlers.audiodescriptionenabled) {\r\n this.player.off('audiodescriptionenabled', this.handlers.audiodescriptionenabled);\r\n }\r\n if (this.handlers.audiodescriptiondisabled) {\r\n this.player.off('audiodescriptiondisabled', this.handlers.audiodescriptiondisabled);\r\n }\r\n\r\n // Remove text cue update listener\r\n if (this.handlers.textcuesupdate) {\r\n this.player.off('textcuesupdate', this.handlers.textcuesupdate);\r\n }\r\n\r\n // Remove floating-state listener\r\n if (this.handlers.floatingchange) {\r\n this.player.off('floatingchange', this.handlers.floatingchange);\r\n }\r\n \r\n // Remove settings button event listeners\r\n if (this.settingsButton) {\r\n if (this.handlers.settingsClick) {\r\n this.settingsButton.removeEventListener('click', this.handlers.settingsClick);\r\n }\r\n if (this.handlers.settingsKeydown) {\r\n this.settingsButton.removeEventListener('keydown', this.handlers.settingsKeydown);\r\n }\r\n }\r\n \r\n // Remove style dialog event listeners\r\n if (this.handlers.styleDialogKeydown) {\r\n document.removeEventListener('keydown', this.handlers.styleDialogKeydown);\r\n }\r\n\r\n // Remove document click listener\r\n if (this.handlers.documentClick) {\r\n document.removeEventListener('click', this.handlers.documentClick);\r\n }\r\n \r\n // Remove window-level listeners\r\n if (this.handlers.resize) {\r\n window.removeEventListener('resize', this.handlers.resize);\r\n }\r\n\r\n // Cleanup all managed timeouts\r\n this.timeouts.forEach((timeoutId: ReturnType<typeof setTimeout>) => clearTimeout(timeoutId));\r\n this.timeouts.clear();\r\n\r\n this.handlers = {} as TranscriptHandlers;\r\n\r\n // Remove DOM element\r\n if (this.transcriptWindow && this.transcriptWindow.parentNode) {\r\n this.transcriptWindow.parentNode.removeChild(this.transcriptWindow);\r\n }\r\n \r\n this.transcriptWindow = null;\r\n this.transcriptHeader = null;\r\n this.transcriptContent = null;\r\n this.transcriptEntries = [];\r\n this.styleDialog = null;\r\n this.transcriptResizeHandles = [];\r\n this.liveRegion = null;\r\n this._vttCache.clear();\r\n }\r\n\r\n announceLive(message: string) {\r\n if (!this.liveRegion) return;\r\n this.liveRegion.textContent = message || '';\r\n }\r\n}\r\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/renderers/VimeoRenderer.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Renderer } from '../types/renderer.js';\r\nimport type { Player } from '../core/Player.js';\r\nimport { loadScriptOnce } from '../utils/ScriptLoader.js';\r\n\r\nexport class VimeoRenderer implements Renderer {\r\n player: Player;\r\n media: HTMLMediaElement;\r\n vimeo: VimeoPlayer | null;\r\n videoId: string | null;\r\n isReady: boolean;\r\n iframe: HTMLDivElement | null;\r\n\r\n constructor(player: Player) {\r\n this.player = player;\r\n this.media = player.element;\r\n this.vimeo = null;\r\n this.videoId = null;\r\n this.isReady = false;\r\n this.iframe = null;\r\n }\r\n\r\n async init() {\r\n // Extract video ID from URL - use currentSource which works for external renderers\r\n const src = this.player.currentSource || this.player.element.src;\r\n this.videoId = this.extractVideoId(src);\r\n \r\n if (!this.videoId) {\r\n throw new Error('Invalid Vimeo URL');\r\n }\r\n\r\n // Load Vimeo Player API\r\n await this.loadVimeoAPI();\r\n\r\n // Create iframe\r\n this.createIframe();\r\n\r\n // Initialize player\r\n await this.initializePlayer();\r\n }\r\n\r\n extractVideoId(url: string) {\r\n const patterns = [\r\n /vimeo\\.com\\/(\\d+)/,\r\n /vimeo\\.com\\/video\\/(\\d+)/,\r\n /player\\.vimeo\\.com\\/video\\/(\\d+)/\r\n ];\r\n\r\n for (const pattern of patterns) {\r\n const match = url.match(pattern);\r\n if (match && match[1]) {\r\n return match[1];\r\n }\r\n }\r\n\r\n return null;\r\n }\r\n\r\n async loadVimeoAPI() {\r\n // Check if API is already loaded\r\n if (window.Vimeo && window.Vimeo.Player) {\r\n return Promise.resolve();\r\n }\r\n\r\n return loadScriptOnce('https://player.vimeo.com/api/player.js', {\r\n isReady: () => !!(window.Vimeo && window.Vimeo.Player)\r\n });\r\n }\r\n\r\n createIframe() {\r\n // Hide original element\r\n this.player.element.style.display = 'none';\r\n\r\n // Create container for iframe\r\n this.iframe = document.createElement('div');\r\n this.iframe.id = `vimeo-player-${Math.random().toString(36).substr(2, 9)}`;\r\n this.iframe.style.width = '100%';\r\n this.iframe.style.maxHeight = '100%';\r\n \r\n this.player.element.parentNode?.insertBefore(this.iframe, this.player.element);\r\n }\r\n\r\n async initializePlayer() {\r\n const options: Record<string, unknown> = {\r\n id: this.videoId,\r\n width: '100%',\r\n height: '100%',\r\n controls: true,\r\n autoplay: this.player.options.autoplay,\r\n muted: this.player.options.muted,\r\n loop: this.player.options.loop,\r\n keyboard: false\r\n };\r\n\r\n if (this.player.options.startTime > 0) {\r\n options.startTime = this.player.options.startTime;\r\n }\r\n\r\n if (!window.Vimeo || !this.iframe) {\r\n // The SDK never became available (or the iframe container is missing).\r\n // Reject so Player.init() surfaces a real failure instead of treating a\r\n // non-functional embed as a successful initialization.\r\n throw new Error('Vimeo Player API is not available');\r\n }\r\n this.vimeo = new window.Vimeo.Player(this.iframe.id, options as VimeoPlayerOptions);\r\n\r\n // Wait for player to be ready\r\n await this.vimeo.ready();\r\n this.isReady = true;\r\n \r\n // Ensure the iframe has 100% width and height\r\n const vimeoIframe = this.iframe?.querySelector('iframe');\r\n if (vimeoIframe) {\r\n vimeoIframe.style.width = '100%';\r\n vimeoIframe.style.height = '100%';\r\n vimeoIframe.setAttribute('width', '100%');\r\n vimeoIframe.setAttribute('height', '100%');\r\n }\r\n \r\n // Hide VidPly controls - Vimeo has its own\r\n if (this.player.container) {\r\n this.player.container.classList.add('vidply-external-controls');\r\n }\r\n\r\n this.attachEvents();\r\n\r\n // Get initial duration\r\n try {\r\n const duration = await this.vimeo.getDuration();\r\n this.player.state.duration = duration;\r\n this.player.emit('loadedmetadata');\r\n } catch (error) {\r\n this.player.log('Error getting duration:', error, 'warn');\r\n }\r\n }\r\n\r\n attachEvents() {\r\n const vimeo = this.vimeo;\r\n if (!vimeo) return;\r\n\r\n // Vimeo's on() callback signature is (...args: unknown[]) => void, so\r\n // each handler narrows its own payload before reading fields.\r\n vimeo.on('play', () => {\r\n this.player.state.playing = true;\r\n this.player.state.paused = false;\r\n this.player.state.ended = false;\r\n this.player.emit('play');\r\n\r\n if (this.player.options.onPlay) {\r\n this.player.options.onPlay.call(this.player);\r\n }\r\n });\r\n\r\n vimeo.on('pause', () => {\r\n this.player.state.playing = false;\r\n this.player.state.paused = true;\r\n this.player.emit('pause');\r\n\r\n if (this.player.options.onPause) {\r\n this.player.options.onPause.call(this.player);\r\n }\r\n });\r\n\r\n vimeo.on('ended', () => {\r\n this.player.state.playing = false;\r\n this.player.state.paused = true;\r\n this.player.state.ended = true;\r\n this.player.emit('ended');\r\n\r\n if (this.player.options.onEnded) {\r\n this.player.options.onEnded.call(this.player);\r\n }\r\n });\r\n\r\n vimeo.on('timeupdate', (...args: unknown[]) => {\r\n const data = args[0] as { seconds: number; duration: number };\r\n this.player.state.currentTime = data.seconds;\r\n this.player.state.duration = data.duration;\r\n this.player.emit('timeupdate', data.seconds);\r\n\r\n if (this.player.options.onTimeUpdate) {\r\n this.player.options.onTimeUpdate.call(this.player, data.seconds);\r\n }\r\n });\r\n\r\n vimeo.on('volumechange', (...args: unknown[]) => {\r\n const data = args[0] as { volume: number };\r\n this.player.state.volume = data.volume;\r\n this.player.emit('volumechange', data.volume);\r\n });\r\n\r\n vimeo.on('bufferstart', () => {\r\n this.player.state.buffering = true;\r\n this.player.emit('waiting');\r\n });\r\n\r\n vimeo.on('bufferend', () => {\r\n this.player.state.buffering = false;\r\n this.player.emit('canplay');\r\n });\r\n\r\n vimeo.on('seeking', () => {\r\n this.player.state.seeking = true;\r\n this.player.emit('seeking');\r\n });\r\n\r\n vimeo.on('seeked', () => {\r\n this.player.state.seeking = false;\r\n this.player.emit('seeked');\r\n });\r\n\r\n vimeo.on('playbackratechange', (...args: unknown[]) => {\r\n const data = args[0] as { playbackRate: number };\r\n this.player.state.playbackSpeed = data.playbackRate;\r\n this.player.emit('ratechange', data.playbackRate);\r\n });\r\n\r\n vimeo.on('error', (...args: unknown[]) => {\r\n const error = args[0] as { message?: string } | undefined;\r\n this.player.handleError(new Error(`Vimeo error: ${error?.message ?? 'unknown'}`));\r\n });\r\n }\r\n\r\n play() {\r\n if (this.isReady && this.vimeo) {\r\n // Save scroll position to prevent browser from scrolling to video\r\n const scrollX = window.scrollX;\r\n const scrollY = window.scrollY;\r\n \r\n this.vimeo.play().catch((error: unknown) => {\r\n this.player.log('Play error:', error, 'warn');\r\n });\r\n \r\n // Restore scroll position immediately to prevent auto-scroll\r\n window.scrollTo(scrollX, scrollY);\r\n }\r\n }\r\n\r\n pause() {\r\n if (this.isReady && this.vimeo) {\r\n this.vimeo.pause().catch((error: unknown) => {\r\n this.player.log('Pause error:', error, 'warn');\r\n });\r\n }\r\n }\r\n\r\n seek(time: number) {\r\n if (this.isReady && this.vimeo) {\r\n this.vimeo.setCurrentTime(time).catch((error: unknown) => {\r\n this.player.log('Seek error:', error, 'warn');\r\n });\r\n }\r\n }\r\n\r\n setVolume(volume: number) {\r\n if (this.isReady && this.vimeo) {\r\n this.vimeo.setVolume(volume).catch((error: unknown) => {\r\n this.player.log('Volume error:', error, 'warn');\r\n });\r\n this.player.state.volume = volume;\r\n }\r\n }\r\n\r\n setMuted(muted: boolean) {\r\n if (this.isReady && this.vimeo) {\r\n if (muted) {\r\n this.vimeo.setVolume(0);\r\n } else {\r\n this.vimeo.setVolume(this.player.state.volume);\r\n }\r\n this.player.state.muted = muted;\r\n }\r\n }\r\n\r\n setPlaybackSpeed(speed: number) {\r\n if (this.isReady && this.vimeo) {\r\n this.vimeo.setPlaybackRate(speed).catch((error: unknown) => {\r\n this.player.log('Playback rate error:', error, 'warn');\r\n });\r\n this.player.state.playbackSpeed = speed;\r\n }\r\n }\r\n\r\n destroy() {\r\n if (this.vimeo && this.vimeo.destroy) {\r\n this.vimeo.destroy();\r\n }\r\n\r\n if (this.iframe && this.iframe.parentNode) {\r\n this.iframe.parentNode.removeChild(this.iframe);\r\n }\r\n\r\n // Show original element\r\n if (this.player.element) {\r\n this.player.element.style.display = '';\r\n }\r\n }\r\n}\r\n\r\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/renderers/YouTubeRenderer.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Renderer } from '../types/renderer.js';\r\nimport type { Player } from '../core/Player.js';\r\nimport { loadScriptOnce } from '../utils/ScriptLoader.js';\r\n\r\nexport class YouTubeRenderer implements Renderer {\r\n player: Player;\r\n media: HTMLMediaElement;\r\n youtube: YTPlayer | null;\r\n videoId: string | null;\r\n isReady: boolean;\r\n iframe: HTMLDivElement | null;\r\n timeUpdateInterval?: ReturnType<typeof setInterval>;\r\n\r\n constructor(player: Player) {\r\n this.player = player;\r\n this.media = player.element;\r\n this.youtube = null;\r\n this.videoId = null;\r\n this.isReady = false;\r\n this.iframe = null;\r\n }\r\n\r\n async init() {\r\n // Extract video ID from URL - use currentSource which works for external renderers\r\n const src = this.player.currentSource || this.player.element.src;\r\n this.videoId = this.extractVideoId(src);\r\n \r\n if (!this.videoId) {\r\n throw new Error('Invalid YouTube URL');\r\n }\r\n\r\n // Load YouTube IFrame API\r\n await this.loadYouTubeAPI();\r\n\r\n // Create iframe\r\n this.createIframe();\r\n\r\n // Initialize player\r\n await this.initializePlayer();\r\n }\r\n\r\n extractVideoId(url: string) {\r\n const patterns = [\r\n /(?:youtube\\.com\\/watch\\?v=|youtu\\.be\\/)([^&\\s]+)/,\r\n /youtube\\.com\\/embed\\/([^&\\s]+)/\r\n ];\r\n\r\n for (const pattern of patterns) {\r\n const match = url.match(pattern);\r\n if (match && match[1]) {\r\n return match[1];\r\n }\r\n }\r\n\r\n return null;\r\n }\r\n\r\n async loadYouTubeAPI() {\r\n // Check if API is already loaded\r\n if (window.YT && window.YT.Player) {\r\n return Promise.resolve();\r\n }\r\n\r\n // The IFrame API exposes window.YT.Player a short time after its script\r\n // loads (it signals readiness via the global onYouTubeIframeAPIReady\r\n // callback). loadScriptOnce dedupes the injection and the readiness\r\n // predicate polls for YT.Player, also covering the case where the host\r\n // page already included the API script.\r\n return loadScriptOnce('https://www.youtube.com/iframe_api', {\r\n isReady: () => !!(window.YT && window.YT.Player),\r\n readyTimeout: 8000\r\n });\r\n }\r\n\r\n createIframe() {\r\n // Hide original element\r\n this.player.element.style.display = 'none';\r\n\r\n // Create container for iframe\r\n this.iframe = document.createElement('div');\r\n this.iframe.id = `youtube-player-${Math.random().toString(36).substr(2, 9)}`;\r\n this.iframe.style.width = '100%';\r\n this.iframe.style.maxHeight = '100%';\r\n \r\n this.player.element.parentNode?.insertBefore(this.iframe, this.player.element);\r\n }\r\n\r\n async initializePlayer() {\r\n return new Promise<void>((resolve, reject) => {\r\n if (!window.YT || !this.iframe) {\r\n // The SDK never became available (or the iframe container is missing).\r\n // Reject so Player.init() surfaces a real failure instead of treating\r\n // a non-functional embed as a successful initialization.\r\n reject(new Error('YouTube IFrame API is not available'));\r\n return;\r\n }\r\n this.youtube = new window.YT.Player(this.iframe.id, {\r\n videoId: this.videoId ?? undefined,\r\n width: '100%',\r\n height: '100%',\r\n playerVars: {\r\n controls: 1, // Use YouTube native controls\r\n disablekb: 0, // Allow keyboard controls\r\n fs: 1, // Allow fullscreen\r\n modestbranding: 1,\r\n rel: 0,\r\n showinfo: 0,\r\n iv_load_policy: 3,\r\n autoplay: this.player.options.autoplay ? 1 : 0,\r\n mute: this.player.options.muted ? 1 : 0,\r\n start: this.player.options.startTime || 0\r\n },\r\n events: {\r\n onReady: (_event: unknown) => {\r\n this.isReady = true;\r\n this.attachEvents();\r\n // Hide VidPly controls - YouTube has its own\r\n if (this.player.container) {\r\n this.player.container.classList.add('vidply-external-controls');\r\n }\r\n resolve();\r\n },\r\n onStateChange: (event: unknown) => this.handleStateChange(event as { data: number }),\r\n onError: (event: unknown) => this.handleError(event as { data: 2 | 5 | 100 | 101 | 150 })\r\n }\r\n });\r\n });\r\n }\r\n\r\n attachEvents() {\r\n // Set up polling for time updates (YouTube doesn't provide timeupdate events)\r\n this.timeUpdateInterval = setInterval(() => {\r\n const youtube = this.youtube;\r\n if (this.isReady && youtube) {\r\n const currentTime = youtube.getCurrentTime();\r\n const duration = youtube.getDuration();\r\n\r\n this.player.state.currentTime = currentTime;\r\n this.player.state.duration = duration;\r\n\r\n this.player.emit('timeupdate', currentTime);\r\n }\r\n }, 250);\r\n\r\n // Initial metadata\r\n const youtube = this.youtube;\r\n if (youtube && youtube.getDuration) {\r\n this.player.state.duration = youtube.getDuration();\r\n this.player.emit('loadedmetadata');\r\n }\r\n }\r\n\r\n handleStateChange(event: { data: number }) {\r\n const states = window.YT?.PlayerState;\r\n if (!states) return;\r\n\r\n switch (event.data) {\r\n case states.PLAYING:\r\n this.player.state.playing = true;\r\n this.player.state.paused = false;\r\n this.player.state.ended = false;\r\n this.player.state.buffering = false;\r\n this.player.emit('play');\r\n this.player.emit('playing');\r\n \r\n if (this.player.options.onPlay) {\r\n this.player.options.onPlay.call(this.player);\r\n }\r\n break;\r\n\r\n case states.PAUSED:\r\n this.player.state.playing = false;\r\n this.player.state.paused = true;\r\n this.player.emit('pause');\r\n \r\n if (this.player.options.onPause) {\r\n this.player.options.onPause.call(this.player);\r\n }\r\n break;\r\n\r\n case states.ENDED:\r\n this.player.state.playing = false;\r\n this.player.state.paused = true;\r\n this.player.state.ended = true;\r\n this.player.emit('ended');\r\n\r\n if (this.player.options.onEnded) {\r\n this.player.options.onEnded.call(this.player);\r\n }\r\n\r\n if (this.player.options.loop && this.youtube) {\r\n this.youtube.seekTo(0);\r\n this.youtube.playVideo();\r\n }\r\n break;\r\n\r\n case states.BUFFERING:\r\n this.player.state.buffering = true;\r\n this.player.emit('waiting');\r\n break;\r\n\r\n case states.CUED:\r\n this.player.emit('loadedmetadata');\r\n break;\r\n }\r\n }\r\n\r\n handleError(event: { data: 2 | 5 | 100 | 101 | 150 }) {\r\n const errors = {\r\n 2: 'Invalid video ID',\r\n 5: 'HTML5 player error',\r\n 100: 'Video not found',\r\n 101: 'Video not allowed to be played in embedded players',\r\n 150: 'Video not allowed to be played in embedded players'\r\n };\r\n\r\n const error = new Error(errors[event.data] || 'YouTube player error');\r\n this.player.handleError(error);\r\n }\r\n\r\n play() {\r\n if (this.isReady && this.youtube) {\r\n // Save scroll position to prevent browser from scrolling to video\r\n const scrollX = window.scrollX;\r\n const scrollY = window.scrollY;\r\n \r\n this.youtube.playVideo();\r\n \r\n // Restore scroll position immediately to prevent auto-scroll\r\n window.scrollTo(scrollX, scrollY);\r\n }\r\n }\r\n\r\n pause() {\r\n if (this.isReady && this.youtube) {\r\n this.youtube.pauseVideo();\r\n }\r\n }\r\n\r\n seek(time: number) {\r\n if (this.isReady && this.youtube) {\r\n this.youtube.seekTo(time, true);\r\n }\r\n }\r\n\r\n setVolume(volume: number) {\r\n if (this.isReady && this.youtube) {\r\n this.youtube.setVolume(volume * 100);\r\n this.player.state.volume = volume;\r\n }\r\n }\r\n\r\n setMuted(muted: boolean) {\r\n if (this.isReady && this.youtube) {\r\n if (muted) {\r\n this.youtube.mute();\r\n } else {\r\n this.youtube.unMute();\r\n }\r\n this.player.state.muted = muted;\r\n }\r\n }\r\n\r\n setPlaybackSpeed(speed: number) {\r\n if (this.isReady && this.youtube) {\r\n this.youtube.setPlaybackRate(speed);\r\n this.player.state.playbackSpeed = speed;\r\n }\r\n }\r\n\r\n destroy() {\r\n if (this.timeUpdateInterval) {\r\n clearInterval(this.timeUpdateInterval);\r\n }\r\n\r\n if (this.youtube && this.youtube.destroy) {\r\n this.youtube.destroy();\r\n }\r\n\r\n if (this.iframe && this.iframe.parentNode) {\r\n this.iframe.parentNode.removeChild(this.iframe);\r\n }\r\n\r\n // Show original element\r\n if (this.player.element) {\r\n this.player.element.style.display = '';\r\n }\r\n }\r\n}\r\n\r\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["youtube"]
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/controls/CaptionManager.ts"],
|
|
4
|
-
"sourcesContent": ["/**\r\n * Caption/Subtitle Manager\r\n */\r\n\r\nimport {DOMUtils} from '../utils/DOMUtils.js';\r\nimport {i18n} from '../i18n/i18n.js';\r\nimport {StorageManager} from '../utils/StorageManager.js';\r\nimport {debounce, isMobile, rafWithTimeout, reducedMotionScrollOptions} from '../utils/PerformanceUtils.js';\r\nimport {deriveTrackLabel} from '../utils/TrackLabelUtils.js';\r\nimport type { Player } from '../core/Player.js';\r\n\r\n/**\r\n * Deduplicated subtitle/captions entry shown in the captions menu. Native HLS\r\n * on Safari can expose the same language as both `SUBTITLES` (WebVTT sidecar)\r\n * and `CLOSED-CAPTIONS` (inband CEA-608) groups; the duplicates are kept in\r\n * `alternatives` so we can listen on all of them and use whichever delivers\r\n * cues first.\r\n */\r\nexport type CaptionTrackEntry = {\r\n track: TextTrack;\r\n language: string;\r\n label: string;\r\n kind: string;\r\n index: number;\r\n isDefault: boolean;\r\n alternatives: TextTrack[];\r\n};\r\n\r\ntype CueChangeHandler = () => void;\r\n\r\nexport class CaptionManager {\r\n player: Player;\r\n _altCueChangeHandler: CueChangeHandler | null = null;\r\n cueChangeHandler: CueChangeHandler | null = null;\r\n currentCue: VTTCue | null;\r\n currentTrack: CaptionTrackEntry | null;\r\n debouncedPositionCaptions!: () => void;\r\n element!: HTMLElement;\r\n storage: StorageManager;\r\n tracks: CaptionTrackEntry[];\r\n\r\n constructor(player: Player) {\r\n this.player = player;\r\n this.tracks = [];\r\n this.currentTrack = null;\r\n this.currentCue = null;\r\n\r\n // Storage manager\r\n this.storage = new StorageManager('vidply');\r\n\r\n // Load saved preferences\r\n this.loadSavedPreferences();\r\n\r\n this.init();\r\n }\r\n \r\n loadSavedPreferences() {\r\n const saved = this.storage.getCaptionPreferences();\r\n if (saved) {\r\n if (typeof saved.fontSize === 'string') this.player.options.captionsFontSize = saved.fontSize;\r\n if (typeof saved.fontFamily === 'string') this.player.options.captionsFontFamily = saved.fontFamily;\r\n if (typeof saved.color === 'string') this.player.options.captionsColor = saved.color;\r\n if (typeof saved.backgroundColor === 'string') this.player.options.captionsBackgroundColor = saved.backgroundColor;\r\n if (typeof saved.opacity === 'number') this.player.options.captionsOpacity = saved.opacity;\r\n }\r\n }\r\n \r\n saveCaptionPreferences() {\r\n this.storage.saveCaptionPreferences({\r\n fontSize: this.player.options.captionsFontSize,\r\n fontFamily: this.player.options.captionsFontFamily,\r\n color: this.player.options.captionsColor,\r\n backgroundColor: this.player.options.captionsBackgroundColor,\r\n opacity: this.player.options.captionsOpacity\r\n });\r\n }\r\n\r\n init() {\r\n this.createElement();\r\n this.loadTracks();\r\n this.attachEvents();\r\n\r\n // Only enable captions via captionsDefault option if no default track was found\r\n // (loadTracks() already enables tracks with the default attribute)\r\n if (this.player.options.captionsDefault && this.tracks.length > 0 && !this.currentTrack) {\r\n this.enable();\r\n }\r\n }\r\n\r\n createElement() {\r\n // The native <track> is rendered hidden and captions are drawn into\r\n // this overlay, so assistive tech cannot read cues from the track\r\n // itself. aria-live=\"polite\" makes each cue change announceable for\r\n // screen-reader users (notably deaf-blind/braille users) without\r\n // interrupting other speech (WCAG 1.2.2, 4.1.3).\r\n this.element = DOMUtils.createElement('div', {\r\n className: `${this.player.options.classPrefix}-captions`,\r\n attributes: {\r\n 'role': 'region',\r\n 'aria-label': i18n.t('player.captions'),\r\n 'aria-live': 'polite'\r\n }\r\n });\r\n\r\n // Apply caption styles\r\n this.updateStyles();\r\n\r\n // Append to videoWrapper if it exists, otherwise to container\r\n const target = this.player.videoWrapper || this.player.container;\r\n target.appendChild(this.element);\r\n }\r\n\r\n loadTracks() {\r\n const textTracks = this.player.element.textTracks;\r\n let defaultTrackIndex = -1;\r\n\r\n // Safari's native HLS can expose both SUBTITLES (WebVTT sidecar) and\r\n // CLOSED-CAPTIONS (inband CEA-608) groups as separate TextTrack objects\r\n // with the same language and label. Deduplicate to one menu entry per\r\n // language+label, storing the extras as alternatives. enable() will\r\n // listen on all of them and use whichever delivers cues first.\r\n const seen = new Map();\r\n\r\n for (let i = 0; i < textTracks.length; i++) {\r\n const track = textTracks[i];\r\n if (!track) continue;\r\n\r\n if ((track.kind === 'subtitles' || track.kind === 'captions') && !track._vidplyStale) {\r\n track.mode = 'hidden';\r\n\r\n const dedupeKey = `${track.language}|${track.label}`;\r\n const existing = seen.get(dedupeKey);\r\n\r\n if (existing) {\r\n existing.alternatives.push(track);\r\n continue;\r\n }\r\n\r\n const trackElement = this.player.findTrackElement(track) as Element | undefined;\r\n const isDefault = trackElement ? trackElement.hasAttribute('default') : false;\r\n const entry: CaptionTrackEntry = {\r\n track,\r\n language: track.language,\r\n label: deriveTrackLabel(track.label, track.language),\r\n kind: track.kind,\r\n index: i,\r\n isDefault,\r\n alternatives: []\r\n };\r\n\r\n this.tracks.push(entry);\r\n seen.set(dedupeKey, entry);\r\n\r\n if (isDefault) {\r\n defaultTrackIndex = this.tracks.length - 1;\r\n }\r\n }\r\n }\r\n \r\n if (defaultTrackIndex >= 0) {\r\n requestAnimationFrame(() => {\r\n this.enable(defaultTrackIndex);\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Sync hls.js subtitle rendition to match the given language.\r\n * Matches by lang, language, or falls back to name/label.\r\n */\r\n private _syncHlsSubtitleTrack(targetLang: string, targetLabel?: string) {\r\n const renderer = this.player.renderer;\r\n if (!renderer?.hls || !renderer.hls.subtitleTracks?.length) return;\r\n\r\n const tracks = renderer.hls.subtitleTracks as Array<{ lang?: string; name?: string }>;\r\n let hlsIndex = tracks.findIndex(\r\n (t) => t.lang === targetLang\r\n );\r\n // Fallback: match by name/label\r\n if (hlsIndex < 0 && targetLabel) {\r\n hlsIndex = tracks.findIndex(\r\n (t) => t.name === targetLabel\r\n );\r\n }\r\n if (hlsIndex >= 0 && renderer.hls.subtitleTrack !== hlsIndex) {\r\n renderer.hls.subtitleTrack = hlsIndex;\r\n this.player.log(`HLS subtitle track set to index ${hlsIndex} (${targetLang})`, 'info');\r\n }\r\n }\r\n\r\n attachEvents() {\r\n this.player.on('timeupdate', () => {\r\n this.updateCaptions();\r\n });\r\n\r\n // When hls.js finishes loading new subtitle cues, refresh captions\r\n this.player.on('textcuesupdate', () => {\r\n this.updateCaptions();\r\n });\r\n\r\n this.player.on('captionschange', () => {\r\n this.updateStyles();\r\n });\r\n \r\n // Debounced resize handler to avoid excessive recalculations\r\n this.debouncedPositionCaptions = debounce(() => {\r\n this.positionCaptionsOnMobile();\r\n }, 150);\r\n \r\n window.addEventListener('resize', this.debouncedPositionCaptions, {\r\n signal: this.player.lifecycleSignal\r\n });\r\n \r\n // Recalculate on fullscreen change with RAF\r\n this.player.on('enterfullscreen', () => {\r\n rafWithTimeout(() => this.positionCaptionsOnMobile(), 100);\r\n });\r\n \r\n this.player.on('exitfullscreen', () => {\r\n rafWithTimeout(() => this.positionCaptionsOnMobile(), 100);\r\n });\r\n }\r\n\r\n enable(trackIndex = 0) {\r\n if (this.tracks.length === 0) {\r\n return;\r\n }\r\n\r\n // Disable current track and clean up alternative listeners\r\n this._cleanupTrackListeners();\r\n\r\n // Enable selected track\r\n const selectedTrack = this.tracks[trackIndex];\r\n\r\n if (selectedTrack && selectedTrack.track) {\r\n selectedTrack.track.mode = 'hidden';\r\n this.currentTrack = selectedTrack;\r\n this.player.state.captionsEnabled = true;\r\n\r\n if (selectedTrack.language) {\r\n this.element.setAttribute('lang', selectedTrack.language);\r\n }\r\n\r\n // Bind handlers to locals so the non-null narrowing survives both\r\n // closure captures and `this.X = …` reassignments. Without this,\r\n // `addEventListener(this.cueChangeHandler)` widens to `… | null`\r\n // and stops type-checking even though we just assigned a function.\r\n const cueChangeHandler: CueChangeHandler = () => {\r\n this.updateCaptions();\r\n };\r\n this.cueChangeHandler = cueChangeHandler;\r\n selectedTrack.track.addEventListener('cuechange', cueChangeHandler);\r\n\r\n // Native HLS dedup: Safari can expose duplicate TextTrack objects\r\n // for the same language (SUBTITLES vs CLOSED-CAPTIONS groups) but\r\n // only one actually delivers cues. Listen on all alternatives and\r\n // swap to whichever fires first with real cues.\r\n if (selectedTrack.alternatives && selectedTrack.alternatives.length > 0) {\r\n const altCueChangeHandler: CueChangeHandler = () => {\r\n if (this.currentTrack !== selectedTrack) return;\r\n for (const alt of selectedTrack.alternatives) {\r\n if (alt.activeCues && alt.activeCues.length > 0) {\r\n this.player.log(`Switching to alternative caption track for \"${selectedTrack.label}\"`, 'info');\r\n selectedTrack.track.removeEventListener('cuechange', cueChangeHandler);\r\n selectedTrack.alternatives.forEach((a) => a.removeEventListener('cuechange', altCueChangeHandler));\r\n selectedTrack.track = alt;\r\n selectedTrack.track.addEventListener('cuechange', cueChangeHandler);\r\n this._altCueChangeHandler = null;\r\n this.updateCaptions();\r\n return;\r\n }\r\n }\r\n };\r\n this._altCueChangeHandler = altCueChangeHandler;\r\n selectedTrack.alternatives.forEach((alt) => {\r\n alt.mode = 'hidden';\r\n alt.addEventListener('cuechange', altCueChangeHandler);\r\n });\r\n }\r\n\r\n // The \"ready\" sense actually lives on the underlying <track> HTML\r\n // element — not on the TextTrack itself, which has no `readyState`\r\n // or `load` event. Look the element up so this is type-safe and\r\n // also fires the load callback in browsers where TextTrack alone\r\n // would never have done so.\r\n const trackElement = this.player.findTrackElement(selectedTrack.track) as HTMLTrackElement | undefined;\r\n const ensureTrackReady = () => {\r\n if (trackElement && trackElement.readyState < 2) {\r\n const onTrackLoad = () => {\r\n trackElement.removeEventListener('load', onTrackLoad);\r\n trackElement.removeEventListener('error', onTrackLoad);\r\n requestAnimationFrame(() => {\r\n if (this.currentTrack && this.currentTrack.track === selectedTrack.track) {\r\n this.updateCaptions();\r\n }\r\n });\r\n };\r\n trackElement.addEventListener('load', onTrackLoad, { once: true });\r\n trackElement.addEventListener('error', onTrackLoad, { once: true });\r\n } else {\r\n requestAnimationFrame(() => {\r\n if (this.currentTrack && this.currentTrack.track === selectedTrack.track) {\r\n this.updateCaptions();\r\n }\r\n });\r\n }\r\n };\r\n\r\n requestAnimationFrame(() => {\r\n if (this.currentTrack && this.currentTrack.track === selectedTrack.track) {\r\n ensureTrackReady();\r\n }\r\n });\r\n\r\n // Sync hls.js subtitle rendition to match the selected track language\r\n this._syncHlsSubtitleTrack(selectedTrack.language, selectedTrack.label);\r\n\r\n this.player.emit('captionsenabled', selectedTrack);\r\n }\r\n }\r\n\r\n _cleanupTrackListeners() {\r\n if (this.currentTrack && this.currentTrack.track) {\r\n const cueChangeHandler = this.cueChangeHandler;\r\n if (cueChangeHandler) {\r\n this.currentTrack.track.removeEventListener('cuechange', cueChangeHandler);\r\n }\r\n const altCueChangeHandler = this._altCueChangeHandler;\r\n if (altCueChangeHandler && this.currentTrack.alternatives) {\r\n this.currentTrack.alternatives.forEach((alt) => {\r\n alt.removeEventListener('cuechange', altCueChangeHandler);\r\n });\r\n }\r\n this.currentTrack.track.mode = 'hidden';\r\n }\r\n this._altCueChangeHandler = null;\r\n }\r\n\r\n disable() {\r\n this._cleanupTrackListeners();\r\n this.currentTrack = null;\r\n\r\n this.element.style.display = 'none';\r\n this.element.replaceChildren();\r\n this.element.removeAttribute('lang');\r\n this.currentCue = null;\r\n this.player.state.captionsEnabled = false;\r\n this.player.emit('captionsdisabled');\r\n }\r\n\r\n updateCaptions() {\r\n if (!this.currentTrack || !this.currentTrack.track) {\r\n return;\r\n }\r\n\r\n // When the renderer handles its own caption display (e.g. dash.js TTML\r\n // rendering div), skip VidPly's caption overlay entirely.\r\n if (this.player.renderer?.handlesOwnCaptions?.()) {\r\n return;\r\n }\r\n\r\n // Ensure track mode is set to 'hidden' (not 'disabled') to receive cue updates\r\n // 'hidden' mode loads cues and fires cuechange events, but doesn't show native captions\r\n if (this.currentTrack.track.mode === 'disabled') {\r\n this.currentTrack.track.mode = 'hidden';\r\n }\r\n\r\n if (this.currentTrack.track.mode === 'showing') {\r\n this.currentTrack.track.mode = 'hidden';\r\n }\r\n\r\n if (!this.currentTrack.track.activeCues) {\r\n if (this.currentTrack.track.cues && this.currentTrack.track.cues.length > 0) {\r\n if (this.currentCue) {\r\n this.element.replaceChildren();\r\n this.element.style.display = 'none';\r\n this.currentCue = null;\r\n }\r\n }\r\n return;\r\n }\r\n\r\n const activeCues = this.currentTrack.track.activeCues;\r\n const isAudioPlayer = this.player.element.tagName.toLowerCase() === 'audio';\r\n\r\n if (activeCues.length > 0) {\r\n // VTT/SRT subtitle tracks deliver `VTTCue` instances (which extend\r\n // TextTrackCue with `.text`). The lib.dom typing widens this to\r\n // `TextTrackCue`, so cast at the boundary.\r\n const cue = activeCues[0] as VTTCue;\r\n\r\n if (this.currentCue !== cue) {\r\n this.currentCue = cue;\r\n\r\n const rawText = cue.text || '';\r\n if (!rawText.trim()) {\r\n return;\r\n }\r\n\r\n const fragment = DOMUtils.renderVTTToDOM(rawText);\r\n\r\n if (isAudioPlayer) {\r\n const existingCues = this.element.querySelectorAll(`.${this.player.options.classPrefix}-caption-cue`);\r\n existingCues.forEach((el: Element) => el.classList.remove(`${this.player.options.classPrefix}-caption-active`));\r\n\r\n const cueId = `cue-${cue.startTime}-${cue.endTime}`;\r\n let cueElement = this.element.querySelector(`[data-cue-id=\"${cueId}\"]`);\r\n\r\n if (!cueElement) {\r\n cueElement = document.createElement('div');\r\n cueElement.className = `${this.player.options.classPrefix}-caption-cue`;\r\n cueElement.setAttribute('data-cue-id', cueId);\r\n cueElement.replaceChildren(fragment);\r\n this.element.appendChild(cueElement);\r\n } else {\r\n cueElement.replaceChildren(fragment);\r\n }\r\n\r\n cueElement.classList.add(`${this.player.options.classPrefix}-caption-active`);\r\n\r\n requestAnimationFrame(() => {\r\n if (cueElement) {\r\n cueElement.scrollIntoView(reducedMotionScrollOptions('center'));\r\n }\r\n });\r\n } else {\r\n this.element.replaceChildren(fragment);\r\n }\r\n\r\n this.element.style.display = 'block';\r\n this.positionCaptionsOnMobile();\r\n this.player.emit('captionchange', cue);\r\n }\r\n } else if (this.currentCue) {\r\n if (!isAudioPlayer) {\r\n this.element.replaceChildren();\r\n this.element.style.display = 'none';\r\n }\r\n this.currentCue = null;\r\n }\r\n }\r\n \r\n positionCaptionsOnMobile() {\r\n if (!this.element || this.element.style.display === 'none') {\r\n return;\r\n }\r\n \r\n const isFullscreen = this.player.state?.fullscreen || false;\r\n const mobile = isMobile();\r\n \r\n if (!mobile && !isFullscreen) {\r\n // Reset to CSS defaults on desktop\r\n this.element.style.bottom = '';\r\n return;\r\n }\r\n \r\n const controls = this.player.controlBar?.element;\r\n if (!controls) {\r\n return;\r\n }\r\n \r\n // Use requestAnimationFrame to ensure layout is complete\r\n requestAnimationFrame(() => {\r\n if (!this.element || this.element.style.display === 'none') {\r\n return;\r\n }\r\n \r\n const controlsRect = controls.getBoundingClientRect();\r\n if (!this.player.videoWrapper) return;\r\n const wrapperRect = this.player.videoWrapper.getBoundingClientRect();\r\n const bottomOffset = wrapperRect.bottom - controlsRect.top + 16;\r\n \r\n this.element.style.bottom = `${bottomOffset}px`;\r\n \r\n if (this.player.options.debug) {\r\n console.log('[VidPly] Caption position:', {\r\n mobile,\r\n isFullscreen,\r\n controlsHeight: controlsRect.height,\r\n bottomOffset: `${bottomOffset}px`\r\n });\r\n }\r\n });\r\n }\r\n\r\n // VTT formatting is parsed via DOMUtils.renderVTTToDOM() which returns\r\n // a DocumentFragment built with createElement / createTextNode. The\r\n // previous regex-based parseVTTFormatting helper was removed because\r\n // it produced strings that were assigned to `innerHTML`, which is\r\n // unsafe for cue text from third-party WebVTT sources.\r\n\r\n updateStyles() {\r\n if (!this.element) return;\r\n\r\n const options = this.player.options;\r\n\r\n this.element.style.fontSize = options.captionsFontSize;\r\n this.element.style.fontFamily = options.captionsFontFamily;\r\n this.element.style.color = options.captionsColor;\r\n this.element.style.backgroundColor = this.hexToRgba(\r\n options.captionsBackgroundColor,\r\n options.captionsOpacity\r\n );\r\n }\r\n\r\n hexToRgba(hex: string, alpha: number) {\r\n const result = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(hex);\r\n if (result && result[1] && result[2] && result[3]) {\r\n return `rgba(${parseInt(result[1], 16)}, ${parseInt(result[2], 16)}, ${parseInt(result[3], 16)}, ${alpha})`;\r\n }\r\n return hex;\r\n }\r\n\r\n setCaptionStyle(property: string, value: string | number) {\r\n // The persisted preference shape is mixed (string for font/colors,\r\n // number for opacity). Cast through the player's loose options\r\n // index so we don't have to split this method into 5 strongly-typed\r\n // overloads when the underlying string-keyed bag is itself loose.\r\n switch (property) {\r\n case 'fontSize':\r\n this.player.options.captionsFontSize = String(value);\r\n break;\r\n case 'fontFamily':\r\n this.player.options.captionsFontFamily = String(value);\r\n break;\r\n case 'color':\r\n this.player.options.captionsColor = String(value);\r\n break;\r\n case 'backgroundColor':\r\n this.player.options.captionsBackgroundColor = String(value);\r\n break;\r\n case 'opacity':\r\n this.player.options.captionsOpacity = Number(value);\r\n break;\r\n }\r\n\r\n this.updateStyles();\r\n this.saveCaptionPreferences();\r\n this.player.emit('captionschange');\r\n }\r\n\r\n getAvailableTracks() {\r\n return this.tracks.map((t, index) => ({\r\n index,\r\n language: t.language,\r\n label: t.label || t.language,\r\n kind: t.kind\r\n }));\r\n }\r\n\r\n /**\r\n * Refresh tracks list - useful when HLS adds subtitle tracks dynamically\r\n */\r\n refreshTracks() {\r\n // Store current track info to restore after refresh\r\n const currentLanguage = this.currentTrack?.language;\r\n const wasEnabled = this.player.state.captionsEnabled;\r\n \r\n // Disable current caption\r\n if (this.currentTrack) {\r\n this.disable();\r\n }\r\n \r\n // Clear existing tracks\r\n this.tracks = [];\r\n \r\n // Reload tracks from video element\r\n this.loadTracks();\r\n \r\n this.player.log(`Caption tracks refreshed, found ${this.tracks.length} tracks`, 'info');\r\n \r\n // Try to restore previous track if it exists\r\n if (wasEnabled && currentLanguage && this.tracks.length > 0) {\r\n const matchingIndex = this.tracks.findIndex((t) => t.language === currentLanguage);\r\n if (matchingIndex >= 0) {\r\n this.enable(matchingIndex);\r\n }\r\n }\r\n \r\n return this.tracks.length;\r\n }\r\n\r\n switchTrack(trackIndex: number) {\r\n if (trackIndex >= 0 && trackIndex < this.tracks.length) {\r\n this.disable();\r\n this.enable(trackIndex);\r\n }\r\n }\r\n\r\n destroy() {\r\n this.disable();\r\n\r\n if (this.element && this.element.parentNode) {\r\n this.element.parentNode.removeChild(this.element);\r\n }\r\n }\r\n}\r\n\r\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|