Compare commits

...

27 Commits

Author SHA1 Message Date
a978348123 Version bump 2015-07-28 11:31:32 +10:00
00cf797c20 Restored !default and fullscreen logic in SASS 2015-07-28 11:29:49 +10:00
6b0f58dab2 Merge pull request #102 from ChristianPV/patch-1
Update plyr.scss - Make it work
2015-07-28 10:13:47 +10:00
04cf5dfda1 Update plyr.scss - Make it work
There were some things that needed to be changed for the sass preprocessor to work. There were some variables declared as @ that need to be declared with the right syntax: $. Also sass evaluation is eager, while less evaluation is _lazy_ that means in sass, mixins should be declared before they are use. I hope you can accept this change as it's currently not working. Thanks!
2015-07-27 13:04:02 -03:00
fe1989dea1 gulp 2015-07-25 22:06:13 +10:00
5d19b43888 Tooltip tweak 2015-07-25 21:35:12 +10:00
06ed345f29 YouTube quality tweak 2015-07-25 21:11:17 +10:00
e0cd34c996 Fix for omitted kind attribute on <track> (fixes #88) 2015-07-25 20:45:11 +10:00
06641d5709 Docs 2015-07-25 20:29:54 +10:00
a0d2d5cd24 Minor tweaks 2015-07-25 20:29:19 +10:00
e9cdbfb8da Safari fix (fixes #96), YouTube tweaks, docs 2015-07-25 19:51:32 +10:00
df64fdac9e Tab focus and caption position fixes (fixes #61, fixes #92) 2015-07-25 18:30:47 +10:00
4dbbbd04cc Merge pull request #99 from ChristianPV/patch-1
Update bower.json - fix sass file name
2015-07-25 10:59:56 +10:00
c9c3ee9014 Update bower.json - fix sass file name
I installed this package with bower and encountered an error while checking for existence of main bower files. I changed the filename to the correct one. Please take a look. Thanks!
2015-07-24 11:51:16 -03:00
67191c2a75 Merge branch 'master' of github.com:selz/plyr 2015-07-22 11:36:35 +10:00
8ba4522b3e Docs 2015-07-22 11:36:24 +10:00
52eaf62b58 Update readme.md 2015-07-21 12:30:41 +10:00
8d43f412ac Docs CSS 2015-07-21 10:48:31 +10:00
e9ea90f527 Update readme.md 2015-07-21 10:38:48 +10:00
5dc0d84300 Version bump 2015-07-21 08:51:44 +10:00
ec8923ef08 Merge branch 'master' of github.com:selz/plyr 2015-07-21 08:51:25 +10:00
5a414572f9 Tooltip fix (Fixes #97) 2015-07-21 08:51:14 +10:00
7f40307b0a Update readme.md 2015-07-20 23:11:11 +10:00
a12485d10f Update readme.md 2015-07-20 23:09:57 +10:00
4695bbf483 Merge branch 'master' of github.com:selz/plyr 2015-07-20 23:04:13 +10:00
20ee77a55e Docs tweak 2015-07-20 23:03:24 +10:00
78a0ac8674 Update readme.md 2015-07-20 22:51:33 +10:00
20 changed files with 554 additions and 203 deletions

View File

@ -17,7 +17,7 @@
"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.scss",
"src/js/plyr.js" "src/js/plyr.js"
], ],
"ignore": [ "ignore": [
@ -30,4 +30,4 @@
"url": "git://github.com/selz/plyr.git" "url": "git://github.com/selz/plyr.git"
}, },
"license": "MIT" "license": "MIT"
} }

View File

@ -17,6 +17,7 @@
"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/src/js/lib/classlist.js",
"docs/dist/templates.js", "docs/dist/templates.js",
"docs/src/js/docs.js" "docs/src/js/docs.js"
] ]

View File

@ -1,7 +1,28 @@
# Changelog # Changelog
## v1.2.6
- SASS updates and fixes (cheers @ChristianPV)
## v1.2.5
- Fix for YouTube quality (let them decide quality)
## v1.2.4
- Fix for omitted kind attribute on <track> (fixes #88)
## v1.2.3
- Fix for YouTube on iPhone or unsupported browsers (fallback to YouTube native)
- Docs tidy up
- Fullscreen for Safari fix (Fixes #96)
## v1.2.2
- Fix for :focus keyboard vs mouse (Fixes #61)
- Fix for caption positioning in full screen (Fixes #92)
## v1.2.1
- Tooltip bug fix
## v1.2.0 ## v1.2.0
- Added YouTube support. - Added YouTube support
## v1.1.13 ## v1.1.13
- Added icon prefix option for when using default controls - Added icon prefix option for when using default controls

2
dist/plyr.css vendored

File diff suppressed because one or more lines are too long

2
dist/plyr.js vendored

File diff suppressed because one or more lines are too long

2
docs/dist/docs.css vendored

File diff suppressed because one or more lines are too long

2
docs/dist/docs.js vendored

File diff suppressed because one or more lines are too long

View File

@ -6,7 +6,7 @@
<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.2.0/docs.css"> <link rel="stylesheet" href="//cdn.plyr.io/1.2.6/docs.css">
</head> </head>
<body> <body>
<main> <main>

View File

@ -8,10 +8,10 @@
<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="https://cdn.plyr.io/1.2.0/plyr.css?1"> <link rel="stylesheet" href="https://cdn.plyr.io/1.2.6/plyr.css?2">
<!-- Docs styles --> <!-- Docs styles -->
<link rel="stylesheet" href="https://cdn.plyr.io/1.2.0/docs.css?2"> <link rel="stylesheet" href="https://cdn.plyr.io/1.2.6/docs.css?1">
</head> </head>
<body> <body>
<header> <header>
@ -92,18 +92,18 @@
a.send(); a.send();
a.onload = function(){ a.onload = function(){
var c = d.createElement("div"); var c = d.createElement("div");
c.style.display="none"; c.setAttribute("hidden", "");
c.innerHTML = a.responseText; c.innerHTML = a.responseText;
b.insertBefore(c, b.childNodes[0]); b.insertBefore(c, b.childNodes[0]);
} }
} }
})(document, "https://cdn.plyr.io/1.2.0/sprite.svg"); })(document, "https://cdn.plyr.io/1.2.6/sprite.svg");
</script> </script>
<!-- Plyr core script --> <!-- Plyr core script -->
<script src="https://cdn.plyr.io/1.2.0/plyr.js?1"></script> <script src="https://cdn.plyr.io/1.2.6/plyr.js?1"></script>
<!-- Docs script --> <!-- Docs script -->
<script src="https://cdn.plyr.io/1.2.0/docs.js?1"></script> <script src="https://cdn.plyr.io/1.2.6/docs.js?1"></script>
</body> </body>
</html> </html>

View File

@ -10,6 +10,7 @@ plyr.setup({
volume: 9, volume: 9,
title: "Video demo", title: "Video demo",
html: templates.controls.render({}), html: templates.controls.render({}),
tooltips: true,
captions: { captions: {
defaultActive: true defaultActive: true
}, },
@ -107,7 +108,7 @@ plyr.setup({
// Add star // Add star
function formatGitHubCount(count) { function formatGitHubCount(count) {
return "&bigstar; " + count; return "&#9733; " + count;
} }
// Check if it's in session storage first // Check if it's in session storage first

View File

@ -0,0 +1,237 @@
/*
* classList.js: Cross-browser full element.classList implementation.
* 1.1.20150312
*
* By Eli Grey, http://eligrey.com
* License: Dedicated to the public domain.
* See https://github.com/eligrey/classList.js/blob/master/LICENSE.md
*/
/*global self, document, DOMException */
/*! @source http://purl.eligrey.com/github/classList.js/blob/master/classList.js */
if ("document" in self) {
// Full polyfill for browsers with no classList support
if (!("classList" in document.createElement("_"))) {
(function (view) {
"use strict";
if (!('Element' in view)) return;
var
classListProp = "classList"
, protoProp = "prototype"
, elemCtrProto = view.Element[protoProp]
, objCtr = Object
, strTrim = String[protoProp].trim || function () {
return this.replace(/^\s+|\s+$/g, "");
}
, arrIndexOf = Array[protoProp].indexOf || function (item) {
var
i = 0
, len = this.length
;
for (; i < len; i++) {
if (i in this && this[i] === item) {
return i;
}
}
return -1;
}
// Vendors: please allow content code to instantiate DOMExceptions
, DOMEx = function (type, message) {
this.name = type;
this.code = DOMException[type];
this.message = message;
}
, checkTokenAndGetIndex = function (classList, token) {
if (token === "") {
throw new DOMEx(
"SYNTAX_ERR"
, "An invalid or illegal string was specified"
);
}
if (/\s/.test(token)) {
throw new DOMEx(
"INVALID_CHARACTER_ERR"
, "String contains an invalid character"
);
}
return arrIndexOf.call(classList, token);
}
, ClassList = function (elem) {
var
trimmedClasses = strTrim.call(elem.getAttribute("class") || "")
, classes = trimmedClasses ? trimmedClasses.split(/\s+/) : []
, i = 0
, len = classes.length
;
for (; i < len; i++) {
this.push(classes[i]);
}
this._updateClassName = function () {
elem.setAttribute("class", this.toString());
};
}
, classListProto = ClassList[protoProp] = []
, classListGetter = function () {
return new ClassList(this);
}
;
// Most DOMException implementations don't allow calling DOMException's toString()
// on non-DOMExceptions. Error's toString() is sufficient here.
DOMEx[protoProp] = Error[protoProp];
classListProto.item = function (i) {
return this[i] || null;
};
classListProto.contains = function (token) {
token += "";
return checkTokenAndGetIndex(this, token) !== -1;
};
classListProto.add = function () {
var
tokens = arguments
, i = 0
, l = tokens.length
, token
, updated = false
;
do {
token = tokens[i] + "";
if (checkTokenAndGetIndex(this, token) === -1) {
this.push(token);
updated = true;
}
}
while (++i < l);
if (updated) {
this._updateClassName();
}
};
classListProto.remove = function () {
var
tokens = arguments
, i = 0
, l = tokens.length
, token
, updated = false
, index
;
do {
token = tokens[i] + "";
index = checkTokenAndGetIndex(this, token);
while (index !== -1) {
this.splice(index, 1);
updated = true;
index = checkTokenAndGetIndex(this, token);
}
}
while (++i < l);
if (updated) {
this._updateClassName();
}
};
classListProto.toggle = function (token, force) {
token += "";
var
result = this.contains(token)
, method = result ?
force !== true && "remove"
:
force !== false && "add"
;
if (method) {
this[method](token);
}
if (force === true || force === false) {
return force;
} else {
return !result;
}
};
classListProto.toString = function () {
return this.join(" ");
};
if (objCtr.defineProperty) {
var classListPropDesc = {
get: classListGetter
, enumerable: true
, configurable: true
};
try {
objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
} catch (ex) { // IE 8 doesn't support enumerable:true
if (ex.number === -0x7FF5EC54) {
classListPropDesc.enumerable = false;
objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
}
}
} else if (objCtr[protoProp].__defineGetter__) {
elemCtrProto.__defineGetter__(classListProp, classListGetter);
}
}(self));
} else {
// There is full or partial native classList support, so just check if we need
// to normalize the add/remove and toggle APIs.
(function () {
"use strict";
var testElement = document.createElement("_");
testElement.classList.add("c1", "c2");
// Polyfill for IE 10/11 and Firefox <26, where classList.add and
// classList.remove exist but support only one argument at a time.
if (!testElement.classList.contains("c2")) {
var createMethod = function(method) {
var original = DOMTokenList.prototype[method];
DOMTokenList.prototype[method] = function(token) {
var i, len = arguments.length;
for (i = 0; i < len; i++) {
token = arguments[i];
original.call(this, token);
}
};
};
createMethod('add');
createMethod('remove');
}
testElement.classList.toggle("c3", false);
// Polyfill for IE 10 and Firefox <24, where classList.toggle does not
// support the second argument.
if (testElement.classList.contains("c3")) {
var _toggle = DOMTokenList.prototype.toggle;
DOMTokenList.prototype.toggle = function(token, force) {
if (1 in arguments && !this.contains(token) === !force) {
return force;
} else {
return _toggle.call(this, token);
}
};
}
testElement = null;
}());
}
}

View File

@ -8,17 +8,24 @@
box-sizing: border-box; box-sizing: border-box;
} }
// Hidden
[hidden] {
display: none;
}
// Base // Base
html { html {
height: 100%;
font-size: 100%; font-size: 100%;
background: linear-gradient(#fff, @body-background) fixed;
} }
body { body {
font-family: "Avenir", "Helvetica Neue", Helvetica, Arial, sans-serif; font-family: "Avenir", "Helvetica Neue", Helvetica, Arial, sans-serif;
background: @body-background;
line-height: 1.5; line-height: 1.5;
text-align: center; text-align: center;
color: @gray; color: @gray;
.font-smoothing(on); .font-smoothing(on);
padding: 0 (@padding-base / 2);
} }
// Header // Header

View File

@ -25,6 +25,7 @@ nav {
position: relative; position: relative;
margin: 0 auto @padding-base; margin: 0 auto @padding-base;
max-width: @example-width-video; max-width: @example-width-video;
white-space: nowrap;
&::before { &::before {
content: ""; content: "";
@ -101,7 +102,8 @@ nav {
} }
} }
.btn-primary { .btn-primary {
background: linear-gradient(@link-color, darken(@link-color, 3%)); background-image: linear-gradient(@link-color, darken(@link-color, 3%));
background-color: @link-color;
border-color: darken(@link-color, 10%); border-color: darken(@link-color, 10%);
box-shadow: 0 1px 1px rgba(0,0,0, .15); box-shadow: 0 1px 1px rgba(0,0,0, .15);
text-shadow: 0 1px 1px rgba(0,0,0, .1); text-shadow: 0 1px 1px rgba(0,0,0, .1);
@ -123,7 +125,7 @@ nav {
position: relative; position: relative;
margin-left: 6px; margin-left: 6px;
padding: ((@padding-base / 2) - 1px); padding: ((@padding-base / 2) - 1px);
background: @body-background; background: #fff;
border: 1px solid @gray-light; border: 1px solid @gray-light;
&::before { &::before {

View File

@ -22,9 +22,22 @@
overflow: hidden; overflow: hidden;
} }
} }
// Base styles
.example-video .player { .example-video .player {
max-width: @example-width-video; max-width: @example-width-video;
video,
iframe {
border-radius: @border-radius-base;
}
iframe {
-webkit-mask-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAA5JREFUeNpiYGBgAAgwAAAEAAGbA+oJAAAAAElFTkSuQmCC);
}
}
// Style full supported player
.example-video .player-video,
.example-video .player-youtube {
video, video,
iframe { iframe {
border-radius: @border-radius-base @border-radius-base 0 0; border-radius: @border-radius-base @border-radius-base 0 0;
@ -35,5 +48,14 @@
&-fullscreen, &-fullscreen,
&.fullscreen-active { &.fullscreen-active {
max-width: none; max-width: none;
.player-controls,
video,
iframe {
border-radius: 0;
}
iframe {
-webkit-mask-image: none;
}
} }
} }

View File

@ -165,11 +165,6 @@ build.templates();
build.less(bundles.docs.less, "docs"); build.less(bundles.docs.less, "docs");
build.js(bundles.docs.js, "docs"); build.js(bundles.docs.js, "docs");
// Default gulp task
gulp.task("default", function(){
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);
@ -193,6 +188,11 @@ gulp.task("watch", function () {
gulp.watch(paths.docs.src.templates, ["js"]); gulp.watch(paths.docs.src.templates, ["js"]);
}); });
// Default gulp task
gulp.task("default", function(){
run("templates", tasks.js, tasks.less, "sprite", "watch");
});
// Publish a version to CDN and docs // Publish a version to CDN and docs
// -------------------------------------------- // --------------------------------------------

View File

@ -1,6 +1,6 @@
{ {
"name": "plyr", "name": "plyr",
"version": "1.2.0", "version": "1.2.6",
"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",

View File

@ -3,19 +3,19 @@ A simple, accessible HTML5 media player.
[Checkout the demo](http://plyr.io) [Checkout the demo](http://plyr.io)
[![Image of Plyr](https://cdn.plyr.io/static/plyr.png?2)](http://plyr.io) [![Image of Plyr](https://cdn.plyr.io/static/plyr.jpg)](http://plyr.io)
## Why? ## Why?
We wanted a lightweight, accessible and customisable media player that just supports *modern* browsers. Sure, there are many other players out there but we wanted to keep things simple, using the right elements for the job. We wanted a lightweight, accessible and customisable media player that just supports [*modern*](#browser-support) browsers. Sure, there are many other players out there but we wanted to keep things simple, using the right elements for the job.
## Features ## Features
- **Accessible** - full support for captions and screen readers. - **Accessible** - full support for captions and screen readers.
- **Lightweight** - just 6.4KB minified and gzipped. - **Lightweight** - just 7.5KB minified and gzipped.
- **[Customisable](#html)** - make the player look how you want with the markup you want. - **[Customisable](#html)** - 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.
- **Audio & Video** - support for both formats. - **Audio & Video** - support for both formats.
- **[Embed](#embed)** - support for YouTube (Vimeo soon). - **[Embedded Video](#embeds)** - support for YouTube (Vimeo soon).
- **[API](#api)** - toggle playback, volume, seeking, and more. - **[API](#api)** - toggle playback, volume, seeking, and more.
- **[Fullscreen](#fullscreen)** - supports native fullscreen with fallback to "full window" modes. - **[Fullscreen](#fullscreen)** - supports native fullscreen with fallback to "full window" modes.
- **No dependencies** - written in vanilla JavaScript, no jQuery required. - **No dependencies** - written in vanilla JavaScript, no jQuery required.
@ -39,7 +39,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.2.0/plyr.js` to `https://cdn.plyr.io/1.2.0/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.2.6/plyr.js` to `https://cdn.plyr.io/1.2.6/plyr.js`
### Bower ### Bower
If bower is your thang, you can grab Plyr using: If bower is your thang, you can grab Plyr using:
@ -59,11 +59,11 @@ More info is on [npm](https://www.npmjs.com/package/ember-cli-plyr) and [GitHub]
If you want to use our CDN, you can use the following: If you want to use our CDN, you can use the following:
```html ```html
<link rel="stylesheet" href="https://cdn.plyr.io/1.2.0/plyr.css"> <link rel="stylesheet" href="https://cdn.plyr.io/1.2.6/plyr.css">
<script src="https://cdn.plyr.io/1.2.0/plyr.js"></script> <script src="https://cdn.plyr.io/1.2.6/plyr.js"></script>
``` ```
You can also access the `sprite.svg` file at `https://cdn.plyr.io/1.2.0/sprite.svg`. You can also access the `sprite.svg` file at `https://cdn.plyr.io/1.2.6/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.
@ -360,6 +360,9 @@ Here's a list of the methods supported:
<strong>array</strong><br> <strong>array</strong><br>
<code>.source([{ src: "/path/to/video.webm", type: "video/webm", ...more attributes... }, { src: "/path/to/video.mp4", type: "video/mp4", ...more attributes... }])`</code><br> <code>.source([{ src: "/path/to/video.webm", type: "video/webm", ...more attributes... }, { src: "/path/to/video.mp4", type: "video/mp4", ...more attributes... }])`</code><br>
This will inject a child `source` element for every element in the array with the specified attributes. `src` is the only required attribute although adding `type` is recommended as it helps the browser decide which file to download and play. This will inject a child `source` element for every element in the array with the specified attributes. `src` is the only required attribute although adding `type` is recommended as it helps the browser decide which file to download and play.
<br><br>
<strong>YouTube</strong><br>
Currently this API method only accepts a YouTube ID when used with a YouTube player. I will add URL support soon, along with being able to swap between types (e.g. YouTube to Audio or Video and vice versa.)
</td> </td>
</tr> </tr>
<tr> <tr>

View File

@ -1,6 +1,6 @@
// ========================================================================== // ==========================================================================
// Plyr // Plyr
// plyr.js v1.2.0 // plyr.js v1.2.6
// https://github.com/selz/plyr // https://github.com/selz/plyr
// License: The MIT License (MIT) // License: The MIT License (MIT)
// ========================================================================== // ==========================================================================
@ -59,7 +59,7 @@
loading: "loading", loading: "loading",
tooltip: "player-tooltip", tooltip: "player-tooltip",
hidden: "sr-only", hidden: "sr-only",
hover: "hover", hover: "player-hover",
captions: { captions: {
enabled: "captions-enabled", enabled: "captions-enabled",
active: "captions-active" active: "captions-active"
@ -529,12 +529,12 @@
}, },
browserPrefixes = "webkit moz o ms khtml".split(" "); browserPrefixes = "webkit moz o ms khtml".split(" ");
// check for native support // Check for native support
if (typeof document.cancelFullScreen != "undefined") { if (typeof document.cancelFullScreen != "undefined") {
fullscreen.supportsFullScreen = true; fullscreen.supportsFullScreen = true;
} }
else { else {
// check for fullscreen support by vendor prefix // Check for fullscreen support by vendor prefix
for (var i = 0, il = browserPrefixes.length; i < il; i++ ) { for (var i = 0, il = browserPrefixes.length; i < il; i++ ) {
fullscreen.prefix = browserPrefixes[i]; fullscreen.prefix = browserPrefixes[i];
@ -551,12 +551,6 @@
} }
} }
// 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;
}
// Update methods to do something useful // Update methods to do something useful
if (fullscreen.supportsFullScreen) { if (fullscreen.supportsFullScreen) {
// Yet again Microsoft awesomeness, // Yet again Microsoft awesomeness,
@ -565,9 +559,8 @@
fullscreen.isFullScreen = function(element) { fullscreen.isFullScreen = function(element) {
if(typeof element == "undefined") { if(typeof element == "undefined") {
element = document; element = document.body;
} }
switch (this.prefix) { switch (this.prefix) {
case "": case "":
return document.fullscreenElement == element; return document.fullscreenElement == element;
@ -578,7 +571,10 @@
} }
}; };
fullscreen.requestFullScreen = function(element) { fullscreen.requestFullScreen = function(element) {
return (this.prefix === "") ? element.requestFullScreen() : element[this.prefix + (this.prefix == "ms" ? "RequestFullscreen" : "RequestFullScreen")](this.prefix === "webkit" ? element.ALLOW_KEYBOARD_INPUT : null); if(typeof element == "undefined") {
element = document.body;
}
return (this.prefix === "") ? element.requestFullScreen() : element[this.prefix + (this.prefix == "ms" ? "RequestFullscreen" : "RequestFullScreen")]();
}; };
fullscreen.cancelFullScreen = function() { fullscreen.cancelFullScreen = function() {
return (this.prefix === "") ? document.cancelFullScreen() : document[this.prefix + (this.prefix == "ms" ? "ExitFullscreen" : "CancelFullScreen")](); return (this.prefix === "") ? document.cancelFullScreen() : document[this.prefix + (this.prefix == "ms" ? "ExitFullscreen" : "CancelFullScreen")]();
@ -852,11 +848,11 @@
// Cache the container // Cache the container
player.videoContainer = wrapper; player.videoContainer = wrapper;
} }
}
// YouTube // YouTube
if(player.type == "youtube") { if(player.type == "youtube") {
_setupYouTube(player.media.getAttribute("data-video-id")); _setupYouTube(player.media.getAttribute("data-video-id"));
}
} }
// Autoplay // Autoplay
@ -910,8 +906,7 @@
videoId: id, videoId: id,
playerVars: { playerVars: {
autoplay: 0, autoplay: 0,
controls: 0, controls: (player.supported.full ? 0 : 1),
vq: "hd720",
rel: 0, rel: 0,
showinfo: 0, showinfo: 0,
iv_load_policy: 3, iv_load_policy: 3,
@ -953,14 +948,16 @@
} }
}, 200); }, 200);
// Only setup controls once if(player.supported.full) {
if(!player.container.querySelectorAll(config.selectors.controls).length) { // Only setup controls once
_setupInterface(); if(!player.container.querySelectorAll(config.selectors.controls).length) {
} _setupInterface();
}
// Display duration if available // Display duration if available
if(config.displayDuration) { if(config.displayDuration) {
_displayDuration(); _displayDuration();
}
} }
}, },
onStateChange: function(event) { onStateChange: function(event) {
@ -1030,8 +1027,8 @@
for (var i = 0; i < children.length; i++) { for (var i = 0; i < children.length; i++) {
if (children[i].nodeName.toLowerCase() === "track") { if (children[i].nodeName.toLowerCase() === "track") {
kind = children[i].getAttribute("kind"); kind = children[i].kind;
if (kind === "captions") { if (kind === "captions" || kind === "subtitles") {
captionSrc = children[i].getAttribute("src"); captionSrc = children[i].getAttribute("src");
} }
} }
@ -1083,7 +1080,7 @@
for (var y=0; y < tracks.length; y++) { for (var y=0; y < tracks.length; y++) {
var track = tracks[y]; var track = tracks[y];
if (track.kind === "captions") { if (track.kind === "captions" || track.kind === "subtitles") {
_on(track, "cuechange", function() { _on(track, "cuechange", function() {
// Clear container // Clear container
player.captionsContainer.innerHTML = ""; player.captionsContainer.innerHTML = "";
@ -1322,7 +1319,7 @@
// Show the player controls // Show the player controls
function _showControls() { function _showControls() {
// Set shown class // Set shown class
_toggleClass(player.controls, config.classes.hover, true); _toggleClass(player.container, config.classes.hover, true);
// Clear timer every movement // Clear timer every movement
window.clearTimeout(hoverTimer); window.clearTimeout(hoverTimer);
@ -1330,7 +1327,7 @@
// If the mouse is not over the controls, set a timeout to hide them // If the mouse is not over the controls, set a timeout to hide them
if(!isMouseOver) { if(!isMouseOver) {
hoverTimer = window.setTimeout(function() { hoverTimer = window.setTimeout(function() {
_toggleClass(player.controls, config.classes.hover, false); _toggleClass(player.container, config.classes.hover, false);
}, 2000); }, 2000);
} }
} }
@ -1675,6 +1672,34 @@
// IE doesn't support input event, so we fallback to change // IE doesn't support input event, so we fallback to change
var inputEvent = (player.browser.name == "IE" ? "change" : "input"); var inputEvent = (player.browser.name == "IE" ? "change" : "input");
// Detect tab focus
function checkFocus() {
var focused = document.activeElement;
if (!focused || focused == document.body) {
focused = null;
}
else if (document.querySelector){
focused = document.querySelector(":focus");
}
for (var button in player.buttons) {
var element = player.buttons[button];
_toggleClass(element, "tab-focus", (element === focused));
}
}
_on(window, "keyup", function(event) {
var code = (event.keyCode ? event.keyCode : event.which);
if(code == 9) { checkFocus(); }
});
for (var button in player.buttons) {
var element = player.buttons[button];
_on(element, "blur", function() {
_toggleClass(element, "tab-focus", false);
});
}
// Play // Play
_on(player.buttons.play, "click", function() { _on(player.buttons.play, "click", function() {
_play(); _play();
@ -1956,7 +1981,7 @@
case "youtube": case "youtube":
basic = true; basic = true;
full = !oldIE; full = (!oldIE && !iPhone);
break; break;
default: default:

View File

@ -28,7 +28,8 @@
// Tooltips // Tooltips
@tooltip-bg: @controls-bg; @tooltip-bg: @controls-bg;
@tooltip-color: #fff; @tooltip-border-color: @off-white;
@tooltip-color: @control-color;
@tooltip-padding: @control-spacing; @tooltip-padding: @control-spacing;
@tooltip-arrow-size: 5px; @tooltip-arrow-size: 5px;
@tooltip-radius: 3px; @tooltip-radius: 3px;
@ -325,37 +326,40 @@
opacity: 0; opacity: 0;
background: @tooltip-bg; background: @tooltip-bg;
border: 1px solid @tooltip-border-color;
border-radius: @tooltip-radius; border-radius: @tooltip-radius;
color: @tooltip-color; color: @tooltip-color;
font-size: @font-size-small; font-size: @font-size-small;
line-height: 1.5; line-height: 1.5;
font-weight: 600; font-weight: 600;
transform: translate(-50%, (@tooltip-padding * 3)); transform: translate(-50%, (@tooltip-padding * 3)) scale(0);
transition: transform .2s .2s ease, opacity .2s .2s ease; transform-origin: 50% 100%;
transition: transform .2s .1s ease, opacity .2s .1s ease;
// Arrow
&::after { &::after {
content: ""; content: "";
display: block;
position: absolute; position: absolute;
z-index: 1;
top: 100%;
left: 50%; left: 50%;
bottom: -@tooltip-arrow-size; display: block;
margin-left: -@tooltip-arrow-size; width: 10px;
width: 0; height: 10px;
height: 0; background: @tooltip-bg;
transition: inherit; transform: translate(-50%, -50%) rotate(45deg) translateY(1px);
border-style: solid; border: 1px solid @tooltip-border-color;
border-width: @tooltip-arrow-size @tooltip-arrow-size 0 @tooltip-arrow-size; border-width: 0 1px 1px 0;
border-color: @controls-bg transparent transparent;
} }
} }
label:hover .player-tooltip, label:hover .player-tooltip,
input:focus + label .player-tooltip, input.tab-focus:focus + label .player-tooltip,
button:hover .player-tooltip, button:hover .player-tooltip,
button:focus .player-tooltip { button.tab-focus:focus .player-tooltip {
visibility: visible; visibility: visible;
opacity: 1; opacity: 1;
transform: translate(-50%, 0); transform: translate(-50%, 0) scale(1);
} }
label:hover .player-tooltip, label:hover .player-tooltip,
button:hover .player-tooltip { button:hover .player-tooltip {
@ -490,7 +494,7 @@
// Volume control // Volume control
// <input[type='range']> element // <input[type='range']> element
// Specificity is for bootstrap compatibility // Specificity is for bootstrap compatibility
&-volume[type=range] { &-volume[type="range"] {
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
-webkit-appearance: none; -webkit-appearance: none;
@ -595,16 +599,7 @@
.player-video-wrapper { .player-video-wrapper {
height: 100%; height: 100%;
width: 100%; width: 100%;
}
.player-captions {
top: auto;
bottom: 90px;
@media (min-width: @bp-control-split) {
bottom: 60px;
}
}
}
.player-controls { .player-controls {
position: absolute; position: absolute;
bottom: 0; bottom: 0;
@ -613,13 +608,29 @@
} }
// Hide controls when playing in full screen // Hide controls when playing in full screen
&.fullscreen-hide-controls.playing .player-controls { &.fullscreen-hide-controls.playing {
transform: translateY(100%) translateY(@control-spacing / 2); .player-controls {
transition: transform .3s .2s ease; transform: translateY(100%) translateY(@control-spacing / 2);
transition: transform .3s .2s ease;
&.hover { }
&.player-hover .player-controls {
transform: translateY(0); transform: translateY(0);
} }
.player-captions {
bottom: (@control-spacing / 2);
transition: bottom .3s .2s ease;
}
}
// Captions
.player-captions,
&.fullscreen-hide-controls.playing.player-hover .player-captions {
top: auto;
bottom: 90px;
@media (min-width: @bp-control-split) {
bottom: 60px;
}
} }
} }

View File

@ -7,50 +7,69 @@
// ------------------------------- // -------------------------------
// Colors // Colors
$blue: #3498DB !default; $blue: #3498DB !default;
$gray-dark: #343F4A !default; $gray-dark: #343F4A !default;
$gray: #565D64 !default; $gray: #565D64 !default;
$gray-light: #6B7D86 !default; $gray-light: #6B7D86 !default;
$gray-lighter: #CBD0D3 !default; $gray-lighter: #CBD0D3 !default;
$off-white: #D6DADD !default; $off-white: #D6DADD !default;
// Font sizes // Font sizes
$font-size-small: 14px !default; $font-size-small: 14px !default;
$font-size-base: 16px !default; $font-size-base: 16px !default;
$font-size-large: ceil(($font-size-base * 1.5)) !default; $font-size-large: ceil(($font-size-base * 1.5)) !default;
// Controls // Controls
$control-spacing: 10px !default; $control-spacing: 10px !default;
$controls-bg: #fff !default; $controls-bg: #fff !default;
$control-bg-hover: @blue !default; $control-bg-hover: $blue !default; !default
.contrast-control-color($controls-bg);
.contrast-control-color-hover($control-bg-hover); // Contrast
@mixin contrast-control-color($color: "") {
$control-color: null !global;
@if lightness($color) >= 65% {
$control-color: $gray-light;
} @else if(lightness($color) < 65%) {
$control-color: $gray-lighter;
}
}
@mixin contrast-control-color-hover($color: "") {
$control-color-hover: null !global;
@if lightness($color) >= 65% {
$control-color-hover: $gray;
} @else if lightness($color) < 65% {
$control-color-hover: #fff;
}
}
@include contrast-control-color($controls-bg);
@include contrast-control-color-hover($control-bg-hover);
// Tooltips // Tooltips
$tooltip-bg: $controls-bg !default; $tooltip-bg: $controls-bg !default;
$tooltip-color: #fff !default; $tooltip-color: $control-color !default;
$tooltip-padding: $control-spacing !default; $tooltip-padding: $control-spacing !default;
$tooltip-arrow-size: 5px !default; $tooltip-arrow-size: 5px !default;
$tooltip-radius: 3px !default; $tooltip-radius: 3px !default;
// Progress // Progress
$progress-bg: rgba(red($gray), green($gray), blue($gray), .2) !default; $progress-bg: rgba(red($gray), green($gray), blue($gray), .2) !default;
$progress-playing-bg: $blue !default; $progress-playing-bg: $blue !default;
$progress-buffered-bg: rgba(red($gray), green($gray), blue($gray), .25) !default; $progress-buffered-bg: rgba(red($gray), green($gray), blue($gray), .25) !default;
$progress-loading-size: 40px !default; $progress-loading-size: 40px !default
$progress-loading-bg: rgba(0,0,0, .15) !default; $progress-loading-bg: rgba(0,0,0, .15) !default;
// Volume // Volume
$volume-track-height: 6px !default; $volume-track-height: 6px !default;
$volume-track-bg: darken($controls-bg, 10%) !default; $volume-track-bg: darken($controls-bg, 10%) !default;
$volume-thumb-height: ($volume-track-height * 2) !default; $volume-thumb-height: ($volume-track-height * 2) !default;
$volume-thumb-width: ($volume-track-height * 2) !default; $volume-thumb-width: ($volume-track-height * 2) !default;
$volume-thumb-bg: $control-color !default; $volume-thumb-bg: $control-color !default;
$volume-thumb-bg-focus: $control-bg-hover !default; $volume-thumb-bg-focus: $control-bg-hover !default;
// Breakpoints // Breakpoints
$bp-control-split: 560px !default; // When controls split into left/right $bp-control-split: 560px !default; // When controls split into left/right
$bp-captions-large: 768px !default; // When captions jump to the larger font size $bp-captions-large: 768px !default; // When captions jump to the larger font size
// Animation // Animation
// --------------------------------------- // ---------------------------------------
@ -59,45 +78,24 @@ $bp-captions-large: 768px !default; // When captions jump to the larger fo
to { background-position: $progress-loading-size 0; } to { background-position: $progress-loading-size 0; }
} }
// Mixins
// -------------------------------
// Contrast
@mixin contrast-control-color($color: "") {
@if (lightness($color) >= 65%) {
$control-color: $gray-light;
}
@else if(lightness(@color) < 65%) {
$control-color: $gray-lighter;
}
}
@mixin contrast-control-color-hover($color: "") {
@if (lightness($color) >= 65%) {
$control-color-hover: $gray;
}
@else if (lightness($color) < 65%) {
$control-color-hover: #fff;
}
}
// Font smoothing // Font smoothing
@mixin font-smoothing($mode: on) @mixin font-smoothing($mode: on)
{ {
@if ($mode == 'on') { @if ($mode == 'on') {
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
} }
@else if ($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;
} }
} }
// 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; }
} }
@ -108,7 +106,7 @@ $bp-captions-large: 768px !default; // When captions jump to the larger fo
outline-offset: 0; outline-offset: 0;
} }
// Range mixins // <input type="range"> styling
@mixin volume-thumb() @mixin volume-thumb()
{ {
height: $volume-thumb-height; height: $volume-thumb-height;
@ -153,7 +151,7 @@ $bp-captions-large: 768px !default; // When captions jump to the larger fo
// Styles // Styles
// ------------------------------- // -------------------------------
// Base // Base
.player { .player {
position: relative; position: relative;
max-width: 100%; max-width: 100%;
@ -164,8 +162,8 @@ $bp-captions-large: 768px !default; // When captions jump to the larger fo
&, &,
*, *,
*::after, *::after,
*::before { *::before {
box-sizing: border-box; box-sizing: border-box;
} }
// For video // For video
@ -179,6 +177,21 @@ $bp-captions-large: 768px !default; // When captions jump to the larger fo
vertical-align: middle; vertical-align: middle;
} }
// For embeds
&-video-embed {
padding-bottom: 56.25%; /* 16:9 */
height: 0;
iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border: 0;
}
}
// Captions // Captions
&-captions { &-captions {
display: none; display: none;
@ -191,10 +204,10 @@ $bp-captions-large: 768px !default; // When captions jump to the larger fo
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();
@ -232,15 +245,15 @@ $bp-captions-large: 768px !default; // When captions jump to the larger fo
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;
background .3s ease, color .3s ease, opacity .3s ease; transition: background .3s ease, color .3s ease, opacity .3s ease;
border-radius: 3px; border-radius: 3px;
cursor: pointer; cursor: pointer;
@ -256,7 +269,7 @@ $bp-captions-large: 768px !default; // When captions jump to the larger fo
.inverted:checked + label { .inverted:checked + label {
opacity: .5; opacity: .5;
} }
button, button,
.inverted + label, .inverted + label,
input:checked + label { input:checked + label {
color: $control-color; color: $control-color;
@ -267,7 +280,7 @@ $bp-captions-large: 768px !default; // When captions jump to the larger fo
background: transparent; background: transparent;
overflow: hidden; overflow: hidden;
} }
// Specificity for overriding .inverted // Specificity for overriding .inverted
button:focus, button:focus,
button:hover, button:hover,
@ -329,8 +342,9 @@ $bp-captions-large: 768px !default; // When captions jump to the larger fo
line-height: 1.5; line-height: 1.5;
font-weight: 600; font-weight: 600;
transform: translate(-50%, ($tooltip-padding * 3)); transform: translate(-50%, ($tooltip-padding * 3)) scale(0);
transition: transform .2s .2s ease, opacity .2s .2s ease; transform-origin: 50% 100%;
transition: transform .2s .1s ease, opacity .2s .1s ease;
&::after { &::after {
content: ""; content: "";
@ -348,12 +362,12 @@ $bp-captions-large: 768px !default; // When captions jump to the larger fo
} }
} }
label:hover .player-tooltip, label:hover .player-tooltip,
input:focus + label .player-tooltip, input:focus + label .player-tooltip,
button:hover .player-tooltip, button:hover .player-tooltip,
button:focus .player-tooltip { button:focus .player-tooltip {
visibility: visible; visibility: visible;
opacity: 1; opacity: 1;
transform: translate(-50%, 0); transform: translate(-50%, 0) scale(1);
} }
label:hover .player-tooltip, label:hover .player-tooltip,
button:hover .player-tooltip { button:hover .player-tooltip {
@ -389,7 +403,7 @@ $bp-captions-large: 768px !default; // When captions jump to the larger fo
background: transparent; background: transparent;
} }
&-buffer[value], &-buffer[value],
&-played[value] { &-played[value] {
&::-webkit-progress-bar { &::-webkit-progress-bar {
background: transparent; background: transparent;
} }
@ -435,7 +449,7 @@ $bp-captions-large: 768px !default; // When captions jump to the larger fo
-moz-appearance: none; -moz-appearance: none;
@include seek-thumb(); @include seek-thumb();
} }
// Microsoft // Microsoft
&::-ms-track { &::-ms-track {
color: transparent; color: transparent;
@ -465,15 +479,15 @@ $bp-captions-large: 768px !default; // When captions jump to the larger fo
background-repeat: repeat-x; background-repeat: repeat-x;
background-color: $progress-buffered-bg; background-color: $progress-buffered-bg;
background-image: linear-gradient( background-image: linear-gradient(
-45deg, -45deg,
$progress-loading-bg 25%, $progress-loading-bg 25%,
transparent 25%, transparent 25%,
transparent 50%, transparent 50%,
$progress-loading-bg 50%, $progress-loading-bg 50%,
$progress-loading-bg 75%, $progress-loading-bg 75%,
transparent 75%, transparent 75%,
transparent); transparent);
color: transparent; color: transparent;
} }
// States // States
@ -495,11 +509,11 @@ $bp-captions-large: 768px !default; // When captions jump to the larger fo
-moz-appearance: none; -moz-appearance: none;
width: 100px; width: 100px;
margin: 0 $control-spacing 0 0; margin: 0 $control-spacing 0 0;
padding: 0; padding: 0;
cursor: pointer; cursor: pointer;
background: transparent; background: transparent;
border: none; border: none;
// Webkit // Webkit
&::-webkit-slider-runnable-track { &::-webkit-slider-runnable-track {
@include volume-track(); @include volume-track();
@ -517,7 +531,7 @@ $bp-captions-large: 768px !default; // When captions jump to the larger fo
&::-moz-range-thumb { &::-moz-range-thumb {
@include volume-thumb(); @include volume-thumb();
} }
// Microsoft // Microsoft
&::-ms-track { &::-ms-track {
height: $volume-track-height; height: $volume-track-height;
@ -593,15 +607,6 @@ $bp-captions-large: 768px !default; // When captions jump to the larger fo
.player-video-wrapper { .player-video-wrapper {
height: 100%; height: 100%;
width: 100%; width: 100%;
.player-captions {
top: auto;
bottom: 90px;
@media (min-width: $bp-control-split) {
bottom: 60px;
}
}
} }
.player-controls { .player-controls {
position: absolute; position: absolute;
@ -611,13 +616,29 @@ $bp-captions-large: 768px !default; // When captions jump to the larger fo
} }
// Hide controls when playing in full screen // Hide controls when playing in full screen
&.fullscreen-hide-controls.playing .player-controls { &.fullscreen-hide-controls.playing {
transform: translateY(100%) translateY($control-spacing / 2); .player-controls {
transition: transform .3s .2s ease; transform: translateY(100%) translateY($control-spacing / 2);
transition: transform .3s .2s ease;
&.hover { }
&.player-hover .player-controls {
transform: translateY(0); transform: translateY(0);
} }
.player-captions {
bottom: ($control-spacing / 2);
transition: bottom .3s .2s ease;
}
}
// Captions
.player-captions,
&.fullscreen-hide-controls.playing.player-hover .player-captions {
top: auto;
bottom: 90px;
@media (min-width: $bp-control-split) {
bottom: 60px;
}
} }
} }
@ -645,4 +666,4 @@ $bp-captions-large: 768px !default; // When captions jump to the larger fo
&.fullscreen-enabled [data-player='fullscreen'] + label { &.fullscreen-enabled [data-player='fullscreen'] + label {
display: inline-block; display: inline-block;
} }
} }