diff options
Diffstat (limited to 'src/js/plugins')
-rw-r--r-- | src/js/plugins/ads.js | 38 | ||||
-rw-r--r-- | src/js/plugins/vimeo.js | 98 | ||||
-rw-r--r-- | src/js/plugins/youtube.js | 104 |
3 files changed, 143 insertions, 97 deletions
diff --git a/src/js/plugins/ads.js b/src/js/plugins/ads.js index 0246e221..07eee58f 100644 --- a/src/js/plugins/ads.js +++ b/src/js/plugins/ads.js @@ -7,7 +7,12 @@ /* global google */ import i18n from '../i18n'; -import utils from '../utils'; +import { createElement } from './../utils/elements'; +import { trigger } from './../utils/events'; +import is from './../utils/is'; +import loadScript from './../utils/loadScript'; +import { formatTime } from './../utils/time'; +import { buildUrlParams } from './../utils/urls'; class Ads { /** @@ -44,7 +49,7 @@ class Ads { } get enabled() { - return this.player.isVideo && this.player.config.ads.enabled && !utils.is.empty(this.publisherId); + return this.player.isVideo && this.player.config.ads.enabled && !is.empty(this.publisherId); } /** @@ -53,9 +58,8 @@ class Ads { load() { if (this.enabled) { // 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.sdk) + if (!is.object(window.google) || !is.object(window.google.ima)) { + loadScript(this.player.config.urls.googleIMA.sdk) .then(() => { this.ready(); }) @@ -103,7 +107,7 @@ class Ads { const base = 'https://go.aniview.com/api/adserver6/vast/'; - return `${base}?${utils.buildUrlParams(params)}`; + return `${base}?${buildUrlParams(params)}`; } /** @@ -116,7 +120,7 @@ class Ads { */ setupIMA() { // Create the container for our advertisements - this.elements.container = utils.createElement('div', { + this.elements.container = createElement('div', { class: this.player.config.classNames.ads, }); this.player.elements.container.appendChild(this.elements.container); @@ -184,7 +188,7 @@ class Ads { } const update = () => { - const time = utils.formatTime(Math.max(this.manager.getRemainingTime(), 0)); + const time = formatTime(Math.max(this.manager.getRemainingTime(), 0)); const label = `${i18n.get('advertisement', this.player.config)} - ${time}`; this.elements.container.setAttribute('data-badge-text', label); }; @@ -212,14 +216,14 @@ class Ads { this.cuePoints = this.manager.getCuePoints(); // Add advertisement cue's within the time line if available - if (!utils.is.empty(this.cuePoints)) { + if (!is.empty(this.cuePoints)) { this.cuePoints.forEach(cuePoint => { if (cuePoint !== 0 && cuePoint !== -1 && cuePoint < this.player.duration) { const seekElement = this.player.elements.progress; - if (utils.is.element(seekElement)) { + if (is.element(seekElement)) { const cuePercentage = 100 / this.player.duration * cuePoint; - const cue = utils.createElement('span', { + const cue = createElement('span', { class: this.player.config.classNames.cues, }); @@ -266,7 +270,7 @@ class Ads { // Proxy event const dispatchEvent = type => { const event = `ads${type.replace(/_/g, '').toLowerCase()}`; - utils.dispatchEvent.call(this.player, this.player.media, event); + trigger.call(this.player, this.player.media, event); }; switch (event.type) { @@ -393,7 +397,7 @@ class Ads { this.player.on('seeked', () => { const seekedTime = this.player.currentTime; - if (utils.is.empty(this.cuePoints)) { + if (is.empty(this.cuePoints)) { return; } @@ -530,9 +534,9 @@ class Ads { trigger(event, ...args) { const handlers = this.events[event]; - if (utils.is.array(handlers)) { + if (is.array(handlers)) { handlers.forEach(handler => { - if (utils.is.function(handler)) { + if (is.function(handler)) { handler.apply(this, args); } }); @@ -546,7 +550,7 @@ class Ads { * @return {Ads} */ on(event, callback) { - if (!utils.is.array(this.events[event])) { + if (!is.array(this.events[event])) { this.events[event] = []; } @@ -577,7 +581,7 @@ class Ads { * @param {string} from */ clearSafetyTimer(from) { - if (!utils.is.nullOrUndefined(this.safetyTimer)) { + if (!is.nullOrUndefined(this.safetyTimer)) { this.player.debug.log(`Safety timer cleared from: ${from}`); clearTimeout(this.safetyTimer); diff --git a/src/js/plugins/vimeo.js b/src/js/plugins/vimeo.js index 652c920c..76a85424 100644 --- a/src/js/plugins/vimeo.js +++ b/src/js/plugins/vimeo.js @@ -5,7 +5,34 @@ import captions from './../captions'; import controls from './../controls'; import ui from './../ui'; -import utils from './../utils'; +import { createElement, replaceElement, toggleClass } from './../utils/elements'; +import { trigger } from './../utils/events'; +import fetch from './../utils/fetch'; +import is from './../utils/is'; +import loadScript from './../utils/loadScript'; +import { format, stripHTML } from './../utils/strings'; +import { buildUrlParams } from './../utils/urls'; + +// Parse Vimeo ID from URL +function parseId(url) { + if (is.empty(url)) { + return null; + } + + if (is.number(Number(url))) { + return url; + } + + const regex = /^.*(vimeo.com\/|video\/)(\d+).*/; + return url.match(regex) ? RegExp.$2 : url; +} + +// Get aspect ratio for dimensions +function getAspectRatio(width, height) { + const getRatio = (w, h) => (h === 0 ? w : getRatio(h, w % h)); + const ratio = getRatio(width, height); + return `${width / ratio}:${height / ratio}`; +} // Set playback state and trigger change (only on actual change) function assurePlaybackState(play) { @@ -14,22 +41,21 @@ function assurePlaybackState(play) { } if (this.media.paused === play) { this.media.paused = !play; - utils.dispatchEvent.call(this, this.media, play ? 'play' : 'pause'); + trigger.call(this, this.media, play ? 'play' : 'pause'); } } const vimeo = { setup() { // Add embed class for responsive - utils.toggleClass(this.elements.wrapper, this.config.classNames.embed, true); + toggleClass(this.elements.wrapper, this.config.classNames.embed, true); // Set intial ratio vimeo.setAspectRatio.call(this); // Load the API if not already - if (!utils.is.object(window.Vimeo)) { - utils - .loadScript(this.config.urls.vimeo.sdk) + if (!is.object(window.Vimeo)) { + loadScript(this.config.urls.vimeo.sdk) .then(() => { vimeo.ready.call(this); }) @@ -44,7 +70,7 @@ const vimeo = { // Set aspect ratio // For Vimeo we have an extra 300% height <div> to hide the standard controls and UI setAspectRatio(input) { - const ratio = utils.is.string(input) ? input.split(':') : this.config.ratio.split(':'); + const ratio = is.string(input) ? input.split(':') : this.config.ratio.split(':'); const padding = 100 / ratio[0] * ratio[1]; this.elements.wrapper.style.paddingBottom = `${padding}%`; @@ -73,34 +99,34 @@ const vimeo = { gesture: 'media', playsinline: !this.config.fullscreen.iosNative, }; - const params = utils.buildUrlParams(options); + const params = buildUrlParams(options); // Get the source URL or ID let source = player.media.getAttribute('src'); // Get from <div> if needed - if (utils.is.empty(source)) { + if (is.empty(source)) { source = player.media.getAttribute(player.config.attributes.embed.id); } - const id = utils.parseVimeoId(source); + const id = parseId(source); // Build an iframe - const iframe = utils.createElement('iframe'); - const src = utils.format(player.config.urls.vimeo.iframe, id, params); + const iframe = createElement('iframe'); + const src = format(player.config.urls.vimeo.iframe, id, params); iframe.setAttribute('src', src); iframe.setAttribute('allowfullscreen', ''); iframe.setAttribute('allowtransparency', ''); iframe.setAttribute('allow', 'autoplay'); // Inject the package - const wrapper = utils.createElement('div', { class: player.config.classNames.embedContainer }); + const wrapper = createElement('div', { class: player.config.classNames.embedContainer }); wrapper.appendChild(iframe); - player.media = utils.replaceElement(wrapper, player.media); + player.media = 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)) { + fetch(format(player.config.urls.vimeo.api, id), 'json').then(response => { + if (is.empty(response)) { return; } @@ -160,7 +186,7 @@ const vimeo = { // Set seeking state and trigger event media.seeking = true; - utils.dispatchEvent.call(player, media, 'seeking'); + trigger.call(player, media, 'seeking'); // If paused, mute until seek is complete Promise.resolve(restorePause && embed.setVolume(0)) @@ -187,7 +213,7 @@ const vimeo = { .setPlaybackRate(input) .then(() => { speed = input; - utils.dispatchEvent.call(player, player.media, 'ratechange'); + trigger.call(player, player.media, 'ratechange'); }) .catch(error => { // Hide menu item (and menu if empty) @@ -207,7 +233,7 @@ const vimeo = { set(input) { player.embed.setVolume(input).then(() => { volume = input; - utils.dispatchEvent.call(player, player.media, 'volumechange'); + trigger.call(player, player.media, 'volumechange'); }); }, }); @@ -219,11 +245,11 @@ const vimeo = { return muted; }, set(input) { - const toggle = utils.is.boolean(input) ? input : false; + const toggle = is.boolean(input) ? input : false; player.embed.setVolume(toggle ? 0 : player.config.volume).then(() => { muted = toggle; - utils.dispatchEvent.call(player, player.media, 'volumechange'); + trigger.call(player, player.media, 'volumechange'); }); }, }); @@ -235,7 +261,7 @@ const vimeo = { return loop; }, set(input) { - const toggle = utils.is.boolean(input) ? input : player.config.loop.active; + const toggle = is.boolean(input) ? input : player.config.loop.active; player.embed.setLoop(toggle).then(() => { loop = toggle; @@ -272,7 +298,7 @@ const vimeo = { player.embed.getVideoWidth(), player.embed.getVideoHeight(), ]).then(dimensions => { - const ratio = utils.getAspectRatio(dimensions[0], dimensions[1]); + const ratio = getAspectRatio(dimensions[0], dimensions[1]); vimeo.setAspectRatio.call(this, ratio); }); @@ -290,13 +316,13 @@ const vimeo = { // Get current time player.embed.getCurrentTime().then(value => { currentTime = value; - utils.dispatchEvent.call(player, player.media, 'timeupdate'); + trigger.call(player, player.media, 'timeupdate'); }); // Get duration player.embed.getDuration().then(value => { player.media.duration = value; - utils.dispatchEvent.call(player, player.media, 'durationchange'); + trigger.call(player, player.media, 'durationchange'); }); // Get captions @@ -306,7 +332,7 @@ const vimeo = { }); player.embed.on('cuechange', ({ cues = [] }) => { - const strippedCues = cues.map(cue => utils.stripHTML(cue.text)); + const strippedCues = cues.map(cue => stripHTML(cue.text)); captions.updateCues.call(player, strippedCues); }); @@ -315,11 +341,11 @@ const vimeo = { player.embed.getPaused().then(paused => { assurePlaybackState.call(player, !paused); if (!paused) { - utils.dispatchEvent.call(player, player.media, 'playing'); + trigger.call(player, player.media, 'playing'); } }); - if (utils.is.element(player.embed.element) && player.supported.ui) { + if (is.element(player.embed.element) && player.supported.ui) { const frame = player.embed.element; // Fix keyboard focus issues @@ -330,7 +356,7 @@ const vimeo = { player.embed.on('play', () => { assurePlaybackState.call(player, true); - utils.dispatchEvent.call(player, player.media, 'playing'); + trigger.call(player, player.media, 'playing'); }); player.embed.on('pause', () => { @@ -340,16 +366,16 @@ const vimeo = { player.embed.on('timeupdate', data => { player.media.seeking = false; currentTime = data.seconds; - utils.dispatchEvent.call(player, player.media, 'timeupdate'); + trigger.call(player, player.media, 'timeupdate'); }); player.embed.on('progress', data => { player.media.buffered = data.percent; - utils.dispatchEvent.call(player, player.media, 'progress'); + trigger.call(player, player.media, 'progress'); // Check all loaded if (parseInt(data.percent, 10) === 1) { - utils.dispatchEvent.call(player, player.media, 'canplaythrough'); + trigger.call(player, player.media, 'canplaythrough'); } // Get duration as if we do it before load, it gives an incorrect value @@ -357,24 +383,24 @@ const vimeo = { player.embed.getDuration().then(value => { if (value !== player.media.duration) { player.media.duration = value; - utils.dispatchEvent.call(player, player.media, 'durationchange'); + trigger.call(player, player.media, 'durationchange'); } }); }); player.embed.on('seeked', () => { player.media.seeking = false; - utils.dispatchEvent.call(player, player.media, 'seeked'); + trigger.call(player, player.media, 'seeked'); }); player.embed.on('ended', () => { player.media.paused = true; - utils.dispatchEvent.call(player, player.media, 'ended'); + trigger.call(player, player.media, 'ended'); }); player.embed.on('error', detail => { player.media.error = detail; - utils.dispatchEvent.call(player, player.media, 'error'); + trigger.call(player, player.media, 'error'); }); // Rebuild UI diff --git a/src/js/plugins/youtube.js b/src/js/plugins/youtube.js index 9b067c8a..e486aa43 100644 --- a/src/js/plugins/youtube.js +++ b/src/js/plugins/youtube.js @@ -4,7 +4,24 @@ import controls from './../controls'; import ui from './../ui'; -import utils from './../utils'; +import { dedupe } from './../utils/arrays'; +import { createElement, replaceElement, toggleClass } from './../utils/elements'; +import { trigger } from './../utils/events'; +import fetch from './../utils/fetch'; +import is from './../utils/is'; +import loadImage from './../utils/loadImage'; +import loadScript from './../utils/loadScript'; +import { format, generateId } from './../utils/strings'; + +// Parse YouTube ID from URL +function parseId(url) { + if (is.empty(url)) { + return null; + } + + const regex = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|&v=)([^#&?]*).*/; + return url.match(regex) ? RegExp.$2 : url; +} // Standardise YouTube quality unit function mapQualityUnit(input) { @@ -57,11 +74,11 @@ function mapQualityUnit(input) { } function mapQualityUnits(levels) { - if (utils.is.empty(levels)) { + if (is.empty(levels)) { return levels; } - return utils.dedupe(levels.map(level => mapQualityUnit(level))); + return dedupe(levels.map(level => mapQualityUnit(level))); } // Set playback state and trigger change (only on actual change) @@ -71,24 +88,24 @@ function assurePlaybackState(play) { } if (this.media.paused === play) { this.media.paused = !play; - utils.dispatchEvent.call(this, this.media, play ? 'play' : 'pause'); + trigger.call(this, this.media, play ? 'play' : 'pause'); } } const youtube = { setup() { // Add embed class for responsive - utils.toggleClass(this.elements.wrapper, this.config.classNames.embed, true); + toggleClass(this.elements.wrapper, this.config.classNames.embed, true); // Set aspect ratio youtube.setAspectRatio.call(this); // Setup API - if (utils.is.object(window.YT) && utils.is.function(window.YT.Player)) { + if (is.object(window.YT) && is.function(window.YT.Player)) { youtube.ready.call(this); } else { // Load the API - utils.loadScript(this.config.urls.youtube.sdk).catch(error => { + loadScript(this.config.urls.youtube.sdk).catch(error => { this.debug.warn('YouTube API failed to load', error); }); @@ -115,10 +132,10 @@ const youtube = { // Try via undocumented API method first // This method disappears now and then though... // https://github.com/sampotts/plyr/issues/709 - if (utils.is.function(this.embed.getVideoData)) { + if (is.function(this.embed.getVideoData)) { const { title } = this.embed.getVideoData(); - if (utils.is.empty(title)) { + if (is.empty(title)) { this.config.title = title; ui.setTitle.call(this); return; @@ -127,13 +144,12 @@ const youtube = { // Or via Google API const key = this.config.keys.google; - if (utils.is.string(key) && !utils.is.empty(key)) { - const url = utils.format(this.config.urls.youtube.api, videoId, key); + if (is.string(key) && !is.empty(key)) { + const url = format(this.config.urls.youtube.api, videoId, key); - utils - .fetch(url) + fetch(url) .then(result => { - if (utils.is.object(result)) { + if (is.object(result)) { this.config.title = result.items[0].snippet.title; ui.setTitle.call(this); } @@ -154,7 +170,7 @@ const youtube = { // Ignore already setup (race condition) const currentId = player.media.getAttribute('id'); - if (!utils.is.empty(currentId) && currentId.startsWith('youtube-')) { + if (!is.empty(currentId) && currentId.startsWith('youtube-')) { return; } @@ -162,23 +178,23 @@ const youtube = { let source = player.media.getAttribute('src'); // Get from <div> if needed - if (utils.is.empty(source)) { + if (is.empty(source)) { source = player.media.getAttribute(this.config.attributes.embed.id); } // Replace the <iframe> with a <div> due to YouTube API issues - const videoId = utils.parseYouTubeId(source); - const id = utils.generateId(player.provider); - const container = utils.createElement('div', { id }); - player.media = utils.replaceElement(container, player.media); + const videoId = parseId(source); + const id = generateId(player.provider); + const container = createElement('div', { id }); + player.media = replaceElement(container, player.media); // Set poster image const posterSrc = format => `https://img.youtube.com/vi/${videoId}/${format}default.jpg`; // Check thumbnail images in order of quality, but reject fallback thumbnails (120px wide) - utils.loadImage(posterSrc('maxres'), 121) // Higest quality and unpadded - .catch(() => utils.loadImage(posterSrc('sd'), 121)) // 480p padded 4:3 - .catch(() => utils.loadImage(posterSrc('hq'))) // 360p padded 4:3. Always exists + loadImage(posterSrc('maxres'), 121) // Higest quality and unpadded + .catch(() => loadImage(posterSrc('sd'), 121)) // 480p padded 4:3 + .catch(() => loadImage(posterSrc('hq'))) // 360p padded 4:3. Always exists .then(image => ui.setPoster.call(player, image.src)) .then(posterSrc => { // If the image is padded, use background-size "cover" instead (like youtube does too with their posters) @@ -213,7 +229,7 @@ const youtube = { onError(event) { // If we've already fired an error, don't do it again // YouTube fires onError twice - if (utils.is.object(player.media.error)) { + if (is.object(player.media.error)) { return; } @@ -250,10 +266,10 @@ const youtube = { player.media.error = detail; - utils.dispatchEvent.call(player, player.media, 'error'); + trigger.call(player, player.media, 'error'); }, onPlaybackQualityChange() { - utils.dispatchEvent.call(player, player.media, 'qualitychange', false, { + trigger.call(player, player.media, 'qualitychange', false, { quality: player.media.quality, }); }, @@ -264,7 +280,7 @@ const youtube = { // Get current speed player.media.playbackRate = instance.getPlaybackRate(); - utils.dispatchEvent.call(player, player.media, 'ratechange'); + trigger.call(player, player.media, 'ratechange'); }, onReady(event) { // Get the instance @@ -305,7 +321,7 @@ const youtube = { // Set seeking state and trigger event player.media.seeking = true; - utils.dispatchEvent.call(player, player.media, 'seeking'); + trigger.call(player, player.media, 'seeking'); // Seek after events sent instance.seekTo(time); @@ -334,7 +350,7 @@ const youtube = { instance.setPlaybackQuality(mapQualityUnit(quality)); // Trigger request event - utils.dispatchEvent.call(player, player.media, 'qualityrequested', false, { + trigger.call(player, player.media, 'qualityrequested', false, { quality, }); }, @@ -349,7 +365,7 @@ const youtube = { set(input) { volume = input; instance.setVolume(volume * 100); - utils.dispatchEvent.call(player, player.media, 'volumechange'); + trigger.call(player, player.media, 'volumechange'); }, }); @@ -360,10 +376,10 @@ const youtube = { return muted; }, set(input) { - const toggle = utils.is.boolean(input) ? input : muted; + const toggle = is.boolean(input) ? input : muted; muted = toggle; instance[toggle ? 'mute' : 'unMute'](); - utils.dispatchEvent.call(player, player.media, 'volumechange'); + trigger.call(player, player.media, 'volumechange'); }, }); @@ -389,8 +405,8 @@ const youtube = { player.media.setAttribute('tabindex', -1); } - utils.dispatchEvent.call(player, player.media, 'timeupdate'); - utils.dispatchEvent.call(player, player.media, 'durationchange'); + trigger.call(player, player.media, 'timeupdate'); + trigger.call(player, player.media, 'durationchange'); // Reset timer clearInterval(player.timers.buffering); @@ -402,7 +418,7 @@ const youtube = { // Trigger progress only when we actually buffer something if (player.media.lastBuffered === null || player.media.lastBuffered < player.media.buffered) { - utils.dispatchEvent.call(player, player.media, 'progress'); + trigger.call(player, player.media, 'progress'); } // Set last buffer point @@ -413,7 +429,7 @@ const youtube = { clearInterval(player.timers.buffering); // Trigger event - utils.dispatchEvent.call(player, player.media, 'canplaythrough'); + trigger.call(player, player.media, 'canplaythrough'); } }, 200); @@ -435,7 +451,7 @@ const youtube = { if (seeked) { // Unset seeking and fire seeked event player.media.seeking = false; - utils.dispatchEvent.call(player, player.media, 'seeked'); + trigger.call(player, player.media, 'seeked'); } // Handle events @@ -448,11 +464,11 @@ const youtube = { switch (event.data) { case -1: // Update scrubber - utils.dispatchEvent.call(player, player.media, 'timeupdate'); + trigger.call(player, player.media, 'timeupdate'); // Get loaded % from YouTube player.media.buffered = instance.getVideoLoadedFraction(); - utils.dispatchEvent.call(player, player.media, 'progress'); + trigger.call(player, player.media, 'progress'); break; @@ -465,7 +481,7 @@ const youtube = { instance.stopVideo(); instance.playVideo(); } else { - utils.dispatchEvent.call(player, player.media, 'ended'); + trigger.call(player, player.media, 'ended'); } break; @@ -477,11 +493,11 @@ const youtube = { } else { assurePlaybackState.call(player, true); - utils.dispatchEvent.call(player, player.media, 'playing'); + trigger.call(player, player.media, 'playing'); // Poll to get playback progress player.timers.playing = setInterval(() => { - utils.dispatchEvent.call(player, player.media, 'timeupdate'); + trigger.call(player, player.media, 'timeupdate'); }, 50); // Check duration again due to YouTube bug @@ -489,7 +505,7 @@ const youtube = { // https://code.google.com/p/gdata-issues/issues/detail?id=8690 if (player.media.duration !== instance.getDuration()) { player.media.duration = instance.getDuration(); - utils.dispatchEvent.call(player, player.media, 'durationchange'); + trigger.call(player, player.media, 'durationchange'); } // Get quality @@ -511,7 +527,7 @@ const youtube = { break; } - utils.dispatchEvent.call(player, player.elements.container, 'statechange', false, { + trigger.call(player, player.elements.container, 'statechange', false, { code: event.data, }); }, |