vidply 1.1.18 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/README.md +15 -8
  2. package/dist/dev/{vidply.AudioDescriptionManager-GJO454ID.js → vidply.AudioDescriptionManager-R47Q4YR4.js} +12 -10
  3. package/dist/dev/vidply.AudioDescriptionManager-R47Q4YR4.js.map +7 -0
  4. package/dist/dev/vidply.CaptionStyleMenu-7XHRBWUM.js +300 -0
  5. package/dist/dev/vidply.CaptionStyleMenu-7XHRBWUM.js.map +7 -0
  6. package/dist/dev/{vidply.DASHRenderer-CAIEEBIY.js → vidply.DASHRenderer-PWEMC3BT.js} +42 -35
  7. package/dist/dev/vidply.DASHRenderer-PWEMC3BT.js.map +7 -0
  8. package/dist/dev/{vidply.FloatingPlayerManager-V4UV3DSB.js → vidply.FloatingPlayerManager-PKNNNAS2.js} +10 -5
  9. package/dist/dev/{vidply.FloatingPlayerManager-V4UV3DSB.js.map → vidply.FloatingPlayerManager-PKNNNAS2.js.map} +2 -2
  10. package/dist/dev/{vidply.HLSRenderer-KQLJ6EEO.js → vidply.HLSRenderer-E7RQ74HI.js} +81 -46
  11. package/dist/dev/vidply.HLSRenderer-E7RQ74HI.js.map +7 -0
  12. package/dist/dev/vidply.HTML5Renderer-BU4QJJJY.js +12 -0
  13. package/dist/dev/{vidply.SignLanguageManager-3YJHYEJS.js → vidply.SignLanguageManager-FET5FQZ7.js} +14 -13
  14. package/dist/dev/vidply.SignLanguageManager-FET5FQZ7.js.map +7 -0
  15. package/dist/dev/{vidply.SoundCloudRenderer-SAJ6RQNB.js → vidply.SoundCloudRenderer-RVSJZODO.js} +30 -18
  16. package/dist/dev/vidply.SoundCloudRenderer-RVSJZODO.js.map +7 -0
  17. package/dist/dev/{vidply.TranscriptManager-KRLWUIL5.js → vidply.TranscriptManager-5GXOQCLX.js} +24 -21
  18. package/dist/dev/vidply.TranscriptManager-5GXOQCLX.js.map +7 -0
  19. package/dist/dev/{vidply.VimeoRenderer-H425TIXI.js → vidply.VimeoRenderer-ER2LSBBW.js} +8 -9
  20. package/dist/dev/vidply.VimeoRenderer-ER2LSBBW.js.map +7 -0
  21. package/dist/dev/{vidply.YouTubeRenderer-RJFSC7DO.js → vidply.YouTubeRenderer-NFTOWJAB.js} +10 -25
  22. package/dist/dev/vidply.YouTubeRenderer-NFTOWJAB.js.map +7 -0
  23. package/dist/dev/{vidply.chunk-I3Y5BLC6.js → vidply.chunk-64WXMTGL.js} +15 -48
  24. package/dist/dev/vidply.chunk-64WXMTGL.js.map +7 -0
  25. package/dist/dev/vidply.chunk-6RQA4ROD.js +57 -0
  26. package/dist/dev/vidply.chunk-6RQA4ROD.js.map +7 -0
  27. package/dist/dev/{vidply.chunk-ZR5JQPCM.js → vidply.chunk-BNFIUL2Z.js} +7 -4
  28. package/dist/dev/{vidply.chunk-ZR5JQPCM.js.map → vidply.chunk-BNFIUL2Z.js.map} +2 -2
  29. package/dist/dev/vidply.chunk-DZSDO3B7.js +87 -0
  30. package/dist/dev/vidply.chunk-DZSDO3B7.js.map +7 -0
  31. package/dist/dev/{vidply.chunk-DEVK3LVC.js → vidply.chunk-JJ47NO24.js} +5 -2
  32. package/dist/dev/vidply.chunk-JJ47NO24.js.map +7 -0
  33. package/dist/dev/{vidply.chunk-KGJQUMTY.js → vidply.chunk-KI7C3QVL.js} +2 -2
  34. package/dist/dev/{vidply.chunk-KGJQUMTY.js.map → vidply.chunk-KI7C3QVL.js.map} +1 -1
  35. package/dist/dev/{vidply.chunk-BQ3SUKRY.js → vidply.chunk-NQPURMQY.js} +35 -19
  36. package/dist/dev/vidply.chunk-NQPURMQY.js.map +7 -0
  37. package/dist/dev/{vidply.chunk-QGWFSJ7R.js → vidply.chunk-RZWZGDV7.js} +10 -9
  38. package/dist/dev/vidply.chunk-RZWZGDV7.js.map +7 -0
  39. package/dist/dev/{vidply.chunk-4VFODBFZ.js → vidply.chunk-SK4V2DAU.js} +6 -6
  40. package/dist/dev/vidply.chunk-SK4V2DAU.js.map +7 -0
  41. package/dist/dev/{vidply.chunk-PROLQGB6.js → vidply.chunk-VMSQAZM7.js} +2 -2
  42. package/dist/dev/{vidply.chunk-ZFY7FNCU.js → vidply.chunk-W25HUCHS.js} +28 -24
  43. package/dist/dev/vidply.chunk-W25HUCHS.js.map +7 -0
  44. package/dist/dev/{vidply.de-NCS32MR5.js → vidply.de-4YAT7FFM.js} +2 -2
  45. package/dist/dev/{vidply.es-KTLE6NCH.js → vidply.es-LEWIRUBH.js} +2 -2
  46. package/dist/dev/vidply.esm.js +216 -376
  47. package/dist/dev/vidply.esm.js.map +2 -2
  48. package/dist/dev/{vidply.fr-7CEEBQ77.js → vidply.fr-Q4GV2NOE.js} +2 -2
  49. package/dist/dev/{vidply.ja-5UIYTCX7.js → vidply.ja-S6ITA6WU.js} +2 -2
  50. package/dist/legacy/vidply.js +845 -538
  51. package/dist/legacy/vidply.js.map +4 -4
  52. package/dist/legacy/vidply.min.js +2 -2
  53. package/dist/legacy/vidply.min.meta.json +134 -52
  54. package/dist/prod/vidply.AudioDescriptionManager-RHLIJW5C.min.js +6 -0
  55. package/dist/prod/vidply.CaptionStyleMenu-M3KVQWB5.min.js +6 -0
  56. package/dist/prod/vidply.DASHRenderer-DN3S27AV.min.js +6 -0
  57. package/dist/prod/{vidply.FloatingPlayerManager-RRMX5WBA.min.js → vidply.FloatingPlayerManager-SKAZ4J43.min.js} +2 -2
  58. package/dist/prod/vidply.HLSRenderer-62HH4YTK.min.js +6 -0
  59. package/dist/prod/vidply.HTML5Renderer-IU5T3WEW.min.js +6 -0
  60. package/dist/prod/vidply.SignLanguageManager-V3OBTSSF.min.js +6 -0
  61. package/dist/prod/vidply.SoundCloudRenderer-CXRVZIAJ.min.js +6 -0
  62. package/dist/prod/vidply.TranscriptManager-SYM3DFUY.min.js +6 -0
  63. package/dist/prod/vidply.VimeoRenderer-72TWRYFQ.min.js +6 -0
  64. package/dist/prod/vidply.YouTubeRenderer-UMHEV3KD.min.js +6 -0
  65. package/dist/prod/vidply.chunk-2TB6MUHO.min.js +6 -0
  66. package/dist/prod/{vidply.chunk-TKTTOLX5.min.js → vidply.chunk-ABPXVDLZ.min.js} +1 -1
  67. package/dist/prod/vidply.chunk-EKEYJWMG.min.js +6 -0
  68. package/dist/prod/{vidply.chunk-AHEIUUN3.min.js → vidply.chunk-P3PQBY7N.min.js} +1 -1
  69. package/dist/prod/vidply.chunk-P4UXOHWA.min.js +6 -0
  70. package/dist/prod/{vidply.chunk-J45YW6UC.min.js → vidply.chunk-RI3X7WHK.min.js} +2 -2
  71. package/dist/prod/vidply.chunk-TM6AXJVD.min.js +6 -0
  72. package/dist/prod/vidply.chunk-UK7YGOB5.min.js +6 -0
  73. package/dist/prod/vidply.chunk-WX3MVEAL.min.js +6 -0
  74. package/dist/prod/vidply.chunk-YEENJS3Q.min.js +6 -0
  75. package/dist/prod/vidply.chunk-Z3YDSA7O.min.js +6 -0
  76. package/dist/prod/{vidply.de-NVBWZKXD.min.js → vidply.de-BCHXRQ53.min.js} +1 -1
  77. package/dist/prod/{vidply.es-EVA5ETFV.min.js → vidply.es-PICAKMZD.min.js} +1 -1
  78. package/dist/prod/vidply.esm.min.js +2 -2
  79. package/dist/prod/{vidply.fr-2BFHYAIB.min.js → vidply.fr-ZILI7CDR.min.js} +1 -1
  80. package/dist/prod/{vidply.ja-KLEUHUWZ.min.js → vidply.ja-5WQ4OK4J.min.js} +1 -1
  81. package/dist/vidply.css +148 -244
  82. package/dist/vidply.esm.min.meta.json +414 -238
  83. package/dist/vidply.min.css +1 -1
  84. package/package.json +10 -7
  85. package/src/controls/CaptionManager.ts +11 -4
  86. package/src/controls/CaptionStyleMenu.ts +370 -0
  87. package/src/controls/ControlBar.ts +93 -403
  88. package/src/controls/KeyboardManager.ts +78 -2
  89. package/src/controls/SettingsDialog.ts +32 -2
  90. package/src/controls/TranscriptManager.ts +12 -10
  91. package/src/core/AudioDescriptionManager.ts +8 -6
  92. package/src/core/FloatingPlayerManager.ts +11 -0
  93. package/src/core/MetadataAlertsManager.ts +2 -1
  94. package/src/core/Player.ts +32 -11
  95. package/src/core/PseudoFullscreen.ts +4 -2
  96. package/src/core/SignLanguageManager.ts +9 -7
  97. package/src/features/PlaylistManager.ts +41 -26
  98. package/src/icons/Icons.ts +1 -1
  99. package/src/index.ts +1 -1
  100. package/src/renderers/DASHRenderer.ts +42 -33
  101. package/src/renderers/HLSRenderer.ts +100 -54
  102. package/src/renderers/HTML5Renderer.ts +28 -21
  103. package/src/renderers/SoundCloudRenderer.ts +33 -18
  104. package/src/renderers/VimeoRenderer.ts +7 -7
  105. package/src/renderers/YouTubeRenderer.ts +14 -29
  106. package/src/styles/vidply.css +148 -244
  107. package/src/types/globals.d.ts +36 -2
  108. package/src/types/renderer.ts +0 -5
  109. package/src/utils/DOMUtils.ts +4 -3
  110. package/src/utils/DownloadInfo.ts +2 -2
  111. package/src/utils/DraggableResizable.ts +4 -1
  112. package/src/utils/MenuFactory.ts +36 -14
  113. package/src/utils/MenuUtils.ts +31 -13
  114. package/src/utils/PerformanceUtils.ts +23 -0
  115. package/src/utils/ScriptLoader.ts +137 -0
  116. package/src/utils/SettingsMenuFactory.ts +21 -8
  117. package/src/utils/StorageManager.ts +4 -1
  118. package/src/utils/TimeUtils.ts +3 -3
  119. package/dist/dev/vidply.AudioDescriptionManager-GJO454ID.js.map +0 -7
  120. package/dist/dev/vidply.DASHRenderer-CAIEEBIY.js.map +0 -7
  121. package/dist/dev/vidply.HLSRenderer-KQLJ6EEO.js.map +0 -7
  122. package/dist/dev/vidply.HTML5Renderer-TZTNNAQK.js +0 -12
  123. package/dist/dev/vidply.SignLanguageManager-3YJHYEJS.js.map +0 -7
  124. package/dist/dev/vidply.SoundCloudRenderer-SAJ6RQNB.js.map +0 -7
  125. package/dist/dev/vidply.TranscriptManager-KRLWUIL5.js.map +0 -7
  126. package/dist/dev/vidply.VimeoRenderer-H425TIXI.js.map +0 -7
  127. package/dist/dev/vidply.YouTubeRenderer-RJFSC7DO.js.map +0 -7
  128. package/dist/dev/vidply.chunk-4VFODBFZ.js.map +0 -7
  129. package/dist/dev/vidply.chunk-BQ3SUKRY.js.map +0 -7
  130. package/dist/dev/vidply.chunk-DEVK3LVC.js.map +0 -7
  131. package/dist/dev/vidply.chunk-I3Y5BLC6.js.map +0 -7
  132. package/dist/dev/vidply.chunk-QGWFSJ7R.js.map +0 -7
  133. package/dist/dev/vidply.chunk-ZFY7FNCU.js.map +0 -7
  134. package/dist/prod/vidply.AudioDescriptionManager-BHLFV4AX.min.js +0 -6
  135. package/dist/prod/vidply.DASHRenderer-EU76KE2D.min.js +0 -6
  136. package/dist/prod/vidply.HLSRenderer-2KUQ6CXF.min.js +0 -6
  137. package/dist/prod/vidply.HTML5Renderer-6MT7K3S3.min.js +0 -6
  138. package/dist/prod/vidply.SignLanguageManager-45XEBRUO.min.js +0 -6
  139. package/dist/prod/vidply.SoundCloudRenderer-UTH6RXEP.min.js +0 -6
  140. package/dist/prod/vidply.TranscriptManager-JEWHNZRE.min.js +0 -6
  141. package/dist/prod/vidply.VimeoRenderer-46FRK7KO.min.js +0 -6
  142. package/dist/prod/vidply.YouTubeRenderer-AB7Q7ZX3.min.js +0 -6
  143. package/dist/prod/vidply.chunk-3Y7DZFWE.min.js +0 -6
  144. package/dist/prod/vidply.chunk-CIOBWWPJ.min.js +0 -6
  145. package/dist/prod/vidply.chunk-H7OZCZSW.min.js +0 -6
  146. package/dist/prod/vidply.chunk-S7UXDVRO.min.js +0 -6
  147. package/dist/prod/vidply.chunk-T3GGXROP.min.js +0 -6
  148. package/dist/prod/vidply.chunk-YXAFRFSQ.min.js +0 -6
  149. /package/dist/dev/{vidply.HTML5Renderer-TZTNNAQK.js.map → vidply.HTML5Renderer-BU4QJJJY.js.map} +0 -0
  150. /package/dist/dev/{vidply.chunk-PROLQGB6.js.map → vidply.chunk-VMSQAZM7.js.map} +0 -0
  151. /package/dist/dev/{vidply.de-NCS32MR5.js.map → vidply.de-4YAT7FFM.js.map} +0 -0
  152. /package/dist/dev/{vidply.es-KTLE6NCH.js.map → vidply.es-LEWIRUBH.js.map} +0 -0
  153. /package/dist/dev/{vidply.fr-7CEEBQ77.js.map → vidply.fr-Q4GV2NOE.js.map} +0 -0
  154. /package/dist/dev/{vidply.ja-5UIYTCX7.js.map → vidply.ja-S6ITA6WU.js.map} +0 -0
package/README.md CHANGED
@@ -10,7 +10,7 @@ internationalized with support for 5 languages and complete WCAG 2.2 AA complian
10
10
  ![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue.svg)
11
11
  ![ESM](https://img.shields.io/badge/ESM-Module-yellow.svg)
12
12
  ![WCAG](https://img.shields.io/badge/WCAG-2.2%20AA-green.svg)
13
- ![Version](https://img.shields.io/badge/version-1.1.10-brightgreen.svg)
13
+ ![npm version](https://img.shields.io/npm/v/vidply.svg)
14
14
 
15
15
  ## Live Demos
16
16
 
@@ -46,11 +46,11 @@ Try VidPly in action:
46
46
  - **Vimeo Integration** - Seamless Vimeo player integration
47
47
  - **SoundCloud Integration** - Play SoundCloud tracks and sets via the Widget API with unified controls
48
48
  - **HLS Streaming** - Adaptive bitrate streaming with quality selection and dynamic subtitle detection
49
- - Uses `hls.js` on Chrome / Firefox / Edge / desktop Safari for full feature parity (quality menu, captions,
49
+ - Uses **hls.js 1.6.16** on Chrome / Firefox / Edge / desktop Safari for full feature parity (quality menu, captions,
50
50
  transcript)
51
51
  - Falls back to native HLS on iOS / iPadOS where MSE is unavailable; native text tracks are still surfaced through the
52
52
  VidPly captions and transcript UI
53
- - **DASH Streaming** - MPEG-DASH support via dash.js with adaptive quality, TTML and WebVTT subtitles
53
+ - **DASH Streaming** - MPEG-DASH support via **dash.js 5.2.0** (modern UMD) with adaptive quality, TTML and WebVTT subtitles
54
54
  - **Buffering Spinner** - Centered loading spinner shown automatically while media is buffering (HTML5, HLS, DASH)
55
55
  - **Download Button** - Optional download control with custom URL support (`downloadButton` + `downloadUrl`)
56
56
  - **Preview Thumbnails** - Video preview thumbnails on progress bar hover
@@ -68,15 +68,22 @@ Try VidPly in action:
68
68
  ### Accessibility Features (WCAG 2.2 AA Compliant)
69
69
 
70
70
  - **Full Keyboard Navigation** - All features accessible via keyboard, custom shortcuts, menu navigation with Arrow keys
71
- - **Screen Reader Support** - Complete ARIA labels (`aria-controls`, `aria-expanded`, `aria-haspopup`), live regions
72
- - **Interactive Transcripts** - Click-to-seek and auto-scroll with proper semantic HTML
71
+ (non-interactive/disabled menu items are excluded from roving focus)
72
+ - **Screen Reader Support** - Complete ARIA labels (`aria-controls`, `aria-expanded`, `aria-haspopup`), live regions;
73
+ single-select menus (speed/quality/captions) use `role="menuitemradio"` + `aria-checked`, and both mouse- and
74
+ keyboard-driven play/pause, caption and volume changes are announced
75
+ - **Interactive Transcripts** - Click-to-seek and auto-scroll with proper semantic HTML and a focus trap while open
73
76
  - **Sign Language Overlay** - Picture-in-picture with drag/resize, keyboard accessible
74
77
  - **Audio Description** - Alternate audio track with visual content descriptions
75
78
  - **Caption Styling** - Fully customizable (font, size, color, opacity, edge style)
76
79
  - **High Contrast Mode** - Windows HCM support, color-independent design
77
- - **Focus Management** - Logical focus order, programmatic focus handling, visible indicators
78
- - **Touch Accessibility** - Buttons sized at or above the WCAG 2.2 AA 24×24 CSS-pixel minimum (SC 2.5.8); swipeable
79
- interfaces
80
+ - **Reduced Motion** - Smooth scrolling and animations honor `prefers-reduced-motion` (SC 2.3.3)
81
+ - **Zoom & Reflow** - Pinch-zoom is preserved even in (pseudo-)fullscreen; no fixed `maximum-scale` lock (SC 1.4.4 /
82
+ 1.4.10)
83
+ - **Focus Management** - Logical focus order, programmatic focus handling, visible indicators, focus traps in the
84
+ transcript window and settings dialog
85
+ - **Touch Accessibility** - Buttons, resize handles and slider thumbs sized at or above the WCAG 2.2 AA 24×24
86
+ CSS-pixel minimum (SC 2.5.8); swipeable interfaces
80
87
 
81
88
  ### Captions & Subtitles
82
89
 
@@ -1,13 +1,14 @@
1
1
  /*!
2
- * VidPly v1.1.18 - Universal, Accessible Video Player
2
+ * VidPly v1.2.0 - 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
  CaptionManager
8
- } from "./vidply.chunk-I3Y5BLC6.js";
9
- import "./vidply.chunk-ZR5JQPCM.js";
10
- import "./vidply.chunk-QGWFSJ7R.js";
8
+ } from "./vidply.chunk-64WXMTGL.js";
9
+ import "./vidply.chunk-BNFIUL2Z.js";
10
+ import "./vidply.chunk-6RQA4ROD.js";
11
+ import "./vidply.chunk-RZWZGDV7.js";
11
12
 
12
13
  // src/core/AudioDescriptionManager.ts
13
14
  var AudioDescriptionManager = class {
@@ -261,7 +262,7 @@ var AudioDescriptionManager = class {
261
262
  });
262
263
  this.player.element.load();
263
264
  await new Promise((resolve) => {
264
- setTimeout(() => {
265
+ this.player.setManagedTimeout(() => {
265
266
  tracksToReadd.forEach(({ trackInfo, parent, nextSibling, attributes }) => {
266
267
  const newSrc = toDescribed ? trackInfo.describedSrc : trackInfo.originalSrc;
267
268
  if (!newSrc) {
@@ -271,8 +272,9 @@ var AudioDescriptionManager = class {
271
272
  const newTrackElement = document.createElement("track");
272
273
  newTrackElement.setAttribute("src", newSrc);
273
274
  Object.keys(attributes).forEach((attrName) => {
274
- if (attrName !== "src" && attrName !== "data-desc-src") {
275
- newTrackElement.setAttribute(attrName, attributes[attrName]);
275
+ const attrValue = attributes[attrName];
276
+ if (attrName !== "src" && attrName !== "data-desc-src" && attrValue !== void 0) {
277
+ newTrackElement.setAttribute(attrName, attrValue);
276
278
  }
277
279
  });
278
280
  const targetParent = parent || this.player.element;
@@ -310,10 +312,10 @@ var AudioDescriptionManager = class {
310
312
  }, 300);
311
313
  };
312
314
  if (this.player.element.readyState >= 1) {
313
- setTimeout(setupNewTracks, 200);
315
+ this.player.setManagedTimeout(setupNewTracks, 200);
314
316
  } else {
315
317
  this.player.element.addEventListener("loadedmetadata", setupNewTracks, { once: true });
316
- setTimeout(setupNewTracks, 2e3);
318
+ this.player.setManagedTimeout(setupNewTracks, 2e3);
317
319
  }
318
320
  resolve();
319
321
  }, 100);
@@ -589,4 +591,4 @@ var AudioDescriptionManager = class {
589
591
  export {
590
592
  AudioDescriptionManager
591
593
  };
592
- //# sourceMappingURL=vidply.AudioDescriptionManager-GJO454ID.js.map
594
+ //# sourceMappingURL=vidply.AudioDescriptionManager-R47Q4YR4.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/core/AudioDescriptionManager.ts"],
4
+ "sourcesContent": ["/**\r\n * Audio Description Manager\r\n * Handles audio-described video source switching and caption track swapping\r\n */\r\n\r\nimport { CaptionManager } from '../controls/CaptionManager.js';\r\nimport type { Player } from './Player.js';\r\n\r\n/**\r\n * Caption-track entry tracked by the audio-description manager. Each\r\n * entry pairs a `<track>` element with its described/original source\r\n * URLs so the manager can swap the rendered captions when the user\r\n * toggles audio description.\r\n */\r\ninterface CaptionTrackInfo {\r\n trackElement: HTMLTrackElement;\r\n originalSrc: string | null;\r\n describedSrc: string;\r\n originalTrackSrc: string | null;\r\n explicit: boolean;\r\n}\r\n\r\nexport class AudioDescriptionManager {\r\n player: Player;\r\n captionTracks: CaptionTrackInfo[];\r\n desiredState: boolean;\r\n enabled: boolean;\r\n originalSource: string | null;\r\n sourceElement: Element | null;\r\n src: string | null;\r\n\r\n constructor(player: Player) {\r\n this.player = player;\r\n \r\n // State\r\n this.enabled = false;\r\n this.desiredState = false;\r\n \r\n // Sources\r\n this.src = player.options.audioDescriptionSrc;\r\n this.sourceElement = null;\r\n this.originalSource = null;\r\n this.captionTracks = [];\r\n }\r\n\r\n /**\r\n * Initialize audio description from source elements\r\n * Called during player initialization\r\n */\r\n initFromSourceElements(sourceElements: Element[], trackElements: Element[]) {\r\n // Check for source elements with audio description attributes\r\n for (const sourceEl of sourceElements) {\r\n const descSrc = sourceEl.getAttribute('data-desc-src');\r\n const origSrc = sourceEl.getAttribute('data-orig-src');\r\n \r\n if (descSrc || origSrc) {\r\n if (!this.sourceElement) {\r\n this.sourceElement = sourceEl;\r\n }\r\n \r\n if (origSrc) {\r\n if (!this.originalSource) {\r\n this.originalSource = origSrc;\r\n }\r\n if (!this.player.originalSrc) {\r\n this.player.originalSrc = origSrc;\r\n }\r\n } else {\r\n const currentSrcAttr = sourceEl.getAttribute('src');\r\n if (!this.originalSource && currentSrcAttr) {\r\n this.originalSource = currentSrcAttr;\r\n }\r\n if (!this.player.originalSrc && currentSrcAttr) {\r\n this.player.originalSrc = currentSrcAttr;\r\n }\r\n }\r\n \r\n if (descSrc && !this.src) {\r\n this.src = descSrc;\r\n }\r\n }\r\n }\r\n\r\n // Check for text tracks with audio description versions\r\n trackElements.forEach((trackEl: Element) => {\r\n const trackKind = trackEl.getAttribute('kind');\r\n const trackDescSrc = trackEl.getAttribute('data-desc-src');\r\n\r\n if ((trackKind === 'captions' || trackKind === 'subtitles' ||\r\n trackKind === 'chapters' || trackKind === 'descriptions') &&\r\n trackDescSrc && trackEl instanceof HTMLTrackElement) {\r\n this.captionTracks.push({\r\n trackElement: trackEl,\r\n originalSrc: trackEl.getAttribute('src'),\r\n describedSrc: trackDescSrc,\r\n originalTrackSrc: trackEl.getAttribute('data-orig-src') || trackEl.getAttribute('src'),\r\n explicit: true\r\n });\r\n this.player.log(`Found explicit described ${trackKind} track: ${trackEl.getAttribute('src')} -> ${trackDescSrc}`);\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * Check if audio description is available\r\n */\r\n isAvailable() {\r\n const hasSourceElementsWithDesc = this.player.sourceElements.some(\r\n (el: Element) => el.getAttribute('data-desc-src')\r\n );\r\n return Boolean(this.src || hasSourceElementsWithDesc || this.captionTracks.length > 0);\r\n }\r\n\r\n /**\r\n * Enable audio description\r\n */\r\n async enable() {\r\n const hasSourceElementsWithDesc = this.player.sourceElements.some(\r\n (el: Element) => el.getAttribute('data-desc-src')\r\n );\r\n const hasTracksWithDesc = this.captionTracks.length > 0;\r\n \r\n if (!this.src && !hasSourceElementsWithDesc && !hasTracksWithDesc) {\r\n console.warn('VidPly: No audio description source, source elements, or tracks provided');\r\n return;\r\n }\r\n\r\n this.desiredState = true;\r\n\r\n // Store current state for restoration\r\n const currentTime = this.player.state.currentTime;\r\n const wasPlaying = this.player.state.playing;\r\n const posterValue = (this.player.element as HTMLVideoElement).poster || \r\n this.player.element.getAttribute('poster') || \r\n this.player.options.poster;\r\n const shouldKeepPoster = currentTime < 0.1 && !wasPlaying;\r\n\r\n // Get current caption text for synchronization\r\n const currentCaptionText = this._getCurrentCaptionText();\r\n\r\n // Switch to audio-described version based on what's available\r\n if (this.sourceElement) {\r\n // Use source element approach (data-desc-src on <source> elements)\r\n await this._enableWithSourceElement(currentTime, wasPlaying, posterValue, shouldKeepPoster, currentCaptionText);\r\n } else if (this.src) {\r\n // Use direct src approach (audioDescriptionSrc option)\r\n await this._enableWithDirectSrc(currentTime, wasPlaying, posterValue, shouldKeepPoster);\r\n } else if (hasTracksWithDesc) {\r\n // Only caption tracks with descriptions - swap tracks without changing video source\r\n await this._swapCaptionTracks(true);\r\n this.enabled = true;\r\n this.player.emit('audiodescriptionenabled');\r\n }\r\n // If none of the above, we already returned at the top check\r\n }\r\n\r\n /**\r\n * Disable audio description\r\n */\r\n async disable() {\r\n this.desiredState = false;\r\n\r\n // If we only had caption tracks (no video source swap), just swap tracks back\r\n const hasTracksWithDesc = this.captionTracks.length > 0;\r\n if (!this.sourceElement && !this.src && hasTracksWithDesc) {\r\n await this._swapCaptionTracks(false);\r\n this.enabled = false;\r\n this.player.emit('audiodescriptiondisabled');\r\n return;\r\n }\r\n\r\n if (!this.player.originalSrc) {\r\n return;\r\n }\r\n\r\n // Store current state\r\n const currentTime = this.player.state.currentTime;\r\n const wasPlaying = this.player.state.playing;\r\n const posterValue = (this.player.element as HTMLVideoElement).poster || \r\n this.player.element.getAttribute('poster') || \r\n this.player.options.poster;\r\n const shouldKeepPoster = currentTime < 0.1 && !wasPlaying;\r\n\r\n // Get current caption for sync\r\n const currentCaptionText = this._getCurrentCaptionText();\r\n\r\n if (this.sourceElement) {\r\n await this._disableWithSourceElement(currentTime, wasPlaying, posterValue, shouldKeepPoster, currentCaptionText);\r\n } else if (this.src) {\r\n await this._disableWithDirectSrc(currentTime, wasPlaying, posterValue);\r\n }\r\n }\r\n\r\n /**\r\n * Toggle audio description\r\n */\r\n async toggle() {\r\n const descriptionTrack = this.player.findTextTrack('descriptions');\r\n const hasAudioDescriptionSrc = this.isAvailable();\r\n \r\n if (descriptionTrack && !hasAudioDescriptionSrc) {\r\n // Toggle description track playback\r\n if (descriptionTrack.mode === 'showing') {\r\n descriptionTrack.mode = 'hidden';\r\n this.enabled = false;\r\n this.player.emit('audiodescriptiondisabled');\r\n } else {\r\n descriptionTrack.mode = 'showing';\r\n this.enabled = true;\r\n this.player.emit('audiodescriptionenabled');\r\n }\r\n } else if (descriptionTrack && hasAudioDescriptionSrc) {\r\n // Toggle both\r\n if (this.enabled) {\r\n this.desiredState = false;\r\n await this.disable();\r\n } else {\r\n descriptionTrack.mode = 'showing';\r\n this.desiredState = true;\r\n await this.enable();\r\n }\r\n } else if (hasAudioDescriptionSrc) {\r\n // Toggle source\r\n if (this.enabled) {\r\n this.desiredState = false;\r\n await this.disable();\r\n } else {\r\n this.desiredState = true;\r\n await this.enable();\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Get current caption text for synchronization\r\n */\r\n _getCurrentCaptionText() {\r\n if (this.player.captionManager && \r\n this.player.captionManager.currentTrack && \r\n this.player.captionManager.currentCue) {\r\n return this.player.captionManager.currentCue.text;\r\n }\r\n return null;\r\n }\r\n\r\n /**\r\n * Validate that a track URL exists. Bounded by the player's lifecycle\r\n * AbortController + an 8s timeout so a torn-down player cannot leak\r\n * the request.\r\n */\r\n async _validateTrackExists(url: string): Promise<boolean> {\r\n if (typeof url !== 'string' || !url) return false;\r\n const signals: AbortSignal[] = [];\r\n const lifecycle = (this.player as { lifecycleSignal?: AbortSignal }).lifecycleSignal;\r\n if (lifecycle) signals.push(lifecycle);\r\n if (typeof AbortSignal !== 'undefined' && typeof AbortSignal.timeout === 'function') {\r\n signals.push(AbortSignal.timeout(8000));\r\n }\r\n let signal: AbortSignal | undefined;\r\n if (signals.length === 1) signal = signals[0];\r\n else if (signals.length > 1) {\r\n const anyFn = (AbortSignal as { any?: (s: AbortSignal[]) => AbortSignal }).any;\r\n signal = anyFn ? anyFn(signals) : signals[0];\r\n }\r\n try {\r\n const response = await fetch(url, { method: 'HEAD', signal });\r\n return response.ok;\r\n } catch {\r\n return false;\r\n }\r\n }\r\n\r\n /**\r\n * Swap caption tracks to described versions\r\n */\r\n async _swapCaptionTracks(toDescribed = true) {\r\n if (this.captionTracks.length === 0) return [];\r\n\r\n const swappedTracks: CaptionTrackInfo[] = [];\r\n \r\n const validationPromises = this.captionTracks.map(async (trackInfo) => {\r\n if (trackInfo.trackElement && trackInfo.describedSrc) {\r\n if (trackInfo.explicit === true) {\r\n const url = toDescribed ? trackInfo.describedSrc : trackInfo.originalSrc;\r\n if (!url) return { trackInfo, exists: false };\r\n try {\r\n const exists = await this._validateTrackExists(url);\r\n return { trackInfo, exists };\r\n } catch {\r\n return { trackInfo, exists: false };\r\n }\r\n }\r\n }\r\n return { trackInfo, exists: false };\r\n });\r\n \r\n const validationResults = await Promise.all(validationPromises);\r\n const tracksToSwap = validationResults.filter(result => result.exists);\r\n \r\n if (tracksToSwap.length > 0) {\r\n // Store track modes before removing\r\n const trackModes = new Map();\r\n tracksToSwap.forEach(({ trackInfo }) => {\r\n const textTrack = trackInfo.trackElement.track;\r\n if (textTrack) {\r\n trackModes.set(trackInfo, {\r\n wasShowing: textTrack.mode === 'showing',\r\n wasHidden: textTrack.mode === 'hidden'\r\n });\r\n } else {\r\n trackModes.set(trackInfo, { wasShowing: false, wasHidden: false });\r\n }\r\n });\r\n \r\n // Store track info and remove\r\n const tracksToReadd = tracksToSwap.map(({ trackInfo }) => {\r\n const attributes: Record<string, string> = {};\r\n Array.from(trackInfo.trackElement.attributes).forEach((attr: Attr) => {\r\n attributes[attr.name] = attr.value;\r\n });\r\n \r\n const result = {\r\n trackInfo,\r\n oldSrc: trackInfo.trackElement.getAttribute('src'),\r\n parent: trackInfo.trackElement.parentNode,\r\n nextSibling: trackInfo.trackElement.nextSibling,\r\n attributes\r\n };\r\n \r\n trackInfo.trackElement.remove();\r\n return result;\r\n });\r\n \r\n // Force browser to process removal\r\n this.player.element.load();\r\n \r\n // Re-add tracks with new src. Use a managed timeout so the swap\r\n // is cancelled (and won't mutate a torn-down player) on destroy.\r\n await new Promise<void>(resolve => {\r\n this.player.setManagedTimeout(() => {\r\n tracksToReadd.forEach(({ trackInfo, parent, nextSibling, attributes }) => {\r\n const newSrc = toDescribed ? trackInfo.describedSrc : trackInfo.originalSrc;\r\n if (!newSrc) {\r\n // Skip tracks without a usable src; no swap can occur.\r\n return;\r\n }\r\n swappedTracks.push(trackInfo);\r\n\r\n const newTrackElement = document.createElement('track');\r\n newTrackElement.setAttribute('src', newSrc);\r\n \r\n Object.keys(attributes).forEach(attrName => {\r\n const attrValue = attributes[attrName];\r\n if (attrName !== 'src' && attrName !== 'data-desc-src' && attrValue !== undefined) {\r\n newTrackElement.setAttribute(attrName, attrValue);\r\n }\r\n });\r\n \r\n // Use the player's video element as parent if the original parent is null\r\n const targetParent = parent || this.player.element;\r\n \r\n if (nextSibling && nextSibling.parentNode) {\r\n targetParent.insertBefore(newTrackElement, nextSibling);\r\n } else {\r\n targetParent.appendChild(newTrackElement);\r\n }\r\n \r\n trackInfo.trackElement = newTrackElement;\r\n });\r\n \r\n this.player.invalidateTrackCache();\r\n \r\n // Setup new tracks\r\n const setupNewTracks = () => {\r\n this.player.setManagedTimeout(() => {\r\n swappedTracks.forEach((trackInfo) => {\r\n const trackElement = trackInfo.trackElement;\r\n const newTextTrack = trackElement.track;\r\n if (newTextTrack) {\r\n const modeInfo = trackModes.get(trackInfo) || { wasShowing: false, wasHidden: false };\r\n newTextTrack.mode = 'hidden';\r\n\r\n const restoreMode = () => {\r\n if (modeInfo.wasShowing || modeInfo.wasHidden) {\r\n newTextTrack.mode = 'hidden';\r\n } else {\r\n newTextTrack.mode = 'disabled';\r\n }\r\n };\r\n\r\n // `readyState` and the `load`/`error`\r\n // events live on `<track>`, not on\r\n // the underlying TextTrack object.\r\n if (trackElement.readyState >= 2) {\r\n restoreMode();\r\n } else {\r\n trackElement.addEventListener('load', restoreMode, { once: true });\r\n trackElement.addEventListener('error', restoreMode, { once: true });\r\n }\r\n }\r\n });\r\n }, 300);\r\n };\r\n \r\n if (this.player.element.readyState >= 1) {\r\n this.player.setManagedTimeout(setupNewTracks, 200);\r\n } else {\r\n this.player.element.addEventListener('loadedmetadata', setupNewTracks, { once: true });\r\n this.player.setManagedTimeout(setupNewTracks, 2000);\r\n }\r\n \r\n resolve();\r\n }, 100);\r\n });\r\n }\r\n \r\n return swappedTracks;\r\n }\r\n\r\n /**\r\n * Update source elements to described versions\r\n */\r\n _updateSourceElements(toDescribed = true) {\r\n const sourceElements = this.player.sourceElements;\r\n const sourcesToUpdate: Array<{ src: string | null; type: string | null; origSrc: string | null; descSrc: string | null }> = [];\r\n \r\n sourceElements.forEach((sourceEl: Element) => {\r\n const descSrcAttr = sourceEl.getAttribute('data-desc-src');\r\n const currentSrc = sourceEl.getAttribute('src');\r\n \r\n if (descSrcAttr) {\r\n const type = sourceEl.getAttribute('type');\r\n const origSrc = sourceEl.getAttribute('data-orig-src') || currentSrc;\r\n \r\n sourcesToUpdate.push({\r\n src: toDescribed ? descSrcAttr : origSrc,\r\n type,\r\n origSrc,\r\n descSrc: descSrcAttr\r\n });\r\n } else {\r\n sourcesToUpdate.push({\r\n src: sourceEl.getAttribute('src'),\r\n type: sourceEl.getAttribute('type'),\r\n origSrc: null,\r\n descSrc: null\r\n });\r\n }\r\n });\r\n \r\n // Remove src attribute if present\r\n if (this.player.element.hasAttribute('src')) {\r\n this.player.element.removeAttribute('src');\r\n }\r\n \r\n // Remove all source elements\r\n sourceElements.forEach((sourceEl: Element) => sourceEl.remove());\r\n \r\n // Re-add with updated src\r\n sourcesToUpdate.forEach((sourceInfo) => {\r\n if (!sourceInfo.src) {\r\n return;\r\n }\r\n const newSource = document.createElement('source');\r\n newSource.setAttribute('src', sourceInfo.src);\r\n if (sourceInfo.type) {\r\n newSource.setAttribute('type', sourceInfo.type);\r\n }\r\n if (sourceInfo.origSrc) {\r\n newSource.setAttribute('data-orig-src', sourceInfo.origSrc);\r\n }\r\n if (sourceInfo.descSrc) {\r\n newSource.setAttribute('data-desc-src', sourceInfo.descSrc);\r\n }\r\n \r\n const firstTrack = this.player.element.querySelector('track');\r\n if (firstTrack) {\r\n this.player.element.insertBefore(newSource, firstTrack);\r\n } else {\r\n this.player.element.appendChild(newSource);\r\n }\r\n });\r\n \r\n this.player._sourceElementsDirty = true;\r\n this.player._sourceElementsCache = null;\r\n }\r\n\r\n /**\r\n * Wait for media to be ready\r\n */\r\n async _waitForMediaReady(needSeek = false) {\r\n // Wait for metadata\r\n await new Promise<void>((resolve) => {\r\n if (this.player.element.readyState >= 1) {\r\n resolve();\r\n } else {\r\n const onLoad = () => {\r\n this.player.element.removeEventListener('loadedmetadata', onLoad);\r\n resolve();\r\n };\r\n this.player.element.addEventListener('loadedmetadata', onLoad);\r\n }\r\n });\r\n \r\n // Wait for tracks\r\n await new Promise(resolve => setTimeout(resolve, 300));\r\n \r\n // Wait for playback if needed\r\n if (needSeek) {\r\n await new Promise<void>((resolve) => {\r\n if (this.player.element.readyState >= 3) {\r\n resolve();\r\n } else {\r\n const onCanPlay = () => {\r\n this.player.element.removeEventListener('canplay', onCanPlay);\r\n this.player.element.removeEventListener('canplaythrough', onCanPlay);\r\n resolve();\r\n };\r\n this.player.element.addEventListener('canplay', onCanPlay, { once: true });\r\n this.player.element.addEventListener('canplaythrough', onCanPlay, { once: true });\r\n setTimeout(() => {\r\n this.player.element.removeEventListener('canplay', onCanPlay);\r\n this.player.element.removeEventListener('canplaythrough', onCanPlay);\r\n resolve();\r\n }, 3000);\r\n }\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Restore playback state after source change\r\n */\r\n async _restorePlaybackState(currentTime: number, wasPlaying: boolean, shouldKeepPoster: boolean, currentCaptionText: string | null) {\r\n // Try to find matching caption for sync\r\n let syncTime = currentTime;\r\n if (currentCaptionText && this.player.captionManager && this.player.captionManager.tracks.length > 0) {\r\n await new Promise(resolve => setTimeout(resolve, 500));\r\n const matchingTime = this.player.findMatchingCaptionTime(\r\n currentCaptionText, \r\n this.player.captionManager.tracks\r\n );\r\n if (matchingTime !== null) {\r\n syncTime = matchingTime;\r\n if (this.player.options.debug) {\r\n this.player.log(`[VidPly] Syncing via caption: ${currentTime}s -> ${syncTime}s`);\r\n }\r\n }\r\n }\r\n \r\n // Seek\r\n if (syncTime > 0) {\r\n this.player.seek(syncTime);\r\n await new Promise(resolve => setTimeout(resolve, 100));\r\n }\r\n \r\n // Play/pause\r\n if (wasPlaying) {\r\n await this.player.play();\r\n this.player.setManagedTimeout(() => {\r\n this.player.hidePosterOverlay();\r\n }, 100);\r\n } else {\r\n this.player.pause();\r\n if (!shouldKeepPoster) {\r\n this.player.hidePosterOverlay();\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Enable with source element method\r\n */\r\n async _enableWithSourceElement(currentTime: number, wasPlaying: boolean, posterValue: string | null, shouldKeepPoster: boolean, currentCaptionText: string | null) {\r\n // Swap caption tracks\r\n await this._swapCaptionTracks(true);\r\n \r\n // Update source elements\r\n this._updateSourceElements(true);\r\n \r\n // Preserve poster\r\n if (posterValue && this.player.element.tagName === 'VIDEO') {\r\n (this.player.element as HTMLVideoElement).poster = posterValue;\r\n }\r\n \r\n // Reload\r\n this.player.element.load();\r\n \r\n // Wait for ready\r\n await this._waitForMediaReady(currentTime > 0 || wasPlaying);\r\n \r\n // Restore playback\r\n await this._restorePlaybackState(currentTime, wasPlaying, shouldKeepPoster, currentCaptionText);\r\n \r\n // Update state\r\n if (!this.desiredState) return;\r\n this.enabled = true;\r\n this.player.state.audioDescriptionEnabled = true;\r\n this.player.emit('audiodescriptionenabled');\r\n \r\n // Reload transcript if visible\r\n this._reloadTranscript();\r\n }\r\n\r\n /**\r\n * Enable with direct src method\r\n */\r\n async _enableWithDirectSrc(currentTime: number, wasPlaying: boolean, posterValue: string | null, shouldKeepPoster: boolean) {\r\n // Swap caption tracks\r\n await this._swapCaptionTracks(true);\r\n \r\n // Set poster\r\n if (posterValue && this.player.element.tagName === 'VIDEO') {\r\n (this.player.element as HTMLVideoElement).poster = posterValue;\r\n }\r\n \r\n // Set src (this method should only be called when this.src exists)\r\n if (!this.src) return;\r\n this.player.element.src = this.src;\r\n \r\n // Wait and restore\r\n await this._waitForMediaReady(currentTime > 0 || wasPlaying);\r\n \r\n if (currentTime > 0) {\r\n this.player.seek(currentTime);\r\n await new Promise(resolve => setTimeout(resolve, 100));\r\n }\r\n \r\n if (wasPlaying) {\r\n await this.player.play();\r\n } else {\r\n this.player.pause();\r\n if (!shouldKeepPoster) {\r\n this.player.hidePosterOverlay();\r\n }\r\n }\r\n \r\n if (!this.desiredState) return;\r\n this.enabled = true;\r\n this.player.state.audioDescriptionEnabled = true;\r\n this.player.emit('audiodescriptionenabled');\r\n \r\n // Reload transcript if visible\r\n this._reloadTranscript();\r\n }\r\n\r\n /**\r\n * Disable with source element method\r\n */\r\n async _disableWithSourceElement(currentTime: number, wasPlaying: boolean, posterValue: string | null, shouldKeepPoster: boolean, currentCaptionText: string | null) {\r\n // Swap caption tracks back\r\n await this._swapCaptionTracks(false);\r\n \r\n // Update source elements\r\n this._updateSourceElements(false);\r\n \r\n // Preserve poster\r\n if (posterValue && this.player.element.tagName === 'VIDEO') {\r\n (this.player.element as HTMLVideoElement).poster = posterValue;\r\n }\r\n \r\n // Reload\r\n this.player.element.load();\r\n this.player.invalidateTrackCache();\r\n \r\n // Wait for ready\r\n await this._waitForMediaReady(currentTime > 0 || wasPlaying);\r\n \r\n // Restore playback\r\n await this._restorePlaybackState(currentTime, wasPlaying, shouldKeepPoster, currentCaptionText);\r\n \r\n // Reinitialize caption manager\r\n if (this.player.captionManager) {\r\n this.player.captionManager.destroy();\r\n this.player.captionManager = new CaptionManager(this.player);\r\n }\r\n \r\n // Update state\r\n if (this.desiredState) return;\r\n this.enabled = false;\r\n this.player.state.audioDescriptionEnabled = false;\r\n this.player.emit('audiodescriptiondisabled');\r\n \r\n // Reload transcript if visible\r\n this._reloadTranscript();\r\n }\r\n\r\n /**\r\n * Disable with direct src method\r\n */\r\n async _disableWithDirectSrc(currentTime: number, wasPlaying: boolean, posterValue: string | null) {\r\n // Swap caption tracks back\r\n await this._swapCaptionTracks(false);\r\n \r\n // Set poster\r\n if (posterValue && this.player.element.tagName === 'VIDEO') {\r\n (this.player.element as HTMLVideoElement).poster = posterValue;\r\n }\r\n \r\n // Restore original src\r\n const originalSrcToUse = this.originalSource || this.player.originalSrc;\r\n if (!originalSrcToUse) {\r\n return;\r\n }\r\n this.player.element.src = originalSrcToUse;\r\n this.player.element.load();\r\n \r\n // Wait and restore\r\n await this._waitForMediaReady(currentTime > 0 || wasPlaying);\r\n \r\n if (currentTime > 0) {\r\n this.player.seek(currentTime);\r\n }\r\n \r\n if (wasPlaying) {\r\n await this.player.play();\r\n }\r\n \r\n if (this.desiredState) return;\r\n this.enabled = false;\r\n this.player.state.audioDescriptionEnabled = false;\r\n this.player.emit('audiodescriptiondisabled');\r\n \r\n // Reload transcript if visible\r\n this._reloadTranscript();\r\n }\r\n\r\n /**\r\n * Reload transcript after audio description state change\r\n */\r\n _reloadTranscript() {\r\n if (this.player.transcriptManager && this.player.transcriptManager.isVisible) {\r\n // Wait for tracks to load after source swap\r\n this.player.setManagedTimeout(() => {\r\n if (this.player.transcriptManager && this.player.transcriptManager.loadTranscriptData) {\r\n this.player.transcriptManager.loadTranscriptData();\r\n }\r\n }, 800);\r\n }\r\n }\r\n\r\n /**\r\n * Update sources (called when playlist changes)\r\n */\r\n updateSources(audioDescriptionSrc: string | null | undefined) {\r\n this.src = audioDescriptionSrc || null;\r\n // Reset state for new playlist item\r\n this.enabled = false;\r\n this.desiredState = false;\r\n this.sourceElement = null;\r\n this.originalSource = null;\r\n this.captionTracks = [];\r\n }\r\n \r\n /**\r\n * Reinitialize from current player elements (called after playlist loads new tracks)\r\n */\r\n reinitialize() {\r\n this.player.invalidateTrackCache();\r\n this.initFromSourceElements(this.player.sourceElements, this.player.trackElements);\r\n }\r\n\r\n /**\r\n * Cleanup\r\n */\r\n destroy() {\r\n this.enabled = false;\r\n this.desiredState = false;\r\n this.captionTracks = [];\r\n this.sourceElement = null;\r\n this.originalSource = null;\r\n }\r\n}\r\n\r\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,300 @@
1
+ /*!
2
+ * VidPly v1.2.0 - Universal, Accessible Video Player
3
+ * (c) 2026 Matthias Peltzer
4
+ * Released under GPL-2.0-or-later License
5
+ */
6
+ import {
7
+ focusFirstElement
8
+ } from "./vidply.chunk-VMSQAZM7.js";
9
+ import {
10
+ DOMUtils,
11
+ i18n
12
+ } from "./vidply.chunk-RZWZGDV7.js";
13
+
14
+ // src/controls/CaptionStyleMenu.ts
15
+ function captionStylePropertyName(property) {
16
+ const stripped = property.replace("captions", "");
17
+ return stripped.charAt(0).toLowerCase() + stripped.slice(1);
18
+ }
19
+ function createStyleControl(player, label, property, options) {
20
+ const group = DOMUtils.createElement("div", {
21
+ className: `${player.options.classPrefix}-style-group`
22
+ });
23
+ const controlId = `${player.options.classPrefix}-${property}-${Date.now()}`;
24
+ const labelEl = DOMUtils.createElement("label", {
25
+ textContent: label,
26
+ attributes: {
27
+ "for": controlId
28
+ },
29
+ style: {
30
+ display: "block",
31
+ fontSize: "12px",
32
+ marginBottom: "4px",
33
+ color: "rgba(255,255,255,0.7)"
34
+ }
35
+ });
36
+ group.appendChild(labelEl);
37
+ const select = DOMUtils.createElement("select", {
38
+ className: `${player.options.classPrefix}-style-select`,
39
+ attributes: {
40
+ "id": controlId
41
+ },
42
+ style: {
43
+ width: "100%",
44
+ padding: "6px",
45
+ background: "var(--vidply-white)",
46
+ border: "1px solid var(--vidply-white-10)",
47
+ borderRadius: "4px",
48
+ color: "var(--vidply-black)",
49
+ fontSize: "13px"
50
+ }
51
+ });
52
+ const currentValue = player.options[property];
53
+ options.forEach((opt) => {
54
+ const option = DOMUtils.createElement("option", {
55
+ textContent: opt.label,
56
+ attributes: { value: opt.value }
57
+ });
58
+ if (opt.value === currentValue) {
59
+ option.selected = true;
60
+ }
61
+ select.appendChild(option);
62
+ });
63
+ select.addEventListener("mousedown", (e) => {
64
+ e.stopPropagation();
65
+ });
66
+ select.addEventListener("click", (e) => {
67
+ e.stopPropagation();
68
+ });
69
+ select.addEventListener("change", (e) => {
70
+ e.stopPropagation();
71
+ player.options[property] = e.target.value;
72
+ if (player.captionManager) {
73
+ player.captionManager.setCaptionStyle(
74
+ captionStylePropertyName(property),
75
+ e.target.value
76
+ );
77
+ }
78
+ });
79
+ group.appendChild(select);
80
+ return group;
81
+ }
82
+ function createColorControl(player, label, property) {
83
+ const group = DOMUtils.createElement("div", {
84
+ className: `${player.options.classPrefix}-style-group`
85
+ });
86
+ const controlId = `${player.options.classPrefix}-${property}-${Date.now()}`;
87
+ const labelEl = DOMUtils.createElement("label", {
88
+ textContent: label,
89
+ attributes: {
90
+ "for": controlId
91
+ },
92
+ style: {
93
+ display: "block",
94
+ fontSize: "12px",
95
+ marginBottom: "4px",
96
+ color: "rgba(255,255,255,0.7)"
97
+ }
98
+ });
99
+ group.appendChild(labelEl);
100
+ const input = DOMUtils.createElement("input", {
101
+ attributes: {
102
+ "id": controlId,
103
+ type: "color",
104
+ value: String(player.options[property] ?? "")
105
+ },
106
+ style: {
107
+ width: "100%",
108
+ height: "32px",
109
+ padding: "2px",
110
+ background: "rgba(255,255,255,0.1)",
111
+ border: "1px solid rgba(255,255,255,0.2)",
112
+ borderRadius: "4px",
113
+ cursor: "pointer"
114
+ }
115
+ });
116
+ input.addEventListener("mousedown", (e) => {
117
+ e.stopPropagation();
118
+ });
119
+ input.addEventListener("click", (e) => {
120
+ e.stopPropagation();
121
+ });
122
+ input.addEventListener("change", (e) => {
123
+ e.stopPropagation();
124
+ player.options[property] = e.target.value;
125
+ if (player.captionManager) {
126
+ player.captionManager.setCaptionStyle(
127
+ captionStylePropertyName(property),
128
+ e.target.value
129
+ );
130
+ }
131
+ });
132
+ group.appendChild(input);
133
+ return group;
134
+ }
135
+ function createOpacityControl(player, label, property) {
136
+ const group = DOMUtils.createElement("div", {
137
+ className: `${player.options.classPrefix}-style-group`
138
+ });
139
+ const controlId = `${player.options.classPrefix}-${property}-${Date.now()}`;
140
+ const labelContainer = DOMUtils.createElement("div", {
141
+ style: {
142
+ display: "flex",
143
+ justifyContent: "space-between",
144
+ marginBottom: "4px"
145
+ }
146
+ });
147
+ const labelEl = DOMUtils.createElement("label", {
148
+ textContent: label,
149
+ attributes: {
150
+ "for": controlId
151
+ },
152
+ style: {
153
+ fontSize: "12px",
154
+ color: "rgba(255,255,255,0.7)"
155
+ }
156
+ });
157
+ const valueEl = DOMUtils.createElement("span", {
158
+ textContent: Math.round(Number(player.options[property] ?? 0) * 100) + "%",
159
+ style: {
160
+ fontSize: "12px",
161
+ color: "rgba(255,255,255,0.7)"
162
+ }
163
+ });
164
+ labelContainer.appendChild(labelEl);
165
+ labelContainer.appendChild(valueEl);
166
+ group.appendChild(labelContainer);
167
+ const input = DOMUtils.createElement("input", {
168
+ attributes: {
169
+ "id": controlId,
170
+ type: "range",
171
+ min: "0",
172
+ max: "1",
173
+ step: "0.1",
174
+ value: String(player.options[property])
175
+ },
176
+ style: {
177
+ width: "100%",
178
+ cursor: "pointer"
179
+ }
180
+ });
181
+ input.addEventListener("mousedown", (e) => {
182
+ e.stopPropagation();
183
+ });
184
+ input.addEventListener("click", (e) => {
185
+ e.stopPropagation();
186
+ });
187
+ input.addEventListener("input", (e) => {
188
+ e.stopPropagation();
189
+ const value = parseFloat(e.target.value);
190
+ valueEl.textContent = Math.round(value * 100) + "%";
191
+ player.options[property] = value;
192
+ if (player.captionManager) {
193
+ player.captionManager.setCaptionStyle(
194
+ captionStylePropertyName(property),
195
+ value
196
+ );
197
+ }
198
+ });
199
+ group.appendChild(input);
200
+ return group;
201
+ }
202
+ function showCaptionStyleMenu(controlBar, button) {
203
+ const player = controlBar.player;
204
+ const existingMenu = document.querySelector(`.${player.options.classPrefix}-caption-style-menu`);
205
+ if (existingMenu) {
206
+ existingMenu.remove();
207
+ button.setAttribute("aria-expanded", "false");
208
+ if (controlBar.openMenu === existingMenu) {
209
+ controlBar.openMenu = null;
210
+ controlBar.openMenuButton = null;
211
+ }
212
+ return;
213
+ }
214
+ const menuLabelId = `${player.options.classPrefix}-caption-style-label-${player.instanceId || ""}`;
215
+ const menu = DOMUtils.createElement("div", {
216
+ className: `${player.options.classPrefix}-caption-style-menu ${player.options.classPrefix}-menu ${player.options.classPrefix}-settings-menu`,
217
+ attributes: {
218
+ "role": "dialog",
219
+ "aria-modal": "false",
220
+ "aria-labelledby": menuLabelId
221
+ }
222
+ });
223
+ const visuallyHiddenLabel = DOMUtils.createElement("h2", {
224
+ textContent: i18n.t("player.captionStyling"),
225
+ attributes: { id: menuLabelId, class: `${player.options.classPrefix}-sr-only` },
226
+ style: {
227
+ position: "absolute",
228
+ width: "1px",
229
+ height: "1px",
230
+ padding: "0",
231
+ margin: "-1px",
232
+ overflow: "hidden",
233
+ clip: "rect(0,0,0,0)",
234
+ whiteSpace: "nowrap",
235
+ border: "0"
236
+ }
237
+ });
238
+ menu.appendChild(visuallyHiddenLabel);
239
+ menu.addEventListener("click", (e) => {
240
+ e.stopPropagation();
241
+ });
242
+ if (!player.captionManager || player.captionManager.tracks.length === 0) {
243
+ const noTracksItem = DOMUtils.createElement("div", {
244
+ className: `${player.options.classPrefix}-menu-item`,
245
+ textContent: i18n.t("player.noCaptions"),
246
+ attributes: {
247
+ "role": "status"
248
+ },
249
+ style: { opacity: "0.5", cursor: "default", padding: "12px 16px" }
250
+ });
251
+ menu.appendChild(noTracksItem);
252
+ menu.style.visibility = "hidden";
253
+ menu.style.display = "block";
254
+ controlBar.insertMenuIntoDOM(menu, button);
255
+ controlBar.positionMenu(menu, button, true);
256
+ requestAnimationFrame(() => {
257
+ menu.style.visibility = "visible";
258
+ });
259
+ controlBar.attachMenuCloseHandler(menu, button, true);
260
+ return;
261
+ }
262
+ menu.appendChild(createStyleControl(
263
+ player,
264
+ i18n.t("styleLabels.fontSize"),
265
+ "captionsFontSize",
266
+ [
267
+ { label: i18n.t("fontSizes.small"), value: "87.5%" },
268
+ { label: i18n.t("fontSizes.normal"), value: "100%" },
269
+ { label: i18n.t("fontSizes.large"), value: "125%" },
270
+ { label: i18n.t("fontSizes.xlarge"), value: "150%" }
271
+ ]
272
+ ));
273
+ menu.appendChild(createStyleControl(
274
+ player,
275
+ i18n.t("styleLabels.font"),
276
+ "captionsFontFamily",
277
+ [
278
+ { label: i18n.t("fontFamilies.sansSerif"), value: "sans-serif" },
279
+ { label: i18n.t("fontFamilies.serif"), value: "serif" },
280
+ { label: i18n.t("fontFamilies.monospace"), value: "monospace" }
281
+ ]
282
+ ));
283
+ menu.appendChild(createColorControl(player, i18n.t("styleLabels.textColor"), "captionsColor"));
284
+ menu.appendChild(createColorControl(player, i18n.t("styleLabels.background"), "captionsBackgroundColor"));
285
+ menu.appendChild(createOpacityControl(player, i18n.t("styleLabels.opacity"), "captionsOpacity"));
286
+ menu.style.minWidth = "220px";
287
+ menu.style.visibility = "hidden";
288
+ menu.style.display = "block";
289
+ controlBar.insertMenuIntoDOM(menu, button);
290
+ controlBar.positionMenu(menu, button, true);
291
+ requestAnimationFrame(() => {
292
+ menu.style.visibility = "visible";
293
+ });
294
+ controlBar.attachMenuCloseHandler(menu, button, true);
295
+ focusFirstElement(menu, `.${player.options.classPrefix}-style-select`);
296
+ }
297
+ export {
298
+ showCaptionStyleMenu
299
+ };
300
+ //# sourceMappingURL=vidply.CaptionStyleMenu-7XHRBWUM.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/controls/CaptionStyleMenu.ts"],
4
+ "sourcesContent": ["/**\n * Caption-style menu (font size/family, text & background color, opacity).\n *\n * Extracted from ControlBar and loaded on demand via dynamic import() so the\n * builder code for this relatively rarely-opened panel is not part of the\n * always-loaded core bundle. ControlBar.showCaptionStyleMenu() awaits this\n * module the first time the panel is opened.\n */\n\nimport { DOMUtils } from '../utils/DOMUtils.js';\nimport { i18n } from '../i18n/i18n.js';\nimport { focusFirstElement } from '../utils/FocusUtils.js';\nimport type { ControlBar } from './ControlBar.js';\nimport type { Player } from '../core/Player.js';\n\nfunction captionStylePropertyName(property: string): string {\n const stripped = property.replace('captions', '');\n return stripped.charAt(0).toLowerCase() + stripped.slice(1);\n}\n\nfunction createStyleControl(\n player: Player,\n label: string,\n property: string,\n options: Array<{ label: string; value: string }>\n): HTMLElement {\n const group = DOMUtils.createElement('div', {\n className: `${player.options.classPrefix}-style-group`\n });\n\n const controlId = `${player.options.classPrefix}-${property}-${Date.now()}`;\n\n const labelEl = DOMUtils.createElement('label', {\n textContent: label,\n attributes: {\n 'for': controlId\n },\n style: {\n display: 'block',\n fontSize: '12px',\n marginBottom: '4px',\n color: 'rgba(255,255,255,0.7)'\n }\n });\n group.appendChild(labelEl);\n\n const select = DOMUtils.createElement('select', {\n className: `${player.options.classPrefix}-style-select`,\n attributes: {\n 'id': controlId\n },\n style: {\n width: '100%',\n padding: '6px',\n background: 'var(--vidply-white)',\n border: '1px solid var(--vidply-white-10)',\n borderRadius: '4px',\n color: 'var(--vidply-black)',\n fontSize: '13px'\n }\n });\n\n const currentValue = player.options[property];\n options.forEach((opt) => {\n const option = DOMUtils.createElement('option', {\n textContent: opt.label,\n attributes: { value: opt.value }\n });\n if (opt.value === currentValue) {\n (option as HTMLOptionElement).selected = true;\n }\n select.appendChild(option);\n });\n\n select.addEventListener('mousedown', (e) => {\n e.stopPropagation();\n });\n\n select.addEventListener('click', (e) => {\n e.stopPropagation();\n });\n\n select.addEventListener('change', (e) => {\n e.stopPropagation();\n player.options[property] = (e.target as HTMLSelectElement).value;\n if (player.captionManager) {\n player.captionManager.setCaptionStyle(\n captionStylePropertyName(property),\n (e.target as HTMLSelectElement).value\n );\n }\n });\n\n group.appendChild(select);\n return group;\n}\n\nfunction createColorControl(player: Player, label: string, property: string): HTMLElement {\n const group = DOMUtils.createElement('div', {\n className: `${player.options.classPrefix}-style-group`\n });\n\n const controlId = `${player.options.classPrefix}-${property}-${Date.now()}`;\n\n const labelEl = DOMUtils.createElement('label', {\n textContent: label,\n attributes: {\n 'for': controlId\n },\n style: {\n display: 'block',\n fontSize: '12px',\n marginBottom: '4px',\n color: 'rgba(255,255,255,0.7)'\n }\n });\n group.appendChild(labelEl);\n\n const input = DOMUtils.createElement('input', {\n attributes: {\n 'id': controlId,\n type: 'color',\n value: String(player.options[property] ?? '')\n },\n style: {\n width: '100%',\n height: '32px',\n padding: '2px',\n background: 'rgba(255,255,255,0.1)',\n border: '1px solid rgba(255,255,255,0.2)',\n borderRadius: '4px',\n cursor: 'pointer'\n }\n });\n\n input.addEventListener('mousedown', (e) => {\n e.stopPropagation();\n });\n\n input.addEventListener('click', (e) => {\n e.stopPropagation();\n });\n\n input.addEventListener('change', (e) => {\n e.stopPropagation();\n player.options[property] = (e.target as HTMLInputElement).value;\n if (player.captionManager) {\n player.captionManager.setCaptionStyle(\n captionStylePropertyName(property),\n (e.target as HTMLInputElement).value\n );\n }\n });\n\n group.appendChild(input);\n return group;\n}\n\nfunction createOpacityControl(player: Player, label: string, property: string): HTMLElement {\n const group = DOMUtils.createElement('div', {\n className: `${player.options.classPrefix}-style-group`\n });\n\n const controlId = `${player.options.classPrefix}-${property}-${Date.now()}`;\n\n const labelContainer = DOMUtils.createElement('div', {\n style: {\n display: 'flex',\n justifyContent: 'space-between',\n marginBottom: '4px'\n }\n });\n\n const labelEl = DOMUtils.createElement('label', {\n textContent: label,\n attributes: {\n 'for': controlId\n },\n style: {\n fontSize: '12px',\n color: 'rgba(255,255,255,0.7)'\n }\n });\n\n const valueEl = DOMUtils.createElement('span', {\n textContent: Math.round(Number(player.options[property] ?? 0) * 100) + '%',\n style: {\n fontSize: '12px',\n color: 'rgba(255,255,255,0.7)'\n }\n });\n\n labelContainer.appendChild(labelEl);\n labelContainer.appendChild(valueEl);\n group.appendChild(labelContainer);\n\n const input = DOMUtils.createElement('input', {\n attributes: {\n 'id': controlId,\n type: 'range',\n min: '0',\n max: '1',\n step: '0.1',\n value: String(player.options[property])\n },\n style: {\n width: '100%',\n cursor: 'pointer'\n }\n });\n\n input.addEventListener('mousedown', (e) => {\n e.stopPropagation();\n });\n\n input.addEventListener('click', (e) => {\n e.stopPropagation();\n });\n\n input.addEventListener('input', (e) => {\n e.stopPropagation();\n const value = parseFloat((e.target as HTMLInputElement).value);\n valueEl.textContent = Math.round(value * 100) + '%';\n player.options[property] = value;\n if (player.captionManager) {\n player.captionManager.setCaptionStyle(\n captionStylePropertyName(property),\n value\n );\n }\n });\n\n group.appendChild(input);\n return group;\n}\n\n/**\n * Build and show the caption-style dialog for the given ControlBar. Mirrors\n * the toggle/positioning behavior of the other ControlBar menus by delegating\n * back to the ControlBar's shared DOM-insertion / positioning / close helpers.\n */\nexport function showCaptionStyleMenu(controlBar: ControlBar, button: HTMLElement): void {\n const player = controlBar.player;\n\n // Remove existing menu if any (toggle behavior)\n const existingMenu = document.querySelector(`.${player.options.classPrefix}-caption-style-menu`);\n if (existingMenu) {\n existingMenu.remove();\n button.setAttribute('aria-expanded', 'false');\n if (controlBar.openMenu === existingMenu) {\n controlBar.openMenu = null;\n controlBar.openMenuButton = null;\n }\n return;\n }\n\n // The caption-style panel contains form controls (selects, color\n // inputs, sliders), not single-action menuitems. Modeling it as a\n // dialog yields correct semantics for AT and matches the\n // SettingsDialog pattern.\n const menuLabelId = `${player.options.classPrefix}-caption-style-label-${player.instanceId || ''}`;\n const menu = DOMUtils.createElement('div', {\n className: `${player.options.classPrefix}-caption-style-menu ${player.options.classPrefix}-menu ${player.options.classPrefix}-settings-menu`,\n attributes: {\n 'role': 'dialog',\n 'aria-modal': 'false',\n 'aria-labelledby': menuLabelId\n }\n });\n const visuallyHiddenLabel = DOMUtils.createElement('h2', {\n textContent: i18n.t('player.captionStyling'),\n attributes: { id: menuLabelId, class: `${player.options.classPrefix}-sr-only` },\n style: {\n position: 'absolute',\n width: '1px',\n height: '1px',\n padding: '0',\n margin: '-1px',\n overflow: 'hidden',\n clip: 'rect(0,0,0,0)',\n whiteSpace: 'nowrap',\n border: '0'\n }\n });\n menu.appendChild(visuallyHiddenLabel);\n\n // Prevent menu from closing when clicking inside\n menu.addEventListener('click', (e) => {\n e.stopPropagation();\n });\n\n // Check if there are any caption tracks\n if (!player.captionManager || player.captionManager.tracks.length === 0) {\n // Show \"No captions available\" message\n const noTracksItem = DOMUtils.createElement('div', {\n className: `${player.options.classPrefix}-menu-item`,\n textContent: i18n.t('player.noCaptions'),\n attributes: {\n 'role': 'status'\n },\n style: { opacity: '0.5', cursor: 'default', padding: '12px 16px' }\n });\n menu.appendChild(noTracksItem);\n\n menu.style.visibility = 'hidden';\n menu.style.display = 'block';\n\n controlBar.insertMenuIntoDOM(menu, button);\n controlBar.positionMenu(menu, button, true);\n\n requestAnimationFrame(() => {\n menu.style.visibility = 'visible';\n });\n\n controlBar.attachMenuCloseHandler(menu, button, true);\n return;\n }\n\n // Font Size\n menu.appendChild(createStyleControl(\n player,\n i18n.t('styleLabels.fontSize'),\n 'captionsFontSize',\n [\n { label: i18n.t('fontSizes.small'), value: '87.5%' },\n { label: i18n.t('fontSizes.normal'), value: '100%' },\n { label: i18n.t('fontSizes.large'), value: '125%' },\n { label: i18n.t('fontSizes.xlarge'), value: '150%' }\n ]\n ));\n\n // Font Family\n menu.appendChild(createStyleControl(\n player,\n i18n.t('styleLabels.font'),\n 'captionsFontFamily',\n [\n { label: i18n.t('fontFamilies.sansSerif'), value: 'sans-serif' },\n { label: i18n.t('fontFamilies.serif'), value: 'serif' },\n { label: i18n.t('fontFamilies.monospace'), value: 'monospace' }\n ]\n ));\n\n // Text Color\n menu.appendChild(createColorControl(player, i18n.t('styleLabels.textColor'), 'captionsColor'));\n\n // Background Color\n menu.appendChild(createColorControl(player, i18n.t('styleLabels.background'), 'captionsBackgroundColor'));\n\n // Opacity\n menu.appendChild(createOpacityControl(player, i18n.t('styleLabels.opacity'), 'captionsOpacity'));\n\n // Set min-width for caption style menu\n menu.style.minWidth = '220px';\n\n menu.style.visibility = 'hidden';\n menu.style.display = 'block';\n\n controlBar.insertMenuIntoDOM(menu, button);\n controlBar.positionMenu(menu, button, true);\n\n requestAnimationFrame(() => {\n menu.style.visibility = 'visible';\n });\n\n controlBar.attachMenuCloseHandler(menu, button, true);\n\n // Auto-focus the first style select element\n focusFirstElement(menu, `.${player.options.classPrefix}-style-select`);\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }