From 16c3a7d9e5be8ed2ffbbcee1c786b88d1cecc4cd Mon Sep 17 00:00:00 2001 From: Albin Larsson Date: Tue, 15 May 2018 05:16:06 +0200 Subject: Rewrite ui.setPoster to check that images arent broken or youtube fallback images. Only show poster element when valid --- src/js/ui.js | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) (limited to 'src/js/ui.js') diff --git a/src/js/ui.js b/src/js/ui.js index 2347b5c8..50764a86 100644 --- a/src/js/ui.js +++ b/src/js/ui.js @@ -105,8 +105,10 @@ const ui = { // Set the title ui.setTitle.call(this); - // Set the poster image - ui.setPoster.call(this); + // Assure the poster image is set, if the property was added before the UI was created + if (this.poster) { + ui.setPoster.call(this, this.poster); + } }, // Setup aria attribute for play and iframe title @@ -146,15 +148,34 @@ const ui = { } }, - // Set the poster image - setPoster() { - if (!utils.is.element(this.elements.poster) || utils.is.empty(this.poster)) { - return; + // Toggle poster + togglePoster(enable) { + utils.toggleClass(this.elements.container, this.config.classNames.posterEnabled, enable); + }, + + // Set the poster image (async) + setPoster(poster) { + // Set property regardless of validity + this.media.setAttribute('poster', poster); + + // Bail if element is missing + if (!utils.is.element(this.elements.poster)) { + return Promise.reject(); } - // Set the inline style - const posters = this.poster.split(','); - this.elements.poster.style.backgroundImage = posters.map(p => `url('${p}')`).join(','); + // Load the image, and set poster if successful + const loadPromise = utils.loadImage(poster) + .then(() => { + this.elements.poster.style.backgroundImage = `url('${poster}')`; + ui.togglePoster.call(this, true); + return poster; + }); + + // Hide the element if the poster can't be loaded (otherwise it will just be a black element covering the video) + loadPromise.catch(() => ui.togglePoster.call(this, false)); + + // Return the promise so the caller can use it as well + return loadPromise; }, // Check playing state -- cgit v1.2.3 From c845558d960412ad5e942334fd9f60ed173e0a5a Mon Sep 17 00:00:00 2001 From: Albin Larsson Date: Tue, 15 May 2018 16:22:51 +0200 Subject: Youtube poster: Set css backgroundSize to 'cover' for padded youtube thumbnails --- src/js/ui.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'src/js/ui.js') diff --git a/src/js/ui.js b/src/js/ui.js index 50764a86..f844f93c 100644 --- a/src/js/ui.js +++ b/src/js/ui.js @@ -105,8 +105,8 @@ const ui = { // Set the title ui.setTitle.call(this); - // Assure the poster image is set, if the property was added before the UI was created - if (this.poster) { + // Assure the poster image is set, if the property was added before the element was created + if (this.poster && this.elements.poster && !this.elements.poster.style.backgroundImage) { ui.setPoster.call(this, this.poster); } }, @@ -167,6 +167,11 @@ const ui = { const loadPromise = utils.loadImage(poster) .then(() => { this.elements.poster.style.backgroundImage = `url('${poster}')`; + Object.assign(this.elements.poster.style, { + backgroundImage: `url('${poster}')`, + // Reset backgroundSize as well (since it can be set to "cover" for padded thumbnails for youtube) + backgroundSize: '', + }); ui.togglePoster.call(this, true); return poster; }); -- cgit v1.2.3 From d7356726a16d64d242cd20421bff431d66d146b8 Mon Sep 17 00:00:00 2001 From: Albin Larsson Date: Thu, 10 May 2018 00:36:02 +0200 Subject: Remove ui.checkFailed() and error class --- src/js/ui.js | 23 ----------------------- 1 file changed, 23 deletions(-) (limited to 'src/js/ui.js') diff --git a/src/js/ui.js b/src/js/ui.js index 2347b5c8..ea592d82 100644 --- a/src/js/ui.js +++ b/src/js/ui.js @@ -196,29 +196,6 @@ const ui = { }, this.loading ? 250 : 0); }, - // Check if media failed to load - checkFailed() { - // https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/networkState - this.failed = this.media.networkState === 3; - - if (this.failed) { - utils.toggleClass(this.elements.container, this.config.classNames.loading, false); - utils.toggleClass(this.elements.container, this.config.classNames.error, true); - } - - // Clear timer - clearTimeout(this.timers.failed); - - // Timer to prevent flicker when seeking - this.timers.loading = setTimeout(() => { - // Toggle container class hook - utils.toggleClass(this.elements.container, this.config.classNames.loading, this.loading); - - // Show controls if loading, hide if done - this.toggleControls(this.loading); - }, this.loading ? 250 : 0); - }, - // Update volume UI and storage updateVolume() { if (!this.supported.ui) { -- cgit v1.2.3 From 37c5fbfe16ba0969b727b8359fdd04eb0bf7a021 Mon Sep 17 00:00:00 2001 From: Albin Larsson Date: Wed, 16 May 2018 04:52:04 +0200 Subject: toggleControls() rewrite --- src/js/ui.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'src/js/ui.js') diff --git a/src/js/ui.js b/src/js/ui.js index ea592d82..557599da 100644 --- a/src/js/ui.js +++ b/src/js/ui.js @@ -173,7 +173,7 @@ const ui = { } // Toggle controls - this.toggleControls(!this.playing); + ui.toggleControls.call(this); }, // Check if media is loading @@ -188,14 +188,24 @@ const ui = { // Timer to prevent flicker when seeking this.timers.loading = setTimeout(() => { - // Toggle container class hook + // Update progress bar loading class state utils.toggleClass(this.elements.container, this.config.classNames.loading, this.loading); - // Show controls if loading, hide if done - this.toggleControls(this.loading); + // Update controls visibility + ui.toggleControls.call(this); }, this.loading ? 250 : 0); }, + // Toggle controls based on state and `force` argument + toggleControls(force) { + const { controls } = this.elements; + + 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)); + } + }, + // Update volume UI and storage updateVolume() { if (!this.supported.ui) { -- cgit v1.2.3 From 51814249afd4337c1a7d1426ea913988228a7574 Mon Sep 17 00:00:00 2001 From: Sam Potts Date: Sat, 19 May 2018 11:24:56 +1000 Subject: Reduce circular dependencies --- src/js/ui.js | 140 +---------------------------------------------------------- 1 file changed, 2 insertions(+), 138 deletions(-) (limited to 'src/js/ui.js') diff --git a/src/js/ui.js b/src/js/ui.js index 2347b5c8..8f3f6a77 100644 --- a/src/js/ui.js +++ b/src/js/ui.js @@ -74,10 +74,10 @@ const ui = { this.quality = null; // Reset volume display - ui.updateVolume.call(this); + controls.updateVolume.call(this); // Reset time display - ui.timeUpdate.call(this); + controls.timeUpdate.call(this); // Update the UI ui.checkPlaying.call(this); @@ -218,142 +218,6 @@ const ui = { this.toggleControls(this.loading); }, this.loading ? 250 : 0); }, - - // Update volume UI and storage - updateVolume() { - if (!this.supported.ui) { - return; - } - - // Update range - if (utils.is.element(this.elements.inputs.volume)) { - ui.setRange.call(this, this.elements.inputs.volume, this.muted ? 0 : this.volume); - } - - // Update mute state - if (utils.is.element(this.elements.buttons.mute)) { - utils.toggleState(this.elements.buttons.mute, this.muted || this.volume === 0); - } - }, - - // Update seek value and lower fill - setRange(target, value = 0) { - if (!utils.is.element(target)) { - return; - } - - // eslint-disable-next-line - target.value = value; - - // Webkit range fill - controls.updateRangeFill.call(this, target); - }, - - // Set value - setProgress(target, input) { - const value = utils.is.number(input) ? input : 0; - const progress = utils.is.element(target) ? target : this.elements.display.buffer; - - // Update value and label - if (utils.is.element(progress)) { - progress.value = value; - - // Update text label inside - const label = progress.getElementsByTagName('span')[0]; - if (utils.is.element(label)) { - label.childNodes[0].nodeValue = value; - } - } - }, - - // Update elements - updateProgress(event) { - if (!this.supported.ui || !utils.is.event(event)) { - return; - } - - let value = 0; - - if (event) { - switch (event.type) { - // Video playing - case 'timeupdate': - case 'seeking': - value = utils.getPercentage(this.currentTime, this.duration); - - // Set seek range value only if it's a 'natural' time event - if (event.type === 'timeupdate') { - ui.setRange.call(this, this.elements.inputs.seek, value); - } - - break; - - // Check buffer status - case 'playing': - case 'progress': - ui.setProgress.call(this, this.elements.display.buffer, this.buffered * 100); - - break; - - default: - break; - } - } - }, - - // Update the displayed time - updateTimeDisplay(target = null, time = 0, inverted = false) { - // Bail if there's no element to display or the value isn't a number - if (!utils.is.element(target) || !utils.is.number(time)) { - return; - } - - // Always display hours if duration is over an hour - const forceHours = utils.getHours(this.duration) > 0; - - // eslint-disable-next-line no-param-reassign - target.textContent = utils.formatTime(time, forceHours, inverted); - }, - - // Handle time change event - timeUpdate(event) { - // Only invert if only one time element is displayed and used for both duration and currentTime - const invert = !utils.is.element(this.elements.display.duration) && this.config.invertTime; - - // Duration - ui.updateTimeDisplay.call(this, this.elements.display.currentTime, invert ? this.duration - this.currentTime : this.currentTime, invert); - - // Ignore updates while seeking - if (event && event.type === 'timeupdate' && this.media.seeking) { - return; - } - - // Playing progress - ui.updateProgress.call(this, event); - }, - - // Show the duration on metadataloaded - durationUpdate() { - if (!this.supported.ui) { - return; - } - - // If there's a spot to display duration - const hasDuration = utils.is.element(this.elements.display.duration); - - // If there's only one time display, display duration there - if (!hasDuration && this.config.displayDuration && this.paused) { - ui.updateTimeDisplay.call(this, this.elements.display.currentTime, this.duration); - } - - // If there's a duration element, update content - if (hasDuration) { - ui.updateTimeDisplay.call(this, this.elements.display.duration, this.duration); - } - - // Update the tooltip (if visible) - controls.updateSeekTooltip.call(this); - }, }; export default ui; -- cgit v1.2.3 From 90c5735904354f5fde0dcdae9f8894fe9088739c Mon Sep 17 00:00:00 2001 From: Sam Potts Date: Mon, 28 May 2018 10:19:07 +1000 Subject: WIP --- src/js/ui.js | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'src/js/ui.js') diff --git a/src/js/ui.js b/src/js/ui.js index 3a8f2d05..5b14e2fe 100644 --- a/src/js/ui.js +++ b/src/js/ui.js @@ -164,17 +164,16 @@ const ui = { } // Load the image, and set poster if successful - const loadPromise = utils.loadImage(poster) - .then(() => { - this.elements.poster.style.backgroundImage = `url('${poster}')`; - Object.assign(this.elements.poster.style, { - backgroundImage: `url('${poster}')`, - // Reset backgroundSize as well (since it can be set to "cover" for padded thumbnails for youtube) - backgroundSize: '', - }); - ui.togglePoster.call(this, true); - return poster; + const loadPromise = utils.loadImage(poster).then(() => { + this.elements.poster.style.backgroundImage = `url('${poster}')`; + Object.assign(this.elements.poster.style, { + backgroundImage: `url('${poster}')`, + // Reset backgroundSize as well (since it can be set to "cover" for padded thumbnails for youtube) + backgroundSize: '', }); + ui.togglePoster.call(this, true); + return poster; + }); // Hide the element if the poster can't be loaded (otherwise it will just be a black element covering the video) loadPromise.catch(() => ui.togglePoster.call(this, false)); @@ -190,8 +189,10 @@ const ui = { utils.toggleClass(this.elements.container, this.config.classNames.paused, this.paused); utils.toggleClass(this.elements.container, this.config.classNames.stopped, this.stopped); - // Set ARIA state - utils.toggleState(this.elements.buttons.play, this.playing); + // Set state + Array.from(this.elements.buttons.play).forEach(target => { + target.pressed = this.playing; + }); // Only update controls on non timeupdate events if (utils.is.event(event) && event.type === 'timeupdate') { -- cgit v1.2.3 From 7aad747c25b07fedf4a4fc75095c560ea3c9899c Mon Sep 17 00:00:00 2001 From: Albin Larsson Date: Sun, 27 May 2018 05:08:18 +0200 Subject: Optimize captions code reused and ensure captionsenabled/captionsdisabled will be sent on initial setup --- src/js/ui.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/js/ui.js') diff --git a/src/js/ui.js b/src/js/ui.js index 3a8f2d05..0d8e532f 100644 --- a/src/js/ui.js +++ b/src/js/ui.js @@ -55,8 +55,10 @@ const ui = { // Remove native controls ui.toggleNativeControls.call(this); - // Captions - captions.setup.call(this); + // Setup captions for html5 + if (this.isHTML5) { + captions.setup.call(this); + } // Reset volume this.volume = null; -- cgit v1.2.3 From c9298fde768d14975a41ce8018fd0f10116943aa Mon Sep 17 00:00:00 2001 From: Albin Larsson Date: Sun, 13 May 2018 19:58:23 +0200 Subject: Fix typo --- src/js/ui.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/js/ui.js') diff --git a/src/js/ui.js b/src/js/ui.js index 0d8e532f..9a6692dc 100644 --- a/src/js/ui.js +++ b/src/js/ui.js @@ -55,7 +55,7 @@ const ui = { // Remove native controls ui.toggleNativeControls.call(this); - // Setup captions for html5 + // Setup captions for HTML5 if (this.isHTML5) { captions.setup.call(this); } -- cgit v1.2.3 From 9d798893b56e2ebe9da2d6b87f639f2d2ecee365 Mon Sep 17 00:00:00 2001 From: Albin Larsson Date: Tue, 29 May 2018 16:06:07 +0200 Subject: Call duration update method manually if user config has duration --- src/js/ui.js | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/js/ui.js') diff --git a/src/js/ui.js b/src/js/ui.js index 3a8f2d05..aa398e3a 100644 --- a/src/js/ui.js +++ b/src/js/ui.js @@ -109,6 +109,12 @@ const ui = { if (this.poster && this.elements.poster && !this.elements.poster.style.backgroundImage) { ui.setPoster.call(this, this.poster); } + + // Manually set the duration if user has overridden it. + // The event listeners for it doesn't get called if preload is disabled (#701) + if (this.config.duration) { + controls.durationUpdate.call(this); + } }, // Setup aria attribute for play and iframe title -- cgit v1.2.3 From 38f10d4cc67b3109189699f7e65189a852064236 Mon Sep 17 00:00:00 2001 From: Sam Potts Date: Mon, 11 Jun 2018 16:19:11 +1000 Subject: WIP --- src/js/ui.js | 3 --- 1 file changed, 3 deletions(-) (limited to 'src/js/ui.js') diff --git a/src/js/ui.js b/src/js/ui.js index e90a1492..979d8341 100644 --- a/src/js/ui.js +++ b/src/js/ui.js @@ -127,9 +127,6 @@ const ui = { // If there's a media title set, use that for the label if (utils.is.string(this.config.title) && !utils.is.empty(this.config.title)) { label += `, ${this.config.title}`; - - // Set container label - this.elements.container.setAttribute('aria-label', this.config.title); } // If there's a play button, set label -- cgit v1.2.3 From 392dfd024c505f5ae1bbb2f0d3e0793c251a1f35 Mon Sep 17 00:00:00 2001 From: Sam Potts Date: Wed, 13 Jun 2018 00:02:55 +1000 Subject: Utils broken down into seperate files and exports --- src/js/ui.js | 70 ++++++++++++++++++++++++++++++------------------------------ 1 file changed, 35 insertions(+), 35 deletions(-) (limited to 'src/js/ui.js') diff --git a/src/js/ui.js b/src/js/ui.js index d6ab0e59..e3faf42f 100644 --- a/src/js/ui.js +++ b/src/js/ui.js @@ -6,15 +6,16 @@ import captions from './captions'; import controls from './controls'; import i18n from './i18n'; import support from './support'; -import utils from './utils'; - -// Sniff out the browser -const browser = utils.getBrowser(); +import browser from './utils/browser'; +import { getElement, toggleClass, toggleState } from './utils/elements'; +import { trigger } from './utils/events'; +import is from './utils/is'; +import loadImage from './utils/loadImage'; const ui = { addStyleHook() { - utils.toggleClass(this.elements.container, this.config.selectors.container.replace('.', ''), true); - utils.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 @@ -44,7 +45,7 @@ const ui = { } // Inject custom controls if not present - if (!utils.is.element(this.elements.controls)) { + if (!is.element(this.elements.controls)) { // Inject custom controls controls.inject.call(this); @@ -85,23 +86,23 @@ const ui = { ui.checkPlaying.call(this); // Check for picture-in-picture support - utils.toggleClass(this.elements.container, this.config.classNames.pip.supported, support.pip && this.isHTML5 && this.isVideo); + toggleClass(this.elements.container, this.config.classNames.pip.supported, support.pip && this.isHTML5 && this.isVideo); // Check for airplay support - utils.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 - utils.toggleClass(this.elements.container, this.config.classNames.isIos, browser.isIos); + toggleClass(this.elements.container, this.config.classNames.isIos, browser.isIos); // Add touch class - utils.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; // Ready event at end of execution stack setTimeout(() => { - utils.dispatchEvent.call(this, this.media, 'ready'); + trigger.call(this, this.media, 'ready'); }, 0); // Set the title @@ -125,7 +126,7 @@ const ui = { let label = i18n.get('play', this.config); // If there's a media title set, use that for the label - if (utils.is.string(this.config.title) && !utils.is.empty(this.config.title)) { + if (is.string(this.config.title) && !is.empty(this.config.title)) { label += `, ${this.config.title}`; // Set container label @@ -133,7 +134,7 @@ const ui = { } // If there's a play button, set label - if (utils.is.nodeList(this.elements.buttons.play)) { + if (is.nodeList(this.elements.buttons.play)) { Array.from(this.elements.buttons.play).forEach(button => { button.setAttribute('aria-label', label); }); @@ -142,14 +143,14 @@ const ui = { // Set iframe title // https://github.com/sampotts/plyr/issues/124 if (this.isEmbed) { - const iframe = utils.getElement.call(this, 'iframe'); + const iframe = getElement.call(this, 'iframe'); - if (!utils.is.element(iframe)) { + if (!is.element(iframe)) { return; } // Default to media type - const title = !utils.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 +159,7 @@ const ui = { // Toggle poster togglePoster(enable) { - utils.toggleClass(this.elements.container, this.config.classNames.posterEnabled, enable); + toggleClass(this.elements.container, this.config.classNames.posterEnabled, enable); }, // Set the poster image (async) @@ -167,22 +168,21 @@ const ui = { this.media.setAttribute('poster', poster); // Bail if element is missing - if (!utils.is.element(this.elements.poster)) { + if (!is.element(this.elements.poster)) { return Promise.reject(); } // Load the image, and set poster if successful - const loadPromise = utils.loadImage(poster) - .then(() => { - this.elements.poster.style.backgroundImage = `url('${poster}')`; - Object.assign(this.elements.poster.style, { - backgroundImage: `url('${poster}')`, - // Reset backgroundSize as well (since it can be set to "cover" for padded thumbnails for youtube) - backgroundSize: '', - }); - ui.togglePoster.call(this, true); - return poster; + const loadPromise = loadImage(poster).then(() => { + this.elements.poster.style.backgroundImage = `url('${poster}')`; + Object.assign(this.elements.poster.style, { + backgroundImage: `url('${poster}')`, + // Reset backgroundSize as well (since it can be set to "cover" for padded thumbnails for youtube) + backgroundSize: '', }); + ui.togglePoster.call(this, true); + return poster; + }); // Hide the element if the poster can't be loaded (otherwise it will just be a black element covering the video) loadPromise.catch(() => ui.togglePoster.call(this, false)); @@ -194,15 +194,15 @@ const ui = { // Check playing state checkPlaying(event) { // Class hooks - utils.toggleClass(this.elements.container, this.config.classNames.playing, this.playing); - utils.toggleClass(this.elements.container, this.config.classNames.paused, this.paused); - utils.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 ARIA state - utils.toggleState(this.elements.buttons.play, this.playing); + toggleState(this.elements.buttons.play, this.playing); // Only update controls on non timeupdate events - if (utils.is.event(event) && event.type === 'timeupdate') { + if (is.event(event) && event.type === 'timeupdate') { return; } @@ -223,7 +223,7 @@ const ui = { // Timer to prevent flicker when seeking this.timers.loading = setTimeout(() => { // Update progress bar loading class state - utils.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); -- cgit v1.2.3 From 6bff6b317d6adcd9f94c8d4d8ee225d39f784e0f Mon Sep 17 00:00:00 2001 From: Albin Larsson Date: Wed, 13 Jun 2018 22:18:57 +0200 Subject: Remove line breaks in arrays --- src/js/ui.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'src/js/ui.js') diff --git a/src/js/ui.js b/src/js/ui.js index d3d86124..285739a7 100644 --- a/src/js/ui.js +++ b/src/js/ui.js @@ -212,10 +212,7 @@ const ui = { // Check if media is loading checkLoading(event) { - this.loading = [ - 'stalled', - 'waiting', - ].includes(event.type); + this.loading = ['stalled', 'waiting'].includes(event.type); // Clear timer clearTimeout(this.timers.loading); -- cgit v1.2.3 From 115f352ade7fbe133a42fd434dbcc1fca13287a7 Mon Sep 17 00:00:00 2001 From: Albin Larsson Date: Fri, 15 Jun 2018 23:56:47 +0200 Subject: Respect call order and prioritize public API calls for setting poster, in order to avoid race conditions --- src/js/ui.js | 63 +++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 37 insertions(+), 26 deletions(-) (limited to 'src/js/ui.js') diff --git a/src/js/ui.js b/src/js/ui.js index 285739a7..5d7a6ae3 100644 --- a/src/js/ui.js +++ b/src/js/ui.js @@ -8,7 +8,7 @@ import i18n from './i18n'; import support from './support'; import browser from './utils/browser'; import { getElement, toggleClass, toggleState } from './utils/elements'; -import { triggerEvent } from './utils/events'; +import { ready, triggerEvent } from './utils/events'; import is from './utils/is'; import loadImage from './utils/loadImage'; @@ -109,8 +109,8 @@ const ui = { ui.setTitle.call(this); // Assure the poster image is set, if the property was added before the element was created - if (this.poster && this.elements.poster && !this.elements.poster.style.backgroundImage) { - ui.setPoster.call(this, this.poster); + if (this.poster) { + ui.setPoster.call(this, this.poster, false).catch(() => {}); } // Manually set the duration if user has overridden it. @@ -163,32 +163,43 @@ const ui = { }, // Set the poster image (async) - setPoster(poster) { - // Set property regardless of validity - this.media.setAttribute('poster', poster); - - // Bail if element is missing - if (!is.element(this.elements.poster)) { - return Promise.reject(); + // Used internally for the poster setter, with the passive option forced to false + setPoster(poster, passive = true) { + // Don't override if call is passive + if (passive && this.poster) { + return Promise.reject(new Error('Poster already set')); } - // Load the image, and set poster if successful - const loadPromise = loadImage(poster).then(() => { - this.elements.poster.style.backgroundImage = `url('${poster}')`; - Object.assign(this.elements.poster.style, { - backgroundImage: `url('${poster}')`, - // Reset backgroundSize as well (since it can be set to "cover" for padded thumbnails for youtube) - backgroundSize: '', - }); - ui.togglePoster.call(this, true); - return poster; - }); - - // Hide the element if the poster can't be loaded (otherwise it will just be a black element covering the video) - loadPromise.catch(() => ui.togglePoster.call(this, false)); + // Set property synchronously to respect the call order + this.media.setAttribute('poster', poster); - // Return the promise so the caller can use it as well - return loadPromise; + // Wait until ui is ready + return ready.call(this) + // Load image + .then(() => loadImage(poster)) + .catch(err => { + // Hide poster on error unless it's been set by another call + if (poster === this.poster) { + ui.togglePoster.call(this, false); + } + // Rethrow + throw err; + }) + .then(() => { + // Prevent race conditions + if (poster !== this.poster) { + throw new Error('setPoster cancelled by later call to setPoster'); + } + }) + .then(() => { + Object.assign(this.elements.poster.style, { + backgroundImage: `url('${poster}')`, + // Reset backgroundSize as well (since it can be set to "cover" for padded thumbnails for youtube) + backgroundSize: '', + }); + ui.togglePoster.call(this, true); + return poster; + }); }, // Check playing state -- cgit v1.2.3 From d4abb4b1438cb316aacae480e7b7e9b055a60b24 Mon Sep 17 00:00:00 2001 From: Sam Potts Date: Sun, 17 Jun 2018 01:04:55 +1000 Subject: 120 line width, package upgrade --- src/js/ui.js | 61 +++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 34 insertions(+), 27 deletions(-) (limited to 'src/js/ui.js') diff --git a/src/js/ui.js b/src/js/ui.js index 5d7a6ae3..e0d7c6ae 100644 --- a/src/js/ui.js +++ b/src/js/ui.js @@ -86,7 +86,11 @@ const ui = { ui.checkPlaying.call(this); // Check for picture-in-picture support - toggleClass(this.elements.container, this.config.classNames.pip.supported, support.pip && this.isHTML5 && this.isVideo); + toggleClass( + this.elements.container, + this.config.classNames.pip.supported, + support.pip && this.isHTML5 && this.isVideo, + ); // Check for airplay support toggleClass(this.elements.container, this.config.classNames.airplay.supported, support.airplay && this.isHTML5); @@ -174,32 +178,35 @@ const ui = { this.media.setAttribute('poster', poster); // Wait until ui is ready - return ready.call(this) - // Load image - .then(() => loadImage(poster)) - .catch(err => { - // Hide poster on error unless it's been set by another call - if (poster === this.poster) { - ui.togglePoster.call(this, false); - } - // Rethrow - throw err; - }) - .then(() => { - // Prevent race conditions - if (poster !== this.poster) { - throw new Error('setPoster cancelled by later call to setPoster'); - } - }) - .then(() => { - Object.assign(this.elements.poster.style, { - backgroundImage: `url('${poster}')`, - // Reset backgroundSize as well (since it can be set to "cover" for padded thumbnails for youtube) - backgroundSize: '', - }); - ui.togglePoster.call(this, true); - return poster; - }); + return ( + ready + .call(this) + // Load image + .then(() => loadImage(poster)) + .catch(err => { + // Hide poster on error unless it's been set by another call + if (poster === this.poster) { + ui.togglePoster.call(this, false); + } + // Rethrow + throw err; + }) + .then(() => { + // Prevent race conditions + if (poster !== this.poster) { + throw new Error('setPoster cancelled by later call to setPoster'); + } + }) + .then(() => { + Object.assign(this.elements.poster.style, { + backgroundImage: `url('${poster}')`, + // Reset backgroundSize as well (since it can be set to "cover" for padded thumbnails for youtube) + backgroundSize: '', + }); + ui.togglePoster.call(this, true); + return poster; + }) + ); }, // Check playing state -- cgit v1.2.3 From ea4d91d2a011cdfc835619cb90a25074c039e777 Mon Sep 17 00:00:00 2001 From: Sam Potts Date: Mon, 18 Jun 2018 23:21:03 +1000 Subject: v3.3.15 --- src/js/ui.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'src/js/ui.js') diff --git a/src/js/ui.js b/src/js/ui.js index b77ee131..d0b27ae7 100644 --- a/src/js/ui.js +++ b/src/js/ui.js @@ -214,9 +214,11 @@ const ui = { toggleClass(this.elements.container, this.config.classNames.stopped, this.stopped); // Set state - Array.from(this.elements.buttons.play).forEach(target => { - target.pressed = this.playing; - }); + if (is.nodeList(this.elements.buttons.play)) { + Array.from(this.elements.buttons.play).forEach(target => { + target.pressed = this.playing; + }); + } // Only update controls on non timeupdate events if (is.event(event) && event.type === 'timeupdate') { -- cgit v1.2.3 From b61ba02f3dbed71f595188d69441132972767eb0 Mon Sep 17 00:00:00 2001 From: Sam Potts Date: Tue, 19 Jun 2018 09:12:21 +1000 Subject: Fix issue with play button not changing state (fixes #1048) --- src/js/ui.js | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) (limited to 'src/js/ui.js') diff --git a/src/js/ui.js b/src/js/ui.js index d0b27ae7..34fe7e82 100644 --- a/src/js/ui.js +++ b/src/js/ui.js @@ -135,11 +135,9 @@ const ui = { } // If there's a play button, set label - if (is.nodeList(this.elements.buttons.play)) { - Array.from(this.elements.buttons.play).forEach(button => { - button.setAttribute('aria-label', label); - }); - } + Array.from(this.elements.buttons.play || []).forEach(button => { + button.setAttribute('aria-label', label); + }); // Set iframe title // https://github.com/sampotts/plyr/issues/124 @@ -214,11 +212,9 @@ const ui = { toggleClass(this.elements.container, this.config.classNames.stopped, this.stopped); // Set state - if (is.nodeList(this.elements.buttons.play)) { - Array.from(this.elements.buttons.play).forEach(target => { - target.pressed = this.playing; - }); - } + Array.from(this.elements.buttons.play || []).forEach(target => { + target.pressed = this.playing; + }); // Only update controls on non timeupdate events if (is.event(event) && event.type === 'timeupdate') { -- cgit v1.2.3 From 297f297d181b694446e04f653da694660e9971b4 Mon Sep 17 00:00:00 2001 From: Sam Potts Date: Mon, 13 Aug 2018 21:39:16 +1000 Subject: Moved i18n to utils --- src/js/ui.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/js/ui.js') diff --git a/src/js/ui.js b/src/js/ui.js index 34fe7e82..f0c898bf 100644 --- a/src/js/ui.js +++ b/src/js/ui.js @@ -4,11 +4,11 @@ import captions from './captions'; import controls from './controls'; -import i18n from './i18n'; import support from './support'; import browser from './utils/browser'; import { getElement, toggleClass } from './utils/elements'; import { ready, triggerEvent } from './utils/events'; +import i18n from './utils/i18n'; import is from './utils/is'; import loadImage from './utils/loadImage'; -- cgit v1.2.3 From 69d0d6d7eec8729da952aeea497db8f747a0ddfb Mon Sep 17 00:00:00 2001 From: James Date: Tue, 23 Oct 2018 10:08:46 +1100 Subject: Prevent immediate hiding of controls on mobile --- src/js/ui.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src/js/ui.js') diff --git a/src/js/ui.js b/src/js/ui.js index f0c898bf..8e50bb83 100644 --- a/src/js/ui.js +++ b/src/js/ui.js @@ -247,8 +247,11 @@ const ui = { const { controls } = this.elements; 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)); + // Don't hide controls if a touch-device user recently seeked. (Must be limited to touch devices, or it occasionally prevents desktop controls from hiding.) + const recentTouchSeek = (this.touch && this.lastSeekTime + 2000 > Date.now()); + + // Show controls if force, loading, paused, button interaction, or recent seek, otherwise hide + this.toggleControls(Boolean(force || this.loading || this.paused || controls.pressed || controls.hover || recentTouchSeek)); } }, }; -- cgit v1.2.3