@cloudnest/redxplyr 1.0.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 (127) hide show
  1. package/.editorconfig +10 -0
  2. package/.gitpod.yml +6 -0
  3. package/.node-version +1 -0
  4. package/.prettierrc +7 -0
  5. package/.stickler.yml +5 -0
  6. package/.stylelintrc.json +26 -0
  7. package/CHANGELOG.md +16 -0
  8. package/CONTRIBUTING.md +34 -0
  9. package/CONTROLS.md +49 -0
  10. package/Dockerfile +32 -0
  11. package/LICENSE.md +22 -0
  12. package/README.md +194 -0
  13. package/cspell.json +48 -0
  14. package/dist/redxplyr.css +1 -0
  15. package/dist/redxplyr.js +8801 -0
  16. package/dist/redxplyr.min.js +2 -0
  17. package/dist/redxplyr.min.js.map +1 -0
  18. package/dist/redxplyr.min.mjs +1 -0
  19. package/dist/redxplyr.min.mjs.map +1 -0
  20. package/dist/redxplyr.mjs +8793 -0
  21. package/dist/redxplyr.polyfilled.js +9294 -0
  22. package/dist/redxplyr.polyfilled.min.js +2 -0
  23. package/dist/redxplyr.polyfilled.min.js.map +1 -0
  24. package/dist/redxplyr.polyfilled.min.mjs +1 -0
  25. package/dist/redxplyr.polyfilled.min.mjs.map +1 -0
  26. package/dist/redxplyr.polyfilled.mjs +9286 -0
  27. package/dist/redxplyr.svg +1 -0
  28. package/eslint.config.mjs +39 -0
  29. package/gulpfile.js +8 -0
  30. package/package.json +114 -0
  31. package/pnpm-workspace.yaml +8 -0
  32. package/src/js/captions.js +411 -0
  33. package/src/js/config/defaults.js +459 -0
  34. package/src/js/config/states.js +10 -0
  35. package/src/js/config/types.js +34 -0
  36. package/src/js/console.js +28 -0
  37. package/src/js/controls.js +1870 -0
  38. package/src/js/fullscreen.js +305 -0
  39. package/src/js/html5.js +148 -0
  40. package/src/js/listeners.js +854 -0
  41. package/src/js/media.js +61 -0
  42. package/src/js/plugins/ads.js +647 -0
  43. package/src/js/plugins/preview-thumbnails.js +706 -0
  44. package/src/js/plugins/vimeo.js +443 -0
  45. package/src/js/plugins/youtube.js +451 -0
  46. package/src/js/plyr.d.ts +729 -0
  47. package/src/js/plyr.js +1291 -0
  48. package/src/js/plyr.polyfilled.js +13 -0
  49. package/src/js/source.js +155 -0
  50. package/src/js/storage.js +70 -0
  51. package/src/js/support.js +100 -0
  52. package/src/js/ui.js +297 -0
  53. package/src/js/utils/animation.js +33 -0
  54. package/src/js/utils/arrays.js +23 -0
  55. package/src/js/utils/browser.js +21 -0
  56. package/src/js/utils/elements.js +263 -0
  57. package/src/js/utils/events.js +116 -0
  58. package/src/js/utils/fetch.js +45 -0
  59. package/src/js/utils/i18n.js +47 -0
  60. package/src/js/utils/is.js +81 -0
  61. package/src/js/utils/load-image.js +19 -0
  62. package/src/js/utils/load-script.js +14 -0
  63. package/src/js/utils/load-sprite.js +77 -0
  64. package/src/js/utils/numbers.js +17 -0
  65. package/src/js/utils/objects.js +43 -0
  66. package/src/js/utils/promise.js +14 -0
  67. package/src/js/utils/strings.js +80 -0
  68. package/src/js/utils/style.js +148 -0
  69. package/src/js/utils/time.js +36 -0
  70. package/src/js/utils/urls.js +40 -0
  71. package/src/sass/base.scss +69 -0
  72. package/src/sass/components/badges.scss +12 -0
  73. package/src/sass/components/captions.scss +58 -0
  74. package/src/sass/components/control.scss +52 -0
  75. package/src/sass/components/controls.scss +65 -0
  76. package/src/sass/components/menus.scss +205 -0
  77. package/src/sass/components/poster.scss +27 -0
  78. package/src/sass/components/progress.scss +107 -0
  79. package/src/sass/components/sliders.scss +99 -0
  80. package/src/sass/components/times.scss +20 -0
  81. package/src/sass/components/tooltips.scss +91 -0
  82. package/src/sass/components/volume.scss +18 -0
  83. package/src/sass/lib/animation.scss +31 -0
  84. package/src/sass/lib/css-vars.scss +103 -0
  85. package/src/sass/lib/functions.scss +3 -0
  86. package/src/sass/lib/mixins.scss +82 -0
  87. package/src/sass/plugins/ads.scss +53 -0
  88. package/src/sass/plugins/preview-thumbnails/index.scss +121 -0
  89. package/src/sass/plugins/preview-thumbnails/settings.scss +17 -0
  90. package/src/sass/plyr.scss +46 -0
  91. package/src/sass/settings/badges.scss +7 -0
  92. package/src/sass/settings/breakpoints.scss +9 -0
  93. package/src/sass/settings/captions.scss +10 -0
  94. package/src/sass/settings/colors.scss +18 -0
  95. package/src/sass/settings/controls.scss +30 -0
  96. package/src/sass/settings/cosmetics.scss +5 -0
  97. package/src/sass/settings/helpers.scss +7 -0
  98. package/src/sass/settings/menus.scss +13 -0
  99. package/src/sass/settings/progress.scss +18 -0
  100. package/src/sass/settings/sliders.scss +39 -0
  101. package/src/sass/settings/tooltips.scss +11 -0
  102. package/src/sass/settings/type.scss +16 -0
  103. package/src/sass/states/fullscreen.scss +15 -0
  104. package/src/sass/types/audio.scss +61 -0
  105. package/src/sass/types/video.scss +170 -0
  106. package/src/sass/utils/animation.scss +7 -0
  107. package/src/sass/utils/hidden.scss +28 -0
  108. package/src/sprite/plyr-airplay.svg +8 -0
  109. package/src/sprite/plyr-captions-off.svg +7 -0
  110. package/src/sprite/plyr-captions-on.svg +7 -0
  111. package/src/sprite/plyr-download.svg +8 -0
  112. package/src/sprite/plyr-enter-fullscreen.svg +4 -0
  113. package/src/sprite/plyr-exit-fullscreen.svg +4 -0
  114. package/src/sprite/plyr-fast-forward.svg +3 -0
  115. package/src/sprite/plyr-logo-vimeo.svg +6 -0
  116. package/src/sprite/plyr-logo-youtube.svg +6 -0
  117. package/src/sprite/plyr-muted.svg +8 -0
  118. package/src/sprite/plyr-pause.svg +8 -0
  119. package/src/sprite/plyr-pip.svg +6 -0
  120. package/src/sprite/plyr-play.svg +5 -0
  121. package/src/sprite/plyr-restart.svg +5 -0
  122. package/src/sprite/plyr-rewind.svg +3 -0
  123. package/src/sprite/plyr-settings.svg +5 -0
  124. package/src/sprite/plyr-volume.svg +11 -0
  125. package/tasks/build.js +226 -0
  126. package/tasks/deploy.js +216 -0
  127. package/tasks/utils/publish.js +34 -0
@@ -0,0 +1,443 @@
1
+ // ==========================================================================
2
+ // Vimeo plugin
3
+ // ==========================================================================
4
+
5
+ import captions from '../captions';
6
+ import controls from '../controls';
7
+ import ui from '../ui';
8
+ import { createElement, replaceElement, toggleClass } from '../utils/elements';
9
+ import { triggerEvent } from '../utils/events';
10
+ import fetch from '../utils/fetch';
11
+ import is from '../utils/is';
12
+ import loadScript from '../utils/load-script';
13
+ import { format, stripHTML } from '../utils/strings';
14
+ import { roundAspectRatio, setAspectRatio } from '../utils/style';
15
+ import { buildUrlParams } from '../utils/urls';
16
+
17
+ // Parse Vimeo ID from URL
18
+ function parseId(url) {
19
+ if (is.empty(url)) {
20
+ return null;
21
+ }
22
+
23
+ if (is.number(Number(url))) {
24
+ return url;
25
+ }
26
+
27
+ // eslint-disable-next-line regexp/optimal-quantifier-concatenation
28
+ const regex = /^.*(vimeo.com\/|video\/)(\d+).*/;
29
+ const match = url.match(regex);
30
+ return match ? match[2] : url;
31
+ }
32
+
33
+ // Try to extract a hash for private videos from the URL
34
+ function parseHash(url) {
35
+ /* This regex matches a hexadecimal hash if given in any of these forms:
36
+ * - [https://player.]vimeo.com/video/{id}/{hash}[?params]
37
+ * - [https://player.]vimeo.com/video/{id}?h={hash}[&params]
38
+ * - [https://player.]vimeo.com/video/{id}?[params]&h={hash}
39
+ * - video/{id}/{hash}
40
+ * If matched, the hash is available in capture group 4
41
+ */
42
+ const regex = /^.*(vimeo.com\/|video\/)(\d+)(\?.*h=|\/)+([\d,a-f]+)/;
43
+ const found = url.match(regex);
44
+
45
+ return found && found.length === 5 ? found[4] : null;
46
+ }
47
+
48
+ // Set playback state and trigger change (only on actual change)
49
+ function assurePlaybackState(play) {
50
+ if (play && !this.embed.hasPlayed) {
51
+ this.embed.hasPlayed = true;
52
+ }
53
+ if (this.media.paused === play) {
54
+ this.media.paused = !play;
55
+ triggerEvent.call(this, this.media, play ? 'play' : 'pause');
56
+ }
57
+ }
58
+
59
+ const vimeo = {
60
+ setup() {
61
+ const player = this;
62
+
63
+ // Add embed class for responsive
64
+ toggleClass(player.elements.wrapper, player.config.classNames.embed, true);
65
+
66
+ // Set speed options from config
67
+ player.options.speed = player.config.speed.options;
68
+
69
+ // Set initial ratio
70
+ setAspectRatio.call(player);
71
+
72
+ // Load the SDK if not already
73
+ if (!is.object(window.Vimeo)) {
74
+ loadScript(player.config.urls.vimeo.sdk)
75
+ .then(() => {
76
+ vimeo.ready.call(player);
77
+ })
78
+ .catch((error) => {
79
+ player.debug.warn('Vimeo SDK (player.js) failed to load', error);
80
+ });
81
+ }
82
+ else {
83
+ vimeo.ready.call(player);
84
+ }
85
+ },
86
+
87
+ // API Ready
88
+ ready() {
89
+ const player = this;
90
+ const config = player.config.vimeo;
91
+ const { premium, referrerPolicy, ...frameParams } = config;
92
+ // Get the source URL or ID
93
+ let source = player.media.getAttribute('src');
94
+ let hash = '';
95
+ // Get from <div> if needed
96
+ if (is.empty(source)) {
97
+ source = player.media.getAttribute(player.config.attributes.embed.id);
98
+ // hash can also be set as attribute on the <div>
99
+ hash = player.media.getAttribute(player.config.attributes.embed.hash);
100
+ }
101
+ else {
102
+ hash = parseHash(source);
103
+ }
104
+ const hashParam = hash ? { h: hash } : {};
105
+
106
+ // If the owner has a pro or premium account then we can hide controls etc
107
+ if (premium) {
108
+ Object.assign(frameParams, {
109
+ controls: false,
110
+ sidedock: false,
111
+ });
112
+ }
113
+
114
+ // Get Vimeo params for the iframe
115
+ const params = buildUrlParams({
116
+ loop: player.config.loop.active,
117
+ autoplay: player.autoplay,
118
+ muted: player.muted,
119
+ gesture: 'media',
120
+ playsinline: player.config.playsinline,
121
+ // hash has to be added to iframe-URL
122
+ ...hashParam,
123
+ ...frameParams,
124
+ });
125
+
126
+ const id = parseId(source);
127
+ // Build an iframe
128
+ const iframe = createElement('iframe');
129
+ const src = format(player.config.urls.vimeo.iframe, id, params);
130
+ iframe.setAttribute('src', src);
131
+ iframe.setAttribute('allowfullscreen', '');
132
+ iframe.setAttribute(
133
+ 'allow',
134
+ ['autoplay', 'fullscreen', 'picture-in-picture', 'encrypted-media', 'accelerometer', 'gyroscope'].join('; '),
135
+ );
136
+
137
+ // Set the referrer policy if required
138
+ if (!is.empty(referrerPolicy)) {
139
+ iframe.setAttribute('referrerPolicy', referrerPolicy);
140
+ }
141
+
142
+ // Inject the package
143
+ if (premium || !config.customControls) {
144
+ iframe.setAttribute('data-poster', player.poster);
145
+ player.media = replaceElement(iframe, player.media);
146
+ }
147
+ else {
148
+ const wrapper = createElement('div', {
149
+ 'class': player.config.classNames.embedContainer,
150
+ 'data-poster': player.poster,
151
+ });
152
+ wrapper.appendChild(iframe);
153
+ player.media = replaceElement(wrapper, player.media);
154
+ }
155
+
156
+ // Get poster image
157
+ if (!config.customControls) {
158
+ fetch(format(player.config.urls.vimeo.api, src)).then((response) => {
159
+ if (is.empty(response) || !response.thumbnail_url) {
160
+ return;
161
+ }
162
+
163
+ // Set and show poster
164
+ ui.setPoster.call(player, response.thumbnail_url).catch(() => {});
165
+ });
166
+ }
167
+
168
+ // Setup instance
169
+ // https://github.com/vimeo/player.js
170
+ player.embed = new window.Vimeo.Player(iframe, {
171
+ autopause: player.config.autopause,
172
+ muted: player.muted,
173
+ });
174
+
175
+ player.media.paused = true;
176
+ player.media.currentTime = 0;
177
+
178
+ // Disable native text track rendering
179
+ if (player.supported.ui) {
180
+ player.embed.disableTextTrack();
181
+ }
182
+
183
+ // Create a faux HTML5 API using the Vimeo API
184
+ player.media.play = () => {
185
+ assurePlaybackState.call(player, true);
186
+ return player.embed.play();
187
+ };
188
+
189
+ player.media.pause = () => {
190
+ assurePlaybackState.call(player, false);
191
+ return player.embed.pause();
192
+ };
193
+
194
+ player.media.stop = () => {
195
+ player.pause();
196
+ player.currentTime = 0;
197
+ };
198
+
199
+ // Seeking
200
+ let { currentTime } = player.media;
201
+ Object.defineProperty(player.media, 'currentTime', {
202
+ get() {
203
+ return currentTime;
204
+ },
205
+ set(time) {
206
+ // Vimeo will automatically play on seek if the video hasn't been played before
207
+
208
+ // Get current paused state and volume etc
209
+ const { embed, media, paused, volume } = player;
210
+ const restorePause = paused && !embed.hasPlayed;
211
+
212
+ // Set seeking state and trigger event
213
+ media.seeking = true;
214
+ triggerEvent.call(player, media, 'seeking');
215
+
216
+ // If paused, mute until seek is complete
217
+ Promise.resolve(restorePause && embed.setVolume(0))
218
+ // Seek
219
+ .then(() => embed.setCurrentTime(time))
220
+ // Restore paused
221
+ .then(() => restorePause && embed.pause())
222
+ // Restore volume
223
+ .then(() => restorePause && embed.setVolume(volume))
224
+ .catch(() => {
225
+ // Do nothing
226
+ });
227
+ },
228
+ });
229
+
230
+ // Playback speed
231
+ let speed = player.config.speed.selected;
232
+ Object.defineProperty(player.media, 'playbackRate', {
233
+ get() {
234
+ return speed;
235
+ },
236
+ set(input) {
237
+ player.embed
238
+ .setPlaybackRate(input)
239
+ .then(() => {
240
+ speed = input;
241
+ triggerEvent.call(player, player.media, 'ratechange');
242
+ })
243
+ .catch(() => {
244
+ // Cannot set Playback Rate, Video is probably not on Pro account
245
+ player.options.speed = [1];
246
+ });
247
+ },
248
+ });
249
+
250
+ // Volume
251
+ let { volume } = player.config;
252
+ Object.defineProperty(player.media, 'volume', {
253
+ get() {
254
+ return volume;
255
+ },
256
+ set(input) {
257
+ player.embed.setVolume(input).then(() => {
258
+ volume = input;
259
+ triggerEvent.call(player, player.media, 'volumechange');
260
+ });
261
+ },
262
+ });
263
+
264
+ // Muted
265
+ let { muted } = player.config;
266
+ Object.defineProperty(player.media, 'muted', {
267
+ get() {
268
+ return muted;
269
+ },
270
+ set(input) {
271
+ const toggle = is.boolean(input) ? input : false;
272
+
273
+ player.embed.setMuted(toggle ? true : player.config.muted).then(() => {
274
+ muted = toggle;
275
+ triggerEvent.call(player, player.media, 'volumechange');
276
+ });
277
+ },
278
+ });
279
+
280
+ // Loop
281
+ let { loop } = player.config;
282
+ Object.defineProperty(player.media, 'loop', {
283
+ get() {
284
+ return loop;
285
+ },
286
+ set(input) {
287
+ const toggle = is.boolean(input) ? input : player.config.loop.active;
288
+
289
+ player.embed.setLoop(toggle).then(() => {
290
+ loop = toggle;
291
+ });
292
+ },
293
+ });
294
+
295
+ // Source
296
+ let currentSrc;
297
+ player.embed
298
+ .getVideoUrl()
299
+ .then((value) => {
300
+ currentSrc = value;
301
+ controls.setDownloadUrl.call(player);
302
+ })
303
+ .catch((error) => {
304
+ this.debug.warn(error);
305
+ });
306
+
307
+ Object.defineProperty(player.media, 'currentSrc', {
308
+ get() {
309
+ return currentSrc;
310
+ },
311
+ });
312
+
313
+ // Ended
314
+ Object.defineProperty(player.media, 'ended', {
315
+ get() {
316
+ return player.currentTime === player.duration;
317
+ },
318
+ });
319
+
320
+ // Set aspect ratio based on video size
321
+ Promise.all([player.embed.getVideoWidth(), player.embed.getVideoHeight()]).then((dimensions) => {
322
+ const [width, height] = dimensions;
323
+ player.embed.ratio = roundAspectRatio(width, height);
324
+ setAspectRatio.call(this);
325
+ });
326
+
327
+ // Set autopause
328
+ player.embed.setAutopause(player.config.autopause).then((state) => {
329
+ player.config.autopause = state;
330
+ });
331
+
332
+ // Get title
333
+ player.embed.getVideoTitle().then((title) => {
334
+ player.config.title = title;
335
+ ui.setTitle.call(this);
336
+ });
337
+
338
+ // Get current time
339
+ player.embed.getCurrentTime().then((value) => {
340
+ currentTime = value;
341
+ triggerEvent.call(player, player.media, 'timeupdate');
342
+ });
343
+
344
+ // Get duration
345
+ player.embed.getDuration().then((value) => {
346
+ player.media.duration = value;
347
+ triggerEvent.call(player, player.media, 'durationchange');
348
+ });
349
+
350
+ // Get captions
351
+ player.embed.getTextTracks().then((tracks) => {
352
+ player.media.textTracks = tracks;
353
+ captions.setup.call(player);
354
+ });
355
+
356
+ player.embed.on('cuechange', ({ cues = [] }) => {
357
+ const strippedCues = cues.map(cue => stripHTML(cue.text));
358
+ captions.updateCues.call(player, strippedCues);
359
+ });
360
+
361
+ player.embed.on('loaded', () => {
362
+ // Assure state and events are updated on autoplay
363
+ player.embed.getPaused().then((paused) => {
364
+ assurePlaybackState.call(player, !paused);
365
+ if (!paused) {
366
+ triggerEvent.call(player, player.media, 'playing');
367
+ }
368
+ });
369
+
370
+ if (is.element(player.embed.element) && player.supported.ui) {
371
+ const frame = player.embed.element;
372
+
373
+ // Fix keyboard focus issues
374
+ // https://github.com/xgauravyaduvanshii/redxplyr/issues/317
375
+ frame.setAttribute('tabindex', -1);
376
+ }
377
+ });
378
+
379
+ player.embed.on('bufferstart', () => {
380
+ triggerEvent.call(player, player.media, 'waiting');
381
+ });
382
+
383
+ player.embed.on('bufferend', () => {
384
+ triggerEvent.call(player, player.media, 'playing');
385
+ });
386
+
387
+ player.embed.on('play', () => {
388
+ assurePlaybackState.call(player, true);
389
+ triggerEvent.call(player, player.media, 'playing');
390
+ });
391
+
392
+ player.embed.on('pause', () => {
393
+ assurePlaybackState.call(player, false);
394
+ });
395
+
396
+ player.embed.on('timeupdate', (data) => {
397
+ player.media.seeking = false;
398
+ currentTime = data.seconds;
399
+ triggerEvent.call(player, player.media, 'timeupdate');
400
+ });
401
+
402
+ player.embed.on('progress', (data) => {
403
+ player.media.buffered = data.percent;
404
+ triggerEvent.call(player, player.media, 'progress');
405
+
406
+ // Check all loaded
407
+ if (Number.parseInt(data.percent, 10) === 1) {
408
+ triggerEvent.call(player, player.media, 'canplaythrough');
409
+ }
410
+
411
+ // Get duration as if we do it before load, it gives an incorrect value
412
+ // https://github.com/xgauravyaduvanshii/redxplyr/issues/891
413
+ player.embed.getDuration().then((value) => {
414
+ if (value !== player.media.duration) {
415
+ player.media.duration = value;
416
+ triggerEvent.call(player, player.media, 'durationchange');
417
+ }
418
+ });
419
+ });
420
+
421
+ player.embed.on('seeked', () => {
422
+ player.media.seeking = false;
423
+ triggerEvent.call(player, player.media, 'seeked');
424
+ });
425
+
426
+ player.embed.on('ended', () => {
427
+ player.media.paused = true;
428
+ triggerEvent.call(player, player.media, 'ended');
429
+ });
430
+
431
+ player.embed.on('error', (detail) => {
432
+ player.media.error = detail;
433
+ triggerEvent.call(player, player.media, 'error');
434
+ });
435
+
436
+ // Rebuild UI
437
+ if (config.customControls) {
438
+ setTimeout(() => ui.build.call(player), 0);
439
+ }
440
+ },
441
+ };
442
+
443
+ export default vimeo;