@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 @@
1
+ <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><symbol id="plyr-airplay" viewBox="0 0 18 18"><path d="M16 1H2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h3v-2H3V3h12v8h-2v2h3a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1"/><path d="M4 17h10l-5-6z"/></symbol><symbol id="plyr-captions-off" viewBox="0 0 18 18"><path fill-opacity=".5" fill-rule="evenodd" d="M1 1c-.6 0-1 .4-1 1v11c0 .6.4 1 1 1h4.6l2.7 2.7c.2.2.4.3.7.3s.5-.1.7-.3l2.7-2.7H17c.6 0 1-.4 1-1V2c0-.6-.4-1-1-1zm4.52 10.15c1.99 0 3.01-1.32 3.28-2.41l-1.29-.39c-.19.66-.78 1.45-1.99 1.45-1.14 0-2.2-.83-2.2-2.34 0-1.61 1.12-2.37 2.18-2.37 1.23 0 1.78.75 1.95 1.43l1.3-.41C8.47 4.96 7.46 3.76 5.5 3.76c-1.9 0-3.61 1.44-3.61 3.7s1.65 3.69 3.63 3.69m7.57 0c1.99 0 3.01-1.32 3.28-2.41l-1.29-.39c-.19.66-.78 1.45-1.99 1.45-1.14 0-2.2-.83-2.2-2.34 0-1.61 1.12-2.37 2.18-2.37 1.23 0 1.78.75 1.95 1.43l1.3-.41c-.28-1.15-1.29-2.35-3.25-2.35-1.9 0-3.61 1.44-3.61 3.7s1.65 3.69 3.63 3.69"/></symbol><symbol id="plyr-captions-on" viewBox="0 0 18 18"><path fill-rule="evenodd" d="M1 1c-.6 0-1 .4-1 1v11c0 .6.4 1 1 1h4.6l2.7 2.7c.2.2.4.3.7.3s.5-.1.7-.3l2.7-2.7H17c.6 0 1-.4 1-1V2c0-.6-.4-1-1-1zm4.52 10.15c1.99 0 3.01-1.32 3.28-2.41l-1.29-.39c-.19.66-.78 1.45-1.99 1.45-1.14 0-2.2-.83-2.2-2.34 0-1.61 1.12-2.37 2.18-2.37 1.23 0 1.78.75 1.95 1.43l1.3-.41C8.47 4.96 7.46 3.76 5.5 3.76c-1.9 0-3.61 1.44-3.61 3.7s1.65 3.69 3.63 3.69m7.57 0c1.99 0 3.01-1.32 3.28-2.41l-1.29-.39c-.19.66-.78 1.45-1.99 1.45-1.14 0-2.2-.83-2.2-2.34 0-1.61 1.12-2.37 2.18-2.37 1.23 0 1.78.75 1.95 1.43l1.3-.41c-.28-1.15-1.29-2.35-3.25-2.35-1.9 0-3.61 1.44-3.61 3.7s1.65 3.69 3.63 3.69"/></symbol><symbol id="plyr-download" viewBox="0 0 18 18"><path d="M9 13c.3 0 .5-.1.7-.3L15.4 7 14 5.6l-4 4V1H8v8.6l-4-4L2.6 7l5.7 5.7c.2.2.4.3.7.3m-7 2h14v2H2z"/></symbol><symbol id="plyr-enter-fullscreen" viewBox="0 0 18 18"><path d="M10 3h3.6l-4 4L11 8.4l4-4V8h2V1h-7zM7 9.6l-4 4V10H1v7h7v-2H4.4l4-4z"/></symbol><symbol id="plyr-exit-fullscreen" viewBox="0 0 18 18"><path d="M1 12h3.6l-4 4L2 17.4l4-4V17h2v-7H1zM16 .6l-4 4V1h-2v7h7V6h-3.6l4-4z"/></symbol><symbol id="plyr-fast-forward" viewBox="0 0 18 18"><path d="M7.875 7.171 0 1v16l7.875-6.171V17L18 9 7.875 1z"/></symbol><symbol id="plyr-logo-vimeo" viewBox="0 0 18 18"><path d="M17 5.3c-.1 1.6-1.2 3.7-3.3 6.4-2.2 2.8-4 4.2-5.5 4.2-.9 0-1.7-.9-2.4-2.6C5 10.9 4.4 6 3 6c-.1 0-.5.3-1.2.8l-.8-1c.8-.7 3.5-3.4 4.7-3.5S7.7 3 8 4.8c.3 2 .8 6.1 1.8 6.1.9 0 2.5-3.4 2.6-4 .1-.9-.3-1.9-2.3-1.1.8-2.6 2.3-3.8 4.5-3.8q2.55.15 2.4 3.3"/></symbol><symbol id="plyr-logo-youtube" viewBox="0 0 18 18"><path d="M16.8 5.8c-.2-1.3-.8-2.2-2.2-2.4C12.4 3 9 3 9 3s-3.4 0-5.6.4C2 3.6 1.3 4.5 1.2 5.8 1 7.1 1 9 1 9s0 1.9.2 3.2.8 2.2 2.2 2.4C5.6 15 9 15 9 15s3.4 0 5.6-.4c1.4-.3 2-1.1 2.2-2.4S17 9 17 9s0-1.9-.2-3.2M7 12V6l5 3z"/></symbol><symbol id="plyr-muted" viewBox="0 0 18 18"><path d="m12.4 12.5 2.1-2.1 2.1 2.1 1.4-1.4L15.9 9 18 6.9l-1.4-1.4-2.1 2.1-2.1-2.1L11 6.9 13.1 9 11 11.1zM3.786 6.008H.714C.286 6.008 0 6.31 0 6.76v4.512c0 .452.286.752.714.752h3.072l4.071 3.858c.5.3 1.143 0 1.143-.602V2.752c0-.601-.643-.977-1.143-.601z"/></symbol><symbol id="plyr-pause" viewBox="0 0 18 18"><path d="M6 1H3c-.6 0-1 .4-1 1v14c0 .6.4 1 1 1h3c.6 0 1-.4 1-1V2c0-.6-.4-1-1-1m6 0c-.6 0-1 .4-1 1v14c0 .6.4 1 1 1h3c.6 0 1-.4 1-1V2c0-.6-.4-1-1-1z"/></symbol><symbol id="plyr-pip" viewBox="0 0 18 18"><path d="M13.293 3.293 7.022 9.564l1.414 1.414 6.271-6.271L17 7V1h-6z"/><path d="M13 15H3V5h5V3H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-6h-2z"/></symbol><symbol id="plyr-play" viewBox="0 0 18 18"><path d="M15.562 8.1 3.87.225c-.818-.562-1.87 0-1.87.9v15.75c0 .9 1.052 1.462 1.87.9L15.563 9.9c.584-.45.584-1.35 0-1.8"/></symbol><symbol id="plyr-restart" viewBox="0 0 18 18"><path d="m9.7 1.2.7 6.4 2.1-2.1c1.9 1.9 1.9 5.1 0 7-.9 1-2.2 1.5-3.5 1.5s-2.6-.5-3.5-1.5c-1.9-1.9-1.9-5.1 0-7 .6-.6 1.4-1.1 2.3-1.3l-.6-1.9C6 2.6 4.9 3.2 4 4.1 1.3 6.8 1.3 11.2 4 14c1.3 1.3 3.1 2 4.9 2 1.9 0 3.6-.7 4.9-2 2.7-2.7 2.7-7.1 0-9.9L16 1.9z"/></symbol><symbol id="plyr-rewind" viewBox="0 0 18 18"><path d="M10.125 1 0 9l10.125 8v-6.171L18 17V1l-7.875 6.171z"/></symbol><symbol id="plyr-settings" viewBox="0 0 18 18"><path d="M16.135 7.784a2 2 0 0 1-1.23-2.969c.322-.536.225-.998-.094-1.316l-.31-.31c-.318-.318-.78-.415-1.316-.094a2 2 0 0 1-2.969-1.23C10.065 1.258 9.669 1 9.219 1h-.438c-.45 0-.845.258-.997.865a2 2 0 0 1-2.969 1.23c-.536-.322-.999-.225-1.317.093l-.31.31c-.318.318-.415.781-.093 1.317a2 2 0 0 1-1.23 2.969C1.26 7.935 1 8.33 1 8.781v.438c0 .45.258.845.865.997a2 2 0 0 1 1.23 2.969c-.322.536-.225.998.094 1.316l.31.31c.319.319.782.415 1.316.094a2 2 0 0 1 2.969 1.23c.151.607.547.865.997.865h.438c.45 0 .845-.258.997-.865a2 2 0 0 1 2.969-1.23c.535.321.997.225 1.316-.094l.31-.31c.318-.318.415-.781.094-1.316a2 2 0 0 1 1.23-2.969c.607-.151.865-.547.865-.997v-.438c0-.451-.26-.846-.865-.997M9 12a3 3 0 1 1 0-6 3 3 0 0 1 0 6"/></symbol><symbol id="plyr-volume" viewBox="0 0 18 18"><path d="M15.6 3.3c-.4-.4-1-.4-1.4 0s-.4 1 0 1.4C15.4 5.9 16 7.4 16 9s-.6 3.1-1.8 4.3c-.4.4-.4 1 0 1.4.2.2.5.3.7.3.3 0 .5-.1.7-.3C17.1 13.2 18 11.2 18 9s-.9-4.2-2.4-5.7"/><path d="M11.282 5.282a.91.91 0 0 0 0 1.316c.735.735.995 1.458.995 2.402 0 .936-.425 1.917-.995 2.487a.91.91 0 0 0 0 1.316c.145.145.636.262 1.018.156a.7.7 0 0 0 .298-.156C13.773 11.733 14.13 10.16 14.13 9q.001-.255-.011-.51c-.053-.992-.319-2.005-1.522-3.208a.91.91 0 0 0-1.316 0m-7.495.726H.714C.286 6.008 0 6.31 0 6.76v4.512c0 .452.286.752.714.752h3.072l4.071 3.858c.5.3 1.143 0 1.143-.602V2.752c0-.601-.643-.977-1.143-.601z"/></symbol></svg>
@@ -0,0 +1,39 @@
1
+ import antfu from '@antfu/eslint-config';
2
+ import { FlatCompat } from '@eslint/eslintrc';
3
+
4
+ import globals from 'globals';
5
+
6
+ const compat = new FlatCompat({ baseDirectory: import.meta.dirname });
7
+
8
+ export default antfu({
9
+ formatters: {
10
+ css: true,
11
+ html: true,
12
+ markdown: 'prettier',
13
+ svg: 'prettier',
14
+ },
15
+ stylistic: {
16
+ semi: true,
17
+ spacedComment: true,
18
+ indent: 2,
19
+ quotes: 'single',
20
+ },
21
+ ignores: ['node_modules', 'dist', 'demo/v1/redxplyr*', '**/demo/v1/redxplyr*', 'src/js/plyr.d.ts'],
22
+ languageOptions: {
23
+ globals: {
24
+ ...globals.browser,
25
+ Hls: 'readonly',
26
+ jQuery: 'readonly',
27
+ Plyr: 'readonly',
28
+ },
29
+ },
30
+ }, ...compat.config({
31
+ rules: {
32
+ 'antfu/if-newline': 'off',
33
+ },
34
+ }, {
35
+ files: ['**/*.md'],
36
+ rules: {
37
+ 'style/max-len': 'off',
38
+ },
39
+ }));
package/gulpfile.js ADDED
@@ -0,0 +1,8 @@
1
+ // ==========================================================================
2
+ // Gulp build script
3
+ // ==========================================================================
4
+
5
+ export { default } from './tasks/build.js';
6
+
7
+ export * from './tasks/build.js';
8
+ export * from './tasks/deploy.js';
package/package.json ADDED
@@ -0,0 +1,114 @@
1
+ {
2
+ "name": "@cloudnest/redxplyr",
3
+ "type": "module",
4
+ "version": "1.0.0",
5
+ "description": "A polished, accessible and customizable HTML5, YouTube, Vimeo and HLS media player",
6
+ "author": "xgauravyaduvanshii <xgauravyaduvanshii@gmail.com>",
7
+ "license": "MIT",
8
+ "homepage": "https://redxplyr.flyingdarkdev.com",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/xgauravyaduvanshii/redxplyr.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/xgauravyaduvanshii/redxplyr/issues"
15
+ },
16
+ "keywords": [
17
+ "HTML5 Video",
18
+ "HTML5 Audio",
19
+ "Media Player",
20
+ "DASH",
21
+ "Shaka",
22
+ "WordPress",
23
+ "HLS"
24
+ ],
25
+ "exports": {
26
+ ".": {
27
+ "types": "./src/js/plyr.d.ts",
28
+ "import": "./dist/redxplyr.mjs",
29
+ "require": "./dist/redxplyr.js",
30
+ "browser": "./dist/redxplyr.min.js",
31
+ "default": "./dist/redxplyr.js"
32
+ },
33
+ "./dist/redxplyr.css": "./dist/redxplyr.css",
34
+ "./css": "./dist/redxplyr.css",
35
+ "./redxplyr.scss": "./src/sass/plyr.scss",
36
+ "./plyr.scss": "./src/sass/plyr.scss"
37
+ },
38
+ "sass": "./src/sass/plyr.scss",
39
+ "style": "./dist/redxplyr.css",
40
+ "browserslist": "> 1%",
41
+ "scripts": {
42
+ "build": "gulp build",
43
+ "lint": "stylelint **/*.scss && eslint src demo && pnpm remark",
44
+ "lint:fix": "stylelint **/*.scss --fix && eslint src demo --fix",
45
+ "remark": "remark -f --use 'validate-links=repository:\"xgauravyaduvanshii/redxplyr\"' '{,!(node_modules),.?**/}*.md'",
46
+ "deploy": "pnpm run lint && gulp prepare && gulp build && gulp deploy",
47
+ "spellcheck": "cspell \"**/*.{js,md,scss,json}\" --no-must-find-files",
48
+ "start": "gulp"
49
+ },
50
+ "dependencies": {
51
+ "core-js": "^3.45.1",
52
+ "custom-event-polyfill": "^1.0.7",
53
+ "loadjs": "^4.3.0",
54
+ "rangetouch": "^2.0.1",
55
+ "url-polyfill": "^1.1.13"
56
+ },
57
+ "devDependencies": {
58
+ "@antfu/eslint-config": "^5.2.1",
59
+ "@aws-sdk/client-s3": "^3.876.0",
60
+ "@babel/core": "^7.28.3",
61
+ "@babel/plugin-proposal-class-properties": "^7.18.6",
62
+ "@babel/plugin-proposal-optional-chaining": "^7.21.0",
63
+ "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1",
64
+ "@babel/preset-env": "^7.28.3",
65
+ "@eslint/eslintrc": "^3.3.1",
66
+ "@prettier/plugin-xml": "^3.4.2",
67
+ "@rollup/plugin-babel": "^6.0.4",
68
+ "@rollup/plugin-commonjs": "^28.0.6",
69
+ "@rollup/plugin-node-resolve": "^16.0.1",
70
+ "autoprefixer": "^10.4.21",
71
+ "aws-sdk": "^2.1692.0",
72
+ "babel-eslint": "^10.1.0",
73
+ "browser-sync": "^3.0.4",
74
+ "colorette": "2.0.20",
75
+ "cspell": "^9.2.0",
76
+ "cssnano": "^7.1.1",
77
+ "del": "^8.0.0",
78
+ "dotenv": "^17.2.1",
79
+ "eslint": "^9.34.0",
80
+ "eslint-plugin-format": "^1.0.1",
81
+ "fancy-log": "^2.0.0",
82
+ "git-branch": "^2.0.1",
83
+ "globals": "^16.3.0",
84
+ "gulp": "^5.0.1",
85
+ "gulp-better-rollup": "^4.0.1",
86
+ "gulp-filter": "^9.0.1",
87
+ "gulp-header": "^2.0.9",
88
+ "gulp-if": "^3.0.0",
89
+ "gulp-imagemin": "^9.1.0",
90
+ "gulp-open": "^3.0.1",
91
+ "gulp-plumber": "^1.2.1",
92
+ "gulp-postcss": "^10.0.0",
93
+ "gulp-rename": "^2.1.0",
94
+ "gulp-replace": "^1.1.4",
95
+ "gulp-sass": "^6.0.1",
96
+ "gulp-size": "^5.0.0",
97
+ "gulp-sourcemaps": "^3.0.0",
98
+ "gulp-svgstore": "^9.0.0",
99
+ "gulp-terser": "^2.1.0",
100
+ "imagemin-svgo": "^11.0.1",
101
+ "mime": "^4.0.7",
102
+ "postcss": "^8.5.6",
103
+ "postcss-custom-properties": "^14.0.6",
104
+ "postcss-scss": "^4.0.9",
105
+ "remark-cli": "^12.0.1",
106
+ "remark-validate-links": "^13.1.0",
107
+ "rollup": "^4.48.1",
108
+ "sass": "^1.91.0",
109
+ "stylelint": "^16.23.1",
110
+ "stylelint-config-sass-guidelines": "^12.1.0",
111
+ "stylelint-selector-bem-pattern": "^4.0.1",
112
+ "through2": "^4.0.2"
113
+ }
114
+ }
@@ -0,0 +1,8 @@
1
+ allowBuilds:
2
+ '@parcel/watcher': true
3
+ aws-sdk: true
4
+ core-js: true
5
+ es5-ext: true
6
+ gifsicle: true
7
+ mozjpeg: true
8
+ optipng-bin: true
@@ -0,0 +1,411 @@
1
+ // ==========================================================================
2
+ // Plyr Captions
3
+ // TODO: Create as class
4
+ // ==========================================================================
5
+
6
+ import controls from './controls';
7
+ import support from './support';
8
+ import { dedupe } from './utils/arrays';
9
+ import browser from './utils/browser';
10
+ import {
11
+ createElement,
12
+ emptyElement,
13
+ getAttributesFromSelector,
14
+ insertAfter,
15
+ removeElement,
16
+ toggleClass,
17
+ } from './utils/elements';
18
+ import { on, triggerEvent } from './utils/events';
19
+ import fetch from './utils/fetch';
20
+ import i18n from './utils/i18n';
21
+ import is from './utils/is';
22
+ import { getHTML } from './utils/strings';
23
+ import { parseUrl } from './utils/urls';
24
+
25
+ const captions = {
26
+ // Setup captions
27
+ setup() {
28
+ // Requires UI support
29
+ if (!this.supported.ui) {
30
+ return;
31
+ }
32
+
33
+ // Only Vimeo and HTML5 video supported at this point
34
+ if (!this.isVideo || this.isYouTube || (this.isHTML5 && !support.textTracks)) {
35
+ // Clear menu and hide
36
+ if (
37
+ is.array(this.config.controls)
38
+ && this.config.controls.includes('settings')
39
+ && this.config.settings.includes('captions')
40
+ ) {
41
+ controls.setCaptionsMenu.call(this);
42
+ }
43
+
44
+ return;
45
+ }
46
+
47
+ // Inject the container
48
+ if (!is.element(this.elements.captions)) {
49
+ this.elements.captions = createElement('div', getAttributesFromSelector(this.config.selectors.captions));
50
+ this.elements.captions.setAttribute('dir', 'auto');
51
+
52
+ insertAfter(this.elements.captions, this.elements.wrapper);
53
+ }
54
+
55
+ // Fix IE captions if CORS is used
56
+ // Fetch captions and inject as blobs instead (data URIs not supported!)
57
+ if (browser.isIE && window.URL) {
58
+ const elements = this.media.querySelectorAll('track');
59
+
60
+ Array.from(elements).forEach((track) => {
61
+ const src = track.getAttribute('src');
62
+ const url = parseUrl(src);
63
+
64
+ if (
65
+ url !== null
66
+ && url.hostname !== window.location.href.hostname
67
+ && ['http:', 'https:'].includes(url.protocol)
68
+ ) {
69
+ fetch(src, 'blob')
70
+ .then((blob) => {
71
+ track.setAttribute('src', window.URL.createObjectURL(blob));
72
+ })
73
+ .catch(() => {
74
+ removeElement(track);
75
+ });
76
+ }
77
+ });
78
+ }
79
+
80
+ // Get and set initial data
81
+ // The "preferred" options are not realized unless / until the wanted language has a match
82
+ // * languages: Array of user's browser languages.
83
+ // * language: The language preferred by user settings or config
84
+ // * active: The state preferred by user settings or config
85
+ // * toggled: The real captions state
86
+
87
+ const browserLanguages = navigator.languages || [navigator.language || navigator.userLanguage || 'en'];
88
+ const languages = dedupe(browserLanguages.map(language => language.split('-')[0]));
89
+ let language = (this.storage.get('language') || this.captions.language || this.config.captions.language || 'auto').toLowerCase();
90
+
91
+ // Use first browser language when language is 'auto'
92
+ if (language === 'auto') {
93
+ [language] = languages;
94
+ }
95
+
96
+ let active = this.storage.get('captions') || this.captions.active;
97
+ if (!is.boolean(active)) {
98
+ ({ active } = this.config.captions);
99
+ }
100
+
101
+ Object.assign(this.captions, {
102
+ toggled: false,
103
+ active,
104
+ language,
105
+ languages,
106
+ });
107
+
108
+ // Watch changes to textTracks and update captions menu
109
+ if (this.isHTML5) {
110
+ const trackEvents = this.config.captions.update ? 'addtrack removetrack' : 'removetrack';
111
+ on.call(this, this.media.textTracks, trackEvents, captions.update.bind(this));
112
+ }
113
+
114
+ // Update available languages in list next tick (the event must not be triggered before the listeners)
115
+ setTimeout(captions.update.bind(this), 0);
116
+ },
117
+
118
+ // Update available language options in settings based on tracks
119
+ update() {
120
+ const tracks = captions.getTracks.call(this, true);
121
+ // Get the wanted language
122
+ const { active, language, meta, currentTrackNode } = this.captions;
123
+ const languageExists = Boolean(tracks.find(track => track.language === language));
124
+
125
+ // Handle tracks (add event listener and "pseudo"-default)
126
+ if (this.isHTML5 && this.isVideo) {
127
+ tracks
128
+ .filter(track => !meta.get(track))
129
+ .forEach((track) => {
130
+ this.debug.log('Track added', track);
131
+
132
+ // Attempt to store if the original dom element was "default"
133
+ meta.set(track, {
134
+ default: track.mode === 'showing',
135
+ });
136
+
137
+ // Turn off native caption rendering to avoid double captions
138
+ // Note: mode='hidden' forces a track to download. To ensure every track
139
+ // isn't downloaded at once, only 'showing' tracks should be reassigned
140
+
141
+ if (track.mode === 'showing') {
142
+ track.mode = 'hidden';
143
+ }
144
+
145
+ // Add event listener for cue changes
146
+ on.call(this, track, 'cuechange', () => captions.updateCues.call(this));
147
+ });
148
+ }
149
+
150
+ // Update language first time it matches, or if the previous matching track was removed
151
+ if ((languageExists && this.language !== language) || !tracks.includes(currentTrackNode)) {
152
+ captions.setLanguage.call(this, language);
153
+ captions.toggle.call(this, active && languageExists);
154
+ }
155
+
156
+ // Enable or disable captions based on track length
157
+ if (this.elements) {
158
+ toggleClass(this.elements.container, this.config.classNames.captions.enabled, !is.empty(tracks));
159
+ }
160
+
161
+ // Update available languages in list
162
+ if (
163
+ is.array(this.config.controls)
164
+ && this.config.controls.includes('settings')
165
+ && this.config.settings.includes('captions')
166
+ ) {
167
+ controls.setCaptionsMenu.call(this);
168
+ }
169
+ },
170
+
171
+ // Toggle captions display
172
+ // Used internally for the toggleCaptions method, with the passive option forced to false
173
+ toggle(input, passive = true) {
174
+ // If there's no full support
175
+ if (!this.supported.ui) {
176
+ return;
177
+ }
178
+
179
+ const { toggled } = this.captions; // Current state
180
+ const activeClass = this.config.classNames.captions.active;
181
+ // Get the next state
182
+ // If the method is called without parameter, toggle based on current value
183
+ const active = is.nullOrUndefined(input) ? !toggled : input;
184
+
185
+ // Update state and trigger event
186
+ if (active !== toggled) {
187
+ // When passive, don't override user preferences
188
+ if (!passive) {
189
+ this.captions.active = active;
190
+ this.storage.set({ captions: active });
191
+ }
192
+
193
+ // Force language if the call isn't passive and there is no matching language to toggle to
194
+ if (!this.language && active && !passive) {
195
+ const tracks = captions.getTracks.call(this);
196
+ const track = captions.findTrack.call(this, [this.captions.language, ...this.captions.languages], true);
197
+
198
+ // Override user preferences to avoid switching languages if a matching track is added
199
+ this.captions.language = track.language;
200
+
201
+ // Set caption, but don't store in localStorage as user preference
202
+ captions.set.call(this, tracks.indexOf(track));
203
+ return;
204
+ }
205
+
206
+ // Toggle button if it's enabled
207
+ if (this.elements.buttons.captions) {
208
+ this.elements.buttons.captions.pressed = active;
209
+ }
210
+
211
+ // Add class hook
212
+ toggleClass(this.elements.container, activeClass, active);
213
+
214
+ this.captions.toggled = active;
215
+
216
+ // Update settings menu
217
+ controls.updateSetting.call(this, 'captions');
218
+
219
+ // Trigger event (not used internally)
220
+ triggerEvent.call(this, this.media, active ? 'captionsenabled' : 'captionsdisabled');
221
+ }
222
+
223
+ // Wait for the call stack to clear before setting mode='hidden'
224
+ // on the active track - forcing the browser to download it
225
+ setTimeout(() => {
226
+ if (active && this.captions.toggled) {
227
+ this.captions.currentTrackNode.mode = 'hidden';
228
+ }
229
+ });
230
+ },
231
+
232
+ // Set captions by track index
233
+ // Used internally for the currentTrack setter with the passive option forced to false
234
+ set(index, passive = true) {
235
+ const tracks = captions.getTracks.call(this);
236
+
237
+ // Disable captions if setting to -1
238
+ if (index === -1) {
239
+ captions.toggle.call(this, false, passive);
240
+ return;
241
+ }
242
+
243
+ if (!is.number(index)) {
244
+ this.debug.warn('Invalid caption argument', index);
245
+ return;
246
+ }
247
+
248
+ if (!(index in tracks)) {
249
+ this.debug.warn('Track not found', index);
250
+ return;
251
+ }
252
+
253
+ if (this.captions.currentTrack !== index) {
254
+ this.captions.currentTrack = index;
255
+ const track = tracks[index];
256
+ const { language } = track || {};
257
+
258
+ // Store reference to node for invalidation on remove
259
+ this.captions.currentTrackNode = track;
260
+
261
+ // Update settings menu
262
+ controls.updateSetting.call(this, 'captions');
263
+
264
+ // When passive, don't override user preferences
265
+ if (!passive) {
266
+ this.captions.language = language;
267
+ this.storage.set({ language });
268
+ }
269
+
270
+ // Handle Vimeo captions
271
+ if (this.isVimeo) {
272
+ // Enable text track but don't render captions within the player
273
+ // Since we handle that ourselves
274
+ this.embed.enableTextTrack(language, null, false);
275
+ }
276
+
277
+ // Trigger event
278
+ triggerEvent.call(this, this.media, 'languagechange');
279
+ }
280
+
281
+ // Show captions
282
+ captions.toggle.call(this, true, passive);
283
+
284
+ if (this.isHTML5 && this.isVideo) {
285
+ // If we change the active track while a cue is already displayed we need to update it
286
+ captions.updateCues.call(this);
287
+ }
288
+ },
289
+
290
+ // Set captions by language
291
+ // Used internally for the language setter with the passive option forced to false
292
+ setLanguage(input, passive = true) {
293
+ if (!is.string(input)) {
294
+ this.debug.warn('Invalid language argument', input);
295
+ return;
296
+ }
297
+ // Normalize
298
+ const language = input.toLowerCase();
299
+ this.captions.language = language;
300
+
301
+ // Set currentTrack
302
+ const tracks = captions.getTracks.call(this);
303
+ const track = captions.findTrack.call(this, [language]);
304
+ captions.set.call(this, tracks.indexOf(track), passive);
305
+ },
306
+
307
+ // Get current valid caption tracks
308
+ // If update is false it will also ignore tracks without metadata
309
+ // This is used to "freeze" the language options when captions.update is false
310
+ getTracks(update = false) {
311
+ // Handle media or textTracks missing or null
312
+ const tracks = Array.from((this.media || {}).textTracks || []);
313
+ // For HTML5, use cache instead of current tracks when it exists (if captions.update is false)
314
+ // Filter out removed tracks and tracks that aren't captions/subtitles (for example metadata)
315
+ return tracks
316
+ .filter(track => !this.isHTML5 || update || this.captions.meta.has(track))
317
+ .filter(track => ['captions', 'subtitles'].includes(track.kind));
318
+ },
319
+
320
+ // Match tracks based on languages and get the first
321
+ findTrack(languages, force = false) {
322
+ const tracks = captions.getTracks.call(this);
323
+ const sortIsDefault = track => Number((this.captions.meta.get(track) || {}).default);
324
+ const sorted = Array.from(tracks).sort((a, b) => sortIsDefault(b) - sortIsDefault(a));
325
+ let track;
326
+
327
+ languages.every((language) => {
328
+ track = sorted.find(t => t.language === language);
329
+ return !track; // Break iteration if there is a match
330
+ });
331
+
332
+ // If no match is found but is required, get first
333
+ return track || (force ? sorted[0] : undefined);
334
+ },
335
+
336
+ // Get the current track
337
+ getCurrentTrack() {
338
+ return captions.getTracks.call(this)[this.currentTrack];
339
+ },
340
+
341
+ // Get UI label for track
342
+ getLabel(track) {
343
+ let currentTrack = track;
344
+
345
+ if (!is.track(currentTrack) && support.textTracks && this.captions.toggled) {
346
+ currentTrack = captions.getCurrentTrack.call(this);
347
+ }
348
+
349
+ if (is.track(currentTrack)) {
350
+ if (!is.empty(currentTrack.label)) {
351
+ return currentTrack.label;
352
+ }
353
+
354
+ if (!is.empty(currentTrack.language)) {
355
+ return track.language.toUpperCase();
356
+ }
357
+
358
+ return i18n.get('enabled', this.config);
359
+ }
360
+
361
+ return i18n.get('disabled', this.config);
362
+ },
363
+
364
+ // Update captions using current track's active cues
365
+ // Also optional array argument in case there isn't any track (ex: vimeo)
366
+ updateCues(input) {
367
+ // Requires UI
368
+ if (!this.supported.ui) {
369
+ return;
370
+ }
371
+
372
+ if (!is.element(this.elements.captions)) {
373
+ this.debug.warn('No captions element to render to');
374
+ return;
375
+ }
376
+
377
+ // Only accept array or empty input
378
+ if (!is.nullOrUndefined(input) && !Array.isArray(input)) {
379
+ this.debug.warn('updateCues: Invalid input', input);
380
+ return;
381
+ }
382
+
383
+ let cues = input;
384
+
385
+ // Get cues from track
386
+ if (!cues) {
387
+ const track = captions.getCurrentTrack.call(this);
388
+
389
+ cues = Array.from((track || {}).activeCues || [])
390
+ .map(cue => cue.getCueAsHTML())
391
+ .map(getHTML);
392
+ }
393
+
394
+ // Set new caption text
395
+ const content = cues.map(cueText => cueText.trim()).join('\n');
396
+ const changed = content !== this.elements.captions.innerHTML;
397
+
398
+ if (changed) {
399
+ // Empty the container and create a new child element
400
+ emptyElement(this.elements.captions);
401
+ const caption = createElement('span', getAttributesFromSelector(this.config.selectors.caption));
402
+ caption.innerHTML = content;
403
+ this.elements.captions.appendChild(caption);
404
+
405
+ // Trigger event
406
+ triggerEvent.call(this, this.media, 'cuechange');
407
+ }
408
+ },
409
+ };
410
+
411
+ export default captions;