IE & Edge fixes, Storage & Console classes
This commit is contained in:
parent
de54929bb7
commit
c8990bd379
30
.vscode/settings.json
vendored
30
.vscode/settings.json
vendored
@ -1,29 +1 @@
|
||||
{
|
||||
// Exclude from the editor
|
||||
"files.exclude": {
|
||||
"**/node_modules": true
|
||||
},
|
||||
|
||||
// Exclude from search
|
||||
"search.exclude": {
|
||||
"dist/": true
|
||||
},
|
||||
|
||||
// Linting
|
||||
"stylelint.enable": true,
|
||||
"css.validate": false,
|
||||
"scss.validate": false,
|
||||
"javascript.validate.enable": false,
|
||||
|
||||
// Prettier
|
||||
"prettier.eslintIntegration": true,
|
||||
"prettier.stylelintIntegration": true,
|
||||
|
||||
// Formatting
|
||||
"editor.tabSize": 4,
|
||||
"editor.insertSpaces": true,
|
||||
"editor.formatOnSave": true,
|
||||
|
||||
// Trim on save
|
||||
"files.trimTrailingWhitespace": true
|
||||
}
|
||||
{}
|
2
demo/dist/demo.css
vendored
2
demo/dist/demo.css
vendored
File diff suppressed because one or more lines are too long
2
demo/dist/demo.js
vendored
2
demo/dist/demo.js
vendored
@ -1,3 +1,3 @@
|
||||
document.addEventListener("DOMContentLoaded",function(){function e(e,t,o){e&&e.classList[o?"add":"remove"](t)}function t(t,n){if(t in r&&(n||t!==a)&&(a.length||t!==r.video)){switch(t){case r.video:o.source={type:"video",title:"View From A Blue Moon",sources:[{src:"media/View_From_A_Blue_Moon_Trailer-HD.mp4",type:"video/mp4"}],poster:"media/View_From_A_Blue_Moon_Trailer-HD.jpg",tracks:[{kind:"captions",label:"English",srclang:"en",src:"media/View_From_A_Blue_Moon_Trailer-HD.en.vtt",default:!0},{kind:"captions",label:"French",srclang:"fr",src:"media/View_From_A_Blue_Moon_Trailer-HD.fr.vtt"}]};break;case r.audio:o.source={type:"audio",title:"Kishi Bashi – “It All Began With A Burst”",sources:[{src:"https://cdn.plyr.io/static/demo/Kishi_Bashi_-_It_All_Began_With_a_Burst.mp3",type:"audio/mp3"},{src:"https://cdn.plyr.io/static/demo/Kishi_Bashi_-_It_All_Began_With_a_Burst.ogg",type:"audio/ogg"}]};break;case r.youtube:o.source={type:"video",title:"View From A Blue Moon",sources:[{src:"https://youtube.com/watch?v=bTqVqk7FSmY",provider:"youtube"}]};break;case r.vimeo:o.source={type:"video",sources:[{src:"https://vimeo.com/76979871",provider:"vimeo"}]}}a=t,Array.from(i).forEach(function(t){return e(t.parentElement,"active",!1)}),e(document.querySelector('[data-source="'+t+'"]'),"active",!0),Array.from(document.querySelectorAll(".plyr__cite")).forEach(function(e){e.setAttribute("hidden","")}),document.querySelector(".plyr__cite--"+t).removeAttribute("hidden")}}window.shr&&window.shr.setup({count:{classname:"button__count"}});document.addEventListener("focusout",function(e){e.target.classList.remove("tab-focus")}),document.addEventListener("keydown",function(e){9===e.keyCode&&window.setTimeout(function(){document.activeElement.classList.add("tab-focus")},0)});var o=new window.Plyr("#player",{debug:!0,title:"View From A Blue Moon",iconUrl:"../dist/plyr.svg",keyboard:{global:!0},tooltips:{controls:!0},captions:{active:!0},controls:["play-large","play","progress","current-time","mute","volume","captions","settings","fullscreen","pip","airplay"],keys:{google:"AIzaSyDrNwtN3nLH_8rjCmu5Wq3ZCm4MNAVdc0c"}});window.player=o;var i=document.querySelectorAll("[data-source]"),r={video:"video",audio:"audio",youtube:"youtube",vimeo:"vimeo"},a=window.location.hash.replace("#",""),n=window.history&&window.history.pushState;if(Array.from(i).forEach(function(e){e.addEventListener("click",function(){var o=e.getAttribute("data-source");t(o),n&&window.history.pushState({type:o},"","#"+o)})}),window.addEventListener("popstate",function(e){e.state&&"type"in e.state&&t(e.state.type)}),n){var s=!a.length;s&&(a=r.video),a in r&&window.history.replaceState({type:a},"",s?"":"#"+a),a!==r.video&&t(a,!0)}}),"plyr.io"===window.location.host&&(!function(e,t,o,i,r,a,n){e.GoogleAnalyticsObject=r,e.ga=e.ga||function(){(e.ga.q=e.ga.q||[]).push(arguments)},e.ga.l=1*new Date,a=t.createElement(o),n=t.getElementsByTagName(o)[0],a.async=1,a.src="//www.google-analytics.com/analytics.js",n.parentNode.insertBefore(a,n)}(window,document,"script",0,"ga"),window.ga("create","UA-40881672-11","auto"),window.ga("send","pageview"));
|
||||
document.addEventListener("DOMContentLoaded",function(){function e(e,t,o){e&&e.classList[o?"add":"remove"](t)}function t(t,n){if(t in r&&(n||t!==a)&&(a.length||t!==r.video)){switch(t){case r.video:o.source={type:"video",title:"View From A Blue Moon",sources:[{src:"media/View_From_A_Blue_Moon_Trailer-HD.mp4",type:"video/mp4"}],poster:"media/View_From_A_Blue_Moon_Trailer-HD.jpg",tracks:[{kind:"captions",label:"English",srclang:"en",src:"media/View_From_A_Blue_Moon_Trailer-HD.en.vtt",default:!0},{kind:"captions",label:"French",srclang:"fr",src:"media/View_From_A_Blue_Moon_Trailer-HD.fr.vtt"}]};break;case r.audio:o.source={type:"audio",title:"Kishi Bashi – “It All Began With A Burst”",sources:[{src:"https://cdn.plyr.io/static/demo/Kishi_Bashi_-_It_All_Began_With_a_Burst.mp3",type:"audio/mp3"},{src:"https://cdn.plyr.io/static/demo/Kishi_Bashi_-_It_All_Began_With_a_Burst.ogg",type:"audio/ogg"}]};break;case r.youtube:o.source={type:"video",title:"View From A Blue Moon",sources:[{src:"https://youtube.com/watch?v=bTqVqk7FSmY",provider:"youtube"}]};break;case r.vimeo:o.source={type:"video",sources:[{src:"https://vimeo.com/76979871",provider:"vimeo"}]}}a=t,Array.from(i).forEach(function(t){return e(t.parentElement,"active",!1)}),e(document.querySelector('[data-source="'+t+'"]'),"active",!0),Array.from(document.querySelectorAll(".plyr__cite")).forEach(function(e){e.setAttribute("hidden","")}),document.querySelector(".plyr__cite--"+t).removeAttribute("hidden")}}window.shr&&window.shr.setup({count:{classname:"button__count"}});document.addEventListener("focusout",function(e){e.target.classList.remove("tab-focus")}),document.addEventListener("keydown",function(e){9===e.keyCode&&window.setTimeout(function(){document.activeElement.classList.add("tab-focus")},0)});var o=new window.Plyr("#player",{debug:!0,title:"View From A Blue Moon",iconUrl:"../dist/plyr.svg",keyboard:{global:!0},tooltips:{controls:!0},captions:{active:!0},controls:["play-large","play","progress","current-time","mute","volume","captions","settings","fullscreen","pip","airplay"],keys:{google:"AIzaSyDrNwtN3nLH_8rjCmu5Wq3ZCm4MNAVdc0c"}});window.player=o;var i=document.querySelectorAll("[data-source]"),r={video:"video",audio:"audio",youtube:"youtube",vimeo:"vimeo"},a=window.location.hash.replace("#",""),n=window.history&&window.history.pushState;if(Array.from(i).forEach(function(e){e.addEventListener("click",function(){var o=e.getAttribute("data-source");t(o),n&&window.history.pushState({type:o},"","#"+o)})}),window.addEventListener("popstate",function(e){e.state&&"type"in e.state&&t(e.state.type)}),n){var s=!a.length;s&&(a=r.video),a in r&&window.history.replaceState({type:a},"",s?"":"#"+a),a!==r.video&&t(a,!0)}}),"plyr.io"===window.location.host&&(!function(e,t,o,i,r,a,n){e.GoogleAnalyticsObject="ga",e.ga=e.ga||function(){(e.ga.q=e.ga.q||[]).push(arguments)},e.ga.l=1*new Date,a=t.createElement("script"),n=t.getElementsByTagName("script")[0],a.async=1,a.src="//www.google-analytics.com/analytics.js",n.parentNode.insertBefore(a,n)}(window,document),window.ga("create","UA-40881672-11","auto"),window.ga("send","pageview"));
|
||||
|
||||
//# sourceMappingURL=demo.js.map
|
||||
|
2
demo/dist/demo.js.map
vendored
2
demo/dist/demo.js.map
vendored
File diff suppressed because one or more lines are too long
@ -34,10 +34,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}, 0);
|
||||
});
|
||||
|
||||
/* document.body.addEventListener('ready', function(event) {
|
||||
console.log(event);
|
||||
}); */
|
||||
|
||||
// Setup the player
|
||||
const player = new window.Plyr('#player', {
|
||||
debug: true,
|
||||
|
2
dist/plyr.css
vendored
2
dist/plyr.css
vendored
File diff suppressed because one or more lines are too long
2
dist/plyr.js
vendored
2
dist/plyr.js
vendored
File diff suppressed because one or more lines are too long
2
dist/plyr.js.map
vendored
2
dist/plyr.js.map
vendored
File diff suppressed because one or more lines are too long
28
gulpfile.js
28
gulpfile.js
@ -4,7 +4,7 @@
|
||||
/* global require, __dirname */
|
||||
/* eslint no-console: "off" */
|
||||
|
||||
const fs = require('fs');
|
||||
const del = require('del');
|
||||
const path = require('path');
|
||||
const gulp = require('gulp');
|
||||
const gutil = require('gulp-util');
|
||||
@ -70,6 +70,7 @@ const tasks = {
|
||||
sass: [],
|
||||
js: [],
|
||||
sprite: [],
|
||||
clean: ['clean'],
|
||||
};
|
||||
|
||||
// Size plugin
|
||||
@ -97,10 +98,20 @@ const babelrc = {
|
||||
exclude: 'node_modules/**',
|
||||
};
|
||||
|
||||
// Clean out /dist
|
||||
gulp.task('clean', () => {
|
||||
const dirs = [paths.plyr.output, paths.demo.output].map(dir => path.join(dir, '**/*'));
|
||||
|
||||
// Don't delete the mp4
|
||||
dirs.push(`!${path.join(paths.plyr.output, '**/*.mp4')}`);
|
||||
|
||||
del(dirs);
|
||||
});
|
||||
|
||||
const build = {
|
||||
js(files, bundle, options) {
|
||||
Object.keys(files).forEach(key => {
|
||||
const name = `js-${key}`;
|
||||
const name = `js:${key}`;
|
||||
tasks.js.push(name);
|
||||
|
||||
gulp.task(name, () =>
|
||||
@ -124,7 +135,7 @@ const build = {
|
||||
},
|
||||
less(files, bundle) {
|
||||
Object.keys(files).forEach(key => {
|
||||
const name = `less-${key}`;
|
||||
const name = `less:${key}`;
|
||||
tasks.less.push(name);
|
||||
|
||||
gulp.task(name, () =>
|
||||
@ -142,7 +153,7 @@ const build = {
|
||||
},
|
||||
sass(files, bundle) {
|
||||
Object.keys(files).forEach(key => {
|
||||
const name = `sass-${key}`;
|
||||
const name = `sass:${key}`;
|
||||
tasks.sass.push(name);
|
||||
|
||||
gulp.task(name, () =>
|
||||
@ -159,7 +170,7 @@ const build = {
|
||||
});
|
||||
},
|
||||
sprite(bundle) {
|
||||
const name = `sprite-${bundle}`;
|
||||
const name = `svg:sprite:${bundle}`;
|
||||
tasks.sprite.push(name);
|
||||
|
||||
// Process Icons
|
||||
@ -217,7 +228,7 @@ gulp.task('watch', () => {
|
||||
|
||||
// Default gulp task
|
||||
gulp.task('default', () => {
|
||||
run(tasks.js, tasks.less, tasks.sprite, 'watch');
|
||||
run(tasks.clean, tasks.js, tasks.less, tasks.sprite, 'watch');
|
||||
});
|
||||
|
||||
// Publish a version to CDN and demo
|
||||
@ -250,8 +261,7 @@ const options = {
|
||||
|
||||
// If aws is setup
|
||||
if ('cdn' in aws) {
|
||||
const regex =
|
||||
'(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*).(?:0|[1-9][0-9]*)(?:-[\\da-z\\-]+(?:.[\\da-z\\-]+)*)?(?:\\+[\\da-z\\-]+(?:.[\\da-z\\-]+)*)?';
|
||||
const regex = '(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*).(?:0|[1-9][0-9]*)(?:-[\\da-z\\-]+(?:.[\\da-z\\-]+)*)?(?:\\+[\\da-z\\-]+(?:.[\\da-z\\-]+)*)?';
|
||||
const cdnpath = new RegExp(`${aws.cdn.domain}/${regex}`, 'gi');
|
||||
const semver = new RegExp(`v${regex}`, 'gi');
|
||||
const localPath = new RegExp('(../)?dist', 'gi');
|
||||
@ -350,6 +360,6 @@ if ('cdn' in aws) {
|
||||
|
||||
// Do everything
|
||||
gulp.task('publish', () => {
|
||||
run(tasks.js, tasks.less, tasks.sprite, 'cdn', 'demo');
|
||||
run(tasks.clean, tasks.js, tasks.less, tasks.sprite, 'cdn', 'demo');
|
||||
});
|
||||
}
|
||||
|
967
package-lock.json
generated
967
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
11
package.json
11
package.json
@ -9,9 +9,10 @@
|
||||
"babel-eslint": "^8.0.2",
|
||||
"babel-plugin-external-helpers": "^6.22.0",
|
||||
"babel-preset-env": "^1.6.1",
|
||||
"eslint": "^4.11.0",
|
||||
"del": "^3.0.0",
|
||||
"eslint": "^4.12.1",
|
||||
"eslint-config-airbnb": "^16.1.0",
|
||||
"eslint-config-prettier": "^2.8.0",
|
||||
"eslint-config-prettier": "^2.9.0",
|
||||
"eslint-plugin-import": "^2.8.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.0.2",
|
||||
"eslint-plugin-react": "^7.5.1",
|
||||
@ -36,10 +37,10 @@
|
||||
"rollup-plugin-node-resolve": "^3.0.0",
|
||||
"rollup-plugin-uglify": "^2.0.1",
|
||||
"run-sequence": "^2.2.0",
|
||||
"stylelint": "^8.2.0",
|
||||
"stylelint": "^8.3.1",
|
||||
"stylelint-config-prettier": "^2.0.0",
|
||||
"stylelint-config-standard": "^17.0.0",
|
||||
"uglify-es": "^3.1.10"
|
||||
"stylelint-config-standard": "^18.0.0",
|
||||
"uglify-es": "^3.2.0"
|
||||
},
|
||||
"keywords": [
|
||||
"HTML5 Video",
|
||||
|
31
plyr.code-workspace
Normal file
31
plyr.code-workspace
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
// Exclude from the editor
|
||||
"files.exclude": {
|
||||
"**/node_modules": true
|
||||
},
|
||||
// Exclude from search
|
||||
"search.exclude": {
|
||||
"dist/": true
|
||||
},
|
||||
// Linting
|
||||
"stylelint.enable": true,
|
||||
"css.validate": false,
|
||||
"scss.validate": false,
|
||||
"javascript.validate.enable": false,
|
||||
// Prettier
|
||||
"prettier.eslintIntegration": true,
|
||||
"prettier.stylelintIntegration": true,
|
||||
// Formatting
|
||||
"editor.tabSize": 4,
|
||||
"editor.insertSpaces": true,
|
||||
"editor.formatOnSave": true,
|
||||
// Trim on save
|
||||
"files.trimTrailingWhitespace": true
|
||||
}
|
||||
}
|
@ -5,7 +5,6 @@
|
||||
import support from './support';
|
||||
import utils from './utils';
|
||||
import controls from './controls';
|
||||
import storage from './storage';
|
||||
|
||||
const captions = {
|
||||
// Setup captions
|
||||
@ -16,18 +15,24 @@ const captions = {
|
||||
}
|
||||
|
||||
// Set default language if not set
|
||||
if (!utils.is.empty(storage.get.call(this).language)) {
|
||||
this.captions.language = storage.get.call(this).language;
|
||||
} else if (utils.is.empty(this.captions.language)) {
|
||||
const stored = this.storage.get('language');
|
||||
|
||||
if (!utils.is.empty(stored)) {
|
||||
this.captions.language = stored;
|
||||
}
|
||||
|
||||
if (utils.is.empty(this.captions.language)) {
|
||||
this.captions.language = this.config.captions.language.toLowerCase();
|
||||
}
|
||||
|
||||
// Set captions enabled state if not set
|
||||
if (!utils.is.boolean(this.captions.enabled)) {
|
||||
if (!utils.is.empty(storage.get.call(this).language)) {
|
||||
this.captions.enabled = storage.get.call(this).captions;
|
||||
if (!utils.is.boolean(this.captions.active)) {
|
||||
const active = this.storage.get('captions');
|
||||
|
||||
if (utils.is.boolean(active)) {
|
||||
this.captions.active = active;
|
||||
} else {
|
||||
this.captions.enabled = this.config.captions.active;
|
||||
this.captions.active = this.config.captions.active;
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,7 +47,7 @@ const captions = {
|
||||
}
|
||||
|
||||
// Inject the container
|
||||
if (!utils.is.htmlElement(this.elements.captions)) {
|
||||
if (!utils.is.element(this.elements.captions)) {
|
||||
this.elements.captions = utils.createElement('div', utils.getAttributesFromSelector(this.config.selectors.captions));
|
||||
|
||||
utils.insertAfter(this.elements.captions, this.elements.wrapper);
|
||||
@ -141,7 +146,7 @@ const captions = {
|
||||
return;
|
||||
}
|
||||
|
||||
if (utils.is.htmlElement(this.elements.captions)) {
|
||||
if (utils.is.element(this.elements.captions)) {
|
||||
const content = utils.createElement('span');
|
||||
|
||||
// Empty the container
|
||||
@ -160,19 +165,19 @@ const captions = {
|
||||
// Set new caption text
|
||||
this.elements.captions.appendChild(content);
|
||||
} else {
|
||||
this.console.warn('No captions element to render to');
|
||||
this.debug.warn('No captions element to render to');
|
||||
}
|
||||
},
|
||||
|
||||
// Display captions container and button (for initialization)
|
||||
show() {
|
||||
// If there's no caption toggle, bail
|
||||
if (!utils.is.htmlElement(this.elements.buttons.captions)) {
|
||||
if (!utils.is.element(this.elements.buttons.captions)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to load the value from storage
|
||||
let active = storage.get.call(this).captions;
|
||||
let active = this.storage.get('captions');
|
||||
|
||||
// Otherwise fall back to the default config
|
||||
if (!utils.is.boolean(active)) {
|
||||
|
28
src/js/console.js
Normal file
28
src/js/console.js
Normal file
@ -0,0 +1,28 @@
|
||||
// ==========================================================================
|
||||
// Console wrapper
|
||||
// ==========================================================================
|
||||
|
||||
const noop = () => {};
|
||||
|
||||
export default class Console {
|
||||
constructor(player) {
|
||||
this.enabled = window.console && player.config.debug;
|
||||
|
||||
if (this.enabled) {
|
||||
this.log('Debugging enabled');
|
||||
}
|
||||
}
|
||||
|
||||
get log() {
|
||||
// eslint-disable-next-line no-console
|
||||
return this.enabled ? Function.prototype.bind.call(console.log, console) : noop;
|
||||
}
|
||||
get warn() {
|
||||
// eslint-disable-next-line no-console
|
||||
return this.enabled ? Function.prototype.bind.call(console.warn, console) : noop;
|
||||
}
|
||||
get error() {
|
||||
// eslint-disable-next-line no-console
|
||||
return this.enabled ? Function.prototype.bind.call(console.error, console) : noop;
|
||||
}
|
||||
}
|
49
src/js/controls.js
vendored
49
src/js/controls.js
vendored
@ -22,12 +22,12 @@ const controls = {
|
||||
const range = utils.is.event(target) ? target.target : target;
|
||||
|
||||
// Needs to be a valid <input type='range'>
|
||||
if (!utils.is.htmlElement(range) || range.getAttribute('type') !== 'range') {
|
||||
if (!utils.is.element(range) || range.getAttribute('type') !== 'range') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Inject the stylesheet if needed
|
||||
if (!utils.is.htmlElement(this.elements.styleSheet)) {
|
||||
if (!utils.is.element(this.elements.styleSheet)) {
|
||||
this.elements.styleSheet = utils.createElement('style');
|
||||
this.elements.container.appendChild(this.elements.styleSheet);
|
||||
}
|
||||
@ -322,7 +322,7 @@ const controls = {
|
||||
|
||||
// Create time display
|
||||
createTime(type) {
|
||||
const container = utils.createElement('span', {
|
||||
const container = utils.createElement('div', {
|
||||
class: 'plyr__time',
|
||||
});
|
||||
|
||||
@ -368,7 +368,7 @@ const controls = {
|
||||
label.appendChild(faux);
|
||||
label.insertAdjacentHTML('beforeend', title);
|
||||
|
||||
if (utils.is.htmlElement(badge)) {
|
||||
if (utils.is.element(badge)) {
|
||||
label.appendChild(badge);
|
||||
}
|
||||
|
||||
@ -381,8 +381,8 @@ const controls = {
|
||||
// Bail if setting not true
|
||||
if (
|
||||
!this.config.tooltips.seek ||
|
||||
!utils.is.htmlElement(this.elements.inputs.seek) ||
|
||||
!utils.is.htmlElement(this.elements.display.seekTooltip) ||
|
||||
!utils.is.element(this.elements.inputs.seek) ||
|
||||
!utils.is.element(this.elements.display.seekTooltip) ||
|
||||
this.duration === 0
|
||||
) {
|
||||
return;
|
||||
@ -542,7 +542,7 @@ const controls = {
|
||||
|
||||
switch (setting) {
|
||||
case 'captions':
|
||||
value = this.captions.enabled ? this.captions.language : '';
|
||||
value = this.captions.active ? this.captions.language : '';
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -555,13 +555,13 @@ const controls = {
|
||||
|
||||
// Unsupported value
|
||||
if (!this.options[setting].includes(value)) {
|
||||
this.console.warn(`Unsupported value of '${value}' for ${setting}`);
|
||||
this.debug.warn(`Unsupported value of '${value}' for ${setting}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Disabled value
|
||||
if (!this.config[setting].options.includes(value)) {
|
||||
this.console.warn(`Disabled value of '${value}' for ${setting}`);
|
||||
this.debug.warn(`Disabled value of '${value}' for ${setting}`);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -569,7 +569,7 @@ const controls = {
|
||||
}
|
||||
|
||||
// Get the list if we need to
|
||||
if (!utils.is.htmlElement(list)) {
|
||||
if (!utils.is.element(list)) {
|
||||
list = pane && pane.querySelector('ul');
|
||||
}
|
||||
|
||||
@ -582,7 +582,7 @@ const controls = {
|
||||
// Find the radio option
|
||||
const target = list && list.querySelector(`input[value="${value}"]`);
|
||||
|
||||
if (utils.is.htmlElement(target)) {
|
||||
if (utils.is.element(target)) {
|
||||
// Check it
|
||||
target.checked = true;
|
||||
}
|
||||
@ -638,7 +638,7 @@ const controls = {
|
||||
return this.config.i18n.none;
|
||||
}
|
||||
|
||||
if (this.captions.enabled) {
|
||||
if (this.captions.active) {
|
||||
const currentTrack = captions.getCurrentTrack.call(this);
|
||||
|
||||
if (utils.is.track(currentTrack)) {
|
||||
@ -736,10 +736,10 @@ const controls = {
|
||||
toggleMenu(event) {
|
||||
const { form } = this.elements.settings;
|
||||
const button = this.elements.buttons.settings;
|
||||
const show = utils.is.boolean(event) ? event : utils.is.htmlElement(form) && form.getAttribute('aria-hidden') === 'true';
|
||||
const show = utils.is.boolean(event) ? event : utils.is.element(form) && form.getAttribute('aria-hidden') === 'true';
|
||||
|
||||
if (utils.is.event(event)) {
|
||||
const isMenuItem = utils.is.htmlElement(form) && form.contains(event.target);
|
||||
const isMenuItem = utils.is.element(form) && form.contains(event.target);
|
||||
const isButton = event.target === this.elements.buttons.settings;
|
||||
|
||||
// If the click was inside the form or if the click
|
||||
@ -756,11 +756,11 @@ const controls = {
|
||||
}
|
||||
|
||||
// Set form and button attributes
|
||||
if (utils.is.htmlElement(button)) {
|
||||
if (utils.is.element(button)) {
|
||||
button.setAttribute('aria-expanded', show);
|
||||
}
|
||||
|
||||
if (utils.is.htmlElement(form)) {
|
||||
if (utils.is.element(form)) {
|
||||
form.setAttribute('aria-hidden', !show);
|
||||
utils.toggleClass(this.elements.container, this.config.classNames.menu.open, show);
|
||||
|
||||
@ -809,7 +809,7 @@ const controls = {
|
||||
const pane = document.getElementById(tab.getAttribute('aria-controls'));
|
||||
|
||||
// Nothing to show, bail
|
||||
if (!utils.is.htmlElement(pane)) {
|
||||
if (!utils.is.element(pane)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -908,7 +908,7 @@ const controls = {
|
||||
|
||||
// Progress
|
||||
if (this.config.controls.includes('progress')) {
|
||||
const progress = utils.createElement('span', utils.getAttributesFromSelector(this.config.selectors.progress));
|
||||
const progress = utils.createElement('div', utils.getAttributesFromSelector(this.config.selectors.progress));
|
||||
|
||||
// Seek range slider
|
||||
const seek = controls.createRange.call(this, 'seek', {
|
||||
@ -958,7 +958,7 @@ const controls = {
|
||||
|
||||
// Volume range control
|
||||
if (this.config.controls.includes('volume')) {
|
||||
const volume = utils.createElement('span', {
|
||||
const volume = utils.createElement('div', {
|
||||
class: 'plyr__volume',
|
||||
});
|
||||
|
||||
@ -1186,22 +1186,27 @@ const controls = {
|
||||
}
|
||||
|
||||
// Inject into the container by default
|
||||
if (!utils.is.htmlElement(target)) {
|
||||
if (!utils.is.element(target)) {
|
||||
target = this.elements.container;
|
||||
}
|
||||
|
||||
// Inject controls HTML
|
||||
if (utils.is.htmlElement(container)) {
|
||||
if (utils.is.element(container)) {
|
||||
target.appendChild(container);
|
||||
} else {
|
||||
target.insertAdjacentHTML('beforeend', container);
|
||||
}
|
||||
|
||||
// Find the elements if need be
|
||||
if (utils.is.htmlElement(this.elements.controls)) {
|
||||
if (utils.is.element(this.elements.controls)) {
|
||||
utils.findElements.call(this);
|
||||
}
|
||||
|
||||
// Edge sometimes doesn't finish the paint so force a redraw
|
||||
if (window.navigator.userAgent.includes('Edge')) {
|
||||
utils.repaint(target);
|
||||
}
|
||||
|
||||
// Setup tooltips
|
||||
if (this.config.tooltips.controls) {
|
||||
const labels = utils.getElements.call(
|
||||
|
@ -100,12 +100,12 @@ const fullscreen = {
|
||||
const nativeSupport = fullscreen.enabled;
|
||||
|
||||
if (nativeSupport || (this.config.fullscreen.fallback && !utils.inFrame())) {
|
||||
this.console.log(`${nativeSupport ? 'Native' : 'Fallback'} fullscreen enabled`);
|
||||
this.debug.log(`${nativeSupport ? 'Native' : 'Fallback'} fullscreen enabled`);
|
||||
|
||||
// Add styling hook to show button
|
||||
utils.toggleClass(this.elements.container, this.config.classNames.fullscreen.enabled, true);
|
||||
} else {
|
||||
this.console.log('Fullscreen not supported and fallback disabled');
|
||||
this.debug.log('Fullscreen not supported and fallback disabled');
|
||||
}
|
||||
|
||||
// Toggle state
|
||||
|
@ -6,7 +6,6 @@ import support from './support';
|
||||
import utils from './utils';
|
||||
import controls from './controls';
|
||||
import fullscreen from './fullscreen';
|
||||
import storage from './storage';
|
||||
import ui from './ui';
|
||||
|
||||
// Sniff out the browser
|
||||
@ -53,7 +52,7 @@ const listeners = {
|
||||
// and if the focused element is not editable (e.g. text input)
|
||||
// and any that accept key input http://webaim.org/techniques/keyboard/
|
||||
const focused = utils.getFocusElement();
|
||||
if (utils.is.htmlElement(focused) && utils.matches(focused, this.config.selectors.editable)) {
|
||||
if (utils.is.element(focused) && utils.matches(focused, this.config.selectors.editable)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -248,7 +247,7 @@ const listeners = {
|
||||
const wrapper = utils.getElement.call(this, `.${this.config.classNames.video}`);
|
||||
|
||||
// Bail if there's no wrapper (this should never happen)
|
||||
if (!utils.is.htmlElement(wrapper)) {
|
||||
if (!utils.is.element(wrapper)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -285,7 +284,7 @@ const listeners = {
|
||||
// Volume change
|
||||
utils.on(this.media, 'volumechange', () => {
|
||||
// Save to storage
|
||||
storage.set.call(this, { volume: this.volume, muted: this.muted });
|
||||
this.storage.set({ volume: this.volume, muted: this.muted });
|
||||
});
|
||||
|
||||
// Speed change
|
||||
@ -294,7 +293,7 @@ const listeners = {
|
||||
controls.updateSetting.call(this, 'speed');
|
||||
|
||||
// Save to storage
|
||||
storage.set.call(this, { speed: this.speed });
|
||||
this.storage.set({ speed: this.speed });
|
||||
});
|
||||
|
||||
// Quality change
|
||||
@ -303,7 +302,7 @@ const listeners = {
|
||||
controls.updateSetting.call(this, 'quality');
|
||||
|
||||
// Save to storage
|
||||
storage.set.call(this, { quality: this.quality });
|
||||
this.storage.set({ quality: this.quality });
|
||||
});
|
||||
|
||||
// Caption language change
|
||||
@ -312,7 +311,7 @@ const listeners = {
|
||||
controls.updateSetting.call(this, 'captions');
|
||||
|
||||
// Save to storage
|
||||
storage.set.call(this, { language: this.language });
|
||||
this.storage.set({ language: this.language });
|
||||
});
|
||||
|
||||
// Captions toggle
|
||||
@ -321,7 +320,7 @@ const listeners = {
|
||||
controls.updateSetting.call(this, 'captions');
|
||||
|
||||
// Save to storage
|
||||
storage.set.call(this, { captions: this.captions.enabled });
|
||||
this.storage.set({ captions: this.captions.active });
|
||||
});
|
||||
|
||||
// Proxy events to container
|
||||
@ -462,7 +461,7 @@ const listeners = {
|
||||
|
||||
// Current time invert
|
||||
// Only if one time element is used for both currentTime and duration
|
||||
if (this.config.toggleInvert && !utils.is.htmlElement(this.elements.display.duration)) {
|
||||
if (this.config.toggleInvert && !utils.is.element(this.elements.display.duration)) {
|
||||
utils.on(this.elements.display.currentTime, 'click', () => {
|
||||
// Do nothing if we're at the start
|
||||
if (this.currentTime === 0) {
|
||||
|
@ -16,7 +16,7 @@ const media = {
|
||||
setup() {
|
||||
// If there's no media, bail
|
||||
if (!this.media) {
|
||||
this.console.warn('No media element found!');
|
||||
this.debug.warn('No media element found!');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -99,7 +99,7 @@ const media = {
|
||||
this.media.load();
|
||||
|
||||
// Debugging
|
||||
this.console.log('Cancelled network requests');
|
||||
this.debug.log('Cancelled network requests');
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -242,7 +242,7 @@ const vimeo = {
|
||||
});
|
||||
|
||||
player.embed.on('loaded', () => {
|
||||
if (utils.is.htmlElement(player.embed.element) && player.supported.ui) {
|
||||
if (utils.is.element(player.embed.element) && player.supported.ui) {
|
||||
const frame = player.embed.element;
|
||||
|
||||
// Fix keyboard focus issues
|
||||
|
105
src/js/plyr.js
105
src/js/plyr.js
@ -10,15 +10,21 @@ import defaults from './defaults';
|
||||
import support from './support';
|
||||
import utils from './utils';
|
||||
|
||||
import Console from './console';
|
||||
import Storage from './storage';
|
||||
|
||||
import captions from './captions';
|
||||
import controls from './controls';
|
||||
import fullscreen from './fullscreen';
|
||||
import listeners from './listeners';
|
||||
import media from './media';
|
||||
import storage from './storage';
|
||||
import source from './source';
|
||||
import ui from './ui';
|
||||
|
||||
// Private properties
|
||||
// TODO: Use a WeakMap for private globals
|
||||
// const globals = new WeakMap();
|
||||
|
||||
// Globals
|
||||
let scrollPosition = {
|
||||
x: 0,
|
||||
@ -54,7 +60,7 @@ class Plyr {
|
||||
try {
|
||||
return JSON.parse(this.media.getAttribute('data-plyr-config'));
|
||||
} catch (e) {
|
||||
return null;
|
||||
return {};
|
||||
}
|
||||
})()
|
||||
);
|
||||
@ -76,7 +82,7 @@ class Plyr {
|
||||
|
||||
// Captions
|
||||
this.captions = {
|
||||
enabled: null,
|
||||
active: null,
|
||||
currentTrack: null,
|
||||
};
|
||||
|
||||
@ -92,46 +98,35 @@ class Plyr {
|
||||
};
|
||||
|
||||
// Debugging
|
||||
this.console = {
|
||||
log() {},
|
||||
warn() {},
|
||||
error() {},
|
||||
};
|
||||
if (this.config.debug && 'console' in window) {
|
||||
this.console = {
|
||||
log: console.log, // eslint-disable-line
|
||||
warn: console.warn, // eslint-disable-line
|
||||
error: console.error, // eslint-disable-line
|
||||
};
|
||||
this.console.log('Debugging enabled');
|
||||
}
|
||||
// TODO: move to globals
|
||||
this.debug = new Console(this);
|
||||
|
||||
// Log config options and support
|
||||
this.console.log('Config', this.config);
|
||||
this.console.log('Support', support);
|
||||
this.debug.log('Config', this.config);
|
||||
this.debug.log('Support', support);
|
||||
|
||||
// We need an element to setup
|
||||
if (utils.is.nullOrUndefined(this.media) || !utils.is.htmlElement(this.media)) {
|
||||
this.console.error('Setup failed: no suitable element passed');
|
||||
if (utils.is.nullOrUndefined(this.media) || !utils.is.element(this.media)) {
|
||||
this.debug.error('Setup failed: no suitable element passed');
|
||||
return;
|
||||
}
|
||||
|
||||
// Bail if the element is initialized
|
||||
if (this.media.plyr) {
|
||||
this.console.warn('Target already setup');
|
||||
this.debug.warn('Target already setup');
|
||||
return;
|
||||
}
|
||||
|
||||
// Bail if not enabled
|
||||
if (!this.config.enabled) {
|
||||
this.console.error('Setup failed: disabled by config');
|
||||
this.debug.error('Setup failed: disabled by config');
|
||||
return;
|
||||
}
|
||||
|
||||
// Bail if disabled or no basic support
|
||||
// You may want to disable certain UAs etc
|
||||
if (!support.check().api) {
|
||||
this.console.error('Setup failed: no support');
|
||||
this.debug.error('Setup failed: no support');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -158,13 +153,13 @@ class Plyr {
|
||||
this.embedId = this.media.getAttribute(attributes.id);
|
||||
|
||||
if (utils.is.empty(this.provider) || !Object.keys(providers).includes(this.provider)) {
|
||||
this.console.error('Setup failed: Invalid provider');
|
||||
this.debug.error('Setup failed: Invalid provider');
|
||||
return;
|
||||
}
|
||||
|
||||
// Try and get the embed id
|
||||
if (utils.is.empty(this.embedId)) {
|
||||
this.console.error('Setup failed: Embed ID or URL missing');
|
||||
this.debug.error('Setup failed: Embed ID or URL missing');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -202,19 +197,19 @@ class Plyr {
|
||||
break;
|
||||
|
||||
default:
|
||||
this.console.error('Setup failed: unsupported type');
|
||||
this.debug.error('Setup failed: unsupported type');
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup local storage for user settings
|
||||
storage.setup.call(this);
|
||||
this.storage = new Storage(this);
|
||||
|
||||
// Check for support again but with type
|
||||
this.supported = support.check(this.type, this.provider, this.config.inline);
|
||||
|
||||
// If no support for even API, bail
|
||||
if (!this.supported.api) {
|
||||
this.console.error('Setup failed: no support');
|
||||
this.debug.error('Setup failed: no support');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -240,7 +235,7 @@ class Plyr {
|
||||
// Listen for events if debugging
|
||||
if (this.config.debug) {
|
||||
utils.on(this.elements.container, this.config.events.join(' '), event => {
|
||||
this.console.log(`event: ${event.type}`);
|
||||
this.debug.log(`event: ${event.type}`);
|
||||
});
|
||||
}
|
||||
|
||||
@ -386,7 +381,7 @@ class Plyr {
|
||||
this.media.currentTime = targetTime.toFixed(4);
|
||||
|
||||
// Logging
|
||||
this.console.log(`Seeking to ${this.currentTime} seconds`);
|
||||
this.debug.log(`Seeking to ${this.currentTime} seconds`);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -432,7 +427,7 @@ class Plyr {
|
||||
|
||||
// Load volume from storage if no value specified
|
||||
if (!utils.is.number(volume)) {
|
||||
({ volume } = storage.get.call(this));
|
||||
volume = this.storage.get('volume');
|
||||
}
|
||||
|
||||
// Use config if all else fails
|
||||
@ -497,7 +492,7 @@ class Plyr {
|
||||
|
||||
// Load muted state from storage
|
||||
if (!utils.is.boolean(toggle)) {
|
||||
toggle = storage.get.call(this).muted;
|
||||
toggle = this.storage.get('muted');
|
||||
}
|
||||
|
||||
// Use config if all else fails
|
||||
@ -541,9 +536,13 @@ class Plyr {
|
||||
|
||||
if (utils.is.number(input)) {
|
||||
speed = input;
|
||||
} else if (utils.is.number(storage.get.call(this).speed)) {
|
||||
({ speed } = storage.get.call(this));
|
||||
} else {
|
||||
}
|
||||
|
||||
if (!utils.is.number(speed)) {
|
||||
speed = this.storage.get('speed');
|
||||
}
|
||||
|
||||
if (!utils.is.number(speed)) {
|
||||
speed = this.config.speed.selected;
|
||||
}
|
||||
|
||||
@ -556,7 +555,7 @@ class Plyr {
|
||||
}
|
||||
|
||||
if (!this.config.speed.options.includes(speed)) {
|
||||
this.console.warn(`Unsupported speed (${speed})`);
|
||||
this.debug.warn(`Unsupported speed (${speed})`);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -584,14 +583,18 @@ class Plyr {
|
||||
|
||||
if (utils.is.string(input)) {
|
||||
quality = input;
|
||||
} else if (utils.is.number(storage.get.call(this).quality)) {
|
||||
({ quality } = storage.get.call(this));
|
||||
} else {
|
||||
}
|
||||
|
||||
if (!utils.is.string(quality)) {
|
||||
quality = this.storage.get('quality');
|
||||
}
|
||||
|
||||
if (!utils.is.string(quality)) {
|
||||
quality = this.config.quality.selected;
|
||||
}
|
||||
|
||||
if (!this.options.quality.includes(quality)) {
|
||||
this.console.warn(`Unsupported quality option (${quality})`);
|
||||
this.debug.warn(`Unsupported quality option (${quality})`);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -691,7 +694,7 @@ class Plyr {
|
||||
*/
|
||||
set poster(input) {
|
||||
if (!this.isHTML5 || !this.isVideo) {
|
||||
this.console.warn('Poster can only be set on HTML5 video');
|
||||
this.debug.warn('Poster can only be set on HTML5 video');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -733,7 +736,7 @@ class Plyr {
|
||||
*/
|
||||
toggleCaptions(input) {
|
||||
// If there's no full support, or there's no caption toggle
|
||||
if (!this.supported.ui || !utils.is.htmlElement(this.elements.buttons.captions)) {
|
||||
if (!this.supported.ui || !utils.is.element(this.elements.buttons.captions)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -741,21 +744,21 @@ class Plyr {
|
||||
const show = utils.is.boolean(input) ? input : this.elements.container.className.indexOf(this.config.classNames.captions.active) === -1;
|
||||
|
||||
// Nothing to change...
|
||||
if (this.captions.enabled === show) {
|
||||
if (this.captions.active === show) {
|
||||
return this;
|
||||
}
|
||||
|
||||
// Set global
|
||||
this.captions.enabled = show;
|
||||
this.captions.active = show;
|
||||
|
||||
// Toggle state
|
||||
utils.toggleState(this.elements.buttons.captions, this.captions.enabled);
|
||||
utils.toggleState(this.elements.buttons.captions, this.captions.active);
|
||||
|
||||
// Add class hook
|
||||
utils.toggleClass(this.elements.container, this.config.classNames.captions.active, this.captions.enabled);
|
||||
utils.toggleClass(this.elements.container, this.config.classNames.captions.active, this.captions.active);
|
||||
|
||||
// Trigger an event
|
||||
utils.dispatchEvent.call(this, this.media, this.captions.enabled ? 'captionsenabled' : 'captionsdisabled');
|
||||
utils.dispatchEvent.call(this, this.media, this.captions.active ? 'captionsenabled' : 'captionsdisabled');
|
||||
|
||||
// Allow chaining
|
||||
return this;
|
||||
@ -850,7 +853,7 @@ class Plyr {
|
||||
}
|
||||
|
||||
// Set button state
|
||||
if (utils.is.htmlElement(this.elements.buttons.fullscreen)) {
|
||||
if (utils.is.element(this.elements.buttons.fullscreen)) {
|
||||
utils.toggleState(this.elements.buttons.fullscreen, this.fullscreen.active);
|
||||
}
|
||||
|
||||
@ -916,7 +919,7 @@ class Plyr {
|
||||
*/
|
||||
toggleControls(toggle) {
|
||||
// We need controls of course...
|
||||
if (!utils.is.htmlElement(this.elements.controls)) {
|
||||
if (!utils.is.element(this.elements.controls)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -981,7 +984,7 @@ class Plyr {
|
||||
// then set the timer to hide the controls
|
||||
if (!show || this.playing) {
|
||||
this.timers.controls = window.setTimeout(() => {
|
||||
/* this.console.warn({
|
||||
/* this.debug.warn({
|
||||
pressed: this.elements.controls.pressed,
|
||||
hover: this.elements.controls.pressed,
|
||||
playing: this.playing,
|
||||
@ -1088,7 +1091,7 @@ class Plyr {
|
||||
// Replace the container with the original element provided
|
||||
const parent = this.elements.container.parentNode;
|
||||
|
||||
if (utils.is.htmlElement(parent)) {
|
||||
if (utils.is.element(parent)) {
|
||||
parent.replaceChild(this.elements.original, this.elements.container);
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ const source = {
|
||||
// Sources are not checked for support so be careful
|
||||
change(input) {
|
||||
if (!utils.is.object(input) || !('sources' in input) || !input.sources.length) {
|
||||
this.console.warn('Invalid source format');
|
||||
this.debug.warn('Invalid source format');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@ const source = {
|
||||
this.media = null;
|
||||
|
||||
// Reset class name
|
||||
if (utils.is.htmlElement(this.elements.container)) {
|
||||
if (utils.is.element(this.elements.container)) {
|
||||
this.elements.container.removeAttribute('class');
|
||||
}
|
||||
|
||||
@ -105,8 +105,7 @@ const source = {
|
||||
}
|
||||
}
|
||||
|
||||
// Restore class hooks
|
||||
utils.toggleClass(this.elements.container, this.config.classNames.captions.active, this.supported.ui && this.captions.enabled);
|
||||
// Restore class hook
|
||||
ui.addStyleHook.call(this);
|
||||
|
||||
// Set new sources for html5
|
||||
|
@ -2,74 +2,65 @@
|
||||
// Plyr storage
|
||||
// ==========================================================================
|
||||
|
||||
import support from './support';
|
||||
import utils from './utils';
|
||||
|
||||
// Get contents of local storage
|
||||
function get() {
|
||||
const store = window.localStorage.getItem(this.config.storage.key);
|
||||
|
||||
if (utils.is.empty(store)) {
|
||||
return {};
|
||||
class Storage {
|
||||
constructor(player) {
|
||||
this.enabled = player.config.storage.enabled;
|
||||
this.key = player.config.storage.key;
|
||||
}
|
||||
|
||||
return JSON.parse(store);
|
||||
// Check for actual support (see if we can use it)
|
||||
static get supported() {
|
||||
if (!('localStorage' in window)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const test = '___test';
|
||||
|
||||
// Try to use it (it might be disabled, e.g. user is in private mode)
|
||||
// see: https://github.com/sampotts/plyr/issues/131
|
||||
try {
|
||||
window.localStorage.setItem(test, test);
|
||||
window.localStorage.removeItem(test);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
get(key) {
|
||||
const store = window.localStorage.getItem(this.key);
|
||||
|
||||
if (!Storage.supported || utils.is.empty(store)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const json = JSON.parse(store);
|
||||
|
||||
return utils.is.string(key) && key.length ? json[key] : json;
|
||||
}
|
||||
|
||||
set(object) {
|
||||
// Bail if we don't have localStorage support or it's disabled
|
||||
if (!Storage.supported || !this.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Can only store objectst
|
||||
if (!utils.is.object(object)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get current storage
|
||||
const storage = this.get();
|
||||
|
||||
// Update the working copy of the values
|
||||
utils.extend(storage, object);
|
||||
|
||||
// Update storage
|
||||
window.localStorage.setItem(this.key, JSON.stringify(storage));
|
||||
}
|
||||
}
|
||||
|
||||
// Save a value back to local storage
|
||||
function set(object) {
|
||||
// Bail if we don't have localStorage support or it's disabled
|
||||
if (!support.storage || !this.config.storage.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Can only store objectst
|
||||
if (!utils.is.object(object)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get current storage
|
||||
const storage = get.call(this);
|
||||
|
||||
// Update the working copy of the values
|
||||
utils.extend(storage, object);
|
||||
|
||||
// Update storage
|
||||
window.localStorage.setItem(this.config.storage.key, JSON.stringify(storage));
|
||||
}
|
||||
|
||||
// Setup localStorage
|
||||
function setup() {
|
||||
let value = null;
|
||||
let storage = {};
|
||||
|
||||
// Bail if we don't have localStorage support or it's disabled
|
||||
if (!support.storage || !this.config.storage.enabled) {
|
||||
return storage;
|
||||
}
|
||||
|
||||
// Clean up old volume
|
||||
// https://github.com/sampotts/plyr/issues/171
|
||||
window.localStorage.removeItem('plyr-volume');
|
||||
|
||||
// load value from the current key
|
||||
value = window.localStorage.getItem(this.config.storage.key);
|
||||
|
||||
if (!value) {
|
||||
// Key wasn't set (or had been cleared), move along
|
||||
} else if (/^\d+(\.\d+)?$/.test(value)) {
|
||||
// If value is a number, it's probably volume from an older
|
||||
// version of this. See: https://github.com/sampotts/plyr/pull/313
|
||||
// Update the key to be JSON
|
||||
set({
|
||||
volume: parseFloat(value),
|
||||
});
|
||||
} else {
|
||||
// Assume it's JSON from this or a later version of plyr
|
||||
storage = JSON.parse(value);
|
||||
}
|
||||
|
||||
return storage;
|
||||
}
|
||||
|
||||
export default { setup, set, get };
|
||||
export default Storage;
|
||||
|
@ -50,25 +50,6 @@ const support = {
|
||||
};
|
||||
},
|
||||
|
||||
// Local storage
|
||||
// We can't assume if local storage is present that we can use it
|
||||
storage: (() => {
|
||||
if (!('localStorage' in window)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to use it (it might be disabled, e.g. user is in private/porn mode)
|
||||
// see: https://github.com/sampotts/plyr/issues/131
|
||||
const test = '___test';
|
||||
try {
|
||||
window.localStorage.setItem(test, test);
|
||||
window.localStorage.removeItem(test);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
})(),
|
||||
|
||||
// Picture-in-picture support
|
||||
// Safari only currently
|
||||
pip: (() => {
|
||||
|
28
src/js/ui.js
28
src/js/ui.js
@ -31,7 +31,7 @@ const ui = {
|
||||
|
||||
// Don't setup interface if no support
|
||||
if (!this.supported.ui) {
|
||||
this.console.warn(`Basic support only for ${this.provider} ${this.type}`);
|
||||
this.debug.warn(`Basic support only for ${this.provider} ${this.type}`);
|
||||
|
||||
// Remove controls
|
||||
utils.removeElement.call(this, 'controls');
|
||||
@ -47,7 +47,7 @@ const ui = {
|
||||
}
|
||||
|
||||
// Inject custom controls if not present
|
||||
if (!utils.is.htmlElement(this.elements.controls)) {
|
||||
if (!utils.is.element(this.elements.controls)) {
|
||||
// Inject custom controls
|
||||
controls.inject.call(this);
|
||||
|
||||
@ -56,7 +56,7 @@ const ui = {
|
||||
}
|
||||
|
||||
// If there's no controls, bail
|
||||
if (!utils.is.htmlElement(this.elements.controls)) {
|
||||
if (!utils.is.element(this.elements.controls)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -125,7 +125,7 @@ const ui = {
|
||||
if (this.isEmbed) {
|
||||
const iframe = utils.getElement.call(this, 'iframe');
|
||||
|
||||
if (!utils.is.htmlElement(iframe)) {
|
||||
if (!utils.is.element(iframe)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -175,19 +175,19 @@ const ui = {
|
||||
}
|
||||
|
||||
// Update range
|
||||
if (utils.is.htmlElement(this.elements.inputs.volume)) {
|
||||
if (utils.is.element(this.elements.inputs.volume)) {
|
||||
ui.setRange.call(this, this.elements.inputs.volume, this.muted ? 0 : this.volume);
|
||||
}
|
||||
|
||||
// Update mute state
|
||||
if (utils.is.htmlElement(this.elements.buttons.mute)) {
|
||||
if (utils.is.element(this.elements.buttons.mute)) {
|
||||
utils.toggleState(this.elements.buttons.mute, this.muted || this.volume === 0);
|
||||
}
|
||||
},
|
||||
|
||||
// Update seek value and lower fill
|
||||
setRange(target, value = 0) {
|
||||
if (!utils.is.htmlElement(target)) {
|
||||
if (!utils.is.element(target)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -201,15 +201,15 @@ const ui = {
|
||||
// Set <progress> value
|
||||
setProgress(target, input) {
|
||||
const value = utils.is.number(input) ? input : 0;
|
||||
const progress = utils.is.htmlElement(target) ? target : this.elements.display.buffer;
|
||||
const progress = utils.is.element(target) ? target : this.elements.display.buffer;
|
||||
|
||||
// Update value and label
|
||||
if (utils.is.htmlElement(progress)) {
|
||||
if (utils.is.element(progress)) {
|
||||
progress.value = value;
|
||||
|
||||
// Update text label inside
|
||||
const label = progress.getElementsByTagName('span')[0];
|
||||
if (utils.is.htmlElement(label)) {
|
||||
if (utils.is.element(label)) {
|
||||
label.childNodes[0].nodeValue = value;
|
||||
}
|
||||
}
|
||||
@ -267,7 +267,7 @@ const ui = {
|
||||
// Update the displayed time
|
||||
updateTimeDisplay(target = null, time = 0, inverted = false) {
|
||||
// Bail if there's no element to display or the value isn't a number
|
||||
if (!utils.is.htmlElement(target) || !utils.is.number(time)) {
|
||||
if (!utils.is.element(target) || !utils.is.number(time)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -299,7 +299,7 @@ const ui = {
|
||||
// Handle time change event
|
||||
timeUpdate(event) {
|
||||
// Only invert if only one time element is displayed and used for both duration and currentTime
|
||||
const invert = !utils.is.htmlElement(this.elements.display.duration) && this.config.invertTime;
|
||||
const invert = !utils.is.element(this.elements.display.duration) && this.config.invertTime;
|
||||
|
||||
// Duration
|
||||
ui.updateTimeDisplay.call(this, this.elements.display.currentTime, invert ? this.duration - this.currentTime : this.currentTime, invert);
|
||||
@ -320,12 +320,12 @@ const ui = {
|
||||
}
|
||||
|
||||
// If there's only one time display, display duration there
|
||||
if (!utils.is.htmlElement(this.elements.display.duration) && this.config.displayDuration && this.paused) {
|
||||
if (!utils.is.element(this.elements.display.duration) && this.config.displayDuration && this.paused) {
|
||||
ui.updateTimeDisplay.call(this, this.elements.display.currentTime, this.duration);
|
||||
}
|
||||
|
||||
// If there's a duration element, update content
|
||||
if (utils.is.htmlElement(this.elements.display.duration)) {
|
||||
if (utils.is.element(this.elements.display.duration)) {
|
||||
ui.updateTimeDisplay.call(this, this.elements.display.duration, this.duration);
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,9 @@ import support from './support';
|
||||
const utils = {
|
||||
// Check variable types
|
||||
is: {
|
||||
plyr(input) {
|
||||
return this.instanceof(input, Plyr);
|
||||
},
|
||||
object(input) {
|
||||
return this.getConstructor(input) === Object;
|
||||
},
|
||||
@ -25,23 +28,26 @@ const utils = {
|
||||
array(input) {
|
||||
return !this.nullOrUndefined(input) && Array.isArray(input);
|
||||
},
|
||||
nodeList(input) {
|
||||
return this.instanceof(input, window.NodeList);
|
||||
weakMap(input) {
|
||||
return this.instanceof(input, WeakMap);
|
||||
},
|
||||
htmlElement(input) {
|
||||
return this.instanceof(input, window.HTMLElement);
|
||||
nodeList(input) {
|
||||
return this.instanceof(input, NodeList);
|
||||
},
|
||||
element(input) {
|
||||
return this.instanceof(input, Element);
|
||||
},
|
||||
textNode(input) {
|
||||
return this.getConstructor(input) === Text;
|
||||
},
|
||||
event(input) {
|
||||
return this.instanceof(input, window.Event);
|
||||
return this.instanceof(input, Event);
|
||||
},
|
||||
cue(input) {
|
||||
return this.instanceof(input, window.TextTrackCue) || this.instanceof(input, window.VTTCue);
|
||||
return this.instanceof(input, TextTrackCue) || this.instanceof(input, VTTCue);
|
||||
},
|
||||
track(input) {
|
||||
return this.instanceof(input, window.TextTrack) || (!this.nullOrUndefined(input) && this.string(input.kind));
|
||||
return this.instanceof(input, TextTrack) || (!this.nullOrUndefined(input) && this.string(input.kind));
|
||||
},
|
||||
nullOrUndefined(input) {
|
||||
return input === null || typeof input === 'undefined';
|
||||
@ -249,7 +255,7 @@ const utils = {
|
||||
|
||||
// Remove an element
|
||||
removeElement(element) {
|
||||
if (!utils.is.htmlElement(element) || !utils.is.htmlElement(element.parentNode)) {
|
||||
if (!utils.is.element(element) || !utils.is.element(element.parentNode)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -270,6 +276,10 @@ const utils = {
|
||||
|
||||
// Set attributes
|
||||
setAttributes(element, attributes) {
|
||||
if (!utils.is.element(element) || utils.is.empty(attributes)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Object.keys(attributes).forEach(key => {
|
||||
element.setAttribute(key, attributes[key]);
|
||||
});
|
||||
@ -334,7 +344,7 @@ const utils = {
|
||||
|
||||
// Toggle class on an element
|
||||
toggleClass(element, className, toggle) {
|
||||
if (utils.is.htmlElement(element)) {
|
||||
if (utils.is.element(element)) {
|
||||
const contains = element.classList.contains(className);
|
||||
|
||||
element.classList[toggle ? 'add' : 'remove'](className);
|
||||
@ -347,12 +357,12 @@ const utils = {
|
||||
|
||||
// Has class name
|
||||
hasClass(element, className) {
|
||||
return utils.is.htmlElement(element) && element.classList.contains(className);
|
||||
return utils.is.element(element) && element.classList.contains(className);
|
||||
},
|
||||
|
||||
// Toggle hidden attribute on an element
|
||||
toggleHidden(element, toggle) {
|
||||
if (!utils.is.htmlElement(element)) {
|
||||
if (!utils.is.element(element)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -424,14 +434,14 @@ const utils = {
|
||||
};
|
||||
|
||||
// Seek tooltip
|
||||
if (utils.is.htmlElement(this.elements.progress)) {
|
||||
if (utils.is.element(this.elements.progress)) {
|
||||
this.elements.display.seekTooltip = this.elements.progress.querySelector(`.${this.config.classNames.tooltip}`);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
// Log it
|
||||
this.console.warn('It looks like there is a problem with your custom controls HTML', error);
|
||||
this.debug.warn('It looks like there is a problem with your custom controls HTML', error);
|
||||
|
||||
// Restore native video controls
|
||||
this.toggleNativeControls(true);
|
||||
@ -560,7 +570,7 @@ const utils = {
|
||||
// http://www.ssbbartgroup.com/blog/how-not-to-misuse-aria-states-properties-and-roles
|
||||
toggleState(element, input) {
|
||||
// Bail if no target
|
||||
if (!utils.is.htmlElement(element)) {
|
||||
if (!utils.is.element(element)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -580,45 +590,31 @@ const utils = {
|
||||
return (current / max * 100).toFixed(2);
|
||||
},
|
||||
|
||||
// Deep extend/merge destination object with N more objects
|
||||
// http://andrewdupont.net/2009/08/28/deep-extending-objects-in-javascript/
|
||||
// Removed call to arguments.callee (used explicit function name instead)
|
||||
extend(...objects) {
|
||||
const { length } = objects;
|
||||
|
||||
// Bail if nothing to merge
|
||||
if (!length) {
|
||||
return null;
|
||||
// Deep extend destination object with N more objects
|
||||
extend(target = {}, ...sources) {
|
||||
if (!sources.length) {
|
||||
return target;
|
||||
}
|
||||
|
||||
// Return first if specified but nothing to merge
|
||||
if (length === 1) {
|
||||
return objects[0];
|
||||
const source = sources.shift();
|
||||
|
||||
if (!utils.is.object(source)) {
|
||||
return target;
|
||||
}
|
||||
|
||||
// First object is the destination
|
||||
let destination = Array.prototype.shift.call(objects);
|
||||
if (!utils.is.object(destination)) {
|
||||
destination = {};
|
||||
}
|
||||
|
||||
// Loop through all objects to merge
|
||||
objects.forEach(source => {
|
||||
if (!utils.is.object(source)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Object.keys(source).forEach(property => {
|
||||
if (source[property] && source[property].constructor && source[property].constructor === Object) {
|
||||
destination[property] = destination[property] || {};
|
||||
utils.extend(destination[property], source[property]);
|
||||
} else {
|
||||
destination[property] = source[property];
|
||||
Object.keys(source).forEach(key => {
|
||||
if (utils.is.object(source[key])) {
|
||||
if (!Object.keys(target).includes(key)) {
|
||||
Object.assign(target, { [key]: {} });
|
||||
}
|
||||
});
|
||||
|
||||
utils.extend(target[key], source[key]);
|
||||
} else {
|
||||
Object.assign(target, { [key]: source[key] });
|
||||
}
|
||||
});
|
||||
|
||||
return destination;
|
||||
return utils.extend(target, ...sources);
|
||||
},
|
||||
|
||||
// Parse YouTube ID from URL
|
||||
@ -679,6 +675,15 @@ const utils = {
|
||||
|
||||
return typeof type === 'string' ? type : false;
|
||||
})(),
|
||||
|
||||
// Force repaint of element
|
||||
repaint(element) {
|
||||
window.setTimeout(() => {
|
||||
element.setAttribute('hidden', '');
|
||||
element.offsetHeight; // eslint-disable-line
|
||||
element.removeAttribute('hidden');
|
||||
}, 0);
|
||||
},
|
||||
};
|
||||
|
||||
export default utils;
|
||||
|
@ -13,8 +13,8 @@
|
||||
@import 'base';
|
||||
|
||||
@import 'components/badges';
|
||||
@import 'components/buttons';
|
||||
@import 'components/captions';
|
||||
@import 'components/control';
|
||||
@import 'components/controls';
|
||||
@import 'components/embed';
|
||||
@import 'components/menus';
|
||||
|
@ -39,12 +39,12 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (min-width: @plyr-bp-screen-sm) {
|
||||
@media @plyr-mq-sm {
|
||||
padding: (@plyr-control-spacing * 2);
|
||||
font-size: @plyr-font-size-captions-base;
|
||||
}
|
||||
|
||||
@media (min-width: @plyr-bp-screen-md) {
|
||||
@media @plyr-mq-md {
|
||||
font-size: @plyr-font-size-captions-medium;
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,15 @@
|
||||
// --------------------------------------------------------------
|
||||
// Control buttons
|
||||
// --------------------------------------------------------------
|
||||
|
||||
.plyr__control {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
flex-shrink: 0;
|
||||
overflow: visible; // IE11
|
||||
vertical-align: middle;
|
||||
padding: @plyr-control-padding;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
border-radius: 3px;
|
||||
border-radius: @plyr-control-radius;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
color: inherit;
|
||||
@ -66,7 +68,7 @@
|
||||
|
||||
svg {
|
||||
position: relative;
|
||||
left: 2px;
|
||||
left: 2px; // Offset to make the play button look right
|
||||
width: @plyr-control-icon-size-large;
|
||||
height: @plyr-control-icon-size-large;
|
||||
}
|
||||
@ -78,7 +80,7 @@
|
||||
}
|
||||
|
||||
.plyr--full-ui.plyr--video .plyr__control--overlaid {
|
||||
display: inline-block;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.plyr--playing .plyr__control--overlaid {
|
@ -30,7 +30,7 @@
|
||||
margin-left: (@plyr-control-spacing / 2);
|
||||
}
|
||||
|
||||
@media (min-width: @plyr-bp-screen-sm) {
|
||||
@media @plyr-mq-sm {
|
||||
> .plyr__control,
|
||||
.plyr__progress,
|
||||
.plyr__time,
|
||||
|
@ -3,24 +3,20 @@
|
||||
// --------------------------------------------------------------
|
||||
|
||||
.plyr__time {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
font-size: @plyr-font-size-time;
|
||||
}
|
||||
|
||||
// Media duration hidden on small screens
|
||||
.plyr__time + .plyr__time {
|
||||
display: none;
|
||||
|
||||
@media (min-width: @plyr-bp-screen-md) {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
// Add a slash in before
|
||||
&::before {
|
||||
content: '\2044';
|
||||
margin-right: @plyr-control-spacing;
|
||||
}
|
||||
|
||||
@media @plyr-mq-sm-max {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.plyr--video .plyr__time {
|
||||
|
@ -11,12 +11,11 @@
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
@media (min-width: @plyr-bp-screen-sm) {
|
||||
display: block;
|
||||
@media @plyr-mq-sm {
|
||||
max-width: 50px;
|
||||
}
|
||||
|
||||
@media (min-width: @plyr-bp-screen-md) {
|
||||
@media @plyr-mq-md {
|
||||
max-width: 80px;
|
||||
}
|
||||
}
|
||||
|
@ -90,7 +90,7 @@
|
||||
}
|
||||
|
||||
// Large captions in full screen on larger screens
|
||||
@media (min-width: @plyr-bp-screen-lg) {
|
||||
@media @plyr-mq-lg {
|
||||
.plyr__captions {
|
||||
font-size: @plyr-font-size-captions-large;
|
||||
}
|
||||
|
@ -42,10 +42,13 @@
|
||||
@plyr-control-icon-size-large: 20px;
|
||||
@plyr-control-spacing: 10px;
|
||||
@plyr-control-padding: (@plyr-control-spacing * 0.7);
|
||||
@plyr-control-radius: 3px;
|
||||
|
||||
@plyr-video-controls-bg: #000;
|
||||
@plyr-video-control-color: #fff;
|
||||
@plyr-video-control-color-hover: #fff;
|
||||
@plyr-video-control-bg-hover: @plyr-color-main;
|
||||
|
||||
@plyr-audio-controls-bg: #fff;
|
||||
@plyr-audio-control-color: @plyr-color-fiord;
|
||||
@plyr-audio-control-color-hover: #fff;
|
||||
@ -74,20 +77,35 @@
|
||||
@plyr-audio-progress-buffered-bg: fade(@plyr-color-heather, 66%);
|
||||
|
||||
// Range sliders
|
||||
@plyr-range-track-height: 8px;
|
||||
@plyr-range-thumb-height: floor(@plyr-range-track-height * 2);
|
||||
@plyr-range-thumb-width: floor(@plyr-range-track-height * 2);
|
||||
@plyr-range-track-height: 6px;
|
||||
@plyr-range-thumb-height: ceil(@plyr-range-track-height * 2.3);
|
||||
@plyr-range-thumb-width: ceil(@plyr-range-track-height * 2.3);
|
||||
@plyr-range-thumb-bg: #fff;
|
||||
@plyr-range-thumb-border: 2px solid transparent;
|
||||
@plyr-range-thumb-shadow: 0 1px 1px fade(@plyr-video-controls-bg, 15%), 0 0 0 1px fade(@plyr-color-gunmetal, 20%);
|
||||
@plyr-range-thumb-active-border-color: #fff;
|
||||
@plyr-range-thumb-active-bg: @plyr-video-control-bg-hover;
|
||||
@plyr-range-thumb-active-scale: 1.25;
|
||||
@plyr-range-thumb-active-scale: 1.5;
|
||||
@plyr-video-range-track-bg: @plyr-video-progress-buffered-bg;
|
||||
@plyr-audio-range-track-bg: @plyr-audio-progress-buffered-bg;
|
||||
@plyr-range-selected-bg: @plyr-color-main;
|
||||
|
||||
// Breakpoints
|
||||
@plyr-bp-screen-sm: 480px;
|
||||
@plyr-bp-screen-md: 768px;
|
||||
@plyr-bp-screen-lg: 1024px;
|
||||
@plyr-bp-sm: 480px;
|
||||
@plyr-bp-md: 768px;
|
||||
@plyr-bp-lg: 1024px;
|
||||
|
||||
// Max-width media queries
|
||||
@plyr-bp-xs-max: (@plyr-bp-sm - 1);
|
||||
@plyr-bp-sm-max: (@plyr-bp-md - 1);
|
||||
@plyr-bp-md-max: (@plyr-bp-lg - 1);
|
||||
|
||||
// Mobile first
|
||||
@plyr-mq-sm: ~'only screen and (min-width: @{plyr-bp-sm}) ';
|
||||
@plyr-mq-md: ~'only screen and (min-width: @{plyr-bp-md}) ';
|
||||
@plyr-mq-lg: ~'only screen and (min-width: @{plyr-bp-lg}) ';
|
||||
|
||||
// Mobile last
|
||||
@plyr-mq-xs-max: ~'only screen and (max-width: @{plyr-bp-xs-max}) ';
|
||||
@plyr-mq-sm-max: ~'only screen and (max-width: @{plyr-bp-sm-max}) ';
|
||||
@plyr-mq-md-max: ~'only screen and (max-width: @{plyr-bp-md-max}) ';
|
||||
|
Loading…
x
Reference in New Issue
Block a user