diff options
Diffstat (limited to 'src/js/plyr.js')
-rw-r--r-- | src/js/plyr.js | 202 |
1 files changed, 138 insertions, 64 deletions
diff --git a/src/js/plyr.js b/src/js/plyr.js index 90bf00c8..f30d334a 100644 --- a/src/js/plyr.js +++ b/src/js/plyr.js @@ -1,6 +1,6 @@ // ========================================================================== // Plyr -// plyr.js v3.5.0-beta.5 +// plyr.js v3.5.6 // https://github.com/sampotts/plyr // License: The MIT License (MIT) // ========================================================================== @@ -15,7 +15,7 @@ import Fullscreen from './fullscreen'; import Listeners from './listeners'; import media from './media'; import Ads from './plugins/ads'; -import PreviewThumbnails from './plugins/previewThumbnails'; +import PreviewThumbnails from './plugins/preview-thumbnails'; import source from './source'; import Storage from './storage'; import support from './support'; @@ -24,8 +24,10 @@ 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 loadSprite from './utils/load-sprite'; +import { clamp } from './utils/numbers'; import { cloneDeep, extend } from './utils/objects'; +import { getAspectRatio, reduceAspectRatio, setAspectRatio, validateRatio } from './utils/style'; import { parseUrl } from './utils/urls'; // Private properties @@ -149,7 +151,6 @@ class Plyr { // Set media type based on tag or data attribute // Supported: video, audio, vimeo, youtube const type = this.media.tagName.toLowerCase(); - // Embed properties let iframe = null; let url = null; @@ -301,8 +302,8 @@ class Plyr { } // Autoplay if required - if (this.config.autoplay) { - this.play(); + if (this.isHTML5 && this.config.autoplay) { + setTimeout(() => this.play(), 10); } // Seek time will be recorded (in listeners.js) so we can prevent hiding controls for a few seconds after seek @@ -322,27 +323,27 @@ class Plyr { * Types and provider helpers */ get isHTML5() { - return Boolean(this.provider === providers.html5); + return this.provider === providers.html5; } get isEmbed() { - return Boolean(this.isYouTube || this.isVimeo); + return this.isYouTube || this.isVimeo; } get isYouTube() { - return Boolean(this.provider === providers.youtube); + return this.provider === providers.youtube; } get isVimeo() { - return Boolean(this.provider === providers.vimeo); + return this.provider === providers.vimeo; } get isVideo() { - return Boolean(this.type === types.video); + return this.type === types.video; } get isAudio() { - return Boolean(this.type === types.audio); + return this.type === types.audio; } /** @@ -403,7 +404,7 @@ class Plyr { /** * Toggle playback based on current status - * @param {boolean} input + * @param {Boolean} input */ togglePlay(input) { // Toggle based on current state if nothing passed @@ -437,7 +438,7 @@ class Plyr { /** * Rewind - * @param {number} seekTime - how far to rewind in seconds. Defaults to the config.seekTime + * @param {Number} seekTime - how far to rewind in seconds. Defaults to the config.seekTime */ rewind(seekTime) { this.currentTime = this.currentTime - (is.number(seekTime) ? seekTime : this.config.seekTime); @@ -445,7 +446,7 @@ class Plyr { /** * Fast forward - * @param {number} seekTime - how far to fast forward in seconds. Defaults to the config.seekTime + * @param {Number} seekTime - how far to fast forward in seconds. Defaults to the config.seekTime */ forward(seekTime) { this.currentTime = this.currentTime + (is.number(seekTime) ? seekTime : this.config.seekTime); @@ -453,7 +454,7 @@ class Plyr { /** * Seek to a time - * @param {number} input - where to seek to in seconds. Defaults to 0 (the start) + * @param {Number} input - where to seek to in seconds. Defaults to 0 (the start) */ set currentTime(input) { // Bail if media duration isn't available yet @@ -512,7 +513,6 @@ class Plyr { get duration() { // Faux duration set via config const fauxDuration = parseFloat(this.config.duration); - // 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; @@ -523,7 +523,7 @@ class Plyr { /** * Set the player volume - * @param {number} value - must be between 0 and 1. Defaults to the value from local storage and config.volume if not set in storage + * @param {Number} value - must be between 0 and 1. Defaults to the value from local storage and config.volume if not set in storage */ set volume(value) { let volume = value; @@ -574,7 +574,7 @@ class Plyr { /** * Increase volume - * @param {boolean} step - How much to decrease by (between 0 and 1) + * @param {Boolean} step - How much to decrease by (between 0 and 1) */ increaseVolume(step) { const volume = this.media.muted ? 0 : this.volume; @@ -583,7 +583,7 @@ class Plyr { /** * Decrease volume - * @param {boolean} step - How much to decrease by (between 0 and 1) + * @param {Boolean} step - How much to decrease by (between 0 and 1) */ decreaseVolume(step) { this.increaseVolume(-step); @@ -591,7 +591,7 @@ class Plyr { /** * Set muted state - * @param {boolean} mute + * @param {Boolean} mute */ set muted(mute) { let toggle = mute; @@ -643,7 +643,7 @@ class Plyr { /** * Set playback speed - * @param {number} speed - the speed of playback (0.5-2.0) + * @param {Number} speed - the speed of playback (0.5-2.0) */ set speed(input) { let speed = null; @@ -660,24 +660,17 @@ class Plyr { speed = this.config.speed.selected; } - // Set min/max - if (speed < 0.1) { - speed = 0.1; - } - if (speed > 2.0) { - speed = 2.0; - } - - if (!this.config.speed.options.includes(speed)) { - this.debug.warn(`Unsupported speed (${speed})`); - return; - } + // Clamp to min/max + const { minimumSpeed: min, maximumSpeed: max } = this; + speed = clamp(speed, min, max); // Update config this.config.speed.selected = speed; // Set media speed - this.media.playbackRate = speed; + setTimeout(() => { + this.media.playbackRate = speed; + }, 0); } /** @@ -688,9 +681,45 @@ class Plyr { } /** + * Get the minimum allowed speed + */ + get minimumSpeed() { + if (this.isYouTube) { + // https://developers.google.com/youtube/iframe_api_reference#setPlaybackRate + return Math.min(...this.options.speed); + } + + if (this.isVimeo) { + // https://github.com/vimeo/player.js/#setplaybackrateplaybackrate-number-promisenumber-rangeerrorerror + return 0.5; + } + + // https://stackoverflow.com/a/32320020/1191319 + return 0.0625; + } + + /** + * Get the maximum allowed speed + */ + get maximumSpeed() { + if (this.isYouTube) { + // https://developers.google.com/youtube/iframe_api_reference#setPlaybackRate + return Math.max(...this.options.speed); + } + + if (this.isVimeo) { + // https://github.com/vimeo/player.js/#setplaybackrateplaybackrate-number-promisenumber-rangeerrorerror + return 2; + } + + // https://stackoverflow.com/a/32320020/1191319 + return 16; + } + + /** * Set playback quality * Currently HTML5 & YouTube only - * @param {number} input - Quality level + * @param {Number} input - Quality level */ set quality(input) { const config = this.config.quality; @@ -740,7 +769,7 @@ class Plyr { /** * Toggle loop * TODO: Finish fancy new logic. Set the indicator on load as user may pass loop as config - * @param {boolean} input - Whether to loop or not + * @param {Boolean} input - Whether to loop or not */ set loop(input) { const toggle = is.boolean(input) ? input : this.config.loop.active; @@ -800,7 +829,7 @@ class Plyr { /** * Set new media source - * @param {object} input - The new source object (see docs) + * @param {Object} input - The new source object (see docs) */ set source(input) { source.change.call(this, input); @@ -823,8 +852,21 @@ class Plyr { } /** + * Set the download URL + */ + set download(input) { + if (!is.url(input)) { + return; + } + + this.config.urls.download = input; + + controls.setDownloadUrl.call(this); + } + + /** * Set the poster image for a video - * @param {input} - the URL for the new poster image + * @param {String} input - the URL for the new poster image */ set poster(input) { if (!this.isVideo) { @@ -847,8 +889,40 @@ class Plyr { } /** + * Get the current aspect ratio in use + */ + get ratio() { + if (!this.isVideo) { + return null; + } + + const ratio = reduceAspectRatio(getAspectRatio.call(this)); + + return is.array(ratio) ? ratio.join(':') : ratio; + } + + /** + * Set video aspect ratio + */ + set ratio(input) { + if (!this.isVideo) { + this.debug.warn('Aspect ratio can only be set for video'); + return; + } + + if (!is.string(input) || !validateRatio(input)) { + this.debug.error(`Invalid aspect ratio specified (${input})`); + return; + } + + this.config.ratio = input; + + setAspectRatio.call(this); + } + + /** * Set the autoplay state - * @param {boolean} input - Whether to autoplay or not + * @param {Boolean} input - Whether to autoplay or not */ set autoplay(input) { const toggle = is.boolean(input) ? input : this.config.autoplay; @@ -864,7 +938,7 @@ class Plyr { /** * Toggle captions - * @param {boolean} input - Whether to enable captions + * @param {Boolean} input - Whether to enable captions */ toggleCaptions(input) { captions.toggle.call(this, input, false); @@ -872,7 +946,7 @@ class Plyr { /** * Set the caption track by index - * @param {number} - Caption index + * @param {Number} - Caption index */ set currentTrack(input) { captions.set.call(this, input, false); @@ -889,7 +963,7 @@ class Plyr { /** * 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) + * @param {String} - Two character ISO language code (e.g. EN, FR, PT, etc) */ set language(input) { captions.setLanguage.call(this, input, false); @@ -962,17 +1036,15 @@ class Plyr { /** * Toggle the player controls - * @param {boolean} [toggle] - Whether to show the controls + * @param {Boolean} [toggle] - Whether to show the controls */ toggleControls(toggle) { // 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); - // 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); @@ -995,8 +1067,8 @@ class Plyr { /** * Add event listeners - * @param {string} event - Event type - * @param {function} callback - Callback for when event occurs + * @param {String} event - Event type + * @param {Function} callback - Callback for when event occurs */ on(event, callback) { on.call(this, this.elements.container, event, callback); @@ -1004,8 +1076,8 @@ class Plyr { /** * Add event listeners once - * @param {string} event - Event type - * @param {function} callback - Callback for when event occurs + * @param {String} event - Event type + * @param {Function} callback - Callback for when event occurs */ once(event, callback) { once.call(this, this.elements.container, event, callback); @@ -1013,8 +1085,8 @@ class Plyr { /** * Remove event listeners - * @param {string} event - Event type - * @param {function} callback - Callback for when event occurs + * @param {String} event - Event type + * @param {Function} callback - Callback for when event occurs */ off(event, callback) { off(this.elements.container, event, callback); @@ -1024,8 +1096,8 @@ class Plyr { * Destroy an instance * Event listeners are removed when elements are removed * http://stackoverflow.com/questions/12528049/if-a-dom-element-is-removed-are-its-listeners-also-removed-from-memory - * @param {function} callback - Callback for when destroy is complete - * @param {boolean} soft - Whether it's a soft destroy (for source changes etc) + * @param {Function} callback - Callback for when destroy is complete + * @param {Boolean} soft - Whether it's a soft destroy (for source changes etc) */ destroy(callback, soft = false) { if (!this.ready) { @@ -1088,11 +1160,13 @@ class Plyr { // Stop playback this.stop(); + // Clear timeouts + clearTimeout(this.timers.loading); + clearTimeout(this.timers.controls); + clearTimeout(this.timers.resized); + // Provider specific stuff if (this.isHTML5) { - // Clear timeout - clearTimeout(this.timers.loading); - // Restore native video controls ui.toggleNativeControls.call(this, true); @@ -1124,7 +1198,7 @@ class Plyr { /** * Check for support for a mime type (HTML5 only) - * @param {string} type - Mime type + * @param {String} type - Mime type */ supports(type) { return support.mime.call(this, type); @@ -1132,9 +1206,9 @@ class Plyr { /** * Check for support - * @param {string} type - Player type (audio/video) - * @param {string} provider - Provider (html5/youtube/vimeo) - * @param {bool} inline - Where player has `playsinline` sttribute + * @param {String} type - Player type (audio/video) + * @param {String} provider - Provider (html5/youtube/vimeo) + * @param {Boolean} inline - Where player has `playsinline` sttribute */ static supported(type, provider, inline) { return support.check(type, provider, inline); @@ -1142,8 +1216,8 @@ class Plyr { /** * Load an SVG sprite into the page - * @param {string} url - URL for the SVG sprite - * @param {string} [id] - Unique ID + * @param {String} url - URL for the SVG sprite + * @param {String} [id] - Unique ID */ static loadSprite(url, id) { return loadSprite(url, id); @@ -1152,7 +1226,7 @@ class Plyr { /** * Setup multiple instances * @param {*} selector - * @param {object} options + * @param {Object} options */ static setup(selector, options = {}) { let targets = null; |