Compare commits

...

45 Commits

Author SHA1 Message Date
Sam Potts a812650fea v3.2.4 2018-04-27 00:47:51 +10:00
Sam Potts fec7a77d6f v3.2.3 2018-04-25 20:02:36 +10:00
Sam Potts 971e261067 Fix for iOS 9 throwing error for name property in fullscreen API (fixes #908) 2018-04-25 19:59:22 +10:00
Sam Potts 27407ba021 v3.2.2 2018-04-25 19:46:39 +10:00
Sam Potts ef8e58ede4 Fix for hidden buffer and incorrect use of aria-hidden 2018-04-25 19:40:23 +10:00
Sam Potts f1b275aedc v3.2.1 2018-04-23 00:53:54 +10:00
Sam Potts b647af256c More a11y stuff and context menu fix 2018-04-23 00:01:19 +10:00
Sam Potts d2e9ed3467 Merge 2018-04-18 18:34:59 +10:00
Sam Potts 5b39986835 Merge branch 'master' of github.com:sampotts/plyr 2018-04-18 18:29:50 +10:00
Sam Potts a97b08e8ea ARIA and Vimeo fixes 2018-04-18 18:29:43 +10:00
Sam Potts 56d1be9447 Merge pull request #903 from friday/901
Show captions even if toggle button is omitted from controls
2018-04-18 08:49:05 +10:00
Sam Potts a241cb5215 Merge pull request #904 from friday/881
Fullscreen aria-pressed event listened fix for Chrome
2018-04-18 08:48:08 +10:00
Albin Larsson 042b1a8294 Fullscreen aria-pressed event listened fix for Chrome 2018-04-17 20:28:47 +02:00
Albin Larsson 6d79b8cd4c Don't require captions toggle button to be enabled in order to show captions 2018-04-17 18:59:19 +02:00
Sam Potts 88d766aeae v3.2.0 2018-04-17 23:54:38 +10:00
Sam Potts 119b471b84 More bug fixes 2018-04-17 23:51:23 +10:00
Sam Potts 7f079e0ec3 Fix for playing false positive (fixes #898) 2018-04-17 22:52:46 +10:00
Sam Potts 46fe3eecff Fixed bug for captions with no srclang and labels and improved logic (fixes #875) 2018-04-17 22:49:28 +10:00
Sam Potts 3061a701d5 PR merge 2018-04-14 14:58:09 +10:00
Sam Potts e45109e1d7 Merge branch 'master' of github.com:sampotts/plyr 2018-04-14 14:48:20 +10:00
Sam Potts e138e6d51e Merge pull request #895 from nicolasthy/patch-1
Fix IE10 split error
2018-04-14 14:47:57 +10:00
Nicolas Thiry aef1363b04 Fix IE10 with default captions.language 2018-04-13 14:44:05 +02:00
Nicolas Thiry 766dd03d81 Fix IE10 split error
On IE10, Plyr throws the error `Unable to get property 'split' of undefined or null reference`. This fixes the case when `window.navigator.language` is null and can't use the `split()` function.
2018-04-12 22:12:12 +02:00
Sam Potts ab393651ec Merge branch 'master' of github.com:sampotts/plyr 2018-04-11 23:44:44 +10:00
Sam Potts ffd265d0ae Merge pull request #888 from Antonio-Laguna/master
Safer check for active caption
2018-04-11 23:42:40 +10:00
Antonio Laguna 72155472dd Safer check for active caption 2018-04-11 15:39:12 +02:00
Sam Potts 9b7170834e Merge pull request #887 from danielsarin/use-i18n-for-normal-speed
Add i18n support for "Normal" value in speed options
2018-04-11 22:43:47 +10:00
Daniel Sarin 3e57a87bf7 Add i18n support for "Normal" value in speed options 2018-04-11 15:39:23 +03:00
Sam Potts a15d1c9f1c Merge pull request #886 from danielsarin/increate-menu-z-index
Increase menu container z-index to be higher than controls
2018-04-11 22:23:46 +10:00
Daniel Sarin a095a64f90 Increase menu container z-index to be higher than controls 2018-04-11 15:13:34 +03:00
Sam Potts 2374d6b1c4 Merge branch 'master' of github.com:sampotts/plyr 2018-04-11 21:52:36 +10:00
Sam Potts 5ed3ff9084 Restore paused state after seek 2018-04-11 21:52:31 +10:00
Sam Potts 385be55510 Merge pull request #874 from friday/873
Fixes issue leaving fullscreen in Chrome using button
2018-04-10 17:21:15 +10:00
Albin Larsson 3082d0d128 Fixes #873 Can't leave fullscreen in Chrome (using button) 2018-04-05 20:29:01 +02:00
Sam Potts f7e242f054 Merge pull request #871 from friday/867
Fix #867: Add custom property fallback
2018-04-05 09:19:46 +10:00
Sam Potts 2874505004 Merge pull request #868 from friday/null-no-controls
Fix string "null" being appended after the video if controls argument is empty.
2018-04-05 09:19:05 +10:00
Albin Larsson ed9e0c13d7 Fix #867: Add custom property fallback 2018-04-05 00:33:09 +02:00
Albin Larsson 10be94fa99 Fix 'null' being appended after the video if controls is empty array 2018-04-04 21:33:14 +02:00
Sam Potts ee79c46145 Merge branch 'master' of github.com:sampotts/plyr 2018-04-04 16:50:06 +10:00
Sam Potts 384010a2c0 Style fixes 2018-04-04 16:50:00 +10:00
Sam Potts 1e47019122 Merge pull request #863 from friday/data-plyr-config-no-options
Fix loading data-plyr-config when initiating Plyr without any options
2018-04-04 11:33:30 +10:00
Albin Larsson 536c65e82c Fix loading data-plyr-config when initiating Plyr without any options 2018-04-04 03:16:25 +02:00
Sam Potts cdf14932ec Changelog 2018-04-03 23:03:16 +10:00
Sam Potts 3b20dbd9fd v3.1.0 2018-04-03 22:57:34 +10:00
Sam Potts e4d975af00 Styling fixes 2018-04-03 22:56:19 +10:00
47 changed files with 2539 additions and 646 deletions
+1 -1
View File
@@ -11,7 +11,7 @@
"demo": {
"sass": {
"demo.css": "demo/src/sass/bundles/demo.scss",
"error.css": "demo/src/sass/bundles/error.csss"
"error.css": "demo/src/sass/bundles/error.scss"
},
"js": {
"demo.js": "demo/src/js/demo.js"
+46
View File
@@ -1,3 +1,49 @@
## v3.2.4
* Fix issue wher player never reports as ready if controls is empty array
* Fix issue where screen reader labels were removed from time displays
* Fix issue where custom controls placeholders were not populated
* Custom controls HTML example updated
* Fix for aria-label being set to the initial state on toggle buttons, overriding the inner labels
* Fix for hidden mute button on iOS (not functional for Vimeo due to API limitations) (fixes #656)
## v3.2.3
* Fix for iOS 9 throwing error for `name` property in fullscreen API (fixes #908)
## v3.2.2
* Fix for regression in 3.2.1 resulting in hidden buffer display (fixes #920)
* Cleaned up incorrect use of `aria-hidden` attribute
## v3.2.1
* Accessibility improvements for the controls (part of #905 fixes)
* Fix for context menu showing on YouTube (thanks Anthony Recenello in Slack)
* Vimeo fix for their API not returning the right duration until playback begins (fixes #891)
## v3.2.0
* Fullscreen fixes (thanks @friday)
* Menu fix for if speed not in config
* Menu z-index fix (thanks @danielsarin)
* i18n fix for missing "Normal" string (thanks @danielsarin)
* Safer check for active caption (thanks @Antonio-Laguna)
* Add custom property fallback (thanks @friday)
* Fixed bug for captions with no srclang and labels and improved logic (fixes #875)
* Fix for `playing` false positive (fixes #898)
* Fix for IE issue with navigator.language (thanks @nicolasthy) (fixes #893)
* Fix for Vimeo controls missing on iOS (thanks @verde-io) (fixes #807)
* Fix for double vimeo caption rendering (fixes #877)
## v3.1.0
* Styling fixes
## v3.1.0-beta.2
* YouTube playback speed fixes
## v3.1.0-beta.1
* HTML5 quality selection
+3 -1
View File
@@ -59,6 +59,7 @@ i18n: {
captions: 'Captions',
settings: 'Settings',
speed: 'Speed',
normal: 'Normal',
quality: 'Quality',
loop: 'Loop',
start: 'Start',
@@ -120,7 +121,8 @@ const controls = `
<progress class="plyr__progress--buffer" min="0" max="100" value="0">% buffered</progress>
<span role="tooltip" class="plyr__tooltip">00:00</span>
</div>
<div class="plyr__time">00:00</div>
<div class="plyr__time plyr__time--current" aria-label="Current time">00:00</div>
<div class="plyr__time plyr__time--duration" aria-label="Duration">00:00</div>
<button type="button" class="plyr__control" aria-pressed="false" aria-label="Mute" data-plyr="mute">
<svg class="icon--pressed" role="presentation"><use xlink:href="#plyr-muted"></use></svg>
<svg class="icon--not-pressed" role="presentation"><use xlink:href="#plyr-volume"></use></svg>
+1 -1
View File
File diff suppressed because one or more lines are too long
+53 -20
View File
@@ -245,7 +245,13 @@ function objectFrozen(obj) {
}
function truncate(str, max) {
return !max || str.length <= max ? str : str.substr(0, max) + '\u2026';
if (typeof max !== 'number') {
throw new Error('2nd argument to `truncate` function should be a number');
}
if (typeof str !== 'string' || max === 0) {
return str;
}
return str.length <= max ? str : str.substr(0, max) + '\u2026';
}
/**
@@ -544,10 +550,9 @@ function jsonSize(value) {
}
function serializeValue(value) {
var maxLength = 40;
if (typeof value === 'string') {
return value.length <= maxLength ? value : value.substr(0, maxLength - 1) + '\u2026';
var maxLength = 40;
return truncate(value, maxLength);
} else if (
typeof value === 'number' ||
typeof value === 'boolean' ||
@@ -1777,7 +1782,7 @@ Raven.prototype = {
// webpack (using a build step causes webpack #1617). Grunt verifies that
// this value matches package.json during build.
// See: https://github.com/getsentry/raven-js/issues/465
VERSION: '3.24.0',
VERSION: '3.24.2',
debug: false,
@@ -2066,7 +2071,11 @@ Raven.prototype = {
*/
_promiseRejectionHandler: function(event) {
this._logDebug('debug', 'Raven caught unhandled promise rejection:', event);
this.captureException(event.reason);
this.captureException(event.reason, {
extra: {
unhandledPromiseRejection: true
}
});
},
/**
@@ -2207,6 +2216,14 @@ Raven.prototype = {
// stack[0] is `throw new Error(msg)` call itself, we are interested in the frame that was just before that, stack[1]
var initialCall = isArray$1(stack.stack) && stack.stack[1];
// if stack[1] is `Raven.captureException`, it means that someone passed a string to it and we redirected that call
// to be handled by `captureMessage`, thus `initialCall` is the 3rd one, not 2nd
// initialCall => captureException(string) => captureMessage(string)
if (initialCall && initialCall.func === 'Raven.captureException') {
initialCall = stack.stack[2];
}
var fileurl = (initialCall && initialCall.url) || '';
if (
@@ -3004,17 +3021,30 @@ Raven.prototype = {
status_code: null
};
return origFetch.apply(this, args).then(function(response) {
fetchData.status_code = response.status;
return origFetch
.apply(this, args)
.then(function(response) {
fetchData.status_code = response.status;
self.captureBreadcrumb({
type: 'http',
category: 'fetch',
data: fetchData
self.captureBreadcrumb({
type: 'http',
category: 'fetch',
data: fetchData
});
return response;
})
['catch'](function(err) {
// if there is an error performing the request
self.captureBreadcrumb({
type: 'http',
category: 'fetch',
data: fetchData,
level: 'error'
});
throw err;
});
return response;
});
};
},
wrappedBuiltIns
@@ -3027,7 +3057,7 @@ Raven.prototype = {
if (_document.addEventListener) {
_document.addEventListener('click', self._breadcrumbEventHandler('click'), false);
_document.addEventListener('keypress', self._keypressEventHandler(), false);
} else {
} else if (_document.attachEvent) {
// IE8 Compatibility
_document.attachEvent('onclick', self._breadcrumbEventHandler('click'));
_document.attachEvent('onkeypress', self._keypressEventHandler());
@@ -3750,7 +3780,11 @@ Raven.prototype = {
},
_logDebug: function(level) {
if (this._originalConsoleMethods[level] && this.debug) {
// We allow `Raven.debug` and `Raven.config(DSN, { debug: true })` to not make backward incompatible API change
if (
this._originalConsoleMethods[level] &&
(this.debug || this._globalOptions.debug)
) {
// In IE<10 console methods do not have their own 'apply' method
Function.prototype.apply.call(
this._originalConsoleMethods[level],
@@ -3823,11 +3857,11 @@ var singleton = Raven$1;
* const someAppReporter = new Raven.Client();
* const someOtherAppReporter = new Raven.Client();
*
* someAppReporter('__DSN__', {
* someAppReporter.config('__DSN__', {
* ...config goes here
* });
*
* someOtherAppReporter('__OTHER_DSN__', {
* someOtherAppReporter.config('__OTHER_DSN__', {
* ...config goes here
* });
*
@@ -4011,7 +4045,6 @@ singleton.Client = Client;
case types.youtube:
player.source = {
type: 'video',
title: 'View From A Blue Moon',
sources: [{
src: 'https://youtube.com/watch?v=bTqVqk7FSmY',
provider: 'youtube'
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1
View File
File diff suppressed because one or more lines are too long
+7 -1
View File
@@ -6,8 +6,14 @@
<title>Doh. Looks like something went wrong.</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Icons -->
<link rel="icon" href="https://cdn.plyr.io/static/icons/favicon.ico">
<link rel="icon" type="image/png" href="https://cdn.plyr.io/static/icons/32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="https://cdn.plyr.io/static/icons/16x16.png" sizes="16x16">
<link rel="apple-touch-icon" sizes="180x180" href="https://cdn.plyr.io/static/icons/180x180.png">
<!-- Docs styles -->
<link rel="stylesheet" href="dist/error.css">
<link rel="stylesheet" href="dist/error.css?v=2">
<!-- Preload -->
<link rel="preload" as="font" crossorigin type="font/woff2" href="https://cdn.plyr.io/static/fonts/gordita-medium.woff2">
+3 -3
View File
@@ -27,7 +27,7 @@
<meta name="twitter:card" content="summary_large_image">
<!-- Docs styles -->
<link rel="stylesheet" href="dist/demo.css">
<link rel="stylesheet" href="dist/demo.css?v=2">
<!-- Preload -->
<link rel="preload" as="font" crossorigin type="font/woff2" href="https://cdn.plyr.io/static/fonts/gordita-medium.woff2">
@@ -114,7 +114,7 @@
<title>HTML5</title>
<path d="M14.738.326C14.548.118 14.28 0 14 0H2c-.28 0-.55.118-.738.326S.98.81 1.004 1.09l1 11c.03.317.208.603.48.767l5 3c.16.095.338.143.516.143s.356-.048.515-.143l5-3c.273-.164.452-.45.48-.767l1-11c.026-.28-.067-.557-.257-.764zM12 4H6v2h6v5.72l-4 1.334-4-1.333V9h2v1.28l2 .666 2-.667V8H4V2h8v2z"></path>
</svg>
<a href="http://viewfromabluemoon.com/" target="_blank">View From A Blue Moon</a> &copy; Brainfarm
<a href="https://itunes.apple.com/au/movie/view-from-a-blue-moon/id1041586323" target="_blank">View From A Blue Moon</a> &copy; Brainfarm
</small>
</li>
<li class="plyr__cite plyr__cite--audio" hidden>
@@ -141,7 +141,7 @@
</li>
<li class="plyr__cite plyr__cite--vimeo" hidden>
<small>
<a href="https://vimeo.com/ondemand/viewfromabluemoon4k" target="_blank">View From A Blue Moon</a> on&nbsp;
<a href="https://vimeo.com/76979871" target="_blank">The New Vimeo Player</a> on&nbsp;
<span class="color--vimeo">
<svg class="icon" role="presentation">
<title>Vimeo</title>
-1
View File
@@ -182,7 +182,6 @@ import Raven from 'raven-js';
case types.youtube:
player.source = {
type: 'video',
title: 'View From A Blue Moon',
sources: [{
src: 'https://youtube.com/watch?v=bTqVqk7FSmY',
provider: 'youtube',
+6 -6
View File
@@ -3,12 +3,6 @@
// ==========================================================================
@charset 'UTF-8';
// Libs
@import '../lib/fontface';
@import '../lib/mixins';
@import '../lib/normalize';
@import '../lib/reset';
// Settings
@import '../settings/colors';
@import '../settings/cosmetic';
@@ -17,6 +11,12 @@
@import '../settings/spacing';
@import '../settings/type';
// Libs
@import '../lib/fontface';
@import '../lib/mixins';
@import '../lib/normalize';
@import '../lib/reset';
// Layout
@import '../layout/error';
+1
View File
@@ -16,3 +16,4 @@ $plyr-font-size-captions-base: $plyr-font-size-base;
$plyr-font-size-captions-small: $plyr-font-size-small;
$plyr-font-size-captions-medium: 18px;
$plyr-font-size-captions-large: 21px;
$plyr-font-size-menu: $plyr-font-size-base;
+2 -1
View File
@@ -6,5 +6,6 @@ h1 {
@include font-size($font-size-h1);
font-weight: $font-weight-bold;
letter-spacing: $letter-spacing-headings;
margin: 0 0 ($spacing-base / 2);
line-height: 1.2;
margin: 0 0 $spacing-base;
}
+1 -1
View File
File diff suppressed because one or more lines are too long
+307 -173
View File
@@ -77,7 +77,7 @@ var defaults = {
// Sprite (for icons)
loadSprite: true,
iconPrefix: 'plyr',
iconUrl: 'https://cdn.plyr.io/3.1.0-beta.1/plyr.svg',
iconUrl: 'https://cdn.plyr.io/3.2.3/plyr.svg',
// Blank video (used to prevent errors on source change)
blankVideo: 'https://cdn.plyr.io/static/blank.mp4',
@@ -116,7 +116,7 @@ var defaults = {
// Captions settings
captions: {
active: false,
language: window.navigator.language.split('-')[0]
language: (navigator.language || navigator.userLanguage).split('-')[0]
},
// Fullscreen settings
@@ -164,6 +164,7 @@ var defaults = {
captions: 'Captions',
settings: 'Settings',
speed: 'Speed',
normal: 'Normal',
quality: 'Quality',
loop: 'Loop',
start: 'Start',
@@ -171,6 +172,7 @@ var defaults = {
all: 'All',
reset: 'Reset',
disabled: 'Disabled',
enabled: 'Enabled',
advertisement: 'Ad'
},
@@ -1099,6 +1101,26 @@ var utils = {
},
// Toggle hidden
toggleHidden: function toggleHidden(element, hidden) {
if (!utils.is.element(element)) {
return;
}
var hide = hidden;
if (!utils.is.boolean(hide)) {
hide = !element.hasAttribute('hidden');
}
if (hide) {
element.setAttribute('hidden', '');
} else {
element.removeAttribute('hidden');
}
},
// Toggle class on an element
toggleClass: function toggleClass(element, className, toggle) {
if (utils.is.element(element)) {
@@ -1119,20 +1141,6 @@ var utils = {
},
// Toggle hidden attribute on an element
toggleHidden: function toggleHidden(element, toggle) {
if (!utils.is.element(element)) {
return;
}
if (toggle) {
element.setAttribute('hidden', '');
} else {
element.removeAttribute('hidden');
}
},
// Element matches selector
matches: function matches(element, selector) {
var prototype = { Element: Element };
@@ -1192,8 +1200,8 @@ var utils = {
// Display
this.elements.display = {
buffer: utils.getElement.call(this, this.config.selectors.display.buffer),
duration: utils.getElement.call(this, this.config.selectors.display.duration),
currentTime: utils.getElement.call(this, this.config.selectors.display.currentTime)
currentTime: utils.getElement.call(this, this.config.selectors.display.currentTime),
duration: utils.getElement.call(this, this.config.selectors.display.duration)
};
// Seek tooltip
@@ -1702,16 +1710,16 @@ var support = {
// Check for support
// Basic functionality vs full UI
check: function check(type, provider, inline) {
check: function check(type, provider, playsinline) {
var api = false;
var ui = false;
var browser = utils.getBrowser();
var playsInline = browser.isIPhone && inline && support.inline;
var canPlayInline = browser.isIPhone && playsinline && support.playsinline;
switch (provider + ':' + type) {
case 'html5:video':
api = support.video;
ui = api && support.rangeInput && (!browser.isIPhone || playsInline);
ui = api && support.rangeInput && (!browser.isIPhone || canPlayInline);
break;
case 'html5:audio':
@@ -1722,7 +1730,7 @@ var support = {
case 'youtube:video':
case 'vimeo:video':
api = true;
ui = support.rangeInput && (!browser.isIPhone || playsInline);
ui = support.rangeInput && (!browser.isIPhone || canPlayInline);
break;
default:
@@ -1750,7 +1758,7 @@ var support = {
// Inline playback support
// https://webkit.org/blog/6784/new-video-policies-for-ios/
inline: 'playsInline' in document.createElement('video'),
playsinline: 'playsInline' in document.createElement('video'),
// Check for mime type support against a player instance
// Credits: http://diveintohtml5.info/everything.html
@@ -1950,7 +1958,7 @@ var Fullscreen = function () {
// Get prefix
this.prefix = Fullscreen.prefix;
this.name = Fullscreen.name;
this.property = Fullscreen.property;
// Scroll position
this.scrollPosition = { x: 0, y: 0 };
@@ -1965,7 +1973,7 @@ var Fullscreen = function () {
// Fullscreen toggle on double click
utils.on(this.player.elements.container, 'dblclick', function (event) {
// Ignore double click in controls
if (_this.player.elements.controls.contains(event.target)) {
if (utils.is.element(_this.player.elements.controls) && _this.player.elements.controls.contains(event.target)) {
return;
}
@@ -2014,7 +2022,7 @@ var Fullscreen = function () {
} else if (!this.prefix) {
this.target.requestFullscreen();
} else if (!utils.is.empty(this.prefix)) {
this.target[this.prefix + 'Request' + this.name]();
this.target[this.prefix + 'Request' + this.property]();
}
}
@@ -2034,10 +2042,10 @@ var Fullscreen = function () {
} else if (!Fullscreen.native) {
toggleFallback.call(this, false);
} else if (!this.prefix) {
document.cancelFullScreen();
(document.cancelFullScreen || document.exitFullscreen).call(document);
} else if (!utils.is.empty(this.prefix)) {
var action = this.prefix === 'moz' ? 'Cancel' : 'Exit';
document['' + this.prefix + action + this.name]();
document['' + this.prefix + action + this.property]();
}
}
@@ -2075,7 +2083,7 @@ var Fullscreen = function () {
return utils.hasClass(this.target, this.player.config.classNames.fullscreen.fallback);
}
var element = !this.prefix ? document.fullscreenElement : document['' + this.prefix + this.name + 'Element'];
var element = !this.prefix ? document.fullscreenElement : document['' + this.prefix + this.property + 'Element'];
return element === this.target;
}
@@ -2100,7 +2108,7 @@ var Fullscreen = function () {
get: function get$$1() {
// No prefix
if (utils.is.function(document.exitFullscreen)) {
return false;
return '';
}
// Check for fullscreen support by vendor prefix
@@ -2119,7 +2127,7 @@ var Fullscreen = function () {
return value;
}
}, {
key: 'name',
key: 'property',
get: function get$$1() {
return this.prefix === 'moz' ? 'FullScreen' : 'Fullscreen';
}
@@ -2129,6 +2137,36 @@ var Fullscreen = function () {
// ==========================================================================
var i18n = {
get: function get$$1() {
var key = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
if (utils.is.empty(key) || utils.is.empty(config) || !Object.keys(config.i18n).includes(key)) {
return '';
}
var string = config.i18n[key];
var replace = {
'{seektime}': config.seekTime,
'{title}': config.title
};
Object.entries(replace).forEach(function (_ref) {
var _ref2 = slicedToArray(_ref, 2),
key = _ref2[0],
value = _ref2[1];
string = utils.replaceAll(string, key, value);
});
return string;
}
};
// ==========================================================================
var captions = {
// Setup captions
setup: function setup() {
@@ -2168,6 +2206,7 @@ var captions = {
return;
}
// Inject the container
if (!utils.is.element(this.elements.captions)) {
this.elements.captions = utils.createElement('div', utils.getAttributesFromSelector(this.config.selectors.captions));
@@ -2272,9 +2311,54 @@ var captions = {
getCurrentTrack: function getCurrentTrack() {
var _this2 = this;
return captions.getTracks.call(this).find(function (track) {
var tracks = captions.getTracks.call(this);
if (!tracks.length) {
return null;
}
// Get track based on current language
var track = tracks.find(function (track) {
return track.language.toLowerCase() === _this2.language;
});
// Get the <track> with default attribute
if (!track) {
track = utils.getElement.call(this, 'track[default]');
}
// Get the first track
if (!track) {
var _tracks = slicedToArray(tracks, 1);
track = _tracks[0];
}
return track;
},
// Get UI label for track
getLabel: function getLabel(track) {
var currentTrack = track;
if (!utils.is.track(currentTrack) && support.textTracks && this.captions.active) {
currentTrack = captions.getCurrentTrack.call(this);
}
if (utils.is.track(currentTrack)) {
if (!utils.is.empty(currentTrack.label)) {
return currentTrack.label;
}
if (!utils.is.empty(currentTrack.language)) {
return track.language.toUpperCase();
}
return i18n.get('enabled', this.config);
}
return i18n.get('disabled', this.config);
},
@@ -2336,11 +2420,6 @@ var captions = {
// Display captions container and button (for initialization)
show: function show() {
// If there's no caption toggle, bail
if (!utils.is.element(this.elements.buttons.captions)) {
return;
}
// Try to load the value from storage
var active = this.storage.get('captions');
@@ -2360,36 +2439,6 @@ var captions = {
// ==========================================================================
var i18n = {
get: function get$$1() {
var key = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
if (utils.is.empty(key) || utils.is.empty(config) || !Object.keys(config.i18n).includes(key)) {
return '';
}
var string = config.i18n[key];
var replace = {
'{seektime}': config.seekTime,
'{title}': config.title
};
Object.entries(replace).forEach(function (_ref) {
var _ref2 = slicedToArray(_ref, 2),
key = _ref2[0],
value = _ref2[1];
string = utils.replaceAll(string, key, value);
});
return string;
}
};
// ==========================================================================
var ui = {
addStyleHook: function addStyleHook() {
utils.toggleClass(this.elements.container, this.config.selectors.container.replace('.', ''), true);
@@ -2437,11 +2486,6 @@ var ui = {
this.listeners.controls();
}
// If there's no controls, bail
if (!utils.is.element(this.elements.controls)) {
return;
}
// Remove native controls
ui.toggleNativeControls.call(this);
@@ -2682,10 +2726,10 @@ var ui = {
}
// Always display hours if duration is over an hour
var displayHours = utils.getHours(this.duration) > 0;
var forceHours = utils.getHours(this.duration) > 0;
// eslint-disable-next-line no-param-reassign
target.textContent = utils.formatTime(time, displayHours, inverted);
target.textContent = utils.formatTime(time, forceHours, inverted);
},
@@ -2892,11 +2936,6 @@ var browser$1 = utils.getBrowser();
var controls = {
// Webkit polyfill for lower fill range
updateRangeFill: function updateRangeFill(target) {
// WebKit only
if (!browser$1.isWebkit) {
return;
}
// Get range from event if event passed
var range = utils.is.event(target) ? target.target : target;
@@ -2905,6 +2944,14 @@ var controls = {
return;
}
// Set aria value for https://github.com/sampotts/plyr/issues/905
range.setAttribute('aria-valuenow', range.value);
// WebKit only
if (!browser$1.isWebkit) {
return;
}
// Set CSS custom property
range.style.setProperty('--value', range.value / range.max * 100 + '%');
},
@@ -2928,7 +2975,8 @@ var controls = {
// Create <svg>
var icon = document.createElementNS(namespace, 'svg');
utils.setAttributes(icon, utils.extend(attributes, {
role: 'presentation'
role: 'presentation',
focusable: 'false'
}));
// Create the <use> to reference sprite
@@ -3079,7 +3127,6 @@ var controls = {
// Add aria attributes
attributes['aria-pressed'] = false;
attributes['aria-label'] = i18n.get(label, this.config);
} else {
button.appendChild(controls.createIcon.call(this, icon));
button.appendChild(controls.createLabel.call(this, label));
@@ -3110,6 +3157,7 @@ var controls = {
// Seek label
var label = utils.createElement('label', {
for: attributes.id,
id: attributes.id + '-label',
class: this.config.classNames.hidden
}, i18n.get(type, this.config));
@@ -3120,7 +3168,13 @@ var controls = {
max: 100,
step: 0.01,
value: 0,
autocomplete: 'off'
autocomplete: 'off',
// A11y fixes for https://github.com/sampotts/plyr/issues/905
role: 'slider',
'aria-labelledby': attributes.id + '-label',
'aria-valuemin': 0,
'aria-valuemax': 100,
'aria-valuenow': 0
}, attributes));
this.elements.inputs[type] = input;
@@ -3140,7 +3194,9 @@ var controls = {
var progress = utils.createElement('progress', utils.extend(utils.getAttributesFromSelector(this.config.selectors.display[type]), {
min: 0,
max: 100,
value: 0
value: 0,
role: 'presentation',
'aria-hidden': true
}, attributes));
// Create the label inside
@@ -3172,16 +3228,14 @@ var controls = {
// Create time display
createTime: function createTime(type) {
var container = utils.createElement('div', {
class: 'plyr__time'
});
var attributes = utils.getAttributesFromSelector(this.config.selectors.display[type]);
container.appendChild(utils.createElement('span', {
class: this.config.classNames.hidden
}, i18n.get(type, this.config)));
container.appendChild(utils.createElement('span', utils.getAttributesFromSelector(this.config.selectors.display[type]), '00:00'));
var container = utils.createElement('div', utils.extend(attributes, {
class: 'plyr__time ' + attributes.class,
'aria-label': i18n.get(type, this.config)
}), '0:00');
// Reference for updates
this.elements.display[type] = container;
return container;
@@ -3207,7 +3261,7 @@ var controls = {
class: 'plyr__sr-only'
}));
var faux = utils.createElement('span', { 'aria-hidden': true });
var faux = utils.createElement('span', { hidden: '' });
label.appendChild(radio);
label.appendChild(faux);
@@ -3278,11 +3332,7 @@ var controls = {
// Hide/show a tab
toggleTab: function toggleTab(setting, toggle) {
var tab = this.elements.settings.tabs[setting];
var pane = this.elements.settings.panes[setting];
utils.toggleHidden(tab, !toggle);
utils.toggleHidden(pane, !toggle);
utils.toggleHidden(this.elements.settings.tabs[setting], !toggle);
},
@@ -3310,6 +3360,9 @@ var controls = {
var toggle = !utils.is.empty(this.options.quality) && this.options.quality.length > 1;
controls.toggleTab.call(this, type, toggle);
// Check if we need to toggle the parent
controls.checkMenu.call(this);
// If we're hiding, nothing more to do
if (!toggle) {
return;
@@ -3366,16 +3419,17 @@ var controls = {
getLabel: function getLabel(setting, value) {
switch (setting) {
case 'speed':
return value === 1 ? 'Normal' : value + '&times;';
return value === 1 ? i18n.get('normal', this.config) : value + '&times;';
case 'quality':
if (utils.is.number(value)) {
return value + 'p';
}
return utils.toTitleCase(value);
case 'captions':
return controls.getLanguage.call(this);
return captions.getLabel.call(this);
default:
return null;
@@ -3391,7 +3445,18 @@ var controls = {
switch (setting) {
case 'captions':
value = this.captions.active ? this.captions.language : i18n.get('disabled', this.config);
if (this.captions.active) {
if (this.options.captions.length > 2 || !this.options.captions.some(function (lang) {
return lang === 'enabled';
})) {
value = this.captions.language;
} else {
value = 'enabled';
}
} else {
value = '';
}
break;
default:
@@ -3422,17 +3487,19 @@ var controls = {
list = pane && pane.querySelector('ul');
}
// Update the label
if (!utils.is.empty(value)) {
var label = this.elements.settings.tabs[setting].querySelector('.' + this.config.classNames.menu.value);
label.innerHTML = controls.getLabel.call(this, setting, value);
// If there's no list it means it's not been rendered...
if (!utils.is.element(list)) {
return;
}
// Find the radio option
// Update the label
var label = this.elements.settings.tabs[setting].querySelector('.' + this.config.classNames.menu.value);
label.innerHTML = controls.getLabel.call(this, setting, value);
// Find the radio option and check it
var target = list && list.querySelector('input[value="' + value + '"]');
if (utils.is.element(target)) {
// Check it
target.checked = true;
}
},
@@ -3476,22 +3543,6 @@ var controls = {
// Get current selected caption language
// TODO: rework this to user the getter in the API?
getLanguage: function getLanguage() {
if (!this.supported.ui) {
return null;
}
if (support.textTracks && captions.getTracks.call(this).length && this.captions.active) {
var currentTrack = captions.getCurrentTrack.call(this);
if (utils.is.track(currentTrack)) {
return currentTrack.label;
}
}
return i18n.get('disabled', this.config);
},
// Set a list of available captions languages
setCaptionsMenu: function setCaptionsMenu() {
@@ -3508,6 +3559,9 @@ var controls = {
// Empty the menu
utils.emptyElement(list);
// Check if we need to toggle the parent
controls.checkMenu.call(this);
// If there's no captions, bail
if (!toggle) {
return;
@@ -3516,8 +3570,8 @@ var controls = {
// Re-map the tracks into just the data we need
var tracks = captions.getTracks.call(this).map(function (track) {
return {
language: track.language,
label: !utils.is.empty(track.label) ? track.label : track.language.toUpperCase()
language: !utils.is.empty(track.language) ? track.language : 'enabled',
label: captions.getLabel.call(_this3, track)
};
});
@@ -3529,7 +3583,12 @@ var controls = {
// Generate options
tracks.forEach(function (track) {
controls.createMenuItem.call(_this3, track.language, list, 'language', track.label || track.language, controls.createBadge.call(_this3, track.language.toUpperCase()), track.language.toLowerCase() === _this3.captions.language.toLowerCase());
controls.createMenuItem.call(_this3, track.language, list, 'language', track.label, track.language !== 'enabled' ? controls.createBadge.call(_this3, track.language.toUpperCase()) : null, track.language.toLowerCase() === _this3.captions.language.toLowerCase());
});
// Store reference
this.options.captions = tracks.map(function (track) {
return track.language;
});
controls.updateSetting.call(this, type, list);
@@ -3579,10 +3638,6 @@ var controls = {
// Get the list to populate
var list = this.elements.settings.panes.speed.querySelector('ul');
// Show the pane and tab
utils.toggleHidden(this.elements.settings.tabs.speed, false);
utils.toggleHidden(this.elements.settings.panes.speed, false);
// Empty the menu
utils.emptyElement(list);
@@ -3619,7 +3674,7 @@ var controls = {
return;
}
var show = utils.is.boolean(event) ? event : utils.is.element(form) && form.getAttribute('aria-hidden') === 'true';
var show = utils.is.boolean(event) ? event : utils.is.element(form) && form.hasAttribute('hidden');
if (utils.is.event(event)) {
var isMenuItem = utils.is.element(form) && form.contains(event.target);
@@ -3644,7 +3699,7 @@ var controls = {
}
if (utils.is.element(form)) {
form.setAttribute('aria-hidden', !show);
utils.toggleHidden(form, !show);
utils.toggleClass(this.elements.container, this.config.classNames.menu.open, show);
if (show) {
@@ -3661,7 +3716,7 @@ var controls = {
var clone = tab.cloneNode(true);
clone.style.position = 'absolute';
clone.style.opacity = 0;
clone.setAttribute('aria-hidden', false);
clone.removeAttribute('hidden');
// Prevent input's being unchecked due to the name being identical
Array.from(clone.querySelectorAll('input[name]')).forEach(function (input) {
@@ -3707,7 +3762,7 @@ var controls = {
// Hide all other tabs
// Get other tabs
var current = menu.querySelector('[role="tabpanel"][aria-hidden="false"]');
var current = menu.querySelector('[role="tabpanel"]:not([hidden])');
var container = current.parentNode;
// Set other toggles to be expanded false
@@ -3748,11 +3803,11 @@ var controls = {
}
// Set attributes on current tab
current.setAttribute('aria-hidden', true);
utils.toggleHidden(current, true);
current.setAttribute('tabindex', -1);
// Set attributes on target
pane.setAttribute('aria-hidden', !show);
utils.toggleHidden(pane, !show);
tab.setAttribute('aria-expanded', show);
pane.removeAttribute('tabindex');
@@ -3887,7 +3942,7 @@ var controls = {
var form = utils.createElement('form', {
class: 'plyr__menu__container',
id: 'plyr-settings-' + data.id,
'aria-hidden': true,
hidden: '',
'aria-labelled-by': 'plyr-settings-toggle-' + data.id,
role: 'tablist',
tabindex: -1
@@ -3897,7 +3952,6 @@ var controls = {
var home = utils.createElement('div', {
id: 'plyr-settings-' + data.id + '-home',
'aria-hidden': false,
'aria-labelled-by': 'plyr-settings-toggle-' + data.id,
role: 'tabpanel'
});
@@ -3944,11 +3998,10 @@ var controls = {
this.config.settings.forEach(function (type) {
var pane = utils.createElement('div', {
id: 'plyr-settings-' + data.id + '-' + type,
'aria-hidden': true,
hidden: '',
'aria-labelled-by': 'plyr-settings-' + data.id + '-' + type + '-tab',
role: 'tabpanel',
tabindex: -1,
hidden: ''
tabindex: -1
});
var back = utils.createElement('button', {
@@ -4030,17 +4083,21 @@ var controls = {
var container = null;
this.elements.controls = null;
// HTML or Element passed as the option
// Set template properties
var props = {
id: this.id,
seektime: this.config.seekTime,
title: this.config.title
};
var update = true;
if (utils.is.string(this.config.controls) || utils.is.element(this.config.controls)) {
// String or HTMLElement passed as the option
container = this.config.controls;
} else if (utils.is.function(this.config.controls)) {
// A custom function to build controls
// The function can return a HTMLElement or String
container = this.config.controls({
id: this.id,
seektime: this.config.seekTime,
title: this.config.title
});
container = this.config.controls.call(this, props);
} else {
// Create controls
container = controls.create.call(this, {
@@ -4048,10 +4105,35 @@ var controls = {
seektime: this.config.seekTime,
speed: this.speed,
quality: this.quality,
captions: controls.getLanguage.call(this)
captions: captions.getLabel.call(this)
// TODO: Looping
// loop: 'None',
});
update = false;
}
// Replace props with their value
var replace = function replace(input) {
var result = input;
Object.entries(props).forEach(function (_ref) {
var _ref2 = slicedToArray(_ref, 2),
key = _ref2[0],
value = _ref2[1];
result = utils.replaceAll(result, '{' + key + '}', value);
});
return result;
};
// Update markup
if (update) {
if (utils.is.string(this.config.controls)) {
container = replace(container);
} else if (utils.is.element(container)) {
container.innerHTML = replace(container.innerHTML);
}
}
// Controls container
@@ -4070,7 +4152,7 @@ var controls = {
// Inject controls HTML
if (utils.is.element(container)) {
target.appendChild(container);
} else {
} else if (container) {
target.insertAdjacentHTML('beforeend', container);
}
@@ -4353,7 +4435,7 @@ var Listeners = function () {
});
// Display duration
utils.on(this.player.media, 'durationchange loadedmetadata', function (event) {
utils.on(this.player.media, 'durationchange loadeddata loadedmetadata', function (event) {
return ui.durationUpdate.call(_this3.player, event);
});
@@ -4402,6 +4484,10 @@ var Listeners = function () {
// If autoplay, then load advertisement if required
// TODO: Show some sort of loading state while the ad manager loads else there's a delay before ad shows
utils.on(this.player.media, 'playing', function () {
if (!_this3.player.ads) {
return;
}
// If ads are enabled, wait for them first
if (_this3.player.ads.enabled && !_this3.player.ads.initialized) {
// Wait for manager response
@@ -4443,7 +4529,7 @@ var Listeners = function () {
// Disable right click
if (this.player.supported.ui && this.player.config.disableContextMenu) {
utils.on(this.player.media, 'contextmenu', function (event) {
utils.on(this.player.elements.wrapper, 'contextmenu', function (event) {
event.preventDefault();
}, false);
}
@@ -5729,7 +5815,11 @@ var youtube = {
return Number(instance.getCurrentTime());
},
set: function set(time) {
// Vimeo will automatically play on seek
var paused = player.media.paused;
// Set seeking flag
player.media.seeking = true;
// Trigger seeking
@@ -5737,6 +5827,11 @@ var youtube = {
// Seek after events sent
instance.seekTo(time);
// Restore pause state
if (paused) {
player.pause();
}
}
});
@@ -5974,10 +6069,14 @@ var vimeo = {
setAspectRatio: function setAspectRatio(input) {
var ratio = utils.is.string(input) ? input.split(':') : this.config.ratio.split(':');
var padding = 100 / ratio[0] * ratio[1];
var height = 240;
var offset = (height - padding) / (height / 50);
this.elements.wrapper.style.paddingBottom = padding + '%';
this.media.style.transform = 'translateY(-' + offset + '%)';
if (this.supported.ui) {
var height = 240;
var offset = (height - padding) / (height / 50);
this.media.style.transform = 'translateY(-' + offset + '%)';
}
},
@@ -5996,7 +6095,8 @@ var vimeo = {
title: false,
speed: true,
transparent: 0,
gesture: 'media'
gesture: 'media',
playsinline: !this.config.fullscreen.iosNative
};
var params = utils.buildUrlParams(options);
@@ -6030,6 +6130,11 @@ var vimeo = {
player.media.paused = true;
player.media.currentTime = 0;
// Disable native text track rendering
if (player.supported.ui) {
player.embed.disableTextTrack();
}
// Create a faux HTML5 API using the Vimeo API
player.media.play = function () {
player.embed.play().then(function () {
@@ -6068,7 +6173,9 @@ var vimeo = {
utils.dispatchEvent.call(player, player.media, 'seeking');
// Seek after events
player.embed.setCurrentTime(time);
player.embed.setCurrentTime(time).catch(function () {
// Do nothing
});
// Restore pause state
if (paused) {
@@ -6248,6 +6355,15 @@ var vimeo = {
if (parseInt(data.percent, 10) === 1) {
utils.dispatchEvent.call(player, player.media, 'canplaythrough');
}
// Get duration as if we do it before load, it gives an incorrect value
// https://github.com/sampotts/plyr/issues/891
player.embed.getDuration().then(function (value) {
if (value !== player.media.duration) {
player.media.duration = value;
utils.dispatchEvent.call(player, player.media, 'durationchange');
}
});
});
player.embed.on('seeked', function () {
@@ -6399,7 +6515,7 @@ var source = {
_this2.provider = !utils.is.empty(input.sources[0].provider) ? input.sources[0].provider : providers.html5;
// Check for support
_this2.supported = support.check(_this2.type, _this2.provider, _this2.config.inline);
_this2.supported = support.check(_this2.type, _this2.provider, _this2.config.playsinline);
// Create new markup
switch (_this2.provider + ':' + _this2.type) {
@@ -6447,7 +6563,7 @@ var source = {
if (_this2.config.muted) {
_this2.media.setAttribute('muted', '');
}
if (_this2.config.inline) {
if (_this2.config.playsinline) {
_this2.media.setAttribute('playsinline', '');
}
}
@@ -6528,7 +6644,7 @@ var Plyr = function () {
}
// Set config
this.config = utils.extend({}, defaults, options, function () {
this.config = utils.extend({}, defaults, options || {}, function () {
try {
return JSON.parse(_this.media.getAttribute('data-plyr-config'));
} catch (e) {
@@ -6565,7 +6681,8 @@ var Plyr = function () {
// Options
this.options = {
speed: [],
quality: []
quality: [],
captions: []
};
// Debugging
@@ -6650,12 +6767,17 @@ var Plyr = function () {
if (truthy.includes(params.autoplay)) {
this.config.autoplay = true;
}
if (truthy.includes(params.playsinline)) {
this.config.inline = true;
}
if (truthy.includes(params.loop)) {
this.config.loop.active = true;
}
// TODO: replace fullscreen.iosNative with this playsinline config option
// YouTube requires the playsinline in the URL
if (this.isYouTube) {
this.config.playsinline = truthy.includes(params.playsinline);
} else {
this.config.playsinline = true;
}
}
} else {
// <div> with attributes
@@ -6689,7 +6811,7 @@ var Plyr = function () {
this.config.autoplay = true;
}
if (this.media.hasAttribute('playsinline')) {
this.config.inline = true;
this.config.playsinline = true;
}
if (this.media.hasAttribute('muted')) {
this.config.muted = true;
@@ -6706,7 +6828,7 @@ var Plyr = function () {
}
// Check for support again but with type
this.supported = support.check(this.type, this.provider, this.config.inline);
this.supported = support.check(this.type, this.provider, this.config.playsinline);
// If no support for even API, bail
if (!this.supported.api) {
@@ -6926,13 +7048,13 @@ var Plyr = function () {
* @param {boolean} input - Whether to enable captions
*/
value: function toggleCaptions(input) {
// If there's no full support, or there's no caption toggle
if (!this.supported.ui || !utils.is.element(this.elements.buttons.captions)) {
// If there's no full support
if (!this.supported.ui) {
return;
}
// If the method is called without parameter, toggle based on current value
var show = utils.is.boolean(input) ? input : this.elements.container.className.indexOf(this.config.classNames.captions.active) === -1;
var show = utils.is.boolean(input) ? input : !this.elements.container.classList.contains(this.config.classNames.captions.active);
// Nothing to change...
if (this.captions.active === show) {
@@ -7291,7 +7413,7 @@ var Plyr = function () {
}, {
key: 'playing',
get: function get$$1() {
return Boolean(!this.paused && !this.ended && (this.isHTML5 ? this.media.readyState > 2 : true));
return Boolean(this.ready && !this.paused && !this.ended && (this.isHTML5 ? this.media.readyState > 2 : true));
}
/**
@@ -7320,7 +7442,7 @@ var Plyr = function () {
}
// Set
this.media.currentTime = parseFloat(targetTime.toFixed(4));
this.media.currentTime = targetTime;
// Logging
this.debug.log('Seeking to ' + this.currentTime + ' seconds');
@@ -7377,7 +7499,7 @@ var Plyr = function () {
key: 'duration',
get: function get$$1() {
// Faux duration set via config
var fauxDuration = parseInt(this.config.duration, 10);
var fauxDuration = parseFloat(this.config.duration);
// True duration
var realDuration = this.media ? Number(this.media.duration) : 0;
@@ -7728,17 +7850,29 @@ var Plyr = function () {
return;
}
// Toggle captions based on input
this.toggleCaptions(!utils.is.empty(input));
// If empty string is passed, assume disable captions
if (utils.is.empty(input)) {
this.toggleCaptions(false);
return;
}
// Normalize
var language = input.toLowerCase();
// Check for support
if (!this.options.captions.includes(language)) {
this.debug.warn('Unsupported language option: ' + language);
return;
}
// Ensure captions are enabled
this.toggleCaptions(true);
// Enabled only
if (language === 'enabled') {
return;
}
// If nothing to change, bail
if (this.language === language) {
return;
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+307 -173
View File
@@ -5117,7 +5117,7 @@ var defaults = {
// Sprite (for icons)
loadSprite: true,
iconPrefix: 'plyr',
iconUrl: 'https://cdn.plyr.io/3.1.0-beta.2/plyr.svg',
iconUrl: 'https://cdn.plyr.io/3.2.4/plyr.svg',
// Blank video (used to prevent errors on source change)
blankVideo: 'https://cdn.plyr.io/static/blank.mp4',
@@ -5156,7 +5156,7 @@ var defaults = {
// Captions settings
captions: {
active: false,
language: window.navigator.language.split('-')[0]
language: (navigator.language || navigator.userLanguage).split('-')[0]
},
// Fullscreen settings
@@ -5204,6 +5204,7 @@ var defaults = {
captions: 'Captions',
settings: 'Settings',
speed: 'Speed',
normal: 'Normal',
quality: 'Quality',
loop: 'Loop',
start: 'Start',
@@ -5211,6 +5212,7 @@ var defaults = {
all: 'All',
reset: 'Reset',
disabled: 'Disabled',
enabled: 'Enabled',
advertisement: 'Ad'
},
@@ -6133,6 +6135,26 @@ var utils = {
},
// Toggle hidden
toggleHidden: function toggleHidden(element, hidden) {
if (!utils.is.element(element)) {
return;
}
var hide = hidden;
if (!utils.is.boolean(hide)) {
hide = !element.hasAttribute('hidden');
}
if (hide) {
element.setAttribute('hidden', '');
} else {
element.removeAttribute('hidden');
}
},
// Toggle class on an element
toggleClass: function toggleClass(element, className, toggle) {
if (utils.is.element(element)) {
@@ -6153,20 +6175,6 @@ var utils = {
},
// Toggle hidden attribute on an element
toggleHidden: function toggleHidden(element, toggle) {
if (!utils.is.element(element)) {
return;
}
if (toggle) {
element.setAttribute('hidden', '');
} else {
element.removeAttribute('hidden');
}
},
// Element matches selector
matches: function matches(element, selector) {
var prototype = { Element: Element };
@@ -6226,8 +6234,8 @@ var utils = {
// Display
this.elements.display = {
buffer: utils.getElement.call(this, this.config.selectors.display.buffer),
duration: utils.getElement.call(this, this.config.selectors.display.duration),
currentTime: utils.getElement.call(this, this.config.selectors.display.currentTime)
currentTime: utils.getElement.call(this, this.config.selectors.display.currentTime),
duration: utils.getElement.call(this, this.config.selectors.display.duration)
};
// Seek tooltip
@@ -6736,16 +6744,16 @@ var support = {
// Check for support
// Basic functionality vs full UI
check: function check(type, provider, inline) {
check: function check(type, provider, playsinline) {
var api = false;
var ui = false;
var browser = utils.getBrowser();
var playsInline = browser.isIPhone && inline && support.inline;
var canPlayInline = browser.isIPhone && playsinline && support.playsinline;
switch (provider + ':' + type) {
case 'html5:video':
api = support.video;
ui = api && support.rangeInput && (!browser.isIPhone || playsInline);
ui = api && support.rangeInput && (!browser.isIPhone || canPlayInline);
break;
case 'html5:audio':
@@ -6756,7 +6764,7 @@ var support = {
case 'youtube:video':
case 'vimeo:video':
api = true;
ui = support.rangeInput && (!browser.isIPhone || playsInline);
ui = support.rangeInput && (!browser.isIPhone || canPlayInline);
break;
default:
@@ -6784,7 +6792,7 @@ var support = {
// Inline playback support
// https://webkit.org/blog/6784/new-video-policies-for-ios/
inline: 'playsInline' in document.createElement('video'),
playsinline: 'playsInline' in document.createElement('video'),
// Check for mime type support against a player instance
// Credits: http://diveintohtml5.info/everything.html
@@ -6984,7 +6992,7 @@ var Fullscreen = function () {
// Get prefix
this.prefix = Fullscreen.prefix;
this.name = Fullscreen.name;
this.property = Fullscreen.property;
// Scroll position
this.scrollPosition = { x: 0, y: 0 };
@@ -6999,7 +7007,7 @@ var Fullscreen = function () {
// Fullscreen toggle on double click
utils.on(this.player.elements.container, 'dblclick', function (event) {
// Ignore double click in controls
if (_this.player.elements.controls.contains(event.target)) {
if (utils.is.element(_this.player.elements.controls) && _this.player.elements.controls.contains(event.target)) {
return;
}
@@ -7048,7 +7056,7 @@ var Fullscreen = function () {
} else if (!this.prefix) {
this.target.requestFullscreen();
} else if (!utils.is.empty(this.prefix)) {
this.target[this.prefix + 'Request' + this.name]();
this.target[this.prefix + 'Request' + this.property]();
}
}
@@ -7068,10 +7076,10 @@ var Fullscreen = function () {
} else if (!Fullscreen.native) {
toggleFallback.call(this, false);
} else if (!this.prefix) {
document.cancelFullScreen();
(document.cancelFullScreen || document.exitFullscreen).call(document);
} else if (!utils.is.empty(this.prefix)) {
var action = this.prefix === 'moz' ? 'Cancel' : 'Exit';
document['' + this.prefix + action + this.name]();
document['' + this.prefix + action + this.property]();
}
}
@@ -7109,7 +7117,7 @@ var Fullscreen = function () {
return utils.hasClass(this.target, this.player.config.classNames.fullscreen.fallback);
}
var element = !this.prefix ? document.fullscreenElement : document['' + this.prefix + this.name + 'Element'];
var element = !this.prefix ? document.fullscreenElement : document['' + this.prefix + this.property + 'Element'];
return element === this.target;
}
@@ -7134,7 +7142,7 @@ var Fullscreen = function () {
get: function get() {
// No prefix
if (utils.is.function(document.exitFullscreen)) {
return false;
return '';
}
// Check for fullscreen support by vendor prefix
@@ -7153,7 +7161,7 @@ var Fullscreen = function () {
return value;
}
}, {
key: 'name',
key: 'property',
get: function get() {
return this.prefix === 'moz' ? 'FullScreen' : 'Fullscreen';
}
@@ -7163,6 +7171,36 @@ var Fullscreen = function () {
// ==========================================================================
var i18n = {
get: function get() {
var key = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
if (utils.is.empty(key) || utils.is.empty(config) || !Object.keys(config.i18n).includes(key)) {
return '';
}
var string = config.i18n[key];
var replace = {
'{seektime}': config.seekTime,
'{title}': config.title
};
Object.entries(replace).forEach(function (_ref) {
var _ref2 = slicedToArray(_ref, 2),
key = _ref2[0],
value = _ref2[1];
string = utils.replaceAll(string, key, value);
});
return string;
}
};
// ==========================================================================
var captions = {
// Setup captions
setup: function setup() {
@@ -7202,6 +7240,7 @@ var captions = {
return;
}
// Inject the container
if (!utils.is.element(this.elements.captions)) {
this.elements.captions = utils.createElement('div', utils.getAttributesFromSelector(this.config.selectors.captions));
@@ -7306,9 +7345,54 @@ var captions = {
getCurrentTrack: function getCurrentTrack() {
var _this2 = this;
return captions.getTracks.call(this).find(function (track) {
var tracks = captions.getTracks.call(this);
if (!tracks.length) {
return null;
}
// Get track based on current language
var track = tracks.find(function (track) {
return track.language.toLowerCase() === _this2.language;
});
// Get the <track> with default attribute
if (!track) {
track = utils.getElement.call(this, 'track[default]');
}
// Get the first track
if (!track) {
var _tracks = slicedToArray(tracks, 1);
track = _tracks[0];
}
return track;
},
// Get UI label for track
getLabel: function getLabel(track) {
var currentTrack = track;
if (!utils.is.track(currentTrack) && support.textTracks && this.captions.active) {
currentTrack = captions.getCurrentTrack.call(this);
}
if (utils.is.track(currentTrack)) {
if (!utils.is.empty(currentTrack.label)) {
return currentTrack.label;
}
if (!utils.is.empty(currentTrack.language)) {
return track.language.toUpperCase();
}
return i18n.get('enabled', this.config);
}
return i18n.get('disabled', this.config);
},
@@ -7370,11 +7454,6 @@ var captions = {
// Display captions container and button (for initialization)
show: function show() {
// If there's no caption toggle, bail
if (!utils.is.element(this.elements.buttons.captions)) {
return;
}
// Try to load the value from storage
var active = this.storage.get('captions');
@@ -7394,36 +7473,6 @@ var captions = {
// ==========================================================================
var i18n = {
get: function get() {
var key = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
if (utils.is.empty(key) || utils.is.empty(config) || !Object.keys(config.i18n).includes(key)) {
return '';
}
var string = config.i18n[key];
var replace = {
'{seektime}': config.seekTime,
'{title}': config.title
};
Object.entries(replace).forEach(function (_ref) {
var _ref2 = slicedToArray(_ref, 2),
key = _ref2[0],
value = _ref2[1];
string = utils.replaceAll(string, key, value);
});
return string;
}
};
// ==========================================================================
var ui = {
addStyleHook: function addStyleHook() {
utils.toggleClass(this.elements.container, this.config.selectors.container.replace('.', ''), true);
@@ -7471,11 +7520,6 @@ var ui = {
this.listeners.controls();
}
// If there's no controls, bail
if (!utils.is.element(this.elements.controls)) {
return;
}
// Remove native controls
ui.toggleNativeControls.call(this);
@@ -7716,10 +7760,10 @@ var ui = {
}
// Always display hours if duration is over an hour
var displayHours = utils.getHours(this.duration) > 0;
var forceHours = utils.getHours(this.duration) > 0;
// eslint-disable-next-line no-param-reassign
target.textContent = utils.formatTime(time, displayHours, inverted);
target.textContent = utils.formatTime(time, forceHours, inverted);
},
@@ -7926,11 +7970,6 @@ var browser$1 = utils.getBrowser();
var controls = {
// Webkit polyfill for lower fill range
updateRangeFill: function updateRangeFill(target) {
// WebKit only
if (!browser$1.isWebkit) {
return;
}
// Get range from event if event passed
var range = utils.is.event(target) ? target.target : target;
@@ -7939,6 +7978,14 @@ var controls = {
return;
}
// Set aria value for https://github.com/sampotts/plyr/issues/905
range.setAttribute('aria-valuenow', range.value);
// WebKit only
if (!browser$1.isWebkit) {
return;
}
// Set CSS custom property
range.style.setProperty('--value', range.value / range.max * 100 + '%');
},
@@ -7962,7 +8009,8 @@ var controls = {
// Create <svg>
var icon = document.createElementNS(namespace, 'svg');
utils.setAttributes(icon, utils.extend(attributes, {
role: 'presentation'
role: 'presentation',
focusable: 'false'
}));
// Create the <use> to reference sprite
@@ -8113,7 +8161,6 @@ var controls = {
// Add aria attributes
attributes['aria-pressed'] = false;
attributes['aria-label'] = i18n.get(label, this.config);
} else {
button.appendChild(controls.createIcon.call(this, icon));
button.appendChild(controls.createLabel.call(this, label));
@@ -8144,6 +8191,7 @@ var controls = {
// Seek label
var label = utils.createElement('label', {
for: attributes.id,
id: attributes.id + '-label',
class: this.config.classNames.hidden
}, i18n.get(type, this.config));
@@ -8154,7 +8202,13 @@ var controls = {
max: 100,
step: 0.01,
value: 0,
autocomplete: 'off'
autocomplete: 'off',
// A11y fixes for https://github.com/sampotts/plyr/issues/905
role: 'slider',
'aria-labelledby': attributes.id + '-label',
'aria-valuemin': 0,
'aria-valuemax': 100,
'aria-valuenow': 0
}, attributes));
this.elements.inputs[type] = input;
@@ -8174,7 +8228,9 @@ var controls = {
var progress = utils.createElement('progress', utils.extend(utils.getAttributesFromSelector(this.config.selectors.display[type]), {
min: 0,
max: 100,
value: 0
value: 0,
role: 'presentation',
'aria-hidden': true
}, attributes));
// Create the label inside
@@ -8206,16 +8262,14 @@ var controls = {
// Create time display
createTime: function createTime(type) {
var container = utils.createElement('div', {
class: 'plyr__time'
});
var attributes = utils.getAttributesFromSelector(this.config.selectors.display[type]);
container.appendChild(utils.createElement('span', {
class: this.config.classNames.hidden
}, i18n.get(type, this.config)));
container.appendChild(utils.createElement('span', utils.getAttributesFromSelector(this.config.selectors.display[type]), '00:00'));
var container = utils.createElement('div', utils.extend(attributes, {
class: 'plyr__time ' + attributes.class,
'aria-label': i18n.get(type, this.config)
}), '0:00');
// Reference for updates
this.elements.display[type] = container;
return container;
@@ -8241,7 +8295,7 @@ var controls = {
class: 'plyr__sr-only'
}));
var faux = utils.createElement('span', { 'aria-hidden': true });
var faux = utils.createElement('span', { hidden: '' });
label.appendChild(radio);
label.appendChild(faux);
@@ -8312,11 +8366,7 @@ var controls = {
// Hide/show a tab
toggleTab: function toggleTab(setting, toggle) {
var tab = this.elements.settings.tabs[setting];
var pane = this.elements.settings.panes[setting];
utils.toggleHidden(tab, !toggle);
utils.toggleHidden(pane, !toggle);
utils.toggleHidden(this.elements.settings.tabs[setting], !toggle);
},
@@ -8344,6 +8394,9 @@ var controls = {
var toggle = !utils.is.empty(this.options.quality) && this.options.quality.length > 1;
controls.toggleTab.call(this, type, toggle);
// Check if we need to toggle the parent
controls.checkMenu.call(this);
// If we're hiding, nothing more to do
if (!toggle) {
return;
@@ -8400,16 +8453,17 @@ var controls = {
getLabel: function getLabel(setting, value) {
switch (setting) {
case 'speed':
return value === 1 ? 'Normal' : value + '&times;';
return value === 1 ? i18n.get('normal', this.config) : value + '&times;';
case 'quality':
if (utils.is.number(value)) {
return value + 'p';
}
return utils.toTitleCase(value);
case 'captions':
return controls.getLanguage.call(this);
return captions.getLabel.call(this);
default:
return null;
@@ -8425,7 +8479,18 @@ var controls = {
switch (setting) {
case 'captions':
value = this.captions.active ? this.captions.language : i18n.get('disabled', this.config);
if (this.captions.active) {
if (this.options.captions.length > 2 || !this.options.captions.some(function (lang) {
return lang === 'enabled';
})) {
value = this.captions.language;
} else {
value = 'enabled';
}
} else {
value = '';
}
break;
default:
@@ -8456,17 +8521,19 @@ var controls = {
list = pane && pane.querySelector('ul');
}
// Update the label
if (!utils.is.empty(value)) {
var label = this.elements.settings.tabs[setting].querySelector('.' + this.config.classNames.menu.value);
label.innerHTML = controls.getLabel.call(this, setting, value);
// If there's no list it means it's not been rendered...
if (!utils.is.element(list)) {
return;
}
// Find the radio option
// Update the label
var label = this.elements.settings.tabs[setting].querySelector('.' + this.config.classNames.menu.value);
label.innerHTML = controls.getLabel.call(this, setting, value);
// Find the radio option and check it
var target = list && list.querySelector('input[value="' + value + '"]');
if (utils.is.element(target)) {
// Check it
target.checked = true;
}
},
@@ -8510,22 +8577,6 @@ var controls = {
// Get current selected caption language
// TODO: rework this to user the getter in the API?
getLanguage: function getLanguage() {
if (!this.supported.ui) {
return null;
}
if (support.textTracks && captions.getTracks.call(this).length && this.captions.active) {
var currentTrack = captions.getCurrentTrack.call(this);
if (utils.is.track(currentTrack)) {
return currentTrack.label;
}
}
return i18n.get('disabled', this.config);
},
// Set a list of available captions languages
setCaptionsMenu: function setCaptionsMenu() {
@@ -8542,6 +8593,9 @@ var controls = {
// Empty the menu
utils.emptyElement(list);
// Check if we need to toggle the parent
controls.checkMenu.call(this);
// If there's no captions, bail
if (!toggle) {
return;
@@ -8550,8 +8604,8 @@ var controls = {
// Re-map the tracks into just the data we need
var tracks = captions.getTracks.call(this).map(function (track) {
return {
language: track.language,
label: !utils.is.empty(track.label) ? track.label : track.language.toUpperCase()
language: !utils.is.empty(track.language) ? track.language : 'enabled',
label: captions.getLabel.call(_this3, track)
};
});
@@ -8563,7 +8617,12 @@ var controls = {
// Generate options
tracks.forEach(function (track) {
controls.createMenuItem.call(_this3, track.language, list, 'language', track.label || track.language, controls.createBadge.call(_this3, track.language.toUpperCase()), track.language.toLowerCase() === _this3.captions.language.toLowerCase());
controls.createMenuItem.call(_this3, track.language, list, 'language', track.label, track.language !== 'enabled' ? controls.createBadge.call(_this3, track.language.toUpperCase()) : null, track.language.toLowerCase() === _this3.captions.language.toLowerCase());
});
// Store reference
this.options.captions = tracks.map(function (track) {
return track.language;
});
controls.updateSetting.call(this, type, list);
@@ -8613,10 +8672,6 @@ var controls = {
// Get the list to populate
var list = this.elements.settings.panes.speed.querySelector('ul');
// Show the pane and tab
utils.toggleHidden(this.elements.settings.tabs.speed, false);
utils.toggleHidden(this.elements.settings.panes.speed, false);
// Empty the menu
utils.emptyElement(list);
@@ -8653,7 +8708,7 @@ var controls = {
return;
}
var show = utils.is.boolean(event) ? event : utils.is.element(form) && form.getAttribute('aria-hidden') === 'true';
var show = utils.is.boolean(event) ? event : utils.is.element(form) && form.hasAttribute('hidden');
if (utils.is.event(event)) {
var isMenuItem = utils.is.element(form) && form.contains(event.target);
@@ -8678,7 +8733,7 @@ var controls = {
}
if (utils.is.element(form)) {
form.setAttribute('aria-hidden', !show);
utils.toggleHidden(form, !show);
utils.toggleClass(this.elements.container, this.config.classNames.menu.open, show);
if (show) {
@@ -8695,7 +8750,7 @@ var controls = {
var clone = tab.cloneNode(true);
clone.style.position = 'absolute';
clone.style.opacity = 0;
clone.setAttribute('aria-hidden', false);
clone.removeAttribute('hidden');
// Prevent input's being unchecked due to the name being identical
Array.from(clone.querySelectorAll('input[name]')).forEach(function (input) {
@@ -8741,7 +8796,7 @@ var controls = {
// Hide all other tabs
// Get other tabs
var current = menu.querySelector('[role="tabpanel"][aria-hidden="false"]');
var current = menu.querySelector('[role="tabpanel"]:not([hidden])');
var container = current.parentNode;
// Set other toggles to be expanded false
@@ -8782,11 +8837,11 @@ var controls = {
}
// Set attributes on current tab
current.setAttribute('aria-hidden', true);
utils.toggleHidden(current, true);
current.setAttribute('tabindex', -1);
// Set attributes on target
pane.setAttribute('aria-hidden', !show);
utils.toggleHidden(pane, !show);
tab.setAttribute('aria-expanded', show);
pane.removeAttribute('tabindex');
@@ -8921,7 +8976,7 @@ var controls = {
var form = utils.createElement('form', {
class: 'plyr__menu__container',
id: 'plyr-settings-' + data.id,
'aria-hidden': true,
hidden: '',
'aria-labelled-by': 'plyr-settings-toggle-' + data.id,
role: 'tablist',
tabindex: -1
@@ -8931,7 +8986,6 @@ var controls = {
var home = utils.createElement('div', {
id: 'plyr-settings-' + data.id + '-home',
'aria-hidden': false,
'aria-labelled-by': 'plyr-settings-toggle-' + data.id,
role: 'tabpanel'
});
@@ -8978,11 +9032,10 @@ var controls = {
this.config.settings.forEach(function (type) {
var pane = utils.createElement('div', {
id: 'plyr-settings-' + data.id + '-' + type,
'aria-hidden': true,
hidden: '',
'aria-labelled-by': 'plyr-settings-' + data.id + '-' + type + '-tab',
role: 'tabpanel',
tabindex: -1,
hidden: ''
tabindex: -1
});
var back = utils.createElement('button', {
@@ -9064,17 +9117,21 @@ var controls = {
var container = null;
this.elements.controls = null;
// HTML or Element passed as the option
// Set template properties
var props = {
id: this.id,
seektime: this.config.seekTime,
title: this.config.title
};
var update = true;
if (utils.is.string(this.config.controls) || utils.is.element(this.config.controls)) {
// String or HTMLElement passed as the option
container = this.config.controls;
} else if (utils.is.function(this.config.controls)) {
// A custom function to build controls
// The function can return a HTMLElement or String
container = this.config.controls({
id: this.id,
seektime: this.config.seekTime,
title: this.config.title
});
container = this.config.controls.call(this, props);
} else {
// Create controls
container = controls.create.call(this, {
@@ -9082,10 +9139,35 @@ var controls = {
seektime: this.config.seekTime,
speed: this.speed,
quality: this.quality,
captions: controls.getLanguage.call(this)
captions: captions.getLabel.call(this)
// TODO: Looping
// loop: 'None',
});
update = false;
}
// Replace props with their value
var replace = function replace(input) {
var result = input;
Object.entries(props).forEach(function (_ref) {
var _ref2 = slicedToArray(_ref, 2),
key = _ref2[0],
value = _ref2[1];
result = utils.replaceAll(result, '{' + key + '}', value);
});
return result;
};
// Update markup
if (update) {
if (utils.is.string(this.config.controls)) {
container = replace(container);
} else if (utils.is.element(container)) {
container.innerHTML = replace(container.innerHTML);
}
}
// Controls container
@@ -9104,7 +9186,7 @@ var controls = {
// Inject controls HTML
if (utils.is.element(container)) {
target.appendChild(container);
} else {
} else if (container) {
target.insertAdjacentHTML('beforeend', container);
}
@@ -9387,7 +9469,7 @@ var Listeners = function () {
});
// Display duration
utils.on(this.player.media, 'durationchange loadedmetadata', function (event) {
utils.on(this.player.media, 'durationchange loadeddata loadedmetadata', function (event) {
return ui.durationUpdate.call(_this3.player, event);
});
@@ -9436,6 +9518,10 @@ var Listeners = function () {
// If autoplay, then load advertisement if required
// TODO: Show some sort of loading state while the ad manager loads else there's a delay before ad shows
utils.on(this.player.media, 'playing', function () {
if (!_this3.player.ads) {
return;
}
// If ads are enabled, wait for them first
if (_this3.player.ads.enabled && !_this3.player.ads.initialized) {
// Wait for manager response
@@ -9477,7 +9563,7 @@ var Listeners = function () {
// Disable right click
if (this.player.supported.ui && this.player.config.disableContextMenu) {
utils.on(this.player.media, 'contextmenu', function (event) {
utils.on(this.player.elements.wrapper, 'contextmenu', function (event) {
event.preventDefault();
}, false);
}
@@ -10763,7 +10849,11 @@ var youtube = {
return Number(instance.getCurrentTime());
},
set: function set(time) {
// Vimeo will automatically play on seek
var paused = player.media.paused;
// Set seeking flag
player.media.seeking = true;
// Trigger seeking
@@ -10771,6 +10861,11 @@ var youtube = {
// Seek after events sent
instance.seekTo(time);
// Restore pause state
if (paused) {
player.pause();
}
}
});
@@ -11008,10 +11103,14 @@ var vimeo = {
setAspectRatio: function setAspectRatio(input) {
var ratio = utils.is.string(input) ? input.split(':') : this.config.ratio.split(':');
var padding = 100 / ratio[0] * ratio[1];
var height = 240;
var offset = (height - padding) / (height / 50);
this.elements.wrapper.style.paddingBottom = padding + '%';
this.media.style.transform = 'translateY(-' + offset + '%)';
if (this.supported.ui) {
var height = 240;
var offset = (height - padding) / (height / 50);
this.media.style.transform = 'translateY(-' + offset + '%)';
}
},
@@ -11030,7 +11129,8 @@ var vimeo = {
title: false,
speed: true,
transparent: 0,
gesture: 'media'
gesture: 'media',
playsinline: !this.config.fullscreen.iosNative
};
var params = utils.buildUrlParams(options);
@@ -11064,6 +11164,11 @@ var vimeo = {
player.media.paused = true;
player.media.currentTime = 0;
// Disable native text track rendering
if (player.supported.ui) {
player.embed.disableTextTrack();
}
// Create a faux HTML5 API using the Vimeo API
player.media.play = function () {
player.embed.play().then(function () {
@@ -11102,7 +11207,9 @@ var vimeo = {
utils.dispatchEvent.call(player, player.media, 'seeking');
// Seek after events
player.embed.setCurrentTime(time);
player.embed.setCurrentTime(time).catch(function () {
// Do nothing
});
// Restore pause state
if (paused) {
@@ -11282,6 +11389,15 @@ var vimeo = {
if (parseInt(data.percent, 10) === 1) {
utils.dispatchEvent.call(player, player.media, 'canplaythrough');
}
// Get duration as if we do it before load, it gives an incorrect value
// https://github.com/sampotts/plyr/issues/891
player.embed.getDuration().then(function (value) {
if (value !== player.media.duration) {
player.media.duration = value;
utils.dispatchEvent.call(player, player.media, 'durationchange');
}
});
});
player.embed.on('seeked', function () {
@@ -11433,7 +11549,7 @@ var source = {
_this2.provider = !utils.is.empty(input.sources[0].provider) ? input.sources[0].provider : providers.html5;
// Check for support
_this2.supported = support.check(_this2.type, _this2.provider, _this2.config.inline);
_this2.supported = support.check(_this2.type, _this2.provider, _this2.config.playsinline);
// Create new markup
switch (_this2.provider + ':' + _this2.type) {
@@ -11481,7 +11597,7 @@ var source = {
if (_this2.config.muted) {
_this2.media.setAttribute('muted', '');
}
if (_this2.config.inline) {
if (_this2.config.playsinline) {
_this2.media.setAttribute('playsinline', '');
}
}
@@ -11562,7 +11678,7 @@ var Plyr = function () {
}
// Set config
this.config = utils.extend({}, defaults, options, function () {
this.config = utils.extend({}, defaults, options || {}, function () {
try {
return JSON.parse(_this.media.getAttribute('data-plyr-config'));
} catch (e) {
@@ -11599,7 +11715,8 @@ var Plyr = function () {
// Options
this.options = {
speed: [],
quality: []
quality: [],
captions: []
};
// Debugging
@@ -11684,12 +11801,17 @@ var Plyr = function () {
if (truthy.includes(params.autoplay)) {
this.config.autoplay = true;
}
if (truthy.includes(params.playsinline)) {
this.config.inline = true;
}
if (truthy.includes(params.loop)) {
this.config.loop.active = true;
}
// TODO: replace fullscreen.iosNative with this playsinline config option
// YouTube requires the playsinline in the URL
if (this.isYouTube) {
this.config.playsinline = truthy.includes(params.playsinline);
} else {
this.config.playsinline = true;
}
}
} else {
// <div> with attributes
@@ -11723,7 +11845,7 @@ var Plyr = function () {
this.config.autoplay = true;
}
if (this.media.hasAttribute('playsinline')) {
this.config.inline = true;
this.config.playsinline = true;
}
if (this.media.hasAttribute('muted')) {
this.config.muted = true;
@@ -11740,7 +11862,7 @@ var Plyr = function () {
}
// Check for support again but with type
this.supported = support.check(this.type, this.provider, this.config.inline);
this.supported = support.check(this.type, this.provider, this.config.playsinline);
// If no support for even API, bail
if (!this.supported.api) {
@@ -11960,13 +12082,13 @@ var Plyr = function () {
* @param {boolean} input - Whether to enable captions
*/
value: function toggleCaptions(input) {
// If there's no full support, or there's no caption toggle
if (!this.supported.ui || !utils.is.element(this.elements.buttons.captions)) {
// If there's no full support
if (!this.supported.ui) {
return;
}
// If the method is called without parameter, toggle based on current value
var show = utils.is.boolean(input) ? input : this.elements.container.className.indexOf(this.config.classNames.captions.active) === -1;
var show = utils.is.boolean(input) ? input : !this.elements.container.classList.contains(this.config.classNames.captions.active);
// Nothing to change...
if (this.captions.active === show) {
@@ -12325,7 +12447,7 @@ var Plyr = function () {
}, {
key: 'playing',
get: function get() {
return Boolean(!this.paused && !this.ended && (this.isHTML5 ? this.media.readyState > 2 : true));
return Boolean(this.ready && !this.paused && !this.ended && (this.isHTML5 ? this.media.readyState > 2 : true));
}
/**
@@ -12354,7 +12476,7 @@ var Plyr = function () {
}
// Set
this.media.currentTime = parseFloat(targetTime.toFixed(4));
this.media.currentTime = targetTime;
// Logging
this.debug.log('Seeking to ' + this.currentTime + ' seconds');
@@ -12411,7 +12533,7 @@ var Plyr = function () {
key: 'duration',
get: function get() {
// Faux duration set via config
var fauxDuration = parseInt(this.config.duration, 10);
var fauxDuration = parseFloat(this.config.duration);
// True duration
var realDuration = this.media ? Number(this.media.duration) : 0;
@@ -12762,17 +12884,29 @@ var Plyr = function () {
return;
}
// Toggle captions based on input
this.toggleCaptions(!utils.is.empty(input));
// If empty string is passed, assume disable captions
if (utils.is.empty(input)) {
this.toggleCaptions(false);
return;
}
// Normalize
var language = input.toLowerCase();
// Check for support
if (!this.options.captions.includes(language)) {
this.debug.warn('Unsupported language option: ' + language);
return;
}
// Ensure captions are enabled
this.toggleCaptions(true);
// Enabled only
if (language === 'enabled') {
return;
}
// If nothing to change, bail
if (this.language === language) {
return;
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+9 -8
View File
@@ -1,6 +1,6 @@
{
"name": "plyr",
"version": "3.1.0-beta.2",
"version": "3.2.4",
"description": "A simple, accessible and customizable HTML5, YouTube and Vimeo media player",
"homepage": "https://plyr.io",
"main": "./dist/plyr.js",
@@ -9,14 +9,14 @@
"style": "./dist/plyr.css",
"devDependencies": {
"babel-core": "^6.26.0",
"babel-eslint": "^8.2.2",
"babel-eslint": "^8.2.3",
"babel-plugin-external-helpers": "^6.22.0",
"babel-preset-env": "^1.6.1",
"del": "^3.0.0",
"eslint": "^4.19.1",
"eslint-config-airbnb-base": "^12.1.0",
"eslint-config-prettier": "^2.9.0",
"eslint-plugin-import": "^2.10.0",
"eslint-plugin-import": "^2.11.0",
"git-branch": "^2.0.1",
"gulp": "^3.9.1",
"gulp-autoprefixer": "^5.0.0",
@@ -28,7 +28,7 @@
"gulp-rename": "^1.2.2",
"gulp-replace": "^0.6.1",
"gulp-s3": "^0.11.0",
"gulp-sass": "^3.2.1",
"gulp-sass": "^4.0.1",
"gulp-size": "^3.0.0",
"gulp-sourcemaps": "^2.6.4",
"gulp-svgmin": "^1.2.4",
@@ -37,16 +37,16 @@
"gulp-util": "^3.0.8",
"prettier-eslint": "^8.8.1",
"prettier-stylelint": "^0.4.2",
"rollup-plugin-babel": "^3.0.3",
"rollup-plugin-babel": "^3.0.4",
"rollup-plugin-commonjs": "^9.1.0",
"rollup-plugin-node-resolve": "^3.3.0",
"run-sequence": "^2.2.1",
"stylelint": "^9.2.0",
"stylelint-config-prettier": "^3.0.4",
"stylelint-config-prettier": "^3.2.0",
"stylelint-config-recommended": "^2.1.0",
"stylelint-config-sass-guidelines": "^5.0.0",
"stylelint-order": "^0.8.1",
"stylelint-scss": "^3.0.0",
"stylelint-scss": "^3.0.1",
"stylelint-selector-bem-pattern": "^2.0.0"
},
"keywords": ["HTML5 Video", "HTML5 Audio", "Media Player", "DASH", "Shaka", "WordPress", "HLS"],
@@ -69,6 +69,7 @@
"babel-polyfill": "^6.26.0",
"custom-event-polyfill": "^0.3.0",
"loadjs": "^3.5.4",
"raven-js": "^3.24.0"
"npm": "^6.0.0",
"raven-js": "^3.24.2"
}
}
+3 -3
View File
@@ -128,7 +128,7 @@ See [initialising](#initialising) for more information on advanced setups.
If you want to use our CDN (provided by [Fastly](https://www.fastly.com/)) for the JavaScript, you can use the following:
```html
<script src="https://cdn.plyr.io/3.1.0-beta.2/plyr.js"></script>
<script src="https://cdn.plyr.io/3.2.4/plyr.js"></script>
```
_Note_: Be sure to read the [polyfills](#polyfills) section below about browser compatibility
@@ -144,13 +144,13 @@ Include the `plyr.css` stylsheet into your `<head>`
If you want to use our CDN (provided by [Fastly](https://www.fastly.com/)) for the default CSS, you can use the following:
```html
<link rel="stylesheet" href="https://cdn.plyr.io/3.1.0-beta.2/plyr.css">
<link rel="stylesheet" href="https://cdn.plyr.io/3.2.4/plyr.css">
```
### SVG Sprite
The SVG sprite is loaded automatically from our CDN (provided by [Fastly](https://www.fastly.com/)). To change this, see the [options](#options) below. For
reference, the CDN hosted SVG sprite can be found at `https://cdn.plyr.io/3.1.0-beta.2/plyr.svg`.
reference, the CDN hosted SVG sprite can be found at `https://cdn.plyr.io/3.2.4/plyr.svg`.
## Ads
+45 -6
View File
@@ -6,6 +6,7 @@
import support from './support';
import utils from './utils';
import controls from './controls';
import i18n from './i18n';
const captions = {
// Setup captions
@@ -46,6 +47,7 @@ const captions = {
return;
}
// Inject the container
if (!utils.is.element(this.elements.captions)) {
this.elements.captions = utils.createElement('div', utils.getAttributesFromSelector(this.config.selectors.captions));
@@ -148,7 +150,49 @@ const captions = {
// Get the current track for the current language
getCurrentTrack() {
return captions.getTracks.call(this).find(track => track.language.toLowerCase() === this.language);
const tracks = captions.getTracks.call(this);
if (!tracks.length) {
return null;
}
// Get track based on current language
let track = tracks.find(track => track.language.toLowerCase() === this.language);
// Get the <track> with default attribute
if (!track) {
track = utils.getElement.call(this, 'track[default]');
}
// Get the first track
if (!track) {
[track] = tracks;
}
return track;
},
// Get UI label for track
getLabel(track) {
let currentTrack = track;
if (!utils.is.track(currentTrack) && support.textTracks && this.captions.active) {
currentTrack = captions.getCurrentTrack.call(this);
}
if (utils.is.track(currentTrack)) {
if (!utils.is.empty(currentTrack.label)) {
return currentTrack.label;
}
if (!utils.is.empty(currentTrack.language)) {
return track.language.toUpperCase();
}
return i18n.get('enabled', this.config);
}
return i18n.get('disabled', this.config);
},
// Display active caption if it contains text
@@ -206,11 +250,6 @@ const captions = {
// Display captions container and button (for initialization)
show() {
// If there's no caption toggle, bail
if (!utils.is.element(this.elements.buttons.captions)) {
return;
}
// Try to load the value from storage
let active = this.storage.get('captions');
+113 -83
View File
@@ -15,11 +15,6 @@ const browser = utils.getBrowser();
const controls = {
// Webkit polyfill for lower fill range
updateRangeFill(target) {
// WebKit only
if (!browser.isWebkit) {
return;
}
// Get range from event if event passed
const range = utils.is.event(target) ? target.target : target;
@@ -28,6 +23,14 @@ const controls = {
return;
}
// Set aria value for https://github.com/sampotts/plyr/issues/905
range.setAttribute('aria-valuenow', range.value);
// WebKit only
if (!browser.isWebkit) {
return;
}
// Set CSS custom property
range.style.setProperty('--value', `${range.value / range.max * 100}%`);
},
@@ -52,6 +55,7 @@ const controls = {
icon,
utils.extend(attributes, {
role: 'presentation',
focusable: 'false',
}),
);
@@ -206,7 +210,6 @@ const controls = {
// Add aria attributes
attributes['aria-pressed'] = false;
attributes['aria-label'] = i18n.get(label, this.config);
} else {
button.appendChild(controls.createIcon.call(this, icon));
button.appendChild(controls.createLabel.call(this, label));
@@ -238,6 +241,7 @@ const controls = {
'label',
{
for: attributes.id,
id: `${attributes.id}-label`,
class: this.config.classNames.hidden,
},
i18n.get(type, this.config),
@@ -255,6 +259,12 @@ const controls = {
step: 0.01,
value: 0,
autocomplete: 'off',
// A11y fixes for https://github.com/sampotts/plyr/issues/905
role: 'slider',
'aria-labelledby': `${attributes.id}-label`,
'aria-valuemin': 0,
'aria-valuemax': 100,
'aria-valuenow': 0,
},
attributes,
),
@@ -281,6 +291,8 @@ const controls = {
min: 0,
max: 100,
value: 0,
role: 'presentation',
'aria-hidden': true,
},
attributes,
),
@@ -314,22 +326,14 @@ const controls = {
// Create time display
createTime(type) {
const container = utils.createElement('div', {
class: 'plyr__time',
});
const attributes = utils.getAttributesFromSelector(this.config.selectors.display[type]);
container.appendChild(
utils.createElement(
'span',
{
class: this.config.classNames.hidden,
},
i18n.get(type, this.config),
),
);
container.appendChild(utils.createElement('span', utils.getAttributesFromSelector(this.config.selectors.display[type]), '00:00'));
const container = utils.createElement('div', utils.extend(attributes, {
class: `plyr__time ${attributes.class}`,
'aria-label': i18n.get(type, this.config),
}), '0:00');
// Reference for updates
this.elements.display[type] = container;
return container;
@@ -354,7 +358,7 @@ const controls = {
}),
);
const faux = utils.createElement('span', { 'aria-hidden': true });
const faux = utils.createElement('span', { hidden: '' });
label.appendChild(radio);
label.appendChild(faux);
@@ -429,11 +433,7 @@ const controls = {
// Hide/show a tab
toggleTab(setting, toggle) {
const tab = this.elements.settings.tabs[setting];
const pane = this.elements.settings.panes[setting];
utils.toggleHidden(tab, !toggle);
utils.toggleHidden(pane, !toggle);
utils.toggleHidden(this.elements.settings.tabs[setting], !toggle);
},
// Set the quality menu
@@ -456,6 +456,9 @@ const controls = {
const toggle = !utils.is.empty(this.options.quality) && this.options.quality.length > 1;
controls.toggleTab.call(this, type, toggle);
// Check if we need to toggle the parent
controls.checkMenu.call(this);
// If we're hiding, nothing more to do
if (!toggle) {
return;
@@ -495,13 +498,15 @@ const controls = {
};
// Sort options by the config and then render options
this.options.quality.sort((a, b) => {
const sorting = this.config.quality.options;
return sorting.indexOf(a) > sorting.indexOf(b) ? 1 : -1;
}).forEach(quality => {
const label = controls.getLabel.call(this, 'quality', quality);
controls.createMenuItem.call(this, quality, list, type, label, getBadge(quality));
});
this.options.quality
.sort((a, b) => {
const sorting = this.config.quality.options;
return sorting.indexOf(a) > sorting.indexOf(b) ? 1 : -1;
})
.forEach(quality => {
const label = controls.getLabel.call(this, 'quality', quality);
controls.createMenuItem.call(this, quality, list, type, label, getBadge(quality));
});
controls.updateSetting.call(this, type, list);
},
@@ -511,16 +516,17 @@ const controls = {
getLabel(setting, value) {
switch (setting) {
case 'speed':
return value === 1 ? 'Normal' : `${value}&times;`;
return value === 1 ? i18n.get('normal', this.config) : `${value}&times;`;
case 'quality':
if (utils.is.number(value)) {
return `${value}p`;
}
return utils.toTitleCase(value);
case 'captions':
return controls.getLanguage.call(this);
return captions.getLabel.call(this);
default:
return null;
@@ -535,7 +541,16 @@ const controls = {
switch (setting) {
case 'captions':
value = this.captions.active ? this.captions.language : i18n.get('disabled', this.config);
if (this.captions.active) {
if (this.options.captions.length > 2 || !this.options.captions.some(lang => lang === 'enabled')) {
value = this.captions.language;
} else {
value = 'enabled';
}
} else {
value = '';
}
break;
default:
@@ -566,17 +581,19 @@ const controls = {
list = pane && pane.querySelector('ul');
}
// Update the label
if (!utils.is.empty(value)) {
const label = this.elements.settings.tabs[setting].querySelector(`.${this.config.classNames.menu.value}`);
label.innerHTML = controls.getLabel.call(this, setting, value);
// If there's no list it means it's not been rendered...
if (!utils.is.element(list)) {
return;
}
// Find the radio option
// Update the label
const label = this.elements.settings.tabs[setting].querySelector(`.${this.config.classNames.menu.value}`);
label.innerHTML = controls.getLabel.call(this, setting, value);
// Find the radio option and check it
const target = list && list.querySelector(`input[value="${value}"]`);
if (utils.is.element(target)) {
// Check it
target.checked = true;
}
},
@@ -627,21 +644,6 @@ const controls = {
// Get current selected caption language
// TODO: rework this to user the getter in the API?
getLanguage() {
if (!this.supported.ui) {
return null;
}
if (support.textTracks && captions.getTracks.call(this).length && this.captions.active) {
const currentTrack = captions.getCurrentTrack.call(this);
if (utils.is.track(currentTrack)) {
return currentTrack.label;
}
}
return i18n.get('disabled', this.config);
},
// Set a list of available captions languages
setCaptionsMenu() {
@@ -656,6 +658,9 @@ const controls = {
// Empty the menu
utils.emptyElement(list);
// Check if we need to toggle the parent
controls.checkMenu.call(this);
// If there's no captions, bail
if (!toggle) {
return;
@@ -663,8 +668,8 @@ const controls = {
// Re-map the tracks into just the data we need
const tracks = captions.getTracks.call(this).map(track => ({
language: track.language,
label: !utils.is.empty(track.label) ? track.label : track.language.toUpperCase(),
language: !utils.is.empty(track.language) ? track.language : 'enabled',
label: captions.getLabel.call(this, track),
}));
// Add the "Disabled" option to turn off captions
@@ -680,12 +685,15 @@ const controls = {
track.language,
list,
'language',
track.label || track.language,
controls.createBadge.call(this, track.language.toUpperCase()),
track.label,
track.language !== 'enabled' ? controls.createBadge.call(this, track.language.toUpperCase()) : null,
track.language.toLowerCase() === this.captions.language.toLowerCase(),
);
});
// Store reference
this.options.captions = tracks.map(track => track.language);
controls.updateSetting.call(this, type, list);
},
@@ -736,10 +744,6 @@ const controls = {
// Get the list to populate
const list = this.elements.settings.panes.speed.querySelector('ul');
// Show the pane and tab
utils.toggleHidden(this.elements.settings.tabs.speed, false);
utils.toggleHidden(this.elements.settings.panes.speed, false);
// Empty the menu
utils.emptyElement(list);
@@ -770,7 +774,7 @@ const controls = {
return;
}
const show = utils.is.boolean(event) ? event : utils.is.element(form) && form.getAttribute('aria-hidden') === 'true';
const show = utils.is.boolean(event) ? event : utils.is.element(form) && form.hasAttribute('hidden');
if (utils.is.event(event)) {
const isMenuItem = utils.is.element(form) && form.contains(event.target);
@@ -795,7 +799,7 @@ const controls = {
}
if (utils.is.element(form)) {
form.setAttribute('aria-hidden', !show);
utils.toggleHidden(form, !show);
utils.toggleClass(this.elements.container, this.config.classNames.menu.open, show);
if (show) {
@@ -811,7 +815,7 @@ const controls = {
const clone = tab.cloneNode(true);
clone.style.position = 'absolute';
clone.style.opacity = 0;
clone.setAttribute('aria-hidden', false);
clone.removeAttribute('hidden');
// Prevent input's being unchecked due to the name being identical
Array.from(clone.querySelectorAll('input[name]')).forEach(input => {
@@ -855,7 +859,7 @@ const controls = {
// Hide all other tabs
// Get other tabs
const current = menu.querySelector('[role="tabpanel"][aria-hidden="false"]');
const current = menu.querySelector('[role="tabpanel"]:not([hidden])');
const container = current.parentNode;
// Set other toggles to be expanded false
@@ -899,11 +903,11 @@ const controls = {
}
// Set attributes on current tab
current.setAttribute('aria-hidden', true);
utils.toggleHidden(current, true);
current.setAttribute('tabindex', -1);
// Set attributes on target
pane.setAttribute('aria-hidden', !show);
utils.toggleHidden(pane, !show);
tab.setAttribute('aria-expanded', show);
pane.removeAttribute('tabindex');
@@ -1045,7 +1049,7 @@ const controls = {
const form = utils.createElement('form', {
class: 'plyr__menu__container',
id: `plyr-settings-${data.id}`,
'aria-hidden': true,
hidden: '',
'aria-labelled-by': `plyr-settings-toggle-${data.id}`,
role: 'tablist',
tabindex: -1,
@@ -1055,7 +1059,6 @@ const controls = {
const home = utils.createElement('div', {
id: `plyr-settings-${data.id}-home`,
'aria-hidden': false,
'aria-labelled-by': `plyr-settings-toggle-${data.id}`,
role: 'tabpanel',
});
@@ -1106,11 +1109,10 @@ const controls = {
this.config.settings.forEach(type => {
const pane = utils.createElement('div', {
id: `plyr-settings-${data.id}-${type}`,
'aria-hidden': true,
hidden: '',
'aria-labelled-by': `plyr-settings-${data.id}-${type}-tab`,
role: 'tabpanel',
tabindex: -1,
hidden: '',
});
const back = utils.createElement(
@@ -1193,17 +1195,21 @@ const controls = {
let container = null;
this.elements.controls = null;
// HTML or Element passed as the option
// Set template properties
const props = {
id: this.id,
seektime: this.config.seekTime,
title: this.config.title,
};
let update = true;
if (utils.is.string(this.config.controls) || utils.is.element(this.config.controls)) {
// String or HTMLElement passed as the option
container = this.config.controls;
} else if (utils.is.function(this.config.controls)) {
// A custom function to build controls
// The function can return a HTMLElement or String
container = this.config.controls({
id: this.id,
seektime: this.config.seekTime,
title: this.config.title,
});
container = this.config.controls.call(this, props);
} else {
// Create controls
container = controls.create.call(this, {
@@ -1211,10 +1217,34 @@ const controls = {
seektime: this.config.seekTime,
speed: this.speed,
quality: this.quality,
captions: controls.getLanguage.call(this),
captions: captions.getLabel.call(this),
// TODO: Looping
// loop: 'None',
});
update = false;
}
// Replace props with their value
const replace = input => {
let result = input;
Object.entries(props).forEach(([
key,
value,
]) => {
result = utils.replaceAll(result, `{${key}}`, value);
});
return result;
};
// Update markup
if (update) {
if (utils.is.string(this.config.controls)) {
container = replace(container);
} else if (utils.is.element(container)) {
container.innerHTML = replace(container.innerHTML);
}
}
// Controls container
@@ -1233,7 +1263,7 @@ const controls = {
// Inject controls HTML
if (utils.is.element(container)) {
target.appendChild(container);
} else {
} else if (container) {
target.insertAdjacentHTML('beforeend', container);
}
+4 -2
View File
@@ -56,7 +56,7 @@ const defaults = {
// Sprite (for icons)
loadSprite: true,
iconPrefix: 'plyr',
iconUrl: 'https://cdn.plyr.io/3.1.0-beta.2/plyr.svg',
iconUrl: 'https://cdn.plyr.io/3.2.4/plyr.svg',
// Blank video (used to prevent errors on source change)
blankVideo: 'https://cdn.plyr.io/static/blank.mp4',
@@ -115,7 +115,7 @@ const defaults = {
// Captions settings
captions: {
active: false,
language: window.navigator.language.split('-')[0],
language: (navigator.language || navigator.userLanguage).split('-')[0],
},
// Fullscreen settings
@@ -177,6 +177,7 @@ const defaults = {
captions: 'Captions',
settings: 'Settings',
speed: 'Speed',
normal: 'Normal',
quality: 'Quality',
loop: 'Loop',
start: 'Start',
@@ -184,6 +185,7 @@ const defaults = {
all: 'All',
reset: 'Reset',
disabled: 'Disabled',
enabled: 'Enabled',
advertisement: 'Ad',
},
+8 -8
View File
@@ -55,7 +55,7 @@ class Fullscreen {
// Get prefix
this.prefix = Fullscreen.prefix;
this.name = Fullscreen.name;
this.property = Fullscreen.property;
// Scroll position
this.scrollPosition = { x: 0, y: 0 };
@@ -70,7 +70,7 @@ class Fullscreen {
// Fullscreen toggle on double click
utils.on(this.player.elements.container, 'dblclick', event => {
// Ignore double click in controls
if (this.player.elements.controls.contains(event.target)) {
if (utils.is.element(this.player.elements.controls) && this.player.elements.controls.contains(event.target)) {
return;
}
@@ -90,7 +90,7 @@ class Fullscreen {
static get prefix() {
// No prefix
if (utils.is.function(document.exitFullscreen)) {
return false;
return '';
}
// Check for fullscreen support by vendor prefix
@@ -113,7 +113,7 @@ class Fullscreen {
return value;
}
static get name() {
static get property() {
return this.prefix === 'moz' ? 'FullScreen' : 'Fullscreen';
}
@@ -138,7 +138,7 @@ class Fullscreen {
return utils.hasClass(this.target, this.player.config.classNames.fullscreen.fallback);
}
const element = !this.prefix ? document.fullscreenElement : document[`${this.prefix}${this.name}Element`];
const element = !this.prefix ? document.fullscreenElement : document[`${this.prefix}${this.property}Element`];
return element === this.target;
}
@@ -176,7 +176,7 @@ class Fullscreen {
} else if (!this.prefix) {
this.target.requestFullscreen();
} else if (!utils.is.empty(this.prefix)) {
this.target[`${this.prefix}Request${this.name}`]();
this.target[`${this.prefix}Request${this.property}`]();
}
}
@@ -193,10 +193,10 @@ class Fullscreen {
} else if (!Fullscreen.native) {
toggleFallback.call(this, false);
} else if (!this.prefix) {
document.cancelFullScreen();
(document.cancelFullScreen || document.exitFullscreen).call(document);
} else if (!utils.is.empty(this.prefix)) {
const action = this.prefix === 'moz' ? 'Cancel' : 'Exit';
document[`${this.prefix}${action}${this.name}`]();
document[`${this.prefix}${action}${this.property}`]();
}
}
+6 -3
View File
@@ -2,7 +2,6 @@
// Plyr Event Listeners
// ==========================================================================
import support from './support';
import utils from './utils';
import controls from './controls';
import ui from './ui';
@@ -254,7 +253,7 @@ class Listeners {
utils.on(this.player.media, 'timeupdate seeking', event => ui.timeUpdate.call(this.player, event));
// Display duration
utils.on(this.player.media, 'durationchange loadedmetadata', event => ui.durationUpdate.call(this.player, event));
utils.on(this.player.media, 'durationchange loadeddata loadedmetadata', event => ui.durationUpdate.call(this.player, event));
// Check for audio tracks on load
// We can't use `loadedmetadata` as it doesn't seem to have audio tracks at that point
@@ -293,6 +292,10 @@ class Listeners {
// If autoplay, then load advertisement if required
// TODO: Show some sort of loading state while the ad manager loads else there's a delay before ad shows
utils.on(this.player.media, 'playing', () => {
if (!this.player.ads) {
return;
}
// If ads are enabled, wait for them first
if (this.player.ads.enabled && !this.player.ads.initialized) {
// Wait for manager response
@@ -331,7 +334,7 @@ class Listeners {
// Disable right click
if (this.player.supported.ui && this.player.config.disableContextMenu) {
utils.on(
this.player.media,
this.player.elements.wrapper,
'contextmenu',
event => {
event.preventDefault();
+25 -4
View File
@@ -35,10 +35,14 @@ const vimeo = {
setAspectRatio(input) {
const ratio = utils.is.string(input) ? input.split(':') : this.config.ratio.split(':');
const padding = 100 / ratio[0] * ratio[1];
const height = 240;
const offset = (height - padding) / (height / 50);
this.elements.wrapper.style.paddingBottom = `${padding}%`;
this.media.style.transform = `translateY(-${offset}%)`;
if (this.supported.ui) {
const height = 240;
const offset = (height - padding) / (height / 50);
this.media.style.transform = `translateY(-${offset}%)`;
}
},
// API Ready
@@ -55,6 +59,7 @@ const vimeo = {
speed: true,
transparent: 0,
gesture: 'media',
playsinline: !this.config.fullscreen.iosNative,
};
const params = utils.buildUrlParams(options);
@@ -88,6 +93,11 @@ const vimeo = {
player.media.paused = true;
player.media.currentTime = 0;
// Disable native text track rendering
if (player.supported.ui) {
player.embed.disableTextTrack();
}
// Create a faux HTML5 API using the Vimeo API
player.media.play = () => {
player.embed.play().then(() => {
@@ -124,7 +134,9 @@ const vimeo = {
utils.dispatchEvent.call(player, player.media, 'seeking');
// Seek after events
player.embed.setCurrentTime(time);
player.embed.setCurrentTime(time).catch(() => {
// Do nothing
});
// Restore pause state
if (paused) {
@@ -310,6 +322,15 @@ const vimeo = {
if (parseInt(data.percent, 10) === 1) {
utils.dispatchEvent.call(player, player.media, 'canplaythrough');
}
// Get duration as if we do it before load, it gives an incorrect value
// https://github.com/sampotts/plyr/issues/891
player.embed.getDuration().then(value => {
if (value !== player.media.duration) {
player.media.duration = value;
utils.dispatchEvent.call(player, player.media, 'durationchange');
}
});
});
player.embed.on('seeked', () => {
+8
View File
@@ -270,6 +270,9 @@ const youtube = {
return Number(instance.getCurrentTime());
},
set(time) {
// Vimeo will automatically play on seek
const { paused } = player.media;
// Set seeking flag
player.media.seeking = true;
@@ -278,6 +281,11 @@ const youtube = {
// Seek after events sent
instance.seekTo(time);
// Restore pause state
if (paused) {
player.pause();
}
},
});
+34 -16
View File
@@ -1,6 +1,6 @@
// ==========================================================================
// Plyr
// plyr.js v3.1.0-beta.2
// plyr.js v3.2.4
// https://github.com/sampotts/plyr
// License: The MIT License (MIT)
// ==========================================================================
@@ -57,7 +57,7 @@ class Plyr {
this.config = utils.extend(
{},
defaults,
options,
options || {},
(() => {
try {
return JSON.parse(this.media.getAttribute('data-plyr-config'));
@@ -97,6 +97,7 @@ class Plyr {
this.options = {
speed: [],
quality: [],
captions: [],
};
// Debugging
@@ -184,12 +185,17 @@ class Plyr {
if (truthy.includes(params.autoplay)) {
this.config.autoplay = true;
}
if (truthy.includes(params.playsinline)) {
this.config.inline = true;
}
if (truthy.includes(params.loop)) {
this.config.loop.active = true;
}
// TODO: replace fullscreen.iosNative with this playsinline config option
// YouTube requires the playsinline in the URL
if (this.isYouTube) {
this.config.playsinline = truthy.includes(params.playsinline);
} else {
this.config.playsinline = true;
}
}
} else {
// <div> with attributes
@@ -223,7 +229,7 @@ class Plyr {
this.config.autoplay = true;
}
if (this.media.hasAttribute('playsinline')) {
this.config.inline = true;
this.config.playsinline = true;
}
if (this.media.hasAttribute('muted')) {
this.config.muted = true;
@@ -240,7 +246,7 @@ class Plyr {
}
// Check for support again but with type
this.supported = support.check(this.type, this.provider, this.config.inline);
this.supported = support.check(this.type, this.provider, this.config.playsinline);
// If no support for even API, bail
if (!this.supported.api) {
@@ -368,7 +374,7 @@ class Plyr {
* Get playing state
*/
get playing() {
return Boolean(!this.paused && !this.ended && (this.isHTML5 ? this.media.readyState > 2 : true));
return Boolean(this.ready && !this.paused && !this.ended && (this.isHTML5 ? this.media.readyState > 2 : true));
}
/**
@@ -446,7 +452,7 @@ class Plyr {
}
// Set
this.media.currentTime = parseFloat(targetTime.toFixed(4));
this.media.currentTime = targetTime;
// Logging
this.debug.log(`Seeking to ${this.currentTime} seconds`);
@@ -492,7 +498,7 @@ class Plyr {
*/
get duration() {
// Faux duration set via config
const fauxDuration = parseInt(this.config.duration, 10);
const fauxDuration = parseFloat(this.config.duration);
// True duration
const realDuration = this.media ? Number(this.media.duration) : 0;
@@ -839,13 +845,13 @@ class Plyr {
* @param {boolean} input - Whether to enable captions
*/
toggleCaptions(input) {
// If there's no full support, or there's no caption toggle
if (!this.supported.ui || !utils.is.element(this.elements.buttons.captions)) {
// If there's no full support
if (!this.supported.ui) {
return;
}
// If the method is called without parameter, toggle based on current value
const show = utils.is.boolean(input) ? input : this.elements.container.className.indexOf(this.config.classNames.captions.active) === -1;
const show = utils.is.boolean(input) ? input : !this.elements.container.classList.contains(this.config.classNames.captions.active);
// Nothing to change...
if (this.captions.active === show) {
@@ -875,17 +881,29 @@ class Plyr {
return;
}
// Toggle captions based on input
this.toggleCaptions(!utils.is.empty(input));
// If empty string is passed, assume disable captions
if (utils.is.empty(input)) {
this.toggleCaptions(false);
return;
}
// Normalize
const language = input.toLowerCase();
// Check for support
if (!this.options.captions.includes(language)) {
this.debug.warn(`Unsupported language option: ${language}`);
return;
}
// Ensure captions are enabled
this.toggleCaptions(true);
// Enabled only
if (language === 'enabled') {
return;
}
// If nothing to change, bail
if (this.language === language) {
return;
+1 -1
View File
@@ -1,6 +1,6 @@
// ==========================================================================
// Plyr Polyfilled Build
// plyr.js v3.1.0-beta.2
// plyr.js v3.2.4
// https://github.com/sampotts/plyr
// License: The MIT License (MIT)
// ==========================================================================
+2 -2
View File
@@ -55,7 +55,7 @@ const source = {
this.provider = !utils.is.empty(input.sources[0].provider) ? input.sources[0].provider : providers.html5;
// Check for support
this.supported = support.check(this.type, this.provider, this.config.inline);
this.supported = support.check(this.type, this.provider, this.config.playsinline);
// Create new markup
switch (`${this.provider}:${this.type}`) {
@@ -103,7 +103,7 @@ const source = {
if (this.config.muted) {
this.media.setAttribute('muted', '');
}
if (this.config.inline) {
if (this.config.playsinline) {
this.media.setAttribute('playsinline', '');
}
}
+5 -5
View File
@@ -12,16 +12,16 @@ const support = {
// Check for support
// Basic functionality vs full UI
check(type, provider, inline) {
check(type, provider, playsinline) {
let api = false;
let ui = false;
const browser = utils.getBrowser();
const playsInline = browser.isIPhone && inline && support.inline;
const canPlayInline = browser.isIPhone && playsinline && support.playsinline;
switch (`${provider}:${type}`) {
case 'html5:video':
api = support.video;
ui = api && support.rangeInput && (!browser.isIPhone || playsInline);
ui = api && support.rangeInput && (!browser.isIPhone || canPlayInline);
break;
case 'html5:audio':
@@ -32,7 +32,7 @@ const support = {
case 'youtube:video':
case 'vimeo:video':
api = true;
ui = support.rangeInput && (!browser.isIPhone || playsInline);
ui = support.rangeInput && (!browser.isIPhone || canPlayInline);
break;
default:
@@ -59,7 +59,7 @@ const support = {
// Inline playback support
// https://webkit.org/blog/6784/new-video-policies-for-ios/
inline: 'playsInline' in document.createElement('video'),
playsinline: 'playsInline' in document.createElement('video'),
// Check for mime type support against a player instance
// Credits: http://diveintohtml5.info/everything.html
+2 -7
View File
@@ -48,11 +48,6 @@ const ui = {
this.listeners.controls();
}
// If there's no controls, bail
if (!utils.is.element(this.elements.controls)) {
return;
}
// Remove native controls
ui.toggleNativeControls.call(this);
@@ -277,10 +272,10 @@ const ui = {
}
// Always display hours if duration is over an hour
const displayHours = utils.getHours(this.duration) > 0;
const forceHours = utils.getHours(this.duration) > 0;
// eslint-disable-next-line no-param-reassign
target.textContent = utils.formatTime(time, displayHours, inverted);
target.textContent = utils.formatTime(time, forceHours, inverted);
},
// Handle time change event
+21 -15
View File
@@ -375,6 +375,25 @@ const utils = {
return attributes;
},
// Toggle hidden
toggleHidden(element, hidden) {
if (!utils.is.element(element)) {
return;
}
let hide = hidden;
if (!utils.is.boolean(hide)) {
hide = !element.hasAttribute('hidden');
}
if (hide) {
element.setAttribute('hidden', '');
} else {
element.removeAttribute('hidden');
}
},
// Toggle class on an element
toggleClass(element, className, toggle) {
if (utils.is.element(element)) {
@@ -393,19 +412,6 @@ const utils = {
return utils.is.element(element) && element.classList.contains(className);
},
// Toggle hidden attribute on an element
toggleHidden(element, toggle) {
if (!utils.is.element(element)) {
return;
}
if (toggle) {
element.setAttribute('hidden', '');
} else {
element.removeAttribute('hidden');
}
},
// Element matches selector
matches(element, selector) {
const prototype = { Element };
@@ -462,8 +468,8 @@ const utils = {
// Display
this.elements.display = {
buffer: utils.getElement.call(this, this.config.selectors.display.buffer),
duration: utils.getElement.call(this, this.config.selectors.display.duration),
currentTime: utils.getElement.call(this, this.config.selectors.display.currentTime),
duration: utils.getElement.call(this, this.config.selectors.display.duration),
};
// Seek tooltip
@@ -752,7 +758,7 @@ const utils = {
return null;
}
return array.reduce((prev, curr) => Math.abs(curr - value) < Math.abs(prev - value) ? curr : prev);
return array.reduce((prev, curr) => (Math.abs(curr - value) < Math.abs(prev - value) ? curr : prev));
},
// Get the provider for a given URL
+5
View File
@@ -26,6 +26,11 @@
width: 100%;
}
button {
font: inherit;
line-height: inherit;
}
// Ignore focus
&:focus {
outline: 0;
+15 -10
View File
@@ -3,14 +3,12 @@
// YouTube, Vimeo, etc
// --------------------------------------------------------------
.plyr__video-embed {
// Default to 16:9 ratio but this is set by JavaScript based on config
$padding: ((100 / 16) * 9);
$height: 240;
$offset: to-percentage(($height - $padding) / ($height / 50));
// Default to 16:9 ratio but this is set by JavaScript based on config
$embed-padding: ((100 / 16) * 9);
.plyr__video-embed {
height: 0;
padding-bottom: to-percentage($padding);
padding-bottom: to-percentage($embed-padding);
position: relative;
iframe {
@@ -22,6 +20,17 @@
user-select: none;
width: 100%;
}
}
// If the full custom UI is supported
.plyr--full-ui .plyr__video-embed {
$height: 240;
$offset: to-percentage(($height - $embed-padding) / ($height / 50));
// To allow mouse events to be captured if full support
iframe {
pointer-events: none;
}
// Vimeo hack
> div {
@@ -30,7 +39,3 @@
transform: translateY(-$offset);
}
}
// To allow mouse events to be captured if full support
.plyr--full-ui .plyr__video-embed iframe {
pointer-events: none;
}
+2 -1
View File
@@ -35,7 +35,7 @@
right: -3px;
text-align: left;
white-space: nowrap;
z-index: 1;
z-index: 3;
> div {
overflow: hidden;
@@ -74,6 +74,7 @@
align-items: center;
color: $plyr-menu-color;
display: flex;
font-size: $plyr-font-size-menu;
padding: ceil($plyr-control-padding / 2) ($plyr-control-padding * 2);
user-select: none;
width: 100%;
+1 -1
View File
@@ -19,7 +19,7 @@
&::-webkit-slider-runnable-track {
@include plyr-range-track();
background-image: linear-gradient(to right, currentColor var(--value), transparent var(--value));
background-image: linear-gradient(to right, currentColor var(--value, 0%), transparent var(--value, 0%));
}
&::-webkit-slider-thumb {
+7 -2
View File
@@ -23,7 +23,12 @@
// Hide sound controls on iOS
// It's not supported to change volume using JavaScript:
// https://developer.apple.com/library/safari/documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/Device-SpecificConsiderations/Device-SpecificConsiderations.html
.plyr--is-ios .plyr__volume,
.plyr--is-ios [data-plyr='mute'] {
.plyr--is-ios .plyr__volume {
display: none !important;
}
// Vimeo has no toggle mute method so hide mute button
// https://github.com/vimeo/player.js/issues/236#issuecomment-384663183
.plyr--is-ios.plyr--vimeo [data-plyr='mute'] {
display: none !important;
}
+2 -1
View File
@@ -8,8 +8,9 @@ $plyr-font-size-small: 14px !default;
$plyr-font-size-large: 18px !default;
$plyr-font-size-xlarge: 21px !default;
$plyr-font-size-time: 14px !default;
$plyr-font-size-time: $plyr-font-size-small !default;
$plyr-font-size-badge: 9px !default;
$plyr-font-size-menu: $plyr-font-size-small !default;
$plyr-font-weight-regular: 500 !default;
$plyr-font-weight-bold: 600 !default;
-9
View File
@@ -2,15 +2,6 @@
// Hiding content nicely
// --------------------------------------------------------------
// Attributes
.plyr--full-ui [hidden] {
display: none;
}
.plyr--full-ui [aria-hidden='true'] {
display: none;
}
// Screen reader only elements
.plyr__sr-only {
clip: rect(1px, 1px, 1px, 1px);
+1473 -68
View File
File diff suppressed because it is too large Load Diff