aboutsummaryrefslogtreecommitdiffstats
path: root/src/js
diff options
context:
space:
mode:
Diffstat (limited to 'src/js')
-rw-r--r--src/js/captions.js4
-rw-r--r--src/js/controls.js87
-rw-r--r--src/js/defaults.js20
-rw-r--r--src/js/listeners.js21
-rw-r--r--src/js/media.js37
-rw-r--r--src/js/plugins/ads.js9
-rw-r--r--src/js/plugins/vimeo.js27
-rw-r--r--src/js/plugins/youtube.js9
-rw-r--r--src/js/plyr.js56
-rw-r--r--src/js/plyr.polyfilled.js4
-rw-r--r--src/js/ui.js38
-rw-r--r--src/js/utils.js72
12 files changed, 223 insertions, 161 deletions
diff --git a/src/js/captions.js b/src/js/captions.js
index c6618fda..e0692dcf 100644
--- a/src/js/captions.js
+++ b/src/js/captions.js
@@ -3,10 +3,10 @@
// TODO: Create as class
// ==========================================================================
-import support from './support';
-import utils from './utils';
import controls from './controls';
import i18n from './i18n';
+import support from './support';
+import utils from './utils';
const captions = {
// Setup captions
diff --git a/src/js/controls.js b/src/js/controls.js
index 7ddcefef..5b16e950 100644
--- a/src/js/controls.js
+++ b/src/js/controls.js
@@ -2,12 +2,12 @@
// Plyr controls
// ==========================================================================
-import support from './support';
-import utils from './utils';
-import ui from './ui';
-import i18n from './i18n';
import captions from './captions';
import html5 from './html5';
+import i18n from './i18n';
+import support from './support';
+import ui from './ui';
+import utils from './utils';
// Sniff out the browser
const browser = utils.getBrowser();
@@ -37,17 +37,74 @@ const controls = {
// Get icon URL
getIconUrl() {
+ const url = new URL(this.config.iconUrl, window.location);
+ const cors = url.host !== window.location.host || (browser.isIE && !window.svg4everybody);
+
return {
url: this.config.iconUrl,
- absolute: this.config.iconUrl.indexOf('http') === 0 || (browser.isIE && !window.svg4everybody),
+ cors,
};
},
+ // Find the UI controls and store references in custom controls
+ // TODO: Allow settings menus with custom controls
+ findElements() {
+ try {
+ this.elements.controls = utils.getElement.call(this, this.config.selectors.controls.wrapper);
+
+ // Buttons
+ this.elements.buttons = {
+ play: utils.getElements.call(this, this.config.selectors.buttons.play),
+ pause: utils.getElement.call(this, this.config.selectors.buttons.pause),
+ restart: utils.getElement.call(this, this.config.selectors.buttons.restart),
+ rewind: utils.getElement.call(this, this.config.selectors.buttons.rewind),
+ fastForward: utils.getElement.call(this, this.config.selectors.buttons.fastForward),
+ mute: utils.getElement.call(this, this.config.selectors.buttons.mute),
+ pip: utils.getElement.call(this, this.config.selectors.buttons.pip),
+ airplay: utils.getElement.call(this, this.config.selectors.buttons.airplay),
+ settings: utils.getElement.call(this, this.config.selectors.buttons.settings),
+ captions: utils.getElement.call(this, this.config.selectors.buttons.captions),
+ fullscreen: utils.getElement.call(this, this.config.selectors.buttons.fullscreen),
+ };
+
+ // Progress
+ this.elements.progress = utils.getElement.call(this, this.config.selectors.progress);
+
+ // Inputs
+ this.elements.inputs = {
+ seek: utils.getElement.call(this, this.config.selectors.inputs.seek),
+ volume: utils.getElement.call(this, this.config.selectors.inputs.volume),
+ };
+
+ // Display
+ this.elements.display = {
+ buffer: utils.getElement.call(this, this.config.selectors.display.buffer),
+ currentTime: utils.getElement.call(this, this.config.selectors.display.currentTime),
+ duration: utils.getElement.call(this, this.config.selectors.display.duration),
+ };
+
+ // Seek tooltip
+ if (utils.is.element(this.elements.progress)) {
+ this.elements.display.seekTooltip = this.elements.progress.querySelector(`.${this.config.classNames.tooltip}`);
+ }
+
+ return true;
+ } catch (error) {
+ // Log it
+ this.debug.warn('It looks like there is a problem with your custom controls HTML', error);
+
+ // Restore native video controls
+ this.toggleNativeControls(true);
+
+ return false;
+ }
+ },
+
// Create <svg> icon
createIcon(type, attributes) {
const namespace = 'http://www.w3.org/2000/svg';
const iconUrl = controls.getIconUrl.call(this);
- const iconPath = `${!iconUrl.absolute ? iconUrl.url : ''}#${this.config.iconPrefix}`;
+ const iconPath = `${!iconUrl.cors ? iconUrl.url : ''}#${this.config.iconPrefix}`;
// Create <svg>
const icon = document.createElementNS(namespace, 'svg');
@@ -840,11 +897,9 @@ const controls = {
},
// Toggle Menu
- showTab(event) {
+ showTab(target = '') {
const { menu } = this.elements.settings;
- const tab = event.target;
- const show = tab.getAttribute('aria-expanded') === 'false';
- const pane = document.getElementById(tab.getAttribute('aria-controls'));
+ const pane = document.getElementById(target);
// Nothing to show, bail
if (!utils.is.element(pane)) {
@@ -907,8 +962,12 @@ const controls = {
current.setAttribute('tabindex', -1);
// Set attributes on target
- utils.toggleHidden(pane, !show);
- tab.setAttribute('aria-expanded', show);
+ utils.toggleHidden(pane, false);
+
+ const tabs = utils.getElements.call(this, `[aria-controls="${target}"]`);
+ Array.from(tabs).forEach(tab => {
+ tab.setAttribute('aria-expanded', true);
+ });
pane.removeAttribute('tabindex');
// Focus the first item
@@ -1183,7 +1242,7 @@ const controls = {
const icon = controls.getIconUrl.call(this);
// Only load external sprite using AJAX
- if (icon.absolute) {
+ if (icon.cors) {
utils.loadSprite(icon.url, 'sprite-plyr');
}
}
@@ -1269,7 +1328,7 @@ const controls = {
// Find the elements if need be
if (!utils.is.element(this.elements.controls)) {
- utils.findElements.call(this);
+ controls.findElements.call(this);
}
// Edge sometimes doesn't finish the paint so force a redraw
diff --git a/src/js/defaults.js b/src/js/defaults.js
index 418b60ae..7857644b 100644
--- a/src/js/defaults.js
+++ b/src/js/defaults.js
@@ -47,8 +47,8 @@ const defaults = {
// Auto hide the controls
hideControls: true,
- // Revert to poster on finish (HTML5 - will cause reload)
- showPosterOnEnd: false,
+ // Reset to start when playback ended
+ resetOnEnd: false,
// Disable the standard context menu
disableContextMenu: true,
@@ -56,7 +56,7 @@ const defaults = {
// Sprite (for icons)
loadSprite: true,
iconPrefix: 'plyr',
- iconUrl: 'https://cdn.plyr.io/3.2.4/plyr.svg',
+ iconUrl: 'https://cdn.plyr.io/3.3.0/plyr.svg',
// Blank video (used to prevent errors on source change)
blankVideo: 'https://cdn.plyr.io/static/blank.mp4',
@@ -192,13 +192,17 @@ const defaults = {
// URLs
urls: {
vimeo: {
- api: 'https://player.vimeo.com/api/player.js',
+ sdk: 'https://player.vimeo.com/api/player.js',
+ iframe: 'https://player.vimeo.com/video/{0}?{1}',
+ api: 'https://vimeo.com/api/v2/video/{0}.json',
},
youtube: {
- api: 'https://www.youtube.com/iframe_api',
+ sdk: 'https://www.youtube.com/iframe_api',
+ api: 'https://www.googleapis.com/youtube/v3/videos?id={0}&key={1}&fields=items(snippet(title))&part=snippet',
+ poster: 'https://img.youtube.com/vi/{0}/maxresdefault.jpg,https://img.youtube.com/vi/{0}/hqdefault.jpg',
},
googleIMA: {
- api: 'https://imasdk.googleapis.com/js/sdkloader/ima3.js',
+ sdk: 'https://imasdk.googleapis.com/js/sdkloader/ima3.js',
},
},
@@ -324,12 +328,14 @@ const defaults = {
classNames: {
video: 'plyr__video-wrapper',
embed: 'plyr__video-embed',
+ poster: 'plyr__poster',
ads: 'plyr__ads',
control: 'plyr__control',
type: 'plyr--{0}',
provider: 'plyr--{0}',
- stopped: 'plyr--stopped',
playing: 'plyr--playing',
+ paused: 'plyr--paused',
+ stopped: 'plyr--stopped',
loading: 'plyr--loading',
error: 'plyr--has-error',
hover: 'plyr--hover',
diff --git a/src/js/listeners.js b/src/js/listeners.js
index 2664e827..f4e9ade3 100644
--- a/src/js/listeners.js
+++ b/src/js/listeners.js
@@ -2,9 +2,9 @@
// Plyr Event Listeners
// ==========================================================================
-import utils from './utils';
import controls from './controls';
import ui from './ui';
+import utils from './utils';
// Sniff out the browser
const browser = utils.getBrowser();
@@ -265,12 +265,9 @@ class Listeners {
// Handle the media finishing
utils.on(this.player.media, 'ended', () => {
// Show poster on end
- if (this.player.isHTML5 && this.player.isVideo && this.player.config.showPosterOnEnd) {
+ if (this.player.isHTML5 && this.player.isVideo && this.player.config.resetOnEnd) {
// Restart
this.player.restart();
-
- // Re-load media
- this.player.media.load();
}
});
@@ -281,7 +278,7 @@ class Listeners {
utils.on(this.player.media, 'volumechange', event => ui.updateVolume.call(this.player, event));
// Handle play/pause
- utils.on(this.player.media, 'playing play pause ended emptied', event => ui.checkPlaying.call(this.player, event));
+ utils.on(this.player.media, 'playing play pause ended emptied timeupdate', event => ui.checkPlaying.call(this.player, event));
// Loading state
utils.on(this.player.media, 'waiting canplay seeked playing', event => ui.checkLoading.call(this.player, event));
@@ -492,12 +489,19 @@ class Listeners {
on(this.player.elements.settings.form, 'click', event => {
event.stopPropagation();
+ // Go back to home tab on click
+ const showHomeTab = () => {
+ const id = `plyr-settings-${this.player.id}-home`;
+ controls.showTab.call(this.player, id);
+ };
+
// Settings menu items - use event delegation as items are added/removed
if (utils.matches(event.target, this.player.config.selectors.inputs.language)) {
proxy(
event,
() => {
this.player.language = event.target.value;
+ showHomeTab();
},
'language',
);
@@ -506,6 +510,7 @@ class Listeners {
event,
() => {
this.player.quality = event.target.value;
+ showHomeTab();
},
'quality',
);
@@ -514,11 +519,13 @@ class Listeners {
event,
() => {
this.player.speed = parseFloat(event.target.value);
+ showHomeTab();
},
'speed',
);
} else {
- controls.showTab.call(this.player, event);
+ const tab = event.target;
+ controls.showTab.call(this.player, tab.getAttribute('aria-controls'));
}
});
diff --git a/src/js/media.js b/src/js/media.js
index bba2c62b..99fc5e85 100644
--- a/src/js/media.js
+++ b/src/js/media.js
@@ -2,15 +2,10 @@
// Plyr Media
// ==========================================================================
-import support from './support';
-import utils from './utils';
-import youtube from './plugins/youtube';
-import vimeo from './plugins/vimeo';
import html5 from './html5';
-import ui from './ui';
-
-// Sniff out the browser
-const browser = utils.getBrowser();
+import vimeo from './plugins/vimeo';
+import youtube from './plugins/youtube';
+import utils from './utils';
const media = {
// Setup media
@@ -33,23 +28,6 @@ const media = {
utils.toggleClass(this.elements.container, this.config.classNames.type.replace('{0}', 'video'), true);
}
- if (this.supported.ui) {
- // Check for picture-in-picture support
- utils.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);
-
- // If there's no autoplay attribute, assume the video is stopped and add state class
- utils.toggleClass(this.elements.container, this.config.classNames.stopped, this.config.autoplay);
-
- // Add iOS class
- utils.toggleClass(this.elements.container, this.config.classNames.isIos, browser.isIos);
-
- // Add touch class
- utils.toggleClass(this.elements.container, this.config.classNames.isTouch, this.touch);
- }
-
// Inject the player wrapper
if (this.isVideo) {
// Create the wrapper div
@@ -59,6 +37,13 @@ const media = {
// Wrap the video in a container
utils.wrap(this.media, this.elements.wrapper);
+
+ // Faux poster container
+ this.elements.poster = utils.createElement('span', {
+ class: this.config.classNames.poster,
+ });
+
+ this.elements.wrapper.appendChild(this.elements.poster);
}
if (this.isEmbed) {
@@ -75,8 +60,6 @@ const media = {
break;
}
} else if (this.isHTML5) {
- ui.setTitle.call(this);
-
html5.extend.call(this);
}
},
diff --git a/src/js/plugins/ads.js b/src/js/plugins/ads.js
index b9d9ac1c..4a3deeaa 100644
--- a/src/js/plugins/ads.js
+++ b/src/js/plugins/ads.js
@@ -6,8 +6,8 @@
/* global google */
-import utils from '../utils';
import i18n from '../i18n';
+import utils from '../utils';
class Ads {
/**
@@ -52,7 +52,7 @@ class Ads {
// Check if the Google IMA3 SDK is loaded or load it ourselves
if (!utils.is.object(window.google) || !utils.is.object(window.google.ima)) {
utils
- .loadScript(this.player.config.urls.googleIMA.api)
+ .loadScript(this.player.config.urls.googleIMA.sdk)
.then(() => {
this.ready();
})
@@ -160,6 +160,9 @@ class Ads {
// We only overlay ads as we only support video.
request.forceNonLinearFullSlot = false;
+ // Mute based on current state
+ request.setAdWillPlayMuted(!this.player.muted);
+
this.loader.requestAds(request);
} catch (e) {
this.onAdError(e);
@@ -226,7 +229,7 @@ class Ads {
// Get skippable state
// TODO: Skip button
- // this.manager.getAdSkippableState();
+ // this.player.debug.warn(this.manager.getAdSkippableState());
// Set volume to match player
this.manager.setVolume(this.player.volume);
diff --git a/src/js/plugins/vimeo.js b/src/js/plugins/vimeo.js
index 24003d3f..f1bc123d 100644
--- a/src/js/plugins/vimeo.js
+++ b/src/js/plugins/vimeo.js
@@ -2,10 +2,10 @@
// Vimeo plugin
// ==========================================================================
-import utils from './../utils';
import captions from './../captions';
import controls from './../controls';
import ui from './../ui';
+import utils from './../utils';
const vimeo = {
setup() {
@@ -18,7 +18,7 @@ const vimeo = {
// Load the API if not already
if (!utils.is.object(window.Vimeo)) {
utils
- .loadScript(this.config.urls.vimeo.api)
+ .loadScript(this.config.urls.vimeo.sdk)
.then(() => {
vimeo.ready.call(this);
})
@@ -68,14 +68,14 @@ const vimeo = {
// Get from <div> if needed
if (utils.is.empty(source)) {
- source = player.media.getAttribute(this.config.attributes.embed.id);
+ source = player.media.getAttribute(player.config.attributes.embed.id);
}
const id = utils.parseVimeoId(source);
// Build an iframe
const iframe = utils.createElement('iframe');
- const src = `https://player.vimeo.com/video/${id}?${params}`;
+ const src = utils.format(player.config.urls.vimeo.iframe, id, params);
iframe.setAttribute('src', src);
iframe.setAttribute('allowfullscreen', '');
iframe.setAttribute('allowtransparency', '');
@@ -86,6 +86,25 @@ const vimeo = {
wrapper.appendChild(iframe);
player.media = utils.replaceElement(wrapper, player.media);
+ // Get poster image
+ utils.fetch(utils.format(player.config.urls.vimeo.api, id), 'json').then(response => {
+ if (utils.is.empty(response)) {
+ return;
+ }
+
+ // Get the URL for thumbnail
+ const url = new URL(response[0].thumbnail_large);
+
+ // Get original image
+ url.pathname = `${url.pathname.split('_')[0]}.jpg`;
+
+ // Set attribute
+ player.media.setAttribute('poster', url.href);
+
+ // Update
+ ui.setPoster.call(player);
+ });
+
// Setup instance
// https://github.com/vimeo/player.js
player.embed = new window.Vimeo.Player(iframe);
diff --git a/src/js/plugins/youtube.js b/src/js/plugins/youtube.js
index 12bc2b11..4fde9319 100644
--- a/src/js/plugins/youtube.js
+++ b/src/js/plugins/youtube.js
@@ -2,9 +2,9 @@
// YouTube plugin
// ==========================================================================
-import utils from './../utils';
import controls from './../controls';
import ui from './../ui';
+import utils from './../utils';
// Standardise YouTube quality unit
function mapQualityUnit(input) {
@@ -77,7 +77,7 @@ const youtube = {
youtube.ready.call(this);
} else {
// Load the API
- utils.loadScript(this.config.urls.youtube.api).catch(error => {
+ utils.loadScript(this.config.urls.youtube.sdk).catch(error => {
this.debug.warn('YouTube API failed to load', error);
});
@@ -117,7 +117,7 @@ const youtube = {
// Or via Google API
const key = this.config.keys.google;
if (utils.is.string(key) && !utils.is.empty(key)) {
- const url = `https://www.googleapis.com/youtube/v3/videos?id=${videoId}&key=${key}&fields=items(snippet(title))&part=snippet`;
+ const url = utils.format(this.config.urls.youtube.api, videoId, key);
utils
.fetch(url)
@@ -161,6 +161,9 @@ const youtube = {
const container = utils.createElement('div', { id });
player.media = utils.replaceElement(container, player.media);
+ // Set poster image
+ player.media.setAttribute('poster', utils.format(player.config.urls.youtube.poster, videoId));
+
// Setup instance
// https://developers.google.com/youtube/iframe_api_reference
player.embed = new window.YT.Player(id, {
diff --git a/src/js/plyr.js b/src/js/plyr.js
index 6daa403a..e77a7562 100644
--- a/src/js/plyr.js
+++ b/src/js/plyr.js
@@ -1,26 +1,24 @@
// ==========================================================================
// Plyr
-// plyr.js v3.2.4
+// plyr.js v3.3.0
// https://github.com/sampotts/plyr
// License: The MIT License (MIT)
// ==========================================================================
-import { providers, types } from './types';
-import defaults from './defaults';
-import support from './support';
-import utils from './utils';
-
+import captions from './captions';
import Console from './console';
+import controls from './controls';
+import defaults from './defaults';
import Fullscreen from './fullscreen';
import Listeners from './listeners';
-import Storage from './storage';
-import Ads from './plugins/ads';
-
-import captions from './captions';
-import controls from './controls';
import media from './media';
+import Ads from './plugins/ads';
import source from './source';
+import Storage from './storage';
+import support from './support';
+import { providers, types } from './types';
import ui from './ui';
+import utils from './utils';
// Private properties
// TODO: Use a WeakMap for private globals
@@ -134,17 +132,9 @@ class Plyr {
}
// Cache original element state for .destroy()
- // TODO: Investigate a better solution as I suspect this causes reported double load issues?
- setTimeout(() => {
- const clone = this.media.cloneNode(true);
-
- // Prevent the clone autoplaying
- if (clone.getAttribute('autoplay')) {
- clone.pause();
- }
-
- this.elements.original = clone;
- }, 0);
+ const clone = this.media.cloneNode(true);
+ clone.autoplay = false;
+ this.elements.original = clone;
// Set media type based on tag or data attribute
// Supported: video, audio, vimeo, youtube
@@ -364,6 +354,13 @@ class Plyr {
}
/**
+ * Get playing state
+ */
+ get playing() {
+ return Boolean(this.ready && !this.paused && !this.ended);
+ }
+
+ /**
* Get paused state
*/
get paused() {
@@ -371,10 +368,10 @@ class Plyr {
}
/**
- * Get playing state
+ * Get stopped state
*/
- get playing() {
- return Boolean(this.ready && !this.paused && !this.ended && (this.isHTML5 ? this.media.readyState > 2 : true));
+ get stopped() {
+ return Boolean(this.paused && this.currentTime === 0);
}
/**
@@ -799,17 +796,18 @@ class Plyr {
}
/**
- * Set the poster image for a HTML5 video
+ * Set the poster image for a video
* @param {input} - the URL for the new poster image
*/
set poster(input) {
- if (!this.isHTML5 || !this.isVideo) {
- this.debug.warn('Poster can only be set on HTML5 video');
+ if (!this.isVideo) {
+ this.debug.warn('Poster can only be set for video');
return;
}
if (utils.is.string(input)) {
this.media.setAttribute('poster', input);
+ ui.setPoster.call(this);
}
}
@@ -817,7 +815,7 @@ class Plyr {
* Get the current poster image
*/
get poster() {
- if (!this.isHTML5 || !this.isVideo) {
+ if (!this.isVideo) {
return null;
}
diff --git a/src/js/plyr.polyfilled.js b/src/js/plyr.polyfilled.js
index a4fd7afa..eae4091a 100644
--- a/src/js/plyr.polyfilled.js
+++ b/src/js/plyr.polyfilled.js
@@ -1,14 +1,12 @@
// ==========================================================================
// Plyr Polyfilled Build
-// plyr.js v3.2.4
+// plyr.js v3.3.0
// https://github.com/sampotts/plyr
// License: The MIT License (MIT)
// ==========================================================================
import 'babel-polyfill';
-
import 'custom-event-polyfill';
-
import Plyr from './plyr';
export default Plyr;
diff --git a/src/js/ui.js b/src/js/ui.js
index ee77a2dd..609d6ab5 100644
--- a/src/js/ui.js
+++ b/src/js/ui.js
@@ -2,10 +2,14 @@
// Plyr UI
// ==========================================================================
-import utils from './utils';
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();
const ui = {
addStyleHook() {
@@ -78,6 +82,18 @@ const ui = {
// Update the 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);
+
+ // Check for airplay support
+ utils.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);
+
+ // Add touch class
+ utils.toggleClass(this.elements.container, this.config.classNames.isTouch, this.touch);
+
// Ready for API calls
this.ready = true;
@@ -88,6 +104,9 @@ const ui = {
// Set the title
ui.setTitle.call(this);
+
+ // Set the poster image
+ ui.setPoster.call(this);
},
// Setup aria attribute for play and iframe title
@@ -121,16 +140,29 @@ const ui = {
// Default to media type
const title = !utils.is.empty(this.config.title) ? this.config.title : 'video';
+ const format = i18n.get('frameTitle', this.config);
+
+ iframe.setAttribute('title', format.replace('{title}', title));
+ }
+ },
- iframe.setAttribute('title', i18n.get('frameTitle', this.config));
+ // Set the poster image
+ setPoster() {
+ if (!utils.is.element(this.elements.poster) || utils.is.empty(this.poster)) {
+ return;
}
+
+ // Set the inline style
+ const posters = this.poster.split(',');
+ this.elements.poster.style.backgroundImage = posters.map(p => `url('${p}')`).join(',');
},
// Check playing state
checkPlaying() {
// Class hooks
utils.toggleClass(this.elements.container, this.config.classNames.playing, this.playing);
- utils.toggleClass(this.elements.container, this.config.classNames.stopped, this.paused);
+ 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);
diff --git a/src/js/utils.js b/src/js/utils.js
index fca40f53..2c06e6aa 100644
--- a/src/js/utils.js
+++ b/src/js/utils.js
@@ -3,7 +3,6 @@
// ==========================================================================
import loadjs from 'loadjs';
-
import support from './support';
import { providers } from './types';
@@ -269,14 +268,14 @@ const utils = {
parent.appendChild(utils.createElement(type, attributes, text));
},
- // Remove an element
+ // Remove element(s)
removeElement(element) {
- if (!utils.is.element(element) || !utils.is.element(element.parentNode)) {
+ if (utils.is.nodeList(element) || utils.is.array(element)) {
+ Array.from(element).forEach(utils.removeElement);
return;
}
- if (utils.is.nodeList(element) || utils.is.array(element)) {
- Array.from(element).forEach(utils.removeElement);
+ if (!utils.is.element(element) || !utils.is.element(element.parentNode)) {
return;
}
@@ -435,60 +434,6 @@ const utils = {
return this.elements.container.querySelector(selector);
},
- // Find the UI controls and store references in custom controls
- // TODO: Allow settings menus with custom controls
- findElements() {
- try {
- this.elements.controls = utils.getElement.call(this, this.config.selectors.controls.wrapper);
-
- // Buttons
- this.elements.buttons = {
- play: utils.getElements.call(this, this.config.selectors.buttons.play),
- pause: utils.getElement.call(this, this.config.selectors.buttons.pause),
- restart: utils.getElement.call(this, this.config.selectors.buttons.restart),
- rewind: utils.getElement.call(this, this.config.selectors.buttons.rewind),
- fastForward: utils.getElement.call(this, this.config.selectors.buttons.fastForward),
- mute: utils.getElement.call(this, this.config.selectors.buttons.mute),
- pip: utils.getElement.call(this, this.config.selectors.buttons.pip),
- airplay: utils.getElement.call(this, this.config.selectors.buttons.airplay),
- settings: utils.getElement.call(this, this.config.selectors.buttons.settings),
- captions: utils.getElement.call(this, this.config.selectors.buttons.captions),
- fullscreen: utils.getElement.call(this, this.config.selectors.buttons.fullscreen),
- };
-
- // Progress
- this.elements.progress = utils.getElement.call(this, this.config.selectors.progress);
-
- // Inputs
- this.elements.inputs = {
- seek: utils.getElement.call(this, this.config.selectors.inputs.seek),
- volume: utils.getElement.call(this, this.config.selectors.inputs.volume),
- };
-
- // Display
- this.elements.display = {
- buffer: utils.getElement.call(this, this.config.selectors.display.buffer),
- currentTime: utils.getElement.call(this, this.config.selectors.display.currentTime),
- duration: utils.getElement.call(this, this.config.selectors.display.duration),
- };
-
- // Seek tooltip
- if (utils.is.element(this.elements.progress)) {
- this.elements.display.seekTooltip = this.elements.progress.querySelector(`.${this.config.classNames.tooltip}`);
- }
-
- return true;
- } catch (error) {
- // Log it
- this.debug.warn('It looks like there is a problem with your custom controls HTML', error);
-
- // Restore native video controls
- this.toggleNativeControls(true);
-
- return false;
- }
- },
-
// Get the focused element
getFocusElement() {
let focused = document.activeElement;
@@ -632,6 +577,15 @@ const utils = {
element.setAttribute('aria-pressed', state);
},
+ // Format string
+ format(input, ...args) {
+ if (utils.is.empty(input)) {
+ return input;
+ }
+
+ return input.toString().replace(/{(\d+)}/g, (match, i) => utils.is.string(args[i]) ? args[i] : '');
+ },
+
// Get percentage
getPercentage(current, max) {
if (current === 0 || max === 0 || Number.isNaN(current) || Number.isNaN(max)) {