Click to play fix, poster fix, iOS controls fixes

This commit is contained in:
Sam Potts 2018-07-30 01:13:12 +10:00
parent 3a8332bdb3
commit 599b33e55f
26 changed files with 854 additions and 636 deletions

2
demo/dist/demo.css vendored

File diff suppressed because one or more lines are too long

7
demo/dist/demo.js vendored
View File

@ -4147,9 +4147,10 @@ typeof navigator === "object" && (function () {
global: true global: true
}, },
tooltips: { tooltips: {
controls: true controls: false,
seek: false
}, },
clickToPlay: false, // clickToPlay: false,
/* controls: [ /* controls: [
'play-large', 'play-large',
'restart', 'restart',
@ -4207,7 +4208,7 @@ typeof navigator === "object" && (function () {
google: 'AIzaSyDrNwtN3nLH_8rjCmu5Wq3ZCm4MNAVdc0c' google: 'AIzaSyDrNwtN3nLH_8rjCmu5Wq3ZCm4MNAVdc0c'
}, },
ads: { ads: {
enabled: true, // enabled: true,
publisherId: '918848828995742' publisherId: '918848828995742'
} }
}); });

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

View File

@ -69,9 +69,10 @@ import Raven from 'raven-js';
global: true, global: true,
}, },
tooltips: { tooltips: {
controls: true, controls: false,
seek: false,
}, },
clickToPlay: false, // clickToPlay: false,
/* controls: [ /* controls: [
'play-large', 'play-large',
'restart', 'restart',
@ -129,7 +130,7 @@ import Raven from 'raven-js';
google: 'AIzaSyDrNwtN3nLH_8rjCmu5Wq3ZCm4MNAVdc0c', google: 'AIzaSyDrNwtN3nLH_8rjCmu5Wq3ZCm4MNAVdc0c',
}, },
ads: { ads: {
enabled: true, // enabled: true,
publisherId: '918848828995742', publisherId: '918848828995742',
}, },
}); });

2
dist/plyr.css vendored

File diff suppressed because one or more lines are too long

383
dist/plyr.js vendored
View File

@ -671,9 +671,13 @@ typeof navigator === "object" && (function (global, factory) {
// Force repaint of element // Force repaint of element
function repaint(element) { function repaint(element) {
setTimeout(function () { setTimeout(function () {
toggleHidden(element, true); try {
element.offsetHeight; // eslint-disable-line toggleHidden(element, true);
toggleHidden(element, false); element.offsetHeight; // eslint-disable-line
toggleHidden(element, false);
} catch (e) {
// Do nothing
}
}, 0); }, 0);
} }
@ -1697,7 +1701,7 @@ typeof navigator === "object" && (function (global, factory) {
// Show the respective menu // Show the respective menu
if (!isRadioButton && [32, 39].includes(event.which)) { if (!isRadioButton && [32, 39].includes(event.which)) {
controls.showMenuPanel.call(_this, type); controls.showMenuPanel.call(_this, type, true);
} else { } else {
var target = void 0; var target = void 0;
@ -1804,7 +1808,7 @@ typeof navigator === "object" && (function (global, factory) {
break; break;
} }
controls.showMenuPanel.call(_this2, 'home'); controls.showMenuPanel.call(_this2, 'home', event.type === 'keydown');
}, type, false); }, type, false);
controls.bindMenuItemShortcuts.call(this, menuItem, type); controls.bindMenuItemShortcuts.call(this, menuItem, type);
@ -2473,6 +2477,7 @@ typeof navigator === "object" && (function (global, factory) {
var _this8 = this; var _this8 = this;
var type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; var type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var tabFocus = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var target = document.getElementById('plyr-settings-' + this.id + '-' + type); var target = document.getElementById('plyr-settings-' + this.id + '-' + type);
@ -2527,7 +2532,7 @@ typeof navigator === "object" && (function (global, factory) {
// Focus the first item // Focus the first item
var firstItem = target.querySelector('[role^="menuitem"]'); var firstItem = target.querySelector('[role^="menuitem"]');
setFocus.call(this, firstItem, true); setFocus.call(this, firstItem, tabFocus);
}, },
@ -2687,7 +2692,7 @@ typeof navigator === "object" && (function (global, factory) {
// Show menu on click // Show menu on click
on(menuItem, 'click', function () { on(menuItem, 'click', function () {
controls.showMenuPanel.call(_this9, type); controls.showMenuPanel.call(_this9, type, false);
}); });
var flex = createElement('span', null, i18n.get(type, _this9.config)); var flex = createElement('span', null, i18n.get(type, _this9.config));
@ -2737,12 +2742,12 @@ typeof navigator === "object" && (function (global, factory) {
event.stopPropagation(); event.stopPropagation();
// Show the respective menu // Show the respective menu
controls.showMenuPanel.call(_this9, 'home'); controls.showMenuPanel.call(_this9, 'home', true);
}, false); }, false);
// Go back via button click // Go back via button click
on(backButton, 'click', function () { on(backButton, 'click', function () {
controls.showMenuPanel.call(_this9, 'home'); controls.showMenuPanel.call(_this9, 'home', false);
}); });
// Add to pane // Add to pane
@ -4319,7 +4324,7 @@ typeof navigator === "object" && (function (global, factory) {
createClass(Listeners, [{ createClass(Listeners, [{
key: 'handleKey', key: 'handleKey',
value: function handleKey(event) { value: function handleKey(event) {
var _this = this; var player = this.player;
var code = event.keyCode ? event.keyCode : event.which; var code = event.keyCode ? event.keyCode : event.which;
var pressed = event.type === 'keydown'; var pressed = event.type === 'keydown';
@ -4339,7 +4344,7 @@ typeof navigator === "object" && (function (global, factory) {
// Seek by the number keys // Seek by the number keys
var seekByKey = function seekByKey() { var seekByKey = function seekByKey() {
// Divide the max duration into 10th's and times by the number value // Divide the max duration into 10th's and times by the number value
_this.player.currentTime = _this.player.duration / 10 * (code - 48); player.currentTime = player.duration / 10 * (code - 48);
}; };
// Handle the key on keydown // Handle the key on keydown
@ -4350,8 +4355,8 @@ typeof navigator === "object" && (function (global, factory) {
// and any that accept key input http://webaim.org/techniques/keyboard/ // and any that accept key input http://webaim.org/techniques/keyboard/
var focused = document.activeElement; var focused = document.activeElement;
if (is.element(focused)) { if (is.element(focused)) {
var editable = this.player.config.selectors.editable; var editable = player.config.selectors.editable;
var seek = this.player.elements.inputs.seek; var seek = player.elements.inputs.seek;
if (focused !== seek && matches(focused, editable)) { if (focused !== seek && matches(focused, editable)) {
@ -4393,52 +4398,52 @@ typeof navigator === "object" && (function (global, factory) {
case 75: case 75:
// Space and K key // Space and K key
if (!repeat) { if (!repeat) {
this.player.togglePlay(); player.togglePlay();
} }
break; break;
case 38: case 38:
// Arrow up // Arrow up
this.player.increaseVolume(0.1); player.increaseVolume(0.1);
break; break;
case 40: case 40:
// Arrow down // Arrow down
this.player.decreaseVolume(0.1); player.decreaseVolume(0.1);
break; break;
case 77: case 77:
// M key // M key
if (!repeat) { if (!repeat) {
this.player.muted = !this.player.muted; player.muted = !player.muted;
} }
break; break;
case 39: case 39:
// Arrow forward // Arrow forward
this.player.forward(); player.forward();
break; break;
case 37: case 37:
// Arrow back // Arrow back
this.player.rewind(); player.rewind();
break; break;
case 70: case 70:
// F key // F key
this.player.fullscreen.toggle(); player.fullscreen.toggle();
break; break;
case 67: case 67:
// C key // C key
if (!repeat) { if (!repeat) {
this.player.toggleCaptions(); player.toggleCaptions();
} }
break; break;
case 76: case 76:
// L key // L key
this.player.loop = !this.player.loop; player.loop = !player.loop;
break; break;
/* case 73: /* case 73:
@ -4457,8 +4462,8 @@ typeof navigator === "object" && (function (global, factory) {
// 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
if (!this.player.fullscreen.enabled && this.player.fullscreen.active && code === 27) { if (!player.fullscreen.enabled && player.fullscreen.active && code === 27) {
this.player.fullscreen.toggle(); player.fullscreen.toggle();
} }
// Store last code for next cycle // Store last code for next cycle
@ -4481,15 +4486,19 @@ typeof navigator === "object" && (function (global, factory) {
}, { }, {
key: 'firstTouch', key: 'firstTouch',
value: function firstTouch() { value: function firstTouch() {
this.player.touch = true; var player = this.player;
player.touch = true;
// Add touch class // Add touch class
toggleClass(this.player.elements.container, this.player.config.classNames.isTouch, true); toggleClass(player.elements.container, player.config.classNames.isTouch, true);
} }
}, { }, {
key: 'setTabFocus', key: 'setTabFocus',
value: function setTabFocus(event) { value: function setTabFocus(event) {
var _this2 = this; var player = this.player;
clearTimeout(this.focusTimer); clearTimeout(this.focusTimer);
@ -4505,8 +4514,8 @@ typeof navigator === "object" && (function (global, factory) {
// Remove current classes // Remove current classes
var removeCurrent = function removeCurrent() { var removeCurrent = function removeCurrent() {
var className = _this2.player.config.classNames.tabFocus; var className = player.config.classNames.tabFocus;
var current = getElements.call(_this2.player, '.' + className); var current = getElements.call(player, '.' + className);
toggleClass(current, className, false); toggleClass(current, className, false);
}; };
@ -4523,16 +4532,15 @@ typeof navigator === "object" && (function (global, factory) {
// Delay the adding of classname until the focus has changed // Delay the adding of classname until the focus has changed
// This event fires before the focusin event // This event fires before the focusin event
this.focusTimer = setTimeout(function () { this.focusTimer = setTimeout(function () {
var focused = document.activeElement; var focused = document.activeElement;
// Ignore if current focus element isn't inside the player // Ignore if current focus element isn't inside the player
if (!_this2.player.elements.container.contains(focused)) { if (!player.elements.container.contains(focused)) {
return; return;
} }
toggleClass(document.activeElement, _this2.player.config.classNames.tabFocus, true); toggleClass(document.activeElement, player.config.classNames.tabFocus, true);
}, 10); }, 10);
} }
@ -4542,20 +4550,22 @@ typeof navigator === "object" && (function (global, factory) {
key: 'global', key: 'global',
value: function global() { value: function global() {
var toggle = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; var toggle = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
var player = this.player;
// Keyboard shortcuts // Keyboard shortcuts
if (this.player.config.keyboard.global) {
toggleListener.call(this.player, window, 'keydown keyup', this.handleKey, toggle, false); if (player.config.keyboard.global) {
toggleListener.call(player, window, 'keydown keyup', this.handleKey, toggle, false);
} }
// Click anywhere closes menu // Click anywhere closes menu
toggleListener.call(this.player, document.body, 'click', this.toggleMenu, toggle); toggleListener.call(player, document.body, 'click', this.toggleMenu, toggle);
// Detect touch by events // Detect touch by events
once.call(this.player, document.body, 'touchstart', this.firstTouch); once.call(player, document.body, 'touchstart', this.firstTouch);
// Tab focus detection // Tab focus detection
toggleListener.call(this.player, document.body, 'keydown focus blur', this.setTabFocus, toggle, false, true); toggleListener.call(player, document.body, 'keydown focus blur', this.setTabFocus, toggle, false, true);
} }
// Container listeners // Container listeners
@ -4563,16 +4573,17 @@ typeof navigator === "object" && (function (global, factory) {
}, { }, {
key: 'container', key: 'container',
value: function container() { value: function container() {
var _this3 = this; var player = this.player;
// Keyboard shortcuts // Keyboard shortcuts
if (!this.player.config.keyboard.global && this.player.config.keyboard.focused) {
on.call(this.player, this.player.elements.container, 'keydown keyup', this.handleKey, false); if (!player.config.keyboard.global && player.config.keyboard.focused) {
on.call(player, player.elements.container, 'keydown keyup', this.handleKey, false);
} }
// Toggle controls on mouse events and entering fullscreen // Toggle controls on mouse events and entering fullscreen
on.call(this.player, this.player.elements.container, 'mousemove mouseleave touchstart touchmove enterfullscreen exitfullscreen', function (event) { on.call(player, player.elements.container, 'mousemove mouseleave touchstart touchmove enterfullscreen exitfullscreen', function (event) {
var controls$$1 = _this3.player.elements.controls; var controls$$1 = player.elements.controls;
// Remove button states for fullscreen // Remove button states for fullscreen
@ -4587,17 +4598,17 @@ typeof navigator === "object" && (function (global, factory) {
var delay = 0; var delay = 0;
if (show) { if (show) {
ui.toggleControls.call(_this3.player, true); ui.toggleControls.call(player, true);
// Use longer timeout for touch devices // Use longer timeout for touch devices
delay = _this3.player.touch ? 3000 : 2000; delay = player.touch ? 3000 : 2000;
} }
// Clear timer // Clear timer
clearTimeout(_this3.player.timers.controls); clearTimeout(player.timers.controls);
// Set new timer to prevent flicker when seeking // Set new timer to prevent flicker when seeking
_this3.player.timers.controls = setTimeout(function () { player.timers.controls = setTimeout(function () {
return ui.toggleControls.call(_this3.player, false); return ui.toggleControls.call(player, false);
}, delay); }, delay);
}); });
} }
@ -4607,76 +4618,77 @@ typeof navigator === "object" && (function (global, factory) {
}, { }, {
key: 'media', key: 'media',
value: function media() { value: function media() {
var _this4 = this; var player = this.player;
// Time change on media // Time change on media
on.call(this.player, this.player.media, 'timeupdate seeking seeked', function (event) {
return controls.timeUpdate.call(_this4.player, event); on.call(player, player.media, 'timeupdate seeking seeked', function (event) {
return controls.timeUpdate.call(player, event);
}); });
// Display duration // Display duration
on.call(this.player, this.player.media, 'durationchange loadeddata loadedmetadata', function (event) { on.call(player, player.media, 'durationchange loadeddata loadedmetadata', function (event) {
return controls.durationUpdate.call(_this4.player, event); return controls.durationUpdate.call(player, event);
}); });
// Check for audio tracks on load // Check for audio tracks on load
// We can't use `loadedmetadata` as it doesn't seem to have audio tracks at that point // We can't use `loadedmetadata` as it doesn't seem to have audio tracks at that point
on.call(this.player, this.player.media, 'canplay', function () { on.call(player, player.media, 'canplay', function () {
toggleHidden(_this4.player.elements.volume, !_this4.player.hasAudio); toggleHidden(player.elements.volume, !player.hasAudio);
toggleHidden(_this4.player.elements.buttons.mute, !_this4.player.hasAudio); toggleHidden(player.elements.buttons.mute, !player.hasAudio);
}); });
// Handle the media finishing // Handle the media finishing
on.call(this.player, this.player.media, 'ended', function () { on.call(player, player.media, 'ended', function () {
// Show poster on end // Show poster on end
if (_this4.player.isHTML5 && _this4.player.isVideo && _this4.player.config.resetOnEnd) { if (player.isHTML5 && player.isVideo && player.config.resetOnEnd) {
// Restart // Restart
_this4.player.restart(); player.restart();
} }
}); });
// Check for buffer progress // Check for buffer progress
on.call(this.player, this.player.media, 'progress playing seeking seeked', function (event) { on.call(player, player.media, 'progress playing seeking seeked', function (event) {
return controls.updateProgress.call(_this4.player, event); return controls.updateProgress.call(player, event);
}); });
// Handle volume changes // Handle volume changes
on.call(this.player, this.player.media, 'volumechange', function (event) { on.call(player, player.media, 'volumechange', function (event) {
return controls.updateVolume.call(_this4.player, event); return controls.updateVolume.call(player, event);
}); });
// Handle play/pause // Handle play/pause
on.call(this.player, this.player.media, 'playing play pause ended emptied timeupdate', function (event) { on.call(player, player.media, 'playing play pause ended emptied timeupdate', function (event) {
return ui.checkPlaying.call(_this4.player, event); return ui.checkPlaying.call(player, event);
}); });
// Loading state // Loading state
on.call(this.player, this.player.media, 'waiting canplay seeked playing', function (event) { on.call(player, player.media, 'waiting canplay seeked playing', function (event) {
return ui.checkLoading.call(_this4.player, event); return ui.checkLoading.call(player, event);
}); });
// If autoplay, then load advertisement if required // If autoplay, then load advertisement if required
// TODO: Show some sort of loading state while the ad manager loads else there's a delay before ad shows // TODO: Show some sort of loading state while the ad manager loads else there's a delay before ad shows
on.call(this.player, this.player.media, 'playing', function () { on.call(player, player.media, 'playing', function () {
if (!_this4.player.ads) { if (!player.ads) {
return; return;
} }
// If ads are enabled, wait for them first // If ads are enabled, wait for them first
if (_this4.player.ads.enabled && !_this4.player.ads.initialized) { if (player.ads.enabled && !player.ads.initialized) {
// Wait for manager response // Wait for manager response
_this4.player.ads.managerPromise.then(function () { player.ads.managerPromise.then(function () {
return _this4.player.ads.play(); return player.ads.play();
}).catch(function () { }).catch(function () {
return _this4.player.play(); return player.play();
}); });
} }
}); });
// Click video // Click video
if (this.player.supported.ui && this.player.config.clickToPlay && !this.player.isAudio) { if (player.supported.ui && player.config.clickToPlay && !player.isAudio) {
// Re-fetch the wrapper // Re-fetch the wrapper
var wrapper = getElement.call(this.player, '.' + this.player.config.classNames.video); var wrapper = getElement.call(player, '.' + player.config.classNames.video);
// Bail if there's no wrapper (this should never happen) // Bail if there's no wrapper (this should never happen)
if (!is.element(wrapper)) { if (!is.element(wrapper)) {
@ -4684,75 +4696,81 @@ typeof navigator === "object" && (function (global, factory) {
} }
// On click play, pause ore restart // On click play, pause ore restart
on.call(this.player, wrapper, 'click', function () { on.call(player, player.elements.container, 'click touchstart', function (event) {
// Touch devices will just show controls (if we're hiding controls) var targets = [player.elements.container, wrapper];
if (_this4.player.config.hideControls && _this4.player.touch && !_this4.player.paused) {
// Ignore if click if not container or in video wrapper
if (!targets.includes(event.target) && !wrapper.contains(event.target)) {
return; return;
} }
if (_this4.player.paused) { // First touch on touch devices will just show controls (if we're hiding controls)
_this4.player.play(); // If controls are shown then it'll toggle like a pointer device
} else if (_this4.player.ended) { if (player.config.hideControls && player.touch && hasClass(player.elements.container, player.config.classNames.hideControls)) {
_this4.player.restart(); return;
_this4.player.play(); }
if (player.ended) {
player.restart();
player.play();
} else { } else {
_this4.player.pause(); player.togglePlay();
} }
}); });
} }
// Disable right click // Disable right click
if (this.player.supported.ui && this.player.config.disableContextMenu) { if (player.supported.ui && player.config.disableContextMenu) {
on.call(this.player, this.player.elements.wrapper, 'contextmenu', function (event) { on.call(player, player.elements.wrapper, 'contextmenu', function (event) {
event.preventDefault(); event.preventDefault();
}, false); }, false);
} }
// Volume change // Volume change
on.call(this.player, this.player.media, 'volumechange', function () { on.call(player, player.media, 'volumechange', function () {
// Save to storage // Save to storage
_this4.player.storage.set({ player.storage.set({
volume: _this4.player.volume, volume: player.volume,
muted: _this4.player.muted muted: player.muted
}); });
}); });
// Speed change // Speed change
on.call(this.player, this.player.media, 'ratechange', function () { on.call(player, player.media, 'ratechange', function () {
// Update UI // Update UI
controls.updateSetting.call(_this4.player, 'speed'); controls.updateSetting.call(player, 'speed');
// Save to storage // Save to storage
_this4.player.storage.set({ speed: _this4.player.speed }); player.storage.set({ speed: player.speed });
}); });
// Quality request // Quality request
on.call(this.player, this.player.media, 'qualityrequested', function (event) { on.call(player, player.media, 'qualityrequested', function (event) {
// Save to storage // Save to storage
_this4.player.storage.set({ quality: event.detail.quality }); player.storage.set({ quality: event.detail.quality });
}); });
// Quality change // Quality change
on.call(this.player, this.player.media, 'qualitychange', function (event) { on.call(player, player.media, 'qualitychange', function (event) {
// Update UI // Update UI
controls.updateSetting.call(_this4.player, 'quality', null, event.detail.quality); controls.updateSetting.call(player, 'quality', null, event.detail.quality);
}); });
// Proxy events to container // Proxy events to container
// Bubble up key events for Edge // Bubble up key events for Edge
var proxyEvents = this.player.config.events.concat(['keyup', 'keydown']).join(' '); var proxyEvents = player.config.events.concat(['keyup', 'keydown']).join(' ');
on.call(this.player, this.player.media, proxyEvents, function (event) { on.call(player, player.media, proxyEvents, function (event) {
var _event$detail = event.detail, var _event$detail = event.detail,
detail = _event$detail === undefined ? {} : _event$detail; detail = _event$detail === undefined ? {} : _event$detail;
// Get error details from media // Get error details from media
if (event.type === 'error') { if (event.type === 'error') {
detail = _this4.player.media.error; detail = player.media.error;
} }
triggerEvent.call(_this4.player, _this4.player.elements.container, event.type, true, detail); triggerEvent.call(player, player.elements.container, event.type, true, detail);
}); });
} }
@ -4761,18 +4779,20 @@ typeof navigator === "object" && (function (global, factory) {
}, { }, {
key: 'proxy', key: 'proxy',
value: function proxy(event, defaultHandler, customHandlerKey) { value: function proxy(event, defaultHandler, customHandlerKey) {
var customHandler = this.player.config.listeners[customHandlerKey]; var player = this.player;
var customHandler = player.config.listeners[customHandlerKey];
var hasCustomHandler = is.function(customHandler); var hasCustomHandler = is.function(customHandler);
var returned = true; var returned = true;
// Execute custom handler // Execute custom handler
if (hasCustomHandler) { if (hasCustomHandler) {
returned = customHandler.call(this.player, event); returned = customHandler.call(player, event);
} }
// Only call default handler if not prevented in custom handler // Only call default handler if not prevented in custom handler
if (returned && is.function(defaultHandler)) { if (returned && is.function(defaultHandler)) {
defaultHandler.call(this.player, event); defaultHandler.call(player, event);
} }
} }
@ -4781,15 +4801,16 @@ typeof navigator === "object" && (function (global, factory) {
}, { }, {
key: 'bind', key: 'bind',
value: function bind(element, type, defaultHandler, customHandlerKey) { value: function bind(element, type, defaultHandler, customHandlerKey) {
var _this5 = this; var _this = this;
var passive = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true; var passive = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
var player = this.player;
var customHandler = this.player.config.listeners[customHandlerKey]; var customHandler = player.config.listeners[customHandlerKey];
var hasCustomHandler = is.function(customHandler); var hasCustomHandler = is.function(customHandler);
on.call(this.player, element, type, function (event) { on.call(player, element, type, function (event) {
return _this5.proxy(event, defaultHandler, customHandlerKey); return _this.proxy(event, defaultHandler, customHandlerKey);
}, passive && !hasCustomHandler); }, passive && !hasCustomHandler);
} }
@ -4798,57 +4819,60 @@ typeof navigator === "object" && (function (global, factory) {
}, { }, {
key: 'controls', key: 'controls',
value: function controls$$1() { value: function controls$$1() {
var _this6 = this; var _this2 = this;
var player = this.player;
// IE doesn't support input event, so we fallback to change // IE doesn't support input event, so we fallback to change
var inputEvent = browser.isIE ? 'change' : 'input'; var inputEvent = browser.isIE ? 'change' : 'input';
// Play/pause toggle // Play/pause toggle
if (this.player.elements.buttons.play) { if (player.elements.buttons.play) {
Array.from(this.player.elements.buttons.play).forEach(function (button) { Array.from(player.elements.buttons.play).forEach(function (button) {
_this6.bind(button, 'click', _this6.player.togglePlay, 'play'); _this2.bind(button, 'click', player.togglePlay, 'play');
}); });
} }
// Pause // Pause
this.bind(this.player.elements.buttons.restart, 'click', this.player.restart, 'restart'); this.bind(player.elements.buttons.restart, 'click', player.restart, 'restart');
// Rewind // Rewind
this.bind(this.player.elements.buttons.rewind, 'click', this.player.rewind, 'rewind'); this.bind(player.elements.buttons.rewind, 'click', player.rewind, 'rewind');
// Rewind // Rewind
this.bind(this.player.elements.buttons.fastForward, 'click', this.player.forward, 'fastForward'); this.bind(player.elements.buttons.fastForward, 'click', player.forward, 'fastForward');
// Mute toggle // Mute toggle
this.bind(this.player.elements.buttons.mute, 'click', function () { this.bind(player.elements.buttons.mute, 'click', function () {
_this6.player.muted = !_this6.player.muted; player.muted = !player.muted;
}, 'mute'); }, 'mute');
// Captions toggle // Captions toggle
this.bind(this.player.elements.buttons.captions, 'click', function () { this.bind(player.elements.buttons.captions, 'click', function () {
return _this6.player.toggleCaptions(); return player.toggleCaptions();
}); });
// Fullscreen toggle // Fullscreen toggle
this.bind(this.player.elements.buttons.fullscreen, 'click', function () { this.bind(player.elements.buttons.fullscreen, 'click', function () {
_this6.player.fullscreen.toggle(); player.fullscreen.toggle();
}, 'fullscreen'); }, 'fullscreen');
// Picture-in-Picture // Picture-in-Picture
this.bind(this.player.elements.buttons.pip, 'click', function () { this.bind(player.elements.buttons.pip, 'click', function () {
_this6.player.pip = 'toggle'; player.pip = 'toggle';
}, 'pip'); }, 'pip');
// Airplay // Airplay
this.bind(this.player.elements.buttons.airplay, 'click', this.player.airplay, 'airplay'); this.bind(player.elements.buttons.airplay, 'click', player.airplay, 'airplay');
// Settings menu - click toggle // Settings menu - click toggle
this.bind(this.player.elements.buttons.settings, 'click', function (event) { this.bind(player.elements.buttons.settings, 'click', function (event) {
controls.toggleMenu.call(_this6.player, event); controls.toggleMenu.call(player, event);
}); });
// Settings menu - keyboard toggle // Settings menu - keyboard toggle
this.bind(this.player.elements.buttons.settings, 'keydown', function (event) { this.bind(player.elements.buttons.settings, 'keydown', function (event) {
// We only care about space // We only care about space
if (event.which !== 32) { if (event.which !== 32) {
return; return;
@ -4861,44 +4885,44 @@ typeof navigator === "object" && (function (global, factory) {
event.stopPropagation(); event.stopPropagation();
// Toggle menu // Toggle menu
controls.toggleMenu.call(_this6.player, event); controls.toggleMenu.call(player, event);
}, null, false); }, null, false);
// Set range input alternative "value", which matches the tooltip time (#954) // Set range input alternative "value", which matches the tooltip time (#954)
this.bind(this.player.elements.inputs.seek, 'mousedown mousemove', function (event) { this.bind(player.elements.inputs.seek, 'mousedown mousemove', function (event) {
var clientRect = _this6.player.elements.progress.getBoundingClientRect(); var rect = player.elements.progress.getBoundingClientRect();
var percent = 100 / clientRect.width * (event.pageX - clientRect.left); var percent = 100 / rect.width * (event.pageX - rect.left);
event.currentTarget.setAttribute('seek-value', percent); event.currentTarget.setAttribute('seek-value', percent);
}); });
// Pause while seeking // Pause while seeking
this.bind(this.player.elements.inputs.seek, 'mousedown mouseup keydown keyup touchstart touchend', function (event) { this.bind(player.elements.inputs.seek, 'mousedown mouseup keydown keyup touchstart touchend', function (event) {
var seek = event.currentTarget; var seek = event.currentTarget;
var code = event.keyCode ? event.keyCode : event.which; var code = event.keyCode ? event.keyCode : event.which;
var eventType = event.type; var eventType = event.type;
var attribute = 'play-on-seeked';
if ((eventType === 'keydown' || eventType === 'keyup') && code !== 39 && code !== 37) { if ((eventType === 'keydown' || eventType === 'keyup') && code !== 39 && code !== 37) {
return; return;
} }
// Was playing before? // Was playing before?
var play = seek.hasAttribute('play-on-seeked'); var play = seek.hasAttribute(attribute);
// Done seeking // Done seeking
var done = ['mouseup', 'touchend', 'keyup'].includes(event.type); var done = ['mouseup', 'touchend', 'keyup'].includes(event.type);
// If we're done seeking and it was playing, resume playback // If we're done seeking and it was playing, resume playback
if (play && done) { if (play && done) {
seek.removeAttribute('play-on-seeked'); seek.removeAttribute(attribute);
_this6.player.play(); player.play();
} else if (!done && _this6.player.playing) { } else if (!done && player.playing) {
seek.setAttribute('play-on-seeked', ''); seek.setAttribute(attribute, '');
_this6.player.pause(); player.pause();
} }
}); });
// Seek // Seek
this.bind(this.player.elements.inputs.seek, inputEvent, function (event) { this.bind(player.elements.inputs.seek, inputEvent, function (event) {
var seek = event.currentTarget; var seek = event.currentTarget;
// If it exists, use seek-value instead of "value" for consistency with tooltip time (#954) // If it exists, use seek-value instead of "value" for consistency with tooltip time (#954)
@ -4910,89 +4934,95 @@ typeof navigator === "object" && (function (global, factory) {
seek.removeAttribute('seek-value'); seek.removeAttribute('seek-value');
_this6.player.currentTime = seekTo / seek.max * _this6.player.duration; // Super weird iOS bug where after you interact with an <input type="range">,
// it takes over further interactions on the page. This is a hack
if (browser.isIos) {
repaint(seek);
}
player.currentTime = seekTo / seek.max * player.duration;
}, 'seek'); }, 'seek');
// Current time invert // Current time invert
// Only if one time element is used for both currentTime and duration // Only if one time element is used for both currentTime and duration
if (this.player.config.toggleInvert && !is.element(this.player.elements.display.duration)) { if (player.config.toggleInvert && !is.element(player.elements.display.duration)) {
this.bind(this.player.elements.display.currentTime, 'click', function () { this.bind(player.elements.display.currentTime, 'click', function () {
// Do nothing if we're at the start // Do nothing if we're at the start
if (_this6.player.currentTime === 0) { if (player.currentTime === 0) {
return; return;
} }
_this6.player.config.invertTime = !_this6.player.config.invertTime; player.config.invertTime = !player.config.invertTime;
controls.timeUpdate.call(_this6.player); controls.timeUpdate.call(player);
}); });
} }
// Volume // Volume
this.bind(this.player.elements.inputs.volume, inputEvent, function (event) { this.bind(player.elements.inputs.volume, inputEvent, function (event) {
_this6.player.volume = event.target.value; player.volume = event.target.value;
}, 'volume'); }, 'volume');
// Polyfill for lower fill in <input type="range"> for webkit // Polyfill for lower fill in <input type="range"> for webkit
if (browser.isWebkit) { if (browser.isWebkit) {
Array.from(getElements.call(this.player, 'input[type="range"]')).forEach(function (element) { Array.from(getElements.call(player, 'input[type="range"]')).forEach(function (element) {
_this6.bind(element, 'input', function (event) { _this2.bind(element, 'input', function (event) {
return controls.updateRangeFill.call(_this6.player, event.target); return controls.updateRangeFill.call(player, event.target);
}); });
}); });
} }
// Seek tooltip // Seek tooltip
this.bind(this.player.elements.progress, 'mouseenter mouseleave mousemove', function (event) { this.bind(player.elements.progress, 'mouseenter mouseleave mousemove', function (event) {
return controls.updateSeekTooltip.call(_this6.player, event); return controls.updateSeekTooltip.call(player, event);
}); });
// Update controls.hover state (used for ui.toggleControls to avoid hiding when interacting) // Update controls.hover state (used for ui.toggleControls to avoid hiding when interacting)
this.bind(this.player.elements.controls, 'mouseenter mouseleave', function (event) { this.bind(player.elements.controls, 'mouseenter mouseleave', function (event) {
_this6.player.elements.controls.hover = !_this6.player.touch && event.type === 'mouseenter'; player.elements.controls.hover = !player.touch && event.type === 'mouseenter';
}); });
// Update controls.pressed state (used for ui.toggleControls to avoid hiding when interacting) // Update controls.pressed state (used for ui.toggleControls to avoid hiding when interacting)
this.bind(this.player.elements.controls, 'mousedown mouseup touchstart touchend touchcancel', function (event) { this.bind(player.elements.controls, 'mousedown mouseup touchstart touchend touchcancel', function (event) {
_this6.player.elements.controls.pressed = ['mousedown', 'touchstart'].includes(event.type); player.elements.controls.pressed = ['mousedown', 'touchstart'].includes(event.type);
}); });
// Focus in/out on controls // Focus in/out on controls
this.bind(this.player.elements.controls, 'focusin focusout', function (event) { this.bind(player.elements.controls, 'focusin focusout', function (event) {
var _player = _this6.player, var config = player.config,
config = _player.config, elements = player.elements,
elements = _player.elements, timers = player.timers;
timers = _player.timers;
var isFocusIn = event.type === 'focusin';
// Skip transition to prevent focus from scrolling the parent element // Skip transition to prevent focus from scrolling the parent element
toggleClass(elements.controls, config.classNames.noTransition, isFocusIn);
toggleClass(elements.controls, config.classNames.noTransition, event.type === 'focusin');
// Toggle // Toggle
ui.toggleControls.call(_this6.player, event.type === 'focusin'); ui.toggleControls.call(player, isFocusIn);
// If focusin, hide again after delay // If focusin, hide again after delay
if (event.type === 'focusin') { if (isFocusIn) {
// Restore transition // Restore transition
setTimeout(function () { setTimeout(function () {
toggleClass(elements.controls, config.classNames.noTransition, false); toggleClass(elements.controls, config.classNames.noTransition, false);
}, 0); }, 0);
// Delay a little more for keyboard users // Delay a little more for keyboard users
var delay = _this6.touch ? 3000 : 4000; var delay = _this2.touch ? 3000 : 4000;
// Clear timer // Clear timer
clearTimeout(timers.controls); clearTimeout(timers.controls);
// Hide // Hide
timers.controls = setTimeout(function () { timers.controls = setTimeout(function () {
return ui.toggleControls.call(_this6.player, false); return ui.toggleControls.call(player, false);
}, delay); }, delay);
} }
}); });
// Mouse wheel for volume // Mouse wheel for volume
this.bind(this.player.elements.inputs.volume, 'wheel', function (event) { this.bind(player.elements.inputs.volume, 'wheel', function (event) {
// Detect "natural" scroll - suppored on OS X Safari only // Detect "natural" scroll - suppored on OS X Safari only
// Other browsers on OS X will be inverted until support improves // Other browsers on OS X will be inverted until support improves
var inverted = event.webkitDirectionInvertedFromDevice; var inverted = event.webkitDirectionInvertedFromDevice;
@ -5012,10 +5042,10 @@ typeof navigator === "object" && (function (global, factory) {
var direction = Math.sign(Math.abs(x) > Math.abs(y) ? x : y); var direction = Math.sign(Math.abs(x) > Math.abs(y) ? x : y);
// Change the volume by 2% // Change the volume by 2%
_this6.player.increaseVolume(direction / 50); player.increaseVolume(direction / 50);
// Don't break page scrolling at max and min // Don't break page scrolling at max and min
var volume = _this6.player.media.volume; var volume = player.media.volume;
if (direction === 1 && volume < 1 || direction === -1 && volume > 0) { if (direction === 1 && volume < 1 || direction === -1 && volume > 0) {
event.preventDefault(); event.preventDefault();
@ -7310,7 +7340,7 @@ typeof navigator === "object" && (function (global, factory) {
if (this.media.hasAttribute('autoplay')) { if (this.media.hasAttribute('autoplay')) {
this.config.autoplay = true; this.config.autoplay = true;
} }
if (this.media.hasAttribute('playsinline')) { if (this.media.hasAttribute('playsinline') || this.media.hasAttribute('webkit-playsinline')) {
this.config.playsinline = true; this.config.playsinline = true;
} }
if (this.media.hasAttribute('muted')) { if (this.media.hasAttribute('muted')) {
@ -7382,7 +7412,9 @@ typeof navigator === "object" && (function (global, factory) {
this.fullscreen = new Fullscreen(this); this.fullscreen = new Fullscreen(this);
// Setup ads if provided // Setup ads if provided
this.ads = new Ads(this); if (this.config.ads.enabled) {
this.ads = new Ads(this);
}
// Autoplay if required // Autoplay if required
if (this.config.autoplay) { if (this.config.autoplay) {
@ -7588,13 +7620,16 @@ typeof navigator === "object" && (function (global, factory) {
if (hiding && this.config.controls.includes('settings') && !is.empty(this.config.settings)) { if (hiding && this.config.controls.includes('settings') && !is.empty(this.config.settings)) {
controls.toggleMenu.call(this, false); controls.toggleMenu.call(this, false);
} }
// Trigger event on change // Trigger event on change
if (hiding !== isHidden) { if (hiding !== isHidden) {
var eventName = hiding ? 'controlshidden' : 'controlsshown'; var eventName = hiding ? 'controlshidden' : 'controlsshown';
triggerEvent.call(this, this.media, eventName); triggerEvent.call(this, this.media, eventName);
} }
return !hiding; return !hiding;
} }
return false; return false;
} }
@ -8084,7 +8119,9 @@ typeof navigator === "object" && (function (global, factory) {
} }
// Trigger request event // Trigger request event
triggerEvent.call(this, this.media, 'qualityrequested', false, { quality: quality }); triggerEvent.call(this, this.media, 'qualityrequested', false, {
quality: quality
});
// Update config // Update config
config.selected = quality; config.selected = quality;

2
dist/plyr.js.map vendored

File diff suppressed because one or more lines are too long

2
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

View File

@ -6057,9 +6057,13 @@ typeof navigator === "object" && (function (global, factory) {
// Force repaint of element // Force repaint of element
function repaint(element) { function repaint(element) {
setTimeout(function () { setTimeout(function () {
toggleHidden(element, true); try {
element.offsetHeight; // eslint-disable-line toggleHidden(element, true);
toggleHidden(element, false); element.offsetHeight; // eslint-disable-line
toggleHidden(element, false);
} catch (e) {
// Do nothing
}
}, 0); }, 0);
} }
@ -7083,7 +7087,7 @@ typeof navigator === "object" && (function (global, factory) {
// Show the respective menu // Show the respective menu
if (!isRadioButton && [32, 39].includes(event.which)) { if (!isRadioButton && [32, 39].includes(event.which)) {
controls.showMenuPanel.call(_this, type); controls.showMenuPanel.call(_this, type, true);
} else { } else {
var target = void 0; var target = void 0;
@ -7190,7 +7194,7 @@ typeof navigator === "object" && (function (global, factory) {
break; break;
} }
controls.showMenuPanel.call(_this2, 'home'); controls.showMenuPanel.call(_this2, 'home', event.type === 'keydown');
}, type, false); }, type, false);
controls.bindMenuItemShortcuts.call(this, menuItem, type); controls.bindMenuItemShortcuts.call(this, menuItem, type);
@ -7859,6 +7863,7 @@ typeof navigator === "object" && (function (global, factory) {
var _this8 = this; var _this8 = this;
var type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; var type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var tabFocus = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var target = document.getElementById('plyr-settings-' + this.id + '-' + type); var target = document.getElementById('plyr-settings-' + this.id + '-' + type);
@ -7913,7 +7918,7 @@ typeof navigator === "object" && (function (global, factory) {
// Focus the first item // Focus the first item
var firstItem = target.querySelector('[role^="menuitem"]'); var firstItem = target.querySelector('[role^="menuitem"]');
setFocus.call(this, firstItem, true); setFocus.call(this, firstItem, tabFocus);
}, },
@ -8073,7 +8078,7 @@ typeof navigator === "object" && (function (global, factory) {
// Show menu on click // Show menu on click
on(menuItem, 'click', function () { on(menuItem, 'click', function () {
controls.showMenuPanel.call(_this9, type); controls.showMenuPanel.call(_this9, type, false);
}); });
var flex = createElement('span', null, i18n.get(type, _this9.config)); var flex = createElement('span', null, i18n.get(type, _this9.config));
@ -8123,12 +8128,12 @@ typeof navigator === "object" && (function (global, factory) {
event.stopPropagation(); event.stopPropagation();
// Show the respective menu // Show the respective menu
controls.showMenuPanel.call(_this9, 'home'); controls.showMenuPanel.call(_this9, 'home', true);
}, false); }, false);
// Go back via button click // Go back via button click
on(backButton, 'click', function () { on(backButton, 'click', function () {
controls.showMenuPanel.call(_this9, 'home'); controls.showMenuPanel.call(_this9, 'home', false);
}); });
// Add to pane // Add to pane
@ -9705,7 +9710,7 @@ typeof navigator === "object" && (function (global, factory) {
createClass(Listeners, [{ createClass(Listeners, [{
key: 'handleKey', key: 'handleKey',
value: function handleKey(event) { value: function handleKey(event) {
var _this = this; var player = this.player;
var code = event.keyCode ? event.keyCode : event.which; var code = event.keyCode ? event.keyCode : event.which;
var pressed = event.type === 'keydown'; var pressed = event.type === 'keydown';
@ -9725,7 +9730,7 @@ typeof navigator === "object" && (function (global, factory) {
// Seek by the number keys // Seek by the number keys
var seekByKey = function seekByKey() { var seekByKey = function seekByKey() {
// Divide the max duration into 10th's and times by the number value // Divide the max duration into 10th's and times by the number value
_this.player.currentTime = _this.player.duration / 10 * (code - 48); player.currentTime = player.duration / 10 * (code - 48);
}; };
// Handle the key on keydown // Handle the key on keydown
@ -9736,8 +9741,8 @@ typeof navigator === "object" && (function (global, factory) {
// and any that accept key input http://webaim.org/techniques/keyboard/ // and any that accept key input http://webaim.org/techniques/keyboard/
var focused = document.activeElement; var focused = document.activeElement;
if (is$1.element(focused)) { if (is$1.element(focused)) {
var editable = this.player.config.selectors.editable; var editable = player.config.selectors.editable;
var seek = this.player.elements.inputs.seek; var seek = player.elements.inputs.seek;
if (focused !== seek && matches(focused, editable)) { if (focused !== seek && matches(focused, editable)) {
@ -9779,52 +9784,52 @@ typeof navigator === "object" && (function (global, factory) {
case 75: case 75:
// Space and K key // Space and K key
if (!repeat) { if (!repeat) {
this.player.togglePlay(); player.togglePlay();
} }
break; break;
case 38: case 38:
// Arrow up // Arrow up
this.player.increaseVolume(0.1); player.increaseVolume(0.1);
break; break;
case 40: case 40:
// Arrow down // Arrow down
this.player.decreaseVolume(0.1); player.decreaseVolume(0.1);
break; break;
case 77: case 77:
// M key // M key
if (!repeat) { if (!repeat) {
this.player.muted = !this.player.muted; player.muted = !player.muted;
} }
break; break;
case 39: case 39:
// Arrow forward // Arrow forward
this.player.forward(); player.forward();
break; break;
case 37: case 37:
// Arrow back // Arrow back
this.player.rewind(); player.rewind();
break; break;
case 70: case 70:
// F key // F key
this.player.fullscreen.toggle(); player.fullscreen.toggle();
break; break;
case 67: case 67:
// C key // C key
if (!repeat) { if (!repeat) {
this.player.toggleCaptions(); player.toggleCaptions();
} }
break; break;
case 76: case 76:
// L key // L key
this.player.loop = !this.player.loop; player.loop = !player.loop;
break; break;
/* case 73: /* case 73:
@ -9843,8 +9848,8 @@ typeof navigator === "object" && (function (global, factory) {
// 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
if (!this.player.fullscreen.enabled && this.player.fullscreen.active && code === 27) { if (!player.fullscreen.enabled && player.fullscreen.active && code === 27) {
this.player.fullscreen.toggle(); player.fullscreen.toggle();
} }
// Store last code for next cycle // Store last code for next cycle
@ -9867,15 +9872,19 @@ typeof navigator === "object" && (function (global, factory) {
}, { }, {
key: 'firstTouch', key: 'firstTouch',
value: function firstTouch() { value: function firstTouch() {
this.player.touch = true; var player = this.player;
player.touch = true;
// Add touch class // Add touch class
toggleClass(this.player.elements.container, this.player.config.classNames.isTouch, true); toggleClass(player.elements.container, player.config.classNames.isTouch, true);
} }
}, { }, {
key: 'setTabFocus', key: 'setTabFocus',
value: function setTabFocus(event) { value: function setTabFocus(event) {
var _this2 = this; var player = this.player;
clearTimeout(this.focusTimer); clearTimeout(this.focusTimer);
@ -9891,8 +9900,8 @@ typeof navigator === "object" && (function (global, factory) {
// Remove current classes // Remove current classes
var removeCurrent = function removeCurrent() { var removeCurrent = function removeCurrent() {
var className = _this2.player.config.classNames.tabFocus; var className = player.config.classNames.tabFocus;
var current = getElements.call(_this2.player, '.' + className); var current = getElements.call(player, '.' + className);
toggleClass(current, className, false); toggleClass(current, className, false);
}; };
@ -9909,16 +9918,15 @@ typeof navigator === "object" && (function (global, factory) {
// Delay the adding of classname until the focus has changed // Delay the adding of classname until the focus has changed
// This event fires before the focusin event // This event fires before the focusin event
this.focusTimer = setTimeout(function () { this.focusTimer = setTimeout(function () {
var focused = document.activeElement; var focused = document.activeElement;
// Ignore if current focus element isn't inside the player // Ignore if current focus element isn't inside the player
if (!_this2.player.elements.container.contains(focused)) { if (!player.elements.container.contains(focused)) {
return; return;
} }
toggleClass(document.activeElement, _this2.player.config.classNames.tabFocus, true); toggleClass(document.activeElement, player.config.classNames.tabFocus, true);
}, 10); }, 10);
} }
@ -9928,20 +9936,22 @@ typeof navigator === "object" && (function (global, factory) {
key: 'global', key: 'global',
value: function global() { value: function global() {
var toggle = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; var toggle = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
var player = this.player;
// Keyboard shortcuts // Keyboard shortcuts
if (this.player.config.keyboard.global) {
toggleListener.call(this.player, window, 'keydown keyup', this.handleKey, toggle, false); if (player.config.keyboard.global) {
toggleListener.call(player, window, 'keydown keyup', this.handleKey, toggle, false);
} }
// Click anywhere closes menu // Click anywhere closes menu
toggleListener.call(this.player, document.body, 'click', this.toggleMenu, toggle); toggleListener.call(player, document.body, 'click', this.toggleMenu, toggle);
// Detect touch by events // Detect touch by events
once.call(this.player, document.body, 'touchstart', this.firstTouch); once.call(player, document.body, 'touchstart', this.firstTouch);
// Tab focus detection // Tab focus detection
toggleListener.call(this.player, document.body, 'keydown focus blur', this.setTabFocus, toggle, false, true); toggleListener.call(player, document.body, 'keydown focus blur', this.setTabFocus, toggle, false, true);
} }
// Container listeners // Container listeners
@ -9949,16 +9959,17 @@ typeof navigator === "object" && (function (global, factory) {
}, { }, {
key: 'container', key: 'container',
value: function container() { value: function container() {
var _this3 = this; var player = this.player;
// Keyboard shortcuts // Keyboard shortcuts
if (!this.player.config.keyboard.global && this.player.config.keyboard.focused) {
on.call(this.player, this.player.elements.container, 'keydown keyup', this.handleKey, false); if (!player.config.keyboard.global && player.config.keyboard.focused) {
on.call(player, player.elements.container, 'keydown keyup', this.handleKey, false);
} }
// Toggle controls on mouse events and entering fullscreen // Toggle controls on mouse events and entering fullscreen
on.call(this.player, this.player.elements.container, 'mousemove mouseleave touchstart touchmove enterfullscreen exitfullscreen', function (event) { on.call(player, player.elements.container, 'mousemove mouseleave touchstart touchmove enterfullscreen exitfullscreen', function (event) {
var controls$$1 = _this3.player.elements.controls; var controls$$1 = player.elements.controls;
// Remove button states for fullscreen // Remove button states for fullscreen
@ -9973,17 +9984,17 @@ typeof navigator === "object" && (function (global, factory) {
var delay = 0; var delay = 0;
if (show) { if (show) {
ui.toggleControls.call(_this3.player, true); ui.toggleControls.call(player, true);
// Use longer timeout for touch devices // Use longer timeout for touch devices
delay = _this3.player.touch ? 3000 : 2000; delay = player.touch ? 3000 : 2000;
} }
// Clear timer // Clear timer
clearTimeout(_this3.player.timers.controls); clearTimeout(player.timers.controls);
// Set new timer to prevent flicker when seeking // Set new timer to prevent flicker when seeking
_this3.player.timers.controls = setTimeout(function () { player.timers.controls = setTimeout(function () {
return ui.toggleControls.call(_this3.player, false); return ui.toggleControls.call(player, false);
}, delay); }, delay);
}); });
} }
@ -9993,76 +10004,79 @@ typeof navigator === "object" && (function (global, factory) {
}, { }, {
key: 'media', key: 'media',
value: function media() { value: function media() {
var _this4 = this; var _this = this;
var player = this.player;
// Time change on media // Time change on media
on.call(this.player, this.player.media, 'timeupdate seeking seeked', function (event) {
return controls.timeUpdate.call(_this4.player, event); on.call(player, player.media, 'timeupdate seeking seeked', function (event) {
return controls.timeUpdate.call(player, event);
}); });
// Display duration // Display duration
on.call(this.player, this.player.media, 'durationchange loadeddata loadedmetadata', function (event) { on.call(player, player.media, 'durationchange loadeddata loadedmetadata', function (event) {
return controls.durationUpdate.call(_this4.player, event); return controls.durationUpdate.call(player, event);
}); });
// Check for audio tracks on load // Check for audio tracks on load
// We can't use `loadedmetadata` as it doesn't seem to have audio tracks at that point // We can't use `loadedmetadata` as it doesn't seem to have audio tracks at that point
on.call(this.player, this.player.media, 'canplay', function () { on.call(player, player.media, 'canplay', function () {
toggleHidden(_this4.player.elements.volume, !_this4.player.hasAudio); toggleHidden(player.elements.volume, !player.hasAudio);
toggleHidden(_this4.player.elements.buttons.mute, !_this4.player.hasAudio); toggleHidden(player.elements.buttons.mute, !player.hasAudio);
}); });
// Handle the media finishing // Handle the media finishing
on.call(this.player, this.player.media, 'ended', function () { on.call(player, player.media, 'ended', function () {
// Show poster on end // Show poster on end
if (_this4.player.isHTML5 && _this4.player.isVideo && _this4.player.config.resetOnEnd) { if (player.isHTML5 && player.isVideo && player.config.resetOnEnd) {
// Restart // Restart
_this4.player.restart(); player.restart();
} }
}); });
// Check for buffer progress // Check for buffer progress
on.call(this.player, this.player.media, 'progress playing seeking seeked', function (event) { on.call(player, player.media, 'progress playing seeking seeked', function (event) {
return controls.updateProgress.call(_this4.player, event); return controls.updateProgress.call(player, event);
}); });
// Handle volume changes // Handle volume changes
on.call(this.player, this.player.media, 'volumechange', function (event) { on.call(player, player.media, 'volumechange', function (event) {
return controls.updateVolume.call(_this4.player, event); return controls.updateVolume.call(player, event);
}); });
// Handle play/pause // Handle play/pause
on.call(this.player, this.player.media, 'playing play pause ended emptied timeupdate', function (event) { on.call(player, player.media, 'playing play pause ended emptied timeupdate', function (event) {
return ui.checkPlaying.call(_this4.player, event); return ui.checkPlaying.call(player, event);
}); });
// Loading state // Loading state
on.call(this.player, this.player.media, 'waiting canplay seeked playing', function (event) { on.call(player, player.media, 'waiting canplay seeked playing', function (event) {
return ui.checkLoading.call(_this4.player, event); return ui.checkLoading.call(player, event);
}); });
// If autoplay, then load advertisement if required // If autoplay, then load advertisement if required
// TODO: Show some sort of loading state while the ad manager loads else there's a delay before ad shows // TODO: Show some sort of loading state while the ad manager loads else there's a delay before ad shows
on.call(this.player, this.player.media, 'playing', function () { on.call(player, player.media, 'playing', function () {
if (!_this4.player.ads) { if (!player.ads) {
return; return;
} }
// If ads are enabled, wait for them first // If ads are enabled, wait for them first
if (_this4.player.ads.enabled && !_this4.player.ads.initialized) { if (player.ads.enabled && !player.ads.initialized) {
// Wait for manager response // Wait for manager response
_this4.player.ads.managerPromise.then(function () { player.ads.managerPromise.then(function () {
return _this4.player.ads.play(); return player.ads.play();
}).catch(function () { }).catch(function () {
return _this4.player.play(); return player.play();
}); });
} }
}); });
// Click video // Click video
if (this.player.supported.ui && this.player.config.clickToPlay && !this.player.isAudio) { if (player.supported.ui && player.config.clickToPlay && !player.isAudio) {
// Re-fetch the wrapper // Re-fetch the wrapper
var wrapper = getElement.call(this.player, '.' + this.player.config.classNames.video); var wrapper = getElement.call(player, '.' + player.config.classNames.video);
// Bail if there's no wrapper (this should never happen) // Bail if there's no wrapper (this should never happen)
if (!is$1.element(wrapper)) { if (!is$1.element(wrapper)) {
@ -10070,75 +10084,77 @@ typeof navigator === "object" && (function (global, factory) {
} }
// On click play, pause ore restart // On click play, pause ore restart
on.call(this.player, wrapper, 'click', function () { on.call(player, wrapper, 'click', function () {
var controlsHidden = hasClass(player.elements.container, _this.config.classNames.hideControls);
console.warn('click');
// Touch devices will just show controls (if we're hiding controls) // Touch devices will just show controls (if we're hiding controls)
if (_this4.player.config.hideControls && _this4.player.touch && !_this4.player.paused) { if (player.config.hideControls && player.touch && controlsHidden) {
return; return;
} }
if (_this4.player.paused) { if (player.ended) {
_this4.player.play(); player.restart();
} else if (_this4.player.ended) { player.play();
_this4.player.restart();
_this4.player.play();
} else { } else {
_this4.player.pause(); _this.togglePlay();
} }
}); });
} }
// Disable right click // Disable right click
if (this.player.supported.ui && this.player.config.disableContextMenu) { if (player.supported.ui && player.config.disableContextMenu) {
on.call(this.player, this.player.elements.wrapper, 'contextmenu', function (event) { on.call(player, player.elements.wrapper, 'contextmenu', function (event) {
event.preventDefault(); event.preventDefault();
}, false); }, false);
} }
// Volume change // Volume change
on.call(this.player, this.player.media, 'volumechange', function () { on.call(player, player.media, 'volumechange', function () {
// Save to storage // Save to storage
_this4.player.storage.set({ player.storage.set({
volume: _this4.player.volume, volume: player.volume,
muted: _this4.player.muted muted: player.muted
}); });
}); });
// Speed change // Speed change
on.call(this.player, this.player.media, 'ratechange', function () { on.call(player, player.media, 'ratechange', function () {
// Update UI // Update UI
controls.updateSetting.call(_this4.player, 'speed'); controls.updateSetting.call(player, 'speed');
// Save to storage // Save to storage
_this4.player.storage.set({ speed: _this4.player.speed }); player.storage.set({ speed: player.speed });
}); });
// Quality request // Quality request
on.call(this.player, this.player.media, 'qualityrequested', function (event) { on.call(player, player.media, 'qualityrequested', function (event) {
// Save to storage // Save to storage
_this4.player.storage.set({ quality: event.detail.quality }); player.storage.set({ quality: event.detail.quality });
}); });
// Quality change // Quality change
on.call(this.player, this.player.media, 'qualitychange', function (event) { on.call(player, player.media, 'qualitychange', function (event) {
// Update UI // Update UI
controls.updateSetting.call(_this4.player, 'quality', null, event.detail.quality); controls.updateSetting.call(player, 'quality', null, event.detail.quality);
}); });
// Proxy events to container // Proxy events to container
// Bubble up key events for Edge // Bubble up key events for Edge
var proxyEvents = this.player.config.events.concat(['keyup', 'keydown']).join(' '); var proxyEvents = player.config.events.concat(['keyup', 'keydown']).join(' ');
on.call(this.player, this.player.media, proxyEvents, function (event) { on.call(player, player.media, proxyEvents, function (event) {
var _event$detail = event.detail, var _event$detail = event.detail,
detail = _event$detail === undefined ? {} : _event$detail; detail = _event$detail === undefined ? {} : _event$detail;
// Get error details from media // Get error details from media
if (event.type === 'error') { if (event.type === 'error') {
detail = _this4.player.media.error; detail = player.media.error;
} }
triggerEvent.call(_this4.player, _this4.player.elements.container, event.type, true, detail); triggerEvent.call(player, player.elements.container, event.type, true, detail);
}); });
} }
@ -10147,18 +10163,20 @@ typeof navigator === "object" && (function (global, factory) {
}, { }, {
key: 'proxy', key: 'proxy',
value: function proxy(event, defaultHandler, customHandlerKey) { value: function proxy(event, defaultHandler, customHandlerKey) {
var customHandler = this.player.config.listeners[customHandlerKey]; var player = this.player;
var customHandler = player.config.listeners[customHandlerKey];
var hasCustomHandler = is$1.function(customHandler); var hasCustomHandler = is$1.function(customHandler);
var returned = true; var returned = true;
// Execute custom handler // Execute custom handler
if (hasCustomHandler) { if (hasCustomHandler) {
returned = customHandler.call(this.player, event); returned = customHandler.call(player, event);
} }
// 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 && is$1.function(defaultHandler)) {
defaultHandler.call(this.player, event); defaultHandler.call(player, event);
} }
} }
@ -10167,15 +10185,16 @@ typeof navigator === "object" && (function (global, factory) {
}, { }, {
key: 'bind', key: 'bind',
value: function bind(element, type, defaultHandler, customHandlerKey) { value: function bind(element, type, defaultHandler, customHandlerKey) {
var _this5 = this; var _this2 = this;
var passive = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true; var passive = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
var player = this.player;
var customHandler = this.player.config.listeners[customHandlerKey]; var customHandler = player.config.listeners[customHandlerKey];
var hasCustomHandler = is$1.function(customHandler); var hasCustomHandler = is$1.function(customHandler);
on.call(this.player, element, type, function (event) { on.call(player, element, type, function (event) {
return _this5.proxy(event, defaultHandler, customHandlerKey); return _this2.proxy(event, defaultHandler, customHandlerKey);
}, passive && !hasCustomHandler); }, passive && !hasCustomHandler);
} }
@ -10184,57 +10203,60 @@ typeof navigator === "object" && (function (global, factory) {
}, { }, {
key: 'controls', key: 'controls',
value: function controls$$1() { value: function controls$$1() {
var _this6 = this; var _this3 = this;
var player = this.player;
// IE doesn't support input event, so we fallback to change // IE doesn't support input event, so we fallback to change
var inputEvent = browser.isIE ? 'change' : 'input'; var inputEvent = browser.isIE ? 'change' : 'input';
// Play/pause toggle // Play/pause toggle
if (this.player.elements.buttons.play) { if (player.elements.buttons.play) {
Array.from(this.player.elements.buttons.play).forEach(function (button) { Array.from(player.elements.buttons.play).forEach(function (button) {
_this6.bind(button, 'click', _this6.player.togglePlay, 'play'); _this3.bind(button, 'click', player.togglePlay, 'play');
}); });
} }
// Pause // Pause
this.bind(this.player.elements.buttons.restart, 'click', this.player.restart, 'restart'); this.bind(player.elements.buttons.restart, 'click', player.restart, 'restart');
// Rewind // Rewind
this.bind(this.player.elements.buttons.rewind, 'click', this.player.rewind, 'rewind'); this.bind(player.elements.buttons.rewind, 'click', player.rewind, 'rewind');
// Rewind // Rewind
this.bind(this.player.elements.buttons.fastForward, 'click', this.player.forward, 'fastForward'); this.bind(player.elements.buttons.fastForward, 'click', player.forward, 'fastForward');
// Mute toggle // Mute toggle
this.bind(this.player.elements.buttons.mute, 'click', function () { this.bind(player.elements.buttons.mute, 'click', function () {
_this6.player.muted = !_this6.player.muted; player.muted = !player.muted;
}, 'mute'); }, 'mute');
// Captions toggle // Captions toggle
this.bind(this.player.elements.buttons.captions, 'click', function () { this.bind(player.elements.buttons.captions, 'click', function () {
return _this6.player.toggleCaptions(); return player.toggleCaptions();
}); });
// Fullscreen toggle // Fullscreen toggle
this.bind(this.player.elements.buttons.fullscreen, 'click', function () { this.bind(player.elements.buttons.fullscreen, 'click', function () {
_this6.player.fullscreen.toggle(); player.fullscreen.toggle();
}, 'fullscreen'); }, 'fullscreen');
// Picture-in-Picture // Picture-in-Picture
this.bind(this.player.elements.buttons.pip, 'click', function () { this.bind(player.elements.buttons.pip, 'click', function () {
_this6.player.pip = 'toggle'; player.pip = 'toggle';
}, 'pip'); }, 'pip');
// Airplay // Airplay
this.bind(this.player.elements.buttons.airplay, 'click', this.player.airplay, 'airplay'); this.bind(player.elements.buttons.airplay, 'click', player.airplay, 'airplay');
// Settings menu - click toggle // Settings menu - click toggle
this.bind(this.player.elements.buttons.settings, 'click', function (event) { this.bind(player.elements.buttons.settings, 'click', function (event) {
controls.toggleMenu.call(_this6.player, event); controls.toggleMenu.call(player, event);
}); });
// Settings menu - keyboard toggle // Settings menu - keyboard toggle
this.bind(this.player.elements.buttons.settings, 'keydown', function (event) { this.bind(player.elements.buttons.settings, 'keydown', function (event) {
// We only care about space // We only care about space
if (event.which !== 32) { if (event.which !== 32) {
return; return;
@ -10247,44 +10269,44 @@ typeof navigator === "object" && (function (global, factory) {
event.stopPropagation(); event.stopPropagation();
// Toggle menu // Toggle menu
controls.toggleMenu.call(_this6.player, event); controls.toggleMenu.call(player, event);
}, null, false); }, null, false);
// Set range input alternative "value", which matches the tooltip time (#954) // Set range input alternative "value", which matches the tooltip time (#954)
this.bind(this.player.elements.inputs.seek, 'mousedown mousemove', function (event) { this.bind(player.elements.inputs.seek, 'mousedown mousemove', function (event) {
var clientRect = _this6.player.elements.progress.getBoundingClientRect(); var clientRect = player.elements.progress.getBoundingClientRect();
var percent = 100 / clientRect.width * (event.pageX - clientRect.left); var percent = 100 / clientRect.width * (event.pageX - clientRect.left);
event.currentTarget.setAttribute('seek-value', percent); event.currentTarget.setAttribute('seek-value', percent);
}); });
// Pause while seeking // Pause while seeking
this.bind(this.player.elements.inputs.seek, 'mousedown mouseup keydown keyup touchstart touchend', function (event) { this.bind(player.elements.inputs.seek, 'mousedown mouseup keydown keyup touchstart touchend', function (event) {
var seek = event.currentTarget; var seek = event.currentTarget;
var code = event.keyCode ? event.keyCode : event.which; var code = event.keyCode ? event.keyCode : event.which;
var eventType = event.type; var eventType = event.type;
var attribute = 'play-on-seeked';
if ((eventType === 'keydown' || eventType === 'keyup') && code !== 39 && code !== 37) { if ((eventType === 'keydown' || eventType === 'keyup') && code !== 39 && code !== 37) {
return; return;
} }
// Was playing before? // Was playing before?
var play = seek.hasAttribute('play-on-seeked'); var play = seek.hasAttribute(attribute);
// Done seeking // Done seeking
var done = ['mouseup', 'touchend', 'keyup'].includes(event.type); var done = ['mouseup', 'touchend', 'keyup'].includes(event.type);
// If we're done seeking and it was playing, resume playback // If we're done seeking and it was playing, resume playback
if (play && done) { if (play && done) {
seek.removeAttribute('play-on-seeked'); seek.removeAttribute(attribute);
_this6.player.play(); player.play();
} else if (!done && _this6.player.playing) { } else if (!done && player.playing) {
seek.setAttribute('play-on-seeked', ''); seek.setAttribute(attribute, '');
_this6.player.pause(); player.pause();
} }
}); });
// Seek // Seek
this.bind(this.player.elements.inputs.seek, inputEvent, function (event) { this.bind(player.elements.inputs.seek, inputEvent, function (event) {
var seek = event.currentTarget; var seek = event.currentTarget;
// If it exists, use seek-value instead of "value" for consistency with tooltip time (#954) // If it exists, use seek-value instead of "value" for consistency with tooltip time (#954)
@ -10296,89 +10318,95 @@ typeof navigator === "object" && (function (global, factory) {
seek.removeAttribute('seek-value'); seek.removeAttribute('seek-value');
_this6.player.currentTime = seekTo / seek.max * _this6.player.duration; // Super weird iOS bug where after you interact with an <input type="range">,
// it takes over further interactions on the page. This is a hack
if (browser.isIos) {
repaint(seek);
}
player.currentTime = seekTo / seek.max * player.duration;
}, 'seek'); }, 'seek');
// Current time invert // Current time invert
// Only if one time element is used for both currentTime and duration // Only if one time element is used for both currentTime and duration
if (this.player.config.toggleInvert && !is$1.element(this.player.elements.display.duration)) { if (player.config.toggleInvert && !is$1.element(player.elements.display.duration)) {
this.bind(this.player.elements.display.currentTime, 'click', function () { this.bind(player.elements.display.currentTime, 'click', function () {
// Do nothing if we're at the start // Do nothing if we're at the start
if (_this6.player.currentTime === 0) { if (player.currentTime === 0) {
return; return;
} }
_this6.player.config.invertTime = !_this6.player.config.invertTime; player.config.invertTime = !player.config.invertTime;
controls.timeUpdate.call(_this6.player); controls.timeUpdate.call(player);
}); });
} }
// Volume // Volume
this.bind(this.player.elements.inputs.volume, inputEvent, function (event) { this.bind(player.elements.inputs.volume, inputEvent, function (event) {
_this6.player.volume = event.target.value; player.volume = event.target.value;
}, 'volume'); }, 'volume');
// Polyfill for lower fill in <input type="range"> for webkit // Polyfill for lower fill in <input type="range"> for webkit
if (browser.isWebkit) { if (browser.isWebkit) {
Array.from(getElements.call(this.player, 'input[type="range"]')).forEach(function (element) { Array.from(getElements.call(player, 'input[type="range"]')).forEach(function (element) {
_this6.bind(element, 'input', function (event) { _this3.bind(element, 'input', function (event) {
return controls.updateRangeFill.call(_this6.player, event.target); return controls.updateRangeFill.call(player, event.target);
}); });
}); });
} }
// Seek tooltip // Seek tooltip
this.bind(this.player.elements.progress, 'mouseenter mouseleave mousemove', function (event) { this.bind(player.elements.progress, 'mouseenter mouseleave mousemove', function (event) {
return controls.updateSeekTooltip.call(_this6.player, event); return controls.updateSeekTooltip.call(player, event);
}); });
// Update controls.hover state (used for ui.toggleControls to avoid hiding when interacting) // Update controls.hover state (used for ui.toggleControls to avoid hiding when interacting)
this.bind(this.player.elements.controls, 'mouseenter mouseleave', function (event) { this.bind(player.elements.controls, 'mouseenter mouseleave', function (event) {
_this6.player.elements.controls.hover = !_this6.player.touch && event.type === 'mouseenter'; player.elements.controls.hover = !player.touch && event.type === 'mouseenter';
}); });
// Update controls.pressed state (used for ui.toggleControls to avoid hiding when interacting) // Update controls.pressed state (used for ui.toggleControls to avoid hiding when interacting)
this.bind(this.player.elements.controls, 'mousedown mouseup touchstart touchend touchcancel', function (event) { this.bind(player.elements.controls, 'mousedown mouseup touchstart touchend touchcancel', function (event) {
_this6.player.elements.controls.pressed = ['mousedown', 'touchstart'].includes(event.type); player.elements.controls.pressed = ['mousedown', 'touchstart'].includes(event.type);
}); });
// Focus in/out on controls // Focus in/out on controls
this.bind(this.player.elements.controls, 'focusin focusout', function (event) { this.bind(player.elements.controls, 'focusin focusout', function (event) {
var _player = _this6.player, var config = player.config,
config = _player.config, elements = player.elements,
elements = _player.elements, timers = player.timers;
timers = _player.timers;
var isFocusIn = event.type === 'focusin';
// Skip transition to prevent focus from scrolling the parent element // Skip transition to prevent focus from scrolling the parent element
toggleClass(elements.controls, config.classNames.noTransition, isFocusIn);
toggleClass(elements.controls, config.classNames.noTransition, event.type === 'focusin');
// Toggle // Toggle
ui.toggleControls.call(_this6.player, event.type === 'focusin'); ui.toggleControls.call(player, isFocusIn);
// If focusin, hide again after delay // If focusin, hide again after delay
if (event.type === 'focusin') { if (isFocusIn) {
// Restore transition // Restore transition
setTimeout(function () { setTimeout(function () {
toggleClass(elements.controls, config.classNames.noTransition, false); toggleClass(elements.controls, config.classNames.noTransition, false);
}, 0); }, 0);
// Delay a little more for keyboard users // Delay a little more for keyboard users
var delay = _this6.touch ? 3000 : 4000; var delay = _this3.touch ? 3000 : 4000;
// Clear timer // Clear timer
clearTimeout(timers.controls); clearTimeout(timers.controls);
// Hide // Hide
timers.controls = setTimeout(function () { timers.controls = setTimeout(function () {
return ui.toggleControls.call(_this6.player, false); return ui.toggleControls.call(player, false);
}, delay); }, delay);
} }
}); });
// Mouse wheel for volume // Mouse wheel for volume
this.bind(this.player.elements.inputs.volume, 'wheel', function (event) { this.bind(player.elements.inputs.volume, 'wheel', function (event) {
// Detect "natural" scroll - suppored on OS X Safari only // Detect "natural" scroll - suppored on OS X Safari only
// Other browsers on OS X will be inverted until support improves // Other browsers on OS X will be inverted until support improves
var inverted = event.webkitDirectionInvertedFromDevice; var inverted = event.webkitDirectionInvertedFromDevice;
@ -10398,10 +10426,10 @@ typeof navigator === "object" && (function (global, factory) {
var direction = Math.sign(Math.abs(x) > Math.abs(y) ? x : y); var direction = Math.sign(Math.abs(x) > Math.abs(y) ? x : y);
// Change the volume by 2% // Change the volume by 2%
_this6.player.increaseVolume(direction / 50); player.increaseVolume(direction / 50);
// Don't break page scrolling at max and min // Don't break page scrolling at max and min
var volume = _this6.player.media.volume; var volume = player.media.volume;
if (direction === 1 && volume < 1 || direction === -1 && volume > 0) { if (direction === 1 && volume < 1 || direction === -1 && volume > 0) {
event.preventDefault(); event.preventDefault();
@ -12690,7 +12718,7 @@ typeof navigator === "object" && (function (global, factory) {
if (this.media.hasAttribute('autoplay')) { if (this.media.hasAttribute('autoplay')) {
this.config.autoplay = true; this.config.autoplay = true;
} }
if (this.media.hasAttribute('playsinline')) { if (this.media.hasAttribute('playsinline') || this.media.hasAttribute('webkit-playsinline')) {
this.config.playsinline = true; this.config.playsinline = true;
} }
if (this.media.hasAttribute('muted')) { if (this.media.hasAttribute('muted')) {
@ -12762,7 +12790,9 @@ typeof navigator === "object" && (function (global, factory) {
this.fullscreen = new Fullscreen(this); this.fullscreen = new Fullscreen(this);
// Setup ads if provided // Setup ads if provided
this.ads = new Ads(this); if (this.config.ads.enabled) {
this.ads = new Ads(this);
}
// Autoplay if required // Autoplay if required
if (this.config.autoplay) { if (this.config.autoplay) {
@ -12968,13 +12998,16 @@ typeof navigator === "object" && (function (global, factory) {
if (hiding && this.config.controls.includes('settings') && !is$1.empty(this.config.settings)) { if (hiding && this.config.controls.includes('settings') && !is$1.empty(this.config.settings)) {
controls.toggleMenu.call(this, false); controls.toggleMenu.call(this, false);
} }
// Trigger event on change // Trigger event on change
if (hiding !== isHidden) { if (hiding !== isHidden) {
var eventName = hiding ? 'controlshidden' : 'controlsshown'; var eventName = hiding ? 'controlshidden' : 'controlsshown';
triggerEvent.call(this, this.media, eventName); triggerEvent.call(this, this.media, eventName);
} }
return !hiding; return !hiding;
} }
return false; return false;
} }
@ -13464,7 +13497,9 @@ typeof navigator === "object" && (function (global, factory) {
} }
// Trigger request event // Trigger request event
triggerEvent.call(this, this.media, 'qualityrequested', false, { quality: quality }); triggerEvent.call(this, this.media, 'qualityrequested', false, {
quality: quality
});
// Update config // Update config
config.selected = quality; config.selected = quality;

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

View File

@ -42,7 +42,7 @@
"babel-plugin-external-helpers": "^6.22.0", "babel-plugin-external-helpers": "^6.22.0",
"babel-preset-env": "^1.7.0", "babel-preset-env": "^1.7.0",
"del": "^3.0.0", "del": "^3.0.0",
"eslint": "^5.1.0", "eslint": "^5.2.0",
"eslint-config-airbnb-base": "^13.0.0", "eslint-config-airbnb-base": "^13.0.0",
"eslint-config-prettier": "^2.9.0", "eslint-config-prettier": "^2.9.0",
"eslint-plugin-import": "^2.13.0", "eslint-plugin-import": "^2.13.0",
@ -57,7 +57,7 @@
"gulp-header": "^2.0.5", "gulp-header": "^2.0.5",
"gulp-open": "^3.0.1", "gulp-open": "^3.0.1",
"gulp-postcss": "^7.0.1", "gulp-postcss": "^7.0.1",
"gulp-rename": "^1.3.0", "gulp-rename": "^1.4.0",
"gulp-replace": "^1.0.0", "gulp-replace": "^1.0.0",
"gulp-s3": "^0.11.0", "gulp-s3": "^0.11.0",
"gulp-sass": "^4.0.1", "gulp-sass": "^4.0.1",
@ -81,15 +81,15 @@
"stylelint-config-recommended": "^2.1.0", "stylelint-config-recommended": "^2.1.0",
"stylelint-config-sass-guidelines": "^5.0.0", "stylelint-config-sass-guidelines": "^5.0.0",
"stylelint-order": "^0.8.1", "stylelint-order": "^0.8.1",
"stylelint-scss": "^3.1.3", "stylelint-scss": "^3.2.0",
"stylelint-selector-bem-pattern": "^2.0.0", "stylelint-selector-bem-pattern": "^2.0.0",
"through2": "^2.0.3" "through2": "^2.0.3"
}, },
"dependencies": { "dependencies": {
"babel-polyfill": "^6.26.0", "babel-polyfill": "^6.26.0",
"custom-event-polyfill": "^0.3.0", "custom-event-polyfill": "^1.0.2",
"loadjs": "^3.5.4", "loadjs": "^3.5.4",
"raven-js": "^3.26.3", "raven-js": "^3.26.4",
"url-polyfill": "^1.0.13" "url-polyfill": "^1.0.13"
} }
} }

View File

@ -162,9 +162,9 @@ reference, the CDN hosted SVG sprite can be found at `https://cdn.plyr.io/3.3.22
## Ads ## Ads
Plyr has partnered up with [vi.ai](http://vi.ai/publisher-video-monetization/?aid=plyrio) to offer monetization options for your videos. Getting setup is easy: Plyr has partnered up with [vi.ai](https://vi.ai/publisher-video-monetization/?aid=plyrio) to offer monetization options for your videos. Getting setup is easy:
* [Sign up for a vi.ai account](http://vi.ai/publisher-video-monetization/?aid=plyrio) * [Sign up for a vi.ai account](https://vi.ai/publisher-video-monetization/?aid=plyrio)
* Grab your publisher ID from the code snippet * Grab your publisher ID from the code snippet
* Enable ads in the [config options](#options) and enter your publisher ID * Enable ads in the [config options](#options) and enter your publisher ID

14
src/js/controls.js vendored
View File

@ -378,7 +378,7 @@ const controls = {
// Show the respective menu // Show the respective menu
if (!isRadioButton && [32,39].includes(event.which)) { if (!isRadioButton && [32,39].includes(event.which)) {
controls.showMenuPanel.call(this, type); controls.showMenuPanel.call(this, type, true);
} else { } else {
let target; let target;
@ -477,7 +477,7 @@ const controls = {
break; break;
} }
controls.showMenuPanel.call(this, 'home'); controls.showMenuPanel.call(this, 'home', event.type === 'keydown');
}, },
type, type,
false, false,
@ -1118,7 +1118,7 @@ const controls = {
}, },
// Show a panel in the menu // Show a panel in the menu
showMenuPanel(type = '') { showMenuPanel(type = '', tabFocus = false) {
const target = document.getElementById(`plyr-settings-${this.id}-${type}`); const target = document.getElementById(`plyr-settings-${this.id}-${type}`);
// Nothing to show, bail // Nothing to show, bail
@ -1170,7 +1170,7 @@ const controls = {
// Focus the first item // Focus the first item
const firstItem = target.querySelector('[role^="menuitem"]'); const firstItem = target.querySelector('[role^="menuitem"]');
setFocus.call(this, firstItem, true); setFocus.call(this, firstItem, tabFocus);
}, },
// Build the default HTML // Build the default HTML
@ -1344,7 +1344,7 @@ const controls = {
// Show menu on click // Show menu on click
on(menuItem, 'click', () => { on(menuItem, 'click', () => {
controls.showMenuPanel.call(this, type); controls.showMenuPanel.call(this, type, false);
}); });
const flex = createElement('span', null, i18n.get(type, this.config)); const flex = createElement('span', null, i18n.get(type, this.config));
@ -1406,12 +1406,12 @@ const controls = {
event.stopPropagation(); event.stopPropagation();
// Show the respective menu // Show the respective menu
controls.showMenuPanel.call(this, 'home'); controls.showMenuPanel.call(this, 'home', true);
}, false); }, false);
// Go back via button click // Go back via button click
on(backButton, 'click', () => { on(backButton, 'click', () => {
controls.showMenuPanel.call(this, 'home'); controls.showMenuPanel.call(this, 'home', false);
}); });
// Add to pane // Add to pane

View File

@ -4,10 +4,12 @@
import controls from './controls'; import controls from './controls';
import ui from './ui'; import ui from './ui';
import { repaint } from './utils/animation';
import browser from './utils/browser'; import browser from './utils/browser';
import { import {
getElement, getElement,
getElements, getElements,
hasClass,
matches, matches,
toggleClass, toggleClass,
toggleHidden, toggleHidden,
@ -30,6 +32,7 @@ class Listeners {
// Handle key presses // Handle key presses
handleKey(event) { handleKey(event) {
const { player } = this;
const code = event.keyCode ? event.keyCode : event.which; const code = event.keyCode ? event.keyCode : event.which;
const pressed = event.type === 'keydown'; const pressed = event.type === 'keydown';
const repeat = pressed && code === this.lastKey; const repeat = pressed && code === this.lastKey;
@ -48,7 +51,7 @@ class Listeners {
// Seek by the number keys // Seek by the number keys
const seekByKey = () => { const seekByKey = () => {
// Divide the max duration into 10th's and times by the number value // Divide the max duration into 10th's and times by the number value
this.player.currentTime = this.player.duration / 10 * (code - 48); player.currentTime = player.duration / 10 * (code - 48);
}; };
// Handle the key on keydown // Handle the key on keydown
@ -59,8 +62,8 @@ class Listeners {
// and any that accept key input http://webaim.org/techniques/keyboard/ // and any that accept key input http://webaim.org/techniques/keyboard/
const focused = document.activeElement; const focused = document.activeElement;
if (is.element(focused)) { if (is.element(focused)) {
const { editable } = this.player.config.selectors; const { editable } = player.config.selectors;
const { seek } = this.player.elements.inputs; const { seek } = player.elements.inputs;
if (focused !== seek && matches(focused, editable)) { if (focused !== seek && matches(focused, editable)) {
return; return;
@ -126,52 +129,52 @@ class Listeners {
case 75: case 75:
// Space and K key // Space and K key
if (!repeat) { if (!repeat) {
this.player.togglePlay(); player.togglePlay();
} }
break; break;
case 38: case 38:
// Arrow up // Arrow up
this.player.increaseVolume(0.1); player.increaseVolume(0.1);
break; break;
case 40: case 40:
// Arrow down // Arrow down
this.player.decreaseVolume(0.1); player.decreaseVolume(0.1);
break; break;
case 77: case 77:
// M key // M key
if (!repeat) { if (!repeat) {
this.player.muted = !this.player.muted; player.muted = !player.muted;
} }
break; break;
case 39: case 39:
// Arrow forward // Arrow forward
this.player.forward(); player.forward();
break; break;
case 37: case 37:
// Arrow back // Arrow back
this.player.rewind(); player.rewind();
break; break;
case 70: case 70:
// F key // F key
this.player.fullscreen.toggle(); player.fullscreen.toggle();
break; break;
case 67: case 67:
// C key // C key
if (!repeat) { if (!repeat) {
this.player.toggleCaptions(); player.toggleCaptions();
} }
break; break;
case 76: case 76:
// L key // L key
this.player.loop = !this.player.loop; player.loop = !player.loop;
break; break;
/* case 73: /* case 73:
@ -193,11 +196,11 @@ class Listeners {
// 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
if ( if (
!this.player.fullscreen.enabled && !player.fullscreen.enabled &&
this.player.fullscreen.active && player.fullscreen.active &&
code === 27 code === 27
) { ) {
this.player.fullscreen.toggle(); player.fullscreen.toggle();
} }
// Store last code for next cycle // Store last code for next cycle
@ -214,17 +217,21 @@ class Listeners {
// Device is touch enabled // Device is touch enabled
firstTouch() { firstTouch() {
this.player.touch = true; const { player } = this;
player.touch = true;
// Add touch class // Add touch class
toggleClass( toggleClass(
this.player.elements.container, player.elements.container,
this.player.config.classNames.isTouch, player.config.classNames.isTouch,
true, true,
); );
} }
setTabFocus(event) { setTabFocus(event) {
const { player } = this;
clearTimeout(this.focusTimer); clearTimeout(this.focusTimer);
// Ignore any key other than tab // Ignore any key other than tab
@ -239,8 +246,8 @@ class Listeners {
// Remove current classes // Remove current classes
const removeCurrent = () => { const removeCurrent = () => {
const className = this.player.config.classNames.tabFocus; const className = player.config.classNames.tabFocus;
const current = getElements.call(this.player, `.${className}`); const current = getElements.call(player, `.${className}`);
toggleClass(current, className, false); toggleClass(current, className, false);
}; };
@ -257,18 +264,17 @@ class Listeners {
// Delay the adding of classname until the focus has changed // Delay the adding of classname until the focus has changed
// This event fires before the focusin event // This event fires before the focusin event
this.focusTimer = setTimeout(() => { this.focusTimer = setTimeout(() => {
const focused = document.activeElement; const focused = document.activeElement;
// Ignore if current focus element isn't inside the player // Ignore if current focus element isn't inside the player
if (!this.player.elements.container.contains(focused)) { if (!player.elements.container.contains(focused)) {
return; return;
} }
toggleClass( toggleClass(
document.activeElement, document.activeElement,
this.player.config.classNames.tabFocus, player.config.classNames.tabFocus,
true, true,
); );
}, 10); }, 10);
@ -276,10 +282,12 @@ class Listeners {
// Global window & document listeners // Global window & document listeners
global(toggle = true) { global(toggle = true) {
const { player } = this;
// Keyboard shortcuts // Keyboard shortcuts
if (this.player.config.keyboard.global) { if (player.config.keyboard.global) {
toggleListener.call( toggleListener.call(
this.player, player,
window, window,
'keydown keyup', 'keydown keyup',
this.handleKey, this.handleKey,
@ -290,7 +298,7 @@ class Listeners {
// Click anywhere closes menu // Click anywhere closes menu
toggleListener.call( toggleListener.call(
this.player, player,
document.body, document.body,
'click', 'click',
this.toggleMenu, this.toggleMenu,
@ -298,11 +306,11 @@ class Listeners {
); );
// Detect touch by events // Detect touch by events
once.call(this.player, document.body, 'touchstart', this.firstTouch); once.call(player, document.body, 'touchstart', this.firstTouch);
// Tab focus detection // Tab focus detection
toggleListener.call( toggleListener.call(
this.player, player,
document.body, document.body,
'keydown focus blur', 'keydown focus blur',
this.setTabFocus, this.setTabFocus,
@ -314,14 +322,13 @@ class Listeners {
// Container listeners // Container listeners
container() { container() {
const { player } = this;
// Keyboard shortcuts // Keyboard shortcuts
if ( if (!player.config.keyboard.global && player.config.keyboard.focused) {
!this.player.config.keyboard.global &&
this.player.config.keyboard.focused
) {
on.call( on.call(
this.player, player,
this.player.elements.container, player.elements.container,
'keydown keyup', 'keydown keyup',
this.handleKey, this.handleKey,
false, false,
@ -330,11 +337,11 @@ class Listeners {
// Toggle controls on mouse events and entering fullscreen // Toggle controls on mouse events and entering fullscreen
on.call( on.call(
this.player, player,
this.player.elements.container, player.elements.container,
'mousemove mouseleave touchstart touchmove enterfullscreen exitfullscreen', 'mousemove mouseleave touchstart touchmove enterfullscreen exitfullscreen',
event => { event => {
const { controls } = this.player.elements; const { controls } = player.elements;
// Remove button states for fullscreen // Remove button states for fullscreen
if (event.type === 'enterfullscreen') { if (event.type === 'enterfullscreen') {
@ -350,17 +357,17 @@ class Listeners {
let delay = 0; let delay = 0;
if (show) { if (show) {
ui.toggleControls.call(this.player, true); ui.toggleControls.call(player, true);
// Use longer timeout for touch devices // Use longer timeout for touch devices
delay = this.player.touch ? 3000 : 2000; delay = player.touch ? 3000 : 2000;
} }
// Clear timer // Clear timer
clearTimeout(this.player.timers.controls); clearTimeout(player.timers.controls);
// Set new timer to prevent flicker when seeking // Set new timer to prevent flicker when seeking
this.player.timers.controls = setTimeout( player.timers.controls = setTimeout(
() => ui.toggleControls.call(this.player, false), () => ui.toggleControls.call(player, false),
delay, delay,
); );
}, },
@ -369,100 +376,89 @@ class Listeners {
// Listen for media events // Listen for media events
media() { media() {
const { player } = this;
// Time change on media // Time change on media
on.call( on.call(player, player.media, 'timeupdate seeking seeked', event =>
this.player, controls.timeUpdate.call(player, event),
this.player.media,
'timeupdate seeking seeked',
event => controls.timeUpdate.call(this.player, event),
); );
// Display duration // Display duration
on.call( on.call(
this.player, player,
this.player.media, player.media,
'durationchange loadeddata loadedmetadata', 'durationchange loadeddata loadedmetadata',
event => controls.durationUpdate.call(this.player, event), event => controls.durationUpdate.call(player, event),
); );
// Check for audio tracks on load // Check for audio tracks on load
// We can't use `loadedmetadata` as it doesn't seem to have audio tracks at that point // We can't use `loadedmetadata` as it doesn't seem to have audio tracks at that point
on.call(this.player, this.player.media, 'canplay', () => { on.call(player, player.media, 'canplay', () => {
toggleHidden(this.player.elements.volume, !this.player.hasAudio); toggleHidden(player.elements.volume, !player.hasAudio);
toggleHidden( toggleHidden(player.elements.buttons.mute, !player.hasAudio);
this.player.elements.buttons.mute,
!this.player.hasAudio,
);
}); });
// Handle the media finishing // Handle the media finishing
on.call(this.player, this.player.media, 'ended', () => { on.call(player, player.media, 'ended', () => {
// Show poster on end // Show poster on end
if ( if (player.isHTML5 && player.isVideo && player.config.resetOnEnd) {
this.player.isHTML5 &&
this.player.isVideo &&
this.player.config.resetOnEnd
) {
// Restart // Restart
this.player.restart(); player.restart();
} }
}); });
// Check for buffer progress // Check for buffer progress
on.call( on.call(
this.player, player,
this.player.media, player.media,
'progress playing seeking seeked', 'progress playing seeking seeked',
event => controls.updateProgress.call(this.player, event), event => controls.updateProgress.call(player, event),
); );
// Handle volume changes // Handle volume changes
on.call(this.player, this.player.media, 'volumechange', event => on.call(player, player.media, 'volumechange', event =>
controls.updateVolume.call(this.player, event), controls.updateVolume.call(player, event),
); );
// Handle play/pause // Handle play/pause
on.call( on.call(
this.player, player,
this.player.media, player.media,
'playing play pause ended emptied timeupdate', 'playing play pause ended emptied timeupdate',
event => ui.checkPlaying.call(this.player, event), event => ui.checkPlaying.call(player, event),
); );
// Loading state // Loading state
on.call( on.call(player, player.media, 'waiting canplay seeked playing', event =>
this.player, ui.checkLoading.call(player, event),
this.player.media,
'waiting canplay seeked playing',
event => ui.checkLoading.call(this.player, event),
); );
// If autoplay, then load advertisement if required // If autoplay, then load advertisement if required
// TODO: Show some sort of loading state while the ad manager loads else there's a delay before ad shows // TODO: Show some sort of loading state while the ad manager loads else there's a delay before ad shows
on.call(this.player, this.player.media, 'playing', () => { on.call(player, player.media, 'playing', () => {
if (!this.player.ads) { if (!player.ads) {
return; return;
} }
// If ads are enabled, wait for them first // If ads are enabled, wait for them first
if (this.player.ads.enabled && !this.player.ads.initialized) { if (player.ads.enabled && !player.ads.initialized) {
// Wait for manager response // Wait for manager response
this.player.ads.managerPromise player.ads.managerPromise
.then(() => this.player.ads.play()) .then(() => player.ads.play())
.catch(() => this.player.play()); .catch(() => player.play());
} }
}); });
// Click video // Click video
if ( if (
this.player.supported.ui && player.supported.ui &&
this.player.config.clickToPlay && player.config.clickToPlay &&
!this.player.isAudio !player.isAudio
) { ) {
// Re-fetch the wrapper // Re-fetch the wrapper
const wrapper = getElement.call( const wrapper = getElement.call(
this.player, player,
`.${this.player.config.classNames.video}`, `.${player.config.classNames.video}`,
); );
// Bail if there's no wrapper (this should never happen) // Bail if there's no wrapper (this should never happen)
@ -471,32 +467,49 @@ class Listeners {
} }
// On click play, pause ore restart // On click play, pause ore restart
on.call(this.player, wrapper, 'click', () => { on.call(
// Touch devices will just show controls (if we're hiding controls) player,
if ( player.elements.container,
this.player.config.hideControls && 'click touchstart',
this.player.touch && event => {
!this.player.paused const targets = [player.elements.container, wrapper];
) {
return;
}
if (this.player.paused) { // Ignore if click if not container or in video wrapper
this.player.play(); if (
} else if (this.player.ended) { !targets.includes(event.target) &&
this.player.restart(); !wrapper.contains(event.target)
this.player.play(); ) {
} else { return;
this.player.pause(); }
}
}); // First touch on touch devices will just show controls (if we're hiding controls)
// If controls are shown then it'll toggle like a pointer device
if (
player.config.hideControls &&
player.touch &&
hasClass(
player.elements.container,
player.config.classNames.hideControls,
)
) {
return;
}
if (player.ended) {
player.restart();
player.play();
} else {
player.togglePlay();
}
},
);
} }
// Disable right click // Disable right click
if (this.player.supported.ui && this.player.config.disableContextMenu) { if (player.supported.ui && player.config.disableContextMenu) {
on.call( on.call(
this.player, player,
this.player.elements.wrapper, player.elements.wrapper,
'contextmenu', 'contextmenu',
event => { event => {
event.preventDefault(); event.preventDefault();
@ -506,34 +519,34 @@ class Listeners {
} }
// Volume change // Volume change
on.call(this.player, this.player.media, 'volumechange', () => { on.call(player, player.media, 'volumechange', () => {
// Save to storage // Save to storage
this.player.storage.set({ player.storage.set({
volume: this.player.volume, volume: player.volume,
muted: this.player.muted, muted: player.muted,
}); });
}); });
// Speed change // Speed change
on.call(this.player, this.player.media, 'ratechange', () => { on.call(player, player.media, 'ratechange', () => {
// Update UI // Update UI
controls.updateSetting.call(this.player, 'speed'); controls.updateSetting.call(player, 'speed');
// Save to storage // Save to storage
this.player.storage.set({ speed: this.player.speed }); player.storage.set({ speed: player.speed });
}); });
// Quality request // Quality request
on.call(this.player, this.player.media, 'qualityrequested', event => { on.call(player, player.media, 'qualityrequested', event => {
// Save to storage // Save to storage
this.player.storage.set({ quality: event.detail.quality }); player.storage.set({ quality: event.detail.quality });
}); });
// Quality change // Quality change
on.call(this.player, this.player.media, 'qualitychange', event => { on.call(player, player.media, 'qualitychange', event => {
// Update UI // Update UI
controls.updateSetting.call( controls.updateSetting.call(
this.player, player,
'quality', 'quality',
null, null,
event.detail.quality, event.detail.quality,
@ -542,21 +555,21 @@ class Listeners {
// Proxy events to container // Proxy events to container
// Bubble up key events for Edge // Bubble up key events for Edge
const proxyEvents = this.player.config.events const proxyEvents = player.config.events
.concat(['keyup', 'keydown']) .concat(['keyup', 'keydown'])
.join(' '); .join(' ');
on.call(this.player, this.player.media, proxyEvents, event => { on.call(player, player.media, proxyEvents, event => {
let { detail = {} } = event; let { detail = {} } = event;
// Get error details from media // Get error details from media
if (event.type === 'error') { if (event.type === 'error') {
detail = this.player.media.error; detail = player.media.error;
} }
triggerEvent.call( triggerEvent.call(
this.player, player,
this.player.elements.container, player.elements.container,
event.type, event.type,
true, true,
detail, detail,
@ -566,28 +579,30 @@ class Listeners {
// Run default and custom handlers // Run default and custom handlers
proxy(event, defaultHandler, customHandlerKey) { proxy(event, defaultHandler, customHandlerKey) {
const customHandler = this.player.config.listeners[customHandlerKey]; const { player } = this;
const customHandler = player.config.listeners[customHandlerKey];
const hasCustomHandler = is.function(customHandler); const hasCustomHandler = is.function(customHandler);
let returned = true; let returned = true;
// Execute custom handler // Execute custom handler
if (hasCustomHandler) { if (hasCustomHandler) {
returned = customHandler.call(this.player, event); returned = customHandler.call(player, event);
} }
// Only call default handler if not prevented in custom handler // Only call default handler if not prevented in custom handler
if (returned && is.function(defaultHandler)) { if (returned && is.function(defaultHandler)) {
defaultHandler.call(this.player, event); defaultHandler.call(player, event);
} }
} }
// Trigger custom and default handlers // Trigger custom and default handlers
bind(element, type, defaultHandler, customHandlerKey, passive = true) { bind(element, type, defaultHandler, customHandlerKey, passive = true) {
const customHandler = this.player.config.listeners[customHandlerKey]; const { player } = this;
const customHandler = player.config.listeners[customHandlerKey];
const hasCustomHandler = is.function(customHandler); const hasCustomHandler = is.function(customHandler);
on.call( on.call(
this.player, player,
element, element,
type, type,
event => this.proxy(event, defaultHandler, customHandlerKey), event => this.proxy(event, defaultHandler, customHandlerKey),
@ -597,91 +612,93 @@ class Listeners {
// Listen for control events // Listen for control events
controls() { controls() {
const { player } = this;
// IE doesn't support input event, so we fallback to change // IE doesn't support input event, so we fallback to change
const inputEvent = browser.isIE ? 'change' : 'input'; const inputEvent = browser.isIE ? 'change' : 'input';
// Play/pause toggle // Play/pause toggle
if (this.player.elements.buttons.play) { if (player.elements.buttons.play) {
Array.from(this.player.elements.buttons.play).forEach(button => { Array.from(player.elements.buttons.play).forEach(button => {
this.bind(button, 'click', this.player.togglePlay, 'play'); this.bind(button, 'click', player.togglePlay, 'play');
}); });
} }
// Pause // Pause
this.bind( this.bind(
this.player.elements.buttons.restart, player.elements.buttons.restart,
'click', 'click',
this.player.restart, player.restart,
'restart', 'restart',
); );
// Rewind // Rewind
this.bind( this.bind(
this.player.elements.buttons.rewind, player.elements.buttons.rewind,
'click', 'click',
this.player.rewind, player.rewind,
'rewind', 'rewind',
); );
// Rewind // Rewind
this.bind( this.bind(
this.player.elements.buttons.fastForward, player.elements.buttons.fastForward,
'click', 'click',
this.player.forward, player.forward,
'fastForward', 'fastForward',
); );
// Mute toggle // Mute toggle
this.bind( this.bind(
this.player.elements.buttons.mute, player.elements.buttons.mute,
'click', 'click',
() => { () => {
this.player.muted = !this.player.muted; player.muted = !player.muted;
}, },
'mute', 'mute',
); );
// Captions toggle // Captions toggle
this.bind(this.player.elements.buttons.captions, 'click', () => this.bind(player.elements.buttons.captions, 'click', () =>
this.player.toggleCaptions(), player.toggleCaptions(),
); );
// Fullscreen toggle // Fullscreen toggle
this.bind( this.bind(
this.player.elements.buttons.fullscreen, player.elements.buttons.fullscreen,
'click', 'click',
() => { () => {
this.player.fullscreen.toggle(); player.fullscreen.toggle();
}, },
'fullscreen', 'fullscreen',
); );
// Picture-in-Picture // Picture-in-Picture
this.bind( this.bind(
this.player.elements.buttons.pip, player.elements.buttons.pip,
'click', 'click',
() => { () => {
this.player.pip = 'toggle'; player.pip = 'toggle';
}, },
'pip', 'pip',
); );
// Airplay // Airplay
this.bind( this.bind(
this.player.elements.buttons.airplay, player.elements.buttons.airplay,
'click', 'click',
this.player.airplay, player.airplay,
'airplay', 'airplay',
); );
// Settings menu - click toggle // Settings menu - click toggle
this.bind(this.player.elements.buttons.settings, 'click', event => { this.bind(player.elements.buttons.settings, 'click', event => {
controls.toggleMenu.call(this.player, event); controls.toggleMenu.call(player, event);
}); });
// Settings menu - keyboard toggle // Settings menu - keyboard toggle
this.bind( this.bind(
this.player.elements.buttons.settings, player.elements.buttons.settings,
'keydown', 'keydown',
event => { event => {
// We only care about space // We only care about space
@ -696,33 +713,28 @@ class Listeners {
event.stopPropagation(); event.stopPropagation();
// Toggle menu // Toggle menu
controls.toggleMenu.call(this.player, event); controls.toggleMenu.call(player, event);
}, },
null, null,
false, false,
); );
// Set range input alternative "value", which matches the tooltip time (#954) // Set range input alternative "value", which matches the tooltip time (#954)
this.bind( this.bind(player.elements.inputs.seek, 'mousedown mousemove', event => {
this.player.elements.inputs.seek, const rect = player.elements.progress.getBoundingClientRect();
'mousedown mousemove', const percent = 100 / rect.width * (event.pageX - rect.left);
event => { event.currentTarget.setAttribute('seek-value', percent);
const clientRect = this.player.elements.progress.getBoundingClientRect(); });
const percent =
100 / clientRect.width * (event.pageX - clientRect.left);
event.currentTarget.setAttribute('seek-value', percent);
},
);
// Pause while seeking // Pause while seeking
this.bind( this.bind(
this.player.elements.inputs.seek, player.elements.inputs.seek,
'mousedown mouseup keydown keyup touchstart touchend', 'mousedown mouseup keydown keyup touchstart touchend',
event => { event => {
const seek = event.currentTarget; const seek = event.currentTarget;
const code = event.keyCode ? event.keyCode : event.which; const code = event.keyCode ? event.keyCode : event.which;
const eventType = event.type; const eventType = event.type;
const attribute = 'play-on-seeked';
if ( if (
(eventType === 'keydown' || eventType === 'keyup') && (eventType === 'keydown' || eventType === 'keyup') &&
@ -731,7 +743,7 @@ class Listeners {
return; return;
} }
// Was playing before? // Was playing before?
const play = seek.hasAttribute('play-on-seeked'); const play = seek.hasAttribute(attribute);
// Done seeking // Done seeking
const done = ['mouseup', 'touchend', 'keyup'].includes( const done = ['mouseup', 'touchend', 'keyup'].includes(
@ -740,18 +752,18 @@ class Listeners {
// If we're done seeking and it was playing, resume playback // If we're done seeking and it was playing, resume playback
if (play && done) { if (play && done) {
seek.removeAttribute('play-on-seeked'); seek.removeAttribute(attribute);
this.player.play(); player.play();
} else if (!done && this.player.playing) { } else if (!done && player.playing) {
seek.setAttribute('play-on-seeked', ''); seek.setAttribute(attribute, '');
this.player.pause(); player.pause();
} }
}, },
); );
// Seek // Seek
this.bind( this.bind(
this.player.elements.inputs.seek, player.elements.inputs.seek,
inputEvent, inputEvent,
event => { event => {
const seek = event.currentTarget; const seek = event.currentTarget;
@ -765,8 +777,13 @@ class Listeners {
seek.removeAttribute('seek-value'); seek.removeAttribute('seek-value');
this.player.currentTime = // Super weird iOS bug where after you interact with an <input type="range">,
seekTo / seek.max * this.player.duration; // it takes over further interactions on the page. This is a hack
if (browser.isIos) {
repaint(seek);
}
player.currentTime = seekTo / seek.max * player.duration;
}, },
'seek', 'seek',
); );
@ -774,65 +791,61 @@ class Listeners {
// Current time invert // Current time invert
// Only if one time element is used for both currentTime and duration // Only if one time element is used for both currentTime and duration
if ( if (
this.player.config.toggleInvert && player.config.toggleInvert &&
!is.element(this.player.elements.display.duration) !is.element(player.elements.display.duration)
) { ) {
this.bind(this.player.elements.display.currentTime, 'click', () => { this.bind(player.elements.display.currentTime, 'click', () => {
// Do nothing if we're at the start // Do nothing if we're at the start
if (this.player.currentTime === 0) { if (player.currentTime === 0) {
return; return;
} }
this.player.config.invertTime = !this.player.config.invertTime; player.config.invertTime = !player.config.invertTime;
controls.timeUpdate.call(this.player); controls.timeUpdate.call(player);
}); });
} }
// Volume // Volume
this.bind( this.bind(
this.player.elements.inputs.volume, player.elements.inputs.volume,
inputEvent, inputEvent,
event => { event => {
this.player.volume = event.target.value; player.volume = event.target.value;
}, },
'volume', 'volume',
); );
// Polyfill for lower fill in <input type="range"> for webkit // Polyfill for lower fill in <input type="range"> for webkit
if (browser.isWebkit) { if (browser.isWebkit) {
Array.from( Array.from(getElements.call(player, 'input[type="range"]')).forEach(
getElements.call(this.player, 'input[type="range"]'), element => {
).forEach(element => { this.bind(element, 'input', event =>
this.bind(element, 'input', event => controls.updateRangeFill.call(player, event.target),
controls.updateRangeFill.call(this.player, event.target), );
); },
}); );
} }
// Seek tooltip // Seek tooltip
this.bind( this.bind(
this.player.elements.progress, player.elements.progress,
'mouseenter mouseleave mousemove', 'mouseenter mouseleave mousemove',
event => controls.updateSeekTooltip.call(this.player, event), event => controls.updateSeekTooltip.call(player, event),
); );
// Update controls.hover state (used for ui.toggleControls to avoid hiding when interacting) // Update controls.hover state (used for ui.toggleControls to avoid hiding when interacting)
this.bind( this.bind(player.elements.controls, 'mouseenter mouseleave', event => {
this.player.elements.controls, player.elements.controls.hover =
'mouseenter mouseleave', !player.touch && event.type === 'mouseenter';
event => { });
this.player.elements.controls.hover =
!this.player.touch && event.type === 'mouseenter';
},
);
// Update controls.pressed state (used for ui.toggleControls to avoid hiding when interacting) // Update controls.pressed state (used for ui.toggleControls to avoid hiding when interacting)
this.bind( this.bind(
this.player.elements.controls, player.elements.controls,
'mousedown mouseup touchstart touchend touchcancel', 'mousedown mouseup touchstart touchend touchcancel',
event => { event => {
this.player.elements.controls.pressed = [ player.elements.controls.pressed = [
'mousedown', 'mousedown',
'touchstart', 'touchstart',
].includes(event.type); ].includes(event.type);
@ -840,21 +853,22 @@ class Listeners {
); );
// Focus in/out on controls // Focus in/out on controls
this.bind(this.player.elements.controls, 'focusin focusout', event => { this.bind(player.elements.controls, 'focusin focusout', event => {
const { config, elements, timers } = this.player; const { config, elements, timers } = player;
const isFocusIn = event.type === 'focusin';
// Skip transition to prevent focus from scrolling the parent element // Skip transition to prevent focus from scrolling the parent element
toggleClass( toggleClass(
elements.controls, elements.controls,
config.classNames.noTransition, config.classNames.noTransition,
event.type === 'focusin', isFocusIn,
); );
// Toggle // Toggle
ui.toggleControls.call(this.player, event.type === 'focusin'); ui.toggleControls.call(player, isFocusIn);
// If focusin, hide again after delay // If focusin, hide again after delay
if (event.type === 'focusin') { if (isFocusIn) {
// Restore transition // Restore transition
setTimeout(() => { setTimeout(() => {
toggleClass( toggleClass(
@ -872,7 +886,7 @@ class Listeners {
// Hide // Hide
timers.controls = setTimeout( timers.controls = setTimeout(
() => ui.toggleControls.call(this.player, false), () => ui.toggleControls.call(player, false),
delay, delay,
); );
} }
@ -880,7 +894,7 @@ class Listeners {
// Mouse wheel for volume // Mouse wheel for volume
this.bind( this.bind(
this.player.elements.inputs.volume, player.elements.inputs.volume,
'wheel', 'wheel',
event => { event => {
// Detect "natural" scroll - suppored on OS X Safari only // Detect "natural" scroll - suppored on OS X Safari only
@ -896,10 +910,10 @@ class Listeners {
const direction = Math.sign(Math.abs(x) > Math.abs(y) ? x : y); const direction = Math.sign(Math.abs(x) > Math.abs(y) ? x : y);
// Change the volume by 2% // Change the volume by 2%
this.player.increaseVolume(direction / 50); player.increaseVolume(direction / 50);
// Don't break page scrolling at max and min // Don't break page scrolling at max and min
const { volume } = this.player.media; const { volume } = player.media;
if ( if (
(direction === 1 && volume < 1) || (direction === 1 && volume < 1) ||
(direction === -1 && volume > 0) (direction === -1 && volume > 0)

View File

@ -52,7 +52,11 @@ class Plyr {
} }
// jQuery, NodeList or Array passed, use first element // jQuery, NodeList or Array passed, use first element
if ((window.jQuery && this.media instanceof jQuery) || is.nodeList(this.media) || is.array(this.media)) { if (
(window.jQuery && this.media instanceof jQuery) ||
is.nodeList(this.media) ||
is.array(this.media)
) {
// eslint-disable-next-line // eslint-disable-next-line
this.media = this.media[0]; this.media = this.media[0];
} }
@ -65,7 +69,9 @@ class Plyr {
options || {}, options || {},
(() => { (() => {
try { try {
return JSON.parse(this.media.getAttribute('data-plyr-config')); return JSON.parse(
this.media.getAttribute('data-plyr-config'),
);
} catch (e) { } catch (e) {
return {}; return {};
} }
@ -185,21 +191,30 @@ class Plyr {
// TODO: replace fullscreen.iosNative with this playsinline config option // TODO: replace fullscreen.iosNative with this playsinline config option
// YouTube requires the playsinline in the URL // YouTube requires the playsinline in the URL
if (this.isYouTube) { if (this.isYouTube) {
this.config.playsinline = truthy.includes(url.searchParams.get('playsinline')); this.config.playsinline = truthy.includes(
url.searchParams.get('playsinline'),
);
} else { } else {
this.config.playsinline = true; this.config.playsinline = true;
} }
} }
} else { } else {
// <div> with attributes // <div> with attributes
this.provider = this.media.getAttribute(this.config.attributes.embed.provider); this.provider = this.media.getAttribute(
this.config.attributes.embed.provider,
);
// Remove attribute // Remove attribute
this.media.removeAttribute(this.config.attributes.embed.provider); this.media.removeAttribute(
this.config.attributes.embed.provider,
);
} }
// Unsupported or missing provider // Unsupported or missing provider
if (is.empty(this.provider) || !Object.keys(providers).includes(this.provider)) { if (
is.empty(this.provider) ||
!Object.keys(providers).includes(this.provider)
) {
this.debug.error('Setup failed: Invalid provider'); this.debug.error('Setup failed: Invalid provider');
return; return;
} }
@ -221,7 +236,10 @@ class Plyr {
if (this.media.hasAttribute('autoplay')) { if (this.media.hasAttribute('autoplay')) {
this.config.autoplay = true; this.config.autoplay = true;
} }
if (this.media.hasAttribute('playsinline')) { if (
this.media.hasAttribute('playsinline') ||
this.media.hasAttribute('webkit-playsinline')
) {
this.config.playsinline = true; this.config.playsinline = true;
} }
if (this.media.hasAttribute('muted')) { if (this.media.hasAttribute('muted')) {
@ -239,7 +257,11 @@ class Plyr {
} }
// Check for support again but with type // Check for support again but with type
this.supported = support.check(this.type, this.provider, this.config.playsinline); this.supported = support.check(
this.type,
this.provider,
this.config.playsinline,
);
// If no support for even API, bail // If no support for even API, bail
if (!this.supported.api) { if (!this.supported.api) {
@ -272,9 +294,14 @@ class Plyr {
// Listen for events if debugging // Listen for events if debugging
if (this.config.debug) { if (this.config.debug) {
on.call(this, this.elements.container, this.config.events.join(' '), event => { on.call(
this.debug.log(`event: ${event.type}`); this,
}); this.elements.container,
this.config.events.join(' '),
event => {
this.debug.log(`event: ${event.type}`);
},
);
} }
// Setup interface // Setup interface
@ -293,7 +320,9 @@ class Plyr {
this.fullscreen = new Fullscreen(this); this.fullscreen = new Fullscreen(this);
// Setup ads if provided // Setup ads if provided
this.ads = new Ads(this); if (this.config.ads.enabled) {
this.ads = new Ads(this);
}
// Autoplay if required // Autoplay if required
if (this.config.autoplay) { if (this.config.autoplay) {
@ -422,7 +451,9 @@ 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 =
this.currentTime -
(is.number(seekTime) ? seekTime : this.config.seekTime);
} }
/** /**
@ -430,7 +461,9 @@ 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 =
this.currentTime +
(is.number(seekTime) ? seekTime : this.config.seekTime);
} }
/** /**
@ -447,7 +480,9 @@ class Plyr {
const inputIsValid = is.number(input) && input > 0; const inputIsValid = is.number(input) && input > 0;
// Set // Set
this.media.currentTime = inputIsValid ? Math.min(input, this.duration) : 0; this.media.currentTime = inputIsValid
? Math.min(input, this.duration)
: 0;
// Logging // Logging
this.debug.log(`Seeking to ${this.currentTime} seconds`); this.debug.log(`Seeking to ${this.currentTime} seconds`);
@ -497,7 +532,10 @@ class Plyr {
// Media duration can be NaN or Infinity before the media has loaded // Media duration can be NaN or Infinity before the media has loaded
const realDuration = (this.media || {}).duration; const realDuration = (this.media || {}).duration;
const duration = !is.number(realDuration) || realDuration === Infinity ? 0 : realDuration; const duration =
!is.number(realDuration) || realDuration === Infinity
? 0
: realDuration;
// If config duration is funky, use regular duration // If config duration is funky, use regular duration
return fauxDuration || duration; return fauxDuration || duration;
@ -691,12 +729,16 @@ class Plyr {
if (!options.includes(quality)) { if (!options.includes(quality)) {
const value = closest(options, quality); const value = closest(options, quality);
this.debug.warn(`Unsupported quality option: ${quality}, using ${value} instead`); this.debug.warn(
`Unsupported quality option: ${quality}, using ${value} instead`,
);
quality = value; quality = value;
} }
// Trigger request event // Trigger request event
triggerEvent.call(this, this.media, 'qualityrequested', false, { quality }); triggerEvent.call(this, this.media, 'qualityrequested', false, {
quality,
});
// Update config // Update config
config.selected = quality; config.selected = quality;
@ -888,7 +930,9 @@ class Plyr {
const toggle = is.boolean(input) ? input : this.pip === states.inline; const toggle = is.boolean(input) ? input : this.pip === states.inline;
// Toggle based on current state // Toggle based on current state
this.media.webkitSetPresentationMode(toggle ? states.pip : states.inline); this.media.webkitSetPresentationMode(
toggle ? states.pip : states.inline,
);
} }
/** /**
@ -921,25 +965,39 @@ class Plyr {
// Don't toggle if missing UI support or if it's audio // Don't toggle if missing UI support or if it's audio
if (this.supported.ui && !this.isAudio) { if (this.supported.ui && !this.isAudio) {
// Get state before change // Get state before change
const isHidden = hasClass(this.elements.container, this.config.classNames.hideControls); const isHidden = hasClass(
this.elements.container,
this.config.classNames.hideControls,
);
// Negate the argument if not undefined since adding the class to hides the controls // Negate the argument if not undefined since adding the class to hides the controls
const force = typeof toggle === 'undefined' ? undefined : !toggle; const force = typeof toggle === 'undefined' ? undefined : !toggle;
// Apply and get updated state // Apply and get updated state
const hiding = toggleClass(this.elements.container, this.config.classNames.hideControls, force); const hiding = toggleClass(
this.elements.container,
this.config.classNames.hideControls,
force,
);
// Close menu // Close menu
if (hiding && this.config.controls.includes('settings') && !is.empty(this.config.settings)) { if (
hiding &&
this.config.controls.includes('settings') &&
!is.empty(this.config.settings)
) {
controls.toggleMenu.call(this, false); controls.toggleMenu.call(this, false);
} }
// Trigger event on change // Trigger event on change
if (hiding !== isHidden) { if (hiding !== isHidden) {
const eventName = hiding ? 'controlshidden' : 'controlsshown'; const eventName = hiding ? 'controlshidden' : 'controlsshown';
triggerEvent.call(this, this.media, eventName); triggerEvent.call(this, this.media, eventName);
} }
return !hiding; return !hiding;
} }
return false; return false;
} }
@ -1017,7 +1075,12 @@ class Plyr {
replaceElement(this.elements.original, this.elements.container); replaceElement(this.elements.original, this.elements.container);
// Event // Event
triggerEvent.call(this, this.elements.original, 'destroyed', true); triggerEvent.call(
this,
this.elements.original,
'destroyed',
true,
);
// Callback // Callback
if (is.function(callback)) { if (is.function(callback)) {

View File

@ -25,9 +25,13 @@ const support = {
// Check for support // Check for support
// Basic functionality vs full UI // Basic functionality vs full UI
check(type, provider, playsinline) { check(type, provider, playsinline) {
const canPlayInline = browser.isIPhone && playsinline && support.playsinline; const canPlayInline =
browser.isIPhone && playsinline && support.playsinline;
const api = support[type] || provider !== 'html5'; const api = support[type] || provider !== 'html5';
const ui = api && support.rangeInput && (type !== 'video' || !browser.isIPhone || canPlayInline); const ui =
api &&
support.rangeInput &&
(type !== 'video' || !browser.isIPhone || canPlayInline);
return { return {
api, api,
@ -37,7 +41,9 @@ const support = {
// Picture-in-picture support // Picture-in-picture support
// Safari only currently // Safari only currently
pip: (() => !browser.isIPhone && is.function(createElement('video').webkitSetPresentationMode))(), pip: (() =>
!browser.isIPhone &&
is.function(createElement('video').webkitSetPresentationMode))(),
// Airplay support // Airplay support
// Safari only currently // Safari only currently
@ -69,7 +75,9 @@ const support = {
} }
try { try {
return Boolean(type && this.media.canPlayType(type).replace(/no/, '')); return Boolean(
type && this.media.canPlayType(type).replace(/no/, ''),
);
} catch (err) { } catch (err) {
return false; return false;
} }
@ -94,7 +102,9 @@ const support = {
// Reduced motion iOS & MacOS setting // Reduced motion iOS & MacOS setting
// https://webkit.org/blog/7551/responsive-design-for-motion/ // https://webkit.org/blog/7551/responsive-design-for-motion/
reducedMotion: 'matchMedia' in window && window.matchMedia('(prefers-reduced-motion)').matches, reducedMotion:
'matchMedia' in window &&
window.matchMedia('(prefers-reduced-motion)').matches,
}; };
export default support; export default support;

View File

@ -14,8 +14,16 @@ import loadImage from './utils/loadImage';
const ui = { const ui = {
addStyleHook() { addStyleHook() {
toggleClass(this.elements.container, this.config.selectors.container.replace('.', ''), true); toggleClass(
toggleClass(this.elements.container, this.config.classNames.uiSupported, this.supported.ui); this.elements.container,
this.config.selectors.container.replace('.', ''),
true,
);
toggleClass(
this.elements.container,
this.config.classNames.uiSupported,
this.supported.ui,
);
}, },
// Toggle native HTML5 media controls // Toggle native HTML5 media controls
@ -35,7 +43,9 @@ const ui = {
// Don't setup interface if no support // Don't setup interface if no support
if (!this.supported.ui) { if (!this.supported.ui) {
this.debug.warn(`Basic support only for ${this.provider} ${this.type}`); this.debug.warn(
`Basic support only for ${this.provider} ${this.type}`,
);
// Restore native controls // Restore native controls
ui.toggleNativeControls.call(this, true); ui.toggleNativeControls.call(this, true);
@ -93,13 +103,25 @@ const ui = {
); );
// Check for airplay support // Check for airplay support
toggleClass(this.elements.container, this.config.classNames.airplay.supported, support.airplay && this.isHTML5); toggleClass(
this.elements.container,
this.config.classNames.airplay.supported,
support.airplay && this.isHTML5,
);
// Add iOS class // Add iOS class
toggleClass(this.elements.container, this.config.classNames.isIos, browser.isIos); toggleClass(
this.elements.container,
this.config.classNames.isIos,
browser.isIos,
);
// Add touch class // Add touch class
toggleClass(this.elements.container, this.config.classNames.isTouch, this.touch); toggleClass(
this.elements.container,
this.config.classNames.isTouch,
this.touch,
);
// Ready for API calls // Ready for API calls
this.ready = true; this.ready = true;
@ -149,7 +171,9 @@ const ui = {
} }
// Default to media type // Default to media type
const title = !is.empty(this.config.title) ? this.config.title : 'video'; const title = !is.empty(this.config.title)
? this.config.title
: 'video';
const format = i18n.get('frameTitle', this.config); const format = i18n.get('frameTitle', this.config);
iframe.setAttribute('title', format.replace('{title}', title)); iframe.setAttribute('title', format.replace('{title}', title));
@ -158,7 +182,11 @@ const ui = {
// Toggle poster // Toggle poster
togglePoster(enable) { togglePoster(enable) {
toggleClass(this.elements.container, this.config.classNames.posterEnabled, enable); toggleClass(
this.elements.container,
this.config.classNames.posterEnabled,
enable,
);
}, },
// Set the poster image (async) // Set the poster image (async)
@ -189,7 +217,9 @@ const ui = {
.then(() => { .then(() => {
// Prevent race conditions // Prevent race conditions
if (poster !== this.poster) { if (poster !== this.poster) {
throw new Error('setPoster cancelled by later call to setPoster'); throw new Error(
'setPoster cancelled by later call to setPoster',
);
} }
}) })
.then(() => { .then(() => {
@ -207,9 +237,21 @@ const ui = {
// Check playing state // Check playing state
checkPlaying(event) { checkPlaying(event) {
// Class hooks // Class hooks
toggleClass(this.elements.container, this.config.classNames.playing, this.playing); toggleClass(
toggleClass(this.elements.container, this.config.classNames.paused, this.paused); this.elements.container,
toggleClass(this.elements.container, this.config.classNames.stopped, this.stopped); this.config.classNames.playing,
this.playing,
);
toggleClass(
this.elements.container,
this.config.classNames.paused,
this.paused,
);
toggleClass(
this.elements.container,
this.config.classNames.stopped,
this.stopped,
);
// Set state // Set state
Array.from(this.elements.buttons.play || []).forEach(target => { Array.from(this.elements.buttons.play || []).forEach(target => {
@ -235,7 +277,11 @@ const ui = {
// Timer to prevent flicker when seeking // Timer to prevent flicker when seeking
this.timers.loading = setTimeout(() => { this.timers.loading = setTimeout(() => {
// Update progress bar loading class state // Update progress bar loading class state
toggleClass(this.elements.container, this.config.classNames.loading, this.loading); toggleClass(
this.elements.container,
this.config.classNames.loading,
this.loading,
);
// Update controls visibility // Update controls visibility
ui.toggleControls.call(this); ui.toggleControls.call(this);
@ -248,7 +294,15 @@ const ui = {
if (controls && this.config.hideControls) { if (controls && this.config.hideControls) {
// Show controls if force, loading, paused, or button interaction, otherwise hide // Show controls if force, loading, paused, or button interaction, otherwise hide
this.toggleControls(Boolean(force || this.loading || this.paused || controls.pressed || controls.hover)); this.toggleControls(
Boolean(
force ||
this.loading ||
this.paused ||
controls.pressed ||
controls.hover,
),
);
} }
}, },
}; };

View File

@ -15,7 +15,9 @@ export const transitionEndEvent = (() => {
transition: 'transitionend', transition: 'transitionend',
}; };
const type = Object.keys(events).find(event => element.style[event] !== undefined); const type = Object.keys(events).find(
event => element.style[event] !== undefined,
);
return is.string(type) ? events[type] : false; return is.string(type) ? events[type] : false;
})(); })();
@ -23,8 +25,12 @@ export const transitionEndEvent = (() => {
// Force repaint of element // Force repaint of element
export function repaint(element) { export function repaint(element) {
setTimeout(() => { setTimeout(() => {
toggleHidden(element, true); try {
element.offsetHeight; // eslint-disable-line toggleHidden(element, true);
toggleHidden(element, false); element.offsetHeight; // eslint-disable-line
toggleHidden(element, false);
} catch (e) {
// Do nothing
}
}, 0); }, 0);
} }

View File

@ -7,17 +7,16 @@
background-position: 50% 50%; background-position: 50% 50%;
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: contain; background-size: contain;
display: none;
height: 100%; height: 100%;
left: 0; left: 0;
opacity: 0; pointer-events: none;
position: absolute; position: absolute;
top: 0; top: 0;
transition: opacity 0.3s ease;
width: 100%; width: 100%;
z-index: 1; z-index: 1;
} }
.plyr--stopped.plyr__poster-enabled .plyr__poster { .plyr--stopped.plyr__poster-enabled .plyr__poster {
opacity: 1; display: block;
pointer-events: none;
} }

View File

@ -3,7 +3,6 @@
// -------------------------------------------------------------- // --------------------------------------------------------------
.plyr__progress { .plyr__progress {
display: flex;
flex: 1; flex: 1;
left: $plyr-range-thumb-height / 2; left: $plyr-range-thumb-height / 2;
margin-right: $plyr-range-thumb-height; margin-right: $plyr-range-thumb-height;

View File

@ -36,7 +36,6 @@
border: 0; border: 0;
border-radius: 100%; border-radius: 100%;
box-shadow: $plyr-range-thumb-shadow; box-shadow: $plyr-range-thumb-shadow;
box-sizing: border-box;
height: $plyr-range-thumb-height; height: $plyr-range-thumb-height;
position: relative; position: relative;
transition: all 0.2s ease; transition: all 0.2s ease;