Refactoring and bug fixing

This commit is contained in:
Sam Potts 2016-08-20 18:02:02 +10:00
parent fc45ab48c9
commit 9d109bf02d
10 changed files with 382 additions and 305 deletions

2
demo/dist/demo.css vendored

File diff suppressed because one or more lines are too long

2
demo/dist/demo.js vendored
View File

@ -1 +1 @@
"document"in self&&("classList"in document.createElement("_")?!function(){"use strict";var e=document.createElement("_");if(e.classList.add("c1","c2"),!e.classList.contains("c2")){var t=function(e){var t=DOMTokenList.prototype[e];DOMTokenList.prototype[e]=function(e){var i,o=arguments.length;for(i=0;o>i;i++)e=arguments[i],t.call(this,e)}};t("add"),t("remove")}if(e.classList.toggle("c3",!1),e.classList.contains("c3")){var i=DOMTokenList.prototype.toggle;DOMTokenList.prototype.toggle=function(e,t){return 1 in arguments&&!this.contains(e)==!t?t:i.call(this,e)}}e=null}():!function(e){"use strict";if("Element"in e){var t="classList",i="prototype",o=e.Element[i],n=Object,s=String[i].trim||function(){return this.replace(/^\s+|\s+$/g,"")},r=Array[i].indexOf||function(e){for(var t=0,i=this.length;i>t;t++)if(t in this&&this[t]===e)return t;return-1},a=function(e,t){this.name=e,this.code=DOMException[e],this.message=t},c=function(e,t){if(""===t)throw new a("SYNTAX_ERR","An invalid or illegal string was specified");if(/\s/.test(t))throw new a("INVALID_CHARACTER_ERR","String contains an invalid character");return r.call(e,t)},l=function(e){for(var t=s.call(e.getAttribute("class")||""),i=t?t.split(/\s+/):[],o=0,n=i.length;n>o;o++)this.push(i[o]);this._updateClassName=function(){e.setAttribute("class",this.toString())}},u=l[i]=[],d=function(){return new l(this)};if(a[i]=Error[i],u.item=function(e){return this[e]||null},u.contains=function(e){return e+="",-1!==c(this,e)},u.add=function(){var e,t=arguments,i=0,o=t.length,n=!1;do e=t[i]+"",-1===c(this,e)&&(this.push(e),n=!0);while(++i<o);n&&this._updateClassName()},u.remove=function(){var e,t,i=arguments,o=0,n=i.length,s=!1;do for(e=i[o]+"",t=c(this,e);-1!==t;)this.splice(t,1),s=!0,t=c(this,e);while(++o<n);s&&this._updateClassName()},u.toggle=function(e,t){e+="";var i=this.contains(e),o=i?t!==!0&&"remove":t!==!1&&"add";return o&&this[o](e),t===!0||t===!1?t:!i},u.toString=function(){return this.join(" ")},n.defineProperty){var p={get:d,enumerable:!0,configurable:!0};try{n.defineProperty(o,t,p)}catch(h){-2146823252===h.number&&(p.enumerable=!1,n.defineProperty(o,t,p))}}else n[i].__defineGetter__&&o.__defineGetter__(t,d)}}(self)),function(){function e(e,t,i){if(e)if(e.classList)e.classList[i?"add":"remove"](t);else{var o=(" "+e.className+" ").replace(/\s+/g," ").replace(" "+t+" ","");e.className=o+(i?" "+t:"")}}function t(t,i){if(t in s&&(i||t!=r)&&(r.length||t!=s.video)){switch(t){case s.video:o.source({type:"video",title:"View From A Blue Moon",sources:[{src:"https://cdn.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.mp4",type:"video/mp4"},{src:"https://cdn.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.webm",type:"video/webm"}],poster:"https://cdn.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.jpg",tracks:[{kind:"captions",label:"English",srclang:"en",src:"https://cdn.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.en.vtt","default":!0}]});break;case s.audio:o.source({type:"audio",title:"Kishi Bashi &ndash; &ldquo;It All Began With A Burst&rdquo;",sources:[{src:"https://cdn.selz.com/plyr/1.5/Kishi_Bashi_-_It_All_Began_With_a_Burst.mp3",type:"audio/mp3"},{src:"https://cdn.selz.com/plyr/1.5/Kishi_Bashi_-_It_All_Began_With_a_Burst.ogg",type:"audio/ogg"}]});break;case s.youtube:o.source({type:"video",title:"View From A Blue Moon",sources:[{src:"bTqVqk7FSmY",type:"youtube"}]});break;case s.vimeo:o.source({type:"video",title:"View From A Blue Moon",sources:[{src:"143418951",type:"vimeo"}]})}r=t;for(var a=n.length-1;a>=0;a--)e(n[a].parentElement,"active",!1);e(document.querySelector('[data-source="'+t+'"]').parentElement,"active",!0)}}document.body.addEventListener("ready",function(e){console.log(e)});var i=plyr.setup({debug:!0,title:"Video demo",iconUrl:"../dist/plyr.svg",tooltips:{controls:!0},captions:{defaultActive:!0}});plyr.loadSprite("dist/demo.svg");for(var o=i[0].plyr,n=document.querySelectorAll("[data-source]"),s={video:"video",audio:"audio",youtube:"youtube",vimeo:"vimeo"},r=window.location.hash.replace("#",""),a=window.history&&window.history.pushState,c=n.length-1;c>=0;c--)n[c].addEventListener("click",function(){var e=this.getAttribute("data-source");t(e),a&&history.pushState({type:e},"","#"+e)});if(window.addEventListener("popstate",function(e){e.state&&"type"in e.state&&t(e.state.type)}),a){var l=!r.length;l&&(r=s.video),r in s&&history.replaceState({type:r},"",l?"":"#"+r),r!==s.video&&t(r,!0)}}(),document.domain.indexOf("plyr.io")>-1&&(!function(e,t,i,o,n,s,r){e.GoogleAnalyticsObject=n,e[n]=e[n]||function(){(e[n].q=e[n].q||[]).push(arguments)},e[n].l=1*new Date,s=t.createElement(i),r=t.getElementsByTagName(i)[0],s.async=1,s.src=o,r.parentNode.insertBefore(s,r)}(window,document,"script","//www.google-analytics.com/analytics.js","ga"),ga("create","UA-40881672-11","auto"),ga("send","pageview"));
"document"in self&&("classList"in document.createElement("_")?!function(){"use strict";var e=document.createElement("_");if(e.classList.add("c1","c2"),!e.classList.contains("c2")){var t=function(e){var t=DOMTokenList.prototype[e];DOMTokenList.prototype[e]=function(e){var i,s=arguments.length;for(i=0;s>i;i++)e=arguments[i],t.call(this,e)}};t("add"),t("remove")}if(e.classList.toggle("c3",!1),e.classList.contains("c3")){var i=DOMTokenList.prototype.toggle;DOMTokenList.prototype.toggle=function(e,t){return 1 in arguments&&!this.contains(e)==!t?t:i.call(this,e)}}e=null}():!function(e){"use strict";if("Element"in e){var t="classList",i="prototype",s=e.Element[i],o=Object,n=String[i].trim||function(){return this.replace(/^\s+|\s+$/g,"")},r=Array[i].indexOf||function(e){for(var t=0,i=this.length;i>t;t++)if(t in this&&this[t]===e)return t;return-1},a=function(e,t){this.name=e,this.code=DOMException[e],this.message=t},c=function(e,t){if(""===t)throw new a("SYNTAX_ERR","An invalid or illegal string was specified");if(/\s/.test(t))throw new a("INVALID_CHARACTER_ERR","String contains an invalid character");return r.call(e,t)},l=function(e){for(var t=n.call(e.getAttribute("class")||""),i=t?t.split(/\s+/):[],s=0,o=i.length;o>s;s++)this.push(i[s]);this._updateClassName=function(){e.setAttribute("class",this.toString())}},u=l[i]=[],d=function(){return new l(this)};if(a[i]=Error[i],u.item=function(e){return this[e]||null},u.contains=function(e){return e+="",-1!==c(this,e)},u.add=function(){var e,t=arguments,i=0,s=t.length,o=!1;do e=t[i]+"",-1===c(this,e)&&(this.push(e),o=!0);while(++i<s);o&&this._updateClassName()},u.remove=function(){var e,t,i=arguments,s=0,o=i.length,n=!1;do for(e=i[s]+"",t=c(this,e);-1!==t;)this.splice(t,1),n=!0,t=c(this,e);while(++s<o);n&&this._updateClassName()},u.toggle=function(e,t){e+="";var i=this.contains(e),s=i?t!==!0&&"remove":t!==!1&&"add";return s&&this[s](e),t===!0||t===!1?t:!i},u.toString=function(){return this.join(" ")},o.defineProperty){var p={get:d,enumerable:!0,configurable:!0};try{o.defineProperty(s,t,p)}catch(h){-2146823252===h.number&&(p.enumerable=!1,o.defineProperty(s,t,p))}}else o[i].__defineGetter__&&s.__defineGetter__(t,d)}}(self)),function(){function e(e,t,i){if(e)if(e.classList)e.classList[i?"add":"remove"](t);else{var s=(" "+e.className+" ").replace(/\s+/g," ").replace(" "+t+" ","");e.className=s+(i?" "+t:"")}}function t(t,i){if(t in n&&(i||t!=r)&&(r.length||t!=n.video)){switch(t){case n.video:s.source({type:"video",title:"View From A Blue Moon",sources:[{src:"https://cdn.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.mp4",type:"video/mp4"},{src:"https://cdn.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.webm",type:"video/webm"}],poster:"https://cdn.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.jpg",tracks:[{kind:"captions",label:"English",srclang:"en",src:"https://cdn.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.en.vtt","default":!0}]});break;case n.audio:s.source({type:"audio",title:"Kishi Bashi &ndash; &ldquo;It All Began With A Burst&rdquo;",sources:[{src:"https://cdn.selz.com/plyr/1.5/Kishi_Bashi_-_It_All_Began_With_a_Burst.mp3",type:"audio/mp3"},{src:"https://cdn.selz.com/plyr/1.5/Kishi_Bashi_-_It_All_Began_With_a_Burst.ogg",type:"audio/ogg"}]});break;case n.youtube:s.source({type:"video",title:"View From A Blue Moon",sources:[{src:"bTqVqk7FSmY",type:"youtube"}]});break;case n.vimeo:s.source({type:"video",title:"View From A Blue Moon",sources:[{src:"143418951",type:"vimeo"}]})}r=t;for(var a=o.length-1;a>=0;a--)e(o[a].parentElement,"active",!1);e(document.querySelector('[data-source="'+t+'"]').parentElement,"active",!0)}}var i=plyr.setup({debug:!0,title:"Video demo",iconUrl:"../dist/plyr.svg",tooltips:{controls:!0},captions:{defaultActive:!0}});plyr.loadSprite("dist/demo.svg");for(var s=i[0],o=document.querySelectorAll("[data-source]"),n={video:"video",audio:"audio",youtube:"youtube",vimeo:"vimeo"},r=window.location.hash.replace("#",""),a=window.history&&window.history.pushState,c=o.length-1;c>=0;c--)o[c].addEventListener("click",function(){var e=this.getAttribute("data-source");t(e),a&&history.pushState({type:e},"","#"+e)});if(window.addEventListener("popstate",function(e){e.state&&"type"in e.state&&t(e.state.type)}),a){var l=!r.length;l&&(r=n.video),r in n&&history.replaceState({type:r},"",l?"":"#"+r),r!==n.video&&t(r,!0)}}(),document.domain.indexOf("plyr.io")>-1&&(!function(e,t,i,s,o,n,r){e.GoogleAnalyticsObject=o,e[o]=e[o]||function(){(e[o].q=e[o].q||[]).push(arguments)},e[o].l=1*new Date,n=t.createElement(i),r=t.getElementsByTagName(i)[0],n.async=1,n.src=s,r.parentNode.insertBefore(n,r)}(window,document,"script","//www.google-analytics.com/analytics.js","ga"),ga("create","UA-40881672-11","auto"),ga("send","pageview"));

View File

@ -73,7 +73,8 @@
</main>
<!-- Plyr core script -->
<script src="../dist/plyr.js"></script>
<!--<script src="../dist/plyr.js"></script>-->
<script src="../src/js/plyr.js"></script>
<!-- Docs script -->
<script src="dist/demo.js"></script>

View File

@ -8,7 +8,7 @@
// General functions
;(function() {
document.body.addEventListener('ready', function(event) { console.log(event); });
//document.body.addEventListener('ready', function(event) { console.log(event); });
// Setup the player
var instances = plyr.setup({
@ -25,7 +25,7 @@
plyr.loadSprite('dist/demo.svg');
// Plyr returns an array regardless
var player = instances[0].plyr;
var player = instances[0];
// Setup type toggle
var buttons = document.querySelectorAll('[data-source]'),

View File

@ -39,9 +39,10 @@ header {
// Sections
section {
padding-bottom: @padding-base;
max-width: @example-width-video;
margin: 0 auto @padding-base;
@media (min-width: @screen-sm) {
padding-bottom: (@padding-base * 2);
margin-bottom: (@padding-base * 2);
}
}

View File

@ -2,11 +2,6 @@
// Examples
// ==========================================================================
section {
margin: 0 auto @padding-base;
max-width: @example-width-video;
}
// For non supported browsers
video {
max-width: 100%;

4
dist/plyr.js vendored

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
{
"name": "plyr",
"version": "1.8.12",
"version": "1.9.0",
"description": "A simple, accessible and customizable HTML5, YouTube and Vimeo media player",
"homepage": "http://plyr.io",
"main": "src/js/plyr.js",

View File

@ -541,11 +541,6 @@ Here's a list of the methods supported:
<td>&mdash;</td>
<td>Destroys the plyr UI and any media event listeners, effectively restoring to the previous state before <code>setup()</code> was called.</td>
</tr>
<tr>
<td><code>restore()</code></td>
<td>&mdash;</td>
<td>Reverses the effects of the <code>destroy()</code> method, restoring the UI and listeners.</td>
</tr>
<tr>
<td><code>getCurrentTime()</code></td>
<td>&mdash;</td>

View File

@ -35,7 +35,7 @@
autoplay: false,
loop: false,
seekTime: 10,
volume: 5,
volume: 10,
volumeMin: 0,
volumeMax: 10,
volumeStep: 1,
@ -86,6 +86,8 @@
duration: '.plyr__time--duration'
},
classes: {
setup: 'plyr--setup',
ready: 'plyr--ready',
videoWrapper: 'plyr__video-wrapper',
embedWrapper: 'plyr__video-embed',
type: 'plyr--{0}',
@ -168,7 +170,9 @@
fullscreen: null
},
// Events to watch on HTML5 media elements
events: ['ended', 'progress', 'stalled', 'playing', 'waiting', 'canplay', 'canplaythrough', 'loadstart', 'loadeddata', 'loadedmetadata', 'timeupdate', 'volumechange', 'play', 'pause', 'error', 'seeking', 'emptied']
events: ['ended', 'progress', 'stalled', 'playing', 'waiting', 'canplay', 'canplaythrough', 'loadstart', 'loadeddata', 'loadedmetadata', 'timeupdate', 'volumechange', 'play', 'pause', 'error', 'seeking', 'emptied'],
// Logging
logPrefix: '[Plyr]'
};
// Credits: http://paypal.github.io/accessible-html5-video-player/
@ -346,7 +350,7 @@
// Unwrap an element
// http://plainjs.com/javascript/manipulation/unwrap-a-dom-element-35/
function _unwrap(wrapper) {
/*function _unwrap(wrapper) {
// Get the element's parent node
var parent = wrapper.parentNode;
@ -357,7 +361,7 @@
// Remove the empty element
parent.removeChild(wrapper);
}
}*/
// Remove an element
function _remove(element) {
@ -575,25 +579,28 @@
return input !== null && typeof(input) === 'object';
},
array: function(input) {
return input !== null && typeof(input) === 'object' && input.constructor === Array;
return input !== null && (typeof(input) === 'object' && input.constructor === Array);
},
number: function(input) {
return typeof(input) === 'number' && !isNaN(input - 0) || (typeof input == 'object' && input.constructor === Number);
return input !== null && (typeof(input) === 'number' && !isNaN(input - 0) || (typeof input == 'object' && input.constructor === Number));
},
string: function(input) {
return typeof input === 'string' || (typeof input == 'object' && input.constructor === String);
return input !== null && (typeof input === 'string' || (typeof input == 'object' && input.constructor === String));
},
boolean: function(input) {
return typeof input === 'boolean';
return input !== null && typeof input === 'boolean';
},
nodeList: function(input) {
return input instanceof NodeList;
return input !== null && input instanceof NodeList;
},
htmlElement: function(input) {
return input instanceof HTMLElement;
return input !== null && input instanceof HTMLElement;
},
function: function(input) {
return input !== null && typeof input === 'function';
},
undefined: function(input) {
return typeof input === 'undefined';
return input !== null && typeof input === 'undefined';
}
};
@ -702,25 +709,31 @@
}
// Player instance
function Plyr(container, config) {
var plyr = this;
plyr.container = container;
plyr.timers = {};
function Plyr(media, config) {
var plyr = this,
timers = {};
// Log config options
_log(config);
// Set media
plyr.media = media;
var original = media.cloneNode(true);
// Debugging
function _log() {
function _console(type, args) {
if (config.debug && window.console) {
console.log.apply(console, arguments);
}
}
function _warn() {
if (config.debug && window.console) {
console.warn.apply(console, arguments);
args = Array.prototype.slice.call(args);
if (_is.string(config.logPrefix) && config.logPrefix.length) {
args.unshift(config.logPrefix);
}
console[type].apply(console, args);
}
}
var _log = function() { _console('log', arguments) },
_warn = function() { _console('warn', arguments) };
// Log config options
_log('Config', config);
// Get icon URL
function _getIconUrl() {
@ -1048,7 +1061,7 @@
_log('Successfully loaded the caption file via AJAX');
}
else {
_warn('There was a problem loading the caption file via AJAX');
_warn(config.logPrefix + 'There was a problem loading the caption file via AJAX');
}
}
};
@ -1076,7 +1089,7 @@
}
// Set the span content
if (_is.undefined(caption)) {
if (_is.string(caption)) {
content.innerHTML = caption.trim();
}
else {
@ -1279,24 +1292,20 @@
html = _replaceAll(html, '{id}', Math.floor(Math.random() * (10000)));
// Controls container
var container;
var target;
// Inject to custom location
if (config.selectors.controls.container !== null) {
container = config.selectors.controls.container;
if (_is.string(container)) {
container = document.querySelector(container);
}
if (_is.string(config.selectors.controls.container)) {
target = document.querySelector(config.selectors.controls.container);
}
// Inject into the container by default
if (!_is.htmlElement(container)) {
container = plyr.container
if (!_is.htmlElement(target)) {
target = plyr.container
}
// Inject controls HTML
container.insertAdjacentHTML('beforeend', html);
target.insertAdjacentHTML('beforeend', html);
// Setup tooltips
if (config.tooltips.controls) {
@ -1475,7 +1484,7 @@
_toggleClass(plyr.container, config.classes.stopped, config.autoplay);
// Add iOS class
_toggleClass(plyr.container, config.classes.isIos, plyr.browser.isIos);
_toggleClass(plyr.ontainer, config.classes.isIos, plyr.browser.isIos);
// Add touch class
_toggleClass(plyr.container, config.classes.isTouch, plyr.browser.isTouch);
@ -1497,9 +1506,6 @@
// Embeds
if (_inArray(config.types.embed, plyr.type)) {
_setupEmbed();
// Clean up
plyr.embedId = null;
}
}
@ -1611,9 +1617,6 @@
// When embeds are ready
function _embedReady() {
// Store reference to API
plyr.container.plyr.embed = plyr.embed;
// Setup the UI if full support
if (plyr.supported.full) {
_setupInterface();
@ -1625,12 +1628,6 @@
// Handle YouTube API ready
function _youTubeReady(videoId, container) {
// Setup timers object
// We have to poll YouTube for updates
if (!('timer' in plyr)) {
plyr.timer = {};
}
// Setup instance
// https://developers.google.com/youtube/iframe_api_reference
plyr.embed = new window.YT.Player(container.id, {
@ -1650,7 +1647,7 @@
},
events: {
'onError': function(event) {
_triggerEvent(plyr.container, 'error', true, {
_triggerEvent(container, 'error', true, {
code: event.data,
embed: event.target
});
@ -1680,32 +1677,37 @@
// Set title
config.title = instance.getVideoData().title;
// Update UI
_embedReady();
// Trigger timeupdate
_triggerEvent(plyr.media, 'timeupdate');
// Reset timer
window.clearInterval(plyr.timer.buffering);
window.clearInterval(timers.buffering);
// Setup buffering
plyr.timer.buffering = window.setInterval(function() {
timers.buffering = window.setInterval(function() {
// Get loaded % from YouTube
plyr.media.buffered = instance.getVideoLoadedFraction();
// Trigger progress
// Trigger progress only when we actually buffer something
if (plyr.media.lastBuffered === null || plyr.media.lastBuffered < plyr.media.buffered) {
_triggerEvent(plyr.media, 'progress');
}
// Set last buffer point
plyr.media.lastBuffered = plyr.media.buffered;
// Bail if we're at 100%
if (plyr.media.buffered === 1) {
window.clearInterval(plyr.timer.buffering);
window.clearInterval(timers.buffering);
// Trigger event
_triggerEvent(plyr.media, 'canplaythrough');
}
}, 200);
// Update UI
_embedReady();
// Display duration if available
_displayDuration();
},
@ -1714,7 +1716,7 @@
var instance = event.target;
// Reset timer
window.clearInterval(plyr.timer.playing);
window.clearInterval(timers.playing);
// Handle events
// -1 Unstarted
@ -1736,7 +1738,7 @@
_triggerEvent(plyr.media, 'playing');
// Poll to get playback progress
plyr.timer.playing = window.setInterval(function() {
timers.playing = window.setInterval(function() {
// Set the current time
plyr.media.currentTime = instance.getCurrentTime();
@ -1752,7 +1754,7 @@
break;
}
_triggerEvent(plyr.container, 'statechange', false, {
_triggerEvent(container, 'statechange', false, {
code: event.data
});
}
@ -1786,6 +1788,7 @@
plyr.embed.stop();
plyr.media.paused = true;
};
plyr.media.paused = true;
plyr.media.currentTime = 0;
@ -1811,12 +1814,14 @@
plyr.embed.enableTextTrack('en');
}*/
plyr.embed.on('loaded', function() {
// Fix keyboard focus issues
// https://github.com/Selz/plyr/issues/317
plyr.embed.on('loaded', function() {
if(_is.htmlElement(plyr.embed.element) && plyr.supported.full) {
plyr.embed.element.setAttribute('tabindex', '-1');
}
//console.log(plyr.embed);
});
plyr.embed.on('play', function() {
@ -1873,6 +1878,7 @@
plyr.embed.pause();
plyr.media.paused = true;
};
plyr.media.paused = true;
plyr.media.currentTime = 0;
@ -2333,10 +2339,10 @@
var loading = (event.type === 'waiting');
// Clear timer
clearTimeout(plyr.timers.loading);
clearTimeout(timers.loading);
// Timer to prevent flicker when seeking
plyr.timers.loading = setTimeout(function() {
timers.loading = setTimeout(function() {
_toggleClass(plyr.container, config.classes.loading, loading);
}, (loading ? 250 : 0));
}
@ -2566,7 +2572,8 @@
// Show the player controls in fullscreen mode
function _toggleControls(toggle) {
if (!config.hideControls || plyr.type === 'audio') {
// Don't hide if config says not to, it's audio, or not loaded/ready
if (!config.hideControls || plyr.type === 'audio' || !_hasClass(plyr.container, config.classes.ready)) {
return;
}
@ -2599,7 +2606,7 @@
}
// Clear timer every movement
window.clearTimeout(plyr.timers.hover);
window.clearTimeout(timers.hover);
// If the mouse is not over the controls, set a timeout to hide them
if (show || plyr.media.paused) {
@ -2619,7 +2626,7 @@
// If toggle is false or if we're playing (regardless of toggle),
// then set the timer to hide the controls
if (!show || !plyr.media.paused) {
plyr.timers.hover = window.setTimeout(function() {
timers.hover = window.setTimeout(function() {
// If the mouse is over the controls (and not entering fullscreen), bail
if ((plyr.controls.pressed || plyr.controls.hover) && !isEnterFullscreen) {
return;
@ -2685,27 +2692,28 @@
// Cancel current network requests
_cancelRequests();
// Clean up YouTube stuff
if (plyr.type === 'youtube') {
// Destroy the embed instance
plyr.embed.destroy();
// Clear timer
window.clearInterval(plyr.timer.buffering);
window.clearInterval(plyr.timer.playing);
}
// HTML5 Video
else if (plyr.type === 'video' && plyr.videoContainer) {
// Remove video wrapper
_remove(plyr.videoContainer);
}
// Destroy instance adn wait for callback
// Vimeo throws a wobbly if you don't wait
_destroy(setup, false);
// Setup new source
function setup() {
// Remove embed object
plyr.embed = null;
// Remove video container
if (plyr.type === 'video' && plyr.videoContainer) {
_remove(plyr.videoContainer);
}
// Remove the old media
_remove(plyr.media);
// Reset class name
if (plyr.container) {
plyr.container.removeAttribute('class');
}
// Set the type
if ('type' in source) {
plyr.type = source.type;
@ -2749,7 +2757,7 @@
config.autoplay = source.autoplay;
}
// Set attributes for audio video
// Set attributes for audio and video
if (_inArray(config.types.html5, plyr.type)) {
if (config.crossorigin) {
plyr.media.setAttribute('crossorigin', '');
@ -2765,9 +2773,6 @@
}
}
// Classname reset
plyr.container.className = plyr.originalClassName;
// Restore class hooks
_toggleClass(plyr.container, config.classes.fullscreen.active, plyr.isFullscreen);
_toggleClass(plyr.container, config.classes.captions.active, plyr.captionsEnabled);
@ -2805,9 +2810,7 @@
// Set aria title and iframe title
config.title = source.title;
_setTitle();
// Reset media objects
plyr.container.plyr.media = plyr.media;
}
}
// Update poster
@ -3106,49 +3109,79 @@
plyr.media.load();
// Debugging
_log("Cancelled network requests for old media");
_log('Cancelled network requests for old media');
}
// Destroy an instance
// Event listeners are removed when elements are removed
// http://stackoverflow.com/questions/12528049/if-a-dom-element-is-removed-are-its-listeners-also-removed-from-memory
function _destroy() {
function _destroy(callback, restore) {
// Bail if the element is not initialized
if (!plyr.init) {
return null;
}
// Reset container classname
plyr.container.setAttribute('class', _getClassname(config.selectors.container));
// Type specific stuff
switch (plyr.type) {
case 'youtube':
// Clear timers
window.clearInterval(timers.buffering);
window.clearInterval(timers.playing);
// Destroy YouTube API
plyr.embed.destroy();
// Clean up
cleanUp();
break;
case 'vimeo':
// Destroy Vimeo API
// then clean up (wait, to prevent postmessage errors)
plyr.embed.unload().then(cleanUp);
// Vimeo does not always return
window.setTimeout(cleanUp, 200);
break;
case 'video':
case 'audio':
// Restore native video controls
_toggleNativeControls(true);
// Clean up
cleanUp();
break;
}
function cleanUp() {
// Default to restore original element
if (!_is.boolean(restore)) {
restore = true;
}
// Callback
if (_is.function(callback)) {
callback.call(original);
}
// Bail if we don't need to restore the original element
if (!restore) {
return;
}
// Remove init flag
plyr.init = false;
// Remove controls
_remove(_getElement(config.selectors.controls.wrapper));
// Replace the container with the original element provided
plyr.container.parentNode.replaceChild(original, plyr.container);
// YouTube
if (plyr.type === 'youtube') {
plyr.embed.destroy();
return;
// Event
_triggerEvent(original, 'destroyed', true);
}
// If video, we need to remove some more
if (plyr.type === 'video') {
// Remove captions container
_remove(_getElement(config.selectors.captions));
// Remove video wrapper
_unwrap(plyr.videoContainer);
}
// Restore native video controls
_toggleNativeControls(true);
// Clone the media element to remove listeners
// http://stackoverflow.com/questions/19469881/javascript-remove-all-event-listeners-of-specific-type
var clone = plyr.media.cloneNode(true);
plyr.media.parentNode.replaceChild(clone, plyr.media);
}
// Setup a player
@ -3164,71 +3197,54 @@
// Sniff out the browser
plyr.browser = _browserSniff();
// Get the media element
plyr.media = plyr.container.querySelectorAll('audio, video')[0];
// Get the div placeholder for YouTube and Vimeo
if (!plyr.media) {
plyr.media = plyr.container.querySelectorAll('[data-type]')[0];
}
// Bail if nothing to setup
if (!plyr.media) {
if (!_is.htmlElement(plyr.media)) {
return;
}
// Load saved settings from localStorage
_setupStorage();
// Get original classname
plyr.originalClassName = plyr.container.className;
// Set media type based on tag or data attribute
// Supported: video, audio, vimeo, youtube
var tagName = plyr.media.tagName.toLowerCase();
var tagName = media.tagName.toLowerCase();
if (tagName === 'div') {
plyr.type = plyr.media.getAttribute('data-type');
plyr.embedId = plyr.media.getAttribute('data-video-id');
plyr.type = media.getAttribute('data-type');
plyr.embedId = media.getAttribute('data-video-id');
// Clean up
plyr.media.removeAttribute('data-type');
plyr.media.removeAttribute('data-video-id');
media.removeAttribute('data-type');
media.removeAttribute('data-video-id');
}
else {
plyr.type = tagName;
config.crossorigin = (plyr.media.getAttribute('crossorigin') !== null);
config.autoplay = (config.autoplay || (plyr.media.getAttribute('autoplay') !== null));
config.loop = (config.loop || (plyr.media.getAttribute('loop') !== null));
config.crossorigin = (media.getAttribute('crossorigin') !== null);
config.autoplay = (config.autoplay || (media.getAttribute('autoplay') !== null));
config.loop = (config.loop || (media.getAttribute('loop') !== null));
}
// Check for support
plyr.supported = supported(plyr.type);
// If no native support, bail
if (!plyr.supported.basic) {
return;
}
// Wrap media
plyr.container = _wrap(media, document.createElement('div'));
// Add style hook
_toggleStyleHook();
// If no native support, bail
if (!plyr.supported.basic) {
return false;
}
// Debug info
_log(plyr.browser.name + ' ' + plyr.browser.version);
_log('' + plyr.browser.name + ' ' + plyr.browser.version);
// Setup media
_setupMedia();
// Setup interface
if (_inArray(config.types.html5, plyr.type)) {
// Bail if no support
if (!plyr.supported.full) {
// Successful setup
plyr.init = true;
// Don't inject controls if no full support
return;
}
// Setup UI
_setupInterface();
@ -3252,7 +3268,7 @@
function _setupInterface() {
// Don't setup interface if no support
if (!plyr.supported.full) {
_warn('No full support for this media type (' + plyr.type + ')');
_warn('Basic support only', plyr.type);
// Remove controls
_remove(_getElement(config.selectors.controls.wrapper));
@ -3311,6 +3327,9 @@
// Ready event
_triggerEvent(plyr.container, 'ready', true);
// Class
_toggleClass(plyr.container, config.classes.ready, true);
}
// Initialize instance
@ -3322,9 +3341,15 @@
}
return {
media: plyr.media,
getContainer: function() { return plyr.container },
getEmbed: function() { return plyr.embed; },
getMedia: function() { return plyr.media; },
getType: function() { return plyr.type; },
isReady: function() { return _hasClass(plyr.container, config.classes.ready); },
on: function(event, callback) { _on(plyr.container, event, callback); },
play: _play,
pause: _pause,
stop: function() { _pause(); _seek(); },
restart: _seek,
rewind: _rewind,
forward: _forward,
@ -3340,8 +3365,7 @@
isFullscreen: function() { return plyr.isFullscreen || false; },
support: function(mimeType) { return _supportMime(plyr, mimeType); },
destroy: _destroy,
restore: _init,
getCurrentTime: function() { return plyr.media.currentTime; }
getCurrentTime: function() { return media.currentTime; }
};
}
@ -3350,10 +3374,18 @@
var x = new XMLHttpRequest();
// If the id is set and sprite exists, bail
if (_is.string(id) && document.querySelector('#' + id) !== null) {
if (_is.string(id) && _is.htmlElement(document.querySelector('#' + id))) {
return;
}
// Create placeholder (to prevent loading twice)
var c = document.createElement('div');
c.setAttribute('hidden', '');
if (_is.string(id)) {
c.setAttribute('id', id);
}
document.body.insertBefore(c, document.body.childNodes[0]);
// Check for CORS support
if ('withCredentials' in x) {
x.open('GET', url, true);
@ -3364,13 +3396,7 @@
// Inject hidden div with sprite on load
x.onload = function() {
var c = document.createElement('div');
c.setAttribute('hidden', '');
if (_is.string(id)) {
c.setAttribute('id', id);
}
c.innerHTML = x.responseText;
document.body.insertBefore(c, document.body.childNodes[0]);
}
x.send();
@ -3418,8 +3444,8 @@
// Setup function
function setup(targets, options) {
// Get the players
var elements = [],
containers = [],
var players = [],
instances = [],
selector = [defaults.selectors.html5, defaults.selectors.embed].join(',');
// Select the elements
@ -3453,6 +3479,20 @@
return false;
}
// Add to container list
function add(target, media) {
if (!_hasClass(media, defaults.classes.hook)) {
players.push({
// Always wrap in a <div> for styling
//container: _wrap(media, document.createElement('div')),
// Could be a container or the media itself
target: target,
// This should be the <video>, <audio> or <div> (YouTube/Vimeo)
media: media
});
}
}
// Check if the targets have multiple media elements
for (var i = 0; i < targets.length; i++) {
var target = targets[i];
@ -3460,38 +3500,38 @@
// Get children
var children = target.querySelectorAll(selector);
// If there's more than one media element, wrap them
if (children.length > 1) {
// If there's more than one media element child, wrap them
if (children.length) {
for (var x = 0; x < children.length; x++) {
containers.push({
element: _wrap(children[x], document.createElement('div')),
original: target
});
add(target, children[x]);
}
}
else {
containers.push({
element: target
});
// Wrap target if it's a media element
else if (_matches(target, selector)) {
add(target, target);
}
}
// Create a player instance for each element
for (var key in containers) {
var element = containers[key].element,
original = containers[key].original || element;
players.forEach(function(player) {
var element = player.target,
media = player.media,
match = false;
// Wrap each media element if is target is media element
// as opposed to a wrapper
if (_matches(element, selector)) {
// Wrap in a <div>
element = _wrap(element, document.createElement('div'));
// The target element can also be the media element
if (media === element) {
match = true;
}
// Setup a player instance and add to the element
if (!('plyr' in element)) {
// Create instance-specific config
var config = _extend({}, defaults, options, JSON.parse(original.getAttribute('data-plyr')));
var data = {};
// Try parsing data attribute config
try { data = JSON.parse(element.getAttribute('data-plyr')); }
catch(e) { }
var config = _extend({}, defaults, options, data);
// Bail if not enabled
if (!config.enabled) {
@ -3499,28 +3539,73 @@
}
// Create new instance
var instance = new Plyr(element, config);
var instance = new Plyr(media, config);
// Go to next if setup failed
if (!_is.object(instance)) {
return;
}
// Set plyr to false if setup failed
element.plyr = (Object.keys(instance).length ? instance : false);
// Maybe we remove this and add a .get() function to get the instance
// If passed a media element or container?
media.plyr = instance;
// Callback
_triggerEvent(original, 'setup', true, {
plyr: element.plyr
// Set class hook
_toggleClass(media, defaults.classes.setup, true);
// Listen for events if debugging
if (config.debug) {
var events = config.events.concat(['setup', 'ready', 'statechange', 'enterfullscreen', 'exitfullscreen', 'captionsenabled', 'captionsdisabled']);
_on(instance.getContainer(), events.join(' '), function() {
console.log([config.logPrefix, 'event:', event.type].join(' '));
});
}
// Callback
_triggerEvent(instance.getContainer(), 'setup', true, {
plyr: instance
});
// Add to return array even if it's already setup
elements.push(element);
instances.push(instance);
});
return instances;
}
return elements;
// Get all instances within a provided container
function get(container) {
// Get selector if string passed
if (_is.string(container)) {
container = document.querySelector(container);
}
// Use body by default to get all on page
else if (_is.undefined(container)) {
container = document.body;
}
// If we have a HTML element
if (_is.htmlElement(container)) {
var elements = container.querySelectorAll('.' + defaults.classes.setup),
instances = [];
Array.prototype.slice.call(elements).forEach(function(element) {
instances.push(element.plyr);
});
return instances;
}
return [];
}
return {
setup: setup,
supported: supported,
loadSprite: loadSprite
loadSprite: loadSprite,
get: get
};
}));