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
},
tooltips: {
controls: true
controls: false,
seek: false
},
clickToPlay: false,
// clickToPlay: false,
/* controls: [
'play-large',
'restart',
@ -4207,7 +4208,7 @@ typeof navigator === "object" && (function () {
google: 'AIzaSyDrNwtN3nLH_8rjCmu5Wq3ZCm4MNAVdc0c'
},
ads: {
enabled: true,
// enabled: true,
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,
},
tooltips: {
controls: true,
controls: false,
seek: false,
},
clickToPlay: false,
// clickToPlay: false,
/* controls: [
'play-large',
'restart',
@ -129,7 +130,7 @@ import Raven from 'raven-js';
google: 'AIzaSyDrNwtN3nLH_8rjCmu5Wq3ZCm4MNAVdc0c',
},
ads: {
enabled: true,
// enabled: true,
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
function repaint(element) {
setTimeout(function () {
toggleHidden(element, true);
element.offsetHeight; // eslint-disable-line
toggleHidden(element, false);
try {
toggleHidden(element, true);
element.offsetHeight; // eslint-disable-line
toggleHidden(element, false);
} catch (e) {
// Do nothing
}
}, 0);
}
@ -1697,7 +1701,7 @@ typeof navigator === "object" && (function (global, factory) {
// Show the respective menu
if (!isRadioButton && [32, 39].includes(event.which)) {
controls.showMenuPanel.call(_this, type);
controls.showMenuPanel.call(_this, type, true);
} else {
var target = void 0;
@ -1804,7 +1808,7 @@ typeof navigator === "object" && (function (global, factory) {
break;
}
controls.showMenuPanel.call(_this2, 'home');
controls.showMenuPanel.call(_this2, 'home', event.type === 'keydown');
}, type, false);
controls.bindMenuItemShortcuts.call(this, menuItem, type);
@ -2473,6 +2477,7 @@ typeof navigator === "object" && (function (global, factory) {
var _this8 = this;
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);
@ -2527,7 +2532,7 @@ typeof navigator === "object" && (function (global, factory) {
// Focus the first item
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
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));
@ -2737,12 +2742,12 @@ typeof navigator === "object" && (function (global, factory) {
event.stopPropagation();
// Show the respective menu
controls.showMenuPanel.call(_this9, 'home');
controls.showMenuPanel.call(_this9, 'home', true);
}, false);
// Go back via button click
on(backButton, 'click', function () {
controls.showMenuPanel.call(_this9, 'home');
controls.showMenuPanel.call(_this9, 'home', false);
});
// Add to pane
@ -4319,7 +4324,7 @@ typeof navigator === "object" && (function (global, factory) {
createClass(Listeners, [{
key: 'handleKey',
value: function handleKey(event) {
var _this = this;
var player = this.player;
var code = event.keyCode ? event.keyCode : event.which;
var pressed = event.type === 'keydown';
@ -4339,7 +4344,7 @@ typeof navigator === "object" && (function (global, factory) {
// Seek by the number keys
var seekByKey = function seekByKey() {
// 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
@ -4350,8 +4355,8 @@ typeof navigator === "object" && (function (global, factory) {
// and any that accept key input http://webaim.org/techniques/keyboard/
var focused = document.activeElement;
if (is.element(focused)) {
var editable = this.player.config.selectors.editable;
var seek = this.player.elements.inputs.seek;
var editable = player.config.selectors.editable;
var seek = player.elements.inputs.seek;
if (focused !== seek && matches(focused, editable)) {
@ -4393,52 +4398,52 @@ typeof navigator === "object" && (function (global, factory) {
case 75:
// Space and K key
if (!repeat) {
this.player.togglePlay();
player.togglePlay();
}
break;
case 38:
// Arrow up
this.player.increaseVolume(0.1);
player.increaseVolume(0.1);
break;
case 40:
// Arrow down
this.player.decreaseVolume(0.1);
player.decreaseVolume(0.1);
break;
case 77:
// M key
if (!repeat) {
this.player.muted = !this.player.muted;
player.muted = !player.muted;
}
break;
case 39:
// Arrow forward
this.player.forward();
player.forward();
break;
case 37:
// Arrow back
this.player.rewind();
player.rewind();
break;
case 70:
// F key
this.player.fullscreen.toggle();
player.fullscreen.toggle();
break;
case 67:
// C key
if (!repeat) {
this.player.toggleCaptions();
player.toggleCaptions();
}
break;
case 76:
// L key
this.player.loop = !this.player.loop;
player.loop = !player.loop;
break;
/* case 73:
@ -4457,8 +4462,8 @@ typeof navigator === "object" && (function (global, factory) {
// Escape is handle natively when in full screen
// So we only need to worry about non native
if (!this.player.fullscreen.enabled && this.player.fullscreen.active && code === 27) {
this.player.fullscreen.toggle();
if (!player.fullscreen.enabled && player.fullscreen.active && code === 27) {
player.fullscreen.toggle();
}
// Store last code for next cycle
@ -4481,15 +4486,19 @@ typeof navigator === "object" && (function (global, factory) {
}, {
key: 'firstTouch',
value: function firstTouch() {
this.player.touch = true;
var player = this.player;
player.touch = true;
// 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',
value: function setTabFocus(event) {
var _this2 = this;
var player = this.player;
clearTimeout(this.focusTimer);
@ -4505,8 +4514,8 @@ typeof navigator === "object" && (function (global, factory) {
// Remove current classes
var removeCurrent = function removeCurrent() {
var className = _this2.player.config.classNames.tabFocus;
var current = getElements.call(_this2.player, '.' + className);
var className = player.config.classNames.tabFocus;
var current = getElements.call(player, '.' + className);
toggleClass(current, className, false);
};
@ -4523,16 +4532,15 @@ typeof navigator === "object" && (function (global, factory) {
// Delay the adding of classname until the focus has changed
// This event fires before the focusin event
this.focusTimer = setTimeout(function () {
var focused = document.activeElement;
// Ignore if current focus element isn't inside the player
if (!_this2.player.elements.container.contains(focused)) {
if (!player.elements.container.contains(focused)) {
return;
}
toggleClass(document.activeElement, _this2.player.config.classNames.tabFocus, true);
toggleClass(document.activeElement, player.config.classNames.tabFocus, true);
}, 10);
}
@ -4542,20 +4550,22 @@ typeof navigator === "object" && (function (global, factory) {
key: 'global',
value: function global() {
var toggle = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
var player = this.player;
// 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
toggleListener.call(this.player, document.body, 'click', this.toggleMenu, toggle);
toggleListener.call(player, document.body, 'click', this.toggleMenu, toggle);
// Detect touch by events
once.call(this.player, document.body, 'touchstart', this.firstTouch);
once.call(player, document.body, 'touchstart', this.firstTouch);
// 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
@ -4563,16 +4573,17 @@ typeof navigator === "object" && (function (global, factory) {
}, {
key: 'container',
value: function container() {
var _this3 = this;
var player = this.player;
// 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
on.call(this.player, this.player.elements.container, 'mousemove mouseleave touchstart touchmove enterfullscreen exitfullscreen', function (event) {
var controls$$1 = _this3.player.elements.controls;
on.call(player, player.elements.container, 'mousemove mouseleave touchstart touchmove enterfullscreen exitfullscreen', function (event) {
var controls$$1 = player.elements.controls;
// Remove button states for fullscreen
@ -4587,17 +4598,17 @@ typeof navigator === "object" && (function (global, factory) {
var delay = 0;
if (show) {
ui.toggleControls.call(_this3.player, true);
ui.toggleControls.call(player, true);
// Use longer timeout for touch devices
delay = _this3.player.touch ? 3000 : 2000;
delay = player.touch ? 3000 : 2000;
}
// Clear timer
clearTimeout(_this3.player.timers.controls);
clearTimeout(player.timers.controls);
// Set new timer to prevent flicker when seeking
_this3.player.timers.controls = setTimeout(function () {
return ui.toggleControls.call(_this3.player, false);
player.timers.controls = setTimeout(function () {
return ui.toggleControls.call(player, false);
}, delay);
});
}
@ -4607,76 +4618,77 @@ typeof navigator === "object" && (function (global, factory) {
}, {
key: 'media',
value: function media() {
var _this4 = this;
var player = this.player;
// 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
on.call(this.player, this.player.media, 'durationchange loadeddata loadedmetadata', function (event) {
return controls.durationUpdate.call(_this4.player, event);
on.call(player, player.media, 'durationchange loadeddata loadedmetadata', function (event) {
return controls.durationUpdate.call(player, event);
});
// Check for audio tracks on load
// We can't use `loadedmetadata` as it doesn't seem to have audio tracks at that point
on.call(this.player, this.player.media, 'canplay', function () {
toggleHidden(_this4.player.elements.volume, !_this4.player.hasAudio);
toggleHidden(_this4.player.elements.buttons.mute, !_this4.player.hasAudio);
on.call(player, player.media, 'canplay', function () {
toggleHidden(player.elements.volume, !player.hasAudio);
toggleHidden(player.elements.buttons.mute, !player.hasAudio);
});
// Handle the media finishing
on.call(this.player, this.player.media, 'ended', function () {
on.call(player, player.media, 'ended', function () {
// Show poster on end
if (_this4.player.isHTML5 && _this4.player.isVideo && _this4.player.config.resetOnEnd) {
if (player.isHTML5 && player.isVideo && player.config.resetOnEnd) {
// Restart
_this4.player.restart();
player.restart();
}
});
// Check for buffer progress
on.call(this.player, this.player.media, 'progress playing seeking seeked', function (event) {
return controls.updateProgress.call(_this4.player, event);
on.call(player, player.media, 'progress playing seeking seeked', function (event) {
return controls.updateProgress.call(player, event);
});
// Handle volume changes
on.call(this.player, this.player.media, 'volumechange', function (event) {
return controls.updateVolume.call(_this4.player, event);
on.call(player, player.media, 'volumechange', function (event) {
return controls.updateVolume.call(player, event);
});
// Handle play/pause
on.call(this.player, this.player.media, 'playing play pause ended emptied timeupdate', function (event) {
return ui.checkPlaying.call(_this4.player, event);
on.call(player, player.media, 'playing play pause ended emptied timeupdate', function (event) {
return ui.checkPlaying.call(player, event);
});
// Loading state
on.call(this.player, this.player.media, 'waiting canplay seeked playing', function (event) {
return ui.checkLoading.call(_this4.player, event);
on.call(player, player.media, 'waiting canplay seeked playing', function (event) {
return ui.checkLoading.call(player, event);
});
// 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
on.call(this.player, this.player.media, 'playing', function () {
if (!_this4.player.ads) {
on.call(player, player.media, 'playing', function () {
if (!player.ads) {
return;
}
// 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
_this4.player.ads.managerPromise.then(function () {
return _this4.player.ads.play();
player.ads.managerPromise.then(function () {
return player.ads.play();
}).catch(function () {
return _this4.player.play();
return player.play();
});
}
});
// 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
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)
if (!is.element(wrapper)) {
@ -4684,75 +4696,81 @@ typeof navigator === "object" && (function (global, factory) {
}
// On click play, pause ore restart
on.call(this.player, wrapper, 'click', function () {
// Touch devices will just show controls (if we're hiding controls)
if (_this4.player.config.hideControls && _this4.player.touch && !_this4.player.paused) {
on.call(player, player.elements.container, 'click touchstart', function (event) {
var targets = [player.elements.container, wrapper];
// Ignore if click if not container or in video wrapper
if (!targets.includes(event.target) && !wrapper.contains(event.target)) {
return;
}
if (_this4.player.paused) {
_this4.player.play();
} else if (_this4.player.ended) {
_this4.player.restart();
_this4.player.play();
// 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 {
_this4.player.pause();
player.togglePlay();
}
});
}
// Disable right click
if (this.player.supported.ui && this.player.config.disableContextMenu) {
on.call(this.player, this.player.elements.wrapper, 'contextmenu', function (event) {
if (player.supported.ui && player.config.disableContextMenu) {
on.call(player, player.elements.wrapper, 'contextmenu', function (event) {
event.preventDefault();
}, false);
}
// Volume change
on.call(this.player, this.player.media, 'volumechange', function () {
on.call(player, player.media, 'volumechange', function () {
// Save to storage
_this4.player.storage.set({
volume: _this4.player.volume,
muted: _this4.player.muted
player.storage.set({
volume: player.volume,
muted: player.muted
});
});
// Speed change
on.call(this.player, this.player.media, 'ratechange', function () {
on.call(player, player.media, 'ratechange', function () {
// Update UI
controls.updateSetting.call(_this4.player, 'speed');
controls.updateSetting.call(player, 'speed');
// Save to storage
_this4.player.storage.set({ speed: _this4.player.speed });
player.storage.set({ speed: player.speed });
});
// Quality request
on.call(this.player, this.player.media, 'qualityrequested', function (event) {
on.call(player, player.media, 'qualityrequested', function (event) {
// Save to storage
_this4.player.storage.set({ quality: event.detail.quality });
player.storage.set({ quality: event.detail.quality });
});
// Quality change
on.call(this.player, this.player.media, 'qualitychange', function (event) {
on.call(player, player.media, 'qualitychange', function (event) {
// 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
// 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,
detail = _event$detail === undefined ? {} : _event$detail;
// Get error details from media
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',
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 returned = true;
// Execute custom handler
if (hasCustomHandler) {
returned = customHandler.call(this.player, event);
returned = customHandler.call(player, event);
}
// Only call default handler if not prevented in custom handler
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',
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 player = this.player;
var customHandler = this.player.config.listeners[customHandlerKey];
var customHandler = player.config.listeners[customHandlerKey];
var hasCustomHandler = is.function(customHandler);
on.call(this.player, element, type, function (event) {
return _this5.proxy(event, defaultHandler, customHandlerKey);
on.call(player, element, type, function (event) {
return _this.proxy(event, defaultHandler, customHandlerKey);
}, passive && !hasCustomHandler);
}
@ -4798,57 +4819,60 @@ typeof navigator === "object" && (function (global, factory) {
}, {
key: 'controls',
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
var inputEvent = browser.isIE ? 'change' : 'input';
// Play/pause toggle
if (this.player.elements.buttons.play) {
Array.from(this.player.elements.buttons.play).forEach(function (button) {
_this6.bind(button, 'click', _this6.player.togglePlay, 'play');
if (player.elements.buttons.play) {
Array.from(player.elements.buttons.play).forEach(function (button) {
_this2.bind(button, 'click', player.togglePlay, 'play');
});
}
// Pause
this.bind(this.player.elements.buttons.restart, 'click', this.player.restart, 'restart');
this.bind(player.elements.buttons.restart, 'click', player.restart, 'restart');
// Rewind
this.bind(this.player.elements.buttons.rewind, 'click', this.player.rewind, 'rewind');
this.bind(player.elements.buttons.rewind, 'click', player.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
this.bind(this.player.elements.buttons.mute, 'click', function () {
_this6.player.muted = !_this6.player.muted;
this.bind(player.elements.buttons.mute, 'click', function () {
player.muted = !player.muted;
}, 'mute');
// Captions toggle
this.bind(this.player.elements.buttons.captions, 'click', function () {
return _this6.player.toggleCaptions();
this.bind(player.elements.buttons.captions, 'click', function () {
return player.toggleCaptions();
});
// Fullscreen toggle
this.bind(this.player.elements.buttons.fullscreen, 'click', function () {
_this6.player.fullscreen.toggle();
this.bind(player.elements.buttons.fullscreen, 'click', function () {
player.fullscreen.toggle();
}, 'fullscreen');
// Picture-in-Picture
this.bind(this.player.elements.buttons.pip, 'click', function () {
_this6.player.pip = 'toggle';
this.bind(player.elements.buttons.pip, 'click', function () {
player.pip = 'toggle';
}, 'pip');
// 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
this.bind(this.player.elements.buttons.settings, 'click', function (event) {
controls.toggleMenu.call(_this6.player, event);
this.bind(player.elements.buttons.settings, 'click', function (event) {
controls.toggleMenu.call(player, event);
});
// 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
if (event.which !== 32) {
return;
@ -4861,44 +4885,44 @@ typeof navigator === "object" && (function (global, factory) {
event.stopPropagation();
// Toggle menu
controls.toggleMenu.call(_this6.player, event);
controls.toggleMenu.call(player, event);
}, null, false);
// Set range input alternative "value", which matches the tooltip time (#954)
this.bind(this.player.elements.inputs.seek, 'mousedown mousemove', function (event) {
var clientRect = _this6.player.elements.progress.getBoundingClientRect();
var percent = 100 / clientRect.width * (event.pageX - clientRect.left);
this.bind(player.elements.inputs.seek, 'mousedown mousemove', function (event) {
var rect = player.elements.progress.getBoundingClientRect();
var percent = 100 / rect.width * (event.pageX - rect.left);
event.currentTarget.setAttribute('seek-value', percent);
});
// 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 code = event.keyCode ? event.keyCode : event.which;
var eventType = event.type;
var attribute = 'play-on-seeked';
if ((eventType === 'keydown' || eventType === 'keyup') && code !== 39 && code !== 37) {
return;
}
// Was playing before?
var play = seek.hasAttribute('play-on-seeked');
var play = seek.hasAttribute(attribute);
// Done seeking
var done = ['mouseup', 'touchend', 'keyup'].includes(event.type);
// If we're done seeking and it was playing, resume playback
if (play && done) {
seek.removeAttribute('play-on-seeked');
_this6.player.play();
} else if (!done && _this6.player.playing) {
seek.setAttribute('play-on-seeked', '');
_this6.player.pause();
seek.removeAttribute(attribute);
player.play();
} else if (!done && player.playing) {
seek.setAttribute(attribute, '');
player.pause();
}
});
// Seek
this.bind(this.player.elements.inputs.seek, inputEvent, function (event) {
this.bind(player.elements.inputs.seek, inputEvent, function (event) {
var seek = event.currentTarget;
// 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');
_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');
// Current time invert
// Only if one time element is used for both currentTime and duration
if (this.player.config.toggleInvert && !is.element(this.player.elements.display.duration)) {
this.bind(this.player.elements.display.currentTime, 'click', function () {
if (player.config.toggleInvert && !is.element(player.elements.display.duration)) {
this.bind(player.elements.display.currentTime, 'click', function () {
// Do nothing if we're at the start
if (_this6.player.currentTime === 0) {
if (player.currentTime === 0) {
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
this.bind(this.player.elements.inputs.volume, inputEvent, function (event) {
_this6.player.volume = event.target.value;
this.bind(player.elements.inputs.volume, inputEvent, function (event) {
player.volume = event.target.value;
}, 'volume');
// Polyfill for lower fill in <input type="range"> for webkit
if (browser.isWebkit) {
Array.from(getElements.call(this.player, 'input[type="range"]')).forEach(function (element) {
_this6.bind(element, 'input', function (event) {
return controls.updateRangeFill.call(_this6.player, event.target);
Array.from(getElements.call(player, 'input[type="range"]')).forEach(function (element) {
_this2.bind(element, 'input', function (event) {
return controls.updateRangeFill.call(player, event.target);
});
});
}
// Seek tooltip
this.bind(this.player.elements.progress, 'mouseenter mouseleave mousemove', function (event) {
return controls.updateSeekTooltip.call(_this6.player, event);
this.bind(player.elements.progress, 'mouseenter mouseleave mousemove', function (event) {
return controls.updateSeekTooltip.call(player, event);
});
// Update controls.hover state (used for ui.toggleControls to avoid hiding when interacting)
this.bind(this.player.elements.controls, 'mouseenter mouseleave', function (event) {
_this6.player.elements.controls.hover = !_this6.player.touch && event.type === 'mouseenter';
this.bind(player.elements.controls, 'mouseenter mouseleave', function (event) {
player.elements.controls.hover = !player.touch && event.type === 'mouseenter';
});
// 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) {
_this6.player.elements.controls.pressed = ['mousedown', 'touchstart'].includes(event.type);
this.bind(player.elements.controls, 'mousedown mouseup touchstart touchend touchcancel', function (event) {
player.elements.controls.pressed = ['mousedown', 'touchstart'].includes(event.type);
});
// Focus in/out on controls
this.bind(this.player.elements.controls, 'focusin focusout', function (event) {
var _player = _this6.player,
config = _player.config,
elements = _player.elements,
timers = _player.timers;
this.bind(player.elements.controls, 'focusin focusout', function (event) {
var config = player.config,
elements = player.elements,
timers = player.timers;
var isFocusIn = event.type === 'focusin';
// Skip transition to prevent focus from scrolling the parent element
toggleClass(elements.controls, config.classNames.noTransition, event.type === 'focusin');
toggleClass(elements.controls, config.classNames.noTransition, isFocusIn);
// Toggle
ui.toggleControls.call(_this6.player, event.type === 'focusin');
ui.toggleControls.call(player, isFocusIn);
// If focusin, hide again after delay
if (event.type === 'focusin') {
if (isFocusIn) {
// Restore transition
setTimeout(function () {
toggleClass(elements.controls, config.classNames.noTransition, false);
}, 0);
// Delay a little more for keyboard users
var delay = _this6.touch ? 3000 : 4000;
var delay = _this2.touch ? 3000 : 4000;
// Clear timer
clearTimeout(timers.controls);
// Hide
timers.controls = setTimeout(function () {
return ui.toggleControls.call(_this6.player, false);
return ui.toggleControls.call(player, false);
}, delay);
}
});
// 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
// Other browsers on OS X will be inverted until support improves
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);
// Change the volume by 2%
_this6.player.increaseVolume(direction / 50);
player.increaseVolume(direction / 50);
// 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) {
event.preventDefault();
@ -7310,7 +7340,7 @@ typeof navigator === "object" && (function (global, factory) {
if (this.media.hasAttribute('autoplay')) {
this.config.autoplay = true;
}
if (this.media.hasAttribute('playsinline')) {
if (this.media.hasAttribute('playsinline') || this.media.hasAttribute('webkit-playsinline')) {
this.config.playsinline = true;
}
if (this.media.hasAttribute('muted')) {
@ -7382,7 +7412,9 @@ typeof navigator === "object" && (function (global, factory) {
this.fullscreen = new Fullscreen(this);
// Setup ads if provided
this.ads = new Ads(this);
if (this.config.ads.enabled) {
this.ads = new Ads(this);
}
// Autoplay if required
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)) {
controls.toggleMenu.call(this, false);
}
// Trigger event on change
if (hiding !== isHidden) {
var eventName = hiding ? 'controlshidden' : 'controlsshown';
triggerEvent.call(this, this.media, eventName);
}
return !hiding;
}
return false;
}
@ -8084,7 +8119,9 @@ typeof navigator === "object" && (function (global, factory) {
}
// Trigger request event
triggerEvent.call(this, this.media, 'qualityrequested', false, { quality: quality });
triggerEvent.call(this, this.media, 'qualityrequested', false, {
quality: quality
});
// Update config
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
function repaint(element) {
setTimeout(function () {
toggleHidden(element, true);
element.offsetHeight; // eslint-disable-line
toggleHidden(element, false);
try {
toggleHidden(element, true);
element.offsetHeight; // eslint-disable-line
toggleHidden(element, false);
} catch (e) {
// Do nothing
}
}, 0);
}
@ -7083,7 +7087,7 @@ typeof navigator === "object" && (function (global, factory) {
// Show the respective menu
if (!isRadioButton && [32, 39].includes(event.which)) {
controls.showMenuPanel.call(_this, type);
controls.showMenuPanel.call(_this, type, true);
} else {
var target = void 0;
@ -7190,7 +7194,7 @@ typeof navigator === "object" && (function (global, factory) {
break;
}
controls.showMenuPanel.call(_this2, 'home');
controls.showMenuPanel.call(_this2, 'home', event.type === 'keydown');
}, type, false);
controls.bindMenuItemShortcuts.call(this, menuItem, type);
@ -7859,6 +7863,7 @@ typeof navigator === "object" && (function (global, factory) {
var _this8 = this;
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);
@ -7913,7 +7918,7 @@ typeof navigator === "object" && (function (global, factory) {
// Focus the first item
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
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));
@ -8123,12 +8128,12 @@ typeof navigator === "object" && (function (global, factory) {
event.stopPropagation();
// Show the respective menu
controls.showMenuPanel.call(_this9, 'home');
controls.showMenuPanel.call(_this9, 'home', true);
}, false);
// Go back via button click
on(backButton, 'click', function () {
controls.showMenuPanel.call(_this9, 'home');
controls.showMenuPanel.call(_this9, 'home', false);
});
// Add to pane
@ -9705,7 +9710,7 @@ typeof navigator === "object" && (function (global, factory) {
createClass(Listeners, [{
key: 'handleKey',
value: function handleKey(event) {
var _this = this;
var player = this.player;
var code = event.keyCode ? event.keyCode : event.which;
var pressed = event.type === 'keydown';
@ -9725,7 +9730,7 @@ typeof navigator === "object" && (function (global, factory) {
// Seek by the number keys
var seekByKey = function seekByKey() {
// 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
@ -9736,8 +9741,8 @@ typeof navigator === "object" && (function (global, factory) {
// and any that accept key input http://webaim.org/techniques/keyboard/
var focused = document.activeElement;
if (is$1.element(focused)) {
var editable = this.player.config.selectors.editable;
var seek = this.player.elements.inputs.seek;
var editable = player.config.selectors.editable;
var seek = player.elements.inputs.seek;
if (focused !== seek && matches(focused, editable)) {
@ -9779,52 +9784,52 @@ typeof navigator === "object" && (function (global, factory) {
case 75:
// Space and K key
if (!repeat) {
this.player.togglePlay();
player.togglePlay();
}
break;
case 38:
// Arrow up
this.player.increaseVolume(0.1);
player.increaseVolume(0.1);
break;
case 40:
// Arrow down
this.player.decreaseVolume(0.1);
player.decreaseVolume(0.1);
break;
case 77:
// M key
if (!repeat) {
this.player.muted = !this.player.muted;
player.muted = !player.muted;
}
break;
case 39:
// Arrow forward
this.player.forward();
player.forward();
break;
case 37:
// Arrow back
this.player.rewind();
player.rewind();
break;
case 70:
// F key
this.player.fullscreen.toggle();
player.fullscreen.toggle();
break;
case 67:
// C key
if (!repeat) {
this.player.toggleCaptions();
player.toggleCaptions();
}
break;
case 76:
// L key
this.player.loop = !this.player.loop;
player.loop = !player.loop;
break;
/* case 73:
@ -9843,8 +9848,8 @@ typeof navigator === "object" && (function (global, factory) {
// Escape is handle natively when in full screen
// So we only need to worry about non native
if (!this.player.fullscreen.enabled && this.player.fullscreen.active && code === 27) {
this.player.fullscreen.toggle();
if (!player.fullscreen.enabled && player.fullscreen.active && code === 27) {
player.fullscreen.toggle();
}
// Store last code for next cycle
@ -9867,15 +9872,19 @@ typeof navigator === "object" && (function (global, factory) {
}, {
key: 'firstTouch',
value: function firstTouch() {
this.player.touch = true;
var player = this.player;
player.touch = true;
// 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',
value: function setTabFocus(event) {
var _this2 = this;
var player = this.player;
clearTimeout(this.focusTimer);
@ -9891,8 +9900,8 @@ typeof navigator === "object" && (function (global, factory) {
// Remove current classes
var removeCurrent = function removeCurrent() {
var className = _this2.player.config.classNames.tabFocus;
var current = getElements.call(_this2.player, '.' + className);
var className = player.config.classNames.tabFocus;
var current = getElements.call(player, '.' + className);
toggleClass(current, className, false);
};
@ -9909,16 +9918,15 @@ typeof navigator === "object" && (function (global, factory) {
// Delay the adding of classname until the focus has changed
// This event fires before the focusin event
this.focusTimer = setTimeout(function () {
var focused = document.activeElement;
// Ignore if current focus element isn't inside the player
if (!_this2.player.elements.container.contains(focused)) {
if (!player.elements.container.contains(focused)) {
return;
}
toggleClass(document.activeElement, _this2.player.config.classNames.tabFocus, true);
toggleClass(document.activeElement, player.config.classNames.tabFocus, true);
}, 10);
}
@ -9928,20 +9936,22 @@ typeof navigator === "object" && (function (global, factory) {
key: 'global',
value: function global() {
var toggle = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
var player = this.player;
// 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
toggleListener.call(this.player, document.body, 'click', this.toggleMenu, toggle);
toggleListener.call(player, document.body, 'click', this.toggleMenu, toggle);
// Detect touch by events
once.call(this.player, document.body, 'touchstart', this.firstTouch);
once.call(player, document.body, 'touchstart', this.firstTouch);
// 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
@ -9949,16 +9959,17 @@ typeof navigator === "object" && (function (global, factory) {
}, {
key: 'container',
value: function container() {
var _this3 = this;
var player = this.player;
// 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
on.call(this.player, this.player.elements.container, 'mousemove mouseleave touchstart touchmove enterfullscreen exitfullscreen', function (event) {
var controls$$1 = _this3.player.elements.controls;
on.call(player, player.elements.container, 'mousemove mouseleave touchstart touchmove enterfullscreen exitfullscreen', function (event) {
var controls$$1 = player.elements.controls;
// Remove button states for fullscreen
@ -9973,17 +9984,17 @@ typeof navigator === "object" && (function (global, factory) {
var delay = 0;
if (show) {
ui.toggleControls.call(_this3.player, true);
ui.toggleControls.call(player, true);
// Use longer timeout for touch devices
delay = _this3.player.touch ? 3000 : 2000;
delay = player.touch ? 3000 : 2000;
}
// Clear timer
clearTimeout(_this3.player.timers.controls);
clearTimeout(player.timers.controls);
// Set new timer to prevent flicker when seeking
_this3.player.timers.controls = setTimeout(function () {
return ui.toggleControls.call(_this3.player, false);
player.timers.controls = setTimeout(function () {
return ui.toggleControls.call(player, false);
}, delay);
});
}
@ -9993,76 +10004,79 @@ typeof navigator === "object" && (function (global, factory) {
}, {
key: 'media',
value: function media() {
var _this4 = this;
var _this = this;
var player = this.player;
// 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
on.call(this.player, this.player.media, 'durationchange loadeddata loadedmetadata', function (event) {
return controls.durationUpdate.call(_this4.player, event);
on.call(player, player.media, 'durationchange loadeddata loadedmetadata', function (event) {
return controls.durationUpdate.call(player, event);
});
// Check for audio tracks on load
// We can't use `loadedmetadata` as it doesn't seem to have audio tracks at that point
on.call(this.player, this.player.media, 'canplay', function () {
toggleHidden(_this4.player.elements.volume, !_this4.player.hasAudio);
toggleHidden(_this4.player.elements.buttons.mute, !_this4.player.hasAudio);
on.call(player, player.media, 'canplay', function () {
toggleHidden(player.elements.volume, !player.hasAudio);
toggleHidden(player.elements.buttons.mute, !player.hasAudio);
});
// Handle the media finishing
on.call(this.player, this.player.media, 'ended', function () {
on.call(player, player.media, 'ended', function () {
// Show poster on end
if (_this4.player.isHTML5 && _this4.player.isVideo && _this4.player.config.resetOnEnd) {
if (player.isHTML5 && player.isVideo && player.config.resetOnEnd) {
// Restart
_this4.player.restart();
player.restart();
}
});
// Check for buffer progress
on.call(this.player, this.player.media, 'progress playing seeking seeked', function (event) {
return controls.updateProgress.call(_this4.player, event);
on.call(player, player.media, 'progress playing seeking seeked', function (event) {
return controls.updateProgress.call(player, event);
});
// Handle volume changes
on.call(this.player, this.player.media, 'volumechange', function (event) {
return controls.updateVolume.call(_this4.player, event);
on.call(player, player.media, 'volumechange', function (event) {
return controls.updateVolume.call(player, event);
});
// Handle play/pause
on.call(this.player, this.player.media, 'playing play pause ended emptied timeupdate', function (event) {
return ui.checkPlaying.call(_this4.player, event);
on.call(player, player.media, 'playing play pause ended emptied timeupdate', function (event) {
return ui.checkPlaying.call(player, event);
});
// Loading state
on.call(this.player, this.player.media, 'waiting canplay seeked playing', function (event) {
return ui.checkLoading.call(_this4.player, event);
on.call(player, player.media, 'waiting canplay seeked playing', function (event) {
return ui.checkLoading.call(player, event);
});
// 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
on.call(this.player, this.player.media, 'playing', function () {
if (!_this4.player.ads) {
on.call(player, player.media, 'playing', function () {
if (!player.ads) {
return;
}
// 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
_this4.player.ads.managerPromise.then(function () {
return _this4.player.ads.play();
player.ads.managerPromise.then(function () {
return player.ads.play();
}).catch(function () {
return _this4.player.play();
return player.play();
});
}
});
// 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
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)
if (!is$1.element(wrapper)) {
@ -10070,75 +10084,77 @@ typeof navigator === "object" && (function (global, factory) {
}
// 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)
if (_this4.player.config.hideControls && _this4.player.touch && !_this4.player.paused) {
if (player.config.hideControls && player.touch && controlsHidden) {
return;
}
if (_this4.player.paused) {
_this4.player.play();
} else if (_this4.player.ended) {
_this4.player.restart();
_this4.player.play();
if (player.ended) {
player.restart();
player.play();
} else {
_this4.player.pause();
_this.togglePlay();
}
});
}
// Disable right click
if (this.player.supported.ui && this.player.config.disableContextMenu) {
on.call(this.player, this.player.elements.wrapper, 'contextmenu', function (event) {
if (player.supported.ui && player.config.disableContextMenu) {
on.call(player, player.elements.wrapper, 'contextmenu', function (event) {
event.preventDefault();
}, false);
}
// Volume change
on.call(this.player, this.player.media, 'volumechange', function () {
on.call(player, player.media, 'volumechange', function () {
// Save to storage
_this4.player.storage.set({
volume: _this4.player.volume,
muted: _this4.player.muted
player.storage.set({
volume: player.volume,
muted: player.muted
});
});
// Speed change
on.call(this.player, this.player.media, 'ratechange', function () {
on.call(player, player.media, 'ratechange', function () {
// Update UI
controls.updateSetting.call(_this4.player, 'speed');
controls.updateSetting.call(player, 'speed');
// Save to storage
_this4.player.storage.set({ speed: _this4.player.speed });
player.storage.set({ speed: player.speed });
});
// Quality request
on.call(this.player, this.player.media, 'qualityrequested', function (event) {
on.call(player, player.media, 'qualityrequested', function (event) {
// Save to storage
_this4.player.storage.set({ quality: event.detail.quality });
player.storage.set({ quality: event.detail.quality });
});
// Quality change
on.call(this.player, this.player.media, 'qualitychange', function (event) {
on.call(player, player.media, 'qualitychange', function (event) {
// 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
// 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,
detail = _event$detail === undefined ? {} : _event$detail;
// Get error details from media
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',
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 returned = true;
// Execute custom handler
if (hasCustomHandler) {
returned = customHandler.call(this.player, event);
returned = customHandler.call(player, event);
}
// Only call default handler if not prevented in custom handler
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',
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 player = this.player;
var customHandler = this.player.config.listeners[customHandlerKey];
var customHandler = player.config.listeners[customHandlerKey];
var hasCustomHandler = is$1.function(customHandler);
on.call(this.player, element, type, function (event) {
return _this5.proxy(event, defaultHandler, customHandlerKey);
on.call(player, element, type, function (event) {
return _this2.proxy(event, defaultHandler, customHandlerKey);
}, passive && !hasCustomHandler);
}
@ -10184,57 +10203,60 @@ typeof navigator === "object" && (function (global, factory) {
}, {
key: 'controls',
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
var inputEvent = browser.isIE ? 'change' : 'input';
// Play/pause toggle
if (this.player.elements.buttons.play) {
Array.from(this.player.elements.buttons.play).forEach(function (button) {
_this6.bind(button, 'click', _this6.player.togglePlay, 'play');
if (player.elements.buttons.play) {
Array.from(player.elements.buttons.play).forEach(function (button) {
_this3.bind(button, 'click', player.togglePlay, 'play');
});
}
// Pause
this.bind(this.player.elements.buttons.restart, 'click', this.player.restart, 'restart');
this.bind(player.elements.buttons.restart, 'click', player.restart, 'restart');
// Rewind
this.bind(this.player.elements.buttons.rewind, 'click', this.player.rewind, 'rewind');
this.bind(player.elements.buttons.rewind, 'click', player.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
this.bind(this.player.elements.buttons.mute, 'click', function () {
_this6.player.muted = !_this6.player.muted;
this.bind(player.elements.buttons.mute, 'click', function () {
player.muted = !player.muted;
}, 'mute');
// Captions toggle
this.bind(this.player.elements.buttons.captions, 'click', function () {
return _this6.player.toggleCaptions();
this.bind(player.elements.buttons.captions, 'click', function () {
return player.toggleCaptions();
});
// Fullscreen toggle
this.bind(this.player.elements.buttons.fullscreen, 'click', function () {
_this6.player.fullscreen.toggle();
this.bind(player.elements.buttons.fullscreen, 'click', function () {
player.fullscreen.toggle();
}, 'fullscreen');
// Picture-in-Picture
this.bind(this.player.elements.buttons.pip, 'click', function () {
_this6.player.pip = 'toggle';
this.bind(player.elements.buttons.pip, 'click', function () {
player.pip = 'toggle';
}, 'pip');
// 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
this.bind(this.player.elements.buttons.settings, 'click', function (event) {
controls.toggleMenu.call(_this6.player, event);
this.bind(player.elements.buttons.settings, 'click', function (event) {
controls.toggleMenu.call(player, event);
});
// 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
if (event.which !== 32) {
return;
@ -10247,44 +10269,44 @@ typeof navigator === "object" && (function (global, factory) {
event.stopPropagation();
// Toggle menu
controls.toggleMenu.call(_this6.player, event);
controls.toggleMenu.call(player, event);
}, null, false);
// Set range input alternative "value", which matches the tooltip time (#954)
this.bind(this.player.elements.inputs.seek, 'mousedown mousemove', function (event) {
var clientRect = _this6.player.elements.progress.getBoundingClientRect();
this.bind(player.elements.inputs.seek, 'mousedown mousemove', function (event) {
var clientRect = player.elements.progress.getBoundingClientRect();
var percent = 100 / clientRect.width * (event.pageX - clientRect.left);
event.currentTarget.setAttribute('seek-value', percent);
});
// 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 code = event.keyCode ? event.keyCode : event.which;
var eventType = event.type;
var attribute = 'play-on-seeked';
if ((eventType === 'keydown' || eventType === 'keyup') && code !== 39 && code !== 37) {
return;
}
// Was playing before?
var play = seek.hasAttribute('play-on-seeked');
var play = seek.hasAttribute(attribute);
// Done seeking
var done = ['mouseup', 'touchend', 'keyup'].includes(event.type);
// If we're done seeking and it was playing, resume playback
if (play && done) {
seek.removeAttribute('play-on-seeked');
_this6.player.play();
} else if (!done && _this6.player.playing) {
seek.setAttribute('play-on-seeked', '');
_this6.player.pause();
seek.removeAttribute(attribute);
player.play();
} else if (!done && player.playing) {
seek.setAttribute(attribute, '');
player.pause();
}
});
// Seek
this.bind(this.player.elements.inputs.seek, inputEvent, function (event) {
this.bind(player.elements.inputs.seek, inputEvent, function (event) {
var seek = event.currentTarget;
// 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');
_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');
// Current time invert
// 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)) {
this.bind(this.player.elements.display.currentTime, 'click', function () {
if (player.config.toggleInvert && !is$1.element(player.elements.display.duration)) {
this.bind(player.elements.display.currentTime, 'click', function () {
// Do nothing if we're at the start
if (_this6.player.currentTime === 0) {
if (player.currentTime === 0) {
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
this.bind(this.player.elements.inputs.volume, inputEvent, function (event) {
_this6.player.volume = event.target.value;
this.bind(player.elements.inputs.volume, inputEvent, function (event) {
player.volume = event.target.value;
}, 'volume');
// Polyfill for lower fill in <input type="range"> for webkit
if (browser.isWebkit) {
Array.from(getElements.call(this.player, 'input[type="range"]')).forEach(function (element) {
_this6.bind(element, 'input', function (event) {
return controls.updateRangeFill.call(_this6.player, event.target);
Array.from(getElements.call(player, 'input[type="range"]')).forEach(function (element) {
_this3.bind(element, 'input', function (event) {
return controls.updateRangeFill.call(player, event.target);
});
});
}
// Seek tooltip
this.bind(this.player.elements.progress, 'mouseenter mouseleave mousemove', function (event) {
return controls.updateSeekTooltip.call(_this6.player, event);
this.bind(player.elements.progress, 'mouseenter mouseleave mousemove', function (event) {
return controls.updateSeekTooltip.call(player, event);
});
// Update controls.hover state (used for ui.toggleControls to avoid hiding when interacting)
this.bind(this.player.elements.controls, 'mouseenter mouseleave', function (event) {
_this6.player.elements.controls.hover = !_this6.player.touch && event.type === 'mouseenter';
this.bind(player.elements.controls, 'mouseenter mouseleave', function (event) {
player.elements.controls.hover = !player.touch && event.type === 'mouseenter';
});
// 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) {
_this6.player.elements.controls.pressed = ['mousedown', 'touchstart'].includes(event.type);
this.bind(player.elements.controls, 'mousedown mouseup touchstart touchend touchcancel', function (event) {
player.elements.controls.pressed = ['mousedown', 'touchstart'].includes(event.type);
});
// Focus in/out on controls
this.bind(this.player.elements.controls, 'focusin focusout', function (event) {
var _player = _this6.player,
config = _player.config,
elements = _player.elements,
timers = _player.timers;
this.bind(player.elements.controls, 'focusin focusout', function (event) {
var config = player.config,
elements = player.elements,
timers = player.timers;
var isFocusIn = event.type === 'focusin';
// Skip transition to prevent focus from scrolling the parent element
toggleClass(elements.controls, config.classNames.noTransition, event.type === 'focusin');
toggleClass(elements.controls, config.classNames.noTransition, isFocusIn);
// Toggle
ui.toggleControls.call(_this6.player, event.type === 'focusin');
ui.toggleControls.call(player, isFocusIn);
// If focusin, hide again after delay
if (event.type === 'focusin') {
if (isFocusIn) {
// Restore transition
setTimeout(function () {
toggleClass(elements.controls, config.classNames.noTransition, false);
}, 0);
// Delay a little more for keyboard users
var delay = _this6.touch ? 3000 : 4000;
var delay = _this3.touch ? 3000 : 4000;
// Clear timer
clearTimeout(timers.controls);
// Hide
timers.controls = setTimeout(function () {
return ui.toggleControls.call(_this6.player, false);
return ui.toggleControls.call(player, false);
}, delay);
}
});
// 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
// Other browsers on OS X will be inverted until support improves
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);
// Change the volume by 2%
_this6.player.increaseVolume(direction / 50);
player.increaseVolume(direction / 50);
// 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) {
event.preventDefault();
@ -12690,7 +12718,7 @@ typeof navigator === "object" && (function (global, factory) {
if (this.media.hasAttribute('autoplay')) {
this.config.autoplay = true;
}
if (this.media.hasAttribute('playsinline')) {
if (this.media.hasAttribute('playsinline') || this.media.hasAttribute('webkit-playsinline')) {
this.config.playsinline = true;
}
if (this.media.hasAttribute('muted')) {
@ -12762,7 +12790,9 @@ typeof navigator === "object" && (function (global, factory) {
this.fullscreen = new Fullscreen(this);
// Setup ads if provided
this.ads = new Ads(this);
if (this.config.ads.enabled) {
this.ads = new Ads(this);
}
// Autoplay if required
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)) {
controls.toggleMenu.call(this, false);
}
// Trigger event on change
if (hiding !== isHidden) {
var eventName = hiding ? 'controlshidden' : 'controlsshown';
triggerEvent.call(this, this.media, eventName);
}
return !hiding;
}
return false;
}
@ -13464,7 +13497,9 @@ typeof navigator === "object" && (function (global, factory) {
}
// Trigger request event
triggerEvent.call(this, this.media, 'qualityrequested', false, { quality: quality });
triggerEvent.call(this, this.media, 'qualityrequested', false, {
quality: quality
});
// Update config
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-preset-env": "^1.7.0",
"del": "^3.0.0",
"eslint": "^5.1.0",
"eslint": "^5.2.0",
"eslint-config-airbnb-base": "^13.0.0",
"eslint-config-prettier": "^2.9.0",
"eslint-plugin-import": "^2.13.0",
@ -57,7 +57,7 @@
"gulp-header": "^2.0.5",
"gulp-open": "^3.0.1",
"gulp-postcss": "^7.0.1",
"gulp-rename": "^1.3.0",
"gulp-rename": "^1.4.0",
"gulp-replace": "^1.0.0",
"gulp-s3": "^0.11.0",
"gulp-sass": "^4.0.1",
@ -81,15 +81,15 @@
"stylelint-config-recommended": "^2.1.0",
"stylelint-config-sass-guidelines": "^5.0.0",
"stylelint-order": "^0.8.1",
"stylelint-scss": "^3.1.3",
"stylelint-scss": "^3.2.0",
"stylelint-selector-bem-pattern": "^2.0.0",
"through2": "^2.0.3"
},
"dependencies": {
"babel-polyfill": "^6.26.0",
"custom-event-polyfill": "^0.3.0",
"custom-event-polyfill": "^1.0.2",
"loadjs": "^3.5.4",
"raven-js": "^3.26.3",
"raven-js": "^3.26.4",
"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
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
* 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
if (!isRadioButton && [32,39].includes(event.which)) {
controls.showMenuPanel.call(this, type);
controls.showMenuPanel.call(this, type, true);
} else {
let target;
@ -477,7 +477,7 @@ const controls = {
break;
}
controls.showMenuPanel.call(this, 'home');
controls.showMenuPanel.call(this, 'home', event.type === 'keydown');
},
type,
false,
@ -1118,7 +1118,7 @@ const controls = {
},
// Show a panel in the menu
showMenuPanel(type = '') {
showMenuPanel(type = '', tabFocus = false) {
const target = document.getElementById(`plyr-settings-${this.id}-${type}`);
// Nothing to show, bail
@ -1170,7 +1170,7 @@ const controls = {
// Focus the first item
const firstItem = target.querySelector('[role^="menuitem"]');
setFocus.call(this, firstItem, true);
setFocus.call(this, firstItem, tabFocus);
},
// Build the default HTML
@ -1344,7 +1344,7 @@ const controls = {
// Show menu on 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));
@ -1406,12 +1406,12 @@ const controls = {
event.stopPropagation();
// Show the respective menu
controls.showMenuPanel.call(this, 'home');
controls.showMenuPanel.call(this, 'home', true);
}, false);
// Go back via button click
on(backButton, 'click', () => {
controls.showMenuPanel.call(this, 'home');
controls.showMenuPanel.call(this, 'home', false);
});
// Add to pane

View File

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

View File

@ -52,7 +52,11 @@ class Plyr {
}
// 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
this.media = this.media[0];
}
@ -65,7 +69,9 @@ class Plyr {
options || {},
(() => {
try {
return JSON.parse(this.media.getAttribute('data-plyr-config'));
return JSON.parse(
this.media.getAttribute('data-plyr-config'),
);
} catch (e) {
return {};
}
@ -185,21 +191,30 @@ class Plyr {
// TODO: replace fullscreen.iosNative with this playsinline config option
// YouTube requires the playsinline in the URL
if (this.isYouTube) {
this.config.playsinline = truthy.includes(url.searchParams.get('playsinline'));
this.config.playsinline = truthy.includes(
url.searchParams.get('playsinline'),
);
} else {
this.config.playsinline = true;
}
}
} else {
// <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
this.media.removeAttribute(this.config.attributes.embed.provider);
this.media.removeAttribute(
this.config.attributes.embed.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');
return;
}
@ -221,7 +236,10 @@ class Plyr {
if (this.media.hasAttribute('autoplay')) {
this.config.autoplay = true;
}
if (this.media.hasAttribute('playsinline')) {
if (
this.media.hasAttribute('playsinline') ||
this.media.hasAttribute('webkit-playsinline')
) {
this.config.playsinline = true;
}
if (this.media.hasAttribute('muted')) {
@ -239,7 +257,11 @@ class Plyr {
}
// 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 (!this.supported.api) {
@ -272,9 +294,14 @@ class Plyr {
// Listen for events if debugging
if (this.config.debug) {
on.call(this, this.elements.container, this.config.events.join(' '), event => {
this.debug.log(`event: ${event.type}`);
});
on.call(
this,
this.elements.container,
this.config.events.join(' '),
event => {
this.debug.log(`event: ${event.type}`);
},
);
}
// Setup interface
@ -293,7 +320,9 @@ class Plyr {
this.fullscreen = new Fullscreen(this);
// Setup ads if provided
this.ads = new Ads(this);
if (this.config.ads.enabled) {
this.ads = new Ads(this);
}
// Autoplay if required
if (this.config.autoplay) {
@ -422,7 +451,9 @@ class Plyr {
* @param {number} seekTime - how far to rewind in seconds. Defaults to the config.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
*/
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;
// Set
this.media.currentTime = inputIsValid ? Math.min(input, this.duration) : 0;
this.media.currentTime = inputIsValid
? Math.min(input, this.duration)
: 0;
// Logging
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
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
return fauxDuration || duration;
@ -691,12 +729,16 @@ class Plyr {
if (!options.includes(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;
}
// Trigger request event
triggerEvent.call(this, this.media, 'qualityrequested', false, { quality });
triggerEvent.call(this, this.media, 'qualityrequested', false, {
quality,
});
// Update config
config.selected = quality;
@ -888,7 +930,9 @@ class Plyr {
const toggle = is.boolean(input) ? input : this.pip === states.inline;
// 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
if (this.supported.ui && !this.isAudio) {
// 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
const force = typeof toggle === 'undefined' ? undefined : !toggle;
// 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
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);
}
// Trigger event on change
if (hiding !== isHidden) {
const eventName = hiding ? 'controlshidden' : 'controlsshown';
triggerEvent.call(this, this.media, eventName);
}
return !hiding;
}
return false;
}
@ -1017,7 +1075,12 @@ class Plyr {
replaceElement(this.elements.original, this.elements.container);
// Event
triggerEvent.call(this, this.elements.original, 'destroyed', true);
triggerEvent.call(
this,
this.elements.original,
'destroyed',
true,
);
// Callback
if (is.function(callback)) {

View File

@ -25,9 +25,13 @@ const support = {
// Check for support
// Basic functionality vs full UI
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 ui = api && support.rangeInput && (type !== 'video' || !browser.isIPhone || canPlayInline);
const ui =
api &&
support.rangeInput &&
(type !== 'video' || !browser.isIPhone || canPlayInline);
return {
api,
@ -37,7 +41,9 @@ const support = {
// Picture-in-picture support
// Safari only currently
pip: (() => !browser.isIPhone && is.function(createElement('video').webkitSetPresentationMode))(),
pip: (() =>
!browser.isIPhone &&
is.function(createElement('video').webkitSetPresentationMode))(),
// Airplay support
// Safari only currently
@ -69,7 +75,9 @@ const support = {
}
try {
return Boolean(type && this.media.canPlayType(type).replace(/no/, ''));
return Boolean(
type && this.media.canPlayType(type).replace(/no/, ''),
);
} catch (err) {
return false;
}
@ -94,7 +102,9 @@ const support = {
// Reduced motion iOS & MacOS setting
// 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;

View File

@ -14,8 +14,16 @@ import loadImage from './utils/loadImage';
const ui = {
addStyleHook() {
toggleClass(this.elements.container, this.config.selectors.container.replace('.', ''), true);
toggleClass(this.elements.container, this.config.classNames.uiSupported, this.supported.ui);
toggleClass(
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
@ -35,7 +43,9 @@ const ui = {
// Don't setup interface if no support
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
ui.toggleNativeControls.call(this, true);
@ -93,13 +103,25 @@ const ui = {
);
// 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
toggleClass(this.elements.container, this.config.classNames.isIos, browser.isIos);
toggleClass(
this.elements.container,
this.config.classNames.isIos,
browser.isIos,
);
// 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
this.ready = true;
@ -149,7 +171,9 @@ const ui = {
}
// 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);
iframe.setAttribute('title', format.replace('{title}', title));
@ -158,7 +182,11 @@ const ui = {
// Toggle poster
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)
@ -189,7 +217,9 @@ const ui = {
.then(() => {
// Prevent race conditions
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(() => {
@ -207,9 +237,21 @@ const ui = {
// Check playing state
checkPlaying(event) {
// Class hooks
toggleClass(this.elements.container, 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);
toggleClass(
this.elements.container,
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
Array.from(this.elements.buttons.play || []).forEach(target => {
@ -235,7 +277,11 @@ const ui = {
// Timer to prevent flicker when seeking
this.timers.loading = setTimeout(() => {
// 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
ui.toggleControls.call(this);
@ -248,7 +294,15 @@ const ui = {
if (controls && this.config.hideControls) {
// 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',
};
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;
})();
@ -23,8 +25,12 @@ export const transitionEndEvent = (() => {
// Force repaint of element
export function repaint(element) {
setTimeout(() => {
toggleHidden(element, true);
element.offsetHeight; // eslint-disable-line
toggleHidden(element, false);
try {
toggleHidden(element, true);
element.offsetHeight; // eslint-disable-line
toggleHidden(element, false);
} catch (e) {
// Do nothing
}
}, 0);
}

View File

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

View File

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

View File

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