Work on v1.5

See changelog.md
This commit is contained in:
Sam Potts 2016-01-10 21:40:22 +11:00
parent 592bcc8d7e
commit 9b09c9c7a0
15 changed files with 341 additions and 290 deletions

View File

@ -1,9 +1,14 @@
# Changelog # Changelog
# v1.5.0 # v1.5.0
- *Beta* Vimeo support (please report bugs) - *Beta* Vimeo support (please report bugs) (fixes #8)
- New options for initialization (see docs) - New options for initialization (you can now pass a selector, HTMLElement or NodeList) (fixes #118)
- - Switched to BEM methodology (you will need to change CSS and probably HTML)
- Decoupled CSS and JS hooks (fixes #129)
- Custom controls container (fixes #98)
- Fix for private/incognito mode local storage bug (fixes #131)
- UMD module setup (fixes #121)
- Specify iframe title for Vimeo and YouTube (fixes #124)
## v1.3.5 ## v1.3.5
- Fixed bug with API use on basic supported browsers - Fixed bug with API use on basic supported browsers

2
dist/plyr.css vendored

File diff suppressed because one or more lines are too long

2
dist/plyr.js vendored

File diff suppressed because one or more lines are too long

2
docs/dist/docs.css vendored

File diff suppressed because one or more lines are too long

2
docs/dist/docs.js vendored
View File

@ -1 +1 @@
"document"in self&&("classList"in document.createElement("_")?!function(){"use strict";var t=document.createElement("_");if(t.classList.add("c1","c2"),!t.classList.contains("c2")){var e=function(t){var e=DOMTokenList.prototype[t];DOMTokenList.prototype[t]=function(t){var n,s=arguments.length;for(n=0;s>n;n++)t=arguments[n],e.call(this,t)}};e("add"),e("remove")}if(t.classList.toggle("c3",!1),t.classList.contains("c3")){var n=DOMTokenList.prototype.toggle;DOMTokenList.prototype.toggle=function(t,e){return 1 in arguments&&!this.contains(t)==!e?e:n.call(this,t)}}t=null}():!function(t){"use strict";if("Element"in t){var e="classList",n="prototype",s=t.Element[n],i=Object,o=String[n].trim||function(){return this.replace(/^\s+|\s+$/g,"")},r=Array[n].indexOf||function(t){for(var e=0,n=this.length;n>e;e++)if(e in this&&this[e]===t)return e;return-1},c=function(t,e){this.name=t,this.code=DOMException[t],this.message=e},a=function(t,e){if(""===e)throw new c("SYNTAX_ERR","An invalid or illegal string was specified");if(/\s/.test(e))throw new c("INVALID_CHARACTER_ERR","String contains an invalid character");return r.call(t,e)},l=function(t){for(var e=o.call(t.getAttribute("class")||""),n=e?e.split(/\s+/):[],s=0,i=n.length;i>s;s++)this.push(n[s]);this._updateClassName=function(){t.setAttribute("class",this.toString())}},u=l[n]=[],p=function(){return new l(this)};if(c[n]=Error[n],u.item=function(t){return this[t]||null},u.contains=function(t){return t+="",-1!==a(this,t)},u.add=function(){var t,e=arguments,n=0,s=e.length,i=!1;do t=e[n]+"",-1===a(this,t)&&(this.push(t),i=!0);while(++n<s);i&&this._updateClassName()},u.remove=function(){var t,e,n=arguments,s=0,i=n.length,o=!1;do for(t=n[s]+"",e=a(this,t);-1!==e;)this.splice(e,1),o=!0,e=a(this,t);while(++s<i);o&&this._updateClassName()},u.toggle=function(t,e){t+="";var n=this.contains(t),s=n?e!==!0&&"remove":e!==!1&&"add";return s&&this[s](t),e===!0||e===!1?e:!n},u.toString=function(){return this.join(" ")},i.defineProperty){var d={get:p,enumerable:!0,configurable:!0};try{i.defineProperty(s,e,d)}catch(m){-2146823252===m.number&&(d.enumerable=!1,i.defineProperty(s,e,d))}}else i[n].__defineGetter__&&s.__defineGetter__(e,p)}}(self)),plyr.setup({debug:!0,title:"Video demo",tooltips:!0,captions:{defaultActive:!0},onSetup:function(){console.log("✓ Setup done")}}),shr.setup({count:{classname:"btn-count"}}),function(){function t(){var t=this,n=t.getAttribute("data-source"),s=document.querySelector(".plyr").plyr;switch(n){case"video":s.source({type:"video",title:"Bug Buck Bunny",sources:[{src:"https://cdn.selz.com/plyr/1.0/movie.mp4",type:"video/mp4"},{src:"https://cdn.selz.com/plyr/1.0/movie.webm",type:"video/webm"}],poster:"https://cdn.selz.com/plyr/1.0/poster.jpg",tracks:[{kind:"captions",label:"English",srclang:"en",src:"https://cdn.selz.com/plyr/1.0/example_captions_en.vtt","default":!0}]});break;case"audio":s.source({type:"audio",title:"96 by Logistics",sources:[{src:"https://cdn.selz.com/plyr/1.0/logistics-96-sample.mp3",type:"audio/mp3"},{src:"https://cdn.selz.com/plyr/1.0/logistics-96-sample.ogg",type:"audio/ogg"}]});break;case"youtube":s.source({type:"youtube",title:"Enovato interview of Dan Cederholm for Made By",sources:"Au87oAJ2jeE"});break;case"vimeo":s.source({type:"vimeo",title:"View from a blue moon",sources:"143418951"})}for(var i=e.length-1;i>=0;i--)e[i].classList.remove("active");event.target.classList.add("active")}for(var e=document.querySelectorAll("[data-source]"),n=e.length-1;n>=0;n--)e[n].addEventListener("click",t)}(),document.domain.indexOf("plyr.io")>-1&&(!function(t,e,n,s,i,o,r){t.GoogleAnalyticsObject=i,t[i]=t[i]||function(){(t[i].q=t[i].q||[]).push(arguments)},t[i].l=1*new Date,o=e.createElement(n),r=e.getElementsByTagName(n)[0],o.async=1,o.src=s,r.parentNode.insertBefore(o,r)}(window,document,"script","//www.google-analytics.com/analytics.js","ga"),ga("create","UA-40881672-11","auto"),ga("send","pageview")); "document"in self&&("classList"in document.createElement("_")?!function(){"use strict";var e=document.createElement("_");if(e.classList.add("c1","c2"),!e.classList.contains("c2")){var t=function(e){var t=DOMTokenList.prototype[e];DOMTokenList.prototype[e]=function(e){var n,i=arguments.length;for(n=0;i>n;n++)e=arguments[n],t.call(this,e)}};t("add"),t("remove")}if(e.classList.toggle("c3",!1),e.classList.contains("c3")){var n=DOMTokenList.prototype.toggle;DOMTokenList.prototype.toggle=function(e,t){return 1 in arguments&&!this.contains(e)==!t?t:n.call(this,e)}}e=null}():!function(e){"use strict";if("Element"in e){var t="classList",n="prototype",i=e.Element[n],s=Object,o=String[n].trim||function(){return this.replace(/^\s+|\s+$/g,"")},r=Array[n].indexOf||function(e){for(var t=0,n=this.length;n>t;t++)if(t in this&&this[t]===e)return t;return-1},a=function(e,t){this.name=e,this.code=DOMException[e],this.message=t},c=function(e,t){if(""===t)throw new a("SYNTAX_ERR","An invalid or illegal string was specified");if(/\s/.test(t))throw new a("INVALID_CHARACTER_ERR","String contains an invalid character");return r.call(e,t)},l=function(e){for(var t=o.call(e.getAttribute("class")||""),n=t?t.split(/\s+/):[],i=0,s=n.length;s>i;i++)this.push(n[i]);this._updateClassName=function(){e.setAttribute("class",this.toString())}},u=l[n]=[],d=function(){return new l(this)};if(a[n]=Error[n],u.item=function(e){return this[e]||null},u.contains=function(e){return e+="",-1!==c(this,e)},u.add=function(){var e,t=arguments,n=0,i=t.length,s=!1;do e=t[n]+"",-1===c(this,e)&&(this.push(e),s=!0);while(++n<i);s&&this._updateClassName()},u.remove=function(){var e,t,n=arguments,i=0,s=n.length,o=!1;do for(e=n[i]+"",t=c(this,e);-1!==t;)this.splice(t,1),o=!0,t=c(this,e);while(++i<s);o&&this._updateClassName()},u.toggle=function(e,t){e+="";var n=this.contains(e),i=n?t!==!0&&"remove":t!==!1&&"add";return i&&this[i](e),t===!0||t===!1?t:!n},u.toString=function(){return this.join(" ")},s.defineProperty){var p={get:d,enumerable:!0,configurable:!0};try{s.defineProperty(i,t,p)}catch(h){-2146823252===h.number&&(p.enumerable=!1,s.defineProperty(i,t,p))}}else s[n].__defineGetter__&&i.__defineGetter__(t,d)}}(self)),plyr.setup(".js-media-player",{debug:!0,title:"Video demo",tooltips:!0,captions:{defaultActive:!0},onSetup:function(){console.log("✓ Setup done")}}),shr.setup({count:{classname:"btn__count"}}),function(){function e(){var e=this,n=e.getAttribute("data-source"),i=document.querySelector(".js-media-player").plyr;switch(n){case"video":i.source({type:"video",title:"View From A Blue Moon",sources:[{src:"https://cdn.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.mp4",type:"video/mp4"},{src:"https://cdn.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.webm",type:"video/webm"}],poster:"https://cdn.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.jpg",tracks:[{kind:"captions",label:"English",srclang:"en",src:"https://cdn.selz.com/plyr/1.0/example_captions_en.vtt","default":!0}]});break;case"audio":i.source({type:"audio",title:"Kishi Bashi &ndash; &ldquo;It All Began With A Burst&rdquo;",sources:[{src:"https://cdn.selz.com/plyr/1.5/Kishi_Bashi_-_It_All_Began_With_a_Burst.mp3",type:"audio/mp3"},{src:"https://cdn.selz.com/plyr/1.5/Kishi_Bashi_-_It_All_Began_With_a_Burst.ogg",type:"audio/ogg"}]});break;case"youtube":i.source({type:"youtube",title:"View From A Blue Moon",sources:"bTqVqk7FSmY"});break;case"vimeo":i.source({type:"vimeo",title:"View From A Blue Moon",sources:"143418951"})}for(var s=t.length-1;s>=0;s--)t[s].classList.remove("btn--active");event.target.classList.add("btn--active")}for(var t=document.querySelectorAll("[data-source]"),n=t.length-1;n>=0;n--)t[n].addEventListener("click",e)}(),document.domain.indexOf("plyr.io")>-1&&(!function(e,t,n,i,s,o,r){e.GoogleAnalyticsObject=s,e[s]=e[s]||function(){(e[s].q=e[s].q||[]).push(arguments)},e[s].l=1*new Date,o=t.createElement(n),r=t.getElementsByTagName(n)[0],o.async=1,o.src=i,r.parentNode.insertBefore(o,r)}(window,document,"script","//www.google-analytics.com/analytics.js","ga"),ga("create","UA-40881672-11","auto"),ga("send","pageview"));

View File

@ -13,20 +13,20 @@
<!-- Docs styles --> <!-- Docs styles -->
<link rel="stylesheet" href="https://cdn.plyr.io/1.3.7/docs.css"> <link rel="stylesheet" href="https://cdn.plyr.io/1.3.7/docs.css">
</head> </head>
<body> <<body>
<header> <header>
<h1>Plyr</h1> <h1>Plyr</h1>
<p>A simple, accessible HTML5 media player by <a href="https://twitter.com/sam_potts" target="_blank">@sam_potts</a> from <a href="https://twitter.com/selz" target="_blank">@selz</a></p> <p>A simple, accessible HTML5 media player by <a href="https://twitter.com/sam_potts" target="_blank">@sam_potts</a> from <a href="https://twitter.com/selz" target="_blank">@selz</a></p>
<nav> <nav>
<ul> <ul>
<li> <li>
<a href="https://github.com/selz/plyr" target="_blank" class="btn btn-primary" data-shr-network="github"> <a href="https://github.com/selz/plyr" target="_blank" class="btn btn--primary" data-shr-network="github">
<svg class="icon"><use xlink:href="#shr-github"/></svg>Download on GitHub <svg class="icon"><use xlink:href="#icon-github"/></svg>Download on GitHub
</a> </a>
</li> </li>
<li> <li>
<a href="https://twitter.com/intent/tweet?text=A+simple+HTML5+media+player+with+custom+controls+and+WebVTT+captions.&url=http%3A%2F%2Fplyr.io&via=Sam_Potts" target="_blank" class="btn btn-twitter" data-shr-network="twitter"> <a href="https://twitter.com/intent/tweet?text=A+simple+HTML5+media+player+with+custom+controls+and+WebVTT+captions.&url=http%3A%2F%2Fplyr.io&via=Sam_Potts" target="_blank" class="btn btn--twitter" data-shr-network="twitter">
<svg class="icon"><use xlink:href="#shr-twitter"/></svg>Tweet <svg class="icon"><use xlink:href="#icon-twitter"/></svg>Tweet
</a> </a>
</li> </li>
</ul> </ul>
@ -34,44 +34,42 @@
</header> </header>
<main role="main" id="main"> <main role="main" id="main">
<nav class="btn-bar nav-panel"> <nav class="btn__bar">
<ul> <ul>
<li> <li>
<button type="button" class="btn active" data-source="video">Video</button> <button type="button" class="btn btn--active" data-source="video">Video</button>
</li> </li>
<li> <li>
<button type="button" class="btn" data-source="audio">Audio</button> <button type="button" class="btn" data-source="audio">Audio</button>
</li> </li>
<li> <li>
<button type="button" class="btn btn-youtube" data-source="youtube"><svg class="icon"><use xlink:href="#icon-youtube"/></svg>YouTube</button> <button type="button" class="btn btn--youtube" data-source="youtube"><svg class="icon"><use xlink:href="#icon-youtube"/></svg>YouTube</button>
</li> </li>
<li> <li>
<button type="button" class="btn btn-vimeo" data-source="vimeo"><svg class="icon"><use xlink:href="#icon-vimeo"/></svg>Vimeo</button> <button type="button" class="btn btn--vimeo" data-source="vimeo"><svg class="icon"><use xlink:href="#icon-vimeo"/></svg>Vimeo <sup>BETA</sup></button>
</li> </li>
</ul> </ul>
</nav> </nav>
<section> <section>
<div class="plyr"> <div class="js-media-player">
<video poster="https://cdn.selz.com/plyr/1.0/poster.jpg" controls crossorigin> <video poster="https://cdn.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.jpg" controls crossorigin>
<!-- Video files --> <!-- Video files -->
<source src="https://cdn.selz.com/plyr/1.0/movie.mp4" type="video/mp4"> <source src="https://cdn.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.mp4" type="video/mp4">
<source src="https://cdn.selz.com/plyr/1.0/movie.webm" type="video/webm"> <source src="https://cdn.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.webm" type="video/webm">
<!-- Text track file --> <!-- Text track file -->
<track kind="captions" label="English" srclang="en" src="https://cdn.selz.com/plyr/1.0/example_captions_en.vtt" default> <track kind="captions" label="English" srclang="en" src="https://cdn.selz.com/plyr/1.0/example_captions_en.vtt" default>
<!-- Fallback for browsers that don't support the <video> element --> <!-- Fallback for browsers that don't support the <video> element -->
<a href="https://cdn.selz.com/plyr/1.0/movie.mp4">Download</a> <a href="https://cdn.selz.com/plyr/1.0/movie.mp4">Download</a>
</video> </video>
</div> </div>
<ul> <ul>
<li class="cite cite-video"><small><a href="https://www.bigbuckbunny.org" target="_blank">Big Buck Bunny</a> &copy; Copyright 2008, Blender Foundation</small></li> <li class="plyr__cite plyr__cite--video"><small><a href="http://viewfromabluemoon.com/" target="_blank">View From A Blue Moon</a> &copy; Brainfarm</small></li>
<li class="cite cite-audio"><small><a href="https://www.hospitalrecords.com/shop/artist/logistics" target="_blank">96 by Logistics</a> &copy; Copyright, Hospital Records</small></li> <li class="plyr__cite plyr__cite--audio"><small><a href="http://www.kishibashi.com/" target="_blank">Kishi Bashi &ndash; &ldquo;It All Began With A Burst&rdquo;</a> &copy; Kishi Bashi</small></li>
<li class="cite cite-youtube"><small><a href="https://www.youtube.com/watch?v=Au87oAJ2jeE" target="_blank">Envato's "Made By" interview of Dan Cederholm</a> on <span class="color-youtube"><svg class="icon"><use xlink:href="#icon-youtube"/></svg>YouTube</span></small> <li class="plyr__cite plyr__cite--youtube"><small><a href="https://www.youtube.com/watch?v=bTqVqk7FSmY" target="_blank">View From A Blue Moon</a> on <span class="color--youtube"><svg class="icon"><use xlink:href="#icon-youtube"/></svg>YouTube</span></small>
<li class="cite cite-vimeo"><small><a href="https://vimeo.com/87701971" target="_blank">Yosemite HD II</a> on <span class="color-vimeo"><svg class="icon"><use xlink:href="#icon-vimeo"/></svg>Vimeo</span></small> <li class="plyr__cite plyr__cite--vimeo"><small><a href="https://vimeo.com/ondemand/viewfromabluemoon4k" target="_blank">View From A Blue Moon</a> on <span class="color--vimeo"><svg class="icon"><use xlink:href="#icon-vimeo"/></svg>Vimeo</span></small>
</ul> </ul>
</section> </section>
</main> </main>

View File

@ -5,7 +5,7 @@
/*global plyr, shr*/ /*global plyr, shr*/
// Setup the player // Setup the player
plyr.setup({ plyr.setup('.js-media-player', {
debug: true, debug: true,
title: 'Video demo', title: 'Video demo',
tooltips: true, tooltips: true,
@ -20,7 +20,7 @@ plyr.setup({
// Setup shr // Setup shr
shr.setup({ shr.setup({
count: { count: {
classname: 'btn-count' classname: 'btn__count'
} }
}); });
@ -37,22 +37,22 @@ shr.setup({
function newSource() { function newSource() {
var trigger = this, var trigger = this,
type = trigger.getAttribute('data-source'), type = trigger.getAttribute('data-source'),
player = document.querySelector('.plyr').plyr; player = document.querySelector('.js-media-player').plyr;
switch(type) { switch(type) {
case 'video': case 'video':
player.source({ player.source({
type: 'video', type: 'video',
title: 'Bug Buck Bunny', title: 'View From A Blue Moon',
sources: [{ sources: [{
src: 'https://cdn.selz.com/plyr/1.0/movie.mp4', src: 'https://cdn.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.mp4',
type: 'video/mp4' type: 'video/mp4'
}, },
{ {
src: 'https://cdn.selz.com/plyr/1.0/movie.webm', src: 'https://cdn.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.webm',
type: 'video/webm' type: 'video/webm'
}], }],
poster: 'https://cdn.selz.com/plyr/1.0/poster.jpg', poster: 'https://cdn.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.jpg',
tracks: [{ tracks: [{
kind: 'captions', kind: 'captions',
label: 'English', label: 'English',
@ -66,13 +66,13 @@ shr.setup({
case 'audio': case 'audio':
player.source({ player.source({
type: 'audio', type: 'audio',
title: '96 by Logistics', title: 'Kishi Bashi &ndash; &ldquo;It All Began With A Burst&rdquo;',
sources: [{ sources: [{
src: 'https://cdn.selz.com/plyr/1.0/logistics-96-sample.mp3', src: 'https://cdn.selz.com/plyr/1.5/Kishi_Bashi_-_It_All_Began_With_a_Burst.mp3',
type: 'audio/mp3' type: 'audio/mp3'
}, },
{ {
src: 'https://cdn.selz.com/plyr/1.0/logistics-96-sample.ogg', src: 'https://cdn.selz.com/plyr/1.5/Kishi_Bashi_-_It_All_Began_With_a_Burst.ogg',
type: 'audio/ogg' type: 'audio/ogg'
}] }]
}); });
@ -81,25 +81,25 @@ shr.setup({
case 'youtube': case 'youtube':
player.source({ player.source({
type: 'youtube', type: 'youtube',
title: 'Enovato interview of Dan Cederholm for Made By', title: 'View From A Blue Moon',
sources: 'Au87oAJ2jeE' sources: 'bTqVqk7FSmY'
}); });
break; break;
case 'vimeo': case 'vimeo':
player.source({ player.source({
type: 'vimeo', type: 'vimeo',
title: 'View from a blue moon', title: 'View From A Blue Moon',
sources: '143418951' sources: '143418951'
}); });
break; break;
} }
for (var x = buttons.length - 1; x >= 0; x--) { for (var x = buttons.length - 1; x >= 0; x--) {
buttons[x].classList.remove('active'); buttons[x].classList.remove('btn--active');
} }
event.target.classList.add('active'); event.target.classList.add('btn--active');
} }
})(); })();

View File

@ -21,7 +21,7 @@ nav {
} }
// Tabs // Tabs
.btn-bar { .btn__bar {
position: relative; position: relative;
margin: 0 auto @padding-base; margin: 0 auto @padding-base;
max-width: @example-width-video; max-width: @example-width-video;
@ -60,10 +60,8 @@ nav {
display: block; display: block;
border-radius: 0; border-radius: 0;
} }
.active { .btn--active {
&:extend(.btn-primary); &:extend(.btn--primary);
}
.btn.active {
box-shadow: inset 0 1px 1px rgba(0,0,0, .2); box-shadow: inset 0 1px 1px rgba(0,0,0, .2);
position: relative; position: relative;
z-index: 1; z-index: 1;
@ -80,7 +78,7 @@ nav {
// Shared // Shared
.btn, .btn,
.btn-count { .btn__count {
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
border-radius: @border-radius-base; border-radius: @border-radius-base;
@ -91,12 +89,12 @@ nav {
// Buttons // Buttons
.btn { .btn {
padding: (@padding-base / 2) @padding-base; padding: (@padding-base / 2) @padding-base;
background: @body-background; background: linear-gradient(lighten(@body-background, 2%), darken(@body-background, 3%));
border: 1px solid @gray-light; border: 1px solid @gray-light;
box-shadow: inset 0 1px 0 #fff, 0 1px 1px rgba(0,0,0, .05); box-shadow: 0 1px 1px rgba(0,0,0, .05);
text-shadow: 0 1px 1px #fff; text-shadow: 0 1px 1px #fff;
color: @gray; color: @gray;
transition: background .3s ease, border .3s ease, color .3s ease; transition: background .1s ease, color .1s ease;
&:hover, &:hover,
&:focus { &:focus {
@ -104,18 +102,18 @@ nav {
color: @gray; color: @gray;
outline: 0; outline: 0;
} }
&-youtube .icon { &--youtube .icon {
color: @color-youtube; color: @color-youtube;
} }
&-vimeo .icon { &--vimeo .icon {
color: @color-vimeo; color: @color-vimeo;
} }
&-twitter .icon { &--twitter .icon {
color: @color-twitter; color: @color-twitter;
} }
} }
.btn-primary { .btn--primary {
background-image: linear-gradient(@link-color, darken(@link-color, 3%)); background-image: linear-gradient(@link-color, darken(@link-color, 5%));
background-color: @link-color; background-color: @link-color;
border-color: darken(@link-color, 10%); border-color: darken(@link-color, 10%);
box-shadow: 0 1px 1px rgba(0,0,0, .15); box-shadow: 0 1px 1px rgba(0,0,0, .15);
@ -128,13 +126,13 @@ nav {
border-color: darken(@link-color, 20%); border-color: darken(@link-color, 20%);
} }
} }
.btn-small { .btn--small {
padding-top: ceil(@padding-base / 3); padding-top: ceil(@padding-base / 3);
padding-bottom: ceil(@padding-base / 3); padding-bottom: ceil(@padding-base / 3);
} }
// Count bubble // Count bubble
.btn-count { .btn__count {
position: relative; position: relative;
margin-left: (@padding-base / 2); margin-left: (@padding-base / 2);
padding: (@padding-base / 2) (@padding-base * .75); padding: (@padding-base / 2) (@padding-base * .75);

View File

@ -3,10 +3,10 @@
// ========================================================================== // ==========================================================================
video, video,
.plyr-video-embed { .plyr__video-embed {
border-radius: @border-radius-base; border-radius: @border-radius-base;
} }
.plyr-video-embed { .plyr__video-embed {
-webkit-mask-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAA5JREFUeNpiYGBgAAgwAAAEAAGbA+oJAAAAAElFTkSuQmCC); -webkit-mask-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAA5JREFUeNpiYGBgAAgwAAAEAAGbA+oJAAAAAElFTkSuQmCC);
} }
@ -15,15 +15,15 @@ video,
margin: 0 auto @padding-base; margin: 0 auto @padding-base;
max-width: @example-width-video; max-width: @example-width-video;
&-controls { &__controls {
border-radius: 0 0 @border-radius-base @border-radius-base; border-radius: 0 0 @border-radius-base @border-radius-base;
} }
video, video,
.plyr-video-embed { .plyr__video-embed {
border-radius: @border-radius-base @border-radius-base 0 0; border-radius: @border-radius-base @border-radius-base 0 0;
} }
&-fullscreen, &--fullscreen,
&.fullscreen-active { &--fullscreen-active {
max-width: none; max-width: none;
.plyr-controls, .plyr-controls,
@ -36,20 +36,20 @@ video,
} }
} }
} }
.plyr-audio { .plyr--audio {
max-width: @example-width-audio; max-width: @example-width-audio;
.plyr-controls { .plyr__controls {
border-radius: @border-radius-base; border-radius: @border-radius-base;
} }
.plyr-progress { .plyr__progress {
border-radius: @border-radius-base @border-radius-base 0 0; border-radius: @border-radius-base @border-radius-base 0 0;
overflow: hidden; overflow: hidden;
} }
} }
// Style full supported player // Style full supported player
.cite { .plyr__cite {
display: none; display: none;
.icon { .icon {
@ -57,9 +57,9 @@ video,
} }
} }
.plyr-video ~ ul .cite-video, .plyr--video ~ ul .plyr__cite--video,
.plyr-audio ~ ul .cite-audio, .plyr--audio ~ ul .plyr__cite--audio,
.plyr-youtube ~ ul .cite-youtube, .plyr--youtube ~ ul .plyr__cite--youtube,
.plyr-vimeo ~ ul .cite-vimeo { .plyr--vimeo ~ ul .plyr__cite--vimeo {
display: block; display: block;
} }

View File

@ -1,13 +0,0 @@
// ==========================================================================
// Panels
// ==========================================================================
// Panels
.panel {
display: none;
&.active {
display: block;
animation: fade-in .3s ease;
}
}

View File

@ -26,7 +26,13 @@ small {
padding: 0 (@padding-base / 2); padding: 0 (@padding-base / 2);
.font-size(14); .font-size(14);
} }
ul sup {
vertical-align: 2px;
.font-size(9);
}
// Lists
ul,
li { li {
list-style: none; list-style: none;
margin: 0; margin: 0;
@ -53,9 +59,9 @@ a {
} }
} }
.color-vimeo { .color--vimeo {
color: @color-vimeo; color: @color-vimeo;
} }
.color-youtube { .color--youtube {
color: @color-youtube; color: @color-youtube;
} }

View File

@ -22,6 +22,5 @@
@import "components/base.less"; @import "components/base.less";
@import "components/icons.less"; @import "components/icons.less";
@import "components/buttons.less"; @import "components/buttons.less";
@import "components/panels.less";
@import "components/error.less"; @import "components/error.less";
@import "components/examples.less"; @import "components/examples.less";

View File

@ -1,12 +1,12 @@
# Plyr # Plyr
A simple, accessible HTML5 media player. A simple, accessible HTML5 media player.
Checkout the [demo](http://plyr.io). Checkout the [demo](http://plyr.io).
[![Image of Plyr](https://cdn.plyr.io/static/plyr.jpg)](http://plyr.io) [![Image of Plyr](https://cdn.plyr.io/static/plyr.jpg)](http://plyr.io)
## Why? ## Why?
We wanted a lightweight, accessible and customisable media player that just supports [*modern*](#browser-support) browsers. Sure, there are many other players out there but we wanted to keep things simple, using the right elements for the job. We wanted a lightweight, accessible and customizable media player that just supports [*modern*](#browser-support) browsers. Sure, there are many other players out there but we wanted to keep things simple, using the right elements for the job.
## Features ## Features
- **Accessible** - full support for VTT captions and screen readers. - **Accessible** - full support for VTT captions and screen readers.
@ -15,19 +15,18 @@ We wanted a lightweight, accessible and customisable media player that just supp
- **Semantic** - uses the *right* elements. `<input type="range">` for volume and `<progress>` for progress and well, `<button>`s for buttons. There's no `<span>` or `<a href="#">` button hacks. - **Semantic** - uses the *right* elements. `<input type="range">` for volume and `<progress>` for progress and well, `<button>`s for buttons. There's no `<span>` or `<a href="#">` button hacks.
- **Responsive** - as you'd expect these days. - **Responsive** - as you'd expect these days.
- **Audio & Video** - support for both formats. - **Audio & Video** - support for both formats.
- **[Embedded Video](#embeds)** - support for YouTube (Vimeo soon). - **[Embedded Video](#embeds)** - support for YouTube and Vimeo (beta).
- **[API](#api)** - toggle playback, volume, seeking, and more. - **[API](#api)** - toggle playback, volume, seeking, and more.
- **[Fullscreen](#fullscreen)** - supports native fullscreen with fallback to "full window" modes. - **[Fullscreen](#fullscreen)** - supports native fullscreen with fallback to "full window" modes.
- **i18n support** - support for internationalization of controls. - **i18n support** - support for internationalization of controls.
- **No dependencies** - written in vanilla JavaScript, no jQuery required. - **No dependencies** - written in vanilla JavaScript, no jQuery required.
Oh and yes, it works with Bootstrap. Oh and yes, it works with Bootstrap.
## Changelog ## Changelog
Check out the [changelog](changelog.md). Check out the [changelog](changelog.md) to see what's been new with Plyr.
## Planned Development ## Planned Development
- Vimeo support
- Playback speed - Playback speed
- Playlists - Playlists
- Multiple language captions (with selection) - Multiple language captions (with selection)
@ -38,7 +37,7 @@ If you have any cool ideas or features, please let me know by [creating an issue
## Implementation ## Implementation
Check `docs/index.html` and `docs/dist/docs.js` for an example setup. Check `docs/index.html` and `docs/dist/docs.js` for an example setup.
**Heads up:** the example `index.html` file needs to be served from a webserver (such as Apache, Nginx, IIS or similar) unless you change the file sources to include http or https. e.g. change `//cdn.plyr.io/1.3.7/plyr.js` to `https://cdn.plyr.io/1.3.7/plyr.js` **Heads up:** the example `index.html` file needs to be served from a webserver (such as Apache, Nginx, IIS or similar) unless you change the file sources to include http or https. e.g. change `//cdn.plyr.io/1.3.7/plyr.js` to `https://cdn.plyr.io/1.3.7/plyr.js`
@ -56,7 +55,7 @@ ember addon:install ember-cli-plyr
``` ```
More info is on [npm](https://www.npmjs.com/package/ember-cli-plyr) and [GitHub](https://github.com/louisrudner/ember-cli-plyr) More info is on [npm](https://www.npmjs.com/package/ember-cli-plyr) and [GitHub](https://github.com/louisrudner/ember-cli-plyr)
### CDN ### CDN
If you want to use our CDN, you can use the following: If you want to use our CDN, you can use the following:
```html ```html
@ -66,13 +65,15 @@ If you want to use our CDN, you can use the following:
You can also access the `sprite.svg` file at `https://cdn.plyr.io/1.3.7/sprite.svg`. You can also access the `sprite.svg` file at `https://cdn.plyr.io/1.3.7/sprite.svg`.
### CSS ### CSS & Styling
If you want to use the default css, add the `plyr.css` file from `/dist` into your head, or even better use `plyr.less` or `plyr.sass` file included in `/src` in your build to save a request. If you want to use the default css, add the `plyr.css` file from `/dist` into your head, or even better use `plyr.less` or `plyr.sass` file included in `/src` in your build to save a request.
```html ```html
<link rel="stylesheet" href="dist/plyr.css"> <link rel="stylesheet" href="dist/plyr.css">
``` ```
The default setup uses the BEM methodology with `plyr` as the block, e.g. `.plyr__controls`. You can change the class hooks in the options. Check out the source for more on this.
### SVG ### SVG
The SVG sprite for the controls icons is loaded in by AJAX to help with performance. This is best added before the closing `</body>`, before any other scripts. The SVG sprite for the controls icons is loaded in by AJAX to help with performance. This is best added before the closing `</body>`, before any other scripts.
@ -92,25 +93,29 @@ The SVG sprite for the controls icons is loaded in by AJAX to help with performa
})(document, "dist/sprite.svg"); })(document, "dist/sprite.svg");
</script> </script>
``` ```
If you're using the `<base>` tag on your site, you may need to use something like this:
[https://gist.github.com/leonderijke/c5cf7c5b2e424c0061d2](svgfixer.js)
More info on SVG sprites here: More info on SVG sprites here:
[http://css-tricks.com/svg-sprites-use-better-icon-fonts/](http://css-tricks.com/svg-sprites-use-better-icon-fonts/) [http://css-tricks.com/svg-sprites-use-better-icon-fonts/](http://css-tricks.com/svg-sprites-use-better-icon-fonts/)
and the AJAX technique here: and the AJAX technique here:
[http://css-tricks.com/ajaxing-svg-sprite/](http://css-tricks.com/ajaxing-svg-sprite/) [http://css-tricks.com/ajaxing-svg-sprite/](http://css-tricks.com/ajaxing-svg-sprite/)
### HTML ### HTML
The only extra markup that's needed to use plyr is a `<div>` wrapper. Replace the source, poster and captions with urls for your media. 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 ```html
<div class="plyr"> <div class="plyr">
<video poster="https://cdn.selz.com/plyr/1.0/poster.jpg" controls crossorigin> <video poster="/path/to/poster.jpg" controls>
<!-- Video files --> <!-- Video files -->
<source src="https://cdn.selz.com/plyr/1.0/movie.mp4" type="video/mp4"> <source src="/path/to/video.mp4" type="video/mp4">
<source src="https://cdn.selz.com/plyr/1.0/movie.webm" type="video/webm"> <source src="/path/to/video.webm" type="video/webm">
<!-- Text track file --> <!-- Text track file -->
<track kind="captions" label="English captions" src="https://cdn.selz.com/plyr/1.0/movie_captions_en.vtt" srclang="en" default> <track kind="captions" label="English captions" src="/path/to/captions.vtt" srclang="en" default>
<!-- Fallback for browsers that don't support the <video> element --> <!-- Fallback for browsers that don't support the <video> element -->
<a href="https://cdn.selz.com/plyr/1.0/movie.mp4">Download</a> <a href="/path/to/movie.mp4">Download</a>
</video> </video>
</div> </div>
``` ```
@ -120,14 +125,14 @@ And the same for `<audio>`
<div class="plyr"> <div class="plyr">
<audio controls> <audio controls>
<!-- Audio files --> <!-- Audio files -->
<source src="https://cdn.selz.com/plyr/1.0/logistics-96-sample.mp3" type="audio/mp3"> <source src="/path/to/audio.mp3" type="audio/mp3">
<source src="https://cdn.selz.com/plyr/1.0/logistics-96-sample.ogg" type="audio/ogg"> <source src="/path/to/audio.ogg" type="audio/ogg">
<!-- Fallback for browsers that don't support the <audio> element --> <!-- Fallback for browsers that don't support the <audio> element -->
<a href="https://cdn.selz.com/plyr/1.0/logistics-96-sample.mp3">Download</a> <a href="/path/to/audio.mp3">Download</a>
</audio> </audio>
</div> </div>
``` ```
For YouTube, Plyr uses the standard YouTube API markup (an empty `<div>`): For YouTube, Plyr uses the standard YouTube API markup (an empty `<div>`):
@ -138,7 +143,7 @@ For YouTube, Plyr uses the standard YouTube API markup (an empty `<div>`):
``` ```
#### Cross Origin (CORS) #### Cross Origin (CORS)
You'll notice the `crossorigin` attribute on the example `<video>` and `<audio>` elements. This is because the media is loaded from another domain. If your media is hosted on another domain, you may need to add this attribute. You'll notice the `crossorigin` attribute on the example `<video>` and `<audio>` elements. This is because the media is loaded from another domain. If your media is hosted on another domain, you may need to add this attribute.
More info on CORS here: More info on CORS here:
[https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS) [https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS)
@ -154,9 +159,9 @@ Here's an example of a default setup:
<script>plyr.setup();</script> <script>plyr.setup();</script>
``` ```
This will look for all elements with the `plyr` classname and setup plyr on each element found. You can specify other options, including a different selector hook below. This will look for all elements with the specified container classname (default is `plyr`) and setup plyr on each element found. You can specify other options, including a different selector hook below. The container classname will be added to the specified element(s) if it is not already present (for the CSS).
You can initialise the player a few other ways: You can initialize the player a few other ways:
Passing a [NodeList](https://developer.mozilla.org/en-US/docs/Web/API/NodeList): Passing a [NodeList](https://developer.mozilla.org/en-US/docs/Web/API/NodeList):
```javascript ```javascript
@ -196,7 +201,7 @@ Options must be passed as an object to the `setup()` method as above.
<td><code>enabled</code></td> <td><code>enabled</code></td>
<td>Boolean</td> <td>Boolean</td>
<td><code>true</code></td> <td><code>true</code></td>
<td>Completely disable Plyr. This would allow you to do a User Agent check or similar to programatically enable or disable Plyr for a certain UA. Example below.</td> <td>Completely disable Plyr. This would allow you to do a User Agent check or similar to programmatically enable or disable Plyr for a certain UA. Example below.</td>
</tr> </tr>
<tr> <tr>
<td><code>html</code></td> <td><code>html</code></td>
@ -214,7 +219,7 @@ Options must be passed as an object to the `setup()` method as above.
<td><code>i18n</code></td> <td><code>i18n</code></td>
<td>Object</td> <td>Object</td>
<td><code><a href="controls.md">See controls.md</a></code></td> <td><code><a href="controls.md">See controls.md</a></code></td>
<td>Used for internationalisation (i18n) of the tooltips/labels within the buttons.</td> <td>Used for internationalization (i18n) of the tooltips/labels within the buttons.</td>
</tr> </tr>
<tr> <tr>
<td><code>iconPrefix</code></td> <td><code>iconPrefix</code></td>
@ -339,7 +344,7 @@ Options must be passed as an object to the `setup()` method as above.
## API ## API
#### Fetching the plyr instance #### Fetching the plyr instance
A `plyr` object is added to any element that Plyr is initialised on. You can then control the player by accessing methods in the `plyr` object. A `plyr` object is added to any element that Plyr is initialized on. You can then control the player by accessing methods in the `plyr` object.
There are two ways to access the instance, firstly you re-query the element container you used for setup (e.g. `.js-plyr`) like so: There are two ways to access the instance, firstly you re-query the element container you used for setup (e.g. `.js-plyr`) like so:
@ -442,14 +447,14 @@ Here's a list of the methods supported:
<td>Array</td> <td>Array</td>
<td> <td>
Set the media source. Set the media source.
<br><br> <br><br>
<strong>string</strong><br> <strong>string</strong><br>
<code>.source("/path/to/video.mp4")</code><br> <code>.source("/path/to/video.mp4")</code><br>
This will set the <code>src</code> attribute on the <code>video</code> or <code>audio</code> element. This will set the <code>src</code> attribute on the <code>video</code> or <code>audio</code> element.
<br><br> <br><br>
<strong>array</strong><br> <strong>array</strong><br>
<code>.source([{ src: "/path/to/video.webm", type: "video/webm", ...more attributes... }, { src: "/path/to/video.mp4", type: "video/mp4", ...more attributes... }])`</code><br> <code>.source([{ src: "/path/to/video.webm", type: "video/webm", ...more attributes... }, { src: "/path/to/video.mp4", type: "video/mp4", ...more attributes... }])`</code><br>
This will inject a child `source` element for every element in the array with the specified attributes. `src` is the only required attribute although adding `type` is recommended as it helps the browser decide which file to download and play. This will inject a child `source` element for every element in the array with the specified attributes. `src` is the only required attribute although adding `type` is recommended as it helps the browser decide which file to download and play.
<br><br> <br><br>
<strong>YouTube</strong><br> <strong>YouTube</strong><br>
Currently this API method only accepts a YouTube ID when used with a YouTube player. I will add URL support soon, along with being able to swap between types (e.g. YouTube to Audio or Video and vice versa.) Currently this API method only accepts a YouTube ID when used with a YouTube player. I will add URL support soon, along with being able to swap between types (e.g. YouTube to Audio or Video and vice versa.)
@ -475,7 +480,7 @@ Here's a list of the methods supported:
#### .source() method #### .source() method
This allows changing the plyr source and type on the fly. This allows changing the plyr source and type on the fly.
Video example: Video example:
@ -483,7 +488,7 @@ Video example:
player.source({ player.source({
type: 'video', type: 'video',
title: 'Bug Buck Bunny', title: 'Bug Buck Bunny',
sources: [{ sources: [{
src: 'https://cdn.selz.com/plyr/1.0/movie.mp4', src: 'https://cdn.selz.com/plyr/1.0/movie.mp4',
type: 'video/mp4' type: 'video/mp4'
}, },
@ -508,7 +513,7 @@ Audio example:
player.source({ player.source({
type: 'audio', type: 'audio',
title: '96 by Logistics', title: '96 by Logistics',
sources: [{ sources: [{
src: 'https://cdn.selz.com/plyr/1.0/logistics-96-sample.mp3', src: 'https://cdn.selz.com/plyr/1.0/logistics-96-sample.mp3',
type: 'audio/mp3' type: 'audio/mp3'
}, },
@ -586,7 +591,7 @@ The `plyr` object on the player element also contains a `media` property which i
```javascript ```javascript
var media = document.querySelector(".plyr").plyr.media; var media = document.querySelector(".plyr").plyr.media;
media.addEventListener("playing", function() { media.addEventListener("playing", function() {
console.log("playing"); console.log("playing");
}); });
``` ```
@ -604,14 +609,14 @@ Currently only YouTube is supported. Vimeo will be coming soon. Some HTML5 media
Due to the way the YouTube API works, the `timeupdate` and `progress` events are triggered by polling every 200ms so the event may trigger without an actual value change. Buffering progress is `media.buffered` in the `plyr` object. It is a a number between 0 and 1 that specifies the percentage of the video that the player shows as buffered. Due to the way the YouTube API works, the `timeupdate` and `progress` events are triggered by polling every 200ms so the event may trigger without an actual value change. Buffering progress is `media.buffered` in the `plyr` object. It is a a number between 0 and 1 that specifies the percentage of the video that the player shows as buffered.
```javascript ```javascript
document.querySelector(".plyr").plyr.media.addEventListener("play", function() { document.querySelector(".plyr").plyr.media.addEventListener("play", function() {
console.log("play"); console.log("play");
}); });
``` ```
The `.source()` API method can also be used but the video id must be passed as the argument. The `.source()` API method can also be used but the video id must be passed as the argument.
Currently caption control is not supported but I will work on this. Currently caption control is not supported but I will work on this.
## Fullscreen ## Fullscreen
@ -648,7 +653,7 @@ Fullscreen in Plyr is supported for all browsers that [currently support it](htt
&sup3; IE10 has no native fullscreen support, fallback can be used (see options) &sup3; IE10 has no native fullscreen support, fallback can be used (see options)
The `enabled` option can be used to disable certain User Agents. For example, if you don't want to use Plyr for smartphones, you could use: The `enabled` option can be used to disable certain User Agents. For example, if you don't want to use Plyr for smartphones, you could use:
```javascript ```javascript
enabled: /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent) enabled: /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent)
@ -658,13 +663,13 @@ If a User Agent is disabled but supports `<video>` and `<audio>` natively, it wi
Any unsupported browsers will display links to download the media if the correct html is used. Any unsupported browsers will display links to download the media if the correct html is used.
### Checking for support ### Checking for support
There's an API method for checking support. You can call `plyr.supported()` and optionally pass a type to it, e.g. `plyr.supported("video")`. It will return an object with two keys; `basic` meaning there's basic support for that media type (or both if no type is passed) and `full` meaning there's full support for plyr. There's an API method for checking support. You can call `plyr.supported()` and optionally pass a type to it, e.g. `plyr.supported("video")`. It will return an object with two keys; `basic` meaning there's basic support for that media type (or both if no type is passed) and `full` meaning there's full support for plyr.
## Issues ## Issues
If you find anything weird with Plyr, please let us know using the GitHub issues tracker. If you find anything weird with Plyr, please let us know using the GitHub issues tracker.
## Author ## Author
Plyr is developed by [@sam_potts](https://twitter.com/sam_potts) / [sampotts.me](http://sampotts.me) with help from the awesome [contributors](https://github.com/Selz/plyr/graphs/contributors) Plyr is developed by [@sam_potts](https://twitter.com/sam_potts) / [sampotts.me](http://sampotts.me) with help from the awesome [contributors](https://github.com/Selz/plyr/graphs/contributors)
## Mentions ## Mentions
- [The Changelog](http://thechangelog.com/plyr-simple-html5-media-player-custom-controls-webvtt-captions/) - [The Changelog](http://thechangelog.com/plyr-simple-html5-media-player-custom-controls-webvtt-captions/)
@ -679,7 +684,7 @@ Plyr is developed by [@sam_potts](https://twitter.com/sam_potts) / [sampotts.me]
## Used by ## Used by
- [Selz.com](https://selz.com) - [Selz.com](https://selz.com)
Let me know on [Twitter](https://twitter.com/sam_potts) I can add you to the above list. It'd be awesome to see how you're using Plyr :-) Let me know on [Twitter](https://twitter.com/sam_potts) I can add you to the above list. It'd be awesome to see how you're using Plyr :-)
## Useful links and credits ## Useful links and credits
Credit to the PayPal HTML5 Video player from which Plyr's caption functionality is ported from: Credit to the PayPal HTML5 Video player from which Plyr's caption functionality is ported from:

View File

@ -7,25 +7,26 @@
// Credits: http://paypal.github.io/accessible-html5-video-player/ // Credits: http://paypal.github.io/accessible-html5-video-player/
// ========================================================================== // ==========================================================================
(function (root, factory) { (function(root, factory) {
'use strict';
/*global define,module*/
if (typeof define === 'function' && define.amd) { if (typeof define === 'function' && define.amd) {
// AMD // AMD
define(null, factory); define(null, factory);
} else if (typeof exports === 'object') { } else if (typeof module === 'object') {
// Node, CommonJS-like // Node, CommonJS-like
module.exports = factory(); module.exports = factory();
} else { } else {
// Browser globals (root is window) // Browser globals (root is window)
root.plyr = factory(); root.plyr = factory();
} }
}(this, function () { }(this, function() {
var api = {};
'use strict'; 'use strict';
/*global YT,$f*/ /*global YT,$f*/
// Globals // Globals
var fullscreen, config; var fullscreen, config, api = {};
// Default config // Default config
var defaults = { var defaults = {
@ -41,7 +42,10 @@
iconPrefix: 'icon', iconPrefix: 'icon',
selectors: { selectors: {
container: '.plyr', container: '.plyr',
controls: '.plyr-controls', controls: {
container: null,
wrapper: '.plyr__controls'
},
labels: '[data-plyr] .sr-only, label .sr-only', labels: '[data-plyr] .sr-only, label .sr-only',
buttons: { buttons: {
seek: '[data-plyr="seek"]', seek: '[data-plyr="seek"]',
@ -56,33 +60,35 @@
fullscreen: '[data-plyr="fullscreen"]' fullscreen: '[data-plyr="fullscreen"]'
}, },
progress: { progress: {
container: '.plyr-progress', container: '.plyr__progress',
buffer: '.plyr-progress-buffer', buffer: '.plyr__progress--buffer',
played: '.plyr-progress-played' played: '.plyr__progress--played'
}, },
captions: '.plyr-captions', captions: '.plyr__captions',
currentTime: '.plyr-current-time', currentTime: '.plyr__time--current',
duration: '.plyr-duration' duration: '.plyr__time--duration'
}, },
classes: { classes: {
videoWrapper: 'plyr-video-wrapper', videoWrapper: 'plyr__video-wrapper',
embedWrapper: 'plyr-video-embed', embedWrapper: 'plyr__video-embed',
type: 'plyr-{0}', type: 'plyr--{0}',
stopped: 'stopped', stopped: 'plyr--stopped',
playing: 'playing', playing: 'plyr--playing',
muted: 'muted', muted: 'plyr--muted',
loading: 'loading', loading: 'plyr--loading',
tooltip: 'plyr-tooltip', hover: 'plyr--hover',
hidden: 'sr-only', tooltip: 'plyr__tooltip',
hover: 'plyr-hover', hidden: 'plyr__sr-only',
isIos: 'plyr--is-ios',
isTouch: 'plyr--is-touch',
captions: { captions: {
enabled: 'captions-enabled', enabled: 'plyr--captions-enabled',
active: 'captions-active' active: 'plyr--captions-active'
}, },
fullscreen: { fullscreen: {
enabled: 'fullscreen-enabled', enabled: 'plyr--fullscreen-enabled',
active: 'fullscreen-active', active: 'plyr--fullscreen-active',
hideControls: 'fullscreen-hide-controls' hideControls: 'plyr--fullscreen--hide-controls'
} }
}, },
captions: { captions: {
@ -112,10 +118,11 @@
volume: 'Volume', volume: 'Volume',
toggleMute: 'Toggle Mute', toggleMute: 'Toggle Mute',
toggleCaptions: 'Toggle Captions', toggleCaptions: 'Toggle Captions',
toggleFullscreen: 'Toggle Fullscreen' toggleFullscreen: 'Toggle Fullscreen',
frameTitle: 'Player for {title}'
}, },
types: { types: {
embed: ['youtube','vimeo'], embed: ['youtube', 'vimeo'],
html5: ['video', 'audio'] html5: ['video', 'audio']
}, },
urls: { urls: {
@ -132,25 +139,25 @@
function _buildControls() { function _buildControls() {
// Open and add the progress and seek elements // Open and add the progress and seek elements
var html = [ var html = [
'<div class="plyr-controls">', '<div class="plyr__controls">',
'<div class="plyr-progress">', '<div class="plyr__progress">',
'<label for="seek{id}" class="sr-only">Seek</label>', '<label for="seek{id}" class="plyr__sr-only">Seek</label>',
'<input id="seek{id}" class="plyr-progress-seek" type="range" min="0" max="100" step="0.5" value="0" data-plyr="seek">', '<input id="seek{id}" class="plyr__progress--seek" type="range" min="0" max="100" step="0.5" value="0" data-plyr="seek">',
'<progress class="plyr-progress-played" max="100" value="0">', '<progress class="plyr__progress--played" max="100" value="0">',
'<span>0</span>% ' + config.i18n.played, '<span>0</span>% ' + config.i18n.played,
'</progress>', '</progress>',
'<progress class="plyr-progress-buffer" max="100" value="0">', '<progress class="plyr__progress--buffer" max="100" value="0">',
'<span>0</span>% ' + config.i18n.buffered, '<span>0</span>% ' + config.i18n.buffered,
'</progress>', '</progress>',
'</div>', '</div>',
'<span class="plyr-controls-left">']; '<span class="plyr__controls--left">'];
// Restart button // Restart button
if (_inArray(config.controls, 'restart')) { if (_inArray(config.controls, 'restart')) {
html.push( html.push(
'<button type="button" data-plyr="restart">', '<button type="button" data-plyr="restart">',
'<svg><use xlink:href="#' + config.iconPrefix + '-restart" /></svg>', '<svg><use xlink:href="#' + config.iconPrefix + '-restart" /></svg>',
'<span class="sr-only">' + config.i18n.restart + '</span>', '<span class="plyr__sr-only">' + config.i18n.restart + '</span>',
'</button>' '</button>'
); );
} }
@ -160,7 +167,7 @@
html.push( html.push(
'<button type="button" data-plyr="rewind">', '<button type="button" data-plyr="rewind">',
'<svg><use xlink:href="#' + config.iconPrefix + '-rewind" /></svg>', '<svg><use xlink:href="#' + config.iconPrefix + '-rewind" /></svg>',
'<span class="sr-only">' + config.i18n.rewind + '</span>', '<span class="plyr__sr-only">' + config.i18n.rewind + '</span>',
'</button>' '</button>'
); );
} }
@ -170,11 +177,11 @@
html.push( html.push(
'<button type="button" data-plyr="play">', '<button type="button" data-plyr="play">',
'<svg><use xlink:href="#' + config.iconPrefix + '-play" /></svg>', '<svg><use xlink:href="#' + config.iconPrefix + '-play" /></svg>',
'<span class="sr-only">' + config.i18n.play + '</span>', '<span class="plyr__sr-only">' + config.i18n.play + '</span>',
'</button>', '</button>',
'<button type="button" data-plyr="pause">', '<button type="button" data-plyr="pause">',
'<svg><use xlink:href="#' + config.iconPrefix + '-pause" /></svg>', '<svg><use xlink:href="#' + config.iconPrefix + '-pause" /></svg>',
'<span class="sr-only">' + config.i18n.pause + '</span>', '<span class="plyr__sr-only">' + config.i18n.pause + '</span>',
'</button>' '</button>'
); );
} }
@ -184,7 +191,7 @@
html.push( html.push(
'<button type="button" data-plyr="fast-forward">', '<button type="button" data-plyr="fast-forward">',
'<svg><use xlink:href="#' + config.iconPrefix + '-fast-forward" /></svg>', '<svg><use xlink:href="#' + config.iconPrefix + '-fast-forward" /></svg>',
'<span class="sr-only">' + config.i18n.forward + '</span>', '<span class="plyr__sr-only">' + config.i18n.forward + '</span>',
'</button>' '</button>'
); );
} }
@ -192,9 +199,9 @@
// Media current time display // Media current time display
if (_inArray(config.controls, 'current-time')) { if (_inArray(config.controls, 'current-time')) {
html.push( html.push(
'<span class="plyr-time">', '<span class="plyr__time">',
'<span class="sr-only">' + config.i18n.currentTime + '</span>', '<span class="plyr__sr-only">' + config.i18n.currentTime + '</span>',
'<span class="plyr-current-time">00:00</span>', '<span class="plyr__time--current">00:00</span>',
'</span>' '</span>'
); );
} }
@ -202,9 +209,9 @@
// Media duration display // Media duration display
if (_inArray(config.controls, 'duration')) { if (_inArray(config.controls, 'duration')) {
html.push( html.push(
'<span class="plyr-time">', '<span class="plyr__time">',
'<span class="sr-only">' + config.i18n.duration + '</span>', '<span class="plyr__sr-only">' + config.i18n.duration + '</span>',
'<span class="plyr-duration">00:00</span>', '<span class="plyr__time--duration">00:00</span>',
'</span>' '</span>'
); );
} }
@ -212,16 +219,16 @@
// Close left controls // Close left controls
html.push( html.push(
'</span>', '</span>',
'<span class="plyr-controls-right">' '<span class="plyr__controls--right">'
); );
// Toggle mute button // Toggle mute button
if (_inArray(config.controls, 'mute')) { if (_inArray(config.controls, 'mute')) {
html.push( html.push(
'<button type="button" data-plyr="mute">', '<button type="button" data-plyr="mute">',
'<svg class="icon-muted"><use xlink:href="#' + config.iconPrefix + '-muted" /></svg>', '<svg class="icon--muted"><use xlink:href="#' + config.iconPrefix + '-muted" /></svg>',
'<svg><use xlink:href="#' + config.iconPrefix + '-volume" /></svg>', '<svg><use xlink:href="#' + config.iconPrefix + '-volume" /></svg>',
'<span class="sr-only">' + config.i18n.toggleMute + '</span>', '<span class="plyr__sr-only">' + config.i18n.toggleMute + '</span>',
'</button>' '</button>'
); );
} }
@ -229,8 +236,8 @@
// Volume range control // Volume range control
if (_inArray(config.controls, 'volume')) { if (_inArray(config.controls, 'volume')) {
html.push( html.push(
'<label for="volume{id}" class="sr-only">' + config.i18n.volume + '</label>', '<label for="volume{id}" class="plyr__sr-only">' + config.i18n.volume + '</label>',
'<input id="volume{id}" class="plyr-volume" type="range" min="0" max="10" value="5" data-plyr="volume">' '<input id="volume{id}" class="plyr__volume" type="range" min="0" max="10" value="5" data-plyr="volume">'
); );
} }
@ -238,9 +245,9 @@
if (_inArray(config.controls, 'captions')) { if (_inArray(config.controls, 'captions')) {
html.push( html.push(
'<button type="button" data-plyr="captions">', '<button type="button" data-plyr="captions">',
'<svg class="icon-captions-on"><use xlink:href="#' + config.iconPrefix + '-captions-on" /></svg>', '<svg class="icon--captions-on"><use xlink:href="#' + config.iconPrefix + '-captions-on" /></svg>',
'<svg><use xlink:href="#' + config.iconPrefix + '-captions-off" /></svg>', '<svg><use xlink:href="#' + config.iconPrefix + '-captions-off" /></svg>',
'<span class="sr-only">' + config.i18n.toggleCaptions + '</span>', '<span class="plyr__sr-only">' + config.i18n.toggleCaptions + '</span>',
'</button>' '</button>'
); );
} }
@ -249,9 +256,9 @@
if (_inArray(config.controls, 'fullscreen')) { if (_inArray(config.controls, 'fullscreen')) {
html.push( html.push(
'<button type="button" data-plyr="fullscreen">', '<button type="button" data-plyr="fullscreen">',
'<svg class="icon-exit-fullscreen"><use xlink:href="#' + config.iconPrefix + '-exit-fullscreen" /></svg>', '<svg class="icon--exit-fullscreen"><use xlink:href="#' + config.iconPrefix + '-exit-fullscreen" /></svg>',
'<svg><use xlink:href="#' + config.iconPrefix + '-enter-fullscreen" /></svg>', '<svg><use xlink:href="#' + config.iconPrefix + '-enter-fullscreen" /></svg>',
'<span class="sr-only">' + config.i18n.toggleFullscreen + '</span>', '<span class="plyr__sr-only">' + config.i18n.toggleFullscreen + '</span>',
'</button>' '</button>'
); );
} }
@ -338,7 +345,8 @@
return { return {
name: name, name: name,
version: majorVersion, version: majorVersion,
ios: /(iPad|iPhone|iPod)/g.test(navigator.platform) ios: /(iPad|iPhone|iPod)/g.test(navigator.platform),
touch: 'ontouchstart' in document.documentElement
}; };
} }
@ -477,14 +485,14 @@
} }
// Toggle class on an element // Toggle class on an element
function _toggleClass(element, name, state) { function _toggleClass(element, className, state) {
if (element) { if (element) {
if (element.classList) { if (element.classList) {
element.classList[state ? 'add' : 'remove'](name); element.classList[state ? 'add' : 'remove'](className);
} }
else { else {
var className = (' ' + element.className + ' ').replace(/\s+/g, ' ').replace(' ' + name + ' ', ''); var current = (' ' + element.className + ' ').replace(/\s+/g, ' ').replace(' ' + className + ' ', '');
element.className = className + (state ? ' ' + name : ''); element.className = current + (state ? ' ' + className : '');
} }
} }
} }
@ -645,12 +653,30 @@
function _storage() { function _storage() {
var storage = { var storage = {
supported: (function() { supported: (function() {
try { if(!('localStorage' in window)) {
return 'localStorage' in window && window.localStorage !== null; return false;
} }
catch(e) {
return false; // Try to use it (it might be disabled, e.g. user is in private/porn mode)
} // see: https://github.com/Selz/plyr/issues/131
try {
// Add test item
window.localStorage.setItem('___test', 'OK');
// Get the test item
var result = window.localStorage.getItem('___test');
// Clean up
window.localStorage.removeItem('___test');
// Check if value matches
return (result === 'OK');
}
catch (e) {
return false;
}
return false;
})() })()
}; };
return storage; return storage;
@ -793,8 +819,25 @@
// Replace all id references with random numbers // Replace all id references with random numbers
html = _replaceAll(html, '{id}', Math.floor(Math.random() * (10000))); html = _replaceAll(html, '{id}', Math.floor(Math.random() * (10000)));
// Inject into the container // Controls container
plyr.container.insertAdjacentHTML('beforeend', html); var container;
// Inject to custom location
if (config.selectors.controls.container !== null) {
container = config.selectors.controls.container;
if(typeof selector === 'string') {
container = document.querySelector(container);
}
}
// Inject into the container by default
if (!(container instanceof HTMLElement)) {
container = plyr.container
}
// Inject controls HTML
container.insertAdjacentHTML('beforeend', html);
// Setup tooltips // Setup tooltips
if (config.tooltips) { if (config.tooltips) {
@ -812,7 +855,7 @@
// Find the UI controls and store references // Find the UI controls and store references
function _findElements() { function _findElements() {
try { try {
plyr.controls = _getElement(config.selectors.controls); plyr.controls = _getElement(config.selectors.controls.wrapper);
// Buttons // Buttons
plyr.buttons = {}; plyr.buttons = {};
@ -863,13 +906,8 @@
} }
} }
// Setup aria attribute for play // Setup aria attribute for play and iframe title
function _setupPlayAria() { function _setTitle(iframe) {
// If there's no play button, bail
if (!plyr.buttons.play) {
return;
}
// Find the current text // Find the current text
var label = plyr.buttons.play.innerText || config.i18n.play; var label = plyr.buttons.play.innerText || config.i18n.play;
@ -878,7 +916,16 @@
label += ', ' + config.title; label += ', ' + config.title;
} }
plyr.buttons.play.setAttribute('aria-label', label); // If there's no play button, bail
if (plyr.buttons.play) {
plyr.buttons.play.setAttribute('aria-label', label);
}
// Set iframe title
// https://github.com/Selz/plyr/issues/124
if (iframe instanceof HTMLElement) {
iframe.setAttribute('title', config.i18n.frameTitle.replace('{title}', config.title));
}
} }
// Setup media // Setup media
@ -900,9 +947,10 @@
_toggleClass(plyr.container, config.classes.stopped, config.autoplay); _toggleClass(plyr.container, config.classes.stopped, config.autoplay);
// Add iOS class // Add iOS class
if (plyr.browser.ios) { _toggleClass(plyr.container, config.classes.isIos, plyr.browser.ios);
_toggleClass(plyr.container, 'ios', true);
} // Add touch class
_toggleClass(plyr.container, config.classes.isTouch, plyr.browser.touch);
// Inject the player wrapper // Inject the player wrapper
if (plyr.type === 'video') { if (plyr.type === 'video') {
@ -1013,11 +1061,14 @@
// Inject and update UI // Inject and update UI
if (plyr.supported.full) { if (plyr.supported.full) {
// Only setup controls once // Only setup controls once
if (!plyr.container.querySelectorAll(config.selectors.controls).length) { if (!plyr.container.querySelectorAll(config.selectors.controls.wrapper).length) {
_setupInterface(); _setupInterface();
} }
} }
// Set title
_setTitle(_getElement('iframe'));
// Set the volume // Set the volume
_setVolume(); _setVolume();
_updateVolume(); _updateVolume();
@ -1966,7 +2017,7 @@
if ('title' in source) { if ('title' in source) {
config.title = source.title; config.title = source.title;
_setupPlayAria(); _setTitle();
} }
} }
@ -2120,7 +2171,7 @@
plyr.init = false; plyr.init = false;
// Remove controls // Remove controls
_remove(_getElement(config.selectors.controls)); _remove(_getElement(config.selectors.controls.wrapper));
// YouTube // YouTube
if (plyr.type === 'youtube') { if (plyr.type === 'youtube') {
@ -2162,6 +2213,9 @@
// Get the media element // Get the media element
plyr.media = plyr.container.querySelectorAll('audio, video, div')[0]; plyr.media = plyr.container.querySelectorAll('audio, video, div')[0];
// Add style hook
_toggleClass(plyr.container, defaults.selectors.container.replace('.', ''), true);
// Get original classname // Get original classname
plyr.originalClassName = plyr.container.className; plyr.originalClassName = plyr.container.className;
@ -2177,7 +2231,7 @@
plyr.media.removeAttribute('data-video-id'); plyr.media.removeAttribute('data-video-id');
} }
else { else {
plyr.type = tagName; plyr.type = tagName;
config.crossorigin = (plyr.media.getAttribute('crossorigin') !== null); config.crossorigin = (plyr.media.getAttribute('crossorigin') !== null);
config.autoplay = (config.autoplay || (plyr.media.getAttribute('autoplay') !== null)); config.autoplay = (config.autoplay || (plyr.media.getAttribute('autoplay') !== null));
config.loop = (config.loop || (plyr.media.getAttribute('loop') !== null)); config.loop = (config.loop || (plyr.media.getAttribute('loop') !== null));
@ -2216,8 +2270,8 @@
_displayDuration(); _displayDuration();
} }
// Set up aria-label for Play button with the title option // Set title on button and frame
_setupPlayAria(); _setTitle();
} }
// Successful setup // Successful setup
@ -2322,7 +2376,7 @@
// Expose setup function // Expose setup function
api.setup = function(elements, options) { api.setup = function(elements, options) {
// Get the players // Get the players
var instances = [], elements = []; var instances = [];
// Select the elements // Select the elements
// Assume elements is a NodeList by default // Assume elements is a NodeList by default

View File

@ -141,18 +141,6 @@
border: 0; border: 0;
} }
// Screen reader only
// -------------------------------
.sr-only {
position: absolute !important;
clip: rect(1px, 1px, 1px, 1px);
padding: 0 !important;
border: 0 !important;
height: 1px !important;
width: 1px !important;
overflow: hidden;
}
// Styles // Styles
// ------------------------------- // -------------------------------
// Base // Base
@ -170,8 +158,19 @@
box-sizing: border-box; box-sizing: border-box;
} }
// Screen reader only elements
&__sr-only {
position: absolute !important;
clip: rect(1px, 1px, 1px, 1px);
padding: 0 !important;
border: 0 !important;
height: 1px !important;
width: 1px !important;
overflow: hidden;
}
// For video // For video
&-video-wrapper { &__video-wrapper {
position: relative; position: relative;
} }
video, video,
@ -181,8 +180,8 @@
vertical-align: middle; vertical-align: middle;
} }
// For embeds // Container for embeds
&-video-embed { &__video-embed {
padding-bottom: 56.25%; /* 16:9 */ padding-bottom: 56.25%; /* 16:9 */
height: 0; height: 0;
overflow: hidden; overflow: hidden;
@ -207,7 +206,7 @@
} }
// Captions // Captions
&-captions { &__captions {
display: none; display: none;
position: absolute; position: absolute;
bottom: 0; bottom: 0;
@ -232,15 +231,15 @@
font-size: @font-size-captions-medium; font-size: @font-size-captions-medium;
} }
} }
&.captions-active &-captions { &--captions-active &__captions {
display: block; display: block;
} }
&.fullscreen-active &-captions { &--fullscreen-active &__captions {
font-size: @font-size-captions-large; font-size: @font-size-captions-large;
} }
// Playback controls // Playback controls
&-controls { &__controls {
.clearfix(); .clearfix();
.font-smoothing(); .font-smoothing();
position: relative; position: relative;
@ -251,15 +250,15 @@
box-shadow: 0 1px 1px rgba(red(@gray-dark), green(@gray-dark), blue(@gray-dark), .2); box-shadow: 0 1px 1px rgba(red(@gray-dark), green(@gray-dark), blue(@gray-dark), .2);
// Layout // Layout
&-right { &--right {
display: block; display: block;
margin: @control-spacing auto 0; margin: @control-spacing auto 0;
} }
@media (min-width: @bp-control-split) { @media (min-width: @bp-control-split) {
&-left { &--left {
float: left; float: left;
} }
&-right { &--right {
float: right; float: right;
margin-top: 0; margin-top: 0;
} }
@ -300,14 +299,14 @@
} }
// Hide toggle icons by default // Hide toggle icons by default
.icon-exit-fullscreen, .icon--exit-fullscreen,
.icon-muted, .icon--muted,
.icon-captions-on { .icon--captions-on {
display: none; display: none;
} }
// plyr time // plyr time
.plyr-time { .plyr__time {
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
margin-left: @control-spacing; margin-left: @control-spacing;
@ -318,7 +317,7 @@
} }
// Media duration hidden on small screens // Media duration hidden on small screens
.plyr-time + .plyr-time { .plyr__time + .plyr__time {
display: none; display: none;
@media (min-width: @bp-control-split) { @media (min-width: @bp-control-split) {
@ -334,7 +333,7 @@
} }
// Tooltips // Tooltips
&-tooltip { &__tooltip {
position: absolute; position: absolute;
z-index: 2; z-index: 2;
bottom: 100%; bottom: 100%;
@ -383,18 +382,18 @@
z-index: 2; z-index: 2;
} }
} }
button:hover .plyr-tooltip, button:hover .plyr__tooltip,
button.tab-focus:focus .plyr-tooltip { button.tab-focus:focus .plyr__tooltip {
opacity: 1; opacity: 1;
transform: translate(-50%, 0) scale(1); transform: translate(-50%, 0) scale(1);
} }
button:hover .plyr-tooltip { button:hover .plyr__tooltip {
z-index: 3; z-index: 3;
} }
// Playback progress // Playback progress
// <progress> element // <progress> element
&-progress { &__progress {
position: absolute; position: absolute;
bottom: 100%; bottom: 100%;
left: 0; left: 0;
@ -403,9 +402,9 @@
height: @control-spacing; height: @control-spacing;
background: @progress-bg; background: @progress-bg;
&-buffer[value], &--buffer[value],
&-played[value], &--played[value],
&-seek[type='range'] { &--seek[type='range'] {
position: absolute; position: absolute;
left: 0; left: 0;
top: 0; top: 0;
@ -420,8 +419,8 @@
border: none; border: none;
background: transparent; background: transparent;
} }
&-buffer[value], &--buffer[value],
&-played[value] { &--played[value] {
&::-webkit-progress-bar { &::-webkit-progress-bar {
background: transparent; background: transparent;
} }
@ -434,18 +433,18 @@
background: currentColor; background: currentColor;
} }
} }
&-played[value] { &--played[value] {
z-index: 2; z-index: 2;
color: @progress-playing-bg; color: @progress-playing-bg;
} }
&-buffer[value] { &--buffer[value] {
color: @progress-buffered-bg; color: @progress-buffered-bg;
} }
// Seek control // Seek control
// <input[type='range']> element // <input[type='range']> element
// Specificity is for bootstrap compatibility // Specificity is for bootstrap compatibility
&-seek[type='range'] { &--seek[type='range'] {
z-index: 4; z-index: 4;
cursor: pointer; cursor: pointer;
outline: 0; outline: 0;
@ -491,7 +490,7 @@
} }
// Loading state // Loading state
&.loading .plyr-progress-buffer { &--loading .plyr__progress--buffer {
animation: progress 1s linear infinite; animation: progress 1s linear infinite;
background-size: @progress-loading-size @progress-loading-size; background-size: @progress-loading-size @progress-loading-size;
background-repeat: repeat-x; background-repeat: repeat-x;
@ -509,18 +508,18 @@
} }
// States // States
&-controls [data-plyr='pause'], &__controls [data-plyr='pause'],
&.playing .plyr-controls [data-plyr='play'] { &--playing .plyr__controls [data-plyr='play'] {
display: none; display: none;
} }
&.playing .plyr-controls [data-plyr='pause'] { &--playing .plyr__controls [data-plyr='pause'] {
display: inline-block; display: inline-block;
} }
// Volume control // Volume control
// <input[type='range']> element // <input[type='range']> element
// Specificity is for bootstrap compatibility // Specificity is for bootstrap compatibility
&-volume[type='range'] { &__volume[type='range'] {
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
-webkit-appearance: none; -webkit-appearance: none;
@ -588,30 +587,30 @@
// Hide sound controls on iOS // Hide sound controls on iOS
// It's not supported to change volume using JavaScript: // It's not supported to change volume using JavaScript:
// https://developer.apple.com/library/safari/documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/Device-SpecificConsiderations/Device-SpecificConsiderations.html // https://developer.apple.com/library/safari/documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/Device-SpecificConsiderations/Device-SpecificConsiderations.html
&.ios &-volume, &--is-ios &-volume,
&.ios [data-plyr='mute'], &--is-ios [data-plyr='mute'],
&-audio.ios &-controls-right { &--is-ios.plyr--audio &__controls--right {
display: none; display: none;
} }
// Center buttons so it looks less odd // Center buttons so it looks less odd
&-audio.ios &-controls-left { &--is-ios.plyr--audio &__controls--left {
float: none; float: none;
} }
// Audio specific styles // Audio specific styles
// Position the progress within the container // Position the progress within the container
&-audio .plyr-controls { &--audio .plyr__controls {
padding-top: (@control-spacing * 2); padding-top: (@control-spacing * 2);
} }
&-audio .plyr-progress { &--audio .plyr__progress {
bottom: auto; bottom: auto;
top: 0; top: 0;
background: @off-white; background: @off-white;
} }
// Full screen mode // Full screen mode
&-fullscreen, &--fullscreen,
&.fullscreen-active { &--fullscreen-active {
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
@ -637,23 +636,23 @@
} }
// Hide controls when playing in full screen // Hide controls when playing in full screen
&.fullscreen-hide-controls.playing { &--fullscreen--hide-controls.plyr--playing {
.plyr-controls { .plyr__controls {
transform: translateY(100%) translateY(@control-spacing / 2); transform: translateY(100%) translateY(@control-spacing / 2);
transition: transform .3s .2s ease; transition: transform .3s .2s ease;
} }
&.plyr-hover .plyr-controls { &.plyr--hover .plyr__controls {
transform: translateY(0); transform: translateY(0);
} }
.plyr-captions { .plyr__captions {
bottom: (@control-spacing / 2); bottom: (@control-spacing / 2);
transition: bottom .3s .2s ease; transition: bottom .3s .2s ease;
} }
} }
// Captions // Captions
.plyr-captions, .plyr__captions,
&.fullscreen-hide-controls.playing.plyr-hover .plyr-captions { &--fullscreen--hide-controls.plyr--playing.plyr--hover .plyr__captions {
top: auto; top: auto;
bottom: 90px; bottom: 90px;
@ -664,9 +663,9 @@
} }
// Change icons on state change // Change icons on state change
&.fullscreen-active .icon-exit-fullscreen, &--fullscreen-active .icon--exit-fullscreen,
&.muted .plyr-controls .icon-muted, &--muted .plyr__controls .icon--muted,
&.captions-active .plyr-controls .icon-captions-on { &--captions-active .plyr__controls .icon--captions-on {
display: block; display: block;
& + svg { & + svg {
@ -679,8 +678,8 @@
[data-plyr='fullscreen'] { [data-plyr='fullscreen'] {
display: none; display: none;
} }
&.captions-enabled [data-plyr='captions'], &--captions-enabled [data-plyr='captions'],
&.fullscreen-enabled [data-plyr='fullscreen'] { &--fullscreen-enabled [data-plyr='fullscreen'] {
display: inline-block; display: inline-block;
} }
} }