aboutsummaryrefslogtreecommitdiffstats
path: root/src/js/plyr.js
diff options
context:
space:
mode:
authorSam Potts <sam@potts.es>2019-01-29 21:34:40 +1100
committerSam Potts <sam@potts.es>2019-01-29 21:34:40 +1100
commitb1da599b5d5891dc1dca44bd6aa9d8d03872fdcb (patch)
treec799fb2b444482f6d99dcdf3f16a957b290888c0 /src/js/plyr.js
parentafc969bac322f9b17dc0554a65fa848eb998c8e6 (diff)
parentb798368ba68853558819d79a995aa0deec27f95e (diff)
downloadplyr-b1da599b5d5891dc1dca44bd6aa9d8d03872fdcb.tar.lz
plyr-b1da599b5d5891dc1dca44bd6aa9d8d03872fdcb.tar.xz
plyr-b1da599b5d5891dc1dca44bd6aa9d8d03872fdcb.zip
Merge branch 'develop' into beta
Diffstat (limited to 'src/js/plyr.js')
-rw-r--r--src/js/plyr.js573
1 files changed, 237 insertions, 336 deletions
diff --git a/src/js/plyr.js b/src/js/plyr.js
index c2a1d6e3..927eb33d 100644
--- a/src/js/plyr.js
+++ b/src/js/plyr.js
@@ -1,24 +1,32 @@
// ==========================================================================
// Plyr
-// plyr.js v3.3.6
+// plyr.js v3.4.8
// https://github.com/sampotts/plyr
// License: The MIT License (MIT)
// ==========================================================================
import captions from './captions';
+import defaults from './config/defaults';
+import { pip } from './config/states';
+import { getProviderByUrl, providers, types } from './config/types';
import Console from './console';
import controls from './controls';
-import defaults from './defaults';
import Fullscreen from './fullscreen';
import Listeners from './listeners';
import media from './media';
import Ads from './plugins/ads';
+import PreviewThumbnails from './plugins/previewThumbnails';
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';
+import { closest } from './utils/arrays';
+import { createElement, hasClass, removeElement, replaceElement, toggleClass, wrap } from './utils/elements';
+import { off, on, once, triggerEvent, unbindListeners } from './utils/events';
+import is from './utils/is';
+import loadSprite from './utils/loadSprite';
+import { cloneDeep, extend } from './utils/objects';
+import { parseUrl } from './utils/urls';
// Private properties
// TODO: Use a WeakMap for private globals
@@ -41,20 +49,21 @@ class Plyr {
this.media = target;
// String selector passed
- if (utils.is.string(this.media)) {
+ if (is.string(this.media)) {
this.media = document.querySelectorAll(this.media);
}
// jQuery, NodeList or Array passed, use first element
- if ((window.jQuery && this.media instanceof jQuery) || utils.is.nodeList(this.media) || utils.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];
}
// Set config
- this.config = utils.extend(
+ this.config = extend(
{},
defaults,
+ Plyr.defaults,
options || {},
(() => {
try {
@@ -68,22 +77,24 @@ class Plyr {
// Elements cache
this.elements = {
container: null,
+ captions: null,
buttons: {},
display: {},
progress: {},
inputs: {},
settings: {
+ popup: null,
menu: null,
- panes: {},
- tabs: {},
+ panels: {},
+ buttons: {},
},
- captions: null,
};
// Captions
this.captions = {
active: null,
- currentTrack: null,
+ currentTrack: -1,
+ meta: new WeakMap(),
};
// Fullscreen
@@ -95,7 +106,6 @@ class Plyr {
this.options = {
speed: [],
quality: [],
- captions: [],
};
// Debugging
@@ -107,7 +117,7 @@ class Plyr {
this.debug.log('Support', support);
// We need an element to setup
- if (utils.is.nullOrUndefined(this.media) || !utils.is.element(this.media)) {
+ if (is.nullOrUndefined(this.media) || !is.element(this.media)) {
this.debug.error('Setup failed: no suitable element passed');
return;
}
@@ -143,7 +153,6 @@ class Plyr {
// Embed properties
let iframe = null;
let url = null;
- let params = null;
// Different setup based on type
switch (type) {
@@ -152,10 +161,10 @@ class Plyr {
iframe = this.media.querySelector('iframe');
// <iframe> type
- if (utils.is.element(iframe)) {
+ if (is.element(iframe)) {
// Detect provider
- url = iframe.getAttribute('src');
- this.provider = utils.getProviderByUrl(url);
+ url = parseUrl(iframe.getAttribute('src'));
+ this.provider = getProviderByUrl(url.toString());
// Rework elements
this.elements.container = this.media;
@@ -165,24 +174,21 @@ class Plyr {
this.elements.container.className = '';
// Get attributes from URL and set config
- params = utils.getUrlParams(url);
- if (!utils.is.empty(params)) {
- const truthy = [
- '1',
- 'true',
- ];
-
- if (truthy.includes(params.autoplay)) {
+ if (url.search.length) {
+ const truthy = ['1', 'true'];
+
+ if (truthy.includes(url.searchParams.get('autoplay'))) {
this.config.autoplay = true;
}
- if (truthy.includes(params.loop)) {
+ if (truthy.includes(url.searchParams.get('loop'))) {
this.config.loop.active = true;
}
// TODO: replace fullscreen.iosNative with this playsinline config option
// YouTube requires the playsinline in the URL
if (this.isYouTube) {
- this.config.playsinline = truthy.includes(params.playsinline);
+ this.config.playsinline = truthy.includes(url.searchParams.get('playsinline'));
+ this.config.youtube.hl = url.searchParams.get('hl'); // TODO: Should this be setting language?
} else {
this.config.playsinline = true;
}
@@ -196,7 +202,7 @@ class Plyr {
}
// Unsupported or missing provider
- if (utils.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;
}
@@ -218,7 +224,7 @@ 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')) {
@@ -244,6 +250,8 @@ class Plyr {
return;
}
+ this.eventListeners = [];
+
// Create listeners
this.listeners = new Listeners(this);
@@ -254,14 +262,11 @@ class Plyr {
this.media.plyr = this;
// Wrap media
- if (!utils.is.element(this.elements.container)) {
- this.elements.container = utils.createElement('div');
- utils.wrap(this.media, this.elements.container);
+ if (!is.element(this.elements.container)) {
+ this.elements.container = createElement('div', { tabindex: 0 });
+ wrap(this.media, this.elements.container);
}
- // Allow focus to be captured
- this.elements.container.setAttribute('tabindex', 0);
-
// Add style hook
ui.addStyleHook.call(this);
@@ -270,7 +275,7 @@ class Plyr {
// Listen for events if debugging
if (this.config.debug) {
- utils.on(this.elements.container, this.config.events.join(' '), event => {
+ on.call(this, this.elements.container, this.config.events.join(' '), event => {
this.debug.log(`event: ${event.type}`);
});
}
@@ -291,12 +296,22 @@ 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) {
this.play();
}
+
+ // Seek time will be recorded (in listeners.js) so we can prevent hiding controls for a few seconds after seek
+ this.lastSeekTime = 0;
+
+ // Setup preview thumbnails if enabled
+ if (this.config.previewThumbnails.enabled) {
+ this.previewThumbnails = new PreviewThumbnails(this);
+ }
}
// ---------------------------------------
@@ -309,18 +324,23 @@ class Plyr {
get isHTML5() {
return Boolean(this.provider === providers.html5);
}
+
get isEmbed() {
return Boolean(this.isYouTube || this.isVimeo);
}
+
get isYouTube() {
return Boolean(this.provider === providers.youtube);
}
+
get isVimeo() {
return Boolean(this.provider === providers.vimeo);
}
+
get isVideo() {
return Boolean(this.type === types.video);
}
+
get isAudio() {
return Boolean(this.type === types.audio);
}
@@ -329,7 +349,7 @@ class Plyr {
* Play the media, or play the advertisement (if they are not blocked)
*/
play() {
- if (!utils.is.function(this.media.play)) {
+ if (!is.function(this.media.play)) {
return null;
}
@@ -341,7 +361,7 @@ class Plyr {
* Pause the media
*/
pause() {
- if (!this.playing || !utils.is.function(this.media.pause)) {
+ if (!this.playing || !is.function(this.media.pause)) {
return;
}
@@ -382,7 +402,7 @@ class Plyr {
*/
togglePlay(input) {
// Toggle based on current state if nothing passed
- const toggle = utils.is.boolean(input) ? input : !this.playing;
+ const toggle = is.boolean(input) ? input : !this.playing;
if (toggle) {
this.play();
@@ -398,7 +418,7 @@ class Plyr {
if (this.isHTML5) {
this.pause();
this.restart();
- } else if (utils.is.function(this.media.stop)) {
+ } else if (is.function(this.media.stop)) {
this.media.stop();
}
}
@@ -415,7 +435,7 @@ class Plyr {
* @param {number} seekTime - how far to rewind in seconds. Defaults to the config.seekTime
*/
rewind(seekTime) {
- this.currentTime = this.currentTime - (utils.is.number(seekTime) ? seekTime : this.config.seekTime);
+ this.currentTime = this.currentTime - (is.number(seekTime) ? seekTime : this.config.seekTime);
}
/**
@@ -423,7 +443,7 @@ class Plyr {
* @param {number} seekTime - how far to fast forward in seconds. Defaults to the config.seekTime
*/
forward(seekTime) {
- this.currentTime = this.currentTime + (utils.is.number(seekTime) ? seekTime : this.config.seekTime);
+ this.currentTime = this.currentTime + (is.number(seekTime) ? seekTime : this.config.seekTime);
}
/**
@@ -431,21 +451,16 @@ class Plyr {
* @param {number} input - where to seek to in seconds. Defaults to 0 (the start)
*/
set currentTime(input) {
- let targetTime = 0;
-
- if (utils.is.number(input)) {
- targetTime = input;
+ // Bail if media duration isn't available yet
+ if (!this.duration) {
+ return;
}
- // Normalise targetTime
- if (targetTime < 0) {
- targetTime = 0;
- } else if (targetTime > this.duration) {
- targetTime = this.duration;
- }
+ // Validate input
+ const inputIsValid = is.number(input) && input > 0;
// Set
- this.media.currentTime = targetTime;
+ this.media.currentTime = inputIsValid ? Math.min(input, this.duration) : 0;
// Logging
this.debug.log(`Seeking to ${this.currentTime} seconds`);
@@ -465,7 +480,7 @@ class Plyr {
const { buffered } = this.media;
// YouTube / Vimeo return a float between 0-1
- if (utils.is.number(buffered)) {
+ if (is.number(buffered)) {
return buffered;
}
@@ -493,11 +508,12 @@ class Plyr {
// Faux duration set via config
const fauxDuration = parseFloat(this.config.duration);
- // True duration
- const realDuration = this.media ? Number(this.media.duration) : 0;
+ // 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;
- // If custom duration is funky, use regular duration
- return !Number.isNaN(fauxDuration) ? fauxDuration : realDuration;
+ // If config duration is funky, use regular duration
+ return fauxDuration || duration;
}
/**
@@ -509,17 +525,17 @@ class Plyr {
const max = 1;
const min = 0;
- if (utils.is.string(volume)) {
+ if (is.string(volume)) {
volume = Number(volume);
}
// Load volume from storage if no value specified
- if (!utils.is.number(volume)) {
+ if (!is.number(volume)) {
volume = this.storage.get('volume');
}
// Use config if all else fails
- if (!utils.is.number(volume)) {
+ if (!is.number(volume)) {
({ volume } = this.config);
}
@@ -539,7 +555,7 @@ class Plyr {
this.media.volume = volume;
// If muted, and we're increasing volume manually, reset muted state
- if (!utils.is.empty(value) && this.muted && volume > 0) {
+ if (!is.empty(value) && this.muted && volume > 0) {
this.muted = false;
}
}
@@ -557,7 +573,7 @@ class Plyr {
*/
increaseVolume(step) {
const volume = this.media.muted ? 0 : this.volume;
- this.volume = volume + (utils.is.number(step) ? step : 1);
+ this.volume = volume + (is.number(step) ? step : 0);
}
/**
@@ -565,8 +581,7 @@ class Plyr {
* @param {boolean} step - How much to decrease by (between 0 and 1)
*/
decreaseVolume(step) {
- const volume = this.media.muted ? 0 : this.volume;
- this.volume = volume - (utils.is.number(step) ? step : 1);
+ this.increaseVolume(-step);
}
/**
@@ -577,12 +592,12 @@ class Plyr {
let toggle = mute;
// Load muted state from storage
- if (!utils.is.boolean(toggle)) {
+ if (!is.boolean(toggle)) {
toggle = this.storage.get('muted');
}
// Use config if all else fails
- if (!utils.is.boolean(toggle)) {
+ if (!is.boolean(toggle)) {
toggle = this.config.muted;
}
@@ -628,15 +643,15 @@ class Plyr {
set speed(input) {
let speed = null;
- if (utils.is.number(input)) {
+ if (is.number(input)) {
speed = input;
}
- if (!utils.is.number(speed)) {
+ if (!is.number(speed)) {
speed = this.storage.get('speed');
}
- if (!utils.is.number(speed)) {
+ if (!is.number(speed)) {
speed = this.config.speed.selected;
}
@@ -673,39 +688,41 @@ class Plyr {
* @param {number} input - Quality level
*/
set quality(input) {
- let quality = null;
-
- if (!utils.is.empty(input)) {
- quality = Number(input);
- }
+ const config = this.config.quality;
+ const options = this.options.quality;
- if (!utils.is.number(quality) || quality === 0) {
- quality = this.storage.get('quality');
+ if (!options.length) {
+ return;
}
- if (!utils.is.number(quality)) {
- quality = this.config.quality.selected;
- }
+ let quality = [
+ !is.empty(input) && Number(input),
+ this.storage.get('quality'),
+ config.selected,
+ config.default,
+ ].find(is.number);
- if (!utils.is.number(quality)) {
- quality = this.config.quality.default;
- }
+ let updateStorage = true;
- if (!this.options.quality.length) {
- return;
- }
+ if (!options.includes(quality)) {
+ const value = closest(options, quality);
+ this.debug.warn(`Unsupported quality option: ${quality}, using ${value} instead`);
+ quality = value;
- if (!this.options.quality.includes(quality)) {
- const closest = utils.closest(this.options.quality, quality);
- this.debug.warn(`Unsupported quality option: ${quality}, using ${closest} instead`);
- quality = closest;
+ // Don't update storage if quality is not supported
+ updateStorage = false;
}
// Update config
- this.config.quality.selected = quality;
+ config.selected = quality;
// Set quality
this.media.quality = quality;
+
+ // Save to storage
+ if (updateStorage) {
+ this.storage.set({ quality });
+ }
}
/**
@@ -721,7 +738,7 @@ class Plyr {
* @param {boolean} input - Whether to loop or not
*/
set loop(input) {
- const toggle = utils.is.boolean(input) ? input : this.config.loop.active;
+ const toggle = is.boolean(input) ? input : this.config.loop.active;
this.config.loop.active = toggle;
this.media.loop = toggle;
@@ -792,6 +809,15 @@ class Plyr {
}
/**
+ * Get a download URL (either source or custom)
+ */
+ get download() {
+ const { download } = this.config.urls;
+
+ return is.url(download) ? download : this.source;
+ }
+
+ /**
* Set the poster image for a video
* @param {input} - the URL for the new poster image
*/
@@ -801,10 +827,7 @@ class Plyr {
return;
}
- if (utils.is.string(input)) {
- this.media.setAttribute('poster', input);
- ui.setPoster.call(this);
- }
+ ui.setPoster.call(this, input, false).catch(() => {});
}
/**
@@ -823,7 +846,7 @@ class Plyr {
* @param {boolean} input - Whether to autoplay or not
*/
set autoplay(input) {
- const toggle = utils.is.boolean(input) ? input : this.config.autoplay;
+ const toggle = is.boolean(input) ? input : this.config.autoplay;
this.config.autoplay = toggle;
}
@@ -839,88 +862,39 @@ class Plyr {
* @param {boolean} input - Whether to enable captions
*/
toggleCaptions(input) {
- // If there's no full support
- if (!this.supported.ui) {
- return;
- }
-
- // If the method is called without parameter, toggle based on current value
- const show = utils.is.boolean(input) ? input : !this.elements.container.classList.contains(this.config.classNames.captions.active);
-
- // Nothing to change...
- if (this.captions.active === show) {
- return;
- }
-
- // Set global
- this.captions.active = show;
-
- // Toggle state
- utils.toggleState(this.elements.buttons.captions, this.captions.active);
+ captions.toggle.call(this, input, false);
+ }
- // Add class hook
- utils.toggleClass(this.elements.container, this.config.classNames.captions.active, this.captions.active);
+ /**
+ * Set the caption track by index
+ * @param {number} - Caption index
+ */
+ set currentTrack(input) {
+ captions.set.call(this, input, false);
+ }
- // Trigger an event
- utils.dispatchEvent.call(this, this.media, this.captions.active ? 'captionsenabled' : 'captionsdisabled');
+ /**
+ * Get the current caption track index (-1 if disabled)
+ */
+ get currentTrack() {
+ const { toggled, currentTrack } = this.captions;
+ return toggled ? currentTrack : -1;
}
/**
- * Set the captions language
+ * Set the wanted language for captions
+ * Since tracks can be added later it won't update the actual caption track until there is a matching track
* @param {string} - Two character ISO language code (e.g. EN, FR, PT, etc)
*/
set language(input) {
- // Nothing specified
- if (!utils.is.string(input)) {
- return;
- }
-
- // If empty string is passed, assume disable captions
- if (utils.is.empty(input)) {
- this.toggleCaptions(false);
- return;
- }
-
- // Normalize
- const language = input.toLowerCase();
-
- // Check for support
- if (!this.options.captions.includes(language)) {
- this.debug.warn(`Unsupported language option: ${language}`);
- return;
- }
-
- // Ensure captions are enabled
- this.toggleCaptions(true);
-
- // Enabled only
- if (language === 'enabled') {
- return;
- }
-
- // If nothing to change, bail
- if (this.language === language) {
- return;
- }
-
- // Update config
- this.captions.language = language;
-
- // Clear caption
- captions.setText.call(this, null);
-
- // Update captions
- captions.setLanguage.call(this);
-
- // Trigger an event
- utils.dispatchEvent.call(this, this.media, 'languagechange');
+ captions.setLanguage.call(this, input, false);
}
/**
- * Get the current captions language
+ * Get the current track's language
*/
get language() {
- return this.captions.language;
+ return (captions.getCurrentTrack.call(this) || {}).language;
}
/**
@@ -929,21 +903,28 @@ class Plyr {
* TODO: detect outside changes
*/
set pip(input) {
- const states = {
- pip: 'picture-in-picture',
- inline: 'inline',
- };
-
// Bail if no support
if (!support.pip) {
return;
}
// Toggle based on current state if not passed
- const toggle = utils.is.boolean(input) ? input : this.pip === states.inline;
+ const toggle = is.boolean(input) ? input : !this.pip;
// Toggle based on current state
- this.media.webkitSetPresentationMode(toggle ? states.pip : states.inline);
+ // Safari
+ if (is.function(this.media.webkitSetPresentationMode)) {
+ this.media.webkitSetPresentationMode(toggle ? pip.active : pip.inactive);
+ }
+
+ // Chrome
+ if (is.function(this.media.requestPictureInPicture)) {
+ if (!this.pip && toggle) {
+ this.media.requestPictureInPicture();
+ } else if (this.pip && !toggle) {
+ document.exitPictureInPicture();
+ }
+ }
}
/**
@@ -954,7 +935,13 @@ class Plyr {
return null;
}
- return this.media.webkitPresentationMode;
+ // Safari
+ if (!is.empty(this.media.webkitPresentationMode)) {
+ return this.media.webkitPresentationMode === pip.active;
+ }
+
+ // Chrome
+ return this.media === document.pictureInPictureElement;
}
/**
@@ -970,119 +957,35 @@ class Plyr {
/**
* Toggle the player controls
- * @param {boolean} toggle - Whether to show the controls
+ * @param {boolean} [toggle] - Whether to show the controls
*/
toggleControls(toggle) {
- // We need controls of course...
- if (!utils.is.element(this.elements.controls)) {
- return;
- }
-
- // Don't hide if no UI support or it's audio
- if (!this.supported.ui || this.isAudio) {
- return;
- }
+ // 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);
- let delay = 0;
- let show = toggle;
- let isEnterFullscreen = false;
-
- // Get toggle state if not set
- if (!utils.is.boolean(toggle)) {
- if (utils.is.event(toggle)) {
- // Is the enter fullscreen event
- isEnterFullscreen = toggle.type === 'enterfullscreen';
-
- // Events that show the controls
- const showEvents = [
- 'touchstart',
- 'touchmove',
- 'mouseenter',
- 'mousemove',
- 'focusin',
- ];
-
- // Events that delay hiding
- const delayEvents = [
- 'touchmove',
- 'touchend',
- 'mousemove',
- ];
-
- // Whether to show controls
- show = showEvents.includes(toggle.type);
-
- // Delay hiding on move events
- if (delayEvents.includes(toggle.type)) {
- delay = 2000;
- }
-
- // Delay a little more for keyboard users
- if (!this.touch && toggle.type === 'focusin') {
- delay = 3000;
- utils.toggleClass(this.elements.controls, this.config.classNames.noTransition, true);
- }
- } else {
- show = utils.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;
- // Clear timer on every call
- clearTimeout(this.timers.controls);
+ // Apply and get updated state
+ const hiding = toggleClass(this.elements.container, this.config.classNames.hideControls, force);
- // If the mouse is not over the controls, set a timeout to hide them
- if (show || this.paused || this.loading) {
- // Check if controls toggled
- const toggled = utils.toggleClass(this.elements.container, this.config.classNames.hideControls, false);
-
- // Trigger event
- if (toggled) {
- utils.dispatchEvent.call(this, this.media, 'controlsshown');
+ // Close menu
+ if (hiding && this.config.controls.includes('settings') && !is.empty(this.config.settings)) {
+ controls.toggleMenu.call(this, false);
}
- // Always show controls when paused or if touch
- if (this.paused || this.loading) {
- return;
+ // Trigger event on change
+ if (hiding !== isHidden) {
+ const eventName = hiding ? 'controlshidden' : 'controlsshown';
+ triggerEvent.call(this, this.media, eventName);
}
- // Delay for hiding on touch
- if (this.touch) {
- delay = 3000;
- }
+ return !hiding;
}
- // If toggle is false or if we're playing (regardless of toggle),
- // then set the timer to hide the controls
- if (!show || this.playing) {
- this.timers.controls = setTimeout(() => {
- // We need controls of course...
- if (!utils.is.element(this.elements.controls)) {
- return;
- }
-
- // If the mouse is over the controls (and not entering fullscreen), bail
- if ((this.elements.controls.pressed || this.elements.controls.hover) && !isEnterFullscreen) {
- return;
- }
-
- // Restore transition behaviour
- if (!utils.hasClass(this.elements.container, this.config.classNames.hideControls)) {
- utils.toggleClass(this.elements.controls, this.config.classNames.noTransition, false);
- }
-
- // Set hideControls class
- const toggled = utils.toggleClass(this.elements.container, this.config.classNames.hideControls, this.config.hideControls);
-
- // Trigger event and close menu
- if (toggled) {
- utils.dispatchEvent.call(this, this.media, 'controlshidden');
-
- if (this.config.controls.includes('settings') && !utils.is.empty(this.config.settings)) {
- controls.toggleMenu.call(this, false);
- }
- }
- }, delay);
- }
+ return false;
}
/**
@@ -1091,7 +994,16 @@ class Plyr {
* @param {function} callback - Callback for when event occurs
*/
on(event, callback) {
- utils.on(this.elements.container, event, callback);
+ on.call(this, this.elements.container, event, callback);
+ }
+
+ /**
+ * Add event listeners once
+ * @param {string} event - Event type
+ * @param {function} callback - Callback for when event occurs
+ */
+ once(event, callback) {
+ once.call(this, this.elements.container, event, callback);
}
/**
@@ -1100,7 +1012,7 @@ class Plyr {
* @param {function} callback - Callback for when event occurs
*/
off(event, callback) {
- utils.off(this.elements.container, event, callback);
+ off(this.elements.container, event, callback);
}
/**
@@ -1126,10 +1038,10 @@ class Plyr {
if (soft) {
if (Object.keys(this.elements).length) {
// Remove elements
- utils.removeElement(this.elements.buttons.play);
- utils.removeElement(this.elements.captions);
- utils.removeElement(this.elements.controls);
- utils.removeElement(this.elements.wrapper);
+ removeElement(this.elements.buttons.play);
+ removeElement(this.elements.captions);
+ removeElement(this.elements.controls);
+ removeElement(this.elements.wrapper);
// Clear for GC
this.elements.buttons.play = null;
@@ -1139,21 +1051,21 @@ class Plyr {
}
// Callback
- if (utils.is.function(callback)) {
+ if (is.function(callback)) {
callback();
}
} else {
// Unbind listeners
- this.listeners.clear();
+ unbindListeners.call(this);
// Replace the container with the original element provided
- utils.replaceElement(this.elements.original, this.elements.container);
+ replaceElement(this.elements.original, this.elements.container);
// Event
- utils.dispatchEvent.call(this, this.elements.original, 'destroyed', true);
+ triggerEvent.call(this, this.elements.original, 'destroyed', true);
// Callback
- if (utils.is.function(callback)) {
+ if (is.function(callback)) {
callback.call(this.elements.original);
}
@@ -1171,50 +1083,37 @@ class Plyr {
// Stop playback
this.stop();
- // Type specific stuff
- switch (`${this.provider}:${this.type}`) {
- case 'html5:video':
- case 'html5:audio':
- // Clear timeout
- clearTimeout(this.timers.loading);
-
- // Restore native video controls
- ui.toggleNativeControls.call(this, true);
-
- // Clean up
- done();
-
- break;
-
- case 'youtube:video':
- // Clear timers
- clearInterval(this.timers.buffering);
- clearInterval(this.timers.playing);
-
- // Destroy YouTube API
- if (this.embed !== null && utils.is.function(this.embed.destroy)) {
- this.embed.destroy();
- }
-
- // Clean up
- done();
-
- break;
-
- case 'vimeo:video':
- // Destroy Vimeo API
- // then clean up (wait, to prevent postmessage errors)
- if (this.embed !== null) {
- this.embed.unload().then(done);
- }
-
- // Vimeo does not always return
- setTimeout(done, 200);
+ // Provider specific stuff
+ if (this.isHTML5) {
+ // Clear timeout
+ clearTimeout(this.timers.loading);
+
+ // Restore native video controls
+ ui.toggleNativeControls.call(this, true);
+
+ // Clean up
+ done();
+ } else if (this.isYouTube) {
+ // Clear timers
+ clearInterval(this.timers.buffering);
+ clearInterval(this.timers.playing);
+
+ // Destroy YouTube API
+ if (this.embed !== null && is.function(this.embed.destroy)) {
+ this.embed.destroy();
+ }
- break;
+ // Clean up
+ done();
+ } else if (this.isVimeo) {
+ // Destroy Vimeo API
+ // then clean up (wait, to prevent postmessage errors)
+ if (this.embed !== null) {
+ this.embed.unload().then(done);
+ }
- default:
- break;
+ // Vimeo does not always return
+ setTimeout(done, 200);
}
}
@@ -1242,7 +1141,7 @@ class Plyr {
* @param {string} [id] - Unique ID
*/
static loadSprite(url, id) {
- return utils.loadSprite(url, id);
+ return loadSprite(url, id);
}
/**
@@ -1253,15 +1152,15 @@ class Plyr {
static setup(selector, options = {}) {
let targets = null;
- if (utils.is.string(selector)) {
+ if (is.string(selector)) {
targets = Array.from(document.querySelectorAll(selector));
- } else if (utils.is.nodeList(selector)) {
+ } else if (is.nodeList(selector)) {
targets = Array.from(selector);
- } else if (utils.is.array(selector)) {
- targets = selector.filter(i => utils.is.element(i));
+ } else if (is.array(selector)) {
+ targets = selector.filter(is.element);
}
- if (utils.is.empty(targets)) {
+ if (is.empty(targets)) {
return null;
}
@@ -1269,4 +1168,6 @@ class Plyr {
}
}
+Plyr.defaults = cloneDeep(defaults);
+
export default Plyr;