UNPKG

@brightcove/player-loader

Version:

An asynchronous script loader for the Brightcove Player.

1,234 lines (1,059 loc) 36.5 kB
/*! @name @brightcove/player-loader @version 1.8.1 @license Apache-2.0 */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global = global || self, global.brightcovePlayerLoader = factory()); }(this, function () { 'use strict'; function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } /* global globalThis window self document */ var getGlobal = function getGlobal() { if (typeof globalThis !== 'undefined') { return globalThis; } if (typeof window !== 'undefined') { return window; } if (typeof self !== 'undefined') { return self; } if (typeof global !== 'undefined') { return global; } return {}; }; var getWindow = function getWindow() { if (typeof window !== 'undefined') { return window; } var globalObject = getGlobal(); if (globalObject.window && globalObject.window.window === globalObject.window) { return globalObject.window; } return globalObject; }; var getDocument = function getDocument() { if (typeof document !== 'undefined') { return document; } var win = getWindow(); if (win && win.document) { return win.document; } throw new Error('document is not available in this environment'); }; var version = "1.8.1"; /*! @name @brightcove/player-url @version 1.2.0 @license Apache-2.0 */ var version$1 = "1.2.0"; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; // The parameters that may include JSON. var JSON_ALLOWED_PARAMS = ['catalogSearch', 'catalogSequence']; // The parameters that may be set as query string parameters for iframes. var IFRAME_ALLOWED_QUERY_PARAMS = ['adConfigId', 'applicationId', 'catalogSearch', 'catalogSequence', 'playlistId', 'playlistVideoId', 'videoId']; /** * Gets the value of a parameter and encodes it as a string. * * For certain keys, JSON is allowed and will be encoded. * * @private * @param {Object} params * A parameters object. See README for details. * * @param {string} key * The key in the params object. * * @return {string|undefined} * The encoded value - or `undefined` if none. */ var getQueryParamValue = function getQueryParamValue(params, key) { if (!params || params[key] === undefined) { return; } // If it's not a string, such as with a catalog search or sequence, we // try to encode it as JSON. if (typeof params[key] !== 'string' && JSON_ALLOWED_PARAMS.indexOf(key) !== -1) { try { return encodeURIComponent(JSON.stringify(params[key])); } catch (x) { // If it's not a string and we can't encode as JSON, it's ignored entirely. return; } } return encodeURIComponent(String(params[key]).trim()) || undefined; }; /** * In some cases, we need to add query string parameters to an iframe URL. * * @private * @param {Object} params * An object of query parameters. * * @return {string} * A query string starting with `?`. If no valid parameters are given, * returns an empty string. */ var getQueryString = function getQueryString(params) { return Object.keys(params).filter(function (k) { return IFRAME_ALLOWED_QUERY_PARAMS.indexOf(k) !== -1; }).reduce(function (qs, k) { var value = getQueryParamValue(params, k); if (value !== undefined) { qs += qs ? '&' : '?'; qs += encodeURIComponent(k) + '=' + value; } return qs; }, ''); }; /** * Generate a URL to a Brightcove Player. * * @param {Object} params * A set of parameters describing the player URL to create. * * @param {string} params.accountId * A Brightcove account ID. * * @param {string} [params.playerId="default"] * A Brightcove player ID. * * @param {string} [params.embedId="default"] * A Brightcove player embed ID. * * @param {boolean} [params.iframe=false] * Whether to return a URL for an HTML document to be embedded in * an iframe. * * @param {boolean} [params.minified=true] * When the `iframe` argument is `false`, this can be used to control * whether the minified or unminified JavaScript URL is returned. * * @param {string} [params.base="https://players.brightcove.net"] * A base CDN protocol and hostname. Mainly used for testing. * * @return {string} * A URL to a Brightcove Player. */ var brightcovePlayerUrl = function brightcovePlayerUrl(_ref) { var accountId = _ref.accountId, _ref$base = _ref.base, base = _ref$base === undefined ? 'https://players.brightcove.net' : _ref$base, _ref$playerId = _ref.playerId, playerId = _ref$playerId === undefined ? 'default' : _ref$playerId, _ref$embedId = _ref.embedId, embedId = _ref$embedId === undefined ? 'default' : _ref$embedId, _ref$iframe = _ref.iframe, iframe = _ref$iframe === undefined ? false : _ref$iframe, _ref$minified = _ref.minified, minified = _ref$minified === undefined ? true : _ref$minified, _ref$queryParams = _ref.queryParams, queryParams = _ref$queryParams === undefined ? null : _ref$queryParams; var ext = ''; if (iframe) { ext += 'html'; } else { if (minified) { ext += 'min.'; } ext += 'js'; } if (base.charAt(base.length - 1) === '/') { base = base.substring(0, base.length - 1); } var qs = ''; if (iframe && queryParams && (typeof queryParams === 'undefined' ? 'undefined' : _typeof(queryParams)) === 'object') { qs = getQueryString(queryParams); } accountId = encodeURIComponent(accountId); playerId = encodeURIComponent(playerId); embedId = encodeURIComponent(embedId); return base + '/' + accountId + '/' + playerId + '_' + embedId + '/index.' + ext + qs; }; /** * The version of this module. * * @type {string} */ brightcovePlayerUrl.VERSION = version$1; var window$1 = getWindow(); var DEFAULTS = { embedId: 'default', embedType: 'in-page', playerId: 'default', Promise: window$1.Promise, refNodeInsert: 'append' }; var DEFAULT_ASPECT_RATIO = '16:9'; var DEFAULT_IFRAME_HORIZONTAL_PLAYLIST = false; var DEFAULT_MAX_WIDTH = '100%'; var EMBED_TAG_NAME_VIDEO = 'video'; var EMBED_TAG_NAME_VIDEOJS = 'video-js'; var EMBED_TYPE_IN_PAGE = 'in-page'; var EMBED_TYPE_IFRAME = 'iframe'; var REF_NODE_INSERT_APPEND = 'append'; var REF_NODE_INSERT_PREPEND = 'prepend'; var REF_NODE_INSERT_BEFORE = 'before'; var REF_NODE_INSERT_AFTER = 'after'; var REF_NODE_INSERT_REPLACE = 'replace'; var JSON_ALLOWED_ATTRS = ['catalogSearch', 'catalogSequence']; var BASE_URL = 'https://players.brightcove.net/'; /** * Gets the URL to a player on CDN. * * @private * @param {Object} params * A parameters object. See README for details. * * @return {string} * A URL. */ var getUrl = function getUrl(params) { if (params.playerUrl) { return params.playerUrl; } var accountId = params.accountId, playerId = params.playerId, embedId = params.embedId, embedOptions = params.embedOptions; var iframe = params.embedType === EMBED_TYPE_IFRAME; return brightcovePlayerUrl({ accountId: accountId, playerId: playerId, embedId: embedId, iframe: iframe, base: BASE_URL, // The unminified embed option is the exact reverse of the minified option // here. minified: embedOptions ? !embedOptions.unminified : true, // Pass the entire params object as query params. This is safe because // @brightcove/player-url only accepts a whitelist of parameters. Anything // else will be ignored. queryParams: params }); }; /** * Function used to get the base URL - primarily for testing. * * @private * @return {string} * The current base URL. */ var getBaseUrl = function getBaseUrl() { return BASE_URL; }; /** * Function used to set the base URL - primarily for testing. * * @private * @param {string} baseUrl * A new base URL (instead of Brightcove CDN). */ var setBaseUrl = function setBaseUrl(baseUrl) { BASE_URL = baseUrl; }; var urls = { getUrl: getUrl, getBaseUrl: getBaseUrl, setBaseUrl: setBaseUrl }; /** * Is this value an element? * * @param {Element} el * A maybe element. * * @return {boolean} * Whether or not the value is a element. */ var isEl = function isEl(el) { return Boolean(el && el.nodeType === 1); }; /** * Is this value an element with a parent node? * * @param {Element} el * A maybe element. * * @return {boolean} * Whether or not the value is a element with a parent node. */ var isElInDom = function isElInDom(el) { return Boolean(isEl(el) && el.parentNode); }; /** * Creates an iframe embed code. * * @private * @param {Object} params * A parameters object. See README for details. * * @return {Element} * The DOM element that will ultimately be passed to the `bc()` function. */ var createIframeEmbed = function createIframeEmbed(params) { var doc = getDocument(); var el = doc.createElement('iframe'); el.setAttribute('allow', 'autoplay;encrypted-media;fullscreen'); el.setAttribute('allowfullscreen', 'allowfullscreen'); el.src = urls.getUrl(params); return el; }; /** * Creates an in-page embed code. * * @private * @param {Object} params * A parameters object. See README for details. * * @return {Element} * The DOM element that will ultimately be passed to the `bc()` function. */ var createInPageEmbed = function createInPageEmbed(params) { var embedOptions = params.embedOptions; var doc = getDocument(); // We DO NOT include the data-account, data-player, or data-embed attributes // here because we will be manually initializing the player. var paramsToAttrs = { adConfigId: 'data-ad-config-id', applicationId: 'data-application-id', catalogSearch: 'data-catalog-search', catalogSequence: 'data-catalog-sequence', deliveryConfigId: 'data-delivery-config-id', playlistId: 'data-playlist-id', playlistVideoId: 'data-playlist-video-id', poster: 'poster', videoId: 'data-video-id' }; var tagName = embedOptions && embedOptions.tagName || EMBED_TAG_NAME_VIDEOJS; var el = doc.createElement(tagName); Object.keys(paramsToAttrs).filter(function (key) { return params[key]; }).forEach(function (key) { var value; // If it's not a string, such as with a catalog search or sequence, we // try to encode it as JSON. if (typeof params[key] !== 'string' && JSON_ALLOWED_ATTRS.indexOf(key) !== -1) { try { value = JSON.stringify(params[key]); // If it fails, don't set anything. } catch (x) { return; } } else { value = String(params[key]).trim(); } el.setAttribute(paramsToAttrs[key], value); }); el.setAttribute('controls', 'controls'); el.classList.add('video-js'); return el; }; /** * Wraps an element in responsive intrinsic ratio elements. * * @private * @param {string} embedType * The type of the embed. * * @param {Object} embedOptions * Embed options from the params. * * @param {Element} el * The DOM element. * * @return {Element} * A new element (if needed). */ var wrapResponsive = function wrapResponsive(embedType, embedOptions, el) { if (!embedOptions.responsive) { return el; } var doc = getDocument(); el.style.position = 'absolute'; el.style.top = '0px'; el.style.right = '0px'; el.style.bottom = '0px'; el.style.left = '0px'; el.style.width = '100%'; el.style.height = '100%'; var responsive = _extends({ aspectRatio: DEFAULT_ASPECT_RATIO, iframeHorizontalPlaylist: DEFAULT_IFRAME_HORIZONTAL_PLAYLIST, maxWidth: DEFAULT_MAX_WIDTH }, embedOptions.responsive); // This value is validate at a higher level, so we can trust that it's in the // correct format. var aspectRatio = responsive.aspectRatio.split(':').map(Number); var inner = doc.createElement('div'); var paddingTop = aspectRatio[1] / aspectRatio[0] * 100; // For iframes with a horizontal playlist, the playlist takes up 20% of the // vertical space (if shown); so, adjust the vertical size of the embed to // avoid black bars. if (embedType === EMBED_TYPE_IFRAME && responsive.iframeHorizontalPlaylist) { paddingTop *= 1.25; } inner.style.paddingTop = paddingTop + '%'; inner.appendChild(el); var outer = doc.createElement('div'); outer.style.position = 'relative'; outer.style.display = 'block'; outer.style.maxWidth = responsive.maxWidth; outer.appendChild(inner); return outer; }; /** * Wraps an element in a Picture-in-Picture plugin container. * * @private * @param {Object} embedOptions * Embed options from the params. * * @param {Element} el * The DOM element. * * @return {Element} * A new element (if needed). */ var wrapPip = function wrapPip(embedOptions, el) { if (!embedOptions.pip) { return el; } var doc = getDocument(); var pip = doc.createElement('div'); pip.classList.add('vjs-pip-container'); pip.appendChild(el); return pip; }; /** * Wraps a bare embed element with necessary parent elements, depending on * embed options given in params. * * @private * @param {string} embedType * The type of the embed. * * @param {Object} embedOptions * Embed options from the params. * * @param {Element} embed * The embed DOM element. * * @return {Element} * A new element (if needed) or the embed itself. */ var wrapEmbed = function wrapEmbed(embedType, embedOptions, embed) { if (!embedOptions) { return embed; } return wrapPip(embedOptions, wrapResponsive(embedType, embedOptions, embed)); }; /** * Inserts a previously-created embed element into the page based on params. * * @private * @param {Object} params * A parameters object. See README for details. * * @param {Element} embed * The embed DOM element. * * @return {Element} * The embed DOM element. */ var insertEmbed = function insertEmbed(params, embed) { var refNode = params.refNode, refNodeInsert = params.refNodeInsert; var refNodeParent = refNode.parentNode; // Wrap the embed, if needed, in container elements to support various // plugins. var wrapped = wrapEmbed(params.embedType, params.embedOptions, embed); // Decide where to insert the wrapped embed. if (refNodeInsert === REF_NODE_INSERT_BEFORE) { refNodeParent.insertBefore(wrapped, refNode); } else if (refNodeInsert === REF_NODE_INSERT_AFTER) { refNodeParent.insertBefore(wrapped, refNode.nextElementSibling || null); } else if (refNodeInsert === REF_NODE_INSERT_REPLACE) { refNodeParent.replaceChild(wrapped, refNode); } else if (refNodeInsert === REF_NODE_INSERT_PREPEND) { refNode.insertBefore(wrapped, refNode.firstChild || null); // Append is the default. } else { refNode.appendChild(wrapped); } // If the playlist embed option is provided, we need to add a playlist element // immediately after the embed. This has to happen after the embed is inserted // into the DOM (above). if (params.embedOptions && params.embedOptions.playlist) { var doc = getDocument(); var playlistTagName = params.embedOptions.playlist.legacy ? 'ul' : 'div'; var playlist = doc.createElement(playlistTagName); playlist.classList.add('vjs-playlist'); embed.parentNode.insertBefore(playlist, embed.nextElementSibling || null); } // Clean up internal reference to the refNode to avoid potential memory // leaks in case the params get persisted somewhere. We won't need it beyond // this point. params.refNode = null; // Return the original embed element that can be passed to `bc()`. return embed; }; /** * Handles `onEmbedCreated` callback invocation. * * @private * @param {Object} params * A parameters object. See README for details. * * @param {Element} embed * The embed DOM element. * * @return {Element} * A possibly-new DOM element. */ var onEmbedCreated = function onEmbedCreated(params, embed) { if (typeof params.onEmbedCreated !== 'function') { return embed; } var result = params.onEmbedCreated(embed); if (isEl(result)) { return result; } return embed; }; /** * Creates an embed code of the appropriate type, runs any customizations * necessary, and inserts it into the DOM. * * @param {Object} params * A parameters object. See README for details. * * @return {Element} * The DOM element that will ultimately be passed to the `bc()` * function. Even when customized or wrapped, the return value will be * the target element. */ var createEmbed = function createEmbed(params) { var embed = params.embedType === EMBED_TYPE_IFRAME ? createIframeEmbed(params) : createInPageEmbed(params); return insertEmbed(params, onEmbedCreated(params, embed)); }; var window$2 = getWindow(); // Tracks previously-downloaded scripts and/or detected players. // // The keys follow the format "accountId_playerId_embedId" where accountId is // optional and defaults to "*". This happens when we detect pre-existing // player globals. var actualCache = new window$2.Map(); /** * Get the cache key given some properties. * * @private * @param {Object} props * Properties describing the player record to cache. * * @param {string} props.playerId * A player ID. * * @param {string} props.embedId * An embed ID. * * @param {string} [props.accountId="*"] * An optional account ID. This is optional because when we search for * pre-existing players to avoid downloads, we will not necessarily * know the account ID. * * @return {string} * A key to be used in the script cache. */ var key = function key(_ref) { var accountId = _ref.accountId, playerId = _ref.playerId, embedId = _ref.embedId; return (accountId || '*') + "_" + playerId + "_" + embedId; }; /** * Add an entry to the script cache. * * @private * @param {Object} props * Properties describing the player record to cache. * * @param {string} props.playerId * A player ID. * * @param {string} props.embedId * An embed ID. * * @param {string} [props.accountId="*"] * An optional account ID. This is optional because when we search for * pre-existing players to avoid downloads, we will not necessarily * know the account ID. If not given, we assume that no script was * downloaded for this player. */ var store = function store(props) { actualCache.set(key(props), props.accountId ? urls.getUrl(props) : ''); }; /** * Checks if the script cache has an entry. * * @private * @param {Object} props * Properties describing the player record to cache. * * @param {string} props.playerId * A player ID. * * @param {string} props.embedId * An embed ID. * * @param {string} [props.accountId="*"] * An optional account ID. This is optional because when we search for * pre-existing players to avoid downloads, we will not necessarily * know the account ID. * * @return {boolean} * Will be `true` if there is a matching cache entry. */ var has = function has(props) { return actualCache.has(key(props)); }; /** * Gets a cache entry. * * @private * @param {Object} props * Properties describing the player record to cache. * * @param {string} props.playerId * A player ID. * * @param {string} props.embedId * An embed ID. * * @param {string} [props.accountId="*"] * An optional account ID. This is optional because when we search for * pre-existing players to avoid downloads, we will not necessarily * know the account ID. * * @return {string} * A cache entry - a URL or empty string. * */ var get = function get(props) { return actualCache.get(key(props)); }; /** * Clears the cache. */ var clear = function clear() { actualCache.clear(); }; /** * Iterates over the cache. * * @param {Function} fn * A callback function that will be called with a value and a key * for each item in the cache. */ var forEach = function forEach(fn) { actualCache.forEach(fn); }; var playerScriptCache = { clear: clear, forEach: forEach, get: get, has: has, key: key, store: store }; var window$3 = getWindow(); var REGEX_PLAYER_EMBED = /^([A-Za-z0-9]+)_([A-Za-z0-9]+)$/; /** * Gets an array of current per-player/per-embed `bc` globals that are * attached to the `bc` global (e.g. `bc.abc123xyz_default`). * * If `bc` is not defined, returns an empty array. * * @private * @return {string[]} * An array of keys. */ var getBcGlobalKeys = function getBcGlobalKeys() { return window$3.bc ? Object.keys(window$3.bc).filter(function (k) { return REGEX_PLAYER_EMBED.test(k); }) : []; }; /** * Gets known global object keys that Brightcove Players may create. * * @private * @return {string[]} * An array of global variables that were added during testing. */ var getGlobalKeys = function getGlobalKeys() { return Object.keys(window$3).filter(function (k) { return /^videojs/i.test(k) || /^(bc)$/.test(k); }); }; /** * Dispose all players from a copy of Video.js. * * @param {Function} videojs * A copy of Video.js. */ var disposeAll = function disposeAll(videojs) { if (!videojs) { return; } Object.keys(videojs.players).forEach(function (k) { var p = videojs.players[k]; if (p) { p.dispose(); } }); }; /** * Resets environment state. * * This will dispose ALL Video.js players on the page and remove ALL `bc` and * `videojs` globals it finds. */ var reset = function reset() { // Remove all script elements from the DOM. playerScriptCache.forEach(function (value, key) { // If no script URL is associated, skip it. if (!value) { return; } // Find all script elements and remove them. var doc = getDocument(); Array.prototype.slice.call(doc.querySelectorAll("script[src=\"" + value + "\"]")).forEach(function (el) { return el.parentNode.removeChild(el); }); }); // Clear the internal cache that have been downloaded. playerScriptCache.clear(); // Dispose any remaining players from the `videojs` global. disposeAll(window$3.videojs); // There may be other `videojs` instances lurking in the bowels of the // `bc` global. This should eliminate any of those. getBcGlobalKeys().forEach(function (k) { return disposeAll(window$3.bc[k].videojs); }); // Delete any global object keys that were created. getGlobalKeys().forEach(function (k) { delete window$3[k]; }); }; /** * At runtime, populate the cache with pre-detected players. This allows * people who have bundled their player or included a script tag before this * runs to not have to re-download players. */ var detectPlayers = function detectPlayers() { getBcGlobalKeys().forEach(function (k) { var matches = k.match(REGEX_PLAYER_EMBED); var props = { playerId: matches[1], embedId: matches[2] }; if (!playerScriptCache.has(props)) { playerScriptCache.store(props); } }); }; var env = { detectPlayers: detectPlayers, reset: reset }; var window$4 = getWindow(); env.detectPlayers(); /** * Is this value a function? * * @private * @param {Function} fn * A maybe function. * * @return {boolean} * Whether or not the value is a function. */ var isFn = function isFn(fn) { return typeof fn === 'function'; }; /** * Checks whether an embedType parameter is valid. * * @private * @param {string} embedType * The value to test. * * @return {boolean} * Whether the value is valid. */ var isValidEmbedType = function isValidEmbedType(embedType) { return embedType === EMBED_TYPE_IN_PAGE || embedType === EMBED_TYPE_IFRAME; }; /** * Checks whether an embedOptions.tagName parameter is valid. * * @private * @param {string} tagName * The value to test. * * @return {boolean} * Whether the value is valid. */ var isValidTagName = function isValidTagName(tagName) { return tagName === EMBED_TAG_NAME_VIDEOJS || tagName === EMBED_TAG_NAME_VIDEO; }; /** * Checks whether a refNodeInsert parameter is valid. * * @private * @param {string} refNodeInsert * The value to test. * * @return {boolean} * Whether the value is valid. */ var isValidRootInsert = function isValidRootInsert(refNodeInsert) { return refNodeInsert === REF_NODE_INSERT_APPEND || refNodeInsert === REF_NODE_INSERT_PREPEND || refNodeInsert === REF_NODE_INSERT_BEFORE || refNodeInsert === REF_NODE_INSERT_AFTER || refNodeInsert === REF_NODE_INSERT_REPLACE; }; /** * Checks parameters and throws an error on validation problems. * * @private * @param {Object} params * A parameters object. See README for details. * * @throws {Error} If accountId is missing. * @throws {Error} If refNode is missing or invalid. * @throws {Error} If embedType is missing or invalid. * @throws {Error} If attempting to use an iframe embed with options. * @throws {Error} If attempting to use embedOptions.responsiveIframe with a * non-iframe embed. * @throws {Error} If refNodeInsert is missing or invalid. */ var checkParams = function checkParams(params) { var accountId = params.accountId, embedOptions = params.embedOptions, embedType = params.embedType, options = params.options, refNode = params.refNode, refNodeInsert = params.refNodeInsert; if (!accountId) { throw new Error('accountId is required'); } else if (!isElInDom(refNode)) { throw new Error('refNode must resolve to a node attached to the DOM'); } else if (!isValidEmbedType(embedType)) { throw new Error('embedType is missing or invalid'); } else if (embedType === EMBED_TYPE_IFRAME && options) { throw new Error('cannot use options with an iframe embed'); } else if (embedOptions && embedOptions.tagName !== undefined && !isValidTagName(embedOptions.tagName)) { throw new Error("embedOptions.tagName is invalid (value: \"" + embedOptions.tagName + "\")"); } else if (embedOptions && embedOptions.responsive && embedOptions.responsive.aspectRatio && !/^\d+\:\d+$/.test(embedOptions.responsive.aspectRatio)) { throw new Error("embedOptions.responsive.aspectRatio must be in the \"n:n\" format (value: \"" + embedOptions.responsive.aspectRatio + "\")"); } else if (!isValidRootInsert(refNodeInsert)) { throw new Error('refNodeInsert is missing or invalid'); } }; /** * Normalizes a `refNode` param to an element - or `null`. * * @private * @param {Element|string} refNode * The value of a `refNode` param. * * @return {Element|null} * A DOM element or `null` if the `refNode` was given as a string and * did not match an element. */ var resolveRefNode = function resolveRefNode(refNode) { if (isElInDom(refNode)) { return refNode; } if (typeof refNode === 'string') { var doc = getDocument(); return doc.querySelector(refNode); } return null; }; /** * Initializes a player and returns it. * * @private * @param {Object} params * A parameters object. See README for details. * * @param {Element} embed * An element that will be passed to the `bc()` function. * * @param {Function} resolve * A function to call if a player is successfully initialized. * * @param {Function} reject * A function to call if a player fails to be initialized. * * @return {Object} * A success object whose `ref` is a player. */ var initPlayer = function initPlayer(params, embed, resolve, reject) { var embedId = params.embedId, playerId = params.playerId; var bc = window$4.bc[playerId + "_" + embedId] || window$4.bc; if (!bc) { return reject(new Error("missing bc function for " + playerId)); } playerScriptCache.store(params); var player; try { player = bc(embed, params.options); // Add a PLAYER_LOADER property to bcinfo to indicate this player was // loaded via that mechanism. if (player.bcinfo) { player.bcinfo.PLAYER_LOADER = true; } } catch (x) { var message = 'Could not initialize the Brightcove Player.'; // Update the rejection message based on known conditions that can cause it. if (params.embedOptions.tagName === EMBED_TAG_NAME_VIDEOJS) { message += ' You are attempting to embed using a "video-js" element.' + ' Please ensure that your Player is v6.11.0 or newer in order to' + ' support this embed type. Alternatively, pass `"video"` for' + ' `embedOptions.tagName`.'; } return reject(new Error(message)); } resolve({ type: EMBED_TYPE_IN_PAGE, ref: player }); }; /** * Loads a player from CDN and embeds it. * * @private * @param {Object} params * A parameters object. See README for details. * * @param {Function} resolve * A function to call if a player is successfully initialized. * * @param {Function} reject * A function to call if a player fails to be initialized. * @return {void} */ var loadPlayer = function loadPlayer(params, resolve, reject) { params.refNode = resolveRefNode(params.refNode); checkParams(params); var refNode = params.refNode, refNodeInsert = params.refNodeInsert; // Store a reference to the refNode parent. When we use the replace method, // we'll need it as the location to store the script element. var refNodeParent = refNode.parentNode; var embed = createEmbed(params); // If this is an iframe, all we need to do is create the embed code and // inject it. Because there is no reliable way to hook into an iframe from // the parent page, we simply resolve immediately upon creating the embed. if (params.embedType === EMBED_TYPE_IFRAME) { resolve({ type: EMBED_TYPE_IFRAME, ref: embed }); return; } // If we've already downloaded this script or detected a matching global, we // should have the proper `bc` global and can bypass the script creation // process. if (playerScriptCache.has(params)) { return initPlayer(params, embed, resolve, reject); } var doc = getDocument(); var script = doc.createElement('script'); script.onload = function () { return initPlayer(params, embed, resolve, reject); }; script.onerror = function () { reject(new Error('player script could not be downloaded')); }; script.async = true; script.charset = 'utf-8'; script.src = urls.getUrl(params); if (refNodeInsert === REF_NODE_INSERT_REPLACE) { refNodeParent.appendChild(script); } else { refNode.appendChild(script); } }; /** * A function for asynchronously loading a Brightcove Player into a web page. * * @param {Object} parameters * A parameters object. See README for details. * * @return {Promise|undefined} * A Promise, if possible. */ var brightcovePlayerLoader = function brightcovePlayerLoader(parameters) { var params = _extends({}, DEFAULTS, parameters); var Promise = params.Promise, onSuccess = params.onSuccess, onFailure = params.onFailure; // When Promise is not available or any success/failure callback is given, // do not attempt to use Promises. if (!isFn(Promise) || isFn(onSuccess) || isFn(onFailure)) { return loadPlayer(params, isFn(onSuccess) ? onSuccess : function () {}, isFn(onFailure) ? onFailure : function (err) { throw err; }); } // Promises are supported, use 'em. return new Promise(function (resolve, reject) { return loadPlayer(params, resolve, reject); }); }; /** * Expose a non-writable, non-configurable property on the * `brightcovePlayerLoader` function. * * @private * @param {string} key * The property key. * * @param {string|Function} value * The value. */ var expose = function expose(key, value) { Object.defineProperty(brightcovePlayerLoader, key, { configurable: false, enumerable: true, value: value, writable: false }); }; /** * Get the base URL for players. By default, this will be the Brightcove CDN. * * @return {string} * The current base URL. */ expose('getBaseUrl', function () { return urls.getBaseUrl(); }); /** * Set the base URL for players. By default, this will be the Brightcove CDN, * but can be overridden with this function. * * @param {string} baseUrl * A new base URL (instead of Brightcove CDN). */ expose('setBaseUrl', function (baseUrl) { urls.setBaseUrl(baseUrl); }); /** * Get the URL for a player. */ expose('getUrl', function (options) { return urls.getUrl(options); }); /** * Completely resets global state. * * This will dispose ALL Video.js players on the page and remove ALL `bc` and * `videojs` globals it finds. */ expose('reset', function () { return env.reset(); }); // Define some read-only constants on the exported function. [['EMBED_TAG_NAME_VIDEO', EMBED_TAG_NAME_VIDEO], ['EMBED_TAG_NAME_VIDEOJS', EMBED_TAG_NAME_VIDEOJS], ['EMBED_TYPE_IN_PAGE', EMBED_TYPE_IN_PAGE], ['EMBED_TYPE_IFRAME', EMBED_TYPE_IFRAME], ['REF_NODE_INSERT_APPEND', REF_NODE_INSERT_APPEND], ['REF_NODE_INSERT_PREPEND', REF_NODE_INSERT_PREPEND], ['REF_NODE_INSERT_BEFORE', REF_NODE_INSERT_BEFORE], ['REF_NODE_INSERT_AFTER', REF_NODE_INSERT_AFTER], ['REF_NODE_INSERT_REPLACE', REF_NODE_INSERT_REPLACE], ['VERSION', version]].forEach(function (arr) { expose(arr[0], arr[1]); }); return brightcovePlayerLoader; }));