Progress for buffer, Safari 8 fix, validating 'html' option

This commit is contained in:
Sam Potts 2015-02-22 10:17:39 +11:00
parent 5f96172dbd
commit 49038e3ca9
10 changed files with 243 additions and 115 deletions

View File

@ -1,6 +1,6 @@
// ========================================================================== // ==========================================================================
// Plyr // Plyr
// plyr.js v1.0.8 // plyr.js v1.0.9
// https://github.com/sampotts/plyr // https://github.com/sampotts/plyr
// ========================================================================== // ==========================================================================
// Credits: http://paypal.github.io/accessible-html5-video-player/ // Credits: http://paypal.github.io/accessible-html5-video-player/
@ -33,7 +33,11 @@
captions: "[data-player='captions']", captions: "[data-player='captions']",
fullscreen: "[data-player='fullscreen']" fullscreen: "[data-player='fullscreen']"
}, },
progress: ".player-progress", progress: {
container: ".player-progress",
buffer: ".player-progress-buffer",
played: ".player-progress-played"
},
captions: ".player-captions", captions: ".player-captions",
duration: ".player-duration", duration: ".player-duration",
seekTime: ".player-seek-time" seekTime: ".player-seek-time"
@ -205,6 +209,11 @@
element.removeEventListener(event, callback, false); element.removeEventListener(event, callback, false);
} }
// Get percentage
function _getPercentage(current, max) {
return Math.floor((current / max) * 100);
}
// Get click position relative to parent // Get click position relative to parent
// http://www.kirupa.com/html5/getting_mouse_click_position.htm // http://www.kirupa.com/html5/getting_mouse_click_position.htm
function _getClickPosition(event) { function _getClickPosition(event) {
@ -419,30 +428,47 @@
// Find the UI controls and store references // Find the UI controls and store references
function _findElements() { function _findElements() {
player.controls = _getElement(config.selectors.controls); try {
player.controls = _getElement(config.selectors.controls);
// Buttons // Buttons
player.buttons = {}; player.buttons = {};
player.buttons.play = _getElement(config.selectors.buttons.play); player.buttons.play = _getElement(config.selectors.buttons.play);
player.buttons.pause = _getElement(config.selectors.buttons.pause); player.buttons.pause = _getElement(config.selectors.buttons.pause);
player.buttons.restart = _getElement(config.selectors.buttons.restart); player.buttons.restart = _getElement(config.selectors.buttons.restart);
player.buttons.rewind = _getElement(config.selectors.buttons.rewind); player.buttons.rewind = _getElement(config.selectors.buttons.rewind);
player.buttons.forward = _getElement(config.selectors.buttons.forward); player.buttons.forward = _getElement(config.selectors.buttons.forward);
player.buttons.mute = _getElement(config.selectors.buttons.mute); player.buttons.mute = _getElement(config.selectors.buttons.mute);
player.buttons.captions = _getElement(config.selectors.buttons.captions); player.buttons.captions = _getElement(config.selectors.buttons.captions);
player.buttons.fullscreen = _getElement(config.selectors.buttons.fullscreen); player.buttons.fullscreen = _getElement(config.selectors.buttons.fullscreen);
// Progress // Progress
player.progress = {}; player.progress = {};
player.progress.bar = _getElement(config.selectors.progress); player.progress.container = _getElement(config.selectors.progress.container);
player.progress.text = player.progress.bar.getElementsByTagName("span")[0];
// Volume // Progress - Buffering
player.volume = _getElement(config.selectors.buttons.volume); player.progress.buffer = {};
player.progress.buffer.bar = _getElement(config.selectors.progress.buffer);
player.progress.buffer.text = player.progress.buffer.bar.getElementsByTagName("span")[0];
// Timing // Progress - Played
player.duration = _getElement(config.selectors.duration); player.progress.played = {};
player.seekTime = _getElements(config.selectors.seekTime); 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 // Setup media
@ -526,7 +552,15 @@
} }
// If caption file exists, process captions // If caption file exists, process captions
else { else {
var track = {}, tracks, j; // 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 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) || if ((player.browserName === "IE" && player.browserMajorVersion === 10) ||
@ -538,28 +572,18 @@
// Set to false so skips to "manual" captioning // Set to false so skips to "manual" captioning
player.isTextTracks = false; player.isTextTracks = false;
// Turn off native caption rendering to avoid double captions [doesn"t work in Safari 7; see patch below]
track = {};
tracks = player.media.textTracks;
for (j=0; j < tracks.length; j++) {
track = player.media.textTracks[j];
track.mode = "hidden";
}
} }
// Rendering caption tracks - native support required - http://caniuse.com/webvtt // Rendering caption tracks
// Native support required - http://caniuse.com/webvtt
if (player.isTextTracks) { if (player.isTextTracks) {
_log("textTracks supported"); _log("textTracks supported");
_showCaptions(player);
for (var y=0; y < tracks.length; y++) {
var track = tracks[y];
track = {};
tracks = player.media.textTracks;
for (j=0; j < tracks.length; j++) {
track = player.media.textTracks[j];
track.mode = "hidden";
if (track.kind === "captions") { if (track.kind === "captions") {
_on(track, "cuechange",function() { _on(track, "cuechange", function() {
if (this.activeCues[0]) { if (this.activeCues[0]) {
if (this.activeCues[0].hasOwnProperty("text")) { if (this.activeCues[0].hasOwnProperty("text")) {
player.captionsContainer.innerHTML = this.activeCues[0].text; player.captionsContainer.innerHTML = this.activeCues[0].text;
@ -572,7 +596,6 @@
// Caption tracks not natively supported // Caption tracks not natively supported
else { else {
_log("textTracks not supported so rendering captions manually"); _log("textTracks not supported so rendering captions manually");
_showCaptions(player);
// Render captions from array at appropriate time // Render captions from array at appropriate time
player.currentCaption = ""; player.currentCaption = "";
@ -601,12 +624,12 @@
xhr.onreadystatechange = function() { xhr.onreadystatechange = function() {
if (xhr.readyState === 4) { if (xhr.readyState === 4) {
if (xhr.status === 200) { if (xhr.status === 200) {
player.captions = [];
var records = [], var records = [],
record, record,
req = xhr.responseText; req = xhr.responseText;
records = req.split("\n\n"); records = req.split("\n\n");
for (var r=0; r < records.length; r++) { for (var r=0; r < records.length; r++) {
record = records[r]; record = records[r];
player.captions[r] = []; player.captions[r] = [];
@ -630,13 +653,17 @@
} }
} }
// If Safari 7, removing track from DOM [see "turn off native caption rendering" above] // If Safari 7+, removing track from DOM [see "turn off native caption rendering" above]
if (player.browserName === "Safari" && player.browserMajorVersion === 7) { if (player.browserName === "Safari" && player.browserMajorVersion >= 7) {
_log("Safari 7 detected; removing track from DOM"); _log("Safari 7+ detected; removing track from DOM");
// Find all <track> elements
tracks = player.media.getElementsByTagName("track"); tracks = player.media.getElementsByTagName("track");
player.media.removeChild(tracks[0]); // Loop through and remove one by one
for (var t=0; t < tracks.length; t++) {
player.media.removeChild(tracks[t]);
}
} }
} }
} }
@ -848,6 +875,54 @@
_toggleClass(player.container, config.classes.muted, (player.media.volume === 0 || player.media.muted)); _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 // Listen for events
function _listeners() { function _listeners() {
// Play // Play
@ -907,30 +982,13 @@
} }
// Duration // Duration
_on(player.media, "timeupdate", function() { _on(player.media, "timeupdate", _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 // Playing progress
player.duration.innerHTML = player.mins + ":" + player.secs; _on(player.media, "timeupdate", _updateProgress);
});
// Progress bar
_on(player.media, "timeupdate", function() {
player.percent = (100 / player.media.duration) * player.media.currentTime;
if (player.percent > 0) {
player.progress.bar.value = player.percent;
player.progress.text.innerHTML = player.percent;
}
});
// Skip when clicking progress bar // Skip when clicking progress bar
_on(player.progress.bar, "click", function(event) { _on(player.progress.played.bar, "click", function(event) {
player.pos = _getClickPosition(event).x / this.offsetWidth; player.pos = _getClickPosition(event).x / this.offsetWidth;
player.media.currentTime = player.pos * player.media.duration; player.media.currentTime = player.pos * player.media.duration;
@ -953,6 +1011,15 @@
_toggleClass(player.container, config.classes.stopped, true); _toggleClass(player.container, config.classes.stopped, true);
_toggleClass(player.container, config.classes.playing); _toggleClass(player.container, config.classes.playing);
}); });
_on(player.media, "loadstart", function() {
_log("loadstart");
});
// Check for buffer progress
_on(player.media, "progress", _updateProgress);
// Also check on start of playing
_on(player.media, "playing", _updateProgress);
} }
function _init() { function _init() {
@ -992,7 +1059,12 @@
_injectControls(); _injectControls();
// Find the elements // Find the elements
_findElements(); if(!_findElements()) {
return false;
}
// Captions
_setupCaptions();
// Set volume // Set volume
_setVolume(); _setVolume();
@ -1000,9 +1072,6 @@
// Setup fullscreen // Setup fullscreen
_setupFullscreen(); _setupFullscreen();
// Captions
_setupCaptions();
// Seeking // Seeking
_setupSeeking(); _setupSeeking();

View File

@ -18,8 +18,9 @@
@control-spacing: 10px; @control-spacing: 10px;
// Progress // Progress
@progress-bg: @gray; @progress-bg: lighten(@gray, 10%);
@progress-value-bg: @blue; @progress-playing-bg: @blue;
@progress-buffered-bg: @gray;
// Range // Range
@range-track-height: 6px; @range-track-height: 6px;
@ -96,9 +97,8 @@
max-width: 100%; max-width: 100%;
min-width: 290px; min-width: 290px;
overflow: hidden; // For the controls overflow: hidden; // For the controls
background: #000;
// BORDER-BOX ALL THE THINGS! // border-box everything
// http://paulirish.com/2012/box-sizing-border-box-ftw/ // http://paulirish.com/2012/box-sizing-border-box-ftw/
&, &,
*, *,
@ -130,10 +130,10 @@
font-size: 16px; font-size: 16px;
font-weight: 600; font-weight: 600;
text-shadow: text-shadow:
-1px -1px 0 rgba(red(@gray-dark), green(@gray-dark), blue(@gray-dark), .5), -1px -1px 0 @gray,
1px -1px 0 rgba(red(@gray-dark), green(@gray-dark), blue(@gray-dark), .5), 1px -1px 0 @gray,
-1px 1px 0 rgba(red(@gray-dark), green(@gray-dark), blue(@gray-dark), .5), -1px 1px 0 @gray,
1px 1px 0 rgba(red(@gray-dark), green(@gray-dark), blue(@gray-dark), .5); 1px 1px 0 @gray;
text-align: center; text-align: center;
.font-smoothing(); .font-smoothing();
@ -206,18 +206,12 @@
input:focus + label, input:focus + label,
button:focus { button:focus {
.tab-focus(); .tab-focus();
color: #fff;
svg {
fill: #fff;
}
} }
button:hover, button:hover,
input + label:hover { input + label:hover {
background: @control-color-active; background: @control-color-active;
color: #fff;
svg {
fill: #fff;
}
} }
.icon-exit-fullscreen, .icon-exit-fullscreen,
.icon-muted { .icon-muted {
@ -243,27 +237,48 @@
right: 0; right: 0;
width: 100%; width: 100%;
height: @control-spacing; height: @control-spacing;
margin: 0; background: @progress-bg;
vertical-align: top;
&-buffer,
&[value] { &-played {
-webkit-appearance: none; position: absolute;
border: none; left: 0;
background: @progress-bg; top: 0;
width: 100%;
height: 100%;
margin: 0;
vertical-align: top;
&[value] {
-webkit-appearance: none;
border: none;
background: transparent;
&::-webkit-progress-bar {
background: transparent;
}
// Inherit from currentColor;
&::-webkit-progress-value {
background: currentColor;
transition: width .1s ease;
}
&::-moz-progress-bar {
background: currentColor;
transition: width .1s ease;
}
}
}
&-played {
z-index: 2;
}
&-played[value] {
cursor: pointer; cursor: pointer;
color: @progress-value-bg; color: @progress-playing-bg;
}
&::-webkit-progress-bar { &-buffer[value] {
background: @progress-bg; color: @progress-buffered-bg;
}
// Inherit from currentColor;
&::-webkit-progress-value {
background: currentColor;
}
&::-moz-progress-bar {
background: currentColor;
}
} }
} }

View File

@ -1,7 +1,12 @@
<div class="player-controls"> <div class="player-controls">
<progress class="player-progress" max="100" value="0"> <div class="player-progress">
<span>0</span>% played <progress class="player-progress-played" max="100" value="0">
</progress> <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"> <span class="player-controls-playback">
<button type="button" data-player="restart"> <button type="button" data-player="restart">
<svg><use xlink:href="#icon-refresh"></use></svg> <svg><use xlink:href="#icon-refresh"></use></svg>

36
changelog.md Normal file
View File

@ -0,0 +1,36 @@
# Changelog
## v1.0.9
- Added buffer progress bar
- Fixed Safari 8 caption track (it needs to be removed from the DOM like in Safari 7)
- Added validation (it works or it doesn't basically) of the `html` option passed
## v1.0.8
- Bug fix
## v1.0.7
- Storing user selected volume in local storage
## v1.0.6
- Fullscreen fallback for older browsers to use "full window"
## v1.0.5
- More minor bug fixes and improvements
## v1.0.4
- Fixed caption legibility issues
## v1.0.3
- Minor bug fixes
## v1.0.2
- Added OGG to <audio> example for Firefox
- Fixed IE11 fullscreen issues
## v1.0.1
- Bug fixes for IE (as per usual)
- Added CSS hooks for media type
- Return instances of Plyr to the element
## v1.0.0
- Initial release

2
dist/css/plyr.css vendored

File diff suppressed because one or more lines are too long

2
dist/js/docs.js vendored

File diff suppressed because one or more lines are too long

2
dist/js/plyr.js vendored

File diff suppressed because one or more lines are too long

View File

@ -1,2 +1,2 @@
var templates = {}; 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(" <progress class=\"player-progress\" 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(" <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: { }}); 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: { }});

View File

@ -26,7 +26,7 @@
<source src="//cdn.sampotts.me/plyr/movie.webm" type="video/webm"> <source src="//cdn.sampotts.me/plyr/movie.webm" type="video/webm">
<!-- Text track file --> <!-- Text track file -->
<track kind="captions" label="English captions" src="//cdn.sampotts.me/plyr/movie_captions.vtt" srclang="en" default> <track kind="captions" label="English" srclang="en" src="assets/movie_en_captions.vtt" default>
<!-- Fallback for browsers that don't support the <video> element --> <!-- Fallback for browsers that don't support the <video> element -->
<div> <div>

View File

@ -8,7 +8,7 @@ We wanted a lightweight, accessible and customisable media player that just supp
## Features ## Features
- **Accessible** - full support for captions and screen readers. - **Accessible** - full support for captions and screen readers.
- **Lightweight** - just 4KB minified and gzipped. - **Lightweight** - just 4.8KB minified and gzipped.
- **Customisable** - make the player look how you want with the markup you want. - **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. - **Semantic** - uses HTML5 form inputs for volume (range) and progress element for playback progress.
- **No dependencies** - written in native JS. - **No dependencies** - written in native JS.
@ -20,6 +20,9 @@ We wanted a lightweight, accessible and customisable media player that just supp
- Accept a string selector, a node, or a nodelist for the `container` property of `selectors`. - Accept a string selector, a node, or a nodelist for the `container` property of `selectors`.
- Accept a selector for the `html` template property. - Accept a selector for the `html` template property.
## Changelog
Check out [the changelog](changelog.md)
## Implementation ## Implementation
### Bower ### Bower