Merge branch 'develop' of github.com:Selz/plyr into develop
# Conflicts: # readme.md
This commit is contained in:
commit
3f41a0cf54
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,a){if(t in n&&(a||t!==r)&&(r.length||t!==n.video)){switch(t){case n.video:o.src={type:"video",title:"View From A Blue Moon",sources:[{src:"https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.mp4",type:"video/mp4"}],poster:"https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.jpg",tracks:[{kind:"captions",label:"English",srclang:"en",src:"https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.en.vtt",default:!0},{kind:"captions",label:"French",srclang:"fr",src:"https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.fr.vtt"}]};break;case n.audio:o.src={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 n.youtube:o.src={type:"video",title:"View From A Blue Moon",sources:[{src:"https://www.youtube.com/watch?v=bTqVqk7FSmY",type:"youtube"}]};break;case n.vimeo:o.src={type:"video",title:"View From A Blue Moon",sources:[{src:"https://vimeo.com/76979871",type:"vimeo"}]}}r=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",tooltips:{controls:!0},captions:{active:!0},controls:["play-large","play","progress","current-time","mute","volume","captions","settings","fullscreen","pip","airplay"]});window.player=o;var i=document.querySelectorAll("[data-source]"),n={video:"video",audio:"audio",youtube:"youtube",vimeo:"vimeo"},r=window.location.hash.replace("#",""),a=window.history&&window.history.pushState;if(Array.from(i).forEach(function(e){e.addEventListener("click",function(){var o=e.getAttribute("data-source");t(o),a&&window.history.pushState({type:o},"","#"+o)})}),window.addEventListener("popstate",function(e){e.state&&"type"in e.state&&t(e.state.type)}),a){var s=!r.length;s&&(r=n.video),r in n&&window.history.replaceState({type:r},"",s?"":"#"+r),r!==n.video&&t(r,!0)}}),"plyr.io"===window.location.host&&(!function(e,t,o,i,n,r,a){e.GoogleAnalyticsObject=n,e.ga=e.ga||function(){(e.ga.q=e.ga.q||[]).push(arguments)},e.ga.l=1*new Date,r=t.createElement(o),a=t.getElementsByTagName(o)[0],r.async=1,r.src="//www.google-analytics.com/analytics.js",a.parentNode.insertBefore(r,a)}(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,a){if(t in n&&(a||t!==r)&&(r.length||t!==n.video)){switch(t){case n.video:o.src={type:"video",title:"View From A Blue Moon",sources:[{src:"https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.mp4",type:"video/mp4"}],poster:"https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.jpg",tracks:[{kind:"captions",label:"English",srclang:"en",src:"https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.en.vtt",default:!0},{kind:"captions",label:"French",srclang:"fr",src:"https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.fr.vtt"}]};break;case n.audio:o.src={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 n.youtube:o.src={type:"video",title:"View From A Blue Moon",sources:[{src:"https://www.youtube.com/watch?v=bTqVqk7FSmY",type:"youtube"}]};break;case n.vimeo:o.src={type:"video",title:"View From A Blue Moon",sources:[{src:"https://vimeo.com/76979871",type:"vimeo"}]}}r=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"]});window.player=o;var i=document.querySelectorAll("[data-source]"),n={video:"video",audio:"audio",youtube:"youtube",vimeo:"vimeo"},r=window.location.hash.replace("#",""),a=window.history&&window.history.pushState;if(Array.from(i).forEach(function(e){e.addEventListener("click",function(){var o=e.getAttribute("data-source");t(o),a&&window.history.pushState({type:o},"","#"+o)})}),window.addEventListener("popstate",function(e){e.state&&"type"in e.state&&t(e.state.type)}),a){var s=!r.length;s&&(r=n.video),r in n&&window.history.replaceState({type:r},"",s?"":"#"+r),r!==n.video&&t(r,!0)}}),"plyr.io"===window.location.host&&(!function(e,t,o,i,n,r,a){e.GoogleAnalyticsObject=n,e.ga=e.ga||function(){(e.ga.q=e.ga.q||[]).push(arguments)},e.ga.l=1*new Date,r=t.createElement(o),a=t.getElementsByTagName(o)[0],r.async=1,r.src="//www.google-analytics.com/analytics.js",a.parentNode.insertBefore(r,a)}(window,document,"script",0,"ga"),window.ga("create","UA-40881672-11","auto"),window.ga("send","pageview"));
|
||||||
|
|
||||||
//# sourceMappingURL=demo.js.map
|
//# 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
@ -43,6 +43,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
debug: true,
|
debug: true,
|
||||||
title: 'View From A Blue Moon',
|
title: 'View From A Blue Moon',
|
||||||
iconUrl: '../dist/plyr.svg',
|
iconUrl: '../dist/plyr.svg',
|
||||||
|
keyboard: {
|
||||||
|
global: true,
|
||||||
|
},
|
||||||
tooltips: {
|
tooltips: {
|
||||||
controls: true,
|
controls: 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
16
src/js/controls.js
vendored
16
src/js/controls.js
vendored
@ -6,11 +6,14 @@ import support from './support';
|
|||||||
import utils from './utils';
|
import utils from './utils';
|
||||||
import ui from './ui';
|
import ui from './ui';
|
||||||
|
|
||||||
|
// Sniff out the browser
|
||||||
|
const browser = utils.getBrowser();
|
||||||
|
|
||||||
const controls = {
|
const controls = {
|
||||||
// Webkit polyfill for lower fill range
|
// Webkit polyfill for lower fill range
|
||||||
updateRangeFill(target) {
|
updateRangeFill(target) {
|
||||||
// WebKit only
|
// WebKit only
|
||||||
if (!this.browser.isWebkit) {
|
if (!browser.isWebkit) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,7 +52,7 @@ const controls = {
|
|||||||
getIconUrl() {
|
getIconUrl() {
|
||||||
return {
|
return {
|
||||||
url: this.config.iconUrl,
|
url: this.config.iconUrl,
|
||||||
absolute: this.config.iconUrl.indexOf('http') === 0 || (this.browser.isIE && !window.svg4everybody),
|
absolute: this.config.iconUrl.indexOf('http') === 0 || (browser.isIE && !window.svg4everybody),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -1139,14 +1142,11 @@ const controls = {
|
|||||||
inject() {
|
inject() {
|
||||||
// Sprite
|
// Sprite
|
||||||
if (this.config.loadSprite) {
|
if (this.config.loadSprite) {
|
||||||
const iconUrl = controls.getIconUrl.call(this);
|
const icon = controls.getIconUrl.call(this);
|
||||||
|
|
||||||
// Only load external sprite using AJAX
|
// Only load external sprite using AJAX
|
||||||
if (iconUrl.absolute) {
|
if (icon.absolute) {
|
||||||
this.log(`AJAX loading absolute SVG sprite ${this.browser.isIE ? '(due to IE)' : ''}`);
|
utils.loadSprite(icon.url, 'sprite-plyr');
|
||||||
utils.loadSprite(iconUrl.url, 'sprite-plyr');
|
|
||||||
} else {
|
|
||||||
this.log('Sprite will be used as external resource directly');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,9 +19,18 @@ const defaults = {
|
|||||||
volume: 1,
|
volume: 1,
|
||||||
muted: false,
|
muted: false,
|
||||||
|
|
||||||
|
// Pass a custom duration
|
||||||
|
duration: null,
|
||||||
|
|
||||||
// Display the media duration
|
// Display the media duration
|
||||||
displayDuration: true,
|
displayDuration: true,
|
||||||
|
|
||||||
|
// Aspect ratio (for embeds)
|
||||||
|
ratio: '16:9',
|
||||||
|
|
||||||
|
// Looping
|
||||||
|
loop: false,
|
||||||
|
|
||||||
// Click video to play
|
// Click video to play
|
||||||
clickToPlay: true,
|
clickToPlay: true,
|
||||||
|
|
||||||
@ -42,22 +51,12 @@ const defaults = {
|
|||||||
// Blank video (used to prevent errors on source change)
|
// Blank video (used to prevent errors on source change)
|
||||||
blankVideo: 'https://cdn.plyr.io/static/blank.mp4',
|
blankVideo: 'https://cdn.plyr.io/static/blank.mp4',
|
||||||
|
|
||||||
// Pass a custom duration
|
|
||||||
duration: null,
|
|
||||||
|
|
||||||
// Quality default
|
// Quality default
|
||||||
quality: {
|
quality: {
|
||||||
default: 'default',
|
default: 'default',
|
||||||
options: ['hd2160', 'hd1440', 'hd1080', 'hd720', 'large', 'medium', 'small', 'tiny', 'default'],
|
options: ['hd2160', 'hd1440', 'hd1080', 'hd720', 'large', 'medium', 'small', 'tiny', 'default'],
|
||||||
},
|
},
|
||||||
|
|
||||||
// Set loops
|
|
||||||
loop: {
|
|
||||||
active: false,
|
|
||||||
start: null,
|
|
||||||
end: null,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Speed default and options to display
|
// Speed default and options to display
|
||||||
speed: {
|
speed: {
|
||||||
default: 1,
|
default: 1,
|
||||||
|
@ -9,7 +9,217 @@ import fullscreen from './fullscreen';
|
|||||||
import storage from './storage';
|
import storage from './storage';
|
||||||
import ui from './ui';
|
import ui from './ui';
|
||||||
|
|
||||||
|
// Sniff out the browser
|
||||||
|
const browser = utils.getBrowser();
|
||||||
|
|
||||||
const listeners = {
|
const listeners = {
|
||||||
|
// Global listeners
|
||||||
|
global() {
|
||||||
|
let last = null;
|
||||||
|
|
||||||
|
// Get the key code for an event
|
||||||
|
const getKeyCode = event => (event.keyCode ? event.keyCode : event.which);
|
||||||
|
|
||||||
|
// Handle key press
|
||||||
|
const handleKey = event => {
|
||||||
|
const code = getKeyCode(event);
|
||||||
|
const pressed = event.type === 'keydown';
|
||||||
|
const held = pressed && code === last;
|
||||||
|
|
||||||
|
// If the event is bubbled from the media element
|
||||||
|
// Firefox doesn't get the keycode for whatever reason
|
||||||
|
if (!utils.is.number(code)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seek by the number keys
|
||||||
|
const seekByKey = () => {
|
||||||
|
// Divide the max duration into 10th's and times by the number value
|
||||||
|
this.currentTime = this.duration / 10 * (code - 48);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle the key on keydown
|
||||||
|
// Reset on keyup
|
||||||
|
if (pressed) {
|
||||||
|
// Which keycodes should we prevent default
|
||||||
|
const preventDefault = [
|
||||||
|
48,
|
||||||
|
49,
|
||||||
|
50,
|
||||||
|
51,
|
||||||
|
52,
|
||||||
|
53,
|
||||||
|
54,
|
||||||
|
56,
|
||||||
|
57,
|
||||||
|
32,
|
||||||
|
75,
|
||||||
|
38,
|
||||||
|
40,
|
||||||
|
77,
|
||||||
|
39,
|
||||||
|
37,
|
||||||
|
70,
|
||||||
|
67,
|
||||||
|
73,
|
||||||
|
76,
|
||||||
|
79,
|
||||||
|
];
|
||||||
|
|
||||||
|
// Check focused element
|
||||||
|
// 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)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the code is found prevent default (e.g. prevent scrolling for arrows)
|
||||||
|
if (preventDefault.includes(code)) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (code) {
|
||||||
|
case 48:
|
||||||
|
case 49:
|
||||||
|
case 50:
|
||||||
|
case 51:
|
||||||
|
case 52:
|
||||||
|
case 53:
|
||||||
|
case 54:
|
||||||
|
case 55:
|
||||||
|
case 56:
|
||||||
|
case 57:
|
||||||
|
// 0-9
|
||||||
|
if (!held) {
|
||||||
|
seekByKey();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 32:
|
||||||
|
case 75:
|
||||||
|
// Space and K key
|
||||||
|
if (!held) {
|
||||||
|
this.warn('togglePlay', event.type);
|
||||||
|
this.togglePlay();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 38:
|
||||||
|
// Arrow up
|
||||||
|
this.increaseVolume(0.1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 40:
|
||||||
|
// Arrow down
|
||||||
|
this.decreaseVolume(0.1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 77:
|
||||||
|
// M key
|
||||||
|
if (!held) {
|
||||||
|
this.muted = 'toggle';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 39:
|
||||||
|
// Arrow forward
|
||||||
|
this.forward();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 37:
|
||||||
|
// Arrow back
|
||||||
|
this.rewind();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 70:
|
||||||
|
// F key
|
||||||
|
this.toggleFullscreen();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 67:
|
||||||
|
// C key
|
||||||
|
if (!held) {
|
||||||
|
this.toggleCaptions();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 73:
|
||||||
|
this.setLoop('start');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 76:
|
||||||
|
this.setLoop();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 79:
|
||||||
|
this.setLoop('end');
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Escape is handle natively when in full screen
|
||||||
|
// So we only need to worry about non native
|
||||||
|
if (!fullscreen.enabled && this.fullscreen.active && code === 27) {
|
||||||
|
this.toggleFullscreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store last code for next cycle
|
||||||
|
last = code;
|
||||||
|
} else {
|
||||||
|
last = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Keyboard shortcuts
|
||||||
|
if (this.config.keyboard.global) {
|
||||||
|
utils.on(window, 'keydown keyup', handleKey, false);
|
||||||
|
} else if (this.config.keyboard.focused) {
|
||||||
|
utils.on(this.elements.container, 'keydown keyup', handleKey, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect tab focus
|
||||||
|
// Remove class on blur/focusout
|
||||||
|
utils.on(this.elements.container, 'focusout', event => {
|
||||||
|
utils.toggleClass(event.target, this.config.classNames.tabFocus, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add classname to tabbed elements
|
||||||
|
utils.on(this.elements.container, 'keydown', event => {
|
||||||
|
if (event.keyCode !== 9) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delay the adding of classname until the focus has changed
|
||||||
|
// This event fires before the focusin event
|
||||||
|
window.setTimeout(() => {
|
||||||
|
utils.toggleClass(utils.getFocusElement(), this.config.classNames.tabFocus, true);
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Toggle controls visibility based on mouse movement
|
||||||
|
if (this.config.hideControls) {
|
||||||
|
// Toggle controls on mouse events and entering fullscreen
|
||||||
|
utils.on(
|
||||||
|
this.elements.container,
|
||||||
|
'mouseenter mouseleave mousemove touchstart touchend touchcancel touchmove enterfullscreen',
|
||||||
|
event => {
|
||||||
|
this.toggleControls(event);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle user exiting fullscreen by escaping etc
|
||||||
|
if (fullscreen.enabled) {
|
||||||
|
utils.on(document, fullscreen.eventType, event => {
|
||||||
|
this.toggleFullscreen(event);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// Listen for media events
|
// Listen for media events
|
||||||
media() {
|
media() {
|
||||||
// Time change on media
|
// Time change on media
|
||||||
@ -134,8 +344,7 @@ const listeners = {
|
|||||||
// Listen for control events
|
// Listen for control events
|
||||||
controls() {
|
controls() {
|
||||||
// IE doesn't support input event, so we fallback to change
|
// IE doesn't support input event, so we fallback to change
|
||||||
const inputEvent = this.browser.isIE ? 'change' : 'input';
|
const inputEvent = browser.isIE ? 'change' : 'input';
|
||||||
let last = null;
|
|
||||||
|
|
||||||
// Trigger custom and default handlers
|
// Trigger custom and default handlers
|
||||||
const proxy = (event, handlerKey, defaultHandler) => {
|
const proxy = (event, handlerKey, defaultHandler) => {
|
||||||
@ -165,188 +374,6 @@ const listeners = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get the key code for an event
|
|
||||||
const getKeyCode = event => (event.keyCode ? event.keyCode : event.which);
|
|
||||||
|
|
||||||
// Handle key press
|
|
||||||
const handleKey = event => {
|
|
||||||
const code = getKeyCode(event);
|
|
||||||
const pressed = event.type === 'keydown';
|
|
||||||
const held = pressed && code === last;
|
|
||||||
|
|
||||||
// If the event is bubbled from the media element
|
|
||||||
// Firefox doesn't get the keycode for whatever reason
|
|
||||||
if (!utils.is.number(code)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Seek by the number keys
|
|
||||||
const seekByKey = () => {
|
|
||||||
// Divide the max duration into 10th's and times by the number value
|
|
||||||
this.currentTime = this.duration / 10 * (code - 48);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Handle the key on keydown
|
|
||||||
// Reset on keyup
|
|
||||||
if (pressed) {
|
|
||||||
// Which keycodes should we prevent default
|
|
||||||
const preventDefault = [
|
|
||||||
48,
|
|
||||||
49,
|
|
||||||
50,
|
|
||||||
51,
|
|
||||||
52,
|
|
||||||
53,
|
|
||||||
54,
|
|
||||||
56,
|
|
||||||
57,
|
|
||||||
32,
|
|
||||||
75,
|
|
||||||
38,
|
|
||||||
40,
|
|
||||||
77,
|
|
||||||
39,
|
|
||||||
37,
|
|
||||||
70,
|
|
||||||
67,
|
|
||||||
73,
|
|
||||||
76,
|
|
||||||
79,
|
|
||||||
];
|
|
||||||
|
|
||||||
// Check focused element
|
|
||||||
// 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)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the code is found prevent default (e.g. prevent scrolling for arrows)
|
|
||||||
if (preventDefault.includes(code)) {
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (code) {
|
|
||||||
case 48:
|
|
||||||
case 49:
|
|
||||||
case 50:
|
|
||||||
case 51:
|
|
||||||
case 52:
|
|
||||||
case 53:
|
|
||||||
case 54:
|
|
||||||
case 55:
|
|
||||||
case 56:
|
|
||||||
case 57:
|
|
||||||
// 0-9
|
|
||||||
if (!held) {
|
|
||||||
seekByKey();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 32:
|
|
||||||
case 75:
|
|
||||||
// Space and K key
|
|
||||||
if (!held) {
|
|
||||||
this.togglePlay();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 38:
|
|
||||||
// Arrow up
|
|
||||||
this.increaseVolume(0.1);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 40:
|
|
||||||
// Arrow down
|
|
||||||
this.decreaseVolume(0.1);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 77:
|
|
||||||
// M key
|
|
||||||
if (!held) {
|
|
||||||
this.muted = 'toggle';
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 39:
|
|
||||||
// Arrow forward
|
|
||||||
this.forward();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 37:
|
|
||||||
// Arrow back
|
|
||||||
this.rewind();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 70:
|
|
||||||
// F key
|
|
||||||
this.toggleFullscreen();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 67:
|
|
||||||
// C key
|
|
||||||
if (!held) {
|
|
||||||
this.toggleCaptions();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 73:
|
|
||||||
this.setLoop('start');
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 76:
|
|
||||||
this.setLoop();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 79:
|
|
||||||
this.setLoop('end');
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Escape is handle natively when in full screen
|
|
||||||
// So we only need to worry about non native
|
|
||||||
if (!fullscreen.enabled && this.fullscreen.active && code === 27) {
|
|
||||||
this.toggleFullscreen();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store last code for next cycle
|
|
||||||
last = code;
|
|
||||||
} else {
|
|
||||||
last = null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Keyboard shortcuts
|
|
||||||
if (this.config.keyboard.focused) {
|
|
||||||
utils.on(this.elements.container, 'keydown keyup', handleKey, false);
|
|
||||||
} else if (this.config.keyboard.global) {
|
|
||||||
utils.on(window, 'keydown keyup', handleKey, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detect tab focus
|
|
||||||
// Remove class on blur/focusout
|
|
||||||
utils.on(this.elements.container, 'focusout', event => {
|
|
||||||
utils.toggleClass(event.target, this.config.classNames.tabFocus, false);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add classname to tabbed elements
|
|
||||||
utils.on(this.elements.container, 'keydown', event => {
|
|
||||||
if (event.keyCode !== 9) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delay the adding of classname until the focus has changed
|
|
||||||
// This event fires before the focusin event
|
|
||||||
window.setTimeout(() => {
|
|
||||||
utils.toggleClass(utils.getFocusElement(), this.config.classNames.tabFocus, true);
|
|
||||||
}, 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Play
|
// Play
|
||||||
utils.on(this.elements.buttons.play, 'click', event => proxy(event, 'play', togglePlay));
|
utils.on(this.elements.buttons.play, 'click', event => proxy(event, 'play', togglePlay));
|
||||||
|
|
||||||
@ -468,7 +495,7 @@ const listeners = {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Polyfill for lower fill in <input type="range"> for webkit
|
// Polyfill for lower fill in <input type="range"> for webkit
|
||||||
if (this.browser.isWebkit) {
|
if (browser.isWebkit) {
|
||||||
utils.on(utils.getElements.call(this, 'input[type="range"]'), 'input', event => {
|
utils.on(utils.getElements.call(this, 'input[type="range"]'), 'input', event => {
|
||||||
controls.updateRangeFill.call(this, event.target);
|
controls.updateRangeFill.call(this, event.target);
|
||||||
});
|
});
|
||||||
@ -481,15 +508,6 @@ const listeners = {
|
|||||||
|
|
||||||
// Toggle controls visibility based on mouse movement
|
// Toggle controls visibility based on mouse movement
|
||||||
if (this.config.hideControls) {
|
if (this.config.hideControls) {
|
||||||
// Toggle controls on mouse events and entering fullscreen
|
|
||||||
utils.on(
|
|
||||||
this.elements.container,
|
|
||||||
'mouseenter mouseleave mousemove touchstart touchend touchcancel touchmove enterfullscreen',
|
|
||||||
event => {
|
|
||||||
this.toggleControls(event);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Watch for cursor over controls so they don't hide when trying to interact
|
// Watch for cursor over controls so they don't hide when trying to interact
|
||||||
utils.on(this.elements.controls, 'mouseenter mouseleave', event => {
|
utils.on(this.elements.controls, 'mouseenter mouseleave', event => {
|
||||||
this.elements.controls.hover = event.type === 'mouseenter';
|
this.elements.controls.hover = event.type === 'mouseenter';
|
||||||
@ -553,13 +571,6 @@ const listeners = {
|
|||||||
}),
|
}),
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
// Handle user exiting fullscreen by escaping etc
|
|
||||||
if (fullscreen.enabled) {
|
|
||||||
utils.on(document, fullscreen.eventType, event => {
|
|
||||||
this.toggleFullscreen(event);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -8,6 +8,9 @@ import youtube from './plugins/youtube';
|
|||||||
import vimeo from './plugins/vimeo';
|
import vimeo from './plugins/vimeo';
|
||||||
import ui from './ui';
|
import ui from './ui';
|
||||||
|
|
||||||
|
// Sniff out the browser
|
||||||
|
const browser = utils.getBrowser();
|
||||||
|
|
||||||
const media = {
|
const media = {
|
||||||
// Setup media
|
// Setup media
|
||||||
setup() {
|
setup() {
|
||||||
@ -45,7 +48,7 @@ const media = {
|
|||||||
utils.toggleClass(this.elements.container, this.config.classNames.stopped, this.config.autoplay);
|
utils.toggleClass(this.elements.container, this.config.classNames.stopped, this.config.autoplay);
|
||||||
|
|
||||||
// Add iOS class
|
// Add iOS class
|
||||||
utils.toggleClass(this.elements.container, this.config.classNames.isIos, this.browser.isIos);
|
utils.toggleClass(this.elements.container, this.config.classNames.isIos, browser.isIos);
|
||||||
|
|
||||||
// Add touch class
|
// Add touch class
|
||||||
utils.toggleClass(this.elements.container, this.config.classNames.isTouch, support.touch);
|
utils.toggleClass(this.elements.container, this.config.classNames.isTouch, support.touch);
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
import utils from './../utils';
|
import utils from './../utils';
|
||||||
import captions from './../captions';
|
import captions from './../captions';
|
||||||
|
import controls from './../controls';
|
||||||
import ui from './../ui';
|
import ui from './../ui';
|
||||||
|
|
||||||
const vimeo = {
|
const vimeo = {
|
||||||
@ -15,6 +16,9 @@ const vimeo = {
|
|||||||
// Add embed class for responsive
|
// Add embed class for responsive
|
||||||
utils.toggleClass(this.elements.wrapper, this.config.classNames.embed, true);
|
utils.toggleClass(this.elements.wrapper, this.config.classNames.embed, true);
|
||||||
|
|
||||||
|
// Set intial ratio
|
||||||
|
vimeo.setAspectRatio.call(this);
|
||||||
|
|
||||||
// Set ID
|
// Set ID
|
||||||
this.media.setAttribute('id', utils.generateId(this.type));
|
this.media.setAttribute('id', utils.generateId(this.type));
|
||||||
|
|
||||||
@ -33,21 +37,31 @@ const vimeo = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Set aspect ratio
|
||||||
|
setAspectRatio(input) {
|
||||||
|
const ratio = utils.is.string(input) ? input.split(':') : this.config.ratio.split(':');
|
||||||
|
const padding = 100 / ratio[0] * ratio[1];
|
||||||
|
const offset = (300 - padding) / 6;
|
||||||
|
this.elements.wrapper.style.paddingBottom = `${padding}%`;
|
||||||
|
this.media.style.transform = `translateY(-${offset}%)`;
|
||||||
|
},
|
||||||
|
|
||||||
// API Ready
|
// API Ready
|
||||||
ready() {
|
ready() {
|
||||||
const player = this;
|
const player = this;
|
||||||
|
|
||||||
// Get Vimeo params for the iframe
|
// Get Vimeo params for the iframe
|
||||||
const options = {
|
const options = {
|
||||||
loop: this.config.loop.active,
|
loop: player.config.loop.active,
|
||||||
autoplay: this.config.autoplay,
|
autoplay: player.config.autoplay,
|
||||||
byline: false,
|
byline: false,
|
||||||
portrait: false,
|
portrait: false,
|
||||||
title: false,
|
title: false,
|
||||||
|
speed: true,
|
||||||
transparent: 0,
|
transparent: 0,
|
||||||
};
|
};
|
||||||
const params = utils.buildUrlParameters(options);
|
const params = utils.buildUrlParameters(options);
|
||||||
const id = utils.parseVimeoId(this.embedId);
|
const id = utils.parseVimeoId(player.embedId);
|
||||||
|
|
||||||
// Build an iframe
|
// Build an iframe
|
||||||
const iframe = utils.createElement('iframe');
|
const iframe = utils.createElement('iframe');
|
||||||
@ -57,7 +71,7 @@ const vimeo = {
|
|||||||
player.media.appendChild(iframe);
|
player.media.appendChild(iframe);
|
||||||
|
|
||||||
// Setup instance
|
// Setup instance
|
||||||
// https://github.com/vimeo/this.js
|
// https://github.com/vimeo/player.js
|
||||||
player.embed = new window.Vimeo.Player(iframe);
|
player.embed = new window.Vimeo.Player(iframe);
|
||||||
|
|
||||||
// Create a faux HTML5 API using the Vimeo API
|
// Create a faux HTML5 API using the Vimeo API
|
||||||
@ -99,18 +113,22 @@ const vimeo = {
|
|||||||
|
|
||||||
// Restore pause state
|
// Restore pause state
|
||||||
if (paused) {
|
if (paused) {
|
||||||
this.pause();
|
player.pause();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Playback speed
|
// Playback speed
|
||||||
// Not currently supported in Vimeo
|
let { playbackRate } = player.media;
|
||||||
Object.defineProperty(player.media, 'playbackRate', {
|
Object.defineProperty(player.media, 'playbackRate', {
|
||||||
get() {
|
get() {
|
||||||
return null;
|
return playbackRate;
|
||||||
|
},
|
||||||
|
set(input) {
|
||||||
|
playbackRate = input;
|
||||||
|
player.embed.setPlaybackRate(input);
|
||||||
|
utils.dispatchEvent.call(player, player.media, 'ratechange');
|
||||||
},
|
},
|
||||||
set() {},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Volume
|
// Volume
|
||||||
@ -148,6 +166,17 @@ const vimeo = {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Set aspect ratio based on video size
|
||||||
|
Promise.all([player.embed.getVideoWidth(), player.embed.getVideoHeight()]).then(dimensions => {
|
||||||
|
const ratio = utils.getAspectRatio(dimensions[0], dimensions[1]);
|
||||||
|
vimeo.setAspectRatio.call(this, ratio);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get available speeds
|
||||||
|
if (player.config.controls.includes('settings') && player.config.settings.includes('speed')) {
|
||||||
|
controls.setSpeedMenu.call(player);
|
||||||
|
}
|
||||||
|
|
||||||
// Get title
|
// Get title
|
||||||
player.embed.getVideoTitle().then(title => {
|
player.embed.getVideoTitle().then(title => {
|
||||||
player.config.title = title;
|
player.config.title = title;
|
||||||
@ -156,7 +185,7 @@ const vimeo = {
|
|||||||
// Get current time
|
// Get current time
|
||||||
player.embed.getCurrentTime().then(value => {
|
player.embed.getCurrentTime().then(value => {
|
||||||
currentTime = value;
|
currentTime = value;
|
||||||
utils.dispatchEvent.call(this, this.media, 'timeupdate');
|
utils.dispatchEvent.call(player, player.media, 'timeupdate');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Get duration
|
// Get duration
|
||||||
@ -202,31 +231,31 @@ const vimeo = {
|
|||||||
utils.dispatchEvent.call(player, player.media, 'pause');
|
utils.dispatchEvent.call(player, player.media, 'pause');
|
||||||
});
|
});
|
||||||
|
|
||||||
this.embed.on('timeupdate', data => {
|
player.embed.on('timeupdate', data => {
|
||||||
this.media.seeking = false;
|
player.media.seeking = false;
|
||||||
currentTime = data.seconds;
|
currentTime = data.seconds;
|
||||||
utils.dispatchEvent.call(this, this.media, 'timeupdate');
|
utils.dispatchEvent.call(player, player.media, 'timeupdate');
|
||||||
});
|
});
|
||||||
|
|
||||||
this.embed.on('progress', data => {
|
player.embed.on('progress', data => {
|
||||||
this.media.buffered = data.percent;
|
player.media.buffered = data.percent;
|
||||||
utils.dispatchEvent.call(this, this.media, 'progress');
|
utils.dispatchEvent.call(player, player.media, 'progress');
|
||||||
|
|
||||||
if (parseInt(data.percent, 10) === 1) {
|
if (parseInt(data.percent, 10) === 1) {
|
||||||
// Trigger event
|
// Trigger event
|
||||||
utils.dispatchEvent.call(this, this.media, 'canplaythrough');
|
utils.dispatchEvent.call(player, player.media, 'canplaythrough');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.embed.on('seeked', () => {
|
player.embed.on('seeked', () => {
|
||||||
this.media.seeking = false;
|
player.media.seeking = false;
|
||||||
utils.dispatchEvent.call(this, this.media, 'seeked');
|
utils.dispatchEvent.call(player, player.media, 'seeked');
|
||||||
utils.dispatchEvent.call(this, this.media, 'play');
|
utils.dispatchEvent.call(player, player.media, 'play');
|
||||||
});
|
});
|
||||||
|
|
||||||
this.embed.on('ended', () => {
|
player.embed.on('ended', () => {
|
||||||
this.media.paused = true;
|
player.media.paused = true;
|
||||||
utils.dispatchEvent.call(this, this.media, 'ended');
|
utils.dispatchEvent.call(player, player.media, 'ended');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Rebuild UI
|
// Rebuild UI
|
||||||
|
@ -17,6 +17,9 @@ const youtube = {
|
|||||||
// Add embed class for responsive
|
// Add embed class for responsive
|
||||||
utils.toggleClass(this.elements.wrapper, this.config.classNames.embed, true);
|
utils.toggleClass(this.elements.wrapper, this.config.classNames.embed, true);
|
||||||
|
|
||||||
|
// Set aspect ratio
|
||||||
|
youtube.setAspectRatio.call(this);
|
||||||
|
|
||||||
// Set ID
|
// Set ID
|
||||||
this.media.setAttribute('id', utils.generateId(this.type));
|
this.media.setAttribute('id', utils.generateId(this.type));
|
||||||
|
|
||||||
@ -44,6 +47,12 @@ const youtube = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Set aspect ratio
|
||||||
|
setAspectRatio() {
|
||||||
|
const ratio = this.config.ratio.split(':');
|
||||||
|
this.elements.wrapper.style.paddingBottom = `${100 / ratio[0] * ratio[1]}%`;
|
||||||
|
},
|
||||||
|
|
||||||
// API ready
|
// API ready
|
||||||
ready(videoId) {
|
ready(videoId) {
|
||||||
const player = this;
|
const player = this;
|
||||||
@ -66,9 +75,9 @@ const youtube = {
|
|||||||
origin: window && window.location.hostname,
|
origin: window && window.location.hostname,
|
||||||
widget_referrer: window && window.location.href,
|
widget_referrer: window && window.location.href,
|
||||||
|
|
||||||
// Captions is flaky on YouTube
|
// Captions are flaky on YouTube
|
||||||
// cc_load_policy: (this.captions.active ? 1 : 0),
|
cc_load_policy: (this.captions.active ? 1 : 0),
|
||||||
// cc_lang_pref: 'en',
|
cc_lang_pref: this.config.captions.language,
|
||||||
},
|
},
|
||||||
events: {
|
events: {
|
||||||
onError(event) {
|
onError(event) {
|
||||||
|
@ -13,6 +13,7 @@ import utils from './utils';
|
|||||||
import captions from './captions';
|
import captions from './captions';
|
||||||
import controls from './controls';
|
import controls from './controls';
|
||||||
import fullscreen from './fullscreen';
|
import fullscreen from './fullscreen';
|
||||||
|
import listeners from './listeners';
|
||||||
import media from './media';
|
import media from './media';
|
||||||
import storage from './storage';
|
import storage from './storage';
|
||||||
import source from './source';
|
import source from './source';
|
||||||
@ -78,7 +79,6 @@ class Plyr {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Captions
|
// Captions
|
||||||
// TODO: captions.enabled should be in config?
|
|
||||||
this.captions = {
|
this.captions = {
|
||||||
enabled: null,
|
enabled: null,
|
||||||
tracks: null,
|
tracks: null,
|
||||||
@ -192,10 +192,7 @@ class Plyr {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sniff out the browser
|
// Setup local storage for user settings
|
||||||
this.browser = utils.getBrowser();
|
|
||||||
|
|
||||||
// Load saved settings from localStorage
|
|
||||||
storage.setup.call(this);
|
storage.setup.call(this);
|
||||||
|
|
||||||
// Check for support again but with type
|
// Check for support again but with type
|
||||||
@ -217,6 +214,9 @@ class Plyr {
|
|||||||
// Allow focus to be captured
|
// Allow focus to be captured
|
||||||
this.elements.container.setAttribute('tabindex', 0);
|
this.elements.container.setAttribute('tabindex', 0);
|
||||||
|
|
||||||
|
// Global listeners
|
||||||
|
listeners.global.call(this);
|
||||||
|
|
||||||
// Add style hook
|
// Add style hook
|
||||||
ui.addStyleHook.call(this);
|
ui.addStyleHook.call(this);
|
||||||
|
|
||||||
@ -237,17 +237,27 @@ class Plyr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------
|
||||||
// API
|
// API
|
||||||
// ---------------------------------------
|
// ---------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the player is HTML5
|
||||||
|
*/
|
||||||
get isHTML5() {
|
get isHTML5() {
|
||||||
return types.html5.includes(this.type);
|
return types.html5.includes(this.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the player is an embed - e.g. YouTube or Vimeo
|
||||||
|
*/
|
||||||
get isEmbed() {
|
get isEmbed() {
|
||||||
return types.embed.includes(this.type);
|
return types.embed.includes(this.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Play
|
/**
|
||||||
|
* Play the media
|
||||||
|
*/
|
||||||
play() {
|
play() {
|
||||||
if ('play' in this.media) {
|
if ('play' in this.media) {
|
||||||
this.media.play();
|
this.media.play();
|
||||||
@ -257,7 +267,9 @@ class Plyr {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pause
|
/**
|
||||||
|
* Pause the media
|
||||||
|
*/
|
||||||
pause() {
|
pause() {
|
||||||
if ('pause' in this.media) {
|
if ('pause' in this.media) {
|
||||||
this.media.pause();
|
this.media.pause();
|
||||||
@ -267,7 +279,10 @@ class Plyr {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Toggle playback
|
/**
|
||||||
|
* Toggle playback based on current status
|
||||||
|
* @param {boolean} toggle
|
||||||
|
*/
|
||||||
togglePlay(toggle) {
|
togglePlay(toggle) {
|
||||||
// True toggle if nothing passed
|
// True toggle if nothing passed
|
||||||
if ((!utils.is.boolean(toggle) && this.media.paused) || toggle) {
|
if ((!utils.is.boolean(toggle) && this.media.paused) || toggle) {
|
||||||
@ -277,31 +292,43 @@ class Plyr {
|
|||||||
return this.pause();
|
return this.pause();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop
|
/**
|
||||||
|
* Stop playback
|
||||||
|
*/
|
||||||
stop() {
|
stop() {
|
||||||
return this.restart().pause();
|
return this.restart().pause();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restart
|
/**
|
||||||
|
* Restart playback
|
||||||
|
*/
|
||||||
restart() {
|
restart() {
|
||||||
this.currentTime = 0;
|
this.currentTime = 0;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rewind
|
/**
|
||||||
|
* Rewind
|
||||||
|
* @param {number} seekTime - how far to rewind in seconds. Defaults to the config.seekTime
|
||||||
|
*/
|
||||||
rewind(seekTime) {
|
rewind(seekTime) {
|
||||||
this.currentTime = this.currentTime - (utils.is.number(seekTime) ? seekTime : this.config.seekTime);
|
this.currentTime = this.currentTime - (utils.is.number(seekTime) ? seekTime : this.config.seekTime);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fast forward
|
/**
|
||||||
|
* Fast forward
|
||||||
|
* @param {number} seekTime - how far to fast forward in seconds. Defaults to the config.seekTime
|
||||||
|
*/
|
||||||
forward(seekTime) {
|
forward(seekTime) {
|
||||||
this.currentTime = this.currentTime + (utils.is.number(seekTime) ? seekTime : this.config.seekTime);
|
this.currentTime = this.currentTime + (utils.is.number(seekTime) ? seekTime : this.config.seekTime);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Seek to time
|
/**
|
||||||
// The input parameter can be an event or a number
|
* Seek to a time
|
||||||
|
* @param {number} input - where to seek to in seconds. Defaults to 0 (the start)
|
||||||
|
*/
|
||||||
set currentTime(input) {
|
set currentTime(input) {
|
||||||
let targetTime = 0;
|
let targetTime = 0;
|
||||||
|
|
||||||
@ -327,7 +354,9 @@ class Plyr {
|
|||||||
return Number(this.media.currentTime);
|
return Number(this.media.currentTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Duration
|
/**
|
||||||
|
* Get the duration of the current media
|
||||||
|
*/
|
||||||
get duration() {
|
get duration() {
|
||||||
// Faux duration set via config
|
// Faux duration set via config
|
||||||
const fauxDuration = parseInt(this.config.duration, 10);
|
const fauxDuration = parseInt(this.config.duration, 10);
|
||||||
@ -339,7 +368,10 @@ class Plyr {
|
|||||||
return !Number.isNaN(fauxDuration) ? fauxDuration : realDuration;
|
return !Number.isNaN(fauxDuration) ? fauxDuration : realDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Volume
|
/**
|
||||||
|
* Set the player volume
|
||||||
|
* @param {number} value - must be between 0 and 1. Defaults to the value from local storage and config.volume if not set in storage
|
||||||
|
*/
|
||||||
set volume(value) {
|
set volume(value) {
|
||||||
let volume = value;
|
let volume = value;
|
||||||
const max = 1;
|
const max = 1;
|
||||||
@ -377,6 +409,9 @@ class Plyr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current player volume
|
||||||
|
*/
|
||||||
get volume() {
|
get volume() {
|
||||||
return this.media.volume;
|
return this.media.volume;
|
||||||
}
|
}
|
||||||
|
@ -84,15 +84,17 @@ const ui = {
|
|||||||
// Update the UI
|
// Update the UI
|
||||||
ui.checkPlaying.call(this);
|
ui.checkPlaying.call(this);
|
||||||
|
|
||||||
|
// Ready for API calls
|
||||||
this.ready = true;
|
this.ready = true;
|
||||||
|
|
||||||
// Ready event at end of execution stack
|
// Ready event at end of execution stack
|
||||||
utils.dispatchEvent.call(this, this.media, 'ready');
|
utils.dispatchEvent.call(this, this.media, 'ready');
|
||||||
|
|
||||||
// Autoplay
|
// Autoplay
|
||||||
if (this.config.autoplay) {
|
// TODO: check we still need this?
|
||||||
|
/* if (this.isEmbed && this.config.autoplay) {
|
||||||
this.play();
|
this.play();
|
||||||
}
|
} */
|
||||||
},
|
},
|
||||||
|
|
||||||
// Show the duration on metadataloaded
|
// Show the duration on metadataloaded
|
||||||
|
137
src/js/utils.js
137
src/js/utils.js
@ -89,6 +89,73 @@ const utils = {
|
|||||||
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
|
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Load an external SVG sprite
|
||||||
|
loadSprite(url, id) {
|
||||||
|
if (typeof url !== 'string') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const prefix = 'cache-';
|
||||||
|
const hasId = typeof id === 'string';
|
||||||
|
let isCached = false;
|
||||||
|
|
||||||
|
function updateSprite(data) {
|
||||||
|
// Inject content
|
||||||
|
this.innerHTML = data;
|
||||||
|
|
||||||
|
// Inject the SVG to the body
|
||||||
|
document.body.insertBefore(this, document.body.childNodes[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only load once
|
||||||
|
if (!hasId || !document.querySelectorAll(`#${id}`).length) {
|
||||||
|
// Create container
|
||||||
|
const container = document.createElement('div');
|
||||||
|
container.setAttribute('hidden', '');
|
||||||
|
|
||||||
|
if (hasId) {
|
||||||
|
container.setAttribute('id', id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check in cache
|
||||||
|
if (support.storage) {
|
||||||
|
const cached = window.localStorage.getItem(prefix + id);
|
||||||
|
isCached = cached !== null;
|
||||||
|
|
||||||
|
if (isCached) {
|
||||||
|
const data = JSON.parse(cached);
|
||||||
|
updateSprite.call(container, data.content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReSharper disable once InconsistentNaming
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
|
||||||
|
// XHR for Chrome/Firefox/Opera/Safari
|
||||||
|
if ('withCredentials' in xhr) {
|
||||||
|
xhr.open('GET', url, true);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Once loaded, inject to container and body
|
||||||
|
xhr.onload = () => {
|
||||||
|
if (support.storage) {
|
||||||
|
window.localStorage.setItem(
|
||||||
|
prefix + id,
|
||||||
|
JSON.stringify({
|
||||||
|
content: xhr.responseText,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSprite.call(container, xhr.responseText);
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.send();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// Generate a random ID
|
// Generate a random ID
|
||||||
generateId(prefix) {
|
generateId(prefix) {
|
||||||
return `${prefix}-${Math.floor(Math.random() * 10000)}`;
|
return `${prefix}-${Math.floor(Math.random() * 10000)}`;
|
||||||
@ -564,71 +631,11 @@ const utils = {
|
|||||||
return fragment.firstChild.innerText;
|
return fragment.firstChild.innerText;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Load an SVG sprite
|
// Get aspect ratio for dimensions
|
||||||
loadSprite(url, id) {
|
getAspectRatio(width, height) {
|
||||||
if (typeof url !== 'string') {
|
const getRatio = (w, h) => (h === 0 ? w : getRatio(h, w % h));
|
||||||
return;
|
const ratio = getRatio(width, height);
|
||||||
}
|
return `${width / ratio}:${height / ratio}`;
|
||||||
|
|
||||||
const prefix = 'cache-';
|
|
||||||
const hasId = typeof id === 'string';
|
|
||||||
let isCached = false;
|
|
||||||
|
|
||||||
function updateSprite(data) {
|
|
||||||
// Inject content
|
|
||||||
this.innerHTML = data;
|
|
||||||
|
|
||||||
// Inject the SVG to the body
|
|
||||||
document.body.insertBefore(this, document.body.childNodes[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only load once
|
|
||||||
if (!hasId || !document.querySelectorAll(`#${id}`).length) {
|
|
||||||
// Create container
|
|
||||||
const container = document.createElement('div');
|
|
||||||
container.setAttribute('hidden', '');
|
|
||||||
|
|
||||||
if (hasId) {
|
|
||||||
container.setAttribute('id', id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check in cache
|
|
||||||
if (support.storage) {
|
|
||||||
const cached = window.localStorage.getItem(prefix + id);
|
|
||||||
isCached = cached !== null;
|
|
||||||
|
|
||||||
if (isCached) {
|
|
||||||
const data = JSON.parse(cached);
|
|
||||||
updateSprite.call(container, data.content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReSharper disable once InconsistentNaming
|
|
||||||
const xhr = new XMLHttpRequest();
|
|
||||||
|
|
||||||
// XHR for Chrome/Firefox/Opera/Safari
|
|
||||||
if ('withCredentials' in xhr) {
|
|
||||||
xhr.open('GET', url, true);
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Once loaded, inject to container and body
|
|
||||||
xhr.onload = () => {
|
|
||||||
if (support.storage) {
|
|
||||||
window.localStorage.setItem(
|
|
||||||
prefix + id,
|
|
||||||
JSON.stringify({
|
|
||||||
content: xhr.responseText,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateSprite.call(container, xhr.responseText);
|
|
||||||
};
|
|
||||||
|
|
||||||
xhr.send();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Get the transition end event
|
// Get the transition end event
|
||||||
|
@ -4,7 +4,11 @@
|
|||||||
// --------------------------------------------------------------
|
// --------------------------------------------------------------
|
||||||
|
|
||||||
.plyr__video-embed {
|
.plyr__video-embed {
|
||||||
padding-bottom: 56.25%; /* 16:9 */
|
// Default to 16:9 ratio but this is set by JavaScript based on config
|
||||||
|
@padding: ((100 / 16) * 9);
|
||||||
|
@offset: unit((300 - @padding) / 6, ~'%');
|
||||||
|
|
||||||
|
padding-bottom: unit(@padding, ~'%');
|
||||||
height: 0;
|
height: 0;
|
||||||
|
|
||||||
iframe {
|
iframe {
|
||||||
@ -20,8 +24,8 @@
|
|||||||
// Vimeo hack
|
// Vimeo hack
|
||||||
> div {
|
> div {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-bottom: 200%;
|
padding-bottom: 300%;
|
||||||
transform: translateY(-35.95%);
|
transform: translateY(-@offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// To allow mouse events to be captured if full support
|
// To allow mouse events to be captured if full support
|
||||||
|
Loading…
x
Reference in New Issue
Block a user