Compare commits

...

16 Commits

Author SHA1 Message Date
9feffb2972 Version bump 2015-03-15 10:18:56 +11:00
acea5cdb24 Built js 2015-03-15 10:14:00 +11:00
55ed577b6a Indentation
Converted to 4 space width tabbing
2015-03-15 10:12:36 +11:00
20b206a161 Merge pull request #62 from brunowego/patch-1
Updated SASS support
2015-03-15 10:00:00 +11:00
9c028a0ecc Updated SASS support 2015-03-14 15:50:32 -03:00
b677c3d7ad Merge branch 'master' of https://github.com/selz/plyr
Conflicts:
	dist/plyr.js
	src/js/plyr.js
2015-03-10 23:57:36 +11:00
aa6bc2df2f Fixes for volume control on iOS 2015-03-10 23:54:52 +11:00
a16579fd21 Fix for repo url 2015-03-09 11:53:26 +11:00
ae5a816df1 Fix for potential issue with .tagName 2015-03-09 11:51:01 +11:00
1532f2ab23 Added tooltip option (Fixes #50) 2015-03-09 00:47:38 +11:00
5370fc5c83 Loading state handling
Fixes #36
2015-03-08 01:24:23 +11:00
8482a1a320 Updated captions 2015-03-07 19:39:01 +11:00
f3603ac3fa Size updated 2015-03-07 19:27:11 +11:00
44fe647a49 Typo 2015-03-07 19:25:25 +11:00
928a89e599 More formatting 2015-03-07 19:24:41 +11:00
80d6d806c4 Formatting 2015-03-07 19:21:54 +11:00
20 changed files with 1664 additions and 1400 deletions

View File

@ -1,33 +1,33 @@
{ {
"name": "plyr", "name": "plyr",
"description": "A simple HTML5 media player using custom controls", "description": "A simple HTML5 media player using custom controls",
"homepage": "http://plyr.io", "homepage": "http://plyr.io",
"keywords": [ "keywords": [
"Audio", "Audio",
"Video", "Video",
"HTML5 Audio", "HTML5 Audio",
"HTml5 Video" "HTml5 Video"
], ],
"authors": [ "authors": [
"Sam Potts <me@sampotts.me>" "Sam Potts <me@sampotts.me>"
], ],
"dependencies": {}, "dependencies": {},
"main": [ "main": [
"dist/plyr.css", "dist/plyr.css",
"dist/plyr.js", "dist/plyr.js",
"dist/sprite.svg", "dist/sprite.svg",
"src/less/plyr.less", "src/less/plyr.less",
"src/sass/plyr.sass", "src/sass/plyr.sass",
"src/js/plyr.js" "src/js/plyr.js"
], ],
"ignore": [ "ignore": [
"node_modules", "node_modules",
"bower_components", "bower_components",
".gitignore" ".gitignore"
], ],
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git://github.com/selz/plyr.git" "url": "git://github.com/selz/plyr.git"
}, },
"license": "MIT" "license": "MIT"
} }

View File

@ -1,25 +1,25 @@
{ {
"plyr": { "plyr": {
"less": { "less": {
"plyr.css": ["src/less/plyr.less"] "plyr.css": ["src/less/plyr.less"]
}, },
"sass": { "sass": {
"plyr.css": ["src/less/plyr.sass"] "plyr.css": ["src/less/plyr.sass"]
}, },
"js": { "js": {
"plyr.js": ["src/js/plyr.js"] "plyr.js": ["src/js/plyr.js"]
} }
}, },
"docs": { "docs": {
"less": { "less": {
"docs.css": ["docs/src/less/docs.less"] "docs.css": ["docs/src/less/docs.less"]
}, },
"js": { "js": {
"docs.js": [ "docs.js": [
"docs/src/js/lib/hogan-3.0.2.mustache.js", "docs/src/js/lib/hogan-3.0.2.mustache.js",
"docs/dist/templates.js", "docs/dist/templates.js",
"docs/src/js/docs.js" "docs/src/js/docs.js"
] ]
} }
} }
} }

View File

@ -1,14 +1,28 @@
# Changelog # Changelog
## v1.0.26
- Fixes for SASS (cheers @brunowego)
- Indentation reset to 4 spaces
## v1.0.25
- Fixes for iOS volume controls (hidden)
- Classnames for left/right controls changed
## v1.0.24
- Added tooltip option to display labels as tooltips (Fixes #50)
## v1.0.23
- Handling loading states in the UI (Fixes #36)
## v1.0.22 ## v1.0.22
- Added support() API method for checking mimetype support - Added support() API method for checking mimetype support
- Added source() API method for setting media source(s) - Added source() API method for setting media source(s) (Fixes #44)
- Added poster() API method for setting poster source - Added poster() API method for setting poster source
- Refactored captions logic for manual captions - Refactored captions logic for manual captions
## v1.0.21 ## v1.0.21
- Added an <input type="range"> for seeking to improve experience (and support dragging) - Added an <input type="range"> for seeking to improve experience (and support dragging) (Fixes #40, #42)
- Icons for restart and captions improved (and some IDs changed) - Icons for restart and captions improved (and some IDs changed) (Fixes #49)
## v1.0.20 ## v1.0.20
- Default controls included (Fixes #45) - Default controls included (Fixes #45)
@ -17,7 +31,7 @@
- License changed to MIT - License changed to MIT
## v1.0.19 ## v1.0.19
- Fixed firefox fullscreen issue (#38) - Fixed firefox fullscreen issue (Fixes #38)
## v1.0.18 ## v1.0.18
- Added CDN references - Added CDN references

View File

@ -31,14 +31,14 @@ This is the default `html` option from `plyr.js`.
"<span>0</span>% buffered", "<span>0</span>% buffered",
"</progress>", "</progress>",
"</div>", "</div>",
"<span class='player-controls-playback'>", "<span class='player-controls-left'>",
"<button type='button' data-player='restart'>", "<button type='button' data-player='restart'>",
"<svg><use xlink:href='#icon-restart'></use></svg>", "<svg><use xlink:href='#icon-restart'></use></svg>",
"<span class='sr-only'>Restart</span>", "<span class='sr-only'>Restart</span>",
"</button>", "</button>",
"<button type='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 {seektime} seconds</span>", "<span class='sr-only'>Rewind {seektime} secs</span>",
"</button>", "</button>",
"<button type='button' data-player='play'>", "<button type='button' data-player='play'>",
"<svg><use xlink:href='#icon-play'></use></svg>", "<svg><use xlink:href='#icon-play'></use></svg>",
@ -50,14 +50,14 @@ This is the default `html` option from `plyr.js`.
"</button>", "</button>",
"<button type='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 {seektime} seconds</span>", "<span class='sr-only'>Forward {seektime} secs</span>",
"</button>", "</button>",
"<span class='player-time'>", "<span class='player-time'>",
"<span class='sr-only'>Time</span>", "<span class='sr-only'>Time</span>",
"<span class='player-duration'>00:00</span>", "<span class='player-duration'>00:00</span>",
"</span>", "</span>",
"</span>", "</span>",
"<span class='player-controls-sound'>", "<span class='player-controls-right'>",
"<input class='inverted sr-only' id='mute{id}' type='checkbox' data-player='mute'>", "<input class='inverted sr-only' id='mute{id}' type='checkbox' data-player='mute'>",
"<label id='mute{id}' for='mute{id}'>", "<label id='mute{id}' for='mute{id}'>",
"<svg class='icon-muted'><use xlink:href='#icon-muted'></use></svg>", "<svg class='icon-muted'><use xlink:href='#icon-muted'></use></svg>",
@ -75,7 +75,7 @@ This is the default `html` option from `plyr.js`.
"<button type='button' data-player='fullscreen'>", "<button type='button' data-player='fullscreen'>",
"<svg class='icon-exit-fullscreen'><use xlink:href='#icon-exit-fullscreen'></use></svg>", "<svg class='icon-exit-fullscreen'><use xlink:href='#icon-exit-fullscreen'></use></svg>",
"<svg><use xlink:href='#icon-enter-fullscreen'></use></svg>", "<svg><use xlink:href='#icon-enter-fullscreen'></use></svg>",
"<span class='sr-only'>Toggle fullscreen</span>", "<span class='sr-only'>Toggle Fullscreen</span>",
"</button>", "</button>",
"</span>", "</span>",
"</div>"].join("\n"); "</div>"].join("\n");

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.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(" <div class=\"player-progress\">");t.b("\n" + i);t.b(" <label for=\"seek{id}\" class=\"sr-only\">Seek</label>");t.b("\n" + i);t.b(" <input id=\"seek{id}\" class=\"player-progress-seek\" type=\"range\" min=\"0\" max=\"100\" step=\"0.5\" value=\"0\" data-player=\"seek\">");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-restart\"></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 {seektime} seconds</span>");t.b("\n" + i);t.b(" </button>");t.b("\n" + i);t.b(" <button type=\"button\" data-player=\"play\">");t.b("\n" + i);t.b(" <svg><use xlink:href=\"#icon-play\"></use></svg>");t.b("\n" + i);t.b(" <span class=\"sr-only\">Play</span>");t.b("\n" + i);t.b(" </button>");t.b("\n" + i);t.b(" <button type=\"button\" data-player=\"pause\">");t.b("\n" + i);t.b(" <svg><use xlink:href=\"#icon-pause\"></use></svg>");t.b("\n" + i);t.b(" <span class=\"sr-only\">Pause</span>");t.b("\n" + i);t.b(" </button>");t.b("\n" + i);t.b(" <button type=\"button\" data-player=\"fast-forward\">");t.b("\n" + i);t.b(" <svg><use xlink:href=\"#icon-fast-forward\"></use></svg>");t.b("\n" + i);t.b(" <span class=\"sr-only\">Fast forward {seektime} 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-volume\"></use></svg>");t.b("\n" + i);t.b(" <span class=\"sr-only\">Toggle 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\" step=\"0.5\" value=\"0\" 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 class=\"icon-captions-on\"><use xlink:href=\"#icon-captions-on\"></use></svg>");t.b("\n" + i);t.b(" <svg><use xlink:href=\"#icon-captions-off\"></use></svg>");t.b("\n" + i);t.b(" <span class=\"sr-only\">Toggle 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-exit-fullscreen\"></use></svg>");t.b("\n" + i);t.b(" <svg><use xlink:href=\"#icon-enter-fullscreen\"></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(" <label for=\"seek{id}\" class=\"sr-only\">Seek</label>");t.b("\n" + i);t.b(" <input id=\"seek{id}\" class=\"player-progress-seek\" type=\"range\" min=\"0\" max=\"100\" step=\"0.5\" value=\"0\" data-player=\"seek\">");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-left\">");t.b("\n" + i);t.b(" <button type=\"button\" data-player=\"restart\">");t.b("\n" + i);t.b(" <svg><use xlink:href=\"#icon-restart\"></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 {seektime} secs</span>");t.b("\n" + i);t.b(" </button>");t.b("\n" + i);t.b(" <button type=\"button\" data-player=\"play\">");t.b("\n" + i);t.b(" <svg><use xlink:href=\"#icon-play\"></use></svg>");t.b("\n" + i);t.b(" <span class=\"sr-only\">Play</span>");t.b("\n" + i);t.b(" </button>");t.b("\n" + i);t.b(" <button type=\"button\" data-player=\"pause\">");t.b("\n" + i);t.b(" <svg><use xlink:href=\"#icon-pause\"></use></svg>");t.b("\n" + i);t.b(" <span class=\"sr-only\">Pause</span>");t.b("\n" + i);t.b(" </button>");t.b("\n" + i);t.b(" <button type=\"button\" data-player=\"fast-forward\">");t.b("\n" + i);t.b(" <svg><use xlink:href=\"#icon-fast-forward\"></use></svg>");t.b("\n" + i);t.b(" <span class=\"sr-only\">Forward {seektime} secs</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-right\">");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-volume\"></use></svg>");t.b("\n" + i);t.b(" <span class=\"sr-only\">Toggle 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\" step=\"0.5\" value=\"0\" 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 class=\"icon-captions-on\"><use xlink:href=\"#icon-captions-on\"></use></svg>");t.b("\n" + i);t.b(" <svg><use xlink:href=\"#icon-captions-off\"></use></svg>");t.b("\n" + i);t.b(" <span class=\"sr-only\">Toggle 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-exit-fullscreen\"></use></svg>");t.b("\n" + i);t.b(" <svg><use xlink:href=\"#icon-enter-fullscreen\"></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

@ -1,18 +1,18 @@
<!doctype html> <!doctype html>
<html lang="en" class="error"> <html lang="en" class="error">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<title>Doh. Looks like something went wrong.</title> <title>Doh. Looks like something went wrong.</title>
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Docs styles --> <!-- Docs styles -->
<link rel="stylesheet" href="//cdn.plyr.io/1.0.22/docs.css"> <link rel="stylesheet" href="//cdn.plyr.io/1.0.26/docs.css">
</head> </head>
<body> <body>
<main> <main>
<h1>Doh.</h1> <h1>Doh.</h1>
<p>Looks like something went wrong.</p> <p>Looks like something went wrong.</p>
<a href="http://plyr.io" class="btn">Back to plyr.io</a> <a href="http://plyr.io" class="btn">Back to plyr.io</a>
</main> </main>
</body> </body>
</html> </html>

View File

@ -1,79 +1,79 @@
<!doctype html> <!doctype html>
<html lang="en"> <html lang="en">
<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="A simple HTML5 media player with custom 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">
<!-- Styles --> <!-- Styles -->
<link rel="stylesheet" href="//cdn.plyr.io/1.0.22/plyr.css"> <link rel="stylesheet" href="//cdn.plyr.io/1.0.26/plyr.css">
<!-- Docs styles --> <!-- Docs styles -->
<link rel="stylesheet" href="//cdn.plyr.io/1.0.22/docs.css"> <link rel="stylesheet" href="//cdn.plyr.io/1.0.26/docs.css">
</head> </head>
<body> <body>
<header> <header>
<h1>Plyr</h1> <h1>Plyr</h1>
<p>A simple HTML5 media player with custom controls and WebVTT captions.</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> <a href="https://github.com/selz/plyr" target="_blank" class="btn">Download on GitHub</a>
</header> </header>
<main> <main>
<section class="example-video"> <section class="example-video">
<div class="player"> <div class="player">
<video poster="//cdn.selz.com/plyr/1.0/poster.jpg" controls crossorigin> <video poster="//cdn.selz.com/plyr/1.0/poster.jpg" controls crossorigin>
<!-- Video files --> <!-- Video files -->
<source src="//cdn.selz.com/plyr/1.0/movie.mp4" type="video/mp4"> <source src="//cdn.selz.com/plyr/1.0/movie.mp4" type="video/mp4">
<source src="//cdn.selz.com/plyr/1.0/movie.webm" type="video/webm"> <source src="//cdn.selz.com/plyr/1.0/movie.webm" type="video/webm">
<!-- Text track file --> <!-- Text track file -->
<track kind="captions" label="English" srclang="en" src="//cdn.selz.com/plyr/1.0/movie_en_captions.vtt" default> <track kind="captions" label="English" srclang="en" src="//cdn.selz.com/plyr/1.0/en.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.selz.com/plyr/1.0/movie.mp4">Download</a> <a href="//cdn.selz.com/plyr/1.0/movie.mp4">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> <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">
<div class="player"> <div class="player">
<audio controls> <audio controls>
<!-- Audio files --> <!-- Audio files -->
<source src="//cdn.selz.com/plyr/1.0/logistics-96-sample.mp3" type="audio/mp3"> <source src="//cdn.selz.com/plyr/1.0/logistics-96-sample.mp3" type="audio/mp3">
<source src="//cdn.selz.com/plyr/1.0/logistics-96-sample.ogg" type="audio/ogg"> <source src="//cdn.selz.com/plyr/1.0/logistics-96-sample.ogg" type="audio/ogg">
<!-- Fallback for browsers that don't support the <audio> element --> <!-- Fallback for browsers that don't support the <audio> element -->
<div> <div>
<a href="//cdn.selz.com/plyr/1.0/logistics-96-sample.mp3">Download</a> <a href="//cdn.selz.com/plyr/1.0/logistics-96-sample.mp3">Download</a>
</div> </div>
</audio> </audio>
</div> </div>
<small>"96" by Logistics, which can be purchased from <a href="https://www.hospitalrecords.com/shop/artist/logistics" target="_blank">Hospital Records</a>.</small> <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>
</main> </main>
<footer> <footer>
<p>Used by &hellip;</p> <p>Used by &hellip;</p>
<a href="https://selz.com" target="_blank" class="logo"> <a href="https://selz.com" target="_blank" class="logo">
<img src="https://d33i624pw6jj68.cloudfront.net/static/img/logos/selz.svg" alt="Selz"> <img src="https://d33i624pw6jj68.cloudfront.net/static/img/logos/selz.svg" alt="Selz">
</a> </a>
</footer> </footer>
<!-- Load SVG defs --> <!-- Load SVG defs -->
<!-- You should bundle all SVG/Icons into one file using a build tool such as gulp and svg store --> <!-- You should bundle all SVG/Icons into one file using a build tool such as gulp and svg store -->
<script> <script>
(function(d,p){var a=new XMLHttpRequest(),b=d.body;a.open("GET",p,!0);a.send();a.onload=function(){var c=d.createElement("div");c.style.display="none";c.innerHTML=a.responseText;b.insertBefore(c,b.childNodes[0])}})(document,"//cdn.plyr.io/1.0.22/sprite.svg"); (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,"//cdn.plyr.io/1.0.26/sprite.svg");
</script> </script>
<!-- Plyr core script --> <!-- Plyr core script -->
<script src="//cdn.plyr.io/1.0.22/plyr.js"></script> <script src="//cdn.plyr.io/1.0.26/plyr.js"></script>
<!-- Docs script --> <!-- Docs script -->
<script src="//cdn.plyr.io/1.0.22/docs.js"></script> <script src="//cdn.plyr.io/1.0.26/docs.js"></script>
</body> </body>
</html> </html>

View File

@ -12,153 +12,153 @@
// Variables // Variables
// --------------------------------------- // ---------------------------------------
// Colors // Colors
@blue: #3498DB; @blue: #3498DB;
@gray-dark: #343f4a; @gray-dark: #343f4a;
@gray: #565d64; @gray: #565d64;
@gray-light: #cbd0d3; @gray-light: #cbd0d3;
// Elements // Elements
@link-color: @blue; @link-color: @blue;
@padding-base: 20px; @padding-base: 20px;
// Breakpoints // Breakpoints
@screen-md: 768px; @screen-md: 768px;
// 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 // Base
body { body {
font-family: "Avenir", "Helvetica Neue", Helvetica, Arial, sans-serif; font-family: "Avenir", "Helvetica Neue", Helvetica, Arial, sans-serif;
background: #fff; background: #fff;
line-height: 1.5; line-height: 1.5;
text-align: center; text-align: center;
color: #6D797F; color: #6D797F;
} }
// Error page // Error page
html.error, html.error,
.error body { .error body {
height: 100%; height: 100%;
} }
.error body { .error body {
width: 100%; width: 100%;
display: table; display: table;
table-layout: fixed; table-layout: fixed;
} }
.error main { .error main {
display: table-cell; display: table-cell;
width: 100%; width: 100%;
vertical-align: middle; vertical-align: middle;
} }
// Type // Type
h1, h1,
h2 { h2 {
letter-spacing: -.025em; letter-spacing: -.025em;
color: #2E3C44; color: #2E3C44;
margin: 0 0 (@padding-base / 2); margin: 0 0 (@padding-base / 2);
line-height: 1.2; line-height: 1.2;
.font-smoothing(); .font-smoothing();
} }
h1 { h1 {
.font-size(64); .font-size(64);
color: #3498DB; color: #3498DB;
} }
p, p,
small { small {
margin: 0 0 @padding-base; margin: 0 0 @padding-base;
} }
small { small {
display: block; display: block;
padding: 0 (@padding-base / 2); padding: 0 (@padding-base / 2);
.font-size(14); .font-size(14);
} }
// Header // Header
header { header {
padding: @padding-base; padding: @padding-base;
margin-bottom: @padding-base; margin-bottom: @padding-base;
p { p {
.font-size(18); .font-size(18);
} }
@media (min-width: 560px) { @media (min-width: 560px) {
padding-top: (@padding-base * 3); padding-top: (@padding-base * 3);
padding-bottom: (@padding-base * 3); padding-bottom: (@padding-base * 3);
} }
} }
// Sections // Sections
section { section {
padding-bottom: @padding-base; padding-bottom: @padding-base;
@media (min-width: 560px) { @media (min-width: 560px) {
padding-bottom: (@padding-base * 2); padding-bottom: (@padding-base * 2);
} }
} }
// Links & Buttons // Links & Buttons
a { a {
text-decoration: none; text-decoration: none;
color: @link-color; color: @link-color;
border-bottom: 1px solid currentColor; border-bottom: 1px solid currentColor;
transition: all .3s ease; transition: all .3s ease;
&:hover, &:hover,
&:focus { &:focus {
color: #000; color: #000;
} }
&:focus { &:focus {
.tab-focus(); .tab-focus();
} }
&.logo { &.logo {
border: 0; border: 0;
} }
} }
.btn { .btn {
display: inline-block; display: inline-block;
padding: (@padding-base / 2) (@padding-base * 1.5); padding: (@padding-base / 2) (@padding-base * 1.5);
background: @link-color; background: @link-color;
border: 0; border: 0;
color: #fff; color: #fff;
.font-smoothing(on); .font-smoothing(on);
font-weight: 600; font-weight: 600;
border-radius: 3px; border-radius: 3px;
user-select: none; user-select: none;
&:hover, &:hover,
&:focus { &:focus {
color: #fff; color: #fff;
background: darken(@link-color, 5%); background: darken(@link-color, 5%);
} }
} }
// Players // Players
.example-audio .player { .example-audio .player {
max-width: 480px; max-width: 480px;
} }
.example-video .player { .example-video .player {
max-width: 1200px; max-width: 1200px;
} }
.example-audio .player, .example-audio .player,
.example-video .player { .example-video .player {
margin: 0 auto @padding-base; margin: 0 auto @padding-base;
&-fullscreen, &-fullscreen,
&.fullscreen-active { &.fullscreen-active {
max-width: none; max-width: none;
} }
} }
// Footer // Footer
footer { footer {
margin-bottom: @padding-base; margin-bottom: @padding-base;
p { p {
margin-bottom: (@padding-base / 2); margin-bottom: (@padding-base / 2);
} }
} }

View File

@ -1,16 +1,16 @@
@font-face { @font-face {
font-family: "Avenir"; font-family: "Avenir";
src: url("//cdn.plyr.io/fonts/avenir-medium.woff2") format("woff2"), src: url("//cdn.plyr.io/fonts/avenir-medium.woff2") format("woff2"),
url("//cdn.plyr.io/fonts/avenir-medium.woff") format("woff"), url("//cdn.plyr.io/fonts/avenir-medium.woff") format("woff"),
url("//cdn.plyr.io/fonts/avenir-medium.ttf") format("truetype"); url("//cdn.plyr.io/fonts/avenir-medium.ttf") format("truetype");
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
} }
@font-face { @font-face {
font-family: "Avenir"; font-family: "Avenir";
src: url("//cdn.plyr.io/fonts/avenir-bold.woff2") format("woff2"), src: url("//cdn.plyr.io/fonts/avenir-bold.woff2") format("woff2"),
url("//cdn.plyr.io/fonts/avenir-bold.woff") format("woff"), url("//cdn.plyr.io/fonts/avenir-bold.woff") format("woff"),
url("//cdn.plyr.io/fonts/avenir-bold.ttf") format("truetype"); url("//cdn.plyr.io/fonts/avenir-bold.ttf") format("truetype");
font-style: normal; font-style: normal;
font-weight: 600; font-weight: 600;
} }

View File

@ -5,38 +5,38 @@
// Contain floats: nicolasgallagher.com/micro-clearfix-hack/ // Contain floats: nicolasgallagher.com/micro-clearfix-hack/
// --------------------------------------- // ---------------------------------------
.clearfix() { .clearfix() {
zoom: 1; zoom: 1;
&:before, &:before,
&:after { content: ""; display: table; } &:after { content: ""; display: table; }
&:after { clear: both; } &:after { clear: both; }
} }
// Webkit-style focus // Webkit-style focus
// --------------------------------------- // ---------------------------------------
.tab-focus() { .tab-focus() {
// Default // Default
outline: thin dotted @gray-dark; outline: thin dotted @gray-dark;
// Webkit // Webkit
//outline: 5px auto -webkit-focus-ring-color; //outline: 5px auto -webkit-focus-ring-color;
outline-offset: 1px; outline-offset: 1px;
} }
// Use rems for font sizing // Use rems for font sizing
// Leave <body> at 100%/16px // Leave <body> at 100%/16px
// --------------------------------------- // ---------------------------------------
.font-size(@font-size: 16){ .font-size(@font-size: 16){
@rem: round((@font-size / 16), 1); @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
// --------------------------------------- // ---------------------------------------
.font-smoothing(@mode: on) when (@mode = on) { .font-smoothing(@mode: on) when (@mode = on) {
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
} }
.font-smoothing(@mode: on) when (@mode = off) { .font-smoothing(@mode: on) when (@mode = off) {
-moz-osx-font-smoothing: auto; -moz-osx-font-smoothing: auto;
-webkit-font-smoothing: subpixel-antialiased; -webkit-font-smoothing: subpixel-antialiased;
} }

View File

@ -1,62 +1,62 @@
<div class="player-controls"> <div class="player-controls">
<div class="player-progress"> <div class="player-progress">
<label for="seek{id}" class="sr-only">Seek</label> <label for="seek{id}" class="sr-only">Seek</label>
<input id="seek{id}" class="player-progress-seek" type="range" min="0" max="100" step="0.5" value="0" data-player="seek"> <input id="seek{id}" class="player-progress-seek" type="range" min="0" max="100" step="0.5" value="0" data-player="seek">
<progress class="player-progress-played" max="100" value="0"> <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"> <progress class="player-progress-buffer" max="100" value="0">
<span>0</span>% buffered <span>0</span>% buffered
</progress> </progress>
</div> </div>
<span class="player-controls-playback"> <span class="player-controls-left">
<button type="button" data-player="restart"> <button type="button" data-player="restart">
<svg><use xlink:href="#icon-restart"></use></svg> <svg><use xlink:href="#icon-restart"></use></svg>
<span class="sr-only">Restart</span> <span class="sr-only">Restart</span>
</button> </button>
<button type="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 {seektime} seconds</span> <span class="sr-only">Rewind {seektime} secs</span>
</button> </button>
<button type="button" data-player="play"> <button type="button" 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 type="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 type="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 {seektime} seconds</span> <span class="sr-only">Forward {seektime} secs</span>
</button> </button>
<span class="player-time"> <span class="player-time">
<span class="sr-only">Time</span> <span class="sr-only">Time</span>
<span class="player-duration">00:00</span> <span class="player-duration">00:00</span>
</span> </span>
</span> </span>
<span class="player-controls-sound"> <span class="player-controls-right">
<input class="inverted sr-only" id="mute{id}" type="checkbox" data-player="mute"> <input class="inverted sr-only" id="mute{id}" type="checkbox" data-player="mute">
<label id="mute{id}" for="mute{id}"> <label id="mute{id}" for="mute{id}">
<svg class="icon-muted"><use xlink:href="#icon-muted"></use></svg> <svg class="icon-muted"><use xlink:href="#icon-muted"></use></svg>
<svg><use xlink:href="#icon-volume"></use></svg> <svg><use xlink:href="#icon-volume"></use></svg>
<span class="sr-only">Toggle Mute</span> <span class="sr-only">Toggle Mute</span>
</label> </label>
<label for="volume{id}" class="sr-only">Volume</label> <label for="volume{id}" class="sr-only">Volume</label>
<input id="volume{id}" class="player-volume" type="range" min="0" max="10" step="0.5" value="0" data-player="volume"> <input id="volume{id}" class="player-volume" type="range" min="0" max="10" step="0.5" value="0" data-player="volume">
<input class="sr-only" id="captions{id}" type="checkbox" data-player="captions"> <input class="sr-only" id="captions{id}" type="checkbox" data-player="captions">
<label for="captions{id}"> <label for="captions{id}">
<svg class="icon-captions-on"><use xlink:href="#icon-captions-on"></use></svg> <svg class="icon-captions-on"><use xlink:href="#icon-captions-on"></use></svg>
<svg><use xlink:href="#icon-captions-off"></use></svg> <svg><use xlink:href="#icon-captions-off"></use></svg>
<span class="sr-only">Toggle Captions</span> <span class="sr-only">Toggle Captions</span>
</label> </label>
<button type="button" data-player="fullscreen"> <button type="button" data-player="fullscreen">
<svg class="icon-exit-fullscreen"><use xlink:href="#icon-exit-fullscreen"></use></svg> <svg class="icon-exit-fullscreen"><use xlink:href="#icon-exit-fullscreen"></use></svg>
<svg><use xlink:href="#icon-enter-fullscreen"></use></svg> <svg><use xlink:href="#icon-enter-fullscreen"></use></svg>
<span class="sr-only">Toggle fullscreen</span> <span class="sr-only">Toggle Fullscreen</span>
</button> </button>
</span> </span>
</div> </div>

View File

@ -4,59 +4,59 @@
/*global require, __dirname*/ /*global require, __dirname*/
/*jshint -W079 */ /*jshint -W079 */
var fs = require("fs"), var fs = require("fs"),
path = require("path"), path = require("path"),
gulp = require("gulp"), gulp = require("gulp"),
gutil = require("gulp-util"), gutil = require("gulp-util"),
concat = require("gulp-concat"), concat = require("gulp-concat"),
uglify = require("gulp-uglify"), uglify = require("gulp-uglify"),
less = require("gulp-less"), less = require("gulp-less"),
sass = require("gulp-sass"), sass = require("gulp-sass"),
minify = require("gulp-minify-css"), minify = require("gulp-minify-css"),
run = require("run-sequence"), run = require("run-sequence"),
prefix = require("gulp-autoprefixer"), prefix = require("gulp-autoprefixer"),
svgstore = require("gulp-svgstore"), svgstore = require("gulp-svgstore"),
svgmin = require("gulp-svgmin"), svgmin = require("gulp-svgmin"),
hogan = require("gulp-hogan-compile"), hogan = require("gulp-hogan-compile"),
rename = require("gulp-rename"), rename = require("gulp-rename"),
s3 = require("gulp-s3"), s3 = require("gulp-s3"),
gzip = require("gulp-gzip"), gzip = require("gulp-gzip"),
replace = require("gulp-replace"), replace = require("gulp-replace"),
open = require("gulp-open"); open = require("gulp-open");
var root = __dirname, var root = __dirname,
paths = { paths = {
plyr: { plyr: {
// Source paths // Source paths
src: { src: {
less: path.join(root, "src/less/**/*"), less: path.join(root, "src/less/**/*"),
sass: path.join(root, "src/sass/**/*"), sass: path.join(root, "src/sass/**/*"),
js: path.join(root, "src/js/**/*"), js: path.join(root, "src/js/**/*"),
sprite: path.join(root, "src/sprite/*.svg") sprite: path.join(root, "src/sprite/*.svg")
}, },
// Output paths // Output paths
output: path.join(root, "dist/") output: path.join(root, "dist/")
}, },
docs: { docs: {
// Source paths // Source paths
src: { src: {
less: path.join(root, "docs/src/less/**/*"), less: path.join(root, "docs/src/less/**/*"),
js: path.join(root, "docs/src/js/**/*"), js: path.join(root, "docs/src/js/**/*"),
templates: path.join(root, "docs/src/templates/*.html") templates: path.join(root, "docs/src/templates/*.html")
}, },
// Output paths // Output paths
output: path.join(root, "docs/dist/"), output: path.join(root, "docs/dist/"),
// Docs // Docs
root: path.join(root, "docs/") root: path.join(root, "docs/")
}, },
upload: [path.join(root, "dist/**"), path.join(root, "docs/dist/**")] upload: [path.join(root, "dist/**"), path.join(root, "docs/dist/**")]
}, },
// Task arrays // Task arrays
tasks = { tasks = {
less: [], less: [],
sass: [], sass: [],
js: [] js: []
}, },
// Fetch bundles from JSON // Fetch bundles from JSON
@ -69,88 +69,88 @@ function loadJSON(path) {
} }
var build = { var build = {
js: function (files, bundle) { js: function (files, bundle) {
for (var key in files) { for (var key in files) {
(function(key) { (function(key) {
var name = "js-" + key; var name = "js-" + key;
tasks.js.push(name); tasks.js.push(name);
gulp.task(name, function () { gulp.task(name, function () {
return gulp return gulp
.src(bundles[bundle].js[key]) .src(bundles[bundle].js[key])
.pipe(concat(key)) .pipe(concat(key))
.pipe(uglify()) .pipe(uglify())
.pipe(gulp.dest(paths[bundle].output)); .pipe(gulp.dest(paths[bundle].output));
}); });
})(key); })(key);
} }
}, },
less: function(files, bundle) { less: function(files, bundle) {
for (var key in files) { for (var key in files) {
(function (key) { (function (key) {
var name = "less-" + key; var name = "less-" + key;
tasks.less.push(name); tasks.less.push(name);
gulp.task(name, function () { gulp.task(name, function () {
return gulp return gulp
.src(bundles[bundle].less[key]) .src(bundles[bundle].less[key])
.pipe(less()) .pipe(less())
.on("error", gutil.log) .on("error", gutil.log)
.pipe(concat(key)) .pipe(concat(key))
.pipe(prefix(["last 2 versions"], { cascade: true })) .pipe(prefix(["last 2 versions"], { cascade: true }))
.pipe(minify()) .pipe(minify())
.pipe(gulp.dest(paths[bundle].output)); .pipe(gulp.dest(paths[bundle].output));
}); });
})(key); })(key);
} }
}, },
sass: function(files, bundle) { sass: function(files, bundle) {
for (var key in files) { for (var key in files) {
(function (key) { (function (key) {
var name = "sass-" + key; var name = "sass-" + key;
tasks.sass.push(name); tasks.sass.push(name);
gulp.task(name, function () { gulp.task(name, function () {
return gulp return gulp
.src(bundles[bundle].sass[key]) .src(bundles[bundle].sass[key])
.pipe(sass()) .pipe(sass())
.on("error", gutil.log) .on("error", gutil.log)
.pipe(concat(key)) .pipe(concat(key))
.pipe(prefix(["last 2 versions"], { cascade: true })) .pipe(prefix(["last 2 versions"], { cascade: true }))
.pipe(minify()) .pipe(minify())
.pipe(gulp.dest(paths[bundle].output)); .pipe(gulp.dest(paths[bundle].output));
}); });
})(key); })(key);
} }
}, },
sprite: function() { sprite: function() {
// Process Icons // Process Icons
gulp.task("sprite", function () { gulp.task("sprite", function () {
return gulp return gulp
.src(paths.plyr.src.sprite) .src(paths.plyr.src.sprite)
.pipe(svgmin({ .pipe(svgmin({
plugins: [{ plugins: [{
removeDesc: true removeDesc: true
}] }]
})) }))
.pipe(svgstore()) .pipe(svgstore())
.pipe(gulp.dest(paths.plyr.output)); .pipe(gulp.dest(paths.plyr.output));
}); });
}, },
templates: function() { templates: function() {
// Build templates // Build templates
gulp.task("templates", function () { gulp.task("templates", function () {
return gulp return gulp
.src(paths.docs.src.templates) .src(paths.docs.src.templates)
.pipe(hogan("templates.js", { .pipe(hogan("templates.js", {
wrapper: false, wrapper: false,
templateName: function (file) { templateName: function (file) {
return path.basename(file.relative.replace(/\\/g, "-"), path.extname(file.relative)); return path.basename(file.relative.replace(/\\/g, "-"), path.extname(file.relative));
} }
})) }))
.pipe(gulp.dest(paths.docs.output)); .pipe(gulp.dest(paths.docs.output));
}); });
} }
}; };
// Plyr core files // Plyr core files
@ -166,22 +166,22 @@ build.js(bundles.docs.js, "docs");
// Default gulp task // Default gulp task
gulp.task("default", function(){ gulp.task("default", function(){
run("templates", tasks.js, tasks.less, "sprite"); run("templates", tasks.js, tasks.less, "sprite");
}); });
// Build all JS (inc. templates) // Build all JS (inc. templates)
gulp.task("js", function(){ gulp.task("js", function(){
run("templates", tasks.js); run("templates", tasks.js);
}); });
// Build SASS (for testing, default is LESS) // Build SASS (for testing, default is LESS)
gulp.task("sass", function(){ gulp.task("sass", function(){
run(tasks.sass); run(tasks.sass);
}); });
// Watch for file changes // Watch for file changes
gulp.task("watch", function () { gulp.task("watch", function () {
// Plyr core // Plyr core
gulp.watch(paths.plyr.src.js, tasks.js); gulp.watch(paths.plyr.src.js, tasks.js);
gulp.watch(paths.plyr.src.less, tasks.less); gulp.watch(paths.plyr.src.less, tasks.less);
gulp.watch(paths.plyr.src.sprite, "sprite"); gulp.watch(paths.plyr.src.sprite, "sprite");
@ -189,7 +189,7 @@ gulp.task("watch", function () {
// Docs // Docs
gulp.watch(paths.docs.src.js, tasks.js); gulp.watch(paths.docs.src.js, tasks.js);
gulp.watch(paths.docs.src.less, tasks.less); gulp.watch(paths.docs.src.less, tasks.less);
gulp.watch(paths.docs.src.templates, "js"); gulp.watch(paths.docs.src.templates, "js");
}); });
// Publish a version to CDN and docs // Publish a version to CDN and docs
@ -198,69 +198,69 @@ gulp.task("watch", function () {
// Some options // Some options
var aws = loadJSON(path.join(root, "aws.json")), var aws = loadJSON(path.join(root, "aws.json")),
version = package.version, version = package.version,
maxAge = 31536000, // seconds 1 year maxAge = 31536000, // seconds 1 year
options = { options = {
cdn: { cdn: {
headers: { headers: {
"Cache-Control": "max-age=" + maxAge + ", no-transform, public", "Cache-Control": "max-age=" + maxAge + ", no-transform, public",
"Vary": "Accept-Encoding" "Vary": "Accept-Encoding"
}, },
gzippedOnly: true gzippedOnly: true
}, },
docs: { docs: {
headers: { headers: {
"Cache-Control": "public, must-revalidate, proxy-revalidate, max-age=0", "Cache-Control": "public, must-revalidate, proxy-revalidate, max-age=0",
"Vary": "Accept-Encoding" "Vary": "Accept-Encoding"
}, },
gzippedOnly: true gzippedOnly: true
} }
}, },
cdnpath = new RegExp(aws.cdn.bucket + "\/(\\d+\\.)?(\\d+\\.)?(\\*|\\d+)","gi"); cdnpath = new RegExp(aws.cdn.bucket + "\/(\\d+\\.)?(\\d+\\.)?(\\*|\\d+)","gi");
// Publish version to CDN bucket // Publish version to CDN bucket
gulp.task("cdn", function () { gulp.task("cdn", function () {
console.log("Uploading " + version + " to " + aws.cdn.bucket); console.log("Uploading " + version + " to " + aws.cdn.bucket);
// Upload to CDN // Upload to CDN
gulp.src(paths.upload) gulp.src(paths.upload)
.pipe(rename(function (path) { .pipe(rename(function (path) {
path.dirname = path.dirname.replace(".", version); path.dirname = path.dirname.replace(".", version);
})) }))
.pipe(gzip()) .pipe(gzip())
.pipe(s3(aws.cdn, options.cdn)); .pipe(s3(aws.cdn, options.cdn));
}); });
// Publish to Docs bucket // Publish to Docs bucket
gulp.task("docs", function () { gulp.task("docs", function () {
console.log("Uploading " + version + " docs to " + aws.docs.bucket); console.log("Uploading " + version + " docs to " + aws.docs.bucket);
// Replace versioned files in *.html // Replace versioned files in *.html
gulp.src([paths.docs.root + "*.html"]) gulp.src([paths.docs.root + "*.html"])
.pipe(replace(cdnpath, aws.cdn.bucket + "/" + version)) .pipe(replace(cdnpath, aws.cdn.bucket + "/" + version))
.pipe(gulp.dest(paths.docs.root)) .pipe(gulp.dest(paths.docs.root))
.pipe(gzip()) .pipe(gzip())
.pipe(s3(aws.docs, options.docs)); .pipe(s3(aws.docs, options.docs));
// Upload error.html to cdn using docs options // Upload error.html to cdn using docs options
gulp.src([paths.docs.root + "error.html"]) gulp.src([paths.docs.root + "error.html"])
.pipe(gzip()) .pipe(gzip())
.pipe(s3(aws.cdn, options.docs)); .pipe(s3(aws.cdn, options.docs));
}); });
// Open the docs site to check it's sweet // Open the docs site to check it's sweet
gulp.task("open", function () { gulp.task("open", function () {
console.log("Opening " + aws.docs.bucket + "..."); console.log("Opening " + aws.docs.bucket + "...");
// A file must be specified or gulp will skip the task // A file must be specified or gulp will skip the task
// Doesn't matter which file since we set the URL above // Doesn't matter which file since we set the URL above
// Weird, I know... // Weird, I know...
gulp.src([paths.docs.root + "index.html"]) gulp.src([paths.docs.root + "index.html"])
.pipe(open("", { .pipe(open("", {
url: "http://" + aws.docs.bucket url: "http://" + aws.docs.bucket
})); }));
}); });
// Do everything // Do everything
gulp.task("publish", function () { gulp.task("publish", function () {
run("templates", tasks.js, tasks.less, "sprite", "cdn", "docs", "open"); run("templates", tasks.js, tasks.less, "sprite", "cdn", "docs", "open");
}); });

View File

@ -1,43 +1,43 @@
{ {
"name": "plyr", "name": "plyr",
"version": "1.0.22", "version": "1.0.26",
"description": "A simple HTML5 media player using custom controls", "description": "A simple HTML5 media player using custom controls",
"homepage": "http://plyr.io", "homepage": "http://plyr.io",
"main": "gulpfile.js", "main": "gulpfile.js",
"dependencies": {}, "dependencies": {},
"devDependencies": { "devDependencies": {
"gulp": "~3.8.6", "gulp": "~3.8.6",
"gulp-autoprefixer": "^0.0.8", "gulp-autoprefixer": "^0.0.8",
"gulp-concat": "~2.3.3", "gulp-concat": "~2.3.3",
"gulp-gzip": "^1.0.0", "gulp-gzip": "^1.0.0",
"gulp-hogan-compile": "^0.4.1", "gulp-hogan-compile": "^0.4.1",
"gulp-less": "~1.3.1", "gulp-less": "~1.3.1",
"gulp-minify-css": "~0.3.6", "gulp-minify-css": "~0.3.6",
"gulp-open": "^0.3.2", "gulp-open": "^0.3.2",
"gulp-rename": "^1.2.0", "gulp-rename": "^1.2.0",
"gulp-replace": "^0.5.3", "gulp-replace": "^0.5.3",
"gulp-s3": "^0.3.0", "gulp-s3": "^0.3.0",
"gulp-sass": "^1.3.3", "gulp-sass": "^1.3.3",
"gulp-svgmin": "^1.0.0", "gulp-svgmin": "^1.0.0",
"gulp-svgstore": "^5.0.0", "gulp-svgstore": "^5.0.0",
"gulp-uglify": "~0.3.1", "gulp-uglify": "~0.3.1",
"gulp-util": "~2.2.20", "gulp-util": "~2.2.20",
"run-sequence": "^0.3.6" "run-sequence": "^0.3.6"
}, },
"scripts": { "scripts": {
"preinstall": "npm install -g gulp" "preinstall": "npm install -g gulp"
}, },
"keywords": [ "keywords": [
"HTML5 Video", "HTML5 Video",
"HTML5 Audio", "HTML5 Audio",
"Media Player" "Media Player"
], ],
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git://github.com/selz/plyr.git" "url": "git://github.com/selz/plyr.git"
}, },
"authors": [ "authors": [
"Sam Potts <me@sampotts.me>" "Sam Potts <me@sampotts.me>"
], ],
"license": "MIT" "license": "MIT"
} }

View File

@ -10,7 +10,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 5.3KB minified and gzipped. - **Lightweight** - just 5.7KB 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 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.
@ -38,7 +38,7 @@ If you have any cool ideas or features, please let me know by [creating an issue
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.0.21/plyr.js` to `https://cdn.plyr.io/1.0.21/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.0.26/plyr.js` to `https://cdn.plyr.io/1.0.26/plyr.js`
### Bower ### Bower
If bower is your thang, you can grab Plyr using: If bower is your thang, you can grab Plyr using:
@ -51,11 +51,11 @@ More info on setting up dependencies can be found in the [Bower Docs](http://bow
If you want to use our CDN, you can use the following. HTTPS (SSL) is supported. If you want to use our CDN, you can use the following. HTTPS (SSL) is supported.
```html ```html
<link rel="stylesheet" href="//cdn.plyr.io/1.0.21/plyr.css"> <link rel="stylesheet" href="//cdn.plyr.io/1.0.26/plyr.css">
<script src="//cdn.plyr.io/1.0.21/plyr.js"></script> <script src="//cdn.plyr.io/1.0.26/plyr.js"></script>
``` ```
You can also access the `sprite.svg` file at `//cdn.plyr.io/1.0.21/sprite.svg`. You can also access the `sprite.svg` file at `//cdn.plyr.io/1.0.26/sprite.svg`.
### CSS ### CSS
If you want to use the default css, add the `plyr.css` file from /dist into your head, or even better use `plyr.less` or `plyr.sass` file included in `/src` in your build to save a request. 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.
@ -190,13 +190,19 @@ You can pass the following options to the setup method.
<td><code>click</code></td> <td><code>click</code></td>
<td>Boolean</td> <td>Boolean</td>
<td><code>true</code></td> <td><code>true</code></td>
<td>Click (or tap) will toggle pause/play of a `<video>`.</td> <td>Click (or tap) will toggle pause/play of a <code>&lt;video&gt;</code>.</td>
</tr>
<tr>
<td><code>tooltips</code></td>
<td>Boolean</td>
<td><code>false</code></td>
<td>Display control labels as tooltips on :hover &amp; :focus (by default, the labels are screen reader only).</td>
</tr> </tr>
<tr> <tr>
<td><code>selectors</code></td> <td><code>selectors</code></td>
<td>Object</td> <td>Object</td>
<td>&mdash;</td> <td>&mdash;</td>
<td>See `plyr.js` in `/src` for more info. The only option you might want to change is `player` which is the hook used for Plyr, the default is `.player`.</td> <td>See <code>plyr.js</code> in <code>/src</code> for more info. The only option you might want to change is <code>player</code> which is the hook used for Plyr, the default is <code>.player</code>.</td>
</tr> </tr>
<tr> <tr>
<td><code>classes</code></td> <td><code>classes</code></td>
@ -208,19 +214,19 @@ You can pass the following options to the setup method.
<td><code>captions</code></td> <td><code>captions</code></td>
<td>Object</td> <td>Object</td>
<td>&mdash;</td> <td>&mdash;</td>
<td>This currently contains one property `defaultActive` which toggles if captions should be on by default. The default value is `false`.</td> <td>One property <code>defaultActive</code> which toggles if captions should be on by default. The default value is <code>false</code>.</td>
</tr> </tr>
<tr> <tr>
<td><code>fullscreen</code></td> <td><code>fullscreen</code></td>
<td>Object</td> <td>Object</td>
<td>&mdash;</td> <td>&mdash;</td>
<td>This currently contains two properties; `enabled` which toggles if fullscreen should be enabled (if the browser supports it). The default value is `true`. Also an extra property called `fallback` which will enable a 'full window' view for older browsers. The default value is `true`.</td> <td>Two properties; <code>enabled</code> which toggles if fullscreen should be enabled (if the browser supports it). The default value is <code>true</code>. Also an extra property called <code>fallback</code> which will enable a full window view for older browsers. The default value is <code>true</code>.</td>
</tr> </tr>
<tr> <tr>
<td><code>storage</code></td> <td><code>storage</code></td>
<td>Object</td> <td>Object</td>
<td>&mdash;</td> <td>&mdash;</td>
<td>This currently contains one property `enabled` which toggles if local storage should be enabled (if the browser supports it). The default value is `true`. This enables storing user settings, currently it only stores volume but more will be added later.</td> <td>Two properties; <code>enabled</code> which toggles if local storage should be enabled (if the browser supports it). The default value is `true`. This enables storing user settings, currently it only stores volume but more will be added later. The second property <code>key</code> is the key used for the local storage. The default is <code>plyr_volume</code> until more settings are stored.</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@ -301,18 +307,18 @@ Here's a list of the methods supported:
Set the media source. Set the media source.
<br><br> <br><br>
<strong>string</strong><br> <strong>string</strong><br>
<em>.source("/path/to/video.mp4")</em><br> <code>.source("/path/to/video.mp4")</code><br>
This will set the "src" attribute on the `video` or `audio` 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>
<em>.source([{ src: "/path/to/video.webm", type: "video/webm", ...more attributes... }, { src: "/path/to/video.mp4", type: "video/mp4", ...more attributes... }])</em><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.
</td> </td>
</tr> </tr>
<tr> <tr>
<td><code>poster(...)</code></td> <td><code>poster(...)</code></td>
<td>String</td> <td>String</td>
<td>Set the poster url. This is supported for the `video` element only.</td> <td>Set the poster url. This is supported for the <code>video</code> element only.</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@ -349,19 +355,21 @@ Fullscreen in Plyr is supported for all browsers that [currently support it](htt
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td>✔&sup1;</td>
<td>✔</td> <td>✔</td>
<td>✔</td> <td>✔</td>
<td>✔</td> <td>✔</td>
<td></td> <td>✖&sup2;</td>
<td>&sup1;</td> <td>✔&sup3;</td>
<td>&sup2;</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
&sup1; Native player used (no support for `<progress>` or `<input type="range">`) &sup1; iPhone forces the native player for `<video>` so no customisation possible. `<audio>` elements have volume controls disabled.
&sup2; IE10 has no native fullscreen support, fallback can be used (see options) &sup2; Native player used (no support for `<progress>` or `<input type="range">`)
&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:

View File

@ -1,7 +1,7 @@
// ========================================================================== // ==========================================================================
// Plyr // Plyr
// plyr.js v1.0.22 // plyr.js v1.0.25
// https://github.com/sampotts/plyr // https://github.com/selz/plyr
// License: The MIT License (MIT) // License: The MIT License (MIT)
// ========================================================================== // ==========================================================================
// Credits: http://paypal.github.io/accessible-html5-video-player/ // Credits: http://paypal.github.io/accessible-html5-video-player/
@ -20,9 +20,11 @@
seekTime: 10, seekTime: 10,
volume: 5, volume: 5,
click: true, click: true,
tooltips: false,
selectors: { selectors: {
container: ".player", container: ".player",
controls: ".player-controls", controls: ".player-controls",
labels: "[data-player] .sr-only, label .sr-only",
buttons: { buttons: {
seek: "[data-player='seek']", seek: "[data-player='seek']",
play: "[data-player='play']", play: "[data-player='play']",
@ -50,6 +52,9 @@
stopped: "stopped", stopped: "stopped",
playing: "playing", playing: "playing",
muted: "muted", muted: "muted",
loading: "loading",
tooltip: "player-tooltip",
hidden: "sr-only",
captions: { captions: {
enabled: "captions-enabled", enabled: "captions-enabled",
active: "captions-active" active: "captions-active"
@ -67,7 +72,8 @@
fallback: true fallback: true
}, },
storage: { storage: {
enabled: true enabled: true,
key: "plyr_volume"
}, },
html: (function() { html: (function() {
return [ return [
@ -82,14 +88,14 @@
"<span>0</span>% buffered", "<span>0</span>% buffered",
"</progress>", "</progress>",
"</div>", "</div>",
"<span class='player-controls-playback'>", "<span class='player-controls-left'>",
"<button type='button' data-player='restart'>", "<button type='button' data-player='restart'>",
"<svg><use xlink:href='#icon-restart'></use></svg>", "<svg><use xlink:href='#icon-restart'></use></svg>",
"<span class='sr-only'>Restart</span>", "<span class='sr-only'>Restart</span>",
"</button>", "</button>",
"<button type='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 {seektime} seconds</span>", "<span class='sr-only'>Rewind {seektime} secs</span>",
"</button>", "</button>",
"<button type='button' data-player='play'>", "<button type='button' data-player='play'>",
"<svg><use xlink:href='#icon-play'></use></svg>", "<svg><use xlink:href='#icon-play'></use></svg>",
@ -101,14 +107,14 @@
"</button>", "</button>",
"<button type='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 {seektime} seconds</span>", "<span class='sr-only'>Forward {seektime} secs</span>",
"</button>", "</button>",
"<span class='player-time'>", "<span class='player-time'>",
"<span class='sr-only'>Time</span>", "<span class='sr-only'>Time</span>",
"<span class='player-duration'>00:00</span>", "<span class='player-duration'>00:00</span>",
"</span>", "</span>",
"</span>", "</span>",
"<span class='player-controls-sound'>", "<span class='player-controls-right'>",
"<input class='inverted sr-only' id='mute{id}' type='checkbox' data-player='mute'>", "<input class='inverted sr-only' id='mute{id}' type='checkbox' data-player='mute'>",
"<label id='mute{id}' for='mute{id}'>", "<label id='mute{id}' for='mute{id}'>",
"<svg class='icon-muted'><use xlink:href='#icon-muted'></use></svg>", "<svg class='icon-muted'><use xlink:href='#icon-muted'></use></svg>",
@ -126,12 +132,12 @@
"<button type='button' data-player='fullscreen'>", "<button type='button' data-player='fullscreen'>",
"<svg class='icon-exit-fullscreen'><use xlink:href='#icon-exit-fullscreen'></use></svg>", "<svg class='icon-exit-fullscreen'><use xlink:href='#icon-exit-fullscreen'></use></svg>",
"<svg><use xlink:href='#icon-enter-fullscreen'></use></svg>", "<svg><use xlink:href='#icon-enter-fullscreen'></use></svg>",
"<span class='sr-only'>Toggle fullscreen</span>", "<span class='sr-only'>Toggle Fullscreen</span>",
"</button>", "</button>",
"</span>", "</span>",
"</div>" "</div>"
].join("\n"); ].join("\n");
})() })()
}; };
// Debugging // Debugging
@ -145,7 +151,7 @@
// Unfortunately, due to mixed support, UA sniffing is required // Unfortunately, due to mixed support, UA sniffing is required
function _browserSniff() { function _browserSniff() {
var nAgt = navigator.userAgent, var nAgt = navigator.userAgent,
browserName = navigator.appName, name = navigator.appName,
fullVersion = ""+parseFloat(navigator.appVersion), fullVersion = ""+parseFloat(navigator.appVersion),
majorVersion = parseInt(navigator.appVersion,10), majorVersion = parseInt(navigator.appVersion,10),
nameOffset, nameOffset,
@ -154,22 +160,22 @@
// MSIE 11 // MSIE 11
if ((navigator.appVersion.indexOf("Windows NT") !== -1) && (navigator.appVersion.indexOf("rv:11") !== -1)) { if ((navigator.appVersion.indexOf("Windows NT") !== -1) && (navigator.appVersion.indexOf("rv:11") !== -1)) {
browserName = "IE"; name = "IE";
fullVersion = "11;"; fullVersion = "11;";
} }
// MSIE // MSIE
else if ((verOffset=nAgt.indexOf("MSIE")) !== -1) { else if ((verOffset=nAgt.indexOf("MSIE")) !== -1) {
browserName = "IE"; name = "IE";
fullVersion = nAgt.substring(verOffset+5); fullVersion = nAgt.substring(verOffset+5);
} }
// Chrome // Chrome
else if ((verOffset=nAgt.indexOf("Chrome")) !== -1) { else if ((verOffset=nAgt.indexOf("Chrome")) !== -1) {
browserName = "Chrome"; name = "Chrome";
fullVersion = nAgt.substring(verOffset+7); fullVersion = nAgt.substring(verOffset+7);
} }
// Safari // Safari
else if ((verOffset=nAgt.indexOf("Safari")) !== -1) { else if ((verOffset=nAgt.indexOf("Safari")) !== -1) {
browserName = "Safari"; name = "Safari";
fullVersion = nAgt.substring(verOffset+7); fullVersion = nAgt.substring(verOffset+7);
if ((verOffset=nAgt.indexOf("Version")) !== -1) { if ((verOffset=nAgt.indexOf("Version")) !== -1) {
fullVersion = nAgt.substring(verOffset+8); fullVersion = nAgt.substring(verOffset+8);
@ -177,15 +183,15 @@
} }
// Firefox // Firefox
else if ((verOffset=nAgt.indexOf("Firefox")) !== -1) { else if ((verOffset=nAgt.indexOf("Firefox")) !== -1) {
browserName = "Firefox"; name = "Firefox";
fullVersion = nAgt.substring(verOffset+8); fullVersion = nAgt.substring(verOffset+8);
} }
// In most other browsers, "name/version" is at the end of userAgent // In most other browsers, "name/version" is at the end of userAgent
else if ( (nameOffset=nAgt.lastIndexOf(" ")+1) < (verOffset=nAgt.lastIndexOf("/")) ) { else if ( (nameOffset=nAgt.lastIndexOf(" ")+1) < (verOffset=nAgt.lastIndexOf("/")) ) {
browserName = nAgt.substring(nameOffset,verOffset); name = nAgt.substring(nameOffset,verOffset);
fullVersion = nAgt.substring(verOffset+1); fullVersion = nAgt.substring(verOffset+1);
if (browserName.toLowerCase()==browserName.toUpperCase()) { if (name.toLowerCase()==name.toUpperCase()) {
browserName = navigator.appName; name = navigator.appName;
} }
} }
// Trim the fullVersion string at semicolon/space if present // Trim the fullVersion string at semicolon/space if present
@ -201,8 +207,13 @@
fullVersion = ""+parseFloat(navigator.appVersion); fullVersion = ""+parseFloat(navigator.appVersion);
majorVersion = parseInt(navigator.appVersion,10); majorVersion = parseInt(navigator.appVersion,10);
} }
// Return data // Return data
return [browserName, majorVersion]; return {
name: name,
version: majorVersion,
ios: /(iPad|iPhone|iPod)/g.test(navigator.platform)
};
} }
// Check for mime type support against a player instance // Check for mime type support against a player instance
@ -552,6 +563,18 @@
// Inject into the container // Inject into the container
player.container.insertAdjacentHTML("beforeend", html); player.container.insertAdjacentHTML("beforeend", html);
// Setup tooltips
if(config.tooltips) {
var labels = _getElements(config.selectors.labels);
for (var i = labels.length - 1; i >= 0; i--) {
var label = labels[i];
_toggleClass(label, config.classes.hidden, false);
_toggleClass(label, config.classes.tooltip, true);
}
}
} }
// Find the UI controls and store references // Find the UI controls and store references
@ -596,6 +619,7 @@
} }
catch(e) { catch(e) {
_log("It looks like there's a problem with your controls html. Bailing.", true); _log("It looks like there's a problem with your controls html. Bailing.", true);
return false; return false;
} }
} }
@ -626,7 +650,7 @@
player.media.removeAttribute("controls"); player.media.removeAttribute("controls");
// Set media type // Set media type
player.type = (player.media.tagName.toLowerCase() == "video" ? "video" : "audio"); player.type = player.media.tagName.toLowerCase();
// Add type class // Add type class
_toggleClass(player.container, config.classes[player.type], true); _toggleClass(player.container, config.classes[player.type], true);
@ -634,6 +658,11 @@
// If there's no autoplay attribute, assume the video is stopped and add state class // 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)); _toggleClass(player.container, config.classes.stopped, (player.media.getAttribute("autoplay") === null));
// Add iOS class
if(player.browser.ios) {
_toggleClass(player.container, "ios", true);
}
// Inject the player wrapper // Inject the player wrapper
if(player.type === "video") { if(player.type === "video") {
// Create the wrapper div // Create the wrapper div
@ -709,10 +738,10 @@
_showCaptions(player); _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.browser.name === "IE" && player.browser.version === 10) ||
(player.browserName === "IE" && player.browserMajorVersion === 11) || (player.browser.name === "IE" && player.browser.version === 11) ||
(player.browserName === "Firefox" && player.browserMajorVersion >= 31) || (player.browser.name === "Firefox" && player.browser.version >= 31) ||
(player.browserName === "Safari" && player.browserMajorVersion >= 7)) { (player.browser.name === "Safari" && player.browser.version >= 7)) {
// Debugging // Debugging
_log("Detected IE 10/11 or Firefox 31+ or Safari 7+."); _log("Detected IE 10/11 or Firefox 31+ or Safari 7+.");
@ -784,7 +813,7 @@
} }
// 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.browser.name === "Safari" && player.browser.version >= 7) {
_log("Safari 7+ detected; removing track from DOM."); _log("Safari 7+ detected; removing track from DOM.");
// Find all <track> elements // Find all <track> elements
@ -940,7 +969,7 @@
// Use default if needed // Use default if needed
if(typeof volume === "undefined") { if(typeof volume === "undefined") {
if(config.storage.enabled && _storage().supported) { if(config.storage.enabled && _storage().supported) {
volume = window.localStorage.plyr_volume || config.volume; volume = window.localStorage[config.storage.key] || config.volume;
} }
else { else {
volume = config.volume; volume = config.volume;
@ -994,6 +1023,19 @@
_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));
} }
// Check if media is loading
function _checkLoading(event) {
var loading = (event.type === "waiting");
// Clear timer
clearTimeout(player.loadingTimer);
// Timer to prevent flicker when seeking
player.loadingTimer = setTimeout(function() {
_toggleClass(player.container, config.classes.loading, loading);
}, (loading ? 250 : 0));
}
// Update <progress> elements // Update <progress> elements
function _updateProgress(event) { function _updateProgress(event) {
var progress = player.progress.played.bar, var progress = player.progress.played.bar,
@ -1232,6 +1274,9 @@
// Handle native play/pause // Handle native play/pause
_on(player.media, "play pause", _checkPlaying); _on(player.media, "play pause", _checkPlaying);
// Loading
_on(player.media, "waiting canplay seeked", _checkLoading);
} }
function _init() { function _init() {
@ -1239,16 +1284,14 @@
fullscreen = _fullscreen(); fullscreen = _fullscreen();
// Sniff // Sniff
player.browserInfo = _browserSniff(); player.browser = _browserSniff();
player.browserName = player.browserInfo[0];
player.browserMajorVersion = player.browserInfo[1];
// Debug info // Debug info
_log(player.browserName + " " + player.browserMajorVersion); _log(player.browser.name + " " + player.browser.version);
// 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.browser.name === "IE" && (player.browser.version === 8 || player.browser.version === 9) ) {
_log("Browser not suppported.", true); _log("Browser not suppported.", true);
return false; return false;
} }
@ -1321,6 +1364,13 @@
// Get the current element // Get the current element
var element = elements[i]; var element = elements[i];
// Disabled for <video> for iPhone
// Since it doesn't allow customisation
if (element.querySelectorAll("audio, video")[0].tagName.toLowerCase() === "video"
&& /iPhone/i.test(navigator.userAgent)) {
continue;
}
// Setup a player instance and add to the element // Setup a player instance and add to the element
if(typeof element.plyr === "undefined") { if(typeof element.plyr === "undefined") {
element.plyr = new Plyr(element); element.plyr = new Plyr(element);

View File

@ -5,486 +5,580 @@
// Variables // Variables
// ------------------------------- // -------------------------------
// Colors // Colors
@blue: #3498DB; @blue: #3498DB;
@gray-dark: #343f4a; @gray-dark: #343f4a;
@gray: #565d64; @gray: #565d64;
@gray-light: #cbd0d3; @gray-light: #cbd0d3;
// Font sizes // Font sizes
@font-size-small: 14px; @font-size-small: 14px;
@font-size-base: 16px; @font-size-base: 16px;
@font-size-large: ceil((@font-size-base * 1.5)); @font-size-large: ceil((@font-size-base * 1.5));
// Controls // Controls
@control-spacing: 10px; @control-spacing: 10px;
@controls-bg: @gray-dark; @controls-bg: @gray-dark;
@control-bg-hover: @blue; @control-bg-hover: @blue;
@control-color: @gray-light; @control-color: @gray-light;
@control-color-inactive: @gray; @control-color-inactive: @gray;
@control-color-hover: #fff; @control-color-hover: #fff;
// Tooltips
@tooltip-bg: @controls-bg;
@tooltip-color: #fff;
@tooltip-padding: @control-spacing;
@tooltip-arrow-size: 5px;
@tooltip-radius: 3px;
// Progress // Progress
@progress-bg: lighten(@gray, 10%); @progress-bg: lighten(@gray, 10%);
@progress-playing-bg: @blue; @progress-playing-bg: @blue;
@progress-buffered-bg: @gray; @progress-buffered-bg: @gray;
@progress-loading-size: 40px;
@progress-loading-bg: rgba(0,0,0, .15);
// Volume // Volume
@volume-track-height: 6px; @volume-track-height: 6px;
@volume-track-bg: @gray; @volume-track-bg: @gray;
@volume-thumb-height: (@volume-track-height * 2); @volume-thumb-height: (@volume-track-height * 2);
@volume-thumb-width: (@volume-track-height * 2); @volume-thumb-width: (@volume-track-height * 2);
@volume-thumb-bg: @control-color; @volume-thumb-bg: @control-color;
@volume-thumb-bg-focus: @control-bg-hover; @volume-thumb-bg-focus: @control-bg-hover;
// Breakpoints // Breakpoints
@bp-control-split: 560px; // When controls split into left/right @bp-control-split: 560px; // When controls split into left/right
@bp-captions-large: 768px; // When captions jump to the larger font size @bp-captions-large: 768px; // When captions jump to the larger font size
// Utility classes & mixins // Utility classes & mixins
// ------------------------------- // -------------------------------
// Screen reader only // Screen reader only
.sr-only { .sr-only {
position: absolute !important; position: absolute !important;
clip: rect(1px, 1px, 1px, 1px); clip: rect(1px, 1px, 1px, 1px);
padding: 0 !important; padding: 0 !important;
border: 0 !important; border: 0 !important;
height: 1px !important; height: 1px !important;
width: 1px !important; width: 1px !important;
overflow: hidden; overflow: hidden;
} }
// Contain floats: nicolasgallagher.com/micro-clearfix-hack/ // Contain floats: nicolasgallagher.com/micro-clearfix-hack/
.clearfix() { .clearfix() {
zoom: 1; zoom: 1;
&:before, &:before,
&:after { content: ""; display: table; } &:after { content: ""; display: table; }
&:after { clear: both; } &:after { clear: both; }
} }
// Tab focus styles // Tab focus styles
.tab-focus() { .tab-focus() {
outline: thin dotted #000; outline: thin dotted #000;
outline-offset: 0; outline-offset: 0;
}
// Animation
// ---------------------------------------
@keyframes progress {
to { background-position: @progress-loading-size 0; }
} }
// <input type="range"> styling // <input type="range"> styling
// --------------------------------------- // ---------------------------------------
.volume-thumb() { .volume-thumb() {
height: @volume-thumb-height; height: @volume-thumb-height;
width: @volume-thumb-width; width: @volume-thumb-width;
background: @volume-thumb-bg; background: @volume-thumb-bg;
border: 0; border: 0;
border-radius: (@volume-thumb-height / 2); border-radius: (@volume-thumb-height / 2);
transition: background .3s ease; transition: background .3s ease;
cursor: ew-resize; cursor: ew-resize;
} }
.volume-track() { .volume-track() {
height: @volume-track-height; height: @volume-track-height;
background: @volume-track-bg; background: @volume-track-bg;
border: 0; border: 0;
border-radius: (@volume-track-height / 2); border-radius: (@volume-track-height / 2);
} }
.seek-thumb() { .seek-thumb() {
background: transparent; background: transparent;
border: 0; border: 0;
width: 2px; width: (@control-spacing * 2);
height: @control-spacing; height: @control-spacing;
} }
.seek-track() { .seek-track() {
background: none; background: none;
border: 0; border: 0;
} }
// Font smoothing // Font smoothing
// --------------------------------------- // ---------------------------------------
.font-smoothing(@mode: on) when (@mode = on) { .font-smoothing(@mode: on) when (@mode = on) {
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
} }
.font-smoothing(@mode: on) when (@mode = off) { .font-smoothing(@mode: on) when (@mode = off) {
-moz-osx-font-smoothing: auto; -moz-osx-font-smoothing: auto;
-webkit-font-smoothing: subpixel-antialiased; -webkit-font-smoothing: subpixel-antialiased;
} }
// Styles // Styles
// ------------------------------- // -------------------------------
// Base // Base
.player { .player {
position: relative; position: relative;
max-width: 100%; max-width: 100%;
min-width: 290px; min-width: 290px;
overflow: hidden; // For the controls
// border-box everything // border-box everything
// http://paulirish.com/2012/box-sizing-border-box-ftw/ // http://paulirish.com/2012/box-sizing-border-box-ftw/
&, &,
*, *,
*::after, *::after,
*::before { *::before {
box-sizing: border-box; box-sizing: border-box;
} }
// For video // For video
&-video-wrapper { &-video-wrapper {
position: relative; position: relative;
} }
video { video {
width: 100%; width: 100%;
height: auto; height: auto;
vertical-align: middle; vertical-align: middle;
} }
// Captions // Captions
&-captions { &-captions {
display: none; display: none;
position: absolute; position: absolute;
bottom: 0; bottom: 0;
left: 0; left: 0;
width: 100%; width: 100%;
padding: 20px; padding: 20px;
min-height: 2.5em; min-height: 2.5em;
color: #fff; color: #fff;
font-size: @font-size-base; font-size: @font-size-base;
font-weight: 600; font-weight: 600;
text-shadow: text-shadow:
-1px -1px 0 @gray, -1px -1px 0 @gray,
1px -1px 0 @gray, 1px -1px 0 @gray,
-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: @bp-captions-large) { @media (min-width: @bp-captions-large) {
font-size: @font-size-large; font-size: @font-size-large;
} }
} }
&.captions-active &-captions { &.captions-active &-captions {
display: block; display: block;
} }
// Player controls // Player controls
&-controls { &-controls {
.clearfix(); .clearfix();
.font-smoothing(); .font-smoothing();
position: relative; position: relative;
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; text-align: center;
// Layout // Layout
&-sound { &-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) {
&-playback { &-left {
float: left; float: left;
} }
&-sound { &-right {
float: right; float: right;
margin-top: 0; margin-top: 0;
} }
} }
input + label, input + label,
button { button {
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
margin: 0 2px; margin: 0 2px;
padding: (@control-spacing / 2) @control-spacing; padding: (@control-spacing / 2) @control-spacing;
transition: background .3s ease; transition: background .3s ease;
border-radius: 3px; border-radius: 3px;
cursor: pointer; cursor: pointer;
svg { svg {
width: 18px; width: 18px;
height: 18px; height: 18px;
display: block; display: block;
fill: currentColor; fill: currentColor;
transition: fill .3s ease; transition: fill .3s ease;
} }
} }
input + label, input + label,
.inverted:checked + label { .inverted:checked + label {
color: @control-color-inactive; color: @control-color-inactive;
} }
button, button,
.inverted + label, .inverted + label,
input:checked + label { input:checked + label {
color: @control-color; color: @control-color;
} }
button { button {
border: 0; border: 0;
background: transparent; background: transparent;
overflow: hidden; overflow: hidden;
} }
button:focus, // Specificity for overriding .inverted
button:hover, button:focus,
input:focus + label, button:hover,
input + label:hover { [type="checkbox"]:focus + label,
background: @control-bg-hover; [type="checkbox"] + label:hover {
color: @control-color-hover; background: @control-bg-hover;
} color: @control-color-hover;
button:focus, }
input:focus + label { button:focus,
outline: 0; input:focus + label {
} outline: 0;
.icon-exit-fullscreen, }
.icon-muted, .icon-exit-fullscreen,
.icon-captions-on { .icon-muted,
display: none; .icon-captions-on {
} display: none;
.player-time { }
display: inline-block; .player-time {
vertical-align: middle; display: inline-block;
margin-left: @control-spacing; vertical-align: middle;
color: @control-color; margin-left: @control-spacing;
font-weight: 600; color: @control-color;
font-size: @font-size-small; font-weight: 600;
.font-smoothing(); font-size: @font-size-small;
} .font-smoothing();
} }
}
// Player progress // Tooltips
// <progress> element &-tooltip {
&-progress { visibility: hidden;
position: absolute; position: absolute;
top: 0; z-index: 2;
left: 0; bottom: 100%;
right: 0; margin-bottom: @tooltip-padding;
width: 100%; padding: @tooltip-padding (@tooltip-padding * 1.5);
height: @control-spacing;
background: @progress-bg;
&-buffer[value], opacity: 0;
&-played[value], background: @tooltip-bg;
&-seek[type=range] { border-radius: @tooltip-radius;
position: absolute; color: @tooltip-color;
left: 0; font-size: @font-size-small;
top: 0; line-height: 1.5;
width: 100%; font-weight: 600;
height: @control-spacing;
margin: 0;
padding: 0;
vertical-align: top;
-webkit-appearance: none; transform: translate(-50%, (@tooltip-padding * 3));
-moz-appearance: none; transition: transform .2s .2s ease, opacity .2s .2s ease;
border: none;
background: transparent;
}
&-buffer[value],
&-played[value] {
&::-webkit-progress-bar {
background: transparent;
}
// Inherit from currentColor; &::after {
&::-webkit-progress-value { content: "";
background: currentColor; display: block;
} position: absolute;
&::-moz-progress-bar { left: 50%;
background: currentColor; bottom: -@tooltip-arrow-size;
} margin-left: -@tooltip-arrow-size;
} width: 0;
&-played[value] { height: 0;
z-index: 2; transition: inherit;
color: @progress-playing-bg; border-style: solid;
} border-width: @tooltip-arrow-size @tooltip-arrow-size 0 @tooltip-arrow-size;
&-buffer[value] { border-color: @controls-bg transparent transparent;
color: @progress-buffered-bg; }
} }
label:hover .player-tooltip,
input:focus + label .player-tooltip,
button:hover .player-tooltip,
button:focus .player-tooltip {
visibility: visible;
opacity: 1;
transform: translate(-50%, 0);
}
label:hover .player-tooltip,
button:hover .player-tooltip {
z-index: 3;
}
// Seek control // Player progress
// <input[type='range']> element // <progress> element
// Specificity is for bootstrap compatibility &-progress {
&-seek[type=range] { position: absolute;
z-index: 3; top: 0;
cursor: pointer; left: 0;
outline: 0; right: 0;
width: 100%;
height: @control-spacing;
background: @progress-bg;
// Webkit &-buffer[value],
&::-webkit-slider-runnable-track { &-played[value],
.seek-track(); &-seek[type=range] {
} position: absolute;
&::-webkit-slider-thumb { left: 0;
-webkit-appearance: none; top: 0;
.seek-thumb(); width: 100%;
} height: @control-spacing;
margin: 0;
padding: 0;
vertical-align: top;
// Mozilla -webkit-appearance: none;
&::-moz-range-track { -moz-appearance: none;
.seek-track(); border: none;
} background: transparent;
&::-moz-range-thumb { }
-moz-appearance: none; &-buffer[value],
.seek-thumb(); &-played[value] {
} &::-webkit-progress-bar {
background: transparent;
}
// Microsoft // Inherit from currentColor;
&::-ms-track { &::-webkit-progress-value {
color: transparent; background: currentColor;
.seek-track(); }
} &::-moz-progress-bar {
&::-ms-fill-lower, background: currentColor;
&::-ms-fill-upper { }
.seek-track(); }
} &-played[value] {
&::-ms-thumb { z-index: 2;
.seek-thumb(); color: @progress-playing-bg;
} }
&-buffer[value] {
color: @progress-buffered-bg;
}
&:focus { // Seek control
outline: 0; // <input[type='range']> element
} // Specificity is for bootstrap compatibility
&::-moz-focus-outer { &-seek[type=range] {
border: 0; z-index: 4;
} cursor: pointer;
} outline: 0;
}
// States // Webkit
&-controls [data-player='pause'], &::-webkit-slider-runnable-track {
&.playing .player-controls [data-player='play'] { .seek-track();
display: none; }
} &::-webkit-slider-thumb {
&.playing .player-controls [data-player='pause'] { -webkit-appearance: none;
display: inline-block; .seek-thumb();
} }
// Volume control // Mozilla
// <input[type='range']> element &::-moz-range-track {
// Specificity is for bootstrap compatibility .seek-track();
&-volume[type=range] { }
display: inline-block; &::-moz-range-thumb {
vertical-align: middle; -moz-appearance: none;
-webkit-appearance: none; .seek-thumb();
-moz-appearance: none; }
width: 100px;
margin: 0 @control-spacing 0 0;
padding: 0;
cursor: pointer;
background: none;
// Webkit // Microsoft
&::-webkit-slider-runnable-track { &::-ms-track {
.volume-track(); color: transparent;
} .seek-track();
&::-webkit-slider-thumb { }
-webkit-appearance: none; &::-ms-fill-lower,
margin-top: -((@volume-thumb-height - @volume-track-height) / 2); &::-ms-fill-upper {
.volume-thumb(); .seek-track();
} }
&::-ms-thumb {
.seek-thumb();
}
// Mozilla &:focus {
&::-moz-range-track { outline: 0;
.volume-track(); }
} &::-moz-focus-outer {
&::-moz-range-thumb { border: 0;
.volume-thumb(); }
} }
}
// Microsoft // Loading state
&::-ms-track { &.loading .player-progress-buffer {
height: @volume-track-height; animation: progress 1s linear infinite;
background: transparent; background-size: @progress-loading-size @progress-loading-size;
border-color: transparent; background-repeat: repeat-x;
border-width: ((@volume-thumb-height - @volume-track-height) / 2) 0; background-color: @progress-buffered-bg;
color: transparent; background-image: linear-gradient(
} -45deg,
&::-ms-fill-lower, @progress-loading-bg 25%,
&::-ms-fill-upper { transparent 25%,
.volume-track(); transparent 50%,
} @progress-loading-bg 50%,
&::-ms-thumb { @progress-loading-bg 75%,
.volume-thumb(); transparent 75%,
} transparent);
color: transparent;
}
&:focus { // States
outline: 0; &-controls [data-player='pause'],
&.playing .player-controls [data-player='play'] {
display: none;
}
&.playing .player-controls [data-player='pause'] {
display: inline-block;
}
&::-webkit-slider-thumb { // Volume control
background: @volume-thumb-bg-focus; // <input[type='range']> element
} // Specificity is for bootstrap compatibility
&::-moz-range-thumb { &-volume[type=range] {
background: @volume-thumb-bg-focus; display: inline-block;
} vertical-align: middle;
&::-ms-thumb { -webkit-appearance: none;
background: @volume-thumb-bg-focus; -moz-appearance: none;
} width: 100px;
} margin: 0 @control-spacing 0 0;
} padding: 0;
cursor: pointer;
background: none;
// Full screen mode // Webkit
&-fullscreen, &::-webkit-slider-runnable-track {
&.fullscreen-active { .volume-track();
position: fixed; }
top: 0; &::-webkit-slider-thumb {
left: 0; -webkit-appearance: none;
right: 0; margin-top: -((@volume-thumb-height - @volume-track-height) / 2);
bottom: 0; .volume-thumb();
height: 100%; }
width: 100%;
z-index: 10000000;
background: #000;
.player-video-wrapper { // Mozilla
height: 100%; &::-moz-range-track {
width: 100%; .volume-track();
}
&::-moz-range-thumb {
.volume-thumb();
}
video { // Microsoft
height: 100%; &::-ms-track {
} height: @volume-track-height;
.player-captions { background: transparent;
top: auto; border-color: transparent;
bottom: 90px; border-width: ((@volume-thumb-height - @volume-track-height) / 2) 0;
color: transparent;
}
&::-ms-fill-lower,
&::-ms-fill-upper {
.volume-track();
}
&::-ms-thumb {
.volume-thumb();
}
@media (min-width: @bp-control-split) and (max-width: (@bp-captions-large - 1)) { &:focus {
bottom: 60px; outline: 0;
}
@media (min-width: @bp-captions-large) {
bottom: 80px;
}
}
}
.player-controls {
position: absolute;
bottom: 0;
left: 0;
right: 0;
}
}
// Change icons on state change &::-webkit-slider-thumb {
&.fullscreen-active .icon-exit-fullscreen, background: @volume-thumb-bg-focus;
&.muted .player-controls .icon-muted, }
&.captions-active .player-controls .icon-captions-on { &::-moz-range-thumb {
display: block; background: @volume-thumb-bg-focus;
}
&::-ms-thumb {
background: @volume-thumb-bg-focus;
}
}
}
& + svg { // Hide sound controls on iOS
display: none; // It's not supported to change volume using JavaScript:
} // https://developer.apple.com/library/safari/documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/Device-SpecificConsiderations/Device-SpecificConsiderations.html
} &.ios &-volume,
&.ios [data-player='mute'],
&.ios [data-player='mute'] + label,
&-audio.ios &-controls-right {
display: none;
}
// Center buttons so it looks less odd
&-audio.ios &-controls-left {
float: none;
}
// Some options are hidden by default // Full screen mode
[data-player='captions'], &-fullscreen,
[data-player='captions'] + label, &.fullscreen-active {
[data-player='fullscreen'], position: fixed;
[data-player='fullscreen'] + label { top: 0;
display: none; left: 0;
} right: 0;
&.captions-enabled [data-player='captions'], bottom: 0;
&.captions-enabled [data-player='captions'] + label, height: 100%;
&.fullscreen-enabled [data-player='fullscreen'], width: 100%;
&.fullscreen-enabled [data-player='fullscreen'] + label { z-index: 10000000;
display: inline-block; background: #000;
}
// Full browser view hides toggle .player-video-wrapper {
&-fullscreen [data-player='fullscreen'], height: 100%;
&-fullscreen [data-player='fullscreen'] + label { width: 100%;
display: none !important;
} video {
height: 100%;
}
.player-captions {
top: auto;
bottom: 90px;
@media (min-width: @bp-control-split) and (max-width: (@bp-captions-large - 1)) {
bottom: 60px;
}
@media (min-width: @bp-captions-large) {
bottom: 80px;
}
}
}
.player-controls {
position: absolute;
bottom: 0;
left: 0;
right: 0;
}
}
// Change icons on state change
&.fullscreen-active .icon-exit-fullscreen,
&.muted .player-controls .icon-muted,
&.captions-active .player-controls .icon-captions-on {
display: block;
& + svg {
display: none;
}
}
// Some options are hidden by default
[data-player='captions'],
[data-player='captions'] + label,
[data-player='fullscreen'],
[data-player='fullscreen'] + label {
display: none;
}
&.captions-enabled [data-player='captions'],
&.captions-enabled [data-player='captions'] + label,
&.fullscreen-enabled [data-player='fullscreen'],
&.fullscreen-enabled [data-player='fullscreen'] + label {
display: inline-block;
}
// Full browser view hides toggle
&-fullscreen [data-player='fullscreen'],
&-fullscreen [data-player='fullscreen'] + label {
display: none !important;
}
} }

View File

@ -5,490 +5,588 @@
// Variables // Variables
// ------------------------------- // -------------------------------
// Colors // Colors
$blue: #3498DB; $blue: #3498DB;
$gray-dark: #343f4a; $gray-dark: #343f4a;
$gray: #565d64; $gray: #565d64;
$gray-light: #cbd0d3; $gray-light: #cbd0d3;
// Font sizes // Font sizes
$font-size-small: 14px; $font-size-small: 14px;
$font-size-base: 16px; $font-size-base: 16px;
$font-size-large: ceil(($font-size-base * 1.5)); $font-size-large: ceil(($font-size-base * 1.5));
// Controls // Controls
$control-spacing: 10px; $control-spacing: 10px;
$controls-bg: $gray-dark; $controls-bg: $gray-dark;
$control-bg-hover: $blue; $control-bg-hover: $blue;
$control-color: $gray-light; $control-color: $gray-light;
$control-color-inactive: $gray; $control-color-inactive: $gray;
$control-color-focus: #fff; $control-color-hover: #fff;
$control-color-hover: #fff;
// Tooltips
$tooltip-bg: $controls-bg;
$tooltip-color: #fff;
$tooltip-padding: $control-spacing;
$tooltip-arrow-size: 5px;
$tooltip-radius: 3px;
// Progress // Progress
$progress-bg: lighten($gray, 10%); $progress-bg: lighten($gray, 10%);
$progress-playing-bg: $blue; $progress-playing-bg: $blue;
$progress-buffered-bg: $gray; $progress-buffered-bg: $gray;
$progress-loading-size: 40px;
$progress-loading-bg: rgba(0,0,0, .15);
// Range // Volume
$volume-track-height: 6px; $volume-track-height: 6px;
$volume-track-bg: $gray; $volume-track-bg: $gray;
$volume-thumb-height: ($volume-track-height * 2); $volume-thumb-height: ($volume-track-height * 2);
$volume-thumb-width: ($volume-track-height * 2); $volume-thumb-width: ($volume-track-height * 2);
$volume-thumb-bg: $control-color; $volume-thumb-bg: $control-color;
$volume-thumb-bg-focus: $control-bg-hover; $volume-thumb-bg-focus: $control-bg-hover;
// Breakpoints // Breakpoints
$bp-control-split: 560px; // When controls split into left/right $bp-control-split: 560px; // When controls split into left/right
$bp-captions-large: 768px; // When captions jump to the larger font size $bp-captions-large: 768px; // When captions jump to the larger font size
// Utility classes & mixins // Utility classes & mixins
// ------------------------------- // -------------------------------
// Screen reader only // Screen reader only
.sr-only { .sr-only {
position: absolute !important; position: absolute !important;
clip: rect(1px, 1px, 1px, 1px); clip: rect(1px, 1px, 1px, 1px);
padding: 0 !important; padding: 0 !important;
border: 0 !important; border: 0 !important;
height: 1px !important; height: 1px !important;
width: 1px !important; width: 1px !important;
overflow: hidden; overflow: hidden;
} }
// Contain floats: nicolasgallagher.com/micro-clearfix-hack/ // Contain floats: nicolasgallagher.com/micro-clearfix-hack/
@mixin clearfix() @mixin clearfix()
{ {
zoom: 1; zoom: 1;
&:before, &:before,
&:after { content: ""; display: table; } &:after { content: ""; display: table; }
&:after { clear: both; } &:after { clear: both; }
} }
// Tab focus styles // Tab focus styles
@mixin tab-focus() @mixin tab-focus()
{ {
outline: thin dotted #000; outline: thin dotted #000;
outline-offset: 0; outline-offset: 0;
}
// Animation
// ---------------------------------------
@keyframes progress {
to { background-position: $progress-loading-size 0; }
} }
// <input type="range"> styling // <input type="range"> styling
// --------------------------------------- // ---------------------------------------
@mixin volume-thumb() @mixin volume-thumb()
{ {
height: $volume-thumb-height; height: $volume-thumb-height;
width: $volume-thumb-width; width: $volume-thumb-width;
background: $volume-thumb-bg; background: $volume-thumb-bg;
border: 0; border: 0;
border-radius: ($volume-thumb-height / 2); border-radius: ($volume-thumb-height / 2);
transition: background .3s ease; transition: background .3s ease;
cursor: ew-resize; cursor: ew-resize;
} }
@mixin volume-track() @mixin volume-track()
{ {
height: $volume-track-height; height: $volume-track-height;
background: $volume-track-bg; background: $volume-track-bg;
border: 0; border: 0;
border-radius: ($volume-track-height / 2); border-radius: ($volume-track-height / 2);
} }
@mixin seek-thumb() { @mixin seek-thumb()
background: transparent; {
border: 0; background: transparent;
width: 2px; border: 0;
height: $control-spacing; width: ($control-spacing * 2);
height: $control-spacing;
} }
@mixin seek-track() { @mixin seek-track()
background: none; {
border: 0; background: none;
border: 0;
} }
// Font smoothing // Font smoothing
// --------------------------------------- // ---------------------------------------
@mixin font-smoothing($mode: on) when ($mode = on) @mixin font-smoothing($mode: on)
{ {
-moz-osx-font-smoothing: grayscale; @if $mode == 'on' {
-webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
} -webkit-font-smoothing: antialiased;
@mixin font-smoothing($mode: on) when ($mode = off) } @else if $mode == 'off' {
{ -moz-osx-font-smoothing: auto;
-moz-osx-font-smoothing: auto; -webkit-font-smoothing: subpixel-antialiased;
-webkit-font-smoothing: subpixel-antialiased; }
} }
// Styles // Styles
// ------------------------------- // -------------------------------
// Base // Base
.player { .player {
position: relative; position: relative;
max-width: 100%; max-width: 100%;
min-width: 290px; min-width: 290px;
overflow: hidden; // For the controls
// border-box everything // border-box everything
// http://paulirish.com/2012/box-sizing-border-box-ftw/ // http://paulirish.com/2012/box-sizing-border-box-ftw/
&, &,
*, *,
*::after, *::after,
*::before { *::before {
box-sizing: border-box; box-sizing: border-box;
} }
// For video // For video
&-video-wrapper { &-video-wrapper {
position: relative; position: relative;
} }
video { video {
width: 100%; width: 100%;
height: auto; height: auto;
vertical-align: middle; vertical-align: middle;
} }
// Captions // Captions
&-captions { &-captions {
display: none; display: none;
position: absolute; position: absolute;
bottom: 0; bottom: 0;
left: 0; left: 0;
width: 100%; width: 100%;
padding: 20px; padding: 20px;
min-height: 2.5em; min-height: 2.5em;
color: #fff; color: #fff;
font-size: $font-size-base; font-size: $font-size-base;
font-weight: 600; font-weight: 600;
text-shadow: text-shadow:
-1px -1px 0 $gray, -1px -1px 0 $gray,
1px -1px 0 $gray, 1px -1px 0 $gray,
-1px 1px 0 $gray, -1px 1px 0 $gray,
1px 1px 0 $gray; 1px 1px 0 $gray;
text-align: center; text-align: center;
@include font-smoothing(); @include font-smoothing();
@media (min-width: $bp-captions-large) { @media (min-width: $bp-captions-large) {
font-size: $font-size-large; font-size: $font-size-large;
} }
} }
&.captions-active &-captions { &.captions-active &-captions {
display: block; display: block;
} }
// Player controls // Player controls
&-controls { &-controls {
@include clearfix(); @include clearfix();
@include font-smoothing(); @include font-smoothing();
position: relative; position: relative;
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; text-align: center;
// Layout // Layout
&-sound { &-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) {
&-playback { &-left {
float: left; float: left;
} }
&-sound { &-right {
float: right; float: right;
margin-top: 0; margin-top: 0;
} }
} }
input + label, input + label,
button { button {
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
margin: 0 2px; margin: 0 2px;
padding: ($control-spacing / 2) $control-spacing; padding: ($control-spacing / 2) $control-spacing;
transition: background .3s ease; transition: background .3s ease;
border-radius: 3px; border-radius: 3px;
cursor: pointer; cursor: pointer;
svg { svg {
width: 18px; width: 18px;
height: 18px; height: 18px;
display: block; display: block;
fill: currentColor; fill: currentColor;
transition: fill .3s ease; transition: fill .3s ease;
} }
} }
input + label, input + label,
.inverted:checked + label { .inverted:checked + label {
color: $control-color-inactive; color: $control-color-inactive;
} }
button, button,
.inverted + label, .inverted + label,
input:checked + label { input:checked + label {
color: $control-color; color: $control-color;
} }
button { button {
border: 0; border: 0;
background: transparent; background: transparent;
overflow: hidden; overflow: hidden;
} }
input:focus + label,
button:focus {
@include tab-focus();
color: $control-color-focus;
}
button:hover,
input + label:hover {
background: $control-bg-hover;
color: $control-color-hover;
}
.icon-exit-fullscreen,
.icon-muted,
.icon-captions-on {
display: none;
}
.player-time {
display: inline-block;
vertical-align: middle;
margin-left: $control-spacing;
color: $control-color;
font-weight: 600;
font-size: $font-size-small;
@include font-smoothing();
}
}
// Player progress // Specificity for overriding .inverted
// <progress> element button:focus,
&-progress { button:hover,
position: absolute; [type="checkbox"]:focus + label,
top: 0; [type="checkbox"] + label:hover {
left: 0; background: $control-bg-hover;
right: 0; color: $control-color-hover;
width: 100%; }
height: $control-spacing; button:focus,
background: $progress-bg; input:focus + label {
outline: 0;
}
.icon-exit-fullscreen,
.icon-muted,
.icon-captions-on {
display: none;
}
.player-time {
display: inline-block;
vertical-align: middle;
margin-left: $control-spacing;
color: $control-color;
font-weight: 600;
font-size: $font-size-small;
@include font-smoothing();
}
}
&-buffer[value], // Tooltips
&-played[value], &-tooltip {
&-seek[type=range] { visibility: hidden;
position: absolute; position: absolute;
left: 0; z-index: 2;
top: 0; bottom: 100%;
width: 100%; margin-bottom: $tooltip-padding;
height: 100%; padding: $tooltip-padding ($tooltip-padding * 1.5);
margin: 0;
vertical-align: top;
-webkit-appearance: none; opacity: 0;
-moz-appearance: none; background: $tooltip-bg;
border: none; border-radius: $tooltip-radius;
background: transparent; color: $tooltip-color;
} font-size: $font-size-small;
line-height: 1.5;
font-weight: 600;
&-buffer[value], transform: translate(-50%, ($tooltip-padding * 3));
&-played[value] { transition: transform .2s .2s ease, opacity .2s .2s ease;
&::-webkit-progress-bar {
background: transparent;
}
// Inherit from currentColor; &::after {
&::-webkit-progress-value { content: "";
background: currentColor; display: block;
} position: absolute;
&::-moz-progress-bar { left: 50%;
background: currentColor; bottom: -$tooltip-arrow-size;
} margin-left: -$tooltip-arrow-size;
} width: 0;
&-played[value] { height: 0;
z-index: 2; transition: inherit;
color: $progress-playing-bg; border-style: solid;
} border-width: $tooltip-arrow-size $tooltip-arrow-size 0 $tooltip-arrow-size;
&-buffer[value] { border-color: $controls-bg transparent transparent;
color: $progress-buffered-bg; }
} }
label:hover .player-tooltip,
input:focus + label .player-tooltip,
button:hover .player-tooltip,
button:focus .player-tooltip {
visibility: visible;
opacity: 1;
transform: translate(-50%, 0);
}
label:hover .player-tooltip,
button:hover .player-tooltip {
z-index: 3;
}
// Seek control // Player progress
// <input[type='range']> element // <progress> element
// Specificity is for bootstrap compatibility &-progress {
&-seek[type=range] { position: absolute;
z-index: 3; top: 0;
cursor: pointer; left: 0;
outline: 0; right: 0;
width: 100%;
height: $control-spacing;
background: $progress-bg;
// Webkit &-buffer[value],
&::-webkit-slider-runnable-track { &-played[value],
@include seek-track(); &-seek[type=range] {
} position: absolute;
&::-webkit-slider-thumb { left: 0;
-webkit-appearance: none; top: 0;
@include seek-thumb(); width: 100%;
} height: $control-spacing;
margin: 0;
padding: 0;
vertical-align: top;
// Mozilla -webkit-appearance: none;
&::-moz-range-track { -moz-appearance: none;
@include seek-track(); border: none;
} background: transparent;
&::-moz-range-thumb { }
-moz-appearance: none; &-buffer[value],
@include seek-thumb(); &-played[value] {
} &::-webkit-progress-bar {
background: transparent;
}
// Microsoft // Inherit from currentColor;
&::-ms-track { &::-webkit-progress-value {
color: transparent; background: currentColor;
@include seek-track(); }
} &::-moz-progress-bar {
&::-ms-fill-lower, background: currentColor;
&::-ms-fill-upper { }
@include seek-track(); }
} &-played[value] {
&::-ms-thumb { z-index: 2;
@include seek-thumb(); color: $progress-playing-bg;
} }
&-buffer[value] {
color: $progress-buffered-bg;
}
&:focus { // Seek control
outline: 0; // <input[type='range']> element
} // Specificity is for bootstrap compatibility
&::-moz-focus-outer { &-seek[type=range] {
border: 0; z-index: 4;
} cursor: pointer;
} outline: 0;
}
// States // Webkit
&-controls [data-player='pause'], &::-webkit-slider-runnable-track {
&.playing .player-controls [data-player='play'] { @include seek-track();
display: none; }
} &::-webkit-slider-thumb {
&.playing .player-controls [data-player='pause'] { -webkit-appearance: none;
display: inline-block; @include seek-thumb();
} }
// Volume control // Mozilla
// <input[type='range']> element &::-moz-range-track {
// Specificity is for bootstrap compatibility @include seek-track();
&-volume[type=range] { }
vertical-align: middle; &::-moz-range-thumb {
-webkit-appearance: none; -moz-appearance: none;
-moz-appearance: none; @include seek-thumb();
width: 100px; }
margin: 0 $control-spacing 0 0;
padding: 0;
cursor: pointer;
background: none;
// Webkit // Microsoft
&::-webkit-slider-runnable-track { &::-ms-track {
@include range-track(); color: transparent;
} @include seek-track();
&::-webkit-slider-thumb { }
-webkit-appearance: none; &::-ms-fill-lower,
margin-top: -(($volume-thumb-height - $volume-track-height) / 2); &::-ms-fill-upper {
@include range-thumb(); @include seek-track();
} }
&::-ms-thumb {
@include seek-thumb();
}
// Mozilla &:focus {
&::-moz-range-track { outline: 0;
@include range-track(); }
} &::-moz-focus-outer {
&::-moz-range-thumb { border: 0;
@include range-thumb(); }
} }
}
// Microsoft // Loading state
&::-ms-track { &.loading .player-progress-buffer {
height: $volume-track-height; animation: progress 1s linear infinite;
background: transparent; background-size: $progress-loading-size $progress-loading-size;
border-color: transparent; background-repeat: repeat-x;
border-width: (($volume-thumb-height - $volume-track-height) / 2) 0; background-color: $progress-buffered-bg;
color: transparent; background-image: linear-gradient(
} -45deg,
&::-ms-fill-lower, $progress-loading-bg 25%,
&::-ms-fill-upper { transparent 25%,
@include range-track(); transparent 50%,
} $progress-loading-bg 50%,
&::-ms-thumb { $progress-loading-bg 75%,
@include range-thumb(); transparent 75%,
} transparent);
color: transparent;
}
&:focus { // States
outline: 0; &-controls [data-player='pause'],
&.playing .player-controls [data-player='play'] {
display: none;
}
&.playing .player-controls [data-player='pause'] {
display: inline-block;
}
&::-webkit-slider-thumb { // Volume control
background: $volume-thumb-bg-focus; // <input[type='range']> element
} // Specificity is for bootstrap compatibility
&::-moz-range-thumb { &-volume[type=range] {
background: $volume-thumb-bg-focus; display: inline-block;
} vertical-align: middle;
&::-ms-thumb { -webkit-appearance: none;
background: $volume-thumb-bg-focus; -moz-appearance: none;
} width: 100px;
} margin: 0 $control-spacing 0 0;
} padding: 0;
cursor: pointer;
background: none;
// Full screen mode // Webkit
&-fullscreen, &::-webkit-slider-runnable-track {
&.fullscreen-active { @include volume-track();
position: fixed; }
top: 0; &::-webkit-slider-thumb {
left: 0; -webkit-appearance: none;
right: 0; margin-top: -(($volume-thumb-height - $volume-track-height) / 2);
bottom: 0; @include volume-thumb();
height: 100%; }
width: 100%;
z-index: 10000000;
background: #000;
.player-video-wrapper { // Mozilla
height: 100%; &::-moz-range-track {
width: 100%; @include volume-track();
}
&::-moz-range-thumb {
@include volume-thumb();
}
video { // Microsoft
height: 100%; &::-ms-track {
} height: $volume-track-height;
.player-captions { background: transparent;
top: auto; border-color: transparent;
bottom: 90px; border-width: (($volume-thumb-height - $volume-track-height) / 2) 0;
color: transparent;
}
&::-ms-fill-lower,
&::-ms-fill-upper {
@include volume-track();
}
&::-ms-thumb {
@include volume-thumb();
}
@media (min-width: $bp-control-split) and (max-width: ($bp-captions-large - 1)) { &:focus {
bottom: 60px; outline: 0;
}
@media (min-width: $bp-captions-large) {
bottom: 80px;
}
}
}
.player-controls {
position: absolute;
bottom: 0;
left: 0;
right: 0;
}
}
// Change icons on state change &::-webkit-slider-thumb {
&.fullscreen-active .icon-exit-fullscreen, background: $volume-thumb-bg-focus;
&.muted .player-controls .icon-muted, }
&.captions-active .player-controls .icon-captions-on { &::-moz-range-thumb {
display: block; background: $volume-thumb-bg-focus;
}
&::-ms-thumb {
background: $volume-thumb-bg-focus;
}
}
}
& + svg { // Hide sound controls on iOS
display: none; // It's not supported to change volume using JavaScript:
} // https://developer.apple.com/library/safari/documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/Device-SpecificConsiderations/Device-SpecificConsiderations.html
} &.ios &-volume,
&.ios [data-player='mute'],
&.ios [data-player='mute'] + label,
&-audio.ios &-controls-right {
display: none;
}
// Center buttons so it looks less odd
&-audio.ios &-controls-left {
float: none;
}
// Some options are hidden by default // Full screen mode
[data-player='captions'], &-fullscreen,
[data-player='captions'] + label, &.fullscreen-active {
[data-player='fullscreen'], position: fixed;
[data-player='fullscreen'] + label { top: 0;
display: none; left: 0;
} right: 0;
&.captions-enabled [data-player='captions'], bottom: 0;
&.captions-enabled [data-player='captions'] + label, height: 100%;
&.fullscreen-enabled [data-player='fullscreen'], width: 100%;
&.fullscreen-enabled [data-player='fullscreen'] + label { z-index: 10000000;
display: inline-block; background: #000;
}
// Full browser view hides toggle .player-video-wrapper {
&-fullscreen [data-player='fullscreen'], height: 100%;
&-fullscreen [data-player='fullscreen'] + label { width: 100%;
display: none !important;
} video {
height: 100%;
}
.player-captions {
top: auto;
bottom: 90px;
@media (min-width: $bp-control-split) and (max-width: ($bp-captions-large - 1)) {
bottom: 60px;
}
@media (min-width: $bp-captions-large) {
bottom: 80px;
}
}
}
.player-controls {
position: absolute;
bottom: 0;
left: 0;
right: 0;
}
}
// Change icons on state change
&.fullscreen-active .icon-exit-fullscreen,
&.muted .player-controls .icon-muted,
&.captions-active .player-controls .icon-captions-on {
display: block;
& + svg {
display: none;
}
}
// Some options are hidden by default
[data-player='captions'],
[data-player='captions'] + label,
[data-player='fullscreen'],
[data-player='fullscreen'] + label {
display: none;
}
&.captions-enabled [data-player='captions'],
&.captions-enabled [data-player='captions'] + label,
&.fullscreen-enabled [data-player='fullscreen'],
&.fullscreen-enabled [data-player='fullscreen'] + label {
display: inline-block;
}
// Full browser view hides toggle
&-fullscreen [data-player='fullscreen'],
&-fullscreen [data-player='fullscreen'] + label {
display: none !important;
}
} }