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
@@ -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
+ }