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
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/controls/TranscriptManager.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Transcript Manager Component\n * Manages transcript display and interaction\n */\n\nimport { DOMUtils } from '../utils/DOMUtils.js';\nimport { TimeUtils } from '../utils/TimeUtils.js';\nimport { createIconElement } from '../icons/Icons.js';\nimport { i18n } from '../i18n/i18n.js';\nimport { StorageManager } from '../utils/StorageManager.js';\nimport { focusElement } from '../utils/FocusUtils.js';\nimport { createMenuItem } from '../utils/MenuUtils.js';\nimport { DraggableResizable } from '../utils/DraggableResizable.js';\nimport { createLabeledSelect, preventDragOnElement } from '../utils/FormUtils.js';\nimport { DraggablePanel } from '../utils/DraggablePanel.js';\nimport { deriveTrackLabel } from '../utils/TrackLabelUtils.js';\nimport type { Player } from '../core/Player.js';\nimport type { Renderer } from '../types/renderer.js';\n\ntype TranscriptCue = TextTrackCue;\ntype TranscriptTrack = TextTrack & { _vidplyStale?: boolean };\ntype TranscriptLanguageInfo = {\n language: string;\n label: string;\n track: TranscriptTrack;\n};\ntype TranscriptCueItem = {\n cue: TranscriptCue;\n type: 'caption' | 'description';\n};\ntype TranscriptEntry = {\n element: HTMLElement;\n cue: TranscriptCue;\n type: 'caption' | 'description';\n startTime: number;\n endTime: number;\n};\n\ninterface TranscriptStyleOptions {\n fontSize: string;\n fontFamily: string;\n color: string;\n backgroundColor: string;\n opacity: number;\n [key: string]: unknown;\n}\n\ninterface TranscriptHandlers {\n timeupdate: () => void;\n seeked: () => void;\n audiodescriptionenabled: () => void;\n audiodescriptiondisabled: () => void;\n textcuesupdate: (() => void) | null;\n resize: (() => void) | null;\n settingsClick: ((e: MouseEvent) => void) | null;\n settingsKeydown: ((e: KeyboardEvent) => void) | null;\n documentClick: ((e: MouseEvent) => void) | null;\n styleDialogKeydown: ((e: KeyboardEvent) => void) | null;\n floatingchange: ((state: 'pinned' | 'auto' | null) => void) | null;\n}\n\ntype TimerHandle = ReturnType<typeof setTimeout>;\n\nexport class TranscriptManager {\n player: Player;\n _cueUpdateTimeout: TimerHandle | null;\n autoscrollCheckbox: HTMLInputElement | null = null;\n autoscrollEnabled: boolean;\n availableTranscriptLanguages: TranscriptLanguageInfo[];\n currentActiveEntry: TranscriptEntry | null;\n currentTranscriptLanguage: string | null;\n customKeyHandler: ((e: KeyboardEvent) => void) | null = null;\n /**\n * True once the style-dialog's outside-click listener has been\n * attached. The settings-menu's outside-click listener is now\n * owned by {@link DraggablePanel} and tracked there; this flag\n * covers the dialog half of the shared `handlers.documentClick`.\n */\n documentClickHandlerAdded: boolean = false;\n draggableResizable: DraggableResizable | null;\n handlers: TranscriptHandlers = {} as TranscriptHandlers;\n headerLeft: HTMLElement | null = null;\n isVisible: boolean;\n languageLabel: HTMLElement | null;\n languageSelector: HTMLSelectElement | null;\n languageSelectorHandler: ((e: Event) => void) | null;\n languageSelectorWrapper: HTMLElement | null = null;\n liveRegion: HTMLElement | null;\n metadataCueChangeHandler: (() => void) | null = null;\n metadataCues: TextTrackCue[];\n settingsButton: HTMLButtonElement | null;\n showTimestamps: boolean;\n showTimestampsButton: HTMLElement | null = null;\n showTimestampsText: Element | null = null;\n storage: StorageManager;\n styleDialog: HTMLElement | null;\n styleDialogJustOpened: boolean;\n styleDialogVisible: boolean;\n timeouts: Set<TimerHandle>;\n transcriptContent: HTMLElement | null = null;\n transcriptEntries: TranscriptEntry[];\n transcriptHeader: HTMLElement | null = null;\n transcriptResizeHandles: HTMLElement[];\n transcriptStyle: TranscriptStyleOptions;\n transcriptWindow: HTMLElement | null;\n _dashActiveLang: string | null;\n _vttCache: Map<string, TranscriptCueItem[]>;\n\n /**\n * Owns the settings-menu DOM scaffold, its outside-click\n * dismissal, keyboard navigation, viewport-aware positioning,\n * and the drag-mode / resize-mode toggle items. Instantiated\n * lazily once the header is built in {@link createTranscriptHeader}.\n */\n private _panel: DraggablePanel | null = null;\n\n // Back-compat getters for panel-owned state. External callers and\n // internal reads (e.g. `this.settingsMenuVisible` inside other\n // transcript methods) keep reading the same properties; the\n // setters are no-ops because the panel is now the authoritative\n // owner of those values.\n get settingsMenu(): HTMLElement | null {\n return this._panel?.settingsMenu ?? null;\n }\n set settingsMenu(_v: HTMLElement | null) {\n // panel-owned.\n }\n\n get settingsMenuVisible(): boolean {\n return this._panel?.settingsMenuVisible ?? false;\n }\n set settingsMenuVisible(_v: boolean) {\n // panel-owned.\n }\n\n get settingsMenuJustOpened(): boolean {\n return this._panel?.justOpened ?? false;\n }\n set settingsMenuJustOpened(_v: boolean) {\n // panel-owned.\n }\n\n get dragOptionButton(): HTMLElement | null {\n return this._panel?.dragOptionButton ?? null;\n }\n get dragOptionText(): Element | null {\n return this._panel?.dragOptionText ?? null;\n }\n get resizeOptionButton(): HTMLElement | null {\n return this._panel?.resizeOptionButton ?? null;\n }\n get resizeOptionText(): Element | null {\n return this._panel?.resizeOptionText ?? null;\n }\n\n constructor(player: Player) {\n this.player = player;\n this.transcriptWindow = null;\n this.transcriptEntries = [];\n this.metadataCues = [];\n this.currentActiveEntry = null;\n this.isVisible = false;\n \n // Storage manager\n this.storage = new StorageManager('vidply');\n \n // Draggable/Resizable utility\n this.draggableResizable = null;\n this.settingsButton = null;\n\n // Resize mode state\n this.transcriptResizeHandles = [];\n this.liveRegion = null;\n \n // Style dialog state\n this.styleDialog = null;\n this.styleDialogVisible = false;\n this.styleDialogJustOpened = false;\n \n // Language selector state\n this.languageSelector = null;\n this.languageLabel = null;\n this.currentTranscriptLanguage = null;\n this.availableTranscriptLanguages = [];\n this.languageSelectorHandler = null;\n \n const savedPreferences = this.storage.getTranscriptPreferences();\n \n this.autoscrollEnabled = typeof savedPreferences?.autoscroll === 'boolean' ? savedPreferences.autoscroll : true;\n \n this.showTimestamps = typeof savedPreferences?.showTimestamps === 'boolean' ? savedPreferences.showTimestamps : false;\n \n const savedFontSize = typeof savedPreferences?.fontSize === 'string' ? savedPreferences.fontSize : undefined;\n const savedFontFamily = typeof savedPreferences?.fontFamily === 'string' ? savedPreferences.fontFamily : undefined;\n const savedColor = typeof savedPreferences?.color === 'string' ? savedPreferences.color : undefined;\n const savedBackgroundColor = typeof savedPreferences?.backgroundColor === 'string' ? savedPreferences.backgroundColor : undefined;\n const savedOpacity = typeof savedPreferences?.opacity === 'number' ? savedPreferences.opacity : undefined;\n const optFontSize = typeof this.player.options.transcriptFontSize === 'string' ? this.player.options.transcriptFontSize : undefined;\n const optFontFamily = typeof this.player.options.transcriptFontFamily === 'string' ? this.player.options.transcriptFontFamily : undefined;\n const optColor = typeof this.player.options.transcriptColor === 'string' ? this.player.options.transcriptColor : undefined;\n const optBackgroundColor = typeof this.player.options.transcriptBackgroundColor === 'string' ? this.player.options.transcriptBackgroundColor : undefined;\n const optOpacity = typeof this.player.options.transcriptOpacity === 'number' ? this.player.options.transcriptOpacity : undefined;\n this.transcriptStyle = {\n fontSize: savedFontSize || optFontSize || '100%',\n fontFamily: savedFontFamily || optFontFamily || 'sans-serif',\n color: savedColor || optColor || '#ffffff',\n backgroundColor: savedBackgroundColor || optBackgroundColor || '#1e1e1e',\n opacity: savedOpacity ?? optOpacity ?? 0.98\n };\n \n // Store event handlers for cleanup\n this.handlers = {\n timeupdate: () => this.updateActiveEntry(),\n seeked: () => this.updateActiveEntry(),\n audiodescriptionenabled: () => {\n if (this.isVisible) {\n this.loadTranscriptData();\n }\n },\n audiodescriptiondisabled: () => {\n if (this.isVisible) {\n this.loadTranscriptData();\n }\n },\n textcuesupdate: null,\n resize: null,\n settingsClick: null,\n settingsKeydown: null,\n documentClick: null,\n styleDialogKeydown: null,\n floatingchange: null\n };\n\n this._cueUpdateTimeout = null;\n this._dashActiveLang = null;\n this._vttCache = new Map<string, TranscriptCueItem[]>();\n \n // Timeout management (for cleanup)\n this.timeouts = new Set();\n \n this.init();\n }\n\n init() {\n // Set up metadata handling immediately (independent of transcript display)\n this.setupMetadataHandlingOnLoad();\n \n // Listen for time updates to highlight active transcript entry\n this.player.on('timeupdate', this.handlers.timeupdate);\n this.player.on('seeked', this.handlers.seeked);\n \n // Listen for audio description changes to reload transcript\n this.player.on('audiodescriptionenabled', this.handlers.audiodescriptionenabled);\n this.player.on('audiodescriptiondisabled', this.handlers.audiodescriptiondisabled);\n\n // Listen for text cue updates (cues arrive incrementally as segments load in HLS/DASH).\n // Skip re-render when we already fetched the full VTT for the active language.\n this.handlers.textcuesupdate = () => {\n if (!this.isVisible) return;\n if (this.currentTranscriptLanguage && this._vttCache.has(this.currentTranscriptLanguage)) return;\n if (this._cueUpdateTimeout) {\n this.clearManagedTimeout(this._cueUpdateTimeout);\n }\n this._cueUpdateTimeout = this.setManagedTimeout(() => {\n this._cueUpdateTimeout = null;\n this.loadTranscriptData();\n }, 400);\n };\n this.player.on('textcuesupdate', this.handlers.textcuesupdate);\n \n // Reposition transcript when entering/exiting fullscreen\n this.player.on('fullscreenchange', () => {\n if (this.isVisible) {\n // Re-setup drag/drop when entering/exiting fullscreen on mobile devices\n // This enables drag/resize when entering fullscreen on mobile\n const isMobile = window.innerWidth < 768;\n if (isMobile) {\n this.setupDragAndDrop();\n }\n \n // Only auto-position if user hasn't manually positioned it\n if (!this.draggableResizable || !this.draggableResizable.manuallyPositioned) {\n // Add a small delay to ensure DOM has updated after fullscreen transition\n this.setManagedTimeout(() => this.positionTranscript(), 100);\n }\n }\n });\n\n // Collapse the transcript whenever the player enters the floating\n // shell. The floating miniplayer is intentionally narrow and only\n // shows a whitelisted subset of controls, so a full-size transcript\n // popup would either overflow the shell or travel with it and break\n // the miniplayer UX. We redock the transcript on exit by simply\n // letting the user re-open it if desired.\n this.handlers.floatingchange = (state) => {\n if (state && this.isVisible) {\n this.hideTranscript();\n }\n };\n this.player.on('floatingchange', this.handlers.floatingchange);\n }\n\n /**\n * For streaming renderers (DASH), tell the renderer to activate the text\n * track for `lang` so dash.js starts downloading subtitle segments and\n * populating cues. Skips the call if the language is already active.\n */\n private _requestStreamingTrack(lang: string | null) {\n if (!lang) return;\n const renderer = this.player.renderer as Renderer | null | undefined;\n if (renderer?.isStreaming && typeof renderer.activateTextTrackForLanguage === 'function') {\n if (this._dashActiveLang !== lang) {\n this._dashActiveLang = lang;\n renderer.activateTextTrackForLanguage(lang);\n }\n }\n }\n\n /**\n * Toggle transcript window visibility\n */\n toggleTranscript() {\n if (this.isVisible) {\n this.hideTranscript();\n } else {\n this.showTranscript();\n }\n }\n\n /**\n * Show transcript window\n */\n showTranscript() {\n // The floating miniplayer hides the transcript control button and is\n // too narrow to host the transcript window, so refuse to open while\n // floating. Covers both the control-bar click (already gated by CSS)\n // and the keyboard shortcut / public API paths.\n if (this.player.state?.floating) {\n return;\n }\n\n // Always invalidate track cache to get fresh HLS subtitle tracks\n this.player.invalidateTrackCache();\n \n if (this.transcriptWindow) {\n this.transcriptWindow.style.display = 'flex';\n this.isVisible = true;\n \n // Reload transcript data to get fresh HLS tracks\n this.loadTranscriptData();\n this._requestStreamingTrack(this.currentTranscriptLanguage);\n this.updateLanguageSelector();\n\n if (this.player.controlBar && typeof this.player.controlBar.updateTranscriptButton === 'function') {\n this.player.controlBar.updateTranscriptButton();\n }\n \n // Focus the settings button for keyboard accessibility\n focusElement(this.settingsButton, { delay: 150 });\n return;\n }\n\n this.createTranscriptWindow();\n this.loadTranscriptData();\n this._requestStreamingTrack(this.currentTranscriptLanguage);\n \n const transcriptWindow = this.transcriptWindow as HTMLElement | null;\n if (transcriptWindow) {\n transcriptWindow.style.display = 'flex';\n \n if (!this.draggableResizable || !this.draggableResizable.manuallyPositioned) {\n this.setManagedTimeout(() => this.positionTranscript(), 0);\n }\n \n focusElement(this.settingsButton, { delay: 150 });\n }\n this.isVisible = true;\n }\n\n /**\n * Hide transcript window\n */\n hideTranscript({ focusButton = false } = {}) {\n if (this.transcriptWindow) {\n this.transcriptWindow.style.display = 'none';\n this.isVisible = false;\n }\n if (this.draggableResizable && this.draggableResizable.pointerResizeMode) {\n this.draggableResizable.disablePointerResizeMode();\n this.updateResizeOptionState();\n }\n this.hideResizeModeIndicator();\n this.announceLive('');\n\n // Update transcript button state in control bar\n if (this.player.controlBar && typeof this.player.controlBar.updateTranscriptButton === 'function') {\n this.player.controlBar.updateTranscriptButton();\n }\n\n if (focusButton) {\n const transcriptButton = this.player.controlBar?.controls?.transcript;\n if (transcriptButton && typeof transcriptButton.focus === 'function') {\n transcriptButton.focus({ preventScroll: true });\n }\n }\n }\n\n /**\n * Create the transcript window UI\n */\n createTranscriptWindow() {\n this.transcriptWindow = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-transcript-window`,\n attributes: {\n 'role': 'dialog',\n 'aria-label': i18n.t('transcript.ariaLabel'),\n 'tabindex': '-1'\n }\n });\n\n // Header (draggable)\n this.transcriptHeader = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-transcript-header`,\n attributes: {\n 'tabindex': '0'\n }\n });\n\n // Header left side (settings button + title)\n this.headerLeft = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-transcript-header-left`\n });\n\n // Settings button\n const settingsAriaLabel = i18n.t('transcript.settingsMenu');\n this.settingsButton = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-transcript-settings`,\n attributes: {\n 'type': 'button',\n 'aria-label': settingsAriaLabel,\n 'aria-expanded': 'false'\n }\n });\n this.settingsButton.appendChild(createIconElement('settings'));\n DOMUtils.attachTooltip(this.settingsButton, settingsAriaLabel, this.player.options.classPrefix);\n\n // The panel owns the settings-menu DOM, lifecycle, and the drag\n // + resize toggle items. Transcript-specific extras (style dialog\n // opener and timestamps toggle) are appended via the extras\n // builder below. The manager keeps ownership of the toggle\n // semantics (announcements, focus target), which the panel\n // invokes via the click callbacks.\n this._panel = new DraggablePanel({\n player: this.player,\n namespace: 'transcript',\n settingsButton: this.settingsButton,\n getDraggable: () => this.draggableResizable,\n i18nKeys: {\n enableDrag: 'transcript.enableDragMode',\n disableDrag: 'transcript.disableDragMode',\n enableDragAria: 'transcript.enableDragModeAria',\n disableDragAria: 'transcript.disableDragModeAria',\n enableResize: 'transcript.enableResizeMode',\n disableResize: 'transcript.disableResizeMode',\n enableResizeAria: 'transcript.enableResizeModeAria',\n disableResizeAria: 'transcript.disableResizeModeAria',\n closeMenu: 'transcript.closeMenu',\n },\n menuAlign: 'left',\n getMenuParent: () =>\n this.headerLeft ?? this.transcriptHeader ?? this.transcriptWindow,\n // The transcript window itself is the anchor for the mode badge\n // so it sits above the panel regardless of where the settings\n // menu was opened from. The default class name is derived from\n // the namespace → `{classPrefix}-transcript-mode-badge`.\n getBadgeHost: () => this.transcriptWindow,\n onDragItemClick: (panel) => {\n this.toggleKeyboardDragMode();\n panel.hide();\n },\n onResizeItemClick: (panel) => {\n const enabled = this.toggleResizeMode({ focus: false });\n if (enabled) {\n panel.hide({ focusButton: false });\n setTimeout(() => {\n this.transcriptWindow?.focus({ preventScroll: true });\n }, 20);\n } else {\n panel.hide({ focusButton: true });\n }\n },\n buildExtraItems: ({ menu, itemClass, classPrefix, stripInlineTooltip }) => {\n const styleOption = createMenuItem({\n classPrefix,\n itemClass,\n icon: 'settings',\n label: 'transcript.styleTranscript',\n onClick: (e: Event) => {\n e.preventDefault();\n e.stopPropagation();\n this._panel?.hide();\n setTimeout(() => {\n this.showStyleDialog();\n }, 50);\n },\n });\n stripInlineTooltip(styleOption);\n menu.appendChild(styleOption);\n\n const timestampsOption = createMenuItem({\n classPrefix,\n itemClass,\n icon: 'clock',\n label: 'transcript.showTimestamps',\n hasTextClass: true,\n onClick: () => {\n this.toggleShowTimestamps();\n },\n });\n timestampsOption.setAttribute('role', 'switch');\n timestampsOption.setAttribute(\n 'aria-checked',\n this.showTimestamps ? 'true' : 'false'\n );\n stripInlineTooltip(timestampsOption);\n this.showTimestampsButton = timestampsOption;\n this.showTimestampsText = timestampsOption.querySelector(\n `.${classPrefix}-settings-text`\n );\n this.updateShowTimestampsState();\n menu.appendChild(timestampsOption);\n },\n });\n\n this.handlers.settingsClick = (e: MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n this._panel?.markJustOpenedForClick();\n this._panel?.toggle();\n };\n this.settingsButton.addEventListener('click', this.handlers.settingsClick);\n \n // Keyboard handler for settings button\n this.handlers.settingsKeydown = (e: KeyboardEvent) => {\n // D key to toggle keyboard drag mode\n if (e.key === 'd' || e.key === 'D') {\n e.preventDefault();\n e.stopPropagation();\n this.toggleKeyboardDragMode();\n }\n // R key to toggle resize mode\n else if (e.key === 'r' || e.key === 'R') {\n e.preventDefault();\n e.stopPropagation();\n this.toggleResizeMode();\n }\n // Escape to close menu if open\n else if (e.key === 'Escape' && this.settingsMenuVisible) {\n e.preventDefault();\n e.stopPropagation();\n this.hideSettingsMenu();\n }\n };\n this.settingsButton.addEventListener('keydown', this.handlers.settingsKeydown);\n\n const title = DOMUtils.createElement('h3', {\n textContent: `${i18n.t('transcript.title')}. ${i18n.t('transcript.dragResizePrompt')}`\n });\n\n // Autoscroll checkbox\n const autoscrollId = `${this.player.options.classPrefix}-transcript-autoscroll-${Date.now()}`;\n \n const autoscrollLabel = DOMUtils.createElement('label', {\n className: `${this.player.options.classPrefix}-transcript-autoscroll-label`,\n attributes: {\n 'for': autoscrollId\n }\n });\n \n this.autoscrollCheckbox = DOMUtils.createElement('input', {\n attributes: {\n 'id': autoscrollId,\n 'type': 'checkbox'\n }\n });\n // Set checked property directly (boolean attribute, not \"true\" string)\n if (this.autoscrollEnabled) {\n this.autoscrollCheckbox.checked = true;\n }\n \n const autoscrollText = DOMUtils.createElement('span', {\n textContent: i18n.t('transcript.autoscroll'),\n className: `${this.player.options.classPrefix}-transcript-autoscroll-text`\n });\n \n autoscrollLabel.appendChild(this.autoscrollCheckbox);\n autoscrollLabel.appendChild(autoscrollText);\n \n // Handle autoscroll checkbox change\n this.autoscrollCheckbox.addEventListener('change', (e: Event) => {\n this.autoscrollEnabled = (e.target as HTMLInputElement).checked;\n this.saveAutoscrollPreference();\n });\n\n preventDragOnElement(autoscrollLabel);\n\n this.transcriptHeader.appendChild(title);\n this.headerLeft.appendChild(this.settingsButton);\n this.headerLeft.appendChild(autoscrollLabel);\n \n // Language selector (will be populated after tracks are loaded)\n const selectId = `${this.player.options.classPrefix}-transcript-language-select-${Date.now()}`;\n const { label: languageLabel, select: languageSelector } = createLabeledSelect({\n classPrefix: this.player.options.classPrefix,\n labelClass: `${this.player.options.classPrefix}-transcript-language-label`,\n selectClass: `${this.player.options.classPrefix}-transcript-language-select`,\n labelText: 'settings.language',\n selectId: selectId,\n hidden: false // Don't hide individual elements, we'll hide the wrapper instead\n });\n \n this.languageLabel = languageLabel;\n this.languageSelector = languageSelector;\n \n const languageSelectorWrapper = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-transcript-language-wrapper`,\n attributes: {\n 'style': 'display: none;'\n }\n });\n languageSelectorWrapper.appendChild(languageLabel);\n languageSelectorWrapper.appendChild(languageSelector);\n this.languageSelectorWrapper = languageSelectorWrapper;\n \n preventDragOnElement(languageSelectorWrapper);\n \n if (this.headerLeft) {\n this.headerLeft.appendChild(languageSelectorWrapper);\n }\n\n const closeAriaLabel = i18n.t('transcript.close');\n const closeButton = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-transcript-close`,\n attributes: {\n 'type': 'button',\n 'aria-label': closeAriaLabel\n }\n });\n closeButton.appendChild(createIconElement('close'));\n DOMUtils.attachTooltip(closeButton, closeAriaLabel, this.player.options.classPrefix);\n closeButton.addEventListener('click', () => this.hideTranscript({ focusButton: true }));\n\n this.transcriptHeader.appendChild(this.headerLeft);\n this.transcriptHeader.appendChild(closeButton);\n\n // Content container\n this.transcriptContent = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-transcript-content`\n });\n\n this.transcriptWindow.appendChild(this.transcriptHeader);\n this.transcriptWindow.appendChild(this.transcriptContent);\n\n this.createResizeHandles();\n\n // Live region for announcements (screen reader feedback)\n this.liveRegion = DOMUtils.createElement('div', {\n className: 'vidply-sr-only',\n attributes: {\n 'aria-live': 'polite',\n 'aria-atomic': 'true'\n }\n });\n this.transcriptWindow.appendChild(this.liveRegion);\n\n // Append to player container\n this.player.container.appendChild(this.transcriptWindow);\n \n // Setup drag functionality FIRST (this will restore saved position if it exists)\n this.setupDragAndDrop();\n \n // Then position it next to the video wrapper ONLY if user hasn't manually positioned it\n // This ensures we don't overwrite saved positions from localStorage\n if (!this.draggableResizable || !this.draggableResizable.manuallyPositioned) {\n this.positionTranscript();\n }\n \n // Outside-click dismissal for the *style dialog* only. The\n // settings-menu equivalent is owned by the DraggablePanel, which\n // attaches its own listener (tied to the player's lifecycle\n // signal) when the menu is first opened.\n this.handlers.documentClick = (e: MouseEvent) => {\n if (this.styleDialogJustOpened) {\n return;\n }\n const target = e.target as Node | null;\n if (this.styleDialogVisible && this.styleDialog &&\n target && !this.styleDialog.contains(target)) {\n this.hideStyleDialog();\n }\n };\n this.documentClickHandlerAdded = false;\n \n // Re-position on window resize (debounced) - but only if not manually positioned\n let resizeTimeout: ReturnType<typeof setTimeout> | null = null;\n this.handlers.resize = () => {\n if (resizeTimeout) {\n this.clearManagedTimeout(resizeTimeout);\n }\n resizeTimeout = this.setManagedTimeout(() => {\n // Only auto-position if user hasn't manually moved it\n if (!this.draggableResizable || !this.draggableResizable.manuallyPositioned) {\n this.positionTranscript();\n }\n }, 100);\n };\n window.addEventListener('resize', this.handlers.resize, {\n signal: this.player.lifecycleSignal\n });\n }\n \n createResizeHandles() {\n const transcriptWindow = this.transcriptWindow;\n if (!transcriptWindow) return;\n\n const directions: Array<'n' | 's' | 'e' | 'w' | 'ne' | 'nw' | 'se' | 'sw'> = ['n', 's', 'e', 'w', 'ne', 'nw', 'se', 'sw'];\n this.transcriptResizeHandles = directions.map(direction => {\n const handle = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-transcript-resize-handle ${this.player.options.classPrefix}-transcript-resize-${direction}`,\n attributes: {\n 'data-direction': direction,\n 'data-vidply-managed-resize': 'true',\n 'aria-hidden': 'true'\n }\n });\n\n handle.style.display = 'none';\n transcriptWindow.appendChild(handle);\n return handle;\n });\n }\n\n /**\n * Position transcript window next to video\n */\n positionTranscript() {\n const playerWithVideoWrapper = this.player as Player & { videoWrapper?: HTMLElement | null };\n if (!this.transcriptWindow || !playerWithVideoWrapper.videoWrapper || !this.isVisible) return;\n \n // Don't auto-position if user has manually positioned it\n if (this.draggableResizable && this.draggableResizable.manuallyPositioned) {\n return;\n }\n \n const isMobile = window.innerWidth < 768;\n const videoRect = playerWithVideoWrapper.videoWrapper.getBoundingClientRect();\n \n // Check if player is in fullscreen mode\n const isFullscreen = this.player.state.fullscreen;\n \n if (isMobile && !isFullscreen) {\n // Mobile: Position directly underneath the video controls as part of the layout\n this.transcriptWindow.style.position = 'relative';\n this.transcriptWindow.style.left = '0';\n this.transcriptWindow.style.right = '0';\n this.transcriptWindow.style.bottom = 'auto';\n this.transcriptWindow.style.top = 'auto';\n this.transcriptWindow.style.width = '100%';\n this.transcriptWindow.style.maxWidth = '100%';\n this.transcriptWindow.style.maxHeight = '300px';\n this.transcriptWindow.style.height = 'auto';\n this.transcriptWindow.style.borderRadius = '0';\n this.transcriptWindow.style.transform = 'none';\n this.transcriptWindow.style.border = 'none';\n this.transcriptWindow.style.borderTop = '1px solid var(--vidply-border-light)';\n // Remove any empty border properties that might have been set\n this.transcriptWindow.style.removeProperty('border-right');\n this.transcriptWindow.style.removeProperty('border-bottom');\n this.transcriptWindow.style.removeProperty('border-left');\n // Remove border-image properties that can cause parse errors\n this.transcriptWindow.style.removeProperty('border-image');\n this.transcriptWindow.style.removeProperty('border-image-source');\n this.transcriptWindow.style.removeProperty('border-image-slice');\n this.transcriptWindow.style.removeProperty('border-image-width');\n this.transcriptWindow.style.removeProperty('border-image-outset');\n this.transcriptWindow.style.removeProperty('border-image-repeat');\n this.transcriptWindow.style.boxShadow = 'none';\n // Disable dragging on mobile\n if (this.transcriptHeader) {\n this.transcriptHeader.style.cursor = 'default';\n }\n \n // Insert directly after video wrapper to appear right under controls\n const videoWrapper = playerWithVideoWrapper.videoWrapper;\n if (videoWrapper && videoWrapper.parentNode && videoWrapper.nextSibling !== this.transcriptWindow) {\n videoWrapper.parentNode.insertBefore(this.transcriptWindow, videoWrapper.nextSibling);\n }\n } else if (isFullscreen) {\n // In fullscreen: position in bottom right corner inside the video\n this.transcriptWindow.style.position = 'fixed';\n this.transcriptWindow.style.left = 'auto';\n this.transcriptWindow.style.right = '20px';\n this.transcriptWindow.style.bottom = '80px'; // Above controls\n this.transcriptWindow.style.top = 'auto';\n this.transcriptWindow.style.maxHeight = 'calc(100vh - 180px)'; // Leave space for controls\n this.transcriptWindow.style.height = 'auto';\n const fullscreenMinWidth = 260;\n const fullscreenAvailable = Math.max(fullscreenMinWidth, window.innerWidth - 40);\n const fullscreenDesired = parseFloat(this.transcriptWindow.style.width) || 400;\n const fullscreenWidth = Math.max(fullscreenMinWidth, Math.min(fullscreenDesired, fullscreenAvailable));\n this.transcriptWindow.style.width = `${fullscreenWidth}px`;\n this.transcriptWindow.style.maxWidth = 'none';\n this.transcriptWindow.style.borderRadius = '8px';\n this.transcriptWindow.style.border = '1px solid var(--vidply-border)';\n // Remove borderTop and any other individual border properties to avoid empty values\n this.transcriptWindow.style.removeProperty('border-top');\n this.transcriptWindow.style.removeProperty('border-right');\n this.transcriptWindow.style.removeProperty('border-bottom');\n this.transcriptWindow.style.removeProperty('border-left');\n // Remove border-image properties that can cause parse errors\n this.transcriptWindow.style.removeProperty('border-image');\n this.transcriptWindow.style.removeProperty('border-image-source');\n this.transcriptWindow.style.removeProperty('border-image-slice');\n this.transcriptWindow.style.removeProperty('border-image-width');\n this.transcriptWindow.style.removeProperty('border-image-outset');\n this.transcriptWindow.style.removeProperty('border-image-repeat');\n // Enable dragging in fullscreen (including touch devices)\n if (this.transcriptHeader) {\n this.transcriptHeader.style.cursor = 'move';\n }\n \n // Move back to container for fullscreen\n if (this.transcriptWindow.parentNode !== this.player.container) {\n this.player.container.appendChild(this.transcriptWindow);\n }\n } else {\n // Desktop mode: position to the right of video, or overlay if insufficient space\n const transcriptWidth = parseFloat(this.transcriptWindow.style.width) || 400;\n const padding = 20;\n const minWidth = 260;\n const containerRect = this.player.container.getBoundingClientRect();\n\n const ensureContainerPositioned = () => {\n const computed = window.getComputedStyle(this.player.container);\n if (computed.position === 'static') {\n this.player.container.style.position = 'relative';\n }\n };\n\n ensureContainerPositioned();\n\n // Calculate available space to the right of the video\n // WCAG 1.4.10 Reflow: Avoid horizontal overflow and content being cut off\n const availableWidth = window.innerWidth - videoRect.right - padding;\n \n // Switch to overlay mode when:\n // 1. Available width is less than the desired transcript width (would cause squeezing/cutoff)\n // 2. OR available width is below minimum usable width\n // This triggers earlier - as soon as the transcript would be squeezed\n const wouldBeCutOff = availableWidth < transcriptWidth;\n const hasMinimumSpace = availableWidth >= minWidth;\n const useOverlay = wouldBeCutOff || !hasMinimumSpace;\n\n if (!useOverlay) {\n // Position to the right of the video (original behavior)\n const left = (videoRect.right - containerRect.left) + padding;\n const appliedWidth = Math.max(minWidth, Math.min(transcriptWidth, availableWidth));\n const appliedHeight = videoRect.height;\n\n this.transcriptWindow.style.position = 'absolute';\n this.transcriptWindow.style.left = `${left}px`;\n this.transcriptWindow.style.right = 'auto';\n this.transcriptWindow.style.bottom = 'auto';\n this.transcriptWindow.style.top = '0';\n this.transcriptWindow.style.height = `${appliedHeight}px`;\n this.transcriptWindow.style.maxHeight = 'none';\n this.transcriptWindow.style.width = `${appliedWidth}px`;\n this.transcriptWindow.style.maxWidth = 'none';\n this.transcriptWindow.style.boxShadow = '';\n } else {\n // Insufficient space: fall back to compact overlay mode inside video area\n // Positioned above controls to prevent horizontal overflow\n const overlayMaxWidth = 320;\n const overlayMaxHeight = 280;\n const overlayWidth = Math.min(overlayMaxWidth, videoRect.width - 40);\n const overlayHeight = Math.min(overlayMaxHeight, videoRect.height - 120);\n \n // Calculate position relative to container, placing overlay inside video area\n const videoWrapperRect = playerWithVideoWrapper.videoWrapper.getBoundingClientRect();\n const controlsHeight = 70; // Approximate height of controls\n \n // Position from top of container to bottom of video minus controls and overlay height\n const overlayActualHeight = Math.max(180, overlayHeight);\n const topPosition = (videoWrapperRect.bottom - containerRect.top) - controlsHeight - overlayActualHeight;\n const rightPosition = (containerRect.right - videoWrapperRect.right) + 12;\n\n this.transcriptWindow.style.position = 'absolute';\n this.transcriptWindow.style.left = 'auto';\n this.transcriptWindow.style.right = `${rightPosition}px`;\n this.transcriptWindow.style.top = `${topPosition}px`;\n this.transcriptWindow.style.bottom = 'auto';\n this.transcriptWindow.style.width = `${Math.max(minWidth, overlayWidth)}px`;\n this.transcriptWindow.style.maxWidth = `${overlayMaxWidth}px`;\n this.transcriptWindow.style.height = `${overlayActualHeight}px`;\n this.transcriptWindow.style.maxHeight = `${overlayMaxHeight}px`;\n this.transcriptWindow.style.boxShadow = '0 4px 16px rgba(0, 0, 0, 0.6)'; // Enhanced shadow for overlay visibility\n }\n\n this.transcriptWindow.style.borderRadius = '8px';\n this.transcriptWindow.style.border = '1px solid var(--vidply-border)';\n // Remove borderTop and any other individual border properties to avoid empty values\n this.transcriptWindow.style.removeProperty('border-top');\n this.transcriptWindow.style.removeProperty('border-right');\n this.transcriptWindow.style.removeProperty('border-bottom');\n this.transcriptWindow.style.removeProperty('border-left');\n // Remove border-image properties that can cause parse errors\n this.transcriptWindow.style.removeProperty('border-image');\n this.transcriptWindow.style.removeProperty('border-image-source');\n this.transcriptWindow.style.removeProperty('border-image-slice');\n this.transcriptWindow.style.removeProperty('border-image-width');\n this.transcriptWindow.style.removeProperty('border-image-outset');\n this.transcriptWindow.style.removeProperty('border-image-repeat');\n // Enable dragging on desktop\n if (this.transcriptHeader) {\n this.transcriptHeader.style.cursor = 'move';\n }\n \n // Move back to container for desktop\n if (this.transcriptWindow.parentNode !== this.player.container) {\n this.player.container.appendChild(this.transcriptWindow);\n }\n }\n }\n\n /**\n * Get available transcript languages from tracks\n */\n getAvailableTranscriptLanguages() {\n const textTracks = this.player.textTracks as TranscriptTrack[];\n const languages = new Map<string, TranscriptLanguageInfo>();\n \n // Collect all caption/subtitle tracks with their languages.\n // For DASH/HLS, tracks may have placeholder labels (AdaptationSet IDs,\n // \"null\", etc.); deriveTrackLabel normalises them to human-readable names.\n textTracks.forEach((track: TranscriptTrack) => {\n if ((track.kind === 'captions' || track.kind === 'subtitles') && !track._vidplyStale) {\n const lang = (track.language ?? '').trim();\n const label = deriveTrackLabel(track.label, track.language, 'player.captions');\n const key = lang || label;\n if (key && !languages.has(key)) {\n languages.set(key, {\n language: lang,\n label,\n track: track\n });\n }\n }\n });\n \n return Array.from(languages.values());\n }\n\n /**\n * Update language selector dropdown\n */\n updateLanguageSelector() {\n const languageSelector = this.languageSelector;\n if (!languageSelector) return;\n \n this.availableTranscriptLanguages = this.getAvailableTranscriptLanguages();\n \n languageSelector.innerHTML = '';\n \n if (this.availableTranscriptLanguages.length < 2) {\n if (this.languageSelectorWrapper) {\n this.languageSelectorWrapper.style.display = 'none';\n }\n return;\n }\n \n if (this.languageSelectorWrapper) {\n this.languageSelectorWrapper.style.display = 'flex';\n }\n \n this.availableTranscriptLanguages.forEach((langInfo: TranscriptLanguageInfo) => {\n const attrs: Record<string, string> = {\n 'value': langInfo.language || langInfo.label\n };\n if (langInfo.language) {\n attrs['lang'] = langInfo.language;\n }\n const option = DOMUtils.createElement('option', {\n textContent: langInfo.label,\n attributes: attrs\n });\n languageSelector.appendChild(option);\n });\n \n if (this.currentTranscriptLanguage) {\n languageSelector.value = this.currentTranscriptLanguage;\n } else if (this.availableTranscriptLanguages.length > 0) {\n const activeTrack = this.player.textTracks.find(\n (track: TranscriptTrack) => (track.kind === 'captions' || track.kind === 'subtitles') && track.mode === 'showing'\n );\n const firstLang = this.availableTranscriptLanguages[0];\n const fallbackLang = firstLang ? firstLang.language : null;\n this.currentTranscriptLanguage = activeTrack ? activeTrack.language : fallbackLang;\n if (this.currentTranscriptLanguage) {\n languageSelector.value = this.currentTranscriptLanguage;\n }\n }\n \n if (this.languageSelectorHandler) {\n languageSelector.removeEventListener('change', this.languageSelectorHandler);\n }\n \n // Handle language change — tell the streaming renderer to switch tracks\n // first so dash.js starts downloading cues for the new language, then\n // render whatever is available (textcuesupdate will re-render later).\n const handler = (e: Event) => {\n this.currentTranscriptLanguage = (e.target as HTMLSelectElement).value;\n this._requestStreamingTrack(this.currentTranscriptLanguage);\n this.loadTranscriptData();\n \n if (this.transcriptContent && this.currentTranscriptLanguage) {\n this.transcriptContent.setAttribute('lang', this.currentTranscriptLanguage);\n }\n };\n this.languageSelectorHandler = handler;\n languageSelector.addEventListener('change', handler);\n }\n\n private _parseVTT(vttText: string): TranscriptCueItem[] {\n const cues: TranscriptCueItem[] = [];\n const blocks = vttText.replace(/\\r\\n/g, '\\n').split(/\\n\\n+/);\n for (const block of blocks) {\n const lines = block.trim().split('\\n');\n let tsLine = -1;\n for (let i = 0; i < lines.length; i++) {\n if (lines[i]?.includes('-->')) { tsLine = i; break; }\n }\n if (tsLine < 0) continue;\n const match = lines[tsLine]?.match(\n /(\\d{1,2}:)?(\\d{2}):(\\d{2})\\.(\\d{3})\\s*-->\\s*(\\d{1,2}:)?(\\d{2}):(\\d{2})\\.(\\d{3})/\n );\n if (!match) continue;\n const startTime =\n (match[1] ? parseInt(match[1]) * 3600 : 0) +\n parseInt(match[2] ?? '0') * 60 +\n parseInt(match[3] ?? '0') +\n parseInt(match[4] ?? '0') / 1000;\n const endTime =\n (match[5] ? parseInt(match[5]) * 3600 : 0) +\n parseInt(match[6] ?? '0') * 60 +\n parseInt(match[7] ?? '0') +\n parseInt(match[8] ?? '0') / 1000;\n const text = lines.slice(tsLine + 1).join('\\n').trim();\n if (!text) continue;\n cues.push({\n cue: { startTime, endTime, text, id: '' } as unknown as TranscriptCue,\n type: 'caption'\n });\n }\n return cues;\n }\n\n private async _loadVttTranscript(lang: string): Promise<TranscriptCueItem[] | null> {\n const cached = this._vttCache.get(lang);\n if (cached) return cached;\n\n const renderer = this.player.renderer as Renderer | null | undefined;\n if (!renderer?.isStreaming || typeof renderer.getTextTrackURLs !== 'function') return null;\n\n const urls: { lang: string; url: string }[] = renderer.getTextTrackURLs();\n const entry = urls.find(u =>\n u.lang === lang || u.lang.startsWith(lang) || lang.startsWith(u.lang)\n );\n if (!entry) return null;\n\n // Build a combined AbortSignal: the player's lifecycle controller plus\n // a 10s timeout per individual fetch.\n const signal = this._buildFetchSignal(10_000);\n\n try {\n const res = await fetch(entry.url, { signal });\n if (!res.ok) return null;\n let text = await res.text();\n\n // HLS subtitle playlists are m3u8 files that reference the actual VTT.\n // Resolve the VTT URI and fetch it instead.\n if (text.trimStart().startsWith('#EXTM3U')) {\n const vttUri = text.split('\\n')\n .map(l => l.trim())\n .find(l => l && !l.startsWith('#'));\n if (!vttUri) return null;\n const baseUrl = entry.url.substring(0, entry.url.lastIndexOf('/') + 1);\n const vttUrl = vttUri.startsWith('http') ? vttUri : new URL(vttUri, baseUrl).href;\n const vttRes = await fetch(vttUrl, { signal: this._buildFetchSignal(10_000) });\n if (!vttRes.ok) return null;\n text = await vttRes.text();\n }\n\n const cues = this._parseVTT(text);\n if (cues.length > 0) this._vttCache.set(lang, cues);\n return cues;\n } catch {\n return null;\n }\n }\n\n /**\n * Build an AbortSignal that fires when either the player is destroyed\n * or `timeoutMs` elapses, whichever happens first.\n */\n private _buildFetchSignal(timeoutMs: number): AbortSignal | undefined {\n const signals: AbortSignal[] = [];\n const lifecycle = (this.player as { lifecycleSignal?: AbortSignal }).lifecycleSignal;\n if (lifecycle) signals.push(lifecycle);\n if (typeof AbortSignal !== 'undefined' && typeof AbortSignal.timeout === 'function') {\n signals.push(AbortSignal.timeout(timeoutMs));\n }\n if (signals.length === 0) return undefined;\n if (signals.length === 1) return signals[0];\n const anyFn = (AbortSignal as { any?: (sigs: AbortSignal[]) => AbortSignal }).any;\n return anyFn ? anyFn(signals) : signals[0];\n }\n\n /**\n * Load transcript data from caption/subtitle tracks\n */\n loadTranscriptData() {\n this.transcriptEntries = [];\n this.currentActiveEntry = null;\n if (this.transcriptContent) {\n this.transcriptContent.innerHTML = '';\n }\n\n // Get all text tracks\n const textTracks = this.player.textTracks as TranscriptTrack[];\n\n // Find track for selected language, or default to first available.\n // Safari native HLS can expose duplicate TextTrack objects for the same\n // language (SUBTITLES vs CLOSED-CAPTIONS); prefer the one with cues loaded.\n let captionTrack: TranscriptTrack | null = null;\n if (this.currentTranscriptLanguage) {\n const candidates = textTracks.filter(\n (track: TranscriptTrack) => (track.kind === 'captions' || track.kind === 'subtitles') && \n track.language === this.currentTranscriptLanguage &&\n !track._vidplyStale\n );\n captionTrack = candidates.find((t: TranscriptTrack) => t.cues && t.cues.length > 0) || candidates[0] || null;\n }\n \n if (!captionTrack) {\n const candidates = textTracks.filter(\n (track: TranscriptTrack) => (track.kind === 'captions' || track.kind === 'subtitles') && !track._vidplyStale\n );\n captionTrack = candidates.find((t: TranscriptTrack) => t.cues && t.cues.length > 0) || candidates[0] || null;\n if (captionTrack) {\n this.currentTranscriptLanguage = captionTrack.language;\n }\n }\n \n // Find description track matching the selected language\n let descriptionTrack: TranscriptTrack | null = null;\n if (this.currentTranscriptLanguage) {\n descriptionTrack = textTracks.find(\n (track: TranscriptTrack) => track.kind === 'descriptions' && track.language === this.currentTranscriptLanguage\n ) || null;\n }\n // Fallback to first available description track if no match found\n if (!descriptionTrack) {\n descriptionTrack = textTracks.find((track: TranscriptTrack) => track.kind === 'descriptions') || null;\n }\n \n const metadataTrack = textTracks.find((track: TranscriptTrack) => track.kind === 'metadata');\n\n // We need at least one track type available for display\n // (captions, descriptions, or metadata - though metadata is not displayed)\n if (!captionTrack && !descriptionTrack && !metadataTrack) {\n this.showNoTranscriptMessage();\n return;\n }\n\n // Enable all tracks to load cues so they're available for the transcript\n const tracksToLoad: TranscriptTrack[] = [captionTrack, descriptionTrack, metadataTrack].filter(\n (track): track is TranscriptTrack => Boolean(track)\n );\n tracksToLoad.forEach((track: TranscriptTrack) => {\n if (track.mode === 'disabled') {\n track.mode = 'hidden';\n }\n });\n\n // For streaming renderers, fetch the complete VTT file directly rather\n // than relying on dash.js's partial segment-based cues.\n const renderer = this.player.renderer as Renderer | null | undefined;\n const isStreaming = renderer?.isStreaming && typeof renderer.getTextTrackURLs === 'function';\n const lang = this.currentTranscriptLanguage || (captionTrack?.language ?? '');\n\n if (isStreaming && lang) {\n const loadingMessage = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-transcript-loading`,\n textContent: i18n.t('transcript.loading')\n });\n this.transcriptContent?.appendChild(loadingMessage);\n\n this._loadVttTranscript(lang).then(vttCues => {\n if (!this.isVisible) return;\n this._renderTranscriptCues(\n vttCues && vttCues.length > 0 ? vttCues : null,\n captionTrack,\n descriptionTrack,\n metadataTrack\n );\n });\n return;\n }\n\n // Non-streaming path: wait for sidecar <track> elements to load cues.\n const primaryTrack = captionTrack;\n const primaryNeedsCues = primaryTrack && (!primaryTrack.cues || primaryTrack.cues.length === 0);\n\n if (primaryNeedsCues) {\n const loadingMessage = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-transcript-loading`,\n textContent: i18n.t('transcript.loading')\n });\n this.transcriptContent?.appendChild(loadingMessage);\n\n const hasSidecarElement = this.player.findTrackElement?.(primaryTrack);\n if (hasSidecarElement) {\n primaryTrack.addEventListener('load', () => {\n this.loadTranscriptData();\n }, { once: true });\n this.setManagedTimeout(() => { this.loadTranscriptData(); }, 1000);\n } else {\n let attempts = 0;\n const poll = () => {\n attempts++;\n if (!this.isVisible) return;\n if (primaryTrack.cues && primaryTrack.cues.length > 0) {\n this.loadTranscriptData();\n return;\n }\n if (attempts < 40) {\n this.setManagedTimeout(poll, 500);\n }\n };\n this.setManagedTimeout(poll, 300);\n }\n return;\n }\n\n this._renderTranscriptCues(null, captionTrack, descriptionTrack, metadataTrack);\n }\n\n private _renderTranscriptCues(\n vttCues: TranscriptCueItem[] | null,\n captionTrack: TranscriptTrack | null,\n descriptionTrack: TranscriptTrack | null,\n metadataTrack: TranscriptTrack | undefined | null\n ) {\n this.transcriptEntries = [];\n this.currentActiveEntry = null;\n const transcriptContent = this.transcriptContent;\n if (transcriptContent) {\n transcriptContent.innerHTML = '';\n }\n\n const allCues: TranscriptCueItem[] = [];\n\n if (vttCues && vttCues.length > 0) {\n allCues.push(...vttCues);\n } else if (captionTrack && captionTrack.cues) {\n Array.from(captionTrack.cues).forEach((cue: TranscriptCue) => {\n allCues.push({ cue, type: 'caption' });\n });\n }\n \n if (descriptionTrack && descriptionTrack.cues) {\n Array.from(descriptionTrack.cues).forEach((cue: TranscriptCue) => {\n allCues.push({ cue, type: 'description' });\n });\n }\n \n if (metadataTrack && metadataTrack.cues) {\n this.metadataCues = Array.from(metadataTrack.cues) as TranscriptCue[];\n this.setupMetadataHandling();\n }\n\n allCues.sort((a, b) => a.cue.startTime - b.cue.startTime);\n\n allCues.forEach((item, index) => {\n const entry = this.createTranscriptEntry(item.cue, index, item.type);\n this.transcriptEntries.push({\n element: entry,\n cue: item.cue,\n type: item.type,\n startTime: item.cue.startTime,\n endTime: item.cue.endTime\n });\n transcriptContent?.appendChild(entry);\n });\n \n this.applyTranscriptStyles();\n this.updateTimestampVisibility();\n \n if (this.transcriptContent && this.currentTranscriptLanguage) {\n this.transcriptContent.setAttribute('lang', this.currentTranscriptLanguage);\n }\n \n this.updateLanguageSelector();\n this.updateActiveEntry();\n }\n\n /**\n * Setup metadata handling on player load\n * This runs independently of transcript loading\n */\n setupMetadataHandlingOnLoad() {\n // Wait for metadata to be loaded\n const setupMetadata = () => {\n const textTracks = this.player.textTracks;\n const metadataTrack = (textTracks as TranscriptTrack[]).find((track: TranscriptTrack) => track.kind === 'metadata');\n \n if (metadataTrack) {\n // Enable the metadata track so cuechange events fire\n // Use 'hidden' mode so it doesn't display anything, but events still work\n if (metadataTrack.mode === 'disabled') {\n metadataTrack.mode = 'hidden';\n }\n \n // Check if we already added the listener\n if (this.metadataCueChangeHandler) {\n metadataTrack.removeEventListener('cuechange', this.metadataCueChangeHandler);\n }\n \n // Add event listener for cue changes\n this.metadataCueChangeHandler = () => {\n const activeCues = Array.from(metadataTrack.activeCues || []) as TranscriptCue[];\n if (activeCues.length > 0) {\n // Debug logging (can be removed in production)\n if (this.player.options.debug) {\n console.log('[VidPly Metadata] Active cues:', activeCues.map((c) => ({\n start: c.startTime,\n end: c.endTime,\n text: (c as VTTCue).text\n })));\n }\n }\n activeCues.forEach((cue: TranscriptCue) => {\n this.handleMetadataCue(cue);\n });\n };\n \n metadataTrack.addEventListener('cuechange', this.metadataCueChangeHandler);\n \n // Debug: Log metadata track setup\n if (this.player.options.debug) {\n const cueCount = metadataTrack.cues ? metadataTrack.cues.length : 0;\n console.log('[VidPly Metadata] Track enabled,', cueCount, 'cues available');\n }\n } else if (this.player.options.debug) {\n console.warn('[VidPly Metadata] No metadata track found');\n }\n };\n \n // Try immediately\n setupMetadata();\n \n // Also try after loadedmetadata event\n this.player.on('loadedmetadata', setupMetadata);\n }\n\n /**\n * Setup metadata handling\n * Metadata cues are not displayed but can be used programmatically\n * This is called when transcript data is loaded (for storing cues)\n */\n setupMetadataHandling() {\n if (!this.metadataCues || this.metadataCues.length === 0) {\n return;\n }\n\n // The actual event handling is set up in setupMetadataHandlingOnLoad()\n // This method just stores the cues for reference\n if (this.player.options.debug) {\n console.log('[VidPly Metadata]', this.metadataCues.length, 'cues stored from transcript load');\n }\n }\n\n /**\n * Handle individual metadata cues\n * Parses metadata text and emits events or triggers actions\n */\n handleMetadataCue(cue: TranscriptCue) {\n const cueText = (cue as TextTrackCue & { text?: string }).text || '';\n const text = cueText.trim();\n \n // Debug logging\n if (this.player.options.debug) {\n console.log('[VidPly Metadata] Processing cue:', {\n time: cue.startTime,\n text: text\n });\n }\n \n // Emit a generic metadata event that developers can listen to\n this.player.emit('metadata', {\n time: cue.startTime,\n endTime: cue.endTime,\n text: text,\n cue: cue\n });\n\n // Parse for specific commands (examples based on wwa_meta.vtt format)\n if (text.includes('PAUSE')) {\n // Automatically pause the video\n if (!this.player.state.paused) {\n if (this.player.options.debug) {\n console.log('[VidPly Metadata] Pausing video at', cue.startTime);\n }\n this.player.pause();\n }\n // Also emit event for developers who want to listen\n this.player.emit('metadata:pause', { time: cue.startTime, text: text });\n }\n\n // Parse for focus directives\n const focusMatch = text.match(/FOCUS:([\\w#-]+)/);\n if (focusMatch && focusMatch[1]) {\n const targetSelector = focusMatch[1];\n // Automatically focus the target element\n const targetElement = document.querySelector(targetSelector) as HTMLElement | null;\n if (targetElement) {\n if (this.player.options.debug) {\n console.log('[VidPly Metadata] Focusing element:', targetSelector);\n }\n // Use setTimeout to ensure DOM is ready\n this.setManagedTimeout(() => {\n targetElement.focus({ preventScroll: true });\n }, 10);\n } else if (this.player.options.debug) {\n console.warn('[VidPly Metadata] Element not found:', targetSelector);\n }\n // Also emit event for developers who want to listen\n this.player.emit('metadata:focus', { \n time: cue.startTime, \n target: targetSelector,\n element: targetElement,\n text: text \n });\n }\n\n // Parse for hashtag references\n const hashtags = text.match(/#[\\w-]+/g);\n if (hashtags) {\n if (this.player.options.debug) {\n console.log('[VidPly Metadata] Hashtags found:', hashtags);\n }\n this.player.emit('metadata:hashtags', {\n time: cue.startTime,\n hashtags: hashtags,\n text: text\n });\n }\n }\n\n /**\n * Create a single transcript entry element\n */\n createTranscriptEntry(cue: TranscriptCue, index: number, type: 'caption' | 'description' = 'caption') {\n const entryText = this.stripVTTFormatting(((cue as TextTrackCue & { text?: string }).text) || '');\n\n // Use a real <button> so screen readers/AT report \"button\", Enter and\n // Space activate it natively, and forms-mode users can find it.\n const seekLabelTemplate = i18n.t('transcript.seekTo') || 'Seek to {time}';\n const ariaLabel = seekLabelTemplate\n .replace('{time}', TimeUtils.formatTime(cue.startTime))\n .replace('{text}', entryText);\n\n const entry = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-transcript-entry ${this.player.options.classPrefix}-transcript-${type}`,\n attributes: {\n 'type': 'button',\n 'data-start': String(cue.startTime),\n 'data-end': String(cue.endTime),\n 'data-type': type,\n 'aria-label': `${ariaLabel} — ${entryText}`\n }\n });\n\n const timestamp = DOMUtils.createElement('span', {\n className: `${this.player.options.classPrefix}-transcript-time`,\n textContent: TimeUtils.formatTime(cue.startTime),\n attributes: {\n 'aria-hidden': 'true' // Hide from screen readers - decorative timestamp\n }\n });\n\n const text = DOMUtils.createElement('span', {\n className: `${this.player.options.classPrefix}-transcript-text`,\n textContent: entryText\n });\n\n entry.appendChild(timestamp);\n entry.appendChild(text);\n\n // Click to seek. <button> elements activate on Enter/Space natively,\n // so a custom keydown handler is no longer needed.\n const seekToTime = () => {\n this.player.seek(cue.startTime);\n if (this.player.state.paused) {\n this.player.play();\n }\n };\n\n entry.addEventListener('click', seekToTime);\n\n return entry;\n }\n\n /**\n * Strip VTT formatting tags from text\n */\n stripVTTFormatting(text: string) {\n // Remove VTT tags like <v Speaker>, <c>, etc.\n return text\n .replace(/<[^>]+>/g, '')\n .replace(/\\n/g, ' ')\n .trim();\n }\n\n /**\n * Show message when no transcript is available\n */\n showNoTranscriptMessage() {\n const message = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-transcript-empty`,\n textContent: i18n.t('transcript.noTranscript')\n });\n this.transcriptContent?.appendChild(message);\n }\n\n /**\n * Update active transcript entry based on current time\n */\n updateActiveEntry() {\n if (!this.isVisible || this.transcriptEntries.length === 0) return;\n\n const currentTime = this.player.state.currentTime;\n \n // Find the entry that matches current time\n const activeEntry = this.transcriptEntries.find(\n (entry: TranscriptEntry) => currentTime >= entry.startTime && currentTime < entry.endTime\n );\n\n if (activeEntry && activeEntry !== this.currentActiveEntry) {\n // Remove previous active class\n if (this.currentActiveEntry) {\n this.currentActiveEntry.element.classList.remove(\n `${this.player.options.classPrefix}-transcript-entry-active`\n );\n }\n\n // Add active class to current entry\n activeEntry.element.classList.add(\n `${this.player.options.classPrefix}-transcript-entry-active`\n );\n \n // Scroll to active entry\n this.scrollToEntry(activeEntry.element);\n \n this.currentActiveEntry = activeEntry;\n } else if (!activeEntry && this.currentActiveEntry) {\n // No active entry, remove active class\n this.currentActiveEntry.element.classList.remove(\n `${this.player.options.classPrefix}-transcript-entry-active`\n );\n this.currentActiveEntry = null;\n }\n }\n\n /**\n * Scroll transcript window to show active entry\n */\n scrollToEntry(entryElement: HTMLElement) {\n if (!this.transcriptContent || !this.autoscrollEnabled) return;\n\n const contentRect = this.transcriptContent.getBoundingClientRect();\n const entryRect = entryElement.getBoundingClientRect();\n\n // Check if entry is out of view\n if (entryRect.top < contentRect.top || entryRect.bottom > contentRect.bottom) {\n // Scroll to center the entry\n const scrollTop = entryElement.offsetTop - (this.transcriptContent.clientHeight / 2) + (entryElement.clientHeight / 2);\n this.transcriptContent.scrollTo({\n top: scrollTop,\n behavior: 'smooth'\n });\n }\n }\n \n /**\n * Save autoscroll preference to localStorage\n */\n saveAutoscrollPreference() {\n const savedPreferences = this.storage.getTranscriptPreferences() || {};\n savedPreferences.autoscroll = this.autoscrollEnabled;\n this.storage.saveTranscriptPreferences(savedPreferences);\n }\n\n /**\n * Setup drag and drop functionality\n */\n setupDragAndDrop() {\n if (!this.transcriptHeader || !this.transcriptWindow) return;\n\n // Check if we're on mobile and not in fullscreen\n const isMobile = window.innerWidth < 768;\n const isFullscreen = this.player.state.fullscreen;\n \n // On mobile devices (< 768px), only enable drag/resize in fullscreen\n // On desktop/tablets (>= 768px), always enable drag/resize\n if (isMobile && !isFullscreen) {\n // Destroy existing instance if exiting fullscreen on mobile\n if (this.draggableResizable) {\n this.draggableResizable.destroy();\n this.draggableResizable = null;\n }\n return; // No drag/resize on mobile when not in fullscreen\n }\n\n // If already initialized, don't re-initialize\n if (this.draggableResizable) {\n return;\n }\n\n // Create DraggableResizable utility with touch support\n this.draggableResizable = new DraggableResizable(this.transcriptWindow, {\n dragHandle: this.transcriptHeader,\n resizeHandles: this.transcriptResizeHandles,\n constrainToViewport: true,\n classPrefix: `${this.player.options.classPrefix}-transcript`,\n keyboardDragKey: 'd',\n keyboardResizeKey: 'r',\n keyboardStep: 10,\n keyboardStepLarge: 50,\n minWidth: 300,\n minHeight: 200,\n maxWidth: () => Math.max(320, window.innerWidth - 40),\n maxHeight: () => Math.max(200, window.innerHeight - 120),\n pointerResizeIndicatorText: i18n.t('transcript.resizeModeHint'),\n onPointerResizeToggle: (enabled) => {\n // Update resize handles visibility\n this.transcriptResizeHandles.forEach((handle: HTMLElement) => {\n handle.style.display = enabled ? 'block' : 'none';\n });\n // Call the state change handler\n this.onPointerResizeModeChange(enabled);\n },\n onDragStart: (e: Event) => {\n // Don't drag if clicking on certain elements\n const ignoreSelectors = [\n `.${this.player.options.classPrefix}-transcript-close`,\n `.${this.player.options.classPrefix}-transcript-settings`,\n `.${this.player.options.classPrefix}-transcript-language-select`,\n `.${this.player.options.classPrefix}-transcript-language-label`,\n `.${this.player.options.classPrefix}-transcript-settings-menu`,\n `.${this.player.options.classPrefix}-transcript-style-dialog`\n ];\n \n for (const selector of ignoreSelectors) {\n if ((e.target as HTMLElement).closest(selector)) {\n return false; // Prevent drag\n }\n }\n \n return true; // Allow drag\n }\n });\n\n // Add custom keyboard handler for special keys (Escape, Home)\n this.customKeyHandler = (e: KeyboardEvent) => {\n const key = e.key.toLowerCase();\n const alreadyPrevented = e.defaultPrevented;\n\n // Don't handle keys if settings menu or style dialog is open (let them handle keys)\n if (this.settingsMenuVisible || this.styleDialogVisible) {\n return;\n }\n\n if (key === 'home') {\n e.preventDefault();\n e.stopPropagation();\n if (this.draggableResizable) {\n if (this.draggableResizable.pointerResizeMode) {\n this.draggableResizable.disablePointerResizeMode();\n }\n this.draggableResizable.manuallyPositioned = false;\n this.positionTranscript();\n this.updateResizeOptionState();\n this.announceLive(i18n.t('transcript.positionReset'));\n }\n return;\n }\n \n if (key === 'r') {\n if (alreadyPrevented) {\n return;\n }\n e.preventDefault();\n e.stopPropagation();\n const enabled = this.toggleResizeMode();\n if (enabled) {\n this.transcriptWindow?.focus({ preventScroll: true });\n }\n return;\n }\n \n if (key === 'escape') {\n // Check priority: resize mode > drag mode > close transcript\n // (settings menu and style dialog already handled by early return above)\n if (this.draggableResizable && this.draggableResizable.pointerResizeMode) {\n e.preventDefault();\n e.stopPropagation();\n this.draggableResizable.disablePointerResizeMode();\n return;\n }\n if (this.draggableResizable && this.draggableResizable.keyboardDragMode) {\n e.preventDefault();\n e.stopPropagation();\n this.draggableResizable.disableKeyboardDragMode();\n this.announceLive(i18n.t('transcript.dragModeDisabled'));\n return;\n }\n // Only close transcript if nothing else is open\n e.preventDefault();\n e.stopPropagation();\n this.hideTranscript({ focusButton: true });\n return;\n }\n };\n \n const customKeyHandler = this.customKeyHandler;\n if (this.transcriptWindow && customKeyHandler) {\n this.transcriptWindow.addEventListener('keydown', customKeyHandler, {\n signal: this.player.lifecycleSignal\n });\n }\n }\n\n\n /**\n * Toggle keyboard drag mode. Mirrors the sign-language flow: a\n * persistent badge is shown on the transcript window while the mode\n * is active, and a live-region announcement is made on each state\n * change.\n */\n toggleKeyboardDragMode() {\n if (this.draggableResizable) {\n const wasEnabled = this.draggableResizable.keyboardDragMode;\n this.draggableResizable.toggleKeyboardDragMode();\n const isEnabled = this.draggableResizable.keyboardDragMode;\n if (!wasEnabled && isEnabled) {\n this.enableMoveMode();\n this._panel?.showBadge(i18n.t('transcript.dragModeBadge'));\n this.announceLive(i18n.t('transcript.dragModeEnabled'));\n } else if (wasEnabled && !isEnabled) {\n this._panel?.hideBadge();\n this.announceLive(i18n.t('transcript.dragModeDisabled'));\n }\n\n this.updateDragOptionState();\n\n if (this.settingsMenuVisible) {\n this.hideSettingsMenu();\n }\n\n this.transcriptWindow?.focus({ preventScroll: true });\n }\n }\n\n /**\n * Toggle settings menu visibility\n */\n toggleSettingsMenu() {\n if (this.settingsMenuVisible) {\n this.hideSettingsMenu();\n } else {\n this.showSettingsMenu();\n }\n }\n\n /**\n * Show the settings menu. Delegates to the shared {@link DraggablePanel};\n * kept as a named method so external callers (tests, other managers)\n * that referenced the legacy API keep working.\n */\n showSettingsMenu() {\n this._panel?.show();\n }\n\n /** @see {@link showSettingsMenu} */\n positionSettingsMenu() {\n this._panel?.reposition();\n }\n\n /** @see {@link showSettingsMenu} */\n hideSettingsMenu({ focusButton = true } = {}) {\n this._panel?.hide({ focusButton });\n }\n\n /**\n * Enable move mode (gives visual feedback)\n */\n /**\n * Brief pulse animation on the transcript window to confirm entry\n * into keyboard drag mode. The textual hint that used to also flash\n * here has been replaced by a persistent {@link DraggablePanel}\n * badge (see `toggleKeyboardDragMode`), so this method only owns\n * the 1s visual cue now.\n */\n enableMoveMode() {\n this.transcriptWindow?.classList.add(\n `${this.player.options.classPrefix}-transcript-move-mode`\n );\n\n setTimeout(() => {\n this.transcriptWindow?.classList.remove(\n `${this.player.options.classPrefix}-transcript-move-mode`\n );\n }, 2000);\n }\n\n /**\n * Toggle resize mode\n */\n toggleResizeMode({ focus = true } = {}) {\n if (!this.draggableResizable) {\n return false;\n }\n\n if (this.draggableResizable.pointerResizeMode) {\n this.draggableResizable.disablePointerResizeMode({ focus });\n return false;\n }\n\n this.draggableResizable.enablePointerResizeMode({ focus });\n return true;\n }\n\n // Thin delegates to the panel's refreshState. Kept as named methods\n // so the existing internal call sites (e.g. `toggleKeyboardDragMode`\n // and `toggleResizeMode`) read naturally without having to chain\n // through the optional panel reference every time.\n updateDragOptionState() {\n this._panel?.refreshDragState();\n }\n\n updateResizeOptionState() {\n this._panel?.refreshResizeState();\n }\n\n toggleShowTimestamps() {\n this.showTimestamps = !this.showTimestamps;\n this.updateShowTimestampsState();\n this.updateTimestampVisibility();\n this.saveTimestampsPreference();\n }\n\n updateShowTimestampsState() {\n if (!this.showTimestampsButton) {\n return;\n }\n \n const text = this.showTimestamps\n ? i18n.t('transcript.hideTimestamps')\n : i18n.t('transcript.showTimestamps');\n const ariaLabel = this.showTimestamps\n ? i18n.t('transcript.hideTimestampsAria')\n : i18n.t('transcript.showTimestampsAria');\n\n this.showTimestampsButton.setAttribute('aria-checked', this.showTimestamps ? 'true' : 'false');\n this.showTimestampsButton.setAttribute('aria-label', ariaLabel);\n\n if (this.showTimestampsText) {\n this.showTimestampsText.textContent = text;\n }\n }\n\n updateTimestampVisibility() {\n if (!this.transcriptContent) return;\n \n const timestamps = this.transcriptContent.querySelectorAll<HTMLElement>(`.${this.player.options.classPrefix}-transcript-time`);\n timestamps.forEach((timestamp) => {\n timestamp.style.display = this.showTimestamps ? '' : 'none';\n });\n }\n\n saveTimestampsPreference() {\n const savedPreferences = this.storage.getTranscriptPreferences() || {};\n savedPreferences.showTimestamps = this.showTimestamps;\n this.storage.saveTranscriptPreferences(savedPreferences);\n }\n\n // Legacy shims kept for any external callers that still invoke the\n // old transient-tooltip API. The persistent badge owned by the\n // {@link DraggablePanel} now replaces the 3-second indicator, so\n // these simply forward to the panel. Safe to remove once the next\n // consumer sweep confirms no external references.\n showResizeModeIndicator() {\n this._panel?.showBadge(i18n.t('transcript.resizeModeBadge'));\n }\n\n hideResizeModeIndicator() {\n this._panel?.hideBadge();\n }\n\n onPointerResizeModeChange(enabled: boolean) {\n this.updateResizeOptionState();\n\n if (enabled) {\n this._panel?.showBadge(i18n.t('transcript.resizeModeBadge'));\n this.announceLive(i18n.t('transcript.resizeModeEnabled'));\n } else {\n this._panel?.hideBadge();\n this.announceLive(i18n.t('transcript.resizeModeDisabled'));\n }\n }\n\n /**\n * Show style dialog\n */\n showStyleDialog() {\n // The style dialog uses an outside-click listener to dismiss\n // itself. Previously that listener was armed the first time the\n // settings menu opened (the same handler also dismissed the\n // settings menu). Now that the settings menu owns its own\n // outside-click, we arm the style-dialog listener here on first\n // open. Tied to `lifecycleSignal` so teardown is automatic.\n if (!this.documentClickHandlerAdded) {\n setTimeout(() => {\n const documentClick = this.handlers.documentClick;\n if (documentClick) {\n document.addEventListener('click', documentClick, {\n signal: this.player.lifecycleSignal\n });\n }\n this.documentClickHandlerAdded = true;\n }, 300);\n }\n\n // If dialog already exists, just show it\n if (this.styleDialog) {\n this.styleDialog.style.display = 'block';\n this.styleDialogVisible = true;\n \n // Re-add keyboard handler\n if (this.handlers.styleDialogKeydown) {\n document.addEventListener('keydown', this.handlers.styleDialogKeydown, {\n signal: this.player.lifecycleSignal\n });\n }\n \n // Set flag to prevent immediate closing from document click\n this.styleDialogJustOpened = true;\n setTimeout(() => {\n this.styleDialogJustOpened = false;\n }, 350);\n \n setTimeout(() => {\n const dialog = this.styleDialog;\n if (!dialog) return;\n const firstSelect = dialog.querySelector<HTMLElement>('select, input');\n if (firstSelect) {\n firstSelect.focus({ preventScroll: true });\n }\n }, 0);\n return;\n }\n\n const styleDialog = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-transcript-style-dialog`\n });\n this.styleDialog = styleDialog;\n\n const title = DOMUtils.createElement('h4', {\n textContent: i18n.t('transcript.styleTitle'),\n className: `${this.player.options.classPrefix}-transcript-style-title`\n });\n styleDialog.appendChild(title);\n\n const fontSizeControl = this.createStyleSelectControl(\n i18n.t('captions.fontSize'),\n 'fontSize',\n [\n { label: i18n.t('fontSizes.small'), value: '90%' },\n { label: i18n.t('fontSizes.normal'), value: '100%' },\n { label: i18n.t('fontSizes.large'), value: '110%' },\n { label: i18n.t('fontSizes.xlarge'), value: '120%' }\n ]\n );\n styleDialog.appendChild(fontSizeControl);\n\n const fontFamilyControl = this.createStyleSelectControl(\n i18n.t('captions.fontFamily'),\n 'fontFamily',\n [\n { label: i18n.t('fontFamilies.sansSerif'), value: 'sans-serif' },\n { label: i18n.t('fontFamilies.serif'), value: 'serif' },\n { label: i18n.t('fontFamilies.monospace'), value: 'monospace' }\n ]\n );\n styleDialog.appendChild(fontFamilyControl);\n\n const colorControl = this.createStyleColorControl(i18n.t('captions.color'), 'color');\n styleDialog.appendChild(colorControl);\n\n const bgColorControl = this.createStyleColorControl(i18n.t('captions.backgroundColor'), 'backgroundColor');\n styleDialog.appendChild(bgColorControl);\n\n const opacityControl = this.createStyleOpacityControl(i18n.t('captions.opacity'), 'opacity');\n styleDialog.appendChild(opacityControl);\n\n const closeBtn = DOMUtils.createElement('button', {\n className: `${this.player.options.classPrefix}-transcript-style-close`,\n textContent: i18n.t('settings.close'),\n attributes: {\n 'type': 'button'\n }\n });\n closeBtn.addEventListener('click', () => this.hideStyleDialog());\n styleDialog.appendChild(closeBtn);\n\n const styleKeyHandler = (e: KeyboardEvent) => {\n if (!this.styleDialogVisible) return;\n \n if (e.key === 'Escape') {\n e.preventDefault();\n e.stopPropagation();\n this.hideStyleDialog();\n return;\n }\n \n if (e.key === 'Tab') {\n const focusableElements = styleDialog.querySelectorAll<HTMLElement>(\n 'select, input, button'\n );\n const firstElement = focusableElements[0];\n const lastElement = focusableElements[focusableElements.length - 1];\n if (!firstElement || !lastElement) return;\n \n if (e.shiftKey && document.activeElement === firstElement) {\n e.preventDefault();\n lastElement.focus({ preventScroll: true });\n } else if (!e.shiftKey && document.activeElement === lastElement) {\n e.preventDefault();\n firstElement.focus({ preventScroll: true });\n }\n }\n };\n this.handlers.styleDialogKeydown = styleKeyHandler;\n document.addEventListener('keydown', styleKeyHandler, {\n signal: this.player.lifecycleSignal\n });\n\n if (this.headerLeft) {\n this.headerLeft.appendChild(styleDialog);\n } else if (this.transcriptHeader) {\n this.transcriptHeader.appendChild(styleDialog);\n }\n \n this.applyTranscriptStyles();\n \n this.styleDialogVisible = true;\n styleDialog.style.display = 'block';\n \n this.styleDialogJustOpened = true;\n setTimeout(() => {\n this.styleDialogJustOpened = false;\n }, 350);\n \n setTimeout(() => {\n const firstSelect = styleDialog.querySelector<HTMLElement>('select, input');\n if (firstSelect) {\n firstSelect.focus({ preventScroll: true });\n }\n }, 0);\n }\n\n /**\n * Hide style dialog\n */\n hideStyleDialog() {\n if (this.styleDialog) {\n this.styleDialog.style.display = 'none';\n this.styleDialogVisible = false;\n \n // Remove keyboard handler\n if (this.handlers.styleDialogKeydown) {\n document.removeEventListener('keydown', this.handlers.styleDialogKeydown);\n }\n \n // Return focus to settings button\n if (this.settingsButton) {\n this.settingsButton.focus({ preventScroll: true });\n }\n }\n }\n\n /**\n * Create style select control\n */\n createStyleSelectControl(label: string, property: string, options: Array<{ label: string; value: string }>) {\n const group = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-transcript-style-group`\n });\n\n // Generate unique ID for the control\n const controlId = `${this.player.options.classPrefix}-transcript-${property}-${Date.now()}`;\n\n const labelEl = DOMUtils.createElement('label', {\n textContent: label,\n attributes: {\n 'for': controlId\n }\n });\n group.appendChild(labelEl);\n\n const select = DOMUtils.createElement('select', {\n className: `${this.player.options.classPrefix}-transcript-style-select`,\n attributes: {\n 'id': controlId\n }\n });\n\n options.forEach((opt: { label: string; value: string }) => {\n const option = DOMUtils.createElement('option', {\n textContent: opt.label,\n attributes: {\n 'value': opt.value\n }\n });\n if (this.transcriptStyle[property as 'fontSize' | 'fontFamily'] === opt.value) {\n option.selected = true;\n }\n select.appendChild(option);\n });\n\n select.addEventListener('change', (e) => {\n this.transcriptStyle[property as 'fontSize' | 'fontFamily'] = (e.target as HTMLSelectElement).value;\n this.applyTranscriptStyles();\n this.savePreferences();\n });\n\n group.appendChild(select);\n return group;\n }\n\n /**\n * Create style color control\n */\n createStyleColorControl(label: string, property: string) {\n const group = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-transcript-style-group`\n });\n\n // Generate unique ID for the control\n const controlId = `${this.player.options.classPrefix}-transcript-${property}-${Date.now()}`;\n\n const labelEl = DOMUtils.createElement('label', {\n textContent: label,\n attributes: {\n 'for': controlId\n }\n });\n group.appendChild(labelEl);\n\n const input = DOMUtils.createElement('input', {\n attributes: {\n 'id': controlId,\n 'type': 'color',\n 'value': this.transcriptStyle[property as 'color' | 'backgroundColor']\n },\n className: `${this.player.options.classPrefix}-transcript-style-color`\n });\n\n input.addEventListener('input', (e) => {\n this.transcriptStyle[property as 'color' | 'backgroundColor'] = (e.target as HTMLInputElement).value;\n this.applyTranscriptStyles();\n this.savePreferences();\n });\n\n group.appendChild(input);\n return group;\n }\n\n /**\n * Create style opacity control\n */\n createStyleOpacityControl(label: string, property: string) {\n const group = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-transcript-style-group`\n });\n\n // Generate unique ID for the control\n const controlId = `${this.player.options.classPrefix}-transcript-${property}-${Date.now()}`;\n\n const labelEl = DOMUtils.createElement('label', {\n textContent: label,\n attributes: {\n 'for': controlId\n }\n });\n group.appendChild(labelEl);\n\n const opacityProperty = property as 'opacity';\n const valueDisplay = DOMUtils.createElement('span', {\n textContent: Math.round(this.transcriptStyle[opacityProperty] * 100) + '%',\n className: `${this.player.options.classPrefix}-transcript-style-value`\n });\n\n const input = DOMUtils.createElement('input', {\n attributes: {\n 'id': controlId,\n 'type': 'range',\n 'min': '0',\n 'max': '1',\n 'step': '0.1',\n 'value': String(this.transcriptStyle[opacityProperty])\n },\n className: `${this.player.options.classPrefix}-transcript-style-range`\n });\n\n input.addEventListener('input', (e) => {\n const value = parseFloat((e.target as HTMLInputElement).value);\n this.transcriptStyle[opacityProperty] = value;\n valueDisplay.textContent = Math.round(value * 100) + '%';\n this.applyTranscriptStyles();\n this.savePreferences();\n });\n\n const inputContainer = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-transcript-style-range-container`\n });\n inputContainer.appendChild(input);\n inputContainer.appendChild(valueDisplay);\n\n group.appendChild(labelEl);\n group.appendChild(inputContainer);\n return group;\n }\n\n /**\n * Save transcript preferences to localStorage\n */\n savePreferences() {\n this.storage.saveTranscriptPreferences(this.transcriptStyle);\n }\n\n /**\n * Apply transcript styles\n */\n applyTranscriptStyles() {\n if (!this.transcriptWindow) return;\n\n // Apply to transcript window background\n this.transcriptWindow.style.backgroundColor = this.transcriptStyle.backgroundColor;\n this.transcriptWindow.style.opacity = String(this.transcriptStyle.opacity);\n\n // Apply to content area\n if (this.transcriptContent) {\n this.transcriptContent.style.fontSize = this.transcriptStyle.fontSize;\n this.transcriptContent.style.fontFamily = this.transcriptStyle.fontFamily;\n this.transcriptContent.style.color = this.transcriptStyle.color;\n }\n\n const textEntries = this.transcriptWindow.querySelectorAll<HTMLElement>(`.${this.player.options.classPrefix}-transcript-text`);\n textEntries.forEach((entry) => {\n entry.style.fontSize = this.transcriptStyle.fontSize;\n entry.style.fontFamily = this.transcriptStyle.fontFamily;\n entry.style.color = this.transcriptStyle.color;\n });\n \n const timeEntries = this.transcriptWindow.querySelectorAll<HTMLElement>(`.${this.player.options.classPrefix}-transcript-time`);\n timeEntries.forEach((entry) => {\n entry.style.fontFamily = this.transcriptStyle.fontFamily;\n });\n }\n\n /**\n * Set a managed timeout that will be cleaned up on destroy\n * @param {Function} callback - Callback function\n * @param {number} delay - Delay in milliseconds\n * @returns {number} Timeout ID\n */\n setManagedTimeout(callback: () => void, delay: number) {\n const timeoutId = setTimeout(() => {\n this.timeouts.delete(timeoutId);\n callback();\n }, delay);\n this.timeouts.add(timeoutId);\n return timeoutId;\n }\n\n /**\n * Clear a managed timeout\n * @param {number} timeoutId - Timeout ID to clear\n */\n clearManagedTimeout(timeoutId: ReturnType<typeof setTimeout> | null) {\n if (timeoutId) {\n clearTimeout(timeoutId);\n this.timeouts.delete(timeoutId);\n }\n }\n\n /**\n * Cleanup\n */\n destroy() {\n this.hideResizeModeIndicator();\n\n // Tear down the settings-menu panel (removes its DOM + keyboard\n // listeners). The outside-click listener was attached with the\n // player's lifecycleSignal, so it cleans itself up as the player\n // is destroyed.\n if (this._panel) {\n this._panel.destroy();\n this._panel = null;\n }\n\n // Destroy draggableResizable utility\n if (this.draggableResizable) {\n if (this.draggableResizable.pointerResizeMode) {\n this.draggableResizable.disablePointerResizeMode();\n this.updateResizeOptionState();\n }\n this.draggableResizable.destroy();\n this.draggableResizable = null;\n }\n \n // Remove custom key handler\n if (this.transcriptWindow && this.customKeyHandler) {\n this.transcriptWindow.removeEventListener('keydown', this.customKeyHandler);\n this.customKeyHandler = null;\n }\n\n // Remove timeupdate and seeked listeners from player\n if (this.handlers.timeupdate) {\n this.player.off('timeupdate', this.handlers.timeupdate);\n }\n if (this.handlers.seeked) {\n this.player.off('seeked', this.handlers.seeked);\n }\n \n // Remove audio description listeners from player\n if (this.handlers.audiodescriptionenabled) {\n this.player.off('audiodescriptionenabled', this.handlers.audiodescriptionenabled);\n }\n if (this.handlers.audiodescriptiondisabled) {\n this.player.off('audiodescriptiondisabled', this.handlers.audiodescriptiondisabled);\n }\n\n // Remove text cue update listener\n if (this.handlers.textcuesupdate) {\n this.player.off('textcuesupdate', this.handlers.textcuesupdate);\n }\n\n // Remove floating-state listener\n if (this.handlers.floatingchange) {\n this.player.off('floatingchange', this.handlers.floatingchange);\n }\n \n // Remove settings button event listeners\n if (this.settingsButton) {\n if (this.handlers.settingsClick) {\n this.settingsButton.removeEventListener('click', this.handlers.settingsClick);\n }\n if (this.handlers.settingsKeydown) {\n this.settingsButton.removeEventListener('keydown', this.handlers.settingsKeydown);\n }\n }\n \n // Remove style dialog event listeners\n if (this.handlers.styleDialogKeydown) {\n document.removeEventListener('keydown', this.handlers.styleDialogKeydown);\n }\n\n // Remove document click listener\n if (this.handlers.documentClick) {\n document.removeEventListener('click', this.handlers.documentClick);\n }\n \n // Remove window-level listeners\n if (this.handlers.resize) {\n window.removeEventListener('resize', this.handlers.resize);\n }\n\n // Cleanup all managed timeouts\n this.timeouts.forEach((timeoutId: ReturnType<typeof setTimeout>) => clearTimeout(timeoutId));\n this.timeouts.clear();\n\n this.handlers = {} as TranscriptHandlers;\n\n // Remove DOM element\n if (this.transcriptWindow && this.transcriptWindow.parentNode) {\n this.transcriptWindow.parentNode.removeChild(this.transcriptWindow);\n }\n \n this.transcriptWindow = null;\n this.transcriptHeader = null;\n this.transcriptContent = null;\n this.transcriptEntries = [];\n this.styleDialog = null;\n this.transcriptResizeHandles = [];\n this.liveRegion = null;\n this._vttCache.clear();\n }\n\n announceLive(message: string) {\n if (!this.liveRegion) return;\n this.liveRegion.textContent = message || '';\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* VidPly v1.2.
|
|
2
|
+
* VidPly v1.2.3 - Universal, Accessible Video Player
|
|
3
3
|
* (c) 2026 Matthias Peltzer
|
|
4
4
|
* Released under GPL-2.0-or-later License
|
|
5
5
|
*/
|
|
6
6
|
import {
|
|
7
7
|
loadScriptOnce
|
|
8
|
-
} from "./vidply.chunk-
|
|
8
|
+
} from "./vidply.chunk-JZNZX32Z.js";
|
|
9
9
|
|
|
10
10
|
// src/renderers/VimeoRenderer.ts
|
|
11
11
|
var VimeoRenderer = class {
|
|
@@ -236,4 +236,4 @@ var VimeoRenderer = class {
|
|
|
236
236
|
export {
|
|
237
237
|
VimeoRenderer
|
|
238
238
|
};
|
|
239
|
-
//# sourceMappingURL=vidply.VimeoRenderer-
|
|
239
|
+
//# sourceMappingURL=vidply.VimeoRenderer-RQFQD7OE.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/renderers/VimeoRenderer.ts"],
|
|
4
|
+
"sourcesContent": ["import type { Renderer } from '../types/renderer.js';\nimport type { Player } from '../core/Player.js';\nimport { loadScriptOnce } from '../utils/ScriptLoader.js';\n\nexport class VimeoRenderer implements Renderer {\n player: Player;\n media: HTMLMediaElement;\n vimeo: VimeoPlayer | null;\n videoId: string | null;\n isReady: boolean;\n iframe: HTMLDivElement | null;\n\n constructor(player: Player) {\n this.player = player;\n this.media = player.element;\n this.vimeo = null;\n this.videoId = null;\n this.isReady = false;\n this.iframe = null;\n }\n\n async init() {\n // Extract video ID from URL - use currentSource which works for external renderers\n const src = this.player.currentSource || this.player.element.src;\n this.videoId = this.extractVideoId(src);\n \n if (!this.videoId) {\n throw new Error('Invalid Vimeo URL');\n }\n\n // Load Vimeo Player API\n await this.loadVimeoAPI();\n\n // Create iframe\n this.createIframe();\n\n // Initialize player\n await this.initializePlayer();\n }\n\n extractVideoId(url: string) {\n const patterns = [\n /vimeo\\.com\\/(\\d+)/,\n /vimeo\\.com\\/video\\/(\\d+)/,\n /player\\.vimeo\\.com\\/video\\/(\\d+)/\n ];\n\n for (const pattern of patterns) {\n const match = url.match(pattern);\n if (match && match[1]) {\n return match[1];\n }\n }\n\n return null;\n }\n\n async loadVimeoAPI() {\n // Check if API is already loaded\n if (window.Vimeo && window.Vimeo.Player) {\n return Promise.resolve();\n }\n\n return loadScriptOnce('https://player.vimeo.com/api/player.js', {\n isReady: () => !!(window.Vimeo && window.Vimeo.Player)\n });\n }\n\n createIframe() {\n // Hide original element\n this.player.element.style.display = 'none';\n\n // Create container for iframe\n this.iframe = document.createElement('div');\n this.iframe.id = `vimeo-player-${Math.random().toString(36).substr(2, 9)}`;\n this.iframe.style.width = '100%';\n this.iframe.style.maxHeight = '100%';\n \n this.player.element.parentNode?.insertBefore(this.iframe, this.player.element);\n }\n\n async initializePlayer() {\n const options: Record<string, unknown> = {\n id: this.videoId,\n width: '100%',\n height: '100%',\n controls: true,\n autoplay: this.player.options.autoplay,\n muted: this.player.options.muted,\n loop: this.player.options.loop,\n keyboard: false\n };\n\n if (this.player.options.startTime > 0) {\n options.startTime = this.player.options.startTime;\n }\n\n if (!window.Vimeo || !this.iframe) {\n // The SDK never became available (or the iframe container is missing).\n // Reject so Player.init() surfaces a real failure instead of treating a\n // non-functional embed as a successful initialization.\n throw new Error('Vimeo Player API is not available');\n }\n this.vimeo = new window.Vimeo.Player(this.iframe.id, options as VimeoPlayerOptions);\n\n // Wait for player to be ready\n await this.vimeo.ready();\n this.isReady = true;\n \n // Ensure the iframe has 100% width and height\n const vimeoIframe = this.iframe?.querySelector('iframe');\n if (vimeoIframe) {\n vimeoIframe.style.width = '100%';\n vimeoIframe.style.height = '100%';\n vimeoIframe.setAttribute('width', '100%');\n vimeoIframe.setAttribute('height', '100%');\n }\n \n // Hide VidPly controls - Vimeo has its own\n if (this.player.container) {\n this.player.container.classList.add('vidply-external-controls');\n }\n\n this.attachEvents();\n\n // Get initial duration\n try {\n const duration = await this.vimeo.getDuration();\n this.player.state.duration = duration;\n this.player.emit('loadedmetadata');\n } catch (error) {\n this.player.log('Error getting duration:', error, 'warn');\n }\n }\n\n attachEvents() {\n const vimeo = this.vimeo;\n if (!vimeo) return;\n\n // Vimeo's on() callback signature is (...args: unknown[]) => void, so\n // each handler narrows its own payload before reading fields.\n vimeo.on('play', () => {\n this.player.state.playing = true;\n this.player.state.paused = false;\n this.player.state.ended = false;\n this.player.emit('play');\n\n if (this.player.options.onPlay) {\n this.player.options.onPlay.call(this.player);\n }\n });\n\n vimeo.on('pause', () => {\n this.player.state.playing = false;\n this.player.state.paused = true;\n this.player.emit('pause');\n\n if (this.player.options.onPause) {\n this.player.options.onPause.call(this.player);\n }\n });\n\n vimeo.on('ended', () => {\n this.player.state.playing = false;\n this.player.state.paused = true;\n this.player.state.ended = true;\n this.player.emit('ended');\n\n if (this.player.options.onEnded) {\n this.player.options.onEnded.call(this.player);\n }\n });\n\n vimeo.on('timeupdate', (...args: unknown[]) => {\n const data = args[0] as { seconds: number; duration: number };\n this.player.state.currentTime = data.seconds;\n this.player.state.duration = data.duration;\n this.player.emit('timeupdate', data.seconds);\n\n if (this.player.options.onTimeUpdate) {\n this.player.options.onTimeUpdate.call(this.player, data.seconds);\n }\n });\n\n vimeo.on('volumechange', (...args: unknown[]) => {\n const data = args[0] as { volume: number };\n this.player.state.volume = data.volume;\n this.player.emit('volumechange', data.volume);\n });\n\n vimeo.on('bufferstart', () => {\n this.player.state.buffering = true;\n this.player.emit('waiting');\n });\n\n vimeo.on('bufferend', () => {\n this.player.state.buffering = false;\n this.player.emit('canplay');\n });\n\n vimeo.on('seeking', () => {\n this.player.state.seeking = true;\n this.player.emit('seeking');\n });\n\n vimeo.on('seeked', () => {\n this.player.state.seeking = false;\n this.player.emit('seeked');\n });\n\n vimeo.on('playbackratechange', (...args: unknown[]) => {\n const data = args[0] as { playbackRate: number };\n this.player.state.playbackSpeed = data.playbackRate;\n this.player.emit('ratechange', data.playbackRate);\n });\n\n vimeo.on('error', (...args: unknown[]) => {\n const error = args[0] as { message?: string } | undefined;\n this.player.handleError(new Error(`Vimeo error: ${error?.message ?? 'unknown'}`));\n });\n }\n\n play() {\n if (this.isReady && this.vimeo) {\n // Save scroll position to prevent browser from scrolling to video\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n \n this.vimeo.play().catch((error: unknown) => {\n this.player.log('Play error:', error, 'warn');\n });\n \n // Restore scroll position immediately to prevent auto-scroll\n window.scrollTo(scrollX, scrollY);\n }\n }\n\n pause() {\n if (this.isReady && this.vimeo) {\n this.vimeo.pause().catch((error: unknown) => {\n this.player.log('Pause error:', error, 'warn');\n });\n }\n }\n\n seek(time: number) {\n if (this.isReady && this.vimeo) {\n this.vimeo.setCurrentTime(time).catch((error: unknown) => {\n this.player.log('Seek error:', error, 'warn');\n });\n }\n }\n\n setVolume(volume: number) {\n if (this.isReady && this.vimeo) {\n this.vimeo.setVolume(volume).catch((error: unknown) => {\n this.player.log('Volume error:', error, 'warn');\n });\n this.player.state.volume = volume;\n }\n }\n\n setMuted(muted: boolean) {\n if (this.isReady && this.vimeo) {\n if (muted) {\n this.vimeo.setVolume(0);\n } else {\n this.vimeo.setVolume(this.player.state.volume);\n }\n this.player.state.muted = muted;\n }\n }\n\n setPlaybackSpeed(speed: number) {\n if (this.isReady && this.vimeo) {\n this.vimeo.setPlaybackRate(speed).catch((error: unknown) => {\n this.player.log('Playback rate error:', error, 'warn');\n });\n this.player.state.playbackSpeed = speed;\n }\n }\n\n destroy() {\n if (this.vimeo && this.vimeo.destroy) {\n this.vimeo.destroy();\n }\n\n if (this.iframe && this.iframe.parentNode) {\n this.iframe.parentNode.removeChild(this.iframe);\n }\n\n // Show original element\n if (this.player.element) {\n this.player.element.style.display = '';\n }\n }\n}\n\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;AAIO,IAAM,gBAAN,MAAwC;AAAA,EAC7C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA,YAAY,QAAgB;AAC1B,SAAK,SAAS;AACd,SAAK,QAAQ,OAAO;AACpB,SAAK,QAAQ;AACb,SAAK,UAAU;AACf,SAAK,UAAU;AACf,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,MAAM,OAAO;AAEX,UAAM,MAAM,KAAK,OAAO,iBAAiB,KAAK,OAAO,QAAQ;AAC7D,SAAK,UAAU,KAAK,eAAe,GAAG;AAEtC,QAAI,CAAC,KAAK,SAAS;AACjB,YAAM,IAAI,MAAM,mBAAmB;AAAA,IACrC;AAGA,UAAM,KAAK,aAAa;AAGxB,SAAK,aAAa;AAGlB,UAAM,KAAK,iBAAiB;AAAA,EAC9B;AAAA,EAEA,eAAe,KAAa;AAC1B,UAAM,WAAW;AAAA,MACf;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEA,eAAW,WAAW,UAAU;AAC9B,YAAM,QAAQ,IAAI,MAAM,OAAO;AAC/B,UAAI,SAAS,MAAM,CAAC,GAAG;AACrB,eAAO,MAAM,CAAC;AAAA,MAChB;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,eAAe;AAEnB,QAAI,OAAO,SAAS,OAAO,MAAM,QAAQ;AACvC,aAAO,QAAQ,QAAQ;AAAA,IACzB;AAEA,WAAO,eAAe,0CAA0C;AAAA,MAC9D,SAAS,MAAM,CAAC,EAAE,OAAO,SAAS,OAAO,MAAM;AAAA,IACjD,CAAC;AAAA,EACH;AAAA,EAEA,eAAe;AAEb,SAAK,OAAO,QAAQ,MAAM,UAAU;AAGpC,SAAK,SAAS,SAAS,cAAc,KAAK;AAC1C,SAAK,OAAO,KAAK,gBAAgB,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,OAAO,GAAG,CAAC,CAAC;AACxE,SAAK,OAAO,MAAM,QAAQ;AAC1B,SAAK,OAAO,MAAM,YAAY;AAE9B,SAAK,OAAO,QAAQ,YAAY,aAAa,KAAK,QAAQ,KAAK,OAAO,OAAO;AAAA,EAC/E;AAAA,EAEA,MAAM,mBAAmB;AACvB,UAAM,UAAmC;AAAA,MACvC,IAAI,KAAK;AAAA,MACT,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,UAAU;AAAA,MACV,UAAU,KAAK,OAAO,QAAQ;AAAA,MAC9B,OAAO,KAAK,OAAO,QAAQ;AAAA,MAC3B,MAAM,KAAK,OAAO,QAAQ;AAAA,MAC1B,UAAU;AAAA,IACZ;AAEA,QAAI,KAAK,OAAO,QAAQ,YAAY,GAAG;AACrC,cAAQ,YAAY,KAAK,OAAO,QAAQ;AAAA,IAC1C;AAEA,QAAI,CAAC,OAAO,SAAS,CAAC,KAAK,QAAQ;AAIjC,YAAM,IAAI,MAAM,mCAAmC;AAAA,IACrD;AACA,SAAK,QAAQ,IAAI,OAAO,MAAM,OAAO,KAAK,OAAO,IAAI,OAA6B;AAGlF,UAAM,KAAK,MAAM,MAAM;AACvB,SAAK,UAAU;AAGf,UAAM,cAAc,KAAK,QAAQ,cAAc,QAAQ;AACvD,QAAI,aAAa;AACf,kBAAY,MAAM,QAAQ;AAC1B,kBAAY,MAAM,SAAS;AAC3B,kBAAY,aAAa,SAAS,MAAM;AACxC,kBAAY,aAAa,UAAU,MAAM;AAAA,IAC3C;AAGA,QAAI,KAAK,OAAO,WAAW;AACzB,WAAK,OAAO,UAAU,UAAU,IAAI,0BAA0B;AAAA,IAChE;AAEA,SAAK,aAAa;AAGlB,QAAI;AACF,YAAM,WAAW,MAAM,KAAK,MAAM,YAAY;AAC9C,WAAK,OAAO,MAAM,WAAW;AAC7B,WAAK,OAAO,KAAK,gBAAgB;AAAA,IACnC,SAAS,OAAO;AACd,WAAK,OAAO,IAAI,2BAA2B,OAAO,MAAM;AAAA,IAC1D;AAAA,EACF;AAAA,EAEA,eAAe;AACb,UAAM,QAAQ,KAAK;AACnB,QAAI,CAAC,MAAO;AAIZ,UAAM,GAAG,QAAQ,MAAM;AACrB,WAAK,OAAO,MAAM,UAAU;AAC5B,WAAK,OAAO,MAAM,SAAS;AAC3B,WAAK,OAAO,MAAM,QAAQ;AAC1B,WAAK,OAAO,KAAK,MAAM;AAEvB,UAAI,KAAK,OAAO,QAAQ,QAAQ;AAC9B,aAAK,OAAO,QAAQ,OAAO,KAAK,KAAK,MAAM;AAAA,MAC7C;AAAA,IACF,CAAC;AAED,UAAM,GAAG,SAAS,MAAM;AACtB,WAAK,OAAO,MAAM,UAAU;AAC5B,WAAK,OAAO,MAAM,SAAS;AAC3B,WAAK,OAAO,KAAK,OAAO;AAExB,UAAI,KAAK,OAAO,QAAQ,SAAS;AAC/B,aAAK,OAAO,QAAQ,QAAQ,KAAK,KAAK,MAAM;AAAA,MAC9C;AAAA,IACF,CAAC;AAED,UAAM,GAAG,SAAS,MAAM;AACtB,WAAK,OAAO,MAAM,UAAU;AAC5B,WAAK,OAAO,MAAM,SAAS;AAC3B,WAAK,OAAO,MAAM,QAAQ;AAC1B,WAAK,OAAO,KAAK,OAAO;AAExB,UAAI,KAAK,OAAO,QAAQ,SAAS;AAC/B,aAAK,OAAO,QAAQ,QAAQ,KAAK,KAAK,MAAM;AAAA,MAC9C;AAAA,IACF,CAAC;AAED,UAAM,GAAG,cAAc,IAAI,SAAoB;AAC7C,YAAM,OAAO,KAAK,CAAC;AACnB,WAAK,OAAO,MAAM,cAAc,KAAK;AACrC,WAAK,OAAO,MAAM,WAAW,KAAK;AAClC,WAAK,OAAO,KAAK,cAAc,KAAK,OAAO;AAE3C,UAAI,KAAK,OAAO,QAAQ,cAAc;AACpC,aAAK,OAAO,QAAQ,aAAa,KAAK,KAAK,QAAQ,KAAK,OAAO;AAAA,MACjE;AAAA,IACF,CAAC;AAED,UAAM,GAAG,gBAAgB,IAAI,SAAoB;AAC/C,YAAM,OAAO,KAAK,CAAC;AACnB,WAAK,OAAO,MAAM,SAAS,KAAK;AAChC,WAAK,OAAO,KAAK,gBAAgB,KAAK,MAAM;AAAA,IAC9C,CAAC;AAED,UAAM,GAAG,eAAe,MAAM;AAC5B,WAAK,OAAO,MAAM,YAAY;AAC9B,WAAK,OAAO,KAAK,SAAS;AAAA,IAC5B,CAAC;AAED,UAAM,GAAG,aAAa,MAAM;AAC1B,WAAK,OAAO,MAAM,YAAY;AAC9B,WAAK,OAAO,KAAK,SAAS;AAAA,IAC5B,CAAC;AAED,UAAM,GAAG,WAAW,MAAM;AACxB,WAAK,OAAO,MAAM,UAAU;AAC5B,WAAK,OAAO,KAAK,SAAS;AAAA,IAC5B,CAAC;AAED,UAAM,GAAG,UAAU,MAAM;AACvB,WAAK,OAAO,MAAM,UAAU;AAC5B,WAAK,OAAO,KAAK,QAAQ;AAAA,IAC3B,CAAC;AAED,UAAM,GAAG,sBAAsB,IAAI,SAAoB;AACrD,YAAM,OAAO,KAAK,CAAC;AACnB,WAAK,OAAO,MAAM,gBAAgB,KAAK;AACvC,WAAK,OAAO,KAAK,cAAc,KAAK,YAAY;AAAA,IAClD,CAAC;AAED,UAAM,GAAG,SAAS,IAAI,SAAoB;AACxC,YAAM,QAAQ,KAAK,CAAC;AACpB,WAAK,OAAO,YAAY,IAAI,MAAM,gBAAgB,OAAO,WAAW,SAAS,EAAE,CAAC;AAAA,IAClF,CAAC;AAAA,EACH;AAAA,EAEA,OAAO;AACL,QAAI,KAAK,WAAW,KAAK,OAAO;AAE9B,YAAM,UAAU,OAAO;AACvB,YAAM,UAAU,OAAO;AAEvB,WAAK,MAAM,KAAK,EAAE,MAAM,CAAC,UAAmB;AAC1C,aAAK,OAAO,IAAI,eAAe,OAAO,MAAM;AAAA,MAC9C,CAAC;AAGD,aAAO,SAAS,SAAS,OAAO;AAAA,IAClC;AAAA,EACF;AAAA,EAEA,QAAQ;AACN,QAAI,KAAK,WAAW,KAAK,OAAO;AAC9B,WAAK,MAAM,MAAM,EAAE,MAAM,CAAC,UAAmB;AAC3C,aAAK,OAAO,IAAI,gBAAgB,OAAO,MAAM;AAAA,MAC/C,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,KAAK,MAAc;AACjB,QAAI,KAAK,WAAW,KAAK,OAAO;AAC9B,WAAK,MAAM,eAAe,IAAI,EAAE,MAAM,CAAC,UAAmB;AACxD,aAAK,OAAO,IAAI,eAAe,OAAO,MAAM;AAAA,MAC9C,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,UAAU,QAAgB;AACxB,QAAI,KAAK,WAAW,KAAK,OAAO;AAC9B,WAAK,MAAM,UAAU,MAAM,EAAE,MAAM,CAAC,UAAmB;AACrD,aAAK,OAAO,IAAI,iBAAiB,OAAO,MAAM;AAAA,MAChD,CAAC;AACD,WAAK,OAAO,MAAM,SAAS;AAAA,IAC7B;AAAA,EACF;AAAA,EAEA,SAAS,OAAgB;AACvB,QAAI,KAAK,WAAW,KAAK,OAAO;AAC9B,UAAI,OAAO;AACT,aAAK,MAAM,UAAU,CAAC;AAAA,MACxB,OAAO;AACL,aAAK,MAAM,UAAU,KAAK,OAAO,MAAM,MAAM;AAAA,MAC/C;AACA,WAAK,OAAO,MAAM,QAAQ;AAAA,IAC5B;AAAA,EACF;AAAA,EAEA,iBAAiB,OAAe;AAC9B,QAAI,KAAK,WAAW,KAAK,OAAO;AAC9B,WAAK,MAAM,gBAAgB,KAAK,EAAE,MAAM,CAAC,UAAmB;AAC1D,aAAK,OAAO,IAAI,wBAAwB,OAAO,MAAM;AAAA,MACvD,CAAC;AACD,WAAK,OAAO,MAAM,gBAAgB;AAAA,IACpC;AAAA,EACF;AAAA,EAEA,UAAU;AACR,QAAI,KAAK,SAAS,KAAK,MAAM,SAAS;AACpC,WAAK,MAAM,QAAQ;AAAA,IACrB;AAEA,QAAI,KAAK,UAAU,KAAK,OAAO,YAAY;AACzC,WAAK,OAAO,WAAW,YAAY,KAAK,MAAM;AAAA,IAChD;AAGA,QAAI,KAAK,OAAO,SAAS;AACvB,WAAK,OAAO,QAAQ,MAAM,UAAU;AAAA,IACtC;AAAA,EACF;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* VidPly v1.2.
|
|
2
|
+
* VidPly v1.2.3 - Universal, Accessible Video Player
|
|
3
3
|
* (c) 2026 Matthias Peltzer
|
|
4
4
|
* Released under GPL-2.0-or-later License
|
|
5
5
|
*/
|
|
6
6
|
import {
|
|
7
7
|
loadScriptOnce
|
|
8
|
-
} from "./vidply.chunk-
|
|
8
|
+
} from "./vidply.chunk-JZNZX32Z.js";
|
|
9
9
|
|
|
10
10
|
// src/renderers/YouTubeRenderer.ts
|
|
11
11
|
var YouTubeRenderer = class {
|
|
@@ -235,4 +235,4 @@ var YouTubeRenderer = class {
|
|
|
235
235
|
export {
|
|
236
236
|
YouTubeRenderer
|
|
237
237
|
};
|
|
238
|
-
//# sourceMappingURL=vidply.YouTubeRenderer-
|
|
238
|
+
//# sourceMappingURL=vidply.YouTubeRenderer-EU2BARZF.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/renderers/YouTubeRenderer.ts"],
|
|
4
|
+
"sourcesContent": ["import type { Renderer } from '../types/renderer.js';\nimport type { Player } from '../core/Player.js';\nimport { loadScriptOnce } from '../utils/ScriptLoader.js';\n\nexport class YouTubeRenderer implements Renderer {\n player: Player;\n media: HTMLMediaElement;\n youtube: YTPlayer | null;\n videoId: string | null;\n isReady: boolean;\n iframe: HTMLDivElement | null;\n timeUpdateInterval?: ReturnType<typeof setInterval>;\n\n constructor(player: Player) {\n this.player = player;\n this.media = player.element;\n this.youtube = null;\n this.videoId = null;\n this.isReady = false;\n this.iframe = null;\n }\n\n async init() {\n // Extract video ID from URL - use currentSource which works for external renderers\n const src = this.player.currentSource || this.player.element.src;\n this.videoId = this.extractVideoId(src);\n \n if (!this.videoId) {\n throw new Error('Invalid YouTube URL');\n }\n\n // Load YouTube IFrame API\n await this.loadYouTubeAPI();\n\n // Create iframe\n this.createIframe();\n\n // Initialize player\n await this.initializePlayer();\n }\n\n extractVideoId(url: string) {\n const patterns = [\n /(?:youtube\\.com\\/watch\\?v=|youtu\\.be\\/)([^&\\s]+)/,\n /youtube\\.com\\/embed\\/([^&\\s]+)/\n ];\n\n for (const pattern of patterns) {\n const match = url.match(pattern);\n if (match && match[1]) {\n return match[1];\n }\n }\n\n return null;\n }\n\n async loadYouTubeAPI() {\n // Check if API is already loaded\n if (window.YT && window.YT.Player) {\n return Promise.resolve();\n }\n\n // The IFrame API exposes window.YT.Player a short time after its script\n // loads (it signals readiness via the global onYouTubeIframeAPIReady\n // callback). loadScriptOnce dedupes the injection and the readiness\n // predicate polls for YT.Player, also covering the case where the host\n // page already included the API script.\n return loadScriptOnce('https://www.youtube.com/iframe_api', {\n isReady: () => !!(window.YT && window.YT.Player),\n readyTimeout: 8000\n });\n }\n\n createIframe() {\n // Hide original element\n this.player.element.style.display = 'none';\n\n // Create container for iframe\n this.iframe = document.createElement('div');\n this.iframe.id = `youtube-player-${Math.random().toString(36).substr(2, 9)}`;\n this.iframe.style.width = '100%';\n this.iframe.style.maxHeight = '100%';\n \n this.player.element.parentNode?.insertBefore(this.iframe, this.player.element);\n }\n\n async initializePlayer() {\n return new Promise<void>((resolve, reject) => {\n if (!window.YT || !this.iframe) {\n // The SDK never became available (or the iframe container is missing).\n // Reject so Player.init() surfaces a real failure instead of treating\n // a non-functional embed as a successful initialization.\n reject(new Error('YouTube IFrame API is not available'));\n return;\n }\n this.youtube = new window.YT.Player(this.iframe.id, {\n videoId: this.videoId ?? undefined,\n width: '100%',\n height: '100%',\n playerVars: {\n controls: 1, // Use YouTube native controls\n disablekb: 0, // Allow keyboard controls\n fs: 1, // Allow fullscreen\n modestbranding: 1,\n rel: 0,\n showinfo: 0,\n iv_load_policy: 3,\n autoplay: this.player.options.autoplay ? 1 : 0,\n mute: this.player.options.muted ? 1 : 0,\n start: this.player.options.startTime || 0\n },\n events: {\n onReady: (_event: unknown) => {\n this.isReady = true;\n this.attachEvents();\n // Hide VidPly controls - YouTube has its own\n if (this.player.container) {\n this.player.container.classList.add('vidply-external-controls');\n }\n resolve();\n },\n onStateChange: (event: unknown) => this.handleStateChange(event as { data: number }),\n onError: (event: unknown) => this.handleError(event as { data: 2 | 5 | 100 | 101 | 150 })\n }\n });\n });\n }\n\n attachEvents() {\n // Set up polling for time updates (YouTube doesn't provide timeupdate events)\n this.timeUpdateInterval = setInterval(() => {\n const youtube = this.youtube;\n if (this.isReady && youtube) {\n const currentTime = youtube.getCurrentTime();\n const duration = youtube.getDuration();\n\n this.player.state.currentTime = currentTime;\n this.player.state.duration = duration;\n\n this.player.emit('timeupdate', currentTime);\n }\n }, 250);\n\n // Initial metadata\n const youtube = this.youtube;\n if (youtube && youtube.getDuration) {\n this.player.state.duration = youtube.getDuration();\n this.player.emit('loadedmetadata');\n }\n }\n\n handleStateChange(event: { data: number }) {\n const states = window.YT?.PlayerState;\n if (!states) return;\n\n switch (event.data) {\n case states.PLAYING:\n this.player.state.playing = true;\n this.player.state.paused = false;\n this.player.state.ended = false;\n this.player.state.buffering = false;\n this.player.emit('play');\n this.player.emit('playing');\n \n if (this.player.options.onPlay) {\n this.player.options.onPlay.call(this.player);\n }\n break;\n\n case states.PAUSED:\n this.player.state.playing = false;\n this.player.state.paused = true;\n this.player.emit('pause');\n \n if (this.player.options.onPause) {\n this.player.options.onPause.call(this.player);\n }\n break;\n\n case states.ENDED:\n this.player.state.playing = false;\n this.player.state.paused = true;\n this.player.state.ended = true;\n this.player.emit('ended');\n\n if (this.player.options.onEnded) {\n this.player.options.onEnded.call(this.player);\n }\n\n if (this.player.options.loop && this.youtube) {\n this.youtube.seekTo(0);\n this.youtube.playVideo();\n }\n break;\n\n case states.BUFFERING:\n this.player.state.buffering = true;\n this.player.emit('waiting');\n break;\n\n case states.CUED:\n this.player.emit('loadedmetadata');\n break;\n }\n }\n\n handleError(event: { data: 2 | 5 | 100 | 101 | 150 }) {\n const errors = {\n 2: 'Invalid video ID',\n 5: 'HTML5 player error',\n 100: 'Video not found',\n 101: 'Video not allowed to be played in embedded players',\n 150: 'Video not allowed to be played in embedded players'\n };\n\n const error = new Error(errors[event.data] || 'YouTube player error');\n this.player.handleError(error);\n }\n\n play() {\n if (this.isReady && this.youtube) {\n // Save scroll position to prevent browser from scrolling to video\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n \n this.youtube.playVideo();\n \n // Restore scroll position immediately to prevent auto-scroll\n window.scrollTo(scrollX, scrollY);\n }\n }\n\n pause() {\n if (this.isReady && this.youtube) {\n this.youtube.pauseVideo();\n }\n }\n\n seek(time: number) {\n if (this.isReady && this.youtube) {\n this.youtube.seekTo(time, true);\n }\n }\n\n setVolume(volume: number) {\n if (this.isReady && this.youtube) {\n this.youtube.setVolume(volume * 100);\n this.player.state.volume = volume;\n }\n }\n\n setMuted(muted: boolean) {\n if (this.isReady && this.youtube) {\n if (muted) {\n this.youtube.mute();\n } else {\n this.youtube.unMute();\n }\n this.player.state.muted = muted;\n }\n }\n\n setPlaybackSpeed(speed: number) {\n if (this.isReady && this.youtube) {\n this.youtube.setPlaybackRate(speed);\n this.player.state.playbackSpeed = speed;\n }\n }\n\n destroy() {\n if (this.timeUpdateInterval) {\n clearInterval(this.timeUpdateInterval);\n }\n\n if (this.youtube && this.youtube.destroy) {\n this.youtube.destroy();\n }\n\n if (this.iframe && this.iframe.parentNode) {\n this.iframe.parentNode.removeChild(this.iframe);\n }\n\n // Show original element\n if (this.player.element) {\n this.player.element.style.display = '';\n }\n }\n}\n\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["youtube"]
|
|
7
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* VidPly v1.2.
|
|
2
|
+
* VidPly v1.2.3 - Universal, Accessible Video Player
|
|
3
3
|
* (c) 2026 Matthias Peltzer
|
|
4
4
|
* Released under GPL-2.0-or-later License
|
|
5
5
|
*/
|
|
6
6
|
import {
|
|
7
7
|
i18n,
|
|
8
8
|
shallowSanitize
|
|
9
|
-
} from "./vidply.chunk-
|
|
9
|
+
} from "./vidply.chunk-I6EDRYGJ.js";
|
|
10
10
|
|
|
11
11
|
// src/utils/StorageManager.ts
|
|
12
12
|
function clamp(n, min, max) {
|
|
@@ -255,4 +255,4 @@ export {
|
|
|
255
255
|
StorageManager,
|
|
256
256
|
deriveTrackLabel
|
|
257
257
|
};
|
|
258
|
-
//# sourceMappingURL=vidply.chunk-
|
|
258
|
+
//# sourceMappingURL=vidply.chunk-6LYZYI5G.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/utils/StorageManager.ts", "../../src/utils/TrackLabelUtils.ts"],
|
|
4
|
+
"sourcesContent": ["import { shallowSanitize } from './Sanitize.js';\n\n/**\n * Type guard helpers used by the validated `get()` overload below.\n * Each guard accepts an unknown JSON value and tells TypeScript whether\n * it matches the expected runtime shape.\n */\nexport type Validator<T> = (value: unknown) => value is T;\n\ninterface WatchProgressEntry {\n currentTime: number;\n duration: number;\n percentage: number;\n updatedAt: number;\n}\n\n/**\n * Loose alias for stored preference payloads. Numeric/string-typed fields\n * are validated at the boundary (`isPlainObject`) and clamped where it\n * matters (volume, playbackSpeed). Individual properties remain `unknown`\n * so callers must narrow before use.\n */\nexport type StoredPreferences = Record<string, unknown>;\n\nfunction clamp(n: number, min: number, max: number): number {\n if (!Number.isFinite(n)) return min;\n if (n < min) return min;\n if (n > max) return max;\n return n;\n}\n\nfunction isFiniteNonNegative(value: unknown): value is number {\n return typeof value === 'number' && Number.isFinite(value) && value >= 0;\n}\n\nfunction isWatchProgressEntry(value: unknown): value is WatchProgressEntry {\n if (!value || typeof value !== 'object') return false;\n const v = value as Record<string, unknown>;\n return (\n isFiniteNonNegative(v.currentTime) &&\n isFiniteNonNegative(v.duration) &&\n typeof v.percentage === 'number' &&\n Number.isFinite(v.percentage) &&\n typeof v.updatedAt === 'number' &&\n Number.isFinite(v.updatedAt)\n );\n}\n\nexport class StorageManager {\n private namespace: string;\n private storage: Storage | null;\n\n static MAX_WATCH_PROGRESS_ENTRIES = 100;\n\n constructor(namespace = 'vidply') {\n this.namespace = namespace;\n this.storage = this.isStorageAvailable() ? localStorage : null;\n }\n\n /**\n * `localStorage` access can throw in private-browsing modes (Safari) and\n * is undefined in non-DOM environments. Both are tolerated here so the\n * Player still works (without persistence) when storage is unavailable.\n */\n isStorageAvailable(): boolean {\n try {\n if (typeof localStorage === 'undefined') return false;\n const test = '__storage_test__';\n localStorage.setItem(test, test);\n localStorage.removeItem(test);\n return true;\n } catch {\n return false;\n }\n }\n\n getKey(key: string): string {\n return `${this.namespace}_${key}`;\n }\n\n set(key: string, value: unknown): boolean {\n if (!this.storage) return false;\n\n try {\n const namespacedKey = this.getKey(key);\n this.storage.setItem(namespacedKey, JSON.stringify(value));\n return true;\n } catch (e) {\n console.warn('Failed to save to localStorage:', e);\n return false;\n }\n }\n\n /**\n * Generic get. Accepts an optional `validator` so callers can assert the\n * runtime shape of the parsed JSON before trusting it. Falls back to\n * `defaultValue` if the payload fails validation.\n */\n get<T = unknown>(key: string, defaultValue: T | null = null, validator?: Validator<T>): T | null {\n if (!this.storage) return defaultValue;\n\n try {\n const namespacedKey = this.getKey(key);\n const raw = this.storage.getItem(namespacedKey);\n if (raw === null) return defaultValue;\n const parsed: unknown = JSON.parse(raw);\n if (validator && !validator(parsed)) {\n console.warn(`[VidPly] Discarding malformed localStorage payload for \"${key}\"`);\n return defaultValue;\n }\n return parsed as T;\n } catch (e) {\n console.warn('Failed to read from localStorage:', e);\n return defaultValue;\n }\n }\n\n remove(key: string): boolean {\n if (!this.storage) return false;\n\n try {\n const namespacedKey = this.getKey(key);\n this.storage.removeItem(namespacedKey);\n return true;\n } catch (e) {\n console.warn('Failed to remove from localStorage:', e);\n return false;\n }\n }\n\n clear(): boolean {\n if (!this.storage) return false;\n\n try {\n const storage = this.storage;\n const keys = Object.keys(storage);\n keys.forEach(key => {\n if (key.startsWith(this.namespace)) {\n storage.removeItem(key);\n }\n });\n return true;\n } catch (e) {\n console.warn('Failed to clear localStorage:', e);\n return false;\n }\n }\n\n saveTranscriptPreferences(preferences: StoredPreferences): boolean {\n return this.set('transcript_preferences', shallowSanitize(preferences));\n }\n\n getTranscriptPreferences(): StoredPreferences | null {\n return this.get<StoredPreferences>(\n 'transcript_preferences',\n null,\n isPlainObject as Validator<StoredPreferences>\n );\n }\n\n saveCaptionPreferences(preferences: StoredPreferences): boolean {\n return this.set('caption_preferences', shallowSanitize(preferences));\n }\n\n getCaptionPreferences(): StoredPreferences | null {\n return this.get<StoredPreferences>('caption_preferences', null, isPlainObject as Validator<StoredPreferences>);\n }\n\n savePlayerPreferences(preferences: StoredPreferences): boolean {\n // Numeric ranges are clamped here so a tampered storage payload cannot\n // ship a `volume: 1e9` straight to a renderer.\n const sanitized = shallowSanitize(preferences);\n if (typeof sanitized.volume === 'number') {\n sanitized.volume = clamp(sanitized.volume, 0, 1);\n }\n if (typeof sanitized.playbackSpeed === 'number') {\n sanitized.playbackSpeed = clamp(sanitized.playbackSpeed, 0.1, 4);\n }\n return this.set('player_preferences', sanitized);\n }\n\n getPlayerPreferences(): StoredPreferences | null {\n const value = this.get<StoredPreferences>('player_preferences', null, isPlainObject as Validator<StoredPreferences>);\n if (!value) return null;\n if (typeof value.volume === 'number') {\n value.volume = clamp(value.volume, 0, 1);\n }\n if (typeof value.playbackSpeed === 'number') {\n value.playbackSpeed = clamp(value.playbackSpeed, 0.1, 4);\n }\n return value;\n }\n\n saveSignLanguagePreferences(preferences: StoredPreferences): boolean {\n return this.set('sign_language_preferences', shallowSanitize(preferences));\n }\n\n getSignLanguagePreferences(): StoredPreferences | null {\n return this.get<StoredPreferences>('sign_language_preferences', null, isPlainObject as Validator<StoredPreferences>);\n }\n\n saveFloatingPreferences(preferences: StoredPreferences): boolean {\n return this.set('floating_preferences', shallowSanitize(preferences));\n }\n\n getFloatingPreferences(): StoredPreferences | null {\n return this.get<StoredPreferences>('floating_preferences', null, isPlainObject as Validator<StoredPreferences>);\n }\n\n /**\n * Persist watch progress for a video id. Numeric inputs are validated +\n * clamped so a caller cannot poison the store with `Infinity`/negatives.\n */\n saveWatchProgress(videoId: string, currentTime: number, duration: number): boolean {\n if (typeof videoId !== 'string' || !videoId) return false;\n if (!Number.isFinite(currentTime) || !Number.isFinite(duration) || duration <= 0) return false;\n\n const safeDuration = clamp(duration, 0.001, 24 * 60 * 60); // cap to 24h to neutralize gigantic numbers\n const safeCurrent = clamp(currentTime, 0, safeDuration);\n\n const allProgress = this.get<Record<string, WatchProgressEntry>>(\n 'watch_progress',\n Object.create(null),\n isWatchProgressMap\n ) ?? Object.create(null) as Record<string, WatchProgressEntry>;\n\n const percentage = (safeCurrent / safeDuration) * 100;\n\n allProgress[videoId] = {\n currentTime: safeCurrent,\n duration: safeDuration,\n percentage: clamp(percentage, 0, 100),\n updatedAt: Date.now()\n };\n\n const entries = Object.entries(allProgress);\n if (entries.length > StorageManager.MAX_WATCH_PROGRESS_ENTRIES) {\n entries.sort((a, b) => a[1].updatedAt - b[1].updatedAt);\n const toRemove = entries.length - StorageManager.MAX_WATCH_PROGRESS_ENTRIES;\n for (let i = 0; i < toRemove; i++) {\n const entry = entries[i];\n if (entry) {\n delete allProgress[entry[0]];\n }\n }\n }\n\n return this.set('watch_progress', allProgress);\n }\n\n getWatchProgress(videoId: string): WatchProgressEntry | null {\n if (!videoId) return null;\n const allProgress = this.get<Record<string, WatchProgressEntry>>(\n 'watch_progress',\n Object.create(null),\n isWatchProgressMap\n ) ?? Object.create(null) as Record<string, WatchProgressEntry>;\n const entry = allProgress[videoId];\n return entry && isWatchProgressEntry(entry) ? entry : null;\n }\n\n clearWatchProgress(videoId: string): boolean {\n if (!videoId) return false;\n const allProgress = this.get<Record<string, WatchProgressEntry>>(\n 'watch_progress',\n Object.create(null),\n isWatchProgressMap\n ) ?? Object.create(null) as Record<string, WatchProgressEntry>;\n if (allProgress[videoId]) {\n delete allProgress[videoId];\n return this.set('watch_progress', allProgress);\n }\n return true;\n }\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction isWatchProgressMap(value: unknown): value is Record<string, WatchProgressEntry> {\n if (!isPlainObject(value)) return false;\n for (const entry of Object.values(value)) {\n if (!isWatchProgressEntry(entry)) return false;\n }\n return true;\n}\n", "import { i18n } from '../i18n/i18n.js';\n\n/**\n * Derive a human-readable label for a TextTrack when the source manifest\n * (DASH AdaptationSet, HLS rendition, etc.) didn't provide one. dash.js\n * falls back to the AdaptationSet @id (typically a digit like \"2\", \"3\")\n * when no <Label> child element is present, which surfaces as cryptic\n * \"2\"/\"3\" entries in the captions menu.\n *\n * dash.js 5.x additionally derives the label as `element.id ?? element.lang`.\n * When neither is present in the MPD, the value is JS `null`, and the\n * browser's TextTrack API stringifies it to the literal \"null\". We treat\n * \"null\"/\"undefined\" (any casing) as placeholders too, and fall back to a\n * localized language name via Intl.DisplayNames.\n */\nexport function deriveTrackLabel(\n rawLabel: string | null | undefined,\n language: string | null | undefined,\n fallbackKey = 'player.captions'\n): string {\n const cleanLabel = (rawLabel ?? '').trim();\n const cleanLang = (language ?? '').trim();\n\n const looksLikePlaceholder =\n cleanLabel === '' ||\n /^\\d+$/.test(cleanLabel) ||\n /^(null|undefined)$/i.test(cleanLabel);\n if (!looksLikePlaceholder) {\n return cleanLabel;\n }\n\n const cleanLangIsUsable =\n cleanLang !== '' && !/^(null|undefined)$/i.test(cleanLang);\n\n if (cleanLangIsUsable) {\n try {\n // Use endonyms: each language shown in its own name\n // (\"English\", \"Deutsch\") rather than translated into the UI\n // language (\"Englisch\", \"Deutsch\").\n const displayNames = new Intl.DisplayNames([cleanLang, 'en'], { type: 'language' });\n const name = displayNames.of(cleanLang);\n if (name && name.toLowerCase() !== cleanLang.toLowerCase()) {\n return name;\n }\n } catch {\n // Intl.DisplayNames may be unavailable — fall through to the code.\n }\n return cleanLang.toUpperCase();\n }\n\n return i18n.t(fallbackKey);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* VidPly v1.2.
|
|
2
|
+
* VidPly v1.2.3 - Universal, Accessible Video Player
|
|
3
3
|
* (c) 2026 Matthias Peltzer
|
|
4
4
|
* Released under GPL-2.0-or-later License
|
|
5
5
|
*/
|
|
6
6
|
import {
|
|
7
7
|
createIconElement
|
|
8
|
-
} from "./vidply.chunk-
|
|
8
|
+
} from "./vidply.chunk-UI3RYCVF.js";
|
|
9
9
|
import {
|
|
10
10
|
focusElement
|
|
11
|
-
} from "./vidply.chunk-
|
|
11
|
+
} from "./vidply.chunk-P25MRJ6O.js";
|
|
12
12
|
import {
|
|
13
13
|
reducedMotionScrollOptions
|
|
14
|
-
} from "./vidply.chunk-
|
|
14
|
+
} from "./vidply.chunk-PRFNESYA.js";
|
|
15
15
|
import {
|
|
16
16
|
DOMUtils,
|
|
17
17
|
i18n
|
|
18
|
-
} from "./vidply.chunk-
|
|
18
|
+
} from "./vidply.chunk-I6EDRYGJ.js";
|
|
19
19
|
|
|
20
20
|
// src/utils/FormUtils.ts
|
|
21
21
|
function createLabeledSelect({
|
|
@@ -639,4 +639,4 @@ export {
|
|
|
639
639
|
createMenuItem,
|
|
640
640
|
DraggablePanel
|
|
641
641
|
};
|
|
642
|
-
//# sourceMappingURL=vidply.chunk-
|
|
642
|
+
//# sourceMappingURL=vidply.chunk-A4ESEOQ2.js.map
|