aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.jshintrc7
-rw-r--r--changelog.md29
-rw-r--r--dist/plyr.css2
-rw-r--r--dist/plyr.js2
-rw-r--r--dist/sprite.svg2
-rw-r--r--docs/dist/docs.css2
-rw-r--r--docs/dist/docs.js2
-rw-r--r--docs/dist/docs.svg1
-rw-r--r--docs/error.html4
-rw-r--r--docs/index.html118
-rw-r--r--docs/index.master.html130
-rw-r--r--docs/src/js/docs.js120
-rw-r--r--docs/src/less/components/buttons.less52
-rw-r--r--docs/src/less/components/examples.less82
-rw-r--r--docs/src/less/components/panels.less13
-rw-r--r--docs/src/less/components/type.less12
-rw-r--r--docs/src/less/docs.less1
-rwxr-xr-xdocs/src/sprite/icon-github.svg12
-rwxr-xr-xdocs/src/sprite/icon-twitter.svg11
-rwxr-xr-xdocs/src/sprite/icon-vimeo.svg9
-rwxr-xr-xdocs/src/sprite/icon-youtube.svg9
-rw-r--r--gulpfile.js39
-rw-r--r--package.json4
-rw-r--r--readme.md306
-rw-r--r--src/js/plyr.js1549
-rw-r--r--src/less/plyr.less300
-rw-r--r--src/sass/plyr.scss110
-rw-r--r--src/sprite/icon-captions-off.svg6
-rw-r--r--src/sprite/icon-captions-on.svg14
-rw-r--r--src/sprite/icon-enter-fullscreen.svg12
-rw-r--r--src/sprite/icon-exit-fullscreen.svg12
-rwxr-xr-xsrc/sprite/icon-fast-forward.svg3
-rw-r--r--src/sprite/icon-muted.svg13
-rw-r--r--src/sprite/icon-pause.svg12
-rwxr-xr-xsrc/sprite/icon-play.svg3
-rw-r--r--src/sprite/icon-restart.svg7
-rw-r--r--src/sprite/icon-rewind.svg6
-rwxr-xr-xsrc/sprite/icon-volume.svg5
38 files changed, 1817 insertions, 1204 deletions
diff --git a/.jshintrc b/.jshintrc
index 7884da2c..0d1a70e2 100644
--- a/.jshintrc
+++ b/.jshintrc
@@ -9,8 +9,7 @@
"rhino" : false,
"couch" : false,
"wsh" : true, // Windows Scripting Host.
- "jquery" : true,
- "predef" : [ "jQuery", "$" ],
+ "jquery" : false,
// Development.
"debug" : false, // Allow debugger statements e.g. browser breakpoints.
@@ -51,6 +50,6 @@
"plusplus" : false, // Prohibit use of `++` & `--`.
"sub" : false, // Tolerate all forms of subscript notation besides dot notation e.g. `dict['key']` instead of `dict.key`.
"trailing" : true, // Prohibit trailing whitespaces.
- "white" : false, // Check against strict whitespace and indentation rules.
- "indent" : 2 // Specify indentation spacing
+ "white" : true, // Check against strict whitespace and indentation rules.
+ "indent" : 4 // Specify indentation spacing
} \ No newline at end of file
diff --git a/changelog.md b/changelog.md
index 1cf71c24..9eb7b009 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,21 +1,34 @@
# Changelog
-# v1.3.5
+# v1.5.0
+- *Beta* Vimeo support (please report bugs) (fixes #8)
+- New options for initialization (you can now pass a selector, HTMLElement or NodeList) (fixes #118)
+- Switched to BEM methodology (you will need to change CSS and probably HTML)
+- Decoupled CSS and JS hooks (fixes #129)
+- Custom controls container (fixes #98)
+- Fix for private/incognito mode local storage bug (fixes #131)
+- UMD module setup (fixes #121)
+- Specify iframe title for Vimeo and YouTube (fixes #124)
+- Better handling of mission controls (fixes #132)
+- Retain classname on source change (fixes #120)
+- Increased thumb size on seek (partially fixes #130)
+
+## v1.3.5
- Fixed bug with API use on basic supported browsers
-# v1.3.4
+## v1.3.4
- Code cleanup by @calvintam236
-# v1.3.3
+## v1.3.3
- Removed captions being read by screen readers
-# v1.3.2
+## v1.3.2
- Voiceover fix for captions
-# v1.3.1
+## v1.3.1
- ARIA improvements for captions being read
-# v1.3.0
+## v1.3.0
- Internationalization support (i18n) using default controls (required markup changes to controls)
- ARIA enhancements for controls (required markup changes to controls)
- Captions legibility improvements
@@ -55,10 +68,10 @@
- Bug fix for Chrome Canary
## v1.1.11
-- Bug fix
+- Bug fix
## v1.1.10
-- Bug fix
+- Bug fix
## v1.1.9
- Bug fix for 1.1.8
diff --git a/dist/plyr.css b/dist/plyr.css
index 78ebe688..03457207 100644
--- a/dist/plyr.css
+++ b/dist/plyr.css
@@ -1 +1 @@
-.player-captions,.player-controls,.player-controls .player-time{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}.player-controls button:focus,.player-progress-seek[type=range]:focus,.player-volume[type=range]:focus{outline:0}@-webkit-keyframes progress{to{background-position:40px 0}}@keyframes progress{to{background-position:40px 0}}.sr-only{position:absolute!important;clip:rect(1px,1px,1px,1px);padding:0!important;border:0!important;height:1px!important;width:1px!important;overflow:hidden}.player,.player-video-wrapper{position:relative}.player{max-width:100%;min-width:290px}.player,.player *,.player ::after,.player ::before{box-sizing:border-box}.player audio,.player video{width:100%;height:auto;vertical-align:middle}.player-video-embed{padding-bottom:56.25%;height:0}.player-video-embed iframe{position:absolute;top:0;left:0;width:100%;height:100%;border:0}.player-captions{display:none;position:absolute;bottom:0;left:0;width:100%;padding:20px 20px 30px;color:#fff;font-size:20px;text-align:center}.player-captions span{border-radius:2px;padding:3px 10px;background:rgba(0,0,0,.9)}.player-captions span:empty{display:none}@media (min-width:768px){.player-captions{font-size:24px}}.player.captions-active .player-captions{display:block}.player.fullscreen-active .player-captions{font-size:32px}.player-controls{zoom:1;position:relative;padding:10px;background:#fff;line-height:1;text-align:center;box-shadow:0 1px 1px rgba(52,63,74,.2)}.player-controls:after,.player-controls:before{content:"";display:table}.player-controls:after{clear:both}.player-controls-right{display:block;margin:10px auto 0}@media (min-width:560px){.player-controls-left{float:left}.player-controls-right{float:right;margin-top:0}}.player-controls button{display:inline-block;vertical-align:middle;margin:0 2px;padding:5px 10px;overflow:hidden;border:0;background:0 0;border-radius:3px;cursor:pointer;color:#6B7D86;transition:background .3s ease,color .3s ease,opacity .3s ease}.player-controls button svg{width:18px;height:18px;display:block;fill:currentColor;transition:fill .3s ease}.player-controls button.tab-focus,.player-controls button:hover{background:#3498DB;color:#fff}.player-controls .icon-captions-on,.player-controls .icon-exit-fullscreen,.player-controls .icon-muted{display:none}.player-controls .player-time{display:inline-block;vertical-align:middle;margin-left:10px;color:#6B7D86;font-weight:600;font-size:14px}.player-controls .player-time+.player-time{display:none}@media (min-width:560px){.player-controls .player-time+.player-time{display:inline-block}}.player-controls .player-time+.player-time::before{content:'\2044';margin-right:10px}.player-tooltip{position:absolute;z-index:2;bottom:100%;margin-bottom:10px;padding:10px 15px;opacity:0;background:#fff;border:1px solid #D6DADD;border-radius:3px;color:#6B7D86;font-size:14px;line-height:1.5;font-weight:600;-webkit-transform:translate(-50%,30px) scale(0);transform:translate(-50%,30px) scale(0);-webkit-transform-origin:50% 100%;transform-origin:50% 100%;transition:opacity .2s .1s ease,-webkit-transform .2s .1s ease;transition:transform .2s .1s ease,opacity .2s .1s ease;transition:transform .2s .1s ease,opacity .2s .1s ease,-webkit-transform .2s .1s ease}.player-tooltip::after{content:'';position:absolute;z-index:1;top:100%;left:50%;display:block;width:10px;height:10px;background:#fff;-webkit-transform:translate(-50%,-50%) rotate(45deg) translateY(1px);transform:translate(-50%,-50%) rotate(45deg) translateY(1px);border:1px solid #D6DADD;border-width:0 1px 1px 0}.player button.tab-focus:focus .player-tooltip,.player button:hover .player-tooltip{opacity:1;-webkit-transform:translate(-50%,0) scale(1);transform:translate(-50%,0) scale(1)}.player button:hover .player-tooltip{z-index:3}.player-progress{position:absolute;bottom:100%;left:0;right:0;width:100%;height:10px;background:rgba(86,93,100,.2)}.player-progress-buffer[value],.player-progress-played[value],.player-progress-seek[type=range]{position:absolute;left:0;top:0;width:100%;height:10px;margin:0;padding:0;vertical-align:top;-webkit-appearance:none;-moz-appearance:none;border:none;background:0 0}.player-progress-buffer[value]::-webkit-progress-bar,.player-progress-played[value]::-webkit-progress-bar{background:0 0}.player-progress-buffer[value]::-webkit-progress-value,.player-progress-played[value]::-webkit-progress-value{background:currentColor}.player-progress-buffer[value]::-moz-progress-bar,.player-progress-played[value]::-moz-progress-bar{background:currentColor}.player-progress-played[value]{z-index:2;color:#3498DB}.player-progress-buffer[value]{color:rgba(86,93,100,.25)}.player-progress-seek[type=range]{z-index:4;cursor:pointer;outline:0}.player-progress-seek[type=range]::-webkit-slider-runnable-track{background:0 0;border:0}.player-progress-seek[type=range]::-webkit-slider-thumb{-webkit-appearance:none;background:0 0;border:0;width:20px;height:10px}.player-progress-seek[type=range]::-moz-range-track{background:0 0;border:0}.player-progress-seek[type=range]::-moz-range-thumb{-moz-appearance:none;background:0 0;border:0;width:20px;height:10px}.player-progress-seek[type=range]::-ms-track{color:transparent;background:0 0;border:0}.player-progress-seek[type=range]::-ms-fill-lower,.player-progress-seek[type=range]::-ms-fill-upper{background:0 0;border:0}.player-progress-seek[type=range]::-ms-thumb{background:0 0;border:0;width:20px;height:10px}.player-progress-seek[type=range]::-moz-focus-outer{border:0}.player.loading .player-progress-buffer{-webkit-animation:progress 1s linear infinite;animation:progress 1s linear infinite;background-size:40px 40px;background-repeat:repeat-x;background-color:rgba(86,93,100,.25);background-image:linear-gradient(-45deg,rgba(0,0,0,.15) 25%,transparent 25%,transparent 50%,rgba(0,0,0,.15) 50%,rgba(0,0,0,.15) 75%,transparent 75%,transparent);color:transparent}.player-controls [data-player=pause],.player.playing .player-controls [data-player=play]{display:none}.player.playing .player-controls [data-player=pause]{display:inline-block}.player-volume[type=range]{display:inline-block;vertical-align:middle;-webkit-appearance:none;-moz-appearance:none;width:100px;margin:0 10px 0 0;padding:0;cursor:pointer;background:0 0;border:none}.player-volume[type=range]::-webkit-slider-runnable-track{height:6px;background:#e6e6e6;border:0;border-radius:3px}.player-volume[type=range]::-webkit-slider-thumb{-webkit-appearance:none;margin-top:-3px;height:12px;width:12px;background:#6B7D86;border:0;border-radius:6px;transition:background .3s ease;cursor:ew-resize}.player-volume[type=range]::-moz-range-track{height:6px;background:#e6e6e6;border:0;border-radius:3px}.player-volume[type=range]::-moz-range-thumb{height:12px;width:12px;background:#6B7D86;border:0;border-radius:6px;transition:background .3s ease;cursor:ew-resize}.player-volume[type=range]::-ms-track{height:6px;background:0 0;border-color:transparent;border-width:3px 0;color:transparent}.player-volume[type=range]::-ms-fill-lower,.player-volume[type=range]::-ms-fill-upper{height:6px;background:#e6e6e6;border:0;border-radius:3px}.player-volume[type=range]::-ms-thumb{height:12px;width:12px;background:#6B7D86;border:0;border-radius:6px;transition:background .3s ease;cursor:ew-resize}.player-volume[type=range]:focus::-webkit-slider-thumb{background:#3498DB}.player-volume[type=range]:focus::-moz-range-thumb{background:#3498DB}.player-volume[type=range]:focus::-ms-thumb{background:#3498DB}.player-audio.ios .player-controls-right,.player.ios .player-volume,.player.ios [data-player=mute]{display:none}.player-audio.ios .player-controls-left{float:none}.player-audio .player-controls{padding-top:20px}.player-audio .player-progress{bottom:auto;top:0;background:#D6DADD}.player-fullscreen,.player.fullscreen-active{position:fixed;top:0;left:0;right:0;bottom:0;height:100%;width:100%;z-index:10000000;background:#000}.player-fullscreen video,.player.fullscreen-active video{height:100%}.player-fullscreen .player-video-wrapper,.player.fullscreen-active .player-video-wrapper{height:100%;width:100%}.player-fullscreen .player-controls,.player.fullscreen-active .player-controls{position:absolute;bottom:0;left:0;right:0}.player-fullscreen.fullscreen-hide-controls.playing .player-controls,.player.fullscreen-active.fullscreen-hide-controls.playing .player-controls{-webkit-transform:translateY(100%) translateY(5px);transform:translateY(100%) translateY(5px);transition:-webkit-transform .3s .2s ease;transition:transform .3s .2s ease;transition:transform .3s .2s ease,-webkit-transform .3s .2s ease}.player-fullscreen.fullscreen-hide-controls.playing.player-hover .player-controls,.player.fullscreen-active.fullscreen-hide-controls.playing.player-hover .player-controls{-webkit-transform:translateY(0);transform:translateY(0)}.player-fullscreen.fullscreen-hide-controls.playing .player-captions,.player.fullscreen-active.fullscreen-hide-controls.playing .player-captions{bottom:5px;transition:bottom .3s .2s ease}.player-fullscreen .player-captions,.player-fullscreen.fullscreen-hide-controls.playing.player-hover .player-captions,.player.fullscreen-active .player-captions,.player.fullscreen-active.fullscreen-hide-controls.playing.player-hover .player-captions{top:auto;bottom:90px}@media (min-width:560px){.player-fullscreen .player-captions,.player-fullscreen.fullscreen-hide-controls.playing.player-hover .player-captions,.player.fullscreen-active .player-captions,.player.fullscreen-active.fullscreen-hide-controls.playing.player-hover .player-captions{bottom:60px}}.player.captions-active .player-controls .icon-captions-on,.player.fullscreen-active .icon-exit-fullscreen,.player.muted .player-controls .icon-muted{display:block}.player [data-player=captions],.player [data-player=fullscreen],.player.captions-active .player-controls .icon-captions-on+svg,.player.fullscreen-active .icon-exit-fullscreen+svg,.player.muted .player-controls .icon-muted+svg{display:none}.player.captions-enabled [data-player=captions],.player.fullscreen-enabled [data-player=fullscreen]{display:inline-block} \ No newline at end of file
+.plyr__captions,.plyr__controls,.plyr__controls .plyr__time{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}@-webkit-keyframes progress{to{background-position:40px 0}}@keyframes progress{to{background-position:40px 0}}.plyr{position:relative;max-width:100%;min-width:290px}.plyr,.plyr *,.plyr ::after,.plyr ::before{box-sizing:border-box}.plyr a,.plyr button,.plyr input,.plyr label{-ms-touch-action:manipulation;touch-action:manipulation}.plyr__sr-only{position:absolute!important;clip:rect(1px,1px,1px,1px);padding:0!important;border:0!important;height:1px!important;width:1px!important;overflow:hidden}.plyr__video-wrapper{position:relative}.plyr audio,.plyr video{width:100%;height:auto;vertical-align:middle}.plyr__video-embed{padding-bottom:56.25%;height:0;overflow:hidden;background:#000}.plyr__video-embed iframe{position:absolute;top:0;left:0;width:100%;height:100%;border:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.plyr__video-embed>div{position:relative;padding-bottom:200%;-webkit-transform:translateY(-35.95%);transform:translateY(-35.95%)}.plyr__captions{display:none;position:absolute;bottom:0;left:0;width:100%;padding:20px 20px 30px;color:#fff;font-size:20px;text-align:center}.plyr__captions span{border-radius:2px;padding:3px 10px;background:rgba(0,0,0,.9)}.plyr__captions span:empty{display:none}@media (min-width:768px){.plyr__captions{font-size:24px}}.plyr--captions-active .plyr__captions{display:block}.plyr--fullscreen-active .plyr__captions{font-size:32px}.plyr__controls{zoom:1;position:relative;padding:10px;background:#fff;line-height:1;text-align:center;box-shadow:0 1px 1px rgba(52,63,74,.2)}.plyr__controls:after,.plyr__controls:before{content:"";display:table}.plyr__controls:after{clear:both}.plyr__controls--right{display:block;margin:10px auto 0}@media (min-width:560px){.plyr__controls--left{float:left}.plyr__controls--right{float:right;margin-top:0}}.plyr__controls button{display:inline-block;vertical-align:middle;margin:0 2px;padding:5px 10px;overflow:hidden;border:0;background:0 0;border-radius:3px;cursor:pointer;color:#6B7D86;transition:background .3s ease,color .3s ease,opacity .3s ease}.plyr__controls button svg{width:18px;height:18px;display:block;fill:currentColor;transition:fill .3s ease}.plyr__controls button.tab-focus,.plyr__controls button:hover{background:#3498DB;color:#fff}.plyr__controls .plyr__time,.plyr__tooltip{color:#6B7D86;font-size:14px;font-weight:600}.plyr__controls button:focus{outline:0}.plyr__controls .icon--captions-on,.plyr__controls .icon--exit-fullscreen,.plyr__controls .icon--muted{display:none}.plyr__controls .plyr__time{display:inline-block;vertical-align:middle;margin-left:10px}.plyr__controls .plyr__time+.plyr__time{display:none}@media (min-width:560px){.plyr__controls .plyr__time+.plyr__time{display:inline-block}}.plyr__controls .plyr__time+.plyr__time::before{content:'\2044';margin-right:10px}.plyr__tooltip{position:absolute;z-index:2;bottom:100%;margin-bottom:10px;padding:10px 15px;opacity:0;background:#fff;box-shadow:0 0 5px rgba(52,63,74,.1),0 0 0 1px rgba(52,63,74,.1);border-radius:3px;line-height:1.5;-webkit-transform:translate(-50%,30px) scale(.8);transform:translate(-50%,30px) scale(.8);-webkit-transform-origin:50% 100%;transform-origin:50% 100%;transition:opacity .2s .1s ease,-webkit-transform .2s .1s ease;transition:transform .2s .1s ease,opacity .2s .1s ease;transition:transform .2s .1s ease,opacity .2s .1s ease,-webkit-transform .2s .1s ease}.plyr__tooltip::after,.plyr__tooltip::before{content:'';position:absolute;width:0;height:0;top:100%;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%)}.plyr__tooltip::after{bottom:-8px;border-right:7px solid transparent;border-top:7px solid rgba(52,63,74,.1);border-left:7px solid transparent;z-index:1}.plyr__tooltip::before{bottom:-6px;border-right:6px solid transparent;border-top:6px solid #fff;border-left:6px solid transparent;z-index:2}.plyr button.tab-focus:focus .plyr__tooltip,.plyr button:hover .plyr__tooltip{opacity:1;-webkit-transform:translate(-50%,0) scale(1);transform:translate(-50%,0) scale(1)}.plyr button:hover .plyr__tooltip{z-index:3}.plyr input[type=range].tab-focus{outline:rgba(52,63,74,.8) dotted 1px;outline-offset:3px}.plyr__progress--seek[type=range]:focus,.plyr__volume[type=range]:focus{outline:0}.plyr__progress{position:absolute;bottom:100%;left:0;right:0;width:100%;height:10px;background:rgba(86,93,100,.2)}.plyr__progress--buffer[value],.plyr__progress--played[value],.plyr__progress--seek[type=range]{position:absolute;left:0;top:0;width:100%;height:10px;margin:0;padding:0;vertical-align:top;-webkit-appearance:none;-moz-appearance:none;border:none;background:0 0}.plyr__progress--buffer[value]::-webkit-progress-bar,.plyr__progress--played[value]::-webkit-progress-bar{background:0 0}.plyr__progress--buffer[value]::-webkit-progress-value,.plyr__progress--played[value]::-webkit-progress-value{background:currentColor}.plyr__progress--buffer[value]::-moz-progress-bar,.plyr__progress--played[value]::-moz-progress-bar{background:currentColor}.plyr__progress--played[value]{z-index:2;color:#3498DB}.plyr__progress--buffer[value]{color:rgba(86,93,100,.25)}.plyr__progress--seek[type=range]{z-index:4;cursor:pointer;outline:0}.plyr__progress--seek[type=range]::-webkit-slider-runnable-track{background:0 0;border:0}.plyr__progress--seek[type=range]::-webkit-slider-thumb{-webkit-appearance:none;background:0 0;border:0;width:40px;height:10px;-webkit-transform:translateX(-50%);transform:translateX(-50%)}.plyr__progress--seek[type=range]::-moz-range-track{background:0 0;border:0}.plyr__progress--seek[type=range]::-moz-range-thumb{-moz-appearance:none;background:0 0;border:0;width:40px;height:10px;transform:translateX(-50%)}.plyr__progress--seek[type=range]::-ms-track{color:transparent;background:0 0;border:0}.plyr__progress--seek[type=range]::-ms-fill-lower,.plyr__progress--seek[type=range]::-ms-fill-upper{background:0 0;border:0}.plyr__progress--seek[type=range]::-ms-thumb{background:0 0;border:0;width:40px;height:10px;transform:translateX(-50%)}.plyr__progress--seek[type=range]::-moz-focus-outer{border:0}.plyr--loading .plyr__progress--buffer{-webkit-animation:progress 1s linear infinite;animation:progress 1s linear infinite;background-size:40px 40px;background-repeat:repeat-x;background-color:rgba(86,93,100,.25);background-image:linear-gradient(-45deg,rgba(0,0,0,.15) 25%,transparent 25%,transparent 50%,rgba(0,0,0,.15) 50%,rgba(0,0,0,.15) 75%,transparent 75%,transparent);color:transparent}.plyr--playing .plyr__controls [data-plyr=play],.plyr__controls [data-plyr=pause]{display:none}.plyr--playing .plyr__controls [data-plyr=pause]{display:inline-block}.plyr__volume[type=range]{display:inline-block;vertical-align:middle;-webkit-appearance:none;-moz-appearance:none;width:100px;margin:0 10px 0 0;padding:0;cursor:pointer;background:0 0;border:none}.plyr__volume[type=range]::-webkit-slider-runnable-track{height:6px;background:#e6e6e6;border:0;border-radius:3px}.plyr__volume[type=range]::-webkit-slider-thumb{-webkit-appearance:none;margin-top:-3px;height:12px;width:12px;background:#6B7D86;border:0;border-radius:100%;transition:background .3s ease,-webkit-transform .2s ease;transition:background .3s ease,transform .2s ease;transition:background .3s ease,transform .2s ease,-webkit-transform .2s ease;cursor:ew-resize}.plyr__volume[type=range]::-moz-range-track{height:6px;background:#e6e6e6;border:0;border-radius:3px}.plyr__volume[type=range]::-moz-range-thumb{height:12px;width:12px;background:#6B7D86;border:0;border-radius:100%;transition:background .3s ease,-webkit-transform .2s ease;transition:background .3s ease,transform .2s ease;transition:background .3s ease,transform .2s ease,-webkit-transform .2s ease;cursor:ew-resize}.plyr__volume[type=range]::-ms-track{height:6px;background:0 0;border-color:transparent;border-width:3px 0;color:transparent}.plyr__volume[type=range]::-ms-fill-lower,.plyr__volume[type=range]::-ms-fill-upper{height:6px;background:#e6e6e6;border:0;border-radius:3px}.plyr__volume[type=range]::-ms-thumb{height:12px;width:12px;background:#6B7D86;border:0;border-radius:100%;transition:background .3s ease,-webkit-transform .2s ease;transition:background .3s ease,transform .2s ease;transition:background .3s ease,transform .2s ease,-webkit-transform .2s ease;cursor:ew-resize}.plyr__volume[type=range]:focus::-webkit-slider-thumb{background:#3498DB}.plyr__volume[type=range]:focus::-moz-range-thumb{background:#3498DB}.plyr__volume[type=range]:focus::-ms-thumb{background:#3498DB}.plyr--is-ios .plyr-volume,.plyr--is-ios [data-plyr=mute],.plyr--is-ios.plyr--audio .plyr__controls--right{display:none}.plyr--is-ios.plyr--audio .plyr__controls--left{float:none}.plyr--audio .plyr__controls{padding-top:20px}.plyr--audio .plyr__progress{bottom:auto;top:0;background:#D6DADD}.plyr--fullscreen,.plyr--fullscreen-active{position:fixed;top:0;left:0;right:0;bottom:0;height:100%;width:100%;z-index:10000000;background:#000}.plyr--fullscreen video,.plyr--fullscreen-active video{height:100%}.plyr--fullscreen .plyr__video-wrapper,.plyr--fullscreen-active .plyr__video-wrapper{height:100%;width:100%}.plyr--fullscreen .plyr__controls,.plyr--fullscreen-active .plyr__controls{position:absolute;bottom:0;left:0;right:0}.plyr--fullscreen--hide-controls.plyr--fullscreen-active.plyr--playing .plyr__controls{-webkit-transform:translateY(100%) translateY(5px);transform:translateY(100%) translateY(5px);transition:-webkit-transform .3s .2s ease;transition:transform .3s .2s ease;transition:transform .3s .2s ease,-webkit-transform .3s .2s ease}.plyr--fullscreen--hide-controls.plyr--fullscreen-active.plyr--playing.plyr--hover .plyr__controls{-webkit-transform:translateY(0);transform:translateY(0)}.plyr--fullscreen--hide-controls.plyr--fullscreen-active.plyr--playing .plyr__captions{bottom:5px;transition:bottom .3s .2s ease}.plyr--fullscreen .plyr__captions,.plyr--fullscreen--hide-controls.plyr--fullscreen-active.plyr--playing.plyr--hover .plyr__captions,.plyr--fullscreen-active .plyr__captions{top:auto;bottom:90px}@media (min-width:560px){.plyr--fullscreen .plyr__captions,.plyr--fullscreen--hide-controls.plyr--fullscreen-active.plyr--playing.plyr--hover .plyr__captions,.plyr--fullscreen-active .plyr__captions{bottom:60px}}.plyr--captions-active .plyr__controls .icon--captions-on,.plyr--fullscreen-active .icon--exit-fullscreen,.plyr--muted .plyr__controls .icon--muted{display:block}.plyr [data-plyr=captions],.plyr [data-plyr=fullscreen],.plyr--captions-active .plyr__controls .icon--captions-on+svg,.plyr--fullscreen-active .icon--exit-fullscreen+svg,.plyr--muted .plyr__controls .icon--muted+svg{display:none}.plyr--captions-enabled [data-plyr=captions],.plyr--fullscreen-enabled [data-plyr=fullscreen]{display:inline-block} \ No newline at end of file
diff --git a/dist/plyr.js b/dist/plyr.js
index 7096fa7d..d853990a 100644
--- a/dist/plyr.js
+++ b/dist/plyr.js
@@ -1 +1 @@
-!function(e){"use strict";function t(){var e=['<div class="player-controls">','<div class="player-progress">','<label for="seek{id}" class="sr-only">Seek</label>','<input id="seek{id}" class="player-progress-seek" type="range" min="0" max="100" step="0.5" value="0" data-player="seek">','<progress class="player-progress-played" max="100" value="0">',"<span>0</span>% "+C.i18n.played,"</progress>",'<progress class="player-progress-buffer" max="100" value="0">',"<span>0</span>% "+C.i18n.buffered,"</progress>","</div>",'<span class="player-controls-left">'];return o(C.controls,"restart")&&e.push('<button type="button" data-player="restart">','<svg><use xlink:href="#'+C.iconPrefix+'-restart" /></svg>','<span class="sr-only">'+C.i18n.restart+"</span>","</button>"),o(C.controls,"rewind")&&e.push('<button type="button" data-player="rewind">','<svg><use xlink:href="#'+C.iconPrefix+'-rewind" /></svg>','<span class="sr-only">'+C.i18n.rewind+"</span>","</button>"),o(C.controls,"play")&&e.push('<button type="button" data-player="play">','<svg><use xlink:href="#'+C.iconPrefix+'-play" /></svg>','<span class="sr-only">'+C.i18n.play+"</span>","</button>",'<button type="button" data-player="pause">','<svg><use xlink:href="#'+C.iconPrefix+'-pause" /></svg>','<span class="sr-only">'+C.i18n.pause+"</span>","</button>"),o(C.controls,"fast-forward")&&e.push('<button type="button" data-player="fast-forward">','<svg><use xlink:href="#'+C.iconPrefix+'-fast-forward" /></svg>','<span class="sr-only">'+C.i18n.forward+"</span>","</button>"),o(C.controls,"current-time")&&e.push('<span class="player-time">','<span class="sr-only">'+C.i18n.currentTime+"</span>",'<span class="player-current-time">00:00</span>',"</span>"),o(C.controls,"duration")&&e.push('<span class="player-time">','<span class="sr-only">'+C.i18n.duration+"</span>",'<span class="player-duration">00:00</span>',"</span>"),e.push("</span>",'<span class="player-controls-right">'),o(C.controls,"mute")&&e.push('<button type="button" data-player="mute">','<svg class="icon-muted"><use xlink:href="#'+C.iconPrefix+'-muted" /></svg>','<svg><use xlink:href="#'+C.iconPrefix+'-volume" /></svg>','<span class="sr-only">'+C.i18n.toggleMute+"</span>","</button>"),o(C.controls,"volume")&&e.push('<label for="volume{id}" class="sr-only">'+C.i18n.volume+"</label>",'<input id="volume{id}" class="player-volume" type="range" min="0" max="10" value="5" data-player="volume">'),o(C.controls,"captions")&&e.push('<button type="button" data-player="captions">','<svg class="icon-captions-on"><use xlink:href="#'+C.iconPrefix+'-captions-on" /></svg>','<svg><use xlink:href="#'+C.iconPrefix+'-captions-off" /></svg>','<span class="sr-only">'+C.i18n.toggleCaptions+"</span>","</button>"),o(C.controls,"fullscreen")&&e.push('<button type="button" data-player="fullscreen">','<svg class="icon-exit-fullscreen"><use xlink:href="#'+C.iconPrefix+'-exit-fullscreen" /></svg>','<svg><use xlink:href="#'+C.iconPrefix+'-enter-fullscreen" /></svg>','<span class="sr-only">'+C.i18n.toggleFullscreen+"</span>","</button>"),e.push("</span>","</div>"),e.join("")}function n(e,t){C.debug&&window.console&&console[t?"error":"log"](e)}function r(){var e,t,n,r=navigator.userAgent,a=navigator.appName,s=""+parseFloat(navigator.appVersion),o=parseInt(navigator.appVersion,10);return-1!==navigator.appVersion.indexOf("Windows NT")&&-1!==navigator.appVersion.indexOf("rv:11")?(a="IE",s="11;"):-1!==(t=r.indexOf("MSIE"))?(a="IE",s=r.substring(t+5)):-1!==(t=r.indexOf("Chrome"))?(a="Chrome",s=r.substring(t+7)):-1!==(t=r.indexOf("Safari"))?(a="Safari",s=r.substring(t+7),-1!==(t=r.indexOf("Version"))&&(s=r.substring(t+8))):-1!==(t=r.indexOf("Firefox"))?(a="Firefox",s=r.substring(t+8)):(e=r.lastIndexOf(" ")+1)<(t=r.lastIndexOf("/"))&&(a=r.substring(e,t),s=r.substring(t+1),a.toLowerCase()==a.toUpperCase()&&(a=navigator.appName)),-1!==(n=s.indexOf(";"))&&(s=s.substring(0,n)),-1!==(n=s.indexOf(" "))&&(s=s.substring(0,n)),o=parseInt(""+s,10),isNaN(o)&&(s=""+parseFloat(navigator.appVersion),o=parseInt(navigator.appVersion,10)),{name:a,version:o,ios:/(iPad|iPhone|iPod)/g.test(navigator.platform)}}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 s(e){if(!document.querySelectorAll('script[src="'+e+'"]').length){var t=document.createElement("script");t.src=e;var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(t,n)}}function o(e,t){return Array.prototype.indexOf&&-1!=e.indexOf(t)}function i(e,t,n){return e.replace(new RegExp(t.replace(/([.*+?\^=!:${}()|\[\]\/\\])/g,"\\$1"),"g"),n)}function u(e,t){e.length||(e=[e]);for(var n=e.length-1;n>=0;n--){var r=n>0?t.cloneNode(!0):t,a=e[n],s=a.parentNode,o=a.nextSibling;r.appendChild(a),o?s.insertBefore(r,o):s.appendChild(r)}}function l(e){for(var t=e.parentNode;e.firstChild;)t.insertBefore(e.firstChild,e);t.removeChild(e)}function c(e){e.parentNode.removeChild(e)}function p(e,t){e.insertBefore(t,e.firstChild)}function d(e,t){for(var n in t)e.setAttribute(n,t[n])}function f(e,t,n){if(e)if(e.classList)e.classList[n?"add":"remove"](t);else{var r=(" "+e.className+" ").replace(/\s+/g," ").replace(" "+t+" ","");e.className=r+(n?" "+t:"")}}function m(e,t,n,r){var a=t.split(" ");if(e instanceof NodeList)for(var s=0;s<e.length;s++)e[s]instanceof Node&&m(e[s],arguments[1],arguments[2],arguments[3]);else for(var o=0;o<a.length;o++)e[r?"addEventListener":"removeEventListener"](a[o],n,!1)}function y(e,t,n){e&&m(e,t,n,!0)}function b(e,t,n){e&&m(e,t,n,!1)}function v(e,t){var n=document.createEvent("MouseEvents");n.initEvent(t,!0,!0),e.dispatchEvent(n)}function g(e,t){return t="boolean"==typeof t?t:!e.getAttribute("aria-pressed"),e.setAttribute("aria-pressed",t),t}function h(e,t){return 0===e||0===t||isNaN(e)||isNaN(t)?0:(e/t*100).toFixed(2)}function w(e,t){for(var n in t)t[n]&&t[n].constructor&&t[n].constructor===Object?(e[n]=e[n]||{},w(e[n],t[n])):e[n]=t[n];return e}function k(){var e={supportsFullScreen:!1,isFullScreen:function(){return!1},requestFullScreen:function(){},cancelFullScreen:function(){},fullScreenEventName:"",element:null,prefix:""},t="webkit moz o ms khtml".split(" ");if("undefined"!=typeof document.cancelFullScreen)e.supportsFullScreen=!0;else for(var n=0,r=t.length;r>n;n++){if(e.prefix=t[n],"undefined"!=typeof document[e.prefix+"CancelFullScreen"]){e.supportsFullScreen=!0;break}if("undefined"!=typeof document.msExitFullscreen&&document.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=document.body),this.prefix){case"":return document.fullscreenElement==e;case"moz":return document.mozFullScreenElement==e;default:return document[this.prefix+"FullscreenElement"]==e}},e.requestFullScreen=function(e){return"undefined"==typeof e&&(e=document.body),""===this.prefix?e.requestFullScreen():e[this.prefix+("ms"==this.prefix?"RequestFullscreen":"RequestFullScreen")]()},e.cancelFullScreen=function(){return""===this.prefix?document.cancelFullScreen():document[this.prefix+("ms"==this.prefix?"ExitFullscreen":"CancelFullScreen")]()},e.element=function(){return""===this.prefix?document.fullscreenElement:document[this.prefix+"FullscreenElement"]}),e}function x(){var e={supported:function(){try{return"localStorage"in window&&null!==window.localStorage}catch(e){return!1}}()};return e}function T(o){function w(e){if(!me.usingTextTracks&&"video"===me.type&&me.supported.full){for(me.subcount=0,e="number"==typeof e?e:me.media.currentTime;A(me.captions[me.subcount][0])<e.toFixed(1);)if(me.subcount++,me.subcount>me.captions.length-1){me.subcount=me.captions.length-1;break}if(me.media.currentTime.toFixed(1)>=E(me.captions[me.subcount][0])&&me.media.currentTime.toFixed(1)<=A(me.captions[me.subcount][0])){me.currentCaption=me.captions[me.subcount][1];var t=me.currentCaption.trim();me.captionsContainer.innerHTML!=t&&(me.captionsContainer.innerHTML="",me.captionsContainer.innerHTML=t)}else me.captionsContainer.innerHTML=""}}function T(){me.buttons.captions&&(f(me.container,C.classes.captions.enabled,!0),C.captions.defaultActive&&(f(me.container,C.classes.captions.active,!0),g(me.buttons.captions,!0)))}function E(e){var t=[];return t=e.split(" --> "),P(t[0])}function A(e){var t=[];return t=e.split(" --> "),P(t[1])}function P(e){if(null===e||void 0===e)return 0;var t,n=[],r=[];return n=e.split(","),r=n[0].split(":"),t=Math.floor(60*r[0]*60)+Math.floor(60*r[1])+Math.floor(r[2])}function N(e){return me.container.querySelectorAll(e)}function M(e){return N(e)[0]}function I(){try{return window.self!==window.top}catch(e){return!0}}function L(){var e=C.html;if(n("Injecting custom controls."),e||(e=t()),e=i(e,"{seektime}",C.seekTime),e=i(e,"{id}",Math.floor(1e4*Math.random())),me.container.insertAdjacentHTML("beforeend",e),C.tooltips)for(var r=N(C.selectors.labels),a=r.length-1;a>=0;a--){var s=r[a];f(s,C.classes.hidden,!1),f(s,C.classes.tooltip,!0)}}function O(){try{return me.controls=M(C.selectors.controls),me.buttons={},me.buttons.seek=M(C.selectors.buttons.seek),me.buttons.play=M(C.selectors.buttons.play),me.buttons.pause=M(C.selectors.buttons.pause),me.buttons.restart=M(C.selectors.buttons.restart),me.buttons.rewind=M(C.selectors.buttons.rewind),me.buttons.forward=M(C.selectors.buttons.forward),me.buttons.fullscreen=M(C.selectors.buttons.fullscreen),me.buttons.mute=M(C.selectors.buttons.mute),me.buttons.captions=M(C.selectors.buttons.captions),me.checkboxes=N('[type="checkbox"]'),me.progress={},me.progress.container=M(C.selectors.progress.container),me.progress.buffer={},me.progress.buffer.bar=M(C.selectors.progress.buffer),me.progress.buffer.text=me.progress.buffer.bar&&me.progress.buffer.bar.getElementsByTagName("span")[0],me.progress.played={},me.progress.played.bar=M(C.selectors.progress.played),me.progress.played.text=me.progress.played.bar&&me.progress.played.bar.getElementsByTagName("span")[0],me.volume=M(C.selectors.buttons.volume),me.duration=M(C.selectors.duration),me.currentTime=M(C.selectors.currentTime),me.seekTime=N(C.selectors.seekTime),!0}catch(e){return n("It looks like there's a problem with your controls html. Bailing.",!0),me.media.setAttribute("controls",""),!1}}function V(){if(me.buttons.play){var e=me.buttons.play.innerText||C.i18n.play;"undefined"!=typeof C.title&&C.title.length&&(e+=", "+C.title),me.buttons.play.setAttribute("aria-label",e)}}function q(){if(!me.media)return n("No audio or video element found!",!0),!1;if(me.supported.full&&(me.media.removeAttribute("controls"),f(me.container,C.classes.type.replace("{0}",me.type),!0),f(me.container,C.classes.stopped,null===me.media.getAttribute("autoplay")&&!C.autoplay),me.browser.ios&&f(me.container,"ios",!0),"video"===me.type)){var e=document.createElement("div");e.setAttribute("class",C.classes.videoWrapper),u(me.media,e),me.videoContainer=e}"youtube"==me.type&&H(me.media.getAttribute("data-video-id")),(null!==me.media.getAttribute("autoplay")||C.autoplay)&&D()}function H(e){for(var t=N('[id^="youtube"]'),n=t.length-1;n>=0;n--)c(t[n]);var r=document.createElement("div");r.setAttribute("id","youtube-"+Math.floor(1e4*Math.random())),me.media.appendChild(r),f(me.media,C.classes.videoWrapper,!0),f(me.media,C.classes.embedWrapper,!0),"object"==typeof YT?R(e,r):(s("https://www.youtube.com/iframe_api"),S.youtube.push(function(){R(e,r)}),window.onYouTubeIframeAPIReady=function(){for(var e=S.youtube.length-1;e>=0;e--)S.youtube[e](),S.youtube.splice(e,1)})}function R(e,t){n("YouTube API Ready"),"timer"in me||(me.timer={}),me.embed=new YT.Player(t.id,{videoId:e,playerVars:{autoplay:C.autoplay?1:0,controls:me.supported.full?0:1,rel:0,showinfo:0,iv_load_policy:3,cc_load_policy:C.captions.defaultActive?1:0,cc_lang_pref:"en",wmode:"transparent",modestbranding:1,disablekb:1},events:{onReady:function(e){var t=e.target;me.media.play=function(){t.playVideo(),me.media.paused=!1},me.media.pause=function(){t.pauseVideo(),me.media.paused=!0},me.media.stop=function(){t.stopVideo(),me.media.paused=!0},me.media.duration=t.getDuration(),me.media.paused=!0,me.media.currentTime=t.getCurrentTime(),me.media.muted=t.isMuted(),v(me.media,"timeupdate"),window.clearInterval(me.timer.buffering),me.timer.buffering=window.setInterval(function(){me.media.buffered=t.getVideoLoadedFraction(),v(me.media,"progress"),1===me.media.buffered&&window.clearInterval(me.timer.buffering)},200),me.supported.full&&(me.container.querySelectorAll(C.selectors.controls).length||fe(),C.displayDuration&&re())},onStateChange:function(e){var t=e.target;switch(window.clearInterval(me.timer.playing),e.data){case 0:me.media.paused=!0,v(me.media,"ended");break;case 1:me.media.paused=!1,v(me.media,"play"),me.timer.playing=window.setInterval(function(){me.media.currentTime=t.getCurrentTime(),v(me.media,"timeupdate")},200);break;case 2:me.media.paused=!0,v(me.media,"pause")}}}})}function B(){if("video"===me.type){me.videoContainer.insertAdjacentHTML("afterbegin",'<div class="'+C.selectors.captions.replace(".","")+'"><span></span></div>'),me.captionsContainer=M(C.selectors.captions).querySelector("span"),me.usingTextTracks=!1,me.media.textTracks&&(me.usingTextTracks=!0);for(var e,t="",r=me.media.childNodes,a=0;a<r.length;a++)"track"===r[a].nodeName.toLowerCase()&&(e=r[a].kind,("captions"===e||"subtitles"===e)&&(t=r[a].getAttribute("src")));if(me.captionExists=!0,""===t?(me.captionExists=!1,n("No caption track found.")):n("Caption track found; URI: "+t),me.captionExists){for(var s=me.media.textTracks,o=0;o<s.length;o++)s[o].mode="hidden";if(T(me),("IE"===me.browser.name&&me.browser.version>=10||"Firefox"===me.browser.name&&me.browser.version>=31||"Chrome"===me.browser.name&&me.browser.version>=43||"Safari"===me.browser.name&&me.browser.version>=7)&&(n("Detected unsupported browser for HTML5 captions. Using fallback."),me.usingTextTracks=!1),me.usingTextTracks){n("TextTracks supported.");for(var i=0;i<s.length;i++){var u=s[i];("captions"===u.kind||"subtitles"===u.kind)&&y(u,"cuechange",function(){me.captionsContainer.innerHTML="",this.activeCues[0]&&this.activeCues[0].hasOwnProperty("text")&&me.captionsContainer.appendChild(this.activeCues[0].getCueAsHTML().trim())})}}else if(n("TextTracks not supported so rendering captions manually."),me.currentCaption="",me.captions=[],""!==t){var l=new XMLHttpRequest;l.onreadystatechange=function(){if(4===l.readyState)if(200===l.status){var e,t=[],r=l.responseText;t=r.split("\n\n");for(var a=0;a<t.length;a++)e=t[a],me.captions[a]=[],me.captions[a]=e.split("\n");me.captions.shift(),n("Successfully loaded the caption file via AJAX.")}else n("There was a problem loading the caption file via AJAX.",!0)},l.open("get",t,!0),l.send()}if("Safari"===me.browser.name&&me.browser.version>=7){n("Safari 7+ detected; removing track from DOM."),s=me.media.getElementsByTagName("track");for(var c=0;c<s.length;c++)me.media.removeChild(s[c])}}else f(me.container,C.classes.captions.enabled)}}function j(){if("audio"!=me.type&&C.fullscreen.enabled){var e=F.supportsFullScreen;e||C.fullscreen.fallback&&!I()?(n((e?"Native":"Fallback")+" fullscreen enabled."),f(me.container,C.classes.fullscreen.enabled,!0)):n("Fullscreen not supported and fallback disabled."),g(me.buttons.fullscreen,!1),C.fullscreen.hideControls&&f(me.container,C.classes.fullscreen.hideControls,!0)}}function D(){me.media.play()}function _(){me.media.pause()}function W(e){e===!0?D():e===!1?_():me.media[me.media.paused?"play":"pause"]()}function U(e){"number"!=typeof e&&(e=C.seekTime),z(me.media.currentTime-e)}function Y(e){"number"!=typeof e&&(e=C.seekTime),z(me.media.currentTime+e)}function z(e){var t=0,r=me.media.paused;"number"==typeof e?t=e:"object"!=typeof e||"input"!==e.type&&"change"!==e.type||(t=e.target.value/e.target.max*me.media.duration),0>t?t=0:t>me.media.duration&&(t=me.media.duration);try{me.media.currentTime=t.toFixed(1)}catch(a){}"youtube"==me.type&&(me.embed.seekTo(t),r&&_(),v(me.media,"timeupdate")),n("Seeking to "+me.media.currentTime+" seconds"),w(t)}function X(){f(me.container,C.classes.playing,!me.media.paused),f(me.container,C.classes.stopped,me.media.paused)}function J(e){function t(){f(me.container,C.classes.hover,!0),window.clearTimeout(a),s||(a=window.setTimeout(function(){f(me.container,C.classes.hover,!1)},2e3))}function n(e){s="mouseenter"===e.type}var r=F.supportsFullScreen;e&&e.type===F.fullScreenEventName?me.isFullscreen=F.isFullScreen(me.container):r?(F.isFullScreen(me.container)?F.cancelFullScreen():F.requestFullScreen(me.container),me.isFullscreen=F.isFullScreen(me.container)):(me.isFullscreen=!me.isFullscreen,me.isFullscreen?(y(document,"keyup",$),document.body.style.overflow="hidden"):(b(document,"keyup",$),document.body.style.overflow="")),f(me.container,C.classes.fullscreen.active,me.isFullscreen),g(me.buttons.fullscreen,me.isFullscreen);var a,s=!1;C.fullscreen.hideControls&&(f(me.controls,C.classes.hover,!1),m(me.controls,"mouseenter mouseleave",n,me.isFullscreen),m(me.container,"mousemove",t,me.isFullscreen))}function $(e){27===(e.which||e.charCode||e.keyCode)&&me.isFullscreen&&J()}function G(e){"undefined"==typeof e&&(e=C.storage.enabled&&x().supported?window.localStorage[C.storage.key]||C.volume:C.volume),e>10&&(e=10),0>e&&(e=0),me.media.volume=parseFloat(e/10),"youtube"==me.type&&(me.embed.setVolume(100*me.media.volume),v(me.media,"volumechange")),me.media.muted&&e>0&&K()}function K(e){"boolean"!=typeof e&&(e=!me.media.muted),g(me.buttons.mute,e),me.media.muted=e,"youtube"===me.type&&(me.embed[me.media.muted?"mute":"unMute"](),v(me.media,"volumechange"))}function Q(){var e=me.media.muted?0:10*me.media.volume;me.supported.full&&me.volume&&(me.volume.value=e),C.storage.enabled&&x().supported&&window.localStorage.setItem(C.storage.key,e),f(me.container,C.classes.muted,0===e),me.supported.full&&me.buttons.mute&&g(me.buttons.mute,0===e)}function Z(e){me.supported.full&&me.buttons.captions&&("boolean"!=typeof e&&(e=-1===me.container.className.indexOf(C.classes.captions.active)),g(me.buttons.captions,e),f(me.container,C.classes.captions.active,e))}function ee(e){var t="waiting"===e.type;clearTimeout(me.loadingTimer),me.loadingTimer=setTimeout(function(){f(me.container,C.classes.loading,t)},t?250:0)}function te(e){var t=me.progress.played.bar,n=me.progress.played.text,r=0;if(e)switch(e.type){case"timeupdate":case"seeking":r=h(me.media.currentTime,me.media.duration),"timeupdate"==e.type&&me.buttons.seek&&(me.buttons.seek.value=r);break;case"change":case"input":r=e.target.value;break;case"playing":case"progress":t=me.progress.buffer.bar,n=me.progress.buffer.text,r=function(){var e=me.media.buffered;return e&&e.length?h(e.end(0),me.media.duration):"number"==typeof e?100*e:0}()}t&&(t.value=r),n&&(n.innerHTML=r)}function ne(e,t){if(t){me.secs=parseInt(e%60),me.mins=parseInt(e/60%60),me.hours=parseInt(e/60/60%60);var n=parseInt(me.media.duration/60/60%60)>0;me.secs=("0"+me.secs).slice(-2),me.mins=("0"+me.mins).slice(-2),t.innerHTML=(n?me.hours+":":"")+me.mins+":"+me.secs}}function re(){var e=me.media.duration||0;!me.duration&&C.displayDuration&&me.media.paused&&ne(e,me.currentTime),me.duration&&ne(e,me.duration)}function ae(e){ne(me.media.currentTime,me.currentTime),te(e)}function se(){for(var e=me.media.querySelectorAll("source"),t=e.length-1;t>=0;t--)c(e[t]);me.media.removeAttribute("src")}function oe(e){if(e.src){var t=document.createElement("source");d(t,e),p(me.media,t)}}function ie(e){if("youtube"===me.type&&"string"==typeof e)return me.embed.destroy(),H(e),void ae();if(_(),z(),se(),"string"==typeof e)oe({src:e});else if(e.constructor===Array)for(var t in e)oe(e[t]);me.supported.full&&(ae(),X()),me.media.load(),(null!==me.media.getAttribute("autoplay")||C.autoplay)&&D()}function ue(e){return null!==e&&void 0!==e?ie(e):"youtube"===me.type?me.embed.getVideoUrl():me.media.currentSrc}function le(e){"video"===me.type&&me.media.setAttribute("poster",e)}function ce(){function e(){var e=document.activeElement;e=e&&e!=document.body?document.querySelector(":focus"):null;for(var t in me.buttons){var n=me.buttons[t];f(n,"tab-focus",n===e)}}var t="IE"==me.browser.name?"change":"input";y(window,"keyup",function(t){var n=t.keyCode?t.keyCode:t.which;9==n&&e()});for(var n in me.buttons){var r=me.buttons[n];y(r,"blur",function(){f(r,"tab-focus",!1)})}y(me.buttons.play,"click",function(){D(),setTimeout(function(){me.buttons.pause.focus()},100)}),y(me.buttons.pause,"click",function(){_(),setTimeout(function(){me.buttons.play.focus()},100)}),y(me.buttons.restart,"click",z),y(me.buttons.rewind,"click",U),y(me.buttons.forward,"click",Y),y(me.buttons.seek,t,z),y(me.volume,t,function(){G(this.value)}),y(me.buttons.mute,"click",K),y(me.buttons.fullscreen,"click",J),F.supportsFullScreen&&y(document,F.fullScreenEventName,J),y(me.media,"timeupdate seeking",ae),y(me.media,"timeupdate",w),y(me.media,"loadedmetadata",re),y(me.buttons.captions,"click",Z),y(me.media,"ended",function(){"video"===me.type&&(me.captionsContainer.innerHTML=""),X()}),y(me.media,"progress playing",te),y(me.media,"volumechange",Q),y(me.media,"play pause",X),y(me.media,"waiting canplay seeked",ee),"video"===me.type&&C.click&&y(me.videoContainer,"click",function(){me.media.paused?v(me.buttons.play,"click"):me.media.ended?(z(),v(me.buttons.play,"click")):v(me.buttons.pause,"click")})}function pe(){if(!me.init)return null;if(me.container.setAttribute("class",C.selectors.container.replace(".","")),me.init=!1,c(M(C.selectors.controls)),"youtube"===me.type)return void me.embed.destroy();"video"===me.type&&(c(M(C.selectors.captions)),l(me.videoContainer)),me.media.setAttribute("controls","");var e=me.media.cloneNode(!0);me.media.parentNode.replaceChild(e,me.media)}function de(){if(me.init)return null;F=k(),me.browser=r(),me.media=me.container.querySelectorAll("audio, video, div")[0];var t=me.media.tagName.toLowerCase();if("div"===t?me.type=me.media.getAttribute("data-type"):me.type=t,me.supported=e.supported(me.type),!me.supported.basic)return!1;if(n(me.browser.name+" "+me.browser.version),q(),"video"==me.type||"audio"==me.type){if(!me.supported.full)return void(me.init=!0);fe(),C.displayDuration&&re(),V()}me.init=!0}function fe(){return L(),O()?(B(),G(),Q(),j(),void ce()):!1}var me=this;return me.container=o,de(),me.init?{media:me.media,play:D,pause:_,restart:z,rewind:U,forward:Y,seek:z,source:ue,poster:le,setVolume:G,togglePlay:W,toggleMute:K,toggleCaptions:Z,toggleFullscreen:J,isFullscreen:function(){return me.isFullscreen||!1},support:function(e){return a(me,e)},destroy:pe,restore:de}:{}}var F,C,S={youtube:[]},E={enabled:!0,debug:!1,autoplay:!1,seekTime:10,volume:5,click:!0,tooltips:!1,displayDuration:!0,iconPrefix:"icon",selectors:{container:".player",controls:".player-controls",labels:"[data-player] .sr-only, label .sr-only",buttons:{seek:'[data-player="seek"]',play:'[data-player="play"]',pause:'[data-player="pause"]',restart:'[data-player="restart"]',rewind:'[data-player="rewind"]',forward:'[data-player="fast-forward"]',mute:'[data-player="mute"]',volume:'[data-player="volume"]',captions:'[data-player="captions"]',fullscreen:'[data-player="fullscreen"]'},progress:{container:".player-progress",buffer:".player-progress-buffer",played:".player-progress-played"},captions:".player-captions",currentTime:".player-current-time",duration:".player-duration"},classes:{videoWrapper:"player-video-wrapper",embedWrapper:"player-video-embed",type:"player-{0}",stopped:"stopped",playing:"playing",muted:"muted",loading:"loading",tooltip:"player-tooltip",hidden:"sr-only",hover:"player-hover",captions:{enabled:"captions-enabled",active:"captions-active"},fullscreen:{enabled:"fullscreen-enabled",active:"fullscreen-active",hideControls:"fullscreen-hide-controls"}},captions:{defaultActive:!1},fullscreen:{enabled:!0,fallback:!0,hideControls:!0},storage:{enabled:!0,key:"plyr_volume"},controls:["restart","rewind","play","fast-forward","current-time","duration","mute","volume","captions","fullscreen"],i18n:{restart:"Restart",rewind:"Rewind {seektime} secs",play:"Play",pause:"Pause",forward:"Forward {seektime} secs",played:"played",buffered:"buffered",currentTime:"Current time",duration:"Duration",volume:"Volume",toggleMute:"Toggle Mute",toggleCaptions:"Toggle Captions",toggleFullscreen:"Toggle Fullscreen"}};e.supported=function(e){var t,n,a=r(),s="IE"===a.name&&a.version<=9,o=/iPhone|iPod/i.test(navigator.userAgent),i=!!document.createElement("audio").canPlayType,u=!!document.createElement("video").canPlayType;switch(e){case"video":t=u,n=t&&!s&&!o;break;case"audio":t=i,n=t&&!s;break;case"youtube":t=!0,n=!s&&!o;break;default:t=i&&u,n=t&&!s}return{basic:t,full:n}},e.setup=function(t){if(C=w(E,t),!C.enabled||!e.supported().basic)return!1;for(var n=document.querySelectorAll(C.selectors.container),r=[],a=n.length-1;a>=0;a--){var s=n[a];if("undefined"==typeof s.plyr){var o=new T(s);s.plyr=Object.keys(o).length?o:!1,"function"==typeof C.onSetup&&C.onSetup.apply(s.plyr)}r.push(s.plyr)}return r}}(this.plyr=this.plyr||{}); \ No newline at end of file
+!function(e,t){"use strict";"function"==typeof define&&define.amd?define(null,function(){t(e,document)}):"object"==typeof module?module.exports=t(e,document):e.plyr=t(e,document)}(this,function(e,t){"use strict";function n(){var e=['<div class="plyr__controls">','<div 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.5" value="0" data-plyr="seek">','<progress class="plyr__progress--played" max="100" value="0">',"<span>0</span>% "+A.i18n.played,"</progress>",'<progress class="plyr__progress--buffer" max="100" value="0">',"<span>0</span>% "+A.i18n.buffered,"</progress>","</div>",'<span class="plyr__controls--left">'];return i(A.controls,"restart")&&e.push('<button type="button" data-plyr="restart">','<svg><use xlink:href="#'+A.iconPrefix+'-restart" /></svg>','<span class="plyr__sr-only">'+A.i18n.restart+"</span>","</button>"),i(A.controls,"rewind")&&e.push('<button type="button" data-plyr="rewind">','<svg><use xlink:href="#'+A.iconPrefix+'-rewind" /></svg>','<span class="plyr__sr-only">'+A.i18n.rewind+"</span>","</button>"),i(A.controls,"play")&&e.push('<button type="button" data-plyr="play">','<svg><use xlink:href="#'+A.iconPrefix+'-play" /></svg>','<span class="plyr__sr-only">'+A.i18n.play+"</span>","</button>",'<button type="button" data-plyr="pause">','<svg><use xlink:href="#'+A.iconPrefix+'-pause" /></svg>','<span class="plyr__sr-only">'+A.i18n.pause+"</span>","</button>"),i(A.controls,"fast-forward")&&e.push('<button type="button" data-plyr="fast-forward">','<svg><use xlink:href="#'+A.iconPrefix+'-fast-forward" /></svg>','<span class="plyr__sr-only">'+A.i18n.forward+"</span>","</button>"),i(A.controls,"current-time")&&e.push('<span class="plyr__time">','<span class="plyr__sr-only">'+A.i18n.currentTime+"</span>",'<span class="plyr__time--current">00:00</span>',"</span>"),i(A.controls,"duration")&&e.push('<span class="plyr__time">','<span class="plyr__sr-only">'+A.i18n.duration+"</span>",'<span class="plyr__time--duration">00:00</span>',"</span>"),e.push("</span>",'<span class="plyr__controls--right">'),i(A.controls,"mute")&&e.push('<button type="button" data-plyr="mute">','<svg class="icon--muted"><use xlink:href="#'+A.iconPrefix+'-muted" /></svg>','<svg><use xlink:href="#'+A.iconPrefix+'-volume" /></svg>','<span class="plyr__sr-only">'+A.i18n.toggleMute+"</span>","</button>"),i(A.controls,"volume")&&e.push('<label for="volume{id}" class="plyr__sr-only">'+A.i18n.volume+"</label>",'<input id="volume{id}" class="plyr__volume" type="range" min="0" max="10" value="5" data-plyr="volume">'),i(A.controls,"captions")&&e.push('<button type="button" data-plyr="captions">','<svg class="icon--captions-on"><use xlink:href="#'+A.iconPrefix+'-captions-on" /></svg>','<svg><use xlink:href="#'+A.iconPrefix+'-captions-off" /></svg>','<span class="plyr__sr-only">'+A.i18n.toggleCaptions+"</span>","</button>"),i(A.controls,"fullscreen")&&e.push('<button type="button" data-plyr="fullscreen">','<svg class="icon--exit-fullscreen"><use xlink:href="#'+A.iconPrefix+'-exit-fullscreen" /></svg>','<svg><use xlink:href="#'+A.iconPrefix+'-enter-fullscreen" /></svg>','<span class="plyr__sr-only">'+A.i18n.toggleFullscreen+"</span>","</button>"),e.push("</span>","</div>"),e.join("")}function r(t,n){A.debug&&e.console&&console[n?"error":"log"](t)}function a(){var e,n,r,a=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=a.indexOf("MSIE"))?(s="IE",o=a.substring(n+5)):-1!==(n=a.indexOf("Chrome"))?(s="Chrome",o=a.substring(n+7)):-1!==(n=a.indexOf("Safari"))?(s="Safari",o=a.substring(n+7),-1!==(n=a.indexOf("Version"))&&(o=a.substring(n+8))):-1!==(n=a.indexOf("Firefox"))?(s="Firefox",o=a.substring(n+8)):(e=a.lastIndexOf(" ")+1)<(n=a.lastIndexOf("/"))&&(s=a.substring(e,n),o=a.substring(n+1),s.toLowerCase()==s.toUpperCase()&&(s=navigator.appName)),-1!==(r=o.indexOf(";"))&&(o=o.substring(0,r)),-1!==(r=o.indexOf(" "))&&(o=o.substring(0,r)),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 s(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 o(e){if(!t.querySelectorAll('script[src="'+e+'"]').length){var n=t.createElement("script");n.src=e;var r=t.getElementsByTagName("script")[0];r.parentNode.insertBefore(n,r)}}function i(e,t){return Array.prototype.indexOf&&-1!=e.indexOf(t)}function l(e,t,n){return e.replace(new RegExp(t.replace(/([.*+?\^=!:${}()|\[\]\/\\])/g,"\\$1"),"g"),n)}function u(e,t){e.length||(e=[e]);for(var n=e.length-1;n>=0;n--){var r=n>0?t.cloneNode(!0):t,a=e[n],s=a.parentNode,o=a.nextSibling;r.appendChild(a),o?s.insertBefore(r,o):s.appendChild(r)}}function c(e){for(var t=e.parentNode;e.firstChild;)t.insertBefore(e.firstChild,e);t.removeChild(e)}function p(e){e.parentNode.removeChild(e)}function d(e,t){e.insertBefore(t,e.firstChild)}function f(e,t){for(var n in t)e.setAttribute(n,"boolean"==typeof t[n]&&t[n]?"":t[n])}function m(e,n,r){var a=t.createElement(e);f(a,r),d(n,a)}function y(e){return e.replace(".","")}function b(e,t,n){e&&e.classList[n?"add":"remove"](t)}function v(e,t){return e?e.classList.contains(t):!1}function g(e,t,n,r){var a=t.split(" ");if(e instanceof NodeList)for(var s=0;s<e.length;s++)e[s]instanceof Node&&g(e[s],arguments[1],arguments[2],arguments[3]);else for(var o=0;o<a.length;o++)e[r?"addEventListener":"removeEventListener"](a[o],n,!1)}function h(e,t,n){e&&g(e,t,n,!0)}function k(e,t,n){e&&g(e,t,n,!1)}function T(e,n){if(e&&n){var r=t.createEvent("MouseEvents");r.initEvent(n,!0,!0),e.dispatchEvent(r)}}function x(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 _(e,t){for(var n in t)t[n]&&t[n].constructor&&t[n].constructor===Object?(e[n]=e[n]||{},_(e[n],t[n])):e[n]=t[n];return e}function F(){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 r=0,a=n.length;a>r;r++){if(e.prefix=n[r],"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 C(){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 S(_){function S(e){if(!Te.usingTextTracks&&"video"===Te.type&&Te.supported.full&&(Te.subcount=0,e="number"==typeof e?e:Te.media.currentTime,Te.captions[Te.subcount])){for(;L(Te.captions[Te.subcount][0])<e.toFixed(1);)if(Te.subcount++,Te.subcount>Te.captions.length-1){Te.subcount=Te.captions.length-1;break}if(Te.media.currentTime.toFixed(1)>=M(Te.captions[Te.subcount][0])&&Te.media.currentTime.toFixed(1)<=L(Te.captions[Te.subcount][0])){Te.currentCaption=Te.captions[Te.subcount][1];var t=Te.currentCaption.trim();Te.captionsContainer.innerHTML!=t&&(Te.captionsContainer.innerHTML="",Te.captionsContainer.innerHTML=t)}else Te.captionsContainer.innerHTML=""}}function P(){Te.buttons.captions&&(b(Te.container,A.classes.captions.enabled,!0),A.captions.defaultActive&&(b(Te.container,A.classes.captions.active,!0),x(Te.buttons.captions,!0)))}function M(e){var t=[];return t=e.split(" --> "),O(t[0])}function L(e){var t=[];return t=e.split(" --> "),O(t[1])}function O(e){if(null===e||void 0===e)return 0;var t,n=[],r=[];return n=e.split(","),r=n[0].split(":"),t=Math.floor(60*r[0]*60)+Math.floor(60*r[1])+Math.floor(r[2])}function V(e){return Te.container.querySelectorAll(e)}function q(e){return V(e)[0]}function H(){try{return e.self!==e.top}catch(t){return!0}}function R(){var e=A.html;r("Injecting custom controls."),e||(e=n()),e=l(e,"{seektime}",A.seekTime),e=l(e,"{id}",Math.floor(1e4*Math.random()));var a;if(null!==A.selectors.controls.container&&(a=A.selectors.controls.container,"string"==typeof selector&&(a=t.querySelector(a))),a instanceof HTMLElement||(a=Te.container),a.insertAdjacentHTML("beforeend",e),A.tooltips)for(var s=V(A.selectors.labels),o=s.length-1;o>=0;o--){var i=s[o];b(i,A.classes.hidden,!1),b(i,A.classes.tooltip,!0)}}function j(){try{return Te.controls=q(A.selectors.controls.wrapper),Te.buttons={},Te.buttons.seek=q(A.selectors.buttons.seek),Te.buttons.play=q(A.selectors.buttons.play),Te.buttons.pause=q(A.selectors.buttons.pause),Te.buttons.restart=q(A.selectors.buttons.restart),Te.buttons.rewind=q(A.selectors.buttons.rewind),Te.buttons.forward=q(A.selectors.buttons.forward),Te.buttons.fullscreen=q(A.selectors.buttons.fullscreen),Te.buttons.volume=q(A.selectors.buttons.volume),Te.buttons.mute=q(A.selectors.buttons.mute),Te.buttons.captions=q(A.selectors.buttons.captions),Te.checkboxes=V('[type="checkbox"]'),Te.progress={},Te.progress.container=q(A.selectors.progress.container),Te.progress.buffer={},Te.progress.buffer.bar=q(A.selectors.progress.buffer),Te.progress.buffer.text=Te.progress.buffer.bar&&Te.progress.buffer.bar.getElementsByTagName("span")[0],Te.progress.played={},Te.progress.played.bar=q(A.selectors.progress.played),Te.progress.played.text=Te.progress.played.bar&&Te.progress.played.bar.getElementsByTagName("span")[0],Te.volume=q(A.selectors.buttons.volume),Te.duration=q(A.selectors.duration),Te.currentTime=q(A.selectors.currentTime),Te.seekTime=V(A.selectors.seekTime),!0}catch(e){return r("It looks like there's a problem with your controls html. Bailing.",!0),Te.media.setAttribute("controls",""),!1}}function D(e){var t=A.i18n.play;"undefined"!=typeof A.title&&A.title.length&&(t+=", "+A.title),Te.buttons.play&&Te.buttons.play.setAttribute("aria-label",t),e instanceof HTMLElement&&e.setAttribute("title",A.i18n.frameTitle.replace("{title}",A.title))}function B(){if(!Te.media)return r("No audio or video element found!",!0),!1;if(Te.supported.full&&(Te.media.removeAttribute("controls"),b(Te.container,A.classes.type.replace("{0}",Te.type),!0),b(Te.container,A.classes.stopped,A.autoplay),b(Te.container,A.classes.isIos,Te.browser.ios),b(Te.container,A.classes.isTouch,Te.browser.touch),"video"===Te.type)){var e=t.createElement("div");e.setAttribute("class",A.classes.videoWrapper),u(Te.media,e),Te.videoContainer=e}i(A.types.embed,Te.type)?(Y(),Te.embedId=null):A.autoplay&&J()}function Y(){for(var n=t.createElement("div"),r=Te.embedId,a=Te.type+"-"+Math.floor(1e4*Math.random()),s=V('[id^="'+Te.type+'-"]'),i=s.length-1;i>=0;i--)p(s[i]);if(b(Te.media,A.classes.videoWrapper,!0),b(Te.media,A.classes.embedWrapper,!0),"youtube"===Te.type)Te.media.appendChild(n),n.setAttribute("id",a),"object"==typeof YT?$(r,n):(o(A.urls.youtube.api),e.onYouTubeReadyCallbacks=e.onYouTubeReadyCallbacks||[],e.onYouTubeReadyCallbacks.push(function(){$(r,n)}),e.onYouTubeIframeAPIReady=function(){e.onYouTubeReadyCallbacks.forEach(function(e){e()})});else if("vimeo"===Te.type){var l=t.createElement("iframe");l.loaded=!1,h(l,"load",function(){l.loaded=!0}),f(l,{src:"https://player.vimeo.com/video/"+r+"?player_id="+a+"&api=1&badge=0&byline=0&portrait=0&title=0",id:a,webkitallowfullscreen:"",mozallowfullscreen:"",allowfullscreen:"",frameborder:0}),n.appendChild(l),Te.media.appendChild(n),"$f"in e||o(A.urls.vimeo.api);var u=e.setInterval(function(){"$f"in e&&l.loaded&&(e.clearInterval(u),z.call(l))},50)}}function W(){Te.supported.full&&(Te.container.querySelectorAll(A.selectors.controls.wrapper).length||he()),D(q("iframe")),se(),oe()}function $(t,n){"timer"in Te||(Te.timer={}),Te.embed=new YT.Player(n.id,{videoId:t,playerVars:{autoplay:A.autoplay?1:0,controls:Te.supported.full?0:1,rel:0,showinfo:0,iv_load_policy:3,cc_load_policy:A.captions.defaultActive?1:0,cc_lang_pref:"en",wmode:"transparent",modestbranding:1,disablekb:1,origin:"*"},events:{onReady:function(t){var n=t.target;Te.media.play=function(){n.playVideo(),Te.media.paused=!1},Te.media.pause=function(){n.pauseVideo(),Te.media.paused=!0},Te.media.stop=function(){n.stopVideo(),Te.media.paused=!0},Te.media.duration=n.getDuration(),Te.media.paused=!A.autoplay,Te.media.currentTime=n.getCurrentTime(),Te.media.muted=n.isMuted(),T(Te.media,"timeupdate"),e.clearInterval(Te.timer.buffering),Te.timer.buffering=e.setInterval(function(){Te.media.buffered=n.getVideoLoadedFraction(),T(Te.media,"progress"),1===Te.media.buffered&&e.clearInterval(Te.timer.buffering)},200),W(),A.displayDuration&&pe()},onStateChange:function(t){var n=t.target;switch(e.clearInterval(Te.timer.playing),t.data){case 0:Te.media.paused=!0,T(Te.media,"ended");break;case 1:Te.media.paused=!1,T(Te.media,"play"),Te.timer.playing=e.setInterval(function(){Te.media.currentTime=n.getCurrentTime(),T(Te.media,"timeupdate")},200);break;case 2:Te.media.paused=!0,T(Te.media,"pause")}}}})}function z(){Te.embed=$f(this),Te.embed.addEvent("ready",function(){Te.media.play=function(){Te.embed.api("play"),Te.media.paused=!1},Te.media.pause=function(){Te.embed.api("pause"),Te.media.paused=!0},Te.media.stop=function(){Te.embed.api("stop"),Te.media.paused=!0},Te.media.paused=!A.autoplay,Te.media.currentTime=0,W(),Te.embed.api("getCurrentTime",function(e){Te.media.currentTime=e,T(Te.media,"timeupdate")}),Te.embed.api("getDuration",function(e){Te.media.duration=e,Te.supported.full&&A.displayDuration&&pe()}),Te.embed.addEvent("play",function(){Te.media.paused=!1,T(Te.media,"play")}),Te.embed.addEvent("pause",function(){Te.media.paused=!0,T(Te.media,"pause")}),Te.embed.addEvent("playProgress",function(e){Te.media.currentTime=e.seconds,T(Te.media,"timeupdate")}),Te.embed.addEvent("loadProgress",function(e){Te.media.buffered=e.percent,T(Te.media,"progress")}),Te.embed.addEvent("finish",function(){Te.media.paused=!0,T(Te.media,"ended")})})}function U(){if("video"===Te.type){q(A.selectors.captions)||Te.videoContainer.insertAdjacentHTML("afterbegin",'<div class="'+y(A.selectors.captions)+'"><span></span></div>'),Te.captionsContainer=q(A.selectors.captions).querySelector("span"),Te.usingTextTracks=!1,Te.media.textTracks&&(Te.usingTextTracks=!0);for(var e,t="",n=Te.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(Te.captionExists=!0,""===t?(Te.captionExists=!1,r("No caption track found.")):r("Caption track found; URI: "+t),Te.captionExists){for(var s=Te.media.textTracks,o=0;o<s.length;o++)s[o].mode="hidden";if(P(Te),("IE"===Te.browser.name&&Te.browser.version>=10||"Firefox"===Te.browser.name&&Te.browser.version>=31||"Chrome"===Te.browser.name&&Te.browser.version>=43||"Safari"===Te.browser.name&&Te.browser.version>=7)&&(r("Detected unsupported browser for HTML5 captions. Using fallback."),Te.usingTextTracks=!1),Te.usingTextTracks){r("TextTracks supported.");for(var i=0;i<s.length;i++){var l=s[i];("captions"===l.kind||"subtitles"===l.kind)&&h(l,"cuechange",function(){Te.captionsContainer.innerHTML="",this.activeCues[0]&&this.activeCues[0].hasOwnProperty("text")&&Te.captionsContainer.appendChild(this.activeCues[0].getCueAsHTML().trim())})}}else if(r("TextTracks not supported so rendering captions manually."),Te.currentCaption="",Te.captions=[],""!==t){var u=new XMLHttpRequest;u.onreadystatechange=function(){if(4===u.readyState)if(200===u.status){var e,t=[],n=u.responseText;t=n.split("\n\n");for(var a=0;a<t.length;a++)e=t[a],Te.captions[a]=[],Te.captions[a]=e.split("\n");Te.captions.shift(),r("Successfully loaded the caption file via AJAX.")}else r("There was a problem loading the caption file via AJAX.",!0)},u.open("get",t,!0),u.send()}if("Safari"===Te.browser.name&&Te.browser.version>=7){r("Safari 7+ detected; removing track from DOM."),s=Te.media.getElementsByTagName("track");for(var c=0;c<s.length;c++)Te.media.removeChild(s[c])}}else b(Te.container,A.classes.captions.enabled)}}function X(){if(("audio"!=Te.type||A.fullscreen.allowAudio)&&A.fullscreen.enabled){var e=E.supportsFullScreen;e||A.fullscreen.fallback&&!H()?(r((e?"Native":"Fallback")+" fullscreen enabled."),b(Te.container,A.classes.fullscreen.enabled,!0)):r("Fullscreen not supported and fallback disabled."),x(Te.buttons.fullscreen,!1),A.fullscreen.hideControls&&b(Te.container,A.classes.fullscreen.hideControls,!0)}}function J(){"play"in Te.media&&Te.media.play()}function K(){"pause"in Te.media&&Te.media.pause()}function G(e){e===!0?J():e===!1?K():Te.media[Te.media.paused?"play":"pause"]()}function Q(e){"number"!=typeof e&&(e=A.seekTime),ee(Te.media.currentTime-e)}function Z(e){"number"!=typeof e&&(e=A.seekTime),ee(Te.media.currentTime+e)}function ee(e){var t=0,n=Te.media.paused;"number"==typeof e?t=e:"object"!=typeof e||"input"!==e.type&&"change"!==e.type||(t=e.target.value/e.target.max*Te.media.duration),0>t?t=0:t>Te.media.duration&&(t=Te.media.duration);try{Te.media.currentTime=t.toFixed(1)}catch(a){}"embed"in Te&&("youtube"===Te.type&&Te.embed.seekTo(t),"vimeo"===Te.type&&Te.embed.api("seekTo",t),T(Te.media,"timeupdate"),n&&K()),r("Seeking to "+Te.media.currentTime+" seconds"),S(t)}function te(){b(Te.container,A.classes.playing,!Te.media.paused),b(Te.container,A.classes.stopped,Te.media.paused)}function ne(n){function r(){b(Te.container,A.classes.hover,!0),e.clearTimeout(o),i||(o=e.setTimeout(function(){b(Te.container,A.classes.hover,!1)},2e3))}function a(e){i="mouseenter"===e.type}var s=E.supportsFullScreen;n&&n.type===E.fullScreenEventName?Te.isFullscreen=E.isFullScreen(Te.container):s?(E.isFullScreen(Te.container)?E.cancelFullScreen():E.requestFullScreen(Te.container),Te.isFullscreen=E.isFullScreen(Te.container)):(Te.isFullscreen=!Te.isFullscreen,Te.isFullscreen?(h(t,"keyup",re),t.body.style.overflow="hidden"):(k(t,"keyup",re),t.body.style.overflow="")),b(Te.container,A.classes.fullscreen.active,Te.isFullscreen),x(Te.buttons.fullscreen,Te.isFullscreen);var o,i=!1;A.fullscreen.hideControls&&(b(Te.controls,A.classes.hover,!1),g(Te.controls,"mouseenter mouseleave",a,Te.isFullscreen),g(Te.container,"mousemove",r,Te.isFullscreen))}function re(e){27===(e.which||e.charCode||e.keyCode)&&Te.isFullscreen&&ne()}function ae(e){"boolean"!=typeof e&&(e=!Te.media.muted),x(Te.buttons.mute,e),Te.media.muted=e,"youtube"===Te.type&&(Te.embed[Te.media.muted?"mute":"unMute"](),T(Te.media,"volumechange")),"vimeo"===Te.type&&(Te.media.muted?Te.embed.api("setVolume",0):Te.embed.api("setVolume",parseFloat(A.volume/10)),T(Te.media,"volumechange"))}function se(t){"undefined"==typeof t&&(t=A.storage.enabled&&C().supported?e.localStorage[A.storage.key]||A.volume:A.volume),t>10&&(t=10),0>t&&(t=0),Te.media.volume=parseFloat(t/10),A.volume=t,"youtube"===Te.type&&Te.embed.setVolume(100*Te.media.volume),"vimeo"===Te.type&&Te.embed.api("setVolume",Te.media.volume),"embed"in Te&&T(Te.media,"volumechange"),Te.media.muted&&t>0&&ae()}function oe(){var t=Te.media.muted?0:10*Te.media.volume;Te.supported.full&&Te.volume&&(Te.volume.value=t),A.storage.enabled&&C().supported&&e.localStorage.setItem(A.storage.key,t),b(Te.container,A.classes.muted,0===t),Te.supported.full&&Te.buttons.mute&&x(Te.buttons.mute,0===t)}function ie(e){Te.supported.full&&Te.buttons.captions&&("boolean"!=typeof e&&(e=-1===Te.container.className.indexOf(A.classes.captions.active)),Te.captionsEnabled=e,x(Te.buttons.captions,Te.captionsEnabled),b(Te.container,A.classes.captions.active,Te.captionsEnabled))}function le(e){var t="waiting"===e.type;clearTimeout(Te.loadingTimer),Te.loadingTimer=setTimeout(function(){b(Te.container,A.classes.loading,t)},t?250:0)}function ue(e){var t=Te.progress.played.bar,n=Te.progress.played.text,r=0;if(e)switch(e.type){case"timeupdate":case"seeking":r=w(Te.media.currentTime,Te.media.duration),"timeupdate"==e.type&&Te.buttons.seek&&(Te.buttons.seek.value=r);break;case"change":case"input":r=e.target.value;break;case"playing":case"progress":t=Te.progress.buffer.bar,n=Te.progress.buffer.text,r=function(){var e=Te.media.buffered;return e&&e.length?w(e.end(0),Te.media.duration):"number"==typeof e?100*e:0}()}t&&(t.value=r),n&&(n.innerHTML=r)}function ce(e,t){if(t){isNaN(e)&&(e=0),Te.secs=parseInt(e%60),Te.mins=parseInt(e/60%60),Te.hours=parseInt(e/60/60%60);var n=parseInt(Te.media.duration/60/60%60)>0;Te.secs=("0"+Te.secs).slice(-2),Te.mins=("0"+Te.mins).slice(-2),t.innerHTML=(n?Te.hours+":":"")+Te.mins+":"+Te.secs}}function pe(){var e=Te.media.duration||0;!Te.duration&&A.displayDuration&&Te.media.paused&&ce(e,Te.currentTime),Te.duration&&ce(e,Te.duration)}function de(e){ce(Te.media.currentTime,Te.currentTime),ue(e)}function fe(e,t){if("string"==typeof t)m(e,Te.media,{src:t});else if(t.constructor===Array)for(var n=t.length-1;n>=0;n--)m(e,Te.media,t[n])}function me(n){if(!("undefined"!=typeof n&&"sources"in n&&n.sources.length))return void r("Invalid source format",!0);if(K(),"youtube"===Te.type?(Te.embed.destroy(),e.clearInterval(Te.timer.buffering),e.clearInterval(Te.timer.playing)):"video"===Te.type&&p(Te.videoContainer),p(Te.media),"type"in n&&(Te.type=n.type,"video"===Te.type)){var a=n.sources[0];"type"in a&&i(A.types.embed,a.type)&&(Te.type=a.type)}switch(Te.type){case"video":Te.media=t.createElement("video");break;case"audio":Te.media=t.createElement("audio");break;case"youtube":case"vimeo":Te.media=t.createElement("div"),Te.embedId=n.sources[0].src}d(Te.container,Te.media),i(A.types.html5,Te.type)&&(A.crossorigin&&Te.media.setAttribute("crossorigin",""),A.autoplay&&Te.media.setAttribute("autoplay",""),"poster"in n&&Te.media.setAttribute("poster",n.poster),A.loop&&Te.media.setAttribute("loop","")),Te.container.className=Te.originalClassName,b(Te.container,A.classes.fullscreen.active,Te.isFullscreen),b(Te.container,A.classes.captions.active,Te.captionsEnabled),A.autoplay=n.autoplay||A.autoplay,i(A.types.html5,Te.type)&&fe("source",n.sources),B(),ke(),i(A.types.html5,Te.type)&&(se(),oe(),Te.supported.full&&(de(),te()),"tracks"in n&&(fe("track",n.tracks),U()),Te.media.load(),A.autoplay&&J()),"title"in n&&(A.title=n.title,D())}function ye(e){"video"===Te.type&&Te.media.setAttribute("poster",e)}function be(){function n(e){e?J():K();var t=Te.buttons[e?"play":"pause"],n=Te.buttons[e?"pause":"play"];if(n){var r=v(t,A.classes.tabFocus);setTimeout(function(){n.focus(),r&&(b(t,A.classes.tabFocus,!1),b(n,A.classes.tabFocus,!0))},100)}}function r(){var e=t.activeElement;e&&e!=t.body?t.querySelector&&(e=t.querySelector(":focus")):e=null;for(var n in Te.buttons){var r=Te.buttons[n];b(r,A.classes.tabFocus,r===e)}}var a="IE"==Te.browser.name?"change":"input";h(e,"keyup",function(e){var t=e.keyCode?e.keyCode:e.which;9==t&&r()}),h(t.body,"click",function(){b(q("."+A.classes.tabFocus),A.classes.tabFocus,!1)});for(var s in Te.buttons){var o=Te.buttons[s];h(o,"blur",function(){b(o,"tab-focus",!1)})}h(Te.buttons.play,"click",function(){n(!0)}),h(Te.buttons.pause,"click",function(){n()}),h(Te.buttons.restart,"click",ee),h(Te.buttons.rewind,"click",Q),h(Te.buttons.forward,"click",Z),h(Te.buttons.seek,a,ee),h(Te.volume,a,function(){se(this.value)}),h(Te.buttons.mute,"click",ae),h(Te.buttons.fullscreen,"click",ne),E.supportsFullScreen&&h(t,E.fullScreenEventName,ne),h(Te.media,"timeupdate seeking",de),h(Te.media,"timeupdate",S),h(Te.media,"loadedmetadata",pe),h(Te.buttons.captions,"click",ie),h(Te.media,"ended",function(){"video"===Te.type&&(Te.captionsContainer.innerHTML=""),te()}),h(Te.media,"progress playing",ue),h(Te.media,"volumechange",oe),h(Te.media,"play pause",te),h(Te.media,"waiting canplay seeked",le),"video"===Te.type&&A.click&&h(Te.videoContainer,"click",function(){Te.media.paused?J():Te.media.ended?(ee(),J()):K()})}function ve(){if(!Te.init)return null;if(Te.container.setAttribute("class",y(A.selectors.container)),Te.init=!1,p(q(A.selectors.controls.wrapper)),"youtube"===Te.type)return void Te.embed.destroy();"video"===Te.type&&(p(q(A.selectors.captions)),c(Te.videoContainer)),Te.media.setAttribute("controls","");var e=Te.media.cloneNode(!0);Te.media.parentNode.replaceChild(e,Te.media)}function ge(){if(Te.init)return null;E=F(),Te.browser=a(),Te.media=Te.container.querySelectorAll("audio, video, div")[0],b(Te.container,N.selectors.container.replace(".",""),!0),Te.originalClassName=Te.container.className;var e=Te.media.tagName.toLowerCase();if("div"===e?(Te.type=Te.media.getAttribute("data-type"),Te.embedId=Te.media.getAttribute("data-video-id"),Te.media.removeAttribute("data-type"),Te.media.removeAttribute("data-video-id")):(Te.type=e,A.crossorigin=null!==Te.media.getAttribute("crossorigin"),A.autoplay=A.autoplay||null!==Te.media.getAttribute("autoplay"),A.loop=A.loop||null!==Te.media.getAttribute("loop")),Te.supported=I.supported(Te.type),!Te.supported.basic)return!1;if(r(Te.browser.name+" "+Te.browser.version),B(),"video"==Te.type||"audio"==Te.type){if(!Te.supported.full)return void(Te.init=!0);he(),A.displayDuration&&pe(),D()}Te.init=!0}function he(){return R(),j()?(U(),ke(),se(),void oe()):!1}function ke(){X(),be()}var Te=this;return Te.container=_,ge(),Te.init?{media:Te.media,play:J,pause:K,restart:ee,rewind:Q,forward:Z,seek:ee,source:me,poster:ye,setVolume:se,togglePlay:G,toggleMute:ae,toggleCaptions:ie,toggleFullscreen:ne,isFullscreen:function(){return Te.isFullscreen||!1},support:function(e){return s(Te,e)},destroy:ve,restore:ge}:{}}var E,A,I={},N={enabled:!0,debug:!1,autoplay:!1,loop:!1,seekTime:10,volume:5,click:!0,tooltips:!1,displayDuration:!0,iconPrefix:"icon",selectors:{container:".plyr",controls:{container:null,wrapper:".plyr__controls"},labels:"[data-plyr] .sr-only, label .sr-only",buttons:{seek:'[data-plyr="seek"]',play:'[data-plyr="play"]',pause:'[data-plyr="pause"]',restart:'[data-plyr="restart"]',rewind:'[data-plyr="rewind"]',forward:'[data-plyr="fast-forward"]',mute:'[data-plyr="mute"]',volume:'[data-plyr="volume"]',captions:'[data-plyr="captions"]',fullscreen:'[data-plyr="fullscreen"]'},progress:{container:".plyr__progress",buffer:".plyr__progress--buffer",played:".plyr__progress--played"},captions:".plyr__captions",currentTime:".plyr__time--current",duration:".plyr__time--duration"},classes:{videoWrapper:"plyr__video-wrapper",embedWrapper:"plyr__video-embed",type:"plyr--{0}",stopped:"plyr--stopped",playing:"plyr--playing",muted:"plyr--muted",loading:"plyr--loading",hover:"plyr--hover",tooltip:"plyr__tooltip",hidden:"plyr__sr-only",isIos:"plyr--is-ios",isTouch:"plyr--is-touch",captions:{enabled:"plyr--captions-enabled",active:"plyr--captions-active"},fullscreen:{enabled:"plyr--fullscreen-enabled",active:"plyr--fullscreen-active",hideControls:"plyr--fullscreen--hide-controls"},tabFocus:"tab-focus"},captions:{defaultActive:!1},fullscreen:{enabled:!0,fallback:!0,hideControls:!0,allowAudio:!1},storage:{enabled:!0,key:"plyr_volume"},controls:["restart","rewind","play","fast-forward","current-time","duration","mute","volume","captions","fullscreen"],i18n:{restart:"Restart",rewind:"Rewind {seektime} secs",play:"Play",pause:"Pause",forward:"Forward {seektime} secs",played:"played",buffered:"buffered",currentTime:"Current time",duration:"Duration",volume:"Volume",toggleMute:"Toggle Mute",toggleCaptions:"Toggle Captions",toggleFullscreen:"Toggle Fullscreen",frameTitle:"Player for {title}"},types:{embed:["youtube","vimeo"],html5:["video","audio"]},urls:{vimeo:{api:"http://cdn.plyr.io/froogaloop/1.0.0/plyr.froogaloop.js"},youtube:{api:"https://www.youtube.com/iframe_api"}}};return I.supported=function(e){var n,r,s=a(),o="IE"===s.name&&s.version<=9,i=/iPhone|iPod/i.test(navigator.userAgent),l=!!t.createElement("audio").canPlayType,u=!!t.createElement("video").canPlayType;switch(e){case"video":n=u,r=n&&!o&&!i;break;case"audio":n=l,r=n&&!o;break;case"vimeo":case"youtube":n=!0,r=!o&&!i;break;default:n=l&&u,r=n&&!o}return{basic:n,full:r}},I.setup=function(e,n){var r=[];if("string"==typeof e?e=t.querySelectorAll(e):e instanceof HTMLElement?e=[e]:e instanceof NodeList||"string"==typeof e||("undefined"==typeof n&&"object"==typeof e&&(n=e),e=t.querySelectorAll(N.selectors.container)),A=_(N,n),!A.enabled||!I.supported().basic||!e.length)return!1;for(var a=e.length-1;a>=0;a--){var s=e[a];if("undefined"==typeof s.plyr){var o=new S(s);s.plyr=Object.keys(o).length?o:!1,"function"==typeof A.onSetup&&A.onSetup.apply(s.plyr)}r.push(s.plyr)}return r},I}); \ No newline at end of file
diff --git a/dist/sprite.svg b/dist/sprite.svg
index b4909d2d..c1f8a560 100644
--- a/dist/sprite.svg
+++ b/dist/sprite.svg
@@ -1 +1 @@
-<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg"><symbol id="icon-captions-off" viewBox="0 0 18 18"><path d="M1 2c-.552 0-1 .448-1 1v12c0 .552.448 1 1 1h16c.552 0 1-.448 1-1V3c0-.552-.448-1-1-1H1zm1 12V4h14v10H2z"/></symbol><symbol id="icon-captions-on" viewBox="0 0 18 18"><path d="M1 2c-.552 0-1 .448-1 1v12c0 .552.448 1 1 1h16c.552 0 1-.448 1-1V3c0-.552-.448-1-1-1H1zm1 12V4h14v10H2z"/><path d="M3 11h3v2H3zM12 11h3v2h-3zM7 11h4v2H7z"/></symbol><symbol id="icon-enter-fullscreen" viewBox="0 0 18 18"><path d="M6.425 10.165l-2.57 2.57.018-1.847c-.003-.552-.45-1-1.002-1.002-.55-.003-.994.442-.99.992l-.024 4.22c0 .264.106.518.295.707.01.007.017.016.026.023.008.007.015.017.023.025.187.188.442.294.706.295l4.22-.023c.55.002.994-.442.992-.992-.003-.55-.45-1-1.002-1.002l-1.847.017 2.568-2.57c.387-.386.387-1.012 0-1.4l-.012-.012c-.387-.386-1.014-.386-1.4 0zM14.154 5.266l-.017 1.846c.003.552.45 1 1.002 1.002.55.003.994-.442.99-.992l.025-4.22c-.002-.264-.107-.518-.296-.707-.01-.007-.017-.016-.026-.023-.008-.007-.015-.017-.023-.025-.187-.188-.442-.294-.706-.295l-4.22.023c-.55-.002-.994.442-.99.992 0 .55.45 1 1 1.002l1.847-.017-2.568 2.57c-.387.386-.387 1.012 0 1.4.4.398 1.026.398 1.413.012l2.57-2.57z"/></symbol><symbol id="icon-exit-fullscreen" viewBox="0 0 18 18"><path d="M14.425 2.165l-2.57 2.57.018-1.847c-.003-.552-.45-1-1.002-1.002-.55-.003-.994.442-.99.992l-.024 4.22c0 .264.106.518.295.707.01.007.017.016.026.023.008.007.015.017.023.025.187.188.442.294.706.295l4.22-.023c.55.002.994-.442.992-.992-.003-.55-.45-1-1.002-1.002l-1.847.017 2.568-2.57c.387-.386.387-1.012 0-1.4l-.012-.012c-.387-.386-1.014-.386-1.4 0zM6.154 13.266l-.017 1.846c.003.552.45 1 1.002 1.002.55.003.994-.442.99-.992l.025-4.22c-.002-.264-.107-.518-.296-.707-.01-.007-.017-.016-.026-.023-.008-.007-.015-.017-.023-.025-.187-.188-.442-.294-.706-.295l-4.22.023c-.55-.002-.994.442-.99.992 0 .55.45 1 1 1.002l1.847-.017-2.568 2.57c-.387.386-.387 1.012 0 1.4.4.398 1.026.398 1.413.012l2.57-2.57z"/></symbol><symbol id="icon-fast-forward" viewBox="0 0 18 18"><path d="M17.57 8.246L7 2c-.552 0-1 .448-1 1v1.954L1 2c-.552 0-1 .448-1 1v12c0 .552.448 1 1 1l5-2.955V15c0 .552.448 1 1 1l10.57-6.246c.266-.158.43-.444.43-.754s-.164-.597-.43-.754zM6 10.722l-4 2.364V4.914l4 2.364v3.444zm2 2.364V4.914L14.915 9 8 13.086z"/></symbol><symbol id="icon-muted" viewBox="0 0 18 18"><path d="M9.214 2c-.11 0-.225.032-.334.1L4.832 4.91C4.75 4.97 4.65 5 4.55 5H.995C.446 5 0 5.448 0 6v6c0 .552.446 1 .996 1H4.55c.1 0 .2.03.282.09L8.88 15.9c.11.068.223.1.334.1.392 0 .747-.4.747-.95V2.95c0-.55-.354-.95-.746-.95zM7.97 12.834L5.58 11.177c-.166-.115-.364-.178-.566-.178H2.49c-.274 0-.497-.225-.497-.5v-3c0-.277.223-.5.498-.5h2.526c.202 0 .4-.063.566-.18L7.97 5.165v7.67zM14.934 8.8c-.086-1.75-1.514-2.992-2.507-3.65-.47-.312-1.094-.122-1.325.408l-.038.086c-.188.43-.045.94.336 1.194.706.473 1.586 1.247 1.624 2.065.032.676-.553 1.468-1.663 2.27-.397.288-.528.84-.284 1.275l.042.075c.266.475.866.624 1.3.312 1.74-1.25 2.586-2.606 2.516-4.037z"/><path d="M13.957 9.2c.086 1.747 1.514 2.99 2.507 3.648.47.312 1.094.122 1.325-.408l.037-.086c.188-.43.045-.94-.336-1.194-.705-.473-1.585-1.247-1.623-2.065-.032-.676.553-1.468 1.663-2.27.398-.288.53-.84.285-1.275l-.042-.075c-.266-.475-.866-.624-1.3-.312-1.74 1.25-2.586 2.606-2.516 4.037z"/></symbol><symbol id="icon-pause" viewBox="0 0 18 18"><path d="M2 4v10c0 2 2 2 2 2h2s2 0 2-2V4c0-2-2-2-2-2H4S2 2 2 4zm2 0h2v10H4V4zM10 4v10c0 2 2 2 2 2h2s2 0 2-2V4c0-2-2-2-2-2h-2s-2 0-2 2zm2 0h2v10h-2V4z"/></symbol><symbol id="icon-play" viewBox="0 0 18 18"><path d="M5 4.914L11.915 9 5 13.086V4.914zM4 2c-.552 0-1 .448-1 1v12c0 .552.448 1 1 1l10.57-6.246c.266-.158.43-.444.43-.754s-.164-.597-.43-.754L4 2z"/></symbol><symbol id="icon-restart" viewBox="0 0 18 18"><path d="M1 2c.552 0 1 .448 1 1v4.318L11 2c.552 0 1 .448 1 1v1.954L17 2c.552 0 1 .448 1 1v12c0 .552-.448 1-1 1l-5-2.955V15c0 .552-.448 1-1 1l-9-5.318V15c0 .552-.448 1-1 1s-1-.448-1-1V3c0-.552.448-1 1-1zm11 8.722l4 2.364V4.914l-4 2.364v3.444zm-2 2.364V4.914L3.085 9 10 13.086z"/></symbol><symbol id="icon-rewind" viewBox="0 0 18 21"><path d="M.43 10.754L11 17c.552 0 1-.448 1-1v-1.954L17 17c.552 0 1-.448 1-1V4c0-.552-.448-1-1-1l-5 2.955V4c0-.552-.448-1-1-1L.43 9.246C.165 9.404 0 9.69 0 10s.164.597.43.754zM12 8.278l4-2.364v8.172l-4-2.364V8.278zm-2-2.364v8.172L3.085 10 10 5.914z"/></symbol><symbol id="icon-volume" viewBox="0 0 18 18"><path d="M10.214 2c-.11 0-.225.032-.334.1L5.832 4.91C5.75 4.97 5.65 5 5.55 5H1.995C1.446 5 1 5.448 1 6v6c0 .552.446 1 .996 1H5.55c.1 0 .2.03.282.09L9.88 15.9c.11.068.223.1.334.1.392 0 .747-.4.747-.95V2.95c0-.55-.354-.95-.746-.95zM8.97 12.834L6.58 11.177c-.166-.115-.364-.178-.566-.178H3.49c-.274 0-.497-.225-.497-.5v-3c0-.277.223-.5.498-.5h2.526c.202 0 .4-.063.566-.18L8.97 5.165v7.67zM16.934 8.8c-.086-1.75-1.514-2.992-2.507-3.65-.47-.312-1.094-.122-1.325.408l-.038.086c-.188.43-.045.94.336 1.194.706.473 1.586 1.247 1.624 2.065.032.676-.553 1.468-1.663 2.27-.397.288-.528.84-.284 1.275l.042.075c.266.475.866.624 1.3.312 1.74-1.25 2.586-2.606 2.516-4.037z"/></symbol></svg> \ No newline at end of file
+<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg"><symbol id="icon-captions-off" viewBox="0 0 18 18"><title>Captions Off</title><path d="M1 2c-.552 0-1 .448-1 1v12c0 .552.448 1 1 1h16c.552 0 1-.448 1-1V3c0-.552-.448-1-1-1H1zm1 12V4h14v10H2z"/></symbol><symbol id="icon-captions-on" viewBox="0 0 18 18"><title>Captions On</title><path d="M1 2c-.552 0-1 .448-1 1v12c0 .552.448 1 1 1h16c.552 0 1-.448 1-1V3c0-.552-.448-1-1-1H1zm1 12V4h14v10H2z"/><path d="M3 11h3v2H3zM12 11h3v2h-3zM7 11h4v2H7z"/></symbol><symbol id="icon-enter-fullscreen" viewBox="0 0 18 18"><title>Enter Fullscreen</title><path d="M10.3 9.7c.7.677 1.4 0 1.4 0L16 5.4V10h2V3c0-.6-.4-1-1-1h-7v2h4.6l-4.3 4.3s-.7.723 0 1.4z"/><path d="M7 2v2H2v10h14v-1h2v2c0 .6-.4 1-1 1H1c-.6 0-1-.4-1-1V3c0-.6.4-1 1-1h6z"/></symbol><symbol id="icon-exit-fullscreen" viewBox="0 0 18 18"><title>Exit Fullscreen</title><path d="M7.7 8.3c-.7-.677-1.4 0-1.4 0L2 12.6V8H0v7c0 .6.4 1 1 1h7v-2H3.4l4.3-4.3s.7-.723 0-1.4z"/><path d="M11 16v-2h5V4H2v1H0V3c0-.6.4-1 1-1h16c.6 0 1 .4 1 1v12c0 .6-.4 1-1 1h-6z"/></symbol><symbol id="icon-fast-forward" viewBox="0 0 18 18"><title>Fast Forward</title><path d="M17.57 8.246L7 2c-.552 0-1 .448-1 1v1.954L1 2c-.552 0-1 .448-1 1v12c0 .552.448 1 1 1l5-2.955V15c0 .552.448 1 1 1l10.57-6.246c.266-.158.43-.444.43-.754s-.164-.597-.43-.754zM6 10.722l-4 2.364V4.914l4 2.364v3.444zm2 2.364V4.914L14.915 9 8 13.086z"/></symbol><symbol id="icon-muted" viewBox="0 0 18 18"><title>Muted</title><path d="M9.214 2c-.11 0-.225.032-.334.1L4.832 4.91C4.75 4.97 4.65 5 4.55 5H.995C.446 5 0 5.448 0 6v6c0 .552.446 1 .996 1H4.55c.1 0 .2.03.282.09L8.88 15.9c.11.068.223.1.334.1.392 0 .747-.4.747-.95V2.95c0-.55-.354-.95-.746-.95zM7.97 12.834L5.58 11.177c-.166-.115-.364-.178-.566-.178H2.49c-.274 0-.497-.225-.497-.5v-3c0-.277.223-.5.498-.5h2.526c.202 0 .4-.063.566-.18L7.97 5.165v7.67zM14.934 8.8c-.086-1.75-1.514-2.992-2.507-3.65-.47-.312-1.094-.122-1.325.408l-.038.086c-.188.43-.045.94.336 1.194.706.473 1.586 1.247 1.624 2.065.032.676-.553 1.468-1.663 2.27-.397.288-.528.84-.284 1.275l.042.075c.266.475.866.624 1.3.312 1.74-1.25 2.586-2.606 2.516-4.037z"/><path d="M13.957 9.2c.086 1.747 1.514 2.99 2.507 3.648.47.312 1.094.122 1.325-.408l.037-.086c.188-.43.045-.94-.336-1.194-.705-.473-1.585-1.247-1.623-2.065-.032-.676.553-1.468 1.663-2.27.398-.288.53-.84.285-1.275l-.042-.075c-.266-.475-.866-.624-1.3-.312-1.74 1.25-2.586 2.606-2.516 4.037z"/></symbol><symbol id="icon-pause" viewBox="0 0 18 18"><title>Pause</title><path d="M2 4v10c0 2 2 2 2 2h2s2 0 2-2V4c0-2-2-2-2-2H4S2 2 2 4zm2 0h2v10H4V4zM10 4v10c0 2 2 2 2 2h2s2 0 2-2V4c0-2-2-2-2-2h-2s-2 0-2 2zm2 0h2v10h-2V4z"/></symbol><symbol id="icon-play" viewBox="0 0 18 18"><title>Play</title><path d="M5 4.914L11.915 9 5 13.086V4.914zM4 2c-.552 0-1 .448-1 1v12c0 .552.448 1 1 1l10.57-6.246c.266-.158.43-.444.43-.754s-.164-.597-.43-.754L4 2z"/></symbol><symbol id="icon-restart" viewBox="0 0 18 18"><title>Restart</title><path d="M1 2c.552 0 1 .448 1 1v4.318L11 2c.552 0 1 .448 1 1v1.954L17 2c.552 0 1 .448 1 1v12c0 .552-.448 1-1 1l-5-2.955V15c0 .552-.448 1-1 1l-9-5.318V15c0 .552-.448 1-1 1s-1-.448-1-1V3c0-.552.448-1 1-1zm11 8.722l4 2.364V4.914l-4 2.364v3.444zm-2 2.364V4.914L3.085 9 10 13.086z"/></symbol><symbol id="icon-rewind" viewBox="0 0 18 21"><title>Rewind</title><path d="M.43 10.754L11 17c.552 0 1-.448 1-1v-1.954L17 17c.552 0 1-.448 1-1V4c0-.552-.448-1-1-1l-5 2.955V4c0-.552-.448-1-1-1L.43 9.246C.165 9.404 0 9.69 0 10s.164.597.43.754zM12 8.278l4-2.364v8.172l-4-2.364V8.278zm-2-2.364v8.172L3.085 10 10 5.914z"/></symbol><symbol id="icon-volume" viewBox="0 0 18 18"><title>Volume</title><path d="M10.214 2c-.11 0-.225.032-.334.1L5.832 4.91C5.75 4.97 5.65 5 5.55 5H1.995C1.446 5 1 5.448 1 6v6c0 .552.446 1 .996 1H5.55c.1 0 .2.03.282.09L9.88 15.9c.11.068.223.1.334.1.392 0 .747-.4.747-.95V2.95c0-.55-.354-.95-.746-.95zM8.97 12.834L6.58 11.177c-.166-.115-.364-.178-.566-.178H3.49c-.274 0-.497-.225-.497-.5v-3c0-.277.223-.5.498-.5h2.526c.202 0 .4-.063.566-.18L8.97 5.165v7.67zM16.934 8.8c-.086-1.75-1.514-2.992-2.507-3.65-.47-.312-1.094-.122-1.325.408l-.038.086c-.188.43-.045.94.336 1.194.706.473 1.586 1.247 1.624 2.065.032.676-.553 1.468-1.663 2.27-.397.288-.528.84-.284 1.275l.042.075c.266.475.866.624 1.3.312 1.74-1.25 2.586-2.606 2.516-4.037z"/></symbol></svg> \ No newline at end of file
diff --git a/docs/dist/docs.css b/docs/dist/docs.css
index dfbc0cb4..5bcbe5fa 100644
--- a/docs/dist/docs.css
+++ b/docs/dist/docs.css
@@ -1 +1 @@
-/*! normalize.css v2.1.3 | MIT License | git.io/normalize */a.logo,img,legend{border:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,small,summary{display:block}[hidden],template{display:none}body,figure,ul li{margin:0}body,h1,h2{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}body,small{padding:0 10px}nav ul,ul li{list-style:none;padding:0}legend,nav ul,ul li{padding:0}.btn-bar,sub,sup{position:relative}.example-video .player iframe,.example-video .player-video iframe,.example-video .player-youtube iframe{-webkit-mask-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAA5JREFUeNpiYGBgAAgwAAAEAAGbA+oJAAAAAElFTkSuQmCC)}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}a{background:0 0;text-decoration:none;color:#3498db;border-bottom:1px dotted currentColor;transition:background .3s ease,color .3s ease,border .3s ease}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}hr{box-sizing:content-box;height:0}mark{background:#ff0;color:#000}code,kbd,pre,samp{font-family:monospace,serif;font-size:1em}pre{white-space:pre-wrap}.btn-bar,nav li{white-space:nowrap}q{quotes:"\201C" "\201D" "\2018" "\2019"}sub,sup{font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}.btn-bar::before,.btn-count::before{content:"";top:50%}sub{bottom:-.25em}svg:not(:root){overflow:hidden}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}button,input{line-height:normal}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=search]{-webkit-appearance:textfield;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top}table{border-collapse:collapse;border-spacing:0}@-webkit-keyframes fade-in{0%{opacity:0}100%{opacity:1}}@keyframes fade-in{0%{opacity:0}100%{opacity:1}}@font-face{font-family:Avenir;src:url(//cdn.plyr.io/fonts/avenir-medium.woff2) format("woff2"),url(//cdn.plyr.io/fonts/avenir-medium.woff) format("woff");font-style:normal;font-weight:400}@font-face{font-family:Avenir;src:url(//cdn.plyr.io/fonts/avenir-bold.woff2) format("woff2"),url(//cdn.plyr.io/fonts/avenir-bold.woff) format("woff");font-style:normal;font-weight:600}h1,h2{letter-spacing:-.025em;color:#2E3C44;margin:0 0 10px;line-height:1.2}h1{font-size:64px;font-size:4rem;color:#3498DB}p,small{margin:0 0 20px}small{font-size:14px;font-size:.875rem}a:focus,a:hover{color:#343f4a;border-bottom-color:transparent}a:focus{outline:#343f4a dotted thin;outline-offset:1px}.color-vimeo{color:#19b7ed}.color-youtube{color:#cc181e}*,::after,::before{box-sizing:border-box}.btn-bar ul,nav li{display:inline-block}html{height:100%;font-size:100%;background:linear-gradient(#fff,#f2f5f7) fixed}body{font-family:Avenir,"Helvetica Neue",Helvetica,Arial,sans-serif;line-height:1.5;text-align:center;color:#55646b}header{padding:20px;margin-bottom:20px}header p{font-size:18px;font-size:1.125rem}section{padding-bottom:20px}@media (min-width:480px){header{padding-top:60px;padding-bottom:60px}section{padding-bottom:40px}}.icon{fill:currentColor;width:18px;height:18px;vertical-align:-3px}a svg,button svg,label svg{pointer-events:none}.btn .icon,a .icon{margin-right:10px}nav ul{margin:0;font-size:0}nav li{margin-top:10px;font-size:16px;font-size:1rem}nav li+li{margin-left:20px}.btn-bar{margin:0 auto 20px;max-width:1200px}.btn-bar::before{position:absolute;left:0;right:0;height:1px;background:#dbe3e8}.btn-bar ul{position:relative;z-index:1;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn-bar li{margin:0}.btn-bar li:first-child .btn{border-radius:4px 0 0 4px}.btn-bar li:last-child .btn{border-radius:0 4px 4px 0}.btn-bar li+li .btn{margin-left:-1px}.btn-bar .btn{display:block;border-radius:0}.btn-bar .btn.active{box-shadow:inset 0 1px 1px rgba(0,0,0,.2);position:relative;z-index:1}.btn-bar .btn.active .icon{color:inherit}@media (min-width:560px){.btn-bar{margin-bottom:40px}}.btn,.btn-count{display:inline-block;vertical-align:middle;border-radius:4px;font-weight:600;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn{padding:10px 20px;background:#f2f5f7;border:1px solid #cbd0d3;box-shadow:inset 0 1px 0 #fff,0 1px 1px rgba(0,0,0,.05);text-shadow:0 1px 1px #fff;color:#55646b;transition:background .3s ease,border .3s ease,color .3s ease}.btn:focus,.btn:hover{border-color:#b5bcc0;color:#55646b;outline:0}.btn-youtube .icon{color:#cc181e}.btn-vimeo .icon{color:#19b7ed}.btn-twitter .icon{color:#4BAAF4}.btn-bar .active,.btn-primary{background-image:linear-gradient(#3498db,#2791d9);background-color:#3498db;border-color:#217dbb;box-shadow:0 1px 1px rgba(0,0,0,.15);text-shadow:0 1px 1px rgba(0,0,0,.1);color:#fff}.btn-primary:focus,.btn-primary:hover{color:#fff;border-color:#196090}.btn-small{padding-top:7px;padding-bottom:7px}.btn-count{position:relative;margin-left:10px;padding:10px 15px;background:#fff;border:1px solid #cbd0d3}.btn-count::before{position:absolute;display:block;width:8px;height:8px;left:1px;margin-top:-4px;background:inherit;border:inherit;border-width:1px 0 0 1px;-webkit-transform:rotate(-45deg) translate(-50%,-50%);transform:rotate(-45deg) translate(-50%,-50%)}.panel{display:none}.panel.active{display:block;-webkit-animation:fade-in .3s ease;animation:fade-in .3s ease}.error body,html.error{height:100%}.error body{width:100%;display:table;table-layout:fixed}.error main{display:table-cell;width:100%;vertical-align:middle}.example-audio .player,.example-video .player{margin:0 auto 20px}.example-audio .player-controls,.example-video .player-controls{border-radius:0 0 4px 4px}.example-audio .player{max-width:520px}.example-audio .player-controls{border-radius:4px}.example-audio .player-progress{border-radius:4px 4px 0 0;overflow:hidden}.example-video .player{max-width:1200px}.example-video .player iframe,.example-video .player video{border-radius:4px}.example-video .player-video iframe,.example-video .player-video video,.example-video .player-youtube iframe,.example-video .player-youtube video{border-radius:4px 4px 0 0}.example-video .player-video-fullscreen,.example-video .player-video.fullscreen-active,.example-video .player-youtube-fullscreen,.example-video .player-youtube.fullscreen-active{max-width:none}.example-video .player-video-fullscreen .player-controls,.example-video .player-video-fullscreen iframe,.example-video .player-video-fullscreen video,.example-video .player-video.fullscreen-active .player-controls,.example-video .player-video.fullscreen-active iframe,.example-video .player-video.fullscreen-active video,.example-video .player-youtube-fullscreen .player-controls,.example-video .player-youtube-fullscreen iframe,.example-video .player-youtube-fullscreen video,.example-video .player-youtube.fullscreen-active .player-controls,.example-video .player-youtube.fullscreen-active iframe,.example-video .player-youtube.fullscreen-active video{border-radius:0}.example-video .player-video-fullscreen iframe,.example-video .player-video.fullscreen-active iframe,.example-video .player-youtube-fullscreen iframe,.example-video .player-youtube.fullscreen-active iframe{-webkit-mask-image:none} \ No newline at end of file
+/*! normalize.css v2.1.3 | MIT License | git.io/normalize */a.logo,img,legend{border:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,small,summary{display:block}[hidden],template{display:none}body,figure,li,ul{margin:0}body,h1,h2{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}body,small{padding:0 10px}li,nav ul,ul{list-style:none;padding:0}legend,li,nav ul,ul{padding:0}.btn__bar,sub,sup{position:relative}.btn__bar,.plyr{max-width:1200px}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}a{background:0 0;text-decoration:none;color:#3498db;border-bottom:1px dotted currentColor;transition:background .3s ease,color .3s ease,border .3s ease}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}hr{box-sizing:content-box;height:0}mark{background:#ff0;color:#000}code,kbd,pre,samp{font-family:monospace,serif;font-size:1em}pre{white-space:pre-wrap}.btn__bar,nav li{white-space:nowrap}q{quotes:"\201C" "\201D" "\2018" "\2019"}sub,sup{font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}svg:not(:root){overflow:hidden}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}button,input{line-height:normal}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=search]{-webkit-appearance:textfield;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top}table{border-collapse:collapse;border-spacing:0}@-webkit-keyframes fade-in{0%{opacity:0}100%{opacity:1}}@keyframes fade-in{0%{opacity:0}100%{opacity:1}}@font-face{font-family:Avenir;src:url(//cdn.plyr.io/fonts/avenir-medium.woff2) format("woff2"),url(//cdn.plyr.io/fonts/avenir-medium.woff) format("woff");font-style:normal;font-weight:400}@font-face{font-family:Avenir;src:url(//cdn.plyr.io/fonts/avenir-bold.woff2) format("woff2"),url(//cdn.plyr.io/fonts/avenir-bold.woff) format("woff");font-style:normal;font-weight:600}h1,h2{letter-spacing:-.025em;color:#2E3C44;margin:0 0 10px;line-height:1.2}h1{font-size:64px;font-size:4rem;color:#3498DB}p,small{margin:0 0 20px}small{font-size:14px;font-size:.875rem}sup{top:-.5em;vertical-align:2px;font-size:9px;font-size:.563rem}.btn__bar::before,.btn__count::before{content:"";top:50%}a:focus,a:hover{color:#343f4a;border-bottom-color:transparent}a:focus{outline:#343f4a dotted thin;outline-offset:1px}.color--vimeo{color:#19b7ed}.color--youtube{color:#cc181e}*,::after,::before{box-sizing:border-box}.btn__bar ul,nav li{display:inline-block}html{height:100%;font-size:100%;background:linear-gradient(#fff,#f2f5f7) fixed}body{font-family:Avenir,"Helvetica Neue",Helvetica,Arial,sans-serif;line-height:1.5;text-align:center;color:#55646b}header{padding:20px;margin-bottom:20px}header p{font-size:18px;font-size:1.125rem}section{padding-bottom:20px}@media (min-width:480px){header{padding-top:60px;padding-bottom:60px}section{padding-bottom:40px}}.icon{fill:currentColor;width:18px;height:18px;vertical-align:-3px}a svg,button svg,label svg{pointer-events:none}.btn .icon,a .icon{margin-right:10px}nav ul{margin:0;font-size:0}nav li{margin-top:10px;font-size:16px;font-size:1rem}nav li+li{margin-left:20px}.btn__bar{margin:0 auto 20px}.btn__bar::before{position:absolute;left:0;right:0;height:1px;background:#dbe3e8}.btn__bar ul{position:relative;z-index:1;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn__bar li{margin:0}.btn__bar li:first-child .btn{border-radius:4px 0 0 4px}.btn__bar li:last-child .btn{border-radius:0 4px 4px 0}.btn__bar li+li .btn{margin-left:-1px}.btn__bar li.active .btn{position:relative;z-index:1}.btn__bar li.active .btn .icon{color:inherit}.btn__bar li.active+li .btn:hover{z-index:0}.btn__bar .btn{position:relative;display:block;border-radius:0}.btn__bar .btn:focus,.btn__bar .btn:hover{z-index:1}@media (min-width:560px){.btn__bar{margin-bottom:40px}}.btn,.btn__count{display:inline-block;vertical-align:middle;border-radius:4px;font-weight:600;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn{padding:10px 20px;background:linear-gradient(#f8fafb,#e9eef1);border:1px solid #cbd0d3;box-shadow:0 1px 1px rgba(0,0,0,.05);text-shadow:0 1px 1px #fff;color:#55646b;transition:background .1s ease,color .1s ease}.btn:focus,.btn:hover{border-color:#b5bcc0;color:#55646b;outline:0}.btn--youtube .icon{color:#cc181e}.btn--vimeo .icon{color:#19b7ed}.btn--twitter .icon{color:#4BAAF4}.btn--primary,.btn__bar li.active .btn{background-image:linear-gradient(#3498db,#258cd1);background-color:#3498db;border-color:#217dbb;box-shadow:0 1px 1px rgba(0,0,0,.15);text-shadow:0 1px 1px rgba(0,0,0,.1);color:#fff}.btn--primary:focus,.btn--primary:hover{color:#fff;border-color:#196090}.btn--small{padding-top:7px;padding-bottom:7px}.btn__count{position:relative;margin-left:10px;padding:10px 15px;background:#fff;border:1px solid #cbd0d3}.btn__count::before{position:absolute;display:block;width:8px;height:8px;left:1px;margin-top:-4px;background:inherit;border:inherit;border-width:1px 0 0 1px;-webkit-transform:rotate(-45deg) translate(-50%,-50%);transform:rotate(-45deg) translate(-50%,-50%)}.error body,html.error{height:100%}.error body{width:100%;display:table;table-layout:fixed}.error main{display:table-cell;width:100%;vertical-align:middle}.plyr__video-embed,video{border-radius:4px}.plyr__video-embed{-webkit-mask-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAA5JREFUeNpiYGBgAAgwAAAEAAGbA+oJAAAAAElFTkSuQmCC)}.plyr{margin:0 auto 20px}.plyr__controls{border-radius:0 0 4px 4px}.plyr .plyr__video-embed,.plyr video{border-radius:4px 4px 0 0}.plyr--fullscreen,.plyr--fullscreen-active{max-width:none}.plyr--fullscreen .plyr-controls,.plyr--fullscreen iframe,.plyr--fullscreen video,.plyr--fullscreen-active .plyr-controls,.plyr--fullscreen-active iframe,.plyr--fullscreen-active video{border-radius:0}.plyr--fullscreen iframe,.plyr--fullscreen-active iframe{-webkit-mask-image:none}.plyr--audio{max-width:520px}.plyr--audio .plyr__controls{border-radius:4px}.plyr--audio .plyr__progress{border-radius:4px 4px 0 0;overflow:hidden}.plyr__cite{display:none}.plyr__cite .icon{margin-right:5px}.plyr--audio~ul .plyr__cite--audio,.plyr--video~ul .plyr__cite--video,.plyr--vimeo~ul .plyr__cite--vimeo,.plyr--youtube~ul .plyr__cite--youtube{display:block} \ No newline at end of file
diff --git a/docs/dist/docs.js b/docs/dist/docs.js
index d4b4b57a..b3840281 100644
--- a/docs/dist/docs.js
+++ b/docs/dist/docs.js
@@ -1 +1 @@
-"document"in self&&("classList"in document.createElement("_")?!function(){"use strict";var t=document.createElement("_");if(t.classList.add("c1","c2"),!t.classList.contains("c2")){var e=function(t){var e=DOMTokenList.prototype[t];DOMTokenList.prototype[t]=function(t){var n,i=arguments.length;for(n=0;i>n;n++)t=arguments[n],e.call(this,t)}};e("add"),e("remove")}if(t.classList.toggle("c3",!1),t.classList.contains("c3")){var n=DOMTokenList.prototype.toggle;DOMTokenList.prototype.toggle=function(t,e){return 1 in arguments&&!this.contains(t)==!e?e:n.call(this,t)}}t=null}():!function(t){"use strict";if("Element"in t){var e="classList",n="prototype",i=t.Element[n],s=Object,r=String[n].trim||function(){return this.replace(/^\s+|\s+$/g,"")},o=Array[n].indexOf||function(t){for(var e=0,n=this.length;n>e;e++)if(e in this&&this[e]===t)return e;return-1},a=function(t,e){this.name=t,this.code=DOMException[t],this.message=e},c=function(t,e){if(""===e)throw new a("SYNTAX_ERR","An invalid or illegal string was specified");if(/\s/.test(e))throw new a("INVALID_CHARACTER_ERR","String contains an invalid character");return o.call(t,e)},l=function(t){for(var e=r.call(t.getAttribute("class")||""),n=e?e.split(/\s+/):[],i=0,s=n.length;s>i;i++)this.push(n[i]);this._updateClassName=function(){t.setAttribute("class",this.toString())}},u=l[n]=[],f=function(){return new l(this)};if(a[n]=Error[n],u.item=function(t){return this[t]||null},u.contains=function(t){return t+="",-1!==c(this,t)},u.add=function(){var t,e=arguments,n=0,i=e.length,s=!1;do t=e[n]+"",-1===c(this,t)&&(this.push(t),s=!0);while(++n<i);s&&this._updateClassName()},u.remove=function(){var t,e,n=arguments,i=0,s=n.length,r=!1;do for(t=n[i]+"",e=c(this,t);-1!==e;)this.splice(e,1),r=!0,e=c(this,t);while(++i<s);r&&this._updateClassName()},u.toggle=function(t,e){t+="";var n=this.contains(t),i=n?e!==!0&&"remove":e!==!1&&"add";return i&&this[i](t),e===!0||e===!1?e:!n},u.toString=function(){return this.join(" ")},s.defineProperty){var d={get:f,enumerable:!0,configurable:!0};try{s.defineProperty(i,e,d)}catch(g){-2146823252===g.number&&(d.enumerable=!1,s.defineProperty(i,e,d))}}else s[n].__defineGetter__&&i.__defineGetter__(e,f)}}(self)),plyr.setup({debug:!0,volume:9,title:"Video demo",tooltips:!0,captions:{defaultActive:!0},onSetup:function(){if("media"in this){var t=this,e=t.media.tagName.toLowerCase(),n=document.querySelector("[data-toggle='fullscreen']");console.log("✓ Setup done for <"+e+">"),"video"===e&&n&&n.addEventListener("click",t.toggleFullscreen,!1)}}}),shr.setup({count:{classname:"btn-count"}}),function(){function t(t){t.preventDefault();for(var s=t.target,r=document.querySelector(s.getAttribute("href")),o=n.length-1;o>=0;o--)n[o].classList.remove(i);for(var a=e.length-1;a>=0;a--)e[a].classList.remove(i);r.classList.add(i),t.target.classList.add(i)}for(var e=document.querySelectorAll(".nav-panel a"),n=document.querySelectorAll(".panels > .panel"),i="active",s=e.length-1;s>=0;s--)e[s].addEventListener("click",t)}(),document.domain.indexOf("plyr.io")>-1&&(!function(t,e,n,i,s,r,o){t.GoogleAnalyticsObject=s,t[s]=t[s]||function(){(t[s].q=t[s].q||[]).push(arguments)},t[s].l=1*new Date,r=e.createElement(n),o=e.getElementsByTagName(n)[0],r.async=1,r.src=i,o.parentNode.insertBefore(r,o)}(window,document,"script","//www.google-analytics.com/analytics.js","ga"),ga("create","UA-40881672-11","auto"),ga("send","pageview")); \ No newline at end of file
+"document"in self&&("classList"in document.createElement("_")?!function(){"use strict";var e=document.createElement("_");if(e.classList.add("c1","c2"),!e.classList.contains("c2")){var t=function(e){var t=DOMTokenList.prototype[e];DOMTokenList.prototype[e]=function(e){var n,i=arguments.length;for(n=0;i>n;n++)e=arguments[n],t.call(this,e)}};t("add"),t("remove")}if(e.classList.toggle("c3",!1),e.classList.contains("c3")){var n=DOMTokenList.prototype.toggle;DOMTokenList.prototype.toggle=function(e,t){return 1 in arguments&&!this.contains(e)==!t?t:n.call(this,e)}}e=null}():!function(e){"use strict";if("Element"in e){var t="classList",n="prototype",i=e.Element[n],s=Object,o=String[n].trim||function(){return this.replace(/^\s+|\s+$/g,"")},r=Array[n].indexOf||function(e){for(var t=0,n=this.length;n>t;t++)if(t in this&&this[t]===e)return t;return-1},a=function(e,t){this.name=e,this.code=DOMException[e],this.message=t},c=function(e,t){if(""===t)throw new a("SYNTAX_ERR","An invalid or illegal string was specified");if(/\s/.test(t))throw new a("INVALID_CHARACTER_ERR","String contains an invalid character");return r.call(e,t)},l=function(e){for(var t=o.call(e.getAttribute("class")||""),n=t?t.split(/\s+/):[],i=0,s=n.length;s>i;i++)this.push(n[i]);this._updateClassName=function(){e.setAttribute("class",this.toString())}},u=l[n]=[],p=function(){return new l(this)};if(a[n]=Error[n],u.item=function(e){return this[e]||null},u.contains=function(e){return e+="",-1!==c(this,e)},u.add=function(){var e,t=arguments,n=0,i=t.length,s=!1;do e=t[n]+"",-1===c(this,e)&&(this.push(e),s=!0);while(++n<i);s&&this._updateClassName()},u.remove=function(){var e,t,n=arguments,i=0,s=n.length,o=!1;do for(e=n[i]+"",t=c(this,e);-1!==t;)this.splice(t,1),o=!0,t=c(this,e);while(++i<s);o&&this._updateClassName()},u.toggle=function(e,t){e+="";var n=this.contains(e),i=n?t!==!0&&"remove":t!==!1&&"add";return i&&this[i](e),t===!0||t===!1?t:!n},u.toString=function(){return this.join(" ")},s.defineProperty){var d={get:p,enumerable:!0,configurable:!0};try{s.defineProperty(i,t,d)}catch(m){-2146823252===m.number&&(d.enumerable=!1,s.defineProperty(i,t,d))}}else s[n].__defineGetter__&&i.__defineGetter__(t,p)}}(self)),plyr.setup(".js-media-player",{debug:!0,title:"Video demo",tooltips:!0,captions:{defaultActive:!0},onSetup:function(){console.log("✓ Setup done")}}),shr.setup({count:{classname:"btn__count"}}),function(){function e(){var e=this,n=e.getAttribute("data-source"),i=document.querySelector(".js-media-player").plyr;switch(n){case"video":i.source({type:"video",title:"View From A Blue Moon",sources:[{src:"https://cdn.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.mp4",type:"video/mp4"},{src:"https://cdn.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.webm",type:"video/webm"}],poster:"https://cdn.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.jpg",tracks:[{kind:"captions",label:"English",srclang:"en",src:"https://cdn.selz.com/plyr/1.0/example_captions_en.vtt","default":!0}]});break;case"audio":i.source({type:"audio",title:"Kishi Bashi &ndash; &ldquo;It All Began With A Burst&rdquo;",sources:[{src:"https://cdn.selz.com/plyr/1.5/Kishi_Bashi_-_It_All_Began_With_a_Burst.mp3",type:"audio/mp3"},{src:"https://cdn.selz.com/plyr/1.5/Kishi_Bashi_-_It_All_Began_With_a_Burst.ogg",type:"audio/ogg"}]});break;case"youtube":i.source({type:"video",title:"View From A Blue Moon",sources:[{src:"bTqVqk7FSmY",type:"youtube"}]});break;case"vimeo":i.source({type:"video",title:"View From A Blue Moon",sources:[{src:"143418951",type:"vimeo"}]})}for(var s=t.length-1;s>=0;s--)t[s].parentElement.classList.remove("active");event.target.parentElement.classList.add("active")}for(var t=document.querySelectorAll("[data-source]"),n=t.length-1;n>=0;n--)t[n].addEventListener("click",e)}(),document.domain.indexOf("plyr.io")>-1&&(!function(e,t,n,i,s,o,r){e.GoogleAnalyticsObject=s,e[s]=e[s]||function(){(e[s].q=e[s].q||[]).push(arguments)},e[s].l=1*new Date,o=t.createElement(n),r=t.getElementsByTagName(n)[0],o.async=1,o.src=i,r.parentNode.insertBefore(o,r)}(window,document,"script","//www.google-analytics.com/analytics.js","ga"),ga("create","UA-40881672-11","auto"),ga("send","pageview")); \ No newline at end of file
diff --git a/docs/dist/docs.svg b/docs/dist/docs.svg
new file mode 100644
index 00000000..6f09b44b
--- /dev/null
+++ b/docs/dist/docs.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg"><symbol id="icon-github" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M8 .2c-4.4 0-8 3.6-8 8 0 3.5 2.3 6.5 5.5 7.6.4.1.5-.2.5-.4V14c-2.2.5-2.7-1-2.7-1-.4-.9-.9-1.2-.9-1.2-.7-.5.1-.5.1-.5.8.1 1.2.8 1.2.8.7 1.3 1.9.9 2.3.7.1-.5.3-.9.5-1.1-1.8-.2-3.6-.9-3.6-4 0-.9.3-1.6.8-2.1-.1-.2-.4-1 .1-2.1 0 0 .7-.2 2.2.8.6-.2 1.3-.3 2-.3s1.4.1 2 .3c1.5-1 2.2-.8 2.2-.8.4 1.1.2 1.9.1 2.1.5.6.8 1.3.8 2.1 0 3.1-1.9 3.7-3.7 3.9.3.4.6.9.6 1.6v2.2c0 .2.1.5.6.4 3.2-1.1 5.5-4.1 5.5-7.6-.1-4.4-3.7-8-8.1-8z"/></symbol><symbol id="icon-twitter" viewBox="0 0 16 16"><title>Twitter</title><path d="M16 3c-.6.3-1.2.4-1.9.5.7-.4 1.2-1 1.4-1.8-.6.4-1.3.6-2.1.8-.6-.6-1.5-1-2.4-1-1.7 0-3.2 1.5-3.2 3.3 0 .3 0 .5.1.7-2.7-.1-5.2-1.4-6.8-3.4-.3.5-.4 1-.4 1.7 0 1.1.6 2.1 1.5 2.7-.5 0-1-.2-1.5-.4C.7 7.7 1.8 9 3.3 9.3c-.3.1-.6.1-.9.1-.2 0-.4 0-.6-.1.4 1.3 1.6 2.3 3.1 2.3-1.1.9-2.5 1.4-4.1 1.4H0c1.5.9 3.2 1.5 5 1.5 6 0 9.3-5 9.3-9.3v-.4C15 4.3 15.6 3.7 16 3z"/></symbol><symbol id="icon-vimeo" viewBox="0 0 16 16"><path d="M16 4.3c-.1 1.6-1.2 3.7-3.3 6.4-2.2 2.8-4 4.2-5.5 4.2-.9 0-1.7-.9-2.4-2.6C4 9.9 3.4 5 2 5c-.1 0-.5.3-1.2.8l-.8-1c.8-.7 3.5-3.4 4.7-3.5 1.2-.1 2 .7 2.3 2.5.3 2 .8 6.1 1.8 6.1.9 0 2.5-3.4 2.6-4 .1-.9-.3-1.9-2.3-1.1.8-2.6 2.3-3.8 4.5-3.8 1.7.1 2.5 1.2 2.4 3.3z"/></symbol><symbol id="icon-youtube" viewBox="0 0 16 16"><path d="M15.8 4.8c-.2-1.3-.8-2.2-2.2-2.4C11.4 2 8 2 8 2s-3.4 0-5.6.4C1 2.6.3 3.5.2 4.8 0 6.1 0 8 0 8s0 1.9.2 3.2c.2 1.3.8 2.2 2.2 2.4C4.6 14 8 14 8 14s3.4 0 5.6-.4c1.4-.3 2-1.1 2.2-2.4C16 9.9 16 8 16 8s0-1.9-.2-3.2zM6 11V5l5 3-5 3z"/></symbol></svg> \ No newline at end of file
diff --git a/docs/error.html b/docs/error.html
index 8f5086ef..7c7bc14a 100644
--- a/docs/error.html
+++ b/docs/error.html
@@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Docs styles -->
- <link rel="stylesheet" href="//cdn.plyr.io/1.3.7/docs.css">
+ <link rel="stylesheet" href="dist/docs.css">
</head>
<body>
<main>
@@ -15,4 +15,4 @@
<a href="http://plyr.io" class="btn">Back to plyr.io</a>
</main>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/docs/index.html b/docs/index.html
index 88d3c44b..8c4026bb 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -8,25 +8,25 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Styles -->
- <link rel="stylesheet" href="https://cdn.plyr.io/1.3.7/plyr.css">
+ <link rel="stylesheet" href="../dist/plyr.css">
<!-- Docs styles -->
- <link rel="stylesheet" href="https://cdn.plyr.io/1.3.7/docs.css">
+ <link rel="stylesheet" href="dist/docs.css">
</head>
<body>
<header>
<h1>Plyr</h1>
- <p>A simple HTML5 media player with custom controls and WebVTT captions by <a href="https://twitter.com/sam_potts" target="_blank">@sam_potts</a> from <a href="https://twitter.com/selz" target="_blank">@selz</a></p>
+ <p>A simple, accessible HTML5 media player by <a href="https://twitter.com/sam_potts" target="_blank">@sam_potts</a> from <a href="https://twitter.com/selz" target="_blank">@selz</a></p>
<nav>
<ul>
<li>
- <a href="https://github.com/selz/plyr" target="_blank" class="btn btn-primary" data-shr-network="github">
- <svg class="icon"><use xlink:href="#shr-github"/></svg>Download on GitHub
+ <a href="https://github.com/selz/plyr" target="_blank" class="btn btn--primary" data-shr-network="github">
+ <svg class="icon"><use xlink:href="#icon-github"/></svg>Download on GitHub
</a>
</li>
<li>
- <a href="https://twitter.com/intent/tweet?text=A+simple+HTML5+media+player+with+custom+controls+and+WebVTT+captions.&url=http%3A%2F%2Fplyr.io&via=Sam_Potts" target="_blank" class="btn btn-twitter" data-shr-network="twitter">
- <svg class="icon"><use xlink:href="#shr-twitter"/></svg>Tweet
+ <a href="https://twitter.com/intent/tweet?text=A+simple+HTML5+media+player+with+custom+controls+and+WebVTT+captions.&url=http%3A%2F%2Fplyr.io&via=Sam_Potts" target="_blank" class="btn btn--twitter" data-shr-network="twitter">
+ <svg class="icon"><use xlink:href="#icon-twitter"/></svg>Tweet
</a>
</li>
</ul>
@@ -34,50 +34,44 @@
</header>
<main role="main" id="main">
- <nav class="btn-bar nav-panel">
+ <nav class="btn__bar">
<ul>
- <li><a href="#video" class="btn active btn-small">Video</a></li>
- <li><a href="#youtube" class="btn btn-small">YouTube</a></li>
- <li><a href="#audio" class="btn btn-small">Audio</a></li>
+ <li class="active">
+ <button type="button" class="btn" data-source="video">Video</button>
+ </li>
+ <li>
+ <button type="button" class="btn" data-source="audio">Audio</button>
+ </li>
+ <li>
+ <button type="button" class="btn btn--youtube" data-source="youtube"><svg class="icon"><use xlink:href="#icon-youtube"/></svg>YouTube</button>
+ </li>
+ <li>
+ <button type="button" class="btn btn--vimeo" data-source="vimeo"><svg class="icon"><use xlink:href="#icon-vimeo"/></svg>Vimeo <sup>BETA</sup></button>
+ </li>
</ul>
</nav>
- <div class="panels">
- <section class="panel example-video active" id="video">
- <div class="player">
- <video poster="poster.jpg" controls crossorigin>
- <!-- Video files -->
- <source src="https://cdn.selz.com/plyr/1.0/movie.mp4" type="video/mp4">
- <source src="https://cdn.selz.com/plyr/1.0/movie.webm" type="video/webm">
+ <section>
+ <div class="js-media-player">
+ <video poster="https://cdn.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.jpg" controls crossorigin>
+ <!-- Video files -->
+ <source src="https://cdn.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.mp4" type="video/mp4">
+ <source src="https://cdn.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.webm" type="video/webm">
- <!-- Text track file -->
- <track kind="captions" label="English" srclang="en" src="https://cdn.selz.com/plyr/1.0/example_captions_en.vtt" default>
+ <!-- Text track file -->
+ <track kind="captions" label="English" srclang="en" src="https://cdn.selz.com/plyr/1.0/example_captions_en.vtt" default>
- <!-- Fallback for browsers that don't support the <video> element -->
- <a href="https://cdn.selz.com/plyr/1.0/movie.mp4">Download</a>
- </video>
- </div>
- <small>Big Buck Bunny. More info can be found at <a href="https://peach.blender.org" target="_blank">peach.blender.org</a>.</small>
- </section>
- <section class="panel example-video" id="youtube">
- <div class="player">
- <div data-video-id="Au87oAJ2jeE" data-type="youtube"></div>
- </div>
- <small>Envato's "Made By" interview of <a href="https://www.youtube.com/watch?v=Au87oAJ2jeE" target="_blank">Dan Cederholm</a> from <a href="https://dribbble.com" target="_blank">Dribbble</a>.</small>
- </section>
- <section class="panel example-audio" id="audio">
- <div class="player">
- <audio controls>
- <!-- Audio files -->
- <source src="//cdn.selz.com/plyr/1.0/logistics-96-sample.mp3" type="audio/mp3">
- <source src="//cdn.selz.com/plyr/1.0/logistics-96-sample.ogg" type="audio/ogg">
+ <!-- Fallback for browsers that don't support the <video> element -->
+ <a href="https://cdn.selz.com/plyr/1.0/movie.mp4">Download</a>
+ </video>
+ </div>
- <!-- Fallback for browsers that don't support the <audio> element -->
- <a href="//cdn.selz.com/plyr/1.0/logistics-96-sample.mp3">Download</a>
- </audio>
- </div>
- <small>"96" by Logistics, which can be purchased from <a href="https://www.hospitalrecords.com/shop/artist/logistics" target="_blank">Hospital Records</a>.</small>
- </section>
- </div>
+ <ul>
+ <li class="plyr__cite plyr__cite--video"><small><a href="http://viewfromabluemoon.com/" target="_blank">View From A Blue Moon</a> &copy; Brainfarm</small></li>
+ <li class="plyr__cite plyr__cite--audio"><small><a href="http://www.kishibashi.com/" target="_blank">Kishi Bashi &ndash; &ldquo;It All Began With A Burst&rdquo;</a> &copy; Kishi Bashi</small></li>
+ <li class="plyr__cite plyr__cite--youtube"><small><a href="https://www.youtube.com/watch?v=bTqVqk7FSmY" target="_blank">View From A Blue Moon</a> on <span class="color--youtube"><svg class="icon"><use xlink:href="#icon-youtube"/></svg>YouTube</span></small>
+ <li class="plyr__cite plyr__cite--vimeo"><small><a href="https://vimeo.com/ondemand/viewfromabluemoon4k" target="_blank">View From A Blue Moon</a> on <span class="color--vimeo"><svg class="icon"><use xlink:href="#icon-vimeo"/></svg>Vimeo</span></small>
+ </ul>
+ </section>
</main>
<!-- Load SVG defs -->
@@ -85,46 +79,32 @@
<script>
(function() {
[
- "https://cdn.shr.one/0.1.9/sprite.svg",
- "https://cdn.plyr.io/1.3.7/sprite.svg"
+ '../dist/sprite.svg',
+ 'dist/docs.svg'
]
.forEach(function(u) {
- var x = new XMLHttpRequest(),
- b = document.body;
-
+ var x = new XMLHttpRequest(), b = document.body;
// Check for CORS support
- // If you're loading from same domain, you can remove the if statement
+ // If you're loading from same domain, you can remove the whole if/else statement
// XHR for Chrome/Firefox/Opera/Safari
- if ("withCredentials" in x) {
- x.open("GET", u, true);
- }
+ if ('withCredentials' in x) { x.open('GET', u, true); }
// XDomainRequest for older IE
- else if (typeof XDomainRequest != "undefined") {
- x = new XDomainRequest();
- x.open("GET", u);
- }
- else {
- return;
- }
+ else if (typeof XDomainRequest != 'undefined') { x = new XDomainRequest(); x.open('GET', u); }
+ else { return; }
x.send();
- x.onload = function() {
- var c = document.createElement("div");
- c.setAttribute("hidden", "");
- c.innerHTML = x.responseText;
- b.insertBefore(c, b.childNodes[0]);
- }
+ x.onload = function() { var c = document.createElement('div'); c.setAttribute('hidden', ''); c.innerHTML = x.responseText; b.insertBefore(c, b.childNodes[0]); }
});
})();
</script>
<!-- Plyr core script -->
- <script src="https://cdn.plyr.io/1.3.7/plyr.js"></script>
+ <script src="../dist/plyr.js"></script>
<!-- Shr core script -->
<script src="https://cdn.shr.one/0.1.9/shr.js"></script>
<!-- Docs script -->
- <script src="https://cdn.plyr.io/1.3.7/docs.js"></script>
+ <script src="dist/docs.js"></script>
</body>
</html>
diff --git a/docs/index.master.html b/docs/index.master.html
deleted file mode 100644
index c6972ae6..00000000
--- a/docs/index.master.html
+++ /dev/null
@@ -1,130 +0,0 @@
-<!doctype html>
-<html lang="en">
- <head>
- <meta charset="utf-8" />
- <title>Plyr - A simple HTML5 media player</title>
- <meta name="description" content="A simple HTML5 media player with custom controls and WebVTT captions.">
- <meta name="author" content="Sam Potts">
- <meta name="viewport" content="width=device-width, initial-scale=1">
-
- <!-- Styles -->
- <link rel="stylesheet" href="../dist/plyr.css">
-
- <!-- Docs styles -->
- <link rel="stylesheet" href="dist/docs.css">
- </head>
- <body>
- <header>
- <h1>Plyr</h1>
- <p>A simple HTML5 media player with custom controls and WebVTT captions by <a href="https://twitter.com/sam_potts" target="_blank">@sam_potts</a> from <a href="https://twitter.com/selz" target="_blank">@selz</a></p>
- <nav>
- <ul>
- <li>
- <a href="https://github.com/selz/plyr" target="_blank" class="btn btn-primary" data-shr-network="github">
- <svg class="icon"><use xlink:href="#shr-github"/></svg>Download on GitHub
- </a>
- </li>
- <li>
- <a href="https://twitter.com/intent/tweet?text=A+simple+HTML5+media+player+with+custom+controls+and+WebVTT+captions.&url=http%3A%2F%2Fplyr.io&via=Sam_Potts" target="_blank" class="btn btn-twitter" data-shr-network="twitter">
- <svg class="icon"><use xlink:href="#shr-twitter"/></svg>Tweet
- </a>
- </li>
- </ul>
- </nav>
- </header>
-
- <main role="main" id="main">
- <nav class="btn-bar nav-panel">
- <ul>
- <li><a href="#video" class="btn active btn-small">Video</a></li>
- <li><a href="#youtube" class="btn btn-small">YouTube</a></li>
- <li><a href="#audio" class="btn btn-small">Audio</a></li>
- </ul>
- </nav>
- <div class="panels">
- <section class="panel example-video active" id="video">
- <div class="player">
- <video poster="poster.jpg" controls crossorigin>
- <!-- Video files -->
- <source src="https://cdn.selz.com/plyr/1.0/movie.mp4" type="video/mp4">
- <source src="https://cdn.selz.com/plyr/1.0/movie.webm" type="video/webm">
-
- <!-- Text track file -->
- <track kind="captions" label="English" srclang="en" src="https://cdn.selz.com/plyr/1.0/example_captions_en.vtt" default>
-
- <!-- Fallback for browsers that don't support the <video> element -->
- <a href="https://cdn.selz.com/plyr/1.0/movie.mp4">Download</a>
- </video>
- </div>
- <small>Big Buck Bunny. More info can be found at <a href="https://peach.blender.org" target="_blank">peach.blender.org</a>.</small>
- </section>
- <section class="panel example-video" id="youtube">
- <div class="player">
- <div data-video-id="Au87oAJ2jeE" data-type="youtube"></div>
- </div>
- <small>Envato's "Made By" interview of <a href="https://www.youtube.com/watch?v=Au87oAJ2jeE" target="_blank">Dan Cederholm</a> from <a href="https://dribbble.com" target="_blank">Dribbble</a>.</small>
- </section>
- <section class="panel example-audio" id="audio">
- <div class="player">
- <audio controls>
- <!-- Audio files -->
- <source src="//cdn.selz.com/plyr/1.0/logistics-96-sample.mp3" type="audio/mp3">
- <source src="//cdn.selz.com/plyr/1.0/logistics-96-sample.ogg" type="audio/ogg">
-
- <!-- Fallback for browsers that don't support the <audio> element -->
- <a href="//cdn.selz.com/plyr/1.0/logistics-96-sample.mp3">Download</a>
- </audio>
- </div>
- <small>"96" by Logistics, which can be purchased from <a href="https://www.hospitalrecords.com/shop/artist/logistics" target="_blank">Hospital Records</a>.</small>
- </section>
- </div>
- </main>
-
- <!-- Load SVG defs -->
- <!-- You should bundle all SVG/Icons into one file using a build tool such as gulp and svg store -->
- <script>
- (function() {
- [
- "https://cdn.shr.one/0.1.9/sprite.svg",
- "../dist/sprite.svg"
- ]
- .forEach(function(u) {
- var x = new XMLHttpRequest(),
- b = document.body;
-
- // Check for CORS support
- // If you're loading from same domain, you can remove the if statement
- // XHR for Chrome/Firefox/Opera/Safari
- if ("withCredentials" in x) {
- x.open("GET", u, true);
- }
- // XDomainRequest for older IE
- else if (typeof XDomainRequest != "undefined") {
- x = new XDomainRequest();
- x.open("GET", u);
- }
- else {
- return;
- }
-
- x.send();
- x.onload = function() {
- var c = document.createElement("div");
- c.setAttribute("hidden", "");
- c.innerHTML = x.responseText;
- b.insertBefore(c, b.childNodes[0]);
- }
- });
- })();
- </script>
-
- <!-- Plyr core script -->
- <script src="../src/js/plyr.js"></script>
-
- <!-- Shr core script -->
- <script src="https://cdn.shr.one/0.1.9/shr.js"></script>
-
- <!-- Docs script -->
- <script src="dist/docs.js"></script>
- </body>
-</html>
diff --git a/docs/src/js/docs.js b/docs/src/js/docs.js
index 518e4e0c..57e6ef8a 100644
--- a/docs/src/js/docs.js
+++ b/docs/src/js/docs.js
@@ -5,75 +5,117 @@
/*global plyr, shr*/
// Setup the player
-plyr.setup({
- debug: true,
- volume: 9,
- title: "Video demo",
- tooltips: true,
+plyr.setup('.js-media-player', {
+ debug: true,
+ title: 'Video demo',
+ tooltips: true,
captions: {
defaultActive: true
},
onSetup: function() {
- if(!("media" in this)) {
- return;
- }
-
- var player = this,
- type = player.media.tagName.toLowerCase(),
- toggle = document.querySelector("[data-toggle='fullscreen']");
-
- console.log("✓ Setup done for <" + type + ">");
-
- if(type === "video" && toggle) {
- toggle.addEventListener("click", player.toggleFullscreen, false);
- }
+ console.log('✓ Setup done');
}
});
// Setup shr
shr.setup({
count: {
- classname: "btn-count"
+ classname: 'btn__count'
}
});
// General functions
(function() {
- // Tabs
- var tabs = document.querySelectorAll(".nav-panel a"),
- panels = document.querySelectorAll(".panels > .panel"),
- activeClass = "active";
+ var buttons = document.querySelectorAll('[data-source]');
- for (var i = tabs.length - 1; i >= 0; i--) {
- tabs[i].addEventListener("click", togglePanel);
+ // Bind to each button
+ for (var i = buttons.length - 1; i >= 0; i--) {
+ buttons[i].addEventListener('click', newSource);
}
- function togglePanel(event) {
- event.preventDefault();
+ // Set a new source
+ function newSource() {
+ var trigger = this,
+ type = trigger.getAttribute('data-source'),
+ player = document.querySelector('.js-media-player').plyr;
+
+ switch(type) {
+ case 'video':
+ player.source({
+ type: 'video',
+ title: 'View From A Blue Moon',
+ sources: [{
+ src: 'https://cdn.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.mp4',
+ type: 'video/mp4'
+ },
+ {
+ src: 'https://cdn.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.webm',
+ type: 'video/webm'
+ }],
+ poster: 'https://cdn.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.jpg',
+ tracks: [{
+ kind: 'captions',
+ label: 'English',
+ srclang:'en',
+ src: 'https://cdn.selz.com/plyr/1.0/example_captions_en.vtt',
+ default: true
+ }]
+ });
+ break;
+
+ case 'audio':
+ player.source({
+ type: 'audio',
+ title: 'Kishi Bashi &ndash; &ldquo;It All Began With A Burst&rdquo;',
+ sources: [{
+ src: 'https://cdn.selz.com/plyr/1.5/Kishi_Bashi_-_It_All_Began_With_a_Burst.mp3',
+ type: 'audio/mp3'
+ },
+ {
+ src: 'https://cdn.selz.com/plyr/1.5/Kishi_Bashi_-_It_All_Began_With_a_Burst.ogg',
+ type: 'audio/ogg'
+ }]
+ });
+ break;
- var tab = event.target,
- panel = document.querySelector(tab.getAttribute("href"));
+ case 'youtube':
+ player.source({
+ type: 'video',
+ title: 'View From A Blue Moon',
+ sources: [{
+ src: 'bTqVqk7FSmY',
+ type: 'youtube'
+ }]
+ });
+ break;
- for (var i = panels.length - 1; i >= 0; i--) {
- panels[i].classList.remove(activeClass);
+ case 'vimeo':
+ player.source({
+ type: 'video',
+ title: 'View From A Blue Moon',
+ sources: [{
+ src: '143418951',
+ type: 'vimeo'
+ }]
+ });
+ break;
}
- for (var x = tabs.length - 1; x >= 0; x--) {
- tabs[x].classList.remove(activeClass);
+ for (var x = buttons.length - 1; x >= 0; x--) {
+ buttons[x].parentElement.classList.remove('active');
}
- panel.classList.add(activeClass);
- event.target.classList.add(activeClass);
+ event.target.parentElement.classList.add('active');
}
})();
// Google analytics
// For demo site (http://[www.]plyr.io) only
-if(document.domain.indexOf("plyr.io") > -1) {
+if(document.domain.indexOf('plyr.io') > -1) {
(function(i,s,o,g,r,a,m){i.GoogleAnalyticsObject=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
- })(window,document,"script","//www.google-analytics.com/analytics.js","ga");
- ga("create", "UA-40881672-11", "auto");
- ga("send", "pageview");
+ })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
+ ga('create', 'UA-40881672-11', 'auto');
+ ga('send', 'pageview');
}
diff --git a/docs/src/less/components/buttons.less b/docs/src/less/components/buttons.less
index 1401cd01..fef9af68 100644
--- a/docs/src/less/components/buttons.less
+++ b/docs/src/less/components/buttons.less
@@ -21,7 +21,7 @@ nav {
}
// Tabs
-.btn-bar {
+.btn__bar {
position: relative;
margin: 0 auto @padding-base;
max-width: @example-width-video;
@@ -55,21 +55,29 @@ nav {
& + li .btn {
margin-left: -1px;
}
+
+ &.active .btn {
+ &:extend(.btn--primary);
+ box-shadow: inset 0 1px 1px rgba(0,0,0, .2);
+ position: relative;
+ z-index: 1;
+
+ .icon {
+ color: inherit;
+ }
+ }
+ &.active + li .btn:hover {
+ z-index: 0;
+ }
}
.btn {
+ position: relative;
display: block;
border-radius: 0;
- }
- .active {
- &:extend(.btn-primary);
- }
- .btn.active {
- box-shadow: inset 0 1px 1px rgba(0,0,0, .2);
- position: relative;
- z-index: 1;
- .icon {
- color: inherit;
+ &:hover,
+ &:focus {
+ z-index: 1;
}
}
@@ -80,7 +88,7 @@ nav {
// Shared
.btn,
-.btn-count {
+.btn__count {
display: inline-block;
vertical-align: middle;
border-radius: @border-radius-base;
@@ -91,12 +99,12 @@ nav {
// Buttons
.btn {
padding: (@padding-base / 2) @padding-base;
- background: @body-background;
+ background: linear-gradient(lighten(@body-background, 2%), darken(@body-background, 3%));
border: 1px solid @gray-light;
- box-shadow: inset 0 1px 0 #fff, 0 1px 1px rgba(0,0,0, .05);
+ box-shadow: 0 1px 1px rgba(0,0,0, .05);
text-shadow: 0 1px 1px #fff;
color: @gray;
- transition: background .3s ease, border .3s ease, color .3s ease;
+ transition: background .1s ease, color .1s ease;
&:hover,
&:focus {
@@ -104,18 +112,18 @@ nav {
color: @gray;
outline: 0;
}
- &-youtube .icon {
+ &--youtube .icon {
color: @color-youtube;
}
- &-vimeo .icon {
+ &--vimeo .icon {
color: @color-vimeo;
}
- &-twitter .icon {
+ &--twitter .icon {
color: @color-twitter;
}
}
-.btn-primary {
- background-image: linear-gradient(@link-color, darken(@link-color, 3%));
+.btn--primary {
+ background-image: linear-gradient(@link-color, darken(@link-color, 5%));
background-color: @link-color;
border-color: darken(@link-color, 10%);
box-shadow: 0 1px 1px rgba(0,0,0, .15);
@@ -128,13 +136,13 @@ nav {
border-color: darken(@link-color, 20%);
}
}
-.btn-small {
+.btn--small {
padding-top: ceil(@padding-base / 3);
padding-bottom: ceil(@padding-base / 3);
}
// Count bubble
-.btn-count {
+.btn__count {
position: relative;
margin-left: (@padding-base / 2);
padding: (@padding-base / 2) (@padding-base * .75);
diff --git a/docs/src/less/components/examples.less b/docs/src/less/components/examples.less
index 97087b02..6dde9690 100644
--- a/docs/src/less/components/examples.less
+++ b/docs/src/less/components/examples.less
@@ -2,54 +2,31 @@
// Examples
// ==========================================================================
+video,
+.plyr__video-embed {
+ border-radius: @border-radius-base;
+}
+.plyr__video-embed {
+ -webkit-mask-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAA5JREFUeNpiYGBgAAgwAAAEAAGbA+oJAAAAAElFTkSuQmCC);
+}
+
// Example players
-.example-audio .player,
-.example-video .player {
+.plyr {
margin: 0 auto @padding-base;
+ max-width: @example-width-video;
- &-controls {
+ &__controls {
border-radius: 0 0 @border-radius-base @border-radius-base;
}
-}
-.example-audio .player {
- max-width: @example-width-audio;
-
- &-controls {
- border-radius: @border-radius-base;
- }
- &-progress {
- border-radius: @border-radius-base @border-radius-base 0 0;
- overflow: hidden;
- }
-}
-// Base styles
-.example-video .player {
- max-width: @example-width-video;
-
video,
- iframe {
- border-radius: @border-radius-base;
- }
- iframe {
- -webkit-mask-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAA5JREFUeNpiYGBgAAgwAAAEAAGbA+oJAAAAAElFTkSuQmCC);
- }
-}
-
-// Style full supported player
-.example-video .player-video,
-.example-video .player-youtube {
- video,
- iframe {
+ .plyr__video-embed {
border-radius: @border-radius-base @border-radius-base 0 0;
}
- iframe {
- -webkit-mask-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAA5JREFUeNpiYGBgAAgwAAAEAAGbA+oJAAAAAElFTkSuQmCC);
- }
- &-fullscreen,
- &.fullscreen-active {
+ &--fullscreen,
+ &--fullscreen-active {
max-width: none;
- .player-controls,
+ .plyr-controls,
video,
iframe {
border-radius: 0;
@@ -58,4 +35,31 @@
-webkit-mask-image: none;
}
}
-} \ No newline at end of file
+}
+.plyr--audio {
+ max-width: @example-width-audio;
+
+ .plyr__controls {
+ border-radius: @border-radius-base;
+ }
+ .plyr__progress {
+ border-radius: @border-radius-base @border-radius-base 0 0;
+ overflow: hidden;
+ }
+}
+
+// Style full supported player
+.plyr__cite {
+ display: none;
+
+ .icon {
+ margin-right: (@padding-base / 4);
+ }
+}
+
+.plyr--video ~ ul .plyr__cite--video,
+.plyr--audio ~ ul .plyr__cite--audio,
+.plyr--youtube ~ ul .plyr__cite--youtube,
+.plyr--vimeo ~ ul .plyr__cite--vimeo {
+ display: block;
+}
diff --git a/docs/src/less/components/panels.less b/docs/src/less/components/panels.less
deleted file mode 100644
index 290e5dfc..00000000
--- a/docs/src/less/components/panels.less
+++ /dev/null
@@ -1,13 +0,0 @@
-// ==========================================================================
-// Panels
-// ==========================================================================
-
-// Panels
-.panel {
- display: none;
-
- &.active {
- display: block;
- animation: fade-in .3s ease;
- }
-} \ No newline at end of file
diff --git a/docs/src/less/components/type.less b/docs/src/less/components/type.less
index 8fc200e2..854da12d 100644
--- a/docs/src/less/components/type.less
+++ b/docs/src/less/components/type.less
@@ -26,7 +26,13 @@ small {
padding: 0 (@padding-base / 2);
.font-size(14);
}
-ul
+sup {
+ vertical-align: 2px;
+ .font-size(9);
+}
+
+// Lists
+ul,
li {
list-style: none;
margin: 0;
@@ -53,9 +59,9 @@ a {
}
}
-.color-vimeo {
+.color--vimeo {
color: @color-vimeo;
}
-.color-youtube {
+.color--youtube {
color: @color-youtube;
}
diff --git a/docs/src/less/docs.less b/docs/src/less/docs.less
index 965dcba1..e828d3b3 100644
--- a/docs/src/less/docs.less
+++ b/docs/src/less/docs.less
@@ -22,6 +22,5 @@
@import "components/base.less";
@import "components/icons.less";
@import "components/buttons.less";
-@import "components/panels.less";
@import "components/error.less";
@import "components/examples.less";
diff --git a/docs/src/sprite/icon-github.svg b/docs/src/sprite/icon-github.svg
new file mode 100755
index 00000000..685dd746
--- /dev/null
+++ b/docs/src/sprite/icon-github.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="16px" height="16px" viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M8,0.2c-4.4,0-8,3.6-8,8c0,3.5,2.3,6.5,5.5,7.6
+ C5.9,15.9,6,15.6,6,15.4c0-0.2,0-0.7,0-1.4C3.8,14.5,3.3,13,3.3,13c-0.4-0.9-0.9-1.2-0.9-1.2c-0.7-0.5,0.1-0.5,0.1-0.5
+ c0.8,0.1,1.2,0.8,1.2,0.8C4.4,13.4,5.6,13,6,12.8c0.1-0.5,0.3-0.9,0.5-1.1c-1.8-0.2-3.6-0.9-3.6-4c0-0.9,0.3-1.6,0.8-2.1
+ c-0.1-0.2-0.4-1,0.1-2.1c0,0,0.7-0.2,2.2,0.8c0.6-0.2,1.3-0.3,2-0.3c0.7,0,1.4,0.1,2,0.3c1.5-1,2.2-0.8,2.2-0.8
+ c0.4,1.1,0.2,1.9,0.1,2.1c0.5,0.6,0.8,1.3,0.8,2.1c0,3.1-1.9,3.7-3.7,3.9C9.7,12,10,12.5,10,13.2c0,1.1,0,1.9,0,2.2
+ c0,0.2,0.1,0.5,0.6,0.4c3.2-1.1,5.5-4.1,5.5-7.6C16,3.8,12.4,0.2,8,0.2z"/>
+</svg>
diff --git a/docs/src/sprite/icon-twitter.svg b/docs/src/sprite/icon-twitter.svg
new file mode 100755
index 00000000..b3f644b1
--- /dev/null
+++ b/docs/src/sprite/icon-twitter.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="16px" height="16px" viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
+ <title>Twitter</title>
+<path d="M16,3c-0.6,0.3-1.2,0.4-1.9,0.5c0.7-0.4,1.2-1,1.4-1.8c-0.6,0.4-1.3,0.6-2.1,0.8c-0.6-0.6-1.5-1-2.4-1
+ C9.3,1.5,7.8,3,7.8,4.8c0,0.3,0,0.5,0.1,0.7C5.2,5.4,2.7,4.1,1.1,2.1c-0.3,0.5-0.4,1-0.4,1.7c0,1.1,0.6,2.1,1.5,2.7
+ c-0.5,0-1-0.2-1.5-0.4c0,0,0,0,0,0c0,1.6,1.1,2.9,2.6,3.2C3,9.4,2.7,9.4,2.4,9.4c-0.2,0-0.4,0-0.6-0.1c0.4,1.3,1.6,2.3,3.1,2.3
+ c-1.1,0.9-2.5,1.4-4.1,1.4c-0.3,0-0.5,0-0.8,0c1.5,0.9,3.2,1.5,5,1.5c6,0,9.3-5,9.3-9.3c0-0.1,0-0.3,0-0.4C15,4.3,15.6,3.7,16,3z"/>
+</svg>
diff --git a/docs/src/sprite/icon-vimeo.svg b/docs/src/sprite/icon-vimeo.svg
new file mode 100755
index 00000000..83dd3dc0
--- /dev/null
+++ b/docs/src/sprite/icon-vimeo.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="16px" height="16px" viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
+<path d="M16,4.3c-0.1,1.6-1.2,3.7-3.3,6.4c-2.2,2.8-4,4.2-5.5,4.2c-0.9,0-1.7-0.9-2.4-2.6C4,9.9,3.4,5,2,5
+ C1.9,5,1.5,5.3,0.8,5.8L0,4.8c0.8-0.7,3.5-3.4,4.7-3.5C5.9,1.2,6.7,2,7,3.8c0.3,2,0.8,6.1,1.8,6.1c0.9,0,2.5-3.4,2.6-4
+ c0.1-0.9-0.3-1.9-2.3-1.1c0.8-2.6,2.3-3.8,4.5-3.8C15.3,1.1,16.1,2.2,16,4.3z"/>
+</svg>
diff --git a/docs/src/sprite/icon-youtube.svg b/docs/src/sprite/icon-youtube.svg
new file mode 100755
index 00000000..8b5d6897
--- /dev/null
+++ b/docs/src/sprite/icon-youtube.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="16px" height="16px" viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
+<path d="M15.8,4.8c-0.2-1.3-0.8-2.2-2.2-2.4C11.4,2,8,2,8,2S4.6,2,2.4,2.4C1,2.6,0.3,3.5,0.2,4.8C0,6.1,0,8,0,8
+ s0,1.9,0.2,3.2c0.2,1.3,0.8,2.2,2.2,2.4C4.6,14,8,14,8,14s3.4,0,5.6-0.4c1.4-0.3,2-1.1,2.2-2.4C16,9.9,16,8,16,8S16,6.1,15.8,4.8z
+ M6,11V5l5,3L6,11z"/>
+</svg>
diff --git a/gulpfile.js b/gulpfile.js
index 865f2a56..ca7c0de5 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -41,7 +41,8 @@ paths = {
// Source paths
src: {
less: path.join(root, "docs/src/less/**/*"),
- js: path.join(root, "docs/src/js/**/*")
+ js: path.join(root, "docs/src/js/**/*"),
+ sprite: path.join(root, "docs/src/sprite/**/*")
},
// Output paths
output: path.join(root, "docs/dist/"),
@@ -55,7 +56,8 @@ paths = {
tasks = {
less: [],
sass: [],
- js: []
+ js: [],
+ sprite: []
},
// Fetch bundles from JSON
@@ -127,18 +129,22 @@ var build = {
})(key);
}
},
- sprite: function() {
+ sprite: function(bundle) {
+ var name = "sprite-" + bundle;
+ tasks.sprite.push(name);
+
// Process Icons
- gulp.task("sprite", function () {
+ gulp.task(name, function () {
return gulp
- .src(paths.plyr.src.sprite)
+ .src(paths[bundle].src.sprite)
.pipe(svgmin({
plugins: [{
removeDesc: true
}]
}))
.pipe(svgstore())
- .pipe(gulp.dest(paths.plyr.output));
+ .pipe(rename({ basename: (bundle == "plyr" ? "sprite" : bundle) }))
+ .pipe(gulp.dest(paths[bundle].output));
});
}
};
@@ -147,11 +153,12 @@ var build = {
build.js(bundles.plyr.js, "plyr");
build.less(bundles.plyr.less, "plyr");
build.sass(bundles.plyr.sass, "plyr");
-build.sprite();
+build.sprite("plyr");
// Docs files
build.less(bundles.docs.less, "docs");
build.js(bundles.docs.js, "docs");
+build.sprite("docs");
// Build all JS
gulp.task("js", function(){
@@ -168,16 +175,17 @@ gulp.task("watch", function () {
// Plyr core
gulp.watch(paths.plyr.src.js, tasks.js);
gulp.watch(paths.plyr.src.less, tasks.less);
- gulp.watch(paths.plyr.src.sprite, ["sprite"]);
+ gulp.watch(paths.plyr.src.sprite, tasks.sprite);
// Docs
gulp.watch(paths.docs.src.js, tasks.js);
gulp.watch(paths.docs.src.less, tasks.less);
+ gulp.watch(paths.docs.src.sprite, tasks.sprite);
});
// Default gulp task
gulp.task("default", function(){
- run(tasks.js, tasks.less, "sprite", "watch");
+ run(tasks.js, tasks.less, tasks.sprite, "watch");
});
// Publish a version to CDN and docs
@@ -206,7 +214,8 @@ options = {
// If aws is setup
if("cdn" in aws) {
- var cdnpath = new RegExp(aws.cdn.bucket + "\/(\\d+\\.)?(\\d+\\.)?(\\*|\\d+)","gi");
+ var cdnpath = new RegExp(aws.cdn.bucket + "\/(\\d+\\.)?(\\d+\\.)?(\\*|\\d+)","gi"),
+ localpath = new RegExp("(\.\.\/)?dist", "gi");
}
// Publish version to CDN bucket
@@ -235,14 +244,14 @@ gulp.task("docs", function () {
.pipe(replace(cdnpath, aws.cdn.bucket + "/" + version))
.pipe(gulp.dest(root));
- // Replace versioned files in *.html
+ // Replace local file paths with remote paths in docs
+ // e.g. "../dist/plyr.js" to "https://cdn.plyr.io/x.x.x/plyr.js"
gulp.src([paths.docs.root + "*.html"])
- .pipe(replace(cdnpath, aws.cdn.bucket + "/" + version))
- .pipe(gulp.dest(paths.docs.root))
+ .pipe(replace(localpath, "https://" + aws.cdn.bucket + "/" + version))
.pipe(gzip())
.pipe(s3(aws.docs, options.docs));
- // Upload error.html to cdn using docs options
+ // Upload error.html to cdn (as well as docs site)
gulp.src([paths.docs.root + "error.html"])
.pipe(gzip())
.pipe(s3(aws.cdn, options.docs));
@@ -263,5 +272,5 @@ gulp.task("open", function () {
// Do everything
gulp.task("publish", function () {
- run(tasks.js, tasks.less, "sprite", "cdn", "docs");
+ run(tasks.js, tasks.less, tasks.sprite, "cdn", "docs");
});
diff --git a/package.json b/package.json
index db78a333..751ce67e 100644
--- a/package.json
+++ b/package.json
@@ -1,12 +1,12 @@
{
"name": "plyr",
- "version": "1.3.7",
+ "version": "1.5.0",
"description": "A simple HTML5 media player using custom controls",
"homepage": "http://plyr.io",
"main": "gulpfile.js",
"dependencies": {},
"devDependencies": {
- "gulp": "^3.8.6",
+ "gulp": "^3.9.0",
"gulp-autoprefixer": "^3.1.0",
"gulp-concat": "^2.3.3",
"gulp-gzip": "^1.0.0",
diff --git a/readme.md b/readme.md
index ce647687..6fe7936d 100644
--- a/readme.md
+++ b/readme.md
@@ -1,12 +1,12 @@
# Plyr
-A simple, accessible HTML5 media player.
+A simple, accessible HTML5 media player.
Checkout the [demo](http://plyr.io).
[![Image of Plyr](https://cdn.plyr.io/static/plyr.jpg)](http://plyr.io)
## Why?
-We wanted a lightweight, accessible and customisable media player that just supports [*modern*](#browser-support) browsers. Sure, there are many other players out there but we wanted to keep things simple, using the right elements for the job.
+We wanted a lightweight, accessible and customizable media player that just supports [*modern*](#browser-support) browsers. Sure, there are many other players out there but we wanted to keep things simple, using the right elements for the job.
## Features
- **Accessible** - full support for VTT captions and screen readers.
@@ -15,19 +15,18 @@ We wanted a lightweight, accessible and customisable media player that just supp
- **Semantic** - uses the *right* elements. `<input type="range">` for volume and `<progress>` for progress and well, `<button>`s for buttons. There's no `<span>` or `<a href="#">` button hacks.
- **Responsive** - as you'd expect these days.
- **Audio & Video** - support for both formats.
-- **[Embedded Video](#embeds)** - support for YouTube (Vimeo soon).
+- **[Embedded Video](#embeds)** - support for YouTube and Vimeo (beta).
- **[API](#api)** - toggle playback, volume, seeking, and more.
- **[Fullscreen](#fullscreen)** - supports native fullscreen with fallback to "full window" modes.
- **i18n support** - support for internationalization of controls.
-- **No dependencies** - written in vanilla JavaScript, no jQuery required.
+- **No dependencies** - written in vanilla JavaScript, no jQuery required.
-Oh and yes, it works with Bootstrap.
+Oh and yes, it works with Bootstrap.
## Changelog
-Check out the [changelog](changelog.md).
+Check out the [changelog](changelog.md) to see what's been new with Plyr.
## Planned Development
-- Vimeo support
- Playback speed
- Playlists
- Multiple language captions (with selection)
@@ -38,7 +37,7 @@ If you have any cool ideas or features, please let me know by [creating an issue
## Implementation
-Check `docs/index.html` and `docs/dist/docs.js` for an example setup.
+Check `docs/index.html` and `docs/dist/docs.js` for an example setup.
**Heads up:** the example `index.html` file needs to be served from a webserver (such as Apache, Nginx, IIS or similar) unless you change the file sources to include http or https. e.g. change `//cdn.plyr.io/1.3.7/plyr.js` to `https://cdn.plyr.io/1.3.7/plyr.js`
@@ -56,7 +55,7 @@ ember addon:install ember-cli-plyr
```
More info is on [npm](https://www.npmjs.com/package/ember-cli-plyr) and [GitHub](https://github.com/louisrudner/ember-cli-plyr)
-### CDN
+### CDN
If you want to use our CDN, you can use the following:
```html
@@ -66,13 +65,15 @@ If you want to use our CDN, you can use the following:
You can also access the `sprite.svg` file at `https://cdn.plyr.io/1.3.7/sprite.svg`.
-### CSS
-If you want to use the default css, add the `plyr.css` file from `/dist` into your head, or even better use `plyr.less` or `plyr.sass` file included in `/src` in your build to save a request.
+### CSS & Styling
+If you want to use the default css, add the `plyr.css` file from `/dist` into your head, or even better use `plyr.less` or `plyr.sass` file included in `/src` in your build to save a request.
```html
<link rel="stylesheet" href="dist/plyr.css">
```
+The default setup uses the BEM methodology with `plyr` as the block, e.g. `.plyr__controls`. You can change the class hooks in the options. Check out the source for more on this.
+
### SVG
The SVG sprite for the controls icons is loaded in by AJAX to help with performance. This is best added before the closing `</body>`, before any other scripts.
@@ -92,68 +93,99 @@ The SVG sprite for the controls icons is loaded in by AJAX to help with performa
})(document, "dist/sprite.svg");
</script>
```
+
+If you're using the `<base>` tag on your site, you may need to use something like this:
+[https://gist.github.com/leonderijke/c5cf7c5b2e424c0061d2](svgfixer.js)
+
More info on SVG sprites here:
-[http://css-tricks.com/svg-sprites-use-better-icon-fonts/](http://css-tricks.com/svg-sprites-use-better-icon-fonts/)
-and the AJAX technique here:
+[http://css-tricks.com/svg-sprites-use-better-icon-fonts/](http://css-tricks.com/svg-sprites-use-better-icon-fonts/)
+and the AJAX technique here:
[http://css-tricks.com/ajaxing-svg-sprite/](http://css-tricks.com/ajaxing-svg-sprite/)
### HTML
The only extra markup that's needed to use plyr is a `<div>` wrapper. Replace the source, poster and captions with urls for your media.
```html
-<div class="player">
- <video poster="https://cdn.selz.com/plyr/1.0/poster.jpg" controls crossorigin>
+<div class="plyr">
+ <video poster="/path/to/poster.jpg" controls>
<!-- Video files -->
- <source src="https://cdn.selz.com/plyr/1.0/movie.mp4" type="video/mp4">
- <source src="https://cdn.selz.com/plyr/1.0/movie.webm" type="video/webm">
-
+ <source src="/path/to/video.mp4" type="video/mp4">
+ <source src="/path/to/video.webm" type="video/webm">
+
<!-- Text track file -->
- <track kind="captions" label="English captions" src="https://cdn.selz.com/plyr/1.0/movie_captions_en.vtt" srclang="en" default>
-
+ <track kind="captions" label="English captions" src="/path/to/captions.vtt" srclang="en" default>
+
<!-- Fallback for browsers that don't support the <video> element -->
- <a href="https://cdn.selz.com/plyr/1.0/movie.mp4">Download</a>
+ <a href="/path/to/movie.mp4">Download</a>
</video>
</div>
```
And the same for `<audio>`
```html
-<div class="player">
+<div class="plyr">
<audio controls>
<!-- Audio files -->
- <source src="https://cdn.selz.com/plyr/1.0/logistics-96-sample.mp3" type="audio/mp3">
- <source src="https://cdn.selz.com/plyr/1.0/logistics-96-sample.ogg" type="audio/ogg">
-
+ <source src="/path/to/audio.mp3" type="audio/mp3">
+ <source src="/path/to/audio.ogg" type="audio/ogg">
+
<!-- Fallback for browsers that don't support the <audio> element -->
- <a href="https://cdn.selz.com/plyr/1.0/logistics-96-sample.mp3">Download</a>
+ <a href="/path/to/audio.mp3">Download</a>
</audio>
</div>
-```
+```
For YouTube, Plyr uses the standard YouTube API markup (an empty `<div>`):
```html
-<div class="player">
+<div class="plyr">
<div data-video-id="L1h9xxCU20g" data-type="youtube"></div>
</div>
```
#### Cross Origin (CORS)
-You'll notice the `crossorigin` attribute on the example `<video>` and `<audio>` elements. This is because the media is loaded from another domain. If your media is hosted on another domain, you may need to add this attribute.
+You'll notice the `crossorigin` attribute on the example `<video>` and `<audio>` elements. This is because the media is loaded from another domain. If your media is hosted on another domain, you may need to add this attribute.
More info on CORS here:
[https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS)
### JavaScript
-Much of the behaviour of the player is configurable when initialising the library. Here's an example of a default setup:
+
+#### Quick setup
+
+Here's an example of a default setup:
```html
-<script src="dist/plyr.js"></script>
+<script src="https://cdn.plyr.io/1.3.5/plyr.js"></script>
<script>plyr.setup();</script>
```
+This will look for all elements with the specified container classname (default is `plyr`) and setup plyr on each element found. You can specify other options, including a different selector hook below. The container classname will be added to the specified element(s) if it is not already present (for the CSS).
+
+You can initialize the player a few other ways:
+
+Passing a [NodeList](https://developer.mozilla.org/en-US/docs/Web/API/NodeList):
+```javascript
+plyr.setup(document.querySelectorAll('.js-plyr'), options);
+```
+
+Passing a [HTMLElement](https://developer.mozilla.org/en/docs/Web/API/HTMLElement):
+```javascript
+plyr.setup(document.querySelector('.js-plyr'), options);
+```
+
+Passing a [string selector](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll):
+```javascript
+plyr.setup('.js-plyr', options);
+```
+
+Passing just the options object:
+```javascript
+plyr.setup(options);
+```
+
#### Options
-You can pass the following options to the setup method using `plyr.setup({...})`.
+Options must be passed as an object to the `setup()` method as above.
<table class="table" width="100%">
<thead>
@@ -169,7 +201,7 @@ You can pass the following options to the setup method using `plyr.setup({...})`
<td><code>enabled</code></td>
<td>Boolean</td>
<td><code>true</code></td>
- <td>Completely disable Plyr. This would allow you to do a User Agent check or similar to programatically enable or disable Plyr for a certain UA. Example below.</td>
+ <td>Completely disable Plyr. This would allow you to do a User Agent check or similar to programmatically enable or disable Plyr for a certain UA. Example below.</td>
</tr>
<tr>
<td><code>html</code></td>
@@ -187,7 +219,7 @@ You can pass the following options to the setup method using `plyr.setup({...})`
<td><code>i18n</code></td>
<td>Object</td>
<td><code><a href="controls.md">See controls.md</a></code></td>
- <td>Used for internationalisation (i18n) of the tooltips/labels within the buttons.</td>
+ <td>Used for internationalization (i18n) of the tooltips/labels within the buttons.</td>
</tr>
<tr>
<td><code>iconPrefix</code></td>
@@ -235,7 +267,7 @@ You can pass the following options to the setup method using `plyr.setup({...})`
<td><code>selectors</code></td>
<td>Object</td>
<td>&mdash;</td>
- <td>See <code>plyr.js</code> in <code>/src</code> for more info. The only option you might want to change is <code>player</code> which is the hook used for Plyr, the default is <code>.player</code>.</td>
+ <td>See <code>plyr.js</code> in <code>/src</code> for more info. You probably don't need to change any of these.</td>
</tr>
<tr>
<td><code>classes</code></td>
@@ -253,7 +285,7 @@ You can pass the following options to the setup method using `plyr.setup({...})`
<td><code>fullscreen</code></td>
<td>Object</td>
<td>&mdash;</td>
- <td>Three properties; <code>enabled</code> which toggles if fullscreen should be enabled (if the browser supports it). The default value is <code>true</code>. A <code>fallback</code> property which will enable a full window view for older browsers. The default value is <code>true</code>. A <code>hideControls</code> property which will hide the controls when fullscreen is active and the video is playing, after 1s. The controls reappear on hover of the progress bar (mouse), focusing a child control or pausing the video (by tap/click of video if `click` is `true`). The default value is <code>true</code>.</td>
+ <td>See <a href="#fullscreen-options">below</a></td>
</tr>
<tr>
<td><code>storage</code></td>
@@ -270,12 +302,68 @@ You can pass the following options to the setup method using `plyr.setup({...})`
</tbody>
</table>
+#### Fullscreen options
+
+<table class="table" width="100%" id="fullscreen-options">
+<thead>
+ <tr>
+ <th width="20%">Option</th>
+ <th width="15%">Type</th>
+ <th width="15%">Default</th>
+ <th width="50%">Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><code>enabled</code></td>
+ <td>Boolean</td>
+ <td><code>true</code></td>
+ <td>Toggles if fullscreen should be enabled (if the browser supports it).</td>
+ </tr>
+ <tr>
+ <td><code>fallback</code></td>
+ <td>Boolean</td>
+ <td><code>true</code></td>
+ <td>Enable a full viewport view for older browsers.</td>
+ </tr>
+ <tr>
+ <td><code>hideControls</code></td>
+ <td>Boolean</td>
+ <td><code>true</code></td>
+ <td>Hide the controls when fullscreen is active and the video is playing, after 1s. The controls reappear on hover of the progress bar (mouse), focusing a child control or pausing the video (by tap/click of video if `click` is `true`).</td>
+ </tr>
+ <tr>
+ <td><code>allowAudio</code></td>
+ <td>Boolean</td>
+ <td><code>false</code></td>
+ <td>Allow audio play to toggle fullscreen. This will be more useful later when posters are supported.</td>
+ </tr>
+ </tbody>
+</table>
+
## API
-A `plyr` object is added to any element that Plyr is initialised on. You can then control the player by accessing methods in the `plyr` object. For example if you wanted to pause Plyr:
+#### Fetching the plyr instance
+A `plyr` object is added to any element that Plyr is initialized on. You can then control the player by accessing methods in the `plyr` object.
+
+There are two ways to access the instance, firstly you re-query the element container you used for setup (e.g. `.js-plyr`) like so:
+
+```javascript
+var player = document.querySelector('.js-plyr');
+```
+
+Or you can use the returned object from your call to the setup method:
+
+```javascript
+var player = plyr.setup('.js-plyr')[0];
+```
+
+This will return an array of plyr instances setup, so you need to specify the index of the instance you want. This is less useful if you are setting up mutliple instances. You can also use the `onSetup` callback documented below which will return each instance one by one, as they are setup.
+
+Once you have your instance, you can use the API methods below on it. For example to pause it:
```javascript
-document.querySelectorAll(".player")[0].plyr.pause();
+player.pause();
```
Here's a list of the methods supported:
@@ -287,8 +375,8 @@ Here's a list of the methods supported:
<th width="15%">Parameters</th>
<th width="65%">Description</th>
</tr>
- </thead>
- <tbody>
+</thead>
+<tbody>
<tr>
<td><code>play()</code></td>
<td>&mdash;</td>
@@ -356,7 +444,7 @@ Here's a list of the methods supported:
</tr>
<tr>
<td><code>source(...)</code></td>
- <td>String or Array or (null|undefined)</td>
+ <td>Array or (null|undefined)</td>
<td>
Get/Set the media source.
<br><br>
@@ -366,7 +454,7 @@ Here's a list of the methods supported:
<br><br>
<strong>array</strong><br>
<code>.source([{ src: "/path/to/video.webm", type: "video/webm", ...more attributes... }, { src: "/path/to/video.mp4", type: "video/mp4", ...more attributes... }])`</code><br>
- This will inject a child `source` element for every element in the array with the specified attributes. `src` is the only required attribute although adding `type` is recommended as it helps the browser decide which file to download and play.
+ This will inject a child `source` element for every element in the array with the specified attributes. `src` is the only required attribute although adding `type` is recommended as it helps the browser decide which file to download and play.
<br><br>
<strong>YouTube</strong><br>
Currently this API method only accepts a YouTube ID when used with a YouTube player. I will add URL support soon, along with being able to swap between types (e.g. YouTube to Audio or Video and vice versa.)
@@ -393,14 +481,126 @@ Here's a list of the methods supported:
</tbody>
</table>
+#### .source() method
+
+This allows changing the plyr source and type on the fly.
+
+Video example:
+
+```javascript
+player.source({
+ type: 'video',
+ title: 'Example title',
+ sources: [{
+ src: '/path/to/movie.mp4',
+ type: 'video/mp4'
+ },
+ {
+ src: '/path/to/movie.webm',
+ type: 'video/webm'
+ }],
+ poster: '/path/to/poster.jpg',
+ tracks: [{
+ kind: 'captions',
+ label: 'English',
+ srclang:'en',
+ src: '/path/to/captions.vtt',
+ default: true
+ }]
+});
+```
+
+Audio example:
+
+```javascript
+player.source({
+ type: 'audio',
+ title: 'Example title',
+ sources: [{
+ src: '/path/to/audio.mp3',
+ type: 'audio/mp3'
+ },
+ {
+ src: '/path/to/audio.ogg',
+ type: 'audio/ogg'
+ }]
+});
+```
+
+YouTube example:
+
+```javascript
+player.source({
+ type: 'video',
+ title: 'Example title',
+ sources: [{
+ src: 'bTqVqk7FSmY',
+ type: 'youtube'
+ }]
+});
+```
+
+Vimeo example
+
+```javascript
+player.source({
+ type: 'video',
+ title: 'Example title',
+ sources: [{
+ src: '143418951',
+ type: 'vimeo'
+ }]
+});
+```
+
+Some more details on the object parameters
+
+<table class="table" width="100%">
+ <thead>
+ <tr>
+ <th width="20%">Key</th>
+ <th width="15%">Type</th>
+ <th width="65%">Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><code>type</code></td>
+ <td>String</td>
+ <td>Options are <code>video</code>, <code>audio</code>, <code>youtube</code> and <code>vimeo</code></td>
+ </tr>
+ <tr>
+ <td><code>title</code></td>
+ <td>String</td>
+ <td>Title of the new media. Used for the aria labelling.</td>
+ </tr>
+ <tr>
+ <td><code>sources</code></td>
+ <td>Array or String</td>
+ <td>This is an array of sources or optionally a string for embedded players (YouTube and Vimeo). `type` is also optional for YouTube and Vimeo when specifying an array. For YouTube and Vimeo media, only the video ID must be passed as the source as shown above. The keys of this object are mapped directly to HTML attributes so more can be added to the object if required.</td>
+ </tr>
+ <tr>
+ <td><code>poster</code></td>
+ <td>String</td>
+ <td>URL for the poster image (video only).</td>
+ </tr>
+ <tr>
+ <td><code>tracks</code></td>
+ <td>Array</td>
+ <td>An array of track objects. Each element in the array is mapped directly to a track element and any keys mapped directly to HTML attributes so as in the example above, it will render as `<track kind="captions" label="English" srclang="en" src="https://cdn.selz.com/plyr/1.0/example_captions_en.vtt" default>`. Booleans are converted to HTML5 value-less attributes.</td>
+ </tr>
+ </tbody>
+</table>
+
+
## Events/Callbacks
The `plyr` object on the player element also contains a `media` property which is a reference to the `<audio>` or `<video>` element within the player which you can use to listen for events. Here's an example:
```javascript
-var media = document.querySelectorAll(".player")[0].plyr.media;
+var media = document.querySelector(".plyr").plyr.media;
-media.addEventListener("playing", function() {
+media.addEventListener("playing", function() {
console.log("playing");
});
```
@@ -418,18 +618,18 @@ Currently only YouTube is supported. Vimeo will be coming soon. Some HTML5 media
Due to the way the YouTube API works, the `timeupdate` and `progress` events are triggered by polling every 200ms so the event may trigger without an actual value change. Buffering progress is `media.buffered` in the `plyr` object. It is a a number between 0 and 1 that specifies the percentage of the video that the player shows as buffered.
```javascript
-document.querySelector(".player").plyr.media.addEventListener("play", function() {
+document.querySelector(".plyr").plyr.media.addEventListener("play", function() {
console.log("play");
});
-```
+```
-The `.source()` API method can also be used but the video id must be passed as the argument.
+The `.source()` API method can also be used but the video id must be passed as the argument.
-Currently caption control is not supported but I will work on this.
+Currently caption control is not supported but I will work on this.
## Fullscreen
-Fullscreen in Plyr is supported for all browsers that [currently support it](http://caniuse.com/#feat=fullscreen). If you're using the default CSS, you can also use a "full browser" mode which will use the full browser window by adding the `player-fullscreen` class to your container.
+Fullscreen in Plyr is supported for all browsers that [currently support it](http://caniuse.com/#feat=fullscreen). If you're using the default CSS, you can also use a "full browser" mode which will use the full browser window by adding the `plyr-fullscreen` class to your container.
## Browser support
@@ -462,7 +662,7 @@ Fullscreen in Plyr is supported for all browsers that [currently support it](htt
&sup3; IE10 has no native fullscreen support, fallback can be used (see options)
-The `enabled` option can be used to disable certain User Agents. For example, if you don't want to use Plyr for smartphones, you could use:
+The `enabled` option can be used to disable certain User Agents. For example, if you don't want to use Plyr for smartphones, you could use:
```javascript
enabled: /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent)
@@ -472,13 +672,13 @@ If a User Agent is disabled but supports `<video>` and `<audio>` natively, it wi
Any unsupported browsers will display links to download the media if the correct html is used.
### Checking for support
-There's an API method for checking support. You can call `plyr.supported()` and optionally pass a type to it, e.g. `plyr.supported("video")`. It will return an object with two keys; `basic` meaning there's basic support for that media type (or both if no type is passed) and `full` meaning there's full support for plyr.
+There's an API method for checking support. You can call `plyr.supported()` and optionally pass a type to it, e.g. `plyr.supported("video")`. It will return an object with two keys; `basic` meaning there's basic support for that media type (or both if no type is passed) and `full` meaning there's full support for plyr.
## Issues
If you find anything weird with Plyr, please let us know using the GitHub issues tracker.
## Author
-Plyr is developed by [@sam_potts](https://twitter.com/sam_potts) / [sampotts.me](http://sampotts.me) with help from the awesome [contributors](https://github.com/Selz/plyr/graphs/contributors)
+Plyr is developed by [@sam_potts](https://twitter.com/sam_potts) / [sampotts.me](http://sampotts.me) with help from the awesome [contributors](https://github.com/Selz/plyr/graphs/contributors)
## Mentions
- [The Changelog](http://thechangelog.com/plyr-simple-html5-media-player-custom-controls-webvtt-captions/)
@@ -493,7 +693,7 @@ Plyr is developed by [@sam_potts](https://twitter.com/sam_potts) / [sampotts.me]
## Used by
- [Selz.com](https://selz.com)
-Let me know on [Twitter](https://twitter.com/sam_potts) I can add you to the above list. It'd be awesome to see how you're using Plyr :-)
+Let me know on [Twitter](https://twitter.com/sam_potts) I can add you to the above list. It'd be awesome to see how you're using Plyr :-)
## Useful links and credits
Credit to the PayPal HTML5 Video player from which Plyr's caption functionality is ported from:
diff --git a/src/js/plyr.js b/src/js/plyr.js
index 638deaad..b3e9cfdf 100644
--- a/src/js/plyr.js
+++ b/src/js/plyr.js
@@ -1,24 +1,39 @@
// ==========================================================================
// Plyr
-// plyr.js v1.3.6
+// plyr.js v1.5.0
// https://github.com/selz/plyr
// License: The MIT License (MIT)
// ==========================================================================
// Credits: http://paypal.github.io/accessible-html5-video-player/
// ==========================================================================
-(function (api) {
+(function(root, factory) {
'use strict';
- /*global YT*/
+ /*global define,module*/
+
+ if (typeof define === 'function' && define.amd) {
+ // AMD
+ define(null, function() { factory(root, document) });
+ } else if (typeof module === 'object') {
+ // Node, CommonJS-like
+ module.exports = factory(root, document);
+ } else {
+ // Browser globals (root is window)
+ root.plyr = factory(root, document);
+ }
+}(this, function(window, document) {
+ 'use strict';
+ /*global YT,$f*/
// Globals
- var fullscreen, config, callbacks = { youtube: [] };
+ var fullscreen, config, api = {};
// Default config
var defaults = {
enabled: true,
debug: false,
autoplay: false,
+ loop: false,
seekTime: 10,
volume: 5,
click: true,
@@ -26,50 +41,56 @@
displayDuration: true,
iconPrefix: 'icon',
selectors: {
- container: '.player',
- controls: '.player-controls',
- labels: '[data-player] .sr-only, label .sr-only',
+ container: '.plyr',
+ controls: {
+ container: null,
+ wrapper: '.plyr__controls'
+ },
+ labels: '[data-plyr] .sr-only, label .sr-only',
buttons: {
- seek: '[data-player="seek"]',
- play: '[data-player="play"]',
- pause: '[data-player="pause"]',
- restart: '[data-player="restart"]',
- rewind: '[data-player="rewind"]',
- forward: '[data-player="fast-forward"]',
- mute: '[data-player="mute"]',
- volume: '[data-player="volume"]',
- captions: '[data-player="captions"]',
- fullscreen: '[data-player="fullscreen"]'
+ seek: '[data-plyr="seek"]',
+ play: '[data-plyr="play"]',
+ pause: '[data-plyr="pause"]',
+ restart: '[data-plyr="restart"]',
+ rewind: '[data-plyr="rewind"]',
+ forward: '[data-plyr="fast-forward"]',
+ mute: '[data-plyr="mute"]',
+ volume: '[data-plyr="volume"]',
+ captions: '[data-plyr="captions"]',
+ fullscreen: '[data-plyr="fullscreen"]'
},
progress: {
- container: '.player-progress',
- buffer: '.player-progress-buffer',
- played: '.player-progress-played'
+ container: '.plyr__progress',
+ buffer: '.plyr__progress--buffer',
+ played: '.plyr__progress--played'
},
- captions: '.player-captions',
- currentTime: '.player-current-time',
- duration: '.player-duration'
+ captions: '.plyr__captions',
+ currentTime: '.plyr__time--current',
+ duration: '.plyr__time--duration'
},
classes: {
- videoWrapper: 'player-video-wrapper',
- embedWrapper: 'player-video-embed',
- type: 'player-{0}',
- stopped: 'stopped',
- playing: 'playing',
- muted: 'muted',
- loading: 'loading',
- tooltip: 'player-tooltip',
- hidden: 'sr-only',
- hover: 'player-hover',
+ videoWrapper: 'plyr__video-wrapper',
+ embedWrapper: 'plyr__video-embed',
+ type: 'plyr--{0}',
+ stopped: 'plyr--stopped',
+ playing: 'plyr--playing',
+ muted: 'plyr--muted',
+ loading: 'plyr--loading',
+ hover: 'plyr--hover',
+ tooltip: 'plyr__tooltip',
+ hidden: 'plyr__sr-only',
+ isIos: 'plyr--is-ios',
+ isTouch: 'plyr--is-touch',
captions: {
- enabled: 'captions-enabled',
- active: 'captions-active'
+ enabled: 'plyr--captions-enabled',
+ active: 'plyr--captions-active'
},
fullscreen: {
- enabled: 'fullscreen-enabled',
- active: 'fullscreen-active',
- hideControls: 'fullscreen-hide-controls'
- }
+ enabled: 'plyr--fullscreen-enabled',
+ active: 'plyr--fullscreen-active',
+ hideControls: 'plyr--fullscreen--hide-controls'
+ },
+ tabFocus: 'tab-focus'
},
captions: {
defaultActive: false
@@ -77,7 +98,8 @@
fullscreen: {
enabled: true,
fallback: true,
- hideControls: true
+ hideControls: true,
+ allowAudio: false
},
storage: {
enabled: true,
@@ -97,7 +119,20 @@
volume: 'Volume',
toggleMute: 'Toggle Mute',
toggleCaptions: 'Toggle Captions',
- toggleFullscreen: 'Toggle Fullscreen'
+ toggleFullscreen: 'Toggle Fullscreen',
+ frameTitle: 'Player for {title}'
+ },
+ types: {
+ embed: ['youtube', 'vimeo'],
+ html5: ['video', 'audio']
+ },
+ urls: {
+ vimeo: {
+ api: 'http://cdn.plyr.io/froogaloop/1.0.0/plyr.froogaloop.js',
+ },
+ youtube: {
+ api: 'https://www.youtube.com/iframe_api'
+ }
}
};
@@ -105,25 +140,25 @@
function _buildControls() {
// Open and add the progress and seek elements
var html = [
- '<div class="player-controls">',
- '<div class="player-progress">',
- '<label for="seek{id}" class="sr-only">Seek</label>',
- '<input id="seek{id}" class="player-progress-seek" type="range" min="0" max="100" step="0.5" value="0" data-player="seek">',
- '<progress class="player-progress-played" max="100" value="0">',
+ '<div class="plyr__controls">',
+ '<div 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.5" value="0" data-plyr="seek">',
+ '<progress class="plyr__progress--played" max="100" value="0">',
'<span>0</span>% ' + config.i18n.played,
'</progress>',
- '<progress class="player-progress-buffer" max="100" value="0">',
+ '<progress class="plyr__progress--buffer" max="100" value="0">',
'<span>0</span>% ' + config.i18n.buffered,
'</progress>',
'</div>',
- '<span class="player-controls-left">'];
+ '<span class="plyr__controls--left">'];
// Restart button
if (_inArray(config.controls, 'restart')) {
html.push(
- '<button type="button" data-player="restart">',
+ '<button type="button" data-plyr="restart">',
'<svg><use xlink:href="#' + config.iconPrefix + '-restart" /></svg>',
- '<span class="sr-only">' + config.i18n.restart + '</span>',
+ '<span class="plyr__sr-only">' + config.i18n.restart + '</span>',
'</button>'
);
}
@@ -131,9 +166,9 @@
// Rewind button
if (_inArray(config.controls, 'rewind')) {
html.push(
- '<button type="button" data-player="rewind">',
+ '<button type="button" data-plyr="rewind">',
'<svg><use xlink:href="#' + config.iconPrefix + '-rewind" /></svg>',
- '<span class="sr-only">' + config.i18n.rewind + '</span>',
+ '<span class="plyr__sr-only">' + config.i18n.rewind + '</span>',
'</button>'
);
}
@@ -141,13 +176,13 @@
// Play/pause button
if (_inArray(config.controls, 'play')) {
html.push(
- '<button type="button" data-player="play">',
+ '<button type="button" data-plyr="play">',
'<svg><use xlink:href="#' + config.iconPrefix + '-play" /></svg>',
- '<span class="sr-only">' + config.i18n.play + '</span>',
+ '<span class="plyr__sr-only">' + config.i18n.play + '</span>',
'</button>',
- '<button type="button" data-player="pause">',
+ '<button type="button" data-plyr="pause">',
'<svg><use xlink:href="#' + config.iconPrefix + '-pause" /></svg>',
- '<span class="sr-only">' + config.i18n.pause + '</span>',
+ '<span class="plyr__sr-only">' + config.i18n.pause + '</span>',
'</button>'
);
}
@@ -155,9 +190,9 @@
// Fast forward button
if (_inArray(config.controls, 'fast-forward')) {
html.push(
- '<button type="button" data-player="fast-forward">',
+ '<button type="button" data-plyr="fast-forward">',
'<svg><use xlink:href="#' + config.iconPrefix + '-fast-forward" /></svg>',
- '<span class="sr-only">' + config.i18n.forward + '</span>',
+ '<span class="plyr__sr-only">' + config.i18n.forward + '</span>',
'</button>'
);
}
@@ -165,9 +200,9 @@
// Media current time display
if (_inArray(config.controls, 'current-time')) {
html.push(
- '<span class="player-time">',
- '<span class="sr-only">' + config.i18n.currentTime + '</span>',
- '<span class="player-current-time">00:00</span>',
+ '<span class="plyr__time">',
+ '<span class="plyr__sr-only">' + config.i18n.currentTime + '</span>',
+ '<span class="plyr__time--current">00:00</span>',
'</span>'
);
}
@@ -175,9 +210,9 @@
// Media duration display
if (_inArray(config.controls, 'duration')) {
html.push(
- '<span class="player-time">',
- '<span class="sr-only">' + config.i18n.duration + '</span>',
- '<span class="player-duration">00:00</span>',
+ '<span class="plyr__time">',
+ '<span class="plyr__sr-only">' + config.i18n.duration + '</span>',
+ '<span class="plyr__time--duration">00:00</span>',
'</span>'
);
}
@@ -185,16 +220,16 @@
// Close left controls
html.push(
'</span>',
- '<span class="player-controls-right">'
+ '<span class="plyr__controls--right">'
);
// Toggle mute button
if (_inArray(config.controls, 'mute')) {
html.push(
- '<button type="button" data-player="mute">',
- '<svg class="icon-muted"><use xlink:href="#' + config.iconPrefix + '-muted" /></svg>',
+ '<button type="button" data-plyr="mute">',
+ '<svg class="icon--muted"><use xlink:href="#' + config.iconPrefix + '-muted" /></svg>',
'<svg><use xlink:href="#' + config.iconPrefix + '-volume" /></svg>',
- '<span class="sr-only">' + config.i18n.toggleMute + '</span>',
+ '<span class="plyr__sr-only">' + config.i18n.toggleMute + '</span>',
'</button>'
);
}
@@ -202,18 +237,18 @@
// Volume range control
if (_inArray(config.controls, 'volume')) {
html.push(
- '<label for="volume{id}" class="sr-only">' + config.i18n.volume + '</label>',
- '<input id="volume{id}" class="player-volume" type="range" min="0" max="10" value="5" data-player="volume">'
+ '<label for="volume{id}" class="plyr__sr-only">' + config.i18n.volume + '</label>',
+ '<input id="volume{id}" class="plyr__volume" type="range" min="0" max="10" value="5" data-plyr="volume">'
);
}
// Toggle captions button
if (_inArray(config.controls, 'captions')) {
html.push(
- '<button type="button" data-player="captions">',
- '<svg class="icon-captions-on"><use xlink:href="#' + config.iconPrefix + '-captions-on" /></svg>',
+ '<button type="button" data-plyr="captions">',
+ '<svg class="icon--captions-on"><use xlink:href="#' + config.iconPrefix + '-captions-on" /></svg>',
'<svg><use xlink:href="#' + config.iconPrefix + '-captions-off" /></svg>',
- '<span class="sr-only">' + config.i18n.toggleCaptions + '</span>',
+ '<span class="plyr__sr-only">' + config.i18n.toggleCaptions + '</span>',
'</button>'
);
}
@@ -221,10 +256,10 @@
// Toggle fullscreen button
if (_inArray(config.controls, 'fullscreen')) {
html.push(
- '<button type="button" data-player="fullscreen">',
- '<svg class="icon-exit-fullscreen"><use xlink:href="#' + config.iconPrefix + '-exit-fullscreen" /></svg>',
+ '<button type="button" data-plyr="fullscreen">',
+ '<svg class="icon--exit-fullscreen"><use xlink:href="#' + config.iconPrefix + '-exit-fullscreen" /></svg>',
'<svg><use xlink:href="#' + config.iconPrefix + '-enter-fullscreen" /></svg>',
- '<span class="sr-only">' + config.i18n.toggleFullscreen + '</span>',
+ '<span class="plyr__sr-only">' + config.i18n.toggleFullscreen + '</span>',
'</button>'
);
}
@@ -284,7 +319,7 @@
name = 'Firefox';
fullVersion = nAgt.substring(verOffset + 8);
}
- // In most other browsers, 'name/version' is at the end of userAgent
+ // In most other browsers, 'name/version' is at the end of userAgent
else if ((nameOffset=nAgt.lastIndexOf(' ') + 1) < (verOffset=nAgt.lastIndexOf('/'))) {
name = nAgt.substring(nameOffset,verOffset);
fullVersion = nAgt.substring(verOffset + 1);
@@ -311,18 +346,19 @@
return {
name: name,
version: majorVersion,
- ios: /(iPad|iPhone|iPod)/g.test(navigator.platform)
+ ios: /(iPad|iPhone|iPod)/g.test(navigator.platform),
+ touch: 'ontouchstart' in document.documentElement
};
}
// Check for mime type support against a player instance
- // Credits: http://diveintohtml5.info/everything.html
- // Related: http://www.leanbackplayer.com/test/h5mt.html
- function _supportMime(player, mimeType) {
- var media = player.media;
+ // Credits: http://diveintohtml5.info/everything.html
+ // Related: http://www.leanbackplyr.com/test/h5mt.html
+ function _supportMime(plyr, mimeType) {
+ var media = plyr.media;
// Only check video types for video players
- if (player.type == 'video') {
+ if (plyr.type == 'video') {
// Check type
switch (mimeType) {
case 'video/webm': return !!(media.canPlayType && media.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/no/, ''));
@@ -332,14 +368,14 @@
}
// Only check audio types for audio players
- else if (player.type == 'audio') {
+ else if (plyr.type == 'audio') {
// Check type
switch (mimeType) {
case 'audio/mpeg': return !!(media.canPlayType && media.canPlayType('audio/mpeg;').replace(/no/, ''));
case 'audio/ogg': return !!(media.canPlayType && media.canPlayType('audio/ogg; codecs="vorbis"').replace(/no/, ''));
case 'audio/wav': return !!(media.canPlayType && media.canPlayType('audio/wav; codecs="1"').replace(/no/, ''));
}
- }
+ }
// If we got this far, we're stuffed
return false;
@@ -361,7 +397,7 @@
function _inArray(haystack, needle) {
return Array.prototype.indexOf && (haystack.indexOf(needle) != -1);
}
-
+
// Replace all
function _replaceAll(string, find, replace) {
return string.replace(new RegExp(find.replace(/([.*+?\^=!:${}()|\[\]\/\\])/g, '\\$1'), 'g'), replace);
@@ -372,28 +408,28 @@
// Convert `elements` to an array, if necessary.
if (!elements.length) {
elements = [elements];
- }
-
+ }
+
// Loops backwards to prevent having to clone the wrapper on the
// first element (see `child` below).
for (var i = elements.length - 1; i >= 0; i--) {
var child = (i > 0) ? wrapper.cloneNode(true) : wrapper;
var element = elements[i];
-
+
// Cache the current parent and sibling.
var parent = element.parentNode;
var sibling = element.nextSibling;
-
+
// Wrap the element (is automatically removed from its current
// parent).
child.appendChild(element);
-
+
// If the element had a sibling, insert the wrapper before
// the sibling to maintain the HTML structure; otherwise, just
// append it to the parent.
if (sibling) {
parent.insertBefore(child, sibling);
- }
+ }
else {
parent.appendChild(child);
}
@@ -428,23 +464,42 @@
// Set attributes
function _setAttributes(element, attributes) {
for (var key in attributes) {
- element.setAttribute(key, attributes[key]);
+ element.setAttribute(key, (typeof attributes[key] === 'boolean' && attributes[key]) ? '' : attributes[key]);
}
}
+ // Insert a HTML element
+ function _insertElement(type, parent, attributes) {
+ // Create a new <element>
+ var element = document.createElement(type);
+
+ // Set all passed attributes
+ _setAttributes(element, attributes);
+
+ // Inject the new element
+ _prependChild(parent, element);
+ }
+
+ // Get a classname from selector
+ function _getClassname(selector) {
+ return selector.replace('.', '');
+ }
+
// Toggle class on an element
- function _toggleClass(element, name, state) {
+ function _toggleClass(element, className, state) {
if (element) {
- if (element.classList) {
- element.classList[state ? 'add' : 'remove'](name);
- }
- else {
- var className = (' ' + element.className + ' ').replace(/\s+/g, ' ').replace(' ' + name + ' ', '');
- element.className = className + (state ? ' ' + name : '');
- }
+ element.classList[state ? 'add' : 'remove'](className);
}
}
+ // Has class name
+ function _hasClass(element, className) {
+ if (element) {
+ return element.classList.contains(className);
+ }
+ return false;
+ }
+
// Toggle event
function _toggleHandler(element, events, callback, toggle) {
var eventList = events.split(' ');
@@ -481,6 +536,11 @@
// Trigger event
function _triggerEvent(element, event) {
+ // Bail if no element
+ if(!element || !event) {
+ return;
+ }
+
// Create faux event
var fauxEvent = document.createEvent('MouseEvents');
@@ -493,12 +553,17 @@
// Toggle aria-pressed state on a toggle button
function _toggleState(target, state) {
+ // Bail if no target
+ if(!target) {
+ return;
+ }
+
// Get state
state = (typeof state === 'boolean' ? state : !target.getAttribute('aria-pressed'));
-
+
// Set the attribute on target
target.setAttribute('aria-pressed', state);
-
+
return state;
}
@@ -518,7 +583,7 @@
if (source[property] && source[property].constructor && source[property].constructor === Object) {
destination[property] = destination[property] || {};
_extend(destination[property], source[property]);
- }
+ }
else {
destination[property] = source[property];
}
@@ -601,12 +666,30 @@
function _storage() {
var storage = {
supported: (function() {
- try {
- return 'localStorage' in window && window.localStorage !== null;
- }
- catch(e) {
- return false;
- }
+ if(!('localStorage' in window)) {
+ return false;
+ }
+
+ // Try to use it (it might be disabled, e.g. user is in private/porn mode)
+ // see: https://github.com/Selz/plyr/issues/131
+ try {
+ // Add test item
+ window.localStorage.setItem('___test', 'OK');
+
+ // Get the test item
+ var result = window.localStorage.getItem('___test');
+
+ // Clean up
+ window.localStorage.removeItem('___test');
+
+ // Check if value matches
+ return (result === 'OK');
+ }
+ catch (e) {
+ return false;
+ }
+
+ return false;
})()
};
return storage;
@@ -614,68 +697,73 @@
// Player instance
function Plyr(container) {
- var player = this;
- player.container = container;
+ var plyr = this;
+ plyr.container = container;
// Captions functions
// Seek the manual caption time and update UI
function _seekManualCaptions(time) {
// If it's not video, or we're using textTracks, bail.
- if (player.usingTextTracks || player.type !== 'video' || !player.supported.full) {
+ if (plyr.usingTextTracks || plyr.type !== 'video' || !plyr.supported.full) {
return;
}
// Reset subcount
- player.subcount = 0;
+ plyr.subcount = 0;
// Check time is a number, if not use currentTime
// IE has a bug where currentTime doesn't go to 0
// https://twitter.com/Sam_Potts/status/573715746506731521
- time = typeof time === 'number' ? time : player.media.currentTime;
+ time = typeof time === 'number' ? time : plyr.media.currentTime;
- while (_timecodeMax(player.captions[player.subcount][0]) < time.toFixed(1)) {
- player.subcount++;
- if (player.subcount > player.captions.length-1) {
- player.subcount = player.captions.length-1;
+ // If there's no subs available, bail
+ if (!plyr.captions[plyr.subcount]) {
+ return;
+ }
+
+ while (_timecodeMax(plyr.captions[plyr.subcount][0]) < time.toFixed(1)) {
+ plyr.subcount++;
+ if (plyr.subcount > plyr.captions.length-1) {
+ plyr.subcount = plyr.captions.length-1;
break;
}
}
// Check if the next caption is in the current time range
- if (player.media.currentTime.toFixed(1) >= _timecodeMin(player.captions[player.subcount][0]) &&
- player.media.currentTime.toFixed(1) <= _timecodeMax(player.captions[player.subcount][0])) {
- player.currentCaption = player.captions[player.subcount][1];
+ if (plyr.media.currentTime.toFixed(1) >= _timecodeMin(plyr.captions[plyr.subcount][0]) &&
+ plyr.media.currentTime.toFixed(1) <= _timecodeMax(plyr.captions[plyr.subcount][0])) {
+ plyr.currentCaption = plyr.captions[plyr.subcount][1];
// Trim caption text
- var content = player.currentCaption.trim();
+ var content = plyr.currentCaption.trim();
// Render the caption (only if changed)
- if (player.captionsContainer.innerHTML != content) {
+ if (plyr.captionsContainer.innerHTML != content) {
// Empty caption
// Otherwise NVDA reads it twice
- player.captionsContainer.innerHTML = '';
+ plyr.captionsContainer.innerHTML = '';
// Set new caption text
- player.captionsContainer.innerHTML = content;
+ plyr.captionsContainer.innerHTML = content;
}
}
else {
- player.captionsContainer.innerHTML = '';
+ plyr.captionsContainer.innerHTML = '';
}
}
// Display captions container and button (for initialization)
function _showCaptions() {
// If there's no caption toggle, bail
- if (!player.buttons.captions) {
+ if (!plyr.buttons.captions) {
return;
}
- _toggleClass(player.container, config.classes.captions.enabled, true);
+ _toggleClass(plyr.container, config.classes.captions.enabled, true);
if (config.captions.defaultActive) {
- _toggleClass(player.container, config.classes.captions.active, true);
- _toggleState(player.buttons.captions, true);
+ _toggleClass(plyr.container, config.classes.captions.active, true);
+ _toggleState(plyr.buttons.captions, true);
}
}
@@ -707,7 +795,7 @@
// Find all elements
function _getElements(selector) {
- return player.container.querySelectorAll(selector);
+ return plyr.container.querySelectorAll(selector);
}
// Find a single element
@@ -719,7 +807,7 @@
function _inFrame() {
try {
return window.self !== window.top;
- }
+ }
catch (e) {
return true;
}
@@ -744,8 +832,25 @@
// Replace all id references with random numbers
html = _replaceAll(html, '{id}', Math.floor(Math.random() * (10000)));
- // Inject into the container
- player.container.insertAdjacentHTML('beforeend', html);
+ // Controls container
+ var container;
+
+ // Inject to custom location
+ if (config.selectors.controls.container !== null) {
+ container = config.selectors.controls.container;
+
+ if(typeof selector === 'string') {
+ container = document.querySelector(container);
+ }
+ }
+
+ // Inject into the container by default
+ if (!(container instanceof HTMLElement)) {
+ container = plyr.container
+ }
+
+ // Inject controls HTML
+ container.insertAdjacentHTML('beforeend', html);
// Setup tooltips
if (config.tooltips) {
@@ -763,44 +868,45 @@
// Find the UI controls and store references
function _findElements() {
try {
- player.controls = _getElement(config.selectors.controls);
+ plyr.controls = _getElement(config.selectors.controls.wrapper);
// Buttons
- player.buttons = {};
- player.buttons.seek = _getElement(config.selectors.buttons.seek);
- player.buttons.play = _getElement(config.selectors.buttons.play);
- player.buttons.pause = _getElement(config.selectors.buttons.pause);
- player.buttons.restart = _getElement(config.selectors.buttons.restart);
- player.buttons.rewind = _getElement(config.selectors.buttons.rewind);
- player.buttons.forward = _getElement(config.selectors.buttons.forward);
- player.buttons.fullscreen = _getElement(config.selectors.buttons.fullscreen);
+ plyr.buttons = {};
+ plyr.buttons.seek = _getElement(config.selectors.buttons.seek);
+ plyr.buttons.play = _getElement(config.selectors.buttons.play);
+ plyr.buttons.pause = _getElement(config.selectors.buttons.pause);
+ plyr.buttons.restart = _getElement(config.selectors.buttons.restart);
+ plyr.buttons.rewind = _getElement(config.selectors.buttons.rewind);
+ plyr.buttons.forward = _getElement(config.selectors.buttons.forward);
+ plyr.buttons.fullscreen = _getElement(config.selectors.buttons.fullscreen);
// Inputs
- player.buttons.mute = _getElement(config.selectors.buttons.mute);
- player.buttons.captions = _getElement(config.selectors.buttons.captions);
- player.checkboxes = _getElements('[type="checkbox"]');
+ plyr.buttons.volume = _getElement(config.selectors.buttons.volume);
+ plyr.buttons.mute = _getElement(config.selectors.buttons.mute);
+ plyr.buttons.captions = _getElement(config.selectors.buttons.captions);
+ plyr.checkboxes = _getElements('[type="checkbox"]');
// Progress
- player.progress = {};
- player.progress.container = _getElement(config.selectors.progress.container);
+ plyr.progress = {};
+ plyr.progress.container = _getElement(config.selectors.progress.container);
// Progress - Buffering
- player.progress.buffer = {};
- player.progress.buffer.bar = _getElement(config.selectors.progress.buffer);
- player.progress.buffer.text = player.progress.buffer.bar && player.progress.buffer.bar.getElementsByTagName('span')[0];
+ plyr.progress.buffer = {};
+ plyr.progress.buffer.bar = _getElement(config.selectors.progress.buffer);
+ plyr.progress.buffer.text = plyr.progress.buffer.bar && plyr.progress.buffer.bar.getElementsByTagName('span')[0];
// Progress - Played
- player.progress.played = {};
- player.progress.played.bar = _getElement(config.selectors.progress.played);
- player.progress.played.text = player.progress.played.bar && player.progress.played.bar.getElementsByTagName('span')[0];
+ plyr.progress.played = {};
+ plyr.progress.played.bar = _getElement(config.selectors.progress.played);
+ plyr.progress.played.text = plyr.progress.played.bar && plyr.progress.played.bar.getElementsByTagName('span')[0];
// Volume
- player.volume = _getElement(config.selectors.buttons.volume);
+ plyr.volume = _getElement(config.selectors.buttons.volume);
// Timing
- player.duration = _getElement(config.selectors.duration);
- player.currentTime = _getElement(config.selectors.currentTime);
- player.seekTime = _getElements(config.selectors.seekTime);
+ plyr.duration = _getElement(config.selectors.duration);
+ plyr.currentTime = _getElement(config.selectors.currentTime);
+ plyr.seekTime = _getElements(config.selectors.seekTime);
return true;
}
@@ -808,135 +914,199 @@
_log('It looks like there\'s a problem with your controls html. Bailing.', true);
// Restore native video controls
- player.media.setAttribute('controls', '');
+ plyr.media.setAttribute('controls', '');
return false;
}
}
- // Setup aria attribute for play
- function _setupPlayAria() {
- // If there's no play button, bail
- if (!player.buttons.play) {
- return;
- }
-
+ // Setup aria attribute for play and iframe title
+ function _setTitle(iframe) {
// Find the current text
- var label = player.buttons.play.innerText || config.i18n.play;
+ var label = config.i18n.play;
// If there's a media title set, use that for the label
if (typeof(config.title) !== 'undefined' && config.title.length) {
label += ', ' + config.title;
}
- player.buttons.play.setAttribute('aria-label', label);
+ // If there's a play button, set label
+ if (plyr.buttons.play) {
+ plyr.buttons.play.setAttribute('aria-label', label);
+ }
+
+ // Set iframe title
+ // https://github.com/Selz/plyr/issues/124
+ if (iframe instanceof HTMLElement) {
+ iframe.setAttribute('title', config.i18n.frameTitle.replace('{title}', config.title));
+ }
}
// Setup media
function _setupMedia() {
// If there's no media, bail
- if (!player.media) {
+ if (!plyr.media) {
_log('No audio or video element found!', true);
return false;
}
- if (player.supported.full) {
+ if (plyr.supported.full) {
// Remove native video controls
- player.media.removeAttribute('controls');
-
+ plyr.media.removeAttribute('controls');
+
// Add type class
- _toggleClass(player.container, config.classes.type.replace('{0}', player.type), true);
+ _toggleClass(plyr.container, config.classes.type.replace('{0}', plyr.type), true);
// If there's no autoplay attribute, assume the video is stopped and add state class
- _toggleClass(player.container, config.classes.stopped, ((player.media.getAttribute('autoplay') === null) && !config.autoplay));
-
+ _toggleClass(plyr.container, config.classes.stopped, config.autoplay);
+
// Add iOS class
- if (player.browser.ios) {
- _toggleClass(player.container, 'ios', true);
- }
+ _toggleClass(plyr.container, config.classes.isIos, plyr.browser.ios);
+
+ // Add touch class
+ _toggleClass(plyr.container, config.classes.isTouch, plyr.browser.touch);
// Inject the player wrapper
- if (player.type === 'video') {
+ if (plyr.type === 'video') {
// Create the wrapper div
var wrapper = document.createElement('div');
wrapper.setAttribute('class', config.classes.videoWrapper);
// Wrap the video in a container
- _wrap(player.media, wrapper);
+ _wrap(plyr.media, wrapper);
// Cache the container
- player.videoContainer = wrapper;
+ plyr.videoContainer = wrapper;
}
}
- // YouTube
- if (player.type == 'youtube') {
- _setupYouTube(player.media.getAttribute('data-video-id'));
- }
+ // Embeds
+ if (_inArray(config.types.embed, plyr.type)) {
+ _setupEmbed();
- // Autoplay
- if (player.media.getAttribute('autoplay') !== null || config.autoplay) {
- _play();
+ // Clean up
+ plyr.embedId = null;
+ }
+ else {
+ // Autoplay
+ if (config.autoplay) {
+ _play();
+ }
}
}
- // Setup YouTube
- function _setupYouTube(id) {
+ // Setup YouTube/Vimeo
+ function _setupEmbed() {
+ var container = document.createElement('div'),
+ videoId = plyr.embedId,
+ id = plyr.type + '-' + Math.floor(Math.random() * (10000));
+
// Remove old containers
- var containers = _getElements('[id^="youtube"]');
+ var containers = _getElements('[id^="' + plyr.type + '-"]');
for (var i = containers.length - 1; i >= 0; i--) {
_remove(containers[i]);
}
- // Create the YouTube container
- var container = document.createElement('div');
- container.setAttribute('id', 'youtube-' + Math.floor(Math.random() * (10000)));
- player.media.appendChild(container);
-
// Add embed class for responsive
- _toggleClass(player.media, config.classes.videoWrapper, true);
- _toggleClass(player.media, config.classes.embedWrapper, true);
+ _toggleClass(plyr.media, config.classes.videoWrapper, true);
+ _toggleClass(plyr.media, config.classes.embedWrapper, true);
- if (typeof YT === 'object') {
- _YTReady(id, container);
- }
- else {
- // Load the API
- _injectScript('https://www.youtube.com/iframe_api');
+ // YouTube
+ if (plyr.type === 'youtube') {
+ // Create the YouTube container
+ plyr.media.appendChild(container);
+
+ // Set ID
+ container.setAttribute('id', id);
- // Add callback to queue
- callbacks.youtube.push(function() { _YTReady(id, container); });
+ // Setup API
+ if (typeof YT === 'object') {
+ _youTubeReady(videoId, container);
+ }
+ else {
+ // Load the API
+ _injectScript(config.urls.youtube.api);
- // Setup callback for the API
- window.onYouTubeIframeAPIReady = function () {
- for (var i = callbacks.youtube.length - 1; i >= 0; i--) {
- // Fire callback
- callbacks.youtube[i]();
+ // Setup callback for the API
+ window.onYouTubeReadyCallbacks = window.onYouTubeReadyCallbacks || [];
+
+ // Add to queue
+ window.onYouTubeReadyCallbacks.push(function() { _youTubeReady(videoId, container) });
+
+ // Set callback to process queue
+ window.onYouTubeIframeAPIReady = function () {
+ window.onYouTubeReadyCallbacks.forEach(function(callback) { callback(); });
+ };
+ }
+ }
+ // Vimeo
+ else if (plyr.type === 'vimeo') {
+ // Inject the iframe
+ var iframe = document.createElement('iframe');
+
+ // Watch for iframe load
+ iframe.loaded = false;
+ _on(iframe, 'load', function() { iframe.loaded = true; });
+
+ _setAttributes(iframe, {
+ 'src': 'https://player.vimeo.com/video/' + videoId + '?player_id=' + id + '&api=1&badge=0&byline=0&portrait=0&title=0',
+ 'id': id,
+ 'webkitallowfullscreen': '',
+ 'mozallowfullscreen': '',
+ 'allowfullscreen': '',
+ 'frameborder': 0
+ });
+ container.appendChild(iframe);
+ plyr.media.appendChild(container);
+
+ // Load the API
+ if (!('$f' in window)) {
+ _injectScript(config.urls.vimeo.api);
+ }
- // Remove from queue
- callbacks.youtube.splice(i, 1);
+ // Wait for fragaloop load
+ var timer = window.setInterval(function() {
+ if ('$f' in window && iframe.loaded) {
+ window.clearInterval(timer);
+ _vimeoReady.call(iframe);
}
- };
+ }, 50);
}
}
- // Handle API ready
- function _YTReady(id, container) {
- _log('YouTube API Ready');
+ // When embeds are ready
+ function _embedReady() {
+ // Inject and update UI
+ if (plyr.supported.full) {
+ // Only setup controls once
+ if (!plyr.container.querySelectorAll(config.selectors.controls.wrapper).length) {
+ _setupInterface();
+ }
+ }
+ // Set title
+ _setTitle(_getElement('iframe'));
+
+ // Set the volume
+ _setVolume();
+ _updateVolume();
+ }
+
+ // Handle YouTube API ready
+ function _youTubeReady(videoId, container) {
// Setup timers object
// We have to poll YouTube for updates
- if (!('timer' in player)) {
- player.timer = {};
+ if (!('timer' in plyr)) {
+ plyr.timer = {};
}
// Setup instance
// https://developers.google.com/youtube/iframe_api_reference
- player.embed = new YT.Player(container.id, {
- videoId: id,
+ plyr.embed = new YT.Player(container.id, {
+ videoId: videoId,
playerVars: {
autoplay: (config.autoplay ? 1 : 0),
- controls: (player.supported.full ? 0 : 1),
+ controls: (plyr.supported.full ? 0 : 1),
rel: 0,
showinfo: 0,
iv_load_policy: 3,
@@ -944,7 +1114,8 @@
cc_lang_pref: 'en',
wmode: 'transparent',
modestbranding: 1,
- disablekb: 1
+ disablekb: 1,
+ origin: '*' // https://code.google.com/p/gdata-issues/issues/detail?id=5788#c45
},
events: {
'onReady': function(event) {
@@ -952,53 +1123,49 @@
var instance = event.target;
// Create a faux HTML5 API using the YouTube API
- player.media.play = function() {
+ plyr.media.play = function() {
instance.playVideo();
- player.media.paused = false;
+ plyr.media.paused = false;
};
- player.media.pause = function() {
+ plyr.media.pause = function() {
instance.pauseVideo();
- player.media.paused = true;
+ plyr.media.paused = true;
};
- player.media.stop = function() {
+ plyr.media.stop = function() {
instance.stopVideo();
- player.media.paused = true
+ plyr.media.paused = true;
};
- player.media.duration = instance.getDuration();
- player.media.paused = true;
- player.media.currentTime = instance.getCurrentTime();
- player.media.muted = instance.isMuted();
+ plyr.media.duration = instance.getDuration();
+ plyr.media.paused = !config.autoplay;
+ plyr.media.currentTime = instance.getCurrentTime();
+ plyr.media.muted = instance.isMuted();
// Trigger timeupdate
- _triggerEvent(player.media, 'timeupdate');
+ _triggerEvent(plyr.media, 'timeupdate');
// Reset timer
- window.clearInterval(player.timer.buffering);
+ window.clearInterval(plyr.timer.buffering);
// Setup buffering
- player.timer.buffering = window.setInterval(function() {
+ plyr.timer.buffering = window.setInterval(function() {
// Get loaded % from YouTube
- player.media.buffered = instance.getVideoLoadedFraction();
-
+ plyr.media.buffered = instance.getVideoLoadedFraction();
+
// Trigger progress
- _triggerEvent(player.media, 'progress');
+ _triggerEvent(plyr.media, 'progress');
// Bail if we're at 100%
- if (player.media.buffered === 1) {
- window.clearInterval(player.timer.buffering);
+ if (plyr.media.buffered === 1) {
+ window.clearInterval(plyr.timer.buffering);
}
}, 200);
- if (player.supported.full) {
- // Only setup controls once
- if (!player.container.querySelectorAll(config.selectors.controls).length) {
- _setupInterface();
- }
+ // Update UI
+ _embedReady();
- // Display duration if available
- if (config.displayDuration) {
- _displayDuration();
- }
+ // Display duration if available
+ if (config.displayDuration) {
+ _displayDuration();
}
},
'onStateChange': function(event) {
@@ -1006,7 +1173,7 @@
var instance = event.target;
// Reset timer
- window.clearInterval(player.timer.playing);
+ window.clearInterval(plyr.timer.playing);
// Handle events
// -1 Unstarted
@@ -1014,56 +1181,136 @@
// 1 Playing
// 2 Paused
// 3 Buffering
- // 5 Video cued
+ // 5 Video cued
switch (event.data) {
- case 0:
- player.media.paused = true;
- _triggerEvent(player.media, 'ended');
+ case 0:
+ plyr.media.paused = true;
+ _triggerEvent(plyr.media, 'ended');
break;
case 1:
- player.media.paused = false;
- _triggerEvent(player.media, 'play');
+ plyr.media.paused = false;
+ _triggerEvent(plyr.media, 'play');
// Poll to get playback progress
- player.timer.playing = window.setInterval(function() {
+ plyr.timer.playing = window.setInterval(function() {
// Set the current time
- player.media.currentTime = instance.getCurrentTime();
+ plyr.media.currentTime = instance.getCurrentTime();
// Trigger timeupdate
- _triggerEvent(player.media, 'timeupdate');
+ _triggerEvent(plyr.media, 'timeupdate');
}, 200);
break;
case 2:
- player.media.paused = true;
- _triggerEvent(player.media, 'pause');
+ plyr.media.paused = true;
+ _triggerEvent(plyr.media, 'pause');
}
}
}
});
}
+ // Vimeo ready
+ function _vimeoReady() {
+ /* jshint validthis: true */
+ plyr.embed = $f(this);
+
+ // Setup on ready
+ plyr.embed.addEvent('ready', function() {
+
+ // Create a faux HTML5 API using the Vimeo API
+ plyr.media.play = function() {
+ plyr.embed.api('play');
+ plyr.media.paused = false;
+ };
+ plyr.media.pause = function() {
+ plyr.embed.api('pause');
+ plyr.media.paused = true;
+ };
+ plyr.media.stop = function() {
+ plyr.embed.api('stop');
+ plyr.media.paused = true;
+ };
+ plyr.media.paused = !config.autoplay;
+ plyr.media.currentTime = 0;
+
+ // Update UI
+ _embedReady();
+
+ plyr.embed.api('getCurrentTime', function (value) {
+ plyr.media.currentTime = value;
+
+ // Trigger timeupdate
+ _triggerEvent(plyr.media, 'timeupdate');
+ });
+
+ plyr.embed.api('getDuration', function(value) {
+ plyr.media.duration = value;
+
+ // Display duration if available
+ if (plyr.supported.full && config.displayDuration) {
+ _displayDuration();
+ }
+ });
+
+ plyr.embed.addEvent('play', function() {
+ plyr.media.paused = false;
+ _triggerEvent(plyr.media, 'play');
+ });
+
+ plyr.embed.addEvent('pause', function() {
+ plyr.media.paused = true;
+ _triggerEvent(plyr.media, 'pause');
+ });
+
+ plyr.embed.addEvent('playProgress', function(data) {
+ plyr.media.currentTime = data.seconds;
+ _triggerEvent(plyr.media, 'timeupdate');
+ });
+
+ plyr.embed.addEvent('loadProgress', function(data) {
+ plyr.media.buffered = data.percent;
+ _triggerEvent(plyr.media, 'progress');
+ });
+
+ plyr.embed.addEvent('finish', function() {
+ plyr.media.paused = true;
+ _triggerEvent(plyr.media, 'ended');
+ });
+
+ // Always seek to 0
+ //plyr.embed.api('seekTo', 0);
+
+ // Prevent autoplay if needed (seek will play)
+ //if (!config.autoplay) {
+ // plyr.embed.api('pause');
+ //}
+ });
+ }
+
// Setup captions
function _setupCaptions() {
- if (player.type === 'video') {
+ if (plyr.type === 'video') {
// Inject the container
- player.videoContainer.insertAdjacentHTML('afterbegin', '<div class="' + config.selectors.captions.replace('.', '') + '"><span></span></div>');
+ if (!_getElement(config.selectors.captions)) {
+ plyr.videoContainer.insertAdjacentHTML('afterbegin', '<div class="' + _getClassname(config.selectors.captions) + '"><span></span></div>');
+ }
// Cache selector
- player.captionsContainer = _getElement(config.selectors.captions).querySelector('span');
+ plyr.captionsContainer = _getElement(config.selectors.captions).querySelector('span');
// Determine if HTML5 textTracks is supported
- player.usingTextTracks = false;
- if (player.media.textTracks) {
- player.usingTextTracks = true;
+ plyr.usingTextTracks = false;
+ if (plyr.media.textTracks) {
+ plyr.usingTextTracks = true;
}
// Get URL of caption file if exists
var captionSrc = '',
kind,
- children = player.media.childNodes;
+ children = plyr.media.childNodes;
for (var i = 0; i < children.length; i++) {
if (children[i].nodeName.toLowerCase() === 'track') {
@@ -1075,9 +1322,9 @@
}
// Record if caption file exists or not
- player.captionExists = true;
+ plyr.captionExists = true;
if (captionSrc === '') {
- player.captionExists = false;
+ plyr.captionExists = false;
_log('No caption track found.');
}
else {
@@ -1085,49 +1332,49 @@
}
// If no caption file exists, hide container for caption text
- if (!player.captionExists) {
- _toggleClass(player.container, config.classes.captions.enabled);
+ if (!plyr.captionExists) {
+ _toggleClass(plyr.container, config.classes.captions.enabled);
}
// If caption file exists, process captions
else {
- // Turn off native caption rendering to avoid double captions
+ // Turn off native caption rendering to avoid double captions
// This doesn't seem to work in Safari 7+, so the <track> elements are removed from the dom below
- var tracks = player.media.textTracks;
+ var tracks = plyr.media.textTracks;
for (var x = 0; x < tracks.length; x++) {
tracks[x].mode = 'hidden';
}
// Enable UI
- _showCaptions(player);
+ _showCaptions(plyr);
// Disable unsupported browsers than report false positive
- if ((player.browser.name === 'IE' && player.browser.version >= 10) ||
- (player.browser.name === 'Firefox' && player.browser.version >= 31) ||
- (player.browser.name === 'Chrome' && player.browser.version >= 43) ||
- (player.browser.name === 'Safari' && player.browser.version >= 7)) {
+ if ((plyr.browser.name === 'IE' && plyr.browser.version >= 10) ||
+ (plyr.browser.name === 'Firefox' && plyr.browser.version >= 31) ||
+ (plyr.browser.name === 'Chrome' && plyr.browser.version >= 43) ||
+ (plyr.browser.name === 'Safari' && plyr.browser.version >= 7)) {
// Debugging
_log('Detected unsupported browser for HTML5 captions. Using fallback.');
// Set to false so skips to 'manual' captioning
- player.usingTextTracks = false;
+ plyr.usingTextTracks = false;
}
// Rendering caption tracks
// Native support required - http://caniuse.com/webvtt
- if (player.usingTextTracks) {
+ if (plyr.usingTextTracks) {
_log('TextTracks supported.');
-
+
for (var y = 0; y < tracks.length; y++) {
var track = tracks[y];
if (track.kind === 'captions' || track.kind === 'subtitles') {
_on(track, 'cuechange', function() {
// Clear container
- player.captionsContainer.innerHTML = '';
+ plyr.captionsContainer.innerHTML = '';
// Display a cue, if there is one
if (this.activeCues[0] && this.activeCues[0].hasOwnProperty('text')) {
- player.captionsContainer.appendChild(this.activeCues[0].getCueAsHTML().trim());
+ plyr.captionsContainer.appendChild(this.activeCues[0].getCueAsHTML().trim());
}
});
}
@@ -1138,8 +1385,8 @@
_log('TextTracks not supported so rendering captions manually.');
// Render captions from array at appropriate time
- player.currentCaption = '';
- player.captions = [];
+ plyr.currentCaption = '';
+ plyr.captions = [];
if (captionSrc !== '') {
// Create XMLHttpRequest Object
@@ -1156,21 +1403,21 @@
for (var r = 0; r < records.length; r++) {
record = records[r];
- player.captions[r] = [];
- player.captions[r] = record.split('\n');
+ plyr.captions[r] = [];
+ plyr.captions[r] = record.split('\n');
}
// Remove first element ('VTT')
- player.captions.shift();
+ plyr.captions.shift();
_log('Successfully loaded the caption file via AJAX.');
- }
+ }
else {
_log('There was a problem loading the caption file via AJAX.', true);
}
}
};
-
+
xhr.open('get', captionSrc, true);
xhr.send();
@@ -1178,15 +1425,15 @@
}
// If Safari 7+, removing track from DOM [see 'turn off native caption rendering' above]
- if (player.browser.name === 'Safari' && player.browser.version >= 7) {
+ if (plyr.browser.name === 'Safari' && plyr.browser.version >= 7) {
_log('Safari 7+ detected; removing track from DOM.');
// Find all <track> elements
- tracks = player.media.getElementsByTagName('track');
-
+ tracks = plyr.media.getElementsByTagName('track');
+
// Loop through and remove one by one
for (var t = 0; t < tracks.length; t++) {
- player.media.removeChild(tracks[t]);
+ plyr.media.removeChild(tracks[t]);
}
}
}
@@ -1195,7 +1442,7 @@
// Setup fullscreen
function _setupFullscreen() {
- if (player.type != 'audio' && config.fullscreen.enabled) {
+ if ((plyr.type != 'audio' || config.fullscreen.allowAudio) && config.fullscreen.enabled) {
// Check for native support
var nativeSupport = fullscreen.supportsFullScreen;
@@ -1203,30 +1450,34 @@
_log((nativeSupport ? 'Native' : 'Fallback') + ' fullscreen enabled.');
// Add styling hook
- _toggleClass(player.container, config.classes.fullscreen.enabled, true);
+ _toggleClass(plyr.container, config.classes.fullscreen.enabled, true);
}
else {
_log('Fullscreen not supported and fallback disabled.');
}
// Toggle state
- _toggleState(player.buttons.fullscreen, false);
+ _toggleState(plyr.buttons.fullscreen, false);
// Set control hide class hook
if (config.fullscreen.hideControls) {
- _toggleClass(player.container, config.classes.fullscreen.hideControls, true);
+ _toggleClass(plyr.container, config.classes.fullscreen.hideControls, true);
}
- }
+ }
}
// Play media
function _play() {
- player.media.play();
+ if('play' in plyr.media) {
+ plyr.media.play();
+ }
}
// Pause media
function _pause() {
- player.media.pause();
+ if('pause' in plyr.media) {
+ plyr.media.pause();
+ }
}
// Toggle playback
@@ -1241,7 +1492,7 @@
}
// True toggle
else {
- player.media[player.media.paused ? 'play' : 'pause']();
+ plyr.media[plyr.media.paused ? 'play' : 'pause']();
}
}
@@ -1251,7 +1502,7 @@
if (typeof seekTime !== 'number') {
seekTime = config.seekTime;
}
- _seek(player.media.currentTime - seekTime);
+ _seek(plyr.media.currentTime - seekTime);
}
// Fast forward
@@ -1260,14 +1511,14 @@
if (typeof seekTime !== 'number') {
seekTime = config.seekTime;
}
- _seek(player.media.currentTime + seekTime);
+ _seek(plyr.media.currentTime + seekTime);
}
// Seek to time
// The input parameter can be an event or a number
function _seek(input) {
var targetTime = 0,
- paused = player.media.paused;
+ paused = plyr.media.paused;
// Explicit position
if (typeof input === 'number') {
@@ -1277,38 +1528,46 @@
else if (typeof input === 'object' && (input.type === 'input' || input.type === 'change')) {
// It's the seek slider
// Seek to the selected time
- targetTime = ((input.target.value / input.target.max) * player.media.duration);
+ targetTime = ((input.target.value / input.target.max) * plyr.media.duration);
}
// Normalise targetTime
if (targetTime < 0) {
targetTime = 0;
}
- else if (targetTime > player.media.duration) {
- targetTime = player.media.duration;
+ else if (targetTime > plyr.media.duration) {
+ targetTime = plyr.media.duration;
}
// Set the current time
// Try/catch incase the media isn't set and we're calling seek() from source() and IE moans
try {
- player.media.currentTime = targetTime.toFixed(1);
+ plyr.media.currentTime = targetTime.toFixed(1);
}
catch(e) {}
- // YouTube
- if (player.type == 'youtube') {
- player.embed.seekTo(targetTime);
+ // Trigger timeupdate for embed and restore pause state
+ if ('embed' in plyr) {
+ // YouTube
+ if (plyr.type === 'youtube') {
+ plyr.embed.seekTo(targetTime);
+ }
- if (paused) {
- _pause();
+ // Vimeo
+ if (plyr.type === 'vimeo') {
+ plyr.embed.api('seekTo', targetTime);
}
// Trigger timeupdate
- _triggerEvent(player.media, 'timeupdate');
+ _triggerEvent(plyr.media, 'timeupdate');
+
+ if (paused) {
+ _pause();
+ }
}
// Logging
- _log('Seeking to ' + player.media.currentTime + ' seconds');
+ _log('Seeking to ' + plyr.media.currentTime + ' seconds');
// Special handling for 'manual' captions
_seekManualCaptions(targetTime);
@@ -1316,8 +1575,8 @@
// Check playing state
function _checkPlaying() {
- _toggleClass(player.container, config.classes.playing, !player.media.paused);
- _toggleClass(player.container, config.classes.stopped, player.media.paused);
+ _toggleClass(plyr.container, config.classes.playing, !plyr.media.paused);
+ _toggleClass(plyr.container, config.classes.stopped, plyr.media.paused);
}
// Toggle fullscreen
@@ -1327,13 +1586,13 @@
// If it's a fullscreen change event, it's probably a native close
if (event && event.type === fullscreen.fullScreenEventName) {
- player.isFullscreen = fullscreen.isFullScreen(player.container);
+ plyr.isFullscreen = fullscreen.isFullScreen(plyr.container);
}
// If there's native support, use it
else if (nativeSupport) {
// Request fullscreen
- if (!fullscreen.isFullScreen(player.container)) {
- fullscreen.requestFullScreen(player.container);
+ if (!fullscreen.isFullScreen(plyr.container)) {
+ fullscreen.requestFullScreen(plyr.container);
}
// Bail from fullscreen
else {
@@ -1341,14 +1600,14 @@
}
// Check if we're actually full screen (it could fail)
- player.isFullscreen = fullscreen.isFullScreen(player.container);
+ plyr.isFullscreen = fullscreen.isFullScreen(plyr.container);
}
else {
// Otherwise, it's a simple toggle
- player.isFullscreen = !player.isFullscreen;
+ plyr.isFullscreen = !plyr.isFullscreen;
// Bind/unbind escape key
- if (player.isFullscreen) {
+ if (plyr.isFullscreen) {
_on(document, 'keyup', _handleEscapeFullscreen);
document.body.style.overflow = 'hidden';
}
@@ -1359,18 +1618,18 @@
}
// Set class hook
- _toggleClass(player.container, config.classes.fullscreen.active, player.isFullscreen);
+ _toggleClass(plyr.container, config.classes.fullscreen.active, plyr.isFullscreen);
// Set button state
- _toggleState(player.buttons.fullscreen, player.isFullscreen);
-
+ _toggleState(plyr.buttons.fullscreen, plyr.isFullscreen);
+
// Toggle controls visibility based on mouse movement and location
var hoverTimer, isMouseOver = false;
// Show the player controls
function _showControls() {
// Set shown class
- _toggleClass(player.container, config.classes.hover, true);
+ _toggleClass(plyr.container, config.classes.hover, true);
// Clear timer every movement
window.clearTimeout(hoverTimer);
@@ -1378,7 +1637,7 @@
// If the mouse is not over the controls, set a timeout to hide them
if (!isMouseOver) {
hoverTimer = window.setTimeout(function() {
- _toggleClass(player.container, config.classes.hover, false);
+ _toggleClass(plyr.container, config.classes.hover, false);
}, 2000);
}
}
@@ -1390,24 +1649,59 @@
if (config.fullscreen.hideControls) {
// Hide on entering full screen
- _toggleClass(player.controls, config.classes.hover, false);
+ _toggleClass(plyr.controls, config.classes.hover, false);
// Keep an eye on the mouse location in relation to controls
- _toggleHandler(player.controls, 'mouseenter mouseleave', _setMouseOver, player.isFullscreen);
+ _toggleHandler(plyr.controls, 'mouseenter mouseleave', _setMouseOver, plyr.isFullscreen);
// Show the controls on mouse move
- _toggleHandler(player.container, 'mousemove', _showControls, player.isFullscreen);
+ _toggleHandler(plyr.container, 'mousemove', _showControls, plyr.isFullscreen);
}
}
- // Bail from faux-fullscreen
+ // Bail from faux-fullscreen
function _handleEscapeFullscreen(event) {
// If it's a keypress and not escape, bail
- if ((event.which || event.charCode || event.keyCode) === 27 && player.isFullscreen) {
+ if ((event.which || event.charCode || event.keyCode) === 27 && plyr.isFullscreen) {
_toggleFullscreen();
}
}
+ // Mute
+ function _toggleMute(muted) {
+ // If the method is called without parameter, toggle based on current value
+ if (typeof muted !== 'boolean') {
+ muted = !plyr.media.muted;
+ }
+
+ // Set button state
+ _toggleState(plyr.buttons.mute, muted);
+
+ // Set mute on the player
+ plyr.media.muted = muted;
+
+ // YouTube
+ if (plyr.type === 'youtube') {
+ plyr.embed[plyr.media.muted ? 'mute' : 'unMute']();
+
+ // Trigger timeupdate
+ _triggerEvent(plyr.media, 'volumechange');
+ }
+
+ // Vimeo
+ if (plyr.type === 'vimeo') {
+ if (plyr.media.muted) {
+ plyr.embed.api('setVolume', 0);
+ }
+ else {
+ plyr.embed.api('setVolume', parseFloat(config.volume / 10));
+ }
+
+ // Trigger timeupdate
+ _triggerEvent(plyr.media, 'volumechange');
+ }
+ }
+
// Set volume
function _setVolume(volume) {
// Use default if no value specified
@@ -1417,7 +1711,7 @@
}
else {
volume = config.volume;
- }
+ }
}
// Maximum is 10
@@ -1430,52 +1724,40 @@
}
// Set the player volume
- player.media.volume = parseFloat(volume / 10);
+ plyr.media.volume = parseFloat(volume / 10);
- // YouTube
- if (player.type == 'youtube') {
- player.embed.setVolume(player.media.volume * 100);
+ // Store in config
+ config.volume = volume;
- // Trigger timeupdate
- _triggerEvent(player.media, 'volumechange');
+ // YouTube
+ if (plyr.type === 'youtube') {
+ plyr.embed.setVolume(plyr.media.volume * 100);
}
- // Toggle muted state
- if (player.media.muted && volume > 0) {
- _toggleMute();
- }
- }
-
- // Mute
- function _toggleMute(muted) {
- // If the method is called without parameter, toggle based on current value
- if (typeof muted !== 'boolean') {
- muted = !player.media.muted;
+ // Vimeo
+ if (plyr.type === 'vimeo') {
+ plyr.embed.api('setVolume', plyr.media.volume);
}
- // Set button state
- _toggleState(player.buttons.mute, muted);
-
- // Set mute on the player
- player.media.muted = muted;
-
- // YouTube
- if (player.type === 'youtube') {
- player.embed[player.media.muted ? 'mute' : 'unMute']();
+ // Trigger volumechange for embeds
+ if ('embed' in plyr) {
+ _triggerEvent(plyr.media, 'volumechange');
+ }
- // Trigger timeupdate
- _triggerEvent(player.media, 'volumechange');
+ // Toggle muted state
+ if (plyr.media.muted && volume > 0) {
+ _toggleMute();
}
}
// Update volume UI and storage
function _updateVolume() {
// Get the current volume
- var volume = player.media.muted ? 0 : (player.media.volume * 10);
+ var volume = plyr.media.muted ? 0 : (plyr.media.volume * 10);
// Update the <input type="range"> if present
- if (player.supported.full && player.volume) {
- player.volume.value = volume;
+ if (plyr.supported.full && plyr.volume) {
+ plyr.volume.value = volume;
}
// Store the volume in storage
@@ -1484,31 +1766,34 @@
}
// Toggle class if muted
- _toggleClass(player.container, config.classes.muted, (volume === 0));
-
+ _toggleClass(plyr.container, config.classes.muted, (volume === 0));
+
// Update checkbox for mute state
- if (player.supported.full && player.buttons.mute) {
- _toggleState(player.buttons.mute, (volume === 0));
+ if (plyr.supported.full && plyr.buttons.mute) {
+ _toggleState(plyr.buttons.mute, (volume === 0));
}
}
// Toggle captions
function _toggleCaptions(show) {
// If there's no full support, or there's no caption toggle
- if (!player.supported.full || !player.buttons.captions) {
+ if (!plyr.supported.full || !plyr.buttons.captions) {
return;
}
// If the method is called without parameter, toggle based on current value
if (typeof show !== 'boolean') {
- show = (player.container.className.indexOf(config.classes.captions.active) === -1);
+ show = (plyr.container.className.indexOf(config.classes.captions.active) === -1);
}
+ // Set global
+ plyr.captionsEnabled = show;
+
// Toggle state
- _toggleState(player.buttons.captions, show);
+ _toggleState(plyr.buttons.captions, plyr.captionsEnabled);
// Add class hook
- _toggleClass(player.container, config.classes.captions.active, show);
+ _toggleClass(plyr.container, config.classes.captions.active, plyr.captionsEnabled);
}
// Check if media is loading
@@ -1516,18 +1801,18 @@
var loading = (event.type === 'waiting');
// Clear timer
- clearTimeout(player.loadingTimer);
+ clearTimeout(plyr.loadingTimer);
// Timer to prevent flicker when seeking
- player.loadingTimer = setTimeout(function() {
- _toggleClass(player.container, config.classes.loading, loading);
+ plyr.loadingTimer = setTimeout(function() {
+ _toggleClass(plyr.container, config.classes.loading, loading);
}, (loading ? 250 : 0));
}
// Update <progress> elements
function _updateProgress(event) {
- var progress = player.progress.played.bar,
- text = player.progress.played.text,
+ var progress = plyr.progress.played.bar,
+ text = plyr.progress.played.text,
value = 0;
if (event) {
@@ -1535,13 +1820,13 @@
// Video playing
case 'timeupdate':
case 'seeking':
- value = _getPercentage(player.media.currentTime, player.media.duration);
+ value = _getPercentage(plyr.media.currentTime, plyr.media.duration);
// Set seek range value only if it's a 'natural' time event
- if (event.type == 'timeupdate' && player.buttons.seek) {
- player.buttons.seek.value = value;
+ if (event.type == 'timeupdate' && plyr.buttons.seek) {
+ plyr.buttons.seek.value = value;
}
-
+
break;
// Events from seek range
@@ -1554,14 +1839,14 @@
// Check buffer status
case 'playing':
case 'progress':
- progress = player.progress.buffer.bar;
- text = player.progress.buffer.text;
+ progress = plyr.progress.buffer.bar;
+ text = plyr.progress.buffer.text;
value = (function() {
- var buffered = player.media.buffered;
+ var buffered = plyr.media.buffered;
// HTML5
if (buffered && buffered.length) {
- return _getPercentage(buffered.end(0), player.media.duration);
+ return _getPercentage(buffered.end(0), plyr.media.duration);
}
// YouTube returns between 0 and 1
else if (typeof buffered === 'number') {
@@ -1589,165 +1874,239 @@
return;
}
- player.secs = parseInt(time % 60);
- player.mins = parseInt((time / 60) % 60);
- player.hours = parseInt(((time / 60) / 60) % 60);
+ // Fallback to 0
+ if (isNaN(time)) {
+ time = 0;
+ }
+
+ plyr.secs = parseInt(time % 60);
+ plyr.mins = parseInt((time / 60) % 60);
+ plyr.hours = parseInt(((time / 60) / 60) % 60);
// Do we need to display hours?
- var displayHours = (parseInt(((player.media.duration / 60) / 60) % 60) > 0);
-
+ var displayHours = (parseInt(((plyr.media.duration / 60) / 60) % 60) > 0);
+
// Ensure it's two digits. For example, 03 rather than 3.
- player.secs = ('0' + player.secs).slice(-2);
- player.mins = ('0' + player.mins).slice(-2);
+ plyr.secs = ('0' + plyr.secs).slice(-2);
+ plyr.mins = ('0' + plyr.mins).slice(-2);
// Render
- element.innerHTML = (displayHours ? player.hours + ':' : '') + player.mins + ':' + player.secs;
+ element.innerHTML = (displayHours ? plyr.hours + ':' : '') + plyr.mins + ':' + plyr.secs;
}
// Show the duration on metadataloaded
function _displayDuration() {
- var duration = player.media.duration || 0;
+ var duration = plyr.media.duration || 0;
// If there's only one time display, display duration there
- if (!player.duration && config.displayDuration && player.media.paused) {
- _updateTimeDisplay(duration, player.currentTime);
+ if (!plyr.duration && config.displayDuration && plyr.media.paused) {
+ _updateTimeDisplay(duration, plyr.currentTime);
}
// If there's a duration element, update content
- if (player.duration) {
- _updateTimeDisplay(duration, player.duration);
+ if (plyr.duration) {
+ _updateTimeDisplay(duration, plyr.duration);
}
}
// Handle time change event
function _timeUpdate(event) {
// Duration
- _updateTimeDisplay(player.media.currentTime, player.currentTime);
+ _updateTimeDisplay(plyr.media.currentTime, plyr.currentTime);
// Playing progress
_updateProgress(event);
}
- // Remove <source> children and src attribute
- function _removeSources() {
- // Find child <source> elements
- var sources = player.media.querySelectorAll('source');
-
- // Remove each
- for (var i = sources.length - 1; i >= 0; i--) {
- _remove(sources[i]);
+ // Add elements to HTML5 media (source, tracks, etc)
+ function _insertChildElements(type, attributes) {
+ if (typeof attributes === 'string') {
+ _insertElement(type, plyr.media, { src: attributes });
}
-
- // Remove src attribute
- player.media.removeAttribute('src');
- }
-
- // Inject a source
- function _addSource(attributes) {
- if (attributes.src) {
- // Create a new <source>
- var element = document.createElement('source');
-
- // Set all passed attributes
- _setAttributes(element, attributes);
-
- // Inject the new source
- _prependChild(player.media, element);
+ else if (attributes.constructor === Array) {
+ for (var i = attributes.length - 1; i >= 0; i--) {
+ _insertElement(type, plyr.media, attributes[i]);
+ }
}
}
// Update source
// Sources are not checked for support so be careful
- function _parseSource(sources) {
- // YouTube
- if (player.type === 'youtube' && typeof sources === 'string') {
- // Destroy YouTube instance
- player.embed.destroy();
-
- // Re-setup YouTube
- // We don't use loadVideoBy[x] here since it has issues
- _setupYouTube(sources);
-
- // Update times
- _timeUpdate();
-
- // Bail
+ function _updateSource(source) {
+ if (typeof source === 'undefined' || !('sources' in source) || !source.sources.length) {
+ _log('Invalid source format', true);
return;
}
- // Pause playback (webkit freaks out)
+ // Pause playback
_pause();
- // Restart
- _seek();
-
- // Remove current sources
- _removeSources();
+ // Clean up YouTube stuff
+ if (plyr.type === 'youtube') {
+ // Destroy the embed instance
+ plyr.embed.destroy();
- // If a single source is passed
- // .source('path/to/video.mp4')
- if (typeof sources === 'string') {
- _addSource({ src: sources });
+ // Clear timer
+ window.clearInterval(plyr.timer.buffering);
+ window.clearInterval(plyr.timer.playing);
}
+ else if (plyr.type === 'video') {
+ // Remove video wrapper
+ _remove(plyr.videoContainer);
+ }
+
+ // Remove the old media
+ _remove(plyr.media);
+
+ // Set the type
+ if ('type' in source) {
+ plyr.type = source.type;
+
+ // Get child type for video (it might be an embed)
+ if(plyr.type === 'video') {
+ var firstSource = source.sources[0];
- // An array of source objects
- // Check if a source exists, use that or set the 'src' attribute?
- // .source([{ src: 'path/to/video.mp4', type: 'video/mp4' },{ src: 'path/to/video.webm', type: 'video/webm' }])
- else if (sources.constructor === Array) {
- for (var index in sources) {
- _addSource(sources[index]);
+ if('type' in firstSource && _inArray(config.types.embed, firstSource.type)) {
+ plyr.type = firstSource.type;
+ }
}
}
- if (player.supported.full) {
- // Reset time display
- _timeUpdate();
+ // Create new markup
+ switch(plyr.type) {
+ case 'video':
+ plyr.media = document.createElement('video');
+ break;
- // Update the UI
- _checkPlaying();
+ case 'audio':
+ plyr.media = document.createElement('audio');
+ break;
+
+ case 'youtube':
+ case 'vimeo':
+ plyr.media = document.createElement('div');
+ plyr.embedId = source.sources[0].src;
+ break;
}
- // Re-load sources
- player.media.load();
+ // Inject the new element
+ _prependChild(plyr.container, plyr.media);
- // Play if autoplay attribute is present
- if (player.media.getAttribute('autoplay') !== null || config.autoplay) {
- _play();
+ // Set attributes for audio video
+ if (_inArray(config.types.html5, plyr.type)) {
+ if (config.crossorigin) {
+ plyr.media.setAttribute('crossorigin', '');
+ }
+ if (config.autoplay) {
+ plyr.media.setAttribute('autoplay', '');
+ }
+ if ('poster' in source) {
+ plyr.media.setAttribute('poster', source.poster);
+ }
+ if (config.loop) {
+ plyr.media.setAttribute('loop', '');
+ }
}
- }
- // Add common function to retrieve media source
- function _source(args) {
- // If not null or undefined, parse it
- if(args !== null && args !== undefined) {
- return _parseSource(args);
+ // Classname reset
+ plyr.container.className = plyr.originalClassName;
+
+ // Restore class hooks
+ _toggleClass(plyr.container, config.classes.fullscreen.active, plyr.isFullscreen);
+ _toggleClass(plyr.container, config.classes.captions.active, plyr.captionsEnabled);
+
+ // Autoplay the new source?
+ config.autoplay = (source.autoplay || config.autoplay);
+
+ // Set new sources for html5
+ if (_inArray(config.types.html5, plyr.type)) {
+ _insertChildElements('source', source.sources);
}
- // Return the current source
+ // Set up from scratch
+ _setupMedia();
+
+ // Trigger media updated
+ _mediaUpdated();
+
+ // HTML5 stuff
+ if (_inArray(config.types.html5, plyr.type)) {
+ // Set volume
+ _setVolume();
+ _updateVolume();
+
+ // UI updates
+ if (plyr.supported.full) {
+ // Reset time display
+ _timeUpdate();
+
+ // Update the UI
+ _checkPlaying();
+ }
- // The following lines inside if/else may fail.
- // Not sure whether to bubble exception up or
- // return a default value or log to console.
- if(player.type === "youtube") {
- return player.embed.getVideoUrl();
+ // Setup captions
+ if ('tracks' in source) {
+ _insertChildElements('track', source.tracks);
+
+ // Captions
+ _setupCaptions();
+ }
+
+ // Load HTML5 sources
+ plyr.media.load();
+
+ // Play if autoplay attribute is present
+ if (config.autoplay) {
+ _play();
+ }
}
- else {
- return player.media.currentSrc;
+
+ if ('title' in source) {
+ config.title = source.title;
+ _setTitle();
}
}
-
// Update poster
function _updatePoster(source) {
- if (player.type === 'video') {
- player.media.setAttribute('poster', source);
+ if (plyr.type === 'video') {
+ plyr.media.setAttribute('poster', source);
}
}
// Listen for events
function _listeners() {
// IE doesn't support input event, so we fallback to change
- var inputEvent = (player.browser.name == 'IE' ? 'change' : 'input');
+ var inputEvent = (plyr.browser.name == 'IE' ? 'change' : 'input');
+
+ // Click play/pause helper
+ function _togglePlay(play) {
+ // Toggle playback
+ if (play) {
+ _play();
+ }
+ else {
+ _pause();
+ }
+
+ // Determine which buttons
+ var trigger = plyr.buttons[play ? "play" : "pause"],
+ target = plyr.buttons[play ? "pause" : "play"];
+
+ // Setup focus and tab focus
+ if(target) {
+ var hadTabFocus = _hasClass(trigger, config.classes.tabFocus);
+
+ setTimeout(function() {
+ target.focus();
+
+ if(hadTabFocus) {
+ _toggleClass(trigger, config.classes.tabFocus, false);
+ _toggleClass(target, config.classes.tabFocus, true);
+ }
+ }, 100);
+ }
+ }
// Detect tab focus
function checkFocus() {
@@ -1755,13 +2114,13 @@
if (!focused || focused == document.body) {
focused = null;
}
- else {
+ else if (document.querySelector) {
focused = document.querySelector(':focus');
}
- for (var button in player.buttons) {
- var element = player.buttons[button];
+ for (var button in plyr.buttons) {
+ var element = plyr.buttons[button];
- _toggleClass(element, 'tab-focus', (element === focused));
+ _toggleClass(element, config.classes.tabFocus, (element === focused));
}
}
_on(window, 'keyup', function(event) {
@@ -1771,8 +2130,11 @@
checkFocus();
}
});
- for (var button in player.buttons) {
- var element = player.buttons[button];
+ _on(document.body, 'click', function() {
+ _toggleClass(_getElement('.' + config.classes.tabFocus), config.classes.tabFocus, false);
+ });
+ for (var button in plyr.buttons) {
+ var element = plyr.buttons[button];
_on(element, 'blur', function() {
_toggleClass(element, 'tab-focus', false);
@@ -1780,62 +2142,56 @@
}
// Play
- _on(player.buttons.play, 'click', function() {
- _play();
- setTimeout(function() { player.buttons.pause.focus(); }, 100);
- });
+ _on(plyr.buttons.play, 'click', function() { _togglePlay(true); });
// Pause
- _on(player.buttons.pause, 'click', function() {
- _pause();
- setTimeout(function() { player.buttons.play.focus(); }, 100);
- });
+ _on(plyr.buttons.pause, 'click', function() { _togglePlay(); });
// Restart
- _on(player.buttons.restart, 'click', _seek);
+ _on(plyr.buttons.restart, 'click', _seek);
// Rewind
- _on(player.buttons.rewind, 'click', _rewind);
+ _on(plyr.buttons.rewind, 'click', _rewind);
// Fast forward
- _on(player.buttons.forward, 'click', _forward);
+ _on(plyr.buttons.forward, 'click', _forward);
- // Seek
- _on(player.buttons.seek, inputEvent, _seek);
+ // Seek
+ _on(plyr.buttons.seek, inputEvent, _seek);
// Set volume
- _on(player.volume, inputEvent, function() {
+ _on(plyr.volume, inputEvent, function() {
_setVolume(this.value);
});
// Mute
- _on(player.buttons.mute, 'click', _toggleMute);
+ _on(plyr.buttons.mute, 'click', _toggleMute);
// Fullscreen
- _on(player.buttons.fullscreen, 'click', _toggleFullscreen);
+ _on(plyr.buttons.fullscreen, 'click', _toggleFullscreen);
// Handle user exiting fullscreen by escaping etc
if (fullscreen.supportsFullScreen) {
_on(document, fullscreen.fullScreenEventName, _toggleFullscreen);
}
-
+
// Time change on media
- _on(player.media, 'timeupdate seeking', _timeUpdate);
+ _on(plyr.media, 'timeupdate seeking', _timeUpdate);
// Update manual captions
- _on(player.media, 'timeupdate', _seekManualCaptions);
+ _on(plyr.media, 'timeupdate', _seekManualCaptions);
// Display duration
- _on(player.media, 'loadedmetadata', _displayDuration);
+ _on(plyr.media, 'loadedmetadata', _displayDuration);
// Captions
- _on(player.buttons.captions, 'click', _toggleCaptions);
+ _on(plyr.buttons.captions, 'click', _toggleCaptions);
// Handle the media finishing
- _on(player.media, 'ended', function() {
- // Clear
- if (player.type === 'video') {
- player.captionsContainer.innerHTML = '';
+ _on(plyr.media, 'ended', function() {
+ // Clear
+ if (plyr.type === 'video') {
+ plyr.captionsContainer.innerHTML = '';
}
// Reset UI
@@ -1843,29 +2199,29 @@
});
// Check for buffer progress
- _on(player.media, 'progress playing', _updateProgress);
+ _on(plyr.media, 'progress playing', _updateProgress);
// Handle native mute
- _on(player.media, 'volumechange', _updateVolume);
+ _on(plyr.media, 'volumechange', _updateVolume);
// Handle native play/pause
- _on(player.media, 'play pause', _checkPlaying);
+ _on(plyr.media, 'play pause', _checkPlaying);
// Loading
- _on(player.media, 'waiting canplay seeked', _checkLoading);
+ _on(plyr.media, 'waiting canplay seeked', _checkLoading);
// Click video
- if (player.type === 'video' && config.click) {
- _on(player.videoContainer, 'click', function() {
- if (player.media.paused) {
- _triggerEvent(player.buttons.play, 'click');
+ if (plyr.type === 'video' && config.click) {
+ _on(plyr.videoContainer, 'click', function() {
+ if (plyr.media.paused) {
+ _play();
}
- else if (player.media.ended) {
+ else if (plyr.media.ended) {
_seek();
- _triggerEvent(player.buttons.play, 'click');
+ _play();
}
else {
- _triggerEvent(player.buttons.pause, 'click');
+ _pause();
}
});
}
@@ -1876,88 +2232,103 @@
// http://stackoverflow.com/questions/12528049/if-a-dom-element-is-removed-are-its-listeners-also-removed-from-memory
function _destroy() {
// Bail if the element is not initialized
- if (!player.init) {
+ if (!plyr.init) {
return null;
}
// Reset container classname
- player.container.setAttribute('class', config.selectors.container.replace('.', ''));
+ plyr.container.setAttribute('class', _getClassname(config.selectors.container));
// Remove init flag
- player.init = false;
+ plyr.init = false;
// Remove controls
- _remove(_getElement(config.selectors.controls));
+ _remove(_getElement(config.selectors.controls.wrapper));
// YouTube
- if (player.type === 'youtube') {
- player.embed.destroy();
+ if (plyr.type === 'youtube') {
+ plyr.embed.destroy();
return;
}
// If video, we need to remove some more
- if (player.type === 'video') {
+ if (plyr.type === 'video') {
// Remove captions
_remove(_getElement(config.selectors.captions));
// Remove video wrapper
- _unwrap(player.videoContainer);
+ _unwrap(plyr.videoContainer);
}
// Restore native video controls
- player.media.setAttribute('controls', '');
+ plyr.media.setAttribute('controls', '');
// Clone the media element to remove listeners
// http://stackoverflow.com/questions/19469881/javascript-remove-all-event-listeners-of-specific-type
- var clone = player.media.cloneNode(true);
- player.media.parentNode.replaceChild(clone, player.media);
+ var clone = plyr.media.cloneNode(true);
+ plyr.media.parentNode.replaceChild(clone, plyr.media);
}
// Setup a player
function _init() {
// Bail if the element is initialized
- if (player.init) {
+ if (plyr.init) {
return null;
}
- // Setup the fullscreen api
+ // Setup the fullscreen api
fullscreen = _fullscreen();
// Sniff out the browser
- player.browser = _browserSniff();
+ plyr.browser = _browserSniff();
// Get the media element
- player.media = player.container.querySelectorAll('audio, video, div')[0];
+ plyr.media = plyr.container.querySelectorAll('audio, video, div')[0];
+
+ // Add style hook
+ _toggleClass(plyr.container, defaults.selectors.container.replace('.', ''), true);
- // Set media type
- var tagName = player.media.tagName.toLowerCase();
+ // Get original classname
+ plyr.originalClassName = plyr.container.className;
+
+ // Set media type based on tag or data attribute
+ // Supported: video, audio, vimeo, youtube
+ var tagName = plyr.media.tagName.toLowerCase();
if (tagName === 'div') {
- player.type = player.media.getAttribute('data-type');
+ plyr.type = plyr.media.getAttribute('data-type');
+ plyr.embedId = plyr.media.getAttribute('data-video-id');
+
+ // Clean up
+ plyr.media.removeAttribute('data-type');
+ plyr.media.removeAttribute('data-video-id');
}
else {
- player.type = tagName;
+ plyr.type = tagName;
+ config.crossorigin = (plyr.media.getAttribute('crossorigin') !== null);
+ config.autoplay = (config.autoplay || (plyr.media.getAttribute('autoplay') !== null));
+ config.loop = (config.loop || (plyr.media.getAttribute('loop') !== null));
}
-
+
// Check for full support
- player.supported = api.supported(player.type);
+ plyr.supported = api.supported(plyr.type);
// If no native support, bail
- if (!player.supported.basic) {
+ if (!plyr.supported.basic) {
return false;
}
// Debug info
- _log(player.browser.name + ' ' + player.browser.version);
+ _log(plyr.browser.name + ' ' + plyr.browser.version);
// Setup media
_setupMedia();
// Setup interface
- if (player.type == 'video' || player.type == 'audio') {
+ if (plyr.type == 'video' || plyr.type == 'audio') {
// Bail if no support
- if (!player.supported.full) {
+ if (!plyr.supported.full) {
// Successful setup
- player.init = true;
+ plyr.init = true;
// Don't inject controls if no full support
return;
@@ -1971,12 +2342,12 @@
_displayDuration();
}
- // Set up aria-label for Play button with the title option
- _setupPlayAria();
+ // Set title on button and frame
+ _setTitle();
}
// Successful setup
- player.init = true;
+ plyr.init = true;
}
function _setupInterface() {
@@ -1991,10 +2362,15 @@
// Captions
_setupCaptions();
+ // Media updated
+ _mediaUpdated();
+
// Set volume
_setVolume();
_updateVolume();
+ }
+ function _mediaUpdated() {
// Setup fullscreen
_setupFullscreen();
@@ -2002,37 +2378,37 @@
_listeners();
}
- // Initialize instance
+ // Initialize instance
_init();
// If init failed, return an empty object
- if (!player.init) {
+ if (!plyr.init) {
return {};
}
return {
- media: player.media,
+ media: plyr.media,
play: _play,
pause: _pause,
restart: _seek,
rewind: _rewind,
forward: _forward,
seek: _seek,
- source: _source,
+ source: _updateSource,
poster: _updatePoster,
setVolume: _setVolume,
togglePlay: _togglePlay,
toggleMute: _toggleMute,
toggleCaptions: _toggleCaptions,
toggleFullscreen: _toggleFullscreen,
- isFullscreen: function() { return player.isFullscreen || false; },
- support: function(mimeType) { return _supportMime(player, mimeType); },
+ isFullscreen: function() { return plyr.isFullscreen || false; },
+ support: function(mimeType) { return _supportMime(plyr, mimeType); },
destroy: _destroy,
restore: _init
};
}
- // Check for support
+ // Check for support
api.supported = function(type) {
var browser = _browserSniff(),
oldIE = (browser.name === 'IE' && browser.version <= 9),
@@ -2042,17 +2418,18 @@
basic, full;
switch (type) {
- case 'video':
+ case 'video':
basic = video;
full = (basic && (!oldIE && !iPhone));
break;
- case 'audio':
+ case 'audio':
basic = audio;
full = (basic && !oldIE);
break;
- case 'youtube':
+ case 'vimeo':
+ case 'youtube':
basic = true;
full = (!oldIE && !iPhone);
break;
@@ -2069,20 +2446,39 @@
};
// Expose setup function
- api.setup = function(options) {
+ api.setup = function(elements, options) {
+ // Get the players
+ var instances = [];
+
+ // Select the elements
+ // Assume elements is a NodeList by default
+ if (typeof elements === 'string') {
+ elements = document.querySelectorAll(elements);
+ }
+ // Single HTMLElement passed
+ else if (elements instanceof HTMLElement) {
+ elements = [elements];
+ }
+ // No selector passed, possibly options as first argument
+ else if (!(elements instanceof NodeList) && typeof elements !== 'string') {
+ // If options are the first argument
+ if (typeof options === 'undefined' && typeof elements === 'object') {
+ options = elements;
+ }
+
+ // Use default selector
+ elements = document.querySelectorAll(defaults.selectors.container);
+ }
+
// Extend the default options with user specified
config = _extend(defaults, options);
// Bail if disabled or no basic support
// You may want to disable certain UAs etc
- if (!config.enabled || !api.supported().basic) {
+ if (!config.enabled || !api.supported().basic || !elements.length) {
return false;
}
- // Get the players
- var elements = document.querySelectorAll(config.selectors.container),
- players = [];
-
// Create a player instance for each element
for (var i = elements.length - 1; i >= 0; i--) {
// Get the current element
@@ -2103,10 +2499,11 @@
}
// Add to return array even if it's already setup
- players.push(element.plyr);
+ instances.push(element.plyr);
}
- return players;
+ return instances;
};
-}(this.plyr = this.plyr || {}));
+ return api;
+}));
diff --git a/src/less/plyr.less b/src/less/plyr.less
index a008fd21..affa7cef 100644
--- a/src/less/plyr.less
+++ b/src/less/plyr.less
@@ -21,7 +21,7 @@
// Captions
@font-size-captions-base: ceil(@font-size-base * 1.25);
@font-size-captions-medium: ceil(@font-size-base * 1.5);
-@font-size-captions-large: (@font-size-base * 2);
+@font-size-captions-large: (@font-size-base * 2);
// Controls
@control-spacing: 10px;
@@ -32,16 +32,18 @@
// Tooltips
@tooltip-bg: @controls-bg;
-@tooltip-border-color: @off-white;
+@tooltip-border-color: fade(@gray-dark, 10%);
+@tooltip-border-width: 1px;
+@tooltip-shadow: 0 0 5px @tooltip-border-color, 0 0 0 @tooltip-border-width @tooltip-border-color;
@tooltip-color: @control-color;
-@tooltip-padding: @control-spacing;
-@tooltip-arrow-size: 5px;
+@tooltip-padding: @control-spacing;
+@tooltip-arrow-size: 6px;
@tooltip-radius: 3px;
// Progress
-@progress-bg: rgba(red(@gray), green(@gray), blue(@gray), .2);
+@progress-bg: fade(@gray, 20%);
@progress-playing-bg: @blue;
-@progress-buffered-bg: rgba(red(@gray), green(@gray), blue(@gray), .25);
+@progress-buffered-bg: fade(@gray, 25%);
@progress-loading-size: 40px;
@progress-loading-bg: rgba(0,0,0, .15);
@@ -59,14 +61,12 @@
// Animation
// ---------------------------------------
-
@keyframes progress {
to { background-position: @progress-loading-size 0; }
}
// Mixins
// -------------------------------
-
// Contrast
.contrast-control-color(@color: "") when (lightness(@color) >= 65%) {
@control-color: @gray-light;
@@ -94,14 +94,14 @@
// Contain floats: nicolasgallagher.com/micro-clearfix-hack/
.clearfix() {
zoom: 1;
- &:before,
+ &:before,
&:after { content: ""; display: table; }
&:after { clear: both; }
}
// Tab focus styles
.tab-focus() {
- outline: thin dotted #000;
- outline-offset: 0;
+ outline: 1px dotted fade(@gray-dark, 80%);
+ outline-offset: 3px;
}
// <input type="range"> styling
@@ -110,8 +110,8 @@
width: @volume-thumb-width;
background: @volume-thumb-bg;
border: 0;
- border-radius: (@volume-thumb-height / 2);
- transition: background .3s ease;
+ border-radius: 100%;
+ transition: background .3s ease, transform .2s ease;
cursor: ew-resize;
}
.volume-track() {
@@ -123,30 +123,19 @@
.seek-thumb() {
background: transparent;
border: 0;
- width: (@control-spacing * 2);
+ width: (@control-spacing * 4);
height: @control-spacing;
+ transform: translateX(-50%);
}
.seek-track() {
background: none;
border: 0;
}
-// Screen reader only
-// -------------------------------
-.sr-only {
- position: absolute !important;
- clip: rect(1px, 1px, 1px, 1px);
- padding: 0 !important;
- border: 0 !important;
- height: 1px !important;
- width: 1px !important;
- overflow: hidden;
-}
-
// Styles
// -------------------------------
-// Base
-.player {
+// Base
+.plyr {
position: relative;
max-width: 100%;
min-width: 290px;
@@ -156,12 +145,28 @@
&,
*,
*::after,
- *::before {
- box-sizing: border-box;
+ *::before {
+ box-sizing: border-box;
+ }
+
+ // Fix 300ms delay
+ a, button, input, label {
+ touch-action: manipulation;
+ }
+
+ // Screen reader only elements
+ &__sr-only {
+ position: absolute !important;
+ clip: rect(1px, 1px, 1px, 1px);
+ padding: 0 !important;
+ border: 0 !important;
+ height: 1px !important;
+ width: 1px !important;
+ overflow: hidden;
}
// For video
- &-video-wrapper {
+ &__video-wrapper {
position: relative;
}
video,
@@ -171,11 +176,13 @@
vertical-align: middle;
}
- // For embeds
- &-video-embed {
+ // Container for embeds
+ &__video-embed {
padding-bottom: 56.25%; /* 16:9 */
height: 0;
-
+ overflow: hidden;
+ background: #000;
+
iframe {
position: absolute;
top: 0;
@@ -183,11 +190,19 @@
width: 100%;
height: 100%;
border: 0;
+ user-select: none;
+ }
+
+ // Vimeo hack
+ > div {
+ position: relative;
+ padding-bottom: 200%;
+ transform: translateY(-35.95%);
}
}
// Captions
- &-captions {
+ &__captions {
display: none;
position: absolute;
bottom: 0;
@@ -212,15 +227,15 @@
font-size: @font-size-captions-medium;
}
}
- &.captions-active &-captions {
+ &--captions-active &__captions {
display: block;
}
- &.fullscreen-active &-captions {
+ &--fullscreen-active &__captions {
font-size: @font-size-captions-large;
}
- // Player controls
- &-controls {
+ // Playback controls
+ &__controls {
.clearfix();
.font-smoothing();
position: relative;
@@ -231,20 +246,20 @@
box-shadow: 0 1px 1px rgba(red(@gray-dark), green(@gray-dark), blue(@gray-dark), .2);
// Layout
- &-right {
+ &--right {
display: block;
margin: @control-spacing auto 0;
}
@media (min-width: @bp-control-split) {
- &-left {
+ &--left {
float: left;
}
- &-right {
+ &--right {
float: right;
margin-top: 0;
}
}
-
+
// Buttons
button {
display: inline-block;
@@ -258,7 +273,7 @@
cursor: pointer;
color: @control-color;
transition: background .3s ease, color .3s ease, opacity .3s ease;
-
+
svg {
width: 18px;
height: 18px;
@@ -266,7 +281,7 @@
fill: currentColor;
transition: fill .3s ease;
}
-
+
// Hover and tab focus
&.tab-focus,
&:hover {
@@ -280,14 +295,14 @@
}
// Hide toggle icons by default
- .icon-exit-fullscreen,
- .icon-muted,
- .icon-captions-on {
+ .icon--exit-fullscreen,
+ .icon--muted,
+ .icon--captions-on {
display: none;
}
- // Player time
- .player-time {
+ // plyr time
+ .plyr__time {
display: inline-block;
vertical-align: middle;
margin-left: @control-spacing;
@@ -298,7 +313,7 @@
}
// Media duration hidden on small screens
- .player-time + .player-time {
+ .plyr__time + .plyr__time {
display: none;
@media (min-width: @bp-control-split) {
@@ -314,7 +329,7 @@
}
// Tooltips
- &-tooltip {
+ &__tooltip {
position: absolute;
z-index: 2;
bottom: 100%;
@@ -323,45 +338,63 @@
opacity: 0;
background: @tooltip-bg;
- border: 1px solid @tooltip-border-color;
+ box-shadow: @tooltip-shadow;
border-radius: @tooltip-radius;
color: @tooltip-color;
font-size: @font-size-small;
line-height: 1.5;
font-weight: 600;
- transform: translate(-50%, (@tooltip-padding * 3)) scale(0);
+ transform: translate(-50%, (@tooltip-padding * 3)) scale(.8);
transform-origin: 50% 100%;
transition: transform .2s .1s ease, opacity .2s .1s ease;
-
- // Arrow
- &::after {
+
+ // Arrows
+ &::after,
+ &::before {
content: '';
position: absolute;
- z-index: 1;
+ width: 0;
+ height: 0;
top: 100%;
left: 50%;
- display: block;
- width: 10px;
- height: 10px;
- background: @tooltip-bg;
- transform: translate(-50%, -50%) rotate(45deg) translateY(1px);
- border: 1px solid @tooltip-border-color;
- border-width: 0 1px 1px 0;
+ transform: translateX(-50%);
+ }
+ // The border triangle
+ &::after {
+ @border-arrow-size: (@tooltip-arrow-size + (@tooltip-border-width * 1));
+ bottom: -(@border-arrow-size + @tooltip-border-width);
+ border-right: @border-arrow-size solid transparent;
+ border-top: @border-arrow-size solid @tooltip-border-color;
+ border-left: @border-arrow-size solid transparent;
+ z-index: 1;
+ }
+ // The background triangle
+ &::before {
+ bottom: -@tooltip-arrow-size;
+ border-right: @tooltip-arrow-size solid transparent;
+ border-top: @tooltip-arrow-size solid @tooltip-bg;
+ border-left: @tooltip-arrow-size solid transparent;
+ z-index: 2;
}
}
- button:hover .player-tooltip,
- button.tab-focus:focus .player-tooltip {
+ button:hover .plyr__tooltip,
+ button.tab-focus:focus .plyr__tooltip {
opacity: 1;
transform: translate(-50%, 0) scale(1);
}
- button:hover .player-tooltip {
+ button:hover .plyr__tooltip {
z-index: 3;
}
- // Player progress
+ // Common range styles
+ input[type='range'].tab-focus {
+ .tab-focus();
+ }
+
+ // Playback progress
// <progress> element
- &-progress {
+ &__progress {
position: absolute;
bottom: 100%;
left: 0;
@@ -370,9 +403,9 @@
height: @control-spacing;
background: @progress-bg;
- &-buffer[value],
- &-played[value],
- &-seek[type='range'] {
+ &--buffer[value],
+ &--played[value],
+ &--seek[type='range'] {
position: absolute;
left: 0;
top: 0;
@@ -387,8 +420,8 @@
border: none;
background: transparent;
}
- &-buffer[value],
- &-played[value] {
+ &--buffer[value],
+ &--played[value] {
&::-webkit-progress-bar {
background: transparent;
}
@@ -401,18 +434,18 @@
background: currentColor;
}
}
- &-played[value] {
+ &--played[value] {
z-index: 2;
color: @progress-playing-bg;
}
- &-buffer[value] {
+ &--buffer[value] {
color: @progress-buffered-bg;
}
// Seek control
// <input[type='range']> element
// Specificity is for bootstrap compatibility
- &-seek[type='range'] {
+ &--seek[type='range'] {
z-index: 4;
cursor: pointer;
outline: 0;
@@ -434,7 +467,7 @@
-moz-appearance: none;
.seek-thumb();
}
-
+
// Microsoft
&::-ms-track {
color: transparent;
@@ -458,47 +491,47 @@
}
// Loading state
- &.loading .player-progress-buffer {
+ &--loading .plyr__progress--buffer {
animation: progress 1s linear infinite;
background-size: @progress-loading-size @progress-loading-size;
background-repeat: repeat-x;
background-color: @progress-buffered-bg;
background-image: linear-gradient(
- -45deg,
- @progress-loading-bg 25%,
- transparent 25%,
- transparent 50%,
- @progress-loading-bg 50%,
+ -45deg,
+ @progress-loading-bg 25%,
+ transparent 25%,
+ transparent 50%,
+ @progress-loading-bg 50%,
@progress-loading-bg 75%,
- transparent 75%,
+ transparent 75%,
transparent);
color: transparent;
}
// States
- &-controls [data-player='pause'],
- &.playing .player-controls [data-player='play'] {
+ &__controls [data-plyr='pause'],
+ &--playing .plyr__controls [data-plyr='play'] {
display: none;
}
- &.playing .player-controls [data-player='pause'] {
+ &--playing .plyr__controls [data-plyr='pause'] {
display: inline-block;
}
// Volume control
// <input[type='range']> element
// Specificity is for bootstrap compatibility
- &-volume[type='range'] {
+ &__volume[type='range'] {
display: inline-block;
vertical-align: middle;
-webkit-appearance: none;
-moz-appearance: none;
width: 100px;
margin: 0 @control-spacing 0 0;
- padding: 0;
+ padding: 0;
cursor: pointer;
background: transparent;
border: none;
-
+
// Webkit
&::-webkit-slider-runnable-track {
.volume-track();
@@ -516,7 +549,7 @@
&::-moz-range-thumb {
.volume-thumb();
}
-
+
// Microsoft
&::-ms-track {
height: @volume-track-height;
@@ -551,30 +584,30 @@
// Hide sound controls on iOS
// It's not supported to change volume using JavaScript:
// https://developer.apple.com/library/safari/documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/Device-SpecificConsiderations/Device-SpecificConsiderations.html
- &.ios &-volume,
- &.ios [data-player='mute'],
- &-audio.ios &-controls-right {
+ &--is-ios &-volume,
+ &--is-ios [data-plyr='mute'],
+ &--is-ios.plyr--audio &__controls--right {
display: none;
}
// Center buttons so it looks less odd
- &-audio.ios &-controls-left {
+ &--is-ios.plyr--audio &__controls--left {
float: none;
}
// Audio specific styles
// Position the progress within the container
- &-audio .player-controls {
+ &--audio .plyr__controls {
padding-top: (@control-spacing * 2);
}
- &-audio .player-progress {
+ &--audio .plyr__progress {
bottom: auto;
top: 0;
background: @off-white;
}
// Full screen mode
- &-fullscreen,
- &.fullscreen-active {
+ &--fullscreen,
+ &--fullscreen-active {
position: fixed;
top: 0;
left: 0;
@@ -588,48 +621,49 @@
video {
height: 100%;
}
- .player-video-wrapper {
+ .plyr__video-wrapper {
height: 100%;
width: 100%;
- }
- .player-controls {
+ }
+ .plyr__controls {
position: absolute;
bottom: 0;
left: 0;
right: 0;
}
+ }
- // Hide controls when playing in full screen
- &.fullscreen-hide-controls.playing {
- .player-controls {
- transform: translateY(100%) translateY(@control-spacing / 2);
- transition: transform .3s .2s ease;
- }
- &.player-hover .player-controls {
- transform: translateY(0);
- }
- .player-captions {
- bottom: (@control-spacing / 2);
- transition: bottom .3s .2s ease;
- }
+ // Hide controls when playing in full screen
+ &--fullscreen--hide-controls&--fullscreen-active&--playing {
+ .plyr__controls {
+ transform: translateY(100%) translateY(@control-spacing / 2);
+ transition: transform .3s .2s ease;
+ }
+ &.plyr--hover .plyr__controls {
+ transform: translateY(0);
}
+ .plyr__captions {
+ bottom: (@control-spacing / 2);
+ transition: bottom .3s .2s ease;
+ }
+ }
- // Captions
- .player-captions,
- &.fullscreen-hide-controls.playing.player-hover .player-captions {
- top: auto;
- bottom: 90px;
+ // Captions
+ &--fullscreen .plyr__captions,
+ &--fullscreen-active .plyr__captions,
+ &--fullscreen--hide-controls&--fullscreen-active&--playing&--hover &__captions {
+ top: auto;
+ bottom: 90px;
- @media (min-width: @bp-control-split) {
- bottom: 60px;
- }
+ @media (min-width: @bp-control-split) {
+ bottom: 60px;
}
}
// Change icons on state change
- &.fullscreen-active .icon-exit-fullscreen,
- &.muted .player-controls .icon-muted,
- &.captions-active .player-controls .icon-captions-on {
+ &--fullscreen-active .icon--exit-fullscreen,
+ &--muted .plyr__controls .icon--muted,
+ &--captions-active .plyr__controls .icon--captions-on {
display: block;
& + svg {
@@ -638,12 +672,12 @@
}
// Some options are hidden by default
- [data-player='captions'],
- [data-player='fullscreen'] {
+ [data-plyr='captions'],
+ [data-plyr='fullscreen'] {
display: none;
}
- &.captions-enabled [data-player='captions'],
- &.fullscreen-enabled [data-player='fullscreen'] {
+ &--captions-enabled [data-plyr='captions'],
+ &--fullscreen-enabled [data-plyr='fullscreen'] {
display: inline-block;
}
-} \ No newline at end of file
+}
diff --git a/src/sass/plyr.scss b/src/sass/plyr.scss
index de386cd8..7cefca3a 100644
--- a/src/sass/plyr.scss
+++ b/src/sass/plyr.scss
@@ -44,15 +44,18 @@ $control-color-hover: null !default;
// Tooltips
$tooltip-bg: $controls-bg !default;
+$tooltip-border-color: transparentize(@gray-dark, .1) !default;
+$tooltip-border-width: 1px;
+$tooltip-shadow: 0 0 5px $tooltip-border-color, 0 0 0 $tooltip-border-width $tooltip-border-color;
$tooltip-color: $control-color !default;
$tooltip-padding: $control-spacing !default;
-$tooltip-arrow-size: 5px !default;
+$tooltip-arrow-size: 6px !default;
$tooltip-radius: 3px !default;
// Progress
-$progress-bg: rgba(red($gray), green($gray), blue($gray), .2) !default;
+$progress-bg: transparentize($gray, .2) !default;
$progress-playing-bg: $blue !default;
-$progress-buffered-bg: rgba(red($gray), green($gray), blue($gray), .25) !default;
+$progress-buffered-bg: transparentize($gray, .25) !default;
$progress-loading-size: 40px !default;
$progress-loading-bg: rgba(0,0,0, .15) !default;
@@ -70,7 +73,6 @@ $bp-captions-large: 768px !default; // When captions jump to the larger
// Animation
// ---------------------------------------
-
@keyframes progress {
to { background-position: $progress-loading-size 0; }
}
@@ -149,7 +151,7 @@ $bp-captions-large: 768px !default; // When captions jump to the larger
// Styles
// -------------------------------
// Base
-.player {
+.plyr {
position: relative;
max-width: 100%;
min-width: 290px;
@@ -178,6 +180,8 @@ $bp-captions-large: 768px !default; // When captions jump to the larger
&-video-embed {
padding-bottom: 56.25%; /* 16:9 */
height: 0;
+ overflow: hidden;
+ background: #000;
iframe {
position: absolute;
@@ -187,6 +191,13 @@ $bp-captions-large: 768px !default; // When captions jump to the larger
height: 100%;
border: 0;
}
+
+ // Vimeo hack
+ > div {
+ position: relative;
+ padding-bottom: 200%;
+ transform: translateY(-35.95%);
+ }
}
// Captions
@@ -222,7 +233,7 @@ $bp-captions-large: 768px !default; // When captions jump to the larger
font-size: $font-size-captions-large;
}
- // Player controls
+ // Playback controls
&-controls {
@include clearfix();
@include font-smoothing();
@@ -290,7 +301,7 @@ $bp-captions-large: 768px !default; // When captions jump to the larger
}
// Time display
- .player-time {
+ .plyr-time {
display: inline-block;
vertical-align: middle;
margin-left: $control-spacing;
@@ -301,7 +312,7 @@ $bp-captions-large: 768px !default; // When captions jump to the larger
}
// Media duration hidden on small screens
- .player-time + .player-time {
+ .plyr-time + .plyr-time {
display: none;
@media (min-width: $bp-control-split) {
@@ -326,41 +337,56 @@ $bp-captions-large: 768px !default; // When captions jump to the larger
opacity: 0;
background: $tooltip-bg;
+ box-shadow: $tooltip-shadow;
border-radius: $tooltip-radius;
color: $tooltip-color;
font-size: $font-size-small;
line-height: 1.5;
font-weight: 600;
- transform: translate(-50%, ($tooltip-padding * 3)) scale(0);
+ transform: translate(-50%, ($tooltip-padding * 3)) scale(.8);
transform-origin: 50% 100%;
transition: transform .2s .1s ease, opacity .2s .1s ease;
- &::after {
+ // Arrows
+ &::after,
+ &::before {
content: '';
- display: block;
position: absolute;
- left: 50%;
- bottom: -$tooltip-arrow-size;
- margin-left: -$tooltip-arrow-size;
width: 0;
height: 0;
- transition: inherit;
- border-style: solid;
- border-width: $tooltip-arrow-size $tooltip-arrow-size 0 $tooltip-arrow-size;
- border-color: $controls-bg transparent transparent;
+ top: 100%;
+ left: 50%;
+ transform: translateX(-50%);
+ }
+ // The border triangle
+ &::after {
+ $border-arrow-size: ($tooltip-arrow-size + ($tooltip-border-width * 1));
+ bottom: -($border-arrow-size + $tooltip-border-width);
+ border-right: $border-arrow-size solid transparent;
+ border-top: $border-arrow-size solid $tooltip-border-color;
+ border-left: $border-arrow-size solid transparent;
+ z-index: 1;
+ }
+ // The background triangle
+ &::before {
+ bottom: -$tooltip-arrow-size;
+ border-right: $tooltip-arrow-size solid transparent;
+ border-top: $tooltip-arrow-size solid $tooltip-bg;
+ border-left: $tooltip-arrow-size solid transparent;
+ z-index: 2;
}
}
- button:hover .player-tooltip,
- button:focus .player-tooltip {
+ button:hover .plyr-tooltip,
+ button:focus .plyr-tooltip {
opacity: 1;
transform: translate(-50%, 0) scale(1);
}
- button:hover .player-tooltip {
+ button:hover .plyr-tooltip {
z-index: 3;
}
- // Player progress
+ // Playback progress
// <progress> element
&-progress {
position: absolute;
@@ -459,7 +485,7 @@ $bp-captions-large: 768px !default; // When captions jump to the larger
}
// Loading state
- &.loading .player-progress-buffer {
+ &.loading .plyr-progress-buffer {
animation: progress 1s linear infinite;
background-size: $progress-loading-size $progress-loading-size;
background-repeat: repeat-x;
@@ -477,11 +503,11 @@ $bp-captions-large: 768px !default; // When captions jump to the larger
}
// States
- &-controls [data-player='pause'],
- &.playing .player-controls [data-player='play'] {
+ &-controls [data-plyr='pause'],
+ &.playing .plyr-controls [data-plyr='play'] {
display: none;
}
- &.playing .player-controls [data-player='pause'] {
+ &.playing .plyr-controls [data-plyr='pause'] {
display: inline-block;
}
@@ -553,7 +579,7 @@ $bp-captions-large: 768px !default; // When captions jump to the larger
// It's not supported to change volume using JavaScript:
// https://developer.apple.com/library/safari/documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/Device-SpecificConsiderations/Device-SpecificConsiderations.html
&.ios &-volume,
- &.ios [data-player='mute'],
+ &.ios [data-plyr='mute'],
&-audio.ios &-controls-right {
display: none;
}
@@ -564,10 +590,10 @@ $bp-captions-large: 768px !default; // When captions jump to the larger
// Audio specific styles
// Position the progress within the container
- &-audio .player-controls {
+ &-audio .plyr-controls {
padding-top: ($control-spacing * 2);
}
- &-audio .player-progress {
+ &-audio .plyr-progress {
bottom: auto;
top: 0;
background: $off-white;
@@ -589,11 +615,11 @@ $bp-captions-large: 768px !default; // When captions jump to the larger
video {
height: 100%;
}
- .player-video-wrapper {
+ .plyr-video-wrapper {
height: 100%;
width: 100%;
}
- .player-controls {
+ .plyr-controls {
position: absolute;
bottom: 0;
left: 0;
@@ -602,22 +628,22 @@ $bp-captions-large: 768px !default; // When captions jump to the larger
// Hide controls when playing in full screen
&.fullscreen-hide-controls.playing {
- .player-controls {
+ .plyr-controls {
transform: translateY(100%) translateY($control-spacing / 2);
transition: transform .3s .2s ease;
}
- &.player-hover .player-controls {
+ &.plyr-hover .plyr-controls {
transform: translateY(0);
}
- .player-captions {
+ .plyr-captions {
bottom: ($control-spacing / 2);
transition: bottom .3s .2s ease;
}
}
// Captions
- .player-captions,
- &.fullscreen-hide-controls.playing.player-hover .player-captions {
+ .plyr-captions,
+ &.fullscreen-hide-controls.playing.plyr-hover .plyr-captions {
top: auto;
bottom: 90px;
@@ -629,8 +655,8 @@ $bp-captions-large: 768px !default; // When captions jump to the larger
// Change icons on state change
&.fullscreen-active .icon-exit-fullscreen,
- &.muted .player-controls .icon-muted,
- &.captions-active .player-controls .icon-captions-on {
+ &.muted .plyr-controls .icon-muted,
+ &.captions-active .plyr-controls .icon-captions-on {
display: block;
& + svg {
@@ -639,12 +665,12 @@ $bp-captions-large: 768px !default; // When captions jump to the larger
}
// Some options are hidden by default
- [data-player='captions'],
- [data-player='fullscreen'] {
+ [data-plyr='captions'],
+ [data-plyr='fullscreen'] {
display: none;
}
- &.captions-enabled [data-player='captions'],
- &.fullscreen-enabled [data-player='fullscreen'] {
+ &.captions-enabled [data-plyr='captions'],
+ &.fullscreen-enabled [data-plyr='fullscreen'] {
display: inline-block;
}
}
diff --git a/src/sprite/icon-captions-off.svg b/src/sprite/icon-captions-off.svg
index c9bd5b3c..d9c2c444 100644
--- a/src/sprite/icon-captions-off.svg
+++ b/src/sprite/icon-captions-off.svg
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
- <defs></defs>
- <g id="Page-1" stroke="none" stroke-width="1" sketch:type="MSPage">
- <path d="M1,2 C0.448,2 0,2.448 0,3 L0,15 C0,15.552 0.448,16 1,16 L17,16 C17.552,16 18,15.552 18,15 L18,3 C18,2.448 17.552,2 17,2 L1,2 Z M2,14 L2,4 L16,4 L16,14 L2,14 L2,14 Z" id="Shape" sketch:type="MSShapeGroup"></path>
+ <title>Captions Off</title>
+ <g>
+ <path d="M1,2 C0.448,2 0,2.448 0,3 L0,15 C0,15.552 0.448,16 1,16 L17,16 C17.552,16 18,15.552 18,15 L18,3 C18,2.448 17.552,2 17,2 L1,2 Z M2,14 L2,4 L16,4 L16,14 L2,14 L2,14 Z"></path>
</g>
</svg> \ No newline at end of file
diff --git a/src/sprite/icon-captions-on.svg b/src/sprite/icon-captions-on.svg
index 1ed58de9..9053a31c 100644
--- a/src/sprite/icon-captions-on.svg
+++ b/src/sprite/icon-captions-on.svg
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
- <defs></defs>
- <g id="Page-1" stroke="none" stroke-width="1" sketch:type="MSPage">
- <path d="M1,2 C0.448,2 0,2.448 0,3 L0,15 C0,15.552 0.448,16 1,16 L17,16 C17.552,16 18,15.552 18,15 L18,3 C18,2.448 17.552,2 17,2 L1,2 Z M2,14 L2,4 L16,4 L16,14 L2,14 L2,14 Z" id="Shape" sketch:type="MSShapeGroup"></path>
- <rect id="Rectangle-1" sketch:type="MSShapeGroup" x="3" y="11" width="3" height="2"></rect>
- <rect id="Rectangle-3" sketch:type="MSShapeGroup" x="12" y="11" width="3" height="2"></rect>
- <rect id="Rectangle-2" sketch:type="MSShapeGroup" x="7" y="11" width="4" height="2"></rect>
+<svg viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <title>Captions On</title>
+ <g>
+ <path d="M1,2 C0.448,2 0,2.448 0,3 L0,15 C0,15.552 0.448,16 1,16 L17,16 C17.552,16 18,15.552 18,15 L18,3 C18,2.448 17.552,2 17,2 L1,2 Z M2,14 L2,4 L16,4 L16,14 L2,14 L2,14 Z"></path>
+ <rect x="3" y="11" width="3" height="2"></rect>
+ <rect x="12" y="11" width="3" height="2"></rect>
+ <rect x="7" y="11" width="4" height="2"></rect>
</g>
</svg> \ No newline at end of file
diff --git a/src/sprite/icon-enter-fullscreen.svg b/src/sprite/icon-enter-fullscreen.svg
index 13434bb1..200e44e0 100644
--- a/src/sprite/icon-enter-fullscreen.svg
+++ b/src/sprite/icon-enter-fullscreen.svg
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
- <defs></defs>
- <g id="Page-1" stroke="none" stroke-width="1" sketch:type="MSPage">
- <g id="expand" sketch:type="MSLayerGroup" transform="translate(-1.000000, -1.000000)">
- <path d="M7.00325,17.01 L7.00325,13.377 L8.29625,14.694 C8.68825,15.082 9.32125,15.082 9.71325,14.694 C10.10525,14.306 10.10425,13.678 9.71325,13.291 L6.74625,10.291 C6.55825,10.105 6.30425,10 6.03725,10 C6.02625,10 6.01425,10 6.00325,10.001 C5.99225,10.002 5.98025,10 5.96925,10 C5.70325,10 5.44825,10.105 5.26025,10.291 L2.29325,13.291 C1.90225,13.679 1.90225,14.307 2.29325,14.694 C2.68425,15.081 3.31825,15.082 3.71025,14.694 L5.00425,13.377 L5.00425,17.01 C5.00425,17.557 5.44725,18 5.99425,18 L6.01225,18 C6.55925,18 7.00225,17.557 7.00225,17.01 L7.00325,17.01 Z" id="Shape" sketch:type="MSShapeGroup" transform="translate(6.003438, 14.000000) rotate(-135.000000) translate(-6.003438, -14.000000) "></path>
- <path d="M15.0066876,5.377 L16.2996876,6.694 C16.6916876,7.082 17.3246876,7.082 17.7166876,6.694 C18.1086876,6.306 18.1076876,5.678 17.7166876,5.291 L14.7496876,2.291 C14.5616876,2.105 14.3076876,2 14.0406876,2 C14.0296876,2 14.0176876,2 14.0066876,2.001 C13.9956876,2.002 13.9836876,2 13.9726876,2 C13.7066876,2 13.4516876,2.105 13.2636876,2.291 L10.2966876,5.291 C9.90568756,5.679 9.90568756,6.307 10.2966876,6.694 C10.6876876,7.081 11.3216876,7.082 11.7136876,6.694 L13.0076876,5.377 L13.0076876,9.01 C13.0076876,9.557 13.4506876,10 13.9976876,10 C14.5626876,10 15.0056876,9.557 15.0056876,9.01 L15.0066876,5.377 Z" id="Shape-2" sketch:type="MSShapeGroup" transform="translate(14.006875, 6.000000) rotate(45.000000) translate(-14.006875, -6.000000) "></path>
+<svg width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <title>Enter Fullscreen</title>
+ <g>
+ <g transform="translate(9.000000, 9.000000) rotate(-180.000000) translate(-9.000000, -9.000000) translate(0.000000, 2.000000)">
+ <path d="M7.69999981,6.30000001 C7.00064659,5.62264405 6.3,6.3 6.3,6.3 L2,10.6 L2,6 L0,6 L0,13 C0,13.6 0.4,14 1,14 L8,14 L8,12 L3.4,12 L7.7,7.7 C7.7,7.7 8.39935303,6.97735597 7.69999981,6.30000001 Z"></path>
+ <path d="M11,14 L11,12 L16,12 L16,2 L2,2 L2,3 L0,3 L0,1 C0,0.4 0.4,0 1,0 L17,0 C17.6,0 18,0.4 18,1 L18,13 C18,13.6 17.6,14 17,14 L11,14 Z"></path>
</g>
</g>
</svg> \ No newline at end of file
diff --git a/src/sprite/icon-exit-fullscreen.svg b/src/sprite/icon-exit-fullscreen.svg
index ff4269bc..3c6f31e7 100644
--- a/src/sprite/icon-exit-fullscreen.svg
+++ b/src/sprite/icon-exit-fullscreen.svg
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
- <defs></defs>
- <g id="Page-1" stroke="none" stroke-width="1">
- <g id="collapse" sketch:type="MSLayerGroup" transform="translate(-1.000000, -1.000000)">
- <path d="M15.00325,9.01 L15.00325,5.377 L16.29625,6.694 C16.68825,7.082 17.32125,7.082 17.71325,6.694 C18.10525,6.306 18.10425,5.678 17.71325,5.291 L14.74625,2.291 C14.55825,2.105 14.30425,2 14.03725,2 C14.02625,2 14.01425,2 14.00325,2.001 C13.99225,2.002 13.98025,2 13.96925,2 C13.70325,2 13.44825,2.105 13.26025,2.291 L10.29325,5.291 C9.90225,5.679 9.90225,6.307 10.29325,6.694 C10.68425,7.081 11.31825,7.082 11.71025,6.694 L13.00425,5.377 L13.00425,9.01 C13.00425,9.557 13.44725,10 13.99425,10 L14.01225,10 C14.55925,10 15.00225,9.557 15.00225,9.01 L15.00325,9.01 Z" id="Shape" sketch:type="MSShapeGroup" transform="translate(14.003438, 6.000000) rotate(-135.000000) translate(-14.003438, -6.000000) "></path>
- <path d="M7.00668756,13.377 L8.29968756,14.694 C8.69168756,15.082 9.32468756,15.082 9.71668756,14.694 C10.1086876,14.306 10.1076876,13.678 9.71668756,13.291 L6.74968756,10.291 C6.56168756,10.105 6.30768756,10 6.04068756,10 C6.02968756,10 6.01768756,10 6.00668756,10.001 C5.99568756,10.002 5.98368756,10 5.97268756,10 C5.70668756,10 5.45168756,10.105 5.26368756,10.291 L2.29668756,13.291 C1.90568756,13.679 1.90568756,14.307 2.29668756,14.694 C2.68768756,15.081 3.32168756,15.082 3.71368756,14.694 L5.00768756,13.377 L5.00768756,17.01 C5.00768756,17.557 5.45068756,18 5.99768756,18 C6.56268756,18 7.00568756,17.557 7.00568756,17.01 L7.00668756,13.377 Z" id="Shape-2" sketch:type="MSShapeGroup" transform="translate(6.006875, 14.000000) rotate(45.000000) translate(-6.006875, -14.000000) "></path>
+<svg width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <title>Exit Fullscreen</title>
+ <g>
+ <g transform="translate(0.000000, 2.000000)">
+ <path d="M7.69999981,6.30000001 C7.00064659,5.62264405 6.3,6.3 6.3,6.3 L2,10.6 L2,6 L0,6 L0,13 C0,13.6 0.4,14 1,14 L8,14 L8,12 L3.4,12 L7.7,7.7 C7.7,7.7 8.39935303,6.97735597 7.69999981,6.30000001 Z"></path>
+ <path d="M11,14 L11,12 L16,12 L16,2 L2,2 L2,3 L0,3 L0,1 C0,0.4 0.4,0 1,0 L17,0 C17.6,0 18,0.4 18,1 L18,13 C18,13.6 17.6,14 17,14 L11,14 Z"></path>
</g>
</g>
</svg> \ No newline at end of file
diff --git a/src/sprite/icon-fast-forward.svg b/src/sprite/icon-fast-forward.svg
index 05b23faa..71d5d138 100755
--- a/src/sprite/icon-fast-forward.svg
+++ b/src/sprite/icon-fast-forward.svg
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<svg viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-<path d="M17.569 8.246l-10.569-6.246c-0.552 0-1 0.448-1 1v1.954l-5-2.954c-0.552 0-1 0.448-1 1v12c0 0.552 0.448 1 1 1l5-2.955v1.955c0 0.552 0.448 1 1 1l10.569-6.246c0.267-0.158 0.431-0.444 0.431-0.754s-0.164-0.597-0.431-0.754zM6 10.722l-4 2.364v-8.172l4 2.364v3.444zM8 13.086v-8.172l6.915 4.086-6.915 4.086z"></path>
+ <title>Fast Forward</title>
+ <path d="M17.569 8.246l-10.569-6.246c-0.552 0-1 0.448-1 1v1.954l-5-2.954c-0.552 0-1 0.448-1 1v12c0 0.552 0.448 1 1 1l5-2.955v1.955c0 0.552 0.448 1 1 1l10.569-6.246c0.267-0.158 0.431-0.444 0.431-0.754s-0.164-0.597-0.431-0.754zM6 10.722l-4 2.364v-8.172l4 2.364v3.444zM8 13.086v-8.172l6.915 4.086-6.915 4.086z"></path>
</svg>
diff --git a/src/sprite/icon-muted.svg b/src/sprite/icon-muted.svg
index 532c513d..6d017d02 100644
--- a/src/sprite/icon-muted.svg
+++ b/src/sprite/icon-muted.svg
@@ -1,10 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
- <g id="Page-1" stroke="none" stroke-width="1" sketch:type="MSPage">
- <g id="sound" sketch:type="MSLayerGroup" transform="translate(0.000000, 2.000000)">
- <path d="M9.214,0 C9.103,0 8.989,0.032 8.88,0.101 L4.832,2.911 C4.749,2.969 4.65,3 4.549,3 L0.996,3 C0.446,3 1.33226763e-15,3.448 1.33226763e-15,4 L1.33226763e-15,10 C1.33226763e-15,10.552 0.446,11 0.996,11 L4.549,11 C4.651,11 4.749,11.031 4.832,11.089 L8.88,13.899 C8.989,13.968 9.103,14 9.214,14 C9.606,14 9.961,13.6 9.961,13.051 L9.961,0.95 C9.961,0.4 9.606,0.001 9.214,0.001 L9.214,0 Z M7.969,10.834 L5.582,9.177 C5.416,9.062 5.218,8.999 5.016,8.999 L2.491,8.999 C2.216,8.999 1.993,8.775 1.993,8.499 L1.993,5.499 C1.993,5.223 2.216,4.999 2.491,4.999 L5.016,4.999 C5.218,4.999 5.416,4.937 5.582,4.821 L7.969,3.164 L7.969,10.833 L7.969,10.834 Z" id="Shape" sketch:type="MSShapeGroup"></path>
- <path d="M14.934,6.799 C14.848,5.051 13.42,3.808 12.427,3.15 C11.957,2.838 11.333,3.028 11.102,3.558 L11.064,3.644 C10.876,4.075 11.019,4.583 11.4,4.838 C12.106,5.311 12.986,6.085 13.024,6.903 C13.056,7.579 12.471,8.371 11.361,9.173 C10.963,9.461 10.832,10.012 11.076,10.448 L11.118,10.523 C11.384,10.998 11.984,11.147 12.418,10.835 C14.158,9.584 15.004,8.229 14.934,6.798 L14.934,6.799 Z" id="Shape" sketch:type="MSShapeGroup"></path>
- <path d="M17.934,6.799 C17.848,5.051 16.42,3.808 15.427,3.15 C14.957,2.838 14.333,3.028 14.102,3.558 L14.064,3.644 C13.876,4.075 14.019,4.583 14.4,4.838 C15.106,5.311 15.986,6.085 16.024,6.903 C16.056,7.579 15.471,8.371 14.361,9.173 C13.963,9.461 13.832,10.012 14.076,10.448 L14.118,10.523 C14.384,10.998 14.984,11.147 15.418,10.835 C17.158,9.584 18.004,8.229 17.934,6.798 L17.934,6.799 Z" id="Shape-2" sketch:type="MSShapeGroup" transform="translate(15.945467, 6.999165) rotate(-180.000000) translate(-15.945467, -6.999165) "></path>
- </g>
+<svg viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <title>Muted</title>
+ <g transform="translate(0.000000, 2.000000)">
+ <path d="M9.214,0 C9.103,0 8.989,0.032 8.88,0.101 L4.832,2.911 C4.749,2.969 4.65,3 4.549,3 L0.996,3 C0.446,3 1.33226763e-15,3.448 1.33226763e-15,4 L1.33226763e-15,10 C1.33226763e-15,10.552 0.446,11 0.996,11 L4.549,11 C4.651,11 4.749,11.031 4.832,11.089 L8.88,13.899 C8.989,13.968 9.103,14 9.214,14 C9.606,14 9.961,13.6 9.961,13.051 L9.961,0.95 C9.961,0.4 9.606,0.001 9.214,0.001 L9.214,0 Z M7.969,10.834 L5.582,9.177 C5.416,9.062 5.218,8.999 5.016,8.999 L2.491,8.999 C2.216,8.999 1.993,8.775 1.993,8.499 L1.993,5.499 C1.993,5.223 2.216,4.999 2.491,4.999 L5.016,4.999 C5.218,4.999 5.416,4.937 5.582,4.821 L7.969,3.164 L7.969,10.833 L7.969,10.834 Z"></path>
+ <path d="M14.934,6.799 C14.848,5.051 13.42,3.808 12.427,3.15 C11.957,2.838 11.333,3.028 11.102,3.558 L11.064,3.644 C10.876,4.075 11.019,4.583 11.4,4.838 C12.106,5.311 12.986,6.085 13.024,6.903 C13.056,7.579 12.471,8.371 11.361,9.173 C10.963,9.461 10.832,10.012 11.076,10.448 L11.118,10.523 C11.384,10.998 11.984,11.147 12.418,10.835 C14.158,9.584 15.004,8.229 14.934,6.798 L14.934,6.799 Z"></path>
+ <path d="M17.934,6.799 C17.848,5.051 16.42,3.808 15.427,3.15 C14.957,2.838 14.333,3.028 14.102,3.558 L14.064,3.644 C13.876,4.075 14.019,4.583 14.4,4.838 C15.106,5.311 15.986,6.085 16.024,6.903 C16.056,7.579 15.471,8.371 14.361,9.173 C13.963,9.461 13.832,10.012 14.076,10.448 L14.118,10.523 C14.384,10.998 14.984,11.147 15.418,10.835 C17.158,9.584 18.004,8.229 17.934,6.798 L17.934,6.799 Z" transform="translate(15.945467, 6.999165) rotate(-180.000000) translate(-15.945467, -6.999165) "></path>
</g>
</svg> \ No newline at end of file
diff --git a/src/sprite/icon-pause.svg b/src/sprite/icon-pause.svg
index ee25646d..b4ba82e2 100644
--- a/src/sprite/icon-pause.svg
+++ b/src/sprite/icon-pause.svg
@@ -1,10 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
- <defs></defs>
- <g id="Page-1" stroke="none" stroke-width="1" sketch:type="MSPage">
- <g id="pause" sketch:type="MSLayerGroup" transform="translate(2.000000, 2.000000)">
- <path d="M0,2 L0,12 C5.24848613e-17,14 2,14 2,14 L4,14 C4,14 6,14 6,12 C6,11.786438 6,11.572876 6,11 L6,2 C6,3.17446247e-09 4,0 4,0 L2,0 C2,0 0,0 0,2 Z M2,2 L4,2 L4,12 L2,12 L2,2 Z" id="Path-1" sketch:type="MSShapeGroup"></path>
- <path d="M8,2 L8,12 C8,14 10,14 10,14 L12,14 C12,14 14,14 14,12 C14,11.786438 14,11.572876 14,11 L14,2 C14,3.17446247e-09 12,0 12,0 L10,0 C10,0 8,0 8,2 Z M10,2 L12,2 L12,12 L10,12 L10,2 Z" id="Path-2" sketch:type="MSShapeGroup"></path>
- </g>
+<svg viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <title>Pause</title>
+ <g transform="translate(2.000000, 2.000000)">
+ <path d="M0,2 L0,12 C5.24848613e-17,14 2,14 2,14 L4,14 C4,14 6,14 6,12 C6,11.786438 6,11.572876 6,11 L6,2 C6,3.17446247e-09 4,0 4,0 L2,0 C2,0 0,0 0,2 Z M2,2 L4,2 L4,12 L2,12 L2,2 Z"></path>
+ <path d="M8,2 L8,12 C8,14 10,14 10,14 L12,14 C12,14 14,14 14,12 C14,11.786438 14,11.572876 14,11 L14,2 C14,3.17446247e-09 12,0 12,0 L10,0 C10,0 8,0 8,2 Z M10,2 L12,2 L12,12 L10,12 L10,2 Z"></path>
</g>
</svg> \ No newline at end of file
diff --git a/src/sprite/icon-play.svg b/src/sprite/icon-play.svg
index f0057b32..f564b80f 100755
--- a/src/sprite/icon-play.svg
+++ b/src/sprite/icon-play.svg
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<svg viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-<path d="M5 4.914l6.915 4.086-6.915 4.086v-8.172zM4 2c-0.552 0-1 0.448-1 1v12c0 0.552 0.448 1 1 1l10.569-6.246c0.267-0.158 0.431-0.444 0.431-0.754s-0.164-0.597-0.431-0.754l-10.569-6.246z"></path>
+ <title>Play</title>
+ <path d="M5 4.914l6.915 4.086-6.915 4.086v-8.172zM4 2c-0.552 0-1 0.448-1 1v12c0 0.552 0.448 1 1 1l10.569-6.246c0.267-0.158 0.431-0.444 0.431-0.754s-0.164-0.597-0.431-0.754l-10.569-6.246z"></path>
</svg>
diff --git a/src/sprite/icon-restart.svg b/src/sprite/icon-restart.svg
index 43645a5c..1e393763 100644
--- a/src/sprite/icon-restart.svg
+++ b/src/sprite/icon-restart.svg
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
- <g id="Page-1" stroke="none" stroke-width="1" sketch:type="MSPage">
- <path d="M17,2 C16.448,2 16,2.448 16,3 L16,7.318 L7,2 C6.448,2 6,2.448 6,3 L6,4.954 L1,2 C0.448,2 0,2.448 0,3 L0,15 C0,15.552 0.448,16 1,16 L6,13.045 L6,15 C6,15.552 6.448,16 7,16 L16,10.682 L16,15 C16,15.552 16.448,16 17,16 C17.552,16 18,15.552 18,15 L18,3 C18,2.448 17.552,2 17,2 L17,2 Z M6,10.722 L2,13.086 L2,4.914 L6,7.278 L6,10.722 L6,10.722 Z M8,13.086 L8,4.914 L14.915,9 L8,13.086 L8,13.086 Z" id="Shape" sketch:type="MSShapeGroup" transform="translate(9.000000, 9.000000) scale(-1, 1) translate(-9.000000, -9.000000) "></path>
- </g>
+<svg viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <title>Restart</title>
+ <path d="M17,2 C16.448,2 16,2.448 16,3 L16,7.318 L7,2 C6.448,2 6,2.448 6,3 L6,4.954 L1,2 C0.448,2 0,2.448 0,3 L0,15 C0,15.552 0.448,16 1,16 L6,13.045 L6,15 C6,15.552 6.448,16 7,16 L16,10.682 L16,15 C16,15.552 16.448,16 17,16 C17.552,16 18,15.552 18,15 L18,3 C18,2.448 17.552,2 17,2 L17,2 Z M6,10.722 L2,13.086 L2,4.914 L6,7.278 L6,10.722 L6,10.722 Z M8,13.086 L8,4.914 L14.915,9 L8,13.086 L8,13.086 Z" transform="translate(9.000000, 9.000000) scale(-1, 1) translate(-9.000000, -9.000000) "></path>
</svg> \ No newline at end of file
diff --git a/src/sprite/icon-rewind.svg b/src/sprite/icon-rewind.svg
index 5f122c86..b7beaa34 100644
--- a/src/sprite/icon-rewind.svg
+++ b/src/sprite/icon-rewind.svg
@@ -1,7 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg viewBox="0 0 18 21" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
- <defs></defs>
- <g id="Page-1" stroke="none" stroke-width="1" sketch:type="MSPage">
- <path d="M17.569,9.246 L7,3 C6.448,3 6,3.448 6,4 L6,5.954 L1,3 C0.448,3 0,3.448 0,4 L0,16 C0,16.552 0.448,17 1,17 L6,14.045 L6,16 C6,16.552 6.448,17 7,17 L17.569,10.754 C17.836,10.596 18,10.31 18,10 C18,9.69 17.836,9.403 17.569,9.246 L17.569,9.246 Z M6,11.722 L2,14.086 L2,5.914 L6,8.278 L6,11.722 L6,11.722 Z M8,14.086 L8,5.914 L14.915,10 L8,14.086 L8,14.086 Z" id="Shape" sketch:type="MSShapeGroup" transform="translate(9.000000, 10.000000) rotate(-180.000000) translate(-9.000000, -10.000000) "></path>
- </g>
+ <title>Rewind</title>
+ <path d="M17.569,9.246 L7,3 C6.448,3 6,3.448 6,4 L6,5.954 L1,3 C0.448,3 0,3.448 0,4 L0,16 C0,16.552 0.448,17 1,17 L6,14.045 L6,16 C6,16.552 6.448,17 7,17 L17.569,10.754 C17.836,10.596 18,10.31 18,10 C18,9.69 17.836,9.403 17.569,9.246 L17.569,9.246 Z M6,11.722 L2,14.086 L2,5.914 L6,8.278 L6,11.722 L6,11.722 Z M8,14.086 L8,5.914 L14.915,10 L8,14.086 L8,14.086 Z" transform="translate(9.000000, 10.000000) rotate(-180.000000) translate(-9.000000, -10.000000) "></path>
</svg> \ No newline at end of file
diff --git a/src/sprite/icon-volume.svg b/src/sprite/icon-volume.svg
index edcc8867..27d6d809 100755
--- a/src/sprite/icon-volume.svg
+++ b/src/sprite/icon-volume.svg
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<svg viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-<path d="M10.214 2c-0.111 0-0.225 0.032-0.334 0.101l-4.048 2.81c-0.083 0.058-0.182 0.089-0.283 0.089h-3.553c-0.55 0-0.996 0.448-0.996 1v6c0 0.552 0.446 1 0.996 1h3.553c0.102 0 0.2 0.031 0.283 0.089l4.048 2.81c0.109 0.069 0.223 0.101 0.334 0.101 0.392 0 0.747-0.4 0.747-0.949v-12.101c0-0.55-0.355-0.949-0.747-0.949zM8.969 12.834l-2.387-1.657c-0.166-0.115-0.364-0.178-0.566-0.178h-2.525c-0.275 0-0.498-0.224-0.498-0.5v-3c0-0.276 0.223-0.5 0.498-0.5h2.525c0.202 0 0.4-0.062 0.566-0.178l2.387-1.657v7.669z"></path>
-<path d="M16.934 8.799c-0.086-1.748-1.514-2.991-2.507-3.649-0.47-0.312-1.094-0.122-1.325 0.408l-0.038 0.086c-0.188 0.431-0.045 0.939 0.336 1.194 0.706 0.473 1.586 1.247 1.624 2.065 0.032 0.676-0.553 1.468-1.663 2.27-0.398 0.288-0.529 0.839-0.285 1.275l0.042 0.075c0.266 0.475 0.866 0.624 1.3 0.312 1.74-1.251 2.586-2.606 2.516-4.037z"></path>
+ <title>Volume</title>
+ <path d="M10.214 2c-0.111 0-0.225 0.032-0.334 0.101l-4.048 2.81c-0.083 0.058-0.182 0.089-0.283 0.089h-3.553c-0.55 0-0.996 0.448-0.996 1v6c0 0.552 0.446 1 0.996 1h3.553c0.102 0 0.2 0.031 0.283 0.089l4.048 2.81c0.109 0.069 0.223 0.101 0.334 0.101 0.392 0 0.747-0.4 0.747-0.949v-12.101c0-0.55-0.355-0.949-0.747-0.949zM8.969 12.834l-2.387-1.657c-0.166-0.115-0.364-0.178-0.566-0.178h-2.525c-0.275 0-0.498-0.224-0.498-0.5v-3c0-0.276 0.223-0.5 0.498-0.5h2.525c0.202 0 0.4-0.062 0.566-0.178l2.387-1.657v7.669z"></path>
+ <path d="M16.934 8.799c-0.086-1.748-1.514-2.991-2.507-3.649-0.47-0.312-1.094-0.122-1.325 0.408l-0.038 0.086c-0.188 0.431-0.045 0.939 0.336 1.194 0.706 0.473 1.586 1.247 1.624 2.065 0.032 0.676-0.553 1.468-1.663 2.27-0.398 0.288-0.529 0.839-0.285 1.275l0.042 0.075c0.266 0.475 0.866 0.624 1.3 0.312 1.74-1.251 2.586-2.606 2.516-4.037z"></path>
</svg>