UNPKG

@vimeo/player

Version:

Interact with and control an embedded Vimeo Player.

332 lines (285 loc) 9.53 kB
/** * @module lib/embed */ import Player from '../player'; import { isVimeoUrl, isVimeoEmbed, getVimeoUrl, getOembedDomain } from './functions'; import { parseMessageData } from './postmessage'; const oEmbedParameters = [ 'airplay', 'audio_tracks', 'audiotrack', 'autopause', 'autoplay', 'background', 'byline', 'cc', 'chapter_id', 'chapters', 'chromecast', 'color', 'colors', 'controls', 'dnt', 'end_time', 'fullscreen', 'height', 'id', 'interactive_params', 'keyboard', 'loop', 'maxheight', 'maxwidth', 'muted', 'play_button_position', 'playsinline', 'portrait', 'progress_bar', 'quality_selector', 'responsive', 'skipping_forward', 'speed', 'start_time', 'texttrack', 'title', 'transcript', 'transparent', 'unmute_button', 'url', 'vimeo_logo', 'volume', 'watch_full_video', 'width' ]; /** * Get the 'data-vimeo'-prefixed attributes from an element as an object. * * @param {HTMLElement} element The element. * @param {Object} [defaults={}] The default values to use. * @return {Object<string, string>} */ export function getOEmbedParameters(element, defaults = {}) { return oEmbedParameters.reduce((params, param) => { const value = element.getAttribute(`data-vimeo-${param}`); if (value || value === '') { params[param] = value === '' ? 1 : value; } return params; }, defaults); } /** * Create an embed from oEmbed data inside an element. * * @param {object} data The oEmbed data. * @param {HTMLElement} element The element to put the iframe in. * @return {HTMLIFrameElement} The iframe embed. */ export function createEmbed({ html }, element) { if (!element) { throw new TypeError('An element must be provided'); } if (element.getAttribute('data-vimeo-initialized') !== null) { return element.querySelector('iframe'); } const div = document.createElement('div'); div.innerHTML = html; element.appendChild(div.firstChild); element.setAttribute('data-vimeo-initialized', 'true'); return element.querySelector('iframe'); } /** * Make an oEmbed call for the specified URL. * * @param {string} videoUrl The vimeo.com url for the video. * @param {Object} [params] Parameters to pass to oEmbed. * @param {HTMLElement} element The element. * @return {Promise} */ export function getOEmbedData(videoUrl, params = {}, element) { return new Promise((resolve, reject) => { if (!isVimeoUrl(videoUrl)) { throw new TypeError(`“${videoUrl}” is not a vimeo.com url.`); } const domain = getOembedDomain(videoUrl); let url = `https://${domain}/api/oembed.json?url=${encodeURIComponent(videoUrl)}`; for (const param in params) { if (params.hasOwnProperty(param)) { url += `&${param}=${encodeURIComponent(params[param])}`; } } const xhr = 'XDomainRequest' in window ? new XDomainRequest() : new XMLHttpRequest(); xhr.open('GET', url, true); xhr.onload = function() { if (xhr.status === 404) { reject(new Error(`“${videoUrl}” was not found.`)); return; } if (xhr.status === 403) { reject(new Error(`“${videoUrl}” is not embeddable.`)); return; } try { const json = JSON.parse(xhr.responseText); // Check api response for 403 on oembed if (json.domain_status_code === 403) { // We still want to create the embed to give users visual feedback createEmbed(json, element); reject(new Error(`“${videoUrl}” is not embeddable.`)); return; } resolve(json); } catch (error) { reject(error); } }; xhr.onerror = function() { const status = xhr.status ? ` (${xhr.status})` : ''; reject(new Error(`There was an error fetching the embed code from Vimeo${status}.`)); }; xhr.send(); }); } /** * Initialize all embeds within a specific element * * @param {HTMLElement} [parent=document] The parent element. * @return {void} */ export function initializeEmbeds(parent = document) { const elements = [].slice.call(parent.querySelectorAll('[data-vimeo-id], [data-vimeo-url]')); const handleError = (error) => { if ('console' in window && console.error) { console.error(`There was an error creating an embed: ${error}`); } }; elements.forEach((element) => { try { // Skip any that have data-vimeo-defer if (element.getAttribute('data-vimeo-defer') !== null) { return; } const params = getOEmbedParameters(element); const url = getVimeoUrl(params); getOEmbedData(url, params, element).then((data) => { return createEmbed(data, element); }).catch(handleError); } catch (error) { handleError(error); } }); } /** * Resize embeds when messaged by the player. * * @param {HTMLElement} [parent=document] The parent element. * @return {void} */ export function resizeEmbeds(parent = document) { // Prevent execution if users include the player.js script multiple times. if (window.VimeoPlayerResizeEmbeds_) { return; } window.VimeoPlayerResizeEmbeds_ = true; const onMessage = (event) => { if (!isVimeoUrl(event.origin)) { return; } // 'spacechange' is fired only on embeds with cards if (!event.data || event.data.event !== 'spacechange') { return; } const iframes = parent.querySelectorAll('iframe'); for (let i = 0; i < iframes.length; i++) { if (iframes[i].contentWindow !== event.source) { continue; } // Change padding-bottom of the enclosing div to accommodate // card carousel without distorting aspect ratio const space = iframes[i].parentElement; space.style.paddingBottom = `${event.data.data[0].bottom}px`; break; } }; window.addEventListener('message', onMessage); } /** * Add chapters to existing metadata for Google SEO * * @param {HTMLElement} [parent=document] The parent element. * @return {void} */ export function initAppendVideoMetadata(parent = document) { // Prevent execution if users include the player.js script multiple times. if (window.VimeoSeoMetadataAppended) { return; } window.VimeoSeoMetadataAppended = true; const onMessage = (event) => { if (!isVimeoUrl(event.origin)) { return; } const data = parseMessageData(event.data); if (!data || data.event !== 'ready') { return; } const iframes = parent.querySelectorAll('iframe'); for (let i = 0; i < iframes.length; i++) { const iframe = iframes[i]; // Initiate appendVideoMetadata if iframe is a Vimeo embed const isValidMessageSource = iframe.contentWindow === event.source; if (isVimeoEmbed(iframe.src) && isValidMessageSource) { const player = new Player(iframe); player.callMethod('appendVideoMetadata', window.location.href); } } }; window.addEventListener('message', onMessage); } /** * Seek to time indicated by vimeo_t query parameter if present in URL * * @param {HTMLElement} [parent=document] The parent element. * @return {void} */ export function checkUrlTimeParam(parent = document) { // Prevent execution if users include the player.js script multiple times. if (window.VimeoCheckedUrlTimeParam) { return; } window.VimeoCheckedUrlTimeParam = true; const handleError = (error) => { if ('console' in window && console.error) { console.error(`There was an error getting video Id: ${error}`); } }; const onMessage = (event) => { if (!isVimeoUrl(event.origin)) { return; } const data = parseMessageData(event.data); if (!data || data.event !== 'ready') { return; } const iframes = parent.querySelectorAll('iframe'); for (let i = 0; i < iframes.length; i++) { const iframe = iframes[i]; const isValidMessageSource = iframe.contentWindow === event.source; if (isVimeoEmbed(iframe.src) && isValidMessageSource) { const player = new Player(iframe); player .getVideoId() .then((videoId) => { const matches = new RegExp(`[?&]vimeo_t_${videoId}=([^&#]*)`).exec(window.location.href); if (matches && matches[1]) { const sec = decodeURI(matches[1]); player.setCurrentTime(sec); } return; }) .catch(handleError); } } }; window.addEventListener('message', onMessage); }