Linting changes

This commit is contained in:
Sam Potts 2019-06-01 18:45:07 +10:00
parent 5ddd9e02de
commit c9055f391b
42 changed files with 5246 additions and 4376 deletions

View File

@ -1,6 +1,7 @@
{ {
"parser": "babel-eslint", "parser": "babel-eslint",
"extends": ["airbnb-base", "prettier"], "extends": ["airbnb-base", "prettier"],
"plugins": ["simple-import-sort", "import"],
"env": { "env": {
"browser": true, "browser": true,
"es6": true "es6": true
@ -10,24 +11,18 @@
"jQuery": false "jQuery": false
}, },
"rules": { "rules": {
"import/no-cycle": 1, "import/no-cycle": "warn",
"no-const-assign": 1, "padding-line-between-statements": [
"no-shadow": 0, "error",
"no-this-before-super": 1, {
"no-undef": 1, "blankLine": "never",
"no-unreachable": 1, "prev": ["singleline-const", "singleline-let", "singleline-var"],
"no-unused-vars": 1, "next": ["singleline-const", "singleline-let", "singleline-var"]
"constructor-super": 1, }
"valid-typeof": 1, ],
"indent": [2, 4, { "SwitchCase": 1 }], "sort-imports": "off",
"quotes": [2, "single", "avoid-escape"], "import/order": "off",
"semi": [2, "always"], "simple-import-sort/sort": "error"
"eqeqeq": [2, "always"],
"one-var": [2, "never"],
"comma-dangle": [2, "always-multiline"],
"spaced-comment": [2, "always"],
"no-restricted-globals": 2,
"no-param-reassign": [2, { "props": false }]
}, },
"parserOptions": { "parserOptions": {
"sourceType": "module" "sourceType": "module"

2
demo/dist/demo.css vendored

File diff suppressed because one or more lines are too long

1126
demo/dist/demo.js vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -50,6 +50,17 @@
type="font/woff2" type="font/woff2"
href="https://cdn.plyr.io/static/fonts/gordita-bold.woff2" href="https://cdn.plyr.io/static/fonts/gordita-bold.woff2"
/> />
<!-- Google Analytics-->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-132699580-1"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'UA-132699580-1');
</script>
</head> </head>
<body> <body>

View File

@ -5,6 +5,7 @@
// ========================================================================== // ==========================================================================
import Raven from 'raven-js'; import Raven from 'raven-js';
import Plyr from '../../../src/js/plyr'; import Plyr from '../../../src/js/plyr';
(() => { (() => {

2
dist/plyr.css vendored

File diff suppressed because one or more lines are too long

1120
dist/plyr.js vendored

File diff suppressed because it is too large Load Diff

2
dist/plyr.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
dist/plyr.min.mjs vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1120
dist/plyr.mjs vendored

File diff suppressed because it is too large Load Diff

2427
dist/plyr.polyfilled.js vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2427
dist/plyr.polyfilled.mjs vendored

File diff suppressed because it is too large Load Diff

View File

@ -6,24 +6,28 @@
const path = require('path'); const path = require('path');
const gulp = require('gulp'); const gulp = require('gulp');
// ------------------------------------
// JavaScript // JavaScript
// ------------------------------------
const terser = require('gulp-terser'); const terser = require('gulp-terser');
const rollup = require('gulp-better-rollup'); const rollup = require('gulp-better-rollup');
const babel = require('rollup-plugin-babel'); const babel = require('rollup-plugin-babel');
const commonjs = require('rollup-plugin-commonjs'); const commonjs = require('rollup-plugin-commonjs');
const resolve = require('rollup-plugin-node-resolve'); const resolve = require('rollup-plugin-node-resolve');
// ------------------------------------
// CSS // CSS
// ------------------------------------
const sass = require('gulp-sass'); const sass = require('gulp-sass');
const clean = require('gulp-clean-css'); const clean = require('gulp-clean-css');
const prefix = require('gulp-autoprefixer'); const prefix = require('gulp-autoprefixer');
// ------------------------------------
// Images // Images
// ------------------------------------
const svgstore = require('gulp-svgstore'); const svgstore = require('gulp-svgstore');
const imagemin = require('gulp-imagemin'); const imagemin = require('gulp-imagemin');
// ------------------------------------
// Utils // Utils
// ------------------------------------
const del = require('del'); const del = require('del');
const filter = require('gulp-filter'); const filter = require('gulp-filter');
const header = require('gulp-header'); const header = require('gulp-header');
@ -37,18 +41,22 @@ const plumber = require('gulp-plumber');
const size = require('gulp-size'); const size = require('gulp-size');
const sourcemaps = require('gulp-sourcemaps'); const sourcemaps = require('gulp-sourcemaps');
const through = require('through2'); const through = require('through2');
// ------------------------------------
// Deployment // Deployment
// ------------------------------------
const aws = require('aws-sdk'); const aws = require('aws-sdk');
const publish = require('gulp-awspublish'); const publish = require('gulp-awspublish');
const FastlyPurge = require('fastly-purge'); const FastlyPurge = require('fastly-purge');
// ------------------------------------
// Configs
// ------------------------------------
const pkg = require('./package.json'); const pkg = require('./package.json');
const build = require('./build.json'); const build = require('./build.json');
const deploy = require('./deploy.json'); const deploy = require('./deploy.json');
// ------------------------------------
// Info from package
// ------------------------------------
const { browserslist: browsers, version } = pkg; const { browserslist: browsers, version } = pkg;
const minSuffix = '.min'; const minSuffix = '.min';
// Get AWS config // Get AWS config

View File

@ -31,31 +31,33 @@
"scripts": { "scripts": {
"build": "gulp build", "build": "gulp build",
"lint": "eslint src/js && npm run-script remark", "lint": "eslint src/js && npm run-script remark",
"lint:fix": "eslint --fix src/js",
"remark": "remark -f --use 'validate-links=repository:\"sampotts/plyr\"' '{,!(node_modules),.?**/}*.md'", "remark": "remark -f --use 'validate-links=repository:\"sampotts/plyr\"' '{,!(node_modules),.?**/}*.md'",
"deploy": "yarn lint && gulp deploy" "deploy": "yarn lint && gulp deploy"
}, },
"devDependencies": { "devDependencies": {
"ansi-colors": "^3.2.4", "ansi-colors": "^3.2.4",
"aws-sdk": "^2.437.0", "aws-sdk": "^2.466.0",
"@babel/core": "^7.4.3", "@babel/core": "^7.4.5",
"@babel/preset-env": "^7.4.3", "@babel/preset-env": "^7.4.5",
"babel-eslint": "^10.0.1", "babel-eslint": "^10.0.1",
"del": "^4.1.0", "del": "^4.1.1",
"eslint": "^5.16.0", "eslint": "^5.16.0",
"eslint-config-airbnb-base": "^13.1.0", "eslint-config-airbnb-base": "^13.1.0",
"eslint-config-prettier": "^4.1.0", "eslint-config-prettier": "^4.3.0",
"eslint-plugin-import": "^2.16.0", "eslint-plugin-import": "^2.17.3",
"eslint-plugin-simple-import-sort": "^3.1.1",
"fancy-log": "^1.3.3", "fancy-log": "^1.3.3",
"fastly-purge": "^1.0.1", "fastly-purge": "^1.0.1",
"git-branch": "^2.0.1", "git-branch": "^2.0.1",
"gulp": "^4.0.0", "gulp": "^4.0.2",
"gulp-autoprefixer": "^6.0.0", "gulp-autoprefixer": "^6.1.0",
"gulp-awspublish": "^4.0.0", "gulp-awspublish": "^4.0.0",
"gulp-better-rollup": "^4.0.1", "gulp-better-rollup": "^4.0.1",
"gulp-clean-css": "^4.0.0", "gulp-clean-css": "^4.2.0",
"gulp-filter": "^5.1.0", "gulp-filter": "^6.0.0",
"gulp-header": "^2.0.7", "gulp-header": "^2.0.7",
"gulp-imagemin": "^5.0.3", "gulp-imagemin": "^6.0.0",
"gulp-open": "^3.0.1", "gulp-open": "^3.0.1",
"gulp-plumber": "^1.2.1", "gulp-plumber": "^1.2.1",
"gulp-postcss": "^8.0.0", "gulp-postcss": "^8.0.0",
@ -65,31 +67,31 @@
"gulp-size": "^3.0.0", "gulp-size": "^3.0.0",
"gulp-sourcemaps": "^2.6.5", "gulp-sourcemaps": "^2.6.5",
"gulp-svgstore": "^7.0.1", "gulp-svgstore": "^7.0.1",
"gulp-terser": "^1.1.7", "gulp-terser": "^1.2.0",
"postcss-custom-properties": "^8.0.10", "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.2", "remark-validate-links": "^8.0.2",
"rollup": "^1.10.0", "rollup": "^1.13.0",
"rollup-plugin-babel": "^4.3.2", "rollup-plugin-babel": "^4.3.2",
"rollup-plugin-commonjs": "^9.3.4", "rollup-plugin-commonjs": "^10.0.0",
"rollup-plugin-node-resolve": "^4.2.3", "rollup-plugin-node-resolve": "^5.0.1",
"stylelint": "^9.10.1", "stylelint": "^10.0.1",
"stylelint-config-prettier": "^5.0.0", "stylelint-config-prettier": "^5.2.0",
"stylelint-config-recommended": "^2.1.0", "stylelint-config-recommended": "^2.2.0",
"stylelint-config-sass-guidelines": "^5.4.0", "stylelint-config-sass-guidelines": "^6.0.0",
"stylelint-order": "^2.2.1", "stylelint-order": "^3.0.0",
"stylelint-scss": "^3.5.4", "stylelint-scss": "^3.8.0",
"stylelint-selector-bem-pattern": "^2.1.0", "stylelint-selector-bem-pattern": "^2.1.0",
"through2": "^3.0.1" "through2": "^3.0.1"
}, },
"dependencies": { "dependencies": {
"core-js": "^3.0.1", "core-js": "^3.1.3",
"custom-event-polyfill": "^1.0.7", "custom-event-polyfill": "^1.0.7",
"loadjs": "^3.6.1", "loadjs": "^3.6.1",
"rangetouch": "^2.0.0", "rangetouch": "^2.0.0",
"raven-js": "^3.27.0", "raven-js": "^3.27.1",
"url-polyfill": "^1.1.5" "url-polyfill": "^1.1.5"
} }
} }

View File

@ -23,9 +23,6 @@
"editor.tabSize": 4, "editor.tabSize": 4,
"editor.insertSpaces": true, "editor.insertSpaces": true,
"editor.formatOnSave": true, "editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true
},
// Trim on save // Trim on save
"files.trimTrailingWhitespace": true "files.trimTrailingWhitespace": true

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.3/plyr.js"></script> <script src="https://cdn.plyr.io/3.5.4/plyr.js"></script>
``` ```
...or... ...or...
```html ```html
<script src="https://cdn.plyr.io/3.5.3/plyr.polyfilled.js"></script> <script src="https://cdn.plyr.io/3.5.4/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.3/plyr.css" /> <link rel="stylesheet" href="https://cdn.plyr.io/3.5.4/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.3/plyr.svg`. reference, the CDN hosted SVG sprite can be found at `https://cdn.plyr.io/3.5.4/plyr.svg`.
# Ads # Ads

View File

@ -85,7 +85,6 @@ const captions = {
const browserLanguages = navigator.languages || [navigator.language || navigator.userLanguage || 'en']; const browserLanguages = navigator.languages || [navigator.language || navigator.userLanguage || 'en'];
const languages = dedupe(browserLanguages.map(language => language.split('-')[0])); const languages = dedupe(browserLanguages.map(language => language.split('-')[0]));
let language = (this.storage.get('language') || this.config.captions.language || 'auto').toLowerCase(); let language = (this.storage.get('language') || this.config.captions.language || 'auto').toLowerCase();
// Use first browser language when language is 'auto' // Use first browser language when language is 'auto'
@ -134,7 +133,7 @@ const captions = {
}); });
// Turn off native caption rendering to avoid double captions // Turn off native caption rendering to avoid double captions
track.mode = 'hidden'; Object.assign(track, { mode: 'hidden' });
// Add event listener for cue changes // Add event listener for cue changes
on.call(this, track, 'cuechange', () => captions.updateCues.call(this)); on.call(this, track, 'cuechange', () => captions.updateCues.call(this));
@ -166,7 +165,6 @@ const captions = {
const { toggled } = this.captions; // Current state const { toggled } = this.captions; // Current state
const activeClass = this.config.classNames.captions.active; const activeClass = this.config.classNames.captions.active;
// Get the next state // Get the next state
// If the method is called without parameter, toggle based on current value // If the method is called without parameter, toggle based on current value
const active = is.nullOrUndefined(input) ? !toggled : input; const active = is.nullOrUndefined(input) ? !toggled : input;
@ -304,7 +302,7 @@ const captions = {
let track; let track;
languages.every(language => { languages.every(language => {
track = sorted.find(track => track.language === language); track = sorted.find(t => t.language === language);
return !track; // Break iteration if there is a match return !track; // Break iteration if there is a match
}); });

52
src/js/controls.js vendored
View File

@ -4,13 +4,27 @@
// ========================================================================== // ==========================================================================
import RangeTouch from 'rangetouch'; import RangeTouch from 'rangetouch';
import captions from './captions'; import captions from './captions';
import html5 from './html5'; import html5 from './html5';
import support from './support'; import support from './support';
import { repaint, transitionEndEvent } from './utils/animation'; import { repaint, transitionEndEvent } from './utils/animation';
import { dedupe } from './utils/arrays'; import { dedupe } from './utils/arrays';
import browser from './utils/browser'; import browser from './utils/browser';
import { createElement, emptyElement, getAttributesFromSelector, getElement, getElements, hasClass, matches, removeElement, setAttributes, setFocus, toggleClass, toggleHidden } from './utils/elements'; import {
createElement,
emptyElement,
getAttributesFromSelector,
getElement,
getElements,
hasClass,
matches,
removeElement,
setAttributes,
setFocus,
toggleClass,
toggleHidden,
} from './utils/elements';
import { off, on } from './utils/events'; import { off, on } from './utils/events';
import i18n from './utils/i18n'; import i18n from './utils/i18n';
import is from './utils/is'; import is from './utils/is';
@ -92,7 +106,6 @@ const controls = {
const namespace = 'http://www.w3.org/2000/svg'; const namespace = 'http://www.w3.org/2000/svg';
const iconUrl = controls.getIconUrl.call(this); const iconUrl = controls.getIconUrl.call(this);
const iconPath = `${!iconUrl.cors ? iconUrl.url : ''}#${this.config.iconPrefix}`; const iconPath = `${!iconUrl.cors ? iconUrl.url : ''}#${this.config.iconPrefix}`;
// Create <svg> // Create <svg>
const icon = document.createElementNS(namespace, 'svg'); const icon = document.createElementNS(namespace, 'svg');
setAttributes( setAttributes(
@ -480,15 +493,15 @@ const controls = {
get() { get() {
return menuItem.getAttribute('aria-checked') === 'true'; return menuItem.getAttribute('aria-checked') === 'true';
}, },
set(checked) { set(check) {
// Ensure exclusivity // Ensure exclusivity
if (checked) { if (check) {
Array.from(menuItem.parentNode.children) Array.from(menuItem.parentNode.children)
.filter(node => matches(node, '[role="menuitemradio"]')) .filter(node => matches(node, '[role="menuitemradio"]'))
.forEach(node => node.setAttribute('aria-checked', 'false')); .forEach(node => node.setAttribute('aria-checked', 'false'));
} }
menuItem.setAttribute('aria-checked', checked ? 'true' : 'false'); menuItem.setAttribute('aria-checked', check ? 'true' : 'false');
}, },
}); });
@ -596,17 +609,17 @@ const controls = {
let value = 0; let value = 0;
const setProgress = (target, input) => { const setProgress = (target, input) => {
const value = is.number(input) ? input : 0; const val = is.number(input) ? input : 0;
const progress = is.element(target) ? target : this.elements.display.buffer; const progress = is.element(target) ? target : this.elements.display.buffer;
// Update value and label // Update value and label
if (is.element(progress)) { if (is.element(progress)) {
progress.value = value; progress.value = val;
// Update text label inside // Update text label inside
const label = progress.getElementsByTagName('span')[0]; const label = progress.getElementsByTagName('span')[0];
if (is.element(label)) { if (is.element(label)) {
label.childNodes[0].nodeValue = value; label.childNodes[0].nodeValue = val;
} }
} }
}; };
@ -688,14 +701,8 @@ const controls = {
return; return;
} }
// Calculate percentage
let percent = 0;
const clientRect = this.elements.progress.getBoundingClientRect();
const visible = `${this.config.classNames.tooltip}--visible`; const visible = `${this.config.classNames.tooltip}--visible`;
const toggle = show => toggleClass(this.elements.display.seekTooltip, visible, show);
const toggle = toggle => {
toggleClass(this.elements.display.seekTooltip, visible, toggle);
};
// Hide on touch // Hide on touch
if (this.touch) { if (this.touch) {
@ -704,6 +711,9 @@ const controls = {
} }
// Determine percentage, if already visible // Determine percentage, if already visible
let percent = 0;
const clientRect = this.elements.progress.getBoundingClientRect();
if (is.event(event)) { if (is.event(event)) {
percent = (100 / clientRect.width) * (event.pageX - clientRect.left); percent = (100 / clientRect.width) * (event.pageX - clientRect.left);
} else if (hasClass(this.elements.display.seekTooltip, visible)) { } else if (hasClass(this.elements.display.seekTooltip, visible)) {
@ -1100,7 +1110,7 @@ const controls = {
let target = pane; let target = pane;
if (!is.element(target)) { if (!is.element(target)) {
target = Object.values(this.elements.settings.panels).find(pane => !pane.hidden); target = Object.values(this.elements.settings.panels).find(p => !p.hidden);
} }
const firstItem = target.querySelector('[role^="menuitem"]'); const firstItem = target.querySelector('[role^="menuitem"]');
@ -1398,7 +1408,7 @@ const controls = {
// Settings button / menu // Settings button / menu
if (control === 'settings' && !is.empty(this.config.settings)) { if (control === 'settings' && !is.empty(this.config.settings)) {
const control = createElement( const wrapper = createElement(
'div', 'div',
extend({}, defaultAttributes, { extend({}, defaultAttributes, {
class: `${defaultAttributes.class} plyr__menu`.trim(), class: `${defaultAttributes.class} plyr__menu`.trim(),
@ -1406,7 +1416,7 @@ const controls = {
}), }),
); );
control.appendChild( wrapper.appendChild(
createButton.call(this, 'settings', { createButton.call(this, 'settings', {
'aria-haspopup': true, 'aria-haspopup': true,
'aria-controls': `plyr-settings-${data.id}`, 'aria-controls': `plyr-settings-${data.id}`,
@ -1546,11 +1556,11 @@ const controls = {
}); });
popup.appendChild(inner); popup.appendChild(inner);
control.appendChild(popup); wrapper.appendChild(popup);
container.appendChild(control); container.appendChild(wrapper);
this.elements.settings.popup = popup; this.elements.settings.popup = popup;
this.elements.settings.menu = control; this.elements.settings.menu = wrapper;
} }
// Picture in picture button // Picture in picture button

View File

@ -52,7 +52,7 @@ const html5 = {
get() { get() {
// Get sources // Get sources
const sources = html5.getSources.call(player); const sources = html5.getSources.call(player);
const source = sources.find(source => source.getAttribute('src') === player.source); const source = sources.find(s => s.getAttribute('src') === player.source);
// Return size, if match is found // Return size, if match is found
return source && Number(source.getAttribute('size')); return source && Number(source.getAttribute('size'));
@ -60,9 +60,8 @@ const html5 = {
set(input) { set(input) {
// Get sources // Get sources
const sources = html5.getSources.call(player); const sources = html5.getSources.call(player);
// Get first match for requested size // Get first match for requested size
const source = sources.find(source => Number(source.getAttribute('size')) === input); const source = sources.find(s => Number(s.getAttribute('size')) === input);
// No matching source found // No matching source found
if (!source) { if (!source) {

View File

@ -147,7 +147,7 @@ class Listeners {
player.loop = !player.loop; player.loop = !player.loop;
break; break;
/* case 73: /* case 73:
this.setLoop('start'); this.setLoop('start');
break; break;
@ -275,17 +275,16 @@ class Listeners {
elements.container, elements.container,
'mousemove mouseleave touchstart touchmove enterfullscreen exitfullscreen', 'mousemove mouseleave touchstart touchmove enterfullscreen exitfullscreen',
event => { event => {
const { controls } = elements; const { controls: controlsElement } = elements;
// Remove button states for fullscreen // Remove button states for fullscreen
if (controls && event.type === 'enterfullscreen') { if (controlsElement && event.type === 'enterfullscreen') {
controls.pressed = false; controlsElement.pressed = false;
controls.hover = false; controlsElement.hover = false;
} }
// Show, then hide after a timeout unless another control event occurs // Show, then hide after a timeout unless another control event occurs
const show = ['touchstart', 'touchmove', 'mousemove'].includes(event.type); const show = ['touchstart', 'touchmove', 'mousemove'].includes(event.type);
let delay = 0; let delay = 0;
if (show) { if (show) {
@ -351,7 +350,6 @@ class Listeners {
} }
const isEnter = event.type === 'enterfullscreen'; const isEnter = event.type === 'enterfullscreen';
// Set the player size when entering fullscreen to viewport size // Set the player size when entering fullscreen to viewport size
const { padding, ratio } = setPlayerSize(isEnter); const { padding, ratio } = setPlayerSize(isEnter);
@ -542,7 +540,6 @@ class Listeners {
controls() { controls() {
const { player } = this; const { player } = this;
const { elements } = player; const { elements } = player;
// IE doesn't support input event, so we fallback to change // IE doesn't support input event, so we fallback to change
const inputEvent = browser.isIE ? 'change' : 'input'; const inputEvent = browser.isIE ? 'change' : 'input';
@ -678,7 +675,6 @@ class Listeners {
// Was playing before? // Was playing before?
const play = seek.hasAttribute(attribute); const play = seek.hasAttribute(attribute);
// Done seeking // Done seeking
const done = ['mouseup', 'touchend', 'keyup'].includes(event.type); const done = ['mouseup', 'touchend', 'keyup'].includes(event.type);
@ -706,7 +702,6 @@ class Listeners {
inputEvent, inputEvent,
event => { event => {
const seek = event.currentTarget; const seek = event.currentTarget;
// If it exists, use seek-value instead of "value" for consistency with tooltip time (#954) // If it exists, use seek-value instead of "value" for consistency with tooltip time (#954)
let seekTo = seek.getAttribute('seek-value'); let seekTo = seek.getAttribute('seek-value');
@ -806,7 +801,7 @@ class Listeners {
// Show controls when they receive focus (e.g., when using keyboard tab key) // Show controls when they receive focus (e.g., when using keyboard tab key)
this.bind(elements.controls, 'focusin', () => { this.bind(elements.controls, 'focusin', () => {
const { config, elements, timers } = player; const { config, timers } = player;
// Skip transition to prevent focus from scrolling the parent element // Skip transition to prevent focus from scrolling the parent element
toggleClass(elements.controls, config.classNames.noTransition, true); toggleClass(elements.controls, config.classNames.noTransition, true);
@ -837,10 +832,8 @@ class Listeners {
// Detect "natural" scroll - suppored on OS X Safari only // Detect "natural" scroll - suppored on OS X Safari only
// Other browsers on OS X will be inverted until support improves // Other browsers on OS X will be inverted until support improves
const inverted = event.webkitDirectionInvertedFromDevice; const inverted = event.webkitDirectionInvertedFromDevice;
// Get delta from event. Invert if `inverted` is true // Get delta from event. Invert if `inverted` is true
const [x, y] = [event.deltaX, -event.deltaY].map(value => (inverted ? -value : value)); const [x, y] = [event.deltaX, -event.deltaY].map(value => (inverted ? -value : value));
// Using the biggest delta, normalize to 1 or -1 (or 0 if no delta) // Using the biggest delta, normalize to 1 or -1 (or 0 if no delta)
const direction = Math.sign(Math.abs(x) > Math.abs(y) ? x : y); const direction = Math.sign(Math.abs(x) > Math.abs(y) ? x : y);

View File

@ -267,7 +267,7 @@ class Ads {
// Advertisement regular events // Advertisement regular events
Object.keys(google.ima.AdEvent.Type).forEach(type => { Object.keys(google.ima.AdEvent.Type).forEach(type => {
this.manager.addEventListener(google.ima.AdEvent.Type[type], event => this.onAdEvent(event)); this.manager.addEventListener(google.ima.AdEvent.Type[type], e => this.onAdEvent(e));
}); });
// Resolve our adsManager // Resolve our adsManager
@ -303,7 +303,6 @@ class Ads {
*/ */
onAdEvent(event) { onAdEvent(event) {
const { container } = this.player.elements; const { container } = this.player.elements;
// Retrieve the ad from the event. Some events (e.g. ALL_ADS_COMPLETED) // Retrieve the ad from the event. Some events (e.g. ALL_ADS_COMPLETED)
// don't have ad object associated // don't have ad object associated
const ad = event.getAd(); const ad = event.getAd();
@ -311,8 +310,7 @@ class Ads {
// Proxy event // Proxy event
const dispatchEvent = type => { const dispatchEvent = type => {
const event = `ads${type.replace(/_/g, '').toLowerCase()}`; triggerEvent.call(this.player, this.player.media, `ads${type.replace(/_/g, '').toLowerCase()}`);
triggerEvent.call(this.player, this.player.media, event);
}; };
// Bubble the event // Bubble the event

View File

@ -2,6 +2,7 @@ import { createElement } from '../utils/elements';
import { once } from '../utils/events'; import { once } from '../utils/events';
import fetch from '../utils/fetch'; import fetch from '../utils/fetch';
import is from '../utils/is'; import is from '../utils/is';
import { extend } from '../utils/objects';
import { formatTime } from '../utils/time'; import { formatTime } from '../utils/time';
// Arg: vttDataString example: "WEBVTT\n\n1\n00:00:05.000 --> 00:00:10.000\n1080p-00001.jpg" // Arg: vttDataString example: "WEBVTT\n\n1\n00:00:05.000 --> 00:00:10.000\n1080p-00001.jpg"
@ -121,7 +122,6 @@ class PreviewThumbnails {
// If string, convert into single-element list // If string, convert into single-element list
const urls = is.string(src) ? [src] : src; const urls = is.string(src) ? [src] : src;
// Loop through each src URL. Download and process the VTT file, storing the resulting data in this.thumbnails // Loop through each src URL. Download and process the VTT file, storing the resulting data in this.thumbnails
const promises = urls.map(u => this.getThumbnail(u)); const promises = urls.map(u => this.getThumbnail(u));
@ -426,7 +426,7 @@ class PreviewThumbnails {
if (image.dataset.index !== currentImage.dataset.index && !image.dataset.deleting) { if (image.dataset.index !== currentImage.dataset.index && !image.dataset.deleting) {
// Wait 200ms, as the new image can take some time to show on certain browsers (even though it was downloaded before showing). This will prevent flicker, and show some generosity towards slower clients // Wait 200ms, as the new image can take some time to show on certain browsers (even though it was downloaded before showing). This will prevent flicker, and show some generosity towards slower clients
// First set attribute 'deleting' to prevent multi-handling of this on repeat firing of this function // First set attribute 'deleting' to prevent multi-handling of this on repeat firing of this function
image.dataset.deleting = true; extend(image, { dataset: { deleting: true } });
// This has to be set before the timeout - to prevent issues switching between hover and scrub // This has to be set before the timeout - to prevent issues switching between hover and scrub
const { currentImageContainer } = this; const { currentImageContainer } = this;
@ -467,7 +467,6 @@ class PreviewThumbnails {
const { urlPrefix } = this.thumbnails[0]; const { urlPrefix } = this.thumbnails[0];
const thumbURL = urlPrefix + newThumbFilename; const thumbURL = urlPrefix + newThumbFilename;
const previewImage = new Image(); const previewImage = new Image();
previewImage.src = thumbURL; previewImage.src = thumbURL;
previewImage.onload = () => { previewImage.onload = () => {
@ -601,11 +600,9 @@ class PreviewThumbnails {
const seekbarRect = this.player.elements.progress.getBoundingClientRect(); const seekbarRect = this.player.elements.progress.getBoundingClientRect();
const plyrRect = this.player.elements.container.getBoundingClientRect(); const plyrRect = this.player.elements.container.getBoundingClientRect();
const { container } = this.elements.thumb; const { container } = this.elements.thumb;
// Find the lowest and highest desired left-position, so we don't slide out the side of the video container // Find the lowest and highest desired left-position, so we don't slide out the side of the video container
const minVal = plyrRect.left - seekbarRect.left + 10; const minVal = plyrRect.left - seekbarRect.left + 10;
const maxVal = plyrRect.right - seekbarRect.left - container.clientWidth - 10; const maxVal = plyrRect.right - seekbarRect.left - container.clientWidth - 10;
// Set preview container position to: mousepos, minus seekbar.left, minus half of previewContainer.clientWidth // Set preview container position to: mousepos, minus seekbar.left, minus half of previewContainer.clientWidth
let previewPos = this.mousePosX - seekbarRect.left - container.clientWidth / 2; let previewPos = this.mousePosX - seekbarRect.left - container.clientWidth / 2;
@ -636,9 +633,13 @@ class PreviewThumbnails {
// Find difference between height and preview container height // Find difference between height and preview container height
const multiplier = this.thumbContainerHeight / frame.h; const multiplier = this.thumbContainerHeight / frame.h;
// eslint-disable-next-line no-param-reassign
previewImage.style.height = `${Math.floor(previewImage.naturalHeight * multiplier)}px`; previewImage.style.height = `${Math.floor(previewImage.naturalHeight * multiplier)}px`;
// eslint-disable-next-line no-param-reassign
previewImage.style.width = `${Math.floor(previewImage.naturalWidth * multiplier)}px`; previewImage.style.width = `${Math.floor(previewImage.naturalWidth * multiplier)}px`;
// eslint-disable-next-line no-param-reassign
previewImage.style.left = `-${frame.x * multiplier}px`; previewImage.style.left = `-${frame.x * multiplier}px`;
// eslint-disable-next-line no-param-reassign
previewImage.style.top = `-${frame.y * multiplier}px`; previewImage.style.top = `-${frame.y * multiplier}px`;
} }
} }

View File

@ -91,7 +91,6 @@ const vimeo = {
} }
const id = parseId(source); const id = parseId(source);
// Build an iframe // Build an iframe
const iframe = createElement('iframe'); const iframe = createElement('iframe');
const src = format(player.config.urls.vimeo.iframe, id, params); const src = format(player.config.urls.vimeo.iframe, id, params);
@ -102,7 +101,6 @@ const vimeo = {
// Get poster, if already set // Get poster, if already set
const { poster } = player; const { poster } = player;
// Inject the package // Inject the package
const wrapper = createElement('div', { poster, class: player.config.classNames.embedContainer }); const wrapper = createElement('div', { poster, class: player.config.classNames.embedContainer });
wrapper.appendChild(iframe); wrapper.appendChild(iframe);

View File

@ -107,7 +107,6 @@ const youtube = {
// API ready // API ready
ready() { ready() {
const player = this; const player = this;
// Ignore already setup (race condition) // Ignore already setup (race condition)
const currentId = player.media.getAttribute('id'); const currentId = player.media.getAttribute('id');
if (!is.empty(currentId) && currentId.startsWith('youtube-')) { if (!is.empty(currentId) && currentId.startsWith('youtube-')) {
@ -125,25 +124,23 @@ const youtube = {
// Replace the <iframe> with a <div> due to YouTube API issues // Replace the <iframe> with a <div> due to YouTube API issues
const videoId = parseId(source); const videoId = parseId(source);
const id = generateId(player.provider); const id = generateId(player.provider);
// Get poster, if already set // Get poster, if already set
const { poster } = player; const { poster } = player;
// Replace media element // Replace media element
const container = createElement('div', { id, poster }); const container = createElement('div', { id, poster });
player.media = replaceElement(container, player.media); player.media = replaceElement(container, player.media);
// Id to poster wrapper // Id to poster wrapper
const posterSrc = format => `https://i.ytimg.com/vi/${videoId}/${format}default.jpg`; const posterSrc = s => `https://i.ytimg.com/vi/${videoId}/${s}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
.catch(() => loadImage(posterSrc('sd'), 121)) // 480p padded 4:3 .catch(() => loadImage(posterSrc('sd'), 121)) // 480p padded 4:3
.catch(() => loadImage(posterSrc('hq'))) // 360p padded 4:3. Always exists .catch(() => loadImage(posterSrc('hq'))) // 360p padded 4:3. Always exists
.then(image => ui.setPoster.call(player, image.src)) .then(image => ui.setPoster.call(player, image.src))
.then(posterSrc => { .then(src => {
// If the image is padded, use background-size "cover" instead (like youtube does too with their posters) // If the image is padded, use background-size "cover" instead (like youtube does too with their posters)
if (!posterSrc.includes('maxres')) { if (!src.includes('maxres')) {
player.elements.poster.style.backgroundSize = 'cover'; player.elements.poster.style.backgroundSize = 'cover';
} }
}) })

View File

@ -151,7 +151,6 @@ class Plyr {
// Set media type based on tag or data attribute // Set media type based on tag or data attribute
// Supported: video, audio, vimeo, youtube // Supported: video, audio, vimeo, youtube
const type = this.media.tagName.toLowerCase(); const type = this.media.tagName.toLowerCase();
// Embed properties // Embed properties
let iframe = null; let iframe = null;
let url = null; let url = null;
@ -324,27 +323,27 @@ class Plyr {
* Types and provider helpers * Types and provider helpers
*/ */
get isHTML5() { get isHTML5() {
return Boolean(this.provider === providers.html5); return this.provider === providers.html5;
} }
get isEmbed() { get isEmbed() {
return Boolean(this.isYouTube || this.isVimeo); return this.isYouTube || this.isVimeo;
} }
get isYouTube() { get isYouTube() {
return Boolean(this.provider === providers.youtube); return this.provider === providers.youtube;
} }
get isVimeo() { get isVimeo() {
return Boolean(this.provider === providers.vimeo); return this.provider === providers.vimeo;
} }
get isVideo() { get isVideo() {
return Boolean(this.type === types.video); return this.type === types.video;
} }
get isAudio() { get isAudio() {
return Boolean(this.type === types.audio); return this.type === types.audio;
} }
/** /**
@ -514,7 +513,6 @@ class Plyr {
get duration() { get duration() {
// Faux duration set via config // Faux duration set via config
const fauxDuration = parseFloat(this.config.duration); const fauxDuration = parseFloat(this.config.duration);
// Media duration can be NaN or Infinity before the media has loaded // Media duration can be NaN or Infinity before the media has loaded
const realDuration = (this.media || {}).duration; const realDuration = (this.media || {}).duration;
const duration = !is.number(realDuration) || realDuration === Infinity ? 0 : realDuration; const duration = !is.number(realDuration) || realDuration === Infinity ? 0 : realDuration;
@ -1045,10 +1043,8 @@ class Plyr {
if (this.supported.ui && !this.isAudio) { if (this.supported.ui && !this.isAudio) {
// Get state before change // Get state before change
const isHidden = hasClass(this.elements.container, this.config.classNames.hideControls); const isHidden = hasClass(this.elements.container, this.config.classNames.hideControls);
// Negate the argument if not undefined since adding the class to hides the controls // Negate the argument if not undefined since adding the class to hides the controls
const force = typeof toggle === 'undefined' ? undefined : !toggle; const force = typeof toggle === 'undefined' ? undefined : !toggle;
// Apply and get updated state // Apply and get updated state
const hiding = toggleClass(this.elements.container, this.config.classNames.hideControls, force); const hiding = toggleClass(this.elements.container, this.config.classNames.hideControls, force);

View File

@ -7,6 +7,7 @@
import 'custom-event-polyfill'; import 'custom-event-polyfill';
import 'url-polyfill'; import 'url-polyfill';
import Plyr from './plyr'; import Plyr from './plyr';
export default Plyr; export default Plyr;

View File

@ -213,7 +213,7 @@ const ui = {
// Set state // Set state
Array.from(this.elements.buttons.play || []).forEach(target => { Array.from(this.elements.buttons.play || []).forEach(target => {
target.pressed = this.playing; Object.assign(target, { pressed: this.playing });
}); });
// Only update controls on non timeupdate events // Only update controls on non timeupdate events
@ -247,15 +247,22 @@ const ui = {
// Toggle controls based on state and `force` argument // Toggle controls based on state and `force` argument
toggleControls(force) { toggleControls(force) {
const { controls } = this.elements; const { controls: controlsElement } = this.elements;
if (controls && this.config.hideControls) { if (controlsElement && this.config.hideControls) {
// Don't hide controls if a touch-device user recently seeked. (Must be limited to touch devices, or it occasionally prevents desktop controls from hiding.) // Don't hide controls if a touch-device user recently seeked. (Must be limited to touch devices, or it occasionally prevents desktop controls from hiding.)
const recentTouchSeek = this.touch && this.lastSeekTime + 2000 > Date.now(); const recentTouchSeek = this.touch && this.lastSeekTime + 2000 > Date.now();
// Show controls if force, loading, paused, button interaction, or recent seek, otherwise hide // Show controls if force, loading, paused, button interaction, or recent seek, otherwise hide
this.toggleControls( this.toggleControls(
Boolean(force || this.loading || this.paused || controls.pressed || controls.hover || recentTouchSeek), Boolean(
force ||
this.loading ||
this.paused ||
controlsElement.pressed ||
controlsElement.hover ||
recentTouchSeek,
),
); );
} }
}, },

View File

@ -17,7 +17,6 @@ export function wrap(elements, wrapper) {
.reverse() .reverse()
.forEach((element, index) => { .forEach((element, index) => {
const child = index > 0 ? wrapper.cloneNode(true) : wrapper; const child = index > 0 ? wrapper.cloneNode(true) : wrapper;
// Cache the current parent and sibling. // Cache the current parent and sibling.
const parent = element.parentNode; const parent = element.parentNode;
const sibling = element.nextSibling; const sibling = element.nextSibling;
@ -145,12 +144,10 @@ export function getAttributesFromSelector(sel, existingAttributes) {
const selector = s.trim(); const selector = s.trim();
const className = selector.replace('.', ''); const className = selector.replace('.', '');
const stripped = selector.replace(/[[\]]/g, ''); const stripped = selector.replace(/[[\]]/g, '');
// Get the parts and value // Get the parts and value
const parts = stripped.split('='); const parts = stripped.split('=');
const [key] = parts; const [key] = parts;
const value = parts.length > 1 ? parts[1].replace(/["']/g, '') : ''; const value = parts.length > 1 ? parts[1].replace(/["']/g, '') : '';
// Get the first character // Get the first character
const start = selector.charAt(0); const start = selector.charAt(0);
@ -234,14 +231,14 @@ export function matches(element, selector) {
return Array.from(document.querySelectorAll(selector)).includes(this); return Array.from(document.querySelectorAll(selector)).includes(this);
} }
const matches = const method =
prototype.matches || prototype.matches ||
prototype.webkitMatchesSelector || prototype.webkitMatchesSelector ||
prototype.mozMatchesSelector || prototype.mozMatchesSelector ||
prototype.msMatchesSelector || prototype.msMatchesSelector ||
match; match;
return matches.call(element, selector); return method.call(element, selector);
} }
// Find all elements // Find all elements

View File

@ -35,7 +35,6 @@ export function toggleListener(element, event, callback, toggle = false, passive
// Allow multiple events // Allow multiple events
const events = event.split(' '); const events = event.split(' ');
// Build options // Build options
// Default to just the capture boolean for browsers with no passive listener support // Default to just the capture boolean for browsers with no passive listener support
let options = capture; let options = capture;

View File

@ -36,8 +36,8 @@ const i18n = {
'{title}': config.title, '{title}': config.title,
}; };
Object.entries(replace).forEach(([key, value]) => { Object.entries(replace).forEach(([k, v]) => {
string = replaceAll(string, key, value); string = replaceAll(string, k, v);
}); });
return string; return string;

View File

@ -15,10 +15,10 @@ export default function loadSprite(url, id) {
const prefix = 'cache'; const prefix = 'cache';
const hasId = is.string(id); const hasId = is.string(id);
let isCached = false; let isCached = false;
const exists = () => document.getElementById(id) !== null; const exists = () => document.getElementById(id) !== null;
const update = (container, data) => { const update = (container, data) => {
// eslint-disable-next-line no-param-reassign
container.innerHTML = data; container.innerHTML = data;
// Check again incase of race condition // Check again incase of race condition
@ -33,7 +33,6 @@ export default function loadSprite(url, id) {
// Only load once if ID set // Only load once if ID set
if (!hasId || !exists()) { if (!hasId || !exists()) {
const useStorage = Storage.supported; const useStorage = Storage.supported;
// Create container // Create container
const container = document.createElement('div'); const container = document.createElement('div');
container.setAttribute('hidden', ''); container.setAttribute('hidden', '');

View File

@ -64,7 +64,6 @@ export function setAspectRatio(input) {
} }
const ratio = getAspectRatio.call(this, input); const ratio = getAspectRatio.call(this, input);
const [w, h] = is.array(ratio) ? ratio : [0, 0]; const [w, h] = is.array(ratio) ? ratio : [0, 0];
const padding = (100 / w) * h; const padding = (100 / w) * h;

View File

@ -18,7 +18,6 @@ export function formatTime(time = 0, displayHours = false, inverted = false) {
// Format time component to add leading zero // Format time component to add leading zero
const format = value => `0${value}`.slice(-2); const format = value => `0${value}`.slice(-2);
// Breakdown to hours, mins, secs // Breakdown to hours, mins, secs
let hours = getHours(time); let hours = getHours(time);
const mins = getMinutes(time); const mins = getMinutes(time);

1093
yarn.lock

File diff suppressed because it is too large Load Diff