Compare commits

...

13 Commits

Author SHA1 Message Date
ecbda018c5 Merge branch 'master' of github.com:selz/plyr
# Conflicts:
#	src/js/plyr.js
2015-04-10 07:17:23 +10:00
5187311ff0 Bug fix for isFullscreen() in Mozilla (Fixes #38) 2015-04-10 07:16:53 +10:00
d9a94ac7b0 Version bump 2015-04-08 13:42:34 +10:00
3526e322ef Minor bug fixes 2015-04-06 19:42:26 +10:00
99cabd545d Fix for generated IDs for controls, UI tweak for audio 2015-04-06 13:25:36 +10:00
1f7f7b10de Docs tweak 2015-04-06 12:06:34 +10:00
b2421b592a Merge branch 'master' of github.com:selz/plyr 2015-04-06 11:47:43 +10:00
5322f4c62f Fullscreen API methods (Fixes #74), onSetup callback 2015-04-06 11:47:23 +10:00
7ab8647fc8 Merge pull request #75 from franks921/fs-fix-localstorage-volume-settings
fix: use custom localStorage key for volume setting
2015-04-05 21:39:08 +10:00
50c76f3d7e fix: use custom localStorage key for volume setting 2015-04-05 13:26:17 +02:00
2f4c56176d Updated screenshot 2015-04-04 12:58:09 +11:00
7c5f38311b Bug fixes for controls changes 2015-04-04 12:51:24 +11:00
e568bc9c8d Controls improvements
- Added an option to toggle which controls display
- Better handle missing controls
2015-04-04 12:32:37 +11:00
17 changed files with 447 additions and 186 deletions

View File

@ -1,7 +1,28 @@
# Changelog
## v1.1.5
- Fix for incorrect `isFullscreen()` return value in Mozilla (Fixes #38)
## v1.1.4
- Minor bug fixes
## v1.1.3
- Fixes for random id used in controls with multiple instances and one call to setup
- Audio player UI improvements
## v1.1.2
- Added an onSetup callback option
- Added fullscreen API methods `toggleFullscreen()` (must be user iniated), and `isFullscreen()`
## v1.1.1
- Fix for unsupported browser handling
- Fix for config.controls having no effect
## v1.1.0
- Added config option to set which controls are shown (if using the default controls html) and better handling of missing controls
## v1.0.31
- Display duration on metadataloaded
- Display duration on `metadataloaded`
## v1.0.30
- Fixed bug with media longer than 60 minutes (Fixes #69)

View File

@ -2,7 +2,7 @@
This is the markup that is rendered for the Plyr controls. You can use the default controls or provide a customized version of markup based on your needs.
The demo Plyr setup uses a Hogan template. This purely to allow for localization at a later date. Check out `controls.html` in `/src/templates` to get an idea of how the default html is structured. Alternatively check out the `plyr.js` source.
The demo Plyr setup uses a Hogan template. This purely to allow for localization at a later date. Check out `controls.html` in `/src/templates` to get an idea of how the default html is structured.
## Requirements
@ -13,11 +13,11 @@ You need to add several placeholders to your html template that are replaced whe
- `{id}` - the dynamically generated ID for the player (for form controls)
- `{seektime}` - the seek time specified in options for fast forward and rewind
Currently all buttons and inputs need to be present for Plyr to work but later we'll make it more dynamic so if you omit a button or input, it'll still work.
You can include only the controls you need when specifying custom html.
## Default
## Example
This is the default `html` option from `plyr.js`.
This is an example `html` option with all controls.
```javascript
["<div class='player-controls'>",
@ -53,7 +53,11 @@ This is the default `html` option from `plyr.js`.
"<span class='sr-only'>Forward {seektime} secs</span>",
"</button>",
"<span class='player-time'>",
"<span class='sr-only'>Time</span>",
"<span class='sr-only'>Current time</span>",
"<span class='player-current-time'>00:00</span>",
"</span>",
"<span class='player-time'>",
"<span class='sr-only'>Duration</span>",
"<span class='player-duration'>00:00</span>",
"</span>",
"</span>",

2
dist/plyr.css vendored

File diff suppressed because one or more lines are too long

2
dist/plyr.js vendored

File diff suppressed because one or more lines are too long

2
docs/dist/docs.css vendored
View File

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

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 = {};
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: { }});
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\">Current time</span>");t.b("\n" + i);t.b(" <span class=\"player-current-time\">00:00</span>");t.b("\n" + i);t.b(" </span>");t.b("\n" + i);t.b(" <span class=\"player-time\">");t.b("\n" + i);t.b(" <span class=\"sr-only\">Duration</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

@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Docs styles -->
<link rel="stylesheet" href="//cdn.plyr.io/1.0.31/docs.css">
<link rel="stylesheet" href="//cdn.plyr.io/1.1.5/docs.css">
</head>
<body>
<main>

View File

@ -8,10 +8,10 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Styles -->
<link rel="stylesheet" href="//cdn.plyr.io/1.0.31/plyr.css">
<link rel="stylesheet" href="//cdn.plyr.io/1.1.5/plyr.css">
<!-- Docs styles -->
<link rel="stylesheet" href="//cdn.plyr.io/1.0.31/docs.css">
<link rel="stylesheet" href="//cdn.plyr.io/1.1.5/docs.css">
</head>
<body>
<header>
@ -83,13 +83,13 @@
b.insertBefore(c, b.childNodes[0]);
}
}
})(document, "https://cdn.plyr.io/1.0.31/sprite.svg");
})(document, "https://cdn.plyr.io/1.1.5/sprite.svg");
</script>
<!-- Plyr core script -->
<script src="//cdn.plyr.io/1.0.31/plyr.js"></script>
<script src="//cdn.plyr.io/1.1.5/plyr.js"></script>
<!-- Docs script -->
<script src="//cdn.plyr.io/1.0.31/docs.js"></script>
<script src="//cdn.plyr.io/1.1.5/docs.js"></script>
</body>
</html>

View File

@ -11,9 +11,21 @@ plyr.setup({
html: templates.controls.render({}),
captions: {
defaultActive: true
},
onSetup: function() {
var player = this,
type = player.media.tagName.toLowerCase(),
toggle = document.querySelector("[data-toggle='fullscreen']");
console.log("✓ Setup done for <" + type + ">");
if(type === "video" && toggle) {
toggle.addEventListener("click", player.toggleFullscreen, false);
}
}
});
// Google analytics
// For demo site (http://[www.]plyr.io) only
if(document.domain.indexOf("plyr.io") > -1) {

View File

@ -139,7 +139,7 @@ a {
// Players
.example-audio .player {
max-width: 480px;
max-width: 520px;
}
.example-video .player {
max-width: 1200px;

View File

@ -31,7 +31,11 @@
<span class="sr-only">Forward {seektime} secs</span>
</button>
<span class="player-time">
<span class="sr-only">Time</span>
<span class="sr-only">Current time</span>
<span class="player-current-time">00:00</span>
</span>
<span class="player-time">
<span class="sr-only">Duration</span>
<span class="player-duration">00:00</span>
</span>
</span>

View File

@ -1,6 +1,6 @@
{
"name": "plyr",
"version": "1.0.31",
"version": "1.1.5",
"description": "A simple HTML5 media player using custom controls",
"homepage": "http://plyr.io",
"main": "gulpfile.js",

View File

@ -3,14 +3,14 @@ A simple, accessible HTML5 media player.
[Checkout the demo](http://plyr.io)
[![Image of Plyr](https://cdn.plyr.io/static/plyr.png?1)](http://plyr.io)
[![Image of Plyr](https://cdn.plyr.io/static/plyr.png?2)](http://plyr.io)
## Why?
We wanted a lightweight, accessible and customisable media player that just supports *modern* browsers. Sure, there are many other players out there but we wanted to keep things simple, using the right elements for the job.
## Features
- **Accessible** - full support for captions and screen readers.
- **Lightweight** - just 6KB minified and gzipped.
- **Lightweight** - just 6.4KB minified and gzipped.
- **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.
- **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.
**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.31/plyr.js` to `https://cdn.plyr.io/1.0.31/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.1.5/plyr.js` to `https://cdn.plyr.io/1.1.5/plyr.js`
### Bower
If bower is your thang, you can grab Plyr using:
@ -47,15 +47,22 @@ bower install plyr
```
More info on setting up dependencies can be found in the [Bower Docs](http://bower.io/docs/creating-packages/#maintaining-dependencies)
### Ember
The awesome [@louisrudner](https://twitter.com/louisrudner) has created an ember component, available by running:
```
ember addon:install ember-cli-plyr
```
More info is on [npm](https://www.npmjs.com/package/ember-cli-plyr) and [GitHub](https://github.com/louisrudner/ember-cli-plyr)
### CDN
If you want to use our CDN, you can use the following. HTTPS (SSL) is supported.
```html
<link rel="stylesheet" href="//cdn.plyr.io/1.0.31/plyr.css">
<script src="//cdn.plyr.io/1.0.31/plyr.js"></script>
<link rel="stylesheet" href="//cdn.plyr.io/1.1.5/plyr.css">
<script src="//cdn.plyr.io/1.1.5/plyr.js"></script>
```
You can also access the `sprite.svg` file at `//cdn.plyr.io/1.0.31/sprite.svg`.
You can also access the `sprite.svg` file at `//cdn.plyr.io/1.1.5/sprite.svg`.
### 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.
@ -130,7 +137,7 @@ You'll notice the `crossorigin` attribute on the example `<video>` and `<audio>`
More info on CORS here:
[https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS)
###JavaScript
### JavaScript
Much of the behaviour of the player is configurable when initialising the library. Below is an example of a default instance.
```html
@ -168,6 +175,12 @@ You can pass the following options to the setup method.
<td><code><a href="controls.md">See controls.md</a></code></td>
<td>See <a href="controls.md">controls.md</a> for more info on how the html needs to be structured.</td>
</tr>
<tr>
<td><code>controls</code></td>
<td>Array</td>
<td><code>["restart", "rewind", "play", "fast-forward", "current-time", "duration", "mute", "volume", "captions", "fullscreen"]</code></td>
<td>Toggle which control elements you would like to display when using the default controls html. If you specify a <code>html</code> option, this is redundant. The default value is to display everything.</td>
</tr>
<tr>
<td><code>debug</code></td>
<td>Boolean</td>
@ -202,7 +215,7 @@ You can pass the following options to the setup method.
<td><code>displayDuration</code></td>
<td>Boolean</td>
<td><code>true</code></td>
<td>Displays the duration of the media on the "metadataloaded" event (on startup). This will only work if the `preload` attribute is not set to `none`. It is `auto` by default (if the attribute is not present).</td>
<td>Displays the duration of the media on the "metadataloaded" event (on startup) in the current time display. This will only work if the `preload` attribute is not set to `none` (or is not set at all) and you choose not to display the duration (see <code>controls</code> option).</td>
</tr>
<tr>
<td><code>selectors</code></td>
@ -234,6 +247,12 @@ You can pass the following options to the setup method.
<td>&mdash;</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>
<td><code>onSetup</code></td>
<td>Function</td>
<td>&mdash;</td>
<td>This callback function is called on every new plyr instance created. The context (<code>this</code>) is the plyr instance itself.</td>
</tr>
</tbody>
</table>
@ -301,6 +320,16 @@ Here's a list of the methods supported:
<td>&mdash;</td>
<td>Toggles whether captions are enabled.</td>
</tr>
<tr>
<td><code>toggleFullscreen()</code></td>
<td>Event</td>
<td>Toggles fullscreen. This can only be initiated by a user gesture due to browser security, i.e. a user event such as click.</td>
</tr>
<tr>
<td><code>isFullscreen()</code></td>
<td>&mdash;</td>
<td>Boolean returned if the player is in fullscreen.</td>
</tr>
<tr>
<td><code>support(...)</code></td>
<td>String</td>
@ -321,7 +350,7 @@ Here's a list of the methods supported:
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>
</tr>
<tr>
<tr>
<td><code>poster(...)</code></td>
<td>String</td>
<td>Set the poster url. This is supported for the <code>video</code> element only.</td>
@ -344,11 +373,12 @@ A complete list of events can be found here:
[Media Events - W3.org](http://www.w3.org/2010/05/video/mediaevents.html)
## Fullscreen
Fullscreen in Plyr is supported for all browsers that [currently support it](http://caniuse.com/#feat=fullscreen). If you're using the default CSS, you can also use a "full browser" mode which will use the full browser window by adding the `player-fullscreen` class to your container.
## Browser support
<table width="100%" style="text-align: center;">
<table width="100%" style="text-align: center">
<thead>
<tr>
<td>Safari</td>
@ -386,11 +416,14 @@ If a User Agent is disabled but supports `<video>` and `<audio>` natively, it wi
Any unsupported browsers will display links to download the media if the correct html is used.
### Checking for support
There's an API method for checking support. You can call `plyr.supported()` and optionally pass a type to it, e.g. `plyr.supported("video")`. It will return an object with two keys; `basic` meaning there's basic support for that media type (or both if no type is passed) and `full` meaning there's full support for plyr.
## Issues
If you find anything weird with Plyr, please let us know using the GitHub issues tracker.
## Author
Plyr is developed by Sam Potts ([@sam_potts](https://twitter.com/sam_potts)) ([sampotts.me](http://sampotts.me))
Plyr is developed by [@sam_potts](https://twitter.com/sam_potts) / [sampotts.me](http://sampotts.me)
## Mentions
- [The Changelog](http://thechangelog.com/plyr-simple-html5-media-player-custom-controls-webvtt-captions/)

View File

@ -1,6 +1,6 @@
// ==========================================================================
// Plyr
// plyr.js v1.0.31
// plyr.js v1.1.5
// https://github.com/selz/plyr
// License: The MIT License (MIT)
// ==========================================================================
@ -44,6 +44,7 @@
played: ".player-progress-played"
},
captions: ".player-captions",
currentTime: ".player-current-time",
duration: ".player-duration"
},
classes: {
@ -79,71 +80,149 @@
enabled: true,
key: "plyr_volume"
},
html: (function() {
return [
"<div class='player-controls'>",
"<div class='player-progress'>",
"<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'>",
"<progress class='player-progress-played' max='100' value='0'>",
"<span>0</span>% played",
"</progress>",
"<progress class='player-progress-buffer' max='100' value='0'>",
"<span>0</span>% buffered",
"</progress>",
"</div>",
"<span class='player-controls-left'>",
"<button type='button' data-player='restart'>",
"<svg><use xlink:href='#icon-restart'></use></svg>",
"<span class='sr-only'>Restart</span>",
"</button>",
"<button type='button' data-player='rewind'>",
"<svg><use xlink:href='#icon-rewind'></use></svg>",
"<span class='sr-only'>Rewind {seektime} secs</span>",
"</button>",
"<button type='button' data-player='play'>",
"<svg><use xlink:href='#icon-play'></use></svg>",
"<span class='sr-only'>Play</span>",
"</button>",
"<button type='button' data-player='pause'>",
"<svg><use xlink:href='#icon-pause'></use></svg>",
"<span class='sr-only'>Pause</span>",
"</button>",
"<button type='button' data-player='fast-forward'>",
"<svg><use xlink:href='#icon-fast-forward'></use></svg>",
"<span class='sr-only'>Forward {seektime} secs</span>",
"</button>",
"<span class='player-time'>",
"<span class='sr-only'>Time</span>",
"<span class='player-duration'>00:00</span>",
"</span>",
"</span>",
"<span class='player-controls-right'>",
"<input class='inverted sr-only' id='mute{id}' type='checkbox' data-player='mute'>",
"<label id='mute{id}' for='mute{id}'>",
"<svg class='icon-muted'><use xlink:href='#icon-muted'></use></svg>",
"<svg><use xlink:href='#icon-volume'></use></svg>",
"<span class='sr-only'>Toggle Mute</span>",
"</label>",
"<label for='volume{id}' class='sr-only'>Volume</label>",
"<input id='volume{id}' class='player-volume' type='range' min='0' max='10' value='5' data-player='volume'>",
"<input class='sr-only' id='captions{id}' type='checkbox' data-player='captions'>",
"<label for='captions{id}'>",
"<svg class='icon-captions-on'><use xlink:href='#icon-captions-on'></use></svg>",
"<svg><use xlink:href='#icon-captions-off'></use></svg>",
"<span class='sr-only'>Toggle Captions</span>",
"</label>",
"<button type='button' data-player='fullscreen'>",
"<svg class='icon-exit-fullscreen'><use xlink:href='#icon-exit-fullscreen'></use></svg>",
"<svg><use xlink:href='#icon-enter-fullscreen'></use></svg>",
"<span class='sr-only'>Toggle Fullscreen</span>",
"</button>",
"</span>",
"</div>"
].join("\n");
})()
controls: ["restart", "rewind", "play", "fast-forward", "current-time", "duration", "mute", "volume", "captions", "fullscreen"],
onSetup: function() {},
};
// Build the default HTML
function _buildControls() {
// Open and add the progress and seek elements
var html = [
"<div class='player-controls'>",
"<div class='player-progress'>",
"<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'>",
"<progress class='player-progress-played' max='100' value='0'>",
"<span>0</span>% played",
"</progress>",
"<progress class='player-progress-buffer' max='100' value='0'>",
"<span>0</span>% buffered",
"</progress>",
"</div>",
"<span class='player-controls-left'>"];
// Restart button
if(_inArray(config.controls, "restart")) {
html.push(
"<button type='button' data-player='restart'>",
"<svg><use xlink:href='#icon-restart'></use></svg>",
"<span class='sr-only'>Restart</span>",
"</button>"
);
}
// Rewind button
if(_inArray(config.controls, "rewind")) {
html.push(
"<button type='button' data-player='rewind'>",
"<svg><use xlink:href='#icon-rewind'></use></svg>",
"<span class='sr-only'>Rewind {seektime} secs</span>",
"</button>"
);
}
// Play/pause button
if(_inArray(config.controls, "play")) {
html.push(
"<button type='button' data-player='play'>",
"<svg><use xlink:href='#icon-play'></use></svg>",
"<span class='sr-only'>Play</span>",
"</button>",
"<button type='button' data-player='pause'>",
"<svg><use xlink:href='#icon-pause'></use></svg>",
"<span class='sr-only'>Pause</span>",
"</button>"
);
}
// Fast forward button
if(_inArray(config.controls, "fast-forward")) {
html.push(
"<button type='button' data-player='fast-forward'>",
"<svg><use xlink:href='#icon-fast-forward'></use></svg>",
"<span class='sr-only'>Forward {seektime} secs</span>",
"</button>"
);
}
// Media current time display
if(_inArray(config.controls, "current-time")) {
html.push(
"<span class='player-time'>",
"<span class='sr-only'>Current time</span>",
"<span class='player-current-time'>00:00</span>",
"</span>"
);
}
// Media duration display
if(_inArray(config.controls, "duration")) {
html.push(
"<span class='player-time'>",
"<span class='sr-only'>Duration</span>",
"<span class='player-duration'>00:00</span>",
"</span>"
);
}
// Close left controls
html.push(
"</span>",
"<span class='player-controls-right'>"
);
// Toggle mute button
if(_inArray(config.controls, "mute")) {
html.push(
"<input class='inverted sr-only' id='mute{id}' type='checkbox' data-player='mute'>",
"<label id='mute{id}' for='mute{id}'>",
"<svg class='icon-muted'><use xlink:href='#icon-muted'></use></svg>",
"<svg><use xlink:href='#icon-volume'></use></svg>",
"<span class='sr-only'>Toggle Mute</span>",
"</label>"
);
}
// Volume range control
if(_inArray(config.controls, "volume")) {
html.push(
"<label for='volume{id}' class='sr-only'>Volume</label>",
"<input id='volume{id}' class='player-volume' type='range' min='0' max='10' value='5' data-player='volume'>"
);
}
// Toggle captions button
if(_inArray(config.controls, "captions")) {
html.push(
"<input class='sr-only' id='captions{id}' type='checkbox' data-player='captions'>",
"<label for='captions{id}'>",
"<svg class='icon-captions-on'><use xlink:href='#icon-captions-on'></use></svg>",
"<svg><use xlink:href='#icon-captions-off'></use></svg>",
"<span class='sr-only'>Toggle Captions</span>",
"</label>"
);
}
// Toggle fullscreen button
if(_inArray(config.controls, "fullscreen")) {
html.push(
"<button type='button' data-player='fullscreen'>",
"<svg class='icon-exit-fullscreen'><use xlink:href='#icon-exit-fullscreen'></use></svg>",
"<svg><use xlink:href='#icon-enter-fullscreen'></use></svg>",
"<span class='sr-only'>Toggle Fullscreen</span>",
"</button>"
);
}
// Close everything
html.push(
"</span>",
"</div>"
);
return html.join("");
}
// Debugging
function _log(text, error) {
if(config.debug && window.console) {
@ -156,8 +235,8 @@
function _browserSniff() {
var nAgt = navigator.userAgent,
name = navigator.appName,
fullVersion = ""+parseFloat(navigator.appVersion),
majorVersion = parseInt(navigator.appVersion,10),
fullVersion = "" + parseFloat(navigator.appVersion),
majorVersion = parseInt(navigator.appVersion, 10),
nameOffset,
verOffset,
ix;
@ -170,46 +249,47 @@
// MSIE
else if ((verOffset=nAgt.indexOf("MSIE")) !== -1) {
name = "IE";
fullVersion = nAgt.substring(verOffset+5);
fullVersion = nAgt.substring(verOffset + 5);
}
// Chrome
else if ((verOffset=nAgt.indexOf("Chrome")) !== -1) {
name = "Chrome";
fullVersion = nAgt.substring(verOffset+7);
fullVersion = nAgt.substring(verOffset + 7);
}
// Safari
else if ((verOffset=nAgt.indexOf("Safari")) !== -1) {
name = "Safari";
fullVersion = nAgt.substring(verOffset+7);
fullVersion = nAgt.substring(verOffset + 7);
if ((verOffset=nAgt.indexOf("Version")) !== -1) {
fullVersion = nAgt.substring(verOffset+8);
fullVersion = nAgt.substring(verOffset + 8);
}
}
// Firefox
else if ((verOffset=nAgt.indexOf("Firefox")) !== -1) {
name = "Firefox";
fullVersion = nAgt.substring(verOffset+8);
fullVersion = nAgt.substring(verOffset + 8);
}
// In most other browsers, "name/version" is at the end of userAgent
else if ( (nameOffset=nAgt.lastIndexOf(" ")+1) < (verOffset=nAgt.lastIndexOf("/")) ) {
else if ((nameOffset=nAgt.lastIndexOf(" ") + 1) < (verOffset=nAgt.lastIndexOf("/"))) {
name = nAgt.substring(nameOffset,verOffset);
fullVersion = nAgt.substring(verOffset+1);
if (name.toLowerCase()==name.toUpperCase()) {
fullVersion = nAgt.substring(verOffset + 1);
if (name.toLowerCase() == name.toUpperCase()) {
name = navigator.appName;
}
}
// Trim the fullVersion string at semicolon/space if present
if ((ix=fullVersion.indexOf(";")) !== -1) {
fullVersion=fullVersion.substring(0,ix);
if ((ix = fullVersion.indexOf(";")) !== -1) {
fullVersion = fullVersion.substring(0, ix);
}
if ((ix=fullVersion.indexOf(" ")) !== -1) {
fullVersion=fullVersion.substring(0,ix);
if ((ix = fullVersion.indexOf(" ")) !== -1) {
fullVersion = fullVersion.substring(0, ix);
}
// Get major version
majorVersion = parseInt(""+fullVersion,10);
majorVersion = parseInt("" + fullVersion, 10);
if (isNaN(majorVersion)) {
fullVersion = ""+parseFloat(navigator.appVersion);
majorVersion = parseInt(navigator.appVersion,10);
fullVersion = "" + parseFloat(navigator.appVersion);
majorVersion = parseInt(navigator.appVersion, 10);
}
// Return data
@ -249,6 +329,11 @@
// If we got this far, we're stuffed
return false;
}
// Element exists in an array
function _inArray(haystack, needle) {
return Array.prototype.indexOf && (haystack.indexOf(needle) != -1);
}
// Replace all
function _replaceAll(string, find, replace) {
@ -257,7 +342,7 @@
// Wrap an element
function _wrap(elements, wrapper) {
// Convert `elms` to an array, if necessary.
// Convert `elements` to an array, if necessary.
if (!elements.length) {
elements = [elements];
}
@ -265,16 +350,16 @@
// Loops backwards to prevent having to clone the wrapper on the
// first element (see `child` below).
for (var i = elements.length - 1; i >= 0; i--) {
var child = (i > 0) ? wrapper.cloneNode(true) : wrapper;
var el = elements[i];
var child = (i > 0) ? wrapper.cloneNode(true) : wrapper;
var element = elements[i];
// Cache the current parent and sibling.
var parent = el.parentNode;
var sibling = el.nextSibling;
var parent = element.parentNode;
var sibling = element.nextSibling;
// Wrap the element (is automatically removed from its current
// parent).
child.appendChild(el);
child.appendChild(element);
// If the element had a sibling, insert the wrapper before
// the sibling to maintain the HTML structure; otherwise, just
@ -339,12 +424,16 @@
// Bind event
function _on(element, events, callback) {
_toggleHandler(element, events, callback, true);
if(element) {
_toggleHandler(element, events, callback, true);
}
}
// Unbind event
function _off(element, events, callback) {
_toggleHandler(element, events, callback, false);
if(element) {
_toggleHandler(element, events, callback, false);
}
}
// Trigger event
@ -432,7 +521,7 @@
}
}
// Safari doesn't support the ALLOW_KEYBOARD_INPUT flag so set it to not supported
// Safari doesn't support the ALLOW_KEYBOARD_INPUT flag (for security) so set it to not supported
// https://bugs.webkit.org/show_bug.cgi?id=121496
if(fullscreen.prefix === "webkit" && !!navigator.userAgent.match(/Version\/[\d\.]+.*Safari/)) {
fullscreen.supportsFullScreen = false;
@ -444,19 +533,18 @@
// Sometimes the prefix is "ms", sometimes "MS" to keep you on your toes
fullscreen.fullScreenEventName = (fullscreen.prefix == "ms" ? "MSFullscreenChange" : fullscreen.prefix + "fullscreenchange");
fullscreen.isFullScreen = function() {
fullscreen.isFullScreen = function(element) {
if(typeof element == "undefined") {
element = document;
}
switch (this.prefix) {
case "":
return document.fullScreen;
case "webkit":
return document.webkitIsFullScreen;
case "ms":
// Docs say document.msFullScreenElement returns undefined
// if no element is full screem but it returns null, cheers
// https://msdn.microsoft.com/en-us/library/ie/dn265028%28v=vs.85%29.aspx
return (document.msFullscreenElement !== null);
return document.fullscreenElement == element;
case "moz":
return document.mozFullScreenElement == element;
default:
return document[this.prefix + "FullScreen"];
return document[this.prefix + "FullscreenElement"] == element;
}
};
fullscreen.requestFullScreen = function(element) {
@ -533,6 +621,11 @@
// Display captions container and button (for initialization)
function _showCaptions() {
// If there's no caption toggle, bail
if(!player.buttons.captions) {
return;
}
_toggleClass(player.container, config.classes.captions.enabled, true);
if (config.captions.defaultActive) {
@ -589,18 +682,22 @@
// Insert controls
function _injectControls() {
// Make a copy of the html
var html = config.html;
// Insert custom video controls
_log("Injecting custom controls.");
// Use specified html
// Need to do a default?
var html = config.html;
// If no controls are specified, create default
if(!html) {
html = _buildControls();
}
// Replace seek time instances
html = _replaceAll(html, "{seektime}", config.seekTime);
// Replace all id references
html = _replaceAll(html, "{id}", player.random);
// Replace all id references with random numbers
html = _replaceAll(html, "{id}", Math.floor(Math.random() * (10000)));
// Inject into the container
player.container.insertAdjacentHTML("beforeend", html);
@ -645,18 +742,19 @@
// Progress - Buffering
player.progress.buffer = {};
player.progress.buffer.bar = _getElement(config.selectors.progress.buffer);
player.progress.buffer.text = player.progress.buffer.bar.getElementsByTagName("span")[0];
player.progress.buffer.text = player.progress.buffer.bar && player.progress.buffer.bar.getElementsByTagName("span")[0];
// Progress - Played
player.progress.played = {};
player.progress.played.bar = _getElement(config.selectors.progress.played);
player.progress.played.text = player.progress.played.bar.getElementsByTagName("span")[0];
player.progress.played.text = player.progress.played.bar && player.progress.played.bar.getElementsByTagName("span")[0];
// Volume
player.volume = _getElement(config.selectors.buttons.volume);
// Timing
player.duration = _getElement(config.selectors.duration);
player.currentTime = _getElement(config.selectors.currentTime);
player.seekTime = _getElements(config.selectors.seekTime);
return true;
@ -664,12 +762,21 @@
catch(e) {
_log("It looks like there's a problem with your controls html. Bailing.", true);
// Restore native video controls
player.media.setAttribute("controls", "");
return false;
}
}
// Setup aria attributes
function _setupAria() {
// If there's no play button, bail
if(!player.buttons.play) {
return;
}
// Find the current text
var label = player.buttons.play.innerText || "Play";
// If there's a media title set, use that for the label
@ -932,7 +1039,7 @@
targetTime = input;
}
// Event
else if (typeof input === "object" && (input.type === "change" || input.type === "input")) {
else if (typeof input === "object" && (input.type === "input" || input.type === "change")) {
// It's the seek slider
// Seek to the selected time
targetTime = ((input.target.value / input.target.max) * player.media.duration);
@ -973,12 +1080,12 @@
// If it's a fullscreen change event, it's probably a native close
if(event && event.type === fullscreen.fullScreenEventName) {
config.fullscreen.active = fullscreen.isFullScreen();
player.isFullscreen = fullscreen.isFullScreen(player.container);
}
// If there's native support, use it
else if(nativeSupport) {
// Request fullscreen
if(!fullscreen.isFullScreen()) {
if(!fullscreen.isFullScreen(player.container)) {
fullscreen.requestFullScreen(player.container);
}
// Bail from fullscreen
@ -987,14 +1094,14 @@
}
// Check if we're actually full screen (it could fail)
config.fullscreen.active = fullscreen.isFullScreen();
player.isFullscreen = fullscreen.isFullScreen(player.container);
}
else {
// Otherwise, it's a simple toggle
config.fullscreen.active = !config.fullscreen.active;
player.isFullscreen = !player.isFullscreen;
// Bind/unbind escape key
if(config.fullscreen.active) {
if(player.isFullscreen) {
_on(document, "keyup", _handleEscapeFullscreen);
document.body.style.overflow = "hidden";
}
@ -1005,19 +1112,29 @@
}
// Set class hook
_toggleClass(player.container, config.classes.fullscreen.active, config.fullscreen.active);
_toggleClass(player.container, config.classes.fullscreen.active, player.isFullscreen);
// Remove hover class because mouseleave doesn't occur
if (player.isFullscreen) {
_toggleClass(player.controls, config.classes.hover, false);
}
}
// Bail from faux-fullscreen
function _handleEscapeFullscreen(event) {
// If it's a keypress and not escape, bail
if((event.which || event.charCode || event.keyCode) === 27 && config.fullscreen.active) {
if((event.which || event.charCode || event.keyCode) === 27 && player.isFullscreen) {
_toggleFullscreen();
}
}
// Set volume
function _setVolume(volume) {
// Bail if there's no volume element
if(!player.volume) {
return;
}
// Use default if needed
if(typeof volume === "undefined") {
if(config.storage.enabled && _storage().supported) {
@ -1045,7 +1162,7 @@
// Store the volume in storage
if(config.storage.enabled && _storage().supported) {
window.localStorage.plyr_volume = volume;
window.localStorage.setItem(config.storage.key, volume);
}
}
@ -1070,7 +1187,8 @@
// Toggle captions
function _toggleCaptions(show) {
if(!player.supported.full) {
// If there's no full support, or there's no caption toggle
if(!player.supported.full || !player.buttons.captions) {
return;
}
@ -1115,7 +1233,7 @@
value = _getPercentage(player.media.currentTime, player.media.duration);
// Set seek range value only if it's a "natural" time event
if(event.type == "timeupdate") {
if(event.type == "timeupdate" && player.buttons.seek) {
player.buttons.seek.value = value;
}
@ -1147,12 +1265,21 @@
}
// Set values
progress.value = value;
text.innerHTML = value;
if(progress) {
progress.value = value;
}
if(text) {
text.innerHTML = value;
}
}
// Update the displayed time
function _updateTimeDisplay(time) {
function _updateTimeDisplay(time, element) {
// Bail if there's no duration display
if(!element) {
return;
}
player.secs = parseInt(time % 60);
player.mins = parseInt((time / 60) % 60);
player.hours = parseInt(((time / 60) / 60) % 60);
@ -1165,20 +1292,28 @@
player.mins = ("0" + player.mins).slice(-2);
// Render
player.duration.innerHTML = (displayHours ? player.hours + ":" : "") + player.mins + ":" + player.secs;
element.innerHTML = (displayHours ? player.hours + ":" : "") + player.mins + ":" + player.secs;
}
// Show the duration on metadataloaded
function _displayDuration() {
if(player.media.paused) {
_updateTimeDisplay(player.media.duration || 0);
var duration = player.media.duration || 0;
// If there's only one time display, display duration there
if(!player.duration && config.displayDuration && player.media.paused) {
_updateTimeDisplay(duration, player.currentTime);
}
// If there's a duration element, update content
if(player.duration) {
_updateTimeDisplay(duration, player.duration);
}
}
// Handle time change event
function _timeUpdate(event) {
// Duration
_updateTimeDisplay(player.media.currentTime);
_updateTimeDisplay(player.media.currentTime, player.currentTime);
// Playing progress
_updateProgress(event);
@ -1265,6 +1400,9 @@
// Listen for events
function _listeners() {
// IE doesn't support input event, so we fallback to change
var inputEvent = (player.browser.name == "IE" ? "change" : "input");
// Play
_on(player.buttons.play, "click", function() {
_play();
@ -1286,9 +1424,11 @@
// Fast forward
_on(player.buttons.forward, "click", _forward);
// Get the HTML5 range input element and append audio volume adjustment on change/input
// IE10 doesn't support the "input" event so they have to wait for change
_on(player.volume, "change input", function() {
// Seek
_on(player.buttons.seek, inputEvent, _seek);
// Set volume
_on(player.volume, inputEvent, function() {
_setVolume(this.value);
});
@ -1312,12 +1452,7 @@
_on(player.media, "timeupdate", _seekManualCaptions);
// Display duration
if(config.displayDuration) {
_on(player.media, "loadedmetadata", _displayDuration);
}
// Seek
_on(player.buttons.seek, "change input", _seek);
_on(player.media, "loadedmetadata", _displayDuration);
// Captions
_on(player.buttons.captions, "change", function() {
@ -1357,14 +1492,14 @@
if(player.type === "video" && config.click) {
_on(player.videoContainer, "click", function() {
if(player.media.paused) {
_play();
_triggerEvent(player.buttons.play, "click");
}
else if(player.media.ended) {
_seek();
_play();
_triggerEvent(player.buttons.play, "click");
}
else {
_pause();
_triggerEvent(player.buttons.pause, "click");
}
});
}
@ -1373,7 +1508,7 @@
if(config.fullscreen.hideControls) {
_on(player.controls, "mouseenter mouseleave", function(event) {
_toggleClass(player.controls, config.classes.hover, (event.type === "mouseenter"));
})
});
}
}
@ -1404,9 +1539,6 @@
// Setup media
_setupMedia();
// Generate random number for id/for attribute values for controls
player.random = Math.floor(Math.random() * (10000));
// If there's full support
if(player.supported.full) {
// Inject custom controls
@ -1454,11 +1586,13 @@
rewind: _rewind,
forward: _forward,
seek: _seek,
source: _parseSource,
poster: _updatePoster,
setVolume: _setVolume,
toggleMute: _toggleMute,
toggleCaptions: _toggleCaptions,
source: _parseSource,
poster: _updatePoster,
toggleFullscreen: _toggleFullscreen,
isFullscreen: function() { return player.isFullscreen || false; },
support: function(mimeType) { return _supportMime(player, mimeType); }
}
}
@ -1500,7 +1634,7 @@
// Extend the default options with user specified
config = _extend(defaults, options);
// If enabled carry on
// Bail if disabled or no basic support
// You may want to disable certain UAs etc
if(!config.enabled || !api.supported().basic) {
return false;
@ -1522,13 +1656,16 @@
// Set plyr to false if setup failed
element.plyr = (Object.keys(instance).length ? instance : false);
// Callback
config.onSetup.apply(element.plyr);
}
// Add to return array
// Add to return array even if it's already setup
players.push(element.plyr);
}
return players;
}
}(this.plyr = this.plyr || {}));
}(this.plyr = this.plyr || {}));

View File

@ -9,6 +9,7 @@
@gray-dark: #343f4a;
@gray: #565d64;
@gray-light: #cbd0d3;
@off-white: #d6dadd;
// Font sizes
@font-size-small: 14px;
@ -259,6 +260,22 @@
font-size: @font-size-small;
.font-smoothing();
}
// Media duration hidden on small screens
.player-time + .player-time {
display: none;
@media (min-width: @bp-control-split) {
display: inline-block;
}
// Add a slash in before
&::before {
content: "\2044";
margin-right: @control-spacing;
color: darken(@control-color, 30%);
}
}
}
// Tooltips
@ -511,6 +528,17 @@
float: none;
}
// Audio specific styles
// Position the progress within the container
&-audio .player-controls {
padding-top: (@control-spacing * 2);
}
&-audio .player-progress {
bottom: auto;
top: 0;
background: @off-white;
}
// Full screen mode
&-fullscreen,
&.fullscreen-active {

View File

@ -9,6 +9,7 @@ $blue: #3498DB;
$gray-dark: #343f4a;
$gray: #565d64;
$gray-light: #cbd0d3;
$off-white: #d6dadd;
// Font sizes
$font-size-small: 14px;
@ -267,6 +268,22 @@ $bp-captions-large: 768px; // When captions jump to the larger font size
font-size: $font-size-small;
@include font-smoothing();
}
// Media duration hidden on small screens
.player-time + .player-time {
display: none;
@media (min-width: $bp-control-split) {
display: inline-block;
}
// Add a slash in before
&::before {
content: "\2044";
margin-right: $control-spacing;
color: darken($control-color, 30%);
}
}
}
// Tooltips
@ -278,7 +295,7 @@ $bp-captions-large: 768px; // When captions jump to the larger font size
margin-bottom: $tooltip-padding;
padding: $tooltip-padding ($tooltip-padding * 1.5);
opacity: 0;
opacity: 0;
background: $tooltip-bg;
border-radius: $tooltip-radius;
color: $tooltip-color;
@ -287,7 +304,7 @@ $bp-captions-large: 768px; // When captions jump to the larger font size
font-weight: 600;
transform: translate(-50%, ($tooltip-padding * 3));
transition: transform .2s .2s ease, opacity .2s .2s ease;
transition: transform .2s .2s ease, opacity .2s .2s ease;
&::after {
content: "";
@ -519,6 +536,17 @@ $bp-captions-large: 768px; // When captions jump to the larger font size
float: none;
}
// Audio specific styles
// Position the progress within the container
&-audio .player-controls {
padding-top: ($control-spacing * 2);
}
&-audio .player-progress {
bottom: auto;
top: 0;
background: $off-white;
}
// Full screen mode
&-fullscreen,
&.fullscreen-active {
@ -594,10 +622,4 @@ $bp-captions-large: 768px; // When captions jump to the larger font size
&.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;
}
}
}