diff options
author | Sam Potts <me@sampotts.me> | 2017-06-04 15:27:37 +1000 |
---|---|---|
committer | Sam Potts <me@sampotts.me> | 2017-06-04 15:27:37 +1000 |
commit | 7daa08c32f9a85512e4ff946fcbb572bf4bb1c7b (patch) | |
tree | f8598af3a2e2c03382a3bd0addee82a0338da2a9 | |
parent | aaab9ad0829d2fff895316797826d23f294ac966 (diff) | |
download | plyr-7daa08c32f9a85512e4ff946fcbb572bf4bb1c7b.tar.lz plyr-7daa08c32f9a85512e4ff946fcbb572bf4bb1c7b.tar.xz plyr-7daa08c32f9a85512e4ff946fcbb572bf4bb1c7b.zip |
Change to proper constructor + prototypes
-rw-r--r-- | demo/dist/demo.js | 2 | ||||
-rw-r--r-- | demo/src/js/main.js | 4 | ||||
-rw-r--r-- | dist/plyr.js | 4 | ||||
-rw-r--r-- | notes.md | 30 | ||||
-rw-r--r-- | src/js/plyr.js | 2485 |
5 files changed, 1343 insertions, 1182 deletions
diff --git a/demo/dist/demo.js b/demo/dist/demo.js index 322d73bc..bf26da52 100644 --- a/demo/dist/demo.js +++ b/demo/dist/demo.js @@ -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,n=arguments.length;for(i=0;i<n;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",n=e.Element[i],o=Object,r=String[i].trim||function(){return this.replace(/^\s+|\s+$/g,"")},s=Array[i].indexOf||function(e){for(var t=0,i=this.length;t<i;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 s.call(e,t)},l=function(e){for(var t=r.call(e.getAttribute("class")||""),i=t?t.split(/\s+/):[],n=0,o=i.length;n<o;n++)this.push(i[n]);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+="",c(this,e)!==-1},u.add=function(){var e,t=arguments,i=0,n=t.length,o=!1;do e=t[i]+"",c(this,e)===-1&&(this.push(e),o=!0);while(++i<n);o&&this._updateClassName()},u.remove=function(){var e,t,i=arguments,n=0,o=i.length,r=!1;do for(e=i[n]+"",t=c(this,e);t!==-1;)this.splice(t,1),r=!0,t=c(this,e);while(++n<o);r&&this._updateClassName()},u.toggle=function(e,t){e+="";var i=this.contains(e),n=i?t!==!0&&"remove":t!==!1&&"add";return n&&this[n](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(n,t,p)}catch(e){e.number===-2146823252&&(p.enumerable=!1,o.defineProperty(n,t,p))}}else o[i].__defineGetter__&&n.__defineGetter__(t,d)}}(self)),function(){window.loadSprite=function(e,t){function i(e,t){e.innerHTML=t,n.insertBefore(e,n.childNodes[0])}if("string"==typeof e){var n=document.body,o="cache-",r="string"==typeof t,s=!1,a=function(){if(!r)return!1;var e="___test";try{return localStorage.setItem(e,e),localStorage.removeItem(e),!0}catch(e){return!1}}();if(!r||0===document.querySelectorAll("#"+t).length){var c=document.createElement("div");if(c.setAttribute("hidden",""),r&&c.setAttribute("id",t),a){var l=localStorage.getItem(o+t);if(s=null!==l){var u=JSON.parse(l);i(c,u.content)}}var d=new XMLHttpRequest;if(!("withCredentials"in d))return;d.open("GET",e,!0),d.onload=function(){a&&localStorage.setItem(o+t,JSON.stringify({content:d.responseText})),i(c,d.responseText)},d.send()}}}}(),function(){function e(e,t,i){if(e)if(e.classList)e.classList[i?"add":"remove"](t);else{var n=(" "+e.className+" ").replace(/\s+/g," ").replace(" "+t+" ","");e.className=n+(i?" "+t:"")}}function t(t,s){if(t in o&&(s||t!==r)&&(r.length||t!==o.video)){switch(t){case o.video:i.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"}],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 o.audio:i.source({type:"audio",title:"Kishi Bashi – “It All Began With A Burst”",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 o.youtube:i.source({type:"video",title:"View From A Blue Moon",sources:[{src:"https://www.youtube.com/watch?v=bTqVqk7FSmY",type:"youtube"}]});break;case o.vimeo:i.source({type:"video",title:"View From A Blue Moon",sources:[{src:"https://vimeo.com/76979871",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)}}var i=new Plyr("#player",{debug:!0,title:"View From A Blue Moon",iconUrl:"../dist/plyr.svg",tooltips:{controls:!0},captions:{defaultActive:!0},controls:["play-large","play","progress","current-time","mute","volume","captions","settings","fullscreen","pip","airplay"]});window.loadSprite("dist/demo.svg","demo-sprite");var n=document.querySelectorAll("[data-source]"),o={video:"video",audio:"audio",youtube:"youtube",vimeo:"vimeo"},r=window.location.hash.replace("#",""),s=window.history&&window.history.pushState;if([].forEach.call(n,function(e){e.addEventListener("click",function(){var e=this.getAttribute("data-source");t(e),s&&history.pushState({type:e},"","#"+e)})}),window.addEventListener("popstate",function(e){e.state&&"type"in e.state&&t(e.state.type)}),s){var a=!r.length;a&&(r=o.video),r in o&&history.replaceState({type:r},"",a?"":"#"+r),r!==o.video&&t(r,!0)}}(),"plyr.io"===window.location.host&&(!function(e,t,i,n,o,r,s){e.GoogleAnalyticsObject=o,e[o]=e[o]||function(){(e[o].q=e[o].q||[]).push(arguments)},e[o].l=1*new Date,r=t.createElement(i),s=t.getElementsByTagName(i)[0],r.async=1,r.src=n,s.parentNode.insertBefore(r,s)}(window,document,"script","//www.google-analytics.com/analytics.js","ga"),window.ga("create","UA-40881672-11","auto"),window.ga("send","pageview"));
\ No newline at end of file +"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,n=arguments.length;for(i=0;i<n;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",n=e.Element[i],o=Object,r=String[i].trim||function(){return this.replace(/^\s+|\s+$/g,"")},s=Array[i].indexOf||function(e){for(var t=0,i=this.length;t<i;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 s.call(e,t)},l=function(e){for(var t=r.call(e.getAttribute("class")||""),i=t?t.split(/\s+/):[],n=0,o=i.length;n<o;n++)this.push(i[n]);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+="",c(this,e)!==-1},u.add=function(){var e,t=arguments,i=0,n=t.length,o=!1;do e=t[i]+"",c(this,e)===-1&&(this.push(e),o=!0);while(++i<n);o&&this._updateClassName()},u.remove=function(){var e,t,i=arguments,n=0,o=i.length,r=!1;do for(e=i[n]+"",t=c(this,e);t!==-1;)this.splice(t,1),r=!0,t=c(this,e);while(++n<o);r&&this._updateClassName()},u.toggle=function(e,t){e+="";var i=this.contains(e),n=i?t!==!0&&"remove":t!==!1&&"add";return n&&this[n](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(n,t,p)}catch(e){e.number===-2146823252&&(p.enumerable=!1,o.defineProperty(n,t,p))}}else o[i].__defineGetter__&&n.__defineGetter__(t,d)}}(self)),function(){window.loadSprite=function(e,t){function i(e,t){e.innerHTML=t,n.insertBefore(e,n.childNodes[0])}if("string"==typeof e){var n=document.body,o="cache-",r="string"==typeof t,s=!1,a=function(){if(!r)return!1;var e="___test";try{return localStorage.setItem(e,e),localStorage.removeItem(e),!0}catch(e){return!1}}();if(!r||0===document.querySelectorAll("#"+t).length){var c=document.createElement("div");if(c.setAttribute("hidden",""),r&&c.setAttribute("id",t),a){var l=localStorage.getItem(o+t);if(s=null!==l){var u=JSON.parse(l);i(c,u.content)}}var d=new XMLHttpRequest;if(!("withCredentials"in d))return;d.open("GET",e,!0),d.onload=function(){a&&localStorage.setItem(o+t,JSON.stringify({content:d.responseText})),i(c,d.responseText)},d.send()}}}}(),function(){function e(e,t,i){if(e)if(e.classList)e.classList[i?"add":"remove"](t);else{var n=(" "+e.className+" ").replace(/\s+/g," ").replace(" "+t+" ","");e.className=n+(i?" "+t:"")}}function t(t,s){if(t in o&&(s||t!==r)&&(r.length||t!==o.video)){switch(t){case o.video:i.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"}],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 o.audio:i.source({type:"audio",title:"Kishi Bashi – “It All Began With A Burst”",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 o.youtube:i.source({type:"video",title:"View From A Blue Moon",sources:[{src:"https://www.youtube.com/watch?v=bTqVqk7FSmY",type:"youtube"}]});break;case o.vimeo:i.source({type:"video",title:"View From A Blue Moon",sources:[{src:"https://vimeo.com/76979871",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)}}var i=new Plyr("#player",{debug:!0,title:"View From A Blue Moon",iconUrl:"../dist/plyr.svg",tooltips:{controls:!0},captions:{defaultActive:!0},controls:["play-large","play","progress","current-time","mute","volume","captions","settings","fullscreen","pip","airplay"]});window.player=i,window.loadSprite("dist/demo.svg","demo-sprite");var n=document.querySelectorAll("[data-source]"),o={video:"video",audio:"audio",youtube:"youtube",vimeo:"vimeo"},r=window.location.hash.replace("#",""),s=window.history&&window.history.pushState;if([].forEach.call(n,function(e){e.addEventListener("click",function(){var e=this.getAttribute("data-source");t(e),s&&history.pushState({type:e},"","#"+e)})}),window.addEventListener("popstate",function(e){e.state&&"type"in e.state&&t(e.state.type)}),s){var a=!r.length;a&&(r=o.video),r in o&&history.replaceState({type:r},"",a?"":"#"+r),r!==o.video&&t(r,!0)}}(),"plyr.io"===window.location.host&&(!function(e,t,i,n,o,r,s){e.GoogleAnalyticsObject=o,e[o]=e[o]||function(){(e[o].q=e[o].q||[]).push(arguments)},e[o].l=1*new Date,r=t.createElement(i),s=t.getElementsByTagName(i)[0],r.async=1,r.src=n,s.parentNode.insertBefore(r,s)}(window,document,"script","//www.google-analytics.com/analytics.js","ga"),window.ga("create","UA-40881672-11","auto"),window.ga("send","pageview"));
\ No newline at end of file diff --git a/demo/src/js/main.js b/demo/src/js/main.js index 87c71756..ee45d118 100644 --- a/demo/src/js/main.js +++ b/demo/src/js/main.js @@ -37,6 +37,10 @@ ] }); + // Expose for testing + window.player = player; + + // Load demo sprite window.loadSprite('dist/demo.svg', 'demo-sprite'); // Setup type toggle diff --git a/dist/plyr.js b/dist/plyr.js index 36a8f22c..16e19d21 100644 --- a/dist/plyr.js +++ b/dist/plyr.js @@ -1,2 +1,2 @@ -!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=t(e,document):"function"==typeof define&&define.amd?define([],function(){return t(e,document)}):e.Plyr=t(e,document)}("undefined"!=typeof window?window:this,function(e,t){"use strict";function n(n,u){function c(e,t,n,s){i.event(e,t,n,i.extend({},s,{plyr:De}))}function d(e){return je.elements.container.querySelectorAll(e)}function p(e){return d(e)[0]}function m(e){i.is.string(e)?(i.removeElement(je.elements[e]),je.elements[e]=null):i.removeElement(e)}function f(){function e(e){9===e.which&&je.fullscreen.active&&(e.target!==s||e.shiftKey?e.target===n&&e.shiftKey&&(e.preventDefault(),s.focus()):(e.preventDefault(),n.focus()))}var t=d("input:not([disabled]), button:not([disabled])"),n=t[0],s=t[t.length-1];i.on(je.elements.container,"keydown",e,!1)}function y(e,t){i.is.string(t)?i.insertElement(e,je.elements.media,{src:t}):i.is.array(t)&&t.forEach(function(t){i.insertElement(e,je.elements.media,t)})}function g(){return{url:Re.iconUrl,absolute:0===Re.iconUrl.indexOf("http")||je.browser.isIE}}function b(e,n){var s="http://www.w3.org/2000/svg",a=g(),l=(a.absolute?"":a.url)+"#"+Re.iconPrefix,r=t.createElementNS(s,"svg");i.setAttributes(r,i.extend(n,{role:"presentation"}));var o=t.createElementNS(s,"use");return o.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",l+"-"+e),r.appendChild(o),r}function v(e){var t=Re.i18n[e];switch(e){case"pip":t="PIP";break;case"airplay":t="AirPlay"}return i.createElement("span",{class:Re.classes.hidden},t)}function h(e){var t=i.createElement("span",{class:Re.classes.menu.value});return t.appendChild(i.createElement("span",{class:Re.classes.menu.badge},e)),t}function k(e,t){var n,s,a,l=i.createElement("button");switch(i.is.object(t)||(t={}),"class"in t?t.class.indexOf(Re.classes.control)===-1&&(t.class+=" "+Re.classes.control):t.class=Re.classes.control,e){case"mute":a="toggleMute",n="volume",s="muted";break;case"captions":a="toggleCaptions",n="captions-off",s="captions-on";break;case"fullscreen":a="toggleFullscreen",n="enter-fullscreen",s="exit-fullscreen";break;case"play-large":t.class="plyr__play-large",e="play",a="play",n="play";break;default:a=e,n=e}return i.extend(t,i.getAttributesFromSelector(Re.selectors.buttons[e],t)),i.is.string(s)&&l.appendChild(b(s,{class:"icon--"+s})),l.appendChild(b(n)),l.appendChild(v(a)),i.setAttributes(l,t),je.elements.buttons[e]=l,l}function C(e,t){var n=i.createElement("label",{for:t.id,class:Re.classes.hidden},Re.i18n[e]),s=i.createElement("input",i.extend(i.getAttributesFromSelector(Re.selectors.inputs[e]),{type:"range",min:0,max:100,step:.1,value:0,autocomplete:"off"},t));return je.elements.inputs[e]=s,{label:n,input:s}}function E(e,t){var n=i.createElement("progress",i.extend(i.getAttributesFromSelector(Re.selectors.display[e]),{min:0,max:100,value:0},t));if("volume"!==e){n.appendChild(i.createElement("span",null,"0"));var s="";switch(e){case"played":s=Re.i18n.played;break;case"buffer":s=Re.i18n.buffered}n.textContent="% "+s.toLowerCase()}return je.elements.display[e]=n,n}function A(e){var t=i.createElement("span",{class:"plyr__time"});return t.appendChild(i.createElement("span",{class:Re.classes.hidden},Re.i18n[e])),t.appendChild(i.createElement("span",i.getAttributesFromSelector(Re.selectors.display[e]),"00:00")),je.elements.display[e]=t,t}function w(e){var t=i.createElement("div",i.getAttributesFromSelector(Re.selectors.controls.wrapper));if(i.inArray(Re.controls,"restart")&&t.appendChild(k("restart")),i.inArray(Re.controls,"rewind")&&t.appendChild(k("rewind")),i.inArray(Re.controls,"play")&&(t.appendChild(k("play")),t.appendChild(k("pause"))),i.inArray(Re.controls,"fast-forward")&&t.appendChild(k("fast-forward")),i.inArray(Re.controls,"progress")){var n=i.createElement("span",i.getAttributesFromSelector(Re.selectors.progress)),s=C("seek",{id:"plyr-seek-"+e.id});if(n.appendChild(s.label),n.appendChild(s.input),n.appendChild(E("played")),n.appendChild(E("buffer")),Re.tooltips.seek){var a=i.createElement("span",{role:"tooltip",class:Re.classes.tooltip},"00:00");n.appendChild(a),je.elements.display.seekTooltip=a}je.elements.progress=n,t.appendChild(je.elements.progress)}if(i.inArray(Re.controls,"current-time")&&t.appendChild(A("currentTime")),i.inArray(Re.controls,"duration")&&t.appendChild(A("duration")),i.inArray(Re.controls,"mute")&&t.appendChild(k("mute")),i.inArray(Re.controls,"volume")){var l=i.createElement("span",{class:"plyr__volume"}),r={max:10,value:Re.volume},u=C("volume",i.extend(r,{id:"plyr-volume-"+e.id}));l.appendChild(u.label),l.appendChild(u.input);var c=E("volume",r);l.appendChild(c),t.appendChild(l)}if(i.inArray(Re.controls,"captions")&&t.appendChild(k("captions")),i.inArray(Re.controls,"settings")){var d=i.createElement("div",{class:"plyr__menu"});d.appendChild(k("settings",{id:"plyr-settings-toggle-"+e.id,"aria-haspopup":!0,"aria-controls":"plyr-settings-"+e.id,"aria-expanded":!1}));var p=i.createElement("form",{class:"plyr__menu__container",id:"plyr-settings-"+e.id,"aria-hidden":!0,"aria-labelled-by":"plyr-settings-toggle-"+e.id,role:"tablist",tabindex:-1}),m=i.createElement("div"),f=i.createElement("div",{id:"plyr-settings-"+e.id+"-home","aria-hidden":!1,"aria-labelled-by":"plyr-settings-toggle-"+e.id,role:"tabpanel"}),y=i.createElement("ul",{role:"tablist"});Re.settings.forEach(function(t){var n=i.createElement("li",{role:"tab",hidden:""}),s=i.createElement("button",i.extend(i.getAttributesFromSelector(Re.selectors.buttons.settings),{type:"button",class:Re.classes.control+" "+Re.classes.control+"--forward",id:"plyr-settings-"+e.id+"-"+t+"-tab","aria-haspopup":!0,"aria-controls":"plyr-settings-"+e.id+"-"+t,"aria-expanded":!1}),Re.i18n[t]),a=i.createElement("span",{class:Re.classes.menu.value});a.innerHTML=e[t],s.appendChild(a),n.appendChild(s),y.appendChild(n),je.elements.settings.tabs[t]=n}),f.appendChild(y),m.appendChild(f),Re.settings.forEach(function(t){var n=i.createElement("div",{id:"plyr-settings-"+e.id+"-"+t,"aria-hidden":!0,"aria-labelled-by":"plyr-settings-"+e.id+"-"+t+"-tab",role:"tabpanel",tabindex:-1,hidden:""}),s=i.createElement("button",{type:"button",class:Re.classes.control+" "+Re.classes.control+"--back","aria-haspopup":!0,"aria-controls":"plyr-settings-"+e.id+"-home","aria-expanded":!1},Re.i18n[t]);n.appendChild(s);var a=i.createElement("ul");n.appendChild(a),m.appendChild(n),je.elements.settings.panes[t]=n}),p.appendChild(m),d.appendChild(p),t.appendChild(d),je.elements.settings.form=p,je.elements.settings.menu=d}return i.inArray(Re.controls,"pip")&&o.pip&&t.appendChild(k("pip")),i.inArray(Re.controls,"airplay")&&o.airplay&&t.appendChild(k("airplay")),i.inArray(Re.controls,"fullscreen")&&t.appendChild(k("fullscreen")),je.elements.controls=t,S(),F(),t}function T(e,n){function s(e){var t="";switch(e){case"hd2160":t="4K";break;case"hd1440":t="WQHD";break;case"hd1080":t="HD";break;case"hd720":t="HD"}return t.length?h(t):null}function a(e){switch(e){case"hd2160":return"2160P";case"hd1440":return"1440P";case"hd1080":return"1080P";case"hd720":return"720P";case"large":return"480P";case"medium":return"360P";case"small":return"240P";default:return"Auto"}}var l=je.elements.settings.panes.quality.querySelector("ul");je.elements.settings.tabs.quality.removeAttribute("hidden"),je.elements.settings.panes.quality.removeAttribute("hidden"),i.emptyElement(l),i.is.array(e)&&!i.is.empty(e)&&e.filter(function(e){return!i.inArray(["tiny","small"],e)}).forEach(function(e){var n=i.createElement("li"),r=i.createElement("label",{class:Re.classes.control}),o=i.createElement("input",i.extend(i.getAttributesFromSelector(Re.selectors.inputs.quality),{type:"radio",name:"plyr-quality",value:e}));e===Re.quality.selected&&(o.setAttribute("checked",""),o.checked=!0),r.appendChild(o),r.appendChild(t.createTextNode(a(e)));var u=s(e);i.is.htmlElement(u)&&r.appendChild(u),n.appendChild(r),l.appendChild(n)})}function S(){var e=["start","end","all","reset"],t=je.elements.settings.panes.loop.querySelector("ul");je.elements.settings.tabs.loop.removeAttribute("hidden"),je.elements.settings.panes.loop.removeAttribute("hidden"),i.emptyElement(t),e.forEach(function(e){var n=i.createElement("li"),s=i.createElement("button",i.extend(i.getAttributesFromSelector(Re.selectors.buttons.loop),{type:"button",class:Re.classes.control,"data-plyr-loop-action":e}),Re.i18n[e]);if(i.inArray(["start","end"],e)){var a=h("00:00");s.appendChild(a)}n.appendChild(s),t.appendChild(n)})}function x(){var e=je.elements.settings.panes.captions.querySelector("ul");if(je.elements.settings.tabs.captions.removeAttribute("hidden"),je.elements.settings.panes.captions.removeAttribute("hidden"),i.emptyElement(e),!i.is.empty(je.captions.tracks)){var n=[].map.call(je.captions.tracks,function(e){return{language:e.language,badge:!0,label:i.is.empty(e.label)?e.language.toUpperCase():e.label}});n.unshift({language:"off",label:Re.i18n.none}),n.forEach(function(n){var s=i.createElement("li"),a=i.createElement("label",{class:Re.classes.control}),l=i.createElement("input",i.extend(i.getAttributesFromSelector(Re.selectors.inputs.language),{type:"radio",name:"plyr-language",value:n.language}));n.language.toLowerCase()===Re.captions.language.toLowerCase()&&(l.setAttribute("checked",""),l.checked=!0),a.appendChild(l),a.appendChild(t.createTextNode(n.label||n.language)),n.badge&&a.appendChild(h(n.language.toUpperCase())),s.appendChild(a),e.appendChild(s)})}}function F(e){var t=je.elements.settings.panes.speed.querySelector("ul");je.elements.settings.tabs.speed.removeAttribute("hidden"),je.elements.settings.panes.speed.removeAttribute("hidden"),i.emptyElement(t),i.is.array(e)||(e=Re.speed.options),e.forEach(function(e){var n=i.createElement("li"),s=i.createElement("label",{class:Re.classes.control}),a=i.createElement("input",i.extend(i.getAttributesFromSelector(Re.selectors.inputs.speed),{type:"radio",name:"plyr-speed",value:e}));e===Re.speed.selected&&(a.setAttribute("checked",""),a.checked=!0),s.appendChild(a),s.insertAdjacentHTML("beforeend",te(e)),n.appendChild(s),t.appendChild(n)})}function I(){if(je.supported.full&&("audio"!==je.type||Re.fullscreen.allowAudio)&&Re.fullscreen.enabled){var e=o.fullscreen;e||Re.fullscreen.fallback&&!i.inFrame()?(Be((e?"Native":"Fallback")+" fullscreen enabled"),i.toggleClass(je.elements.container,Re.classes.fullscreen.enabled,!0)):Be("Fullscreen not supported and fallback disabled"),je.elements.buttons&&je.elements.buttons.fullscreen&&i.toggleState(je.elements.buttons.fullscreen,!1),f()}}function P(e){if(i.inArray(["video","vimeo"],je.type)&&("video"!==je.type||o.textTracks)&&(i.is.htmlElement(je.elements.captions)||(je.elements.captions=i.createElement("div",i.getAttributesFromSelector(Re.selectors.captions)),i.insertAfter(je.elements.captions,je.elements.wrapper)),je.captions.tracks=i.is.array(e)?e:je.elements.media.textTracks,i.toggleClass(je.elements.container,Re.classes.captions.enabled,!i.is.empty(je.captions.tracks)),!i.is.empty(je.captions.tracks))){if(O(),"video"===je.type){var t=Re.captions.language.toLowerCase();[].forEach.call(je.captions.tracks,function(e){i.off(e,"cuechange",N),e.mode="hidden",e.language===t&&(je.captions.currentTrack=e)}),i.is.track(je.captions.currentTrack)||(He("No language found to match "+t+" in tracks"),je.captions.currentTrack=je.captions.tracks[0]);var n=je.captions.currentTrack;i.is.track(n)&&i.inArray(["captions","subtitles"],n.kind)&&(i.on(n,"cuechange",N),n.activeCues&&n.activeCues.length>0&&N(n))}x()}}function _(){return!o.textTracks||i.is.empty(je.captions.tracks)?"No Subs":je.captions.enabled?je.captions.currentTrack.label:"Disabled"}function N(e){i.is.event(e)&&(e=e.target);var t=e.activeCues[0];i.is.cue(t)?q(t.getCueAsHTML()):q()}function L(e){i.is.string(e)?Re.captions.language=e.toLowerCase():i.is.event(e)&&(Re.captions.language=e.target.value.toLowerCase()),q(),P()}function q(e){if(i.is.htmlElement(je.elements.captions)){var t=i.createElement("span");i.emptyElement(je.elements.captions),i.is.undefined(e)&&(e=""),i.is.string(e)?t.textContent=e.trim():t.appendChild(e),je.elements.captions.appendChild(t)}else He("No captions element to render to")}function O(){if(je.elements.buttons.captions){var e=je.storage.captions;i.is.boolean(e)?Re.captions.active=e:e=Re.captions.active,e&&(i.toggleClass(je.elements.container,Re.classes.captions.active,!0),i.toggleState(je.elements.buttons.captions,!0))}}function M(e){je.supported.full&&je.elements.buttons.captions&&(i.is.boolean(e)||(e=je.elements.container.className.indexOf(Re.classes.captions.active)===-1),je.captions.enabled=e,i.toggleState(je.elements.buttons.captions,je.captions.enabled),i.toggleClass(je.elements.container,Re.classes.captions.active,je.captions.enabled),c(je.elements.container,je.captions.enabled?"captionsenabled":"captionsdisabled",!0),W({captions:je.captions.enabled}))}function j(){if(Re.loadSprite){var e=g();e.absolute?(Be("AJAX loading absolute SVG sprite"+(je.browser.isIE?" (due to IE)":"")),i.loadSprite(e.url,"sprite-plyr")):Be("Sprite will be used as external resource directly")}i.inArray(Re.controls,"play-large")&&(je.elements.buttons.playLarge=k("play-large"),je.elements.container.appendChild(je.elements.buttons.playLarge)),je.id=Math.floor(1e4*Math.random());var n=null;n=i.is.string(Re.controls)?Re.controls:i.is.function(Re.controls)?Re.controls({id:je.id,seektime:Re.seekTime}):w({id:je.id,seektime:Re.seekTime,speed:te(),quality:"HD",captions:_(),loop:"None"});var s;if(i.is.string(Re.selectors.controls.container)&&(s=t.querySelector(Re.selectors.controls.container)),i.is.htmlElement(s)||(s=je.elements.container),i.is.htmlElement(n)?s.appendChild(n):s.insertAdjacentHTML("beforeend",n),i.is.htmlElement(je.elements.controls)&&V(),Re.tooltips.controls)for(var a=d([Re.selectors.controls.wrapper," ",Re.selectors.labels," .",Re.classes.hidden].join("")),l=a.length-1;l>=0;l--){var r=a[l];i.toggleClass(r,Re.classes.hidden,!1),i.toggleClass(r,Re.classes.tooltip,!0)}}function V(){try{return je.elements.controls=p(Re.selectors.controls.wrapper),je.elements.buttons={play:d(Re.selectors.buttons.play),pause:p(Re.selectors.buttons.pause),restart:p(Re.selectors.buttons.restart),rewind:p(Re.selectors.buttons.rewind),forward:p(Re.selectors.buttons.forward),mute:p(Re.selectors.buttons.mute),pip:p(Re.selectors.buttons.pip),airplay:p(Re.selectors.buttons.airplay),settings:p(Re.selectors.buttons.settings),captions:p(Re.selectors.buttons.captions),fullscreen:p(Re.selectors.buttons.fullscreen)},je.elements.progress=p(Re.selectors.progress),je.elements.inputs={seek:p(Re.selectors.inputs.seek),volume:p(Re.selectors.inputs.volume)},je.elements.display={buffer:p(Re.selectors.display.buffer),played:p(Re.selectors.display.played),volume:p(Re.selectors.display.volume),duration:p(Re.selectors.display.duration),currentTime:p(Re.selectors.display.currentTime)},i.is.htmlElement(je.elements.progress)&&(je.elements.display.seekTooltip=je.elements.progress.querySelector("."+Re.classes.tooltip)),!0}catch(e){return He("It looks like there is a problem with your custom controls HTML",e),R(!0),!1}}function D(){i.toggleClass(je.elements.container,Re.selectors.container.replace(".",""),je.supported.full)}function R(e){e&&i.inArray(l.html5,je.type)?je.elements.media.setAttribute("controls",""):je.elements.media.removeAttribute("controls")}function B(e){var t=Re.i18n.play;if(i.is.string(Re.title)&&!i.is.empty(Re.title)&&(t+=", "+Re.title,je.elements.container.setAttribute("aria-label",Re.title)),je.supported.full&&(i.is.htmlElement(je.elements.buttons.play)&&je.elements.buttons.play.setAttribute("aria-label",t),i.is.htmlElement(je.elements.buttons.playLarge)&&je.elements.buttons.playLarge.setAttribute("aria-label",t)),i.is.htmlElement(e)){var n=i.is.string(Re.title)&&!i.is.empty(Re.title)?Re.title:"video";e.setAttribute("title",Re.i18n.frameTitle.replace("{title}",n))}}function H(){var t=null;je.storage={},o.storage&&Re.storage.enabled&&(e.localStorage.removeItem("plyr-volume"),t=e.localStorage.getItem(Re.storage.key),t&&(/^\d+(\.\d+)?$/.test(t)?W({volume:parseFloat(t)}):je.storage=JSON.parse(t)))}function W(t){o.storage&&Re.storage.enabled&&(i.extend(je.storage,t),e.localStorage.setItem(Re.storage.key,JSON.stringify(je.storage)))}function Y(){return je.elements.media?(je.supported.full&&(i.toggleClass(je.elements.container,Re.classes.type.replace("{0}",je.type),!0),i.inArray(l.embed,je.type)&&i.toggleClass(je.elements.container,Re.classes.type.replace("{0}","video"),!0),i.toggleClass(je.elements.container,Re.classes.pip.enabled,o.pip&&"video"===je.type),i.toggleClass(je.elements.container,Re.classes.airplay.enabled,o.airplay&&i.inArray(l.html5,je.type)),i.toggleClass(je.elements.container,Re.classes.stopped,Re.autoplay),i.toggleClass(je.elements.container,Re.classes.isIos,je.browser.isIos),i.toggleClass(je.elements.container,Re.classes.isTouch,o.touch)),i.inArray(["video","youtube","vimeo"],je.type)&&(je.elements.wrapper=i.createElement("div",{class:Re.classes.videoWrapper}),i.wrap(je.elements.media,je.elements.wrapper)),void(i.inArray(l.embed,je.type)&&U())):void He("No media element found!")}function U(){var t,n=je.type+"-"+Math.floor(1e4*Math.random());switch(je.type){case"youtube":t=i.parseYouTubeId(je.embedId);break;default:t=je.embedId}for(var s=d('[id^="'+je.type+'-"]'),a=s.length-1;a>=0;a--)i.removeElement(s[a]);if(i.toggleClass(je.elements.wrapper,Re.classes.embedWrapper,!0),"youtube"===je.type)je.elements.media.setAttribute("id",n),i.is.object(e.YT)?z(t):(i.injectScript(Re.urls.youtube.api),e.onYouTubeReadyCallbacks=e.onYouTubeReadyCallbacks||[],e.onYouTubeReadyCallbacks.push(function(){z(t)}),e.onYouTubeIframeAPIReady=function(){e.onYouTubeReadyCallbacks.forEach(function(e){e()})});else if("vimeo"===je.type)if(je.elements.media.setAttribute("id",n),i.is.object(e.Vimeo))J(t);else{i.injectScript(Re.urls.vimeo.api);var l=e.setInterval(function(){i.is.object(e.Vimeo)&&(e.clearInterval(l),J(t))},50)}else if("soundcloud"===je.type){var r=i.createElement("iframe");r.loaded=!1,i.on(r,"load",function(){r.loaded=!0}),i.setAttributes(r,{src:"https://w.soundcloud.com/player/?url=https://api.soundcloud.com/tracks/"+t,id:n}),je.elements.media.appendChild(r),e.SC||i.injectScript(Re.urls.soundcloud.api);var o=e.setInterval(function(){e.SC&&r.loaded&&(e.clearInterval(o),X.call(r))},50)}}function Q(){je.supported.full&&(qe(),Oe()),B(p("iframe"))}function z(t){je.embed=new e.YT.Player(je.elements.media.id,{videoId:t,playerVars:{autoplay:Re.autoplay?1:0,controls:je.supported.full?0:1,rel:0,showinfo:0,iv_load_policy:3,cc_load_policy:Re.captions.active?1:0,cc_lang_pref:"en",wmode:"transparent",modestbranding:1,disablekb:1,playsinline:1,origin:e.location.href},events:{onError:function(e){c(je.elements.container,"error",!0,{code:e.data,embed:e.target})},onPlaybackQualityChange:function(e){var t=e.target,n=t.getPlaybackQuality();console.warn(n)},onReady:function(t){var n=t.target;je.elements.media.play=function(){n.playVideo(),je.elements.media.paused=!1},je.elements.media.pause=function(){n.pauseVideo(),je.elements.media.paused=!0},je.elements.media.stop=function(){n.stopVideo(),je.elements.media.paused=!0},je.elements.media.duration=n.getDuration(),je.elements.media.paused=!0,je.elements.media.currentTime=0,je.elements.media.muted=n.isMuted();var s=n.getPlaybackRate(),a=n.getAvailablePlaybackRates();console.warn(s,a),Re.title=n.getVideoData().title,je.supported.full&&je.elements.media.setAttribute("tabindex",-1),Q(),c(je.elements.media,"timeupdate"),c(je.elements.media,"durationchange"),e.clearInterval(Ve.buffering),Ve.buffering=e.setInterval(function(){je.elements.media.buffered=n.getVideoLoadedFraction(),(null===je.elements.media.lastBuffered||je.elements.media.lastBuffered<je.elements.media.buffered)&&c(je.elements.media,"progress"),je.elements.media.lastBuffered=je.elements.media.buffered,1===je.elements.media.buffered&&(e.clearInterval(Ve.buffering),c(je.elements.media,"canplaythrough"))},200)},onStateChange:function(t){var n=t.target;switch(e.clearInterval(Ve.playing),t.data){case 0:if(Re.loop.active){n.stopVideo(),n.playVideo();break}je.elements.media.paused=!0,c(je.elements.media,"ended");break;case 1:je.elements.media.paused=!1,je.elements.media.seeking&&c(je.elements.media,"seeked"),je.elements.media.seeking=!1,c(je.elements.media,"play"),c(je.elements.media,"playing"),Ve.playing=e.setInterval(function(){je.elements.media.currentTime=n.getCurrentTime(),c(je.elements.media,"timeupdate")},100),je.elements.media.duration!==n.getDuration()&&(je.elements.media.duration=n.getDuration(),c(je.elements.media,"durationchange"));var s=n.getAvailableQualityLevels(),a=n.getPlaybackQuality();T(s,a);break;case 2:je.elements.media.paused=!0,c(je.elements.media,"pause")}c(je.elements.container,"statechange",!1,{code:t.data})}}})}function J(t){je.embed=new e.Vimeo.Player(je.elements.media,{id:t,loop:Re.loop.active,autoplay:Re.autoplay,byline:!1,portrait:!1,title:!1}),je.elements.media.play=function(){je.embed.play(),je.elements.media.paused=!1},je.elements.media.pause=function(){je.embed.pause(),je.elements.media.paused=!0},je.elements.media.stop=function(){je.embed.stop(),je.elements.media.paused=!0},je.elements.media.paused=!0,je.elements.media.currentTime=0,Q(),je.embed.getCurrentTime().then(function(e){je.elements.media.currentTime=e,c(je.elements.media,"timeupdate")}),je.embed.getDuration().then(function(e){je.elements.media.duration=e,c(je.elements.media,"durationchange")}),je.embed.getTextTracks().then(function(e){P(e),Re.captions.active&&je.embed.enableTextTrack(Re.captions.language.toLowerCase())}),je.embed.on("cuechange",function(e){var t=null;e.cues.length&&(t=i.stripHTML(e.cues[0].text)),q(t)}),je.embed.on("loaded",function(){i.is.htmlElement(je.embed.element)&&je.supported.full&&je.embed.element.setAttribute("tabindex",-1)}),je.embed.on("play",function(){je.elements.media.paused=!1,c(je.elements.media,"play"),c(je.elements.media,"playing")}),je.embed.on("pause",function(){je.elements.media.paused=!0,c(je.elements.media,"pause")}),je.embed.on("timeupdate",function(e){je.elements.media.seeking=!1,je.elements.media.currentTime=e.seconds,c(je.elements.media,"timeupdate")}),je.embed.on("progress",function(e){je.elements.media.buffered=e.percent,c(je.elements.media,"progress"),1===parseInt(e.percent)&&c(je.elements.media,"canplaythrough")}),je.embed.on("seeked",function(){je.elements.media.seeking=!1,c(je.elements.media,"seeked"),c(je.elements.media,"play")}),je.embed.on("ended",function(){je.elements.media.paused=!0,c(je.elements.media,"ended")})}function X(){je.embed=e.SC.Widget(this),je.embed.bind(e.SC.Widget.Events.READY,function(){je.elements.media.play=function(){je.embed.play(),je.elements.media.paused=!1},je.elements.media.pause=function(){je.embed.pause(),je.elements.media.paused=!0},je.elements.media.stop=function(){je.embed.seekTo(0),je.embed.pause(),je.elements.media.paused=!0},je.elements.media.paused=!0,je.elements.media.currentTime=0,je.embed.getDuration(function(e){je.elements.media.duration=e/1e3,Q()}),je.embed.getPosition(function(e){je.elements.media.currentTime=e,c(je.elements.media,"timeupdate")}),je.embed.bind(e.SC.Widget.Events.PLAY,function(){je.elements.media.paused=!1,c(je.elements.media,"play"),c(je.elements.media,"playing")}),je.embed.bind(e.SC.Widget.Events.PAUSE,function(){je.elements.media.paused=!0,c(je.elements.media,"pause")}),je.embed.bind(e.SC.Widget.Events.PLAY_PROGRESS,function(e){je.elements.media.seeking=!1,je.elements.media.currentTime=e.currentPosition/1e3,c(je.elements.media,"timeupdate")}),je.embed.bind(e.SC.Widget.Events.LOAD_PROGRESS,function(e){je.elements.media.buffered=e.loadProgress,c(je.elements.media,"progress"),1===parseInt(e.loadProgress)&&c(je.elements.media,"canplaythrough")}),je.embed.bind(e.SC.Widget.Events.FINISH,function(){je.elements.media.paused=!0,c(je.elements.media,"ended")})})}function $(){"play"in je.elements.media&&je.elements.media.play()}function G(){"pause"in je.elements.media&&je.elements.media.pause()}function K(e){return i.is.boolean(e)||(e=je.elements.media.paused),e?$():G(),e}function Z(e){i.inArray(["start","end","all","none","toggle"],e)||(e="toggle");var n=Number(je.elements.media.currentTime);switch(e){case"start":Re.loop.end&&Re.loop.end<=n&&(Re.loop.end=null),Re.loop.start=n,Re.loop.indicator.start=je.elements.display.played.value;break;case"end":if(Re.loop.start>=n)return;Re.loop.end=n,Re.loop.indicator.end=je.elements.display.played.value;break;case"all":Re.loop.start=0,Re.loop.end=je.elements.media.duration-2,Re.loop.indicator.start=0,Re.loop.indicator.end=100;break;case"toggle":Re.loop.active?(Re.loop.start=0,Re.loop.end=null):(Re.loop.start=0,Re.loop.end=je.elements.media.duration-2);break;default:Re.loop.start=0,Re.loop.end=null}Re.loop.active=i.is.number(Re.loop.start)&&i.is.number(Re.loop.end);var s=(Ce(Re.loop.start,p('[data-plyr-loop="start"]')),null);i.is.number(Re.loop.end)&&(s=Ce(Re.loop.end,t.querySelector('[data-loop__value="loopout"]'))),Re.loop.active}function ee(e){return i.is.event(e)?e=parseFloat(e.target.value):i.is.number(e)||(e=parseFloat(je.storage.speed||Re.speed.selected)),e<.1&&(e=.1),e>2&&(e=2),i.is.array(Re.speed.options)?(Re.speed.selected=e,je.elements.media.playbackRate=e,void W({speed:e})):void He("Invalid speeds format")}function te(e){return i.is.number(e)||(e=Re.speed.selected),1===e?"Normal":e+"×"}function ne(e){i.is.number(e)||(e=Re.seekTime),ae(je.elements.media.currentTime-e)}function se(e){i.is.number(e)||(e=Re.seekTime),ae(je.elements.media.currentTime+e)}function ae(e){var t=0,n=je.elements.media.paused,s=le();i.is.number(e)?t=e:i.is.event(e)&&i.inArray(["input","change"],e.type)&&(t=e.target.value/e.target.max*s),t<0?t=0:t>s&&(t=s),we(t);try{je.elements.media.currentTime=t.toFixed(4)}catch(e){}if(i.inArray(l.embed,je.type)){switch(je.type){case"youtube":je.embed.seekTo(t);break;case"vimeo":je.embed.setCurrentTime(t.toFixed(0));break;case"soundcloud":je.embed.seekTo(1e3*t)}n&&G(),c(je.elements.media,"timeupdate"),je.elements.media.seeking=!0,c(je.elements.media,"seeking")}Be("Seeking to "+je.elements.media.currentTime+" seconds")}function le(){var e=parseInt(Re.duration),t=0;return null===je.elements.media.duration||isNaN(je.elements.media.duration)||(t=je.elements.media.duration),isNaN(e)?t:e}function ie(){i.toggleClass(je.elements.container,Re.classes.playing,!je.elements.media.paused),i.toggleClass(je.elements.container,Re.classes.stopped,je.elements.media.paused),Se(je.elements.media.paused)}function re(){s={x:e.pageXOffset||0,y:e.pageYOffset||0}}function oe(){e.scrollTo(s.x,s.y)}function ue(e){var n=o.fullscreen;if(n){if(!e||e.type!==r.eventType)return r.isFullScreen(je.elements.container)?r.cancelFullScreen():(re(),r.requestFullScreen(je.elements.container)),void(je.fullscreen.active=r.isFullScreen(je.elements.container));je.fullscreen.active=r.isFullScreen(je.elements.container)}else je.fullscreen.active=!je.fullscreen.active,t.body.style.overflow=je.fullscreen.active?"hidden":"";i.toggleClass(je.elements.container,Re.classes.fullscreen.active,je.fullscreen.active),f(je.fullscreen.active),je.elements.buttons&&je.elements.buttons.fullscreen&&i.toggleState(je.elements.buttons.fullscreen,je.fullscreen.active),c(je.elements.container,je.fullscreen.active?"enterfullscreen":"exitfullscreen",!0),!je.fullscreen.active&&n&&oe()}function ce(e){var t=je.elements.settings.form,n=je.elements.buttons.settings,s=i.is.boolean(e)?e:"true"===t.getAttribute("aria-hidden");if(i.is.event(e)){var a=t.contains(e.target),l=e.target===je.elements.buttons.settings;if(a||!a&&!l&&s)return;l&&e.stopPropagation()}t.setAttribute("aria-hidden",!s),n.setAttribute("aria-expanded",s),s?t.removeAttribute("tabindex"):t.setAttribute("tabindex",-1)}function de(e){var t,n,s=e.cloneNode(!0);return s.style.position="absolute",s.style.opacity=0,s.setAttribute("aria-hidden",!1),[].forEach.call(s.querySelectorAll("input[name]"),function(e){var t=e.getAttribute("name");e.setAttribute("name",t+"-clone")}),e.parentNode.appendChild(s),t=s.scrollWidth,n=s.scrollHeight,i.removeElement(s),{width:t,height:n}}function pe(e){var n=je.elements.settings.menu,s=e.target,a="false"===s.getAttribute("aria-expanded"),l=t.getElementById(s.getAttribute("aria-controls"));if(i.is.htmlElement(l)){var r="tabpanel"===l.getAttribute("role");if(r){var u=n.querySelector('[role="tabpanel"][aria-hidden="false"]'),c=u.parentNode;if([].forEach.call(n.querySelectorAll('[aria-controls="'+u.getAttribute("id")+'"]'),function(e){e.setAttribute("aria-expanded",!1)}),o.transitions){c.style.width=u.scrollWidth+"px",c.style.height=u.scrollHeight+"px";var d=de(l),p=function(e){e.target===c&&i.inArray(["width","height"],e.propertyName)&&(c.style.width="",c.style.height="",i.off(c,i.transitionEnd,p))};i.on(c,i.transitionEnd,p),c.style.width=d.width+"px",c.style.height=d.height+"px"}u.setAttribute("aria-hidden",!0),u.setAttribute("tabindex",-1),l.setAttribute("aria-hidden",!a),s.setAttribute("aria-expanded",a),l.removeAttribute("tabindex")}}}function me(e){if(i.is.boolean(e)||(e=!je.elements.media.muted),i.toggleState(je.elements.buttons.mute,e),je.elements.media.muted=e,0===je.elements.media.volume&&fe(Re.volume),i.inArray(l.embed,je.type)){switch(je.type){case"youtube":je.embed[je.elements.media.muted?"mute":"unMute"]();break;case"vimeo":case"soundcloud":je.embed.setVolume(je.elements.media.muted?0:parseFloat(Re.volume/10))}c(je.elements.media,"volumechange")}}function fe(e){var t=10,n=0;if(i.is.event(e)&&(e=e.target.value),i.is.undefined(e)&&(e=je.storage.volume),(null===e||isNaN(e))&&(e=Re.volume),e>t&&(e=t),e<n&&(e=n),je.elements.media.volume=parseFloat(e/t),je.elements.display.volume&&(je.elements.display.volume.value=e),i.inArray(l.embed,je.type)){switch(je.type){case"youtube":je.embed.setVolume(100*je.elements.media.volume);break;case"vimeo":case"soundcloud":je.embed.setVolume(je.elements.media.volume)}c(je.elements.media,"volumechange")}0===e?je.elements.media.muted=!0:je.elements.media.muted&&e>0&&me()}function ye(e){var t=je.elements.media.muted?0:10*je.elements.media.volume;i.is.number(e)||(e=1),fe(t+e)}function ge(e){var t=je.elements.media.muted?0:10*je.elements.media.volume;i.is.number(e)||(e=1),fe(t-e)}function be(){var e=je.elements.media.muted?0:10*je.elements.media.volume;je.supported.full&&(je.elements.inputs.volume&&(je.elements.inputs.volume.value=e),je.elements.display.volume&&(je.elements.display.volume.value=e)),W({volume:e}),i.toggleClass(je.elements.container,Re.classes.muted,0===e),je.supported.full&&je.elements.buttons.mute&&i.toggleState(je.elements.buttons.mute,0===e)}function ve(e){var t="waiting"===e.type;clearTimeout(Ve.loading),Ve.loading=setTimeout(function(){i.toggleClass(je.elements.container,Re.classes.loading,t),Se(t)},t?250:0)}function he(e){if(je.supported.full){var t=je.elements.display.played,n=0,s=le();if(e)switch(e.type){case"timeupdate":case"seeking":if(je.elements.controls.pressed)return;n=i.getPercentage(je.elements.media.currentTime,s),"timeupdate"===e.type&&je.elements.inputs.seek&&(je.elements.inputs.seek.value=n);break;case"playing":case"progress":t=je.elements.display.buffer,n=function(){var e=je.elements.media.buffered;return e&&e.length?i.getPercentage(e.end(0),s):i.is.number(e)?100*e:0}()}i.is.number(Re.loop.start)&&i.is.number(Re.loop.end)&&je.elements.media.currentTime>=Re.loop.end&&ae(Re.loop.start),ke(t,n)}}function ke(e,t){if(je.supported.full){if(i.is.undefined(t)&&(t=0),i.is.undefined(e)){if(!i.is.htmlElement(je.elements.display.buffer))return;e=je.elements.display.buffer}if(i.is.htmlElement(e)){e.value=t;var n=e.getElementsByTagName("span")[0];i.is.htmlElement(n)&&(n.childNodes[0].nodeValue=t)}}}function Ce(e,t){if(t){isNaN(e)&&(e=0);var n=parseInt(e%60),s=parseInt(e/60%60),a=parseInt(e/60/60%60),l=parseInt(le()/60/60%60)>0;n=("0"+n).slice(-2),s=("0"+s).slice(-2);var i=(l?a+":":"")+s+":"+n;return t.textContent=i,i}}function Ee(){if(je.supported.full){var e=le()||0; -!je.elements.display.duration&&Re.displayDuration&&je.elements.media.paused&&Ce(e,je.elements.display.currentTime),je.elements.display.duration&&Ce(e,je.elements.display.duration),Te()}}function Ae(e){Ce(je.elements.media.currentTime,je.elements.display.currentTime),e&&"timeupdate"===e.type&&je.elements.media.seeking||he(e)}function we(e){i.is.number(e)||(e=0);var t=le(),n=i.getPercentage(e,t);je.elements.progress&&je.elements.display.played&&(je.elements.display.played.value=n),je.elements.buttons&&je.elements.inputs.seek&&(je.elements.inputs.seek.value=n)}function Te(e){var t=le();if(Re.tooltips.seek&&i.is.htmlElement(je.elements.inputs.seek)&&i.is.htmlElement(je.elements.display.seekTooltip)&&0!==t){var n=je.elements.inputs.seek.getBoundingClientRect(),s=0,a=Re.classes.tooltip+"--visible";if(i.is.event(e))s=100/n.width*(e.pageX-n.left);else{if(!i.hasClass(je.elements.display.seekTooltip,a))return;s=je.elements.display.seekTooltip.style.left.replace("%","")}s<0?s=0:s>100&&(s=100),Ce(t/100*s,je.elements.display.seekTooltip),je.elements.display.seekTooltip.style.left=s+"%",i.is.event(e)&&i.inArray(["mouseenter","mouseleave"],e.type)&&i.toggleClass(je.elements.display.seekTooltip,a,"mouseenter"===e.type)}}function Se(t){if(Re.hideControls&&"audio"!==je.type){var n=0,s=!1,a=t,l=i.hasClass(je.elements.container,Re.classes.loading);if(i.is.boolean(t)||(t&&t.type?(s="enterfullscreen"===t.type,a=i.inArray(["mousemove","touchstart","mouseenter","focus"],t.type),i.inArray(["mousemove","touchmove"],t.type)&&(n=2e3),"focus"===t.type&&(n=3e3)):a=i.hasClass(je.elements.container,Re.classes.hideControls)),e.clearTimeout(Ve.hover),a||je.elements.media.paused||l){if(i.toggleClass(je.elements.container,Re.classes.hideControls,!1),je.elements.media.paused||l)return;o.touch&&(n=3e3)}a&&je.elements.media.paused||(Ve.hover=e.setTimeout(function(){(!je.elements.controls.pressed&&!je.elements.controls.hover||s)&&i.toggleClass(je.elements.container,Re.classes.hideControls,!0)},n))}}function xe(e){if(!i.is.undefined(e))return void Fe(e);var t;switch(je.type){case"youtube":t=je.embed.getVideoUrl();break;case"vimeo":je.embed.getVideoUrl.then(function(e){t=e});break;case"soundcloud":je.embed.getCurrentSound(function(e){t=e.permalink_url});break;default:t=je.elements.media.currentSrc}return t||""}function Fe(e){function t(){if(je.embed=null,m("media"),m("captions"),m("wrapper"),je.elements.container&&je.elements.container.removeAttribute("class"),"type"in e&&(je.type=e.type,"video"===je.type)){var t=e.sources[0];"type"in t&&i.inArray(l.embed,t.type)&&(je.type=t.type)}switch(je.supported=i.checkSupport(je.type,Re.inline),je.type){case"video":je.elements.media=i.createElement("video");break;case"audio":je.elements.media=i.createElement("audio");break;case"youtube":case"vimeo":case"soundcloud":je.elements.media=i.createElement("div"),je.embedId=e.sources[0].src}i.prependChild(je.elements.container,je.elements.media),i.is.boolean(e.autoplay)&&(Re.autoplay=e.autoplay),i.inArray(l.html5,je.type)&&(Re.crossorigin&&je.elements.media.setAttribute("crossorigin",""),Re.autoplay&&je.elements.media.setAttribute("autoplay",""),"poster"in e&&je.elements.media.setAttribute("poster",e.poster),Re.loop.active&&je.elements.media.setAttribute("loop",""),Re.inline&&je.elements.media.setAttribute("playsinline","")),i.toggleClass(je.elements.container,Re.classes.fullscreen.active,je.fullscreen.active),i.toggleClass(je.elements.container,Re.classes.captions.active,je.captions.enabled),D(),i.inArray(l.html5,je.type)&&y("source",e.sources),Y(),i.inArray(l.html5,je.type)&&("tracks"in e&&y("track",e.tracks),je.elements.media.load()),(i.inArray(l.html5,je.type)||i.inArray(l.embed,je.type)&&!je.supported.full)&&(qe(),Oe()),Re.title=e.title,B()}return i.is.object(e)&&"sources"in e&&e.sources.length?(i.toggleClass(je.elements.container,Re.classes.ready,!1),G(),we(),ke(),Ne(),void Le(t,!1)):void He("Invalid source format")}function Ie(e){"video"===je.type&&je.elements.media.setAttribute("poster",e)}function Pe(){function n(){var e=K(),t=je.elements.buttons[e?"play":"pause"],n=je.elements.buttons[e?"pause":"play"];if(n){var s=i.hasClass(t,Re.classes.tabFocus);setTimeout(function(){i.is.htmlElement(n)&&n.focus(),s&&(i.toggleClass(t,Re.classes.tabFocus,!1),i.toggleClass(n,Re.classes.tabFocus,!0))},100)}}function s(e){return e.keyCode?e.keyCode:e.which}function a(e){i.toggleClass(d("."+Re.classes.tabFocus),Re.classes.tabFocus,!1),je.elements.container.contains(e)&&i.toggleClass(e,Re.classes.tabFocus,!0)}function l(e){function t(){var e=je.elements.media.duration;i.is.number(e)&&ae(e/10*(a-48))}var a=s(e),l="keydown"===e.type,r=l&&a===c;if(i.is.number(a))if(l){var u=[48,49,50,51,52,53,54,56,57,32,75,38,40,77,39,37,70,67,73,76,79],d=[38,40];if(i.inArray(d,a)){var p=i.getFocusElement();if(i.is.htmlElement(p)&&"radio"===i.getFocusElement().type)return}switch(i.inArray(u,a)&&(e.preventDefault(),e.stopPropagation()),a){case 48:case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:r||t();break;case 32:case 75:r||n();break;case 38:ye();break;case 40:ge();break;case 77:r||me();break;case 39:se();break;case 37:ne();break;case 70:ue();break;case 67:r||M();break;case 73:Z("start");break;case 76:Z();break;case 79:Z("end")}!o.fullscreen&&je.fullscreen.active&&27===a&&ue(),c=a}else c=null}var u=je.browser.isIE?"change":"input";if(Re.keyboardShortcuts.focused){var c=null;Re.keyboardShortcuts.global&&i.on(e,"keydown keyup",function(e){var t=s(e),n=i.getFocusElement(),a=[48,49,50,51,52,53,54,56,57,75,77,70,67,73,76,79];!i.inArray(a,t)||i.is.htmlElement(n)&&i.matches(n,Re.selectors.editable)||l(e)},!1),i.on(je.elements.container,"keydown keyup",l,!1)}i.on(e,"keyup",function(e){var t=s(e),n=i.getFocusElement();9===t&&a(n)}),i.on(t.body,"click",function(){i.toggleClass(p("."+Re.classes.tabFocus),Re.classes.tabFocus,!1)});for(var m in je.elements.buttons){var f=je.elements.buttons[m];i.on(f,"blur",function(){i.toggleClass(f,"tab-focus",!1)})}var y=function(e,t,n){i.is.function(t)&&t.call(this,e),i.is.function(n)&&n.call(this,e)};i.proxy(je.elements.buttons.play,"click",Re.listeners.play,n),i.proxy(je.elements.buttons.playLarge,"click",Re.listeners.play,n),i.proxy(je.elements.buttons.pause,"click",Re.listeners.pause,n),i.proxy(je.elements.buttons.restart,"click",Re.listeners.restart,ae),i.proxy(je.elements.buttons.rewind,"click",Re.listeners.rewind,ne),i.proxy(je.elements.buttons.forward,"click",Re.listeners.forward,se),i.proxy(je.elements.buttons.mute,"click",Re.listeners.mute,me),i.proxy(je.elements.buttons.captions,"click",Re.listeners.captions,M),i.proxy(je.elements.buttons.fullscreen,"click",Re.listeners.fullscreen,ue),i.proxy(je.elements.buttons.pip,"click",Re.listeners.pip,function(e){o.pip&&je.elements.media.webkitSetPresentationMode("picture-in-picture"===je.elements.media.webkitPresentationMode?"inline":"picture-in-picture")}),i.proxy(je.elements.buttons.airplay,"click",Re.listeners.airplay,function(e){o.airplay&&je.elements.media.webkitShowPlaybackTargetPicker()}),i.on(je.elements.buttons.settings,"click",ce),i.on(t.documentElement,"click",ce),i.on(je.elements.settings.form,"click",pe),i.on(je.elements.settings.form,"click",function(e){i.matches(e.target,Re.selectors.inputs.language)?y.call(this,e,Re.listeners.language,L):i.matches(e.target,Re.selectors.inputs.quality)?y.call(this,e,Re.listeners.quality,function(){He("Set quality")}):i.matches(e.target,Re.selectors.inputs.speed)?y.call(this,e,Re.listeners.speed,ee):i.matches(e.target,Re.selectors.buttons.loop)&&y.call(this,e,Re.listeners.loop,function(){var t=e.target.getAttribute("data-loop__value")||e.target.getAttribute("data-loop__type");i.inArray(["start","end","all","none"],t)&&Z(t)})}),i.proxy(je.elements.inputs.seek,u,Re.listeners.seek,ae),i.proxy(je.elements.inputs.volume,u,Re.listeners.volume,fe),i.on(je.elements.progress,"mouseenter mouseleave mousemove",Te),Re.hideControls&&(i.on(je.elements.container,"mouseenter mouseleave mousemove touchstart touchend touchcancel touchmove enterfullscreen",Se),i.on(je.elements.controls,"mouseenter mouseleave",function(e){je.elements.controls.hover="mouseenter"===e.type}),i.on(je.elements.controls,"mousedown mouseup touchstart touchend touchcancel",function(e){je.elements.controls.pressed=i.inArray(["mousedown","touchstart"],e.type)}),i.on(je.elements.controls,"focus blur",Se,!0,!0)),i.proxy(je.elements.inputs.volume,"wheel",Re.listeners.volume,function(e){var t=e.webkitDirectionInvertedFromDevice,n=.2,s=0;(e.deltaY<0||e.deltaX>0)&&(t?(ge(n),s=-1):(ye(n),s=1)),(e.deltaY>0||e.deltaX<0)&&(t?(ye(n),s=1):(ge(n),s=-1)),(1===s&&je.elements.media.volume<1||s===-1&&je.elements.media.volume>0)&&e.preventDefault()},!1),o.fullscreen&&i.on(t,r.eventType,ue)}function _e(){if(i.on(je.elements.media,"timeupdate seeking",Ae),i.on(je.elements.media,"durationchange loadedmetadata",Ee),i.on(je.elements.media,"ended",function(){"video"===je.type&&Re.showPosterOnEnd&&("video"===je.type&&q(),ae(),je.elements.media.load())}),i.on(je.elements.media,"progress playing",he),i.on(je.elements.media,"volumechange",be),i.on(je.elements.media,"play pause ended",ie),i.on(je.elements.media,"waiting canplay seeked",ve),Re.clickToPlay&&"audio"!==je.type){var e=p("."+Re.classes.videoWrapper);if(!e)return;e.style.cursor="pointer",i.on(e,"click",function(){Re.hideControls&&o.touch&&!je.elements.media.paused||(je.elements.media.paused?$():je.elements.media.ended?(ae(),$()):G())})}Re.disableContextMenu&&i.on(je.elements.media,"contextmenu",function(e){e.preventDefault()},!1),i.on(je.elements.media,Re.events.concat(["keyup","keydown"]).join(" "),function(e){c(je.elements.container,e.type,!0)})}function Ne(){if(i.inArray(l.html5,je.type)){for(var e=je.elements.media.querySelectorAll("source"),t=0;t<e.length;t++)i.removeElement(e[t]);je.elements.media.setAttribute("src","https://cdn.selz.com/plyr/blank.mp4"),je.elements.media.load(),Be("Cancelled network requests")}}function Le(n,s){function a(){i.is.boolean(s)||(s=!0),i.is.function(n)&&n.call(je.original),s&&(je.elements.container.parentNode.replaceChild(je.original,je.elements.container),t.body.style.overflow="",c(je.original,"destroyed",!0))}switch(je.type){case"youtube":e.clearInterval(Ve.buffering),e.clearInterval(Ve.playing),je.embed.destroy(),a();break;case"vimeo":je.embed.unload().then(a),e.setTimeout(a,200);break;case"video":case"audio":R(!0),a()}}function qe(){return je.supported.full?(i.is.htmlElement(je.elements.controls)||(j(),Pe()),void(i.is.htmlElement(je.elements.controls)&&(_e(),R(),I(),P(),fe(),be(),ee(),Z(),Ae(),ie()))):(He("Basic support only",je.type),m("controls"),m("buttons.play"),void R(!0))}function Oe(){c(je.elements.container,"ready",!0),Re.autoplay&&$()}function Me(e){if(!i.is.htmlElement(e))return We("Setup failed. No suitable element passed."),!1;if(!Re.enabled)return!1;if(!i.checkSupport().basic)return!1;if(e.plyr)return!1;var t=e.tagName.toLowerCase();switch(t){case"div":if(je.type=e.getAttribute("data-type"),je.embedId=e.getAttribute("data-video-id"),i.is.empty(je.type)||i.is.empty(je.embedId))return!1;e.removeAttribute("data-type"),e.removeAttribute("data-video-id");break;case"iframe":break;case"video":case"audio":je.type=t,Re.crossorigin=null!==e.getAttribute("crossorigin"),Re.autoplay=Re.autoplay||null!==e.getAttribute("autoplay"),Re.inline=null!==e.getAttribute("playsinline"),Re.loop.active=Re.loop||null!==e.getAttribute("loop");break;default:return!1}if(je.browser=i.getBrowser(),H(),je.supported=i.checkSupport(je.type,Re.inline),!je.supported.basic)return!1;if(je.elements.container=i.wrap(e,i.createElement("div")),je.original=e.cloneNode(!0),je.elements.container.setAttribute("tabindex",0),D(),Be(je.browser.name+" "+je.browser.version),Y(),Re.debug){var n=Re.events.concat(["setup","statechange","enterfullscreen","exitfullscreen","captionsenabled","captionsdisabled"]);i.on(je.elements.container,n.join(" "),function(e){Be(["event:",e.type].join(" ").trim())})}return(i.inArray(l.html5,je.type)||i.inArray(l.embed,je.type)&&!je.supported.full)&&(qe(),Oe(),B()),!0}var je=this,Ve={},De={};i.is.string(n)&&(n=t.querySelectorAll(n)),(e.jQuery&&n instanceof jQuery||i.is.nodeList(n)||i.is.array(n))&&(n=n[0]);var Re=i.extend({},a,u,function(){try{return JSON.parse(n.getAttribute("data-plyr"))}catch(e){}}());je.elements={container:null,buttons:{},display:{},progress:{},inputs:{},settings:{menu:null,panes:{},tabs:{}},media:n,captions:null},je.captions={enabled:!1,captions:[],tracks:[],currentTrack:null},je.fullscreen={active:!1};var Be=function(){},He=function(){},We=function(){};return Re.debug&&"console"in e&&(Be=e.console.log,He=e.console.warn,We=e.console.error),Be("Config",Re),Be("Support",o),De={getOriginal:function(){return je.original},getContainer:function(){return je.elements.container},getEmbed:function(){return je.embed},getMedia:function(){return je.elements.media},getType:function(){return je.type},getDuration:le,getCurrentTime:function(){return je.elements.media.currentTime},getVolume:function(){return je.elements.media.volume},isMuted:function(){return je.elements.media.muted},isReady:function(){return i.hasClass(je.elements.container,Re.classes.ready)},isLoading:function(){return i.hasClass(je.elements.container,Re.classes.loading)},isPaused:function(){return je.elements.media.paused},isLooping:function(){return Re.loop.active},on:function(e,t){return i.on(je.elements.container,e,t),this},play:$,pause:G,loop:Z,stop:function(){G(),ae()},restart:ae,rewind:ne,forward:se,seek:ae,source:xe,poster:Ie,setVolume:fe,setSpeed:ee,togglePlay:K,toggleMute:me,toggleCaptions:M,toggleFullscreen:ue,toggleControls:Se,setLanguage:L,isFullscreen:je.fullscreen.active,support:function(e){return o.mime(je,e)},destroy:Le},Me(je.elements.media)?De:null}var s={x:0,y:0},a={enabled:!0,title:"",debug:!1,autoplay:!1,seekTime:10,volume:10,duration:null,displayDuration:!0,loadSprite:!0,iconPrefix:"plyr",iconUrl:"https://cdn.plyr.io/2.0.10/plyr.svg",clickToPlay:!0,hideControls:!0,showPosterOnEnd:!1,disableContextMenu:!0,quality:{default:"auto",selected:"auto"},loop:{active:!1,start:0,end:null,indicator:{start:0,end:0}},speed:{selected:1,options:[.25,.5,.75,1,1.25,1.5,2]},keyboardShortcuts:{focused:!0,global:!1},tooltips:{controls:!1,seek:!0},selectors:{editable:"input, textarea, select, [contenteditable]",container:".plyr",controls:{container:null,wrapper:".plyr__controls"},labels:"[data-plyr]",buttons:{play:'[data-plyr="play"]',pause:'[data-plyr="pause"]',restart:'[data-plyr="restart"]',rewind:'[data-plyr="rewind"]',forward:'[data-plyr="fast-forward"]',mute:'[data-plyr="mute"]',captions:'[data-plyr="captions"]',fullscreen:'[data-plyr="fullscreen"]',pip:'[data-plyr="pip"]',airplay:'[data-plyr="airplay"]',settings:'[data-plyr="settings"]',loop:'[data-plyr="loop"]'},inputs:{seek:'[data-plyr="seek"]',volume:'[data-plyr="volume"]',speed:'[data-plyr="speed"]',language:'[data-plyr="language"]',quality:'[data-plyr="quality"]'},display:{currentTime:".plyr__time--current",duration:".plyr__time--duration",buffer:".plyr__progress--buffer",played:".plyr__progress--played",loop:".plyr__progress--loop",volume:".plyr__volume--display"},progress:".plyr__progress",captions:".plyr__captions",menu:{quality:".js-plyr__menu__list--quality"}},classes:{setup:"plyr--setup",ready:"plyr--ready",videoWrapper:"plyr__video-wrapper",embedWrapper:"plyr__video-embed",control:"plyr__control",type:"plyr--{0}",stopped:"plyr--stopped",playing:"plyr--playing",muted:"plyr--muted",loading:"plyr--loading",hover:"plyr--hover",tooltip:"plyr__tooltip",hidden:"plyr__sr-only",hideControls:"plyr--hide-controls",isIos:"plyr--is-ios",isTouch:"plyr--is-touch",menu:{value:"plyr__menu__value",badge:"plyr__badge"},captions:{enabled:"plyr--captions-enabled",active:"plyr--captions-active"},fullscreen:{enabled:"plyr--fullscreen-enabled",active:"plyr--fullscreen-active"},pip:{enabled:"plyr--pip-enabled",active:"plyr--pip-active"},airplay:{enabled:"plyr--airplay-enabled",active:"plyr--airplay-active"},tabFocus:"tab-focus"},captions:{active:!1,language:e.navigator.language.split("-")[0]},fullscreen:{enabled:!0,fallback:!0,allowAudio:!1},storage:{enabled:!0,key:"plyr"},controls:["play-large","play","progress","current-time","mute","volume","captions","settings","pip","airplay","fullscreen"],settings:["captions","quality","speed","loop"],i18n:{restart:"Restart",rewind:"Rewind {seektime} secs",play:"Play",pause:"Pause",forward:"Forward {seektime} secs",seek:"Seek",played:"Played",buffered:"Buffered",currentTime:"Current time",duration:"Duration",volume:"Volume",toggleMute:"Toggle Mute",toggleCaptions:"Toggle Captions",toggleFullscreen:"Toggle Fullscreen",frameTitle:"Player for {title}",captions:"Captions",settings:"Settings",speed:"Speed",quality:"Quality",loop:"Loop",start:"Start",end:"End",all:"All",reset:"Reset",none:"None"},urls:{vimeo:{api:"https://player.vimeo.com/api/player.js"},youtube:{api:"https://www.youtube.com/iframe_api"},soundcloud:{api:"https://w.soundcloud.com/player/api.js"}},listeners:{seek:null,play:null,pause:null,restart:null,rewind:null,forward:null,mute:null,volume:null,captions:null,fullscreen:null,pip:null,airplay:null,speed:null,quality:null,loop:null,language:null},events:["ended","progress","stalled","playing","waiting","canplay","canplaythrough","loadstart","loadeddata","loadedmetadata","timeupdate","volumechange","play","pause","error","seeking","seeked","emptied"],logPrefix:""},l={embed:["youtube","vimeo","soundcloud"],html5:["video","audio"]},i={is:{object:function(e){return null!==e&&"object"==typeof e&&e.constructor===Object},array:function(e){return null!==e&&Array.isArray(e)},number:function(e){return null!==e&&("number"==typeof e&&!isNaN(e-0)||"object"==typeof e&&e.constructor===Number)},string:function(e){return null!==e&&("string"==typeof e||"object"==typeof e&&e.constructor===String)},boolean:function(e){return null!==e&&"boolean"==typeof e},nodeList:function(e){return null!==e&&e instanceof NodeList},htmlElement:function(e){return null!==e&&e instanceof HTMLElement},function:function(e){return null!==e&&"function"==typeof e},event:function(e){return null!==e&&e instanceof Event},cue:function(t){return null!==t&&(t instanceof e.TextTrackCue||t instanceof e.VTTCue)},track:function(t){return null!==t&&t instanceof e.TextTrack},undefined:function(e){return null!==e&&"undefined"==typeof e},empty:function(e){return null===e||this.undefined(e)||(this.string(e)||this.array(e)||this.nodeList(e))&&0===e.length||this.object(e)&&0===Object.keys(e).length}},getBrowser:function(){var e,t,n,s=navigator.userAgent,a=navigator.appName,l=""+parseFloat(navigator.appVersion),i=parseInt(navigator.appVersion,10),r=!1,o=!1,u=!1,c=!1;return navigator.appVersion.indexOf("Windows NT")!==-1&&navigator.appVersion.indexOf("rv:11")!==-1?(r=!0,a="IE",l="11"):(t=s.indexOf("MSIE"))!==-1?(r=!0,a="IE",l=s.substring(t+5)):(t=s.indexOf("Chrome"))!==-1?(u=!0,a="Chrome",l=s.substring(t+7)):(t=s.indexOf("Safari"))!==-1?(c=!0,a="Safari",l=s.substring(t+7),(t=s.indexOf("Version"))!==-1&&(l=s.substring(t+8))):(t=s.indexOf("Firefox"))!==-1?(o=!0,a="Firefox",l=s.substring(t+8)):(e=s.lastIndexOf(" ")+1)<(t=s.lastIndexOf("/"))&&(a=s.substring(e,t),l=s.substring(t+1),a.toLowerCase()===a.toUpperCase()&&(a=navigator.appName)),(n=l.indexOf(";"))!==-1&&(l=l.substring(0,n)),(n=l.indexOf(" "))!==-1&&(l=l.substring(0,n)),i=parseInt(""+l,10),isNaN(i)&&(l=""+parseFloat(navigator.appVersion),i=parseInt(navigator.appVersion,10)),{name:a,version:i,isIE:r,isOldIE:r&&i<=9,isFirefox:o,isChrome:u,isSafari:c,isIPhone:/(iPhone|iPod)/gi.test(navigator.platform),isIos:/(iPad|iPhone|iPod)/gi.test(navigator.platform)}},checkSupport:function(e,t){var n=!1,s=!1,a=i.getBrowser(),l=a.isIPhone&&t&&o.inline;switch(e){case"video":n=o.video,s=n&&!a.isOldIE&&(!a.isIPhone||l);break;case"audio":n=o.audio,s=n&&!a.isOldIE;break;case"youtube":n=o.video,s=n&&!a.isOldIE&&(!a.isIPhone||l);break;case"vimeo":case"soundcloud":n=!0,s=!a.isOldIE&&!a.isIos;break;default:n=o.audio&&o.video,s=n&&!a.isOldIE}return{basic:n,full:s}},injectScript:function(e){if(!t.querySelectorAll('script[src="'+e+'"]').length){var n=t.createElement("script");n.src=e;var s=t.getElementsByTagName("script")[0];s.parentNode.insertBefore(n,s)}},inFrame:function(){try{return e.self!==e.top}catch(e){return!0}},inArray:function(e,t){return i.is.array(e)&&e.indexOf(t)!==-1},replaceAll:function(e,t,n){return e.replace(new RegExp(t.replace(/([.*+?\^=!:${}()|\[\]\/\\])/g,"\\$1"),"g"),n)},wrap:function(e,t){e.length||(e=[e]);for(var n=e.length-1;n>=0;n--){var s=n>0?t.cloneNode(!0):t,a=e[n],l=a.parentNode,i=a.nextSibling;return s.appendChild(a),i?l.insertBefore(s,i):l.appendChild(s),s}},removeElement:function(e){i.is.htmlElement(e)&&i.is.htmlElement(e.parentNode)&&e.parentNode.removeChild(e)},prependChild:function(e,t){e.insertBefore(t,e.firstChild)},insertAfter:function(e,t){t.parentNode.insertBefore(e,t.nextSibling)},createElement:function(e,n,s){var a=t.createElement(e);return i.is.object(n)&&i.setAttributes(a,n),i.is.string(s)&&(a.textContent=s),a},insertElement:function(e,t,n,s){var a=i.createElement(e,n,s);i.prependChild(t,a)},emptyElement:function(e){for(var t=e.childNodes.length;t--;)e.removeChild(e.lastChild)},setAttributes:function(e,t){for(var n in t)e.setAttribute(n,t[n])},getAttributesFromSelector:function(e,t){if(!i.is.string(e)||i.is.empty(e))return{};var n={};return e.split(",").forEach(function(e){e=e.trim();var s=e.charAt(0);switch(s){case".":var a=e.replace(".","");i.is.object(t)&&i.is.string(t.class)&&(t.class+=" "+a),n.class=a;break;case"#":n.id=e.replace("#","");break;case"[":e=e.replace(/[\[\]]/g,"");var l=e.split("="),r=l[0],o=l.length>1?l[1].replace(/[\"\']/g,""):"";n[r]=o}}),n},toggleClass:function(e,t,n){if(e)if(e.classList)e.classList[n?"add":"remove"](t);else{var s=(" "+e.className+" ").replace(/\s+/g," ").replace(" "+t+" ","");e.className=s+(n?" "+t:"")}},hasClass:function(e,t){return!!e&&(e.classList?e.classList.contains(t):new RegExp("(\\s|^)"+t+"(\\s|$)").test(e.className))},matches:function(e,n){var s=Element.prototype,a=s.matches||s.webkitMatchesSelector||s.mozMatchesSelector||s.msMatchesSelector||function(e){return[].indexOf.call(t.querySelectorAll(e),this)!==-1};return a.call(e,n)},getFocusElement:function(){var e=t.activeElement;return e=e&&e!==t.body?t.querySelector(":focus"):null},proxy:function(e,t,n,s,a,l){i.on(e,t,function(t){n&&n.apply(e,[t]),s.apply(e,[t])},a,l)},toggleListener:function(e,t,n,s,a,l){if(t=t.split(" "),i.is.boolean(l)||(l=!1),i.is.boolean(a)||(a=!0),e instanceof NodeList){var r=1===arguments.length?[arguments[0]]:Array.apply(null,arguments);return r.shift(),void[].forEach.call(e,function(e){e instanceof Node&&i.toggleListener.apply(null,[e].concat(r))})}var u=l;o.passiveListeners&&(u={passive:a,capture:l}),t.forEach(function(t){e[s?"addEventListener":"removeEventListener"](t,n,u)})},on:function(e,t,n,s,a){i.is.undefined(e)||i.toggleListener(e,t,n,!0,s,a)},off:function(e,t,n,s,a){i.is.undefined(e)||i.toggleListener(e,t,n,!1,s,a)},event:function(n,s,a,l){if(n&&s){i.is.boolean(a)||(a=!1);var r;i.is.function(e.CustomEvent)?r=e.CustomEvent:(r=function(e,n){n=n||{bubbles:!1,cancelable:!1,detail:void 0};var s=t.createEvent("CustomEvent");return s.initCustomEvent(e,n.bubbles,n.cancelable,n.detail),s},r.prototype=e.Event.prototype);var o=new r(s,{bubbles:a,detail:l});n.dispatchEvent(o)}},toggleState:function(e,t){if(e)return t=i.is.boolean(t)?t:!e.getAttribute("aria-pressed"),e.setAttribute("aria-pressed",t),t},getPercentage:function(e,t){return 0===e||0===t||isNaN(e)||isNaN(t)?0:(e/t*100).toFixed(2)},extend:function(){var e=arguments;if(e.length){if(1===e.length)return e[0];var t=Array.prototype.shift.call(e);i.is.object(t)||(t={});for(var n=e.length,s=0;s<n;s++){var a=e[s];i.is.object(a)||(a={});for(var l in a)a[l]&&a[l].constructor&&a[l].constructor===Object?(t[l]=t[l]||{},i.extend(t[l],a[l])):t[l]=a[l]}return t}},parseYouTubeId:function(e){var t=/^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/;return e.match(t)?RegExp.$2:e},stripHTML:function(e){var n=t.createDocumentFragment(),s=t.createElement("div");return n.appendChild(s),s.innerHTML=e,n.firstChild.innerText},loadSprite:function(n,s){function a(e,n){e.innerHTML=n,t.body.insertBefore(e,t.body.childNodes[0])}if("string"==typeof n){var l="cache-",i="string"==typeof s,r=!1;if(!i||!t.querySelectorAll("#"+s).length){var u=t.createElement("div");if(u.setAttribute("hidden",""),i&&u.setAttribute("id",s),o.storage){var c=e.localStorage.getItem(l+s);if(r=null!==c){var d=JSON.parse(c);a(u,d.content)}}var p=new XMLHttpRequest;if(!("withCredentials"in p))return;p.open("GET",n,!0),p.onload=function(){o.storage&&e.localStorage.setItem(l+s,JSON.stringify({content:p.responseText})),a(u,p.responseText)},p.send()}}},transitionEnd:function(){var e=t.createElement("span"),n={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var s in n)if(void 0!==e.style[s])return n[s];return!1}()},r=function(){var e=function(){var e=!1;return i.is.function(t.cancelFullScreen)?e="":["webkit","o","moz","ms","khtml"].some(function(n){return i.is.function(t[n+"CancelFullScreen"])?(e=n,!0):i.is.function(t.msExitFullscreen)&&t.msFullscreenEnabled?(e="ms",!0):void 0}),e}();return{prefix:e,eventType:"ms"===e?"MSFullscreenChange":e+"fullscreenchange",isFullScreen:function(n){if(!o.fullscreen)return!1;switch(i.is.undefined(n)&&(n=t.body),this.prefix){case"":return t.fullscreenElement===n;case"moz":return t.mozFullScreenElement===n;default:return t[e+"FullscreenElement"]===n}},requestFullScreen:function(n){return!!o.fullscreen&&(i.is.htmlElement(n)||(n=t.body),""===e?n.requestFullScreen():n[e+("ms"===e?"RequestFullscreen":"RequestFullScreen")]())},cancelFullScreen:function(){return!!o.fullscreen&&(""===e?t.cancelFullScreen():t[e+("ms"===e?"ExitFullscreen":"CancelFullScreen")]())},element:function(){return o.fullscreen?""===e?t.fullscreenElement:t[e+"FullscreenElement"]:null}}}(),o={audio:"canPlayType"in t.createElement("audio"),video:"canPlayType"in t.createElement("video"),fullscreen:r.prefix!==!1,storage:function(){if(!("localStorage"in e))return!1;var t="___test";try{return e.localStorage.setItem(t,t),e.localStorage.removeItem(t),!0}catch(e){return!1}return!1}(),pip:function(){var e=i.getBrowser();return!e.isIPhone&&i.is.function(i.createElement("video").webkitSetPresentationMode)}(),airplay:i.is.function(e.WebKitPlaybackTargetAvailabilityEvent),inline:"playsInline"in t.createElement("video"),mime:function(e,t){var n=e.media;try{if(!i.is.function(n.canPlayType))return!1;if("video"===e.type)switch(t){case"video/webm":return n.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/no/,"");case"video/mp4":return n.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"').replace(/no/,"");case"video/ogg":return n.canPlayType('video/ogg; codecs="theora"').replace(/no/,"")}else if("audio"===e.type)switch(t){case"audio/mpeg":return n.canPlayType("audio/mpeg;").replace(/no/,"");case"audio/ogg":return n.canPlayType('audio/ogg; codecs="vorbis"').replace(/no/,"");case"audio/wav":return n.canPlayType('audio/wav; codecs="1"').replace(/no/,"")}}catch(e){return!1}return!1},textTracks:"textTracks"in t.createElement("video"),passiveListeners:function(){var t=!1;try{var n=Object.defineProperty({},"passive",{get:function(){t=!0}});e.addEventListener("test",null,n)}catch(e){}return t}(),touch:"ontouchstart"in t.documentElement,transitions:!(i.transitionEnd===!1||"matchMedia"in e&&e.matchMedia("(prefers-reduced-motion)").matches)};return n});
\ No newline at end of file +(function(e,t,n){"use strict";"object"==typeof exports?module.exports=n(require):"function"==typeof define&&define.amd?define(n):t[e]=n()}).call(this,"Plyr",this,function(e){"use strict";function t(e,t){function n(e,t,n,i){a.event(e,t,n,a.extend({},i,{plyr:pe}))}function l(e){return pe.elements.container.querySelectorAll(e)}function c(e){return l(e)[0]}function u(e){a.is.string(e)?(a.removeElement(pe.elements[e]),pe.elements[e]=null):a.removeElement(e)}function d(){function e(e){9===e.which&&pe.fullscreen.active&&(e.target!==i||e.shiftKey?e.target===n&&e.shiftKey&&(e.preventDefault(),i.focus()):(e.preventDefault(),n.focus()))}var t=l("input:not([disabled]), button:not([disabled])"),n=t[0],i=t[t.length-1];a.on(pe.elements.container,"keydown",e,!1)}function p(e,t){a.is.string(t)?a.insertElement(e,pe.media,{src:t}):a.is.array(t)&&t.forEach(function(t){a.insertElement(e,pe.media,t)})}function m(){return{url:pe.config.iconUrl,absolute:0===pe.config.iconUrl.indexOf("http")||pe.browser.isIE}}function f(e,t){var n="http://www.w3.org/2000/svg",i=m(),o=(i.absolute?"":i.url)+"#"+pe.config.iconPrefix,s=document.createElementNS(n,"svg");a.setAttributes(s,a.extend(t,{role:"presentation"}));var r=document.createElementNS(n,"use");return r.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",o+"-"+e),s.appendChild(r),s}function g(e){var t=pe.config.i18n[e];switch(e){case"pip":t="PIP";break;case"airplay":t="AirPlay"}return a.createElement("span",{class:pe.config.classes.hidden},t)}function y(e){var t=a.createElement("span",{class:pe.config.classes.menu.value});return t.appendChild(a.createElement("span",{class:pe.config.classes.menu.badge},e)),t}function b(e,t){var n,i,o,s=a.createElement("button");switch(a.is.object(t)||(t={}),"class"in t?t.class.indexOf(pe.config.classes.control)===-1&&(t.class+=" "+pe.config.classes.control):t.class=pe.config.classes.control,e){case"mute":o="toggleMute",n="volume",i="muted";break;case"captions":o="toggleCaptions",n="captions-off",i="captions-on";break;case"fullscreen":o="toggleFullscreen",n="enter-fullscreen",i="exit-fullscreen";break;case"play-large":t.class="plyr__play-large",e="play",o="play",n="play";break;default:o=e,n=e}return a.extend(t,a.getAttributesFromSelector(pe.config.selectors.buttons[e],t)),a.is.string(i)&&s.appendChild(f(i,{class:"icon--"+i})),s.appendChild(f(n)),s.appendChild(g(o)),a.setAttributes(s,t),pe.elements.buttons[e]=s,s}function v(e,t){var n=a.createElement("label",{for:t.id,class:pe.config.classes.hidden},pe.config.i18n[e]),i=a.createElement("input",a.extend(a.getAttributesFromSelector(pe.config.selectors.inputs[e]),{type:"range",min:0,max:100,step:.1,value:0,autocomplete:"off"},t));return pe.elements.inputs[e]=i,{label:n,input:i}}function h(e,t){var n=a.createElement("progress",a.extend(a.getAttributesFromSelector(pe.config.selectors.display[e]),{min:0,max:100,value:0},t));if("volume"!==e){n.appendChild(a.createElement("span",null,"0"));var i="";switch(e){case"played":i=pe.config.i18n.played;break;case"buffer":i=pe.config.i18n.buffered}n.textContent="% "+i.toLowerCase()}return pe.elements.display[e]=n,n}function w(e){var t=a.createElement("span",{class:"plyr__time"});return t.appendChild(a.createElement("span",{class:pe.config.classes.hidden},pe.config.i18n[e])),t.appendChild(a.createElement("span",a.getAttributesFromSelector(pe.config.selectors.display[e]),"00:00")),pe.elements.display[e]=t,t}function k(e){var t=a.createElement("div",a.getAttributesFromSelector(pe.config.selectors.controls.wrapper));if(a.inArray(pe.config.controls,"restart")&&t.appendChild(b("restart")),a.inArray(pe.config.controls,"rewind")&&t.appendChild(b("rewind")),a.inArray(pe.config.controls,"play")&&(t.appendChild(b("play")),t.appendChild(b("pause"))),a.inArray(pe.config.controls,"fast-forward")&&t.appendChild(b("fast-forward")),a.inArray(pe.config.controls,"progress")){var n=a.createElement("span",a.getAttributesFromSelector(pe.config.selectors.progress)),i=v("seek",{id:"plyr-seek-"+e.id});if(n.appendChild(i.label),n.appendChild(i.input),n.appendChild(h("played")),n.appendChild(h("buffer")),pe.config.tooltips.seek){var o=a.createElement("span",{role:"tooltip",class:pe.config.classes.tooltip},"00:00");n.appendChild(o),pe.elements.display.seekTooltip=o}pe.elements.progress=n,t.appendChild(pe.elements.progress)}if(a.inArray(pe.config.controls,"current-time")&&t.appendChild(w("currentTime")),a.inArray(pe.config.controls,"duration")&&t.appendChild(w("duration")),a.inArray(pe.config.controls,"mute")&&t.appendChild(b("mute")),a.inArray(pe.config.controls,"volume")){var s=a.createElement("span",{class:"plyr__volume"}),l={max:10,value:pe.config.volume},c=v("volume",a.extend(l,{id:"plyr-volume-"+e.id}));s.appendChild(c.label),s.appendChild(c.input);var u=h("volume",l);s.appendChild(u),t.appendChild(s)}if(a.inArray(pe.config.controls,"captions")&&t.appendChild(b("captions")),a.inArray(pe.config.controls,"settings")){var d=a.createElement("div",{class:"plyr__menu"});d.appendChild(b("settings",{id:"plyr-settings-toggle-"+e.id,"aria-haspopup":!0,"aria-controls":"plyr-settings-"+e.id,"aria-expanded":!1}));var p=a.createElement("form",{class:"plyr__menu__container",id:"plyr-settings-"+e.id,"aria-hidden":!0,"aria-labelled-by":"plyr-settings-toggle-"+e.id,role:"tablist",tabindex:-1}),m=a.createElement("div"),f=a.createElement("div",{id:"plyr-settings-"+e.id+"-home","aria-hidden":!1,"aria-labelled-by":"plyr-settings-toggle-"+e.id,role:"tabpanel"}),g=a.createElement("ul",{role:"tablist"});pe.config.settings.forEach(function(t){var n=a.createElement("li",{role:"tab",hidden:""}),i=a.createElement("button",a.extend(a.getAttributesFromSelector(pe.config.selectors.buttons.settings),{type:"button",class:pe.config.classes.control+" "+pe.config.classes.control+"--forward",id:"plyr-settings-"+e.id+"-"+t+"-tab","aria-haspopup":!0,"aria-controls":"plyr-settings-"+e.id+"-"+t,"aria-expanded":!1}),pe.config.i18n[t]),o=a.createElement("span",{class:pe.config.classes.menu.value});o.innerHTML=e[t],i.appendChild(o),n.appendChild(i),g.appendChild(n),pe.elements.settings.tabs[t]=n}),f.appendChild(g),m.appendChild(f),pe.config.settings.forEach(function(t){var n=a.createElement("div",{id:"plyr-settings-"+e.id+"-"+t,"aria-hidden":!0,"aria-labelled-by":"plyr-settings-"+e.id+"-"+t+"-tab",role:"tabpanel",tabindex:-1,hidden:""}),i=a.createElement("button",{type:"button",class:pe.config.classes.control+" "+pe.config.classes.control+"--back","aria-haspopup":!0,"aria-controls":"plyr-settings-"+e.id+"-home","aria-expanded":!1},pe.config.i18n[t]);n.appendChild(i);var o=a.createElement("ul");n.appendChild(o),m.appendChild(n),pe.elements.settings.panes[t]=n}),p.appendChild(m),d.appendChild(p),t.appendChild(d),pe.elements.settings.form=p,pe.elements.settings.menu=d}return a.inArray(pe.config.controls,"pip")&&r.pip&&t.appendChild(b("pip")),a.inArray(pe.config.controls,"airplay")&&r.airplay&&t.appendChild(b("airplay")),a.inArray(pe.config.controls,"fullscreen")&&t.appendChild(b("fullscreen")),a.inArray(pe.config.controls,"play-large")&&(pe.elements.buttons.playLarge=b("play-large"),pe.elements.container.appendChild(pe.elements.buttons.playLarge)),pe.elements.controls=t,E(),S(),t}function C(e,t){function n(e){var t="";switch(e){case"hd2160":t="4K";break;case"hd1440":t="WQHD";break;case"hd1080":t="HD";break;case"hd720":t="HD"}return t.length?y(t):null}function i(e){switch(e){case"hd2160":return"2160P";case"hd1440":return"1440P";case"hd1080":return"1080P";case"hd720":return"720P";case"large":return"480P";case"medium":return"360P";case"small":return"240P";default:return"Auto"}}var o=pe.elements.settings.panes.quality.querySelector("ul");pe.elements.settings.tabs.quality.removeAttribute("hidden"),pe.elements.settings.panes.quality.removeAttribute("hidden"),a.emptyElement(o),a.is.array(e)&&!a.is.empty(e)&&e.filter(function(e){return!a.inArray(["tiny","small"],e)}).forEach(function(e){var t=a.createElement("li"),s=a.createElement("label",{class:pe.config.classes.control}),r=a.createElement("input",a.extend(a.getAttributesFromSelector(pe.config.selectors.inputs.quality),{type:"radio",name:"plyr-quality",value:e}));e===pe.config.quality.selected&&(r.checked=!0),s.appendChild(r),s.appendChild(document.createTextNode(i(e)));var l=n(e);a.is.htmlElement(l)&&s.appendChild(l),t.appendChild(s),o.appendChild(t)})}function E(){var e=["start","end","all","reset"],t=pe.elements.settings.panes.loop.querySelector("ul");pe.elements.settings.tabs.loop.removeAttribute("hidden"),pe.elements.settings.panes.loop.removeAttribute("hidden"),a.emptyElement(t),e.forEach(function(e){var n=a.createElement("li"),i=a.createElement("button",a.extend(a.getAttributesFromSelector(pe.config.selectors.buttons.loop),{type:"button",class:pe.config.classes.control,"data-plyr-loop-action":e}),pe.config.i18n[e]);if(a.inArray(["start","end"],e)){var o=y("00:00");i.appendChild(o)}n.appendChild(i),t.appendChild(n)})}function A(){var e=pe.elements.settings.panes.captions.querySelector("ul");if(pe.elements.settings.tabs.captions.removeAttribute("hidden"),pe.elements.settings.panes.captions.removeAttribute("hidden"),a.emptyElement(e),!a.is.empty(pe.captions.tracks)){var t=[].map.call(pe.captions.tracks,function(e){return{language:e.language,badge:!0,label:a.is.empty(e.label)?e.language.toUpperCase():e.label}});t.unshift({language:"off",label:pe.config.i18n.none}),t.forEach(function(t){var n=a.createElement("li"),i=a.createElement("label",{class:pe.config.classes.control}),o=a.createElement("input",a.extend(a.getAttributesFromSelector(pe.config.selectors.inputs.language),{type:"radio",name:"plyr-language",value:t.language}));t.language.toLowerCase()===pe.config.captions.language.toLowerCase()&&(o.checked=!0),i.appendChild(o),i.appendChild(document.createTextNode(t.label||t.language)),t.badge&&i.appendChild(y(t.language.toUpperCase())),n.appendChild(i),e.appendChild(n)})}}function S(e){var t=pe.elements.settings.panes.speed.querySelector("ul");pe.elements.settings.tabs.speed.removeAttribute("hidden"),pe.elements.settings.panes.speed.removeAttribute("hidden"),a.emptyElement(t),a.is.array(e)||(e=pe.config.speed.options),e.forEach(function(e){var n=a.createElement("li"),i=a.createElement("label",{class:pe.config.classes.control}),o=a.createElement("input",a.extend(a.getAttributesFromSelector(pe.config.selectors.inputs.speed),{type:"radio",name:"plyr-speed",value:e}));e===pe.config.speed.selected&&(o.checked=!0),i.appendChild(o),i.insertAdjacentHTML("beforeend",U(e)),n.appendChild(i),t.appendChild(n)})}function T(){if(pe.supported.full&&("audio"!==pe.type||pe.config.fullscreen.allowAudio)&&pe.config.fullscreen.enabled){var e=r.fullscreen;e||pe.config.fullscreen.fallback&&!a.inFrame()?(fe((e?"Native":"Fallback")+" fullscreen enabled"),a.toggleClass(pe.elements.container,pe.config.classes.fullscreen.enabled,!0)):fe("Fullscreen not supported and fallback disabled"),pe.elements.buttons&&pe.elements.buttons.fullscreen&&a.toggleState(pe.elements.buttons.fullscreen,!1),d()}}function x(e){if(a.inArray(["video","vimeo"],pe.type)&&("video"!==pe.type||r.textTracks)&&(a.is.htmlElement(pe.elements.captions)||(pe.elements.captions=a.createElement("div",a.getAttributesFromSelector(pe.config.selectors.captions)),a.insertAfter(pe.elements.captions,pe.elements.wrapper)),pe.captions.tracks=a.is.array(e)?e:pe.media.textTracks,a.toggleClass(pe.elements.container,pe.config.classes.captions.enabled,!a.is.empty(pe.captions.tracks)),!a.is.empty(pe.captions.tracks))){if(_(),"video"===pe.type){var t=pe.config.captions.language.toLowerCase();[].forEach.call(pe.captions.tracks,function(e){a.off(e,"cuechange",F),e.mode="hidden",e.language===t&&(pe.captions.currentTrack=e)}),a.is.track(pe.captions.currentTrack)||(ge("No language found to match "+t+" in tracks"),pe.captions.currentTrack=pe.captions.tracks[0]);var n=pe.captions.currentTrack;a.is.track(n)&&a.inArray(["captions","subtitles"],n.kind)&&(a.on(n,"cuechange",F),n.activeCues&&n.activeCues.length>0&&F(n))}A()}}function P(){return!r.textTracks||a.is.empty(pe.captions.tracks)?"No Subs":pe.captions.enabled?pe.captions.currentTrack.label:"Disabled"}function F(e){a.is.event(e)&&(e=e.target);var t=e.activeCues[0];a.is.cue(t)?I(t.getCueAsHTML()):I()}function I(e){if(a.is.htmlElement(pe.elements.captions)){var t=a.createElement("span");a.emptyElement(pe.elements.captions),a.is.undefined(e)&&(e=""),a.is.string(e)?t.textContent=e.trim():t.appendChild(e),pe.elements.captions.appendChild(t)}else ge("No captions element to render to")}function _(){if(pe.elements.buttons.captions){var e=pe.storage.captions;a.is.boolean(e)?pe.config.captions.active=e:e=pe.config.captions.active,e&&(a.toggleClass(pe.elements.container,pe.config.classes.captions.active,!0),a.toggleState(pe.elements.buttons.captions,!0))}}function N(){if(pe.config.loadSprite){var e=m();e.absolute?(fe("AJAX loading absolute SVG sprite"+(pe.browser.isIE?" (due to IE)":"")),a.loadSprite(e.url,"sprite-plyr")):fe("Sprite will be used as external resource directly")}pe.id=Math.floor(1e4*Math.random());var t=null;t=a.is.string(pe.config.controls)?pe.config.controls:a.is.function(pe.config.controls)?pe.config.controls({id:pe.id,seektime:pe.config.seekTime}):k({id:pe.id,seektime:pe.config.seekTime,speed:U(),quality:"HD",captions:P(),loop:"None"});var n;if(a.is.string(pe.config.selectors.controls.container)&&(n=document.querySelector(pe.config.selectors.controls.container)),a.is.htmlElement(n)||(n=pe.elements.container),a.is.htmlElement(t)?n.appendChild(t):n.insertAdjacentHTML("beforeend",t),a.is.htmlElement(pe.elements.controls)&&L(),pe.config.tooltips.controls)for(var i=l([pe.config.selectors.controls.wrapper," ",pe.config.selectors.labels," .",pe.config.classes.hidden].join("")),o=i.length-1;o>=0;o--){var s=i[o];a.toggleClass(s,pe.config.classes.hidden,!1),a.toggleClass(s,pe.config.classes.tooltip,!0)}}function L(){try{return pe.elements.controls=c(pe.config.selectors.controls.wrapper),pe.elements.buttons={play:l(pe.config.selectors.buttons.play),pause:c(pe.config.selectors.buttons.pause),restart:c(pe.config.selectors.buttons.restart),rewind:c(pe.config.selectors.buttons.rewind),forward:c(pe.config.selectors.buttons.forward),mute:c(pe.config.selectors.buttons.mute),pip:c(pe.config.selectors.buttons.pip),airplay:c(pe.config.selectors.buttons.airplay),settings:c(pe.config.selectors.buttons.settings),captions:c(pe.config.selectors.buttons.captions),fullscreen:c(pe.config.selectors.buttons.fullscreen)},pe.elements.progress=c(pe.config.selectors.progress),pe.elements.inputs={seek:c(pe.config.selectors.inputs.seek),volume:c(pe.config.selectors.inputs.volume)},pe.elements.display={buffer:c(pe.config.selectors.display.buffer),played:c(pe.config.selectors.display.played),volume:c(pe.config.selectors.display.volume),duration:c(pe.config.selectors.display.duration),currentTime:c(pe.config.selectors.display.currentTime)},a.is.htmlElement(pe.elements.progress)&&(pe.elements.display.seekTooltip=pe.elements.progress.querySelector("."+pe.config.classes.tooltip)),!0}catch(e){return ge("It looks like there is a problem with your custom controls HTML",e),M(!0),!1}}function q(){a.toggleClass(pe.elements.container,pe.config.selectors.container.replace(".",""),pe.supported.full)}function M(e){e&&a.inArray(o.html5,pe.type)?pe.media.setAttribute("controls",""):pe.media.removeAttribute("controls")}function V(e){var t=pe.config.i18n.play;if(a.is.string(pe.config.title)&&!a.is.empty(pe.config.title)&&(t+=", "+pe.config.title,pe.elements.container.setAttribute("aria-label",pe.config.title)),pe.supported.full&&(a.is.htmlElement(pe.elements.buttons.play)&&pe.elements.buttons.play.setAttribute("aria-label",t),a.is.htmlElement(pe.elements.buttons.playLarge)&&pe.elements.buttons.playLarge.setAttribute("aria-label",t)),a.is.htmlElement(e)){var n=a.is.string(pe.config.title)&&!a.is.empty(pe.config.title)?pe.config.title:"video";e.setAttribute("title",pe.config.i18n.frameTitle.replace("{title}",n))}}function O(){var e=null;pe.storage={},r.storage&&pe.config.storage.enabled&&(window.localStorage.removeItem("plyr-volume"),e=window.localStorage.getItem(pe.config.storage.key),e&&(/^\d+(\.\d+)?$/.test(e)?D({volume:parseFloat(e)}):pe.storage=JSON.parse(e)))}function D(e){r.storage&&pe.config.storage.enabled&&(a.extend(pe.storage,e),window.localStorage.setItem(pe.config.storage.key,JSON.stringify(pe.storage)))}function j(){return pe.media?(pe.supported.full&&(a.toggleClass(pe.elements.container,pe.config.classes.type.replace("{0}",pe.type),!0),a.inArray(o.embed,pe.type)&&a.toggleClass(pe.elements.container,pe.config.classes.type.replace("{0}","video"),!0),a.toggleClass(pe.elements.container,pe.config.classes.pip.enabled,r.pip&&"video"===pe.type),a.toggleClass(pe.elements.container,pe.config.classes.airplay.enabled,r.airplay&&a.inArray(o.html5,pe.type)),a.toggleClass(pe.elements.container,pe.config.classes.stopped,pe.config.autoplay),a.toggleClass(pe.elements.container,pe.config.classes.isIos,pe.browser.isIos),a.toggleClass(pe.elements.container,pe.config.classes.isTouch,r.touch)),a.inArray(["video","youtube","vimeo"],pe.type)&&(pe.elements.wrapper=a.createElement("div",{class:pe.config.classes.video}),a.wrap(pe.media,pe.elements.wrapper)),void(a.inArray(o.embed,pe.type)&&R())):void ge("No media element found!")}function R(){var e,t=pe.type+"-"+Math.floor(1e4*Math.random());switch(pe.type){case"youtube":e=a.parseYouTubeId(pe.embedId);break;default:e=pe.embedId}for(var n=l('[id^="'+pe.type+'-"]'),i=n.length-1;i>=0;i--)a.removeElement(n[i]);if(a.toggleClass(pe.elements.wrapper,pe.config.classes.embed,!0),"youtube"===pe.type)pe.media.setAttribute("id",t),a.is.object(window.YT)?B(e):(a.injectScript(pe.config.urls.youtube.api),window.onYouTubeReadyCallbacks=window.onYouTubeReadyCallbacks||[],window.onYouTubeReadyCallbacks.push(function(){B(e)}),window.onYouTubeIframeAPIReady=function(){window.onYouTubeReadyCallbacks.forEach(function(e){e()})});else if("vimeo"===pe.type)if(pe.media.setAttribute("id",t),a.is.object(window.Vimeo))Y(e);else{a.injectScript(pe.config.urls.vimeo.api);var o=window.setInterval(function(){a.is.object(window.Vimeo)&&(window.clearInterval(o),Y(e))},50)}else if("soundcloud"===pe.type){var s=a.createElement("iframe");s.loaded=!1,a.on(s,"load",function(){s.loaded=!0}),a.setAttributes(s,{src:"https://w.soundcloud.com/player/?url=https://api.soundcloud.com/tracks/"+e,id:t}),pe.media.appendChild(s),window.SC||a.injectScript(pe.config.urls.soundcloud.api);var r=window.setInterval(function(){window.SC&&s.loaded&&(window.clearInterval(r),W.call(s))},50)}}function H(){pe.supported.full&&(ce(),ue()),V(c("iframe"))}function B(e){pe.embed=new window.YT.Player(pe.media.id,{videoId:e,playerVars:{autoplay:pe.config.autoplay?1:0,controls:pe.supported.full?0:1,rel:0,showinfo:0,iv_load_policy:3,cc_load_policy:pe.config.captions.active?1:0,cc_lang_pref:"en",wmode:"transparent",modestbranding:1,disablekb:1,playsinline:1,origin:window.location.href},events:{onError:function(e){n(pe.elements.container,"error",!0,{code:e.data,embed:e.target})},onPlaybackQualityChange:function(e){var t=e.target,n=t.getPlaybackQuality();console.warn(n)},onReady:function(e){var t=e.target;pe.media.play=function(){t.playVideo(),pe.media.paused=!1},pe.media.pause=function(){t.pauseVideo(),pe.media.paused=!0},pe.media.stop=function(){t.stopVideo(),pe.media.paused=!0},pe.media.duration=t.getDuration(),pe.media.paused=!0,pe.media.currentTime=0,pe.media.muted=t.isMuted();var i=t.getPlaybackRate(),o=t.getAvailablePlaybackRates();console.warn(i,o),pe.config.title=t.getVideoData().title,pe.supported.full&&pe.media.setAttribute("tabindex",-1),H(),n(pe.media,"timeupdate"),n(pe.media,"durationchange"),window.clearInterval(me.buffering),me.buffering=window.setInterval(function(){pe.media.buffered=t.getVideoLoadedFraction(),(null===pe.media.lastBuffered||pe.media.lastBuffered<pe.media.buffered)&&n(pe.media,"progress"),pe.media.lastBuffered=pe.media.buffered,1===pe.media.buffered&&(window.clearInterval(me.buffering),n(pe.media,"canplaythrough"))},200)},onStateChange:function(e){var t=e.target;switch(window.clearInterval(me.playing),e.data){case 0:if(pe.config.loop.active){t.stopVideo(),t.playVideo();break}pe.media.paused=!0,n(pe.media,"ended");break;case 1:pe.media.paused=!1,pe.media.seeking&&n(pe.media,"seeked"),pe.media.seeking=!1,n(pe.media,"play"),n(pe.media,"playing"),me.playing=window.setInterval(function(){pe.media.currentTime=t.getCurrentTime(),n(pe.media,"timeupdate")},100),pe.media.duration!==t.getDuration()&&(pe.media.duration=t.getDuration(),n(pe.media,"durationchange"));var i=t.getAvailableQualityLevels(),o=t.getPlaybackQuality();C(i,o);break;case 2:pe.media.paused=!0,n(pe.media,"pause")}n(pe.elements.container,"statechange",!1,{code:e.data})}}})}function Y(e){pe.embed=new window.Vimeo.Player(pe.media,{id:e,loop:pe.config.loop.active,autoplay:pe.config.autoplay,byline:!1,portrait:!1,title:!1}),pe.media.play=function(){pe.embed.play(),pe.media.paused=!1},pe.media.pause=function(){pe.embed.pause(),pe.media.paused=!0},pe.media.stop=function(){pe.embed.stop(),pe.media.paused=!0},pe.media.paused=!0,pe.media.currentTime=0,H(),pe.embed.getCurrentTime().then(function(e){pe.media.currentTime=e,n(pe.media,"timeupdate")}),pe.embed.getDuration().then(function(e){pe.media.duration=e,n(pe.media,"durationchange")}),pe.embed.getTextTracks().then(function(e){x(e),pe.config.captions.active&&pe.embed.enableTextTrack(pe.config.captions.language.toLowerCase())}),pe.embed.on("cuechange",function(e){var t=null;e.cues.length&&(t=a.stripHTML(e.cues[0].text)),I(t)}),pe.embed.on("loaded",function(){a.is.htmlElement(pe.embed.element)&&pe.supported.full&&pe.embed.element.setAttribute("tabindex",-1)}),pe.embed.on("play",function(){pe.media.paused=!1,n(pe.media,"play"),n(pe.media,"playing")}),pe.embed.on("pause",function(){pe.media.paused=!0,n(pe.media,"pause")}),pe.embed.on("timeupdate",function(e){pe.media.seeking=!1,pe.media.currentTime=e.seconds,n(pe.media,"timeupdate")}),pe.embed.on("progress",function(e){pe.media.buffered=e.percent,n(pe.media,"progress"),1===parseInt(e.percent)&&n(pe.media,"canplaythrough")}),pe.embed.on("seeked",function(){pe.media.seeking=!1,n(pe.media,"seeked"),n(pe.media,"play")}),pe.embed.on("ended",function(){pe.media.paused=!0,n(pe.media,"ended")})}function W(){pe.embed=window.SC.Widget(this),pe.embed.bind(window.SC.Widget.Events.READY,function(){pe.media.play=function(){pe.embed.play(),pe.media.paused=!1},pe.media.pause=function(){pe.embed.pause(),pe.media.paused=!0},pe.media.stop=function(){pe.embed.seekTo(0),pe.embed.pause(),pe.media.paused=!0},pe.media.paused=!0,pe.media.currentTime=0,pe.embed.getDuration(function(e){pe.media.duration=e/1e3,H()}),pe.embed.getPosition(function(e){pe.media.currentTime=e,n(pe.media,"timeupdate")}),pe.embed.bind(window.SC.Widget.Events.PLAY,function(){pe.media.paused=!1,n(pe.media,"play"),n(pe.media,"playing")}),pe.embed.bind(window.SC.Widget.Events.PAUSE,function(){pe.media.paused=!0,n(pe.media,"pause")}),pe.embed.bind(window.SC.Widget.Events.PLAY_PROGRESS,function(e){pe.media.seeking=!1,pe.media.currentTime=e.currentPosition/1e3,n(pe.media,"timeupdate")}),pe.embed.bind(window.SC.Widget.Events.LOAD_PROGRESS,function(e){pe.media.buffered=e.loadProgress,n(pe.media,"progress"),1===parseInt(e.loadProgress)&&n(pe.media,"canplaythrough")}),pe.embed.bind(window.SC.Widget.Events.FINISH,function(){pe.media.paused=!0,n(pe.media,"ended")})})}function U(e){return a.is.number(e)||(e=pe.config.speed.selected),1===e?"Normal":e+"×"}function Q(){a.toggleClass(pe.elements.container,pe.config.classes.playing,!pe.media.paused),a.toggleClass(pe.elements.container,pe.config.classes.stopped,pe.media.paused),pe.toggleControls(pe.media.paused)}function z(e){var t=pe.elements.settings.form,n=pe.elements.buttons.settings,i=a.is.boolean(e)?e:"true"===t.getAttribute("aria-hidden");if(a.is.event(e)){var o=t.contains(e.target),s=e.target===pe.elements.buttons.settings;if(o||!o&&!s&&i)return;s&&e.stopPropagation()}t.setAttribute("aria-hidden",!i),n.setAttribute("aria-expanded",i),i?t.removeAttribute("tabindex"):t.setAttribute("tabindex",-1)}function J(e){var t,n,i=e.cloneNode(!0);return i.style.position="absolute",i.style.opacity=0,i.setAttribute("aria-hidden",!1),[].forEach.call(i.querySelectorAll("input[name]"),function(e){var t=e.getAttribute("name");e.setAttribute("name",t+"-clone")}),e.parentNode.appendChild(i),t=i.scrollWidth,n=i.scrollHeight,a.removeElement(i),{width:t,height:n}}function X(e){var t=pe.elements.settings.menu,n=e.target,i="false"===n.getAttribute("aria-expanded"),o=document.getElementById(n.getAttribute("aria-controls"));if(a.is.htmlElement(o)){var s="tabpanel"===o.getAttribute("role");if(s){var l=t.querySelector('[role="tabpanel"][aria-hidden="false"]'),c=l.parentNode;if([].forEach.call(t.querySelectorAll('[aria-controls="'+l.getAttribute("id")+'"]'),function(e){e.setAttribute("aria-expanded",!1)}),r.transitions&&!r.reducedMotion){c.style.width=l.scrollWidth+"px",c.style.height=l.scrollHeight+"px";var u=J(o),d=function(e){e.target===c&&a.inArray(["width","height"],e.propertyName)&&(c.style.width="",c.style.height="",a.off(c,a.transitionEnd,d))};a.on(c,a.transitionEnd,d),c.style.width=u.width+"px",c.style.height=u.height+"px"}l.setAttribute("aria-hidden",!0),l.setAttribute("tabindex",-1),o.setAttribute("aria-hidden",!i),n.setAttribute("aria-expanded",i),o.removeAttribute("tabindex")}}}function $(){var e=pe.media.muted?0:10*pe.media.volume;pe.supported.full&&(pe.elements.inputs.volume&&(pe.elements.inputs.volume.value=e),pe.elements.display.volume&&(pe.elements.display.volume.value=e)),D({volume:e}),a.toggleClass(pe.elements.container,pe.config.classes.muted,0===e),pe.supported.full&&pe.elements.buttons.mute&&a.toggleState(pe.elements.buttons.mute,0===e)}function G(e){pe.loading="waiting"===e.type,clearTimeout(me.loading),me.loading=setTimeout(function(){a.toggleClass(pe.elements.container,pe.config.classes.loading,pe.loading),pe.toggleControls(pe.loading)},pe.loading?250:0)}function K(e){if(pe.supported.full){var t=pe.elements.display.played,n=0,i=pe.getDuration();if(e)switch(e.type){case"timeupdate":case"seeking":if(pe.elements.controls.pressed)return;n=a.getPercentage(pe.media.currentTime,i),"timeupdate"===e.type&&pe.elements.inputs.seek&&(pe.elements.inputs.seek.value=n);break;case"playing":case"progress":t=pe.elements.display.buffer,n=function(){var e=pe.media.buffered;return e&&e.length?a.getPercentage(e.end(0),i):a.is.number(e)?100*e:0}()}a.is.number(pe.config.loop.start)&&a.is.number(pe.config.loop.end)&&pe.media.currentTime>=pe.config.loop.end&&pe.seek(pe.config.loop.start),Z(t,n)}}function Z(e,t){if(pe.supported.full){if(a.is.undefined(t)&&(t=0),a.is.undefined(e)){if(!a.is.htmlElement(pe.elements.display.buffer))return;e=pe.elements.display.buffer}if(a.is.htmlElement(e)){e.value=t;var n=e.getElementsByTagName("span")[0];a.is.htmlElement(n)&&(n.childNodes[0].nodeValue=t)}}}function ee(e,t){if(t){isNaN(e)&&(e=0);var n=parseInt(e%60),i=parseInt(e/60%60),o=parseInt(e/60/60%60),a=pe.getDuration(),s=parseInt(a/60/60%60)>0;n=("0"+n).slice(-2),i=("0"+i).slice(-2);var r=(s?o+":":"")+i+":"+n;return t.textContent=r,r}}function te(){if(pe.supported.full){var e=pe.getDuration()||0;!pe.elements.display.duration&&pe.config.displayDuration&&pe.media.paused&&ee(e,pe.elements.display.currentTime),pe.elements.display.duration&&ee(e,pe.elements.display.duration),oe()}}function ne(e){ee(pe.media.currentTime,pe.elements.display.currentTime),e&&"timeupdate"===e.type&&pe.media.seeking||K(e)}function ie(e){a.is.number(e)||(e=0);var t=pe.getDuration(),n=a.getPercentage(e,t);pe.elements.progress&&pe.elements.display.played&&(pe.elements.display.played.value=n),pe.elements.buttons&&pe.elements.inputs.seek&&(pe.elements.inputs.seek.value=n)}function oe(e){var t=pe.getDuration();if(pe.config.tooltips.seek&&a.is.htmlElement(pe.elements.inputs.seek)&&a.is.htmlElement(pe.elements.display.seekTooltip)&&0!==t){var n=pe.elements.inputs.seek.getBoundingClientRect(),i=0,o=pe.config.classes.tooltip+"--visible";if(a.is.event(e))i=100/n.width*(e.pageX-n.left);else{if(!a.hasClass(pe.elements.display.seekTooltip,o))return;i=pe.elements.display.seekTooltip.style.left.replace("%","")}i<0?i=0:i>100&&(i=100),ee(t/100*i,pe.elements.display.seekTooltip),pe.elements.display.seekTooltip.style.left=i+"%",a.is.event(e)&&a.inArray(["mouseenter","mouseleave"],e.type)&&a.toggleClass(pe.elements.display.seekTooltip,o,"mouseenter"===e.type)}}function ae(e){function t(){if(pe.embed=null,u("media"),u("captions"),u("wrapper"),pe.elements.container&&pe.elements.container.removeAttribute("class"),"type"in e&&(pe.type=e.type,"video"===pe.type)){var t=e.sources[0];"type"in t&&a.inArray(o.embed,t.type)&&(pe.type=t.type)}switch(pe.supported=a.checkSupport(pe.type,pe.config.inline),pe.type){case"video":pe.media=a.createElement("video");break;case"audio":pe.media=a.createElement("audio");break;case"youtube":case"vimeo":case"soundcloud":pe.media=a.createElement("div"),pe.embedId=e.sources[0].src}a.prependChild(pe.elements.container,pe.media),a.is.boolean(e.autoplay)&&(pe.config.autoplay=e.autoplay),a.inArray(o.html5,pe.type)&&(pe.config.crossorigin&&pe.media.setAttribute("crossorigin",""),pe.config.autoplay&&pe.media.setAttribute("autoplay",""),"poster"in e&&pe.media.setAttribute("poster",e.poster),pe.config.loop.active&&pe.media.setAttribute("loop",""),pe.config.inline&&pe.media.setAttribute("playsinline","")),a.toggleClass(pe.elements.container,pe.config.classes.fullscreen.active,pe.fullscreen.active),a.toggleClass(pe.elements.container,pe.config.classes.captions.active,pe.captions.enabled),q(),a.inArray(o.html5,pe.type)&&p("source",e.sources),j(),a.inArray(o.html5,pe.type)&&("tracks"in e&&p("track",e.tracks),pe.media.load()),(a.inArray(o.html5,pe.type)||a.inArray(o.embed,pe.type)&&!pe.supported.full)&&(ce(),ue()),pe.config.title=e.title,V()}return a.is.object(e)&&"sources"in e&&e.sources.length?(pe.ready=!1,pe.stop(),ie(),Z(),le(),void pe.destroy(t,!1)):void ge("Invalid source format")}function se(){function e(){var e=pe.togglePlay(),t=pe.elements.buttons[e?"play":"pause"],n=pe.elements.buttons[e?"pause":"play"];if(n){var i=a.hasClass(t,pe.config.classes.tabFocus);setTimeout(function(){a.is.htmlElement(n)&&n.focus(),i&&(a.toggleClass(t,pe.config.classes.tabFocus,!1),a.toggleClass(n,pe.config.classes.tabFocus,!0))},100)}}function t(e){return e.keyCode?e.keyCode:e.which}function n(e){a.toggleClass(l("."+pe.config.classes.tabFocus),pe.config.classes.tabFocus,!1),pe.elements.container.contains(e)&&a.toggleClass(e,pe.config.classes.tabFocus,!0)}function i(n){function i(){var e=pe.media.duration;a.is.number(e)&&pe.seek(e/10*(o-48))}var o=t(n),s="keydown"===n.type,l=s&&o===u;if(a.is.number(o))if(s){var c=[48,49,50,51,52,53,54,56,57,32,75,38,40,77,39,37,70,67,73,76,79],d=[38,40];if(a.inArray(d,o)){var p=a.getFocusElement();if(a.is.htmlElement(p)&&"radio"===a.getFocusElement().type)return}switch(a.inArray(c,o)&&(n.preventDefault(),n.stopPropagation()),o){case 48:case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:l||i();break;case 32:case 75:l||e();break;case 38:pe.increaseVolume();break;case 40:pe.decreaseVolume();break;case 77:l||pe.toggleMute();break;case 39:pe.forward();break;case 37:pe.rewind();break;case 70:pe.toggleFullscreen();break;case 67:l||pe.toggleCaptions();break;case 73:pe.loop("start");break;case 76:pe.loop();break;case 79:pe.loop("end")}!r.fullscreen&&pe.fullscreen.active&&27===o&&pe.toggleFullscreen(),u=o}else u=null}var o=pe.browser.isIE?"change":"input";if(pe.config.keyboardShortcuts.focused){var u=null;pe.config.keyboardShortcuts.global&&a.on(window,"keydown keyup",function(e){var n=t(e),o=a.getFocusElement(),s=[48,49,50,51,52,53,54,56,57,75,77,70,67,73,76,79];!a.inArray(s,n)||a.is.htmlElement(o)&&a.matches(o,pe.config.selectors.editable)||i(e)},!1),a.on(pe.elements.container,"keydown keyup",i,!1)}a.on(window,"keyup",function(e){var i=t(e),o=a.getFocusElement();9===i&&n(o); +}),a.on(document.body,"click",function(){a.toggleClass(c("."+pe.config.classes.tabFocus),pe.config.classes.tabFocus,!1)});for(var d in pe.elements.buttons){var p=pe.elements.buttons[d];a.on(p,"blur",function(){a.toggleClass(p,"tab-focus",!1)})}var m=function(e,t,n){a.is.function(t)&&t.call(this,e),a.is.function(n)&&n.call(this,e)};a.proxy(pe.elements.buttons.play,"click",pe.config.listeners.play,e),a.proxy(pe.elements.buttons.playLarge,"click",pe.config.listeners.play,e),a.proxy(pe.elements.buttons.pause,"click",pe.config.listeners.pause,e),a.proxy(pe.elements.buttons.restart,"click",pe.config.listeners.restart,function(){pe.restart()}),a.proxy(pe.elements.buttons.rewind,"click",pe.config.listeners.rewind,function(){pe.rewind()}),a.proxy(pe.elements.buttons.forward,"click",pe.config.listeners.forward,function(){pe.forward()}),a.proxy(pe.elements.buttons.mute,"click",pe.config.listeners.mute,function(){pe.toggleMute()}),a.proxy(pe.elements.buttons.captions,"click",pe.config.listeners.captions,function(){pe.toggleCaptions()}),a.proxy(pe.elements.buttons.fullscreen,"click",pe.config.listeners.fullscreen,function(e){pe.toggleFullscreen(e)}),a.proxy(pe.elements.buttons.pip,"click",pe.config.listeners.pip,function(){pe.togglePictureInPicture()}),a.proxy(pe.elements.buttons.airplay,"click",pe.config.listeners.airplay,function(){pe.airPlay()}),a.on(pe.elements.buttons.settings,"click",z),a.on(document.documentElement,"click",z),a.on(pe.elements.settings.form,"click",X),a.on(pe.elements.settings.form,"click",function(e){a.matches(e.target,pe.config.selectors.inputs.language)?m.call(this,e,pe.config.listeners.language,function(){pe.language(e.target.value.toLowerCase())}):a.matches(e.target,pe.config.selectors.inputs.quality)?m.call(this,e,pe.config.listeners.quality,function(){ge("Set quality")}):a.matches(e.target,pe.config.selectors.inputs.speed)?m.call(this,e,pe.config.listeners.speed,function(){pe.setSpeed(parseFloat(e.target.value))}):a.matches(e.target,pe.config.selectors.buttons.loop)&&m.call(this,e,pe.config.listeners.loop,function(){var t=e.target.getAttribute("data-loop__value")||e.target.getAttribute("data-loop__type");a.inArray(["start","end","all","none"],t)&&pe.loop(t)})}),a.proxy(pe.elements.inputs.seek,o,pe.config.listeners.seek,function(e){var t=pe.getDuration();pe.seek(e.target.value/e.target.max*t)}),a.proxy(pe.elements.inputs.volume,o,pe.config.listeners.volume,function(){pe.setVolume(event.target.value)}),a.on(pe.elements.progress,"mouseenter mouseleave mousemove",oe),pe.config.hideControls&&(a.on(pe.elements.container,"mouseenter mouseleave mousemove touchstart touchend touchcancel touchmove enterfullscreen",function(e){pe.toggleControls(e)}),a.on(pe.elements.controls,"mouseenter mouseleave",function(e){pe.elements.controls.hover="mouseenter"===e.type}),a.on(pe.elements.controls,"mousedown mouseup touchstart touchend touchcancel",function(e){pe.elements.controls.pressed=a.inArray(["mousedown","touchstart"],e.type)}),a.on(pe.elements.controls,"focus blur",function(e){pe.toggleControls(e)},!0,!0)),a.proxy(pe.elements.inputs.volume,"wheel",pe.config.listeners.volume,function(e){var t=e.webkitDirectionInvertedFromDevice,n=.2,i=0;(e.deltaY<0||e.deltaX>0)&&(t?(pe.decreaseVolume(n),i=-1):(pe.increaseVolume(n),i=1)),(e.deltaY>0||e.deltaX<0)&&(t?(pe.increaseVolume(n),i=1):(pe.decreaseVolume(n),i=-1)),(1===i&&pe.media.volume<1||i===-1&&pe.media.volume>0)&&e.preventDefault()},!1),r.fullscreen&&a.on(document,s.eventType,function(e){pe.toggleFullscreen(e)})}function re(){if(a.on(pe.media,"timeupdate seeking",ne),a.on(pe.media,"durationchange loadedmetadata",te),a.on(pe.media,"ended",function(){"video"===pe.type&&pe.config.showPosterOnEnd&&("video"===pe.type&&I(),pe.restart(),pe.media.load())}),a.on(pe.media,"progress playing",K),a.on(pe.media,"volumechange",$),a.on(pe.media,"play pause ended",Q),a.on(pe.media,"waiting canplay seeked",G),pe.config.clickToPlay&&"audio"!==pe.type){var e=c("."+pe.config.classes.video);if(!e)return;e.style.cursor="pointer",a.on(e,"click",function(){pe.config.hideControls&&r.touch&&!pe.media.paused||(pe.media.paused?pe.play():pe.media.ended?(pe.restart(),pe.play()):pe.pause())})}pe.config.disableContextMenu&&a.on(pe.media,"contextmenu",function(e){e.preventDefault()},!1),a.on(pe.media,pe.config.events.concat(["keyup","keydown"]).join(" "),function(e){n(pe.elements.container,e.type,!0)})}function le(){if(a.inArray(o.html5,pe.type)){for(var e=pe.media.querySelectorAll("source"),t=0;t<e.length;t++)a.removeElement(e[t]);pe.media.setAttribute("src","https://cdn.selz.com/plyr/blank.mp4"),pe.media.load(),fe("Cancelled network requests")}}function ce(){return pe.supported.full?(a.is.htmlElement(pe.elements.controls)||(N(),se()),void(a.is.htmlElement(pe.elements.controls)&&(re(),M(),T(),x(),pe.setVolume(),$(),pe.setSpeed(),pe.loop(),ne(),Q()))):(ge("Basic support only",pe.type),u("controls"),u("buttons.play"),void M(!0))}function ue(){n(pe.elements.container,"ready",!0),pe.config.autoplay&&pe.play()}function de(e){if(null===e||a.is.undefined(e)||!a.is.htmlElement(e))return void ye("Setup failed: no suitable element passed");if(!pe.config.enabled)return void ye("Setup failed: disabled by config");if(!a.checkSupport().basic)return void ye("Setup failed: no support");if(e.plyr)return fe("Target already setup"),e.plyr;var t=e.tagName.toLowerCase();switch(t){case"div":if(pe.type=e.getAttribute("data-type"),pe.embedId=e.getAttribute("data-video-id"),a.is.empty(pe.type))return void ye("Setup failed: embed type missing");if(a.is.empty(pe.embedId))return void ye("Setup failed: video id missing");e.removeAttribute("data-type"),e.removeAttribute("data-video-id");break;case"iframe":break;case"video":case"audio":pe.type=t,pe.config.crossorigin=null!==e.getAttribute("crossorigin"),pe.config.autoplay=pe.config.autoplay||null!==e.getAttribute("autoplay"),pe.config.inline=null!==e.getAttribute("playsinline"),pe.config.loop.active=pe.config.loop||null!==e.getAttribute("loop");break;default:return ye("Setup failed: unsupported type"),!1}if(pe.browser=a.getBrowser(),O(),pe.supported=a.checkSupport(pe.type,pe.config.inline),!pe.supported.basic)return ye("Setup failed: no support"),!1;if(pe.elements.container=a.wrap(e,a.createElement("div")),pe.original=e.cloneNode(!0),pe.elements.container.setAttribute("tabindex",0),q(),fe(pe.browser.name+" "+pe.browser.version),j(),pe.config.debug){var n=pe.config.events.concat(["setup","statechange","enterfullscreen","exitfullscreen","captionsenabled","captionsdisabled"]);a.on(pe.elements.container,n.join(" "),function(e){fe(["event:",e.type].join(" ").trim())})}(a.inArray(o.html5,pe.type)||a.inArray(o.embed,pe.type)&&!pe.supported.full)&&(ce(),ue(),V())}var pe=this,me={};pe.media=e,a.is.string(pe.media)&&(pe.media=document.querySelectorAll(pe.media)),(window.jQuery&&pe.media instanceof jQuery||a.is.nodeList(pe.media)||a.is.array(pe.media))&&(pe.media=pe.media[0]),pe.config=a.extend({},i,t,function(){try{return JSON.parse(pe.media.getAttribute("data-plyr"))}catch(e){}}()),pe.elements={container:null,buttons:{},display:{},progress:{},inputs:{},settings:{menu:null,panes:{},tabs:{}},captions:null},pe.captions={enabled:!1,captions:[],tracks:[],currentTrack:null},pe.fullscreen={active:!1};var fe=function(){},ge=function(){},ye=function(){};pe.config.debug&&"console"in window&&(fe=console.log,ge=console.warn,ye=console.error,fe("Debugging enabled")),fe("Config",pe.config),fe("Support",r),pe.core={getElement:c,getElements:l,trigger:n,setCaption:I,setupCaptions:x,toggleNativeControls:M,updateTimeDisplay:ee,updateSeekDisplay:ie,updateSource:ae,updateStorage:D,timers:me,support:r,log:fe,warn:ge,error:ye},de(pe.media)}var n={x:0,y:0},i={enabled:!0,title:"",debug:!1,autoplay:!1,seekTime:10,volume:10,duration:null,displayDuration:!0,loadSprite:!0,iconPrefix:"plyr",iconUrl:"https://cdn.plyr.io/2.0.10/plyr.svg",clickToPlay:!0,hideControls:!0,showPosterOnEnd:!1,disableContextMenu:!0,quality:{default:"auto",selected:"auto"},loop:{active:!1,start:0,end:null,indicator:{start:0,end:0}},speed:{selected:1,options:[.25,.5,.75,1,1.25,1.5,2]},keyboardShortcuts:{focused:!0,global:!1},tooltips:{controls:!1,seek:!0},selectors:{editable:"input, textarea, select, [contenteditable]",container:".plyr",controls:{container:null,wrapper:".plyr__controls"},labels:"[data-plyr]",buttons:{play:'[data-plyr="play"]',pause:'[data-plyr="pause"]',restart:'[data-plyr="restart"]',rewind:'[data-plyr="rewind"]',forward:'[data-plyr="fast-forward"]',mute:'[data-plyr="mute"]',captions:'[data-plyr="captions"]',fullscreen:'[data-plyr="fullscreen"]',pip:'[data-plyr="pip"]',airplay:'[data-plyr="airplay"]',settings:'[data-plyr="settings"]',loop:'[data-plyr="loop"]'},inputs:{seek:'[data-plyr="seek"]',volume:'[data-plyr="volume"]',speed:'[data-plyr="speed"]',language:'[data-plyr="language"]',quality:'[data-plyr="quality"]'},display:{currentTime:".plyr__time--current",duration:".plyr__time--duration",buffer:".plyr__progress--buffer",played:".plyr__progress--played",loop:".plyr__progress--loop",volume:".plyr__volume--display"},progress:".plyr__progress",captions:".plyr__captions",menu:{quality:".js-plyr__menu__list--quality"}},classes:{video:"plyr__video-wrapper",embed:"plyr__video-embed",control:"plyr__control",type:"plyr--{0}",stopped:"plyr--stopped",playing:"plyr--playing",muted:"plyr--muted",loading:"plyr--loading",hover:"plyr--hover",tooltip:"plyr__tooltip",hidden:"plyr__sr-only",hideControls:"plyr--hide-controls",isIos:"plyr--is-ios",isTouch:"plyr--is-touch",menu:{value:"plyr__menu__value",badge:"plyr__badge"},captions:{enabled:"plyr--captions-enabled",active:"plyr--captions-active"},fullscreen:{enabled:"plyr--fullscreen-enabled",active:"plyr--fullscreen-active"},pip:{enabled:"plyr--pip-enabled",active:"plyr--pip-active"},airplay:{enabled:"plyr--airplay-enabled",active:"plyr--airplay-active"},tabFocus:"tab-focus"},captions:{active:!1,language:window.navigator.language.split("-")[0]},fullscreen:{enabled:!0,fallback:!0,allowAudio:!1},storage:{enabled:!0,key:"plyr"},controls:["play-large","play","progress","current-time","mute","volume","captions","settings","pip","airplay","fullscreen"],settings:["captions","quality","speed","loop"],i18n:{restart:"Restart",rewind:"Rewind {seektime} secs",play:"Play",pause:"Pause",forward:"Forward {seektime} secs",seek:"Seek",played:"Played",buffered:"Buffered",currentTime:"Current time",duration:"Duration",volume:"Volume",toggleMute:"Toggle Mute",toggleCaptions:"Toggle Captions",toggleFullscreen:"Toggle Fullscreen",frameTitle:"Player for {title}",captions:"Captions",settings:"Settings",speed:"Speed",quality:"Quality",loop:"Loop",start:"Start",end:"End",all:"All",reset:"Reset",none:"None"},urls:{vimeo:{api:"https://player.vimeo.com/api/player.js"},youtube:{api:"https://www.youtube.com/iframe_api"},soundcloud:{api:"https://w.soundcloud.com/player/api.js"}},listeners:{seek:null,play:null,pause:null,restart:null,rewind:null,forward:null,mute:null,volume:null,captions:null,fullscreen:null,pip:null,airplay:null,speed:null,quality:null,loop:null,language:null},events:["ended","progress","stalled","playing","waiting","canplay","canplaythrough","loadstart","loadeddata","loadedmetadata","timeupdate","volumechange","play","pause","error","seeking","seeked","emptied"],logPrefix:""},o={embed:["youtube","vimeo","soundcloud"],html5:["video","audio"]},a={is:{object:function(e){return null!==e&&"object"==typeof e&&e.constructor===Object},array:function(e){return null!==e&&Array.isArray(e)},number:function(e){return null!==e&&("number"==typeof e&&!isNaN(e-0)||"object"==typeof e&&e.constructor===Number)},string:function(e){return null!==e&&("string"==typeof e||"object"==typeof e&&e.constructor===String)},boolean:function(e){return null!==e&&"boolean"==typeof e},nodeList:function(e){return null!==e&&e instanceof NodeList},htmlElement:function(e){return null!==e&&e instanceof HTMLElement},function:function(e){return null!==e&&"function"==typeof e},event:function(e){return null!==e&&e instanceof Event},cue:function(e){return null!==e&&(e instanceof window.TextTrackCue||e instanceof window.VTTCue)},track:function(e){return null!==e&&e instanceof window.TextTrack},undefined:function(e){return null!==e&&"undefined"==typeof e},empty:function(e){return null===e||this.undefined(e)||(this.string(e)||this.array(e)||this.nodeList(e))&&0===e.length||this.object(e)&&0===Object.keys(e).length}},getBrowser:function(){var e,t,n,i=navigator.userAgent,o=navigator.appName,a=""+parseFloat(navigator.appVersion),s=parseInt(navigator.appVersion,10),r=!1,l=!1,c=!1,u=!1;return navigator.appVersion.indexOf("Windows NT")!==-1&&navigator.appVersion.indexOf("rv:11")!==-1?(r=!0,o="IE",a="11"):(t=i.indexOf("MSIE"))!==-1?(r=!0,o="IE",a=i.substring(t+5)):(t=i.indexOf("Chrome"))!==-1?(c=!0,o="Chrome",a=i.substring(t+7)):(t=i.indexOf("Safari"))!==-1?(u=!0,o="Safari",a=i.substring(t+7),(t=i.indexOf("Version"))!==-1&&(a=i.substring(t+8))):(t=i.indexOf("Firefox"))!==-1?(l=!0,o="Firefox",a=i.substring(t+8)):(e=i.lastIndexOf(" ")+1)<(t=i.lastIndexOf("/"))&&(o=i.substring(e,t),a=i.substring(t+1),o.toLowerCase()===o.toUpperCase()&&(o=navigator.appName)),(n=a.indexOf(";"))!==-1&&(a=a.substring(0,n)),(n=a.indexOf(" "))!==-1&&(a=a.substring(0,n)),s=parseInt(""+a,10),isNaN(s)&&(a=""+parseFloat(navigator.appVersion),s=parseInt(navigator.appVersion,10)),{name:o,version:s,isIE:r,isOldIE:r&&s<=9,isFirefox:l,isChrome:c,isSafari:u,isIPhone:/(iPhone|iPod)/gi.test(navigator.platform),isIos:/(iPad|iPhone|iPod)/gi.test(navigator.platform)}},checkSupport:function(e,t){var n=!1,i=!1,o=a.getBrowser(),s=o.isIPhone&&t&&r.inline;switch(e){case"video":n=r.video,i=n&&!o.isOldIE&&(!o.isIPhone||s);break;case"audio":n=r.audio,i=n&&!o.isOldIE;break;case"youtube":n=r.video,i=n&&!o.isOldIE&&(!o.isIPhone||s);break;case"vimeo":case"soundcloud":n=!0,i=!o.isOldIE&&!o.isIos;break;default:n=r.audio&&r.video,i=n&&!o.isOldIE}return{basic:n,full:i}},injectScript:function(e){if(!document.querySelectorAll('script[src="'+e+'"]').length){var t=document.createElement("script");t.src=e;var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(t,n)}},inFrame:function(){try{return window.self!==window.top}catch(e){return!0}},inArray:function(e,t){return a.is.array(e)&&e.indexOf(t)!==-1},replaceAll:function(e,t,n){return e.replace(new RegExp(t.replace(/([.*+?\^=!:${}()|\[\]\/\\])/g,"\\$1"),"g"),n)},wrap:function(e,t){e.length||(e=[e]);for(var n=e.length-1;n>=0;n--){var i=n>0?t.cloneNode(!0):t,o=e[n],a=o.parentNode,s=o.nextSibling;return i.appendChild(o),s?a.insertBefore(i,s):a.appendChild(i),i}},removeElement:function(e){a.is.htmlElement(e)&&a.is.htmlElement(e.parentNode)&&e.parentNode.removeChild(e)},prependChild:function(e,t){e.insertBefore(t,e.firstChild)},insertAfter:function(e,t){t.parentNode.insertBefore(e,t.nextSibling)},createElement:function(e,t,n){var i=document.createElement(e);return a.is.object(t)&&a.setAttributes(i,t),a.is.string(n)&&(i.textContent=n),i},insertElement:function(e,t,n,i){var o=a.createElement(e,n,i);a.prependChild(t,o)},emptyElement:function(e){for(var t=e.childNodes.length;t--;)e.removeChild(e.lastChild)},setAttributes:function(e,t){for(var n in t)e.setAttribute(n,t[n])},getAttributesFromSelector:function(e,t){if(!a.is.string(e)||a.is.empty(e))return{};var n={};return e.split(",").forEach(function(e){e=e.trim();var i=e.charAt(0);switch(i){case".":var o=e.replace(".","");a.is.object(t)&&a.is.string(t.class)&&(t.class+=" "+o),n.class=o;break;case"#":n.id=e.replace("#","");break;case"[":e=e.replace(/[\[\]]/g,"");var s=e.split("="),r=s[0],l=s.length>1?s[1].replace(/[\"\']/g,""):"";n[r]=l}}),n},toggleClass:function(e,t,n){if(e)if(e.classList)e.classList[n?"add":"remove"](t);else{var i=(" "+e.className+" ").replace(/\s+/g," ").replace(" "+t+" ","");e.className=i+(n?" "+t:"")}},hasClass:function(e,t){return!!e&&(e.classList?e.classList.contains(t):new RegExp("(\\s|^)"+t+"(\\s|$)").test(e.className))},matches:function(e,t){var n=Element.prototype,i=n.matches||n.webkitMatchesSelector||n.mozMatchesSelector||n.msMatchesSelector||function(e){return[].indexOf.call(document.querySelectorAll(e),this)!==-1};return i.call(e,t)},getFocusElement:function(){var e=document.activeElement;return e=e&&e!==document.body?document.querySelector(":focus"):null},proxy:function(e,t,n,i,o,s){a.on(e,t,function(t){n&&n.apply(e,[t]),i.apply(e,[t])},o,s)},toggleListener:function(e,t,n,i,o,s){if(t=t.split(" "),a.is.boolean(s)||(s=!1),a.is.boolean(o)||(o=!0),e instanceof NodeList){var l=1===arguments.length?[arguments[0]]:Array.apply(null,arguments);return l.shift(),void[].forEach.call(e,function(e){e instanceof Node&&a.toggleListener.apply(null,[e].concat(l))})}var c=s;r.passiveListeners&&(c={passive:o,capture:s}),t.forEach(function(t){e[i?"addEventListener":"removeEventListener"](t,n,c)})},on:function(e,t,n,i,o){a.is.undefined(e)||a.toggleListener(e,t,n,!0,i,o)},off:function(e,t,n,i,o){a.is.undefined(e)||a.toggleListener(e,t,n,!1,i,o)},event:function(e,t,n,i){if(e&&t){a.is.boolean(n)||(n=!1);var o;a.is.function(window.CustomEvent)?o=window.CustomEvent:(o=function(e,t){t=t||{bubbles:!1,cancelable:!1,detail:void 0};var n=document.createEvent("CustomEvent");return n.initCustomEvent(e,t.bubbles,t.cancelable,t.detail),n},o.prototype=window.Event.prototype);var s=new o(t,{bubbles:n,detail:i});e.dispatchEvent(s)}},toggleState:function(e,t){if(e)return t=a.is.boolean(t)?t:!e.getAttribute("aria-pressed"),e.setAttribute("aria-pressed",t),t},getPercentage:function(e,t){return 0===e||0===t||isNaN(e)||isNaN(t)?0:(e/t*100).toFixed(2)},extend:function(){var e=arguments;if(e.length){if(1===e.length)return e[0];var t=Array.prototype.shift.call(e);a.is.object(t)||(t={});for(var n=e.length,i=0;i<n;i++){var o=e[i];a.is.object(o)||(o={});for(var s in o)o[s]&&o[s].constructor&&o[s].constructor===Object?(t[s]=t[s]||{},a.extend(t[s],o[s])):t[s]=o[s]}return t}},parseYouTubeId:function(e){var t=/^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/;return e.match(t)?RegExp.$2:e},stripHTML:function(e){var t=document.createDocumentFragment(),n=document.createElement("div");return t.appendChild(n),n.innerHTML=e,t.firstChild.innerText},loadSprite:function(e,t){function n(e,t){e.innerHTML=t,document.body.insertBefore(e,document.body.childNodes[0])}if("string"==typeof e){var i="cache-",o="string"==typeof t,a=!1;if(!o||!document.querySelectorAll("#"+t).length){var s=document.createElement("div");if(s.setAttribute("hidden",""),o&&s.setAttribute("id",t),r.storage){var l=window.localStorage.getItem(i+t);if(a=null!==l){var c=JSON.parse(l);n(s,c.content)}}var u=new XMLHttpRequest;if(!("withCredentials"in u))return;u.open("GET",e,!0),u.onload=function(){r.storage&&window.localStorage.setItem(i+t,JSON.stringify({content:u.responseText})),n(s,u.responseText)},u.send()}}},transitionEnd:function(){var e=document.createElement("span"),t={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var n in t)if(void 0!==e.style[n])return t[n];return!1}()},s=function(){var e=function(){var e=!1;return a.is.function(document.cancelFullScreen)?e="":["webkit","o","moz","ms","khtml"].some(function(t){return a.is.function(document[t+"CancelFullScreen"])?(e=t,!0):a.is.function(document.msExitFullscreen)&&document.msFullscreenEnabled?(e="ms",!0):void 0}),e}();return{prefix:e,eventType:"ms"===e?"MSFullscreenChange":e+"fullscreenchange",isFullScreen:function(t){if(!r.fullscreen)return!1;switch(a.is.undefined(t)&&(t=document.body),this.prefix){case"":return document.fullscreenElement===t;case"moz":return document.mozFullScreenElement===t;default:return document[e+"FullscreenElement"]===t}},requestFullScreen:function(t){return!!r.fullscreen&&(a.is.htmlElement(t)||(t=document.body),""===e?t.requestFullScreen():t[e+("ms"===e?"RequestFullscreen":"RequestFullScreen")]())},cancelFullScreen:function(){return!!r.fullscreen&&(""===e?document.cancelFullScreen():document[e+("ms"===e?"ExitFullscreen":"CancelFullScreen")]())},element:function(){return r.fullscreen?""===e?document.fullscreenElement:document[e+"FullscreenElement"]:null}}}(),r={audio:"canPlayType"in document.createElement("audio"),video:"canPlayType"in document.createElement("video"),fullscreen:s.prefix!==!1,storage:function(){if(!("localStorage"in window))return!1;var e="___test";try{return window.localStorage.setItem(e,e),window.localStorage.removeItem(e),!0}catch(e){return!1}return!1}(),pip:function(){var e=a.getBrowser();return!e.isIPhone&&a.is.function(a.createElement("video").webkitSetPresentationMode)}(),airplay:a.is.function(window.WebKitPlaybackTargetAvailabilityEvent),inline:"playsInline"in document.createElement("video"),mime:function(e,t){var n=e.media;try{if(!a.is.function(n.canPlayType))return!1;if("video"===e.type)switch(t){case"video/webm":return n.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/no/,"");case"video/mp4":return n.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"').replace(/no/,"");case"video/ogg":return n.canPlayType('video/ogg; codecs="theora"').replace(/no/,"")}else if("audio"===e.type)switch(t){case"audio/mpeg":return n.canPlayType("audio/mpeg;").replace(/no/,"");case"audio/ogg":return n.canPlayType('audio/ogg; codecs="vorbis"').replace(/no/,"");case"audio/wav":return n.canPlayType('audio/wav; codecs="1"').replace(/no/,"")}}catch(e){return!1}return!1},textTracks:"textTracks"in document.createElement("video"),passiveListeners:function(){var e=!1;try{var t=Object.defineProperty({},"passive",{get:function(){e=!0}});window.addEventListener("test",null,t)}catch(e){}return e}(),touch:"ontouchstart"in document.documentElement,transitions:a.transitionEnd!==!1,reducedMotion:"matchMedia"in window&&window.matchMedia("(prefers-reduced-motion)").matches};return t.prototype.play=function(){var e=this;return"play"in e.media&&e.media.play(),e},t.prototype.pause=function(){var e=this;return"pause"in e.media&&e.media.pause(),e},t.prototype.togglePlay=function(e){var t=this;return a.is.boolean(e)||(e=t.media.paused),e?t.play():t.pause(),e},t.prototype.stop=function(){var e=this;return e.restart(),e.pause(),e},t.prototype.restart=function(){var e=this;return e.seek(),e},t.prototype.rewind=function(e){var t=this;return a.is.number(e)||(e=t.config.seekTime),t.seek(t.media.currentTime-e),t},t.prototype.forward=function(e){var t=this;return a.is.number(e)||(e=t.config.seekTime),t.seek(t.media.currentTime+e),t},t.prototype.seek=function(e){var t=this,n=0,i=t.media.paused,s=t.getDuration();a.is.number(e)&&(n=e),n<0?n=0:n>s&&(n=s),t.core.updateSeekDisplay(n);try{t.media.currentTime=n.toFixed(4)}catch(e){}if(a.inArray(o.embed,t.type)){switch(t.type){case"youtube":t.embed.seekTo(n);break;case"vimeo":t.embed.setCurrentTime(n.toFixed(0));break;case"soundcloud":t.embed.seekTo(1e3*n)}i&&t.pause(),t.core.trigger(t.media,"timeupdate"),t.media.seeking=!0,t.core.trigger(t.media,"seeking")}return t.core.log("Seeking to "+t.media.currentTime+" seconds"),t},t.prototype.setVolume=function(e){var t=this,n=10,i=0;if(a.is.undefined(e)&&(e=t.storage.volume),(null===e||isNaN(e))&&(e=t.config.volume),e>n&&(e=n),e<i&&(e=i),t.media.volume=parseFloat(e/n),t.elements.display.volume&&(t.elements.display.volume.value=e),a.inArray(o.embed,t.type)){switch(t.type){case"youtube":t.embed.setVolume(100*t.media.volume);break;case"vimeo":case"soundcloud":t.embed.setVolume(t.media.volume)}t.core.trigger(t.media,"volumechange")}return 0===e?t.media.muted=!0:t.media.muted&&e>0&&t.toggleMute(),t},t.prototype.increaseVolume=function(e){var t=this,n=t.media.muted?0:10*t.media.volume;return a.is.number(e)||(e=1),t.setVolume(n+e),t},t.prototype.decreaseVolume=function(e){var t=this,n=t.media.muted?0:10*t.media.volume;return a.is.number(e)||(e=1),t.setVolume(n-e),t},t.prototype.toggleMute=function(e){var t=this;if(a.is.boolean(e)||(e=!t.media.muted),a.toggleState(t.elements.buttons.mute,e),t.media.muted=e,0===t.media.volume&&t.volume(t.config.volume),a.inArray(o.embed,t.type)){switch(t.type){case"youtube":t.embed[t.media.muted?"mute":"unMute"]();break;case"vimeo":case"soundcloud":t.embed.setVolume(t.media.muted?0:parseFloat(t.config.volume/10))}t.core.trigger(t.media,"volumechange")}return t},t.prototype.setSpeed=function(e){var t=this;return a.is.number(e)||(e=parseFloat(t.storage.speed||t.config.speed.selected)),e<.1&&(e=.1),e>2&&(e=2),a.is.array(t.config.speed.options)?(t.config.speed.selected=e,t.media.playbackRate=e,t.core.updateStorage({speed:e}),t):void t.core.warn("Invalid speeds format")},t.prototype.loop=function(e){var t=this;a.inArray(["start","end","all","none","toggle"],e)||(e="toggle");var n=Number(t.media.currentTime);switch(e){case"start":t.config.loop.end&&t.config.loop.end<=n&&(t.config.loop.end=null),t.config.loop.start=n,t.config.loop.indicator.start=t.elements.display.played.value;break;case"end":if(t.config.loop.start>=n)return;t.config.loop.end=n,t.config.loop.indicator.end=t.elements.display.played.value;break;case"all":t.config.loop.start=0,t.config.loop.end=t.media.duration-2,t.config.loop.indicator.start=0,t.config.loop.indicator.end=100;break;case"toggle":t.config.loop.active?(t.config.loop.start=0,t.config.loop.end=null):(t.config.loop.start=0,t.config.loop.end=t.media.duration-2);break;default:t.config.loop.start=0,t.config.loop.end=null}t.config.loop.active=a.is.number(t.config.loop.start)&&a.is.number(t.config.loop.end);var i=(t.core.updateTimeDisplay(t.config.loop.start,t.core.getElement('[data-plyr-loop="start"]')),null);return a.is.number(t.config.loop.end)&&(i=t.core.updateTimeDisplay(t.config.loop.end,t.core.getElement('[data-loop__value="loopout"]'))),t.config.loop.active,t},t.prototype.source=function(e){var t=this;if(a.is.object(e))return t.core.updateSource(e),t;var n;switch(t.type){case"youtube":n=t.embed.getVideoUrl();break;case"vimeo":t.embed.getVideoUrl.then(function(e){n=e});break;case"soundcloud":t.embed.getCurrentSound(function(e){n=e.permalink_url});break;default:n=t.media.currentSrc}return n},t.prototype.poster=function(e){var t=this;return a.is.string(e)?("video"===t.type?t.media.setAttribute("poster",e):t.core.warn("Poster can only be set on HTML5 video"),t):t.media.getAttribute("poster")},t.prototype.toggleCaptions=function(e){var t=this;if(t.supported.full&&t.elements.buttons.captions)return a.is.boolean(e)||(e=t.elements.container.className.indexOf(t.config.classes.captions.active)===-1),t.captions.enabled=e,a.toggleState(t.elements.buttons.captions,t.captions.enabled),a.toggleClass(t.elements.container,t.config.classes.captions.active,t.captions.enabled),t.core.trigger(t.elements.container,t.captions.enabled?"captionsenabled":"captionsdisabled",!0),t.core.updateStorage({captions:t.captions.enabled}),t},t.prototype.language=function(e){var t=this;return a.is.string(e)?(t.config.captions.language=e.toLowerCase(),t.core.setCaption(),t.core.setupCaptions(),t):t.config.captions.language},t.prototype.toggleFullscreen=function(e){function t(){n={x:window.pageXOffset||0,y:window.pageYOffset||0}}function i(){window.scrollTo(n.x,n.y)}var o=this,l=r.fullscreen;if(l){if(!a.is.event(e)||e.type!==s.eventType)return s.isFullScreen(o.elements.container)?s.cancelFullScreen():(t(),s.requestFullScreen(o.elements.container)),void(o.fullscreen.active=s.isFullScreen(o.elements.container));o.fullscreen.active=s.isFullScreen(o.elements.container)}else o.fullscreen.active=!o.fullscreen.active,document.body.style.overflow=o.fullscreen.active?"hidden":"";return a.toggleClass(o.elements.container,o.config.classes.fullscreen.active,o.fullscreen.active),o.elements.buttons&&o.elements.buttons.fullscreen&&a.toggleState(o.elements.buttons.fullscreen,o.fullscreen.active),o.core.trigger(o.elements.container,o.fullscreen.active?"enterfullscreen":"exitfullscreen",!0),!o.fullscreen.active&&l&&i(),o},t.prototype.togglePictureInPicture=function(e){var t=this,n={pip:"picture-in-picture",inline:"inline"};if(t.core.support.pip)return a.is.boolean(e)||(e=t.media.webkitPresentationMode===n.inline),t.media.webkitSetPresentationMode(e?n.pip:n.inline),t},t.prototype.airPlay=function(){var e=this;if(e.core.support.airplay)return e.media.webkitShowPlaybackTargetPicker(),e},t.prototype.toggleControls=function(e){var t=this;if(t.config.hideControls&&"audio"!==t.type){var n=0,i=e,o=!1,s=a.hasClass(t.elements.container,t.config.classes.loading);if(a.is.boolean(e)||(a.is.event(e)?(o="enterfullscreen"===e.type,i=a.inArray(["mousemove","touchstart","mouseenter","focus"],e.type),a.inArray(["mousemove","touchmove"],e.type)&&(n=2e3),"focus"===e.type&&(n=3e3)):i=a.hasClass(t.elements.container,t.config.classes.hideControls)),window.clearTimeout(t.core.timers.hover),i||t.media.paused||s){if(a.toggleClass(t.elements.container,t.config.classes.hideControls,!1),t.media.paused||s)return;r.touch&&(n=3e3)}return i&&t.media.paused||(t.core.timers.hover=window.setTimeout(function(){(!t.elements.controls.pressed&&!t.elements.controls.hover||o)&&a.toggleClass(t.elements.container,t.config.classes.hideControls,!0)},n)),t}},t.prototype.on=function(e,t){var n=this;return a.on(n.elements.container,e,t),n},t.prototype.supports=function(e){return r.mime(this,e)},t.prototype.destroy=function(e,t){function n(){a.is.boolean(t)||(t=!0),a.is.function(e)&&e.call(i.original),t&&(i.elements.container.parentNode.replaceChild(i.original,i.elements.container),document.body.style.overflow="",i.core.trigger(i.original,"destroyed",!0))}var i=this;switch(i.type){case"youtube":window.clearInterval(i.timers.buffering),window.clearInterval(i.timers.playing),i.embed.destroy(),n();break;case"vimeo":i.embed.unload().then(n),window.setTimeout(n,200);break;case"video":case"audio":i.core.toggleNativeControls(!0),n()}return i},t.prototype.getDuration=function(){var e=this,t=parseInt(e.config.duration),n=0;return null===e.media.duration||isNaN(e.media.duration)||(n=e.media.duration),isNaN(t)?n:t},t});
\ No newline at end of file @@ -40,7 +40,35 @@ - Added `playsinline` support for iOS 10 - Embed setup now accepts an <iframe> as the target element for true progressive enhancement -#### Breaking changes +## Changes + +### Config changes +- videoWrapper -> video +- embedWrapper -> embed +- setup and ready classes removed + +### API changes +- Can now chain most functions (need to document which can) +- support -> supports +- isFullscreen -> fullscreen.active +- new 'language' +- getType -> type +- getEmbed -> embed +- getContainer removed +- getMedia -> media +- getCurrentTime -> media.currentTime +- getVolume -> media.volume +- isMuted -> media.muted +- isLoading -> media.loading +- isPaused -> media.paused +- updatePoster -> poster +- setVolume -> volume +- increaseVolume (new) +- decreaseVolume (new) +- togglePictureInPicture (new) +- airPlay (new) + +#### Other breaking changes - New config options for loop - Selectors changes (new `input` and `display` object) - DOCUMENT - Custom HTML option now `controls` which accepts a string (HTML), a function (your own template engine) or array (use built in controls) diff --git a/src/js/plyr.js b/src/js/plyr.js index 2a44a13b..459bc6fd 100644 --- a/src/js/plyr.js +++ b/src/js/plyr.js @@ -5,25 +5,20 @@ // License: The MIT License (MIT) // ========================================================================== -(function(root, factory) { +(function(name, context, definition) { + /* global define,module,require,YUI */ 'use strict'; - /* global define,module */ - if (typeof module === 'object' && typeof module.exports === 'object') { - // Node, CommonJS-like - module.exports = factory(root, document); + if (typeof exports === 'object') { + module.exports = definition(require); } else if (typeof define === 'function' && define.amd) { - // AMD - define([], function() { - return factory(root, document); - }); + define(definition); } else { - // Browser globals (root is window) - root.Plyr = factory(root, document); + context[name] = definition(); } -}(typeof window !== 'undefined' ? window : this, function(window, document) { +}).call(this, 'Plyr', this, function(require) { 'use strict'; - /* global jQuery */ + /* global jQuery, console */ // Globals var scroll = { @@ -132,10 +127,8 @@ // Class hooks added to the player in different states classes: { - setup: 'plyr--setup', - ready: 'plyr--ready', - videoWrapper: 'plyr__video-wrapper', - embedWrapper: 'plyr__video-embed', + video: 'plyr__video-wrapper', + embed: 'plyr__video-embed', control: 'plyr__control', type: 'plyr--{0}', stopped: 'plyr--stopped', @@ -306,7 +299,7 @@ html5: ['video', 'audio'] }; - // Utilities outside of Plyr scope + // Utilities var utils = { // Check variable types is: { @@ -1197,25 +1190,30 @@ }; // Player instance - function Player(element, options) { + function Player(media, options) { var player = this; var timers = {}; var api = {}; + // Get the media element + player.media = media; + // String selector passed - if (utils.is.string(element)) { - element = document.querySelectorAll(element); + if (utils.is.string(player.media)) { + player.media = document.querySelectorAll(player.media); } // jQuery, NodeList or Array passed, use first element - if ((window.jQuery && element instanceof jQuery) || utils.is.nodeList(element) || utils.is.array(element)) { - element = element[0]; + if ((window.jQuery && player.media instanceof jQuery) || + utils.is.nodeList(player.media) || + utils.is.array(player.media)) { + player.media = player.media[0]; } - // Config - var config = utils.extend({}, defaults, options, (function() { + // Set config + player.config = utils.extend({}, defaults, options, (function() { try { - return JSON.parse(element.getAttribute('data-plyr')); + return JSON.parse(player.media.getAttribute('data-plyr')); } catch (e) {} })()); @@ -1231,7 +1229,6 @@ panes: {}, tabs: {} }, - media: element, captions: null }; @@ -1252,20 +1249,21 @@ var log = function() {}; var warn = function() {}; var error = function() {}; - if (config.debug && 'console' in window) { - log = window.console.log; - warn = window.console.warn; - error = window.console.error; + if (player.config.debug && 'console' in window) { + log = console.log; + warn = console.warn; + error = console.error; + log('Debugging enabled'); } // Log config options and support - log('Config', config); + log('Config', player.config); log('Support', support); // Trigger events, with plyr instance passed function trigger(element, type, bubbles, properties) { utils.event(element, type, bubbles, utils.extend({}, properties, { - plyr: api + plyr: player })); } @@ -1318,12 +1316,12 @@ // Add elements to HTML5 media (source, tracks, etc) function insertElements(type, attributes) { if (utils.is.string(attributes)) { - utils.insertElement(type, player.elements.media, { + utils.insertElement(type, player.media, { src: attributes }); } else if (utils.is.array(attributes)) { attributes.forEach(function(attribute) { - utils.insertElement(type, player.elements.media, attribute); + utils.insertElement(type, player.media, attribute); }); } } @@ -1331,8 +1329,8 @@ // Get icon URL function getIconUrl() { return { - url: config.iconUrl, - absolute: (config.iconUrl.indexOf("http") === 0) || player.browser.isIE + url: player.config.iconUrl, + absolute: (player.config.iconUrl.indexOf("http") === 0) || player.browser.isIE }; } @@ -1340,7 +1338,7 @@ function createIcon(type, attributes) { var namespace = 'http://www.w3.org/2000/svg'; var iconUrl = getIconUrl(); - var iconPath = (!iconUrl.absolute ? iconUrl.url : '') + '#' + config.iconPrefix; + var iconPath = (!iconUrl.absolute ? iconUrl.url : '') + '#' + player.config.iconPrefix; // Create <svg> var icon = document.createElementNS(namespace, 'svg'); @@ -1360,7 +1358,7 @@ // Create hidden text label function createLabel(type) { - var text = config.i18n[type]; + var text = player.config.i18n[type]; switch (type) { case 'pip': @@ -1373,18 +1371,18 @@ } return utils.createElement('span', { - class: config.classes.hidden + class: player.config.classes.hidden }, text); } // Create a badge function createBadge(text) { var badge = utils.createElement('span', { - class: config.classes.menu.value + class: player.config.classes.menu.value }); badge.appendChild(utils.createElement('span', { - class: config.classes.menu.badge + class: player.config.classes.menu.badge }, text)); return badge; @@ -1402,11 +1400,11 @@ } if ('class' in attributes) { - if (attributes.class.indexOf(config.classes.control) === -1) { - attributes.class += ' ' + config.classes.control; + if (attributes.class.indexOf(player.config.classes.control) === -1) { + attributes.class += ' ' + player.config.classes.control; } } else { - attributes.class = config.classes.control; + attributes.class = player.config.classes.control; } // Large play button @@ -1442,7 +1440,7 @@ } // Merge attributes - utils.extend(attributes, utils.getAttributesFromSelector(config.selectors.buttons[type], attributes)); + utils.extend(attributes, utils.getAttributesFromSelector(player.config.selectors.buttons[type], attributes)); // Add toggle icon if needed if (utils.is.string(iconToggled)) { @@ -1466,11 +1464,11 @@ // Seek label var label = utils.createElement('label', { for: attributes.id, - class: config.classes.hidden - }, config.i18n[type]); + class: player.config.classes.hidden + }, player.config.i18n[type]); // Seek input - var input = utils.createElement('input', utils.extend(utils.getAttributesFromSelector(config.selectors.inputs[type]), { + var input = utils.createElement('input', utils.extend(utils.getAttributesFromSelector(player.config.selectors.inputs[type]), { type: 'range', min: 0, max: 100, @@ -1489,7 +1487,7 @@ // Create a <progress> function createProgress(type, attributes) { - var progress = utils.createElement('progress', utils.extend(utils.getAttributesFromSelector(config.selectors.display[type]), { + var progress = utils.createElement('progress', utils.extend(utils.getAttributesFromSelector(player.config.selectors.display[type]), { min: 0, max: 100, value: 0 @@ -1502,11 +1500,11 @@ var suffix = ''; switch (type) { case 'played': - suffix = config.i18n.played; + suffix = player.config.i18n.played; break; case 'buffer': - suffix = config.i18n.buffered; + suffix = player.config.i18n.buffered; break; } @@ -1525,10 +1523,10 @@ }); container.appendChild(utils.createElement('span', { - class: config.classes.hidden - }, config.i18n[type])); + class: player.config.classes.hidden + }, player.config.i18n[type])); - container.appendChild(utils.createElement('span', utils.getAttributesFromSelector(config.selectors.display[type]), '00:00')); + container.appendChild(utils.createElement('span', utils.getAttributesFromSelector(player.config.selectors.display[type]), '00:00')); player.elements.display[type] = container; @@ -1538,33 +1536,33 @@ // Build the default HTML function createControls(data) { // Create the container - var controls = utils.createElement('div', utils.getAttributesFromSelector(config.selectors.controls.wrapper)); + var controls = utils.createElement('div', utils.getAttributesFromSelector(player.config.selectors.controls.wrapper)); // Restart button - if (utils.inArray(config.controls, 'restart')) { + if (utils.inArray(player.config.controls, 'restart')) { controls.appendChild(createButton('restart')); } // Rewind button - if (utils.inArray(config.controls, 'rewind')) { + if (utils.inArray(player.config.controls, 'rewind')) { controls.appendChild(createButton('rewind')); } // Play Pause button // TODO: This should be a toggle button really? - if (utils.inArray(config.controls, 'play')) { + if (utils.inArray(player.config.controls, 'play')) { controls.appendChild(createButton('play')); controls.appendChild(createButton('pause')); } // Fast forward button - if (utils.inArray(config.controls, 'fast-forward')) { + if (utils.inArray(player.config.controls, 'fast-forward')) { controls.appendChild(createButton('fast-forward')); } // Progress - if (utils.inArray(config.controls, 'progress')) { - var container = utils.createElement('span', utils.getAttributesFromSelector(config.selectors.progress)); + if (utils.inArray(player.config.controls, 'progress')) { + var container = utils.createElement('span', utils.getAttributesFromSelector(player.config.selectors.progress)); // Seek range slider var seek = createRange('seek', { @@ -1582,10 +1580,10 @@ container.appendChild(createProgress('buffer')); // Seek tooltip - if (config.tooltips.seek) { + if (player.config.tooltips.seek) { var tooltip = utils.createElement('span', { role: 'tooltip', - class: config.classes.tooltip + class: player.config.classes.tooltip }, '00:00'); container.appendChild(tooltip); @@ -1597,22 +1595,22 @@ } // Media current time display - if (utils.inArray(config.controls, 'current-time')) { + if (utils.inArray(player.config.controls, 'current-time')) { controls.appendChild(createTime('currentTime')); } // Media duration display - if (utils.inArray(config.controls, 'duration')) { + if (utils.inArray(player.config.controls, 'duration')) { controls.appendChild(createTime('duration')); } // Toggle mute button - if (utils.inArray(config.controls, 'mute')) { + if (utils.inArray(player.config.controls, 'mute')) { controls.appendChild(createButton('mute')); } // Volume range control - if (utils.inArray(config.controls, 'volume')) { + if (utils.inArray(player.config.controls, 'volume')) { var volume = utils.createElement('span', { class: 'plyr__volume' }); @@ -1620,7 +1618,7 @@ // Set the attributes var attributes = { max: 10, - value: config.volume + value: player.config.volume }; // Create the volume range slider @@ -1638,12 +1636,12 @@ } // Toggle captions button - if (utils.inArray(config.controls, 'captions')) { + if (utils.inArray(player.config.controls, 'captions')) { controls.appendChild(createButton('captions')); } // Settings button / menu - if (utils.inArray(config.controls, 'settings')) { + if (utils.inArray(player.config.controls, 'settings')) { var menu = utils.createElement('div', { class: 'plyr__menu' }); @@ -1679,23 +1677,23 @@ }); // Build the tabs - config.settings.forEach(function(type) { + player.config.settings.forEach(function(type) { var tab = utils.createElement('li', { role: 'tab', hidden: '' }); - var button = utils.createElement('button', utils.extend(utils.getAttributesFromSelector(config.selectors.buttons.settings), { + var button = utils.createElement('button', utils.extend(utils.getAttributesFromSelector(player.config.selectors.buttons.settings), { type: 'button', - class: config.classes.control + ' ' + config.classes.control + '--forward', + class: player.config.classes.control + ' ' + player.config.classes.control + '--forward', id: 'plyr-settings-' + data.id + '-' + type + '-tab', 'aria-haspopup': true, 'aria-controls': 'plyr-settings-' + data.id + '-' + type, 'aria-expanded': false - }), config.i18n[type]); + }), player.config.i18n[type]); var value = utils.createElement('span', { - class: config.classes.menu.value + class: player.config.classes.menu.value }); // Speed contains HTML entities @@ -1712,7 +1710,7 @@ inner.appendChild(home); // Build the panes - config.settings.forEach(function(type) { + player.config.settings.forEach(function(type) { var pane = utils.createElement('div', { id: 'plyr-settings-' + data.id + '-' + type, 'aria-hidden': true, @@ -1724,11 +1722,11 @@ var back = utils.createElement('button', { type: 'button', - class: config.classes.control + ' ' + config.classes.control + '--back', + class: player.config.classes.control + ' ' + player.config.classes.control + '--back', 'aria-haspopup': true, 'aria-controls': 'plyr-settings-' + data.id + '-home', 'aria-expanded': false - }, config.i18n[type]); + }, player.config.i18n[type]); pane.appendChild(back); @@ -1749,20 +1747,26 @@ } // Picture in picture button - if (utils.inArray(config.controls, 'pip') && support.pip) { + if (utils.inArray(player.config.controls, 'pip') && support.pip) { controls.appendChild(createButton('pip')); } // Airplay button - if (utils.inArray(config.controls, 'airplay') && support.airplay) { + if (utils.inArray(player.config.controls, 'airplay') && support.airplay) { controls.appendChild(createButton('airplay')); } // Toggle fullscreen button - if (utils.inArray(config.controls, 'fullscreen')) { + if (utils.inArray(player.config.controls, 'fullscreen')) { controls.appendChild(createButton('fullscreen')); } + // Larger overlaid play button + if (utils.inArray(player.config.controls, 'play-large')) { + player.elements.buttons.playLarge = createButton('play-large'); + player.elements.container.appendChild(player.elements.buttons.playLarge); + } + player.elements.controls = controls; setLoopMenu(); @@ -1840,16 +1844,16 @@ var item = utils.createElement('li'); var label = utils.createElement('label', { - class: config.classes.control + class: player.config.classes.control }); - var radio = utils.createElement('input', utils.extend(utils.getAttributesFromSelector(config.selectors.inputs.quality), { + var radio = utils.createElement('input', utils.extend(utils.getAttributesFromSelector(player.config.selectors.inputs.quality), { type: 'radio', name: 'plyr-quality', value: quality, })); - if (quality === config.quality.selected) { + if (quality === player.config.quality.selected) { radio.checked = true; } @@ -1882,11 +1886,11 @@ options.forEach(function(option) { var item = utils.createElement('li'); - var button = utils.createElement('button', utils.extend(utils.getAttributesFromSelector(config.selectors.buttons.loop), { + var button = utils.createElement('button', utils.extend(utils.getAttributesFromSelector(player.config.selectors.buttons.loop), { type: 'button', - class: config.classes.control, + class: player.config.classes.control, 'data-plyr-loop-action': option - }), config.i18n[option]); + }), player.config.i18n[option]); if (utils.inArray(['start', 'end'], option)) { var badge = createBadge('00:00'); @@ -1926,7 +1930,7 @@ // Add the "None" option to turn off captions tracks.unshift({ language: 'off', - label: config.i18n.none + label: player.config.i18n.none }); // Generate options @@ -1934,16 +1938,16 @@ var item = utils.createElement('li'); var label = utils.createElement('label', { - class: config.classes.control + class: player.config.classes.control }); - var radio = utils.createElement('input', utils.extend(utils.getAttributesFromSelector(config.selectors.inputs.language), { + var radio = utils.createElement('input', utils.extend(utils.getAttributesFromSelector(player.config.selectors.inputs.language), { type: 'radio', name: 'plyr-language', value: track.language, })); - if (track.language.toLowerCase() === config.captions.language.toLowerCase()) { + if (track.language.toLowerCase() === player.config.captions.language.toLowerCase()) { radio.checked = true; } @@ -1972,23 +1976,23 @@ // If there's no captions, bail if (!utils.is.array(options)) { - options = config.speed.options; + options = player.config.speed.options; } options.forEach(function(speed) { var item = utils.createElement('li'); var label = utils.createElement('label', { - class: config.classes.control + class: player.config.classes.control }); - var radio = utils.createElement('input', utils.extend(utils.getAttributesFromSelector(config.selectors.inputs.speed), { + var radio = utils.createElement('input', utils.extend(utils.getAttributesFromSelector(player.config.selectors.inputs.speed), { type: 'radio', name: 'plyr-speed', value: speed, })); - if (speed === config.speed.selected) { + if (speed === player.config.speed.selected) { radio.checked = true; } @@ -2005,15 +2009,15 @@ return; } - if ((player.type !== 'audio' || config.fullscreen.allowAudio) && config.fullscreen.enabled) { + if ((player.type !== 'audio' || player.config.fullscreen.allowAudio) && player.config.fullscreen.enabled) { // Check for native support var nativeSupport = support.fullscreen; - if (nativeSupport || (config.fullscreen.fallback && !utils.inFrame())) { + if (nativeSupport || (player.config.fullscreen.fallback && !utils.inFrame())) { log((nativeSupport ? 'Native' : 'Fallback') + ' fullscreen enabled'); // Add styling hook - utils.toggleClass(player.elements.container, config.classes.fullscreen.enabled, true); + utils.toggleClass(player.elements.container, player.config.classes.fullscreen.enabled, true); } else { log('Fullscreen not supported and fallback disabled'); } @@ -2037,15 +2041,15 @@ // Inject the container if (!utils.is.htmlElement(player.elements.captions)) { - player.elements.captions = utils.createElement('div', utils.getAttributesFromSelector(config.selectors.captions)); + player.elements.captions = utils.createElement('div', utils.getAttributesFromSelector(player.config.selectors.captions)); utils.insertAfter(player.elements.captions, player.elements.wrapper); } // Get tracks - player.captions.tracks = utils.is.array(tracks) ? tracks : player.elements.media.textTracks; + player.captions.tracks = utils.is.array(tracks) ? tracks : player.media.textTracks; // Set the class hook - utils.toggleClass(player.elements.container, config.classes.captions.enabled, !utils.is.empty(player.captions.tracks)); + utils.toggleClass(player.elements.container, player.config.classes.captions.enabled, !utils.is.empty(player.captions.tracks)); // If no caption file exists, hide container for caption text if (utils.is.empty(player.captions.tracks)) { @@ -2056,7 +2060,7 @@ showCaptions(); if (player.type === 'video') { - var language = config.captions.language.toLowerCase(); + var language = player.config.captions.language.toLowerCase(); // Turn off native caption rendering to avoid double captions [].forEach.call(player.captions.tracks, function(track) { @@ -2124,22 +2128,6 @@ } } - // Select active caption - function setLanguage(language) { - // Save config - if (utils.is.string(language)) { - config.captions.language = language.toLowerCase(); - } else if (utils.is.event(language)) { - config.captions.language = language.target.value.toLowerCase(); - } - - // Clear caption - setCaption(); - - // Re-run setup - setupCaptions(); - } - // Set the current caption function setCaption(caption) { if (utils.is.htmlElement(player.elements.captions)) { @@ -2182,51 +2170,21 @@ // Otherwise fall back to the default config if (!utils.is.boolean(active)) { - active = config.captions.active; + active = player.config.captions.active; } else { - config.captions.active = active; + player.config.captions.active = active; } if (active) { - utils.toggleClass(player.elements.container, config.classes.captions.active, true); + utils.toggleClass(player.elements.container, player.config.classes.captions.active, true); utils.toggleState(player.elements.buttons.captions, true); } } - // Toggle captions - function toggleCaptions(show) { - // If there's no full support, or there's no caption toggle - if (!player.supported.full || !player.elements.buttons.captions) { - return; - } - - // If the method is called without parameter, toggle based on current value - if (!utils.is.boolean(show)) { - show = (player.elements.container.className.indexOf(config.classes.captions.active) === -1); - } - - // Set global - player.captions.enabled = show; - - // Toggle state - utils.toggleState(player.elements.buttons.captions, player.captions.enabled); - - // Add class hook - utils.toggleClass(player.elements.container, config.classes.captions.active, player.captions.enabled); - - // Trigger an event - trigger(player.elements.container, player.captions.enabled ? 'captionsenabled' : 'captionsdisabled', true); - - // Save captions state to localStorage - updateStorage({ - captions: player.captions.enabled - }); - } - // Insert controls function injectControls() { // Sprite - if (config.loadSprite) { + if (player.config.loadSprite) { var iconUrl = getIconUrl(); // Only load external sprite using AJAX @@ -2238,12 +2196,6 @@ } } - // Larger overlaid play button - if (utils.inArray(config.controls, 'play-large')) { - player.elements.buttons.playLarge = createButton('play-large'); - player.elements.container.appendChild(player.elements.buttons.playLarge); - } - // Create a unique ID player.id = Math.floor(Math.random() * 10000); @@ -2251,22 +2203,22 @@ var controls = null; // HTML passed as the option - if (utils.is.string(config.controls)) { - controls = config.controls; + if (utils.is.string(player.config.controls)) { + controls = player.config.controls; } // A custom function to build controls // The function can return a HTMLElement or String - else if (utils.is.function(config.controls)) { - controls = config.controls({ + else if (utils.is.function(player.config.controls)) { + controls = player.config.controls({ id: player.id, - seektime: config.seekTime + seektime: player.config.seekTime }); } // Create controls else { controls = createControls({ id: player.id, - seektime: config.seekTime, + seektime: player.config.seekTime, speed: getSpeedLabel(), // TODO: Get current quality quality: 'HD', @@ -2280,8 +2232,8 @@ var target; // Inject to custom location - if (utils.is.string(config.selectors.controls.container)) { - target = document.querySelector(config.selectors.controls.container); + if (utils.is.string(player.config.selectors.controls.container)) { + target = document.querySelector(player.config.selectors.controls.container); } // Inject into the container by default @@ -2302,14 +2254,14 @@ } // Setup tooltips - if (config.tooltips.controls) { - var labels = getElements([config.selectors.controls.wrapper, ' ', config.selectors.labels, ' .', config.classes.hidden].join('')); + if (player.config.tooltips.controls) { + var labels = getElements([player.config.selectors.controls.wrapper, ' ', player.config.selectors.labels, ' .', player.config.classes.hidden].join('')); for (var i = labels.length - 1; i >= 0; i--) { var label = labels[i]; - utils.toggleClass(label, config.classes.hidden, false); - utils.toggleClass(label, config.classes.tooltip, true); + utils.toggleClass(label, player.config.classes.hidden, false); + utils.toggleClass(label, player.config.classes.tooltip, true); } } } @@ -2318,44 +2270,44 @@ // TODO: Allow settings menus with custom controls (coming soon!) function findElements() { try { - player.elements.controls = getElement(config.selectors.controls.wrapper); + player.elements.controls = getElement(player.config.selectors.controls.wrapper); // Buttons player.elements.buttons = { - play: getElements(config.selectors.buttons.play), - pause: getElement(config.selectors.buttons.pause), - restart: getElement(config.selectors.buttons.restart), - rewind: getElement(config.selectors.buttons.rewind), - forward: getElement(config.selectors.buttons.forward), - mute: getElement(config.selectors.buttons.mute), - pip: getElement(config.selectors.buttons.pip), - airplay: getElement(config.selectors.buttons.airplay), - settings: getElement(config.selectors.buttons.settings), - captions: getElement(config.selectors.buttons.captions), - fullscreen: getElement(config.selectors.buttons.fullscreen) + play: getElements(player.config.selectors.buttons.play), + pause: getElement(player.config.selectors.buttons.pause), + restart: getElement(player.config.selectors.buttons.restart), + rewind: getElement(player.config.selectors.buttons.rewind), + forward: getElement(player.config.selectors.buttons.forward), + mute: getElement(player.config.selectors.buttons.mute), + pip: getElement(player.config.selectors.buttons.pip), + airplay: getElement(player.config.selectors.buttons.airplay), + settings: getElement(player.config.selectors.buttons.settings), + captions: getElement(player.config.selectors.buttons.captions), + fullscreen: getElement(player.config.selectors.buttons.fullscreen) }; // Progress - player.elements.progress = getElement(config.selectors.progress); + player.elements.progress = getElement(player.config.selectors.progress); // Inputs player.elements.inputs = { - seek: getElement(config.selectors.inputs.seek), - volume: getElement(config.selectors.inputs.volume), + seek: getElement(player.config.selectors.inputs.seek), + volume: getElement(player.config.selectors.inputs.volume), }; // Display player.elements.display = { - buffer: getElement(config.selectors.display.buffer), - played: getElement(config.selectors.display.played), - volume: getElement(config.selectors.display.volume), - duration: getElement(config.selectors.display.duration), - currentTime: getElement(config.selectors.display.currentTime), + buffer: getElement(player.config.selectors.display.buffer), + played: getElement(player.config.selectors.display.played), + volume: getElement(player.config.selectors.display.volume), + duration: getElement(player.config.selectors.display.duration), + currentTime: getElement(player.config.selectors.display.currentTime), }; // Seek tooltip if (utils.is.htmlElement(player.elements.progress)) { - player.elements.display.seekTooltip = player.elements.progress.querySelector('.' + config.classes.tooltip); + player.elements.display.seekTooltip = player.elements.progress.querySelector('.' + player.config.classes.tooltip); } return true; @@ -2372,29 +2324,29 @@ // Toggle style hook function toggleStyleHook() { - utils.toggleClass(player.elements.container, config.selectors.container.replace('.', ''), player.supported.full); + utils.toggleClass(player.elements.container, player.config.selectors.container.replace('.', ''), player.supported.full); } // Toggle native controls function toggleNativeControls(toggle) { if (toggle && utils.inArray(types.html5, player.type)) { - player.elements.media.setAttribute('controls', ''); + player.media.setAttribute('controls', ''); } else { - player.elements.media.removeAttribute('controls'); + player.media.removeAttribute('controls'); } } // Setup aria attribute for play and iframe title function setTitle(iframe) { // Find the current text - var label = config.i18n.play; + var label = player.config.i18n.play; // If there's a media title set, use that for the label - if (utils.is.string(config.title) && !utils.is.empty(config.title)) { - label += ', ' + config.title; + if (utils.is.string(player.config.title) && !utils.is.empty(player.config.title)) { + label += ', ' + player.config.title; // Set container label - player.elements.container.setAttribute('aria-label', config.title); + player.elements.container.setAttribute('aria-label', player.config.title); } // If there's a play button, set label @@ -2410,8 +2362,8 @@ // Set iframe title // https://github.com/sampotts/plyr/issues/124 if (utils.is.htmlElement(iframe)) { - var title = utils.is.string(config.title) && !utils.is.empty(config.title) ? config.title : 'video'; - iframe.setAttribute('title', config.i18n.frameTitle.replace('{title}', title)); + var title = utils.is.string(player.config.title) && !utils.is.empty(player.config.title) ? player.config.title : 'video'; + iframe.setAttribute('title', player.config.i18n.frameTitle.replace('{title}', title)); } } @@ -2421,7 +2373,7 @@ player.storage = {}; // Bail if we don't have localStorage support or it's disabled - if (!support.storage || !config.storage.enabled) { + if (!support.storage || !player.config.storage.enabled) { return; } @@ -2430,7 +2382,7 @@ window.localStorage.removeItem('plyr-volume'); // load value from the current key - value = window.localStorage.getItem(config.storage.key); + value = window.localStorage.getItem(player.config.storage.key); if (!value) { // Key wasn't set (or had been cleared), move along @@ -2451,7 +2403,7 @@ // Save a value back to local storage function updateStorage(value) { // Bail if we don't have localStorage support or it's disabled - if (!support.storage || !config.storage.enabled) { + if (!support.storage || !player.config.storage.enabled) { return; } @@ -2459,52 +2411,52 @@ utils.extend(player.storage, value); // Update storage - window.localStorage.setItem(config.storage.key, JSON.stringify(player.storage)); + window.localStorage.setItem(player.config.storage.key, JSON.stringify(player.storage)); } // Setup media function setupMedia() { // If there's no media, bail - if (!player.elements.media) { + if (!player.media) { warn('No media element found!'); return; } if (player.supported.full) { // Add type class - utils.toggleClass(player.elements.container, config.classes.type.replace('{0}', player.type), true); + utils.toggleClass(player.elements.container, player.config.classes.type.replace('{0}', player.type), true); // Add video class for embeds // This will require changes if audio embeds are added if (utils.inArray(types.embed, player.type)) { - utils.toggleClass(player.elements.container, config.classes.type.replace('{0}', 'video'), true); + utils.toggleClass(player.elements.container, player.config.classes.type.replace('{0}', 'video'), true); } // Check for picture-in-picture support - utils.toggleClass(player.elements.container, config.classes.pip.enabled, support.pip && player.type === 'video'); + utils.toggleClass(player.elements.container, player.config.classes.pip.enabled, support.pip && player.type === 'video'); // Check for airplay support - utils.toggleClass(player.elements.container, config.classes.airplay.enabled, support.airplay && utils.inArray(types.html5, player.type)); + utils.toggleClass(player.elements.container, player.config.classes.airplay.enabled, support.airplay && utils.inArray(types.html5, player.type)); // If there's no autoplay attribute, assume the video is stopped and add state class - utils.toggleClass(player.elements.container, config.classes.stopped, config.autoplay); + utils.toggleClass(player.elements.container, player.config.classes.stopped, player.config.autoplay); // Add iOS class - utils.toggleClass(player.elements.container, config.classes.isIos, player.browser.isIos); + utils.toggleClass(player.elements.container, player.config.classes.isIos, player.browser.isIos); // Add touch class - utils.toggleClass(player.elements.container, config.classes.isTouch, support.touch); + utils.toggleClass(player.elements.container, player.config.classes.isTouch, support.touch); } // Inject the player wrapper if (utils.inArray(['video', 'youtube', 'vimeo'], player.type)) { // Create the wrapper div player.elements.wrapper = utils.createElement('div', { - class: config.classes.videoWrapper + class: player.config.classes.video }); // Wrap the video in a container - utils.wrap(player.elements.media, player.elements.wrapper); + utils.wrap(player.media, player.elements.wrapper); } // Embeds @@ -2536,18 +2488,18 @@ } // Add embed class for responsive - utils.toggleClass(player.elements.wrapper, config.classes.embedWrapper, true); + utils.toggleClass(player.elements.wrapper, player.config.classes.embed, true); if (player.type === 'youtube') { // Set ID - player.elements.media.setAttribute('id', id); + player.media.setAttribute('id', id); // Setup API if (utils.is.object(window.YT)) { youTubeReady(mediaId); } else { // Load the API - utils.injectScript(config.urls.youtube.api); + utils.injectScript(player.config.urls.youtube.api); // Setup callback for the API window.onYouTubeReadyCallbacks = window.onYouTubeReadyCallbacks || []; @@ -2566,11 +2518,11 @@ } } else if (player.type === 'vimeo') { // Set ID - player.elements.media.setAttribute('id', id); + player.media.setAttribute('id', id); // Load the API if not already if (!utils.is.object(window.Vimeo)) { - utils.injectScript(config.urls.vimeo.api); + utils.injectScript(player.config.urls.vimeo.api); // Wait for fragaloop load var vimeoTimer = window.setInterval(function() { @@ -2598,11 +2550,11 @@ 'id': id }); - player.elements.media.appendChild(soundCloud); + player.media.appendChild(soundCloud); // Load the API if not already if (!window.SC) { - utils.injectScript(config.urls.soundcloud.api); + utils.injectScript(player.config.urls.soundcloud.api); } // Wait for SC load @@ -2631,15 +2583,15 @@ function youTubeReady(videoId) { // Setup instance // https://developers.google.com/youtube/iframe_api_reference - player.embed = new window.YT.Player(player.elements.media.id, { + player.embed = new window.YT.Player(player.media.id, { videoId: videoId, playerVars: { - autoplay: (config.autoplay ? 1 : 0), + autoplay: (player.config.autoplay ? 1 : 0), controls: (player.supported.full ? 0 : 1), rel: 0, showinfo: 0, iv_load_policy: 3, - cc_load_policy: (config.captions.active ? 1 : 0), + cc_load_policy: (player.config.captions.active ? 1 : 0), cc_lang_pref: 'en', wmode: 'transparent', modestbranding: 1, @@ -2669,22 +2621,22 @@ var instance = event.target; // Create a faux HTML5 API using the YouTube API - player.elements.media.play = function() { + player.media.play = function() { instance.playVideo(); - player.elements.media.paused = false; + player.media.paused = false; }; - player.elements.media.pause = function() { + player.media.pause = function() { instance.pauseVideo(); - player.elements.media.paused = true; + player.media.paused = true; }; - player.elements.media.stop = function() { + player.media.stop = function() { instance.stopVideo(); - player.elements.media.paused = true; + player.media.paused = true; }; - player.elements.media.duration = instance.getDuration(); - player.elements.media.paused = true; - player.elements.media.currentTime = 0; - player.elements.media.muted = instance.isMuted(); + player.media.duration = instance.getDuration(); + player.media.paused = true; + player.media.currentTime = 0; + player.media.muted = instance.isMuted(); // Get available speeds var speed = instance.getPlaybackRate(); @@ -2693,21 +2645,21 @@ console.warn(speed, speedOptions); // Set title - config.title = instance.getVideoData().title; + player.config.title = instance.getVideoData().title; // Set the tabindex if (player.supported.full) { - player.elements.media.setAttribute('tabindex', -1); + player.media.setAttribute('tabindex', -1); } // Update UI embedReady(); // Trigger timeupdate - trigger(player.elements.media, 'timeupdate'); + trigger(player.media, 'timeupdate'); // Trigger timeupdate - trigger(player.elements.media, 'durationchange'); + trigger(player.media, 'durationchange'); // Reset timer window.clearInterval(timers.buffering); @@ -2715,22 +2667,22 @@ // Setup buffering timers.buffering = window.setInterval(function() { // Get loaded % from YouTube - player.elements.media.buffered = instance.getVideoLoadedFraction(); + player.media.buffered = instance.getVideoLoadedFraction(); // Trigger progress only when we actually buffer something - if (player.elements.media.lastBuffered === null || player.elements.media.lastBuffered < player.elements.media.buffered) { - trigger(player.elements.media, 'progress'); + if (player.media.lastBuffered === null || player.media.lastBuffered < player.media.buffered) { + trigger(player.media, 'progress'); } // Set last buffer point - player.elements.media.lastBuffered = player.elements.media.buffered; + player.media.lastBuffered = player.media.buffered; // Bail if we're at 100% - if (player.elements.media.buffered === 1) { + if (player.media.buffered === 1) { window.clearInterval(timers.buffering); // Trigger event - trigger(player.elements.media, 'canplaythrough'); + trigger(player.media, 'canplaythrough'); } }, 200); }, @@ -2751,43 +2703,43 @@ switch (event.data) { case 0: // YouTube doesn't support loop for a single video, so mimick it. - if (config.loop.active) { + if (player.config.loop.active) { // YouTube needs a call to `stopVideo` before playing again instance.stopVideo(); instance.playVideo(); break; } - player.elements.media.paused = true; - trigger(player.elements.media, 'ended'); + player.media.paused = true; + trigger(player.media, 'ended'); break; case 1: - player.elements.media.paused = false; + player.media.paused = false; // If we were seeking, fire seeked event - if (player.elements.media.seeking) { - trigger(player.elements.media, 'seeked'); + if (player.media.seeking) { + trigger(player.media, 'seeked'); } - player.elements.media.seeking = false; - trigger(player.elements.media, 'play'); - trigger(player.elements.media, 'playing'); + player.media.seeking = false; + trigger(player.media, 'play'); + trigger(player.media, 'playing'); // Poll to get playback progress timers.playing = window.setInterval(function() { // Set the current time - player.elements.media.currentTime = instance.getCurrentTime(); + player.media.currentTime = instance.getCurrentTime(); // Trigger timeupdate - trigger(player.elements.media, 'timeupdate'); + trigger(player.media, 'timeupdate'); }, 100); // Check duration again due to YouTube bug // https://github.com/sampotts/plyr/issues/374 // https://code.google.com/p/gdata-issues/issues/detail?id=8690 - if (player.elements.media.duration !== instance.getDuration()) { - player.elements.media.duration = instance.getDuration(); - trigger(player.elements.media, 'durationchange'); + if (player.media.duration !== instance.getDuration()) { + player.media.duration = instance.getDuration(); + trigger(player.media, 'durationchange'); } // Get quality @@ -2798,8 +2750,8 @@ break; case 2: - player.elements.media.paused = true; - trigger(player.elements.media, 'pause'); + player.media.paused = true; + trigger(player.media, 'pause'); break; } @@ -2815,47 +2767,47 @@ function vimeoReady(mediaId) { // Setup instance // https://github.com/vimeo/player.js - player.embed = new window.Vimeo.Player(player.elements.media, { + player.embed = new window.Vimeo.Player(player.media, { id: mediaId, - loop: config.loop.active, - autoplay: config.autoplay, + loop: player.config.loop.active, + autoplay: player.config.autoplay, byline: false, portrait: false, title: false }); // Create a faux HTML5 API using the Vimeo API - player.elements.media.play = function() { + player.media.play = function() { player.embed.play(); - player.elements.media.paused = false; + player.media.paused = false; }; - player.elements.media.pause = function() { + player.media.pause = function() { player.embed.pause(); - player.elements.media.paused = true; + player.media.paused = true; }; - player.elements.media.stop = function() { + player.media.stop = function() { player.embed.stop(); - player.elements.media.paused = true; + player.media.paused = true; }; - player.elements.media.paused = true; - player.elements.media.currentTime = 0; + player.media.paused = true; + player.media.currentTime = 0; // Update UI embedReady(); player.embed.getCurrentTime().then(function(value) { - player.elements.media.currentTime = value; + player.media.currentTime = value; // Trigger timeupdate - trigger(player.elements.media, 'timeupdate'); + trigger(player.media, 'timeupdate'); }); player.embed.getDuration().then(function(value) { - player.elements.media.duration = value; + player.media.duration = value; // Trigger timeupdate - trigger(player.elements.media, 'durationchange'); + trigger(player.media, 'durationchange'); }); // Get captions @@ -2864,8 +2816,8 @@ setupCaptions(tracks); // TODO: Captions - if (config.captions.active) { - player.embed.enableTextTrack(config.captions.language.toLowerCase()); + if (player.config.captions.active) { + player.embed.enableTextTrack(player.config.captions.language.toLowerCase()); } }); @@ -2888,41 +2840,41 @@ }); player.embed.on('play', function() { - player.elements.media.paused = false; - trigger(player.elements.media, 'play'); - trigger(player.elements.media, 'playing'); + player.media.paused = false; + trigger(player.media, 'play'); + trigger(player.media, 'playing'); }); player.embed.on('pause', function() { - player.elements.media.paused = true; - trigger(player.elements.media, 'pause'); + player.media.paused = true; + trigger(player.media, 'pause'); }); player.embed.on('timeupdate', function(data) { - player.elements.media.seeking = false; - player.elements.media.currentTime = data.seconds; - trigger(player.elements.media, 'timeupdate'); + player.media.seeking = false; + player.media.currentTime = data.seconds; + trigger(player.media, 'timeupdate'); }); player.embed.on('progress', function(data) { - player.elements.media.buffered = data.percent; - trigger(player.elements.media, 'progress'); + player.media.buffered = data.percent; + trigger(player.media, 'progress'); if (parseInt(data.percent) === 1) { // Trigger event - trigger(player.elements.media, 'canplaythrough'); + trigger(player.media, 'canplaythrough'); } }); player.embed.on('seeked', function() { - player.elements.media.seeking = false; - trigger(player.elements.media, 'seeked'); - trigger(player.elements.media, 'play'); + player.media.seeking = false; + trigger(player.media, 'seeked'); + trigger(player.media, 'play'); }); player.embed.on('ended', function() { - player.elements.media.paused = true; - trigger(player.elements.media, 'ended'); + player.media.paused = true; + trigger(player.media, 'ended'); }); } @@ -2934,219 +2886,75 @@ // Setup on ready player.embed.bind(window.SC.Widget.Events.READY, function() { // Create a faux HTML5 API using the Soundcloud API - player.elements.media.play = function() { + player.media.play = function() { player.embed.play(); - player.elements.media.paused = false; + player.media.paused = false; }; - player.elements.media.pause = function() { + player.media.pause = function() { player.embed.pause(); - player.elements.media.paused = true; + player.media.paused = true; }; - player.elements.media.stop = function() { + player.media.stop = function() { player.embed.seekTo(0); player.embed.pause(); - player.elements.media.paused = true; + player.media.paused = true; }; - player.elements.media.paused = true; - player.elements.media.currentTime = 0; + player.media.paused = true; + player.media.currentTime = 0; player.embed.getDuration(function(value) { - player.elements.media.duration = value / 1000; + player.media.duration = value / 1000; // Update UI embedReady(); }); player.embed.getPosition(function(value) { - player.elements.media.currentTime = value; + player.media.currentTime = value; // Trigger timeupdate - trigger(player.elements.media, 'timeupdate'); + trigger(player.media, 'timeupdate'); }); player.embed.bind(window.SC.Widget.Events.PLAY, function() { - player.elements.media.paused = false; - trigger(player.elements.media, 'play'); - trigger(player.elements.media, 'playing'); + player.media.paused = false; + trigger(player.media, 'play'); + trigger(player.media, 'playing'); }); player.embed.bind(window.SC.Widget.Events.PAUSE, function() { - player.elements.media.paused = true; - trigger(player.elements.media, 'pause'); + player.media.paused = true; + trigger(player.media, 'pause'); }); player.embed.bind(window.SC.Widget.Events.PLAY_PROGRESS, function(data) { - player.elements.media.seeking = false; - player.elements.media.currentTime = data.currentPosition / 1000; - trigger(player.elements.media, 'timeupdate'); + player.media.seeking = false; + player.media.currentTime = data.currentPosition / 1000; + trigger(player.media, 'timeupdate'); }); player.embed.bind(window.SC.Widget.Events.LOAD_PROGRESS, function(data) { - player.elements.media.buffered = data.loadProgress; - trigger(player.elements.media, 'progress'); + player.media.buffered = data.loadProgress; + trigger(player.media, 'progress'); if (parseInt(data.loadProgress) === 1) { // Trigger event - trigger(player.elements.media, 'canplaythrough'); + trigger(player.media, 'canplaythrough'); } }); player.embed.bind(window.SC.Widget.Events.FINISH, function() { - player.elements.media.paused = true; - trigger(player.elements.media, 'ended'); + player.media.paused = true; + trigger(player.media, 'ended'); }); }); } - // Play media - function play() { - if ('play' in player.elements.media) { - player.elements.media.play(); - } - } - - // Pause media - function pause() { - if ('pause' in player.elements.media) { - player.elements.media.pause(); - } - } - - // Toggle playback - function togglePlay(toggle) { - // True toggle - if (!utils.is.boolean(toggle)) { - toggle = player.elements.media.paused; - } - - if (toggle) { - play(); - } else { - pause(); - } - - return toggle; - } - - // Toggle loop - // TODO: Set the indicator on load as user may pass loop as config - function toggleLoop(type) { - // Set default to be a true toggle - if (!utils.inArray(['start', 'end', 'all', 'none', 'toggle'], type)) { - type = 'toggle'; - } - - var currentTime = Number(player.elements.media.currentTime); - - switch (type) { - case 'start': - if (config.loop.end && config.loop.end <= currentTime) { - config.loop.end = null; - } - config.loop.start = currentTime; - config.loop.indicator.start = player.elements.display.played.value; - break; - - case 'end': - if (config.loop.start >= currentTime) { - return; - } - config.loop.end = currentTime; - config.loop.indicator.end = player.elements.display.played.value; - break; - - case 'all': - config.loop.start = 0; - config.loop.end = player.elements.media.duration - 2; - config.loop.indicator.start = 0; - config.loop.indicator.end = 100; - break; - - case 'toggle': - if (config.loop.active) { - config.loop.start = 0; - config.loop.end = null; - } else { - config.loop.start = 0; - config.loop.end = player.elements.media.duration - 2; - } - break; - - default: - config.loop.start = 0; - config.loop.end = null; - break; - } - - // Check if can loop - config.loop.active = utils.is.number(config.loop.start) && utils.is.number(config.loop.end); - var start = updateTimeDisplay(config.loop.start, getElement('[data-plyr-loop="start"]')); - var end = null; - - if (utils.is.number(config.loop.end)) { - // Find the <span> inside button - end = updateTimeDisplay(config.loop.end, document.querySelector('[data-loop__value="loopout"]')); - } else { - // Find the <span> inside button - //end = document.querySelector('[data-loop__value="loopout"]').innerHTML = ''; - } - - if (config.loop.active) { - // TODO: Improve the design of the loop indicator and put styling in CSS where it's meant to be - //getElement('[data-menu="loop"]').innerHTML = start + ' - ' + end; - //getElement(config.selectors.progress.looped).style.position = 'absolute'; - //getElement(config.selectors.progress.looped).style.left = config.loopinPositionPercentage + '%'; - //getElement(config.selectors.progress.looped).style.width = (config.loopoutPositionPercentage - config.loopinPositionPercentage) + '%'; - //getElement(config.selectors.progress.looped).style.background = '#ffbb00'; - //getElement(config.selectors.progress.looped).style.height = '3px'; - //getElement(config.selectors.progress.looped).style.top = '3px'; - //getElement(config.selectors.progress.looped).style['border-radius'] = '100px'; - } else { - //getElement('[data-menu="loop"]').innerHTML = config.i18n.loopNone; - //getElement(config.selectors.progress.looped).style.width = '0px'; - } - } - - // Set playback speed - function setSpeed(speed) { - // Load speed from storage or default value - if (utils.is.event(speed)) { - speed = parseFloat(speed.target.value); - } else if (!utils.is.number(speed)) { - speed = parseFloat(player.storage.speed || config.speed.selected); - } - - // Set min/max - if (speed < 0.1) { - speed = 0.1; - } - if (speed > 2.0) { - speed = 2.0; - } - - if (!utils.is.array(config.speed.options)) { - warn('Invalid speeds format'); - return; - } - - // Store current speed - config.speed.selected = speed; - - // Set HTML5 speed - // TODO: set YouTube - player.elements.media.playbackRate = speed; - - // Save speed to localStorage - updateStorage({ - speed: speed - }); - } - // Get the current speed value function getSpeedLabel(speed) { if (!utils.is.number(speed)) { - speed = config.speed.selected; + speed = player.config.speed.selected; } if (speed === 1) { @@ -3156,182 +2964,13 @@ return speed + '×'; } - // Rewind - function rewind(seekTime) { - // Use default if needed - if (!utils.is.number(seekTime)) { - seekTime = config.seekTime; - } - seek(player.elements.media.currentTime - seekTime); - } - - // Fast forward - function forward(seekTime) { - // Use default if needed - if (!utils.is.number(seekTime)) { - seekTime = config.seekTime; - } - seek(player.elements.media.currentTime + seekTime); - } - - // Seek to time - // The input parameter can be an event or a number - function seek(input) { - var targetTime = 0; - var paused = player.elements.media.paused; - var duration = getDuration(); - - if (utils.is.number(input)) { - targetTime = input; - } else if (utils.is.event(input) && utils.inArray(['input', 'change'], input.type)) { - // It's the seek slider - // Seek to the selected time - targetTime = ((input.target.value / input.target.max) * duration); - } - - // Normalise targetTime - if (targetTime < 0) { - targetTime = 0; - } else if (targetTime > duration) { - targetTime = duration; - } - - // Update seek range and progress - updateSeekDisplay(targetTime); - - // Set the current time - // Try/catch incase the media isn't set and we're calling seek() from source() and IE moans - try { - player.elements.media.currentTime = targetTime.toFixed(4); - } catch (e) {} - - // Embeds - if (utils.inArray(types.embed, player.type)) { - switch (player.type) { - case 'youtube': - player.embed.seekTo(targetTime); - break; - - case 'vimeo': - // Round to nearest second for vimeo - player.embed.setCurrentTime(targetTime.toFixed(0)); - break; - - case 'soundcloud': - player.embed.seekTo(targetTime * 1000); - break; - } - - if (paused) { - pause(); - } - - // Trigger timeupdate - trigger(player.elements.media, 'timeupdate'); - - // Set seeking flag - player.elements.media.seeking = true; - - // Trigger seeking - trigger(player.elements.media, 'seeking'); - } - - // Logging - log('Seeking to ' + player.elements.media.currentTime + ' seconds'); - } - - // Get the duration (or custom if set) - function getDuration() { - // It should be a number, but parse it just incase - var duration = parseInt(config.duration); - - // True duration - var mediaDuration = 0; - - // Only if duration available - if (player.elements.media.duration !== null && !isNaN(player.elements.media.duration)) { - mediaDuration = player.elements.media.duration; - } - - // If custom duration is funky, use regular duration - return (isNaN(duration) ? mediaDuration : duration); - } - // Check playing state function checkPlaying() { - utils.toggleClass(player.elements.container, config.classes.playing, !player.elements.media.paused); + utils.toggleClass(player.elements.container, player.config.classes.playing, !player.media.paused); - utils.toggleClass(player.elements.container, config.classes.stopped, player.elements.media.paused); + utils.toggleClass(player.elements.container, player.config.classes.stopped, player.media.paused); - toggleControls(player.elements.media.paused); - } - - // Save scroll position - function saveScrollPosition() { - scroll = { - x: window.pageXOffset || 0, - y: window.pageYOffset || 0 - }; - } - - // Restore scroll position - function restoreScrollPosition() { - window.scrollTo(scroll.x, scroll.y); - } - - // Toggle fullscreen - function toggleFullscreen(event) { - // Check for native support - var nativeSupport = support.fullscreen; - - if (nativeSupport) { - // If it's a fullscreen change event, update the UI - if (event && event.type === fullscreen.eventType) { - player.fullscreen.active = fullscreen.isFullScreen(player.elements.container); - } else { - // Else it's a user request to enter or exit - if (!fullscreen.isFullScreen(player.elements.container)) { - // Save scroll position - saveScrollPosition(); - - // Request full screen - fullscreen.requestFullScreen(player.elements.container); - } else { - // Bail from fullscreen - fullscreen.cancelFullScreen(); - } - - // Check if we're actually full screen (it could fail) - player.fullscreen.active = fullscreen.isFullScreen(player.elements.container); - - return; - } - } else { - // Otherwise, it's a simple toggle - player.fullscreen.active = !player.fullscreen.active; - - // Bind/unbind escape key - document.body.style.overflow = player.fullscreen.active ? 'hidden' : ''; - } - - // Set class hook - utils.toggleClass(player.elements.container, config.classes.fullscreen.active, player.fullscreen.active); - - // Trap focus - focusTrap(player.fullscreen.active); - - // Set button state - if (player.elements.buttons && player.elements.buttons.fullscreen) { - utils.toggleState(player.elements.buttons.fullscreen, player.fullscreen.active); - } - - // Trigger an event - trigger(player.elements.container, player.fullscreen.active ? 'enterfullscreen' : 'exitfullscreen', true); - - // Restore scroll position - if (!player.fullscreen.active && nativeSupport) { - restoreScrollPosition(); - } + player.toggleControls(player.media.paused); } // Show/hide menu @@ -3471,131 +3110,10 @@ pane.removeAttribute('tabindex'); } - // Mute - function toggleMute(muted) { - // If the method is called without parameter, toggle based on current value - if (!utils.is.boolean(muted)) { - muted = !player.elements.media.muted; - } - - // Set button state - utils.toggleState(player.elements.buttons.mute, muted); - - // Set mute on the player - player.elements.media.muted = muted; - - // If volume is 0 after unmuting, set to default - if (player.elements.media.volume === 0) { - setVolume(config.volume); - } - - // Embeds - if (utils.inArray(types.embed, player.type)) { - // YouTube - switch (player.type) { - case 'youtube': - player.embed[player.elements.media.muted ? 'mute' : 'unMute'](); - break; - - case 'vimeo': - case 'soundcloud': - player.embed.setVolume(player.elements.media.muted ? 0 : parseFloat(config.volume / 10)); - break; - } - - // Trigger volumechange for embeds - trigger(player.elements.media, 'volumechange'); - } - } - - // Set volume - function setVolume(volume) { - var max = 10; - var min = 0; - - // If volume is event, get from input - if (utils.is.event(volume)) { - volume = volume.target.value; - } - - // Load volume from storage if no value specified - if (utils.is.undefined(volume)) { - volume = player.storage.volume; - } - - // Use config if all else fails - if (volume === null || isNaN(volume)) { - volume = config.volume; - } - - // Maximum is volumeMax - if (volume > max) { - volume = max; - } - // Minimum is volumeMin - if (volume < min) { - volume = min; - } - - // Set the player volume - player.elements.media.volume = parseFloat(volume / max); - - // Set the display - if (player.elements.display.volume) { - player.elements.display.volume.value = volume; - } - - // Embeds - if (utils.inArray(types.embed, player.type)) { - switch (player.type) { - case 'youtube': - player.embed.setVolume(player.elements.media.volume * 100); - break; - - case 'vimeo': - case 'soundcloud': - player.embed.setVolume(player.elements.media.volume); - break; - } - - // Trigger volumechange for embeds - trigger(player.elements.media, 'volumechange'); - } - - // Toggle muted state - if (volume === 0) { - player.elements.media.muted = true; - } else if (player.elements.media.muted && volume > 0) { - toggleMute(); - } - } - - // Increase volume - function increaseVolume(step) { - var volume = player.elements.media.muted ? 0 : (player.elements.media.volume * 10); - - if (!utils.is.number(step)) { - step = 1; - } - - setVolume(volume + step); - } - - // Decrease volume - function decreaseVolume(step) { - var volume = player.elements.media.muted ? 0 : (player.elements.media.volume * 10); - - if (!utils.is.number(step)) { - step = 1; - } - - setVolume(volume - step); - } - // Update volume UI and storage function updateVolume() { // Get the current volume - var volume = player.elements.media.muted ? 0 : (player.elements.media.volume * 10); + var volume = player.media.muted ? 0 : (player.media.volume * 10); // Update the <input type="range"> if present if (player.supported.full) { @@ -3613,7 +3131,7 @@ }); // Toggle class if muted - utils.toggleClass(player.elements.container, config.classes.muted, (volume === 0)); + utils.toggleClass(player.elements.container, player.config.classes.muted, (volume === 0)); // Update checkbox for mute state if (player.supported.full && player.elements.buttons.mute) { @@ -3623,7 +3141,7 @@ // Check if media is loading function checkLoading(event) { - var loading = (event.type === 'waiting'); + player.loading = (event.type === 'waiting'); // Clear timer clearTimeout(timers.loading); @@ -3631,11 +3149,11 @@ // Timer to prevent flicker when seeking timers.loading = setTimeout(function() { // Toggle container class hook - utils.toggleClass(player.elements.container, config.classes.loading, loading); + utils.toggleClass(player.elements.container, player.config.classes.loading, player.loading); // Show controls if loading, hide if done - toggleControls(loading); - }, (loading ? 250 : 0)); + player.toggleControls(player.loading); + }, (player.loading ? 250 : 0)); } // Update <progress> elements @@ -3644,9 +3162,9 @@ return; } - var progress = player.elements.display.played, - value = 0, - duration = getDuration(); + var progress = player.elements.display.played; + var value = 0; + var duration = player.getDuration(); if (event) { switch (event.type) { @@ -3657,7 +3175,7 @@ return; } - value = utils.getPercentage(player.elements.media.currentTime, duration); + value = utils.getPercentage(player.media.currentTime, duration); // Set seek range value only if it's a 'natural' time event if (event.type === 'timeupdate' && player.elements.inputs.seek) { @@ -3671,7 +3189,7 @@ case 'progress': progress = player.elements.display.buffer; value = (function() { - var buffered = player.elements.media.buffered; + var buffered = player.media.buffered; if (buffered && buffered.length) { // HTML5 @@ -3688,8 +3206,8 @@ } } - if (utils.is.number(config.loop.start) && utils.is.number(config.loop.end) && player.elements.media.currentTime >= config.loop.end) { - seek(config.loop.start); + if (utils.is.number(player.config.loop.start) && utils.is.number(player.config.loop.end) && player.media.currentTime >= player.config.loop.end) { + player.seek(player.config.loop.start); } setProgress(progress, value); @@ -3741,9 +3259,10 @@ var secs = parseInt(time % 60); var mins = parseInt((time / 60) % 60); var hours = parseInt(((time / 60) / 60) % 60); + var duration = player.getDuration(); // Do we need to display hours? - var displayHours = (parseInt(((getDuration() / 60) / 60) % 60) > 0); + var displayHours = (parseInt(((duration / 60) / 60) % 60) > 0); // Ensure it's two digits. For example, 03 rather than 3. secs = ('0' + secs).slice(-2); @@ -3766,10 +3285,10 @@ } // Determine duration - var duration = getDuration() || 0; + var duration = player.getDuration() || 0; // If there's only one time display, display duration there - if (!player.elements.display.duration && config.displayDuration && player.elements.media.paused) { + if (!player.elements.display.duration && player.config.displayDuration && player.media.paused) { updateTimeDisplay(duration, player.elements.display.currentTime); } @@ -3785,10 +3304,10 @@ // Handle time change event function timeUpdate(event) { // Duration - updateTimeDisplay(player.elements.media.currentTime, player.elements.display.currentTime); + updateTimeDisplay(player.media.currentTime, player.elements.display.currentTime); // Ignore updates while seeking - if (event && event.type === 'timeupdate' && player.elements.media.seeking) { + if (event && event.type === 'timeupdate' && player.media.seeking) { return; } @@ -3803,8 +3322,8 @@ time = 0; } - var duration = getDuration(), - value = utils.getPercentage(time, duration); + var duration = player.getDuration(); + var value = utils.getPercentage(time, duration); // Update progress if (player.elements.progress && player.elements.display.played) { @@ -3819,17 +3338,17 @@ // Update hover tooltip for seeking function updateSeekTooltip(event) { - var duration = getDuration(); + var duration = player.getDuration(); // Bail if setting not true - if (!config.tooltips.seek || !utils.is.htmlElement(player.elements.inputs.seek) || !utils.is.htmlElement(player.elements.display.seekTooltip) || duration === 0) { + if (!player.config.tooltips.seek || !utils.is.htmlElement(player.elements.inputs.seek) || !utils.is.htmlElement(player.elements.display.seekTooltip) || duration === 0) { return; } // Calculate percentage var clientRect = player.elements.inputs.seek.getBoundingClientRect(); var percent = 0; - var visible = config.classes.tooltip + '--visible'; + var visible = player.config.classes.tooltip + '--visible'; // Determine percentage, if already visible if (utils.is.event(event)) { @@ -3862,108 +3381,6 @@ } } - // Show the player controls in fullscreen mode - function toggleControls(toggle) { - // Don't hide if config says not to, it's audio, or not ready or loading - if (!config.hideControls || player.type === 'audio') { - return; - } - - var delay = 0; - var isEnterFullscreen = false; - var show = toggle; - var loading = utils.hasClass(player.elements.container, config.classes.loading); - - // Default to false if no boolean - if (!utils.is.boolean(toggle)) { - if (toggle && toggle.type) { - // Is the enter fullscreen event - isEnterFullscreen = (toggle.type === 'enterfullscreen'); - - // Whether to show controls - show = utils.inArray(['mousemove', 'touchstart', 'mouseenter', 'focus'], toggle.type); - - // Delay hiding on move events - if (utils.inArray(['mousemove', 'touchmove'], toggle.type)) { - delay = 2000; - } - - // Delay a little more for keyboard users - if (toggle.type === 'focus') { - delay = 3000; - } - } else { - show = utils.hasClass(player.elements.container, config.classes.hideControls); - } - } - - // Clear timer every movement - window.clearTimeout(timers.hover); - - // If the mouse is not over the controls, set a timeout to hide them - if (show || player.elements.media.paused || loading) { - utils.toggleClass(player.elements.container, config.classes.hideControls, false); - - // Always show controls when paused or if touch - if (player.elements.media.paused || loading) { - return; - } - - // Delay for hiding on touch - if (support.touch) { - delay = 3000; - } - } - - // If toggle is false or if we're playing (regardless of toggle), - // then set the timer to hide the controls - if (!show || !player.elements.media.paused) { - timers.hover = window.setTimeout(function() { - // If the mouse is over the controls (and not entering fullscreen), bail - if ((player.elements.controls.pressed || player.elements.controls.hover) && !isEnterFullscreen) { - return; - } - - utils.toggleClass(player.elements.container, config.classes.hideControls, true); - }, delay); - } - } - - // Add common function to retrieve media source - function source(source) { - // If not null or undefined, parse it - if (!utils.is.undefined(source)) { - updateSource(source); - return; - } - - // Return the current source - var url; - switch (player.type) { - case 'youtube': - url = player.embed.getVideoUrl(); - break; - - case 'vimeo': - player.embed.getVideoUrl.then(function(value) { - url = value; - }); - break; - - case 'soundcloud': - player.embed.getCurrentSound(function(object) { - url = object.permalink_url; - }); - break; - - default: - url = player.elements.media.currentSrc; - break; - } - - return url || ''; - } - // Update source // Sources are not checked for support so be careful function updateSource(source) { @@ -3973,10 +3390,10 @@ } // Remove ready class hook - utils.toggleClass(player.elements.container, config.classes.ready, false); + player.ready = false; - // Pause playback - pause(); + // Stop playback + player.stop(); // Update seek range and progress updateSeekDisplay(); @@ -4021,56 +3438,56 @@ } // Check for support - player.supported = utils.checkSupport(player.type, config.inline); + player.supported = utils.checkSupport(player.type, player.config.inline); // Create new markup switch (player.type) { case 'video': - player.elements.media = utils.createElement('video'); + player.media = utils.createElement('video'); break; case 'audio': - player.elements.media = utils.createElement('audio'); + player.media = utils.createElement('audio'); break; case 'youtube': case 'vimeo': case 'soundcloud': - player.elements.media = utils.createElement('div'); + player.media = utils.createElement('div'); player.embedId = source.sources[0].src; break; } // Inject the new element - utils.prependChild(player.elements.container, player.elements.media); + utils.prependChild(player.elements.container, player.media); // Autoplay the new source? if (utils.is.boolean(source.autoplay)) { - config.autoplay = source.autoplay; + player.config.autoplay = source.autoplay; } // Set attributes for audio and video if (utils.inArray(types.html5, player.type)) { - if (config.crossorigin) { - player.elements.media.setAttribute('crossorigin', ''); + if (player.config.crossorigin) { + player.media.setAttribute('crossorigin', ''); } - if (config.autoplay) { - player.elements.media.setAttribute('autoplay', ''); + if (player.config.autoplay) { + player.media.setAttribute('autoplay', ''); } if ('poster' in source) { - player.elements.media.setAttribute('poster', source.poster); + player.media.setAttribute('poster', source.poster); } - if (config.loop.active) { - player.elements.media.setAttribute('loop', ''); + if (player.config.loop.active) { + player.media.setAttribute('loop', ''); } - if (config.inline) { - player.elements.media.setAttribute('playsinline', ''); + if (player.config.inline) { + player.media.setAttribute('playsinline', ''); } } // Restore class hooks - utils.toggleClass(player.elements.container, config.classes.fullscreen.active, player.fullscreen.active); - utils.toggleClass(player.elements.container, config.classes.captions.active, player.captions.enabled); + utils.toggleClass(player.elements.container, player.config.classes.fullscreen.active, player.fullscreen.active); + utils.toggleClass(player.elements.container, player.config.classes.captions.active, player.captions.enabled); toggleStyleHook(); // Set new sources for html5 @@ -4089,7 +3506,7 @@ } // Load HTML5 sources - player.elements.media.load(); + player.media.load(); } // If HTML5 or embed but not fully supported, setupInterface and call ready now @@ -4102,20 +3519,13 @@ } // Set aria title and iframe title - config.title = source.title; + player.config.title = source.title; setTitle(); } // Destroy instance adn wait for callback // Vimeo throws a wobbly if you don't wait - destroy(setup, false); - } - - // Update poster - function updatePoster(source) { - if (player.type === 'video') { - player.elements.media.setAttribute('poster', source); - } + player.destroy(setup, false); } // Listen for control events @@ -4124,8 +3534,8 @@ var inputEvent = (player.browser.isIE ? 'change' : 'input'); // Click play/pause helper - function _togglePlay() { - var play = togglePlay(); + function togglePlay() { + var play = player.togglePlay(); // Determine which buttons var trigger = player.elements.buttons[play ? 'play' : 'pause']; @@ -4133,7 +3543,7 @@ // Setup focus and tab focus if (target) { - var hadTabFocus = utils.hasClass(trigger, config.classes.tabFocus); + var hadTabFocus = utils.hasClass(trigger, player.config.classes.tabFocus); setTimeout(function() { if (utils.is.htmlElement(target)) { @@ -4141,8 +3551,8 @@ } if (hadTabFocus) { - utils.toggleClass(trigger, config.classes.tabFocus, false); - utils.toggleClass(target, config.classes.tabFocus, true); + utils.toggleClass(trigger, player.config.classes.tabFocus, false); + utils.toggleClass(target, player.config.classes.tabFocus, true); } }, 100); } @@ -4155,19 +3565,19 @@ // Detect tab focus function checkTabFocus(focused) { - utils.toggleClass(getElements('.' + config.classes.tabFocus), config.classes.tabFocus, false); + utils.toggleClass(getElements('.' + player.config.classes.tabFocus), player.config.classes.tabFocus, false); if (player.elements.container.contains(focused)) { - utils.toggleClass(focused, config.classes.tabFocus, true); + utils.toggleClass(focused, player.config.classes.tabFocus, true); } } // Keyboard shortcuts - if (config.keyboardShortcuts.focused) { + if (player.config.keyboardShortcuts.focused) { var last = null; // Handle global presses - if (config.keyboardShortcuts.global) { + if (player.config.keyboardShortcuts.global) { utils.on(window, 'keydown keyup', function(event) { var code = getKeyCode(event); var focused = utils.getFocusElement(); @@ -4176,7 +3586,7 @@ // Only handle global key press if key is in the allowed keys // and if the focused element is not editable (e.g. text input) // and any that accept key input http://webaim.org/techniques/keyboard/ - if (utils.inArray(allowed, code) && (!utils.is.htmlElement(focused) || !utils.matches(focused, config.selectors.editable))) { + if (utils.inArray(allowed, code) && (!utils.is.htmlElement(focused) || !utils.matches(focused, player.config.selectors.editable))) { handleKey(event); } }, false); @@ -4200,7 +3610,7 @@ // Seek by the number keys function seekByKey() { // Get current duration - var duration = player.elements.media.duration; + var duration = player.media.duration; // Bail if we have no duration set if (!utils.is.number(duration)) { @@ -4208,7 +3618,7 @@ } // Divide the max duration into 10th's and times by the number value - seek((duration / 10) * (code - 48)); + player.seek((duration / 10) * (code - 48)); } // Handle the key on keydown @@ -4253,66 +3663,66 @@ case 75: // Space and K key if (!held) { - _togglePlay(); + togglePlay(); } break; case 38: // Arrow up - increaseVolume(); + player.increaseVolume(); break; case 40: // Arrow down - decreaseVolume(); + player.decreaseVolume(); break; case 77: // M key if (!held) { - toggleMute(); + player.toggleMute(); } break; case 39: // Arrow forward - forward(); + player.forward(); break; case 37: // Arrow back - rewind(); + player.rewind(); break; case 70: // F key - toggleFullscreen(); + player.toggleFullscreen(); break; case 67: // C key if (!held) { - toggleCaptions(); + player.toggleCaptions(); } break; case 73: - toggleLoop('start'); + player.loop('start'); break; case 76: - toggleLoop(); + player.loop(); break; case 79: - toggleLoop('end'); + player.loop('end'); break; } // Escape is handle natively when in full screen // So we only need to worry about non native if (!support.fullscreen && player.fullscreen.active && code === 27) { - toggleFullscreen(); + player.toggleFullscreen(); } // Store last code for next cycle @@ -4332,7 +3742,7 @@ } }); utils.on(document.body, 'click', function() { - utils.toggleClass(getElement('.' + config.classes.tabFocus), config.classes.tabFocus, false); + utils.toggleClass(getElement('.' + player.config.classes.tabFocus), player.config.classes.tabFocus, false); }); for (var button in player.elements.buttons) { var element = player.elements.buttons[button]; @@ -4353,44 +3763,50 @@ } // Play - utils.proxy(player.elements.buttons.play, 'click', config.listeners.play, _togglePlay); - utils.proxy(player.elements.buttons.playLarge, 'click', config.listeners.play, _togglePlay); + utils.proxy(player.elements.buttons.play, 'click', player.config.listeners.play, togglePlay); + utils.proxy(player.elements.buttons.playLarge, 'click', player.config.listeners.play, togglePlay); // Pause - utils.proxy(player.elements.buttons.pause, 'click', config.listeners.pause, _togglePlay); + utils.proxy(player.elements.buttons.pause, 'click', player.config.listeners.pause, togglePlay); // Pause - utils.proxy(player.elements.buttons.restart, 'click', config.listeners.restart, seek); + utils.proxy(player.elements.buttons.restart, 'click', player.config.listeners.restart, function() { + player.restart(); + }); // Rewind - utils.proxy(player.elements.buttons.rewind, 'click', config.listeners.rewind, rewind); + utils.proxy(player.elements.buttons.rewind, 'click', player.config.listeners.rewind, function() { + player.rewind(); + }); // Rewind - utils.proxy(player.elements.buttons.forward, 'click', config.listeners.forward, forward); + utils.proxy(player.elements.buttons.forward, 'click', player.config.listeners.forward, function() { + player.forward(); + }); // Mute - utils.proxy(player.elements.buttons.mute, 'click', config.listeners.mute, toggleMute); + utils.proxy(player.elements.buttons.mute, 'click', player.config.listeners.mute, function() { + player.toggleMute(); + }); // Captions - utils.proxy(player.elements.buttons.captions, 'click', config.listeners.captions, toggleCaptions); + utils.proxy(player.elements.buttons.captions, 'click', player.config.listeners.captions, function() { + player.toggleCaptions(); + }); // Fullscreen - utils.proxy(player.elements.buttons.fullscreen, 'click', config.listeners.fullscreen, toggleFullscreen); + utils.proxy(player.elements.buttons.fullscreen, 'click', player.config.listeners.fullscreen, function(event) { + player.toggleFullscreen(event); + }); // Picture-in-Picture - utils.proxy(player.elements.buttons.pip, 'click', config.listeners.pip, function(event) { - if (!support.pip) { - return; - } - player.elements.media.webkitSetPresentationMode(player.elements.media.webkitPresentationMode === 'picture-in-picture' ? 'inline' : 'picture-in-picture'); + utils.proxy(player.elements.buttons.pip, 'click', player.config.listeners.pip, function() { + player.togglePictureInPicture(); }); // Airplay - utils.proxy(player.elements.buttons.airplay, 'click', config.listeners.airplay, function(event) { - if (!support.airplay) { - return; - } - player.elements.media.webkitShowPlaybackTargetPicker(); + utils.proxy(player.elements.buttons.airplay, 'click', player.config.listeners.airplay, function() { + player.airPlay(); }); // Settings menu @@ -4405,49 +3821,60 @@ // Settings menu items - use event delegation as items are added/removed utils.on(player.elements.settings.form, 'click', function(event) { // Settings - Language - if (utils.matches(event.target, config.selectors.inputs.language)) { - handlerProxy.call(this, event, config.listeners.language, setLanguage); + if (utils.matches(event.target, player.config.selectors.inputs.language)) { + handlerProxy.call(this, event, player.config.listeners.language, function() { + player.language(event.target.value.toLowerCase()); + }); } // Settings - Quality - else if (utils.matches(event.target, config.selectors.inputs.quality)) { - handlerProxy.call(this, event, config.listeners.quality, function() { + else if (utils.matches(event.target, player.config.selectors.inputs.quality)) { + handlerProxy.call(this, event, player.config.listeners.quality, function() { warn("Set quality"); }); } // Settings - Speed - else if (utils.matches(event.target, config.selectors.inputs.speed)) { - handlerProxy.call(this, event, config.listeners.speed, setSpeed); + else if (utils.matches(event.target, player.config.selectors.inputs.speed)) { + handlerProxy.call(this, event, player.config.listeners.speed, function() { + player.setSpeed(parseFloat(event.target.value)); + }); } // Settings - Looping // TODO: use toggle buttons - else if (utils.matches(event.target, config.selectors.buttons.loop)) { - handlerProxy.call(this, event, config.listeners.loop, function() { + else if (utils.matches(event.target, player.config.selectors.buttons.loop)) { + handlerProxy.call(this, event, player.config.listeners.loop, function() { // TODO: This should be done in the method itself I think var value = event.target.getAttribute('data-loop__value') || event.target.getAttribute('data-loop__type'); if (utils.inArray(['start', 'end', 'all', 'none'], value)) { - toggleLoop(value); + player.loop(value); } }); } }); // Seek - utils.proxy(player.elements.inputs.seek, inputEvent, config.listeners.seek, seek); + utils.proxy(player.elements.inputs.seek, inputEvent, player.config.listeners.seek, function(event) { + var duration = player.getDuration(); + player.seek((event.target.value / event.target.max) * duration); + }); // Seek - utils.proxy(player.elements.inputs.volume, inputEvent, config.listeners.volume, setVolume); + utils.proxy(player.elements.inputs.volume, inputEvent, player.config.listeners.volume, function() { + player.setVolume(event.target.value); + }); // Seek tooltip utils.on(player.elements.progress, 'mouseenter mouseleave mousemove', updateSeekTooltip); // Toggle controls visibility based on mouse movement - if (config.hideControls) { + if (player.config.hideControls) { // Toggle controls on mouse events and entering fullscreen - utils.on(player.elements.container, 'mouseenter mouseleave mousemove touchstart touchend touchcancel touchmove enterfullscreen', toggleControls); + utils.on(player.elements.container, 'mouseenter mouseleave mousemove touchstart touchend touchcancel touchmove enterfullscreen', function(event) { + player.toggleControls(event); + }); // Watch for cursor over controls so they don't hide when trying to interact utils.on(player.elements.controls, 'mouseenter mouseleave', function(event) { @@ -4461,11 +3888,13 @@ // Focus in/out on controls // TODO: Check we need capture here - utils.on(player.elements.controls, 'focus blur', toggleControls, true, true); + utils.on(player.elements.controls, 'focus blur', function(event) { + player.toggleControls(event); + }, true, true); } // Mouse wheel for volume - utils.proxy(player.elements.inputs.volume, 'wheel', config.listeners.volume, function(event) { + utils.proxy(player.elements.inputs.volume, 'wheel', player.config.listeners.volume, function(event) { // Detect "natural" scroll - suppored on OS X Safari only // Other browsers on OS X will be inverted until support improves var inverted = event.webkitDirectionInvertedFromDevice; @@ -4475,10 +3904,10 @@ // Scroll down (or up on natural) to decrease if (event.deltaY < 0 || event.deltaX > 0) { if (inverted) { - decreaseVolume(step); + player.decreaseVolume(step); direction = -1; } else { - increaseVolume(step); + player.increaseVolume(step); direction = 1; } } @@ -4486,68 +3915,70 @@ // Scroll up (or down on natural) to increase if (event.deltaY > 0 || event.deltaX < 0) { if (inverted) { - increaseVolume(step); + player.increaseVolume(step); direction = 1; } else { - decreaseVolume(step); + player.decreaseVolume(step); direction = -1; } } // Don't break page scrolling at max and min - if ((direction === 1 && player.elements.media.volume < 1) || - (direction === -1 && player.elements.media.volume > 0)) { + if ((direction === 1 && player.media.volume < 1) || + (direction === -1 && player.media.volume > 0)) { event.preventDefault(); } }, false); // Handle user exiting fullscreen by escaping etc if (support.fullscreen) { - utils.on(document, fullscreen.eventType, toggleFullscreen); + utils.on(document, fullscreen.eventType, function(event) { + player.toggleFullscreen(event); + }); } } // Listen for media events function mediaListeners() { // Time change on media - utils.on(player.elements.media, 'timeupdate seeking', timeUpdate); + utils.on(player.media, 'timeupdate seeking', timeUpdate); // Display duration - utils.on(player.elements.media, 'durationchange loadedmetadata', displayDuration); + utils.on(player.media, 'durationchange loadedmetadata', displayDuration); // Handle the media finishing - utils.on(player.elements.media, 'ended', function() { + utils.on(player.media, 'ended', function() { // Show poster on end - if (player.type === 'video' && config.showPosterOnEnd) { + if (player.type === 'video' && player.config.showPosterOnEnd) { // Clear if (player.type === 'video') { setCaption(); } // Restart - seek(); + player.restart(); // Re-load media - player.elements.media.load(); + player.media.load(); } }); // Check for buffer progress - utils.on(player.elements.media, 'progress playing', updateProgress); + utils.on(player.media, 'progress playing', updateProgress); // Handle native mute - utils.on(player.elements.media, 'volumechange', updateVolume); + utils.on(player.media, 'volumechange', updateVolume); // Handle native play/pause - utils.on(player.elements.media, 'play pause ended', checkPlaying); + utils.on(player.media, 'play pause ended', checkPlaying); // Loading - utils.on(player.elements.media, 'waiting canplay seeked', checkLoading); + utils.on(player.media, 'waiting canplay seeked', checkLoading); // Click video - if (config.clickToPlay && player.type !== 'audio') { + if (player.config.clickToPlay && player.type !== 'audio') { // Re-fetch the wrapper - var wrapper = getElement('.' + config.classes.videoWrapper); + var wrapper = getElement('.' + player.config.classes.video); // Bail if there's no wrapper (this should never happen) if (!wrapper) { @@ -4560,31 +3991,31 @@ // On click play, pause ore restart utils.on(wrapper, 'click', function() { // Touch devices will just show controls (if we're hiding controls) - if (config.hideControls && support.touch && !player.elements.media.paused) { + if (player.config.hideControls && support.touch && !player.media.paused) { return; } - if (player.elements.media.paused) { - play(); - } else if (player.elements.media.ended) { - seek(); - play(); + if (player.media.paused) { + player.play(); + } else if (player.media.ended) { + player.restart(); + player.play(); } else { - pause(); + player.pause(); } }); } // Disable right click - if (config.disableContextMenu) { - utils.on(player.elements.media, 'contextmenu', function(event) { + if (player.config.disableContextMenu) { + utils.on(player.media, 'contextmenu', function(event) { event.preventDefault(); }, false); } // Proxy events to container // Bubble up key events for Edge - utils.on(player.elements.media, config.events.concat(['keyup', 'keydown']).join(' '), function(event) { + utils.on(player.media, player.config.events.concat(['keyup', 'keydown']).join(' '), function(event) { trigger(player.elements.container, event.type, true); }); } @@ -4597,7 +4028,7 @@ } // Remove child sources - var sources = player.elements.media.querySelectorAll('source'); + var sources = player.media.querySelectorAll('source'); for (var i = 0; i < sources.length; i++) { utils.removeElement(sources[i]); } @@ -4605,84 +4036,17 @@ // Set blank video src attribute // This is to prevent a MEDIA_ERR_SRC_NOT_SUPPORTED error // Info: http://stackoverflow.com/questions/32231579/how-to-properly-dispose-of-an-html5-video-and-close-socket-or-connection - player.elements.media.setAttribute('src', 'https://cdn.selz.com/plyr/blank.mp4'); + player.media.setAttribute('src', 'https://cdn.selz.com/plyr/blank.mp4'); // Load the new empty source // This will cancel existing requests // See https://github.com/sampotts/plyr/issues/174 - player.elements.media.load(); + player.media.load(); // Debugging log('Cancelled network requests'); } - // 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(callback, restore) { - // Type specific stuff - switch (player.type) { - case 'youtube': - // Clear timers - window.clearInterval(timers.buffering); - window.clearInterval(timers.playing); - - // Destroy YouTube API - player.embed.destroy(); - - // Clean up - cleanUp(); - - break; - - case 'vimeo': - // Destroy Vimeo API - // then clean up (wait, to prevent postmessage errors) - player.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 (!utils.is.boolean(restore)) { - restore = true; - } - - // Callback - if (utils.is.function(callback)) { - callback.call(player.original); - } - - // Bail if we don't need to restore the original element - if (!restore) { - return; - } - - // Replace the container with the original element provided - player.elements.container.parentNode.replaceChild(player.original, player.elements.container); - - // Reset overflow (incase destroyed while fullscreen) - document.body.style.overflow = ''; - - // Event - trigger(player.original, 'destroyed', true); - } - } - // Setup the UI function setupInterface() { // Don't setup interface if no support @@ -4729,14 +4093,14 @@ setupCaptions(); // Set volume - setVolume(); + player.setVolume(); updateVolume(); // Set playback speed - setSpeed(); + player.setSpeed(); // Set loop - toggleLoop(); + player.loop(); // Reset time display timeUpdate(); @@ -4747,46 +4111,40 @@ // Everything done function ready() { - // Set class hook on media element - // utils.toggleClass(player.elements.media, defaults.classes.setup, true); - - // Set container class for ready - // utils.toggleClass(player.elements.container, config.classes.ready, true); - - // Store a refernce to instance - // player.elements.media.plyr = api; - // Ready event at end of execution stack trigger(player.elements.container, 'ready', true); // Autoplay - if (config.autoplay) { - play(); + if (player.config.autoplay) { + player.play(); } } // Setup a player function setup(target) { // We need an element to setup - if (!utils.is.htmlElement(target)) { - error('Setup failed. No suitable element passed.'); - return false; + if (target === null || utils.is.undefined(target) || !utils.is.htmlElement(target)) { + error('Setup failed: no suitable element passed'); + return; } // Bail if not enabled - if (!config.enabled) { - return false; + if (!player.config.enabled) { + error('Setup failed: disabled by config'); + return; } // Bail if disabled or no basic support // You may want to disable certain UAs etc if (!utils.checkSupport().basic) { - return false; + error('Setup failed: no support'); + return; } // Bail if the element is initialized if (target.plyr) { - return false; + log('Target already setup'); + return target.plyr; } // Set media type based on tag or data attribute @@ -4799,8 +4157,14 @@ player.type = target.getAttribute('data-type'); player.embedId = target.getAttribute('data-video-id'); - if (utils.is.empty(player.type) || utils.is.empty(player.embedId)) { - return false; + if (utils.is.empty(player.type)) { + error('Setup failed: embed type missing'); + return; + } + + if (utils.is.empty(player.embedId)) { + error('Setup failed: video id missing'); + return; } // Clean up @@ -4815,13 +4179,14 @@ case 'video': case 'audio': player.type = type; - config.crossorigin = target.getAttribute('crossorigin') !== null; - config.autoplay = config.autoplay || (target.getAttribute('autoplay') !== null); - config.inline = target.getAttribute('playsinline') !== null; - config.loop.active = config.loop || (target.getAttribute('loop') !== null); + player.config.crossorigin = target.getAttribute('crossorigin') !== null; + player.config.autoplay = player.config.autoplay || (target.getAttribute('autoplay') !== null); + player.config.inline = target.getAttribute('playsinline') !== null; + player.config.loop.active = player.config.loop || (target.getAttribute('loop') !== null); break; default: + error('Setup failed: unsupported type'); return false; } @@ -4831,11 +4196,12 @@ // Load saved settings from localStorage setupStorage(); - // Check for support - player.supported = utils.checkSupport(player.type, config.inline); + // Check for support again but with type + player.supported = utils.checkSupport(player.type, player.config.inline); // If no native support, bail if (!player.supported.basic) { + error('Setup failed: no support'); return false; } @@ -4858,8 +4224,8 @@ setupMedia(); // Listen for events if debugging - if (config.debug) { - var events = config.events.concat(['setup', 'statechange', 'enterfullscreen', 'exitfullscreen', 'captionsenabled', 'captionsdisabled']); + if (player.config.debug) { + var events = player.config.events.concat(['setup', 'statechange', 'enterfullscreen', 'exitfullscreen', 'captionsenabled', 'captionsdisabled']); utils.on(player.elements.container, events.join(' '), function(event) { log(['event:', event.type].join(' ').trim()); @@ -4878,90 +4244,853 @@ // Set title on button and frame setTitle(); } - - // Successful setup - return true; } - // Expose prototypes - api = { - getOriginal: function() { - return player.original; - }, - getContainer: function() { - return player.elements.container - }, - getEmbed: function() { - return player.embed; - }, - getMedia: function() { - return player.elements.media; - }, - getType: function() { - return player.type; - }, - getDuration: getDuration, - getCurrentTime: function() { - return player.elements.media.currentTime; - }, - getVolume: function() { - return player.elements.media.volume; - }, - isMuted: function() { - return player.elements.media.muted; - }, - isReady: function() { - return utils.hasClass(player.elements.container, config.classes.ready); - }, - isLoading: function() { - return utils.hasClass(player.elements.container, config.classes.loading); - }, - isPaused: function() { - return player.elements.media.paused; - }, - isLooping: function() { - return config.loop.active; - }, - on: function(event, callback) { - utils.on(player.elements.container, event, callback); - return this; - }, - play: play, - pause: pause, - loop: toggleLoop, - stop: function() { - pause(); - seek(); - }, - restart: seek, - rewind: rewind, - forward: forward, - seek: seek, - source: source, - poster: updatePoster, - setVolume: setVolume, - setSpeed: setSpeed, - togglePlay: togglePlay, - toggleMute: toggleMute, - toggleCaptions: toggleCaptions, - toggleFullscreen: toggleFullscreen, - toggleControls: toggleControls, - setLanguage: setLanguage, - isFullscreen: player.fullscreen.active, - support: function(mimeType) { - return support.mime(player, mimeType); - }, - destroy: destroy + // Expose some core functions + player.core = { + getElement: getElement, + getElements: getElements, + trigger: trigger, + setCaption: setCaption, + setupCaptions: setupCaptions, + toggleNativeControls: toggleNativeControls, + updateTimeDisplay: updateTimeDisplay, + updateSeekDisplay: updateSeekDisplay, + updateSource: updateSource, + updateStorage: updateStorage, + timers: timers, + support: support, + + // Debugging + log: log, + warn: warn, + error: error }; // Initialize instance - if (!setup(player.elements.media)) { - return null; + setup(player.media); + } + + // API + // --------------------------------------- + // Play + Player.prototype.play = function() { + var player = this; + + if ('play' in player.media) { + player.media.play(); } - // Expose API - return api; - } + // Allow chaining + return player; + }; + + // Pause + Player.prototype.pause = function() { + var player = this; + + if ('pause' in player.media) { + player.media.pause(); + } + + // Allow chaining + return player; + }; + + // Toggle playback + Player.prototype.togglePlay = function(toggle) { + var player = this; + + // True toggle if nothing passed + if (!utils.is.boolean(toggle)) { + toggle = player.media.paused; + } + + if (toggle) { + player.play(); + } else { + player.pause(); + } + + return toggle; + }; + + // Stop + Player.prototype.stop = function() { + var player = this; + + player.restart(); + player.pause(); + + // Allow chaining + return player; + }; + + // Restart + Player.prototype.restart = function() { + var player = this; + + // Seek to 0 + player.seek(); + + // Allow chaining + return player; + }; + + // Rewind + Player.prototype.rewind = function(seekTime) { + var player = this; + + // Use default if needed + if (!utils.is.number(seekTime)) { + seekTime = player.config.seekTime; + } + + player.seek(player.media.currentTime - seekTime); + + // Allow chaining + return player; + }; + + // Fast forward + Player.prototype.forward = function(seekTime) { + var player = this; + + // Use default if needed + if (!utils.is.number(seekTime)) { + seekTime = player.config.seekTime; + } + + player.seek(player.media.currentTime + seekTime); + + // Allow chaining + return player; + }; + + // Seek to time + // The input parameter can be an event or a number + Player.prototype.seek = function(input) { + var player = this; + var targetTime = 0; + var paused = player.media.paused; + var duration = player.getDuration(); + + if (utils.is.number(input)) { + targetTime = input; + } + + // Normalise targetTime + if (targetTime < 0) { + targetTime = 0; + } else if (targetTime > duration) { + targetTime = duration; + } + + // Update seek range and progress + player.core.updateSeekDisplay(targetTime); + + // Set the current time + // Try/catch incase the media isn't set and we're calling seek() from source() and IE moans + try { + player.media.currentTime = targetTime.toFixed(4); + } catch (e) {} + + // Embeds + if (utils.inArray(types.embed, player.type)) { + switch (player.type) { + case 'youtube': + player.embed.seekTo(targetTime); + break; + + case 'vimeo': + // Round to nearest second for vimeo + player.embed.setCurrentTime(targetTime.toFixed(0)); + break; + + case 'soundcloud': + player.embed.seekTo(targetTime * 1000); + break; + } + + if (paused) { + player.pause(); + } + + // Trigger timeupdate + player.core.trigger(player.media, 'timeupdate'); + + // Set seeking flag + player.media.seeking = true; + + // Trigger seeking + player.core.trigger(player.media, 'seeking'); + } + + // Logging + player.core.log('Seeking to ' + player.media.currentTime + ' seconds'); + + // Allow chaining + return player; + }; + + // Set volume + Player.prototype.setVolume = function(volume) { + var player = this; + var max = 10; + var min = 0; + + // Load volume from storage if no value specified + if (utils.is.undefined(volume)) { + volume = player.storage.volume; + } + + // Use config if all else fails + if (volume === null || isNaN(volume)) { + volume = player.config.volume; + } + + // Maximum is volumeMax + if (volume > max) { + volume = max; + } + // Minimum is volumeMin + if (volume < min) { + volume = min; + } + + // Set the player volume + player.media.volume = parseFloat(volume / max); + + // Set the display + if (player.elements.display.volume) { + player.elements.display.volume.value = volume; + } + + // Embeds + if (utils.inArray(types.embed, player.type)) { + switch (player.type) { + case 'youtube': + player.embed.setVolume(player.media.volume * 100); + break; + + case 'vimeo': + case 'soundcloud': + player.embed.setVolume(player.media.volume); + break; + } + + // Trigger volumechange for embeds + player.core.trigger(player.media, 'volumechange'); + } + + // Toggle muted state + if (volume === 0) { + player.media.muted = true; + } else if (player.media.muted && volume > 0) { + player.toggleMute(); + } + + // Allow chaining + return player; + }; + + // Increase volume + Player.prototype.increaseVolume = function(step) { + var player = this; + var volume = player.media.muted ? 0 : (player.media.volume * 10); + + if (!utils.is.number(step)) { + step = 1; + } + + player.setVolume(volume + step); + + // Allow chaining + return player; + }; + + // Decrease volume + Player.prototype.decreaseVolume = function(step) { + var player = this; + var volume = player.media.muted ? 0 : (player.media.volume * 10); + + if (!utils.is.number(step)) { + step = 1; + } + + player.setVolume(volume - step); + + // Allow chaining + return player; + }; + + // Toggle mute + Player.prototype.toggleMute = function(muted) { + var player = this; + + // If the method is called without parameter, toggle based on current value + if (!utils.is.boolean(muted)) { + muted = !player.media.muted; + } + + // Set button state + utils.toggleState(player.elements.buttons.mute, muted); + + // Set mute on the player + player.media.muted = muted; + + // If volume is 0 after unmuting, set to default + if (player.media.volume === 0) { + player.volume(player.config.volume); + } + + // Embeds + if (utils.inArray(types.embed, player.type)) { + // YouTube + switch (player.type) { + case 'youtube': + player.embed[player.media.muted ? 'mute' : 'unMute'](); + break; + + case 'vimeo': + case 'soundcloud': + player.embed.setVolume(player.media.muted ? 0 : parseFloat(player.config.volume / 10)); + break; + } + + // Trigger volumechange for embeds + player.core.trigger(player.media, 'volumechange'); + } + + // Allow chaining + return player; + }; + + // Set playback speed + Player.prototype.setSpeed = function(speed) { + var player = this; + + // Load speed from storage or default value + if (!utils.is.number(speed)) { + speed = parseFloat(player.storage.speed || player.config.speed.selected); + } + + // Set min/max + if (speed < 0.1) { + speed = 0.1; + } + if (speed > 2.0) { + speed = 2.0; + } + + if (!utils.is.array(player.config.speed.options)) { + player.core.warn('Invalid speeds format'); + return; + } + + // Store current speed + player.config.speed.selected = speed; + + // Set HTML5 speed + // TODO: set YouTube + player.media.playbackRate = speed; + + // Save speed to localStorage + player.core.updateStorage({ + speed: speed + }); + + // Allow chaining + return player; + }; + + // Toggle loop + // TODO: Finish logic + // TODO: Set the indicator on load as user may pass loop as config + Player.prototype.loop = function(type) { + var player = this; + + // Set default to be a true toggle + if (!utils.inArray(['start', 'end', 'all', 'none', 'toggle'], type)) { + type = 'toggle'; + } + + var currentTime = Number(player.media.currentTime); + + switch (type) { + case 'start': + if (player.config.loop.end && player.config.loop.end <= currentTime) { + player.config.loop.end = null; + } + player.config.loop.start = currentTime; + player.config.loop.indicator.start = player.elements.display.played.value; + break; + + case 'end': + if (player.config.loop.start >= currentTime) { + return; + } + player.config.loop.end = currentTime; + player.config.loop.indicator.end = player.elements.display.played.value; + break; + + case 'all': + player.config.loop.start = 0; + player.config.loop.end = player.media.duration - 2; + player.config.loop.indicator.start = 0; + player.config.loop.indicator.end = 100; + break; + + case 'toggle': + if (player.config.loop.active) { + player.config.loop.start = 0; + player.config.loop.end = null; + } else { + player.config.loop.start = 0; + player.config.loop.end = player.media.duration - 2; + } + break; + + default: + player.config.loop.start = 0; + player.config.loop.end = null; + break; + } + + // Check if can loop + player.config.loop.active = utils.is.number(player.config.loop.start) && utils.is.number(player.config.loop.end); + var start = player.core.updateTimeDisplay(player.config.loop.start, player.core.getElement('[data-plyr-loop="start"]')); + var end = null; + + if (utils.is.number(player.config.loop.end)) { + // Find the <span> inside button + end = player.core.updateTimeDisplay(player.config.loop.end, player.core.getElement('[data-loop__value="loopout"]')); + } else { + // Find the <span> inside button + //end = document.querySelector('[data-loop__value="loopout"]').innerHTML = ''; + } + + if (player.config.loop.active) { + // TODO: Improve the design of the loop indicator and put styling in CSS where it's meant to be + //getElement('[data-menu="loop"]').innerHTML = start + ' - ' + end; + //getElement(player.config.selectors.progress.looped).style.position = 'absolute'; + //getElement(player.config.selectors.progress.looped).style.left = player.config.loopinPositionPercentage + '%'; + //getElement(player.config.selectors.progress.looped).style.width = (player.config.loopoutPositionPercentage - player.config.loopinPositionPercentage) + '%'; + //getElement(player.config.selectors.progress.looped).style.background = '#ffbb00'; + //getElement(player.config.selectors.progress.looped).style.height = '3px'; + //getElement(player.config.selectors.progress.looped).style.top = '3px'; + //getElement(player.config.selectors.progress.looped).style['border-radius'] = '100px'; + } else { + //getElement('[data-menu="loop"]').innerHTML = player.config.i18n.loopNone; + //getElement(player.config.selectors.progress.looped).style.width = '0px'; + } + + // Allow chaining + return player; + }; + + // Add common function to retrieve media source + Player.prototype.source = function(source) { + var player = this; + + // If object or string, parse it + if (utils.is.object(source)) { + player.core.updateSource(source); + return player; + } + + // Return the current source + var url; + + switch (player.type) { + case 'youtube': + url = player.embed.getVideoUrl(); + break; + + case 'vimeo': + player.embed.getVideoUrl.then(function(value) { + url = value; + }); + break; + + case 'soundcloud': + player.embed.getCurrentSound(function(object) { + url = object.permalink_url; + }); + break; + + default: + url = player.media.currentSrc; + break; + } + + return url; + }; + + // Set or get poster + Player.prototype.poster = function(source) { + var player = this; + + if (!utils.is.string(source)) { + return player.media.getAttribute('poster'); + } else if (player.type === 'video') { + player.media.setAttribute('poster', source); + } else { + player.core.warn('Poster can only be set on HTML5 video'); + } + + // Allow chaining + return player; + }; + + // Toggle captions + Player.prototype.toggleCaptions = function(show) { + var player = this; + + // If there's no full support, or there's no caption toggle + if (!player.supported.full || !player.elements.buttons.captions) { + return; + } + + // If the method is called without parameter, toggle based on current value + if (!utils.is.boolean(show)) { + show = (player.elements.container.className.indexOf(player.config.classes.captions.active) === -1); + } + + // Set global + player.captions.enabled = show; + + // Toggle state + utils.toggleState(player.elements.buttons.captions, player.captions.enabled); + + // Add class hook + utils.toggleClass(player.elements.container, player.config.classes.captions.active, player.captions.enabled); + + // Trigger an event + player.core.trigger(player.elements.container, player.captions.enabled ? 'captionsenabled' : 'captionsdisabled', true); + + // Save captions state to localStorage + player.core.updateStorage({ + captions: player.captions.enabled + }); + + // Allow chaining + return player; + }; + + // Select active caption + Player.prototype.language = function(language) { + var player = this; + + if (utils.is.string(language)) { + // Update config + player.config.captions.language = language.toLowerCase(); + } else { + // If no language passed, return current language + return player.config.captions.language; + } + + // Clear caption + player.core.setCaption(); + + // Re-run setup + player.core.setupCaptions(); + + // Allow chaining + return player; + }; + + // Toggle fullscreen + // Requires user input event + Player.prototype.toggleFullscreen = function(event) { + var player = this; + + // Save scroll position + function saveScrollPosition() { + scroll = { + x: window.pageXOffset || 0, + y: window.pageYOffset || 0 + }; + } + + // Restore scroll position + function restoreScrollPosition() { + window.scrollTo(scroll.x, scroll.y); + } + + // Check for native support + var nativeSupport = support.fullscreen; + + if (nativeSupport) { + // If it's a fullscreen change event, update the UI + if (utils.is.event(event) && event.type === fullscreen.eventType) { + player.fullscreen.active = fullscreen.isFullScreen(player.elements.container); + } else { + // Else it's a user request to enter or exit + if (!fullscreen.isFullScreen(player.elements.container)) { + // Save scroll position + saveScrollPosition(); + + // Request full screen + fullscreen.requestFullScreen(player.elements.container); + } else { + // Bail from fullscreen + fullscreen.cancelFullScreen(); + } + + // Check if we're actually full screen (it could fail) + player.fullscreen.active = fullscreen.isFullScreen(player.elements.container); + + return; + } + } else { + // Otherwise, it's a simple toggle + player.fullscreen.active = !player.fullscreen.active; + + // Bind/unbind escape key + document.body.style.overflow = player.fullscreen.active ? 'hidden' : ''; + } + + // Set class hook + utils.toggleClass(player.elements.container, player.config.classes.fullscreen.active, player.fullscreen.active); + + // Set button state + if (player.elements.buttons && player.elements.buttons.fullscreen) { + utils.toggleState(player.elements.buttons.fullscreen, player.fullscreen.active); + } + + // Trigger an event + player.core.trigger(player.elements.container, player.fullscreen.active ? 'enterfullscreen' : 'exitfullscreen', true); + + // Restore scroll position + if (!player.fullscreen.active && nativeSupport) { + restoreScrollPosition(); + } + + // Allow chaining + return player; + }; + + // Toggle picture-in-picture + // TODO: update player with state, support, enabled + // TODO: detect outside changes + Player.prototype.togglePictureInPicture = function(toggle) { + var player = this; + var states = { + pip: 'picture-in-picture', + inline: 'inline' + }; + + // Bail if no support + if (!player.core.support.pip) { + return; + } + + // Toggle based on current state if not passed + if (!utils.is.boolean(toggle)) { + toggle = player.media.webkitPresentationMode === states.inline; + } + + // Toggle based on current state + player.media.webkitSetPresentationMode(toggle ? states.pip : states.inline); + + // Allow chaining + return player; + }; + + // Trigger airplay + // TODO: update player with state, support, enabled + Player.prototype.airPlay = function() { + var player = this; + + // Bail if no support + if (!player.core.support.airplay) { + return; + } + + // Show dialog + player.media.webkitShowPlaybackTargetPicker(); + + // Allow chaining + return player; + }; + + // Show the player controls in fullscreen mode + Player.prototype.toggleControls = function(toggle) { + var player = this; + + // Don't hide if config says not to, it's audio, or not ready or loading + if (!player.config.hideControls || player.type === 'audio') { + return; + } + + var delay = 0; + var show = toggle; + var isEnterFullscreen = false; + var loading = utils.hasClass(player.elements.container, player.config.classes.loading); + + // Default to false if no boolean + if (!utils.is.boolean(toggle)) { + if (utils.is.event(toggle)) { + // Is the enter fullscreen event + isEnterFullscreen = (toggle.type === 'enterfullscreen'); + + // Whether to show controls + show = utils.inArray(['mousemove', 'touchstart', 'mouseenter', 'focus'], toggle.type); + + // Delay hiding on move events + if (utils.inArray(['mousemove', 'touchmove'], toggle.type)) { + delay = 2000; + } + + // Delay a little more for keyboard users + if (toggle.type === 'focus') { + delay = 3000; + } + } else { + show = utils.hasClass(player.elements.container, player.config.classes.hideControls); + } + } + + // Clear timer every movement + window.clearTimeout(player.core.timers.hover); + + // If the mouse is not over the controls, set a timeout to hide them + if (show || player.media.paused || loading) { + utils.toggleClass(player.elements.container, player.config.classes.hideControls, false); + + // Always show controls when paused or if touch + if (player.media.paused || loading) { + return; + } + + // Delay for hiding on touch + if (support.touch) { + delay = 3000; + } + } + + // If toggle is false or if we're playing (regardless of toggle), + // then set the timer to hide the controls + if (!show || !player.media.paused) { + player.core.timers.hover = window.setTimeout(function() { + // If the mouse is over the controls (and not entering fullscreen), bail + if ((player.elements.controls.pressed || player.elements.controls.hover) && !isEnterFullscreen) { + return; + } + + utils.toggleClass(player.elements.container, player.config.classes.hideControls, true); + }, delay); + } + + // Allow chaining + return player; + }; + + // Event listener + Player.prototype.on = function(event, callback) { + var player = this; + + utils.on(player.elements.container, event, callback); + + // Allow chaining + return player; + }; + + // Check for support + Player.prototype.supports = function(mimeType) { + return support.mime(this, mimeType); + }; + + // 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 + Player.prototype.destroy = function(callback, restore) { + var player = this; + // Type specific stuff + switch (player.type) { + case 'youtube': + // Clear timers + window.clearInterval(player.timers.buffering); + window.clearInterval(player.timers.playing); + + // Destroy YouTube API + player.embed.destroy(); + + // Clean up + cleanUp(); + + break; + + case 'vimeo': + // Destroy Vimeo API + // then clean up (wait, to prevent postmessage errors) + player.embed.unload().then(cleanUp); + + // Vimeo does not always return + window.setTimeout(cleanUp, 200); + + break; + + case 'video': + case 'audio': + // Restore native video controls + player.core.toggleNativeControls(true); + + // Clean up + cleanUp(); + + break; + } + + function cleanUp() { + // Default to restore original element + if (!utils.is.boolean(restore)) { + restore = true; + } + + // Callback + if (utils.is.function(callback)) { + callback.call(player.original); + } + + // Bail if we don't need to restore the original element + if (!restore) { + return; + } + + // Replace the container with the original element provided + player.elements.container.parentNode.replaceChild(player.original, player.elements.container); + + // Reset overflow (incase destroyed while fullscreen) + document.body.style.overflow = ''; + + // Event + player.core.trigger(player.original, 'destroyed', true); + } + + // Allow chaining + return player; + }; + + // Get the duration (or custom if set) + Player.prototype.getDuration = function() { + var player = this; + + // It should be a number, but parse it just incase + var duration = parseInt(player.config.duration); + + // True duration + var mediaDuration = 0; + + // Only if duration available + if (player.media.duration !== null && !isNaN(player.media.duration)) { + mediaDuration = player.media.duration; + } + + // If custom duration is funky, use regular duration + return (isNaN(duration) ? mediaDuration : duration); + }; return Player; -})); +}); |