Compare commits

...

24 Commits

Author SHA1 Message Date
f477fdf9e2 Minor changes 2015-02-22 10:19:47 +11:00
49038e3ca9 Progress for buffer, Safari 8 fix, validating 'html' option 2015-02-22 10:17:39 +11:00
5f96172dbd Bumping version 2015-02-19 23:18:39 +11:00
2bd6a8390c Fix for empty local storage 2015-02-19 23:16:51 +11:00
3e68cec6ea Bumping version 2015-02-19 22:38:15 +11:00
b24d763d40 Storing volume in local storage 2015-02-19 22:37:41 +11:00
d690560fc2 Fullscreen fallback for older browsers 2015-02-19 21:46:45 +11:00
d46d40fa17 Font size fix for examples, tidied up variables 2015-02-19 12:21:37 +11:00
18001e7799 Fix for control alignment 2015-02-18 17:10:41 +11:00
aa39aa8a58 Prevent multiple instances on one element 2015-02-18 16:17:27 +11:00
c7c48bbe3c Improved caption legibility on white backgrounds 2015-02-18 00:56:07 +11:00
484617e2d7 Tidy up 2015-02-18 00:18:35 +11:00
841cc957c9 Improve docs layout on small screens 2015-02-18 00:01:35 +11:00
e89e87de62 Prevent poster being downloaded twice 2015-02-17 23:40:57 +11:00
b7ea8c3875 Merge 2015-02-17 23:18:03 +11:00
a67e495910 Merge branch 'master' of github.com:selz/plyr
Conflicts:
	dist/js/plyr.js
2015-02-17 23:17:39 +11:00
97d6216409 Removed XHR for IE8 2015-02-17 23:17:15 +11:00
c55faa3505 Added OGG to <audio> example, Fixed IE11 Fullscreen 2015-02-17 22:57:00 +11:00
f8d71829e0 JS updated 2015-02-17 21:52:20 +11:00
bde1df7a98 Fixing display issues in IE
- Added class hooks for media type
- Returning plyr instances when calling .setup()
2015-02-17 21:49:31 +11:00
9827e6a0bc Prevent buttons submitting forms, return players 2015-02-17 18:44:31 +11:00
fc2bb9fcb4 Demo update, missing semi colon 2015-02-17 16:14:29 +11:00
c0254d76e3 Added bower reference 2015-02-17 15:54:02 +11:00
d00b9dc44b Tweaks to docs 2015-02-17 15:43:09 +11:00
17 changed files with 587 additions and 307 deletions

View File

@ -1,13 +1,11 @@
// ========================================================================== // ==========================================================================
// Plyr // Plyr
// plyr.js v1.0.0 // 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/
// ========================================================================== // ==========================================================================
/*global ActiveXObject*/
(function (api) { (function (api) {
"use strict"; "use strict";
@ -35,32 +33,58 @@
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"
}, },
classes: { classes: {
videoContainer: "player-video", video: "player-video",
videoWrapper: "player-video-wrapper",
audio: "player-audio",
stopped: "stopped", stopped: "stopped",
playing: "playing", playing: "playing",
muted: "muted", muted: "muted",
captions: { captions: {
active: "captions-active", enabled: "captions-enabled",
enabled: "captions-enabled" active: "captions-active"
}, },
fullscreen: { fullscreen: {
enabled: "fullscreen-enabled" enabled: "fullscreen-enabled",
active: "fullscreen-active"
} }
}, },
captions: { captions: {
defaultActive: false defaultActive: false
}, },
fullscreen: { fullscreen: {
enabled: true enabled: true,
fallback: true
},
storage: {
enabled: true,
supported: function() {
try {
return "localStorage" in window && window.localStorage !== null;
}
catch(e) {
return false;
}
}
} }
}; };
// Debugging
function _log(text, error) {
if(config.debug && window.console) {
console[(error ? "error" : "log")](text);
}
}
// Credits: http://paypal.github.io/accessible-html5-video-player/ // Credits: http://paypal.github.io/accessible-html5-video-player/
// Unfortunately, due to scattered support, browser sniffing is required // Unfortunately, due to scattered support, browser sniffing is required
function _browserSniff() { function _browserSniff() {
@ -170,7 +194,7 @@
} }
else { else {
var className = (" " + element.className + " ").replace(/\s+/g, " ").replace(" " + name + " ", ""); var className = (" " + element.className + " ").replace(/\s+/g, " ").replace(" " + name + " ", "");
element.className = className + (state ? " " + name : "") element.className = className + (state ? " " + name : "");
} }
} }
} }
@ -180,6 +204,16 @@
element.addEventListener(event, callback, false); element.addEventListener(event, callback, false);
} }
// Unbind event
function _off(element, event, callback) {
element.removeEventListener(event, callback, false);
}
// Get percentage
function _getPercentage(current, max) {
return Math.floor((current / max) * 100);
}
// Get click position relative to parent // 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) {
@ -247,7 +281,12 @@
if (typeof document[fullscreen.prefix + "CancelFullScreen"] != "undefined") { if (typeof document[fullscreen.prefix + "CancelFullScreen"] != "undefined") {
fullscreen.supportsFullScreen = true; fullscreen.supportsFullScreen = true;
break;
}
// Special case for MS (when isn't it?)
else if (typeof document.msExitFullscreen != "undefined" && document.msFullscreenEnabled) {
fullscreen.prefix = "ms";
fullscreen.supportsFullScreen = true;
break; break;
} }
} }
@ -261,7 +300,9 @@
// Update methods to do something useful // Update methods to do something useful
if (fullscreen.supportsFullScreen) { if (fullscreen.supportsFullScreen) {
fullscreen.fullScreenEventName = fullscreen.prefix + "fullscreenchange"; // Yet again Microsoft awesomeness,
// Sometimes the prefix is "ms", sometimes "MS" to keep you on your toes
fullscreen.fullScreenEventName = (fullscreen.prefix == "ms" ? "MSFullscreenChange" : fullscreen.prefix + "fullscreenchange");
fullscreen.isFullScreen = function() { fullscreen.isFullScreen = function() {
switch (this.prefix) { switch (this.prefix) {
@ -269,15 +310,20 @@
return document.fullScreen; return document.fullScreen;
case "webkit": case "webkit":
return document.webkitIsFullScreen; return document.webkitIsFullScreen;
case "ms":
// Docs say document.msFullScreenElement returns undefined
// if no element is full screem but it returns null, cheers
// https://msdn.microsoft.com/en-us/library/ie/dn265028%28v=vs.85%29.aspx
return (document.msFullscreenElement !== null);
default: default:
return document[this.prefix + "FullScreen"]; return document[this.prefix + "FullScreen"];
} }
}; };
fullscreen.requestFullScreen = function(element) { fullscreen.requestFullScreen = function(element) {
return (this.prefix === "") ? element.requestFullScreen() : element[this.prefix + "RequestFullScreen"](this.prefix === "webkit" ? element.ALLOW_KEYBOARD_INPUT : null); return (this.prefix === "") ? element.requestFullScreen() : element[this.prefix + (this.prefix == "ms" ? "RequestFullscreen" : "RequestFullScreen")](this.prefix === "webkit" ? element.ALLOW_KEYBOARD_INPUT : null);
}; };
fullscreen.cancelFullScreen = function() { fullscreen.cancelFullScreen = function() {
return (this.prefix === "") ? document.cancelFullScreen() : document[this.prefix + "CancelFullScreen"](); return (this.prefix === "") ? document.cancelFullScreen() : document[this.prefix + (this.prefix == "ms" ? "ExitFullscreen" : "CancelFullScreen")]();
}; };
fullscreen.element = function() { fullscreen.element = function() {
return (this.prefix === "") ? document.fullscreenElement : document[this.prefix + "FullscreenElement"]; return (this.prefix === "") ? document.fullscreenElement : document[this.prefix + "FullscreenElement"];
@ -351,12 +397,20 @@
return _getElements(selector)[0]; return _getElements(selector)[0];
} }
// Determine if we're in an iframe
function _inFrame() {
try {
return window.self !== window.top;
}
catch (e) {
return true;
}
}
// Insert controls // Insert controls
function _injectControls() { function _injectControls() {
// Insert custom video controls // Insert custom video controls
if (config.debug) { _log("Injecting custom controls.");
console.log("Injecting custom controls");
}
// Use specified html // Use specified html
// Need to do a default? // Need to do a default?
@ -374,6 +428,7 @@
// Find the UI controls and store references // Find the UI controls and store references
function _findElements() { function _findElements() {
try {
player.controls = _getElement(config.selectors.controls); player.controls = _getElement(config.selectors.controls);
// Buttons // Buttons
@ -389,8 +444,17 @@
// 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];
// Progress - Buffering
player.progress.buffer = {};
player.progress.buffer.bar = _getElement(config.selectors.progress.buffer);
player.progress.buffer.text = player.progress.buffer.bar.getElementsByTagName("span")[0];
// Progress - Played
player.progress.played = {};
player.progress.played.bar = _getElement(config.selectors.progress.played);
player.progress.played.text = player.progress.played.bar.getElementsByTagName("span")[0];
// Volume // Volume
player.volume = _getElement(config.selectors.buttons.volume); player.volume = _getElement(config.selectors.buttons.volume);
@ -398,6 +462,13 @@
// Timing // Timing
player.duration = _getElement(config.selectors.duration); player.duration = _getElement(config.selectors.duration);
player.seekTime = _getElements(config.selectors.seekTime); 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
@ -406,24 +477,27 @@
// If there's no media, bail // If there's no media, bail
if(!player.media) { if(!player.media) {
console.error("No audio or video element found!"); _log("No audio or video element found!", true);
return false; return false;
} }
// If there's no autoplay attribute, assume the video is stopped
_toggleClass(player.container, config.classes.stopped, (player.media.getAttribute("autoplay") === null));
// Remove native video controls // Remove native video controls
player.media.removeAttribute("controls"); player.media.removeAttribute("controls");
// Set type // Set media type
player.type = (player.media.tagName.toLowerCase() == "video" ? "video" : "audio"); player.type = (player.media.tagName.toLowerCase() == "video" ? "video" : "audio");
// Add type class
_toggleClass(player.container, config.classes[player.type], true);
// If there's no autoplay attribute, assume the video is stopped and add state class
_toggleClass(player.container, config.classes.stopped, (player.media.getAttribute("autoplay") === null));
// Inject the player wrapper // Inject the player wrapper
if(player.type === "video") { if(player.type === "video") {
// Create the wrapper div // Create the wrapper div
var wrapper = document.createElement("div"); var wrapper = document.createElement("div");
wrapper.setAttribute("class", config.classes.videoContainer); wrapper.setAttribute("class", config.classes.videoWrapper);
// Wrap the video in a container // Wrap the video in a container
_wrap(player.media, wrapper); _wrap(player.media, wrapper);
@ -466,14 +540,10 @@
player.captionExists = true; player.captionExists = true;
if (captionSrc === "") { if (captionSrc === "") {
player.captionExists = false; player.captionExists = false;
if (config.debug) { _log("No caption track found.");
console.log("No caption track found.");
}
} }
else { else {
if (config.debug) { _log("Caption track found; URI: " + captionSrc);
console.log("Caption track found; URI: " + captionSrc);
}
} }
// If no caption file exists, hide container for caption text // If no caption file exists, hide container for caption text
@ -482,40 +552,36 @@
} }
// 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) ||
(player.browserName === "IE" && player.browserMajorVersion === 11) || (player.browserName === "IE" && player.browserMajorVersion === 11) ||
(player.browserName === "Firefox" && player.browserMajorVersion >= 31) || (player.browserName === "Firefox" && player.browserMajorVersion >= 31) ||
(player.browserName === "Safari" && player.browserMajorVersion >= 7)) { (player.browserName === "Safari" && player.browserMajorVersion >= 7)) {
if (config.debug) { // Debugging
console.log("Detected IE 10/11 or Firefox 31+ or Safari 7+"); _log("Detected IE 10/11 or Firefox 31+ or Safari 7+.");
}
// 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) {
if (config.debug) { _log("TextTracks supported.");
console.log("textTracks supported");
} for (var y=0; y < tracks.length; y++) {
_showCaptions(player); 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]) {
@ -529,10 +595,7 @@
} }
// Caption tracks not natively supported // Caption tracks not natively supported
else { else {
if (config.debug) { _log("TextTracks not supported so rendering captions manually.");
console.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 = "";
@ -556,56 +619,51 @@
if (captionSrc !== "") { if (captionSrc !== "") {
// Create XMLHttpRequest Object // Create XMLHttpRequest Object
var xhr; var xhr = new XMLHttpRequest();
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
}
else if (window.ActiveXObject) { // IE8
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
xhr.onreadystatechange = function() { xhr.onreadystatechange = function() {
if (xhr.readyState === 4) { if (xhr.readyState === 4) {
if (xhr.status === 200) { if (xhr.status === 200) {
if (config.debug) {
console.log("xhr = 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] = [];
player.captions[r] = record.split("\n"); player.captions[r] = record.split("\n");
} }
// Remove first element ("VTT") // Remove first element ("VTT")
player.captions.shift(); player.captions.shift();
if (config.debug) { _log("Successfully loaded the caption file via AJAX.");
console.log("Successfully loaded the caption file via ajax.");
}
} }
else { else {
if (config.debug) { _log("There was a problem loading the caption file via AJAX.", true);
console.log("There was a problem loading the caption file via ajax.");
}
} }
} }
} }
xhr.open("get", captionSrc, true); xhr.open("get", captionSrc, true);
xhr.send(); xhr.send();
} }
} }
// 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) {
if (config.debug) { _log("Safari 7+ detected; removing track from DOM.");
console.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]);
}
} }
} }
} }
@ -621,15 +679,17 @@
// Setup fullscreen // Setup fullscreen
function _setupFullscreen() { function _setupFullscreen() {
if(player.type === "video" && config.fullscreen.enabled) { if(player.type === "video" && config.fullscreen.enabled) {
if(fullscreen.supportsFullScreen) { // Check for native support
if(config.debug) { var nativeSupport = fullscreen.supportsFullScreen;
console.log("Fullscreen enabled");
}
if(nativeSupport || (config.fullscreen.fallback && !_inFrame())) {
_log((nativeSupport ? "Native" : "Fallback") + " fullscreen enabled.");
// Add styling hook
_toggleClass(player.container, config.classes.fullscreen.enabled, true); _toggleClass(player.container, config.classes.fullscreen.enabled, true);
} }
else if(config.debug) { else {
console.warn("Fullscreen not supported"); _log("Fullscreen not supported and fallback disabled.");
} }
} }
} }
@ -708,20 +768,65 @@
// Toggle fullscreen // Toggle fullscreen
function _toggleFullscreen() { function _toggleFullscreen() {
// Check for native support
var nativeSupport = fullscreen.supportsFullScreen;
// If it's a fullscreen change event, it's probably a native close
if(event.type === fullscreen.fullScreenEventName) {
config.fullscreen.active = fullscreen.isFullScreen();
}
// If there's native support, use it
else if(nativeSupport) {
// Request fullscreen
if(!fullscreen.isFullScreen()) { if(!fullscreen.isFullScreen()) {
fullscreen.requestFullScreen(player.container); fullscreen.requestFullScreen(player.container);
} }
// Bail from fullscreen
else { else {
fullscreen.cancelFullScreen(); fullscreen.cancelFullScreen();
} }
// Check if we're actually full screen (it could fail)
config.fullscreen.active = fullscreen.isFullScreen();
}
else {
// Otherwise, it's a simple toggle
config.fullscreen.active = !config.fullscreen.active;
// Bind/unbind escape key
if(config.fullscreen.active) {
_on(document, "keyup", _handleEscapeFullscreen);
document.body.style.overflow = "hidden";
}
else {
_off(document, "keyup", _handleEscapeFullscreen);
document.body.style.overflow = "";
}
}
// Set class hook
_toggleClass(player.container, config.classes.fullscreen.active, config.fullscreen.active);
}
// Bail from faux-fullscreen
function _handleEscapeFullscreen(event) {
// If it's a keypress and not escape, bail
if((event.which || event.charCode || event.keyCode) === 27 && config.fullscreen.active) {
_toggleFullscreen();
}
} }
// Set volume // Set volume
function _setVolume(volume) { function _setVolume(volume) {
// Use default if needed // Use default if needed
if(typeof volume === "undefined") { if(typeof volume === "undefined") {
if(config.storage.enabled && config.storage.supported) {
volume = window.localStorage.plyr_volume || config.volume;
}
else {
volume = config.volume; volume = config.volume;
} }
}
// Maximum is 10 // Maximum is 10
if(volume > 10) { if(volume > 10) {
volume = 10; volume = 10;
@ -730,6 +835,11 @@
player.volume.value = volume; player.volume.value = volume;
player.media.volume = parseFloat(volume / 10); player.media.volume = parseFloat(volume / 10);
_checkMute(); _checkMute();
// Store the volume in storage
if(config.storage.enabled && config.storage.supported) {
window.localStorage.plyr_volume = volume;
}
} }
// Mute // Mute
@ -765,26 +875,56 @@
_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() {
// Fullscreen
_on(player.buttons.fullscreen, "click", _toggleFullscreen);
// Click video
if(player.type === "video" && config.click) {
_on(player.videoContainer, "click", function() {
if(player.media.paused) {
_play();
}
else if(player.media.ended) {
_restart();
}
else {
_pause();
}
});
}
// Play // Play
_on(player.buttons.play, "click", function() { _on(player.buttons.play, "click", function() {
_play(); _play();
@ -820,31 +960,35 @@
_toggleMute(this.checked); _toggleMute(this.checked);
}); });
// Duration // Fullscreen
_on(player.media, "timeupdate", function() { _on(player.buttons.fullscreen, "click", _toggleFullscreen);
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. // Handle user exiting fullscreen by escaping etc
player.secs = ("0" + player.secs).slice(-2); _on(document, fullscreen.fullScreenEventName, _toggleFullscreen);
player.mins = ("0" + player.mins).slice(-2);
// Render // Click video
player.duration.innerHTML = player.mins + ":" + player.secs; if(player.type === "video" && config.click) {
}); _on(player.videoContainer, "click", function() {
if(player.media.paused) {
// Progress bar _play();
_on(player.media, "timeupdate", function() { }
player.percent = (100 / player.media.duration) * player.media.currentTime; else if(player.media.ended) {
_restart();
if (player.percent > 0) { }
player.progress.bar.value = player.percent; else {
player.progress.text.innerHTML = player.percent; _pause();
} }
}); });
}
// Duration
_on(player.media, "timeupdate", _updateTimeDisplay);
// Playing progress
_on(player.media, "timeupdate", _updateProgress);
// 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;
@ -867,6 +1011,11 @@
_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);
}); });
// Check for buffer progress
_on(player.media, "progress", _updateProgress);
// Also check on start of playing
_on(player.media, "playing", _updateProgress);
} }
function _init() { function _init() {
@ -879,16 +1028,12 @@
player.browserMajorVersion = player.browserInfo[1]; player.browserMajorVersion = player.browserInfo[1];
// Debug info // Debug info
if(config.debug) { _log(player.browserName + " " + player.browserMajorVersion);
console.log(player.browserName + " " + player.browserMajorVersion);
}
// If IE8, stop customization (use fallback) // If IE8, stop customization (use fallback)
// If IE9, stop customization (use native controls) // If IE9, stop customization (use native controls)
if (player.browserName === "IE" && (player.browserMajorVersion === 8 || player.browserMajorVersion === 9) ) { if (player.browserName === "IE" && (player.browserMajorVersion === 8 || player.browserMajorVersion === 9) ) {
if(config.debug) { _log("Browser not suppported.", true);
console.error("Browser not suppported.");
}
return false; return false;
} }
@ -910,17 +1055,19 @@
_injectControls(); _injectControls();
// Find the elements // Find the elements
_findElements(); if(!_findElements()) {
return false;
// Set volume }
_setVolume(config.volume);
// Setup fullscreen
_setupFullscreen();
// Captions // Captions
_setupCaptions(); _setupCaptions();
// Set volume
_setVolume();
// Setup fullscreen
_setupFullscreen();
// Seeking // Seeking
_setupSeeking(); _setupSeeking();
@ -955,7 +1102,7 @@
} }
// Get the players // Get the players
var elements = document.querySelectorAll(config.selectors.container); var elements = document.querySelectorAll(config.selectors.container), players = [];
// Create a player instance for each element // Create a player instance for each element
for (var i = elements.length - 1; i >= 0; i--) { for (var i = elements.length - 1; i >= 0; i--) {
@ -963,7 +1110,14 @@
var element = elements[i]; var element = elements[i];
// Setup a player instance and add to the element // Setup a player instance and add to the element
if(typeof element.plyr === "undefined") {
element.plyr = new Plyr(element); element.plyr = new Plyr(element);
} }
// Add to return array
players.push(element.plyr);
}
return players;
} }
}(this.plyr = this.plyr || {})); }(this.plyr = this.plyr || {}));

View File

@ -3,52 +3,52 @@
// ========================================================================== // ==========================================================================
// Reset // Reset
@import "lib/normalize.less"; @import "docs/normalize.less";
// Mixins // Mixins
@import "lib/mixins.less"; @import "docs/mixins.less";
@font-face { // Variables
font-family: "Avenir"; // ---------------------------------------
src: url("../../assets/fonts/AvenirLTStd-Medium.woff2") format("woff2"), // Colors
url("../../assets/fonts/AvenirLTStd-Medium.woff") format("woff"), @blue: #3498DB;
url("../../assets/fonts/AvenirLTStd-Medium.ttf") format("truetype"); @gray-dark: #343f4a;
font-style: normal; @gray: #565d64;
font-weight: 400; @gray-light: #cbd0d3;
}
@font-face { // Elements
font-family: "Avenir"; @link-color: @blue;
src: url("../../assets/fonts/AvenirLTStd-Heavy.woff2") format("woff2"), @padding-base: 20px;
url("../../assets/fonts/AvenirLTStd-Heavy.woff") format("woff"),
url("../../assets/fonts/AvenirLTStd-Heavy.ttf") format("truetype"); // Breakpoints
font-style: normal; @screen-md: 768px;
font-weight: 600;
}
// BORDER-BOX ALL THE THINGS! // BORDER-BOX ALL THE THINGS!
// http://paulirish.com/2012/box-sizing-border-box-ftw/ // http://paulirish.com/2012/box-sizing-border-box-ftw/
*, *::after, *::before { *, *::after, *::before {
box-sizing: border-box; box-sizing: border-box;
} }
// Base
html { html {
font-size: 62.5%; //font-size: 62.5%;
} }
body { body {
font-family: "Avenir", "Helvetica Neue", Helvetica, Arial, sans-serif; font-family: "Avenir", "Helvetica Neue", Helvetica, Arial, sans-serif;
background: #fff; background: #fff;
.font-size(16); //.font-size(16);
line-height: 1.5; line-height: 1.5;
text-align: center; text-align: center;
color: #6D797F; color: #6D797F;
} }
// Type
h1, h1,
h2 { h2 {
letter-spacing: -.025em; letter-spacing: -.025em;
color: #2E3C44; color: #2E3C44;
margin: 0 0 10px; margin: 0 0 (@padding-base / 2);
line-height: 1.2; line-height: 1.2;
-webkit-font-smoothing: antialiased; .font-smoothing();
-moz-osx-font-smoothing: grayscale;
} }
h1 { h1 {
.font-size(64); .font-size(64);
@ -56,34 +56,67 @@ h1 {
} }
p, p,
small { small {
margin: 0 0 20px; margin: 0 0 @padding-base;
} }
small { small {
display: block; display: block;
padding: 0 (@padding-base / 2);
.font-size(14); .font-size(14);
} }
// Header
header { header {
padding: 60px 0; padding: @padding-base;
margin-bottom: 20px; margin-bottom: @padding-base;
p { p {
.font-size(18); .font-size(18);
} }
@media (min-width: 560px) {
padding-top: (@padding-base * 3);
padding-bottom: (@padding-base * 3);
} }
}
// Sections
section {
padding-bottom: @padding-base;
@media (min-width: 560px) {
padding-bottom: (@padding-base * 2);
}
}
// Links & Buttons
a { a {
text-decoration: none; text-decoration: none;
color: #3498DB; color: @link-color;
border-bottom: 1px solid currentColor; border-bottom: 1px solid currentColor;
transition: color .3s ease, border .3s ease; transition: all .3s ease;
&:hover, &:hover,
&:focus { &:focus {
color: #000; color: #000;
} }
&:focus {
.tab-focus();
} }
}
.btn {
display: inline-block;
padding: (@padding-base / 2) (@padding-base * 1.5);
background: @link-color;
border: 0;
color: #fff;
.font-smoothing(on);
font-weight: 600;
border-radius: 3px;
section { &:hover,
padding-bottom: 80px; &:focus {
color: #fff;
background: darken(@link-color, 5%);
}
} }
// Players // Players
@ -95,10 +128,14 @@ section {
} }
.example-audio .player, .example-audio .player,
.example-video .player { .example-video .player {
margin: 0 auto 20px; margin: 0 auto @padding-base;
&:fullscreen, &-fullscreen,
&-fullscreen { &.fullscreen-active {
max-width: none; max-width: none;
} }
} }
// Fonts
// Last to not block rendering
@import "docs/fontface.less";

View File

@ -0,0 +1,16 @@
@font-face {
font-family: "Avenir";
src: url("../../assets/fonts/AvenirLTStd-Medium.woff2") format("woff2"),
url("../../assets/fonts/AvenirLTStd-Medium.woff") format("woff"),
url("../../assets/fonts/AvenirLTStd-Medium.ttf") format("truetype");
font-style: normal;
font-weight: 400;
}
@font-face {
font-family: "Avenir";
src: url("../../assets/fonts/AvenirLTStd-Heavy.woff2") format("woff2"),
url("../../assets/fonts/AvenirLTStd-Heavy.woff") format("woff"),
url("../../assets/fonts/AvenirLTStd-Heavy.ttf") format("truetype");
font-style: normal;
font-weight: 600;
}

View File

@ -3,6 +3,7 @@
// ========================================================================== // ==========================================================================
// Contain floats: nicolasgallagher.com/micro-clearfix-hack/ // Contain floats: nicolasgallagher.com/micro-clearfix-hack/
// ---------------------------------------
.clearfix() { .clearfix() {
zoom: 1; zoom: 1;
&:before, &:before,
@ -11,6 +12,7 @@
} }
// Webkit-style focus // Webkit-style focus
// ---------------------------------------
.tab-focus() { .tab-focus() {
// Default // Default
outline: thin dotted @gray-dark; outline: thin dotted @gray-dark;
@ -20,8 +22,21 @@
} }
// Use rems for font sizing // Use rems for font sizing
// Leave <body> at 100%/16px
// ---------------------------------------
.font-size(@font-size: 16){ .font-size(@font-size: 16){
@rem: (@font-size / 10); @rem: round((@font-size / 16), 1);
font-size: @font-size * 1px; font-size: (@font-size * 1px);
font-size: ~"@{rem}rem"; font-size: ~"@{rem}rem";
} }
// Font smoothing
// ---------------------------------------
.font-smoothing(@mode: on) when (@mode = on) {
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
}
.font-smoothing(@mode: on) when (@mode = off) {
-moz-osx-font-smoothing: auto;
-webkit-font-smoothing: subpixel-antialiased;
}

View File

@ -18,16 +18,22 @@
@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;
@range-track-bg: @gray;
@range-thumb-height: (@range-track-height * 2); @range-thumb-height: (@range-track-height * 2);
@range-thumb-width: (@range-track-height * 2); @range-thumb-width: (@range-track-height * 2);
@range-thumb-bg: @control-color; @range-thumb-bg: @control-color;
@range-thumb-bg-focus: @control-color-active; @range-thumb-bg-focus: @control-color-active;
// Breakpoints
@bp-control-split: 560px; // When controls split into left/right
@bg-captions-large: 768px; // When captions jump to the larger font size
// Utility classes & mixins // Utility classes & mixins
// ------------------------------- // -------------------------------
// Screen reader only // Screen reader only
@ -54,7 +60,6 @@
outline-offset: 1px; outline-offset: 1px;
} }
// Range styling // Range styling
// --------------------------------------- // ---------------------------------------
.range-thumb() { .range-thumb() {
@ -68,7 +73,7 @@
} }
.range-track() { .range-track() {
height: @range-track-height; height: @range-track-height;
background: @gray; background: @range-track-bg;
border: 0; border: 0;
border-radius: (@range-track-height / 2); border-radius: (@range-track-height / 2);
} }
@ -92,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/
&, &,
*, *,
@ -104,10 +108,9 @@
} }
// For video // For video
&-video { &-video-wrapper {
position: relative; position: relative;
} }
video { video {
width: 100%; width: 100%;
height: auto; height: auto;
@ -125,11 +128,16 @@
min-height: 2.5em; min-height: 2.5em;
color: #fff; color: #fff;
font-size: 16px; font-size: 16px;
text-shadow: 0 1px 1px rgba(0,0,0, .75); font-weight: 600;
text-shadow:
-1px -1px 0 @gray,
1px -1px 0 @gray,
-1px 1px 0 @gray,
1px 1px 0 @gray;
text-align: center; text-align: center;
.font-smoothing(); .font-smoothing();
@media (min-width: 560px) { @media (min-width: @bg-captions-large) {
font-size: 24px; font-size: 24px;
} }
} }
@ -145,13 +153,14 @@
padding: (@control-spacing * 2) @control-spacing @control-spacing; padding: (@control-spacing * 2) @control-spacing @control-spacing;
background: @controls-bg; background: @controls-bg;
line-height: 1; line-height: 1;
text-align: center;
// Layout // Layout
&-sound { &-sound {
display: inline-block; display: block;
margin-top: @control-spacing; margin: @control-spacing auto 0;
} }
@media (min-width: 560px) { @media (min-width: @bp-control-split) {
&-playback { &-playback {
float: left; float: left;
} }
@ -194,21 +203,15 @@
background: transparent; background: transparent;
overflow: hidden; overflow: hidden;
} }
button:hover,
label:hover {
background: @control-color-active;
svg {
fill: #fff;
}
}
input:focus + label, input:focus + label,
button:focus { button:focus {
.tab-focus(); .tab-focus();
color: #fff;
svg {
fill: #fff;
} }
button:hover,
input + label:hover {
background: @control-color-active;
color: #fff;
} }
.icon-exit-fullscreen, .icon-exit-fullscreen,
.icon-muted { .icon-muted {
@ -234,29 +237,50 @@
right: 0; right: 0;
width: 100%; width: 100%;
height: @control-spacing; height: @control-spacing;
background: @progress-bg;
&-buffer,
&-played {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
margin: 0; margin: 0;
vertical-align: top; vertical-align: top;
&[value] { &[value] {
-webkit-appearance: none; -webkit-appearance: none;
border: none; border: none;
background: @progress-bg; background: transparent;
cursor: pointer;
color: @progress-value-bg;
&::-webkit-progress-bar { &::-webkit-progress-bar {
background: @progress-bg; background: transparent;
} }
// Inherit from currentColor; // Inherit from currentColor;
&::-webkit-progress-value { &::-webkit-progress-value {
background: currentColor; background: currentColor;
transition: width .1s ease;
} }
&::-moz-progress-bar { &::-moz-progress-bar {
background: currentColor; background: currentColor;
transition: width .1s ease;
} }
} }
} }
&-played {
z-index: 2;
}
&-played[value] {
cursor: pointer;
color: @progress-playing-bg;
}
&-buffer[value] {
color: @progress-buffered-bg;
}
}
// States // States
&-controls [data-player='pause'], &-controls [data-player='pause'],
@ -340,17 +364,17 @@
// Full screen mode // Full screen mode
&-fullscreen, &-fullscreen,
&:fullscreen { &.fullscreen-active {
position: absolute; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
height: 100%; height: 100%;
width: 100%; width: 100%;
z-index: 999999; z-index: 10000000;
.player-video { .player-video-wrapper {
height: 100%; height: 100%;
width: 100%; width: 100%;
@ -361,15 +385,13 @@
top: auto; top: auto;
bottom: 90px; bottom: 90px;
@media (min-width: 560px) and (max-width: 767px) { @media (min-width: @bp-control-split) and (max-width: (@bg-captions-large - 1)) {
bottom: 60px; bottom: 60px;
} }
@media (min-width: @bg-captions-large) {
@media (min-width: 768px) {
bottom: 80px; bottom: 80px;
} }
} }
} }
.player-controls { .player-controls {
position: absolute; position: absolute;
@ -407,18 +429,3 @@
display: none !important; display: none !important;
} }
} }
// Fixing display for IE10+
@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {
.video-controls .player-volume {
position: relative;
padding: 0;
}
.player-time {
margin-top: 4px;
}
.player-captions {
padding: 8px;
min-height: 36px;
}
}

View File

@ -1,25 +1,30 @@
<div class="player-controls"> <div class="player-controls">
<progress class="player-progress" max="100" value="0"> <div class="player-progress">
<progress class="player-progress-played" max="100" value="0">
<span>0</span>% played <span>0</span>% played
</progress> </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 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>
<span class="sr-only">Restart</span> <span class="sr-only">Restart</span>
</button> </button>
<button data-player="rewind"> <button type="button" data-player="rewind">
<svg><use xlink:href="#icon-rewind"></use></svg> <svg><use xlink:href="#icon-rewind"></use></svg>
<span class="sr-only">Rewind <span class="player-seek-time">10</span> seconds</span> <span class="sr-only">Rewind <span class="player-seek-time">10</span> seconds</span>
</button> </button>
<button aria-label="{aria-label}" data-player="play"> <button type="button" aria-label="{aria-label}" data-player="play">
<svg><use xlink:href="#icon-play"></use></svg> <svg><use xlink:href="#icon-play"></use></svg>
<span class="sr-only">Play</span> <span class="sr-only">Play</span>
</button> </button>
<button data-player="pause"> <button type="button" data-player="pause">
<svg><use xlink:href="#icon-pause"></use></svg> <svg><use xlink:href="#icon-pause"></use></svg>
<span class="sr-only">Pause</span> <span class="sr-only">Pause</span>
</button> </button>
<button data-player="fast-forward"> <button type="button" data-player="fast-forward">
<svg><use xlink:href="#icon-fast-forward"></use></svg> <svg><use xlink:href="#icon-fast-forward"></use></svg>
<span class="sr-only">Fast forward <span class="player-seek-time">10</span> seconds</span> <span class="sr-only">Fast forward <span class="player-seek-time">10</span> seconds</span>
</button> </button>
@ -45,7 +50,7 @@
<span class="sr-only">Captions</span> <span class="sr-only">Captions</span>
</label> </label>
<button data-player="fullscreen"> <button type="button" data-player="fullscreen">
<svg class="icon-exit-fullscreen"><use xlink:href="#icon-collapse"></use></svg> <svg class="icon-exit-fullscreen"><use xlink:href="#icon-collapse"></use></svg>
<svg><use xlink:href="#icon-expand"></use></svg> <svg><use xlink:href="#icon-expand"></use></svg>
<span class="sr-only">Toggle fullscreen</span> <span class="sr-only">Toggle fullscreen</span>

View File

@ -25,5 +25,5 @@
"type": "git", "type": "git",
"url": "git://github.com/selz/plyr.git" "url": "git://github.com/selz/plyr.git"
}, },
"license": "MIT" "license": "BSD"
} }

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/docs.css vendored
View File

@ -1 +1 @@
/*! normalize.css v2.1.3 | MIT License | git.io/normalize */article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}[hidden],template{display:none}html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}a{background:0 0}a:focus{outline:dotted thin}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}hr{box-sizing:content-box;height:0}mark{background:#ff0;color:#000}code,kbd,pre,samp{font-family:monospace,serif;font-size:1em}pre{white-space:pre-wrap}q{quotes:"\201C" "\201D" "\2018" "\2019"}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:0}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}button,input{line-height:normal}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=search]{-webkit-appearance:textfield;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top}table{border-collapse:collapse;border-spacing:0}@font-face{font-family:Avenir;src:url(../../assets/fonts/AvenirLTStd-Medium.woff2) format("woff2"),url(../../assets/fonts/AvenirLTStd-Medium.woff) format("woff"),url(../../assets/fonts/AvenirLTStd-Medium.ttf) format("truetype");font-style:normal;font-weight:400}@font-face{font-family:Avenir;src:url(../../assets/fonts/AvenirLTStd-Heavy.woff2) format("woff2"),url(../../assets/fonts/AvenirLTStd-Heavy.woff) format("woff"),url(../../assets/fonts/AvenirLTStd-Heavy.ttf) format("truetype");font-style:normal;font-weight:600}*,::after,::before{box-sizing:border-box}html{font-size:62.5%}body{font-family:Avenir,"Helvetica Neue",Helvetica,Arial,sans-serif;background:#fff;font-size:16px;font-size:1.6rem;line-height:1.5;text-align:center;color:#6D797F}h1,h2{letter-spacing:-.025em;color:#2E3C44;margin:0 0 10px;line-height:1.2;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}h1{font-size:64px;font-size:6.4rem;color:#3498DB}p,small{margin:0 0 20px}small{display:block;font-size:14px;font-size:1.4rem}header{padding:60px 0;margin-bottom:20px}header p{font-size:18px;font-size:1.8rem}a{text-decoration:none;color:#3498DB;border-bottom:1px solid currentColor;transition:color .3s ease,border .3s ease}a:focus,a:hover{color:#000}section{padding-bottom:80px}.example-audio .player{max-width:480px}.example-video .player{max-width:1200px}.example-audio .player,.example-video .player{margin:0 auto 20px}.example-audio .player-fullscreen,.example-audio .player:-webkit-full-screen,.example-video .player-fullscreen,.example-video .player:-webkit-full-screen{max-width:none}.example-audio .player-fullscreen,.example-audio .player:-moz-full-screen,.example-video .player-fullscreen,.example-video .player:-moz-full-screen{max-width:none}.example-audio .player-fullscreen,.example-audio .player:-ms-fullscreen,.example-video .player-fullscreen,.example-video .player:-ms-fullscreen{max-width:none}.example-audio .player-fullscreen,.example-audio .player:fullscreen,.example-video .player-fullscreen,.example-video .player:fullscreen{max-width:none} /*! normalize.css v2.1.3 | MIT License | git.io/normalize */article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}[hidden],template{display:none}html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}a{background:0 0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}hr{box-sizing:content-box;height:0}mark{background:#ff0;color:#000}code,kbd,pre,samp{font-family:monospace,serif;font-size:1em}pre{white-space:pre-wrap}q{quotes:"\201C" "\201D" "\2018" "\2019"}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:0}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}button,input{line-height:normal}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=search]{-webkit-appearance:textfield;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top}table{border-collapse:collapse;border-spacing:0}*,::after,::before{box-sizing:border-box}body{font-family:Avenir,"Helvetica Neue",Helvetica,Arial,sans-serif;background:#fff;line-height:1.5;text-align:center;color:#6D797F}h1,h2{letter-spacing:-.025em;color:#2E3C44;margin:0 0 10px;line-height:1.2;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}h1{font-size:64px;font-size:4rem;color:#3498DB}p,small{margin:0 0 20px}small{display:block;padding:0 10px;font-size:14px;font-size:.9rem}header{padding:20px;margin-bottom:20px}header p{font-size:18px;font-size:1.1rem}@media (min-width:560px){header{padding-top:60px;padding-bottom:60px}}section{padding-bottom:20px}@media (min-width:560px){section{padding-bottom:40px}}a{text-decoration:none;color:#3498db;border-bottom:1px solid currentColor;transition:all .3s ease}a:focus,a:hover{color:#000}a:focus{outline:#343f4a dotted thin;outline-offset:1px}.btn{display:inline-block;padding:10px 30px;background:#3498db;border:0;color:#fff;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-weight:600;border-radius:3px}.btn:focus,.btn:hover{color:#fff;background:#258cd1}.example-audio .player{max-width:480px}.example-video .player{max-width:1200px}.example-audio .player,.example-video .player{margin:0 auto 20px}.example-audio .player-fullscreen,.example-audio .player.fullscreen-active,.example-video .player-fullscreen,.example-video .player.fullscreen-active{max-width:none}@font-face{font-family:Avenir;src:url(../../assets/fonts/AvenirLTStd-Medium.woff2) format("woff2"),url(../../assets/fonts/AvenirLTStd-Medium.woff) format("woff"),url(../../assets/fonts/AvenirLTStd-Medium.ttf) format("truetype");font-style:normal;font-weight:400}@font-face{font-family:Avenir;src:url(../../assets/fonts/AvenirLTStd-Heavy.woff2) format("woff2"),url(../../assets/fonts/AvenirLTStd-Heavy.woff) format("woff"),url(../../assets/fonts/AvenirLTStd-Heavy.ttf) format("truetype");font-style:normal;font-weight:600}

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 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 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 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 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 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 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

@ -3,7 +3,7 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<title>Plyr - A simple HTML5 media player</title> <title>Plyr - A simple HTML5 media player</title>
<meta name="description" content="Custom HTML5 video controls and WebVTT captions."> <meta name="description" content="A simple HTML5 media player with custom controls and WebVTT captions.">
<meta name="author" content="Sam Potts"> <meta name="author" content="Sam Potts">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
@ -14,7 +14,8 @@
<body> <body>
<header> <header>
<h1>Plyr</h1> <h1>Plyr</h1>
<p>A simple HTML5 media player</p> <p>A simple HTML5 media player with custom controls and WebVTT captions.</p>
<a href="https://github.com/selz/plyr" target="_blank" class="btn">Download on Github</a>
</header> </header>
<section class="example-video"> <section class="example-video">
@ -25,16 +26,15 @@
<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_en.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>
<a href="//cdn.sampotts.me/plyr/movie.mp4"> <a href="//cdn.sampotts.me/plyr/movie.mp4">Download</a>
<img src="//cdn.sampotts.me/plyr/poster.jpg" alt="Download">
</a>
</div> </div>
</video> </video>
</div> </div>
<small>Big Buck Bunny. More info can be found at <a href="https://peach.blender.org" target="_blank">peach.blender.org</a>.</small>
</section> </section>
<section class="example-audio"> <section class="example-audio">
@ -42,6 +42,7 @@
<audio controls> <audio controls>
<!-- Audio files --> <!-- Audio files -->
<source src="//cdn.sampotts.me/plyr/logistics-96-sample.mp3" type="audio/mp3"> <source src="//cdn.sampotts.me/plyr/logistics-96-sample.mp3" type="audio/mp3">
<source src="//cdn.sampotts.me/plyr/logistics-96-sample.ogg" type="application/ogg">
<!-- Fallback for browsers that don't support the <audio> element --> <!-- Fallback for browsers that don't support the <audio> element -->
<div> <div>
@ -49,23 +50,12 @@
</div> </div>
</audio> </audio>
</div> </div>
<small>"96" by Logistics. More can be purchased from <a href="https://www.hospitalrecords.com/shop/artist/logistics" target="_blank">Hospital Records</a>.</small> <small>"96" by Logistics, which can be purchased from <a href="https://www.hospitalrecords.com/shop/artist/logistics" target="_blank">Hospital Records</a>.</small>
</section> </section>
<!-- Load SVG defs --> <!-- Load SVG defs -->
<script> <script>
(function(d,p){ (function(d,p){var a=new XMLHttpRequest(),b=d.body;a.open("GET",p,!0);a.send();a.onload=function(){var c=d.createElement("div");c.style.display="none";c.innerHTML=a.responseText;b.insertBefore(c,b.childNodes[0])}})(document,"dist/svg/sprite.svg");
var a=new XMLHttpRequest(),
b=d.body;
a.open("GET",p,!0);
a.send();
a.onload=function(){
var c=d.createElement("div");
c.style.display="none";
c.innerHTML=a.responseText;
b.insertBefore(c,b.childNodes[0])
}
})(document,"dist/svg/sprite.svg");
</script> </script>
<!-- Core player --> <!-- Core player -->
@ -73,5 +63,15 @@
<!-- Docs setup --> <!-- Docs setup -->
<script src="dist/js/docs.js"></script> <script src="dist/js/docs.js"></script>
<!-- GA -->
<script>
(function(i,s,o,g,r,a,m){i.GoogleAnalyticsObject=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-40881672-11', 'auto');
ga('send', 'pageview');
</script>
</body> </body>
</html> </html>

View File

@ -33,5 +33,5 @@
"authors": [ "authors": [
"Sam Potts <me@sampotts.me>" "Sam Potts <me@sampotts.me>"
], ],
"license": "MIT" "license": "BSD"
} }

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,8 +20,18 @@ 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
If bower is your thang, you can grab Plyr using:
```
bower install plyr
```
More info on setting up dependencies can be found in the [Bower Docs](http://bower.io/docs/creating-packages/#maintaining-dependencies)
### CSS ### CSS
If you want to use the default css, add the css file from /dist into your head, or even better use the less file included in /assets in your build to save a request. If you want to use the default css, add the css file from /dist into your head, or even better use the less file included in /assets in your build to save a request.