vidply 1.2.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. package/README.md +946 -946
  2. package/dist/dev/{vidply.AudioDescriptionManager-R47Q4YR4.js → vidply.AudioDescriptionManager-6RFVF27Z.js} +275 -19
  3. package/dist/dev/vidply.AudioDescriptionManager-6RFVF27Z.js.map +7 -0
  4. package/dist/dev/{vidply.CaptionStyleMenu-7XHRBWUM.js → vidply.CaptionStyleMenu-R2SCQ6VP.js} +4 -4
  5. package/dist/dev/{vidply.DASHRenderer-PWEMC3BT.js → vidply.DASHRenderer-EOVEW73H.js} +3 -3
  6. package/dist/dev/vidply.DASHRenderer-EOVEW73H.js.map +7 -0
  7. package/dist/dev/{vidply.FloatingPlayerManager-PKNNNAS2.js → vidply.FloatingPlayerManager-V5XQWS75.js} +5 -5
  8. package/dist/dev/vidply.FloatingPlayerManager-V5XQWS75.js.map +7 -0
  9. package/dist/dev/{vidply.HLSRenderer-E7RQ74HI.js → vidply.HLSRenderer-ZBEFVH5W.js} +4 -4
  10. package/dist/dev/vidply.HLSRenderer-ZBEFVH5W.js.map +7 -0
  11. package/dist/dev/vidply.HTML5Renderer-P7ICKYU4.js +12 -0
  12. package/dist/dev/{vidply.SignLanguageManager-FET5FQZ7.js → vidply.SignLanguageManager-7IAJOVF6.js} +8 -8
  13. package/dist/dev/vidply.SignLanguageManager-7IAJOVF6.js.map +7 -0
  14. package/dist/dev/{vidply.SoundCloudRenderer-RVSJZODO.js → vidply.SoundCloudRenderer-OA7QQYGN.js} +3 -3
  15. package/dist/dev/vidply.SoundCloudRenderer-OA7QQYGN.js.map +7 -0
  16. package/dist/dev/{vidply.TranscriptManager-5GXOQCLX.js → vidply.TranscriptManager-BPM3T23L.js} +10 -10
  17. package/dist/dev/vidply.TranscriptManager-BPM3T23L.js.map +7 -0
  18. package/dist/dev/{vidply.VimeoRenderer-ER2LSBBW.js → vidply.VimeoRenderer-BRHFYPNJ.js} +3 -3
  19. package/dist/dev/vidply.VimeoRenderer-BRHFYPNJ.js.map +7 -0
  20. package/dist/dev/{vidply.YouTubeRenderer-NFTOWJAB.js → vidply.YouTubeRenderer-HRTN4EJ5.js} +3 -3
  21. package/dist/dev/vidply.YouTubeRenderer-HRTN4EJ5.js.map +7 -0
  22. package/dist/dev/{vidply.chunk-6RQA4ROD.js → vidply.chunk-5IK3IC5V.js} +2 -2
  23. package/dist/dev/{vidply.chunk-JJ47NO24.js → vidply.chunk-C55OBT5H.js} +2 -2
  24. package/dist/dev/vidply.chunk-C55OBT5H.js.map +7 -0
  25. package/dist/dev/{vidply.chunk-DZSDO3B7.js → vidply.chunk-HOYEMJSD.js} +2 -2
  26. package/dist/dev/{vidply.chunk-VMSQAZM7.js → vidply.chunk-JAKZNGOR.js} +2 -2
  27. package/dist/dev/{vidply.chunk-64WXMTGL.js → vidply.chunk-LDVFAFGC.js} +5 -5
  28. package/dist/dev/vidply.chunk-LDVFAFGC.js.map +7 -0
  29. package/dist/dev/{vidply.chunk-RZWZGDV7.js → vidply.chunk-NSMAZCMB.js} +6 -6
  30. package/dist/dev/vidply.chunk-NSMAZCMB.js.map +7 -0
  31. package/dist/dev/{vidply.chunk-KI7C3QVL.js → vidply.chunk-RQG3MA7W.js} +2 -2
  32. package/dist/dev/vidply.chunk-RQG3MA7W.js.map +7 -0
  33. package/dist/dev/{vidply.chunk-BNFIUL2Z.js → vidply.chunk-UHMVDBDB.js} +3 -3
  34. package/dist/dev/vidply.chunk-UHMVDBDB.js.map +7 -0
  35. package/dist/dev/{vidply.chunk-NQPURMQY.js → vidply.chunk-W5BR65JY.js} +6 -6
  36. package/dist/dev/vidply.chunk-W5BR65JY.js.map +7 -0
  37. package/dist/dev/{vidply.chunk-W25HUCHS.js → vidply.chunk-XAK3DPTO.js} +2 -2
  38. package/dist/dev/vidply.chunk-XAK3DPTO.js.map +7 -0
  39. package/dist/dev/{vidply.chunk-SK4V2DAU.js → vidply.chunk-YVCYQZJI.js} +3 -3
  40. package/dist/dev/{vidply.de-4YAT7FFM.js → vidply.de-5W5LBFSJ.js} +2 -2
  41. package/dist/dev/vidply.de-5W5LBFSJ.js.map +7 -0
  42. package/dist/dev/{vidply.es-LEWIRUBH.js → vidply.es-SHDAT6L6.js} +2 -2
  43. package/dist/dev/vidply.es-SHDAT6L6.js.map +7 -0
  44. package/dist/dev/vidply.esm.js +73 -34
  45. package/dist/dev/vidply.esm.js.map +2 -2
  46. package/dist/dev/{vidply.fr-Q4GV2NOE.js → vidply.fr-2JANOMNB.js} +2 -2
  47. package/dist/dev/vidply.fr-2JANOMNB.js.map +7 -0
  48. package/dist/dev/{vidply.ja-S6ITA6WU.js → vidply.ja-WWMSTEJH.js} +2 -2
  49. package/dist/dev/vidply.ja-WWMSTEJH.js.map +7 -0
  50. package/dist/legacy/vidply.js +345 -32
  51. package/dist/legacy/vidply.js.map +3 -3
  52. package/dist/legacy/vidply.min.js +2 -2
  53. package/dist/legacy/vidply.min.meta.json +66 -47
  54. package/dist/prod/vidply.AudioDescriptionManager-YTDVDQBO.min.js +6 -0
  55. package/dist/prod/{vidply.CaptionStyleMenu-M3KVQWB5.min.js → vidply.CaptionStyleMenu-2AFF3HF2.min.js} +2 -2
  56. package/dist/prod/{vidply.DASHRenderer-DN3S27AV.min.js → vidply.DASHRenderer-URDOTOKS.min.js} +2 -2
  57. package/dist/prod/{vidply.FloatingPlayerManager-SKAZ4J43.min.js → vidply.FloatingPlayerManager-QUBFK7CQ.min.js} +2 -2
  58. package/dist/prod/{vidply.HLSRenderer-62HH4YTK.min.js → vidply.HLSRenderer-46KNUVAB.min.js} +2 -2
  59. package/dist/prod/vidply.HTML5Renderer-7TQDMIGY.min.js +6 -0
  60. package/dist/prod/{vidply.SignLanguageManager-V3OBTSSF.min.js → vidply.SignLanguageManager-H4C36EVL.min.js} +2 -2
  61. package/dist/prod/{vidply.SoundCloudRenderer-CXRVZIAJ.min.js → vidply.SoundCloudRenderer-OYSWMW7D.min.js} +2 -2
  62. package/dist/prod/{vidply.TranscriptManager-SYM3DFUY.min.js → vidply.TranscriptManager-BA3DAS63.min.js} +2 -2
  63. package/dist/prod/{vidply.VimeoRenderer-72TWRYFQ.min.js → vidply.VimeoRenderer-NF3COTHU.min.js} +2 -2
  64. package/dist/prod/{vidply.YouTubeRenderer-UMHEV3KD.min.js → vidply.YouTubeRenderer-UCQKYIJ4.min.js} +2 -2
  65. package/dist/prod/{vidply.chunk-Z3YDSA7O.min.js → vidply.chunk-2HNCP64N.min.js} +1 -1
  66. package/dist/prod/{vidply.chunk-EKEYJWMG.min.js → vidply.chunk-6ECBIYVI.min.js} +2 -2
  67. package/dist/prod/{vidply.chunk-YEENJS3Q.min.js → vidply.chunk-7S5AFTST.min.js} +2 -2
  68. package/dist/prod/{vidply.chunk-P4UXOHWA.min.js → vidply.chunk-EUOA2DJK.min.js} +1 -1
  69. package/dist/prod/{vidply.chunk-2TB6MUHO.min.js → vidply.chunk-G6KGXNEW.min.js} +1 -1
  70. package/dist/prod/{vidply.chunk-ABPXVDLZ.min.js → vidply.chunk-JPMAK3NS.min.js} +1 -1
  71. package/dist/prod/{vidply.chunk-P3PQBY7N.min.js → vidply.chunk-L4O44K63.min.js} +1 -1
  72. package/dist/prod/{vidply.chunk-WX3MVEAL.min.js → vidply.chunk-M76ABMKB.min.js} +1 -1
  73. package/dist/prod/{vidply.chunk-TM6AXJVD.min.js → vidply.chunk-WRVDVD33.min.js} +2 -2
  74. package/dist/prod/{vidply.chunk-RI3X7WHK.min.js → vidply.chunk-X2FQEQYR.min.js} +2 -2
  75. package/dist/prod/{vidply.chunk-UK7YGOB5.min.js → vidply.chunk-XXUH7ADU.min.js} +2 -2
  76. package/dist/prod/{vidply.de-BCHXRQ53.min.js → vidply.de-JMCATTKZ.min.js} +1 -1
  77. package/dist/prod/{vidply.es-PICAKMZD.min.js → vidply.es-YVG37V7Z.min.js} +1 -1
  78. package/dist/prod/vidply.esm.min.js +2 -2
  79. package/dist/prod/{vidply.fr-ZILI7CDR.min.js → vidply.fr-OGKGN2R4.min.js} +1 -1
  80. package/dist/prod/{vidply.ja-5WQ4OK4J.min.js → vidply.ja-Z6DS7QLS.min.js} +1 -1
  81. package/dist/vidply.css +5796 -5796
  82. package/dist/vidply.esm.min.meta.json +148 -135
  83. package/package.json +7 -7
  84. package/src/controls/CaptionManager.ts +598 -598
  85. package/src/controls/ControlBar.ts +4159 -4159
  86. package/src/controls/KeyboardManager.ts +383 -383
  87. package/src/controls/SettingsDialog.ts +488 -488
  88. package/src/controls/TranscriptManager.ts +2426 -2426
  89. package/src/core/AudioDescriptionManager.ts +863 -774
  90. package/src/core/DescriptionSpeechManager.ts +232 -0
  91. package/src/core/FloatingPlayerManager.ts +674 -674
  92. package/src/core/LazyInit.ts +80 -80
  93. package/src/core/MetadataAlertsManager.ts +514 -514
  94. package/src/core/Player.ts +2915 -2872
  95. package/src/core/PosterManager.ts +169 -169
  96. package/src/core/PseudoFullscreen.ts +179 -179
  97. package/src/core/ResponsiveManager.ts +231 -231
  98. package/src/core/ResumeManager.ts +236 -236
  99. package/src/core/SignLanguageManager.ts +1308 -1308
  100. package/src/core/ThemeManager.ts +157 -157
  101. package/src/features/PlaylistManager.ts +1838 -1827
  102. package/src/i18n/i18n.ts +223 -223
  103. package/src/i18n/languages/de.ts +217 -217
  104. package/src/i18n/languages/en.ts +218 -218
  105. package/src/i18n/languages/es.ts +214 -214
  106. package/src/i18n/languages/fr.ts +214 -214
  107. package/src/i18n/languages/ja.ts +214 -214
  108. package/src/icons/Icons.ts +167 -167
  109. package/src/index.ts +196 -193
  110. package/src/renderers/DASHRenderer.ts +1077 -1077
  111. package/src/renderers/HLSRenderer.ts +944 -944
  112. package/src/renderers/HTML5Renderer.ts +389 -389
  113. package/src/renderers/SoundCloudRenderer.ts +437 -437
  114. package/src/renderers/VimeoRenderer.ts +298 -298
  115. package/src/renderers/YouTubeRenderer.ts +290 -290
  116. package/src/styles/vidply.css +5796 -5796
  117. package/src/types/events.ts +124 -122
  118. package/src/types/globals.d.ts +313 -313
  119. package/src/types/options.ts +185 -182
  120. package/src/types/renderer.ts +58 -58
  121. package/src/utils/DOMUtils.ts +281 -281
  122. package/src/utils/DownloadInfo.ts +171 -171
  123. package/src/utils/DraggablePanel.ts +539 -539
  124. package/src/utils/DraggablePanelMenu.ts +195 -195
  125. package/src/utils/DraggableResizable.ts +997 -997
  126. package/src/utils/EventEmitter.ts +59 -59
  127. package/src/utils/FormUtils.ts +95 -95
  128. package/src/utils/MenuFactory.ts +483 -483
  129. package/src/utils/Sanitize.ts +70 -70
  130. package/src/utils/SettingsMenuFactory.ts +289 -289
  131. package/src/utils/StorageManager.ts +287 -287
  132. package/src/utils/TrackLabelUtils.ts +52 -52
  133. package/src/utils/UrlSafe.ts +69 -69
  134. package/src/utils/VideoFrameCapture.ts +114 -114
  135. package/src/utils/WindowComponents.ts +250 -250
  136. package/dist/dev/vidply.AudioDescriptionManager-R47Q4YR4.js.map +0 -7
  137. package/dist/dev/vidply.DASHRenderer-PWEMC3BT.js.map +0 -7
  138. package/dist/dev/vidply.FloatingPlayerManager-PKNNNAS2.js.map +0 -7
  139. package/dist/dev/vidply.HLSRenderer-E7RQ74HI.js.map +0 -7
  140. package/dist/dev/vidply.HTML5Renderer-BU4QJJJY.js +0 -12
  141. package/dist/dev/vidply.SignLanguageManager-FET5FQZ7.js.map +0 -7
  142. package/dist/dev/vidply.SoundCloudRenderer-RVSJZODO.js.map +0 -7
  143. package/dist/dev/vidply.TranscriptManager-5GXOQCLX.js.map +0 -7
  144. package/dist/dev/vidply.VimeoRenderer-ER2LSBBW.js.map +0 -7
  145. package/dist/dev/vidply.YouTubeRenderer-NFTOWJAB.js.map +0 -7
  146. package/dist/dev/vidply.chunk-64WXMTGL.js.map +0 -7
  147. package/dist/dev/vidply.chunk-BNFIUL2Z.js.map +0 -7
  148. package/dist/dev/vidply.chunk-JJ47NO24.js.map +0 -7
  149. package/dist/dev/vidply.chunk-KI7C3QVL.js.map +0 -7
  150. package/dist/dev/vidply.chunk-NQPURMQY.js.map +0 -7
  151. package/dist/dev/vidply.chunk-RZWZGDV7.js.map +0 -7
  152. package/dist/dev/vidply.chunk-W25HUCHS.js.map +0 -7
  153. package/dist/dev/vidply.de-4YAT7FFM.js.map +0 -7
  154. package/dist/dev/vidply.es-LEWIRUBH.js.map +0 -7
  155. package/dist/dev/vidply.fr-Q4GV2NOE.js.map +0 -7
  156. package/dist/dev/vidply.ja-S6ITA6WU.js.map +0 -7
  157. package/dist/prod/vidply.AudioDescriptionManager-RHLIJW5C.min.js +0 -6
  158. package/dist/prod/vidply.HTML5Renderer-IU5T3WEW.min.js +0 -6
  159. /package/dist/dev/{vidply.CaptionStyleMenu-7XHRBWUM.js.map → vidply.CaptionStyleMenu-R2SCQ6VP.js.map} +0 -0
  160. /package/dist/dev/{vidply.HTML5Renderer-BU4QJJJY.js.map → vidply.HTML5Renderer-P7ICKYU4.js.map} +0 -0
  161. /package/dist/dev/{vidply.chunk-6RQA4ROD.js.map → vidply.chunk-5IK3IC5V.js.map} +0 -0
  162. /package/dist/dev/{vidply.chunk-DZSDO3B7.js.map → vidply.chunk-HOYEMJSD.js.map} +0 -0
  163. /package/dist/dev/{vidply.chunk-VMSQAZM7.js.map → vidply.chunk-JAKZNGOR.js.map} +0 -0
  164. /package/dist/dev/{vidply.chunk-SK4V2DAU.js.map → vidply.chunk-YVCYQZJI.js.map} +0 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/utils/FormUtils.ts", "../../src/utils/MenuUtils.ts", "../../src/utils/DraggablePanelMenu.ts", "../../src/utils/DraggablePanel.ts"],
4
+ "sourcesContent": ["import { DOMUtils } from './DOMUtils.js';\nimport { i18n } from '../i18n/i18n.js';\n\nexport interface SelectOption {\n value: string;\n text: string;\n selected?: boolean;\n}\n\nexport interface LabeledSelectOptions {\n classPrefix: string;\n labelClass: string;\n selectClass: string;\n labelText: string;\n selectId: string;\n hidden?: boolean;\n onChange?: (e: Event) => void;\n options?: SelectOption[];\n}\n\nexport function createLabeledSelect({\n classPrefix: _classPrefix,\n labelClass,\n selectClass,\n labelText,\n selectId,\n hidden = false,\n onChange = undefined,\n options = []\n}: LabeledSelectOptions): { label: HTMLLabelElement; select: HTMLSelectElement } {\n const isI18nKey =\n typeof labelText === 'string' &&\n (labelText.startsWith('transcript.') ||\n labelText.startsWith('player.') ||\n labelText.startsWith('settings.') ||\n labelText.startsWith('captions.'));\n const labelTextContent = isI18nKey ? i18n.t(labelText) || labelText : labelText;\n\n const label = DOMUtils.createElement('label', {\n className: labelClass,\n textContent: labelTextContent,\n attributes: {\n for: selectId,\n style: hidden ? 'display: none;' : undefined\n }\n });\n\n const select = DOMUtils.createElement('select', {\n className: selectClass,\n attributes: {\n id: selectId,\n style: hidden ? 'display: none;' : undefined\n }\n });\n\n options.forEach(opt => {\n const option = DOMUtils.createElement('option', {\n textContent: opt.text,\n attributes: {\n value: opt.value,\n selected: opt.selected ? 'selected' : undefined\n }\n });\n select.appendChild(option);\n });\n\n if (onChange) {\n select.addEventListener('change', onChange);\n }\n\n return { label, select };\n}\n\nexport function toggleLabeledSelect(\n label: HTMLElement | null,\n select: HTMLElement | null,\n show: boolean\n): void {\n if (label) {\n label.style.display = show ? 'block' : 'none';\n }\n if (select) {\n select.style.display = show ? 'block' : 'none';\n }\n}\n\nexport function preventDragOnElement(element: HTMLElement | null): void {\n if (!element) return;\n\n (['pointerdown', 'mousedown', 'click'] as const).forEach(eventType => {\n element.addEventListener(eventType, (e: Event) => {\n e.stopPropagation();\n });\n });\n}\n", "import { DOMUtils } from './DOMUtils.js';\nimport { createIconElement } from '../icons/Icons.js';\nimport { i18n } from '../i18n/i18n.js';\nimport { focusElement } from './FocusUtils.js';\nimport { reducedMotionScrollOptions } from './PerformanceUtils.js';\n\nexport interface MenuItemOptions {\n classPrefix: string;\n itemClass: string;\n icon?: string;\n label: string;\n ariaLabel?: string;\n onClick?: (e: Event) => void;\n hasTextClass?: boolean;\n}\n\nexport function createMenuItem({\n classPrefix,\n itemClass,\n icon,\n label,\n ariaLabel,\n onClick,\n hasTextClass = false\n}: MenuItemOptions): HTMLElement {\n const isI18nKeyForAria =\n typeof label === 'string' &&\n (label.startsWith('transcript.') || label.startsWith('player.') || label.startsWith('settings.'));\n const ariaLabelText = ariaLabel || (isI18nKeyForAria ? i18n.t(label) || label : label);\n\n const button = DOMUtils.createElement('button', {\n className: itemClass,\n attributes: {\n type: 'button',\n 'aria-label': ariaLabelText,\n tabindex: '-1'\n }\n });\n\n if (icon) {\n button.appendChild(createIconElement(icon));\n }\n\n const isI18nKey =\n typeof label === 'string' &&\n (label.startsWith('transcript.') || label.startsWith('player.') || label.startsWith('settings.'));\n const textContent = isI18nKey ? i18n.t(label) || label : label;\n\n const text = DOMUtils.createElement('span', {\n textContent,\n className: hasTextClass ? `${classPrefix}-settings-text` : undefined,\n attributes: { 'aria-hidden': 'true' }\n });\n button.appendChild(text);\n\n if (onClick) {\n button.addEventListener('click', onClick);\n }\n\n return button;\n}\n\nexport function attachMenuKeyboardNavigation(\n menu: HTMLElement | null,\n button: HTMLElement | null,\n itemSelector: string,\n onClose?: () => void\n): ((e: KeyboardEvent) => void) | undefined {\n if (!menu) return undefined;\n\n // Only enabled items participate in roving navigation. aria-disabled\n // empty-state entries must never receive focus (WCAG 2.1.1, 4.1.2).\n const menuItems = Array.from(menu.querySelectorAll<HTMLElement>(itemSelector))\n .filter((item) => item.getAttribute('aria-disabled') !== 'true');\n if (menuItems.length === 0) return undefined;\n\n const handleKeyDown = (e: KeyboardEvent) => {\n const currentIndex = menuItems.indexOf(document.activeElement as HTMLElement);\n\n switch (e.key) {\n case 'ArrowDown': {\n e.preventDefault();\n e.stopPropagation();\n const nextIndex = (currentIndex + 1) % menuItems.length;\n menuItems.forEach((item, idx) => {\n item.setAttribute('tabindex', idx === nextIndex ? '0' : '-1');\n });\n const next = menuItems[nextIndex];\n if (next) {\n next.focus({ preventScroll: false });\n next.scrollIntoView(reducedMotionScrollOptions('nearest'));\n }\n break;\n }\n\n case 'ArrowUp': {\n e.preventDefault();\n e.stopPropagation();\n const prevIndex = (currentIndex - 1 + menuItems.length) % menuItems.length;\n menuItems.forEach((item, idx) => {\n item.setAttribute('tabindex', idx === prevIndex ? '0' : '-1');\n });\n const prev = menuItems[prevIndex];\n if (prev) {\n prev.focus({ preventScroll: false });\n prev.scrollIntoView(reducedMotionScrollOptions('nearest'));\n }\n break;\n }\n\n case 'Home': {\n e.preventDefault();\n e.stopPropagation();\n menuItems.forEach((item, idx) => {\n item.setAttribute('tabindex', idx === 0 ? '0' : '-1');\n });\n const firstItem = menuItems[0];\n if (firstItem) {\n firstItem.focus({ preventScroll: false });\n firstItem.scrollIntoView(reducedMotionScrollOptions('nearest'));\n }\n break;\n }\n\n case 'End': {\n e.preventDefault();\n e.stopPropagation();\n const lastIndex = menuItems.length - 1;\n menuItems.forEach((item, idx) => {\n item.setAttribute('tabindex', idx === lastIndex ? '0' : '-1');\n });\n const lastItem = menuItems[lastIndex];\n if (lastItem) {\n lastItem.focus({ preventScroll: false });\n lastItem.scrollIntoView(reducedMotionScrollOptions('nearest'));\n }\n break;\n }\n\n case 'Enter':\n case ' ': {\n e.preventDefault();\n e.stopPropagation();\n if (document.activeElement && menuItems.includes(document.activeElement as HTMLElement)) {\n (document.activeElement as HTMLElement).click();\n if (onClose) {\n setTimeout(() => {\n if (button && document.contains(button)) {\n button.focus();\n }\n }, 0);\n }\n }\n break;\n }\n\n case 'Escape':\n e.preventDefault();\n e.stopPropagation();\n if (onClose) {\n onClose();\n }\n break;\n }\n };\n\n menu.addEventListener('keydown', handleKeyDown as EventListener, true);\n return handleKeyDown;\n}\n\nexport function focusFirstMenuItem(\n menu: HTMLElement | null,\n itemSelector: string,\n delay = 0\n): void {\n if (!menu) return;\n\n setTimeout(() => {\n const menuItems = Array.from(menu.querySelectorAll<HTMLElement>(itemSelector))\n .filter((item) => item.getAttribute('aria-disabled') !== 'true');\n const firstItem = menuItems[0];\n if (firstItem) {\n menuItems.forEach((item, index) => {\n item.setAttribute('tabindex', index === 0 ? '0' : '-1');\n });\n focusElement(firstItem, { delay: 0 });\n firstItem.scrollIntoView(reducedMotionScrollOptions('nearest'));\n }\n }, delay);\n}\n", "/**\n * Shared helpers for the settings-menu items that toggle drag / resize\n * mode on a `DraggableResizable`-powered panel.\n *\n * Previously `SignLanguageManager` and `TranscriptManager` both carried\n * near-identical copies of:\n * - `updateDragOptionState` / `updateResizeOptionState`\n * (button `aria-checked` + label + i18n-driven text sync)\n * - `positionSettingsMenuImmediate`\n * (place the menu relative to the settings button, with above/below\n * flipping and viewport-edge clamping)\n *\n * This module owns those pieces so a future manager (or a refactor of\n * an existing one) doesn't have to re-derive the rules from scratch.\n *\n * NOTE: the full \"DraggablePanel\" abstraction that also owns the menu\n * *contents* (language selector, style dialog, pinned state, etc.) is\n * deliberately NOT attempted here — the per-manager specifics are\n * significant and an attempt to unify them would churn security-\n * sensitive code without meaningfully reducing LOC.\n */\n\n/** Visual state of a two-state (\"currently enabled / currently disabled\")\n * toggle menu item. */\nexport interface ToggleMenuItemState {\n /** Whether the underlying mode is currently active. */\n enabled: boolean;\n /** Label shown when the mode is active (i.e. the label invites the\n * user to disable it). */\n enabledText: string;\n /** Label shown when the mode is inactive. */\n disabledText: string;\n /** `aria-label` for screen readers when the mode is active. */\n enabledAria: string;\n /** `aria-label` for screen readers when the mode is inactive. */\n disabledAria: string;\n}\n\n/**\n * Sync a settings-menu toggle button's `aria-checked`, `aria-label`,\n * and visible text node with the current mode state. Both properties\n * are updated unconditionally so the element stays in sync even if a\n * previous render left them stale.\n *\n * The caller owns the DOM nodes; this helper only mutates known-safe\n * properties (no innerHTML), so a translated label with angle brackets\n * or ampersands is stored as text, not parsed as markup.\n */\nexport function updateToggleMenuItem(\n button: HTMLElement | null | undefined,\n textElement: Element | null | undefined,\n state: ToggleMenuItemState\n): void {\n if (!button) return;\n\n button.setAttribute('aria-checked', state.enabled ? 'true' : 'false');\n button.setAttribute('aria-label', state.enabled ? state.enabledAria : state.disabledAria);\n\n if (textElement) {\n // `textContent` exists on `Node`, so we can accept the broader\n // `Element` type here and let callers pass either an `HTMLElement`\n // (TranscriptManager keeps the tighter type for the sake of its\n // `.style` access elsewhere) or a plain `Element` returned by\n // `querySelector`.\n textElement.textContent = state.enabled ? state.enabledText : state.disabledText;\n }\n}\n\nexport interface PositionSettingsMenuOptions {\n /**\n * Horizontal anchoring:\n * - `'left'` : align the menu's left edge with the button's left edge\n * (the TranscriptManager convention — simple panels).\n * - `'center'` : centre the menu horizontally on the button, with\n * viewport-edge clamping (the SignLanguageManager\n * convention — small floating panel that can end up\n * near the viewport edges).\n */\n align?: 'left' | 'center';\n /** Vertical gap (px) between button and menu. */\n gap?: number;\n /** Extra breathing room (px) before we decide to flip above. */\n spaceReserve?: number;\n}\n\n/**\n * Position a settings menu relative to its button, using offsets that\n * are relative to the button's offset parent (so transforms on ancestor\n * elements don't throw the layout off).\n *\n * When there isn't enough room below the button but there is room\n * above, the menu flips upward and picks up a `vidply-menu-above` CSS\n * class so arrows / drop-shadows can be mirrored via stylesheet.\n */\nexport function positionSettingsMenu(\n menu: HTMLElement | null | undefined,\n button: HTMLElement | null | undefined,\n opts: PositionSettingsMenuOptions = {}\n): void {\n if (!menu || !button) return;\n const { align = 'left', gap = 4, spaceReserve = 20 } = opts;\n\n const parentContainer = button.parentElement;\n if (!parentContainer) return;\n\n const buttonRect = button.getBoundingClientRect();\n const parentRect = parentContainer.getBoundingClientRect();\n const menuRect = menu.getBoundingClientRect();\n const viewportHeight = window.innerHeight;\n const viewportWidth = window.innerWidth;\n\n const buttonBottom = buttonRect.bottom - parentRect.top;\n const buttonTop = buttonRect.top - parentRect.top;\n const buttonLeftOffset = buttonRect.left - parentRect.left;\n const buttonCenterX = buttonLeftOffset + buttonRect.width / 2;\n\n const spaceAbove = buttonRect.top;\n const spaceBelow = viewportHeight - buttonRect.bottom;\n\n // Vertical placement: default below, flip above when tight.\n let menuTop: number | null = buttonBottom + gap;\n let menuBottomPx: number | null = null;\n if (spaceBelow < menuRect.height + spaceReserve && spaceAbove > spaceBelow) {\n if (align === 'center') {\n // Measure from the bottom edge of the parent so the menu sits\n // directly above the button without being pushed off-screen.\n const parentHeight = parentRect.bottom - parentRect.top;\n menuBottomPx = parentHeight - buttonTop + gap;\n menuTop = null;\n } else {\n menuTop = buttonTop - menuRect.height - gap;\n }\n menu.classList.add('vidply-menu-above');\n } else {\n menu.classList.remove('vidply-menu-above');\n }\n\n // Horizontal placement: simple left-align vs viewport-clamped centre.\n let leftValue: string;\n let rightValue: string;\n let transform: string;\n\n if (align === 'center') {\n const menuLeftAbsolute = buttonRect.left + buttonRect.width / 2 - menuRect.width / 2;\n if (menuLeftAbsolute < 10) {\n leftValue = '0';\n rightValue = 'auto';\n transform = 'translateX(0)';\n } else if (menuLeftAbsolute + menuRect.width > viewportWidth - 10) {\n leftValue = 'auto';\n rightValue = '0';\n transform = 'translateX(0)';\n } else {\n leftValue = `${buttonCenterX}px`;\n rightValue = 'auto';\n transform = 'translateX(-50%)';\n }\n } else {\n leftValue = `${buttonLeftOffset}px`;\n rightValue = 'auto';\n transform = 'translateX(0)';\n }\n\n if (menuTop !== null) {\n menu.style.top = `${menuTop}px`;\n menu.style.bottom = 'auto';\n } else if (menuBottomPx !== null) {\n menu.style.top = 'auto';\n menu.style.bottom = `${menuBottomPx}px`;\n }\n\n menu.style.left = leftValue;\n menu.style.right = rightValue;\n menu.style.transform = transform;\n}\n\n/**\n * RAF-deferred wrapper around {@link positionSettingsMenu}. Some\n * callers need to re-position after layout has settled (e.g. when the\n * panel size just changed and `getBoundingClientRect` would still\n * return the pre-change measurements on this frame).\n */\nexport function positionSettingsMenuDeferred(\n menu: HTMLElement | null | undefined,\n button: HTMLElement | null | undefined,\n opts: PositionSettingsMenuOptions = {}\n): void {\n requestAnimationFrame(() => {\n // The extra setTimeout is intentional: older WebKit still reports\n // stale geometry during the first RAF callback following a DOM\n // mutation. A 10ms trailing microtask gives layout a chance to\n // settle without being user-perceptible.\n setTimeout(() => positionSettingsMenu(menu, button, opts), 10);\n });\n}\n", "/**\n * Owns the settings-menu lifecycle for a `DraggableResizable`-powered\n * floating panel: create, show, hide, outside-click dismissal, keyboard\n * navigation, viewport-aware positioning, and the drag-mode / resize-\n * mode toggle items.\n *\n * `SignLanguageManager` and `TranscriptManager` both present the same\n * pattern: a settings button that opens a menu containing a \"Keyboard\n * drag mode\" toggle, a \"Resize mode\" toggle, manager-specific extras,\n * and a close item. This class captures that pattern so each manager\n * only has to describe:\n * - how to resolve its `DraggableResizable` (for state readback),\n * - which i18n keys to use on the toggle items,\n * - where to place the menu in its own layout (via `getMenuParent`),\n * - how to toggle each mode (`onDragItemClick` / `onResizeItemClick`\n * run the manager's side effects — announcements, badges, focus),\n * - any extra menu items (`buildExtraItems`, e.g. style dialog,\n * timestamp toggle for TranscriptManager).\n *\n * The manager's own public API (`showSettingsMenu`, `toggleKeyboardDragMode`,\n * `hideSettingsMenu`, ...) is preserved by forwarding to this panel.\n * External code that reads `settingsMenuVisible` or the option-button\n * elements keeps working via getters on the manager that proxy the\n * panel's state.\n *\n * Event listeners attached to `document` are tied to the player's\n * `lifecycleSignal` so they are torn down automatically when the\n * player is destroyed.\n */\n\nimport { DOMUtils } from './DOMUtils.js';\nimport { i18n } from '../i18n/i18n.js';\nimport {\n attachMenuKeyboardNavigation,\n createMenuItem,\n focusFirstMenuItem,\n} from './MenuUtils.js';\nimport {\n positionSettingsMenu,\n positionSettingsMenuDeferred,\n updateToggleMenuItem,\n} from './DraggablePanelMenu.js';\n\nimport type { Player } from '../core/Player.js';\nimport type { DraggableResizable } from './DraggableResizable.js';\n\n/**\n * i18n keys used for the drag-mode and resize-mode toggle items and\n * the \"Close\" item. All values must be translation keys (passed\n * through `i18n.t()` by `createMenuItem`/`updateToggleMenuItem`).\n */\nexport interface DraggablePanelI18nKeys {\n enableDrag: string;\n disableDrag: string;\n enableDragAria: string;\n disableDragAria: string;\n enableResize: string;\n disableResize: string;\n enableResizeAria: string;\n disableResizeAria: string;\n closeMenu: string;\n}\n\nexport interface DraggablePanelExtrasContext {\n menu: HTMLElement;\n itemClass: string;\n classPrefix: string;\n /**\n * Strip the tooltip and duplicate `button-text` nodes from a menu\n * item. `createMenuItem` is shared with toolbar buttons that want a\n * hover tooltip; settings-menu rows already show the same text\n * inline, so the tooltip would cause screen readers to read the\n * label twice. Exposed here so manager-provided extras can reuse\n * the same stripping logic the panel applies to its own items.\n */\n stripInlineTooltip: (item: HTMLElement) => void;\n}\n\nexport interface DraggablePanelOptions {\n player: Player;\n /**\n * Namespace for CSS classes, e.g. `'sign-language'` or `'transcript'`.\n * The final menu class is `{classPrefix}-{namespace}-settings-menu`\n * and item class is `{classPrefix}-{namespace}-settings-item`.\n */\n namespace: string;\n /** The button that opens the menu. */\n settingsButton: HTMLElement;\n /** Lazy draggable lookup (it may be re-created by the manager). */\n getDraggable: () => DraggableResizable | null;\n i18nKeys: DraggablePanelI18nKeys;\n /**\n * Horizontal anchoring of the menu relative to the settings button.\n * - `'left'` : align left edge (Transcript — header-row button).\n * - `'center'` : centre horizontally with viewport clamping (Sign\n * language — floating near viewport edges).\n */\n menuAlign: 'left' | 'center';\n /** Where to attach the menu node when first created. */\n getMenuParent: () => HTMLElement | null;\n /**\n * Called when the drag-mode item is clicked. The manager is\n * expected to toggle drag mode and run any side effects\n * (announcements, badges, focus). After this runs the panel will\n * refresh menu item state.\n */\n onDragItemClick: (panel: DraggablePanel) => void;\n /** Same for the resize-mode item. */\n onResizeItemClick: (panel: DraggablePanel) => void;\n /**\n * Insert manager-specific items (style dialog, timestamps, ...)\n * between the resize item and the close item.\n */\n buildExtraItems?: (ctx: DraggablePanelExtrasContext) => void;\n /** Optional: vertical gap (px) between button and menu. */\n menuGap?: number;\n /** Optional: breathing room (px) before the menu flips above. */\n menuSpaceReserve?: number;\n /**\n * Where to attach the mode-feedback badge while keyboard drag /\n * pointer resize mode is active. If omitted, {@link DraggablePanel.showBadge}\n * is a no-op (the manager effectively opts out of the badge).\n */\n getBadgeHost?: () => HTMLElement | null;\n /**\n * CSS class for the badge element. Defaults to\n * `{classPrefix}-{namespace}-mode-badge`. Sign-language passes a\n * shorter class (`vidply-sign-mode-badge`) to preserve existing\n * CSS. Transcript uses the default.\n */\n badgeClass?: string;\n}\n\nexport class DraggablePanel {\n private readonly opts: DraggablePanelOptions;\n\n /** Populated lazily on first `show()`. */\n settingsMenu: HTMLElement | null = null;\n settingsMenuVisible = false;\n dragOptionButton: HTMLElement | null = null;\n dragOptionText: Element | null = null;\n resizeOptionButton: HTMLElement | null = null;\n resizeOptionText: Element | null = null;\n\n private _justOpened = false;\n private _justOpenedTimer: ReturnType<typeof setTimeout> | null = null;\n private _keyHandler: ((e: KeyboardEvent) => void) | null = null;\n private _documentClick: ((e: MouseEvent) => void) | null = null;\n private _documentClickAdded = false;\n private _modeBadge: HTMLElement | null = null;\n\n constructor(opts: DraggablePanelOptions) {\n this.opts = opts;\n }\n\n /** True while the just-opened debounce window (prevents the same\n * click that opened the menu from also closing it via document\n * `mousedown` / `click`). */\n get justOpened(): boolean {\n return this._justOpened;\n }\n\n get classPrefix(): string {\n return this.opts.player.options.classPrefix;\n }\n\n get menuClass(): string {\n return `${this.classPrefix}-${this.opts.namespace}-settings-menu`;\n }\n\n get itemClass(): string {\n return `${this.classPrefix}-${this.opts.namespace}-settings-item`;\n }\n\n /**\n * Show the menu. First call creates the DOM; subsequent calls reuse\n * it. Refreshes menu item state from the current draggable.\n */\n show(): void {\n this._markJustOpened(350);\n this._ensureDocumentClickHandler();\n\n if (this.settingsMenu) {\n this.settingsMenu.style.display = 'block';\n this.settingsMenuVisible = true;\n this.opts.settingsButton.setAttribute('aria-expanded', 'true');\n this._attachKeyboardNavigation();\n this._positionImmediate();\n this.refreshState();\n focusFirstMenuItem(this.settingsMenu, `.${this.itemClass}`);\n return;\n }\n\n this._createMenu();\n }\n\n /**\n * Hide the menu. By default returns focus to the settings button;\n * callers can opt out when the next interaction should land\n * elsewhere (e.g. on the wrapper after enabling drag mode).\n */\n hide({ focusButton = true }: { focusButton?: boolean } = {}): void {\n if (!this.settingsMenu) return;\n\n this.settingsMenu.style.display = 'none';\n this.settingsMenuVisible = false;\n this._clearJustOpened();\n\n if (this._keyHandler) {\n this.settingsMenu.removeEventListener('keydown', this._keyHandler, true);\n this._keyHandler = null;\n }\n\n const items = this.settingsMenu.querySelectorAll<HTMLElement>(`.${this.itemClass}`);\n items.forEach((item) => item.setAttribute('tabindex', '-1'));\n\n const { settingsButton } = this.opts;\n settingsButton.setAttribute('aria-expanded', 'false');\n if (focusButton) {\n settingsButton.focus({ preventScroll: true });\n }\n }\n\n toggle(): void {\n if (this.settingsMenuVisible) {\n this.hide();\n } else {\n this.show();\n }\n }\n\n /** Set a short \"just opened\" guard so the document-click handler\n * attached for outside-dismissal ignores the originating click. */\n markJustOpenedForClick(): void {\n if (this._documentClick) {\n this._markJustOpened(100);\n }\n }\n\n /** Refresh the drag and resize toggle item state from the draggable. */\n refreshState(): void {\n this.refreshDragState();\n this.refreshResizeState();\n }\n\n refreshDragState(): void {\n const draggable = this.opts.getDraggable();\n updateToggleMenuItem(this.dragOptionButton, this.dragOptionText, {\n enabled: Boolean(draggable?.keyboardDragMode),\n enabledText: i18n.t(this.opts.i18nKeys.disableDrag),\n disabledText: i18n.t(this.opts.i18nKeys.enableDrag),\n enabledAria: i18n.t(this.opts.i18nKeys.disableDragAria),\n disabledAria: i18n.t(this.opts.i18nKeys.enableDragAria),\n });\n }\n\n refreshResizeState(): void {\n const draggable = this.opts.getDraggable();\n updateToggleMenuItem(this.resizeOptionButton, this.resizeOptionText, {\n enabled: Boolean(draggable?.pointerResizeMode),\n enabledText: i18n.t(this.opts.i18nKeys.disableResize),\n disabledText: i18n.t(this.opts.i18nKeys.enableResize),\n enabledAria: i18n.t(this.opts.i18nKeys.disableResizeAria),\n disabledAria: i18n.t(this.opts.i18nKeys.enableResizeAria),\n });\n }\n\n /**\n * Show a persistent mode-feedback badge (e.g. \"Drag mode: arrow\n * keys to move, Esc to exit\") anchored to the host element\n * returned by `getBadgeHost`. Replaces any previous badge. The\n * badge is a real DOM element (not a CSS pseudo-element) so its\n * text is translatable, selectable, visible under high-contrast\n * themes, and reflected in browser translation overlays.\n *\n * Marked `aria-hidden` because the accompanying live-region\n * announcement (the manager's responsibility) already conveys the\n * state change to assistive tech.\n */\n showBadge(text: string): void {\n const host = this.opts.getBadgeHost?.();\n if (!host) return;\n this.hideBadge();\n\n const className =\n this.opts.badgeClass ??\n `${this.classPrefix}-${this.opts.namespace}-mode-badge`;\n const badge = DOMUtils.createElement('span', {\n className,\n textContent: text,\n attributes: { 'aria-hidden': 'true' },\n });\n host.appendChild(badge);\n this._modeBadge = badge;\n }\n\n /** Remove the mode-feedback badge if one is showing. */\n hideBadge(): void {\n if (this._modeBadge && this._modeBadge.parentNode) {\n this._modeBadge.remove();\n }\n this._modeBadge = null;\n }\n\n /** RAF-deferred reposition (e.g. after a panel resize). */\n reposition(): void {\n positionSettingsMenuDeferred(this.settingsMenu, this.opts.settingsButton, {\n align: this.opts.menuAlign,\n gap: this.opts.menuGap ?? 4,\n spaceReserve: this.opts.menuSpaceReserve ?? 20,\n });\n }\n\n /**\n * Tear down any DOM/listeners owned by this panel. Safe to call\n * multiple times. Callers must still drop their own references.\n */\n destroy(): void {\n if (this._justOpenedTimer) {\n clearTimeout(this._justOpenedTimer);\n this._justOpenedTimer = null;\n }\n this._justOpened = false;\n\n this.hideBadge();\n\n if (this.settingsMenu) {\n if (this._keyHandler) {\n this.settingsMenu.removeEventListener('keydown', this._keyHandler, true);\n }\n this.settingsMenu.remove();\n this.settingsMenu = null;\n }\n\n this._keyHandler = null;\n this.settingsMenuVisible = false;\n this.dragOptionButton = null;\n this.dragOptionText = null;\n this.resizeOptionButton = null;\n this.resizeOptionText = null;\n\n // The document-click listener was attached with the player's\n // lifecycleSignal, so it is cleaned up automatically. We clear\n // the local reference so a later show() re-installs.\n this._documentClick = null;\n this._documentClickAdded = false;\n }\n\n private _createMenu(): void {\n const { player, settingsButton, i18nKeys } = this.opts;\n\n const menu = DOMUtils.createElement('div', {\n className: this.menuClass,\n attributes: { role: 'menu' },\n });\n this.settingsMenu = menu;\n\n const dragOption = createMenuItem({\n classPrefix: this.classPrefix,\n itemClass: this.itemClass,\n icon: 'move',\n label: i18nKeys.enableDrag,\n hasTextClass: true,\n onClick: (event) => {\n event.preventDefault();\n event.stopPropagation();\n this.opts.onDragItemClick(this);\n this.refreshState();\n },\n });\n dragOption.setAttribute('role', 'switch');\n dragOption.setAttribute('aria-checked', 'false');\n dragOption.setAttribute('data-setting', 'keyboard-drag');\n this._stripInlineTooltip(dragOption);\n this.dragOptionButton = dragOption;\n this.dragOptionText = dragOption.querySelector(`.${this.classPrefix}-settings-text`);\n\n const resizeOption = createMenuItem({\n classPrefix: this.classPrefix,\n itemClass: this.itemClass,\n icon: 'resize',\n label: i18nKeys.enableResize,\n hasTextClass: true,\n onClick: (event) => {\n event.preventDefault();\n event.stopPropagation();\n this.opts.onResizeItemClick(this);\n this.refreshState();\n },\n });\n resizeOption.setAttribute('role', 'switch');\n resizeOption.setAttribute('aria-checked', 'false');\n this._stripInlineTooltip(resizeOption);\n this.resizeOptionButton = resizeOption;\n this.resizeOptionText = resizeOption.querySelector(`.${this.classPrefix}-settings-text`);\n\n menu.appendChild(dragOption);\n menu.appendChild(resizeOption);\n\n if (this.opts.buildExtraItems) {\n this.opts.buildExtraItems({\n menu,\n itemClass: this.itemClass,\n classPrefix: this.classPrefix,\n stripInlineTooltip: (item) => this._stripInlineTooltip(item),\n });\n }\n\n const closeOption = createMenuItem({\n classPrefix: this.classPrefix,\n itemClass: this.itemClass,\n icon: 'close',\n label: i18nKeys.closeMenu,\n onClick: () => this.hide(),\n });\n this._stripInlineTooltip(closeOption);\n menu.appendChild(closeOption);\n\n menu.style.visibility = 'hidden';\n menu.style.display = 'block';\n\n const parent = this.opts.getMenuParent();\n if (settingsButton.parentNode) {\n settingsButton.insertAdjacentElement('afterend', menu);\n } else if (parent) {\n parent.appendChild(menu);\n }\n\n this._positionImmediate();\n\n requestAnimationFrame(() => {\n if (this.settingsMenu) {\n this.settingsMenu.style.visibility = 'visible';\n }\n });\n\n this._attachKeyboardNavigation();\n\n this.settingsMenuVisible = true;\n settingsButton.setAttribute('aria-expanded', 'true');\n this.refreshState();\n\n focusFirstMenuItem(menu, `.${this.itemClass}`);\n\n // Don't reveal the fact that we created the menu to callers that\n // didn't trigger show() explicitly — but this is always driven\n // by a show() so the `player` reference is just for\n // documentation clarity here.\n void player;\n }\n\n private _attachKeyboardNavigation(): void {\n const menu = this.settingsMenu;\n if (!menu) return;\n if (this._keyHandler) {\n menu.removeEventListener('keydown', this._keyHandler, true);\n }\n const handler = attachMenuKeyboardNavigation(\n menu,\n this.opts.settingsButton,\n `.${this.itemClass}`,\n () => this.hide({ focusButton: true })\n );\n this._keyHandler = handler ?? null;\n }\n\n private _positionImmediate(): void {\n positionSettingsMenu(this.settingsMenu, this.opts.settingsButton, {\n align: this.opts.menuAlign,\n gap: this.opts.menuGap ?? 4,\n spaceReserve: this.opts.menuSpaceReserve ?? 20,\n });\n }\n\n /**\n * Remove tooltip and duplicate button-text nodes from a menu item.\n * `createMenuItem` is used both for toolbar buttons (which want a\n * tooltip) and for settings-menu rows (which show the same text\n * inline). This strips the duplicated pieces so screen readers\n * don't read the label twice.\n */\n private _stripInlineTooltip(item: HTMLElement): void {\n const tooltip = item.querySelector(`.${this.classPrefix}-tooltip`);\n if (tooltip) tooltip.remove();\n const buttonText = item.querySelector(`.${this.classPrefix}-button-text`);\n if (buttonText) buttonText.remove();\n }\n\n private _markJustOpened(durationMs: number): void {\n this._justOpened = true;\n if (this._justOpenedTimer) {\n clearTimeout(this._justOpenedTimer);\n }\n this._justOpenedTimer = setTimeout(() => {\n this._justOpened = false;\n this._justOpenedTimer = null;\n }, durationMs);\n }\n\n private _clearJustOpened(): void {\n this._justOpened = false;\n if (this._justOpenedTimer) {\n clearTimeout(this._justOpenedTimer);\n this._justOpenedTimer = null;\n }\n }\n\n private _ensureDocumentClickHandler(): void {\n if (this._documentClickAdded) return;\n\n this._documentClick = (event: MouseEvent) => {\n if (this._justOpened) return;\n const target = event.target as Node | null;\n const { settingsButton } = this.opts;\n\n if (settingsButton === target || (target && settingsButton.contains(target))) {\n return;\n }\n if (this.settingsMenu && target && this.settingsMenu.contains(target)) {\n return;\n }\n if (this.settingsMenuVisible) {\n this.hide();\n }\n };\n\n // Delay attach so the click that opened the menu doesn't instantly\n // close it even if the justOpened flag somehow races.\n setTimeout(() => {\n const handler = this._documentClick;\n if (!handler) return;\n document.addEventListener('mousedown', handler, {\n capture: true,\n signal: this.opts.player.lifecycleSignal,\n });\n this._documentClickAdded = true;\n }, 300);\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * VidPly v1.2.0 - Universal, Accessible Video Player
2
+ * VidPly v1.2.1 - Universal, Accessible Video Player
3
3
  * (c) 2026 Matthias Peltzer
4
4
  * Released under GPL-2.0-or-later License
5
5
  */
@@ -297,4 +297,4 @@ var HTML5Renderer = class {
297
297
  export {
298
298
  HTML5Renderer
299
299
  };
300
- //# sourceMappingURL=vidply.chunk-W25HUCHS.js.map
300
+ //# sourceMappingURL=vidply.chunk-XAK3DPTO.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/renderers/HTML5Renderer.ts"],
4
+ "sourcesContent": ["import type { Renderer } from '../types/renderer.js';\nimport type { Player } from '../core/Player.js';\n\nexport class HTML5Renderer implements Renderer {\n player: Player;\n media: HTMLMediaElement;\n _didDeferredLoad: boolean;\n // All media listeners are registered with this controller's signal so a\n // single abort() in destroy() detaches every one of them. Avoids the leak\n // of removeEventListener being called with fresh, non-matching callbacks.\n private _listenerController: AbortController;\n\n constructor(player: Player) {\n this.player = player;\n this.media = player.element;\n this._didDeferredLoad = false;\n this._listenerController = new AbortController();\n }\n\n async init() {\n // Hide native controls\n this.media.controls = false;\n this.media.removeAttribute('controls');\n \n this.attachEvents();\n \n // Set preload + optionally defer network loading until user play\n if (this.player.options.deferLoad) {\n // Allow metadata preload while still avoiding full media download.\n // Note: browsers may still fetch metadata automatically when preload=\"metadata\".\n this.media.preload = this.player.options.preload || 'none';\n \n // Firefox requires an explicit load() call to fetch metadata even with preload=\"metadata\".\n // Only call load() if preload is set to \"metadata\" to ensure duration is available.\n if (this.player.options.preload === 'metadata') {\n this.media.load();\n }\n } else {\n this.media.preload = this.player.options.preload;\n // Load media (eager)\n this.media.load();\n }\n \n // Show VidPly controls (remove external controls class if present)\n if (this.player.container) {\n this.player.container.classList.remove('vidply-external-controls');\n }\n }\n\n attachEvents() {\n const { signal } = this._listenerController;\n\n // Playback events\n this.media.addEventListener('loadedmetadata', () => {\n this.player.state.duration = this.media.duration;\n this.player.emit('loadedmetadata');\n \n // Auto-generate poster if none exists (for HTML5 video only)\n if (this.media.tagName === 'VIDEO') {\n this.player.autoGeneratePoster().catch((error: unknown) => {\n this.player.log('Failed to auto-generate poster:', error, 'warn');\n });\n }\n }, { signal });\n\n this.media.addEventListener('durationchange', () => {\n const duration = this.media.duration;\n if (duration && isFinite(duration) && duration > 0) {\n this.player.state.duration = duration;\n this.player.emit('durationchange', duration);\n }\n }, { signal });\n\n this.media.addEventListener('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 // Pause other players if enabled\n if (this.player.options.pauseOthersOnPlay) {\n this.pauseOtherPlayers();\n }\n }, { signal });\n\n this.media.addEventListener('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 }, { signal });\n\n this.media.addEventListener('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 // Handle loop\n if (this.player.options.loop) {\n this.player.seek(0);\n this.player.play();\n }\n }, { signal });\n\n this.media.addEventListener('timeupdate', () => {\n this.player.state.currentTime = this.media.currentTime;\n this.player.emit('timeupdate', this.media.currentTime);\n \n if (this.player.options.onTimeUpdate) {\n this.player.options.onTimeUpdate.call(this.player, this.media.currentTime);\n }\n }, { signal });\n\n this.media.addEventListener('volumechange', () => {\n this.player.state.volume = this.media.volume;\n this.player.state.muted = this.media.muted;\n this.player.emit('volumechange', this.media.volume);\n \n if (this.player.options.onVolumeChange) {\n this.player.options.onVolumeChange.call(this.player, this.media.volume);\n }\n }, { signal });\n\n this.media.addEventListener('seeking', () => {\n this.player.state.seeking = true;\n this.player.emit('seeking');\n }, { signal });\n\n this.media.addEventListener('seeked', () => {\n this.player.state.seeking = false;\n this.player.emit('seeked');\n }, { signal });\n\n this.media.addEventListener('waiting', () => {\n this.player.state.buffering = true;\n this.player.emit('waiting');\n }, { signal });\n\n this.media.addEventListener('canplay', () => {\n this.player.state.buffering = false;\n this.player.emit('canplay');\n }, { signal });\n\n this.media.addEventListener('progress', () => {\n if (this.media.buffered.length > 0) {\n const buffered = this.media.buffered.end(this.media.buffered.length - 1);\n this.player.emit('progress', buffered);\n }\n }, { signal });\n\n this.media.addEventListener('error', (_e: Event) => {\n this.player.handleError(this.media.error);\n }, { signal });\n\n this.media.addEventListener('ratechange', () => {\n this.player.state.playbackSpeed = this.media.playbackRate;\n this.player.emit('ratechange', this.media.playbackRate);\n }, { signal });\n }\n\n pauseOtherPlayers() {\n // Pause other VidPly instances\n const allPlayers = document.querySelectorAll('.vidply-player');\n allPlayers.forEach(playerEl => {\n if (playerEl !== this.player.container) {\n const video = playerEl.querySelector('video, audio') as HTMLMediaElement | null;\n if (video && !video.paused) {\n video.pause();\n }\n }\n });\n }\n\n play() {\n // Save scroll position to prevent browser from scrolling to video\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n\n // If deferLoad is enabled, trigger load only on the first user play request.\n if (this.player.options.deferLoad && !this._didDeferredLoad) {\n try {\n // Only call load() if browser hasn't loaded anything yet.\n if (this.media.readyState === 0) {\n this.media.load();\n }\n } catch {\n // ignore\n }\n this._didDeferredLoad = true;\n }\n \n const promise = this.media.play();\n \n // Restore scroll position immediately to prevent auto-scroll\n window.scrollTo(scrollX, scrollY);\n \n if (promise !== undefined) {\n promise.catch(error => {\n this.player.log('Play failed:', error, 'warn');\n \n // If autoplay failed, try muted autoplay\n if (this.player.options.autoplay && !this.player.state.muted) {\n this.player.log('Retrying play with muted audio', 'info');\n this.media.muted = true;\n \n // Save scroll position again for retry\n const retryScrollX = window.scrollX;\n const retryScrollY = window.scrollY;\n this.media.play().then(() => {\n window.scrollTo(retryScrollX, retryScrollY);\n }).catch(err => {\n this.player.handleError(err);\n });\n }\n });\n return promise;\n }\n return Promise.resolve();\n }\n\n /**\n * Ensure the media element has been loaded at least once (metadata/initial state)\n * without starting playback. Useful for playlists to behave like single videos.\n */\n ensureLoaded() {\n if (!this.player.options.deferLoad || this._didDeferredLoad) {\n return;\n }\n\n try {\n if (this.media.readyState === 0) {\n this.media.load();\n }\n } catch {\n // ignore\n }\n this._didDeferredLoad = true;\n }\n\n pause() {\n this.media.pause();\n }\n\n seek(time: number) {\n this.media.currentTime = time;\n }\n\n setVolume(volume: number) {\n this.media.volume = volume;\n }\n\n setMuted(muted: boolean) {\n this.media.muted = muted;\n }\n\n setPlaybackSpeed(speed: number) {\n this.media.playbackRate = speed;\n }\n\n /**\n * Get available quality levels from source elements\n * @returns {Array} Array of quality objects with index, height, width, and src\n */\n getQualities() {\n const sources = Array.from(this.media.querySelectorAll('source'));\n \n if (sources.length <= 1) {\n return [];\n }\n\n return sources.map((source, index) => {\n // Extract quality from data-quality attribute (preferred, HTML5-valid)\n // Fallback to deprecated 'label' attribute for backward compatibility\n // Note: 'label' is not a valid HTML attribute on <source> elements\n const label = source.getAttribute('data-quality') || source.getAttribute('data-label') || source.getAttribute('label') || '';\n const height = source.getAttribute('data-height') || String(this.extractHeightFromLabel(label));\n const width = source.getAttribute('data-width') || '';\n \n return {\n index,\n height: height ? parseInt(height) : 0,\n width: width ? parseInt(width) : 0,\n src: source.src,\n type: source.type,\n name: label || (height ? `${height}p` : `Quality ${index + 1}`)\n };\n }).filter(q => q.height > 0); // Only return qualities with valid height\n }\n\n /**\n * Extract height from quality label (e.g., \"1080p\" -> 1080)\n * @param {string} label \n * @returns {number}\n */\n extractHeightFromLabel(label: string) {\n const match = label.match(/(\\d+)p/i);\n return match && match[1] ? parseInt(match[1], 10) : 0;\n }\n\n /**\n * Switch to a specific quality level\n * @param {number} qualityIndex - Index of the quality level (-1 for auto, not applicable for HTML5)\n */\n switchQuality(qualityIndex: number) {\n const qualities = this.getQualities();\n \n if (qualityIndex < 0 || qualityIndex >= qualities.length) {\n this.player.log('Invalid quality index', 'warn');\n return;\n }\n\n const quality = qualities[qualityIndex];\n if (!quality || !quality.src) {\n return;\n }\n const currentTime = this.media.currentTime;\n const wasPlaying = !this.media.paused;\n\n // Store the current source for comparison\n const currentSrc = this.media.currentSrc;\n \n // Don't switch if already at this quality\n if (currentSrc === quality.src) {\n this.player.log('Already at this quality level', 'info');\n return;\n }\n\n this.player.log(`Switching to quality: ${quality.name}`, 'info');\n\n // Update the src\n this.media.src = quality.src;\n \n // Wait for the new source to load, then restore playback state\n const onLoadedMetadata = () => {\n this.media.removeEventListener('loadedmetadata', onLoadedMetadata);\n \n // Restore playback position\n this.media.currentTime = currentTime;\n \n // Resume playback if it was playing\n if (wasPlaying) {\n this.media.play().catch(() => {\n this.player.log('Failed to resume playback after quality switch', 'warn');\n });\n }\n \n // Emit quality change event\n this.player.emit('qualitychange', { quality: quality.name, index: qualityIndex });\n };\n\n this.media.addEventListener('loadedmetadata', onLoadedMetadata);\n this.media.load();\n }\n\n /**\n * Get current quality index\n * @returns {number}\n */\n getCurrentQuality() {\n const qualities = this.getQualities();\n const currentSrc = this.media.currentSrc;\n \n for (let i = 0; i < qualities.length; i++) {\n if (qualities[i]?.src === currentSrc) {\n return i;\n }\n }\n \n return 0; // Default to first quality if not found\n }\n\n destroy() {\n // Detach every media listener registered in attachEvents() in one shot.\n this._listenerController.abort();\n }\n}\n\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -1,11 +1,11 @@
1
1
  /*!
2
- * VidPly v1.2.0 - Universal, Accessible Video Player
2
+ * VidPly v1.2.1 - 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
- } from "./vidply.chunk-RZWZGDV7.js";
8
+ } from "./vidply.chunk-NSMAZCMB.js";
9
9
 
10
10
  // src/utils/TimeUtils.ts
11
11
  var TimeUtils = {
@@ -64,4 +64,4 @@ var TimeUtils = {
64
64
  export {
65
65
  TimeUtils
66
66
  };
67
- //# sourceMappingURL=vidply.chunk-SK4V2DAU.js.map
67
+ //# sourceMappingURL=vidply.chunk-YVCYQZJI.js.map
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * VidPly v1.2.0 - Universal, Accessible Video Player
2
+ * VidPly v1.2.1 - Universal, Accessible Video Player
3
3
  * (c) 2026 Matthias Peltzer
4
4
  * Released under GPL-2.0-or-later License
5
5
  */
@@ -220,4 +220,4 @@ var de = {
220
220
  export {
221
221
  de
222
222
  };
223
- //# sourceMappingURL=vidply.de-4YAT7FFM.js.map
223
+ //# sourceMappingURL=vidply.de-5W5LBFSJ.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/i18n/languages/de.ts"],
4
+ "sourcesContent": ["/**\n * German translations for VidPly\n */\n\nexport const de = {\n player: {\n label: 'Videoplayer',\n play: 'Abspielen',\n pause: 'Pause',\n stop: 'Stopp',\n restart: 'Von vorne beginnen',\n rewind: 'Zurückspulen',\n forward: 'Vorspulen',\n rewindSeconds: '{seconds} Sekunden zurückspulen',\n forwardSeconds: '{seconds} Sekunden vorspulen',\n previous: 'Vorheriger Titel',\n next: 'Nächster Titel',\n playlist: 'Wiedergabeliste umschalten',\n volume: 'Lautstärke',\n mute: 'Stumm',\n unmute: 'Ton ein',\n fullscreen: 'Vollbild',\n exitFullscreen: 'Vollbild beenden',\n captions: 'Untertitel',\n chapters: 'Kapitel',\n quality: 'Qualität',\n captionStyling: 'Untertitel-Stil',\n transcript: 'Transkript umschalten',\n audioDescription: 'Audiodeskription',\n signLanguage: 'Gebärdensprache-Video',\n settings: 'Einstellungen',\n speed: 'Wiedergabegeschwindigkeit',\n pip: 'Bild-in-Bild',\n floatingPlayer: 'Schwebender Player',\n floatingPlayerClose: 'Schwebenden Player schlie\\u00dfen',\n floatingPlayerEnter: 'Video ausklappen',\n floatingPlayerExit: 'Video andocken',\n floatingPlayerDialog: 'Schwebender Videoplayer. D zum Verschieben, R zum Gr\\u00f6\\u00dfen\\u00e4ndern, Escape zum Schlie\\u00dfen.',\n download: 'Herunterladen',\n downloadWithFormat: 'Herunterladen {format}',\n downloadWithSize: 'Herunterladen ({size})',\n downloadWithFormatSize: 'Herunterladen {format} ({size})',\n currentTime: 'Aktuelle Zeit',\n duration: 'Dauer',\n progress: 'Fortschritt',\n seekForward: '{seconds} Sekunden vorspulen',\n seekBackward: '{seconds} Sekunden zurückspulen',\n volumeUp: 'Lauter',\n volumeDown: 'Leiser',\n loading: 'Lädt...',\n loadingChapters: 'Kapitel werden geladen...',\n error: 'Fehler beim Laden',\n buffering: 'Puffern...',\n signLanguageVideo: 'Gebärdensprache-Video',\n closeSignLanguage: 'Gebärdensprache-Video schließen',\n signLanguageSettings: 'Gebärdensprache-Einstellungen',\n startPlaybackFirst: 'Bitte starten Sie die Wiedergabe zuerst.',\n startPlaybackForAudioDescription: 'Bitte starten Sie die Wiedergabe zuerst, um die Audiodeskription zu nutzen.',\n startPlaybackForSignLanguage: 'Bitte starten Sie die Wiedergabe zuerst, um das Gebärdensprache-Video zu nutzen.',\n noChapters: 'Keine Kapitel verfügbar',\n noCaptions: 'Keine Untertitel verfügbar',\n auto: 'Automatisch',\n autoQuality: 'Automatisch (keine Qualitätsauswahl verfügbar)',\n noQuality: 'Qualitätsauswahl nicht verfügbar',\n signLanguageDragResize: 'Gebärdensprache-Video - Drücken Sie D zum Verschieben per Tastatur, R zum Ändern der Größe',\n signLanguageDragActive: 'Gebärdensprache-Video - Verschiebemodus aktiv. Pfeiltasten zum Bewegen, Escape zum Beenden.',\n signLanguageResizeActive: 'Gebärdensprache-Video - Größenänderungsmodus aktiv. Links-/Rechts-Pfeiltasten zum Ändern der Größe, Escape zum Beenden.',\n enableSignDragMode: 'Verschiebemodus aktivieren. Tastenkombination: D-Taste',\n disableSignDragMode: 'Verschiebemodus deaktivieren. Tastenkombination: D-Taste',\n enableSignDragModeAria: 'Tastatur-Verschiebemodus mit Pfeiltasten aktivieren. Tastenkombination: D-Taste',\n disableSignDragModeAria: 'Tastatur-Verschiebemodus mit Pfeiltasten deaktivieren. Tastenkombination: D-Taste',\n enableSignResizeMode: 'Größenänderungsmodus aktivieren. Tastenkombination: R-Taste',\n disableSignResizeMode: 'Größenänderungsmodus deaktivieren. Tastenkombination: R-Taste',\n enableSignResizeModeAria: 'Tastatur-Größenänderungsmodus mit Pfeiltasten aktivieren. Tastenkombination: R-Taste',\n disableSignResizeModeAria: 'Tastatur-Größenänderungsmodus mit Pfeiltasten deaktivieren. Tastenkombination: R-Taste',\n signDragModeHint: 'Verschiebemodus: Pfeiltasten zum Bewegen, Esc zum Beenden',\n signResizeModeHint: 'Größenänderungsmodus: Pfeiltasten zum Ändern, Esc zum Beenden',\n signDragModeDisabled: 'Verschiebemodus für Gebärdensprache-Video deaktiviert.',\n signResizeModeDisabled: 'Größenänderungsmodus für Gebärdensprache-Video deaktiviert.',\n resizeHandle: 'Größenänderung {direction}-Ecke',\n moreOptions: 'Weitere Optionen',\n noMoreOptions: 'Keine weiteren Optionen verfügbar',\n playing: 'Wird abgespielt',\n paused: 'Pausiert',\n muted: 'Stummgeschaltet',\n unmuted: 'Ton an',\n captionsOn: 'Untertitel an',\n captionsOff: 'Untertitel aus',\n volumePercent: 'Lautstärke {percent} Prozent',\n volumePercentMuted: 'Stummgeschaltet',\n speedRate: 'Geschwindigkeit {rate}x'\n },\n captions: {\n off: 'Aus',\n select: 'Untertitel auswählen',\n fontSize: 'Schriftgröße',\n fontFamily: 'Schriftart',\n color: 'Textfarbe',\n backgroundColor: 'Hintergrundfarbe',\n opacity: 'Deckkraft'\n },\n fontSizes: {\n small: 'Klein',\n normal: 'Normal',\n large: 'Groß',\n xlarge: 'Sehr groß'\n },\n fontFamilies: {\n sansSerif: 'Sans-serif',\n serif: 'Serif',\n monospace: 'Monospace'\n },\n styleLabels: {\n textColor: 'Textfarbe',\n background: 'Hintergrund',\n font: 'Schrift',\n fontSize: 'Schriftgröße',\n opacity: 'Deckkraft'\n },\n audioDescription: {\n enable: 'Audiodeskription aktivieren',\n disable: 'Audiodeskription deaktivieren'\n },\n signLanguage: {\n show: 'Gebärdensprache-Video anzeigen',\n hide: 'Gebärdensprache-Video ausblenden',\n showInMainView: 'Gebärdensprache im Hauptvideo anzeigen',\n hideInMainView: 'Gebärdensprache aus Hauptvideo entfernen'\n },\n transcript: {\n title: 'Transkript',\n ariaLabel: 'Video-Transkript',\n close: 'Transkript schließen',\n loading: 'Transkript wird geladen...',\n noTranscript: 'Kein Transkript für dieses Video verfügbar.',\n settings: 'Transkript-Einstellungen. Eingabetaste zum Öffnen des Menüs drücken oder D zum Aktivieren des Verschiebemodus',\n keyboardDragMode: 'Tastatur-Verschiebemodus mit Pfeiltasten umschalten. Tastenkombination: D-Taste',\n keyboardDragActive: '⌨️ Tastatur-Verschiebemodus aktiv (Pfeiltasten zum Bewegen, Umschalt+Pfeiltasten für große Schritte, D oder ESC zum Beenden)',\n dragResizePrompt: 'Drücken Sie D zum Verschieben oder R zur Größenänderung. Home setzt die Position zurück, Esc schließt.',\n dragModeEnabled: 'Tastatur-Verschiebemodus aktiviert. Pfeiltasten zum Bewegen, Umschalt+Pfeiltasten für größere Schritte. D oder Esc zum Beenden.',\n dragModeDisabled: 'Tastatur-Verschiebemodus deaktiviert.',\n enableDragMode: 'Verschiebemodus aktivieren. Tastenkombination: D-Taste',\n disableDragMode: 'Verschiebemodus deaktivieren. Tastenkombination: D-Taste',\n enableDragModeAria: 'Tastatur-Verschiebemodus mit Pfeiltasten aktivieren. Tastenkombination: D-Taste',\n disableDragModeAria: 'Tastatur-Verschiebemodus mit Pfeiltasten deaktivieren. Tastenkombination: D-Taste',\n resizeWindow: 'Fenster vergrößern/verkleinern',\n disableResizeWindow: 'Resize-Modus deaktivieren',\n enableResizeMode: 'Größenänderungsmodus aktivieren. Tastenkombination: R-Taste',\n disableResizeMode: 'Größenänderungsmodus deaktivieren. Tastenkombination: R-Taste',\n enableResizeModeAria: 'Tastatur-Größenänderungsmodus mit Pfeiltasten aktivieren. Tastenkombination: R-Taste',\n disableResizeModeAria: 'Tastatur-Größenänderungsmodus mit Pfeiltasten deaktivieren. Tastenkombination: R-Taste',\n resizeModeHint: 'Griffe aktiviert. Ziehen Sie Kanten oder Ecken zum Anpassen. Esc oder R zum Beenden.',\n resizeModeEnabled: 'Resize-Modus aktiviert. Kanten oder Ecken ziehen; Esc oder R beendet.',\n resizeModeDisabled: 'Resize-Modus deaktiviert.',\n dragModeBadge: 'Verschiebemodus: Pfeiltasten zum Bewegen, Esc zum Beenden',\n resizeModeBadge: 'Resize-Modus: Kanten oder Ecken ziehen, Esc zum Beenden',\n positionReset: 'Transkriptposition zurückgesetzt.',\n styleTranscript: 'Transkript-Stileinstellungen öffnen',\n closeMenu: 'Menü schließen',\n styleTitle: 'Transkript-Stil',\n autoscroll: 'Auto-Scroll',\n settingsMenu: 'Transkript-Dialog-Einstellungen',\n showTimestamps: 'Zeitstempel anzeigen',\n hideTimestamps: 'Zeitstempel ausblenden',\n showTimestampsAria: 'Zeitstempel im Transkript anzeigen',\n hideTimestampsAria: 'Zeitstempel im Transkript ausblenden',\n seekTo: 'Springe zu {time}'\n },\n settings: {\n title: 'Einstellungen',\n quality: 'Qualität',\n speed: 'Geschwindigkeit',\n captions: 'Untertitel',\n language: 'Sprache',\n reset: 'Zurücksetzen',\n close: 'Schließen'\n },\n speeds: {\n normal: 'Normal'\n },\n time: {\n display: 'Zeitanzeige',\n durationPrefix: 'Dauer: ',\n of: 'von',\n hour: '{count} Stunde',\n hours: '{count} Stunden',\n minute: '{count} Minute',\n minutes: '{count} Minuten',\n second: '{count} Sekunde',\n seconds: '{count} Sekunden'\n },\n resume: {\n prompt: 'Bei {time} fortsetzen?',\n resume: 'Fortsetzen',\n startOver: 'Von vorne'\n },\n playlist: {\n title: 'Wiedergabeliste',\n trackOf: 'Titel {current} von {total}',\n nowPlaying: 'Läuft gerade: Titel {current} von {total}. {title}{artist}',\n by: ' von ',\n untitled: 'Ohne Titel',\n trackUntitled: 'Titel {number}',\n currentlyPlaying: 'Wird gerade abgespielt',\n notPlaying: 'Nicht aktiv',\n pressEnterPlay: 'Eingabetaste zum Abspielen',\n pressEnterRestart: 'Eingabetaste zum Neustart',\n keyboardInstructions: 'Wiedergabelisten-Navigation: Verwenden Sie die Pfeiltasten nach oben und unten, um zwischen Titeln zu wechseln. Drücken Sie Bild auf oder Bild ab, um 5 Titel zu überspringen. Drücken Sie Pos1, um zum ersten Titel zu springen, Ende für den letzten Titel. Drücken Sie die Eingabetaste oder Leertaste, um den ausgewählten Titel abzuspielen.',\n endOfPlaylist: 'Ende der Wiedergabeliste. {current} von {total}.',\n beginningOfPlaylist: 'Anfang der Wiedergabeliste. 1 von {total}.',\n jumpedToLastTrack: 'Zum letzten Titel gesprungen. {current} von {total}.',\n jumpedToFirstTrack: 'Zum ersten Titel gesprungen. 1 von {total}.',\n firstTrack: 'Erster Titel. 1 von {total}.',\n lastTrack: 'Letzter Titel. {current} von {total}.'\n }\n};\n\n"],
5
+ "mappings": ";;;;;;;AAIO,IAAM,KAAK;AAAA,EACd,QAAQ;AAAA,IACJ,OAAO;AAAA,IACP,MAAM;AAAA,IACN,OAAO;AAAA,IACP,MAAM;AAAA,IACN,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,eAAe;AAAA,IACf,gBAAgB;AAAA,IAChB,UAAU;AAAA,IACV,MAAM;AAAA,IACN,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,UAAU;AAAA,IACV,UAAU;AAAA,IACV,SAAS;AAAA,IACT,gBAAgB;AAAA,IAChB,YAAY;AAAA,IACZ,kBAAkB;AAAA,IAClB,cAAc;AAAA,IACd,UAAU;AAAA,IACV,OAAO;AAAA,IACP,KAAK;AAAA,IACL,gBAAgB;AAAA,IAChB,qBAAqB;AAAA,IACrB,qBAAqB;AAAA,IACrB,oBAAoB;AAAA,IACpB,sBAAsB;AAAA,IACtB,UAAU;AAAA,IACV,oBAAoB;AAAA,IACpB,kBAAkB;AAAA,IAClB,wBAAwB;AAAA,IACxB,aAAa;AAAA,IACb,UAAU;AAAA,IACV,UAAU;AAAA,IACV,aAAa;AAAA,IACb,cAAc;AAAA,IACd,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,OAAO;AAAA,IACP,WAAW;AAAA,IACX,mBAAmB;AAAA,IACnB,mBAAmB;AAAA,IACnB,sBAAsB;AAAA,IACtB,oBAAoB;AAAA,IACpB,kCAAkC;AAAA,IAClC,8BAA8B;AAAA,IAC9B,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,MAAM;AAAA,IACN,aAAa;AAAA,IACb,WAAW;AAAA,IACX,wBAAwB;AAAA,IACxB,wBAAwB;AAAA,IACxB,0BAA0B;AAAA,IAC1B,oBAAoB;AAAA,IACpB,qBAAqB;AAAA,IACrB,wBAAwB;AAAA,IACxB,yBAAyB;AAAA,IACzB,sBAAsB;AAAA,IACtB,uBAAuB;AAAA,IACvB,0BAA0B;AAAA,IAC1B,2BAA2B;AAAA,IAC3B,kBAAkB;AAAA,IAClB,oBAAoB;AAAA,IACpB,sBAAsB;AAAA,IACtB,wBAAwB;AAAA,IACxB,cAAc;AAAA,IACd,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,IACf,oBAAoB;AAAA,IACpB,WAAW;AAAA,EACf;AAAA,EACA,UAAU;AAAA,IACN,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,iBAAiB;AAAA,IACjB,SAAS;AAAA,EACb;AAAA,EACA,WAAW;AAAA,IACP,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,QAAQ;AAAA,EACZ;AAAA,EACA,cAAc;AAAA,IACV,WAAW;AAAA,IACX,OAAO;AAAA,IACP,WAAW;AAAA,EACf;AAAA,EACA,aAAa;AAAA,IACT,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,MAAM;AAAA,IACN,UAAU;AAAA,IACV,SAAS;AAAA,EACb;AAAA,EACA,kBAAkB;AAAA,IACd,QAAQ;AAAA,IACR,SAAS;AAAA,EACb;AAAA,EACA,cAAc;AAAA,IACV,MAAM;AAAA,IACN,MAAM;AAAA,IACN,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,EACpB;AAAA,EACA,YAAY;AAAA,IACR,OAAO;AAAA,IACP,WAAW;AAAA,IACX,OAAO;AAAA,IACP,SAAS;AAAA,IACT,cAAc;AAAA,IACd,UAAU;AAAA,IACV,kBAAkB;AAAA,IAClB,oBAAoB;AAAA,IACpB,kBAAkB;AAAA,IAClB,iBAAiB;AAAA,IACjB,kBAAkB;AAAA,IAClB,gBAAgB;AAAA,IAChB,iBAAiB;AAAA,IACjB,oBAAoB;AAAA,IACpB,qBAAqB;AAAA,IACrB,cAAc;AAAA,IACd,qBAAqB;AAAA,IACrB,kBAAkB;AAAA,IAClB,mBAAmB;AAAA,IACnB,sBAAsB;AAAA,IACtB,uBAAuB;AAAA,IACvB,gBAAgB;AAAA,IAChB,mBAAmB;AAAA,IACnB,oBAAoB;AAAA,IACpB,eAAe;AAAA,IACf,iBAAiB;AAAA,IACjB,eAAe;AAAA,IACf,iBAAiB;AAAA,IACjB,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,IAChB,oBAAoB;AAAA,IACpB,oBAAoB;AAAA,IACpB,QAAQ;AAAA,EACZ;AAAA,EACA,UAAU;AAAA,IACN,OAAO;AAAA,IACP,SAAS;AAAA,IACT,OAAO;AAAA,IACP,UAAU;AAAA,IACV,UAAU;AAAA,IACV,OAAO;AAAA,IACP,OAAO;AAAA,EACX;AAAA,EACA,QAAQ;AAAA,IACJ,QAAQ;AAAA,EACZ;AAAA,EACA,MAAM;AAAA,IACF,SAAS;AAAA,IACT,gBAAgB;AAAA,IAChB,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,SAAS;AAAA,EACb;AAAA,EACA,QAAQ;AAAA,IACJ,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,WAAW;AAAA,EACf;AAAA,EACA,UAAU;AAAA,IACN,OAAO;AAAA,IACP,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,IAAI;AAAA,IACJ,UAAU;AAAA,IACV,eAAe;AAAA,IACf,kBAAkB;AAAA,IAClB,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,mBAAmB;AAAA,IACnB,sBAAsB;AAAA,IACtB,eAAe;AAAA,IACf,qBAAqB;AAAA,IACrB,mBAAmB;AAAA,IACnB,oBAAoB;AAAA,IACpB,YAAY;AAAA,IACZ,WAAW;AAAA,EACf;AACJ;",
6
+ "names": []
7
+ }
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * VidPly v1.2.0 - Universal, Accessible Video Player
2
+ * VidPly v1.2.1 - Universal, Accessible Video Player
3
3
  * (c) 2026 Matthias Peltzer
4
4
  * Released under GPL-2.0-or-later License
5
5
  */
@@ -217,4 +217,4 @@ var es = {
217
217
  export {
218
218
  es
219
219
  };
220
- //# sourceMappingURL=vidply.es-LEWIRUBH.js.map
220
+ //# sourceMappingURL=vidply.es-SHDAT6L6.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/i18n/languages/es.ts"],
4
+ "sourcesContent": ["/**\n * Spanish translations for VidPly\n */\n\nexport const es = {\n player: {\n label: 'Reproductor de video',\n play: 'Reproducir',\n pause: 'Pausa',\n stop: 'Detener',\n restart: 'Reiniciar desde el principio',\n rewind: 'Retroceder',\n forward: 'Avanzar',\n rewindSeconds: 'Retroceder {seconds} segundos',\n forwardSeconds: 'Avanzar {seconds} segundos',\n previous: 'Pista anterior',\n next: 'Siguiente pista',\n playlist: 'Alternar lista de reproducción',\n volume: 'Volumen',\n mute: 'Silenciar',\n unmute: 'Activar sonido',\n fullscreen: 'Pantalla completa',\n exitFullscreen: 'Salir de pantalla completa',\n captions: 'Subtítulos',\n chapters: 'Capítulos',\n quality: 'Calidad',\n captionStyling: 'Estilo de subtítulos',\n transcript: 'Alternar transcripción',\n audioDescription: 'Audiodescripción',\n signLanguage: 'Video en lengua de señas',\n settings: 'Configuración',\n speed: 'Velocidad de reproducción',\n pip: 'Imagen en imagen',\n floatingPlayer: 'Reproductor flotante',\n floatingPlayerClose: 'Cerrar reproductor flotante',\n floatingPlayerEnter: 'Sacar el v\\u00eddeo',\n floatingPlayerExit: 'Anclar el v\\u00eddeo',\n floatingPlayerDialog: 'Reproductor de v\\u00eddeo flotante. Pulsa D para mover, R para redimensionar, Escape para cerrar.',\n download: 'Descargar',\n downloadWithFormat: 'Descargar {format}',\n downloadWithSize: 'Descargar ({size})',\n downloadWithFormatSize: 'Descargar {format} ({size})',\n currentTime: 'Tiempo actual',\n duration: 'Duración',\n progress: 'Progreso',\n seekForward: 'Avanzar {seconds} segundos',\n seekBackward: 'Retroceder {seconds} segundos',\n volumeUp: 'Subir volumen',\n volumeDown: 'Bajar volumen',\n loading: 'Cargando...',\n loadingChapters: 'Cargando capítulos...',\n error: 'Error al cargar',\n buffering: 'Almacenando en búfer...',\n signLanguageVideo: 'Video en Lengua de Señas',\n closeSignLanguage: 'Cerrar video en lengua de señas',\n signLanguageSettings: 'Configuración de lengua de señas',\n noChapters: 'No hay capítulos disponibles',\n noCaptions: 'No hay subtítulos disponibles',\n auto: 'Automático',\n autoQuality: 'Automático (selección de calidad no disponible)',\n noQuality: 'Selección de calidad no disponible',\n signLanguageDragResize: 'Video en Lengua de Señas - Presione D para arrastrar con el teclado, R para cambiar el tamaño',\n signLanguageDragActive: 'Video en Lengua de Señas - Modo de arrastre activo. Use las teclas de flecha para mover, Escape para salir.',\n signLanguageResizeActive: 'Video en Lengua de Señas - Modo de cambio de tamaño activo. Use las teclas de flecha izquierda/derecha para cambiar el tamaño, Escape para salir.',\n enableSignDragMode: 'Activar modo de arrastre. Atajo: tecla D',\n disableSignDragMode: 'Desactivar modo de arrastre. Atajo: tecla D',\n enableSignDragModeAria: 'Activar modo de arrastre con teclado usando teclas de flecha. Atajo: tecla D',\n disableSignDragModeAria: 'Desactivar modo de arrastre con teclado usando teclas de flecha. Atajo: tecla D',\n enableSignResizeMode: 'Activar modo de cambio de tamaño. Atajo: tecla R',\n disableSignResizeMode: 'Desactivar modo de cambio de tamaño. Atajo: tecla R',\n enableSignResizeModeAria: 'Activar modo de cambio de tamaño con teclado usando teclas de flecha. Atajo: tecla R',\n disableSignResizeModeAria: 'Desactivar modo de cambio de tamaño con teclado usando teclas de flecha. Atajo: tecla R',\n signDragModeHint: 'Modo de arrastre: flechas para mover, Esc para salir',\n signResizeModeHint: 'Modo de cambio de tamaño: flechas para redimensionar, Esc para salir',\n signDragModeDisabled: 'Modo de arrastre del video en lengua de señas desactivado.',\n signResizeModeDisabled: 'Modo de cambio de tamaño del video en lengua de señas desactivado.',\n resizeHandle: 'Cambiar tamaño esquina {direction}',\n moreOptions: 'Más opciones',\n noMoreOptions: 'No hay opciones adicionales disponibles',\n playing: 'Reproduciendo',\n paused: 'En pausa',\n muted: 'Silenciado',\n unmuted: 'Sonido activado',\n captionsOn: 'Subtítulos activados',\n captionsOff: 'Subtítulos desactivados',\n volumePercent: 'Volumen {percent} por ciento',\n volumePercentMuted: 'Silenciado',\n speedRate: 'Velocidad {rate}x'\n },\n captions: {\n off: 'Desactivado',\n select: 'Seleccionar subtítulos',\n fontSize: 'Tamaño de fuente',\n fontFamily: 'Familia de fuente',\n color: 'Color de texto',\n backgroundColor: 'Color de fondo',\n opacity: 'Opacidad'\n },\n fontSizes: {\n small: 'Pequeño',\n normal: 'Normal',\n large: 'Grande',\n xlarge: 'Muy grande'\n },\n fontFamilies: {\n sansSerif: 'Sans-serif',\n serif: 'Serif',\n monospace: 'Monospace'\n },\n styleLabels: {\n textColor: 'Color de texto',\n background: 'Fondo',\n font: 'Fuente',\n fontSize: 'Tamaño de fuente',\n opacity: 'Opacidad'\n },\n audioDescription: {\n enable: 'Activar audiodescripción',\n disable: 'Desactivar audiodescripción'\n },\n signLanguage: {\n show: 'Mostrar video en lengua de señas',\n hide: 'Ocultar video en lengua de señas',\n showInMainView: 'Mostrar lengua de señas en video principal',\n hideInMainView: 'Ocultar lengua de señas del video principal'\n },\n transcript: {\n title: 'Transcripción',\n ariaLabel: 'Transcripción de video',\n close: 'Cerrar transcripción',\n loading: 'Cargando transcripción...',\n noTranscript: 'No hay transcripción disponible para este video.',\n settings: 'Configuración de transcripción. Presione Enter para abrir el menú o D para activar el modo de arrastre',\n keyboardDragMode: 'Alternar modo de arrastre con teclado usando teclas de flecha. Atajo: tecla D',\n keyboardDragActive: '⌨️ Modo de Arrastre con Teclado Activo (Teclas de flecha para mover, Mayús+Flechas para pasos grandes, D o ESC para salir)',\n dragResizePrompt: 'Pulsa D para mover o R para cambiar el tamaño. Home restablece la posición; Esc cierra.',\n dragModeEnabled: 'Modo de arrastre con teclado activado. Usa flechas para mover, Mayús+Flechas para pasos grandes. Pulsa D o Esc para salir.',\n dragModeDisabled: 'Modo de arrastre con teclado desactivado.',\n enableDragMode: 'Activar modo de arrastre. Atajo: tecla D',\n disableDragMode: 'Desactivar modo de arrastre. Atajo: tecla D',\n enableDragModeAria: 'Activar modo de arrastre con teclado usando teclas de flecha. Atajo: tecla D',\n disableDragModeAria: 'Desactivar modo de arrastre con teclado usando teclas de flecha. Atajo: tecla D',\n resizeWindow: 'Cambiar tamaño de ventana',\n disableResizeWindow: 'Desactivar modo de cambio de tamaño',\n enableResizeMode: 'Activar modo de cambio de tamaño. Atajo: tecla R',\n disableResizeMode: 'Desactivar modo de cambio de tamaño. Atajo: tecla R',\n enableResizeModeAria: 'Activar modo de cambio de tamaño con teclado usando teclas de flecha. Atajo: tecla R',\n disableResizeModeAria: 'Desactivar modo de cambio de tamaño con teclado usando teclas de flecha. Atajo: tecla R',\n resizeModeHint: 'Controladores habilitados. Arrastra bordes o esquinas para ajustar. Pulsa Esc o R para salir.',\n resizeModeEnabled: 'Modo de cambio de tamaño activado. Arrastra bordes o esquinas. Pulsa Esc o R para salir.',\n resizeModeDisabled: 'Modo de cambio de tamaño desactivado.',\n dragModeBadge: 'Modo mover: flechas para mover, Esc para salir',\n resizeModeBadge: 'Modo redimensionar: arrastra bordes o esquinas, Esc para salir',\n positionReset: 'Posición de la transcripción restablecida.',\n styleTranscript: 'Abrir configuración de estilo de transcripción',\n closeMenu: 'Cerrar menú',\n styleTitle: 'Estilo de Transcripción',\n autoscroll: 'Desplazamiento automático',\n settingsMenu: 'Configuración del diálogo de transcripción',\n showTimestamps: 'Mostrar marcas de tiempo',\n hideTimestamps: 'Ocultar marcas de tiempo',\n showTimestampsAria: 'Mostrar marcas de tiempo en la transcripción',\n hideTimestampsAria: 'Ocultar marcas de tiempo en la transcripción',\n seekTo: 'Saltar a {time}'\n },\n settings: {\n title: 'Configuración',\n quality: 'Calidad',\n speed: 'Velocidad',\n captions: 'Subtítulos',\n language: 'Idioma',\n reset: 'Restablecer',\n close: 'Cerrar'\n },\n speeds: {\n normal: 'Normal'\n },\n time: {\n display: 'Visualización de tiempo',\n durationPrefix: 'Duración: ',\n of: 'de',\n hour: '{count} hora',\n hours: '{count} horas',\n minute: '{count} minuto',\n minutes: '{count} minutos',\n second: '{count} segundo',\n seconds: '{count} segundos'\n },\n resume: {\n prompt: '¿Continuar desde {time}?',\n resume: 'Continuar',\n startOver: 'Empezar de nuevo'\n },\n playlist: {\n title: 'Lista de reproducción',\n trackOf: 'Pista {current} de {total}',\n nowPlaying: 'Reproduciendo ahora: Pista {current} de {total}. {title}{artist}',\n by: ' por ',\n untitled: 'Sin título',\n trackUntitled: 'Pista {number}',\n currentlyPlaying: 'Reproduciendo actualmente',\n notPlaying: 'Sin reproducir',\n pressEnterPlay: 'Pulsa Enter para reproducir',\n pressEnterRestart: 'Pulsa Enter para reiniciar',\n keyboardInstructions: 'Navegación de lista de reproducción: Use las teclas de flecha arriba y abajo para moverse entre pistas. Pulse Retroceder página o Avanzar página para saltar 5 pistas. Pulse Inicio para ir a la primera pista, Fin para la última pista. Pulse Intro o Espacio para reproducir la pista seleccionada.',\n endOfPlaylist: 'Fin de la lista de reproducción. {current} de {total}.',\n beginningOfPlaylist: 'Inicio de la lista de reproducción. 1 de {total}.',\n jumpedToLastTrack: 'Saltó a la última pista. {current} de {total}.',\n jumpedToFirstTrack: 'Saltó a la primera pista. 1 de {total}.',\n firstTrack: 'Primera pista. 1 de {total}.',\n lastTrack: 'Última pista. {current} de {total}.'\n }\n};\n\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -1,39 +1,39 @@
1
1
  /*!
2
- * VidPly v1.2.0 - Universal, Accessible Video Player
2
+ * VidPly v1.2.1 - 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
  TimeUtils
8
- } from "./vidply.chunk-SK4V2DAU.js";
8
+ } from "./vidply.chunk-YVCYQZJI.js";
9
9
  import {
10
10
  createIconElement,
11
11
  createPlayOverlay
12
- } from "./vidply.chunk-KI7C3QVL.js";
12
+ } from "./vidply.chunk-RQG3MA7W.js";
13
13
  import {
14
14
  focusElement
15
- } from "./vidply.chunk-VMSQAZM7.js";
15
+ } from "./vidply.chunk-JAKZNGOR.js";
16
16
  import {
17
17
  HTML5Renderer
18
- } from "./vidply.chunk-W25HUCHS.js";
18
+ } from "./vidply.chunk-XAK3DPTO.js";
19
19
  import {
20
20
  CaptionManager
21
- } from "./vidply.chunk-64WXMTGL.js";
21
+ } from "./vidply.chunk-LDVFAFGC.js";
22
22
  import {
23
23
  StorageManager
24
- } from "./vidply.chunk-BNFIUL2Z.js";
24
+ } from "./vidply.chunk-UHMVDBDB.js";
25
25
  import {
26
26
  debounce,
27
27
  isMobile,
28
28
  rafWithTimeout,
29
29
  reducedMotionScrollOptions,
30
30
  throttle
31
- } from "./vidply.chunk-6RQA4ROD.js";
31
+ } from "./vidply.chunk-5IK3IC5V.js";
32
32
  import {
33
33
  DOMUtils,
34
34
  i18n,
35
35
  isForbiddenKey
36
- } from "./vidply.chunk-RZWZGDV7.js";
36
+ } from "./vidply.chunk-NSMAZCMB.js";
37
37
 
38
38
  // src/utils/EventEmitter.ts
39
39
  var EventEmitter = class {
@@ -2051,7 +2051,7 @@ var ControlBar = class {
2051
2051
  return button;
2052
2052
  }
2053
2053
  showCaptionStyleMenu(button) {
2054
- import("./vidply.CaptionStyleMenu-7XHRBWUM.js").then(({ showCaptionStyleMenu }) => showCaptionStyleMenu(this, button)).catch((error) => this.player.log("Failed to load caption style menu:", error, "error"));
2054
+ import("./vidply.CaptionStyleMenu-R2SCQ6VP.js").then(({ showCaptionStyleMenu }) => showCaptionStyleMenu(this, button)).catch((error) => this.player.log("Failed to load caption style menu:", error, "error"));
2055
2055
  }
2056
2056
  createSpeedButton() {
2057
2057
  const button = DOMUtils.createElement("button", {
@@ -4700,21 +4700,21 @@ var SignLanguageManagerModule = null;
4700
4700
  var FloatingPlayerManagerModule = null;
4701
4701
  async function loadAudioDescriptionManager() {
4702
4702
  if (!AudioDescriptionManagerModule) {
4703
- const module = await import("./vidply.AudioDescriptionManager-R47Q4YR4.js");
4703
+ const module = await import("./vidply.AudioDescriptionManager-6RFVF27Z.js");
4704
4704
  AudioDescriptionManagerModule = module.AudioDescriptionManager;
4705
4705
  }
4706
4706
  return AudioDescriptionManagerModule;
4707
4707
  }
4708
4708
  async function loadSignLanguageManager() {
4709
4709
  if (!SignLanguageManagerModule) {
4710
- const module = await import("./vidply.SignLanguageManager-FET5FQZ7.js");
4710
+ const module = await import("./vidply.SignLanguageManager-7IAJOVF6.js");
4711
4711
  SignLanguageManagerModule = module.SignLanguageManager;
4712
4712
  }
4713
4713
  return SignLanguageManagerModule;
4714
4714
  }
4715
4715
  async function loadFloatingPlayerManager() {
4716
4716
  if (!FloatingPlayerManagerModule) {
4717
- const module = await import("./vidply.FloatingPlayerManager-PKNNNAS2.js");
4717
+ const module = await import("./vidply.FloatingPlayerManager-V5XQWS75.js");
4718
4718
  FloatingPlayerManagerModule = module.FloatingPlayerManager;
4719
4719
  }
4720
4720
  return FloatingPlayerManagerModule;
@@ -4967,6 +4967,9 @@ var Player = class _Player extends EventEmitter {
4967
4967
  audioDescriptionSrc: null,
4968
4968
  // URL to audio-described version
4969
4969
  audioDescriptionButton: true,
4970
+ audioDescriptionMode: "auto",
4971
+ audioDescriptionSpeech: true,
4972
+ audioDescriptionExtended: true,
4970
4973
  // Sign Language
4971
4974
  signLanguage: true,
4972
4975
  signLanguageSrc: null,
@@ -5364,7 +5367,7 @@ var Player = class _Player extends EventEmitter {
5364
5367
  if (!this.options.transcript && !this.options.transcriptButton) {
5365
5368
  return null;
5366
5369
  }
5367
- const module = await import("./vidply.TranscriptManager-5GXOQCLX.js");
5370
+ const module = await import("./vidply.TranscriptManager-BPM3T23L.js");
5368
5371
  const fallbackDefault = module.default;
5369
5372
  const Manager = module.TranscriptManager || fallbackDefault;
5370
5373
  if (!Manager) {
@@ -5392,15 +5395,33 @@ var Player = class _Player extends EventEmitter {
5392
5395
  if (this.audioDescriptionManager) {
5393
5396
  return this.audioDescriptionManager;
5394
5397
  }
5395
- const hasAudioDescSrc = this.options.audioDescriptionSrc || this.audioDescriptionSrc;
5396
- const hasAudioDescButton = this.options.audioDescriptionButton;
5397
- if (!hasAudioDescSrc && !hasAudioDescButton) {
5398
+ if (!this.hasAudioDescriptionContent()) {
5398
5399
  return null;
5399
5400
  }
5400
5401
  const AudioDescManager = await loadAudioDescriptionManager();
5401
5402
  this.audioDescriptionManager = new AudioDescManager(this);
5402
5403
  return this.audioDescriptionManager;
5403
5404
  }
5405
+ /**
5406
+ * True when the current media actually exposes audio-description content:
5407
+ * an explicit described-audio source, `<source>` elements carrying
5408
+ * `data-desc-src` / `data-orig-src`, or a `descriptions` text track.
5409
+ * Mirrors `ControlBar.hasAudioDescription()` so the chunk load and the
5410
+ * button visibility stay in lock-step.
5411
+ */
5412
+ hasAudioDescriptionContent() {
5413
+ if (this.options.audioDescriptionSrc || this.audioDescriptionSrc) {
5414
+ return true;
5415
+ }
5416
+ const hasSourceElementsWithDesc = this.sourceElements.some(
5417
+ (el) => el.getAttribute("data-desc-src") || el.getAttribute("data-orig-src")
5418
+ );
5419
+ if (hasSourceElementsWithDesc) {
5420
+ return true;
5421
+ }
5422
+ const textTracks = this.element ? Array.from(this.element.textTracks || []) : [];
5423
+ return textTracks.some((track) => track.kind === "descriptions");
5424
+ }
5404
5425
  // ============================================
5405
5426
  // Resume Playback Methods
5406
5427
  // ============================================
@@ -5412,16 +5433,24 @@ var Player = class _Player extends EventEmitter {
5412
5433
  if (this.signLanguageManager) {
5413
5434
  return this.signLanguageManager;
5414
5435
  }
5415
- const hasSignLangSrc = this.options.signLanguageSrc || this.signLanguageSrc;
5416
- const hasSignLangSources = this.options.signLanguageSources && Object.keys(this.options.signLanguageSources).length > 0;
5417
- const hasSignLangButton = this.options.signLanguageButton;
5418
- if (!hasSignLangSrc && !hasSignLangSources && !hasSignLangButton) {
5436
+ if (!this.hasSignLanguageContent()) {
5419
5437
  return null;
5420
5438
  }
5421
5439
  const SignLangManager = await loadSignLanguageManager();
5422
5440
  this.signLanguageManager = new SignLangManager(this);
5423
5441
  return this.signLanguageManager;
5424
5442
  }
5443
+ /**
5444
+ * True when a sign-language video source (single `signLanguageSrc` or a
5445
+ * `signLanguageSources` map) is configured. Mirrors
5446
+ * `ControlBar.hasSignLanguage()`.
5447
+ */
5448
+ hasSignLanguageContent() {
5449
+ if (this.options.signLanguageSrc || this.signLanguageSrc) {
5450
+ return true;
5451
+ }
5452
+ return !!(this.options.signLanguageSources && Object.keys(this.options.signLanguageSources).length > 0);
5453
+ }
5425
5454
  /**
5426
5455
  * Lazy-load and instantiate the floating (in-page PiP) manager. Only
5427
5456
  * created when `options.floating === true` and the media element is a
@@ -5446,13 +5475,10 @@ var Player = class _Player extends EventEmitter {
5446
5475
  */
5447
5476
  async initFeatureManagers() {
5448
5477
  const promises = [];
5449
- const hasSourceElementsWithDesc = this.sourceElements.some(
5450
- (el) => el.getAttribute("data-desc-src") || el.getAttribute("data-orig-src")
5451
- );
5452
- if (this.options.audioDescriptionButton || this.options.audioDescriptionSrc || this.audioDescriptionSrc || hasSourceElementsWithDesc) {
5478
+ if (this.hasAudioDescriptionContent()) {
5453
5479
  promises.push(this.ensureAudioDescriptionManager());
5454
5480
  }
5455
- if (this.options.signLanguageButton || this.options.signLanguageSrc || this.signLanguageSrc || this.options.signLanguageSources && Object.keys(this.options.signLanguageSources).length > 0) {
5481
+ if (this.hasSignLanguageContent()) {
5456
5482
  promises.push(this.ensureSignLanguageManager());
5457
5483
  }
5458
5484
  if (this.options.floating && this.element && this.element.tagName === "VIDEO") {
@@ -5782,6 +5808,9 @@ var Player = class _Player extends EventEmitter {
5782
5808
  }
5783
5809
  this.currentSource = src;
5784
5810
  this._pendingSource = null;
5811
+ if (this.hasAudioDescriptionContent()) {
5812
+ await this.ensureAudioDescriptionManager();
5813
+ }
5785
5814
  this.audioDescriptionManager?.initFromSourceElements(this.sourceElements, this.trackElements);
5786
5815
  if (!this.originalSrc) {
5787
5816
  this.originalSrc = src;
@@ -5807,19 +5836,19 @@ var Player = class _Player extends EventEmitter {
5807
5836
  }
5808
5837
  async _detectRendererClass(src) {
5809
5838
  if (src.includes("youtube.com") || src.includes("youtu.be")) {
5810
- const module = await import("./vidply.YouTubeRenderer-NFTOWJAB.js");
5839
+ const module = await import("./vidply.YouTubeRenderer-HRTN4EJ5.js");
5811
5840
  return module.YouTubeRenderer ?? module.default;
5812
5841
  } else if (src.includes("vimeo.com")) {
5813
- const module = await import("./vidply.VimeoRenderer-ER2LSBBW.js");
5842
+ const module = await import("./vidply.VimeoRenderer-BRHFYPNJ.js");
5814
5843
  return module.VimeoRenderer ?? module.default;
5815
5844
  } else if (src.includes(".m3u8")) {
5816
- const module = await import("./vidply.HLSRenderer-E7RQ74HI.js");
5845
+ const module = await import("./vidply.HLSRenderer-ZBEFVH5W.js");
5817
5846
  return module.HLSRenderer ?? module.default;
5818
5847
  } else if (src.includes(".mpd")) {
5819
- const module = await import("./vidply.DASHRenderer-PWEMC3BT.js");
5848
+ const module = await import("./vidply.DASHRenderer-EOVEW73H.js");
5820
5849
  return module.DASHRenderer ?? module.default;
5821
5850
  } else if (src.includes("soundcloud.com") || src.includes("api.soundcloud.com")) {
5822
- const module = await import("./vidply.SoundCloudRenderer-RVSJZODO.js");
5851
+ const module = await import("./vidply.SoundCloudRenderer-OA7QQYGN.js");
5823
5852
  return module.SoundCloudRenderer ?? module.default;
5824
5853
  }
5825
5854
  return HTML5Renderer;
@@ -7285,12 +7314,19 @@ var PlaylistManager = class {
7285
7314
  if (typeof this.player.invalidateTrackCache === "function") {
7286
7315
  this.player.invalidateTrackCache();
7287
7316
  }
7288
- if (this.player.audioDescriptionManager && typeof this.player.audioDescriptionManager.initFromSourceElements === "function") {
7317
+ const reinitAudioDescription = (adm) => {
7318
+ if (!adm || typeof adm.initFromSourceElements !== "function") return;
7289
7319
  try {
7290
- this.player.audioDescriptionManager.captionTracks = [];
7291
- this.player.audioDescriptionManager.initFromSourceElements(this.player.sourceElements, this.player.trackElements);
7320
+ adm.captionTracks = [];
7321
+ adm.initFromSourceElements(this.player.sourceElements, this.player.trackElements);
7292
7322
  } catch {
7293
7323
  }
7324
+ };
7325
+ if (this.player.audioDescriptionManager) {
7326
+ reinitAudioDescription(this.player.audioDescriptionManager);
7327
+ } else if (this.player.hasAudioDescriptionContent?.()) {
7328
+ void this.player.ensureAudioDescriptionManager().then(reinitAudioDescription).catch(() => {
7329
+ });
7294
7330
  }
7295
7331
  if (this.player.captionManager && typeof this.player.captionManager.loadTracks === "function") {
7296
7332
  try {
@@ -8152,6 +8188,9 @@ function parseDataAttributes(dataset) {
8152
8188
  signLanguageDisplayMode: "signLanguageDisplayMode",
8153
8189
  audioDescriptionSrc: "audioDescriptionSrc",
8154
8190
  audioDescriptionButton: "audioDescriptionButton",
8191
+ audioDescriptionMode: "audioDescriptionMode",
8192
+ audioDescriptionSpeech: "audioDescriptionSpeech",
8193
+ audioDescriptionExtended: "audioDescriptionExtended",
8155
8194
  autoplay: "autoplay",
8156
8195
  loop: "loop",
8157
8196
  muted: "muted",