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
package/src/index.ts CHANGED
@@ -1,193 +1,196 @@
1
- /**
2
- * VidPly - Universal Video Player
3
- * Main Entry Point
4
- */
5
-
6
- import { Player } from './core/Player.js';
7
- import { observeForLazyInit } from './core/LazyInit.js';
8
- import { PlaylistManager } from './features/PlaylistManager.js';
9
- import { isForbiddenKey } from './utils/Sanitize.js';
10
- import type { PlayerOptions } from './types/options.js';
11
-
12
- // Re-export so existing consumers of `import { LazyHandle } from 'vidply'`
13
- // keep working; the canonical definition now lives in core/LazyInit.
14
- export type { LazyHandle } from './core/LazyInit.js';
15
-
16
- /**
17
- * Filter `__proto__` / `constructor` / `prototype` keys from an attacker-
18
- * influenced JSON object before merging it into the player options. This
19
- * avoids prototype-pollution turning a malformed `data-vidply-options`
20
- * attribute into a global gadget.
21
- */
22
- function sanitizeOptionsObject(input: unknown): Partial<PlayerOptions> {
23
- if (!input || typeof input !== 'object' || Array.isArray(input)) {
24
- return {};
25
- }
26
- const out: Record<string, unknown> = Object.create(null);
27
- for (const [key, value] of Object.entries(input as Record<string, unknown>)) {
28
- if (isForbiddenKey(key)) continue;
29
- out[key] = value;
30
- }
31
- return out as Partial<PlayerOptions>;
32
- }
33
-
34
- /**
35
- * Parse a `data-vidply-options` JSON string. Robust against malformed input
36
- * — a single broken attribute on one element no longer aborts the auto-init
37
- * loop for every other player on the page.
38
- */
39
- function parseInlineOptions(element: HTMLElement): Partial<PlayerOptions> {
40
- const raw = element.dataset.vidplyOptions;
41
- if (!raw) return {};
42
- try {
43
- const parsed: unknown = JSON.parse(raw);
44
- return sanitizeOptionsObject(parsed);
45
- } catch (err) {
46
- console.warn('[VidPly] Ignored malformed data-vidply-options:', err);
47
- return {};
48
- }
49
- }
50
-
51
- function initializePlayers(): void {
52
- const elements = document.querySelectorAll<HTMLElement>('[data-vidply]');
53
-
54
- elements.forEach((element) => {
55
- const options = parseInlineOptions(element);
56
- const dataOptions = parseDataAttributes(element.dataset);
57
- const mergedOptions: Partial<PlayerOptions> = { ...dataOptions, ...options };
58
-
59
- const lazyInit =
60
- element.dataset.vidplyLazy !== 'false' && mergedOptions.lazyInit !== false;
61
- const lazyMargin =
62
- element.dataset.vidplyLazyMargin || (mergedOptions.lazyMargin as string) || '500px';
63
-
64
- if (lazyInit && 'IntersectionObserver' in window) {
65
- observeForLazyInit<Partial<PlayerOptions>>(
66
- element,
67
- mergedOptions,
68
- lazyMargin,
69
- (target, opts) => { new Player(target, opts); }
70
- );
71
- } else {
72
- new Player(element, mergedOptions);
73
- }
74
- });
75
- }
76
-
77
- function parseDataAttributes(dataset: DOMStringMap): Partial<PlayerOptions> {
78
- const options: Record<string, unknown> = Object.create(null);
79
-
80
- const attributeMap: Record<string, keyof PlayerOptions> = {
81
- signLanguageSrc: 'signLanguageSrc',
82
- signLanguageButton: 'signLanguageButton',
83
- signLanguagePosition: 'signLanguagePosition',
84
- signLanguageDisplayMode: 'signLanguageDisplayMode',
85
- audioDescriptionSrc: 'audioDescriptionSrc',
86
- audioDescriptionButton: 'audioDescriptionButton',
87
- autoplay: 'autoplay',
88
- loop: 'loop',
89
- muted: 'muted',
90
- controls: 'controls',
91
- poster: 'poster',
92
- width: 'width',
93
- height: 'height',
94
- language: 'language',
95
- captions: 'captions',
96
- captionsDefault: 'captionsDefault',
97
- transcript: 'transcript',
98
- transcriptButton: 'transcriptButton',
99
- keyboard: 'keyboard',
100
- responsive: 'responsive',
101
- pipButton: 'pipButton',
102
- fullscreenButton: 'fullscreenButton',
103
- floating: 'floating',
104
- floatingPosition: 'floatingPosition',
105
- floatingMinViewportWidth: 'floatingMinViewportWidth',
106
- lazyInit: 'lazyInit',
107
- lazyMargin: 'lazyMargin',
108
- theme: 'theme'
109
- };
110
-
111
- for (const [dataKey, optionKey] of Object.entries(attributeMap)) {
112
- if (isForbiddenKey(optionKey)) continue;
113
- const value = dataset[dataKey];
114
- if (value === undefined) continue;
115
- if (value === 'true') {
116
- options[optionKey] = true;
117
- } else if (value === 'false') {
118
- options[optionKey] = false;
119
- } else if (value !== '' && !Number.isNaN(Number(value))) {
120
- options[optionKey] = Number(value);
121
- } else {
122
- options[optionKey] = value;
123
- }
124
- }
125
-
126
- const signLanguageSources: Record<string, string> = Object.create(null);
127
- for (const key of Object.keys(dataset)) {
128
- if (key.startsWith('signLanguageSrc') && key !== 'signLanguageSrc') {
129
- const langMatch = key.match(/^signLanguageSrc([A-Z][a-z]*)$/);
130
- if (langMatch && langMatch[1]) {
131
- const langCode = langMatch[1].toLowerCase();
132
- const value = dataset[key];
133
- if (value !== undefined) {
134
- signLanguageSources[langCode] = value;
135
- }
136
- }
137
- }
138
- }
139
-
140
- if (Object.keys(signLanguageSources).length > 0) {
141
- options.signLanguageSources = signLanguageSources;
142
- if (dataset.signLanguageSrc && !options.signLanguageSrc) {
143
- options.signLanguageSrc = dataset.signLanguageSrc;
144
- }
145
- }
146
-
147
- if (dataset.vidplyLanguageFiles) {
148
- try {
149
- const parsed = JSON.parse(dataset.vidplyLanguageFiles);
150
- if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
151
- options.languageFiles = sanitizeOptionsObject(parsed) as Record<string, string>;
152
- }
153
- } catch (e) {
154
- console.warn('Invalid JSON in data-vidply-language-files:', e);
155
- }
156
- }
157
-
158
- if (dataset.vidplyLanguageFile) {
159
- try {
160
- const parsed = JSON.parse(dataset.vidplyLanguageFile);
161
- if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
162
- options.languageFiles = sanitizeOptionsObject(parsed) as Record<string, string>;
163
- }
164
- } catch {
165
- if (dataset.vidplyLanguageFileCode && dataset.vidplyLanguageFileUrl) {
166
- options.languageFile = dataset.vidplyLanguageFileCode;
167
- options.languageFileUrl = dataset.vidplyLanguageFileUrl;
168
- }
169
- }
170
- } else if (dataset.vidplyLanguageFileCode && dataset.vidplyLanguageFileUrl) {
171
- options.languageFile = dataset.vidplyLanguageFileCode;
172
- options.languageFileUrl = dataset.vidplyLanguageFileUrl;
173
- }
174
-
175
- return options as Partial<PlayerOptions>;
176
- }
177
-
178
- if (document.readyState === 'loading') {
179
- document.addEventListener('DOMContentLoaded', initializePlayers);
180
- } else {
181
- initializePlayers();
182
- }
183
-
184
- export { Player, PlaylistManager };
185
- export type { PlayerOptions } from './types/options.js';
186
- export type {
187
- PlayerEventMap,
188
- PlaylistTrack,
189
- FloatingChangeDetail
190
- } from './types/events.js';
191
- export type { PlayerState } from './types/state.js';
192
- export type { Renderer, QualityLevel } from './types/renderer.js';
193
- export default Player;
1
+ /**
2
+ * VidPly - Universal Video Player
3
+ * Main Entry Point
4
+ */
5
+
6
+ import { Player } from './core/Player.js';
7
+ import { observeForLazyInit } from './core/LazyInit.js';
8
+ import { PlaylistManager } from './features/PlaylistManager.js';
9
+ import { isForbiddenKey } from './utils/Sanitize.js';
10
+ import type { PlayerOptions } from './types/options.js';
11
+
12
+ // Re-export so existing consumers of `import { LazyHandle } from 'vidply'`
13
+ // keep working; the canonical definition now lives in core/LazyInit.
14
+ export type { LazyHandle } from './core/LazyInit.js';
15
+
16
+ /**
17
+ * Filter `__proto__` / `constructor` / `prototype` keys from an attacker-
18
+ * influenced JSON object before merging it into the player options. This
19
+ * avoids prototype-pollution turning a malformed `data-vidply-options`
20
+ * attribute into a global gadget.
21
+ */
22
+ function sanitizeOptionsObject(input: unknown): Partial<PlayerOptions> {
23
+ if (!input || typeof input !== 'object' || Array.isArray(input)) {
24
+ return {};
25
+ }
26
+ const out: Record<string, unknown> = Object.create(null);
27
+ for (const [key, value] of Object.entries(input as Record<string, unknown>)) {
28
+ if (isForbiddenKey(key)) continue;
29
+ out[key] = value;
30
+ }
31
+ return out as Partial<PlayerOptions>;
32
+ }
33
+
34
+ /**
35
+ * Parse a `data-vidply-options` JSON string. Robust against malformed input
36
+ * — a single broken attribute on one element no longer aborts the auto-init
37
+ * loop for every other player on the page.
38
+ */
39
+ function parseInlineOptions(element: HTMLElement): Partial<PlayerOptions> {
40
+ const raw = element.dataset.vidplyOptions;
41
+ if (!raw) return {};
42
+ try {
43
+ const parsed: unknown = JSON.parse(raw);
44
+ return sanitizeOptionsObject(parsed);
45
+ } catch (err) {
46
+ console.warn('[VidPly] Ignored malformed data-vidply-options:', err);
47
+ return {};
48
+ }
49
+ }
50
+
51
+ function initializePlayers(): void {
52
+ const elements = document.querySelectorAll<HTMLElement>('[data-vidply]');
53
+
54
+ elements.forEach((element) => {
55
+ const options = parseInlineOptions(element);
56
+ const dataOptions = parseDataAttributes(element.dataset);
57
+ const mergedOptions: Partial<PlayerOptions> = { ...dataOptions, ...options };
58
+
59
+ const lazyInit =
60
+ element.dataset.vidplyLazy !== 'false' && mergedOptions.lazyInit !== false;
61
+ const lazyMargin =
62
+ element.dataset.vidplyLazyMargin || (mergedOptions.lazyMargin as string) || '500px';
63
+
64
+ if (lazyInit && 'IntersectionObserver' in window) {
65
+ observeForLazyInit<Partial<PlayerOptions>>(
66
+ element,
67
+ mergedOptions,
68
+ lazyMargin,
69
+ (target, opts) => { new Player(target, opts); }
70
+ );
71
+ } else {
72
+ new Player(element, mergedOptions);
73
+ }
74
+ });
75
+ }
76
+
77
+ function parseDataAttributes(dataset: DOMStringMap): Partial<PlayerOptions> {
78
+ const options: Record<string, unknown> = Object.create(null);
79
+
80
+ const attributeMap: Record<string, keyof PlayerOptions> = {
81
+ signLanguageSrc: 'signLanguageSrc',
82
+ signLanguageButton: 'signLanguageButton',
83
+ signLanguagePosition: 'signLanguagePosition',
84
+ signLanguageDisplayMode: 'signLanguageDisplayMode',
85
+ audioDescriptionSrc: 'audioDescriptionSrc',
86
+ audioDescriptionButton: 'audioDescriptionButton',
87
+ audioDescriptionMode: 'audioDescriptionMode',
88
+ audioDescriptionSpeech: 'audioDescriptionSpeech',
89
+ audioDescriptionExtended: 'audioDescriptionExtended',
90
+ autoplay: 'autoplay',
91
+ loop: 'loop',
92
+ muted: 'muted',
93
+ controls: 'controls',
94
+ poster: 'poster',
95
+ width: 'width',
96
+ height: 'height',
97
+ language: 'language',
98
+ captions: 'captions',
99
+ captionsDefault: 'captionsDefault',
100
+ transcript: 'transcript',
101
+ transcriptButton: 'transcriptButton',
102
+ keyboard: 'keyboard',
103
+ responsive: 'responsive',
104
+ pipButton: 'pipButton',
105
+ fullscreenButton: 'fullscreenButton',
106
+ floating: 'floating',
107
+ floatingPosition: 'floatingPosition',
108
+ floatingMinViewportWidth: 'floatingMinViewportWidth',
109
+ lazyInit: 'lazyInit',
110
+ lazyMargin: 'lazyMargin',
111
+ theme: 'theme'
112
+ };
113
+
114
+ for (const [dataKey, optionKey] of Object.entries(attributeMap)) {
115
+ if (isForbiddenKey(optionKey)) continue;
116
+ const value = dataset[dataKey];
117
+ if (value === undefined) continue;
118
+ if (value === 'true') {
119
+ options[optionKey] = true;
120
+ } else if (value === 'false') {
121
+ options[optionKey] = false;
122
+ } else if (value !== '' && !Number.isNaN(Number(value))) {
123
+ options[optionKey] = Number(value);
124
+ } else {
125
+ options[optionKey] = value;
126
+ }
127
+ }
128
+
129
+ const signLanguageSources: Record<string, string> = Object.create(null);
130
+ for (const key of Object.keys(dataset)) {
131
+ if (key.startsWith('signLanguageSrc') && key !== 'signLanguageSrc') {
132
+ const langMatch = key.match(/^signLanguageSrc([A-Z][a-z]*)$/);
133
+ if (langMatch && langMatch[1]) {
134
+ const langCode = langMatch[1].toLowerCase();
135
+ const value = dataset[key];
136
+ if (value !== undefined) {
137
+ signLanguageSources[langCode] = value;
138
+ }
139
+ }
140
+ }
141
+ }
142
+
143
+ if (Object.keys(signLanguageSources).length > 0) {
144
+ options.signLanguageSources = signLanguageSources;
145
+ if (dataset.signLanguageSrc && !options.signLanguageSrc) {
146
+ options.signLanguageSrc = dataset.signLanguageSrc;
147
+ }
148
+ }
149
+
150
+ if (dataset.vidplyLanguageFiles) {
151
+ try {
152
+ const parsed = JSON.parse(dataset.vidplyLanguageFiles);
153
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
154
+ options.languageFiles = sanitizeOptionsObject(parsed) as Record<string, string>;
155
+ }
156
+ } catch (e) {
157
+ console.warn('Invalid JSON in data-vidply-language-files:', e);
158
+ }
159
+ }
160
+
161
+ if (dataset.vidplyLanguageFile) {
162
+ try {
163
+ const parsed = JSON.parse(dataset.vidplyLanguageFile);
164
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
165
+ options.languageFiles = sanitizeOptionsObject(parsed) as Record<string, string>;
166
+ }
167
+ } catch {
168
+ if (dataset.vidplyLanguageFileCode && dataset.vidplyLanguageFileUrl) {
169
+ options.languageFile = dataset.vidplyLanguageFileCode;
170
+ options.languageFileUrl = dataset.vidplyLanguageFileUrl;
171
+ }
172
+ }
173
+ } else if (dataset.vidplyLanguageFileCode && dataset.vidplyLanguageFileUrl) {
174
+ options.languageFile = dataset.vidplyLanguageFileCode;
175
+ options.languageFileUrl = dataset.vidplyLanguageFileUrl;
176
+ }
177
+
178
+ return options as Partial<PlayerOptions>;
179
+ }
180
+
181
+ if (document.readyState === 'loading') {
182
+ document.addEventListener('DOMContentLoaded', initializePlayers);
183
+ } else {
184
+ initializePlayers();
185
+ }
186
+
187
+ export { Player, PlaylistManager };
188
+ export type { PlayerOptions } from './types/options.js';
189
+ export type {
190
+ PlayerEventMap,
191
+ PlaylistTrack,
192
+ FloatingChangeDetail
193
+ } from './types/events.js';
194
+ export type { PlayerState } from './types/state.js';
195
+ export type { Renderer, QualityLevel } from './types/renderer.js';
196
+ export default Player;