vidply 1.2.0 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. package/README.md +974 -946
  2. package/dist/dev/{vidply.AudioDescriptionManager-R47Q4YR4.js → vidply.AudioDescriptionManager-JYYLK5KB.js} +275 -19
  3. package/dist/dev/vidply.AudioDescriptionManager-JYYLK5KB.js.map +7 -0
  4. package/dist/dev/{vidply.CaptionStyleMenu-7XHRBWUM.js → vidply.CaptionStyleMenu-47RCKQF5.js} +4 -4
  5. package/dist/dev/{vidply.DASHRenderer-PWEMC3BT.js → vidply.DASHRenderer-VEJ4HDHO.js} +3 -3
  6. package/dist/dev/vidply.DASHRenderer-VEJ4HDHO.js.map +7 -0
  7. package/dist/dev/{vidply.FloatingPlayerManager-PKNNNAS2.js → vidply.FloatingPlayerManager-C2UVRPZY.js} +5 -5
  8. package/dist/dev/vidply.FloatingPlayerManager-C2UVRPZY.js.map +7 -0
  9. package/dist/dev/{vidply.HLSRenderer-E7RQ74HI.js → vidply.HLSRenderer-ZVBK2TNQ.js} +4 -4
  10. package/dist/dev/vidply.HLSRenderer-ZVBK2TNQ.js.map +7 -0
  11. package/dist/dev/vidply.HTML5Renderer-HLRQUOCU.js +12 -0
  12. package/dist/dev/{vidply.SignLanguageManager-FET5FQZ7.js → vidply.SignLanguageManager-FLUCBW5H.js} +8 -8
  13. package/dist/dev/vidply.SignLanguageManager-FLUCBW5H.js.map +7 -0
  14. package/dist/dev/{vidply.SoundCloudRenderer-RVSJZODO.js → vidply.SoundCloudRenderer-MGMAJ7NK.js} +3 -3
  15. package/dist/dev/vidply.SoundCloudRenderer-MGMAJ7NK.js.map +7 -0
  16. package/dist/dev/{vidply.TranscriptManager-5GXOQCLX.js → vidply.TranscriptManager-Y2RYXHJW.js} +10 -10
  17. package/dist/dev/vidply.TranscriptManager-Y2RYXHJW.js.map +7 -0
  18. package/dist/dev/{vidply.VimeoRenderer-ER2LSBBW.js → vidply.VimeoRenderer-RQFQD7OE.js} +3 -3
  19. package/dist/dev/vidply.VimeoRenderer-RQFQD7OE.js.map +7 -0
  20. package/dist/dev/{vidply.YouTubeRenderer-NFTOWJAB.js → vidply.YouTubeRenderer-EU2BARZF.js} +3 -3
  21. package/dist/dev/vidply.YouTubeRenderer-EU2BARZF.js.map +7 -0
  22. package/dist/dev/{vidply.chunk-BNFIUL2Z.js → vidply.chunk-6LYZYI5G.js} +3 -3
  23. package/dist/dev/vidply.chunk-6LYZYI5G.js.map +7 -0
  24. package/dist/dev/{vidply.chunk-NQPURMQY.js → vidply.chunk-A4ESEOQ2.js} +6 -6
  25. package/dist/dev/vidply.chunk-A4ESEOQ2.js.map +7 -0
  26. package/dist/dev/{vidply.chunk-W25HUCHS.js → vidply.chunk-CGYIED2I.js} +2 -2
  27. package/dist/dev/vidply.chunk-CGYIED2I.js.map +7 -0
  28. package/dist/dev/{vidply.chunk-SK4V2DAU.js → vidply.chunk-HZ7UOKOU.js} +3 -3
  29. package/dist/dev/{vidply.chunk-RZWZGDV7.js → vidply.chunk-I6EDRYGJ.js} +33 -6
  30. package/dist/dev/vidply.chunk-I6EDRYGJ.js.map +7 -0
  31. package/dist/dev/{vidply.chunk-64WXMTGL.js → vidply.chunk-JKMQGLCJ.js} +5 -5
  32. package/dist/dev/vidply.chunk-JKMQGLCJ.js.map +7 -0
  33. package/dist/dev/{vidply.chunk-DZSDO3B7.js → vidply.chunk-JZNZX32Z.js} +2 -2
  34. package/dist/dev/{vidply.chunk-VMSQAZM7.js → vidply.chunk-P25MRJ6O.js} +2 -2
  35. package/dist/dev/{vidply.chunk-6RQA4ROD.js → vidply.chunk-PRFNESYA.js} +2 -2
  36. package/dist/dev/{vidply.chunk-KI7C3QVL.js → vidply.chunk-UI3RYCVF.js} +4 -3
  37. package/dist/dev/vidply.chunk-UI3RYCVF.js.map +7 -0
  38. package/dist/dev/{vidply.chunk-JJ47NO24.js → vidply.chunk-UNFGMABP.js} +2 -2
  39. package/dist/dev/vidply.chunk-UNFGMABP.js.map +7 -0
  40. package/dist/dev/{vidply.de-4YAT7FFM.js → vidply.de-VAUVSQXC.js} +29 -2
  41. package/dist/dev/vidply.de-VAUVSQXC.js.map +7 -0
  42. package/dist/dev/{vidply.es-LEWIRUBH.js → vidply.es-G65HFWO6.js} +29 -2
  43. package/dist/dev/vidply.es-G65HFWO6.js.map +7 -0
  44. package/dist/dev/vidply.esm.js +648 -35
  45. package/dist/dev/vidply.esm.js.map +3 -3
  46. package/dist/dev/{vidply.fr-Q4GV2NOE.js → vidply.fr-MJNOZELM.js} +29 -2
  47. package/dist/dev/vidply.fr-MJNOZELM.js.map +7 -0
  48. package/dist/dev/{vidply.ja-S6ITA6WU.js → vidply.ja-INMDHIYJ.js} +29 -2
  49. package/dist/dev/vidply.ja-INMDHIYJ.js.map +7 -0
  50. package/dist/legacy/vidply.js +1062 -34
  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 +127 -55
  54. package/dist/prod/vidply.AudioDescriptionManager-EKWUNOPE.min.js +6 -0
  55. package/dist/prod/{vidply.CaptionStyleMenu-M3KVQWB5.min.js → vidply.CaptionStyleMenu-JTWSNHRQ.min.js} +2 -2
  56. package/dist/prod/{vidply.DASHRenderer-DN3S27AV.min.js → vidply.DASHRenderer-CIDAHFWM.min.js} +2 -2
  57. package/dist/prod/{vidply.FloatingPlayerManager-SKAZ4J43.min.js → vidply.FloatingPlayerManager-VZXHPCPM.min.js} +2 -2
  58. package/dist/prod/{vidply.HLSRenderer-62HH4YTK.min.js → vidply.HLSRenderer-Q6EFYKID.min.js} +2 -2
  59. package/dist/prod/vidply.HTML5Renderer-XRDJUTOO.min.js +6 -0
  60. package/dist/prod/{vidply.SignLanguageManager-V3OBTSSF.min.js → vidply.SignLanguageManager-JUOSA5AM.min.js} +2 -2
  61. package/dist/prod/{vidply.SoundCloudRenderer-CXRVZIAJ.min.js → vidply.SoundCloudRenderer-7ODD46PO.min.js} +2 -2
  62. package/dist/prod/{vidply.TranscriptManager-SYM3DFUY.min.js → vidply.TranscriptManager-4B2QKYGQ.min.js} +2 -2
  63. package/dist/prod/{vidply.VimeoRenderer-72TWRYFQ.min.js → vidply.VimeoRenderer-MR3UYLY7.min.js} +2 -2
  64. package/dist/prod/{vidply.YouTubeRenderer-UMHEV3KD.min.js → vidply.YouTubeRenderer-ITRTC7NR.min.js} +2 -2
  65. package/dist/prod/{vidply.chunk-YEENJS3Q.min.js → vidply.chunk-3EEDXU4T.min.js} +2 -2
  66. package/dist/prod/{vidply.chunk-P4UXOHWA.min.js → vidply.chunk-5HTX5QXW.min.js} +1 -1
  67. package/dist/prod/{vidply.chunk-ABPXVDLZ.min.js → vidply.chunk-6OGHHIYP.min.js} +2 -2
  68. package/dist/prod/{vidply.chunk-UK7YGOB5.min.js → vidply.chunk-GUPGMFUZ.min.js} +2 -2
  69. package/dist/prod/{vidply.chunk-WX3MVEAL.min.js → vidply.chunk-MWEBITCP.min.js} +1 -1
  70. package/dist/prod/{vidply.chunk-2TB6MUHO.min.js → vidply.chunk-PL3P3JHR.min.js} +1 -1
  71. package/dist/prod/{vidply.chunk-EKEYJWMG.min.js → vidply.chunk-QQZGV7TD.min.js} +2 -2
  72. package/dist/prod/{vidply.chunk-Z3YDSA7O.min.js → vidply.chunk-U5IRRYG5.min.js} +1 -1
  73. package/dist/prod/{vidply.chunk-TM6AXJVD.min.js → vidply.chunk-VKHV4QHA.min.js} +2 -2
  74. package/dist/prod/{vidply.chunk-P3PQBY7N.min.js → vidply.chunk-XA4M574A.min.js} +1 -1
  75. package/dist/prod/{vidply.chunk-RI3X7WHK.min.js → vidply.chunk-ZPTV4JEP.min.js} +2 -2
  76. package/dist/prod/{vidply.de-BCHXRQ53.min.js → vidply.de-UZHFCEZU.min.js} +2 -2
  77. package/dist/prod/{vidply.es-PICAKMZD.min.js → vidply.es-BOISUV2S.min.js} +2 -2
  78. package/dist/prod/vidply.esm.min.js +2 -2
  79. package/dist/prod/{vidply.fr-ZILI7CDR.min.js → vidply.fr-ICSMKPFM.min.js} +2 -2
  80. package/dist/prod/{vidply.ja-5WQ4OK4J.min.js → vidply.ja-VQ2HKGAY.min.js} +2 -2
  81. package/dist/vidply.css +5852 -5796
  82. package/dist/vidply.esm.min.meta.json +204 -149
  83. package/dist/vidply.min.css +1 -1
  84. package/package.json +11 -11
  85. package/src/controls/CaptionManager.ts +598 -598
  86. package/src/controls/ControlBar.ts +4191 -4159
  87. package/src/controls/KeyboardHelp.ts +326 -0
  88. package/src/controls/KeyboardManager.ts +387 -383
  89. package/src/controls/SettingsDialog.ts +488 -488
  90. package/src/controls/TranscriptManager.ts +2426 -2426
  91. package/src/core/AudioDescriptionManager.ts +863 -774
  92. package/src/core/DescriptionSpeechManager.ts +232 -0
  93. package/src/core/FloatingPlayerManager.ts +674 -674
  94. package/src/core/LazyInit.ts +80 -80
  95. package/src/core/MediaSessionManager.ts +341 -0
  96. package/src/core/MetadataAlertsManager.ts +514 -514
  97. package/src/core/Player.ts +2973 -2872
  98. package/src/core/PosterManager.ts +169 -169
  99. package/src/core/PseudoFullscreen.ts +179 -179
  100. package/src/core/ResponsiveManager.ts +231 -231
  101. package/src/core/ResumeManager.ts +236 -236
  102. package/src/core/SignLanguageManager.ts +1308 -1308
  103. package/src/core/ThemeManager.ts +157 -157
  104. package/src/features/PlaylistManager.ts +1838 -1827
  105. package/src/i18n/i18n.ts +223 -223
  106. package/src/i18n/languages/de.ts +244 -217
  107. package/src/i18n/languages/en.ts +245 -218
  108. package/src/i18n/languages/es.ts +241 -214
  109. package/src/i18n/languages/fr.ts +241 -214
  110. package/src/i18n/languages/ja.ts +241 -214
  111. package/src/icons/Icons.ts +168 -167
  112. package/src/index.ts +196 -193
  113. package/src/renderers/DASHRenderer.ts +1077 -1077
  114. package/src/renderers/HLSRenderer.ts +944 -944
  115. package/src/renderers/HTML5Renderer.ts +389 -389
  116. package/src/renderers/SoundCloudRenderer.ts +437 -437
  117. package/src/renderers/VimeoRenderer.ts +298 -298
  118. package/src/renderers/YouTubeRenderer.ts +290 -290
  119. package/src/styles/vidply.css +5852 -5796
  120. package/src/types/events.ts +128 -122
  121. package/src/types/globals.d.ts +313 -313
  122. package/src/types/options.ts +199 -182
  123. package/src/types/renderer.ts +58 -58
  124. package/src/utils/DOMUtils.ts +281 -281
  125. package/src/utils/DownloadInfo.ts +171 -171
  126. package/src/utils/DraggablePanel.ts +539 -539
  127. package/src/utils/DraggablePanelMenu.ts +195 -195
  128. package/src/utils/DraggableResizable.ts +997 -997
  129. package/src/utils/EventEmitter.ts +59 -59
  130. package/src/utils/FormUtils.ts +95 -95
  131. package/src/utils/MenuFactory.ts +483 -483
  132. package/src/utils/Sanitize.ts +70 -70
  133. package/src/utils/SettingsMenuFactory.ts +289 -289
  134. package/src/utils/StorageManager.ts +287 -287
  135. package/src/utils/TrackLabelUtils.ts +52 -52
  136. package/src/utils/UrlSafe.ts +69 -69
  137. package/src/utils/VideoFrameCapture.ts +114 -114
  138. package/src/utils/WindowComponents.ts +250 -250
  139. package/dist/dev/vidply.AudioDescriptionManager-R47Q4YR4.js.map +0 -7
  140. package/dist/dev/vidply.DASHRenderer-PWEMC3BT.js.map +0 -7
  141. package/dist/dev/vidply.FloatingPlayerManager-PKNNNAS2.js.map +0 -7
  142. package/dist/dev/vidply.HLSRenderer-E7RQ74HI.js.map +0 -7
  143. package/dist/dev/vidply.HTML5Renderer-BU4QJJJY.js +0 -12
  144. package/dist/dev/vidply.SignLanguageManager-FET5FQZ7.js.map +0 -7
  145. package/dist/dev/vidply.SoundCloudRenderer-RVSJZODO.js.map +0 -7
  146. package/dist/dev/vidply.TranscriptManager-5GXOQCLX.js.map +0 -7
  147. package/dist/dev/vidply.VimeoRenderer-ER2LSBBW.js.map +0 -7
  148. package/dist/dev/vidply.YouTubeRenderer-NFTOWJAB.js.map +0 -7
  149. package/dist/dev/vidply.chunk-64WXMTGL.js.map +0 -7
  150. package/dist/dev/vidply.chunk-BNFIUL2Z.js.map +0 -7
  151. package/dist/dev/vidply.chunk-JJ47NO24.js.map +0 -7
  152. package/dist/dev/vidply.chunk-KI7C3QVL.js.map +0 -7
  153. package/dist/dev/vidply.chunk-NQPURMQY.js.map +0 -7
  154. package/dist/dev/vidply.chunk-RZWZGDV7.js.map +0 -7
  155. package/dist/dev/vidply.chunk-W25HUCHS.js.map +0 -7
  156. package/dist/dev/vidply.de-4YAT7FFM.js.map +0 -7
  157. package/dist/dev/vidply.es-LEWIRUBH.js.map +0 -7
  158. package/dist/dev/vidply.fr-Q4GV2NOE.js.map +0 -7
  159. package/dist/dev/vidply.ja-S6ITA6WU.js.map +0 -7
  160. package/dist/prod/vidply.AudioDescriptionManager-RHLIJW5C.min.js +0 -6
  161. package/dist/prod/vidply.HTML5Renderer-IU5T3WEW.min.js +0 -6
  162. /package/dist/dev/{vidply.CaptionStyleMenu-7XHRBWUM.js.map → vidply.CaptionStyleMenu-47RCKQF5.js.map} +0 -0
  163. /package/dist/dev/{vidply.HTML5Renderer-BU4QJJJY.js.map → vidply.HTML5Renderer-HLRQUOCU.js.map} +0 -0
  164. /package/dist/dev/{vidply.chunk-SK4V2DAU.js.map → vidply.chunk-HZ7UOKOU.js.map} +0 -0
  165. /package/dist/dev/{vidply.chunk-DZSDO3B7.js.map → vidply.chunk-JZNZX32Z.js.map} +0 -0
  166. /package/dist/dev/{vidply.chunk-VMSQAZM7.js.map → vidply.chunk-P25MRJ6O.js.map} +0 -0
  167. /package/dist/dev/{vidply.chunk-6RQA4ROD.js.map → vidply.chunk-PRFNESYA.js.map} +0 -0
@@ -1,231 +1,231 @@
1
- /**
2
- * Resize / orientation / fullscreen handling extracted from Player.
3
- *
4
- * Three responsibilities live here:
5
- *
6
- * 1. Reacting to container size changes (ResizeObserver when
7
- * available, `window.resize` fallback otherwise) so the control
8
- * bar and the transcript panel stay correctly laid out.
9
- * 2. Listening for orientation changes on mobile so the transcript
10
- * panel re-positions after the viewport rotates.
11
- * 3. Tracking native fullscreen changes across vendor prefixes so
12
- * `state.fullscreen` stays in sync with reality — including the
13
- * inert/overlay bookkeeping that's shared with the pseudo-
14
- * fullscreen fallback.
15
- *
16
- * Every listener is attached with the Player's `lifecycleSignal`
17
- * (or, for the ResizeObserver / older matchMedia listeners that
18
- * don't accept AbortSignal, cleaned up explicitly in `cleanup()`).
19
- */
20
-
21
- import { PseudoFullscreenController } from './PseudoFullscreen.js';
22
- import type { ControlBar } from '../controls/ControlBar.js';
23
- import type { Player } from './Player.js';
24
-
25
- export class ResponsiveManager {
26
- private readonly player: Player;
27
- private orientationQuery: MediaQueryList | null = null;
28
- private orientationHandler: ((e: MediaQueryListEvent) => void) | null = null;
29
-
30
- constructor(player: Player) {
31
- this.player = player;
32
- }
33
-
34
- setup(): void {
35
- this.setupResizeTracking();
36
- this.setupOrientationTracking();
37
- this.setupFullscreenTracking();
38
- }
39
-
40
- private setupResizeTracking(): void {
41
- const player = this.player;
42
-
43
- if (typeof ResizeObserver !== 'undefined') {
44
- // Preferred path: ResizeObserver fires per-container, not per-viewport,
45
- // so multiple players on one page don't stomp on each other.
46
- player.resizeObserver = new ResizeObserver((entries) => {
47
- for (const entry of entries) {
48
- const width = entry.contentRect.width;
49
-
50
- const controlBar = player.controlBar as (ControlBar & {
51
- updateControlsForViewport?: (w: number) => void
52
- }) | null;
53
- if (controlBar && typeof controlBar.updateControlsForViewport === 'function') {
54
- controlBar.updateControlsForViewport(width);
55
- }
56
-
57
- if (player.transcriptManager && player.transcriptManager.isVisible) {
58
- player.transcriptManager.positionTranscript();
59
- }
60
- }
61
- });
62
-
63
- player.resizeObserver.observe(player.container);
64
- return;
65
- }
66
-
67
- // Fallback: `window.resize`. We still respect
68
- // `draggableResizable.manuallyPositioned` so we don't stomp on a
69
- // user-chosen transcript location.
70
- player.resizeHandler = () => {
71
- const width = player.container.clientWidth;
72
-
73
- const controlBar = player.controlBar as (ControlBar & {
74
- updateControlsForViewport?: (w: number) => void
75
- }) | null;
76
- if (controlBar && typeof controlBar.updateControlsForViewport === 'function') {
77
- controlBar.updateControlsForViewport(width);
78
- }
79
-
80
- if (player.transcriptManager && player.transcriptManager.isVisible) {
81
- if (!player.transcriptManager.draggableResizable
82
- || !player.transcriptManager.draggableResizable.manuallyPositioned) {
83
- player.transcriptManager.positionTranscript();
84
- }
85
- }
86
- };
87
-
88
- window.addEventListener('resize', player.resizeHandler, { signal: player.lifecycleSignal });
89
- }
90
-
91
- private setupOrientationTracking(): void {
92
- const player = this.player;
93
- if (!window.matchMedia) return;
94
-
95
- this.orientationHandler = () => {
96
- // Wait for layout to settle before we recompute positions —
97
- // otherwise `getBoundingClientRect` still reflects the
98
- // pre-rotation viewport.
99
- setTimeout(() => {
100
- if (player.transcriptManager && player.transcriptManager.isVisible) {
101
- if (!player.transcriptManager.draggableResizable
102
- || !player.transcriptManager.draggableResizable.manuallyPositioned) {
103
- player.transcriptManager.positionTranscript();
104
- }
105
- }
106
- }, 100);
107
- };
108
-
109
- const orientationQuery = window.matchMedia('(orientation: portrait)');
110
- if (orientationQuery.addEventListener) {
111
- // Modern MediaQueryList supports AddEventListenerOptions including
112
- // `signal`, so teardown happens automatically via the player's
113
- // lifecycle AbortController.
114
- orientationQuery.addEventListener('change', this.orientationHandler, {
115
- signal: player.lifecycleSignal
116
- });
117
- } else if ((orientationQuery as MediaQueryList).addListener) {
118
- // Older Safari: no signal support; `cleanup()` will remove this
119
- // explicitly below.
120
- (orientationQuery as MediaQueryList).addListener(this.orientationHandler);
121
- }
122
-
123
- this.orientationQuery = orientationQuery;
124
- // Mirror the field onto the player too; some older call sites
125
- // reach for `player.orientationQuery` directly.
126
- player.orientationQuery = orientationQuery;
127
- player.orientationHandler = this.orientationHandler;
128
- }
129
-
130
- private setupFullscreenTracking(): void {
131
- const player = this.player;
132
-
133
- player.fullscreenChangeHandler = () => {
134
- const doc = document as Document & {
135
- webkitFullscreenElement?: Element | null;
136
- mozFullScreenElement?: Element | null;
137
- msFullscreenElement?: Element | null;
138
- };
139
- const isFullscreen = Boolean(
140
- document.fullscreenElement ||
141
- doc.webkitFullscreenElement ||
142
- doc.mozFullScreenElement ||
143
- doc.msFullscreenElement
144
- );
145
-
146
- if (player.state.fullscreen === isFullscreen) return;
147
-
148
- player.state.fullscreen = isFullscreen;
149
-
150
- if (!player.pseudoFullscreen) {
151
- player.pseudoFullscreen = new PseudoFullscreenController(player);
152
- }
153
-
154
- if (isFullscreen) {
155
- player.container.classList.add(`${player.options.classPrefix}-fullscreen`);
156
- // CSS fallback for browsers without :has() support.
157
- document.body.classList.add('vidply-fullscreen-active');
158
- // Make background content inert even in real-fullscreen mode —
159
- // the overlay still leaves DOM focus reachable on some browsers.
160
- player.pseudoFullscreen.makeBackgroundInert();
161
- } else {
162
- player.container.classList.remove(`${player.options.classPrefix}-fullscreen`);
163
- document.body.classList.remove('vidply-fullscreen-active');
164
- player.pseudoFullscreen.restoreBackgroundInteractivity();
165
- player._disablePseudoFullscreen();
166
- }
167
-
168
- player.emit('fullscreenchange', isFullscreen);
169
-
170
- if (player.controlBar) {
171
- player.controlBar.updateFullscreenButton();
172
- }
173
-
174
- // Reposition the sign-language overlay after the transition
175
- // completes; drag/resize affordances differ between windowed
176
- // and fullscreen on mobile so we re-install them.
177
- if (player.signLanguageWrapper && player.signLanguageWrapper.style.display !== 'none') {
178
- const isMobileDevice = window.innerWidth < 768;
179
- if (isMobileDevice) {
180
- player.setupSignLanguageInteraction();
181
- }
182
-
183
- player.setManagedTimeout(() => {
184
- requestAnimationFrame(() => {
185
- player.storage.saveSignLanguagePreferences({ size: null });
186
- if (player.signLanguageWrapper) {
187
- player.signLanguageWrapper.style.width = isFullscreen ? '400px' : '280px';
188
- }
189
- player.constrainSignLanguagePosition();
190
- });
191
- }, 500);
192
- }
193
- };
194
-
195
- const opts = { signal: player.lifecycleSignal };
196
- document.addEventListener('fullscreenchange', player.fullscreenChangeHandler, opts);
197
- document.addEventListener('webkitfullscreenchange', player.fullscreenChangeHandler, opts);
198
- document.addEventListener('mozfullscreenchange', player.fullscreenChangeHandler, opts);
199
- document.addEventListener('MSFullscreenChange', player.fullscreenChangeHandler, opts);
200
- }
201
-
202
- /**
203
- * Tear down listeners that aren't covered by the Player's
204
- * lifecycle AbortController. The `window.resize` and
205
- * `document.fullscreenchange` listeners are already cleaned up
206
- * via `{signal}`; only the ResizeObserver and old-Safari
207
- * matchMedia listener need an explicit removal.
208
- */
209
- cleanup(): void {
210
- const player = this.player;
211
-
212
- if (player.resizeObserver) {
213
- player.resizeObserver.disconnect();
214
- player.resizeObserver = null;
215
- }
216
- player.resizeHandler = null;
217
- player.fullscreenChangeHandler = null;
218
-
219
- if (this.orientationQuery && this.orientationHandler) {
220
- if (this.orientationQuery.removeEventListener) {
221
- this.orientationQuery.removeEventListener('change', this.orientationHandler);
222
- } else if ((this.orientationQuery as MediaQueryList).removeListener) {
223
- (this.orientationQuery as MediaQueryList).removeListener(this.orientationHandler);
224
- }
225
- this.orientationQuery = null;
226
- this.orientationHandler = null;
227
- }
228
- player.orientationQuery = null;
229
- player.orientationHandler = null;
230
- }
231
- }
1
+ /**
2
+ * Resize / orientation / fullscreen handling extracted from Player.
3
+ *
4
+ * Three responsibilities live here:
5
+ *
6
+ * 1. Reacting to container size changes (ResizeObserver when
7
+ * available, `window.resize` fallback otherwise) so the control
8
+ * bar and the transcript panel stay correctly laid out.
9
+ * 2. Listening for orientation changes on mobile so the transcript
10
+ * panel re-positions after the viewport rotates.
11
+ * 3. Tracking native fullscreen changes across vendor prefixes so
12
+ * `state.fullscreen` stays in sync with reality — including the
13
+ * inert/overlay bookkeeping that's shared with the pseudo-
14
+ * fullscreen fallback.
15
+ *
16
+ * Every listener is attached with the Player's `lifecycleSignal`
17
+ * (or, for the ResizeObserver / older matchMedia listeners that
18
+ * don't accept AbortSignal, cleaned up explicitly in `cleanup()`).
19
+ */
20
+
21
+ import { PseudoFullscreenController } from './PseudoFullscreen.js';
22
+ import type { ControlBar } from '../controls/ControlBar.js';
23
+ import type { Player } from './Player.js';
24
+
25
+ export class ResponsiveManager {
26
+ private readonly player: Player;
27
+ private orientationQuery: MediaQueryList | null = null;
28
+ private orientationHandler: ((e: MediaQueryListEvent) => void) | null = null;
29
+
30
+ constructor(player: Player) {
31
+ this.player = player;
32
+ }
33
+
34
+ setup(): void {
35
+ this.setupResizeTracking();
36
+ this.setupOrientationTracking();
37
+ this.setupFullscreenTracking();
38
+ }
39
+
40
+ private setupResizeTracking(): void {
41
+ const player = this.player;
42
+
43
+ if (typeof ResizeObserver !== 'undefined') {
44
+ // Preferred path: ResizeObserver fires per-container, not per-viewport,
45
+ // so multiple players on one page don't stomp on each other.
46
+ player.resizeObserver = new ResizeObserver((entries) => {
47
+ for (const entry of entries) {
48
+ const width = entry.contentRect.width;
49
+
50
+ const controlBar = player.controlBar as (ControlBar & {
51
+ updateControlsForViewport?: (w: number) => void
52
+ }) | null;
53
+ if (controlBar && typeof controlBar.updateControlsForViewport === 'function') {
54
+ controlBar.updateControlsForViewport(width);
55
+ }
56
+
57
+ if (player.transcriptManager && player.transcriptManager.isVisible) {
58
+ player.transcriptManager.positionTranscript();
59
+ }
60
+ }
61
+ });
62
+
63
+ player.resizeObserver.observe(player.container);
64
+ return;
65
+ }
66
+
67
+ // Fallback: `window.resize`. We still respect
68
+ // `draggableResizable.manuallyPositioned` so we don't stomp on a
69
+ // user-chosen transcript location.
70
+ player.resizeHandler = () => {
71
+ const width = player.container.clientWidth;
72
+
73
+ const controlBar = player.controlBar as (ControlBar & {
74
+ updateControlsForViewport?: (w: number) => void
75
+ }) | null;
76
+ if (controlBar && typeof controlBar.updateControlsForViewport === 'function') {
77
+ controlBar.updateControlsForViewport(width);
78
+ }
79
+
80
+ if (player.transcriptManager && player.transcriptManager.isVisible) {
81
+ if (!player.transcriptManager.draggableResizable
82
+ || !player.transcriptManager.draggableResizable.manuallyPositioned) {
83
+ player.transcriptManager.positionTranscript();
84
+ }
85
+ }
86
+ };
87
+
88
+ window.addEventListener('resize', player.resizeHandler, { signal: player.lifecycleSignal });
89
+ }
90
+
91
+ private setupOrientationTracking(): void {
92
+ const player = this.player;
93
+ if (!window.matchMedia) return;
94
+
95
+ this.orientationHandler = () => {
96
+ // Wait for layout to settle before we recompute positions —
97
+ // otherwise `getBoundingClientRect` still reflects the
98
+ // pre-rotation viewport.
99
+ setTimeout(() => {
100
+ if (player.transcriptManager && player.transcriptManager.isVisible) {
101
+ if (!player.transcriptManager.draggableResizable
102
+ || !player.transcriptManager.draggableResizable.manuallyPositioned) {
103
+ player.transcriptManager.positionTranscript();
104
+ }
105
+ }
106
+ }, 100);
107
+ };
108
+
109
+ const orientationQuery = window.matchMedia('(orientation: portrait)');
110
+ if (orientationQuery.addEventListener) {
111
+ // Modern MediaQueryList supports AddEventListenerOptions including
112
+ // `signal`, so teardown happens automatically via the player's
113
+ // lifecycle AbortController.
114
+ orientationQuery.addEventListener('change', this.orientationHandler, {
115
+ signal: player.lifecycleSignal
116
+ });
117
+ } else if ((orientationQuery as MediaQueryList).addListener) {
118
+ // Older Safari: no signal support; `cleanup()` will remove this
119
+ // explicitly below.
120
+ (orientationQuery as MediaQueryList).addListener(this.orientationHandler);
121
+ }
122
+
123
+ this.orientationQuery = orientationQuery;
124
+ // Mirror the field onto the player too; some older call sites
125
+ // reach for `player.orientationQuery` directly.
126
+ player.orientationQuery = orientationQuery;
127
+ player.orientationHandler = this.orientationHandler;
128
+ }
129
+
130
+ private setupFullscreenTracking(): void {
131
+ const player = this.player;
132
+
133
+ player.fullscreenChangeHandler = () => {
134
+ const doc = document as Document & {
135
+ webkitFullscreenElement?: Element | null;
136
+ mozFullScreenElement?: Element | null;
137
+ msFullscreenElement?: Element | null;
138
+ };
139
+ const isFullscreen = Boolean(
140
+ document.fullscreenElement ||
141
+ doc.webkitFullscreenElement ||
142
+ doc.mozFullScreenElement ||
143
+ doc.msFullscreenElement
144
+ );
145
+
146
+ if (player.state.fullscreen === isFullscreen) return;
147
+
148
+ player.state.fullscreen = isFullscreen;
149
+
150
+ if (!player.pseudoFullscreen) {
151
+ player.pseudoFullscreen = new PseudoFullscreenController(player);
152
+ }
153
+
154
+ if (isFullscreen) {
155
+ player.container.classList.add(`${player.options.classPrefix}-fullscreen`);
156
+ // CSS fallback for browsers without :has() support.
157
+ document.body.classList.add('vidply-fullscreen-active');
158
+ // Make background content inert even in real-fullscreen mode —
159
+ // the overlay still leaves DOM focus reachable on some browsers.
160
+ player.pseudoFullscreen.makeBackgroundInert();
161
+ } else {
162
+ player.container.classList.remove(`${player.options.classPrefix}-fullscreen`);
163
+ document.body.classList.remove('vidply-fullscreen-active');
164
+ player.pseudoFullscreen.restoreBackgroundInteractivity();
165
+ player._disablePseudoFullscreen();
166
+ }
167
+
168
+ player.emit('fullscreenchange', isFullscreen);
169
+
170
+ if (player.controlBar) {
171
+ player.controlBar.updateFullscreenButton();
172
+ }
173
+
174
+ // Reposition the sign-language overlay after the transition
175
+ // completes; drag/resize affordances differ between windowed
176
+ // and fullscreen on mobile so we re-install them.
177
+ if (player.signLanguageWrapper && player.signLanguageWrapper.style.display !== 'none') {
178
+ const isMobileDevice = window.innerWidth < 768;
179
+ if (isMobileDevice) {
180
+ player.setupSignLanguageInteraction();
181
+ }
182
+
183
+ player.setManagedTimeout(() => {
184
+ requestAnimationFrame(() => {
185
+ player.storage.saveSignLanguagePreferences({ size: null });
186
+ if (player.signLanguageWrapper) {
187
+ player.signLanguageWrapper.style.width = isFullscreen ? '400px' : '280px';
188
+ }
189
+ player.constrainSignLanguagePosition();
190
+ });
191
+ }, 500);
192
+ }
193
+ };
194
+
195
+ const opts = { signal: player.lifecycleSignal };
196
+ document.addEventListener('fullscreenchange', player.fullscreenChangeHandler, opts);
197
+ document.addEventListener('webkitfullscreenchange', player.fullscreenChangeHandler, opts);
198
+ document.addEventListener('mozfullscreenchange', player.fullscreenChangeHandler, opts);
199
+ document.addEventListener('MSFullscreenChange', player.fullscreenChangeHandler, opts);
200
+ }
201
+
202
+ /**
203
+ * Tear down listeners that aren't covered by the Player's
204
+ * lifecycle AbortController. The `window.resize` and
205
+ * `document.fullscreenchange` listeners are already cleaned up
206
+ * via `{signal}`; only the ResizeObserver and old-Safari
207
+ * matchMedia listener need an explicit removal.
208
+ */
209
+ cleanup(): void {
210
+ const player = this.player;
211
+
212
+ if (player.resizeObserver) {
213
+ player.resizeObserver.disconnect();
214
+ player.resizeObserver = null;
215
+ }
216
+ player.resizeHandler = null;
217
+ player.fullscreenChangeHandler = null;
218
+
219
+ if (this.orientationQuery && this.orientationHandler) {
220
+ if (this.orientationQuery.removeEventListener) {
221
+ this.orientationQuery.removeEventListener('change', this.orientationHandler);
222
+ } else if ((this.orientationQuery as MediaQueryList).removeListener) {
223
+ (this.orientationQuery as MediaQueryList).removeListener(this.orientationHandler);
224
+ }
225
+ this.orientationQuery = null;
226
+ this.orientationHandler = null;
227
+ }
228
+ player.orientationQuery = null;
229
+ player.orientationHandler = null;
230
+ }
231
+ }