(window["webpackjsonp"] = window["webpackjsonp"] || []).push([[68],{ /***/ 265: /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; /* webpack var injection */(function(global) {/* harmony import */ var global_window__webpack_imported_module_0__ = __webpack_require__(0); /* harmony import */ var global_window__webpack_imported_module_0___default = /*#__pure__*/__webpack_require__.n(global_window__webpack_imported_module_0__); /* harmony import */ var global_document__webpack_imported_module_1__ = __webpack_require__(3); /* harmony import */ var global_document__webpack_imported_module_1___default = /*#__pure__*/__webpack_require__.n(global_document__webpack_imported_module_1__); /* harmony import */ var _babel_runtime_helpers_extends__webpack_imported_module_2__ = __webpack_require__(35); /* harmony import */ var _babel_runtime_helpers_extends__webpack_imported_module_2___default = /*#__pure__*/__webpack_require__.n(_babel_runtime_helpers_extends__webpack_imported_module_2__); /* harmony import */ var keycode__webpack_imported_module_3__ = __webpack_require__(7); /* harmony import */ var keycode__webpack_imported_module_3___default = /*#__pure__*/__webpack_require__.n(keycode__webpack_imported_module_3__); /* harmony import */ var _babel_runtime_helpers_assertthisinitialized__webpack_imported_module_4__ = __webpack_require__(6); /* harmony import */ var _babel_runtime_helpers_assertthisinitialized__webpack_imported_module_4___default = /*#__pure__*/__webpack_require__.n(_babel_runtime_helpers_assertthisinitialized__webpack_imported_module_4__); /* harmony import */ var _babel_runtime_helpers_inheritsloose__webpack_imported_module_5__ = __webpack_require__(4); /* harmony import */ var _babel_runtime_helpers_inheritsloose__webpack_imported_module_5___default = /*#__pure__*/__webpack_require__.n(_babel_runtime_helpers_inheritsloose__webpack_imported_module_5__); /* harmony import */ var safe_json_parse_tuple__webpack_imported_module_6__ = __webpack_require__(266); /* harmony import */ var safe_json_parse_tuple__webpack_imported_module_6___default = /*#__pure__*/__webpack_require__.n(safe_json_parse_tuple__webpack_imported_module_6__); /* harmony import */ var _videojs_xhr__webpack_imported_module_7__ = __webpack_require__(169); /* harmony import */ var _videojs_xhr__webpack_imported_module_7___default = /*#__pure__*/__webpack_require__.n(_videojs_xhr__webpack_imported_module_7__); /* harmony import */ var videojs_vtt_js__webpack_imported_module_8__ = __webpack_require__(170); /* harmony import */ var videojs_vtt_js__webpack_imported_module_8___default = /*#__pure__*/__webpack_require__.n(videojs_vtt_js__webpack_imported_module_8__); /* harmony import */ var _babel_runtime_helpers_construct__webpack_imported_module_9__ = __webpack_require__(160); /* harmony import */ var _babel_runtime_helpers_construct__webpack_imported_module_9___default = /*#__pure__*/__webpack_require__.n(_babel_runtime_helpers_construct__webpack_imported_module_9__); /* harmony import */ var _babel_runtime_helpers_inherits__webpack_imported_module_10__ = __webpack_require__(267); /* harmony import */ var _babel_runtime_helpers_inherits__webpack_imported_module_10___default = /*#__pure__*/__webpack_require__.n(_babel_runtime_helpers_inherits__webpack_imported_module_10__); /* harmony import */ var _videojs_vhs_utils_es_resolve_url_js__webpack_imported_module_11__ = __webpack_require__(81); /* harmony import */ var m3u8_parser__webpack_imported_module_12__ = __webpack_require__(268); /* harmony import */ var _videojs_vhs_utils_es_codecs_js__webpack_imported_module_13__ = __webpack_require__(14); /* harmony import */ var _videojs_vhs_utils_es_media_types_js__webpack_imported_module_14__ = __webpack_require__(172); /* harmony import */ var _videojs_vhs_utils_es_byte_helpers__webpack_imported_module_15__ = __webpack_require__(1); /* harmony import */ var mpd_parser__webpack_imported_module_16__ = __webpack_require__(53); /* harmony import */ var mux_js_lib_tools_parse_sidx__webpack_imported_module_17__ = __webpack_require__(271); /* harmony import */ var mux_js_lib_tools_parse_sidx__webpack_imported_module_17___default = /*#__pure__*/__webpack_require__.n(mux_js_lib_tools_parse_sidx__webpack_imported_module_17__); /* harmony import */ var _videojs_vhs_utils_es_id3_helpers__webpack_imported_module_18__ = __webpack_require__(59); /* harmony import */ var _videojs_vhs_utils_es_containers__webpack_imported_module_19__ = __webpack_require__(101); /* harmony import */ var mux_js_lib_utils_clock__webpack_imported_module_20__ = __webpack_require__(100); /* harmony import */ var mux_js_lib_utils_clock__webpack_imported_module_20___default = /*#__pure__*/__webpack_require__.n(mux_js_lib_utils_clock__webpack_imported_module_20__); /* harmony import */ var _babel_runtime_helpers_wrapnativesuper__webpack_imported_module_21__ = __webpack_require__(272); /* harmony import */ var _babel_runtime_helpers_wrapnativesuper__webpack_imported_module_21___default = /*#__pure__*/__webpack_require__.n(_babel_runtime_helpers_wrapnativesuper__webpack_imported_module_21__); /** * @license * video.js 7.21.5 * copyright brightcove, inc. * available under apache license version 2.0 * * * includes vtt.js * available under apache license version 2.0 * */ var version$5 = "7.21.5"; /** * an object that contains lifecycle hooks as keys which point to an array * of functions that are run when a lifecycle is triggered * * @private */ var hooks_ = {}; /** * get a list of hooks for a specific lifecycle * * @param {string} type * the lifecyle to get hooks from * * @param {function|function[]} [fn] * optionally add a hook (or hooks) to the lifecycle that your are getting. * * @return {array} * an array of hooks, or an empty array if there are none. */ var hooks = function hooks(type, fn) { hooks_[type] = hooks_[type] || []; if (fn) { hooks_[type] = hooks_[type].concat(fn); } return hooks_[type]; }; /** * add a function hook to a specific videojs lifecycle. * * @param {string} type * the lifecycle to hook the function to. * * @param {function|function[]} * the function or array of functions to attach. */ var hook = function hook(type, fn) { hooks(type, fn); }; /** * remove a hook from a specific videojs lifecycle. * * @param {string} type * the lifecycle that the function hooked to * * @param {function} fn * the hooked function to remove * * @return {boolean} * the function that was removed or undef */ var removehook = function removehook(type, fn) { var index = hooks(type).indexof(fn); if (index <= -1) { return false; } hooks_[type] = hooks_[type].slice(); hooks_[type].splice(index, 1); return true; }; /** * add a function hook that will only run once to a specific videojs lifecycle. * * @param {string} type * the lifecycle to hook the function to. * * @param {function|function[]} * the function or array of functions to attach. */ var hookonce = function hookonce(type, fn) { hooks(type, [].concat(fn).map(function (original) { var wrapper = function wrapper() { removehook(type, wrapper); return original.apply(void 0, arguments); }; return wrapper; })); }; /** * @file fullscreen-api.js * @module fullscreen-api * @private */ /** * store the browser-specific methods for the fullscreen api. * * @type {object} * @see [specification]{@link https://fullscreen.spec.whatwg.org} * @see [map approach from screenfull.js]{@link https://github.com/sindresorhus/screenfull.js} */ var fullscreenapi = { prefixed: true }; // browser api methods var apimap = [['requestfullscreen', 'exitfullscreen', 'fullscreenelement', 'fullscreenenabled', 'fullscreenchange', 'fullscreenerror', 'fullscreen'], // webkit ['webkitrequestfullscreen', 'webkitexitfullscreen', 'webkitfullscreenelement', 'webkitfullscreenenabled', 'webkitfullscreenchange', 'webkitfullscreenerror', '-webkit-full-screen'], // mozilla ['mozrequestfullscreen', 'mozcancelfullscreen', 'mozfullscreenelement', 'mozfullscreenenabled', 'mozfullscreenchange', 'mozfullscreenerror', '-moz-full-screen'], // microsoft ['msrequestfullscreen', 'msexitfullscreen', 'msfullscreenelement', 'msfullscreenenabled', 'msfullscreenchange', 'msfullscreenerror', '-ms-fullscreen']]; var specapi = apimap[0]; var browserapi; // determine the supported set of functions for (var i = 0; i < apimap.length; i++) { // check for exitfullscreen function if (apimap[i][1] in global_document__webpack_imported_module_1___default.a) { browserapi = apimap[i]; break; } } // map the browser api names to the spec api names if (browserapi) { for (var _i = 0; _i < browserapi.length; _i++) { fullscreenapi[specapi[_i]] = browserapi[_i]; } fullscreenapi.prefixed = browserapi[0] !== specapi[0]; } /** * @file create-logger.js * @module create-logger */ var history = []; /** * log messages to the console and history based on the type of message * * @private * @param {string} type * the name of the console method to use. * * @param {array} args * the arguments to be passed to the matching console method. */ var logbytypefactory = function logbytypefactory(name, log) { return function (type, level, args) { var lvl = log.levels[level]; var lvlregexp = new regexp("^(" + lvl + ")$"); if (type !== 'log') { // add the type to the front of the message when it's not "log". args.unshift(type.touppercase() + ':'); } // add console prefix after adding to history. args.unshift(name + ':'); // add a clone of the args at this point to history. if (history) { history.push([].concat(args)); // only store 1000 history entries var splice = history.length - 1000; history.splice(0, splice > 0 ? splice : 0); } // if there's no console then don't try to output messages, but they will // still be stored in history. if (!global_window__webpack_imported_module_0___default.a.console) { return; } // was setting these once outside of this function, but containing them // in the function makes it easier to test cases where console doesn't exist // when the module is executed. var fn = global_window__webpack_imported_module_0___default.a.console[type]; if (!fn && type === 'debug') { // certain browsers don't have support for console.debug. for those, we // should default to the closest comparable log. fn = global_window__webpack_imported_module_0___default.a.console.info || global_window__webpack_imported_module_0___default.a.console.log; } // bail out if there's no console or if this type is not allowed by the // current logging level. if (!fn || !lvl || !lvlregexp.test(type)) { return; } fn[array.isarray(args) ? 'apply' : 'call'](global_window__webpack_imported_module_0___default.a.console, args); }; }; function createlogger$1(name) { // this is the private tracking variable for logging level. var level = 'info'; // the curried logbytype bound to the specific log and history var logbytype; /** * logs plain debug messages. similar to `console.log`. * * due to [limitations](https://github.com/jsdoc3/jsdoc/issues/955#issuecomment-313829149) * of our jsdoc template, we cannot properly document this as both a function * and a namespace, so its function signature is documented here. * * #### arguments * ##### *args * mixed[] * * any combination of values that could be passed to `console.log()`. * * #### return value * * `undefined` * * @namespace * @param {mixed[]} args * one or more messages or objects that should be logged. */ var log = function log() { for (var _len = arguments.length, args = new array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } logbytype('log', level, args); }; // this is the logbytype helper that the logging methods below use logbytype = logbytypefactory(name, log); /** * create a new sublogger which chains the old name to the new name. * * for example, doing `videojs.log.createlogger('player')` and then using that logger will log the following: * ```js * mylogger('foo'); * // > videojs: player: foo * ``` * * @param {string} name * the name to add call the new logger * @return {object} */ log.createlogger = function (subname) { return createlogger$1(name + ': ' + subname); }; /** * enumeration of available logging levels, where the keys are the level names * and the values are `|`-separated strings containing logging methods allowed * in that logging level. these strings are used to create a regular expression * matching the function name being called. * * levels provided by video.js are: * * - `off`: matches no calls. any value that can be cast to `false` will have * this effect. the most restrictive. * - `all`: matches only video.js-provided functions (`debug`, `log`, * `log.warn`, and `log.error`). * - `debug`: matches `log.debug`, `log`, `log.warn`, and `log.error` calls. * - `info` (default): matches `log`, `log.warn`, and `log.error` calls. * - `warn`: matches `log.warn` and `log.error` calls. * - `error`: matches only `log.error` calls. * * @type {object} */ log.levels = { all: 'debug|log|warn|error', off: '', debug: 'debug|log|warn|error', info: 'log|warn|error', warn: 'warn|error', error: 'error', default: level }; /** * get or set the current logging level. * * if a string matching a key from {@link module:log.levels} is provided, acts * as a setter. * * @param {string} [lvl] * pass a valid level to set a new logging level. * * @return {string} * the current logging level. */ log.level = function (lvl) { if (typeof lvl === 'string') { if (!log.levels.hasownproperty(lvl)) { throw new error("\"" + lvl + "\" in not a valid log level"); } level = lvl; } return level; }; /** * returns an array containing everything that has been logged to the history. * * this array is a shallow clone of the internal history record. however, its * contents are _not_ cloned; so, mutating objects inside this array will * mutate them in history. * * @return {array} */ log.history = function () { return history ? [].concat(history) : []; }; /** * allows you to filter the history by the given logger name * * @param {string} fname * the name to filter by * * @return {array} * the filtered list to return */ log.history.filter = function (fname) { return (history || []).filter(function (historyitem) { // if the first item in each historyitem includes `fname`, then it's a match return new regexp(".*" + fname + ".*").test(historyitem[0]); }); }; /** * clears the internal history tracking, but does not prevent further history * tracking. */ log.history.clear = function () { if (history) { history.length = 0; } }; /** * disable history tracking if it is currently enabled. */ log.history.disable = function () { if (history !== null) { history.length = 0; history = null; } }; /** * enable history tracking if it is currently disabled. */ log.history.enable = function () { if (history === null) { history = []; } }; /** * logs error messages. similar to `console.error`. * * @param {mixed[]} args * one or more messages or objects that should be logged as an error */ log.error = function () { for (var _len2 = arguments.length, args = new array(_len2), _key2 = 0; _key2 < _len2; _key2++) { args[_key2] = arguments[_key2]; } return logbytype('error', level, args); }; /** * logs warning messages. similar to `console.warn`. * * @param {mixed[]} args * one or more messages or objects that should be logged as a warning. */ log.warn = function () { for (var _len3 = arguments.length, args = new array(_len3), _key3 = 0; _key3 < _len3; _key3++) { args[_key3] = arguments[_key3]; } return logbytype('warn', level, args); }; /** * logs debug messages. similar to `console.debug`, but may also act as a comparable * log if `console.debug` is not available * * @param {mixed[]} args * one or more messages or objects that should be logged as debug. */ log.debug = function () { for (var _len4 = arguments.length, args = new array(_len4), _key4 = 0; _key4 < _len4; _key4++) { args[_key4] = arguments[_key4]; } return logbytype('debug', level, args); }; return log; } /** * @file log.js * @module log */ var log$1 = createlogger$1('videojs'); var createlogger = log$1.createlogger; /** * @file obj.js * @module obj */ /** * @callback obj:eachcallback * * @param {mixed} value * the current key for the object that is being iterated over. * * @param {string} key * the current key-value for object that is being iterated over */ /** * @callback obj:reducecallback * * @param {mixed} accum * the value that is accumulating over the reduce loop. * * @param {mixed} value * the current key for the object that is being iterated over. * * @param {string} key * the current key-value for object that is being iterated over * * @return {mixed} * the new accumulated value. */ var tostring = object.prototype.tostring; /** * get the keys of an object * * @param {object} * the object to get the keys from * * @return {string[]} * an array of the keys from the object. returns an empty array if the * object passed in was invalid or had no keys. * * @private */ var keys = function keys(object) { return isobject(object) ? object.keys(object) : []; }; /** * array-like iteration for objects. * * @param {object} object * the object to iterate over * * @param {obj:eachcallback} fn * the callback function which is called for each key in the object. */ function each(object, fn) { keys(object).foreach(function (key) { return fn(object[key], key); }); } /** * array-like reduce for objects. * * @param {object} object * the object that you want to reduce. * * @param {function} fn * a callback function which is called for each key in the object. it * receives the accumulated value and the per-iteration value and key * as arguments. * * @param {mixed} [initial = 0] * starting value * * @return {mixed} * the final accumulated value. */ function reduce(object, fn, initial) { if (initial === void 0) { initial = 0; } return keys(object).reduce(function (accum, key) { return fn(accum, object[key], key); }, initial); } /** * object.assign-style object shallow merge/extend. * * @param {object} target * @param {object} ...sources * @return {object} */ function assign(target) { for (var _len = arguments.length, sources = new array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { sources[_key - 1] = arguments[_key]; } if (object.assign) { return _babel_runtime_helpers_extends__webpack_imported_module_2___default.a.apply(void 0, [target].concat(sources)); } sources.foreach(function (source) { if (!source) { return; } each(source, function (value, key) { target[key] = value; }); }); return target; } /** * returns whether a value is an object of any kind - including dom nodes, * arrays, regular expressions, etc. not functions, though. * * this avoids the gotcha where using `typeof` on a `null` value * results in `'object'`. * * @param {object} value * @return {boolean} */ function isobject(value) { return !!value && typeof value === 'object'; } /** * returns whether an object appears to be a "plain" object - that is, a * direct instance of `object`. * * @param {object} value * @return {boolean} */ function isplain(value) { return isobject(value) && tostring.call(value) === '[object object]' && value.constructor === object; } /** * @file computed-style.js * @module computed-style */ /** * a safe getcomputedstyle. * * this is needed because in firefox, if the player is loaded in an iframe with * `display:none`, then `getcomputedstyle` returns `null`, so, we do a * null-check to make sure that the player doesn't break in these cases. * * @function * @param {element} el * the element you want the computed style of * * @param {string} prop * the property name you want * * @see https://bugzilla.mozilla.org/show_bug.cgi?id=548397 */ function computedstyle(el, prop) { if (!el || !prop) { return ''; } if (typeof global_window__webpack_imported_module_0___default.a.getcomputedstyle === 'function') { var computedstylevalue; try { computedstylevalue = global_window__webpack_imported_module_0___default.a.getcomputedstyle(el); } catch (e) { return ''; } return computedstylevalue ? computedstylevalue.getpropertyvalue(prop) || computedstylevalue[prop] : ''; } return ''; } /** * @file browser.js * @module browser */ var user_agent = global_window__webpack_imported_module_0___default.a.navigator && global_window__webpack_imported_module_0___default.a.navigator.useragent || ''; var webkitversionmap = /applewebkit\/([\d.]+)/i.exec(user_agent); var applewebkitversion = webkitversionmap ? parsefloat(webkitversionmap.pop()) : null; /** * whether or not this device is an ipod. * * @static * @const * @type {boolean} */ var is_ipod = /ipod/i.test(user_agent); /** * the detected ios version - or `null`. * * @static * @const * @type {string|null} */ var ios_version = function () { var match = user_agent.match(/os (\d+)_/i); if (match && match[1]) { return match[1]; } return null; }(); /** * whether or not this is an android device. * * @static * @const * @type {boolean} */ var is_android = /android/i.test(user_agent); /** * the detected android version - or `null`. * * @static * @const * @type {number|string|null} */ var android_version = function () { // this matches android major.minor.patch versions // android_version is major.minor as a number, if minor isn't available, then only major is returned var match = user_agent.match(/android (\d+)(?:\.(\d+))?(?:\.(\d+))*/i); if (!match) { return null; } var major = match[1] && parsefloat(match[1]); var minor = match[2] && parsefloat(match[2]); if (major && minor) { return parsefloat(match[1] + '.' + match[2]); } else if (major) { return major; } return null; }(); /** * whether or not this is a native android browser. * * @static * @const * @type {boolean} */ var is_native_android = is_android && android_version < 5 && applewebkitversion < 537; /** * whether or not this is mozilla firefox. * * @static * @const * @type {boolean} */ var is_firefox = /firefox/i.test(user_agent); /** * whether or not this is microsoft edge. * * @static * @const * @type {boolean} */ var is_edge = /edg/i.test(user_agent); /** * whether or not this is google chrome. * * this will also be `true` for chrome on ios, which will have different support * as it is actually safari under the hood. * * @static * @const * @type {boolean} */ var is_chrome = !is_edge && (/chrome/i.test(user_agent) || /crios/i.test(user_agent)); /** * the detected google chrome version - or `null`. * * @static * @const * @type {number|null} */ var chrome_version = function () { var match = user_agent.match(/(chrome|crios)\/(\d+)/); if (match && match[2]) { return parsefloat(match[2]); } return null; }(); /** * the detected internet explorer version - or `null`. * * @static * @const * @type {number|null} */ var ie_version = function () { var result = /msie\s(\d+)\.\d/.exec(user_agent); var version = result && parsefloat(result[1]); if (!version && /trident\/7.0/i.test(user_agent) && /rv:11.0/.test(user_agent)) { // ie 11 has a different user agent string than other ie versions version = 11.0; } return version; }(); /** * whether or not this is desktop safari. * * @static * @const * @type {boolean} */ var is_safari = /safari/i.test(user_agent) && !is_chrome && !is_android && !is_edge; /** * whether or not this is a windows machine. * * @static * @const * @type {boolean} */ var is_windows = /windows/i.test(user_agent); /** * whether or not this device is touch-enabled. * * @static * @const * @type {boolean} */ var touch_enabled = boolean(isreal() && ('ontouchstart' in global_window__webpack_imported_module_0___default.a || global_window__webpack_imported_module_0___default.a.navigator.maxtouchpoints || global_window__webpack_imported_module_0___default.a.documenttouch && global_window__webpack_imported_module_0___default.a.document instanceof global_window__webpack_imported_module_0___default.a.documenttouch)); /** * whether or not this device is an ipad. * * @static * @const * @type {boolean} */ var is_ipad = /ipad/i.test(user_agent) || is_safari && touch_enabled && !/iphone/i.test(user_agent); /** * whether or not this device is an iphone. * * @static * @const * @type {boolean} */ // the facebook app's uiwebview identifies as both an iphone and ipad, so // to identify iphones, we need to exclude ipads. // http://artsy.github.io/blog/2012/10/18/the-perils-of-ios-user-agent-sniffing/ var is_iphone = /iphone/i.test(user_agent) && !is_ipad; /** * whether or not this is an ios device. * * @static * @const * @type {boolean} */ var is_ios = is_iphone || is_ipad || is_ipod; /** * whether or not this is any flavor of safari - including ios. * * @static * @const * @type {boolean} */ var is_any_safari = (is_safari || is_ios) && !is_chrome; var browser = /*#__pure__*/object.freeze({ __proto__: null, is_ipod: is_ipod, ios_version: ios_version, is_android: is_android, android_version: android_version, is_native_android: is_native_android, is_firefox: is_firefox, is_edge: is_edge, is_chrome: is_chrome, chrome_version: chrome_version, ie_version: ie_version, is_safari: is_safari, is_windows: is_windows, touch_enabled: touch_enabled, is_ipad: is_ipad, is_iphone: is_iphone, is_ios: is_ios, is_any_safari: is_any_safari }); /** * @file dom.js * @module dom */ /** * detect if a value is a string with any non-whitespace characters. * * @private * @param {string} str * the string to check * * @return {boolean} * will be `true` if the string is non-blank, `false` otherwise. * */ function isnonblankstring(str) { // we use str.trim as it will trim any whitespace characters // from the front or back of non-whitespace characters. aka // any string that contains non-whitespace characters will // still contain them after `trim` but whitespace only strings // will have a length of 0, failing this check. return typeof str === 'string' && boolean(str.trim()); } /** * throws an error if the passed string has whitespace. this is used by * class methods to be relatively consistent with the classlist api. * * @private * @param {string} str * the string to check for whitespace. * * @throws {error} * throws an error if there is whitespace in the string. */ function throwifwhitespace(str) { // str.indexof instead of regex because str.indexof is faster performance wise. if (str.indexof(' ') >= 0) { throw new error('class has illegal whitespace characters'); } } /** * produce a regular expression for matching a classname within an elements classname. * * @private * @param {string} classname * the classname to generate the regexp for. * * @return {regexp} * the regexp that will check for a specific `classname` in an elements * classname. */ function classregexp(classname) { return new regexp('(^|\\s)' + classname + '($|\\s)'); } /** * whether the current dom interface appears to be real (i.e. not simulated). * * @return {boolean} * will be `true` if the dom appears to be real, `false` otherwise. */ function isreal() { // both document and window will never be undefined thanks to `global`. return global_document__webpack_imported_module_1___default.a === global_window__webpack_imported_module_0___default.a.document; } /** * determines, via duck typing, whether or not a value is a dom element. * * @param {mixed} value * the value to check. * * @return {boolean} * will be `true` if the value is a dom element, `false` otherwise. */ function isel(value) { return isobject(value) && value.nodetype === 1; } /** * determines if the current dom is embedded in an iframe. * * @return {boolean} * will be `true` if the dom is embedded in an iframe, `false` * otherwise. */ function isinframe() { // we need a try/catch here because safari will throw errors when attempting // to get either `parent` or `self` try { return global_window__webpack_imported_module_0___default.a.parent !== global_window__webpack_imported_module_0___default.a.self; } catch (x) { return true; } } /** * creates functions to query the dom using a given method. * * @private * @param {string} method * the method to create the query with. * * @return {function} * the query method */ function createquerier(method) { return function (selector, context) { if (!isnonblankstring(selector)) { return global_document__webpack_imported_module_1___default.a[method](null); } if (isnonblankstring(context)) { context = global_document__webpack_imported_module_1___default.a.queryselector(context); } var ctx = isel(context) ? context : global_document__webpack_imported_module_1___default.a; return ctx[method] && ctx[method](selector); }; } /** * creates an element and applies properties, attributes, and inserts content. * * @param {string} [tagname='div'] * name of tag to be created. * * @param {object} [properties={}] * element properties to be applied. * * @param {object} [attributes={}] * element attributes to be applied. * * @param {module:dom~contentdescriptor} content * a content descriptor object. * * @return {element} * the element that was created. */ function createel(tagname, properties, attributes, content) { if (tagname === void 0) { tagname = 'div'; } if (properties === void 0) { properties = {}; } if (attributes === void 0) { attributes = {}; } var el = global_document__webpack_imported_module_1___default.a.createelement(tagname); object.getownpropertynames(properties).foreach(function (propname) { var val = properties[propname]; // see #2176 // we originally were accepting both properties and attributes in the // same object, but that doesn't work so well. if (propname.indexof('aria-') !== -1 || propname === 'role' || propname === 'type') { log$1.warn('setting attributes in the second argument of createel()\n' + 'has been deprecated. use the third argument instead.\n' + ("createel(type, properties, attributes). attempting to set " + propname + " to " + val + ".")); el.setattribute(propname, val); // handle textcontent since it's not supported everywhere and we have a // method for it. } else if (propname === 'textcontent') { textcontent(el, val); } else if (el[propname] !== val || propname === 'tabindex') { el[propname] = val; } }); object.getownpropertynames(attributes).foreach(function (attrname) { el.setattribute(attrname, attributes[attrname]); }); if (content) { appendcontent(el, content); } return el; } /** * injects text into an element, replacing any existing contents entirely. * * @param {element} el * the element to add text content into * * @param {string} text * the text content to add. * * @return {element} * the element with added text content. */ function textcontent(el, text) { if (typeof el.textcontent === 'undefined') { el.innertext = text; } else { el.textcontent = text; } return el; } /** * insert an element as the first child node of another * * @param {element} child * element to insert * * @param {element} parent * element to insert child into */ function prependto(child, parent) { if (parent.firstchild) { parent.insertbefore(child, parent.firstchild); } else { parent.appendchild(child); } } /** * check if an element has a class name. * * @param {element} element * element to check * * @param {string} classtocheck * class name to check for * * @return {boolean} * will be `true` if the element has a class, `false` otherwise. * * @throws {error} * throws an error if `classtocheck` has white space. */ function hasclass(element, classtocheck) { throwifwhitespace(classtocheck); if (element.classlist) { return element.classlist.contains(classtocheck); } return classregexp(classtocheck).test(element.classname); } /** * add a class name to an element. * * @param {element} element * element to add class name to. * * @param {string} classtoadd * class name to add. * * @return {element} * the dom element with the added class name. */ function addclass(element, classtoadd) { if (element.classlist) { element.classlist.add(classtoadd); // don't need to `throwifwhitespace` here because `haselclass` will do it // in the case of classlist not being supported. } else if (!hasclass(element, classtoadd)) { element.classname = (element.classname + ' ' + classtoadd).trim(); } return element; } /** * remove a class name from an element. * * @param {element} element * element to remove a class name from. * * @param {string} classtoremove * class name to remove * * @return {element} * the dom element with class name removed. */ function removeclass(element, classtoremove) { // protect in case the player gets disposed if (!element) { log$1.warn("removeclass was called with an element that doesn't exist"); return null; } if (element.classlist) { element.classlist.remove(classtoremove); } else { throwifwhitespace(classtoremove); element.classname = element.classname.split(/\s+/).filter(function (c) { return c !== classtoremove; }).join(' '); } return element; } /** * the callback definition for toggleclass. * * @callback module:dom~predicatecallback * @param {element} element * the dom element of the component. * * @param {string} classtotoggle * the `classname` that wants to be toggled * * @return {boolean|undefined} * if `true` is returned, the `classtotoggle` will be added to the * `element`. if `false`, the `classtotoggle` will be removed from * the `element`. if `undefined`, the callback will be ignored. */ /** * adds or removes a class name to/from an element depending on an optional * condition or the presence/absence of the class name. * * @param {element} element * the element to toggle a class name on. * * @param {string} classtotoggle * the class that should be toggled. * * @param {boolean|module:dom~predicatecallback} [predicate] * see the return value for {@link module:dom~predicatecallback} * * @return {element} * the element with a class that has been toggled. */ function toggleclass(element, classtotoggle, predicate) { // this cannot use `classlist` internally because ie11 does not support the // second parameter to the `classlist.toggle()` method! which is fine because // `classlist` will be used by the add/remove functions. var has = hasclass(element, classtotoggle); if (typeof predicate === 'function') { predicate = predicate(element, classtotoggle); } if (typeof predicate !== 'boolean') { predicate = !has; } // if the necessary class operation matches the current state of the // element, no action is required. if (predicate === has) { return; } if (predicate) { addclass(element, classtotoggle); } else { removeclass(element, classtotoggle); } return element; } /** * apply attributes to an html element. * * @param {element} el * element to add attributes to. * * @param {object} [attributes] * attributes to be applied. */ function setattributes(el, attributes) { object.getownpropertynames(attributes).foreach(function (attrname) { var attrvalue = attributes[attrname]; if (attrvalue === null || typeof attrvalue === 'undefined' || attrvalue === false) { el.removeattribute(attrname); } else { el.setattribute(attrname, attrvalue === true ? '' : attrvalue); } }); } /** * get an element's attribute values, as defined on the html tag. * * attributes are not the same as properties. they're defined on the tag * or with setattribute. * * @param {element} tag * element from which to get tag attributes. * * @return {object} * all attributes of the element. boolean attributes will be `true` or * `false`, others will be strings. */ function getattributes(tag) { var obj = {}; // known boolean attributes // we can check for matching boolean properties, but not all browsers // and not all tags know about these attributes, so, we still want to check them manually var knownbooleans = ',' + 'autoplay,controls,playsinline,loop,muted,default,defaultmuted' + ','; if (tag && tag.attributes && tag.attributes.length > 0) { var attrs = tag.attributes; for (var i = attrs.length - 1; i >= 0; i--) { var attrname = attrs[i].name; var attrval = attrs[i].value; // check for known booleans // the matching element property will return a value for typeof if (typeof tag[attrname] === 'boolean' || knownbooleans.indexof(',' + attrname + ',') !== -1) { // the value of an included boolean attribute is typically an empty // string ('') which would equal false if we just check for a false value. // we also don't want support bad code like autoplay='false' attrval = attrval !== null ? true : false; } obj[attrname] = attrval; } } return obj; } /** * get the value of an element's attribute. * * @param {element} el * a dom element. * * @param {string} attribute * attribute to get the value of. * * @return {string} * the value of the attribute. */ function getattribute(el, attribute) { return el.getattribute(attribute); } /** * set the value of an element's attribute. * * @param {element} el * a dom element. * * @param {string} attribute * attribute to set. * * @param {string} value * value to set the attribute to. */ function setattribute(el, attribute, value) { el.setattribute(attribute, value); } /** * remove an element's attribute. * * @param {element} el * a dom element. * * @param {string} attribute * attribute to remove. */ function removeattribute(el, attribute) { el.removeattribute(attribute); } /** * attempt to block the ability to select text. */ function blocktextselection() { global_document__webpack_imported_module_1___default.a.body.focus(); global_document__webpack_imported_module_1___default.a.onselectstart = function () { return false; }; } /** * turn off text selection blocking. */ function unblocktextselection() { global_document__webpack_imported_module_1___default.a.onselectstart = function () { return true; }; } /** * identical to the native `getboundingclientrect` function, but ensures that * the method is supported at all (it is in all browsers we claim to support) * and that the element is in the dom before continuing. * * this wrapper function also shims properties which are not provided by some * older browsers (namely, ie8). * * additionally, some browsers do not support adding properties to a * `clientrect`/`domrect` object; so, we shallow-copy it with the standard * properties (except `x` and `y` which are not widely supported). this helps * avoid implementations where keys are non-enumerable. * * @param {element} el * element whose `clientrect` we want to calculate. * * @return {object|undefined} * always returns a plain object - or `undefined` if it cannot. */ function getboundingclientrect(el) { if (el && el.getboundingclientrect && el.parentnode) { var rect = el.getboundingclientrect(); var result = {}; ['bottom', 'height', 'left', 'right', 'top', 'width'].foreach(function (k) { if (rect[k] !== undefined) { result[k] = rect[k]; } }); if (!result.height) { result.height = parsefloat(computedstyle(el, 'height')); } if (!result.width) { result.width = parsefloat(computedstyle(el, 'width')); } return result; } } /** * represents the position of a dom element on the page. * * @typedef {object} module:dom~position * * @property {number} left * pixels to the left. * * @property {number} top * pixels from the top. */ /** * get the position of an element in the dom. * * uses `getboundingclientrect` technique from john resig. * * @see http://ejohn.org/blog/getboundingclientrect-is-awesome/ * * @param {element} el * element from which to get offset. * * @return {module:dom~position} * the position of the element that was passed in. */ function findposition(el) { if (!el || el && !el.offsetparent) { return { left: 0, top: 0, width: 0, height: 0 }; } var width = el.offsetwidth; var height = el.offsetheight; var left = 0; var top = 0; while (el.offsetparent && el !== global_document__webpack_imported_module_1___default.a[fullscreenapi.fullscreenelement]) { left += el.offsetleft; top += el.offsettop; el = el.offsetparent; } return { left: left, top: top, width: width, height: height }; } /** * represents x and y coordinates for a dom element or mouse pointer. * * @typedef {object} module:dom~coordinates * * @property {number} x * x coordinate in pixels * * @property {number} y * y coordinate in pixels */ /** * get the pointer position within an element. * * the base on the coordinates are the bottom left of the element. * * @param {element} el * element on which to get the pointer position on. * * @param {eventtarget~event} event * event object. * * @return {module:dom~coordinates} * a coordinates object corresponding to the mouse position. * */ function getpointerposition(el, event) { var translated = { x: 0, y: 0 }; if (is_ios) { var item = el; while (item && item.nodename.tolowercase() !== 'html') { var transform = computedstyle(item, 'transform'); if (/^matrix/.test(transform)) { var values = transform.slice(7, -1).split(/,\s/).map(number); translated.x += values[4]; translated.y += values[5]; } else if (/^matrix3d/.test(transform)) { var _values = transform.slice(9, -1).split(/,\s/).map(number); translated.x += _values[12]; translated.y += _values[13]; } item = item.parentnode; } } var position = {}; var boxtarget = findposition(event.target); var box = findposition(el); var boxw = box.width; var boxh = box.height; var offsety = event.offsety - (box.top - boxtarget.top); var offsetx = event.offsetx - (box.left - boxtarget.left); if (event.changedtouches) { offsetx = event.changedtouches[0].pagex - box.left; offsety = event.changedtouches[0].pagey + box.top; if (is_ios) { offsetx -= translated.x; offsety -= translated.y; } } position.y = 1 - math.max(0, math.min(1, offsety / boxh)); position.x = math.max(0, math.min(1, offsetx / boxw)); return position; } /** * determines, via duck typing, whether or not a value is a text node. * * @param {mixed} value * check if this value is a text node. * * @return {boolean} * will be `true` if the value is a text node, `false` otherwise. */ function istextnode(value) { return isobject(value) && value.nodetype === 3; } /** * empties the contents of an element. * * @param {element} el * the element to empty children from * * @return {element} * the element with no children */ function emptyel(el) { while (el.firstchild) { el.removechild(el.firstchild); } return el; } /** * this is a mixed value that describes content to be injected into the dom * via some method. it can be of the following types: * * type | description * -----------|------------- * `string` | the value will be normalized into a text node. * `element` | the value will be accepted as-is. * `textnode` | the value will be accepted as-is. * `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). * `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. * * @typedef {string|element|textnode|array|function} module:dom~contentdescriptor */ /** * normalizes content for eventual insertion into the dom. * * this allows a wide range of content definition methods, but helps protect * from falling into the trap of simply writing to `innerhtml`, which could * be an xss concern. * * the content for an element can be passed in multiple types and * combinations, whose behavior is as follows: * * @param {module:dom~contentdescriptor} content * a content descriptor value. * * @return {array} * all of the content that was passed in, normalized to an array of * elements or text nodes. */ function normalizecontent(content) { // first, invoke content if it is a function. if it produces an array, // that needs to happen before normalization. if (typeof content === 'function') { content = content(); } // next up, normalize to an array, so one or many items can be normalized, // filtered, and returned. return (array.isarray(content) ? content : [content]).map(function (value) { // first, invoke value if it is a function to produce a new value, // which will be subsequently normalized to a node of some kind. if (typeof value === 'function') { value = value(); } if (isel(value) || istextnode(value)) { return value; } if (typeof value === 'string' && /\s/.test(value)) { return global_document__webpack_imported_module_1___default.a.createtextnode(value); } }).filter(function (value) { return value; }); } /** * normalizes and appends content to an element. * * @param {element} el * element to append normalized content to. * * @param {module:dom~contentdescriptor} content * a content descriptor value. * * @return {element} * the element with appended normalized content. */ function appendcontent(el, content) { normalizecontent(content).foreach(function (node) { return el.appendchild(node); }); return el; } /** * normalizes and inserts content into an element; this is identical to * `appendcontent()`, except it empties the element first. * * @param {element} el * element to insert normalized content into. * * @param {module:dom~contentdescriptor} content * a content descriptor value. * * @return {element} * the element with inserted normalized content. */ function insertcontent(el, content) { return appendcontent(emptyel(el), content); } /** * check if an event was a single left click. * * @param {eventtarget~event} event * event object. * * @return {boolean} * will be `true` if a single left click, `false` otherwise. */ function issingleleftclick(event) { // note: if you create something draggable, be sure to // call it on both `mousedown` and `mousemove` event, // otherwise `mousedown` should be enough for a button if (event.button === undefined && event.buttons === undefined) { // why do we need `buttons` ? // because, middle mouse sometimes have this: // e.button === 0 and e.buttons === 4 // furthermore, we want to prevent combination click, something like // hold middlemouse then left click, that would be // e.button === 0, e.buttons === 5 // just `button` is not gonna work // alright, then what this block does ? // this is for chrome `simulate mobile devices` // i want to support this as well return true; } if (event.button === 0 && event.buttons === undefined) { // touch screen, sometimes on some specific device, `buttons` // doesn't have anything (safari on ios, blackberry...) return true; } // `mouseup` event on a single left click has // `button` and `buttons` equal to 0 if (event.type === 'mouseup' && event.button === 0 && event.buttons === 0) { return true; } if (event.button !== 0 || event.buttons !== 1) { // this is the reason we have those if else block above // if any special case we can catch and let it slide // we do it above, when get to here, this definitely // is-not-left-click return false; } return true; } /** * finds a single dom element matching `selector` within the optional * `context` of another dom element (defaulting to `document`). * * @param {string} selector * a valid css selector, which will be passed to `queryselector`. * * @param {element|string} [context=document] * a dom element within which to query. can also be a selector * string in which case the first matching element will be used * as context. if missing (or no element matches selector), falls * back to `document`. * * @return {element|null} * the element that was found or null. */ var $ = createquerier('queryselector'); /** * finds a all dom elements matching `selector` within the optional * `context` of another dom element (defaulting to `document`). * * @param {string} selector * a valid css selector, which will be passed to `queryselectorall`. * * @param {element|string} [context=document] * a dom element within which to query. can also be a selector * string in which case the first matching element will be used * as context. if missing (or no element matches selector), falls * back to `document`. * * @return {nodelist} * a element list of elements that were found. will be empty if none * were found. * */ var $$ = createquerier('queryselectorall'); var dom = /*#__pure__*/object.freeze({ __proto__: null, isreal: isreal, isel: isel, isinframe: isinframe, createel: createel, textcontent: textcontent, prependto: prependto, hasclass: hasclass, addclass: addclass, removeclass: removeclass, toggleclass: toggleclass, setattributes: setattributes, getattributes: getattributes, getattribute: getattribute, setattribute: setattribute, removeattribute: removeattribute, blocktextselection: blocktextselection, unblocktextselection: unblocktextselection, getboundingclientrect: getboundingclientrect, findposition: findposition, getpointerposition: getpointerposition, istextnode: istextnode, emptyel: emptyel, normalizecontent: normalizecontent, appendcontent: appendcontent, insertcontent: insertcontent, issingleleftclick: issingleleftclick, $: $, $$: $$ }); /** * @file setup.js - functions for setting up a player without * user interaction based on the data-setup `attribute` of the video tag. * * @module setup */ var _windowloaded = false; var videojs$1; /** * set up any tags that have a data-setup `attribute` when the player is started. */ var autosetup = function autosetup() { if (videojs$1.options.autosetup === false) { return; } var vids = array.prototype.slice.call(global_document__webpack_imported_module_1___default.a.getelementsbytagname('video')); var audios = array.prototype.slice.call(global_document__webpack_imported_module_1___default.a.getelementsbytagname('audio')); var divs = array.prototype.slice.call(global_document__webpack_imported_module_1___default.a.getelementsbytagname('video-js')); var mediaels = vids.concat(audios, divs); // check if any media elements exist if (mediaels && mediaels.length > 0) { for (var i = 0, e = mediaels.length; i < e; i++) { var mediael = mediaels[i]; // check if element exists, has getattribute func. if (mediael && mediael.getattribute) { // make sure this player hasn't already been set up. if (mediael.player === undefined) { var options = mediael.getattribute('data-setup'); // check if data-setup attr exists. // we only auto-setup if they've added the data-setup attr. if (options !== null) { // create new video.js instance. videojs$1(mediael); } } // if getattribute isn't defined, we need to wait for the dom. } else { autosetuptimeout(1); break; } } // no videos were found, so keep looping unless page is finished loading. } else if (!_windowloaded) { autosetuptimeout(1); } }; /** * wait until the page is loaded before running autosetup. this will be called in * autosetup if `hasloaded` returns false. * * @param {number} wait * how long to wait in ms * * @param {module:videojs} [vjs] * the videojs library function */ function autosetuptimeout(wait, vjs) { // protect against breakage in non-browser environments if (!isreal()) { return; } if (vjs) { videojs$1 = vjs; } global_window__webpack_imported_module_0___default.a.settimeout(autosetup, wait); } /** * used to set the internal tracking of window loaded state to true. * * @private */ function setwindowloaded() { _windowloaded = true; global_window__webpack_imported_module_0___default.a.removeeventlistener('load', setwindowloaded); } if (isreal()) { if (global_document__webpack_imported_module_1___default.a.readystate === 'complete') { setwindowloaded(); } else { /** * listen for the load event on window, and set _windowloaded to true. * * we use a standard event listener here to avoid incrementing the guid * before any players are created. * * @listens load */ global_window__webpack_imported_module_0___default.a.addeventlistener('load', setwindowloaded); } } /** * @file stylesheet.js * @module stylesheet */ /** * create a dom syle element given a classname for it. * * @param {string} classname * the classname to add to the created style element. * * @return {element} * the element that was created. */ var createstyleelement = function createstyleelement(classname) { var style = global_document__webpack_imported_module_1___default.a.createelement('style'); style.classname = classname; return style; }; /** * add text to a dom element. * * @param {element} el * the element to add text content to. * * @param {string} content * the text to add to the element. */ var settextcontent = function settextcontent(el, content) { if (el.stylesheet) { el.stylesheet.csstext = content; } else { el.textcontent = content; } }; /** * @file guid.js * @module guid */ // default value for guids. this allows us to reset the guid counter in tests. // // the initial guid is 3 because some users have come to rely on the first // default player id ending up as `vjs_video_3`. // // see: https://github.com/videojs/video.js/pull/6216 var _initialguid = 3; /** * unique id for an element or function * * @type {number} */ var _guid = _initialguid; /** * get a unique auto-incrementing id by number that has not been returned before. * * @return {number} * a new unique id. */ function newguid() { return _guid++; } /** * @file dom-data.js * @module dom-data */ var fakeweakmap; if (!global_window__webpack_imported_module_0___default.a.weakmap) { fakeweakmap = /*#__pure__*/function () { function fakeweakmap() { this.vdata = 'vdata' + math.floor(global_window__webpack_imported_module_0___default.a.performance && global_window__webpack_imported_module_0___default.a.performance.now() || date.now()); this.data = {}; } var _proto = fakeweakmap.prototype; _proto.set = function set(key, value) { var access = key[this.vdata] || newguid(); if (!key[this.vdata]) { key[this.vdata] = access; } this.data[access] = value; return this; }; _proto.get = function get(key) { var access = key[this.vdata]; // we have data, return it if (access) { return this.data[access]; } // we don't have data, return nothing. // return undefined explicitly as that's the contract for this method log$1('we have no data for this element', key); return undefined; }; _proto.has = function has(key) { var access = key[this.vdata]; return access in this.data; }; _proto["delete"] = function _delete(key) { var access = key[this.vdata]; if (access) { delete this.data[access]; delete key[this.vdata]; } }; return fakeweakmap; }(); } /** * element data store. * * allows for binding data to an element without putting it directly on the * element. ex. event listeners are stored here. * (also from jsninja.com, slightly modified and updated for closure compiler) * * @type {object} * @private */ var domdata = global_window__webpack_imported_module_0___default.a.weakmap ? new weakmap() : new fakeweakmap(); /** * @file events.js. an event system (john resig - secrets of a js ninja http://jsninja.com/) * (original book version wasn't completely usable, so fixed some things and made closure compiler compatible) * this should work very similarly to jquery's events, however it's based off the book version which isn't as * robust as jquery's, so there's probably some differences. * * @file events.js * @module events */ /** * clean up the listener cache and dispatchers * * @param {element|object} elem * element to clean up * * @param {string} type * type of event to clean up */ function _cleanupevents(elem, type) { if (!domdata.has(elem)) { return; } var data = domdata.get(elem); // remove the events of a particular type if there are none left if (data.handlers[type].length === 0) { delete data.handlers[type]; // data.handlers[type] = null; // setting to null was causing an error with data.handlers // remove the meta-handler from the element if (elem.removeeventlistener) { elem.removeeventlistener(type, data.dispatcher, false); } else if (elem.detachevent) { elem.detachevent('on' + type, data.dispatcher); } } // remove the events object if there are no types left if (object.getownpropertynames(data.handlers).length <= 0) { delete data.handlers; delete data.dispatcher; delete data.disabled; } // finally remove the element data if there is no data left if (object.getownpropertynames(data).length === 0) { domdata["delete"](elem); } } /** * loops through an array of event types and calls the requested method for each type. * * @param {function} fn * the event method we want to use. * * @param {element|object} elem * element or object to bind listeners to * * @param {string} type * type of event to bind to. * * @param {eventtarget~eventlistener} callback * event listener. */ function _handlemultipleevents(fn, elem, types, callback) { types.foreach(function (type) { // call the event method for each one of the types fn(elem, type, callback); }); } /** * fix a native event to have standard property values * * @param {object} event * event object to fix. * * @return {object} * fixed event object. */ function fixevent(event) { if (event.fixed_) { return event; } function returntrue() { return true; } function returnfalse() { return false; } // test if fixing up is needed // used to check if !event.stoppropagation instead of ispropagationstopped // but native events return true for stoppropagation, but don't have // other expected methods like ispropagationstopped. seems to be a problem // with the javascript ninja code. so we're just overriding all events now. if (!event || !event.ispropagationstopped || !event.isimmediatepropagationstopped) { var old = event || global_window__webpack_imported_module_0___default.a.event; event = {}; // clone the old object so that we can modify the values event = {}; // ie8 doesn't like when you mess with native event properties // firefox returns false for event.hasownproperty('type') and other props // which makes copying more difficult. // todo: probably best to create a whitelist of event props for (var key in old) { // safari 6.0.3 warns you if you try to copy deprecated layerx/y // chrome warns you if you try to copy deprecated keyboardevent.keylocation // and webkitmovementx/y // lighthouse complains if event.path is copied if (key !== 'layerx' && key !== 'layery' && key !== 'keylocation' && key !== 'webkitmovementx' && key !== 'webkitmovementy' && key !== 'path') { // chrome 32+ warns if you try to copy deprecated returnvalue, but // we still want to if preventdefault isn't supported (ie8). if (!(key === 'returnvalue' && old.preventdefault)) { event[key] = old[key]; } } } // the event occurred on this element if (!event.target) { event.target = event.srcelement || global_document__webpack_imported_module_1___default.a; } // handle which other element the event is related to if (!event.relatedtarget) { event.relatedtarget = event.fromelement === event.target ? event.toelement : event.fromelement; } // stop the default browser action event.preventdefault = function () { if (old.preventdefault) { old.preventdefault(); } event.returnvalue = false; old.returnvalue = false; event.defaultprevented = true; }; event.defaultprevented = false; // stop the event from bubbling event.stoppropagation = function () { if (old.stoppropagation) { old.stoppropagation(); } event.cancelbubble = true; old.cancelbubble = true; event.ispropagationstopped = returntrue; }; event.ispropagationstopped = returnfalse; // stop the event from bubbling and executing other handlers event.stopimmediatepropagation = function () { if (old.stopimmediatepropagation) { old.stopimmediatepropagation(); } event.isimmediatepropagationstopped = returntrue; event.stoppropagation(); }; event.isimmediatepropagationstopped = returnfalse; // handle mouse position if (event.clientx !== null && event.clientx !== undefined) { var doc = global_document__webpack_imported_module_1___default.a.documentelement; var body = global_document__webpack_imported_module_1___default.a.body; event.pagex = event.clientx + (doc && doc.scrollleft || body && body.scrollleft || 0) - (doc && doc.clientleft || body && body.clientleft || 0); event.pagey = event.clienty + (doc && doc.scrolltop || body && body.scrolltop || 0) - (doc && doc.clienttop || body && body.clienttop || 0); } // handle key presses event.which = event.charcode || event.keycode; // fix button for mouse clicks: // 0 == left; 1 == middle; 2 == right if (event.button !== null && event.button !== undefined) { // the following is disabled because it does not pass videojs-standard // and... yikes. /* eslint-disable */ event.button = event.button & 1 ? 0 : event.button & 4 ? 1 : event.button & 2 ? 2 : 0; /* eslint-enable */ } } event.fixed_ = true; // returns fixed-up instance return event; } /** * whether passive event listeners are supported */ var _supportspassive; var supportspassive = function supportspassive() { if (typeof _supportspassive !== 'boolean') { _supportspassive = false; try { var opts = object.defineproperty({}, 'passive', { get: function get() { _supportspassive = true; } }); global_window__webpack_imported_module_0___default.a.addeventlistener('test', null, opts); global_window__webpack_imported_module_0___default.a.removeeventlistener('test', null, opts); } catch (e) {// disregard } } return _supportspassive; }; /** * touch events chrome expects to be passive */ var passiveevents = ['touchstart', 'touchmove']; /** * add an event listener to element * it stores the handler function in a separate cache object * and adds a generic handler to the element's event, * along with a unique id (guid) to the element. * * @param {element|object} elem * element or object to bind listeners to * * @param {string|string[]} type * type of event to bind to. * * @param {eventtarget~eventlistener} fn * event listener. */ function on(elem, type, fn) { if (array.isarray(type)) { return _handlemultipleevents(on, elem, type, fn); } if (!domdata.has(elem)) { domdata.set(elem, {}); } var data = domdata.get(elem); // we need a place to store all our handler data if (!data.handlers) { data.handlers = {}; } if (!data.handlers[type]) { data.handlers[type] = []; } if (!fn.guid) { fn.guid = newguid(); } data.handlers[type].push(fn); if (!data.dispatcher) { data.disabled = false; data.dispatcher = function (event, hash) { if (data.disabled) { return; } event = fixevent(event); var handlers = data.handlers[event.type]; if (handlers) { // copy handlers so if handlers are added/removed during the process it doesn't throw everything off. var handlerscopy = handlers.slice(0); for (var m = 0, n = handlerscopy.length; m < n; m++) { if (event.isimmediatepropagationstopped()) { break; } else { try { handlerscopy[m].call(elem, event, hash); } catch (e) { log$1.error(e); } } } } }; } if (data.handlers[type].length === 1) { if (elem.addeventlistener) { var options = false; if (supportspassive() && passiveevents.indexof(type) > -1) { options = { passive: true }; } elem.addeventlistener(type, data.dispatcher, options); } else if (elem.attachevent) { elem.attachevent('on' + type, data.dispatcher); } } } /** * removes event listeners from an element * * @param {element|object} elem * object to remove listeners from. * * @param {string|string[]} [type] * type of listener to remove. don't include to remove all events from element. * * @param {eventtarget~eventlistener} [fn] * specific listener to remove. don't include to remove listeners for an event * type. */ function off(elem, type, fn) { // don't want to add a cache object through geteldata if not needed if (!domdata.has(elem)) { return; } var data = domdata.get(elem); // if no events exist, nothing to unbind if (!data.handlers) { return; } if (array.isarray(type)) { return _handlemultipleevents(off, elem, type, fn); } // utility function var removetype = function removetype(el, t) { data.handlers[t] = []; _cleanupevents(el, t); }; // are we removing all bound events? if (type === undefined) { for (var t in data.handlers) { if (object.prototype.hasownproperty.call(data.handlers || {}, t)) { removetype(elem, t); } } return; } var handlers = data.handlers[type]; // if no handlers exist, nothing to unbind if (!handlers) { return; } // if no listener was provided, remove all listeners for type if (!fn) { removetype(elem, type); return; } // we're only removing a single handler if (fn.guid) { for (var n = 0; n < handlers.length; n++) { if (handlers[n].guid === fn.guid) { handlers.splice(n--, 1); } } } _cleanupevents(elem, type); } /** * trigger an event for an element * * @param {element|object} elem * element to trigger an event on * * @param {eventtarget~event|string} event * a string (the type) or an event object with a type attribute * * @param {object} [hash] * data hash to pass along with the event * * @return {boolean|undefined} * returns the opposite of `defaultprevented` if default was * prevented. otherwise, returns `undefined` */ function trigger(elem, event, hash) { // fetches element data and a reference to the parent (for bubbling). // don't want to add a data object to cache for every parent, // so checking haseldata first. var elemdata = domdata.has(elem) ? domdata.get(elem) : {}; var parent = elem.parentnode || elem.ownerdocument; // type = event.type || event, // handler; // if an event name was passed as a string, creates an event out of it if (typeof event === 'string') { event = { type: event, target: elem }; } else if (!event.target) { event.target = elem; } // normalizes the event properties. event = fixevent(event); // if the passed element has a dispatcher, executes the established handlers. if (elemdata.dispatcher) { elemdata.dispatcher.call(elem, event, hash); } // unless explicitly stopped or the event does not bubble (e.g. media events) // recursively calls this function to bubble the event up the dom. if (parent && !event.ispropagationstopped() && event.bubbles === true) { trigger.call(null, parent, event, hash); // if at the top of the dom, triggers the default action unless disabled. } else if (!parent && !event.defaultprevented && event.target && event.target[event.type]) { if (!domdata.has(event.target)) { domdata.set(event.target, {}); } var targetdata = domdata.get(event.target); // checks if the target has a default action for this event. if (event.target[event.type]) { // temporarily disables event dispatching on the target as we have already executed the handler. targetdata.disabled = true; // executes the default action. if (typeof event.target[event.type] === 'function') { event.target[event.type](); } // re-enables event dispatching. targetdata.disabled = false; } } // inform the triggerer if the default was prevented by returning false return !event.defaultprevented; } /** * trigger a listener only once for an event. * * @param {element|object} elem * element or object to bind to. * * @param {string|string[]} type * name/type of event * * @param {event~eventlistener} fn * event listener function */ function one(elem, type, fn) { if (array.isarray(type)) { return _handlemultipleevents(one, elem, type, fn); } var func = function func() { off(elem, type, func); fn.apply(this, arguments); }; // copy the guid to the new function so it can removed using the original function's id func.guid = fn.guid = fn.guid || newguid(); on(elem, type, func); } /** * trigger a listener only once and then turn if off for all * configured events * * @param {element|object} elem * element or object to bind to. * * @param {string|string[]} type * name/type of event * * @param {event~eventlistener} fn * event listener function */ function any(elem, type, fn) { var func = function func() { off(elem, type, func); fn.apply(this, arguments); }; // copy the guid to the new function so it can removed using the original function's id func.guid = fn.guid = fn.guid || newguid(); // multiple ons, but one off for everything on(elem, type, func); } var events = /*#__pure__*/object.freeze({ __proto__: null, fixevent: fixevent, on: on, off: off, trigger: trigger, one: one, any: any }); /** * @file fn.js * @module fn */ var update_refresh_interval = 30; /** * bind (a.k.a proxy or context). a simple method for changing the context of * a function. * * it also stores a unique id on the function so it can be easily removed from * events. * * @function * @param {mixed} context * the object to bind as scope. * * @param {function} fn * the function to be bound to a scope. * * @param {number} [uid] * an optional unique id for the function to be set * * @return {function} * the new function that will be bound into the context given */ var bind = function bind(context, fn, uid) { // make sure the function has a unique id if (!fn.guid) { fn.guid = newguid(); } // create the new function that changes the context var bound = fn.bind(context); // allow for the ability to individualize this function // needed in the case where multiple objects might share the same prototype // if both items add an event listener with the same function, then you try to remove just one // it will remove both because they both have the same guid. // when using this, you need to use the bind method when you remove the listener as well. // currently used in text tracks bound.guid = uid ? uid + '_' + fn.guid : fn.guid; return bound; }; /** * wraps the given function, `fn`, with a new function that only invokes `fn` * at most once per every `wait` milliseconds. * * @function * @param {function} fn * the function to be throttled. * * @param {number} wait * the number of milliseconds by which to throttle. * * @return {function} */ var throttle = function throttle(fn, wait) { var last = global_window__webpack_imported_module_0___default.a.performance.now(); var throttled = function throttled() { var now = global_window__webpack_imported_module_0___default.a.performance.now(); if (now - last >= wait) { fn.apply(void 0, arguments); last = now; } }; return throttled; }; /** * creates a debounced function that delays invoking `func` until after `wait` * milliseconds have elapsed since the last time the debounced function was * invoked. * * inspired by lodash and underscore implementations. * * @function * @param {function} func * the function to wrap with debounce behavior. * * @param {number} wait * the number of milliseconds to wait after the last invocation. * * @param {boolean} [immediate] * whether or not to invoke the function immediately upon creation. * * @param {object} [context=window] * the "context" in which the debounced function should debounce. for * example, if this function should be tied to a video.js player, * the player can be passed here. alternatively, defaults to the * global `window` object. * * @return {function} * a debounced function. */ var debounce = function debounce(func, wait, immediate, context) { if (context === void 0) { context = global_window__webpack_imported_module_0___default.a; } var timeout; var cancel = function cancel() { context.cleartimeout(timeout); timeout = null; }; /* eslint-disable consistent-this */ var debounced = function debounced() { var self = this; var args = arguments; var _later = function later() { timeout = null; _later = null; if (!immediate) { func.apply(self, args); } }; if (!timeout && immediate) { func.apply(self, args); } context.cleartimeout(timeout); timeout = context.settimeout(_later, wait); }; /* eslint-enable consistent-this */ debounced.cancel = cancel; return debounced; }; /** * @file src/js/event-target.js */ /** * `eventtarget` is a class that can have the same api as the dom `eventtarget`. it * adds shorthand functions that wrap around lengthy functions. for example: * the `on` function is a wrapper around `addeventlistener`. * * @see [eventtarget spec]{@link https://www.w3.org/tr/dom-level-2-events/events.html#events-eventtarget} * @class eventtarget */ var eventtarget$2 = function eventtarget() {}; /** * a custom dom event. * * @typedef {object} eventtarget~event * @see [properties]{@link https://developer.mozilla.org/en-us/docs/web/api/customevent} */ /** * all event listeners should follow the following format. * * @callback eventtarget~eventlistener * @this {eventtarget} * * @param {eventtarget~event} event * the event that triggered this function * * @param {object} [hash] * hash of data sent during the event */ /** * an object containing event names as keys and booleans as values. * * > note: if an event name is set to a true value here {@link eventtarget#trigger} * will have extra functionality. see that function for more information. * * @property eventtarget.prototype.allowedevents_ * @private */ eventtarget$2.prototype.allowedevents_ = {}; /** * adds an `event listener` to an instance of an `eventtarget`. an `event listener` is a * function that will get called when an event with a certain name gets triggered. * * @param {string|string[]} type * an event name or an array of event names. * * @param {eventtarget~eventlistener} fn * the function to call with `eventtarget`s */ eventtarget$2.prototype.on = function (type, fn) { // remove the addeventlistener alias before calling events.on // so we don't get into an infinite type loop var ael = this.addeventlistener; this.addeventlistener = function () {}; on(this, type, fn); this.addeventlistener = ael; }; /** * an alias of {@link eventtarget#on}. allows `eventtarget` to mimic * the standard dom api. * * @function * @see {@link eventtarget#on} */ eventtarget$2.prototype.addeventlistener = eventtarget$2.prototype.on; /** * removes an `event listener` for a specific event from an instance of `eventtarget`. * this makes it so that the `event listener` will no longer get called when the * named event happens. * * @param {string|string[]} type * an event name or an array of event names. * * @param {eventtarget~eventlistener} fn * the function to remove. */ eventtarget$2.prototype.off = function (type, fn) { off(this, type, fn); }; /** * an alias of {@link eventtarget#off}. allows `eventtarget` to mimic * the standard dom api. * * @function * @see {@link eventtarget#off} */ eventtarget$2.prototype.removeeventlistener = eventtarget$2.prototype.off; /** * this function will add an `event listener` that gets triggered only once. after the * first trigger it will get removed. this is like adding an `event listener` * with {@link eventtarget#on} that calls {@link eventtarget#off} on itself. * * @param {string|string[]} type * an event name or an array of event names. * * @param {eventtarget~eventlistener} fn * the function to be called once for each event name. */ eventtarget$2.prototype.one = function (type, fn) { // remove the addeventlistener aliasing events.on // so we don't get into an infinite type loop var ael = this.addeventlistener; this.addeventlistener = function () {}; one(this, type, fn); this.addeventlistener = ael; }; eventtarget$2.prototype.any = function (type, fn) { // remove the addeventlistener aliasing events.on // so we don't get into an infinite type loop var ael = this.addeventlistener; this.addeventlistener = function () {}; any(this, type, fn); this.addeventlistener = ael; }; /** * this function causes an event to happen. this will then cause any `event listeners` * that are waiting for that event, to get called. if there are no `event listeners` * for an event then nothing will happen. * * if the name of the `event` that is being triggered is in `eventtarget.allowedevents_`. * trigger will also call the `on` + `uppercaseeventname` function. * * example: * 'click' is in `eventtarget.allowedevents_`, so, trigger will attempt to call * `onclick` if it exists. * * @param {string|eventtarget~event|object} event * the name of the event, an `event`, or an object with a key of type set to * an event name. */ eventtarget$2.prototype.trigger = function (event) { var type = event.type || event; // deprecation // in a future version we should default target to `this` // similar to how we default the target to `elem` in // `events.trigger`. right now the default `target` will be // `document` due to the `event.fixevent` call. if (typeof event === 'string') { event = { type: type }; } event = fixevent(event); if (this.allowedevents_[type] && this['on' + type]) { this['on' + type](event); } trigger(this, event); }; /** * an alias of {@link eventtarget#trigger}. allows `eventtarget` to mimic * the standard dom api. * * @function * @see {@link eventtarget#trigger} */ eventtarget$2.prototype.dispatchevent = eventtarget$2.prototype.trigger; var event_map; eventtarget$2.prototype.queuetrigger = function (event) { var _this = this; // only set up event_map if it'll be used if (!event_map) { event_map = new map(); } var type = event.type || event; var map = event_map.get(this); if (!map) { map = new map(); event_map.set(this, map); } var oldtimeout = map.get(type); map["delete"](type); global_window__webpack_imported_module_0___default.a.cleartimeout(oldtimeout); var timeout = global_window__webpack_imported_module_0___default.a.settimeout(function () { map["delete"](type); // if we cleared out all timeouts for the current target, delete its map if (map.size === 0) { map = null; event_map["delete"](_this); } _this.trigger(event); }, 0); map.set(type, timeout); }; /** * @file mixins/evented.js * @module evented */ var objname = function objname(obj) { if (typeof obj.name === 'function') { return obj.name(); } if (typeof obj.name === 'string') { return obj.name; } if (obj.name_) { return obj.name_; } if (obj.constructor && obj.constructor.name) { return obj.constructor.name; } return typeof obj; }; /** * returns whether or not an object has had the evented mixin applied. * * @param {object} object * an object to test. * * @return {boolean} * whether or not the object appears to be evented. */ var isevented = function isevented(object) { return object instanceof eventtarget$2 || !!object.eventbusel_ && ['on', 'one', 'off', 'trigger'].every(function (k) { return typeof object[k] === 'function'; }); }; /** * adds a callback to run after the evented mixin applied. * * @param {object} object * an object to add * @param {function} callback * the callback to run. */ var addeventedcallback = function addeventedcallback(target, callback) { if (isevented(target)) { callback(); } else { if (!target.eventedcallbacks) { target.eventedcallbacks = []; } target.eventedcallbacks.push(callback); } }; /** * whether a value is a valid event type - non-empty string or array. * * @private * @param {string|array} type * the type value to test. * * @return {boolean} * whether or not the type is a valid event type. */ var isvalideventtype = function isvalideventtype(type) { return (// the regex here verifies that the `type` contains at least one non- // whitespace character. typeof type === 'string' && /\s/.test(type) || array.isarray(type) && !!type.length ); }; /** * validates a value to determine if it is a valid event target. throws if not. * * @private * @throws {error} * if the target does not appear to be a valid event target. * * @param {object} target * the object to test. * * @param {object} obj * the evented object we are validating for * * @param {string} fnname * the name of the evented mixin function that called this. */ var validatetarget = function validatetarget(target, obj, fnname) { if (!target || !target.nodename && !isevented(target)) { throw new error("invalid target for " + objname(obj) + "#" + fnname + "; must be a dom node or evented object."); } }; /** * validates a value to determine if it is a valid event target. throws if not. * * @private * @throws {error} * if the type does not appear to be a valid event type. * * @param {string|array} type * the type to test. * * @param {object} obj * the evented object we are validating for * * @param {string} fnname * the name of the evented mixin function that called this. */ var validateeventtype = function validateeventtype(type, obj, fnname) { if (!isvalideventtype(type)) { throw new error("invalid event type for " + objname(obj) + "#" + fnname + "; must be a non-empty string or array."); } }; /** * validates a value to determine if it is a valid listener. throws if not. * * @private * @throws {error} * if the listener is not a function. * * @param {function} listener * the listener to test. * * @param {object} obj * the evented object we are validating for * * @param {string} fnname * the name of the evented mixin function that called this. */ var validatelistener = function validatelistener(listener, obj, fnname) { if (typeof listener !== 'function') { throw new error("invalid listener for " + objname(obj) + "#" + fnname + "; must be a function."); } }; /** * takes an array of arguments given to `on()` or `one()`, validates them, and * normalizes them into an object. * * @private * @param {object} self * the evented object on which `on()` or `one()` was called. this * object will be bound as the `this` value for the listener. * * @param {array} args * an array of arguments passed to `on()` or `one()`. * * @param {string} fnname * the name of the evented mixin function that called this. * * @return {object} * an object containing useful values for `on()` or `one()` calls. */ var normalizelistenargs = function normalizelistenargs(self, args, fnname) { // if the number of arguments is less than 3, the target is always the // evented object itself. var istargetingself = args.length < 3 || args[0] === self || args[0] === self.eventbusel_; var target; var type; var listener; if (istargetingself) { target = self.eventbusel_; // deal with cases where we got 3 arguments, but we are still listening to // the evented object itself. if (args.length >= 3) { args.shift(); } type = args[0]; listener = args[1]; } else { target = args[0]; type = args[1]; listener = args[2]; } validatetarget(target, self, fnname); validateeventtype(type, self, fnname); validatelistener(listener, self, fnname); listener = bind(self, listener); return { istargetingself: istargetingself, target: target, type: type, listener: listener }; }; /** * adds the listener to the event type(s) on the target, normalizing for * the type of target. * * @private * @param {element|object} target * a dom node or evented object. * * @param {string} method * the event binding method to use ("on" or "one"). * * @param {string|array} type * one or more event type(s). * * @param {function} listener * a listener function. */ var listen = function listen(target, method, type, listener) { validatetarget(target, target, method); if (target.nodename) { events[method](target, type, listener); } else { target[method](type, listener); } }; /** * contains methods that provide event capabilities to an object which is passed * to {@link module:evented|evented}. * * @mixin eventedmixin */ var eventedmixin = { /** * add a listener to an event (or events) on this object or another evented * object. * * @param {string|array|element|object} targetortype * if this is a string or array, it represents the event type(s) * that will trigger the listener. * * another evented object can be passed here instead, which will * cause the listener to listen for events on _that_ object. * * in either case, the listener's `this` value will be bound to * this object. * * @param {string|array|function} typeorlistener * if the first argument was a string or array, this should be the * listener function. otherwise, this is a string or array of event * type(s). * * @param {function} [listener] * if the first argument was another evented object, this will be * the listener function. */ on: function on() { var _this = this; for (var _len = arguments.length, args = new array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } var _normalizelistenargs = normalizelistenargs(this, args, 'on'), istargetingself = _normalizelistenargs.istargetingself, target = _normalizelistenargs.target, type = _normalizelistenargs.type, listener = _normalizelistenargs.listener; listen(target, 'on', type, listener); // if this object is listening to another evented object. if (!istargetingself) { // if this object is disposed, remove the listener. var removelistenerondispose = function removelistenerondispose() { return _this.off(target, type, listener); }; // use the same function id as the listener so we can remove it later it // using the id of the original listener. removelistenerondispose.guid = listener.guid; // add a listener to the target's dispose event as well. this ensures // that if the target is disposed before this object, we remove the // removal listener that was just added. otherwise, we create a memory leak. var removeremoverontargetdispose = function removeremoverontargetdispose() { return _this.off('dispose', removelistenerondispose); }; // use the same function id as the listener so we can remove it later // it using the id of the original listener. removeremoverontargetdispose.guid = listener.guid; listen(this, 'on', 'dispose', removelistenerondispose); listen(target, 'on', 'dispose', removeremoverontargetdispose); } }, /** * add a listener to an event (or events) on this object or another evented * object. the listener will be called once per event and then removed. * * @param {string|array|element|object} targetortype * if this is a string or array, it represents the event type(s) * that will trigger the listener. * * another evented object can be passed here instead, which will * cause the listener to listen for events on _that_ object. * * in either case, the listener's `this` value will be bound to * this object. * * @param {string|array|function} typeorlistener * if the first argument was a string or array, this should be the * listener function. otherwise, this is a string or array of event * type(s). * * @param {function} [listener] * if the first argument was another evented object, this will be * the listener function. */ one: function one() { var _this2 = this; for (var _len2 = arguments.length, args = new array(_len2), _key2 = 0; _key2 < _len2; _key2++) { args[_key2] = arguments[_key2]; } var _normalizelistenargs2 = normalizelistenargs(this, args, 'one'), istargetingself = _normalizelistenargs2.istargetingself, target = _normalizelistenargs2.target, type = _normalizelistenargs2.type, listener = _normalizelistenargs2.listener; // targeting this evented object. if (istargetingself) { listen(target, 'one', type, listener); // targeting another evented object. } else { // todo: this wrapper is incorrect! it should only // remove the wrapper for the event type that called it. // instead all listners are removed on the first trigger! // see https://github.com/videojs/video.js/issues/5962 var wrapper = function wrapper() { _this2.off(target, type, wrapper); for (var _len3 = arguments.length, largs = new array(_len3), _key3 = 0; _key3 < _len3; _key3++) { largs[_key3] = arguments[_key3]; } listener.apply(null, largs); }; // use the same function id as the listener so we can remove it later // it using the id of the original listener. wrapper.guid = listener.guid; listen(target, 'one', type, wrapper); } }, /** * add a listener to an event (or events) on this object or another evented * object. the listener will only be called once for the first event that is triggered * then removed. * * @param {string|array|element|object} targetortype * if this is a string or array, it represents the event type(s) * that will trigger the listener. * * another evented object can be passed here instead, which will * cause the listener to listen for events on _that_ object. * * in either case, the listener's `this` value will be bound to * this object. * * @param {string|array|function} typeorlistener * if the first argument was a string or array, this should be the * listener function. otherwise, this is a string or array of event * type(s). * * @param {function} [listener] * if the first argument was another evented object, this will be * the listener function. */ any: function any() { var _this3 = this; for (var _len4 = arguments.length, args = new array(_len4), _key4 = 0; _key4 < _len4; _key4++) { args[_key4] = arguments[_key4]; } var _normalizelistenargs3 = normalizelistenargs(this, args, 'any'), istargetingself = _normalizelistenargs3.istargetingself, target = _normalizelistenargs3.target, type = _normalizelistenargs3.type, listener = _normalizelistenargs3.listener; // targeting this evented object. if (istargetingself) { listen(target, 'any', type, listener); // targeting another evented object. } else { var wrapper = function wrapper() { _this3.off(target, type, wrapper); for (var _len5 = arguments.length, largs = new array(_len5), _key5 = 0; _key5 < _len5; _key5++) { largs[_key5] = arguments[_key5]; } listener.apply(null, largs); }; // use the same function id as the listener so we can remove it later // it using the id of the original listener. wrapper.guid = listener.guid; listen(target, 'any', type, wrapper); } }, /** * removes listener(s) from event(s) on an evented object. * * @param {string|array|element|object} [targetortype] * if this is a string or array, it represents the event type(s). * * another evented object can be passed here instead, in which case * all 3 arguments are _required_. * * @param {string|array|function} [typeorlistener] * if the first argument was a string or array, this may be the * listener function. otherwise, this is a string or array of event * type(s). * * @param {function} [listener] * if the first argument was another evented object, this will be * the listener function; otherwise, _all_ listeners bound to the * event type(s) will be removed. */ off: function off$1(targetortype, typeorlistener, listener) { // targeting this evented object. if (!targetortype || isvalideventtype(targetortype)) { off(this.eventbusel_, targetortype, typeorlistener); // targeting another evented object. } else { var target = targetortype; var type = typeorlistener; // fail fast and in a meaningful way! validatetarget(target, this, 'off'); validateeventtype(type, this, 'off'); validatelistener(listener, this, 'off'); // ensure there's at least a guid, even if the function hasn't been used listener = bind(this, listener); // remove the dispose listener on this evented object, which was given // the same guid as the event listener in on(). this.off('dispose', listener); if (target.nodename) { off(target, type, listener); off(target, 'dispose', listener); } else if (isevented(target)) { target.off(type, listener); target.off('dispose', listener); } } }, /** * fire an event on this evented object, causing its listeners to be called. * * @param {string|object} event * an event type or an object with a type property. * * @param {object} [hash] * an additional object to pass along to listeners. * * @return {boolean} * whether or not the default behavior was prevented. */ trigger: function trigger$1(event, hash) { validatetarget(this.eventbusel_, this, 'trigger'); var type = event && typeof event !== 'string' ? event.type : event; if (!isvalideventtype(type)) { var 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.'; if (event) { (this.log || log$1).error(error); } else { throw new error(error); } } return trigger(this.eventbusel_, event, hash); } }; /** * applies {@link module:evented~eventedmixin|eventedmixin} to a target object. * * @param {object} target * the object to which to add event methods. * * @param {object} [options={}] * options for customizing the mixin behavior. * * @param {string} [options.eventbuskey] * by default, adds a `eventbusel_` dom element to the target object, * which is used as an event bus. if the target object already has a * dom element that should be used, pass its key here. * * @return {object} * the target object. */ function evented(target, options) { if (options === void 0) { options = {}; } var _options = options, eventbuskey = _options.eventbuskey; // set or create the eventbusel_. if (eventbuskey) { if (!target[eventbuskey].nodename) { throw new error("the eventbuskey \"" + eventbuskey + "\" does not refer to an element."); } target.eventbusel_ = target[eventbuskey]; } else { target.eventbusel_ = createel('span', { classname: 'vjs-event-bus' }); } assign(target, eventedmixin); if (target.eventedcallbacks) { target.eventedcallbacks.foreach(function (callback) { callback(); }); } // when any evented object is disposed, it removes all its listeners. target.on('dispose', function () { target.off(); [target, target.el_, target.eventbusel_].foreach(function (val) { if (val && domdata.has(val)) { domdata["delete"](val); } }); global_window__webpack_imported_module_0___default.a.settimeout(function () { target.eventbusel_ = null; }, 0); }); return target; } /** * @file mixins/stateful.js * @module stateful */ /** * contains methods that provide statefulness to an object which is passed * to {@link module:stateful}. * * @mixin statefulmixin */ var statefulmixin = { /** * a hash containing arbitrary keys and values representing the state of * the object. * * @type {object} */ state: {}, /** * set the state of an object by mutating its * {@link module:stateful~statefulmixin.state|state} object in place. * * @fires module:stateful~statefulmixin#statechanged * @param {object|function} stateupdates * a new set of properties to shallow-merge into the plugin state. * can be a plain object or a function returning a plain object. * * @return {object|undefined} * an object containing changes that occurred. if no changes * occurred, returns `undefined`. */ setstate: function setstate(stateupdates) { var _this = this; // support providing the `stateupdates` state as a function. if (typeof stateupdates === 'function') { stateupdates = stateupdates(); } var changes; each(stateupdates, function (value, key) { // record the change if the value is different from what's in the // current state. if (_this.state[key] !== value) { changes = changes || {}; changes[key] = { from: _this.state[key], to: value }; } _this.state[key] = value; }); // only trigger "statechange" if there were changes and we have a trigger // function. this allows us to not require that the target object be an // evented object. if (changes && isevented(this)) { /** * an event triggered on an object that is both * {@link module:stateful|stateful} and {@link module:evented|evented} * indicating that its state has changed. * * @event module:stateful~statefulmixin#statechanged * @type {object} * @property {object} changes * a hash containing the properties that were changed and * the values they were changed `from` and `to`. */ this.trigger({ changes: changes, type: 'statechanged' }); } return changes; } }; /** * applies {@link module:stateful~statefulmixin|statefulmixin} to a target * object. * * if the target object is {@link module:evented|evented} and has a * `handlestatechanged` method, that method will be automatically bound to the * `statechanged` event on itself. * * @param {object} target * the object to be made stateful. * * @param {object} [defaultstate] * a default set of properties to populate the newly-stateful object's * `state` property. * * @return {object} * returns the `target`. */ function stateful(target, defaultstate) { assign(target, statefulmixin); // this happens after the mixing-in because we need to replace the `state` // added in that step. target.state = assign({}, target.state, defaultstate); // auto-bind the `handlestatechanged` method of the target object if it exists. if (typeof target.handlestatechanged === 'function' && isevented(target)) { target.on('statechanged', target.handlestatechanged); } return target; } /** * @file string-cases.js * @module to-lower-case */ /** * lowercase the first letter of a string. * * @param {string} string * string to be lowercased * * @return {string} * the string with a lowercased first letter */ var tolowercase = function tolowercase(string) { if (typeof string !== 'string') { return string; } return string.replace(/./, function (w) { return w.tolowercase(); }); }; /** * uppercase the first letter of a string. * * @param {string} string * string to be uppercased * * @return {string} * the string with an uppercased first letter */ var totitlecase$1 = function totitlecase(string) { if (typeof string !== 'string') { return string; } return string.replace(/./, function (w) { return w.touppercase(); }); }; /** * compares the titlecase versions of the two strings for equality. * * @param {string} str1 * the first string to compare * * @param {string} str2 * the second string to compare * * @return {boolean} * whether the titlecase versions of the strings are equal */ var titlecaseequals = function titlecaseequals(str1, str2) { return totitlecase$1(str1) === totitlecase$1(str2); }; /** * @file merge-options.js * @module merge-options */ /** * merge two objects recursively. * * performs a deep merge like * {@link https://lodash.com/docs/4.17.10#merge|lodash.merge}, but only merges * plain objects (not arrays, elements, or anything else). * * non-plain object values will be copied directly from the right-most * argument. * * @static * @param {object[]} sources * one or more objects to merge into a new object. * * @return {object} * a new object that is the merged result of all sources. */ function mergeoptions$3() { var result = {}; for (var _len = arguments.length, sources = new array(_len), _key = 0; _key < _len; _key++) { sources[_key] = arguments[_key]; } sources.foreach(function (source) { if (!source) { return; } each(source, function (value, key) { if (!isplain(value)) { result[key] = value; return; } if (!isplain(result[key])) { result[key] = {}; } result[key] = mergeoptions$3(result[key], value); }); }); return result; } var mapsham = /*#__pure__*/function () { function mapsham() { this.map_ = {}; } var _proto = mapsham.prototype; _proto.has = function has(key) { return key in this.map_; }; _proto["delete"] = function _delete(key) { var has = this.has(key); delete this.map_[key]; return has; }; _proto.set = function set(key, value) { this.map_[key] = value; return this; }; _proto.foreach = function foreach(callback, thisarg) { for (var key in this.map_) { callback.call(thisarg, this.map_[key], key, this); } }; return mapsham; }(); var map$1 = global_window__webpack_imported_module_0___default.a.map ? global_window__webpack_imported_module_0___default.a.map : mapsham; var setsham = /*#__pure__*/function () { function setsham() { this.set_ = {}; } var _proto = setsham.prototype; _proto.has = function has(key) { return key in this.set_; }; _proto["delete"] = function _delete(key) { var has = this.has(key); delete this.set_[key]; return has; }; _proto.add = function add(key) { this.set_[key] = 1; return this; }; _proto.foreach = function foreach(callback, thisarg) { for (var key in this.set_) { callback.call(thisarg, key, key, this); } }; return setsham; }(); var set$1 = global_window__webpack_imported_module_0___default.a.set ? global_window__webpack_imported_module_0___default.a.set : setsham; /** * player component - base class for all ui objects * * @file component.js */ /** * base class for all ui components. * components are ui objects which represent both a javascript object and an element * in the dom. they can be children of other components, and can have * children themselves. * * components can also use methods from {@link eventtarget} */ var component$1 = /*#__pure__*/function () { /** * a callback that is called when a component is ready. does not have any * paramters and any callback value will be ignored. * * @callback component~readycallback * @this component */ /** * creates an instance of this class. * * @param {player} player * the `player` that this class should be attached to. * * @param {object} [options] * the key/value store of component options. * * @param {object[]} [options.children] * an array of children objects to intialize this component with. children objects have * a name property that will be used if more than one component of the same type needs to be * added. * * @param {string} [options.classname] * a class or space separated list of classes to add the component * * @param {component~readycallback} [ready] * function that gets called when the `component` is ready. */ function component(player, options, ready) { var _this = this; // the component might be the player itself and we can't pass `this` to super if (!player && this.play) { this.player_ = player = this; // eslint-disable-line } else { this.player_ = player; } this.isdisposed_ = false; // hold the reference to the parent component via `addchild` method this.parentcomponent_ = null; // make a copy of prototype.options_ to protect against overriding defaults this.options_ = mergeoptions$3({}, this.options_); // updated options with supplied options options = this.options_ = mergeoptions$3(this.options_, options); // get id from options or options element if one is supplied this.id_ = options.id || options.el && options.el.id; // if there was no id from the options, generate one if (!this.id_) { // don't require the player id function in the case of mock players var id = player && player.id && player.id() || 'no_player'; this.id_ = id + "_component_" + newguid(); } this.name_ = options.name || null; // create element if one wasn't provided in options if (options.el) { this.el_ = options.el; } else if (options.createel !== false) { this.el_ = this.createel(); } if (options.classname && this.el_) { options.classname.split(' ').foreach(function (c) { return _this.addclass(c); }); } // if evented is anything except false, we want to mixin in evented if (options.evented !== false) { // make this an evented object and use `el_`, if available, as its event bus evented(this, { eventbuskey: this.el_ ? 'el_' : null }); this.handlelanguagechange = this.handlelanguagechange.bind(this); this.on(this.player_, 'languagechange', this.handlelanguagechange); } stateful(this, this.constructor.defaultstate); this.children_ = []; this.childindex_ = {}; this.childnameindex_ = {}; this.settimeoutids_ = new set$1(); this.setintervalids_ = new set$1(); this.rafids_ = new set$1(); this.namedrafs_ = new map$1(); this.clearingtimersondispose_ = false; // add any child components in options if (options.initchildren !== false) { this.initchildren(); } // don't want to trigger ready here or it will go before init is actually // finished for all children that run this constructor this.ready(ready); if (options.reporttouchactivity !== false) { this.enabletouchactivity(); } } /** * dispose of the `component` and all child components. * * @fires component#dispose * * @param {object} options * @param {element} options.originalel element with which to replace player element */ var _proto = component.prototype; _proto.dispose = function dispose(options) { if (options === void 0) { options = {}; } // bail out if the component has already been disposed. if (this.isdisposed_) { return; } if (this.readyqueue_) { this.readyqueue_.length = 0; } /** * triggered when a `component` is disposed. * * @event component#dispose * @type {eventtarget~event} * * @property {boolean} [bubbles=false] * set to false so that the dispose event does not * bubble up */ this.trigger({ type: 'dispose', bubbles: false }); this.isdisposed_ = true; // dispose all children. if (this.children_) { for (var i = this.children_.length - 1; i >= 0; i--) { if (this.children_[i].dispose) { this.children_[i].dispose(); } } } // delete child references this.children_ = null; this.childindex_ = null; this.childnameindex_ = null; this.parentcomponent_ = null; if (this.el_) { // remove element from dom if (this.el_.parentnode) { if (options.restoreel) { this.el_.parentnode.replacechild(options.restoreel, this.el_); } else { this.el_.parentnode.removechild(this.el_); } } this.el_ = null; } // remove reference to the player after disposing of the element this.player_ = null; } /** * determine whether or not this component has been disposed. * * @return {boolean} * if the component has been disposed, will be `true`. otherwise, `false`. */ ; _proto.isdisposed = function isdisposed() { return boolean(this.isdisposed_); } /** * return the {@link player} that the `component` has attached to. * * @return {player} * the player that this `component` has attached to. */ ; _proto.player = function player() { return this.player_; } /** * deep merge of options objects with new options. * > note: when both `obj` and `options` contain properties whose values are objects. * the two properties get merged using {@link module:mergeoptions} * * @param {object} obj * the object that contains new options. * * @return {object} * a new object of `this.options_` and `obj` merged together. */ ; _proto.options = function options(obj) { if (!obj) { return this.options_; } this.options_ = mergeoptions$3(this.options_, obj); return this.options_; } /** * get the `component`s dom element * * @return {element} * the dom element for this `component`. */ ; _proto.el = function el() { return this.el_; } /** * create the `component`s dom element. * * @param {string} [tagname] * element's dom node type. e.g. 'div' * * @param {object} [properties] * an object of properties that should be set. * * @param {object} [attributes] * an object of attributes that should be set. * * @return {element} * the element that gets created. */ ; _proto.createel = function createel$1(tagname, properties, attributes) { return createel(tagname, properties, attributes); } /** * localize a string given the string in english. * * if tokens are provided, it'll try and run a simple token replacement on the provided string. * the tokens it looks for look like `{1}` with the index being 1-indexed into the tokens array. * * if a `defaultvalue` is provided, it'll use that over `string`, * if a value isn't found in provided language files. * this is useful if you want to have a descriptive key for token replacement * but have a succinct localized string and not require `en.json` to be included. * * currently, it is used for the progress bar timing. * ```js * { * "progress bar timing: currenttime={1} duration={2}": "{1} of {2}" * } * ``` * it is then used like so: * ```js * this.localize('progress bar timing: currenttime={1} duration{2}', * [this.player_.currenttime(), this.player_.duration()], * '{1} of {2}'); * ``` * * which outputs something like: `01:23 of 24:56`. * * * @param {string} string * the string to localize and the key to lookup in the language files. * @param {string[]} [tokens] * if the current item has token replacements, provide the tokens here. * @param {string} [defaultvalue] * defaults to `string`. can be a default value to use for token replacement * if the lookup key is needed to be separate. * * @return {string} * the localized string or if no localization exists the english string. */ ; _proto.localize = function localize(string, tokens, defaultvalue) { if (defaultvalue === void 0) { defaultvalue = string; } var code = this.player_.language && this.player_.language(); var languages = this.player_.languages && this.player_.languages(); var language = languages && languages[code]; var primarycode = code && code.split('-')[0]; var primarylang = languages && languages[primarycode]; var localizedstring = defaultvalue; if (language && language[string]) { localizedstring = language[string]; } else if (primarylang && primarylang[string]) { localizedstring = primarylang[string]; } if (tokens) { localizedstring = localizedstring.replace(/\{(\d+)\}/g, function (match, index) { var value = tokens[index - 1]; var ret = value; if (typeof value === 'undefined') { ret = match; } return ret; }); } return localizedstring; } /** * handles language change for the player in components. should be overriden by sub-components. * * @abstract */ ; _proto.handlelanguagechange = function handlelanguagechange() {} /** * return the `component`s dom element. this is where children get inserted. * this will usually be the the same as the element returned in {@link component#el}. * * @return {element} * the content element for this `component`. */ ; _proto.contentel = function contentel() { return this.contentel_ || this.el_; } /** * get this `component`s id * * @return {string} * the id of this `component` */ ; _proto.id = function id() { return this.id_; } /** * get the `component`s name. the name gets used to reference the `component` * and is set during registration. * * @return {string} * the name of this `component`. */ ; _proto.name = function name() { return this.name_; } /** * get an array of all child components * * @return {array} * the children */ ; _proto.children = function children() { return this.children_; } /** * returns the child `component` with the given `id`. * * @param {string} id * the id of the child `component` to get. * * @return {component|undefined} * the child `component` with the given `id` or undefined. */ ; _proto.getchildbyid = function getchildbyid(id) { return this.childindex_[id]; } /** * returns the child `component` with the given `name`. * * @param {string} name * the name of the child `component` to get. * * @return {component|undefined} * the child `component` with the given `name` or undefined. */ ; _proto.getchild = function getchild(name) { if (!name) { return; } return this.childnameindex_[name]; } /** * returns the descendant `component` following the givent * descendant `names`. for instance ['foo', 'bar', 'baz'] would * try to get 'foo' on the current component, 'bar' on the 'foo' * component and 'baz' on the 'bar' component and return undefined * if any of those don't exist. * * @param {...string[]|...string} names * the name of the child `component` to get. * * @return {component|undefined} * the descendant `component` following the given descendant * `names` or undefined. */ ; _proto.getdescendant = function getdescendant() { for (var _len = arguments.length, names = new array(_len), _key = 0; _key < _len; _key++) { names[_key] = arguments[_key]; } // flatten array argument into the main array names = names.reduce(function (acc, n) { return acc.concat(n); }, []); var currentchild = this; for (var i = 0; i < names.length; i++) { currentchild = currentchild.getchild(names[i]); if (!currentchild || !currentchild.getchild) { return; } } return currentchild; } /** * add a child `component` inside the current `component`. * * * @param {string|component} child * the name or instance of a child to add. * * @param {object} [options={}] * the key/value store of options that will get passed to children of * the child. * * @param {number} [index=this.children_.length] * the index to attempt to add a child into. * * @return {component} * the `component` that gets added as a child. when using a string the * `component` will get created by this process. */ ; _proto.addchild = function addchild(child, options, index) { if (options === void 0) { options = {}; } if (index === void 0) { index = this.children_.length; } var component; var componentname; // if child is a string, create component with options if (typeof child === 'string') { componentname = totitlecase$1(child); var componentclassname = options.componentclass || componentname; // set name through options options.name = componentname; // create a new object & element for this controls set // if there's no .player_, this is a player var componentclass = component.getcomponent(componentclassname); if (!componentclass) { throw new error("component " + componentclassname + " does not exist"); } // data stored directly on the videojs object may be // misidentified as a component to retain // backwards-compatibility with 4.x. check to make sure the // component class can be instantiated. if (typeof componentclass !== 'function') { return null; } component = new componentclass(this.player_ || this, options); // child is a component instance } else { component = child; } if (component.parentcomponent_) { component.parentcomponent_.removechild(component); } this.children_.splice(index, 0, component); component.parentcomponent_ = this; if (typeof component.id === 'function') { this.childindex_[component.id()] = component; } // if a name wasn't used to create the component, check if we can use the // name function of the component componentname = componentname || component.name && totitlecase$1(component.name()); if (componentname) { this.childnameindex_[componentname] = component; this.childnameindex_[tolowercase(componentname)] = component; } // add the ui object's element to the container div (box) // having an element is not required if (typeof component.el === 'function' && component.el()) { // if inserting before a component, insert before that component's element var refnode = null; if (this.children_[index + 1]) { // most children are components, but the video tech is an html element if (this.children_[index + 1].el_) { refnode = this.children_[index + 1].el_; } else if (isel(this.children_[index + 1])) { refnode = this.children_[index + 1]; } } this.contentel().insertbefore(component.el(), refnode); } // return so it can stored on parent object if desired. return component; } /** * remove a child `component` from this `component`s list of children. also removes * the child `component`s element from this `component`s element. * * @param {component} component * the child `component` to remove. */ ; _proto.removechild = function removechild(component) { if (typeof component === 'string') { component = this.getchild(component); } if (!component || !this.children_) { return; } var childfound = false; for (var i = this.children_.length - 1; i >= 0; i--) { if (this.children_[i] === component) { childfound = true; this.children_.splice(i, 1); break; } } if (!childfound) { return; } component.parentcomponent_ = null; this.childindex_[component.id()] = null; this.childnameindex_[totitlecase$1(component.name())] = null; this.childnameindex_[tolowercase(component.name())] = null; var compel = component.el(); if (compel && compel.parentnode === this.contentel()) { this.contentel().removechild(component.el()); } } /** * add and initialize default child `component`s based upon options. */ ; _proto.initchildren = function initchildren() { var _this2 = this; var children = this.options_.children; if (children) { // `this` is `parent` var parentoptions = this.options_; var handleadd = function handleadd(child) { var name = child.name; var opts = child.opts; // allow options for children to be set at the parent options // e.g. videojs(id, { controlbar: false }); // instead of videojs(id, { children: { controlbar: false }); if (parentoptions[name] !== undefined) { opts = parentoptions[name]; } // allow for disabling default components // e.g. options['children']['posterimage'] = false if (opts === false) { return; } // allow options to be passed as a simple boolean if no configuration // is necessary. if (opts === true) { opts = {}; } // we also want to pass the original player options // to each component as well so they don't need to // reach back into the player for options later. opts.playeroptions = _this2.options_.playeroptions; // create and add the child component. // add a direct reference to the child by name on the parent instance. // if two of the same component are used, different names should be supplied // for each var newchild = _this2.addchild(name, opts); if (newchild) { _this2[name] = newchild; } }; // allow for an array of children details to passed in the options var workingchildren; var tech = component.getcomponent('tech'); if (array.isarray(children)) { workingchildren = children; } else { workingchildren = object.keys(children); } workingchildren // children that are in this.options_ but also in workingchildren would // give us extra children we do not want. so, we want to filter them out. .concat(object.keys(this.options_).filter(function (child) { return !workingchildren.some(function (wchild) { if (typeof wchild === 'string') { return child === wchild; } return child === wchild.name; }); })).map(function (child) { var name; var opts; if (typeof child === 'string') { name = child; opts = children[name] || _this2.options_[name] || {}; } else { name = child.name; opts = child; } return { name: name, opts: opts }; }).filter(function (child) { // we have to make sure that child.name isn't in the techorder since // techs are registerd as components but can't aren't compatible // see https://github.com/videojs/video.js/issues/2772 var c = component.getcomponent(child.opts.componentclass || totitlecase$1(child.name)); return c && !tech.istech(c); }).foreach(handleadd); } } /** * builds the default dom class name. should be overriden by sub-components. * * @return {string} * the dom class name for this object. * * @abstract */ ; _proto.buildcssclass = function buildcssclass() { // child classes can include a function that does: // return 'class name' + this._super(); return ''; } /** * bind a listener to the component's ready state. * different from event listeners in that if the ready event has already happened * it will trigger the function immediately. * * @return {component} * returns itself; method can be chained. */ ; _proto.ready = function ready(fn, sync) { if (sync === void 0) { sync = false; } if (!fn) { return; } if (!this.isready_) { this.readyqueue_ = this.readyqueue_ || []; this.readyqueue_.push(fn); return; } if (sync) { fn.call(this); } else { // call the function asynchronously by default for consistency this.settimeout(fn, 1); } } /** * trigger all the ready listeners for this `component`. * * @fires component#ready */ ; _proto.triggerready = function triggerready() { this.isready_ = true; // ensure ready is triggered asynchronously this.settimeout(function () { var readyqueue = this.readyqueue_; // reset ready queue this.readyqueue_ = []; if (readyqueue && readyqueue.length > 0) { readyqueue.foreach(function (fn) { fn.call(this); }, this); } // allow for using event listeners also /** * triggered when a `component` is ready. * * @event component#ready * @type {eventtarget~event} */ this.trigger('ready'); }, 1); } /** * find a single dom element matching a `selector`. this can be within the `component`s * `contentel()` or another custom context. * * @param {string} selector * a valid css selector, which will be passed to `queryselector`. * * @param {element|string} [context=this.contentel()] * a dom element within which to query. can also be a selector string in * which case the first matching element will get used as context. if * missing `this.contentel()` gets used. if `this.contentel()` returns * nothing it falls back to `document`. * * @return {element|null} * the dom element that was found, or null * * @see [information on css selectors](https://developer.mozilla.org/en-us/docs/web/guide/css/getting_started/selectors) */ ; _proto.$ = function $$1(selector, context) { return $(selector, context || this.contentel()); } /** * finds all dom element matching a `selector`. this can be within the `component`s * `contentel()` or another custom context. * * @param {string} selector * a valid css selector, which will be passed to `queryselectorall`. * * @param {element|string} [context=this.contentel()] * a dom element within which to query. can also be a selector string in * which case the first matching element will get used as context. if * missing `this.contentel()` gets used. if `this.contentel()` returns * nothing it falls back to `document`. * * @return {nodelist} * a list of dom elements that were found * * @see [information on css selectors](https://developer.mozilla.org/en-us/docs/web/guide/css/getting_started/selectors) */ ; _proto.$$ = function $$$1(selector, context) { return $$(selector, context || this.contentel()); } /** * check if a component's element has a css class name. * * @param {string} classtocheck * css class name to check. * * @return {boolean} * - true if the `component` has the class. * - false if the `component` does not have the class` */ ; _proto.hasclass = function hasclass$1(classtocheck) { return hasclass(this.el_, classtocheck); } /** * add a css class name to the `component`s element. * * @param {string} classtoadd * css class name to add */ ; _proto.addclass = function addclass$1(classtoadd) { addclass(this.el_, classtoadd); } /** * remove a css class name from the `component`s element. * * @param {string} classtoremove * css class name to remove */ ; _proto.removeclass = function removeclass$1(classtoremove) { removeclass(this.el_, classtoremove); } /** * add or remove a css class name from the component's element. * - `classtotoggle` gets added when {@link component#hasclass} would return false. * - `classtotoggle` gets removed when {@link component#hasclass} would return true. * * @param {string} classtotoggle * the class to add or remove based on (@link component#hasclass} * * @param {boolean|dom~predicate} [predicate] * an {@link dom~predicate} function or a boolean */ ; _proto.toggleclass = function toggleclass$1(classtotoggle, predicate) { toggleclass(this.el_, classtotoggle, predicate); } /** * show the `component`s element if it is hidden by removing the * 'vjs-hidden' class name from it. */ ; _proto.show = function show() { this.removeclass('vjs-hidden'); } /** * hide the `component`s element if it is currently showing by adding the * 'vjs-hidden` class name to it. */ ; _proto.hide = function hide() { this.addclass('vjs-hidden'); } /** * lock a `component`s element in its visible state by adding the 'vjs-lock-showing' * class name to it. used during fadein/fadeout. * * @private */ ; _proto.lockshowing = function lockshowing() { this.addclass('vjs-lock-showing'); } /** * unlock a `component`s element from its visible state by removing the 'vjs-lock-showing' * class name from it. used during fadein/fadeout. * * @private */ ; _proto.unlockshowing = function unlockshowing() { this.removeclass('vjs-lock-showing'); } /** * get the value of an attribute on the `component`s element. * * @param {string} attribute * name of the attribute to get the value from. * * @return {string|null} * - the value of the attribute that was asked for. * - can be an empty string on some browsers if the attribute does not exist * or has no value * - most browsers will return null if the attibute does not exist or has * no value. * * @see [dom api]{@link https://developer.mozilla.org/en-us/docs/web/api/element/getattribute} */ ; _proto.getattribute = function getattribute$1(attribute) { return getattribute(this.el_, attribute); } /** * set the value of an attribute on the `component`'s element * * @param {string} attribute * name of the attribute to set. * * @param {string} value * value to set the attribute to. * * @see [dom api]{@link https://developer.mozilla.org/en-us/docs/web/api/element/setattribute} */ ; _proto.setattribute = function setattribute$1(attribute, value) { setattribute(this.el_, attribute, value); } /** * remove an attribute from the `component`s element. * * @param {string} attribute * name of the attribute to remove. * * @see [dom api]{@link https://developer.mozilla.org/en-us/docs/web/api/element/removeattribute} */ ; _proto.removeattribute = function removeattribute$1(attribute) { removeattribute(this.el_, attribute); } /** * get or set the width of the component based upon the css styles. * see {@link component#dimension} for more detailed information. * * @param {number|string} [num] * the width that you want to set postfixed with '%', 'px' or nothing. * * @param {boolean} [skiplisteners] * skip the componentresize event trigger * * @return {number|string} * the width when getting, zero if there is no width. can be a string * postpixed with '%' or 'px'. */ ; _proto.width = function width(num, skiplisteners) { return this.dimension('width', num, skiplisteners); } /** * get or set the height of the component based upon the css styles. * see {@link component#dimension} for more detailed information. * * @param {number|string} [num] * the height that you want to set postfixed with '%', 'px' or nothing. * * @param {boolean} [skiplisteners] * skip the componentresize event trigger * * @return {number|string} * the width when getting, zero if there is no width. can be a string * postpixed with '%' or 'px'. */ ; _proto.height = function height(num, skiplisteners) { return this.dimension('height', num, skiplisteners); } /** * set both the width and height of the `component` element at the same time. * * @param {number|string} width * width to set the `component`s element to. * * @param {number|string} height * height to set the `component`s element to. */ ; _proto.dimensions = function dimensions(width, height) { // skip componentresize listeners on width for optimization this.width(width, true); this.height(height); } /** * get or set width or height of the `component` element. this is the shared code * for the {@link component#width} and {@link component#height}. * * things to know: * - if the width or height in an number this will return the number postfixed with 'px'. * - if the width/height is a percent this will return the percent postfixed with '%' * - hidden elements have a width of 0 with `window.getcomputedstyle`. this function * defaults to the `component`s `style.width` and falls back to `window.getcomputedstyle`. * see [this]{@link http://www.foliotek.com/devblog/getting-the-width-of-a-hidden-element-with-jquery-using-width/} * for more information * - if you want the computed style of the component, use {@link component#currentwidth} * and {@link {component#currentheight} * * @fires component#componentresize * * @param {string} widthorheight 8 'width' or 'height' * * @param {number|string} [num] 8 new dimension * * @param {boolean} [skiplisteners] * skip componentresize event trigger * * @return {number} * the dimension when getting or 0 if unset */ ; _proto.dimension = function dimension(widthorheight, num, skiplisteners) { if (num !== undefined) { // set to zero if null or literally nan (nan !== nan) if (num === null || num !== num) { num = 0; } // check if using css width/height (% or px) and adjust if (('' + num).indexof('%') !== -1 || ('' + num).indexof('px') !== -1) { this.el_.style[widthorheight] = num; } else if (num === 'auto') { this.el_.style[widthorheight] = ''; } else { this.el_.style[widthorheight] = num + 'px'; } // skiplisteners allows us to avoid triggering the resize event when setting both width and height if (!skiplisteners) { /** * triggered when a component is resized. * * @event component#componentresize * @type {eventtarget~event} */ this.trigger('componentresize'); } return; } // not setting a value, so getting it // make sure element exists if (!this.el_) { return 0; } // get dimension value from style var val = this.el_.style[widthorheight]; var pxindex = val.indexof('px'); if (pxindex !== -1) { // return the pixel value with no 'px' return parseint(val.slice(0, pxindex), 10); } // no px so using % or no style was set, so falling back to offsetwidth/height // if component has display:none, offset will return 0 // todo: handle display:none and no dimension style using px return parseint(this.el_['offset' + totitlecase$1(widthorheight)], 10); } /** * get the computed width or the height of the component's element. * * uses `window.getcomputedstyle`. * * @param {string} widthorheight * a string containing 'width' or 'height'. whichever one you want to get. * * @return {number} * the dimension that gets asked for or 0 if nothing was set * for that dimension. */ ; _proto.currentdimension = function currentdimension(widthorheight) { var computedwidthorheight = 0; if (widthorheight !== 'width' && widthorheight !== 'height') { throw new error('currentdimension only accepts width or height value'); } computedwidthorheight = computedstyle(this.el_, widthorheight); // remove 'px' from variable and parse as integer computedwidthorheight = parsefloat(computedwidthorheight); // if the computed value is still 0, it's possible that the browser is lying // and we want to check the offset values. // this code also runs wherever getcomputedstyle doesn't exist. if (computedwidthorheight === 0 || isnan(computedwidthorheight)) { var rule = "offset" + totitlecase$1(widthorheight); computedwidthorheight = this.el_[rule]; } return computedwidthorheight; } /** * an object that contains width and height values of the `component`s * computed style. uses `window.getcomputedstyle`. * * @typedef {object} component~dimensionobject * * @property {number} width * the width of the `component`s computed style. * * @property {number} height * the height of the `component`s computed style. */ /** * get an object that contains computed width and height values of the * component's element. * * uses `window.getcomputedstyle`. * * @return {component~dimensionobject} * the computed dimensions of the component's element. */ ; _proto.currentdimensions = function currentdimensions() { return { width: this.currentdimension('width'), height: this.currentdimension('height') }; } /** * get the computed width of the component's element. * * uses `window.getcomputedstyle`. * * @return {number} * the computed width of the component's element. */ ; _proto.currentwidth = function currentwidth() { return this.currentdimension('width'); } /** * get the computed height of the component's element. * * uses `window.getcomputedstyle`. * * @return {number} * the computed height of the component's element. */ ; _proto.currentheight = function currentheight() { return this.currentdimension('height'); } /** * set the focus to this component */ ; _proto.focus = function focus() { this.el_.focus(); } /** * remove the focus from this component */ ; _proto.blur = function blur() { this.el_.blur(); } /** * when this component receives a `keydown` event which it does not process, * it passes the event to the player for handling. * * @param {eventtarget~event} event * the `keydown` event that caused this function to be called. */ ; _proto.handlekeydown = function handlekeydown(event) { if (this.player_) { // we only stop propagation here because we want unhandled events to fall // back to the browser. exclude tab for focus trapping. if (!keycode__webpack_imported_module_3___default.a.iseventkey(event, 'tab')) { event.stoppropagation(); } this.player_.handlekeydown(event); } } /** * many components used to have a `handlekeypress` method, which was poorly * named because it listened to a `keydown` event. this method name now * delegates to `handlekeydown`. this means anyone calling `handlekeypress` * will not see their method calls stop working. * * @param {eventtarget~event} event * the event that caused this function to be called. */ ; _proto.handlekeypress = function handlekeypress(event) { this.handlekeydown(event); } /** * emit a 'tap' events when touch event support gets detected. this gets used to * support toggling the controls through a tap on the video. they get enabled * because every sub-component would have extra overhead otherwise. * * @private * @fires component#tap * @listens component#touchstart * @listens component#touchmove * @listens component#touchleave * @listens component#touchcancel * @listens component#touchend */ ; _proto.emittapevents = function emittapevents() { // track the start time so we can determine how long the touch lasted var touchstart = 0; var firsttouch = null; // maximum movement allowed during a touch event to still be considered a tap // other popular libs use anywhere from 2 (hammer.js) to 15, // so 10 seems like a nice, round number. var tapmovementthreshold = 10; // the maximum length a touch can be while still being considered a tap var touchtimethreshold = 200; var couldbetap; this.on('touchstart', function (event) { // if more than one finger, don't consider treating this as a click if (event.touches.length === 1) { // copy pagex/pagey from the object firsttouch = { pagex: event.touches[0].pagex, pagey: event.touches[0].pagey }; // record start time so we can detect a tap vs. "touch and hold" touchstart = global_window__webpack_imported_module_0___default.a.performance.now(); // reset couldbetap tracking couldbetap = true; } }); this.on('touchmove', function (event) { // if more than one finger, don't consider treating this as a click if (event.touches.length > 1) { couldbetap = false; } else if (firsttouch) { // some devices will throw touchmoves for all but the slightest of taps. // so, if we moved only a small distance, this could still be a tap var xdiff = event.touches[0].pagex - firsttouch.pagex; var ydiff = event.touches[0].pagey - firsttouch.pagey; var touchdistance = math.sqrt(xdiff * xdiff + ydiff * ydiff); if (touchdistance > tapmovementthreshold) { couldbetap = false; } } }); var notap = function notap() { couldbetap = false; }; // todo: listen to the original target. http://youtu.be/dujfpxokup8?t=13m8s this.on('touchleave', notap); this.on('touchcancel', notap); // when the touch ends, measure how long it took and trigger the appropriate // event this.on('touchend', function (event) { firsttouch = null; // proceed only if the touchmove/leave/cancel event didn't happen if (couldbetap === true) { // measure how long the touch lasted var touchtime = global_window__webpack_imported_module_0___default.a.performance.now() - touchstart; // make sure the touch was less than the threshold to be considered a tap if (touchtime < touchtimethreshold) { // don't let browser turn this into a click event.preventdefault(); /** * triggered when a `component` is tapped. * * @event component#tap * @type {eventtarget~event} */ this.trigger('tap'); // it may be good to copy the touchend event object and change the // type to tap, if the other event properties aren't exact after // events.fixevent runs (e.g. event.target) } } }); } /** * this function reports user activity whenever touch events happen. this can get * turned off by any sub-components that wants touch events to act another way. * * report user touch activity when touch events occur. user activity gets used to * determine when controls should show/hide. it is simple when it comes to mouse * events, because any mouse event should show the controls. so we capture mouse * events that bubble up to the player and report activity when that happens. * with touch events it isn't as easy as `touchstart` and `touchend` toggle player * controls. so touch events can't help us at the player level either. * * user activity gets checked asynchronously. so what could happen is a tap event * on the video turns the controls off. then the `touchend` event bubbles up to * the player. which, if it reported user activity, would turn the controls right * back on. we also don't want to completely block touch events from bubbling up. * furthermore a `touchmove` event and anything other than a tap, should not turn * controls back on. * * @listens component#touchstart * @listens component#touchmove * @listens component#touchend * @listens component#touchcancel */ ; _proto.enabletouchactivity = function enabletouchactivity() { // don't continue if the root player doesn't support reporting user activity if (!this.player() || !this.player().reportuseractivity) { return; } // listener for reporting that the user is active var report = bind(this.player(), this.player().reportuseractivity); var touchholding; this.on('touchstart', function () { report(); // for as long as the they are touching the device or have their mouse down, // we consider them active even if they're not moving their finger or mouse. // so we want to continue to update that they are active this.clearinterval(touchholding); // report at the same interval as activitycheck touchholding = this.setinterval(report, 250); }); var touchend = function touchend(event) { report(); // stop the interval that maintains activity if the touch is holding this.clearinterval(touchholding); }; this.on('touchmove', report); this.on('touchend', touchend); this.on('touchcancel', touchend); } /** * a callback that has no parameters and is bound into `component`s context. * * @callback component~genericcallback * @this component */ /** * creates a function that runs after an `x` millisecond timeout. this function is a * wrapper around `window.settimeout`. there are a few reasons to use this one * instead though: * 1. it gets cleared via {@link component#cleartimeout} when * {@link component#dispose} gets called. * 2. the function callback will gets turned into a {@link component~genericcallback} * * > note: you can't use `window.cleartimeout` on the id returned by this function. this * will cause its dispose listener not to get cleaned up! please use * {@link component#cleartimeout} or {@link component#dispose} instead. * * @param {component~genericcallback} fn * the function that will be run after `timeout`. * * @param {number} timeout * timeout in milliseconds to delay before executing the specified function. * * @return {number} * returns a timeout id that gets used to identify the timeout. it can also * get used in {@link component#cleartimeout} to clear the timeout that * was set. * * @listens component#dispose * @see [similar to]{@link https://developer.mozilla.org/en-us/docs/web/api/windowtimers/settimeout} */ ; _proto.settimeout = function settimeout(fn, timeout) { var _this3 = this; // declare as variables so they are properly available in timeout function // eslint-disable-next-line var timeoutid; fn = bind(this, fn); this.cleartimersondispose_(); timeoutid = global_window__webpack_imported_module_0___default.a.settimeout(function () { if (_this3.settimeoutids_.has(timeoutid)) { _this3.settimeoutids_["delete"](timeoutid); } fn(); }, timeout); this.settimeoutids_.add(timeoutid); return timeoutid; } /** * clears a timeout that gets created via `window.settimeout` or * {@link component#settimeout}. if you set a timeout via {@link component#settimeout} * use this function instead of `window.cleartimout`. if you don't your dispose * listener will not get cleaned up until {@link component#dispose}! * * @param {number} timeoutid * the id of the timeout to clear. the return value of * {@link component#settimeout} or `window.settimeout`. * * @return {number} * returns the timeout id that was cleared. * * @see [similar to]{@link https://developer.mozilla.org/en-us/docs/web/api/windowtimers/cleartimeout} */ ; _proto.cleartimeout = function cleartimeout(timeoutid) { if (this.settimeoutids_.has(timeoutid)) { this.settimeoutids_["delete"](timeoutid); global_window__webpack_imported_module_0___default.a.cleartimeout(timeoutid); } return timeoutid; } /** * creates a function that gets run every `x` milliseconds. this function is a wrapper * around `window.setinterval`. there are a few reasons to use this one instead though. * 1. it gets cleared via {@link component#clearinterval} when * {@link component#dispose} gets called. * 2. the function callback will be a {@link component~genericcallback} * * @param {component~genericcallback} fn * the function to run every `x` seconds. * * @param {number} interval * execute the specified function every `x` milliseconds. * * @return {number} * returns an id that can be used to identify the interval. it can also be be used in * {@link component#clearinterval} to clear the interval. * * @listens component#dispose * @see [similar to]{@link https://developer.mozilla.org/en-us/docs/web/api/windowtimers/setinterval} */ ; _proto.setinterval = function setinterval(fn, interval) { fn = bind(this, fn); this.cleartimersondispose_(); var intervalid = global_window__webpack_imported_module_0___default.a.setinterval(fn, interval); this.setintervalids_.add(intervalid); return intervalid; } /** * clears an interval that gets created via `window.setinterval` or * {@link component#setinterval}. if you set an inteval via {@link component#setinterval} * use this function instead of `window.clearinterval`. if you don't your dispose * listener will not get cleaned up until {@link component#dispose}! * * @param {number} intervalid * the id of the interval to clear. the return value of * {@link component#setinterval} or `window.setinterval`. * * @return {number} * returns the interval id that was cleared. * * @see [similar to]{@link https://developer.mozilla.org/en-us/docs/web/api/windowtimers/clearinterval} */ ; _proto.clearinterval = function clearinterval(intervalid) { if (this.setintervalids_.has(intervalid)) { this.setintervalids_["delete"](intervalid); global_window__webpack_imported_module_0___default.a.clearinterval(intervalid); } return intervalid; } /** * queues up a callback to be passed to requestanimationframe (raf), but * with a few extra bonuses: * * - supports browsers that do not support raf by falling back to * {@link component#settimeout}. * * - the callback is turned into a {@link component~genericcallback} (i.e. * bound to the component). * * - automatic cancellation of the raf callback is handled if the component * is disposed before it is called. * * @param {component~genericcallback} fn * a function that will be bound to this component and executed just * before the browser's next repaint. * * @return {number} * returns an raf id that gets used to identify the timeout. it can * also be used in {@link component#cancelanimationframe} to cancel * the animation frame callback. * * @listens component#dispose * @see [similar to]{@link https://developer.mozilla.org/en-us/docs/web/api/window/requestanimationframe} */ ; _proto.requestanimationframe = function requestanimationframe(fn) { var _this4 = this; // fall back to using a timer. if (!this.supportsraf_) { return this.settimeout(fn, 1000 / 60); } this.cleartimersondispose_(); // declare as variables so they are properly available in raf function // eslint-disable-next-line var id; fn = bind(this, fn); id = global_window__webpack_imported_module_0___default.a.requestanimationframe(function () { if (_this4.rafids_.has(id)) { _this4.rafids_["delete"](id); } fn(); }); this.rafids_.add(id); return id; } /** * request an animation frame, but only one named animation * frame will be queued. another will never be added until * the previous one finishes. * * @param {string} name * the name to give this requestanimationframe * * @param {component~genericcallback} fn * a function that will be bound to this component and executed just * before the browser's next repaint. */ ; _proto.requestnamedanimationframe = function requestnamedanimationframe(name, fn) { var _this5 = this; if (this.namedrafs_.has(name)) { return; } this.cleartimersondispose_(); fn = bind(this, fn); var id = this.requestanimationframe(function () { fn(); if (_this5.namedrafs_.has(name)) { _this5.namedrafs_["delete"](name); } }); this.namedrafs_.set(name, id); return name; } /** * cancels a current named animation frame if it exists. * * @param {string} name * the name of the requestanimationframe to cancel. */ ; _proto.cancelnamedanimationframe = function cancelnamedanimationframe(name) { if (!this.namedrafs_.has(name)) { return; } this.cancelanimationframe(this.namedrafs_.get(name)); this.namedrafs_["delete"](name); } /** * cancels a queued callback passed to {@link component#requestanimationframe} * (raf). * * if you queue an raf callback via {@link component#requestanimationframe}, * use this function instead of `window.cancelanimationframe`. if you don't, * your dispose listener will not get cleaned up until {@link component#dispose}! * * @param {number} id * the raf id to clear. the return value of {@link component#requestanimationframe}. * * @return {number} * returns the raf id that was cleared. * * @see [similar to]{@link https://developer.mozilla.org/en-us/docs/web/api/window/cancelanimationframe} */ ; _proto.cancelanimationframe = function cancelanimationframe(id) { // fall back to using a timer. if (!this.supportsraf_) { return this.cleartimeout(id); } if (this.rafids_.has(id)) { this.rafids_["delete"](id); global_window__webpack_imported_module_0___default.a.cancelanimationframe(id); } return id; } /** * a function to setup `requestanimationframe`, `settimeout`, * and `setinterval`, clearing on dispose. * * > previously each timer added and removed dispose listeners on it's own. * for better performance it was decided to batch them all, and use `set`s * to track outstanding timer ids. * * @private */ ; _proto.cleartimersondispose_ = function cleartimersondispose_() { var _this6 = this; if (this.clearingtimersondispose_) { return; } this.clearingtimersondispose_ = true; this.one('dispose', function () { [['namedrafs_', 'cancelnamedanimationframe'], ['rafids_', 'cancelanimationframe'], ['settimeoutids_', 'cleartimeout'], ['setintervalids_', 'clearinterval']].foreach(function (_ref) { var idname = _ref[0], cancelname = _ref[1]; // for a `set` key will actually be the value again // so foreach((val, val) =>` but for maps we want to use // the key. _this6[idname].foreach(function (val, key) { return _this6[cancelname](key); }); }); _this6.clearingtimersondispose_ = false; }); } /** * register a `component` with `videojs` given the name and the component. * * > note: {@link tech}s should not be registered as a `component`. {@link tech}s * should be registered using {@link tech.registertech} or * {@link videojs:videojs.registertech}. * * > note: this function can also be seen on videojs as * {@link videojs:videojs.registercomponent}. * * @param {string} name * the name of the `component` to register. * * @param {component} componenttoregister * the `component` class to register. * * @return {component} * the `component` that was registered. */ ; component.registercomponent = function registercomponent(name, componenttoregister) { if (typeof name !== 'string' || !name) { throw new error("illegal component name, \"" + name + "\"; must be a non-empty string."); } var tech = component.getcomponent('tech'); // we need to make sure this check is only done if tech has been registered. var istech = tech && tech.istech(componenttoregister); var iscomp = component === componenttoregister || component.prototype.isprototypeof(componenttoregister.prototype); if (istech || !iscomp) { var reason; if (istech) { reason = 'techs must be registered using tech.registertech()'; } else { reason = 'must be a component subclass'; } throw new error("illegal component, \"" + name + "\"; " + reason + "."); } name = totitlecase$1(name); if (!component.components_) { component.components_ = {}; } var player = component.getcomponent('player'); if (name === 'player' && player && player.players) { var players = player.players; var playernames = object.keys(players); // if we have players that were disposed, then their name will still be // in players.players. so, we must loop through and verify that the value // for each item is not null. this allows registration of the player component // after all players have been disposed or before any were created. if (players && playernames.length > 0 && playernames.map(function (pname) { return players[pname]; }).every(boolean)) { throw new error('can not register player component after player has been created.'); } } component.components_[name] = componenttoregister; component.components_[tolowercase(name)] = componenttoregister; return componenttoregister; } /** * get a `component` based on the name it was registered with. * * @param {string} name * the name of the component to get. * * @return {component} * the `component` that got registered under the given name. */ ; component.getcomponent = function getcomponent(name) { if (!name || !component.components_) { return; } return component.components_[name]; }; return component; }(); /** * whether or not this component supports `requestanimationframe`. * * this is exposed primarily for testing purposes. * * @private * @type {boolean} */ component$1.prototype.supportsraf_ = typeof global_window__webpack_imported_module_0___default.a.requestanimationframe === 'function' && typeof global_window__webpack_imported_module_0___default.a.cancelanimationframe === 'function'; component$1.registercomponent('component', component$1); /** * @file time-ranges.js * @module time-ranges */ /** * returns the time for the specified index at the start or end * of a timerange object. * * @typedef {function} timerangeindex * * @param {number} [index=0] * the range number to return the time for. * * @return {number} * the time offset at the specified index. * * @deprecated the index argument must be provided. * in the future, leaving it out will throw an error. */ /** * an object that contains ranges of time. * * @typedef {object} timerange * * @property {number} length * the number of time ranges represented by this object. * * @property {module:time-ranges~timerangeindex} start * returns the time offset at which a specified time range begins. * * @property {module:time-ranges~timerangeindex} end * returns the time offset at which a specified time range ends. * * @see https://developer.mozilla.org/en-us/docs/web/api/timeranges */ /** * check if any of the time ranges are over the maximum index. * * @private * @param {string} fnname * the function name to use for logging * * @param {number} index * the index to check * * @param {number} maxindex * the maximum possible index * * @throws {error} if the timeranges provided are over the maxindex */ function rangecheck(fnname, index, maxindex) { if (typeof index !== 'number' || index < 0 || index > maxindex) { throw new error("failed to execute '" + fnname + "' on 'timeranges': the index provided (" + index + ") is non-numeric or out of bounds (0-" + maxindex + ")."); } } /** * get the time for the specified index at the start or end * of a timerange object. * * @private * @param {string} fnname * the function name to use for logging * * @param {string} valueindex * the property that should be used to get the time. should be * 'start' or 'end' * * @param {array} ranges * an array of time ranges * * @param {array} [rangeindex=0] * the index to start the search at * * @return {number} * the time that offset at the specified index. * * @deprecated rangeindex must be set to a value, in the future this will throw an error. * @throws {error} if rangeindex is more than the length of ranges */ function getrange(fnname, valueindex, ranges, rangeindex) { rangecheck(fnname, rangeindex, ranges.length - 1); return ranges[rangeindex][valueindex]; } /** * create a time range object given ranges of time. * * @private * @param {array} [ranges] * an array of time ranges. */ function createtimerangesobj(ranges) { var timerangesobj; if (ranges === undefined || ranges.length === 0) { timerangesobj = { length: 0, start: function start() { throw new error('this timeranges object is empty'); }, end: function end() { throw new error('this timeranges object is empty'); } }; } else { timerangesobj = { length: ranges.length, start: getrange.bind(null, 'start', 0, ranges), end: getrange.bind(null, 'end', 1, ranges) }; } if (global_window__webpack_imported_module_0___default.a.symbol && global_window__webpack_imported_module_0___default.a.symbol.iterator) { timerangesobj[global_window__webpack_imported_module_0___default.a.symbol.iterator] = function () { return (ranges || []).values(); }; } return timerangesobj; } /** * create a `timerange` object which mimics an * {@link https://developer.mozilla.org/en-us/docs/web/api/timeranges|html5 timeranges instance}. * * @param {number|array[]} start * the start of a single range (a number) or an array of ranges (an * array of arrays of two numbers each). * * @param {number} end * the end of a single range. cannot be used with the array form of * the `start` argument. */ function createtimeranges(start, end) { if (array.isarray(start)) { return createtimerangesobj(start); } else if (start === undefined || end === undefined) { return createtimerangesobj(); } return createtimerangesobj([[start, end]]); } /** * @file buffer.js * @module buffer */ /** * compute the percentage of the media that has been buffered. * * @param {timerange} buffered * the current `timerange` object representing buffered time ranges * * @param {number} duration * total duration of the media * * @return {number} * percent buffered of the total duration in decimal form. */ function bufferedpercent(buffered, duration) { var bufferedduration = 0; var start; var end; if (!duration) { return 0; } if (!buffered || !buffered.length) { buffered = createtimeranges(0, 0); } for (var i = 0; i < buffered.length; i++) { start = buffered.start(i); end = buffered.end(i); // buffered end can be bigger than duration by a very small fraction if (end > duration) { end = duration; } bufferedduration += end - start; } return bufferedduration / duration; } /** * @file media-error.js */ /** * a custom `mediaerror` class which mimics the standard html5 `mediaerror` class. * * @param {number|string|object|mediaerror} value * this can be of multiple types: * - number: should be a standard error code * - string: an error message (the code will be 0) * - object: arbitrary properties * - `mediaerror` (native): used to populate a video.js `mediaerror` object * - `mediaerror` (video.js): will return itself if it's already a * video.js `mediaerror` object. * * @see [mediaerror spec]{@link https://dev.w3.org/html5/spec-author-view/video.html#mediaerror} * @see [encrypted mediaerror spec]{@link https://www.w3.org/tr/2013/wd-encrypted-media-20130510/#error-codes} * * @class mediaerror */ function mediaerror(value) { // allow redundant calls to this constructor to avoid having `instanceof` // checks peppered around the code. if (value instanceof mediaerror) { return value; } if (typeof value === 'number') { this.code = value; } else if (typeof value === 'string') { // default code is zero, so this is a custom error this.message = value; } else if (isobject(value)) { // we assign the `code` property manually because native `mediaerror` objects // do not expose it as an own/enumerable property of the object. if (typeof value.code === 'number') { this.code = value.code; } assign(this, value); } if (!this.message) { this.message = mediaerror.defaultmessages[this.code] || ''; } } /** * the error code that refers two one of the defined `mediaerror` types * * @type {number} */ mediaerror.prototype.code = 0; /** * an optional message that to show with the error. message is not part of the html5 * video spec but allows for more informative custom errors. * * @type {string} */ mediaerror.prototype.message = ''; /** * an optional status code that can be set by plugins to allow even more detail about * the error. for example a plugin might provide a specific http status code and an * error message for that code. then when the plugin gets that error this class will * know how to display an error message for it. this allows a custom message to show * up on the `player` error overlay. * * @type {array} */ mediaerror.prototype.status = null; /** * errors indexed by the w3c standard. the order **cannot change**! see the * specification listed under {@link mediaerror} for more information. * * @enum {array} * @readonly * @property {string} 0 - media_err_custom * @property {string} 1 - media_err_aborted * @property {string} 2 - media_err_network * @property {string} 3 - media_err_decode * @property {string} 4 - media_err_src_not_supported * @property {string} 5 - media_err_encrypted */ mediaerror.errortypes = ['media_err_custom', 'media_err_aborted', 'media_err_network', 'media_err_decode', 'media_err_src_not_supported', 'media_err_encrypted']; /** * the default `mediaerror` messages based on the {@link mediaerror.errortypes}. * * @type {array} * @constant */ mediaerror.defaultmessages = { 1: 'you aborted the media playback', 2: 'a network error caused the media download to fail part-way.', 3: 'the media playback was aborted due to a corruption problem or because the media used features your browser did not support.', 4: 'the media could not be loaded, either because the server or network failed or because the format is not supported.', 5: 'the media is encrypted and we do not have the keys to decrypt it.' }; // add types as properties on mediaerror // e.g. mediaerror.media_err_src_not_supported = 4; for (var errnum = 0; errnum < mediaerror.errortypes.length; errnum++) { mediaerror[mediaerror.errortypes[errnum]] = errnum; // values should be accessible on both the class and instance mediaerror.prototype[mediaerror.errortypes[errnum]] = errnum; } // jsdocs for instance/static members added above /** * returns whether an object is `promise`-like (i.e. has a `then` method). * * @param {object} value * an object that may or may not be `promise`-like. * * @return {boolean} * whether or not the object is `promise`-like. */ function ispromise(value) { return value !== undefined && value !== null && typeof value.then === 'function'; } /** * silence a promise-like object. * * this is useful for avoiding non-harmful, but potentially confusing "uncaught * play promise" rejection error messages. * * @param {object} value * an object that may or may not be `promise`-like. */ function silencepromise(value) { if (ispromise(value)) { value.then(null, function (e) {}); } } /** * @file text-track-list-converter.js utilities for capturing text track state and * re-creating tracks based on a capture. * * @module text-track-list-converter */ /** * examine a single {@link texttrack} and return a json-compatible javascript object that * represents the {@link texttrack}'s state. * * @param {texttrack} track * the text track to query. * * @return {object} * a serializable javascript representation of the texttrack. * @private */ var tracktojson_ = function tracktojson_(track) { var ret = ['kind', 'label', 'language', 'id', 'inbandmetadatatrackdispatchtype', 'mode', 'src'].reduce(function (acc, prop, i) { if (track[prop]) { acc[prop] = track[prop]; } return acc; }, { cues: track.cues && array.prototype.map.call(track.cues, function (cue) { return { starttime: cue.starttime, endtime: cue.endtime, text: cue.text, id: cue.id }; }) }); return ret; }; /** * examine a {@link tech} and return a json-compatible javascript array that represents the * state of all {@link texttrack}s currently configured. the return array is compatible with * {@link text-track-list-converter:jsontotexttracks}. * * @param {tech} tech * the tech object to query * * @return {array} * a serializable javascript representation of the {@link tech}s * {@link texttracklist}. */ var texttrackstojson = function texttrackstojson(tech) { var trackels = tech.$$('track'); var trackobjs = array.prototype.map.call(trackels, function (t) { return t.track; }); var tracks = array.prototype.map.call(trackels, function (trackel) { var json = tracktojson_(trackel.track); if (trackel.src) { json.src = trackel.src; } return json; }); return tracks.concat(array.prototype.filter.call(tech.texttracks(), function (track) { return trackobjs.indexof(track) === -1; }).map(tracktojson_)); }; /** * create a set of remote {@link texttrack}s on a {@link tech} based on an array of javascript * object {@link texttrack} representations. * * @param {array} json * an array of `texttrack` representation objects, like those that would be * produced by `texttrackstojson`. * * @param {tech} tech * the `tech` to create the `texttrack`s on. */ var jsontotexttracks = function jsontotexttracks(json, tech) { json.foreach(function (track) { var addedtrack = tech.addremotetexttrack(track).track; if (!track.src && track.cues) { track.cues.foreach(function (cue) { return addedtrack.addcue(cue); }); } }); return tech.texttracks(); }; var texttrackconverter = { texttrackstojson: texttrackstojson, jsontotexttracks: jsontotexttracks, tracktojson_: tracktojson_ }; var modal_class_name = 'vjs-modal-dialog'; /** * the `modaldialog` displays over the video and its controls, which blocks * interaction with the player until it is closed. * * modal dialogs include a "close" button and will close when that button * is activated - or when esc is pressed anywhere. * * @extends component */ var modaldialog = /*#__pure__*/function (_component) { _babel_runtime_helpers_inheritsloose__webpack_imported_module_5___default()(modaldialog, _component); /** * create an instance of this class. * * @param {player} player * the `player` that this class should be attached to. * * @param {object} [options] * the key/value store of player options. * * @param {mixed} [options.content=undefined] * provide customized content for this modal. * * @param {string} [options.description] * a text description for the modal, primarily for accessibility. * * @param {boolean} [options.fillalways=false] * normally, modals are automatically filled only the first time * they open. this tells the modal to refresh its content * every time it opens. * * @param {string} [options.label] * a text label for the modal, primarily for accessibility. * * @param {boolean} [options.pauseonopen=true] * if `true`, playback will will be paused if playing when * the modal opens, and resumed when it closes. * * @param {boolean} [options.temporary=true] * if `true`, the modal can only be opened once; it will be * disposed as soon as it's closed. * * @param {boolean} [options.uncloseable=false] * if `true`, the user will not be able to close the modal * through the ui in the normal ways. programmatic closing is * still possible. */ function modaldialog(player, options) { var _this; _this = _component.call(this, player, options) || this; _this.handlekeydown_ = function (e) { return _this.handlekeydown(e); }; _this.close_ = function (e) { return _this.close(e); }; _this.opened_ = _this.hasbeenopened_ = _this.hasbeenfilled_ = false; _this.closeable(!_this.options_.uncloseable); _this.content(_this.options_.content); // make sure the contentel is defined after any children are initialized // because we only want the contents of the modal in the contentel // (not the ui elements like the close button). _this.contentel_ = createel('div', { classname: modal_class_name + "-content" }, { role: 'document' }); _this.descel_ = createel('p', { classname: modal_class_name + "-description vjs-control-text", id: _this.el().getattribute('aria-describedby') }); textcontent(_this.descel_, _this.description()); _this.el_.appendchild(_this.descel_); _this.el_.appendchild(_this.contentel_); return _this; } /** * create the `modaldialog`'s dom element * * @return {element} * the dom element that gets created. */ var _proto = modaldialog.prototype; _proto.createel = function createel() { return _component.prototype.createel.call(this, 'div', { classname: this.buildcssclass(), tabindex: -1 }, { 'aria-describedby': this.id() + "_description", 'aria-hidden': 'true', 'aria-label': this.label(), 'role': 'dialog' }); }; _proto.dispose = function dispose() { this.contentel_ = null; this.descel_ = null; this.previouslyactiveel_ = null; _component.prototype.dispose.call(this); } /** * builds the default dom `classname`. * * @return {string} * the dom `classname` for this object. */ ; _proto.buildcssclass = function buildcssclass() { return modal_class_name + " vjs-hidden " + _component.prototype.buildcssclass.call(this); } /** * returns the label string for this modal. primarily used for accessibility. * * @return {string} * the localized or raw label of this modal. */ ; _proto.label = function label() { return this.localize(this.options_.label || 'modal window'); } /** * returns the description string for this modal. primarily used for * accessibility. * * @return {string} * the localized or raw description of this modal. */ ; _proto.description = function description() { var desc = this.options_.description || this.localize('this is a modal window.'); // append a universal closeability message if the modal is closeable. if (this.closeable()) { desc += ' ' + this.localize('this modal can be closed by pressing the escape key or activating the close button.'); } return desc; } /** * opens the modal. * * @fires modaldialog#beforemodalopen * @fires modaldialog#modalopen */ ; _proto.open = function open() { if (!this.opened_) { var player = this.player(); /** * fired just before a `modaldialog` is opened. * * @event modaldialog#beforemodalopen * @type {eventtarget~event} */ this.trigger('beforemodalopen'); this.opened_ = true; // fill content if the modal has never opened before and // never been filled. if (this.options_.fillalways || !this.hasbeenopened_ && !this.hasbeenfilled_) { this.fill(); } // if the player was playing, pause it and take note of its previously // playing state. this.wasplaying_ = !player.paused(); if (this.options_.pauseonopen && this.wasplaying_) { player.pause(); } this.on('keydown', this.handlekeydown_); // hide controls and note if they were enabled. this.hadcontrols_ = player.controls(); player.controls(false); this.show(); this.conditionalfocus_(); this.el().setattribute('aria-hidden', 'false'); /** * fired just after a `modaldialog` is opened. * * @event modaldialog#modalopen * @type {eventtarget~event} */ this.trigger('modalopen'); this.hasbeenopened_ = true; } } /** * if the `modaldialog` is currently open or closed. * * @param {boolean} [value] * if given, it will open (`true`) or close (`false`) the modal. * * @return {boolean} * the current open state of the modaldialog */ ; _proto.opened = function opened(value) { if (typeof value === 'boolean') { this[value ? 'open' : 'close'](); } return this.opened_; } /** * closes the modal, does nothing if the `modaldialog` is * not open. * * @fires modaldialog#beforemodalclose * @fires modaldialog#modalclose */ ; _proto.close = function close() { if (!this.opened_) { return; } var player = this.player(); /** * fired just before a `modaldialog` is closed. * * @event modaldialog#beforemodalclose * @type {eventtarget~event} */ this.trigger('beforemodalclose'); this.opened_ = false; if (this.wasplaying_ && this.options_.pauseonopen) { player.play(); } this.off('keydown', this.handlekeydown_); if (this.hadcontrols_) { player.controls(true); } this.hide(); this.el().setattribute('aria-hidden', 'true'); /** * fired just after a `modaldialog` is closed. * * @event modaldialog#modalclose * @type {eventtarget~event} */ this.trigger('modalclose'); this.conditionalblur_(); if (this.options_.temporary) { this.dispose(); } } /** * check to see if the `modaldialog` is closeable via the ui. * * @param {boolean} [value] * if given as a boolean, it will set the `closeable` option. * * @return {boolean} * returns the final value of the closable option. */ ; _proto.closeable = function closeable(value) { if (typeof value === 'boolean') { var closeable = this.closeable_ = !!value; var close = this.getchild('closebutton'); // if this is being made closeable and has no close button, add one. if (closeable && !close) { // the close button should be a child of the modal - not its // content element, so temporarily change the content element. var temp = this.contentel_; this.contentel_ = this.el_; close = this.addchild('closebutton', { controltext: 'close modal dialog' }); this.contentel_ = temp; this.on(close, 'close', this.close_); } // if this is being made uncloseable and has a close button, remove it. if (!closeable && close) { this.off(close, 'close', this.close_); this.removechild(close); close.dispose(); } } return this.closeable_; } /** * fill the modal's content element with the modal's "content" option. * the content element will be emptied before this change takes place. */ ; _proto.fill = function fill() { this.fillwith(this.content()); } /** * fill the modal's content element with arbitrary content. * the content element will be emptied before this change takes place. * * @fires modaldialog#beforemodalfill * @fires modaldialog#modalfill * * @param {mixed} [content] * the same rules apply to this as apply to the `content` option. */ ; _proto.fillwith = function fillwith(content) { var contentel = this.contentel(); var parentel = contentel.parentnode; var nextsiblingel = contentel.nextsibling; /** * fired just before a `modaldialog` is filled with content. * * @event modaldialog#beforemodalfill * @type {eventtarget~event} */ this.trigger('beforemodalfill'); this.hasbeenfilled_ = true; // detach the content element from the dom before performing // manipulation to avoid modifying the live dom multiple times. parentel.removechild(contentel); this.empty(); insertcontent(contentel, content); /** * fired just after a `modaldialog` is filled with content. * * @event modaldialog#modalfill * @type {eventtarget~event} */ this.trigger('modalfill'); // re-inject the re-filled content element. if (nextsiblingel) { parentel.insertbefore(contentel, nextsiblingel); } else { parentel.appendchild(contentel); } // make sure that the close button is last in the dialog dom var closebutton = this.getchild('closebutton'); if (closebutton) { parentel.appendchild(closebutton.el_); } } /** * empties the content element. this happens anytime the modal is filled. * * @fires modaldialog#beforemodalempty * @fires modaldialog#modalempty */ ; _proto.empty = function empty() { /** * fired just before a `modaldialog` is emptied. * * @event modaldialog#beforemodalempty * @type {eventtarget~event} */ this.trigger('beforemodalempty'); emptyel(this.contentel()); /** * fired just after a `modaldialog` is emptied. * * @event modaldialog#modalempty * @type {eventtarget~event} */ this.trigger('modalempty'); } /** * gets or sets the modal content, which gets normalized before being * rendered into the dom. * * this does not update the dom or fill the modal, but it is called during * that process. * * @param {mixed} [value] * if defined, sets the internal content value to be used on the * next call(s) to `fill`. this value is normalized before being * inserted. to "clear" the internal content value, pass `null`. * * @return {mixed} * the current content of the modal dialog */ ; _proto.content = function content(value) { if (typeof value !== 'undefined') { this.content_ = value; } return this.content_; } /** * conditionally focus the modal dialog if focus was previously on the player. * * @private */ ; _proto.conditionalfocus_ = function conditionalfocus_() { var activeel = global_document__webpack_imported_module_1___default.a.activeelement; var playerel = this.player_.el_; this.previouslyactiveel_ = null; if (playerel.contains(activeel) || playerel === activeel) { this.previouslyactiveel_ = activeel; this.focus(); } } /** * conditionally blur the element and refocus the last focused element * * @private */ ; _proto.conditionalblur_ = function conditionalblur_() { if (this.previouslyactiveel_) { this.previouslyactiveel_.focus(); this.previouslyactiveel_ = null; } } /** * keydown handler. attached when modal is focused. * * @listens keydown */ ; _proto.handlekeydown = function handlekeydown(event) { // do not allow keydowns to reach out of the modal dialog. event.stoppropagation(); if (keycode__webpack_imported_module_3___default.a.iseventkey(event, 'escape') && this.closeable()) { event.preventdefault(); this.close(); return; } // exit early if it isn't a tab key if (!keycode__webpack_imported_module_3___default.a.iseventkey(event, 'tab')) { return; } var focusableels = this.focusableels_(); var activeel = this.el_.queryselector(':focus'); var focusindex; for (var i = 0; i < focusableels.length; i++) { if (activeel === focusableels[i]) { focusindex = i; break; } } if (global_document__webpack_imported_module_1___default.a.activeelement === this.el_) { focusindex = 0; } if (event.shiftkey && focusindex === 0) { focusableels[focusableels.length - 1].focus(); event.preventdefault(); } else if (!event.shiftkey && focusindex === focusableels.length - 1) { focusableels[0].focus(); event.preventdefault(); } } /** * get all focusable elements * * @private */ ; _proto.focusableels_ = function focusableels_() { var allchildren = this.el_.queryselectorall('*'); return array.prototype.filter.call(allchildren, function (child) { return (child instanceof global_window__webpack_imported_module_0___default.a.htmlanchorelement || child instanceof global_window__webpack_imported_module_0___default.a.htmlareaelement) && child.hasattribute('href') || (child instanceof global_window__webpack_imported_module_0___default.a.htmlinputelement || child instanceof global_window__webpack_imported_module_0___default.a.htmlselectelement || child instanceof global_window__webpack_imported_module_0___default.a.htmltextareaelement || child instanceof global_window__webpack_imported_module_0___default.a.htmlbuttonelement) && !child.hasattribute('disabled') || child instanceof global_window__webpack_imported_module_0___default.a.htmliframeelement || child instanceof global_window__webpack_imported_module_0___default.a.htmlobjectelement || child instanceof global_window__webpack_imported_module_0___default.a.htmlembedelement || child.hasattribute('tabindex') && child.getattribute('tabindex') !== -1 || child.hasattribute('contenteditable'); }); }; return modaldialog; }(component$1); /** * default options for `modaldialog` default options. * * @type {object} * @private */ modaldialog.prototype.options_ = { pauseonopen: true, temporary: true }; component$1.registercomponent('modaldialog', modaldialog); /** * common functionaliy between {@link texttracklist}, {@link audiotracklist}, and * {@link videotracklist} * * @extends eventtarget */ var tracklist = /*#__pure__*/function (_eventtarget) { _babel_runtime_helpers_inheritsloose__webpack_imported_module_5___default()(tracklist, _eventtarget); /** * create an instance of this class * * @param {track[]} tracks * a list of tracks to initialize the list with. * * @abstract */ function tracklist(tracks) { var _this; if (tracks === void 0) { tracks = []; } _this = _eventtarget.call(this) || this; _this.tracks_ = []; /** * @memberof tracklist * @member {number} length * the current number of `track`s in the this trackist. * @instance */ object.defineproperty(_babel_runtime_helpers_assertthisinitialized__webpack_imported_module_4___default()(_this), 'length', { get: function get() { return this.tracks_.length; } }); for (var i = 0; i < tracks.length; i++) { _this.addtrack(tracks[i]); } return _this; } /** * add a {@link track} to the `tracklist` * * @param {track} track * the audio, video, or text track to add to the list. * * @fires tracklist#addtrack */ var _proto = tracklist.prototype; _proto.addtrack = function addtrack(track) { var _this2 = this; var index = this.tracks_.length; if (!('' + index in this)) { object.defineproperty(this, index, { get: function get() { return this.tracks_[index]; } }); } // do not add duplicate tracks if (this.tracks_.indexof(track) === -1) { this.tracks_.push(track); /** * triggered when a track is added to a track list. * * @event tracklist#addtrack * @type {eventtarget~event} * @property {track} track * a reference to track that was added. */ this.trigger({ track: track, type: 'addtrack', target: this }); } /** * triggered when a track label is changed. * * @event tracklist#addtrack * @type {eventtarget~event} * @property {track} track * a reference to track that was added. */ track.labelchange_ = function () { _this2.trigger({ track: track, type: 'labelchange', target: _this2 }); }; if (isevented(track)) { track.addeventlistener('labelchange', track.labelchange_); } } /** * remove a {@link track} from the `tracklist` * * @param {track} rtrack * the audio, video, or text track to remove from the list. * * @fires tracklist#removetrack */ ; _proto.removetrack = function removetrack(rtrack) { var track; for (var i = 0, l = this.length; i < l; i++) { if (this[i] === rtrack) { track = this[i]; if (track.off) { track.off(); } this.tracks_.splice(i, 1); break; } } if (!track) { return; } /** * triggered when a track is removed from track list. * * @event tracklist#removetrack * @type {eventtarget~event} * @property {track} track * a reference to track that was removed. */ this.trigger({ track: track, type: 'removetrack', target: this }); } /** * get a track from the tracklist by a tracks id * * @param {string} id - the id of the track to get * @method gettrackbyid * @return {track} * @private */ ; _proto.gettrackbyid = function gettrackbyid(id) { var result = null; for (var i = 0, l = this.length; i < l; i++) { var track = this[i]; if (track.id === id) { result = track; break; } } return result; }; return tracklist; }(eventtarget$2); /** * triggered when a different track is selected/enabled. * * @event tracklist#change * @type {eventtarget~event} */ /** * events that can be called with on + eventname. see {@link eventhandler}. * * @property {object} tracklist#allowedevents_ * @private */ tracklist.prototype.allowedevents_ = { change: 'change', addtrack: 'addtrack', removetrack: 'removetrack', labelchange: 'labelchange' }; // emulate attribute eventhandler support to allow for feature detection for (var event in tracklist.prototype.allowedevents_) { tracklist.prototype['on' + event] = null; } /** * anywhere we call this function we diverge from the spec * as we only support one enabled audiotrack at a time * * @param {audiotracklist} list * list to work on * * @param {audiotrack} track * the track to skip * * @private */ var disableothers$1 = function disableothers(list, track) { for (var i = 0; i < list.length; i++) { if (!object.keys(list[i]).length || track.id === list[i].id) { continue; } // another audio track is enabled, disable it list[i].enabled = false; } }; /** * the current list of {@link audiotrack} for a media file. * * @see [spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotracklist} * @extends tracklist */ var audiotracklist = /*#__pure__*/function (_tracklist) { _babel_runtime_helpers_inheritsloose__webpack_imported_module_5___default()(audiotracklist, _tracklist); /** * create an instance of this class. * * @param {audiotrack[]} [tracks=[]] * a list of `audiotrack` to instantiate the list with. */ function audiotracklist(tracks) { var _this; if (tracks === void 0) { tracks = []; } // make sure only 1 track is enabled // sorted from last index to first index for (var i = tracks.length - 1; i >= 0; i--) { if (tracks[i].enabled) { disableothers$1(tracks, tracks[i]); break; } } _this = _tracklist.call(this, tracks) || this; _this.changing_ = false; return _this; } /** * add an {@link audiotrack} to the `audiotracklist`. * * @param {audiotrack} track * the audiotrack to add to the list * * @fires tracklist#addtrack */ var _proto = audiotracklist.prototype; _proto.addtrack = function addtrack(track) { var _this2 = this; if (track.enabled) { disableothers$1(this, track); } _tracklist.prototype.addtrack.call(this, track); // native tracks don't have this if (!track.addeventlistener) { return; } track.enabledchange_ = function () { // when we are disabling other tracks (since we don't support // more than one track at a time) we will set changing_ // to true so that we don't trigger additional change events if (_this2.changing_) { return; } _this2.changing_ = true; disableothers$1(_this2, track); _this2.changing_ = false; _this2.trigger('change'); }; /** * @listens audiotrack#enabledchange * @fires tracklist#change */ track.addeventlistener('enabledchange', track.enabledchange_); }; _proto.removetrack = function removetrack(rtrack) { _tracklist.prototype.removetrack.call(this, rtrack); if (rtrack.removeeventlistener && rtrack.enabledchange_) { rtrack.removeeventlistener('enabledchange', rtrack.enabledchange_); rtrack.enabledchange_ = null; } }; return audiotracklist; }(tracklist); /** * un-select all other {@link videotrack}s that are selected. * * @param {videotracklist} list * list to work on * * @param {videotrack} track * the track to skip * * @private */ var disableothers = function disableothers(list, track) { for (var i = 0; i < list.length; i++) { if (!object.keys(list[i]).length || track.id === list[i].id) { continue; } // another video track is enabled, disable it list[i].selected = false; } }; /** * the current list of {@link videotrack} for a video. * * @see [spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#videotracklist} * @extends tracklist */ var videotracklist = /*#__pure__*/function (_tracklist) { _babel_runtime_helpers_inheritsloose__webpack_imported_module_5___default()(videotracklist, _tracklist); /** * create an instance of this class. * * @param {videotrack[]} [tracks=[]] * a list of `videotrack` to instantiate the list with. */ function videotracklist(tracks) { var _this; if (tracks === void 0) { tracks = []; } // make sure only 1 track is enabled // sorted from last index to first index for (var i = tracks.length - 1; i >= 0; i--) { if (tracks[i].selected) { disableothers(tracks, tracks[i]); break; } } _this = _tracklist.call(this, tracks) || this; _this.changing_ = false; /** * @member {number} videotracklist#selectedindex * the current index of the selected {@link videotrack`}. */ object.defineproperty(_babel_runtime_helpers_assertthisinitialized__webpack_imported_module_4___default()(_this), 'selectedindex', { get: function get() { for (var _i = 0; _i < this.length; _i++) { if (this[_i].selected) { return _i; } } return -1; }, set: function set() {} }); return _this; } /** * add a {@link videotrack} to the `videotracklist`. * * @param {videotrack} track * the videotrack to add to the list * * @fires tracklist#addtrack */ var _proto = videotracklist.prototype; _proto.addtrack = function addtrack(track) { var _this2 = this; if (track.selected) { disableothers(this, track); } _tracklist.prototype.addtrack.call(this, track); // native tracks don't have this if (!track.addeventlistener) { return; } track.selectedchange_ = function () { if (_this2.changing_) { return; } _this2.changing_ = true; disableothers(_this2, track); _this2.changing_ = false; _this2.trigger('change'); }; /** * @listens videotrack#selectedchange * @fires tracklist#change */ track.addeventlistener('selectedchange', track.selectedchange_); }; _proto.removetrack = function removetrack(rtrack) { _tracklist.prototype.removetrack.call(this, rtrack); if (rtrack.removeeventlistener && rtrack.selectedchange_) { rtrack.removeeventlistener('selectedchange', rtrack.selectedchange_); rtrack.selectedchange_ = null; } }; return videotracklist; }(tracklist); /** * the current list of {@link texttrack} for a media file. * * @see [spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttracklist} * @extends tracklist */ var texttracklist = /*#__pure__*/function (_tracklist) { _babel_runtime_helpers_inheritsloose__webpack_imported_module_5___default()(texttracklist, _tracklist); function texttracklist() { return _tracklist.apply(this, arguments) || this; } var _proto = texttracklist.prototype; /** * add a {@link texttrack} to the `texttracklist` * * @param {texttrack} track * the text track to add to the list. * * @fires tracklist#addtrack */ _proto.addtrack = function addtrack(track) { var _this = this; _tracklist.prototype.addtrack.call(this, track); if (!this.queuechange_) { this.queuechange_ = function () { return _this.queuetrigger('change'); }; } if (!this.triggerselectedlanguagechange) { this.triggerselectedlanguagechange_ = function () { return _this.trigger('selectedlanguagechange'); }; } /** * @listens texttrack#modechange * @fires tracklist#change */ track.addeventlistener('modechange', this.queuechange_); var nonlanguagetexttrackkind = ['metadata', 'chapters']; if (nonlanguagetexttrackkind.indexof(track.kind) === -1) { track.addeventlistener('modechange', this.triggerselectedlanguagechange_); } }; _proto.removetrack = function removetrack(rtrack) { _tracklist.prototype.removetrack.call(this, rtrack); // manually remove the event handlers we added if (rtrack.removeeventlistener) { if (this.queuechange_) { rtrack.removeeventlistener('modechange', this.queuechange_); } if (this.selectedlanguagechange_) { rtrack.removeeventlistener('modechange', this.triggerselectedlanguagechange_); } } }; return texttracklist; }(tracklist); /** * @file html-track-element-list.js */ /** * the current list of {@link htmltrackelement}s. */ var htmltrackelementlist = /*#__pure__*/function () { /** * create an instance of this class. * * @param {htmltrackelement[]} [tracks=[]] * a list of `htmltrackelement` to instantiate the list with. */ function htmltrackelementlist(trackelements) { if (trackelements === void 0) { trackelements = []; } this.trackelements_ = []; /** * @memberof htmltrackelementlist * @member {number} length * the current number of `track`s in the this trackist. * @instance */ object.defineproperty(this, 'length', { get: function get() { return this.trackelements_.length; } }); for (var i = 0, length = trackelements.length; i < length; i++) { this.addtrackelement_(trackelements[i]); } } /** * add an {@link htmltrackelement} to the `htmltrackelementlist` * * @param {htmltrackelement} trackelement * the track element to add to the list. * * @private */ var _proto = htmltrackelementlist.prototype; _proto.addtrackelement_ = function addtrackelement_(trackelement) { var index = this.trackelements_.length; if (!('' + index in this)) { object.defineproperty(this, index, { get: function get() { return this.trackelements_[index]; } }); } // do not add duplicate elements if (this.trackelements_.indexof(trackelement) === -1) { this.trackelements_.push(trackelement); } } /** * get an {@link htmltrackelement} from the `htmltrackelementlist` given an * {@link texttrack}. * * @param {texttrack} track * the track associated with a track element. * * @return {htmltrackelement|undefined} * the track element that was found or undefined. * * @private */ ; _proto.gettrackelementbytrack_ = function gettrackelementbytrack_(track) { var trackelement_; for (var i = 0, length = this.trackelements_.length; i < length; i++) { if (track === this.trackelements_[i].track) { trackelement_ = this.trackelements_[i]; break; } } return trackelement_; } /** * remove a {@link htmltrackelement} from the `htmltrackelementlist` * * @param {htmltrackelement} trackelement * the track element to remove from the list. * * @private */ ; _proto.removetrackelement_ = function removetrackelement_(trackelement) { for (var i = 0, length = this.trackelements_.length; i < length; i++) { if (trackelement === this.trackelements_[i]) { if (this.trackelements_[i].track && typeof this.trackelements_[i].track.off === 'function') { this.trackelements_[i].track.off(); } if (typeof this.trackelements_[i].off === 'function') { this.trackelements_[i].off(); } this.trackelements_.splice(i, 1); break; } } }; return htmltrackelementlist; }(); /** * @file text-track-cue-list.js */ /** * @typedef {object} texttrackcuelist~texttrackcue * * @property {string} id * the unique id for this text track cue * * @property {number} starttime * the start time for this text track cue * * @property {number} endtime * the end time for this text track cue * * @property {boolean} pauseonexit * pause when the end time is reached if true. * * @see [spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackcue} */ /** * a list of texttrackcues. * * @see [spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackcuelist} */ var texttrackcuelist = /*#__pure__*/function () { /** * create an instance of this class.. * * @param {array} cues * a list of cues to be initialized with */ function texttrackcuelist(cues) { texttrackcuelist.prototype.setcues_.call(this, cues); /** * @memberof texttrackcuelist * @member {number} length * the current number of `texttrackcue`s in the texttrackcuelist. * @instance */ object.defineproperty(this, 'length', { get: function get() { return this.length_; } }); } /** * a setter for cues in this list. creates getters * an an index for the cues. * * @param {array} cues * an array of cues to set * * @private */ var _proto = texttrackcuelist.prototype; _proto.setcues_ = function setcues_(cues) { var oldlength = this.length || 0; var i = 0; var l = cues.length; this.cues_ = cues; this.length_ = cues.length; var defineprop = function defineprop(index) { if (!('' + index in this)) { object.defineproperty(this, '' + index, { get: function get() { return this.cues_[index]; } }); } }; if (oldlength < l) { i = oldlength; for (; i < l; i++) { defineprop.call(this, i); } } } /** * get a `texttrackcue` that is currently in the `texttrackcuelist` by id. * * @param {string} id * the id of the cue that should be searched for. * * @return {texttrackcuelist~texttrackcue|null} * a single cue or null if none was found. */ ; _proto.getcuebyid = function getcuebyid(id) { var result = null; for (var i = 0, l = this.length; i < l; i++) { var cue = this[i]; if (cue.id === id) { result = cue; break; } } return result; }; return texttrackcuelist; }(); /** * @file track-kinds.js */ /** * all possible `videotrackkind`s * * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-videotrack-kind * @typedef videotrack~kind * @enum */ var videotrackkind = { alternative: 'alternative', captions: 'captions', main: 'main', sign: 'sign', subtitles: 'subtitles', commentary: 'commentary' }; /** * all possible `audiotrackkind`s * * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-audiotrack-kind * @typedef audiotrack~kind * @enum */ var audiotrackkind = { 'alternative': 'alternative', 'descriptions': 'descriptions', 'main': 'main', 'main-desc': 'main-desc', 'translation': 'translation', 'commentary': 'commentary' }; /** * all possible `texttrackkind`s * * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-texttrack-kind * @typedef texttrack~kind * @enum */ var texttrackkind = { subtitles: 'subtitles', captions: 'captions', descriptions: 'descriptions', chapters: 'chapters', metadata: 'metadata' }; /** * all possible `texttrackmode`s * * @see https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackmode * @typedef texttrack~mode * @enum */ var texttrackmode = { disabled: 'disabled', hidden: 'hidden', showing: 'showing' }; /** * a track class that contains all of the common functionality for {@link audiotrack}, * {@link videotrack}, and {@link texttrack}. * * > note: this class should not be used directly * * @see {@link https://html.spec.whatwg.org/multipage/embedded-content.html} * @extends eventtarget * @abstract */ var track = /*#__pure__*/function (_eventtarget) { _babel_runtime_helpers_inheritsloose__webpack_imported_module_5___default()(track, _eventtarget); /** * create an instance of this class. * * @param {object} [options={}] * object of option names and values * * @param {string} [options.kind=''] * a valid kind for the track type you are creating. * * @param {string} [options.id='vjs_track_' + guid.newguid()] * a unique id for this audiotrack. * * @param {string} [options.label=''] * the menu label for this track. * * @param {string} [options.language=''] * a valid two character language code. * * @abstract */ function track(options) { var _this; if (options === void 0) { options = {}; } _this = _eventtarget.call(this) || this; var trackprops = { id: options.id || 'vjs_track_' + newguid(), kind: options.kind || '', language: options.language || '' }; var label = options.label || ''; /** * @memberof track * @member {string} id * the id of this track. cannot be changed after creation. * @instance * * @readonly */ /** * @memberof track * @member {string} kind * the kind of track that this is. cannot be changed after creation. * @instance * * @readonly */ /** * @memberof track * @member {string} language * the two letter language code for this track. cannot be changed after * creation. * @instance * * @readonly */ var _loop = function _loop(key) { object.defineproperty(_babel_runtime_helpers_assertthisinitialized__webpack_imported_module_4___default()(_this), key, { get: function get() { return trackprops[key]; }, set: function set() {} }); }; for (var key in trackprops) { _loop(key); } /** * @memberof track * @member {string} label * the label of this track. cannot be changed after creation. * @instance * * @fires track#labelchange */ object.defineproperty(_babel_runtime_helpers_assertthisinitialized__webpack_imported_module_4___default()(_this), 'label', { get: function get() { return label; }, set: function set(newlabel) { if (newlabel !== label) { label = newlabel; /** * an event that fires when label changes on this track. * * > note: this is not part of the spec! * * @event track#labelchange * @type {eventtarget~event} */ this.trigger('labelchange'); } } }); return _this; } return track; }(eventtarget$2); /** * @file url.js * @module url */ /** * @typedef {object} url:urlobject * * @property {string} protocol * the protocol of the url that was parsed. * * @property {string} hostname * the hostname of the url that was parsed. * * @property {string} port * the port of the url that was parsed. * * @property {string} pathname * the pathname of the url that was parsed. * * @property {string} search * the search query of the url that was parsed. * * @property {string} hash * the hash of the url that was parsed. * * @property {string} host * the host of the url that was parsed. */ /** * resolve and parse the elements of a url. * * @function * @param {string} url * the url to parse * * @return {url:urlobject} * an object of url details */ var parseurl = function parseurl(url) { // this entire method can be replace with url once we are able to drop ie11 var props = ['protocol', 'hostname', 'port', 'pathname', 'search', 'hash', 'host']; // add the url to an anchor and let the browser parse the url var a = global_document__webpack_imported_module_1___default.a.createelement('a'); a.href = url; // copy the specific url properties to a new object // this is also needed for ie because the anchor loses its // properties when it's removed from the dom var details = {}; for (var i = 0; i < props.length; i++) { details[props[i]] = a[props[i]]; } // ie adds the port to the host property unlike everyone else. if // a port identifier is added for standard ports, strip it. if (details.protocol === 'http:') { details.host = details.host.replace(/:80$/, ''); } if (details.protocol === 'https:') { details.host = details.host.replace(/:443$/, ''); } if (!details.protocol) { details.protocol = global_window__webpack_imported_module_0___default.a.location.protocol; } /* istanbul ignore if */ if (!details.host) { details.host = global_window__webpack_imported_module_0___default.a.location.host; } return details; }; /** * get absolute version of relative url. used to tell flash the correct url. * * @function * @param {string} url * url to make absolute * * @return {string} * absolute url * * @see http://stackoverflow.com/questions/470832/getting-an-absolute-url-from-a-relative-one-ie6-issue */ var getabsoluteurl = function getabsoluteurl(url) { // check if absolute url if (!url.match(/^https?:\/\//)) { // convert to absolute url. flash hosted off-site needs an absolute url. // add the url to an anchor and let the browser parse the url var a = global_document__webpack_imported_module_1___default.a.createelement('a'); a.href = url; url = a.href; } return url; }; /** * returns the extension of the passed file name. it will return an empty string * if passed an invalid path. * * @function * @param {string} path * the filename path like '/path/to/file.mp4' * * @return {string} * the extension in lower case or an empty string if no * extension could be found. */ var getfileextension = function getfileextension(path) { if (typeof path === 'string') { var splitpathre = /^(\/?)([\s\s]*?)((?:\.{1,2}|[^\/]+?)(\.([^\.\/\?]+)))(?:[\/]*|[\?].*)$/; var pathparts = splitpathre.exec(path); if (pathparts) { return pathparts.pop().tolowercase(); } } return ''; }; /** * returns whether the url passed is a cross domain request or not. * * @function * @param {string} url * the url to check. * * @param {object} [winloc] * the domain to check the url against, defaults to window.location * * @param {string} [winloc.protocol] * the window location protocol defaults to window.location.protocol * * @param {string} [winloc.host] * the window location host defaults to window.location.host * * @return {boolean} * whether it is a cross domain request or not. */ var iscrossorigin = function iscrossorigin(url, winloc) { if (winloc === void 0) { winloc = global_window__webpack_imported_module_0___default.a.location; } var urlinfo = parseurl(url); // ie8 protocol relative urls will return ':' for protocol var srcprotocol = urlinfo.protocol === ':' ? winloc.protocol : urlinfo.protocol; // check if url is for another domain/origin // ie8 doesn't know location.origin, so we won't rely on it here var crossorigin = srcprotocol + urlinfo.host !== winloc.protocol + winloc.host; return crossorigin; }; var url = /*#__pure__*/object.freeze({ __proto__: null, parseurl: parseurl, getabsoluteurl: getabsoluteurl, getfileextension: getfileextension, iscrossorigin: iscrossorigin }); /** * takes a webvtt file contents and parses it into cues * * @param {string} srccontent * webvtt file contents * * @param {texttrack} track * texttrack to add cues to. cues come from the srccontent. * * @private */ var parsecues = function parsecues(srccontent, track) { var parser = new global_window__webpack_imported_module_0___default.a.webvtt.parser(global_window__webpack_imported_module_0___default.a, global_window__webpack_imported_module_0___default.a.vttjs, global_window__webpack_imported_module_0___default.a.webvtt.stringdecoder()); var errors = []; parser.oncue = function (cue) { track.addcue(cue); }; parser.onparsingerror = function (error) { errors.push(error); }; parser.onflush = function () { track.trigger({ type: 'loadeddata', target: track }); }; parser.parse(srccontent); if (errors.length > 0) { if (global_window__webpack_imported_module_0___default.a.console && global_window__webpack_imported_module_0___default.a.console.groupcollapsed) { global_window__webpack_imported_module_0___default.a.console.groupcollapsed("text track parsing errors for " + track.src); } errors.foreach(function (error) { return log$1.error(error); }); if (global_window__webpack_imported_module_0___default.a.console && global_window__webpack_imported_module_0___default.a.console.groupend) { global_window__webpack_imported_module_0___default.a.console.groupend(); } } parser.flush(); }; /** * load a `texttrack` from a specified url. * * @param {string} src * url to load track from. * * @param {texttrack} track * track to add cues to. comes from the content at the end of `url`. * * @private */ var loadtrack = function loadtrack(src, track) { var opts = { uri: src }; var crossorigin = iscrossorigin(src); if (crossorigin) { opts.cors = crossorigin; } var withcredentials = track.tech_.crossorigin() === 'use-credentials'; if (withcredentials) { opts.withcredentials = withcredentials; } _videojs_xhr__webpack_imported_module_7___default()(opts, bind(this, function (err, response, responsebody) { if (err) { return log$1.error(err, response); } track.loaded_ = true; // make sure that vttjs has loaded, otherwise, wait till it finished loading // note: this is only used for the alt/video.novtt.js build if (typeof global_window__webpack_imported_module_0___default.a.webvtt !== 'function') { if (track.tech_) { // to prevent use before define eslint error, we define loadhandler // as a let here track.tech_.any(['vttjsloaded', 'vttjserror'], function (event) { if (event.type === 'vttjserror') { log$1.error("vttjs failed to load, stopping trying to process " + track.src); return; } return parsecues(responsebody, track); }); } } else { parsecues(responsebody, track); } })); }; /** * a representation of a single `texttrack`. * * @see [spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrack} * @extends track */ var texttrack = /*#__pure__*/function (_track) { _babel_runtime_helpers_inheritsloose__webpack_imported_module_5___default()(texttrack, _track); /** * create an instance of this class. * * @param {object} options={} * object of option names and values * * @param {tech} options.tech * a reference to the tech that owns this texttrack. * * @param {texttrack~kind} [options.kind='subtitles'] * a valid text track kind. * * @param {texttrack~mode} [options.mode='disabled'] * a valid text track mode. * * @param {string} [options.id='vjs_track_' + guid.newguid()] * a unique id for this texttrack. * * @param {string} [options.label=''] * the menu label for this track. * * @param {string} [options.language=''] * a valid two character language code. * * @param {string} [options.srclang=''] * a valid two character language code. an alternative, but deprioritized * version of `options.language` * * @param {string} [options.src] * a url to texttrack cues. * * @param {boolean} [options.default] * if this track should default to on or off. */ function texttrack(options) { var _this; if (options === void 0) { options = {}; } if (!options.tech) { throw new error('a tech was not provided.'); } var settings = mergeoptions$3(options, { kind: texttrackkind[options.kind] || 'subtitles', language: options.language || options.srclang || '' }); var mode = texttrackmode[settings.mode] || 'disabled'; var default_ = settings["default"]; if (settings.kind === 'metadata' || settings.kind === 'chapters') { mode = 'hidden'; } _this = _track.call(this, settings) || this; _this.tech_ = settings.tech; _this.cues_ = []; _this.activecues_ = []; _this.preload_ = _this.tech_.preloadtexttracks !== false; var cues = new texttrackcuelist(_this.cues_); var activecues = new texttrackcuelist(_this.activecues_); var changed = false; _this.timeupdatehandler = bind(_babel_runtime_helpers_assertthisinitialized__webpack_imported_module_4___default()(_this), function (event) { if (event === void 0) { event = {}; } if (this.tech_.isdisposed()) { return; } if (!this.tech_.isready_) { if (event.type !== 'timeupdate') { this.rvf_ = this.tech_.requestvideoframecallback(this.timeupdatehandler); } return; } // accessing this.activecues for the side-effects of updating itself // due to its nature as a getter function. do not remove or cues will // stop updating! // use the setter to prevent deletion from uglify (pure_getters rule) this.activecues = this.activecues; if (changed) { this.trigger('cuechange'); changed = false; } if (event.type !== 'timeupdate') { this.rvf_ = this.tech_.requestvideoframecallback(this.timeupdatehandler); } }); var disposehandler = function disposehandler() { _this.stoptracking(); }; _this.tech_.one('dispose', disposehandler); if (mode !== 'disabled') { _this.starttracking(); } object.defineproperties(_babel_runtime_helpers_assertthisinitialized__webpack_imported_module_4___default()(_this), { /** * @memberof texttrack * @member {boolean} default * if this track was set to be on or off by default. cannot be changed after * creation. * @instance * * @readonly */ "default": { get: function get() { return default_; }, set: function set() {} }, /** * @memberof texttrack * @member {string} mode * set the mode of this texttrack to a valid {@link texttrack~mode}. will * not be set if setting to an invalid mode. * @instance * * @fires texttrack#modechange */ mode: { get: function get() { return mode; }, set: function set(newmode) { if (!texttrackmode[newmode]) { return; } if (mode === newmode) { return; } mode = newmode; if (!this.preload_ && mode !== 'disabled' && this.cues.length === 0) { // on-demand load. loadtrack(this.src, this); } this.stoptracking(); if (mode !== 'disabled') { this.starttracking(); } /** * an event that fires when mode changes on this track. this allows * the texttracklist that holds this track to act accordingly. * * > note: this is not part of the spec! * * @event texttrack#modechange * @type {eventtarget~event} */ this.trigger('modechange'); } }, /** * @memberof texttrack * @member {texttrackcuelist} cues * the text track cue list for this texttrack. * @instance */ cues: { get: function get() { if (!this.loaded_) { return null; } return cues; }, set: function set() {} }, /** * @memberof texttrack * @member {texttrackcuelist} activecues * the list text track cues that are currently active for this texttrack. * @instance */ activecues: { get: function get() { if (!this.loaded_) { return null; } // nothing to do if (this.cues.length === 0) { return activecues; } var ct = this.tech_.currenttime(); var active = []; for (var i = 0, l = this.cues.length; i < l; i++) { var cue = this.cues[i]; if (cue.starttime <= ct && cue.endtime >= ct) { active.push(cue); } else if (cue.starttime === cue.endtime && cue.starttime <= ct && cue.starttime + 0.5 >= ct) { active.push(cue); } } changed = false; if (active.length !== this.activecues_.length) { changed = true; } else { for (var _i = 0; _i < active.length; _i++) { if (this.activecues_.indexof(active[_i]) === -1) { changed = true; } } } this.activecues_ = active; activecues.setcues_(this.activecues_); return activecues; }, // /!\ keep this setter empty (see the timeupdate handler above) set: function set() {} } }); if (settings.src) { _this.src = settings.src; if (!_this.preload_) { // tracks will load on-demand. // act like we're loaded for other purposes. _this.loaded_ = true; } if (_this.preload_ || settings.kind !== 'subtitles' && settings.kind !== 'captions') { loadtrack(_this.src, _babel_runtime_helpers_assertthisinitialized__webpack_imported_module_4___default()(_this)); } } else { _this.loaded_ = true; } return _this; } var _proto = texttrack.prototype; _proto.starttracking = function starttracking() { // more precise cues based on requestvideoframecallback with a requestanimationfram fallback this.rvf_ = this.tech_.requestvideoframecallback(this.timeupdatehandler); // also listen to timeupdate in case rvfc/raf stops (window in background, audio in video el) this.tech_.on('timeupdate', this.timeupdatehandler); }; _proto.stoptracking = function stoptracking() { if (this.rvf_) { this.tech_.cancelvideoframecallback(this.rvf_); this.rvf_ = undefined; } this.tech_.off('timeupdate', this.timeupdatehandler); } /** * add a cue to the internal list of cues. * * @param {texttrack~cue} cue * the cue to add to our internal list */ ; _proto.addcue = function addcue(originalcue) { var cue = originalcue; if (cue.constructor && cue.constructor.name !== 'vttcue') { cue = new global_window__webpack_imported_module_0___default.a.vttjs.vttcue(originalcue.starttime, originalcue.endtime, originalcue.text); for (var prop in originalcue) { if (!(prop in cue)) { cue[prop] = originalcue[prop]; } } // make sure that `id` is copied over cue.id = originalcue.id; cue.originalcue_ = originalcue; } var tracks = this.tech_.texttracks(); for (var i = 0; i < tracks.length; i++) { if (tracks[i] !== this) { tracks[i].removecue(cue); } } this.cues_.push(cue); this.cues.setcues_(this.cues_); } /** * remove a cue from our internal list * * @param {texttrack~cue} removecue * the cue to remove from our internal list */ ; _proto.removecue = function removecue(_removecue) { var i = this.cues_.length; while (i--) { var cue = this.cues_[i]; if (cue === _removecue || cue.originalcue_ && cue.originalcue_ === _removecue) { this.cues_.splice(i, 1); this.cues.setcues_(this.cues_); break; } } }; return texttrack; }(track); /** * cuechange - one or more cues in the track have become active or stopped being active. */ texttrack.prototype.allowedevents_ = { cuechange: 'cuechange' }; /** * a representation of a single `audiotrack`. if it is part of an {@link audiotracklist} * only one `audiotrack` in the list will be enabled at a time. * * @see [spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotrack} * @extends track */ var audiotrack = /*#__pure__*/function (_track) { _babel_runtime_helpers_inheritsloose__webpack_imported_module_5___default()(audiotrack, _track); /** * create an instance of this class. * * @param {object} [options={}] * object of option names and values * * @param {audiotrack~kind} [options.kind=''] * a valid audio track kind * * @param {string} [options.id='vjs_track_' + guid.newguid()] * a unique id for this audiotrack. * * @param {string} [options.label=''] * the menu label for this track. * * @param {string} [options.language=''] * a valid two character language code. * * @param {boolean} [options.enabled] * if this track is the one that is currently playing. if this track is part of * an {@link audiotracklist}, only one {@link audiotrack} will be enabled. */ function audiotrack(options) { var _this; if (options === void 0) { options = {}; } var settings = mergeoptions$3(options, { kind: audiotrackkind[options.kind] || '' }); _this = _track.call(this, settings) || this; var enabled = false; /** * @memberof audiotrack * @member {boolean} enabled * if this `audiotrack` is enabled or not. when setting this will * fire {@link audiotrack#enabledchange} if the state of enabled is changed. * @instance * * @fires videotrack#selectedchange */ object.defineproperty(_babel_runtime_helpers_assertthisinitialized__webpack_imported_module_4___default()(_this), 'enabled', { get: function get() { return enabled; }, set: function set(newenabled) { // an invalid or unchanged value if (typeof newenabled !== 'boolean' || newenabled === enabled) { return; } enabled = newenabled; /** * an event that fires when enabled changes on this track. this allows * the audiotracklist that holds this track to act accordingly. * * > note: this is not part of the spec! native tracks will do * this internally without an event. * * @event audiotrack#enabledchange * @type {eventtarget~event} */ this.trigger('enabledchange'); } }); // if the user sets this track to selected then // set selected to that true value otherwise // we keep it false if (settings.enabled) { _this.enabled = settings.enabled; } _this.loaded_ = true; return _this; } return audiotrack; }(track); /** * a representation of a single `videotrack`. * * @see [spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#videotrack} * @extends track */ var videotrack = /*#__pure__*/function (_track) { _babel_runtime_helpers_inheritsloose__webpack_imported_module_5___default()(videotrack, _track); /** * create an instance of this class. * * @param {object} [options={}] * object of option names and values * * @param {string} [options.kind=''] * a valid {@link videotrack~kind} * * @param {string} [options.id='vjs_track_' + guid.newguid()] * a unique id for this audiotrack. * * @param {string} [options.label=''] * the menu label for this track. * * @param {string} [options.language=''] * a valid two character language code. * * @param {boolean} [options.selected] * if this track is the one that is currently playing. */ function videotrack(options) { var _this; if (options === void 0) { options = {}; } var settings = mergeoptions$3(options, { kind: videotrackkind[options.kind] || '' }); _this = _track.call(this, settings) || this; var selected = false; /** * @memberof videotrack * @member {boolean} selected * if this `videotrack` is selected or not. when setting this will * fire {@link videotrack#selectedchange} if the state of selected changed. * @instance * * @fires videotrack#selectedchange */ object.defineproperty(_babel_runtime_helpers_assertthisinitialized__webpack_imported_module_4___default()(_this), 'selected', { get: function get() { return selected; }, set: function set(newselected) { // an invalid or unchanged value if (typeof newselected !== 'boolean' || newselected === selected) { return; } selected = newselected; /** * an event that fires when selected changes on this track. this allows * the videotracklist that holds this track to act accordingly. * * > note: this is not part of the spec! native tracks will do * this internally without an event. * * @event videotrack#selectedchange * @type {eventtarget~event} */ this.trigger('selectedchange'); } }); // if the user sets this track to selected then // set selected to that true value otherwise // we keep it false if (settings.selected) { _this.selected = settings.selected; } return _this; } return videotrack; }(track); /** * @memberof htmltrackelement * @typedef {htmltrackelement~readystate} * @enum {number} */ var none = 0; var loading = 1; var loaded = 2; var error = 3; /** * a single track represented in the dom. * * @see [spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#htmltrackelement} * @extends eventtarget */ var htmltrackelement = /*#__pure__*/function (_eventtarget) { _babel_runtime_helpers_inheritsloose__webpack_imported_module_5___default()(htmltrackelement, _eventtarget); /** * create an instance of this class. * * @param {object} options={} * object of option names and values * * @param {tech} options.tech * a reference to the tech that owns this htmltrackelement. * * @param {texttrack~kind} [options.kind='subtitles'] * a valid text track kind. * * @param {texttrack~mode} [options.mode='disabled'] * a valid text track mode. * * @param {string} [options.id='vjs_track_' + guid.newguid()] * a unique id for this texttrack. * * @param {string} [options.label=''] * the menu label for this track. * * @param {string} [options.language=''] * a valid two character language code. * * @param {string} [options.srclang=''] * a valid two character language code. an alternative, but deprioritized * version of `options.language` * * @param {string} [options.src] * a url to texttrack cues. * * @param {boolean} [options.default] * if this track should default to on or off. */ function htmltrackelement(options) { var _this; if (options === void 0) { options = {}; } _this = _eventtarget.call(this) || this; var readystate; var track = new texttrack(options); _this.kind = track.kind; _this.src = track.src; _this.srclang = track.language; _this.label = track.label; _this["default"] = track["default"]; object.defineproperties(_babel_runtime_helpers_assertthisinitialized__webpack_imported_module_4___default()(_this), { /** * @memberof htmltrackelement * @member {htmltrackelement~readystate} readystate * the current ready state of the track element. * @instance */ readystate: { get: function get() { return readystate; } }, /** * @memberof htmltrackelement * @member {texttrack} track * the underlying texttrack object. * @instance * */ track: { get: function get() { return track; } } }); readystate = none; /** * @listens texttrack#loadeddata * @fires htmltrackelement#load */ track.addeventlistener('loadeddata', function () { readystate = loaded; _this.trigger({ type: 'load', target: _babel_runtime_helpers_assertthisinitialized__webpack_imported_module_4___default()(_this) }); }); return _this; } return htmltrackelement; }(eventtarget$2); htmltrackelement.prototype.allowedevents_ = { load: 'load' }; htmltrackelement.none = none; htmltrackelement.loading = loading; htmltrackelement.loaded = loaded; htmltrackelement.error = error; /* * this file contains all track properties that are used in * player.js, tech.js, html5.js and possibly other techs in the future. */ var normal = { audio: { listclass: audiotracklist, trackclass: audiotrack, capitalname: 'audio' }, video: { listclass: videotracklist, trackclass: videotrack, capitalname: 'video' }, text: { listclass: texttracklist, trackclass: texttrack, capitalname: 'text' } }; object.keys(normal).foreach(function (type) { normal[type].gettername = type + "tracks"; normal[type].privatename = type + "tracks_"; }); var remote = { remotetext: { listclass: texttracklist, trackclass: texttrack, capitalname: 'remotetext', gettername: 'remotetexttracks', privatename: 'remotetexttracks_' }, remotetextel: { listclass: htmltrackelementlist, trackclass: htmltrackelement, capitalname: 'remotetexttrackels', gettername: 'remotetexttrackels', privatename: 'remotetexttrackels_' } }; var all = _babel_runtime_helpers_extends__webpack_imported_module_2___default()({}, normal, remote); remote.names = object.keys(remote); normal.names = object.keys(normal); all.names = [].concat(remote.names).concat(normal.names); /** * an object containing a structure like: `{src: 'url', type: 'mimetype'}` or string * that just contains the src url alone. * * `var sourceobject = {src: 'http://ex.com/video.mp4', type: 'video/mp4'};` * `var sourcestring = 'http://example.com/some-video.mp4';` * * @typedef {object|string} tech~sourceobject * * @property {string} src * the url to the source * * @property {string} type * the mime type of the source */ /** * a function used by {@link tech} to create a new {@link texttrack}. * * @private * * @param {tech} self * an instance of the tech class. * * @param {string} kind * `texttrack` kind (subtitles, captions, descriptions, chapters, or metadata) * * @param {string} [label] * label to identify the text track * * @param {string} [language] * two letter language abbreviation * * @param {object} [options={}] * an object with additional text track options * * @return {texttrack} * the text track that was created. */ function createtrackhelper(self, kind, label, language, options) { if (options === void 0) { options = {}; } var tracks = self.texttracks(); options.kind = kind; if (label) { options.label = label; } if (language) { options.language = language; } options.tech = self; var track = new all.text.trackclass(options); tracks.addtrack(track); return track; } /** * this is the base class for media playback technology controllers, such as * {@link html5} * * @extends component */ var tech = /*#__pure__*/function (_component) { _babel_runtime_helpers_inheritsloose__webpack_imported_module_5___default()(tech, _component); /** * create an instance of this tech. * * @param {object} [options] * the key/value store of player options. * * @param {component~readycallback} ready * callback function to call when the `html5` tech is ready. */ function tech(options, ready) { var _this; if (options === void 0) { options = {}; } if (ready === void 0) { ready = function ready() {}; } // we don't want the tech to report user activity automatically. // this is done manually in addcontrolslisteners options.reporttouchactivity = false; _this = _component.call(this, null, options, ready) || this; _this.ondurationchange_ = function (e) { return _this.ondurationchange(e); }; _this.trackprogress_ = function (e) { return _this.trackprogress(e); }; _this.trackcurrenttime_ = function (e) { return _this.trackcurrenttime(e); }; _this.stoptrackingcurrenttime_ = function (e) { return _this.stoptrackingcurrenttime(e); }; _this.disposesourcehandler_ = function (e) { return _this.disposesourcehandler(e); }; _this.queuedhanders_ = new set(); // keep track of whether the current source has played at all to // implement a very limited played() _this.hasstarted_ = false; _this.on('playing', function () { this.hasstarted_ = true; }); _this.on('loadstart', function () { this.hasstarted_ = false; }); all.names.foreach(function (name) { var props = all[name]; if (options && options[props.gettername]) { _this[props.privatename] = options[props.gettername]; } }); // manually track progress in cases where the browser/tech doesn't report it. if (!_this.featuresprogressevents) { _this.manualprogresson(); } // manually track timeupdates in cases where the browser/tech doesn't report it. if (!_this.featurestimeupdateevents) { _this.manualtimeupdateson(); } ['text', 'audio', 'video'].foreach(function (track) { if (options["native" + track + "tracks"] === false) { _this["featuresnative" + track + "tracks"] = false; } }); if (options.nativecaptions === false || options.nativetexttracks === false) { _this.featuresnativetexttracks = false; } else if (options.nativecaptions === true || options.nativetexttracks === true) { _this.featuresnativetexttracks = true; } if (!_this.featuresnativetexttracks) { _this.emulatetexttracks(); } _this.preloadtexttracks = options.preloadtexttracks !== false; _this.autoremotetexttracks_ = new all.text.listclass(); _this.inittracklisteners(); // turn on component tap events only if not using native controls if (!options.nativecontrolsfortouch) { _this.emittapevents(); } if (_this.constructor) { _this.name_ = _this.constructor.name || 'unknown tech'; } return _this; } /** * a special function to trigger source set in a way that will allow player * to re-trigger if the player or tech are not ready yet. * * @fires tech#sourceset * @param {string} src the source string at the time of the source changing. */ var _proto = tech.prototype; _proto.triggersourceset = function triggersourceset(src) { var _this2 = this; if (!this.isready_) { // on initial ready we have to trigger source set // 1ms after ready so that player can watch for it. this.one('ready', function () { return _this2.settimeout(function () { return _this2.triggersourceset(src); }, 1); }); } /** * fired when the source is set on the tech causing the media element * to reload. * * @see {@link player#event:sourceset} * @event tech#sourceset * @type {eventtarget~event} */ this.trigger({ src: src, type: 'sourceset' }); } /* fallbacks for unsupported event types ================================================================================ */ /** * polyfill the `progress` event for browsers that don't support it natively. * * @see {@link tech#trackprogress} */ ; _proto.manualprogresson = function manualprogresson() { this.on('durationchange', this.ondurationchange_); this.manualprogress = true; // trigger progress watching when a source begins loading this.one('ready', this.trackprogress_); } /** * turn off the polyfill for `progress` events that was created in * {@link tech#manualprogresson} */ ; _proto.manualprogressoff = function manualprogressoff() { this.manualprogress = false; this.stoptrackingprogress(); this.off('durationchange', this.ondurationchange_); } /** * this is used to trigger a `progress` event when the buffered percent changes. it * sets an interval function that will be called every 500 milliseconds to check if the * buffer end percent has changed. * * > this function is called by {@link tech#manualprogresson} * * @param {eventtarget~event} event * the `ready` event that caused this to run. * * @listens tech#ready * @fires tech#progress */ ; _proto.trackprogress = function trackprogress(event) { this.stoptrackingprogress(); this.progressinterval = this.setinterval(bind(this, function () { // don't trigger unless buffered amount is greater than last time var numbufferedpercent = this.bufferedpercent(); if (this.bufferedpercent_ !== numbufferedpercent) { /** * see {@link player#progress} * * @event tech#progress * @type {eventtarget~event} */ this.trigger('progress'); } this.bufferedpercent_ = numbufferedpercent; if (numbufferedpercent === 1) { this.stoptrackingprogress(); } }), 500); } /** * update our internal duration on a `durationchange` event by calling * {@link tech#duration}. * * @param {eventtarget~event} event * the `durationchange` event that caused this to run. * * @listens tech#durationchange */ ; _proto.ondurationchange = function ondurationchange(event) { this.duration_ = this.duration(); } /** * get and create a `timerange` object for buffering. * * @return {timerange} * the time range object that was created. */ ; _proto.buffered = function buffered() { return createtimeranges(0, 0); } /** * get the percentage of the current video that is currently buffered. * * @return {number} * a number from 0 to 1 that represents the decimal percentage of the * video that is buffered. * */ ; _proto.bufferedpercent = function bufferedpercent$1() { return bufferedpercent(this.buffered(), this.duration_); } /** * turn off the polyfill for `progress` events that was created in * {@link tech#manualprogresson} * stop manually tracking progress events by clearing the interval that was set in * {@link tech#trackprogress}. */ ; _proto.stoptrackingprogress = function stoptrackingprogress() { this.clearinterval(this.progressinterval); } /** * polyfill the `timeupdate` event for browsers that don't support it. * * @see {@link tech#trackcurrenttime} */ ; _proto.manualtimeupdateson = function manualtimeupdateson() { this.manualtimeupdates = true; this.on('play', this.trackcurrenttime_); this.on('pause', this.stoptrackingcurrenttime_); } /** * turn off the polyfill for `timeupdate` events that was created in * {@link tech#manualtimeupdateson} */ ; _proto.manualtimeupdatesoff = function manualtimeupdatesoff() { this.manualtimeupdates = false; this.stoptrackingcurrenttime(); this.off('play', this.trackcurrenttime_); this.off('pause', this.stoptrackingcurrenttime_); } /** * sets up an interval function to track current time and trigger `timeupdate` every * 250 milliseconds. * * @listens tech#play * @triggers tech#timeupdate */ ; _proto.trackcurrenttime = function trackcurrenttime() { if (this.currenttimeinterval) { this.stoptrackingcurrenttime(); } this.currenttimeinterval = this.setinterval(function () { /** * triggered at an interval of 250ms to indicated that time is passing in the video. * * @event tech#timeupdate * @type {eventtarget~event} */ this.trigger({ type: 'timeupdate', target: this, manuallytriggered: true }); // 42 = 24 fps // 250 is what webkit uses // ff uses 15 }, 250); } /** * stop the interval function created in {@link tech#trackcurrenttime} so that the * `timeupdate` event is no longer triggered. * * @listens {tech#pause} */ ; _proto.stoptrackingcurrenttime = function stoptrackingcurrenttime() { this.clearinterval(this.currenttimeinterval); // #1002 - if the video ends right before the next timeupdate would happen, // the progress bar won't make it all the way to the end this.trigger({ type: 'timeupdate', target: this, manuallytriggered: true }); } /** * turn off all event polyfills, clear the `tech`s {@link audiotracklist}, * {@link videotracklist}, and {@link texttracklist}, and dispose of this tech. * * @fires component#dispose */ ; _proto.dispose = function dispose() { // clear out all tracks because we can't reuse them between techs this.cleartracks(normal.names); // turn off any manual progress or timeupdate tracking if (this.manualprogress) { this.manualprogressoff(); } if (this.manualtimeupdates) { this.manualtimeupdatesoff(); } _component.prototype.dispose.call(this); } /** * clear out a single `tracklist` or an array of `tracklists` given their names. * * > note: techs without source handlers should call this between sources for `video` * & `audio` tracks. you don't want to use them between tracks! * * @param {string[]|string} types * tracklist names to clear, valid names are `video`, `audio`, and * `text`. */ ; _proto.cleartracks = function cleartracks(types) { var _this3 = this; types = [].concat(types); // clear out all tracks because we can't reuse them between techs types.foreach(function (type) { var list = _this3[type + "tracks"]() || []; var i = list.length; while (i--) { var track = list[i]; if (type === 'text') { _this3.removeremotetexttrack(track); } list.removetrack(track); } }); } /** * remove any texttracks added via addremotetexttrack that are * flagged for automatic garbage collection */ ; _proto.cleanupautotexttracks = function cleanupautotexttracks() { var list = this.autoremotetexttracks_ || []; var i = list.length; while (i--) { var track = list[i]; this.removeremotetexttrack(track); } } /** * reset the tech, which will removes all sources and reset the internal readystate. * * @abstract */ ; _proto.reset = function reset() {} /** * get the value of `crossorigin` from the tech. * * @abstract * * @see {html5#crossorigin} */ ; _proto.crossorigin = function crossorigin() {} /** * set the value of `crossorigin` on the tech. * * @abstract * * @param {string} crossorigin the crossorigin value * @see {html5#setcrossorigin} */ ; _proto.setcrossorigin = function setcrossorigin() {} /** * get or set an error on the tech. * * @param {mediaerror} [err] * error to set on the tech * * @return {mediaerror|null} * the current error object on the tech, or null if there isn't one. */ ; _proto.error = function error(err) { if (err !== undefined) { this.error_ = new mediaerror(err); this.trigger('error'); } return this.error_; } /** * returns the `timerange`s that have been played through for the current source. * * > note: this implementation is incomplete. it does not track the played `timerange`. * it only checks whether the source has played at all or not. * * @return {timerange} * - a single time range if this video has played * - an empty set of ranges if not. */ ; _proto.played = function played() { if (this.hasstarted_) { return createtimeranges(0, 0); } return createtimeranges(); } /** * start playback * * @abstract * * @see {html5#play} */ ; _proto.play = function play() {} /** * set whether we are scrubbing or not * * @abstract * * @see {html5#setscrubbing} */ ; _proto.setscrubbing = function setscrubbing() {} /** * get whether we are scrubbing or not * * @abstract * * @see {html5#scrubbing} */ ; _proto.scrubbing = function scrubbing() {} /** * causes a manual time update to occur if {@link tech#manualtimeupdateson} was * previously called. * * @fires tech#timeupdate */ ; _proto.setcurrenttime = function setcurrenttime() { // improve the accuracy of manual timeupdates if (this.manualtimeupdates) { /** * a manual `timeupdate` event. * * @event tech#timeupdate * @type {eventtarget~event} */ this.trigger({ type: 'timeupdate', target: this, manuallytriggered: true }); } } /** * turn on listeners for {@link videotracklist}, {@link {audiotracklist}, and * {@link texttracklist} events. * * this adds {@link eventtarget~eventlisteners} for `addtrack`, and `removetrack`. * * @fires tech#audiotrackchange * @fires tech#videotrackchange * @fires tech#texttrackchange */ ; _proto.inittracklisteners = function inittracklisteners() { var _this4 = this; /** * triggered when tracks are added or removed on the tech {@link audiotracklist} * * @event tech#audiotrackchange * @type {eventtarget~event} */ /** * triggered when tracks are added or removed on the tech {@link videotracklist} * * @event tech#videotrackchange * @type {eventtarget~event} */ /** * triggered when tracks are added or removed on the tech {@link texttracklist} * * @event tech#texttrackchange * @type {eventtarget~event} */ normal.names.foreach(function (name) { var props = normal[name]; var tracklistchanges = function tracklistchanges() { _this4.trigger(name + "trackchange"); }; var tracks = _this4[props.gettername](); tracks.addeventlistener('removetrack', tracklistchanges); tracks.addeventlistener('addtrack', tracklistchanges); _this4.on('dispose', function () { tracks.removeeventlistener('removetrack', tracklistchanges); tracks.removeeventlistener('addtrack', tracklistchanges); }); }); } /** * emulate texttracks using vtt.js if necessary * * @fires tech#vttjsloaded * @fires tech#vttjserror */ ; _proto.addwebvttscript_ = function addwebvttscript_() { var _this5 = this; if (global_window__webpack_imported_module_0___default.a.webvtt) { return; } // initially, tech.el_ is a child of a dummy-div wait until the component system // signals that the tech is ready at which point tech.el_ is part of the dom // before inserting the webvtt script if (global_document__webpack_imported_module_1___default.a.body.contains(this.el())) { // load via require if available and vtt.js script location was not passed in // as an option. novtt builds will turn the above require call into an empty object // which will cause this if check to always fail. if (!this.options_['vtt.js'] && isplain(videojs_vtt_js__webpack_imported_module_8___default.a) && object.keys(videojs_vtt_js__webpack_imported_module_8___default.a).length > 0) { this.trigger('vttjsloaded'); return; } // load vtt.js via the script location option or the cdn of no location was // passed in var script = global_document__webpack_imported_module_1___default.a.createelement('script'); script.src = this.options_['vtt.js'] || 'https://vjs.zencdn.net/vttjs/0.14.1/vtt.min.js'; script.onload = function () { /** * fired when vtt.js is loaded. * * @event tech#vttjsloaded * @type {eventtarget~event} */ _this5.trigger('vttjsloaded'); }; script.onerror = function () { /** * fired when vtt.js was not loaded due to an error * * @event tech#vttjsloaded * @type {eventtarget~event} */ _this5.trigger('vttjserror'); }; this.on('dispose', function () { script.onload = null; script.onerror = null; }); // but have not loaded yet and we set it to true before the inject so that // we don't overwrite the injected window.webvtt if it loads right away global_window__webpack_imported_module_0___default.a.webvtt = true; this.el().parentnode.appendchild(script); } else { this.ready(this.addwebvttscript_); } } /** * emulate texttracks * */ ; _proto.emulatetexttracks = function emulatetexttracks() { var _this6 = this; var tracks = this.texttracks(); var remotetracks = this.remotetexttracks(); var handleaddtrack = function handleaddtrack(e) { return tracks.addtrack(e.track); }; var handleremovetrack = function handleremovetrack(e) { return tracks.removetrack(e.track); }; remotetracks.on('addtrack', handleaddtrack); remotetracks.on('removetrack', handleremovetrack); this.addwebvttscript_(); var updatedisplay = function updatedisplay() { return _this6.trigger('texttrackchange'); }; var texttrackschanges = function texttrackschanges() { updatedisplay(); for (var i = 0; i < tracks.length; i++) { var track = tracks[i]; track.removeeventlistener('cuechange', updatedisplay); if (track.mode === 'showing') { track.addeventlistener('cuechange', updatedisplay); } } }; texttrackschanges(); tracks.addeventlistener('change', texttrackschanges); tracks.addeventlistener('addtrack', texttrackschanges); tracks.addeventlistener('removetrack', texttrackschanges); this.on('dispose', function () { remotetracks.off('addtrack', handleaddtrack); remotetracks.off('removetrack', handleremovetrack); tracks.removeeventlistener('change', texttrackschanges); tracks.removeeventlistener('addtrack', texttrackschanges); tracks.removeeventlistener('removetrack', texttrackschanges); for (var i = 0; i < tracks.length; i++) { var track = tracks[i]; track.removeeventlistener('cuechange', updatedisplay); } }); } /** * create and returns a remote {@link texttrack} object. * * @param {string} kind * `texttrack` kind (subtitles, captions, descriptions, chapters, or metadata) * * @param {string} [label] * label to identify the text track * * @param {string} [language] * two letter language abbreviation * * @return {texttrack} * the texttrack that gets created. */ ; _proto.addtexttrack = function addtexttrack(kind, label, language) { if (!kind) { throw new error('texttrack kind is required but was not provided'); } return createtrackhelper(this, kind, label, language); } /** * create an emulated texttrack for use by addremotetexttrack * * this is intended to be overridden by classes that inherit from * tech in order to create native or custom texttracks. * * @param {object} options * the object should contain the options to initialize the texttrack with. * * @param {string} [options.kind] * `texttrack` kind (subtitles, captions, descriptions, chapters, or metadata). * * @param {string} [options.label]. * label to identify the text track * * @param {string} [options.language] * two letter language abbreviation. * * @return {htmltrackelement} * the track element that gets created. */ ; _proto.createremotetexttrack = function createremotetexttrack(options) { var track = mergeoptions$3(options, { tech: this }); return new remote.remotetextel.trackclass(track); } /** * creates a remote text track object and returns an html track element. * * > note: this can be an emulated {@link htmltrackelement} or a native one. * * @param {object} options * see {@link tech#createremotetexttrack} for more detailed properties. * * @param {boolean} [manualcleanup=true] * - when false: the texttrack will be automatically removed from the video * element whenever the source changes * - when true: the texttrack will have to be cleaned up manually * * @return {htmltrackelement} * an html track element. * * @deprecated the default functionality for this function will be equivalent * to "manualcleanup=false" in the future. the manualcleanup parameter will * also be removed. */ ; _proto.addremotetexttrack = function addremotetexttrack(options, manualcleanup) { var _this7 = this; if (options === void 0) { options = {}; } var htmltrackelement = this.createremotetexttrack(options); if (manualcleanup !== true && manualcleanup !== false) { // deprecation warning log$1.warn('calling addremotetexttrack without explicitly setting the "manualcleanup" parameter to `true` is deprecated and default to `false` in future version of video.js'); manualcleanup = true; } // store htmltrackelement and texttrack to remote list this.remotetexttrackels().addtrackelement_(htmltrackelement); this.remotetexttracks().addtrack(htmltrackelement.track); if (manualcleanup !== true) { // create the texttracklist if it doesn't exist this.ready(function () { return _this7.autoremotetexttracks_.addtrack(htmltrackelement.track); }); } return htmltrackelement; } /** * remove a remote text track from the remote `texttracklist`. * * @param {texttrack} track * `texttrack` to remove from the `texttracklist` */ ; _proto.removeremotetexttrack = function removeremotetexttrack(track) { var trackelement = this.remotetexttrackels().gettrackelementbytrack_(track); // remove htmltrackelement and texttrack from remote list this.remotetexttrackels().removetrackelement_(trackelement); this.remotetexttracks().removetrack(track); this.autoremotetexttracks_.removetrack(track); } /** * gets available media playback quality metrics as specified by the w3c's media * playback quality api. * * @see [spec]{@link https://wicg.github.io/media-playback-quality} * * @return {object} * an object with supported media playback quality metrics * * @abstract */ ; _proto.getvideoplaybackquality = function getvideoplaybackquality() { return {}; } /** * attempt to create a floating video window always on top of other windows * so that users may continue consuming media while they interact with other * content sites, or applications on their device. * * @see [spec]{@link https://wicg.github.io/picture-in-picture} * * @return {promise|undefined} * a promise with a picture-in-picture window if the browser supports * promises (or one was passed in as an option). it returns undefined * otherwise. * * @abstract */ ; _proto.requestpictureinpicture = function requestpictureinpicture() { var promiseclass = this.options_.promise || global_window__webpack_imported_module_0___default.a.promise; if (promiseclass) { return promiseclass.reject(); } } /** * a method to check for the value of the 'disablepictureinpicture'