Merge pull request #1662 from sampotts/develop

3.5.7
This commit is contained in:
Sam Potts 2020-01-30 14:23:40 +00:00 committed by GitHub
commit 58f5380694
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 8124 additions and 4161 deletions

View File

@ -2,11 +2,11 @@
// See http://go.microsoft.com/fwlink/?LinkId=827846 // See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format // for the documentation about the extensions.json format
"recommendations": [ "recommendations": [
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
"dbaeumer.vscode-eslint", "dbaeumer.vscode-eslint",
"wix.vscode-import-cost",
"esbenp.prettier-vscode", "esbenp.prettier-vscode",
"shinnn.stylelint", "wayou.vscode-todo-highlight",
"wayou.vscode-todo-highlight" "wix.vscode-import-cost",
"stylelint.vscode-stylelint",
"pflannery.vscode-versionlens"
] ]
} }

View File

@ -1,15 +1,35 @@
## v3.5.6 ### v3.5.7
- Typescript typings (thanks @ondratra)
- `togglePlay` now also returns a `Promise` (thanks @azizhk)
- Documentation improvements (thanks @MaxGiting, @0xflotus and @thatrobotdev)
- Accessibility tweak for the play button (thanks @lunika)
- Fix for ads configuration (thanks @SoftCreatR)
- Added localisation key for PIP (picture-in-picture) (thanks @lmislm)
- Preserve viewBox attribute in SVG sprite symbols (thanks @bseib)
- Fix being unable to unmute autoplayed video on iOS (thanks @sumanbh)
- Fixed Plyr container not resizing responsively (thanks @shravan2x)
- Change vimeo demo video (thanks @thatrobotdev)
- Fix for `Uncaught RangeError: Maximum call stack size exceeded` (thanks @laukstein)
- Improve fullscreen experience on some devices (thanks @savroff)
- Improvements to buffering state for embedded players (thanks @doostinharrell)
- Prevents IE11 with resetOnEnd option set to true to play video again (thanks @Felipe K. De Boni)
- Fix for multiple poster image downloads (use the native poster only for HTML5 videos)
- Various presentational fixes
- Removed logic to hide/show volume controls based on audio track detection due to it's problematic nature. If you want to hide volume control, use the `controls` option to do so.
### v3.5.6
- Another Edge fix (thanks Nick Hawk via Slack) - Another Edge fix (thanks Nick Hawk via Slack)
## v3.5.5 ### v3.5.5
- YouTube fix for when there are other embeds on the page (thanks @aFarkas) - YouTube fix for when there are other embeds on the page (thanks @aFarkas)
- Separated demo dependencies into their own package.json - Separated demo dependencies into their own package.json
- Fix for Edge controls flexbox issue when resizing the player (thanks Nick Hawk via Slack) - Fix for Edge controls flexbox issue when resizing the player (thanks Nick Hawk via Slack)
- More aspect ratio fixes - More aspect ratio fixes
## v3.5.4 ### v3.5.4
- Added: Set download URL via new setter - Added: Set download URL via new setter
- Improvement: The order of the `controls` option now effects the order in the DOM - i.e. you can re-order the controls - Note: this may break any custom CSS you have setup. Please see the changes in the PR to the default SASS - Improvement: The order of the `controls` option now effects the order in the DOM - i.e. you can re-order the controls - Note: this may break any custom CSS you have setup. Please see the changes in the PR to the default SASS
@ -22,7 +42,7 @@
- Improvement: Automatic aspect ratio for YouTube is now supported, meaning all aspect ratios are set based on media content - Note: we're now using a different API to get YouTube video metadata so you may need to adjust any CSPs you have setup - Improvement: Automatic aspect ratio for YouTube is now supported, meaning all aspect ratios are set based on media content - Note: we're now using a different API to get YouTube video metadata so you may need to adjust any CSPs you have setup
- Fix for menu in the Shadow DOM (thanks @emielbeinema) - Fix for menu in the Shadow DOM (thanks @emielbeinema)
## v3.5.3 ### v3.5.3
- Improved the usage of the `ratio` config option; it now works as expected and for all video types. The default has not changed, it is to dynamically, where possible (except YouTube where 16:9 is used) determine the ratio from the media source so this is not a breaking change. - Improved the usage of the `ratio` config option; it now works as expected and for all video types. The default has not changed, it is to dynamically, where possible (except YouTube where 16:9 is used) determine the ratio from the media source so this is not a breaking change.
- Added new `ratio` getter and setter - Added new `ratio` getter and setter
@ -30,11 +50,11 @@
- Fix: Allow absolute paths in preview thumbnails - Fix: Allow absolute paths in preview thumbnails
- Improvement: Allow optional hours and ms in VTT parser in preview thumbnails - Improvement: Allow optional hours and ms in VTT parser in preview thumbnails
## v3.5.2 ### v3.5.2
- Fixed issue where the preview thumbnail was present while scrubbing - Fixed issue where the preview thumbnail was present while scrubbing
## v3.5.1 ### v3.5.1
- Fixed build issues with babel and browserslist - Fixed build issues with babel and browserslist

2
demo/dist/demo.css vendored

File diff suppressed because one or more lines are too long

2940
demo/dist/demo.js vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
demo/dist/demo.svg vendored

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

2
demo/dist/error.css vendored

File diff suppressed because one or more lines are too long

View File

@ -68,7 +68,7 @@ const sources = {
type: 'video', type: 'video',
sources: [ sources: [
{ {
src: 'https://vimeo.com/76979871', src: 'https://vimeo.com/383514704',
provider: 'vimeo', provider: 'vimeo',
}, },
], ],

2
dist/plyr.css vendored

File diff suppressed because one or more lines are too long

204
dist/plyr.js vendored
View File

@ -2,7 +2,7 @@ typeof navigator === "object" && (function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define('Plyr', factory) : typeof define === 'function' && define.amd ? define('Plyr', factory) :
(global = global || self, global.Plyr = factory()); (global = global || self, global.Plyr = factory());
}(this, function () { 'use strict'; }(this, (function () { 'use strict';
function _classCallCheck(instance, Constructor) { function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) { if (!(instance instanceof Constructor)) {
@ -41,6 +41,40 @@ typeof navigator === "object" && (function (global, factory) {
return obj; return obj;
} }
function ownKeys(object, enumerableOnly) {
var keys = Object.keys(object);
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(object);
if (enumerableOnly) symbols = symbols.filter(function (sym) {
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
});
keys.push.apply(keys, symbols);
}
return keys;
}
function _objectSpread2(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i] != null ? arguments[i] : {};
if (i % 2) {
ownKeys(Object(source), true).forEach(function (key) {
_defineProperty(target, key, source[key]);
});
} else if (Object.getOwnPropertyDescriptors) {
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
} else {
ownKeys(Object(source)).forEach(function (key) {
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
});
}
}
return target;
}
function _slicedToArray(arr, i) { function _slicedToArray(arr, i) {
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
} }
@ -66,6 +100,10 @@ typeof navigator === "object" && (function (global, factory) {
} }
function _iterableToArrayLimit(arr, i) { function _iterableToArrayLimit(arr, i) {
if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) {
return;
}
var _arr = []; var _arr = [];
var _n = true; var _n = true;
var _d = false; var _d = false;
@ -583,7 +621,6 @@ typeof navigator === "object" && (function (global, factory) {
isIos: /(iPad|iPhone|iPod)/gi.test(navigator.platform) isIos: /(iPad|iPhone|iPod)/gi.test(navigator.platform)
}; };
// ==========================================================================
// https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md // https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md
// https://www.youtube.com/watch?v=NPM6172J22g // https://www.youtube.com/watch?v=NPM6172J22g
@ -700,7 +737,7 @@ typeof navigator === "object" && (function (global, factory) {
var event = new CustomEvent(type, { var event = new CustomEvent(type, {
bubbles: bubbles, bubbles: bubbles,
detail: Object.assign({}, detail, { detail: _objectSpread2({}, detail, {
plyr: this plyr: this
}) })
}); // Dispatch the event }); // Dispatch the event
@ -929,9 +966,6 @@ typeof navigator === "object" && (function (global, factory) {
// Attribute selector // Attribute selector
attributes[key] = value; attributes[key] = value;
break; break;
default:
break;
} }
}); });
return extend(existing, attributes); return extend(existing, attributes);
@ -1202,6 +1236,7 @@ typeof navigator === "object" && (function (global, factory) {
return {}; return {};
} }
var wrapper = this.elements.wrapper;
var ratio = getAspectRatio.call(this, input); var ratio = getAspectRatio.call(this, input);
var _ref = is$1.array(ratio) ? ratio : [0, 0], var _ref = is$1.array(ratio) ? ratio : [0, 0],
@ -1210,14 +1245,14 @@ typeof navigator === "object" && (function (global, factory) {
h = _ref2[1]; h = _ref2[1];
var padding = 100 / w * h; var padding = 100 / w * h;
this.elements.wrapper.style.paddingBottom = "".concat(padding, "%"); // For Vimeo we have an extra <div> to hide the standard controls and UI wrapper.style.paddingBottom = "".concat(padding, "%"); // For Vimeo we have an extra <div> to hide the standard controls and UI
if (this.isVimeo && this.supported.ui) { if (this.isVimeo && this.supported.ui) {
var height = 240; var height = 240;
var offset = (height - padding) / (height / 50); var offset = (height - padding) / (height / 50);
this.media.style.transform = "translateY(-".concat(offset, "%)"); this.media.style.transform = "translateY(-".concat(offset, "%)");
} else if (this.isHTML5) { } else if (this.isHTML5) {
this.elements.wrapper.classList.toggle(this.config.classNames.videoFixedRatio, ratio !== null); wrapper.classList.toggle(this.config.classNames.videoFixedRatio, ratio !== null);
} }
return { return {
@ -1249,7 +1284,12 @@ typeof navigator === "object" && (function (global, factory) {
}, },
// Get quality levels // Get quality levels
getQualityOptions: function getQualityOptions() { getQualityOptions: function getQualityOptions() {
// Get sizes from <source> elements // Whether we're forcing all options (e.g. for streaming)
if (this.config.quality.forced) {
return this.config.quality.options;
} // Get sizes from <source> elements
return html5.getSources.call(this).map(function (source) { return html5.getSources.call(this).map(function (source) {
return Number(source.getAttribute('size')); return Number(source.getAttribute('size'));
}).filter(Boolean); }).filter(Boolean);
@ -1277,6 +1317,10 @@ typeof navigator === "object" && (function (global, factory) {
return source && Number(source.getAttribute('size')); return source && Number(source.getAttribute('size'));
}, },
set: function set(input) { set: function set(input) {
// If we're using an an external handler...
if (player.config.quality.forced && is$1.function(player.config.quality.onChange)) {
player.config.quality.onChange(input);
} else {
// Get sources // Get sources
var sources = html5.getSources.call(player); // Get first match for requested size var sources = html5.getSources.call(player); // Get first match for requested size
@ -1300,6 +1344,10 @@ typeof navigator === "object" && (function (global, factory) {
if (preload !== 'none' || readyState) { if (preload !== 'none' || readyState) {
// Restore time // Restore time
player.once('loadedmetadata', function () { player.once('loadedmetadata', function () {
if (player.currentTime === 0) {
return;
}
player.currentTime = currentTime; // Resume playing player.currentTime = currentTime; // Resume playing
if (!paused) { if (!paused) {
@ -1308,6 +1356,7 @@ typeof navigator === "object" && (function (global, factory) {
}); // Load new source }); // Load new source
player.media.load(); player.media.load();
}
} // Trigger change event } // Trigger change event
@ -1678,7 +1727,7 @@ typeof navigator === "object" && (function (global, factory) {
// Bail if the value isn't a number // Bail if the value isn't a number
if (!is$1.number(time)) { if (!is$1.number(time)) {
return formatTime(null, displayHours, inverted); return formatTime(undefined, displayHours, inverted);
} // Format time component to add leading zero } // Format time component to add leading zero
@ -1787,9 +1836,11 @@ typeof navigator === "object" && (function (global, factory) {
createLabel: function createLabel(key) { createLabel: function createLabel(key) {
var attr = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var attr = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var text = i18n.get(key, this.config); var text = i18n.get(key, this.config);
var attributes = Object.assign({}, attr, {
var attributes = _objectSpread2({}, attr, {
class: [attr.class, this.config.classNames.hidden].filter(Boolean).join(' ') class: [attr.class, this.config.classNames.hidden].filter(Boolean).join(' ')
}); });
return createElement('span', attributes, text); return createElement('span', attributes, text);
}, },
// Create a badge // Create a badge
@ -1999,7 +2050,7 @@ typeof navigator === "object" && (function (global, factory) {
var _this2 = this; var _this2 = this;
// Navigate through menus via arrow keys and space // Navigate through menus via arrow keys and space
on(menuItem, 'keydown keyup', function (event) { on.call(this, menuItem, 'keydown keyup', function (event) {
// We only care about space and ⬆️ ⬇️️ ➡️ // We only care about space and ⬆️ ⬇️️ ➡️
if (![32, 38, 39, 40].includes(event.which)) { if (![32, 38, 39, 40].includes(event.which)) {
return; return;
@ -2041,7 +2092,7 @@ typeof navigator === "object" && (function (global, factory) {
}, false); // Enter will fire a `click` event but we still need to manage focus }, false); // Enter will fire a `click` event but we still need to manage focus
// So we bind to keyup which fires after and set focus here // So we bind to keyup which fires after and set focus here
on(menuItem, 'keyup', function (event) { on.call(this, menuItem, 'keyup', function (event) {
if (event.which !== 13) { if (event.which !== 13) {
return; return;
} }
@ -2118,9 +2169,6 @@ typeof navigator === "object" && (function (global, factory) {
case 'speed': case 'speed':
_this3.speed = parseFloat(value); _this3.speed = parseFloat(value);
break; break;
default:
break;
} }
controls.showMenuPanel.call(_this3, 'home', is$1.keyboardEvent(event)); controls.showMenuPanel.call(_this3, 'home', is$1.keyboardEvent(event));
@ -2229,9 +2277,6 @@ typeof navigator === "object" && (function (global, factory) {
case 'progress': case 'progress':
setProgress(this.elements.display.buffer, this.buffered * 100); setProgress(this.elements.display.buffer, this.buffered * 100);
break; break;
default:
break;
} }
} }
}, },
@ -2885,9 +2930,11 @@ typeof navigator === "object" && (function (global, factory) {
if (control === 'mute') { if (control === 'mute') {
volume.appendChild(createButton.call(_this10, 'mute')); volume.appendChild(createButton.call(_this10, 'mute'));
} // Volume range control } // Volume range control
// Ignored on iOS as it's handled globally
// https://developer.apple.com/library/safari/documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/Device-SpecificConsiderations/Device-SpecificConsiderations.html
if (control === 'volume') { if (control === 'volume' && !browser.isIos) {
// Set the attributes // Set the attributes
var attributes = { var attributes = {
max: 1, max: 1,
@ -2946,7 +2993,7 @@ typeof navigator === "object" && (function (global, factory) {
bindMenuItemShortcuts.call(_this10, menuItem, type); // Show menu on click bindMenuItemShortcuts.call(_this10, menuItem, type); // Show menu on click
on(menuItem, 'click', function () { on.call(_this10, menuItem, 'click', function () {
showMenuPanel.call(_this10, type, false); showMenuPanel.call(_this10, type, false);
}); });
var flex = createElement('span', null, i18n.get(type, _this10.config)); var flex = createElement('span', null, i18n.get(type, _this10.config));
@ -2977,7 +3024,7 @@ typeof navigator === "object" && (function (global, factory) {
class: _this10.config.classNames.hidden class: _this10.config.classNames.hidden
}, i18n.get('menuBack', _this10.config))); // Go back via keyboard }, i18n.get('menuBack', _this10.config))); // Go back via keyboard
on(pane, 'keydown', function (event) { on.call(_this10, pane, 'keydown', function (event) {
// We only care about <- // We only care about <-
if (event.which !== 37) { if (event.which !== 37) {
return; return;
@ -2990,7 +3037,7 @@ typeof navigator === "object" && (function (global, factory) {
showMenuPanel.call(_this10, 'home', true); showMenuPanel.call(_this10, 'home', true);
}, false); // Go back via button click }, false); // Go back via button click
on(backButton, 'click', function () { on.call(_this10, backButton, 'click', function () {
showMenuPanel.call(_this10, 'home', false); showMenuPanel.call(_this10, 'home', false);
}); // Add to pane }); // Add to pane
@ -3652,7 +3699,9 @@ typeof navigator === "object" && (function (global, factory) {
// Quality default // Quality default
quality: { quality: {
default: 576, default: 576,
options: [4320, 2880, 2160, 1440, 1080, 720, 576, 480, 360, 240] options: [4320, 2880, 2160, 1440, 1080, 720, 576, 480, 360, 240],
forced: false,
onChange: null
}, },
// Set loops // Set loops
loop: { loop: {
@ -3729,6 +3778,7 @@ typeof navigator === "object" && (function (global, factory) {
frameTitle: 'Player for {title}', frameTitle: 'Player for {title}',
captions: 'Captions', captions: 'Captions',
settings: 'Settings', settings: 'Settings',
pip: 'PIP',
menuBack: 'Go back to previous menu', menuBack: 'Go back to previous menu',
speed: 'Speed', speed: 'Speed',
normal: 'Normal', normal: 'Normal',
@ -4168,7 +4218,9 @@ typeof navigator === "object" && (function (global, factory) {
} else if (!Fullscreen.native || this.forceFallback) { } else if (!Fullscreen.native || this.forceFallback) {
toggleFallback.call(this, true); toggleFallback.call(this, true);
} else if (!this.prefix) { } else if (!this.prefix) {
this.target.requestFullscreen(); this.target.requestFullscreen({
navigationUI: "hide"
});
} else if (!is$1.empty(this.prefix)) { } else if (!is$1.empty(this.prefix)) {
this.target["".concat(this.prefix, "Request").concat(this.property)](); this.target["".concat(this.prefix, "Request").concat(this.property)]();
} }
@ -4475,6 +4527,7 @@ typeof navigator === "object" && (function (global, factory) {
Object.assign(target, { Object.assign(target, {
pressed: _this3.playing pressed: _this3.playing
}); });
target.setAttribute('aria-label', i18n.get(_this3.playing ? 'pause' : 'play', _this3.config));
}); // Only update controls on non timeupdate events }); // Only update controls on non timeupdate events
if (is$1.event(event) && event.type === 'timeupdate') { if (is$1.event(event) && event.type === 'timeupdate') {
@ -4655,19 +4708,6 @@ typeof navigator === "object" && (function (global, factory) {
// L key // L key
player.loop = !player.loop; player.loop = !player.loop;
break; break;
/* case 73:
this.setLoop('start');
break;
case 76:
this.setLoop();
break;
case 79:
this.setLoop('end');
break; */
default:
break;
} // Escape is handle natively when in full screen } // Escape is handle natively when in full screen
// So we only need to worry about non native // So we only need to worry about non native
@ -4884,19 +4924,15 @@ typeof navigator === "object" && (function (global, factory) {
on.call(player, player.media, 'durationchange loadeddata loadedmetadata', function (event) { on.call(player, player.media, 'durationchange loadeddata loadedmetadata', function (event) {
return controls.durationUpdate.call(player, event); return controls.durationUpdate.call(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
on.call(player, player.media, 'canplay loadeddata', function () {
toggleHidden(elements.volume, !player.hasAudio);
toggleHidden(elements.buttons.mute, !player.hasAudio);
}); // Handle the media finishing }); // Handle the media finishing
on.call(player, player.media, 'ended', function () { on.call(player, player.media, 'ended', function () {
// Show poster on end // Show poster on end
if (player.isHTML5 && player.isVideo && player.config.resetOnEnd) { if (player.isHTML5 && player.isVideo && player.config.resetOnEnd) {
// Restart // Restart
player.restart(); player.restart(); // Call pause otherwise IE11 will start playing the video again
player.pause();
} }
}); // Check for buffer progress }); // Check for buffer progress
@ -5009,7 +5045,7 @@ typeof navigator === "object" && (function (global, factory) {
} // Only call default handler if not prevented in custom handler } // Only call default handler if not prevented in custom handler
if (returned && is$1.function(defaultHandler)) { if (returned !== false && is$1.function(defaultHandler)) {
defaultHandler.call(player, event); defaultHandler.call(player, event);
} }
} // Trigger custom and default handlers } // Trigger custom and default handlers
@ -5397,12 +5433,13 @@ typeof navigator === "object" && (function (global, factory) {
async = args.async, async = args.async,
maxTries = (args.numRetries || 0) + 1, maxTries = (args.numRetries || 0) + 1,
beforeCallbackFn = args.before || devnull, beforeCallbackFn = args.before || devnull,
pathname = path.replace(/[\?|#].*$/, ''),
pathStripped = path.replace(/^(css|img)!/, ''), pathStripped = path.replace(/^(css|img)!/, ''),
isLegacyIECss, isLegacyIECss,
e; e;
numTries = numTries || 0; numTries = numTries || 0;
if (/(^css!|\.css$)/.test(path)) { if (/(^css!|\.css$)/.test(pathname)) {
// css // css
e = doc.createElement('link'); e = doc.createElement('link');
e.rel = 'stylesheet'; e.rel = 'stylesheet';
@ -5415,7 +5452,7 @@ typeof navigator === "object" && (function (global, factory) {
e.rel = 'preload'; e.rel = 'preload';
e.as = 'style'; e.as = 'style';
} }
} else if (/(^img!|\.(png|gif|jpg|svg)$)/.test(path)) { } else if (/(^img!|\.(png|gif|jpg|svg|webp)$)/.test(pathname)) {
// image // image
e = doc.createElement('img'); e = doc.createElement('img');
e.src = pathStripped; e.src = pathStripped;
@ -5888,6 +5925,12 @@ typeof navigator === "object" && (function (global, factory) {
frame.setAttribute('tabindex', -1); frame.setAttribute('tabindex', -1);
} }
}); });
player.embed.on('bufferstart', function () {
triggerEvent.call(player, player.media, 'waiting');
});
player.embed.on('bufferend', function () {
triggerEvent.call(player, player.media, 'playing');
});
player.embed.on('play', function () { player.embed.on('play', function () {
assurePlaybackState.call(player, true); assurePlaybackState.call(player, true);
triggerEvent.call(player, player.media, 'playing'); triggerEvent.call(player, player.media, 'playing');
@ -6321,7 +6364,9 @@ typeof navigator === "object" && (function (global, factory) {
assurePlaybackState$1.call(player, false); assurePlaybackState$1.call(player, false);
break; break;
default: case 3:
// Trigger waiting event to add loading classes to container as the video buffers.
triggerEvent.call(player, player.media, 'waiting');
break; break;
} }
@ -6363,11 +6408,13 @@ typeof navigator === "object" && (function (global, factory) {
wrap(this.media, this.elements.wrapper); // Faux poster container wrap(this.media, this.elements.wrapper); // Faux poster container
if (this.isEmbed) {
this.elements.poster = createElement('div', { this.elements.poster = createElement('div', {
class: this.config.classNames.poster class: this.config.classNames.poster
}); });
this.elements.wrapper.appendChild(this.elements.poster); this.elements.wrapper.appendChild(this.elements.poster);
} }
}
if (this.isHTML5) { if (this.isHTML5) {
html5.extend.call(this); html5.extend.call(this);
@ -6736,9 +6783,6 @@ typeof navigator === "object" && (function (global, factory) {
} }
break; break;
default:
break;
} }
} }
/** /**
@ -7010,7 +7054,7 @@ typeof navigator === "object" && (function (global, factory) {
cb: Date.now(), cb: Date.now(),
AV_WIDTH: 640, AV_WIDTH: 640,
AV_HEIGHT: 480, AV_HEIGHT: 480,
AV_CDIM2: this.publisherId AV_CDIM2: config.publisherId
}; };
var base = 'https://go.aniview.com/api/adserver6/vast/'; var base = 'https://go.aniview.com/api/adserver6/vast/';
return "".concat(base, "?").concat(buildUrlParams(params)); return "".concat(base, "?").concat(buildUrlParams(params));
@ -7075,6 +7119,21 @@ typeof navigator === "object" && (function (global, factory) {
*/ */
var fitRatio = function fitRatio(ratio, outer) {
var targetRatio = outer.width / outer.height;
var result = {};
if (ratio > targetRatio) {
result.width = outer.width;
result.height = 1 / ratio * outer.width;
} else {
result.height = outer.height;
result.width = ratio * outer.height;
}
return result;
};
var PreviewThumbnails = var PreviewThumbnails =
/*#__PURE__*/ /*#__PURE__*/
function () { function () {
@ -7619,9 +7678,15 @@ typeof navigator === "object" && (function (global, factory) {
}, { }, {
key: "setScrubbingContainerSize", key: "setScrubbingContainerSize",
value: function setScrubbingContainerSize() { value: function setScrubbingContainerSize() {
this.elements.scrubbing.container.style.width = "".concat(this.player.media.clientWidth, "px"); // Can't use media.clientHeight - html5 video goes big and does black bars above and below var _fitRatio = fitRatio(this.thumbAspectRatio, {
width: this.player.media.clientWidth,
height: this.player.media.clientHeight
}),
width = _fitRatio.width,
height = _fitRatio.height;
this.elements.scrubbing.container.style.height = "".concat(this.player.media.clientWidth / this.thumbAspectRatio, "px"); this.elements.scrubbing.container.style.width = "".concat(width, "px");
this.elements.scrubbing.container.style.height = "".concat(height, "px");
} // Sprites need to be offset to the correct location } // Sprites need to be offset to the correct location
}, { }, {
@ -7634,9 +7699,9 @@ typeof navigator === "object" && (function (global, factory) {
var multiplier = this.thumbContainerHeight / frame.h; // eslint-disable-next-line no-param-reassign var multiplier = this.thumbContainerHeight / frame.h; // eslint-disable-next-line no-param-reassign
previewImage.style.height = "".concat(Math.floor(previewImage.naturalHeight * multiplier), "px"); // eslint-disable-next-line no-param-reassign previewImage.style.height = "".concat(previewImage.naturalHeight * multiplier, "px"); // eslint-disable-next-line no-param-reassign
previewImage.style.width = "".concat(Math.floor(previewImage.naturalWidth * multiplier), "px"); // eslint-disable-next-line no-param-reassign previewImage.style.width = "".concat(previewImage.naturalWidth * multiplier, "px"); // eslint-disable-next-line no-param-reassign
previewImage.style.left = "-".concat(frame.x * multiplier, "px"); // eslint-disable-next-line no-param-reassign previewImage.style.left = "-".concat(frame.x * multiplier, "px"); // eslint-disable-next-line no-param-reassign
@ -7674,8 +7739,13 @@ typeof navigator === "object" && (function (global, factory) {
key: "thumbContainerHeight", key: "thumbContainerHeight",
get: function get() { get: function get() {
if (this.mouseDown) { if (this.mouseDown) {
// Can't use media.clientHeight - HTML5 video goes big and does black bars above and below var _fitRatio2 = fitRatio(this.thumbAspectRatio, {
return Math.floor(this.player.media.clientWidth / this.thumbAspectRatio); width: this.player.media.clientWidth,
height: this.player.media.clientHeight
}),
height = _fitRatio2.height;
return height;
} }
return Math.floor(this.player.media.clientWidth / this.thumbAspectRatio / 4); return Math.floor(this.player.media.clientWidth / this.thumbAspectRatio / 4);
@ -8159,10 +8229,10 @@ typeof navigator === "object" && (function (global, factory) {
key: "pause", key: "pause",
value: function pause() { value: function pause() {
if (!this.playing || !is$1.function(this.media.pause)) { if (!this.playing || !is$1.function(this.media.pause)) {
return; return null;
} }
this.media.pause(); return this.media.pause();
} }
/** /**
* Get playing state * Get playing state
@ -8180,10 +8250,10 @@ typeof navigator === "object" && (function (global, factory) {
var toggle = is$1.boolean(input) ? input : !this.playing; var toggle = is$1.boolean(input) ? input : !this.playing;
if (toggle) { if (toggle) {
this.play(); return this.play();
} else {
this.pause();
} }
return this.pause();
} }
/** /**
* Stop playback * Stop playback
@ -8216,7 +8286,7 @@ typeof navigator === "object" && (function (global, factory) {
}, { }, {
key: "rewind", key: "rewind",
value: function rewind(seekTime) { value: function rewind(seekTime) {
this.currentTime = this.currentTime - (is$1.number(seekTime) ? seekTime : this.config.seekTime); this.currentTime -= is$1.number(seekTime) ? seekTime : this.config.seekTime;
} }
/** /**
* Fast forward * Fast forward
@ -8226,7 +8296,7 @@ typeof navigator === "object" && (function (global, factory) {
}, { }, {
key: "forward", key: "forward",
value: function forward(seekTime) { value: function forward(seekTime) {
this.currentTime = this.currentTime + (is$1.number(seekTime) ? seekTime : this.config.seekTime); this.currentTime += is$1.number(seekTime) ? seekTime : this.config.seekTime;
} }
/** /**
* Seek to a time * Seek to a time
@ -9128,4 +9198,4 @@ typeof navigator === "object" && (function (global, factory) {
return Plyr; return Plyr;
})); })));

4
dist/plyr.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4
dist/plyr.min.mjs vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

200
dist/plyr.mjs vendored
View File

@ -35,6 +35,40 @@ function _defineProperty(obj, key, value) {
return obj; return obj;
} }
function ownKeys(object, enumerableOnly) {
var keys = Object.keys(object);
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(object);
if (enumerableOnly) symbols = symbols.filter(function (sym) {
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
});
keys.push.apply(keys, symbols);
}
return keys;
}
function _objectSpread2(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i] != null ? arguments[i] : {};
if (i % 2) {
ownKeys(Object(source), true).forEach(function (key) {
_defineProperty(target, key, source[key]);
});
} else if (Object.getOwnPropertyDescriptors) {
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
} else {
ownKeys(Object(source)).forEach(function (key) {
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
});
}
}
return target;
}
function _slicedToArray(arr, i) { function _slicedToArray(arr, i) {
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
} }
@ -60,6 +94,10 @@ function _iterableToArray(iter) {
} }
function _iterableToArrayLimit(arr, i) { function _iterableToArrayLimit(arr, i) {
if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) {
return;
}
var _arr = []; var _arr = [];
var _n = true; var _n = true;
var _d = false; var _d = false;
@ -577,7 +615,6 @@ var browser = {
isIos: /(iPad|iPhone|iPod)/gi.test(navigator.platform) isIos: /(iPad|iPhone|iPod)/gi.test(navigator.platform)
}; };
// ==========================================================================
// https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md // https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md
// https://www.youtube.com/watch?v=NPM6172J22g // https://www.youtube.com/watch?v=NPM6172J22g
@ -694,7 +731,7 @@ function triggerEvent(element) {
var event = new CustomEvent(type, { var event = new CustomEvent(type, {
bubbles: bubbles, bubbles: bubbles,
detail: Object.assign({}, detail, { detail: _objectSpread2({}, detail, {
plyr: this plyr: this
}) })
}); // Dispatch the event }); // Dispatch the event
@ -923,9 +960,6 @@ function getAttributesFromSelector(sel, existingAttributes) {
// Attribute selector // Attribute selector
attributes[key] = value; attributes[key] = value;
break; break;
default:
break;
} }
}); });
return extend(existing, attributes); return extend(existing, attributes);
@ -1196,6 +1230,7 @@ function setAspectRatio(input) {
return {}; return {};
} }
var wrapper = this.elements.wrapper;
var ratio = getAspectRatio.call(this, input); var ratio = getAspectRatio.call(this, input);
var _ref = is$1.array(ratio) ? ratio : [0, 0], var _ref = is$1.array(ratio) ? ratio : [0, 0],
@ -1204,14 +1239,14 @@ function setAspectRatio(input) {
h = _ref2[1]; h = _ref2[1];
var padding = 100 / w * h; var padding = 100 / w * h;
this.elements.wrapper.style.paddingBottom = "".concat(padding, "%"); // For Vimeo we have an extra <div> to hide the standard controls and UI wrapper.style.paddingBottom = "".concat(padding, "%"); // For Vimeo we have an extra <div> to hide the standard controls and UI
if (this.isVimeo && this.supported.ui) { if (this.isVimeo && this.supported.ui) {
var height = 240; var height = 240;
var offset = (height - padding) / (height / 50); var offset = (height - padding) / (height / 50);
this.media.style.transform = "translateY(-".concat(offset, "%)"); this.media.style.transform = "translateY(-".concat(offset, "%)");
} else if (this.isHTML5) { } else if (this.isHTML5) {
this.elements.wrapper.classList.toggle(this.config.classNames.videoFixedRatio, ratio !== null); wrapper.classList.toggle(this.config.classNames.videoFixedRatio, ratio !== null);
} }
return { return {
@ -1243,7 +1278,12 @@ var html5 = {
}, },
// Get quality levels // Get quality levels
getQualityOptions: function getQualityOptions() { getQualityOptions: function getQualityOptions() {
// Get sizes from <source> elements // Whether we're forcing all options (e.g. for streaming)
if (this.config.quality.forced) {
return this.config.quality.options;
} // Get sizes from <source> elements
return html5.getSources.call(this).map(function (source) { return html5.getSources.call(this).map(function (source) {
return Number(source.getAttribute('size')); return Number(source.getAttribute('size'));
}).filter(Boolean); }).filter(Boolean);
@ -1271,6 +1311,10 @@ var html5 = {
return source && Number(source.getAttribute('size')); return source && Number(source.getAttribute('size'));
}, },
set: function set(input) { set: function set(input) {
// If we're using an an external handler...
if (player.config.quality.forced && is$1.function(player.config.quality.onChange)) {
player.config.quality.onChange(input);
} else {
// Get sources // Get sources
var sources = html5.getSources.call(player); // Get first match for requested size var sources = html5.getSources.call(player); // Get first match for requested size
@ -1294,6 +1338,10 @@ var html5 = {
if (preload !== 'none' || readyState) { if (preload !== 'none' || readyState) {
// Restore time // Restore time
player.once('loadedmetadata', function () { player.once('loadedmetadata', function () {
if (player.currentTime === 0) {
return;
}
player.currentTime = currentTime; // Resume playing player.currentTime = currentTime; // Resume playing
if (!paused) { if (!paused) {
@ -1302,6 +1350,7 @@ var html5 = {
}); // Load new source }); // Load new source
player.media.load(); player.media.load();
}
} // Trigger change event } // Trigger change event
@ -1672,7 +1721,7 @@ function formatTime() {
// Bail if the value isn't a number // Bail if the value isn't a number
if (!is$1.number(time)) { if (!is$1.number(time)) {
return formatTime(null, displayHours, inverted); return formatTime(undefined, displayHours, inverted);
} // Format time component to add leading zero } // Format time component to add leading zero
@ -1781,9 +1830,11 @@ var controls = {
createLabel: function createLabel(key) { createLabel: function createLabel(key) {
var attr = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var attr = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var text = i18n.get(key, this.config); var text = i18n.get(key, this.config);
var attributes = Object.assign({}, attr, {
var attributes = _objectSpread2({}, attr, {
class: [attr.class, this.config.classNames.hidden].filter(Boolean).join(' ') class: [attr.class, this.config.classNames.hidden].filter(Boolean).join(' ')
}); });
return createElement('span', attributes, text); return createElement('span', attributes, text);
}, },
// Create a badge // Create a badge
@ -1993,7 +2044,7 @@ var controls = {
var _this2 = this; var _this2 = this;
// Navigate through menus via arrow keys and space // Navigate through menus via arrow keys and space
on(menuItem, 'keydown keyup', function (event) { on.call(this, menuItem, 'keydown keyup', function (event) {
// We only care about space and ⬆️ ⬇️️ ➡️ // We only care about space and ⬆️ ⬇️️ ➡️
if (![32, 38, 39, 40].includes(event.which)) { if (![32, 38, 39, 40].includes(event.which)) {
return; return;
@ -2035,7 +2086,7 @@ var controls = {
}, false); // Enter will fire a `click` event but we still need to manage focus }, false); // Enter will fire a `click` event but we still need to manage focus
// So we bind to keyup which fires after and set focus here // So we bind to keyup which fires after and set focus here
on(menuItem, 'keyup', function (event) { on.call(this, menuItem, 'keyup', function (event) {
if (event.which !== 13) { if (event.which !== 13) {
return; return;
} }
@ -2112,9 +2163,6 @@ var controls = {
case 'speed': case 'speed':
_this3.speed = parseFloat(value); _this3.speed = parseFloat(value);
break; break;
default:
break;
} }
controls.showMenuPanel.call(_this3, 'home', is$1.keyboardEvent(event)); controls.showMenuPanel.call(_this3, 'home', is$1.keyboardEvent(event));
@ -2223,9 +2271,6 @@ var controls = {
case 'progress': case 'progress':
setProgress(this.elements.display.buffer, this.buffered * 100); setProgress(this.elements.display.buffer, this.buffered * 100);
break; break;
default:
break;
} }
} }
}, },
@ -2879,9 +2924,11 @@ var controls = {
if (control === 'mute') { if (control === 'mute') {
volume.appendChild(createButton.call(_this10, 'mute')); volume.appendChild(createButton.call(_this10, 'mute'));
} // Volume range control } // Volume range control
// Ignored on iOS as it's handled globally
// https://developer.apple.com/library/safari/documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/Device-SpecificConsiderations/Device-SpecificConsiderations.html
if (control === 'volume') { if (control === 'volume' && !browser.isIos) {
// Set the attributes // Set the attributes
var attributes = { var attributes = {
max: 1, max: 1,
@ -2940,7 +2987,7 @@ var controls = {
bindMenuItemShortcuts.call(_this10, menuItem, type); // Show menu on click bindMenuItemShortcuts.call(_this10, menuItem, type); // Show menu on click
on(menuItem, 'click', function () { on.call(_this10, menuItem, 'click', function () {
showMenuPanel.call(_this10, type, false); showMenuPanel.call(_this10, type, false);
}); });
var flex = createElement('span', null, i18n.get(type, _this10.config)); var flex = createElement('span', null, i18n.get(type, _this10.config));
@ -2971,7 +3018,7 @@ var controls = {
class: _this10.config.classNames.hidden class: _this10.config.classNames.hidden
}, i18n.get('menuBack', _this10.config))); // Go back via keyboard }, i18n.get('menuBack', _this10.config))); // Go back via keyboard
on(pane, 'keydown', function (event) { on.call(_this10, pane, 'keydown', function (event) {
// We only care about <- // We only care about <-
if (event.which !== 37) { if (event.which !== 37) {
return; return;
@ -2984,7 +3031,7 @@ var controls = {
showMenuPanel.call(_this10, 'home', true); showMenuPanel.call(_this10, 'home', true);
}, false); // Go back via button click }, false); // Go back via button click
on(backButton, 'click', function () { on.call(_this10, backButton, 'click', function () {
showMenuPanel.call(_this10, 'home', false); showMenuPanel.call(_this10, 'home', false);
}); // Add to pane }); // Add to pane
@ -3646,7 +3693,9 @@ var defaults$1 = {
// Quality default // Quality default
quality: { quality: {
default: 576, default: 576,
options: [4320, 2880, 2160, 1440, 1080, 720, 576, 480, 360, 240] options: [4320, 2880, 2160, 1440, 1080, 720, 576, 480, 360, 240],
forced: false,
onChange: null
}, },
// Set loops // Set loops
loop: { loop: {
@ -3723,6 +3772,7 @@ var defaults$1 = {
frameTitle: 'Player for {title}', frameTitle: 'Player for {title}',
captions: 'Captions', captions: 'Captions',
settings: 'Settings', settings: 'Settings',
pip: 'PIP',
menuBack: 'Go back to previous menu', menuBack: 'Go back to previous menu',
speed: 'Speed', speed: 'Speed',
normal: 'Normal', normal: 'Normal',
@ -4162,7 +4212,9 @@ function () {
} else if (!Fullscreen.native || this.forceFallback) { } else if (!Fullscreen.native || this.forceFallback) {
toggleFallback.call(this, true); toggleFallback.call(this, true);
} else if (!this.prefix) { } else if (!this.prefix) {
this.target.requestFullscreen(); this.target.requestFullscreen({
navigationUI: "hide"
});
} else if (!is$1.empty(this.prefix)) { } else if (!is$1.empty(this.prefix)) {
this.target["".concat(this.prefix, "Request").concat(this.property)](); this.target["".concat(this.prefix, "Request").concat(this.property)]();
} }
@ -4469,6 +4521,7 @@ var ui = {
Object.assign(target, { Object.assign(target, {
pressed: _this3.playing pressed: _this3.playing
}); });
target.setAttribute('aria-label', i18n.get(_this3.playing ? 'pause' : 'play', _this3.config));
}); // Only update controls on non timeupdate events }); // Only update controls on non timeupdate events
if (is$1.event(event) && event.type === 'timeupdate') { if (is$1.event(event) && event.type === 'timeupdate') {
@ -4649,19 +4702,6 @@ function () {
// L key // L key
player.loop = !player.loop; player.loop = !player.loop;
break; break;
/* case 73:
this.setLoop('start');
break;
case 76:
this.setLoop();
break;
case 79:
this.setLoop('end');
break; */
default:
break;
} // Escape is handle natively when in full screen } // Escape is handle natively when in full screen
// So we only need to worry about non native // So we only need to worry about non native
@ -4878,19 +4918,15 @@ function () {
on.call(player, player.media, 'durationchange loadeddata loadedmetadata', function (event) { on.call(player, player.media, 'durationchange loadeddata loadedmetadata', function (event) {
return controls.durationUpdate.call(player, event); return controls.durationUpdate.call(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
on.call(player, player.media, 'canplay loadeddata', function () {
toggleHidden(elements.volume, !player.hasAudio);
toggleHidden(elements.buttons.mute, !player.hasAudio);
}); // Handle the media finishing }); // Handle the media finishing
on.call(player, player.media, 'ended', function () { on.call(player, player.media, 'ended', function () {
// Show poster on end // Show poster on end
if (player.isHTML5 && player.isVideo && player.config.resetOnEnd) { if (player.isHTML5 && player.isVideo && player.config.resetOnEnd) {
// Restart // Restart
player.restart(); player.restart(); // Call pause otherwise IE11 will start playing the video again
player.pause();
} }
}); // Check for buffer progress }); // Check for buffer progress
@ -5003,7 +5039,7 @@ function () {
} // Only call default handler if not prevented in custom handler } // Only call default handler if not prevented in custom handler
if (returned && is$1.function(defaultHandler)) { if (returned !== false && is$1.function(defaultHandler)) {
defaultHandler.call(player, event); defaultHandler.call(player, event);
} }
} // Trigger custom and default handlers } // Trigger custom and default handlers
@ -5391,12 +5427,13 @@ var loadjs_umd = createCommonjsModule(function (module, exports) {
async = args.async, async = args.async,
maxTries = (args.numRetries || 0) + 1, maxTries = (args.numRetries || 0) + 1,
beforeCallbackFn = args.before || devnull, beforeCallbackFn = args.before || devnull,
pathname = path.replace(/[\?|#].*$/, ''),
pathStripped = path.replace(/^(css|img)!/, ''), pathStripped = path.replace(/^(css|img)!/, ''),
isLegacyIECss, isLegacyIECss,
e; e;
numTries = numTries || 0; numTries = numTries || 0;
if (/(^css!|\.css$)/.test(path)) { if (/(^css!|\.css$)/.test(pathname)) {
// css // css
e = doc.createElement('link'); e = doc.createElement('link');
e.rel = 'stylesheet'; e.rel = 'stylesheet';
@ -5409,7 +5446,7 @@ var loadjs_umd = createCommonjsModule(function (module, exports) {
e.rel = 'preload'; e.rel = 'preload';
e.as = 'style'; e.as = 'style';
} }
} else if (/(^img!|\.(png|gif|jpg|svg)$)/.test(path)) { } else if (/(^img!|\.(png|gif|jpg|svg|webp)$)/.test(pathname)) {
// image // image
e = doc.createElement('img'); e = doc.createElement('img');
e.src = pathStripped; e.src = pathStripped;
@ -5882,6 +5919,12 @@ var vimeo = {
frame.setAttribute('tabindex', -1); frame.setAttribute('tabindex', -1);
} }
}); });
player.embed.on('bufferstart', function () {
triggerEvent.call(player, player.media, 'waiting');
});
player.embed.on('bufferend', function () {
triggerEvent.call(player, player.media, 'playing');
});
player.embed.on('play', function () { player.embed.on('play', function () {
assurePlaybackState.call(player, true); assurePlaybackState.call(player, true);
triggerEvent.call(player, player.media, 'playing'); triggerEvent.call(player, player.media, 'playing');
@ -6315,7 +6358,9 @@ var youtube = {
assurePlaybackState$1.call(player, false); assurePlaybackState$1.call(player, false);
break; break;
default: case 3:
// Trigger waiting event to add loading classes to container as the video buffers.
triggerEvent.call(player, player.media, 'waiting');
break; break;
} }
@ -6357,11 +6402,13 @@ var media = {
wrap(this.media, this.elements.wrapper); // Faux poster container wrap(this.media, this.elements.wrapper); // Faux poster container
if (this.isEmbed) {
this.elements.poster = createElement('div', { this.elements.poster = createElement('div', {
class: this.config.classNames.poster class: this.config.classNames.poster
}); });
this.elements.wrapper.appendChild(this.elements.poster); this.elements.wrapper.appendChild(this.elements.poster);
} }
}
if (this.isHTML5) { if (this.isHTML5) {
html5.extend.call(this); html5.extend.call(this);
@ -6730,9 +6777,6 @@ function () {
} }
break; break;
default:
break;
} }
} }
/** /**
@ -7004,7 +7048,7 @@ function () {
cb: Date.now(), cb: Date.now(),
AV_WIDTH: 640, AV_WIDTH: 640,
AV_HEIGHT: 480, AV_HEIGHT: 480,
AV_CDIM2: this.publisherId AV_CDIM2: config.publisherId
}; };
var base = 'https://go.aniview.com/api/adserver6/vast/'; var base = 'https://go.aniview.com/api/adserver6/vast/';
return "".concat(base, "?").concat(buildUrlParams(params)); return "".concat(base, "?").concat(buildUrlParams(params));
@ -7069,6 +7113,21 @@ var parseVtt = function parseVtt(vttDataString) {
*/ */
var fitRatio = function fitRatio(ratio, outer) {
var targetRatio = outer.width / outer.height;
var result = {};
if (ratio > targetRatio) {
result.width = outer.width;
result.height = 1 / ratio * outer.width;
} else {
result.height = outer.height;
result.width = ratio * outer.height;
}
return result;
};
var PreviewThumbnails = var PreviewThumbnails =
/*#__PURE__*/ /*#__PURE__*/
function () { function () {
@ -7613,9 +7672,15 @@ function () {
}, { }, {
key: "setScrubbingContainerSize", key: "setScrubbingContainerSize",
value: function setScrubbingContainerSize() { value: function setScrubbingContainerSize() {
this.elements.scrubbing.container.style.width = "".concat(this.player.media.clientWidth, "px"); // Can't use media.clientHeight - html5 video goes big and does black bars above and below var _fitRatio = fitRatio(this.thumbAspectRatio, {
width: this.player.media.clientWidth,
height: this.player.media.clientHeight
}),
width = _fitRatio.width,
height = _fitRatio.height;
this.elements.scrubbing.container.style.height = "".concat(this.player.media.clientWidth / this.thumbAspectRatio, "px"); this.elements.scrubbing.container.style.width = "".concat(width, "px");
this.elements.scrubbing.container.style.height = "".concat(height, "px");
} // Sprites need to be offset to the correct location } // Sprites need to be offset to the correct location
}, { }, {
@ -7628,9 +7693,9 @@ function () {
var multiplier = this.thumbContainerHeight / frame.h; // eslint-disable-next-line no-param-reassign var multiplier = this.thumbContainerHeight / frame.h; // eslint-disable-next-line no-param-reassign
previewImage.style.height = "".concat(Math.floor(previewImage.naturalHeight * multiplier), "px"); // eslint-disable-next-line no-param-reassign previewImage.style.height = "".concat(previewImage.naturalHeight * multiplier, "px"); // eslint-disable-next-line no-param-reassign
previewImage.style.width = "".concat(Math.floor(previewImage.naturalWidth * multiplier), "px"); // eslint-disable-next-line no-param-reassign previewImage.style.width = "".concat(previewImage.naturalWidth * multiplier, "px"); // eslint-disable-next-line no-param-reassign
previewImage.style.left = "-".concat(frame.x * multiplier, "px"); // eslint-disable-next-line no-param-reassign previewImage.style.left = "-".concat(frame.x * multiplier, "px"); // eslint-disable-next-line no-param-reassign
@ -7668,8 +7733,13 @@ function () {
key: "thumbContainerHeight", key: "thumbContainerHeight",
get: function get() { get: function get() {
if (this.mouseDown) { if (this.mouseDown) {
// Can't use media.clientHeight - HTML5 video goes big and does black bars above and below var _fitRatio2 = fitRatio(this.thumbAspectRatio, {
return Math.floor(this.player.media.clientWidth / this.thumbAspectRatio); width: this.player.media.clientWidth,
height: this.player.media.clientHeight
}),
height = _fitRatio2.height;
return height;
} }
return Math.floor(this.player.media.clientWidth / this.thumbAspectRatio / 4); return Math.floor(this.player.media.clientWidth / this.thumbAspectRatio / 4);
@ -8153,10 +8223,10 @@ function () {
key: "pause", key: "pause",
value: function pause() { value: function pause() {
if (!this.playing || !is$1.function(this.media.pause)) { if (!this.playing || !is$1.function(this.media.pause)) {
return; return null;
} }
this.media.pause(); return this.media.pause();
} }
/** /**
* Get playing state * Get playing state
@ -8174,10 +8244,10 @@ function () {
var toggle = is$1.boolean(input) ? input : !this.playing; var toggle = is$1.boolean(input) ? input : !this.playing;
if (toggle) { if (toggle) {
this.play(); return this.play();
} else {
this.pause();
} }
return this.pause();
} }
/** /**
* Stop playback * Stop playback
@ -8210,7 +8280,7 @@ function () {
}, { }, {
key: "rewind", key: "rewind",
value: function rewind(seekTime) { value: function rewind(seekTime) {
this.currentTime = this.currentTime - (is$1.number(seekTime) ? seekTime : this.config.seekTime); this.currentTime -= is$1.number(seekTime) ? seekTime : this.config.seekTime;
} }
/** /**
* Fast forward * Fast forward
@ -8220,7 +8290,7 @@ function () {
}, { }, {
key: "forward", key: "forward",
value: function forward(seekTime) { value: function forward(seekTime) {
this.currentTime = this.currentTime + (is$1.number(seekTime) ? seekTime : this.config.seekTime); this.currentTime += is$1.number(seekTime) ? seekTime : this.config.seekTime;
} }
/** /**
* Seek to a time * Seek to a time

1284
dist/plyr.polyfilled.js vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1280
dist/plyr.polyfilled.mjs vendored

File diff suppressed because it is too large Load Diff

2
dist/plyr.svg vendored

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@ -1,7 +1,6 @@
// ========================================================================== // ==========================================================================
// Gulp build script // Gulp build script
// ========================================================================== // ==========================================================================
/* global require, __dirname */
/* eslint no-console: "off" */ /* eslint no-console: "off" */
const path = require('path'); const path = require('path');
@ -41,6 +40,7 @@ const plumber = require('gulp-plumber');
const size = require('gulp-size'); const size = require('gulp-size');
const sourcemaps = require('gulp-sourcemaps'); const sourcemaps = require('gulp-sourcemaps');
const through = require('through2'); const through = require('through2');
const browserSync = require('browser-sync').create();
// ------------------------------------ // ------------------------------------
// Deployment // Deployment
// ------------------------------------ // ------------------------------------
@ -222,7 +222,13 @@ Object.entries(build.sprite).forEach(([filename, entry]) => {
gulp gulp
.src(src) .src(src)
.pipe(plumber()) .pipe(plumber())
.pipe(imagemin()) .pipe(
imagemin([
imagemin.svgo({
plugins: [{ removeViewBox: false }],
}),
]),
)
.pipe(svgstore()) .pipe(svgstore())
.pipe(rename({ basename: path.parse(filename).name })) .pipe(rename({ basename: path.parse(filename).name }))
.pipe(size(sizeOptions)) .pipe(size(sizeOptions))
@ -245,11 +251,23 @@ gulp.task('watch', () => {
gulp.watch(paths.demo.src.sass, gulp.parallel(...tasks.css)); gulp.watch(paths.demo.src.sass, gulp.parallel(...tasks.css));
}); });
// Serve via browser sync
gulp.task('serve', () =>
browserSync.init({
server: {
baseDir: paths.demo.root,
},
notify: false,
watch: true,
ghostMode: false,
}),
);
// Build distribution // Build distribution
gulp.task('build', gulp.series(tasks.clean, gulp.parallel(...tasks.js, ...tasks.css, ...tasks.sprite))); gulp.task('build', gulp.series(tasks.clean, gulp.parallel(...tasks.js, ...tasks.css, ...tasks.sprite)));
// Default gulp task // Default gulp task
gulp.task('default', gulp.series('build', 'watch')); gulp.task('default', gulp.series('build', gulp.parallel('serve', 'watch')));
// Publish a version to CDN and demo // Publish a version to CDN and demo
// -------------------------------------------- // --------------------------------------------
@ -331,7 +349,10 @@ gulp.task('version', done => {
const files = ['plyr.js', 'plyr.polyfilled.js', 'config/defaults.js']; const files = ['plyr.js', 'plyr.polyfilled.js', 'config/defaults.js'];
return gulp return gulp
.src(files.map(file => path.join(__dirname, `src/js/${file}`)), { base: '.' }) .src(
files.map(file => path.join(__dirname, `src/js/${file}`)),
{ base: '.' },
)
.pipe(replace(semver, `v${version}`)) .pipe(replace(semver, `v${version}`))
.pipe(replace(cdnpath, `${domain}/${version}/`)) .pipe(replace(cdnpath, `${domain}/${version}/`))
.pipe(gulp.dest('./')); .pipe(gulp.dest('./'));

View File

@ -5,6 +5,7 @@
"homepage": "https://plyr.io", "homepage": "https://plyr.io",
"author": "Sam Potts <sam@potts.es>", "author": "Sam Potts <sam@potts.es>",
"main": "dist/plyr.js", "main": "dist/plyr.js",
"types": "src/js/plyr.d.ts",
"module": "dist/plyr.min.mjs", "module": "dist/plyr.min.mjs",
"jsnext:main": "dist/plyr.min.mjs", "jsnext:main": "dist/plyr.min.mjs",
"browser": "dist/plyr.min.js", "browser": "dist/plyr.min.js",
@ -36,61 +37,62 @@
"deploy": "yarn lint && gulp deploy" "deploy": "yarn lint && gulp deploy"
}, },
"devDependencies": { "devDependencies": {
"ansi-colors": "^4.0.1", "ansi-colors": "^4.1.1",
"aws-sdk": "^2.478.0", "aws-sdk": "^2.610.0",
"@babel/core": "^7.4.5", "@babel/core": "^7.8.3",
"@babel/preset-env": "^7.4.5", "@babel/preset-env": "^7.8.3",
"babel-eslint": "^10.0.2", "babel-eslint": "^10.0.3",
"del": "^4.1.1", "browser-sync": "^2.26.7",
"eslint": "^5.16.0", "del": "^5.1.0",
"eslint-config-airbnb-base": "^13.1.0", "eslint": "^6.8.0",
"eslint-config-prettier": "^5.0.0", "eslint-config-airbnb-base": "^14.0.0",
"eslint-plugin-import": "^2.17.3", "eslint-config-prettier": "^6.9.0",
"eslint-plugin-simple-import-sort": "^4.0.0", "eslint-plugin-import": "^2.20.0",
"eslint-plugin-simple-import-sort": "^5.0.1",
"fancy-log": "^1.3.3", "fancy-log": "^1.3.3",
"fastly-purge": "^1.0.1", "fastly-purge": "^1.0.1",
"git-branch": "^2.0.1", "git-branch": "^2.0.1",
"gulp": "^4.0.2", "gulp": "^4.0.2",
"gulp-autoprefixer": "^6.1.0", "gulp-autoprefixer": "^7.0.1",
"gulp-awspublish": "^4.0.0", "gulp-awspublish": "^4.1.1",
"gulp-better-rollup": "^4.0.1", "gulp-better-rollup": "^4.0.1",
"gulp-clean-css": "^4.2.0", "gulp-clean-css": "^4.2.0",
"gulp-filter": "^6.0.0", "gulp-filter": "^6.0.0",
"gulp-header": "^2.0.7", "gulp-header": "^2.0.9",
"gulp-imagemin": "^6.0.0", "gulp-imagemin": "^7.1.0",
"gulp-open": "^3.0.1", "gulp-open": "^3.0.1",
"gulp-plumber": "^1.2.1", "gulp-plumber": "^1.2.1",
"gulp-postcss": "^8.0.0", "gulp-postcss": "^8.0.0",
"gulp-rename": "^1.4.0", "gulp-rename": "^2.0.0",
"gulp-replace": "^1.0.0", "gulp-replace": "^1.0.0",
"gulp-sass": "^4.0.2", "gulp-sass": "^4.0.2",
"gulp-size": "^3.0.0", "gulp-size": "^3.0.0",
"gulp-sourcemaps": "^2.6.5", "gulp-sourcemaps": "^2.6.5",
"gulp-svgstore": "^7.0.1", "gulp-svgstore": "^7.0.1",
"gulp-terser": "^1.2.0", "gulp-terser": "^1.2.0",
"postcss-custom-properties": "^9.0.1", "postcss-custom-properties": "^9.0.2",
"prettier-eslint": "^9.0.0", "prettier-eslint": "^9.0.1",
"prettier-stylelint": "^0.4.2", "prettier-stylelint": "^0.4.2",
"remark-cli": "^6.0.1", "remark-cli": "^7.0.1",
"remark-validate-links": "^8.0.3", "remark-validate-links": "^9.2.0",
"rollup": "^1.15.6", "rollup": "^1.29.1",
"rollup-plugin-babel": "^4.3.2", "rollup-plugin-babel": "^4.3.3",
"rollup-plugin-commonjs": "^10.0.0", "rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-node-resolve": "^5.0.3", "rollup-plugin-node-resolve": "^5.2.0",
"stylelint": "^10.1.0", "stylelint": "^13.0.0",
"stylelint-config-prettier": "^5.2.0", "stylelint-config-prettier": "^8.0.1",
"stylelint-config-recommended": "^2.2.0", "stylelint-config-recommended": "^3.0.0",
"stylelint-config-sass-guidelines": "^6.0.0", "stylelint-config-sass-guidelines": "^6.2.0",
"stylelint-order": "^3.0.0", "stylelint-order": "^4.0.0",
"stylelint-scss": "^3.8.0", "stylelint-scss": "^3.13.0",
"stylelint-selector-bem-pattern": "^2.1.0", "stylelint-selector-bem-pattern": "^2.1.0",
"through2": "^3.0.1" "through2": "^3.0.1"
}, },
"dependencies": { "dependencies": {
"core-js": "^3.1.4", "core-js": "^3.6.4",
"custom-event-polyfill": "^1.0.7", "custom-event-polyfill": "^1.0.7",
"loadjs": "^3.6.1", "loadjs": "^4.2.0",
"rangetouch": "^2.0.0", "rangetouch": "^2.0.0",
"url-polyfill": "^1.1.5" "url-polyfill": "^1.1.7"
} }
} }

View File

@ -9,22 +9,29 @@
"**/node_modules": true, "**/node_modules": true,
"**/dist": true "**/dist": true
}, },
// Linting // Linting
"stylelint.enable": true, "stylelint.enable": true,
"css.validate": false, "css.validate": false,
"scss.validate": false, "scss.validate": false,
"javascript.validate.enable": false, "javascript.validate.enable": false,
// Prettier
"prettier.eslintIntegration": true,
"prettier.stylelintIntegration": true,
// Formatting // Formatting
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.tabSize": 4, "editor.tabSize": 4,
"editor.insertSpaces": true, "editor.insertSpaces": true,
"editor.formatOnSave": true, "editor.formatOnSave": true,
// Trim on save // Trim on save
"files.trimTrailingWhitespace": true "files.trimTrailingWhitespace": true,
// Special file associations
"files.associations": {
".eslintrc": "jsonc"
},
"editor.codeActionsOnSave": {
"source.fixAll": true
}
} }
} }

View File

@ -116,7 +116,7 @@ import Plyr from 'plyr';
const player = new Plyr('#player'); const player = new Plyr('#player');
``` ```
Alertnatively you can include the `plyr.js` script before the closing `</body>` tag and then in your JS create a new instance of Plyr as below. Alternatively you can include the `plyr.js` script before the closing `</body>` tag and then in your JS create a new instance of Plyr as below.
```html ```html
<script src="path/to/plyr.js"></script> <script src="path/to/plyr.js"></script>
@ -630,7 +630,7 @@ Fullscreen in Plyr is supported by all browsers that [currently support it](http
Plyr supports the last 2 versions of most _modern_ browsers. Plyr supports the last 2 versions of most _modern_ browsers.
| Browser | Supported | | Browser | Supported |
| ------------- | ------------- | | ------------- | --------------- |
| Safari | ✓ | | Safari | ✓ |
| Mobile Safari | ✓&sup1; | | Mobile Safari | ✓&sup1; |
| Firefox | ✓ | | Firefox | ✓ |
@ -638,7 +638,7 @@ Plyr supports the last 2 versions of most _modern_ browsers.
| Opera | ✓ | | Opera | ✓ |
| Edge | ✓ | | Edge | ✓ |
| IE11 | ✓&sup3; | | IE11 | ✓&sup3; |
| IE10 | ✓&sup2;&sup3; | | IE10 | ✓<sup>2,3</sup> |
1. Mobile Safari on the iPhone forces the native player for `<video>` unless the `playsinline` attribute is present. Volume controls are also disabled as they are handled device wide. 1. Mobile Safari on the iPhone forces the native player for `<video>` unless the `playsinline` attribute is present. Volume controls are also disabled as they are handled device wide.
2. Native player used (no support for `<progress>` or `<input type="range">`) but the API is supported. No native fullscreen support, fallback can be used (see [options](#options)). 2. Native player used (no support for `<progress>` or `<input type="range">`) but the API is supported. No native fullscreen support, fallback can be used (see [options](#options)).
@ -682,7 +682,7 @@ Some awesome folks have made plugins for CMSs and Components for JavaScript fram
| --------- | -------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | | --------- | -------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| WordPress | Brandon Lavigne ([@drrobotnik](https://github.com/drrobotnik)) | [https://wordpress.org/plugins/plyr/](https://wordpress.org/plugins/plyr/) | | WordPress | Brandon Lavigne ([@drrobotnik](https://github.com/drrobotnik)) | [https://wordpress.org/plugins/plyr/](https://wordpress.org/plugins/plyr/) |
| Angular | Simon Bobrov ([@smnbbrv](https://github.com/smnbbrv)) | [https://github.com/smnbbrv/ngx-plyr](https://github.com/smnbbrv/ngx-plyr) | | Angular | Simon Bobrov ([@smnbbrv](https://github.com/smnbbrv)) | [https://github.com/smnbbrv/ngx-plyr](https://github.com/smnbbrv/ngx-plyr) |
| React | Jose Miguel Bejarano ([@xDae](https://github.com/xDae)) | [https://github.com/xDae/react-plyr](https://github.com/xDae/react-plyr) | | React | Chintan Prajapati ([@chintan9](https://github.com/chintan9)) | [https://github.com/chintan9/plyr-react](https://github.com/chintan9/plyr-react) |
| Vue | Gabe Dunn ([@redxtech](https://github.com/redxtech)) | [https://github.com/redxtech/vue-plyr](https://github.com/redxtech/vue-plyr) | | Vue | Gabe Dunn ([@redxtech](https://github.com/redxtech)) | [https://github.com/redxtech/vue-plyr](https://github.com/redxtech/vue-plyr) |
| Neos | Jon Uhlmann ([@jonnitto](https://github.com/jonnitto)) | [https://packagist.org/packages/jonnitto/plyr](https://packagist.org/packages/jonnitto/plyr) | | Neos | Jon Uhlmann ([@jonnitto](https://github.com/jonnitto)) | [https://packagist.org/packages/jonnitto/plyr](https://packagist.org/packages/jonnitto/plyr) |
| Kirby | Dominik Pschenitschni ([@dpschen](https://github.com/dpschen)) | [https://github.com/dpschen/kirby-plyrtag](https://github.com/dpschen/kirby-plyrtag) | | Kirby | Dominik Pschenitschni ([@dpschen](https://github.com/dpschen)) | [https://github.com/dpschen/kirby-plyrtag](https://github.com/dpschen/kirby-plyrtag) |
@ -735,9 +735,7 @@ If you want to be added to the list, open a pull request. It'd be awesome to see
# Useful links and credits # Useful links and credits
Credit to the PayPal HTML5 Video player from which Plyr's caption functionality was originally ported from: - [PayPal's Accessible HTML5 Video Player (which Plyr was originally ported from)](https://github.com/paypal/accessible-html5-video-player)
- [PayPal's Accessible HTML5 Video Player](https://github.com/paypal/accessible-html5-video-player)
- [An awesome guide for Plyr in Japanese!](http://syncer.jp/how-to-use-plyr-io) by [@arayutw](https://twitter.com/arayutw) - [An awesome guide for Plyr in Japanese!](http://syncer.jp/how-to-use-plyr-io) by [@arayutw](https://twitter.com/arayutw)
# Thanks # Thanks

View File

@ -70,6 +70,8 @@ const defaults = {
quality: { quality: {
default: 576, default: 576,
options: [4320, 2880, 2160, 1440, 1080, 720, 576, 480, 360, 240], options: [4320, 2880, 2160, 1440, 1080, 720, 576, 480, 360, 240],
forced: false,
onChange: null,
}, },
// Set loops // Set loops
@ -164,6 +166,7 @@ const defaults = {
frameTitle: 'Player for {title}', frameTitle: 'Player for {title}',
captions: 'Captions', captions: 'Captions',
settings: 'Settings', settings: 'Settings',
pip: 'PIP',
menuBack: 'Go back to previous menu', menuBack: 'Go back to previous menu',
speed: 'Speed', speed: 'Speed',
normal: 'Normal', normal: 'Normal',

21
src/js/controls.js vendored
View File

@ -139,10 +139,7 @@ const controls = {
// Create hidden text label // Create hidden text label
createLabel(key, attr = {}) { createLabel(key, attr = {}) {
const text = i18n.get(key, this.config); const text = i18n.get(key, this.config);
const attributes = { ...attr, class: [attr.class, this.config.classNames.hidden].filter(Boolean).join(' ') };
const attributes = Object.assign({}, attr, {
class: [attr.class, this.config.classNames.hidden].filter(Boolean).join(' '),
});
return createElement('span', attributes, text); return createElement('span', attributes, text);
}, },
@ -402,7 +399,8 @@ const controls = {
// https://bugzilla.mozilla.org/show_bug.cgi?id=1220143 // https://bugzilla.mozilla.org/show_bug.cgi?id=1220143
bindMenuItemShortcuts(menuItem, type) { bindMenuItemShortcuts(menuItem, type) {
// Navigate through menus via arrow keys and space // Navigate through menus via arrow keys and space
on( on.call(
this,
menuItem, menuItem,
'keydown keyup', 'keydown keyup',
event => { event => {
@ -452,7 +450,7 @@ const controls = {
// Enter will fire a `click` event but we still need to manage focus // Enter will fire a `click` event but we still need to manage focus
// So we bind to keyup which fires after and set focus here // So we bind to keyup which fires after and set focus here
on(menuItem, 'keyup', event => { on.call(this, menuItem, 'keyup', event => {
if (event.which !== 13) { if (event.which !== 13) {
return; return;
} }
@ -1380,7 +1378,9 @@ const controls = {
} }
// Volume range control // Volume range control
if (control === 'volume') { // Ignored on iOS as it's handled globally
// https://developer.apple.com/library/safari/documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/Device-SpecificConsiderations/Device-SpecificConsiderations.html
if (control === 'volume' && !browser.isIos) {
// Set the attributes // Set the attributes
const attributes = { const attributes = {
max: 1, max: 1,
@ -1463,7 +1463,7 @@ const controls = {
bindMenuItemShortcuts.call(this, menuItem, type); bindMenuItemShortcuts.call(this, menuItem, type);
// Show menu on click // Show menu on click
on(menuItem, 'click', () => { on.call(this, menuItem, 'click', () => {
showMenuPanel.call(this, type, false); showMenuPanel.call(this, type, false);
}); });
@ -1515,7 +1515,8 @@ const controls = {
); );
// Go back via keyboard // Go back via keyboard
on( on.call(
this,
pane, pane,
'keydown', 'keydown',
event => { event => {
@ -1535,7 +1536,7 @@ const controls = {
); );
// Go back via button click // Go back via button click
on(backButton, 'click', () => { on.call(this, backButton, 'click', () => {
showMenuPanel.call(this, 'home', false); showMenuPanel.call(this, 'home', false);
}); });

View File

@ -228,7 +228,7 @@ class Fullscreen {
} else if (!Fullscreen.native || this.forceFallback) { } else if (!Fullscreen.native || this.forceFallback) {
toggleFallback.call(this, true); toggleFallback.call(this, true);
} else if (!this.prefix) { } else if (!this.prefix) {
this.target.requestFullscreen(); this.target.requestFullscreen({ navigationUI: "hide" });
} else if (!is.empty(this.prefix)) { } else if (!is.empty(this.prefix)) {
this.target[`${this.prefix}Request${this.property}`](); this.target[`${this.prefix}Request${this.property}`]();
} }

View File

@ -30,6 +30,11 @@ const html5 = {
// Get quality levels // Get quality levels
getQualityOptions() { getQualityOptions() {
// Whether we're forcing all options (e.g. for streaming)
if (this.config.quality.forced) {
return this.config.quality.options;
}
// Get sizes from <source> elements // Get sizes from <source> elements
return html5.getSources return html5.getSources
.call(this) .call(this)
@ -60,6 +65,10 @@ const html5 = {
return source && Number(source.getAttribute('size')); return source && Number(source.getAttribute('size'));
}, },
set(input) { set(input) {
// If we're using an an external handler...
if (player.config.quality.forced && is.function(player.config.quality.onChange)) {
player.config.quality.onChange(input);
} else {
// Get sources // Get sources
const sources = html5.getSources.call(player); const sources = html5.getSources.call(player);
// Get first match for requested size // Get first match for requested size
@ -80,6 +89,10 @@ const html5 = {
if (preload !== 'none' || readyState) { if (preload !== 'none' || readyState) {
// Restore time // Restore time
player.once('loadedmetadata', () => { player.once('loadedmetadata', () => {
if (player.currentTime === 0) {
return;
}
player.currentTime = currentTime; player.currentTime = currentTime;
// Resume playing // Resume playing
@ -91,6 +104,7 @@ const html5 = {
// Load new source // Load new source
player.media.load(); player.media.load();
} }
}
// Trigger change event // Trigger change event
triggerEvent.call(player, player.media, 'qualitychange', false, { triggerEvent.call(player, player.media, 'qualitychange', false, {

View File

@ -6,7 +6,7 @@ import controls from './controls';
import ui from './ui'; import ui from './ui';
import { repaint } from './utils/animation'; import { repaint } from './utils/animation';
import browser from './utils/browser'; import browser from './utils/browser';
import { getElement, getElements, matches, toggleClass, toggleHidden } from './utils/elements'; import { getElement, getElements, matches, toggleClass } from './utils/elements';
import { off, on, once, toggleListener, triggerEvent } from './utils/events'; import { off, on, once, toggleListener, triggerEvent } from './utils/events';
import is from './utils/is'; import is from './utils/is';
import { getAspectRatio, setAspectRatio } from './utils/style'; import { getAspectRatio, setAspectRatio } from './utils/style';
@ -377,19 +377,15 @@ class Listeners {
controls.durationUpdate.call(player, event), controls.durationUpdate.call(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
on.call(player, player.media, 'canplay loadeddata', () => {
toggleHidden(elements.volume, !player.hasAudio);
toggleHidden(elements.buttons.mute, !player.hasAudio);
});
// Handle the media finishing // Handle the media finishing
on.call(player, player.media, 'ended', () => { on.call(player, player.media, 'ended', () => {
// Show poster on end // Show poster on end
if (player.isHTML5 && player.isVideo && player.config.resetOnEnd) { if (player.isHTML5 && player.isVideo && player.config.resetOnEnd) {
// Restart // Restart
player.restart(); player.restart();
// Call pause otherwise IE11 will start playing the video again
player.pause();
} }
}); });
@ -663,7 +659,7 @@ class Listeners {
const code = event.keyCode ? event.keyCode : event.which; const code = event.keyCode ? event.keyCode : event.which;
const attribute = 'play-on-seeked'; const attribute = 'play-on-seeked';
if (is.keyboardEvent(event) && (code !== 39 && code !== 37)) { if (is.keyboardEvent(event) && code !== 39 && code !== 37) {
return; return;
} }

View File

@ -39,12 +39,14 @@ const media = {
wrap(this.media, this.elements.wrapper); wrap(this.media, this.elements.wrapper);
// Faux poster container // Faux poster container
if (this.isEmbed) {
this.elements.poster = createElement('div', { this.elements.poster = createElement('div', {
class: this.config.classNames.poster, class: this.config.classNames.poster,
}); });
this.elements.wrapper.appendChild(this.elements.poster); this.elements.wrapper.appendChild(this.elements.poster);
} }
}
if (this.isHTML5) { if (this.isHTML5) {
html5.extend.call(this); html5.extend.call(this);

View File

@ -136,7 +136,7 @@ class Ads {
cb: Date.now(), cb: Date.now(),
AV_WIDTH: 640, AV_WIDTH: 640,
AV_HEIGHT: 480, AV_HEIGHT: 480,
AV_CDIM2: this.publisherId, AV_CDIM2: config.publisherId,
}; };
const base = 'https://go.aniview.com/api/adserver6/vast/'; const base = 'https://go.aniview.com/api/adserver6/vast/';

View File

@ -63,6 +63,20 @@ const parseVtt = vttDataString => {
* - This implementation uses multiple separate img elements. Other implementations use background-image on one element. This would be nice and simple, but Firefox and Safari have flickering issues with replacing backgrounds of larger images. It seems that YouTube perhaps only avoids this because they don't have the option for high-res previews (even the fullscreen ones, when mousedown/seeking). Images appear over the top of each other, and previous ones are discarded once the new ones have been rendered * - This implementation uses multiple separate img elements. Other implementations use background-image on one element. This would be nice and simple, but Firefox and Safari have flickering issues with replacing backgrounds of larger images. It seems that YouTube perhaps only avoids this because they don't have the option for high-res previews (even the fullscreen ones, when mousedown/seeking). Images appear over the top of each other, and previous ones are discarded once the new ones have been rendered
*/ */
const fitRatio = (ratio, outer) => {
const targetRatio = outer.width / outer.height;
const result = {};
if (ratio > targetRatio) {
result.width = outer.width;
result.height = (1 / ratio) * outer.width;
} else {
result.height = outer.height;
result.width = ratio * outer.height;
}
return result;
};
class PreviewThumbnails { class PreviewThumbnails {
/** /**
* PreviewThumbnails constructor. * PreviewThumbnails constructor.
@ -540,8 +554,11 @@ class PreviewThumbnails {
get thumbContainerHeight() { get thumbContainerHeight() {
if (this.mouseDown) { if (this.mouseDown) {
// Can't use media.clientHeight - HTML5 video goes big and does black bars above and below const { height } = fitRatio(this.thumbAspectRatio, {
return Math.floor(this.player.media.clientWidth / this.thumbAspectRatio); width: this.player.media.clientWidth,
height: this.player.media.clientHeight,
});
return height;
} }
return Math.floor(this.player.media.clientWidth / this.thumbAspectRatio / 4); return Math.floor(this.player.media.clientWidth / this.thumbAspectRatio / 4);
@ -624,9 +641,12 @@ class PreviewThumbnails {
// Can't use 100% width, in case the video is a different aspect ratio to the video container // Can't use 100% width, in case the video is a different aspect ratio to the video container
setScrubbingContainerSize() { setScrubbingContainerSize() {
this.elements.scrubbing.container.style.width = `${this.player.media.clientWidth}px`; const { width, height } = fitRatio(this.thumbAspectRatio, {
// Can't use media.clientHeight - html5 video goes big and does black bars above and below width: this.player.media.clientWidth,
this.elements.scrubbing.container.style.height = `${this.player.media.clientWidth / this.thumbAspectRatio}px`; height: this.player.media.clientHeight,
});
this.elements.scrubbing.container.style.width = `${width}px`;
this.elements.scrubbing.container.style.height = `${height}px`;
} }
// Sprites need to be offset to the correct location // Sprites need to be offset to the correct location
@ -639,9 +659,9 @@ class PreviewThumbnails {
const multiplier = this.thumbContainerHeight / frame.h; const multiplier = this.thumbContainerHeight / frame.h;
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
previewImage.style.height = `${Math.floor(previewImage.naturalHeight * multiplier)}px`; previewImage.style.height = `${previewImage.naturalHeight * multiplier}px`;
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
previewImage.style.width = `${Math.floor(previewImage.naturalWidth * multiplier)}px`; previewImage.style.width = `${previewImage.naturalWidth * multiplier}px`;
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
previewImage.style.left = `-${frame.x * multiplier}px`; previewImage.style.left = `-${frame.x * multiplier}px`;
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign

View File

@ -335,6 +335,14 @@ const vimeo = {
} }
}); });
player.embed.on('bufferstart', () => {
triggerEvent.call(player, player.media, 'waiting');
});
player.embed.on('bufferend', () => {
triggerEvent.call(player, player.media, 'playing');
});
player.embed.on('play', () => { player.embed.on('play', () => {
assurePlaybackState.call(player, true); assurePlaybackState.call(player, true);
triggerEvent.call(player, player.media, 'playing'); triggerEvent.call(player, player.media, 'playing');

View File

@ -416,6 +416,12 @@ const youtube = {
break; break;
case 3:
// Trigger waiting event to add loading classes to container as the video buffers.
triggerEvent.call(player, player.media, 'waiting');
break;
default: default:
break; break;
} }

560
src/js/plyr.d.ts vendored Normal file
View File

@ -0,0 +1,560 @@
// Type definitions for plyr 3.5
// Project: https://plyr.io
// Definitions by: ondratra <https://github.com/ondratra>
// TypeScript Version: 3.0
export = Plyr;
export as namespace Plyr;
declare class Plyr {
/**
* Setup a new instance
*/
static setup(targets: NodeList | HTMLElement | HTMLElement[] | string, options?: Plyr.Options): Plyr[];
/**
* Check for support
* @param mediaType
* @param provider
* @param playsInline Whether the player has the playsinline attribute (only applicable to iOS 10+)
*/
static supported(mediaType?: Plyr.MediaType, provider?: Plyr.Provider, playsInline?: boolean): Plyr.Support;
constructor(targets: NodeList | HTMLElement | HTMLElement[] | string, options?: Plyr.Options);
/**
* Indicates if the current player is HTML5.
*/
readonly isHTML5: boolean;
/**
* Indicates if the current player is an embedded player.
*/
readonly isEmbed: boolean;
/**
* Indicates if the current player is playing.
*/
readonly playing: boolean;
/**
* Indicates if the current player is paused.
*/
readonly paused: boolean;
/**
* Indicates if the current player is stopped.
*/
readonly stopped: boolean;
/**
* Indicates if the current player has finished playback.
*/
readonly ended: boolean;
/**
* Returns a float between 0 and 1 indicating how much of the media is buffered
*/
readonly buffered: number;
/**
* Gets or sets the currentTime for the player. The setter accepts a float in seconds.
*/
currentTime: number;
/**
* Indicates if the current player is seeking.
*/
readonly seeking: boolean;
/**
* Returns the duration for the current media.
*/
readonly duration: number;
/**
* Gets or sets the volume for the player. The setter accepts a float between 0 and 1.
*/
volume: number;
/**
* Gets or sets the muted state of the player. The setter accepts a boolean.
*/
muted: boolean;
/**
* Indicates if the current media has an audio track.
*/
readonly hasAudio: boolean;
/**
* Gets or sets the speed for the player. The setter accepts a value in the options specified in your config. Generally the minimum should be 0.5.
*/
speed: number;
/**
* Gets or sets the quality for the player. The setter accepts a value from the options specified in your config.
* Remarks: YouTube only. HTML5 will follow.
*/
quality: string;
/**
* Gets or sets the current loop state of the player.
*/
loop: boolean;
/**
* Gets or sets the current source for the player.
*/
source: Plyr.SourceInfo;
/**
* Gets or sets the current poster image URL for the player.
*/
poster: string;
/**
* Gets or sets the autoplay state of the player.
*/
autoplay: boolean;
/**
* Gets or sets the caption track by index. 1 means the track is missing or captions is not active
*/
currentTrack: number;
/**
* Gets or sets the preferred captions language for the player. The setter accepts an ISO twoletter language code. Support for the languages is dependent on the captions you include.
* If your captions don't have any language data, or if you have multiple tracks with the same language, you may want to use currentTrack instead.
*/
language: string;
/**
* Gets or sets the picture-in-picture state of the player. This currently only supported on Safari 10+ on MacOS Sierra+ and iOS 10+.
*/
pip: boolean;
readonly fullscreen: Plyr.FullscreenControl;
/**
* Start playback.
* For HTML5 players, play() will return a Promise in some browsers - WebKit and Mozilla according to MDN at time of writing.
*/
play(): Promise<void> | void;
/**
* Pause playback.
*/
pause(): void;
/**
* Toggle playback, if no parameters are passed, it will toggle based on current status.
*/
togglePlay(toggle?: boolean): boolean;
/**
* Stop playback and reset to start.
*/
stop(): void;
/**
* Restart playback.
*/
restart(): void;
/**
* Rewind playback by the specified seek time. If no parameter is passed, the default seek time will be used.
*/
rewind(seekTime?: number): void;
/**
* Fast forward by the specified seek time. If no parameter is passed, the default seek time will be used.
*/
forward(seekTime?: number): void;
/**
* Increase volume by the specified step. If no parameter is passed, the default step will be used.
*/
increaseVolume(step?: number): void;
/**
* Increase volume by the specified step. If no parameter is passed, the default step will be used.
*/
decreaseVolume(step?: number): void;
/**
* Toggle captions display. If no parameter is passed, it will toggle based on current status.
*/
toggleCaptions(toggle?: boolean): void;
/**
* Trigger the airplay dialog on supported devices.
*/
airplay(): void;
/**
* Toggle the controls (video only). Takes optional truthy value to force it on/off.
*/
toggleControls(toggle: boolean): void;
/**
* Add an event listener for the specified event.
*/
on(event: Plyr.StandardEvent | Plyr.Html5Event | Plyr.YoutubeEvent, callback: (this: this, event: Plyr.PlyrEvent) => void): void;
/**
* Add an event listener for the specified event once.
*/
once(event: Plyr.StandardEvent | Plyr.Html5Event | Plyr.YoutubeEvent, callback: (this: this, event: Plyr.PlyrEvent) => void): void;
/**
* Remove an event listener for the specified event.
*/
off(event: Plyr.StandardEvent | Plyr.Html5Event | Plyr.YoutubeEvent, callback: (this: this, event: Plyr.PlyrEvent) => void): void;
/**
* Check support for a mime type.
*/
supports(type: string): boolean;
/**
* Destroy lib instance
*/
destroy(): void;
}
declare namespace Plyr {
type MediaType = "audio" | "video";
type Provider = "html5" | "youtube" | "vimeo";
type StandardEvent = "progress" | "playing" | "play" | "pause" | "timeupdate" | "volumechange" | "seeking" | "seeked" | "ratechange" | "ended" | "enterfullscreen" | "exitfullscreen"
| "captionsenabled" | "captionsdisabled" | "languagechange" | "controlshidden" | "controlsshown" | "ready";
type Html5Event = "loadstart" | "loadeddata" | "loadedmetadata" | "canplay" | "canplaythrough" | "stalled" | "waiting" | "emptied" | "cuechange" | "error";
type YoutubeEvent = "statechange" | "qualitychange" | "qualityrequested";
interface FullscreenControl {
/**
* Indicates if the current player is in fullscreen mode.
*/
readonly active: boolean;
/**
* Indicates if the current player has fullscreen enabled.
*/
readonly enabled: boolean;
/**
* Enter fullscreen. If fullscreen is not supported, a fallback ""full window/viewport"" is used instead.
*/
enter(): void;
/**
* Exit fullscreen.
*/
exit(): void;
/**
* Toggle fullscreen.
*/
toggle(): void;
}
interface Options {
/**
* Completely disable Plyr. This would allow you to do a User Agent check or similar to programmatically enable or disable Plyr for a certain UA. Example below.
*/
enabled?: boolean;
/**
* Display debugging information in the console
*/
debug?: boolean;
/**
* If a function is passed, it is assumed your method will return either an element or HTML string for the controls. Three arguments will be passed to your function;
* id (the unique id for the player), seektime (the seektime step in seconds), and title (the media title). See controls.md for more info on how the html needs to be structured.
* Defaults to ['play-large', 'play', 'progress', 'current-time', 'mute', 'volume', 'captions', 'settings', 'pip', 'airplay', 'fullscreen']
*/
controls?: string[] | ((id: string, seektime: number, title: string) => unknown) | Element;
/**
* If you're using the default controls are used then you can specify which settings to show in the menu
* Defaults to ['captions', 'quality', 'speed', 'loop']
*/
settings?: string[];
/**
* Used for internationalization (i18n) of the text within the UI.
*/
i18n?: any;
/**
* Load the SVG sprite specified as the iconUrl option (if a URL). If false, it is assumed you are handling sprite loading yourself.
*/
loadSprite?: boolean;
/**
* Specify a URL or path to the SVG sprite. See the SVG section for more info.
*/
iconUrl?: string;
/**
* Specify the id prefix for the icons used in the default controls (e.g. plyr-play would be plyr).
* This is to prevent clashes if you're using your own SVG sprite but with the default controls.
* Most people can ignore this option.
*/
iconPrefix?: string;
/**
* Specify a URL or path to a blank video file used to properly cancel network requests.
*/
blankUrl?: string;
/**
* Autoplay the media on load. This is generally advised against on UX grounds. It is also disabled by default in some browsers.
* If the autoplay attribute is present on a <video> or <audio> element, this will be automatically set to true.
*/
autoplay?: boolean;
/**
* Only allow one player playing at once.
*/
autopause?: boolean;
/**
* The time, in seconds, to seek when a user hits fast forward or rewind.
*/
seekTime?: number;
/**
* A number, between 0 and 1, representing the initial volume of the player.
*/
volume?: number;
/**
* Whether to start playback muted. If the muted attribute is present on a <video> or <audio> element, this will be automatically set to true.
*/
muted?: boolean;
/**
* Click (or tap) of the video container will toggle play/pause.
*/
clickToPlay?: boolean;
/**
* Disable right click menu on video to help as very primitive obfuscation to prevent downloads of content.
*/
disableContextMenu?: boolean;
/**
* Hide video controls automatically after 2s of no mouse or focus movement, on control element blur (tab out), on playback start or entering fullscreen.
* As soon as the mouse is moved, a control element is focused or playback is paused, the controls reappear instantly.
*/
hideControls?: boolean;
/**
* Reset the playback to the start once playback is complete.
*/
resetOnEnd?: boolean;
/**
* Enable keyboard shortcuts for focused players only or globally
*/
keyboard?: KeyboardOptions;
/**
* controls: Display control labels as tooltips on :hover & :focus (by default, the labels are screen reader only).
* seek: Display a seek tooltip to indicate on click where the media would seek to.
*/
tooltips?: TooltipOptions;
/**
* Specify a custom duration for media.
*/
duration?: number;
/**
* Displays the duration of the media on the metadataloaded event (on startup) in the current time display.
* This will only work if the preload attribute is not set to none (or is not set at all) and you choose not to display the duration (see controls option).
*/
displayDuration?: boolean;
/**
* Display the current time as a countdown rather than an incremental counter.
*/
invertTime?: boolean;
/**
* Allow users to click to toggle the above.
*/
toggleInvert?: boolean;
/**
* Allows binding of event listeners to the controls before the default handlers. See the defaults.js for available listeners.
* If your handler prevents default on the event (event.preventDefault()), the default handler will not fire.
*/
listeners?: {[key: string]: (error: PlyrEvent) => void};
/**
* active: Toggles if captions should be active by default. language: Sets the default language to load (if available). 'auto' uses the browser language.
* update: Listen to changes to tracks and update menu. This is needed for some streaming libraries, but can result in unselectable language options).
*/
captions?: CaptionOptions;
/**
* enabled: Toggles whether fullscreen should be enabled. fallback: Allow fallback to a full-window solution.
* iosNative: whether to use native iOS fullscreen when entering fullscreen (no custom controls)
*/
fullscreen?: FullScreenOptions;
/**
* The aspect ratio you want to use for embedded players.
*/
ratio?: string;
/**
* enabled: Allow use of local storage to store user settings. key: The key name to use.
*/
storage?: StorageOptions;
/**
* selected: The default speed for playback. options: Options to display in the menu. Most browsers will refuse to play slower than 0.5.
*/
speed?: SpeedOptions;
/**
* Currently only supported by YouTube. default is the default quality level, determined by YouTube. options are the options to display.
*/
quality?: QualityOptions;
/**
* active: Whether to loop the current video. If the loop attribute is present on a <video> or <audio> element,
* this will be automatically set to true This is an object to support future functionality.
*/
loop?: LoopOptions;
/**
* enabled: Whether to enable vi.ai ads. publisherId: Your unique vi.ai publisher ID.
*/
ads?: AdOptions;
}
interface QualityOptions {
default: string;
options: string[];
}
interface LoopOptions {
active: boolean;
}
interface AdOptions {
enabled: boolean;
publisherId: string;
}
interface SpeedOptions {
selected: number;
options: number[];
}
interface KeyboardOptions {
focused?: boolean;
global?: boolean;
}
interface TooltipOptions {
controls?: boolean;
seek?: boolean;
}
interface FullScreenOptions {
enabled?: boolean;
fallback?: boolean;
allowAudio?: boolean;
}
interface CaptionOptions {
active?: boolean;
language?: string;
update?: boolean;
}
interface StorageOptions {
enabled?: boolean;
key?: string;
}
interface SourceInfo {
/**
* Note: YouTube and Vimeo are currently not supported as audio sources.
*/
type: MediaType;
/**
* Title of the new media. Used for the aria-label attribute on the play button, and outer container. YouTube and Vimeo are populated automatically.
*/
title?: string;
/**
* This is an array of sources. For HTML5 media, the properties of this object are mapped directly to HTML attributes so more can be added to the object if required.
*/
sources: Source[];
/**
* The URL for the poster image (HTML5 video only).
*/
poster?: string;
/**
* An array of track objects. Each element in the array is mapped directly to a track element and any keys mapped directly to HTML attributes so as in the example above,
* it will render as <track kind="captions" label="English" srclang="en" src="https://cdn.selz.com/plyr/1.0/example_captions_en.vtt" default> and similar for the French version.
* Booleans are converted to HTML5 value-less attributes.
*/
tracks?: Track[];
}
interface Source {
/**
* The URL of the media file (or YouTube/Vimeo URL).
*/
src: string;
/**
* The MIME type of the media file (if HTML5).
*/
type?: string;
provider?: Provider;
size?: number;
}
type TrackKind = "subtitles" | "captions" | "descriptions" | "chapters" | "metadata";
interface Track {
/**
* Indicates how the text track is meant to be used
*/
kind: TrackKind;
/**
* Indicates a user-readable title for the track
*/
label: string;
/**
* The language of the track text data. It must be a valid BCP 47 language tag. If the kind attribute is set to subtitles, then srclang must be defined.
*/
srcLang?: string;
/**
* The URL of the track (.vtt file).
*/
src: string;
default?: boolean;
}
interface PlyrEvent extends CustomEvent {
readonly detail: { readonly plyr: Plyr; };
}
interface Support {
api: boolean;
ui: boolean;
}
}

View File

@ -368,10 +368,10 @@ class Plyr {
*/ */
pause() { pause() {
if (!this.playing || !is.function(this.media.pause)) { if (!this.playing || !is.function(this.media.pause)) {
return; return null;
} }
this.media.pause(); return this.media.pause();
} }
/** /**
@ -411,10 +411,10 @@ class Plyr {
const toggle = is.boolean(input) ? input : !this.playing; const toggle = is.boolean(input) ? input : !this.playing;
if (toggle) { if (toggle) {
this.play(); return this.play();
} else {
this.pause();
} }
return this.pause();
} }
/** /**
@ -441,7 +441,7 @@ class Plyr {
* @param {Number} seekTime - how far to rewind in seconds. Defaults to the config.seekTime * @param {Number} seekTime - how far to rewind in seconds. Defaults to the config.seekTime
*/ */
rewind(seekTime) { rewind(seekTime) {
this.currentTime = this.currentTime - (is.number(seekTime) ? seekTime : this.config.seekTime); this.currentTime -= is.number(seekTime) ? seekTime : this.config.seekTime;
} }
/** /**
@ -449,7 +449,7 @@ class Plyr {
* @param {Number} seekTime - how far to fast forward in seconds. Defaults to the config.seekTime * @param {Number} seekTime - how far to fast forward in seconds. Defaults to the config.seekTime
*/ */
forward(seekTime) { forward(seekTime) {
this.currentTime = this.currentTime + (is.number(seekTime) ? seekTime : this.config.seekTime); this.currentTime += is.number(seekTime) ? seekTime : this.config.seekTime;
} }
/** /**

View File

@ -198,7 +198,9 @@ const ui = {
// Reset backgroundSize as well (since it can be set to "cover" for padded thumbnails for youtube) // Reset backgroundSize as well (since it can be set to "cover" for padded thumbnails for youtube)
backgroundSize: '', backgroundSize: '',
}); });
ui.togglePoster.call(this, true); ui.togglePoster.call(this, true);
return poster; return poster;
}) })
); );
@ -214,6 +216,7 @@ const ui = {
// Set state // Set state
Array.from(this.elements.buttons.play || []).forEach(target => { Array.from(this.elements.buttons.play || []).forEach(target => {
Object.assign(target, { pressed: this.playing }); Object.assign(target, { pressed: this.playing });
target.setAttribute('aria-label', i18n.get(this.playing ? 'pause' : 'play', this.config));
}); });
// Only update controls on non timeupdate events // Only update controls on non timeupdate events

View File

@ -90,9 +90,7 @@ export function triggerEvent(element, type = '', bubbles = false, detail = {}) {
// Create and dispatch the event // Create and dispatch the event
const event = new CustomEvent(type, { const event = new CustomEvent(type, {
bubbles, bubbles,
detail: Object.assign({}, detail, { detail: { ...detail, plyr: this,},
plyr: this,
}),
}); });
// Dispatch the event // Dispatch the event

View File

@ -56,11 +56,12 @@ export function setAspectRatio(input) {
return {}; return {};
} }
const { wrapper } = this.elements;
const ratio = getAspectRatio.call(this, input); const ratio = getAspectRatio.call(this, input);
const [w, h] = is.array(ratio) ? ratio : [0, 0]; const [w, h] = is.array(ratio) ? ratio : [0, 0];
const padding = (100 / w) * h; const padding = (100 / w) * h;
this.elements.wrapper.style.paddingBottom = `${padding}%`; wrapper.style.paddingBottom = `${padding}%`;
// For Vimeo we have an extra <div> to hide the standard controls and UI // For Vimeo we have an extra <div> to hide the standard controls and UI
if (this.isVimeo && this.supported.ui) { if (this.isVimeo && this.supported.ui) {
@ -68,7 +69,7 @@ export function setAspectRatio(input) {
const offset = (height - padding) / (height / 50); const offset = (height - padding) / (height / 50);
this.media.style.transform = `translateY(-${offset}%)`; this.media.style.transform = `translateY(-${offset}%)`;
} else if (this.isHTML5) { } else if (this.isHTML5) {
this.elements.wrapper.classList.toggle(this.config.classNames.videoFixedRatio, ratio !== null); wrapper.classList.toggle(this.config.classNames.videoFixedRatio, ratio !== null);
} }
return { padding, ratio }; return { padding, ratio };

View File

@ -13,7 +13,7 @@ export const getSeconds = value => Math.trunc(value % 60, 10);
export function formatTime(time = 0, displayHours = false, inverted = false) { export function formatTime(time = 0, displayHours = false, inverted = false) {
// Bail if the value isn't a number // Bail if the value isn't a number
if (!is.number(time)) { if (!is.number(time)) {
return formatTime(null, displayHours, inverted); return formatTime(undefined, displayHours, inverted);
} }
// Format time component to add leading zero // Format time component to add leading zero

View File

@ -5,24 +5,27 @@
// Base // Base
.plyr { .plyr {
@include plyr-font-smoothing($plyr-font-smoothing); @include plyr-font-smoothing($plyr-font-smoothing);
align-items: center;
direction: ltr; direction: ltr;
display: flex;
font-family: $plyr-font-family; font-family: $plyr-font-family;
font-variant-numeric: tabular-nums; // Force monosace-esque number widths font-variant-numeric: tabular-nums; // Force monosace-esque number widths
font-weight: $plyr-font-weight-regular; font-weight: $plyr-font-weight-regular;
height: 100%;
line-height: $plyr-line-height; line-height: $plyr-line-height;
max-width: 100%; max-width: 100%;
min-width: 200px; min-width: 200px;
position: relative; position: relative;
text-shadow: none; text-shadow: none;
transition: box-shadow 0.3s ease; transition: box-shadow 0.3s ease;
z-index: 0; // Force any border radius
// Media elements // Media elements
video, video,
audio { audio,
border-radius: inherit; iframe {
height: auto; display: block;
vertical-align: middle; height: 100%;
width: 100%; width: 100%;
} }

View File

@ -0,0 +1,7 @@
// --------------------------------------------------------------
// Audio styles
// --------------------------------------------------------------
.plyr--audio {
display: block;
}

View File

@ -41,14 +41,6 @@
&.plyr__time + .plyr__time { &.plyr__time + .plyr__time {
padding-left: 0; padding-left: 0;
} }
&.plyr__volume {
padding-right: ($plyr-control-spacing / 2);
}
&.plyr__volume:first-child {
padding-right: 0;
}
} }
// Hide empty controls // Hide empty controls

View File

@ -14,11 +14,10 @@
.plyr__video-wrapper { .plyr__video-wrapper {
background: #000; background: #000;
border-radius: inherit; height: 100%;
margin: auto;
overflow: hidden; overflow: hidden;
position: relative; width: 100%;
// Require z-index to force border-radius
z-index: 0;
} }
// Default to 16:9 ratio but this is set by JavaScript based on config // Default to 16:9 ratio but this is set by JavaScript based on config
@ -33,12 +32,9 @@ $embed-padding: ((100 / 16) * 9);
.plyr__video-embed iframe, .plyr__video-embed iframe,
.plyr__video-wrapper--fixed-ratio video { .plyr__video-wrapper--fixed-ratio video {
border: 0; border: 0;
height: 100%;
left: 0; left: 0;
position: absolute; position: absolute;
top: 0; top: 0;
user-select: none;
width: 100%;
} }
// If the full custom UI is supported // If the full custom UI is supported

View File

@ -5,11 +5,11 @@
.plyr__volume { .plyr__volume {
align-items: center; align-items: center;
display: flex; display: flex;
flex: 1;
position: relative; position: relative;
input[type='range'] { input[type='range'] {
margin-left: ($plyr-control-spacing / 2); margin-left: ($plyr-control-spacing / 2);
margin-right: ($plyr-control-spacing / 2);
position: relative; position: relative;
z-index: 2; z-index: 2;
} }
@ -22,16 +22,3 @@
max-width: 110px; max-width: 110px;
} }
} }
// 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 {
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;
}

View File

@ -25,6 +25,7 @@
@import 'base'; @import 'base';
@import 'components/audio';
@import 'components/badges'; @import 'components/badges';
@import 'components/captions'; @import 'components/captions';
@import 'components/control'; @import 'components/control';

View File

@ -24,8 +24,8 @@
// Fallback for unsupported browsers // Fallback for unsupported browsers
.plyr--fullscreen-fallback { .plyr--fullscreen-fallback {
@include plyr-fullscreen-active(); @include plyr-fullscreen-active();
bottom: 0; bottom: 0;
display: block;
left: 0; left: 0;
position: fixed; position: fixed;
right: 0; right: 0;

4845
yarn.lock

File diff suppressed because it is too large Load Diff