Compare commits

..

33 Commits

Author SHA1 Message Date
Sam Potts 2bd08cdc28 3.5.3 2019-04-12 18:44:05 +10:00
Sam Potts 5fefabe3bd Merge pull request #1410 from sampotts/develop
v3.5.3
2019-04-12 18:39:14 +10:00
Sam Potts e281078441 Bump version 2019-04-12 18:38:46 +10:00
Sam Potts 9bb75f6f52 Changelog 2019-04-12 18:37:11 +10:00
Sam Potts 3b7a24456d Package upgrades 2019-04-12 18:36:55 +10:00
Sam Potts c885d59270 Moved all video styles together 2019-04-12 12:43:45 +10:00
Sam Potts 9f30d54963 Merge branch 'master' into develop
# Conflicts:
#	readme.md
2019-04-12 12:40:12 +10:00
Sam Potts b247093495 Aspect ratio improvements (fixes #1042, fixes #1366) 2019-04-12 12:19:48 +10:00
Sam Potts 9ca7b861a9 Autoplay tweak for HTML5 2019-04-12 12:14:12 +10:00
Sam Potts 2eccf0dd05 Fix YouTube autoplay (fixes #1185) 2019-04-12 12:13:46 +10:00
Sam Potts 566b957e65 Merge pull request #1362 from doublex/master
#46 - two patches from 'jamesoflol'
2019-04-11 21:20:23 +10:00
Sam Potts a8456f4ca7 Merge pull request #1404 from taion/clear-timeouts
fix: Properly clear all timeouts on destroy
2019-04-11 21:18:52 +10:00
Sam Potts 0f3098040d Merge pull request #1407 from freezer278/http-youtube-fix
fixed setting youtube host for non-https case
2019-04-11 21:18:15 +10:00
Vladimir Morozov 21539be3f2 code cleanup 2019-04-04 09:32:38 +03:00
Vladimir Morozov c22f5c4b39 code cleanup 2019-04-04 08:56:46 +03:00
Vladimir Morozov f4b47a9275 fixed setting youtube host for non-https case 2019-04-04 08:51:20 +03:00
Jimmy Jia 266b70d9d0 fix: Properly clear all timeouts on destroy 2019-04-01 14:42:51 -04:00
Sam Potts 6e68ad6d15 Update readme.md 2019-03-26 21:43:59 +11:00
Sam Potts c202551e6d Merge branch 'develop' of github.com:sampotts/plyr into develop 2019-03-16 11:58:12 +11:00
Sam Potts 5b7a025d26 Housekeeping 2019-03-16 11:57:15 +11:00
Sam Potts 26bcf83960 Merge pull request #1376 from ar31an/patch-1
Update poster src
2019-03-07 18:59:58 +11:00
Arslan Javed e4acff4f8d Update poster src 2019-03-07 12:54:07 +05:00
Sam Potts 568ddf2390 Merge pull request #1375 from sampotts/master
Merge back
2019-03-07 18:30:05 +11:00
Your Name ce91945544 Preview seek: optional hours and ms in VTT parser 2019-02-27 15:45:24 +01:00
Your Name 11fed8d1b5 Preview seek: fix: allow absolute thumbnail paths 2019-02-27 15:43:36 +01:00
Sam Potts 4c3bf25b8a Fixed issue where the preview thumbnail was present while scrubbing 2019-02-24 12:10:20 +11:00
Sam Potts 215fc3677a v3.5.1 2019-02-23 13:14:01 +11:00
Sam Potts 4d3b6b882e Merge pull request #1356 from sampotts/develop
v3.5.1
2019-02-23 13:10:20 +11:00
Sam Potts 83eda174af v3.5.1 2019-02-23 13:09:26 +11:00
Sam Potts 1c29cb890e Merge branch 'master' into develop
# Conflicts:
#	gulpfile.js
2019-02-23 13:08:32 +11:00
Sam Potts 438ebe5013 Jsdoc updates 2019-02-23 13:07:35 +11:00
Sam Potts 4335a2a0d1 Update .npmignore 2019-02-23 13:07:26 +11:00
Sam Potts 825fd292ae Fix build 2019-02-23 13:07:17 +11:00
38 changed files with 19879 additions and 14089 deletions
+2 -1
View File
@@ -2,8 +2,9 @@ demo
.github .github
.vscode .vscode
*.code-workspace *.code-workspace
build.json
credentials.json credentials.json
bundles.json deploy.json
yarn.lock yarn.lock
package-lock.json package-lock.json
*.mp4 *.mp4
+16
View File
@@ -1,3 +1,19 @@
## v3.5.3
- Improved the usage of the `ratio` config option; it now works as expected and for all video types. The default has not changed, it is to dynamically, where possible (except YouTube where 16:9 is used) determine the ratio from the media source so this is not a breaking change.
- Added new `ratio` getter and setter
- Fix: Properly clear all timeouts on destroy
- Fix: Allow absolute paths in preview thumbnails
- Improvement: Allow optional hours and ms in VTT parser in preview thumbnails
## v3.5.2
- Fixed issue where the preview thumbnail was present while scrubbing
## v3.5.1
- Fixed build issues with babel and browserslist
## v3.5.0 ## v3.5.0
- Preview seek/scrubbing thumbnails (thanks @jamesoflol) - Preview seek/scrubbing thumbnails (thanks @jamesoflol)
+1 -1
View File
File diff suppressed because one or more lines are too long
+4595 -4694
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+758 -622
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+755 -619
View File
File diff suppressed because it is too large Load Diff
+6314 -3494
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+6311 -3491
View File
File diff suppressed because it is too large Load Diff
+40 -43
View File
@@ -30,6 +30,7 @@ const header = require('gulp-header');
const gitbranch = require('git-branch'); const gitbranch = require('git-branch');
const rename = require('gulp-rename'); const rename = require('gulp-rename');
const replace = require('gulp-replace'); const replace = require('gulp-replace');
const ansi = require('ansi-colors');
const log = require('fancy-log'); const log = require('fancy-log');
const open = require('gulp-open'); const open = require('gulp-open');
const plumber = require('gulp-plumber'); const plumber = require('gulp-plumber');
@@ -104,32 +105,14 @@ const tasks = {
css: [], css: [],
js: [], js: [],
sprite: [], sprite: [],
clean: ['clean'], clean: 'clean',
}; };
// Size plugin // Size plugin
const sizeOptions = { showFiles: true, gzip: true }; const sizeOptions = { showFiles: true, gzip: true };
// Babel config
const babelrc = (polyfill = false) => ({
presets: [
[
'@babel/preset-env',
{
targets: {
browsers,
},
useBuiltIns: polyfill ? 'usage' : false,
modules: false,
},
],
],
babelrc: false,
exclude: 'node_modules/**',
});
// Clean out /dist // Clean out /dist
gulp.task('clean', done => { gulp.task(tasks.clean, done => {
const dirs = [paths.plyr.output, paths.demo.output].map(dir => path.join(dir, '**/*')); const dirs = [paths.plyr.output, paths.demo.output].map(dir => path.join(dir, '**/*'));
// Don't delete the mp4 // Don't delete the mp4
@@ -148,19 +131,35 @@ Object.entries(build.js).forEach(([filename, entry]) => {
const polyfill = filename.includes('polyfilled'); const polyfill = filename.includes('polyfilled');
const extension = format === 'es' ? 'mjs' : 'js'; const extension = format === 'es' ? 'mjs' : 'js';
gulp.task(name, () => { gulp.task(name, () =>
return gulp gulp
.src(entry.src) .src(entry.src)
.pipe(plumber()) .pipe(plumber())
.pipe(sourcemaps.init()) .pipe(sourcemaps.init())
.pipe( .pipe(
rollup( rollup(
{ {
plugins: [resolve(), commonjs(), babel(babelrc(polyfill))], plugins: [
resolve(),
commonjs(),
babel({
presets: [
[
'@babel/env',
{
// debug: true,
useBuiltIns: polyfill ? 'usage' : false,
corejs: polyfill ? 3 : undefined,
},
],
],
babelrc: false,
exclude: [/\/core-js\//],
}),
],
}, },
{ {
name: entry.namespace, name: entry.namespace,
// exports: 'named',
format, format,
}, },
), ),
@@ -172,13 +171,13 @@ Object.entries(build.js).forEach(([filename, entry]) => {
}), }),
) )
.pipe(gulp.dest(entry.dist)) .pipe(gulp.dest(entry.dist))
.pipe(filter(`**/*${extension}`)) .pipe(filter(`**/*.${extension}`))
.pipe(terser()) .pipe(terser())
.pipe(rename({ suffix: minSuffix })) .pipe(rename({ suffix: minSuffix }))
.pipe(size(sizeOptions)) .pipe(size(sizeOptions))
.pipe(sourcemaps.write('')) .pipe(sourcemaps.write(''))
.pipe(gulp.dest(entry.dist)); .pipe(gulp.dest(entry.dist)),
}); );
}); });
}); });
@@ -187,8 +186,8 @@ Object.entries(build.css).forEach(([filename, entry]) => {
const name = `css:${filename}`; const name = `css:${filename}`;
tasks.css.push(name); tasks.css.push(name);
gulp.task(name, () => { gulp.task(name, () =>
return gulp gulp
.src(entry.src) .src(entry.src)
.pipe(plumber()) .pipe(plumber())
.pipe(sass()) .pipe(sass())
@@ -199,8 +198,8 @@ Object.entries(build.css).forEach(([filename, entry]) => {
) )
.pipe(clean()) .pipe(clean())
.pipe(size(sizeOptions)) .pipe(size(sizeOptions))
.pipe(gulp.dest(entry.dist)); .pipe(gulp.dest(entry.dist)),
}); );
}); });
// SVG Sprites // SVG Sprites
@@ -208,18 +207,16 @@ Object.entries(build.sprite).forEach(([filename, entry]) => {
const name = `sprite:${filename}`; const name = `sprite:${filename}`;
tasks.sprite.push(name); tasks.sprite.push(name);
log(path.basename(filename)); gulp.task(name, () =>
gulp
gulp.task(name, () => {
return gulp
.src(entry.src) .src(entry.src)
.pipe(plumber()) .pipe(plumber())
.pipe(imagemin()) .pipe(imagemin())
.pipe(svgstore()) .pipe(svgstore())
.pipe(rename({ basename: path.parse(filename).name })) .pipe(rename({ basename: path.parse(filename).name }))
.pipe(size(sizeOptions)) .pipe(size(sizeOptions))
.pipe(gulp.dest(entry.dist)); .pipe(gulp.dest(entry.dist)),
}); );
}); });
// Build all JS // Build all JS
@@ -317,7 +314,7 @@ gulp.task('version', done => {
const { domain } = deploy.cdn; const { domain } = deploy.cdn;
console.log(`Updating versions to '${version}'...`); log(`Uploading ${ansi.green.bold(version)} to ${ansi.cyan(domain)}...`);
// Replace versioned URLs in source // Replace versioned URLs in source
const files = ['plyr.js', 'plyr.polyfilled.js', 'config/defaults.js']; const files = ['plyr.js', 'plyr.polyfilled.js', 'config/defaults.js'];
@@ -342,7 +339,7 @@ gulp.task('cdn', done => {
throw new Error('No publisher instance. Check AWS configuration.'); throw new Error('No publisher instance. Check AWS configuration.');
} }
console.log(`Uploading '${version}' to ${domain}...`); log(`Uploading ${ansi.green.bold(pkg.version)} to ${ansi.cyan(domain)}...`);
// Upload to CDN // Upload to CDN
return ( return (
@@ -385,13 +382,13 @@ gulp.task('purge', () => {
const purge = new FastlyPurge(fastly.token); const purge = new FastlyPurge(fastly.token);
list.forEach(url => { list.forEach(url => {
console.log(`Purging ${url}...`); log(`Purging ${ansi.cyan(url)}...`);
purge.url(url, (error, result) => { purge.url(url, (error, result) => {
if (error) { if (error) {
console.log(error); log.error(error);
} else if (result) { } else if (result) {
console.log(result); log(result);
} }
}); });
}); });
@@ -412,7 +409,7 @@ gulp.task('demo', done => {
throw new Error('No publisher instance. Check AWS configuration.'); throw new Error('No publisher instance. Check AWS configuration.');
} }
console.log(`Uploading '${version}' demo to ${deploy.demo.domain}...`); log(`Uploading ${ansi.green.bold(pkg.version)} to ${ansi.cyan(domain)}...`);
// Replace versioned files in readme.md // Replace versioned files in readme.md
gulp.src([`${__dirname}/readme.md`]) gulp.src([`${__dirname}/readme.md`])
+25 -27
View File
@@ -1,6 +1,6 @@
{ {
"name": "plyr", "name": "plyr",
"version": "3.5.0", "version": "3.5.3",
"description": "A simple, accessible and customizable HTML5, YouTube and Vimeo media player", "description": "A simple, accessible and customizable HTML5, YouTube and Vimeo media player",
"homepage": "https://plyr.io", "homepage": "https://plyr.io",
"author": "Sam Potts <sam@potts.es>", "author": "Sam Potts <sam@potts.es>",
@@ -27,10 +27,7 @@
"bugs": { "bugs": {
"url": "https://github.com/sampotts/plyr/issues" "url": "https://github.com/sampotts/plyr/issues"
}, },
"browserslist": [ "browserslist": "> 1%",
"> 1%",
"not dead"
],
"scripts": { "scripts": {
"build": "gulp build", "build": "gulp build",
"lint": "eslint src/js && npm run-script remark", "lint": "eslint src/js && npm run-script remark",
@@ -38,15 +35,15 @@
"deploy": "yarn lint && gulp deploy" "deploy": "yarn lint && gulp deploy"
}, },
"devDependencies": { "devDependencies": {
"aws-sdk": "^2.404.0", "ansi-colors": "^3.2.4",
"@babel/core": "^7.3.3", "aws-sdk": "^2.437.0",
"@babel/preset-env": "^7.3.1", "@babel/core": "^7.4.3",
"@babel/preset-env": "^7.4.3",
"babel-eslint": "^10.0.1", "babel-eslint": "^10.0.1",
"babel-preset-minify": "^0.5.0", "del": "^4.1.0",
"del": "^3.0.0", "eslint": "^5.16.0",
"eslint": "^5.14.0",
"eslint-config-airbnb-base": "^13.1.0", "eslint-config-airbnb-base": "^13.1.0",
"eslint-config-prettier": "^4.0.0", "eslint-config-prettier": "^4.1.0",
"eslint-plugin-import": "^2.16.0", "eslint-plugin-import": "^2.16.0",
"fancy-log": "^1.3.3", "fancy-log": "^1.3.3",
"fastly-purge": "^1.0.1", "fastly-purge": "^1.0.1",
@@ -54,7 +51,7 @@
"gulp": "^4.0.0", "gulp": "^4.0.0",
"gulp-autoprefixer": "^6.0.0", "gulp-autoprefixer": "^6.0.0",
"gulp-awspublish": "^4.0.0", "gulp-awspublish": "^4.0.0",
"gulp-better-rollup": "^3.4.0", "gulp-better-rollup": "^4.0.1",
"gulp-clean-css": "^4.0.0", "gulp-clean-css": "^4.0.0",
"gulp-filter": "^5.1.0", "gulp-filter": "^5.1.0",
"gulp-header": "^2.0.7", "gulp-header": "^2.0.7",
@@ -66,32 +63,33 @@
"gulp-replace": "^1.0.0", "gulp-replace": "^1.0.0",
"gulp-sass": "^4.0.2", "gulp-sass": "^4.0.2",
"gulp-size": "^3.0.0", "gulp-size": "^3.0.0",
"gulp-sourcemaps": "^2.6.4", "gulp-sourcemaps": "^2.6.5",
"gulp-svgstore": "^7.0.1", "gulp-svgstore": "^7.0.1",
"gulp-terser": "^1.1.7", "gulp-terser": "^1.1.7",
"postcss-custom-properties": "^8.0.9", "postcss-custom-properties": "^8.0.10",
"prettier-eslint": "^8.8.2", "prettier-eslint": "^8.8.2",
"prettier-stylelint": "^0.4.2", "prettier-stylelint": "^0.4.2",
"remark-cli": "^6.0.1", "remark-cli": "^6.0.1",
"remark-validate-links": "^8.0.0", "remark-validate-links": "^8.0.2",
"rollup": "^1.10.0",
"rollup-plugin-babel": "^4.3.2", "rollup-plugin-babel": "^4.3.2",
"rollup-plugin-commonjs": "^9.2.0", "rollup-plugin-commonjs": "^9.3.4",
"rollup-plugin-node-resolve": "^4.0.0", "rollup-plugin-node-resolve": "^4.2.3",
"stylelint": "^9.10.1", "stylelint": "^9.10.1",
"stylelint-config-prettier": "^4.0.0", "stylelint-config-prettier": "^5.0.0",
"stylelint-config-recommended": "^2.1.0", "stylelint-config-recommended": "^2.1.0",
"stylelint-config-sass-guidelines": "^5.3.0", "stylelint-config-sass-guidelines": "^5.3.0",
"stylelint-order": "^2.0.0", "stylelint-order": "^2.2.1",
"stylelint-scss": "^3.5.3", "stylelint-scss": "^3.5.4",
"stylelint-selector-bem-pattern": "^2.0.0", "stylelint-selector-bem-pattern": "^2.1.0",
"through2": "^3.0.0" "through2": "^3.0.1"
}, },
"dependencies": { "dependencies": {
"core-js": "^2.6.5", "core-js": "^3.0.1",
"custom-event-polyfill": "^1.0.6", "custom-event-polyfill": "^1.0.7",
"loadjs": "^3.5.5", "loadjs": "^3.6.1",
"rangetouch": "^2.0.0", "rangetouch": "^2.0.0",
"raven-js": "^3.27.0", "raven-js": "^3.27.0",
"url-polyfill": "^1.1.3" "url-polyfill": "^1.1.5"
} }
} }
+5 -7
View File
@@ -5,23 +5,20 @@
} }
], ],
"settings": { "settings": {
// Exclude from the editor
"files.exclude": {
"**/node_modules": true
},
// Exclude from search
"search.exclude": { "search.exclude": {
"dist/": true, "**/node_modules": true,
"demo/dist/": true "**/dist": true
}, },
// Linting // Linting
"stylelint.enable": true, "stylelint.enable": true,
"css.validate": false, "css.validate": false,
"scss.validate": false, "scss.validate": false,
"javascript.validate.enable": false, "javascript.validate.enable": false,
// Prettier // Prettier
"prettier.eslintIntegration": true, "prettier.eslintIntegration": true,
"prettier.stylelintIntegration": true, "prettier.stylelintIntegration": true,
// Formatting // Formatting
"editor.tabSize": 4, "editor.tabSize": 4,
"editor.insertSpaces": true, "editor.insertSpaces": true,
@@ -29,6 +26,7 @@
"editor.codeActionsOnSave": { "editor.codeActionsOnSave": {
"source.organizeImports": true "source.organizeImports": true
}, },
// Trim on save // Trim on save
"files.trimTrailingWhitespace": true "files.trimTrailingWhitespace": true
} }
+9 -9
View File
@@ -123,13 +123,13 @@ See [initialising](#initialising) for more information on advanced setups.
You can use our CDN (provided by [Fastly](https://www.fastly.com/)) for the JavaScript. There's 2 versions; one with and one without [polyfills](#polyfills). My recommendation would be to manage polyfills seperately as part of your application but to make life easier you can use the polyfilled build. You can use our CDN (provided by [Fastly](https://www.fastly.com/)) for the JavaScript. There's 2 versions; one with and one without [polyfills](#polyfills). My recommendation would be to manage polyfills seperately as part of your application but to make life easier you can use the polyfilled build.
```html ```html
<script src="https://cdn.plyr.io/3.5.0/plyr.js"></script> <script src="https://cdn.plyr.io/3.5.3/plyr.js"></script>
``` ```
...or... ...or...
```html ```html
<script src="https://cdn.plyr.io/3.5.0/plyr.polyfilled.js"></script> <script src="https://cdn.plyr.io/3.5.3/plyr.polyfilled.js"></script>
``` ```
## CSS ## CSS
@@ -143,13 +143,13 @@ Include the `plyr.css` stylsheet into your `<head>`
If you want to use our CDN (provided by [Fastly](https://www.fastly.com/)) for the default CSS, you can use the following: If you want to use our CDN (provided by [Fastly](https://www.fastly.com/)) for the default CSS, you can use the following:
```html ```html
<link rel="stylesheet" href="https://cdn.plyr.io/3.5.0/plyr.css" /> <link rel="stylesheet" href="https://cdn.plyr.io/3.5.3/plyr.css" />
``` ```
## SVG Sprite ## SVG Sprite
The SVG sprite is loaded automatically from our CDN (provided by [Fastly](https://www.fastly.com/)). To change this, see the [options](#options) below. For The SVG sprite is loaded automatically from our CDN (provided by [Fastly](https://www.fastly.com/)). To change this, see the [options](#options) below. For
reference, the CDN hosted SVG sprite can be found at `https://cdn.plyr.io/3.5.0/plyr.svg`. reference, the CDN hosted SVG sprite can be found at `https://cdn.plyr.io/3.5.3/plyr.svg`.
# Ads # Ads
@@ -293,10 +293,10 @@ Note the single quotes encapsulating the JSON and double quotes on the object ke
| `listeners` | Object | `null` | Allows binding of event listeners to the controls before the default handlers. See the `defaults.js` for available listeners. If your handler prevents default on the event (`event.preventDefault()`), the default handler will not fire. | | `listeners` | Object | `null` | Allows binding of event listeners to the controls before the default handlers. See the `defaults.js` for available listeners. If your handler prevents default on the event (`event.preventDefault()`), the default handler will not fire. |
| `captions` | Object | `{ active: false, language: 'auto', update: false }` | `active`: Toggles if captions should be active by default. `language`: Sets the default language to load (if available). 'auto' uses the browser language. `update`: Listen to changes to tracks and update menu. This is needed for some streaming libraries, but can result in unselectable language options). | | `captions` | Object | `{ active: false, language: 'auto', update: false }` | `active`: Toggles if captions should be active by default. `language`: Sets the default language to load (if available). 'auto' uses the browser language. `update`: Listen to changes to tracks and update menu. This is needed for some streaming libraries, but can result in unselectable language options). |
| `fullscreen` | Object | `{ enabled: true, fallback: true, iosNative: false }` | `enabled`: Toggles whether fullscreen should be enabled. `fallback`: Allow fallback to a full-window solution (`true`/`false`/`'force'`). `iosNative`: whether to use native iOS fullscreen when entering fullscreen (no custom controls) | | `fullscreen` | Object | `{ enabled: true, fallback: true, iosNative: false }` | `enabled`: Toggles whether fullscreen should be enabled. `fallback`: Allow fallback to a full-window solution (`true`/`false`/`'force'`). `iosNative`: whether to use native iOS fullscreen when entering fullscreen (no custom controls) |
| `ratio` | String | `16:9` | The aspect ratio you want to use for embedded players. | | `ratio` | String | `null` | Force an aspect ratio for all videos. The format is `'w:h'` - e.g. `'16:9'` or `'4:3'`. If this is not specified then the default for HTML5 and Vimeo is to use the native resolution of the video. As dimensions are not available from YouTube via SDK, 16:9 is forced as a sensible default. |
| `storage` | Object | `{ enabled: true, key: 'plyr' }` | `enabled`: Allow use of local storage to store user settings. `key`: The key name to use. | | `storage` | Object | `{ enabled: true, key: 'plyr' }` | `enabled`: Allow use of local storage to store user settings. `key`: The key name to use. |
| `speed` | Object | `{ selected: 1, options: [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2] }` | `selected`: The default speed for playback. `options`: Options to display in the menu. Most browsers will refuse to play slower than 0.5. | | `speed` | Object | `{ selected: 1, options: [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2] }` | `selected`: The default speed for playback. `options`: Options to display in the menu. Most browsers will refuse to play slower than 0.5. |
| `quality` | Object | `{ default: 'default', options: ['hd2160', 'hd1440', 'hd1080', 'hd720', 'large', 'medium', 'small', 'tiny', 'default'] }` | Currently only supported by YouTube. `default` is the default quality level, determined by YouTube. `options` are the options to display. | | `quality` | Object | `{ default: 576, options: [4320, 2880, 2160, 1440, 1080, 720, 576, 480, 360, 240] }` | `default` is the default quality level (if it exists in your sources). `options` are the options to display. This is used to filter the available sources. |
| `loop` | Object | `{ active: false }` | `active`: Whether to loop the current video. If the `loop` attribute is present on a `<video>` or `<audio>` element, this will be automatically set to true This is an object to support future functionality. | | `loop` | Object | `{ active: false }` | `active`: Whether to loop the current video. If the `loop` attribute is present on a `<video>` or `<audio>` element, this will be automatically set to true This is an object to support future functionality. |
| `ads` | Object | `{ enabled: false, publisherId: '' }` | `enabled`: Whether to enable advertisements. `publisherId`: Your unique [vi.ai](https://vi.ai/publisher-video-monetization/?aid=plyrio) publisher ID. | | `ads` | Object | `{ enabled: false, publisherId: '' }` | `enabled`: Whether to enable advertisements. `publisherId`: Your unique [vi.ai](https://vi.ai/publisher-video-monetization/?aid=plyrio) publisher ID. |
| `urls` | Object | See source. | If you wish to override any API URLs then you can do so here. You can also set a custom download URL for the download button. | | `urls` | Object | See source. | If you wish to override any API URLs then you can do so here. You can also set a custom download URL for the download button. |
@@ -404,10 +404,10 @@ player.fullscreen.active; // false;
| `language` | ✓ | ✓ | Gets or sets the preferred captions language for the player. The setter accepts an ISO two-letter language code. Support for the languages is dependent on the captions you include. If your captions don't have any language data, or if you have multiple tracks with the same language, you may want to use `currentTrack` instead. | | `language` | ✓ | ✓ | Gets or sets the preferred captions language for the player. The setter accepts an ISO two-letter language code. Support for the languages is dependent on the captions you include. If your captions don't have any language data, or if you have multiple tracks with the same language, you may want to use `currentTrack` instead. |
| `fullscreen.active` | ✓ | - | Returns a boolean indicating if the current player is in fullscreen mode. | | `fullscreen.active` | ✓ | - | Returns a boolean indicating if the current player is in fullscreen mode. |
| `fullscreen.enabled` | ✓ | - | Returns a boolean indicating if the current player has fullscreen enabled. | | `fullscreen.enabled` | ✓ | - | Returns a boolean indicating if the current player has fullscreen enabled. |
| `pip`&sup2; | ✓ | ✓ | Gets or sets the picture-in-picture state of the player. The setter accepts a boolean. This currently only supported on Safari 10+ (on MacOS Sierra+ and iOS 10+) and Chrome 70+. | | `pip`&sup1; | ✓ | ✓ | Gets or sets the picture-in-picture state of the player. The setter accepts a boolean. This currently only supported on Safari 10+ (on MacOS Sierra+ and iOS 10+) and Chrome 70+. |
| `ratio` | ✓ | ✓ | Gets or sets the video aspect ratio. The setter accepts a string in the same format as the `ratio` option. |
1. YouTube only. HTML5 will follow. 1. HTML5 only
2. HTML5 only
### The `.source` setter ### The `.source` setter
+5 -3
View File
@@ -42,8 +42,9 @@ const defaults = {
// Clicking the currentTime inverts it's value to show time left rather than elapsed // Clicking the currentTime inverts it's value to show time left rather than elapsed
toggleInvert: true, toggleInvert: true,
// Aspect ratio (for embeds) // Force an aspect ratio
ratio: '16:9', // The format must be `'w:h'` (e.g. `'16:9'`)
ratio: null,
// Click video container to play/pause // Click video container to play/pause
clickToPlay: true, clickToPlay: true,
@@ -60,7 +61,7 @@ const defaults = {
// Sprite (for icons) // Sprite (for icons)
loadSprite: true, loadSprite: true,
iconPrefix: 'plyr', iconPrefix: 'plyr',
iconUrl: 'https://cdn.plyr.io/3.5.0/plyr.svg', iconUrl: 'https://cdn.plyr.io/3.5.3/plyr.svg',
// Blank video (used to prevent errors on source change) // Blank video (used to prevent errors on source change)
blankVideo: 'https://cdn.plyr.io/static/blank.mp4', blankVideo: 'https://cdn.plyr.io/static/blank.mp4',
@@ -330,6 +331,7 @@ const defaults = {
provider: 'plyr--{0}', provider: 'plyr--{0}',
video: 'plyr__video-wrapper', video: 'plyr__video-wrapper',
embed: 'plyr__video-embed', embed: 'plyr__video-embed',
videoFixedRatio: 'plyr__video-wrapper--fixed-ratio',
embedContainer: 'plyr__video-embed__container', embedContainer: 'plyr__video-embed__container',
poster: 'plyr__poster', poster: 'plyr__poster',
posterEnabled: 'plyr__poster-enabled', posterEnabled: 'plyr__poster-enabled',
+4
View File
@@ -6,6 +6,7 @@ import support from './support';
import { removeElement } from './utils/elements'; import { removeElement } from './utils/elements';
import { triggerEvent } from './utils/events'; import { triggerEvent } from './utils/events';
import is from './utils/is'; import is from './utils/is';
import { setAspectRatio } from './utils/style';
const html5 = { const html5 = {
getSources() { getSources() {
@@ -43,6 +44,9 @@ const html5 = {
const player = this; const player = this;
// Set aspect ratio if set
setAspectRatio.call(player);
// Quality // Quality
Object.defineProperty(player.media, 'quality', { Object.defineProperty(player.media, 'quality', {
get() { get() {
+4 -4
View File
@@ -9,7 +9,7 @@ import browser from './utils/browser';
import { getElement, getElements, matches, toggleClass, toggleHidden } from './utils/elements'; import { getElement, getElements, matches, toggleClass, toggleHidden } from './utils/elements';
import { off, on, once, toggleListener, triggerEvent } from './utils/events'; import { off, on, once, toggleListener, triggerEvent } from './utils/events';
import is from './utils/is'; import is from './utils/is';
import { setAspectRatio } from './utils/style'; import { getAspectRatio, setAspectRatio } from './utils/style';
class Listeners { class Listeners {
constructor(player) { constructor(player) {
@@ -317,10 +317,10 @@ class Listeners {
} }
const target = player.elements.wrapper.firstChild; const target = player.elements.wrapper.firstChild;
const [, height] = ratio.split(':').map(Number); const [, y] = ratio;
const [videoWidth, videoHeight] = player.embed.ratio.split(':').map(Number); const [videoX, videoY] = getAspectRatio.call(this);
target.style.maxWidth = toggle ? `${(height / videoHeight) * videoWidth}px` : null; target.style.maxWidth = toggle ? `${(y / videoY) * videoX}px` : null;
target.style.margin = toggle ? '0 auto' : null; target.style.margin = toggle ? '0 auto' : null;
}; };
+8 -8
View File
@@ -17,7 +17,7 @@ import { buildUrlParams } from '../utils/urls';
class Ads { class Ads {
/** /**
* Ads constructor. * Ads constructor.
* @param {object} player * @param {Object} player
* @return {Ads} * @return {Ads}
*/ */
constructor(player) { constructor(player) {
@@ -198,7 +198,7 @@ class Ads {
/** /**
* Update the ad countdown * Update the ad countdown
* @param {boolean} start * @param {Boolean} start
*/ */
pollCountdown(start = false) { pollCountdown(start = false) {
if (!start) { if (!start) {
@@ -559,7 +559,7 @@ class Ads {
/** /**
* Handles callbacks after an ad event was invoked * Handles callbacks after an ad event was invoked
* @param {string} event - Event type * @param {String} event - Event type
*/ */
trigger(event, ...args) { trigger(event, ...args) {
const handlers = this.events[event]; const handlers = this.events[event];
@@ -575,8 +575,8 @@ class Ads {
/** /**
* Add event listeners * Add event listeners
* @param {string} event - Event type * @param {String} event - Event type
* @param {function} callback - Callback for when event occurs * @param {Function} callback - Callback for when event occurs
* @return {Ads} * @return {Ads}
*/ */
on(event, callback) { on(event, callback) {
@@ -594,8 +594,8 @@ class Ads {
* The advertisement has 12 seconds to get its things together. We stop this timer when the * The advertisement has 12 seconds to get its things together. We stop this timer when the
* advertisement is playing, or when a user action is required to start, then we clear the * advertisement is playing, or when a user action is required to start, then we clear the
* timer on ad ready * timer on ad ready
* @param {number} time * @param {Number} time
* @param {string} from * @param {String} from
*/ */
startSafetyTimer(time, from) { startSafetyTimer(time, from) {
this.player.debug.log(`Safety timer invoked from: ${from}`); this.player.debug.log(`Safety timer invoked from: ${from}`);
@@ -608,7 +608,7 @@ class Ads {
/** /**
* Clear our safety timer(s) * Clear our safety timer(s)
* @param {string} from * @param {String} from
*/ */
clearSafetyTimer(from) { clearSafetyTimer(from) {
if (!is.nullOrUndefined(this.safetyTimer)) { if (!is.nullOrUndefined(this.safetyTimer)) {
+12 -6
View File
@@ -17,17 +17,17 @@ const parseVtt = vttDataString => {
if (!is.number(result.startTime)) { if (!is.number(result.startTime)) {
// The line with start and end times on it is the first line of interest // The line with start and end times on it is the first line of interest
const matchTimes = line.match( const matchTimes = line.match(
/([0-9]{2}):([0-9]{2}):([0-9]{2}).([0-9]{2,3})( ?--> ?)([0-9]{2}):([0-9]{2}):([0-9]{2}).([0-9]{2,3})/, /([0-9]{2})?:?([0-9]{2}):([0-9]{2}).([0-9]{2,3})( ?--> ?)([0-9]{2})?:?([0-9]{2}):([0-9]{2}).([0-9]{2,3})/,
); // Note that this currently ignores caption formatting directives that are optionally on the end of this line - fine for non-captions VTT ); // Note that this currently ignores caption formatting directives that are optionally on the end of this line - fine for non-captions VTT
if (matchTimes) { if (matchTimes) {
result.startTime = result.startTime =
Number(matchTimes[1]) * 60 * 60 + Number(matchTimes[1] || 0) * 60 * 60 +
Number(matchTimes[2]) * 60 + Number(matchTimes[2]) * 60 +
Number(matchTimes[3]) + Number(matchTimes[3]) +
Number(`0.${matchTimes[4]}`); Number(`0.${matchTimes[4]}`);
result.endTime = result.endTime =
Number(matchTimes[6]) * 60 * 60 + Number(matchTimes[6] || 0) * 60 * 60 +
Number(matchTimes[7]) * 60 + Number(matchTimes[7]) * 60 +
Number(matchTimes[8]) + Number(matchTimes[8]) +
Number(`0.${matchTimes[9]}`); Number(`0.${matchTimes[9]}`);
@@ -148,7 +148,10 @@ class PreviewThumbnails {
// If the URLs don't start with '/', then we need to set their relative path to be the location of the VTT file // If the URLs don't start with '/', then we need to set their relative path to be the location of the VTT file
// If the URLs do start with '/', then they obviously don't need a prefix, so it will remain blank // If the URLs do start with '/', then they obviously don't need a prefix, so it will remain blank
if (!thumbnail.frames[0].text.startsWith('/')) { // If the thumbnail URLs start with with none of '/', 'http://' or 'https://', then we need to set their relative path to be the location of the VTT file
if (!thumbnail.frames[0].text.startsWith('/') &&
!thumbnail.frames[0].text.startsWith('http://') &&
!thumbnail.frames[0].text.startsWith('https://')) {
thumbnail.urlPrefix = url.substring(0, url.lastIndexOf('/') + 1); thumbnail.urlPrefix = url.substring(0, url.lastIndexOf('/') + 1);
} }
@@ -220,6 +223,7 @@ class PreviewThumbnails {
// Only act on left mouse button (0), or touch device (event.button is false) // Only act on left mouse button (0), or touch device (event.button is false)
if (event.button === false || event.button === 0) { if (event.button === false || event.button === 0) {
this.mouseDown = true; this.mouseDown = true;
// Wait until media has a duration // Wait until media has a duration
if (this.player.media.duration) { if (this.player.media.duration) {
this.toggleScrubbingContainer(true); this.toggleScrubbingContainer(true);
@@ -307,7 +311,6 @@ class PreviewThumbnails {
if (this.mouseDown) { if (this.mouseDown) {
this.setScrubbingContainerSize(); this.setScrubbingContainerSize();
} else { } else {
this.toggleThumbContainer(true);
this.setThumbContainerSizeAndPos(); this.setThumbContainerSizeAndPos();
} }
@@ -319,7 +322,10 @@ class PreviewThumbnails {
const hasThumb = thumbNum >= 0; const hasThumb = thumbNum >= 0;
let qualityIndex = 0; let qualityIndex = 0;
this.toggleThumbContainer(hasThumb); // Show the thumb container if we're not scrubbing
if (!this.mouseDown) {
this.toggleThumbContainer(hasThumb);
}
// No matching thumb found // No matching thumb found
if (!hasThumb) { if (!hasThumb) {
+1 -1
View File
@@ -282,7 +282,7 @@ const vimeo = {
Promise.all([player.embed.getVideoWidth(), player.embed.getVideoHeight()]).then(dimensions => { Promise.all([player.embed.getVideoWidth(), player.embed.getVideoHeight()]).then(dimensions => {
const [width, height] = dimensions; const [width, height] = dimensions;
player.embed.ratio = `${width}:${height}`; player.embed.ratio = `${width}:${height}`;
setAspectRatio.call(this, player.embed.ratio); setAspectRatio.call(this);
}); });
// Set autopause // Set autopause
+16 -3
View File
@@ -34,6 +34,19 @@ function assurePlaybackState(play) {
} }
} }
function getHost(config) {
if (config.noCookie) {
return 'https://www.youtube-nocookie.com';
}
if (window.location.protocol === 'http:') {
return 'http://www.youtube.com';
}
// Use YouTube's default
return undefined;
}
const youtube = { const youtube = {
setup() { setup() {
// Add embed class for responsive // Add embed class for responsive
@@ -130,7 +143,7 @@ const youtube = {
player.media = replaceElement(container, player.media); player.media = replaceElement(container, player.media);
// Id to poster wrapper // Id to poster wrapper
const posterSrc = format => `https://img.youtube.com/vi/${videoId}/${format}default.jpg`; const posterSrc = format => `https://i.ytimg.com/vi/${videoId}/${format}default.jpg`;
// Check thumbnail images in order of quality, but reject fallback thumbnails (120px wide) // Check thumbnail images in order of quality, but reject fallback thumbnails (120px wide)
loadImage(posterSrc('maxres'), 121) // Higest quality and unpadded loadImage(posterSrc('maxres'), 121) // Higest quality and unpadded
@@ -151,7 +164,7 @@ const youtube = {
// https://developers.google.com/youtube/iframe_api_reference // https://developers.google.com/youtube/iframe_api_reference
player.embed = new window.YT.Player(id, { player.embed = new window.YT.Player(id, {
videoId, videoId,
host: config.noCookie ? 'https://www.youtube-nocookie.com' : undefined, host: getHost(config),
playerVars: extend( playerVars: extend(
{}, {},
{ {
@@ -386,7 +399,7 @@ const youtube = {
case 1: case 1:
// Restore paused state (YouTube starts playing on seek if the video hasn't been played yet) // Restore paused state (YouTube starts playing on seek if the video hasn't been played yet)
if (player.media.paused && !player.embed.hasPlayed) { if (!player.config.autoplay && player.media.paused && !player.embed.hasPlayed) {
player.media.pause(); player.media.pause();
} else { } else {
assurePlaybackState.call(player, true); assurePlaybackState.call(player, true);
+70 -39
View File
@@ -1,6 +1,6 @@
// ========================================================================== // ==========================================================================
// Plyr // Plyr
// plyr.js v3.5.0 // plyr.js v3.5.3
// https://github.com/sampotts/plyr // https://github.com/sampotts/plyr
// License: The MIT License (MIT) // License: The MIT License (MIT)
// ========================================================================== // ==========================================================================
@@ -26,6 +26,7 @@ import { off, on, once, triggerEvent, unbindListeners } from './utils/events';
import is from './utils/is'; import is from './utils/is';
import loadSprite from './utils/loadSprite'; import loadSprite from './utils/loadSprite';
import { cloneDeep, extend } from './utils/objects'; import { cloneDeep, extend } from './utils/objects';
import { getAspectRatio, reduceAspectRatio, setAspectRatio, validateRatio } from './utils/style';
import { parseUrl } from './utils/urls'; import { parseUrl } from './utils/urls';
// Private properties // Private properties
@@ -301,8 +302,8 @@ class Plyr {
} }
// Autoplay if required // Autoplay if required
if (this.config.autoplay) { if (this.isHTML5 && this.config.autoplay) {
this.play(); setTimeout(() => this.play(), 10);
} }
// Seek time will be recorded (in listeners.js) so we can prevent hiding controls for a few seconds after seek // Seek time will be recorded (in listeners.js) so we can prevent hiding controls for a few seconds after seek
@@ -403,7 +404,7 @@ class Plyr {
/** /**
* Toggle playback based on current status * Toggle playback based on current status
* @param {boolean} input * @param {Boolean} input
*/ */
togglePlay(input) { togglePlay(input) {
// Toggle based on current state if nothing passed // Toggle based on current state if nothing passed
@@ -437,7 +438,7 @@ class Plyr {
/** /**
* Rewind * Rewind
* @param {number} seekTime - how far to rewind in seconds. Defaults to the config.seekTime * @param {Number} seekTime - how far to rewind in seconds. Defaults to the config.seekTime
*/ */
rewind(seekTime) { rewind(seekTime) {
this.currentTime = this.currentTime - (is.number(seekTime) ? seekTime : this.config.seekTime); this.currentTime = this.currentTime - (is.number(seekTime) ? seekTime : this.config.seekTime);
@@ -445,7 +446,7 @@ class Plyr {
/** /**
* Fast forward * Fast forward
* @param {number} seekTime - how far to fast forward in seconds. Defaults to the config.seekTime * @param {Number} seekTime - how far to fast forward in seconds. Defaults to the config.seekTime
*/ */
forward(seekTime) { forward(seekTime) {
this.currentTime = this.currentTime + (is.number(seekTime) ? seekTime : this.config.seekTime); this.currentTime = this.currentTime + (is.number(seekTime) ? seekTime : this.config.seekTime);
@@ -453,7 +454,7 @@ class Plyr {
/** /**
* Seek to a time * Seek to a time
* @param {number} input - where to seek to in seconds. Defaults to 0 (the start) * @param {Number} input - where to seek to in seconds. Defaults to 0 (the start)
*/ */
set currentTime(input) { set currentTime(input) {
// Bail if media duration isn't available yet // Bail if media duration isn't available yet
@@ -523,7 +524,7 @@ class Plyr {
/** /**
* Set the player volume * Set the player volume
* @param {number} value - must be between 0 and 1. Defaults to the value from local storage and config.volume if not set in storage * @param {Number} value - must be between 0 and 1. Defaults to the value from local storage and config.volume if not set in storage
*/ */
set volume(value) { set volume(value) {
let volume = value; let volume = value;
@@ -574,7 +575,7 @@ class Plyr {
/** /**
* Increase volume * Increase volume
* @param {boolean} step - How much to decrease by (between 0 and 1) * @param {Boolean} step - How much to decrease by (between 0 and 1)
*/ */
increaseVolume(step) { increaseVolume(step) {
const volume = this.media.muted ? 0 : this.volume; const volume = this.media.muted ? 0 : this.volume;
@@ -583,7 +584,7 @@ class Plyr {
/** /**
* Decrease volume * Decrease volume
* @param {boolean} step - How much to decrease by (between 0 and 1) * @param {Boolean} step - How much to decrease by (between 0 and 1)
*/ */
decreaseVolume(step) { decreaseVolume(step) {
this.increaseVolume(-step); this.increaseVolume(-step);
@@ -591,7 +592,7 @@ class Plyr {
/** /**
* Set muted state * Set muted state
* @param {boolean} mute * @param {Boolean} mute
*/ */
set muted(mute) { set muted(mute) {
let toggle = mute; let toggle = mute;
@@ -643,7 +644,7 @@ class Plyr {
/** /**
* Set playback speed * Set playback speed
* @param {number} speed - the speed of playback (0.5-2.0) * @param {Number} speed - the speed of playback (0.5-2.0)
*/ */
set speed(input) { set speed(input) {
let speed = null; let speed = null;
@@ -690,7 +691,7 @@ class Plyr {
/** /**
* Set playback quality * Set playback quality
* Currently HTML5 & YouTube only * Currently HTML5 & YouTube only
* @param {number} input - Quality level * @param {Number} input - Quality level
*/ */
set quality(input) { set quality(input) {
const config = this.config.quality; const config = this.config.quality;
@@ -740,7 +741,7 @@ class Plyr {
/** /**
* Toggle loop * Toggle loop
* TODO: Finish fancy new logic. Set the indicator on load as user may pass loop as config * TODO: Finish fancy new logic. Set the indicator on load as user may pass loop as config
* @param {boolean} input - Whether to loop or not * @param {Boolean} input - Whether to loop or not
*/ */
set loop(input) { set loop(input) {
const toggle = is.boolean(input) ? input : this.config.loop.active; const toggle = is.boolean(input) ? input : this.config.loop.active;
@@ -800,7 +801,7 @@ class Plyr {
/** /**
* Set new media source * Set new media source
* @param {object} input - The new source object (see docs) * @param {Object} input - The new source object (see docs)
*/ */
set source(input) { set source(input) {
source.change.call(this, input); source.change.call(this, input);
@@ -824,7 +825,7 @@ class Plyr {
/** /**
* Set the poster image for a video * Set the poster image for a video
* @param {input} - the URL for the new poster image * @param {String} input - the URL for the new poster image
*/ */
set poster(input) { set poster(input) {
if (!this.isVideo) { if (!this.isVideo) {
@@ -846,9 +847,37 @@ class Plyr {
return this.media.getAttribute('poster'); return this.media.getAttribute('poster');
} }
/**
* Get the current aspect ratio in use
*/
get ratio() {
const ratio = reduceAspectRatio(getAspectRatio.call(this));
return is.array(ratio) ? ratio.join(':') : ratio;
}
/**
* Set video aspect ratio
*/
set ratio(input) {
if (!this.isVideo) {
this.debug.warn('Aspect ratio can only be set for video');
return;
}
if (!is.string(input) || !validateRatio(input)) {
this.debug.error(`Invalid aspect ratio specified (${input})`);
return;
}
this.config.ratio = input;
setAspectRatio.call(this);
}
/** /**
* Set the autoplay state * Set the autoplay state
* @param {boolean} input - Whether to autoplay or not * @param {Boolean} input - Whether to autoplay or not
*/ */
set autoplay(input) { set autoplay(input) {
const toggle = is.boolean(input) ? input : this.config.autoplay; const toggle = is.boolean(input) ? input : this.config.autoplay;
@@ -864,7 +893,7 @@ class Plyr {
/** /**
* Toggle captions * Toggle captions
* @param {boolean} input - Whether to enable captions * @param {Boolean} input - Whether to enable captions
*/ */
toggleCaptions(input) { toggleCaptions(input) {
captions.toggle.call(this, input, false); captions.toggle.call(this, input, false);
@@ -872,7 +901,7 @@ class Plyr {
/** /**
* Set the caption track by index * Set the caption track by index
* @param {number} - Caption index * @param {Number} - Caption index
*/ */
set currentTrack(input) { set currentTrack(input) {
captions.set.call(this, input, false); captions.set.call(this, input, false);
@@ -889,7 +918,7 @@ class Plyr {
/** /**
* Set the wanted language for captions * Set the wanted language for captions
* Since tracks can be added later it won't update the actual caption track until there is a matching track * Since tracks can be added later it won't update the actual caption track until there is a matching track
* @param {string} - Two character ISO language code (e.g. EN, FR, PT, etc) * @param {String} - Two character ISO language code (e.g. EN, FR, PT, etc)
*/ */
set language(input) { set language(input) {
captions.setLanguage.call(this, input, false); captions.setLanguage.call(this, input, false);
@@ -962,7 +991,7 @@ class Plyr {
/** /**
* Toggle the player controls * Toggle the player controls
* @param {boolean} [toggle] - Whether to show the controls * @param {Boolean} [toggle] - Whether to show the controls
*/ */
toggleControls(toggle) { toggleControls(toggle) {
// Don't toggle if missing UI support or if it's audio // Don't toggle if missing UI support or if it's audio
@@ -995,8 +1024,8 @@ class Plyr {
/** /**
* Add event listeners * Add event listeners
* @param {string} event - Event type * @param {String} event - Event type
* @param {function} callback - Callback for when event occurs * @param {Function} callback - Callback for when event occurs
*/ */
on(event, callback) { on(event, callback) {
on.call(this, this.elements.container, event, callback); on.call(this, this.elements.container, event, callback);
@@ -1004,8 +1033,8 @@ class Plyr {
/** /**
* Add event listeners once * Add event listeners once
* @param {string} event - Event type * @param {String} event - Event type
* @param {function} callback - Callback for when event occurs * @param {Function} callback - Callback for when event occurs
*/ */
once(event, callback) { once(event, callback) {
once.call(this, this.elements.container, event, callback); once.call(this, this.elements.container, event, callback);
@@ -1013,8 +1042,8 @@ class Plyr {
/** /**
* Remove event listeners * Remove event listeners
* @param {string} event - Event type * @param {String} event - Event type
* @param {function} callback - Callback for when event occurs * @param {Function} callback - Callback for when event occurs
*/ */
off(event, callback) { off(event, callback) {
off(this.elements.container, event, callback); off(this.elements.container, event, callback);
@@ -1024,8 +1053,8 @@ class Plyr {
* Destroy an instance * Destroy an instance
* Event listeners are removed when elements are removed * Event listeners are removed when elements are removed
* http://stackoverflow.com/questions/12528049/if-a-dom-element-is-removed-are-its-listeners-also-removed-from-memory * http://stackoverflow.com/questions/12528049/if-a-dom-element-is-removed-are-its-listeners-also-removed-from-memory
* @param {function} callback - Callback for when destroy is complete * @param {Function} callback - Callback for when destroy is complete
* @param {boolean} soft - Whether it's a soft destroy (for source changes etc) * @param {Boolean} soft - Whether it's a soft destroy (for source changes etc)
*/ */
destroy(callback, soft = false) { destroy(callback, soft = false) {
if (!this.ready) { if (!this.ready) {
@@ -1088,11 +1117,13 @@ class Plyr {
// Stop playback // Stop playback
this.stop(); this.stop();
// Clear timeouts
clearTimeout(this.timers.loading);
clearTimeout(this.timers.controls);
clearTimeout(this.timers.resized);
// Provider specific stuff // Provider specific stuff
if (this.isHTML5) { if (this.isHTML5) {
// Clear timeout
clearTimeout(this.timers.loading);
// Restore native video controls // Restore native video controls
ui.toggleNativeControls.call(this, true); ui.toggleNativeControls.call(this, true);
@@ -1124,7 +1155,7 @@ class Plyr {
/** /**
* Check for support for a mime type (HTML5 only) * Check for support for a mime type (HTML5 only)
* @param {string} type - Mime type * @param {String} type - Mime type
*/ */
supports(type) { supports(type) {
return support.mime.call(this, type); return support.mime.call(this, type);
@@ -1132,9 +1163,9 @@ class Plyr {
/** /**
* Check for support * Check for support
* @param {string} type - Player type (audio/video) * @param {String} type - Player type (audio/video)
* @param {string} provider - Provider (html5/youtube/vimeo) * @param {String} provider - Provider (html5/youtube/vimeo)
* @param {bool} inline - Where player has `playsinline` sttribute * @param {Boolean} inline - Where player has `playsinline` sttribute
*/ */
static supported(type, provider, inline) { static supported(type, provider, inline) {
return support.check(type, provider, inline); return support.check(type, provider, inline);
@@ -1142,8 +1173,8 @@ class Plyr {
/** /**
* Load an SVG sprite into the page * Load an SVG sprite into the page
* @param {string} url - URL for the SVG sprite * @param {String} url - URL for the SVG sprite
* @param {string} [id] - Unique ID * @param {String} [id] - Unique ID
*/ */
static loadSprite(url, id) { static loadSprite(url, id) {
return loadSprite(url, id); return loadSprite(url, id);
@@ -1152,7 +1183,7 @@ class Plyr {
/** /**
* Setup multiple instances * Setup multiple instances
* @param {*} selector * @param {*} selector
* @param {object} options * @param {Object} options
*/ */
static setup(selector, options = {}) { static setup(selector, options = {}) {
let targets = null; let targets = null;
+1 -1
View File
@@ -1,6 +1,6 @@
// ========================================================================== // ==========================================================================
// Plyr Polyfilled Build // Plyr Polyfilled Build
// plyr.js v3.5.0 // plyr.js v3.5.3
// https://github.com/sampotts/plyr // https://github.com/sampotts/plyr
// License: The MIT License (MIT) // License: The MIT License (MIT)
// ========================================================================== // ==========================================================================
+53 -14
View File
@@ -4,26 +4,63 @@
import is from './is'; import is from './is';
/* function reduceAspectRatio(width, height) { export function validateRatio(input) {
const getRatio = (w, h) => (h === 0 ? w : getRatio(h, w % h)); if (!is.array(input) && (!is.string(input) || !input.includes(':'))) {
const ratio = getRatio(width, height); return false;
return `${width / ratio}:${height / ratio}`; }
} */
const ratio = is.array(input) ? input : input.split(':');
return ratio.map(Number).every(is.number);
}
export function reduceAspectRatio(ratio) {
if (!is.array(ratio) || !ratio.every(is.number)) {
return null;
}
const [width, height] = ratio;
const getDivider = (w, h) => (h === 0 ? w : getDivider(h, w % h));
const divider = getDivider(width, height);
return [width / divider, height / divider];
}
export function getAspectRatio(input) {
const parse = ratio => {
if (!validateRatio(ratio)) {
return null;
}
return ratio.split(':').map(Number);
};
// Provided ratio
let ratio = parse(input);
// Get from config
if (ratio === null) {
ratio = parse(this.config.ratio);
}
// Get from embed
if (ratio === null && !is.empty(this.embed) && is.string(this.embed.ratio)) {
ratio = parse(this.embed.ratio);
}
return ratio;
}
// Set aspect ratio for responsive container // Set aspect ratio for responsive container
export function setAspectRatio(input) { export function setAspectRatio(input) {
let ratio = input; if (!this.isVideo) {
return {};
if (!is.string(ratio) && !is.nullOrUndefined(this.embed)) {
({ ratio } = this.embed);
} }
if (!is.string(ratio)) { const ratio = getAspectRatio.call(this, input);
({ ratio } = this.config);
}
const [x, y] = ratio.split(':').map(Number); const [w, h] = is.array(ratio) ? ratio : [0, 0];
const padding = (100 / x) * y; const padding = (100 / w) * h;
this.elements.wrapper.style.paddingBottom = `${padding}%`; this.elements.wrapper.style.paddingBottom = `${padding}%`;
@@ -32,6 +69,8 @@ export function setAspectRatio(input) {
const height = 240; const height = 240;
const offset = (height - padding) / (height / 50); const offset = (height - padding) / (height / 50);
this.media.style.transform = `translateY(-${offset}%)`; this.media.style.transform = `translateY(-${offset}%)`;
} else if (this.isHTML5) {
this.elements.wrapper.classList.toggle(this.config.classNames.videoFixedRatio, ratio !== null);
} }
return { padding, ratio }; return { padding, ratio };
+2 -2
View File
@@ -6,8 +6,8 @@ import is from './is';
/** /**
* Parse a string to a URL object * Parse a string to a URL object
* @param {string} input - the URL to be parsed * @param {String} input - the URL to be parsed
* @param {boolean} safe - failsafe parsing * @param {Boolean} safe - failsafe parsing
*/ */
export function parseUrl(input, safe = true) { export function parseUrl(input, safe = true) {
let url = input; let url = input;
-36
View File
@@ -1,36 +0,0 @@
// --------------------------------------------------------------
// Embedded players
// YouTube, Vimeo, etc
// --------------------------------------------------------------
// Default to 16:9 ratio but this is set by JavaScript based on config
$embed-padding: ((100 / 16) * 9);
.plyr__video-embed {
height: 0;
padding-bottom: to-percentage($embed-padding);
position: relative;
iframe {
border: 0;
height: 100%;
left: 0;
position: absolute;
top: 0;
user-select: none;
width: 100%;
}
}
// If the full custom UI is supported
.plyr--full-ui .plyr__video-embed {
$height: 240;
$offset: to-percentage(($height - $embed-padding) / ($height / 50));
// Only used for Vimeo
> .plyr__video-embed__container {
padding-bottom: to-percentage($height);
position: relative;
transform: translateY(-$offset);
}
}
+33
View File
@@ -20,3 +20,36 @@
// Require z-index to force border-radius // Require z-index to force border-radius
z-index: 0; z-index: 0;
} }
// Default to 16:9 ratio but this is set by JavaScript based on config
$embed-padding: ((100 / 16) * 9);
.plyr__video-embed,
.plyr__video-wrapper--fixed-ratio {
height: 0;
padding-bottom: to-percentage($embed-padding);
}
.plyr__video-embed iframe,
.plyr__video-wrapper--fixed-ratio video {
border: 0;
height: 100%;
left: 0;
position: absolute;
top: 0;
user-select: none;
width: 100%;
}
// If the full custom UI is supported
.plyr--full-ui .plyr__video-embed {
$height: 240;
$offset: to-percentage(($height - $embed-padding) / ($height / 50));
// Only used for Vimeo
> .plyr__video-embed__container {
padding-bottom: to-percentage($height);
position: relative;
transform: translateY(-$offset);
}
}
-1
View File
@@ -29,7 +29,6 @@
@import 'components/captions'; @import 'components/captions';
@import 'components/control'; @import 'components/control';
@import 'components/controls'; @import 'components/controls';
@import 'components/embed';
@import 'components/menus'; @import 'components/menus';
@import 'components/sliders'; @import 'components/sliders';
@import 'components/poster'; @import 'components/poster';
+778 -902
View File
File diff suppressed because it is too large Load Diff