diff options
Diffstat (limited to 'src/js')
-rw-r--r-- | src/js/captions.js | 6 | ||||
-rw-r--r-- | src/js/controls.js | 2 | ||||
-rw-r--r-- | src/js/defaults.js | 3 | ||||
-rw-r--r-- | src/js/fullscreen.js | 2 | ||||
-rw-r--r-- | src/js/listeners.js | 4 | ||||
-rw-r--r-- | src/js/media.js | 9 | ||||
-rw-r--r-- | src/js/plugins/vimeo.js | 5 | ||||
-rw-r--r-- | src/js/plugins/youtube.js | 5 | ||||
-rw-r--r-- | src/js/plyr.js | 101 | ||||
-rw-r--r-- | src/js/source.js | 31 | ||||
-rw-r--r-- | src/js/support.js | 18 | ||||
-rw-r--r-- | src/js/types.js | 16 | ||||
-rw-r--r-- | src/js/ui.js | 2 | ||||
-rw-r--r-- | src/js/utils.js | 27 |
14 files changed, 123 insertions, 108 deletions
diff --git a/src/js/captions.js b/src/js/captions.js index ed1dab8b..61ed80bf 100644 --- a/src/js/captions.js +++ b/src/js/captions.js @@ -32,7 +32,7 @@ const captions = { } // Only Vimeo and HTML5 video supported at this point - if (!['video', 'vimeo'].includes(this.type) || (this.type === 'video' && !support.textTracks)) { + if (!this.isVideo || this.isYouTube || (this.isVideo && !support.textTracks)) { // Clear menu and hide if (this.config.controls.includes('settings') && this.config.settings.includes('captions')) { controls.setCaptionsMenu.call(this); @@ -71,7 +71,7 @@ const captions = { // Set the captions language setLanguage() { // Setup HTML5 track rendering - if (this.type === 'video') { + if (this.isVideo) { captions.getTracks.call(this).forEach(track => { // Remove previous bindings utils.on(track, 'cuechange', event => captions.setCue.call(this, event)); @@ -91,7 +91,7 @@ const captions = { captions.setCue.call(this, currentTrack); } } - } else if (this.type === 'vimeo' && this.captions.active) { + } else if (this.isVimeo && this.captions.active) { this.embed.enableTextTrack(this.language); } }, diff --git a/src/js/controls.js b/src/js/controls.js index e7248f66..2adea50f 100644 --- a/src/js/controls.js +++ b/src/js/controls.js @@ -445,7 +445,7 @@ const controls = { } // Toggle the pane and tab - const toggle = !utils.is.empty(this.options.quality) && this.type === 'youtube'; + const toggle = !utils.is.empty(this.options.quality) && this.isYouTube; controls.toggleTab.call(this, type, toggle); // If we're hiding, nothing more to do diff --git a/src/js/defaults.js b/src/js/defaults.js index 77bc2457..1a0faeed 100644 --- a/src/js/defaults.js +++ b/src/js/defaults.js @@ -13,7 +13,7 @@ const defaults = { autoplay: false, // Only allow one media playing at once (vimeo only) - autopause: false, + autopause: true, // Default time to skip when rewind/fast forward seekTime: 10, @@ -267,6 +267,7 @@ const defaults = { embed: 'plyr__video-embed', control: 'plyr__control', type: 'plyr--{0}', + provider: 'plyr--{0}', stopped: 'plyr--stopped', playing: 'plyr--playing', loading: 'plyr--loading', diff --git a/src/js/fullscreen.js b/src/js/fullscreen.js index 01703659..ac65c2bf 100644 --- a/src/js/fullscreen.js +++ b/src/js/fullscreen.js @@ -92,7 +92,7 @@ const fullscreen = { // Setup fullscreen setup() { - if (!this.supported.ui || this.type === 'audio' || !this.config.fullscreen.enabled) { + if (!this.supported.ui || this.isAudio || !this.config.fullscreen.enabled) { return; } diff --git a/src/js/listeners.js b/src/js/listeners.js index 8f95b1a7..7f64375c 100644 --- a/src/js/listeners.js +++ b/src/js/listeners.js @@ -221,7 +221,7 @@ const listeners = { // Handle the media finishing utils.on(this.media, 'ended', () => { // Show poster on end - if (this.type === 'video' && this.config.showPosterOnEnd) { + if (this.isHTML5 && this.isVideo && this.config.showPosterOnEnd) { // Restart this.restart(); @@ -243,7 +243,7 @@ const listeners = { utils.on(this.media, 'stalled waiting canplay seeked playing', event => ui.checkLoading.call(this, event)); // Click video - if (this.supported.ui && this.config.clickToPlay && this.type !== 'audio') { + if (this.supported.ui && this.config.clickToPlay && !this.isAudio) { // Re-fetch the wrapper const wrapper = utils.getElement.call(this, `.${this.config.classNames.video}`); diff --git a/src/js/media.js b/src/js/media.js index 8c0559ac..9eea9a5d 100644 --- a/src/js/media.js +++ b/src/js/media.js @@ -23,6 +23,9 @@ const media = { // Add type class utils.toggleClass(this.elements.container, this.config.classNames.type.replace('{0}', this.type), true); + // Add provider class + utils.toggleClass(this.elements.container, this.config.classNames.provider.replace('{0}', this.provider), true); + // Add video class for embeds // This will require changes if audio embeds are added if (this.isEmbed) { @@ -31,7 +34,7 @@ const media = { if (this.supported.ui) { // Check for picture-in-picture support - utils.toggleClass(this.elements.container, this.config.classNames.pip.supported, support.pip && this.type === 'video'); + 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); @@ -47,7 +50,7 @@ const media = { } // Inject the player wrapper - if (['video', 'youtube', 'vimeo'].includes(this.type)) { + if (this.isVideo || this.isYouTube || this.isVimeo) { // Create the wrapper div this.elements.wrapper = utils.createElement('div', { class: this.config.classNames.video, @@ -58,7 +61,7 @@ const media = { } if (this.isEmbed) { - switch (this.type) { + switch (this.provider) { case 'youtube': youtube.setup.call(this); break; diff --git a/src/js/plugins/vimeo.js b/src/js/plugins/vimeo.js index 6c32302a..21d34beb 100644 --- a/src/js/plugins/vimeo.js +++ b/src/js/plugins/vimeo.js @@ -4,13 +4,12 @@ import utils from './../utils'; import captions from './../captions'; -import controls from './../controls'; import ui from './../ui'; const vimeo = { setup() { // Remove old containers - const containers = utils.getElements.call(this, `[id^="${this.type}-"]`); + const containers = utils.getElements.call(this, `[id^="${this.provider}-"]`); Array.from(containers).forEach(utils.removeElement); // Add embed class for responsive @@ -20,7 +19,7 @@ const vimeo = { vimeo.setAspectRatio.call(this); // Set ID - this.media.setAttribute('id', utils.generateId(this.type)); + this.media.setAttribute('id', utils.generateId(this.provider)); // Load the API if not already if (!utils.is.object(window.Vimeo)) { diff --git a/src/js/plugins/youtube.js b/src/js/plugins/youtube.js index ce5b46e1..cf529fba 100644 --- a/src/js/plugins/youtube.js +++ b/src/js/plugins/youtube.js @@ -11,7 +11,7 @@ const youtube = { const videoId = utils.parseYouTubeId(this.embedId); // Remove old containers - const containers = utils.getElements.call(this, `[id^="${this.type}-"]`); + const containers = utils.getElements.call(this, `[id^="${this.provider}-"]`); Array.from(containers).forEach(utils.removeElement); // Add embed class for responsive @@ -21,7 +21,7 @@ const youtube = { youtube.setAspectRatio.call(this); // Set ID - this.media.setAttribute('id', utils.generateId(this.type)); + this.media.setAttribute('id', utils.generateId(this.provider)); // Setup API if (utils.is.object(window.YT)) { @@ -31,6 +31,7 @@ const youtube = { utils.loadScript(this.config.urls.youtube.api); // Setup callback for the API + // YouTube has it's own system of course... window.onYouTubeReadyCallbacks = window.onYouTubeReadyCallbacks || []; // Add to queue diff --git a/src/js/plyr.js b/src/js/plyr.js index ea6d3dec..54734fde 100644 --- a/src/js/plyr.js +++ b/src/js/plyr.js @@ -5,8 +5,8 @@ // License: The MIT License (MIT) // ========================================================================== +import { providers, types } from './types'; import defaults from './defaults'; -import types from './types'; import support from './support'; import utils from './utils'; @@ -40,11 +40,7 @@ class Plyr { } // 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) || utils.is.nodeList(this.media) || utils.is.array(this.media)) { // eslint-disable-next-line this.media = this.media[0]; } @@ -149,7 +145,7 @@ class Plyr { // Embed attributes const attributes = { provider: 'data-plyr-provider', - id: 'data-plyr-provider-id', + id: 'data-plyr-embed-id', }; // Different setup based on type @@ -157,16 +153,18 @@ class Plyr { // TODO: Handle passing an iframe for true progressive enhancement // case 'iframe': case 'div': - this.type = this.media.getAttribute(attributes.provider); + this.type = types.video; // Audio will come later for external providers + this.provider = this.media.getAttribute(attributes.provider); this.embedId = this.media.getAttribute(attributes.id); - if (utils.is.empty(this.type)) { - this.console.error('Setup failed: embed type missing'); + if (utils.is.empty(this.provider) || !Object.keys(providers).includes(this.provider)) { + this.console.error('Setup failed: Invalid provider'); return; } + // Try and get the embed id if (utils.is.empty(this.embedId)) { - this.console.error('Setup failed: video id missing'); + this.console.error('Setup failed: Embed ID or URL missing'); return; } @@ -179,19 +177,24 @@ class Plyr { case 'video': case 'audio': this.type = type; + this.provider = providers.html5; if (this.media.hasAttribute('crossorigin')) { this.config.crossorigin = true; } + if (this.media.hasAttribute('autoplay')) { this.config.autoplay = true; } + if (this.media.hasAttribute('playsinline')) { this.config.inline = true; } + if (this.media.hasAttribute('muted')) { this.config.muted = true; } + if (this.media.hasAttribute('loop')) { this.config.loop.active = true; } @@ -207,7 +210,7 @@ class Plyr { storage.setup.call(this); // Check for support again but with type - this.supported = support.check(this.type, this.config.inline); + this.supported = support.check(this.type, this.provider, this.config.inline); // If no support for even API, bail if (!this.supported.api) { @@ -253,17 +256,25 @@ class Plyr { // --------------------------------------- /** - * If the player is HTML5 + * Types and provider helpers */ get isHTML5() { - return types.html5.includes(this.type); + return this.provider === providers.html5; } - - /** - * If the player is an embed - e.g. YouTube or Vimeo - */ get isEmbed() { - return types.embed.includes(this.type); + return this.isYouTube || this.isVimeo; + } + get isYouTube() { + return this.provider === providers.youtube; + } + get isVimeo() { + return this.provider === providers.vimeo; + } + get isVideo() { + return this.type === types.video; + } + get isAudio() { + return this.type === types.audio; } /** @@ -518,11 +529,7 @@ class Plyr { } // Get audio tracks - return ( - this.media.mozHasAudio || - Boolean(this.media.webkitAudioDecodedByteCount) || - Boolean(this.media.audioTracks && this.media.audioTracks.length) - ); + return this.media.mozHasAudio || Boolean(this.media.webkitAudioDecodedByteCount) || Boolean(this.media.audioTracks && this.media.audioTracks.length); } /** @@ -683,7 +690,7 @@ class Plyr { * @param {input} - the URL for the new poster image */ set poster(input) { - if (!this.isHTML5 || this.type !== 'video') { + if (!this.isHTML5 || !this.isVideo) { this.console.warn('Poster can only be set on HTML5 video'); return; } @@ -697,7 +704,7 @@ class Plyr { * Get the current poster image */ get poster() { - if (!this.isHTML5 || this.type !== 'video') { + if (!this.isHTML5 || !this.isVideo) { return null; } @@ -731,9 +738,7 @@ class Plyr { } // If the method is called without parameter, toggle based on current value - const show = utils.is.boolean(input) - ? input - : this.elements.container.className.indexOf(this.config.classNames.captions.active) === -1; + const show = utils.is.boolean(input) ? input : this.elements.container.className.indexOf(this.config.classNames.captions.active) === -1; // Nothing to change... if (this.captions.enabled === show) { @@ -828,11 +833,7 @@ class Plyr { this.fullscreen.active = !this.fullscreen.active; // Add class hook - utils.toggleClass( - this.elements.container, - this.config.classNames.fullscreen.fallback, - this.fullscreen.active - ); + utils.toggleClass(this.elements.container, this.config.classNames.fullscreen.fallback, this.fullscreen.active); // Make sure we don't lose scroll position if (this.fullscreen.active) { @@ -920,7 +921,7 @@ class Plyr { } // Don't hide if no UI support or it's audio - if (!this.supported.ui || this.type === 'audio') { + if (!this.supported.ui || this.isAudio) { return this; } @@ -980,13 +981,13 @@ class Plyr { // then set the timer to hide the controls if (!show || this.playing) { this.timers.controls = window.setTimeout(() => { - console.warn({ + /* this.console.warn({ pressed: this.elements.controls.pressed, hover: this.elements.controls.pressed, playing: this.playing, paused: this.paused, loading: this.loading, - }); + }); */ // If the mouse is over the controls (and not entering fullscreen), bail if ((this.elements.controls.pressed || this.elements.controls.hover) && !isEnterFullscreen) { @@ -1105,8 +1106,18 @@ class Plyr { }; // Type specific stuff - switch (this.type) { - case 'youtube': + switch (`${this.provider}:${this.type}`) { + case 'html5:video': + case 'html5:audio': + // Restore native video controls + ui.toggleNativeControls.call(this, true); + + // Clean up + done(); + + break; + + case 'youtube:video': // Clear timers window.clearInterval(this.timers.buffering); window.clearInterval(this.timers.playing); @@ -1119,7 +1130,7 @@ class Plyr { break; - case 'vimeo': + case 'vimeo:video': // Destroy Vimeo API // then clean up (wait, to prevent postmessage errors) this.embed.unload().then(done); @@ -1129,16 +1140,6 @@ class Plyr { break; - case 'video': - case 'audio': - // Restore native video controls - ui.toggleNativeControls.call(this, true); - - // Clean up - done(); - - break; - default: break; } diff --git a/src/js/source.js b/src/js/source.js index c670ab09..cbea5433 100644 --- a/src/js/source.js +++ b/src/js/source.js @@ -2,7 +2,7 @@ // Plyr source update // ========================================================================== -import types from './types'; +import { providers } from './types'; import utils from './utils'; import media from './media'; import ui from './ui'; @@ -48,35 +48,25 @@ const source = { this.elements.container.removeAttribute('class'); } - // Set the type - if ('type' in input) { - this.type = input.type; - - // Get child type for video (it might be an embed) - if (this.type === 'video') { - const firstSource = input.sources[0]; - - if ('type' in firstSource && types.embed.includes(firstSource.type)) { - this.type = firstSource.type; - } - } - } + // Set the type and provider + this.type = input.type; + this.provider = !utils.is.empty(input.sources[0].provider) ? input.sources[0].provider : providers.html5; // Check for support - this.supported = support.check(this.type, this.config.inline); + this.supported = support.check(this.type, this.provider, this.config.inline); // Create new markup - switch (this.type) { - case 'video': + switch (`${this.provider}:${this.type}`) { + case 'html5:video': this.media = utils.createElement('video'); break; - case 'audio': + case 'html5:audio': this.media = utils.createElement('audio'); break; - case 'youtube': - case 'vimeo': + case 'youtube:video': + case 'vimeo:video': this.media = utils.createElement('div'); this.embedId = input.sources[0].src; break; @@ -117,7 +107,6 @@ const source = { // Restore class hooks utils.toggleClass(this.elements.container, this.config.classNames.captions.active, this.supported.ui && this.captions.enabled); - ui.addStyleHook.call(this); // Set new sources for html5 diff --git a/src/js/support.js b/src/js/support.js index a8e0cc3e..fc61bbf6 100644 --- a/src/js/support.js +++ b/src/js/support.js @@ -12,29 +12,29 @@ const support = { // Check for support // Basic functionality vs full UI - check(type, inline) { + check(type, provider, inline) { let api = false; let ui = false; const browser = utils.getBrowser(); const playsInline = browser.isIPhone && inline && support.inline; - switch (type) { - case 'video': + switch (`${provider}:${type}`) { + case 'html5:video': api = support.video; ui = api && support.rangeInput && (!browser.isIPhone || playsInline); break; - case 'audio': + case 'html5:audio': api = support.audio; ui = api && support.rangeInput; break; - case 'youtube': + case 'youtube:video': api = true; ui = support.rangeInput && (!browser.isIPhone || playsInline); break; - case 'vimeo': + case 'vimeo:video': api = true; ui = support.rangeInput && !browser.isIPhone; break; @@ -92,12 +92,12 @@ const support = { try { // Bail if no checking function - if (!utils.is.function(media.canPlayType)) { + if (!this.isHTML5 || !utils.is.function(media.canPlayType)) { return false; } // Type specific checks - if (this.type === 'video') { + if (this.isVideo) { switch (type) { case 'video/webm': return media.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/no/, ''); @@ -111,7 +111,7 @@ const support = { default: return false; } - } else if (this.type === 'audio') { + } else if (this.isAudio) { switch (type) { case 'audio/mpeg': return media.canPlayType('audio/mpeg;').replace(/no/, ''); diff --git a/src/js/types.js b/src/js/types.js index 1f402a9b..35716c3c 100644 --- a/src/js/types.js +++ b/src/js/types.js @@ -1,10 +1,16 @@ // ========================================================================== -// Plyr supported types +// Plyr supported types and providers // ========================================================================== -const types = { - embed: ['youtube', 'vimeo'], - html5: ['video', 'audio'], +export const providers = { + html5: 'html5', + youtube: 'youtube', + vimeo: 'vimeo', }; -export default types; +export const types = { + audio: 'audio', + video: 'video', +}; + +export default { providers, types }; diff --git a/src/js/ui.js b/src/js/ui.js index 3ab3a1eb..5fb723d6 100644 --- a/src/js/ui.js +++ b/src/js/ui.js @@ -31,7 +31,7 @@ const ui = { // Don't setup interface if no support if (!this.supported.ui) { - this.console.warn(`Basic support only for ${this.type}`); + this.console.warn(`Basic support only for ${this.provider} ${this.type}`); // Remove controls utils.removeElement.call(this, 'controls'); diff --git a/src/js/utils.js b/src/js/utils.js index 25617ac8..b3ce509b 100644 --- a/src/js/utils.js +++ b/src/js/utils.js @@ -73,24 +73,39 @@ const utils = { // Load an external script loadScript(url, callback) { - // Check script is not already referenced - if (document.querySelectorAll(`script[src="${url}"]`).length) { + const current = document.querySelector(`script[src="${url}"]`); + + // Check script is not already referenced, if so wait for load + if (current !== null) { + current.callbacks = current.callbacks || []; + current.callbacks.push(callback); return; } // Build the element const element = document.createElement('script'); - element.src = url; - // Find first script - const first = document.getElementsByTagName('script')[0]; + // Callback queue + element.callbacks = element.callbacks || []; + element.callbacks.push(callback); // Bind callback if (utils.is.function(callback)) { - element.addEventListener('load', event => callback.call(null, event), false); + element.addEventListener( + 'load', + event => { + element.callbacks.forEach(cb => cb.call(null, event)); + element.callbacks = null; + }, + false + ); } + // Set the URL after binding callback + element.src = url; + // Inject + const first = document.getElementsByTagName('script')[0]; first.parentNode.insertBefore(element, first); }, |