diff options
-rw-r--r-- | bower.json | 2 | ||||
-rw-r--r-- | changelog.md | 23 | ||||
-rw-r--r-- | controls.md | 81 | ||||
-rw-r--r-- | dist/css/docs.css | 2 | ||||
-rw-r--r-- | dist/css/plyr.css | 2 | ||||
-rw-r--r-- | dist/js/docs.js | 2 | ||||
-rw-r--r-- | dist/js/plyr.js | 2 | ||||
-rw-r--r-- | dist/js/templates.js | 2 | ||||
-rw-r--r-- | index.html | 33 | ||||
-rw-r--r-- | package.json | 2 | ||||
-rw-r--r-- | readme.md | 97 | ||||
-rw-r--r-- | src/js/docs.js | 13 | ||||
-rw-r--r-- | src/js/plyr.js | 2251 | ||||
-rw-r--r-- | src/less/docs.less | 13 | ||||
-rw-r--r-- | src/less/plyr.less | 4 | ||||
-rw-r--r-- | src/templates/controls.html | 8 |
16 files changed, 1363 insertions, 1174 deletions
@@ -1,6 +1,6 @@ { "name": "plyr", - "version": "1.0.11", + "version": "1.0.16", "description": "A simple HTML5 media player using custom controls", "homepage": "http://plyr.io", "keywords": [ diff --git a/changelog.md b/changelog.md index 6b4f8089..a2f7dab7 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,28 @@ # Changelog +## v1.0.16 +- Aria label is now dynamic + +## v1.0.15 +- Fix for seek time display in controls +- More documentation for controls html + +## v1.0.14 +- Minor change for bootstrap compatibility + +## v1.0.13 +- Minor tweaks + +## v1.0.12 +- Handle native events (Issue #34) + +## v1.0.11 +- Bug fixes for fullscreen mode + +## v1.0.10 +- Bower includes src files now +- Folder re-arrangement + ## v1.0.9 - Added buffer progress bar - Fixed Safari 8 caption track (it needs to be removed from the DOM like in Safari 7) diff --git a/controls.md b/controls.md new file mode 100644 index 00000000..f6bb1420 --- /dev/null +++ b/controls.md @@ -0,0 +1,81 @@ +# Controls HTML + +This is the markup that is rendered for the Plyr controls. The reason it's a seperate option is to allow full customization of markup based on your needs. It's a pet hate of other libraries that use `<a href="#">` or `<span>`s as buttons! + +The default Plyr setup uses a Hogan template. The reason being to allow for localization at a later date. Check out `controls.html` in `/src/templates` to get an idea of how the default html is structured. Alternatively just use the vanilla HTML below. + +## Requirements + +The classes and data attributes used in your template should match the `selectors` option. + +You need to add several placeholders to your html template that are replaced when rendering: + +- `{id}` - the dynamically generated ID for the player (for form controls) +- `{seektime}` - the seek time specified in options for fast forward and rewind + +Currently all buttons and inputs need to be present for Plyr to work but later we'll make it more dynamic so if you omit a button or input, it'll still work. + +## Vanilla HTML template + +You can of course, just specify vanilla HTML. Here's an example snippet: + +```html +var controls = [ +'<div class="player-controls">', + '<div class="player-progress">', + '<progress class="player-progress-played" max="100" value="0">', + '<span>0</span>% played', + '</progress>', + '<progress class="player-progress-buffer" max="100" value="0">', + '<span>0</span>% buffered', + '</progress>', + '</div>', + '<span class="player-controls-playback">', + '<button type="button" data-player="restart">', + '<svg><use xlink:href="#icon-refresh"></use></svg>', + '<span class="sr-only">Restart</span>', + '</button>', + '<button type="button" data-player="rewind">', + '<svg><use xlink:href="#icon-rewind"></use></svg>', + '<span class="sr-only">Rewind <span class="player-seek-time">{seektime}</span> seconds</span>', + '</button>', + '<button type="button" data-player="play">', + '<svg><use xlink:href="#icon-play"></use></svg>', + '<span class="sr-only">Play</span>', + '</button>', + '<button type="button" data-player="pause">', + '<svg><use xlink:href="#icon-pause"></use></svg>', + '<span class="sr-only">Pause</span>', + '</button>', + '<button type="button" data-player="fast-forward">', + '<svg><use xlink:href="#icon-fast-forward"></use></svg>', + '<span class="sr-only">Fast forward <span class="player-seek-time">{seektime}</span> seconds</span>', + '</button>', + '<span class="player-time">', + '<span class="sr-only">Time</span>', + '<span class="player-duration">00:00</span>', + '</span>', + '</span>', + '<span class="player-controls-sound">', + '<input class="inverted sr-only" id="mute{id}" type="checkbox" data-player="mute">', + '<label id="mute{id}" for="mute{id}">', + '<svg class="icon-muted"><use xlink:href="#icon-muted"></use></svg>', + '<svg><use xlink:href="#icon-sound"></use></svg>', + '<span class="sr-only">Mute</span>', + '</label>', + '<label for="volume{id}" class="sr-only">Volume</label>', + '<input id="volume{id}" class="player-volume" type="range" min="0" max="10" value="5" data-player="volume">', + '<input class="sr-only" id="captions{id}" type="checkbox" data-player="captions">', + '<label for="captions{id}">', + '<svg><use xlink:href="#icon-bubble"></use></svg>', + '<span class="sr-only">Captions</span>', + '</label>', + '<button type="button" data-player="fullscreen">', + '<svg class="icon-exit-fullscreen"><use xlink:href="#icon-collapse"></use></svg>', + '<svg><use xlink:href="#icon-expand"></use></svg>', + '<span class="sr-only">Toggle fullscreen</span>', + '</button>', + '</span>', +'</div>' +].join("\n"); +```
\ No newline at end of file diff --git a/dist/css/docs.css b/dist/css/docs.css index 141c880a..4846c937 100644 --- a/dist/css/docs.css +++ b/dist/css/docs.css @@ -1 +1 @@ -/*! normalize.css v2.1.3 | MIT License | git.io/normalize */article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}[hidden],template{display:none}html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}a{background:0 0}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}q{quotes:"\201C" "\201D" "\2018" "\2019"}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:0}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}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}*,::after,::before{box-sizing:border-box}body{font-family:Avenir,"Helvetica Neue",Helvetica,Arial,sans-serif;background:#fff;line-height:1.5;text-align:center;color:#6D797F}h1,h2{letter-spacing:-.025em;color:#2E3C44;margin:0 0 10px;line-height:1.2;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}h1{font-size:64px;font-size:4rem;color:#3498DB}p,small{margin:0 0 20px}small{display:block;padding:0 10px;font-size:14px;font-size:.9rem}header{padding:20px;margin-bottom:20px}header p{font-size:18px;font-size:1.1rem}@media (min-width:560px){header{padding-top:60px;padding-bottom:60px}}section{padding-bottom:20px}@media (min-width:560px){section{padding-bottom:40px}}a{text-decoration:none;color:#3498db;border-bottom:1px solid currentColor;transition:all .3s ease}a:focus,a:hover{color:#000}a:focus{outline:#343f4a dotted thin;outline-offset:1px}.btn{display:inline-block;padding:10px 30px;background:#3498db;border:0;color:#fff;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-weight:600;border-radius:3px}.btn:focus,.btn:hover{color:#fff;background:#258cd1}.example-audio .player{max-width:480px}.example-video .player{max-width:1200px}.example-audio .player,.example-video .player{margin:0 auto 20px}.example-audio .player-fullscreen,.example-audio .player.fullscreen-active,.example-video .player-fullscreen,.example-video .player.fullscreen-active{max-width:none}@font-face{font-family:Avenir;src:url(../../src/fonts/AvenirLTStd-Medium.woff2) format("woff2"),url(../../src/fonts/AvenirLTStd-Medium.woff) format("woff"),url(../../src/fonts/AvenirLTStd-Medium.ttf) format("truetype");font-style:normal;font-weight:400}@font-face{font-family:Avenir;src:url(../../src/fonts/AvenirLTStd-Heavy.woff2) format("woff2"),url(../../src/fonts/AvenirLTStd-Heavy.woff) format("woff"),url(../../src/fonts/AvenirLTStd-Heavy.ttf) format("truetype");font-style:normal;font-weight:600}
\ No newline at end of file +/*! normalize.css v2.1.3 | MIT License | git.io/normalize */article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}[hidden],template{display:none}html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}a{background:0 0}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}q{quotes:"\201C" "\201D" "\2018" "\2019"}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:0}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}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}*,::after,::before{box-sizing:border-box}body{font-family:Avenir,"Helvetica Neue",Helvetica,Arial,sans-serif;background:#fff;line-height:1.5;text-align:center;color:#6D797F}h1,h2{letter-spacing:-.025em;color:#2E3C44;margin:0 0 10px;line-height:1.2;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}h1{font-size:64px;font-size:4rem;color:#3498DB}p,small{margin:0 0 20px}small{display:block;padding:0 10px;font-size:14px;font-size:.9rem}header{padding:20px;margin-bottom:20px}header p{font-size:18px;font-size:1.1rem}@media (min-width:560px){header{padding-top:60px;padding-bottom:60px}}section{padding-bottom:20px}@media (min-width:560px){section{padding-bottom:40px}}a{text-decoration:none;color:#3498db;border-bottom:1px solid currentColor;transition:all .3s ease}a:focus,a:hover{color:#000}a:focus{outline:#343f4a dotted thin;outline-offset:1px}a.logo{border:0}.btn{display:inline-block;padding:10px 30px;background:#3498db;border:0;color:#fff;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-weight:600;border-radius:3px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:hover{color:#fff;background:#258cd1}.example-audio .player{max-width:480px}.example-video .player{max-width:1200px}.example-audio .player,.example-video .player{margin:0 auto 20px}.example-audio .player-fullscreen,.example-audio .player.fullscreen-active,.example-video .player-fullscreen,.example-video .player.fullscreen-active{max-width:none}footer{margin-bottom:20px}footer p{margin-bottom:10px}@font-face{font-family:Avenir;src:url(../../src/fonts/AvenirLTStd-Medium.woff2) format("woff2"),url(../../src/fonts/AvenirLTStd-Medium.woff) format("woff"),url(../../src/fonts/AvenirLTStd-Medium.ttf) format("truetype");font-style:normal;font-weight:400}@font-face{font-family:Avenir;src:url(../../src/fonts/AvenirLTStd-Heavy.woff2) format("woff2"),url(../../src/fonts/AvenirLTStd-Heavy.woff) format("woff"),url(../../src/fonts/AvenirLTStd-Heavy.ttf) format("truetype");font-style:normal;font-weight:600}
\ No newline at end of file diff --git a/dist/css/plyr.css b/dist/css/plyr.css index c3bc7a6b..bcb209d1 100644 --- a/dist/css/plyr.css +++ b/dist/css/plyr.css @@ -1 +1 @@ -.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{position:relative;max-width:100%;min-width:290px;overflow:hidden}.player,.player *,.player ::after,.player ::before{box-sizing:border-box}.player-video-wrapper{position:relative}.player video{width:100%;height:auto;vertical-align:middle}.player-captions{display:none;position:absolute;bottom:0;left:0;width:100%;padding:20px;min-height:2.5em;color:#fff;font-size:16px;font-weight:600;text-shadow:-1px -1px 0 #565d64,1px -1px 0 #565d64,-1px 1px 0 #565d64,1px 1px 0 #565d64;text-align:center;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}@media (min-width:768px){.player-captions{font-size:24px}}.player.captions-active .player-captions{display:block}.player-controls{zoom:1;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;position:relative;padding:20px 10px 10px;background:#343f4a;line-height:1;text-align:center}.player-controls:after,.player-controls:before{content:"";display:table}.player-controls:after{clear:both}.player-controls-sound{display:block;margin:10px auto 0}@media (min-width:560px){.player-controls-playback{float:left}.player-controls-sound{float:right;margin-top:0}}.player-controls button,.player-controls input+label{display:inline-block;vertical-align:middle;margin:0 2px;padding:5px 10px;transition:background .3s ease;border-radius:3px;cursor:pointer}.player-controls button svg,.player-controls input+label svg{width:18px;height:18px;display:block;fill:currentColor;transition:fill .3s ease}.player-controls input+label,.player-controls input.inverted:checked+label{color:#565d64}.player-controls button,.player-controls input.inverted+label,.player-controls input:checked+label{color:#cbd0d3}.player-controls button{border:0;background:0 0;overflow:hidden}.player-controls button:focus,.player-controls input:focus+label{outline:#000 dotted thin;outline-offset:1px;color:#fff}.player-controls button:hover,.player-controls input+label:hover{background:#3498db;color:#fff}.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:#cbd0d3;font-weight:600;font-size:14px;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}.player-progress{position:absolute;top:0;left:0;right:0;width:100%;height:10px;background:#6e777f}.player-progress-buffer,.player-progress-played{position:absolute;left:0;top:0;width:100%;height:100%;margin:0;vertical-align:top}.player-progress-buffer[value],.player-progress-played[value]{-webkit-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{z-index:2}.player-progress-played[value]{cursor:pointer;color:#3498db}.player-progress-buffer[value]{color:#565d64}.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.muted .player-controls .icon-muted{display:block}.player.muted .player-controls .icon-muted+svg{display:none}.player-volume{vertical-align:middle;-webkit-appearance:none;-moz-appearance:none;width:100px;margin:0 10px 0 0;padding:0;cursor:pointer;background:0 0}.player-volume::-webkit-slider-runnable-track{height:6px;background:#565d64;border:0;border-radius:3px}.player-volume::-webkit-slider-thumb{-webkit-appearance:none;margin-top:-3px;height:12px;width:12px;background:#cbd0d3;border:0;border-radius:6px;transition:background .3s ease;cursor:ew-resize}.player-volume::-moz-range-track{height:6px;background:#565d64;border:0;border-radius:3px}.player-volume::-moz-range-thumb{height:12px;width:12px;background:#cbd0d3;border:0;border-radius:6px;transition:background .3s ease;cursor:ew-resize}.player-volume::-ms-track{height:6px;background:0 0;border-color:transparent;border-width:3px 0;color:transparent}.player-volume::-ms-fill-lower,.player-volume::-ms-fill-upper{height:6px;background:#565d64;border:0;border-radius:3px}.player-volume::-ms-thumb{height:12px;width:12px;background:#cbd0d3;border:0;border-radius:6px;transition:background .3s ease;cursor:ew-resize}.player-volume:focus{outline:0}.player-volume:focus::-webkit-slider-thumb{background:#3498db}.player-volume:focus::-moz-range-thumb{background:#3498db}.player-volume:focus::-ms-thumb{background:#3498db}.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 .player-video-wrapper,.player.fullscreen-active .player-video-wrapper{height:100%;width:100%}.player-fullscreen .player-video-wrapper video,.player.fullscreen-active .player-video-wrapper video{height:100%}.player-fullscreen .player-video-wrapper .player-captions,.player.fullscreen-active .player-video-wrapper .player-captions{top:auto;bottom:90px}@media (min-width:560px) and (max-width:767px){.player-fullscreen .player-video-wrapper .player-captions,.player.fullscreen-active .player-video-wrapper .player-captions{bottom:60px}}@media (min-width:768px){.player-fullscreen .player-video-wrapper .player-captions,.player.fullscreen-active .player-video-wrapper .player-captions{bottom:80px}}.player-fullscreen .player-controls,.player.fullscreen-active .player-controls{position:absolute;bottom:0;left:0;right:0}.player.fullscreen-active .icon-exit-fullscreen{display:block}.player [data-player=captions],.player [data-player=captions]+label,.player [data-player=fullscreen],.player [data-player=fullscreen]+label,.player.fullscreen-active .icon-exit-fullscreen+svg{display:none}.player.captions-enabled [data-player=captions],.player.captions-enabled [data-player=captions]+label,.player.fullscreen-enabled [data-player=fullscreen],.player.fullscreen-enabled [data-player=fullscreen]+label{display:inline-block}.player-fullscreen [data-player=fullscreen],.player-fullscreen [data-player=fullscreen]+label{display:none!important}
\ No newline at end of file +.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{position:relative;max-width:100%;min-width:290px;overflow:hidden}.player,.player *,.player ::after,.player ::before{box-sizing:border-box}.player-video-wrapper{position:relative}.player video{width:100%;height:auto;vertical-align:middle}.player-captions{display:none;position:absolute;bottom:0;left:0;width:100%;padding:20px;min-height:2.5em;color:#fff;font-size:16px;font-weight:600;text-shadow:-1px -1px 0 #565d64,1px -1px 0 #565d64,-1px 1px 0 #565d64,1px 1px 0 #565d64;text-align:center;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}@media (min-width:768px){.player-captions{font-size:24px}}.player.captions-active .player-captions{display:block}.player-controls{zoom:1;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;position:relative;padding:20px 10px 10px;background:#343f4a;line-height:1;text-align:center}.player-controls:after,.player-controls:before{content:"";display:table}.player-controls:after{clear:both}.player-controls-sound{display:block;margin:10px auto 0}@media (min-width:560px){.player-controls-playback{float:left}.player-controls-sound{float:right;margin-top:0}}.player-controls button,.player-controls input+label{display:inline-block;vertical-align:middle;margin:0 2px;padding:5px 10px;transition:background .3s ease;border-radius:3px;cursor:pointer}.player-controls button svg,.player-controls input+label svg{width:18px;height:18px;display:block;fill:currentColor;transition:fill .3s ease}.player-controls input+label,.player-controls input.inverted:checked+label{color:#565d64}.player-controls button,.player-controls input.inverted+label,.player-controls input:checked+label{color:#cbd0d3}.player-controls button{border:0;background:0 0;overflow:hidden}.player-controls button:focus,.player-controls input:focus+label{outline:#000 dotted thin;outline-offset:1px;color:#fff}.player-controls button:hover,.player-controls input+label:hover{background:#3498db;color:#fff}.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:#cbd0d3;font-weight:600;font-size:14px;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}.player-progress{position:absolute;top:0;left:0;right:0;width:100%;height:10px;background:#6e777f}.player-progress-buffer,.player-progress-played{position:absolute;left:0;top:0;width:100%;height:100%;margin:0;vertical-align:top}.player-progress-buffer[value],.player-progress-played[value]{-webkit-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{z-index:2}.player-progress-played[value]{cursor:pointer;color:#3498db}.player-progress-buffer[value]{color:#565d64}.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.muted .player-controls .icon-muted{display:block}.player.muted .player-controls .icon-muted+svg{display:none}.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}.player-volume[type=range]::-webkit-slider-runnable-track{height:6px;background:#565d64;border:0;border-radius:3px}.player-volume[type=range]::-webkit-slider-thumb{-webkit-appearance:none;margin-top:-3px;height:12px;width:12px;background:#cbd0d3;border:0;border-radius:6px;transition:background .3s ease;cursor:ew-resize}.player-volume[type=range]::-moz-range-track{height:6px;background:#565d64;border:0;border-radius:3px}.player-volume[type=range]::-moz-range-thumb{height:12px;width:12px;background:#cbd0d3;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:#565d64;border:0;border-radius:3px}.player-volume[type=range]::-ms-thumb{height:12px;width:12px;background:#cbd0d3;border:0;border-radius:6px;transition:background .3s ease;cursor:ew-resize}.player-volume[type=range]:focus{outline:0}.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-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 .player-video-wrapper,.player.fullscreen-active .player-video-wrapper{height:100%;width:100%}.player-fullscreen .player-video-wrapper video,.player.fullscreen-active .player-video-wrapper video{height:100%}.player-fullscreen .player-video-wrapper .player-captions,.player.fullscreen-active .player-video-wrapper .player-captions{top:auto;bottom:90px}@media (min-width:560px) and (max-width:767px){.player-fullscreen .player-video-wrapper .player-captions,.player.fullscreen-active .player-video-wrapper .player-captions{bottom:60px}}@media (min-width:768px){.player-fullscreen .player-video-wrapper .player-captions,.player.fullscreen-active .player-video-wrapper .player-captions{bottom:80px}}.player-fullscreen .player-controls,.player.fullscreen-active .player-controls{position:absolute;bottom:0;left:0;right:0}.player.fullscreen-active .icon-exit-fullscreen{display:block}.player [data-player=captions],.player [data-player=captions]+label,.player [data-player=fullscreen],.player [data-player=fullscreen]+label,.player.fullscreen-active .icon-exit-fullscreen+svg{display:none}.player.captions-enabled [data-player=captions],.player.captions-enabled [data-player=captions]+label,.player.fullscreen-enabled [data-player=fullscreen],.player.fullscreen-enabled [data-player=fullscreen]+label{display:inline-block}.player-fullscreen [data-player=fullscreen],.player-fullscreen [data-player=fullscreen]+label{display:none!important}
\ No newline at end of file diff --git a/dist/js/docs.js b/dist/js/docs.js index f7465449..7c72517b 100644 --- a/dist/js/docs.js +++ b/dist/js/docs.js @@ -1 +1 @@ -var Hogan={};!function(t){function n(t,n,e){var s;return n&&"object"==typeof n&&(void 0!==n[t]?s=n[t]:e&&n.get&&"function"==typeof n.get&&(s=n.get(t))),s}function e(t,n,e,s,r,a){function i(){}function o(){}i.prototype=t,o.prototype=t.subs;var l,u=new i;u.subs=new o,u.subsText={},u.buf="",s=s||{},u.stackSubs=s,u.subsText=a;for(l in n)s[l]||(s[l]=n[l]);for(l in s)u.subs[l]=s[l];r=r||{},u.stackPartials=r;for(l in e)r[l]||(r[l]=e[l]);for(l in r)u.partials[l]=r[l];return u}function s(t){return String(null===t||void 0===t?"":t)}function r(t){return t=s(t),c.test(t)?t.replace(a,"&").replace(i,"<").replace(o,">").replace(l,"'").replace(u,"""):t}t.Template=function(t,n,e,s){t=t||{},this.r=t.code||this.r,this.c=e,this.options=s||{},this.text=n||"",this.partials=t.partials||{},this.subs=t.subs||{},this.buf=""},t.Template.prototype={r:function(){return""},v:r,t:s,render:function(t,n,e){return this.ri([t],n||{},e)},ri:function(t,n,e){return this.r(t,n,e)},ep:function(t,n){var s=this.partials[t],r=n[s.name];if(s.instance&&s.base==r)return s.instance;if("string"==typeof r){if(!this.c)throw new Error("No compiler available.");r=this.c.compile(r,this.options)}if(!r)return null;if(this.partials[t].base=r,s.subs){n.stackText||(n.stackText={});for(key in s.subs)n.stackText[key]||(n.stackText[key]=void 0!==this.activeSub&&n.stackText[this.activeSub]?n.stackText[this.activeSub]:this.text);r=e(r,s.subs,s.partials,this.stackSubs,this.stackPartials,n.stackText)}return this.partials[t].instance=r,r},rp:function(t,n,e,s){var r=this.ep(t,e);return r?r.ri(n,e,s):""},rs:function(t,n,e){var s=t[t.length-1];if(!p(s))return e(t,n,this),void 0;for(var r=0;r<s.length;r++)t.push(s[r]),e(t,n,this),t.pop()},s:function(t,n,e,s,r,a,i){var o;return p(t)&&0===t.length?!1:("function"==typeof t&&(t=this.ms(t,n,e,s,r,a,i)),o=!!t,!s&&o&&n&&n.push("object"==typeof t?t:n[n.length-1]),o)},d:function(t,e,s,r){var a,i=t.split("."),o=this.f(i[0],e,s,r),l=this.options.modelGet,u=null;if("."===t&&p(e[e.length-2]))o=e[e.length-1];else for(var c=1;c<i.length;c++)a=n(i[c],o,l),void 0!==a?(u=o,o=a):o="";return r&&!o?!1:(r||"function"!=typeof o||(e.push(u),o=this.mv(o,e,s),e.pop()),o)},f:function(t,e,s,r){for(var a=!1,i=null,o=!1,l=this.options.modelGet,u=e.length-1;u>=0;u--)if(i=e[u],a=n(t,i,l),void 0!==a){o=!0;break}return o?(r||"function"!=typeof a||(a=this.mv(a,e,s)),a):r?!1:""},ls:function(t,n,e,r,a){var i=this.options.delimiters;return this.options.delimiters=a,this.b(this.ct(s(t.call(n,r)),n,e)),this.options.delimiters=i,!1},ct:function(t,n,e){if(this.options.disableLambda)throw new Error("Lambda features disabled.");return this.c.compile(t,this.options).render(n,e)},b:function(t){this.buf+=t},fl:function(){var t=this.buf;return this.buf="",t},ms:function(t,n,e,s,r,a,i){var o,l=n[n.length-1],u=t.call(l);return"function"==typeof u?s?!0:(o=this.activeSub&&this.subsText&&this.subsText[this.activeSub]?this.subsText[this.activeSub]:this.text,this.ls(u,l,e,o.substring(r,a),i)):u},mv:function(t,n,e){var r=n[n.length-1],a=t.call(r);return"function"==typeof a?this.ct(s(a.call(r)),r,e):a},sub:function(t,n,e,s){var r=this.subs[t];r&&(this.activeSub=t,r(n,e,this,s),this.activeSub=!1)}};var a=/&/g,i=/</g,o=/>/g,l=/\'/g,u=/\"/g,c=/[&<>\"\']/,p=Array.isArray||function(t){return"[object Array]"===Object.prototype.toString.call(t)}}("undefined"!=typeof exports?exports:Hogan),function(t){function n(t){"}"===t.n.substr(t.n.length-1)&&(t.n=t.n.substring(0,t.n.length-1))}function e(t){return t.trim?t.trim():t.replace(/^\s*|\s*$/g,"")}function s(t,n,e){if(n.charAt(e)!=t.charAt(0))return!1;for(var s=1,r=t.length;r>s;s++)if(n.charAt(e+s)!=t.charAt(s))return!1;return!0}function r(n,e,s,o){var l=[],u=null,c=null,p=null;for(c=s[s.length-1];n.length>0;){if(p=n.shift(),c&&"<"==c.tag&&!(p.tag in k))throw new Error("Illegal content in < super tag.");if(t.tags[p.tag]<=t.tags.$||a(p,o))s.push(p),p.nodes=r(n,p.tag,s,o);else{if("/"==p.tag){if(0===s.length)throw new Error("Closing tag without opener: /"+p.n);if(u=s.pop(),p.n!=u.n&&!i(p.n,u.n,o))throw new Error("Nesting error: "+u.n+" vs. "+p.n);return u.end=p.i,l}"\n"==p.tag&&(p.last=0==n.length||"\n"==n[0].tag)}l.push(p)}if(s.length>0)throw new Error("missing closing tag: "+s.pop().n);return l}function a(t,n){for(var e=0,s=n.length;s>e;e++)if(n[e].o==t.n)return t.tag="#",!0}function i(t,n,e){for(var s=0,r=e.length;r>s;s++)if(e[s].c==t&&e[s].o==n)return!0}function o(t){var n=[];for(var e in t)n.push('"'+u(e)+'": function(c,p,t,i) {'+t[e]+"}");return"{ "+n.join(",")+" }"}function l(t){var n=[];for(var e in t.partials)n.push('"'+u(e)+'":{name:"'+u(t.partials[e].name)+'", '+l(t.partials[e])+"}");return"partials: {"+n.join(",")+"}, subs: "+o(t.subs)}function u(t){return t.replace(y,"\\\\").replace(g,'\\"').replace(d,"\\n").replace(v,"\\r").replace(m,"\\u2028").replace(x,"\\u2029")}function c(t){return~t.indexOf(".")?"d":"f"}function p(t,n){var e="<"+(n.prefix||""),s=e+t.n+w++;return n.partials[s]={name:t.n,partials:{}},n.code+='t.b(t.rp("'+u(s)+'",c,p,"'+(t.indent||"")+'"));',s}function b(t,n){n.code+="t.b(t.t(t."+c(t.n)+'("'+u(t.n)+'",c,p,0)));'}function f(t){return"t.b("+t+");"}var h=/\S/,g=/\"/g,d=/\n/g,v=/\r/g,y=/\\/g,m=/\u2028/,x=/\u2029/;t.tags={"#":1,"^":2,"<":3,$:4,"/":5,"!":6,">":7,"=":8,_v:9,"{":10,"&":11,_t:12},t.scan=function(r,a){function i(){y.length>0&&(m.push({tag:"_t",text:new String(y)}),y="")}function o(){for(var n=!0,e=w;e<m.length;e++)if(n=t.tags[m[e].tag]<t.tags._v||"_t"==m[e].tag&&null===m[e].text.match(h),!n)return!1;return n}function l(t,n){if(i(),t&&o())for(var e,s=w;s<m.length;s++)m[s].text&&((e=m[s+1])&&">"==e.tag&&(e.indent=m[s].text.toString()),m.splice(s,1));else n||m.push({tag:"\n"});x=!1,w=m.length}function u(t,n){var s="="+S,r=t.indexOf(s,n),a=e(t.substring(t.indexOf("=",n)+1,r)).split(" ");return T=a[0],S=a[a.length-1],r+s.length-1}var c=r.length,p=0,b=1,f=2,g=p,d=null,v=null,y="",m=[],x=!1,k=0,w=0,T="{{",S="}}";for(a&&(a=a.split(" "),T=a[0],S=a[1]),k=0;c>k;k++)g==p?s(T,r,k)?(--k,i(),g=b):"\n"==r.charAt(k)?l(x):y+=r.charAt(k):g==b?(k+=T.length-1,v=t.tags[r.charAt(k+1)],d=v?r.charAt(k+1):"_v","="==d?(k=u(r,k),g=p):(v&&k++,g=f),x=k):s(S,r,k)?(m.push({tag:d,n:e(y),otag:T,ctag:S,i:"/"==d?x-T.length:k+S.length}),y="",k+=S.length-1,g=p,"{"==d&&("}}"==S?k++:n(m[m.length-1]))):y+=r.charAt(k);return l(x,!0),m};var k={_t:!0,"\n":!0,$:!0,"/":!0};t.stringify=function(n){return"{code: function (c,p,i) { "+t.wrapMain(n.code)+" },"+l(n)+"}"};var w=0;t.generate=function(n,e,s){w=0;var r={code:"",subs:{},partials:{}};return t.walk(n,r),s.asString?this.stringify(r,e,s):this.makeTemplate(r,e,s)},t.wrapMain=function(t){return'var t=this;t.b(i=i||"");'+t+"return t.fl();"},t.template=t.Template,t.makeTemplate=function(t,n,e){var s=this.makePartials(t);return s.code=new Function("c","p","i",this.wrapMain(t.code)),new this.template(s,n,this,e)},t.makePartials=function(t){var n,e={subs:{},partials:t.partials,name:t.name};for(n in e.partials)e.partials[n]=this.makePartials(e.partials[n]);for(n in t.subs)e.subs[n]=new Function("c","p","t","i",t.subs[n]);return e},t.codegen={"#":function(n,e){e.code+="if(t.s(t."+c(n.n)+'("'+u(n.n)+'",c,p,1),c,p,0,'+n.i+","+n.end+',"'+n.otag+" "+n.ctag+'")){t.rs(c,p,function(c,p,t){',t.walk(n.nodes,e),e.code+="});c.pop();}"},"^":function(n,e){e.code+="if(!t.s(t."+c(n.n)+'("'+u(n.n)+'",c,p,1),c,p,1,0,0,"")){',t.walk(n.nodes,e),e.code+="};"},">":p,"<":function(n,e){var s={partials:{},code:"",subs:{},inPartial:!0};t.walk(n.nodes,s);var r=e.partials[p(n,e)];r.subs=s.subs,r.partials=s.partials},$:function(n,e){var s={subs:{},code:"",partials:e.partials,prefix:n.n};t.walk(n.nodes,s),e.subs[n.n]=s.code,e.inPartial||(e.code+='t.sub("'+u(n.n)+'",c,p,i);')},"\n":function(t,n){n.code+=f('"\\n"'+(t.last?"":" + i"))},_v:function(t,n){n.code+="t.b(t.v(t."+c(t.n)+'("'+u(t.n)+'",c,p,0)));'},_t:function(t,n){n.code+=f('"'+u(t.text)+'"')},"{":b,"&":b},t.walk=function(n,e){for(var s,r=0,a=n.length;a>r;r++)s=t.codegen[n[r].tag],s&&s(n[r],e);return e},t.parse=function(t,n,e){return e=e||{},r(t,"",[],e.sectionTags||[])},t.cache={},t.cacheKey=function(t,n){return[t,!!n.asString,!!n.disableLambda,n.delimiters,!!n.modelGet].join("||")},t.compile=function(n,e){e=e||{};var s=t.cacheKey(n,e),r=this.cache[s];if(r){var a=r.partials;for(var i in a)delete a[i].instance;return r}return r=this.generate(this.parse(this.scan(n,e.delimiters),n,e),n,e),this.cache[s]=r}}("undefined"!=typeof exports?exports:Hogan);var Mustache=function(t){function n(n,e,s,r){var a=this.f(n,e,s,0),i=e;return a&&(i=i.concat(a)),t.Template.prototype.rp.call(this,n,i,s,r)}var e=function(e,s,r){this.rp=n,t.Template.call(this,e,s,r)};e.prototype=t.Template.prototype;var s,r=function(){this.cache={},this.generate=function(t,n){return new e(new Function("c","p","i",t),n,s)}};return r.prototype=t,s=new r,{to_html:function(t,n,e,r){var a=s.compile(t),i=a.render(n,e);return r?(r(i),void 0):i}}}(Hogan),templates={};templates.controls=new Hogan.Template({code:function(t,n,e){var s=this;return s.b(e=e||""),s.b('<div class="player-controls">'),s.b("\n"+e),s.b(' <div class="player-progress">'),s.b("\n"+e),s.b(' <progress class="player-progress-played" max="100" value="0">'),s.b("\n"+e),s.b(" <span>0</span>% played"),s.b("\n"+e),s.b(" </progress>"),s.b("\n"+e),s.b(' <progress class="player-progress-buffer" max="100" value="0">'),s.b("\n"+e),s.b(" <span>0</span>% buffered"),s.b("\n"+e),s.b(" </progress>"),s.b("\n"+e),s.b(" </div>"),s.b("\n"+e),s.b(' <span class="player-controls-playback">'),s.b("\n"+e),s.b(' <button type="button" data-player="restart">'),s.b("\n"+e),s.b(' <svg><use xlink:href="#icon-refresh"></use></svg>'),s.b("\n"+e),s.b(' <span class="sr-only">Restart</span>'),s.b("\n"+e),s.b(" </button>"),s.b("\n"+e),s.b(' <button type="button" data-player="rewind">'),s.b("\n"+e),s.b(' <svg><use xlink:href="#icon-rewind"></use></svg>'),s.b("\n"+e),s.b(' <span class="sr-only">Rewind <span class="player-seek-time">10</span> seconds</span>'),s.b("\n"+e),s.b(" </button>"),s.b("\n"+e),s.b(' <button type="button" aria-label="{aria-label}" data-player="play">'),s.b("\n"+e),s.b(' <svg><use xlink:href="#icon-play"></use></svg>'),s.b("\n"+e),s.b(' <span class="sr-only">Play</span>'),s.b("\n"+e),s.b(" </button>"),s.b("\n"+e),s.b(' <button type="button" data-player="pause">'),s.b("\n"+e),s.b(' <svg><use xlink:href="#icon-pause"></use></svg>'),s.b("\n"+e),s.b(' <span class="sr-only">Pause</span>'),s.b("\n"+e),s.b(" </button>"),s.b("\n"+e),s.b(' <button type="button" data-player="fast-forward">'),s.b("\n"+e),s.b(' <svg><use xlink:href="#icon-fast-forward"></use></svg>'),s.b("\n"+e),s.b(' <span class="sr-only">Fast forward <span class="player-seek-time">10</span> seconds</span>'),s.b("\n"+e),s.b(" </button>"),s.b("\n"+e),s.b(' <span class="player-time">'),s.b("\n"+e),s.b(' <span class="sr-only">Time</span>'),s.b("\n"+e),s.b(' <span class="player-duration">00:00</span>'),s.b("\n"+e),s.b(" </span>"),s.b("\n"+e),s.b(" </span>"),s.b("\n"+e),s.b(' <span class="player-controls-sound">'),s.b("\n"+e),s.b(' <input class="inverted sr-only" id="mute{id}" type="checkbox" data-player="mute">'),s.b("\n"+e),s.b(' <label id="mute{id}" for="mute{id}">'),s.b("\n"+e),s.b(' <svg class="icon-muted"><use xlink:href="#icon-muted"></use></svg>'),s.b("\n"+e),s.b(' <svg><use xlink:href="#icon-sound"></use></svg>'),s.b("\n"+e),s.b(' <span class="sr-only">Mute</span>'),s.b("\n"+e),s.b(" </label>"),s.b("\n"),s.b("\n"+e),s.b(' <label for="volume{id}" class="sr-only">Volume:</label>'),s.b("\n"+e),s.b(' <input id="volume{id}" class="player-volume" type="range" min="0" max="10" value="5" data-player="volume">'),s.b("\n"),s.b("\n"+e),s.b(' <input class="sr-only" id="captions{id}" type="checkbox" data-player="captions">'),s.b("\n"+e),s.b(' <label for="captions{id}">'),s.b("\n"+e),s.b(' <svg><use xlink:href="#icon-bubble"></use></svg>'),s.b("\n"+e),s.b(' <span class="sr-only">Captions</span>'),s.b("\n"+e),s.b(" </label>"),s.b("\n"),s.b("\n"+e),s.b(' <button type="button" data-player="fullscreen">'),s.b("\n"+e),s.b(' <svg class="icon-exit-fullscreen"><use xlink:href="#icon-collapse"></use></svg>'),s.b("\n"+e),s.b(' <svg><use xlink:href="#icon-expand"></use></svg>'),s.b("\n"+e),s.b(' <span class="sr-only">Toggle fullscreen</span>'),s.b("\n"+e),s.b(" </button>"),s.b("\n"+e),s.b(" </span>"),s.b("\n"+e),s.b("</div>"),s.fl()},partials:{},subs:{}}),plyr.setup({debug:!0,title:"Video demo",html:templates.controls.render({}),captions:{defaultActive:!0}});
\ No newline at end of file +var Hogan={};!function(t){function n(t,n,e){var s;return n&&"object"==typeof n&&(void 0!==n[t]?s=n[t]:e&&n.get&&"function"==typeof n.get&&(s=n.get(t))),s}function e(t,n,e,s,a,r){function i(){}function o(){}i.prototype=t,o.prototype=t.subs;var l,u=new i;u.subs=new o,u.subsText={},u.buf="",s=s||{},u.stackSubs=s,u.subsText=r;for(l in n)s[l]||(s[l]=n[l]);for(l in s)u.subs[l]=s[l];a=a||{},u.stackPartials=a;for(l in e)a[l]||(a[l]=e[l]);for(l in a)u.partials[l]=a[l];return u}function s(t){return String(null===t||void 0===t?"":t)}function a(t){return t=s(t),c.test(t)?t.replace(r,"&").replace(i,"<").replace(o,">").replace(l,"'").replace(u,"""):t}t.Template=function(t,n,e,s){t=t||{},this.r=t.code||this.r,this.c=e,this.options=s||{},this.text=n||"",this.partials=t.partials||{},this.subs=t.subs||{},this.buf=""},t.Template.prototype={r:function(){return""},v:a,t:s,render:function(t,n,e){return this.ri([t],n||{},e)},ri:function(t,n,e){return this.r(t,n,e)},ep:function(t,n){var s=this.partials[t],a=n[s.name];if(s.instance&&s.base==a)return s.instance;if("string"==typeof a){if(!this.c)throw new Error("No compiler available.");a=this.c.compile(a,this.options)}if(!a)return null;if(this.partials[t].base=a,s.subs){n.stackText||(n.stackText={});for(key in s.subs)n.stackText[key]||(n.stackText[key]=void 0!==this.activeSub&&n.stackText[this.activeSub]?n.stackText[this.activeSub]:this.text);a=e(a,s.subs,s.partials,this.stackSubs,this.stackPartials,n.stackText)}return this.partials[t].instance=a,a},rp:function(t,n,e,s){var a=this.ep(t,e);return a?a.ri(n,e,s):""},rs:function(t,n,e){var s=t[t.length-1];if(!p(s))return e(t,n,this),void 0;for(var a=0;a<s.length;a++)t.push(s[a]),e(t,n,this),t.pop()},s:function(t,n,e,s,a,r,i){var o;return p(t)&&0===t.length?!1:("function"==typeof t&&(t=this.ms(t,n,e,s,a,r,i)),o=!!t,!s&&o&&n&&n.push("object"==typeof t?t:n[n.length-1]),o)},d:function(t,e,s,a){var r,i=t.split("."),o=this.f(i[0],e,s,a),l=this.options.modelGet,u=null;if("."===t&&p(e[e.length-2]))o=e[e.length-1];else for(var c=1;c<i.length;c++)r=n(i[c],o,l),void 0!==r?(u=o,o=r):o="";return a&&!o?!1:(a||"function"!=typeof o||(e.push(u),o=this.mv(o,e,s),e.pop()),o)},f:function(t,e,s,a){for(var r=!1,i=null,o=!1,l=this.options.modelGet,u=e.length-1;u>=0;u--)if(i=e[u],r=n(t,i,l),void 0!==r){o=!0;break}return o?(a||"function"!=typeof r||(r=this.mv(r,e,s)),r):a?!1:""},ls:function(t,n,e,a,r){var i=this.options.delimiters;return this.options.delimiters=r,this.b(this.ct(s(t.call(n,a)),n,e)),this.options.delimiters=i,!1},ct:function(t,n,e){if(this.options.disableLambda)throw new Error("Lambda features disabled.");return this.c.compile(t,this.options).render(n,e)},b:function(t){this.buf+=t},fl:function(){var t=this.buf;return this.buf="",t},ms:function(t,n,e,s,a,r,i){var o,l=n[n.length-1],u=t.call(l);return"function"==typeof u?s?!0:(o=this.activeSub&&this.subsText&&this.subsText[this.activeSub]?this.subsText[this.activeSub]:this.text,this.ls(u,l,e,o.substring(a,r),i)):u},mv:function(t,n,e){var a=n[n.length-1],r=t.call(a);return"function"==typeof r?this.ct(s(r.call(a)),a,e):r},sub:function(t,n,e,s){var a=this.subs[t];a&&(this.activeSub=t,a(n,e,this,s),this.activeSub=!1)}};var r=/&/g,i=/</g,o=/>/g,l=/\'/g,u=/\"/g,c=/[&<>\"\']/,p=Array.isArray||function(t){return"[object Array]"===Object.prototype.toString.call(t)}}("undefined"!=typeof exports?exports:Hogan),function(t){function n(t){"}"===t.n.substr(t.n.length-1)&&(t.n=t.n.substring(0,t.n.length-1))}function e(t){return t.trim?t.trim():t.replace(/^\s*|\s*$/g,"")}function s(t,n,e){if(n.charAt(e)!=t.charAt(0))return!1;for(var s=1,a=t.length;a>s;s++)if(n.charAt(e+s)!=t.charAt(s))return!1;return!0}function a(n,e,s,o){var l=[],u=null,c=null,p=null;for(c=s[s.length-1];n.length>0;){if(p=n.shift(),c&&"<"==c.tag&&!(p.tag in w))throw new Error("Illegal content in < super tag.");if(t.tags[p.tag]<=t.tags.$||r(p,o))s.push(p),p.nodes=a(n,p.tag,s,o);else{if("/"==p.tag){if(0===s.length)throw new Error("Closing tag without opener: /"+p.n);if(u=s.pop(),p.n!=u.n&&!i(p.n,u.n,o))throw new Error("Nesting error: "+u.n+" vs. "+p.n);return u.end=p.i,l}"\n"==p.tag&&(p.last=0==n.length||"\n"==n[0].tag)}l.push(p)}if(s.length>0)throw new Error("missing closing tag: "+s.pop().n);return l}function r(t,n){for(var e=0,s=n.length;s>e;e++)if(n[e].o==t.n)return t.tag="#",!0}function i(t,n,e){for(var s=0,a=e.length;a>s;s++)if(e[s].c==t&&e[s].o==n)return!0}function o(t){var n=[];for(var e in t)n.push('"'+u(e)+'": function(c,p,t,i) {'+t[e]+"}");return"{ "+n.join(",")+" }"}function l(t){var n=[];for(var e in t.partials)n.push('"'+u(e)+'":{name:"'+u(t.partials[e].name)+'", '+l(t.partials[e])+"}");return"partials: {"+n.join(",")+"}, subs: "+o(t.subs)}function u(t){return t.replace(m,"\\\\").replace(g,'\\"').replace(d,"\\n").replace(v,"\\r").replace(y,"\\u2028").replace(x,"\\u2029")}function c(t){return~t.indexOf(".")?"d":"f"}function p(t,n){var e="<"+(n.prefix||""),s=e+t.n+k++;return n.partials[s]={name:t.n,partials:{}},n.code+='t.b(t.rp("'+u(s)+'",c,p,"'+(t.indent||"")+'"));',s}function b(t,n){n.code+="t.b(t.t(t."+c(t.n)+'("'+u(t.n)+'",c,p,0)));'}function f(t){return"t.b("+t+");"}var h=/\S/,g=/\"/g,d=/\n/g,v=/\r/g,m=/\\/g,y=/\u2028/,x=/\u2029/;t.tags={"#":1,"^":2,"<":3,$:4,"/":5,"!":6,">":7,"=":8,_v:9,"{":10,"&":11,_t:12},t.scan=function(a,r){function i(){m.length>0&&(y.push({tag:"_t",text:new String(m)}),m="")}function o(){for(var n=!0,e=k;e<y.length;e++)if(n=t.tags[y[e].tag]<t.tags._v||"_t"==y[e].tag&&null===y[e].text.match(h),!n)return!1;return n}function l(t,n){if(i(),t&&o())for(var e,s=k;s<y.length;s++)y[s].text&&((e=y[s+1])&&">"==e.tag&&(e.indent=y[s].text.toString()),y.splice(s,1));else n||y.push({tag:"\n"});x=!1,k=y.length}function u(t,n){var s="="+S,a=t.indexOf(s,n),r=e(t.substring(t.indexOf("=",n)+1,a)).split(" ");return T=r[0],S=r[r.length-1],a+s.length-1}var c=a.length,p=0,b=1,f=2,g=p,d=null,v=null,m="",y=[],x=!1,w=0,k=0,T="{{",S="}}";for(r&&(r=r.split(" "),T=r[0],S=r[1]),w=0;c>w;w++)g==p?s(T,a,w)?(--w,i(),g=b):"\n"==a.charAt(w)?l(x):m+=a.charAt(w):g==b?(w+=T.length-1,v=t.tags[a.charAt(w+1)],d=v?a.charAt(w+1):"_v","="==d?(w=u(a,w),g=p):(v&&w++,g=f),x=w):s(S,a,w)?(y.push({tag:d,n:e(m),otag:T,ctag:S,i:"/"==d?x-T.length:w+S.length}),m="",w+=S.length-1,g=p,"{"==d&&("}}"==S?w++:n(y[y.length-1]))):m+=a.charAt(w);return l(x,!0),y};var w={_t:!0,"\n":!0,$:!0,"/":!0};t.stringify=function(n){return"{code: function (c,p,i) { "+t.wrapMain(n.code)+" },"+l(n)+"}"};var k=0;t.generate=function(n,e,s){k=0;var a={code:"",subs:{},partials:{}};return t.walk(n,a),s.asString?this.stringify(a,e,s):this.makeTemplate(a,e,s)},t.wrapMain=function(t){return'var t=this;t.b(i=i||"");'+t+"return t.fl();"},t.template=t.Template,t.makeTemplate=function(t,n,e){var s=this.makePartials(t);return s.code=new Function("c","p","i",this.wrapMain(t.code)),new this.template(s,n,this,e)},t.makePartials=function(t){var n,e={subs:{},partials:t.partials,name:t.name};for(n in e.partials)e.partials[n]=this.makePartials(e.partials[n]);for(n in t.subs)e.subs[n]=new Function("c","p","t","i",t.subs[n]);return e},t.codegen={"#":function(n,e){e.code+="if(t.s(t."+c(n.n)+'("'+u(n.n)+'",c,p,1),c,p,0,'+n.i+","+n.end+',"'+n.otag+" "+n.ctag+'")){t.rs(c,p,function(c,p,t){',t.walk(n.nodes,e),e.code+="});c.pop();}"},"^":function(n,e){e.code+="if(!t.s(t."+c(n.n)+'("'+u(n.n)+'",c,p,1),c,p,1,0,0,"")){',t.walk(n.nodes,e),e.code+="};"},">":p,"<":function(n,e){var s={partials:{},code:"",subs:{},inPartial:!0};t.walk(n.nodes,s);var a=e.partials[p(n,e)];a.subs=s.subs,a.partials=s.partials},$:function(n,e){var s={subs:{},code:"",partials:e.partials,prefix:n.n};t.walk(n.nodes,s),e.subs[n.n]=s.code,e.inPartial||(e.code+='t.sub("'+u(n.n)+'",c,p,i);')},"\n":function(t,n){n.code+=f('"\\n"'+(t.last?"":" + i"))},_v:function(t,n){n.code+="t.b(t.v(t."+c(t.n)+'("'+u(t.n)+'",c,p,0)));'},_t:function(t,n){n.code+=f('"'+u(t.text)+'"')},"{":b,"&":b},t.walk=function(n,e){for(var s,a=0,r=n.length;r>a;a++)s=t.codegen[n[a].tag],s&&s(n[a],e);return e},t.parse=function(t,n,e){return e=e||{},a(t,"",[],e.sectionTags||[])},t.cache={},t.cacheKey=function(t,n){return[t,!!n.asString,!!n.disableLambda,n.delimiters,!!n.modelGet].join("||")},t.compile=function(n,e){e=e||{};var s=t.cacheKey(n,e),a=this.cache[s];if(a){var r=a.partials;for(var i in r)delete r[i].instance;return a}return a=this.generate(this.parse(this.scan(n,e.delimiters),n,e),n,e),this.cache[s]=a}}("undefined"!=typeof exports?exports:Hogan);var Mustache=function(t){function n(n,e,s,a){var r=this.f(n,e,s,0),i=e;return r&&(i=i.concat(r)),t.Template.prototype.rp.call(this,n,i,s,a)}var e=function(e,s,a){this.rp=n,t.Template.call(this,e,s,a)};e.prototype=t.Template.prototype;var s,a=function(){this.cache={},this.generate=function(t,n){return new e(new Function("c","p","i",t),n,s)}};return a.prototype=t,s=new a,{to_html:function(t,n,e,a){var r=s.compile(t),i=r.render(n,e);return a?(a(i),void 0):i}}}(Hogan),templates={};templates.controls=new Hogan.Template({code:function(t,n,e){var s=this;return s.b(e=e||""),s.b('<div class="player-controls">'),s.b("\n"+e),s.b(' <div class="player-progress">'),s.b("\n"+e),s.b(' <progress class="player-progress-played" max="100" value="0">'),s.b("\n"+e),s.b(" <span>0</span>% played"),s.b("\n"+e),s.b(" </progress>"),s.b("\n"+e),s.b(' <progress class="player-progress-buffer" max="100" value="0">'),s.b("\n"+e),s.b(" <span>0</span>% buffered"),s.b("\n"+e),s.b(" </progress>"),s.b("\n"+e),s.b(" </div>"),s.b("\n"+e),s.b(' <span class="player-controls-playback">'),s.b("\n"+e),s.b(' <button type="button" data-player="restart">'),s.b("\n"+e),s.b(' <svg><use xlink:href="#icon-refresh"></use></svg>'),s.b("\n"+e),s.b(' <span class="sr-only">Restart</span>'),s.b("\n"+e),s.b(" </button>"),s.b("\n"+e),s.b(' <button type="button" data-player="rewind">'),s.b("\n"+e),s.b(' <svg><use xlink:href="#icon-rewind"></use></svg>'),s.b("\n"+e),s.b(' <span class="sr-only">Rewind <span class="player-seek-time">{seektime}</span> seconds</span>'),s.b("\n"+e),s.b(" </button>"),s.b("\n"+e),s.b(' <button type="button" data-player="play">'),s.b("\n"+e),s.b(' <svg><use xlink:href="#icon-play"></use></svg>'),s.b("\n"+e),s.b(' <span class="sr-only">Play</span>'),s.b("\n"+e),s.b(" </button>"),s.b("\n"+e),s.b(' <button type="button" data-player="pause">'),s.b("\n"+e),s.b(' <svg><use xlink:href="#icon-pause"></use></svg>'),s.b("\n"+e),s.b(' <span class="sr-only">Pause</span>'),s.b("\n"+e),s.b(" </button>"),s.b("\n"+e),s.b(' <button type="button" data-player="fast-forward">'),s.b("\n"+e),s.b(' <svg><use xlink:href="#icon-fast-forward"></use></svg>'),s.b("\n"+e),s.b(' <span class="sr-only">Fast forward <span class="player-seek-time">{seektime}</span> seconds</span>'),s.b("\n"+e),s.b(" </button>"),s.b("\n"+e),s.b(' <span class="player-time">'),s.b("\n"+e),s.b(' <span class="sr-only">Time</span>'),s.b("\n"+e),s.b(' <span class="player-duration">00:00</span>'),s.b("\n"+e),s.b(" </span>"),s.b("\n"+e),s.b(" </span>"),s.b("\n"+e),s.b(' <span class="player-controls-sound">'),s.b("\n"+e),s.b(' <input class="inverted sr-only" id="mute{id}" type="checkbox" data-player="mute">'),s.b("\n"+e),s.b(' <label id="mute{id}" for="mute{id}">'),s.b("\n"+e),s.b(' <svg class="icon-muted"><use xlink:href="#icon-muted"></use></svg>'),s.b("\n"+e),s.b(' <svg><use xlink:href="#icon-sound"></use></svg>'),s.b("\n"+e),s.b(' <span class="sr-only">Mute</span>'),s.b("\n"+e),s.b(" </label>"),s.b("\n"),s.b("\n"+e),s.b(' <label for="volume{id}" class="sr-only">Volume</label>'),s.b("\n"+e),s.b(' <input id="volume{id}" class="player-volume" type="range" min="0" max="10" value="5" data-player="volume">'),s.b("\n"),s.b("\n"+e),s.b(' <input class="sr-only" id="captions{id}" type="checkbox" data-player="captions">'),s.b("\n"+e),s.b(' <label for="captions{id}">'),s.b("\n"+e),s.b(' <svg><use xlink:href="#icon-bubble"></use></svg>'),s.b("\n"+e),s.b(' <span class="sr-only">Captions</span>'),s.b("\n"+e),s.b(" </label>"),s.b("\n"),s.b("\n"+e),s.b(' <button type="button" data-player="fullscreen">'),s.b("\n"+e),s.b(' <svg class="icon-exit-fullscreen"><use xlink:href="#icon-collapse"></use></svg>'),s.b("\n"+e),s.b(' <svg><use xlink:href="#icon-expand"></use></svg>'),s.b("\n"+e),s.b(' <span class="sr-only">Toggle fullscreen</span>'),s.b("\n"+e),s.b(" </button>"),s.b("\n"+e),s.b(" </span>"),s.b("\n"+e),s.b("</div>"),s.fl()},partials:{},subs:{}}),plyr.setup({debug:!0,title:"Video demo",html:templates.controls.render({}),captions:{defaultActive:!0}}),"plyr.io"===document.domain&&(!function(t,n,e,s,a,r,i){t.GoogleAnalyticsObject=a,t[a]=t[a]||function(){(t[a].q=t[a].q||[]).push(arguments)},t[a].l=1*new Date,r=n.createElement(e),i=n.getElementsByTagName(e)[0],r.async=1,r.src=s,i.parentNode.insertBefore(r,i)}(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/dist/js/plyr.js b/dist/js/plyr.js index 41694ad0..200c18b4 100644 --- a/dist/js/plyr.js +++ b/dist/js/plyr.js @@ -1 +1 @@ -!function(e){"use strict";function t(e,t){b.debug&&window.console&&console[t?"error":"log"](e)}function n(){var e,t,n,r=navigator.userAgent,s=navigator.appName,a=""+parseFloat(navigator.appVersion),o=parseInt(navigator.appVersion,10);return-1!==navigator.appVersion.indexOf("Windows NT")&&-1!==navigator.appVersion.indexOf("rv:11")?(s="IE",a="11;"):-1!==(t=r.indexOf("MSIE"))?(s="IE",a=r.substring(t+5)):-1!==(t=r.indexOf("Chrome"))?(s="Chrome",a=r.substring(t+7)):-1!==(t=r.indexOf("Safari"))?(s="Safari",a=r.substring(t+7),-1!==(t=r.indexOf("Version"))&&(a=r.substring(t+8))):-1!==(t=r.indexOf("Firefox"))?(s="Firefox",a=r.substring(t+8)):(e=r.lastIndexOf(" ")+1)<(t=r.lastIndexOf("/"))&&(s=r.substring(e,t),a=r.substring(t+1),s.toLowerCase()==s.toUpperCase()&&(s=navigator.appName)),-1!==(n=a.indexOf(";"))&&(a=a.substring(0,n)),-1!==(n=a.indexOf(" "))&&(a=a.substring(0,n)),o=parseInt(""+a,10),isNaN(o)&&(a=""+parseFloat(navigator.appVersion),o=parseInt(navigator.appVersion,10)),[s,o]}function r(e,t,n){return e.replace(new RegExp(t.replace(/([.*+?^=!:${}()|\[\]\/\\])/g,"\\$1"),"g"),n)}function s(e,t){e.length||(e=[e]);for(var n=e.length-1;n>=0;n--){var r=n>0?t.cloneNode(!0):t,s=e[n],a=s.parentNode,o=s.nextSibling;r.appendChild(s),o?a.insertBefore(r,o):a.appendChild(r)}}function a(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 o(e,t,n){e.addEventListener(t,n,!1)}function i(e,t,n){e.removeEventListener(t,n,!1)}function c(e,t){return Math.floor(e/t*100)}function l(e){var t=d().isFullScreen()?{x:0,y:0}:u(e.currentTarget);return{x:e.clientX-t.x,y:e.clientY-t.y}}function u(e){for(var t=0,n=0;e;)t+=e.offsetLeft-e.scrollLeft+e.clientLeft,n+=e.offsetTop-e.scrollTop+e.clientTop,e=e.offsetParent;return{x:t,y:n}}function p(e,t){for(var n in t)t[n]&&t[n].constructor&&t[n].constructor===Object?(e[n]=e[n]||{},p(e[n],t[n])):e[n]=t[n];return e}function d(){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"webkit"===e.prefix&&navigator.userAgent.match(/Version\/[\d\.]+.*Safari/)&&(e.supportsFullScreen=!1),e.supportsFullScreen&&(e.fullScreenEventName="ms"==e.prefix?"MSFullscreenChange":e.prefix+"fullscreenchange",e.isFullScreen=function(){switch(this.prefix){case"":return document.fullScreen;case"webkit":return document.webkitIsFullScreen;case"ms":return null!==document.msFullscreenElement;default:return document[this.prefix+"FullScreen"]}},e.requestFullScreen=function(e){return""===this.prefix?e.requestFullScreen():e[this.prefix+("ms"==this.prefix?"RequestFullscreen":"RequestFullScreen")]("webkit"===this.prefix?e.ALLOW_KEYBOARD_INPUT:null)},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 f(e){function u(){for(X.subcount=0;v(X.captions[X.subcount][0])<X.media.currentTime.toFixed(1);)if(X.subcount++,X.subcount>X.captions.length-1){X.subcount=X.captions.length-1;break}}function p(){a(X.container,b.classes.captions.enabled,!0),b.captions.defaultActive&&(a(X.container,b.classes.captions.active,!0),X.buttons.captions.setAttribute("checked","checked"))}function f(e){var t=[];return t=e.split(" --> "),g(t[0])}function v(e){var t=[];return t=e.split(" --> "),g(t[1])}function g(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 y(e){return X.container.querySelectorAll(e)}function h(e){return y(e)[0]}function x(){try{return window.self!==window.top}catch(e){return!0}}function k(){t("Injecting custom controls.");var e=b.html;e=r(e,"{aria-label}",b.playAriaLabel),e=r(e,"{id}",X.random),X.container.insertAdjacentHTML("beforeend",e)}function w(){try{return X.controls=h(b.selectors.controls),X.buttons={},X.buttons.play=h(b.selectors.buttons.play),X.buttons.pause=h(b.selectors.buttons.pause),X.buttons.restart=h(b.selectors.buttons.restart),X.buttons.rewind=h(b.selectors.buttons.rewind),X.buttons.forward=h(b.selectors.buttons.forward),X.buttons.mute=h(b.selectors.buttons.mute),X.buttons.captions=h(b.selectors.buttons.captions),X.buttons.fullscreen=h(b.selectors.buttons.fullscreen),X.progress={},X.progress.container=h(b.selectors.progress.container),X.progress.buffer={},X.progress.buffer.bar=h(b.selectors.progress.buffer),X.progress.buffer.text=X.progress.buffer.bar.getElementsByTagName("span")[0],X.progress.played={},X.progress.played.bar=h(b.selectors.progress.played),X.progress.played.text=X.progress.played.bar.getElementsByTagName("span")[0],X.volume=h(b.selectors.buttons.volume),X.duration=h(b.selectors.duration),X.seekTime=y(b.selectors.seekTime),!0}catch(e){return t("It looks like there's a problem with your controls html. Bailing.",!0),!1}}function T(){if(X.media=X.container.querySelectorAll("audio, video")[0],!X.media)return t("No audio or video element found!",!0),!1;if(X.media.removeAttribute("controls"),X.type="video"==X.media.tagName.toLowerCase()?"video":"audio",a(X.container,b.classes[X.type],!0),a(X.container,b.classes.stopped,null===X.media.getAttribute("autoplay")),"video"===X.type){var e=document.createElement("div");e.setAttribute("class",b.classes.videoWrapper),s(X.media,e),X.videoContainer=e}}function S(){if("video"===X.type){X.videoContainer.insertAdjacentHTML("afterbegin","<div class='"+b.selectors.captions.replace(".","")+"'></div>"),X.captionsContainer=h(b.selectors.captions),X.isTextTracks=!1,X.media.textTracks&&(X.isTextTracks=!0);for(var e,n="",r=X.media.childNodes,s=0;s<r.length;s++)"track"===r[s].nodeName.toLowerCase()&&(e=r[s].getAttribute("kind"),"captions"===e&&(n=r[s].getAttribute("src")));if(X.captionExists=!0,""===n?(X.captionExists=!1,t("No caption track found.")):t("Caption track found; URI: "+n),X.captionExists){for(var i=X.media.textTracks,c=0;c<i.length;c++)i[c].mode="hidden";if(p(X),("IE"===X.browserName&&10===X.browserMajorVersion||"IE"===X.browserName&&11===X.browserMajorVersion||"Firefox"===X.browserName&&X.browserMajorVersion>=31||"Safari"===X.browserName&&X.browserMajorVersion>=7)&&(t("Detected IE 10/11 or Firefox 31+ or Safari 7+."),X.isTextTracks=!1),X.isTextTracks){t("TextTracks supported.");for(var l=0;l<i.length;l++){var u=i[l];"captions"===u.kind&&o(u,"cuechange",function(){this.activeCues[0]&&this.activeCues[0].hasOwnProperty("text")&&(X.captionsContainer.innerHTML=this.activeCues[0].text)})}}else if(t("TextTracks not supported so rendering captions manually."),X.currentCaption="",X.subcount=0,X.captions=[],o(X.media,"timeupdate",function(){X.media.currentTime.toFixed(1)>f(X.captions[X.subcount][0])&&X.media.currentTime.toFixed(1)<v(X.captions[X.subcount][0])&&(X.currentCaption=X.captions[X.subcount][1]),X.media.currentTime.toFixed(1)>v(X.captions[X.subcount][0])&&X.subcount<X.captions.length-1&&X.subcount++,X.captionsContainer.innerHTML=X.currentCaption}),""!==n){var d=new XMLHttpRequest;d.onreadystatechange=function(){if(4===d.readyState)if(200===d.status){var e,n=[],r=d.responseText;n=r.split("\n\n");for(var s=0;s<n.length;s++)e=n[s],X.captions[s]=[],X.captions[s]=e.split("\n");X.captions.shift(),t("Successfully loaded the caption file via AJAX.")}else t("There was a problem loading the caption file via AJAX.",!0)},d.open("get",n,!0),d.send()}if("Safari"===X.browserName&&X.browserMajorVersion>=7){t("Safari 7+ detected; removing track from DOM."),i=X.media.getElementsByTagName("track");for(var m=0;m<i.length;m++)X.media.removeChild(i[m])}}else a(X.container,b.classes.captions.enabled)}}function F(){X.seekTime[0].innerHTML=b.seekInterval,X.seekTime[1].innerHTML=b.seekInterval}function N(){if("video"===X.type&&b.fullscreen.enabled){var e=m.supportsFullScreen;e||b.fullscreen.fallback&&!x()?(t((e?"Native":"Fallback")+" fullscreen enabled."),a(X.container,b.classes.fullscreen.enabled,!0)):t("Fullscreen not supported and fallback disabled.")}}function C(){X.media.play(),a(X.container,b.classes.stopped),a(X.container,b.classes.playing,!0)}function I(){X.media.pause(),a(X.container,b.classes.playing),a(X.container,b.classes.stopped,!0)}function M(){X.media.currentTime=0,X.isTextTracks||(X.subcount=0),C()}function E(e){"undefined"==typeof e&&(e=b.seekInterval);var t=X.media.currentTime-e;X.media.currentTime=0>t?0:t,X.isTextTracks||"video"!==X.type||u(X)}function L(e){"undefined"==typeof e&&(e=b.seekInterval);var t=X.media.currentTime+e;X.media.currentTime=t>X.media.duration?X.media.duration:t,X.isTextTracks||"video"!==X.type||u(X)}function A(){var e=m.supportsFullScreen;event.type===m.fullScreenEventName?b.fullscreen.active=m.isFullScreen():e?(m.isFullScreen()?m.cancelFullScreen():m.requestFullScreen(X.container),b.fullscreen.active=m.isFullScreen()):(b.fullscreen.active=!b.fullscreen.active,b.fullscreen.active?(o(document,"keyup",V),document.body.style.overflow="hidden"):(i(document,"keyup",V),document.body.style.overflow="")),a(X.container,b.classes.fullscreen.active,b.fullscreen.active)}function V(e){27===(e.which||e.charCode||e.keyCode)&&b.fullscreen.active&&A()}function O(e){"undefined"==typeof e&&(e=b.storage.enabled&&b.storage.supported?window.localStorage.plyr_volume||b.volume:b.volume),e>10&&(e=10),X.volume.value=e,X.media.volume=parseFloat(e/10),H(),b.storage.enabled&&b.storage.supported&&(window.localStorage.plyr_volume=e)}function j(e){"undefined"==typeof active&&(e=!X.media.muted,X.buttons.mute.checked=e),X.media.muted=e,H()}function q(e){"undefined"==typeof e&&(e=-1===X.container.className.indexOf(b.classes.captions.active),X.buttons.captions.checked=e),e?a(X.container,b.classes.captions.active,!0):a(X.container,b.classes.captions.active)}function H(){a(X.container,b.classes.muted,0===X.media.volume||X.media.muted)}function B(e){var t,n,r=0;switch(e.type){case"timeupdate":t=X.progress.played.bar,n=X.progress.played.text,r=c(X.media.currentTime,X.media.duration);break;case"playing":case"progress":t=X.progress.buffer.bar,n=X.progress.buffer.text,r=function(){var e=X.media.buffered;return e.length?c(e.end(0),X.media.duration):0}()}t&&r>0&&(t.value=r,n.innerHTML=r)}function R(){X.secs=parseInt(X.media.currentTime%60),X.mins=parseInt(X.media.currentTime/60%60),X.secs=("0"+X.secs).slice(-2),X.mins=("0"+X.mins).slice(-2),X.duration.innerHTML=X.mins+":"+X.secs}function P(){o(X.buttons.play,"click",function(){C(),X.buttons.pause.focus()}),o(X.buttons.pause,"click",function(){I(),X.buttons.play.focus()}),o(X.buttons.restart,"click",M),o(X.buttons.rewind,"click",function(){E(b.seekInterval)}),o(X.buttons.forward,"click",function(){L(b.seekInterval)}),o(X.volume,"change",function(){O(this.value)}),o(X.buttons.mute,"change",function(){j(this.checked)}),o(X.buttons.fullscreen,"click",A),o(document,m.fullScreenEventName,A),"video"===X.type&&b.click&&o(X.videoContainer,"click",function(){X.media.paused?C():X.media.ended?M():I()}),o(X.media,"timeupdate",R),o(X.media,"timeupdate",B),o(X.progress.played.bar,"click",function(e){X.pos=l(e).x/this.offsetWidth,X.media.currentTime=X.pos*X.media.duration,X.isTextTracks||"video"!==X.type||u(X)}),o(X.buttons.captions,"click",function(){q(this.checked)}),o(X.media,"ended",function(){"video"===X.type&&(X.captionsContainer.innerHTML=""),a(X.container,b.classes.stopped,!0),a(X.container,b.classes.playing)}),o(X.media,"progress",B),o(X.media,"playing",B)}function W(){return m=d(),X.browserInfo=n(),X.browserName=X.browserInfo[0],X.browserMajorVersion=X.browserInfo[1],t(X.browserName+" "+X.browserMajorVersion),"IE"!==X.browserName||8!==X.browserMajorVersion&&9!==X.browserMajorVersion?(b.playAriaLabel="undefined"!=typeof b.title&&b.title.length?"Play "+b.title:"Play",T(),X.random=Math.floor(1e4*Math.random()),k(),w()?(S(),O(),N(),F(),P(),void 0):!1):(t("Browser not suppported.",!0),!1)}var X=this;return X.container=e,W(),{media:X.media,play:C,pause:I,restart:M,rewind:E,forward:L,setVolume:O,toggleMute:j,toggleCaptions:q}}var m,b,v={enabled:!0,debug:!1,seekInterval:10,volume:5,click:!0,selectors:{container:".player",controls:".player-controls",buttons:{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",duration:".player-duration",seekTime:".player-seek-time"},classes:{video:"player-video",videoWrapper:"player-video-wrapper",audio:"player-audio",stopped:"stopped",playing:"playing",muted:"muted",captions:{enabled:"captions-enabled",active:"captions-active"},fullscreen:{enabled:"fullscreen-enabled",active:"fullscreen-active"}},captions:{defaultActive:!1},fullscreen:{enabled:!0,fallback:!0},storage:{enabled:!0,supported:function(){try{return"localStorage"in window&&null!==window.localStorage}catch(e){return!1}}}};e.setup=function(e){if(b=p(v,e),!b.enabled)return!1;for(var t=document.querySelectorAll(b.selectors.container),n=[],r=t.length-1;r>=0;r--){var s=t[r];"undefined"==typeof s.plyr&&(s.plyr=new f(s)),n.push(s.plyr)}return n}}(this.plyr=this.plyr||{});
\ No newline at end of file +!function(e){"use strict";function t(e,t){g.debug&&window.console&&console[t?"error":"log"](e)}function n(){var e,t,n,r=navigator.userAgent,s=navigator.appName,o=""+parseFloat(navigator.appVersion),a=parseInt(navigator.appVersion,10);return-1!==navigator.appVersion.indexOf("Windows NT")&&-1!==navigator.appVersion.indexOf("rv:11")?(s="IE",o="11;"):-1!==(t=r.indexOf("MSIE"))?(s="IE",o=r.substring(t+5)):-1!==(t=r.indexOf("Chrome"))?(s="Chrome",o=r.substring(t+7)):-1!==(t=r.indexOf("Safari"))?(s="Safari",o=r.substring(t+7),-1!==(t=r.indexOf("Version"))&&(o=r.substring(t+8))):-1!==(t=r.indexOf("Firefox"))?(s="Firefox",o=r.substring(t+8)):(e=r.lastIndexOf(" ")+1)<(t=r.lastIndexOf("/"))&&(s=r.substring(e,t),o=r.substring(t+1),s.toLowerCase()==s.toUpperCase()&&(s=navigator.appName)),-1!==(n=o.indexOf(";"))&&(o=o.substring(0,n)),-1!==(n=o.indexOf(" "))&&(o=o.substring(0,n)),a=parseInt(""+o,10),isNaN(a)&&(o=""+parseFloat(navigator.appVersion),a=parseInt(navigator.appVersion,10)),[s,a]}function r(e,t,n){return e.replace(new RegExp(t.replace(/([.*+?^=!:${}()|\[\]\/\\])/g,"\\$1"),"g"),n)}function s(e,t){e.length||(e=[e]);for(var n=e.length-1;n>=0;n--){var r=n>0?t.cloneNode(!0):t,s=e[n],o=s.parentNode,a=s.nextSibling;r.appendChild(s),a?o.insertBefore(r,a):o.appendChild(r)}}function o(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 a(e,t,n,r){t=t.split(" ");for(var s=0;s<t.length;s++)e[r?"addEventListener":"removeEventListener"](t[s],n,!1)}function i(e,t,n){a(e,t,n,!0)}function c(e,t,n){a(e,t,n,!1)}function u(e,t){return Math.floor(e/t*100)}function l(e){var t=f().isFullScreen()?{x:0,y:0}:p(e.currentTarget);return{x:e.clientX-t.x,y:e.clientY-t.y}}function p(e){for(var t=0,n=0;e;)t+=e.offsetLeft-e.scrollLeft+e.clientLeft,n+=e.offsetTop-e.scrollTop+e.clientTop,e=e.offsetParent;return{x:t,y:n}}function d(e,t){for(var n in t)t[n]&&t[n].constructor&&t[n].constructor===Object?(e[n]=e[n]||{},d(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:""},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"webkit"===e.prefix&&navigator.userAgent.match(/Version\/[\d\.]+.*Safari/)&&(e.supportsFullScreen=!1),e.supportsFullScreen&&(e.fullScreenEventName="ms"==e.prefix?"MSFullscreenChange":e.prefix+"fullscreenchange",e.isFullScreen=function(){switch(this.prefix){case"":return document.fullScreen;case"webkit":return document.webkitIsFullScreen;case"ms":return null!==document.msFullscreenElement;default:return document[this.prefix+"FullScreen"]}},e.requestFullScreen=function(e){return""===this.prefix?e.requestFullScreen():e[this.prefix+("ms"==this.prefix?"RequestFullscreen":"RequestFullScreen")]("webkit"===this.prefix?e.ALLOW_KEYBOARD_INPUT:null)},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 m(){var e={supported:function(){try{return"localStorage"in window&&null!==window.localStorage}catch(e){return!1}}()};return e}function b(e){function a(){for(U.subcount=0;b(U.captions[U.subcount][0])<U.media.currentTime.toFixed(1);)if(U.subcount++,U.subcount>U.captions.length-1){U.subcount=U.captions.length-1;break}}function p(){o(U.container,g.classes.captions.enabled,!0),g.captions.defaultActive&&(o(U.container,g.classes.captions.active,!0),U.buttons.captions.setAttribute("checked","checked"))}function d(e){var t=[];return t=e.split(" --> "),y(t[0])}function b(e){var t=[];return t=e.split(" --> "),y(t[1])}function y(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 h(e){return U.container.querySelectorAll(e)}function x(e){return h(e)[0]}function T(){try{return window.self!==window.top}catch(e){return!0}}function k(){t("Injecting custom controls.");var e=g.html;e=r(e,"{seektime}",g.seekTime),e=r(e,"{id}",U.random),U.container.insertAdjacentHTML("beforeend",e)}function w(){try{return U.controls=x(g.selectors.controls),U.buttons={},U.buttons.play=x(g.selectors.buttons.play),U.buttons.pause=x(g.selectors.buttons.pause),U.buttons.restart=x(g.selectors.buttons.restart),U.buttons.rewind=x(g.selectors.buttons.rewind),U.buttons.forward=x(g.selectors.buttons.forward),U.buttons.mute=x(g.selectors.buttons.mute),U.buttons.captions=x(g.selectors.buttons.captions),U.buttons.fullscreen=x(g.selectors.buttons.fullscreen),U.progress={},U.progress.container=x(g.selectors.progress.container),U.progress.buffer={},U.progress.buffer.bar=x(g.selectors.progress.buffer),U.progress.buffer.text=U.progress.buffer.bar.getElementsByTagName("span")[0],U.progress.played={},U.progress.played.bar=x(g.selectors.progress.played),U.progress.played.text=U.progress.played.bar.getElementsByTagName("span")[0],U.volume=x(g.selectors.buttons.volume),U.duration=x(g.selectors.duration),U.seekTime=h(g.selectors.seekTime),!0}catch(e){return t("It looks like there's a problem with your controls html. Bailing.",!0),!1}}function S(){var e=U.buttons.play.innerText;"undefined"!=typeof g.title&&g.title.length&&(e=U.buttons.play.innerText+", "+g.title),U.buttons.play.setAttribute("aria-label",e)}function F(){if(U.media=U.container.querySelectorAll("audio, video")[0],!U.media)return t("No audio or video element found!",!0),!1;if(U.media.removeAttribute("controls"),U.type="video"==U.media.tagName.toLowerCase()?"video":"audio",o(U.container,g.classes[U.type],!0),o(U.container,g.classes.stopped,null===U.media.getAttribute("autoplay")),"video"===U.type){var e=document.createElement("div");e.setAttribute("class",g.classes.videoWrapper),s(U.media,e),U.videoContainer=e}}function N(){if("video"===U.type){U.videoContainer.insertAdjacentHTML("afterbegin","<div class='"+g.selectors.captions.replace(".","")+"'></div>"),U.captionsContainer=x(g.selectors.captions),U.isTextTracks=!1,U.media.textTracks&&(U.isTextTracks=!0);for(var e,n="",r=U.media.childNodes,s=0;s<r.length;s++)"track"===r[s].nodeName.toLowerCase()&&(e=r[s].getAttribute("kind"),"captions"===e&&(n=r[s].getAttribute("src")));if(U.captionExists=!0,""===n?(U.captionExists=!1,t("No caption track found.")):t("Caption track found; URI: "+n),U.captionExists){for(var a=U.media.textTracks,c=0;c<a.length;c++)a[c].mode="hidden";if(p(U),("IE"===U.browserName&&10===U.browserMajorVersion||"IE"===U.browserName&&11===U.browserMajorVersion||"Firefox"===U.browserName&&U.browserMajorVersion>=31||"Safari"===U.browserName&&U.browserMajorVersion>=7)&&(t("Detected IE 10/11 or Firefox 31+ or Safari 7+."),U.isTextTracks=!1),U.isTextTracks){t("TextTracks supported.");for(var u=0;u<a.length;u++){var l=a[u];"captions"===l.kind&&i(l,"cuechange",function(){this.activeCues[0]&&this.activeCues[0].hasOwnProperty("text")&&(U.captionsContainer.innerHTML=this.activeCues[0].text)})}}else if(t("TextTracks not supported so rendering captions manually."),U.currentCaption="",U.subcount=0,U.captions=[],i(U.media,"timeupdate",function(){U.media.currentTime.toFixed(1)>d(U.captions[U.subcount][0])&&U.media.currentTime.toFixed(1)<b(U.captions[U.subcount][0])&&(U.currentCaption=U.captions[U.subcount][1]),U.media.currentTime.toFixed(1)>b(U.captions[U.subcount][0])&&U.subcount<U.captions.length-1&&U.subcount++,U.captionsContainer.innerHTML=U.currentCaption}),""!==n){var f=new XMLHttpRequest;f.onreadystatechange=function(){if(4===f.readyState)if(200===f.status){var e,n=[],r=f.responseText;n=r.split("\n\n");for(var s=0;s<n.length;s++)e=n[s],U.captions[s]=[],U.captions[s]=e.split("\n");U.captions.shift(),t("Successfully loaded the caption file via AJAX.")}else t("There was a problem loading the caption file via AJAX.",!0)},f.open("get",n,!0),f.send()}if("Safari"===U.browserName&&U.browserMajorVersion>=7){t("Safari 7+ detected; removing track from DOM."),a=U.media.getElementsByTagName("track");for(var m=0;m<a.length;m++)U.media.removeChild(a[m])}}else o(U.container,g.classes.captions.enabled)}}function C(){U.seekTime[0].innerHTML=g.seekTime,U.seekTime[1].innerHTML=g.seekTime}function M(){if("video"===U.type&&g.fullscreen.enabled){var e=v.supportsFullScreen;e||g.fullscreen.fallback&&!T()?(t((e?"Native":"Fallback")+" fullscreen enabled."),o(U.container,g.classes.fullscreen.enabled,!0)):t("Fullscreen not supported and fallback disabled.")}}function E(){U.media.play()}function A(){U.media.pause()}function I(){U.media.currentTime=0,U.isTextTracks||(U.subcount=0),E()}function L(e){"number"!=typeof e&&(e=g.seekTime);var t=U.media.currentTime-e;U.media.currentTime=0>t?0:t,U.isTextTracks||"video"!==U.type||a(U)}function V(e){"number"!=typeof e&&(e=g.seekTime);var t=U.media.currentTime+e;U.media.currentTime=t>U.media.duration?U.media.duration:t,U.isTextTracks||"video"!==U.type||a(U)}function O(){o(U.container,g.classes.playing,!U.media.paused),o(U.container,g.classes.stopped,U.media.paused)}function j(){var e=v.supportsFullScreen;event.type===v.fullScreenEventName?g.fullscreen.active=v.isFullScreen():e?(v.isFullScreen()?v.cancelFullScreen():v.requestFullScreen(U.container),g.fullscreen.active=v.isFullScreen()):(g.fullscreen.active=!g.fullscreen.active,g.fullscreen.active?(i(document,"keyup",q),document.body.style.overflow="hidden"):(c(document,"keyup",q),document.body.style.overflow="")),o(U.container,g.classes.fullscreen.active,g.fullscreen.active)}function q(e){27===(e.which||e.charCode||e.keyCode)&&g.fullscreen.active&&j()}function H(e){"undefined"==typeof e&&(e=g.storage.enabled&&m().supported?window.localStorage.plyr_volume||g.volume:g.volume),e>10&&(e=10),U.volume.value=e,U.media.volume=parseFloat(e/10),W(),g.storage.enabled&&m().supported&&(window.localStorage.plyr_volume=e)}function B(e){"undefined"==typeof active&&(e=!U.media.muted,U.buttons.mute.checked=e),U.media.muted=e,W()}function R(e){"undefined"==typeof e&&(e=-1===U.container.className.indexOf(g.classes.captions.active),U.buttons.captions.checked=e),e?o(U.container,g.classes.captions.active,!0):o(U.container,g.classes.captions.active)}function W(){o(U.container,g.classes.muted,0===U.media.volume||U.media.muted)}function X(e){var t,n,r=0;switch(e.type){case"timeupdate":t=U.progress.played.bar,n=U.progress.played.text,r=u(U.media.currentTime,U.media.duration);break;case"playing":case"progress":t=U.progress.buffer.bar,n=U.progress.buffer.text,r=function(){var e=U.media.buffered;return e.length?u(e.end(0),U.media.duration):0}()}t&&r>0&&(t.value=r,n.innerHTML=r)}function _(){U.secs=parseInt(U.media.currentTime%60),U.mins=parseInt(U.media.currentTime/60%60),U.secs=("0"+U.secs).slice(-2),U.mins=("0"+U.mins).slice(-2),U.duration.innerHTML=U.mins+":"+U.secs}function D(){i(U.buttons.play,"click",function(){E(),setTimeout(function(){U.buttons.pause.focus()},100)}),i(U.buttons.pause,"click",function(){A(),setTimeout(function(){U.buttons.play.focus()},100)}),i(U.buttons.restart,"click",I),i(U.buttons.rewind,"click",L),i(U.buttons.forward,"click",V),i(U.volume,"change",function(){H(this.value)}),i(U.buttons.mute,"change",function(){B(this.checked)}),i(U.buttons.fullscreen,"click",j),i(document,v.fullScreenEventName,j),"video"===U.type&&g.click&&i(U.videoContainer,"click",function(){U.media.paused?E():U.media.ended?I():A()}),i(U.media,"timeupdate",_),i(U.media,"timeupdate",X),i(U.progress.played.bar,"click",function(e){U.pos=l(e).x/this.offsetWidth,U.media.currentTime=U.pos*U.media.duration,U.isTextTracks||"video"!==U.type||a(U)}),i(U.buttons.captions,"click",function(){R(this.checked)}),i(U.media,"ended",function(){"video"===U.type&&(U.captionsContainer.innerHTML=""),O()}),i(U.media,"progress",X),i(U.media,"playing",X),i(U.media,"volumechange",W),i(U.media,"play pause",O)}function P(){return v=f(),U.browserInfo=n(),U.browserName=U.browserInfo[0],U.browserMajorVersion=U.browserInfo[1],t(U.browserName+" "+U.browserMajorVersion),"IE"!==U.browserName||8!==U.browserMajorVersion&&9!==U.browserMajorVersion?(F(),U.random=Math.floor(1e4*Math.random()),k(),w()?(S(),N(),H(),M(),C(),D(),void 0):!1):(t("Browser not suppported.",!0),!1)}var U=this;return U.container=e,P(),{media:U.media,play:E,pause:A,restart:I,rewind:L,forward:V,setVolume:H,toggleMute:B,toggleCaptions:R}}var v,g,y={enabled:!0,debug:!1,seekTime:10,volume:5,click:!0,selectors:{container:".player",controls:".player-controls",buttons:{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",duration:".player-duration",seekTime:".player-seek-time"},classes:{video:"player-video",videoWrapper:"player-video-wrapper",audio:"player-audio",stopped:"stopped",playing:"playing",muted:"muted",captions:{enabled:"captions-enabled",active:"captions-active"},fullscreen:{enabled:"fullscreen-enabled",active:"fullscreen-active"}},captions:{defaultActive:!1},fullscreen:{enabled:!0,fallback:!0},storage:{enabled:!0}};e.setup=function(e){if(g=d(y,e),!g.enabled)return!1;for(var t=document.querySelectorAll(g.selectors.container),n=[],r=t.length-1;r>=0;r--){var s=t[r];"undefined"==typeof s.plyr&&(s.plyr=new b(s)),n.push(s.plyr)}return n}}(this.plyr=this.plyr||{});
\ No newline at end of file diff --git a/dist/js/templates.js b/dist/js/templates.js index fb0502a9..9526688b 100644 --- a/dist/js/templates.js +++ b/dist/js/templates.js @@ -1,2 +1,2 @@ var templates = {}; - templates['controls'] = new Hogan.Template({code: function (c,p,i) { var t=this;t.b(i=i||"");t.b("<div class=\"player-controls\">");t.b("\n" + i);t.b(" <div class=\"player-progress\">");t.b("\n" + i);t.b(" <progress class=\"player-progress-played\" max=\"100\" value=\"0\">");t.b("\n" + i);t.b(" <span>0</span>% played");t.b("\n" + i);t.b(" </progress>");t.b("\n" + i);t.b(" <progress class=\"player-progress-buffer\" max=\"100\" value=\"0\">");t.b("\n" + i);t.b(" <span>0</span>% buffered");t.b("\n" + i);t.b(" </progress>");t.b("\n" + i);t.b(" </div>");t.b("\n" + i);t.b(" <span class=\"player-controls-playback\">");t.b("\n" + i);t.b(" <button type=\"button\" data-player=\"restart\">");t.b("\n" + i);t.b(" <svg><use xlink:href=\"#icon-refresh\"></use></svg>");t.b("\n" + i);t.b(" <span class=\"sr-only\">Restart</span>");t.b("\n" + i);t.b(" </button>");t.b("\n" + i);t.b(" <button type=\"button\" data-player=\"rewind\">");t.b("\n" + i);t.b(" <svg><use xlink:href=\"#icon-rewind\"></use></svg>");t.b("\n" + i);t.b(" <span class=\"sr-only\">Rewind <span class=\"player-seek-time\">10</span> seconds</span>");t.b("\n" + i);t.b(" </button>");t.b("\n" + i);t.b(" <button type=\"button\" aria-label=\"{aria-label}\" data-player=\"play\">");t.b("\n" + i);t.b(" <svg><use xlink:href=\"#icon-play\"></use></svg>");t.b("\n" + i);t.b(" <span class=\"sr-only\">Play</span>");t.b("\n" + i);t.b(" </button>");t.b("\n" + i);t.b(" <button type=\"button\" data-player=\"pause\">");t.b("\n" + i);t.b(" <svg><use xlink:href=\"#icon-pause\"></use></svg>");t.b("\n" + i);t.b(" <span class=\"sr-only\">Pause</span>");t.b("\n" + i);t.b(" </button>");t.b("\n" + i);t.b(" <button type=\"button\" data-player=\"fast-forward\">");t.b("\n" + i);t.b(" <svg><use xlink:href=\"#icon-fast-forward\"></use></svg>");t.b("\n" + i);t.b(" <span class=\"sr-only\">Fast forward <span class=\"player-seek-time\">10</span> seconds</span>");t.b("\n" + i);t.b(" </button>");t.b("\n" + i);t.b(" <span class=\"player-time\">");t.b("\n" + i);t.b(" <span class=\"sr-only\">Time</span>");t.b("\n" + i);t.b(" <span class=\"player-duration\">00:00</span>");t.b("\n" + i);t.b(" </span>");t.b("\n" + i);t.b(" </span>");t.b("\n" + i);t.b(" <span class=\"player-controls-sound\">");t.b("\n" + i);t.b(" <input class=\"inverted sr-only\" id=\"mute{id}\" type=\"checkbox\" data-player=\"mute\">");t.b("\n" + i);t.b(" <label id=\"mute{id}\" for=\"mute{id}\">");t.b("\n" + i);t.b(" <svg class=\"icon-muted\"><use xlink:href=\"#icon-muted\"></use></svg>");t.b("\n" + i);t.b(" <svg><use xlink:href=\"#icon-sound\"></use></svg>");t.b("\n" + i);t.b(" <span class=\"sr-only\">Mute</span>");t.b("\n" + i);t.b(" </label>");t.b("\n");t.b("\n" + i);t.b(" <label for=\"volume{id}\" class=\"sr-only\">Volume:</label>");t.b("\n" + i);t.b(" <input id=\"volume{id}\" class=\"player-volume\" type=\"range\" min=\"0\" max=\"10\" value=\"5\" data-player=\"volume\">");t.b("\n");t.b("\n" + i);t.b(" <input class=\"sr-only\" id=\"captions{id}\" type=\"checkbox\" data-player=\"captions\">");t.b("\n" + i);t.b(" <label for=\"captions{id}\">");t.b("\n" + i);t.b(" <svg><use xlink:href=\"#icon-bubble\"></use></svg>");t.b("\n" + i);t.b(" <span class=\"sr-only\">Captions</span>");t.b("\n" + i);t.b(" </label>");t.b("\n");t.b("\n" + i);t.b(" <button type=\"button\" data-player=\"fullscreen\">");t.b("\n" + i);t.b(" <svg class=\"icon-exit-fullscreen\"><use xlink:href=\"#icon-collapse\"></use></svg>");t.b("\n" + i);t.b(" <svg><use xlink:href=\"#icon-expand\"></use></svg>");t.b("\n" + i);t.b(" <span class=\"sr-only\">Toggle fullscreen</span>");t.b("\n" + i);t.b(" </button>");t.b("\n" + i);t.b(" </span>");t.b("\n" + i);t.b("</div>");return t.fl(); },partials: {}, subs: { }});
\ No newline at end of file + templates['controls'] = new Hogan.Template({code: function (c,p,i) { var t=this;t.b(i=i||"");t.b("<div class=\"player-controls\">");t.b("\n" + i);t.b(" <div class=\"player-progress\">");t.b("\n" + i);t.b(" <progress class=\"player-progress-played\" max=\"100\" value=\"0\">");t.b("\n" + i);t.b(" <span>0</span>% played");t.b("\n" + i);t.b(" </progress>");t.b("\n" + i);t.b(" <progress class=\"player-progress-buffer\" max=\"100\" value=\"0\">");t.b("\n" + i);t.b(" <span>0</span>% buffered");t.b("\n" + i);t.b(" </progress>");t.b("\n" + i);t.b(" </div>");t.b("\n" + i);t.b(" <span class=\"player-controls-playback\">");t.b("\n" + i);t.b(" <button type=\"button\" data-player=\"restart\">");t.b("\n" + i);t.b(" <svg><use xlink:href=\"#icon-refresh\"></use></svg>");t.b("\n" + i);t.b(" <span class=\"sr-only\">Restart</span>");t.b("\n" + i);t.b(" </button>");t.b("\n" + i);t.b(" <button type=\"button\" data-player=\"rewind\">");t.b("\n" + i);t.b(" <svg><use xlink:href=\"#icon-rewind\"></use></svg>");t.b("\n" + i);t.b(" <span class=\"sr-only\">Rewind <span class=\"player-seek-time\">{seektime}</span> seconds</span>");t.b("\n" + i);t.b(" </button>");t.b("\n" + i);t.b(" <button type=\"button\" data-player=\"play\">");t.b("\n" + i);t.b(" <svg><use xlink:href=\"#icon-play\"></use></svg>");t.b("\n" + i);t.b(" <span class=\"sr-only\">Play</span>");t.b("\n" + i);t.b(" </button>");t.b("\n" + i);t.b(" <button type=\"button\" data-player=\"pause\">");t.b("\n" + i);t.b(" <svg><use xlink:href=\"#icon-pause\"></use></svg>");t.b("\n" + i);t.b(" <span class=\"sr-only\">Pause</span>");t.b("\n" + i);t.b(" </button>");t.b("\n" + i);t.b(" <button type=\"button\" data-player=\"fast-forward\">");t.b("\n" + i);t.b(" <svg><use xlink:href=\"#icon-fast-forward\"></use></svg>");t.b("\n" + i);t.b(" <span class=\"sr-only\">Fast forward <span class=\"player-seek-time\">{seektime}</span> seconds</span>");t.b("\n" + i);t.b(" </button>");t.b("\n" + i);t.b(" <span class=\"player-time\">");t.b("\n" + i);t.b(" <span class=\"sr-only\">Time</span>");t.b("\n" + i);t.b(" <span class=\"player-duration\">00:00</span>");t.b("\n" + i);t.b(" </span>");t.b("\n" + i);t.b(" </span>");t.b("\n" + i);t.b(" <span class=\"player-controls-sound\">");t.b("\n" + i);t.b(" <input class=\"inverted sr-only\" id=\"mute{id}\" type=\"checkbox\" data-player=\"mute\">");t.b("\n" + i);t.b(" <label id=\"mute{id}\" for=\"mute{id}\">");t.b("\n" + i);t.b(" <svg class=\"icon-muted\"><use xlink:href=\"#icon-muted\"></use></svg>");t.b("\n" + i);t.b(" <svg><use xlink:href=\"#icon-sound\"></use></svg>");t.b("\n" + i);t.b(" <span class=\"sr-only\">Mute</span>");t.b("\n" + i);t.b(" </label>");t.b("\n");t.b("\n" + i);t.b(" <label for=\"volume{id}\" class=\"sr-only\">Volume</label>");t.b("\n" + i);t.b(" <input id=\"volume{id}\" class=\"player-volume\" type=\"range\" min=\"0\" max=\"10\" value=\"5\" data-player=\"volume\">");t.b("\n");t.b("\n" + i);t.b(" <input class=\"sr-only\" id=\"captions{id}\" type=\"checkbox\" data-player=\"captions\">");t.b("\n" + i);t.b(" <label for=\"captions{id}\">");t.b("\n" + i);t.b(" <svg><use xlink:href=\"#icon-bubble\"></use></svg>");t.b("\n" + i);t.b(" <span class=\"sr-only\">Captions</span>");t.b("\n" + i);t.b(" </label>");t.b("\n");t.b("\n" + i);t.b(" <button type=\"button\" data-player=\"fullscreen\">");t.b("\n" + i);t.b(" <svg class=\"icon-exit-fullscreen\"><use xlink:href=\"#icon-collapse\"></use></svg>");t.b("\n" + i);t.b(" <svg><use xlink:href=\"#icon-expand\"></use></svg>");t.b("\n" + i);t.b(" <span class=\"sr-only\">Toggle fullscreen</span>");t.b("\n" + i);t.b(" </button>");t.b("\n" + i);t.b(" </span>");t.b("\n" + i);t.b("</div>");return t.fl(); },partials: {}, subs: { }});
\ No newline at end of file @@ -20,17 +20,17 @@ <section class="example-video"> <div class="player"> - <video poster="//cdn.sampotts.me/plyr/poster.jpg" controls crossorigin> + <video poster="//cdn.selz.com/plyr/1.0/poster.jpg" controls crossorigin> <!-- Video files --> - <source src="//cdn.sampotts.me/plyr/movie.mp4" type="video/mp4"> - <source src="//cdn.sampotts.me/plyr/movie.webm" type="video/webm"> + <source src="//cdn.selz.com/plyr/1.0/movie.mp4" type="video/mp4"> + <source src="//cdn.selz.com/plyr/1.0/movie.webm" type="video/webm"> <!-- Text track file --> - <track kind="captions" label="English" srclang="en" src="//cdn.sampotts.me/plyr/movie_en_captions.vtt" default> + <track kind="captions" label="English" srclang="en" src="//cdn.selz.com/plyr/1.0/movie_en_captions.vtt" default> <!-- Fallback for browsers that don't support the <video> element --> <div> - <a href="//cdn.sampotts.me/plyr/movie.mp4">Download</a> + <a href="//cdn.selz.com/plyr/1.0/movie.mp4">Download</a> </div> </video> </div> @@ -41,18 +41,25 @@ <div class="player"> <audio controls> <!-- Audio files --> - <source src="//cdn.sampotts.me/plyr/logistics-96-sample.mp3" type="audio/mp3"> - <source src="//cdn.sampotts.me/plyr/logistics-96-sample.ogg" type="application/ogg"> + <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 --> <div> - <a href="//cdn.sampotts.me/plyr/logistics-96-sample.mp3">Download</a> + <a href="//cdn.selz.com/plyr/1.0/logistics-96-sample.mp3">Download</a> </div> </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> + <footer> + <p>Used by …</p> + <a href="https://selz.com" target="_blank" class="logo"> + <img src="https://d33i624pw6jj68.cloudfront.net/static/img/logos/selz.svg" alt="Selz"> + </a> + </footer> + <!-- Load SVG defs --> <script> (function(d,p){var a=new XMLHttpRequest(),b=d.body;a.open("GET",p,!0);a.send();a.onload=function(){var c=d.createElement("div");c.style.display="none";c.innerHTML=a.responseText;b.insertBefore(c,b.childNodes[0])}})(document,"dist/sprite.svg"); @@ -63,15 +70,5 @@ <!-- Docs setup --> <script src="dist/js/docs.js"></script> - - <!-- GA --> - <script> - (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'); - </script> </body> </html>
\ No newline at end of file diff --git a/package.json b/package.json index b6c2165a..1fc1e99d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "plyr", - "version": "1.0.11", + "version": "1.0.16", "description": "A simple HTML5 media player using custom controls", "homepage": "http://plyr.io", "main": "gulpfile.js", @@ -11,7 +11,8 @@ We wanted a lightweight, accessible and customisable media player that just supp - **Lightweight** - just 4.8KB minified and gzipped. - **Customisable** - make the player look how you want with the markup you want. - **Semantic** - uses HTML5 form inputs for volume (range) and progress element for playback progress. -- **No dependencies** - written in native JS. +- **Responsive** - any screen size. +- **No dependencies** - written in vanilla JavaScript. - **API** - easy to use API. - **Fallback** - if there's no support, the native players are used. - **Fullscreen** - options to run the player full browser or the user can toggle fullscreen. @@ -23,6 +24,7 @@ Check out [the changelog](changelog.md) - Accept a string selector, a node, or a nodelist for the `container` property of `selectors`. - Accept a selector for the `html` template property. - Multiple language captions (with selection) +- Localisation of control labels If you have any cool ideas or features, please let me know by [creating an issue](https://github.com/Selz/plyr/issues/new) or of course, forking and sending a pull request. @@ -36,10 +38,10 @@ bower install plyr More info on setting up dependencies can be found in the [Bower Docs](http://bower.io/docs/creating-packages/#maintaining-dependencies) ### CSS -If you want to use the default css, add the css file from /dist into your head, or even better use the less file included in /assets in your build to save a request. +If you want to use the default css, add the css file from /dist into your head, or even better use the less file included in `/src` in your build to save a request. ```html -<link rel="stylesheet" href="/css/plyr.css" /> +<link rel="stylesheet" href="dist/css/plyr.css"> ``` ### SVG @@ -58,7 +60,7 @@ The SVG sprite for the controls icons is loaded in by AJAX to help with performa c.innerHTML=a.responseText; b.insertBefore(c,b.childNodes[0]) } -})(document,"/svg/sprite.svg"); +})(document,"dist/svg/sprite.svg"); </script> ``` More info on SVG sprites here: @@ -70,19 +72,17 @@ and the AJAX technique here: 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="//cdn.sampotts.me/plyr/poster.jpg" controls crossorigin> + <video poster="//cdn.selz.com/plyr/1.0/poster.jpg" controls crossorigin> <!-- Video files --> - <source src="//cdn.sampotts.me/plyr/movie.mp4" type="video/mp4"> - <source src="//cdn.sampotts.me/plyr/movie.webm" type="video/webm"> + <source src="//cdn.selz.com/plyr/1.0/movie.mp4" type="video/mp4"> + <source src="//cdn.selz.com/plyr/1.0/movie.webm" type="video/webm"> <!-- Text track file --> - <track kind="captions" label="English captions" src="//cdn.sampotts.me/plyr/movie_captions_en.vtt" srclang="en" default> + <track kind="captions" label="English captions" src="//cdn.selz.com/plyr/1.0/movie_captions_en.vtt" srclang="en" default> <!-- Fallback for browsers that don't support the <video> element --> <div> - <a href="//cdn.sampotts.me/plyr/movie.mp4"> - <img src="//cdn.sampotts.me/plyr/poster.jpg" alt="Download"> - </a> + <a href="//cdn.selz.com/plyr/1.0/movie.mp4">Download</a> </div> </video> </div> @@ -93,11 +93,12 @@ And the same for `<audio>` <div class="player"> <audio controls> <!-- Audio files --> - <source src="//cdn.sampotts.me/plyr/logistics-96-sample.mp3" type="audio/mp3"> + <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 --> <div> - <a href="//cdn.sampotts.me/plyr/logistics-96-sample.mp3">Download</a> + <a href="//cdn.selz.com/plyr/1.0/logistics-96-sample.mp3">Download</a> </div> </audio> </div> @@ -113,14 +114,15 @@ More info on CORS here: Much of the behaviour of the player is configurable when initialising the library. Below is an example of a default instance. ```html -<script src="js/plyr.js"></script> +<script src="dist/js/plyr.js"></script> <script> plyr.setup({ html: **your controls html** }); </script> ``` -You can pass the following settings: + +#### Options <table class="table" width="100%"> <thead> @@ -142,11 +144,7 @@ You can pass the following settings: <td><code>html</code></td> <td>String</td> <td><code>—</code></td> - <td>This is **required**. It is the markup used for the controls. In the demo, we use Hogan templates as we are already using them. You can of course, just specify vanilla html. The only requirement is your selectors should match the `selectors` option below. If you check `controls.html` in `/assets/templates` to get an idea of how the default html works. - - You need to add two placeholders to your html template: - - {id} for the dynamically generated ID for the player (for form controls) - - {aria_label} for the dynamically generated play button label for screen readers</td> + <td>This is **required**. See <a href="controls.md">controls.md</a> for more info on how the html needs to be structured.</td> </tr> <tr> <td><code>debug</code></td> @@ -155,7 +153,7 @@ You can pass the following settings: <td>Display debugging information on what Plyr is doing.</td> </tr> <tr> - <td><code>seekInterval</code></td> + <td><code>seekTime</code></td> <td>Number</td> <td><code>10</code></td> <td>The time, in seconds, to seek when a user hits fast forward or rewind.</td> @@ -176,7 +174,7 @@ You can pass the following settings: <td><code>selectors</code></td> <td>Object</td> <td>—</td> - <td>See `plyr.js` in `/assets` for more info. The only option you might want to change is `player` which is the hook used for Plyr, the default is `.player`.</td> + <td>See `plyr.js` in `/src` for more info. The only option you might want to change is `player` which is the hook used for Plyr, the default is `.player`.</td> </tr> <tr> <td><code>classes</code></td> @@ -194,7 +192,13 @@ You can pass the following settings: <td><code>fullscreen</code></td> <td>Object</td> <td>—</td> - <td>This currently contains one property `enabled` which toggles if fullscreen should be enabled (if the browser supports it). The default value is `true`.</td> + <td>This currently contains two properties; `enabled` which toggles if fullscreen should be enabled (if the browser supports it). The default value is `true`. Also an extra property called `fallback` which will enable a 'full window' view for older browsers. The default value is `true`.</td> + </tr> + <tr> + <td><code>storage</code></td> + <td>Object</td> + <td>—</td> + <td>This currently contains one property `enabled` which toggles if local storage should be enabled (if the browser supports it). The default value is `true`. This enables storing user settings, currently it only stores volume but more will be added later.</td> </tr> </tbody> </table> @@ -278,12 +282,34 @@ A complete list of events can be found here: ## 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. -## Support -- Chrome: full support -- Safari: full support -- Firefox: full support -- Internet Explorer 10, 11: full support -- Internet Explorer 9: native player used (no support for `<progress>` or `<input type="range">`) +## Browser support + +<table width="100%" style="text-align: center;"> + <thead> + <tr> + <td>Safari</td> + <td>Firefox</td> + <td>Chrome</td> + <td>Opera</td> + <td>IE9</td> + <td>IE10+</td> + </tr> + </thead> + <tbody> + <tr> + <td>✔</td> + <td>✔</td> + <td>✔</td> + <td>✔</td> + <td>✖¹</td> + <td>✔²</td> + </tr> + </tbody> +</table> + +¹ Native player used (no support for `<progress>` or `<input type="range">`) + +² 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: @@ -298,11 +324,22 @@ Any unsupported browsers will display links to download the media if the correct If you find anything weird with Plyr, please let us know using the Github issues tracker. ## Author -This was created by Sam Potts ([@sam_potts](https://twitter.com/sam_potts)) +Plyr is developed by Sam Potts ([@sam_potts](https://twitter.com/sam_potts)) ([sampotts.me](http://sampotts.me)) + +## Mentions +- [The Changelog](http://thechangelog.com/plyr-simple-html5-media-player-custom-controls-webvtt-captions/) +- [HTML5 Weekly #177](http://html5weekly.com/issues/177) +- [Web Design Weekly #174](https://web-design-weekly.com/2015/02/24/web-design-weekly-174/) + +## 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 :-) ## Useful links and credits Credit to the PayPal HTML5 Video player from which Plyr's caption functionality is ported from: - [PayPal's Accessible HTML5 Video Player](https://github.com/paypal/accessible-html5-video-player) +- The icons used in Plyr are [Vicons](https://dribbble.com/shots/1663443-60-Vicons-Free-Icon-Set) plus some ones I made Also these links helped created Plyr: - [Media Events - W3.org](http://www.w3.org/2010/05/video/mediaevents.html) diff --git a/src/js/docs.js b/src/js/docs.js index 89803ee2..fa19a5b6 100644 --- a/src/js/docs.js +++ b/src/js/docs.js @@ -12,4 +12,15 @@ plyr.setup({ captions: { defaultActive: true } -});
\ No newline at end of file +}); + +// Google analytics +// For demo site (http://plyr.io) only +if(document.domain === "plyr.io") { + (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"); +}
\ No newline at end of file diff --git a/src/js/plyr.js b/src/js/plyr.js index 01d7042c..dba1d69f 100644 --- a/src/js/plyr.js +++ b/src/js/plyr.js @@ -1,1123 +1,1148 @@ // ========================================================================== // Plyr -// plyr.js v1.0.11 +// plyr.js v1.0.16 // https://github.com/sampotts/plyr // ========================================================================== // Credits: http://paypal.github.io/accessible-html5-video-player/ // ========================================================================== (function (api) { - "use strict"; - - // Globals - var fullscreen, config; - - // Default config - var defaults = { - enabled: true, - debug: false, - seekInterval: 10, - volume: 5, - click: true, - selectors: { - container: ".player", - controls: ".player-controls", - buttons: { - 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", - duration: ".player-duration", - seekTime: ".player-seek-time" - }, - classes: { - video: "player-video", - videoWrapper: "player-video-wrapper", - audio: "player-audio", - stopped: "stopped", - playing: "playing", - muted: "muted", - captions: { - enabled: "captions-enabled", - active: "captions-active" - }, - fullscreen: { - enabled: "fullscreen-enabled", - active: "fullscreen-active" - } - }, - captions: { - defaultActive: false - }, - fullscreen: { - enabled: true, - fallback: true - }, - storage: { - enabled: true, - supported: function() { - try { - return "localStorage" in window && window.localStorage !== null; - } - catch(e) { - return false; - } - } - } - }; - - // Debugging - function _log(text, error) { - if(config.debug && window.console) { - console[(error ? "error" : "log")](text); - } - } - - // Credits: http://paypal.github.io/accessible-html5-video-player/ - // Unfortunately, due to scattered support, browser sniffing is required - function _browserSniff() { - var nAgt = navigator.userAgent, - browserName = navigator.appName, - fullVersion = ""+parseFloat(navigator.appVersion), - majorVersion = parseInt(navigator.appVersion,10), - nameOffset, - verOffset, - ix; - - // MSIE 11 - if ((navigator.appVersion.indexOf("Windows NT") !== -1) && (navigator.appVersion.indexOf("rv:11") !== -1)) { - browserName = "IE"; - fullVersion = "11;"; - } - // MSIE - else if ((verOffset=nAgt.indexOf("MSIE")) !== -1) { - browserName = "IE"; - fullVersion = nAgt.substring(verOffset+5); - } - // Chrome - else if ((verOffset=nAgt.indexOf("Chrome")) !== -1) { - browserName = "Chrome"; - fullVersion = nAgt.substring(verOffset+7); - } - // Safari - else if ((verOffset=nAgt.indexOf("Safari")) !== -1) { - browserName = "Safari"; - fullVersion = nAgt.substring(verOffset+7); - if ((verOffset=nAgt.indexOf("Version")) !== -1) { - fullVersion = nAgt.substring(verOffset+8); - } - } - // Firefox - else if ((verOffset=nAgt.indexOf("Firefox")) !== -1) { - browserName = "Firefox"; - fullVersion = nAgt.substring(verOffset+8); - } - // In most other browsers, "name/version" is at the end of userAgent - else if ( (nameOffset=nAgt.lastIndexOf(" ")+1) < (verOffset=nAgt.lastIndexOf("/")) ) { - browserName = nAgt.substring(nameOffset,verOffset); - fullVersion = nAgt.substring(verOffset+1); - if (browserName.toLowerCase()==browserName.toUpperCase()) { - browserName = navigator.appName; - } - } - // Trim the fullVersion string at semicolon/space if present - if ((ix=fullVersion.indexOf(";")) !== -1) { - fullVersion=fullVersion.substring(0,ix); - } - if ((ix=fullVersion.indexOf(" ")) !== -1) { - fullVersion=fullVersion.substring(0,ix); - } - // Get major version - majorVersion = parseInt(""+fullVersion,10); - if (isNaN(majorVersion)) { - fullVersion = ""+parseFloat(navigator.appVersion); - majorVersion = parseInt(navigator.appVersion,10); - } - // Return data - return [browserName, majorVersion]; - } - - // Replace all - function _replaceAll(string, find, replace) { - return string.replace(new RegExp(find.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"), "g"), replace); - } - - // Wrap an element - function _wrap(elements, wrapper) { - // Convert `elms` 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 el = elements[i]; - - // Cache the current parent and sibling. - var parent = el.parentNode; - var sibling = el.nextSibling; - - // Wrap the element (is automatically removed from its current - // parent). - child.appendChild(el); - - // 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); - } - } - } - - // Toggle class on an element - function _toggleClass(element, name, 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 : ""); - } - } - } - - // Bind event - function _on(element, event, callback) { - element.addEventListener(event, callback, false); - } - - // Unbind event - function _off(element, event, callback) { - element.removeEventListener(event, callback, false); - } - - // Get percentage - function _getPercentage(current, max) { - return Math.floor((current / max) * 100); - } - - // Get click position relative to parent - // http://www.kirupa.com/html5/getting_mouse_click_position.htm - function _getClickPosition(event) { - var parentPosition = _fullscreen().isFullScreen() ? { x: 0, y: 0 } : _getPosition(event.currentTarget); - - return { - x: event.clientX - parentPosition.x, - y: event.clientY - parentPosition.y - }; - } - // Get element position - function _getPosition(element) { - var xPosition = 0; - var yPosition = 0; - - while (element) { - xPosition += (element.offsetLeft - element.scrollLeft + element.clientLeft); - yPosition += (element.offsetTop - element.scrollTop + element.clientTop); - element = element.offsetParent; - } - - return { - x: xPosition, - y: yPosition - }; - } - - // Deep extend/merge two Objects - // http://andrewdupont.net/2009/08/28/deep-extending-objects-in-javascript/ - // Removed call to arguments.callee (used explicit function name instead) + "use strict"; + + // Globals + var fullscreen, config; + + // Default config + var defaults = { + enabled: true, + debug: false, + seekTime: 10, + volume: 5, + click: true, + selectors: { + container: ".player", + controls: ".player-controls", + buttons: { + 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", + duration: ".player-duration", + seekTime: ".player-seek-time" + }, + classes: { + video: "player-video", + videoWrapper: "player-video-wrapper", + audio: "player-audio", + stopped: "stopped", + playing: "playing", + muted: "muted", + captions: { + enabled: "captions-enabled", + active: "captions-active" + }, + fullscreen: { + enabled: "fullscreen-enabled", + active: "fullscreen-active" + } + }, + captions: { + defaultActive: false + }, + fullscreen: { + enabled: true, + fallback: true + }, + storage: { + enabled: true + } + }; + + // Debugging + function _log(text, error) { + if(config.debug && window.console) { + console[(error ? "error" : "log")](text); + } + } + + // Credits: http://paypal.github.io/accessible-html5-video-player/ + // Unfortunately, due to scattered support, browser sniffing is required + function _browserSniff() { + var nAgt = navigator.userAgent, + browserName = navigator.appName, + fullVersion = ""+parseFloat(navigator.appVersion), + majorVersion = parseInt(navigator.appVersion,10), + nameOffset, + verOffset, + ix; + + // MSIE 11 + if ((navigator.appVersion.indexOf("Windows NT") !== -1) && (navigator.appVersion.indexOf("rv:11") !== -1)) { + browserName = "IE"; + fullVersion = "11;"; + } + // MSIE + else if ((verOffset=nAgt.indexOf("MSIE")) !== -1) { + browserName = "IE"; + fullVersion = nAgt.substring(verOffset+5); + } + // Chrome + else if ((verOffset=nAgt.indexOf("Chrome")) !== -1) { + browserName = "Chrome"; + fullVersion = nAgt.substring(verOffset+7); + } + // Safari + else if ((verOffset=nAgt.indexOf("Safari")) !== -1) { + browserName = "Safari"; + fullVersion = nAgt.substring(verOffset+7); + if ((verOffset=nAgt.indexOf("Version")) !== -1) { + fullVersion = nAgt.substring(verOffset+8); + } + } + // Firefox + else if ((verOffset=nAgt.indexOf("Firefox")) !== -1) { + browserName = "Firefox"; + fullVersion = nAgt.substring(verOffset+8); + } + // In most other browsers, "name/version" is at the end of userAgent + else if ( (nameOffset=nAgt.lastIndexOf(" ")+1) < (verOffset=nAgt.lastIndexOf("/")) ) { + browserName = nAgt.substring(nameOffset,verOffset); + fullVersion = nAgt.substring(verOffset+1); + if (browserName.toLowerCase()==browserName.toUpperCase()) { + browserName = navigator.appName; + } + } + // Trim the fullVersion string at semicolon/space if present + if ((ix=fullVersion.indexOf(";")) !== -1) { + fullVersion=fullVersion.substring(0,ix); + } + if ((ix=fullVersion.indexOf(" ")) !== -1) { + fullVersion=fullVersion.substring(0,ix); + } + // Get major version + majorVersion = parseInt(""+fullVersion,10); + if (isNaN(majorVersion)) { + fullVersion = ""+parseFloat(navigator.appVersion); + majorVersion = parseInt(navigator.appVersion,10); + } + // Return data + return [browserName, majorVersion]; + } + + // Replace all + function _replaceAll(string, find, replace) { + return string.replace(new RegExp(find.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"), "g"), replace); + } + + // Wrap an element + function _wrap(elements, wrapper) { + // Convert `elms` 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 el = elements[i]; + + // Cache the current parent and sibling. + var parent = el.parentNode; + var sibling = el.nextSibling; + + // Wrap the element (is automatically removed from its current + // parent). + child.appendChild(el); + + // 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); + } + } + } + + // Toggle class on an element + function _toggleClass(element, name, 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 : ""); + } + } + } + + // Toggle event + function _toggleHandler(element, events, callback, toggle) { + events = events.split(" "); + + for (var i = 0; i < events.length; i++) { + element[toggle ? "addEventListener" : "removeEventListener"](events[i], callback, false); + } + } + + // Bind event + function _on(element, events, callback) { + _toggleHandler(element, events, callback, true); + } + + // Unbind event + function _off(element, events, callback) { + _toggleHandler(element, events, callback, false); + } + + // Get percentage + function _getPercentage(current, max) { + return Math.floor((current / max) * 100); + } + + // Get click position relative to parent + // http://www.kirupa.com/html5/getting_mouse_click_position.htm + function _getClickPosition(event) { + var parentPosition = _fullscreen().isFullScreen() ? { x: 0, y: 0 } : _getPosition(event.currentTarget); + + return { + x: event.clientX - parentPosition.x, + y: event.clientY - parentPosition.y + }; + } + // Get element position + function _getPosition(element) { + var xPosition = 0; + var yPosition = 0; + + while (element) { + xPosition += (element.offsetLeft - element.scrollLeft + element.clientLeft); + yPosition += (element.offsetTop - element.scrollTop + element.clientTop); + element = element.offsetParent; + } + + return { + x: xPosition, + y: yPosition + }; + } + + // Deep extend/merge two Objects + // http://andrewdupont.net/2009/08/28/deep-extending-objects-in-javascript/ + // Removed call to arguments.callee (used explicit function name instead) function _extend(destination, source) { - for (var property in source) { - if (source[property] && source[property].constructor && source[property].constructor === Object) { - destination[property] = destination[property] || {}; - _extend(destination[property], source[property]); - } - else { - destination[property] = source[property]; - } - } - return destination; - } - - // Fullscreen API - function _fullscreen() { - var fullscreen = { - supportsFullScreen: false, - isFullScreen: function() { return false; }, - requestFullScreen: function() {}, - cancelFullScreen: function() {}, - fullScreenEventName: "", - element: null, - prefix: "" - }, - browserPrefixes = "webkit moz o ms khtml".split(" "); - - // check for native support - if (typeof document.cancelFullScreen != "undefined") { - fullscreen.supportsFullScreen = true; - } - else { - // check for fullscreen support by vendor prefix - for (var i = 0, il = browserPrefixes.length; i < il; i++ ) { - fullscreen.prefix = browserPrefixes[i]; - - if (typeof document[fullscreen.prefix + "CancelFullScreen"] != "undefined") { - fullscreen.supportsFullScreen = true; - break; - } - // Special case for MS (when isn't it?) - else if (typeof document.msExitFullscreen != "undefined" && document.msFullscreenEnabled) { - fullscreen.prefix = "ms"; - fullscreen.supportsFullScreen = true; - break; - } - } - } - - // Safari doesn't support the ALLOW_KEYBOARD_INPUT flag so set it to not supported - // https://bugs.webkit.org/show_bug.cgi?id=121496 - if(fullscreen.prefix === "webkit" && !!navigator.userAgent.match(/Version\/[\d\.]+.*Safari/)) { - fullscreen.supportsFullScreen = false; - } - - // Update methods to do something useful - if (fullscreen.supportsFullScreen) { - // Yet again Microsoft awesomeness, - // Sometimes the prefix is "ms", sometimes "MS" to keep you on your toes - fullscreen.fullScreenEventName = (fullscreen.prefix == "ms" ? "MSFullscreenChange" : fullscreen.prefix + "fullscreenchange"); - - fullscreen.isFullScreen = function() { - switch (this.prefix) { - case "": - return document.fullScreen; - case "webkit": - return document.webkitIsFullScreen; - case "ms": - // Docs say document.msFullScreenElement returns undefined - // if no element is full screem but it returns null, cheers - // https://msdn.microsoft.com/en-us/library/ie/dn265028%28v=vs.85%29.aspx - return (document.msFullscreenElement !== null); - default: - return document[this.prefix + "FullScreen"]; - } - }; - fullscreen.requestFullScreen = function(element) { - return (this.prefix === "") ? element.requestFullScreen() : element[this.prefix + (this.prefix == "ms" ? "RequestFullscreen" : "RequestFullScreen")](this.prefix === "webkit" ? element.ALLOW_KEYBOARD_INPUT : null); - }; - fullscreen.cancelFullScreen = function() { - return (this.prefix === "") ? document.cancelFullScreen() : document[this.prefix + (this.prefix == "ms" ? "ExitFullscreen" : "CancelFullScreen")](); - }; - fullscreen.element = function() { - return (this.prefix === "") ? document.fullscreenElement : document[this.prefix + "FullscreenElement"]; - }; - } - - return fullscreen; - } - - // Player instance - function Plyr(container) { - var player = this; - player.container = container; - - // Captions functions - // Credits: http://paypal.github.io/accessible-html5-video-player/ - - // For "manual" captions, adjust caption position when play time changed (via rewind, clicking progress bar, etc.) - function _adjustManualCaptions() { - player.subcount = 0; - while (_timecodeMax(player.captions[player.subcount][0]) < player.media.currentTime.toFixed(1)) { - player.subcount++; - if (player.subcount > player.captions.length-1) { - player.subcount = player.captions.length-1; - break; - } - } - } - // Display captions container and button (for initialization) - function _showCaptions() { - _toggleClass(player.container, config.classes.captions.enabled, true); - - if (config.captions.defaultActive) { - _toggleClass(player.container, config.classes.captions.active, true); - player.buttons.captions.setAttribute("checked", "checked"); - } - } - // Utilities for caption time codes - function _timecodeMin(tc) { - var tcpair = []; - tcpair = tc.split(" --> "); - return _subTcSecs(tcpair[0]); - } - function _timecodeMax(tc) { - var tcpair = []; - tcpair = tc.split(" --> "); - return _subTcSecs(tcpair[1]); - } - function _subTcSecs(tc) { - if (tc === null || tc === undefined) { - return 0; - } - else { - var tc1 = [], - tc2 = [], - seconds; - tc1 = tc.split(","); - tc2 = tc1[0].split(":"); - seconds = Math.floor(tc2[0]*60*60) + Math.floor(tc2[1]*60) + Math.floor(tc2[2]); - return seconds; - } - } - - // Find all elements - function _getElements(selector) { - return player.container.querySelectorAll(selector); - } - - // Find a single element - function _getElement(selector) { - return _getElements(selector)[0]; - } - - // Determine if we're in an iframe - function _inFrame() { - try { - return window.self !== window.top; - } - catch (e) { - return true; - } - } - - // Insert controls - function _injectControls() { - // Insert custom video controls - _log("Injecting custom controls."); - - // Use specified html - // Need to do a default? - var html = config.html; - - // Replace aria label instances - html = _replaceAll(html, "{aria-label}", config.playAriaLabel); - - // Replace all id references - html = _replaceAll(html, "{id}", player.random); - - // Inject into the container - player.container.insertAdjacentHTML("beforeend", html); - } - - // Find the UI controls and store references - function _findElements() { - try { - player.controls = _getElement(config.selectors.controls); - - // Buttons - player.buttons = {}; - 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.mute = _getElement(config.selectors.buttons.mute); - player.buttons.captions = _getElement(config.selectors.buttons.captions); - player.buttons.fullscreen = _getElement(config.selectors.buttons.fullscreen); - - // Progress - player.progress = {}; - player.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.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.getElementsByTagName("span")[0]; - - // Volume - player.volume = _getElement(config.selectors.buttons.volume); - - // Timing - player.duration = _getElement(config.selectors.duration); - player.seekTime = _getElements(config.selectors.seekTime); - - return true; - } - catch(e) { - _log("It looks like there's a problem with your controls html. Bailing.", true); - return false; - } - } - - // Setup media - function _setupMedia() { - player.media = player.container.querySelectorAll("audio, video")[0]; - - // If there's no media, bail - if(!player.media) { - _log("No audio or video element found!", true); - return false; - } - - // Remove native video controls - player.media.removeAttribute("controls"); - - // Set media type - player.type = (player.media.tagName.toLowerCase() == "video" ? "video" : "audio"); - - // Add type class - _toggleClass(player.container, config.classes[player.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)); - - // Inject the player wrapper - if(player.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); - - // Cache the container - player.videoContainer = wrapper; - } - } - - // Setup captions - function _setupCaptions() { - if(player.type === "video") { - // Inject the container - player.videoContainer.insertAdjacentHTML("afterbegin", "<div class='" + config.selectors.captions.replace(".", "") + "'></div>"); - - // Cache selector - player.captionsContainer = _getElement(config.selectors.captions); - - // Determine if HTML5 textTracks is supported - player.isTextTracks = false; - if (player.media.textTracks) { - player.isTextTracks = true; - } - - // Get URL of caption file if exists - var captionSrc = "", - kind, - children = player.media.childNodes; - - for (var i = 0; i < children.length; i++) { - if (children[i].nodeName.toLowerCase() === "track") { - kind = children[i].getAttribute("kind"); - if (kind === "captions") { - captionSrc = children[i].getAttribute("src"); - } - } - } - - // Record if caption file exists or not - player.captionExists = true; - if (captionSrc === "") { - player.captionExists = false; - _log("No caption track found."); - } - else { - _log("Caption track found; URI: " + captionSrc); - } - - // If no caption file exists, hide container for caption text - if (!player.captionExists) { - _toggleClass(player.container, config.classes.captions.enabled); - } - // If caption file exists, process captions - else { - // 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; - for (var x=0; x < tracks.length; x++) { - tracks[x].mode = "hidden"; - } - - // Enable UI - _showCaptions(player); - - // If IE 10/11 or Firefox 31+ or Safari 7+, don"t use native captioning (still doesn"t work although they claim it"s now supported) - if ((player.browserName === "IE" && player.browserMajorVersion === 10) || - (player.browserName === "IE" && player.browserMajorVersion === 11) || - (player.browserName === "Firefox" && player.browserMajorVersion >= 31) || - (player.browserName === "Safari" && player.browserMajorVersion >= 7)) { - // Debugging - _log("Detected IE 10/11 or Firefox 31+ or Safari 7+."); - - // Set to false so skips to "manual" captioning - player.isTextTracks = false; - } - - // Rendering caption tracks - // Native support required - http://caniuse.com/webvtt - if (player.isTextTracks) { - _log("TextTracks supported."); - - for (var y=0; y < tracks.length; y++) { - var track = tracks[y]; - - if (track.kind === "captions") { - _on(track, "cuechange", function() { - if (this.activeCues[0]) { - if (this.activeCues[0].hasOwnProperty("text")) { - player.captionsContainer.innerHTML = this.activeCues[0].text; - } - } - }); - } - } - } - // Caption tracks not natively supported - else { - _log("TextTracks not supported so rendering captions manually."); - - // Render captions from array at appropriate time - player.currentCaption = ""; - player.subcount = 0; - player.captions = []; - - _on(player.media, "timeupdate", function() { - // 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]; - } - // Is there a next timecode? - if (player.media.currentTime.toFixed(1) > _timecodeMax(player.captions[player.subcount][0]) && - player.subcount < (player.captions.length-1)) { - player.subcount++; - } - // Render the caption - player.captionsContainer.innerHTML = player.currentCaption; - }); - - if (captionSrc !== "") { - // Create XMLHttpRequest Object - var xhr = new XMLHttpRequest(); - - xhr.onreadystatechange = function() { - if (xhr.readyState === 4) { - if (xhr.status === 200) { - var records = [], - record, - req = xhr.responseText; - - records = req.split("\n\n"); - - for (var r=0; r < records.length; r++) { - record = records[r]; - player.captions[r] = []; - player.captions[r] = record.split("\n"); - } - - // Remove first element ("VTT") - player.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(); - } - } - - // If Safari 7+, removing track from DOM [see "turn off native caption rendering" above] - if (player.browserName === "Safari" && player.browserMajorVersion >= 7) { - _log("Safari 7+ detected; removing track from DOM."); - - // Find all <track> elements - tracks = player.media.getElementsByTagName("track"); - - // Loop through and remove one by one - for (var t=0; t < tracks.length; t++) { - player.media.removeChild(tracks[t]); - } - } - } - } - } - - // Setup seeking - function _setupSeeking() { - // Update number of seconds in rewind and fast forward buttons - player.seekTime[0].innerHTML = config.seekInterval; - player.seekTime[1].innerHTML = config.seekInterval; - } - - // Setup fullscreen - function _setupFullscreen() { - if(player.type === "video" && config.fullscreen.enabled) { - // Check for native support - var nativeSupport = fullscreen.supportsFullScreen; - - if(nativeSupport || (config.fullscreen.fallback && !_inFrame())) { - _log((nativeSupport ? "Native" : "Fallback") + " fullscreen enabled."); - - // Add styling hook - _toggleClass(player.container, config.classes.fullscreen.enabled, true); - } - else { - _log("Fullscreen not supported and fallback disabled."); - } - } - } - - // Play media - function _play() { - player.media.play(); - - _toggleClass(player.container, config.classes.stopped); - _toggleClass(player.container, config.classes.playing, true); - } - - // Pause media - function _pause() { - player.media.pause(); - - _toggleClass(player.container, config.classes.playing); - _toggleClass(player.container, config.classes.stopped, true); - } - - // Restart playback - function _restart() { - // Move to beginning - player.media.currentTime = 0; - - // Special handling for "manual" captions - if (!player.isTextTracks) { - player.subcount = 0; - } - - // Play and ensure the play button is in correct state - _play(); - } - - // Rewind - function _rewind(seekInterval) { - // Use default if needed - if(typeof seekInterval === "undefined") { - seekInterval = config.seekInterval; - } - - var targetTime = player.media.currentTime - seekInterval; - - if (targetTime < 0) { - player.media.currentTime = 0; - } - else { - player.media.currentTime = targetTime; - } - // Special handling for "manual" captions - if (!player.isTextTracks && player.type === "video") { - _adjustManualCaptions(player); - } - } - - // Fast forward - function _forward(seekInterval) { - // Use default if needed - if(typeof seekInterval === "undefined") { - seekInterval = config.seekInterval; - } - - var targetTime = player.media.currentTime + seekInterval; - - if (targetTime > player.media.duration) { - player.media.currentTime = player.media.duration; - } - else { - player.media.currentTime = targetTime; - } - // Special handling for "manual" captions - if (!player.isTextTracks && player.type === "video") { - _adjustManualCaptions(player); - } - } - - // Toggle fullscreen - function _toggleFullscreen() { - // Check for native support - var nativeSupport = fullscreen.supportsFullScreen; - - // If it's a fullscreen change event, it's probably a native close - if(event.type === fullscreen.fullScreenEventName) { - config.fullscreen.active = fullscreen.isFullScreen(); - } - // If there's native support, use it - else if(nativeSupport) { - // Request fullscreen - if(!fullscreen.isFullScreen()) { - fullscreen.requestFullScreen(player.container); - } - // Bail from fullscreen - else { - fullscreen.cancelFullScreen(); - } - - // Check if we're actually full screen (it could fail) - config.fullscreen.active = fullscreen.isFullScreen(); - } - else { - // Otherwise, it's a simple toggle - config.fullscreen.active = !config.fullscreen.active; - - // Bind/unbind escape key - if(config.fullscreen.active) { - _on(document, "keyup", _handleEscapeFullscreen); - document.body.style.overflow = "hidden"; - } - else { - _off(document, "keyup", _handleEscapeFullscreen); - document.body.style.overflow = ""; - } - } - - // Set class hook - _toggleClass(player.container, config.classes.fullscreen.active, config.fullscreen.active); - } - - // Bail from faux-fullscreen - function _handleEscapeFullscreen(event) { - // If it's a keypress and not escape, bail - if((event.which || event.charCode || event.keyCode) === 27 && config.fullscreen.active) { - _toggleFullscreen(); - } - } - - // Set volume - function _setVolume(volume) { - // Use default if needed - if(typeof volume === "undefined") { - if(config.storage.enabled && config.storage.supported) { - volume = window.localStorage.plyr_volume || config.volume; - } - else { - volume = config.volume; - } - } - // Maximum is 10 - if(volume > 10) { - volume = 10; - } - - player.volume.value = volume; - player.media.volume = parseFloat(volume / 10); - _checkMute(); - - // Store the volume in storage - if(config.storage.enabled && config.storage.supported) { - window.localStorage.plyr_volume = volume; - } - } - - // Mute - function _toggleMute(muted) { - // If the method is called without parameter, toggle based on current value - if(typeof active === "undefined") { - muted = !player.media.muted; - player.buttons.mute.checked = muted; - } - - player.media.muted = muted; - _checkMute(); - } - - // Toggle captions - function _toggleCaptions(active) { - // If the method is called without parameter, toggle based on current value - if(typeof active === "undefined") { - active = (player.container.className.indexOf(config.classes.captions.active) === -1); - player.buttons.captions.checked = active; - } - - if (active) { - _toggleClass(player.container, config.classes.captions.active, true); - } - else { - _toggleClass(player.container, config.classes.captions.active); - } - } - - // Check mute state - function _checkMute() { - _toggleClass(player.container, config.classes.muted, (player.media.volume === 0 || player.media.muted)); - } - - // Update <progress> elements - function _updateProgress(event) { - var progress, text, value = 0; - - switch(event.type) { - // Video playing - case "timeupdate": - progress = player.progress.played.bar; - text = player.progress.played.text; - value = _getPercentage(player.media.currentTime, player.media.duration); - break; - - // Check buffer status - case "playing": - case "progress": - progress = player.progress.buffer.bar; - text = player.progress.buffer.text; - value = (function() { - var buffered = player.media.buffered; - - if(buffered.length) { - return _getPercentage(buffered.end(0), player.media.duration); - } - - return 0; - })(); - break; - } - - if (progress && value > 0) { - progress.value = value; - text.innerHTML = value; - } - } - - // Update the displayed play time - function _updateTimeDisplay() { - player.secs = parseInt(player.media.currentTime % 60); - player.mins = parseInt((player.media.currentTime / 60) % 60); - - // 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); - - // Render - player.duration.innerHTML = player.mins + ":" + player.secs; - } - - // Listen for events - function _listeners() { - // Play - _on(player.buttons.play, "click", function() { - _play(); - player.buttons.pause.focus(); - }); - - // Pause - _on(player.buttons.pause, "click", function() { - _pause(); - player.buttons.play.focus(); - }); - - // Restart - _on(player.buttons.restart, "click", _restart); - - // Rewind - _on(player.buttons.rewind, "click", function() { - _rewind(config.seekInterval); - }); - - // Fast forward - _on(player.buttons.forward, "click", function() { - _forward(config.seekInterval); - }); - - // Get the HTML5 range input element and append audio volume adjustment on change - _on(player.volume, "change", function() { - _setVolume(this.value); - }); - - // Mute - _on(player.buttons.mute, "change", function() { - _toggleMute(this.checked); - }); - - // Fullscreen - _on(player.buttons.fullscreen, "click", _toggleFullscreen); - - // Handle user exiting fullscreen by escaping etc - _on(document, fullscreen.fullScreenEventName, _toggleFullscreen); - - // Click video - if(player.type === "video" && config.click) { - _on(player.videoContainer, "click", function() { - if(player.media.paused) { - _play(); - } - else if(player.media.ended) { - _restart(); - } - else { - _pause(); - } - }); - } - - // Duration - _on(player.media, "timeupdate", _updateTimeDisplay); - - // Playing progress - _on(player.media, "timeupdate", _updateProgress); - - // Skip when clicking progress bar - _on(player.progress.played.bar, "click", function(event) { - player.pos = _getClickPosition(event).x / this.offsetWidth; - player.media.currentTime = player.pos * player.media.duration; - - // Special handling for "manual" captions - if (!player.isTextTracks && player.type === "video") { - _adjustManualCaptions(player); - } - }); - - // Captions - _on(player.buttons.captions, "click", function() { - _toggleCaptions(this.checked); - }); - - // Clear captions at end of video - _on(player.media, "ended", function() { - if(player.type === "video") { - player.captionsContainer.innerHTML = ""; - } - _toggleClass(player.container, config.classes.stopped, true); - _toggleClass(player.container, config.classes.playing); - }); - - // Check for buffer progress - _on(player.media, "progress", _updateProgress); - // Also check on start of playing - _on(player.media, "playing", _updateProgress); - } - - function _init() { - // Setup the fullscreen api - fullscreen = _fullscreen(); - - // Sniff - player.browserInfo = _browserSniff(); - player.browserName = player.browserInfo[0]; - player.browserMajorVersion = player.browserInfo[1]; - - // Debug info - _log(player.browserName + " " + player.browserMajorVersion); - - // If IE8, stop customization (use fallback) - // If IE9, stop customization (use native controls) - if (player.browserName === "IE" && (player.browserMajorVersion === 8 || player.browserMajorVersion === 9) ) { - _log("Browser not suppported.", true); - return false; - } - - // Set up aria-label for Play button with the title option - if (typeof(config.title) === "undefined" || !config.title.length) { - config.playAriaLabel = "Play"; - } - else { - config.playAriaLabel = "Play " + config.title; - } - - // Setup media - _setupMedia(); - - // Generate random number for id/for attribute values for controls - player.random = Math.floor(Math.random() * (10000)); - - // Inject custom controls - _injectControls(); - - // Find the elements - if(!_findElements()) { - return false; - } - - // Captions - _setupCaptions(); - - // Set volume - _setVolume(); - - // Setup fullscreen - _setupFullscreen(); - - // Seeking - _setupSeeking(); - - // Listeners - _listeners(); - } - - _init(); - - return { - media: player.media, - play: _play, - pause: _pause, - restart: _restart, - rewind: _rewind, - forward: _forward, - setVolume: _setVolume, - toggleMute: _toggleMute, - toggleCaptions: _toggleCaptions - } - } - - // Expose setup function - api.setup = function(options){ - // Extend the default options with user specified - config = _extend(defaults, options); - - // If enabled carry on - // You may want to disable certain UAs etc - if(!config.enabled) { - 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 - var element = elements[i]; - - // Setup a player instance and add to the element - if(typeof element.plyr === "undefined") { - element.plyr = new Plyr(element); - } - - // Add to return array - players.push(element.plyr); - } - - return players; - } + for (var property in source) { + if (source[property] && source[property].constructor && source[property].constructor === Object) { + destination[property] = destination[property] || {}; + _extend(destination[property], source[property]); + } + else { + destination[property] = source[property]; + } + } + return destination; + } + + // Fullscreen API + function _fullscreen() { + var fullscreen = { + supportsFullScreen: false, + isFullScreen: function() { return false; }, + requestFullScreen: function() {}, + cancelFullScreen: function() {}, + fullScreenEventName: "", + element: null, + prefix: "" + }, + browserPrefixes = "webkit moz o ms khtml".split(" "); + + // check for native support + if (typeof document.cancelFullScreen != "undefined") { + fullscreen.supportsFullScreen = true; + } + else { + // check for fullscreen support by vendor prefix + for (var i = 0, il = browserPrefixes.length; i < il; i++ ) { + fullscreen.prefix = browserPrefixes[i]; + + if (typeof document[fullscreen.prefix + "CancelFullScreen"] != "undefined") { + fullscreen.supportsFullScreen = true; + break; + } + // Special case for MS (when isn't it?) + else if (typeof document.msExitFullscreen != "undefined" && document.msFullscreenEnabled) { + fullscreen.prefix = "ms"; + fullscreen.supportsFullScreen = true; + break; + } + } + } + + // Safari doesn't support the ALLOW_KEYBOARD_INPUT flag so set it to not supported + // https://bugs.webkit.org/show_bug.cgi?id=121496 + if(fullscreen.prefix === "webkit" && !!navigator.userAgent.match(/Version\/[\d\.]+.*Safari/)) { + fullscreen.supportsFullScreen = false; + } + + // Update methods to do something useful + if (fullscreen.supportsFullScreen) { + // Yet again Microsoft awesomeness, + // Sometimes the prefix is "ms", sometimes "MS" to keep you on your toes + fullscreen.fullScreenEventName = (fullscreen.prefix == "ms" ? "MSFullscreenChange" : fullscreen.prefix + "fullscreenchange"); + + fullscreen.isFullScreen = function() { + switch (this.prefix) { + case "": + return document.fullScreen; + case "webkit": + return document.webkitIsFullScreen; + case "ms": + // Docs say document.msFullScreenElement returns undefined + // if no element is full screem but it returns null, cheers + // https://msdn.microsoft.com/en-us/library/ie/dn265028%28v=vs.85%29.aspx + return (document.msFullscreenElement !== null); + default: + return document[this.prefix + "FullScreen"]; + } + }; + fullscreen.requestFullScreen = function(element) { + return (this.prefix === "") ? element.requestFullScreen() : element[this.prefix + (this.prefix == "ms" ? "RequestFullscreen" : "RequestFullScreen")](this.prefix === "webkit" ? element.ALLOW_KEYBOARD_INPUT : null); + }; + fullscreen.cancelFullScreen = function() { + return (this.prefix === "") ? document.cancelFullScreen() : document[this.prefix + (this.prefix == "ms" ? "ExitFullscreen" : "CancelFullScreen")](); + }; + fullscreen.element = function() { + return (this.prefix === "") ? document.fullscreenElement : document[this.prefix + "FullscreenElement"]; + }; + } + + return fullscreen; + } + + // Local storage + function _storage() { + var storage = { + supported: (function() { + try { + return "localStorage" in window && window.localStorage !== null; + } + catch(e) { + return false; + } + })() + } + return storage; + } + + // Player instance + function Plyr(container) { + var player = this; + player.container = container; + + // Captions functions + // Credits: http://paypal.github.io/accessible-html5-video-player/ + + // For "manual" captions, adjust caption position when play time changed (via rewind, clicking progress bar, etc.) + function _adjustManualCaptions() { + player.subcount = 0; + while (_timecodeMax(player.captions[player.subcount][0]) < player.media.currentTime.toFixed(1)) { + player.subcount++; + if (player.subcount > player.captions.length-1) { + player.subcount = player.captions.length-1; + break; + } + } + } + // Display captions container and button (for initialization) + function _showCaptions() { + _toggleClass(player.container, config.classes.captions.enabled, true); + + if (config.captions.defaultActive) { + _toggleClass(player.container, config.classes.captions.active, true); + player.buttons.captions.setAttribute("checked", "checked"); + } + } + // Utilities for caption time codes + function _timecodeMin(tc) { + var tcpair = []; + tcpair = tc.split(" --> "); + return _subTcSecs(tcpair[0]); + } + function _timecodeMax(tc) { + var tcpair = []; + tcpair = tc.split(" --> "); + return _subTcSecs(tcpair[1]); + } + function _subTcSecs(tc) { + if (tc === null || tc === undefined) { + return 0; + } + else { + var tc1 = [], + tc2 = [], + seconds; + tc1 = tc.split(","); + tc2 = tc1[0].split(":"); + seconds = Math.floor(tc2[0]*60*60) + Math.floor(tc2[1]*60) + Math.floor(tc2[2]); + return seconds; + } + } + + // Find all elements + function _getElements(selector) { + return player.container.querySelectorAll(selector); + } + + // Find a single element + function _getElement(selector) { + return _getElements(selector)[0]; + } + + // Determine if we're in an iframe + function _inFrame() { + try { + return window.self !== window.top; + } + catch (e) { + return true; + } + } + + // Insert controls + function _injectControls() { + // Insert custom video controls + _log("Injecting custom controls."); + + // Use specified html + // Need to do a default? + var html = config.html; + + // Replace seek time instances + html = _replaceAll(html, "{seektime}", config.seekTime); + + // Replace all id references + html = _replaceAll(html, "{id}", player.random); + + // Inject into the container + player.container.insertAdjacentHTML("beforeend", html); + } + + // Find the UI controls and store references + function _findElements() { + try { + player.controls = _getElement(config.selectors.controls); + + // Buttons + player.buttons = {}; + 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.mute = _getElement(config.selectors.buttons.mute); + player.buttons.captions = _getElement(config.selectors.buttons.captions); + player.buttons.fullscreen = _getElement(config.selectors.buttons.fullscreen); + + // Progress + player.progress = {}; + player.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.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.getElementsByTagName("span")[0]; + + // Volume + player.volume = _getElement(config.selectors.buttons.volume); + + // Timing + player.duration = _getElement(config.selectors.duration); + player.seekTime = _getElements(config.selectors.seekTime); + + return true; + } + catch(e) { + _log("It looks like there's a problem with your controls html. Bailing.", true); + return false; + } + } + + // Setup aria attributes + function _setupAria() { + var label = player.buttons.play.innerText; + + // If there's a media title set, use that for the label + if (typeof(config.title) !== "undefined" && config.title.length) { + label = player.buttons.play.innerText + ", " + config.title; + } + + player.buttons.play.setAttribute("aria-label", label); + } + + // Setup media + function _setupMedia() { + player.media = player.container.querySelectorAll("audio, video")[0]; + + // If there's no media, bail + if(!player.media) { + _log("No audio or video element found!", true); + return false; + } + + // Remove native video controls + player.media.removeAttribute("controls"); + + // Set media type + player.type = (player.media.tagName.toLowerCase() == "video" ? "video" : "audio"); + + // Add type class + _toggleClass(player.container, config.classes[player.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)); + + // Inject the player wrapper + if(player.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); + + // Cache the container + player.videoContainer = wrapper; + } + } + + // Setup captions + function _setupCaptions() { + if(player.type === "video") { + // Inject the container + player.videoContainer.insertAdjacentHTML("afterbegin", "<div class='" + config.selectors.captions.replace(".", "") + "'></div>"); + + // Cache selector + player.captionsContainer = _getElement(config.selectors.captions); + + // Determine if HTML5 textTracks is supported + player.isTextTracks = false; + if (player.media.textTracks) { + player.isTextTracks = true; + } + + // Get URL of caption file if exists + var captionSrc = "", + kind, + children = player.media.childNodes; + + for (var i = 0; i < children.length; i++) { + if (children[i].nodeName.toLowerCase() === "track") { + kind = children[i].getAttribute("kind"); + if (kind === "captions") { + captionSrc = children[i].getAttribute("src"); + } + } + } + + // Record if caption file exists or not + player.captionExists = true; + if (captionSrc === "") { + player.captionExists = false; + _log("No caption track found."); + } + else { + _log("Caption track found; URI: " + captionSrc); + } + + // If no caption file exists, hide container for caption text + if (!player.captionExists) { + _toggleClass(player.container, config.classes.captions.enabled); + } + // If caption file exists, process captions + else { + // 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; + for (var x=0; x < tracks.length; x++) { + tracks[x].mode = "hidden"; + } + + // Enable UI + _showCaptions(player); + + // If IE 10/11 or Firefox 31+ or Safari 7+, don"t use native captioning (still doesn"t work although they claim it"s now supported) + if ((player.browserName === "IE" && player.browserMajorVersion === 10) || + (player.browserName === "IE" && player.browserMajorVersion === 11) || + (player.browserName === "Firefox" && player.browserMajorVersion >= 31) || + (player.browserName === "Safari" && player.browserMajorVersion >= 7)) { + // Debugging + _log("Detected IE 10/11 or Firefox 31+ or Safari 7+."); + + // Set to false so skips to "manual" captioning + player.isTextTracks = false; + } + + // Rendering caption tracks + // Native support required - http://caniuse.com/webvtt + if (player.isTextTracks) { + _log("TextTracks supported."); + + for (var y=0; y < tracks.length; y++) { + var track = tracks[y]; + + if (track.kind === "captions") { + _on(track, "cuechange", function() { + if (this.activeCues[0]) { + if (this.activeCues[0].hasOwnProperty("text")) { + player.captionsContainer.innerHTML = this.activeCues[0].text; + } + } + }); + } + } + } + // Caption tracks not natively supported + else { + _log("TextTracks not supported so rendering captions manually."); + + // Render captions from array at appropriate time + player.currentCaption = ""; + player.subcount = 0; + player.captions = []; + + _on(player.media, "timeupdate", function() { + // 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]; + } + // Is there a next timecode? + if (player.media.currentTime.toFixed(1) > _timecodeMax(player.captions[player.subcount][0]) && + player.subcount < (player.captions.length-1)) { + player.subcount++; + } + // Render the caption + player.captionsContainer.innerHTML = player.currentCaption; + }); + + if (captionSrc !== "") { + // Create XMLHttpRequest Object + var xhr = new XMLHttpRequest(); + + xhr.onreadystatechange = function() { + if (xhr.readyState === 4) { + if (xhr.status === 200) { + var records = [], + record, + req = xhr.responseText; + + records = req.split("\n\n"); + + for (var r=0; r < records.length; r++) { + record = records[r]; + player.captions[r] = []; + player.captions[r] = record.split("\n"); + } + + // Remove first element ("VTT") + player.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(); + } + } + + // If Safari 7+, removing track from DOM [see "turn off native caption rendering" above] + if (player.browserName === "Safari" && player.browserMajorVersion >= 7) { + _log("Safari 7+ detected; removing track from DOM."); + + // Find all <track> elements + tracks = player.media.getElementsByTagName("track"); + + // Loop through and remove one by one + for (var t=0; t < tracks.length; t++) { + player.media.removeChild(tracks[t]); + } + } + } + } + } + + // Setup seeking + function _setupSeeking() { + // Update number of seconds in rewind and fast forward buttons + player.seekTime[0].innerHTML = config.seekTime; + player.seekTime[1].innerHTML = config.seekTime; + } + + // Setup fullscreen + function _setupFullscreen() { + if(player.type === "video" && config.fullscreen.enabled) { + // Check for native support + var nativeSupport = fullscreen.supportsFullScreen; + + if(nativeSupport || (config.fullscreen.fallback && !_inFrame())) { + _log((nativeSupport ? "Native" : "Fallback") + " fullscreen enabled."); + + // Add styling hook + _toggleClass(player.container, config.classes.fullscreen.enabled, true); + } + else { + _log("Fullscreen not supported and fallback disabled."); + } + } + } + + // Play media + function _play() { + player.media.play(); + } + + // Pause media + function _pause() { + player.media.pause(); + } + + // Restart playback + function _restart() { + // Move to beginning + player.media.currentTime = 0; + + // Special handling for "manual" captions + if (!player.isTextTracks) { + player.subcount = 0; + } + + // Play and ensure the play button is in correct state + _play(); + } + + // Rewind + function _rewind(seekTime) { + // Use default if needed + if(typeof seekTime !== "number") { + seekTime = config.seekTime; + } + + var targetTime = player.media.currentTime - seekTime; + + if (targetTime < 0) { + player.media.currentTime = 0; + } + else { + player.media.currentTime = targetTime; + } + // Special handling for "manual" captions + if (!player.isTextTracks && player.type === "video") { + _adjustManualCaptions(player); + } + } + + // Fast forward + function _forward(seekTime) { + // Use default if needed + if(typeof seekTime !== "number") { + seekTime = config.seekTime; + } + + var targetTime = player.media.currentTime + seekTime; + + if (targetTime > player.media.duration) { + player.media.currentTime = player.media.duration; + } + else { + player.media.currentTime = targetTime; + } + // Special handling for "manual" captions + if (!player.isTextTracks && player.type === "video") { + _adjustManualCaptions(player); + } + } + + // Check playing state + function _checkPlaying() { + _toggleClass(player.container, config.classes.playing, !player.media.paused); + _toggleClass(player.container, config.classes.stopped, player.media.paused); + } + + // Toggle fullscreen + function _toggleFullscreen() { + // Check for native support + var nativeSupport = fullscreen.supportsFullScreen; + + // If it's a fullscreen change event, it's probably a native close + if(event.type === fullscreen.fullScreenEventName) { + config.fullscreen.active = fullscreen.isFullScreen(); + } + // If there's native support, use it + else if(nativeSupport) { + // Request fullscreen + if(!fullscreen.isFullScreen()) { + fullscreen.requestFullScreen(player.container); + } + // Bail from fullscreen + else { + fullscreen.cancelFullScreen(); + } + + // Check if we're actually full screen (it could fail) + config.fullscreen.active = fullscreen.isFullScreen(); + } + else { + // Otherwise, it's a simple toggle + config.fullscreen.active = !config.fullscreen.active; + + // Bind/unbind escape key + if(config.fullscreen.active) { + _on(document, "keyup", _handleEscapeFullscreen); + document.body.style.overflow = "hidden"; + } + else { + _off(document, "keyup", _handleEscapeFullscreen); + document.body.style.overflow = ""; + } + } + + // Set class hook + _toggleClass(player.container, config.classes.fullscreen.active, config.fullscreen.active); + } + + // Bail from faux-fullscreen + function _handleEscapeFullscreen(event) { + // If it's a keypress and not escape, bail + if((event.which || event.charCode || event.keyCode) === 27 && config.fullscreen.active) { + _toggleFullscreen(); + } + } + + // Set volume + function _setVolume(volume) { + // Use default if needed + if(typeof volume === "undefined") { + if(config.storage.enabled && _storage().supported) { + volume = window.localStorage.plyr_volume || config.volume; + } + else { + volume = config.volume; + } + } + // Maximum is 10 + if(volume > 10) { + volume = 10; + } + + player.volume.value = volume; + player.media.volume = parseFloat(volume / 10); + _checkMute(); + + // Store the volume in storage + if(config.storage.enabled && _storage().supported) { + window.localStorage.plyr_volume = volume; + } + } + + // Mute + function _toggleMute(muted) { + // If the method is called without parameter, toggle based on current value + if(typeof active === "undefined") { + muted = !player.media.muted; + player.buttons.mute.checked = muted; + } + + player.media.muted = muted; + _checkMute(); + } + + // Toggle captions + function _toggleCaptions(active) { + // If the method is called without parameter, toggle based on current value + if(typeof active === "undefined") { + active = (player.container.className.indexOf(config.classes.captions.active) === -1); + player.buttons.captions.checked = active; + } + + if (active) { + _toggleClass(player.container, config.classes.captions.active, true); + } + else { + _toggleClass(player.container, config.classes.captions.active); + } + } + + // Check mute state + function _checkMute() { + _toggleClass(player.container, config.classes.muted, (player.media.volume === 0 || player.media.muted)); + } + + // Update <progress> elements + function _updateProgress(event) { + var progress, text, value = 0; + + switch(event.type) { + // Video playing + case "timeupdate": + progress = player.progress.played.bar; + text = player.progress.played.text; + value = _getPercentage(player.media.currentTime, player.media.duration); + break; + + // Check buffer status + case "playing": + case "progress": + progress = player.progress.buffer.bar; + text = player.progress.buffer.text; + value = (function() { + var buffered = player.media.buffered; + + if(buffered.length) { + return _getPercentage(buffered.end(0), player.media.duration); + } + + return 0; + })(); + break; + } + + if (progress && value > 0) { + progress.value = value; + text.innerHTML = value; + } + } + + // Update the displayed play time + function _updateTimeDisplay() { + player.secs = parseInt(player.media.currentTime % 60); + player.mins = parseInt((player.media.currentTime / 60) % 60); + + // 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); + + // Render + player.duration.innerHTML = player.mins + ":" + player.secs; + } + + // Listen for events + function _listeners() { + // Play + _on(player.buttons.play, "click", function() { + _play(); + setTimeout(function() { player.buttons.pause.focus(); }, 100); + }); + + // Pause + _on(player.buttons.pause, "click", function() { + _pause(); + setTimeout(function() { player.buttons.play.focus(); }, 100); + }); + + // Restart + _on(player.buttons.restart, "click", _restart); + + // Rewind + _on(player.buttons.rewind, "click", _rewind); + + // Fast forward + _on(player.buttons.forward, "click", _forward); + + // Get the HTML5 range input element and append audio volume adjustment on change + _on(player.volume, "change", function() { + _setVolume(this.value); + }); + + // Mute + _on(player.buttons.mute, "change", function() { + _toggleMute(this.checked); + }); + + // Fullscreen + _on(player.buttons.fullscreen, "click", _toggleFullscreen); + + // Handle user exiting fullscreen by escaping etc + _on(document, fullscreen.fullScreenEventName, _toggleFullscreen); + + // Click video + if(player.type === "video" && config.click) { + _on(player.videoContainer, "click", function() { + if(player.media.paused) { + _play(); + } + else if(player.media.ended) { + _restart(); + } + else { + _pause(); + } + }); + } + + // Duration + _on(player.media, "timeupdate", _updateTimeDisplay); + + // Playing progress + _on(player.media, "timeupdate", _updateProgress); + + // Skip when clicking progress bar + _on(player.progress.played.bar, "click", function(event) { + player.pos = _getClickPosition(event).x / this.offsetWidth; + player.media.currentTime = player.pos * player.media.duration; + + // Special handling for "manual" captions + if (!player.isTextTracks && player.type === "video") { + _adjustManualCaptions(player); + } + }); + + // Captions + _on(player.buttons.captions, "click", function() { + _toggleCaptions(this.checked); + }); + + // Clear captions at end of video + _on(player.media, "ended", function() { + if(player.type === "video") { + player.captionsContainer.innerHTML = ""; + } + _checkPlaying(); + }); + + // Check for buffer progress + _on(player.media, "progress", _updateProgress); + + // Also check on start of playing + _on(player.media, "playing", _updateProgress); + + // Handle native mute + _on(player.media, "volumechange", _checkMute); + + // Handle native play/pause + _on(player.media, "play pause", _checkPlaying); + } + + function _init() { + // Setup the fullscreen api + fullscreen = _fullscreen(); + + // Sniff + player.browserInfo = _browserSniff(); + player.browserName = player.browserInfo[0]; + player.browserMajorVersion = player.browserInfo[1]; + + // Debug info + _log(player.browserName + " " + player.browserMajorVersion); + + // If IE8, stop customization (use fallback) + // If IE9, stop customization (use native controls) + if (player.browserName === "IE" && (player.browserMajorVersion === 8 || player.browserMajorVersion === 9) ) { + _log("Browser not suppported.", true); + return false; + } + + // Setup media + _setupMedia(); + + // Generate random number for id/for attribute values for controls + player.random = Math.floor(Math.random() * (10000)); + + // Inject custom controls + _injectControls(); + + // Find the elements + if(!_findElements()) { + return false; + } + + // Set up aria-label for Play button with the title option + _setupAria(); + + // Captions + _setupCaptions(); + + // Set volume + _setVolume(); + + // Setup fullscreen + _setupFullscreen(); + + // Seeking + _setupSeeking(); + + // Listeners + _listeners(); + } + + _init(); + + return { + media: player.media, + play: _play, + pause: _pause, + restart: _restart, + rewind: _rewind, + forward: _forward, + setVolume: _setVolume, + toggleMute: _toggleMute, + toggleCaptions: _toggleCaptions + } + } + + // Expose setup function + api.setup = function(options){ + // Extend the default options with user specified + config = _extend(defaults, options); + + // If enabled carry on + // You may want to disable certain UAs etc + if(!config.enabled) { + 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 + var element = elements[i]; + + // Setup a player instance and add to the element + if(typeof element.plyr === "undefined") { + element.plyr = new Plyr(element); + } + + // Add to return array + players.push(element.plyr); + } + + return players; + } }(this.plyr = this.plyr || {}));
\ No newline at end of file diff --git a/src/less/docs.less b/src/less/docs.less index 65639769..890f69dd 100644 --- a/src/less/docs.less +++ b/src/less/docs.less @@ -100,6 +100,9 @@ a { &:focus { .tab-focus(); } + &.logo { + border: 0; + } } .btn { display: inline-block; @@ -110,6 +113,7 @@ a { .font-smoothing(on); font-weight: 600; border-radius: 3px; + user-select: none; &:hover, &:focus { @@ -135,6 +139,15 @@ a { } } +// Footer +footer { + margin-bottom: @padding-base; + + p { + margin-bottom: (@padding-base / 2); + } +} + // Fonts // Last to not block rendering @import "docs/fontface.less";
\ No newline at end of file diff --git a/src/less/plyr.less b/src/less/plyr.less index c98a3067..1bce3877 100644 --- a/src/less/plyr.less +++ b/src/less/plyr.less @@ -306,7 +306,9 @@ // Volume control // <input[type='range']> element - &-volume { + // Specificty is for bootstrap compatibility + &-volume[type=range] { + display: inline-block; vertical-align: middle; -webkit-appearance: none; -moz-appearance: none; diff --git a/src/templates/controls.html b/src/templates/controls.html index 3ae8d8c4..3970d143 100644 --- a/src/templates/controls.html +++ b/src/templates/controls.html @@ -14,9 +14,9 @@ </button> <button type="button" data-player="rewind"> <svg><use xlink:href="#icon-rewind"></use></svg> - <span class="sr-only">Rewind <span class="player-seek-time">10</span> seconds</span> + <span class="sr-only">Rewind <span class="player-seek-time">{seektime}</span> seconds</span> </button> - <button type="button" aria-label="{aria-label}" data-player="play"> + <button type="button" data-player="play"> <svg><use xlink:href="#icon-play"></use></svg> <span class="sr-only">Play</span> </button> @@ -26,7 +26,7 @@ </button> <button type="button" data-player="fast-forward"> <svg><use xlink:href="#icon-fast-forward"></use></svg> - <span class="sr-only">Fast forward <span class="player-seek-time">10</span> seconds</span> + <span class="sr-only">Fast forward <span class="player-seek-time">{seektime}</span> seconds</span> </button> <span class="player-time"> <span class="sr-only">Time</span> @@ -41,7 +41,7 @@ <span class="sr-only">Mute</span> </label> - <label for="volume{id}" class="sr-only">Volume:</label> + <label for="volume{id}" class="sr-only">Volume</label> <input id="volume{id}" class="player-volume" type="range" min="0" max="10" value="5" data-player="volume"> <input class="sr-only" id="captions{id}" type="checkbox" data-player="captions"> |