@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.
- package/.editorconfig +10 -0
- package/.gitpod.yml +6 -0
- package/.node-version +1 -0
- package/.prettierrc +7 -0
- package/.stickler.yml +5 -0
- package/.stylelintrc.json +26 -0
- package/CHANGELOG.md +16 -0
- package/CONTRIBUTING.md +34 -0
- package/CONTROLS.md +49 -0
- package/Dockerfile +32 -0
- package/LICENSE.md +22 -0
- package/README.md +194 -0
- package/cspell.json +48 -0
- package/dist/redxplyr.css +1 -0
- package/dist/redxplyr.js +8801 -0
- package/dist/redxplyr.min.js +2 -0
- package/dist/redxplyr.min.js.map +1 -0
- package/dist/redxplyr.min.mjs +1 -0
- package/dist/redxplyr.min.mjs.map +1 -0
- package/dist/redxplyr.mjs +8793 -0
- package/dist/redxplyr.polyfilled.js +9294 -0
- package/dist/redxplyr.polyfilled.min.js +2 -0
- package/dist/redxplyr.polyfilled.min.js.map +1 -0
- package/dist/redxplyr.polyfilled.min.mjs +1 -0
- package/dist/redxplyr.polyfilled.min.mjs.map +1 -0
- package/dist/redxplyr.polyfilled.mjs +9286 -0
- package/dist/redxplyr.svg +1 -0
- package/eslint.config.mjs +39 -0
- package/gulpfile.js +8 -0
- package/package.json +114 -0
- package/pnpm-workspace.yaml +8 -0
- package/src/js/captions.js +411 -0
- package/src/js/config/defaults.js +459 -0
- package/src/js/config/states.js +10 -0
- package/src/js/config/types.js +34 -0
- package/src/js/console.js +28 -0
- package/src/js/controls.js +1870 -0
- package/src/js/fullscreen.js +305 -0
- package/src/js/html5.js +148 -0
- package/src/js/listeners.js +854 -0
- package/src/js/media.js +61 -0
- package/src/js/plugins/ads.js +647 -0
- package/src/js/plugins/preview-thumbnails.js +706 -0
- package/src/js/plugins/vimeo.js +443 -0
- package/src/js/plugins/youtube.js +451 -0
- package/src/js/plyr.d.ts +729 -0
- package/src/js/plyr.js +1291 -0
- package/src/js/plyr.polyfilled.js +13 -0
- package/src/js/source.js +155 -0
- package/src/js/storage.js +70 -0
- package/src/js/support.js +100 -0
- package/src/js/ui.js +297 -0
- package/src/js/utils/animation.js +33 -0
- package/src/js/utils/arrays.js +23 -0
- package/src/js/utils/browser.js +21 -0
- package/src/js/utils/elements.js +263 -0
- package/src/js/utils/events.js +116 -0
- package/src/js/utils/fetch.js +45 -0
- package/src/js/utils/i18n.js +47 -0
- package/src/js/utils/is.js +81 -0
- package/src/js/utils/load-image.js +19 -0
- package/src/js/utils/load-script.js +14 -0
- package/src/js/utils/load-sprite.js +77 -0
- package/src/js/utils/numbers.js +17 -0
- package/src/js/utils/objects.js +43 -0
- package/src/js/utils/promise.js +14 -0
- package/src/js/utils/strings.js +80 -0
- package/src/js/utils/style.js +148 -0
- package/src/js/utils/time.js +36 -0
- package/src/js/utils/urls.js +40 -0
- package/src/sass/base.scss +69 -0
- package/src/sass/components/badges.scss +12 -0
- package/src/sass/components/captions.scss +58 -0
- package/src/sass/components/control.scss +52 -0
- package/src/sass/components/controls.scss +65 -0
- package/src/sass/components/menus.scss +205 -0
- package/src/sass/components/poster.scss +27 -0
- package/src/sass/components/progress.scss +107 -0
- package/src/sass/components/sliders.scss +99 -0
- package/src/sass/components/times.scss +20 -0
- package/src/sass/components/tooltips.scss +91 -0
- package/src/sass/components/volume.scss +18 -0
- package/src/sass/lib/animation.scss +31 -0
- package/src/sass/lib/css-vars.scss +103 -0
- package/src/sass/lib/functions.scss +3 -0
- package/src/sass/lib/mixins.scss +82 -0
- package/src/sass/plugins/ads.scss +53 -0
- package/src/sass/plugins/preview-thumbnails/index.scss +121 -0
- package/src/sass/plugins/preview-thumbnails/settings.scss +17 -0
- package/src/sass/plyr.scss +46 -0
- package/src/sass/settings/badges.scss +7 -0
- package/src/sass/settings/breakpoints.scss +9 -0
- package/src/sass/settings/captions.scss +10 -0
- package/src/sass/settings/colors.scss +18 -0
- package/src/sass/settings/controls.scss +30 -0
- package/src/sass/settings/cosmetics.scss +5 -0
- package/src/sass/settings/helpers.scss +7 -0
- package/src/sass/settings/menus.scss +13 -0
- package/src/sass/settings/progress.scss +18 -0
- package/src/sass/settings/sliders.scss +39 -0
- package/src/sass/settings/tooltips.scss +11 -0
- package/src/sass/settings/type.scss +16 -0
- package/src/sass/states/fullscreen.scss +15 -0
- package/src/sass/types/audio.scss +61 -0
- package/src/sass/types/video.scss +170 -0
- package/src/sass/utils/animation.scss +7 -0
- package/src/sass/utils/hidden.scss +28 -0
- package/src/sprite/plyr-airplay.svg +8 -0
- package/src/sprite/plyr-captions-off.svg +7 -0
- package/src/sprite/plyr-captions-on.svg +7 -0
- package/src/sprite/plyr-download.svg +8 -0
- package/src/sprite/plyr-enter-fullscreen.svg +4 -0
- package/src/sprite/plyr-exit-fullscreen.svg +4 -0
- package/src/sprite/plyr-fast-forward.svg +3 -0
- package/src/sprite/plyr-logo-vimeo.svg +6 -0
- package/src/sprite/plyr-logo-youtube.svg +6 -0
- package/src/sprite/plyr-muted.svg +8 -0
- package/src/sprite/plyr-pause.svg +8 -0
- package/src/sprite/plyr-pip.svg +6 -0
- package/src/sprite/plyr-play.svg +5 -0
- package/src/sprite/plyr-restart.svg +5 -0
- package/src/sprite/plyr-rewind.svg +3 -0
- package/src/sprite/plyr-settings.svg +5 -0
- package/src/sprite/plyr-volume.svg +11 -0
- package/tasks/build.js +226 -0
- package/tasks/deploy.js +216 -0
- 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,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;
|