diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/js/controls.js | 16 | ||||
-rw-r--r-- | src/js/defaults.js | 3 | ||||
-rw-r--r-- | src/js/listeners.js | 7 | ||||
-rw-r--r-- | src/js/media.js | 5 | ||||
-rw-r--r-- | src/js/plugins/vimeo.js | 7 | ||||
-rw-r--r-- | src/js/plugins/youtube.js | 4 | ||||
-rw-r--r-- | src/js/plyr.js | 62 | ||||
-rw-r--r-- | src/js/ui.js | 6 | ||||
-rw-r--r-- | src/js/utils.js | 134 | ||||
-rw-r--r-- | src/less/components/embed.less | 10 |
10 files changed, 156 insertions, 98 deletions
diff --git a/src/js/controls.js b/src/js/controls.js index d40165e1..ac7ba2b6 100644 --- a/src/js/controls.js +++ b/src/js/controls.js @@ -6,11 +6,14 @@ import support from './support'; import utils from './utils'; import ui from './ui'; +// Sniff out the browser +const browser = utils.getBrowser(); + const controls = { // Webkit polyfill for lower fill range updateRangeFill(target) { // WebKit only - if (!this.browser.isWebkit) { + if (!browser.isWebkit) { return; } @@ -49,7 +52,7 @@ const controls = { getIconUrl() { return { url: this.config.iconUrl, - absolute: this.config.iconUrl.indexOf('http') === 0 || (this.browser.isIE && !window.svg4everybody), + absolute: this.config.iconUrl.indexOf('http') === 0 || (browser.isIE && !window.svg4everybody), }; }, @@ -1139,14 +1142,11 @@ const controls = { inject() { // Sprite if (this.config.loadSprite) { - const iconUrl = controls.getIconUrl.call(this); + const icon = controls.getIconUrl.call(this); // Only load external sprite using AJAX - if (iconUrl.absolute) { - this.log(`AJAX loading absolute SVG sprite ${this.browser.isIE ? '(due to IE)' : ''}`); - utils.loadSprite(iconUrl.url, 'sprite-plyr'); - } else { - this.log('Sprite will be used as external resource directly'); + if (icon.absolute) { + utils.loadSprite(icon.url, 'sprite-plyr'); } } diff --git a/src/js/defaults.js b/src/js/defaults.js index 837b981b..a3d95bb4 100644 --- a/src/js/defaults.js +++ b/src/js/defaults.js @@ -45,6 +45,9 @@ const defaults = { // Pass a custom duration duration: null, + // Aspect ratio (for embeds) + ratio: '16:9', + // Quality default quality: { default: 'default', diff --git a/src/js/listeners.js b/src/js/listeners.js index 9b84a729..29038a21 100644 --- a/src/js/listeners.js +++ b/src/js/listeners.js @@ -9,6 +9,9 @@ import fullscreen from './fullscreen'; import storage from './storage'; import ui from './ui'; +// Sniff out the browser +const browser = utils.getBrowser(); + const listeners = { // Listen for media events media() { @@ -134,7 +137,7 @@ const listeners = { // Listen for control events controls() { // IE doesn't support input event, so we fallback to change - const inputEvent = this.browser.isIE ? 'change' : 'input'; + const inputEvent = browser.isIE ? 'change' : 'input'; let last = null; // Trigger custom and default handlers @@ -468,7 +471,7 @@ const listeners = { ); // Polyfill for lower fill in <input type="range"> for webkit - if (this.browser.isWebkit) { + if (browser.isWebkit) { utils.on(utils.getElements.call(this, 'input[type="range"]'), 'input', event => { controls.updateRangeFill.call(this, event.target); }); diff --git a/src/js/media.js b/src/js/media.js index 9e53f5fc..46e6bec6 100644 --- a/src/js/media.js +++ b/src/js/media.js @@ -8,6 +8,9 @@ import youtube from './plugins/youtube'; import vimeo from './plugins/vimeo'; import ui from './ui'; +// Sniff out the browser +const browser = utils.getBrowser(); + const media = { // Setup media setup() { @@ -45,7 +48,7 @@ const media = { utils.toggleClass(this.elements.container, this.config.classNames.stopped, this.config.autoplay); // Add iOS class - utils.toggleClass(this.elements.container, this.config.classNames.isIos, this.browser.isIos); + utils.toggleClass(this.elements.container, this.config.classNames.isIos, browser.isIos); // Add touch class utils.toggleClass(this.elements.container, this.config.classNames.isTouch, support.touch); diff --git a/src/js/plugins/vimeo.js b/src/js/plugins/vimeo.js index 0b815fa5..0f6aa4db 100644 --- a/src/js/plugins/vimeo.js +++ b/src/js/plugins/vimeo.js @@ -15,6 +15,13 @@ const vimeo = { // Add embed class for responsive utils.toggleClass(this.elements.wrapper, this.config.classNames.embed, true); + // Set aspect ratio + const ratio = this.config.ratio.split(':'); + const padding = 100 / ratio[0] * ratio[1]; + const offset = (100 - padding) / 2; + this.elements.wrapper.style.paddingBottom = `${padding}%`; + this.media.style.transform = `translateY(-${offset}%)`; + // Set ID this.media.setAttribute('id', utils.generateId(this.type)); diff --git a/src/js/plugins/youtube.js b/src/js/plugins/youtube.js index 38f649a5..2c8557dc 100644 --- a/src/js/plugins/youtube.js +++ b/src/js/plugins/youtube.js @@ -17,6 +17,10 @@ const youtube = { // Add embed class for responsive utils.toggleClass(this.elements.wrapper, this.config.classNames.embed, true); + // Set aspect ratio + const ratio = this.config.ratio.split(':'); + this.elements.wrapper.style.paddingBottom = `${100 / ratio[0] * ratio[1]}%`; + // Set ID this.media.setAttribute('id', utils.generateId(this.type)); diff --git a/src/js/plyr.js b/src/js/plyr.js index 5c28887e..08711172 100644 --- a/src/js/plyr.js +++ b/src/js/plyr.js @@ -192,10 +192,7 @@ class Plyr { return; } - // Sniff out the browser - this.browser = utils.getBrowser(); - - // Load saved settings from localStorage + // Setup local storage for user settings storage.setup.call(this); // Check for support again but with type @@ -237,17 +234,27 @@ class Plyr { } } + // --------------------------------------- // API // --------------------------------------- + /** + * If the player is HTML5 + */ get isHTML5() { return types.html5.includes(this.type); } + + /** + * If the player is an embed - e.g. YouTube or Vimeo + */ get isEmbed() { return types.embed.includes(this.type); } - // Play + /** + * Play the media + */ play() { if ('play' in this.media) { this.media.play(); @@ -257,7 +264,9 @@ class Plyr { return this; } - // Pause + /** + * Pause the media + */ pause() { if ('pause' in this.media) { this.media.pause(); @@ -267,7 +276,10 @@ class Plyr { return this; } - // Toggle playback + /** + * Toggle playback based on current status + * @param {boolean} toggle + */ togglePlay(toggle) { // True toggle if nothing passed if ((!utils.is.boolean(toggle) && this.media.paused) || toggle) { @@ -277,31 +289,43 @@ class Plyr { return this.pause(); } - // Stop + /** + * Stop playback + */ stop() { return this.restart().pause(); } - // Restart + /** + * Restart playback + */ restart() { this.currentTime = 0; return this; } - // Rewind + /** + * Rewind + * @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); return this; } - // Fast forward + /** + * Fast forward + * @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); return this; } - // Seek to time - // The input parameter can be an event or a number + /** + * Seek to a time + * @param {number} input - where to seek to in seconds. Defaults to 0 (the start) + */ set currentTime(input) { let targetTime = 0; @@ -327,7 +351,9 @@ class Plyr { return Number(this.media.currentTime); } - // Duration + /** + * Get the duration of the current media + */ get duration() { // Faux duration set via config const fauxDuration = parseInt(this.config.duration, 10); @@ -339,7 +365,10 @@ class Plyr { return !Number.isNaN(fauxDuration) ? fauxDuration : realDuration; } - // Volume + /** + * 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 + */ set volume(value) { let volume = value; const max = 1; @@ -377,6 +406,9 @@ class Plyr { } } + /** + * Get the current player volume + */ get volume() { return this.media.volume; } diff --git a/src/js/ui.js b/src/js/ui.js index c0448054..aa579d8d 100644 --- a/src/js/ui.js +++ b/src/js/ui.js @@ -84,15 +84,17 @@ const ui = { // Update the UI ui.checkPlaying.call(this); + // Ready for API calls this.ready = true; // Ready event at end of execution stack utils.dispatchEvent.call(this, this.media, 'ready'); // Autoplay - if (this.config.autoplay) { + // TODO: check we still need this? + /* if (this.isEmbed && this.config.autoplay) { this.play(); - } + } */ }, // Show the duration on metadataloaded diff --git a/src/js/utils.js b/src/js/utils.js index 4296f345..70519fac 100644 --- a/src/js/utils.js +++ b/src/js/utils.js @@ -89,6 +89,73 @@ const utils = { firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); }, + // Load an external SVG sprite + loadSprite(url, id) { + if (typeof url !== 'string') { + return; + } + + const prefix = 'cache-'; + const hasId = typeof id === 'string'; + let isCached = false; + + function updateSprite(data) { + // Inject content + this.innerHTML = data; + + // Inject the SVG to the body + document.body.insertBefore(this, document.body.childNodes[0]); + } + + // Only load once + if (!hasId || !document.querySelectorAll(`#${id}`).length) { + // Create container + const container = document.createElement('div'); + container.setAttribute('hidden', ''); + + if (hasId) { + container.setAttribute('id', id); + } + + // Check in cache + if (support.storage) { + const cached = window.localStorage.getItem(prefix + id); + isCached = cached !== null; + + if (isCached) { + const data = JSON.parse(cached); + updateSprite.call(container, data.content); + } + } + + // ReSharper disable once InconsistentNaming + const xhr = new XMLHttpRequest(); + + // XHR for Chrome/Firefox/Opera/Safari + if ('withCredentials' in xhr) { + xhr.open('GET', url, true); + } else { + return; + } + + // Once loaded, inject to container and body + xhr.onload = () => { + if (support.storage) { + window.localStorage.setItem( + prefix + id, + JSON.stringify({ + content: xhr.responseText, + }) + ); + } + + updateSprite.call(container, xhr.responseText); + }; + + xhr.send(); + } + }, + // Generate a random ID generateId(prefix) { return `${prefix}-${Math.floor(Math.random() * 10000)}`; @@ -564,73 +631,6 @@ const utils = { return fragment.firstChild.innerText; }, - // Load an SVG sprite - loadSprite(url, id) { - if (typeof url !== 'string') { - return; - } - - const prefix = 'cache-'; - const hasId = typeof id === 'string'; - let isCached = false; - - function updateSprite(data) { - // Inject content - this.innerHTML = data; - - // Inject the SVG to the body - document.body.insertBefore(this, document.body.childNodes[0]); - } - - // Only load once - if (!hasId || !document.querySelectorAll(`#${id}`).length) { - // Create container - const container = document.createElement('div'); - container.setAttribute('hidden', ''); - - if (hasId) { - container.setAttribute('id', id); - } - - // Check in cache - if (support.storage) { - const cached = window.localStorage.getItem(prefix + id); - isCached = cached !== null; - - if (isCached) { - const data = JSON.parse(cached); - updateSprite.call(container, data.content); - } - } - - // ReSharper disable once InconsistentNaming - const xhr = new XMLHttpRequest(); - - // XHR for Chrome/Firefox/Opera/Safari - if ('withCredentials' in xhr) { - xhr.open('GET', url, true); - } else { - return; - } - - // Once loaded, inject to container and body - xhr.onload = () => { - if (support.storage) { - window.localStorage.setItem( - prefix + id, - JSON.stringify({ - content: xhr.responseText, - }) - ); - } - - updateSprite.call(container, xhr.responseText); - }; - - xhr.send(); - } - }, - // Get the transition end event transitionEnd: (() => { const element = document.createElement('span'); diff --git a/src/less/components/embed.less b/src/less/components/embed.less index f6b5b307..dcf889b7 100644 --- a/src/less/components/embed.less +++ b/src/less/components/embed.less @@ -4,7 +4,11 @@ // -------------------------------------------------------------- .plyr__video-embed { - padding-bottom: 56.25%; /* 16:9 */ + // Default to 16:9 ratio but this is set by JavaScript based on config + @padding: ((100 / 16) * 9); + @offset: unit((100 - @padding) / 2, ~'%'); + + padding-bottom: unit(@padding, ~'%'); height: 0; iframe { @@ -20,8 +24,8 @@ // Vimeo hack > div { position: relative; - padding-bottom: 200%; - transform: translateY(-35.95%); + padding-bottom: 100%; + transform: translateY(-@offset); } } // To allow mouse events to be captured if full support |