plyr-rails 0.1.4 → 2.0.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +25 -1
- data/lib/plyr/rails.rb +21 -1
- data/lib/plyr/rails/version.rb +1 -1
- data/vendor/assets/javascripts/plyr.js +3775 -2
- data/vendor/assets/sprite/plyr-captions-off.svg +7 -0
- data/vendor/assets/sprite/plyr-captions-on.svg +7 -0
- data/vendor/assets/sprite/plyr-enter-fullscreen.svg +7 -0
- data/vendor/assets/sprite/plyr-exit-fullscreen.svg +7 -0
- data/vendor/assets/sprite/plyr-fast-forward.svg +6 -0
- data/vendor/assets/sprite/plyr-muted.svg +7 -0
- data/vendor/assets/sprite/plyr-pause.svg +7 -0
- data/vendor/assets/sprite/plyr-play.svg +6 -0
- data/vendor/assets/sprite/plyr-restart.svg +6 -0
- data/vendor/assets/sprite/plyr-rewind.svg +6 -0
- data/vendor/assets/sprite/plyr-volume.svg +8 -0
- data/vendor/assets/stylesheets/mixins.scss +29 -0
- data/vendor/assets/stylesheets/plyr.scss +758 -0
- data/vendor/assets/stylesheets/variables.scss +73 -0
- metadata +16 -4
- data/vendor/assets/images/plyr.svg +0 -1
- data/vendor/assets/stylesheets/plyr.css +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a3a6d134588f8f50c366add75e88f20961ec2341
|
4
|
+
data.tar.gz: 5e54eec74f7d3ad368982cf2ec3ef568e0808d08
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5e55ef225230fca754950a197fb72b7c5e6af4cbda5d8c331eee9d47b17b77f54a6ea39a7abf7ac8c00c27c2fb89de160a2be58a2f9f0e02bf068e5096ab562d
|
7
|
+
data.tar.gz: 0df5c6e34f743755c087012e1348948bf08c1286a45da455eb1fcc5f22578116e7acfaff0ff655ec437e8243411e5425bd55203aaf8028eb91630b2f19b754cf
|
data/README.md
CHANGED
@@ -20,9 +20,33 @@ Or install it yourself as:
|
|
20
20
|
|
21
21
|
$ gem install plyr-rails
|
22
22
|
|
23
|
+
Now you need to edit your `app/assets/javascripts/application.js` file and add the following line:
|
24
|
+
``` javascript
|
25
|
+
//= require plyr
|
26
|
+
```
|
27
|
+
|
28
|
+
Now you need to edit your `app/assets/stylesheets/application.css` file and add the following line:
|
29
|
+
``` css
|
30
|
+
*= require plyr
|
31
|
+
```
|
32
|
+
|
23
33
|
## Usage
|
24
34
|
|
25
|
-
|
35
|
+
To load and intialize the player.
|
36
|
+
|
37
|
+
You need to add this to your `app/assets/javascripts/application.js` file and add the following line:
|
38
|
+
``` javascript
|
39
|
+
$(document).ready(function(){
|
40
|
+
plyr.setup();
|
41
|
+
});
|
42
|
+
```
|
43
|
+
|
44
|
+
Add html code to your any view file `app/views/blogs/index.erb` file and add the following code:
|
45
|
+
``` html
|
46
|
+
<div class="plyr">
|
47
|
+
<div data-video-id="bTqVqk7FSmY" data-type="youtube"></div>
|
48
|
+
</div>
|
49
|
+
```
|
26
50
|
|
27
51
|
## Development
|
28
52
|
|
data/lib/plyr/rails.rb
CHANGED
@@ -2,6 +2,26 @@ require "plyr/rails/version"
|
|
2
2
|
|
3
3
|
module Plyr
|
4
4
|
module Rails
|
5
|
-
class Engine < ::Rails::Engine
|
5
|
+
class Engine < ::Rails::Engine
|
6
|
+
initializer :append_dependent_assets_path, :group => :all do |app|
|
7
|
+
app.config.assets.paths += %w( sprite )
|
8
|
+
|
9
|
+
app.config.assets.precompile += %w( plyr.scss )
|
10
|
+
app.config.assets.precompile += %w( plyr.js )
|
11
|
+
|
12
|
+
app.config.assets.precompile += %w( plyr-captions-off.svg )
|
13
|
+
app.config.assets.precompile += %w( plyr-captions-on.svg )
|
14
|
+
app.config.assets.precompile += %w( plyr-enter-fullscreen.svg )
|
15
|
+
app.config.assets.precompile += %w( plyr-exit-fullscreen.svg )
|
16
|
+
app.config.assets.precompile += %w( plyr-fast-forward.svg )
|
17
|
+
app.config.assets.precompile += %w( plyr-muted.svg )
|
18
|
+
app.config.assets.precompile += %w( plyr-pause.svg )
|
19
|
+
app.config.assets.precompile += %w( plyr-play.svg )
|
20
|
+
app.config.assets.precompile += %w( plyr-restart.svg )
|
21
|
+
app.config.assets.precompile += %w( plyr-rewind.svg )
|
22
|
+
app.config.assets.precompile += %w( plyr-volume.svg )
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
6
26
|
end
|
7
27
|
end
|
data/lib/plyr/rails/version.rb
CHANGED
@@ -1,2 +1,3775 @@
|
|
1
|
-
!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=t(e,document):"function"==typeof define&&define.amd?define(null,function(){t(e,document)}):e.plyr=t(e,document)}("undefined"!=typeof window?window:this,function(e,t){"use strict";function n(){var e,n,a,r=navigator.userAgent,s=navigator.appName,o=""+parseFloat(navigator.appVersion),i=parseInt(navigator.appVersion,10);return-1!==navigator.appVersion.indexOf("Windows NT")&&-1!==navigator.appVersion.indexOf("rv:11")?(s="IE",o="11;"):-1!==(n=r.indexOf("MSIE"))?(s="IE",o=r.substring(n+5)):-1!==(n=r.indexOf("Chrome"))?(s="Chrome",o=r.substring(n+7)):-1!==(n=r.indexOf("Safari"))?(s="Safari",o=r.substring(n+7),-1!==(n=r.indexOf("Version"))&&(o=r.substring(n+8))):-1!==(n=r.indexOf("Firefox"))?(s="Firefox",o=r.substring(n+8)):(e=r.lastIndexOf(" ")+1)<(n=r.lastIndexOf("/"))&&(s=r.substring(e,n),o=r.substring(n+1),s.toLowerCase()==s.toUpperCase()&&(s=navigator.appName)),-1!==(a=o.indexOf(";"))&&(o=o.substring(0,a)),-1!==(a=o.indexOf(" "))&&(o=o.substring(0,a)),i=parseInt(""+o,10),isNaN(i)&&(o=""+parseFloat(navigator.appVersion),i=parseInt(navigator.appVersion,10)),{name:s,version:i,ios:/(iPad|iPhone|iPod)/g.test(navigator.platform),touch:"ontouchstart"in t.documentElement}}function a(e,t){var n=e.media;if("video"==e.type)switch(t){case"video/webm":return!(!n.canPlayType||!n.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/no/,""));case"video/mp4":return!(!n.canPlayType||!n.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"').replace(/no/,""));case"video/ogg":return!(!n.canPlayType||!n.canPlayType('video/ogg; codecs="theora"').replace(/no/,""))}else if("audio"==e.type)switch(t){case"audio/mpeg":return!(!n.canPlayType||!n.canPlayType("audio/mpeg;").replace(/no/,""));case"audio/ogg":return!(!n.canPlayType||!n.canPlayType('audio/ogg; codecs="vorbis"').replace(/no/,""));case"audio/wav":return!(!n.canPlayType||!n.canPlayType('audio/wav; codecs="1"').replace(/no/,""))}return!1}function r(e){if(!t.querySelectorAll('script[src="'+e+'"]').length){var n=t.createElement("script");n.src=e;var a=t.getElementsByTagName("script")[0];a.parentNode.insertBefore(n,a)}}function s(e,t){return Array.prototype.indexOf&&-1!=e.indexOf(t)}function o(e,t,n){return e.replace(new RegExp(t.replace(/([.*+?\^=!:${}()|\[\]\/\\])/g,"\\$1"),"g"),n)}function i(e,t){e.length||(e=[e]);for(var n=e.length-1;n>=0;n--){var a=n>0?t.cloneNode(!0):t,r=e[n],s=r.parentNode,o=r.nextSibling;a.appendChild(r),o?s.insertBefore(a,o):s.appendChild(a)}}function l(e){for(var t=e.parentNode;e.firstChild;)t.insertBefore(e.firstChild,e);t.removeChild(e)}function u(e){e&&e.parentNode.removeChild(e)}function c(e,t){e.insertBefore(t,e.firstChild)}function p(e,t){for(var n in t)e.setAttribute(n,"boolean"==typeof t[n]&&t[n]?"":t[n])}function d(e,n,a){var r=t.createElement(e);p(r,a),c(n,r)}function A(e){return e.replace(".","")}function m(e,t,n){if(e)if(e.classList)e.classList[n?"add":"remove"](t);else{var a=(" "+e.className+" ").replace(/\s+/g," ").replace(" "+t+" ","");e.className=a+(n?" "+t:"")}}function f(e,t){return e?e.classList?e.classList.contains(t):new RegExp("(\\s|^)"+t+"(\\s|$)").test(e.className):!1}function y(e,t,n,a){e&&g(e,t,n,!0,a)}function b(e,t,n,a){e&&g(e,t,n,!1,a)}function v(e,t,n,a,r){y(e,t,function(t){n&&n.apply(e,[t]),a.apply(e,[t])},r)}function g(e,t,n,a,r){var s=t.split(" ");if("boolean"!=typeof r&&(r=!1),e instanceof NodeList)for(var o=0;o<e.length;o++)e[o]instanceof Node&&g(e[o],arguments[1],arguments[2],arguments[3]);else for(var i=0;i<s.length;i++)e[a?"addEventListener":"removeEventListener"](s[i],n,r)}function h(e,t,n,a){if(e&&t){"boolean"!=typeof n&&(n=!1);var r=new CustomEvent(t,{bubbles:n,detail:a});e.dispatchEvent(r)}}function k(e,t){return e?(t="boolean"==typeof t?t:!e.getAttribute("aria-pressed"),e.setAttribute("aria-pressed",t),t):void 0}function w(e,t){return 0===e||0===t||isNaN(e)||isNaN(t)?0:(e/t*100).toFixed(2)}function x(){var e=arguments;if(e.length){if(1==e.lenth)return e[0];for(var t=Array.prototype.shift.call(e),n=e.length,a=0;n>a;a++){var r=e[a];for(var s in r)r[s]&&r[s].constructor&&r[s].constructor===Object?(t[s]=t[s]||{},x(t[s],r[s])):t[s]=r[s]}return t}}function T(){var e={supportsFullScreen:!1,isFullScreen:function(){return!1},requestFullScreen:function(){},cancelFullScreen:function(){},fullScreenEventName:"",element:null,prefix:""},n="webkit moz o ms khtml".split(" ");if("undefined"!=typeof t.cancelFullScreen)e.supportsFullScreen=!0;else for(var a=0,r=n.length;r>a;a++){if(e.prefix=n[a],"undefined"!=typeof t[e.prefix+"CancelFullScreen"]){e.supportsFullScreen=!0;break}if("undefined"!=typeof t.msExitFullscreen&&t.msFullscreenEnabled){e.prefix="ms",e.supportsFullScreen=!0;break}}return e.supportsFullScreen&&(e.fullScreenEventName="ms"==e.prefix?"MSFullscreenChange":e.prefix+"fullscreenchange",e.isFullScreen=function(e){switch("undefined"==typeof e&&(e=t.body),this.prefix){case"":return t.fullscreenElement==e;case"moz":return t.mozFullScreenElement==e;default:return t[this.prefix+"FullscreenElement"]==e}},e.requestFullScreen=function(e){return"undefined"==typeof e&&(e=t.body),""===this.prefix?e.requestFullScreen():e[this.prefix+("ms"==this.prefix?"RequestFullscreen":"RequestFullScreen")]()},e.cancelFullScreen=function(){return""===this.prefix?t.cancelFullScreen():t[this.prefix+("ms"==this.prefix?"ExitFullscreen":"CancelFullScreen")]()},e.element=function(){return""===this.prefix?t.fullscreenElement:t[this.prefix+"FullscreenElement"]}),e}function _(){var t={supported:function(){if(!("localStorage"in e))return!1;try{e.localStorage.setItem("___test","OK");var t=e.localStorage.getItem("___test");return e.localStorage.removeItem("___test"),"OK"===t}catch(n){return!1}return!1}()};return t}function E(g,x){function E(t,n){x.debug&&e.console&&console[n?"warn":"log"](t)}function S(){var e=[],t=x.iconUrl+"#"+x.iconPrefix;return s(x.controls,"play-large")&&e.push('<button type="button" data-plyr="play" class="plyr__play-large">','<svg><use xlink:href="'+t+'-play" /></svg>','<span class="plyr__sr-only">'+x.i18n.play+"</span>","</button>"),e.push('<div class="plyr__controls">'),s(x.controls,"restart")&&e.push('<button type="button" data-plyr="restart">','<svg><use xlink:href="'+t+'-restart" /></svg>','<span class="plyr__sr-only">'+x.i18n.restart+"</span>","</button>"),s(x.controls,"rewind")&&e.push('<button type="button" data-plyr="rewind">','<svg><use xlink:href="'+t+'-rewind" /></svg>','<span class="plyr__sr-only">'+x.i18n.rewind+"</span>","</button>"),s(x.controls,"play")&&e.push('<button type="button" data-plyr="play">','<svg><use xlink:href="'+t+'-play" /></svg>','<span class="plyr__sr-only">'+x.i18n.play+"</span>","</button>",'<button type="button" data-plyr="pause">','<svg><use xlink:href="'+t+'-pause" /></svg>','<span class="plyr__sr-only">'+x.i18n.pause+"</span>","</button>"),s(x.controls,"fast-forward")&&e.push('<button type="button" data-plyr="fast-forward">','<svg><use xlink:href="'+t+'-fast-forward" /></svg>','<span class="plyr__sr-only">'+x.i18n.forward+"</span>","</button>"),s(x.controls,"progress")&&(e.push('<span class="plyr__progress">','<label for="seek{id}" class="plyr__sr-only">Seek</label>','<input id="seek{id}" class="plyr__progress--seek" type="range" min="0" max="100" step="0.1" value="0" data-plyr="seek">','<progress class="plyr__progress--played" max="100" value="0" role="presentation"></progress>','<progress class="plyr__progress--buffer" max="100" value="0">',"<span>0</span>% "+x.i18n.buffered,"</progress>"),x.tooltips.seek&&e.push('<span class="plyr__tooltip">00:00</span>'),e.push("</span>")),s(x.controls,"current-time")&&e.push('<span class="plyr__time">','<span class="plyr__sr-only">'+x.i18n.currentTime+"</span>",'<span class="plyr__time--current">00:00</span>',"</span>"),s(x.controls,"duration")&&e.push('<span class="plyr__time">','<span class="plyr__sr-only">'+x.i18n.duration+"</span>",'<span class="plyr__time--duration">00:00</span>',"</span>"),s(x.controls,"mute")&&e.push('<button type="button" data-plyr="mute">','<svg class="icon--muted"><use xlink:href="'+t+'-muted" /></svg>','<svg><use xlink:href="'+t+'-volume" /></svg>','<span class="plyr__sr-only">'+x.i18n.toggleMute+"</span>","</button>"),s(x.controls,"volume")&&e.push('<span class="plyr__volume">','<label for="volume{id}" class="plyr__sr-only">'+x.i18n.volume+"</label>",'<input id="volume{id}" class="plyr__volume--input" type="range" min="'+x.volumeMin+'" max="'+x.volumeMax+'" value="'+x.volume+'" data-plyr="volume">','<progress class="plyr__volume--display" max="'+x.volumeMax+'" value="'+x.volumeMin+'" role="presentation"></progress>',"</span>"),s(x.controls,"captions")&&e.push('<button type="button" data-plyr="captions">','<svg class="icon--captions-on"><use xlink:href="'+t+'-captions-on" /></svg>','<svg><use xlink:href="'+t+'-captions-off" /></svg>','<span class="plyr__sr-only">'+x.i18n.toggleCaptions+"</span>","</button>"),s(x.controls,"fullscreen")&&e.push('<button type="button" data-plyr="fullscreen">','<svg class="icon--exit-fullscreen"><use xlink:href="'+t+'-exit-fullscreen" /></svg>','<svg><use xlink:href="'+t+'-enter-fullscreen" /></svg>','<span class="plyr__sr-only">'+x.i18n.toggleFullscreen+"</span>","</button>"),e.push("</div>"),e.join("")}function I(){if(Ne.supported.full&&("audio"!=Ne.type||x.fullscreen.allowAudio)&&x.fullscreen.enabled){var e=C.supportsFullScreen;e||x.fullscreen.fallback&&!V()?(E((e?"Native":"Fallback")+" fullscreen enabled"),m(Ne.container,x.classes.fullscreen.enabled,!0)):E("Fullscreen not supported and fallback disabled"),k(Ne.buttons.fullscreen,!1),H()}}function N(){if("video"===Ne.type){L(x.selectors.captions)||Ne.videoContainer.insertAdjacentHTML("afterbegin",'<div class="'+A(x.selectors.captions)+'"></div>'),Ne.usingTextTracks=!1,Ne.media.textTracks&&(Ne.usingTextTracks=!0);for(var e,t="",n=Ne.media.childNodes,a=0;a<n.length;a++)"track"===n[a].nodeName.toLowerCase()&&(e=n[a].kind,"captions"!==e&&"subtitles"!==e||(t=n[a].getAttribute("src")));if(Ne.captionExists=!0,""===t?(Ne.captionExists=!1,E("No caption track found")):E("Caption track found; URI: "+t),Ne.captionExists){for(var r=Ne.media.textTracks,s=0;s<r.length;s++)r[s].mode="hidden";if(R(Ne),("IE"===Ne.browser.name&&Ne.browser.version>=10||"Firefox"===Ne.browser.name&&Ne.browser.version>=31)&&(E("Detected browser with known TextTrack issues - using manual fallback"),Ne.usingTextTracks=!1),Ne.usingTextTracks){E("TextTracks supported");for(var o=0;o<r.length;o++){var i=r[o];"captions"!==i.kind&&"subtitles"!==i.kind||y(i,"cuechange",function(){this.activeCues[0]&&"text"in this.activeCues[0]?M(this.activeCues[0].getCueAsHTML()):M()})}}else if(E("TextTracks not supported so rendering captions manually"),Ne.currentCaption="",Ne.captions=[],""!==t){var l=new XMLHttpRequest;l.onreadystatechange=function(){if(4===l.readyState)if(200===l.status){var e,t=[],n=l.responseText;t=n.split("\n\n");for(var a=0;a<t.length;a++){e=t[a],Ne.captions[a]=[];var r=e.split("\n"),s=0;-1===r[s].indexOf(":")&&(s=1),Ne.captions[a]=[r[s],r[s+1]]}Ne.captions.shift(),E("Successfully loaded the caption file via AJAX")}else E("There was a problem loading the caption file via AJAX",!0)},l.open("get",t,!0),l.send()}}else m(Ne.container,x.classes.captions.enabled)}}function M(e){var n=L(x.selectors.captions),a=t.createElement("span");n.innerHTML="","undefined"==typeof e&&(e=""),"string"==typeof e?a.innerHTML=e.trim():a.appendChild(e),n.appendChild(a);n.offsetHeight}function P(e){function t(e,t){var n=[];n=e.split(" --> ");for(var a=0;a<n.length;a++)n[a]=n[a].replace(/(\d+:\d+:\d+\.\d+).*/,"$1");return r(n[t])}function n(e){return t(e,0)}function a(e){return t(e,1)}function r(e){if(null===e||void 0===e)return 0;var t,n=[],a=[];return n=e.split(","),a=n[0].split(":"),t=Math.floor(60*a[0]*60)+Math.floor(60*a[1])+Math.floor(a[2])}if(!Ne.usingTextTracks&&"video"===Ne.type&&Ne.supported.full&&(Ne.subcount=0,e="number"==typeof e?e:Ne.media.currentTime,Ne.captions[Ne.subcount])){for(;a(Ne.captions[Ne.subcount][0])<e.toFixed(1);)if(Ne.subcount++,Ne.subcount>Ne.captions.length-1){Ne.subcount=Ne.captions.length-1;break}Ne.media.currentTime.toFixed(1)>=n(Ne.captions[Ne.subcount][0])&&Ne.media.currentTime.toFixed(1)<=a(Ne.captions[Ne.subcount][0])?(Ne.currentCaption=Ne.captions[Ne.subcount][1],M(Ne.currentCaption)):M()}}function R(){Ne.buttons.captions&&(m(Ne.container,x.classes.captions.enabled,!0),x.captions.defaultActive&&(m(Ne.container,x.classes.captions.active,!0),k(Ne.buttons.captions,!0)))}function B(e){return Ne.container.querySelectorAll(e)}function L(e){return B(e)[0]}function V(){try{return e.self!==e.top}catch(t){return!0}}function H(){function e(e){9===e.which&&Ne.isFullscreen&&(e.target!==a||e.shiftKey?e.target===n&&e.shiftKey&&(e.preventDefault(),a.focus()):(e.preventDefault(),n.focus()))}var t=B("input:not([disabled]), button:not([disabled])"),n=t[0],a=t[t.length-1];y(Ne.container,"keydown",e)}function O(e,t){if("string"==typeof t)d(e,Ne.media,{src:t});else if(t.constructor===Array)for(var n=t.length-1;n>=0;n--)d(e,Ne.media,t[n])}function W(){var e=x.html;E("Injecting custom controls"),e||(e=S()),e=o(e,"{seektime}",x.seekTime),e=o(e,"{id}",Math.floor(1e4*Math.random()));var n;if(null!==x.selectors.controls.container&&(n=x.selectors.controls.container,"string"==typeof selector&&(n=t.querySelector(n))),n instanceof HTMLElement||(n=Ne.container),n.insertAdjacentHTML("beforeend",e),x.tooltips.controls)for(var a=B([x.selectors.controls.wrapper," ",x.selectors.labels," .",x.classes.hidden].join("")),r=a.length-1;r>=0;r--){var s=a[r];m(s,x.classes.hidden,!1),m(s,x.classes.tooltip,!0)}}function G(){try{return Ne.controls=L(x.selectors.controls.wrapper),Ne.buttons={},Ne.buttons.seek=L(x.selectors.buttons.seek),Ne.buttons.play=B(x.selectors.buttons.play),Ne.buttons.pause=L(x.selectors.buttons.pause),Ne.buttons.restart=L(x.selectors.buttons.restart),Ne.buttons.rewind=L(x.selectors.buttons.rewind),Ne.buttons.forward=L(x.selectors.buttons.forward),Ne.buttons.fullscreen=L(x.selectors.buttons.fullscreen),Ne.buttons.mute=L(x.selectors.buttons.mute),Ne.buttons.captions=L(x.selectors.buttons.captions),Ne.progress={},Ne.progress.container=L(x.selectors.progress.container),Ne.progress.buffer={},Ne.progress.buffer.bar=L(x.selectors.progress.buffer),Ne.progress.buffer.text=Ne.progress.buffer.bar&&Ne.progress.buffer.bar.getElementsByTagName("span")[0],Ne.progress.played=L(x.selectors.progress.played),Ne.progress.tooltip=Ne.progress.container&&Ne.progress.container.querySelector("."+x.classes.tooltip),Ne.volume={},Ne.volume.input=L(x.selectors.volume.input),Ne.volume.display=L(x.selectors.volume.display),Ne.duration=L(x.selectors.duration),Ne.currentTime=L(x.selectors.currentTime),Ne.seekTime=B(x.selectors.seekTime),!0}catch(e){return E("It looks like there is a problem with your controls html",!0),q(!0),!1}}function Y(){m(Ne.container,x.selectors.container.replace(".",""),Ne.supported.full)}function q(e){e?Ne.media.setAttribute("controls",""):Ne.media.removeAttribute("controls")}function z(e){var t=x.i18n.play;if("undefined"!=typeof x.title&&x.title.length&&(t+=", "+x.title),Ne.supported.full&&Ne.buttons.play)for(var n=Ne.buttons.play.length-1;n>=0;n--)Ne.buttons.play[n].setAttribute("aria-label",t);e instanceof HTMLElement&&e.setAttribute("title",x.i18n.frameTitle.replace("{title}",x.title))}function Q(){if(!Ne.media)return E("No audio or video element found",!0),!1;if(Ne.supported.full&&(m(Ne.container,x.classes.type.replace("{0}",Ne.type),!0),s(x.types.embed,Ne.type)&&m(Ne.container,x.classes.type.replace("{0}","video"),!0),m(Ne.container,x.classes.stopped,x.autoplay),m(Ne.container,x.classes.isIos,Ne.browser.ios),m(Ne.container,x.classes.isTouch,Ne.browser.touch),"video"===Ne.type)){var e=t.createElement("div");e.setAttribute("class",x.classes.videoWrapper),i(Ne.media,e),Ne.videoContainer=e}s(x.types.embed,Ne.type)&&(j(),Ne.embedId=null)}function j(){for(var n=t.createElement("div"),a=Ne.embedId,s=Ne.type+"-"+Math.floor(1e4*Math.random()),o=B('[id^="'+Ne.type+'-"]'),i=o.length-1;i>=0;i--)u(o[i]);if(m(Ne.media,x.classes.videoWrapper,!0),m(Ne.media,x.classes.embedWrapper,!0),"youtube"===Ne.type)Ne.media.appendChild(n),n.setAttribute("id",s),"object"==typeof YT?D(a,n):(r(x.urls.youtube.api),e.onYouTubeReadyCallbacks=e.onYouTubeReadyCallbacks||[],e.onYouTubeReadyCallbacks.push(function(){D(a,n)}),e.onYouTubeIframeAPIReady=function(){e.onYouTubeReadyCallbacks.forEach(function(e){e()})});else if("vimeo"===Ne.type){var l=t.createElement("iframe");l.loaded=!1,y(l,"load",function(){l.loaded=!0}),p(l,{src:"https://player.vimeo.com/video/"+a+"?player_id="+s+"&api=1&badge=0&byline=0&portrait=0&title=0",id:s,webkitallowfullscreen:"",mozallowfullscreen:"",allowfullscreen:"",frameborder:0}),Ne.supported.full?(n.appendChild(l),Ne.media.appendChild(n)):Ne.media.appendChild(l),"$f"in e||r(x.urls.vimeo.api);var c=e.setInterval(function(){"$f"in e&&l.loaded&&(e.clearInterval(c),U.call(l))},50)}else if("soundcloud"===Ne.type){var d=t.createElement("iframe");d.loaded=!1,y(d,"load",function(){d.loaded=!0}),p(d,{src:"https://w.soundcloud.com/player/?url=https://api.soundcloud.com/tracks/"+a,id:s}),n.appendChild(d),Ne.media.appendChild(n),e.SC||r(x.urls.soundcloud.api);var A=e.setInterval(function(){e.SC&&d.loaded&&(e.clearInterval(A),Z.call(d))},50)}}function X(){Ne.container.plyr.embed=Ne.embed,Ie(),z(L("iframe"))}function D(t,n){"timer"in Ne||(Ne.timer={}),Ne.embed=new YT.Player(n.id,{videoId:t,playerVars:{autoplay:x.autoplay?1:0,controls:Ne.supported.full?0:1,rel:0,showinfo:0,iv_load_policy:3,cc_load_policy:x.captions.defaultActive?1:0,cc_lang_pref:"en",wmode:"transparent",modestbranding:1,disablekb:1,origin:"*"},events:{onError:function(e){h(Ne.container,"error",!0,{code:e.data,embed:e.target})},onReady:function(t){var n=t.target;Ne.media.play=function(){n.playVideo(),Ne.media.paused=!1},Ne.media.pause=function(){n.pauseVideo(),Ne.media.paused=!0},Ne.media.stop=function(){n.stopVideo(),Ne.media.paused=!0},Ne.media.duration=n.getDuration(),Ne.media.paused=!0,Ne.media.currentTime=n.getCurrentTime(),Ne.media.muted=n.isMuted(),x.title=n.getVideoData().title,h(Ne.media,"timeupdate"),e.clearInterval(Ne.timer.buffering),Ne.timer.buffering=e.setInterval(function(){Ne.media.buffered=n.getVideoLoadedFraction(),h(Ne.media,"progress"),1===Ne.media.buffered&&(e.clearInterval(Ne.timer.buffering),h(Ne.media,"canplaythrough"))},200),X(),be()},onStateChange:function(t){var n=t.target;switch(e.clearInterval(Ne.timer.playing),t.data){case 0:Ne.media.paused=!0,h(Ne.media,"ended");break;case 1:Ne.media.paused=!1,Ne.media.seeking=!1,h(Ne.media,"play"),h(Ne.media,"playing"),Ne.timer.playing=e.setInterval(function(){Ne.media.currentTime=n.getCurrentTime(),h(Ne.media,"timeupdate")},100);break;case 2:Ne.media.paused=!0,h(Ne.media,"pause")}h(Ne.container,"statechange",!1,{code:t.data})}}})}function U(){Ne.embed=$f(this),Ne.embed.addEvent("ready",function(){Ne.media.play=function(){Ne.embed.api("play"),Ne.media.paused=!1},Ne.media.pause=function(){Ne.embed.api("pause"),Ne.media.paused=!0},Ne.media.stop=function(){Ne.embed.api("stop"),Ne.media.paused=!0},Ne.media.paused=!0,Ne.media.currentTime=0,X(),Ne.embed.api("getCurrentTime",function(e){Ne.media.currentTime=e,h(Ne.media,"timeupdate")}),Ne.embed.api("getDuration",function(e){Ne.media.duration=e,be()}),Ne.embed.addEvent("play",function(){Ne.media.paused=!1,h(Ne.media,"play"),h(Ne.media,"playing")}),Ne.embed.addEvent("pause",function(){Ne.media.paused=!0,h(Ne.media,"pause")}),Ne.embed.addEvent("playProgress",function(e){Ne.media.seeking=!1,Ne.media.currentTime=e.seconds,h(Ne.media,"timeupdate")}),Ne.embed.addEvent("loadProgress",function(e){Ne.media.buffered=e.percent,h(Ne.media,"progress"),1===parseInt(e.percent)&&h(Ne.media,"canplaythrough")}),Ne.embed.addEvent("finish",function(){Ne.media.paused=!0,h(Ne.media,"ended")}),x.autoplay&&Ne.embed.api("play")})}function Z(){Ne.embed=e.SC.Widget(this),Ne.embed.bind(e.SC.Widget.Events.READY,function(){Ne.media.play=function(){Ne.embed.play(),Ne.media.paused=!1},Ne.media.pause=function(){Ne.embed.pause(),Ne.media.paused=!0},Ne.media.stop=function(){Ne.embed.seekTo(0),Ne.embed.pause(),Ne.media.paused=!0},Ne.media.paused=!0,Ne.media.currentTime=0,X(),Ne.embed.getPosition(function(e){Ne.media.currentTime=e,h(Ne.media,"timeupdate")}),Ne.embed.getDuration(function(e){Ne.media.duration=e/1e3,be()}),Ne.embed.bind(e.SC.Widget.Events.PLAY,function(){Ne.media.paused=!1,h(Ne.media,"play"),h(Ne.media,"playing")}),Ne.embed.bind(e.SC.Widget.Events.PAUSE,function(){Ne.media.paused=!0,h(Ne.media,"pause")}),Ne.embed.bind(e.SC.Widget.Events.PLAY_PROGRESS,function(e){Ne.media.seeking=!1,Ne.media.currentTime=e.currentPosition/1e3,h(Ne.media,"timeupdate")}),Ne.embed.bind(e.SC.Widget.Events.LOAD_PROGRESS,function(e){Ne.media.buffered=e.loadProgress,h(Ne.media,"progress"),1===parseInt(e.loadProgress)&&h(Ne.media,"canplaythrough")}),Ne.embed.bind(e.SC.Widget.Events.FINISH,function(){Ne.media.paused=!0,h(Ne.media,"ended")}),x.autoplay&&Ne.embed.play()})}function $(){"play"in Ne.media&&Ne.media.play()}function J(){"pause"in Ne.media&&Ne.media.pause()}function K(e){e===!0?$():e===!1?J():Ne.media[Ne.media.paused?"play":"pause"]()}function ee(e){"number"!=typeof e&&(e=x.seekTime),ne(Ne.media.currentTime-e)}function te(e){"number"!=typeof e&&(e=x.seekTime),ne(Ne.media.currentTime+e)}function ne(e){var t=0,n=Ne.media.paused,a=ae();"number"==typeof e?t=e:"object"!=typeof e||"input"!==e.type&&"change"!==e.type||(t=e.target.value/e.target.max*a),0>t?t=0:t>a&&(t=a),ge(t);try{Ne.media.currentTime=t.toFixed(1)}catch(r){}if(s(x.types.embed,Ne.type)){switch(Ne.type){case"youtube":Ne.embed.seekTo(t);break;case"vimeo":Ne.embed.api("seekTo",t.toFixed(0));break;case"soundcloud":Ne.embed.seekTo(1e3*t)}n&&J(),h(Ne.media,"timeupdate"),Ne.media.seeking=!0}E("Seeking to "+Ne.media.currentTime+" seconds"),P(t)}function ae(){var e=parseInt(x.duration),t=0;return null===Ne.media.duration||isNaN(Ne.media.duration)||(t=Ne.media.duration),isNaN(e)?t:e}function re(){m(Ne.container,x.classes.playing,!Ne.media.paused),m(Ne.container,x.classes.stopped,Ne.media.paused),ke(Ne.media.paused)}function se(e){var n=C.supportsFullScreen;e&&e.type===C.fullScreenEventName?Ne.isFullscreen=C.isFullScreen(Ne.container):n?(C.isFullScreen(Ne.container)?C.cancelFullScreen():C.requestFullScreen(Ne.container),Ne.isFullscreen=C.isFullScreen(Ne.container)):(Ne.isFullscreen=!Ne.isFullscreen,Ne.isFullscreen?(y(t,"keyup",oe),t.body.style.overflow="hidden"):(b(t,"keyup",oe),t.body.style.overflow="")),m(Ne.container,x.classes.fullscreen.active,Ne.isFullscreen),Ne.isFullscreen?Ne.container.setAttribute("tabindex","-1"):Ne.container.removeAttribute("tabindex"),H(Ne.isFullscreen),k(Ne.buttons.fullscreen,Ne.isFullscreen),h(Ne.container,Ne.isFullscreen?"enterfullscreen":"exitfullscreen")}function oe(e){27===(e.which||e.charCode||e.keyCode)&&Ne.isFullscreen&&se()}function ie(e){if("boolean"!=typeof e&&(e=!Ne.media.muted),k(Ne.buttons.mute,e),Ne.media.muted=e,0===Ne.media.volume&&le(x.volume),s(x.types.embed,Ne.type)){switch(Ne.type){case"youtube":Ne.embed[Ne.media.muted?"mute":"unMute"]();break;case"vimeo":Ne.embed.api("setVolume",Ne.media.muted?0:parseFloat(x.volume/x.volumeMax));break;case"soundcloud":Ne.embed.setVolume(Ne.media.muted?0:parseFloat(x.volume/x.volumeMax))}h(Ne.media,"volumechange")}}function le(t){var n=x.volumeMax,a=x.volumeMin;if("undefined"==typeof t&&(t=x.volume,x.storage.enabled&&_().supported&&(t=e.localStorage.getItem(x.storage.key),e.localStorage.removeItem("plyr-volume"))),(null===t||isNaN(t))&&(t=x.volume),t>n&&(t=n),a>t&&(t=a),Ne.media.volume=parseFloat(t/n),Ne.volume.display&&(Ne.volume.display.value=t),s(x.types.embed,Ne.type)){switch(Ne.type){case"youtube":Ne.embed.setVolume(100*Ne.media.volume);break;case"vimeo":Ne.embed.api("setVolume",Ne.media.volume);break;case"soundcloud":Ne.embed.setVolume(Ne.media.volume)}h(Ne.media,"volumechange")}Ne.media.muted&&t>0&&ie()}function ue(){var e=Ne.media.muted?0:Ne.media.volume*x.volumeMax;le(e+x.volumeStep/5)}function ce(){var e=Ne.media.muted?0:Ne.media.volume*x.volumeMax;le(e-x.volumeStep/5)}function pe(){var t=Ne.media.muted?0:Ne.media.volume*x.volumeMax;Ne.supported.full&&(Ne.volume.input&&(Ne.volume.input.value=t),Ne.volume.display&&(Ne.volume.display.value=t)),x.storage.enabled&&_().supported&&!isNaN(t)&&e.localStorage.setItem(x.storage.key,t),m(Ne.container,x.classes.muted,0===t),Ne.supported.full&&Ne.buttons.mute&&k(Ne.buttons.mute,0===t)}function de(e){Ne.supported.full&&Ne.buttons.captions&&("boolean"!=typeof e&&(e=-1===Ne.container.className.indexOf(x.classes.captions.active)),Ne.captionsEnabled=e,k(Ne.buttons.captions,Ne.captionsEnabled),m(Ne.container,x.classes.captions.active,Ne.captionsEnabled),h(Ne.container,Ne.captionsEnabled?"captionsenabled":"captionsdisabled"))}function Ae(e){var t="waiting"===e.type;clearTimeout(Ne.timers.loading),Ne.timers.loading=setTimeout(function(){m(Ne.container,x.classes.loading,t)},t?250:0)}function me(e){if(Ne.supported.full){var t=Ne.progress.played,n=0,a=ae();if(e)switch(e.type){case"timeupdate":case"seeking":n=w(Ne.media.currentTime,a),"timeupdate"==e.type&&Ne.buttons.seek&&(Ne.buttons.seek.value=n);break;case"playing":case"progress":t=Ne.progress.buffer,n=function(){var e=Ne.media.buffered;return e&&e.length?w(e.end(0),a):"number"==typeof e?100*e:0}()}fe(t,n)}}function fe(e,t){if(Ne.supported.full){if("undefined"==typeof t&&(t=0),"undefined"==typeof e){if(!Ne.progress||!Ne.progress.buffer)return;e=Ne.progress.buffer}e instanceof HTMLElement?e.value=t:e&&(e.bar&&(e.bar.value=t),e.text&&(e.text.innerHTML=t))}}function ye(e,t){if(t){isNaN(e)&&(e=0),Ne.secs=parseInt(e%60),Ne.mins=parseInt(e/60%60),Ne.hours=parseInt(e/60/60%60);var n=parseInt(ae()/60/60%60)>0;Ne.secs=("0"+Ne.secs).slice(-2),Ne.mins=("0"+Ne.mins).slice(-2),t.innerHTML=(n?Ne.hours+":":"")+Ne.mins+":"+Ne.secs}}function be(){if(Ne.supported.full){var e=ae()||0;!Ne.duration&&x.displayDuration&&Ne.media.paused&&ye(e,Ne.currentTime),Ne.duration&&ye(e,Ne.duration),he()}}function ve(e){ye(Ne.media.currentTime,Ne.currentTime),e&&"timeupdate"==e.type&&Ne.media.seeking||me(e)}function ge(e){"number"!=typeof e&&(e=0);var t=ae(),n=w(e,t);Ne.progress&&Ne.progress.played&&(Ne.progress.played.value=n),Ne.buttons&&Ne.buttons.seek&&(Ne.buttons.seek.value=n)}function he(e){var t=ae();if(x.tooltips.seek&&Ne.progress.container&&0!==t){var n=Ne.progress.container.getBoundingClientRect(),a=0,r=x.classes.tooltip+"--visible";if(e)a=100/n.width*(e.pageX-n.left);else{if(!f(Ne.progress.tooltip,r))return;a=Ne.progress.tooltip.style.left.replace("%","")}0>a?a=0:a>100&&(a=100),ye(t/100*a,Ne.progress.tooltip),Ne.progress.tooltip.style.left=a+"%",e&&s(["mouseenter","mouseleave"],e.type)&&m(Ne.progress.tooltip,r,"mouseenter"===e.type)}}function ke(t){if(x.hideControls&&"audio"!==Ne.type){var n=0,a=!1,r=t;"boolean"!=typeof t&&(t&&t.type?(a="enterfullscreen"===t.type,r=s(["mousemove","mouseenter","focus"],t.type),"mousemove"===t.type&&(n=2e3),"focus"===t.type&&(n=3e3)):r=!1),e.clearTimeout(Ne.timers.hover),(r||Ne.media.paused)&&(m(Ne.container,x.classes.hideControls,!1),Ne.media.paused)||r&&Ne.media.paused||(Ne.timers.hover=e.setTimeout(function(){Ne.controls.active&&!a||m(Ne.container,x.classes.hideControls,!0)},n))}}function we(e){if("undefined"!=typeof e)return void xe(e);var t;switch(Ne.type){case"youtube":t=Ne.embed.getVideoUrl();break;case"vimeo":Ne.embed.api("getVideoUrl",function(e){t=e});break;case"soundcloud":Ne.embed.getCurrentSound(function(e){t=e.permalink_url});break;default:t=Ne.media.currentSrc}return t||""}function xe(n){if(!("undefined"!=typeof n&&"sources"in n&&n.sources.length))return void E("Invalid source format",!0);if(J(),ge(),fe(),Ce(),"youtube"===Ne.type?(Ne.embed.destroy(),e.clearInterval(Ne.timer.buffering),e.clearInterval(Ne.timer.playing)):"video"===Ne.type&&Ne.videoContainer&&u(Ne.videoContainer),Ne.embed=null,u(Ne.media),"type"in n&&(Ne.type=n.type,"video"===Ne.type)){var a=n.sources[0];"type"in a&&s(x.types.embed,a.type)&&(Ne.type=a.type)}switch(Ne.supported=F.supported(Ne.type),Ne.type){case"video":Ne.media=t.createElement("video");break;case"audio":Ne.media=t.createElement("audio");break;case"youtube":case"vimeo":case"soundcloud":Ne.media=t.createElement("div"),Ne.embedId=n.sources[0].src}c(Ne.container,Ne.media),"undefined"!=typeof n.autoplay&&(x.autoplay=n.autoplay),s(x.types.html5,Ne.type)&&(x.crossorigin&&Ne.media.setAttribute("crossorigin",""),x.autoplay&&Ne.media.setAttribute("autoplay",""),"poster"in n&&Ne.media.setAttribute("poster",n.poster),x.loop&&Ne.media.setAttribute("loop","")),Ne.container.className=Ne.originalClassName,m(Ne.container,x.classes.fullscreen.active,Ne.isFullscreen),m(Ne.container,x.classes.captions.active,Ne.captionsEnabled),Y(),s(x.types.html5,Ne.type)&&O("source",n.sources),Q(),s(x.types.html5,Ne.type)&&("tracks"in n&&O("track",n.tracks),Ne.media.load(),Ie(),be()),x.title=n.title,z(),Ne.container.plyr.media=Ne.media}function Te(e){"video"===Ne.type&&Ne.media.setAttribute("poster",e)}function _e(){function n(){var e=Ne.media.paused;e?$():J();var t=Ne.buttons[e?"play":"pause"],n=Ne.buttons[e?"pause":"play"];if(n=n&&n.length>1?n[n.length-1]:n[0]){var a=f(t,x.classes.tabFocus);setTimeout(function(){n.focus(),a&&(m(t,x.classes.tabFocus,!1),m(n,x.classes.tabFocus,!0))},100)}}function a(){var e=t.activeElement;e&&e!=t.body?t.querySelector&&(e=t.querySelector(":focus")):e=null;for(var n in Ne.buttons){var a=Ne.buttons[n];if(a instanceof NodeList)for(var r=0;r<a.length;r++)m(a[r],x.classes.tabFocus,a[r]===e);else m(a,x.classes.tabFocus,a===e)}}var r="IE"==Ne.browser.name?"change":"input";y(e,"keyup",function(e){var t=e.keyCode?e.keyCode:e.which;9==t&&a()}),y(t.body,"click",function(){m(L("."+x.classes.tabFocus),x.classes.tabFocus,!1)});for(var s in Ne.buttons){var o=Ne.buttons[s];y(o,"blur",function(){m(o,"tab-focus",!1)})}v(Ne.buttons.play,"click",x.listeners.play,n),v(Ne.buttons.pause,"click",x.listeners.pause,n),v(Ne.buttons.restart,"click",x.listeners.restart,ne),v(Ne.buttons.rewind,"click",x.listeners.rewind,ee),v(Ne.buttons.forward,"click",x.listeners.forward,te),v(Ne.buttons.seek,r,x.listeners.seek,ne),v(Ne.volume.input,r,x.listeners.volume,function(){le(Ne.volume.input.value)}),v(Ne.buttons.mute,"click",x.listeners.mute,ie),v(Ne.buttons.fullscreen,"click",x.listeners.fullscreen,se),C.supportsFullScreen&&y(t,C.fullScreenEventName,se),y(Ne.buttons.captions,"click",de),y(Ne.progress.container,"mouseenter mouseleave mousemove",he),x.hideControls&&(y(Ne.container,"mouseenter mouseleave mousemove enterfullscreen",ke),y(Ne.controls,"mouseenter mouseleave",function(e){Ne.controls.active="mouseenter"===e.type}),y(Ne.controls,"focus blur",ke,!0)),y(Ne.volume.input,"wheel",function(e){e.preventDefault(),(e.deltaY<0||e.deltaX>0)&&ce(),(e.deltaY>0||e.deltaX<0)&&ue()})}function Ee(){if(y(Ne.media,"timeupdate seeking",ve),y(Ne.media,"timeupdate",P),y(Ne.media,"durationchange loadedmetadata",be),y(Ne.media,"ended",function(){"video"===Ne.type&&M(),re(),ne(0),be(),"video"===Ne.type&&x.showPosterOnEnd&&Ne.media.load()}),y(Ne.media,"progress playing",me),y(Ne.media,"volumechange",pe),y(Ne.media,"play pause",re),y(Ne.media,"waiting canplay seeked",Ae),x.clickToPlay&&"audio"!==Ne.type){var e=L("."+x.classes.videoWrapper);if(!e)return;e.style.cursor="pointer",y(e,"click",function(){Ne.media.paused?$():Ne.media.ended?(ne(),$()):J()})}y(Ne.media,x.events.join(" "),function(e){h(Ne.container,e.type,!0)})}function Ce(){if(s(x.types.html5,Ne.type)){for(var e=Ne.media.querySelectorAll("source"),t=0;t<e.length;t++)u(e[t]);Ne.media.setAttribute("src","data:video/mp4;base64,AAAAHGZ0eXBpc29tAAACAGlzb21pc28ybXA0MQAAAAhmcmVlAAAAGm1kYXQAAAGzABAHAAABthBgUYI9t+8AAAMNbW9vdgAAAGxtdmhkAAAAAMXMvvrFzL76AAAD6AAAACoAAQAAAQAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAABhpb2RzAAAAABCAgIAHAE/////+/wAAAiF0cmFrAAAAXHRraGQAAAAPxcy++sXMvvoAAAABAAAAAAAAACoAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAAgAAAAIAAAAAAG9bWRpYQAAACBtZGhkAAAAAMXMvvrFzL76AAAAGAAAAAEVxwAAAAAALWhkbHIAAAAAAAAAAHZpZGUAAAAAAAAAAAAAAABWaWRlb0hhbmRsZXIAAAABaG1pbmYAAAAUdm1oZAAAAAEAAAAAAAAAAAAAACRkaW5mAAAAHGRyZWYAAAAAAAAAAQAAAAx1cmwgAAAAAQAAAShzdGJsAAAAxHN0c2QAAAAAAAAAAQAAALRtcDR2AAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAgACABIAAAASAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGP//AAAAXmVzZHMAAAAAA4CAgE0AAQAEgICAPyARAAAAAAMNQAAAAAAFgICALQAAAbABAAABtYkTAAABAAAAASAAxI2IAMUARAEUQwAAAbJMYXZjNTMuMzUuMAaAgIABAgAAABhzdHRzAAAAAAAAAAEAAAABAAAAAQAAABxzdHNjAAAAAAAAAAEAAAABAAAAAQAAAAEAAAAUc3RzegAAAAAAAAASAAAAAQAAABRzdGNvAAAAAAAAAAEAAAAsAAAAYHVkdGEAAABYbWV0YQAAAAAAAAAhaGRscgAAAAAAAAAAbWRpcmFwcGwAAAAAAAAAAAAAAAAraWxzdAAAACOpdG9vAAAAG2RhdGEAAAABAAAAAExhdmY1My4yMS4x"),
|
2
|
-
|
1
|
+
// ==========================================================================
|
2
|
+
// Plyr
|
3
|
+
// plyr.js v2.0.11
|
4
|
+
// https://github.com/selz/plyr
|
5
|
+
// License: The MIT License (MIT)
|
6
|
+
// ==========================================================================
|
7
|
+
// Credits: http://paypal.github.io/accessible-html5-video-player/
|
8
|
+
// ==========================================================================
|
9
|
+
|
10
|
+
;(function(root, factory) {
|
11
|
+
'use strict';
|
12
|
+
/*global define,module*/
|
13
|
+
|
14
|
+
if (typeof module === 'object' && typeof module.exports === 'object') {
|
15
|
+
// Node, CommonJS-like
|
16
|
+
module.exports = factory(root, document);
|
17
|
+
} else if (typeof define === 'function' && define.amd) {
|
18
|
+
// AMD
|
19
|
+
define([], function () { return factory(root, document); });
|
20
|
+
} else {
|
21
|
+
// Browser globals (root is window)
|
22
|
+
root.plyr = factory(root, document);
|
23
|
+
}
|
24
|
+
}(typeof window !== 'undefined' ? window : this, function(window, document) {
|
25
|
+
'use strict';
|
26
|
+
|
27
|
+
// Globals
|
28
|
+
var fullscreen,
|
29
|
+
scroll = { x: 0, y: 0 },
|
30
|
+
|
31
|
+
// Default config
|
32
|
+
defaults = {
|
33
|
+
enabled: true,
|
34
|
+
debug: false,
|
35
|
+
autoplay: false,
|
36
|
+
loop: false,
|
37
|
+
seekTime: 10,
|
38
|
+
volume: 10,
|
39
|
+
volumeMin: 0,
|
40
|
+
volumeMax: 10,
|
41
|
+
volumeStep: 1,
|
42
|
+
duration: null,
|
43
|
+
displayDuration: true,
|
44
|
+
loadSprite: true,
|
45
|
+
iconPrefix: 'plyr',
|
46
|
+
iconUrl: 'https://cdn.plyr.io/2.0.11/plyr.svg',
|
47
|
+
clickToPlay: true,
|
48
|
+
hideControls: true,
|
49
|
+
showPosterOnEnd: false,
|
50
|
+
disableContextMenu: true,
|
51
|
+
keyboardShorcuts: {
|
52
|
+
focused: true,
|
53
|
+
global: false
|
54
|
+
},
|
55
|
+
tooltips: {
|
56
|
+
controls: false,
|
57
|
+
seek: true
|
58
|
+
},
|
59
|
+
selectors: {
|
60
|
+
html5: 'video, audio',
|
61
|
+
embed: '[data-type]',
|
62
|
+
editable: 'input, textarea, select, [contenteditable]',
|
63
|
+
container: '.plyr',
|
64
|
+
controls: {
|
65
|
+
container: null,
|
66
|
+
wrapper: '.plyr__controls'
|
67
|
+
},
|
68
|
+
labels: '[data-plyr]',
|
69
|
+
buttons: {
|
70
|
+
seek: '[data-plyr="seek"]',
|
71
|
+
play: '[data-plyr="play"]',
|
72
|
+
pause: '[data-plyr="pause"]',
|
73
|
+
restart: '[data-plyr="restart"]',
|
74
|
+
rewind: '[data-plyr="rewind"]',
|
75
|
+
forward: '[data-plyr="fast-forward"]',
|
76
|
+
mute: '[data-plyr="mute"]',
|
77
|
+
captions: '[data-plyr="captions"]',
|
78
|
+
fullscreen: '[data-plyr="fullscreen"]'
|
79
|
+
},
|
80
|
+
volume: {
|
81
|
+
input: '[data-plyr="volume"]',
|
82
|
+
display: '.plyr__volume--display'
|
83
|
+
},
|
84
|
+
progress: {
|
85
|
+
container: '.plyr__progress',
|
86
|
+
buffer: '.plyr__progress--buffer',
|
87
|
+
played: '.plyr__progress--played'
|
88
|
+
},
|
89
|
+
captions: '.plyr__captions',
|
90
|
+
currentTime: '.plyr__time--current',
|
91
|
+
duration: '.plyr__time--duration'
|
92
|
+
},
|
93
|
+
classes: {
|
94
|
+
setup: 'plyr--setup',
|
95
|
+
ready: 'plyr--ready',
|
96
|
+
videoWrapper: 'plyr__video-wrapper',
|
97
|
+
embedWrapper: 'plyr__video-embed',
|
98
|
+
type: 'plyr--{0}',
|
99
|
+
stopped: 'plyr--stopped',
|
100
|
+
playing: 'plyr--playing',
|
101
|
+
muted: 'plyr--muted',
|
102
|
+
loading: 'plyr--loading',
|
103
|
+
hover: 'plyr--hover',
|
104
|
+
tooltip: 'plyr__tooltip',
|
105
|
+
hidden: 'plyr__sr-only',
|
106
|
+
hideControls: 'plyr--hide-controls',
|
107
|
+
isIos: 'plyr--is-ios',
|
108
|
+
isTouch: 'plyr--is-touch',
|
109
|
+
captions: {
|
110
|
+
enabled: 'plyr--captions-enabled',
|
111
|
+
active: 'plyr--captions-active'
|
112
|
+
},
|
113
|
+
fullscreen: {
|
114
|
+
enabled: 'plyr--fullscreen-enabled',
|
115
|
+
active: 'plyr--fullscreen-active'
|
116
|
+
},
|
117
|
+
tabFocus: 'tab-focus'
|
118
|
+
},
|
119
|
+
captions: {
|
120
|
+
defaultActive: false
|
121
|
+
},
|
122
|
+
fullscreen: {
|
123
|
+
enabled: true,
|
124
|
+
fallback: true,
|
125
|
+
allowAudio: false
|
126
|
+
},
|
127
|
+
storage: {
|
128
|
+
enabled: true,
|
129
|
+
key: 'plyr'
|
130
|
+
},
|
131
|
+
controls: ['play-large', 'play', 'progress', 'current-time', 'mute', 'volume', 'captions', 'fullscreen'],
|
132
|
+
i18n: {
|
133
|
+
restart: 'Restart',
|
134
|
+
rewind: 'Rewind {seektime} secs',
|
135
|
+
play: 'Play',
|
136
|
+
pause: 'Pause',
|
137
|
+
forward: 'Forward {seektime} secs',
|
138
|
+
played: 'played',
|
139
|
+
buffered: 'buffered',
|
140
|
+
currentTime: 'Current time',
|
141
|
+
duration: 'Duration',
|
142
|
+
volume: 'Volume',
|
143
|
+
toggleMute: 'Toggle Mute',
|
144
|
+
toggleCaptions: 'Toggle Captions',
|
145
|
+
toggleFullscreen: 'Toggle Fullscreen',
|
146
|
+
frameTitle: 'Player for {title}'
|
147
|
+
},
|
148
|
+
types: {
|
149
|
+
embed: ['youtube', 'vimeo', 'soundcloud'],
|
150
|
+
html5: ['video', 'audio']
|
151
|
+
},
|
152
|
+
// URLs
|
153
|
+
urls: {
|
154
|
+
vimeo: {
|
155
|
+
api: 'https://player.vimeo.com/api/player.js',
|
156
|
+
},
|
157
|
+
youtube: {
|
158
|
+
api: 'https://www.youtube.com/iframe_api'
|
159
|
+
},
|
160
|
+
soundcloud: {
|
161
|
+
api: 'https://w.soundcloud.com/player/api.js'
|
162
|
+
}
|
163
|
+
},
|
164
|
+
// Custom control listeners
|
165
|
+
listeners: {
|
166
|
+
seek: null,
|
167
|
+
play: null,
|
168
|
+
pause: null,
|
169
|
+
restart: null,
|
170
|
+
rewind: null,
|
171
|
+
forward: null,
|
172
|
+
mute: null,
|
173
|
+
volume: null,
|
174
|
+
captions: null,
|
175
|
+
fullscreen: null
|
176
|
+
},
|
177
|
+
// Events to watch on HTML5 media elements
|
178
|
+
events: ['ready', 'ended', 'progress', 'stalled', 'playing', 'waiting', 'canplay', 'canplaythrough', 'loadstart', 'loadeddata', 'loadedmetadata', 'timeupdate', 'volumechange', 'play', 'pause', 'error', 'seeking', 'seeked', 'emptied'],
|
179
|
+
// Logging
|
180
|
+
logPrefix: '[Plyr]'
|
181
|
+
};
|
182
|
+
|
183
|
+
// Credits: http://paypal.github.io/accessible-html5-video-player/
|
184
|
+
// Unfortunately, due to mixed support, UA sniffing is required
|
185
|
+
function _browserSniff() {
|
186
|
+
var ua = navigator.userAgent,
|
187
|
+
name = navigator.appName,
|
188
|
+
fullVersion = '' + parseFloat(navigator.appVersion),
|
189
|
+
majorVersion = parseInt(navigator.appVersion, 10),
|
190
|
+
nameOffset,
|
191
|
+
verOffset,
|
192
|
+
ix,
|
193
|
+
isIE = false,
|
194
|
+
isFirefox = false,
|
195
|
+
isChrome = false,
|
196
|
+
isSafari = false;
|
197
|
+
|
198
|
+
if ((navigator.appVersion.indexOf('Windows NT') !== -1) && (navigator.appVersion.indexOf('rv:11') !== -1)) {
|
199
|
+
// MSIE 11
|
200
|
+
isIE = true;
|
201
|
+
name = 'IE';
|
202
|
+
fullVersion = '11';
|
203
|
+
} else if ((verOffset = ua.indexOf('MSIE')) !== -1) {
|
204
|
+
// MSIE
|
205
|
+
isIE = true;
|
206
|
+
name = 'IE';
|
207
|
+
fullVersion = ua.substring(verOffset + 5);
|
208
|
+
} else if ((verOffset = ua.indexOf('Chrome')) !== -1) {
|
209
|
+
// Chrome
|
210
|
+
isChrome = true;
|
211
|
+
name = 'Chrome';
|
212
|
+
fullVersion = ua.substring(verOffset + 7);
|
213
|
+
} else if ((verOffset = ua.indexOf('Safari')) !== -1) {
|
214
|
+
// Safari
|
215
|
+
isSafari = true;
|
216
|
+
name = 'Safari';
|
217
|
+
fullVersion = ua.substring(verOffset + 7);
|
218
|
+
if ((verOffset = ua.indexOf('Version')) !== -1) {
|
219
|
+
fullVersion = ua.substring(verOffset + 8);
|
220
|
+
}
|
221
|
+
} else if ((verOffset = ua.indexOf('Firefox')) !== -1) {
|
222
|
+
// Firefox
|
223
|
+
isFirefox = true;
|
224
|
+
name = 'Firefox';
|
225
|
+
fullVersion = ua.substring(verOffset + 8);
|
226
|
+
} else if ((nameOffset = ua.lastIndexOf(' ') + 1) < (verOffset = ua.lastIndexOf('/'))) {
|
227
|
+
// In most other browsers, 'name/version' is at the end of userAgent
|
228
|
+
name = ua.substring(nameOffset,verOffset);
|
229
|
+
fullVersion = ua.substring(verOffset + 1);
|
230
|
+
|
231
|
+
if (name.toLowerCase() === name.toUpperCase()) {
|
232
|
+
name = navigator.appName;
|
233
|
+
}
|
234
|
+
}
|
235
|
+
|
236
|
+
// Trim the fullVersion string at semicolon/space if present
|
237
|
+
if ((ix = fullVersion.indexOf(';')) !== -1) {
|
238
|
+
fullVersion = fullVersion.substring(0, ix);
|
239
|
+
}
|
240
|
+
if ((ix = fullVersion.indexOf(' ')) !== -1) {
|
241
|
+
fullVersion = fullVersion.substring(0, ix);
|
242
|
+
}
|
243
|
+
|
244
|
+
// Get major version
|
245
|
+
majorVersion = parseInt('' + fullVersion, 10);
|
246
|
+
if (isNaN(majorVersion)) {
|
247
|
+
fullVersion = '' + parseFloat(navigator.appVersion);
|
248
|
+
majorVersion = parseInt(navigator.appVersion, 10);
|
249
|
+
}
|
250
|
+
|
251
|
+
// Return data
|
252
|
+
return {
|
253
|
+
name: name,
|
254
|
+
version: majorVersion,
|
255
|
+
isIE: isIE,
|
256
|
+
isFirefox: isFirefox,
|
257
|
+
isChrome: isChrome,
|
258
|
+
isSafari: isSafari,
|
259
|
+
isIos: /(iPad|iPhone|iPod)/g.test(navigator.platform),
|
260
|
+
isIphone: /(iPhone|iPod)/g.test(navigator.userAgent),
|
261
|
+
isTouch: 'ontouchstart' in document.documentElement
|
262
|
+
};
|
263
|
+
}
|
264
|
+
|
265
|
+
// Check for mime type support against a player instance
|
266
|
+
// Credits: http://diveintohtml5.info/everything.html
|
267
|
+
// Related: http://www.leanbackplyr.com/test/h5mt.html
|
268
|
+
function _supportMime(plyr, mimeType) {
|
269
|
+
var media = plyr.media;
|
270
|
+
|
271
|
+
if (plyr.type === 'video') {
|
272
|
+
// Check type
|
273
|
+
switch (mimeType) {
|
274
|
+
case 'video/webm': return !!(media.canPlayType && media.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/no/, ''));
|
275
|
+
case 'video/mp4': return !!(media.canPlayType && media.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"').replace(/no/, ''));
|
276
|
+
case 'video/ogg': return !!(media.canPlayType && media.canPlayType('video/ogg; codecs="theora"').replace(/no/, ''));
|
277
|
+
}
|
278
|
+
} else if (plyr.type === 'audio') {
|
279
|
+
// Check type
|
280
|
+
switch (mimeType) {
|
281
|
+
case 'audio/mpeg': return !!(media.canPlayType && media.canPlayType('audio/mpeg;').replace(/no/, ''));
|
282
|
+
case 'audio/ogg': return !!(media.canPlayType && media.canPlayType('audio/ogg; codecs="vorbis"').replace(/no/, ''));
|
283
|
+
case 'audio/wav': return !!(media.canPlayType && media.canPlayType('audio/wav; codecs="1"').replace(/no/, ''));
|
284
|
+
}
|
285
|
+
}
|
286
|
+
|
287
|
+
// If we got this far, we're stuffed
|
288
|
+
return false;
|
289
|
+
}
|
290
|
+
|
291
|
+
// Inject a script
|
292
|
+
function _injectScript(source) {
|
293
|
+
if (document.querySelectorAll('script[src="' + source + '"]').length) {
|
294
|
+
return;
|
295
|
+
}
|
296
|
+
|
297
|
+
var tag = document.createElement('script');
|
298
|
+
tag.src = source;
|
299
|
+
var firstScriptTag = document.getElementsByTagName('script')[0];
|
300
|
+
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
|
301
|
+
}
|
302
|
+
|
303
|
+
// Element exists in an array
|
304
|
+
function _inArray(haystack, needle) {
|
305
|
+
return Array.prototype.indexOf && (haystack.indexOf(needle) !== -1);
|
306
|
+
}
|
307
|
+
|
308
|
+
// Replace all
|
309
|
+
function _replaceAll(string, find, replace) {
|
310
|
+
return string.replace(new RegExp(find.replace(/([.*+?\^=!:${}()|\[\]\/\\])/g, '\\$1'), 'g'), replace);
|
311
|
+
}
|
312
|
+
|
313
|
+
// Wrap an element
|
314
|
+
function _wrap(elements, wrapper) {
|
315
|
+
// Convert `elements` to an array, if necessary.
|
316
|
+
if (!elements.length) {
|
317
|
+
elements = [elements];
|
318
|
+
}
|
319
|
+
|
320
|
+
// Loops backwards to prevent having to clone the wrapper on the
|
321
|
+
// first element (see `child` below).
|
322
|
+
for (var i = elements.length - 1; i >= 0; i--) {
|
323
|
+
var child = (i > 0) ? wrapper.cloneNode(true) : wrapper;
|
324
|
+
var element = elements[i];
|
325
|
+
|
326
|
+
// Cache the current parent and sibling.
|
327
|
+
var parent = element.parentNode;
|
328
|
+
var sibling = element.nextSibling;
|
329
|
+
|
330
|
+
// Wrap the element (is automatically removed from its current
|
331
|
+
// parent).
|
332
|
+
child.appendChild(element);
|
333
|
+
|
334
|
+
// If the element had a sibling, insert the wrapper before
|
335
|
+
// the sibling to maintain the HTML structure; otherwise, just
|
336
|
+
// append it to the parent.
|
337
|
+
if (sibling) {
|
338
|
+
parent.insertBefore(child, sibling);
|
339
|
+
} else {
|
340
|
+
parent.appendChild(child);
|
341
|
+
}
|
342
|
+
|
343
|
+
return child;
|
344
|
+
}
|
345
|
+
}
|
346
|
+
|
347
|
+
// Unwrap an element
|
348
|
+
// http://plainjs.com/javascript/manipulation/unwrap-a-dom-element-35/
|
349
|
+
/*function _unwrap(wrapper) {
|
350
|
+
// Get the element's parent node
|
351
|
+
var parent = wrapper.parentNode;
|
352
|
+
|
353
|
+
// Move all children out of the element
|
354
|
+
while (wrapper.firstChild) {
|
355
|
+
parent.insertBefore(wrapper.firstChild, wrapper);
|
356
|
+
}
|
357
|
+
|
358
|
+
// Remove the empty element
|
359
|
+
parent.removeChild(wrapper);
|
360
|
+
}*/
|
361
|
+
|
362
|
+
// Remove an element
|
363
|
+
function _remove(element) {
|
364
|
+
if (!element) {
|
365
|
+
return;
|
366
|
+
}
|
367
|
+
element.parentNode.removeChild(element);
|
368
|
+
}
|
369
|
+
|
370
|
+
// Prepend child
|
371
|
+
function _prependChild(parent, element) {
|
372
|
+
parent.insertBefore(element, parent.firstChild);
|
373
|
+
}
|
374
|
+
|
375
|
+
// Set attributes
|
376
|
+
function _setAttributes(element, attributes) {
|
377
|
+
for (var key in attributes) {
|
378
|
+
element.setAttribute(key, (_is.boolean(attributes[key]) && attributes[key]) ? '' : attributes[key]);
|
379
|
+
}
|
380
|
+
}
|
381
|
+
|
382
|
+
// Insert a HTML element
|
383
|
+
function _insertElement(type, parent, attributes) {
|
384
|
+
// Create a new <element>
|
385
|
+
var element = document.createElement(type);
|
386
|
+
|
387
|
+
// Set all passed attributes
|
388
|
+
_setAttributes(element, attributes);
|
389
|
+
|
390
|
+
// Inject the new element
|
391
|
+
_prependChild(parent, element);
|
392
|
+
}
|
393
|
+
|
394
|
+
// Get a classname from selector
|
395
|
+
function _getClassname(selector) {
|
396
|
+
return selector.replace('.', '');
|
397
|
+
}
|
398
|
+
|
399
|
+
// Toggle class on an element
|
400
|
+
function _toggleClass(element, className, state) {
|
401
|
+
if (element) {
|
402
|
+
if (element.classList) {
|
403
|
+
element.classList[state ? 'add' : 'remove'](className);
|
404
|
+
} else {
|
405
|
+
var name = (' ' + element.className + ' ').replace(/\s+/g, ' ').replace(' ' + className + ' ', '');
|
406
|
+
element.className = name + (state ? ' ' + className : '');
|
407
|
+
}
|
408
|
+
}
|
409
|
+
}
|
410
|
+
|
411
|
+
// Has class name
|
412
|
+
function _hasClass(element, className) {
|
413
|
+
if (element) {
|
414
|
+
if (element.classList) {
|
415
|
+
return element.classList.contains(className);
|
416
|
+
} else {
|
417
|
+
return new RegExp('(\\s|^)' + className + '(\\s|$)').test(element.className);
|
418
|
+
}
|
419
|
+
}
|
420
|
+
return false;
|
421
|
+
}
|
422
|
+
|
423
|
+
// Element matches selector
|
424
|
+
function _matches(element, selector) {
|
425
|
+
var p = Element.prototype;
|
426
|
+
|
427
|
+
var f = p.matches || p.webkitMatchesSelector || p.mozMatchesSelector || p.msMatchesSelector || function(s) {
|
428
|
+
return [].indexOf.call(document.querySelectorAll(s), this) !== -1;
|
429
|
+
};
|
430
|
+
|
431
|
+
return f.call(element, selector);
|
432
|
+
}
|
433
|
+
|
434
|
+
// Bind along with custom handler
|
435
|
+
function _proxyListener(element, eventName, userListener, defaultListener, useCapture) {
|
436
|
+
_on(element, eventName, function(event) {
|
437
|
+
if (userListener) {
|
438
|
+
userListener.apply(element, [event]);
|
439
|
+
}
|
440
|
+
defaultListener.apply(element, [event]);
|
441
|
+
}, useCapture);
|
442
|
+
}
|
443
|
+
|
444
|
+
// Toggle event listener
|
445
|
+
function _toggleListener(element, events, callback, toggle, useCapture) {
|
446
|
+
var eventList = events.split(' ');
|
447
|
+
|
448
|
+
// Whether the listener is a capturing listener or not
|
449
|
+
// Default to false
|
450
|
+
if (!_is.boolean(useCapture)) {
|
451
|
+
useCapture = false;
|
452
|
+
}
|
453
|
+
|
454
|
+
// If a nodelist is passed, call itself on each node
|
455
|
+
if (element instanceof NodeList) {
|
456
|
+
for (var x = 0; x < element.length; x++) {
|
457
|
+
if (element[x] instanceof Node) {
|
458
|
+
_toggleListener(element[x], arguments[1], arguments[2], arguments[3]);
|
459
|
+
}
|
460
|
+
}
|
461
|
+
return;
|
462
|
+
}
|
463
|
+
|
464
|
+
// If a single node is passed, bind the event listener
|
465
|
+
for (var i = 0; i < eventList.length; i++) {
|
466
|
+
element[toggle ? 'addEventListener' : 'removeEventListener'](eventList[i], callback, useCapture);
|
467
|
+
}
|
468
|
+
}
|
469
|
+
|
470
|
+
// Bind event
|
471
|
+
function _on(element, events, callback, useCapture) {
|
472
|
+
if (element) {
|
473
|
+
_toggleListener(element, events, callback, true, useCapture);
|
474
|
+
}
|
475
|
+
}
|
476
|
+
|
477
|
+
// Unbind event
|
478
|
+
/*function _off(element, events, callback, useCapture) {
|
479
|
+
if (element) {
|
480
|
+
_toggleListener(element, events, callback, false, useCapture);
|
481
|
+
}
|
482
|
+
}*/
|
483
|
+
|
484
|
+
// Trigger event
|
485
|
+
function _event(element, type, bubbles, properties) {
|
486
|
+
// Bail if no element
|
487
|
+
if (!element || !type) {
|
488
|
+
return;
|
489
|
+
}
|
490
|
+
|
491
|
+
// Default bubbles to false
|
492
|
+
if (!_is.boolean(bubbles)) {
|
493
|
+
bubbles = false;
|
494
|
+
}
|
495
|
+
|
496
|
+
// Create and dispatch the event
|
497
|
+
var event = new CustomEvent(type, {
|
498
|
+
bubbles: bubbles,
|
499
|
+
detail: properties
|
500
|
+
});
|
501
|
+
|
502
|
+
// Dispatch the event
|
503
|
+
element.dispatchEvent(event);
|
504
|
+
}
|
505
|
+
|
506
|
+
// Toggle aria-pressed state on a toggle button
|
507
|
+
// http://www.ssbbartgroup.com/blog/how-not-to-misuse-aria-states-properties-and-roles
|
508
|
+
function _toggleState(target, state) {
|
509
|
+
// Bail if no target
|
510
|
+
if (!target) {
|
511
|
+
return;
|
512
|
+
}
|
513
|
+
|
514
|
+
// Get state
|
515
|
+
state = (_is.boolean(state) ? state : !target.getAttribute('aria-pressed'));
|
516
|
+
|
517
|
+
// Set the attribute on target
|
518
|
+
target.setAttribute('aria-pressed', state);
|
519
|
+
|
520
|
+
return state;
|
521
|
+
}
|
522
|
+
|
523
|
+
// Get percentage
|
524
|
+
function _getPercentage(current, max) {
|
525
|
+
if (current === 0 || max === 0 || isNaN(current) || isNaN(max)) {
|
526
|
+
return 0;
|
527
|
+
}
|
528
|
+
return ((current / max) * 100).toFixed(2);
|
529
|
+
}
|
530
|
+
|
531
|
+
// Deep extend/merge destination object with N more objects
|
532
|
+
// http://andrewdupont.net/2009/08/28/deep-extending-objects-in-javascript/
|
533
|
+
// Removed call to arguments.callee (used explicit function name instead)
|
534
|
+
function _extend() {
|
535
|
+
// Get arguments
|
536
|
+
var objects = arguments;
|
537
|
+
|
538
|
+
// Bail if nothing to merge
|
539
|
+
if (!objects.length) {
|
540
|
+
return;
|
541
|
+
}
|
542
|
+
|
543
|
+
// Return first if specified but nothing to merge
|
544
|
+
if (objects.length === 1) {
|
545
|
+
return objects[0];
|
546
|
+
}
|
547
|
+
|
548
|
+
// First object is the destination
|
549
|
+
var destination = Array.prototype.shift.call(objects),
|
550
|
+
length = objects.length;
|
551
|
+
|
552
|
+
// Loop through all objects to merge
|
553
|
+
for (var i = 0; i < length; i++) {
|
554
|
+
var source = objects[i];
|
555
|
+
|
556
|
+
for (var property in source) {
|
557
|
+
if (source[property] && source[property].constructor && source[property].constructor === Object) {
|
558
|
+
destination[property] = destination[property] || {};
|
559
|
+
_extend(destination[property], source[property]);
|
560
|
+
} else {
|
561
|
+
destination[property] = source[property];
|
562
|
+
}
|
563
|
+
}
|
564
|
+
}
|
565
|
+
|
566
|
+
return destination;
|
567
|
+
}
|
568
|
+
|
569
|
+
// Check variable types
|
570
|
+
var _is = {
|
571
|
+
object: function(input) {
|
572
|
+
return input !== null && typeof(input) === 'object';
|
573
|
+
},
|
574
|
+
array: function(input) {
|
575
|
+
return input !== null && (typeof(input) === 'object' && input.constructor === Array);
|
576
|
+
},
|
577
|
+
number: function(input) {
|
578
|
+
return input !== null && (typeof(input) === 'number' && !isNaN(input - 0) || (typeof input === 'object' && input.constructor === Number));
|
579
|
+
},
|
580
|
+
string: function(input) {
|
581
|
+
return input !== null && (typeof input === 'string' || (typeof input === 'object' && input.constructor === String));
|
582
|
+
},
|
583
|
+
boolean: function(input) {
|
584
|
+
return input !== null && typeof input === 'boolean';
|
585
|
+
},
|
586
|
+
nodeList: function(input) {
|
587
|
+
return input !== null && input instanceof NodeList;
|
588
|
+
},
|
589
|
+
htmlElement: function(input) {
|
590
|
+
return input !== null && input instanceof HTMLElement;
|
591
|
+
},
|
592
|
+
function: function(input) {
|
593
|
+
return input !== null && typeof input === 'function';
|
594
|
+
},
|
595
|
+
undefined: function(input) {
|
596
|
+
return input !== null && typeof input === 'undefined';
|
597
|
+
}
|
598
|
+
};
|
599
|
+
|
600
|
+
// Parse YouTube ID from url
|
601
|
+
function _parseYouTubeId(url) {
|
602
|
+
var regex = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/;
|
603
|
+
return (url.match(regex)) ? RegExp.$2 : url;
|
604
|
+
}
|
605
|
+
|
606
|
+
// Parse Vimeo ID from url
|
607
|
+
function _parseVimeoId(url) {
|
608
|
+
var regex = /^.*(vimeo.com\/|video\/)(\d+).*/;
|
609
|
+
return (url.match(regex)) ? RegExp.$2 : url;
|
610
|
+
}
|
611
|
+
|
612
|
+
// Fullscreen API
|
613
|
+
function _fullscreen() {
|
614
|
+
var fullscreen = {
|
615
|
+
supportsFullScreen: false,
|
616
|
+
isFullScreen: function() { return false; },
|
617
|
+
requestFullScreen: function() {},
|
618
|
+
cancelFullScreen: function() {},
|
619
|
+
fullScreenEventName: '',
|
620
|
+
element: null,
|
621
|
+
prefix: ''
|
622
|
+
},
|
623
|
+
browserPrefixes = 'webkit o moz ms khtml'.split(' ');
|
624
|
+
|
625
|
+
// Check for native support
|
626
|
+
if (!_is.undefined(document.cancelFullScreen)) {
|
627
|
+
fullscreen.supportsFullScreen = true;
|
628
|
+
} else {
|
629
|
+
// Check for fullscreen support by vendor prefix
|
630
|
+
for (var i = 0, il = browserPrefixes.length; i < il; i++ ) {
|
631
|
+
fullscreen.prefix = browserPrefixes[i];
|
632
|
+
|
633
|
+
if (!_is.undefined(document[fullscreen.prefix + 'CancelFullScreen'])) {
|
634
|
+
fullscreen.supportsFullScreen = true;
|
635
|
+
break;
|
636
|
+
} else if (!_is.undefined(document.msExitFullscreen) && document.msFullscreenEnabled) {
|
637
|
+
// Special case for MS (when isn't it?)
|
638
|
+
fullscreen.prefix = 'ms';
|
639
|
+
fullscreen.supportsFullScreen = true;
|
640
|
+
break;
|
641
|
+
}
|
642
|
+
}
|
643
|
+
}
|
644
|
+
|
645
|
+
// Update methods to do something useful
|
646
|
+
if (fullscreen.supportsFullScreen) {
|
647
|
+
// Yet again Microsoft awesomeness,
|
648
|
+
// Sometimes the prefix is 'ms', sometimes 'MS' to keep you on your toes
|
649
|
+
fullscreen.fullScreenEventName = (fullscreen.prefix === 'ms' ? 'MSFullscreenChange' : fullscreen.prefix + 'fullscreenchange');
|
650
|
+
|
651
|
+
fullscreen.isFullScreen = function(element) {
|
652
|
+
if (_is.undefined(element)) {
|
653
|
+
element = document.body;
|
654
|
+
}
|
655
|
+
switch (this.prefix) {
|
656
|
+
case '':
|
657
|
+
return document.fullscreenElement === element;
|
658
|
+
case 'moz':
|
659
|
+
return document.mozFullScreenElement === element;
|
660
|
+
default:
|
661
|
+
return document[this.prefix + 'FullscreenElement'] === element;
|
662
|
+
}
|
663
|
+
};
|
664
|
+
fullscreen.requestFullScreen = function(element) {
|
665
|
+
if (_is.undefined(element)) {
|
666
|
+
element = document.body;
|
667
|
+
}
|
668
|
+
return (this.prefix === '') ? element.requestFullScreen() : element[this.prefix + (this.prefix === 'ms' ? 'RequestFullscreen' : 'RequestFullScreen')]();
|
669
|
+
};
|
670
|
+
fullscreen.cancelFullScreen = function() {
|
671
|
+
return (this.prefix === '') ? document.cancelFullScreen() : document[this.prefix + (this.prefix === 'ms' ? 'ExitFullscreen' : 'CancelFullScreen')]();
|
672
|
+
};
|
673
|
+
fullscreen.element = function() {
|
674
|
+
return (this.prefix === '') ? document.fullscreenElement : document[this.prefix + 'FullscreenElement'];
|
675
|
+
};
|
676
|
+
}
|
677
|
+
|
678
|
+
return fullscreen;
|
679
|
+
}
|
680
|
+
|
681
|
+
// Local storage
|
682
|
+
var _storage = {
|
683
|
+
supported: (function() {
|
684
|
+
if (!('localStorage' in window)) {
|
685
|
+
return false;
|
686
|
+
}
|
687
|
+
|
688
|
+
// Try to use it (it might be disabled, e.g. user is in private/porn mode)
|
689
|
+
// see: https://github.com/Selz/plyr/issues/131
|
690
|
+
try {
|
691
|
+
// Add test item
|
692
|
+
window.localStorage.setItem('___test', 'OK');
|
693
|
+
|
694
|
+
// Get the test item
|
695
|
+
var result = window.localStorage.getItem('___test');
|
696
|
+
|
697
|
+
// Clean up
|
698
|
+
window.localStorage.removeItem('___test');
|
699
|
+
|
700
|
+
// Check if value matches
|
701
|
+
return (result === 'OK');
|
702
|
+
}
|
703
|
+
catch (e) {
|
704
|
+
return false;
|
705
|
+
}
|
706
|
+
|
707
|
+
return false;
|
708
|
+
})()
|
709
|
+
};
|
710
|
+
|
711
|
+
// Player instance
|
712
|
+
function Plyr(media, config) {
|
713
|
+
var plyr = this,
|
714
|
+
timers = {},
|
715
|
+
api;
|
716
|
+
|
717
|
+
// Set media
|
718
|
+
plyr.media = media;
|
719
|
+
var original = media.cloneNode(true);
|
720
|
+
|
721
|
+
// Trigger events, with plyr instance passed
|
722
|
+
function _triggerEvent(element, type, bubbles, properties) {
|
723
|
+
_event(element, type, bubbles, _extend({}, properties, {
|
724
|
+
plyr: api
|
725
|
+
}));
|
726
|
+
}
|
727
|
+
|
728
|
+
// Debugging
|
729
|
+
function _console(type, args) {
|
730
|
+
if (config.debug && window.console) {
|
731
|
+
args = Array.prototype.slice.call(args);
|
732
|
+
|
733
|
+
if (_is.string(config.logPrefix) && config.logPrefix.length) {
|
734
|
+
args.unshift(config.logPrefix);
|
735
|
+
}
|
736
|
+
|
737
|
+
console[type].apply(console, args);
|
738
|
+
}
|
739
|
+
}
|
740
|
+
var _log = function() { _console('log', arguments) },
|
741
|
+
_warn = function() { _console('warn', arguments) };
|
742
|
+
|
743
|
+
// Log config options
|
744
|
+
_log('Config', config);
|
745
|
+
|
746
|
+
// Get icon URL
|
747
|
+
function _getIconUrl() {
|
748
|
+
return {
|
749
|
+
url: config.iconUrl,
|
750
|
+
absolute: (config.iconUrl.indexOf("http") === 0) || plyr.browser.isIE
|
751
|
+
};
|
752
|
+
}
|
753
|
+
|
754
|
+
// Build the default HTML
|
755
|
+
function _buildControls() {
|
756
|
+
// Create html array
|
757
|
+
var html = [],
|
758
|
+
iconUrl = _getIconUrl(),
|
759
|
+
iconPath = (!iconUrl.absolute ? iconUrl.url : '') + '#' + config.iconPrefix;
|
760
|
+
|
761
|
+
// Larger overlaid play button
|
762
|
+
if (_inArray(config.controls, 'play-large')) {
|
763
|
+
html.push(
|
764
|
+
'<button type="button" data-plyr="play" class="plyr__play-large">',
|
765
|
+
'<svg><use xlink:href="' + iconPath + '-play" /></svg>',
|
766
|
+
'<span class="plyr__sr-only">' + config.i18n.play + '</span>',
|
767
|
+
'</button>'
|
768
|
+
);
|
769
|
+
}
|
770
|
+
|
771
|
+
html.push('<div class="plyr__controls">');
|
772
|
+
|
773
|
+
// Restart button
|
774
|
+
if (_inArray(config.controls, 'restart')) {
|
775
|
+
html.push(
|
776
|
+
'<button type="button" data-plyr="restart">',
|
777
|
+
'<svg><use xlink:href="' + iconPath + '-restart" /></svg>',
|
778
|
+
'<span class="plyr__sr-only">' + config.i18n.restart + '</span>',
|
779
|
+
'</button>'
|
780
|
+
);
|
781
|
+
}
|
782
|
+
|
783
|
+
// Rewind button
|
784
|
+
if (_inArray(config.controls, 'rewind')) {
|
785
|
+
html.push(
|
786
|
+
'<button type="button" data-plyr="rewind">',
|
787
|
+
'<svg><use xlink:href="' + iconPath + '-rewind" /></svg>',
|
788
|
+
'<span class="plyr__sr-only">' + config.i18n.rewind + '</span>',
|
789
|
+
'</button>'
|
790
|
+
);
|
791
|
+
}
|
792
|
+
|
793
|
+
// Play Pause button
|
794
|
+
// TODO: This should be a toggle button really?
|
795
|
+
if (_inArray(config.controls, 'play')) {
|
796
|
+
html.push(
|
797
|
+
'<button type="button" data-plyr="play">',
|
798
|
+
'<svg><use xlink:href="' + iconPath + '-play" /></svg>',
|
799
|
+
'<span class="plyr__sr-only">' + config.i18n.play + '</span>',
|
800
|
+
'</button>',
|
801
|
+
'<button type="button" data-plyr="pause">',
|
802
|
+
'<svg><use xlink:href="' + iconPath + '-pause" /></svg>',
|
803
|
+
'<span class="plyr__sr-only">' + config.i18n.pause + '</span>',
|
804
|
+
'</button>'
|
805
|
+
);
|
806
|
+
}
|
807
|
+
|
808
|
+
// Fast forward button
|
809
|
+
if (_inArray(config.controls, 'fast-forward')) {
|
810
|
+
html.push(
|
811
|
+
'<button type="button" data-plyr="fast-forward">',
|
812
|
+
'<svg><use xlink:href="' + iconPath + '-fast-forward" /></svg>',
|
813
|
+
'<span class="plyr__sr-only">' + config.i18n.forward + '</span>',
|
814
|
+
'</button>'
|
815
|
+
);
|
816
|
+
}
|
817
|
+
|
818
|
+
// Progress
|
819
|
+
if (_inArray(config.controls, 'progress')) {
|
820
|
+
// Create progress
|
821
|
+
html.push('<span class="plyr__progress">',
|
822
|
+
'<label for="seek{id}" class="plyr__sr-only">Seek</label>',
|
823
|
+
'<input id="seek{id}" class="plyr__progress--seek" type="range" min="0" max="100" step="0.1" value="0" data-plyr="seek">',
|
824
|
+
'<progress class="plyr__progress--played" max="100" value="0" role="presentation"></progress>',
|
825
|
+
'<progress class="plyr__progress--buffer" max="100" value="0">',
|
826
|
+
'<span>0</span>% ' + config.i18n.buffered,
|
827
|
+
'</progress>');
|
828
|
+
|
829
|
+
// Seek tooltip
|
830
|
+
if (config.tooltips.seek) {
|
831
|
+
html.push('<span class="plyr__tooltip">00:00</span>');
|
832
|
+
}
|
833
|
+
|
834
|
+
// Close
|
835
|
+
html.push('</span>');
|
836
|
+
}
|
837
|
+
|
838
|
+
// Media current time display
|
839
|
+
if (_inArray(config.controls, 'current-time')) {
|
840
|
+
html.push(
|
841
|
+
'<span class="plyr__time">',
|
842
|
+
'<span class="plyr__sr-only">' + config.i18n.currentTime + '</span>',
|
843
|
+
'<span class="plyr__time--current">00:00</span>',
|
844
|
+
'</span>'
|
845
|
+
);
|
846
|
+
}
|
847
|
+
|
848
|
+
// Media duration display
|
849
|
+
if (_inArray(config.controls, 'duration')) {
|
850
|
+
html.push(
|
851
|
+
'<span class="plyr__time">',
|
852
|
+
'<span class="plyr__sr-only">' + config.i18n.duration + '</span>',
|
853
|
+
'<span class="plyr__time--duration">00:00</span>',
|
854
|
+
'</span>'
|
855
|
+
);
|
856
|
+
}
|
857
|
+
|
858
|
+
// Toggle mute button
|
859
|
+
if (_inArray(config.controls, 'mute')) {
|
860
|
+
html.push(
|
861
|
+
'<button type="button" data-plyr="mute">',
|
862
|
+
'<svg class="icon--muted"><use xlink:href="' + iconPath + '-muted" /></svg>',
|
863
|
+
'<svg><use xlink:href="' + iconPath + '-volume" /></svg>',
|
864
|
+
'<span class="plyr__sr-only">' + config.i18n.toggleMute + '</span>',
|
865
|
+
'</button>'
|
866
|
+
);
|
867
|
+
}
|
868
|
+
|
869
|
+
// Volume range control
|
870
|
+
if (_inArray(config.controls, 'volume')) {
|
871
|
+
html.push(
|
872
|
+
'<span class="plyr__volume">',
|
873
|
+
'<label for="volume{id}" class="plyr__sr-only">' + config.i18n.volume + '</label>',
|
874
|
+
'<input id="volume{id}" class="plyr__volume--input" type="range" min="' + config.volumeMin + '" max="' + config.volumeMax + '" value="' + config.volume + '" data-plyr="volume">',
|
875
|
+
'<progress class="plyr__volume--display" max="' + config.volumeMax + '" value="' + config.volumeMin + '" role="presentation"></progress>',
|
876
|
+
'</span>'
|
877
|
+
);
|
878
|
+
}
|
879
|
+
|
880
|
+
// Toggle captions button
|
881
|
+
if (_inArray(config.controls, 'captions')) {
|
882
|
+
html.push(
|
883
|
+
'<button type="button" data-plyr="captions">',
|
884
|
+
'<svg class="icon--captions-on"><use xlink:href="' + iconPath + '-captions-on" /></svg>',
|
885
|
+
'<svg><use xlink:href="' + iconPath+ '-captions-off" /></svg>',
|
886
|
+
'<span class="plyr__sr-only">' + config.i18n.toggleCaptions + '</span>',
|
887
|
+
'</button>'
|
888
|
+
);
|
889
|
+
}
|
890
|
+
|
891
|
+
// Toggle fullscreen button
|
892
|
+
if (_inArray(config.controls, 'fullscreen')) {
|
893
|
+
html.push(
|
894
|
+
'<button type="button" data-plyr="fullscreen">',
|
895
|
+
'<svg class="icon--exit-fullscreen"><use xlink:href="' + iconPath + '-exit-fullscreen" /></svg>',
|
896
|
+
'<svg><use xlink:href="' + iconPath + '-enter-fullscreen" /></svg>',
|
897
|
+
'<span class="plyr__sr-only">' + config.i18n.toggleFullscreen + '</span>',
|
898
|
+
'</button>'
|
899
|
+
);
|
900
|
+
}
|
901
|
+
|
902
|
+
// Close everything
|
903
|
+
html.push('</div>');
|
904
|
+
|
905
|
+
return html.join('');
|
906
|
+
}
|
907
|
+
|
908
|
+
// Setup fullscreen
|
909
|
+
function _setupFullscreen() {
|
910
|
+
if (!plyr.supported.full) {
|
911
|
+
return;
|
912
|
+
}
|
913
|
+
|
914
|
+
if ((plyr.type !== 'audio' || config.fullscreen.allowAudio) && config.fullscreen.enabled) {
|
915
|
+
// Check for native support
|
916
|
+
var nativeSupport = fullscreen.supportsFullScreen;
|
917
|
+
|
918
|
+
if (nativeSupport || (config.fullscreen.fallback && !_inFrame())) {
|
919
|
+
_log((nativeSupport ? 'Native' : 'Fallback') + ' fullscreen enabled');
|
920
|
+
|
921
|
+
// Add styling hook
|
922
|
+
_toggleClass(plyr.container, config.classes.fullscreen.enabled, true);
|
923
|
+
} else {
|
924
|
+
_log('Fullscreen not supported and fallback disabled');
|
925
|
+
}
|
926
|
+
|
927
|
+
// Toggle state
|
928
|
+
if (plyr.buttons && plyr.buttons.fullscreen) {
|
929
|
+
_toggleState(plyr.buttons.fullscreen, false);
|
930
|
+
}
|
931
|
+
|
932
|
+
// Setup focus trap
|
933
|
+
_focusTrap();
|
934
|
+
}
|
935
|
+
}
|
936
|
+
|
937
|
+
// Setup captions
|
938
|
+
function _setupCaptions() {
|
939
|
+
// Bail if not HTML5 video
|
940
|
+
if (plyr.type !== 'video') {
|
941
|
+
return;
|
942
|
+
}
|
943
|
+
|
944
|
+
// Inject the container
|
945
|
+
if (!_getElement(config.selectors.captions)) {
|
946
|
+
plyr.videoContainer.insertAdjacentHTML('afterbegin', '<div class="' + _getClassname(config.selectors.captions) + '"></div>');
|
947
|
+
}
|
948
|
+
|
949
|
+
// Determine if HTML5 textTracks is supported
|
950
|
+
plyr.usingTextTracks = false;
|
951
|
+
if (plyr.media.textTracks) {
|
952
|
+
plyr.usingTextTracks = true;
|
953
|
+
}
|
954
|
+
|
955
|
+
// Get URL of caption file if exists
|
956
|
+
var captionSrc = '',
|
957
|
+
kind,
|
958
|
+
children = plyr.media.childNodes;
|
959
|
+
|
960
|
+
for (var i = 0; i < children.length; i++) {
|
961
|
+
if (children[i].nodeName.toLowerCase() === 'track') {
|
962
|
+
kind = children[i].kind;
|
963
|
+
if (kind === 'captions' || kind === 'subtitles') {
|
964
|
+
captionSrc = children[i].getAttribute('src');
|
965
|
+
}
|
966
|
+
}
|
967
|
+
}
|
968
|
+
|
969
|
+
// Record if caption file exists or not
|
970
|
+
plyr.captionExists = true;
|
971
|
+
if (captionSrc === '') {
|
972
|
+
plyr.captionExists = false;
|
973
|
+
_log('No caption track found');
|
974
|
+
} else {
|
975
|
+
_log('Caption track found; URI: ' + captionSrc);
|
976
|
+
}
|
977
|
+
|
978
|
+
// If no caption file exists, hide container for caption text
|
979
|
+
if (!plyr.captionExists) {
|
980
|
+
_toggleClass(plyr.container, config.classes.captions.enabled);
|
981
|
+
} else {
|
982
|
+
// Turn off native caption rendering to avoid double captions
|
983
|
+
// This doesn't seem to work in Safari 7+, so the <track> elements are removed from the dom below
|
984
|
+
var tracks = plyr.media.textTracks;
|
985
|
+
for (var x = 0; x < tracks.length; x++) {
|
986
|
+
tracks[x].mode = 'hidden';
|
987
|
+
}
|
988
|
+
|
989
|
+
// Enable UI
|
990
|
+
_showCaptions(plyr);
|
991
|
+
|
992
|
+
// Disable unsupported browsers than report false positive
|
993
|
+
// Firefox bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1033144
|
994
|
+
if ((plyr.browser.isIE && plyr.browser.version >= 10) ||
|
995
|
+
(plyr.browser.isFirefox && plyr.browser.version >= 31)) {
|
996
|
+
|
997
|
+
// Debugging
|
998
|
+
_log('Detected browser with known TextTrack issues - using manual fallback');
|
999
|
+
|
1000
|
+
// Set to false so skips to 'manual' captioning
|
1001
|
+
plyr.usingTextTracks = false;
|
1002
|
+
}
|
1003
|
+
|
1004
|
+
// Rendering caption tracks
|
1005
|
+
// Native support required - http://caniuse.com/webvtt
|
1006
|
+
if (plyr.usingTextTracks) {
|
1007
|
+
_log('TextTracks supported');
|
1008
|
+
|
1009
|
+
for (var y = 0; y < tracks.length; y++) {
|
1010
|
+
var track = tracks[y];
|
1011
|
+
|
1012
|
+
if (track.kind === 'captions' || track.kind === 'subtitles') {
|
1013
|
+
_on(track, 'cuechange', function() {
|
1014
|
+
// Display a cue, if there is one
|
1015
|
+
if (this.activeCues[0] && 'text' in this.activeCues[0]) {
|
1016
|
+
_setCaption(this.activeCues[0].getCueAsHTML());
|
1017
|
+
} else {
|
1018
|
+
_setCaption();
|
1019
|
+
}
|
1020
|
+
});
|
1021
|
+
}
|
1022
|
+
}
|
1023
|
+
} else {
|
1024
|
+
// Caption tracks not natively supported
|
1025
|
+
_log('TextTracks not supported so rendering captions manually');
|
1026
|
+
|
1027
|
+
// Render captions from array at appropriate time
|
1028
|
+
plyr.currentCaption = '';
|
1029
|
+
plyr.captions = [];
|
1030
|
+
|
1031
|
+
if (captionSrc !== '') {
|
1032
|
+
// Create XMLHttpRequest Object
|
1033
|
+
var xhr = new XMLHttpRequest();
|
1034
|
+
|
1035
|
+
xhr.onreadystatechange = function() {
|
1036
|
+
if (xhr.readyState === 4) {
|
1037
|
+
if (xhr.status === 200) {
|
1038
|
+
var captions = [],
|
1039
|
+
caption,
|
1040
|
+
req = xhr.responseText;
|
1041
|
+
|
1042
|
+
//According to webvtt spec, line terminator consists of one of the following
|
1043
|
+
// CRLF (U+000D U+000A), LF (U+000A) or CR (U+000D)
|
1044
|
+
var lineSeparator = '\r\n';
|
1045
|
+
if(req.indexOf(lineSeparator+lineSeparator) === -1) {
|
1046
|
+
if(req.indexOf('\r\r') !== -1){
|
1047
|
+
lineSeparator = '\r';
|
1048
|
+
} else {
|
1049
|
+
lineSeparator = '\n';
|
1050
|
+
}
|
1051
|
+
}
|
1052
|
+
|
1053
|
+
captions = req.split(lineSeparator+lineSeparator);
|
1054
|
+
|
1055
|
+
for (var r = 0; r < captions.length; r++) {
|
1056
|
+
caption = captions[r];
|
1057
|
+
plyr.captions[r] = [];
|
1058
|
+
|
1059
|
+
// Get the parts of the captions
|
1060
|
+
var parts = caption.split(lineSeparator),
|
1061
|
+
index = 0;
|
1062
|
+
|
1063
|
+
// Incase caption numbers are added
|
1064
|
+
if (parts[index].indexOf(":") === -1) {
|
1065
|
+
index = 1;
|
1066
|
+
}
|
1067
|
+
|
1068
|
+
plyr.captions[r] = [parts[index], parts[index + 1]];
|
1069
|
+
}
|
1070
|
+
|
1071
|
+
// Remove first element ('VTT')
|
1072
|
+
plyr.captions.shift();
|
1073
|
+
|
1074
|
+
_log('Successfully loaded the caption file via AJAX');
|
1075
|
+
} else {
|
1076
|
+
_warn(config.logPrefix + 'There was a problem loading the caption file via AJAX');
|
1077
|
+
}
|
1078
|
+
}
|
1079
|
+
};
|
1080
|
+
|
1081
|
+
xhr.open('get', captionSrc, true);
|
1082
|
+
|
1083
|
+
xhr.send();
|
1084
|
+
}
|
1085
|
+
}
|
1086
|
+
}
|
1087
|
+
}
|
1088
|
+
|
1089
|
+
// Set the current caption
|
1090
|
+
function _setCaption(caption) {
|
1091
|
+
/* jshint unused:false */
|
1092
|
+
var container = _getElement(config.selectors.captions),
|
1093
|
+
content = document.createElement('span');
|
1094
|
+
|
1095
|
+
// Empty the container
|
1096
|
+
container.innerHTML = '';
|
1097
|
+
|
1098
|
+
// Default to empty
|
1099
|
+
if (_is.undefined(caption)) {
|
1100
|
+
caption = '';
|
1101
|
+
}
|
1102
|
+
|
1103
|
+
// Set the span content
|
1104
|
+
if (_is.string(caption)) {
|
1105
|
+
content.innerHTML = caption.trim();
|
1106
|
+
} else {
|
1107
|
+
content.appendChild(caption);
|
1108
|
+
}
|
1109
|
+
|
1110
|
+
// Set new caption text
|
1111
|
+
container.appendChild(content);
|
1112
|
+
|
1113
|
+
// Force redraw (for Safari)
|
1114
|
+
var redraw = container.offsetHeight;
|
1115
|
+
}
|
1116
|
+
|
1117
|
+
// Captions functions
|
1118
|
+
// Seek the manual caption time and update UI
|
1119
|
+
function _seekManualCaptions(time) {
|
1120
|
+
// Utilities for caption time codes
|
1121
|
+
function _timecodeCommon(tc, pos) {
|
1122
|
+
var tcpair = [];
|
1123
|
+
tcpair = tc.split(' --> ');
|
1124
|
+
for(var i = 0; i < tcpair.length; i++) {
|
1125
|
+
// WebVTT allows for extra meta data after the timestamp line
|
1126
|
+
// So get rid of this if it exists
|
1127
|
+
tcpair[i] = tcpair[i].replace(/(\d+:\d+:\d+\.\d+).*/, "$1");
|
1128
|
+
}
|
1129
|
+
return _subTcSecs(tcpair[pos]);
|
1130
|
+
}
|
1131
|
+
function _timecodeMin(tc) {
|
1132
|
+
return _timecodeCommon(tc, 0);
|
1133
|
+
}
|
1134
|
+
function _timecodeMax(tc) {
|
1135
|
+
return _timecodeCommon(tc, 1);
|
1136
|
+
}
|
1137
|
+
function _subTcSecs(tc) {
|
1138
|
+
if (tc === null || tc === undefined) {
|
1139
|
+
return 0;
|
1140
|
+
} else {
|
1141
|
+
var tc1 = [],
|
1142
|
+
tc2 = [],
|
1143
|
+
seconds;
|
1144
|
+
tc1 = tc.split(',');
|
1145
|
+
tc2 = tc1[0].split(':');
|
1146
|
+
seconds = Math.floor(tc2[0]*60*60) + Math.floor(tc2[1]*60) + Math.floor(tc2[2]);
|
1147
|
+
return seconds;
|
1148
|
+
}
|
1149
|
+
}
|
1150
|
+
|
1151
|
+
// If it's not video, or we're using textTracks, bail.
|
1152
|
+
if (plyr.usingTextTracks || plyr.type !== 'video' || !plyr.supported.full) {
|
1153
|
+
return;
|
1154
|
+
}
|
1155
|
+
|
1156
|
+
// Reset subcount
|
1157
|
+
plyr.subcount = 0;
|
1158
|
+
|
1159
|
+
// Check time is a number, if not use currentTime
|
1160
|
+
// IE has a bug where currentTime doesn't go to 0
|
1161
|
+
// https://twitter.com/Sam_Potts/status/573715746506731521
|
1162
|
+
time = _is.number(time) ? time : plyr.media.currentTime;
|
1163
|
+
|
1164
|
+
// If there's no subs available, bail
|
1165
|
+
if (!plyr.captions[plyr.subcount]) {
|
1166
|
+
return;
|
1167
|
+
}
|
1168
|
+
|
1169
|
+
while (_timecodeMax(plyr.captions[plyr.subcount][0]) < time.toFixed(1)) {
|
1170
|
+
plyr.subcount++;
|
1171
|
+
if (plyr.subcount > plyr.captions.length - 1) {
|
1172
|
+
plyr.subcount = plyr.captions.length - 1;
|
1173
|
+
break;
|
1174
|
+
}
|
1175
|
+
}
|
1176
|
+
|
1177
|
+
// Check if the next caption is in the current time range
|
1178
|
+
if (plyr.media.currentTime.toFixed(1) >= _timecodeMin(plyr.captions[plyr.subcount][0]) &&
|
1179
|
+
plyr.media.currentTime.toFixed(1) <= _timecodeMax(plyr.captions[plyr.subcount][0])) {
|
1180
|
+
plyr.currentCaption = plyr.captions[plyr.subcount][1];
|
1181
|
+
|
1182
|
+
// Render the caption
|
1183
|
+
_setCaption(plyr.currentCaption);
|
1184
|
+
} else {
|
1185
|
+
_setCaption();
|
1186
|
+
}
|
1187
|
+
}
|
1188
|
+
|
1189
|
+
// Display captions container and button (for initialization)
|
1190
|
+
function _showCaptions() {
|
1191
|
+
// If there's no caption toggle, bail
|
1192
|
+
if (!plyr.buttons.captions) {
|
1193
|
+
return;
|
1194
|
+
}
|
1195
|
+
|
1196
|
+
_toggleClass(plyr.container, config.classes.captions.enabled, true);
|
1197
|
+
|
1198
|
+
// Try to load the value from storage
|
1199
|
+
var active = plyr.storage.captionsEnabled;
|
1200
|
+
|
1201
|
+
// Otherwise fall back to the default config
|
1202
|
+
if (!_is.boolean(active)) {
|
1203
|
+
active = config.captions.defaultActive;
|
1204
|
+
}
|
1205
|
+
|
1206
|
+
if (active) {
|
1207
|
+
_toggleClass(plyr.container, config.classes.captions.active, true);
|
1208
|
+
_toggleState(plyr.buttons.captions, true);
|
1209
|
+
}
|
1210
|
+
}
|
1211
|
+
|
1212
|
+
// Find all elements
|
1213
|
+
function _getElements(selector) {
|
1214
|
+
return plyr.container.querySelectorAll(selector);
|
1215
|
+
}
|
1216
|
+
|
1217
|
+
// Find a single element
|
1218
|
+
function _getElement(selector) {
|
1219
|
+
return _getElements(selector)[0];
|
1220
|
+
}
|
1221
|
+
|
1222
|
+
// Determine if we're in an iframe
|
1223
|
+
function _inFrame() {
|
1224
|
+
try {
|
1225
|
+
return window.self !== window.top;
|
1226
|
+
}
|
1227
|
+
catch (e) {
|
1228
|
+
return true;
|
1229
|
+
}
|
1230
|
+
}
|
1231
|
+
|
1232
|
+
// Trap focus inside container
|
1233
|
+
function _focusTrap() {
|
1234
|
+
var tabbables = _getElements('input:not([disabled]), button:not([disabled])'),
|
1235
|
+
first = tabbables[0],
|
1236
|
+
last = tabbables[tabbables.length - 1];
|
1237
|
+
|
1238
|
+
function _checkFocus(event) {
|
1239
|
+
// If it is TAB
|
1240
|
+
if (event.which === 9 && plyr.isFullscreen) {
|
1241
|
+
if (event.target === last && !event.shiftKey) {
|
1242
|
+
// Move focus to first element that can be tabbed if Shift isn't used
|
1243
|
+
event.preventDefault();
|
1244
|
+
first.focus();
|
1245
|
+
} else if (event.target === first && event.shiftKey) {
|
1246
|
+
// Move focus to last element that can be tabbed if Shift is used
|
1247
|
+
event.preventDefault();
|
1248
|
+
last.focus();
|
1249
|
+
}
|
1250
|
+
}
|
1251
|
+
}
|
1252
|
+
|
1253
|
+
// Bind the handler
|
1254
|
+
_on(plyr.container, 'keydown', _checkFocus);
|
1255
|
+
}
|
1256
|
+
|
1257
|
+
// Add elements to HTML5 media (source, tracks, etc)
|
1258
|
+
function _insertChildElements(type, attributes) {
|
1259
|
+
if (_is.string(attributes)) {
|
1260
|
+
_insertElement(type, plyr.media, { src: attributes });
|
1261
|
+
} else if (attributes.constructor === Array) {
|
1262
|
+
for (var i = attributes.length - 1; i >= 0; i--) {
|
1263
|
+
_insertElement(type, plyr.media, attributes[i]);
|
1264
|
+
}
|
1265
|
+
}
|
1266
|
+
}
|
1267
|
+
|
1268
|
+
// Insert controls
|
1269
|
+
function _injectControls() {
|
1270
|
+
// Sprite
|
1271
|
+
if (config.loadSprite) {
|
1272
|
+
var iconUrl = _getIconUrl();
|
1273
|
+
|
1274
|
+
// Only load external sprite using AJAX
|
1275
|
+
if (iconUrl.absolute) {
|
1276
|
+
_log('AJAX loading absolute SVG sprite' + (plyr.browser.isIE ? ' (due to IE)' : ''));
|
1277
|
+
loadSprite(iconUrl.url, "sprite-plyr");
|
1278
|
+
} else {
|
1279
|
+
_log('Sprite will be used as external resource directly');
|
1280
|
+
}
|
1281
|
+
}
|
1282
|
+
|
1283
|
+
// Make a copy of the html
|
1284
|
+
var html = config.html;
|
1285
|
+
|
1286
|
+
// Insert custom video controls
|
1287
|
+
_log('Injecting custom controls');
|
1288
|
+
|
1289
|
+
// If no controls are specified, create default
|
1290
|
+
if (!html) {
|
1291
|
+
html = _buildControls();
|
1292
|
+
}
|
1293
|
+
|
1294
|
+
// Replace seek time instances
|
1295
|
+
html = _replaceAll(html, '{seektime}', config.seekTime);
|
1296
|
+
|
1297
|
+
// Replace all id references with random numbers
|
1298
|
+
html = _replaceAll(html, '{id}', Math.floor(Math.random() * (10000)));
|
1299
|
+
|
1300
|
+
// Controls container
|
1301
|
+
var target;
|
1302
|
+
|
1303
|
+
// Inject to custom location
|
1304
|
+
if (_is.string(config.selectors.controls.container)) {
|
1305
|
+
target = document.querySelector(config.selectors.controls.container);
|
1306
|
+
}
|
1307
|
+
|
1308
|
+
// Inject into the container by default
|
1309
|
+
if (!_is.htmlElement(target)) {
|
1310
|
+
target = plyr.container
|
1311
|
+
}
|
1312
|
+
|
1313
|
+
// Inject controls HTML
|
1314
|
+
target.insertAdjacentHTML('beforeend', html);
|
1315
|
+
|
1316
|
+
// Setup tooltips
|
1317
|
+
if (config.tooltips.controls) {
|
1318
|
+
var labels = _getElements([config.selectors.controls.wrapper, ' ', config.selectors.labels, ' .', config.classes.hidden].join(''));
|
1319
|
+
|
1320
|
+
for (var i = labels.length - 1; i >= 0; i--) {
|
1321
|
+
var label = labels[i];
|
1322
|
+
|
1323
|
+
_toggleClass(label, config.classes.hidden, false);
|
1324
|
+
_toggleClass(label, config.classes.tooltip, true);
|
1325
|
+
}
|
1326
|
+
}
|
1327
|
+
}
|
1328
|
+
|
1329
|
+
// Find the UI controls and store references
|
1330
|
+
function _findElements() {
|
1331
|
+
try {
|
1332
|
+
plyr.controls = _getElement(config.selectors.controls.wrapper);
|
1333
|
+
|
1334
|
+
// Buttons
|
1335
|
+
plyr.buttons = {};
|
1336
|
+
plyr.buttons.seek = _getElement(config.selectors.buttons.seek);
|
1337
|
+
plyr.buttons.play = _getElements(config.selectors.buttons.play);
|
1338
|
+
plyr.buttons.pause = _getElement(config.selectors.buttons.pause);
|
1339
|
+
plyr.buttons.restart = _getElement(config.selectors.buttons.restart);
|
1340
|
+
plyr.buttons.rewind = _getElement(config.selectors.buttons.rewind);
|
1341
|
+
plyr.buttons.forward = _getElement(config.selectors.buttons.forward);
|
1342
|
+
plyr.buttons.fullscreen = _getElement(config.selectors.buttons.fullscreen);
|
1343
|
+
|
1344
|
+
// Inputs
|
1345
|
+
plyr.buttons.mute = _getElement(config.selectors.buttons.mute);
|
1346
|
+
plyr.buttons.captions = _getElement(config.selectors.buttons.captions);
|
1347
|
+
|
1348
|
+
// Progress
|
1349
|
+
plyr.progress = {};
|
1350
|
+
plyr.progress.container = _getElement(config.selectors.progress.container);
|
1351
|
+
|
1352
|
+
// Progress - Buffering
|
1353
|
+
plyr.progress.buffer = {};
|
1354
|
+
plyr.progress.buffer.bar = _getElement(config.selectors.progress.buffer);
|
1355
|
+
plyr.progress.buffer.text = plyr.progress.buffer.bar && plyr.progress.buffer.bar.getElementsByTagName('span')[0];
|
1356
|
+
|
1357
|
+
// Progress - Played
|
1358
|
+
plyr.progress.played = _getElement(config.selectors.progress.played);
|
1359
|
+
|
1360
|
+
// Seek tooltip
|
1361
|
+
plyr.progress.tooltip = plyr.progress.container && plyr.progress.container.querySelector('.' + config.classes.tooltip);
|
1362
|
+
|
1363
|
+
// Volume
|
1364
|
+
plyr.volume = {};
|
1365
|
+
plyr.volume.input = _getElement(config.selectors.volume.input);
|
1366
|
+
plyr.volume.display = _getElement(config.selectors.volume.display);
|
1367
|
+
|
1368
|
+
// Timing
|
1369
|
+
plyr.duration = _getElement(config.selectors.duration);
|
1370
|
+
plyr.currentTime = _getElement(config.selectors.currentTime);
|
1371
|
+
plyr.seekTime = _getElements(config.selectors.seekTime);
|
1372
|
+
|
1373
|
+
return true;
|
1374
|
+
}
|
1375
|
+
catch(e) {
|
1376
|
+
_warn('It looks like there is a problem with your controls HTML');
|
1377
|
+
|
1378
|
+
// Restore native video controls
|
1379
|
+
_toggleNativeControls(true);
|
1380
|
+
|
1381
|
+
return false;
|
1382
|
+
}
|
1383
|
+
}
|
1384
|
+
|
1385
|
+
// Toggle style hook
|
1386
|
+
function _toggleStyleHook() {
|
1387
|
+
_toggleClass(plyr.container, config.selectors.container.replace('.', ''), plyr.supported.full);
|
1388
|
+
}
|
1389
|
+
|
1390
|
+
// Toggle native controls
|
1391
|
+
function _toggleNativeControls(toggle) {
|
1392
|
+
if (toggle && _inArray(config.types.html5, plyr.type)) {
|
1393
|
+
plyr.media.setAttribute('controls', '');
|
1394
|
+
} else {
|
1395
|
+
plyr.media.removeAttribute('controls');
|
1396
|
+
}
|
1397
|
+
}
|
1398
|
+
|
1399
|
+
// Setup aria attribute for play and iframe title
|
1400
|
+
function _setTitle(iframe) {
|
1401
|
+
// Find the current text
|
1402
|
+
var label = config.i18n.play;
|
1403
|
+
|
1404
|
+
// If there's a media title set, use that for the label
|
1405
|
+
if (_is.string(config.title) && config.title.length) {
|
1406
|
+
label += ', ' + config.title;
|
1407
|
+
|
1408
|
+
// Set container label
|
1409
|
+
plyr.container.setAttribute('aria-label', config.title);
|
1410
|
+
}
|
1411
|
+
|
1412
|
+
// If there's a play button, set label
|
1413
|
+
if (plyr.supported.full && plyr.buttons.play) {
|
1414
|
+
for (var i = plyr.buttons.play.length - 1; i >= 0; i--) {
|
1415
|
+
plyr.buttons.play[i].setAttribute('aria-label', label);
|
1416
|
+
}
|
1417
|
+
}
|
1418
|
+
|
1419
|
+
// Set iframe title
|
1420
|
+
// https://github.com/Selz/plyr/issues/124
|
1421
|
+
if (_is.htmlElement(iframe)) {
|
1422
|
+
iframe.setAttribute('title', config.i18n.frameTitle.replace('{title}', config.title));
|
1423
|
+
}
|
1424
|
+
}
|
1425
|
+
|
1426
|
+
// Setup localStorage
|
1427
|
+
function _setupStorage() {
|
1428
|
+
var value = null;
|
1429
|
+
plyr.storage = {};
|
1430
|
+
|
1431
|
+
// Bail if we don't have localStorage support or it's disabled
|
1432
|
+
if (!_storage.supported || !config.storage.enabled) {
|
1433
|
+
return;
|
1434
|
+
}
|
1435
|
+
|
1436
|
+
// Clean up old volume
|
1437
|
+
// https://github.com/Selz/plyr/issues/171
|
1438
|
+
window.localStorage.removeItem('plyr-volume');
|
1439
|
+
|
1440
|
+
// load value from the current key
|
1441
|
+
value = window.localStorage.getItem(config.storage.key);
|
1442
|
+
|
1443
|
+
if (!value) {
|
1444
|
+
// Key wasn't set (or had been cleared), move along
|
1445
|
+
return;
|
1446
|
+
} else if (/^\d+(\.\d+)?$/.test(value)) {
|
1447
|
+
// If value is a number, it's probably volume from an older
|
1448
|
+
// version of plyr. See: https://github.com/Selz/plyr/pull/313
|
1449
|
+
// Update the key to be JSON
|
1450
|
+
_updateStorage({volume: parseFloat(value)});
|
1451
|
+
} else {
|
1452
|
+
// Assume it's JSON from this or a later version of plyr
|
1453
|
+
plyr.storage = JSON.parse(value);
|
1454
|
+
}
|
1455
|
+
}
|
1456
|
+
|
1457
|
+
// Save a value back to local storage
|
1458
|
+
function _updateStorage(value) {
|
1459
|
+
// Bail if we don't have localStorage support or it's disabled
|
1460
|
+
if (!_storage.supported || !config.storage.enabled) {
|
1461
|
+
return;
|
1462
|
+
}
|
1463
|
+
|
1464
|
+
// Update the working copy of the values
|
1465
|
+
_extend(plyr.storage, value);
|
1466
|
+
|
1467
|
+
// Update storage
|
1468
|
+
window.localStorage.setItem(config.storage.key, JSON.stringify(plyr.storage));
|
1469
|
+
}
|
1470
|
+
|
1471
|
+
// Setup media
|
1472
|
+
function _setupMedia() {
|
1473
|
+
// If there's no media, bail
|
1474
|
+
if (!plyr.media) {
|
1475
|
+
_warn('No media element found!');
|
1476
|
+
return;
|
1477
|
+
}
|
1478
|
+
|
1479
|
+
if (plyr.supported.full) {
|
1480
|
+
// Add type class
|
1481
|
+
_toggleClass(plyr.container, config.classes.type.replace('{0}', plyr.type), true);
|
1482
|
+
|
1483
|
+
// Add video class for embeds
|
1484
|
+
// This will require changes if audio embeds are added
|
1485
|
+
if (_inArray(config.types.embed, plyr.type)) {
|
1486
|
+
_toggleClass(plyr.container, config.classes.type.replace('{0}', 'video'), true);
|
1487
|
+
}
|
1488
|
+
|
1489
|
+
// If there's no autoplay attribute, assume the video is stopped and add state class
|
1490
|
+
_toggleClass(plyr.container, config.classes.stopped, config.autoplay);
|
1491
|
+
|
1492
|
+
// Add iOS class
|
1493
|
+
_toggleClass(plyr.ontainer, config.classes.isIos, plyr.browser.isIos);
|
1494
|
+
|
1495
|
+
// Add touch class
|
1496
|
+
_toggleClass(plyr.container, config.classes.isTouch, plyr.browser.isTouch);
|
1497
|
+
|
1498
|
+
// Inject the player wrapper
|
1499
|
+
if (plyr.type === 'video') {
|
1500
|
+
// Create the wrapper div
|
1501
|
+
var wrapper = document.createElement('div');
|
1502
|
+
wrapper.setAttribute('class', config.classes.videoWrapper);
|
1503
|
+
|
1504
|
+
// Wrap the video in a container
|
1505
|
+
_wrap(plyr.media, wrapper);
|
1506
|
+
|
1507
|
+
// Cache the container
|
1508
|
+
plyr.videoContainer = wrapper;
|
1509
|
+
}
|
1510
|
+
}
|
1511
|
+
|
1512
|
+
// Embeds
|
1513
|
+
if (_inArray(config.types.embed, plyr.type)) {
|
1514
|
+
_setupEmbed();
|
1515
|
+
}
|
1516
|
+
}
|
1517
|
+
|
1518
|
+
// Setup YouTube/Vimeo
|
1519
|
+
function _setupEmbed() {
|
1520
|
+
var container = document.createElement('div'),
|
1521
|
+
mediaId,
|
1522
|
+
id = plyr.type + '-' + Math.floor(Math.random() * (10000));
|
1523
|
+
|
1524
|
+
// Parse IDs from URLs if supplied
|
1525
|
+
switch (plyr.type) {
|
1526
|
+
case 'youtube':
|
1527
|
+
mediaId = _parseYouTubeId(plyr.embedId);
|
1528
|
+
break;
|
1529
|
+
|
1530
|
+
case 'vimeo':
|
1531
|
+
mediaId = _parseVimeoId(plyr.embedId);
|
1532
|
+
break;
|
1533
|
+
|
1534
|
+
default:
|
1535
|
+
mediaId = plyr.embedId;
|
1536
|
+
}
|
1537
|
+
|
1538
|
+
// Remove old containers
|
1539
|
+
var containers = _getElements('[id^="' + plyr.type + '-"]');
|
1540
|
+
for (var i = containers.length - 1; i >= 0; i--) {
|
1541
|
+
_remove(containers[i]);
|
1542
|
+
}
|
1543
|
+
|
1544
|
+
// Add embed class for responsive
|
1545
|
+
_toggleClass(plyr.media, config.classes.videoWrapper, true);
|
1546
|
+
_toggleClass(plyr.media, config.classes.embedWrapper, true);
|
1547
|
+
|
1548
|
+
if (plyr.type === 'youtube') {
|
1549
|
+
// Create the YouTube container
|
1550
|
+
plyr.media.appendChild(container);
|
1551
|
+
|
1552
|
+
// Set ID
|
1553
|
+
container.setAttribute('id', id);
|
1554
|
+
|
1555
|
+
// Setup API
|
1556
|
+
if (_is.object(window.YT)) {
|
1557
|
+
_youTubeReady(mediaId, container);
|
1558
|
+
} else {
|
1559
|
+
// Load the API
|
1560
|
+
_injectScript(config.urls.youtube.api);
|
1561
|
+
|
1562
|
+
// Setup callback for the API
|
1563
|
+
window.onYouTubeReadyCallbacks = window.onYouTubeReadyCallbacks || [];
|
1564
|
+
|
1565
|
+
// Add to queue
|
1566
|
+
window.onYouTubeReadyCallbacks.push(function() { _youTubeReady(mediaId, container); });
|
1567
|
+
|
1568
|
+
// Set callback to process queue
|
1569
|
+
window.onYouTubeIframeAPIReady = function () {
|
1570
|
+
window.onYouTubeReadyCallbacks.forEach(function(callback) { callback(); });
|
1571
|
+
};
|
1572
|
+
}
|
1573
|
+
} else if (plyr.type === 'vimeo') {
|
1574
|
+
// Vimeo needs an extra div to hide controls on desktop (which has full support)
|
1575
|
+
if (plyr.supported.full) {
|
1576
|
+
plyr.media.appendChild(container);
|
1577
|
+
} else {
|
1578
|
+
container = plyr.media;
|
1579
|
+
}
|
1580
|
+
|
1581
|
+
// Set ID
|
1582
|
+
container.setAttribute('id', id);
|
1583
|
+
|
1584
|
+
// Load the API if not already
|
1585
|
+
if (!_is.object(window.Vimeo)) {
|
1586
|
+
_injectScript(config.urls.vimeo.api);
|
1587
|
+
|
1588
|
+
// Wait for fragaloop load
|
1589
|
+
var vimeoTimer = window.setInterval(function() {
|
1590
|
+
if (_is.object(window.Vimeo)) {
|
1591
|
+
window.clearInterval(vimeoTimer);
|
1592
|
+
_vimeoReady(mediaId, container);
|
1593
|
+
}
|
1594
|
+
}, 50);
|
1595
|
+
} else {
|
1596
|
+
_vimeoReady(mediaId, container);
|
1597
|
+
}
|
1598
|
+
} else if (plyr.type === 'soundcloud') {
|
1599
|
+
// TODO: Currently unsupported and undocumented
|
1600
|
+
// Inject the iframe
|
1601
|
+
var soundCloud = document.createElement('iframe');
|
1602
|
+
|
1603
|
+
// Watch for iframe load
|
1604
|
+
soundCloud.loaded = false;
|
1605
|
+
_on(soundCloud, 'load', function() { soundCloud.loaded = true; });
|
1606
|
+
|
1607
|
+
_setAttributes(soundCloud, {
|
1608
|
+
'src': 'https://w.soundcloud.com/player/?url=https://api.soundcloud.com/tracks/' + mediaId,
|
1609
|
+
'id': id
|
1610
|
+
});
|
1611
|
+
|
1612
|
+
container.appendChild(soundCloud);
|
1613
|
+
plyr.media.appendChild(container);
|
1614
|
+
|
1615
|
+
// Load the API if not already
|
1616
|
+
if (!window.SC) {
|
1617
|
+
_injectScript(config.urls.soundcloud.api);
|
1618
|
+
}
|
1619
|
+
|
1620
|
+
// Wait for SC load
|
1621
|
+
var soundCloudTimer = window.setInterval(function() {
|
1622
|
+
if (window.SC && soundCloud.loaded) {
|
1623
|
+
window.clearInterval(soundCloudTimer);
|
1624
|
+
_soundcloudReady.call(soundCloud);
|
1625
|
+
}
|
1626
|
+
}, 50);
|
1627
|
+
}
|
1628
|
+
}
|
1629
|
+
|
1630
|
+
// When embeds are ready
|
1631
|
+
function _embedReady() {
|
1632
|
+
// Setup the UI and call ready if full support
|
1633
|
+
if (plyr.supported.full) {
|
1634
|
+
_setupInterface();
|
1635
|
+
_ready();
|
1636
|
+
}
|
1637
|
+
|
1638
|
+
// Set title
|
1639
|
+
_setTitle(_getElement('iframe'));
|
1640
|
+
}
|
1641
|
+
|
1642
|
+
// Handle YouTube API ready
|
1643
|
+
function _youTubeReady(videoId, container) {
|
1644
|
+
// Setup instance
|
1645
|
+
// https://developers.google.com/youtube/iframe_api_reference
|
1646
|
+
plyr.embed = new window.YT.Player(container.id, {
|
1647
|
+
videoId: videoId,
|
1648
|
+
playerVars: {
|
1649
|
+
autoplay: (config.autoplay ? 1 : 0),
|
1650
|
+
controls: (plyr.supported.full ? 0 : 1),
|
1651
|
+
rel: 0,
|
1652
|
+
showinfo: 0,
|
1653
|
+
iv_load_policy: 3,
|
1654
|
+
cc_load_policy: (config.captions.defaultActive ? 1 : 0),
|
1655
|
+
cc_lang_pref: 'en',
|
1656
|
+
wmode: 'transparent',
|
1657
|
+
modestbranding: 1,
|
1658
|
+
disablekb: 1,
|
1659
|
+
origin: '*' // https://code.google.com/p/gdata-issues/issues/detail?id=5788#c45
|
1660
|
+
},
|
1661
|
+
events: {
|
1662
|
+
'onError': function(event) {
|
1663
|
+
_triggerEvent(plyr.container, 'error', true, {
|
1664
|
+
code: event.data,
|
1665
|
+
embed: event.target
|
1666
|
+
});
|
1667
|
+
},
|
1668
|
+
'onReady': function(event) {
|
1669
|
+
// Get the instance
|
1670
|
+
var instance = event.target;
|
1671
|
+
|
1672
|
+
// Create a faux HTML5 API using the YouTube API
|
1673
|
+
plyr.media.play = function() {
|
1674
|
+
instance.playVideo();
|
1675
|
+
plyr.media.paused = false;
|
1676
|
+
};
|
1677
|
+
plyr.media.pause = function() {
|
1678
|
+
instance.pauseVideo();
|
1679
|
+
plyr.media.paused = true;
|
1680
|
+
};
|
1681
|
+
plyr.media.stop = function() {
|
1682
|
+
instance.stopVideo();
|
1683
|
+
plyr.media.paused = true;
|
1684
|
+
};
|
1685
|
+
plyr.media.duration = instance.getDuration();
|
1686
|
+
plyr.media.paused = true;
|
1687
|
+
plyr.media.currentTime = 0;
|
1688
|
+
plyr.media.muted = instance.isMuted();
|
1689
|
+
|
1690
|
+
// Set title
|
1691
|
+
config.title = instance.getVideoData().title;
|
1692
|
+
|
1693
|
+
// Set the tabindex
|
1694
|
+
if (plyr.supported.full) {
|
1695
|
+
plyr.media.querySelector('iframe').setAttribute('tabindex', '-1');
|
1696
|
+
}
|
1697
|
+
|
1698
|
+
// Update UI
|
1699
|
+
_embedReady();
|
1700
|
+
|
1701
|
+
// Trigger timeupdate
|
1702
|
+
_triggerEvent(plyr.media, 'timeupdate');
|
1703
|
+
|
1704
|
+
// Trigger timeupdate
|
1705
|
+
_triggerEvent(plyr.media, 'durationchange');
|
1706
|
+
|
1707
|
+
// Reset timer
|
1708
|
+
window.clearInterval(timers.buffering);
|
1709
|
+
|
1710
|
+
// Setup buffering
|
1711
|
+
timers.buffering = window.setInterval(function() {
|
1712
|
+
// Get loaded % from YouTube
|
1713
|
+
plyr.media.buffered = instance.getVideoLoadedFraction();
|
1714
|
+
|
1715
|
+
// Trigger progress only when we actually buffer something
|
1716
|
+
if (plyr.media.lastBuffered === null || plyr.media.lastBuffered < plyr.media.buffered) {
|
1717
|
+
_triggerEvent(plyr.media, 'progress');
|
1718
|
+
}
|
1719
|
+
|
1720
|
+
// Set last buffer point
|
1721
|
+
plyr.media.lastBuffered = plyr.media.buffered;
|
1722
|
+
|
1723
|
+
// Bail if we're at 100%
|
1724
|
+
if (plyr.media.buffered === 1) {
|
1725
|
+
window.clearInterval(timers.buffering);
|
1726
|
+
|
1727
|
+
// Trigger event
|
1728
|
+
_triggerEvent(plyr.media, 'canplaythrough');
|
1729
|
+
}
|
1730
|
+
}, 200);
|
1731
|
+
},
|
1732
|
+
'onStateChange': function(event) {
|
1733
|
+
// Get the instance
|
1734
|
+
var instance = event.target;
|
1735
|
+
|
1736
|
+
// Reset timer
|
1737
|
+
window.clearInterval(timers.playing);
|
1738
|
+
|
1739
|
+
// Handle events
|
1740
|
+
// -1 Unstarted
|
1741
|
+
// 0 Ended
|
1742
|
+
// 1 Playing
|
1743
|
+
// 2 Paused
|
1744
|
+
// 3 Buffering
|
1745
|
+
// 5 Video cued
|
1746
|
+
switch (event.data) {
|
1747
|
+
case 0:
|
1748
|
+
plyr.media.paused = true;
|
1749
|
+
_triggerEvent(plyr.media, 'ended');
|
1750
|
+
break;
|
1751
|
+
|
1752
|
+
case 1:
|
1753
|
+
plyr.media.paused = false;
|
1754
|
+
|
1755
|
+
// If we were seeking, fire seeked event
|
1756
|
+
if (plyr.media.seeking) {
|
1757
|
+
_triggerEvent(plyr.media, 'seeked');
|
1758
|
+
}
|
1759
|
+
|
1760
|
+
plyr.media.seeking = false;
|
1761
|
+
_triggerEvent(plyr.media, 'play');
|
1762
|
+
_triggerEvent(plyr.media, 'playing');
|
1763
|
+
|
1764
|
+
// Poll to get playback progress
|
1765
|
+
timers.playing = window.setInterval(function() {
|
1766
|
+
// Set the current time
|
1767
|
+
plyr.media.currentTime = instance.getCurrentTime();
|
1768
|
+
|
1769
|
+
// Trigger timeupdate
|
1770
|
+
_triggerEvent(plyr.media, 'timeupdate');
|
1771
|
+
}, 100);
|
1772
|
+
|
1773
|
+
// Check duration again due to YouTube bug
|
1774
|
+
// https://github.com/Selz/plyr/issues/374
|
1775
|
+
// https://code.google.com/p/gdata-issues/issues/detail?id=8690
|
1776
|
+
if (plyr.media.duration !== instance.getDuration()) {
|
1777
|
+
plyr.media.duration = instance.getDuration();
|
1778
|
+
_triggerEvent(plyr.media, 'durationchange');
|
1779
|
+
}
|
1780
|
+
|
1781
|
+
break;
|
1782
|
+
|
1783
|
+
case 2:
|
1784
|
+
plyr.media.paused = true;
|
1785
|
+
_triggerEvent(plyr.media, 'pause');
|
1786
|
+
break;
|
1787
|
+
}
|
1788
|
+
|
1789
|
+
_triggerEvent(plyr.container, 'statechange', false, {
|
1790
|
+
code: event.data
|
1791
|
+
});
|
1792
|
+
}
|
1793
|
+
}
|
1794
|
+
});
|
1795
|
+
}
|
1796
|
+
|
1797
|
+
// Vimeo ready
|
1798
|
+
function _vimeoReady(mediaId, container) {
|
1799
|
+
// Setup instance
|
1800
|
+
// https://github.com/vimeo/player.js
|
1801
|
+
plyr.embed = new window.Vimeo.Player(container, {
|
1802
|
+
id: parseInt(mediaId),
|
1803
|
+
loop: config.loop,
|
1804
|
+
autoplay: config.autoplay,
|
1805
|
+
byline: false,
|
1806
|
+
portrait: false,
|
1807
|
+
title: false
|
1808
|
+
});
|
1809
|
+
|
1810
|
+
// Create a faux HTML5 API using the Vimeo API
|
1811
|
+
plyr.media.play = function() {
|
1812
|
+
plyr.embed.play();
|
1813
|
+
plyr.media.paused = false;
|
1814
|
+
};
|
1815
|
+
plyr.media.pause = function() {
|
1816
|
+
plyr.embed.pause();
|
1817
|
+
plyr.media.paused = true;
|
1818
|
+
};
|
1819
|
+
plyr.media.stop = function() {
|
1820
|
+
plyr.embed.stop();
|
1821
|
+
plyr.media.paused = true;
|
1822
|
+
};
|
1823
|
+
|
1824
|
+
plyr.media.paused = true;
|
1825
|
+
plyr.media.currentTime = 0;
|
1826
|
+
|
1827
|
+
// Update UI
|
1828
|
+
_embedReady();
|
1829
|
+
|
1830
|
+
plyr.embed.getCurrentTime().then(function(value) {
|
1831
|
+
plyr.media.currentTime = value;
|
1832
|
+
|
1833
|
+
// Trigger timeupdate
|
1834
|
+
_triggerEvent(plyr.media, 'timeupdate');
|
1835
|
+
});
|
1836
|
+
|
1837
|
+
plyr.embed.getDuration().then(function(value) {
|
1838
|
+
plyr.media.duration = value;
|
1839
|
+
|
1840
|
+
// Trigger timeupdate
|
1841
|
+
_triggerEvent(plyr.media, 'durationchange');
|
1842
|
+
});
|
1843
|
+
|
1844
|
+
// TODO: Captions
|
1845
|
+
/*if (config.captions.defaultActive) {
|
1846
|
+
plyr.embed.enableTextTrack('en');
|
1847
|
+
}*/
|
1848
|
+
|
1849
|
+
plyr.embed.on('loaded', function() {
|
1850
|
+
// Fix keyboard focus issues
|
1851
|
+
// https://github.com/Selz/plyr/issues/317
|
1852
|
+
if (_is.htmlElement(plyr.embed.element) && plyr.supported.full) {
|
1853
|
+
plyr.embed.element.setAttribute('tabindex', '-1');
|
1854
|
+
}
|
1855
|
+
});
|
1856
|
+
|
1857
|
+
plyr.embed.on('play', function() {
|
1858
|
+
plyr.media.paused = false;
|
1859
|
+
_triggerEvent(plyr.media, 'play');
|
1860
|
+
_triggerEvent(plyr.media, 'playing');
|
1861
|
+
});
|
1862
|
+
|
1863
|
+
plyr.embed.on('pause', function() {
|
1864
|
+
plyr.media.paused = true;
|
1865
|
+
_triggerEvent(plyr.media, 'pause');
|
1866
|
+
});
|
1867
|
+
|
1868
|
+
plyr.embed.on('timeupdate', function(data) {
|
1869
|
+
plyr.media.seeking = false;
|
1870
|
+
plyr.media.currentTime = data.seconds;
|
1871
|
+
_triggerEvent(plyr.media, 'timeupdate');
|
1872
|
+
});
|
1873
|
+
|
1874
|
+
plyr.embed.on('progress', function(data) {
|
1875
|
+
plyr.media.buffered = data.percent;
|
1876
|
+
_triggerEvent(plyr.media, 'progress');
|
1877
|
+
|
1878
|
+
if (parseInt(data.percent) === 1) {
|
1879
|
+
// Trigger event
|
1880
|
+
_triggerEvent(plyr.media, 'canplaythrough');
|
1881
|
+
}
|
1882
|
+
});
|
1883
|
+
|
1884
|
+
plyr.embed.on('seeked', function() {
|
1885
|
+
plyr.media.seeking = false;
|
1886
|
+
_triggerEvent(plyr.media, 'seeked');
|
1887
|
+
_triggerEvent(plyr.media, 'play');
|
1888
|
+
});
|
1889
|
+
|
1890
|
+
plyr.embed.on('ended', function() {
|
1891
|
+
plyr.media.paused = true;
|
1892
|
+
_triggerEvent(plyr.media, 'ended');
|
1893
|
+
});
|
1894
|
+
}
|
1895
|
+
|
1896
|
+
// Soundcloud ready
|
1897
|
+
function _soundcloudReady() {
|
1898
|
+
/* jshint validthis: true */
|
1899
|
+
plyr.embed = window.SC.Widget(this);
|
1900
|
+
|
1901
|
+
// Setup on ready
|
1902
|
+
plyr.embed.bind(window.SC.Widget.Events.READY, function() {
|
1903
|
+
// Create a faux HTML5 API using the Soundcloud API
|
1904
|
+
plyr.media.play = function() {
|
1905
|
+
plyr.embed.play();
|
1906
|
+
plyr.media.paused = false;
|
1907
|
+
};
|
1908
|
+
plyr.media.pause = function() {
|
1909
|
+
plyr.embed.pause();
|
1910
|
+
plyr.media.paused = true;
|
1911
|
+
};
|
1912
|
+
plyr.media.stop = function() {
|
1913
|
+
plyr.embed.seekTo(0);
|
1914
|
+
plyr.embed.pause();
|
1915
|
+
plyr.media.paused = true;
|
1916
|
+
};
|
1917
|
+
|
1918
|
+
plyr.media.paused = true;
|
1919
|
+
plyr.media.currentTime = 0;
|
1920
|
+
|
1921
|
+
plyr.embed.getDuration(function(value) {
|
1922
|
+
plyr.media.duration = value/1000;
|
1923
|
+
|
1924
|
+
// Update UI
|
1925
|
+
_embedReady();
|
1926
|
+
});
|
1927
|
+
|
1928
|
+
plyr.embed.getPosition(function(value) {
|
1929
|
+
plyr.media.currentTime = value;
|
1930
|
+
|
1931
|
+
// Trigger timeupdate
|
1932
|
+
_triggerEvent(plyr.media, 'timeupdate');
|
1933
|
+
});
|
1934
|
+
|
1935
|
+
plyr.embed.bind(window.SC.Widget.Events.PLAY, function() {
|
1936
|
+
plyr.media.paused = false;
|
1937
|
+
_triggerEvent(plyr.media, 'play');
|
1938
|
+
_triggerEvent(plyr.media, 'playing');
|
1939
|
+
});
|
1940
|
+
|
1941
|
+
plyr.embed.bind(window.SC.Widget.Events.PAUSE, function() {
|
1942
|
+
plyr.media.paused = true;
|
1943
|
+
_triggerEvent(plyr.media, 'pause');
|
1944
|
+
});
|
1945
|
+
|
1946
|
+
plyr.embed.bind(window.SC.Widget.Events.PLAY_PROGRESS, function(data) {
|
1947
|
+
plyr.media.seeking = false;
|
1948
|
+
plyr.media.currentTime = data.currentPosition/1000;
|
1949
|
+
_triggerEvent(plyr.media, 'timeupdate');
|
1950
|
+
});
|
1951
|
+
|
1952
|
+
plyr.embed.bind(window.SC.Widget.Events.LOAD_PROGRESS, function(data) {
|
1953
|
+
plyr.media.buffered = data.loadProgress;
|
1954
|
+
_triggerEvent(plyr.media, 'progress');
|
1955
|
+
|
1956
|
+
if (parseInt(data.loadProgress) === 1) {
|
1957
|
+
// Trigger event
|
1958
|
+
_triggerEvent(plyr.media, 'canplaythrough');
|
1959
|
+
}
|
1960
|
+
});
|
1961
|
+
|
1962
|
+
plyr.embed.bind(window.SC.Widget.Events.FINISH, function() {
|
1963
|
+
plyr.media.paused = true;
|
1964
|
+
_triggerEvent(plyr.media, 'ended');
|
1965
|
+
});
|
1966
|
+
});
|
1967
|
+
}
|
1968
|
+
|
1969
|
+
// Play media
|
1970
|
+
function _play() {
|
1971
|
+
if ('play' in plyr.media) {
|
1972
|
+
plyr.media.play();
|
1973
|
+
}
|
1974
|
+
}
|
1975
|
+
|
1976
|
+
// Pause media
|
1977
|
+
function _pause() {
|
1978
|
+
if ('pause' in plyr.media) {
|
1979
|
+
plyr.media.pause();
|
1980
|
+
}
|
1981
|
+
}
|
1982
|
+
|
1983
|
+
// Toggle playback
|
1984
|
+
function _togglePlay(toggle) {
|
1985
|
+
// True toggle
|
1986
|
+
if (!_is.boolean(toggle)) {
|
1987
|
+
toggle = plyr.media.paused;
|
1988
|
+
}
|
1989
|
+
|
1990
|
+
if (toggle) {
|
1991
|
+
_play();
|
1992
|
+
} else {
|
1993
|
+
_pause();
|
1994
|
+
}
|
1995
|
+
|
1996
|
+
return toggle;
|
1997
|
+
}
|
1998
|
+
|
1999
|
+
// Rewind
|
2000
|
+
function _rewind(seekTime) {
|
2001
|
+
// Use default if needed
|
2002
|
+
if (!_is.number(seekTime)) {
|
2003
|
+
seekTime = config.seekTime;
|
2004
|
+
}
|
2005
|
+
_seek(plyr.media.currentTime - seekTime);
|
2006
|
+
}
|
2007
|
+
|
2008
|
+
// Fast forward
|
2009
|
+
function _forward(seekTime) {
|
2010
|
+
// Use default if needed
|
2011
|
+
if (!_is.number(seekTime)) {
|
2012
|
+
seekTime = config.seekTime;
|
2013
|
+
}
|
2014
|
+
_seek(plyr.media.currentTime + seekTime);
|
2015
|
+
}
|
2016
|
+
|
2017
|
+
// Seek to time
|
2018
|
+
// The input parameter can be an event or a number
|
2019
|
+
function _seek(input) {
|
2020
|
+
var targetTime = 0,
|
2021
|
+
paused = plyr.media.paused,
|
2022
|
+
duration = _getDuration();
|
2023
|
+
|
2024
|
+
if (_is.number(input)) {
|
2025
|
+
targetTime = input;
|
2026
|
+
} else if (_is.object(input) && _inArray(['input', 'change'], input.type)) {
|
2027
|
+
// It's the seek slider
|
2028
|
+
// Seek to the selected time
|
2029
|
+
targetTime = ((input.target.value / input.target.max) * duration);
|
2030
|
+
}
|
2031
|
+
|
2032
|
+
// Normalise targetTime
|
2033
|
+
if (targetTime < 0) {
|
2034
|
+
targetTime = 0;
|
2035
|
+
} else if (targetTime > duration) {
|
2036
|
+
targetTime = duration;
|
2037
|
+
}
|
2038
|
+
|
2039
|
+
// Update seek range and progress
|
2040
|
+
_updateSeekDisplay(targetTime);
|
2041
|
+
|
2042
|
+
// Set the current time
|
2043
|
+
// Try/catch incase the media isn't set and we're calling seek() from source() and IE moans
|
2044
|
+
try {
|
2045
|
+
plyr.media.currentTime = targetTime.toFixed(4);
|
2046
|
+
}
|
2047
|
+
catch(e) {}
|
2048
|
+
|
2049
|
+
// Embeds
|
2050
|
+
if (_inArray(config.types.embed, plyr.type)) {
|
2051
|
+
switch(plyr.type) {
|
2052
|
+
case 'youtube':
|
2053
|
+
plyr.embed.seekTo(targetTime);
|
2054
|
+
break;
|
2055
|
+
|
2056
|
+
case 'vimeo':
|
2057
|
+
// Round to nearest second for vimeo
|
2058
|
+
plyr.embed.setCurrentTime(targetTime.toFixed(0));
|
2059
|
+
break;
|
2060
|
+
|
2061
|
+
case 'soundcloud':
|
2062
|
+
plyr.embed.seekTo(targetTime * 1000);
|
2063
|
+
break;
|
2064
|
+
}
|
2065
|
+
|
2066
|
+
if (paused) {
|
2067
|
+
_pause();
|
2068
|
+
}
|
2069
|
+
|
2070
|
+
// Trigger timeupdate
|
2071
|
+
_triggerEvent(plyr.media, 'timeupdate');
|
2072
|
+
|
2073
|
+
// Set seeking flag
|
2074
|
+
plyr.media.seeking = true;
|
2075
|
+
|
2076
|
+
// Trigger seeking
|
2077
|
+
_triggerEvent(plyr.media, 'seeking');
|
2078
|
+
}
|
2079
|
+
|
2080
|
+
// Logging
|
2081
|
+
_log('Seeking to ' + plyr.media.currentTime + ' seconds');
|
2082
|
+
|
2083
|
+
// Special handling for 'manual' captions
|
2084
|
+
_seekManualCaptions(targetTime);
|
2085
|
+
}
|
2086
|
+
|
2087
|
+
// Get the duration (or custom if set)
|
2088
|
+
function _getDuration() {
|
2089
|
+
// It should be a number, but parse it just incase
|
2090
|
+
var duration = parseInt(config.duration),
|
2091
|
+
|
2092
|
+
// True duration
|
2093
|
+
mediaDuration = 0;
|
2094
|
+
|
2095
|
+
// Only if duration available
|
2096
|
+
if (plyr.media.duration !== null && !isNaN(plyr.media.duration)) {
|
2097
|
+
mediaDuration = plyr.media.duration;
|
2098
|
+
}
|
2099
|
+
|
2100
|
+
// If custom duration is funky, use regular duration
|
2101
|
+
return (isNaN(duration) ? mediaDuration : duration);
|
2102
|
+
}
|
2103
|
+
|
2104
|
+
// Check playing state
|
2105
|
+
function _checkPlaying() {
|
2106
|
+
_toggleClass(plyr.container, config.classes.playing, !plyr.media.paused);
|
2107
|
+
|
2108
|
+
_toggleClass(plyr.container, config.classes.stopped, plyr.media.paused);
|
2109
|
+
|
2110
|
+
_toggleControls(plyr.media.paused);
|
2111
|
+
}
|
2112
|
+
|
2113
|
+
// Save scroll position
|
2114
|
+
function _saveScrollPosition() {
|
2115
|
+
scroll = {
|
2116
|
+
x: window.pageXOffset || 0,
|
2117
|
+
y: window.pageYOffset || 0
|
2118
|
+
};
|
2119
|
+
}
|
2120
|
+
|
2121
|
+
// Restore scroll position
|
2122
|
+
function _restoreScrollPosition() {
|
2123
|
+
window.scrollTo(scroll.x, scroll.y);
|
2124
|
+
}
|
2125
|
+
|
2126
|
+
// Toggle fullscreen
|
2127
|
+
function _toggleFullscreen(event) {
|
2128
|
+
// Check for native support
|
2129
|
+
var nativeSupport = fullscreen.supportsFullScreen;
|
2130
|
+
|
2131
|
+
if (nativeSupport) {
|
2132
|
+
// If it's a fullscreen change event, update the UI
|
2133
|
+
if (event && event.type === fullscreen.fullScreenEventName) {
|
2134
|
+
plyr.isFullscreen = fullscreen.isFullScreen(plyr.container);
|
2135
|
+
} else {
|
2136
|
+
// Else it's a user request to enter or exit
|
2137
|
+
if (!fullscreen.isFullScreen(plyr.container)) {
|
2138
|
+
// Save scroll position
|
2139
|
+
_saveScrollPosition();
|
2140
|
+
|
2141
|
+
// Request full screen
|
2142
|
+
fullscreen.requestFullScreen(plyr.container);
|
2143
|
+
} else {
|
2144
|
+
// Bail from fullscreen
|
2145
|
+
fullscreen.cancelFullScreen();
|
2146
|
+
}
|
2147
|
+
|
2148
|
+
// Check if we're actually full screen (it could fail)
|
2149
|
+
plyr.isFullscreen = fullscreen.isFullScreen(plyr.container);
|
2150
|
+
|
2151
|
+
return;
|
2152
|
+
}
|
2153
|
+
} else {
|
2154
|
+
// Otherwise, it's a simple toggle
|
2155
|
+
plyr.isFullscreen = !plyr.isFullscreen;
|
2156
|
+
|
2157
|
+
// Bind/unbind escape key
|
2158
|
+
document.body.style.overflow = plyr.isFullscreen ? 'hidden' : '';
|
2159
|
+
}
|
2160
|
+
|
2161
|
+
// Set class hook
|
2162
|
+
_toggleClass(plyr.container, config.classes.fullscreen.active, plyr.isFullscreen);
|
2163
|
+
|
2164
|
+
// Trap focus
|
2165
|
+
_focusTrap(plyr.isFullscreen);
|
2166
|
+
|
2167
|
+
// Set button state
|
2168
|
+
if (plyr.buttons && plyr.buttons.fullscreen) {
|
2169
|
+
_toggleState(plyr.buttons.fullscreen, plyr.isFullscreen);
|
2170
|
+
}
|
2171
|
+
|
2172
|
+
// Trigger an event
|
2173
|
+
_triggerEvent(plyr.container, plyr.isFullscreen ? 'enterfullscreen' : 'exitfullscreen', true);
|
2174
|
+
|
2175
|
+
// Restore scroll position
|
2176
|
+
if (!plyr.isFullscreen && nativeSupport) {
|
2177
|
+
_restoreScrollPosition();
|
2178
|
+
}
|
2179
|
+
}
|
2180
|
+
|
2181
|
+
// Mute
|
2182
|
+
function _toggleMute(muted) {
|
2183
|
+
// If the method is called without parameter, toggle based on current value
|
2184
|
+
if (!_is.boolean(muted)) {
|
2185
|
+
muted = !plyr.media.muted;
|
2186
|
+
}
|
2187
|
+
|
2188
|
+
// Set button state
|
2189
|
+
_toggleState(plyr.buttons.mute, muted);
|
2190
|
+
|
2191
|
+
// Set mute on the player
|
2192
|
+
plyr.media.muted = muted;
|
2193
|
+
|
2194
|
+
// If volume is 0 after unmuting, set to default
|
2195
|
+
if (plyr.media.volume === 0) {
|
2196
|
+
_setVolume(config.volume);
|
2197
|
+
}
|
2198
|
+
|
2199
|
+
// Embeds
|
2200
|
+
if (_inArray(config.types.embed, plyr.type)) {
|
2201
|
+
// YouTube
|
2202
|
+
switch(plyr.type) {
|
2203
|
+
case 'youtube':
|
2204
|
+
plyr.embed[plyr.media.muted ? 'mute' : 'unMute']();
|
2205
|
+
break;
|
2206
|
+
|
2207
|
+
case 'vimeo':
|
2208
|
+
case 'soundcloud':
|
2209
|
+
plyr.embed.setVolume(plyr.media.muted ? 0 : parseFloat(config.volume / config.volumeMax));
|
2210
|
+
break;
|
2211
|
+
}
|
2212
|
+
|
2213
|
+
// Trigger volumechange for embeds
|
2214
|
+
_triggerEvent(plyr.media, 'volumechange');
|
2215
|
+
}
|
2216
|
+
}
|
2217
|
+
|
2218
|
+
// Set volume
|
2219
|
+
function _setVolume(volume) {
|
2220
|
+
var max = config.volumeMax,
|
2221
|
+
min = config.volumeMin;
|
2222
|
+
|
2223
|
+
// Load volume from storage if no value specified
|
2224
|
+
if (_is.undefined(volume)) {
|
2225
|
+
volume = plyr.storage.volume;
|
2226
|
+
}
|
2227
|
+
|
2228
|
+
// Use config if all else fails
|
2229
|
+
if (volume === null || isNaN(volume)) {
|
2230
|
+
volume = config.volume;
|
2231
|
+
}
|
2232
|
+
|
2233
|
+
// Maximum is volumeMax
|
2234
|
+
if (volume > max) {
|
2235
|
+
volume = max;
|
2236
|
+
}
|
2237
|
+
// Minimum is volumeMin
|
2238
|
+
if (volume < min) {
|
2239
|
+
volume = min;
|
2240
|
+
}
|
2241
|
+
|
2242
|
+
// Set the player volume
|
2243
|
+
plyr.media.volume = parseFloat(volume / max);
|
2244
|
+
|
2245
|
+
// Set the display
|
2246
|
+
if (plyr.volume.display) {
|
2247
|
+
plyr.volume.display.value = volume;
|
2248
|
+
}
|
2249
|
+
|
2250
|
+
// Embeds
|
2251
|
+
if (_inArray(config.types.embed, plyr.type)) {
|
2252
|
+
switch(plyr.type) {
|
2253
|
+
case 'youtube':
|
2254
|
+
plyr.embed.setVolume(plyr.media.volume * 100);
|
2255
|
+
break;
|
2256
|
+
|
2257
|
+
case 'vimeo':
|
2258
|
+
case 'soundcloud':
|
2259
|
+
plyr.embed.setVolume(plyr.media.volume);
|
2260
|
+
break;
|
2261
|
+
}
|
2262
|
+
|
2263
|
+
// Trigger volumechange for embeds
|
2264
|
+
_triggerEvent(plyr.media, 'volumechange');
|
2265
|
+
}
|
2266
|
+
|
2267
|
+
// Toggle muted state
|
2268
|
+
if (volume === 0) {
|
2269
|
+
plyr.media.muted = true;
|
2270
|
+
} else if (plyr.media.muted && volume > 0) {
|
2271
|
+
_toggleMute();
|
2272
|
+
}
|
2273
|
+
}
|
2274
|
+
|
2275
|
+
// Increase volume
|
2276
|
+
function _increaseVolume(step) {
|
2277
|
+
var volume = plyr.media.muted ? 0 : (plyr.media.volume * config.volumeMax);
|
2278
|
+
|
2279
|
+
if (!_is.number(step)) {
|
2280
|
+
step = config.volumeStep;
|
2281
|
+
}
|
2282
|
+
|
2283
|
+
_setVolume(volume + step);
|
2284
|
+
}
|
2285
|
+
|
2286
|
+
// Decrease volume
|
2287
|
+
function _decreaseVolume(step) {
|
2288
|
+
var volume = plyr.media.muted ? 0 : (plyr.media.volume * config.volumeMax);
|
2289
|
+
|
2290
|
+
if (!_is.number(step)) {
|
2291
|
+
step = config.volumeStep;
|
2292
|
+
}
|
2293
|
+
|
2294
|
+
_setVolume(volume - step);
|
2295
|
+
}
|
2296
|
+
|
2297
|
+
// Update volume UI and storage
|
2298
|
+
function _updateVolume() {
|
2299
|
+
// Get the current volume
|
2300
|
+
var volume = plyr.media.muted ? 0 : (plyr.media.volume * config.volumeMax);
|
2301
|
+
|
2302
|
+
// Update the <input type="range"> if present
|
2303
|
+
if (plyr.supported.full) {
|
2304
|
+
if (plyr.volume.input) {
|
2305
|
+
plyr.volume.input.value = volume;
|
2306
|
+
}
|
2307
|
+
if (plyr.volume.display) {
|
2308
|
+
plyr.volume.display.value = volume;
|
2309
|
+
}
|
2310
|
+
}
|
2311
|
+
|
2312
|
+
// Update the volume in storage
|
2313
|
+
_updateStorage({volume: volume});
|
2314
|
+
|
2315
|
+
// Toggle class if muted
|
2316
|
+
_toggleClass(plyr.container, config.classes.muted, (volume === 0));
|
2317
|
+
|
2318
|
+
// Update checkbox for mute state
|
2319
|
+
if (plyr.supported.full && plyr.buttons.mute) {
|
2320
|
+
_toggleState(plyr.buttons.mute, (volume === 0));
|
2321
|
+
}
|
2322
|
+
}
|
2323
|
+
|
2324
|
+
// Toggle captions
|
2325
|
+
function _toggleCaptions(show) {
|
2326
|
+
// If there's no full support, or there's no caption toggle
|
2327
|
+
if (!plyr.supported.full || !plyr.buttons.captions) {
|
2328
|
+
return;
|
2329
|
+
}
|
2330
|
+
|
2331
|
+
// If the method is called without parameter, toggle based on current value
|
2332
|
+
if (!_is.boolean(show)) {
|
2333
|
+
show = (plyr.container.className.indexOf(config.classes.captions.active) === -1);
|
2334
|
+
}
|
2335
|
+
|
2336
|
+
// Set global
|
2337
|
+
plyr.captionsEnabled = show;
|
2338
|
+
|
2339
|
+
// Toggle state
|
2340
|
+
_toggleState(plyr.buttons.captions, plyr.captionsEnabled);
|
2341
|
+
|
2342
|
+
// Add class hook
|
2343
|
+
_toggleClass(plyr.container, config.classes.captions.active, plyr.captionsEnabled);
|
2344
|
+
|
2345
|
+
// Trigger an event
|
2346
|
+
_triggerEvent(plyr.container, plyr.captionsEnabled ? 'captionsenabled' : 'captionsdisabled', true);
|
2347
|
+
|
2348
|
+
// Save captions state to localStorage
|
2349
|
+
_updateStorage({captionsEnabled: plyr.captionsEnabled});
|
2350
|
+
}
|
2351
|
+
|
2352
|
+
// Check if media is loading
|
2353
|
+
function _checkLoading(event) {
|
2354
|
+
var loading = (event.type === 'waiting');
|
2355
|
+
|
2356
|
+
// Clear timer
|
2357
|
+
clearTimeout(timers.loading);
|
2358
|
+
|
2359
|
+
// Timer to prevent flicker when seeking
|
2360
|
+
timers.loading = setTimeout(function() {
|
2361
|
+
// Toggle container class hook
|
2362
|
+
_toggleClass(plyr.container, config.classes.loading, loading);
|
2363
|
+
|
2364
|
+
// Show controls if loading, hide if done
|
2365
|
+
_toggleControls(loading);
|
2366
|
+
}, (loading ? 250 : 0));
|
2367
|
+
}
|
2368
|
+
|
2369
|
+
// Update <progress> elements
|
2370
|
+
function _updateProgress(event) {
|
2371
|
+
if (!plyr.supported.full) {
|
2372
|
+
return;
|
2373
|
+
}
|
2374
|
+
|
2375
|
+
var progress = plyr.progress.played,
|
2376
|
+
value = 0,
|
2377
|
+
duration = _getDuration();
|
2378
|
+
|
2379
|
+
if (event) {
|
2380
|
+
switch (event.type) {
|
2381
|
+
// Video playing
|
2382
|
+
case 'timeupdate':
|
2383
|
+
case 'seeking':
|
2384
|
+
if (plyr.controls.pressed) {
|
2385
|
+
return;
|
2386
|
+
}
|
2387
|
+
|
2388
|
+
value = _getPercentage(plyr.media.currentTime, duration);
|
2389
|
+
|
2390
|
+
// Set seek range value only if it's a 'natural' time event
|
2391
|
+
if (event.type === 'timeupdate' && plyr.buttons.seek) {
|
2392
|
+
plyr.buttons.seek.value = value;
|
2393
|
+
}
|
2394
|
+
|
2395
|
+
break;
|
2396
|
+
|
2397
|
+
// Check buffer status
|
2398
|
+
case 'playing':
|
2399
|
+
case 'progress':
|
2400
|
+
progress = plyr.progress.buffer;
|
2401
|
+
value = (function() {
|
2402
|
+
var buffered = plyr.media.buffered;
|
2403
|
+
|
2404
|
+
if (buffered && buffered.length) {
|
2405
|
+
// HTML5
|
2406
|
+
return _getPercentage(buffered.end(0), duration);
|
2407
|
+
} else if (_is.number(buffered)) {
|
2408
|
+
// YouTube returns between 0 and 1
|
2409
|
+
return (buffered * 100);
|
2410
|
+
}
|
2411
|
+
|
2412
|
+
return 0;
|
2413
|
+
})();
|
2414
|
+
|
2415
|
+
break;
|
2416
|
+
}
|
2417
|
+
}
|
2418
|
+
|
2419
|
+
// Set values
|
2420
|
+
_setProgress(progress, value);
|
2421
|
+
}
|
2422
|
+
|
2423
|
+
// Set <progress> value
|
2424
|
+
function _setProgress(progress, value) {
|
2425
|
+
if (!plyr.supported.full) {
|
2426
|
+
return;
|
2427
|
+
}
|
2428
|
+
|
2429
|
+
// Default to 0
|
2430
|
+
if (_is.undefined(value)) {
|
2431
|
+
value = 0;
|
2432
|
+
}
|
2433
|
+
// Default to buffer or bail
|
2434
|
+
if (_is.undefined(progress)) {
|
2435
|
+
if (plyr.progress && plyr.progress.buffer) {
|
2436
|
+
progress = plyr.progress.buffer;
|
2437
|
+
} else {
|
2438
|
+
return;
|
2439
|
+
}
|
2440
|
+
}
|
2441
|
+
|
2442
|
+
// One progress element passed
|
2443
|
+
if (_is.htmlElement(progress)) {
|
2444
|
+
progress.value = value;
|
2445
|
+
} else if (progress) {
|
2446
|
+
// Object of progress + text element
|
2447
|
+
if (progress.bar) {
|
2448
|
+
progress.bar.value = value;
|
2449
|
+
}
|
2450
|
+
if (progress.text) {
|
2451
|
+
progress.text.innerHTML = value;
|
2452
|
+
}
|
2453
|
+
}
|
2454
|
+
}
|
2455
|
+
|
2456
|
+
// Update the displayed time
|
2457
|
+
function _updateTimeDisplay(time, element) {
|
2458
|
+
// Bail if there's no duration display
|
2459
|
+
if (!element) {
|
2460
|
+
return;
|
2461
|
+
}
|
2462
|
+
|
2463
|
+
// Fallback to 0
|
2464
|
+
if (isNaN(time)) {
|
2465
|
+
time = 0;
|
2466
|
+
}
|
2467
|
+
|
2468
|
+
plyr.secs = parseInt(time % 60);
|
2469
|
+
plyr.mins = parseInt((time / 60) % 60);
|
2470
|
+
plyr.hours = parseInt(((time / 60) / 60) % 60);
|
2471
|
+
|
2472
|
+
// Do we need to display hours?
|
2473
|
+
var displayHours = (parseInt(((_getDuration() / 60) / 60) % 60) > 0);
|
2474
|
+
|
2475
|
+
// Ensure it's two digits. For example, 03 rather than 3.
|
2476
|
+
plyr.secs = ('0' + plyr.secs).slice(-2);
|
2477
|
+
plyr.mins = ('0' + plyr.mins).slice(-2);
|
2478
|
+
|
2479
|
+
// Render
|
2480
|
+
element.innerHTML = (displayHours ? plyr.hours + ':' : '') + plyr.mins + ':' + plyr.secs;
|
2481
|
+
}
|
2482
|
+
|
2483
|
+
// Show the duration on metadataloaded
|
2484
|
+
function _displayDuration() {
|
2485
|
+
if (!plyr.supported.full) {
|
2486
|
+
return;
|
2487
|
+
}
|
2488
|
+
|
2489
|
+
// Determine duration
|
2490
|
+
var duration = _getDuration() || 0;
|
2491
|
+
|
2492
|
+
// If there's only one time display, display duration there
|
2493
|
+
if (!plyr.duration && config.displayDuration && plyr.media.paused) {
|
2494
|
+
_updateTimeDisplay(duration, plyr.currentTime);
|
2495
|
+
}
|
2496
|
+
|
2497
|
+
// If there's a duration element, update content
|
2498
|
+
if (plyr.duration) {
|
2499
|
+
_updateTimeDisplay(duration, plyr.duration);
|
2500
|
+
}
|
2501
|
+
|
2502
|
+
// Update the tooltip (if visible)
|
2503
|
+
_updateSeekTooltip();
|
2504
|
+
}
|
2505
|
+
|
2506
|
+
// Handle time change event
|
2507
|
+
function _timeUpdate(event) {
|
2508
|
+
// Duration
|
2509
|
+
_updateTimeDisplay(plyr.media.currentTime, plyr.currentTime);
|
2510
|
+
|
2511
|
+
// Ignore updates while seeking
|
2512
|
+
if (event && event.type === 'timeupdate' && plyr.media.seeking) {
|
2513
|
+
return;
|
2514
|
+
}
|
2515
|
+
|
2516
|
+
// Playing progress
|
2517
|
+
_updateProgress(event);
|
2518
|
+
}
|
2519
|
+
|
2520
|
+
// Update seek range and progress
|
2521
|
+
function _updateSeekDisplay(time) {
|
2522
|
+
// Default to 0
|
2523
|
+
if (!_is.number(time)) {
|
2524
|
+
time = 0;
|
2525
|
+
}
|
2526
|
+
|
2527
|
+
var duration = _getDuration(),
|
2528
|
+
value = _getPercentage(time, duration);
|
2529
|
+
|
2530
|
+
// Update progress
|
2531
|
+
if (plyr.progress && plyr.progress.played) {
|
2532
|
+
plyr.progress.played.value = value;
|
2533
|
+
}
|
2534
|
+
|
2535
|
+
// Update seek range input
|
2536
|
+
if (plyr.buttons && plyr.buttons.seek) {
|
2537
|
+
plyr.buttons.seek.value = value;
|
2538
|
+
}
|
2539
|
+
}
|
2540
|
+
|
2541
|
+
// Update hover tooltip for seeking
|
2542
|
+
function _updateSeekTooltip(event) {
|
2543
|
+
var duration = _getDuration();
|
2544
|
+
|
2545
|
+
// Bail if setting not true
|
2546
|
+
if (!config.tooltips.seek || !plyr.progress.container || duration === 0) {
|
2547
|
+
return;
|
2548
|
+
}
|
2549
|
+
|
2550
|
+
// Calculate percentage
|
2551
|
+
var clientRect = plyr.progress.container.getBoundingClientRect(),
|
2552
|
+
percent = 0,
|
2553
|
+
visible = config.classes.tooltip + '--visible';
|
2554
|
+
|
2555
|
+
// Determine percentage, if already visible
|
2556
|
+
if (!event) {
|
2557
|
+
if (_hasClass(plyr.progress.tooltip, visible)) {
|
2558
|
+
percent = plyr.progress.tooltip.style.left.replace('%', '');
|
2559
|
+
} else {
|
2560
|
+
return;
|
2561
|
+
}
|
2562
|
+
} else {
|
2563
|
+
percent = ((100 / clientRect.width) * (event.pageX - clientRect.left));
|
2564
|
+
}
|
2565
|
+
|
2566
|
+
// Set bounds
|
2567
|
+
if (percent < 0) {
|
2568
|
+
percent = 0;
|
2569
|
+
} else if (percent > 100) {
|
2570
|
+
percent = 100;
|
2571
|
+
}
|
2572
|
+
|
2573
|
+
// Display the time a click would seek to
|
2574
|
+
_updateTimeDisplay(((duration / 100) * percent), plyr.progress.tooltip);
|
2575
|
+
|
2576
|
+
// Set position
|
2577
|
+
plyr.progress.tooltip.style.left = percent + "%";
|
2578
|
+
|
2579
|
+
// Show/hide the tooltip
|
2580
|
+
// If the event is a moues in/out and percentage is inside bounds
|
2581
|
+
if (event && _inArray(['mouseenter', 'mouseleave'], event.type)) {
|
2582
|
+
_toggleClass(plyr.progress.tooltip, visible, (event.type === 'mouseenter'));
|
2583
|
+
}
|
2584
|
+
}
|
2585
|
+
|
2586
|
+
// Show the player controls in fullscreen mode
|
2587
|
+
function _toggleControls(toggle) {
|
2588
|
+
// Don't hide if config says not to, it's audio, or not ready or loading
|
2589
|
+
if (!config.hideControls || plyr.type === 'audio') {
|
2590
|
+
return;
|
2591
|
+
}
|
2592
|
+
|
2593
|
+
var delay = 0,
|
2594
|
+
isEnterFullscreen = false,
|
2595
|
+
show = toggle,
|
2596
|
+
loading = _hasClass(plyr.container, config.classes.loading);
|
2597
|
+
|
2598
|
+
// Default to false if no boolean
|
2599
|
+
if (!_is.boolean(toggle)) {
|
2600
|
+
if (toggle && toggle.type) {
|
2601
|
+
// Is the enter fullscreen event
|
2602
|
+
isEnterFullscreen = (toggle.type === 'enterfullscreen');
|
2603
|
+
|
2604
|
+
// Whether to show controls
|
2605
|
+
show = _inArray(['mousemove', 'touchstart', 'mouseenter', 'focus'], toggle.type);
|
2606
|
+
|
2607
|
+
// Delay hiding on move events
|
2608
|
+
if (_inArray(['mousemove', 'touchmove'], toggle.type)) {
|
2609
|
+
delay = 2000;
|
2610
|
+
}
|
2611
|
+
|
2612
|
+
// Delay a little more for keyboard users
|
2613
|
+
if (toggle.type === 'focus') {
|
2614
|
+
delay = 3000;
|
2615
|
+
}
|
2616
|
+
} else {
|
2617
|
+
show = _hasClass(plyr.container, config.classes.hideControls);
|
2618
|
+
}
|
2619
|
+
}
|
2620
|
+
|
2621
|
+
// Clear timer every movement
|
2622
|
+
window.clearTimeout(timers.hover);
|
2623
|
+
|
2624
|
+
// If the mouse is not over the controls, set a timeout to hide them
|
2625
|
+
if (show || plyr.media.paused || loading) {
|
2626
|
+
_toggleClass(plyr.container, config.classes.hideControls, false);
|
2627
|
+
|
2628
|
+
// Always show controls when paused or if touch
|
2629
|
+
if (plyr.media.paused || loading) {
|
2630
|
+
return;
|
2631
|
+
}
|
2632
|
+
|
2633
|
+
// Delay for hiding on touch
|
2634
|
+
if (plyr.browser.isTouch) {
|
2635
|
+
delay = 3000;
|
2636
|
+
}
|
2637
|
+
}
|
2638
|
+
|
2639
|
+
// If toggle is false or if we're playing (regardless of toggle),
|
2640
|
+
// then set the timer to hide the controls
|
2641
|
+
if (!show || !plyr.media.paused) {
|
2642
|
+
timers.hover = window.setTimeout(function() {
|
2643
|
+
// If the mouse is over the controls (and not entering fullscreen), bail
|
2644
|
+
if ((plyr.controls.pressed || plyr.controls.hover) && !isEnterFullscreen) {
|
2645
|
+
return;
|
2646
|
+
}
|
2647
|
+
|
2648
|
+
_toggleClass(plyr.container, config.classes.hideControls, true);
|
2649
|
+
}, delay);
|
2650
|
+
}
|
2651
|
+
}
|
2652
|
+
|
2653
|
+
// Add common function to retrieve media source
|
2654
|
+
function _source(source) {
|
2655
|
+
// If not null or undefined, parse it
|
2656
|
+
if (!_is.undefined(source)) {
|
2657
|
+
_updateSource(source);
|
2658
|
+
return;
|
2659
|
+
}
|
2660
|
+
|
2661
|
+
// Return the current source
|
2662
|
+
var url;
|
2663
|
+
switch(plyr.type) {
|
2664
|
+
case 'youtube':
|
2665
|
+
url = plyr.embed.getVideoUrl();
|
2666
|
+
break;
|
2667
|
+
|
2668
|
+
case 'vimeo':
|
2669
|
+
plyr.embed.getVideoUrl.then(function (value) {
|
2670
|
+
url = value;
|
2671
|
+
});
|
2672
|
+
break;
|
2673
|
+
|
2674
|
+
case 'soundcloud':
|
2675
|
+
plyr.embed.getCurrentSound(function(object) {
|
2676
|
+
url = object.permalink_url;
|
2677
|
+
});
|
2678
|
+
break;
|
2679
|
+
|
2680
|
+
default:
|
2681
|
+
url = plyr.media.currentSrc;
|
2682
|
+
break;
|
2683
|
+
}
|
2684
|
+
|
2685
|
+
return url || '';
|
2686
|
+
}
|
2687
|
+
|
2688
|
+
// Update source
|
2689
|
+
// Sources are not checked for support so be careful
|
2690
|
+
function _updateSource(source) {
|
2691
|
+
if (!_is.object(source) || !('sources' in source) || !source.sources.length) {
|
2692
|
+
_warn('Invalid source format');
|
2693
|
+
return;
|
2694
|
+
}
|
2695
|
+
|
2696
|
+
// Remove ready class hook
|
2697
|
+
_toggleClass(plyr.container, config.classes.ready, false);
|
2698
|
+
|
2699
|
+
// Pause playback
|
2700
|
+
_pause();
|
2701
|
+
|
2702
|
+
// Update seek range and progress
|
2703
|
+
_updateSeekDisplay();
|
2704
|
+
|
2705
|
+
// Reset buffer progress
|
2706
|
+
_setProgress();
|
2707
|
+
|
2708
|
+
// Cancel current network requests
|
2709
|
+
_cancelRequests();
|
2710
|
+
|
2711
|
+
// Setup new source
|
2712
|
+
function setup() {
|
2713
|
+
// Remove embed object
|
2714
|
+
plyr.embed = null;
|
2715
|
+
|
2716
|
+
// Remove the old media
|
2717
|
+
_remove(plyr.media);
|
2718
|
+
|
2719
|
+
// Remove video container
|
2720
|
+
if (plyr.type === 'video' && plyr.videoContainer) {
|
2721
|
+
_remove(plyr.videoContainer);
|
2722
|
+
}
|
2723
|
+
|
2724
|
+
// Reset class name
|
2725
|
+
if (plyr.container) {
|
2726
|
+
plyr.container.removeAttribute('class');
|
2727
|
+
}
|
2728
|
+
|
2729
|
+
// Set the type
|
2730
|
+
if ('type' in source) {
|
2731
|
+
plyr.type = source.type;
|
2732
|
+
|
2733
|
+
// Get child type for video (it might be an embed)
|
2734
|
+
if (plyr.type === 'video') {
|
2735
|
+
var firstSource = source.sources[0];
|
2736
|
+
|
2737
|
+
if ('type' in firstSource && _inArray(config.types.embed, firstSource.type)) {
|
2738
|
+
plyr.type = firstSource.type;
|
2739
|
+
}
|
2740
|
+
}
|
2741
|
+
}
|
2742
|
+
|
2743
|
+
// Check for support
|
2744
|
+
plyr.supported = supported(plyr.type);
|
2745
|
+
|
2746
|
+
// Create new markup
|
2747
|
+
switch(plyr.type) {
|
2748
|
+
case 'video':
|
2749
|
+
plyr.media = document.createElement('video');
|
2750
|
+
break;
|
2751
|
+
|
2752
|
+
case 'audio':
|
2753
|
+
plyr.media = document.createElement('audio');
|
2754
|
+
break;
|
2755
|
+
|
2756
|
+
case 'youtube':
|
2757
|
+
case 'vimeo':
|
2758
|
+
case 'soundcloud':
|
2759
|
+
plyr.media = document.createElement('div');
|
2760
|
+
plyr.embedId = source.sources[0].src;
|
2761
|
+
break;
|
2762
|
+
}
|
2763
|
+
|
2764
|
+
// Inject the new element
|
2765
|
+
_prependChild(plyr.container, plyr.media);
|
2766
|
+
|
2767
|
+
// Autoplay the new source?
|
2768
|
+
if (_is.boolean(source.autoplay)) {
|
2769
|
+
config.autoplay = source.autoplay;
|
2770
|
+
}
|
2771
|
+
|
2772
|
+
// Set attributes for audio and video
|
2773
|
+
if (_inArray(config.types.html5, plyr.type)) {
|
2774
|
+
if (config.crossorigin) {
|
2775
|
+
plyr.media.setAttribute('crossorigin', '');
|
2776
|
+
}
|
2777
|
+
if (config.autoplay) {
|
2778
|
+
plyr.media.setAttribute('autoplay', '');
|
2779
|
+
}
|
2780
|
+
if ('poster' in source) {
|
2781
|
+
plyr.media.setAttribute('poster', source.poster);
|
2782
|
+
}
|
2783
|
+
if (config.loop) {
|
2784
|
+
plyr.media.setAttribute('loop', '');
|
2785
|
+
}
|
2786
|
+
}
|
2787
|
+
|
2788
|
+
// Restore class hooks
|
2789
|
+
_toggleClass(plyr.container, config.classes.fullscreen.active, plyr.isFullscreen);
|
2790
|
+
_toggleClass(plyr.container, config.classes.captions.active, plyr.captionsEnabled);
|
2791
|
+
_toggleStyleHook();
|
2792
|
+
|
2793
|
+
// Set new sources for html5
|
2794
|
+
if (_inArray(config.types.html5, plyr.type)) {
|
2795
|
+
_insertChildElements('source', source.sources);
|
2796
|
+
}
|
2797
|
+
|
2798
|
+
// Set up from scratch
|
2799
|
+
_setupMedia();
|
2800
|
+
|
2801
|
+
// HTML5 stuff
|
2802
|
+
if (_inArray(config.types.html5, plyr.type)) {
|
2803
|
+
// Setup captions
|
2804
|
+
if ('tracks' in source) {
|
2805
|
+
_insertChildElements('track', source.tracks);
|
2806
|
+
}
|
2807
|
+
|
2808
|
+
// Load HTML5 sources
|
2809
|
+
plyr.media.load();
|
2810
|
+
}
|
2811
|
+
|
2812
|
+
// If HTML5 or embed but not fully supported, setupInterface and call ready now
|
2813
|
+
if (_inArray(config.types.html5, plyr.type) || (_inArray(config.types.embed, plyr.type) && !plyr.supported.full)) {
|
2814
|
+
// Setup interface
|
2815
|
+
_setupInterface();
|
2816
|
+
|
2817
|
+
// Call ready
|
2818
|
+
_ready();
|
2819
|
+
}
|
2820
|
+
|
2821
|
+
// Set aria title and iframe title
|
2822
|
+
config.title = source.title;
|
2823
|
+
_setTitle();
|
2824
|
+
}
|
2825
|
+
|
2826
|
+
// Destroy instance adn wait for callback
|
2827
|
+
// Vimeo throws a wobbly if you don't wait
|
2828
|
+
_destroy(setup, false);
|
2829
|
+
}
|
2830
|
+
|
2831
|
+
// Update poster
|
2832
|
+
function _updatePoster(source) {
|
2833
|
+
if (plyr.type === 'video') {
|
2834
|
+
plyr.media.setAttribute('poster', source);
|
2835
|
+
}
|
2836
|
+
}
|
2837
|
+
|
2838
|
+
// Listen for control events
|
2839
|
+
function _controlListeners() {
|
2840
|
+
// IE doesn't support input event, so we fallback to change
|
2841
|
+
var inputEvent = (plyr.browser.isIE ? 'change' : 'input');
|
2842
|
+
|
2843
|
+
// Click play/pause helper
|
2844
|
+
function togglePlay() {
|
2845
|
+
var play = _togglePlay();
|
2846
|
+
|
2847
|
+
// Determine which buttons
|
2848
|
+
var trigger = plyr.buttons[play ? 'play' : 'pause'],
|
2849
|
+
target = plyr.buttons[play ? 'pause' : 'play'];
|
2850
|
+
|
2851
|
+
// Get the last play button to account for the large play button
|
2852
|
+
if (target && target.length > 1) {
|
2853
|
+
target = target[target.length - 1];
|
2854
|
+
} else {
|
2855
|
+
target = target[0];
|
2856
|
+
}
|
2857
|
+
|
2858
|
+
// Setup focus and tab focus
|
2859
|
+
if (target) {
|
2860
|
+
var hadTabFocus = _hasClass(trigger, config.classes.tabFocus);
|
2861
|
+
|
2862
|
+
setTimeout(function() {
|
2863
|
+
target.focus();
|
2864
|
+
|
2865
|
+
if (hadTabFocus) {
|
2866
|
+
_toggleClass(trigger, config.classes.tabFocus, false);
|
2867
|
+
_toggleClass(target, config.classes.tabFocus, true);
|
2868
|
+
}
|
2869
|
+
}, 100);
|
2870
|
+
}
|
2871
|
+
}
|
2872
|
+
|
2873
|
+
// Get the focused element
|
2874
|
+
function getFocusElement() {
|
2875
|
+
var focused = document.activeElement;
|
2876
|
+
|
2877
|
+
if (!focused || focused === document.body) {
|
2878
|
+
focused = null;
|
2879
|
+
} else {
|
2880
|
+
focused = document.querySelector(':focus');
|
2881
|
+
}
|
2882
|
+
|
2883
|
+
return focused;
|
2884
|
+
}
|
2885
|
+
|
2886
|
+
// Get the key code for an event
|
2887
|
+
function getKeyCode(event) {
|
2888
|
+
return event.keyCode ? event.keyCode : event.which;
|
2889
|
+
}
|
2890
|
+
|
2891
|
+
// Detect tab focus
|
2892
|
+
function checkTabFocus(focused) {
|
2893
|
+
for (var button in plyr.buttons) {
|
2894
|
+
var element = plyr.buttons[button];
|
2895
|
+
|
2896
|
+
if (_is.nodeList(element)) {
|
2897
|
+
for (var i = 0; i < element.length; i++) {
|
2898
|
+
_toggleClass(element[i], config.classes.tabFocus, (element[i] === focused));
|
2899
|
+
}
|
2900
|
+
} else {
|
2901
|
+
_toggleClass(element, config.classes.tabFocus, (element === focused));
|
2902
|
+
}
|
2903
|
+
}
|
2904
|
+
}
|
2905
|
+
|
2906
|
+
// Keyboard shortcuts
|
2907
|
+
if (config.keyboardShorcuts.focused) {
|
2908
|
+
var last = null;
|
2909
|
+
|
2910
|
+
// Handle global presses
|
2911
|
+
if (config.keyboardShorcuts.global) {
|
2912
|
+
_on(window, 'keydown keyup', function(event) {
|
2913
|
+
var code = getKeyCode(event),
|
2914
|
+
focused = getFocusElement(),
|
2915
|
+
allowed = [48,49,50,51,52,53,54,56,57,75,77,70,67],
|
2916
|
+
count = get().length;
|
2917
|
+
|
2918
|
+
// Only handle global key press if there's only one player
|
2919
|
+
// and the key is in the allowed keys
|
2920
|
+
// and if the focused element is not editable (e.g. text input)
|
2921
|
+
// and any that accept key input http://webaim.org/techniques/keyboard/
|
2922
|
+
if (count === 1 && _inArray(allowed, code) && (!_is.htmlElement(focused) || !_matches(focused, config.selectors.editable))) {
|
2923
|
+
handleKey(event);
|
2924
|
+
}
|
2925
|
+
});
|
2926
|
+
}
|
2927
|
+
|
2928
|
+
// Handle presses on focused
|
2929
|
+
_on(plyr.container, 'keydown keyup', handleKey);
|
2930
|
+
}
|
2931
|
+
|
2932
|
+
function handleKey(event) {
|
2933
|
+
var code = getKeyCode(event),
|
2934
|
+
pressed = event.type === 'keydown',
|
2935
|
+
held = pressed && code === last;
|
2936
|
+
|
2937
|
+
// If the event is bubbled from the media element
|
2938
|
+
// Firefox doesn't get the keycode for whatever reason
|
2939
|
+
if (!_is.number(code)) {
|
2940
|
+
return;
|
2941
|
+
}
|
2942
|
+
|
2943
|
+
// Seek by the number keys
|
2944
|
+
function seekByKey() {
|
2945
|
+
// Get current duration
|
2946
|
+
var duration = plyr.media.duration;
|
2947
|
+
|
2948
|
+
// Bail if we have no duration set
|
2949
|
+
if (!_is.number(duration)) {
|
2950
|
+
return;
|
2951
|
+
}
|
2952
|
+
|
2953
|
+
// Divide the max duration into 10th's and times by the number value
|
2954
|
+
_seek((duration / 10) * (code - 48));
|
2955
|
+
}
|
2956
|
+
|
2957
|
+
// Handle the key on keydown
|
2958
|
+
// Reset on keyup
|
2959
|
+
if (pressed) {
|
2960
|
+
// Which keycodes should we prevent default
|
2961
|
+
var preventDefault = [48,49,50,51,52,53,54,56,57,32,75,38,40,77,39,37,70,67];
|
2962
|
+
|
2963
|
+
// If the code is found prevent default (e.g. prevent scrolling for arrows)
|
2964
|
+
if (_inArray(preventDefault, code)) {
|
2965
|
+
event.preventDefault();
|
2966
|
+
event.stopPropagation();
|
2967
|
+
}
|
2968
|
+
|
2969
|
+
switch(code) {
|
2970
|
+
// 0-9
|
2971
|
+
case 48:
|
2972
|
+
case 49:
|
2973
|
+
case 50:
|
2974
|
+
case 51:
|
2975
|
+
case 52:
|
2976
|
+
case 53:
|
2977
|
+
case 54:
|
2978
|
+
case 55:
|
2979
|
+
case 56:
|
2980
|
+
case 57: if (!held) { seekByKey(); } break;
|
2981
|
+
// Space and K key
|
2982
|
+
case 32:
|
2983
|
+
case 75: if (!held) { _togglePlay(); } break;
|
2984
|
+
// Arrow up
|
2985
|
+
case 38: _increaseVolume(); break;
|
2986
|
+
// Arrow down
|
2987
|
+
case 40: _decreaseVolume(); break;
|
2988
|
+
// M key
|
2989
|
+
case 77: if (!held) { _toggleMute() } break;
|
2990
|
+
// Arrow forward
|
2991
|
+
case 39: _forward(); break;
|
2992
|
+
// Arrow back
|
2993
|
+
case 37: _rewind(); break;
|
2994
|
+
// F key
|
2995
|
+
case 70: _toggleFullscreen(); break;
|
2996
|
+
// C key
|
2997
|
+
case 67: if (!held) { _toggleCaptions(); } break;
|
2998
|
+
}
|
2999
|
+
|
3000
|
+
// Escape is handle natively when in full screen
|
3001
|
+
// So we only need to worry about non native
|
3002
|
+
if (!fullscreen.supportsFullScreen && plyr.isFullscreen && code === 27) {
|
3003
|
+
_toggleFullscreen();
|
3004
|
+
}
|
3005
|
+
|
3006
|
+
// Store last code for next cycle
|
3007
|
+
last = code;
|
3008
|
+
} else {
|
3009
|
+
last = null;
|
3010
|
+
}
|
3011
|
+
}
|
3012
|
+
|
3013
|
+
// Focus/tab management
|
3014
|
+
_on(window, 'keyup', function(event) {
|
3015
|
+
var code = getKeyCode(event),
|
3016
|
+
focused = getFocusElement();
|
3017
|
+
|
3018
|
+
if (code === 9) {
|
3019
|
+
checkTabFocus(focused);
|
3020
|
+
}
|
3021
|
+
});
|
3022
|
+
_on(document.body, 'click', function() {
|
3023
|
+
_toggleClass(_getElement('.' + config.classes.tabFocus), config.classes.tabFocus, false);
|
3024
|
+
});
|
3025
|
+
for (var button in plyr.buttons) {
|
3026
|
+
var element = plyr.buttons[button];
|
3027
|
+
|
3028
|
+
_on(element, 'blur', function() {
|
3029
|
+
_toggleClass(element, 'tab-focus', false);
|
3030
|
+
});
|
3031
|
+
}
|
3032
|
+
|
3033
|
+
// Play
|
3034
|
+
_proxyListener(plyr.buttons.play, 'click', config.listeners.play, togglePlay);
|
3035
|
+
|
3036
|
+
// Pause
|
3037
|
+
_proxyListener(plyr.buttons.pause, 'click', config.listeners.pause, togglePlay);
|
3038
|
+
|
3039
|
+
// Restart
|
3040
|
+
_proxyListener(plyr.buttons.restart, 'click', config.listeners.restart, _seek);
|
3041
|
+
|
3042
|
+
// Rewind
|
3043
|
+
_proxyListener(plyr.buttons.rewind, 'click', config.listeners.rewind, _rewind);
|
3044
|
+
|
3045
|
+
// Fast forward
|
3046
|
+
_proxyListener(plyr.buttons.forward, 'click', config.listeners.forward, _forward);
|
3047
|
+
|
3048
|
+
// Seek
|
3049
|
+
_proxyListener(plyr.buttons.seek, inputEvent, config.listeners.seek, _seek);
|
3050
|
+
|
3051
|
+
// Set volume
|
3052
|
+
_proxyListener(plyr.volume.input, inputEvent, config.listeners.volume, function() {
|
3053
|
+
_setVolume(plyr.volume.input.value);
|
3054
|
+
});
|
3055
|
+
|
3056
|
+
// Mute
|
3057
|
+
_proxyListener(plyr.buttons.mute, 'click', config.listeners.mute, _toggleMute);
|
3058
|
+
|
3059
|
+
// Fullscreen
|
3060
|
+
_proxyListener(plyr.buttons.fullscreen, 'click', config.listeners.fullscreen, _toggleFullscreen);
|
3061
|
+
|
3062
|
+
// Handle user exiting fullscreen by escaping etc
|
3063
|
+
if (fullscreen.supportsFullScreen) {
|
3064
|
+
_on(document, fullscreen.fullScreenEventName, _toggleFullscreen);
|
3065
|
+
}
|
3066
|
+
|
3067
|
+
// Captions
|
3068
|
+
_on(plyr.buttons.captions, 'click', _toggleCaptions);
|
3069
|
+
|
3070
|
+
// Seek tooltip
|
3071
|
+
_on(plyr.progress.container, 'mouseenter mouseleave mousemove', _updateSeekTooltip);
|
3072
|
+
|
3073
|
+
// Toggle controls visibility based on mouse movement
|
3074
|
+
if (config.hideControls) {
|
3075
|
+
// Toggle controls on mouse events and entering fullscreen
|
3076
|
+
_on(plyr.container, 'mouseenter mouseleave mousemove touchstart touchend touchcancel touchmove enterfullscreen', _toggleControls);
|
3077
|
+
|
3078
|
+
// Watch for cursor over controls so they don't hide when trying to interact
|
3079
|
+
_on(plyr.controls, 'mouseenter mouseleave', function(event) {
|
3080
|
+
plyr.controls.hover = event.type === 'mouseenter';
|
3081
|
+
});
|
3082
|
+
|
3083
|
+
// Watch for cursor over controls so they don't hide when trying to interact
|
3084
|
+
_on(plyr.controls, 'mousedown mouseup touchstart touchend touchcancel', function(event) {
|
3085
|
+
plyr.controls.pressed = _inArray(['mousedown', 'touchstart'], event.type);
|
3086
|
+
});
|
3087
|
+
|
3088
|
+
// Focus in/out on controls
|
3089
|
+
_on(plyr.controls, 'focus blur', _toggleControls, true);
|
3090
|
+
}
|
3091
|
+
|
3092
|
+
// Adjust volume on scroll
|
3093
|
+
_on(plyr.volume.input, 'wheel', function(event) {
|
3094
|
+
event.preventDefault();
|
3095
|
+
|
3096
|
+
// Detect "natural" scroll - suppored on OS X Safari only
|
3097
|
+
// Other browsers on OS X will be inverted until support improves
|
3098
|
+
var inverted = event.webkitDirectionInvertedFromDevice,
|
3099
|
+
step = (config.volumeStep / 5);
|
3100
|
+
|
3101
|
+
// Scroll down (or up on natural) to decrease
|
3102
|
+
if (event.deltaY < 0 || event.deltaX > 0) {
|
3103
|
+
if (inverted) {
|
3104
|
+
_decreaseVolume(step);
|
3105
|
+
} else {
|
3106
|
+
_increaseVolume(step);
|
3107
|
+
}
|
3108
|
+
}
|
3109
|
+
|
3110
|
+
// Scroll up (or down on natural) to increase
|
3111
|
+
if (event.deltaY > 0 || event.deltaX < 0) {
|
3112
|
+
if (inverted) {
|
3113
|
+
_increaseVolume(step);
|
3114
|
+
} else {
|
3115
|
+
_decreaseVolume(step);
|
3116
|
+
}
|
3117
|
+
}
|
3118
|
+
});
|
3119
|
+
}
|
3120
|
+
|
3121
|
+
// Listen for media events
|
3122
|
+
function _mediaListeners() {
|
3123
|
+
// Time change on media
|
3124
|
+
_on(plyr.media, 'timeupdate seeking', _timeUpdate);
|
3125
|
+
|
3126
|
+
// Update manual captions
|
3127
|
+
_on(plyr.media, 'timeupdate', _seekManualCaptions);
|
3128
|
+
|
3129
|
+
// Display duration
|
3130
|
+
_on(plyr.media, 'durationchange loadedmetadata', _displayDuration);
|
3131
|
+
|
3132
|
+
// Handle the media finishing
|
3133
|
+
_on(plyr.media, 'ended', function() {
|
3134
|
+
// Show poster on end
|
3135
|
+
if (plyr.type === 'video' && config.showPosterOnEnd) {
|
3136
|
+
// Clear
|
3137
|
+
if (plyr.type === 'video') {
|
3138
|
+
_setCaption();
|
3139
|
+
}
|
3140
|
+
|
3141
|
+
// Restart
|
3142
|
+
_seek();
|
3143
|
+
|
3144
|
+
// Re-load media
|
3145
|
+
plyr.media.load();
|
3146
|
+
}
|
3147
|
+
});
|
3148
|
+
|
3149
|
+
// Check for buffer progress
|
3150
|
+
_on(plyr.media, 'progress playing', _updateProgress);
|
3151
|
+
|
3152
|
+
// Handle native mute
|
3153
|
+
_on(plyr.media, 'volumechange', _updateVolume);
|
3154
|
+
|
3155
|
+
// Handle native play/pause
|
3156
|
+
_on(plyr.media, 'play pause ended', _checkPlaying);
|
3157
|
+
|
3158
|
+
// Loading
|
3159
|
+
_on(plyr.media, 'waiting canplay seeked', _checkLoading);
|
3160
|
+
|
3161
|
+
// Click video
|
3162
|
+
if (config.clickToPlay && plyr.type !== 'audio') {
|
3163
|
+
// Re-fetch the wrapper
|
3164
|
+
var wrapper = _getElement('.' + config.classes.videoWrapper);
|
3165
|
+
|
3166
|
+
// Bail if there's no wrapper (this should never happen)
|
3167
|
+
if (!wrapper) {
|
3168
|
+
return;
|
3169
|
+
}
|
3170
|
+
|
3171
|
+
// Set cursor
|
3172
|
+
wrapper.style.cursor = "pointer";
|
3173
|
+
|
3174
|
+
// On click play, pause ore restart
|
3175
|
+
_on(wrapper, 'click', function() {
|
3176
|
+
// Touch devices will just show controls (if we're hiding controls)
|
3177
|
+
if (config.hideControls && plyr.browser.isTouch && !plyr.media.paused) {
|
3178
|
+
return;
|
3179
|
+
}
|
3180
|
+
|
3181
|
+
if (plyr.media.paused) {
|
3182
|
+
_play();
|
3183
|
+
} else if (plyr.media.ended) {
|
3184
|
+
_seek();
|
3185
|
+
_play();
|
3186
|
+
} else {
|
3187
|
+
_pause();
|
3188
|
+
}
|
3189
|
+
});
|
3190
|
+
}
|
3191
|
+
|
3192
|
+
// Disable right click
|
3193
|
+
if (config.disableContextMenu) {
|
3194
|
+
_on(plyr.media, 'contextmenu', function(event) { event.preventDefault(); });
|
3195
|
+
}
|
3196
|
+
|
3197
|
+
// Proxy events to container
|
3198
|
+
// Bubble up key events for Edge
|
3199
|
+
_on(plyr.media, config.events.concat(['keyup', 'keydown']).join(' '), function(event) {
|
3200
|
+
_triggerEvent(plyr.container, event.type, true);
|
3201
|
+
});
|
3202
|
+
}
|
3203
|
+
|
3204
|
+
// Cancel current network requests
|
3205
|
+
// See https://github.com/Selz/plyr/issues/174
|
3206
|
+
function _cancelRequests() {
|
3207
|
+
if (!_inArray(config.types.html5, plyr.type)) {
|
3208
|
+
return;
|
3209
|
+
}
|
3210
|
+
|
3211
|
+
// Remove child sources
|
3212
|
+
var sources = plyr.media.querySelectorAll('source');
|
3213
|
+
for (var i = 0; i < sources.length; i++) {
|
3214
|
+
_remove(sources[i]);
|
3215
|
+
}
|
3216
|
+
|
3217
|
+
// Set blank video src attribute
|
3218
|
+
// This is to prevent a MEDIA_ERR_SRC_NOT_SUPPORTED error
|
3219
|
+
// Info: http://stackoverflow.com/questions/32231579/how-to-properly-dispose-of-an-html5-video-and-close-socket-or-connection
|
3220
|
+
plyr.media.setAttribute('src', 'https://cdn.selz.com/plyr/blank.mp4');
|
3221
|
+
|
3222
|
+
// Load the new empty source
|
3223
|
+
// This will cancel existing requests
|
3224
|
+
// See https://github.com/Selz/plyr/issues/174
|
3225
|
+
plyr.media.load();
|
3226
|
+
|
3227
|
+
// Debugging
|
3228
|
+
_log('Cancelled network requests');
|
3229
|
+
}
|
3230
|
+
|
3231
|
+
// Destroy an instance
|
3232
|
+
// Event listeners are removed when elements are removed
|
3233
|
+
// http://stackoverflow.com/questions/12528049/if-a-dom-element-is-removed-are-its-listeners-also-removed-from-memory
|
3234
|
+
function _destroy(callback, restore) {
|
3235
|
+
// Bail if the element is not initialized
|
3236
|
+
if (!plyr.init) {
|
3237
|
+
return null;
|
3238
|
+
}
|
3239
|
+
|
3240
|
+
// Type specific stuff
|
3241
|
+
switch (plyr.type) {
|
3242
|
+
case 'youtube':
|
3243
|
+
// Clear timers
|
3244
|
+
window.clearInterval(timers.buffering);
|
3245
|
+
window.clearInterval(timers.playing);
|
3246
|
+
|
3247
|
+
// Destroy YouTube API
|
3248
|
+
plyr.embed.destroy();
|
3249
|
+
|
3250
|
+
// Clean up
|
3251
|
+
cleanUp();
|
3252
|
+
|
3253
|
+
break;
|
3254
|
+
|
3255
|
+
case 'vimeo':
|
3256
|
+
// Destroy Vimeo API
|
3257
|
+
// then clean up (wait, to prevent postmessage errors)
|
3258
|
+
plyr.embed.unload().then(cleanUp);
|
3259
|
+
|
3260
|
+
// Vimeo does not always return
|
3261
|
+
timers.cleanUp = window.setTimeout(cleanUp, 200);
|
3262
|
+
|
3263
|
+
break;
|
3264
|
+
|
3265
|
+
case 'video':
|
3266
|
+
case 'audio':
|
3267
|
+
// Restore native video controls
|
3268
|
+
_toggleNativeControls(true);
|
3269
|
+
|
3270
|
+
// Clean up
|
3271
|
+
cleanUp();
|
3272
|
+
|
3273
|
+
break;
|
3274
|
+
}
|
3275
|
+
|
3276
|
+
function cleanUp() {
|
3277
|
+
clearTimeout(timers.cleanUp);
|
3278
|
+
|
3279
|
+
// Default to restore original element
|
3280
|
+
if (!_is.boolean(restore)) {
|
3281
|
+
restore = true;
|
3282
|
+
}
|
3283
|
+
|
3284
|
+
// Callback
|
3285
|
+
if (_is.function(callback)) {
|
3286
|
+
callback.call(original);
|
3287
|
+
}
|
3288
|
+
|
3289
|
+
// Bail if we don't need to restore the original element
|
3290
|
+
if (!restore) {
|
3291
|
+
return;
|
3292
|
+
}
|
3293
|
+
|
3294
|
+
// Remove init flag
|
3295
|
+
plyr.init = false;
|
3296
|
+
|
3297
|
+
// Replace the container with the original element provided
|
3298
|
+
plyr.container.parentNode.replaceChild(original, plyr.container);
|
3299
|
+
|
3300
|
+
// Allow overflow (set on fullscreen)
|
3301
|
+
document.body.style.overflow = '';
|
3302
|
+
|
3303
|
+
// Event
|
3304
|
+
_triggerEvent(original, 'destroyed', true);
|
3305
|
+
}
|
3306
|
+
}
|
3307
|
+
|
3308
|
+
// Setup a player
|
3309
|
+
function _init() {
|
3310
|
+
// Bail if the element is initialized
|
3311
|
+
if (plyr.init) {
|
3312
|
+
return null;
|
3313
|
+
}
|
3314
|
+
|
3315
|
+
// Setup the fullscreen api
|
3316
|
+
fullscreen = _fullscreen();
|
3317
|
+
|
3318
|
+
// Sniff out the browser
|
3319
|
+
plyr.browser = _browserSniff();
|
3320
|
+
|
3321
|
+
// Bail if nothing to setup
|
3322
|
+
if (!_is.htmlElement(plyr.media)) {
|
3323
|
+
return;
|
3324
|
+
}
|
3325
|
+
|
3326
|
+
// Load saved settings from localStorage
|
3327
|
+
_setupStorage();
|
3328
|
+
|
3329
|
+
// Set media type based on tag or data attribute
|
3330
|
+
// Supported: video, audio, vimeo, youtube
|
3331
|
+
var tagName = media.tagName.toLowerCase();
|
3332
|
+
if (tagName === 'div') {
|
3333
|
+
plyr.type = media.getAttribute('data-type');
|
3334
|
+
plyr.embedId = media.getAttribute('data-video-id');
|
3335
|
+
|
3336
|
+
// Clean up
|
3337
|
+
media.removeAttribute('data-type');
|
3338
|
+
media.removeAttribute('data-video-id');
|
3339
|
+
} else {
|
3340
|
+
plyr.type = tagName;
|
3341
|
+
config.crossorigin = (media.getAttribute('crossorigin') !== null);
|
3342
|
+
config.autoplay = (config.autoplay || (media.getAttribute('autoplay') !== null));
|
3343
|
+
config.loop = (config.loop || (media.getAttribute('loop') !== null));
|
3344
|
+
}
|
3345
|
+
|
3346
|
+
// Check for support
|
3347
|
+
plyr.supported = supported(plyr.type);
|
3348
|
+
|
3349
|
+
// If no native support, bail
|
3350
|
+
if (!plyr.supported.basic) {
|
3351
|
+
return;
|
3352
|
+
}
|
3353
|
+
|
3354
|
+
// Wrap media
|
3355
|
+
plyr.container = _wrap(media, document.createElement('div'));
|
3356
|
+
|
3357
|
+
// Allow focus to be captured
|
3358
|
+
plyr.container.setAttribute('tabindex', 0);
|
3359
|
+
|
3360
|
+
// Add style hook
|
3361
|
+
_toggleStyleHook();
|
3362
|
+
|
3363
|
+
// Debug info
|
3364
|
+
_log('' + plyr.browser.name + ' ' + plyr.browser.version);
|
3365
|
+
|
3366
|
+
// Setup media
|
3367
|
+
_setupMedia();
|
3368
|
+
|
3369
|
+
// Setup interface
|
3370
|
+
// If embed but not fully supported, setupInterface (to avoid flash of controls) and call ready now
|
3371
|
+
if (_inArray(config.types.html5, plyr.type) || (_inArray(config.types.embed, plyr.type) && !plyr.supported.full)) {
|
3372
|
+
// Setup UI
|
3373
|
+
_setupInterface();
|
3374
|
+
|
3375
|
+
// Call ready
|
3376
|
+
_ready();
|
3377
|
+
|
3378
|
+
// Set title on button and frame
|
3379
|
+
_setTitle();
|
3380
|
+
}
|
3381
|
+
|
3382
|
+
// Successful setup
|
3383
|
+
plyr.init = true;
|
3384
|
+
}
|
3385
|
+
|
3386
|
+
// Setup the UI
|
3387
|
+
function _setupInterface() {
|
3388
|
+
// Don't setup interface if no support
|
3389
|
+
if (!plyr.supported.full) {
|
3390
|
+
_warn('Basic support only', plyr.type);
|
3391
|
+
|
3392
|
+
// Remove controls
|
3393
|
+
_remove(_getElement(config.selectors.controls.wrapper));
|
3394
|
+
|
3395
|
+
// Remove large play
|
3396
|
+
_remove(_getElement(config.selectors.buttons.play));
|
3397
|
+
|
3398
|
+
// Restore native controls
|
3399
|
+
_toggleNativeControls(true);
|
3400
|
+
|
3401
|
+
// Bail
|
3402
|
+
return;
|
3403
|
+
}
|
3404
|
+
|
3405
|
+
// Inject custom controls if not present
|
3406
|
+
var controlsMissing = !_getElements(config.selectors.controls.wrapper).length;
|
3407
|
+
if (controlsMissing) {
|
3408
|
+
// Inject custom controls
|
3409
|
+
_injectControls();
|
3410
|
+
}
|
3411
|
+
|
3412
|
+
// Find the elements
|
3413
|
+
if (!_findElements()) {
|
3414
|
+
return;
|
3415
|
+
}
|
3416
|
+
|
3417
|
+
// If the controls are injected, re-bind listeners for controls
|
3418
|
+
if (controlsMissing) {
|
3419
|
+
_controlListeners();
|
3420
|
+
}
|
3421
|
+
|
3422
|
+
// Media element listeners
|
3423
|
+
_mediaListeners();
|
3424
|
+
|
3425
|
+
// Remove native controls
|
3426
|
+
_toggleNativeControls();
|
3427
|
+
|
3428
|
+
// Setup fullscreen
|
3429
|
+
_setupFullscreen();
|
3430
|
+
|
3431
|
+
// Captions
|
3432
|
+
_setupCaptions();
|
3433
|
+
|
3434
|
+
// Set volume
|
3435
|
+
_setVolume();
|
3436
|
+
_updateVolume();
|
3437
|
+
|
3438
|
+
// Reset time display
|
3439
|
+
_timeUpdate();
|
3440
|
+
|
3441
|
+
// Update the UI
|
3442
|
+
_checkPlaying();
|
3443
|
+
}
|
3444
|
+
|
3445
|
+
api = {
|
3446
|
+
getOriginal: function() { return original; },
|
3447
|
+
getContainer: function() { return plyr.container },
|
3448
|
+
getEmbed: function() { return plyr.embed; },
|
3449
|
+
getMedia: function() { return plyr.media; },
|
3450
|
+
getType: function() { return plyr.type; },
|
3451
|
+
getDuration: _getDuration,
|
3452
|
+
getCurrentTime: function() { return plyr.media.currentTime; },
|
3453
|
+
getVolume: function() { return plyr.media.volume; },
|
3454
|
+
isMuted: function() { return plyr.media.muted; },
|
3455
|
+
isReady: function() { return _hasClass(plyr.container, config.classes.ready); },
|
3456
|
+
isLoading: function() { return _hasClass(plyr.container, config.classes.loading); },
|
3457
|
+
isPaused: function() { return plyr.media.paused; },
|
3458
|
+
on: function(event, callback) { _on(plyr.container, event, callback); return this; },
|
3459
|
+
play: _play,
|
3460
|
+
pause: _pause,
|
3461
|
+
stop: function() { _pause(); _seek(); },
|
3462
|
+
restart: _seek,
|
3463
|
+
rewind: _rewind,
|
3464
|
+
forward: _forward,
|
3465
|
+
seek: _seek,
|
3466
|
+
source: _source,
|
3467
|
+
poster: _updatePoster,
|
3468
|
+
setVolume: _setVolume,
|
3469
|
+
togglePlay: _togglePlay,
|
3470
|
+
toggleMute: _toggleMute,
|
3471
|
+
toggleCaptions: _toggleCaptions,
|
3472
|
+
toggleFullscreen: _toggleFullscreen,
|
3473
|
+
toggleControls: _toggleControls,
|
3474
|
+
isFullscreen: function() { return plyr.isFullscreen || false; },
|
3475
|
+
support: function(mimeType) { return _supportMime(plyr, mimeType); },
|
3476
|
+
destroy: _destroy
|
3477
|
+
};
|
3478
|
+
|
3479
|
+
// Everything done
|
3480
|
+
function _ready() {
|
3481
|
+
// Ready event at end of execution stack
|
3482
|
+
window.setTimeout(function() {
|
3483
|
+
_triggerEvent(plyr.media, 'ready');
|
3484
|
+
}, 0);
|
3485
|
+
|
3486
|
+
// Set class hook on media element
|
3487
|
+
_toggleClass(plyr.media, defaults.classes.setup, true);
|
3488
|
+
|
3489
|
+
// Set container class for ready
|
3490
|
+
_toggleClass(plyr.container, config.classes.ready, true);
|
3491
|
+
|
3492
|
+
// Store a refernce to instance
|
3493
|
+
plyr.media.plyr = api;
|
3494
|
+
|
3495
|
+
// Autoplay
|
3496
|
+
if (config.autoplay) {
|
3497
|
+
_play();
|
3498
|
+
}
|
3499
|
+
}
|
3500
|
+
|
3501
|
+
// Initialize instance
|
3502
|
+
_init();
|
3503
|
+
|
3504
|
+
// If init failed, return null
|
3505
|
+
if (!plyr.init) {
|
3506
|
+
return null;
|
3507
|
+
}
|
3508
|
+
|
3509
|
+
return api;
|
3510
|
+
}
|
3511
|
+
|
3512
|
+
// Load a sprite
|
3513
|
+
function loadSprite(url, id) {
|
3514
|
+
var x = new XMLHttpRequest();
|
3515
|
+
|
3516
|
+
// If the id is set and sprite exists, bail
|
3517
|
+
if (_is.string(id) && _is.htmlElement(document.querySelector('#' + id))) {
|
3518
|
+
return;
|
3519
|
+
}
|
3520
|
+
|
3521
|
+
// Create placeholder (to prevent loading twice)
|
3522
|
+
var container = document.createElement('div');
|
3523
|
+
container.setAttribute('hidden', '');
|
3524
|
+
if (_is.string(id)) {
|
3525
|
+
container.setAttribute('id', id);
|
3526
|
+
}
|
3527
|
+
document.body.insertBefore(container, document.body.childNodes[0]);
|
3528
|
+
|
3529
|
+
// Check for CORS support
|
3530
|
+
if ('withCredentials' in x) {
|
3531
|
+
x.open('GET', url, true);
|
3532
|
+
} else {
|
3533
|
+
return;
|
3534
|
+
}
|
3535
|
+
|
3536
|
+
// Inject hidden div with sprite on load
|
3537
|
+
x.onload = function() {
|
3538
|
+
container.innerHTML = x.responseText;
|
3539
|
+
}
|
3540
|
+
|
3541
|
+
x.send();
|
3542
|
+
}
|
3543
|
+
|
3544
|
+
// Check for support
|
3545
|
+
function supported(type) {
|
3546
|
+
var browser = _browserSniff(),
|
3547
|
+
isOldIE = (browser.isIE && browser.version <= 9),
|
3548
|
+
isIos = browser.isIos,
|
3549
|
+
isIphone = browser.isIphone,
|
3550
|
+
audioSupport = !!document.createElement('audio').canPlayType,
|
3551
|
+
videoSupport = !!document.createElement('video').canPlayType,
|
3552
|
+
basic = false,
|
3553
|
+
full = false;
|
3554
|
+
|
3555
|
+
switch (type) {
|
3556
|
+
case 'video':
|
3557
|
+
basic = videoSupport;
|
3558
|
+
full = (basic && (!isOldIE && !isIphone));
|
3559
|
+
break;
|
3560
|
+
|
3561
|
+
case 'audio':
|
3562
|
+
basic = audioSupport;
|
3563
|
+
full = (basic && !isOldIE);
|
3564
|
+
break;
|
3565
|
+
|
3566
|
+
// Vimeo does not seem to be supported on iOS via API
|
3567
|
+
// Issue raised https://github.com/vimeo/player.js/issues/87
|
3568
|
+
case 'vimeo':
|
3569
|
+
basic = true;
|
3570
|
+
full = (!isOldIE && !isIos);
|
3571
|
+
break;
|
3572
|
+
|
3573
|
+
case 'youtube':
|
3574
|
+
basic = true;
|
3575
|
+
full = (!isOldIE && !isIos);
|
3576
|
+
|
3577
|
+
// YouTube seems to work on iOS 10+ on iPad
|
3578
|
+
if (isIos && !isIphone && browser.version >= 10) {
|
3579
|
+
full = true;
|
3580
|
+
}
|
3581
|
+
|
3582
|
+
break;
|
3583
|
+
|
3584
|
+
case 'soundcloud':
|
3585
|
+
basic = true;
|
3586
|
+
full = (!isOldIE && !isIphone);
|
3587
|
+
break;
|
3588
|
+
|
3589
|
+
default:
|
3590
|
+
basic = (audioSupport && videoSupport);
|
3591
|
+
full = (basic && !isOldIE);
|
3592
|
+
}
|
3593
|
+
|
3594
|
+
return {
|
3595
|
+
basic: basic,
|
3596
|
+
full: full
|
3597
|
+
};
|
3598
|
+
}
|
3599
|
+
|
3600
|
+
// Setup function
|
3601
|
+
function setup(targets, options) {
|
3602
|
+
// Get the players
|
3603
|
+
var players = [],
|
3604
|
+
instances = [],
|
3605
|
+
selector = [defaults.selectors.html5, defaults.selectors.embed].join(',');
|
3606
|
+
|
3607
|
+
// Select the elements
|
3608
|
+
if (_is.string(targets)) {
|
3609
|
+
// String selector passed
|
3610
|
+
targets = document.querySelectorAll(targets);
|
3611
|
+
} else if (_is.htmlElement(targets)) {
|
3612
|
+
// Single HTMLElement passed
|
3613
|
+
targets = [targets];
|
3614
|
+
} else if (!_is.nodeList(targets) && !_is.array(targets) && !_is.string(targets)) {
|
3615
|
+
// No selector passed, possibly options as first argument
|
3616
|
+
// If options are the first argument
|
3617
|
+
if (_is.undefined(options) && _is.object(targets)) {
|
3618
|
+
options = targets;
|
3619
|
+
}
|
3620
|
+
|
3621
|
+
// Use default selector
|
3622
|
+
targets = document.querySelectorAll(selector);
|
3623
|
+
}
|
3624
|
+
|
3625
|
+
// Convert NodeList to array
|
3626
|
+
if (_is.nodeList(targets)) {
|
3627
|
+
targets = Array.prototype.slice.call(targets);
|
3628
|
+
}
|
3629
|
+
|
3630
|
+
// Bail if disabled or no basic support
|
3631
|
+
// You may want to disable certain UAs etc
|
3632
|
+
if (!supported().basic || !targets.length) {
|
3633
|
+
return false;
|
3634
|
+
}
|
3635
|
+
|
3636
|
+
// Add to container list
|
3637
|
+
function add(target, media) {
|
3638
|
+
if (!_hasClass(media, defaults.classes.hook)) {
|
3639
|
+
players.push({
|
3640
|
+
// Always wrap in a <div> for styling
|
3641
|
+
//container: _wrap(media, document.createElement('div')),
|
3642
|
+
// Could be a container or the media itself
|
3643
|
+
target: target,
|
3644
|
+
// This should be the <video>, <audio> or <div> (YouTube/Vimeo)
|
3645
|
+
media: media
|
3646
|
+
});
|
3647
|
+
}
|
3648
|
+
}
|
3649
|
+
|
3650
|
+
// Check if the targets have multiple media elements
|
3651
|
+
for (var i = 0; i < targets.length; i++) {
|
3652
|
+
var target = targets[i];
|
3653
|
+
|
3654
|
+
// Get children
|
3655
|
+
var children = target.querySelectorAll(selector);
|
3656
|
+
|
3657
|
+
// If there's more than one media element child, wrap them
|
3658
|
+
if (children.length) {
|
3659
|
+
for (var x = 0; x < children.length; x++) {
|
3660
|
+
add(target, children[x]);
|
3661
|
+
}
|
3662
|
+
} else if (_matches(target, selector)) {
|
3663
|
+
// Target is media element
|
3664
|
+
add(target, target);
|
3665
|
+
}
|
3666
|
+
}
|
3667
|
+
|
3668
|
+
// Create a player instance for each element
|
3669
|
+
players.forEach(function(player) {
|
3670
|
+
var element = player.target,
|
3671
|
+
media = player.media,
|
3672
|
+
match = false;
|
3673
|
+
|
3674
|
+
// The target element can also be the media element
|
3675
|
+
if (media === element) {
|
3676
|
+
match = true;
|
3677
|
+
}
|
3678
|
+
|
3679
|
+
// Setup a player instance and add to the element
|
3680
|
+
// Create instance-specific config
|
3681
|
+
var data = {};
|
3682
|
+
|
3683
|
+
// Try parsing data attribute config
|
3684
|
+
try { data = JSON.parse(element.getAttribute('data-plyr')); }
|
3685
|
+
catch(e) { }
|
3686
|
+
|
3687
|
+
var config = _extend({}, defaults, options, data);
|
3688
|
+
|
3689
|
+
// Bail if not enabled
|
3690
|
+
if (!config.enabled) {
|
3691
|
+
return null;
|
3692
|
+
}
|
3693
|
+
|
3694
|
+
// Create new instance
|
3695
|
+
var instance = new Plyr(media, config);
|
3696
|
+
|
3697
|
+
// Go to next if setup failed
|
3698
|
+
if (!_is.object(instance)) {
|
3699
|
+
return;
|
3700
|
+
}
|
3701
|
+
|
3702
|
+
// Listen for events if debugging
|
3703
|
+
if (config.debug) {
|
3704
|
+
var events = config.events.concat(['setup', 'statechange', 'enterfullscreen', 'exitfullscreen', 'captionsenabled', 'captionsdisabled']);
|
3705
|
+
|
3706
|
+
_on(instance.getContainer(), events.join(' '), function(event) {
|
3707
|
+
console.log([config.logPrefix, 'event:', event.type].join(' '), event.detail.plyr);
|
3708
|
+
});
|
3709
|
+
}
|
3710
|
+
|
3711
|
+
// Callback
|
3712
|
+
_event(instance.getContainer(), 'setup', true, {
|
3713
|
+
plyr: instance
|
3714
|
+
});
|
3715
|
+
|
3716
|
+
// Add to return array even if it's already setup
|
3717
|
+
instances.push(instance);
|
3718
|
+
});
|
3719
|
+
|
3720
|
+
return instances;
|
3721
|
+
}
|
3722
|
+
|
3723
|
+
// Get all instances within a provided container
|
3724
|
+
function get(container) {
|
3725
|
+
if (_is.string(container)) {
|
3726
|
+
// Get selector if string passed
|
3727
|
+
container = document.querySelector(container);
|
3728
|
+
} else if (_is.undefined(container)) {
|
3729
|
+
// Use body by default to get all on page
|
3730
|
+
container = document.body;
|
3731
|
+
}
|
3732
|
+
|
3733
|
+
// If we have a HTML element
|
3734
|
+
if (_is.htmlElement(container)) {
|
3735
|
+
var elements = container.querySelectorAll('.' + defaults.classes.setup),
|
3736
|
+
instances = [];
|
3737
|
+
|
3738
|
+
Array.prototype.slice.call(elements).forEach(function(element) {
|
3739
|
+
if (_is.object(element.plyr)) {
|
3740
|
+
instances.push(element.plyr);
|
3741
|
+
}
|
3742
|
+
});
|
3743
|
+
|
3744
|
+
return instances;
|
3745
|
+
}
|
3746
|
+
|
3747
|
+
return [];
|
3748
|
+
}
|
3749
|
+
|
3750
|
+
return {
|
3751
|
+
setup: setup,
|
3752
|
+
supported: supported,
|
3753
|
+
loadSprite: loadSprite,
|
3754
|
+
get: get
|
3755
|
+
};
|
3756
|
+
}));
|
3757
|
+
|
3758
|
+
// Custom event polyfill
|
3759
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent
|
3760
|
+
(function () {
|
3761
|
+
if (typeof window.CustomEvent === 'function') {
|
3762
|
+
return;
|
3763
|
+
}
|
3764
|
+
|
3765
|
+
function CustomEvent(event, params) {
|
3766
|
+
params = params || { bubbles: false, cancelable: false, detail: undefined };
|
3767
|
+
var evt = document.createEvent('CustomEvent');
|
3768
|
+
evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
|
3769
|
+
return evt;
|
3770
|
+
}
|
3771
|
+
|
3772
|
+
CustomEvent.prototype = window.Event.prototype;
|
3773
|
+
|
3774
|
+
window.CustomEvent = CustomEvent;
|
3775
|
+
})();
|