{"version":3,"file":"video-lazy.min.js","sources":["https:\/\/lms.svhs.co\/media\/player\/videojs\/amd\/src\/video-lazy.js"],"sourcesContent":["\/**\n * @license\n * Video.js 8.17.3 \n * Copyright Brightcove, Inc. \n * Available under Apache License Version 2.0\n * \n *\n * Includes vtt.js \n * Available under Apache License Version 2.0\n * \n *\/\n\n(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :\n typeof define === 'function' && define.amd ? define(factory) :\n (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.videojs = factory());\n})(this, (function () { 'use strict';\n\n var version$5 = \"8.17.3\";\n\n \/**\n * An Object that contains lifecycle hooks as keys which point to an array\n * of functions that are run when a lifecycle is triggered\n *\n * @private\n *\/\n const hooks_ = {};\n\n \/**\n * Get a list of hooks for a specific lifecycle\n *\n * @param {string} type\n * the lifecycle to get hooks from\n *\n * @param {Function|Function[]} [fn]\n * Optionally add a hook (or hooks) to the lifecycle that your are getting.\n *\n * @return {Array}\n * an array of hooks, or an empty array if there are none.\n *\/\n const hooks = function (type, fn) {\n hooks_[type] = hooks_[type] || [];\n if (fn) {\n hooks_[type] = hooks_[type].concat(fn);\n }\n return hooks_[type];\n };\n\n \/**\n * Add a function hook to a specific videojs lifecycle.\n *\n * @param {string} type\n * the lifecycle to hook the function to.\n *\n * @param {Function|Function[]}\n * The function or array of functions to attach.\n *\/\n const hook = function (type, fn) {\n hooks(type, fn);\n };\n\n \/**\n * Remove a hook from a specific videojs lifecycle.\n *\n * @param {string} type\n * the lifecycle that the function hooked to\n *\n * @param {Function} fn\n * The hooked function to remove\n *\n * @return {boolean}\n * The function that was removed or undef\n *\/\n const removeHook = function (type, fn) {\n const index = hooks(type).indexOf(fn);\n if (index <= -1) {\n return false;\n }\n hooks_[type] = hooks_[type].slice();\n hooks_[type].splice(index, 1);\n return true;\n };\n\n \/**\n * Add a function hook that will only run once to a specific videojs lifecycle.\n *\n * @param {string} type\n * the lifecycle to hook the function to.\n *\n * @param {Function|Function[]}\n * The function or array of functions to attach.\n *\/\n const hookOnce = function (type, fn) {\n hooks(type, [].concat(fn).map(original => {\n const wrapper = (...args) => {\n removeHook(type, wrapper);\n return original(...args);\n };\n return wrapper;\n }));\n };\n\n \/**\n * @file fullscreen-api.js\n * @module fullscreen-api\n *\/\n\n \/**\n * Store the browser-specific methods for the fullscreen API.\n *\n * @type {Object}\n * @see [Specification]{@link https:\/\/fullscreen.spec.whatwg.org}\n * @see [Map Approach From Screenfull.js]{@link https:\/\/github.com\/sindresorhus\/screenfull.js}\n *\/\n const FullscreenApi = {\n prefixed: true\n };\n\n \/\/ browser API methods\n const apiMap = [['requestFullscreen', 'exitFullscreen', 'fullscreenElement', 'fullscreenEnabled', 'fullscreenchange', 'fullscreenerror', 'fullscreen'],\n \/\/ WebKit\n ['webkitRequestFullscreen', 'webkitExitFullscreen', 'webkitFullscreenElement', 'webkitFullscreenEnabled', 'webkitfullscreenchange', 'webkitfullscreenerror', '-webkit-full-screen']];\n const specApi = apiMap[0];\n let browserApi;\n\n \/\/ determine the supported set of functions\n for (let i = 0; i < apiMap.length; i++) {\n \/\/ check for exitFullscreen function\n if (apiMap[i][1] in document) {\n browserApi = apiMap[i];\n break;\n }\n }\n\n \/\/ map the browser API names to the spec API names\n if (browserApi) {\n for (let i = 0; i < browserApi.length; i++) {\n FullscreenApi[specApi[i]] = browserApi[i];\n }\n FullscreenApi.prefixed = browserApi[0] !== specApi[0];\n }\n\n \/**\n * @file create-logger.js\n * @module create-logger\n *\/\n\n \/\/ This is the private tracking variable for the logging history.\n let history = [];\n\n \/**\n * Log messages to the console and history based on the type of message\n *\n * @private\n * @param {string} name\n * The name of the console method to use.\n *\n * @param {Object} log\n * The arguments to be passed to the matching console method.\n *\n * @param {string} [styles]\n * styles for name\n *\/\n const LogByTypeFactory = (name, log, styles) => (type, level, args) => {\n const lvl = log.levels[level];\n const lvlRegExp = new RegExp(`^(${lvl})$`);\n let resultName = name;\n if (type !== 'log') {\n \/\/ Add the type to the front of the message when it's not \"log\".\n args.unshift(type.toUpperCase() + ':');\n }\n if (styles) {\n resultName = `%c${name}`;\n args.unshift(styles);\n }\n\n \/\/ Add console prefix after adding to history.\n args.unshift(resultName + ':');\n\n \/\/ Add a clone of the args at this point to history.\n if (history) {\n history.push([].concat(args));\n\n \/\/ only store 1000 history entries\n const splice = history.length - 1000;\n history.splice(0, splice > 0 ? splice : 0);\n }\n\n \/\/ If there's no console then don't try to output messages, but they will\n \/\/ still be stored in history.\n if (!window.console) {\n return;\n }\n\n \/\/ Was setting these once outside of this function, but containing them\n \/\/ in the function makes it easier to test cases where console doesn't exist\n \/\/ when the module is executed.\n let fn = window.console[type];\n if (!fn && type === 'debug') {\n \/\/ Certain browsers don't have support for console.debug. For those, we\n \/\/ should default to the closest comparable log.\n fn = window.console.info || window.console.log;\n }\n\n \/\/ Bail out if there's no console or if this type is not allowed by the\n \/\/ current logging level.\n if (!fn || !lvl || !lvlRegExp.test(type)) {\n return;\n }\n fn[Array.isArray(args) ? 'apply' : 'call'](window.console, args);\n };\n function createLogger$1(name, delimiter = ':', styles = '') {\n \/\/ This is the private tracking variable for logging level.\n let level = 'info';\n\n \/\/ the curried logByType bound to the specific log and history\n let logByType;\n\n \/**\n * Logs plain debug messages. Similar to `console.log`.\n *\n * Due to [limitations](https:\/\/github.com\/jsdoc3\/jsdoc\/issues\/955#issuecomment-313829149)\n * of our JSDoc template, we cannot properly document this as both a function\n * and a namespace, so its function signature is documented here.\n *\n * #### Arguments\n * ##### *args\n * *[]\n *\n * Any combination of values that could be passed to `console.log()`.\n *\n * #### Return Value\n *\n * `undefined`\n *\n * @namespace\n * @param {...*} args\n * One or more messages or objects that should be logged.\n *\/\n function log(...args) {\n logByType('log', level, args);\n }\n\n \/\/ This is the logByType helper that the logging methods below use\n logByType = LogByTypeFactory(name, log, styles);\n\n \/**\n * Create a new subLogger which chains the old name to the new name.\n *\n * For example, doing `mylogger = videojs.log.createLogger('player')` and then using that logger will log the following:\n * ```js\n * mylogger('foo');\n * \/\/ > VIDEOJS: player: foo\n * ```\n *\n * @param {string} subName\n * The name to add call the new logger\n * @param {string} [subDelimiter]\n * Optional delimiter\n * @param {string} [subStyles]\n * Optional styles\n * @return {Object}\n *\/\n log.createLogger = (subName, subDelimiter, subStyles) => {\n const resultDelimiter = subDelimiter !== undefined ? subDelimiter : delimiter;\n const resultStyles = subStyles !== undefined ? subStyles : styles;\n const resultName = `${name} ${resultDelimiter} ${subName}`;\n return createLogger$1(resultName, resultDelimiter, resultStyles);\n };\n\n \/**\n * Create a new logger.\n *\n * @param {string} newName\n * The name for the new logger\n * @param {string} [newDelimiter]\n * Optional delimiter\n * @param {string} [newStyles]\n * Optional styles\n * @return {Object}\n *\/\n log.createNewLogger = (newName, newDelimiter, newStyles) => {\n return createLogger$1(newName, newDelimiter, newStyles);\n };\n\n \/**\n * Enumeration of available logging levels, where the keys are the level names\n * and the values are `|`-separated strings containing logging methods allowed\n * in that logging level. These strings are used to create a regular expression\n * matching the function name being called.\n *\n * Levels provided by Video.js are:\n *\n * - `off`: Matches no calls. Any value that can be cast to `false` will have\n * this effect. The most restrictive.\n * - `all`: Matches only Video.js-provided functions (`debug`, `log`,\n * `log.warn`, and `log.error`).\n * - `debug`: Matches `log.debug`, `log`, `log.warn`, and `log.error` calls.\n * - `info` (default): Matches `log`, `log.warn`, and `log.error` calls.\n * - `warn`: Matches `log.warn` and `log.error` calls.\n * - `error`: Matches only `log.error` calls.\n *\n * @type {Object}\n *\/\n log.levels = {\n all: 'debug|log|warn|error',\n off: '',\n debug: 'debug|log|warn|error',\n info: 'log|warn|error',\n warn: 'warn|error',\n error: 'error',\n DEFAULT: level\n };\n\n \/**\n * Get or set the current logging level.\n *\n * If a string matching a key from {@link module:log.levels} is provided, acts\n * as a setter.\n *\n * @param {'all'|'debug'|'info'|'warn'|'error'|'off'} [lvl]\n * Pass a valid level to set a new logging level.\n *\n * @return {string}\n * The current logging level.\n *\/\n log.level = lvl => {\n if (typeof lvl === 'string') {\n if (!log.levels.hasOwnProperty(lvl)) {\n throw new Error(`\"${lvl}\" in not a valid log level`);\n }\n level = lvl;\n }\n return level;\n };\n\n \/**\n * Returns an array containing everything that has been logged to the history.\n *\n * This array is a shallow clone of the internal history record. However, its\n * contents are _not_ cloned; so, mutating objects inside this array will\n * mutate them in history.\n *\n * @return {Array}\n *\/\n log.history = () => history ? [].concat(history) : [];\n\n \/**\n * Allows you to filter the history by the given logger name\n *\n * @param {string} fname\n * The name to filter by\n *\n * @return {Array}\n * The filtered list to return\n *\/\n log.history.filter = fname => {\n return (history || []).filter(historyItem => {\n \/\/ if the first item in each historyItem includes `fname`, then it's a match\n return new RegExp(`.*${fname}.*`).test(historyItem[0]);\n });\n };\n\n \/**\n * Clears the internal history tracking, but does not prevent further history\n * tracking.\n *\/\n log.history.clear = () => {\n if (history) {\n history.length = 0;\n }\n };\n\n \/**\n * Disable history tracking if it is currently enabled.\n *\/\n log.history.disable = () => {\n if (history !== null) {\n history.length = 0;\n history = null;\n }\n };\n\n \/**\n * Enable history tracking if it is currently disabled.\n *\/\n log.history.enable = () => {\n if (history === null) {\n history = [];\n }\n };\n\n \/**\n * Logs error messages. Similar to `console.error`.\n *\n * @param {...*} args\n * One or more messages or objects that should be logged as an error\n *\/\n log.error = (...args) => logByType('error', level, args);\n\n \/**\n * Logs warning messages. Similar to `console.warn`.\n *\n * @param {...*} args\n * One or more messages or objects that should be logged as a warning.\n *\/\n log.warn = (...args) => logByType('warn', level, args);\n\n \/**\n * Logs debug messages. Similar to `console.debug`, but may also act as a comparable\n * log if `console.debug` is not available\n *\n * @param {...*} args\n * One or more messages or objects that should be logged as debug.\n *\/\n log.debug = (...args) => logByType('debug', level, args);\n return log;\n }\n\n \/**\n * @file log.js\n * @module log\n *\/\n const log$1 = createLogger$1('VIDEOJS');\n const createLogger = log$1.createLogger;\n\n \/**\n * @file obj.js\n * @module obj\n *\/\n\n \/**\n * @callback obj:EachCallback\n *\n * @param {*} value\n * The current key for the object that is being iterated over.\n *\n * @param {string} key\n * The current key-value for object that is being iterated over\n *\/\n\n \/**\n * @callback obj:ReduceCallback\n *\n * @param {*} accum\n * The value that is accumulating over the reduce loop.\n *\n * @param {*} value\n * The current key for the object that is being iterated over.\n *\n * @param {string} key\n * The current key-value for object that is being iterated over\n *\n * @return {*}\n * The new accumulated value.\n *\/\n const toString$1 = Object.prototype.toString;\n\n \/**\n * Get the keys of an Object\n *\n * @param {Object}\n * The Object to get the keys from\n *\n * @return {string[]}\n * An array of the keys from the object. Returns an empty array if the\n * object passed in was invalid or had no keys.\n *\n * @private\n *\/\n const keys = function (object) {\n return isObject$1(object) ? Object.keys(object) : [];\n };\n\n \/**\n * Array-like iteration for objects.\n *\n * @param {Object} object\n * The object to iterate over\n *\n * @param {obj:EachCallback} fn\n * The callback function which is called for each key in the object.\n *\/\n function each(object, fn) {\n keys(object).forEach(key => fn(object[key], key));\n }\n\n \/**\n * Array-like reduce for objects.\n *\n * @param {Object} object\n * The Object that you want to reduce.\n *\n * @param {Function} fn\n * A callback function which is called for each key in the object. It\n * receives the accumulated value and the per-iteration value and key\n * as arguments.\n *\n * @param {*} [initial = 0]\n * Starting value\n *\n * @return {*}\n * The final accumulated value.\n *\/\n function reduce(object, fn, initial = 0) {\n return keys(object).reduce((accum, key) => fn(accum, object[key], key), initial);\n }\n\n \/**\n * Returns whether a value is an object of any kind - including DOM nodes,\n * arrays, regular expressions, etc. Not functions, though.\n *\n * This avoids the gotcha where using `typeof` on a `null` value\n * results in `'object'`.\n *\n * @param {Object} value\n * @return {boolean}\n *\/\n function isObject$1(value) {\n return !!value && typeof value === 'object';\n }\n\n \/**\n * Returns whether an object appears to be a \"plain\" object - that is, a\n * direct instance of `Object`.\n *\n * @param {Object} value\n * @return {boolean}\n *\/\n function isPlain(value) {\n return isObject$1(value) && toString$1.call(value) === '[object Object]' && value.constructor === Object;\n }\n\n \/**\n * Merge two objects recursively.\n *\n * Performs a deep merge like\n * {@link https:\/\/lodash.com\/docs\/4.17.10#merge|lodash.merge}, but only merges\n * plain objects (not arrays, elements, or anything else).\n *\n * Non-plain object values will be copied directly from the right-most\n * argument.\n *\n * @param {Object[]} sources\n * One or more objects to merge into a new object.\n *\n * @return {Object}\n * A new object that is the merged result of all sources.\n *\/\n function merge$2(...sources) {\n const result = {};\n sources.forEach(source => {\n if (!source) {\n return;\n }\n each(source, (value, key) => {\n if (!isPlain(value)) {\n result[key] = value;\n return;\n }\n if (!isPlain(result[key])) {\n result[key] = {};\n }\n result[key] = merge$2(result[key], value);\n });\n });\n return result;\n }\n\n \/**\n * Returns an array of values for a given object\n *\n * @param {Object} source - target object\n * @return {Array} - object values\n *\/\n function values$1(source = {}) {\n const result = [];\n for (const key in source) {\n if (source.hasOwnProperty(key)) {\n const value = source[key];\n result.push(value);\n }\n }\n return result;\n }\n\n \/**\n * Object.defineProperty but \"lazy\", which means that the value is only set after\n * it is retrieved the first time, rather than being set right away.\n *\n * @param {Object} obj the object to set the property on\n * @param {string} key the key for the property to set\n * @param {Function} getValue the function used to get the value when it is needed.\n * @param {boolean} setter whether a setter should be allowed or not\n *\/\n function defineLazyProperty(obj, key, getValue, setter = true) {\n const set = value => Object.defineProperty(obj, key, {\n value,\n enumerable: true,\n writable: true\n });\n const options = {\n configurable: true,\n enumerable: true,\n get() {\n const value = getValue();\n set(value);\n return value;\n }\n };\n if (setter) {\n options.set = set;\n }\n return Object.defineProperty(obj, key, options);\n }\n\n var Obj = \/*#__PURE__*\/Object.freeze({\n __proto__: null,\n each: each,\n reduce: reduce,\n isObject: isObject$1,\n isPlain: isPlain,\n merge: merge$2,\n values: values$1,\n defineLazyProperty: defineLazyProperty\n });\n\n \/**\n * @file browser.js\n * @module browser\n *\/\n\n \/**\n * Whether or not this device is an iPod.\n *\n * @static\n * @type {Boolean}\n *\/\n let IS_IPOD = false;\n\n \/**\n * The detected iOS version - or `null`.\n *\n * @static\n * @type {string|null}\n *\/\n let IOS_VERSION = null;\n\n \/**\n * Whether or not this is an Android device.\n *\n * @static\n * @type {Boolean}\n *\/\n let IS_ANDROID = false;\n\n \/**\n * The detected Android version - or `null` if not Android or indeterminable.\n *\n * @static\n * @type {number|string|null}\n *\/\n let ANDROID_VERSION;\n\n \/**\n * Whether or not this is Mozilla Firefox.\n *\n * @static\n * @type {Boolean}\n *\/\n let IS_FIREFOX = false;\n\n \/**\n * Whether or not this is Microsoft Edge.\n *\n * @static\n * @type {Boolean}\n *\/\n let IS_EDGE = false;\n\n \/**\n * Whether or not this is any Chromium Browser\n *\n * @static\n * @type {Boolean}\n *\/\n let IS_CHROMIUM = false;\n\n \/**\n * Whether or not this is any Chromium browser that is not Edge.\n *\n * This will also be `true` for Chrome on iOS, which will have different support\n * as it is actually Safari under the hood.\n *\n * Deprecated, as the behaviour to not match Edge was to prevent Legacy Edge's UA matching.\n * IS_CHROMIUM should be used instead.\n * \"Chromium but not Edge\" could be explicitly tested with IS_CHROMIUM && !IS_EDGE\n *\n * @static\n * @deprecated\n * @type {Boolean}\n *\/\n let IS_CHROME = false;\n\n \/**\n * The detected Chromium version - or `null`.\n *\n * @static\n * @type {number|null}\n *\/\n let CHROMIUM_VERSION = null;\n\n \/**\n * The detected Google Chrome version - or `null`.\n * This has always been the _Chromium_ version, i.e. would return on Chromium Edge.\n * Deprecated, use CHROMIUM_VERSION instead.\n *\n * @static\n * @deprecated\n * @type {number|null}\n *\/\n let CHROME_VERSION = null;\n\n \/**\n * Whether or not this is a Chromecast receiver application.\n *\n * @static\n * @type {Boolean}\n *\/\n const IS_CHROMECAST_RECEIVER = Boolean(window.cast && window.cast.framework && window.cast.framework.CastReceiverContext);\n\n \/**\n * The detected Internet Explorer version - or `null`.\n *\n * @static\n * @deprecated\n * @type {number|null}\n *\/\n let IE_VERSION = null;\n\n \/**\n * Whether or not this is desktop Safari.\n *\n * @static\n * @type {Boolean}\n *\/\n let IS_SAFARI = false;\n\n \/**\n * Whether or not this is a Windows machine.\n *\n * @static\n * @type {Boolean}\n *\/\n let IS_WINDOWS = false;\n\n \/**\n * Whether or not this device is an iPad.\n *\n * @static\n * @type {Boolean}\n *\/\n let IS_IPAD = false;\n\n \/**\n * Whether or not this device is an iPhone.\n *\n * @static\n * @type {Boolean}\n *\/\n \/\/ The Facebook app's UIWebView identifies as both an iPhone and iPad, so\n \/\/ to identify iPhones, we need to exclude iPads.\n \/\/ http:\/\/artsy.github.io\/blog\/2012\/10\/18\/the-perils-of-ios-user-agent-sniffing\/\n let IS_IPHONE = false;\n\n \/**\n * Whether or not this is a Tizen device.\n *\n * @static\n * @type {Boolean}\n *\/\n let IS_TIZEN = false;\n\n \/**\n * Whether or not this is a WebOS device.\n *\n * @static\n * @type {Boolean}\n *\/\n let IS_WEBOS = false;\n\n \/**\n * Whether or not this is a Smart TV (Tizen or WebOS) device.\n *\n * @static\n * @type {Boolean}\n *\/\n let IS_SMART_TV = false;\n\n \/**\n * Whether or not this device is touch-enabled.\n *\n * @static\n * @const\n * @type {Boolean}\n *\/\n const TOUCH_ENABLED = Boolean(isReal() && ('ontouchstart' in window || window.navigator.maxTouchPoints || window.DocumentTouch && window.document instanceof window.DocumentTouch));\n const UAD = window.navigator && window.navigator.userAgentData;\n if (UAD && UAD.platform && UAD.brands) {\n \/\/ If userAgentData is present, use it instead of userAgent to avoid warnings\n \/\/ Currently only implemented on Chromium\n \/\/ userAgentData does not expose Android version, so ANDROID_VERSION remains `null`\n\n IS_ANDROID = UAD.platform === 'Android';\n IS_EDGE = Boolean(UAD.brands.find(b => b.brand === 'Microsoft Edge'));\n IS_CHROMIUM = Boolean(UAD.brands.find(b => b.brand === 'Chromium'));\n IS_CHROME = !IS_EDGE && IS_CHROMIUM;\n CHROMIUM_VERSION = CHROME_VERSION = (UAD.brands.find(b => b.brand === 'Chromium') || {}).version || null;\n IS_WINDOWS = UAD.platform === 'Windows';\n }\n\n \/\/ If the browser is not Chromium, either userAgentData is not present which could be an old Chromium browser,\n \/\/ or it's a browser that has added userAgentData since that we don't have tests for yet. In either case,\n \/\/ the checks need to be made agiainst the regular userAgent string.\n if (!IS_CHROMIUM) {\n const USER_AGENT = window.navigator && window.navigator.userAgent || '';\n IS_IPOD = \/iPod\/i.test(USER_AGENT);\n IOS_VERSION = function () {\n const match = USER_AGENT.match(\/OS (\\d+)_\/i);\n if (match && match[1]) {\n return match[1];\n }\n return null;\n }();\n IS_ANDROID = \/Android\/i.test(USER_AGENT);\n ANDROID_VERSION = function () {\n \/\/ This matches Android Major.Minor.Patch versions\n \/\/ ANDROID_VERSION is Major.Minor as a Number, if Minor isn't available, then only Major is returned\n const match = USER_AGENT.match(\/Android (\\d+)(?:\\.(\\d+))?(?:\\.(\\d+))*\/i);\n if (!match) {\n return null;\n }\n const major = match[1] && parseFloat(match[1]);\n const minor = match[2] && parseFloat(match[2]);\n if (major && minor) {\n return parseFloat(match[1] + '.' + match[2]);\n } else if (major) {\n return major;\n }\n return null;\n }();\n IS_FIREFOX = \/Firefox\/i.test(USER_AGENT);\n IS_EDGE = \/Edg\/i.test(USER_AGENT);\n IS_CHROMIUM = \/Chrome\/i.test(USER_AGENT) || \/CriOS\/i.test(USER_AGENT);\n IS_CHROME = !IS_EDGE && IS_CHROMIUM;\n CHROMIUM_VERSION = CHROME_VERSION = function () {\n const match = USER_AGENT.match(\/(Chrome|CriOS)\\\/(\\d+)\/);\n if (match && match[2]) {\n return parseFloat(match[2]);\n }\n return null;\n }();\n IE_VERSION = function () {\n const result = \/MSIE\\s(\\d+)\\.\\d\/.exec(USER_AGENT);\n let version = result && parseFloat(result[1]);\n if (!version && \/Trident\\\/7.0\/i.test(USER_AGENT) && \/rv:11.0\/.test(USER_AGENT)) {\n \/\/ IE 11 has a different user agent string than other IE versions\n version = 11.0;\n }\n return version;\n }();\n IS_TIZEN = \/Tizen\/i.test(USER_AGENT);\n IS_WEBOS = \/Web0S\/i.test(USER_AGENT);\n IS_SMART_TV = IS_TIZEN || IS_WEBOS;\n IS_SAFARI = \/Safari\/i.test(USER_AGENT) && !IS_CHROME && !IS_ANDROID && !IS_EDGE && !IS_SMART_TV;\n IS_WINDOWS = \/Windows\/i.test(USER_AGENT);\n IS_IPAD = \/iPad\/i.test(USER_AGENT) || IS_SAFARI && TOUCH_ENABLED && !\/iPhone\/i.test(USER_AGENT);\n IS_IPHONE = \/iPhone\/i.test(USER_AGENT) && !IS_IPAD;\n }\n\n \/**\n * Whether or not this is an iOS device.\n *\n * @static\n * @const\n * @type {Boolean}\n *\/\n const IS_IOS = IS_IPHONE || IS_IPAD || IS_IPOD;\n\n \/**\n * Whether or not this is any flavor of Safari - including iOS.\n *\n * @static\n * @const\n * @type {Boolean}\n *\/\n const IS_ANY_SAFARI = (IS_SAFARI || IS_IOS) && !IS_CHROME;\n\n var browser = \/*#__PURE__*\/Object.freeze({\n __proto__: null,\n get IS_IPOD () { return IS_IPOD; },\n get IOS_VERSION () { return IOS_VERSION; },\n get IS_ANDROID () { return IS_ANDROID; },\n get ANDROID_VERSION () { return ANDROID_VERSION; },\n get IS_FIREFOX () { return IS_FIREFOX; },\n get IS_EDGE () { return IS_EDGE; },\n get IS_CHROMIUM () { return IS_CHROMIUM; },\n get IS_CHROME () { return IS_CHROME; },\n get CHROMIUM_VERSION () { return CHROMIUM_VERSION; },\n get CHROME_VERSION () { return CHROME_VERSION; },\n IS_CHROMECAST_RECEIVER: IS_CHROMECAST_RECEIVER,\n get IE_VERSION () { return IE_VERSION; },\n get IS_SAFARI () { return IS_SAFARI; },\n get IS_WINDOWS () { return IS_WINDOWS; },\n get IS_IPAD () { return IS_IPAD; },\n get IS_IPHONE () { return IS_IPHONE; },\n get IS_TIZEN () { return IS_TIZEN; },\n get IS_WEBOS () { return IS_WEBOS; },\n get IS_SMART_TV () { return IS_SMART_TV; },\n TOUCH_ENABLED: TOUCH_ENABLED,\n IS_IOS: IS_IOS,\n IS_ANY_SAFARI: IS_ANY_SAFARI\n });\n\n \/**\n * @file dom.js\n * @module dom\n *\/\n\n \/**\n * Detect if a value is a string with any non-whitespace characters.\n *\n * @private\n * @param {string} str\n * The string to check\n *\n * @return {boolean}\n * Will be `true` if the string is non-blank, `false` otherwise.\n *\n *\/\n function isNonBlankString(str) {\n \/\/ we use str.trim as it will trim any whitespace characters\n \/\/ from the front or back of non-whitespace characters. aka\n \/\/ Any string that contains non-whitespace characters will\n \/\/ still contain them after `trim` but whitespace only strings\n \/\/ will have a length of 0, failing this check.\n return typeof str === 'string' && Boolean(str.trim());\n }\n\n \/**\n * Throws an error if the passed string has whitespace. This is used by\n * class methods to be relatively consistent with the classList API.\n *\n * @private\n * @param {string} str\n * The string to check for whitespace.\n *\n * @throws {Error}\n * Throws an error if there is whitespace in the string.\n *\/\n function throwIfWhitespace(str) {\n \/\/ str.indexOf instead of regex because str.indexOf is faster performance wise.\n if (str.indexOf(' ') >= 0) {\n throw new Error('class has illegal whitespace characters');\n }\n }\n\n \/**\n * Whether the current DOM interface appears to be real (i.e. not simulated).\n *\n * @return {boolean}\n * Will be `true` if the DOM appears to be real, `false` otherwise.\n *\/\n function isReal() {\n \/\/ Both document and window will never be undefined thanks to `global`.\n return document === window.document;\n }\n\n \/**\n * Determines, via duck typing, whether or not a value is a DOM element.\n *\n * @param {*} value\n * The value to check.\n *\n * @return {boolean}\n * Will be `true` if the value is a DOM element, `false` otherwise.\n *\/\n function isEl(value) {\n return isObject$1(value) && value.nodeType === 1;\n }\n\n \/**\n * Determines if the current DOM is embedded in an iframe.\n *\n * @return {boolean}\n * Will be `true` if the DOM is embedded in an iframe, `false`\n * otherwise.\n *\/\n function isInFrame() {\n \/\/ We need a try\/catch here because Safari will throw errors when attempting\n \/\/ to get either `parent` or `self`\n try {\n return window.parent !== window.self;\n } catch (x) {\n return true;\n }\n }\n\n \/**\n * Creates functions to query the DOM using a given method.\n *\n * @private\n * @param {string} method\n * The method to create the query with.\n *\n * @return {Function}\n * The query method\n *\/\n function createQuerier(method) {\n return function (selector, context) {\n if (!isNonBlankString(selector)) {\n return document[method](null);\n }\n if (isNonBlankString(context)) {\n context = document.querySelector(context);\n }\n const ctx = isEl(context) ? context : document;\n return ctx[method] && ctx[method](selector);\n };\n }\n\n \/**\n * Creates an element and applies properties, attributes, and inserts content.\n *\n * @param {string} [tagName='div']\n * Name of tag to be created.\n *\n * @param {Object} [properties={}]\n * Element properties to be applied.\n *\n * @param {Object} [attributes={}]\n * Element attributes to be applied.\n *\n * @param {ContentDescriptor} [content]\n * A content descriptor object.\n *\n * @return {Element}\n * The element that was created.\n *\/\n function createEl(tagName = 'div', properties = {}, attributes = {}, content) {\n const el = document.createElement(tagName);\n Object.getOwnPropertyNames(properties).forEach(function (propName) {\n const val = properties[propName];\n\n \/\/ Handle textContent since it's not supported everywhere and we have a\n \/\/ method for it.\n if (propName === 'textContent') {\n textContent(el, val);\n } else if (el[propName] !== val || propName === 'tabIndex') {\n el[propName] = val;\n }\n });\n Object.getOwnPropertyNames(attributes).forEach(function (attrName) {\n el.setAttribute(attrName, attributes[attrName]);\n });\n if (content) {\n appendContent(el, content);\n }\n return el;\n }\n\n \/**\n * Injects text into an element, replacing any existing contents entirely.\n *\n * @param {HTMLElement} el\n * The element to add text content into\n *\n * @param {string} text\n * The text content to add.\n *\n * @return {Element}\n * The element with added text content.\n *\/\n function textContent(el, text) {\n if (typeof el.textContent === 'undefined') {\n el.innerText = text;\n } else {\n el.textContent = text;\n }\n return el;\n }\n\n \/**\n * Insert an element as the first child node of another\n *\n * @param {Element} child\n * Element to insert\n *\n * @param {Element} parent\n * Element to insert child into\n *\/\n function prependTo(child, parent) {\n if (parent.firstChild) {\n parent.insertBefore(child, parent.firstChild);\n } else {\n parent.appendChild(child);\n }\n }\n\n \/**\n * Check if an element has a class name.\n *\n * @param {Element} element\n * Element to check\n *\n * @param {string} classToCheck\n * Class name to check for\n *\n * @return {boolean}\n * Will be `true` if the element has a class, `false` otherwise.\n *\n * @throws {Error}\n * Throws an error if `classToCheck` has white space.\n *\/\n function hasClass(element, classToCheck) {\n throwIfWhitespace(classToCheck);\n return element.classList.contains(classToCheck);\n }\n\n \/**\n * Add a class name to an element.\n *\n * @param {Element} element\n * Element to add class name to.\n *\n * @param {...string} classesToAdd\n * One or more class name to add.\n *\n * @return {Element}\n * The DOM element with the added class name.\n *\/\n function addClass(element, ...classesToAdd) {\n element.classList.add(...classesToAdd.reduce((prev, current) => prev.concat(current.split(\/\\s+\/)), []));\n return element;\n }\n\n \/**\n * Remove a class name from an element.\n *\n * @param {Element} element\n * Element to remove a class name from.\n *\n * @param {...string} classesToRemove\n * One or more class name to remove.\n *\n * @return {Element}\n * The DOM element with class name removed.\n *\/\n function removeClass(element, ...classesToRemove) {\n \/\/ Protect in case the player gets disposed\n if (!element) {\n log$1.warn(\"removeClass was called with an element that doesn't exist\");\n return null;\n }\n element.classList.remove(...classesToRemove.reduce((prev, current) => prev.concat(current.split(\/\\s+\/)), []));\n return element;\n }\n\n \/**\n * The callback definition for toggleClass.\n *\n * @callback module:dom~PredicateCallback\n * @param {Element} element\n * The DOM element of the Component.\n *\n * @param {string} classToToggle\n * The `className` that wants to be toggled\n *\n * @return {boolean|undefined}\n * If `true` is returned, the `classToToggle` will be added to the\n * `element`. If `false`, the `classToToggle` will be removed from\n * the `element`. If `undefined`, the callback will be ignored.\n *\/\n\n \/**\n * Adds or removes a class name to\/from an element depending on an optional\n * condition or the presence\/absence of the class name.\n *\n * @param {Element} element\n * The element to toggle a class name on.\n *\n * @param {string} classToToggle\n * The class that should be toggled.\n *\n * @param {boolean|module:dom~PredicateCallback} [predicate]\n * See the return value for {@link module:dom~PredicateCallback}\n *\n * @return {Element}\n * The element with a class that has been toggled.\n *\/\n function toggleClass(element, classToToggle, predicate) {\n if (typeof predicate === 'function') {\n predicate = predicate(element, classToToggle);\n }\n if (typeof predicate !== 'boolean') {\n predicate = undefined;\n }\n classToToggle.split(\/\\s+\/).forEach(className => element.classList.toggle(className, predicate));\n return element;\n }\n\n \/**\n * Apply attributes to an HTML element.\n *\n * @param {Element} el\n * Element to add attributes to.\n *\n * @param {Object} [attributes]\n * Attributes to be applied.\n *\/\n function setAttributes(el, attributes) {\n Object.getOwnPropertyNames(attributes).forEach(function (attrName) {\n const attrValue = attributes[attrName];\n if (attrValue === null || typeof attrValue === 'undefined' || attrValue === false) {\n el.removeAttribute(attrName);\n } else {\n el.setAttribute(attrName, attrValue === true ? '' : attrValue);\n }\n });\n }\n\n \/**\n * Get an element's attribute values, as defined on the HTML tag.\n *\n * Attributes are not the same as properties. They're defined on the tag\n * or with setAttribute.\n *\n * @param {Element} tag\n * Element from which to get tag attributes.\n *\n * @return {Object}\n * All attributes of the element. Boolean attributes will be `true` or\n * `false`, others will be strings.\n *\/\n function getAttributes(tag) {\n const obj = {};\n\n \/\/ known boolean attributes\n \/\/ we can check for matching boolean properties, but not all browsers\n \/\/ and not all tags know about these attributes, so, we still want to check them manually\n const knownBooleans = ['autoplay', 'controls', 'playsinline', 'loop', 'muted', 'default', 'defaultMuted'];\n if (tag && tag.attributes && tag.attributes.length > 0) {\n const attrs = tag.attributes;\n for (let i = attrs.length - 1; i >= 0; i--) {\n const attrName = attrs[i].name;\n \/** @type {boolean|string} *\/\n let attrVal = attrs[i].value;\n\n \/\/ check for known booleans\n \/\/ the matching element property will return a value for typeof\n if (knownBooleans.includes(attrName)) {\n \/\/ the value of an included boolean attribute is typically an empty\n \/\/ string ('') which would equal false if we just check for a false value.\n \/\/ we also don't want support bad code like autoplay='false'\n attrVal = attrVal !== null ? true : false;\n }\n obj[attrName] = attrVal;\n }\n }\n return obj;\n }\n\n \/**\n * Get the value of an element's attribute.\n *\n * @param {Element} el\n * A DOM element.\n *\n * @param {string} attribute\n * Attribute to get the value of.\n *\n * @return {string}\n * The value of the attribute.\n *\/\n function getAttribute(el, attribute) {\n return el.getAttribute(attribute);\n }\n\n \/**\n * Set the value of an element's attribute.\n *\n * @param {Element} el\n * A DOM element.\n *\n * @param {string} attribute\n * Attribute to set.\n *\n * @param {string} value\n * Value to set the attribute to.\n *\/\n function setAttribute(el, attribute, value) {\n el.setAttribute(attribute, value);\n }\n\n \/**\n * Remove an element's attribute.\n *\n * @param {Element} el\n * A DOM element.\n *\n * @param {string} attribute\n * Attribute to remove.\n *\/\n function removeAttribute(el, attribute) {\n el.removeAttribute(attribute);\n }\n\n \/**\n * Attempt to block the ability to select text.\n *\/\n function blockTextSelection() {\n document.body.focus();\n document.onselectstart = function () {\n return false;\n };\n }\n\n \/**\n * Turn off text selection blocking.\n *\/\n function unblockTextSelection() {\n document.onselectstart = function () {\n return true;\n };\n }\n\n \/**\n * Identical to the native `getBoundingClientRect` function, but ensures that\n * the method is supported at all (it is in all browsers we claim to support)\n * and that the element is in the DOM before continuing.\n *\n * This wrapper function also shims properties which are not provided by some\n * older browsers (namely, IE8).\n *\n * Additionally, some browsers do not support adding properties to a\n * `ClientRect`\/`DOMRect` object; so, we shallow-copy it with the standard\n * properties (except `x` and `y` which are not widely supported). This helps\n * avoid implementations where keys are non-enumerable.\n *\n * @param {Element} el\n * Element whose `ClientRect` we want to calculate.\n *\n * @return {Object|undefined}\n * Always returns a plain object - or `undefined` if it cannot.\n *\/\n function getBoundingClientRect(el) {\n if (el && el.getBoundingClientRect && el.parentNode) {\n const rect = el.getBoundingClientRect();\n const result = {};\n ['bottom', 'height', 'left', 'right', 'top', 'width'].forEach(k => {\n if (rect[k] !== undefined) {\n result[k] = rect[k];\n }\n });\n if (!result.height) {\n result.height = parseFloat(computedStyle(el, 'height'));\n }\n if (!result.width) {\n result.width = parseFloat(computedStyle(el, 'width'));\n }\n return result;\n }\n }\n\n \/**\n * Represents the position of a DOM element on the page.\n *\n * @typedef {Object} module:dom~Position\n *\n * @property {number} left\n * Pixels to the left.\n *\n * @property {number} top\n * Pixels from the top.\n *\/\n\n \/**\n * Get the position of an element in the DOM.\n *\n * Uses `getBoundingClientRect` technique from John Resig.\n *\n * @see http:\/\/ejohn.org\/blog\/getboundingclientrect-is-awesome\/\n *\n * @param {Element} el\n * Element from which to get offset.\n *\n * @return {module:dom~Position}\n * The position of the element that was passed in.\n *\/\n function findPosition(el) {\n if (!el || el && !el.offsetParent) {\n return {\n left: 0,\n top: 0,\n width: 0,\n height: 0\n };\n }\n const width = el.offsetWidth;\n const height = el.offsetHeight;\n let left = 0;\n let top = 0;\n while (el.offsetParent && el !== document[FullscreenApi.fullscreenElement]) {\n left += el.offsetLeft;\n top += el.offsetTop;\n el = el.offsetParent;\n }\n return {\n left,\n top,\n width,\n height\n };\n }\n\n \/**\n * Represents x and y coordinates for a DOM element or mouse pointer.\n *\n * @typedef {Object} module:dom~Coordinates\n *\n * @property {number} x\n * x coordinate in pixels\n *\n * @property {number} y\n * y coordinate in pixels\n *\/\n\n \/**\n * Get the pointer position within an element.\n *\n * The base on the coordinates are the bottom left of the element.\n *\n * @param {Element} el\n * Element on which to get the pointer position on.\n *\n * @param {Event} event\n * Event object.\n *\n * @return {module:dom~Coordinates}\n * A coordinates object corresponding to the mouse position.\n *\n *\/\n function getPointerPosition(el, event) {\n const translated = {\n x: 0,\n y: 0\n };\n if (IS_IOS) {\n let item = el;\n while (item && item.nodeName.toLowerCase() !== 'html') {\n const transform = computedStyle(item, 'transform');\n if (\/^matrix\/.test(transform)) {\n const values = transform.slice(7, -1).split(\/,\\s\/).map(Number);\n translated.x += values[4];\n translated.y += values[5];\n } else if (\/^matrix3d\/.test(transform)) {\n const values = transform.slice(9, -1).split(\/,\\s\/).map(Number);\n translated.x += values[12];\n translated.y += values[13];\n }\n if (item.assignedSlot && item.assignedSlot.parentElement && window.WebKitCSSMatrix) {\n const transformValue = window.getComputedStyle(item.assignedSlot.parentElement).transform;\n const matrix = new window.WebKitCSSMatrix(transformValue);\n translated.x += matrix.m41;\n translated.y += matrix.m42;\n }\n item = item.parentNode || item.host;\n }\n }\n const position = {};\n const boxTarget = findPosition(event.target);\n const box = findPosition(el);\n const boxW = box.width;\n const boxH = box.height;\n let offsetY = event.offsetY - (box.top - boxTarget.top);\n let offsetX = event.offsetX - (box.left - boxTarget.left);\n if (event.changedTouches) {\n offsetX = event.changedTouches[0].pageX - box.left;\n offsetY = event.changedTouches[0].pageY + box.top;\n if (IS_IOS) {\n offsetX -= translated.x;\n offsetY -= translated.y;\n }\n }\n position.y = 1 - Math.max(0, Math.min(1, offsetY \/ boxH));\n position.x = Math.max(0, Math.min(1, offsetX \/ boxW));\n return position;\n }\n\n \/**\n * Determines, via duck typing, whether or not a value is a text node.\n *\n * @param {*} value\n * Check if this value is a text node.\n *\n * @return {boolean}\n * Will be `true` if the value is a text node, `false` otherwise.\n *\/\n function isTextNode$1(value) {\n return isObject$1(value) && value.nodeType === 3;\n }\n\n \/**\n * Empties the contents of an element.\n *\n * @param {Element} el\n * The element to empty children from\n *\n * @return {Element}\n * The element with no children\n *\/\n function emptyEl(el) {\n while (el.firstChild) {\n el.removeChild(el.firstChild);\n }\n return el;\n }\n\n \/**\n * This is a mixed value that describes content to be injected into the DOM\n * via some method. It can be of the following types:\n *\n * Type | Description\n * -----------|-------------\n * `string` | The value will be normalized into a text node.\n * `Element` | The value will be accepted as-is.\n * `Text` | A TextNode. The value will be accepted as-is.\n * `Array` | A one-dimensional array of strings, elements, text nodes, or functions. These functions should return a string, element, or text node (any other return value, like an array, will be ignored).\n * `Function` | A function, which is expected to return a string, element, text node, or array - any of the other possible values described above. This means that a content descriptor could be a function that returns an array of functions, but those second-level functions must return strings, elements, or text nodes.\n *\n * @typedef {string|Element|Text|Array|Function} ContentDescriptor\n *\/\n\n \/**\n * Normalizes content for eventual insertion into the DOM.\n *\n * This allows a wide range of content definition methods, but helps protect\n * from falling into the trap of simply writing to `innerHTML`, which could\n * be an XSS concern.\n *\n * The content for an element can be passed in multiple types and\n * combinations, whose behavior is as follows:\n *\n * @param {ContentDescriptor} content\n * A content descriptor value.\n *\n * @return {Array}\n * All of the content that was passed in, normalized to an array of\n * elements or text nodes.\n *\/\n function normalizeContent(content) {\n \/\/ First, invoke content if it is a function. If it produces an array,\n \/\/ that needs to happen before normalization.\n if (typeof content === 'function') {\n content = content();\n }\n\n \/\/ Next up, normalize to an array, so one or many items can be normalized,\n \/\/ filtered, and returned.\n return (Array.isArray(content) ? content : [content]).map(value => {\n \/\/ First, invoke value if it is a function to produce a new value,\n \/\/ which will be subsequently normalized to a Node of some kind.\n if (typeof value === 'function') {\n value = value();\n }\n if (isEl(value) || isTextNode$1(value)) {\n return value;\n }\n if (typeof value === 'string' && \/\\S\/.test(value)) {\n return document.createTextNode(value);\n }\n }).filter(value => value);\n }\n\n \/**\n * Normalizes and appends content to an element.\n *\n * @param {Element} el\n * Element to append normalized content to.\n *\n * @param {ContentDescriptor} content\n * A content descriptor value.\n *\n * @return {Element}\n * The element with appended normalized content.\n *\/\n function appendContent(el, content) {\n normalizeContent(content).forEach(node => el.appendChild(node));\n return el;\n }\n\n \/**\n * Normalizes and inserts content into an element; this is identical to\n * `appendContent()`, except it empties the element first.\n *\n * @param {Element} el\n * Element to insert normalized content into.\n *\n * @param {ContentDescriptor} content\n * A content descriptor value.\n *\n * @return {Element}\n * The element with inserted normalized content.\n *\/\n function insertContent(el, content) {\n return appendContent(emptyEl(el), content);\n }\n\n \/**\n * Check if an event was a single left click.\n *\n * @param {MouseEvent} event\n * Event object.\n *\n * @return {boolean}\n * Will be `true` if a single left click, `false` otherwise.\n *\/\n function isSingleLeftClick(event) {\n \/\/ Note: if you create something draggable, be sure to\n \/\/ call it on both `mousedown` and `mousemove` event,\n \/\/ otherwise `mousedown` should be enough for a button\n\n if (event.button === undefined && event.buttons === undefined) {\n \/\/ Why do we need `buttons` ?\n \/\/ Because, middle mouse sometimes have this:\n \/\/ e.button === 0 and e.buttons === 4\n \/\/ Furthermore, we want to prevent combination click, something like\n \/\/ HOLD middlemouse then left click, that would be\n \/\/ e.button === 0, e.buttons === 5\n \/\/ just `button` is not gonna work\n\n \/\/ Alright, then what this block does ?\n \/\/ this is for chrome `simulate mobile devices`\n \/\/ I want to support this as well\n\n return true;\n }\n if (event.button === 0 && event.buttons === undefined) {\n \/\/ Touch screen, sometimes on some specific device, `buttons`\n \/\/ doesn't have anything (safari on ios, blackberry...)\n\n return true;\n }\n\n \/\/ `mouseup` event on a single left click has\n \/\/ `button` and `buttons` equal to 0\n if (event.type === 'mouseup' && event.button === 0 && event.buttons === 0) {\n return true;\n }\n\n \/\/ MacOS Sonoma trackpad when \"tap to click enabled\"\n if (event.type === 'mousedown' && event.button === 0 && event.buttons === 0) {\n return true;\n }\n if (event.button !== 0 || event.buttons !== 1) {\n \/\/ This is the reason we have those if else block above\n \/\/ if any special case we can catch and let it slide\n \/\/ we do it above, when get to here, this definitely\n \/\/ is-not-left-click\n\n return false;\n }\n return true;\n }\n\n \/**\n * Finds a single DOM element matching `selector` within the optional\n * `context` of another DOM element (defaulting to `document`).\n *\n * @param {string} selector\n * A valid CSS selector, which will be passed to `querySelector`.\n *\n * @param {Element|String} [context=document]\n * A DOM element within which to query. Can also be a selector\n * string in which case the first matching element will be used\n * as context. If missing (or no element matches selector), falls\n * back to `document`.\n *\n * @return {Element|null}\n * The element that was found or null.\n *\/\n const $ = createQuerier('querySelector');\n\n \/**\n * Finds a all DOM elements matching `selector` within the optional\n * `context` of another DOM element (defaulting to `document`).\n *\n * @param {string} selector\n * A valid CSS selector, which will be passed to `querySelectorAll`.\n *\n * @param {Element|String} [context=document]\n * A DOM element within which to query. Can also be a selector\n * string in which case the first matching element will be used\n * as context. If missing (or no element matches selector), falls\n * back to `document`.\n *\n * @return {NodeList}\n * A element list of elements that were found. Will be empty if none\n * were found.\n *\n *\/\n const $$ = createQuerier('querySelectorAll');\n\n \/**\n * A safe getComputedStyle.\n *\n * This is needed because in Firefox, if the player is loaded in an iframe with\n * `display:none`, then `getComputedStyle` returns `null`, so, we do a\n * null-check to make sure that the player doesn't break in these cases.\n *\n * @param {Element} el\n * The element you want the computed style of\n *\n * @param {string} prop\n * The property name you want\n *\n * @see https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=548397\n *\/\n function computedStyle(el, prop) {\n if (!el || !prop) {\n return '';\n }\n if (typeof window.getComputedStyle === 'function') {\n let computedStyleValue;\n try {\n computedStyleValue = window.getComputedStyle(el);\n } catch (e) {\n return '';\n }\n return computedStyleValue ? computedStyleValue.getPropertyValue(prop) || computedStyleValue[prop] : '';\n }\n return '';\n }\n\n \/**\n * Copy document style sheets to another window.\n *\n * @param {Window} win\n * The window element you want to copy the document style sheets to.\n *\n *\/\n function copyStyleSheetsToWindow(win) {\n [...document.styleSheets].forEach(styleSheet => {\n try {\n const cssRules = [...styleSheet.cssRules].map(rule => rule.cssText).join('');\n const style = document.createElement('style');\n style.textContent = cssRules;\n win.document.head.appendChild(style);\n } catch (e) {\n const link = document.createElement('link');\n link.rel = 'stylesheet';\n link.type = styleSheet.type;\n \/\/ For older Safari this has to be the string; on other browsers setting the MediaList works\n link.media = styleSheet.media.mediaText;\n link.href = styleSheet.href;\n win.document.head.appendChild(link);\n }\n });\n }\n\n var Dom = \/*#__PURE__*\/Object.freeze({\n __proto__: null,\n isReal: isReal,\n isEl: isEl,\n isInFrame: isInFrame,\n createEl: createEl,\n textContent: textContent,\n prependTo: prependTo,\n hasClass: hasClass,\n addClass: addClass,\n removeClass: removeClass,\n toggleClass: toggleClass,\n setAttributes: setAttributes,\n getAttributes: getAttributes,\n getAttribute: getAttribute,\n setAttribute: setAttribute,\n removeAttribute: removeAttribute,\n blockTextSelection: blockTextSelection,\n unblockTextSelection: unblockTextSelection,\n getBoundingClientRect: getBoundingClientRect,\n findPosition: findPosition,\n getPointerPosition: getPointerPosition,\n isTextNode: isTextNode$1,\n emptyEl: emptyEl,\n normalizeContent: normalizeContent,\n appendContent: appendContent,\n insertContent: insertContent,\n isSingleLeftClick: isSingleLeftClick,\n $: $,\n $$: $$,\n computedStyle: computedStyle,\n copyStyleSheetsToWindow: copyStyleSheetsToWindow\n });\n\n \/**\n * @file setup.js - Functions for setting up a player without\n * user interaction based on the data-setup `attribute` of the video tag.\n *\n * @module setup\n *\/\n let _windowLoaded = false;\n let videojs$1;\n\n \/**\n * Set up any tags that have a data-setup `attribute` when the player is started.\n *\/\n const autoSetup = function () {\n if (videojs$1.options.autoSetup === false) {\n return;\n }\n const vids = Array.prototype.slice.call(document.getElementsByTagName('video'));\n const audios = Array.prototype.slice.call(document.getElementsByTagName('audio'));\n const divs = Array.prototype.slice.call(document.getElementsByTagName('video-js'));\n const mediaEls = vids.concat(audios, divs);\n\n \/\/ Check if any media elements exist\n if (mediaEls && mediaEls.length > 0) {\n for (let i = 0, e = mediaEls.length; i < e; i++) {\n const mediaEl = mediaEls[i];\n\n \/\/ Check if element exists, has getAttribute func.\n if (mediaEl && mediaEl.getAttribute) {\n \/\/ Make sure this player hasn't already been set up.\n if (mediaEl.player === undefined) {\n const options = mediaEl.getAttribute('data-setup');\n\n \/\/ Check if data-setup attr exists.\n \/\/ We only auto-setup if they've added the data-setup attr.\n if (options !== null) {\n \/\/ Create new video.js instance.\n videojs$1(mediaEl);\n }\n }\n\n \/\/ If getAttribute isn't defined, we need to wait for the DOM.\n } else {\n autoSetupTimeout(1);\n break;\n }\n }\n\n \/\/ No videos were found, so keep looping unless page is finished loading.\n } else if (!_windowLoaded) {\n autoSetupTimeout(1);\n }\n };\n\n \/**\n * Wait until the page is loaded before running autoSetup. This will be called in\n * autoSetup if `hasLoaded` returns false.\n *\n * @param {number} wait\n * How long to wait in ms\n *\n * @param {module:videojs} [vjs]\n * The videojs library function\n *\/\n function autoSetupTimeout(wait, vjs) {\n \/\/ Protect against breakage in non-browser environments\n if (!isReal()) {\n return;\n }\n if (vjs) {\n videojs$1 = vjs;\n }\n window.setTimeout(autoSetup, wait);\n }\n\n \/**\n * Used to set the internal tracking of window loaded state to true.\n *\n * @private\n *\/\n function setWindowLoaded() {\n _windowLoaded = true;\n window.removeEventListener('load', setWindowLoaded);\n }\n if (isReal()) {\n if (document.readyState === 'complete') {\n setWindowLoaded();\n } else {\n \/**\n * Listen for the load event on window, and set _windowLoaded to true.\n *\n * We use a standard event listener here to avoid incrementing the GUID\n * before any players are created.\n *\n * @listens load\n *\/\n window.addEventListener('load', setWindowLoaded);\n }\n }\n\n \/**\n * @file stylesheet.js\n * @module stylesheet\n *\/\n\n \/**\n * Create a DOM style element given a className for it.\n *\n * @param {string} className\n * The className to add to the created style element.\n *\n * @return {Element}\n * The element that was created.\n *\/\n const createStyleElement = function (className) {\n const style = document.createElement('style');\n style.className = className;\n return style;\n };\n\n \/**\n * Add text to a DOM element.\n *\n * @param {Element} el\n * The Element to add text content to.\n *\n * @param {string} content\n * The text to add to the element.\n *\/\n const setTextContent = function (el, content) {\n if (el.styleSheet) {\n el.styleSheet.cssText = content;\n } else {\n el.textContent = content;\n }\n };\n\n \/**\n * @file dom-data.js\n * @module dom-data\n *\/\n\n \/**\n * Element Data Store.\n *\n * Allows for binding data to an element without putting it directly on the\n * element. Ex. Event listeners are stored here.\n * (also from jsninja.com, slightly modified and updated for closure compiler)\n *\n * @type {Object}\n * @private\n *\/\n var DomData = new WeakMap();\n\n \/**\n * @file guid.js\n * @module guid\n *\/\n\n \/\/ Default value for GUIDs. This allows us to reset the GUID counter in tests.\n \/\/\n \/\/ The initial GUID is 3 because some users have come to rely on the first\n \/\/ default player ID ending up as `vjs_video_3`.\n \/\/\n \/\/ See: https:\/\/github.com\/videojs\/video.js\/pull\/6216\n const _initialGuid = 3;\n\n \/**\n * Unique ID for an element or function\n *\n * @type {Number}\n *\/\n let _guid = _initialGuid;\n\n \/**\n * Get a unique auto-incrementing ID by number that has not been returned before.\n *\n * @return {number}\n * A new unique ID.\n *\/\n function newGUID() {\n return _guid++;\n }\n\n \/**\n * @file events.js. An Event System (John Resig - Secrets of a JS Ninja http:\/\/jsninja.com\/)\n * (Original book version wasn't completely usable, so fixed some things and made Closure Compiler compatible)\n * This should work very similarly to jQuery's events, however it's based off the book version which isn't as\n * robust as jquery's, so there's probably some differences.\n *\n * @file events.js\n * @module events\n *\/\n\n \/**\n * Clean up the listener cache and dispatchers\n *\n * @param {Element|Object} elem\n * Element to clean up\n *\n * @param {string} type\n * Type of event to clean up\n *\/\n function _cleanUpEvents(elem, type) {\n if (!DomData.has(elem)) {\n return;\n }\n const data = DomData.get(elem);\n\n \/\/ Remove the events of a particular type if there are none left\n if (data.handlers[type].length === 0) {\n delete data.handlers[type];\n \/\/ data.handlers[type] = null;\n \/\/ Setting to null was causing an error with data.handlers\n\n \/\/ Remove the meta-handler from the element\n if (elem.removeEventListener) {\n elem.removeEventListener(type, data.dispatcher, false);\n } else if (elem.detachEvent) {\n elem.detachEvent('on' + type, data.dispatcher);\n }\n }\n\n \/\/ Remove the events object if there are no types left\n if (Object.getOwnPropertyNames(data.handlers).length <= 0) {\n delete data.handlers;\n delete data.dispatcher;\n delete data.disabled;\n }\n\n \/\/ Finally remove the element data if there is no data left\n if (Object.getOwnPropertyNames(data).length === 0) {\n DomData.delete(elem);\n }\n }\n\n \/**\n * Loops through an array of event types and calls the requested method for each type.\n *\n * @param {Function} fn\n * The event method we want to use.\n *\n * @param {Element|Object} elem\n * Element or object to bind listeners to\n *\n * @param {string[]} types\n * Type of event to bind to.\n *\n * @param {Function} callback\n * Event listener.\n *\/\n function _handleMultipleEvents(fn, elem, types, callback) {\n types.forEach(function (type) {\n \/\/ Call the event method for each one of the types\n fn(elem, type, callback);\n });\n }\n\n \/**\n * Fix a native event to have standard property values\n *\n * @param {Object} event\n * Event object to fix.\n *\n * @return {Object}\n * Fixed event object.\n *\/\n function fixEvent(event) {\n if (event.fixed_) {\n return event;\n }\n function returnTrue() {\n return true;\n }\n function returnFalse() {\n return false;\n }\n\n \/\/ Test if fixing up is needed\n \/\/ Used to check if !event.stopPropagation instead of isPropagationStopped\n \/\/ But native events return true for stopPropagation, but don't have\n \/\/ other expected methods like isPropagationStopped. Seems to be a problem\n \/\/ with the Javascript Ninja code. So we're just overriding all events now.\n if (!event || !event.isPropagationStopped || !event.isImmediatePropagationStopped) {\n const old = event || window.event;\n event = {};\n \/\/ Clone the old object so that we can modify the values event = {};\n \/\/ IE8 Doesn't like when you mess with native event properties\n \/\/ Firefox returns false for event.hasOwnProperty('type') and other props\n \/\/ which makes copying more difficult.\n\n \/\/ TODO: Probably best to create an allowlist of event props\n const deprecatedProps = ['layerX', 'layerY', 'keyLocation', 'path', 'webkitMovementX', 'webkitMovementY', 'mozPressure', 'mozInputSource'];\n for (const key in old) {\n \/\/ Safari 6.0.3 warns you if you try to copy deprecated layerX\/Y\n \/\/ Chrome warns you if you try to copy deprecated keyboardEvent.keyLocation\n \/\/ and webkitMovementX\/Y\n \/\/ Lighthouse complains if Event.path is copied\n if (!deprecatedProps.includes(key)) {\n \/\/ Chrome 32+ warns if you try to copy deprecated returnValue, but\n \/\/ we still want to if preventDefault isn't supported (IE8).\n if (!(key === 'returnValue' && old.preventDefault)) {\n event[key] = old[key];\n }\n }\n }\n\n \/\/ The event occurred on this element\n if (!event.target) {\n event.target = event.srcElement || document;\n }\n\n \/\/ Handle which other element the event is related to\n if (!event.relatedTarget) {\n event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;\n }\n\n \/\/ Stop the default browser action\n event.preventDefault = function () {\n if (old.preventDefault) {\n old.preventDefault();\n }\n event.returnValue = false;\n old.returnValue = false;\n event.defaultPrevented = true;\n };\n event.defaultPrevented = false;\n\n \/\/ Stop the event from bubbling\n event.stopPropagation = function () {\n if (old.stopPropagation) {\n old.stopPropagation();\n }\n event.cancelBubble = true;\n old.cancelBubble = true;\n event.isPropagationStopped = returnTrue;\n };\n event.isPropagationStopped = returnFalse;\n\n \/\/ Stop the event from bubbling and executing other handlers\n event.stopImmediatePropagation = function () {\n if (old.stopImmediatePropagation) {\n old.stopImmediatePropagation();\n }\n event.isImmediatePropagationStopped = returnTrue;\n event.stopPropagation();\n };\n event.isImmediatePropagationStopped = returnFalse;\n\n \/\/ Handle mouse position\n if (event.clientX !== null && event.clientX !== undefined) {\n const doc = document.documentElement;\n const body = document.body;\n event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);\n event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);\n }\n\n \/\/ Handle key presses\n event.which = event.charCode || event.keyCode;\n\n \/\/ Fix button for mouse clicks:\n \/\/ 0 == left; 1 == middle; 2 == right\n if (event.button !== null && event.button !== undefined) {\n \/\/ The following is disabled because it does not pass videojs-standard\n \/\/ and... yikes.\n \/* eslint-disable *\/\n event.button = event.button & 1 ? 0 : event.button & 4 ? 1 : event.button & 2 ? 2 : 0;\n \/* eslint-enable *\/\n }\n }\n event.fixed_ = true;\n \/\/ Returns fixed-up instance\n return event;\n }\n\n \/**\n * Whether passive event listeners are supported\n *\/\n let _supportsPassive;\n const supportsPassive = function () {\n if (typeof _supportsPassive !== 'boolean') {\n _supportsPassive = false;\n try {\n const opts = Object.defineProperty({}, 'passive', {\n get() {\n _supportsPassive = true;\n }\n });\n window.addEventListener('test', null, opts);\n window.removeEventListener('test', null, opts);\n } catch (e) {\n \/\/ disregard\n }\n }\n return _supportsPassive;\n };\n\n \/**\n * Touch events Chrome expects to be passive\n *\/\n const passiveEvents = ['touchstart', 'touchmove'];\n\n \/**\n * Add an event listener to element\n * It stores the handler function in a separate cache object\n * and adds a generic handler to the element's event,\n * along with a unique id (guid) to the element.\n *\n * @param {Element|Object} elem\n * Element or object to bind listeners to\n *\n * @param {string|string[]} type\n * Type of event to bind to.\n *\n * @param {Function} fn\n * Event listener.\n *\/\n function on(elem, type, fn) {\n if (Array.isArray(type)) {\n return _handleMultipleEvents(on, elem, type, fn);\n }\n if (!DomData.has(elem)) {\n DomData.set(elem, {});\n }\n const data = DomData.get(elem);\n\n \/\/ We need a place to store all our handler data\n if (!data.handlers) {\n data.handlers = {};\n }\n if (!data.handlers[type]) {\n data.handlers[type] = [];\n }\n if (!fn.guid) {\n fn.guid = newGUID();\n }\n data.handlers[type].push(fn);\n if (!data.dispatcher) {\n data.disabled = false;\n data.dispatcher = function (event, hash) {\n if (data.disabled) {\n return;\n }\n event = fixEvent(event);\n const handlers = data.handlers[event.type];\n if (handlers) {\n \/\/ Copy handlers so if handlers are added\/removed during the process it doesn't throw everything off.\n const handlersCopy = handlers.slice(0);\n for (let m = 0, n = handlersCopy.length; m < n; m++) {\n if (event.isImmediatePropagationStopped()) {\n break;\n } else {\n try {\n handlersCopy[m].call(elem, event, hash);\n } catch (e) {\n log$1.error(e);\n }\n }\n }\n }\n };\n }\n if (data.handlers[type].length === 1) {\n if (elem.addEventListener) {\n let options = false;\n if (supportsPassive() && passiveEvents.indexOf(type) > -1) {\n options = {\n passive: true\n };\n }\n elem.addEventListener(type, data.dispatcher, options);\n } else if (elem.attachEvent) {\n elem.attachEvent('on' + type, data.dispatcher);\n }\n }\n }\n\n \/**\n * Removes event listeners from an element\n *\n * @param {Element|Object} elem\n * Object to remove listeners from.\n *\n * @param {string|string[]} [type]\n * Type of listener to remove. Don't include to remove all events from element.\n *\n * @param {Function} [fn]\n * Specific listener to remove. Don't include to remove listeners for an event\n * type.\n *\/\n function off(elem, type, fn) {\n \/\/ Don't want to add a cache object through getElData if not needed\n if (!DomData.has(elem)) {\n return;\n }\n const data = DomData.get(elem);\n\n \/\/ If no events exist, nothing to unbind\n if (!data.handlers) {\n return;\n }\n if (Array.isArray(type)) {\n return _handleMultipleEvents(off, elem, type, fn);\n }\n\n \/\/ Utility function\n const removeType = function (el, t) {\n data.handlers[t] = [];\n _cleanUpEvents(el, t);\n };\n\n \/\/ Are we removing all bound events?\n if (type === undefined) {\n for (const t in data.handlers) {\n if (Object.prototype.hasOwnProperty.call(data.handlers || {}, t)) {\n removeType(elem, t);\n }\n }\n return;\n }\n const handlers = data.handlers[type];\n\n \/\/ If no handlers exist, nothing to unbind\n if (!handlers) {\n return;\n }\n\n \/\/ If no listener was provided, remove all listeners for type\n if (!fn) {\n removeType(elem, type);\n return;\n }\n\n \/\/ We're only removing a single handler\n if (fn.guid) {\n for (let n = 0; n < handlers.length; n++) {\n if (handlers[n].guid === fn.guid) {\n handlers.splice(n--, 1);\n }\n }\n }\n _cleanUpEvents(elem, type);\n }\n\n \/**\n * Trigger an event for an element\n *\n * @param {Element|Object} elem\n * Element to trigger an event on\n *\n * @param {EventTarget~Event|string} event\n * A string (the type) or an event object with a type attribute\n *\n * @param {Object} [hash]\n * data hash to pass along with the event\n *\n * @return {boolean|undefined}\n * Returns the opposite of `defaultPrevented` if default was\n * prevented. Otherwise, returns `undefined`\n *\/\n function trigger(elem, event, hash) {\n \/\/ Fetches element data and a reference to the parent (for bubbling).\n \/\/ Don't want to add a data object to cache for every parent,\n \/\/ so checking hasElData first.\n const elemData = DomData.has(elem) ? DomData.get(elem) : {};\n const parent = elem.parentNode || elem.ownerDocument;\n \/\/ type = event.type || event,\n \/\/ handler;\n\n \/\/ If an event name was passed as a string, creates an event out of it\n if (typeof event === 'string') {\n event = {\n type: event,\n target: elem\n };\n } else if (!event.target) {\n event.target = elem;\n }\n\n \/\/ Normalizes the event properties.\n event = fixEvent(event);\n\n \/\/ If the passed element has a dispatcher, executes the established handlers.\n if (elemData.dispatcher) {\n elemData.dispatcher.call(elem, event, hash);\n }\n\n \/\/ Unless explicitly stopped or the event does not bubble (e.g. media events)\n \/\/ recursively calls this function to bubble the event up the DOM.\n if (parent && !event.isPropagationStopped() && event.bubbles === true) {\n trigger.call(null, parent, event, hash);\n\n \/\/ If at the top of the DOM, triggers the default action unless disabled.\n } else if (!parent && !event.defaultPrevented && event.target && event.target[event.type]) {\n if (!DomData.has(event.target)) {\n DomData.set(event.target, {});\n }\n const targetData = DomData.get(event.target);\n\n \/\/ Checks if the target has a default action for this event.\n if (event.target[event.type]) {\n \/\/ Temporarily disables event dispatching on the target as we have already executed the handler.\n targetData.disabled = true;\n \/\/ Executes the default action.\n if (typeof event.target[event.type] === 'function') {\n event.target[event.type]();\n }\n \/\/ Re-enables event dispatching.\n targetData.disabled = false;\n }\n }\n\n \/\/ Inform the triggerer if the default was prevented by returning false\n return !event.defaultPrevented;\n }\n\n \/**\n * Trigger a listener only once for an event.\n *\n * @param {Element|Object} elem\n * Element or object to bind to.\n *\n * @param {string|string[]} type\n * Name\/type of event\n *\n * @param {Event~EventListener} fn\n * Event listener function\n *\/\n function one(elem, type, fn) {\n if (Array.isArray(type)) {\n return _handleMultipleEvents(one, elem, type, fn);\n }\n const func = function () {\n off(elem, type, func);\n fn.apply(this, arguments);\n };\n\n \/\/ copy the guid to the new function so it can removed using the original function's ID\n func.guid = fn.guid = fn.guid || newGUID();\n on(elem, type, func);\n }\n\n \/**\n * Trigger a listener only once and then turn if off for all\n * configured events\n *\n * @param {Element|Object} elem\n * Element or object to bind to.\n *\n * @param {string|string[]} type\n * Name\/type of event\n *\n * @param {Event~EventListener} fn\n * Event listener function\n *\/\n function any(elem, type, fn) {\n const func = function () {\n off(elem, type, func);\n fn.apply(this, arguments);\n };\n\n \/\/ copy the guid to the new function so it can removed using the original function's ID\n func.guid = fn.guid = fn.guid || newGUID();\n\n \/\/ multiple ons, but one off for everything\n on(elem, type, func);\n }\n\n var Events = \/*#__PURE__*\/Object.freeze({\n __proto__: null,\n fixEvent: fixEvent,\n on: on,\n off: off,\n trigger: trigger,\n one: one,\n any: any\n });\n\n \/**\n * @file fn.js\n * @module fn\n *\/\n const UPDATE_REFRESH_INTERVAL = 30;\n\n \/**\n * A private, internal-only function for changing the context of a function.\n *\n * It also stores a unique id on the function so it can be easily removed from\n * events.\n *\n * @private\n * @function\n * @param {*} context\n * The object to bind as scope.\n *\n * @param {Function} fn\n * The function to be bound to a scope.\n *\n * @param {number} [uid]\n * An optional unique ID for the function to be set\n *\n * @return {Function}\n * The new function that will be bound into the context given\n *\/\n const bind_ = function (context, fn, uid) {\n \/\/ Make sure the function has a unique ID\n if (!fn.guid) {\n fn.guid = newGUID();\n }\n\n \/\/ Create the new function that changes the context\n const bound = fn.bind(context);\n\n \/\/ Allow for the ability to individualize this function\n \/\/ Needed in the case where multiple objects might share the same prototype\n \/\/ IF both items add an event listener with the same function, then you try to remove just one\n \/\/ it will remove both because they both have the same guid.\n \/\/ when using this, you need to use the bind method when you remove the listener as well.\n \/\/ currently used in text tracks\n bound.guid = uid ? uid + '_' + fn.guid : fn.guid;\n return bound;\n };\n\n \/**\n * Wraps the given function, `fn`, with a new function that only invokes `fn`\n * at most once per every `wait` milliseconds.\n *\n * @function\n * @param {Function} fn\n * The function to be throttled.\n *\n * @param {number} wait\n * The number of milliseconds by which to throttle.\n *\n * @return {Function}\n *\/\n const throttle = function (fn, wait) {\n let last = window.performance.now();\n const throttled = function (...args) {\n const now = window.performance.now();\n if (now - last >= wait) {\n fn(...args);\n last = now;\n }\n };\n return throttled;\n };\n\n \/**\n * Creates a debounced function that delays invoking `func` until after `wait`\n * milliseconds have elapsed since the last time the debounced function was\n * invoked.\n *\n * Inspired by lodash and underscore implementations.\n *\n * @function\n * @param {Function} func\n * The function to wrap with debounce behavior.\n *\n * @param {number} wait\n * The number of milliseconds to wait after the last invocation.\n *\n * @param {boolean} [immediate]\n * Whether or not to invoke the function immediately upon creation.\n *\n * @param {Object} [context=window]\n * The \"context\" in which the debounced function should debounce. For\n * example, if this function should be tied to a Video.js player,\n * the player can be passed here. Alternatively, defaults to the\n * global `window` object.\n *\n * @return {Function}\n * A debounced function.\n *\/\n const debounce = function (func, wait, immediate, context = window) {\n let timeout;\n const cancel = () => {\n context.clearTimeout(timeout);\n timeout = null;\n };\n\n \/* eslint-disable consistent-this *\/\n const debounced = function () {\n const self = this;\n const args = arguments;\n let later = function () {\n timeout = null;\n later = null;\n if (!immediate) {\n func.apply(self, args);\n }\n };\n if (!timeout && immediate) {\n func.apply(self, args);\n }\n context.clearTimeout(timeout);\n timeout = context.setTimeout(later, wait);\n };\n \/* eslint-enable consistent-this *\/\n\n debounced.cancel = cancel;\n return debounced;\n };\n\n var Fn = \/*#__PURE__*\/Object.freeze({\n __proto__: null,\n UPDATE_REFRESH_INTERVAL: UPDATE_REFRESH_INTERVAL,\n bind_: bind_,\n throttle: throttle,\n debounce: debounce\n });\n\n \/**\n * @file src\/js\/event-target.js\n *\/\n let EVENT_MAP;\n\n \/**\n * `EventTarget` is a class that can have the same API as the DOM `EventTarget`. It\n * adds shorthand functions that wrap around lengthy functions. For example:\n * the `on` function is a wrapper around `addEventListener`.\n *\n * @see [EventTarget Spec]{@link https:\/\/www.w3.org\/TR\/DOM-Level-2-Events\/events.html#Events-EventTarget}\n * @class EventTarget\n *\/\n class EventTarget$2 {\n \/**\n * Adds an `event listener` to an instance of an `EventTarget`. An `event listener` is a\n * function that will get called when an event with a certain name gets triggered.\n *\n * @param {string|string[]} type\n * An event name or an array of event names.\n *\n * @param {Function} fn\n * The function to call with `EventTarget`s\n *\/\n on(type, fn) {\n \/\/ Remove the addEventListener alias before calling Events.on\n \/\/ so we don't get into an infinite type loop\n const ael = this.addEventListener;\n this.addEventListener = () => {};\n on(this, type, fn);\n this.addEventListener = ael;\n }\n \/**\n * Removes an `event listener` for a specific event from an instance of `EventTarget`.\n * This makes it so that the `event listener` will no longer get called when the\n * named event happens.\n *\n * @param {string|string[]} type\n * An event name or an array of event names.\n *\n * @param {Function} fn\n * The function to remove.\n *\/\n off(type, fn) {\n off(this, type, fn);\n }\n \/**\n * This function will add an `event listener` that gets triggered only once. After the\n * first trigger it will get removed. This is like adding an `event listener`\n * with {@link EventTarget#on} that calls {@link EventTarget#off} on itself.\n *\n * @param {string|string[]} type\n * An event name or an array of event names.\n *\n * @param {Function} fn\n * The function to be called once for each event name.\n *\/\n one(type, fn) {\n \/\/ Remove the addEventListener aliasing Events.on\n \/\/ so we don't get into an infinite type loop\n const ael = this.addEventListener;\n this.addEventListener = () => {};\n one(this, type, fn);\n this.addEventListener = ael;\n }\n \/**\n * This function will add an `event listener` that gets triggered only once and is\n * removed from all events. This is like adding an array of `event listener`s\n * with {@link EventTarget#on} that calls {@link EventTarget#off} on all events the\n * first time it is triggered.\n *\n * @param {string|string[]} type\n * An event name or an array of event names.\n *\n * @param {Function} fn\n * The function to be called once for each event name.\n *\/\n any(type, fn) {\n \/\/ Remove the addEventListener aliasing Events.on\n \/\/ so we don't get into an infinite type loop\n const ael = this.addEventListener;\n this.addEventListener = () => {};\n any(this, type, fn);\n this.addEventListener = ael;\n }\n \/**\n * This function causes an event to happen. This will then cause any `event listeners`\n * that are waiting for that event, to get called. If there are no `event listeners`\n * for an event then nothing will happen.\n *\n * If the name of the `Event` that is being triggered is in `EventTarget.allowedEvents_`.\n * Trigger will also call the `on` + `uppercaseEventName` function.\n *\n * Example:\n * 'click' is in `EventTarget.allowedEvents_`, so, trigger will attempt to call\n * `onClick` if it exists.\n *\n * @param {string|EventTarget~Event|Object} event\n * The name of the event, an `Event`, or an object with a key of type set to\n * an event name.\n *\/\n trigger(event) {\n const type = event.type || event;\n\n \/\/ deprecation\n \/\/ In a future version we should default target to `this`\n \/\/ similar to how we default the target to `elem` in\n \/\/ `Events.trigger`. Right now the default `target` will be\n \/\/ `document` due to the `Event.fixEvent` call.\n if (typeof event === 'string') {\n event = {\n type\n };\n }\n event = fixEvent(event);\n if (this.allowedEvents_[type] && this['on' + type]) {\n this['on' + type](event);\n }\n trigger(this, event);\n }\n queueTrigger(event) {\n \/\/ only set up EVENT_MAP if it'll be used\n if (!EVENT_MAP) {\n EVENT_MAP = new Map();\n }\n const type = event.type || event;\n let map = EVENT_MAP.get(this);\n if (!map) {\n map = new Map();\n EVENT_MAP.set(this, map);\n }\n const oldTimeout = map.get(type);\n map.delete(type);\n window.clearTimeout(oldTimeout);\n const timeout = window.setTimeout(() => {\n map.delete(type);\n \/\/ if we cleared out all timeouts for the current target, delete its map\n if (map.size === 0) {\n map = null;\n EVENT_MAP.delete(this);\n }\n this.trigger(event);\n }, 0);\n map.set(type, timeout);\n }\n }\n\n \/**\n * A Custom DOM event.\n *\n * @typedef {CustomEvent} Event\n * @see [Properties]{@link https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/CustomEvent}\n *\/\n\n \/**\n * All event listeners should follow the following format.\n *\n * @callback EventListener\n * @this {EventTarget}\n *\n * @param {Event} event\n * the event that triggered this function\n *\n * @param {Object} [hash]\n * hash of data sent during the event\n *\/\n\n \/**\n * An object containing event names as keys and booleans as values.\n *\n * > NOTE: If an event name is set to a true value here {@link EventTarget#trigger}\n * will have extra functionality. See that function for more information.\n *\n * @property EventTarget.prototype.allowedEvents_\n * @protected\n *\/\n EventTarget$2.prototype.allowedEvents_ = {};\n\n \/**\n * An alias of {@link EventTarget#on}. Allows `EventTarget` to mimic\n * the standard DOM API.\n *\n * @function\n * @see {@link EventTarget#on}\n *\/\n EventTarget$2.prototype.addEventListener = EventTarget$2.prototype.on;\n\n \/**\n * An alias of {@link EventTarget#off}. Allows `EventTarget` to mimic\n * the standard DOM API.\n *\n * @function\n * @see {@link EventTarget#off}\n *\/\n EventTarget$2.prototype.removeEventListener = EventTarget$2.prototype.off;\n\n \/**\n * An alias of {@link EventTarget#trigger}. Allows `EventTarget` to mimic\n * the standard DOM API.\n *\n * @function\n * @see {@link EventTarget#trigger}\n *\/\n EventTarget$2.prototype.dispatchEvent = EventTarget$2.prototype.trigger;\n\n \/**\n * @file mixins\/evented.js\n * @module evented\n *\/\n const objName = obj => {\n if (typeof obj.name === 'function') {\n return obj.name();\n }\n if (typeof obj.name === 'string') {\n return obj.name;\n }\n if (obj.name_) {\n return obj.name_;\n }\n if (obj.constructor && obj.constructor.name) {\n return obj.constructor.name;\n }\n return typeof obj;\n };\n\n \/**\n * Returns whether or not an object has had the evented mixin applied.\n *\n * @param {Object} object\n * An object to test.\n *\n * @return {boolean}\n * Whether or not the object appears to be evented.\n *\/\n const isEvented = object => object instanceof EventTarget$2 || !!object.eventBusEl_ && ['on', 'one', 'off', 'trigger'].every(k => typeof object[k] === 'function');\n\n \/**\n * Adds a callback to run after the evented mixin applied.\n *\n * @param {Object} target\n * An object to Add\n * @param {Function} callback\n * The callback to run.\n *\/\n const addEventedCallback = (target, callback) => {\n if (isEvented(target)) {\n callback();\n } else {\n if (!target.eventedCallbacks) {\n target.eventedCallbacks = [];\n }\n target.eventedCallbacks.push(callback);\n }\n };\n\n \/**\n * Whether a value is a valid event type - non-empty string or array.\n *\n * @private\n * @param {string|Array} type\n * The type value to test.\n *\n * @return {boolean}\n * Whether or not the type is a valid event type.\n *\/\n const isValidEventType = type =>\n \/\/ The regex here verifies that the `type` contains at least one non-\n \/\/ whitespace character.\n typeof type === 'string' && \/\\S\/.test(type) || Array.isArray(type) && !!type.length;\n\n \/**\n * Validates a value to determine if it is a valid event target. Throws if not.\n *\n * @private\n * @throws {Error}\n * If the target does not appear to be a valid event target.\n *\n * @param {Object} target\n * The object to test.\n *\n * @param {Object} obj\n * The evented object we are validating for\n *\n * @param {string} fnName\n * The name of the evented mixin function that called this.\n *\/\n const validateTarget = (target, obj, fnName) => {\n if (!target || !target.nodeName && !isEvented(target)) {\n throw new Error(`Invalid target for ${objName(obj)}#${fnName}; must be a DOM node or evented object.`);\n }\n };\n\n \/**\n * Validates a value to determine if it is a valid event target. Throws if not.\n *\n * @private\n * @throws {Error}\n * If the type does not appear to be a valid event type.\n *\n * @param {string|Array} type\n * The type to test.\n *\n * @param {Object} obj\n * The evented object we are validating for\n *\n * @param {string} fnName\n * The name of the evented mixin function that called this.\n *\/\n const validateEventType = (type, obj, fnName) => {\n if (!isValidEventType(type)) {\n throw new Error(`Invalid event type for ${objName(obj)}#${fnName}; must be a non-empty string or array.`);\n }\n };\n\n \/**\n * Validates a value to determine if it is a valid listener. Throws if not.\n *\n * @private\n * @throws {Error}\n * If the listener is not a function.\n *\n * @param {Function} listener\n * The listener to test.\n *\n * @param {Object} obj\n * The evented object we are validating for\n *\n * @param {string} fnName\n * The name of the evented mixin function that called this.\n *\/\n const validateListener = (listener, obj, fnName) => {\n if (typeof listener !== 'function') {\n throw new Error(`Invalid listener for ${objName(obj)}#${fnName}; must be a function.`);\n }\n };\n\n \/**\n * Takes an array of arguments given to `on()` or `one()`, validates them, and\n * normalizes them into an object.\n *\n * @private\n * @param {Object} self\n * The evented object on which `on()` or `one()` was called. This\n * object will be bound as the `this` value for the listener.\n *\n * @param {Array} args\n * An array of arguments passed to `on()` or `one()`.\n *\n * @param {string} fnName\n * The name of the evented mixin function that called this.\n *\n * @return {Object}\n * An object containing useful values for `on()` or `one()` calls.\n *\/\n const normalizeListenArgs = (self, args, fnName) => {\n \/\/ If the number of arguments is less than 3, the target is always the\n \/\/ evented object itself.\n const isTargetingSelf = args.length < 3 || args[0] === self || args[0] === self.eventBusEl_;\n let target;\n let type;\n let listener;\n if (isTargetingSelf) {\n target = self.eventBusEl_;\n\n \/\/ Deal with cases where we got 3 arguments, but we are still listening to\n \/\/ the evented object itself.\n if (args.length >= 3) {\n args.shift();\n }\n [type, listener] = args;\n } else {\n \/\/ This was `[target, type, listener] = args;` but this block needs more than\n \/\/ one statement to produce minified output compatible with Chrome 53.\n \/\/ See https:\/\/github.com\/videojs\/video.js\/pull\/8810\n target = args[0];\n type = args[1];\n listener = args[2];\n }\n validateTarget(target, self, fnName);\n validateEventType(type, self, fnName);\n validateListener(listener, self, fnName);\n listener = bind_(self, listener);\n return {\n isTargetingSelf,\n target,\n type,\n listener\n };\n };\n\n \/**\n * Adds the listener to the event type(s) on the target, normalizing for\n * the type of target.\n *\n * @private\n * @param {Element|Object} target\n * A DOM node or evented object.\n *\n * @param {string} method\n * The event binding method to use (\"on\" or \"one\").\n *\n * @param {string|Array} type\n * One or more event type(s).\n *\n * @param {Function} listener\n * A listener function.\n *\/\n const listen = (target, method, type, listener) => {\n validateTarget(target, target, method);\n if (target.nodeName) {\n Events[method](target, type, listener);\n } else {\n target[method](type, listener);\n }\n };\n\n \/**\n * Contains methods that provide event capabilities to an object which is passed\n * to {@link module:evented|evented}.\n *\n * @mixin EventedMixin\n *\/\n const EventedMixin = {\n \/**\n * Add a listener to an event (or events) on this object or another evented\n * object.\n *\n * @param {string|Array|Element|Object} targetOrType\n * If this is a string or array, it represents the event type(s)\n * that will trigger the listener.\n *\n * Another evented object can be passed here instead, which will\n * cause the listener to listen for events on _that_ object.\n *\n * In either case, the listener's `this` value will be bound to\n * this object.\n *\n * @param {string|Array|Function} typeOrListener\n * If the first argument was a string or array, this should be the\n * listener function. Otherwise, this is a string or array of event\n * type(s).\n *\n * @param {Function} [listener]\n * If the first argument was another evented object, this will be\n * the listener function.\n *\/\n on(...args) {\n const {\n isTargetingSelf,\n target,\n type,\n listener\n } = normalizeListenArgs(this, args, 'on');\n listen(target, 'on', type, listener);\n\n \/\/ If this object is listening to another evented object.\n if (!isTargetingSelf) {\n \/\/ If this object is disposed, remove the listener.\n const removeListenerOnDispose = () => this.off(target, type, listener);\n\n \/\/ Use the same function ID as the listener so we can remove it later it\n \/\/ using the ID of the original listener.\n removeListenerOnDispose.guid = listener.guid;\n\n \/\/ Add a listener to the target's dispose event as well. This ensures\n \/\/ that if the target is disposed BEFORE this object, we remove the\n \/\/ removal listener that was just added. Otherwise, we create a memory leak.\n const removeRemoverOnTargetDispose = () => this.off('dispose', removeListenerOnDispose);\n\n \/\/ Use the same function ID as the listener so we can remove it later\n \/\/ it using the ID of the original listener.\n removeRemoverOnTargetDispose.guid = listener.guid;\n listen(this, 'on', 'dispose', removeListenerOnDispose);\n listen(target, 'on', 'dispose', removeRemoverOnTargetDispose);\n }\n },\n \/**\n * Add a listener to an event (or events) on this object or another evented\n * object. The listener will be called once per event and then removed.\n *\n * @param {string|Array|Element|Object} targetOrType\n * If this is a string or array, it represents the event type(s)\n * that will trigger the listener.\n *\n * Another evented object can be passed here instead, which will\n * cause the listener to listen for events on _that_ object.\n *\n * In either case, the listener's `this` value will be bound to\n * this object.\n *\n * @param {string|Array|Function} typeOrListener\n * If the first argument was a string or array, this should be the\n * listener function. Otherwise, this is a string or array of event\n * type(s).\n *\n * @param {Function} [listener]\n * If the first argument was another evented object, this will be\n * the listener function.\n *\/\n one(...args) {\n const {\n isTargetingSelf,\n target,\n type,\n listener\n } = normalizeListenArgs(this, args, 'one');\n\n \/\/ Targeting this evented object.\n if (isTargetingSelf) {\n listen(target, 'one', type, listener);\n\n \/\/ Targeting another evented object.\n } else {\n \/\/ TODO: This wrapper is incorrect! It should only\n \/\/ remove the wrapper for the event type that called it.\n \/\/ Instead all listeners are removed on the first trigger!\n \/\/ see https:\/\/github.com\/videojs\/video.js\/issues\/5962\n const wrapper = (...largs) => {\n this.off(target, type, wrapper);\n listener.apply(null, largs);\n };\n\n \/\/ Use the same function ID as the listener so we can remove it later\n \/\/ it using the ID of the original listener.\n wrapper.guid = listener.guid;\n listen(target, 'one', type, wrapper);\n }\n },\n \/**\n * Add a listener to an event (or events) on this object or another evented\n * object. The listener will only be called once for the first event that is triggered\n * then removed.\n *\n * @param {string|Array|Element|Object} targetOrType\n * If this is a string or array, it represents the event type(s)\n * that will trigger the listener.\n *\n * Another evented object can be passed here instead, which will\n * cause the listener to listen for events on _that_ object.\n *\n * In either case, the listener's `this` value will be bound to\n * this object.\n *\n * @param {string|Array|Function} typeOrListener\n * If the first argument was a string or array, this should be the\n * listener function. Otherwise, this is a string or array of event\n * type(s).\n *\n * @param {Function} [listener]\n * If the first argument was another evented object, this will be\n * the listener function.\n *\/\n any(...args) {\n const {\n isTargetingSelf,\n target,\n type,\n listener\n } = normalizeListenArgs(this, args, 'any');\n\n \/\/ Targeting this evented object.\n if (isTargetingSelf) {\n listen(target, 'any', type, listener);\n\n \/\/ Targeting another evented object.\n } else {\n const wrapper = (...largs) => {\n this.off(target, type, wrapper);\n listener.apply(null, largs);\n };\n\n \/\/ Use the same function ID as the listener so we can remove it later\n \/\/ it using the ID of the original listener.\n wrapper.guid = listener.guid;\n listen(target, 'any', type, wrapper);\n }\n },\n \/**\n * Removes listener(s) from event(s) on an evented object.\n *\n * @param {string|Array|Element|Object} [targetOrType]\n * If this is a string or array, it represents the event type(s).\n *\n * Another evented object can be passed here instead, in which case\n * ALL 3 arguments are _required_.\n *\n * @param {string|Array|Function} [typeOrListener]\n * If the first argument was a string or array, this may be the\n * listener function. Otherwise, this is a string or array of event\n * type(s).\n *\n * @param {Function} [listener]\n * If the first argument was another evented object, this will be\n * the listener function; otherwise, _all_ listeners bound to the\n * event type(s) will be removed.\n *\/\n off(targetOrType, typeOrListener, listener) {\n \/\/ Targeting this evented object.\n if (!targetOrType || isValidEventType(targetOrType)) {\n off(this.eventBusEl_, targetOrType, typeOrListener);\n\n \/\/ Targeting another evented object.\n } else {\n const target = targetOrType;\n const type = typeOrListener;\n\n \/\/ Fail fast and in a meaningful way!\n validateTarget(target, this, 'off');\n validateEventType(type, this, 'off');\n validateListener(listener, this, 'off');\n\n \/\/ Ensure there's at least a guid, even if the function hasn't been used\n listener = bind_(this, listener);\n\n \/\/ Remove the dispose listener on this evented object, which was given\n \/\/ the same guid as the event listener in on().\n this.off('dispose', listener);\n if (target.nodeName) {\n off(target, type, listener);\n off(target, 'dispose', listener);\n } else if (isEvented(target)) {\n target.off(type, listener);\n target.off('dispose', listener);\n }\n }\n },\n \/**\n * Fire an event on this evented object, causing its listeners to be called.\n *\n * @param {string|Object} event\n * An event type or an object with a type property.\n *\n * @param {Object} [hash]\n * An additional object to pass along to listeners.\n *\n * @return {boolean}\n * Whether or not the default behavior was prevented.\n *\/\n trigger(event, hash) {\n validateTarget(this.eventBusEl_, this, 'trigger');\n const type = event && typeof event !== 'string' ? event.type : event;\n if (!isValidEventType(type)) {\n throw new Error(`Invalid event type for ${objName(this)}#trigger; ` + 'must be a non-empty string or object with a type key that has a non-empty value.');\n }\n return trigger(this.eventBusEl_, event, hash);\n }\n };\n\n \/**\n * Applies {@link module:evented~EventedMixin|EventedMixin} to a target object.\n *\n * @param {Object} target\n * The object to which to add event methods.\n *\n * @param {Object} [options={}]\n * Options for customizing the mixin behavior.\n *\n * @param {string} [options.eventBusKey]\n * By default, adds a `eventBusEl_` DOM element to the target object,\n * which is used as an event bus. If the target object already has a\n * DOM element that should be used, pass its key here.\n *\n * @return {Object}\n * The target object.\n *\/\n function evented(target, options = {}) {\n const {\n eventBusKey\n } = options;\n\n \/\/ Set or create the eventBusEl_.\n if (eventBusKey) {\n if (!target[eventBusKey].nodeName) {\n throw new Error(`The eventBusKey \"${eventBusKey}\" does not refer to an element.`);\n }\n target.eventBusEl_ = target[eventBusKey];\n } else {\n target.eventBusEl_ = createEl('span', {\n className: 'vjs-event-bus'\n });\n }\n Object.assign(target, EventedMixin);\n if (target.eventedCallbacks) {\n target.eventedCallbacks.forEach(callback => {\n callback();\n });\n }\n\n \/\/ When any evented object is disposed, it removes all its listeners.\n target.on('dispose', () => {\n target.off();\n [target, target.el_, target.eventBusEl_].forEach(function (val) {\n if (val && DomData.has(val)) {\n DomData.delete(val);\n }\n });\n window.setTimeout(() => {\n target.eventBusEl_ = null;\n }, 0);\n });\n return target;\n }\n\n \/**\n * @file mixins\/stateful.js\n * @module stateful\n *\/\n\n \/**\n * Contains methods that provide statefulness to an object which is passed\n * to {@link module:stateful}.\n *\n * @mixin StatefulMixin\n *\/\n const StatefulMixin = {\n \/**\n * A hash containing arbitrary keys and values representing the state of\n * the object.\n *\n * @type {Object}\n *\/\n state: {},\n \/**\n * Set the state of an object by mutating its\n * {@link module:stateful~StatefulMixin.state|state} object in place.\n *\n * @fires module:stateful~StatefulMixin#statechanged\n * @param {Object|Function} stateUpdates\n * A new set of properties to shallow-merge into the plugin state.\n * Can be a plain object or a function returning a plain object.\n *\n * @return {Object|undefined}\n * An object containing changes that occurred. If no changes\n * occurred, returns `undefined`.\n *\/\n setState(stateUpdates) {\n \/\/ Support providing the `stateUpdates` state as a function.\n if (typeof stateUpdates === 'function') {\n stateUpdates = stateUpdates();\n }\n let changes;\n each(stateUpdates, (value, key) => {\n \/\/ Record the change if the value is different from what's in the\n \/\/ current state.\n if (this.state[key] !== value) {\n changes = changes || {};\n changes[key] = {\n from: this.state[key],\n to: value\n };\n }\n this.state[key] = value;\n });\n\n \/\/ Only trigger \"statechange\" if there were changes AND we have a trigger\n \/\/ function. This allows us to not require that the target object be an\n \/\/ evented object.\n if (changes && isEvented(this)) {\n \/**\n * An event triggered on an object that is both\n * {@link module:stateful|stateful} and {@link module:evented|evented}\n * indicating that its state has changed.\n *\n * @event module:stateful~StatefulMixin#statechanged\n * @type {Object}\n * @property {Object} changes\n * A hash containing the properties that were changed and\n * the values they were changed `from` and `to`.\n *\/\n this.trigger({\n changes,\n type: 'statechanged'\n });\n }\n return changes;\n }\n };\n\n \/**\n * Applies {@link module:stateful~StatefulMixin|StatefulMixin} to a target\n * object.\n *\n * If the target object is {@link module:evented|evented} and has a\n * `handleStateChanged` method, that method will be automatically bound to the\n * `statechanged` event on itself.\n *\n * @param {Object} target\n * The object to be made stateful.\n *\n * @param {Object} [defaultState]\n * A default set of properties to populate the newly-stateful object's\n * `state` property.\n *\n * @return {Object}\n * Returns the `target`.\n *\/\n function stateful(target, defaultState) {\n Object.assign(target, StatefulMixin);\n\n \/\/ This happens after the mixing-in because we need to replace the `state`\n \/\/ added in that step.\n target.state = Object.assign({}, target.state, defaultState);\n\n \/\/ Auto-bind the `handleStateChanged` method of the target object if it exists.\n if (typeof target.handleStateChanged === 'function' && isEvented(target)) {\n target.on('statechanged', target.handleStateChanged);\n }\n return target;\n }\n\n \/**\n * @file str.js\n * @module to-lower-case\n *\/\n\n \/**\n * Lowercase the first letter of a string.\n *\n * @param {string} string\n * String to be lowercased\n *\n * @return {string}\n * The string with a lowercased first letter\n *\/\n const toLowerCase = function (string) {\n if (typeof string !== 'string') {\n return string;\n }\n return string.replace(\/.\/, w => w.toLowerCase());\n };\n\n \/**\n * Uppercase the first letter of a string.\n *\n * @param {string} string\n * String to be uppercased\n *\n * @return {string}\n * The string with an uppercased first letter\n *\/\n const toTitleCase$1 = function (string) {\n if (typeof string !== 'string') {\n return string;\n }\n return string.replace(\/.\/, w => w.toUpperCase());\n };\n\n \/**\n * Compares the TitleCase versions of the two strings for equality.\n *\n * @param {string} str1\n * The first string to compare\n *\n * @param {string} str2\n * The second string to compare\n *\n * @return {boolean}\n * Whether the TitleCase versions of the strings are equal\n *\/\n const titleCaseEquals = function (str1, str2) {\n return toTitleCase$1(str1) === toTitleCase$1(str2);\n };\n\n var Str = \/*#__PURE__*\/Object.freeze({\n __proto__: null,\n toLowerCase: toLowerCase,\n toTitleCase: toTitleCase$1,\n titleCaseEquals: titleCaseEquals\n });\n\n \/**\n * Player Component - Base class for all UI objects\n *\n * @file component.js\n *\/\n\n \/** @import Player from '.\/player' *\/\n\n \/**\n * A callback to be called if and when the component is ready.\n * `this` will be the Component instance.\n *\n * @callback ReadyCallback\n * @returns {void}\n *\/\n\n \/**\n * Base class for all UI Components.\n * Components are UI objects which represent both a javascript object and an element\n * in the DOM. They can be children of other components, and can have\n * children themselves.\n *\n * Components can also use methods from {@link EventTarget}\n *\/\n class Component$1 {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options]\n * The key\/value store of component options.\n *\n * @param {Object[]} [options.children]\n * An array of children objects to initialize this component with. Children objects have\n * a name property that will be used if more than one component of the same type needs to be\n * added.\n *\n * @param {string} [options.className]\n * A class or space separated list of classes to add the component\n *\n * @param {ReadyCallback} [ready]\n * Function that gets called when the `Component` is ready.\n *\/\n constructor(player, options, ready) {\n \/\/ The component might be the player itself and we can't pass `this` to super\n if (!player && this.play) {\n this.player_ = player = this; \/\/ eslint-disable-line\n } else {\n this.player_ = player;\n }\n this.isDisposed_ = false;\n\n \/\/ Hold the reference to the parent component via `addChild` method\n this.parentComponent_ = null;\n\n \/\/ Make a copy of prototype.options_ to protect against overriding defaults\n this.options_ = merge$2({}, this.options_);\n\n \/\/ Updated options with supplied options\n options = this.options_ = merge$2(this.options_, options);\n\n \/\/ Get ID from options or options element if one is supplied\n this.id_ = options.id || options.el && options.el.id;\n\n \/\/ If there was no ID from the options, generate one\n if (!this.id_) {\n \/\/ Don't require the player ID function in the case of mock players\n const id = player && player.id && player.id() || 'no_player';\n this.id_ = `${id}_component_${newGUID()}`;\n }\n this.name_ = options.name || null;\n\n \/\/ Create element if one wasn't provided in options\n if (options.el) {\n this.el_ = options.el;\n } else if (options.createEl !== false) {\n this.el_ = this.createEl();\n }\n if (options.className && this.el_) {\n options.className.split(' ').forEach(c => this.addClass(c));\n }\n\n \/\/ Remove the placeholder event methods. If the component is evented, the\n \/\/ real methods are added next\n ['on', 'off', 'one', 'any', 'trigger'].forEach(fn => {\n this[fn] = undefined;\n });\n\n \/\/ if evented is anything except false, we want to mixin in evented\n if (options.evented !== false) {\n \/\/ Make this an evented object and use `el_`, if available, as its event bus\n evented(this, {\n eventBusKey: this.el_ ? 'el_' : null\n });\n this.handleLanguagechange = this.handleLanguagechange.bind(this);\n this.on(this.player_, 'languagechange', this.handleLanguagechange);\n }\n stateful(this, this.constructor.defaultState);\n this.children_ = [];\n this.childIndex_ = {};\n this.childNameIndex_ = {};\n this.setTimeoutIds_ = new Set();\n this.setIntervalIds_ = new Set();\n this.rafIds_ = new Set();\n this.namedRafs_ = new Map();\n this.clearingTimersOnDispose_ = false;\n\n \/\/ Add any child components in options\n if (options.initChildren !== false) {\n this.initChildren();\n }\n\n \/\/ Don't want to trigger ready here or it will go before init is actually\n \/\/ finished for all children that run this constructor\n this.ready(ready);\n if (options.reportTouchActivity !== false) {\n this.enableTouchActivity();\n }\n }\n\n \/\/ `on`, `off`, `one`, `any` and `trigger` are here so tsc includes them in definitions.\n \/\/ They are replaced or removed in the constructor\n\n \/**\n * Adds an `event listener` to an instance of an `EventTarget`. An `event listener` is a\n * function that will get called when an event with a certain name gets triggered.\n *\n * @param {string|string[]} type\n * An event name or an array of event names.\n *\n * @param {Function} fn\n * The function to call with `EventTarget`s\n *\/\n on(type, fn) {}\n\n \/**\n * Removes an `event listener` for a specific event from an instance of `EventTarget`.\n * This makes it so that the `event listener` will no longer get called when the\n * named event happens.\n *\n * @param {string|string[]} type\n * An event name or an array of event names.\n *\n * @param {Function} [fn]\n * The function to remove. If not specified, all listeners managed by Video.js will be removed.\n *\/\n off(type, fn) {}\n\n \/**\n * This function will add an `event listener` that gets triggered only once. After the\n * first trigger it will get removed. This is like adding an `event listener`\n * with {@link EventTarget#on} that calls {@link EventTarget#off} on itself.\n *\n * @param {string|string[]} type\n * An event name or an array of event names.\n *\n * @param {Function} fn\n * The function to be called once for each event name.\n *\/\n one(type, fn) {}\n\n \/**\n * This function will add an `event listener` that gets triggered only once and is\n * removed from all events. This is like adding an array of `event listener`s\n * with {@link EventTarget#on} that calls {@link EventTarget#off} on all events the\n * first time it is triggered.\n *\n * @param {string|string[]} type\n * An event name or an array of event names.\n *\n * @param {Function} fn\n * The function to be called once for each event name.\n *\/\n any(type, fn) {}\n\n \/**\n * This function causes an event to happen. This will then cause any `event listeners`\n * that are waiting for that event, to get called. If there are no `event listeners`\n * for an event then nothing will happen.\n *\n * If the name of the `Event` that is being triggered is in `EventTarget.allowedEvents_`.\n * Trigger will also call the `on` + `uppercaseEventName` function.\n *\n * Example:\n * 'click' is in `EventTarget.allowedEvents_`, so, trigger will attempt to call\n * `onClick` if it exists.\n *\n * @param {string|Event|Object} event\n * The name of the event, an `Event`, or an object with a key of type set to\n * an event name.\n *\n * @param {Object} [hash]\n * Optionally extra argument to pass through to an event listener\n *\/\n trigger(event, hash) {}\n\n \/**\n * Dispose of the `Component` and all child components.\n *\n * @fires Component#dispose\n *\n * @param {Object} options\n * @param {Element} options.originalEl element with which to replace player element\n *\/\n dispose(options = {}) {\n \/\/ Bail out if the component has already been disposed.\n if (this.isDisposed_) {\n return;\n }\n if (this.readyQueue_) {\n this.readyQueue_.length = 0;\n }\n\n \/**\n * Triggered when a `Component` is disposed.\n *\n * @event Component#dispose\n * @type {Event}\n *\n * @property {boolean} [bubbles=false]\n * set to false so that the dispose event does not\n * bubble up\n *\/\n this.trigger({\n type: 'dispose',\n bubbles: false\n });\n this.isDisposed_ = true;\n\n \/\/ Dispose all children.\n if (this.children_) {\n for (let i = this.children_.length - 1; i >= 0; i--) {\n if (this.children_[i].dispose) {\n this.children_[i].dispose();\n }\n }\n }\n\n \/\/ Delete child references\n this.children_ = null;\n this.childIndex_ = null;\n this.childNameIndex_ = null;\n this.parentComponent_ = null;\n if (this.el_) {\n \/\/ Remove element from DOM\n if (this.el_.parentNode) {\n if (options.restoreEl) {\n this.el_.parentNode.replaceChild(options.restoreEl, this.el_);\n } else {\n this.el_.parentNode.removeChild(this.el_);\n }\n }\n this.el_ = null;\n }\n\n \/\/ remove reference to the player after disposing of the element\n this.player_ = null;\n }\n\n \/**\n * Determine whether or not this component has been disposed.\n *\n * @return {boolean}\n * If the component has been disposed, will be `true`. Otherwise, `false`.\n *\/\n isDisposed() {\n return Boolean(this.isDisposed_);\n }\n\n \/**\n * Return the {@link Player} that the `Component` has attached to.\n *\n * @return {Player}\n * The player that this `Component` has attached to.\n *\/\n player() {\n return this.player_;\n }\n\n \/**\n * Deep merge of options objects with new options.\n * > Note: When both `obj` and `options` contain properties whose values are objects.\n * The two properties get merged using {@link module:obj.merge}\n *\n * @param {Object} obj\n * The object that contains new options.\n *\n * @return {Object}\n * A new object of `this.options_` and `obj` merged together.\n *\/\n options(obj) {\n if (!obj) {\n return this.options_;\n }\n this.options_ = merge$2(this.options_, obj);\n return this.options_;\n }\n\n \/**\n * Get the `Component`s DOM element\n *\n * @return {Element}\n * The DOM element for this `Component`.\n *\/\n el() {\n return this.el_;\n }\n\n \/**\n * Create the `Component`s DOM element.\n *\n * @param {string} [tagName]\n * Element's DOM node type. e.g. 'div'\n *\n * @param {Object} [properties]\n * An object of properties that should be set.\n *\n * @param {Object} [attributes]\n * An object of attributes that should be set.\n *\n * @return {Element}\n * The element that gets created.\n *\/\n createEl(tagName, properties, attributes) {\n return createEl(tagName, properties, attributes);\n }\n\n \/**\n * Localize a string given the string in english.\n *\n * If tokens are provided, it'll try and run a simple token replacement on the provided string.\n * The tokens it looks for look like `{1}` with the index being 1-indexed into the tokens array.\n *\n * If a `defaultValue` is provided, it'll use that over `string`,\n * if a value isn't found in provided language files.\n * This is useful if you want to have a descriptive key for token replacement\n * but have a succinct localized string and not require `en.json` to be included.\n *\n * Currently, it is used for the progress bar timing.\n * ```js\n * {\n * \"progress bar timing: currentTime={1} duration={2}\": \"{1} of {2}\"\n * }\n * ```\n * It is then used like so:\n * ```js\n * this.localize('progress bar timing: currentTime={1} duration{2}',\n * [this.player_.currentTime(), this.player_.duration()],\n * '{1} of {2}');\n * ```\n *\n * Which outputs something like: `01:23 of 24:56`.\n *\n *\n * @param {string} string\n * The string to localize and the key to lookup in the language files.\n * @param {string[]} [tokens]\n * If the current item has token replacements, provide the tokens here.\n * @param {string} [defaultValue]\n * Defaults to `string`. Can be a default value to use for token replacement\n * if the lookup key is needed to be separate.\n *\n * @return {string}\n * The localized string or if no localization exists the english string.\n *\/\n localize(string, tokens, defaultValue = string) {\n const code = this.player_.language && this.player_.language();\n const languages = this.player_.languages && this.player_.languages();\n const language = languages && languages[code];\n const primaryCode = code && code.split('-')[0];\n const primaryLang = languages && languages[primaryCode];\n let localizedString = defaultValue;\n if (language && language[string]) {\n localizedString = language[string];\n } else if (primaryLang && primaryLang[string]) {\n localizedString = primaryLang[string];\n }\n if (tokens) {\n localizedString = localizedString.replace(\/\\{(\\d+)\\}\/g, function (match, index) {\n const value = tokens[index - 1];\n let ret = value;\n if (typeof value === 'undefined') {\n ret = match;\n }\n return ret;\n });\n }\n return localizedString;\n }\n\n \/**\n * Handles language change for the player in components. Should be overridden by sub-components.\n *\n * @abstract\n *\/\n handleLanguagechange() {}\n\n \/**\n * Return the `Component`s DOM element. This is where children get inserted.\n * This will usually be the the same as the element returned in {@link Component#el}.\n *\n * @return {Element}\n * The content element for this `Component`.\n *\/\n contentEl() {\n return this.contentEl_ || this.el_;\n }\n\n \/**\n * Get this `Component`s ID\n *\n * @return {string}\n * The id of this `Component`\n *\/\n id() {\n return this.id_;\n }\n\n \/**\n * Get the `Component`s name. The name gets used to reference the `Component`\n * and is set during registration.\n *\n * @return {string}\n * The name of this `Component`.\n *\/\n name() {\n return this.name_;\n }\n\n \/**\n * Get an array of all child components\n *\n * @return {Array}\n * The children\n *\/\n children() {\n return this.children_;\n }\n\n \/**\n * Returns the child `Component` with the given `id`.\n *\n * @param {string} id\n * The id of the child `Component` to get.\n *\n * @return {Component|undefined}\n * The child `Component` with the given `id` or undefined.\n *\/\n getChildById(id) {\n return this.childIndex_[id];\n }\n\n \/**\n * Returns the child `Component` with the given `name`.\n *\n * @param {string} name\n * The name of the child `Component` to get.\n *\n * @return {Component|undefined}\n * The child `Component` with the given `name` or undefined.\n *\/\n getChild(name) {\n if (!name) {\n return;\n }\n return this.childNameIndex_[name];\n }\n\n \/**\n * Returns the descendant `Component` following the givent\n * descendant `names`. For instance ['foo', 'bar', 'baz'] would\n * try to get 'foo' on the current component, 'bar' on the 'foo'\n * component and 'baz' on the 'bar' component and return undefined\n * if any of those don't exist.\n *\n * @param {...string[]|...string} names\n * The name of the child `Component` to get.\n *\n * @return {Component|undefined}\n * The descendant `Component` following the given descendant\n * `names` or undefined.\n *\/\n getDescendant(...names) {\n \/\/ flatten array argument into the main array\n names = names.reduce((acc, n) => acc.concat(n), []);\n let currentChild = this;\n for (let i = 0; i < names.length; i++) {\n currentChild = currentChild.getChild(names[i]);\n if (!currentChild || !currentChild.getChild) {\n return;\n }\n }\n return currentChild;\n }\n\n \/**\n * Adds an SVG icon element to another element or component.\n *\n * @param {string} iconName\n * The name of icon. A list of all the icon names can be found at 'sandbox\/svg-icons.html'\n *\n * @param {Element} [el=this.el()]\n * Element to set the title on. Defaults to the current Component's element.\n *\n * @return {Element}\n * The newly created icon element.\n *\/\n setIcon(iconName, el = this.el()) {\n \/\/ TODO: In v9 of video.js, we will want to remove font icons entirely.\n \/\/ This means this check, as well as the others throughout the code, and\n \/\/ the unecessary CSS for font icons, will need to be removed.\n \/\/ See https:\/\/github.com\/videojs\/video.js\/pull\/8260 as to which components\n \/\/ need updating.\n if (!this.player_.options_.experimentalSvgIcons) {\n return;\n }\n const xmlnsURL = 'http:\/\/www.w3.org\/2000\/svg';\n\n \/\/ The below creates an element in the format of:\n \/\/ ....<\/use><\/svg><\/span>\n const iconContainer = createEl('span', {\n className: 'vjs-icon-placeholder vjs-svg-icon'\n }, {\n 'aria-hidden': 'true'\n });\n const svgEl = document.createElementNS(xmlnsURL, 'svg');\n svgEl.setAttributeNS(null, 'viewBox', '0 0 512 512');\n const useEl = document.createElementNS(xmlnsURL, 'use');\n svgEl.appendChild(useEl);\n useEl.setAttributeNS(null, 'href', `#vjs-icon-${iconName}`);\n iconContainer.appendChild(svgEl);\n\n \/\/ Replace a pre-existing icon if one exists.\n if (this.iconIsSet_) {\n el.replaceChild(iconContainer, el.querySelector('.vjs-icon-placeholder'));\n } else {\n el.appendChild(iconContainer);\n }\n this.iconIsSet_ = true;\n return iconContainer;\n }\n\n \/**\n * Add a child `Component` inside the current `Component`.\n *\n * @param {string|Component} child\n * The name or instance of a child to add.\n *\n * @param {Object} [options={}]\n * The key\/value store of options that will get passed to children of\n * the child.\n *\n * @param {number} [index=this.children_.length]\n * The index to attempt to add a child into.\n *\n *\n * @return {Component}\n * The `Component` that gets added as a child. When using a string the\n * `Component` will get created by this process.\n *\/\n addChild(child, options = {}, index = this.children_.length) {\n let component;\n let componentName;\n\n \/\/ If child is a string, create component with options\n if (typeof child === 'string') {\n componentName = toTitleCase$1(child);\n const componentClassName = options.componentClass || componentName;\n\n \/\/ Set name through options\n options.name = componentName;\n\n \/\/ Create a new object & element for this controls set\n \/\/ If there's no .player_, this is a player\n const ComponentClass = Component$1.getComponent(componentClassName);\n if (!ComponentClass) {\n throw new Error(`Component ${componentClassName} does not exist`);\n }\n\n \/\/ data stored directly on the videojs object may be\n \/\/ misidentified as a component to retain\n \/\/ backwards-compatibility with 4.x. check to make sure the\n \/\/ component class can be instantiated.\n if (typeof ComponentClass !== 'function') {\n return null;\n }\n component = new ComponentClass(this.player_ || this, options);\n\n \/\/ child is a component instance\n } else {\n component = child;\n }\n if (component.parentComponent_) {\n component.parentComponent_.removeChild(component);\n }\n this.children_.splice(index, 0, component);\n component.parentComponent_ = this;\n if (typeof component.id === 'function') {\n this.childIndex_[component.id()] = component;\n }\n\n \/\/ If a name wasn't used to create the component, check if we can use the\n \/\/ name function of the component\n componentName = componentName || component.name && toTitleCase$1(component.name());\n if (componentName) {\n this.childNameIndex_[componentName] = component;\n this.childNameIndex_[toLowerCase(componentName)] = component;\n }\n\n \/\/ Add the UI object's element to the container div (box)\n \/\/ Having an element is not required\n if (typeof component.el === 'function' && component.el()) {\n \/\/ If inserting before a component, insert before that component's element\n let refNode = null;\n if (this.children_[index + 1]) {\n \/\/ Most children are components, but the video tech is an HTML element\n if (this.children_[index + 1].el_) {\n refNode = this.children_[index + 1].el_;\n } else if (isEl(this.children_[index + 1])) {\n refNode = this.children_[index + 1];\n }\n }\n this.contentEl().insertBefore(component.el(), refNode);\n }\n\n \/\/ Return so it can stored on parent object if desired.\n return component;\n }\n\n \/**\n * Remove a child `Component` from this `Component`s list of children. Also removes\n * the child `Component`s element from this `Component`s element.\n *\n * @param {Component} component\n * The child `Component` to remove.\n *\/\n removeChild(component) {\n if (typeof component === 'string') {\n component = this.getChild(component);\n }\n if (!component || !this.children_) {\n return;\n }\n let childFound = false;\n for (let i = this.children_.length - 1; i >= 0; i--) {\n if (this.children_[i] === component) {\n childFound = true;\n this.children_.splice(i, 1);\n break;\n }\n }\n if (!childFound) {\n return;\n }\n component.parentComponent_ = null;\n this.childIndex_[component.id()] = null;\n this.childNameIndex_[toTitleCase$1(component.name())] = null;\n this.childNameIndex_[toLowerCase(component.name())] = null;\n const compEl = component.el();\n if (compEl && compEl.parentNode === this.contentEl()) {\n this.contentEl().removeChild(component.el());\n }\n }\n\n \/**\n * Add and initialize default child `Component`s based upon options.\n *\/\n initChildren() {\n const children = this.options_.children;\n if (children) {\n \/\/ `this` is `parent`\n const parentOptions = this.options_;\n const handleAdd = child => {\n const name = child.name;\n let opts = child.opts;\n\n \/\/ Allow options for children to be set at the parent options\n \/\/ e.g. videojs(id, { controlBar: false });\n \/\/ instead of videojs(id, { children: { controlBar: false });\n if (parentOptions[name] !== undefined) {\n opts = parentOptions[name];\n }\n\n \/\/ Allow for disabling default components\n \/\/ e.g. options['children']['posterImage'] = false\n if (opts === false) {\n return;\n }\n\n \/\/ Allow options to be passed as a simple boolean if no configuration\n \/\/ is necessary.\n if (opts === true) {\n opts = {};\n }\n\n \/\/ We also want to pass the original player options\n \/\/ to each component as well so they don't need to\n \/\/ reach back into the player for options later.\n opts.playerOptions = this.options_.playerOptions;\n\n \/\/ Create and add the child component.\n \/\/ Add a direct reference to the child by name on the parent instance.\n \/\/ If two of the same component are used, different names should be supplied\n \/\/ for each\n const newChild = this.addChild(name, opts);\n if (newChild) {\n this[name] = newChild;\n }\n };\n\n \/\/ Allow for an array of children details to passed in the options\n let workingChildren;\n const Tech = Component$1.getComponent('Tech');\n if (Array.isArray(children)) {\n workingChildren = children;\n } else {\n workingChildren = Object.keys(children);\n }\n workingChildren\n \/\/ children that are in this.options_ but also in workingChildren would\n \/\/ give us extra children we do not want. So, we want to filter them out.\n .concat(Object.keys(this.options_).filter(function (child) {\n return !workingChildren.some(function (wchild) {\n if (typeof wchild === 'string') {\n return child === wchild;\n }\n return child === wchild.name;\n });\n })).map(child => {\n let name;\n let opts;\n if (typeof child === 'string') {\n name = child;\n opts = children[name] || this.options_[name] || {};\n } else {\n name = child.name;\n opts = child;\n }\n return {\n name,\n opts\n };\n }).filter(child => {\n \/\/ we have to make sure that child.name isn't in the techOrder since\n \/\/ techs are registered as Components but can't aren't compatible\n \/\/ See https:\/\/github.com\/videojs\/video.js\/issues\/2772\n const c = Component$1.getComponent(child.opts.componentClass || toTitleCase$1(child.name));\n return c && !Tech.isTech(c);\n }).forEach(handleAdd);\n }\n }\n\n \/**\n * Builds the default DOM class name. Should be overridden by sub-components.\n *\n * @return {string}\n * The DOM class name for this object.\n *\n * @abstract\n *\/\n buildCSSClass() {\n \/\/ Child classes can include a function that does:\n \/\/ return 'CLASS NAME' + this._super();\n return '';\n }\n\n \/**\n * Bind a listener to the component's ready state.\n * Different from event listeners in that if the ready event has already happened\n * it will trigger the function immediately.\n *\n * @param {ReadyCallback} fn\n * Function that gets called when the `Component` is ready.\n *\/\n ready(fn, sync = false) {\n if (!fn) {\n return;\n }\n if (!this.isReady_) {\n this.readyQueue_ = this.readyQueue_ || [];\n this.readyQueue_.push(fn);\n return;\n }\n if (sync) {\n fn.call(this);\n } else {\n \/\/ Call the function asynchronously by default for consistency\n this.setTimeout(fn, 1);\n }\n }\n\n \/**\n * Trigger all the ready listeners for this `Component`.\n *\n * @fires Component#ready\n *\/\n triggerReady() {\n this.isReady_ = true;\n\n \/\/ Ensure ready is triggered asynchronously\n this.setTimeout(function () {\n const readyQueue = this.readyQueue_;\n\n \/\/ Reset Ready Queue\n this.readyQueue_ = [];\n if (readyQueue && readyQueue.length > 0) {\n readyQueue.forEach(function (fn) {\n fn.call(this);\n }, this);\n }\n\n \/\/ Allow for using event listeners also\n \/**\n * Triggered when a `Component` is ready.\n *\n * @event Component#ready\n * @type {Event}\n *\/\n this.trigger('ready');\n }, 1);\n }\n\n \/**\n * Find a single DOM element matching a `selector`. This can be within the `Component`s\n * `contentEl()` or another custom context.\n *\n * @param {string} selector\n * A valid CSS selector, which will be passed to `querySelector`.\n *\n * @param {Element|string} [context=this.contentEl()]\n * A DOM element within which to query. Can also be a selector string in\n * which case the first matching element will get used as context. If\n * missing `this.contentEl()` gets used. If `this.contentEl()` returns\n * nothing it falls back to `document`.\n *\n * @return {Element|null}\n * the dom element that was found, or null\n *\n * @see [Information on CSS Selectors](https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/Guide\/CSS\/Getting_Started\/Selectors)\n *\/\n $(selector, context) {\n return $(selector, context || this.contentEl());\n }\n\n \/**\n * Finds all DOM element matching a `selector`. This can be within the `Component`s\n * `contentEl()` or another custom context.\n *\n * @param {string} selector\n * A valid CSS selector, which will be passed to `querySelectorAll`.\n *\n * @param {Element|string} [context=this.contentEl()]\n * A DOM element within which to query. Can also be a selector string in\n * which case the first matching element will get used as context. If\n * missing `this.contentEl()` gets used. If `this.contentEl()` returns\n * nothing it falls back to `document`.\n *\n * @return {NodeList}\n * a list of dom elements that were found\n *\n * @see [Information on CSS Selectors](https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/Guide\/CSS\/Getting_Started\/Selectors)\n *\/\n $$(selector, context) {\n return $$(selector, context || this.contentEl());\n }\n\n \/**\n * Check if a component's element has a CSS class name.\n *\n * @param {string} classToCheck\n * CSS class name to check.\n *\n * @return {boolean}\n * - True if the `Component` has the class.\n * - False if the `Component` does not have the class`\n *\/\n hasClass(classToCheck) {\n return hasClass(this.el_, classToCheck);\n }\n\n \/**\n * Add a CSS class name to the `Component`s element.\n *\n * @param {...string} classesToAdd\n * One or more CSS class name to add.\n *\/\n addClass(...classesToAdd) {\n addClass(this.el_, ...classesToAdd);\n }\n\n \/**\n * Remove a CSS class name from the `Component`s element.\n *\n * @param {...string} classesToRemove\n * One or more CSS class name to remove.\n *\/\n removeClass(...classesToRemove) {\n removeClass(this.el_, ...classesToRemove);\n }\n\n \/**\n * Add or remove a CSS class name from the component's element.\n * - `classToToggle` gets added when {@link Component#hasClass} would return false.\n * - `classToToggle` gets removed when {@link Component#hasClass} would return true.\n *\n * @param {string} classToToggle\n * The class to add or remove based on (@link Component#hasClass}\n *\n * @param {boolean|Dom~predicate} [predicate]\n * An {@link Dom~predicate} function or a boolean\n *\/\n toggleClass(classToToggle, predicate) {\n toggleClass(this.el_, classToToggle, predicate);\n }\n\n \/**\n * Show the `Component`s element if it is hidden by removing the\n * 'vjs-hidden' class name from it.\n *\/\n show() {\n this.removeClass('vjs-hidden');\n }\n\n \/**\n * Hide the `Component`s element if it is currently showing by adding the\n * 'vjs-hidden` class name to it.\n *\/\n hide() {\n this.addClass('vjs-hidden');\n }\n\n \/**\n * Lock a `Component`s element in its visible state by adding the 'vjs-lock-showing'\n * class name to it. Used during fadeIn\/fadeOut.\n *\n * @private\n *\/\n lockShowing() {\n this.addClass('vjs-lock-showing');\n }\n\n \/**\n * Unlock a `Component`s element from its visible state by removing the 'vjs-lock-showing'\n * class name from it. Used during fadeIn\/fadeOut.\n *\n * @private\n *\/\n unlockShowing() {\n this.removeClass('vjs-lock-showing');\n }\n\n \/**\n * Get the value of an attribute on the `Component`s element.\n *\n * @param {string} attribute\n * Name of the attribute to get the value from.\n *\n * @return {string|null}\n * - The value of the attribute that was asked for.\n * - Can be an empty string on some browsers if the attribute does not exist\n * or has no value\n * - Most browsers will return null if the attribute does not exist or has\n * no value.\n *\n * @see [DOM API]{@link https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Element\/getAttribute}\n *\/\n getAttribute(attribute) {\n return getAttribute(this.el_, attribute);\n }\n\n \/**\n * Set the value of an attribute on the `Component`'s element\n *\n * @param {string} attribute\n * Name of the attribute to set.\n *\n * @param {string} value\n * Value to set the attribute to.\n *\n * @see [DOM API]{@link https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Element\/setAttribute}\n *\/\n setAttribute(attribute, value) {\n setAttribute(this.el_, attribute, value);\n }\n\n \/**\n * Remove an attribute from the `Component`s element.\n *\n * @param {string} attribute\n * Name of the attribute to remove.\n *\n * @see [DOM API]{@link https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Element\/removeAttribute}\n *\/\n removeAttribute(attribute) {\n removeAttribute(this.el_, attribute);\n }\n\n \/**\n * Get or set the width of the component based upon the CSS styles.\n * See {@link Component#dimension} for more detailed information.\n *\n * @param {number|string} [num]\n * The width that you want to set postfixed with '%', 'px' or nothing.\n *\n * @param {boolean} [skipListeners]\n * Skip the componentresize event trigger\n *\n * @return {number|undefined}\n * The width when getting, zero if there is no width\n *\/\n width(num, skipListeners) {\n return this.dimension('width', num, skipListeners);\n }\n\n \/**\n * Get or set the height of the component based upon the CSS styles.\n * See {@link Component#dimension} for more detailed information.\n *\n * @param {number|string} [num]\n * The height that you want to set postfixed with '%', 'px' or nothing.\n *\n * @param {boolean} [skipListeners]\n * Skip the componentresize event trigger\n *\n * @return {number|undefined}\n * The height when getting, zero if there is no height\n *\/\n height(num, skipListeners) {\n return this.dimension('height', num, skipListeners);\n }\n\n \/**\n * Set both the width and height of the `Component` element at the same time.\n *\n * @param {number|string} width\n * Width to set the `Component`s element to.\n *\n * @param {number|string} height\n * Height to set the `Component`s element to.\n *\/\n dimensions(width, height) {\n \/\/ Skip componentresize listeners on width for optimization\n this.width(width, true);\n this.height(height);\n }\n\n \/**\n * Get or set width or height of the `Component` element. This is the shared code\n * for the {@link Component#width} and {@link Component#height}.\n *\n * Things to know:\n * - If the width or height in an number this will return the number postfixed with 'px'.\n * - If the width\/height is a percent this will return the percent postfixed with '%'\n * - Hidden elements have a width of 0 with `window.getComputedStyle`. This function\n * defaults to the `Component`s `style.width` and falls back to `window.getComputedStyle`.\n * See [this]{@link http:\/\/www.foliotek.com\/devblog\/getting-the-width-of-a-hidden-element-with-jquery-using-width\/}\n * for more information\n * - If you want the computed style of the component, use {@link Component#currentWidth}\n * and {@link {Component#currentHeight}\n *\n * @fires Component#componentresize\n *\n * @param {string} widthOrHeight\n 8 'width' or 'height'\n *\n * @param {number|string} [num]\n 8 New dimension\n *\n * @param {boolean} [skipListeners]\n * Skip componentresize event trigger\n *\n * @return {number|undefined}\n * The dimension when getting or 0 if unset\n *\/\n dimension(widthOrHeight, num, skipListeners) {\n if (num !== undefined) {\n \/\/ Set to zero if null or literally NaN (NaN !== NaN)\n if (num === null || num !== num) {\n num = 0;\n }\n\n \/\/ Check if using css width\/height (% or px) and adjust\n if (('' + num).indexOf('%') !== -1 || ('' + num).indexOf('px') !== -1) {\n this.el_.style[widthOrHeight] = num;\n } else if (num === 'auto') {\n this.el_.style[widthOrHeight] = '';\n } else {\n this.el_.style[widthOrHeight] = num + 'px';\n }\n\n \/\/ skipListeners allows us to avoid triggering the resize event when setting both width and height\n if (!skipListeners) {\n \/**\n * Triggered when a component is resized.\n *\n * @event Component#componentresize\n * @type {Event}\n *\/\n this.trigger('componentresize');\n }\n return;\n }\n\n \/\/ Not setting a value, so getting it\n \/\/ Make sure element exists\n if (!this.el_) {\n return 0;\n }\n\n \/\/ Get dimension value from style\n const val = this.el_.style[widthOrHeight];\n const pxIndex = val.indexOf('px');\n if (pxIndex !== -1) {\n \/\/ Return the pixel value with no 'px'\n return parseInt(val.slice(0, pxIndex), 10);\n }\n\n \/\/ No px so using % or no style was set, so falling back to offsetWidth\/height\n \/\/ If component has display:none, offset will return 0\n \/\/ TODO: handle display:none and no dimension style using px\n return parseInt(this.el_['offset' + toTitleCase$1(widthOrHeight)], 10);\n }\n\n \/**\n * Get the computed width or the height of the component's element.\n *\n * Uses `window.getComputedStyle`.\n *\n * @param {string} widthOrHeight\n * A string containing 'width' or 'height'. Whichever one you want to get.\n *\n * @return {number}\n * The dimension that gets asked for or 0 if nothing was set\n * for that dimension.\n *\/\n currentDimension(widthOrHeight) {\n let computedWidthOrHeight = 0;\n if (widthOrHeight !== 'width' && widthOrHeight !== 'height') {\n throw new Error('currentDimension only accepts width or height value');\n }\n computedWidthOrHeight = computedStyle(this.el_, widthOrHeight);\n\n \/\/ remove 'px' from variable and parse as integer\n computedWidthOrHeight = parseFloat(computedWidthOrHeight);\n\n \/\/ if the computed value is still 0, it's possible that the browser is lying\n \/\/ and we want to check the offset values.\n \/\/ This code also runs wherever getComputedStyle doesn't exist.\n if (computedWidthOrHeight === 0 || isNaN(computedWidthOrHeight)) {\n const rule = `offset${toTitleCase$1(widthOrHeight)}`;\n computedWidthOrHeight = this.el_[rule];\n }\n return computedWidthOrHeight;\n }\n\n \/**\n * An object that contains width and height values of the `Component`s\n * computed style. Uses `window.getComputedStyle`.\n *\n * @typedef {Object} Component~DimensionObject\n *\n * @property {number} width\n * The width of the `Component`s computed style.\n *\n * @property {number} height\n * The height of the `Component`s computed style.\n *\/\n\n \/**\n * Get an object that contains computed width and height values of the\n * component's element.\n *\n * Uses `window.getComputedStyle`.\n *\n * @return {Component~DimensionObject}\n * The computed dimensions of the component's element.\n *\/\n currentDimensions() {\n return {\n width: this.currentDimension('width'),\n height: this.currentDimension('height')\n };\n }\n\n \/**\n * Get the computed width of the component's element.\n *\n * Uses `window.getComputedStyle`.\n *\n * @return {number}\n * The computed width of the component's element.\n *\/\n currentWidth() {\n return this.currentDimension('width');\n }\n\n \/**\n * Get the computed height of the component's element.\n *\n * Uses `window.getComputedStyle`.\n *\n * @return {number}\n * The computed height of the component's element.\n *\/\n currentHeight() {\n return this.currentDimension('height');\n }\n\n \/**\n * Retrieves the position and size information of the component's element.\n *\n * @return {Object} An object with `boundingClientRect` and `center` properties.\n * - `boundingClientRect`: An object with properties `x`, `y`, `width`,\n * `height`, `top`, `right`, `bottom`, and `left`, representing\n * the bounding rectangle of the element.\n * - `center`: An object with properties `x` and `y`, representing\n * the center point of the element. `width` and `height` are set to 0.\n *\/\n getPositions() {\n const rect = this.el_.getBoundingClientRect();\n\n \/\/ Creating objects that mirror DOMRectReadOnly for boundingClientRect and center\n const boundingClientRect = {\n x: rect.x,\n y: rect.y,\n width: rect.width,\n height: rect.height,\n top: rect.top,\n right: rect.right,\n bottom: rect.bottom,\n left: rect.left\n };\n\n \/\/ Calculating the center position\n const center = {\n x: rect.left + rect.width \/ 2,\n y: rect.top + rect.height \/ 2,\n width: 0,\n height: 0,\n top: rect.top + rect.height \/ 2,\n right: rect.left + rect.width \/ 2,\n bottom: rect.top + rect.height \/ 2,\n left: rect.left + rect.width \/ 2\n };\n return {\n boundingClientRect,\n center\n };\n }\n\n \/**\n * Set the focus to this component\n *\/\n focus() {\n this.el_.focus();\n }\n\n \/**\n * Remove the focus from this component\n *\/\n blur() {\n this.el_.blur();\n }\n\n \/**\n * When this Component receives a `keydown` event which it does not process,\n * it passes the event to the Player for handling.\n *\n * @param {KeyboardEvent} event\n * The `keydown` event that caused this function to be called.\n *\/\n handleKeyDown(event) {\n if (this.player_) {\n \/\/ We only stop propagation here because we want unhandled events to fall\n \/\/ back to the browser. Exclude Tab for focus trapping, exclude also when spatialNavigation is enabled.\n if (event.key !== 'Tab' && !(this.player_.options_.playerOptions.spatialNavigation && this.player_.options_.playerOptions.spatialNavigation.enabled)) {\n event.stopPropagation();\n }\n this.player_.handleKeyDown(event);\n }\n }\n\n \/**\n * Many components used to have a `handleKeyPress` method, which was poorly\n * named because it listened to a `keydown` event. This method name now\n * delegates to `handleKeyDown`. This means anyone calling `handleKeyPress`\n * will not see their method calls stop working.\n *\n * @param {KeyboardEvent} event\n * The event that caused this function to be called.\n *\/\n handleKeyPress(event) {\n this.handleKeyDown(event);\n }\n\n \/**\n * Emit a 'tap' events when touch event support gets detected. This gets used to\n * support toggling the controls through a tap on the video. They get enabled\n * because every sub-component would have extra overhead otherwise.\n *\n * @protected\n * @fires Component#tap\n * @listens Component#touchstart\n * @listens Component#touchmove\n * @listens Component#touchleave\n * @listens Component#touchcancel\n * @listens Component#touchend\n *\/\n emitTapEvents() {\n \/\/ Track the start time so we can determine how long the touch lasted\n let touchStart = 0;\n let firstTouch = null;\n\n \/\/ Maximum movement allowed during a touch event to still be considered a tap\n \/\/ Other popular libs use anywhere from 2 (hammer.js) to 15,\n \/\/ so 10 seems like a nice, round number.\n const tapMovementThreshold = 10;\n\n \/\/ The maximum length a touch can be while still being considered a tap\n const touchTimeThreshold = 200;\n let couldBeTap;\n this.on('touchstart', function (event) {\n \/\/ If more than one finger, don't consider treating this as a click\n if (event.touches.length === 1) {\n \/\/ Copy pageX\/pageY from the object\n firstTouch = {\n pageX: event.touches[0].pageX,\n pageY: event.touches[0].pageY\n };\n \/\/ Record start time so we can detect a tap vs. \"touch and hold\"\n touchStart = window.performance.now();\n \/\/ Reset couldBeTap tracking\n couldBeTap = true;\n }\n });\n this.on('touchmove', function (event) {\n \/\/ If more than one finger, don't consider treating this as a click\n if (event.touches.length > 1) {\n couldBeTap = false;\n } else if (firstTouch) {\n \/\/ Some devices will throw touchmoves for all but the slightest of taps.\n \/\/ So, if we moved only a small distance, this could still be a tap\n const xdiff = event.touches[0].pageX - firstTouch.pageX;\n const ydiff = event.touches[0].pageY - firstTouch.pageY;\n const touchDistance = Math.sqrt(xdiff * xdiff + ydiff * ydiff);\n if (touchDistance > tapMovementThreshold) {\n couldBeTap = false;\n }\n }\n });\n const noTap = function () {\n couldBeTap = false;\n };\n\n \/\/ TODO: Listen to the original target. http:\/\/youtu.be\/DujfpXOKUp8?t=13m8s\n this.on('touchleave', noTap);\n this.on('touchcancel', noTap);\n\n \/\/ When the touch ends, measure how long it took and trigger the appropriate\n \/\/ event\n this.on('touchend', function (event) {\n firstTouch = null;\n \/\/ Proceed only if the touchmove\/leave\/cancel event didn't happen\n if (couldBeTap === true) {\n \/\/ Measure how long the touch lasted\n const touchTime = window.performance.now() - touchStart;\n\n \/\/ Make sure the touch was less than the threshold to be considered a tap\n if (touchTime < touchTimeThreshold) {\n \/\/ Don't let browser turn this into a click\n event.preventDefault();\n \/**\n * Triggered when a `Component` is tapped.\n *\n * @event Component#tap\n * @type {MouseEvent}\n *\/\n this.trigger('tap');\n \/\/ It may be good to copy the touchend event object and change the\n \/\/ type to tap, if the other event properties aren't exact after\n \/\/ Events.fixEvent runs (e.g. event.target)\n }\n }\n });\n }\n\n \/**\n * This function reports user activity whenever touch events happen. This can get\n * turned off by any sub-components that wants touch events to act another way.\n *\n * Report user touch activity when touch events occur. User activity gets used to\n * determine when controls should show\/hide. It is simple when it comes to mouse\n * events, because any mouse event should show the controls. So we capture mouse\n * events that bubble up to the player and report activity when that happens.\n * With touch events it isn't as easy as `touchstart` and `touchend` toggle player\n * controls. So touch events can't help us at the player level either.\n *\n * User activity gets checked asynchronously. So what could happen is a tap event\n * on the video turns the controls off. Then the `touchend` event bubbles up to\n * the player. Which, if it reported user activity, would turn the controls right\n * back on. We also don't want to completely block touch events from bubbling up.\n * Furthermore a `touchmove` event and anything other than a tap, should not turn\n * controls back on.\n *\n * @listens Component#touchstart\n * @listens Component#touchmove\n * @listens Component#touchend\n * @listens Component#touchcancel\n *\/\n enableTouchActivity() {\n \/\/ Don't continue if the root player doesn't support reporting user activity\n if (!this.player() || !this.player().reportUserActivity) {\n return;\n }\n\n \/\/ listener for reporting that the user is active\n const report = bind_(this.player(), this.player().reportUserActivity);\n let touchHolding;\n this.on('touchstart', function () {\n report();\n \/\/ For as long as the they are touching the device or have their mouse down,\n \/\/ we consider them active even if they're not moving their finger or mouse.\n \/\/ So we want to continue to update that they are active\n this.clearInterval(touchHolding);\n \/\/ report at the same interval as activityCheck\n touchHolding = this.setInterval(report, 250);\n });\n const touchEnd = function (event) {\n report();\n \/\/ stop the interval that maintains activity if the touch is holding\n this.clearInterval(touchHolding);\n };\n this.on('touchmove', report);\n this.on('touchend', touchEnd);\n this.on('touchcancel', touchEnd);\n }\n\n \/**\n * A callback that has no parameters and is bound into `Component`s context.\n *\n * @callback Component~GenericCallback\n * @this Component\n *\/\n\n \/**\n * Creates a function that runs after an `x` millisecond timeout. This function is a\n * wrapper around `window.setTimeout`. There are a few reasons to use this one\n * instead though:\n * 1. It gets cleared via {@link Component#clearTimeout} when\n * {@link Component#dispose} gets called.\n * 2. The function callback will gets turned into a {@link Component~GenericCallback}\n *\n * > Note: You can't use `window.clearTimeout` on the id returned by this function. This\n * will cause its dispose listener not to get cleaned up! Please use\n * {@link Component#clearTimeout} or {@link Component#dispose} instead.\n *\n * @param {Component~GenericCallback} fn\n * The function that will be run after `timeout`.\n *\n * @param {number} timeout\n * Timeout in milliseconds to delay before executing the specified function.\n *\n * @return {number}\n * Returns a timeout ID that gets used to identify the timeout. It can also\n * get used in {@link Component#clearTimeout} to clear the timeout that\n * was set.\n *\n * @listens Component#dispose\n * @see [Similar to]{@link https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/WindowTimers\/setTimeout}\n *\/\n setTimeout(fn, timeout) {\n \/\/ declare as variables so they are properly available in timeout function\n \/\/ eslint-disable-next-line\n var timeoutId;\n fn = bind_(this, fn);\n this.clearTimersOnDispose_();\n timeoutId = window.setTimeout(() => {\n if (this.setTimeoutIds_.has(timeoutId)) {\n this.setTimeoutIds_.delete(timeoutId);\n }\n fn();\n }, timeout);\n this.setTimeoutIds_.add(timeoutId);\n return timeoutId;\n }\n\n \/**\n * Clears a timeout that gets created via `window.setTimeout` or\n * {@link Component#setTimeout}. If you set a timeout via {@link Component#setTimeout}\n * use this function instead of `window.clearTimout`. If you don't your dispose\n * listener will not get cleaned up until {@link Component#dispose}!\n *\n * @param {number} timeoutId\n * The id of the timeout to clear. The return value of\n * {@link Component#setTimeout} or `window.setTimeout`.\n *\n * @return {number}\n * Returns the timeout id that was cleared.\n *\n * @see [Similar to]{@link https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/WindowTimers\/clearTimeout}\n *\/\n clearTimeout(timeoutId) {\n if (this.setTimeoutIds_.has(timeoutId)) {\n this.setTimeoutIds_.delete(timeoutId);\n window.clearTimeout(timeoutId);\n }\n return timeoutId;\n }\n\n \/**\n * Creates a function that gets run every `x` milliseconds. This function is a wrapper\n * around `window.setInterval`. There are a few reasons to use this one instead though.\n * 1. It gets cleared via {@link Component#clearInterval} when\n * {@link Component#dispose} gets called.\n * 2. The function callback will be a {@link Component~GenericCallback}\n *\n * @param {Component~GenericCallback} fn\n * The function to run every `x` seconds.\n *\n * @param {number} interval\n * Execute the specified function every `x` milliseconds.\n *\n * @return {number}\n * Returns an id that can be used to identify the interval. It can also be be used in\n * {@link Component#clearInterval} to clear the interval.\n *\n * @listens Component#dispose\n * @see [Similar to]{@link https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/WindowTimers\/setInterval}\n *\/\n setInterval(fn, interval) {\n fn = bind_(this, fn);\n this.clearTimersOnDispose_();\n const intervalId = window.setInterval(fn, interval);\n this.setIntervalIds_.add(intervalId);\n return intervalId;\n }\n\n \/**\n * Clears an interval that gets created via `window.setInterval` or\n * {@link Component#setInterval}. If you set an interval via {@link Component#setInterval}\n * use this function instead of `window.clearInterval`. If you don't your dispose\n * listener will not get cleaned up until {@link Component#dispose}!\n *\n * @param {number} intervalId\n * The id of the interval to clear. The return value of\n * {@link Component#setInterval} or `window.setInterval`.\n *\n * @return {number}\n * Returns the interval id that was cleared.\n *\n * @see [Similar to]{@link https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/WindowTimers\/clearInterval}\n *\/\n clearInterval(intervalId) {\n if (this.setIntervalIds_.has(intervalId)) {\n this.setIntervalIds_.delete(intervalId);\n window.clearInterval(intervalId);\n }\n return intervalId;\n }\n\n \/**\n * Queues up a callback to be passed to requestAnimationFrame (rAF), but\n * with a few extra bonuses:\n *\n * - Supports browsers that do not support rAF by falling back to\n * {@link Component#setTimeout}.\n *\n * - The callback is turned into a {@link Component~GenericCallback} (i.e.\n * bound to the component).\n *\n * - Automatic cancellation of the rAF callback is handled if the component\n * is disposed before it is called.\n *\n * @param {Component~GenericCallback} fn\n * A function that will be bound to this component and executed just\n * before the browser's next repaint.\n *\n * @return {number}\n * Returns an rAF ID that gets used to identify the timeout. It can\n * also be used in {@link Component#cancelAnimationFrame} to cancel\n * the animation frame callback.\n *\n * @listens Component#dispose\n * @see [Similar to]{@link https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/window\/requestAnimationFrame}\n *\/\n requestAnimationFrame(fn) {\n this.clearTimersOnDispose_();\n\n \/\/ declare as variables so they are properly available in rAF function\n \/\/ eslint-disable-next-line\n var id;\n fn = bind_(this, fn);\n id = window.requestAnimationFrame(() => {\n if (this.rafIds_.has(id)) {\n this.rafIds_.delete(id);\n }\n fn();\n });\n this.rafIds_.add(id);\n return id;\n }\n\n \/**\n * Request an animation frame, but only one named animation\n * frame will be queued. Another will never be added until\n * the previous one finishes.\n *\n * @param {string} name\n * The name to give this requestAnimationFrame\n *\n * @param {Component~GenericCallback} fn\n * A function that will be bound to this component and executed just\n * before the browser's next repaint.\n *\/\n requestNamedAnimationFrame(name, fn) {\n if (this.namedRafs_.has(name)) {\n return;\n }\n this.clearTimersOnDispose_();\n fn = bind_(this, fn);\n const id = this.requestAnimationFrame(() => {\n fn();\n if (this.namedRafs_.has(name)) {\n this.namedRafs_.delete(name);\n }\n });\n this.namedRafs_.set(name, id);\n return name;\n }\n\n \/**\n * Cancels a current named animation frame if it exists.\n *\n * @param {string} name\n * The name of the requestAnimationFrame to cancel.\n *\/\n cancelNamedAnimationFrame(name) {\n if (!this.namedRafs_.has(name)) {\n return;\n }\n this.cancelAnimationFrame(this.namedRafs_.get(name));\n this.namedRafs_.delete(name);\n }\n\n \/**\n * Cancels a queued callback passed to {@link Component#requestAnimationFrame}\n * (rAF).\n *\n * If you queue an rAF callback via {@link Component#requestAnimationFrame},\n * use this function instead of `window.cancelAnimationFrame`. If you don't,\n * your dispose listener will not get cleaned up until {@link Component#dispose}!\n *\n * @param {number} id\n * The rAF ID to clear. The return value of {@link Component#requestAnimationFrame}.\n *\n * @return {number}\n * Returns the rAF ID that was cleared.\n *\n * @see [Similar to]{@link https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/window\/cancelAnimationFrame}\n *\/\n cancelAnimationFrame(id) {\n if (this.rafIds_.has(id)) {\n this.rafIds_.delete(id);\n window.cancelAnimationFrame(id);\n }\n return id;\n }\n\n \/**\n * A function to setup `requestAnimationFrame`, `setTimeout`,\n * and `setInterval`, clearing on dispose.\n *\n * > Previously each timer added and removed dispose listeners on it's own.\n * For better performance it was decided to batch them all, and use `Set`s\n * to track outstanding timer ids.\n *\n * @private\n *\/\n clearTimersOnDispose_() {\n if (this.clearingTimersOnDispose_) {\n return;\n }\n this.clearingTimersOnDispose_ = true;\n this.one('dispose', () => {\n [['namedRafs_', 'cancelNamedAnimationFrame'], ['rafIds_', 'cancelAnimationFrame'], ['setTimeoutIds_', 'clearTimeout'], ['setIntervalIds_', 'clearInterval']].forEach(([idName, cancelName]) => {\n \/\/ for a `Set` key will actually be the value again\n \/\/ so forEach((val, val) =>` but for maps we want to use\n \/\/ the key.\n this[idName].forEach((val, key) => this[cancelName](key));\n });\n this.clearingTimersOnDispose_ = false;\n });\n }\n\n \/**\n * Decide whether an element is actually disabled or not.\n *\n * @function isActuallyDisabled\n * @param element {Node}\n * @return {boolean}\n *\n * @see {@link https:\/\/html.spec.whatwg.org\/multipage\/semantics-other.html#concept-element-disabled}\n *\/\n getIsDisabled() {\n return Boolean(this.el_.disabled);\n }\n\n \/**\n * Decide whether the element is expressly inert or not.\n *\n * @see {@link https:\/\/html.spec.whatwg.org\/multipage\/interaction.html#expressly-inert}\n * @function isExpresslyInert\n * @param element {Node}\n * @return {boolean}\n *\/\n getIsExpresslyInert() {\n return this.el_.inert && !this.el_.ownerDocument.documentElement.inert;\n }\n\n \/**\n * Determine whether or not this component can be considered as focusable component.\n *\n * @param {HTMLElement} el - The HTML element representing the component.\n * @return {boolean}\n * If the component can be focused, will be `true`. Otherwise, `false`.\n *\/\n getIsFocusable(el) {\n const element = el || this.el_;\n return element.tabIndex >= 0 && !(this.getIsDisabled() || this.getIsExpresslyInert());\n }\n\n \/**\n * Determine whether or not this component is currently visible\/enabled\/etc...\n *\n * @param {HTMLElement} el - The HTML element representing the component.\n * @return {boolean}\n * If the component can is currently visible & enabled, will be `true`. Otherwise, `false`.\n *\/\n getIsAvailableToBeFocused(el) {\n \/**\n * Decide the style property of this element is specified whether it's visible or not.\n *\n * @function isVisibleStyleProperty\n * @param element {CSSStyleDeclaration}\n * @return {boolean}\n *\/\n function isVisibleStyleProperty(element) {\n const elementStyle = window.getComputedStyle(element, null);\n const thisVisibility = elementStyle.getPropertyValue('visibility');\n const thisDisplay = elementStyle.getPropertyValue('display');\n const invisibleStyle = ['hidden', 'collapse'];\n return thisDisplay !== 'none' && !invisibleStyle.includes(thisVisibility);\n }\n\n \/**\n * Decide whether the element is being rendered or not.\n * 1. If an element has the style as \"visibility: hidden | collapse\" or \"display: none\", it is not being rendered.\n * 2. If an element has the style as \"opacity: 0\", it is not being rendered.(that is, invisible).\n * 3. If width and height of an element are explicitly set to 0, it is not being rendered.\n * 4. If a parent element is hidden, an element itself is not being rendered.\n * (CSS visibility property and display property are inherited.)\n *\n * @see {@link https:\/\/html.spec.whatwg.org\/multipage\/rendering.html#being-rendered}\n * @function isBeingRendered\n * @param element {Node}\n * @return {boolean}\n *\/\n function isBeingRendered(element) {\n if (!isVisibleStyleProperty(element.parentElement)) {\n return false;\n }\n if (!isVisibleStyleProperty(element) || element.style.opacity === '0' || window.getComputedStyle(element).height === '0px' || window.getComputedStyle(element).width === '0px') {\n return false;\n }\n return true;\n }\n\n \/**\n * Determine if the element is visible for the user or not.\n * 1. If an element sum of its offsetWidth, offsetHeight, height and width is less than 1 is not visible.\n * 2. If elementCenter.x is less than is not visible.\n * 3. If elementCenter.x is more than the document's width is not visible.\n * 4. If elementCenter.y is less than 0 is not visible.\n * 5. If elementCenter.y is the document's height is not visible.\n *\n * @function isVisible\n * @param element {Node}\n * @return {boolean}\n *\/\n function isVisible(element) {\n if (element.offsetWidth + element.offsetHeight + element.getBoundingClientRect().height + element.getBoundingClientRect().width === 0) {\n return false;\n }\n\n \/\/ Define elementCenter object with props of x and y\n \/\/ x: Left position relative to the viewport plus element's width (no margin) divided between 2.\n \/\/ y: Top position relative to the viewport plus element's height (no margin) divided between 2.\n const elementCenter = {\n x: element.getBoundingClientRect().left + element.offsetWidth \/ 2,\n y: element.getBoundingClientRect().top + element.offsetHeight \/ 2\n };\n if (elementCenter.x < 0) {\n return false;\n }\n if (elementCenter.x > (document.documentElement.clientWidth || window.innerWidth)) {\n return false;\n }\n if (elementCenter.y < 0) {\n return false;\n }\n if (elementCenter.y > (document.documentElement.clientHeight || window.innerHeight)) {\n return false;\n }\n let pointContainer = document.elementFromPoint(elementCenter.x, elementCenter.y);\n while (pointContainer) {\n if (pointContainer === element) {\n return true;\n }\n if (pointContainer.parentNode) {\n pointContainer = pointContainer.parentNode;\n } else {\n return false;\n }\n }\n }\n\n \/\/ If no DOM element was passed as argument use this component's element.\n if (!el) {\n el = this.el();\n }\n\n \/\/ If element is visible, is being rendered & either does not have a parent element or its tabIndex is not negative.\n if (isVisible(el) && isBeingRendered(el) && (!el.parentElement || el.tabIndex >= 0)) {\n return true;\n }\n return false;\n }\n\n \/**\n * Register a `Component` with `videojs` given the name and the component.\n *\n * > NOTE: {@link Tech}s should not be registered as a `Component`. {@link Tech}s\n * should be registered using {@link Tech.registerTech} or\n * {@link videojs:videojs.registerTech}.\n *\n * > NOTE: This function can also be seen on videojs as\n * {@link videojs:videojs.registerComponent}.\n *\n * @param {string} name\n * The name of the `Component` to register.\n *\n * @param {Component} ComponentToRegister\n * The `Component` class to register.\n *\n * @return {Component}\n * The `Component` that was registered.\n *\/\n static registerComponent(name, ComponentToRegister) {\n if (typeof name !== 'string' || !name) {\n throw new Error(`Illegal component name, \"${name}\"; must be a non-empty string.`);\n }\n const Tech = Component$1.getComponent('Tech');\n\n \/\/ We need to make sure this check is only done if Tech has been registered.\n const isTech = Tech && Tech.isTech(ComponentToRegister);\n const isComp = Component$1 === ComponentToRegister || Component$1.prototype.isPrototypeOf(ComponentToRegister.prototype);\n if (isTech || !isComp) {\n let reason;\n if (isTech) {\n reason = 'techs must be registered using Tech.registerTech()';\n } else {\n reason = 'must be a Component subclass';\n }\n throw new Error(`Illegal component, \"${name}\"; ${reason}.`);\n }\n name = toTitleCase$1(name);\n if (!Component$1.components_) {\n Component$1.components_ = {};\n }\n const Player = Component$1.getComponent('Player');\n if (name === 'Player' && Player && Player.players) {\n const players = Player.players;\n const playerNames = Object.keys(players);\n\n \/\/ If we have players that were disposed, then their name will still be\n \/\/ in Players.players. So, we must loop through and verify that the value\n \/\/ for each item is not null. This allows registration of the Player component\n \/\/ after all players have been disposed or before any were created.\n if (players && playerNames.length > 0 && playerNames.map(pname => players[pname]).every(Boolean)) {\n throw new Error('Can not register Player component after player has been created.');\n }\n }\n Component$1.components_[name] = ComponentToRegister;\n Component$1.components_[toLowerCase(name)] = ComponentToRegister;\n return ComponentToRegister;\n }\n\n \/**\n * Get a `Component` based on the name it was registered with.\n *\n * @param {string} name\n * The Name of the component to get.\n *\n * @return {typeof Component}\n * The `Component` that got registered under the given name.\n *\/\n static getComponent(name) {\n if (!name || !Component$1.components_) {\n return;\n }\n return Component$1.components_[name];\n }\n }\n Component$1.registerComponent('Component', Component$1);\n\n \/**\n * @file time.js\n * @module time\n *\/\n\n \/**\n * Returns the time for the specified index at the start or end\n * of a TimeRange object.\n *\n * @typedef {Function} TimeRangeIndex\n *\n * @param {number} [index=0]\n * The range number to return the time for.\n *\n * @return {number}\n * The time offset at the specified index.\n *\n * @deprecated The index argument must be provided.\n * In the future, leaving it out will throw an error.\n *\/\n\n \/**\n * An object that contains ranges of time, which mimics {@link TimeRanges}.\n *\n * @typedef {Object} TimeRange\n *\n * @property {number} length\n * The number of time ranges represented by this object.\n *\n * @property {module:time~TimeRangeIndex} start\n * Returns the time offset at which a specified time range begins.\n *\n * @property {module:time~TimeRangeIndex} end\n * Returns the time offset at which a specified time range ends.\n *\n * @see https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/TimeRanges\n *\/\n\n \/**\n * Check if any of the time ranges are over the maximum index.\n *\n * @private\n * @param {string} fnName\n * The function name to use for logging\n *\n * @param {number} index\n * The index to check\n *\n * @param {number} maxIndex\n * The maximum possible index\n *\n * @throws {Error} if the timeRanges provided are over the maxIndex\n *\/\n function rangeCheck(fnName, index, maxIndex) {\n if (typeof index !== 'number' || index < 0 || index > maxIndex) {\n throw new Error(`Failed to execute '${fnName}' on 'TimeRanges': The index provided (${index}) is non-numeric or out of bounds (0-${maxIndex}).`);\n }\n }\n\n \/**\n * Get the time for the specified index at the start or end\n * of a TimeRange object.\n *\n * @private\n * @param {string} fnName\n * The function name to use for logging\n *\n * @param {string} valueIndex\n * The property that should be used to get the time. should be\n * 'start' or 'end'\n *\n * @param {Array} ranges\n * An array of time ranges\n *\n * @param {Array} [rangeIndex=0]\n * The index to start the search at\n *\n * @return {number}\n * The time that offset at the specified index.\n *\n * @deprecated rangeIndex must be set to a value, in the future this will throw an error.\n * @throws {Error} if rangeIndex is more than the length of ranges\n *\/\n function getRange(fnName, valueIndex, ranges, rangeIndex) {\n rangeCheck(fnName, rangeIndex, ranges.length - 1);\n return ranges[rangeIndex][valueIndex];\n }\n\n \/**\n * Create a time range object given ranges of time.\n *\n * @private\n * @param {Array} [ranges]\n * An array of time ranges.\n *\n * @return {TimeRange}\n *\/\n function createTimeRangesObj(ranges) {\n let timeRangesObj;\n if (ranges === undefined || ranges.length === 0) {\n timeRangesObj = {\n length: 0,\n start() {\n throw new Error('This TimeRanges object is empty');\n },\n end() {\n throw new Error('This TimeRanges object is empty');\n }\n };\n } else {\n timeRangesObj = {\n length: ranges.length,\n start: getRange.bind(null, 'start', 0, ranges),\n end: getRange.bind(null, 'end', 1, ranges)\n };\n }\n if (window.Symbol && window.Symbol.iterator) {\n timeRangesObj[window.Symbol.iterator] = () => (ranges || []).values();\n }\n return timeRangesObj;\n }\n\n \/**\n * Create a `TimeRange` object which mimics an\n * {@link https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/TimeRanges|HTML5 TimeRanges instance}.\n *\n * @param {number|Array[]} start\n * The start of a single range (a number) or an array of ranges (an\n * array of arrays of two numbers each).\n *\n * @param {number} end\n * The end of a single range. Cannot be used with the array form of\n * the `start` argument.\n *\n * @return {TimeRange}\n *\/\n function createTimeRanges$1(start, end) {\n if (Array.isArray(start)) {\n return createTimeRangesObj(start);\n } else if (start === undefined || end === undefined) {\n return createTimeRangesObj();\n }\n return createTimeRangesObj([[start, end]]);\n }\n\n \/**\n * Format seconds as a time string, H:MM:SS or M:SS. Supplying a guide (in\n * seconds) will force a number of leading zeros to cover the length of the\n * guide.\n *\n * @private\n * @param {number} seconds\n * Number of seconds to be turned into a string\n *\n * @param {number} guide\n * Number (in seconds) to model the string after\n *\n * @return {string}\n * Time formatted as H:MM:SS or M:SS\n *\/\n const defaultImplementation = function (seconds, guide) {\n seconds = seconds < 0 ? 0 : seconds;\n let s = Math.floor(seconds % 60);\n let m = Math.floor(seconds \/ 60 % 60);\n let h = Math.floor(seconds \/ 3600);\n const gm = Math.floor(guide \/ 60 % 60);\n const gh = Math.floor(guide \/ 3600);\n\n \/\/ handle invalid times\n if (isNaN(seconds) || seconds === Infinity) {\n \/\/ '-' is false for all relational operators (e.g. <, >=) so this setting\n \/\/ will add the minimum number of fields specified by the guide\n h = m = s = '-';\n }\n\n \/\/ Check if we need to show hours\n h = h > 0 || gh > 0 ? h + ':' : '';\n\n \/\/ If hours are showing, we may need to add a leading zero.\n \/\/ Always show at least one digit of minutes.\n m = ((h || gm >= 10) && m < 10 ? '0' + m : m) + ':';\n\n \/\/ Check if leading zero is need for seconds\n s = s < 10 ? '0' + s : s;\n return h + m + s;\n };\n\n \/\/ Internal pointer to the current implementation.\n let implementation = defaultImplementation;\n\n \/**\n * Replaces the default formatTime implementation with a custom implementation.\n *\n * @param {Function} customImplementation\n * A function which will be used in place of the default formatTime\n * implementation. Will receive the current time in seconds and the\n * guide (in seconds) as arguments.\n *\/\n function setFormatTime(customImplementation) {\n implementation = customImplementation;\n }\n\n \/**\n * Resets formatTime to the default implementation.\n *\/\n function resetFormatTime() {\n implementation = defaultImplementation;\n }\n\n \/**\n * Delegates to either the default time formatting function or a custom\n * function supplied via `setFormatTime`.\n *\n * Formats seconds as a time string (H:MM:SS or M:SS). Supplying a\n * guide (in seconds) will force a number of leading zeros to cover the\n * length of the guide.\n *\n * @example formatTime(125, 600) === \"02:05\"\n * @param {number} seconds\n * Number of seconds to be turned into a string\n *\n * @param {number} guide\n * Number (in seconds) to model the string after\n *\n * @return {string}\n * Time formatted as H:MM:SS or M:SS\n *\/\n function formatTime(seconds, guide = seconds) {\n return implementation(seconds, guide);\n }\n\n var Time = \/*#__PURE__*\/Object.freeze({\n __proto__: null,\n createTimeRanges: createTimeRanges$1,\n createTimeRange: createTimeRanges$1,\n setFormatTime: setFormatTime,\n resetFormatTime: resetFormatTime,\n formatTime: formatTime\n });\n\n \/**\n * @file buffer.js\n * @module buffer\n *\/\n\n \/** @import { TimeRange } from '.\/time' *\/\n\n \/**\n * Compute the percentage of the media that has been buffered.\n *\n * @param {TimeRange} buffered\n * The current `TimeRanges` object representing buffered time ranges\n *\n * @param {number} duration\n * Total duration of the media\n *\n * @return {number}\n * Percent buffered of the total duration in decimal form.\n *\/\n function bufferedPercent(buffered, duration) {\n let bufferedDuration = 0;\n let start;\n let end;\n if (!duration) {\n return 0;\n }\n if (!buffered || !buffered.length) {\n buffered = createTimeRanges$1(0, 0);\n }\n for (let i = 0; i < buffered.length; i++) {\n start = buffered.start(i);\n end = buffered.end(i);\n\n \/\/ buffered end can be bigger than duration by a very small fraction\n if (end > duration) {\n end = duration;\n }\n bufferedDuration += end - start;\n }\n return bufferedDuration \/ duration;\n }\n\n \/**\n * @file media-error.js\n *\/\n\n \/**\n * A Custom `MediaError` class which mimics the standard HTML5 `MediaError` class.\n *\n * @param {number|string|Object|MediaError} value\n * This can be of multiple types:\n * - number: should be a standard error code\n * - string: an error message (the code will be 0)\n * - Object: arbitrary properties\n * - `MediaError` (native): used to populate a video.js `MediaError` object\n * - `MediaError` (video.js): will return itself if it's already a\n * video.js `MediaError` object.\n *\n * @see [MediaError Spec]{@link https:\/\/dev.w3.org\/html5\/spec-author-view\/video.html#mediaerror}\n * @see [Encrypted MediaError Spec]{@link https:\/\/www.w3.org\/TR\/2013\/WD-encrypted-media-20130510\/#error-codes}\n *\n * @class MediaError\n *\/\n function MediaError(value) {\n \/\/ Allow redundant calls to this constructor to avoid having `instanceof`\n \/\/ checks peppered around the code.\n if (value instanceof MediaError) {\n return value;\n }\n if (typeof value === 'number') {\n this.code = value;\n } else if (typeof value === 'string') {\n \/\/ default code is zero, so this is a custom error\n this.message = value;\n } else if (isObject$1(value)) {\n \/\/ We assign the `code` property manually because native `MediaError` objects\n \/\/ do not expose it as an own\/enumerable property of the object.\n if (typeof value.code === 'number') {\n this.code = value.code;\n }\n Object.assign(this, value);\n }\n if (!this.message) {\n this.message = MediaError.defaultMessages[this.code] || '';\n }\n }\n\n \/**\n * The error code that refers two one of the defined `MediaError` types\n *\n * @type {Number}\n *\/\n MediaError.prototype.code = 0;\n\n \/**\n * An optional message that to show with the error. Message is not part of the HTML5\n * video spec but allows for more informative custom errors.\n *\n * @type {String}\n *\/\n MediaError.prototype.message = '';\n\n \/**\n * An optional status code that can be set by plugins to allow even more detail about\n * the error. For example a plugin might provide a specific HTTP status code and an\n * error message for that code. Then when the plugin gets that error this class will\n * know how to display an error message for it. This allows a custom message to show\n * up on the `Player` error overlay.\n *\n * @type {Array}\n *\/\n MediaError.prototype.status = null;\n\n \/**\n * An object containing an error type, as well as other information regarding the error.\n *\n * @typedef {{errorType: string, [key: string]: any}} ErrorMetadata\n *\/\n\n \/**\n * An optional object to give more detail about the error. This can be used to give\n * a higher level of specificity to an error versus the more generic MediaError codes.\n * `metadata` expects an `errorType` string that should align with the values from videojs.Error.\n *\n * @type {ErrorMetadata}\n *\/\n MediaError.prototype.metadata = null;\n\n \/**\n * Errors indexed by the W3C standard. The order **CANNOT CHANGE**! See the\n * specification listed under {@link MediaError} for more information.\n *\n * @enum {array}\n * @readonly\n * @property {string} 0 - MEDIA_ERR_CUSTOM\n * @property {string} 1 - MEDIA_ERR_ABORTED\n * @property {string} 2 - MEDIA_ERR_NETWORK\n * @property {string} 3 - MEDIA_ERR_DECODE\n * @property {string} 4 - MEDIA_ERR_SRC_NOT_SUPPORTED\n * @property {string} 5 - MEDIA_ERR_ENCRYPTED\n *\/\n MediaError.errorTypes = ['MEDIA_ERR_CUSTOM', 'MEDIA_ERR_ABORTED', 'MEDIA_ERR_NETWORK', 'MEDIA_ERR_DECODE', 'MEDIA_ERR_SRC_NOT_SUPPORTED', 'MEDIA_ERR_ENCRYPTED'];\n\n \/**\n * The default `MediaError` messages based on the {@link MediaError.errorTypes}.\n *\n * @type {Array}\n * @constant\n *\/\n MediaError.defaultMessages = {\n 1: 'You aborted the media playback',\n 2: 'A network error caused the media download to fail part-way.',\n 3: 'The media playback was aborted due to a corruption problem or because the media used features your browser did not support.',\n 4: 'The media could not be loaded, either because the server or network failed or because the format is not supported.',\n 5: 'The media is encrypted and we do not have the keys to decrypt it.'\n };\n\n \/**\n * W3C error code for any custom error.\n *\n * @member MediaError#MEDIA_ERR_CUSTOM\n * @constant {number}\n * @default 0\n *\/\n MediaError.MEDIA_ERR_CUSTOM = 0;\n\n \/**\n * W3C error code for any custom error.\n *\n * @member MediaError.MEDIA_ERR_CUSTOM\n * @constant {number}\n * @default 0\n *\/\n MediaError.prototype.MEDIA_ERR_CUSTOM = 0;\n\n \/**\n * W3C error code for media error aborted.\n *\n * @member MediaError#MEDIA_ERR_ABORTED\n * @constant {number}\n * @default 1\n *\/\n MediaError.MEDIA_ERR_ABORTED = 1;\n\n \/**\n * W3C error code for media error aborted.\n *\n * @member MediaError.MEDIA_ERR_ABORTED\n * @constant {number}\n * @default 1\n *\/\n MediaError.prototype.MEDIA_ERR_ABORTED = 1;\n\n \/**\n * W3C error code for any network error.\n *\n * @member MediaError#MEDIA_ERR_NETWORK\n * @constant {number}\n * @default 2\n *\/\n MediaError.MEDIA_ERR_NETWORK = 2;\n\n \/**\n * W3C error code for any network error.\n *\n * @member MediaError.MEDIA_ERR_NETWORK\n * @constant {number}\n * @default 2\n *\/\n MediaError.prototype.MEDIA_ERR_NETWORK = 2;\n\n \/**\n * W3C error code for any decoding error.\n *\n * @member MediaError#MEDIA_ERR_DECODE\n * @constant {number}\n * @default 3\n *\/\n MediaError.MEDIA_ERR_DECODE = 3;\n\n \/**\n * W3C error code for any decoding error.\n *\n * @member MediaError.MEDIA_ERR_DECODE\n * @constant {number}\n * @default 3\n *\/\n MediaError.prototype.MEDIA_ERR_DECODE = 3;\n\n \/**\n * W3C error code for any time that a source is not supported.\n *\n * @member MediaError#MEDIA_ERR_SRC_NOT_SUPPORTED\n * @constant {number}\n * @default 4\n *\/\n MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED = 4;\n\n \/**\n * W3C error code for any time that a source is not supported.\n *\n * @member MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED\n * @constant {number}\n * @default 4\n *\/\n MediaError.prototype.MEDIA_ERR_SRC_NOT_SUPPORTED = 4;\n\n \/**\n * W3C error code for any time that a source is encrypted.\n *\n * @member MediaError#MEDIA_ERR_ENCRYPTED\n * @constant {number}\n * @default 5\n *\/\n MediaError.MEDIA_ERR_ENCRYPTED = 5;\n\n \/**\n * W3C error code for any time that a source is encrypted.\n *\n * @member MediaError.MEDIA_ERR_ENCRYPTED\n * @constant {number}\n * @default 5\n *\/\n MediaError.prototype.MEDIA_ERR_ENCRYPTED = 5;\n\n var tuple = SafeParseTuple;\n function SafeParseTuple(obj, reviver) {\n var json;\n var error = null;\n try {\n json = JSON.parse(obj, reviver);\n } catch (err) {\n error = err;\n }\n return [error, json];\n }\n\n \/**\n * Returns whether an object is `Promise`-like (i.e. has a `then` method).\n *\n * @param {Object} value\n * An object that may or may not be `Promise`-like.\n *\n * @return {boolean}\n * Whether or not the object is `Promise`-like.\n *\/\n function isPromise(value) {\n return value !== undefined && value !== null && typeof value.then === 'function';\n }\n\n \/**\n * Silence a Promise-like object.\n *\n * This is useful for avoiding non-harmful, but potentially confusing \"uncaught\n * play promise\" rejection error messages.\n *\n * @param {Object} value\n * An object that may or may not be `Promise`-like.\n *\/\n function silencePromise(value) {\n if (isPromise(value)) {\n value.then(null, e => {});\n }\n }\n\n \/**\n * @file text-track-list-converter.js Utilities for capturing text track state and\n * re-creating tracks based on a capture.\n *\n * @module text-track-list-converter\n *\/\n\n \/** @import Tech from '..\/tech\/tech' *\/\n\n \/**\n * Examine a single {@link TextTrack} and return a JSON-compatible javascript object that\n * represents the {@link TextTrack}'s state.\n *\n * @param {TextTrack} track\n * The text track to query.\n *\n * @return {Object}\n * A serializable javascript representation of the TextTrack.\n * @private\n *\/\n const trackToJson_ = function (track) {\n const ret = ['kind', 'label', 'language', 'id', 'inBandMetadataTrackDispatchType', 'mode', 'src'].reduce((acc, prop, i) => {\n if (track[prop]) {\n acc[prop] = track[prop];\n }\n return acc;\n }, {\n cues: track.cues && Array.prototype.map.call(track.cues, function (cue) {\n return {\n startTime: cue.startTime,\n endTime: cue.endTime,\n text: cue.text,\n id: cue.id\n };\n })\n });\n return ret;\n };\n\n \/**\n * Examine a {@link Tech} and return a JSON-compatible javascript array that represents the\n * state of all {@link TextTrack}s currently configured. The return array is compatible with\n * {@link text-track-list-converter:jsonToTextTracks}.\n *\n * @param {Tech} tech\n * The tech object to query\n *\n * @return {Array}\n * A serializable javascript representation of the {@link Tech}s\n * {@link TextTrackList}.\n *\/\n const textTracksToJson = function (tech) {\n const trackEls = tech.$$('track');\n const trackObjs = Array.prototype.map.call(trackEls, t => t.track);\n const tracks = Array.prototype.map.call(trackEls, function (trackEl) {\n const json = trackToJson_(trackEl.track);\n if (trackEl.src) {\n json.src = trackEl.src;\n }\n return json;\n });\n return tracks.concat(Array.prototype.filter.call(tech.textTracks(), function (track) {\n return trackObjs.indexOf(track) === -1;\n }).map(trackToJson_));\n };\n\n \/**\n * Create a set of remote {@link TextTrack}s on a {@link Tech} based on an array of javascript\n * object {@link TextTrack} representations.\n *\n * @param {Array} json\n * An array of `TextTrack` representation objects, like those that would be\n * produced by `textTracksToJson`.\n *\n * @param {Tech} tech\n * The `Tech` to create the `TextTrack`s on.\n *\/\n const jsonToTextTracks = function (json, tech) {\n json.forEach(function (track) {\n const addedTrack = tech.addRemoteTextTrack(track).track;\n if (!track.src && track.cues) {\n track.cues.forEach(cue => addedTrack.addCue(cue));\n }\n });\n return tech.textTracks();\n };\n var textTrackConverter = {\n textTracksToJson,\n jsonToTextTracks,\n trackToJson_\n };\n\n \/**\n * @file modal-dialog.js\n *\/\n\n \/** @import Player from '.\/player' *\/\n \/** @import { ContentDescriptor } from '.\/utils\/dom' *\/\n\n const MODAL_CLASS_NAME = 'vjs-modal-dialog';\n\n \/**\n * The `ModalDialog` displays over the video and its controls, which blocks\n * interaction with the player until it is closed.\n *\n * Modal dialogs include a \"Close\" button and will close when that button\n * is activated - or when ESC is pressed anywhere.\n *\n * @extends Component\n *\/\n class ModalDialog extends Component$1 {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options]\n * The key\/value store of player options.\n *\n * @param {ContentDescriptor} [options.content=undefined]\n * Provide customized content for this modal.\n *\n * @param {string} [options.description]\n * A text description for the modal, primarily for accessibility.\n *\n * @param {boolean} [options.fillAlways=false]\n * Normally, modals are automatically filled only the first time\n * they open. This tells the modal to refresh its content\n * every time it opens.\n *\n * @param {string} [options.label]\n * A text label for the modal, primarily for accessibility.\n *\n * @param {boolean} [options.pauseOnOpen=true]\n * If `true`, playback will will be paused if playing when\n * the modal opens, and resumed when it closes.\n *\n * @param {boolean} [options.temporary=true]\n * If `true`, the modal can only be opened once; it will be\n * disposed as soon as it's closed.\n *\n * @param {boolean} [options.uncloseable=false]\n * If `true`, the user will not be able to close the modal\n * through the UI in the normal ways. Programmatic closing is\n * still possible.\n *\/\n constructor(player, options) {\n super(player, options);\n this.handleKeyDown_ = e => this.handleKeyDown(e);\n this.close_ = e => this.close(e);\n this.opened_ = this.hasBeenOpened_ = this.hasBeenFilled_ = false;\n this.closeable(!this.options_.uncloseable);\n this.content(this.options_.content);\n\n \/\/ Make sure the contentEl is defined AFTER any children are initialized\n \/\/ because we only want the contents of the modal in the contentEl\n \/\/ (not the UI elements like the close button).\n this.contentEl_ = createEl('div', {\n className: `${MODAL_CLASS_NAME}-content`\n }, {\n role: 'document'\n });\n this.descEl_ = createEl('p', {\n className: `${MODAL_CLASS_NAME}-description vjs-control-text`,\n id: this.el().getAttribute('aria-describedby')\n });\n textContent(this.descEl_, this.description());\n this.el_.appendChild(this.descEl_);\n this.el_.appendChild(this.contentEl_);\n }\n\n \/**\n * Create the `ModalDialog`'s DOM element\n *\n * @return {Element}\n * The DOM element that gets created.\n *\/\n createEl() {\n return super.createEl('div', {\n className: this.buildCSSClass(),\n tabIndex: -1\n }, {\n 'aria-describedby': `${this.id()}_description`,\n 'aria-hidden': 'true',\n 'aria-label': this.label(),\n 'role': 'dialog',\n 'aria-live': 'polite'\n });\n }\n dispose() {\n this.contentEl_ = null;\n this.descEl_ = null;\n this.previouslyActiveEl_ = null;\n super.dispose();\n }\n\n \/**\n * Builds the default DOM `className`.\n *\n * @return {string}\n * The DOM `className` for this object.\n *\/\n buildCSSClass() {\n return `${MODAL_CLASS_NAME} vjs-hidden ${super.buildCSSClass()}`;\n }\n\n \/**\n * Returns the label string for this modal. Primarily used for accessibility.\n *\n * @return {string}\n * the localized or raw label of this modal.\n *\/\n label() {\n return this.localize(this.options_.label || 'Modal Window');\n }\n\n \/**\n * Returns the description string for this modal. Primarily used for\n * accessibility.\n *\n * @return {string}\n * The localized or raw description of this modal.\n *\/\n description() {\n let desc = this.options_.description || this.localize('This is a modal window.');\n\n \/\/ Append a universal closeability message if the modal is closeable.\n if (this.closeable()) {\n desc += ' ' + this.localize('This modal can be closed by pressing the Escape key or activating the close button.');\n }\n return desc;\n }\n\n \/**\n * Opens the modal.\n *\n * @fires ModalDialog#beforemodalopen\n * @fires ModalDialog#modalopen\n *\/\n open() {\n if (this.opened_) {\n if (this.options_.fillAlways) {\n this.fill();\n }\n return;\n }\n const player = this.player();\n\n \/**\n * Fired just before a `ModalDialog` is opened.\n *\n * @event ModalDialog#beforemodalopen\n * @type {Event}\n *\/\n this.trigger('beforemodalopen');\n this.opened_ = true;\n\n \/\/ Fill content if the modal has never opened before and\n \/\/ never been filled.\n if (this.options_.fillAlways || !this.hasBeenOpened_ && !this.hasBeenFilled_) {\n this.fill();\n }\n\n \/\/ If the player was playing, pause it and take note of its previously\n \/\/ playing state.\n this.wasPlaying_ = !player.paused();\n if (this.options_.pauseOnOpen && this.wasPlaying_) {\n player.pause();\n }\n this.on('keydown', this.handleKeyDown_);\n\n \/\/ Hide controls and note if they were enabled.\n this.hadControls_ = player.controls();\n player.controls(false);\n this.show();\n this.conditionalFocus_();\n this.el().setAttribute('aria-hidden', 'false');\n\n \/**\n * Fired just after a `ModalDialog` is opened.\n *\n * @event ModalDialog#modalopen\n * @type {Event}\n *\/\n this.trigger('modalopen');\n this.hasBeenOpened_ = true;\n }\n\n \/**\n * If the `ModalDialog` is currently open or closed.\n *\n * @param {boolean} [value]\n * If given, it will open (`true`) or close (`false`) the modal.\n *\n * @return {boolean}\n * the current open state of the modaldialog\n *\/\n opened(value) {\n if (typeof value === 'boolean') {\n this[value ? 'open' : 'close']();\n }\n return this.opened_;\n }\n\n \/**\n * Closes the modal, does nothing if the `ModalDialog` is\n * not open.\n *\n * @fires ModalDialog#beforemodalclose\n * @fires ModalDialog#modalclose\n *\/\n close() {\n if (!this.opened_) {\n return;\n }\n const player = this.player();\n\n \/**\n * Fired just before a `ModalDialog` is closed.\n *\n * @event ModalDialog#beforemodalclose\n * @type {Event}\n *\/\n this.trigger('beforemodalclose');\n this.opened_ = false;\n if (this.wasPlaying_ && this.options_.pauseOnOpen) {\n player.play();\n }\n this.off('keydown', this.handleKeyDown_);\n if (this.hadControls_) {\n player.controls(true);\n }\n this.hide();\n this.el().setAttribute('aria-hidden', 'true');\n\n \/**\n * Fired just after a `ModalDialog` is closed.\n *\n * @event ModalDialog#modalclose\n * @type {Event}\n *\n * @property {boolean} [bubbles=true]\n *\/\n this.trigger({\n type: 'modalclose',\n bubbles: true\n });\n this.conditionalBlur_();\n if (this.options_.temporary) {\n this.dispose();\n }\n }\n\n \/**\n * Check to see if the `ModalDialog` is closeable via the UI.\n *\n * @param {boolean} [value]\n * If given as a boolean, it will set the `closeable` option.\n *\n * @return {boolean}\n * Returns the final value of the closable option.\n *\/\n closeable(value) {\n if (typeof value === 'boolean') {\n const closeable = this.closeable_ = !!value;\n let close = this.getChild('closeButton');\n\n \/\/ If this is being made closeable and has no close button, add one.\n if (closeable && !close) {\n \/\/ The close button should be a child of the modal - not its\n \/\/ content element, so temporarily change the content element.\n const temp = this.contentEl_;\n this.contentEl_ = this.el_;\n close = this.addChild('closeButton', {\n controlText: 'Close Modal Dialog'\n });\n this.contentEl_ = temp;\n this.on(close, 'close', this.close_);\n }\n\n \/\/ If this is being made uncloseable and has a close button, remove it.\n if (!closeable && close) {\n this.off(close, 'close', this.close_);\n this.removeChild(close);\n close.dispose();\n }\n }\n return this.closeable_;\n }\n\n \/**\n * Fill the modal's content element with the modal's \"content\" option.\n * The content element will be emptied before this change takes place.\n *\/\n fill() {\n this.fillWith(this.content());\n }\n\n \/**\n * Fill the modal's content element with arbitrary content.\n * The content element will be emptied before this change takes place.\n *\n * @fires ModalDialog#beforemodalfill\n * @fires ModalDialog#modalfill\n *\n * @param {ContentDescriptor} [content]\n * The same rules apply to this as apply to the `content` option.\n *\/\n fillWith(content) {\n const contentEl = this.contentEl();\n const parentEl = contentEl.parentNode;\n const nextSiblingEl = contentEl.nextSibling;\n\n \/**\n * Fired just before a `ModalDialog` is filled with content.\n *\n * @event ModalDialog#beforemodalfill\n * @type {Event}\n *\/\n this.trigger('beforemodalfill');\n this.hasBeenFilled_ = true;\n\n \/\/ Detach the content element from the DOM before performing\n \/\/ manipulation to avoid modifying the live DOM multiple times.\n parentEl.removeChild(contentEl);\n this.empty();\n insertContent(contentEl, content);\n \/**\n * Fired just after a `ModalDialog` is filled with content.\n *\n * @event ModalDialog#modalfill\n * @type {Event}\n *\/\n this.trigger('modalfill');\n\n \/\/ Re-inject the re-filled content element.\n if (nextSiblingEl) {\n parentEl.insertBefore(contentEl, nextSiblingEl);\n } else {\n parentEl.appendChild(contentEl);\n }\n\n \/\/ make sure that the close button is last in the dialog DOM\n const closeButton = this.getChild('closeButton');\n if (closeButton) {\n parentEl.appendChild(closeButton.el_);\n }\n\n \/**\n * Fired after `ModalDialog` is re-filled with content & close button is appended.\n *\n * @event ModalDialog#aftermodalfill\n * @type {Event}\n *\/\n this.trigger('aftermodalfill');\n }\n\n \/**\n * Empties the content element. This happens anytime the modal is filled.\n *\n * @fires ModalDialog#beforemodalempty\n * @fires ModalDialog#modalempty\n *\/\n empty() {\n \/**\n * Fired just before a `ModalDialog` is emptied.\n *\n * @event ModalDialog#beforemodalempty\n * @type {Event}\n *\/\n this.trigger('beforemodalempty');\n emptyEl(this.contentEl());\n\n \/**\n * Fired just after a `ModalDialog` is emptied.\n *\n * @event ModalDialog#modalempty\n * @type {Event}\n *\/\n this.trigger('modalempty');\n }\n\n \/**\n * Gets or sets the modal content, which gets normalized before being\n * rendered into the DOM.\n *\n * This does not update the DOM or fill the modal, but it is called during\n * that process.\n *\n * @param {ContentDescriptor} [value]\n * If defined, sets the internal content value to be used on the\n * next call(s) to `fill`. This value is normalized before being\n * inserted. To \"clear\" the internal content value, pass `null`.\n *\n * @return {ContentDescriptor}\n * The current content of the modal dialog\n *\/\n content(value) {\n if (typeof value !== 'undefined') {\n this.content_ = value;\n }\n return this.content_;\n }\n\n \/**\n * conditionally focus the modal dialog if focus was previously on the player.\n *\n * @private\n *\/\n conditionalFocus_() {\n const activeEl = document.activeElement;\n const playerEl = this.player_.el_;\n this.previouslyActiveEl_ = null;\n if (playerEl.contains(activeEl) || playerEl === activeEl) {\n this.previouslyActiveEl_ = activeEl;\n this.focus();\n }\n }\n\n \/**\n * conditionally blur the element and refocus the last focused element\n *\n * @private\n *\/\n conditionalBlur_() {\n if (this.previouslyActiveEl_) {\n this.previouslyActiveEl_.focus();\n this.previouslyActiveEl_ = null;\n }\n }\n\n \/**\n * Keydown handler. Attached when modal is focused.\n *\n * @listens keydown\n *\/\n handleKeyDown(event) {\n \/**\n * Fired a custom keyDown event that bubbles.\n *\n * @event ModalDialog#modalKeydown\n * @type {Event}\n *\/\n this.trigger({\n type: 'modalKeydown',\n originalEvent: event,\n target: this,\n bubbles: true\n });\n \/\/ Do not allow keydowns to reach out of the modal dialog.\n event.stopPropagation();\n if (event.key === 'Escape' && this.closeable()) {\n event.preventDefault();\n this.close();\n return;\n }\n\n \/\/ exit early if it isn't a tab key\n if (event.key !== 'Tab') {\n return;\n }\n const focusableEls = this.focusableEls_();\n const activeEl = this.el_.querySelector(':focus');\n let focusIndex;\n for (let i = 0; i < focusableEls.length; i++) {\n if (activeEl === focusableEls[i]) {\n focusIndex = i;\n break;\n }\n }\n if (document.activeElement === this.el_) {\n focusIndex = 0;\n }\n if (event.shiftKey && focusIndex === 0) {\n focusableEls[focusableEls.length - 1].focus();\n event.preventDefault();\n } else if (!event.shiftKey && focusIndex === focusableEls.length - 1) {\n focusableEls[0].focus();\n event.preventDefault();\n }\n }\n\n \/**\n * get all focusable elements\n *\n * @private\n *\/\n focusableEls_() {\n const allChildren = this.el_.querySelectorAll('*');\n return Array.prototype.filter.call(allChildren, child => {\n return (child instanceof window.HTMLAnchorElement || child instanceof window.HTMLAreaElement) && child.hasAttribute('href') || (child instanceof window.HTMLInputElement || child instanceof window.HTMLSelectElement || child instanceof window.HTMLTextAreaElement || child instanceof window.HTMLButtonElement) && !child.hasAttribute('disabled') || child instanceof window.HTMLIFrameElement || child instanceof window.HTMLObjectElement || child instanceof window.HTMLEmbedElement || child.hasAttribute('tabindex') && child.getAttribute('tabindex') !== -1 || child.hasAttribute('contenteditable');\n });\n }\n }\n\n \/**\n * Default options for `ModalDialog` default options.\n *\n * @type {Object}\n * @private\n *\/\n ModalDialog.prototype.options_ = {\n pauseOnOpen: true,\n temporary: true\n };\n Component$1.registerComponent('ModalDialog', ModalDialog);\n\n \/**\n * @file track-list.js\n *\/\n\n \/** @import Track from '.\/track' *\/\n\n \/**\n * Common functionaliy between {@link TextTrackList}, {@link AudioTrackList}, and\n * {@link VideoTrackList}\n *\n * @extends EventTarget\n *\/\n class TrackList extends EventTarget$2 {\n \/**\n * Create an instance of this class\n *\n * @param { Track[] } tracks\n * A list of tracks to initialize the list with.\n *\n * @abstract\n *\/\n constructor(tracks = []) {\n super();\n this.tracks_ = [];\n\n \/**\n * @memberof TrackList\n * @member {number} length\n * The current number of `Track`s in the this Trackist.\n * @instance\n *\/\n Object.defineProperty(this, 'length', {\n get() {\n return this.tracks_.length;\n }\n });\n for (let i = 0; i < tracks.length; i++) {\n this.addTrack(tracks[i]);\n }\n }\n\n \/**\n * Add a {@link Track} to the `TrackList`\n *\n * @param {Track} track\n * The audio, video, or text track to add to the list.\n *\n * @fires TrackList#addtrack\n *\/\n addTrack(track) {\n const index = this.tracks_.length;\n if (!('' + index in this)) {\n Object.defineProperty(this, index, {\n get() {\n return this.tracks_[index];\n }\n });\n }\n\n \/\/ Do not add duplicate tracks\n if (this.tracks_.indexOf(track) === -1) {\n this.tracks_.push(track);\n \/**\n * Triggered when a track is added to a track list.\n *\n * @event TrackList#addtrack\n * @type {Event}\n * @property {Track} track\n * A reference to track that was added.\n *\/\n this.trigger({\n track,\n type: 'addtrack',\n target: this\n });\n }\n\n \/**\n * Triggered when a track label is changed.\n *\n * @event TrackList#addtrack\n * @type {Event}\n * @property {Track} track\n * A reference to track that was added.\n *\/\n track.labelchange_ = () => {\n this.trigger({\n track,\n type: 'labelchange',\n target: this\n });\n };\n if (isEvented(track)) {\n track.addEventListener('labelchange', track.labelchange_);\n }\n }\n\n \/**\n * Remove a {@link Track} from the `TrackList`\n *\n * @param {Track} rtrack\n * The audio, video, or text track to remove from the list.\n *\n * @fires TrackList#removetrack\n *\/\n removeTrack(rtrack) {\n let track;\n for (let i = 0, l = this.length; i < l; i++) {\n if (this[i] === rtrack) {\n track = this[i];\n if (track.off) {\n track.off();\n }\n this.tracks_.splice(i, 1);\n break;\n }\n }\n if (!track) {\n return;\n }\n\n \/**\n * Triggered when a track is removed from track list.\n *\n * @event TrackList#removetrack\n * @type {Event}\n * @property {Track} track\n * A reference to track that was removed.\n *\/\n this.trigger({\n track,\n type: 'removetrack',\n target: this\n });\n }\n\n \/**\n * Get a Track from the TrackList by a tracks id\n *\n * @param {string} id - the id of the track to get\n * @method getTrackById\n * @return {Track}\n * @private\n *\/\n getTrackById(id) {\n let result = null;\n for (let i = 0, l = this.length; i < l; i++) {\n const track = this[i];\n if (track.id === id) {\n result = track;\n break;\n }\n }\n return result;\n }\n }\n\n \/**\n * Triggered when a different track is selected\/enabled.\n *\n * @event TrackList#change\n * @type {Event}\n *\/\n\n \/**\n * Events that can be called with on + eventName. See {@link EventHandler}.\n *\n * @property {Object} TrackList#allowedEvents_\n * @protected\n *\/\n TrackList.prototype.allowedEvents_ = {\n change: 'change',\n addtrack: 'addtrack',\n removetrack: 'removetrack',\n labelchange: 'labelchange'\n };\n\n \/\/ emulate attribute EventHandler support to allow for feature detection\n for (const event in TrackList.prototype.allowedEvents_) {\n TrackList.prototype['on' + event] = null;\n }\n\n \/**\n * @file audio-track-list.js\n *\/\n\n \/** @import AudioTrack from '.\/audio-track' *\/\n\n \/**\n * Anywhere we call this function we diverge from the spec\n * as we only support one enabled audiotrack at a time\n *\n * @param {AudioTrackList} list\n * list to work on\n *\n * @param {AudioTrack} track\n * The track to skip\n *\n * @private\n *\/\n const disableOthers$1 = function (list, track) {\n for (let i = 0; i < list.length; i++) {\n if (!Object.keys(list[i]).length || track.id === list[i].id) {\n continue;\n }\n \/\/ another audio track is enabled, disable it\n list[i].enabled = false;\n }\n };\n\n \/**\n * The current list of {@link AudioTrack} for a media file.\n *\n * @see [Spec]{@link https:\/\/html.spec.whatwg.org\/multipage\/embedded-content.html#audiotracklist}\n * @extends TrackList\n *\/\n class AudioTrackList extends TrackList {\n \/**\n * Create an instance of this class.\n *\n * @param {AudioTrack[]} [tracks=[]]\n * A list of `AudioTrack` to instantiate the list with.\n *\/\n constructor(tracks = []) {\n \/\/ make sure only 1 track is enabled\n \/\/ sorted from last index to first index\n for (let i = tracks.length - 1; i >= 0; i--) {\n if (tracks[i].enabled) {\n disableOthers$1(tracks, tracks[i]);\n break;\n }\n }\n super(tracks);\n this.changing_ = false;\n }\n\n \/**\n * Add an {@link AudioTrack} to the `AudioTrackList`.\n *\n * @param {AudioTrack} track\n * The AudioTrack to add to the list\n *\n * @fires TrackList#addtrack\n *\/\n addTrack(track) {\n if (track.enabled) {\n disableOthers$1(this, track);\n }\n super.addTrack(track);\n \/\/ native tracks don't have this\n if (!track.addEventListener) {\n return;\n }\n track.enabledChange_ = () => {\n \/\/ when we are disabling other tracks (since we don't support\n \/\/ more than one track at a time) we will set changing_\n \/\/ to true so that we don't trigger additional change events\n if (this.changing_) {\n return;\n }\n this.changing_ = true;\n disableOthers$1(this, track);\n this.changing_ = false;\n this.trigger('change');\n };\n\n \/**\n * @listens AudioTrack#enabledchange\n * @fires TrackList#change\n *\/\n track.addEventListener('enabledchange', track.enabledChange_);\n }\n removeTrack(rtrack) {\n super.removeTrack(rtrack);\n if (rtrack.removeEventListener && rtrack.enabledChange_) {\n rtrack.removeEventListener('enabledchange', rtrack.enabledChange_);\n rtrack.enabledChange_ = null;\n }\n }\n }\n\n \/**\n * @file video-track-list.js\n *\/\n\n \/** @import VideoTrack from '.\/video-track' *\/\n\n \/**\n * Un-select all other {@link VideoTrack}s that are selected.\n *\n * @param {VideoTrackList} list\n * list to work on\n *\n * @param {VideoTrack} track\n * The track to skip\n *\n * @private\n *\/\n const disableOthers = function (list, track) {\n for (let i = 0; i < list.length; i++) {\n if (!Object.keys(list[i]).length || track.id === list[i].id) {\n continue;\n }\n \/\/ another video track is enabled, disable it\n list[i].selected = false;\n }\n };\n\n \/**\n * The current list of {@link VideoTrack} for a video.\n *\n * @see [Spec]{@link https:\/\/html.spec.whatwg.org\/multipage\/embedded-content.html#videotracklist}\n * @extends TrackList\n *\/\n class VideoTrackList extends TrackList {\n \/**\n * Create an instance of this class.\n *\n * @param {VideoTrack[]} [tracks=[]]\n * A list of `VideoTrack` to instantiate the list with.\n *\/\n constructor(tracks = []) {\n \/\/ make sure only 1 track is enabled\n \/\/ sorted from last index to first index\n for (let i = tracks.length - 1; i >= 0; i--) {\n if (tracks[i].selected) {\n disableOthers(tracks, tracks[i]);\n break;\n }\n }\n super(tracks);\n this.changing_ = false;\n\n \/**\n * @member {number} VideoTrackList#selectedIndex\n * The current index of the selected {@link VideoTrack`}.\n *\/\n Object.defineProperty(this, 'selectedIndex', {\n get() {\n for (let i = 0; i < this.length; i++) {\n if (this[i].selected) {\n return i;\n }\n }\n return -1;\n },\n set() {}\n });\n }\n\n \/**\n * Add a {@link VideoTrack} to the `VideoTrackList`.\n *\n * @param {VideoTrack} track\n * The VideoTrack to add to the list\n *\n * @fires TrackList#addtrack\n *\/\n addTrack(track) {\n if (track.selected) {\n disableOthers(this, track);\n }\n super.addTrack(track);\n \/\/ native tracks don't have this\n if (!track.addEventListener) {\n return;\n }\n track.selectedChange_ = () => {\n if (this.changing_) {\n return;\n }\n this.changing_ = true;\n disableOthers(this, track);\n this.changing_ = false;\n this.trigger('change');\n };\n\n \/**\n * @listens VideoTrack#selectedchange\n * @fires TrackList#change\n *\/\n track.addEventListener('selectedchange', track.selectedChange_);\n }\n removeTrack(rtrack) {\n super.removeTrack(rtrack);\n if (rtrack.removeEventListener && rtrack.selectedChange_) {\n rtrack.removeEventListener('selectedchange', rtrack.selectedChange_);\n rtrack.selectedChange_ = null;\n }\n }\n }\n\n \/**\n * @file text-track-list.js\n *\/\n\n \/** @import TextTrack from '.\/text-track' *\/\n\n \/**\n * The current list of {@link TextTrack} for a media file.\n *\n * @see [Spec]{@link https:\/\/html.spec.whatwg.org\/multipage\/embedded-content.html#texttracklist}\n * @extends TrackList\n *\/\n class TextTrackList extends TrackList {\n \/**\n * Add a {@link TextTrack} to the `TextTrackList`\n *\n * @param {TextTrack} track\n * The text track to add to the list.\n *\n * @fires TrackList#addtrack\n *\/\n addTrack(track) {\n super.addTrack(track);\n if (!this.queueChange_) {\n this.queueChange_ = () => this.queueTrigger('change');\n }\n if (!this.triggerSelectedlanguagechange) {\n this.triggerSelectedlanguagechange_ = () => this.trigger('selectedlanguagechange');\n }\n\n \/**\n * @listens TextTrack#modechange\n * @fires TrackList#change\n *\/\n track.addEventListener('modechange', this.queueChange_);\n const nonLanguageTextTrackKind = ['metadata', 'chapters'];\n if (nonLanguageTextTrackKind.indexOf(track.kind) === -1) {\n track.addEventListener('modechange', this.triggerSelectedlanguagechange_);\n }\n }\n removeTrack(rtrack) {\n super.removeTrack(rtrack);\n\n \/\/ manually remove the event handlers we added\n if (rtrack.removeEventListener) {\n if (this.queueChange_) {\n rtrack.removeEventListener('modechange', this.queueChange_);\n }\n if (this.selectedlanguagechange_) {\n rtrack.removeEventListener('modechange', this.triggerSelectedlanguagechange_);\n }\n }\n }\n }\n\n \/**\n * @file html-track-element-list.js\n *\/\n\n \/**\n * The current list of {@link HtmlTrackElement}s.\n *\/\n class HtmlTrackElementList {\n \/**\n * Create an instance of this class.\n *\n * @param {HtmlTrackElement[]} [tracks=[]]\n * A list of `HtmlTrackElement` to instantiate the list with.\n *\/\n constructor(trackElements = []) {\n this.trackElements_ = [];\n\n \/**\n * @memberof HtmlTrackElementList\n * @member {number} length\n * The current number of `Track`s in the this Trackist.\n * @instance\n *\/\n Object.defineProperty(this, 'length', {\n get() {\n return this.trackElements_.length;\n }\n });\n for (let i = 0, length = trackElements.length; i < length; i++) {\n this.addTrackElement_(trackElements[i]);\n }\n }\n\n \/**\n * Add an {@link HtmlTrackElement} to the `HtmlTrackElementList`\n *\n * @param {HtmlTrackElement} trackElement\n * The track element to add to the list.\n *\n * @private\n *\/\n addTrackElement_(trackElement) {\n const index = this.trackElements_.length;\n if (!('' + index in this)) {\n Object.defineProperty(this, index, {\n get() {\n return this.trackElements_[index];\n }\n });\n }\n\n \/\/ Do not add duplicate elements\n if (this.trackElements_.indexOf(trackElement) === -1) {\n this.trackElements_.push(trackElement);\n }\n }\n\n \/**\n * Get an {@link HtmlTrackElement} from the `HtmlTrackElementList` given an\n * {@link TextTrack}.\n *\n * @param {TextTrack} track\n * The track associated with a track element.\n *\n * @return {HtmlTrackElement|undefined}\n * The track element that was found or undefined.\n *\n * @private\n *\/\n getTrackElementByTrack_(track) {\n let trackElement_;\n for (let i = 0, length = this.trackElements_.length; i < length; i++) {\n if (track === this.trackElements_[i].track) {\n trackElement_ = this.trackElements_[i];\n break;\n }\n }\n return trackElement_;\n }\n\n \/**\n * Remove a {@link HtmlTrackElement} from the `HtmlTrackElementList`\n *\n * @param {HtmlTrackElement} trackElement\n * The track element to remove from the list.\n *\n * @private\n *\/\n removeTrackElement_(trackElement) {\n for (let i = 0, length = this.trackElements_.length; i < length; i++) {\n if (trackElement === this.trackElements_[i]) {\n if (this.trackElements_[i].track && typeof this.trackElements_[i].track.off === 'function') {\n this.trackElements_[i].track.off();\n }\n if (typeof this.trackElements_[i].off === 'function') {\n this.trackElements_[i].off();\n }\n this.trackElements_.splice(i, 1);\n break;\n }\n }\n }\n }\n\n \/**\n * @file text-track-cue-list.js\n *\/\n\n \/**\n * @typedef {Object} TextTrackCueList~TextTrackCue\n *\n * @property {string} id\n * The unique id for this text track cue\n *\n * @property {number} startTime\n * The start time for this text track cue\n *\n * @property {number} endTime\n * The end time for this text track cue\n *\n * @property {boolean} pauseOnExit\n * Pause when the end time is reached if true.\n *\n * @see [Spec]{@link https:\/\/html.spec.whatwg.org\/multipage\/embedded-content.html#texttrackcue}\n *\/\n\n \/**\n * A List of TextTrackCues.\n *\n * @see [Spec]{@link https:\/\/html.spec.whatwg.org\/multipage\/embedded-content.html#texttrackcuelist}\n *\/\n class TextTrackCueList {\n \/**\n * Create an instance of this class..\n *\n * @param {Array} cues\n * A list of cues to be initialized with\n *\/\n constructor(cues) {\n TextTrackCueList.prototype.setCues_.call(this, cues);\n\n \/**\n * @memberof TextTrackCueList\n * @member {number} length\n * The current number of `TextTrackCue`s in the TextTrackCueList.\n * @instance\n *\/\n Object.defineProperty(this, 'length', {\n get() {\n return this.length_;\n }\n });\n }\n\n \/**\n * A setter for cues in this list. Creates getters\n * an an index for the cues.\n *\n * @param {Array} cues\n * An array of cues to set\n *\n * @private\n *\/\n setCues_(cues) {\n const oldLength = this.length || 0;\n let i = 0;\n const l = cues.length;\n this.cues_ = cues;\n this.length_ = cues.length;\n const defineProp = function (index) {\n if (!('' + index in this)) {\n Object.defineProperty(this, '' + index, {\n get() {\n return this.cues_[index];\n }\n });\n }\n };\n if (oldLength < l) {\n i = oldLength;\n for (; i < l; i++) {\n defineProp.call(this, i);\n }\n }\n }\n\n \/**\n * Get a `TextTrackCue` that is currently in the `TextTrackCueList` by id.\n *\n * @param {string} id\n * The id of the cue that should be searched for.\n *\n * @return {TextTrackCueList~TextTrackCue|null}\n * A single cue or null if none was found.\n *\/\n getCueById(id) {\n let result = null;\n for (let i = 0, l = this.length; i < l; i++) {\n const cue = this[i];\n if (cue.id === id) {\n result = cue;\n break;\n }\n }\n return result;\n }\n }\n\n \/**\n * @file track-kinds.js\n *\/\n\n \/**\n * All possible `VideoTrackKind`s\n *\n * @see https:\/\/html.spec.whatwg.org\/multipage\/embedded-content.html#dom-videotrack-kind\n * @typedef VideoTrack~Kind\n * @enum\n *\/\n const VideoTrackKind = {\n alternative: 'alternative',\n captions: 'captions',\n main: 'main',\n sign: 'sign',\n subtitles: 'subtitles',\n commentary: 'commentary'\n };\n\n \/**\n * All possible `AudioTrackKind`s\n *\n * @see https:\/\/html.spec.whatwg.org\/multipage\/embedded-content.html#dom-audiotrack-kind\n * @typedef AudioTrack~Kind\n * @enum\n *\/\n const AudioTrackKind = {\n 'alternative': 'alternative',\n 'descriptions': 'descriptions',\n 'main': 'main',\n 'main-desc': 'main-desc',\n 'translation': 'translation',\n 'commentary': 'commentary'\n };\n\n \/**\n * All possible `TextTrackKind`s\n *\n * @see https:\/\/html.spec.whatwg.org\/multipage\/embedded-content.html#dom-texttrack-kind\n * @typedef TextTrack~Kind\n * @enum\n *\/\n const TextTrackKind = {\n subtitles: 'subtitles',\n captions: 'captions',\n descriptions: 'descriptions',\n chapters: 'chapters',\n metadata: 'metadata'\n };\n\n \/**\n * All possible `TextTrackMode`s\n *\n * @see https:\/\/html.spec.whatwg.org\/multipage\/embedded-content.html#texttrackmode\n * @typedef TextTrack~Mode\n * @enum\n *\/\n const TextTrackMode = {\n disabled: 'disabled',\n hidden: 'hidden',\n showing: 'showing'\n };\n\n \/**\n * @file track.js\n *\/\n\n \/**\n * A Track class that contains all of the common functionality for {@link AudioTrack},\n * {@link VideoTrack}, and {@link TextTrack}.\n *\n * > Note: This class should not be used directly\n *\n * @see {@link https:\/\/html.spec.whatwg.org\/multipage\/embedded-content.html}\n * @extends EventTarget\n * @abstract\n *\/\n class Track extends EventTarget$2 {\n \/**\n * Create an instance of this class.\n *\n * @param {Object} [options={}]\n * Object of option names and values\n *\n * @param {string} [options.kind='']\n * A valid kind for the track type you are creating.\n *\n * @param {string} [options.id='vjs_track_' + Guid.newGUID()]\n * A unique id for this AudioTrack.\n *\n * @param {string} [options.label='']\n * The menu label for this track.\n *\n * @param {string} [options.language='']\n * A valid two character language code.\n *\n * @abstract\n *\/\n constructor(options = {}) {\n super();\n const trackProps = {\n id: options.id || 'vjs_track_' + newGUID(),\n kind: options.kind || '',\n language: options.language || ''\n };\n let label = options.label || '';\n\n \/**\n * @memberof Track\n * @member {string} id\n * The id of this track. Cannot be changed after creation.\n * @instance\n *\n * @readonly\n *\/\n\n \/**\n * @memberof Track\n * @member {string} kind\n * The kind of track that this is. Cannot be changed after creation.\n * @instance\n *\n * @readonly\n *\/\n\n \/**\n * @memberof Track\n * @member {string} language\n * The two letter language code for this track. Cannot be changed after\n * creation.\n * @instance\n *\n * @readonly\n *\/\n\n for (const key in trackProps) {\n Object.defineProperty(this, key, {\n get() {\n return trackProps[key];\n },\n set() {}\n });\n }\n\n \/**\n * @memberof Track\n * @member {string} label\n * The label of this track. Cannot be changed after creation.\n * @instance\n *\n * @fires Track#labelchange\n *\/\n Object.defineProperty(this, 'label', {\n get() {\n return label;\n },\n set(newLabel) {\n if (newLabel !== label) {\n label = newLabel;\n\n \/**\n * An event that fires when label changes on this track.\n *\n * > Note: This is not part of the spec!\n *\n * @event Track#labelchange\n * @type {Event}\n *\/\n this.trigger('labelchange');\n }\n }\n });\n }\n }\n\n \/**\n * @file url.js\n * @module url\n *\/\n\n \/**\n * Resolve and parse the elements of a URL.\n *\n * @function\n * @param {string} url\n * The url to parse\n *\n * @return {URL}\n * An object of url details\n *\/\n const parseUrl = function (url) {\n return new URL(url, document.baseURI);\n };\n\n \/**\n * Get absolute version of relative URL.\n *\n * @function\n * @param {string} url\n * URL to make absolute\n *\n * @return {string}\n * Absolute URL\n *\/\n const getAbsoluteURL = function (url) {\n return new URL(url, document.baseURI).href;\n };\n\n \/**\n * Returns the extension of the passed file name. It will return an empty string\n * if passed an invalid path.\n *\n * @function\n * @param {string} path\n * The fileName path like '\/path\/to\/file.mp4'\n *\n * @return {string}\n * The extension in lower case or an empty string if no\n * extension could be found.\n *\/\n const getFileExtension = function (path) {\n if (typeof path === 'string') {\n const splitPathRe = \/^(\\\/?)([\\s\\S]*?)((?:\\.{1,2}|[^\\\/]+?)(\\.([^\\.\\\/\\?]+)))(?:[\\\/]*|[\\?].*)$\/;\n const pathParts = splitPathRe.exec(path);\n if (pathParts) {\n return pathParts.pop().toLowerCase();\n }\n }\n return '';\n };\n\n \/**\n * Returns whether the url passed is a cross domain request or not.\n *\n * @function\n * @param {string} url\n * The url to check.\n *\n * @param {URL} [winLoc]\n * the domain to check the url against, defaults to window.location\n *\n * @return {boolean}\n * Whether it is a cross domain request or not.\n *\/\n const isCrossOrigin = function (url, winLoc = window.location) {\n return parseUrl(url).origin !== winLoc.origin;\n };\n\n var Url = \/*#__PURE__*\/Object.freeze({\n __proto__: null,\n parseUrl: parseUrl,\n getAbsoluteURL: getAbsoluteURL,\n getFileExtension: getFileExtension,\n isCrossOrigin: isCrossOrigin\n });\n\n var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};\n\n function unwrapExports (x) {\n \treturn x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;\n }\n\n function createCommonjsModule(fn, module) {\n \treturn module = { exports: {} }, fn(module, module.exports), module.exports;\n }\n\n var win;\n if (typeof window !== \"undefined\") {\n win = window;\n } else if (typeof commonjsGlobal !== \"undefined\") {\n win = commonjsGlobal;\n } else if (typeof self !== \"undefined\") {\n win = self;\n } else {\n win = {};\n }\n var window_1 = win;\n\n var _extends_1 = createCommonjsModule(function (module) {\n function _extends() {\n return (module.exports = _extends = Object.assign ? Object.assign.bind() : function (n) {\n for (var e = 1; e < arguments.length; e++) {\n var t = arguments[e];\n for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]);\n }\n return n;\n }, module.exports.__esModule = true, module.exports[\"default\"] = module.exports), _extends.apply(null, arguments);\n }\n module.exports = _extends, module.exports.__esModule = true, module.exports[\"default\"] = module.exports;\n });\n var _extends$1 = unwrapExports(_extends_1);\n\n var isFunction_1 = isFunction;\n var toString = Object.prototype.toString;\n function isFunction(fn) {\n if (!fn) {\n return false;\n }\n var string = toString.call(fn);\n return string === '[object Function]' || typeof fn === 'function' && string !== '[object RegExp]' || typeof window !== 'undefined' && (\n \/\/ IE8 and below\n fn === window.setTimeout || fn === window.alert || fn === window.confirm || fn === window.prompt);\n }\n\n function _createForOfIteratorHelperLoose(o, allowArrayLike) {\n var it = typeof Symbol !== \"undefined\" && o[Symbol.iterator] || o[\"@@iterator\"];\n if (it) return (it = it.call(o)).next.bind(it);\n if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === \"number\") {\n if (it) o = it;\n var i = 0;\n return function () {\n if (i >= o.length) return {\n done: true\n };\n return {\n done: false,\n value: o[i++]\n };\n };\n }\n throw new TypeError(\"Invalid attempt to iterate non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\");\n }\n function _unsupportedIterableToArray(o, minLen) {\n if (!o) return;\n if (typeof o === \"string\") return _arrayLikeToArray(o, minLen);\n var n = Object.prototype.toString.call(o).slice(8, -1);\n if (n === \"Object\" && o.constructor) n = o.constructor.name;\n if (n === \"Map\" || n === \"Set\") return Array.from(o);\n if (n === \"Arguments\" || \/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$\/.test(n)) return _arrayLikeToArray(o, minLen);\n }\n function _arrayLikeToArray(arr, len) {\n if (len == null || len > arr.length) len = arr.length;\n for (var i = 0, arr2 = new Array(len); i < len; i++) {\n arr2[i] = arr[i];\n }\n return arr2;\n }\n var InterceptorsStorage = \/*#__PURE__*\/function () {\n function InterceptorsStorage() {\n this.typeToInterceptorsMap_ = new Map();\n this.enabled_ = false;\n }\n var _proto = InterceptorsStorage.prototype;\n _proto.getIsEnabled = function getIsEnabled() {\n return this.enabled_;\n };\n _proto.enable = function enable() {\n this.enabled_ = true;\n };\n _proto.disable = function disable() {\n this.enabled_ = false;\n };\n _proto.reset = function reset() {\n this.typeToInterceptorsMap_ = new Map();\n this.enabled_ = false;\n };\n _proto.addInterceptor = function addInterceptor(type, interceptor) {\n if (!this.typeToInterceptorsMap_.has(type)) {\n this.typeToInterceptorsMap_.set(type, new Set());\n }\n var interceptorsSet = this.typeToInterceptorsMap_.get(type);\n if (interceptorsSet.has(interceptor)) {\n \/\/ already have this interceptor\n return false;\n }\n interceptorsSet.add(interceptor);\n return true;\n };\n _proto.removeInterceptor = function removeInterceptor(type, interceptor) {\n var interceptorsSet = this.typeToInterceptorsMap_.get(type);\n if (interceptorsSet && interceptorsSet.has(interceptor)) {\n interceptorsSet.delete(interceptor);\n return true;\n }\n return false;\n };\n _proto.clearInterceptorsByType = function clearInterceptorsByType(type) {\n var interceptorsSet = this.typeToInterceptorsMap_.get(type);\n if (!interceptorsSet) {\n return false;\n }\n this.typeToInterceptorsMap_.delete(type);\n this.typeToInterceptorsMap_.set(type, new Set());\n return true;\n };\n _proto.clear = function clear() {\n if (!this.typeToInterceptorsMap_.size) {\n return false;\n }\n this.typeToInterceptorsMap_ = new Map();\n return true;\n };\n _proto.getForType = function getForType(type) {\n return this.typeToInterceptorsMap_.get(type) || new Set();\n };\n _proto.execute = function execute(type, payload) {\n var interceptors = this.getForType(type);\n for (var _iterator = _createForOfIteratorHelperLoose(interceptors), _step; !(_step = _iterator()).done;) {\n var interceptor = _step.value;\n try {\n payload = interceptor(payload);\n } catch (e) {\/\/ignore\n }\n }\n return payload;\n };\n return InterceptorsStorage;\n }();\n var interceptors = InterceptorsStorage;\n\n var RetryManager = \/*#__PURE__*\/function () {\n function RetryManager() {\n this.maxAttempts_ = 1;\n this.delayFactor_ = 0.1;\n this.fuzzFactor_ = 0.1;\n this.initialDelay_ = 1000;\n this.enabled_ = false;\n }\n var _proto = RetryManager.prototype;\n _proto.getIsEnabled = function getIsEnabled() {\n return this.enabled_;\n };\n _proto.enable = function enable() {\n this.enabled_ = true;\n };\n _proto.disable = function disable() {\n this.enabled_ = false;\n };\n _proto.reset = function reset() {\n this.maxAttempts_ = 1;\n this.delayFactor_ = 0.1;\n this.fuzzFactor_ = 0.1;\n this.initialDelay_ = 1000;\n this.enabled_ = false;\n };\n _proto.getMaxAttempts = function getMaxAttempts() {\n return this.maxAttempts_;\n };\n _proto.setMaxAttempts = function setMaxAttempts(maxAttempts) {\n this.maxAttempts_ = maxAttempts;\n };\n _proto.getDelayFactor = function getDelayFactor() {\n return this.delayFactor_;\n };\n _proto.setDelayFactor = function setDelayFactor(delayFactor) {\n this.delayFactor_ = delayFactor;\n };\n _proto.getFuzzFactor = function getFuzzFactor() {\n return this.fuzzFactor_;\n };\n _proto.setFuzzFactor = function setFuzzFactor(fuzzFactor) {\n this.fuzzFactor_ = fuzzFactor;\n };\n _proto.getInitialDelay = function getInitialDelay() {\n return this.initialDelay_;\n };\n _proto.setInitialDelay = function setInitialDelay(initialDelay) {\n this.initialDelay_ = initialDelay;\n };\n _proto.createRetry = function createRetry(_temp) {\n var _ref = _temp === void 0 ? {} : _temp,\n maxAttempts = _ref.maxAttempts,\n delayFactor = _ref.delayFactor,\n fuzzFactor = _ref.fuzzFactor,\n initialDelay = _ref.initialDelay;\n return new Retry({\n maxAttempts: maxAttempts || this.maxAttempts_,\n delayFactor: delayFactor || this.delayFactor_,\n fuzzFactor: fuzzFactor || this.fuzzFactor_,\n initialDelay: initialDelay || this.initialDelay_\n });\n };\n return RetryManager;\n }();\n var Retry = \/*#__PURE__*\/function () {\n function Retry(options) {\n this.maxAttempts_ = options.maxAttempts;\n this.delayFactor_ = options.delayFactor;\n this.fuzzFactor_ = options.fuzzFactor;\n this.currentDelay_ = options.initialDelay;\n this.currentAttempt_ = 1;\n }\n var _proto2 = Retry.prototype;\n _proto2.moveToNextAttempt = function moveToNextAttempt() {\n this.currentAttempt_++;\n var delayDelta = this.currentDelay_ * this.delayFactor_;\n this.currentDelay_ = this.currentDelay_ + delayDelta;\n };\n _proto2.shouldRetry = function shouldRetry() {\n return this.currentAttempt_ < this.maxAttempts_;\n };\n _proto2.getCurrentDelay = function getCurrentDelay() {\n return this.currentDelay_;\n };\n _proto2.getCurrentMinPossibleDelay = function getCurrentMinPossibleDelay() {\n return (1 - this.fuzzFactor_) * this.currentDelay_;\n };\n _proto2.getCurrentMaxPossibleDelay = function getCurrentMaxPossibleDelay() {\n return (1 + this.fuzzFactor_) * this.currentDelay_;\n }\n \/**\n * For example fuzzFactor is 0.1\n * This means \u00b110% deviation\n * So if we have delay as 1000\n * This function can generate any value from 900 to 1100\n *\/;\n _proto2.getCurrentFuzzedDelay = function getCurrentFuzzedDelay() {\n var lowValue = this.getCurrentMinPossibleDelay();\n var highValue = this.getCurrentMaxPossibleDelay();\n return lowValue + Math.random() * (highValue - lowValue);\n };\n return Retry;\n }();\n var retry = RetryManager;\n\n var httpResponseHandler = function httpResponseHandler(callback, decodeResponseBody) {\n if (decodeResponseBody === void 0) {\n decodeResponseBody = false;\n }\n return function (err, response, responseBody) {\n \/\/ if the XHR failed, return that error\n if (err) {\n callback(err);\n return;\n } \/\/ if the HTTP status code is 4xx or 5xx, the request also failed\n\n if (response.statusCode >= 400 && response.statusCode <= 599) {\n var cause = responseBody;\n if (decodeResponseBody) {\n if (window_1.TextDecoder) {\n var charset = getCharset(response.headers && response.headers['content-type']);\n try {\n cause = new TextDecoder(charset).decode(responseBody);\n } catch (e) {}\n } else {\n cause = String.fromCharCode.apply(null, new Uint8Array(responseBody));\n }\n }\n callback({\n cause: cause\n });\n return;\n } \/\/ otherwise, request succeeded\n\n callback(null, responseBody);\n };\n };\n function getCharset(contentTypeHeader) {\n if (contentTypeHeader === void 0) {\n contentTypeHeader = '';\n }\n return contentTypeHeader.toLowerCase().split(';').reduce(function (charset, contentType) {\n var _contentType$split = contentType.split('='),\n type = _contentType$split[0],\n value = _contentType$split[1];\n if (type.trim() === 'charset') {\n return value.trim();\n }\n return charset;\n }, 'utf-8');\n }\n var httpHandler = httpResponseHandler;\n\n createXHR.httpHandler = httpHandler;\n createXHR.requestInterceptorsStorage = new interceptors();\n createXHR.responseInterceptorsStorage = new interceptors();\n createXHR.retryManager = new retry();\n \/**\n * @license\n * slighly modified parse-headers 2.0.2 \n * Copyright (c) 2014 David Bj\u00f6rklund\n * Available under the MIT license\n * \n *\/\n\n var parseHeaders = function parseHeaders(headers) {\n var result = {};\n if (!headers) {\n return result;\n }\n headers.trim().split('\\n').forEach(function (row) {\n var index = row.indexOf(':');\n var key = row.slice(0, index).trim().toLowerCase();\n var value = row.slice(index + 1).trim();\n if (typeof result[key] === 'undefined') {\n result[key] = value;\n } else if (Array.isArray(result[key])) {\n result[key].push(value);\n } else {\n result[key] = [result[key], value];\n }\n });\n return result;\n };\n var lib = createXHR; \/\/ Allow use of default import syntax in TypeScript\n\n var default_1 = createXHR;\n createXHR.XMLHttpRequest = window_1.XMLHttpRequest || noop$1;\n createXHR.XDomainRequest = \"withCredentials\" in new createXHR.XMLHttpRequest() ? createXHR.XMLHttpRequest : window_1.XDomainRequest;\n forEachArray([\"get\", \"put\", \"post\", \"patch\", \"head\", \"delete\"], function (method) {\n createXHR[method === \"delete\" ? \"del\" : method] = function (uri, options, callback) {\n options = initParams(uri, options, callback);\n options.method = method.toUpperCase();\n return _createXHR(options);\n };\n });\n function forEachArray(array, iterator) {\n for (var i = 0; i < array.length; i++) {\n iterator(array[i]);\n }\n }\n function isEmpty(obj) {\n for (var i in obj) {\n if (obj.hasOwnProperty(i)) return false;\n }\n return true;\n }\n function initParams(uri, options, callback) {\n var params = uri;\n if (isFunction_1(options)) {\n callback = options;\n if (typeof uri === \"string\") {\n params = {\n uri: uri\n };\n }\n } else {\n params = _extends_1({}, options, {\n uri: uri\n });\n }\n params.callback = callback;\n return params;\n }\n function createXHR(uri, options, callback) {\n options = initParams(uri, options, callback);\n return _createXHR(options);\n }\n function _createXHR(options) {\n if (typeof options.callback === \"undefined\") {\n throw new Error(\"callback argument missing\");\n } \/\/ call all registered request interceptors for a given request type:\n\n if (options.requestType && createXHR.requestInterceptorsStorage.getIsEnabled()) {\n var requestInterceptorPayload = {\n uri: options.uri || options.url,\n headers: options.headers || {},\n body: options.body,\n metadata: options.metadata || {},\n retry: options.retry,\n timeout: options.timeout\n };\n var updatedPayload = createXHR.requestInterceptorsStorage.execute(options.requestType, requestInterceptorPayload);\n options.uri = updatedPayload.uri;\n options.headers = updatedPayload.headers;\n options.body = updatedPayload.body;\n options.metadata = updatedPayload.metadata;\n options.retry = updatedPayload.retry;\n options.timeout = updatedPayload.timeout;\n }\n var called = false;\n var callback = function cbOnce(err, response, body) {\n if (!called) {\n called = true;\n options.callback(err, response, body);\n }\n };\n function readystatechange() {\n \/\/ do not call load 2 times when response interceptors are enabled\n \/\/ why do we even need this 2nd load?\n if (xhr.readyState === 4 && !createXHR.responseInterceptorsStorage.getIsEnabled()) {\n setTimeout(loadFunc, 0);\n }\n }\n function getBody() {\n \/\/ Chrome with requestType=blob throws errors arround when even testing access to responseText\n var body = undefined;\n if (xhr.response) {\n body = xhr.response;\n } else {\n body = xhr.responseText || getXml(xhr);\n }\n if (isJson) {\n try {\n body = JSON.parse(body);\n } catch (e) {}\n }\n return body;\n }\n function errorFunc(evt) {\n clearTimeout(timeoutTimer);\n clearTimeout(options.retryTimeout);\n if (!(evt instanceof Error)) {\n evt = new Error(\"\" + (evt || \"Unknown XMLHttpRequest Error\"));\n }\n evt.statusCode = 0; \/\/ we would like to retry on error:\n\n if (!aborted && createXHR.retryManager.getIsEnabled() && options.retry && options.retry.shouldRetry()) {\n options.retryTimeout = setTimeout(function () {\n options.retry.moveToNextAttempt(); \/\/ we want to re-use the same options and the same xhr object:\n\n options.xhr = xhr;\n _createXHR(options);\n }, options.retry.getCurrentFuzzedDelay());\n return;\n } \/\/ call all registered response interceptors for a given request type:\n\n if (options.requestType && createXHR.responseInterceptorsStorage.getIsEnabled()) {\n var responseInterceptorPayload = {\n headers: failureResponse.headers || {},\n body: failureResponse.body,\n responseUrl: xhr.responseURL,\n responseType: xhr.responseType\n };\n var _updatedPayload = createXHR.responseInterceptorsStorage.execute(options.requestType, responseInterceptorPayload);\n failureResponse.body = _updatedPayload.body;\n failureResponse.headers = _updatedPayload.headers;\n }\n return callback(evt, failureResponse);\n } \/\/ will load the data & process the response in a special response object\n\n function loadFunc() {\n if (aborted) return;\n var status;\n clearTimeout(timeoutTimer);\n clearTimeout(options.retryTimeout);\n if (options.useXDR && xhr.status === undefined) {\n \/\/IE8 CORS GET successful response doesn't have a status field, but body is fine\n status = 200;\n } else {\n status = xhr.status === 1223 ? 204 : xhr.status;\n }\n var response = failureResponse;\n var err = null;\n if (status !== 0) {\n response = {\n body: getBody(),\n statusCode: status,\n method: method,\n headers: {},\n url: uri,\n rawRequest: xhr\n };\n if (xhr.getAllResponseHeaders) {\n \/\/remember xhr can in fact be XDR for CORS in IE\n response.headers = parseHeaders(xhr.getAllResponseHeaders());\n }\n } else {\n err = new Error(\"Internal XMLHttpRequest Error\");\n } \/\/ call all registered response interceptors for a given request type:\n\n if (options.requestType && createXHR.responseInterceptorsStorage.getIsEnabled()) {\n var responseInterceptorPayload = {\n headers: response.headers || {},\n body: response.body,\n responseUrl: xhr.responseURL,\n responseType: xhr.responseType\n };\n var _updatedPayload2 = createXHR.responseInterceptorsStorage.execute(options.requestType, responseInterceptorPayload);\n response.body = _updatedPayload2.body;\n response.headers = _updatedPayload2.headers;\n }\n return callback(err, response, response.body);\n }\n var xhr = options.xhr || null;\n if (!xhr) {\n if (options.cors || options.useXDR) {\n xhr = new createXHR.XDomainRequest();\n } else {\n xhr = new createXHR.XMLHttpRequest();\n }\n }\n var key;\n var aborted;\n var uri = xhr.url = options.uri || options.url;\n var method = xhr.method = options.method || \"GET\";\n var body = options.body || options.data;\n var headers = xhr.headers = options.headers || {};\n var sync = !!options.sync;\n var isJson = false;\n var timeoutTimer;\n var failureResponse = {\n body: undefined,\n headers: {},\n statusCode: 0,\n method: method,\n url: uri,\n rawRequest: xhr\n };\n if (\"json\" in options && options.json !== false) {\n isJson = true;\n headers[\"accept\"] || headers[\"Accept\"] || (headers[\"Accept\"] = \"application\/json\"); \/\/Don't override existing accept header declared by user\n\n if (method !== \"GET\" && method !== \"HEAD\") {\n headers[\"content-type\"] || headers[\"Content-Type\"] || (headers[\"Content-Type\"] = \"application\/json\"); \/\/Don't override existing accept header declared by user\n\n body = JSON.stringify(options.json === true ? body : options.json);\n }\n }\n xhr.onreadystatechange = readystatechange;\n xhr.onload = loadFunc;\n xhr.onerror = errorFunc; \/\/ IE9 must have onprogress be set to a unique function.\n\n xhr.onprogress = function () {\/\/ IE must die\n };\n xhr.onabort = function () {\n aborted = true;\n clearTimeout(options.retryTimeout);\n };\n xhr.ontimeout = errorFunc;\n xhr.open(method, uri, !sync, options.username, options.password); \/\/has to be after open\n\n if (!sync) {\n xhr.withCredentials = !!options.withCredentials;\n } \/\/ Cannot set timeout with sync request\n \/\/ not setting timeout on the xhr object, because of old webkits etc. not handling that correctly\n \/\/ both npm's request and jquery 1.x use this kind of timeout, so this is being consistent\n\n if (!sync && options.timeout > 0) {\n timeoutTimer = setTimeout(function () {\n if (aborted) return;\n aborted = true; \/\/IE9 may still call readystatechange\n\n xhr.abort(\"timeout\");\n var e = new Error(\"XMLHttpRequest timeout\");\n e.code = \"ETIMEDOUT\";\n errorFunc(e);\n }, options.timeout);\n }\n if (xhr.setRequestHeader) {\n for (key in headers) {\n if (headers.hasOwnProperty(key)) {\n xhr.setRequestHeader(key, headers[key]);\n }\n }\n } else if (options.headers && !isEmpty(options.headers)) {\n throw new Error(\"Headers cannot be set on an XDomainRequest object\");\n }\n if (\"responseType\" in options) {\n xhr.responseType = options.responseType;\n }\n if (\"beforeSend\" in options && typeof options.beforeSend === \"function\") {\n options.beforeSend(xhr);\n } \/\/ Microsoft Edge browser sends \"undefined\" when send is called with undefined value.\n \/\/ XMLHttpRequest spec says to pass null as body to indicate no body\n \/\/ See https:\/\/github.com\/naugtur\/xhr\/issues\/100.\n\n xhr.send(body || null);\n return xhr;\n }\n function getXml(xhr) {\n \/\/ xhr.responseXML will throw Exception \"InvalidStateError\" or \"DOMException\"\n \/\/ See https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/XMLHttpRequest\/responseXML.\n try {\n if (xhr.responseType === \"document\") {\n return xhr.responseXML;\n }\n var firefoxBugTakenEffect = xhr.responseXML && xhr.responseXML.documentElement.nodeName === \"parsererror\";\n if (xhr.responseType === \"\" && !firefoxBugTakenEffect) {\n return xhr.responseXML;\n }\n } catch (e) {}\n return null;\n }\n function noop$1() {}\n lib.default = default_1;\n\n \/**\n * @file text-track.js\n *\/\n\n \/** @import Tech from '..\/tech\/tech' *\/\n\n \/**\n * Takes a webvtt file contents and parses it into cues\n *\n * @param {string} srcContent\n * webVTT file contents\n *\n * @param {TextTrack} track\n * TextTrack to add cues to. Cues come from the srcContent.\n *\n * @private\n *\/\n const parseCues = function (srcContent, track) {\n const parser = new window.WebVTT.Parser(window, window.vttjs, window.WebVTT.StringDecoder());\n const errors = [];\n parser.oncue = function (cue) {\n track.addCue(cue);\n };\n parser.onparsingerror = function (error) {\n errors.push(error);\n };\n parser.onflush = function () {\n track.trigger({\n type: 'loadeddata',\n target: track\n });\n };\n parser.parse(srcContent);\n if (errors.length > 0) {\n if (window.console && window.console.groupCollapsed) {\n window.console.groupCollapsed(`Text Track parsing errors for ${track.src}`);\n }\n errors.forEach(error => log$1.error(error));\n if (window.console && window.console.groupEnd) {\n window.console.groupEnd();\n }\n }\n parser.flush();\n };\n\n \/**\n * Load a `TextTrack` from a specified url.\n *\n * @param {string} src\n * Url to load track from.\n *\n * @param {TextTrack} track\n * Track to add cues to. Comes from the content at the end of `url`.\n *\n * @private\n *\/\n const loadTrack = function (src, track) {\n const opts = {\n uri: src\n };\n const crossOrigin = isCrossOrigin(src);\n if (crossOrigin) {\n opts.cors = crossOrigin;\n }\n const withCredentials = track.tech_.crossOrigin() === 'use-credentials';\n if (withCredentials) {\n opts.withCredentials = withCredentials;\n }\n lib(opts, bind_(this, function (err, response, responseBody) {\n if (err) {\n return log$1.error(err, response);\n }\n track.loaded_ = true;\n\n \/\/ Make sure that vttjs has loaded, otherwise, wait till it finished loading\n \/\/ NOTE: this is only used for the alt\/video.novtt.js build\n if (typeof window.WebVTT !== 'function') {\n if (track.tech_) {\n \/\/ to prevent use before define eslint error, we define loadHandler\n \/\/ as a let here\n track.tech_.any(['vttjsloaded', 'vttjserror'], event => {\n if (event.type === 'vttjserror') {\n log$1.error(`vttjs failed to load, stopping trying to process ${track.src}`);\n return;\n }\n return parseCues(responseBody, track);\n });\n }\n } else {\n parseCues(responseBody, track);\n }\n }));\n };\n\n \/**\n * A representation of a single `TextTrack`.\n *\n * @see [Spec]{@link https:\/\/html.spec.whatwg.org\/multipage\/embedded-content.html#texttrack}\n * @extends Track\n *\/\n class TextTrack extends Track {\n \/**\n * Create an instance of this class.\n *\n * @param {Object} options={}\n * Object of option names and values\n *\n * @param {Tech} options.tech\n * A reference to the tech that owns this TextTrack.\n *\n * @param {TextTrack~Kind} [options.kind='subtitles']\n * A valid text track kind.\n *\n * @param {TextTrack~Mode} [options.mode='disabled']\n * A valid text track mode.\n *\n * @param {string} [options.id='vjs_track_' + Guid.newGUID()]\n * A unique id for this TextTrack.\n *\n * @param {string} [options.label='']\n * The menu label for this track.\n *\n * @param {string} [options.language='']\n * A valid two character language code.\n *\n * @param {string} [options.srclang='']\n * A valid two character language code. An alternative, but deprioritized\n * version of `options.language`\n *\n * @param {string} [options.src]\n * A url to TextTrack cues.\n *\n * @param {boolean} [options.default]\n * If this track should default to on or off.\n *\/\n constructor(options = {}) {\n if (!options.tech) {\n throw new Error('A tech was not provided.');\n }\n const settings = merge$2(options, {\n kind: TextTrackKind[options.kind] || 'subtitles',\n language: options.language || options.srclang || ''\n });\n let mode = TextTrackMode[settings.mode] || 'disabled';\n const default_ = settings.default;\n if (settings.kind === 'metadata' || settings.kind === 'chapters') {\n mode = 'hidden';\n }\n super(settings);\n this.tech_ = settings.tech;\n this.cues_ = [];\n this.activeCues_ = [];\n this.preload_ = this.tech_.preloadTextTracks !== false;\n const cues = new TextTrackCueList(this.cues_);\n const activeCues = new TextTrackCueList(this.activeCues_);\n let changed = false;\n this.timeupdateHandler = bind_(this, function (event = {}) {\n if (this.tech_.isDisposed()) {\n return;\n }\n if (!this.tech_.isReady_) {\n if (event.type !== 'timeupdate') {\n this.rvf_ = this.tech_.requestVideoFrameCallback(this.timeupdateHandler);\n }\n return;\n }\n\n \/\/ Accessing this.activeCues for the side-effects of updating itself\n \/\/ due to its nature as a getter function. Do not remove or cues will\n \/\/ stop updating!\n \/\/ Use the setter to prevent deletion from uglify (pure_getters rule)\n this.activeCues = this.activeCues;\n if (changed) {\n this.trigger('cuechange');\n changed = false;\n }\n if (event.type !== 'timeupdate') {\n this.rvf_ = this.tech_.requestVideoFrameCallback(this.timeupdateHandler);\n }\n });\n const disposeHandler = () => {\n this.stopTracking();\n };\n this.tech_.one('dispose', disposeHandler);\n if (mode !== 'disabled') {\n this.startTracking();\n }\n Object.defineProperties(this, {\n \/**\n * @memberof TextTrack\n * @member {boolean} default\n * If this track was set to be on or off by default. Cannot be changed after\n * creation.\n * @instance\n *\n * @readonly\n *\/\n default: {\n get() {\n return default_;\n },\n set() {}\n },\n \/**\n * @memberof TextTrack\n * @member {string} mode\n * Set the mode of this TextTrack to a valid {@link TextTrack~Mode}. Will\n * not be set if setting to an invalid mode.\n * @instance\n *\n * @fires TextTrack#modechange\n *\/\n mode: {\n get() {\n return mode;\n },\n set(newMode) {\n if (!TextTrackMode[newMode]) {\n return;\n }\n if (mode === newMode) {\n return;\n }\n mode = newMode;\n if (!this.preload_ && mode !== 'disabled' && this.cues.length === 0) {\n \/\/ On-demand load.\n loadTrack(this.src, this);\n }\n this.stopTracking();\n if (mode !== 'disabled') {\n this.startTracking();\n }\n \/**\n * An event that fires when mode changes on this track. This allows\n * the TextTrackList that holds this track to act accordingly.\n *\n * > Note: This is not part of the spec!\n *\n * @event TextTrack#modechange\n * @type {Event}\n *\/\n this.trigger('modechange');\n }\n },\n \/**\n * @memberof TextTrack\n * @member {TextTrackCueList} cues\n * The text track cue list for this TextTrack.\n * @instance\n *\/\n cues: {\n get() {\n if (!this.loaded_) {\n return null;\n }\n return cues;\n },\n set() {}\n },\n \/**\n * @memberof TextTrack\n * @member {TextTrackCueList} activeCues\n * The list text track cues that are currently active for this TextTrack.\n * @instance\n *\/\n activeCues: {\n get() {\n if (!this.loaded_) {\n return null;\n }\n\n \/\/ nothing to do\n if (this.cues.length === 0) {\n return activeCues;\n }\n const ct = this.tech_.currentTime();\n const active = [];\n for (let i = 0, l = this.cues.length; i < l; i++) {\n const cue = this.cues[i];\n if (cue.startTime <= ct && cue.endTime >= ct) {\n active.push(cue);\n }\n }\n changed = false;\n if (active.length !== this.activeCues_.length) {\n changed = true;\n } else {\n for (let i = 0; i < active.length; i++) {\n if (this.activeCues_.indexOf(active[i]) === -1) {\n changed = true;\n }\n }\n }\n this.activeCues_ = active;\n activeCues.setCues_(this.activeCues_);\n return activeCues;\n },\n \/\/ \/!\\ Keep this setter empty (see the timeupdate handler above)\n set() {}\n }\n });\n if (settings.src) {\n this.src = settings.src;\n if (!this.preload_) {\n \/\/ Tracks will load on-demand.\n \/\/ Act like we're loaded for other purposes.\n this.loaded_ = true;\n }\n if (this.preload_ || settings.kind !== 'subtitles' && settings.kind !== 'captions') {\n loadTrack(this.src, this);\n }\n } else {\n this.loaded_ = true;\n }\n }\n startTracking() {\n \/\/ More precise cues based on requestVideoFrameCallback with a requestAnimationFram fallback\n this.rvf_ = this.tech_.requestVideoFrameCallback(this.timeupdateHandler);\n \/\/ Also listen to timeupdate in case rVFC\/rAF stops (window in background, audio in video el)\n this.tech_.on('timeupdate', this.timeupdateHandler);\n }\n stopTracking() {\n if (this.rvf_) {\n this.tech_.cancelVideoFrameCallback(this.rvf_);\n this.rvf_ = undefined;\n }\n this.tech_.off('timeupdate', this.timeupdateHandler);\n }\n\n \/**\n * Add a cue to the internal list of cues.\n *\n * @param {TextTrack~Cue} cue\n * The cue to add to our internal list\n *\/\n addCue(originalCue) {\n let cue = originalCue;\n\n \/\/ Testing if the cue is a VTTCue in a way that survives minification\n if (!('getCueAsHTML' in cue)) {\n cue = new window.vttjs.VTTCue(originalCue.startTime, originalCue.endTime, originalCue.text);\n for (const prop in originalCue) {\n if (!(prop in cue)) {\n cue[prop] = originalCue[prop];\n }\n }\n\n \/\/ make sure that `id` is copied over\n cue.id = originalCue.id;\n cue.originalCue_ = originalCue;\n }\n const tracks = this.tech_.textTracks();\n for (let i = 0; i < tracks.length; i++) {\n if (tracks[i] !== this) {\n tracks[i].removeCue(cue);\n }\n }\n this.cues_.push(cue);\n this.cues.setCues_(this.cues_);\n }\n\n \/**\n * Remove a cue from our internal list\n *\n * @param {TextTrack~Cue} removeCue\n * The cue to remove from our internal list\n *\/\n removeCue(removeCue) {\n let i = this.cues_.length;\n while (i--) {\n const cue = this.cues_[i];\n if (cue === removeCue || cue.originalCue_ && cue.originalCue_ === removeCue) {\n this.cues_.splice(i, 1);\n this.cues.setCues_(this.cues_);\n break;\n }\n }\n }\n }\n\n \/**\n * cuechange - One or more cues in the track have become active or stopped being active.\n *\n * @protected\n *\/\n TextTrack.prototype.allowedEvents_ = {\n cuechange: 'cuechange'\n };\n\n \/**\n * A representation of a single `AudioTrack`. If it is part of an {@link AudioTrackList}\n * only one `AudioTrack` in the list will be enabled at a time.\n *\n * @see [Spec]{@link https:\/\/html.spec.whatwg.org\/multipage\/embedded-content.html#audiotrack}\n * @extends Track\n *\/\n class AudioTrack extends Track {\n \/**\n * Create an instance of this class.\n *\n * @param {Object} [options={}]\n * Object of option names and values\n *\n * @param {AudioTrack~Kind} [options.kind='']\n * A valid audio track kind\n *\n * @param {string} [options.id='vjs_track_' + Guid.newGUID()]\n * A unique id for this AudioTrack.\n *\n * @param {string} [options.label='']\n * The menu label for this track.\n *\n * @param {string} [options.language='']\n * A valid two character language code.\n *\n * @param {boolean} [options.enabled]\n * If this track is the one that is currently playing. If this track is part of\n * an {@link AudioTrackList}, only one {@link AudioTrack} will be enabled.\n *\/\n constructor(options = {}) {\n const settings = merge$2(options, {\n kind: AudioTrackKind[options.kind] || ''\n });\n super(settings);\n let enabled = false;\n\n \/**\n * @memberof AudioTrack\n * @member {boolean} enabled\n * If this `AudioTrack` is enabled or not. When setting this will\n * fire {@link AudioTrack#enabledchange} if the state of enabled is changed.\n * @instance\n *\n * @fires VideoTrack#selectedchange\n *\/\n Object.defineProperty(this, 'enabled', {\n get() {\n return enabled;\n },\n set(newEnabled) {\n \/\/ an invalid or unchanged value\n if (typeof newEnabled !== 'boolean' || newEnabled === enabled) {\n return;\n }\n enabled = newEnabled;\n\n \/**\n * An event that fires when enabled changes on this track. This allows\n * the AudioTrackList that holds this track to act accordingly.\n *\n * > Note: This is not part of the spec! Native tracks will do\n * this internally without an event.\n *\n * @event AudioTrack#enabledchange\n * @type {Event}\n *\/\n this.trigger('enabledchange');\n }\n });\n\n \/\/ if the user sets this track to selected then\n \/\/ set selected to that true value otherwise\n \/\/ we keep it false\n if (settings.enabled) {\n this.enabled = settings.enabled;\n }\n this.loaded_ = true;\n }\n }\n\n \/**\n * A representation of a single `VideoTrack`.\n *\n * @see [Spec]{@link https:\/\/html.spec.whatwg.org\/multipage\/embedded-content.html#videotrack}\n * @extends Track\n *\/\n class VideoTrack extends Track {\n \/**\n * Create an instance of this class.\n *\n * @param {Object} [options={}]\n * Object of option names and values\n *\n * @param {string} [options.kind='']\n * A valid {@link VideoTrack~Kind}\n *\n * @param {string} [options.id='vjs_track_' + Guid.newGUID()]\n * A unique id for this AudioTrack.\n *\n * @param {string} [options.label='']\n * The menu label for this track.\n *\n * @param {string} [options.language='']\n * A valid two character language code.\n *\n * @param {boolean} [options.selected]\n * If this track is the one that is currently playing.\n *\/\n constructor(options = {}) {\n const settings = merge$2(options, {\n kind: VideoTrackKind[options.kind] || ''\n });\n super(settings);\n let selected = false;\n\n \/**\n * @memberof VideoTrack\n * @member {boolean} selected\n * If this `VideoTrack` is selected or not. When setting this will\n * fire {@link VideoTrack#selectedchange} if the state of selected changed.\n * @instance\n *\n * @fires VideoTrack#selectedchange\n *\/\n Object.defineProperty(this, 'selected', {\n get() {\n return selected;\n },\n set(newSelected) {\n \/\/ an invalid or unchanged value\n if (typeof newSelected !== 'boolean' || newSelected === selected) {\n return;\n }\n selected = newSelected;\n\n \/**\n * An event that fires when selected changes on this track. This allows\n * the VideoTrackList that holds this track to act accordingly.\n *\n * > Note: This is not part of the spec! Native tracks will do\n * this internally without an event.\n *\n * @event VideoTrack#selectedchange\n * @type {Event}\n *\/\n this.trigger('selectedchange');\n }\n });\n\n \/\/ if the user sets this track to selected then\n \/\/ set selected to that true value otherwise\n \/\/ we keep it false\n if (settings.selected) {\n this.selected = settings.selected;\n }\n }\n }\n\n \/**\n * @file html-track-element.js\n *\/\n\n \/** @import Tech from '..\/tech\/tech' *\/\n\n \/**\n * A single track represented in the DOM.\n *\n * @see [Spec]{@link https:\/\/html.spec.whatwg.org\/multipage\/embedded-content.html#htmltrackelement}\n * @extends EventTarget\n *\/\n class HTMLTrackElement extends EventTarget$2 {\n \/**\n * Create an instance of this class.\n *\n * @param {Object} options={}\n * Object of option names and values\n *\n * @param {Tech} options.tech\n * A reference to the tech that owns this HTMLTrackElement.\n *\n * @param {TextTrack~Kind} [options.kind='subtitles']\n * A valid text track kind.\n *\n * @param {TextTrack~Mode} [options.mode='disabled']\n * A valid text track mode.\n *\n * @param {string} [options.id='vjs_track_' + Guid.newGUID()]\n * A unique id for this TextTrack.\n *\n * @param {string} [options.label='']\n * The menu label for this track.\n *\n * @param {string} [options.language='']\n * A valid two character language code.\n *\n * @param {string} [options.srclang='']\n * A valid two character language code. An alternative, but deprioritized\n * version of `options.language`\n *\n * @param {string} [options.src]\n * A url to TextTrack cues.\n *\n * @param {boolean} [options.default]\n * If this track should default to on or off.\n *\/\n constructor(options = {}) {\n super();\n let readyState;\n const track = new TextTrack(options);\n this.kind = track.kind;\n this.src = track.src;\n this.srclang = track.language;\n this.label = track.label;\n this.default = track.default;\n Object.defineProperties(this, {\n \/**\n * @memberof HTMLTrackElement\n * @member {HTMLTrackElement~ReadyState} readyState\n * The current ready state of the track element.\n * @instance\n *\/\n readyState: {\n get() {\n return readyState;\n }\n },\n \/**\n * @memberof HTMLTrackElement\n * @member {TextTrack} track\n * The underlying TextTrack object.\n * @instance\n *\n *\/\n track: {\n get() {\n return track;\n }\n }\n });\n readyState = HTMLTrackElement.NONE;\n\n \/**\n * @listens TextTrack#loadeddata\n * @fires HTMLTrackElement#load\n *\/\n track.addEventListener('loadeddata', () => {\n readyState = HTMLTrackElement.LOADED;\n this.trigger({\n type: 'load',\n target: this\n });\n });\n }\n }\n\n \/**\n * @protected\n *\/\n HTMLTrackElement.prototype.allowedEvents_ = {\n load: 'load'\n };\n\n \/**\n * The text track not loaded state.\n *\n * @type {number}\n * @static\n *\/\n HTMLTrackElement.NONE = 0;\n\n \/**\n * The text track loading state.\n *\n * @type {number}\n * @static\n *\/\n HTMLTrackElement.LOADING = 1;\n\n \/**\n * The text track loaded state.\n *\n * @type {number}\n * @static\n *\/\n HTMLTrackElement.LOADED = 2;\n\n \/**\n * The text track failed to load state.\n *\n * @type {number}\n * @static\n *\/\n HTMLTrackElement.ERROR = 3;\n\n \/*\n * This file contains all track properties that are used in\n * player.js, tech.js, html5.js and possibly other techs in the future.\n *\/\n\n const NORMAL = {\n audio: {\n ListClass: AudioTrackList,\n TrackClass: AudioTrack,\n capitalName: 'Audio'\n },\n video: {\n ListClass: VideoTrackList,\n TrackClass: VideoTrack,\n capitalName: 'Video'\n },\n text: {\n ListClass: TextTrackList,\n TrackClass: TextTrack,\n capitalName: 'Text'\n }\n };\n Object.keys(NORMAL).forEach(function (type) {\n NORMAL[type].getterName = `${type}Tracks`;\n NORMAL[type].privateName = `${type}Tracks_`;\n });\n const REMOTE = {\n remoteText: {\n ListClass: TextTrackList,\n TrackClass: TextTrack,\n capitalName: 'RemoteText',\n getterName: 'remoteTextTracks',\n privateName: 'remoteTextTracks_'\n },\n remoteTextEl: {\n ListClass: HtmlTrackElementList,\n TrackClass: HTMLTrackElement,\n capitalName: 'RemoteTextTrackEls',\n getterName: 'remoteTextTrackEls',\n privateName: 'remoteTextTrackEls_'\n }\n };\n const ALL = Object.assign({}, NORMAL, REMOTE);\n REMOTE.names = Object.keys(REMOTE);\n NORMAL.names = Object.keys(NORMAL);\n ALL.names = [].concat(REMOTE.names).concat(NORMAL.names);\n\n var minDoc = {};\n\n var topLevel = typeof commonjsGlobal !== 'undefined' ? commonjsGlobal : typeof window !== 'undefined' ? window : {};\n var doccy;\n if (typeof document !== 'undefined') {\n doccy = document;\n } else {\n doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'];\n if (!doccy) {\n doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'] = minDoc;\n }\n }\n var document_1 = doccy;\n\n \/**\n * Copyright 2013 vtt.js Contributors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http:\/\/www.apache.org\/licenses\/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\/\n\n \/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- *\/\n \/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: *\/\n\n var _objCreate = Object.create || function () {\n function F() {}\n return function (o) {\n if (arguments.length !== 1) {\n throw new Error('Object.create shim only accepts one parameter.');\n }\n F.prototype = o;\n return new F();\n };\n }();\n\n \/\/ Creates a new ParserError object from an errorData object. The errorData\n \/\/ object should have default code and message properties. The default message\n \/\/ property can be overriden by passing in a message parameter.\n \/\/ See ParsingError.Errors below for acceptable errors.\n function ParsingError(errorData, message) {\n this.name = \"ParsingError\";\n this.code = errorData.code;\n this.message = message || errorData.message;\n }\n ParsingError.prototype = _objCreate(Error.prototype);\n ParsingError.prototype.constructor = ParsingError;\n\n \/\/ ParsingError metadata for acceptable ParsingErrors.\n ParsingError.Errors = {\n BadSignature: {\n code: 0,\n message: \"Malformed WebVTT signature.\"\n },\n BadTimeStamp: {\n code: 1,\n message: \"Malformed time stamp.\"\n }\n };\n\n \/\/ Try to parse input as a time stamp.\n function parseTimeStamp(input) {\n function computeSeconds(h, m, s, f) {\n return (h | 0) * 3600 + (m | 0) * 60 + (s | 0) + (f | 0) \/ 1000;\n }\n var m = input.match(\/^(\\d+):(\\d{1,2})(:\\d{1,2})?\\.(\\d{3})\/);\n if (!m) {\n return null;\n }\n if (m[3]) {\n \/\/ Timestamp takes the form of [hours]:[minutes]:[seconds].[milliseconds]\n return computeSeconds(m[1], m[2], m[3].replace(\":\", \"\"), m[4]);\n } else if (m[1] > 59) {\n \/\/ Timestamp takes the form of [hours]:[minutes].[milliseconds]\n \/\/ First position is hours as it's over 59.\n return computeSeconds(m[1], m[2], 0, m[4]);\n } else {\n \/\/ Timestamp takes the form of [minutes]:[seconds].[milliseconds]\n return computeSeconds(0, m[1], m[2], m[4]);\n }\n }\n\n \/\/ A settings object holds key\/value pairs and will ignore anything but the first\n \/\/ assignment to a specific key.\n function Settings() {\n this.values = _objCreate(null);\n }\n Settings.prototype = {\n \/\/ Only accept the first assignment to any key.\n set: function (k, v) {\n if (!this.get(k) && v !== \"\") {\n this.values[k] = v;\n }\n },\n \/\/ Return the value for a key, or a default value.\n \/\/ If 'defaultKey' is passed then 'dflt' is assumed to be an object with\n \/\/ a number of possible default values as properties where 'defaultKey' is\n \/\/ the key of the property that will be chosen; otherwise it's assumed to be\n \/\/ a single value.\n get: function (k, dflt, defaultKey) {\n if (defaultKey) {\n return this.has(k) ? this.values[k] : dflt[defaultKey];\n }\n return this.has(k) ? this.values[k] : dflt;\n },\n \/\/ Check whether we have a value for a key.\n has: function (k) {\n return k in this.values;\n },\n \/\/ Accept a setting if its one of the given alternatives.\n alt: function (k, v, a) {\n for (var n = 0; n < a.length; ++n) {\n if (v === a[n]) {\n this.set(k, v);\n break;\n }\n }\n },\n \/\/ Accept a setting if its a valid (signed) integer.\n integer: function (k, v) {\n if (\/^-?\\d+$\/.test(v)) {\n \/\/ integer\n this.set(k, parseInt(v, 10));\n }\n },\n \/\/ Accept a setting if its a valid percentage.\n percent: function (k, v) {\n if (v.match(\/^([\\d]{1,3})(\\.[\\d]*)?%$\/)) {\n v = parseFloat(v);\n if (v >= 0 && v <= 100) {\n this.set(k, v);\n return true;\n }\n }\n return false;\n }\n };\n\n \/\/ Helper function to parse input into groups separated by 'groupDelim', and\n \/\/ interprete each group as a key\/value pair separated by 'keyValueDelim'.\n function parseOptions(input, callback, keyValueDelim, groupDelim) {\n var groups = groupDelim ? input.split(groupDelim) : [input];\n for (var i in groups) {\n if (typeof groups[i] !== \"string\") {\n continue;\n }\n var kv = groups[i].split(keyValueDelim);\n if (kv.length !== 2) {\n continue;\n }\n var k = kv[0].trim();\n var v = kv[1].trim();\n callback(k, v);\n }\n }\n function parseCue(input, cue, regionList) {\n \/\/ Remember the original input if we need to throw an error.\n var oInput = input;\n \/\/ 4.1 WebVTT timestamp\n function consumeTimeStamp() {\n var ts = parseTimeStamp(input);\n if (ts === null) {\n throw new ParsingError(ParsingError.Errors.BadTimeStamp, \"Malformed timestamp: \" + oInput);\n }\n \/\/ Remove time stamp from input.\n input = input.replace(\/^[^\\sa-zA-Z-]+\/, \"\");\n return ts;\n }\n\n \/\/ 4.4.2 WebVTT cue settings\n function consumeCueSettings(input, cue) {\n var settings = new Settings();\n parseOptions(input, function (k, v) {\n switch (k) {\n case \"region\":\n \/\/ Find the last region we parsed with the same region id.\n for (var i = regionList.length - 1; i >= 0; i--) {\n if (regionList[i].id === v) {\n settings.set(k, regionList[i].region);\n break;\n }\n }\n break;\n case \"vertical\":\n settings.alt(k, v, [\"rl\", \"lr\"]);\n break;\n case \"line\":\n var vals = v.split(\",\"),\n vals0 = vals[0];\n settings.integer(k, vals0);\n settings.percent(k, vals0) ? settings.set(\"snapToLines\", false) : null;\n settings.alt(k, vals0, [\"auto\"]);\n if (vals.length === 2) {\n settings.alt(\"lineAlign\", vals[1], [\"start\", \"center\", \"end\"]);\n }\n break;\n case \"position\":\n vals = v.split(\",\");\n settings.percent(k, vals[0]);\n if (vals.length === 2) {\n settings.alt(\"positionAlign\", vals[1], [\"start\", \"center\", \"end\"]);\n }\n break;\n case \"size\":\n settings.percent(k, v);\n break;\n case \"align\":\n settings.alt(k, v, [\"start\", \"center\", \"end\", \"left\", \"right\"]);\n break;\n }\n }, \/:\/, \/\\s\/);\n\n \/\/ Apply default values for any missing fields.\n cue.region = settings.get(\"region\", null);\n cue.vertical = settings.get(\"vertical\", \"\");\n try {\n cue.line = settings.get(\"line\", \"auto\");\n } catch (e) {}\n cue.lineAlign = settings.get(\"lineAlign\", \"start\");\n cue.snapToLines = settings.get(\"snapToLines\", true);\n cue.size = settings.get(\"size\", 100);\n \/\/ Safari still uses the old middle value and won't accept center\n try {\n cue.align = settings.get(\"align\", \"center\");\n } catch (e) {\n cue.align = settings.get(\"align\", \"middle\");\n }\n try {\n cue.position = settings.get(\"position\", \"auto\");\n } catch (e) {\n cue.position = settings.get(\"position\", {\n start: 0,\n left: 0,\n center: 50,\n middle: 50,\n end: 100,\n right: 100\n }, cue.align);\n }\n cue.positionAlign = settings.get(\"positionAlign\", {\n start: \"start\",\n left: \"start\",\n center: \"center\",\n middle: \"center\",\n end: \"end\",\n right: \"end\"\n }, cue.align);\n }\n function skipWhitespace() {\n input = input.replace(\/^\\s+\/, \"\");\n }\n\n \/\/ 4.1 WebVTT cue timings.\n skipWhitespace();\n cue.startTime = consumeTimeStamp(); \/\/ (1) collect cue start time\n skipWhitespace();\n if (input.substr(0, 3) !== \"-->\") {\n \/\/ (3) next characters must match \"-->\"\n throw new ParsingError(ParsingError.Errors.BadTimeStamp, \"Malformed time stamp (time stamps must be separated by '-->'): \" + oInput);\n }\n input = input.substr(3);\n skipWhitespace();\n cue.endTime = consumeTimeStamp(); \/\/ (5) collect cue end time\n\n \/\/ 4.1 WebVTT cue settings list.\n skipWhitespace();\n consumeCueSettings(input, cue);\n }\n\n \/\/ When evaluating this file as part of a Webpack bundle for server\n \/\/ side rendering, `document` is an empty object.\n var TEXTAREA_ELEMENT = document_1.createElement && document_1.createElement(\"textarea\");\n var TAG_NAME = {\n c: \"span\",\n i: \"i\",\n b: \"b\",\n u: \"u\",\n ruby: \"ruby\",\n rt: \"rt\",\n v: \"span\",\n lang: \"span\"\n };\n\n \/\/ 5.1 default text color\n \/\/ 5.2 default text background color is equivalent to text color with bg_ prefix\n var DEFAULT_COLOR_CLASS = {\n white: 'rgba(255,255,255,1)',\n lime: 'rgba(0,255,0,1)',\n cyan: 'rgba(0,255,255,1)',\n red: 'rgba(255,0,0,1)',\n yellow: 'rgba(255,255,0,1)',\n magenta: 'rgba(255,0,255,1)',\n blue: 'rgba(0,0,255,1)',\n black: 'rgba(0,0,0,1)'\n };\n var TAG_ANNOTATION = {\n v: \"title\",\n lang: \"lang\"\n };\n var NEEDS_PARENT = {\n rt: \"ruby\"\n };\n\n \/\/ Parse content into a document fragment.\n function parseContent(window, input) {\n function nextToken() {\n \/\/ Check for end-of-string.\n if (!input) {\n return null;\n }\n\n \/\/ Consume 'n' characters from the input.\n function consume(result) {\n input = input.substr(result.length);\n return result;\n }\n var m = input.match(\/^([^<]*)(<[^>]*>?)?\/);\n \/\/ If there is some text before the next tag, return it, otherwise return\n \/\/ the tag.\n return consume(m[1] ? m[1] : m[2]);\n }\n function unescape(s) {\n TEXTAREA_ELEMENT.innerHTML = s;\n s = TEXTAREA_ELEMENT.textContent;\n TEXTAREA_ELEMENT.textContent = \"\";\n return s;\n }\n function shouldAdd(current, element) {\n return !NEEDS_PARENT[element.localName] || NEEDS_PARENT[element.localName] === current.localName;\n }\n\n \/\/ Create an element for this tag.\n function createElement(type, annotation) {\n var tagName = TAG_NAME[type];\n if (!tagName) {\n return null;\n }\n var element = window.document.createElement(tagName);\n var name = TAG_ANNOTATION[type];\n if (name && annotation) {\n element[name] = annotation.trim();\n }\n return element;\n }\n var rootDiv = window.document.createElement(\"div\"),\n current = rootDiv,\n t,\n tagStack = [];\n while ((t = nextToken()) !== null) {\n if (t[0] === '<') {\n if (t[1] === \"\/\") {\n \/\/ If the closing tag matches, move back up to the parent node.\n if (tagStack.length && tagStack[tagStack.length - 1] === t.substr(2).replace(\">\", \"\")) {\n tagStack.pop();\n current = current.parentNode;\n }\n \/\/ Otherwise just ignore the end tag.\n continue;\n }\n var ts = parseTimeStamp(t.substr(1, t.length - 2));\n var node;\n if (ts) {\n \/\/ Timestamps are lead nodes as well.\n node = window.document.createProcessingInstruction(\"timestamp\", ts);\n current.appendChild(node);\n continue;\n }\n var m = t.match(\/^<([^.\\s\/0-9>]+)(\\.[^\\s\\\\>]+)?([^>\\\\]+)?(\\\\?)>?$\/);\n \/\/ If we can't parse the tag, skip to the next tag.\n if (!m) {\n continue;\n }\n \/\/ Try to construct an element, and ignore the tag if we couldn't.\n node = createElement(m[1], m[3]);\n if (!node) {\n continue;\n }\n \/\/ Determine if the tag should be added based on the context of where it\n \/\/ is placed in the cuetext.\n if (!shouldAdd(current, node)) {\n continue;\n }\n \/\/ Set the class list (as a list of classes, separated by space).\n if (m[2]) {\n var classes = m[2].split('.');\n classes.forEach(function (cl) {\n var bgColor = \/^bg_\/.test(cl);\n \/\/ slice out `bg_` if it's a background color\n var colorName = bgColor ? cl.slice(3) : cl;\n if (DEFAULT_COLOR_CLASS.hasOwnProperty(colorName)) {\n var propName = bgColor ? 'background-color' : 'color';\n var propValue = DEFAULT_COLOR_CLASS[colorName];\n node.style[propName] = propValue;\n }\n });\n node.className = classes.join(' ');\n }\n \/\/ Append the node to the current node, and enter the scope of the new\n \/\/ node.\n tagStack.push(m[1]);\n current.appendChild(node);\n current = node;\n continue;\n }\n\n \/\/ Text nodes are leaf nodes.\n current.appendChild(window.document.createTextNode(unescape(t)));\n }\n return rootDiv;\n }\n\n \/\/ This is a list of all the Unicode characters that have a strong\n \/\/ right-to-left category. What this means is that these characters are\n \/\/ written right-to-left for sure. It was generated by pulling all the strong\n \/\/ right-to-left characters out of the Unicode data table. That table can\n \/\/ found at: http:\/\/www.unicode.org\/Public\/UNIDATA\/UnicodeData.txt\n var strongRTLRanges = [[0x5be, 0x5be], [0x5c0, 0x5c0], [0x5c3, 0x5c3], [0x5c6, 0x5c6], [0x5d0, 0x5ea], [0x5f0, 0x5f4], [0x608, 0x608], [0x60b, 0x60b], [0x60d, 0x60d], [0x61b, 0x61b], [0x61e, 0x64a], [0x66d, 0x66f], [0x671, 0x6d5], [0x6e5, 0x6e6], [0x6ee, 0x6ef], [0x6fa, 0x70d], [0x70f, 0x710], [0x712, 0x72f], [0x74d, 0x7a5], [0x7b1, 0x7b1], [0x7c0, 0x7ea], [0x7f4, 0x7f5], [0x7fa, 0x7fa], [0x800, 0x815], [0x81a, 0x81a], [0x824, 0x824], [0x828, 0x828], [0x830, 0x83e], [0x840, 0x858], [0x85e, 0x85e], [0x8a0, 0x8a0], [0x8a2, 0x8ac], [0x200f, 0x200f], [0xfb1d, 0xfb1d], [0xfb1f, 0xfb28], [0xfb2a, 0xfb36], [0xfb38, 0xfb3c], [0xfb3e, 0xfb3e], [0xfb40, 0xfb41], [0xfb43, 0xfb44], [0xfb46, 0xfbc1], [0xfbd3, 0xfd3d], [0xfd50, 0xfd8f], [0xfd92, 0xfdc7], [0xfdf0, 0xfdfc], [0xfe70, 0xfe74], [0xfe76, 0xfefc], [0x10800, 0x10805], [0x10808, 0x10808], [0x1080a, 0x10835], [0x10837, 0x10838], [0x1083c, 0x1083c], [0x1083f, 0x10855], [0x10857, 0x1085f], [0x10900, 0x1091b], [0x10920, 0x10939], [0x1093f, 0x1093f], [0x10980, 0x109b7], [0x109be, 0x109bf], [0x10a00, 0x10a00], [0x10a10, 0x10a13], [0x10a15, 0x10a17], [0x10a19, 0x10a33], [0x10a40, 0x10a47], [0x10a50, 0x10a58], [0x10a60, 0x10a7f], [0x10b00, 0x10b35], [0x10b40, 0x10b55], [0x10b58, 0x10b72], [0x10b78, 0x10b7f], [0x10c00, 0x10c48], [0x1ee00, 0x1ee03], [0x1ee05, 0x1ee1f], [0x1ee21, 0x1ee22], [0x1ee24, 0x1ee24], [0x1ee27, 0x1ee27], [0x1ee29, 0x1ee32], [0x1ee34, 0x1ee37], [0x1ee39, 0x1ee39], [0x1ee3b, 0x1ee3b], [0x1ee42, 0x1ee42], [0x1ee47, 0x1ee47], [0x1ee49, 0x1ee49], [0x1ee4b, 0x1ee4b], [0x1ee4d, 0x1ee4f], [0x1ee51, 0x1ee52], [0x1ee54, 0x1ee54], [0x1ee57, 0x1ee57], [0x1ee59, 0x1ee59], [0x1ee5b, 0x1ee5b], [0x1ee5d, 0x1ee5d], [0x1ee5f, 0x1ee5f], [0x1ee61, 0x1ee62], [0x1ee64, 0x1ee64], [0x1ee67, 0x1ee6a], [0x1ee6c, 0x1ee72], [0x1ee74, 0x1ee77], [0x1ee79, 0x1ee7c], [0x1ee7e, 0x1ee7e], [0x1ee80, 0x1ee89], [0x1ee8b, 0x1ee9b], [0x1eea1, 0x1eea3], [0x1eea5, 0x1eea9], [0x1eeab, 0x1eebb], [0x10fffd, 0x10fffd]];\n function isStrongRTLChar(charCode) {\n for (var i = 0; i < strongRTLRanges.length; i++) {\n var currentRange = strongRTLRanges[i];\n if (charCode >= currentRange[0] && charCode <= currentRange[1]) {\n return true;\n }\n }\n return false;\n }\n function determineBidi(cueDiv) {\n var nodeStack = [],\n text = \"\",\n charCode;\n if (!cueDiv || !cueDiv.childNodes) {\n return \"ltr\";\n }\n function pushNodes(nodeStack, node) {\n for (var i = node.childNodes.length - 1; i >= 0; i--) {\n nodeStack.push(node.childNodes[i]);\n }\n }\n function nextTextNode(nodeStack) {\n if (!nodeStack || !nodeStack.length) {\n return null;\n }\n var node = nodeStack.pop(),\n text = node.textContent || node.innerText;\n if (text) {\n \/\/ TODO: This should match all unicode type B characters (paragraph\n \/\/ separator characters). See issue #115.\n var m = text.match(\/^.*(\\n|\\r)\/);\n if (m) {\n nodeStack.length = 0;\n return m[0];\n }\n return text;\n }\n if (node.tagName === \"ruby\") {\n return nextTextNode(nodeStack);\n }\n if (node.childNodes) {\n pushNodes(nodeStack, node);\n return nextTextNode(nodeStack);\n }\n }\n pushNodes(nodeStack, cueDiv);\n while (text = nextTextNode(nodeStack)) {\n for (var i = 0; i < text.length; i++) {\n charCode = text.charCodeAt(i);\n if (isStrongRTLChar(charCode)) {\n return \"rtl\";\n }\n }\n }\n return \"ltr\";\n }\n function computeLinePos(cue) {\n if (typeof cue.line === \"number\" && (cue.snapToLines || cue.line >= 0 && cue.line <= 100)) {\n return cue.line;\n }\n if (!cue.track || !cue.track.textTrackList || !cue.track.textTrackList.mediaElement) {\n return -1;\n }\n var track = cue.track,\n trackList = track.textTrackList,\n count = 0;\n for (var i = 0; i < trackList.length && trackList[i] !== track; i++) {\n if (trackList[i].mode === \"showing\") {\n count++;\n }\n }\n return ++count * -1;\n }\n function StyleBox() {}\n\n \/\/ Apply styles to a div. If there is no div passed then it defaults to the\n \/\/ div on 'this'.\n StyleBox.prototype.applyStyles = function (styles, div) {\n div = div || this.div;\n for (var prop in styles) {\n if (styles.hasOwnProperty(prop)) {\n div.style[prop] = styles[prop];\n }\n }\n };\n StyleBox.prototype.formatStyle = function (val, unit) {\n return val === 0 ? 0 : val + unit;\n };\n\n \/\/ Constructs the computed display state of the cue (a div). Places the div\n \/\/ into the overlay which should be a block level element (usually a div).\n function CueStyleBox(window, cue, styleOptions) {\n StyleBox.call(this);\n this.cue = cue;\n\n \/\/ Parse our cue's text into a DOM tree rooted at 'cueDiv'. This div will\n \/\/ have inline positioning and will function as the cue background box.\n this.cueDiv = parseContent(window, cue.text);\n var styles = {\n color: \"rgba(255, 255, 255, 1)\",\n backgroundColor: \"rgba(0, 0, 0, 0.8)\",\n position: \"relative\",\n left: 0,\n right: 0,\n top: 0,\n bottom: 0,\n display: \"inline\",\n writingMode: cue.vertical === \"\" ? \"horizontal-tb\" : cue.vertical === \"lr\" ? \"vertical-lr\" : \"vertical-rl\",\n unicodeBidi: \"plaintext\"\n };\n this.applyStyles(styles, this.cueDiv);\n\n \/\/ Create an absolutely positioned div that will be used to position the cue\n \/\/ div. Note, all WebVTT cue-setting alignments are equivalent to the CSS\n \/\/ mirrors of them except middle instead of center on Safari.\n this.div = window.document.createElement(\"div\");\n styles = {\n direction: determineBidi(this.cueDiv),\n writingMode: cue.vertical === \"\" ? \"horizontal-tb\" : cue.vertical === \"lr\" ? \"vertical-lr\" : \"vertical-rl\",\n unicodeBidi: \"plaintext\",\n textAlign: cue.align === \"middle\" ? \"center\" : cue.align,\n font: styleOptions.font,\n whiteSpace: \"pre-line\",\n position: \"absolute\"\n };\n this.applyStyles(styles);\n this.div.appendChild(this.cueDiv);\n\n \/\/ Calculate the distance from the reference edge of the viewport to the text\n \/\/ position of the cue box. The reference edge will be resolved later when\n \/\/ the box orientation styles are applied.\n var textPos = 0;\n switch (cue.positionAlign) {\n case \"start\":\n case \"line-left\":\n textPos = cue.position;\n break;\n case \"center\":\n textPos = cue.position - cue.size \/ 2;\n break;\n case \"end\":\n case \"line-right\":\n textPos = cue.position - cue.size;\n break;\n }\n\n \/\/ Horizontal box orientation; textPos is the distance from the left edge of the\n \/\/ area to the left edge of the box and cue.size is the distance extending to\n \/\/ the right from there.\n if (cue.vertical === \"\") {\n this.applyStyles({\n left: this.formatStyle(textPos, \"%\"),\n width: this.formatStyle(cue.size, \"%\")\n });\n \/\/ Vertical box orientation; textPos is the distance from the top edge of the\n \/\/ area to the top edge of the box and cue.size is the height extending\n \/\/ downwards from there.\n } else {\n this.applyStyles({\n top: this.formatStyle(textPos, \"%\"),\n height: this.formatStyle(cue.size, \"%\")\n });\n }\n this.move = function (box) {\n this.applyStyles({\n top: this.formatStyle(box.top, \"px\"),\n bottom: this.formatStyle(box.bottom, \"px\"),\n left: this.formatStyle(box.left, \"px\"),\n right: this.formatStyle(box.right, \"px\"),\n height: this.formatStyle(box.height, \"px\"),\n width: this.formatStyle(box.width, \"px\")\n });\n };\n }\n CueStyleBox.prototype = _objCreate(StyleBox.prototype);\n CueStyleBox.prototype.constructor = CueStyleBox;\n\n \/\/ Represents the co-ordinates of an Element in a way that we can easily\n \/\/ compute things with such as if it overlaps or intersects with another Element.\n \/\/ Can initialize it with either a StyleBox or another BoxPosition.\n function BoxPosition(obj) {\n \/\/ Either a BoxPosition was passed in and we need to copy it, or a StyleBox\n \/\/ was passed in and we need to copy the results of 'getBoundingClientRect'\n \/\/ as the object returned is readonly. All co-ordinate values are in reference\n \/\/ to the viewport origin (top left).\n var lh, height, width, top;\n if (obj.div) {\n height = obj.div.offsetHeight;\n width = obj.div.offsetWidth;\n top = obj.div.offsetTop;\n var rects = (rects = obj.div.childNodes) && (rects = rects[0]) && rects.getClientRects && rects.getClientRects();\n obj = obj.div.getBoundingClientRect();\n \/\/ In certain cases the outter div will be slightly larger then the sum of\n \/\/ the inner div's lines. This could be due to bold text, etc, on some platforms.\n \/\/ In this case we should get the average line height and use that. This will\n \/\/ result in the desired behaviour.\n lh = rects ? Math.max(rects[0] && rects[0].height || 0, obj.height \/ rects.length) : 0;\n }\n this.left = obj.left;\n this.right = obj.right;\n this.top = obj.top || top;\n this.height = obj.height || height;\n this.bottom = obj.bottom || top + (obj.height || height);\n this.width = obj.width || width;\n this.lineHeight = lh !== undefined ? lh : obj.lineHeight;\n }\n\n \/\/ Move the box along a particular axis. Optionally pass in an amount to move\n \/\/ the box. If no amount is passed then the default is the line height of the\n \/\/ box.\n BoxPosition.prototype.move = function (axis, toMove) {\n toMove = toMove !== undefined ? toMove : this.lineHeight;\n switch (axis) {\n case \"+x\":\n this.left += toMove;\n this.right += toMove;\n break;\n case \"-x\":\n this.left -= toMove;\n this.right -= toMove;\n break;\n case \"+y\":\n this.top += toMove;\n this.bottom += toMove;\n break;\n case \"-y\":\n this.top -= toMove;\n this.bottom -= toMove;\n break;\n }\n };\n\n \/\/ Check if this box overlaps another box, b2.\n BoxPosition.prototype.overlaps = function (b2) {\n return this.left < b2.right && this.right > b2.left && this.top < b2.bottom && this.bottom > b2.top;\n };\n\n \/\/ Check if this box overlaps any other boxes in boxes.\n BoxPosition.prototype.overlapsAny = function (boxes) {\n for (var i = 0; i < boxes.length; i++) {\n if (this.overlaps(boxes[i])) {\n return true;\n }\n }\n return false;\n };\n\n \/\/ Check if this box is within another box.\n BoxPosition.prototype.within = function (container) {\n return this.top >= container.top && this.bottom <= container.bottom && this.left >= container.left && this.right <= container.right;\n };\n\n \/\/ Check if this box is entirely within the container or it is overlapping\n \/\/ on the edge opposite of the axis direction passed. For example, if \"+x\" is\n \/\/ passed and the box is overlapping on the left edge of the container, then\n \/\/ return true.\n BoxPosition.prototype.overlapsOppositeAxis = function (container, axis) {\n switch (axis) {\n case \"+x\":\n return this.left < container.left;\n case \"-x\":\n return this.right > container.right;\n case \"+y\":\n return this.top < container.top;\n case \"-y\":\n return this.bottom > container.bottom;\n }\n };\n\n \/\/ Find the percentage of the area that this box is overlapping with another\n \/\/ box.\n BoxPosition.prototype.intersectPercentage = function (b2) {\n var x = Math.max(0, Math.min(this.right, b2.right) - Math.max(this.left, b2.left)),\n y = Math.max(0, Math.min(this.bottom, b2.bottom) - Math.max(this.top, b2.top)),\n intersectArea = x * y;\n return intersectArea \/ (this.height * this.width);\n };\n\n \/\/ Convert the positions from this box to CSS compatible positions using\n \/\/ the reference container's positions. This has to be done because this\n \/\/ box's positions are in reference to the viewport origin, whereas, CSS\n \/\/ values are in referecne to their respective edges.\n BoxPosition.prototype.toCSSCompatValues = function (reference) {\n return {\n top: this.top - reference.top,\n bottom: reference.bottom - this.bottom,\n left: this.left - reference.left,\n right: reference.right - this.right,\n height: this.height,\n width: this.width\n };\n };\n\n \/\/ Get an object that represents the box's position without anything extra.\n \/\/ Can pass a StyleBox, HTMLElement, or another BoxPositon.\n BoxPosition.getSimpleBoxPosition = function (obj) {\n var height = obj.div ? obj.div.offsetHeight : obj.tagName ? obj.offsetHeight : 0;\n var width = obj.div ? obj.div.offsetWidth : obj.tagName ? obj.offsetWidth : 0;\n var top = obj.div ? obj.div.offsetTop : obj.tagName ? obj.offsetTop : 0;\n obj = obj.div ? obj.div.getBoundingClientRect() : obj.tagName ? obj.getBoundingClientRect() : obj;\n var ret = {\n left: obj.left,\n right: obj.right,\n top: obj.top || top,\n height: obj.height || height,\n bottom: obj.bottom || top + (obj.height || height),\n width: obj.width || width\n };\n return ret;\n };\n\n \/\/ Move a StyleBox to its specified, or next best, position. The containerBox\n \/\/ is the box that contains the StyleBox, such as a div. boxPositions are\n \/\/ a list of other boxes that the styleBox can't overlap with.\n function moveBoxToLinePosition(window, styleBox, containerBox, boxPositions) {\n \/\/ Find the best position for a cue box, b, on the video. The axis parameter\n \/\/ is a list of axis, the order of which, it will move the box along. For example:\n \/\/ Passing [\"+x\", \"-x\"] will move the box first along the x axis in the positive\n \/\/ direction. If it doesn't find a good position for it there it will then move\n \/\/ it along the x axis in the negative direction.\n function findBestPosition(b, axis) {\n var bestPosition,\n specifiedPosition = new BoxPosition(b),\n percentage = 1; \/\/ Highest possible so the first thing we get is better.\n\n for (var i = 0; i < axis.length; i++) {\n while (b.overlapsOppositeAxis(containerBox, axis[i]) || b.within(containerBox) && b.overlapsAny(boxPositions)) {\n b.move(axis[i]);\n }\n \/\/ We found a spot where we aren't overlapping anything. This is our\n \/\/ best position.\n if (b.within(containerBox)) {\n return b;\n }\n var p = b.intersectPercentage(containerBox);\n \/\/ If we're outside the container box less then we were on our last try\n \/\/ then remember this position as the best position.\n if (percentage > p) {\n bestPosition = new BoxPosition(b);\n percentage = p;\n }\n \/\/ Reset the box position to the specified position.\n b = new BoxPosition(specifiedPosition);\n }\n return bestPosition || specifiedPosition;\n }\n var boxPosition = new BoxPosition(styleBox),\n cue = styleBox.cue,\n linePos = computeLinePos(cue),\n axis = [];\n\n \/\/ If we have a line number to align the cue to.\n if (cue.snapToLines) {\n var size;\n switch (cue.vertical) {\n case \"\":\n axis = [\"+y\", \"-y\"];\n size = \"height\";\n break;\n case \"rl\":\n axis = [\"+x\", \"-x\"];\n size = \"width\";\n break;\n case \"lr\":\n axis = [\"-x\", \"+x\"];\n size = \"width\";\n break;\n }\n var step = boxPosition.lineHeight,\n position = step * Math.round(linePos),\n maxPosition = containerBox[size] + step,\n initialAxis = axis[0];\n\n \/\/ If the specified intial position is greater then the max position then\n \/\/ clamp the box to the amount of steps it would take for the box to\n \/\/ reach the max position.\n if (Math.abs(position) > maxPosition) {\n position = position < 0 ? -1 : 1;\n position *= Math.ceil(maxPosition \/ step) * step;\n }\n\n \/\/ If computed line position returns negative then line numbers are\n \/\/ relative to the bottom of the video instead of the top. Therefore, we\n \/\/ need to increase our initial position by the length or width of the\n \/\/ video, depending on the writing direction, and reverse our axis directions.\n if (linePos < 0) {\n position += cue.vertical === \"\" ? containerBox.height : containerBox.width;\n axis = axis.reverse();\n }\n\n \/\/ Move the box to the specified position. This may not be its best\n \/\/ position.\n boxPosition.move(initialAxis, position);\n } else {\n \/\/ If we have a percentage line value for the cue.\n var calculatedPercentage = boxPosition.lineHeight \/ containerBox.height * 100;\n switch (cue.lineAlign) {\n case \"center\":\n linePos -= calculatedPercentage \/ 2;\n break;\n case \"end\":\n linePos -= calculatedPercentage;\n break;\n }\n\n \/\/ Apply initial line position to the cue box.\n switch (cue.vertical) {\n case \"\":\n styleBox.applyStyles({\n top: styleBox.formatStyle(linePos, \"%\")\n });\n break;\n case \"rl\":\n styleBox.applyStyles({\n left: styleBox.formatStyle(linePos, \"%\")\n });\n break;\n case \"lr\":\n styleBox.applyStyles({\n right: styleBox.formatStyle(linePos, \"%\")\n });\n break;\n }\n axis = [\"+y\", \"-x\", \"+x\", \"-y\"];\n\n \/\/ Get the box position again after we've applied the specified positioning\n \/\/ to it.\n boxPosition = new BoxPosition(styleBox);\n }\n var bestPosition = findBestPosition(boxPosition, axis);\n styleBox.move(bestPosition.toCSSCompatValues(containerBox));\n }\n function WebVTT$1() {\n \/\/ Nothing\n }\n\n \/\/ Helper to allow strings to be decoded instead of the default binary utf8 data.\n WebVTT$1.StringDecoder = function () {\n return {\n decode: function (data) {\n if (!data) {\n return \"\";\n }\n if (typeof data !== \"string\") {\n throw new Error(\"Error - expected string data.\");\n }\n return decodeURIComponent(encodeURIComponent(data));\n }\n };\n };\n WebVTT$1.convertCueToDOMTree = function (window, cuetext) {\n if (!window || !cuetext) {\n return null;\n }\n return parseContent(window, cuetext);\n };\n var FONT_SIZE_PERCENT = 0.05;\n var FONT_STYLE = \"sans-serif\";\n var CUE_BACKGROUND_PADDING = \"1.5%\";\n\n \/\/ Runs the processing model over the cues and regions passed to it.\n \/\/ @param overlay A block level element (usually a div) that the computed cues\n \/\/ and regions will be placed into.\n WebVTT$1.processCues = function (window, cues, overlay) {\n if (!window || !cues || !overlay) {\n return null;\n }\n\n \/\/ Remove all previous children.\n while (overlay.firstChild) {\n overlay.removeChild(overlay.firstChild);\n }\n var paddedOverlay = window.document.createElement(\"div\");\n paddedOverlay.style.position = \"absolute\";\n paddedOverlay.style.left = \"0\";\n paddedOverlay.style.right = \"0\";\n paddedOverlay.style.top = \"0\";\n paddedOverlay.style.bottom = \"0\";\n paddedOverlay.style.margin = CUE_BACKGROUND_PADDING;\n overlay.appendChild(paddedOverlay);\n\n \/\/ Determine if we need to compute the display states of the cues. This could\n \/\/ be the case if a cue's state has been changed since the last computation or\n \/\/ if it has not been computed yet.\n function shouldCompute(cues) {\n for (var i = 0; i < cues.length; i++) {\n if (cues[i].hasBeenReset || !cues[i].displayState) {\n return true;\n }\n }\n return false;\n }\n\n \/\/ We don't need to recompute the cues' display states. Just reuse them.\n if (!shouldCompute(cues)) {\n for (var i = 0; i < cues.length; i++) {\n paddedOverlay.appendChild(cues[i].displayState);\n }\n return;\n }\n var boxPositions = [],\n containerBox = BoxPosition.getSimpleBoxPosition(paddedOverlay),\n fontSize = Math.round(containerBox.height * FONT_SIZE_PERCENT * 100) \/ 100;\n var styleOptions = {\n font: fontSize + \"px \" + FONT_STYLE\n };\n (function () {\n var styleBox, cue;\n for (var i = 0; i < cues.length; i++) {\n cue = cues[i];\n\n \/\/ Compute the intial position and styles of the cue div.\n styleBox = new CueStyleBox(window, cue, styleOptions);\n paddedOverlay.appendChild(styleBox.div);\n\n \/\/ Move the cue div to it's correct line position.\n moveBoxToLinePosition(window, styleBox, containerBox, boxPositions);\n\n \/\/ Remember the computed div so that we don't have to recompute it later\n \/\/ if we don't have too.\n cue.displayState = styleBox.div;\n boxPositions.push(BoxPosition.getSimpleBoxPosition(styleBox));\n }\n })();\n };\n WebVTT$1.Parser = function (window, vttjs, decoder) {\n if (!decoder) {\n decoder = vttjs;\n vttjs = {};\n }\n if (!vttjs) {\n vttjs = {};\n }\n this.window = window;\n this.vttjs = vttjs;\n this.state = \"INITIAL\";\n this.buffer = \"\";\n this.decoder = decoder || new TextDecoder(\"utf8\");\n this.regionList = [];\n };\n WebVTT$1.Parser.prototype = {\n \/\/ If the error is a ParsingError then report it to the consumer if\n \/\/ possible. If it's not a ParsingError then throw it like normal.\n reportOrThrowError: function (e) {\n if (e instanceof ParsingError) {\n this.onparsingerror && this.onparsingerror(e);\n } else {\n throw e;\n }\n },\n parse: function (data) {\n var self = this;\n\n \/\/ If there is no data then we won't decode it, but will just try to parse\n \/\/ whatever is in buffer already. This may occur in circumstances, for\n \/\/ example when flush() is called.\n if (data) {\n \/\/ Try to decode the data that we received.\n self.buffer += self.decoder.decode(data, {\n stream: true\n });\n }\n function collectNextLine() {\n var buffer = self.buffer;\n var pos = 0;\n while (pos < buffer.length && buffer[pos] !== '\\r' && buffer[pos] !== '\\n') {\n ++pos;\n }\n var line = buffer.substr(0, pos);\n \/\/ Advance the buffer early in case we fail below.\n if (buffer[pos] === '\\r') {\n ++pos;\n }\n if (buffer[pos] === '\\n') {\n ++pos;\n }\n self.buffer = buffer.substr(pos);\n return line;\n }\n\n \/\/ 3.4 WebVTT region and WebVTT region settings syntax\n function parseRegion(input) {\n var settings = new Settings();\n parseOptions(input, function (k, v) {\n switch (k) {\n case \"id\":\n settings.set(k, v);\n break;\n case \"width\":\n settings.percent(k, v);\n break;\n case \"lines\":\n settings.integer(k, v);\n break;\n case \"regionanchor\":\n case \"viewportanchor\":\n var xy = v.split(',');\n if (xy.length !== 2) {\n break;\n }\n \/\/ We have to make sure both x and y parse, so use a temporary\n \/\/ settings object here.\n var anchor = new Settings();\n anchor.percent(\"x\", xy[0]);\n anchor.percent(\"y\", xy[1]);\n if (!anchor.has(\"x\") || !anchor.has(\"y\")) {\n break;\n }\n settings.set(k + \"X\", anchor.get(\"x\"));\n settings.set(k + \"Y\", anchor.get(\"y\"));\n break;\n case \"scroll\":\n settings.alt(k, v, [\"up\"]);\n break;\n }\n }, \/=\/, \/\\s\/);\n\n \/\/ Create the region, using default values for any values that were not\n \/\/ specified.\n if (settings.has(\"id\")) {\n var region = new (self.vttjs.VTTRegion || self.window.VTTRegion)();\n region.width = settings.get(\"width\", 100);\n region.lines = settings.get(\"lines\", 3);\n region.regionAnchorX = settings.get(\"regionanchorX\", 0);\n region.regionAnchorY = settings.get(\"regionanchorY\", 100);\n region.viewportAnchorX = settings.get(\"viewportanchorX\", 0);\n region.viewportAnchorY = settings.get(\"viewportanchorY\", 100);\n region.scroll = settings.get(\"scroll\", \"\");\n \/\/ Register the region.\n self.onregion && self.onregion(region);\n \/\/ Remember the VTTRegion for later in case we parse any VTTCues that\n \/\/ reference it.\n self.regionList.push({\n id: settings.get(\"id\"),\n region: region\n });\n }\n }\n\n \/\/ draft-pantos-http-live-streaming-20\n \/\/ https:\/\/tools.ietf.org\/html\/draft-pantos-http-live-streaming-20#section-3.5\n \/\/ 3.5 WebVTT\n function parseTimestampMap(input) {\n var settings = new Settings();\n parseOptions(input, function (k, v) {\n switch (k) {\n case \"MPEGT\":\n settings.integer(k + 'S', v);\n break;\n case \"LOCA\":\n settings.set(k + 'L', parseTimeStamp(v));\n break;\n }\n }, \/[^\\d]:\/, \/,\/);\n self.ontimestampmap && self.ontimestampmap({\n \"MPEGTS\": settings.get(\"MPEGTS\"),\n \"LOCAL\": settings.get(\"LOCAL\")\n });\n }\n\n \/\/ 3.2 WebVTT metadata header syntax\n function parseHeader(input) {\n if (input.match(\/X-TIMESTAMP-MAP\/)) {\n \/\/ This line contains HLS X-TIMESTAMP-MAP metadata\n parseOptions(input, function (k, v) {\n switch (k) {\n case \"X-TIMESTAMP-MAP\":\n parseTimestampMap(v);\n break;\n }\n }, \/=\/);\n } else {\n parseOptions(input, function (k, v) {\n switch (k) {\n case \"Region\":\n \/\/ 3.3 WebVTT region metadata header syntax\n parseRegion(v);\n break;\n }\n }, \/:\/);\n }\n }\n\n \/\/ 5.1 WebVTT file parsing.\n try {\n var line;\n if (self.state === \"INITIAL\") {\n \/\/ We can't start parsing until we have the first line.\n if (!\/\\r\\n|\\n\/.test(self.buffer)) {\n return this;\n }\n line = collectNextLine();\n var m = line.match(\/^WEBVTT([ \\t].*)?$\/);\n if (!m || !m[0]) {\n throw new ParsingError(ParsingError.Errors.BadSignature);\n }\n self.state = \"HEADER\";\n }\n var alreadyCollectedLine = false;\n while (self.buffer) {\n \/\/ We can't parse a line until we have the full line.\n if (!\/\\r\\n|\\n\/.test(self.buffer)) {\n return this;\n }\n if (!alreadyCollectedLine) {\n line = collectNextLine();\n } else {\n alreadyCollectedLine = false;\n }\n switch (self.state) {\n case \"HEADER\":\n \/\/ 13-18 - Allow a header (metadata) under the WEBVTT line.\n if (\/:\/.test(line)) {\n parseHeader(line);\n } else if (!line) {\n \/\/ An empty line terminates the header and starts the body (cues).\n self.state = \"ID\";\n }\n continue;\n case \"NOTE\":\n \/\/ Ignore NOTE blocks.\n if (!line) {\n self.state = \"ID\";\n }\n continue;\n case \"ID\":\n \/\/ Check for the start of NOTE blocks.\n if (\/^NOTE($|[ \\t])\/.test(line)) {\n self.state = \"NOTE\";\n break;\n }\n \/\/ 19-29 - Allow any number of line terminators, then initialize new cue values.\n if (!line) {\n continue;\n }\n self.cue = new (self.vttjs.VTTCue || self.window.VTTCue)(0, 0, \"\");\n \/\/ Safari still uses the old middle value and won't accept center\n try {\n self.cue.align = \"center\";\n } catch (e) {\n self.cue.align = \"middle\";\n }\n self.state = \"CUE\";\n \/\/ 30-39 - Check if self line contains an optional identifier or timing data.\n if (line.indexOf(\"-->\") === -1) {\n self.cue.id = line;\n continue;\n }\n \/\/ Process line as start of a cue.\n \/*falls through*\/\n case \"CUE\":\n \/\/ 40 - Collect cue timings and settings.\n try {\n parseCue(line, self.cue, self.regionList);\n } catch (e) {\n self.reportOrThrowError(e);\n \/\/ In case of an error ignore rest of the cue.\n self.cue = null;\n self.state = \"BADCUE\";\n continue;\n }\n self.state = \"CUETEXT\";\n continue;\n case \"CUETEXT\":\n var hasSubstring = line.indexOf(\"-->\") !== -1;\n \/\/ 34 - If we have an empty line then report the cue.\n \/\/ 35 - If we have the special substring '-->' then report the cue,\n \/\/ but do not collect the line as we need to process the current\n \/\/ one as a new cue.\n if (!line || hasSubstring && (alreadyCollectedLine = true)) {\n \/\/ We are done parsing self cue.\n self.oncue && self.oncue(self.cue);\n self.cue = null;\n self.state = \"ID\";\n continue;\n }\n if (self.cue.text) {\n self.cue.text += \"\\n\";\n }\n self.cue.text += line.replace(\/\\u2028\/g, '\\n').replace(\/u2029\/g, '\\n');\n continue;\n case \"BADCUE\":\n \/\/ BADCUE\n \/\/ 54-62 - Collect and discard the remaining cue.\n if (!line) {\n self.state = \"ID\";\n }\n continue;\n }\n }\n } catch (e) {\n self.reportOrThrowError(e);\n\n \/\/ If we are currently parsing a cue, report what we have.\n if (self.state === \"CUETEXT\" && self.cue && self.oncue) {\n self.oncue(self.cue);\n }\n self.cue = null;\n \/\/ Enter BADWEBVTT state if header was not parsed correctly otherwise\n \/\/ another exception occurred so enter BADCUE state.\n self.state = self.state === \"INITIAL\" ? \"BADWEBVTT\" : \"BADCUE\";\n }\n return this;\n },\n flush: function () {\n var self = this;\n try {\n \/\/ Finish decoding the stream.\n self.buffer += self.decoder.decode();\n \/\/ Synthesize the end of the current cue or region.\n if (self.cue || self.state === \"HEADER\") {\n self.buffer += \"\\n\\n\";\n self.parse();\n }\n \/\/ If we've flushed, parsed, and we're still on the INITIAL state then\n \/\/ that means we don't have enough of the stream to parse the first\n \/\/ line.\n if (self.state === \"INITIAL\") {\n throw new ParsingError(ParsingError.Errors.BadSignature);\n }\n } catch (e) {\n self.reportOrThrowError(e);\n }\n self.onflush && self.onflush();\n return this;\n }\n };\n var vtt = WebVTT$1;\n\n \/**\n * Copyright 2013 vtt.js Contributors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http:\/\/www.apache.org\/licenses\/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\/\n\n var autoKeyword = \"auto\";\n var directionSetting = {\n \"\": 1,\n \"lr\": 1,\n \"rl\": 1\n };\n var alignSetting = {\n \"start\": 1,\n \"center\": 1,\n \"end\": 1,\n \"left\": 1,\n \"right\": 1,\n \"auto\": 1,\n \"line-left\": 1,\n \"line-right\": 1\n };\n function findDirectionSetting(value) {\n if (typeof value !== \"string\") {\n return false;\n }\n var dir = directionSetting[value.toLowerCase()];\n return dir ? value.toLowerCase() : false;\n }\n function findAlignSetting(value) {\n if (typeof value !== \"string\") {\n return false;\n }\n var align = alignSetting[value.toLowerCase()];\n return align ? value.toLowerCase() : false;\n }\n function VTTCue(startTime, endTime, text) {\n \/**\n * Shim implementation specific properties. These properties are not in\n * the spec.\n *\/\n\n \/\/ Lets us know when the VTTCue's data has changed in such a way that we need\n \/\/ to recompute its display state. This lets us compute its display state\n \/\/ lazily.\n this.hasBeenReset = false;\n\n \/**\n * VTTCue and TextTrackCue properties\n * http:\/\/dev.w3.org\/html5\/webvtt\/#vttcue-interface\n *\/\n\n var _id = \"\";\n var _pauseOnExit = false;\n var _startTime = startTime;\n var _endTime = endTime;\n var _text = text;\n var _region = null;\n var _vertical = \"\";\n var _snapToLines = true;\n var _line = \"auto\";\n var _lineAlign = \"start\";\n var _position = \"auto\";\n var _positionAlign = \"auto\";\n var _size = 100;\n var _align = \"center\";\n Object.defineProperties(this, {\n \"id\": {\n enumerable: true,\n get: function () {\n return _id;\n },\n set: function (value) {\n _id = \"\" + value;\n }\n },\n \"pauseOnExit\": {\n enumerable: true,\n get: function () {\n return _pauseOnExit;\n },\n set: function (value) {\n _pauseOnExit = !!value;\n }\n },\n \"startTime\": {\n enumerable: true,\n get: function () {\n return _startTime;\n },\n set: function (value) {\n if (typeof value !== \"number\") {\n throw new TypeError(\"Start time must be set to a number.\");\n }\n _startTime = value;\n this.hasBeenReset = true;\n }\n },\n \"endTime\": {\n enumerable: true,\n get: function () {\n return _endTime;\n },\n set: function (value) {\n if (typeof value !== \"number\") {\n throw new TypeError(\"End time must be set to a number.\");\n }\n _endTime = value;\n this.hasBeenReset = true;\n }\n },\n \"text\": {\n enumerable: true,\n get: function () {\n return _text;\n },\n set: function (value) {\n _text = \"\" + value;\n this.hasBeenReset = true;\n }\n },\n \"region\": {\n enumerable: true,\n get: function () {\n return _region;\n },\n set: function (value) {\n _region = value;\n this.hasBeenReset = true;\n }\n },\n \"vertical\": {\n enumerable: true,\n get: function () {\n return _vertical;\n },\n set: function (value) {\n var setting = findDirectionSetting(value);\n \/\/ Have to check for false because the setting an be an empty string.\n if (setting === false) {\n throw new SyntaxError(\"Vertical: an invalid or illegal direction string was specified.\");\n }\n _vertical = setting;\n this.hasBeenReset = true;\n }\n },\n \"snapToLines\": {\n enumerable: true,\n get: function () {\n return _snapToLines;\n },\n set: function (value) {\n _snapToLines = !!value;\n this.hasBeenReset = true;\n }\n },\n \"line\": {\n enumerable: true,\n get: function () {\n return _line;\n },\n set: function (value) {\n if (typeof value !== \"number\" && value !== autoKeyword) {\n throw new SyntaxError(\"Line: an invalid number or illegal string was specified.\");\n }\n _line = value;\n this.hasBeenReset = true;\n }\n },\n \"lineAlign\": {\n enumerable: true,\n get: function () {\n return _lineAlign;\n },\n set: function (value) {\n var setting = findAlignSetting(value);\n if (!setting) {\n console.warn(\"lineAlign: an invalid or illegal string was specified.\");\n } else {\n _lineAlign = setting;\n this.hasBeenReset = true;\n }\n }\n },\n \"position\": {\n enumerable: true,\n get: function () {\n return _position;\n },\n set: function (value) {\n if (value < 0 || value > 100) {\n throw new Error(\"Position must be between 0 and 100.\");\n }\n _position = value;\n this.hasBeenReset = true;\n }\n },\n \"positionAlign\": {\n enumerable: true,\n get: function () {\n return _positionAlign;\n },\n set: function (value) {\n var setting = findAlignSetting(value);\n if (!setting) {\n console.warn(\"positionAlign: an invalid or illegal string was specified.\");\n } else {\n _positionAlign = setting;\n this.hasBeenReset = true;\n }\n }\n },\n \"size\": {\n enumerable: true,\n get: function () {\n return _size;\n },\n set: function (value) {\n if (value < 0 || value > 100) {\n throw new Error(\"Size must be between 0 and 100.\");\n }\n _size = value;\n this.hasBeenReset = true;\n }\n },\n \"align\": {\n enumerable: true,\n get: function () {\n return _align;\n },\n set: function (value) {\n var setting = findAlignSetting(value);\n if (!setting) {\n throw new SyntaxError(\"align: an invalid or illegal alignment string was specified.\");\n }\n _align = setting;\n this.hasBeenReset = true;\n }\n }\n });\n\n \/**\n * Other spec defined properties\n *\/\n\n \/\/ http:\/\/www.whatwg.org\/specs\/web-apps\/current-work\/multipage\/the-video-element.html#text-track-cue-display-state\n this.displayState = undefined;\n }\n\n \/**\n * VTTCue methods\n *\/\n\n VTTCue.prototype.getCueAsHTML = function () {\n \/\/ Assume WebVTT.convertCueToDOMTree is on the global.\n return WebVTT.convertCueToDOMTree(window, this.text);\n };\n var vttcue = VTTCue;\n\n \/**\n * Copyright 2013 vtt.js Contributors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http:\/\/www.apache.org\/licenses\/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\/\n\n var scrollSetting = {\n \"\": true,\n \"up\": true\n };\n function findScrollSetting(value) {\n if (typeof value !== \"string\") {\n return false;\n }\n var scroll = scrollSetting[value.toLowerCase()];\n return scroll ? value.toLowerCase() : false;\n }\n function isValidPercentValue(value) {\n return typeof value === \"number\" && value >= 0 && value <= 100;\n }\n\n \/\/ VTTRegion shim http:\/\/dev.w3.org\/html5\/webvtt\/#vttregion-interface\n function VTTRegion() {\n var _width = 100;\n var _lines = 3;\n var _regionAnchorX = 0;\n var _regionAnchorY = 100;\n var _viewportAnchorX = 0;\n var _viewportAnchorY = 100;\n var _scroll = \"\";\n Object.defineProperties(this, {\n \"width\": {\n enumerable: true,\n get: function () {\n return _width;\n },\n set: function (value) {\n if (!isValidPercentValue(value)) {\n throw new Error(\"Width must be between 0 and 100.\");\n }\n _width = value;\n }\n },\n \"lines\": {\n enumerable: true,\n get: function () {\n return _lines;\n },\n set: function (value) {\n if (typeof value !== \"number\") {\n throw new TypeError(\"Lines must be set to a number.\");\n }\n _lines = value;\n }\n },\n \"regionAnchorY\": {\n enumerable: true,\n get: function () {\n return _regionAnchorY;\n },\n set: function (value) {\n if (!isValidPercentValue(value)) {\n throw new Error(\"RegionAnchorX must be between 0 and 100.\");\n }\n _regionAnchorY = value;\n }\n },\n \"regionAnchorX\": {\n enumerable: true,\n get: function () {\n return _regionAnchorX;\n },\n set: function (value) {\n if (!isValidPercentValue(value)) {\n throw new Error(\"RegionAnchorY must be between 0 and 100.\");\n }\n _regionAnchorX = value;\n }\n },\n \"viewportAnchorY\": {\n enumerable: true,\n get: function () {\n return _viewportAnchorY;\n },\n set: function (value) {\n if (!isValidPercentValue(value)) {\n throw new Error(\"ViewportAnchorY must be between 0 and 100.\");\n }\n _viewportAnchorY = value;\n }\n },\n \"viewportAnchorX\": {\n enumerable: true,\n get: function () {\n return _viewportAnchorX;\n },\n set: function (value) {\n if (!isValidPercentValue(value)) {\n throw new Error(\"ViewportAnchorX must be between 0 and 100.\");\n }\n _viewportAnchorX = value;\n }\n },\n \"scroll\": {\n enumerable: true,\n get: function () {\n return _scroll;\n },\n set: function (value) {\n var setting = findScrollSetting(value);\n \/\/ Have to check for false as an empty string is a legal value.\n if (setting === false) {\n console.warn(\"Scroll: an invalid or illegal string was specified.\");\n } else {\n _scroll = setting;\n }\n }\n }\n });\n }\n var vttregion = VTTRegion;\n\n var browserIndex = createCommonjsModule(function (module) {\n \/**\n * Copyright 2013 vtt.js Contributors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http:\/\/www.apache.org\/licenses\/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\/\n\n \/\/ Default exports for Node. Export the extended versions of VTTCue and\n \/\/ VTTRegion in Node since we likely want the capability to convert back and\n \/\/ forth between JSON. If we don't then it's not that big of a deal since we're\n \/\/ off browser.\n\n var vttjs = module.exports = {\n WebVTT: vtt,\n VTTCue: vttcue,\n VTTRegion: vttregion\n };\n window_1.vttjs = vttjs;\n window_1.WebVTT = vttjs.WebVTT;\n var cueShim = vttjs.VTTCue;\n var regionShim = vttjs.VTTRegion;\n var nativeVTTCue = window_1.VTTCue;\n var nativeVTTRegion = window_1.VTTRegion;\n vttjs.shim = function () {\n window_1.VTTCue = cueShim;\n window_1.VTTRegion = regionShim;\n };\n vttjs.restore = function () {\n window_1.VTTCue = nativeVTTCue;\n window_1.VTTRegion = nativeVTTRegion;\n };\n if (!window_1.VTTCue) {\n vttjs.shim();\n }\n });\n browserIndex.WebVTT;\n browserIndex.VTTCue;\n browserIndex.VTTRegion;\n\n \/**\n * @file tech.js\n *\/\n\n \/** @import { TimeRange } from '..\/utils\/time' *\/\n\n \/**\n * An Object containing a structure like: `{src: 'url', type: 'mimetype'}` or string\n * that just contains the src url alone.\n * * `var SourceObject = {src: 'http:\/\/ex.com\/video.mp4', type: 'video\/mp4'};`\n * `var SourceString = 'http:\/\/example.com\/some-video.mp4';`\n *\n * @typedef {Object|string} SourceObject\n *\n * @property {string} src\n * The url to the source\n *\n * @property {string} type\n * The mime type of the source\n *\/\n\n \/**\n * A function used by {@link Tech} to create a new {@link TextTrack}.\n *\n * @private\n *\n * @param {Tech} self\n * An instance of the Tech class.\n *\n * @param {string} kind\n * `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata)\n *\n * @param {string} [label]\n * Label to identify the text track\n *\n * @param {string} [language]\n * Two letter language abbreviation\n *\n * @param {Object} [options={}]\n * An object with additional text track options\n *\n * @return {TextTrack}\n * The text track that was created.\n *\/\n function createTrackHelper(self, kind, label, language, options = {}) {\n const tracks = self.textTracks();\n options.kind = kind;\n if (label) {\n options.label = label;\n }\n if (language) {\n options.language = language;\n }\n options.tech = self;\n const track = new ALL.text.TrackClass(options);\n tracks.addTrack(track);\n return track;\n }\n\n \/**\n * This is the base class for media playback technology controllers, such as\n * {@link HTML5}\n *\n * @extends Component\n *\/\n class Tech extends Component$1 {\n \/**\n * Create an instance of this Tech.\n *\n * @param {Object} [options]\n * The key\/value store of player options.\n *\n * @param {Function} [ready]\n * Callback function to call when the `HTML5` Tech is ready.\n *\/\n constructor(options = {}, ready = function () {}) {\n \/\/ we don't want the tech to report user activity automatically.\n \/\/ This is done manually in addControlsListeners\n options.reportTouchActivity = false;\n super(null, options, ready);\n this.onDurationChange_ = e => this.onDurationChange(e);\n this.trackProgress_ = e => this.trackProgress(e);\n this.trackCurrentTime_ = e => this.trackCurrentTime(e);\n this.stopTrackingCurrentTime_ = e => this.stopTrackingCurrentTime(e);\n this.disposeSourceHandler_ = e => this.disposeSourceHandler(e);\n this.queuedHanders_ = new Set();\n\n \/\/ keep track of whether the current source has played at all to\n \/\/ implement a very limited played()\n this.hasStarted_ = false;\n this.on('playing', function () {\n this.hasStarted_ = true;\n });\n this.on('loadstart', function () {\n this.hasStarted_ = false;\n });\n ALL.names.forEach(name => {\n const props = ALL[name];\n if (options && options[props.getterName]) {\n this[props.privateName] = options[props.getterName];\n }\n });\n\n \/\/ Manually track progress in cases where the browser\/tech doesn't report it.\n if (!this.featuresProgressEvents) {\n this.manualProgressOn();\n }\n\n \/\/ Manually track timeupdates in cases where the browser\/tech doesn't report it.\n if (!this.featuresTimeupdateEvents) {\n this.manualTimeUpdatesOn();\n }\n ['Text', 'Audio', 'Video'].forEach(track => {\n if (options[`native${track}Tracks`] === false) {\n this[`featuresNative${track}Tracks`] = false;\n }\n });\n if (options.nativeCaptions === false || options.nativeTextTracks === false) {\n this.featuresNativeTextTracks = false;\n } else if (options.nativeCaptions === true || options.nativeTextTracks === true) {\n this.featuresNativeTextTracks = true;\n }\n if (!this.featuresNativeTextTracks) {\n this.emulateTextTracks();\n }\n this.preloadTextTracks = options.preloadTextTracks !== false;\n this.autoRemoteTextTracks_ = new ALL.text.ListClass();\n this.initTrackListeners();\n\n \/\/ Turn on component tap events only if not using native controls\n if (!options.nativeControlsForTouch) {\n this.emitTapEvents();\n }\n if (this.constructor) {\n this.name_ = this.constructor.name || 'Unknown Tech';\n }\n }\n\n \/**\n * A special function to trigger source set in a way that will allow player\n * to re-trigger if the player or tech are not ready yet.\n *\n * @fires Tech#sourceset\n * @param {string} src The source string at the time of the source changing.\n *\/\n triggerSourceset(src) {\n if (!this.isReady_) {\n \/\/ on initial ready we have to trigger source set\n \/\/ 1ms after ready so that player can watch for it.\n this.one('ready', () => this.setTimeout(() => this.triggerSourceset(src), 1));\n }\n\n \/**\n * Fired when the source is set on the tech causing the media element\n * to reload.\n *\n * @see {@link Player#event:sourceset}\n * @event Tech#sourceset\n * @type {Event}\n *\/\n this.trigger({\n src,\n type: 'sourceset'\n });\n }\n\n \/* Fallbacks for unsupported event types\n ================================================================================ *\/\n\n \/**\n * Polyfill the `progress` event for browsers that don't support it natively.\n *\n * @see {@link Tech#trackProgress}\n *\/\n manualProgressOn() {\n this.on('durationchange', this.onDurationChange_);\n this.manualProgress = true;\n\n \/\/ Trigger progress watching when a source begins loading\n this.one('ready', this.trackProgress_);\n }\n\n \/**\n * Turn off the polyfill for `progress` events that was created in\n * {@link Tech#manualProgressOn}\n *\/\n manualProgressOff() {\n this.manualProgress = false;\n this.stopTrackingProgress();\n this.off('durationchange', this.onDurationChange_);\n }\n\n \/**\n * This is used to trigger a `progress` event when the buffered percent changes. It\n * sets an interval function that will be called every 500 milliseconds to check if the\n * buffer end percent has changed.\n *\n * > This function is called by {@link Tech#manualProgressOn}\n *\n * @param {Event} event\n * The `ready` event that caused this to run.\n *\n * @listens Tech#ready\n * @fires Tech#progress\n *\/\n trackProgress(event) {\n this.stopTrackingProgress();\n this.progressInterval = this.setInterval(bind_(this, function () {\n \/\/ Don't trigger unless buffered amount is greater than last time\n\n const numBufferedPercent = this.bufferedPercent();\n if (this.bufferedPercent_ !== numBufferedPercent) {\n \/**\n * See {@link Player#progress}\n *\n * @event Tech#progress\n * @type {Event}\n *\/\n this.trigger('progress');\n }\n this.bufferedPercent_ = numBufferedPercent;\n if (numBufferedPercent === 1) {\n this.stopTrackingProgress();\n }\n }), 500);\n }\n\n \/**\n * Update our internal duration on a `durationchange` event by calling\n * {@link Tech#duration}.\n *\n * @param {Event} event\n * The `durationchange` event that caused this to run.\n *\n * @listens Tech#durationchange\n *\/\n onDurationChange(event) {\n this.duration_ = this.duration();\n }\n\n \/**\n * Get and create a `TimeRange` object for buffering.\n *\n * @return {TimeRange}\n * The time range object that was created.\n *\/\n buffered() {\n return createTimeRanges$1(0, 0);\n }\n\n \/**\n * Get the percentage of the current video that is currently buffered.\n *\n * @return {number}\n * A number from 0 to 1 that represents the decimal percentage of the\n * video that is buffered.\n *\n *\/\n bufferedPercent() {\n return bufferedPercent(this.buffered(), this.duration_);\n }\n\n \/**\n * Turn off the polyfill for `progress` events that was created in\n * {@link Tech#manualProgressOn}\n * Stop manually tracking progress events by clearing the interval that was set in\n * {@link Tech#trackProgress}.\n *\/\n stopTrackingProgress() {\n this.clearInterval(this.progressInterval);\n }\n\n \/**\n * Polyfill the `timeupdate` event for browsers that don't support it.\n *\n * @see {@link Tech#trackCurrentTime}\n *\/\n manualTimeUpdatesOn() {\n this.manualTimeUpdates = true;\n this.on('play', this.trackCurrentTime_);\n this.on('pause', this.stopTrackingCurrentTime_);\n }\n\n \/**\n * Turn off the polyfill for `timeupdate` events that was created in\n * {@link Tech#manualTimeUpdatesOn}\n *\/\n manualTimeUpdatesOff() {\n this.manualTimeUpdates = false;\n this.stopTrackingCurrentTime();\n this.off('play', this.trackCurrentTime_);\n this.off('pause', this.stopTrackingCurrentTime_);\n }\n\n \/**\n * Sets up an interval function to track current time and trigger `timeupdate` every\n * 250 milliseconds.\n *\n * @listens Tech#play\n * @triggers Tech#timeupdate\n *\/\n trackCurrentTime() {\n if (this.currentTimeInterval) {\n this.stopTrackingCurrentTime();\n }\n this.currentTimeInterval = this.setInterval(function () {\n \/**\n * Triggered at an interval of 250ms to indicated that time is passing in the video.\n *\n * @event Tech#timeupdate\n * @type {Event}\n *\/\n this.trigger({\n type: 'timeupdate',\n target: this,\n manuallyTriggered: true\n });\n\n \/\/ 42 = 24 fps \/\/ 250 is what Webkit uses \/\/ FF uses 15\n }, 250);\n }\n\n \/**\n * Stop the interval function created in {@link Tech#trackCurrentTime} so that the\n * `timeupdate` event is no longer triggered.\n *\n * @listens {Tech#pause}\n *\/\n stopTrackingCurrentTime() {\n this.clearInterval(this.currentTimeInterval);\n\n \/\/ #1002 - if the video ends right before the next timeupdate would happen,\n \/\/ the progress bar won't make it all the way to the end\n this.trigger({\n type: 'timeupdate',\n target: this,\n manuallyTriggered: true\n });\n }\n\n \/**\n * Turn off all event polyfills, clear the `Tech`s {@link AudioTrackList},\n * {@link VideoTrackList}, and {@link TextTrackList}, and dispose of this Tech.\n *\n * @fires Component#dispose\n *\/\n dispose() {\n \/\/ clear out all tracks because we can't reuse them between techs\n this.clearTracks(NORMAL.names);\n\n \/\/ Turn off any manual progress or timeupdate tracking\n if (this.manualProgress) {\n this.manualProgressOff();\n }\n if (this.manualTimeUpdates) {\n this.manualTimeUpdatesOff();\n }\n super.dispose();\n }\n\n \/**\n * Clear out a single `TrackList` or an array of `TrackLists` given their names.\n *\n * > Note: Techs without source handlers should call this between sources for `video`\n * & `audio` tracks. You don't want to use them between tracks!\n *\n * @param {string[]|string} types\n * TrackList names to clear, valid names are `video`, `audio`, and\n * `text`.\n *\/\n clearTracks(types) {\n types = [].concat(types);\n \/\/ clear out all tracks because we can't reuse them between techs\n types.forEach(type => {\n const list = this[`${type}Tracks`]() || [];\n let i = list.length;\n while (i--) {\n const track = list[i];\n if (type === 'text') {\n this.removeRemoteTextTrack(track);\n }\n list.removeTrack(track);\n }\n });\n }\n\n \/**\n * Remove any TextTracks added via addRemoteTextTrack that are\n * flagged for automatic garbage collection\n *\/\n cleanupAutoTextTracks() {\n const list = this.autoRemoteTextTracks_ || [];\n let i = list.length;\n while (i--) {\n const track = list[i];\n this.removeRemoteTextTrack(track);\n }\n }\n\n \/**\n * Reset the tech, which will removes all sources and reset the internal readyState.\n *\n * @abstract\n *\/\n reset() {}\n\n \/**\n * Get the value of `crossOrigin` from the tech.\n *\n * @abstract\n *\n * @see {Html5#crossOrigin}\n *\/\n crossOrigin() {}\n\n \/**\n * Set the value of `crossOrigin` on the tech.\n *\n * @abstract\n *\n * @param {string} crossOrigin the crossOrigin value\n * @see {Html5#setCrossOrigin}\n *\/\n setCrossOrigin() {}\n\n \/**\n * Get or set an error on the Tech.\n *\n * @param {MediaError} [err]\n * Error to set on the Tech\n *\n * @return {MediaError|null}\n * The current error object on the tech, or null if there isn't one.\n *\/\n error(err) {\n if (err !== undefined) {\n this.error_ = new MediaError(err);\n this.trigger('error');\n }\n return this.error_;\n }\n\n \/**\n * Returns the `TimeRange`s that have been played through for the current source.\n *\n * > NOTE: This implementation is incomplete. It does not track the played `TimeRange`.\n * It only checks whether the source has played at all or not.\n *\n * @return {TimeRange}\n * - A single time range if this video has played\n * - An empty set of ranges if not.\n *\/\n played() {\n if (this.hasStarted_) {\n return createTimeRanges$1(0, 0);\n }\n return createTimeRanges$1();\n }\n\n \/**\n * Start playback\n *\n * @abstract\n *\n * @see {Html5#play}\n *\/\n play() {}\n\n \/**\n * Set whether we are scrubbing or not\n *\n * @abstract\n * @param {boolean} _isScrubbing\n * - true for we are currently scrubbing\n * - false for we are no longer scrubbing\n *\n * @see {Html5#setScrubbing}\n *\/\n setScrubbing(_isScrubbing) {}\n\n \/**\n * Get whether we are scrubbing or not\n *\n * @abstract\n *\n * @see {Html5#scrubbing}\n *\/\n scrubbing() {}\n\n \/**\n * Causes a manual time update to occur if {@link Tech#manualTimeUpdatesOn} was\n * previously called.\n *\n * @param {number} _seconds\n * Set the current time of the media to this.\n * @fires Tech#timeupdate\n *\/\n setCurrentTime(_seconds) {\n \/\/ improve the accuracy of manual timeupdates\n if (this.manualTimeUpdates) {\n \/**\n * A manual `timeupdate` event.\n *\n * @event Tech#timeupdate\n * @type {Event}\n *\/\n this.trigger({\n type: 'timeupdate',\n target: this,\n manuallyTriggered: true\n });\n }\n }\n\n \/**\n * Turn on listeners for {@link VideoTrackList}, {@link {AudioTrackList}, and\n * {@link TextTrackList} events.\n *\n * This adds {@link EventTarget~EventListeners} for `addtrack`, and `removetrack`.\n *\n * @fires Tech#audiotrackchange\n * @fires Tech#videotrackchange\n * @fires Tech#texttrackchange\n *\/\n initTrackListeners() {\n \/**\n * Triggered when tracks are added or removed on the Tech {@link AudioTrackList}\n *\n * @event Tech#audiotrackchange\n * @type {Event}\n *\/\n\n \/**\n * Triggered when tracks are added or removed on the Tech {@link VideoTrackList}\n *\n * @event Tech#videotrackchange\n * @type {Event}\n *\/\n\n \/**\n * Triggered when tracks are added or removed on the Tech {@link TextTrackList}\n *\n * @event Tech#texttrackchange\n * @type {Event}\n *\/\n NORMAL.names.forEach(name => {\n const props = NORMAL[name];\n const trackListChanges = () => {\n this.trigger(`${name}trackchange`);\n };\n const tracks = this[props.getterName]();\n tracks.addEventListener('removetrack', trackListChanges);\n tracks.addEventListener('addtrack', trackListChanges);\n this.on('dispose', () => {\n tracks.removeEventListener('removetrack', trackListChanges);\n tracks.removeEventListener('addtrack', trackListChanges);\n });\n });\n }\n\n \/**\n * Emulate TextTracks using vtt.js if necessary\n *\n * @fires Tech#vttjsloaded\n * @fires Tech#vttjserror\n *\/\n addWebVttScript_() {\n if (window.WebVTT) {\n return;\n }\n\n \/\/ Initially, Tech.el_ is a child of a dummy-div wait until the Component system\n \/\/ signals that the Tech is ready at which point Tech.el_ is part of the DOM\n \/\/ before inserting the WebVTT script\n if (document.body.contains(this.el())) {\n \/\/ load via require if available and vtt.js script location was not passed in\n \/\/ as an option. novtt builds will turn the above require call into an empty object\n \/\/ which will cause this if check to always fail.\n if (!this.options_['vtt.js'] && isPlain(browserIndex) && Object.keys(browserIndex).length > 0) {\n this.trigger('vttjsloaded');\n return;\n }\n\n \/\/ load vtt.js via the script location option or the cdn of no location was\n \/\/ passed in\n const script = document.createElement('script');\n script.src = this.options_['vtt.js'] || 'https:\/\/vjs.zencdn.net\/vttjs\/0.14.1\/vtt.min.js';\n script.onload = () => {\n \/**\n * Fired when vtt.js is loaded.\n *\n * @event Tech#vttjsloaded\n * @type {Event}\n *\/\n this.trigger('vttjsloaded');\n };\n script.onerror = () => {\n \/**\n * Fired when vtt.js was not loaded due to an error\n *\n * @event Tech#vttjsloaded\n * @type {Event}\n *\/\n this.trigger('vttjserror');\n };\n this.on('dispose', () => {\n script.onload = null;\n script.onerror = null;\n });\n \/\/ but have not loaded yet and we set it to true before the inject so that\n \/\/ we don't overwrite the injected window.WebVTT if it loads right away\n window.WebVTT = true;\n this.el().parentNode.appendChild(script);\n } else {\n this.ready(this.addWebVttScript_);\n }\n }\n\n \/**\n * Emulate texttracks\n *\n *\/\n emulateTextTracks() {\n const tracks = this.textTracks();\n const remoteTracks = this.remoteTextTracks();\n const handleAddTrack = e => tracks.addTrack(e.track);\n const handleRemoveTrack = e => tracks.removeTrack(e.track);\n remoteTracks.on('addtrack', handleAddTrack);\n remoteTracks.on('removetrack', handleRemoveTrack);\n this.addWebVttScript_();\n const updateDisplay = () => this.trigger('texttrackchange');\n const textTracksChanges = () => {\n updateDisplay();\n for (let i = 0; i < tracks.length; i++) {\n const track = tracks[i];\n track.removeEventListener('cuechange', updateDisplay);\n if (track.mode === 'showing') {\n track.addEventListener('cuechange', updateDisplay);\n }\n }\n };\n textTracksChanges();\n tracks.addEventListener('change', textTracksChanges);\n tracks.addEventListener('addtrack', textTracksChanges);\n tracks.addEventListener('removetrack', textTracksChanges);\n this.on('dispose', function () {\n remoteTracks.off('addtrack', handleAddTrack);\n remoteTracks.off('removetrack', handleRemoveTrack);\n tracks.removeEventListener('change', textTracksChanges);\n tracks.removeEventListener('addtrack', textTracksChanges);\n tracks.removeEventListener('removetrack', textTracksChanges);\n for (let i = 0; i < tracks.length; i++) {\n const track = tracks[i];\n track.removeEventListener('cuechange', updateDisplay);\n }\n });\n }\n\n \/**\n * Create and returns a remote {@link TextTrack} object.\n *\n * @param {string} kind\n * `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata)\n *\n * @param {string} [label]\n * Label to identify the text track\n *\n * @param {string} [language]\n * Two letter language abbreviation\n *\n * @return {TextTrack}\n * The TextTrack that gets created.\n *\/\n addTextTrack(kind, label, language) {\n if (!kind) {\n throw new Error('TextTrack kind is required but was not provided');\n }\n return createTrackHelper(this, kind, label, language);\n }\n\n \/**\n * Create an emulated TextTrack for use by addRemoteTextTrack\n *\n * This is intended to be overridden by classes that inherit from\n * Tech in order to create native or custom TextTracks.\n *\n * @param {Object} options\n * The object should contain the options to initialize the TextTrack with.\n *\n * @param {string} [options.kind]\n * `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata).\n *\n * @param {string} [options.label].\n * Label to identify the text track\n *\n * @param {string} [options.language]\n * Two letter language abbreviation.\n *\n * @return {HTMLTrackElement}\n * The track element that gets created.\n *\/\n createRemoteTextTrack(options) {\n const track = merge$2(options, {\n tech: this\n });\n return new REMOTE.remoteTextEl.TrackClass(track);\n }\n\n \/**\n * Creates a remote text track object and returns an html track element.\n *\n * > Note: This can be an emulated {@link HTMLTrackElement} or a native one.\n *\n * @param {Object} options\n * See {@link Tech#createRemoteTextTrack} for more detailed properties.\n *\n * @param {boolean} [manualCleanup=false]\n * - When false: the TextTrack will be automatically removed from the video\n * element whenever the source changes\n * - When True: The TextTrack will have to be cleaned up manually\n *\n * @return {HTMLTrackElement}\n * An Html Track Element.\n *\n *\/\n addRemoteTextTrack(options = {}, manualCleanup) {\n const htmlTrackElement = this.createRemoteTextTrack(options);\n if (typeof manualCleanup !== 'boolean') {\n manualCleanup = false;\n }\n\n \/\/ store HTMLTrackElement and TextTrack to remote list\n this.remoteTextTrackEls().addTrackElement_(htmlTrackElement);\n this.remoteTextTracks().addTrack(htmlTrackElement.track);\n if (manualCleanup === false) {\n \/\/ create the TextTrackList if it doesn't exist\n this.ready(() => this.autoRemoteTextTracks_.addTrack(htmlTrackElement.track));\n }\n return htmlTrackElement;\n }\n\n \/**\n * Remove a remote text track from the remote `TextTrackList`.\n *\n * @param {TextTrack} track\n * `TextTrack` to remove from the `TextTrackList`\n *\/\n removeRemoteTextTrack(track) {\n const trackElement = this.remoteTextTrackEls().getTrackElementByTrack_(track);\n\n \/\/ remove HTMLTrackElement and TextTrack from remote list\n this.remoteTextTrackEls().removeTrackElement_(trackElement);\n this.remoteTextTracks().removeTrack(track);\n this.autoRemoteTextTracks_.removeTrack(track);\n }\n\n \/**\n * Gets available media playback quality metrics as specified by the W3C's Media\n * Playback Quality API.\n *\n * @see [Spec]{@link https:\/\/wicg.github.io\/media-playback-quality}\n *\n * @return {Object}\n * An object with supported media playback quality metrics\n *\n * @abstract\n *\/\n getVideoPlaybackQuality() {\n return {};\n }\n\n \/**\n * Attempt to create a floating video window always on top of other windows\n * so that users may continue consuming media while they interact with other\n * content sites, or applications on their device.\n *\n * @see [Spec]{@link https:\/\/wicg.github.io\/picture-in-picture}\n *\n * @return {Promise|undefined}\n * A promise with a Picture-in-Picture window if the browser supports\n * Promises (or one was passed in as an option). It returns undefined\n * otherwise.\n *\n * @abstract\n *\/\n requestPictureInPicture() {\n return Promise.reject();\n }\n\n \/**\n * A method to check for the value of the 'disablePictureInPicture' ` tag to control the CORS\n * behavior.\n *\n * @param {string|null} [value]\n * The value to set the `PosterImages`'s crossorigin to. If an argument is\n * given, must be one of `anonymous` or `use-credentials`.\n *\n * @return {string|null|undefined}\n * - The current crossorigin value of the `Player` when getting.\n * - undefined when setting\n *\/\n PosterImage.prototype.crossorigin = PosterImage.prototype.crossOrigin;\n Component$1.registerComponent('PosterImage', PosterImage);\n\n \/**\n * @file text-track-display.js\n *\/\n\n \/** @import Player from '..\/player' *\/\n\n const darkGray = '#222';\n const lightGray = '#ccc';\n const fontMap = {\n monospace: 'monospace',\n sansSerif: 'sans-serif',\n serif: 'serif',\n monospaceSansSerif: '\"Andale Mono\", \"Lucida Console\", monospace',\n monospaceSerif: '\"Courier New\", monospace',\n proportionalSansSerif: 'sans-serif',\n proportionalSerif: 'serif',\n casual: '\"Comic Sans MS\", Impact, fantasy',\n script: '\"Monotype Corsiva\", cursive',\n smallcaps: '\"Andale Mono\", \"Lucida Console\", monospace, sans-serif'\n };\n\n \/**\n * Construct an rgba color from a given hex color code.\n *\n * @param {number} color\n * Hex number for color, like #f0e or #f604e2.\n *\n * @param {number} opacity\n * Value for opacity, 0.0 - 1.0.\n *\n * @return {string}\n * The rgba color that was created, like 'rgba(255, 0, 0, 0.3)'.\n *\/\n function constructColor(color, opacity) {\n let hex;\n if (color.length === 4) {\n \/\/ color looks like \"#f0e\"\n hex = color[1] + color[1] + color[2] + color[2] + color[3] + color[3];\n } else if (color.length === 7) {\n \/\/ color looks like \"#f604e2\"\n hex = color.slice(1);\n } else {\n throw new Error('Invalid color code provided, ' + color + '; must be formatted as e.g. #f0e or #f604e2.');\n }\n return 'rgba(' + parseInt(hex.slice(0, 2), 16) + ',' + parseInt(hex.slice(2, 4), 16) + ',' + parseInt(hex.slice(4, 6), 16) + ',' + opacity + ')';\n }\n\n \/**\n * Try to update the style of a DOM element. Some style changes will throw an error,\n * particularly in IE8. Those should be noops.\n *\n * @param {Element} el\n * The DOM element to be styled.\n *\n * @param {string} style\n * The CSS property on the element that should be styled.\n *\n * @param {string} rule\n * The style rule that should be applied to the property.\n *\n * @private\n *\/\n function tryUpdateStyle(el, style, rule) {\n try {\n el.style[style] = rule;\n } catch (e) {\n \/\/ Satisfies linter.\n return;\n }\n }\n\n \/**\n * Converts the CSS top\/right\/bottom\/left property numeric value to string in pixels.\n *\n * @param {number} position\n * The CSS top\/right\/bottom\/left property value.\n *\n * @return {string}\n * The CSS property value that was created, like '10px'.\n *\n * @private\n *\/\n function getCSSPositionValue(position) {\n return position ? `${position}px` : '';\n }\n\n \/**\n * The component for displaying text track cues.\n *\n * @extends Component\n *\/\n class TextTrackDisplay extends Component$1 {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options]\n * The key\/value store of player options.\n *\n * @param {Function} [ready]\n * The function to call when `TextTrackDisplay` is ready.\n *\/\n constructor(player, options, ready) {\n super(player, options, ready);\n const updateDisplayTextHandler = e => this.updateDisplay(e);\n const updateDisplayHandler = e => {\n this.updateDisplayOverlay();\n this.updateDisplay(e);\n };\n player.on('loadstart', e => this.toggleDisplay(e));\n player.on('texttrackchange', updateDisplayTextHandler);\n player.on('loadedmetadata', e => {\n this.updateDisplayOverlay();\n this.preselectTrack(e);\n });\n\n \/\/ This used to be called during player init, but was causing an error\n \/\/ if a track should show by default and the display hadn't loaded yet.\n \/\/ Should probably be moved to an external track loader when we support\n \/\/ tracks that don't need a display.\n player.ready(bind_(this, function () {\n if (player.tech_ && player.tech_.featuresNativeTextTracks) {\n this.hide();\n return;\n }\n player.on('fullscreenchange', updateDisplayHandler);\n player.on('playerresize', updateDisplayHandler);\n const screenOrientation = window.screen.orientation || window;\n const changeOrientationEvent = window.screen.orientation ? 'change' : 'orientationchange';\n screenOrientation.addEventListener(changeOrientationEvent, updateDisplayHandler);\n player.on('dispose', () => screenOrientation.removeEventListener(changeOrientationEvent, updateDisplayHandler));\n const tracks = this.options_.playerOptions.tracks || [];\n for (let i = 0; i < tracks.length; i++) {\n this.player_.addRemoteTextTrack(tracks[i], true);\n }\n this.preselectTrack();\n }));\n }\n\n \/**\n * Preselect a track following this precedence:\n * - matches the previously selected {@link TextTrack}'s language and kind\n * - matches the previously selected {@link TextTrack}'s language only\n * - is the first default captions track\n * - is the first default descriptions track\n *\n * @listens Player#loadstart\n *\/\n preselectTrack() {\n const modes = {\n captions: 1,\n subtitles: 1\n };\n const trackList = this.player_.textTracks();\n const userPref = this.player_.cache_.selectedLanguage;\n let firstDesc;\n let firstCaptions;\n let preferredTrack;\n for (let i = 0; i < trackList.length; i++) {\n const track = trackList[i];\n if (userPref && userPref.enabled && userPref.language && userPref.language === track.language && track.kind in modes) {\n \/\/ Always choose the track that matches both language and kind\n if (track.kind === userPref.kind) {\n preferredTrack = track;\n \/\/ or choose the first track that matches language\n } else if (!preferredTrack) {\n preferredTrack = track;\n }\n\n \/\/ clear everything if offTextTrackMenuItem was clicked\n } else if (userPref && !userPref.enabled) {\n preferredTrack = null;\n firstDesc = null;\n firstCaptions = null;\n } else if (track.default) {\n if (track.kind === 'descriptions' && !firstDesc) {\n firstDesc = track;\n } else if (track.kind in modes && !firstCaptions) {\n firstCaptions = track;\n }\n }\n }\n\n \/\/ The preferredTrack matches the user preference and takes\n \/\/ precedence over all the other tracks.\n \/\/ So, display the preferredTrack before the first default track\n \/\/ and the subtitles\/captions track before the descriptions track\n if (preferredTrack) {\n preferredTrack.mode = 'showing';\n } else if (firstCaptions) {\n firstCaptions.mode = 'showing';\n } else if (firstDesc) {\n firstDesc.mode = 'showing';\n }\n }\n\n \/**\n * Turn display of {@link TextTrack}'s from the current state into the other state.\n * There are only two states:\n * - 'shown'\n * - 'hidden'\n *\n * @listens Player#loadstart\n *\/\n toggleDisplay() {\n if (this.player_.tech_ && this.player_.tech_.featuresNativeTextTracks) {\n this.hide();\n } else {\n this.show();\n }\n }\n\n \/**\n * Create the {@link Component}'s DOM element.\n *\n * @return {Element}\n * The element that was created.\n *\/\n createEl() {\n return super.createEl('div', {\n className: 'vjs-text-track-display'\n }, {\n 'translate': 'yes',\n 'aria-live': 'off',\n 'aria-atomic': 'true'\n });\n }\n\n \/**\n * Clear all displayed {@link TextTrack}s.\n *\/\n clearDisplay() {\n if (typeof window.WebVTT === 'function') {\n window.WebVTT.processCues(window, [], this.el_);\n }\n }\n\n \/**\n * Update the displayed TextTrack when a either a {@link Player#texttrackchange} or\n * a {@link Player#fullscreenchange} is fired.\n *\n * @listens Player#texttrackchange\n * @listens Player#fullscreenchange\n *\/\n updateDisplay() {\n const tracks = this.player_.textTracks();\n const allowMultipleShowingTracks = this.options_.allowMultipleShowingTracks;\n this.clearDisplay();\n if (allowMultipleShowingTracks) {\n const showingTracks = [];\n for (let i = 0; i < tracks.length; ++i) {\n const track = tracks[i];\n if (track.mode !== 'showing') {\n continue;\n }\n showingTracks.push(track);\n }\n this.updateForTrack(showingTracks);\n return;\n }\n\n \/\/ Track display prioritization model: if multiple tracks are 'showing',\n \/\/ display the first 'subtitles' or 'captions' track which is 'showing',\n \/\/ otherwise display the first 'descriptions' track which is 'showing'\n\n let descriptionsTrack = null;\n let captionsSubtitlesTrack = null;\n let i = tracks.length;\n while (i--) {\n const track = tracks[i];\n if (track.mode === 'showing') {\n if (track.kind === 'descriptions') {\n descriptionsTrack = track;\n } else {\n captionsSubtitlesTrack = track;\n }\n }\n }\n if (captionsSubtitlesTrack) {\n if (this.getAttribute('aria-live') !== 'off') {\n this.setAttribute('aria-live', 'off');\n }\n this.updateForTrack(captionsSubtitlesTrack);\n } else if (descriptionsTrack) {\n if (this.getAttribute('aria-live') !== 'assertive') {\n this.setAttribute('aria-live', 'assertive');\n }\n this.updateForTrack(descriptionsTrack);\n }\n }\n\n \/**\n * Updates the displayed TextTrack to be sure it overlays the video when a either\n * a {@link Player#texttrackchange} or a {@link Player#fullscreenchange} is fired.\n *\/\n updateDisplayOverlay() {\n \/\/ inset-inline and inset-block are not supprted on old chrome, but these are\n \/\/ only likely to be used on TV devices\n if (!this.player_.videoHeight() || !window.CSS.supports('inset-inline: 10px')) {\n return;\n }\n const playerWidth = this.player_.currentWidth();\n const playerHeight = this.player_.currentHeight();\n const playerAspectRatio = playerWidth \/ playerHeight;\n const videoAspectRatio = this.player_.videoWidth() \/ this.player_.videoHeight();\n let insetInlineMatch = 0;\n let insetBlockMatch = 0;\n if (Math.abs(playerAspectRatio - videoAspectRatio) > 0.1) {\n if (playerAspectRatio > videoAspectRatio) {\n insetInlineMatch = Math.round((playerWidth - playerHeight * videoAspectRatio) \/ 2);\n } else {\n insetBlockMatch = Math.round((playerHeight - playerWidth \/ videoAspectRatio) \/ 2);\n }\n }\n tryUpdateStyle(this.el_, 'insetInline', getCSSPositionValue(insetInlineMatch));\n tryUpdateStyle(this.el_, 'insetBlock', getCSSPositionValue(insetBlockMatch));\n }\n\n \/**\n * Style {@Link TextTrack} activeCues according to {@Link TextTrackSettings}.\n *\n * @param {TextTrack} track\n * Text track object containing active cues to style.\n *\/\n updateDisplayState(track) {\n const overrides = this.player_.textTrackSettings.getValues();\n const cues = track.activeCues;\n let i = cues.length;\n while (i--) {\n const cue = cues[i];\n if (!cue) {\n continue;\n }\n const cueDiv = cue.displayState;\n if (overrides.color) {\n cueDiv.firstChild.style.color = overrides.color;\n }\n if (overrides.textOpacity) {\n tryUpdateStyle(cueDiv.firstChild, 'color', constructColor(overrides.color || '#fff', overrides.textOpacity));\n }\n if (overrides.backgroundColor) {\n cueDiv.firstChild.style.backgroundColor = overrides.backgroundColor;\n }\n if (overrides.backgroundOpacity) {\n tryUpdateStyle(cueDiv.firstChild, 'backgroundColor', constructColor(overrides.backgroundColor || '#000', overrides.backgroundOpacity));\n }\n if (overrides.windowColor) {\n if (overrides.windowOpacity) {\n tryUpdateStyle(cueDiv, 'backgroundColor', constructColor(overrides.windowColor, overrides.windowOpacity));\n } else {\n cueDiv.style.backgroundColor = overrides.windowColor;\n }\n }\n if (overrides.edgeStyle) {\n if (overrides.edgeStyle === 'dropshadow') {\n cueDiv.firstChild.style.textShadow = `2px 2px 3px ${darkGray}, 2px 2px 4px ${darkGray}, 2px 2px 5px ${darkGray}`;\n } else if (overrides.edgeStyle === 'raised') {\n cueDiv.firstChild.style.textShadow = `1px 1px ${darkGray}, 2px 2px ${darkGray}, 3px 3px ${darkGray}`;\n } else if (overrides.edgeStyle === 'depressed') {\n cueDiv.firstChild.style.textShadow = `1px 1px ${lightGray}, 0 1px ${lightGray}, -1px -1px ${darkGray}, 0 -1px ${darkGray}`;\n } else if (overrides.edgeStyle === 'uniform') {\n cueDiv.firstChild.style.textShadow = `0 0 4px ${darkGray}, 0 0 4px ${darkGray}, 0 0 4px ${darkGray}, 0 0 4px ${darkGray}`;\n }\n }\n if (overrides.fontPercent && overrides.fontPercent !== 1) {\n const fontSize = window.parseFloat(cueDiv.style.fontSize);\n cueDiv.style.fontSize = fontSize * overrides.fontPercent + 'px';\n cueDiv.style.height = 'auto';\n cueDiv.style.top = 'auto';\n }\n if (overrides.fontFamily && overrides.fontFamily !== 'default') {\n if (overrides.fontFamily === 'small-caps') {\n cueDiv.firstChild.style.fontVariant = 'small-caps';\n } else {\n cueDiv.firstChild.style.fontFamily = fontMap[overrides.fontFamily];\n }\n }\n }\n }\n\n \/**\n * Add an {@link TextTrack} to to the {@link Tech}s {@link TextTrackList}.\n *\n * @param {TextTrack|TextTrack[]} tracks\n * Text track object or text track array to be added to the list.\n *\/\n updateForTrack(tracks) {\n if (!Array.isArray(tracks)) {\n tracks = [tracks];\n }\n if (typeof window.WebVTT !== 'function' || tracks.every(track => {\n return !track.activeCues;\n })) {\n return;\n }\n const cues = [];\n\n \/\/ push all active track cues\n for (let i = 0; i < tracks.length; ++i) {\n const track = tracks[i];\n for (let j = 0; j < track.activeCues.length; ++j) {\n cues.push(track.activeCues[j]);\n }\n }\n\n \/\/ removes all cues before it processes new ones\n window.WebVTT.processCues(window, cues, this.el_);\n\n \/\/ add unique class to each language text track & add settings styling if necessary\n for (let i = 0; i < tracks.length; ++i) {\n const track = tracks[i];\n for (let j = 0; j < track.activeCues.length; ++j) {\n const cueEl = track.activeCues[j].displayState;\n addClass(cueEl, 'vjs-text-track-cue', 'vjs-text-track-cue-' + (track.language ? track.language : i));\n if (track.language) {\n setAttribute(cueEl, 'lang', track.language);\n }\n }\n if (this.player_.textTrackSettings) {\n this.updateDisplayState(track);\n }\n }\n }\n }\n Component$1.registerComponent('TextTrackDisplay', TextTrackDisplay);\n\n \/**\n * @file loading-spinner.js\n *\/\n\n \/**\n * A loading spinner for use during waiting\/loading events.\n *\n * @extends Component\n *\/\n class LoadingSpinner extends Component$1 {\n \/**\n * Create the `LoadingSpinner`s DOM element.\n *\n * @return {Element}\n * The dom element that gets created.\n *\/\n createEl() {\n const isAudio = this.player_.isAudio();\n const playerType = this.localize(isAudio ? 'Audio Player' : 'Video Player');\n const controlText = createEl('span', {\n className: 'vjs-control-text',\n textContent: this.localize('{1} is loading.', [playerType])\n });\n const el = super.createEl('div', {\n className: 'vjs-loading-spinner',\n dir: 'ltr'\n });\n el.appendChild(controlText);\n return el;\n }\n\n \/**\n * Update control text on languagechange\n *\/\n handleLanguagechange() {\n this.$('.vjs-control-text').textContent = this.localize('{1} is loading.', [this.player_.isAudio() ? 'Audio Player' : 'Video Player']);\n }\n }\n Component$1.registerComponent('LoadingSpinner', LoadingSpinner);\n\n \/**\n * @file button.js\n *\/\n\n \/**\n * Base class for all buttons.\n *\n * @extends ClickableComponent\n *\/\n class Button extends ClickableComponent {\n \/**\n * Create the `Button`s DOM element.\n *\n * @param {string} [tag=\"button\"]\n * The element's node type. This argument is IGNORED: no matter what\n * is passed, it will always create a `button` element.\n *\n * @param {Object} [props={}]\n * An object of properties that should be set on the element.\n *\n * @param {Object} [attributes={}]\n * An object of attributes that should be set on the element.\n *\n * @return {Element}\n * The element that gets created.\n *\/\n createEl(tag, props = {}, attributes = {}) {\n tag = 'button';\n props = Object.assign({\n className: this.buildCSSClass()\n }, props);\n\n \/\/ Add attributes for button element\n attributes = Object.assign({\n \/\/ Necessary since the default button type is \"submit\"\n type: 'button'\n }, attributes);\n const el = createEl(tag, props, attributes);\n if (!this.player_.options_.experimentalSvgIcons) {\n el.appendChild(createEl('span', {\n className: 'vjs-icon-placeholder'\n }, {\n 'aria-hidden': true\n }));\n }\n this.createControlTextEl(el);\n return el;\n }\n\n \/**\n * Add a child `Component` inside of this `Button`.\n *\n * @param {string|Component} child\n * The name or instance of a child to add.\n *\n * @param {Object} [options={}]\n * The key\/value store of options that will get passed to children of\n * the child.\n *\n * @return {Component}\n * The `Component` that gets added as a child. When using a string the\n * `Component` will get created by this process.\n *\n * @deprecated since version 5\n *\/\n addChild(child, options = {}) {\n const className = this.constructor.name;\n log$1.warn(`Adding an actionable (user controllable) child to a Button (${className}) is not supported; use a ClickableComponent instead.`);\n\n \/\/ Avoid the error message generated by ClickableComponent's addChild method\n return Component$1.prototype.addChild.call(this, child, options);\n }\n\n \/**\n * Enable the `Button` element so that it can be activated or clicked. Use this with\n * {@link Button#disable}.\n *\/\n enable() {\n super.enable();\n this.el_.removeAttribute('disabled');\n }\n\n \/**\n * Disable the `Button` element so that it cannot be activated or clicked. Use this with\n * {@link Button#enable}.\n *\/\n disable() {\n super.disable();\n this.el_.setAttribute('disabled', 'disabled');\n }\n\n \/**\n * This gets called when a `Button` has focus and `keydown` is triggered via a key\n * press.\n *\n * @param {KeyboardEvent} event\n * The event that caused this function to get called.\n *\n * @listens keydown\n *\/\n handleKeyDown(event) {\n \/\/ Ignore Space or Enter key operation, which is handled by the browser for\n \/\/ a button - though not for its super class, ClickableComponent. Also,\n \/\/ prevent the event from propagating through the DOM and triggering Player\n \/\/ hotkeys. We do not preventDefault here because we _want_ the browser to\n \/\/ handle it.\n if (event.key === ' ' || event.key === 'Enter') {\n event.stopPropagation();\n return;\n }\n\n \/\/ Pass keypress handling up for unsupported keys\n super.handleKeyDown(event);\n }\n }\n Component$1.registerComponent('Button', Button);\n\n \/**\n * @file big-play-button.js\n *\/\n\n \/**\n * The initial play button that shows before the video has played. The hiding of the\n * `BigPlayButton` get done via CSS and `Player` states.\n *\n * @extends Button\n *\/\n class BigPlayButton extends Button {\n constructor(player, options) {\n super(player, options);\n this.mouseused_ = false;\n this.setIcon('play');\n this.on('mousedown', e => this.handleMouseDown(e));\n }\n\n \/**\n * Builds the default DOM `className`.\n *\n * @return {string}\n * The DOM `className` for this object. Always returns 'vjs-big-play-button'.\n *\/\n buildCSSClass() {\n return 'vjs-big-play-button';\n }\n\n \/**\n * This gets called when a `BigPlayButton` \"clicked\". See {@link ClickableComponent}\n * for more detailed information on what a click can be.\n *\n * @param {KeyboardEvent|MouseEvent|TouchEvent} event\n * The `keydown`, `tap`, or `click` event that caused this function to be\n * called.\n *\n * @listens tap\n * @listens click\n *\/\n handleClick(event) {\n const playPromise = this.player_.play();\n\n \/\/ exit early if clicked via the mouse\n if (this.mouseused_ && 'clientX' in event && 'clientY' in event) {\n silencePromise(playPromise);\n if (this.player_.tech(true)) {\n this.player_.tech(true).focus();\n }\n return;\n }\n const cb = this.player_.getChild('controlBar');\n const playToggle = cb && cb.getChild('playToggle');\n if (!playToggle) {\n this.player_.tech(true).focus();\n return;\n }\n const playFocus = () => playToggle.focus();\n if (isPromise(playPromise)) {\n playPromise.then(playFocus, () => {});\n } else {\n this.setTimeout(playFocus, 1);\n }\n }\n\n \/**\n * Event handler that is called when a `BigPlayButton` receives a\n * `keydown` event.\n *\n * @param {KeyboardEvent} event\n * The `keydown` event that caused this function to be called.\n *\n * @listens keydown\n *\/\n handleKeyDown(event) {\n this.mouseused_ = false;\n super.handleKeyDown(event);\n }\n\n \/**\n * Handle `mousedown` events on the `BigPlayButton`.\n *\n * @param {MouseEvent} event\n * `mousedown` or `touchstart` event that triggered this function\n *\n * @listens mousedown\n *\/\n handleMouseDown(event) {\n this.mouseused_ = true;\n }\n }\n\n \/**\n * The text that should display over the `BigPlayButton`s controls. Added to for localization.\n *\n * @type {string}\n * @protected\n *\/\n BigPlayButton.prototype.controlText_ = 'Play Video';\n Component$1.registerComponent('BigPlayButton', BigPlayButton);\n\n \/**\n * @file close-button.js\n *\/\n\n \/** @import Player from '.\/player' *\/\n\n \/**\n * The `CloseButton` is a `{@link Button}` that fires a `close` event when\n * it gets clicked.\n *\n * @extends Button\n *\/\n class CloseButton extends Button {\n \/**\n * Creates an instance of the this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options]\n * The key\/value store of player options.\n *\/\n constructor(player, options) {\n super(player, options);\n this.setIcon('cancel');\n this.controlText(options && options.controlText || this.localize('Close'));\n }\n\n \/**\n * Builds the default DOM `className`.\n *\n * @return {string}\n * The DOM `className` for this object.\n *\/\n buildCSSClass() {\n return `vjs-close-button ${super.buildCSSClass()}`;\n }\n\n \/**\n * This gets called when a `CloseButton` gets clicked. See\n * {@link ClickableComponent#handleClick} for more information on when\n * this will be triggered\n *\n * @param {Event} event\n * The `keydown`, `tap`, or `click` event that caused this function to be\n * called.\n *\n * @listens tap\n * @listens click\n * @fires CloseButton#close\n *\/\n handleClick(event) {\n \/**\n * Triggered when the a `CloseButton` is clicked.\n *\n * @event CloseButton#close\n * @type {Event}\n *\n * @property {boolean} [bubbles=false]\n * set to false so that the close event does not\n * bubble up to parents if there is no listener\n *\/\n this.trigger({\n type: 'close',\n bubbles: false\n });\n }\n \/**\n * Event handler that is called when a `CloseButton` receives a\n * `keydown` event.\n *\n * By default, if the key is Esc, it will trigger a `click` event.\n *\n * @param {KeyboardEvent} event\n * The `keydown` event that caused this function to be called.\n *\n * @listens keydown\n *\/\n handleKeyDown(event) {\n \/\/ Esc button will trigger `click` event\n if (event.key === 'Escape') {\n event.preventDefault();\n event.stopPropagation();\n this.trigger('click');\n } else {\n \/\/ Pass keypress handling up for unsupported keys\n super.handleKeyDown(event);\n }\n }\n }\n Component$1.registerComponent('CloseButton', CloseButton);\n\n \/**\n * @file play-toggle.js\n *\/\n\n \/** @import Player from '.\/player' *\/\n\n \/**\n * Button to toggle between play and pause.\n *\n * @extends Button\n *\/\n class PlayToggle extends Button {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options={}]\n * The key\/value store of player options.\n *\/\n constructor(player, options = {}) {\n super(player, options);\n\n \/\/ show or hide replay icon\n options.replay = options.replay === undefined || options.replay;\n this.setIcon('play');\n this.on(player, 'play', e => this.handlePlay(e));\n this.on(player, 'pause', e => this.handlePause(e));\n if (options.replay) {\n this.on(player, 'ended', e => this.handleEnded(e));\n }\n }\n\n \/**\n * Builds the default DOM `className`.\n *\n * @return {string}\n * The DOM `className` for this object.\n *\/\n buildCSSClass() {\n return `vjs-play-control ${super.buildCSSClass()}`;\n }\n\n \/**\n * This gets called when an `PlayToggle` is \"clicked\". See\n * {@link ClickableComponent} for more detailed information on what a click can be.\n *\n * @param {Event} [event]\n * The `keydown`, `tap`, or `click` event that caused this function to be\n * called.\n *\n * @listens tap\n * @listens click\n *\/\n handleClick(event) {\n if (this.player_.paused()) {\n silencePromise(this.player_.play());\n } else {\n this.player_.pause();\n }\n }\n\n \/**\n * This gets called once after the video has ended and the user seeks so that\n * we can change the replay button back to a play button.\n *\n * @param {Event} [event]\n * The event that caused this function to run.\n *\n * @listens Player#seeked\n *\/\n handleSeeked(event) {\n this.removeClass('vjs-ended');\n if (this.player_.paused()) {\n this.handlePause(event);\n } else {\n this.handlePlay(event);\n }\n }\n\n \/**\n * Add the vjs-playing class to the element so it can change appearance.\n *\n * @param {Event} [event]\n * The event that caused this function to run.\n *\n * @listens Player#play\n *\/\n handlePlay(event) {\n this.removeClass('vjs-ended', 'vjs-paused');\n this.addClass('vjs-playing');\n \/\/ change the button text to \"Pause\"\n this.setIcon('pause');\n this.controlText('Pause');\n }\n\n \/**\n * Add the vjs-paused class to the element so it can change appearance.\n *\n * @param {Event} [event]\n * The event that caused this function to run.\n *\n * @listens Player#pause\n *\/\n handlePause(event) {\n this.removeClass('vjs-playing');\n this.addClass('vjs-paused');\n \/\/ change the button text to \"Play\"\n this.setIcon('play');\n this.controlText('Play');\n }\n\n \/**\n * Add the vjs-ended class to the element so it can change appearance\n *\n * @param {Event} [event]\n * The event that caused this function to run.\n *\n * @listens Player#ended\n *\/\n handleEnded(event) {\n this.removeClass('vjs-playing');\n this.addClass('vjs-ended');\n \/\/ change the button text to \"Replay\"\n this.setIcon('replay');\n this.controlText('Replay');\n\n \/\/ on the next seek remove the replay button\n this.one(this.player_, 'seeked', e => this.handleSeeked(e));\n }\n }\n\n \/**\n * The text that should display over the `PlayToggle`s controls. Added for localization.\n *\n * @type {string}\n * @protected\n *\/\n PlayToggle.prototype.controlText_ = 'Play';\n Component$1.registerComponent('PlayToggle', PlayToggle);\n\n \/**\n * @file time-display.js\n *\/\n\n \/** @import Player from '..\/..\/player' *\/\n\n \/**\n * Displays time information about the video\n *\n * @extends Component\n *\/\n class TimeDisplay extends Component$1 {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options]\n * The key\/value store of player options.\n *\/\n constructor(player, options) {\n super(player, options);\n this.on(player, ['timeupdate', 'ended', 'seeking'], e => this.update(e));\n this.updateTextNode_();\n }\n\n \/**\n * Create the `Component`'s DOM element\n *\n * @return {Element}\n * The element that was created.\n *\/\n createEl() {\n const className = this.buildCSSClass();\n const el = super.createEl('div', {\n className: `${className} vjs-time-control vjs-control`\n });\n const span = createEl('span', {\n className: 'vjs-control-text',\n textContent: `${this.localize(this.labelText_)}\\u00a0`\n }, {\n role: 'presentation'\n });\n el.appendChild(span);\n this.contentEl_ = createEl('span', {\n className: `${className}-display`\n }, {\n \/\/ span elements have no implicit role, but some screen readers (notably VoiceOver)\n \/\/ treat them as a break between items in the DOM when using arrow keys\n \/\/ (or left-to-right swipes on iOS) to read contents of a page. Using\n \/\/ role='presentation' causes VoiceOver to NOT treat this span as a break.\n role: 'presentation'\n });\n el.appendChild(this.contentEl_);\n return el;\n }\n dispose() {\n this.contentEl_ = null;\n this.textNode_ = null;\n super.dispose();\n }\n\n \/**\n * Updates the displayed time according to the `updateContent` function which is defined in the child class.\n *\n * @param {Event} [event]\n * The `timeupdate`, `ended` or `seeking` (if enableSmoothSeeking is true) event that caused this function to be called.\n *\/\n update(event) {\n if (!this.player_.options_.enableSmoothSeeking && event.type === 'seeking') {\n return;\n }\n this.updateContent(event);\n }\n\n \/**\n * Updates the time display text node with a new time\n *\n * @param {number} [time=0] the time to update to\n *\n * @private\n *\/\n updateTextNode_(time = 0) {\n time = formatTime(time);\n if (this.formattedTime_ === time) {\n return;\n }\n this.formattedTime_ = time;\n this.requestNamedAnimationFrame('TimeDisplay#updateTextNode_', () => {\n if (!this.contentEl_) {\n return;\n }\n let oldNode = this.textNode_;\n if (oldNode && this.contentEl_.firstChild !== oldNode) {\n oldNode = null;\n log$1.warn('TimeDisplay#updateTextnode_: Prevented replacement of text node element since it was no longer a child of this node. Appending a new node instead.');\n }\n this.textNode_ = document.createTextNode(this.formattedTime_);\n if (!this.textNode_) {\n return;\n }\n if (oldNode) {\n this.contentEl_.replaceChild(this.textNode_, oldNode);\n } else {\n this.contentEl_.appendChild(this.textNode_);\n }\n });\n }\n\n \/**\n * To be filled out in the child class, should update the displayed time\n * in accordance with the fact that the current time has changed.\n *\n * @param {Event} [event]\n * The `timeupdate` event that caused this to run.\n *\n * @listens Player#timeupdate\n *\/\n updateContent(event) {}\n }\n\n \/**\n * The text that is added to the `TimeDisplay` for screen reader users.\n *\n * @type {string}\n * @private\n *\/\n TimeDisplay.prototype.labelText_ = 'Time';\n\n \/**\n * The text that should display over the `TimeDisplay`s controls. Added to for localization.\n *\n * @type {string}\n * @protected\n *\n * @deprecated in v7; controlText_ is not used in non-active display Components\n *\/\n TimeDisplay.prototype.controlText_ = 'Time';\n Component$1.registerComponent('TimeDisplay', TimeDisplay);\n\n \/**\n * @file current-time-display.js\n *\/\n\n \/**\n * Displays the current time\n *\n * @extends Component\n *\/\n class CurrentTimeDisplay extends TimeDisplay {\n \/**\n * Builds the default DOM `className`.\n *\n * @return {string}\n * The DOM `className` for this object.\n *\/\n buildCSSClass() {\n return 'vjs-current-time';\n }\n\n \/**\n * Update current time display\n *\n * @param {Event} [event]\n * The `timeupdate` event that caused this function to run.\n *\n * @listens Player#timeupdate\n *\/\n updateContent(event) {\n \/\/ Allows for smooth scrubbing, when player can't keep up.\n let time;\n if (this.player_.ended()) {\n time = this.player_.duration();\n } else {\n time = this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime();\n }\n this.updateTextNode_(time);\n }\n }\n\n \/**\n * The text that is added to the `CurrentTimeDisplay` for screen reader users.\n *\n * @type {string}\n * @private\n *\/\n CurrentTimeDisplay.prototype.labelText_ = 'Current Time';\n\n \/**\n * The text that should display over the `CurrentTimeDisplay`s controls. Added to for localization.\n *\n * @type {string}\n * @protected\n *\n * @deprecated in v7; controlText_ is not used in non-active display Components\n *\/\n CurrentTimeDisplay.prototype.controlText_ = 'Current Time';\n Component$1.registerComponent('CurrentTimeDisplay', CurrentTimeDisplay);\n\n \/**\n * @file duration-display.js\n *\/\n\n \/** @import Player from '..\/..\/player' *\/\n\n \/**\n * Displays the duration\n *\n * @extends Component\n *\/\n class DurationDisplay extends TimeDisplay {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options]\n * The key\/value store of player options.\n *\/\n constructor(player, options) {\n super(player, options);\n const updateContent = e => this.updateContent(e);\n\n \/\/ we do not want to\/need to throttle duration changes,\n \/\/ as they should always display the changed duration as\n \/\/ it has changed\n this.on(player, 'durationchange', updateContent);\n\n \/\/ Listen to loadstart because the player duration is reset when a new media element is loaded,\n \/\/ but the durationchange on the user agent will not fire.\n \/\/ @see [Spec]{@link https:\/\/www.w3.org\/TR\/2011\/WD-html5-20110113\/video.html#media-element-load-algorithm}\n this.on(player, 'loadstart', updateContent);\n\n \/\/ Also listen for timeupdate (in the parent) and loadedmetadata because removing those\n \/\/ listeners could have broken dependent applications\/libraries. These\n \/\/ can likely be removed for 7.0.\n this.on(player, 'loadedmetadata', updateContent);\n }\n\n \/**\n * Builds the default DOM `className`.\n *\n * @return {string}\n * The DOM `className` for this object.\n *\/\n buildCSSClass() {\n return 'vjs-duration';\n }\n\n \/**\n * Update duration time display.\n *\n * @param {Event} [event]\n * The `durationchange`, `timeupdate`, or `loadedmetadata` event that caused\n * this function to be called.\n *\n * @listens Player#durationchange\n * @listens Player#timeupdate\n * @listens Player#loadedmetadata\n *\/\n updateContent(event) {\n const duration = this.player_.duration();\n this.updateTextNode_(duration);\n }\n }\n\n \/**\n * The text that is added to the `DurationDisplay` for screen reader users.\n *\n * @type {string}\n * @private\n *\/\n DurationDisplay.prototype.labelText_ = 'Duration';\n\n \/**\n * The text that should display over the `DurationDisplay`s controls. Added to for localization.\n *\n * @type {string}\n * @protected\n *\n * @deprecated in v7; controlText_ is not used in non-active display Components\n *\/\n DurationDisplay.prototype.controlText_ = 'Duration';\n Component$1.registerComponent('DurationDisplay', DurationDisplay);\n\n \/**\n * @file time-divider.js\n *\/\n\n \/**\n * The separator between the current time and duration.\n * Can be hidden if it's not needed in the design.\n *\n * @extends Component\n *\/\n class TimeDivider extends Component$1 {\n \/**\n * Create the component's DOM element\n *\n * @return {Element}\n * The element that was created.\n *\/\n createEl() {\n const el = super.createEl('div', {\n className: 'vjs-time-control vjs-time-divider'\n }, {\n \/\/ this element and its contents can be hidden from assistive techs since\n \/\/ it is made extraneous by the announcement of the control text\n \/\/ for the current time and duration displays\n 'aria-hidden': true\n });\n const div = super.createEl('div');\n const span = super.createEl('span', {\n textContent: '\/'\n });\n div.appendChild(span);\n el.appendChild(div);\n return el;\n }\n }\n Component$1.registerComponent('TimeDivider', TimeDivider);\n\n \/**\n * @file remaining-time-display.js\n *\/\n\n \/** @import Player from '..\/..\/player' *\/\n\n \/**\n * Displays the time left in the video\n *\n * @extends Component\n *\/\n class RemainingTimeDisplay extends TimeDisplay {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options]\n * The key\/value store of player options.\n *\/\n constructor(player, options) {\n super(player, options);\n this.on(player, 'durationchange', e => this.updateContent(e));\n }\n\n \/**\n * Builds the default DOM `className`.\n *\n * @return {string}\n * The DOM `className` for this object.\n *\/\n buildCSSClass() {\n return 'vjs-remaining-time';\n }\n\n \/**\n * Create the `Component`'s DOM element with the \"minus\" character prepend to the time\n *\n * @return {Element}\n * The element that was created.\n *\/\n createEl() {\n const el = super.createEl();\n if (this.options_.displayNegative !== false) {\n el.insertBefore(createEl('span', {}, {\n 'aria-hidden': true\n }, '-'), this.contentEl_);\n }\n return el;\n }\n\n \/**\n * Update remaining time display.\n *\n * @param {Event} [event]\n * The `timeupdate` or `durationchange` event that caused this to run.\n *\n * @listens Player#timeupdate\n * @listens Player#durationchange\n *\/\n updateContent(event) {\n if (typeof this.player_.duration() !== 'number') {\n return;\n }\n let time;\n\n \/\/ @deprecated We should only use remainingTimeDisplay\n \/\/ as of video.js 7\n if (this.player_.ended()) {\n time = 0;\n } else if (this.player_.remainingTimeDisplay) {\n time = this.player_.remainingTimeDisplay();\n } else {\n time = this.player_.remainingTime();\n }\n this.updateTextNode_(time);\n }\n }\n\n \/**\n * The text that is added to the `RemainingTimeDisplay` for screen reader users.\n *\n * @type {string}\n * @private\n *\/\n RemainingTimeDisplay.prototype.labelText_ = 'Remaining Time';\n\n \/**\n * The text that should display over the `RemainingTimeDisplay`s controls. Added to for localization.\n *\n * @type {string}\n * @protected\n *\n * @deprecated in v7; controlText_ is not used in non-active display Components\n *\/\n RemainingTimeDisplay.prototype.controlText_ = 'Remaining Time';\n Component$1.registerComponent('RemainingTimeDisplay', RemainingTimeDisplay);\n\n \/**\n * @file live-display.js\n *\/\n\n \/** @import Player from '.\/player' *\/\n\n \/\/ TODO - Future make it click to snap to live\n\n \/**\n * Displays the live indicator when duration is Infinity.\n *\n * @extends Component\n *\/\n class LiveDisplay extends Component$1 {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options]\n * The key\/value store of player options.\n *\/\n constructor(player, options) {\n super(player, options);\n this.updateShowing();\n this.on(this.player(), 'durationchange', e => this.updateShowing(e));\n }\n\n \/**\n * Create the `Component`'s DOM element\n *\n * @return {Element}\n * The element that was created.\n *\/\n createEl() {\n const el = super.createEl('div', {\n className: 'vjs-live-control vjs-control'\n });\n this.contentEl_ = createEl('div', {\n className: 'vjs-live-display'\n }, {\n 'aria-live': 'off'\n });\n this.contentEl_.appendChild(createEl('span', {\n className: 'vjs-control-text',\n textContent: `${this.localize('Stream Type')}\\u00a0`\n }));\n this.contentEl_.appendChild(document.createTextNode(this.localize('LIVE')));\n el.appendChild(this.contentEl_);\n return el;\n }\n dispose() {\n this.contentEl_ = null;\n super.dispose();\n }\n\n \/**\n * Check the duration to see if the LiveDisplay should be showing or not. Then show\/hide\n * it accordingly\n *\n * @param {Event} [event]\n * The {@link Player#durationchange} event that caused this function to run.\n *\n * @listens Player#durationchange\n *\/\n updateShowing(event) {\n if (this.player().duration() === Infinity) {\n this.show();\n } else {\n this.hide();\n }\n }\n }\n Component$1.registerComponent('LiveDisplay', LiveDisplay);\n\n \/**\n * @file seek-to-live.js\n *\/\n\n \/** @import Player from '.\/player' *\/\n\n \/**\n * Displays the live indicator when duration is Infinity.\n *\n * @extends Component\n *\/\n class SeekToLive extends Button {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options]\n * The key\/value store of player options.\n *\/\n constructor(player, options) {\n super(player, options);\n this.updateLiveEdgeStatus();\n if (this.player_.liveTracker) {\n this.updateLiveEdgeStatusHandler_ = e => this.updateLiveEdgeStatus(e);\n this.on(this.player_.liveTracker, 'liveedgechange', this.updateLiveEdgeStatusHandler_);\n }\n }\n\n \/**\n * Create the `Component`'s DOM element\n *\n * @return {Element}\n * The element that was created.\n *\/\n createEl() {\n const el = super.createEl('button', {\n className: 'vjs-seek-to-live-control vjs-control'\n });\n this.setIcon('circle', el);\n this.textEl_ = createEl('span', {\n className: 'vjs-seek-to-live-text',\n textContent: this.localize('LIVE')\n }, {\n 'aria-hidden': 'true'\n });\n el.appendChild(this.textEl_);\n return el;\n }\n\n \/**\n * Update the state of this button if we are at the live edge\n * or not\n *\/\n updateLiveEdgeStatus() {\n \/\/ default to live edge\n if (!this.player_.liveTracker || this.player_.liveTracker.atLiveEdge()) {\n this.setAttribute('aria-disabled', true);\n this.addClass('vjs-at-live-edge');\n this.controlText('Seek to live, currently playing live');\n } else {\n this.setAttribute('aria-disabled', false);\n this.removeClass('vjs-at-live-edge');\n this.controlText('Seek to live, currently behind live');\n }\n }\n\n \/**\n * On click bring us as near to the live point as possible.\n * This requires that we wait for the next `live-seekable-change`\n * event which will happen every segment length seconds.\n *\/\n handleClick() {\n this.player_.liveTracker.seekToLiveEdge();\n }\n\n \/**\n * Dispose of the element and stop tracking\n *\/\n dispose() {\n if (this.player_.liveTracker) {\n this.off(this.player_.liveTracker, 'liveedgechange', this.updateLiveEdgeStatusHandler_);\n }\n this.textEl_ = null;\n super.dispose();\n }\n }\n \/**\n * The text that should display over the `SeekToLive`s control. Added for localization.\n *\n * @type {string}\n * @protected\n *\/\n SeekToLive.prototype.controlText_ = 'Seek to live, currently playing live';\n Component$1.registerComponent('SeekToLive', SeekToLive);\n\n \/**\n * @file num.js\n * @module num\n *\/\n\n \/**\n * Keep a number between a min and a max value\n *\n * @param {number} number\n * The number to clamp\n *\n * @param {number} min\n * The minimum value\n * @param {number} max\n * The maximum value\n *\n * @return {number}\n * the clamped number\n *\/\n function clamp(number, min, max) {\n number = Number(number);\n return Math.min(max, Math.max(min, isNaN(number) ? min : number));\n }\n\n var Num = \/*#__PURE__*\/Object.freeze({\n __proto__: null,\n clamp: clamp\n });\n\n \/**\n * @file slider.js\n *\/\n\n \/** @import Player from '..\/player' *\/\n\n \/**\n * The base functionality for a slider. Can be vertical or horizontal.\n * For instance the volume bar or the seek bar on a video is a slider.\n *\n * @extends Component\n *\/\n class Slider extends Component$1 {\n \/**\n * Create an instance of this class\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options]\n * The key\/value store of player options.\n *\/\n constructor(player, options) {\n super(player, options);\n this.handleMouseDown_ = e => this.handleMouseDown(e);\n this.handleMouseUp_ = e => this.handleMouseUp(e);\n this.handleKeyDown_ = e => this.handleKeyDown(e);\n this.handleClick_ = e => this.handleClick(e);\n this.handleMouseMove_ = e => this.handleMouseMove(e);\n this.update_ = e => this.update(e);\n\n \/\/ Set property names to bar to match with the child Slider class is looking for\n this.bar = this.getChild(this.options_.barName);\n\n \/\/ Set a horizontal or vertical class on the slider depending on the slider type\n this.vertical(!!this.options_.vertical);\n this.enable();\n }\n\n \/**\n * Are controls are currently enabled for this slider or not.\n *\n * @return {boolean}\n * true if controls are enabled, false otherwise\n *\/\n enabled() {\n return this.enabled_;\n }\n\n \/**\n * Enable controls for this slider if they are disabled\n *\/\n enable() {\n if (this.enabled()) {\n return;\n }\n this.on('mousedown', this.handleMouseDown_);\n this.on('touchstart', this.handleMouseDown_);\n this.on('keydown', this.handleKeyDown_);\n this.on('click', this.handleClick_);\n\n \/\/ TODO: deprecated, controlsvisible does not seem to be fired\n this.on(this.player_, 'controlsvisible', this.update);\n if (this.playerEvent) {\n this.on(this.player_, this.playerEvent, this.update);\n }\n this.removeClass('disabled');\n this.setAttribute('tabindex', 0);\n this.enabled_ = true;\n }\n\n \/**\n * Disable controls for this slider if they are enabled\n *\/\n disable() {\n if (!this.enabled()) {\n return;\n }\n const doc = this.bar.el_.ownerDocument;\n this.off('mousedown', this.handleMouseDown_);\n this.off('touchstart', this.handleMouseDown_);\n this.off('keydown', this.handleKeyDown_);\n this.off('click', this.handleClick_);\n this.off(this.player_, 'controlsvisible', this.update_);\n this.off(doc, 'mousemove', this.handleMouseMove_);\n this.off(doc, 'mouseup', this.handleMouseUp_);\n this.off(doc, 'touchmove', this.handleMouseMove_);\n this.off(doc, 'touchend', this.handleMouseUp_);\n this.removeAttribute('tabindex');\n this.addClass('disabled');\n if (this.playerEvent) {\n this.off(this.player_, this.playerEvent, this.update);\n }\n this.enabled_ = false;\n }\n\n \/**\n * Create the `Slider`s DOM element.\n *\n * @param {string} type\n * Type of element to create.\n *\n * @param {Object} [props={}]\n * List of properties in Object form.\n *\n * @param {Object} [attributes={}]\n * list of attributes in Object form.\n *\n * @return {Element}\n * The element that gets created.\n *\/\n createEl(type, props = {}, attributes = {}) {\n \/\/ Add the slider element class to all sub classes\n props.className = props.className + ' vjs-slider';\n props = Object.assign({\n tabIndex: 0\n }, props);\n attributes = Object.assign({\n 'role': 'slider',\n 'aria-valuenow': 0,\n 'aria-valuemin': 0,\n 'aria-valuemax': 100\n }, attributes);\n return super.createEl(type, props, attributes);\n }\n\n \/**\n * Handle `mousedown` or `touchstart` events on the `Slider`.\n *\n * @param {MouseEvent} event\n * `mousedown` or `touchstart` event that triggered this function\n *\n * @listens mousedown\n * @listens touchstart\n * @fires Slider#slideractive\n *\/\n handleMouseDown(event) {\n const doc = this.bar.el_.ownerDocument;\n if (event.type === 'mousedown') {\n event.preventDefault();\n }\n \/\/ Do not call preventDefault() on touchstart in Chrome\n \/\/ to avoid console warnings. Use a 'touch-action: none' style\n \/\/ instead to prevent unintended scrolling.\n \/\/ https:\/\/developers.google.com\/web\/updates\/2017\/01\/scrolling-intervention\n if (event.type === 'touchstart' && !IS_CHROME) {\n event.preventDefault();\n }\n blockTextSelection();\n this.addClass('vjs-sliding');\n \/**\n * Triggered when the slider is in an active state\n *\n * @event Slider#slideractive\n * @type {MouseEvent}\n *\/\n this.trigger('slideractive');\n this.on(doc, 'mousemove', this.handleMouseMove_);\n this.on(doc, 'mouseup', this.handleMouseUp_);\n this.on(doc, 'touchmove', this.handleMouseMove_);\n this.on(doc, 'touchend', this.handleMouseUp_);\n this.handleMouseMove(event, true);\n }\n\n \/**\n * Handle the `mousemove`, `touchmove`, and `mousedown` events on this `Slider`.\n * The `mousemove` and `touchmove` events will only only trigger this function during\n * `mousedown` and `touchstart`. This is due to {@link Slider#handleMouseDown} and\n * {@link Slider#handleMouseUp}.\n *\n * @param {MouseEvent} event\n * `mousedown`, `mousemove`, `touchstart`, or `touchmove` event that triggered\n * this function\n * @param {boolean} mouseDown this is a flag that should be set to true if `handleMouseMove` is called directly. It allows us to skip things that should not happen if coming from mouse down but should happen on regular mouse move handler. Defaults to false.\n *\n * @listens mousemove\n * @listens touchmove\n *\/\n handleMouseMove(event) {}\n\n \/**\n * Handle `mouseup` or `touchend` events on the `Slider`.\n *\n * @param {MouseEvent} event\n * `mouseup` or `touchend` event that triggered this function.\n *\n * @listens touchend\n * @listens mouseup\n * @fires Slider#sliderinactive\n *\/\n handleMouseUp(event) {\n const doc = this.bar.el_.ownerDocument;\n unblockTextSelection();\n this.removeClass('vjs-sliding');\n \/**\n * Triggered when the slider is no longer in an active state.\n *\n * @event Slider#sliderinactive\n * @type {Event}\n *\/\n this.trigger('sliderinactive');\n this.off(doc, 'mousemove', this.handleMouseMove_);\n this.off(doc, 'mouseup', this.handleMouseUp_);\n this.off(doc, 'touchmove', this.handleMouseMove_);\n this.off(doc, 'touchend', this.handleMouseUp_);\n this.update();\n }\n\n \/**\n * Update the progress bar of the `Slider`.\n *\n * @return {number}\n * The percentage of progress the progress bar represents as a\n * number from 0 to 1.\n *\/\n update() {\n \/\/ In VolumeBar init we have a setTimeout for update that pops and update\n \/\/ to the end of the execution stack. The player is destroyed before then\n \/\/ update will cause an error\n \/\/ If there's no bar...\n if (!this.el_ || !this.bar) {\n return;\n }\n\n \/\/ clamp progress between 0 and 1\n \/\/ and only round to four decimal places, as we round to two below\n const progress = this.getProgress();\n if (progress === this.progress_) {\n return progress;\n }\n this.progress_ = progress;\n this.requestNamedAnimationFrame('Slider#update', () => {\n \/\/ Set the new bar width or height\n const sizeKey = this.vertical() ? 'height' : 'width';\n\n \/\/ Convert to a percentage for css value\n this.bar.el().style[sizeKey] = (progress * 100).toFixed(2) + '%';\n });\n return progress;\n }\n\n \/**\n * Get the percentage of the bar that should be filled\n * but clamped and rounded.\n *\n * @return {number}\n * percentage filled that the slider is\n *\/\n getProgress() {\n return Number(clamp(this.getPercent(), 0, 1).toFixed(4));\n }\n\n \/**\n * Calculate distance for slider\n *\n * @param {Event} event\n * The event that caused this function to run.\n *\n * @return {number}\n * The current position of the Slider.\n * - position.x for vertical `Slider`s\n * - position.y for horizontal `Slider`s\n *\/\n calculateDistance(event) {\n const position = getPointerPosition(this.el_, event);\n if (this.vertical()) {\n return position.y;\n }\n return position.x;\n }\n\n \/**\n * Handle a `keydown` event on the `Slider`. Watches for left, right, up, and down\n * arrow keys. This function will only be called when the slider has focus. See\n * {@link Slider#handleFocus} and {@link Slider#handleBlur}.\n *\n * @param {KeyboardEvent} event\n * the `keydown` event that caused this function to run.\n *\n * @listens keydown\n *\/\n handleKeyDown(event) {\n const spatialNavOptions = this.options_.playerOptions.spatialNavigation;\n const spatialNavEnabled = spatialNavOptions && spatialNavOptions.enabled;\n const horizontalSeek = spatialNavOptions && spatialNavOptions.horizontalSeek;\n if (spatialNavEnabled) {\n if (horizontalSeek && event.key === 'ArrowLeft' || !horizontalSeek && event.key === 'ArrowDown') {\n event.preventDefault();\n event.stopPropagation();\n this.stepBack();\n } else if (horizontalSeek && event.key === 'ArrowRight' || !horizontalSeek && event.key === 'ArrowUp') {\n event.preventDefault();\n event.stopPropagation();\n this.stepForward();\n } else {\n super.handleKeyDown(event);\n }\n\n \/\/ Left and Down Arrows\n } else if (event.key === 'ArrowLeft' || event.key === 'ArrowDown') {\n event.preventDefault();\n event.stopPropagation();\n this.stepBack();\n\n \/\/ Up and Right Arrows\n } else if (event.key === 'ArrowUp' || event.key === 'ArrowRight') {\n event.preventDefault();\n event.stopPropagation();\n this.stepForward();\n } else {\n \/\/ Pass keydown handling up for unsupported keys\n super.handleKeyDown(event);\n }\n }\n\n \/**\n * Listener for click events on slider, used to prevent clicks\n * from bubbling up to parent elements like button menus.\n *\n * @param {Object} event\n * Event that caused this object to run\n *\/\n handleClick(event) {\n event.stopPropagation();\n event.preventDefault();\n }\n\n \/**\n * Get\/set if slider is horizontal for vertical\n *\n * @param {boolean} [bool]\n * - true if slider is vertical,\n * - false is horizontal\n *\n * @return {boolean}\n * - true if slider is vertical, and getting\n * - false if the slider is horizontal, and getting\n *\/\n vertical(bool) {\n if (bool === undefined) {\n return this.vertical_ || false;\n }\n this.vertical_ = !!bool;\n if (this.vertical_) {\n this.addClass('vjs-slider-vertical');\n } else {\n this.addClass('vjs-slider-horizontal');\n }\n }\n }\n Component$1.registerComponent('Slider', Slider);\n\n \/**\n * @file load-progress-bar.js\n *\/\n\n \/** @import Player from '..\/..\/player' *\/\n\n \/\/ get the percent width of a time compared to the total end\n const percentify = (time, end) => clamp(time \/ end * 100, 0, 100).toFixed(2) + '%';\n\n \/**\n * Shows loading progress\n *\n * @extends Component\n *\/\n class LoadProgressBar extends Component$1 {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options]\n * The key\/value store of player options.\n *\/\n constructor(player, options) {\n super(player, options);\n this.partEls_ = [];\n this.on(player, 'progress', e => this.update(e));\n }\n\n \/**\n * Create the `Component`'s DOM element\n *\n * @return {Element}\n * The element that was created.\n *\/\n createEl() {\n const el = super.createEl('div', {\n className: 'vjs-load-progress'\n });\n const wrapper = createEl('span', {\n className: 'vjs-control-text'\n });\n const loadedText = createEl('span', {\n textContent: this.localize('Loaded')\n });\n const separator = document.createTextNode(': ');\n this.percentageEl_ = createEl('span', {\n className: 'vjs-control-text-loaded-percentage',\n textContent: '0%'\n });\n el.appendChild(wrapper);\n wrapper.appendChild(loadedText);\n wrapper.appendChild(separator);\n wrapper.appendChild(this.percentageEl_);\n return el;\n }\n dispose() {\n this.partEls_ = null;\n this.percentageEl_ = null;\n super.dispose();\n }\n\n \/**\n * Update progress bar\n *\n * @param {Event} [event]\n * The `progress` event that caused this function to run.\n *\n * @listens Player#progress\n *\/\n update(event) {\n this.requestNamedAnimationFrame('LoadProgressBar#update', () => {\n const liveTracker = this.player_.liveTracker;\n const buffered = this.player_.buffered();\n const duration = liveTracker && liveTracker.isLive() ? liveTracker.seekableEnd() : this.player_.duration();\n const bufferedEnd = this.player_.bufferedEnd();\n const children = this.partEls_;\n const percent = percentify(bufferedEnd, duration);\n if (this.percent_ !== percent) {\n \/\/ update the width of the progress bar\n this.el_.style.width = percent;\n \/\/ update the control-text\n textContent(this.percentageEl_, percent);\n this.percent_ = percent;\n }\n\n \/\/ add child elements to represent the individual buffered time ranges\n for (let i = 0; i < buffered.length; i++) {\n const start = buffered.start(i);\n const end = buffered.end(i);\n let part = children[i];\n if (!part) {\n part = this.el_.appendChild(createEl());\n children[i] = part;\n }\n\n \/\/ only update if changed\n if (part.dataset.start === start && part.dataset.end === end) {\n continue;\n }\n part.dataset.start = start;\n part.dataset.end = end;\n\n \/\/ set the percent based on the width of the progress bar (bufferedEnd)\n part.style.left = percentify(start, bufferedEnd);\n part.style.width = percentify(end - start, bufferedEnd);\n }\n\n \/\/ remove unused buffered range elements\n for (let i = children.length; i > buffered.length; i--) {\n this.el_.removeChild(children[i - 1]);\n }\n children.length = buffered.length;\n });\n }\n }\n Component$1.registerComponent('LoadProgressBar', LoadProgressBar);\n\n \/**\n * @file time-tooltip.js\n *\/\n\n \/** @import Player from '..\/..\/player' *\/\n\n \/**\n * Time tooltips display a time above the progress bar.\n *\n * @extends Component\n *\/\n class TimeTooltip extends Component$1 {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The {@link Player} that this class should be attached to.\n *\n * @param {Object} [options]\n * The key\/value store of player options.\n *\/\n constructor(player, options) {\n super(player, options);\n this.update = throttle(bind_(this, this.update), UPDATE_REFRESH_INTERVAL);\n }\n\n \/**\n * Create the time tooltip DOM element\n *\n * @return {Element}\n * The element that was created.\n *\/\n createEl() {\n return super.createEl('div', {\n className: 'vjs-time-tooltip'\n }, {\n 'aria-hidden': 'true'\n });\n }\n\n \/**\n * Updates the position of the time tooltip relative to the `SeekBar`.\n *\n * @param {Object} seekBarRect\n * The `ClientRect` for the {@link SeekBar} element.\n *\n * @param {number} seekBarPoint\n * A number from 0 to 1, representing a horizontal reference point\n * from the left edge of the {@link SeekBar}\n *\/\n update(seekBarRect, seekBarPoint, content) {\n const tooltipRect = findPosition(this.el_);\n const playerRect = getBoundingClientRect(this.player_.el());\n const seekBarPointPx = seekBarRect.width * seekBarPoint;\n\n \/\/ do nothing if either rect isn't available\n \/\/ for example, if the player isn't in the DOM for testing\n if (!playerRect || !tooltipRect) {\n return;\n }\n\n \/\/ This is the space left of the `seekBarPoint` available within the bounds\n \/\/ of the player. We calculate any gap between the left edge of the player\n \/\/ and the left edge of the `SeekBar` and add the number of pixels in the\n \/\/ `SeekBar` before hitting the `seekBarPoint`\n let spaceLeftOfPoint = seekBarRect.left - playerRect.left + seekBarPointPx;\n\n \/\/ This is the space right of the `seekBarPoint` available within the bounds\n \/\/ of the player. We calculate the number of pixels from the `seekBarPoint`\n \/\/ to the right edge of the `SeekBar` and add to that any gap between the\n \/\/ right edge of the `SeekBar` and the player.\n let spaceRightOfPoint = seekBarRect.width - seekBarPointPx + (playerRect.right - seekBarRect.right);\n\n \/\/ spaceRightOfPoint is always NaN for mouse time display\n \/\/ because the seekbarRect does not have a right property. This causes\n \/\/ the mouse tool tip to be truncated when it's close to the right edge of the player.\n \/\/ In such cases, we ignore the `playerRect.right - seekBarRect.right` value when calculating.\n \/\/ For the sake of consistency, we ignore seekBarRect.left - playerRect.left for the left edge.\n if (!spaceRightOfPoint) {\n spaceRightOfPoint = seekBarRect.width - seekBarPointPx;\n spaceLeftOfPoint = seekBarPointPx;\n }\n \/\/ This is the number of pixels by which the tooltip will need to be pulled\n \/\/ further to the right to center it over the `seekBarPoint`.\n let pullTooltipBy = tooltipRect.width \/ 2;\n\n \/\/ Adjust the `pullTooltipBy` distance to the left or right depending on\n \/\/ the results of the space calculations above.\n if (spaceLeftOfPoint < pullTooltipBy) {\n pullTooltipBy += pullTooltipBy - spaceLeftOfPoint;\n } else if (spaceRightOfPoint < pullTooltipBy) {\n pullTooltipBy = spaceRightOfPoint;\n }\n\n \/\/ Due to the imprecision of decimal\/ratio based calculations and varying\n \/\/ rounding behaviors, there are cases where the spacing adjustment is off\n \/\/ by a pixel or two. This adds insurance to these calculations.\n if (pullTooltipBy < 0) {\n pullTooltipBy = 0;\n } else if (pullTooltipBy > tooltipRect.width) {\n pullTooltipBy = tooltipRect.width;\n }\n\n \/\/ prevent small width fluctuations within 0.4px from\n \/\/ changing the value below.\n \/\/ This really helps for live to prevent the play\n \/\/ progress time tooltip from jittering\n pullTooltipBy = Math.round(pullTooltipBy);\n this.el_.style.right = `-${pullTooltipBy}px`;\n this.write(content);\n }\n\n \/**\n * Write the time to the tooltip DOM element.\n *\n * @param {string} content\n * The formatted time for the tooltip.\n *\/\n write(content) {\n textContent(this.el_, content);\n }\n\n \/**\n * Updates the position of the time tooltip relative to the `SeekBar`.\n *\n * @param {Object} seekBarRect\n * The `ClientRect` for the {@link SeekBar} element.\n *\n * @param {number} seekBarPoint\n * A number from 0 to 1, representing a horizontal reference point\n * from the left edge of the {@link SeekBar}\n *\n * @param {number} time\n * The time to update the tooltip to, not used during live playback\n *\n * @param {Function} cb\n * A function that will be called during the request animation frame\n * for tooltips that need to do additional animations from the default\n *\/\n updateTime(seekBarRect, seekBarPoint, time, cb) {\n this.requestNamedAnimationFrame('TimeTooltip#updateTime', () => {\n let content;\n const duration = this.player_.duration();\n if (this.player_.liveTracker && this.player_.liveTracker.isLive()) {\n const liveWindow = this.player_.liveTracker.liveWindow();\n const secondsBehind = liveWindow - seekBarPoint * liveWindow;\n content = (secondsBehind < 1 ? '' : '-') + formatTime(secondsBehind, liveWindow);\n } else {\n content = formatTime(time, duration);\n }\n this.update(seekBarRect, seekBarPoint, content);\n if (cb) {\n cb();\n }\n });\n }\n }\n Component$1.registerComponent('TimeTooltip', TimeTooltip);\n\n \/**\n * @file play-progress-bar.js\n *\/\n\n \/**\n * Used by {@link SeekBar} to display media playback progress as part of the\n * {@link ProgressControl}.\n *\n * @extends Component\n *\/\n class PlayProgressBar extends Component$1 {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The {@link Player} that this class should be attached to.\n *\n * @param {Object} [options]\n * The key\/value store of player options.\n *\/\n constructor(player, options) {\n super(player, options);\n this.setIcon('circle');\n this.update = throttle(bind_(this, this.update), UPDATE_REFRESH_INTERVAL);\n }\n\n \/**\n * Create the the DOM element for this class.\n *\n * @return {Element}\n * The element that was created.\n *\/\n createEl() {\n return super.createEl('div', {\n className: 'vjs-play-progress vjs-slider-bar'\n }, {\n 'aria-hidden': 'true'\n });\n }\n\n \/**\n * Enqueues updates to its own DOM as well as the DOM of its\n * {@link TimeTooltip} child.\n *\n * @param {Object} seekBarRect\n * The `ClientRect` for the {@link SeekBar} element.\n *\n * @param {number} seekBarPoint\n * A number from 0 to 1, representing a horizontal reference point\n * from the left edge of the {@link SeekBar}\n *\/\n update(seekBarRect, seekBarPoint) {\n const timeTooltip = this.getChild('timeTooltip');\n if (!timeTooltip) {\n return;\n }\n const time = this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime();\n timeTooltip.updateTime(seekBarRect, seekBarPoint, time);\n }\n }\n\n \/**\n * Default options for {@link PlayProgressBar}.\n *\n * @type {Object}\n * @private\n *\/\n PlayProgressBar.prototype.options_ = {\n children: []\n };\n\n \/\/ Time tooltips should not be added to a player on mobile devices\n if (!IS_IOS && !IS_ANDROID) {\n PlayProgressBar.prototype.options_.children.push('timeTooltip');\n }\n Component$1.registerComponent('PlayProgressBar', PlayProgressBar);\n\n \/**\n * @file mouse-time-display.js\n *\/\n\n \/**\n * The {@link MouseTimeDisplay} component tracks mouse movement over the\n * {@link ProgressControl}. It displays an indicator and a {@link TimeTooltip}\n * indicating the time which is represented by a given point in the\n * {@link ProgressControl}.\n *\n * @extends Component\n *\/\n class MouseTimeDisplay extends Component$1 {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The {@link Player} that this class should be attached to.\n *\n * @param {Object} [options]\n * The key\/value store of player options.\n *\/\n constructor(player, options) {\n super(player, options);\n this.update = throttle(bind_(this, this.update), UPDATE_REFRESH_INTERVAL);\n }\n\n \/**\n * Create the DOM element for this class.\n *\n * @return {Element}\n * The element that was created.\n *\/\n createEl() {\n return super.createEl('div', {\n className: 'vjs-mouse-display'\n });\n }\n\n \/**\n * Enqueues updates to its own DOM as well as the DOM of its\n * {@link TimeTooltip} child.\n *\n * @param {Object} seekBarRect\n * The `ClientRect` for the {@link SeekBar} element.\n *\n * @param {number} seekBarPoint\n * A number from 0 to 1, representing a horizontal reference point\n * from the left edge of the {@link SeekBar}\n *\/\n update(seekBarRect, seekBarPoint) {\n const time = seekBarPoint * this.player_.duration();\n this.getChild('timeTooltip').updateTime(seekBarRect, seekBarPoint, time, () => {\n this.el_.style.left = `${seekBarRect.width * seekBarPoint}px`;\n });\n }\n }\n\n \/**\n * Default options for `MouseTimeDisplay`\n *\n * @type {Object}\n * @private\n *\/\n MouseTimeDisplay.prototype.options_ = {\n children: ['timeTooltip']\n };\n Component$1.registerComponent('MouseTimeDisplay', MouseTimeDisplay);\n\n \/**\n * @file seek-bar.js\n *\/\n\n \/\/ The number of seconds the `step*` functions move the timeline.\n const STEP_SECONDS = 5;\n\n \/\/ The multiplier of STEP_SECONDS that PgUp\/PgDown move the timeline.\n const PAGE_KEY_MULTIPLIER = 12;\n\n \/**\n * Seek bar and container for the progress bars. Uses {@link PlayProgressBar}\n * as its `bar`.\n *\n * @extends Slider\n *\/\n class SeekBar extends Slider {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options]\n * The key\/value store of player options.\n *\/\n constructor(player, options) {\n super(player, options);\n this.setEventHandlers_();\n }\n\n \/**\n * Sets the event handlers\n *\n * @private\n *\/\n setEventHandlers_() {\n this.update_ = bind_(this, this.update);\n this.update = throttle(this.update_, UPDATE_REFRESH_INTERVAL);\n this.on(this.player_, ['durationchange', 'timeupdate'], this.update);\n this.on(this.player_, ['ended'], this.update_);\n if (this.player_.liveTracker) {\n this.on(this.player_.liveTracker, 'liveedgechange', this.update);\n }\n\n \/\/ when playing, let's ensure we smoothly update the play progress bar\n \/\/ via an interval\n this.updateInterval = null;\n this.enableIntervalHandler_ = e => this.enableInterval_(e);\n this.disableIntervalHandler_ = e => this.disableInterval_(e);\n this.on(this.player_, ['playing'], this.enableIntervalHandler_);\n this.on(this.player_, ['ended', 'pause', 'waiting'], this.disableIntervalHandler_);\n\n \/\/ we don't need to update the play progress if the document is hidden,\n \/\/ also, this causes the CPU to spike and eventually crash the page on IE11.\n if ('hidden' in document && 'visibilityState' in document) {\n this.on(document, 'visibilitychange', this.toggleVisibility_);\n }\n }\n toggleVisibility_(e) {\n if (document.visibilityState === 'hidden') {\n this.cancelNamedAnimationFrame('SeekBar#update');\n this.cancelNamedAnimationFrame('Slider#update');\n this.disableInterval_(e);\n } else {\n if (!this.player_.ended() && !this.player_.paused()) {\n this.enableInterval_();\n }\n\n \/\/ we just switched back to the page and someone may be looking, so, update ASAP\n this.update();\n }\n }\n enableInterval_() {\n if (this.updateInterval) {\n return;\n }\n this.updateInterval = this.setInterval(this.update, UPDATE_REFRESH_INTERVAL);\n }\n disableInterval_(e) {\n if (this.player_.liveTracker && this.player_.liveTracker.isLive() && e && e.type !== 'ended') {\n return;\n }\n if (!this.updateInterval) {\n return;\n }\n this.clearInterval(this.updateInterval);\n this.updateInterval = null;\n }\n\n \/**\n * Create the `Component`'s DOM element\n *\n * @return {Element}\n * The element that was created.\n *\/\n createEl() {\n return super.createEl('div', {\n className: 'vjs-progress-holder'\n }, {\n 'aria-label': this.localize('Progress Bar')\n });\n }\n\n \/**\n * This function updates the play progress bar and accessibility\n * attributes to whatever is passed in.\n *\n * @param {Event} [event]\n * The `timeupdate` or `ended` event that caused this to run.\n *\n * @listens Player#timeupdate\n *\n * @return {number}\n * The current percent at a number from 0-1\n *\/\n update(event) {\n \/\/ ignore updates while the tab is hidden\n if (document.visibilityState === 'hidden') {\n return;\n }\n const percent = super.update();\n this.requestNamedAnimationFrame('SeekBar#update', () => {\n const currentTime = this.player_.ended() ? this.player_.duration() : this.getCurrentTime_();\n const liveTracker = this.player_.liveTracker;\n let duration = this.player_.duration();\n if (liveTracker && liveTracker.isLive()) {\n duration = this.player_.liveTracker.liveCurrentTime();\n }\n if (this.percent_ !== percent) {\n \/\/ machine readable value of progress bar (percentage complete)\n this.el_.setAttribute('aria-valuenow', (percent * 100).toFixed(2));\n this.percent_ = percent;\n }\n if (this.currentTime_ !== currentTime || this.duration_ !== duration) {\n \/\/ human readable value of progress bar (time complete)\n this.el_.setAttribute('aria-valuetext', this.localize('progress bar timing: currentTime={1} duration={2}', [formatTime(currentTime, duration), formatTime(duration, duration)], '{1} of {2}'));\n this.currentTime_ = currentTime;\n this.duration_ = duration;\n }\n\n \/\/ update the progress bar time tooltip with the current time\n if (this.bar) {\n this.bar.update(getBoundingClientRect(this.el()), this.getProgress());\n }\n });\n return percent;\n }\n\n \/**\n * Prevent liveThreshold from causing seeks to seem like they\n * are not happening from a user perspective.\n *\n * @param {number} ct\n * current time to seek to\n *\/\n userSeek_(ct) {\n if (this.player_.liveTracker && this.player_.liveTracker.isLive()) {\n this.player_.liveTracker.nextSeekedFromUser();\n }\n this.player_.currentTime(ct);\n }\n\n \/**\n * Get the value of current time but allows for smooth scrubbing,\n * when player can't keep up.\n *\n * @return {number}\n * The current time value to display\n *\n * @private\n *\/\n getCurrentTime_() {\n return this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime();\n }\n\n \/**\n * Get the percentage of media played so far.\n *\n * @return {number}\n * The percentage of media played so far (0 to 1).\n *\/\n getPercent() {\n const currentTime = this.getCurrentTime_();\n let percent;\n const liveTracker = this.player_.liveTracker;\n if (liveTracker && liveTracker.isLive()) {\n percent = (currentTime - liveTracker.seekableStart()) \/ liveTracker.liveWindow();\n\n \/\/ prevent the percent from changing at the live edge\n if (liveTracker.atLiveEdge()) {\n percent = 1;\n }\n } else {\n percent = currentTime \/ this.player_.duration();\n }\n return percent;\n }\n\n \/**\n * Handle mouse down on seek bar\n *\n * @param {MouseEvent} event\n * The `mousedown` event that caused this to run.\n *\n * @listens mousedown\n *\/\n handleMouseDown(event) {\n if (!isSingleLeftClick(event)) {\n return;\n }\n\n \/\/ Stop event propagation to prevent double fire in progress-control.js\n event.stopPropagation();\n this.videoWasPlaying = !this.player_.paused();\n this.player_.pause();\n super.handleMouseDown(event);\n }\n\n \/**\n * Handle mouse move on seek bar\n *\n * @param {MouseEvent} event\n * The `mousemove` event that caused this to run.\n * @param {boolean} mouseDown this is a flag that should be set to true if `handleMouseMove` is called directly. It allows us to skip things that should not happen if coming from mouse down but should happen on regular mouse move handler. Defaults to false\n *\n * @listens mousemove\n *\/\n handleMouseMove(event, mouseDown = false) {\n if (!isSingleLeftClick(event) || isNaN(this.player_.duration())) {\n return;\n }\n if (!mouseDown && !this.player_.scrubbing()) {\n this.player_.scrubbing(true);\n }\n let newTime;\n const distance = this.calculateDistance(event);\n const liveTracker = this.player_.liveTracker;\n if (!liveTracker || !liveTracker.isLive()) {\n newTime = distance * this.player_.duration();\n\n \/\/ Don't let video end while scrubbing.\n if (newTime === this.player_.duration()) {\n newTime = newTime - 0.1;\n }\n } else {\n if (distance >= 0.99) {\n liveTracker.seekToLiveEdge();\n return;\n }\n const seekableStart = liveTracker.seekableStart();\n const seekableEnd = liveTracker.liveCurrentTime();\n newTime = seekableStart + distance * liveTracker.liveWindow();\n\n \/\/ Don't let video end while scrubbing.\n if (newTime >= seekableEnd) {\n newTime = seekableEnd;\n }\n\n \/\/ Compensate for precision differences so that currentTime is not less\n \/\/ than seekable start\n if (newTime <= seekableStart) {\n newTime = seekableStart + 0.1;\n }\n\n \/\/ On android seekableEnd can be Infinity sometimes,\n \/\/ this will cause newTime to be Infinity, which is\n \/\/ not a valid currentTime.\n if (newTime === Infinity) {\n return;\n }\n }\n\n \/\/ Set new time (tell player to seek to new time)\n this.userSeek_(newTime);\n if (this.player_.options_.enableSmoothSeeking) {\n this.update();\n }\n }\n enable() {\n super.enable();\n const mouseTimeDisplay = this.getChild('mouseTimeDisplay');\n if (!mouseTimeDisplay) {\n return;\n }\n mouseTimeDisplay.show();\n }\n disable() {\n super.disable();\n const mouseTimeDisplay = this.getChild('mouseTimeDisplay');\n if (!mouseTimeDisplay) {\n return;\n }\n mouseTimeDisplay.hide();\n }\n\n \/**\n * Handle mouse up on seek bar\n *\n * @param {MouseEvent} event\n * The `mouseup` event that caused this to run.\n *\n * @listens mouseup\n *\/\n handleMouseUp(event) {\n super.handleMouseUp(event);\n\n \/\/ Stop event propagation to prevent double fire in progress-control.js\n if (event) {\n event.stopPropagation();\n }\n this.player_.scrubbing(false);\n\n \/**\n * Trigger timeupdate because we're done seeking and the time has changed.\n * This is particularly useful for if the player is paused to time the time displays.\n *\n * @event Tech#timeupdate\n * @type {Event}\n *\/\n this.player_.trigger({\n type: 'timeupdate',\n target: this,\n manuallyTriggered: true\n });\n if (this.videoWasPlaying) {\n silencePromise(this.player_.play());\n } else {\n \/\/ We're done seeking and the time has changed.\n \/\/ If the player is paused, make sure we display the correct time on the seek bar.\n this.update_();\n }\n }\n\n \/**\n * Move more quickly fast forward for keyboard-only users\n *\/\n stepForward() {\n this.userSeek_(this.player_.currentTime() + STEP_SECONDS);\n }\n\n \/**\n * Move more quickly rewind for keyboard-only users\n *\/\n stepBack() {\n this.userSeek_(this.player_.currentTime() - STEP_SECONDS);\n }\n\n \/**\n * Toggles the playback state of the player\n * This gets called when enter or space is used on the seekbar\n *\n * @param {KeyboardEvent} event\n * The `keydown` event that caused this function to be called\n *\n *\/\n handleAction(event) {\n if (this.player_.paused()) {\n this.player_.play();\n } else {\n this.player_.pause();\n }\n }\n\n \/**\n * Called when this SeekBar has focus and a key gets pressed down.\n * Supports the following keys:\n *\n * Space or Enter key fire a click event\n * Home key moves to start of the timeline\n * End key moves to end of the timeline\n * Digit \"0\" through \"9\" keys move to 0%, 10% ... 80%, 90% of the timeline\n * PageDown key moves back a larger step than ArrowDown\n * PageUp key moves forward a large step\n *\n * @param {KeyboardEvent} event\n * The `keydown` event that caused this function to be called.\n *\n * @listens keydown\n *\/\n handleKeyDown(event) {\n const liveTracker = this.player_.liveTracker;\n if (event.key === ' ' || event.key === 'Enter') {\n event.preventDefault();\n event.stopPropagation();\n this.handleAction(event);\n } else if (event.key === 'Home') {\n event.preventDefault();\n event.stopPropagation();\n this.userSeek_(0);\n } else if (event.key === 'End') {\n event.preventDefault();\n event.stopPropagation();\n if (liveTracker && liveTracker.isLive()) {\n this.userSeek_(liveTracker.liveCurrentTime());\n } else {\n this.userSeek_(this.player_.duration());\n }\n } else if (\/^[0-9]$\/.test(event.key)) {\n event.preventDefault();\n event.stopPropagation();\n const gotoFraction = parseInt(event.key, 10) * 0.1;\n if (liveTracker && liveTracker.isLive()) {\n this.userSeek_(liveTracker.seekableStart() + liveTracker.liveWindow() * gotoFraction);\n } else {\n this.userSeek_(this.player_.duration() * gotoFraction);\n }\n } else if (event.key === 'PageDown') {\n event.preventDefault();\n event.stopPropagation();\n this.userSeek_(this.player_.currentTime() - STEP_SECONDS * PAGE_KEY_MULTIPLIER);\n } else if (event.key === 'PageUp') {\n event.preventDefault();\n event.stopPropagation();\n this.userSeek_(this.player_.currentTime() + STEP_SECONDS * PAGE_KEY_MULTIPLIER);\n } else {\n \/\/ Pass keydown handling up for unsupported keys\n super.handleKeyDown(event);\n }\n }\n dispose() {\n this.disableInterval_();\n this.off(this.player_, ['durationchange', 'timeupdate'], this.update);\n this.off(this.player_, ['ended'], this.update_);\n if (this.player_.liveTracker) {\n this.off(this.player_.liveTracker, 'liveedgechange', this.update);\n }\n this.off(this.player_, ['playing'], this.enableIntervalHandler_);\n this.off(this.player_, ['ended', 'pause', 'waiting'], this.disableIntervalHandler_);\n\n \/\/ we don't need to update the play progress if the document is hidden,\n \/\/ also, this causes the CPU to spike and eventually crash the page on IE11.\n if ('hidden' in document && 'visibilityState' in document) {\n this.off(document, 'visibilitychange', this.toggleVisibility_);\n }\n super.dispose();\n }\n }\n\n \/**\n * Default options for the `SeekBar`\n *\n * @type {Object}\n * @private\n *\/\n SeekBar.prototype.options_ = {\n children: ['loadProgressBar', 'playProgressBar'],\n barName: 'playProgressBar'\n };\n\n \/\/ MouseTimeDisplay tooltips should not be added to a player on mobile devices\n if (!IS_IOS && !IS_ANDROID) {\n SeekBar.prototype.options_.children.splice(1, 0, 'mouseTimeDisplay');\n }\n Component$1.registerComponent('SeekBar', SeekBar);\n\n \/**\n * @file progress-control.js\n *\/\n\n \/**\n * The Progress Control component contains the seek bar, load progress,\n * and play progress.\n *\n * @extends Component\n *\/\n class ProgressControl extends Component$1 {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options]\n * The key\/value store of player options.\n *\/\n constructor(player, options) {\n super(player, options);\n this.handleMouseMove = throttle(bind_(this, this.handleMouseMove), UPDATE_REFRESH_INTERVAL);\n this.throttledHandleMouseSeek = throttle(bind_(this, this.handleMouseSeek), UPDATE_REFRESH_INTERVAL);\n this.handleMouseUpHandler_ = e => this.handleMouseUp(e);\n this.handleMouseDownHandler_ = e => this.handleMouseDown(e);\n this.enable();\n }\n\n \/**\n * Create the `Component`'s DOM element\n *\n * @return {Element}\n * The element that was created.\n *\/\n createEl() {\n return super.createEl('div', {\n className: 'vjs-progress-control vjs-control'\n });\n }\n\n \/**\n * When the mouse moves over the `ProgressControl`, the pointer position\n * gets passed down to the `MouseTimeDisplay` component.\n *\n * @param {Event} event\n * The `mousemove` event that caused this function to run.\n *\n * @listen mousemove\n *\/\n handleMouseMove(event) {\n const seekBar = this.getChild('seekBar');\n if (!seekBar) {\n return;\n }\n const playProgressBar = seekBar.getChild('playProgressBar');\n const mouseTimeDisplay = seekBar.getChild('mouseTimeDisplay');\n if (!playProgressBar && !mouseTimeDisplay) {\n return;\n }\n const seekBarEl = seekBar.el();\n const seekBarRect = findPosition(seekBarEl);\n let seekBarPoint = getPointerPosition(seekBarEl, event).x;\n\n \/\/ The default skin has a gap on either side of the `SeekBar`. This means\n \/\/ that it's possible to trigger this behavior outside the boundaries of\n \/\/ the `SeekBar`. This ensures we stay within it at all times.\n seekBarPoint = clamp(seekBarPoint, 0, 1);\n if (mouseTimeDisplay) {\n mouseTimeDisplay.update(seekBarRect, seekBarPoint);\n }\n if (playProgressBar) {\n playProgressBar.update(seekBarRect, seekBar.getProgress());\n }\n }\n\n \/**\n * A throttled version of the {@link ProgressControl#handleMouseSeek} listener.\n *\n * @method ProgressControl#throttledHandleMouseSeek\n * @param {Event} event\n * The `mousemove` event that caused this function to run.\n *\n * @listen mousemove\n * @listen touchmove\n *\/\n\n \/**\n * Handle `mousemove` or `touchmove` events on the `ProgressControl`.\n *\n * @param {Event} event\n * `mousedown` or `touchstart` event that triggered this function\n *\n * @listens mousemove\n * @listens touchmove\n *\/\n handleMouseSeek(event) {\n const seekBar = this.getChild('seekBar');\n if (seekBar) {\n seekBar.handleMouseMove(event);\n }\n }\n\n \/**\n * Are controls are currently enabled for this progress control.\n *\n * @return {boolean}\n * true if controls are enabled, false otherwise\n *\/\n enabled() {\n return this.enabled_;\n }\n\n \/**\n * Disable all controls on the progress control and its children\n *\/\n disable() {\n this.children().forEach(child => child.disable && child.disable());\n if (!this.enabled()) {\n return;\n }\n this.off(['mousedown', 'touchstart'], this.handleMouseDownHandler_);\n this.off(this.el_, 'mousemove', this.handleMouseMove);\n this.removeListenersAddedOnMousedownAndTouchstart();\n this.addClass('disabled');\n this.enabled_ = false;\n\n \/\/ Restore normal playback state if controls are disabled while scrubbing\n if (this.player_.scrubbing()) {\n const seekBar = this.getChild('seekBar');\n this.player_.scrubbing(false);\n if (seekBar.videoWasPlaying) {\n silencePromise(this.player_.play());\n }\n }\n }\n\n \/**\n * Enable all controls on the progress control and its children\n *\/\n enable() {\n this.children().forEach(child => child.enable && child.enable());\n if (this.enabled()) {\n return;\n }\n this.on(['mousedown', 'touchstart'], this.handleMouseDownHandler_);\n this.on(this.el_, 'mousemove', this.handleMouseMove);\n this.removeClass('disabled');\n this.enabled_ = true;\n }\n\n \/**\n * Cleanup listeners after the user finishes interacting with the progress controls\n *\/\n removeListenersAddedOnMousedownAndTouchstart() {\n const doc = this.el_.ownerDocument;\n this.off(doc, 'mousemove', this.throttledHandleMouseSeek);\n this.off(doc, 'touchmove', this.throttledHandleMouseSeek);\n this.off(doc, 'mouseup', this.handleMouseUpHandler_);\n this.off(doc, 'touchend', this.handleMouseUpHandler_);\n }\n\n \/**\n * Handle `mousedown` or `touchstart` events on the `ProgressControl`.\n *\n * @param {Event} event\n * `mousedown` or `touchstart` event that triggered this function\n *\n * @listens mousedown\n * @listens touchstart\n *\/\n handleMouseDown(event) {\n const doc = this.el_.ownerDocument;\n const seekBar = this.getChild('seekBar');\n if (seekBar) {\n seekBar.handleMouseDown(event);\n }\n this.on(doc, 'mousemove', this.throttledHandleMouseSeek);\n this.on(doc, 'touchmove', this.throttledHandleMouseSeek);\n this.on(doc, 'mouseup', this.handleMouseUpHandler_);\n this.on(doc, 'touchend', this.handleMouseUpHandler_);\n }\n\n \/**\n * Handle `mouseup` or `touchend` events on the `ProgressControl`.\n *\n * @param {Event} event\n * `mouseup` or `touchend` event that triggered this function.\n *\n * @listens touchend\n * @listens mouseup\n *\/\n handleMouseUp(event) {\n const seekBar = this.getChild('seekBar');\n if (seekBar) {\n seekBar.handleMouseUp(event);\n }\n this.removeListenersAddedOnMousedownAndTouchstart();\n }\n }\n\n \/**\n * Default options for `ProgressControl`\n *\n * @type {Object}\n * @private\n *\/\n ProgressControl.prototype.options_ = {\n children: ['seekBar']\n };\n Component$1.registerComponent('ProgressControl', ProgressControl);\n\n \/**\n * @file picture-in-picture-toggle.js\n *\/\n\n \/** @import Player from '.\/player' *\/\n\n \/**\n * Toggle Picture-in-Picture mode\n *\n * @extends Button\n *\/\n class PictureInPictureToggle extends Button {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options]\n * The key\/value store of player options.\n *\n * @listens Player#enterpictureinpicture\n * @listens Player#leavepictureinpicture\n *\/\n constructor(player, options) {\n super(player, options);\n this.setIcon('picture-in-picture-enter');\n this.on(player, ['enterpictureinpicture', 'leavepictureinpicture'], e => this.handlePictureInPictureChange(e));\n this.on(player, ['disablepictureinpicturechanged', 'loadedmetadata'], e => this.handlePictureInPictureEnabledChange(e));\n this.on(player, ['loadedmetadata', 'audioonlymodechange', 'audiopostermodechange'], () => this.handlePictureInPictureAudioModeChange());\n\n \/\/ TODO: Deactivate button on player emptied event.\n this.disable();\n }\n\n \/**\n * Builds the default DOM `className`.\n *\n * @return {string}\n * The DOM `className` for this object.\n *\/\n buildCSSClass() {\n return `vjs-picture-in-picture-control vjs-hidden ${super.buildCSSClass()}`;\n }\n\n \/**\n * Displays or hides the button depending on the audio mode detection.\n * Exits picture-in-picture if it is enabled when switching to audio mode.\n *\/\n handlePictureInPictureAudioModeChange() {\n \/\/ This audio detection will not detect HLS or DASH audio-only streams because there was no reliable way to detect them at the time\n const isSourceAudio = this.player_.currentType().substring(0, 5) === 'audio';\n const isAudioMode = isSourceAudio || this.player_.audioPosterMode() || this.player_.audioOnlyMode();\n if (!isAudioMode) {\n this.show();\n return;\n }\n if (this.player_.isInPictureInPicture()) {\n this.player_.exitPictureInPicture();\n }\n this.hide();\n }\n\n \/**\n * Enables or disables button based on availability of a Picture-In-Picture mode.\n *\n * Enabled if\n * - `player.options().enableDocumentPictureInPicture` is true and\n * window.documentPictureInPicture is available; or\n * - `player.disablePictureInPicture()` is false and\n * element.requestPictureInPicture is available\n *\/\n handlePictureInPictureEnabledChange() {\n if (document.pictureInPictureEnabled && this.player_.disablePictureInPicture() === false || this.player_.options_.enableDocumentPictureInPicture && 'documentPictureInPicture' in window) {\n this.enable();\n } else {\n this.disable();\n }\n }\n\n \/**\n * Handles enterpictureinpicture and leavepictureinpicture on the player and change control text accordingly.\n *\n * @param {Event} [event]\n * The {@link Player#enterpictureinpicture} or {@link Player#leavepictureinpicture} event that caused this function to be\n * called.\n *\n * @listens Player#enterpictureinpicture\n * @listens Player#leavepictureinpicture\n *\/\n handlePictureInPictureChange(event) {\n if (this.player_.isInPictureInPicture()) {\n this.setIcon('picture-in-picture-exit');\n this.controlText('Exit Picture-in-Picture');\n } else {\n this.setIcon('picture-in-picture-enter');\n this.controlText('Picture-in-Picture');\n }\n this.handlePictureInPictureEnabledChange();\n }\n\n \/**\n * This gets called when an `PictureInPictureToggle` is \"clicked\". See\n * {@link ClickableComponent} for more detailed information on what a click can be.\n *\n * @param {Event} [event]\n * The `keydown`, `tap`, or `click` event that caused this function to be\n * called.\n *\n * @listens tap\n * @listens click\n *\/\n handleClick(event) {\n if (!this.player_.isInPictureInPicture()) {\n this.player_.requestPictureInPicture();\n } else {\n this.player_.exitPictureInPicture();\n }\n }\n\n \/**\n * Show the `Component`s element if it is hidden by removing the\n * 'vjs-hidden' class name from it only in browsers that support the Picture-in-Picture API.\n *\/\n show() {\n \/\/ Does not allow to display the pictureInPictureToggle in browsers that do not support the Picture-in-Picture API, e.g. Firefox.\n if (typeof document.exitPictureInPicture !== 'function') {\n return;\n }\n super.show();\n }\n }\n\n \/**\n * The text that should display over the `PictureInPictureToggle`s controls. Added for localization.\n *\n * @type {string}\n * @protected\n *\/\n PictureInPictureToggle.prototype.controlText_ = 'Picture-in-Picture';\n Component$1.registerComponent('PictureInPictureToggle', PictureInPictureToggle);\n\n \/**\n * @file fullscreen-toggle.js\n *\/\n\n \/** @import Player from '.\/player' *\/\n\n \/**\n * Toggle fullscreen video\n *\n * @extends Button\n *\/\n class FullscreenToggle extends Button {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options]\n * The key\/value store of player options.\n *\/\n constructor(player, options) {\n super(player, options);\n this.setIcon('fullscreen-enter');\n this.on(player, 'fullscreenchange', e => this.handleFullscreenChange(e));\n if (document[player.fsApi_.fullscreenEnabled] === false) {\n this.disable();\n }\n }\n\n \/**\n * Builds the default DOM `className`.\n *\n * @return {string}\n * The DOM `className` for this object.\n *\/\n buildCSSClass() {\n return `vjs-fullscreen-control ${super.buildCSSClass()}`;\n }\n\n \/**\n * Handles fullscreenchange on the player and change control text accordingly.\n *\n * @param {Event} [event]\n * The {@link Player#fullscreenchange} event that caused this function to be\n * called.\n *\n * @listens Player#fullscreenchange\n *\/\n handleFullscreenChange(event) {\n if (this.player_.isFullscreen()) {\n this.controlText('Exit Fullscreen');\n this.setIcon('fullscreen-exit');\n } else {\n this.controlText('Fullscreen');\n this.setIcon('fullscreen-enter');\n }\n }\n\n \/**\n * This gets called when an `FullscreenToggle` is \"clicked\". See\n * {@link ClickableComponent} for more detailed information on what a click can be.\n *\n * @param {Event} [event]\n * The `keydown`, `tap`, or `click` event that caused this function to be\n * called.\n *\n * @listens tap\n * @listens click\n *\/\n handleClick(event) {\n if (!this.player_.isFullscreen()) {\n this.player_.requestFullscreen();\n } else {\n this.player_.exitFullscreen();\n }\n }\n }\n\n \/**\n * The text that should display over the `FullscreenToggle`s controls. Added for localization.\n *\n * @type {string}\n * @protected\n *\/\n FullscreenToggle.prototype.controlText_ = 'Fullscreen';\n Component$1.registerComponent('FullscreenToggle', FullscreenToggle);\n\n \/** @import Component from '..\/..\/component' *\/\n \/** @import Player from '..\/..\/player' *\/\n\n \/**\n * Check if volume control is supported and if it isn't hide the\n * `Component` that was passed using the `vjs-hidden` class.\n *\n * @param {Component} self\n * The component that should be hidden if volume is unsupported\n *\n * @param {Player} player\n * A reference to the player\n *\n * @private\n *\/\n const checkVolumeSupport = function (self, player) {\n \/\/ hide volume controls when they're not supported by the current tech\n if (player.tech_ && !player.tech_.featuresVolumeControl) {\n self.addClass('vjs-hidden');\n }\n self.on(player, 'loadstart', function () {\n if (!player.tech_.featuresVolumeControl) {\n self.addClass('vjs-hidden');\n } else {\n self.removeClass('vjs-hidden');\n }\n });\n };\n\n \/**\n * @file volume-level.js\n *\/\n\n \/**\n * Shows volume level\n *\n * @extends Component\n *\/\n class VolumeLevel extends Component$1 {\n \/**\n * Create the `Component`'s DOM element\n *\n * @return {Element}\n * The element that was created.\n *\/\n createEl() {\n const el = super.createEl('div', {\n className: 'vjs-volume-level'\n });\n this.setIcon('circle', el);\n el.appendChild(super.createEl('span', {\n className: 'vjs-control-text'\n }));\n return el;\n }\n }\n Component$1.registerComponent('VolumeLevel', VolumeLevel);\n\n \/**\n * @file volume-level-tooltip.js\n *\/\n\n \/** @import Player from '..\/..\/player' *\/\n\n \/**\n * Volume level tooltips display a volume above or side by side the volume bar.\n *\n * @extends Component\n *\/\n class VolumeLevelTooltip extends Component$1 {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The {@link Player} that this class should be attached to.\n *\n * @param {Object} [options]\n * The key\/value store of player options.\n *\/\n constructor(player, options) {\n super(player, options);\n this.update = throttle(bind_(this, this.update), UPDATE_REFRESH_INTERVAL);\n }\n\n \/**\n * Create the volume tooltip DOM element\n *\n * @return {Element}\n * The element that was created.\n *\/\n createEl() {\n return super.createEl('div', {\n className: 'vjs-volume-tooltip'\n }, {\n 'aria-hidden': 'true'\n });\n }\n\n \/**\n * Updates the position of the tooltip relative to the `VolumeBar` and\n * its content text.\n *\n * @param {Object} rangeBarRect\n * The `ClientRect` for the {@link VolumeBar} element.\n *\n * @param {number} rangeBarPoint\n * A number from 0 to 1, representing a horizontal\/vertical reference point\n * from the left edge of the {@link VolumeBar}\n *\n * @param {boolean} vertical\n * Referees to the Volume control position\n * in the control bar{@link VolumeControl}\n *\n *\/\n update(rangeBarRect, rangeBarPoint, vertical, content) {\n if (!vertical) {\n const tooltipRect = getBoundingClientRect(this.el_);\n const playerRect = getBoundingClientRect(this.player_.el());\n const volumeBarPointPx = rangeBarRect.width * rangeBarPoint;\n if (!playerRect || !tooltipRect) {\n return;\n }\n const spaceLeftOfPoint = rangeBarRect.left - playerRect.left + volumeBarPointPx;\n const spaceRightOfPoint = rangeBarRect.width - volumeBarPointPx + (playerRect.right - rangeBarRect.right);\n let pullTooltipBy = tooltipRect.width \/ 2;\n if (spaceLeftOfPoint < pullTooltipBy) {\n pullTooltipBy += pullTooltipBy - spaceLeftOfPoint;\n } else if (spaceRightOfPoint < pullTooltipBy) {\n pullTooltipBy = spaceRightOfPoint;\n }\n if (pullTooltipBy < 0) {\n pullTooltipBy = 0;\n } else if (pullTooltipBy > tooltipRect.width) {\n pullTooltipBy = tooltipRect.width;\n }\n this.el_.style.right = `-${pullTooltipBy}px`;\n }\n this.write(`${content}%`);\n }\n\n \/**\n * Write the volume to the tooltip DOM element.\n *\n * @param {string} content\n * The formatted volume for the tooltip.\n *\/\n write(content) {\n textContent(this.el_, content);\n }\n\n \/**\n * Updates the position of the volume tooltip relative to the `VolumeBar`.\n *\n * @param {Object} rangeBarRect\n * The `ClientRect` for the {@link VolumeBar} element.\n *\n * @param {number} rangeBarPoint\n * A number from 0 to 1, representing a horizontal\/vertical reference point\n * from the left edge of the {@link VolumeBar}\n *\n * @param {boolean} vertical\n * Referees to the Volume control position\n * in the control bar{@link VolumeControl}\n *\n * @param {number} volume\n * The volume level to update the tooltip to\n *\n * @param {Function} cb\n * A function that will be called during the request animation frame\n * for tooltips that need to do additional animations from the default\n *\/\n updateVolume(rangeBarRect, rangeBarPoint, vertical, volume, cb) {\n this.requestNamedAnimationFrame('VolumeLevelTooltip#updateVolume', () => {\n this.update(rangeBarRect, rangeBarPoint, vertical, volume.toFixed(0));\n if (cb) {\n cb();\n }\n });\n }\n }\n Component$1.registerComponent('VolumeLevelTooltip', VolumeLevelTooltip);\n\n \/**\n * @file mouse-volume-level-display.js\n *\/\n\n \/**\n * The {@link MouseVolumeLevelDisplay} component tracks mouse movement over the\n * {@link VolumeControl}. It displays an indicator and a {@link VolumeLevelTooltip}\n * indicating the volume level which is represented by a given point in the\n * {@link VolumeBar}.\n *\n * @extends Component\n *\/\n class MouseVolumeLevelDisplay extends Component$1 {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The {@link Player} that this class should be attached to.\n *\n * @param {Object} [options]\n * The key\/value store of player options.\n *\/\n constructor(player, options) {\n super(player, options);\n this.update = throttle(bind_(this, this.update), UPDATE_REFRESH_INTERVAL);\n }\n\n \/**\n * Create the DOM element for this class.\n *\n * @return {Element}\n * The element that was created.\n *\/\n createEl() {\n return super.createEl('div', {\n className: 'vjs-mouse-display'\n });\n }\n\n \/**\n * Enquires updates to its own DOM as well as the DOM of its\n * {@link VolumeLevelTooltip} child.\n *\n * @param {Object} rangeBarRect\n * The `ClientRect` for the {@link VolumeBar} element.\n *\n * @param {number} rangeBarPoint\n * A number from 0 to 1, representing a horizontal\/vertical reference point\n * from the left edge of the {@link VolumeBar}\n *\n * @param {boolean} vertical\n * Referees to the Volume control position\n * in the control bar{@link VolumeControl}\n *\n *\/\n update(rangeBarRect, rangeBarPoint, vertical) {\n const volume = 100 * rangeBarPoint;\n this.getChild('volumeLevelTooltip').updateVolume(rangeBarRect, rangeBarPoint, vertical, volume, () => {\n if (vertical) {\n this.el_.style.bottom = `${rangeBarRect.height * rangeBarPoint}px`;\n } else {\n this.el_.style.left = `${rangeBarRect.width * rangeBarPoint}px`;\n }\n });\n }\n }\n\n \/**\n * Default options for `MouseVolumeLevelDisplay`\n *\n * @type {Object}\n * @private\n *\/\n MouseVolumeLevelDisplay.prototype.options_ = {\n children: ['volumeLevelTooltip']\n };\n Component$1.registerComponent('MouseVolumeLevelDisplay', MouseVolumeLevelDisplay);\n\n \/**\n * @file volume-bar.js\n *\/\n\n \/**\n * The bar that contains the volume level and can be clicked on to adjust the level\n *\n * @extends Slider\n *\/\n class VolumeBar extends Slider {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options]\n * The key\/value store of player options.\n *\/\n constructor(player, options) {\n super(player, options);\n this.on('slideractive', e => this.updateLastVolume_(e));\n this.on(player, 'volumechange', e => this.updateARIAAttributes(e));\n player.ready(() => this.updateARIAAttributes());\n }\n\n \/**\n * Create the `Component`'s DOM element\n *\n * @return {Element}\n * The element that was created.\n *\/\n createEl() {\n return super.createEl('div', {\n className: 'vjs-volume-bar vjs-slider-bar'\n }, {\n 'aria-label': this.localize('Volume Level'),\n 'aria-live': 'polite'\n });\n }\n\n \/**\n * Handle mouse down on volume bar\n *\n * @param {Event} event\n * The `mousedown` event that caused this to run.\n *\n * @listens mousedown\n *\/\n handleMouseDown(event) {\n if (!isSingleLeftClick(event)) {\n return;\n }\n super.handleMouseDown(event);\n }\n\n \/**\n * Handle movement events on the {@link VolumeMenuButton}.\n *\n * @param {Event} event\n * The event that caused this function to run.\n *\n * @listens mousemove\n *\/\n handleMouseMove(event) {\n const mouseVolumeLevelDisplay = this.getChild('mouseVolumeLevelDisplay');\n if (mouseVolumeLevelDisplay) {\n const volumeBarEl = this.el();\n const volumeBarRect = getBoundingClientRect(volumeBarEl);\n const vertical = this.vertical();\n let volumeBarPoint = getPointerPosition(volumeBarEl, event);\n volumeBarPoint = vertical ? volumeBarPoint.y : volumeBarPoint.x;\n \/\/ The default skin has a gap on either side of the `VolumeBar`. This means\n \/\/ that it's possible to trigger this behavior outside the boundaries of\n \/\/ the `VolumeBar`. This ensures we stay within it at all times.\n volumeBarPoint = clamp(volumeBarPoint, 0, 1);\n mouseVolumeLevelDisplay.update(volumeBarRect, volumeBarPoint, vertical);\n }\n if (!isSingleLeftClick(event)) {\n return;\n }\n this.checkMuted();\n this.player_.volume(this.calculateDistance(event));\n }\n\n \/**\n * If the player is muted unmute it.\n *\/\n checkMuted() {\n if (this.player_.muted()) {\n this.player_.muted(false);\n }\n }\n\n \/**\n * Get percent of volume level\n *\n * @return {number}\n * Volume level percent as a decimal number.\n *\/\n getPercent() {\n if (this.player_.muted()) {\n return 0;\n }\n return this.player_.volume();\n }\n\n \/**\n * Increase volume level for keyboard users\n *\/\n stepForward() {\n this.checkMuted();\n this.player_.volume(this.player_.volume() + 0.1);\n }\n\n \/**\n * Decrease volume level for keyboard users\n *\/\n stepBack() {\n this.checkMuted();\n this.player_.volume(this.player_.volume() - 0.1);\n }\n\n \/**\n * Update ARIA accessibility attributes\n *\n * @param {Event} [event]\n * The `volumechange` event that caused this function to run.\n *\n * @listens Player#volumechange\n *\/\n updateARIAAttributes(event) {\n const ariaValue = this.player_.muted() ? 0 : this.volumeAsPercentage_();\n this.el_.setAttribute('aria-valuenow', ariaValue);\n this.el_.setAttribute('aria-valuetext', ariaValue + '%');\n }\n\n \/**\n * Returns the current value of the player volume as a percentage\n *\n * @private\n *\/\n volumeAsPercentage_() {\n return Math.round(this.player_.volume() * 100);\n }\n\n \/**\n * When user starts dragging the VolumeBar, store the volume and listen for\n * the end of the drag. When the drag ends, if the volume was set to zero,\n * set lastVolume to the stored volume.\n *\n * @listens slideractive\n * @private\n *\/\n updateLastVolume_() {\n const volumeBeforeDrag = this.player_.volume();\n this.one('sliderinactive', () => {\n if (this.player_.volume() === 0) {\n this.player_.lastVolume_(volumeBeforeDrag);\n }\n });\n }\n }\n\n \/**\n * Default options for the `VolumeBar`\n *\n * @type {Object}\n * @private\n *\/\n VolumeBar.prototype.options_ = {\n children: ['volumeLevel'],\n barName: 'volumeLevel'\n };\n\n \/\/ MouseVolumeLevelDisplay tooltip should not be added to a player on mobile devices\n if (!IS_IOS && !IS_ANDROID) {\n VolumeBar.prototype.options_.children.splice(0, 0, 'mouseVolumeLevelDisplay');\n }\n\n \/**\n * Call the update event for this Slider when this event happens on the player.\n *\n * @type {string}\n *\/\n VolumeBar.prototype.playerEvent = 'volumechange';\n Component$1.registerComponent('VolumeBar', VolumeBar);\n\n \/**\n * @file volume-control.js\n *\/\n\n \/**\n * The component for controlling the volume level\n *\n * @extends Component\n *\/\n class VolumeControl extends Component$1 {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options={}]\n * The key\/value store of player options.\n *\/\n constructor(player, options = {}) {\n options.vertical = options.vertical || false;\n\n \/\/ Pass the vertical option down to the VolumeBar if\n \/\/ the VolumeBar is turned on.\n if (typeof options.volumeBar === 'undefined' || isPlain(options.volumeBar)) {\n options.volumeBar = options.volumeBar || {};\n options.volumeBar.vertical = options.vertical;\n }\n super(player, options);\n\n \/\/ hide this control if volume support is missing\n checkVolumeSupport(this, player);\n this.throttledHandleMouseMove = throttle(bind_(this, this.handleMouseMove), UPDATE_REFRESH_INTERVAL);\n this.handleMouseUpHandler_ = e => this.handleMouseUp(e);\n this.on('mousedown', e => this.handleMouseDown(e));\n this.on('touchstart', e => this.handleMouseDown(e));\n this.on('mousemove', e => this.handleMouseMove(e));\n\n \/\/ while the slider is active (the mouse has been pressed down and\n \/\/ is dragging) or in focus we do not want to hide the VolumeBar\n this.on(this.volumeBar, ['focus', 'slideractive'], () => {\n this.volumeBar.addClass('vjs-slider-active');\n this.addClass('vjs-slider-active');\n this.trigger('slideractive');\n });\n this.on(this.volumeBar, ['blur', 'sliderinactive'], () => {\n this.volumeBar.removeClass('vjs-slider-active');\n this.removeClass('vjs-slider-active');\n this.trigger('sliderinactive');\n });\n }\n\n \/**\n * Create the `Component`'s DOM element\n *\n * @return {Element}\n * The element that was created.\n *\/\n createEl() {\n let orientationClass = 'vjs-volume-horizontal';\n if (this.options_.vertical) {\n orientationClass = 'vjs-volume-vertical';\n }\n return super.createEl('div', {\n className: `vjs-volume-control vjs-control ${orientationClass}`\n });\n }\n\n \/**\n * Handle `mousedown` or `touchstart` events on the `VolumeControl`.\n *\n * @param {Event} event\n * `mousedown` or `touchstart` event that triggered this function\n *\n * @listens mousedown\n * @listens touchstart\n *\/\n handleMouseDown(event) {\n const doc = this.el_.ownerDocument;\n this.on(doc, 'mousemove', this.throttledHandleMouseMove);\n this.on(doc, 'touchmove', this.throttledHandleMouseMove);\n this.on(doc, 'mouseup', this.handleMouseUpHandler_);\n this.on(doc, 'touchend', this.handleMouseUpHandler_);\n }\n\n \/**\n * Handle `mouseup` or `touchend` events on the `VolumeControl`.\n *\n * @param {Event} event\n * `mouseup` or `touchend` event that triggered this function.\n *\n * @listens touchend\n * @listens mouseup\n *\/\n handleMouseUp(event) {\n const doc = this.el_.ownerDocument;\n this.off(doc, 'mousemove', this.throttledHandleMouseMove);\n this.off(doc, 'touchmove', this.throttledHandleMouseMove);\n this.off(doc, 'mouseup', this.handleMouseUpHandler_);\n this.off(doc, 'touchend', this.handleMouseUpHandler_);\n }\n\n \/**\n * Handle `mousedown` or `touchstart` events on the `VolumeControl`.\n *\n * @param {Event} event\n * `mousedown` or `touchstart` event that triggered this function\n *\n * @listens mousedown\n * @listens touchstart\n *\/\n handleMouseMove(event) {\n this.volumeBar.handleMouseMove(event);\n }\n }\n\n \/**\n * Default options for the `VolumeControl`\n *\n * @type {Object}\n * @private\n *\/\n VolumeControl.prototype.options_ = {\n children: ['volumeBar']\n };\n Component$1.registerComponent('VolumeControl', VolumeControl);\n\n \/** @import Component from '..\/..\/component' *\/\n \/** @import Player from '..\/..\/player' *\/\n\n \/**\n * Check if muting volume is supported and if it isn't hide the mute toggle\n * button.\n *\n * @param {Component} self\n * A reference to the mute toggle button\n *\n * @param {Player} player\n * A reference to the player\n *\n * @private\n *\/\n const checkMuteSupport = function (self, player) {\n \/\/ hide mute toggle button if it's not supported by the current tech\n if (player.tech_ && !player.tech_.featuresMuteControl) {\n self.addClass('vjs-hidden');\n }\n self.on(player, 'loadstart', function () {\n if (!player.tech_.featuresMuteControl) {\n self.addClass('vjs-hidden');\n } else {\n self.removeClass('vjs-hidden');\n }\n });\n };\n\n \/**\n * @file mute-toggle.js\n *\/\n\n \/** @import Player from '.\/player' *\/\n\n \/**\n * A button component for muting the audio.\n *\n * @extends Button\n *\/\n class MuteToggle extends Button {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options]\n * The key\/value store of player options.\n *\/\n constructor(player, options) {\n super(player, options);\n\n \/\/ hide this control if volume support is missing\n checkMuteSupport(this, player);\n this.on(player, ['loadstart', 'volumechange'], e => this.update(e));\n }\n\n \/**\n * Builds the default DOM `className`.\n *\n * @return {string}\n * The DOM `className` for this object.\n *\/\n buildCSSClass() {\n return `vjs-mute-control ${super.buildCSSClass()}`;\n }\n\n \/**\n * This gets called when an `MuteToggle` is \"clicked\". See\n * {@link ClickableComponent} for more detailed information on what a click can be.\n *\n * @param {Event} [event]\n * The `keydown`, `tap`, or `click` event that caused this function to be\n * called.\n *\n * @listens tap\n * @listens click\n *\/\n handleClick(event) {\n const vol = this.player_.volume();\n const lastVolume = this.player_.lastVolume_();\n if (vol === 0) {\n const volumeToSet = lastVolume < 0.1 ? 0.1 : lastVolume;\n this.player_.volume(volumeToSet);\n this.player_.muted(false);\n } else {\n this.player_.muted(this.player_.muted() ? false : true);\n }\n }\n\n \/**\n * Update the `MuteToggle` button based on the state of `volume` and `muted`\n * on the player.\n *\n * @param {Event} [event]\n * The {@link Player#loadstart} event if this function was called\n * through an event.\n *\n * @listens Player#loadstart\n * @listens Player#volumechange\n *\/\n update(event) {\n this.updateIcon_();\n this.updateControlText_();\n }\n\n \/**\n * Update the appearance of the `MuteToggle` icon.\n *\n * Possible states (given `level` variable below):\n * - 0: crossed out\n * - 1: zero bars of volume\n * - 2: one bar of volume\n * - 3: two bars of volume\n *\n * @private\n *\/\n updateIcon_() {\n const vol = this.player_.volume();\n let level = 3;\n this.setIcon('volume-high');\n\n \/\/ in iOS when a player is loaded with muted attribute\n \/\/ and volume is changed with a native mute button\n \/\/ we want to make sure muted state is updated\n if (IS_IOS && this.player_.tech_ && this.player_.tech_.el_) {\n this.player_.muted(this.player_.tech_.el_.muted);\n }\n if (vol === 0 || this.player_.muted()) {\n this.setIcon('volume-mute');\n level = 0;\n } else if (vol < 0.33) {\n this.setIcon('volume-low');\n level = 1;\n } else if (vol < 0.67) {\n this.setIcon('volume-medium');\n level = 2;\n }\n removeClass(this.el_, [0, 1, 2, 3].reduce((str, i) => str + `${i ? ' ' : ''}vjs-vol-${i}`, ''));\n addClass(this.el_, `vjs-vol-${level}`);\n }\n\n \/**\n * If `muted` has changed on the player, update the control text\n * (`title` attribute on `vjs-mute-control` element and content of\n * `vjs-control-text` element).\n *\n * @private\n *\/\n updateControlText_() {\n const soundOff = this.player_.muted() || this.player_.volume() === 0;\n const text = soundOff ? 'Unmute' : 'Mute';\n if (this.controlText() !== text) {\n this.controlText(text);\n }\n }\n }\n\n \/**\n * The text that should display over the `MuteToggle`s controls. Added for localization.\n *\n * @type {string}\n * @protected\n *\/\n MuteToggle.prototype.controlText_ = 'Mute';\n Component$1.registerComponent('MuteToggle', MuteToggle);\n\n \/**\n * @file volume-control.js\n *\/\n\n \/**\n * A Component to contain the MuteToggle and VolumeControl so that\n * they can work together.\n *\n * @extends Component\n *\/\n class VolumePanel extends Component$1 {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options={}]\n * The key\/value store of player options.\n *\/\n constructor(player, options = {}) {\n if (typeof options.inline !== 'undefined') {\n options.inline = options.inline;\n } else {\n options.inline = true;\n }\n\n \/\/ pass the inline option down to the VolumeControl as vertical if\n \/\/ the VolumeControl is on.\n if (typeof options.volumeControl === 'undefined' || isPlain(options.volumeControl)) {\n options.volumeControl = options.volumeControl || {};\n options.volumeControl.vertical = !options.inline;\n }\n super(player, options);\n\n \/\/ this handler is used by mouse handler methods below\n this.handleKeyPressHandler_ = e => this.handleKeyPress(e);\n this.on(player, ['loadstart'], e => this.volumePanelState_(e));\n this.on(this.muteToggle, 'keyup', e => this.handleKeyPress(e));\n this.on(this.volumeControl, 'keyup', e => this.handleVolumeControlKeyUp(e));\n this.on('keydown', e => this.handleKeyPress(e));\n this.on('mouseover', e => this.handleMouseOver(e));\n this.on('mouseout', e => this.handleMouseOut(e));\n\n \/\/ while the slider is active (the mouse has been pressed down and\n \/\/ is dragging) we do not want to hide the VolumeBar\n this.on(this.volumeControl, ['slideractive'], this.sliderActive_);\n this.on(this.volumeControl, ['sliderinactive'], this.sliderInactive_);\n }\n\n \/**\n * Add vjs-slider-active class to the VolumePanel\n *\n * @listens VolumeControl#slideractive\n * @private\n *\/\n sliderActive_() {\n this.addClass('vjs-slider-active');\n }\n\n \/**\n * Removes vjs-slider-active class to the VolumePanel\n *\n * @listens VolumeControl#sliderinactive\n * @private\n *\/\n sliderInactive_() {\n this.removeClass('vjs-slider-active');\n }\n\n \/**\n * Adds vjs-hidden or vjs-mute-toggle-only to the VolumePanel\n * depending on MuteToggle and VolumeControl state\n *\n * @listens Player#loadstart\n * @private\n *\/\n volumePanelState_() {\n \/\/ hide volume panel if neither volume control or mute toggle\n \/\/ are displayed\n if (this.volumeControl.hasClass('vjs-hidden') && this.muteToggle.hasClass('vjs-hidden')) {\n this.addClass('vjs-hidden');\n }\n\n \/\/ if only mute toggle is visible we don't want\n \/\/ volume panel expanding when hovered or active\n if (this.volumeControl.hasClass('vjs-hidden') && !this.muteToggle.hasClass('vjs-hidden')) {\n this.addClass('vjs-mute-toggle-only');\n }\n }\n\n \/**\n * Create the `Component`'s DOM element\n *\n * @return {Element}\n * The element that was created.\n *\/\n createEl() {\n let orientationClass = 'vjs-volume-panel-horizontal';\n if (!this.options_.inline) {\n orientationClass = 'vjs-volume-panel-vertical';\n }\n return super.createEl('div', {\n className: `vjs-volume-panel vjs-control ${orientationClass}`\n });\n }\n\n \/**\n * Dispose of the `volume-panel` and all child components.\n *\/\n dispose() {\n this.handleMouseOut();\n super.dispose();\n }\n\n \/**\n * Handles `keyup` events on the `VolumeControl`, looking for ESC, which closes\n * the volume panel and sets focus on `MuteToggle`.\n *\n * @param {Event} event\n * The `keyup` event that caused this function to be called.\n *\n * @listens keyup\n *\/\n handleVolumeControlKeyUp(event) {\n if (event.key === 'Escape') {\n this.muteToggle.focus();\n }\n }\n\n \/**\n * This gets called when a `VolumePanel` gains hover via a `mouseover` event.\n * Turns on listening for `mouseover` event. When they happen it\n * calls `this.handleMouseOver`.\n *\n * @param {Event} event\n * The `mouseover` event that caused this function to be called.\n *\n * @listens mouseover\n *\/\n handleMouseOver(event) {\n this.addClass('vjs-hover');\n on(document, 'keyup', this.handleKeyPressHandler_);\n }\n\n \/**\n * This gets called when a `VolumePanel` gains hover via a `mouseout` event.\n * Turns on listening for `mouseout` event. When they happen it\n * calls `this.handleMouseOut`.\n *\n * @param {Event} event\n * The `mouseout` event that caused this function to be called.\n *\n * @listens mouseout\n *\/\n handleMouseOut(event) {\n this.removeClass('vjs-hover');\n off(document, 'keyup', this.handleKeyPressHandler_);\n }\n\n \/**\n * Handles `keyup` event on the document or `keydown` event on the `VolumePanel`,\n * looking for ESC, which hides the `VolumeControl`.\n *\n * @param {Event} event\n * The keypress that triggered this event.\n *\n * @listens keydown | keyup\n *\/\n handleKeyPress(event) {\n if (event.key === 'Escape') {\n this.handleMouseOut();\n }\n }\n }\n\n \/**\n * Default options for the `VolumeControl`\n *\n * @type {Object}\n * @private\n *\/\n VolumePanel.prototype.options_ = {\n children: ['muteToggle', 'volumeControl']\n };\n Component$1.registerComponent('VolumePanel', VolumePanel);\n\n \/**\n * Button to skip forward a configurable amount of time\n * through a video. Renders in the control bar.\n *\n * e.g. options: {controlBar: {skipButtons: forward: 5}}\n *\n * @extends Button\n *\/\n class SkipForward extends Button {\n constructor(player, options) {\n super(player, options);\n this.validOptions = [5, 10, 30];\n this.skipTime = this.getSkipForwardTime();\n if (this.skipTime && this.validOptions.includes(this.skipTime)) {\n this.setIcon(`forward-${this.skipTime}`);\n this.controlText(this.localize('Skip forward {1} seconds', [this.skipTime.toLocaleString(player.language())]));\n this.show();\n } else {\n this.hide();\n }\n }\n getSkipForwardTime() {\n const playerOptions = this.options_.playerOptions;\n return playerOptions.controlBar && playerOptions.controlBar.skipButtons && playerOptions.controlBar.skipButtons.forward;\n }\n buildCSSClass() {\n return `vjs-skip-forward-${this.getSkipForwardTime()} ${super.buildCSSClass()}`;\n }\n\n \/**\n * On click, skips forward in the duration\/seekable range by a configurable amount of seconds.\n * If the time left in the duration\/seekable range is less than the configured 'skip forward' time,\n * skips to end of duration\/seekable range.\n *\n * Handle a click on a `SkipForward` button\n *\n * @param {EventTarget~Event} event\n * The `click` event that caused this function\n * to be called\n *\/\n handleClick(event) {\n if (isNaN(this.player_.duration())) {\n return;\n }\n const currentVideoTime = this.player_.currentTime();\n const liveTracker = this.player_.liveTracker;\n const duration = liveTracker && liveTracker.isLive() ? liveTracker.seekableEnd() : this.player_.duration();\n let newTime;\n if (currentVideoTime + this.skipTime <= duration) {\n newTime = currentVideoTime + this.skipTime;\n } else {\n newTime = duration;\n }\n this.player_.currentTime(newTime);\n }\n\n \/**\n * Update control text on languagechange\n *\/\n handleLanguagechange() {\n this.controlText(this.localize('Skip forward {1} seconds', [this.skipTime]));\n }\n }\n SkipForward.prototype.controlText_ = 'Skip Forward';\n Component$1.registerComponent('SkipForward', SkipForward);\n\n \/**\n * Button to skip backward a configurable amount of time\n * through a video. Renders in the control bar.\n *\n * * e.g. options: {controlBar: {skipButtons: backward: 5}}\n *\n * @extends Button\n *\/\n class SkipBackward extends Button {\n constructor(player, options) {\n super(player, options);\n this.validOptions = [5, 10, 30];\n this.skipTime = this.getSkipBackwardTime();\n if (this.skipTime && this.validOptions.includes(this.skipTime)) {\n this.setIcon(`replay-${this.skipTime}`);\n this.controlText(this.localize('Skip backward {1} seconds', [this.skipTime.toLocaleString(player.language())]));\n this.show();\n } else {\n this.hide();\n }\n }\n getSkipBackwardTime() {\n const playerOptions = this.options_.playerOptions;\n return playerOptions.controlBar && playerOptions.controlBar.skipButtons && playerOptions.controlBar.skipButtons.backward;\n }\n buildCSSClass() {\n return `vjs-skip-backward-${this.getSkipBackwardTime()} ${super.buildCSSClass()}`;\n }\n\n \/**\n * On click, skips backward in the video by a configurable amount of seconds.\n * If the current time in the video is less than the configured 'skip backward' time,\n * skips to beginning of video or seekable range.\n *\n * Handle a click on a `SkipBackward` button\n *\n * @param {EventTarget~Event} event\n * The `click` event that caused this function\n * to be called\n *\/\n handleClick(event) {\n const currentVideoTime = this.player_.currentTime();\n const liveTracker = this.player_.liveTracker;\n const seekableStart = liveTracker && liveTracker.isLive() && liveTracker.seekableStart();\n let newTime;\n if (seekableStart && currentVideoTime - this.skipTime <= seekableStart) {\n newTime = seekableStart;\n } else if (currentVideoTime >= this.skipTime) {\n newTime = currentVideoTime - this.skipTime;\n } else {\n newTime = 0;\n }\n this.player_.currentTime(newTime);\n }\n\n \/**\n * Update control text on languagechange\n *\/\n handleLanguagechange() {\n this.controlText(this.localize('Skip backward {1} seconds', [this.skipTime]));\n }\n }\n SkipBackward.prototype.controlText_ = 'Skip Backward';\n Component$1.registerComponent('SkipBackward', SkipBackward);\n\n \/**\n * @file menu.js\n *\/\n\n \/** @import Player from '..\/player' *\/\n\n \/**\n * The Menu component is used to build popup menus, including subtitle and\n * captions selection menus.\n *\n * @extends Component\n *\/\n class Menu extends Component$1 {\n \/**\n * Create an instance of this class.\n *\n * @param {Player} player\n * the player that this component should attach to\n *\n * @param {Object} [options]\n * Object of option names and values\n *\n *\/\n constructor(player, options) {\n super(player, options);\n if (options) {\n this.menuButton_ = options.menuButton;\n }\n this.focusedChild_ = -1;\n this.on('keydown', e => this.handleKeyDown(e));\n\n \/\/ All the menu item instances share the same blur handler provided by the menu container.\n this.boundHandleBlur_ = e => this.handleBlur(e);\n this.boundHandleTapClick_ = e => this.handleTapClick(e);\n }\n\n \/**\n * Add event listeners to the {@link MenuItem}.\n *\n * @param {Object} component\n * The instance of the `MenuItem` to add listeners to.\n *\n *\/\n addEventListenerForItem(component) {\n if (!(component instanceof Component$1)) {\n return;\n }\n this.on(component, 'blur', this.boundHandleBlur_);\n this.on(component, ['tap', 'click'], this.boundHandleTapClick_);\n }\n\n \/**\n * Remove event listeners from the {@link MenuItem}.\n *\n * @param {Object} component\n * The instance of the `MenuItem` to remove listeners.\n *\n *\/\n removeEventListenerForItem(component) {\n if (!(component instanceof Component$1)) {\n return;\n }\n this.off(component, 'blur', this.boundHandleBlur_);\n this.off(component, ['tap', 'click'], this.boundHandleTapClick_);\n }\n\n \/**\n * This method will be called indirectly when the component has been added\n * before the component adds to the new menu instance by `addItem`.\n * In this case, the original menu instance will remove the component\n * by calling `removeChild`.\n *\n * @param {Object} component\n * The instance of the `MenuItem`\n *\/\n removeChild(component) {\n if (typeof component === 'string') {\n component = this.getChild(component);\n }\n this.removeEventListenerForItem(component);\n super.removeChild(component);\n }\n\n \/**\n * Add a {@link MenuItem} to the menu.\n *\n * @param {Object|string} component\n * The name or instance of the `MenuItem` to add.\n *\n *\/\n addItem(component) {\n const childComponent = this.addChild(component);\n if (childComponent) {\n this.addEventListenerForItem(childComponent);\n }\n }\n\n \/**\n * Create the `Menu`s DOM element.\n *\n * @return {Element}\n * the element that was created\n *\/\n createEl() {\n const contentElType = this.options_.contentElType || 'ul';\n this.contentEl_ = createEl(contentElType, {\n className: 'vjs-menu-content'\n });\n this.contentEl_.setAttribute('role', 'menu');\n const el = super.createEl('div', {\n append: this.contentEl_,\n className: 'vjs-menu'\n });\n el.appendChild(this.contentEl_);\n\n \/\/ Prevent clicks from bubbling up. Needed for Menu Buttons,\n \/\/ where a click on the parent is significant\n on(el, 'click', function (event) {\n event.preventDefault();\n event.stopImmediatePropagation();\n });\n return el;\n }\n dispose() {\n this.contentEl_ = null;\n this.boundHandleBlur_ = null;\n this.boundHandleTapClick_ = null;\n super.dispose();\n }\n\n \/**\n * Called when a `MenuItem` loses focus.\n *\n * @param {Event} event\n * The `blur` event that caused this function to be called.\n *\n * @listens blur\n *\/\n handleBlur(event) {\n const relatedTarget = event.relatedTarget || document.activeElement;\n\n \/\/ Close menu popup when a user clicks outside the menu\n if (!this.children().some(element => {\n return element.el() === relatedTarget;\n })) {\n const btn = this.menuButton_;\n if (btn && btn.buttonPressed_ && relatedTarget !== btn.el().firstChild) {\n btn.unpressButton();\n }\n }\n }\n\n \/**\n * Called when a `MenuItem` gets clicked or tapped.\n *\n * @param {Event} event\n * The `click` or `tap` event that caused this function to be called.\n *\n * @listens click,tap\n *\/\n handleTapClick(event) {\n \/\/ Unpress the associated MenuButton, and move focus back to it\n if (this.menuButton_) {\n this.menuButton_.unpressButton();\n const childComponents = this.children();\n if (!Array.isArray(childComponents)) {\n return;\n }\n const foundComponent = childComponents.filter(component => component.el() === event.target)[0];\n if (!foundComponent) {\n return;\n }\n\n \/\/ don't focus menu button if item is a caption settings item\n \/\/ because focus will move elsewhere\n if (foundComponent.name() !== 'CaptionSettingsMenuItem') {\n this.menuButton_.focus();\n }\n }\n }\n\n \/**\n * Handle a `keydown` event on this menu. This listener is added in the constructor.\n *\n * @param {KeyboardEvent} event\n * A `keydown` event that happened on the menu.\n *\n * @listens keydown\n *\/\n handleKeyDown(event) {\n \/\/ Left and Down Arrows\n if (event.key === 'ArrowLeft' || event.key === 'ArrowDown') {\n event.preventDefault();\n event.stopPropagation();\n this.stepForward();\n\n \/\/ Up and Right Arrows\n } else if (event.key === 'ArrowRight' || event.key === 'ArrowUp') {\n event.preventDefault();\n event.stopPropagation();\n this.stepBack();\n }\n }\n\n \/**\n * Move to next (lower) menu item for keyboard users.\n *\/\n stepForward() {\n let stepChild = 0;\n if (this.focusedChild_ !== undefined) {\n stepChild = this.focusedChild_ + 1;\n }\n this.focus(stepChild);\n }\n\n \/**\n * Move to previous (higher) menu item for keyboard users.\n *\/\n stepBack() {\n let stepChild = 0;\n if (this.focusedChild_ !== undefined) {\n stepChild = this.focusedChild_ - 1;\n }\n this.focus(stepChild);\n }\n\n \/**\n * Set focus on a {@link MenuItem} in the `Menu`.\n *\n * @param {Object|string} [item=0]\n * Index of child item set focus on.\n *\/\n focus(item = 0) {\n const children = this.children().slice();\n const haveTitle = children.length && children[0].hasClass('vjs-menu-title');\n if (haveTitle) {\n children.shift();\n }\n if (children.length > 0) {\n if (item < 0) {\n item = 0;\n } else if (item >= children.length) {\n item = children.length - 1;\n }\n this.focusedChild_ = item;\n children[item].el_.focus();\n }\n }\n }\n Component$1.registerComponent('Menu', Menu);\n\n \/**\n * @file menu-button.js\n *\/\n\n \/** @import Player from '..\/player' *\/\n\n \/**\n * A `MenuButton` class for any popup {@link Menu}.\n *\n * @extends Component\n *\/\n class MenuButton extends Component$1 {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options={}]\n * The key\/value store of player options.\n *\/\n constructor(player, options = {}) {\n super(player, options);\n this.menuButton_ = new Button(player, options);\n this.menuButton_.controlText(this.controlText_);\n this.menuButton_.el_.setAttribute('aria-haspopup', 'true');\n\n \/\/ Add buildCSSClass values to the button, not the wrapper\n const buttonClass = Button.prototype.buildCSSClass();\n this.menuButton_.el_.className = this.buildCSSClass() + ' ' + buttonClass;\n this.menuButton_.removeClass('vjs-control');\n this.addChild(this.menuButton_);\n this.update();\n this.enabled_ = true;\n const handleClick = e => this.handleClick(e);\n this.handleMenuKeyUp_ = e => this.handleMenuKeyUp(e);\n this.on(this.menuButton_, 'tap', handleClick);\n this.on(this.menuButton_, 'click', handleClick);\n this.on(this.menuButton_, 'keydown', e => this.handleKeyDown(e));\n this.on(this.menuButton_, 'mouseenter', () => {\n this.addClass('vjs-hover');\n this.menu.show();\n on(document, 'keyup', this.handleMenuKeyUp_);\n });\n this.on('mouseleave', e => this.handleMouseLeave(e));\n this.on('keydown', e => this.handleSubmenuKeyDown(e));\n }\n\n \/**\n * Update the menu based on the current state of its items.\n *\/\n update() {\n const menu = this.createMenu();\n if (this.menu) {\n this.menu.dispose();\n this.removeChild(this.menu);\n }\n this.menu = menu;\n this.addChild(menu);\n\n \/**\n * Track the state of the menu button\n *\n * @type {Boolean}\n * @private\n *\/\n this.buttonPressed_ = false;\n this.menuButton_.el_.setAttribute('aria-expanded', 'false');\n if (this.items && this.items.length <= this.hideThreshold_) {\n this.hide();\n this.menu.contentEl_.removeAttribute('role');\n } else {\n this.show();\n this.menu.contentEl_.setAttribute('role', 'menu');\n }\n }\n\n \/**\n * Create the menu and add all items to it.\n *\n * @return {Menu}\n * The constructed menu\n *\/\n createMenu() {\n const menu = new Menu(this.player_, {\n menuButton: this\n });\n\n \/**\n * Hide the menu if the number of items is less than or equal to this threshold. This defaults\n * to 0 and whenever we add items which can be hidden to the menu we'll increment it. We list\n * it here because every time we run `createMenu` we need to reset the value.\n *\n * @protected\n * @type {Number}\n *\/\n this.hideThreshold_ = 0;\n\n \/\/ Add a title list item to the top\n if (this.options_.title) {\n const titleEl = createEl('li', {\n className: 'vjs-menu-title',\n textContent: toTitleCase$1(this.options_.title),\n tabIndex: -1\n });\n const titleComponent = new Component$1(this.player_, {\n el: titleEl\n });\n menu.addItem(titleComponent);\n }\n this.items = this.createItems();\n if (this.items) {\n \/\/ Add menu items to the menu\n for (let i = 0; i < this.items.length; i++) {\n menu.addItem(this.items[i]);\n }\n }\n return menu;\n }\n\n \/**\n * Create the list of menu items. Specific to each subclass.\n *\n * @abstract\n *\/\n createItems() {}\n\n \/**\n * Create the `MenuButtons`s DOM element.\n *\n * @return {Element}\n * The element that gets created.\n *\/\n createEl() {\n return super.createEl('div', {\n className: this.buildWrapperCSSClass()\n }, {});\n }\n\n \/**\n * Overwrites the `setIcon` method from `Component`.\n * In this case, we want the icon to be appended to the menuButton.\n *\n * @param {string} name\n * The icon name to be added.\n *\/\n setIcon(name) {\n super.setIcon(name, this.menuButton_.el_);\n }\n\n \/**\n * Allow sub components to stack CSS class names for the wrapper element\n *\n * @return {string}\n * The constructed wrapper DOM `className`\n *\/\n buildWrapperCSSClass() {\n let menuButtonClass = 'vjs-menu-button';\n\n \/\/ If the inline option is passed, we want to use different styles altogether.\n if (this.options_.inline === true) {\n menuButtonClass += '-inline';\n } else {\n menuButtonClass += '-popup';\n }\n\n \/\/ TODO: Fix the CSS so that this isn't necessary\n const buttonClass = Button.prototype.buildCSSClass();\n return `vjs-menu-button ${menuButtonClass} ${buttonClass} ${super.buildCSSClass()}`;\n }\n\n \/**\n * Builds the default DOM `className`.\n *\n * @return {string}\n * The DOM `className` for this object.\n *\/\n buildCSSClass() {\n let menuButtonClass = 'vjs-menu-button';\n\n \/\/ If the inline option is passed, we want to use different styles altogether.\n if (this.options_.inline === true) {\n menuButtonClass += '-inline';\n } else {\n menuButtonClass += '-popup';\n }\n return `vjs-menu-button ${menuButtonClass} ${super.buildCSSClass()}`;\n }\n\n \/**\n * Get or set the localized control text that will be used for accessibility.\n *\n * > NOTE: This will come from the internal `menuButton_` element.\n *\n * @param {string} [text]\n * Control text for element.\n *\n * @param {Element} [el=this.menuButton_.el()]\n * Element to set the title on.\n *\n * @return {string}\n * - The control text when getting\n *\/\n controlText(text, el = this.menuButton_.el()) {\n return this.menuButton_.controlText(text, el);\n }\n\n \/**\n * Dispose of the `menu-button` and all child components.\n *\/\n dispose() {\n this.handleMouseLeave();\n super.dispose();\n }\n\n \/**\n * Handle a click on a `MenuButton`.\n * See {@link ClickableComponent#handleClick} for instances where this is called.\n *\n * @param {Event} event\n * The `keydown`, `tap`, or `click` event that caused this function to be\n * called.\n *\n * @listens tap\n * @listens click\n *\/\n handleClick(event) {\n if (this.buttonPressed_) {\n this.unpressButton();\n } else {\n this.pressButton();\n }\n }\n\n \/**\n * Handle `mouseleave` for `MenuButton`.\n *\n * @param {Event} event\n * The `mouseleave` event that caused this function to be called.\n *\n * @listens mouseleave\n *\/\n handleMouseLeave(event) {\n this.removeClass('vjs-hover');\n off(document, 'keyup', this.handleMenuKeyUp_);\n }\n\n \/**\n * Set the focus to the actual button, not to this element\n *\/\n focus() {\n this.menuButton_.focus();\n }\n\n \/**\n * Remove the focus from the actual button, not this element\n *\/\n blur() {\n this.menuButton_.blur();\n }\n\n \/**\n * Handle tab, escape, down arrow, and up arrow keys for `MenuButton`. See\n * {@link ClickableComponent#handleKeyDown} for instances where this is called.\n *\n * @param {Event} event\n * The `keydown` event that caused this function to be called.\n *\n * @listens keydown\n *\/\n handleKeyDown(event) {\n \/\/ Escape or Tab unpress the 'button'\n if (event.key === 'Esc' || event.key === 'Tab') {\n if (this.buttonPressed_) {\n this.unpressButton();\n }\n\n \/\/ Don't preventDefault for Tab key - we still want to lose focus\n if (!event.key === 'Tab') {\n event.preventDefault();\n \/\/ Set focus back to the menu button's button\n this.menuButton_.focus();\n }\n \/\/ Up Arrow or Down Arrow also 'press' the button to open the menu\n } else if (event.key === 'Up' || event.key === 'Down' && !(this.player_.options_.playerOptions.spatialNavigation && this.player_.options_.playerOptions.spatialNavigation.enabled)) {\n if (!this.buttonPressed_) {\n event.preventDefault();\n this.pressButton();\n }\n }\n }\n\n \/**\n * Handle a `keyup` event on a `MenuButton`. The listener for this is added in\n * the constructor.\n *\n * @param {Event} event\n * Key press event\n *\n * @listens keyup\n *\/\n handleMenuKeyUp(event) {\n \/\/ Escape hides popup menu\n if (event.key === 'Esc' || event.key === 'Tab') {\n this.removeClass('vjs-hover');\n }\n }\n\n \/**\n * This method name now delegates to `handleSubmenuKeyDown`. This means\n * anyone calling `handleSubmenuKeyPress` will not see their method calls\n * stop working.\n *\n * @param {Event} event\n * The event that caused this function to be called.\n *\/\n handleSubmenuKeyPress(event) {\n this.handleSubmenuKeyDown(event);\n }\n\n \/**\n * Handle a `keydown` event on a sub-menu. The listener for this is added in\n * the constructor.\n *\n * @param {Event} event\n * Key press event\n *\n * @listens keydown\n *\/\n handleSubmenuKeyDown(event) {\n \/\/ Escape or Tab unpress the 'button'\n if (event.key === 'Esc' || event.key === 'Tab') {\n if (this.buttonPressed_) {\n this.unpressButton();\n }\n \/\/ Don't preventDefault for Tab key - we still want to lose focus\n if (!event.key === 'Tab') {\n event.preventDefault();\n \/\/ Set focus back to the menu button's button\n this.menuButton_.focus();\n }\n }\n }\n\n \/**\n * Put the current `MenuButton` into a pressed state.\n *\/\n pressButton() {\n if (this.enabled_) {\n this.buttonPressed_ = true;\n this.menu.show();\n this.menu.lockShowing();\n this.menuButton_.el_.setAttribute('aria-expanded', 'true');\n\n \/\/ set the focus into the submenu, except on iOS where it is resulting in\n \/\/ undesired scrolling behavior when the player is in an iframe\n if (IS_IOS && isInFrame()) {\n \/\/ Return early so that the menu isn't focused\n return;\n }\n this.menu.focus();\n }\n }\n\n \/**\n * Take the current `MenuButton` out of a pressed state.\n *\/\n unpressButton() {\n if (this.enabled_) {\n this.buttonPressed_ = false;\n this.menu.unlockShowing();\n this.menu.hide();\n this.menuButton_.el_.setAttribute('aria-expanded', 'false');\n }\n }\n\n \/**\n * Disable the `MenuButton`. Don't allow it to be clicked.\n *\/\n disable() {\n this.unpressButton();\n this.enabled_ = false;\n this.addClass('vjs-disabled');\n this.menuButton_.disable();\n }\n\n \/**\n * Enable the `MenuButton`. Allow it to be clicked.\n *\/\n enable() {\n this.enabled_ = true;\n this.removeClass('vjs-disabled');\n this.menuButton_.enable();\n }\n }\n Component$1.registerComponent('MenuButton', MenuButton);\n\n \/**\n * @file track-button.js\n *\/\n\n \/** @import Player from '.\/player' *\/\n\n \/**\n * The base class for buttons that toggle specific track types (e.g. subtitles).\n *\n * @extends MenuButton\n *\/\n class TrackButton extends MenuButton {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options]\n * The key\/value store of player options.\n *\/\n constructor(player, options) {\n const tracks = options.tracks;\n super(player, options);\n if (this.items.length <= 1) {\n this.hide();\n }\n if (!tracks) {\n return;\n }\n const updateHandler = bind_(this, this.update);\n tracks.addEventListener('removetrack', updateHandler);\n tracks.addEventListener('addtrack', updateHandler);\n tracks.addEventListener('labelchange', updateHandler);\n this.player_.on('ready', updateHandler);\n this.player_.on('dispose', function () {\n tracks.removeEventListener('removetrack', updateHandler);\n tracks.removeEventListener('addtrack', updateHandler);\n tracks.removeEventListener('labelchange', updateHandler);\n });\n }\n }\n Component$1.registerComponent('TrackButton', TrackButton);\n\n \/**\n * @file menu-item.js\n *\/\n\n \/** @import Player from '..\/player' *\/\n\n \/**\n * The component for a menu item. `
  • `\n *\n * @extends ClickableComponent\n *\/\n class MenuItem extends ClickableComponent {\n \/**\n * Creates an instance of the this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options={}]\n * The key\/value store of player options.\n *\n *\/\n constructor(player, options) {\n super(player, options);\n this.selectable = options.selectable;\n this.isSelected_ = options.selected || false;\n this.multiSelectable = options.multiSelectable;\n this.selected(this.isSelected_);\n if (this.selectable) {\n if (this.multiSelectable) {\n this.el_.setAttribute('role', 'menuitemcheckbox');\n } else {\n this.el_.setAttribute('role', 'menuitemradio');\n }\n } else {\n this.el_.setAttribute('role', 'menuitem');\n }\n }\n\n \/**\n * Create the `MenuItem's DOM element\n *\n * @param {string} [type=li]\n * Element's node type, not actually used, always set to `li`.\n *\n * @param {Object} [props={}]\n * An object of properties that should be set on the element\n *\n * @param {Object} [attrs={}]\n * An object of attributes that should be set on the element\n *\n * @return {Element}\n * The element that gets created.\n *\/\n createEl(type, props, attrs) {\n \/\/ The control is textual, not just an icon\n this.nonIconControl = true;\n const el = super.createEl('li', Object.assign({\n className: 'vjs-menu-item',\n tabIndex: -1\n }, props), attrs);\n\n \/\/ swap icon with menu item text.\n const menuItemEl = createEl('span', {\n className: 'vjs-menu-item-text',\n textContent: this.localize(this.options_.label)\n });\n\n \/\/ If using SVG icons, the element with vjs-icon-placeholder will be added separately.\n if (this.player_.options_.experimentalSvgIcons) {\n el.appendChild(menuItemEl);\n } else {\n el.replaceChild(menuItemEl, el.querySelector('.vjs-icon-placeholder'));\n }\n return el;\n }\n\n \/**\n * Ignore keys which are used by the menu, but pass any other ones up. See\n * {@link ClickableComponent#handleKeyDown} for instances where this is called.\n *\n * @param {KeyboardEvent} event\n * The `keydown` event that caused this function to be called.\n *\n * @listens keydown\n *\/\n handleKeyDown(event) {\n if (!['Tab', 'Escape', 'ArrowUp', 'ArrowLeft', 'ArrowRight', 'ArrowDown'].includes(event.key)) {\n \/\/ Pass keydown handling up for unused keys\n super.handleKeyDown(event);\n }\n }\n\n \/**\n * Any click on a `MenuItem` puts it into the selected state.\n * See {@link ClickableComponent#handleClick} for instances where this is called.\n *\n * @param {Event} event\n * The `keydown`, `tap`, or `click` event that caused this function to be\n * called.\n *\n * @listens tap\n * @listens click\n *\/\n handleClick(event) {\n this.selected(true);\n }\n\n \/**\n * Set the state for this menu item as selected or not.\n *\n * @param {boolean} selected\n * if the menu item is selected or not\n *\/\n selected(selected) {\n if (this.selectable) {\n if (selected) {\n this.addClass('vjs-selected');\n this.el_.setAttribute('aria-checked', 'true');\n \/\/ aria-checked isn't fully supported by browsers\/screen readers,\n \/\/ so indicate selected state to screen reader in the control text.\n this.controlText(', selected');\n this.isSelected_ = true;\n } else {\n this.removeClass('vjs-selected');\n this.el_.setAttribute('aria-checked', 'false');\n \/\/ Indicate un-selected state to screen reader\n this.controlText('');\n this.isSelected_ = false;\n }\n }\n }\n }\n Component$1.registerComponent('MenuItem', MenuItem);\n\n \/**\n * @file text-track-menu-item.js\n *\/\n\n \/** @import Player from '..\/..\/player' *\/\n\n \/**\n * The specific menu item type for selecting a language within a text track kind\n *\n * @extends MenuItem\n *\/\n class TextTrackMenuItem extends MenuItem {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options]\n * The key\/value store of player options.\n *\/\n constructor(player, options) {\n const track = options.track;\n const tracks = player.textTracks();\n\n \/\/ Modify options for parent MenuItem class's init.\n options.label = track.label || track.language || 'Unknown';\n options.selected = track.mode === 'showing';\n super(player, options);\n this.track = track;\n \/\/ Determine the relevant kind(s) of tracks for this component and filter\n \/\/ out empty kinds.\n this.kinds = (options.kinds || [options.kind || this.track.kind]).filter(Boolean);\n const changeHandler = (...args) => {\n this.handleTracksChange.apply(this, args);\n };\n const selectedLanguageChangeHandler = (...args) => {\n this.handleSelectedLanguageChange.apply(this, args);\n };\n player.on(['loadstart', 'texttrackchange'], changeHandler);\n tracks.addEventListener('change', changeHandler);\n tracks.addEventListener('selectedlanguagechange', selectedLanguageChangeHandler);\n this.on('dispose', function () {\n player.off(['loadstart', 'texttrackchange'], changeHandler);\n tracks.removeEventListener('change', changeHandler);\n tracks.removeEventListener('selectedlanguagechange', selectedLanguageChangeHandler);\n });\n\n \/\/ iOS7 doesn't dispatch change events to TextTrackLists when an\n \/\/ associated track's mode changes. Without something like\n \/\/ Object.observe() (also not present on iOS7), it's not\n \/\/ possible to detect changes to the mode attribute and polyfill\n \/\/ the change event. As a poor substitute, we manually dispatch\n \/\/ change events whenever the controls modify the mode.\n if (tracks.onchange === undefined) {\n let event;\n this.on(['tap', 'click'], function () {\n if (typeof window.Event !== 'object') {\n \/\/ Android 2.3 throws an Illegal Constructor error for window.Event\n try {\n event = new window.Event('change');\n } catch (err) {\n \/\/ continue regardless of error\n }\n }\n if (!event) {\n event = document.createEvent('Event');\n event.initEvent('change', true, true);\n }\n tracks.dispatchEvent(event);\n });\n }\n\n \/\/ set the default state based on current tracks\n this.handleTracksChange();\n }\n\n \/**\n * This gets called when an `TextTrackMenuItem` is \"clicked\". See\n * {@link ClickableComponent} for more detailed information on what a click can be.\n *\n * @param {Event} event\n * The `keydown`, `tap`, or `click` event that caused this function to be\n * called.\n *\n * @listens tap\n * @listens click\n *\/\n handleClick(event) {\n const referenceTrack = this.track;\n const tracks = this.player_.textTracks();\n super.handleClick(event);\n if (!tracks) {\n return;\n }\n for (let i = 0; i < tracks.length; i++) {\n const track = tracks[i];\n\n \/\/ If the track from the text tracks list is not of the right kind,\n \/\/ skip it. We do not want to affect tracks of incompatible kind(s).\n if (this.kinds.indexOf(track.kind) === -1) {\n continue;\n }\n\n \/\/ If this text track is the component's track and it is not showing,\n \/\/ set it to showing.\n if (track === referenceTrack) {\n if (track.mode !== 'showing') {\n track.mode = 'showing';\n }\n\n \/\/ If this text track is not the component's track and it is not\n \/\/ disabled, set it to disabled.\n } else if (track.mode !== 'disabled') {\n track.mode = 'disabled';\n }\n }\n }\n\n \/**\n * Handle text track list change\n *\n * @param {Event} event\n * The `change` event that caused this function to be called.\n *\n * @listens TextTrackList#change\n *\/\n handleTracksChange(event) {\n const shouldBeSelected = this.track.mode === 'showing';\n\n \/\/ Prevent redundant selected() calls because they may cause\n \/\/ screen readers to read the appended control text unnecessarily\n if (shouldBeSelected !== this.isSelected_) {\n this.selected(shouldBeSelected);\n }\n }\n handleSelectedLanguageChange(event) {\n if (this.track.mode === 'showing') {\n const selectedLanguage = this.player_.cache_.selectedLanguage;\n\n \/\/ Don't replace the kind of track across the same language\n if (selectedLanguage && selectedLanguage.enabled && selectedLanguage.language === this.track.language && selectedLanguage.kind !== this.track.kind) {\n return;\n }\n this.player_.cache_.selectedLanguage = {\n enabled: true,\n language: this.track.language,\n kind: this.track.kind\n };\n }\n }\n dispose() {\n \/\/ remove reference to track object on dispose\n this.track = null;\n super.dispose();\n }\n }\n Component$1.registerComponent('TextTrackMenuItem', TextTrackMenuItem);\n\n \/**\n * @file off-text-track-menu-item.js\n *\/\n\n \/** @import Player from '..\/..\/player' *\/\n\n \/**\n * A special menu item for turning off a specific type of text track\n *\n * @extends TextTrackMenuItem\n *\/\n class OffTextTrackMenuItem extends TextTrackMenuItem {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options]\n * The key\/value store of player options.\n *\/\n constructor(player, options) {\n \/\/ Create pseudo track info\n \/\/ Requires options['kind']\n options.track = {\n player,\n \/\/ it is no longer necessary to store `kind` or `kinds` on the track itself\n \/\/ since they are now stored in the `kinds` property of all instances of\n \/\/ TextTrackMenuItem, but this will remain for backwards compatibility\n kind: options.kind,\n kinds: options.kinds,\n default: false,\n mode: 'disabled'\n };\n if (!options.kinds) {\n options.kinds = [options.kind];\n }\n if (options.label) {\n options.track.label = options.label;\n } else {\n options.track.label = options.kinds.join(' and ') + ' off';\n }\n\n \/\/ MenuItem is selectable\n options.selectable = true;\n \/\/ MenuItem is NOT multiSelectable (i.e. only one can be marked \"selected\" at a time)\n options.multiSelectable = false;\n super(player, options);\n }\n\n \/**\n * Handle text track change\n *\n * @param {Event} event\n * The event that caused this function to run\n *\/\n handleTracksChange(event) {\n const tracks = this.player().textTracks();\n let shouldBeSelected = true;\n for (let i = 0, l = tracks.length; i < l; i++) {\n const track = tracks[i];\n if (this.options_.kinds.indexOf(track.kind) > -1 && track.mode === 'showing') {\n shouldBeSelected = false;\n break;\n }\n }\n\n \/\/ Prevent redundant selected() calls because they may cause\n \/\/ screen readers to read the appended control text unnecessarily\n if (shouldBeSelected !== this.isSelected_) {\n this.selected(shouldBeSelected);\n }\n }\n handleSelectedLanguageChange(event) {\n const tracks = this.player().textTracks();\n let allHidden = true;\n for (let i = 0, l = tracks.length; i < l; i++) {\n const track = tracks[i];\n if (['captions', 'descriptions', 'subtitles'].indexOf(track.kind) > -1 && track.mode === 'showing') {\n allHidden = false;\n break;\n }\n }\n if (allHidden) {\n this.player_.cache_.selectedLanguage = {\n enabled: false\n };\n }\n }\n\n \/**\n * Update control text and label on languagechange\n *\/\n handleLanguagechange() {\n this.$('.vjs-menu-item-text').textContent = this.player_.localize(this.options_.label);\n super.handleLanguagechange();\n }\n }\n Component$1.registerComponent('OffTextTrackMenuItem', OffTextTrackMenuItem);\n\n \/**\n * @file text-track-button.js\n *\/\n\n \/** @import Player from '..\/..\/player' *\/\n\n \/**\n * The base class for buttons that toggle specific text track types (e.g. subtitles)\n *\n * @extends MenuButton\n *\/\n class TextTrackButton extends TrackButton {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options={}]\n * The key\/value store of player options.\n *\/\n constructor(player, options = {}) {\n options.tracks = player.textTracks();\n super(player, options);\n }\n\n \/**\n * Create a menu item for each text track\n *\n * @param {TextTrackMenuItem[]} [items=[]]\n * Existing array of items to use during creation\n *\n * @return {TextTrackMenuItem[]}\n * Array of menu items that were created\n *\/\n createItems(items = [], TrackMenuItem = TextTrackMenuItem) {\n \/\/ Label is an override for the [track] off label\n \/\/ USed to localise captions\/subtitles\n let label;\n if (this.label_) {\n label = `${this.label_} off`;\n }\n \/\/ Add an OFF menu item to turn all tracks off\n items.push(new OffTextTrackMenuItem(this.player_, {\n kinds: this.kinds_,\n kind: this.kind_,\n label\n }));\n this.hideThreshold_ += 1;\n const tracks = this.player_.textTracks();\n if (!Array.isArray(this.kinds_)) {\n this.kinds_ = [this.kind_];\n }\n for (let i = 0; i < tracks.length; i++) {\n const track = tracks[i];\n\n \/\/ only add tracks that are of an appropriate kind and have a label\n if (this.kinds_.indexOf(track.kind) > -1) {\n const item = new TrackMenuItem(this.player_, {\n track,\n kinds: this.kinds_,\n kind: this.kind_,\n \/\/ MenuItem is selectable\n selectable: true,\n \/\/ MenuItem is NOT multiSelectable (i.e. only one can be marked \"selected\" at a time)\n multiSelectable: false\n });\n item.addClass(`vjs-${track.kind}-menu-item`);\n items.push(item);\n }\n }\n return items;\n }\n }\n Component$1.registerComponent('TextTrackButton', TextTrackButton);\n\n \/**\n * @file chapters-track-menu-item.js\n *\/\n\n \/** @import Player from '..\/..\/player' *\/\n\n \/**\n * The chapter track menu item\n *\n * @extends MenuItem\n *\/\n class ChaptersTrackMenuItem extends MenuItem {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options]\n * The key\/value store of player options.\n *\/\n constructor(player, options) {\n const track = options.track;\n const cue = options.cue;\n const currentTime = player.currentTime();\n\n \/\/ Modify options for parent MenuItem class's init.\n options.selectable = true;\n options.multiSelectable = false;\n options.label = cue.text;\n options.selected = cue.startTime <= currentTime && currentTime < cue.endTime;\n super(player, options);\n this.track = track;\n this.cue = cue;\n }\n\n \/**\n * This gets called when an `ChaptersTrackMenuItem` is \"clicked\". See\n * {@link ClickableComponent} for more detailed information on what a click can be.\n *\n * @param {Event} [event]\n * The `keydown`, `tap`, or `click` event that caused this function to be\n * called.\n *\n * @listens tap\n * @listens click\n *\/\n handleClick(event) {\n super.handleClick();\n this.player_.currentTime(this.cue.startTime);\n }\n }\n Component$1.registerComponent('ChaptersTrackMenuItem', ChaptersTrackMenuItem);\n\n \/**\n * @file chapters-button.js\n *\/\n\n \/** @import Player from '..\/..\/player' *\/\n \/** @import Menu from '..\/..\/menu\/menu' *\/\n \/** @import TextTrack from '..\/..\/tracks\/text-track' *\/\n \/** @import TextTrackMenuItem from '..\/text-track-controls\/text-track-menu-item' *\/\n\n \/**\n * The button component for toggling and selecting chapters\n * Chapters act much differently than other text tracks\n * Cues are navigation vs. other tracks of alternative languages\n *\n * @extends TextTrackButton\n *\/\n class ChaptersButton extends TextTrackButton {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options]\n * The key\/value store of player options.\n *\n * @param {Function} [ready]\n * The function to call when this function is ready.\n *\/\n constructor(player, options, ready) {\n super(player, options, ready);\n this.setIcon('chapters');\n this.selectCurrentItem_ = () => {\n this.items.forEach(item => {\n item.selected(this.track_.activeCues[0] === item.cue);\n });\n };\n }\n\n \/**\n * Builds the default DOM `className`.\n *\n * @return {string}\n * The DOM `className` for this object.\n *\/\n buildCSSClass() {\n return `vjs-chapters-button ${super.buildCSSClass()}`;\n }\n buildWrapperCSSClass() {\n return `vjs-chapters-button ${super.buildWrapperCSSClass()}`;\n }\n\n \/**\n * Update the menu based on the current state of its items.\n *\n * @param {Event} [event]\n * An event that triggered this function to run.\n *\n * @listens TextTrackList#addtrack\n * @listens TextTrackList#removetrack\n * @listens TextTrackList#change\n *\/\n update(event) {\n if (event && event.track && event.track.kind !== 'chapters') {\n return;\n }\n const track = this.findChaptersTrack();\n if (track !== this.track_) {\n this.setTrack(track);\n super.update();\n } else if (!this.items || track && track.cues && track.cues.length !== this.items.length) {\n \/\/ Update the menu initially or if the number of cues has changed since set\n super.update();\n }\n }\n\n \/**\n * Set the currently selected track for the chapters button.\n *\n * @param {TextTrack} track\n * The new track to select. Nothing will change if this is the currently selected\n * track.\n *\/\n setTrack(track) {\n if (this.track_ === track) {\n return;\n }\n if (!this.updateHandler_) {\n this.updateHandler_ = this.update.bind(this);\n }\n\n \/\/ here this.track_ refers to the old track instance\n if (this.track_) {\n const remoteTextTrackEl = this.player_.remoteTextTrackEls().getTrackElementByTrack_(this.track_);\n if (remoteTextTrackEl) {\n remoteTextTrackEl.removeEventListener('load', this.updateHandler_);\n }\n this.track_.removeEventListener('cuechange', this.selectCurrentItem_);\n this.track_ = null;\n }\n this.track_ = track;\n\n \/\/ here this.track_ refers to the new track instance\n if (this.track_) {\n this.track_.mode = 'hidden';\n const remoteTextTrackEl = this.player_.remoteTextTrackEls().getTrackElementByTrack_(this.track_);\n if (remoteTextTrackEl) {\n remoteTextTrackEl.addEventListener('load', this.updateHandler_);\n }\n this.track_.addEventListener('cuechange', this.selectCurrentItem_);\n }\n }\n\n \/**\n * Find the track object that is currently in use by this ChaptersButton\n *\n * @return {TextTrack|undefined}\n * The current track or undefined if none was found.\n *\/\n findChaptersTrack() {\n const tracks = this.player_.textTracks() || [];\n for (let i = tracks.length - 1; i >= 0; i--) {\n \/\/ We will always choose the last track as our chaptersTrack\n const track = tracks[i];\n if (track.kind === this.kind_) {\n return track;\n }\n }\n }\n\n \/**\n * Get the caption for the ChaptersButton based on the track label. This will also\n * use the current tracks localized kind as a fallback if a label does not exist.\n *\n * @return {string}\n * The tracks current label or the localized track kind.\n *\/\n getMenuCaption() {\n if (this.track_ && this.track_.label) {\n return this.track_.label;\n }\n return this.localize(toTitleCase$1(this.kind_));\n }\n\n \/**\n * Create menu from chapter track\n *\n * @return {Menu}\n * New menu for the chapter buttons\n *\/\n createMenu() {\n this.options_.title = this.getMenuCaption();\n return super.createMenu();\n }\n\n \/**\n * Create a menu item for each text track\n *\n * @return {TextTrackMenuItem[]}\n * Array of menu items\n *\/\n createItems() {\n const items = [];\n if (!this.track_) {\n return items;\n }\n const cues = this.track_.cues;\n if (!cues) {\n return items;\n }\n for (let i = 0, l = cues.length; i < l; i++) {\n const cue = cues[i];\n const mi = new ChaptersTrackMenuItem(this.player_, {\n track: this.track_,\n cue\n });\n items.push(mi);\n }\n return items;\n }\n }\n\n \/**\n * `kind` of TextTrack to look for to associate it with this menu.\n *\n * @type {string}\n * @private\n *\/\n ChaptersButton.prototype.kind_ = 'chapters';\n\n \/**\n * The text that should display over the `ChaptersButton`s controls. Added for localization.\n *\n * @type {string}\n * @protected\n *\/\n ChaptersButton.prototype.controlText_ = 'Chapters';\n Component$1.registerComponent('ChaptersButton', ChaptersButton);\n\n \/**\n * @file descriptions-button.js\n *\/\n\n \/** @import Player from '..\/..\/player' *\/\n\n \/**\n * The button component for toggling and selecting descriptions\n *\n * @extends TextTrackButton\n *\/\n class DescriptionsButton extends TextTrackButton {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options]\n * The key\/value store of player options.\n *\n * @param {Function} [ready]\n * The function to call when this component is ready.\n *\/\n constructor(player, options, ready) {\n super(player, options, ready);\n this.setIcon('audio-description');\n const tracks = player.textTracks();\n const changeHandler = bind_(this, this.handleTracksChange);\n tracks.addEventListener('change', changeHandler);\n this.on('dispose', function () {\n tracks.removeEventListener('change', changeHandler);\n });\n }\n\n \/**\n * Handle text track change\n *\n * @param {Event} event\n * The event that caused this function to run\n *\n * @listens TextTrackList#change\n *\/\n handleTracksChange(event) {\n const tracks = this.player().textTracks();\n let disabled = false;\n\n \/\/ Check whether a track of a different kind is showing\n for (let i = 0, l = tracks.length; i < l; i++) {\n const track = tracks[i];\n if (track.kind !== this.kind_ && track.mode === 'showing') {\n disabled = true;\n break;\n }\n }\n\n \/\/ If another track is showing, disable this menu button\n if (disabled) {\n this.disable();\n } else {\n this.enable();\n }\n }\n\n \/**\n * Builds the default DOM `className`.\n *\n * @return {string}\n * The DOM `className` for this object.\n *\/\n buildCSSClass() {\n return `vjs-descriptions-button ${super.buildCSSClass()}`;\n }\n buildWrapperCSSClass() {\n return `vjs-descriptions-button ${super.buildWrapperCSSClass()}`;\n }\n }\n\n \/**\n * `kind` of TextTrack to look for to associate it with this menu.\n *\n * @type {string}\n * @private\n *\/\n DescriptionsButton.prototype.kind_ = 'descriptions';\n\n \/**\n * The text that should display over the `DescriptionsButton`s controls. Added for localization.\n *\n * @type {string}\n * @protected\n *\/\n DescriptionsButton.prototype.controlText_ = 'Descriptions';\n Component$1.registerComponent('DescriptionsButton', DescriptionsButton);\n\n \/**\n * @file subtitles-button.js\n *\/\n\n \/** @import Player from '..\/..\/player' *\/\n\n \/**\n * The button component for toggling and selecting subtitles\n *\n * @extends TextTrackButton\n *\/\n class SubtitlesButton extends TextTrackButton {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options]\n * The key\/value store of player options.\n *\n * @param {Function} [ready]\n * The function to call when this component is ready.\n *\/\n constructor(player, options, ready) {\n super(player, options, ready);\n this.setIcon('subtitles');\n }\n\n \/**\n * Builds the default DOM `className`.\n *\n * @return {string}\n * The DOM `className` for this object.\n *\/\n buildCSSClass() {\n return `vjs-subtitles-button ${super.buildCSSClass()}`;\n }\n buildWrapperCSSClass() {\n return `vjs-subtitles-button ${super.buildWrapperCSSClass()}`;\n }\n }\n\n \/**\n * `kind` of TextTrack to look for to associate it with this menu.\n *\n * @type {string}\n * @private\n *\/\n SubtitlesButton.prototype.kind_ = 'subtitles';\n\n \/**\n * The text that should display over the `SubtitlesButton`s controls. Added for localization.\n *\n * @type {string}\n * @protected\n *\/\n SubtitlesButton.prototype.controlText_ = 'Subtitles';\n Component$1.registerComponent('SubtitlesButton', SubtitlesButton);\n\n \/**\n * @file caption-settings-menu-item.js\n *\/\n\n \/** @import Player from '..\/..\/player' *\/\n\n \/**\n * The menu item for caption track settings menu\n *\n * @extends TextTrackMenuItem\n *\/\n class CaptionSettingsMenuItem extends TextTrackMenuItem {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options]\n * The key\/value store of player options.\n *\/\n constructor(player, options) {\n options.track = {\n player,\n kind: options.kind,\n label: options.kind + ' settings',\n selectable: false,\n default: false,\n mode: 'disabled'\n };\n\n \/\/ CaptionSettingsMenuItem has no concept of 'selected'\n options.selectable = false;\n options.name = 'CaptionSettingsMenuItem';\n super(player, options);\n this.addClass('vjs-texttrack-settings');\n this.controlText(', opens ' + options.kind + ' settings dialog');\n }\n\n \/**\n * This gets called when an `CaptionSettingsMenuItem` is \"clicked\". See\n * {@link ClickableComponent} for more detailed information on what a click can be.\n *\n * @param {Event} [event]\n * The `keydown`, `tap`, or `click` event that caused this function to be\n * called.\n *\n * @listens tap\n * @listens click\n *\/\n handleClick(event) {\n this.player().getChild('textTrackSettings').open();\n }\n\n \/**\n * Update control text and label on languagechange\n *\/\n handleLanguagechange() {\n this.$('.vjs-menu-item-text').textContent = this.player_.localize(this.options_.kind + ' settings');\n super.handleLanguagechange();\n }\n }\n Component$1.registerComponent('CaptionSettingsMenuItem', CaptionSettingsMenuItem);\n\n \/**\n * @file captions-button.js\n *\/\n\n \/** @import Player from '..\/..\/player' *\/\n\n \/**\n * The button component for toggling and selecting captions\n *\n * @extends TextTrackButton\n *\/\n class CaptionsButton extends TextTrackButton {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options]\n * The key\/value store of player options.\n *\n * @param {Function} [ready]\n * The function to call when this component is ready.\n *\/\n constructor(player, options, ready) {\n super(player, options, ready);\n this.setIcon('captions');\n }\n\n \/**\n * Builds the default DOM `className`.\n *\n * @return {string}\n * The DOM `className` for this object.\n *\/\n buildCSSClass() {\n return `vjs-captions-button ${super.buildCSSClass()}`;\n }\n buildWrapperCSSClass() {\n return `vjs-captions-button ${super.buildWrapperCSSClass()}`;\n }\n\n \/**\n * Create caption menu items\n *\n * @return {CaptionSettingsMenuItem[]}\n * The array of current menu items.\n *\/\n createItems() {\n const items = [];\n if (!(this.player().tech_ && this.player().tech_.featuresNativeTextTracks) && this.player().getChild('textTrackSettings')) {\n items.push(new CaptionSettingsMenuItem(this.player_, {\n kind: this.kind_\n }));\n this.hideThreshold_ += 1;\n }\n return super.createItems(items);\n }\n }\n\n \/**\n * `kind` of TextTrack to look for to associate it with this menu.\n *\n * @type {string}\n * @private\n *\/\n CaptionsButton.prototype.kind_ = 'captions';\n\n \/**\n * The text that should display over the `CaptionsButton`s controls. Added for localization.\n *\n * @type {string}\n * @protected\n *\/\n CaptionsButton.prototype.controlText_ = 'Captions';\n Component$1.registerComponent('CaptionsButton', CaptionsButton);\n\n \/**\n * @file subs-caps-menu-item.js\n *\/\n\n \/**\n * SubsCapsMenuItem has an [cc] icon to distinguish captions from subtitles\n * in the SubsCapsMenu.\n *\n * @extends TextTrackMenuItem\n *\/\n class SubsCapsMenuItem extends TextTrackMenuItem {\n createEl(type, props, attrs) {\n const el = super.createEl(type, props, attrs);\n const parentSpan = el.querySelector('.vjs-menu-item-text');\n if (this.options_.track.kind === 'captions') {\n if (this.player_.options_.experimentalSvgIcons) {\n this.setIcon('captions', el);\n } else {\n parentSpan.appendChild(createEl('span', {\n className: 'vjs-icon-placeholder'\n }, {\n 'aria-hidden': true\n }));\n }\n parentSpan.appendChild(createEl('span', {\n className: 'vjs-control-text',\n \/\/ space added as the text will visually flow with the\n \/\/ label\n textContent: ` ${this.localize('Captions')}`\n }));\n }\n return el;\n }\n }\n Component$1.registerComponent('SubsCapsMenuItem', SubsCapsMenuItem);\n\n \/**\n * @file sub-caps-button.js\n *\/\n\n \/** @import Player from '..\/..\/player' *\/\n\n \/**\n * The button component for toggling and selecting captions and\/or subtitles\n *\n * @extends TextTrackButton\n *\/\n class SubsCapsButton extends TextTrackButton {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options]\n * The key\/value store of player options.\n *\n * @param {Function} [ready]\n * The function to call when this component is ready.\n *\/\n constructor(player, options = {}) {\n super(player, options);\n\n \/\/ Although North America uses \"captions\" in most cases for\n \/\/ \"captions and subtitles\" other locales use \"subtitles\"\n this.label_ = 'subtitles';\n this.setIcon('subtitles');\n if (['en', 'en-us', 'en-ca', 'fr-ca'].indexOf(this.player_.language_) > -1) {\n this.label_ = 'captions';\n this.setIcon('captions');\n }\n this.menuButton_.controlText(toTitleCase$1(this.label_));\n }\n\n \/**\n * Builds the default DOM `className`.\n *\n * @return {string}\n * The DOM `className` for this object.\n *\/\n buildCSSClass() {\n return `vjs-subs-caps-button ${super.buildCSSClass()}`;\n }\n buildWrapperCSSClass() {\n return `vjs-subs-caps-button ${super.buildWrapperCSSClass()}`;\n }\n\n \/**\n * Create caption\/subtitles menu items\n *\n * @return {CaptionSettingsMenuItem[]}\n * The array of current menu items.\n *\/\n createItems() {\n let items = [];\n if (!(this.player().tech_ && this.player().tech_.featuresNativeTextTracks) && this.player().getChild('textTrackSettings')) {\n items.push(new CaptionSettingsMenuItem(this.player_, {\n kind: this.label_\n }));\n this.hideThreshold_ += 1;\n }\n items = super.createItems(items, SubsCapsMenuItem);\n return items;\n }\n }\n\n \/**\n * `kind`s of TextTrack to look for to associate it with this menu.\n *\n * @type {array}\n * @private\n *\/\n SubsCapsButton.prototype.kinds_ = ['captions', 'subtitles'];\n\n \/**\n * The text that should display over the `SubsCapsButton`s controls.\n *\n *\n * @type {string}\n * @protected\n *\/\n SubsCapsButton.prototype.controlText_ = 'Subtitles';\n Component$1.registerComponent('SubsCapsButton', SubsCapsButton);\n\n \/**\n * @file audio-track-menu-item.js\n *\/\n\n \/** @import Player from '..\/..\/player' *\/\n\n \/**\n * An {@link AudioTrack} {@link MenuItem}\n *\n * @extends MenuItem\n *\/\n class AudioTrackMenuItem extends MenuItem {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options]\n * The key\/value store of player options.\n *\/\n constructor(player, options) {\n const track = options.track;\n const tracks = player.audioTracks();\n\n \/\/ Modify options for parent MenuItem class's init.\n options.label = track.label || track.language || 'Unknown';\n options.selected = track.enabled;\n super(player, options);\n this.track = track;\n this.addClass(`vjs-${track.kind}-menu-item`);\n const changeHandler = (...args) => {\n this.handleTracksChange.apply(this, args);\n };\n tracks.addEventListener('change', changeHandler);\n this.on('dispose', () => {\n tracks.removeEventListener('change', changeHandler);\n });\n }\n createEl(type, props, attrs) {\n const el = super.createEl(type, props, attrs);\n const parentSpan = el.querySelector('.vjs-menu-item-text');\n if (['main-desc', 'descriptions'].indexOf(this.options_.track.kind) >= 0) {\n parentSpan.appendChild(createEl('span', {\n className: 'vjs-icon-placeholder'\n }, {\n 'aria-hidden': true\n }));\n parentSpan.appendChild(createEl('span', {\n className: 'vjs-control-text',\n textContent: ' ' + this.localize('Descriptions')\n }));\n }\n return el;\n }\n\n \/**\n * This gets called when an `AudioTrackMenuItem is \"clicked\". See {@link ClickableComponent}\n * for more detailed information on what a click can be.\n *\n * @param {Event} [event]\n * The `keydown`, `tap`, or `click` event that caused this function to be\n * called.\n *\n * @listens tap\n * @listens click\n *\/\n handleClick(event) {\n super.handleClick(event);\n\n \/\/ the audio track list will automatically toggle other tracks\n \/\/ off for us.\n this.track.enabled = true;\n\n \/\/ when native audio tracks are used, we want to make sure that other tracks are turned off\n if (this.player_.tech_.featuresNativeAudioTracks) {\n const tracks = this.player_.audioTracks();\n for (let i = 0; i < tracks.length; i++) {\n const track = tracks[i];\n\n \/\/ skip the current track since we enabled it above\n if (track === this.track) {\n continue;\n }\n track.enabled = track === this.track;\n }\n }\n }\n\n \/**\n * Handle any {@link AudioTrack} change.\n *\n * @param {Event} [event]\n * The {@link AudioTrackList#change} event that caused this to run.\n *\n * @listens AudioTrackList#change\n *\/\n handleTracksChange(event) {\n this.selected(this.track.enabled);\n }\n }\n Component$1.registerComponent('AudioTrackMenuItem', AudioTrackMenuItem);\n\n \/**\n * @file audio-track-button.js\n *\/\n\n \/**\n * The base class for buttons that toggle specific {@link AudioTrack} types.\n *\n * @extends TrackButton\n *\/\n class AudioTrackButton extends TrackButton {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options={}]\n * The key\/value store of player options.\n *\/\n constructor(player, options = {}) {\n options.tracks = player.audioTracks();\n super(player, options);\n this.setIcon('audio');\n }\n\n \/**\n * Builds the default DOM `className`.\n *\n * @return {string}\n * The DOM `className` for this object.\n *\/\n buildCSSClass() {\n return `vjs-audio-button ${super.buildCSSClass()}`;\n }\n buildWrapperCSSClass() {\n return `vjs-audio-button ${super.buildWrapperCSSClass()}`;\n }\n\n \/**\n * Create a menu item for each audio track\n *\n * @param {AudioTrackMenuItem[]} [items=[]]\n * An array of existing menu items to use.\n *\n * @return {AudioTrackMenuItem[]}\n * An array of menu items\n *\/\n createItems(items = []) {\n \/\/ if there's only one audio track, there no point in showing it\n this.hideThreshold_ = 1;\n const tracks = this.player_.audioTracks();\n for (let i = 0; i < tracks.length; i++) {\n const track = tracks[i];\n items.push(new AudioTrackMenuItem(this.player_, {\n track,\n \/\/ MenuItem is selectable\n selectable: true,\n \/\/ MenuItem is NOT multiSelectable (i.e. only one can be marked \"selected\" at a time)\n multiSelectable: false\n }));\n }\n return items;\n }\n }\n\n \/**\n * The text that should display over the `AudioTrackButton`s controls. Added for localization.\n *\n * @type {string}\n * @protected\n *\/\n AudioTrackButton.prototype.controlText_ = 'Audio Track';\n Component$1.registerComponent('AudioTrackButton', AudioTrackButton);\n\n \/**\n * @file playback-rate-menu-item.js\n *\/\n\n \/** @import Player from '..\/..\/player' *\/\n\n \/**\n * The specific menu item type for selecting a playback rate.\n *\n * @extends MenuItem\n *\/\n class PlaybackRateMenuItem extends MenuItem {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options]\n * The key\/value store of player options.\n *\/\n constructor(player, options) {\n const label = options.rate;\n const rate = parseFloat(label, 10);\n\n \/\/ Modify options for parent MenuItem class's init.\n options.label = label;\n options.selected = rate === player.playbackRate();\n options.selectable = true;\n options.multiSelectable = false;\n super(player, options);\n this.label = label;\n this.rate = rate;\n this.on(player, 'ratechange', e => this.update(e));\n }\n\n \/**\n * This gets called when an `PlaybackRateMenuItem` is \"clicked\". See\n * {@link ClickableComponent} for more detailed information on what a click can be.\n *\n * @param {Event} [event]\n * The `keydown`, `tap`, or `click` event that caused this function to be\n * called.\n *\n * @listens tap\n * @listens click\n *\/\n handleClick(event) {\n super.handleClick();\n this.player().playbackRate(this.rate);\n }\n\n \/**\n * Update the PlaybackRateMenuItem when the playbackrate changes.\n *\n * @param {Event} [event]\n * The `ratechange` event that caused this function to run.\n *\n * @listens Player#ratechange\n *\/\n update(event) {\n this.selected(this.player().playbackRate() === this.rate);\n }\n }\n\n \/**\n * The text that should display over the `PlaybackRateMenuItem`s controls. Added for localization.\n *\n * @type {string}\n * @private\n *\/\n PlaybackRateMenuItem.prototype.contentElType = 'button';\n Component$1.registerComponent('PlaybackRateMenuItem', PlaybackRateMenuItem);\n\n \/**\n * @file playback-rate-menu-button.js\n *\/\n\n \/** @import Player from '..\/..\/player' *\/\n\n \/**\n * The component for controlling the playback rate.\n *\n * @extends MenuButton\n *\/\n class PlaybackRateMenuButton extends MenuButton {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options]\n * The key\/value store of player options.\n *\/\n constructor(player, options) {\n super(player, options);\n this.menuButton_.el_.setAttribute('aria-describedby', this.labelElId_);\n this.updateVisibility();\n this.updateLabel();\n this.on(player, 'loadstart', e => this.updateVisibility(e));\n this.on(player, 'ratechange', e => this.updateLabel(e));\n this.on(player, 'playbackrateschange', e => this.handlePlaybackRateschange(e));\n }\n\n \/**\n * Create the `Component`'s DOM element\n *\n * @return {Element}\n * The element that was created.\n *\/\n createEl() {\n const el = super.createEl();\n this.labelElId_ = 'vjs-playback-rate-value-label-' + this.id_;\n this.labelEl_ = createEl('div', {\n className: 'vjs-playback-rate-value',\n id: this.labelElId_,\n textContent: '1x'\n });\n el.appendChild(this.labelEl_);\n return el;\n }\n dispose() {\n this.labelEl_ = null;\n super.dispose();\n }\n\n \/**\n * Builds the default DOM `className`.\n *\n * @return {string}\n * The DOM `className` for this object.\n *\/\n buildCSSClass() {\n return `vjs-playback-rate ${super.buildCSSClass()}`;\n }\n buildWrapperCSSClass() {\n return `vjs-playback-rate ${super.buildWrapperCSSClass()}`;\n }\n\n \/**\n * Create the list of menu items. Specific to each subclass.\n *\n *\/\n createItems() {\n const rates = this.playbackRates();\n const items = [];\n for (let i = rates.length - 1; i >= 0; i--) {\n items.push(new PlaybackRateMenuItem(this.player(), {\n rate: rates[i] + 'x'\n }));\n }\n return items;\n }\n\n \/**\n * On playbackrateschange, update the menu to account for the new items.\n *\n * @listens Player#playbackrateschange\n *\/\n handlePlaybackRateschange(event) {\n this.update();\n }\n\n \/**\n * Get possible playback rates\n *\n * @return {Array}\n * All possible playback rates\n *\/\n playbackRates() {\n const player = this.player();\n return player.playbackRates && player.playbackRates() || [];\n }\n\n \/**\n * Get whether playback rates is supported by the tech\n * and an array of playback rates exists\n *\n * @return {boolean}\n * Whether changing playback rate is supported\n *\/\n playbackRateSupported() {\n return this.player().tech_ && this.player().tech_.featuresPlaybackRate && this.playbackRates() && this.playbackRates().length > 0;\n }\n\n \/**\n * Hide playback rate controls when they're no playback rate options to select\n *\n * @param {Event} [event]\n * The event that caused this function to run.\n *\n * @listens Player#loadstart\n *\/\n updateVisibility(event) {\n if (this.playbackRateSupported()) {\n this.removeClass('vjs-hidden');\n } else {\n this.addClass('vjs-hidden');\n }\n }\n\n \/**\n * Update button label when rate changed\n *\n * @param {Event} [event]\n * The event that caused this function to run.\n *\n * @listens Player#ratechange\n *\/\n updateLabel(event) {\n if (this.playbackRateSupported()) {\n this.labelEl_.textContent = this.player().playbackRate() + 'x';\n }\n }\n }\n\n \/**\n * The text that should display over the `PlaybackRateMenuButton`s controls.\n *\n * Added for localization.\n *\n * @type {string}\n * @protected\n *\/\n PlaybackRateMenuButton.prototype.controlText_ = 'Playback Rate';\n Component$1.registerComponent('PlaybackRateMenuButton', PlaybackRateMenuButton);\n\n \/**\n * @file spacer.js\n *\/\n\n \/**\n * Just an empty spacer element that can be used as an append point for plugins, etc.\n * Also can be used to create space between elements when necessary.\n *\n * @extends Component\n *\/\n class Spacer extends Component$1 {\n \/**\n * Builds the default DOM `className`.\n *\n * @return {string}\n * The DOM `className` for this object.\n *\/\n buildCSSClass() {\n return `vjs-spacer ${super.buildCSSClass()}`;\n }\n\n \/**\n * Create the `Component`'s DOM element\n *\n * @return {Element}\n * The element that was created.\n *\/\n createEl(tag = 'div', props = {}, attributes = {}) {\n if (!props.className) {\n props.className = this.buildCSSClass();\n }\n return super.createEl(tag, props, attributes);\n }\n }\n Component$1.registerComponent('Spacer', Spacer);\n\n \/**\n * @file custom-control-spacer.js\n *\/\n\n \/**\n * Spacer specifically meant to be used as an insertion point for new plugins, etc.\n *\n * @extends Spacer\n *\/\n class CustomControlSpacer extends Spacer {\n \/**\n * Builds the default DOM `className`.\n *\n * @return {string}\n * The DOM `className` for this object.\n *\/\n buildCSSClass() {\n return `vjs-custom-control-spacer ${super.buildCSSClass()}`;\n }\n\n \/**\n * Create the `Component`'s DOM element\n *\n * @return {Element}\n * The element that was created.\n *\/\n createEl() {\n return super.createEl('div', {\n className: this.buildCSSClass(),\n \/\/ No-flex\/table-cell mode requires there be some content\n \/\/ in the cell to fill the remaining space of the table.\n textContent: '\\u00a0'\n });\n }\n }\n Component$1.registerComponent('CustomControlSpacer', CustomControlSpacer);\n\n \/**\n * @file control-bar.js\n *\/\n\n \/**\n * Container of main controls.\n *\n * @extends Component\n *\/\n class ControlBar extends Component$1 {\n \/**\n * Create the `Component`'s DOM element\n *\n * @return {Element}\n * The element that was created.\n *\/\n createEl() {\n return super.createEl('div', {\n className: 'vjs-control-bar',\n dir: 'ltr'\n });\n }\n }\n\n \/**\n * Default options for `ControlBar`\n *\n * @type {Object}\n * @private\n *\/\n ControlBar.prototype.options_ = {\n children: ['playToggle', 'skipBackward', 'skipForward', 'volumePanel', 'currentTimeDisplay', 'timeDivider', 'durationDisplay', 'progressControl', 'liveDisplay', 'seekToLive', 'remainingTimeDisplay', 'customControlSpacer', 'playbackRateMenuButton', 'chaptersButton', 'descriptionsButton', 'subsCapsButton', 'audioTrackButton', 'pictureInPictureToggle', 'fullscreenToggle']\n };\n Component$1.registerComponent('ControlBar', ControlBar);\n\n \/**\n * @file error-display.js\n *\/\n\n \/** @import Player from '.\/player' *\/\n\n \/**\n * A display that indicates an error has occurred. This means that the video\n * is unplayable.\n *\n * @extends ModalDialog\n *\/\n class ErrorDisplay extends ModalDialog {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options]\n * The key\/value store of player options.\n *\/\n constructor(player, options) {\n super(player, options);\n this.on(player, 'error', e => {\n this.open(e);\n });\n }\n\n \/**\n * Builds the default DOM `className`.\n *\n * @return {string}\n * The DOM `className` for this object.\n *\n * @deprecated Since version 5.\n *\/\n buildCSSClass() {\n return `vjs-error-display ${super.buildCSSClass()}`;\n }\n\n \/**\n * Gets the localized error message based on the `Player`s error.\n *\n * @return {string}\n * The `Player`s error message localized or an empty string.\n *\/\n content() {\n const error = this.player().error();\n return error ? this.localize(error.message) : '';\n }\n }\n\n \/**\n * The default options for an `ErrorDisplay`.\n *\n * @private\n *\/\n ErrorDisplay.prototype.options_ = Object.assign({}, ModalDialog.prototype.options_, {\n pauseOnOpen: false,\n fillAlways: true,\n temporary: false,\n uncloseable: true\n });\n Component$1.registerComponent('ErrorDisplay', ErrorDisplay);\n\n \/** @import Player from '.\/player' *\/\n \/** @import { ContentDescriptor } from '..\/utils\/dom' *\/\n\n \/**\n * Creates DOM element of 'select' & its options.\n *\n * @extends Component\n *\/\n class TextTrackSelect extends Component$1 {\n \/**\n * Creates an instance of this class.\n *\n * @param {Player} player\n * The `Player` that this class should be attached to.\n *\n * @param {Object} [options]\n * The key\/value store of player options.\n *\n * @param {ContentDescriptor} [options.content=undefined]\n * Provide customized content for this modal.\n *\n * @param {string} [options.legendId]\n * A text with part of an string to create atribute of aria-labelledby.\n *\n * @param {string} [options.id]\n * A text with part of an string to create atribute of aria-labelledby.\n *\n * @param {Array} [options.SelectOptions]\n * Array that contains the value & textContent of for each of the\n * options elements.\n *\/\n constructor(player, options = {}) {\n super(player, options);\n this.el_.setAttribute('aria-labelledby', this.selectLabelledbyIds);\n }\n\n \/**\n * Create the `TextTrackSelect`'s DOM element\n *\n * @return {Element}\n * The DOM element that gets created.\n *\/\n createEl() {\n this.selectLabelledbyIds = [this.options_.legendId, this.options_.labelId].join(' ').trim();\n\n \/\/ Create select & inner options\n const selectoptions = createEl('select', {\n id: this.options_.id\n }, {}, this.options_.SelectOptions.map(optionText => {\n \/\/ Constructs an id for the