PageSourceSearch

https://midlandloftskc.com/wp-content/plugins/wpvr/public/js/video.js?ver=1

js midlandloftskc.com collected 2026-10-04 23:05:54 UTC 1,834,656 bytes, 55,669 lines download raw bytes

vendor: 8,590 bytes, lines 1-271
1/**
2 * @license
3 * Video.js 7.7.4 <http://videojs.com/>
4 * Copyright Brightcove, Inc. <https://www.brightcove.com/>
5 * Available under Apache License Version 2.0
6 * <https://github.com/videojs/video.js/blob/master/LICENSE>
7 *
8 * Includes vtt.js <https://github.com/mozilla/vtt.js>
9 * Available under Apache License Version 2.0
10 * <https://github.com/mozilla/vtt.js/blob/master/LICENSE>
11 */
12
13(function (global, factory) {
14   typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('global/window'), require('global/document')) :
15   typeof define === 'function' && define.amd ? define(['global/window', 'global/document'], factory) :
16   (global = global || self, global.videojs = factory(global.window, global.document));
17 }(this, function (window$1, document) { 'use strict';
18 
19   window$1 = window$1 && window$1.hasOwnProperty('default') ? window$1['default'] : window$1;
20   document = document && document.hasOwnProperty('default') ? document['default'] : document;
21 
22   var version = "7.7.4";
23 
24   /**
25    * @file create-logger.js
26    * @module create-logger
27    */
28 
29   var history = [];
30   /**
31    * Log messages to the console and history based on the type of message
32    *
33    * @private
34    * @param  {string} type
35    *         The name of the console method to use.
36    *
37    * @param  {Array} args
38    *         The arguments to be passed to the matching console method.
39    */
40 
41   var LogByTypeFactory = function LogByTypeFactory(name, log) {
42     return function (type, level, args) {
43       var lvl = log.levels[level];
44       var lvlRegExp = new RegExp("^(" + lvl + ")$");
45 
46       if (type !== 'log') {
47         // Add the type to the front of the message when it's not "log".
48         args.unshift(type.toUpperCase() + ':');
49       } // Add console prefix after adding to history.
50 
51 
52       args.unshift(name + ':'); // Add a clone of the args at this point to history.
53 
54       if (history) {
55         history.push([].concat(args)); // only store 1000 history entries
56 
57         var splice = history.length - 1000;
58         history.splice(0, splice > 0 ? splice : 0);
59       } // If there's no console then don't try to output messages, but they will
60       // still be stored in history.
61 
62 
63       if (!window$1.console) {
64         return;
65       } // Was setting these once outside of this function, but containing them
66       // in the function makes it easier to test cases where console doesn't exist
67       // when the module is executed.
68 
69 
70       var fn = window$1.console[type];
71 
72       if (!fn && type === 'debug') {
73         // Certain browsers don't have support for console.debug. For those, we
74         // should default to the closest comparable log.
75         fn = window$1.console.info || window$1.console.log;
76       } // Bail out if there's no console or if this type is not allowed by the
77       // current logging level.
78 
79 
80       if (!fn || !lvl || !lvlRegExp.test(type)) {
81         return;
82       }
83 
84       fn[Array.isArray(args) ? 'apply' : 'call'](window$1.console, args);
85     };
86   };
87 
88   function createLogger(name) {
89     // This is the private tracking variable for logging level.
90     var level = 'info'; // the curried logByType bound to the specific log and history
91 
92     var logByType;
93     /**
94      * Logs plain debug messages. Similar to `console.log`.
95      *
96      * Due to [limitations](https://github.com/jsdoc3/jsdoc/issues/955#issuecomment-313829149)
97      * of our JSDoc template, we cannot properly document this as both a function
98      * and a namespace, so its function signature is documented here.
99      *
100      * #### Arguments
101      * ##### *args
102      * Mixed[]
103      *
104      * Any combination of values that could be passed to `console.log()`.
105      *
106      * #### Return Value
107      *
108      * `undefined`
109      *
110      * @namespace
111      * @param    {Mixed[]} args
112      *           One or more messages or objects that should be logged.
113      */
114 
115     var log = function log() {
116       for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
117         args[_key] = arguments[_key];
118       }
119 
120       logByType('log', level, args);
121     }; // This is the logByType helper that the logging methods below use
122 
123 
124     logByType = LogByTypeFactory(name, log);
125     /**
126      * Create a new sublogger which chains the old name to the new name.
127      *
128      * For example, doing `videojs.log.createLogger('player')` and then using that logger will log the following:
129      * ```js
130      *  mylogger('foo');
131      *  // > VIDEOJS: player: foo
132      * ```
133      *
134      * @param {string} name
135      *        The name to add call the new logger
136      * @return {Object}
137      */
138 
139     log.createLogger = function (subname) {
140       return createLogger(name + ': ' + subname);
141     };
142     /**
143      * Enumeration of available logging levels, where the keys are the level names
144      * and the values are `|`-separated strings containing logging methods allowed
145      * in that logging level. These strings are used to create a regular expression
146      * matching the function name being called.
147      *
148      * Levels provided by Video.js are:
149      *
150      * - `off`: Matches no calls. Any value that can be cast to `false` will have
151      *   this effect. The most restrictive.
152      * - `all`: Matches only Video.js-provided functions (`debug`, `log`,
153      *   `log.warn`, and `log.error`).
154      * - `debug`: Matches `log.debug`, `log`, `log.warn`, and `log.error` calls.
155      * - `info` (default): Matches `log`, `log.warn`, and `log.error` calls.
156      * - `warn`: Matches `log.warn` and `log.error` calls.
157      * - `error`: Matches only `log.error` calls.
158      *
159      * @type {Object}
160      */
161 
162 
163     log.levels = {
164       all: 'debug|log|warn|error',
165       off: '',
166       debug: 'debug|log|warn|error',
167       info: 'log|warn|error',
168       warn: 'warn|error',
169       error: 'error',
170       DEFAULT: level
171     };
172     /**
173      * Get or set the current logging level.
174      *
175      * If a string matching a key from {@link module:log.levels} is provided, acts
176      * as a setter.
177      *
178      * @param  {string} [lvl]
179      *         Pass a valid level to set a new logging level.
180      *
181      * @return {string}
182      *         The current logging level.
183      */
184 
185     log.level = function (lvl) {
186       if (typeof lvl === 'string') {
187         if (!log.levels.hasOwnProperty(lvl)) {
188           throw new Error("\"" + lvl + "\" in not a valid log level");
189         }
190 
191         level = lvl;
192       }
193 
194       return level;
195     };
196     /**
197      * Returns an array containing everything that has been logged to the history.
198      *
199      * This array is a shallow clone of the internal history record. However, its
200      * contents are _not_ cloned; so, mutating objects inside this array will
201      * mutate them in history.
202      *
203      * @return {Array}
204      */
205 
206 
207     log.history = function () {
208       return history ? [].concat(history) : [];
209     };
210     /**
211      * Allows you to filter the history by the given logger name
212      *
213      * @param {string} fname
214      *        The name to filter by
215      *
216      * @return {Array}
217      *         The filtered list to return
218      */
219 
220 
221     log.history.filter = function (fname) {
222       return (history || []).filter(function (historyItem) {
223         // if the first item in each historyItem includes `fname`, then it's a match
224         return new RegExp(".*" + fname + ".*").test(historyItem[0]);
225       });
226     };
227     /**
228      * Clears the internal history tracking, but does not prevent further history
229      * tracking.
230      */
231 
232 
233     log.history.clear = function () {
234       if (history) {
235         history.length = 0;
236       }
237     };
238     /**
239      * Disable history tracking if it is currently enabled.
240      */
241 
242 
243     log.history.disable = function () {
244       if (history !== null) {
245         history.length = 0;
246         history = null;
247       }
248     };
249     /**
250      * Enable history tracking if it is currently disabled.
251      */
252 
253 
254     log.history.enable = function () {
255       if (history === null) {
256         history = [];
257       }
258     };
259     /**
260      * Logs error messages. Similar to `console.error`.
261      *
262      * @param {Mixed[]} args
263      *        One or more messages or objects that should be logged as an error
264      */
265 
266 
267     log.error = function () {
268       for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
269         args[_key2] = arguments[_key2];
270       }
271 
272       return logByType('error', level, args);
273     };
274     /**
275      * Logs warning messages. Similar to `console.warn`.
276      *
277      * @param {Mixed[]} args
278      *        One or more messages or objects that should be logged as a warning.
279      */
280 
281 
282     log.warn = function () {
283       for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
284         args[_key3] = arguments[_key3];
285       }
286 
287       return logByType('warn', level, args);
288     };
289     /**
290      * Logs debug messages. Similar to `console.debug`, but may also act as a comparable
291      * log if `console.debug` is not available
292      *
293      * @param {Mixed[]} args
294      *        One or more messages or objects that should be logged as debug.
295      */
296 
297 
298     log.debug = function () {
299       for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
300         args[_key4] = arguments[_key4];
301       }
302 
303       return logByType('debug', level, args);
304     };
305 
306     return log;
307   }
308 
309   /**
310    * @file log.js
311    * @module log
312    */
313   var log = createLogger('VIDEOJS');
314   var createLogger$1 = log.createLogger;
315 
316   function createCommonjsModule(fn, module) {
317      return module = { exports: {} }, fn(module, module.exports), module.exports;
318   }
319 
320   var _extends_1 = createCommonjsModule(function (module) {
321     function _extends() {
322       module.exports = _extends = Object.assign || function (target) {
323         for (var i = 1; i < arguments.length; i++) {
324           var source = arguments[i];
325 
326           for (var key in source) {
327             if (Object.prototype.hasOwnProperty.call(source, key)) {
328               target[key] = source[key];
329             }
330           }
331         }
332 
333         return target;
334       };
335 
336       return _extends.apply(this, arguments);
337     }
338 
339     module.exports = _extends;
340   });
341 
342   /**
343    * @file obj.js
344    * @module obj
345    */
346 
347   /**
348    * @callback obj:EachCallback
349    *
350    * @param {Mixed} value
351    *        The current key for the object that is being iterated over.
352    *
353    * @param {string} key
354    *        The current key-value for object that is being iterated over
355    */
356 
357   /**
358    * @callback obj:ReduceCallback
359    *
360    * @param {Mixed} accum
361    *        The value that is accumulating over the reduce loop.
362    *
363    * @param {Mixed} value
364    *        The current key for the object that is being iterated over.
365    *
366    * @param {string} key
367    *        The current key-value for object that is being iterated over
368    *
369    * @return {Mixed}
370    *         The new accumulated value.
371    */
372   var toString = Object.prototype.toString;
373   /**
374    * Get the keys of an Object
375    *
376    * @param {Object}
377    *        The Object to get the keys from
378    *
379    * @return {string[]}
380    *         An array of the keys from the object. Returns an empty array if the
381    *         object passed in was invalid or had no keys.
382    *
383    * @private
384    */
385 
386   var keys = function keys(object) {
387     return isObject(object) ? Object.keys(object) : [];
388   };
389   /**
390    * Array-like iteration for objects.
391    *
392    * @param {Object} object
393    *        The object to iterate over
394    *
395    * @param {obj:EachCallback} fn
396    *        The callback function which is called for each key in the object.
397    */
398 
399 
400   function each(object, fn) {
401     keys(object).forEach(function (key) {
402       return fn(object[key], key);
403     });
404   }
405   /**
406    * Array-like reduce for objects.
407    *
408    * @param {Object} object
409    *        The Object that you want to reduce.
410    *
411    * @param {Function} fn
412    *         A callback function which is called for each key in the object. It
413    *         receives the accumulated value and the per-iteration value and key
414    *         as arguments.
415    *
416    * @param {Mixed} [initial = 0]
417    *        Starting value
418    *
419    * @return {Mixed}
420    *         The final accumulated value.
421    */
422 
423   function reduce(object, fn, initial) {
424     if (initial === void 0) {
425       initial = 0;
426     }
427 
428     return keys(object).reduce(function (accum, key) {
429       return fn(accum, object[key], key);
430     }, initial);
431   }
432   /**
433    * Object.assign-style object shallow merge/extend.
434    *
435    * @param  {Object} target
436    * @param  {Object} ...sources
437    * @return {Object}
438    */
439 
440   function assign(target) {
441     for (var _len = arguments.length, sources = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
442       sources[_key - 1] = arguments[_key];
443     }
444 
445     if (Object.assign) {
446       return _extends_1.apply(void 0, [target].concat(sources));
447     }
448 
vendor: 15,893 bytes, lines 449-994
449     sources.forEach(function (source) {
450       if (!source) {
451         return;
452       }
453 
454       each(source, function (value, key) {
455         target[key] = value;
456       });
457     });
458     return target;
459   }
460   /**
461    * Returns whether a value is an object of any kind - including DOM nodes,
462    * arrays, regular expressions, etc. Not functions, though.
463    *
464    * This avoids the gotcha where using `typeof` on a `null` value
465    * results in `'object'`.
466    *
467    * @param  {Object} value
468    * @return {boolean}
469    */
470 
471   function isObject(value) {
472     return !!value && typeof value === 'object';
473   }
474   /**
475    * Returns whether an object appears to be a "plain" object - that is, a
476    * direct instance of `Object`.
477    *
478    * @param  {Object} value
479    * @return {boolean}
480    */
481 
482   function isPlain(value) {
483     return isObject(value) && toString.call(value) === '[object Object]' && value.constructor === Object;
484   }
485 
486   /**
487    * @file computed-style.js
488    * @module computed-style
489    */
490   /**
491    * A safe getComputedStyle.
492    *
493    * This is needed because in Firefox, if the player is loaded in an iframe with
494    * `display:none`, then `getComputedStyle` returns `null`, so, we do a
495    * null-check to make sure that the player doesn't break in these cases.
496    *
497    * @function
498    * @param    {Element} el
499    *           The element you want the computed style of
500    *
501    * @param    {string} prop
502    *           The property name you want
503    *
504    * @see      https://bugzilla.mozilla.org/show_bug.cgi?id=548397
505    */
506 
507   function computedStyle(el, prop) {
508     if (!el || !prop) {
509       return '';
510     }
511 
512     if (typeof window$1.getComputedStyle === 'function') {
513       var computedStyleValue = window$1.getComputedStyle(el);
514       return computedStyleValue ? computedStyleValue.getPropertyValue(prop) || computedStyleValue[prop] : '';
515     }
516 
517     return '';
518   }
519 
520   /**
521    * @file dom.js
522    * @module dom
523    */
524   /**
525    * Detect if a value is a string with any non-whitespace characters.
526    *
527    * @private
528    * @param  {string} str
529    *         The string to check
530    *
531    * @return {boolean}
532    *         Will be `true` if the string is non-blank, `false` otherwise.
533    *
534    */
535 
536   function isNonBlankString(str) {
537     // we use str.trim as it will trim any whitespace characters
538     // from the front or back of non-whitespace characters. aka
539     // Any string that contains non-whitespace characters will
540     // still contain them after `trim` but whitespace only strings
541     // will have a length of 0, failing this check.
542     return typeof str === 'string' && Boolean(str.trim());
543   }
544   /**
545    * Throws an error if the passed string has whitespace. This is used by
546    * class methods to be relatively consistent with the classList API.
547    *
548    * @private
549    * @param  {string} str
550    *         The string to check for whitespace.
551    *
552    * @throws {Error}
553    *         Throws an error if there is whitespace in the string.
554    */
555 
556 
557   function throwIfWhitespace(str) {
558     // str.indexOf instead of regex because str.indexOf is faster performance wise.
559     if (str.indexOf(' ') >= 0) {
560       throw new Error('class has illegal whitespace characters');
561     }
562   }
563   /**
564    * Produce a regular expression for matching a className within an elements className.
565    *
566    * @private
567    * @param  {string} className
568    *         The className to generate the RegExp for.
569    *
570    * @return {RegExp}
571    *         The RegExp that will check for a specific `className` in an elements
572    *         className.
573    */
574 
575 
576   function classRegExp(className) {
577     return new RegExp('(^|\\s)' + className + '($|\\s)');
578   }
579   /**
580    * Whether the current DOM interface appears to be real (i.e. not simulated).
581    *
582    * @return {boolean}
583    *         Will be `true` if the DOM appears to be real, `false` otherwise.
584    */
585 
586 
587   function isReal() {
588     // Both document and window will never be undefined thanks to `global`.
589     return document === window$1.document;
590   }
591   /**
592    * Determines, via duck typing, whether or not a value is a DOM element.
593    *
594    * @param  {Mixed} value
595    *         The value to check.
596    *
597    * @return {boolean}
598    *         Will be `true` if the value is a DOM element, `false` otherwise.
599    */
600 
601   function isEl(value) {
602     return isObject(value) && value.nodeType === 1;
603   }
604   /**
605    * Determines if the current DOM is embedded in an iframe.
606    *
607    * @return {boolean}
608    *         Will be `true` if the DOM is embedded in an iframe, `false`
609    *         otherwise.
610    */
611 
612   function isInFrame() {
613     // We need a try/catch here because Safari will throw errors when attempting
614     // to get either `parent` or `self`
615     try {
616       return window$1.parent !== window$1.self;
617     } catch (x) {
618       return true;
619     }
620   }
621   /**
622    * Creates functions to query the DOM using a given method.
623    *
624    * @private
625    * @param   {string} method
626    *          The method to create the query with.
627    *
628    * @return  {Function}
629    *          The query method
630    */
631 
632   function createQuerier(method) {
633     return function (selector, context) {
634       if (!isNonBlankString(selector)) {
635         return document[method](null);
636       }
637 
638       if (isNonBlankString(context)) {
639         context = document.querySelector(context);
640       }
641 
642       var ctx = isEl(context) ? context : document;
643       return ctx[method] && ctx[method](selector);
644     };
645   }
646   /**
647    * Creates an element and applies properties, attributes, and inserts content.
648    *
649    * @param  {string} [tagName='div']
650    *         Name of tag to be created.
651    *
652    * @param  {Object} [properties={}]
653    *         Element properties to be applied.
654    *
655    * @param  {Object} [attributes={}]
656    *         Element attributes to be applied.
657    *
658    * @param {module:dom~ContentDescriptor} content
659    *        A content descriptor object.
660    *
661    * @return {Element}
662    *         The element that was created.
663    */
664 
665 
666   function createEl(tagName, properties, attributes, content) {
667     if (tagName === void 0) {
668       tagName = 'div';
669     }
670 
671     if (properties === void 0) {
672       properties = {};
673     }
674 
675     if (attributes === void 0) {
676       attributes = {};
677     }
678 
679     var el = document.createElement(tagName);
680     Object.getOwnPropertyNames(properties).forEach(function (propName) {
681       var val = properties[propName]; // See #2176
682       // We originally were accepting both properties and attributes in the
683       // same object, but that doesn't work so well.
684 
685       if (propName.indexOf('aria-') !== -1 || propName === 'role' || propName === 'type') {
686         log.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 + "."));
687         el.setAttribute(propName, val); // Handle textContent since it's not supported everywhere and we have a
688         // method for it.
689       } else if (propName === 'textContent') {
690         textContent(el, val);
691       } else if (el[propName] !== val) {
692         el[propName] = val;
693       }
694     });
695     Object.getOwnPropertyNames(attributes).forEach(function (attrName) {
696       el.setAttribute(attrName, attributes[attrName]);
697     });
698 
699     if (content) {
700       appendContent(el, content);
701     }
702 
703     return el;
704   }
705   /**
706    * Injects text into an element, replacing any existing contents entirely.
707    *
708    * @param  {Element} el
709    *         The element to add text content into
710    *
711    * @param  {string} text
712    *         The text content to add.
713    *
714    * @return {Element}
715    *         The element with added text content.
716    */
717 
718   function textContent(el, text) {
719     if (typeof el.textContent === 'undefined') {
720       el.innerText = text;
721     } else {
722       el.textContent = text;
723     }
724 
725     return el;
726   }
727   /**
728    * Insert an element as the first child node of another
729    *
730    * @param {Element} child
731    *        Element to insert
732    *
733    * @param {Element} parent
734    *        Element to insert child into
735    */
736 
737   function prependTo(child, parent) {
738     if (parent.firstChild) {
739       parent.insertBefore(child, parent.firstChild);
740     } else {
741       parent.appendChild(child);
742     }
743   }
744   /**
745    * Check if an element has a class name.
746    *
747    * @param  {Element} element
748    *         Element to check
749    *
750    * @param  {string} classToCheck
751    *         Class name to check for
752    *
753    * @return {boolean}
754    *         Will be `true` if the element has a class, `false` otherwise.
755    *
756    * @throws {Error}
757    *         Throws an error if `classToCheck` has white space.
758    */
759 
760   function hasClass(element, classToCheck) {
761     throwIfWhitespace(classToCheck);
762 
763     if (element.classList) {
764       return element.classList.contains(classToCheck);
765     }
766 
767     return classRegExp(classToCheck).test(element.className);
768   }
769   /**
770    * Add a class name to an element.
771    *
772    * @param  {Element} element
773    *         Element to add class name to.
774    *
775    * @param  {string} classToAdd
776    *         Class name to add.
777    *
778    * @return {Element}
779    *         The DOM element with the added class name.
780    */
781 
782   function addClass(element, classToAdd) {
783     if (element.classList) {
784       element.classList.add(classToAdd); // Don't need to `throwIfWhitespace` here because `hasElClass` will do it
785       // in the case of classList not being supported.
786     } else if (!hasClass(element, classToAdd)) {
787       element.className = (element.className + ' ' + classToAdd).trim();
788     }
789 
790     return element;
791   }
792   /**
793    * Remove a class name from an element.
794    *
795    * @param  {Element} element
796    *         Element to remove a class name from.
797    *
798    * @param  {string} classToRemove
799    *         Class name to remove
800    *
801    * @return {Element}
802    *         The DOM element with class name removed.
803    */
804 
805   function removeClass(element, classToRemove) {
806     if (element.classList) {
807       element.classList.remove(classToRemove);
808     } else {
809       throwIfWhitespace(classToRemove);
810       element.className = element.className.split(/\s+/).filter(function (c) {
811         return c !== classToRemove;
812       }).join(' ');
813     }
814 
815     return element;
816   }
817   /**
818    * The callback definition for toggleClass.
819    *
820    * @callback module:dom~PredicateCallback
821    * @param    {Element} element
822    *           The DOM element of the Component.
823    *
824    * @param    {string} classToToggle
825    *           The `className` that wants to be toggled
826    *
827    * @return   {boolean|undefined}
828    *           If `true` is returned, the `classToToggle` will be added to the
829    *           `element`. If `false`, the `classToToggle` will be removed from
830    *           the `element`. If `undefined`, the callback will be ignored.
831    */
832 
833   /**
834    * Adds or removes a class name to/from an element depending on an optional
835    * condition or the presence/absence of the class name.
836    *
837    * @param  {Element} element
838    *         The element to toggle a class name on.
839    *
840    * @param  {string} classToToggle
841    *         The class that should be toggled.
842    *
843    * @param  {boolean|module:dom~PredicateCallback} [predicate]
844    *         See the return value for {@link module:dom~PredicateCallback}
845    *
846    * @return {Element}
847    *         The element with a class that has been toggled.
848    */
849 
850   function toggleClass(element, classToToggle, predicate) {
851     // This CANNOT use `classList` internally because IE11 does not support the
852     // second parameter to the `classList.toggle()` method! Which is fine because
853     // `classList` will be used by the add/remove functions.
854     var has = hasClass(element, classToToggle);
855 
856     if (typeof predicate === 'function') {
857       predicate = predicate(element, classToToggle);
858     }
859 
860     if (typeof predicate !== 'boolean') {
861       predicate = !has;
862     } // If the necessary class operation matches the current state of the
863     // element, no action is required.
864 
865 
866     if (predicate === has) {
867       return;
868     }
869 
870     if (predicate) {
871       addClass(element, classToToggle);
872     } else {
873       removeClass(element, classToToggle);
874     }
875 
876     return element;
877   }
878   /**
879    * Apply attributes to an HTML element.
880    *
881    * @param {Element} el
882    *        Element to add attributes to.
883    *
884    * @param {Object} [attributes]
885    *        Attributes to be applied.
886    */
887 
888   function setAttributes(el, attributes) {
889     Object.getOwnPropertyNames(attributes).forEach(function (attrName) {
890       var attrValue = attributes[attrName];
891 
892       if (attrValue === null || typeof attrValue === 'undefined' || attrValue === false) {
893         el.removeAttribute(attrName);
894       } else {
895         el.setAttribute(attrName, attrValue === true ? '' : attrValue);
896       }
897     });
898   }
899   /**
900    * Get an element's attribute values, as defined on the HTML tag.
901    *
902    * Attributes are not the same as properties. They're defined on the tag
903    * or with setAttribute.
904    *
905    * @param  {Element} tag
906    *         Element from which to get tag attributes.
907    *
908    * @return {Object}
909    *         All attributes of the element. Boolean attributes will be `true` or
910    *         `false`, others will be strings.
911    */
912 
913   function getAttributes(tag) {
914     var obj = {}; // known boolean attributes
915     // we can check for matching boolean properties, but not all browsers
916     // and not all tags know about these attributes, so, we still want to check them manually
917 
918     var knownBooleans = ',' + 'autoplay,controls,playsinline,loop,muted,default,defaultMuted' + ',';
919 
920     if (tag && tag.attributes && tag.attributes.length > 0) {
921       var attrs = tag.attributes;
922 
923       for (var i = attrs.length - 1; i >= 0; i--) {
924         var attrName = attrs[i].name;
925         var attrVal = attrs[i].value; // check for known booleans
926         // the matching element property will return a value for typeof
927 
928         if (typeof tag[attrName] === 'boolean' || knownBooleans.indexOf(',' + attrName + ',') !== -1) {
929           // the value of an included boolean attribute is typically an empty
930           // string ('') which would equal false if we just check for a false value.
931           // we also don't want support bad code like autoplay='false'
932           attrVal = attrVal !== null ? true : false;
933         }
934 
935         obj[attrName] = attrVal;
936       }
937     }
938 
939     return obj;
940   }
941   /**
942    * Get the value of an element's attribute.
943    *
944    * @param {Element} el
945    *        A DOM element.
946    *
947    * @param {string} attribute
948    *        Attribute to get the value of.
949    *
950    * @return {string}
951    *         The value of the attribute.
952    */
953 
954   function getAttribute(el, attribute) {
955     return el.getAttribute(attribute);
956   }
957   /**
958    * Set the value of an element's attribute.
959    *
960    * @param {Element} el
961    *        A DOM element.
962    *
963    * @param {string} attribute
964    *        Attribute to set.
965    *
966    * @param {string} value
967    *        Value to set the attribute to.
968    */
969 
970   function setAttribute(el, attribute, value) {
971     el.setAttribute(attribute, value);
972   }
973   /**
974    * Remove an element's attribute.
975    *
976    * @param {Element} el
977    *        A DOM element.
978    *
979    * @param {string} attribute
980    *        Attribute to remove.
981    */
982 
983   function removeAttribute(el, attribute) {
984     el.removeAttribute(attribute);
985   }
986   /**
987    * Attempt to block the ability to select text.
988    */
989 
990   function blockTextSelection() {
991     document.body.focus();
992 
993     document.onselectstart = function () {
994       return false;
vendor: 5,520 bytes, lines 995-1186
995     };
996   }
997   /**
998    * Turn off text selection blocking.
999    */
1000 
1001   function unblockTextSelection() {
1002     document.onselectstart = function () {
1003       return true;
1004     };
1005   }
1006   /**
1007    * Identical to the native `getBoundingClientRect` function, but ensures that
1008    * the method is supported at all (it is in all browsers we claim to support)
1009    * and that the element is in the DOM before continuing.
1010    *
1011    * This wrapper function also shims properties which are not provided by some
1012    * older browsers (namely, IE8).
1013    *
1014    * Additionally, some browsers do not support adding properties to a
1015    * `ClientRect`/`DOMRect` object; so, we shallow-copy it with the standard
1016    * properties (except `x` and `y` which are not widely supported). This helps
1017    * avoid implementations where keys are non-enumerable.
1018    *
1019    * @param  {Element} el
1020    *         Element whose `ClientRect` we want to calculate.
1021    *
1022    * @return {Object|undefined}
1023    *         Always returns a plain object - or `undefined` if it cannot.
1024    */
1025 
1026   function getBoundingClientRect(el) {
1027     if (el && el.getBoundingClientRect && el.parentNode) {
1028       var rect = el.getBoundingClientRect();
1029       var result = {};
1030       ['bottom', 'height', 'left', 'right', 'top', 'width'].forEach(function (k) {
1031         if (rect[k] !== undefined) {
1032           result[k] = rect[k];
1033         }
1034       });
1035 
1036       if (!result.height) {
1037         result.height = parseFloat(computedStyle(el, 'height'));
1038       }
1039 
1040       if (!result.width) {
1041         result.width = parseFloat(computedStyle(el, 'width'));
1042       }
1043 
1044       return result;
1045     }
1046   }
1047   /**
1048    * Represents the position of a DOM element on the page.
1049    *
1050    * @typedef  {Object} module:dom~Position
1051    *
1052    * @property {number} left
1053    *           Pixels to the left.
1054    *
1055    * @property {number} top
1056    *           Pixels from the top.
1057    */
1058 
1059   /**
1060    * Get the position of an element in the DOM.
1061    *
1062    * Uses `getBoundingClientRect` technique from John Resig.
1063    *
1064    * @see http://ejohn.org/blog/getboundingclientrect-is-awesome/
1065    *
1066    * @param  {Element} el
1067    *         Element from which to get offset.
1068    *
1069    * @return {module:dom~Position}
1070    *         The position of the element that was passed in.
1071    */
1072 
1073   function findPosition(el) {
1074     var box;
1075 
1076     if (el.getBoundingClientRect && el.parentNode) {
1077       box = el.getBoundingClientRect();
1078     }
1079 
1080     if (!box) {
1081       return {
1082         left: 0,
1083         top: 0
1084       };
1085     }
1086 
1087     var docEl = document.documentElement;
1088     var body = document.body;
1089     var clientLeft = docEl.clientLeft || body.clientLeft || 0;
1090     var scrollLeft = window$1.pageXOffset || body.scrollLeft;
1091     var left = box.left + scrollLeft - clientLeft;
1092     var clientTop = docEl.clientTop || body.clientTop || 0;
1093     var scrollTop = window$1.pageYOffset || body.scrollTop;
1094     var top = box.top + scrollTop - clientTop; // Android sometimes returns slightly off decimal values, so need to round
1095 
1096     return {
1097       left: Math.round(left),
1098       top: Math.round(top)
1099     };
1100   }
1101   /**
1102    * Represents x and y coordinates for a DOM element or mouse pointer.
1103    *
1104    * @typedef  {Object} module:dom~Coordinates
1105    *
1106    * @property {number} x
1107    *           x coordinate in pixels
1108    *
1109    * @property {number} y
1110    *           y coordinate in pixels
1111    */
1112 
1113   /**
1114    * Get the pointer position within an element.
1115    *
1116    * The base on the coordinates are the bottom left of the element.
1117    *
1118    * @param  {Element} el
1119    *         Element on which to get the pointer position on.
1120    *
1121    * @param  {EventTarget~Event} event
1122    *         Event object.
1123    *
1124    * @return {module:dom~Coordinates}
1125    *         A coordinates object corresponding to the mouse position.
1126    *
1127    */
1128 
1129   function getPointerPosition(el, event) {
1130     var position = {};
1131     var box = findPosition(el);
1132     var boxW = el.offsetWidth;
1133     var boxH = el.offsetHeight;
1134     var boxY = box.top;
1135     var boxX = box.left;
1136     var pageY = event.pageY;
1137     var pageX = event.pageX;
1138 
1139     if (event.changedTouches) {
1140       pageX = event.changedTouches[0].pageX;
1141       pageY = event.changedTouches[0].pageY;
1142     }
1143 
1144     position.y = Math.max(0, Math.min(1, (boxY - pageY + boxH) / boxH));
1145     position.x = Math.max(0, Math.min(1, (pageX - boxX) / boxW));
1146     return position;
1147   }
1148   /**
1149    * Determines, via duck typing, whether or not a value is a text node.
1150    *
1151    * @param  {Mixed} value
1152    *         Check if this value is a text node.
1153    *
1154    * @return {boolean}
1155    *         Will be `true` if the value is a text node, `false` otherwise.
1156    */
1157 
1158   function isTextNode(value) {
1159     return isObject(value) && value.nodeType === 3;
1160   }
1161   /**
1162    * Empties the contents of an element.
1163    *
1164    * @param  {Element} el
1165    *         The element to empty children from
1166    *
1167    * @return {Element}
1168    *         The element with no children
1169    */
1170 
1171   function emptyEl(el) {
1172     while (el.firstChild) {
1173       el.removeChild(el.firstChild);
1174     }
1175 
1176     return el;
1177   }
1178   /**
1179    * This is a mixed value that describes content to be injected into the DOM
1180    * via some method. It can be of the following types:
1181    *
1182    * Type       | Description
1183    * -----------|-------------
1184    * `string`   | The value will be normalized into a text node.
1185    * `Element`  | The value will be accepted as-is.
1186    * `TextNode` | The value will be accepted as-is.
1187    * `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).
1188    * `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.
1189    *
1190    * @typedef {string|Element|TextNode|Array|Function} module:dom~ContentDescriptor
1191    */
1192 
1193   /**
1194    * Normalizes content for eventual insertion into the DOM.
1195    *
1196    * This allows a wide range of content definition methods, but helps protect
1197    * from falling into the trap of simply writing to `innerHTML`, which could
1198    * be an XSS concern.
1199    *
1200    * The content for an element can be passed in multiple types and
1201    * combinations, whose behavior is as follows:
1202    *
1203    * @param {module:dom~ContentDescriptor} content
1204    *        A content descriptor value.
1205    *
1206    * @return {Array}
1207    *         All of the content that was passed in, normalized to an array of
1208    *         elements or text nodes.
1209    */
1210 
1211   function normalizeContent(content) {
1212     // First, invoke content if it is a function. If it produces an array,
1213     // that needs to happen before normalization.
1214     if (typeof content === 'function') {
1215       content = content();
1216     } // Next up, normalize to an array, so one or many items can be normalized,
1217     // filtered, and returned.
1218 
1219 
1220     return (Array.isArray(content) ? content : [content]).map(function (value) {
1221       // First, invoke value if it is a function to produce a new value,
1222       // which will be subsequently normalized to a Node of some kind.
1223       if (typeof value === 'function') {
1224         value = value();
1225       }
1226 
1227       if (isEl(value) || isTextNode(value)) {
1228         return value;
1229       }
1230 
1231       if (typeof value === 'string' && /\S/.test(value)) {
1232         return document.createTextNode(value);
1233       }
1234     }).filter(function (value) {
1235       return value;
1236     });
1237   }
1238   /**
1239    * Normalizes and appends content to an element.
1240    *
1241    * @param  {Element} el
1242    *         Element to append normalized content to.
1243    *
1244    * @param {module:dom~ContentDescriptor} content
1245    *        A content descriptor value.
1246    *
1247    * @return {Element}
1248    *         The element with appended normalized content.
1249    */
1250 
1251   function appendContent(el, content) {
1252     normalizeContent(content).forEach(function (node) {
1253       return el.appendChild(node);
1254     });
1255     return el;
1256   }
1257   /**
1258    * Normalizes and inserts content into an element; this is identical to
1259    * `appendContent()`, except it empties the element first.
1260    *
1261    * @param {Element} el
1262    *        Element to insert normalized content into.
1263    *
1264    * @param {module:dom~ContentDescriptor} content
1265    *        A content descriptor value.
1266    *
1267    * @return {Element}
1268    *         The element with inserted normalized content.
1269    */
1270 
1271   function insertContent(el, content) {
1272     return appendContent(emptyEl(el), content);
1273   }
1274   /**
1275    * Check if an event was a single left click.
1276    *
1277    * @param  {EventTarget~Event} event
1278    *         Event object.
1279    *
1280    * @return {boolean}
1281    *         Will be `true` if a single left click, `false` otherwise.
1282    */
1283 
1284   function isSingleLeftClick(event) {
1285     // Note: if you create something draggable, be sure to
1286     // call it on both `mousedown` and `mousemove` event,
1287     // otherwise `mousedown` should be enough for a button
1288     if (event.button === undefined && event.buttons === undefined) {
1289       // Why do we need `buttons` ?
1290       // Because, middle mouse sometimes have this:
1291       // e.button === 0 and e.buttons === 4
1292       // Furthermore, we want to prevent combination click, something like
1293       // HOLD middlemouse then left click, that would be
1294       // e.button === 0, e.buttons === 5
1295       // just `button` is not gonna work
1296       // Alright, then what this block does ?
1297       // this is for chrome `simulate mobile devices`
vendor: 37,694 bytes, lines 1298-2565
1298       // I want to support this as well
1299       return true;
1300     }
1301 
1302     if (event.button === 0 && event.buttons === undefined) {
1303       // Touch screen, sometimes on some specific device, `buttons`
1304       // doesn't have anything (safari on ios, blackberry...)
1305       return true;
1306     } // `mouseup` event on a single left click has
1307     // `button` and `buttons` equal to 0
1308 
1309 
1310     if (event.type === 'mouseup' && event.button === 0 && event.buttons === 0) {
1311       return true;
1312     }
1313 
1314     if (event.button !== 0 || event.buttons !== 1) {
1315       // This is the reason we have those if else block above
1316       // if any special case we can catch and let it slide
1317       // we do it above, when get to here, this definitely
1318       // is-not-left-click
1319       return false;
1320     }
1321 
1322     return true;
1323   }
1324   /**
1325    * Finds a single DOM element matching `selector` within the optional
1326    * `context` of another DOM element (defaulting to `document`).
1327    *
1328    * @param  {string} selector
1329    *         A valid CSS selector, which will be passed to `querySelector`.
1330    *
1331    * @param  {Element|String} [context=document]
1332    *         A DOM element within which to query. Can also be a selector
1333    *         string in which case the first matching element will be used
1334    *         as context. If missing (or no element matches selector), falls
1335    *         back to `document`.
1336    *
1337    * @return {Element|null}
1338    *         The element that was found or null.
1339    */
1340 
1341   var $ = createQuerier('querySelector');
1342   /**
1343    * Finds a all DOM elements matching `selector` within the optional
1344    * `context` of another DOM element (defaulting to `document`).
1345    *
1346    * @param  {string} selector
1347    *         A valid CSS selector, which will be passed to `querySelectorAll`.
1348    *
1349    * @param  {Element|String} [context=document]
1350    *         A DOM element within which to query. Can also be a selector
1351    *         string in which case the first matching element will be used
1352    *         as context. If missing (or no element matches selector), falls
1353    *         back to `document`.
1354    *
1355    * @return {NodeList}
1356    *         A element list of elements that were found. Will be empty if none
1357    *         were found.
1358    *
1359    */
1360 
1361   var $$ = createQuerier('querySelectorAll');
1362 
1363   var Dom = /*#__PURE__*/Object.freeze({
1364     isReal: isReal,
1365     isEl: isEl,
1366     isInFrame: isInFrame,
1367     createEl: createEl,
1368     textContent: textContent,
1369     prependTo: prependTo,
1370     hasClass: hasClass,
1371     addClass: addClass,
1372     removeClass: removeClass,
1373     toggleClass: toggleClass,
1374     setAttributes: setAttributes,
1375     getAttributes: getAttributes,
1376     getAttribute: getAttribute,
1377     setAttribute: setAttribute,
1378     removeAttribute: removeAttribute,
1379     blockTextSelection: blockTextSelection,
1380     unblockTextSelection: unblockTextSelection,
1381     getBoundingClientRect: getBoundingClientRect,
1382     findPosition: findPosition,
1383     getPointerPosition: getPointerPosition,
1384     isTextNode: isTextNode,
1385     emptyEl: emptyEl,
1386     normalizeContent: normalizeContent,
1387     appendContent: appendContent,
1388     insertContent: insertContent,
1389     isSingleLeftClick: isSingleLeftClick,
1390     $: $,
1391     $$: $$
1392   });
1393 
1394   /**
1395    * @file setup.js - Functions for setting up a player without
1396    * user interaction based on the data-setup `attribute` of the video tag.
1397    *
1398    * @module setup
1399    */
1400   var _windowLoaded = false;
1401   var videojs;
1402   /**
1403    * Set up any tags that have a data-setup `attribute` when the player is started.
1404    */
1405 
1406   var autoSetup = function autoSetup() {
1407     // Protect against breakage in non-browser environments and check global autoSetup option.
1408     if (!isReal() || videojs.options.autoSetup === false) {
1409       return;
1410     }
1411 
1412     var vids = Array.prototype.slice.call(document.getElementsByTagName('video'));
1413     var audios = Array.prototype.slice.call(document.getElementsByTagName('audio'));
1414     var divs = Array.prototype.slice.call(document.getElementsByTagName('video-js'));
1415     var mediaEls = vids.concat(audios, divs); // Check if any media elements exist
1416 
1417     if (mediaEls && mediaEls.length > 0) {
1418       for (var i = 0, e = mediaEls.length; i < e; i++) {
1419         var mediaEl = mediaEls[i]; // Check if element exists, has getAttribute func.
1420 
1421         if (mediaEl && mediaEl.getAttribute) {
1422           // Make sure this player hasn't already been set up.
1423           if (mediaEl.player === undefined) {
1424             var options = mediaEl.getAttribute('data-setup'); // Check if data-setup attr exists.
1425             // We only auto-setup if they've added the data-setup attr.
1426 
1427             if (options !== null) {
1428               // Create new video.js instance.
1429               videojs(mediaEl);
1430             }
1431           } // If getAttribute isn't defined, we need to wait for the DOM.
1432 
1433         } else {
1434           autoSetupTimeout(1);
1435           break;
1436         }
1437       } // No videos were found, so keep looping unless page is finished loading.
1438 
1439     } else if (!_windowLoaded) {
1440       autoSetupTimeout(1);
1441     }
1442   };
1443   /**
1444    * Wait until the page is loaded before running autoSetup. This will be called in
1445    * autoSetup if `hasLoaded` returns false.
1446    *
1447    * @param {number} wait
1448    *        How long to wait in ms
1449    *
1450    * @param {module:videojs} [vjs]
1451    *        The videojs library function
1452    */
1453 
1454 
1455   function autoSetupTimeout(wait, vjs) {
1456     if (vjs) {
1457       videojs = vjs;
1458     }
1459 
1460     window$1.setTimeout(autoSetup, wait);
1461   }
1462   /**
1463    * Used to set the internal tracking of window loaded state to true.
1464    *
1465    * @private
1466    */
1467 
1468 
1469   function setWindowLoaded() {
1470     _windowLoaded = true;
1471     window$1.removeEventListener('load', setWindowLoaded);
1472   }
1473 
1474   if (isReal()) {
1475     if (document.readyState === 'complete') {
1476       setWindowLoaded();
1477     } else {
1478       /**
1479        * Listen for the load event on window, and set _windowLoaded to true.
1480        *
1481        * We use a standard event listener here to avoid incrementing the GUID
1482        * before any players are created.
1483        *
1484        * @listens load
1485        */
1486       window$1.addEventListener('load', setWindowLoaded);
1487     }
1488   }
1489 
1490   /**
1491    * @file stylesheet.js
1492    * @module stylesheet
1493    */
1494   /**
1495    * Create a DOM syle element given a className for it.
1496    *
1497    * @param {string} className
1498    *        The className to add to the created style element.
1499    *
1500    * @return {Element}
1501    *         The element that was created.
1502    */
1503 
1504   var createStyleElement = function createStyleElement(className) {
1505     var style = document.createElement('style');
1506     style.className = className;
1507     return style;
1508   };
1509   /**
1510    * Add text to a DOM element.
1511    *
1512    * @param {Element} el
1513    *        The Element to add text content to.
1514    *
1515    * @param {string} content
1516    *        The text to add to the element.
1517    */
1518 
1519   var setTextContent = function setTextContent(el, content) {
1520     if (el.styleSheet) {
1521       el.styleSheet.cssText = content;
1522     } else {
1523       el.textContent = content;
1524     }
1525   };
1526 
1527   /**
1528    * @file guid.js
1529    * @module guid
1530    */
1531   // Default value for GUIDs. This allows us to reset the GUID counter in tests.
1532   //
1533   // The initial GUID is 3 because some users have come to rely on the first
1534   // default player ID ending up as `vjs_video_3`.
1535   //
1536   // See: https://github.com/videojs/video.js/pull/6216
1537   var _initialGuid = 3;
1538   /**
1539    * Unique ID for an element or function
1540    *
1541    * @type {Number}
1542    */
1543 
1544   var _guid = _initialGuid;
1545   /**
1546    * Get a unique auto-incrementing ID by number that has not been returned before.
1547    *
1548    * @return {number}
1549    *         A new unique ID.
1550    */
1551 
1552   function newGUID() {
1553     return _guid++;
1554   }
1555 
1556   /**
1557    * @file dom-data.js
1558    * @module dom-data
1559    */
1560   var FakeWeakMap;
1561 
1562   if (!window$1.WeakMap) {
1563     FakeWeakMap =
1564     /*#__PURE__*/
1565     function () {
1566       function FakeWeakMap() {
1567         this.vdata = 'vdata' + Math.floor(window$1.performance && window$1.performance.now() || Date.now());
1568         this.data = {};
1569       }
1570 
1571       var _proto = FakeWeakMap.prototype;
1572 
1573       _proto.set = function set(key, value) {
1574         var access = key[this.vdata] || newGUID();
1575 
1576         if (!key[this.vdata]) {
1577           key[this.vdata] = access;
1578         }
1579 
1580         this.data[access] = value;
1581         return this;
1582       };
1583 
1584       _proto.get = function get(key) {
1585         var access = key[this.vdata]; // we have data, return it
1586 
1587         if (access) {
1588           return this.data[access];
1589         } // we don't have data, return nothing.
1590         // return undefined explicitly as that's the contract for this method
1591 
1592 
1593         log('We have no data for this element', key);
1594         return undefined;
1595       };
1596 
1597       _proto.has = function has(key) {
1598         var access = key[this.vdata];
1599         return access in this.data;
1600       };
1601 
1602       _proto["delete"] = function _delete(key) {
1603         var access = key[this.vdata];
1604 
1605         if (access) {
1606           delete this.data[access];
1607           delete key[this.vdata];
1608         }
1609       };
1610 
1611       return FakeWeakMap;
1612     }();
1613   }
1614   /**
1615    * Element Data Store.
1616    *
1617    * Allows for binding data to an element without putting it directly on the
1618    * element. Ex. Event listeners are stored here.
1619    * (also from jsninja.com, slightly modified and updated for closure compiler)
1620    *
1621    * @type {Object}
1622    * @private
1623    */
1624 
1625 
1626   var DomData = window$1.WeakMap ? new WeakMap() : new FakeWeakMap();
1627 
1628   /**
1629    * @file events.js. An Event System (John Resig - Secrets of a JS Ninja http://jsninja.com/)
1630    * (Original book version wasn't completely usable, so fixed some things and made Closure Compiler compatible)
1631    * This should work very similarly to jQuery's events, however it's based off the book version which isn't as
1632    * robust as jquery's, so there's probably some differences.
1633    *
1634    * @file events.js
1635    * @module events
1636    */
1637   /**
1638    * Clean up the listener cache and dispatchers
1639    *
1640    * @param {Element|Object} elem
1641    *        Element to clean up
1642    *
1643    * @param {string} type
1644    *        Type of event to clean up
1645    */
1646 
1647   function _cleanUpEvents(elem, type) {
1648     if (!DomData.has(elem)) {
1649       return;
1650     }
1651 
1652     var data = DomData.get(elem); // Remove the events of a particular type if there are none left
1653 
1654     if (data.handlers[type].length === 0) {
1655       delete data.handlers[type]; // data.handlers[type] = null;
1656       // Setting to null was causing an error with data.handlers
1657       // Remove the meta-handler from the element
1658 
1659       if (elem.removeEventListener) {
1660         elem.removeEventListener(type, data.dispatcher, false);
1661       } else if (elem.detachEvent) {
1662         elem.detachEvent('on' + type, data.dispatcher);
1663       }
1664     } // Remove the events object if there are no types left
1665 
1666 
1667     if (Object.getOwnPropertyNames(data.handlers).length <= 0) {
1668       delete data.handlers;
1669       delete data.dispatcher;
1670       delete data.disabled;
1671     } // Finally remove the element data if there is no data left
1672 
1673 
1674     if (Object.getOwnPropertyNames(data).length === 0) {
1675       DomData["delete"](elem);
1676     }
1677   }
1678   /**
1679    * Loops through an array of event types and calls the requested method for each type.
1680    *
1681    * @param {Function} fn
1682    *        The event method we want to use.
1683    *
1684    * @param {Element|Object} elem
1685    *        Element or object to bind listeners to
1686    *
1687    * @param {string} type
1688    *        Type of event to bind to.
1689    *
1690    * @param {EventTarget~EventListener} callback
1691    *        Event listener.
1692    */
1693 
1694 
1695   function _handleMultipleEvents(fn, elem, types, callback) {
1696     types.forEach(function (type) {
1697       // Call the event method for each one of the types
1698       fn(elem, type, callback);
1699     });
1700   }
1701   /**
1702    * Fix a native event to have standard property values
1703    *
1704    * @param {Object} event
1705    *        Event object to fix.
1706    *
1707    * @return {Object}
1708    *         Fixed event object.
1709    */
1710 
1711 
1712   function fixEvent(event) {
1713     if (event.fixed_) {
1714       return event;
1715     }
1716 
1717     function returnTrue() {
1718       return true;
1719     }
1720 
1721     function returnFalse() {
1722       return false;
1723     } // Test if fixing up is needed
1724     // Used to check if !event.stopPropagation instead of isPropagationStopped
1725     // But native events return true for stopPropagation, but don't have
1726     // other expected methods like isPropagationStopped. Seems to be a problem
1727     // with the Javascript Ninja code. So we're just overriding all events now.
1728 
1729 
1730     if (!event || !event.isPropagationStopped) {
1731       var old = event || window$1.event;
1732       event = {}; // Clone the old object so that we can modify the values event = {};
1733       // IE8 Doesn't like when you mess with native event properties
1734       // Firefox returns false for event.hasOwnProperty('type') and other props
1735       //  which makes copying more difficult.
1736       // TODO: Probably best to create a whitelist of event props
1737 
1738       for (var key in old) {
1739         // Safari 6.0.3 warns you if you try to copy deprecated layerX/Y
1740         // Chrome warns you if you try to copy deprecated keyboardEvent.keyLocation
1741         // and webkitMovementX/Y
1742         if (key !== 'layerX' && key !== 'layerY' && key !== 'keyLocation' && key !== 'webkitMovementX' && key !== 'webkitMovementY') {
1743           // Chrome 32+ warns if you try to copy deprecated returnValue, but
1744           // we still want to if preventDefault isn't supported (IE8).
1745           if (!(key === 'returnValue' && old.preventDefault)) {
1746             event[key] = old[key];
1747           }
1748         }
1749       } // The event occurred on this element
1750 
1751 
1752       if (!event.target) {
1753         event.target = event.srcElement || document;
1754       } // Handle which other element the event is related to
1755 
1756 
1757       if (!event.relatedTarget) {
1758         event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
1759       } // Stop the default browser action
1760 
1761 
1762       event.preventDefault = function () {
1763         if (old.preventDefault) {
1764           old.preventDefault();
1765         }
1766 
1767         event.returnValue = false;
1768         old.returnValue = false;
1769         event.defaultPrevented = true;
1770       };
1771 
1772       event.defaultPrevented = false; // Stop the event from bubbling
1773 
1774       event.stopPropagation = function () {
1775         if (old.stopPropagation) {
1776           old.stopPropagation();
1777         }
1778 
1779         event.cancelBubble = true;
1780         old.cancelBubble = true;
1781         event.isPropagationStopped = returnTrue;
1782       };
1783 
1784       event.isPropagationStopped = returnFalse; // Stop the event from bubbling and executing other handlers
1785 
1786       event.stopImmediatePropagation = function () {
1787         if (old.stopImmediatePropagation) {
1788           old.stopImmediatePropagation();
1789         }
1790 
1791         event.isImmediatePropagationStopped = returnTrue;
1792         event.stopPropagation();
1793       };
1794 
1795       event.isImmediatePropagationStopped = returnFalse; // Handle mouse position
1796 
1797       if (event.clientX !== null && event.clientX !== undefined) {
1798         var doc = document.documentElement;
1799         var body = document.body;
1800         event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
1801         event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
1802       } // Handle key presses
1803 
1804 
1805       event.which = event.charCode || event.keyCode; // Fix button for mouse clicks:
1806       // 0 == left; 1 == middle; 2 == right
1807 
1808       if (event.button !== null && event.button !== undefined) {
1809         // The following is disabled because it does not pass videojs-standard
1810         // and... yikes.
1811 
1812         /* eslint-disable */
1813         event.button = event.button & 1 ? 0 : event.button & 4 ? 1 : event.button & 2 ? 2 : 0;
1814         /* eslint-enable */
1815       }
1816     }
1817 
1818     event.fixed_ = true; // Returns fixed-up instance
1819 
1820     return event;
1821   }
1822   /**
1823    * Whether passive event listeners are supported
1824    */
1825 
1826   var _supportsPassive;
1827 
1828   var supportsPassive = function supportsPassive() {
1829     if (typeof _supportsPassive !== 'boolean') {
1830       _supportsPassive = false;
1831 
1832       try {
1833         var opts = Object.defineProperty({}, 'passive', {
1834           get: function get() {
1835             _supportsPassive = true;
1836           }
1837         });
1838         window$1.addEventListener('test', null, opts);
1839         window$1.removeEventListener('test', null, opts);
1840       } catch (e) {// disregard
1841       }
1842     }
1843 
1844     return _supportsPassive;
1845   };
1846   /**
1847    * Touch events Chrome expects to be passive
1848    */
1849 
1850 
1851   var passiveEvents = ['touchstart', 'touchmove'];
1852   /**
1853    * Add an event listener to element
1854    * It stores the handler function in a separate cache object
1855    * and adds a generic handler to the element's event,
1856    * along with a unique id (guid) to the element.
1857    *
1858    * @param {Element|Object} elem
1859    *        Element or object to bind listeners to
1860    *
1861    * @param {string|string[]} type
1862    *        Type of event to bind to.
1863    *
1864    * @param {EventTarget~EventListener} fn
1865    *        Event listener.
1866    */
1867 
1868   function on(elem, type, fn) {
1869     if (Array.isArray(type)) {
1870       return _handleMultipleEvents(on, elem, type, fn);
1871     }
1872 
1873     if (!DomData.has(elem)) {
1874       DomData.set(elem, {});
1875     }
1876 
1877     var data = DomData.get(elem); // We need a place to store all our handler data
1878 
1879     if (!data.handlers) {
1880       data.handlers = {};
1881     }
1882 
1883     if (!data.handlers[type]) {
1884       data.handlers[type] = [];
1885     }
1886 
1887     if (!fn.guid) {
1888       fn.guid = newGUID();
1889     }
1890 
1891     data.handlers[type].push(fn);
1892 
1893     if (!data.dispatcher) {
1894       data.disabled = false;
1895 
1896       data.dispatcher = function (event, hash) {
1897         if (data.disabled) {
1898           return;
1899         }
1900 
1901         event = fixEvent(event);
1902         var handlers = data.handlers[event.type];
1903 
1904         if (handlers) {
1905           // Copy handlers so if handlers are added/removed during the process it doesn't throw everything off.
1906           var handlersCopy = handlers.slice(0);
1907 
1908           for (var m = 0, n = handlersCopy.length; m < n; m++) {
1909             if (event.isImmediatePropagationStopped()) {
1910               break;
1911             } else {
1912               try {
1913                 handlersCopy[m].call(elem, event, hash);
1914               } catch (e) {
1915                 log.error(e);
1916               }
1917             }
1918           }
1919         }
1920       };
1921     }
1922 
1923     if (data.handlers[type].length === 1) {
1924       if (elem.addEventListener) {
1925         var options = false;
1926 
1927         if (supportsPassive() && passiveEvents.indexOf(type) > -1) {
1928           options = {
1929             passive: true
1930           };
1931         }
1932 
1933         elem.addEventListener(type, data.dispatcher, options);
1934       } else if (elem.attachEvent) {
1935         elem.attachEvent('on' + type, data.dispatcher);
1936       }
1937     }
1938   }
1939   /**
1940    * Removes event listeners from an element
1941    *
1942    * @param {Element|Object} elem
1943    *        Object to remove listeners from.
1944    *
1945    * @param {string|string[]} [type]
1946    *        Type of listener to remove. Don't include to remove all events from element.
1947    *
1948    * @param {EventTarget~EventListener} [fn]
1949    *        Specific listener to remove. Don't include to remove listeners for an event
1950    *        type.
1951    */
1952 
1953   function off(elem, type, fn) {
1954     // Don't want to add a cache object through getElData if not needed
1955     if (!DomData.has(elem)) {
1956       return;
1957     }
1958 
1959     var data = DomData.get(elem); // If no events exist, nothing to unbind
1960 
1961     if (!data.handlers) {
1962       return;
1963     }
1964 
1965     if (Array.isArray(type)) {
1966       return _handleMultipleEvents(off, elem, type, fn);
1967     } // Utility function
1968 
1969 
1970     var removeType = function removeType(el, t) {
1971       data.handlers[t] = [];
1972 
1973       _cleanUpEvents(el, t);
1974     }; // Are we removing all bound events?
1975 
1976 
1977     if (type === undefined) {
1978       for (var t in data.handlers) {
1979         if (Object.prototype.hasOwnProperty.call(data.handlers || {}, t)) {
1980           removeType(elem, t);
1981         }
1982       }
1983 
1984       return;
1985     }
1986 
1987     var handlers = data.handlers[type]; // If no handlers exist, nothing to unbind
1988 
1989     if (!handlers) {
1990       return;
1991     } // If no listener was provided, remove all listeners for type
1992 
1993 
1994     if (!fn) {
1995       removeType(elem, type);
1996       return;
1997     } // We're only removing a single handler
1998 
1999 
2000     if (fn.guid) {
2001       for (var n = 0; n < handlers.length; n++) {
2002         if (handlers[n].guid === fn.guid) {
2003           handlers.splice(n--, 1);
2004         }
2005       }
2006     }
2007 
2008     _cleanUpEvents(elem, type);
2009   }
2010   /**
2011    * Trigger an event for an element
2012    *
2013    * @param {Element|Object} elem
2014    *        Element to trigger an event on
2015    *
2016    * @param {EventTarget~Event|string} event
2017    *        A string (the type) or an event object with a type attribute
2018    *
2019    * @param {Object} [hash]
2020    *        data hash to pass along with the event
2021    *
2022    * @return {boolean|undefined}
2023    *         Returns the opposite of `defaultPrevented` if default was
2024    *         prevented. Otherwise, returns `undefined`
2025    */
2026 
2027   function trigger(elem, event, hash) {
2028     // Fetches element data and a reference to the parent (for bubbling).
2029     // Don't want to add a data object to cache for every parent,
2030     // so checking hasElData first.
2031     var elemData = DomData.has(elem) ? DomData.get(elem) : {};
2032     var parent = elem.parentNode || elem.ownerDocument; // type = event.type || event,
2033     // handler;
2034     // If an event name was passed as a string, creates an event out of it
2035 
2036     if (typeof event === 'string') {
2037       event = {
2038         type: event,
2039         target: elem
2040       };
2041     } else if (!event.target) {
2042       event.target = elem;
2043     } // Normalizes the event properties.
2044 
2045 
2046     event = fixEvent(event); // If the passed element has a dispatcher, executes the established handlers.
2047 
2048     if (elemData.dispatcher) {
2049       elemData.dispatcher.call(elem, event, hash);
2050     } // Unless explicitly stopped or the event does not bubble (e.g. media events)
2051     // recursively calls this function to bubble the event up the DOM.
2052 
2053 
2054     if (parent && !event.isPropagationStopped() && event.bubbles === true) {
2055       trigger.call(null, parent, event, hash); // If at the top of the DOM, triggers the default action unless disabled.
2056     } else if (!parent && !event.defaultPrevented && event.target && event.target[event.type]) {
2057       if (!DomData.has(event.target)) {
2058         DomData.set(event.target, {});
2059       }
2060 
2061       var targetData = DomData.get(event.target); // Checks if the target has a default action for this event.
2062 
2063       if (event.target[event.type]) {
2064         // Temporarily disables event dispatching on the target as we have already executed the handler.
2065         targetData.disabled = true; // Executes the default action.
2066 
2067         if (typeof event.target[event.type] === 'function') {
2068           event.target[event.type]();
2069         } // Re-enables event dispatching.
2070 
2071 
2072         targetData.disabled = false;
2073       }
2074     } // Inform the triggerer if the default was prevented by returning false
2075 
2076 
2077     return !event.defaultPrevented;
2078   }
2079   /**
2080    * Trigger a listener only once for an event.
2081    *
2082    * @param {Element|Object} elem
2083    *        Element or object to bind to.
2084    *
2085    * @param {string|string[]} type
2086    *        Name/type of event
2087    *
2088    * @param {Event~EventListener} fn
2089    *        Event listener function
2090    */
2091 
2092   function one(elem, type, fn) {
2093     if (Array.isArray(type)) {
2094       return _handleMultipleEvents(one, elem, type, fn);
2095     }
2096 
2097     var func = function func() {
2098       off(elem, type, func);
2099       fn.apply(this, arguments);
2100     }; // copy the guid to the new function so it can removed using the original function's ID
2101 
2102 
2103     func.guid = fn.guid = fn.guid || newGUID();
2104     on(elem, type, func);
2105   }
2106   /**
2107    * Trigger a listener only once and then turn if off for all
2108    * configured events
2109    *
2110    * @param {Element|Object} elem
2111    *        Element or object to bind to.
2112    *
2113    * @param {string|string[]} type
2114    *        Name/type of event
2115    *
2116    * @param {Event~EventListener} fn
2117    *        Event listener function
2118    */
2119 
2120   function any(elem, type, fn) {
2121     var func = function func() {
2122       off(elem, type, func);
2123       fn.apply(this, arguments);
2124     }; // copy the guid to the new function so it can removed using the original function's ID
2125 
2126 
2127     func.guid = fn.guid = fn.guid || newGUID(); // multiple ons, but one off for everything
2128 
2129     on(elem, type, func);
2130   }
2131 
2132   var Events = /*#__PURE__*/Object.freeze({
2133     fixEvent: fixEvent,
2134     on: on,
2135     off: off,
2136     trigger: trigger,
2137     one: one,
2138     any: any
2139   });
2140 
2141   /**
2142    * @file fn.js
2143    * @module fn
2144    */
2145   var UPDATE_REFRESH_INTERVAL = 30;
2146   /**
2147    * Bind (a.k.a proxy or context). A simple method for changing the context of
2148    * a function.
2149    *
2150    * It also stores a unique id on the function so it can be easily removed from
2151    * events.
2152    *
2153    * @function
2154    * @param    {Mixed} context
2155    *           The object to bind as scope.
2156    *
2157    * @param    {Function} fn
2158    *           The function to be bound to a scope.
2159    *
2160    * @param    {number} [uid]
2161    *           An optional unique ID for the function to be set
2162    *
2163    * @return   {Function}
2164    *           The new function that will be bound into the context given
2165    */
2166 
2167   var bind = function bind(context, fn, uid) {
2168     // Make sure the function has a unique ID
2169     if (!fn.guid) {
2170       fn.guid = newGUID();
2171     } // Create the new function that changes the context
2172 
2173 
2174     var bound = fn.bind(context); // Allow for the ability to individualize this function
2175     // Needed in the case where multiple objects might share the same prototype
2176     // IF both items add an event listener with the same function, then you try to remove just one
2177     // it will remove both because they both have the same guid.
2178     // when using this, you need to use the bind method when you remove the listener as well.
2179     // currently used in text tracks
2180 
2181     bound.guid = uid ? uid + '_' + fn.guid : fn.guid;
2182     return bound;
2183   };
2184   /**
2185    * Wraps the given function, `fn`, with a new function that only invokes `fn`
2186    * at most once per every `wait` milliseconds.
2187    *
2188    * @function
2189    * @param    {Function} fn
2190    *           The function to be throttled.
2191    *
2192    * @param    {number}   wait
2193    *           The number of milliseconds by which to throttle.
2194    *
2195    * @return   {Function}
2196    */
2197 
2198   var throttle = function throttle(fn, wait) {
2199     var last = window$1.performance.now();
2200 
2201     var throttled = function throttled() {
2202       var now = window$1.performance.now();
2203 
2204       if (now - last >= wait) {
2205         fn.apply(void 0, arguments);
2206         last = now;
2207       }
2208     };
2209 
2210     return throttled;
2211   };
2212   /**
2213    * Creates a debounced function that delays invoking `func` until after `wait`
2214    * milliseconds have elapsed since the last time the debounced function was
2215    * invoked.
2216    *
2217    * Inspired by lodash and underscore implementations.
2218    *
2219    * @function
2220    * @param    {Function} func
2221    *           The function to wrap with debounce behavior.
2222    *
2223    * @param    {number} wait
2224    *           The number of milliseconds to wait after the last invocation.
2225    *
2226    * @param    {boolean} [immediate]
2227    *           Whether or not to invoke the function immediately upon creation.
2228    *
2229    * @param    {Object} [context=window]
2230    *           The "context" in which the debounced function should debounce. For
2231    *           example, if this function should be tied to a Video.js player,
2232    *           the player can be passed here. Alternatively, defaults to the
2233    *           global `window` object.
2234    *
2235    * @return   {Function}
2236    *           A debounced function.
2237    */
2238 
2239   var debounce = function debounce(func, wait, immediate, context) {
2240     if (context === void 0) {
2241       context = window$1;
2242     }
2243 
2244     var timeout;
2245 
2246     var cancel = function cancel() {
2247       context.clearTimeout(timeout);
2248       timeout = null;
2249     };
2250     /* eslint-disable consistent-this */
2251 
2252 
2253     var debounced = function debounced() {
2254       var self = this;
2255       var args = arguments;
2256 
2257       var _later = function later() {
2258         timeout = null;
2259         _later = null;
2260 
2261         if (!immediate) {
2262           func.apply(self, args);
2263         }
2264       };
2265 
2266       if (!timeout && immediate) {
2267         func.apply(self, args);
2268       }
2269 
2270       context.clearTimeout(timeout);
2271       timeout = context.setTimeout(_later, wait);
2272     };
2273     /* eslint-enable consistent-this */
2274 
2275 
2276     debounced.cancel = cancel;
2277     return debounced;
2278   };
2279 
2280   /**
2281    * @file src/js/event-target.js
2282    */
2283   /**
2284    * `EventTarget` is a class that can have the same API as the DOM `EventTarget`. It
2285    * adds shorthand functions that wrap around lengthy functions. For example:
2286    * the `on` function is a wrapper around `addEventListener`.
2287    *
2288    * @see [EventTarget Spec]{@link https://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-EventTarget}
2289    * @class EventTarget
2290    */
2291 
2292   var EventTarget = function EventTarget() {};
2293   /**
2294    * A Custom DOM event.
2295    *
2296    * @typedef {Object} EventTarget~Event
2297    * @see [Properties]{@link https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent}
2298    */
2299 
2300   /**
2301    * All event listeners should follow the following format.
2302    *
2303    * @callback EventTarget~EventListener
2304    * @this {EventTarget}
2305    *
2306    * @param {EventTarget~Event} event
2307    *        the event that triggered this function
2308    *
2309    * @param {Object} [hash]
2310    *        hash of data sent during the event
2311    */
2312 
2313   /**
2314    * An object containing event names as keys and booleans as values.
2315    *
2316    * > NOTE: If an event name is set to a true value here {@link EventTarget#trigger}
2317    *         will have extra functionality. See that function for more information.
2318    *
2319    * @property EventTarget.prototype.allowedEvents_
2320    * @private
2321    */
2322 
2323 
2324   EventTarget.prototype.allowedEvents_ = {};
2325   /**
2326    * Adds an `event listener` to an instance of an `EventTarget`. An `event listener` is a
2327    * function that will get called when an event with a certain name gets triggered.
2328    *
2329    * @param {string|string[]} type
2330    *        An event name or an array of event names.
2331    *
2332    * @param {EventTarget~EventListener} fn
2333    *        The function to call with `EventTarget`s
2334    */
2335 
2336   EventTarget.prototype.on = function (type, fn) {
2337     // Remove the addEventListener alias before calling Events.on
2338     // so we don't get into an infinite type loop
2339     var ael = this.addEventListener;
2340 
2341     this.addEventListener = function () {};
2342 
2343     on(this, type, fn);
2344     this.addEventListener = ael;
2345   };
2346   /**
2347    * An alias of {@link EventTarget#on}. Allows `EventTarget` to mimic
2348    * the standard DOM API.
2349    *
2350    * @function
2351    * @see {@link EventTarget#on}
2352    */
2353 
2354 
2355   EventTarget.prototype.addEventListener = EventTarget.prototype.on;
2356   /**
2357    * Removes an `event listener` for a specific event from an instance of `EventTarget`.
2358    * This makes it so that the `event listener` will no longer get called when the
2359    * named event happens.
2360    *
2361    * @param {string|string[]} type
2362    *        An event name or an array of event names.
2363    *
2364    * @param {EventTarget~EventListener} fn
2365    *        The function to remove.
2366    */
2367 
2368   EventTarget.prototype.off = function (type, fn) {
2369     off(this, type, fn);
2370   };
2371   /**
2372    * An alias of {@link EventTarget#off}. Allows `EventTarget` to mimic
2373    * the standard DOM API.
2374    *
2375    * @function
2376    * @see {@link EventTarget#off}
2377    */
2378 
2379 
2380   EventTarget.prototype.removeEventListener = EventTarget.prototype.off;
2381   /**
2382    * This function will add an `event listener` that gets triggered only once. After the
2383    * first trigger it will get removed. This is like adding an `event listener`
2384    * with {@link EventTarget#on} that calls {@link EventTarget#off} on itself.
2385    *
2386    * @param {string|string[]} type
2387    *        An event name or an array of event names.
2388    *
2389    * @param {EventTarget~EventListener} fn
2390    *        The function to be called once for each event name.
2391    */
2392 
2393   EventTarget.prototype.one = function (type, fn) {
2394     // Remove the addEventListener aliasing Events.on
2395     // so we don't get into an infinite type loop
2396     var ael = this.addEventListener;
2397 
2398     this.addEventListener = function () {};
2399 
2400     one(this, type, fn);
2401     this.addEventListener = ael;
2402   };
2403 
2404   EventTarget.prototype.any = function (type, fn) {
2405     // Remove the addEventListener aliasing Events.on
2406     // so we don't get into an infinite type loop
2407     var ael = this.addEventListener;
2408 
2409     this.addEventListener = function () {};
2410 
2411     any(this, type, fn);
2412     this.addEventListener = ael;
2413   };
2414   /**
2415    * This function causes an event to happen. This will then cause any `event listeners`
2416    * that are waiting for that event, to get called. If there are no `event listeners`
2417    * for an event then nothing will happen.
2418    *
2419    * If the name of the `Event` that is being triggered is in `EventTarget.allowedEvents_`.
2420    * Trigger will also call the `on` + `uppercaseEventName` function.
2421    *
2422    * Example:
2423    * 'click' is in `EventTarget.allowedEvents_`, so, trigger will attempt to call
2424    * `onClick` if it exists.
2425    *
2426    * @param {string|EventTarget~Event|Object} event
2427    *        The name of the event, an `Event`, or an object with a key of type set to
2428    *        an event name.
2429    */
2430 
2431 
2432   EventTarget.prototype.trigger = function (event) {
2433     var type = event.type || event; // deprecation
2434     // In a future version we should default target to `this`
2435     // similar to how we default the target to `elem` in
2436     // `Events.trigger`. Right now the default `target` will be
2437     // `document` due to the `Event.fixEvent` call.
2438 
2439     if (typeof event === 'string') {
2440       event = {
2441         type: type
2442       };
2443     }
2444 
2445     event = fixEvent(event);
2446 
2447     if (this.allowedEvents_[type] && this['on' + type]) {
2448       this['on' + type](event);
2449     }
2450 
2451     trigger(this, event);
2452   };
2453   /**
2454    * An alias of {@link EventTarget#trigger}. Allows `EventTarget` to mimic
2455    * the standard DOM API.
2456    *
2457    * @function
2458    * @see {@link EventTarget#trigger}
2459    */
2460 
2461 
2462   EventTarget.prototype.dispatchEvent = EventTarget.prototype.trigger;
2463   var EVENT_MAP;
2464 
2465   EventTarget.prototype.queueTrigger = function (event) {
2466     var _this = this;
2467 
2468     // only set up EVENT_MAP if it'll be used
2469     if (!EVENT_MAP) {
2470       EVENT_MAP = new Map();
2471     }
2472 
2473     var type = event.type || event;
2474     var map = EVENT_MAP.get(this);
2475 
2476     if (!map) {
2477       map = new Map();
2478       EVENT_MAP.set(this, map);
2479     }
2480 
2481     var oldTimeout = map.get(type);
2482     map["delete"](type);
2483     window$1.clearTimeout(oldTimeout);
2484     var timeout = window$1.setTimeout(function () {
2485       // if we cleared out all timeouts for the current target, delete its map
2486       if (map.size === 0) {
2487         map = null;
2488         EVENT_MAP["delete"](_this);
2489       }
2490 
2491       _this.trigger(event);
2492     }, 0);
2493     map.set(type, timeout);
2494   };
2495 
2496   /**
2497    * @file mixins/evented.js
2498    * @module evented
2499    */
2500   /**
2501    * Returns whether or not an object has had the evented mixin applied.
2502    *
2503    * @param  {Object} object
2504    *         An object to test.
2505    *
2506    * @return {boolean}
2507    *         Whether or not the object appears to be evented.
2508    */
2509 
2510   var isEvented = function isEvented(object) {
2511     return object instanceof EventTarget || !!object.eventBusEl_ && ['on', 'one', 'off', 'trigger'].every(function (k) {
2512       return typeof object[k] === 'function';
2513     });
2514   };
2515   /**
2516    * Adds a callback to run after the evented mixin applied.
2517    *
2518    * @param  {Object} object
2519    *         An object to Add
2520    * @param  {Function} callback
2521    *         The callback to run.
2522    */
2523 
2524 
2525   var addEventedCallback = function addEventedCallback(target, callback) {
2526     if (isEvented(target)) {
2527       callback();
2528     } else {
2529       if (!target.eventedCallbacks) {
2530         target.eventedCallbacks = [];
2531       }
2532 
2533       target.eventedCallbacks.push(callback);
2534     }
2535   };
2536   /**
2537    * Whether a value is a valid event type - non-empty string or array.
2538    *
2539    * @private
2540    * @param  {string|Array} type
2541    *         The type value to test.
2542    *
2543    * @return {boolean}
2544    *         Whether or not the type is a valid event type.
2545    */
2546 
2547 
2548   var isValidEventType = function isValidEventType(type) {
2549     return (// The regex here verifies that the `type` contains at least one non-
2550       // whitespace character.
2551       typeof type === 'string' && /\S/.test(type) || Array.isArray(type) && !!type.length
2552     );
2553   };
2554   /**
2555    * Validates a value to determine if it is a valid event target. Throws if not.
2556    *
2557    * @private
2558    * @throws {Error}
2559    *         If the target does not appear to be a valid event target.
2560    *
2561    * @param  {Object} target
2562    *         The object to test.
2563    */
2564 
2565 
vendor: 6,489 bytes, lines 2566-2759
2566   var validateTarget = function validateTarget(target) {
2567     if (!target.nodeName && !isEvented(target)) {
2568       throw new Error('Invalid target; must be a DOM node or evented object.');
2569     }
2570   };
2571   /**
2572    * Validates a value to determine if it is a valid event target. Throws if not.
2573    *
2574    * @private
2575    * @throws {Error}
2576    *         If the type does not appear to be a valid event type.
2577    *
2578    * @param  {string|Array} type
2579    *         The type to test.
2580    */
2581 
2582 
2583   var validateEventType = function validateEventType(type) {
2584     if (!isValidEventType(type)) {
2585       throw new Error('Invalid event type; must be a non-empty string or array.');
2586     }
2587   };
2588   /**
2589    * Validates a value to determine if it is a valid listener. Throws if not.
2590    *
2591    * @private
2592    * @throws {Error}
2593    *         If the listener is not a function.
2594    *
2595    * @param  {Function} listener
2596    *         The listener to test.
2597    */
2598 
2599 
2600   var validateListener = function validateListener(listener) {
2601     if (typeof listener !== 'function') {
2602       throw new Error('Invalid listener; must be a function.');
2603     }
2604   };
2605   /**
2606    * Takes an array of arguments given to `on()` or `one()`, validates them, and
2607    * normalizes them into an object.
2608    *
2609    * @private
2610    * @param  {Object} self
2611    *         The evented object on which `on()` or `one()` was called. This
2612    *         object will be bound as the `this` value for the listener.
2613    *
2614    * @param  {Array} args
2615    *         An array of arguments passed to `on()` or `one()`.
2616    *
2617    * @return {Object}
2618    *         An object containing useful values for `on()` or `one()` calls.
2619    */
2620 
2621 
2622   var normalizeListenArgs = function normalizeListenArgs(self, args) {
2623     // If the number of arguments is less than 3, the target is always the
2624     // evented object itself.
2625     var isTargetingSelf = args.length < 3 || args[0] === self || args[0] === self.eventBusEl_;
2626     var target;
2627     var type;
2628     var listener;
2629 
2630     if (isTargetingSelf) {
2631       target = self.eventBusEl_; // Deal with cases where we got 3 arguments, but we are still listening to
2632       // the evented object itself.
2633 
2634       if (args.length >= 3) {
2635         args.shift();
2636       }
2637 
2638       type = args[0];
2639       listener = args[1];
2640     } else {
2641       target = args[0];
2642       type = args[1];
2643       listener = args[2];
2644     }
2645 
2646     validateTarget(target);
2647     validateEventType(type);
2648     validateListener(listener);
2649     listener = bind(self, listener);
2650     return {
2651       isTargetingSelf: isTargetingSelf,
2652       target: target,
2653       type: type,
2654       listener: listener
2655     };
2656   };
2657   /**
2658    * Adds the listener to the event type(s) on the target, normalizing for
2659    * the type of target.
2660    *
2661    * @private
2662    * @param  {Element|Object} target
2663    *         A DOM node or evented object.
2664    *
2665    * @param  {string} method
2666    *         The event binding method to use ("on" or "one").
2667    *
2668    * @param  {string|Array} type
2669    *         One or more event type(s).
2670    *
2671    * @param  {Function} listener
2672    *         A listener function.
2673    */
2674 
2675 
2676   var listen = function listen(target, method, type, listener) {
2677     validateTarget(target);
2678 
2679     if (target.nodeName) {
2680       Events[method](target, type, listener);
2681     } else {
2682       target[method](type, listener);
2683     }
2684   };
2685   /**
2686    * Contains methods that provide event capabilities to an object which is passed
2687    * to {@link module:evented|evented}.
2688    *
2689    * @mixin EventedMixin
2690    */
2691 
2692 
2693   var EventedMixin = {
2694     /**
2695      * Add a listener to an event (or events) on this object or another evented
2696      * object.
2697      *
2698      * @param  {string|Array|Element|Object} targetOrType
2699      *         If this is a string or array, it represents the event type(s)
2700      *         that will trigger the listener.
2701      *
2702      *         Another evented object can be passed here instead, which will
2703      *         cause the listener to listen for events on _that_ object.
2704      *
2705      *         In either case, the listener's `this` value will be bound to
2706      *         this object.
2707      *
2708      * @param  {string|Array|Function} typeOrListener
2709      *         If the first argument was a string or array, this should be the
2710      *         listener function. Otherwise, this is a string or array of event
2711      *         type(s).
2712      *
2713      * @param  {Function} [listener]
2714      *         If the first argument was another evented object, this will be
2715      *         the listener function.
2716      */
2717     on: function on() {
2718       var _this = this;
2719 
2720       for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
2721         args[_key] = arguments[_key];
2722       }
2723 
2724       var _normalizeListenArgs = normalizeListenArgs(this, args),
2725           isTargetingSelf = _normalizeListenArgs.isTargetingSelf,
2726           target = _normalizeListenArgs.target,
2727           type = _normalizeListenArgs.type,
2728           listener = _normalizeListenArgs.listener;
2729 
2730       listen(target, 'on', type, listener); // If this object is listening to another evented object.
2731 
2732       if (!isTargetingSelf) {
2733         // If this object is disposed, remove the listener.
2734         var removeListenerOnDispose = function removeListenerOnDispose() {
2735           return _this.off(target, type, listener);
2736         }; // Use the same function ID as the listener so we can remove it later it
2737         // using the ID of the original listener.
2738 
2739 
2740         removeListenerOnDispose.guid = listener.guid; // Add a listener to the target's dispose event as well. This ensures
2741         // that if the target is disposed BEFORE this object, we remove the
2742         // removal listener that was just added. Otherwise, we create a memory leak.
2743 
2744         var removeRemoverOnTargetDispose = function removeRemoverOnTargetDispose() {
2745           return _this.off('dispose', removeListenerOnDispose);
2746         }; // Use the same function ID as the listener so we can remove it later
2747         // it using the ID of the original listener.
2748 
2749 
2750         removeRemoverOnTargetDispose.guid = listener.guid;
2751         listen(this, 'on', 'dispose', removeListenerOnDispose);
2752         listen(target, 'on', 'dispose', removeRemoverOnTargetDispose);
2753       }
2754     },
2755 
2756     /**
2757      * Add a listener to an event (or events) on this object or another evented
2758      * object. The listener will be called once per event and then removed.
2759      *
2760      * @param  {string|Array|Element|Object} targetOrType
2761      *         If this is a string or array, it represents the event type(s)
2762      *         that will trigger the listener.
2763      *
2764      *         Another evented object can be passed here instead, which will
2765      *         cause the listener to listen for events on _that_ object.
2766      *
2767      *         In either case, the listener's `this` value will be bound to
2768      *         this object.
2769      *
2770      * @param  {string|Array|Function} typeOrListener
2771      *         If the first argument was a string or array, this should be the
2772      *         listener function. Otherwise, this is a string or array of event
2773      *         type(s).
2774      *
2775      * @param  {Function} [listener]
2776      *         If the first argument was another evented object, this will be
2777      *         the listener function.
2778      */
2779     one: function one() {
2780       var _this2 = this;
2781 
2782       for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
2783         args[_key2] = arguments[_key2];
2784       }
2785 
2786       var _normalizeListenArgs2 = normalizeListenArgs(this, args),
2787           isTargetingSelf = _normalizeListenArgs2.isTargetingSelf,
2788           target = _normalizeListenArgs2.target,
2789           type = _normalizeListenArgs2.type,
2790           listener = _normalizeListenArgs2.listener; // Targeting this evented object.
2791 
2792 
2793       if (isTargetingSelf) {
2794         listen(target, 'one', type, listener); // Targeting another evented object.
2795       } else {
2796         // TODO: This wrapper is incorrect! It should only
2797         //       remove the wrapper for the event type that called it.
2798         //       Instead all listners are removed on the first trigger!
2799         //       see https://github.com/videojs/video.js/issues/5962
2800         var wrapper = function wrapper() {
2801           _this2.off(target, type, wrapper);
2802 
2803           for (var _len3 = arguments.length, largs = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
2804             largs[_key3] = arguments[_key3];
2805           }
2806 
2807           listener.apply(null, largs);
2808         }; // Use the same function ID as the listener so we can remove it later
2809         // it using the ID of the original listener.
2810 
2811 
2812         wrapper.guid = listener.guid;
2813         listen(target, 'one', type, wrapper);
2814       }
2815     },
2816 
2817     /**
2818      * Add a listener to an event (or events) on this object or another evented
2819      * object. The listener will only be called once for the first event that is triggered
2820      * then removed.
2821      *
2822      * @param  {string|Array|Element|Object} targetOrType
2823      *         If this is a string or array, it represents the event type(s)
2824      *         that will trigger the listener.
2825      *
2826      *         Another evented object can be passed here instead, which will
2827      *         cause the listener to listen for events on _that_ object.
2828      *
2829      *         In either case, the listener's `this` value will be bound to
2830      *         this object.
2831      *
2832      * @param  {string|Array|Function} typeOrListener
2833      *         If the first argument was a string or array, this should be the
2834      *         listener function. Otherwise, this is a string or array of event
2835      *         type(s).
2836      *
2837      * @param  {Function} [listener]
2838      *         If the first argument was another evented object, this will be
2839      *         the listener function.
2840      */
2841     any: function any() {
2842       var _this3 = this;
2843 
2844       for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
2845         args[_key4] = arguments[_key4];
2846       }
2847 
2848       var _normalizeListenArgs3 = normalizeListenArgs(this, args),
2849           isTargetingSelf = _normalizeListenArgs3.isTargetingSelf,
2850           target = _normalizeListenArgs3.target,
2851           type = _normalizeListenArgs3.type,
2852           listener = _normalizeListenArgs3.listener; // Targeting this evented object.
2853 
2854 
2855       if (isTargetingSelf) {
2856         listen(target, 'any', type, listener); // Targeting another evented object.
2857       } else {
2858         var wrapper = function wrapper() {
2859           _this3.off(target, type, wrapper);
2860 
2861           for (var _len5 = arguments.length, largs = new Array(_len5), _key5 = 0; _key5 < _len5; _key5++) {
2862             largs[_key5] = arguments[_key5];
2863           }
2864 
2865           listener.apply(null, largs);
2866         }; // Use the same function ID as the listener so we can remove it later
2867         // it using the ID of the original listener.
2868 
2869 
2870         wrapper.guid = listener.guid;
2871         listen(target, 'any', type, wrapper);
2872       }
2873     },
2874 
2875     /**
2876      * Removes listener(s) from event(s) on an evented object.
2877      *
2878      * @param  {string|Array|Element|Object} [targetOrType]
2879      *         If this is a string or array, it represents the event type(s).
2880      *
2881      *         Another evented object can be passed here instead, in which case
2882      *         ALL 3 arguments are _required_.
2883      *
2884      * @param  {string|Array|Function} [typeOrListener]
2885      *         If the first argument was a string or array, this may be the
2886      *         listener function. Otherwise, this is a string or array of event
2887      *         type(s).
2888      *
2889      * @param  {Function} [listener]
2890      *         If the first argument was another evented object, this will be
2891      *         the listener function; otherwise, _all_ listeners bound to the
2892      *         event type(s) will be removed.
2893      */
2894     off: function off$1(targetOrType, typeOrListener, listener) {
2895       // Targeting this evented object.
2896       if (!targetOrType || isValidEventType(targetOrType)) {
2897         off(this.eventBusEl_, targetOrType, typeOrListener); // Targeting another evented object.
2898       } else {
2899         var target = targetOrType;
2900         var type = typeOrListener; // Fail fast and in a meaningful way!
2901 
2902         validateTarget(target);
2903         validateEventType(type);
2904         validateListener(listener); // Ensure there's at least a guid, even if the function hasn't been used
2905 
2906         listener = bind(this, listener); // Remove the dispose listener on this evented object, which was given
2907         // the same guid as the event listener in on().
2908 
2909         this.off('dispose', listener);
2910 
2911         if (target.nodeName) {
2912           off(target, type, listener);
2913           off(target, 'dispose', listener);
2914         } else if (isEvented(target)) {
2915           target.off(type, listener);
2916           target.off('dispose', listener);
2917         }
2918       }
2919     },
2920 
2921     /**
2922      * Fire an event on this evented object, causing its listeners to be called.
2923      *
2924      * @param   {string|Object} event
2925      *          An event type or an object with a type property.
2926      *
2927      * @param   {Object} [hash]
2928      *          An additional object to pass along to listeners.
2929      *
2930      * @return {boolean}
2931      *          Whether or not the default behavior was prevented.
2932      */
2933     trigger: function trigger$1(event, hash) {
2934       return trigger(this.eventBusEl_, event, hash);
2935     }
2936   };
2937   /**
2938    * Applies {@link module:evented~EventedMixin|EventedMixin} to a target object.
2939    *
2940    * @param  {Object} target
2941    *         The object to which to add event methods.
2942    *
2943    * @param  {Object} [options={}]
2944    *         Options for customizing the mixin behavior.
2945    *
2946    * @param  {string} [options.eventBusKey]
2947    *         By default, adds a `eventBusEl_` DOM element to the target object,
2948    *         which is used as an event bus. If the target object already has a
2949    *         DOM element that should be used, pass its key here.
2950    *
2951    * @return {Object}
2952    *         The target object.
2953    */
2954 
2955   function evented(target, options) {
2956     if (options === void 0) {
2957       options = {};
2958     }
2959 
2960     var _options = options,
2961         eventBusKey = _options.eventBusKey; // Set or create the eventBusEl_.
2962 
2963     if (eventBusKey) {
2964       if (!target[eventBusKey].nodeName) {
2965         throw new Error("The eventBusKey \"" + eventBusKey + "\" does not refer to an element.");
2966       }
2967 
2968       target.eventBusEl_ = target[eventBusKey];
2969     } else {
2970       target.eventBusEl_ = createEl('span', {
2971         className: 'vjs-event-bus'
2972       });
2973     }
2974 
2975     assign(target, EventedMixin);
2976 
2977     if (target.eventedCallbacks) {
2978       target.eventedCallbacks.forEach(function (callback) {
2979         callback();
2980       });
2981     } // When any evented object is disposed, it removes all its listeners.
2982 
2983 
2984     target.on('dispose', function () {
2985       target.off();
2986       window$1.setTimeout(function () {
2987         target.eventBusEl_ = null;
2988       }, 0);
2989     });
2990     return target;
2991   }
2992 
2993   /**
2994    * @file mixins/stateful.js
2995    * @module stateful
2996    */
2997   /**
2998    * Contains methods that provide statefulness to an object which is passed
2999    * to {@link module:stateful}.
3000    *
3001    * @mixin StatefulMixin
3002    */
3003 
3004   var StatefulMixin = {
3005     /**
3006      * A hash containing arbitrary keys and values representing the state of
3007      * the object.
3008      *
3009      * @type {Object}
3010      */
3011     state: {},
3012 
3013     /**
3014      * Set the state of an object by mutating its
3015      * {@link module:stateful~StatefulMixin.state|state} object in place.
3016      *
3017      * @fires   module:stateful~StatefulMixin#statechanged
3018      * @param   {Object|Function} stateUpdates
3019      *          A new set of properties to shallow-merge into the plugin state.
3020      *          Can be a plain object or a function returning a plain object.
3021      *
3022      * @return {Object|undefined}
vendor: 88,523 bytes, lines 3023-5787
3023      *          An object containing changes that occurred. If no changes
3024      *          occurred, returns `undefined`.
3025      */
3026     setState: function setState(stateUpdates) {
3027       var _this = this;
3028 
3029       // Support providing the `stateUpdates` state as a function.
3030       if (typeof stateUpdates === 'function') {
3031         stateUpdates = stateUpdates();
3032       }
3033 
3034       var changes;
3035       each(stateUpdates, function (value, key) {
3036         // Record the change if the value is different from what's in the
3037         // current state.
3038         if (_this.state[key] !== value) {
3039           changes = changes || {};
3040           changes[key] = {
3041             from: _this.state[key],
3042             to: value
3043           };
3044         }
3045 
3046         _this.state[key] = value;
3047       }); // Only trigger "statechange" if there were changes AND we have a trigger
3048       // function. This allows us to not require that the target object be an
3049       // evented object.
3050 
3051       if (changes && isEvented(this)) {
3052         /**
3053          * An event triggered on an object that is both
3054          * {@link module:stateful|stateful} and {@link module:evented|evented}
3055          * indicating that its state has changed.
3056          *
3057          * @event    module:stateful~StatefulMixin#statechanged
3058          * @type     {Object}
3059          * @property {Object} changes
3060          *           A hash containing the properties that were changed and
3061          *           the values they were changed `from` and `to`.
3062          */
3063         this.trigger({
3064           changes: changes,
3065           type: 'statechanged'
3066         });
3067       }
3068 
3069       return changes;
3070     }
3071   };
3072   /**
3073    * Applies {@link module:stateful~StatefulMixin|StatefulMixin} to a target
3074    * object.
3075    *
3076    * If the target object is {@link module:evented|evented} and has a
3077    * `handleStateChanged` method, that method will be automatically bound to the
3078    * `statechanged` event on itself.
3079    *
3080    * @param   {Object} target
3081    *          The object to be made stateful.
3082    *
3083    * @param   {Object} [defaultState]
3084    *          A default set of properties to populate the newly-stateful object's
3085    *          `state` property.
3086    *
3087    * @return {Object}
3088    *          Returns the `target`.
3089    */
3090 
3091   function stateful(target, defaultState) {
3092     assign(target, StatefulMixin); // This happens after the mixing-in because we need to replace the `state`
3093     // added in that step.
3094 
3095     target.state = assign({}, target.state, defaultState); // Auto-bind the `handleStateChanged` method of the target object if it exists.
3096 
3097     if (typeof target.handleStateChanged === 'function' && isEvented(target)) {
3098       target.on('statechanged', target.handleStateChanged);
3099     }
3100 
3101     return target;
3102   }
3103 
3104   /**
3105    * @file string-cases.js
3106    * @module to-lower-case
3107    */
3108 
3109   /**
3110    * Lowercase the first letter of a string.
3111    *
3112    * @param {string} string
3113    *        String to be lowercased
3114    *
3115    * @return {string}
3116    *         The string with a lowercased first letter
3117    */
3118   var toLowerCase = function toLowerCase(string) {
3119     if (typeof string !== 'string') {
3120       return string;
3121     }
3122 
3123     return string.replace(/./, function (w) {
3124       return w.toLowerCase();
3125     });
3126   };
3127   /**
3128    * Uppercase the first letter of a string.
3129    *
3130    * @param {string} string
3131    *        String to be uppercased
3132    *
3133    * @return {string}
3134    *         The string with an uppercased first letter
3135    */
3136 
3137   var toTitleCase = function toTitleCase(string) {
3138     if (typeof string !== 'string') {
3139       return string;
3140     }
3141 
3142     return string.replace(/./, function (w) {
3143       return w.toUpperCase();
3144     });
3145   };
3146   /**
3147    * Compares the TitleCase versions of the two strings for equality.
3148    *
3149    * @param {string} str1
3150    *        The first string to compare
3151    *
3152    * @param {string} str2
3153    *        The second string to compare
3154    *
3155    * @return {boolean}
3156    *         Whether the TitleCase versions of the strings are equal
3157    */
3158 
3159   var titleCaseEquals = function titleCaseEquals(str1, str2) {
3160     return toTitleCase(str1) === toTitleCase(str2);
3161   };
3162 
3163   /**
3164    * @file merge-options.js
3165    * @module merge-options
3166    */
3167   /**
3168    * Merge two objects recursively.
3169    *
3170    * Performs a deep merge like
3171    * {@link https://lodash.com/docs/4.17.10#merge|lodash.merge}, but only merges
3172    * plain objects (not arrays, elements, or anything else).
3173    *
3174    * Non-plain object values will be copied directly from the right-most
3175    * argument.
3176    *
3177    * @static
3178    * @param   {Object[]} sources
3179    *          One or more objects to merge into a new object.
3180    *
3181    * @return {Object}
3182    *          A new object that is the merged result of all sources.
3183    */
3184 
3185   function mergeOptions() {
3186     var result = {};
3187 
3188     for (var _len = arguments.length, sources = new Array(_len), _key = 0; _key < _len; _key++) {
3189       sources[_key] = arguments[_key];
3190     }
3191 
3192     sources.forEach(function (source) {
3193       if (!source) {
3194         return;
3195       }
3196 
3197       each(source, function (value, key) {
3198         if (!isPlain(value)) {
3199           result[key] = value;
3200           return;
3201         }
3202 
3203         if (!isPlain(result[key])) {
3204           result[key] = {};
3205         }
3206 
3207         result[key] = mergeOptions(result[key], value);
3208       });
3209     });
3210     return result;
3211   }
3212 
3213   /**
3214    * Player Component - Base class for all UI objects
3215    *
3216    * @file component.js
3217    */
3218   /**
3219    * Base class for all UI Components.
3220    * Components are UI objects which represent both a javascript object and an element
3221    * in the DOM. They can be children of other components, and can have
3222    * children themselves.
3223    *
3224    * Components can also use methods from {@link EventTarget}
3225    */
3226 
3227   var Component =
3228   /*#__PURE__*/
3229   function () {
3230     /**
3231      * A callback that is called when a component is ready. Does not have any
3232      * paramters and any callback value will be ignored.
3233      *
3234      * @callback Component~ReadyCallback
3235      * @this Component
3236      */
3237 
3238     /**
3239      * Creates an instance of this class.
3240      *
3241      * @param {Player} player
3242      *        The `Player` that this class should be attached to.
3243      *
3244      * @param {Object} [options]
3245      *        The key/value store of player options.
3246      *
3247      * @param {Object[]} [options.children]
3248      *        An array of children objects to intialize this component with. Children objects have
3249      *        a name property that will be used if more than one component of the same type needs to be
3250      *        added.
3251      *
3252      * @param {Component~ReadyCallback} [ready]
3253      *        Function that gets called when the `Component` is ready.
3254      */
3255     function Component(player, options, ready) {
3256       // The component might be the player itself and we can't pass `this` to super
3257       if (!player && this.play) {
3258         this.player_ = player = this; // eslint-disable-line
3259       } else {
3260         this.player_ = player;
3261       }
3262 
3263       this.isDisposed_ = false; // Hold the reference to the parent component via `addChild` method
3264 
3265       this.parentComponent_ = null; // Make a copy of prototype.options_ to protect against overriding defaults
3266 
3267       this.options_ = mergeOptions({}, this.options_); // Updated options with supplied options
3268 
3269       options = this.options_ = mergeOptions(this.options_, options); // Get ID from options or options element if one is supplied
3270 
3271       this.id_ = options.id || options.el && options.el.id; // If there was no ID from the options, generate one
3272 
3273       if (!this.id_) {
3274         // Don't require the player ID function in the case of mock players
3275         var id = player && player.id && player.id() || 'no_player';
3276         this.id_ = id + "_component_" + newGUID();
3277       }
3278 
3279       this.name_ = options.name || null; // Create element if one wasn't provided in options
3280 
3281       if (options.el) {
3282         this.el_ = options.el;
3283       } else if (options.createEl !== false) {
3284         this.el_ = this.createEl();
3285       } // if evented is anything except false, we want to mixin in evented
3286 
3287 
3288       if (options.evented !== false) {
3289         // Make this an evented object and use `el_`, if available, as its event bus
3290         evented(this, {
3291           eventBusKey: this.el_ ? 'el_' : null
3292         });
3293       }
3294 
3295       stateful(this, this.constructor.defaultState);
3296       this.children_ = [];
3297       this.childIndex_ = {};
3298       this.childNameIndex_ = {};
3299       var SetSham;
3300 
3301       if (!window$1.Set) {
3302         SetSham =
3303         /*#__PURE__*/
3304         function () {
3305           function SetSham() {
3306             this.set_ = {};
3307           }
3308 
3309           var _proto2 = SetSham.prototype;
3310 
3311           _proto2.has = function has(key) {
3312             return key in this.set_;
3313           };
3314 
3315           _proto2["delete"] = function _delete(key) {
3316             var has = this.has(key);
3317             delete this.set_[key];
3318             return has;
3319           };
3320 
3321           _proto2.add = function add(key) {
3322             this.set_[key] = 1;
3323             return this;
3324           };
3325 
3326           _proto2.forEach = function forEach(callback, thisArg) {
3327             for (var key in this.set_) {
3328               callback.call(thisArg, key, key, this);
3329             }
3330           };
3331 
3332           return SetSham;
3333         }();
3334       }
3335 
3336       this.setTimeoutIds_ = window$1.Set ? new Set() : new SetSham();
3337       this.setIntervalIds_ = window$1.Set ? new Set() : new SetSham();
3338       this.rafIds_ = window$1.Set ? new Set() : new SetSham();
3339       this.clearingTimersOnDispose_ = false; // Add any child components in options
3340 
3341       if (options.initChildren !== false) {
3342         this.initChildren();
3343       }
3344 
3345       this.ready(ready); // Don't want to trigger ready here or it will before init is actually
3346       // finished for all children that run this constructor
3347 
3348       if (options.reportTouchActivity !== false) {
3349         this.enableTouchActivity();
3350       }
3351     }
3352     /**
3353      * Dispose of the `Component` and all child components.
3354      *
3355      * @fires Component#dispose
3356      */
3357 
3358 
3359     var _proto = Component.prototype;
3360 
3361     _proto.dispose = function dispose() {
3362       // Bail out if the component has already been disposed.
3363       if (this.isDisposed_) {
3364         return;
3365       }
3366       /**
3367        * Triggered when a `Component` is disposed.
3368        *
3369        * @event Component#dispose
3370        * @type {EventTarget~Event}
3371        *
3372        * @property {boolean} [bubbles=false]
3373        *           set to false so that the dispose event does not
3374        *           bubble up
3375        */
3376 
3377 
3378       this.trigger({
3379         type: 'dispose',
3380         bubbles: false
3381       });
3382       this.isDisposed_ = true; // Dispose all children.
3383 
3384       if (this.children_) {
3385         for (var i = this.children_.length - 1; i >= 0; i--) {
3386           if (this.children_[i].dispose) {
3387             this.children_[i].dispose();
3388           }
3389         }
3390       } // Delete child references
3391 
3392 
3393       this.children_ = null;
3394       this.childIndex_ = null;
3395       this.childNameIndex_ = null;
3396       this.parentComponent_ = null;
3397 
3398       if (this.el_) {
3399         // Remove element from DOM
3400         if (this.el_.parentNode) {
3401           this.el_.parentNode.removeChild(this.el_);
3402         }
3403 
3404         if (DomData.has(this.el_)) {
3405           DomData["delete"](this.el_);
3406         }
3407 
3408         this.el_ = null;
3409       } // remove reference to the player after disposing of the element
3410 
3411 
3412       this.player_ = null;
3413     }
3414     /**
3415      * Determine whether or not this component has been disposed.
3416      *
3417      * @return {boolean}
3418      *         If the component has been disposed, will be `true`. Otherwise, `false`.
3419      */
3420     ;
3421 
3422     _proto.isDisposed = function isDisposed() {
3423       return Boolean(this.isDisposed_);
3424     }
3425     /**
3426      * Return the {@link Player} that the `Component` has attached to.
3427      *
3428      * @return {Player}
3429      *         The player that this `Component` has attached to.
3430      */
3431     ;
3432 
3433     _proto.player = function player() {
3434       return this.player_;
3435     }
3436     /**
3437      * Deep merge of options objects with new options.
3438      * > Note: When both `obj` and `options` contain properties whose values are objects.
3439      *         The two properties get merged using {@link module:mergeOptions}
3440      *
3441      * @param {Object} obj
3442      *        The object that contains new options.
3443      *
3444      * @return {Object}
3445      *         A new object of `this.options_` and `obj` merged together.
3446      */
3447     ;
3448 
3449     _proto.options = function options(obj) {
3450       if (!obj) {
3451         return this.options_;
3452       }
3453 
3454       this.options_ = mergeOptions(this.options_, obj);
3455       return this.options_;
3456     }
3457     /**
3458      * Get the `Component`s DOM element
3459      *
3460      * @return {Element}
3461      *         The DOM element for this `Component`.
3462      */
3463     ;
3464 
3465     _proto.el = function el() {
3466       return this.el_;
3467     }
3468     /**
3469      * Create the `Component`s DOM element.
3470      *
3471      * @param {string} [tagName]
3472      *        Element's DOM node type. e.g. 'div'
3473      *
3474      * @param {Object} [properties]
3475      *        An object of properties that should be set.
3476      *
3477      * @param {Object} [attributes]
3478      *        An object of attributes that should be set.
3479      *
3480      * @return {Element}
3481      *         The element that gets created.
3482      */
3483     ;
3484 
3485     _proto.createEl = function createEl$1(tagName, properties, attributes) {
3486       return createEl(tagName, properties, attributes);
3487     }
3488     /**
3489      * Localize a string given the string in english.
3490      *
3491      * If tokens are provided, it'll try and run a simple token replacement on the provided string.
3492      * The tokens it looks for look like `{1}` with the index being 1-indexed into the tokens array.
3493      *
3494      * If a `defaultValue` is provided, it'll use that over `string`,
3495      * if a value isn't found in provided language files.
3496      * This is useful if you want to have a descriptive key for token replacement
3497      * but have a succinct localized string and not require `en.json` to be included.
3498      *
3499      * Currently, it is used for the progress bar timing.
3500      * ```js
3501      * {
3502      *   "progress bar timing: currentTime={1} duration={2}": "{1} of {2}"
3503      * }
3504      * ```
3505      * It is then used like so:
3506      * ```js
3507      * this.localize('progress bar timing: currentTime={1} duration{2}',
3508      *               [this.player_.currentTime(), this.player_.duration()],
3509      *               '{1} of {2}');
3510      * ```
3511      *
3512      * Which outputs something like: `01:23 of 24:56`.
3513      *
3514      *
3515      * @param {string} string
3516      *        The string to localize and the key to lookup in the language files.
3517      * @param {string[]} [tokens]
3518      *        If the current item has token replacements, provide the tokens here.
3519      * @param {string} [defaultValue]
3520      *        Defaults to `string`. Can be a default value to use for token replacement
3521      *        if the lookup key is needed to be separate.
3522      *
3523      * @return {string}
3524      *         The localized string or if no localization exists the english string.
3525      */
3526     ;
3527 
3528     _proto.localize = function localize(string, tokens, defaultValue) {
3529       if (defaultValue === void 0) {
3530         defaultValue = string;
3531       }
3532 
3533       var code = this.player_.language && this.player_.language();
3534       var languages = this.player_.languages && this.player_.languages();
3535       var language = languages && languages[code];
3536       var primaryCode = code && code.split('-')[0];
3537       var primaryLang = languages && languages[primaryCode];
3538       var localizedString = defaultValue;
3539 
3540       if (language && language[string]) {
3541         localizedString = language[string];
3542       } else if (primaryLang && primaryLang[string]) {
3543         localizedString = primaryLang[string];
3544       }
3545 
3546       if (tokens) {
3547         localizedString = localizedString.replace(/\{(\d+)\}/g, function (match, index) {
3548           var value = tokens[index - 1];
3549           var ret = value;
3550 
3551           if (typeof value === 'undefined') {
3552             ret = match;
3553           }
3554 
3555           return ret;
3556         });
3557       }
3558 
3559       return localizedString;
3560     }
3561     /**
3562      * Return the `Component`s DOM element. This is where children get inserted.
3563      * This will usually be the the same as the element returned in {@link Component#el}.
3564      *
3565      * @return {Element}
3566      *         The content element for this `Component`.
3567      */
3568     ;
3569 
3570     _proto.contentEl = function contentEl() {
3571       return this.contentEl_ || this.el_;
3572     }
3573     /**
3574      * Get this `Component`s ID
3575      *
3576      * @return {string}
3577      *         The id of this `Component`
3578      */
3579     ;
3580 
3581     _proto.id = function id() {
3582       return this.id_;
3583     }
3584     /**
3585      * Get the `Component`s name. The name gets used to reference the `Component`
3586      * and is set during registration.
3587      *
3588      * @return {string}
3589      *         The name of this `Component`.
3590      */
3591     ;
3592 
3593     _proto.name = function name() {
3594       return this.name_;
3595     }
3596     /**
3597      * Get an array of all child components
3598      *
3599      * @return {Array}
3600      *         The children
3601      */
3602     ;
3603 
3604     _proto.children = function children() {
3605       return this.children_;
3606     }
3607     /**
3608      * Returns the child `Component` with the given `id`.
3609      *
3610      * @param {string} id
3611      *        The id of the child `Component` to get.
3612      *
3613      * @return {Component|undefined}
3614      *         The child `Component` with the given `id` or undefined.
3615      */
3616     ;
3617 
3618     _proto.getChildById = function getChildById(id) {
3619       return this.childIndex_[id];
3620     }
3621     /**
3622      * Returns the child `Component` with the given `name`.
3623      *
3624      * @param {string} name
3625      *        The name of the child `Component` to get.
3626      *
3627      * @return {Component|undefined}
3628      *         The child `Component` with the given `name` or undefined.
3629      */
3630     ;
3631 
3632     _proto.getChild = function getChild(name) {
3633       if (!name) {
3634         return;
3635       }
3636 
3637       return this.childNameIndex_[name];
3638     }
3639     /**
3640      * Add a child `Component` inside the current `Component`.
3641      *
3642      *
3643      * @param {string|Component} child
3644      *        The name or instance of a child to add.
3645      *
3646      * @param {Object} [options={}]
3647      *        The key/value store of options that will get passed to children of
3648      *        the child.
3649      *
3650      * @param {number} [index=this.children_.length]
3651      *        The index to attempt to add a child into.
3652      *
3653      * @return {Component}
3654      *         The `Component` that gets added as a child. When using a string the
3655      *         `Component` will get created by this process.
3656      */
3657     ;
3658 
3659     _proto.addChild = function addChild(child, options, index) {
3660       if (options === void 0) {
3661         options = {};
3662       }
3663 
3664       if (index === void 0) {
3665         index = this.children_.length;
3666       }
3667 
3668       var component;
3669       var componentName; // If child is a string, create component with options
3670 
3671       if (typeof child === 'string') {
3672         componentName = toTitleCase(child);
3673         var componentClassName = options.componentClass || componentName; // Set name through options
3674 
3675         options.name = componentName; // Create a new object & element for this controls set
3676         // If there's no .player_, this is a player
3677 
3678         var ComponentClass = Component.getComponent(componentClassName);
3679 
3680         if (!ComponentClass) {
3681           throw new Error("Component " + componentClassName + " does not exist");
3682         } // data stored directly on the videojs object may be
3683         // misidentified as a component to retain
3684         // backwards-compatibility with 4.x. check to make sure the
3685         // component class can be instantiated.
3686 
3687 
3688         if (typeof ComponentClass !== 'function') {
3689           return null;
3690         }
3691 
3692         component = new ComponentClass(this.player_ || this, options); // child is a component instance
3693       } else {
3694         component = child;
3695       }
3696 
3697       if (component.parentComponent_) {
3698         component.parentComponent_.removeChild(component);
3699       }
3700 
3701       this.children_.splice(index, 0, component);
3702       component.parentComponent_ = this;
3703 
3704       if (typeof component.id === 'function') {
3705         this.childIndex_[component.id()] = component;
3706       } // If a name wasn't used to create the component, check if we can use the
3707       // name function of the component
3708 
3709 
3710       componentName = componentName || component.name && toTitleCase(component.name());
3711 
3712       if (componentName) {
3713         this.childNameIndex_[componentName] = component;
3714         this.childNameIndex_[toLowerCase(componentName)] = component;
3715       } // Add the UI object's element to the container div (box)
3716       // Having an element is not required
3717 
3718 
3719       if (typeof component.el === 'function' && component.el()) {
3720         // If inserting before a component, insert before that component's element
3721         var refNode = null;
3722 
3723         if (this.children_[index + 1] && this.children_[index + 1].el_) {
3724           refNode = this.children_[index + 1].el_;
3725         }
3726 
3727         this.contentEl().insertBefore(component.el(), refNode);
3728       } // Return so it can stored on parent object if desired.
3729 
3730 
3731       return component;
3732     }
3733     /**
3734      * Remove a child `Component` from this `Component`s list of children. Also removes
3735      * the child `Component`s element from this `Component`s element.
3736      *
3737      * @param {Component} component
3738      *        The child `Component` to remove.
3739      */
3740     ;
3741 
3742     _proto.removeChild = function removeChild(component) {
3743       if (typeof component === 'string') {
3744         component = this.getChild(component);
3745       }
3746 
3747       if (!component || !this.children_) {
3748         return;
3749       }
3750 
3751       var childFound = false;
3752 
3753       for (var i = this.children_.length - 1; i >= 0; i--) {
3754         if (this.children_[i] === component) {
3755           childFound = true;
3756           this.children_.splice(i, 1);
3757           break;
3758         }
3759       }
3760 
3761       if (!childFound) {
3762         return;
3763       }
3764 
3765       component.parentComponent_ = null;
3766       this.childIndex_[component.id()] = null;
3767       this.childNameIndex_[toTitleCase(component.name())] = null;
3768       this.childNameIndex_[toLowerCase(component.name())] = null;
3769       var compEl = component.el();
3770 
3771       if (compEl && compEl.parentNode === this.contentEl()) {
3772         this.contentEl().removeChild(component.el());
3773       }
3774     }
3775     /**
3776      * Add and initialize default child `Component`s based upon options.
3777      */
3778     ;
3779 
3780     _proto.initChildren = function initChildren() {
3781       var _this = this;
3782 
3783       var children = this.options_.children;
3784 
3785       if (children) {
3786         // `this` is `parent`
3787         var parentOptions = this.options_;
3788 
3789         var handleAdd = function handleAdd(child) {
3790           var name = child.name;
3791           var opts = child.opts; // Allow options for children to be set at the parent options
3792           // e.g. videojs(id, { controlBar: false });
3793           // instead of videojs(id, { children: { controlBar: false });
3794 
3795           if (parentOptions[name] !== undefined) {
3796             opts = parentOptions[name];
3797           } // Allow for disabling default components
3798           // e.g. options['children']['posterImage'] = false
3799 
3800 
3801           if (opts === false) {
3802             return;
3803           } // Allow options to be passed as a simple boolean if no configuration
3804           // is necessary.
3805 
3806 
3807           if (opts === true) {
3808             opts = {};
3809           } // We also want to pass the original player options
3810           // to each component as well so they don't need to
3811           // reach back into the player for options later.
3812 
3813 
3814           opts.playerOptions = _this.options_.playerOptions; // Create and add the child component.
3815           // Add a direct reference to the child by name on the parent instance.
3816           // If two of the same component are used, different names should be supplied
3817           // for each
3818 
3819           var newChild = _this.addChild(name, opts);
3820 
3821           if (newChild) {
3822             _this[name] = newChild;
3823           }
3824         }; // Allow for an array of children details to passed in the options
3825 
3826 
3827         var workingChildren;
3828         var Tech = Component.getComponent('Tech');
3829 
3830         if (Array.isArray(children)) {
3831           workingChildren = children;
3832         } else {
3833           workingChildren = Object.keys(children);
3834         }
3835 
3836         workingChildren // children that are in this.options_ but also in workingChildren  would
3837         // give us extra children we do not want. So, we want to filter them out.
3838         .concat(Object.keys(this.options_).filter(function (child) {
3839           return !workingChildren.some(function (wchild) {
3840             if (typeof wchild === 'string') {
3841               return child === wchild;
3842             }
3843 
3844             return child === wchild.name;
3845           });
3846         })).map(function (child) {
3847           var name;
3848           var opts;
3849 
3850           if (typeof child === 'string') {
3851             name = child;
3852             opts = children[name] || _this.options_[name] || {};
3853           } else {
3854             name = child.name;
3855             opts = child;
3856           }
3857 
3858           return {
3859             name: name,
3860             opts: opts
3861           };
3862         }).filter(function (child) {
3863           // we have to make sure that child.name isn't in the techOrder since
3864           // techs are registerd as Components but can't aren't compatible
3865           // See https://github.com/videojs/video.js/issues/2772
3866           var c = Component.getComponent(child.opts.componentClass || toTitleCase(child.name));
3867           return c && !Tech.isTech(c);
3868         }).forEach(handleAdd);
3869       }
3870     }
3871     /**
3872      * Builds the default DOM class name. Should be overriden by sub-components.
3873      *
3874      * @return {string}
3875      *         The DOM class name for this object.
3876      *
3877      * @abstract
3878      */
3879     ;
3880 
3881     _proto.buildCSSClass = function buildCSSClass() {
3882       // Child classes can include a function that does:
3883       // return 'CLASS NAME' + this._super();
3884       return '';
3885     }
3886     /**
3887      * Bind a listener to the component's ready state.
3888      * Different from event listeners in that if the ready event has already happened
3889      * it will trigger the function immediately.
3890      *
3891      * @return {Component}
3892      *         Returns itself; method can be chained.
3893      */
3894     ;
3895 
3896     _proto.ready = function ready(fn, sync) {
3897       if (sync === void 0) {
3898         sync = false;
3899       }
3900 
3901       if (!fn) {
3902         return;
3903       }
3904 
3905       if (!this.isReady_) {
3906         this.readyQueue_ = this.readyQueue_ || [];
3907         this.readyQueue_.push(fn);
3908         return;
3909       }
3910 
3911       if (sync) {
3912         fn.call(this);
3913       } else {
3914         // Call the function asynchronously by default for consistency
3915         this.setTimeout(fn, 1);
3916       }
3917     }
3918     /**
3919      * Trigger all the ready listeners for this `Component`.
3920      *
3921      * @fires Component#ready
3922      */
3923     ;
3924 
3925     _proto.triggerReady = function triggerReady() {
3926       this.isReady_ = true; // Ensure ready is triggered asynchronously
3927 
3928       this.setTimeout(function () {
3929         var readyQueue = this.readyQueue_; // Reset Ready Queue
3930 
3931         this.readyQueue_ = [];
3932 
3933         if (readyQueue && readyQueue.length > 0) {
3934           readyQueue.forEach(function (fn) {
3935             fn.call(this);
3936           }, this);
3937         } // Allow for using event listeners also
3938 
3939         /**
3940          * Triggered when a `Component` is ready.
3941          *
3942          * @event Component#ready
3943          * @type {EventTarget~Event}
3944          */
3945 
3946 
3947         this.trigger('ready');
3948       }, 1);
3949     }
3950     /**
3951      * Find a single DOM element matching a `selector`. This can be within the `Component`s
3952      * `contentEl()` or another custom context.
3953      *
3954      * @param {string} selector
3955      *        A valid CSS selector, which will be passed to `querySelector`.
3956      *
3957      * @param {Element|string} [context=this.contentEl()]
3958      *        A DOM element within which to query. Can also be a selector string in
3959      *        which case the first matching element will get used as context. If
3960      *        missing `this.contentEl()` gets used. If  `this.contentEl()` returns
3961      *        nothing it falls back to `document`.
3962      *
3963      * @return {Element|null}
3964      *         the dom element that was found, or null
3965      *
3966      * @see [Information on CSS Selectors](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors)
3967      */
3968     ;
3969 
3970     _proto.$ = function $$1(selector, context) {
3971       return $(selector, context || this.contentEl());
3972     }
3973     /**
3974      * Finds all DOM element matching a `selector`. This can be within the `Component`s
3975      * `contentEl()` or another custom context.
3976      *
3977      * @param {string} selector
3978      *        A valid CSS selector, which will be passed to `querySelectorAll`.
3979      *
3980      * @param {Element|string} [context=this.contentEl()]
3981      *        A DOM element within which to query. Can also be a selector string in
3982      *        which case the first matching element will get used as context. If
3983      *        missing `this.contentEl()` gets used. If  `this.contentEl()` returns
3984      *        nothing it falls back to `document`.
3985      *
3986      * @return {NodeList}
3987      *         a list of dom elements that were found
3988      *
3989      * @see [Information on CSS Selectors](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors)
3990      */
3991     ;
3992 
3993     _proto.$$ = function $$$1(selector, context) {
3994       return $$(selector, context || this.contentEl());
3995     }
3996     /**
3997      * Check if a component's element has a CSS class name.
3998      *
3999      * @param {string} classToCheck
4000      *        CSS class name to check.
4001      *
4002      * @return {boolean}
4003      *         - True if the `Component` has the class.
4004      *         - False if the `Component` does not have the class`
4005      */
4006     ;
4007 
4008     _proto.hasClass = function hasClass$1(classToCheck) {
4009       return hasClass(this.el_, classToCheck);
4010     }
4011     /**
4012      * Add a CSS class name to the `Component`s element.
4013      *
4014      * @param {string} classToAdd
4015      *        CSS class name to add
4016      */
4017     ;
4018 
4019     _proto.addClass = function addClass$1(classToAdd) {
4020       addClass(this.el_, classToAdd);
4021     }
4022     /**
4023      * Remove a CSS class name from the `Component`s element.
4024      *
4025      * @param {string} classToRemove
4026      *        CSS class name to remove
4027      */
4028     ;
4029 
4030     _proto.removeClass = function removeClass$1(classToRemove) {
4031       removeClass(this.el_, classToRemove);
4032     }
4033     /**
4034      * Add or remove a CSS class name from the component's element.
4035      * - `classToToggle` gets added when {@link Component#hasClass} would return false.
4036      * - `classToToggle` gets removed when {@link Component#hasClass} would return true.
4037      *
4038      * @param  {string} classToToggle
4039      *         The class to add or remove based on (@link Component#hasClass}
4040      *
4041      * @param  {boolean|Dom~predicate} [predicate]
4042      *         An {@link Dom~predicate} function or a boolean
4043      */
4044     ;
4045 
4046     _proto.toggleClass = function toggleClass$1(classToToggle, predicate) {
4047       toggleClass(this.el_, classToToggle, predicate);
4048     }
4049     /**
4050      * Show the `Component`s element if it is hidden by removing the
4051      * 'vjs-hidden' class name from it.
4052      */
4053     ;
4054 
4055     _proto.show = function show() {
4056       this.removeClass('vjs-hidden');
4057     }
4058     /**
4059      * Hide the `Component`s element if it is currently showing by adding the
4060      * 'vjs-hidden` class name to it.
4061      */
4062     ;
4063 
4064     _proto.hide = function hide() {
4065       this.addClass('vjs-hidden');
4066     }
4067     /**
4068      * Lock a `Component`s element in its visible state by adding the 'vjs-lock-showing'
4069      * class name to it. Used during fadeIn/fadeOut.
4070      *
4071      * @private
4072      */
4073     ;
4074 
4075     _proto.lockShowing = function lockShowing() {
4076       this.addClass('vjs-lock-showing');
4077     }
4078     /**
4079      * Unlock a `Component`s element from its visible state by removing the 'vjs-lock-showing'
4080      * class name from it. Used during fadeIn/fadeOut.
4081      *
4082      * @private
4083      */
4084     ;
4085 
4086     _proto.unlockShowing = function unlockShowing() {
4087       this.removeClass('vjs-lock-showing');
4088     }
4089     /**
4090      * Get the value of an attribute on the `Component`s element.
4091      *
4092      * @param {string} attribute
4093      *        Name of the attribute to get the value from.
4094      *
4095      * @return {string|null}
4096      *         - The value of the attribute that was asked for.
4097      *         - Can be an empty string on some browsers if the attribute does not exist
4098      *           or has no value
4099      *         - Most browsers will return null if the attibute does not exist or has
4100      *           no value.
4101      *
4102      * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttribute}
4103      */
4104     ;
4105 
4106     _proto.getAttribute = function getAttribute$1(attribute) {
4107       return getAttribute(this.el_, attribute);
4108     }
4109     /**
4110      * Set the value of an attribute on the `Component`'s element
4111      *
4112      * @param {string} attribute
4113      *        Name of the attribute to set.
4114      *
4115      * @param {string} value
4116      *        Value to set the attribute to.
4117      *
4118      * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttribute}
4119      */
4120     ;
4121 
4122     _proto.setAttribute = function setAttribute$1(attribute, value) {
4123       setAttribute(this.el_, attribute, value);
4124     }
4125     /**
4126      * Remove an attribute from the `Component`s element.
4127      *
4128      * @param {string} attribute
4129      *        Name of the attribute to remove.
4130      *
4131      * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/removeAttribute}
4132      */
4133     ;
4134 
4135     _proto.removeAttribute = function removeAttribute$1(attribute) {
4136       removeAttribute(this.el_, attribute);
4137     }
4138     /**
4139      * Get or set the width of the component based upon the CSS styles.
4140      * See {@link Component#dimension} for more detailed information.
4141      *
4142      * @param {number|string} [num]
4143      *        The width that you want to set postfixed with '%', 'px' or nothing.
4144      *
4145      * @param {boolean} [skipListeners]
4146      *        Skip the componentresize event trigger
4147      *
4148      * @return {number|string}
4149      *         The width when getting, zero if there is no width. Can be a string
4150      *           postpixed with '%' or 'px'.
4151      */
4152     ;
4153 
4154     _proto.width = function width(num, skipListeners) {
4155       return this.dimension('width', num, skipListeners);
4156     }
4157     /**
4158      * Get or set the height of the component based upon the CSS styles.
4159      * See {@link Component#dimension} for more detailed information.
4160      *
4161      * @param {number|string} [num]
4162      *        The height that you want to set postfixed with '%', 'px' or nothing.
4163      *
4164      * @param {boolean} [skipListeners]
4165      *        Skip the componentresize event trigger
4166      *
4167      * @return {number|string}
4168      *         The width when getting, zero if there is no width. Can be a string
4169      *         postpixed with '%' or 'px'.
4170      */
4171     ;
4172 
4173     _proto.height = function height(num, skipListeners) {
4174       return this.dimension('height', num, skipListeners);
4175     }
4176     /**
4177      * Set both the width and height of the `Component` element at the same time.
4178      *
4179      * @param  {number|string} width
4180      *         Width to set the `Component`s element to.
4181      *
4182      * @param  {number|string} height
4183      *         Height to set the `Component`s element to.
4184      */
4185     ;
4186 
4187     _proto.dimensions = function dimensions(width, height) {
4188       // Skip componentresize listeners on width for optimization
4189       this.width(width, true);
4190       this.height(height);
4191     }
4192     /**
4193      * Get or set width or height of the `Component` element. This is the shared code
4194      * for the {@link Component#width} and {@link Component#height}.
4195      *
4196      * Things to know:
4197      * - If the width or height in an number this will return the number postfixed with 'px'.
4198      * - If the width/height is a percent this will return the percent postfixed with '%'
4199      * - Hidden elements have a width of 0 with `window.getComputedStyle`. This function
4200      *   defaults to the `Component`s `style.width` and falls back to `window.getComputedStyle`.
4201      *   See [this]{@link http://www.foliotek.com/devblog/getting-the-width-of-a-hidden-element-with-jquery-using-width/}
4202      *   for more information
4203      * - If you want the computed style of the component, use {@link Component#currentWidth}
4204      *   and {@link {Component#currentHeight}
4205      *
4206      * @fires Component#componentresize
4207      *
4208      * @param {string} widthOrHeight
4209      8        'width' or 'height'
4210      *
4211      * @param  {number|string} [num]
4212      8         New dimension
4213      *
4214      * @param  {boolean} [skipListeners]
4215      *         Skip componentresize event trigger
4216      *
4217      * @return {number}
4218      *         The dimension when getting or 0 if unset
4219      */
4220     ;
4221 
4222     _proto.dimension = function dimension(widthOrHeight, num, skipListeners) {
4223       if (num !== undefined) {
4224         // Set to zero if null or literally NaN (NaN !== NaN)
4225         if (num === null || num !== num) {
4226           num = 0;
4227         } // Check if using css width/height (% or px) and adjust
4228 
4229 
4230         if (('' + num).indexOf('%') !== -1 || ('' + num).indexOf('px') !== -1) {
4231           this.el_.style[widthOrHeight] = num;
4232         } else if (num === 'auto') {
4233           this.el_.style[widthOrHeight] = '';
4234         } else {
4235           this.el_.style[widthOrHeight] = num + 'px';
4236         } // skipListeners allows us to avoid triggering the resize event when setting both width and height
4237 
4238 
4239         if (!skipListeners) {
4240           /**
4241            * Triggered when a component is resized.
4242            *
4243            * @event Component#componentresize
4244            * @type {EventTarget~Event}
4245            */
4246           this.trigger('componentresize');
4247         }
4248 
4249         return;
4250       } // Not setting a value, so getting it
4251       // Make sure element exists
4252 
4253 
4254       if (!this.el_) {
4255         return 0;
4256       } // Get dimension value from style
4257 
4258 
4259       var val = this.el_.style[widthOrHeight];
4260       var pxIndex = val.indexOf('px');
4261 
4262       if (pxIndex !== -1) {
4263         // Return the pixel value with no 'px'
4264         return parseInt(val.slice(0, pxIndex), 10);
4265       } // No px so using % or no style was set, so falling back to offsetWidth/height
4266       // If component has display:none, offset will return 0
4267       // TODO: handle display:none and no dimension style using px
4268 
4269 
4270       return parseInt(this.el_['offset' + toTitleCase(widthOrHeight)], 10);
4271     }
4272     /**
4273      * Get the computed width or the height of the component's element.
4274      *
4275      * Uses `window.getComputedStyle`.
4276      *
4277      * @param {string} widthOrHeight
4278      *        A string containing 'width' or 'height'. Whichever one you want to get.
4279      *
4280      * @return {number}
4281      *         The dimension that gets asked for or 0 if nothing was set
4282      *         for that dimension.
4283      */
4284     ;
4285 
4286     _proto.currentDimension = function currentDimension(widthOrHeight) {
4287       var computedWidthOrHeight = 0;
4288 
4289       if (widthOrHeight !== 'width' && widthOrHeight !== 'height') {
4290         throw new Error('currentDimension only accepts width or height value');
4291       }
4292 
4293       computedWidthOrHeight = computedStyle(this.el_, widthOrHeight); // remove 'px' from variable and parse as integer
4294 
4295       computedWidthOrHeight = parseFloat(computedWidthOrHeight); // if the computed value is still 0, it's possible that the browser is lying
4296       // and we want to check the offset values.
4297       // This code also runs wherever getComputedStyle doesn't exist.
4298 
4299       if (computedWidthOrHeight === 0 || isNaN(computedWidthOrHeight)) {
4300         var rule = "offset" + toTitleCase(widthOrHeight);
4301         computedWidthOrHeight = this.el_[rule];
4302       }
4303 
4304       return computedWidthOrHeight;
4305     }
4306     /**
4307      * An object that contains width and height values of the `Component`s
4308      * computed style. Uses `window.getComputedStyle`.
4309      *
4310      * @typedef {Object} Component~DimensionObject
4311      *
4312      * @property {number} width
4313      *           The width of the `Component`s computed style.
4314      *
4315      * @property {number} height
4316      *           The height of the `Component`s computed style.
4317      */
4318 
4319     /**
4320      * Get an object that contains computed width and height values of the
4321      * component's element.
4322      *
4323      * Uses `window.getComputedStyle`.
4324      *
4325      * @return {Component~DimensionObject}
4326      *         The computed dimensions of the component's element.
4327      */
4328     ;
4329 
4330     _proto.currentDimensions = function currentDimensions() {
4331       return {
4332         width: this.currentDimension('width'),
4333         height: this.currentDimension('height')
4334       };
4335     }
4336     /**
4337      * Get the computed width of the component's element.
4338      *
4339      * Uses `window.getComputedStyle`.
4340      *
4341      * @return {number}
4342      *         The computed width of the component's element.
4343      */
4344     ;
4345 
4346     _proto.currentWidth = function currentWidth() {
4347       return this.currentDimension('width');
4348     }
4349     /**
4350      * Get the computed height of the component's element.
4351      *
4352      * Uses `window.getComputedStyle`.
4353      *
4354      * @return {number}
4355      *         The computed height of the component's element.
4356      */
4357     ;
4358 
4359     _proto.currentHeight = function currentHeight() {
4360       return this.currentDimension('height');
4361     }
4362     /**
4363      * Set the focus to this component
4364      */
4365     ;
4366 
4367     _proto.focus = function focus() {
4368       this.el_.focus();
4369     }
4370     /**
4371      * Remove the focus from this component
4372      */
4373     ;
4374 
4375     _proto.blur = function blur() {
4376       this.el_.blur();
4377     }
4378     /**
4379      * When this Component receives a `keydown` event which it does not process,
4380      *  it passes the event to the Player for handling.
4381      *
4382      * @param {EventTarget~Event} event
4383      *        The `keydown` event that caused this function to be called.
4384      */
4385     ;
4386 
4387     _proto.handleKeyDown = function handleKeyDown(event) {
4388       if (this.player_) {
4389         // We only stop propagation here because we want unhandled events to fall
4390         // back to the browser.
4391         event.stopPropagation();
4392         this.player_.handleKeyDown(event);
4393       }
4394     }
4395     /**
4396      * Many components used to have a `handleKeyPress` method, which was poorly
4397      * named because it listened to a `keydown` event. This method name now
4398      * delegates to `handleKeyDown`. This means anyone calling `handleKeyPress`
4399      * will not see their method calls stop working.
4400      *
4401      * @param {EventTarget~Event} event
4402      *        The event that caused this function to be called.
4403      */
4404     ;
4405 
4406     _proto.handleKeyPress = function handleKeyPress(event) {
4407       this.handleKeyDown(event);
4408     }
4409     /**
4410      * Emit a 'tap' events when touch event support gets detected. This gets used to
4411      * support toggling the controls through a tap on the video. They get enabled
4412      * because every sub-component would have extra overhead otherwise.
4413      *
4414      * @private
4415      * @fires Component#tap
4416      * @listens Component#touchstart
4417      * @listens Component#touchmove
4418      * @listens Component#touchleave
4419      * @listens Component#touchcancel
4420      * @listens Component#touchend
4421       */
4422     ;
4423 
4424     _proto.emitTapEvents = function emitTapEvents() {
4425       // Track the start time so we can determine how long the touch lasted
4426       var touchStart = 0;
4427       var firstTouch = null; // Maximum movement allowed during a touch event to still be considered a tap
4428       // Other popular libs use anywhere from 2 (hammer.js) to 15,
4429       // so 10 seems like a nice, round number.
4430 
4431       var tapMovementThreshold = 10; // The maximum length a touch can be while still being considered a tap
4432 
4433       var touchTimeThreshold = 200;
4434       var couldBeTap;
4435       this.on('touchstart', function (event) {
4436         // If more than one finger, don't consider treating this as a click
4437         if (event.touches.length === 1) {
4438           // Copy pageX/pageY from the object
4439           firstTouch = {
4440             pageX: event.touches[0].pageX,
4441             pageY: event.touches[0].pageY
4442           }; // Record start time so we can detect a tap vs. "touch and hold"
4443 
4444           touchStart = window$1.performance.now(); // Reset couldBeTap tracking
4445 
4446           couldBeTap = true;
4447         }
4448       });
4449       this.on('touchmove', function (event) {
4450         // If more than one finger, don't consider treating this as a click
4451         if (event.touches.length > 1) {
4452           couldBeTap = false;
4453         } else if (firstTouch) {
4454           // Some devices will throw touchmoves for all but the slightest of taps.
4455           // So, if we moved only a small distance, this could still be a tap
4456           var xdiff = event.touches[0].pageX - firstTouch.pageX;
4457           var ydiff = event.touches[0].pageY - firstTouch.pageY;
4458           var touchDistance = Math.sqrt(xdiff * xdiff + ydiff * ydiff);
4459 
4460           if (touchDistance > tapMovementThreshold) {
4461             couldBeTap = false;
4462           }
4463         }
4464       });
4465 
4466       var noTap = function noTap() {
4467         couldBeTap = false;
4468       }; // TODO: Listen to the original target. http://youtu.be/DujfpXOKUp8?t=13m8s
4469 
4470 
4471       this.on('touchleave', noTap);
4472       this.on('touchcancel', noTap); // When the touch ends, measure how long it took and trigger the appropriate
4473       // event
4474 
4475       this.on('touchend', function (event) {
4476         firstTouch = null; // Proceed only if the touchmove/leave/cancel event didn't happen
4477 
4478         if (couldBeTap === true) {
4479           // Measure how long the touch lasted
4480           var touchTime = window$1.performance.now() - touchStart; // Make sure the touch was less than the threshold to be considered a tap
4481 
4482           if (touchTime < touchTimeThreshold) {
4483             // Don't let browser turn this into a click
4484             event.preventDefault();
4485             /**
4486              * Triggered when a `Component` is tapped.
4487              *
4488              * @event Component#tap
4489              * @type {EventTarget~Event}
4490              */
4491 
4492             this.trigger('tap'); // It may be good to copy the touchend event object and change the
4493             // type to tap, if the other event properties aren't exact after
4494             // Events.fixEvent runs (e.g. event.target)
4495           }
4496         }
4497       });
4498     }
4499     /**
4500      * This function reports user activity whenever touch events happen. This can get
4501      * turned off by any sub-components that wants touch events to act another way.
4502      *
4503      * Report user touch activity when touch events occur. User activity gets used to
4504      * determine when controls should show/hide. It is simple when it comes to mouse
4505      * events, because any mouse event should show the controls. So we capture mouse
4506      * events that bubble up to the player and report activity when that happens.
4507      * With touch events it isn't as easy as `touchstart` and `touchend` toggle player
4508      * controls. So touch events can't help us at the player level either.
4509      *
4510      * User activity gets checked asynchronously. So what could happen is a tap event
4511      * on the video turns the controls off. Then the `touchend` event bubbles up to
4512      * the player. Which, if it reported user activity, would turn the controls right
4513      * back on. We also don't want to completely block touch events from bubbling up.
4514      * Furthermore a `touchmove` event and anything other than a tap, should not turn
4515      * controls back on.
4516      *
4517      * @listens Component#touchstart
4518      * @listens Component#touchmove
4519      * @listens Component#touchend
4520      * @listens Component#touchcancel
4521      */
4522     ;
4523 
4524     _proto.enableTouchActivity = function enableTouchActivity() {
4525       // Don't continue if the root player doesn't support reporting user activity
4526       if (!this.player() || !this.player().reportUserActivity) {
4527         return;
4528       } // listener for reporting that the user is active
4529 
4530 
4531       var report = bind(this.player(), this.player().reportUserActivity);
4532       var touchHolding;
4533       this.on('touchstart', function () {
4534         report(); // For as long as the they are touching the device or have their mouse down,
4535         // we consider them active even if they're not moving their finger or mouse.
4536         // So we want to continue to update that they are active
4537 
4538         this.clearInterval(touchHolding); // report at the same interval as activityCheck
4539 
4540         touchHolding = this.setInterval(report, 250);
4541       });
4542 
4543       var touchEnd = function touchEnd(event) {
4544         report(); // stop the interval that maintains activity if the touch is holding
4545 
4546         this.clearInterval(touchHolding);
4547       };
4548 
4549       this.on('touchmove', report);
4550       this.on('touchend', touchEnd);
4551       this.on('touchcancel', touchEnd);
4552     }
4553     /**
4554      * A callback that has no parameters and is bound into `Component`s context.
4555      *
4556      * @callback Component~GenericCallback
4557      * @this Component
4558      */
4559 
4560     /**
4561      * Creates a function that runs after an `x` millisecond timeout. This function is a
4562      * wrapper around `window.setTimeout`. There are a few reasons to use this one
4563      * instead though:
4564      * 1. It gets cleared via  {@link Component#clearTimeout} when
4565      *    {@link Component#dispose} gets called.
4566      * 2. The function callback will gets turned into a {@link Component~GenericCallback}
4567      *
4568      * > Note: You can't use `window.clearTimeout` on the id returned by this function. This
4569      *         will cause its dispose listener not to get cleaned up! Please use
4570      *         {@link Component#clearTimeout} or {@link Component#dispose} instead.
4571      *
4572      * @param {Component~GenericCallback} fn
4573      *        The function that will be run after `timeout`.
4574      *
4575      * @param {number} timeout
4576      *        Timeout in milliseconds to delay before executing the specified function.
4577      *
4578      * @return {number}
4579      *         Returns a timeout ID that gets used to identify the timeout. It can also
4580      *         get used in {@link Component#clearTimeout} to clear the timeout that
4581      *         was set.
4582      *
4583      * @listens Component#dispose
4584      * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout}
4585      */
4586     ;
4587 
4588     _proto.setTimeout = function setTimeout(fn, timeout) {
4589       var _this2 = this;
4590 
4591       // declare as variables so they are properly available in timeout function
4592       // eslint-disable-next-line
4593       var timeoutId;
4594       fn = bind(this, fn);
4595       this.clearTimersOnDispose_();
4596       timeoutId = window$1.setTimeout(function () {
4597         if (_this2.setTimeoutIds_.has(timeoutId)) {
4598           _this2.setTimeoutIds_["delete"](timeoutId);
4599         }
4600 
4601         fn();
4602       }, timeout);
4603       this.setTimeoutIds_.add(timeoutId);
4604       return timeoutId;
4605     }
4606     /**
4607      * Clears a timeout that gets created via `window.setTimeout` or
4608      * {@link Component#setTimeout}. If you set a timeout via {@link Component#setTimeout}
4609      * use this function instead of `window.clearTimout`. If you don't your dispose
4610      * listener will not get cleaned up until {@link Component#dispose}!
4611      *
4612      * @param {number} timeoutId
4613      *        The id of the timeout to clear. The return value of
4614      *        {@link Component#setTimeout} or `window.setTimeout`.
4615      *
4616      * @return {number}
4617      *         Returns the timeout id that was cleared.
4618      *
4619      * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearTimeout}
4620      */
4621     ;
4622 
4623     _proto.clearTimeout = function clearTimeout(timeoutId) {
4624       if (this.setTimeoutIds_.has(timeoutId)) {
4625         this.setTimeoutIds_["delete"](timeoutId);
4626         window$1.clearTimeout(timeoutId);
4627       }
4628 
4629       return timeoutId;
4630     }
4631     /**
4632      * Creates a function that gets run every `x` milliseconds. This function is a wrapper
4633      * around `window.setInterval`. There are a few reasons to use this one instead though.
4634      * 1. It gets cleared via  {@link Component#clearInterval} when
4635      *    {@link Component#dispose} gets called.
4636      * 2. The function callback will be a {@link Component~GenericCallback}
4637      *
4638      * @param {Component~GenericCallback} fn
4639      *        The function to run every `x` seconds.
4640      *
4641      * @param {number} interval
4642      *        Execute the specified function every `x` milliseconds.
4643      *
4644      * @return {number}
4645      *         Returns an id that can be used to identify the interval. It can also be be used in
4646      *         {@link Component#clearInterval} to clear the interval.
4647      *
4648      * @listens Component#dispose
4649      * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setInterval}
4650      */
4651     ;
4652 
4653     _proto.setInterval = function setInterval(fn, interval) {
4654       fn = bind(this, fn);
4655       this.clearTimersOnDispose_();
4656       var intervalId = window$1.setInterval(fn, interval);
4657       this.setIntervalIds_.add(intervalId);
4658       return intervalId;
4659     }
4660     /**
4661      * Clears an interval that gets created via `window.setInterval` or
4662      * {@link Component#setInterval}. If you set an inteval via {@link Component#setInterval}
4663      * use this function instead of `window.clearInterval`. If you don't your dispose
4664      * listener will not get cleaned up until {@link Component#dispose}!
4665      *
4666      * @param {number} intervalId
4667      *        The id of the interval to clear. The return value of
4668      *        {@link Component#setInterval} or `window.setInterval`.
4669      *
4670      * @return {number}
4671      *         Returns the interval id that was cleared.
4672      *
4673      * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearInterval}
4674      */
4675     ;
4676 
4677     _proto.clearInterval = function clearInterval(intervalId) {
4678       if (this.setIntervalIds_.has(intervalId)) {
4679         this.setIntervalIds_["delete"](intervalId);
4680         window$1.clearInterval(intervalId);
4681       }
4682 
4683       return intervalId;
4684     }
4685     /**
4686      * Queues up a callback to be passed to requestAnimationFrame (rAF), but
4687      * with a few extra bonuses:
4688      *
4689      * - Supports browsers that do not support rAF by falling back to
4690      *   {@link Component#setTimeout}.
4691      *
4692      * - The callback is turned into a {@link Component~GenericCallback} (i.e.
4693      *   bound to the component).
4694      *
4695      * - Automatic cancellation of the rAF callback is handled if the component
4696      *   is disposed before it is called.
4697      *
4698      * @param  {Component~GenericCallback} fn
4699      *         A function that will be bound to this component and executed just
4700      *         before the browser's next repaint.
4701      *
4702      * @return {number}
4703      *         Returns an rAF ID that gets used to identify the timeout. It can
4704      *         also be used in {@link Component#cancelAnimationFrame} to cancel
4705      *         the animation frame callback.
4706      *
4707      * @listens Component#dispose
4708      * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame}
4709      */
4710     ;
4711 
4712     _proto.requestAnimationFrame = function requestAnimationFrame(fn) {
4713       var _this3 = this;
4714 
4715       // Fall back to using a timer.
4716       if (!this.supportsRaf_) {
4717         return this.setTimeout(fn, 1000 / 60);
4718       }
4719 
4720       this.clearTimersOnDispose_(); // declare as variables so they are properly available in rAF function
4721       // eslint-disable-next-line
4722 
4723       var id;
4724       fn = bind(this, fn);
4725       id = window$1.requestAnimationFrame(function () {
4726         if (_this3.rafIds_.has(id)) {
4727           _this3.rafIds_["delete"](id);
4728         }
4729 
4730         fn();
4731       });
4732       this.rafIds_.add(id);
4733       return id;
4734     }
4735     /**
4736      * Cancels a queued callback passed to {@link Component#requestAnimationFrame}
4737      * (rAF).
4738      *
4739      * If you queue an rAF callback via {@link Component#requestAnimationFrame},
4740      * use this function instead of `window.cancelAnimationFrame`. If you don't,
4741      * your dispose listener will not get cleaned up until {@link Component#dispose}!
4742      *
4743      * @param {number} id
4744      *        The rAF ID to clear. The return value of {@link Component#requestAnimationFrame}.
4745      *
4746      * @return {number}
4747      *         Returns the rAF ID that was cleared.
4748      *
4749      * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/window/cancelAnimationFrame}
4750      */
4751     ;
4752 
4753     _proto.cancelAnimationFrame = function cancelAnimationFrame(id) {
4754       // Fall back to using a timer.
4755       if (!this.supportsRaf_) {
4756         return this.clearTimeout(id);
4757       }
4758 
4759       if (this.rafIds_.has(id)) {
4760         this.rafIds_["delete"](id);
4761         window$1.cancelAnimationFrame(id);
4762       }
4763 
4764       return id;
4765     }
4766     /**
4767      * A function to setup `requestAnimationFrame`, `setTimeout`,
4768      * and `setInterval`, clearing on dispose.
4769      *
4770      * > Previously each timer added and removed dispose listeners on it's own.
4771      * For better performance it was decided to batch them all, and use `Set`s
4772      * to track outstanding timer ids.
4773      *
4774      * @private
4775      */
4776     ;
4777 
4778     _proto.clearTimersOnDispose_ = function clearTimersOnDispose_() {
4779       var _this4 = this;
4780 
4781       if (this.clearingTimersOnDispose_) {
4782         return;
4783       }
4784 
4785       this.clearingTimersOnDispose_ = true;
4786       this.one('dispose', function () {
4787         [['rafIds_', 'cancelAnimationFrame'], ['setTimeoutIds_', 'clearTimeout'], ['setIntervalIds_', 'clearInterval']].forEach(function (_ref) {
4788           var idName = _ref[0],
4789               cancelName = _ref[1];
4790 
4791           _this4[idName].forEach(_this4[cancelName], _this4);
4792         });
4793         _this4.clearingTimersOnDispose_ = false;
4794       });
4795     }
4796     /**
4797      * Register a `Component` with `videojs` given the name and the component.
4798      *
4799      * > NOTE: {@link Tech}s should not be registered as a `Component`. {@link Tech}s
4800      *         should be registered using {@link Tech.registerTech} or
4801      *         {@link videojs:videojs.registerTech}.
4802      *
4803      * > NOTE: This function can also be seen on videojs as
4804      *         {@link videojs:videojs.registerComponent}.
4805      *
4806      * @param {string} name
4807      *        The name of the `Component` to register.
4808      *
4809      * @param {Component} ComponentToRegister
4810      *        The `Component` class to register.
4811      *
4812      * @return {Component}
4813      *         The `Component` that was registered.
4814      */
4815     ;
4816 
4817     Component.registerComponent = function registerComponent(name, ComponentToRegister) {
4818       if (typeof name !== 'string' || !name) {
4819         throw new Error("Illegal component name, \"" + name + "\"; must be a non-empty string.");
4820       }
4821 
4822       var Tech = Component.getComponent('Tech'); // We need to make sure this check is only done if Tech has been registered.
4823 
4824       var isTech = Tech && Tech.isTech(ComponentToRegister);
4825       var isComp = Component === ComponentToRegister || Component.prototype.isPrototypeOf(ComponentToRegister.prototype);
4826 
4827       if (isTech || !isComp) {
4828         var reason;
4829 
4830         if (isTech) {
4831           reason = 'techs must be registered using Tech.registerTech()';
4832         } else {
4833           reason = 'must be a Component subclass';
4834         }
4835 
4836         throw new Error("Illegal component, \"" + name + "\"; " + reason + ".");
4837       }
4838 
4839       name = toTitleCase(name);
4840 
4841       if (!Component.components_) {
4842         Component.components_ = {};
4843       }
4844 
4845       var Player = Component.getComponent('Player');
4846 
4847       if (name === 'Player' && Player && Player.players) {
4848         var players = Player.players;
4849         var playerNames = Object.keys(players); // If we have players that were disposed, then their name will still be
4850         // in Players.players. So, we must loop through and verify that the value
4851         // for each item is not null. This allows registration of the Player component
4852         // after all players have been disposed or before any were created.
4853 
4854         if (players && playerNames.length > 0 && playerNames.map(function (pname) {
4855           return players[pname];
4856         }).every(Boolean)) {
4857           throw new Error('Can not register Player component after player has been created.');
4858         }
4859       }
4860 
4861       Component.components_[name] = ComponentToRegister;
4862       Component.components_[toLowerCase(name)] = ComponentToRegister;
4863       return ComponentToRegister;
4864     }
4865     /**
4866      * Get a `Component` based on the name it was registered with.
4867      *
4868      * @param {string} name
4869      *        The Name of the component to get.
4870      *
4871      * @return {Component}
4872      *         The `Component` that got registered under the given name.
4873      *
4874      * @deprecated In `videojs` 6 this will not return `Component`s that were not
4875      *             registered using {@link Component.registerComponent}. Currently we
4876      *             check the global `videojs` object for a `Component` name and
4877      *             return that if it exists.
4878      */
4879     ;
4880 
4881     Component.getComponent = function getComponent(name) {
4882       if (!name || !Component.components_) {
4883         return;
4884       }
4885 
4886       return Component.components_[name];
4887     };
4888 
4889     return Component;
4890   }();
4891   /**
4892    * Whether or not this component supports `requestAnimationFrame`.
4893    *
4894    * This is exposed primarily for testing purposes.
4895    *
4896    * @private
4897    * @type {Boolean}
4898    */
4899 
4900 
4901   Component.prototype.supportsRaf_ = typeof window$1.requestAnimationFrame === 'function' && typeof window$1.cancelAnimationFrame === 'function';
4902   Component.registerComponent('Component', Component);
4903 
4904   function _assertThisInitialized(self) {
4905     if (self === void 0) {
4906       throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
4907     }
4908 
4909     return self;
4910   }
4911 
4912   var assertThisInitialized = _assertThisInitialized;
4913 
4914   function _inheritsLoose(subClass, superClass) {
4915     subClass.prototype = Object.create(superClass.prototype);
4916     subClass.prototype.constructor = subClass;
4917     subClass.__proto__ = superClass;
4918   }
4919 
4920   var inheritsLoose = _inheritsLoose;
4921 
4922   /**
4923    * @file browser.js
4924    * @module browser
4925    */
4926   var USER_AGENT = window$1.navigator && window$1.navigator.userAgent || '';
4927   var webkitVersionMap = /AppleWebKit\/([\d.]+)/i.exec(USER_AGENT);
4928   var appleWebkitVersion = webkitVersionMap ? parseFloat(webkitVersionMap.pop()) : null;
4929   /**
4930    * Whether or not this device is an iPod.
4931    *
4932    * @static
4933    * @const
4934    * @type {Boolean}
4935    */
4936 
4937   var IS_IPOD = /iPod/i.test(USER_AGENT);
4938   /**
4939    * The detected iOS version - or `null`.
4940    *
4941    * @static
4942    * @const
4943    * @type {string|null}
4944    */
4945 
4946   var IOS_VERSION = function () {
4947     var match = USER_AGENT.match(/OS (\d+)_/i);
4948 
4949     if (match && match[1]) {
4950       return match[1];
4951     }
4952 
4953     return null;
4954   }();
4955   /**
4956    * Whether or not this is an Android device.
4957    *
4958    * @static
4959    * @const
4960    * @type {Boolean}
4961    */
4962 
4963   var IS_ANDROID = /Android/i.test(USER_AGENT);
4964   /**
4965    * The detected Android version - or `null`.
4966    *
4967    * @static
4968    * @const
4969    * @type {number|string|null}
4970    */
4971 
4972   var ANDROID_VERSION = function () {
4973     // This matches Android Major.Minor.Patch versions
4974     // ANDROID_VERSION is Major.Minor as a Number, if Minor isn't available, then only Major is returned
4975     var match = USER_AGENT.match(/Android (\d+)(?:\.(\d+))?(?:\.(\d+))*/i);
4976 
4977     if (!match) {
4978       return null;
4979     }
4980 
4981     var major = match[1] && parseFloat(match[1]);
4982     var minor = match[2] && parseFloat(match[2]);
4983 
4984     if (major && minor) {
4985       return parseFloat(match[1] + '.' + match[2]);
4986     } else if (major) {
4987       return major;
4988     }
4989 
4990     return null;
4991   }();
4992   /**
4993    * Whether or not this is a native Android browser.
4994    *
4995    * @static
4996    * @const
4997    * @type {Boolean}
4998    */
4999 
5000   var IS_NATIVE_ANDROID = IS_ANDROID && ANDROID_VERSION < 5 && appleWebkitVersion < 537;
5001   /**
5002    * Whether or not this is Mozilla Firefox.
5003    *
5004    * @static
5005    * @const
5006    * @type {Boolean}
5007    */
5008 
5009   var IS_FIREFOX = /Firefox/i.test(USER_AGENT);
5010   /**
5011    * Whether or not this is Microsoft Edge.
5012    *
5013    * @static
5014    * @const
5015    * @type {Boolean}
5016    */
5017 
5018   var IS_EDGE = /Edge/i.test(USER_AGENT);
5019   /**
5020    * Whether or not this is Google Chrome.
5021    *
5022    * This will also be `true` for Chrome on iOS, which will have different support
5023    * as it is actually Safari under the hood.
5024    *
5025    * @static
5026    * @const
5027    * @type {Boolean}
5028    */
5029 
5030   var IS_CHROME = !IS_EDGE && (/Chrome/i.test(USER_AGENT) || /CriOS/i.test(USER_AGENT));
5031   /**
5032    * The detected Google Chrome version - or `null`.
5033    *
5034    * @static
5035    * @const
5036    * @type {number|null}
5037    */
5038 
5039   var CHROME_VERSION = function () {
5040     var match = USER_AGENT.match(/(Chrome|CriOS)\/(\d+)/);
5041 
5042     if (match && match[2]) {
5043       return parseFloat(match[2]);
5044     }
5045 
5046     return null;
5047   }();
5048   /**
5049    * The detected Internet Explorer version - or `null`.
5050    *
5051    * @static
5052    * @const
5053    * @type {number|null}
5054    */
5055 
5056   var IE_VERSION = function () {
5057     var result = /MSIE\s(\d+)\.\d/.exec(USER_AGENT);
5058     var version = result && parseFloat(result[1]);
5059 
5060     if (!version && /Trident\/7.0/i.test(USER_AGENT) && /rv:11.0/.test(USER_AGENT)) {
5061       // IE 11 has a different user agent string than other IE versions
5062       version = 11.0;
5063     }
5064 
5065     return version;
5066   }();
5067   /**
5068    * Whether or not this is desktop Safari.
5069    *
5070    * @static
5071    * @const
5072    * @type {Boolean}
5073    */
5074 
5075   var IS_SAFARI = /Safari/i.test(USER_AGENT) && !IS_CHROME && !IS_ANDROID && !IS_EDGE;
5076   /**
5077    * Whether or not this is a Windows machine.
5078    *
5079    * @static
5080    * @const
5081    * @type {Boolean}
5082    */
5083 
5084   var IS_WINDOWS = /Windows/i.test(USER_AGENT);
5085   /**
5086    * Whether or not this device is touch-enabled.
5087    *
5088    * @static
5089    * @const
5090    * @type {Boolean}
5091    */
5092 
5093   var TOUCH_ENABLED = isReal() && ('ontouchstart' in window$1 || window$1.navigator.maxTouchPoints || window$1.DocumentTouch && window$1.document instanceof window$1.DocumentTouch);
5094   /**
5095    * Whether or not this device is an iPad.
5096    *
5097    * @static
5098    * @const
5099    * @type {Boolean}
5100    */
5101 
5102   var IS_IPAD = /iPad/i.test(USER_AGENT) || IS_SAFARI && TOUCH_ENABLED && !/iPhone/i.test(USER_AGENT);
5103   /**
5104    * Whether or not this device is an iPhone.
5105    *
5106    * @static
5107    * @const
5108    * @type {Boolean}
5109    */
5110   // The Facebook app's UIWebView identifies as both an iPhone and iPad, so
5111   // to identify iPhones, we need to exclude iPads.
5112   // http://artsy.github.io/blog/2012/10/18/the-perils-of-ios-user-agent-sniffing/
5113 
5114   var IS_IPHONE = /iPhone/i.test(USER_AGENT) && !IS_IPAD;
5115   /**
5116    * Whether or not this is an iOS device.
5117    *
5118    * @static
5119    * @const
5120    * @type {Boolean}
5121    */
5122 
5123   var IS_IOS = IS_IPHONE || IS_IPAD || IS_IPOD;
5124   /**
5125    * Whether or not this is any flavor of Safari - including iOS.
5126    *
5127    * @static
5128    * @const
5129    * @type {Boolean}
5130    */
5131 
5132   var IS_ANY_SAFARI = (IS_SAFARI || IS_IOS) && !IS_CHROME;
5133 
5134   var browser = /*#__PURE__*/Object.freeze({
5135     IS_IPOD: IS_IPOD,
5136     IOS_VERSION: IOS_VERSION,
5137     IS_ANDROID: IS_ANDROID,
5138     ANDROID_VERSION: ANDROID_VERSION,
5139     IS_NATIVE_ANDROID: IS_NATIVE_ANDROID,
5140     IS_FIREFOX: IS_FIREFOX,
5141     IS_EDGE: IS_EDGE,
5142     IS_CHROME: IS_CHROME,
5143     CHROME_VERSION: CHROME_VERSION,
5144     IE_VERSION: IE_VERSION,
5145     IS_SAFARI: IS_SAFARI,
5146     IS_WINDOWS: IS_WINDOWS,
5147     TOUCH_ENABLED: TOUCH_ENABLED,
5148     IS_IPAD: IS_IPAD,
5149     IS_IPHONE: IS_IPHONE,
5150     IS_IOS: IS_IOS,
5151     IS_ANY_SAFARI: IS_ANY_SAFARI
5152   });
5153 
5154   /**
5155    * @file time-ranges.js
5156    * @module time-ranges
5157    */
5158 
5159   /**
5160    * Returns the time for the specified index at the start or end
5161    * of a TimeRange object.
5162    *
5163    * @typedef    {Function} TimeRangeIndex
5164    *
5165    * @param      {number} [index=0]
5166    *             The range number to return the time for.
5167    *
5168    * @return     {number}
5169    *             The time offset at the specified index.
5170    *
5171    * @deprecated The index argument must be provided.
5172    *             In the future, leaving it out will throw an error.
5173    */
5174 
5175   /**
5176    * An object that contains ranges of time.
5177    *
5178    * @typedef  {Object} TimeRange
5179    *
5180    * @property {number} length
5181    *           The number of time ranges represented by this object.
5182    *
5183    * @property {module:time-ranges~TimeRangeIndex} start
5184    *           Returns the time offset at which a specified time range begins.
5185    *
5186    * @property {module:time-ranges~TimeRangeIndex} end
5187    *           Returns the time offset at which a specified time range ends.
5188    *
5189    * @see https://developer.mozilla.org/en-US/docs/Web/API/TimeRanges
5190    */
5191 
5192   /**
5193    * Check if any of the time ranges are over the maximum index.
5194    *
5195    * @private
5196    * @param   {string} fnName
5197    *          The function name to use for logging
5198    *
5199    * @param   {number} index
5200    *          The index to check
5201    *
5202    * @param   {number} maxIndex
5203    *          The maximum possible index
5204    *
5205    * @throws  {Error} if the timeRanges provided are over the maxIndex
5206    */
5207   function rangeCheck(fnName, index, maxIndex) {
5208     if (typeof index !== 'number' || index < 0 || index > maxIndex) {
5209       throw new Error("Failed to execute '" + fnName + "' on 'TimeRanges': The index provided (" + index + ") is non-numeric or out of bounds (0-" + maxIndex + ").");
5210     }
5211   }
5212   /**
5213    * Get the time for the specified index at the start or end
5214    * of a TimeRange object.
5215    *
5216    * @private
5217    * @param      {string} fnName
5218    *             The function name to use for logging
5219    *
5220    * @param      {string} valueIndex
5221    *             The property that should be used to get the time. should be
5222    *             'start' or 'end'
5223    *
5224    * @param      {Array} ranges
5225    *             An array of time ranges
5226    *
5227    * @param      {Array} [rangeIndex=0]
5228    *             The index to start the search at
5229    *
5230    * @return     {number}
5231    *             The time that offset at the specified index.
5232    *
5233    * @deprecated rangeIndex must be set to a value, in the future this will throw an error.
5234    * @throws     {Error} if rangeIndex is more than the length of ranges
5235    */
5236 
5237 
5238   function getRange(fnName, valueIndex, ranges, rangeIndex) {
5239     rangeCheck(fnName, rangeIndex, ranges.length - 1);
5240     return ranges[rangeIndex][valueIndex];
5241   }
5242   /**
5243    * Create a time range object given ranges of time.
5244    *
5245    * @private
5246    * @param   {Array} [ranges]
5247    *          An array of time ranges.
5248    */
5249 
5250 
5251   function createTimeRangesObj(ranges) {
5252     if (ranges === undefined || ranges.length === 0) {
5253       return {
5254         length: 0,
5255         start: function start() {
5256           throw new Error('This TimeRanges object is empty');
5257         },
5258         end: function end() {
5259           throw new Error('This TimeRanges object is empty');
5260         }
5261       };
5262     }
5263 
5264     return {
5265       length: ranges.length,
5266       start: getRange.bind(null, 'start', 0, ranges),
5267       end: getRange.bind(null, 'end', 1, ranges)
5268     };
5269   }
5270   /**
5271    * Create a `TimeRange` object which mimics an
5272    * {@link https://developer.mozilla.org/en-US/docs/Web/API/TimeRanges|HTML5 TimeRanges instance}.
5273    *
5274    * @param {number|Array[]} start
5275    *        The start of a single range (a number) or an array of ranges (an
5276    *        array of arrays of two numbers each).
5277    *
5278    * @param {number} end
5279    *        The end of a single range. Cannot be used with the array form of
5280    *        the `start` argument.
5281    */
5282 
5283 
5284   function createTimeRanges(start, end) {
5285     if (Array.isArray(start)) {
5286       return createTimeRangesObj(start);
5287     } else if (start === undefined || end === undefined) {
5288       return createTimeRangesObj();
5289     }
5290 
5291     return createTimeRangesObj([[start, end]]);
5292   }
5293 
5294   /**
5295    * @file buffer.js
5296    * @module buffer
5297    */
5298   /**
5299    * Compute the percentage of the media that has been buffered.
5300    *
5301    * @param {TimeRange} buffered
5302    *        The current `TimeRange` object representing buffered time ranges
5303    *
5304    * @param {number} duration
5305    *        Total duration of the media
5306    *
5307    * @return {number}
5308    *         Percent buffered of the total duration in decimal form.
5309    */
5310 
5311   function bufferedPercent(buffered, duration) {
5312     var bufferedDuration = 0;
5313     var start;
5314     var end;
5315 
5316     if (!duration) {
5317       return 0;
5318     }
5319 
5320     if (!buffered || !buffered.length) {
5321       buffered = createTimeRanges(0, 0);
5322     }
5323 
5324     for (var i = 0; i < buffered.length; i++) {
5325       start = buffered.start(i);
5326       end = buffered.end(i); // buffered end can be bigger than duration by a very small fraction
5327 
5328       if (end > duration) {
5329         end = duration;
5330       }
5331 
5332       bufferedDuration += end - start;
5333     }
5334 
5335     return bufferedDuration / duration;
5336   }
5337 
5338   /**
5339    * @file fullscreen-api.js
5340    * @module fullscreen-api
5341    * @private
5342    */
5343   /**
5344    * Store the browser-specific methods for the fullscreen API.
5345    *
5346    * @type {Object}
5347    * @see [Specification]{@link https://fullscreen.spec.whatwg.org}
5348    * @see [Map Approach From Screenfull.js]{@link https://github.com/sindresorhus/screenfull.js}
5349    */
5350 
5351   var FullscreenApi = {
5352     prefixed: true
5353   }; // browser API methods
5354 
5355   var apiMap = [['requestFullscreen', 'exitFullscreen', 'fullscreenElement', 'fullscreenEnabled', 'fullscreenchange', 'fullscreenerror', 'fullscreen'], // WebKit
5356   ['webkitRequestFullscreen', 'webkitExitFullscreen', 'webkitFullscreenElement', 'webkitFullscreenEnabled', 'webkitfullscreenchange', 'webkitfullscreenerror', '-webkit-full-screen'], // Mozilla
5357   ['mozRequestFullScreen', 'mozCancelFullScreen', 'mozFullScreenElement', 'mozFullScreenEnabled', 'mozfullscreenchange', 'mozfullscreenerror', '-moz-full-screen'], // Microsoft
5358   ['msRequestFullscreen', 'msExitFullscreen', 'msFullscreenElement', 'msFullscreenEnabled', 'MSFullscreenChange', 'MSFullscreenError', '-ms-fullscreen']];
5359   var specApi = apiMap[0];
5360   var browserApi; // determine the supported set of functions
5361 
5362   for (var i = 0; i < apiMap.length; i++) {
5363     // check for exitFullscreen function
5364     if (apiMap[i][1] in document) {
5365       browserApi = apiMap[i];
5366       break;
5367     }
5368   } // map the browser API names to the spec API names
5369 
5370 
5371   if (browserApi) {
5372     for (var _i = 0; _i < browserApi.length; _i++) {
5373       FullscreenApi[specApi[_i]] = browserApi[_i];
5374     }
5375 
5376     FullscreenApi.prefixed = browserApi[0] !== specApi[0];
5377   }
5378 
5379   /**
5380    * @file media-error.js
5381    */
5382   /**
5383    * A Custom `MediaError` class which mimics the standard HTML5 `MediaError` class.
5384    *
5385    * @param {number|string|Object|MediaError} value
5386    *        This can be of multiple types:
5387    *        - number: should be a standard error code
5388    *        - string: an error message (the code will be 0)
5389    *        - Object: arbitrary properties
5390    *        - `MediaError` (native): used to populate a video.js `MediaError` object
5391    *        - `MediaError` (video.js): will return itself if it's already a
5392    *          video.js `MediaError` object.
5393    *
5394    * @see [MediaError Spec]{@link https://dev.w3.org/html5/spec-author-view/video.html#mediaerror}
5395    * @see [Encrypted MediaError Spec]{@link https://www.w3.org/TR/2013/WD-encrypted-media-20130510/#error-codes}
5396    *
5397    * @class MediaError
5398    */
5399 
5400   function MediaError(value) {
5401     // Allow redundant calls to this constructor to avoid having `instanceof`
5402     // checks peppered around the code.
5403     if (value instanceof MediaError) {
5404       return value;
5405     }
5406 
5407     if (typeof value === 'number') {
5408       this.code = value;
5409     } else if (typeof value === 'string') {
5410       // default code is zero, so this is a custom error
5411       this.message = value;
5412     } else if (isObject(value)) {
5413       // We assign the `code` property manually because native `MediaError` objects
5414       // do not expose it as an own/enumerable property of the object.
5415       if (typeof value.code === 'number') {
5416         this.code = value.code;
5417       }
5418 
5419       assign(this, value);
5420     }
5421 
5422     if (!this.message) {
5423       this.message = MediaError.defaultMessages[this.code] || '';
5424     }
5425   }
5426   /**
5427    * The error code that refers two one of the defined `MediaError` types
5428    *
5429    * @type {Number}
5430    */
5431 
5432 
5433   MediaError.prototype.code = 0;
5434   /**
5435    * An optional message that to show with the error. Message is not part of the HTML5
5436    * video spec but allows for more informative custom errors.
5437    *
5438    * @type {String}
5439    */
5440 
5441   MediaError.prototype.message = '';
5442   /**
5443    * An optional status code that can be set by plugins to allow even more detail about
5444    * the error. For example a plugin might provide a specific HTTP status code and an
5445    * error message for that code. Then when the plugin gets that error this class will
5446    * know how to display an error message for it. This allows a custom message to show
5447    * up on the `Player` error overlay.
5448    *
5449    * @type {Array}
5450    */
5451 
5452   MediaError.prototype.status = null;
5453   /**
5454    * Errors indexed by the W3C standard. The order **CANNOT CHANGE**! See the
5455    * specification listed under {@link MediaError} for more information.
5456    *
5457    * @enum {array}
5458    * @readonly
5459    * @property {string} 0 - MEDIA_ERR_CUSTOM
5460    * @property {string} 1 - MEDIA_ERR_ABORTED
5461    * @property {string} 2 - MEDIA_ERR_NETWORK
5462    * @property {string} 3 - MEDIA_ERR_DECODE
5463    * @property {string} 4 - MEDIA_ERR_SRC_NOT_SUPPORTED
5464    * @property {string} 5 - MEDIA_ERR_ENCRYPTED
5465    */
5466 
5467   MediaError.errorTypes = ['MEDIA_ERR_CUSTOM', 'MEDIA_ERR_ABORTED', 'MEDIA_ERR_NETWORK', 'MEDIA_ERR_DECODE', 'MEDIA_ERR_SRC_NOT_SUPPORTED', 'MEDIA_ERR_ENCRYPTED'];
5468   /**
5469    * The default `MediaError` messages based on the {@link MediaError.errorTypes}.
5470    *
5471    * @type {Array}
5472    * @constant
5473    */
5474 
5475   MediaError.defaultMessages = {
5476     1: 'You aborted the media playback',
5477     2: 'A network error caused the media download to fail part-way.',
5478     3: 'The media playback was aborted due to a corruption problem or because the media used features your browser did not support.',
5479     4: 'The media could not be loaded, either because the server or network failed or because the format is not supported.',
5480     5: 'The media is encrypted and we do not have the keys to decrypt it.'
5481   }; // Add types as properties on MediaError
5482   // e.g. MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED = 4;
5483 
5484   for (var errNum = 0; errNum < MediaError.errorTypes.length; errNum++) {
5485     MediaError[MediaError.errorTypes[errNum]] = errNum; // values should be accessible on both the class and instance
5486 
5487     MediaError.prototype[MediaError.errorTypes[errNum]] = errNum;
5488   } // jsdocs for instance/static members added above
5489 
5490   var tuple = SafeParseTuple;
5491 
5492   function SafeParseTuple(obj, reviver) {
5493     var json;
5494     var error = null;
5495 
5496     try {
5497       json = JSON.parse(obj, reviver);
5498     } catch (err) {
5499       error = err;
5500     }
5501 
5502     return [error, json];
5503   }
5504 
5505   /**
5506    * Returns whether an object is `Promise`-like (i.e. has a `then` method).
5507    *
5508    * @param  {Object}  value
5509    *         An object that may or may not be `Promise`-like.
5510    *
5511    * @return {boolean}
5512    *         Whether or not the object is `Promise`-like.
5513    */
5514   function isPromise(value) {
5515     return value !== undefined && value !== null && typeof value.then === 'function';
5516   }
5517   /**
5518    * Silence a Promise-like object.
5519    *
5520    * This is useful for avoiding non-harmful, but potentially confusing "uncaught
5521    * play promise" rejection error messages.
5522    *
5523    * @param  {Object} value
5524    *         An object that may or may not be `Promise`-like.
5525    */
5526 
5527   function silencePromise(value) {
5528     if (isPromise(value)) {
5529       value.then(null, function (e) {});
5530     }
5531   }
5532 
5533   /**
5534    * @file text-track-list-converter.js Utilities for capturing text track state and
5535    * re-creating tracks based on a capture.
5536    *
5537    * @module text-track-list-converter
5538    */
5539 
5540   /**
5541    * Examine a single {@link TextTrack} and return a JSON-compatible javascript object that
5542    * represents the {@link TextTrack}'s state.
5543    *
5544    * @param {TextTrack} track
5545    *        The text track to query.
5546    *
5547    * @return {Object}
5548    *         A serializable javascript representation of the TextTrack.
5549    * @private
5550    */
5551   var trackToJson_ = function trackToJson_(track) {
5552     var ret = ['kind', 'label', 'language', 'id', 'inBandMetadataTrackDispatchType', 'mode', 'src'].reduce(function (acc, prop, i) {
5553       if (track[prop]) {
5554         acc[prop] = track[prop];
5555       }
5556 
5557       return acc;
5558     }, {
5559       cues: track.cues && Array.prototype.map.call(track.cues, function (cue) {
5560         return {
5561           startTime: cue.startTime,
5562           endTime: cue.endTime,
5563           text: cue.text,
5564           id: cue.id
5565         };
5566       })
5567     });
5568     return ret;
5569   };
5570   /**
5571    * Examine a {@link Tech} and return a JSON-compatible javascript array that represents the
5572    * state of all {@link TextTrack}s currently configured. The return array is compatible with
5573    * {@link text-track-list-converter:jsonToTextTracks}.
5574    *
5575    * @param {Tech} tech
5576    *        The tech object to query
5577    *
5578    * @return {Array}
5579    *         A serializable javascript representation of the {@link Tech}s
5580    *         {@link TextTrackList}.
5581    */
5582 
5583 
5584   var textTracksToJson = function textTracksToJson(tech) {
5585     var trackEls = tech.$$('track');
5586     var trackObjs = Array.prototype.map.call(trackEls, function (t) {
5587       return t.track;
5588     });
5589     var tracks = Array.prototype.map.call(trackEls, function (trackEl) {
5590       var json = trackToJson_(trackEl.track);
5591 
5592       if (trackEl.src) {
5593         json.src = trackEl.src;
5594       }
5595 
5596       return json;
5597     });
5598     return tracks.concat(Array.prototype.filter.call(tech.textTracks(), function (track) {
5599       return trackObjs.indexOf(track) === -1;
5600     }).map(trackToJson_));
5601   };
5602   /**
5603    * Create a set of remote {@link TextTrack}s on a {@link Tech} based on an array of javascript
5604    * object {@link TextTrack} representations.
5605    *
5606    * @param {Array} json
5607    *        An array of `TextTrack` representation objects, like those that would be
5608    *        produced by `textTracksToJson`.
5609    *
5610    * @param {Tech} tech
5611    *        The `Tech` to create the `TextTrack`s on.
5612    */
5613 
5614 
5615   var jsonToTextTracks = function jsonToTextTracks(json, tech) {
5616     json.forEach(function (track) {
5617       var addedTrack = tech.addRemoteTextTrack(track).track;
5618 
5619       if (!track.src && track.cues) {
5620         track.cues.forEach(function (cue) {
5621           return addedTrack.addCue(cue);
5622         });
5623       }
5624     });
5625     return tech.textTracks();
5626   };
5627 
5628   var textTrackConverter = {
5629     textTracksToJson: textTracksToJson,
5630     jsonToTextTracks: jsonToTextTracks,
5631     trackToJson_: trackToJson_
5632   };
5633 
5634   var keycode = createCommonjsModule(function (module, exports) {
5635     // Source: http://jsfiddle.net/vWx8V/
5636     // http://stackoverflow.com/questions/5603195/full-list-of-javascript-keycodes
5637 
5638     /**
5639      * Conenience method returns corresponding value for given keyName or keyCode.
5640      *
5641      * @param {Mixed} keyCode {Number} or keyName {String}
5642      * @return {Mixed}
5643      * @api public
5644      */
5645     function keyCode(searchInput) {
5646       // Keyboard Events
5647       if (searchInput && 'object' === typeof searchInput) {
5648         var hasKeyCode = searchInput.which || searchInput.keyCode || searchInput.charCode;
5649         if (hasKeyCode) searchInput = hasKeyCode;
5650       } // Numbers
5651 
5652 
5653       if ('number' === typeof searchInput) return names[searchInput]; // Everything else (cast to string)
5654 
5655       var search = String(searchInput); // check codes
5656 
5657       var foundNamedKey = codes[search.toLowerCase()];
5658       if (foundNamedKey) return foundNamedKey; // check aliases
5659 
5660       var foundNamedKey = aliases[search.toLowerCase()];
5661       if (foundNamedKey) return foundNamedKey; // weird character?
5662 
5663       if (search.length === 1) return search.charCodeAt(0);
5664       return undefined;
5665     }
5666     /**
5667      * Compares a keyboard event with a given keyCode or keyName.
5668      *
5669      * @param {Event} event Keyboard event that should be tested
5670      * @param {Mixed} keyCode {Number} or keyName {String}
5671      * @return {Boolean}
5672      * @api public
5673      */
5674 
5675 
5676     keyCode.isEventKey = function isEventKey(event, nameOrCode) {
5677       if (event && 'object' === typeof event) {
5678         var keyCode = event.which || event.keyCode || event.charCode;
5679 
5680         if (keyCode === null || keyCode === undefined) {
5681           return false;
5682         }
5683 
5684         if (typeof nameOrCode === 'string') {
5685           // check codes
5686           var foundNamedKey = codes[nameOrCode.toLowerCase()];
5687 
5688           if (foundNamedKey) {
5689             return foundNamedKey === keyCode;
5690           } // check aliases
5691 
5692 
5693           var foundNamedKey = aliases[nameOrCode.toLowerCase()];
5694 
5695           if (foundNamedKey) {
5696             return foundNamedKey === keyCode;
5697           }
5698         } else if (typeof nameOrCode === 'number') {
5699           return nameOrCode === keyCode;
5700         }
5701 
5702         return false;
5703       }
5704     };
5705 
5706     exports = module.exports = keyCode;
5707     /**
5708      * Get by name
5709      *
5710      *   exports.code['enter'] // => 13
5711      */
5712 
5713     var codes = exports.code = exports.codes = {
5714       'backspace': 8,
5715       'tab': 9,
5716       'enter': 13,
5717       'shift': 16,
5718       'ctrl': 17,
5719       'alt': 18,
5720       'pause/break': 19,
5721       'caps lock': 20,
5722       'esc': 27,
5723       'space': 32,
5724       'page up': 33,
5725       'page down': 34,
5726       'end': 35,
5727       'home': 36,
5728       'left': 37,
5729       'up': 38,
5730       'right': 39,
5731       'down': 40,
5732       'insert': 45,
5733       'delete': 46,
5734       'command': 91,
5735       'left command': 91,
5736       'right command': 93,
5737       'numpad *': 106,
5738       'numpad +': 107,
5739       'numpad -': 109,
5740       'numpad .': 110,
5741       'numpad /': 111,
5742       'num lock': 144,
5743       'scroll lock': 145,
5744       'my computer': 182,
5745       'my calculator': 183,
5746       ';': 186,
5747       '=': 187,
5748       ',': 188,
5749       '-': 189,
5750       '.': 190,
5751       '/': 191,
5752       '`': 192,
5753       '[': 219,
5754       '\\': 220,
5755       ']': 221,
5756       "'": 222 // Helper aliases
5757 
5758     };
5759     var aliases = exports.aliases = {
5760       'windows': 91,
5761       '⇧': 16,
5762       '⌥': 18,
5763       '⌃': 17,
5764       '⌘': 91,
5765       'ctl': 17,
5766       'control': 17,
5767       'option': 18,
5768       'pause': 19,
5769       'break': 19,
5770       'caps': 20,
5771       'return': 13,
5772       'escape': 27,
5773       'spc': 32,
5774       'spacebar': 32,
5775       'pgup': 33,
5776       'pgdn': 34,
5777       'ins': 45,
5778       'del': 46,
5779       'cmd': 91
5780       /*!
5781        * Programatically add the following
5782        */
5783       // lower case chars
5784 
5785     };
5786 
5787     for (i = 97; i < 123; i++) {
5788       codes[String.fromCharCode(i)] = i - 32;
5789     } // numbers
5790 
5791 
5792     for (var i = 48; i < 58; i++) {
5793       codes[i - 48] = i;
5794     } // function keys
5795 
5796 
5797     for (i = 1; i < 13; i++) {
5798       codes['f' + i] = i + 111;
5799     } // numpad keys
5800 
5801 
5802     for (i = 0; i < 10; i++) {
5803       codes['numpad ' + i] = i + 96;
5804     }
5805     /**
5806      * Get by code
5807      *
5808      *   exports.name[13] // => 'Enter'
5809      */
5810 
5811 
5812     var names = exports.names = exports.title = {}; // title for backward compat
5813     // Create reverse mapping
5814 
5815     for (i in codes) {
5816       names[codes[i]] = i;
5817     } // Add aliases
5818 
5819 
5820     for (var alias in aliases) {
5821       codes[alias] = aliases[alias];
5822     }
5823   });
5824   var keycode_1 = keycode.code;
5825   var keycode_2 = keycode.codes;
5826   var keycode_3 = keycode.aliases;
5827   var keycode_4 = keycode.names;
5828   var keycode_5 = keycode.title;
5829 
5830   var MODAL_CLASS_NAME = 'vjs-modal-dialog';
5831   /**
5832    * The `ModalDialog` displays over the video and its controls, which blocks
5833    * interaction with the player until it is closed.
5834    *
5835    * Modal dialogs include a "Close" button and will close when that button
5836    * is activated - or when ESC is pressed anywhere.
5837    *
5838    * @extends Component
5839    */
5840 
5841   var ModalDialog =
5842   /*#__PURE__*/
5843   function (_Component) {
5844     inheritsLoose(ModalDialog, _Component);
5845 
5846     /**
5847      * Create an instance of this class.
5848      *
5849      * @param {Player} player
5850      *        The `Player` that this class should be attached to.
5851      *
5852      * @param {Object} [options]
5853      *        The key/value store of player options.
5854      *
5855      * @param {Mixed} [options.content=undefined]
5856      *        Provide customized content for this modal.
5857      *
5858      * @param {string} [options.description]
5859      *        A text description for the modal, primarily for accessibility.
5860      *
5861      * @param {boolean} [options.fillAlways=false]
5862      *        Normally, modals are automatically filled only the first time
5863      *        they open. This tells the modal to refresh its content
5864      *        every time it opens.
5865      *
5866      * @param {string} [options.label]
5867      *        A text label for the modal, primarily for accessibility.
5868      *
5869      * @param {boolean} [options.pauseOnOpen=true]
5870      *        If `true`, playback will will be paused if playing when
5871      *        the modal opens, and resumed when it closes.
5872      *
5873      * @param {boolean} [options.temporary=true]
5874      *        If `true`, the modal can only be opened once; it will be
5875      *        disposed as soon as it's closed.
5876      *
5877      * @param {boolean} [options.uncloseable=false]
5878      *        If `true`, the user will not be able to close the modal
5879      *        through the UI in the normal ways. Programmatic closing is
5880      *        still possible.
5881      */
5882     function ModalDialog(player, options) {
5883       var _this;
5884 
5885       _this = _Component.call(this, player, options) || this;
5886       _this.opened_ = _this.hasBeenOpened_ = _this.hasBeenFilled_ = false;
5887 
5888       _this.closeable(!_this.options_.uncloseable);
5889 
5890       _this.content(_this.options_.content); // Make sure the contentEl is defined AFTER any children are initialized
5891       // because we only want the contents of the modal in the contentEl
5892       // (not the UI elements like the close button).
5893 
5894 
5895       _this.contentEl_ = createEl('div', {
5896         className: MODAL_CLASS_NAME + "-content"
5897       }, {
5898         role: 'document'
5899       });
5900       _this.descEl_ = createEl('p', {
5901         className: MODAL_CLASS_NAME + "-description vjs-control-text",
5902         id: _this.el().getAttribute('aria-describedby')
5903       });
5904       textContent(_this.descEl_, _this.description());
5905 
5906       _this.el_.appendChild(_this.descEl_);
5907 
5908       _this.el_.appendChild(_this.contentEl_);
5909 
5910       return _this;
5911     }
5912     /**
5913      * Create the `ModalDialog`'s DOM element
5914      *
5915      * @return {Element}
5916      *         The DOM element that gets created.
5917      */
5918 
5919 
5920     var _proto = ModalDialog.prototype;
5921 
5922     _proto.createEl = function createEl() {
5923       return _Component.prototype.createEl.call(this, 'div', {
5924         className: this.buildCSSClass(),
5925         tabIndex: -1
5926       }, {
5927         'aria-describedby': this.id() + "_description",
5928         'aria-hidden': 'true',
5929         'aria-label': this.label(),
5930         'role': 'dialog'
5931       });
5932     };
5933 
5934     _proto.dispose = function dispose() {
5935       this.contentEl_ = null;
5936       this.descEl_ = null;
5937       this.previouslyActiveEl_ = null;
5938 
5939       _Component.prototype.dispose.call(this);
5940     }
5941     /**
5942      * Builds the default DOM `className`.
5943      *
5944      * @return {string}
5945      *         The DOM `className` for this object.
5946      */
5947     ;
5948 
5949     _proto.buildCSSClass = function buildCSSClass() {
5950       return MODAL_CLASS_NAME + " vjs-hidden " + _Component.prototype.buildCSSClass.call(this);
5951     }
5952     /**
vendor: 37,023 bytes, lines 5953-7331
5953      * Returns the label string for this modal. Primarily used for accessibility.
5954      *
5955      * @return {string}
5956      *         the localized or raw label of this modal.
5957      */
5958     ;
5959 
5960     _proto.label = function label() {
5961       return this.localize(this.options_.label || 'Modal Window');
5962     }
5963     /**
5964      * Returns the description string for this modal. Primarily used for
5965      * accessibility.
5966      *
5967      * @return {string}
5968      *         The localized or raw description of this modal.
5969      */
5970     ;
5971 
5972     _proto.description = function description() {
5973       var desc = this.options_.description || this.localize('This is a modal window.'); // Append a universal closeability message if the modal is closeable.
5974 
5975       if (this.closeable()) {
5976         desc += ' ' + this.localize('This modal can be closed by pressing the Escape key or activating the close button.');
5977       }
5978 
5979       return desc;
5980     }
5981     /**
5982      * Opens the modal.
5983      *
5984      * @fires ModalDialog#beforemodalopen
5985      * @fires ModalDialog#modalopen
5986      */
5987     ;
5988 
5989     _proto.open = function open() {
5990       if (!this.opened_) {
5991         var player = this.player();
5992         /**
5993           * Fired just before a `ModalDialog` is opened.
5994           *
5995           * @event ModalDialog#beforemodalopen
5996           * @type {EventTarget~Event}
5997           */
5998 
5999         this.trigger('beforemodalopen');
6000         this.opened_ = true; // Fill content if the modal has never opened before and
6001         // never been filled.
6002 
6003         if (this.options_.fillAlways || !this.hasBeenOpened_ && !this.hasBeenFilled_) {
6004           this.fill();
6005         } // If the player was playing, pause it and take note of its previously
6006         // playing state.
6007 
6008 
6009         this.wasPlaying_ = !player.paused();
6010 
6011         if (this.options_.pauseOnOpen && this.wasPlaying_) {
6012           player.pause();
6013         }
6014 
6015         this.on('keydown', this.handleKeyDown); // Hide controls and note if they were enabled.
6016 
6017         this.hadControls_ = player.controls();
6018         player.controls(false);
6019         this.show();
6020         this.conditionalFocus_();
6021         this.el().setAttribute('aria-hidden', 'false');
6022         /**
6023           * Fired just after a `ModalDialog` is opened.
6024           *
6025           * @event ModalDialog#modalopen
6026           * @type {EventTarget~Event}
6027           */
6028 
6029         this.trigger('modalopen');
6030         this.hasBeenOpened_ = true;
6031       }
6032     }
6033     /**
6034      * If the `ModalDialog` is currently open or closed.
6035      *
6036      * @param  {boolean} [value]
6037      *         If given, it will open (`true`) or close (`false`) the modal.
6038      *
6039      * @return {boolean}
6040      *         the current open state of the modaldialog
6041      */
6042     ;
6043 
6044     _proto.opened = function opened(value) {
6045       if (typeof value === 'boolean') {
6046         this[value ? 'open' : 'close']();
6047       }
6048 
6049       return this.opened_;
6050     }
6051     /**
6052      * Closes the modal, does nothing if the `ModalDialog` is
6053      * not open.
6054      *
6055      * @fires ModalDialog#beforemodalclose
6056      * @fires ModalDialog#modalclose
6057      */
6058     ;
6059 
6060     _proto.close = function close() {
6061       if (!this.opened_) {
6062         return;
6063       }
6064 
6065       var player = this.player();
6066       /**
6067         * Fired just before a `ModalDialog` is closed.
6068         *
6069         * @event ModalDialog#beforemodalclose
6070         * @type {EventTarget~Event}
6071         */
6072 
6073       this.trigger('beforemodalclose');
6074       this.opened_ = false;
6075 
6076       if (this.wasPlaying_ && this.options_.pauseOnOpen) {
6077         player.play();
6078       }
6079 
6080       this.off('keydown', this.handleKeyDown);
6081 
6082       if (this.hadControls_) {
6083         player.controls(true);
6084       }
6085 
6086       this.hide();
6087       this.el().setAttribute('aria-hidden', 'true');
6088       /**
6089         * Fired just after a `ModalDialog` is closed.
6090         *
6091         * @event ModalDialog#modalclose
6092         * @type {EventTarget~Event}
6093         */
6094 
6095       this.trigger('modalclose');
6096       this.conditionalBlur_();
6097 
6098       if (this.options_.temporary) {
6099         this.dispose();
6100       }
6101     }
6102     /**
6103      * Check to see if the `ModalDialog` is closeable via the UI.
6104      *
6105      * @param  {boolean} [value]
6106      *         If given as a boolean, it will set the `closeable` option.
6107      *
6108      * @return {boolean}
6109      *         Returns the final value of the closable option.
6110      */
6111     ;
6112 
6113     _proto.closeable = function closeable(value) {
6114       if (typeof value === 'boolean') {
6115         var closeable = this.closeable_ = !!value;
6116         var close = this.getChild('closeButton'); // If this is being made closeable and has no close button, add one.
6117 
6118         if (closeable && !close) {
6119           // The close button should be a child of the modal - not its
6120           // content element, so temporarily change the content element.
6121           var temp = this.contentEl_;
6122           this.contentEl_ = this.el_;
6123           close = this.addChild('closeButton', {
6124             controlText: 'Close Modal Dialog'
6125           });
6126           this.contentEl_ = temp;
6127           this.on(close, 'close', this.close);
6128         } // If this is being made uncloseable and has a close button, remove it.
6129 
6130 
6131         if (!closeable && close) {
6132           this.off(close, 'close', this.close);
6133           this.removeChild(close);
6134           close.dispose();
6135         }
6136       }
6137 
6138       return this.closeable_;
6139     }
6140     /**
6141      * Fill the modal's content element with the modal's "content" option.
6142      * The content element will be emptied before this change takes place.
6143      */
6144     ;
6145 
6146     _proto.fill = function fill() {
6147       this.fillWith(this.content());
6148     }
6149     /**
6150      * Fill the modal's content element with arbitrary content.
6151      * The content element will be emptied before this change takes place.
6152      *
6153      * @fires ModalDialog#beforemodalfill
6154      * @fires ModalDialog#modalfill
6155      *
6156      * @param {Mixed} [content]
6157      *        The same rules apply to this as apply to the `content` option.
6158      */
6159     ;
6160 
6161     _proto.fillWith = function fillWith(content) {
6162       var contentEl = this.contentEl();
6163       var parentEl = contentEl.parentNode;
6164       var nextSiblingEl = contentEl.nextSibling;
6165       /**
6166         * Fired just before a `ModalDialog` is filled with content.
6167         *
6168         * @event ModalDialog#beforemodalfill
6169         * @type {EventTarget~Event}
6170         */
6171 
6172       this.trigger('beforemodalfill');
6173       this.hasBeenFilled_ = true; // Detach the content element from the DOM before performing
6174       // manipulation to avoid modifying the live DOM multiple times.
6175 
6176       parentEl.removeChild(contentEl);
6177       this.empty();
6178       insertContent(contentEl, content);
6179       /**
6180        * Fired just after a `ModalDialog` is filled with content.
6181        *
6182        * @event ModalDialog#modalfill
6183        * @type {EventTarget~Event}
6184        */
6185 
6186       this.trigger('modalfill'); // Re-inject the re-filled content element.
6187 
6188       if (nextSiblingEl) {
6189         parentEl.insertBefore(contentEl, nextSiblingEl);
6190       } else {
6191         parentEl.appendChild(contentEl);
6192       } // make sure that the close button is last in the dialog DOM
6193 
6194 
6195       var closeButton = this.getChild('closeButton');
6196 
6197       if (closeButton) {
6198         parentEl.appendChild(closeButton.el_);
6199       }
6200     }
6201     /**
6202      * Empties the content element. This happens anytime the modal is filled.
6203      *
6204      * @fires ModalDialog#beforemodalempty
6205      * @fires ModalDialog#modalempty
6206      */
6207     ;
6208 
6209     _proto.empty = function empty() {
6210       /**
6211       * Fired just before a `ModalDialog` is emptied.
6212       *
6213       * @event ModalDialog#beforemodalempty
6214       * @type {EventTarget~Event}
6215       */
6216       this.trigger('beforemodalempty');
6217       emptyEl(this.contentEl());
6218       /**
6219       * Fired just after a `ModalDialog` is emptied.
6220       *
6221       * @event ModalDialog#modalempty
6222       * @type {EventTarget~Event}
6223       */
6224 
6225       this.trigger('modalempty');
6226     }
6227     /**
6228      * Gets or sets the modal content, which gets normalized before being
6229      * rendered into the DOM.
6230      *
6231      * This does not update the DOM or fill the modal, but it is called during
6232      * that process.
6233      *
6234      * @param  {Mixed} [value]
6235      *         If defined, sets the internal content value to be used on the
6236      *         next call(s) to `fill`. This value is normalized before being
6237      *         inserted. To "clear" the internal content value, pass `null`.
6238      *
6239      * @return {Mixed}
6240      *         The current content of the modal dialog
6241      */
6242     ;
6243 
6244     _proto.content = function content(value) {
6245       if (typeof value !== 'undefined') {
6246         this.content_ = value;
6247       }
6248 
6249       return this.content_;
6250     }
6251     /**
6252      * conditionally focus the modal dialog if focus was previously on the player.
6253      *
6254      * @private
6255      */
6256     ;
6257 
6258     _proto.conditionalFocus_ = function conditionalFocus_() {
6259       var activeEl = document.activeElement;
6260       var playerEl = this.player_.el_;
6261       this.previouslyActiveEl_ = null;
6262 
6263       if (playerEl.contains(activeEl) || playerEl === activeEl) {
6264         this.previouslyActiveEl_ = activeEl;
6265         this.focus();
6266       }
6267     }
6268     /**
6269      * conditionally blur the element and refocus the last focused element
6270      *
6271      * @private
6272      */
6273     ;
6274 
6275     _proto.conditionalBlur_ = function conditionalBlur_() {
6276       if (this.previouslyActiveEl_) {
6277         this.previouslyActiveEl_.focus();
6278         this.previouslyActiveEl_ = null;
6279       }
6280     }
6281     /**
6282      * Keydown handler. Attached when modal is focused.
6283      *
6284      * @listens keydown
6285      */
6286     ;
6287 
6288     _proto.handleKeyDown = function handleKeyDown(event) {
6289       // Do not allow keydowns to reach out of the modal dialog.
6290       event.stopPropagation();
6291 
6292       if (keycode.isEventKey(event, 'Escape') && this.closeable()) {
6293         event.preventDefault();
6294         this.close();
6295         return;
6296       } // exit early if it isn't a tab key
6297 
6298 
6299       if (!keycode.isEventKey(event, 'Tab')) {
6300         return;
6301       }
6302 
6303       var focusableEls = this.focusableEls_();
6304       var activeEl = this.el_.querySelector(':focus');
6305       var focusIndex;
6306 
6307       for (var i = 0; i < focusableEls.length; i++) {
6308         if (activeEl === focusableEls[i]) {
6309           focusIndex = i;
6310           break;
6311         }
6312       }
6313 
6314       if (document.activeElement === this.el_) {
6315         focusIndex = 0;
6316       }
6317 
6318       if (event.shiftKey && focusIndex === 0) {
6319         focusableEls[focusableEls.length - 1].focus();
6320         event.preventDefault();
6321       } else if (!event.shiftKey && focusIndex === focusableEls.length - 1) {
6322         focusableEls[0].focus();
6323         event.preventDefault();
6324       }
6325     }
6326     /**
6327      * get all focusable elements
6328      *
6329      * @private
6330      */
6331     ;
6332 
6333     _proto.focusableEls_ = function focusableEls_() {
6334       var allChildren = this.el_.querySelectorAll('*');
6335       return Array.prototype.filter.call(allChildren, function (child) {
6336         return (child instanceof window$1.HTMLAnchorElement || child instanceof window$1.HTMLAreaElement) && child.hasAttribute('href') || (child instanceof window$1.HTMLInputElement || child instanceof window$1.HTMLSelectElement || child instanceof window$1.HTMLTextAreaElement || child instanceof window$1.HTMLButtonElement) && !child.hasAttribute('disabled') || child instanceof window$1.HTMLIFrameElement || child instanceof window$1.HTMLObjectElement || child instanceof window$1.HTMLEmbedElement || child.hasAttribute('tabindex') && child.getAttribute('tabindex') !== -1 || child.hasAttribute('contenteditable');
6337       });
6338     };
6339 
6340     return ModalDialog;
6341   }(Component);
6342   /**
6343    * Default options for `ModalDialog` default options.
6344    *
6345    * @type {Object}
6346    * @private
6347    */
6348 
6349 
6350   ModalDialog.prototype.options_ = {
6351     pauseOnOpen: true,
6352     temporary: true
6353   };
6354   Component.registerComponent('ModalDialog', ModalDialog);
6355 
6356   /**
6357    * Common functionaliy between {@link TextTrackList}, {@link AudioTrackList}, and
6358    * {@link VideoTrackList}
6359    *
6360    * @extends EventTarget
6361    */
6362 
6363   var TrackList =
6364   /*#__PURE__*/
6365   function (_EventTarget) {
6366     inheritsLoose(TrackList, _EventTarget);
6367 
6368     /**
6369      * Create an instance of this class
6370      *
6371      * @param {Track[]} tracks
6372      *        A list of tracks to initialize the list with.
6373      *
6374      * @abstract
6375      */
6376     function TrackList(tracks) {
6377       var _this;
6378 
6379       if (tracks === void 0) {
6380         tracks = [];
6381       }
6382 
6383       _this = _EventTarget.call(this) || this;
6384       _this.tracks_ = [];
6385       /**
6386        * @memberof TrackList
6387        * @member {number} length
6388        *         The current number of `Track`s in the this Trackist.
6389        * @instance
6390        */
6391 
6392       Object.defineProperty(assertThisInitialized(_this), 'length', {
6393         get: function get() {
6394           return this.tracks_.length;
6395         }
6396       });
6397 
6398       for (var i = 0; i < tracks.length; i++) {
6399         _this.addTrack(tracks[i]);
6400       }
6401 
6402       return _this;
6403     }
6404     /**
6405      * Add a {@link Track} to the `TrackList`
6406      *
6407      * @param {Track} track
6408      *        The audio, video, or text track to add to the list.
6409      *
6410      * @fires TrackList#addtrack
6411      */
6412 
6413 
6414     var _proto = TrackList.prototype;
6415 
6416     _proto.addTrack = function addTrack(track) {
6417       var index = this.tracks_.length;
6418 
6419       if (!('' + index in this)) {
6420         Object.defineProperty(this, index, {
6421           get: function get() {
6422             return this.tracks_[index];
6423           }
6424         });
6425       } // Do not add duplicate tracks
6426 
6427 
6428       if (this.tracks_.indexOf(track) === -1) {
6429         this.tracks_.push(track);
6430         /**
6431          * Triggered when a track is added to a track list.
6432          *
6433          * @event TrackList#addtrack
6434          * @type {EventTarget~Event}
6435          * @property {Track} track
6436          *           A reference to track that was added.
6437          */
6438 
6439         this.trigger({
6440           track: track,
6441           type: 'addtrack',
6442           target: this
6443         });
6444       }
6445     }
6446     /**
6447      * Remove a {@link Track} from the `TrackList`
6448      *
6449      * @param {Track} rtrack
6450      *        The audio, video, or text track to remove from the list.
6451      *
6452      * @fires TrackList#removetrack
6453      */
6454     ;
6455 
6456     _proto.removeTrack = function removeTrack(rtrack) {
6457       var track;
6458 
6459       for (var i = 0, l = this.length; i < l; i++) {
6460         if (this[i] === rtrack) {
6461           track = this[i];
6462 
6463           if (track.off) {
6464             track.off();
6465           }
6466 
6467           this.tracks_.splice(i, 1);
6468           break;
6469         }
6470       }
6471 
6472       if (!track) {
6473         return;
6474       }
6475       /**
6476        * Triggered when a track is removed from track list.
6477        *
6478        * @event TrackList#removetrack
6479        * @type {EventTarget~Event}
6480        * @property {Track} track
6481        *           A reference to track that was removed.
6482        */
6483 
6484 
6485       this.trigger({
6486         track: track,
6487         type: 'removetrack',
6488         target: this
6489       });
6490     }
6491     /**
6492      * Get a Track from the TrackList by a tracks id
6493      *
6494      * @param {string} id - the id of the track to get
6495      * @method getTrackById
6496      * @return {Track}
6497      * @private
6498      */
6499     ;
6500 
6501     _proto.getTrackById = function getTrackById(id) {
6502       var result = null;
6503 
6504       for (var i = 0, l = this.length; i < l; i++) {
6505         var track = this[i];
6506 
6507         if (track.id === id) {
6508           result = track;
6509           break;
6510         }
6511       }
6512 
6513       return result;
6514     };
6515 
6516     return TrackList;
6517   }(EventTarget);
6518   /**
6519    * Triggered when a different track is selected/enabled.
6520    *
6521    * @event TrackList#change
6522    * @type {EventTarget~Event}
6523    */
6524 
6525   /**
6526    * Events that can be called with on + eventName. See {@link EventHandler}.
6527    *
6528    * @property {Object} TrackList#allowedEvents_
6529    * @private
6530    */
6531 
6532 
6533   TrackList.prototype.allowedEvents_ = {
6534     change: 'change',
6535     addtrack: 'addtrack',
6536     removetrack: 'removetrack'
6537   }; // emulate attribute EventHandler support to allow for feature detection
6538 
6539   for (var event in TrackList.prototype.allowedEvents_) {
6540     TrackList.prototype['on' + event] = null;
6541   }
6542 
6543   /**
6544    * Anywhere we call this function we diverge from the spec
6545    * as we only support one enabled audiotrack at a time
6546    *
6547    * @param {AudioTrackList} list
6548    *        list to work on
6549    *
6550    * @param {AudioTrack} track
6551    *        The track to skip
6552    *
6553    * @private
6554    */
6555 
6556   var disableOthers = function disableOthers(list, track) {
6557     for (var i = 0; i < list.length; i++) {
6558       if (!Object.keys(list[i]).length || track.id === list[i].id) {
6559         continue;
6560       } // another audio track is enabled, disable it
6561 
6562 
6563       list[i].enabled = false;
6564     }
6565   };
6566   /**
6567    * The current list of {@link AudioTrack} for a media file.
6568    *
6569    * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotracklist}
6570    * @extends TrackList
6571    */
6572 
6573 
6574   var AudioTrackList =
6575   /*#__PURE__*/
6576   function (_TrackList) {
6577     inheritsLoose(AudioTrackList, _TrackList);
6578 
6579     /**
6580      * Create an instance of this class.
6581      *
6582      * @param {AudioTrack[]} [tracks=[]]
6583      *        A list of `AudioTrack` to instantiate the list with.
6584      */
6585     function AudioTrackList(tracks) {
6586       var _this;
6587 
6588       if (tracks === void 0) {
6589         tracks = [];
6590       }
6591 
6592       // make sure only 1 track is enabled
6593       // sorted from last index to first index
6594       for (var i = tracks.length - 1; i >= 0; i--) {
6595         if (tracks[i].enabled) {
6596           disableOthers(tracks, tracks[i]);
6597           break;
6598         }
6599       }
6600 
6601       _this = _TrackList.call(this, tracks) || this;
6602       _this.changing_ = false;
6603       return _this;
6604     }
6605     /**
6606      * Add an {@link AudioTrack} to the `AudioTrackList`.
6607      *
6608      * @param {AudioTrack} track
6609      *        The AudioTrack to add to the list
6610      *
6611      * @fires TrackList#addtrack
6612      */
6613 
6614 
6615     var _proto = AudioTrackList.prototype;
6616 
6617     _proto.addTrack = function addTrack(track) {
6618       var _this2 = this;
6619 
6620       if (track.enabled) {
6621         disableOthers(this, track);
6622       }
6623 
6624       _TrackList.prototype.addTrack.call(this, track); // native tracks don't have this
6625 
6626 
6627       if (!track.addEventListener) {
6628         return;
6629       }
6630 
6631       track.enabledChange_ = function () {
6632         // when we are disabling other tracks (since we don't support
6633         // more than one track at a time) we will set changing_
6634         // to true so that we don't trigger additional change events
6635         if (_this2.changing_) {
6636           return;
6637         }
6638 
6639         _this2.changing_ = true;
6640         disableOthers(_this2, track);
6641         _this2.changing_ = false;
6642 
6643         _this2.trigger('change');
6644       };
6645       /**
6646        * @listens AudioTrack#enabledchange
6647        * @fires TrackList#change
6648        */
6649 
6650 
6651       track.addEventListener('enabledchange', track.enabledChange_);
6652     };
6653 
6654     _proto.removeTrack = function removeTrack(rtrack) {
6655       _TrackList.prototype.removeTrack.call(this, rtrack);
6656 
6657       if (rtrack.removeEventListener && rtrack.enabledChange_) {
6658         rtrack.removeEventListener('enabledchange', rtrack.enabledChange_);
6659         rtrack.enabledChange_ = null;
6660       }
6661     };
6662 
6663     return AudioTrackList;
6664   }(TrackList);
6665 
6666   /**
6667    * Un-select all other {@link VideoTrack}s that are selected.
6668    *
6669    * @param {VideoTrackList} list
6670    *        list to work on
6671    *
6672    * @param {VideoTrack} track
6673    *        The track to skip
6674    *
6675    * @private
6676    */
6677 
6678   var disableOthers$1 = function disableOthers(list, track) {
6679     for (var i = 0; i < list.length; i++) {
6680       if (!Object.keys(list[i]).length || track.id === list[i].id) {
6681         continue;
6682       } // another video track is enabled, disable it
6683 
6684 
6685       list[i].selected = false;
6686     }
6687   };
6688   /**
6689    * The current list of {@link VideoTrack} for a video.
6690    *
6691    * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#videotracklist}
6692    * @extends TrackList
6693    */
6694 
6695 
6696   var VideoTrackList =
6697   /*#__PURE__*/
6698   function (_TrackList) {
6699     inheritsLoose(VideoTrackList, _TrackList);
6700 
6701     /**
6702      * Create an instance of this class.
6703      *
6704      * @param {VideoTrack[]} [tracks=[]]
6705      *        A list of `VideoTrack` to instantiate the list with.
6706      */
6707     function VideoTrackList(tracks) {
6708       var _this;
6709 
6710       if (tracks === void 0) {
6711         tracks = [];
6712       }
6713 
6714       // make sure only 1 track is enabled
6715       // sorted from last index to first index
6716       for (var i = tracks.length - 1; i >= 0; i--) {
6717         if (tracks[i].selected) {
6718           disableOthers$1(tracks, tracks[i]);
6719           break;
6720         }
6721       }
6722 
6723       _this = _TrackList.call(this, tracks) || this;
6724       _this.changing_ = false;
6725       /**
6726        * @member {number} VideoTrackList#selectedIndex
6727        *         The current index of the selected {@link VideoTrack`}.
6728        */
6729 
6730       Object.defineProperty(assertThisInitialized(_this), 'selectedIndex', {
6731         get: function get() {
6732           for (var _i = 0; _i < this.length; _i++) {
6733             if (this[_i].selected) {
6734               return _i;
6735             }
6736           }
6737 
6738           return -1;
6739         },
6740         set: function set() {}
6741       });
6742       return _this;
6743     }
6744     /**
6745      * Add a {@link VideoTrack} to the `VideoTrackList`.
6746      *
6747      * @param {VideoTrack} track
6748      *        The VideoTrack to add to the list
6749      *
6750      * @fires TrackList#addtrack
6751      */
6752 
6753 
6754     var _proto = VideoTrackList.prototype;
6755 
6756     _proto.addTrack = function addTrack(track) {
6757       var _this2 = this;
6758 
6759       if (track.selected) {
6760         disableOthers$1(this, track);
6761       }
6762 
6763       _TrackList.prototype.addTrack.call(this, track); // native tracks don't have this
6764 
6765 
6766       if (!track.addEventListener) {
6767         return;
6768       }
6769 
6770       track.selectedChange_ = function () {
6771         if (_this2.changing_) {
6772           return;
6773         }
6774 
6775         _this2.changing_ = true;
6776         disableOthers$1(_this2, track);
6777         _this2.changing_ = false;
6778 
6779         _this2.trigger('change');
6780       };
6781       /**
6782        * @listens VideoTrack#selectedchange
6783        * @fires TrackList#change
6784        */
6785 
6786 
6787       track.addEventListener('selectedchange', track.selectedChange_);
6788     };
6789 
6790     _proto.removeTrack = function removeTrack(rtrack) {
6791       _TrackList.prototype.removeTrack.call(this, rtrack);
6792 
6793       if (rtrack.removeEventListener && rtrack.selectedChange_) {
6794         rtrack.removeEventListener('selectedchange', rtrack.selectedChange_);
6795         rtrack.selectedChange_ = null;
6796       }
6797     };
6798 
6799     return VideoTrackList;
6800   }(TrackList);
6801 
6802   /**
6803    * The current list of {@link TextTrack} for a media file.
6804    *
6805    * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttracklist}
6806    * @extends TrackList
6807    */
6808 
6809   var TextTrackList =
6810   /*#__PURE__*/
6811   function (_TrackList) {
6812     inheritsLoose(TextTrackList, _TrackList);
6813 
6814     function TextTrackList() {
6815       return _TrackList.apply(this, arguments) || this;
6816     }
6817 
6818     var _proto = TextTrackList.prototype;
6819 
6820     /**
6821      * Add a {@link TextTrack} to the `TextTrackList`
6822      *
6823      * @param {TextTrack} track
6824      *        The text track to add to the list.
6825      *
6826      * @fires TrackList#addtrack
6827      */
6828     _proto.addTrack = function addTrack(track) {
6829       var _this = this;
6830 
6831       _TrackList.prototype.addTrack.call(this, track);
6832 
6833       if (!this.queueChange_) {
6834         this.queueChange_ = function () {
6835           return _this.queueTrigger('change');
6836         };
6837       }
6838 
6839       if (!this.triggerSelectedlanguagechange) {
6840         this.triggerSelectedlanguagechange_ = function () {
6841           return _this.trigger('selectedlanguagechange');
6842         };
6843       }
6844       /**
6845        * @listens TextTrack#modechange
6846        * @fires TrackList#change
6847        */
6848 
6849 
6850       track.addEventListener('modechange', this.queueChange_);
6851       var nonLanguageTextTrackKind = ['metadata', 'chapters'];
6852 
6853       if (nonLanguageTextTrackKind.indexOf(track.kind) === -1) {
6854         track.addEventListener('modechange', this.triggerSelectedlanguagechange_);
6855       }
6856     };
6857 
6858     _proto.removeTrack = function removeTrack(rtrack) {
6859       _TrackList.prototype.removeTrack.call(this, rtrack); // manually remove the event handlers we added
6860 
6861 
6862       if (rtrack.removeEventListener) {
6863         if (this.queueChange_) {
6864           rtrack.removeEventListener('modechange', this.queueChange_);
6865         }
6866 
6867         if (this.selectedlanguagechange_) {
6868           rtrack.removeEventListener('modechange', this.triggerSelectedlanguagechange_);
6869         }
6870       }
6871     };
6872 
6873     return TextTrackList;
6874   }(TrackList);
6875 
6876   /**
6877    * @file html-track-element-list.js
6878    */
6879 
6880   /**
6881    * The current list of {@link HtmlTrackElement}s.
6882    */
6883   var HtmlTrackElementList =
6884   /*#__PURE__*/
6885   function () {
6886     /**
6887      * Create an instance of this class.
6888      *
6889      * @param {HtmlTrackElement[]} [tracks=[]]
6890      *        A list of `HtmlTrackElement` to instantiate the list with.
6891      */
6892     function HtmlTrackElementList(trackElements) {
6893       if (trackElements === void 0) {
6894         trackElements = [];
6895       }
6896 
6897       this.trackElements_ = [];
6898       /**
6899        * @memberof HtmlTrackElementList
6900        * @member {number} length
6901        *         The current number of `Track`s in the this Trackist.
6902        * @instance
6903        */
6904 
6905       Object.defineProperty(this, 'length', {
6906         get: function get() {
6907           return this.trackElements_.length;
6908         }
6909       });
6910 
6911       for (var i = 0, length = trackElements.length; i < length; i++) {
6912         this.addTrackElement_(trackElements[i]);
6913       }
6914     }
6915     /**
6916      * Add an {@link HtmlTrackElement} to the `HtmlTrackElementList`
6917      *
6918      * @param {HtmlTrackElement} trackElement
6919      *        The track element to add to the list.
6920      *
6921      * @private
6922      */
6923 
6924 
6925     var _proto = HtmlTrackElementList.prototype;
6926 
6927     _proto.addTrackElement_ = function addTrackElement_(trackElement) {
6928       var index = this.trackElements_.length;
6929 
6930       if (!('' + index in this)) {
6931         Object.defineProperty(this, index, {
6932           get: function get() {
6933             return this.trackElements_[index];
6934           }
6935         });
6936       } // Do not add duplicate elements
6937 
6938 
6939       if (this.trackElements_.indexOf(trackElement) === -1) {
6940         this.trackElements_.push(trackElement);
6941       }
6942     }
6943     /**
6944      * Get an {@link HtmlTrackElement} from the `HtmlTrackElementList` given an
6945      * {@link TextTrack}.
6946      *
6947      * @param {TextTrack} track
6948      *        The track associated with a track element.
6949      *
6950      * @return {HtmlTrackElement|undefined}
6951      *         The track element that was found or undefined.
6952      *
6953      * @private
6954      */
6955     ;
6956 
6957     _proto.getTrackElementByTrack_ = function getTrackElementByTrack_(track) {
6958       var trackElement_;
6959 
6960       for (var i = 0, length = this.trackElements_.length; i < length; i++) {
6961         if (track === this.trackElements_[i].track) {
6962           trackElement_ = this.trackElements_[i];
6963           break;
6964         }
6965       }
6966 
6967       return trackElement_;
6968     }
6969     /**
6970      * Remove a {@link HtmlTrackElement} from the `HtmlTrackElementList`
6971      *
6972      * @param {HtmlTrackElement} trackElement
6973      *        The track element to remove from the list.
6974      *
6975      * @private
6976      */
6977     ;
6978 
6979     _proto.removeTrackElement_ = function removeTrackElement_(trackElement) {
6980       for (var i = 0, length = this.trackElements_.length; i < length; i++) {
6981         if (trackElement === this.trackElements_[i]) {
6982           if (this.trackElements_[i].track && typeof this.trackElements_[i].track.off === 'function') {
6983             this.trackElements_[i].track.off();
6984           }
6985 
6986           if (typeof this.trackElements_[i].off === 'function') {
6987             this.trackElements_[i].off();
6988           }
6989 
6990           this.trackElements_.splice(i, 1);
6991           break;
6992         }
6993       }
6994     };
6995 
6996     return HtmlTrackElementList;
6997   }();
6998 
6999   /**
7000    * @file text-track-cue-list.js
7001    */
7002 
7003   /**
7004    * @typedef {Object} TextTrackCueList~TextTrackCue
7005    *
7006    * @property {string} id
7007    *           The unique id for this text track cue
7008    *
7009    * @property {number} startTime
7010    *           The start time for this text track cue
7011    *
7012    * @property {number} endTime
7013    *           The end time for this text track cue
7014    *
7015    * @property {boolean} pauseOnExit
7016    *           Pause when the end time is reached if true.
7017    *
7018    * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackcue}
7019    */
7020 
7021   /**
7022    * A List of TextTrackCues.
7023    *
7024    * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackcuelist}
7025    */
7026   var TextTrackCueList =
7027   /*#__PURE__*/
7028   function () {
7029     /**
7030      * Create an instance of this class..
7031      *
7032      * @param {Array} cues
7033      *        A list of cues to be initialized with
7034      */
7035     function TextTrackCueList(cues) {
7036       TextTrackCueList.prototype.setCues_.call(this, cues);
7037       /**
7038        * @memberof TextTrackCueList
7039        * @member {number} length
7040        *         The current number of `TextTrackCue`s in the TextTrackCueList.
7041        * @instance
7042        */
7043 
7044       Object.defineProperty(this, 'length', {
7045         get: function get() {
7046           return this.length_;
7047         }
7048       });
7049     }
7050     /**
7051      * A setter for cues in this list. Creates getters
7052      * an an index for the cues.
7053      *
7054      * @param {Array} cues
7055      *        An array of cues to set
7056      *
7057      * @private
7058      */
7059 
7060 
7061     var _proto = TextTrackCueList.prototype;
7062 
7063     _proto.setCues_ = function setCues_(cues) {
7064       var oldLength = this.length || 0;
7065       var i = 0;
7066       var l = cues.length;
7067       this.cues_ = cues;
7068       this.length_ = cues.length;
7069 
7070       var defineProp = function defineProp(index) {
7071         if (!('' + index in this)) {
7072           Object.defineProperty(this, '' + index, {
7073             get: function get() {
7074               return this.cues_[index];
7075             }
7076           });
7077         }
7078       };
7079 
7080       if (oldLength < l) {
7081         i = oldLength;
7082 
7083         for (; i < l; i++) {
7084           defineProp.call(this, i);
7085         }
7086       }
7087     }
7088     /**
7089      * Get a `TextTrackCue` that is currently in the `TextTrackCueList` by id.
7090      *
7091      * @param {string} id
7092      *        The id of the cue that should be searched for.
7093      *
7094      * @return {TextTrackCueList~TextTrackCue|null}
7095      *         A single cue or null if none was found.
7096      */
7097     ;
7098 
7099     _proto.getCueById = function getCueById(id) {
7100       var result = null;
7101 
7102       for (var i = 0, l = this.length; i < l; i++) {
7103         var cue = this[i];
7104 
7105         if (cue.id === id) {
7106           result = cue;
7107           break;
7108         }
7109       }
7110 
7111       return result;
7112     };
7113 
7114     return TextTrackCueList;
7115   }();
7116 
7117   /**
7118    * @file track-kinds.js
7119    */
7120 
7121   /**
7122    * All possible `VideoTrackKind`s
7123    *
7124    * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-videotrack-kind
7125    * @typedef VideoTrack~Kind
7126    * @enum
7127    */
7128   var VideoTrackKind = {
7129     alternative: 'alternative',
7130     captions: 'captions',
7131     main: 'main',
7132     sign: 'sign',
7133     subtitles: 'subtitles',
7134     commentary: 'commentary'
7135   };
7136   /**
7137    * All possible `AudioTrackKind`s
7138    *
7139    * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-audiotrack-kind
7140    * @typedef AudioTrack~Kind
7141    * @enum
7142    */
7143 
7144   var AudioTrackKind = {
7145     'alternative': 'alternative',
7146     'descriptions': 'descriptions',
7147     'main': 'main',
7148     'main-desc': 'main-desc',
7149     'translation': 'translation',
7150     'commentary': 'commentary'
7151   };
7152   /**
7153    * All possible `TextTrackKind`s
7154    *
7155    * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-texttrack-kind
7156    * @typedef TextTrack~Kind
7157    * @enum
7158    */
7159 
7160   var TextTrackKind = {
7161     subtitles: 'subtitles',
7162     captions: 'captions',
7163     descriptions: 'descriptions',
7164     chapters: 'chapters',
7165     metadata: 'metadata'
7166   };
7167   /**
7168    * All possible `TextTrackMode`s
7169    *
7170    * @see https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackmode
7171    * @typedef TextTrack~Mode
7172    * @enum
7173    */
7174 
7175   var TextTrackMode = {
7176     disabled: 'disabled',
7177     hidden: 'hidden',
7178     showing: 'showing'
7179   };
7180 
7181   /**
7182    * A Track class that contains all of the common functionality for {@link AudioTrack},
7183    * {@link VideoTrack}, and {@link TextTrack}.
7184    *
7185    * > Note: This class should not be used directly
7186    *
7187    * @see {@link https://html.spec.whatwg.org/multipage/embedded-content.html}
7188    * @extends EventTarget
7189    * @abstract
7190    */
7191 
7192   var Track =
7193   /*#__PURE__*/
7194   function (_EventTarget) {
7195     inheritsLoose(Track, _EventTarget);
7196 
7197     /**
7198      * Create an instance of this class.
7199      *
7200      * @param {Object} [options={}]
7201      *        Object of option names and values
7202      *
7203      * @param {string} [options.kind='']
7204      *        A valid kind for the track type you are creating.
7205      *
7206      * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
7207      *        A unique id for this AudioTrack.
7208      *
7209      * @param {string} [options.label='']
7210      *        The menu label for this track.
7211      *
7212      * @param {string} [options.language='']
7213      *        A valid two character language code.
7214      *
7215      * @abstract
7216      */
7217     function Track(options) {
7218       var _this;
7219 
7220       if (options === void 0) {
7221         options = {};
7222       }
7223 
7224       _this = _EventTarget.call(this) || this;
7225       var trackProps = {
7226         id: options.id || 'vjs_track_' + newGUID(),
7227         kind: options.kind || '',
7228         label: options.label || '',
7229         language: options.language || ''
7230       };
7231       /**
7232        * @memberof Track
7233        * @member {string} id
7234        *         The id of this track. Cannot be changed after creation.
7235        * @instance
7236        *
7237        * @readonly
7238        */
7239 
7240       /**
7241        * @memberof Track
7242        * @member {string} kind
7243        *         The kind of track that this is. Cannot be changed after creation.
7244        * @instance
7245        *
7246        * @readonly
7247        */
7248 
7249       /**
7250        * @memberof Track
7251        * @member {string} label
7252        *         The label of this track. Cannot be changed after creation.
7253        * @instance
7254        *
7255        * @readonly
7256        */
7257 
7258       /**
7259        * @memberof Track
7260        * @member {string} language
7261        *         The two letter language code for this track. Cannot be changed after
7262        *         creation.
7263        * @instance
7264        *
7265        * @readonly
7266        */
7267 
7268       var _loop = function _loop(key) {
7269         Object.defineProperty(assertThisInitialized(_this), key, {
7270           get: function get() {
7271             return trackProps[key];
7272           },
7273           set: function set() {}
7274         });
7275       };
7276 
7277       for (var key in trackProps) {
7278         _loop(key);
7279       }
7280 
7281       return _this;
7282     }
7283 
7284     return Track;
7285   }(EventTarget);
7286 
7287   /**
7288    * @file url.js
7289    * @module url
7290    */
7291   /**
7292    * @typedef {Object} url:URLObject
7293    *
7294    * @property {string} protocol
7295    *           The protocol of the url that was parsed.
7296    *
7297    * @property {string} hostname
7298    *           The hostname of the url that was parsed.
7299    *
7300    * @property {string} port
7301    *           The port of the url that was parsed.
7302    *
7303    * @property {string} pathname
7304    *           The pathname of the url that was parsed.
7305    *
7306    * @property {string} search
7307    *           The search query of the url that was parsed.
7308    *
7309    * @property {string} hash
7310    *           The hash of the url that was parsed.
7311    *
7312    * @property {string} host
7313    *           The host of the url that was parsed.
7314    */
7315 
7316   /**
7317    * Resolve and parse the elements of a URL.
7318    *
7319    * @function
7320    * @param    {String} url
7321    *           The url to parse
7322    *
7323    * @return   {url:URLObject}
7324    *           An object of url details
7325    */
7326 
7327   var parseUrl = function parseUrl(url) {
7328     var props = ['protocol', 'hostname', 'port', 'pathname', 'search', 'hash', 'host']; // add the url to an anchor and let the browser parse the URL
7329 
7330     var a = document.createElement('a');
7331     a.href = url; // IE8 (and 9?) Fix
7332     // ie8 doesn't parse the URL correctly until the anchor is actually
7333     // added to the body, and an innerHTML is needed to trigger the parsing
7334 
7335     var addToBody = a.host === '' && a.protocol !== 'file:';
7336     var div;
7337 
7338     if (addToBody) {
7339       div = document.createElement('div');
7340       div.innerHTML = "<a href=\"" + url + "\"></a>";
7341       a = div.firstChild; // prevent the div from affecting layout
7342 
7343       div.setAttribute('style', 'display:none; position:absolute;');
7344       document.body.appendChild(div);
7345     } // Copy the specific URL properties to a new object
7346     // This is also needed for IE8 because the anchor loses its
7347     // properties when it's removed from the dom
7348 
7349 
7350     var details = {};
7351 
7352     for (var i = 0; i < props.length; i++) {
7353       details[props[i]] = a[props[i]];
7354     } // IE9 adds the port to the host property unlike everyone else. If
7355     // a port identifier is added for standard ports, strip it.
7356 
7357 
7358     if (details.protocol === 'http:') {
7359       details.host = details.host.replace(/:80$/, '');
7360     }
7361 
7362     if (details.protocol === 'https:') {
7363       details.host = details.host.replace(/:443$/, '');
7364     }
7365 
7366     if (!details.protocol) {
7367       details.protocol = window$1.location.protocol;
7368     }
7369 
7370     if (addToBody) {
7371       document.body.removeChild(div);
7372     }
7373 
7374     return details;
7375   };
7376   /**
7377    * Get absolute version of relative URL. Used to tell Flash the correct URL.
7378    *
7379    * @function
7380    * @param    {string} url
7381    *           URL to make absolute
7382    *
7383    * @return   {string}
7384    *           Absolute URL
7385    *
7386    * @see      http://stackoverflow.com/questions/470832/getting-an-absolute-url-from-a-relative-one-ie6-issue
7387    */
7388 
7389   var getAbsoluteURL = function getAbsoluteURL(url) {
7390     // Check if absolute URL
7391     if (!url.match(/^https?:\/\//)) {
7392       // Convert to absolute URL. Flash hosted off-site needs an absolute URL.
7393       var div = document.createElement('div');
7394       div.innerHTML = "<a href=\"" + url + "\">x</a>";
7395       url = div.firstChild.href;
7396     }
7397 
7398     return url;
7399   };
7400   /**
7401    * Returns the extension of the passed file name. It will return an empty string
7402    * if passed an invalid path.
7403    *
7404    * @function
7405    * @param    {string} path
7406    *           The fileName path like '/path/to/file.mp4'
7407    *
7408    * @return  {string}
7409    *           The extension in lower case or an empty string if no
7410    *           extension could be found.
7411    */
7412 
7413   var getFileExtension = function getFileExtension(path) {
7414     if (typeof path === 'string') {
7415       var splitPathRe = /^(\/?)([\s\S]*?)((?:\.{1,2}|[^\/]+?)(\.([^\.\/\?]+)))(?:[\/]*|[\?].*)$/;
7416       var pathParts = splitPathRe.exec(path);
7417 
7418       if (pathParts) {
7419         return pathParts.pop().toLowerCase();
7420       }
7421     }
7422 
7423     return '';
7424   };
7425   /**
7426    * Returns whether the url passed is a cross domain request or not.
7427    *
7428    * @function
7429    * @param    {string} url
7430    *           The url to check.
7431    *
7432    * @param    {Object} [winLoc]
7433    *           the domain to check the url against, defaults to window.location
7434    *
7435    * @param    {string} [winLoc.protocol]
7436    *           The window location protocol defaults to window.location.protocol
7437    *
7438    * @param    {string} [winLoc.host]
7439    *           The window location host defaults to window.location.host
7440    *
7441    * @return   {boolean}
7442    *           Whether it is a cross domain request or not.
7443    */
7444 
7445   var isCrossOrigin = function isCrossOrigin(url, winLoc) {
7446     if (winLoc === void 0) {
7447       winLoc = window$1.location;
7448     }
7449 
7450     var urlInfo = parseUrl(url); // IE8 protocol relative urls will return ':' for protocol
7451 
7452     var srcProtocol = urlInfo.protocol === ':' ? winLoc.protocol : urlInfo.protocol; // Check if url is for another domain/origin
7453     // IE8 doesn't know location.origin, so we won't rely on it here
7454 
7455     var crossOrigin = srcProtocol + urlInfo.host !== winLoc.protocol + winLoc.host;
7456     return crossOrigin;
7457   };
7458 
7459   var Url = /*#__PURE__*/Object.freeze({
7460     parseUrl: parseUrl,
7461     getAbsoluteURL: getAbsoluteURL,
7462     getFileExtension: getFileExtension,
7463     isCrossOrigin: isCrossOrigin
7464   });
7465 
7466   var isFunction_1 = isFunction;
7467   var toString$1 = Object.prototype.toString;
7468 
7469   function isFunction(fn) {
7470     var string = toString$1.call(fn);
7471     return string === '[object Function]' || typeof fn === 'function' && string !== '[object RegExp]' || typeof window !== 'undefined' && ( // IE8 and below
7472     fn === window.setTimeout || fn === window.alert || fn === window.confirm || fn === window.prompt);
7473   }
7474 
7475   /**
7476    * @license
7477    * slighly modified parse-headers 2.0.2 <https://github.com/kesla/parse-headers/>
7478    * Copyright (c) 2014 David Björklund
7479    * Available under the MIT license
7480    * <https://github.com/kesla/parse-headers/blob/master/LICENCE>
7481    */
7482 
7483 
7484   var parseHeaders = function parseHeaders(headers) {
7485     var result = {};
7486 
7487     if (!headers) {
7488       return result;
7489     }
7490 
7491     headers.trim().split('\n').forEach(function (row) {
7492       var index = row.indexOf(':');
7493       var key = row.slice(0, index).trim().toLowerCase();
7494       var value = row.slice(index + 1).trim();
7495 
7496       if (typeof result[key] === 'undefined') {
7497         result[key] = value;
7498       } else if (Array.isArray(result[key])) {
7499         result[key].push(value);
7500       } else {
7501         result[key] = [result[key], value];
7502       }
7503     });
7504     return result;
7505   };
7506 
7507   var xhr = createXHR; // Allow use of default import syntax in TypeScript
7508 
7509   var default_1 = createXHR;
7510   createXHR.XMLHttpRequest = window$1.XMLHttpRequest || noop;
7511   createXHR.XDomainRequest = "withCredentials" in new createXHR.XMLHttpRequest() ? createXHR.XMLHttpRequest : window$1.XDomainRequest;
vendor: 29,830 bytes, lines 7512-8548
7512   forEachArray(["get", "put", "post", "patch", "head", "delete"], function (method) {
7513     createXHR[method === "delete" ? "del" : method] = function (uri, options, callback) {
7514       options = initParams(uri, options, callback);
7515       options.method = method.toUpperCase();
7516       return _createXHR(options);
7517     };
7518   });
7519 
7520   function forEachArray(array, iterator) {
7521     for (var i = 0; i < array.length; i++) {
7522       iterator(array[i]);
7523     }
7524   }
7525 
7526   function isEmpty(obj) {
7527     for (var i in obj) {
7528       if (obj.hasOwnProperty(i)) return false;
7529     }
7530 
7531     return true;
7532   }
7533 
7534   function initParams(uri, options, callback) {
7535     var params = uri;
7536 
7537     if (isFunction_1(options)) {
7538       callback = options;
7539 
7540       if (typeof uri === "string") {
7541         params = {
7542           uri: uri
7543         };
7544       }
7545     } else {
7546       params = _extends_1({}, options, {
7547         uri: uri
7548       });
7549     }
7550 
7551     params.callback = callback;
7552     return params;
7553   }
7554 
7555   function createXHR(uri, options, callback) {
7556     options = initParams(uri, options, callback);
7557     return _createXHR(options);
7558   }
7559 
7560   function _createXHR(options) {
7561     if (typeof options.callback === "undefined") {
7562       throw new Error("callback argument missing");
7563     }
7564 
7565     var called = false;
7566 
7567     var callback = function cbOnce(err, response, body) {
7568       if (!called) {
7569         called = true;
7570         options.callback(err, response, body);
7571       }
7572     };
7573 
7574     function readystatechange() {
7575       if (xhr.readyState === 4) {
7576         setTimeout(loadFunc, 0);
7577       }
7578     }
7579 
7580     function getBody() {
7581       // Chrome with requestType=blob throws errors arround when even testing access to responseText
7582       var body = undefined;
7583 
7584       if (xhr.response) {
7585         body = xhr.response;
7586       } else {
7587         body = xhr.responseText || getXml(xhr);
7588       }
7589 
7590       if (isJson) {
7591         try {
7592           body = JSON.parse(body);
7593         } catch (e) {}
7594       }
7595 
7596       return body;
7597     }
7598 
7599     function errorFunc(evt) {
7600       clearTimeout(timeoutTimer);
7601 
7602       if (!(evt instanceof Error)) {
7603         evt = new Error("" + (evt || "Unknown XMLHttpRequest Error"));
7604       }
7605 
7606       evt.statusCode = 0;
7607       return callback(evt, failureResponse);
7608     } // will load the data & process the response in a special response object
7609 
7610 
7611     function loadFunc() {
7612       if (aborted) return;
7613       var status;
7614       clearTimeout(timeoutTimer);
7615 
7616       if (options.useXDR && xhr.status === undefined) {
7617         //IE8 CORS GET successful response doesn't have a status field, but body is fine
7618         status = 200;
7619       } else {
7620         status = xhr.status === 1223 ? 204 : xhr.status;
7621       }
7622 
7623       var response = failureResponse;
7624       var err = null;
7625 
7626       if (status !== 0) {
7627         response = {
7628           body: getBody(),
7629           statusCode: status,
7630           method: method,
7631           headers: {},
7632           url: uri,
7633           rawRequest: xhr
7634         };
7635 
7636         if (xhr.getAllResponseHeaders) {
7637           //remember xhr can in fact be XDR for CORS in IE
7638           response.headers = parseHeaders(xhr.getAllResponseHeaders());
7639         }
7640       } else {
7641         err = new Error("Internal XMLHttpRequest Error");
7642       }
7643 
7644       return callback(err, response, response.body);
7645     }
7646 
7647     var xhr = options.xhr || null;
7648 
7649     if (!xhr) {
7650       if (options.cors || options.useXDR) {
7651         xhr = new createXHR.XDomainRequest();
7652       } else {
7653         xhr = new createXHR.XMLHttpRequest();
7654       }
7655     }
7656 
7657     var key;
7658     var aborted;
7659     var uri = xhr.url = options.uri || options.url;
7660     var method = xhr.method = options.method || "GET";
7661     var body = options.body || options.data;
7662     var headers = xhr.headers = options.headers || {};
7663     var sync = !!options.sync;
7664     var isJson = false;
7665     var timeoutTimer;
7666     var failureResponse = {
7667       body: undefined,
7668       headers: {},
7669       statusCode: 0,
7670       method: method,
7671       url: uri,
7672       rawRequest: xhr
7673     };
7674 
7675     if ("json" in options && options.json !== false) {
7676       isJson = true;
7677       headers["accept"] || headers["Accept"] || (headers["Accept"] = "application/json"); //Don't override existing accept header declared by user
7678 
7679       if (method !== "GET" && method !== "HEAD") {
7680         headers["content-type"] || headers["Content-Type"] || (headers["Content-Type"] = "application/json"); //Don't override existing accept header declared by user
7681 
7682         body = JSON.stringify(options.json === true ? body : options.json);
7683       }
7684     }
7685 
7686     xhr.onreadystatechange = readystatechange;
7687     xhr.onload = loadFunc;
7688     xhr.onerror = errorFunc; // IE9 must have onprogress be set to a unique function.
7689 
7690     xhr.onprogress = function () {// IE must die
7691     };
7692 
7693     xhr.onabort = function () {
7694       aborted = true;
7695     };
7696 
7697     xhr.ontimeout = errorFunc;
7698     xhr.open(method, uri, !sync, options.username, options.password); //has to be after open
7699 
7700     if (!sync) {
7701       xhr.withCredentials = !!options.withCredentials;
7702     } // Cannot set timeout with sync request
7703     // not setting timeout on the xhr object, because of old webkits etc. not handling that correctly
7704     // both npm's request and jquery 1.x use this kind of timeout, so this is being consistent
7705 
7706 
7707     if (!sync && options.timeout > 0) {
7708       timeoutTimer = setTimeout(function () {
7709         if (aborted) return;
7710         aborted = true; //IE9 may still call readystatechange
7711 
7712         xhr.abort("timeout");
7713         var e = new Error("XMLHttpRequest timeout");
7714         e.code = "ETIMEDOUT";
7715         errorFunc(e);
7716       }, options.timeout);
7717     }
7718 
7719     if (xhr.setRequestHeader) {
7720       for (key in headers) {
7721         if (headers.hasOwnProperty(key)) {
7722           xhr.setRequestHeader(key, headers[key]);
7723         }
7724       }
7725     } else if (options.headers && !isEmpty(options.headers)) {
7726       throw new Error("Headers cannot be set on an XDomainRequest object");
7727     }
7728 
7729     if ("responseType" in options) {
7730       xhr.responseType = options.responseType;
7731     }
7732 
7733     if ("beforeSend" in options && typeof options.beforeSend === "function") {
7734       options.beforeSend(xhr);
7735     } // Microsoft Edge browser sends "undefined" when send is called with undefined value.
7736     // XMLHttpRequest spec says to pass null as body to indicate no body
7737     // See https://github.com/naugtur/xhr/issues/100.
7738 
7739 
7740     xhr.send(body || null);
7741     return xhr;
7742   }
7743 
7744   function getXml(xhr) {
7745     // xhr.responseXML will throw Exception "InvalidStateError" or "DOMException"
7746     // See https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseXML.
7747     try {
7748       if (xhr.responseType === "document") {
7749         return xhr.responseXML;
7750       }
7751 
7752       var firefoxBugTakenEffect = xhr.responseXML && xhr.responseXML.documentElement.nodeName === "parsererror";
7753 
7754       if (xhr.responseType === "" && !firefoxBugTakenEffect) {
7755         return xhr.responseXML;
7756       }
7757     } catch (e) {}
7758 
7759     return null;
7760   }
7761 
7762   function noop() {}
7763   xhr["default"] = default_1;
7764 
7765   /**
7766    * Takes a webvtt file contents and parses it into cues
7767    *
7768    * @param {string} srcContent
7769    *        webVTT file contents
7770    *
7771    * @param {TextTrack} track
7772    *        TextTrack to add cues to. Cues come from the srcContent.
7773    *
7774    * @private
7775    */
7776 
7777   var parseCues = function parseCues(srcContent, track) {
7778     var parser = new window$1.WebVTT.Parser(window$1, window$1.vttjs, window$1.WebVTT.StringDecoder());
7779     var errors = [];
7780 
7781     parser.oncue = function (cue) {
7782       track.addCue(cue);
7783     };
7784 
7785     parser.onparsingerror = function (error) {
7786       errors.push(error);
7787     };
7788 
7789     parser.onflush = function () {
7790       track.trigger({
7791         type: 'loadeddata',
7792         target: track
7793       });
7794     };
7795 
7796     parser.parse(srcContent);
7797 
7798     if (errors.length > 0) {
7799       if (window$1.console && window$1.console.groupCollapsed) {
7800         window$1.console.groupCollapsed("Text Track parsing errors for " + track.src);
7801       }
7802 
7803       errors.forEach(function (error) {
7804         return log.error(error);
7805       });
7806 
7807       if (window$1.console && window$1.console.groupEnd) {
7808         window$1.console.groupEnd();
7809       }
7810     }
7811 
7812     parser.flush();
7813   };
7814   /**
7815    * Load a `TextTrack` from a specified url.
7816    *
7817    * @param {string} src
7818    *        Url to load track from.
7819    *
7820    * @param {TextTrack} track
7821    *        Track to add cues to. Comes from the content at the end of `url`.
7822    *
7823    * @private
7824    */
7825 
7826 
7827   var loadTrack = function loadTrack(src, track) {
7828     var opts = {
7829       uri: src
7830     };
7831     var crossOrigin = isCrossOrigin(src);
7832 
7833     if (crossOrigin) {
7834       opts.cors = crossOrigin;
7835     }
7836 
7837     xhr(opts, bind(this, function (err, response, responseBody) {
7838       if (err) {
7839         return log.error(err, response);
7840       }
7841 
7842       track.loaded_ = true; // Make sure that vttjs has loaded, otherwise, wait till it finished loading
7843       // NOTE: this is only used for the alt/video.novtt.js build
7844 
7845       if (typeof window$1.WebVTT !== 'function') {
7846         if (track.tech_) {
7847           // to prevent use before define eslint error, we define loadHandler
7848           // as a let here
7849           track.tech_.any(['vttjsloaded', 'vttjserror'], function (event) {
7850             if (event.type === 'vttjserror') {
7851               log.error("vttjs failed to load, stopping trying to process " + track.src);
7852               return;
7853             }
7854 
7855             return parseCues(responseBody, track);
7856           });
7857         }
7858       } else {
7859         parseCues(responseBody, track);
7860       }
7861     }));
7862   };
7863   /**
7864    * A representation of a single `TextTrack`.
7865    *
7866    * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrack}
7867    * @extends Track
7868    */
7869 
7870 
7871   var TextTrack =
7872   /*#__PURE__*/
7873   function (_Track) {
7874     inheritsLoose(TextTrack, _Track);
7875 
7876     /**
7877      * Create an instance of this class.
7878      *
7879      * @param {Object} options={}
7880      *        Object of option names and values
7881      *
7882      * @param {Tech} options.tech
7883      *        A reference to the tech that owns this TextTrack.
7884      *
7885      * @param {TextTrack~Kind} [options.kind='subtitles']
7886      *        A valid text track kind.
7887      *
7888      * @param {TextTrack~Mode} [options.mode='disabled']
7889      *        A valid text track mode.
7890      *
7891      * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
7892      *        A unique id for this TextTrack.
7893      *
7894      * @param {string} [options.label='']
7895      *        The menu label for this track.
7896      *
7897      * @param {string} [options.language='']
7898      *        A valid two character language code.
7899      *
7900      * @param {string} [options.srclang='']
7901      *        A valid two character language code. An alternative, but deprioritized
7902      *        version of `options.language`
7903      *
7904      * @param {string} [options.src]
7905      *        A url to TextTrack cues.
7906      *
7907      * @param {boolean} [options.default]
7908      *        If this track should default to on or off.
7909      */
7910     function TextTrack(options) {
7911       var _this;
7912 
7913       if (options === void 0) {
7914         options = {};
7915       }
7916 
7917       if (!options.tech) {
7918         throw new Error('A tech was not provided.');
7919       }
7920 
7921       var settings = mergeOptions(options, {
7922         kind: TextTrackKind[options.kind] || 'subtitles',
7923         language: options.language || options.srclang || ''
7924       });
7925       var mode = TextTrackMode[settings.mode] || 'disabled';
7926       var default_ = settings["default"];
7927 
7928       if (settings.kind === 'metadata' || settings.kind === 'chapters') {
7929         mode = 'hidden';
7930       }
7931 
7932       _this = _Track.call(this, settings) || this;
7933       _this.tech_ = settings.tech;
7934       _this.cues_ = [];
7935       _this.activeCues_ = [];
7936       _this.preload_ = _this.tech_.preloadTextTracks !== false;
7937       var cues = new TextTrackCueList(_this.cues_);
7938       var activeCues = new TextTrackCueList(_this.activeCues_);
7939       var changed = false;
7940       var timeupdateHandler = bind(assertThisInitialized(_this), function () {
7941         // Accessing this.activeCues for the side-effects of updating itself
7942         // due to its nature as a getter function. Do not remove or cues will
7943         // stop updating!
7944         // Use the setter to prevent deletion from uglify (pure_getters rule)
7945         this.activeCues = this.activeCues;
7946 
7947         if (changed) {
7948           this.trigger('cuechange');
7949           changed = false;
7950         }
7951       });
7952 
7953       if (mode !== 'disabled') {
7954         _this.tech_.ready(function () {
7955           _this.tech_.on('timeupdate', timeupdateHandler);
7956         }, true);
7957       }
7958 
7959       Object.defineProperties(assertThisInitialized(_this), {
7960         /**
7961          * @memberof TextTrack
7962          * @member {boolean} default
7963          *         If this track was set to be on or off by default. Cannot be changed after
7964          *         creation.
7965          * @instance
7966          *
7967          * @readonly
7968          */
7969         "default": {
7970           get: function get() {
7971             return default_;
7972           },
7973           set: function set() {}
7974         },
7975 
7976         /**
7977          * @memberof TextTrack
7978          * @member {string} mode
7979          *         Set the mode of this TextTrack to a valid {@link TextTrack~Mode}. Will
7980          *         not be set if setting to an invalid mode.
7981          * @instance
7982          *
7983          * @fires TextTrack#modechange
7984          */
7985         mode: {
7986           get: function get() {
7987             return mode;
7988           },
7989           set: function set(newMode) {
7990             var _this2 = this;
7991 
7992             if (!TextTrackMode[newMode]) {
7993               return;
7994             }
7995 
7996             mode = newMode;
7997 
7998             if (!this.preload_ && mode !== 'disabled' && this.cues.length === 0) {
7999               // On-demand load.
8000               loadTrack(this.src, this);
8001             }
8002 
8003             if (mode !== 'disabled') {
8004               this.tech_.ready(function () {
8005                 _this2.tech_.on('timeupdate', timeupdateHandler);
8006               }, true);
8007             } else {
8008               this.tech_.off('timeupdate', timeupdateHandler);
8009             }
8010             /**
8011              * An event that fires when mode changes on this track. This allows
8012              * the TextTrackList that holds this track to act accordingly.
8013              *
8014              * > Note: This is not part of the spec!
8015              *
8016              * @event TextTrack#modechange
8017              * @type {EventTarget~Event}
8018              */
8019 
8020 
8021             this.trigger('modechange');
8022           }
8023         },
8024 
8025         /**
8026          * @memberof TextTrack
8027          * @member {TextTrackCueList} cues
8028          *         The text track cue list for this TextTrack.
8029          * @instance
8030          */
8031         cues: {
8032           get: function get() {
8033             if (!this.loaded_) {
8034               return null;
8035             }
8036 
8037             return cues;
8038           },
8039           set: function set() {}
8040         },
8041 
8042         /**
8043          * @memberof TextTrack
8044          * @member {TextTrackCueList} activeCues
8045          *         The list text track cues that are currently active for this TextTrack.
8046          * @instance
8047          */
8048         activeCues: {
8049           get: function get() {
8050             if (!this.loaded_) {
8051               return null;
8052             } // nothing to do
8053 
8054 
8055             if (this.cues.length === 0) {
8056               return activeCues;
8057             }
8058 
8059             var ct = this.tech_.currentTime();
8060             var active = [];
8061 
8062             for (var i = 0, l = this.cues.length; i < l; i++) {
8063               var cue = this.cues[i];
8064 
8065               if (cue.startTime <= ct && cue.endTime >= ct) {
8066                 active.push(cue);
8067               } else if (cue.startTime === cue.endTime && cue.startTime <= ct && cue.startTime + 0.5 >= ct) {
8068                 active.push(cue);
8069               }
8070             }
8071 
8072             changed = false;
8073 
8074             if (active.length !== this.activeCues_.length) {
8075               changed = true;
8076             } else {
8077               for (var _i = 0; _i < active.length; _i++) {
8078                 if (this.activeCues_.indexOf(active[_i]) === -1) {
8079                   changed = true;
8080                 }
8081               }
8082             }
8083 
8084             this.activeCues_ = active;
8085             activeCues.setCues_(this.activeCues_);
8086             return activeCues;
8087           },
8088           // /!\ Keep this setter empty (see the timeupdate handler above)
8089           set: function set() {}
8090         }
8091       });
8092 
8093       if (settings.src) {
8094         _this.src = settings.src;
8095 
8096         if (!_this.preload_) {
8097           // Tracks will load on-demand.
8098           // Act like we're loaded for other purposes.
8099           _this.loaded_ = true;
8100         }
8101 
8102         if (_this.preload_ || default_ || settings.kind !== 'subtitles' && settings.kind !== 'captions') {
8103           loadTrack(_this.src, assertThisInitialized(_this));
8104         }
8105       } else {
8106         _this.loaded_ = true;
8107       }
8108 
8109       return _this;
8110     }
8111     /**
8112      * Add a cue to the internal list of cues.
8113      *
8114      * @param {TextTrack~Cue} cue
8115      *        The cue to add to our internal list
8116      */
8117 
8118 
8119     var _proto = TextTrack.prototype;
8120 
8121     _proto.addCue = function addCue(originalCue) {
8122       var cue = originalCue;
8123 
8124       if (window$1.vttjs && !(originalCue instanceof window$1.vttjs.VTTCue)) {
8125         cue = new window$1.vttjs.VTTCue(originalCue.startTime, originalCue.endTime, originalCue.text);
8126 
8127         for (var prop in originalCue) {
8128           if (!(prop in cue)) {
8129             cue[prop] = originalCue[prop];
8130           }
8131         } // make sure that `id` is copied over
8132 
8133 
8134         cue.id = originalCue.id;
8135         cue.originalCue_ = originalCue;
8136       }
8137 
8138       var tracks = this.tech_.textTracks();
8139 
8140       for (var i = 0; i < tracks.length; i++) {
8141         if (tracks[i] !== this) {
8142           tracks[i].removeCue(cue);
8143         }
8144       }
8145 
8146       this.cues_.push(cue);
8147       this.cues.setCues_(this.cues_);
8148     }
8149     /**
8150      * Remove a cue from our internal list
8151      *
8152      * @param {TextTrack~Cue} removeCue
8153      *        The cue to remove from our internal list
8154      */
8155     ;
8156 
8157     _proto.removeCue = function removeCue(_removeCue) {
8158       var i = this.cues_.length;
8159 
8160       while (i--) {
8161         var cue = this.cues_[i];
8162 
8163         if (cue === _removeCue || cue.originalCue_ && cue.originalCue_ === _removeCue) {
8164           this.cues_.splice(i, 1);
8165           this.cues.setCues_(this.cues_);
8166           break;
8167         }
8168       }
8169     };
8170 
8171     return TextTrack;
8172   }(Track);
8173   /**
8174    * cuechange - One or more cues in the track have become active or stopped being active.
8175    */
8176 
8177 
8178   TextTrack.prototype.allowedEvents_ = {
8179     cuechange: 'cuechange'
8180   };
8181 
8182   /**
8183    * A representation of a single `AudioTrack`. If it is part of an {@link AudioTrackList}
8184    * only one `AudioTrack` in the list will be enabled at a time.
8185    *
8186    * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotrack}
8187    * @extends Track
8188    */
8189 
8190   var AudioTrack =
8191   /*#__PURE__*/
8192   function (_Track) {
8193     inheritsLoose(AudioTrack, _Track);
8194 
8195     /**
8196      * Create an instance of this class.
8197      *
8198      * @param {Object} [options={}]
8199      *        Object of option names and values
8200      *
8201      * @param {AudioTrack~Kind} [options.kind='']
8202      *        A valid audio track kind
8203      *
8204      * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
8205      *        A unique id for this AudioTrack.
8206      *
8207      * @param {string} [options.label='']
8208      *        The menu label for this track.
8209      *
8210      * @param {string} [options.language='']
8211      *        A valid two character language code.
8212      *
8213      * @param {boolean} [options.enabled]
8214      *        If this track is the one that is currently playing. If this track is part of
8215      *        an {@link AudioTrackList}, only one {@link AudioTrack} will be enabled.
8216      */
8217     function AudioTrack(options) {
8218       var _this;
8219 
8220       if (options === void 0) {
8221         options = {};
8222       }
8223 
8224       var settings = mergeOptions(options, {
8225         kind: AudioTrackKind[options.kind] || ''
8226       });
8227       _this = _Track.call(this, settings) || this;
8228       var enabled = false;
8229       /**
8230        * @memberof AudioTrack
8231        * @member {boolean} enabled
8232        *         If this `AudioTrack` is enabled or not. When setting this will
8233        *         fire {@link AudioTrack#enabledchange} if the state of enabled is changed.
8234        * @instance
8235        *
8236        * @fires VideoTrack#selectedchange
8237        */
8238 
8239       Object.defineProperty(assertThisInitialized(_this), 'enabled', {
8240         get: function get() {
8241           return enabled;
8242         },
8243         set: function set(newEnabled) {
8244           // an invalid or unchanged value
8245           if (typeof newEnabled !== 'boolean' || newEnabled === enabled) {
8246             return;
8247           }
8248 
8249           enabled = newEnabled;
8250           /**
8251            * An event that fires when enabled changes on this track. This allows
8252            * the AudioTrackList that holds this track to act accordingly.
8253            *
8254            * > Note: This is not part of the spec! Native tracks will do
8255            *         this internally without an event.
8256            *
8257            * @event AudioTrack#enabledchange
8258            * @type {EventTarget~Event}
8259            */
8260 
8261           this.trigger('enabledchange');
8262         }
8263       }); // if the user sets this track to selected then
8264       // set selected to that true value otherwise
8265       // we keep it false
8266 
8267       if (settings.enabled) {
8268         _this.enabled = settings.enabled;
8269       }
8270 
8271       _this.loaded_ = true;
8272       return _this;
8273     }
8274 
8275     return AudioTrack;
8276   }(Track);
8277 
8278   /**
8279    * A representation of a single `VideoTrack`.
8280    *
8281    * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#videotrack}
8282    * @extends Track
8283    */
8284 
8285   var VideoTrack =
8286   /*#__PURE__*/
8287   function (_Track) {
8288     inheritsLoose(VideoTrack, _Track);
8289 
8290     /**
8291      * Create an instance of this class.
8292      *
8293      * @param {Object} [options={}]
8294      *        Object of option names and values
8295      *
8296      * @param {string} [options.kind='']
8297      *        A valid {@link VideoTrack~Kind}
8298      *
8299      * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
8300      *        A unique id for this AudioTrack.
8301      *
8302      * @param {string} [options.label='']
8303      *        The menu label for this track.
8304      *
8305      * @param {string} [options.language='']
8306      *        A valid two character language code.
8307      *
8308      * @param {boolean} [options.selected]
8309      *        If this track is the one that is currently playing.
8310      */
8311     function VideoTrack(options) {
8312       var _this;
8313 
8314       if (options === void 0) {
8315         options = {};
8316       }
8317 
8318       var settings = mergeOptions(options, {
8319         kind: VideoTrackKind[options.kind] || ''
8320       });
8321       _this = _Track.call(this, settings) || this;
8322       var selected = false;
8323       /**
8324        * @memberof VideoTrack
8325        * @member {boolean} selected
8326        *         If this `VideoTrack` is selected or not. When setting this will
8327        *         fire {@link VideoTrack#selectedchange} if the state of selected changed.
8328        * @instance
8329        *
8330        * @fires VideoTrack#selectedchange
8331        */
8332 
8333       Object.defineProperty(assertThisInitialized(_this), 'selected', {
8334         get: function get() {
8335           return selected;
8336         },
8337         set: function set(newSelected) {
8338           // an invalid or unchanged value
8339           if (typeof newSelected !== 'boolean' || newSelected === selected) {
8340             return;
8341           }
8342 
8343           selected = newSelected;
8344           /**
8345            * An event that fires when selected changes on this track. This allows
8346            * the VideoTrackList that holds this track to act accordingly.
8347            *
8348            * > Note: This is not part of the spec! Native tracks will do
8349            *         this internally without an event.
8350            *
8351            * @event VideoTrack#selectedchange
8352            * @type {EventTarget~Event}
8353            */
8354 
8355           this.trigger('selectedchange');
8356         }
8357       }); // if the user sets this track to selected then
8358       // set selected to that true value otherwise
8359       // we keep it false
8360 
8361       if (settings.selected) {
8362         _this.selected = settings.selected;
8363       }
8364 
8365       return _this;
8366     }
8367 
8368     return VideoTrack;
8369   }(Track);
8370 
8371   /**
8372    * @memberof HTMLTrackElement
8373    * @typedef {HTMLTrackElement~ReadyState}
8374    * @enum {number}
8375    */
8376 
8377   var NONE = 0;
8378   var LOADING = 1;
8379   var LOADED = 2;
8380   var ERROR = 3;
8381   /**
8382    * A single track represented in the DOM.
8383    *
8384    * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#htmltrackelement}
8385    * @extends EventTarget
8386    */
8387 
8388   var HTMLTrackElement =
8389   /*#__PURE__*/
8390   function (_EventTarget) {
8391     inheritsLoose(HTMLTrackElement, _EventTarget);
8392 
8393     /**
8394      * Create an instance of this class.
8395      *
8396      * @param {Object} options={}
8397      *        Object of option names and values
8398      *
8399      * @param {Tech} options.tech
8400      *        A reference to the tech that owns this HTMLTrackElement.
8401      *
8402      * @param {TextTrack~Kind} [options.kind='subtitles']
8403      *        A valid text track kind.
8404      *
8405      * @param {TextTrack~Mode} [options.mode='disabled']
8406      *        A valid text track mode.
8407      *
8408      * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
8409      *        A unique id for this TextTrack.
8410      *
8411      * @param {string} [options.label='']
8412      *        The menu label for this track.
8413      *
8414      * @param {string} [options.language='']
8415      *        A valid two character language code.
8416      *
8417      * @param {string} [options.srclang='']
8418      *        A valid two character language code. An alternative, but deprioritized
8419      *        vesion of `options.language`
8420      *
8421      * @param {string} [options.src]
8422      *        A url to TextTrack cues.
8423      *
8424      * @param {boolean} [options.default]
8425      *        If this track should default to on or off.
8426      */
8427     function HTMLTrackElement(options) {
8428       var _this;
8429 
8430       if (options === void 0) {
8431         options = {};
8432       }
8433 
8434       _this = _EventTarget.call(this) || this;
8435       var readyState;
8436       var track = new TextTrack(options);
8437       _this.kind = track.kind;
8438       _this.src = track.src;
8439       _this.srclang = track.language;
8440       _this.label = track.label;
8441       _this["default"] = track["default"];
8442       Object.defineProperties(assertThisInitialized(_this), {
8443         /**
8444          * @memberof HTMLTrackElement
8445          * @member {HTMLTrackElement~ReadyState} readyState
8446          *         The current ready state of the track element.
8447          * @instance
8448          */
8449         readyState: {
8450           get: function get() {
8451             return readyState;
8452           }
8453         },
8454 
8455         /**
8456          * @memberof HTMLTrackElement
8457          * @member {TextTrack} track
8458          *         The underlying TextTrack object.
8459          * @instance
8460          *
8461          */
8462         track: {
8463           get: function get() {
8464             return track;
8465           }
8466         }
8467       });
8468       readyState = NONE;
8469       /**
8470        * @listens TextTrack#loadeddata
8471        * @fires HTMLTrackElement#load
8472        */
8473 
8474       track.addEventListener('loadeddata', function () {
8475         readyState = LOADED;
8476 
8477         _this.trigger({
8478           type: 'load',
8479           target: assertThisInitialized(_this)
8480         });
8481       });
8482       return _this;
8483     }
8484 
8485     return HTMLTrackElement;
8486   }(EventTarget);
8487 
8488   HTMLTrackElement.prototype.allowedEvents_ = {
8489     load: 'load'
8490   };
8491   HTMLTrackElement.NONE = NONE;
8492   HTMLTrackElement.LOADING = LOADING;
8493   HTMLTrackElement.LOADED = LOADED;
8494   HTMLTrackElement.ERROR = ERROR;
8495 
8496   /*
8497    * This file contains all track properties that are used in
8498    * player.js, tech.js, html5.js and possibly other techs in the future.
8499    */
8500 
8501   var NORMAL = {
8502     audio: {
8503       ListClass: AudioTrackList,
8504       TrackClass: AudioTrack,
8505       capitalName: 'Audio'
8506     },
8507     video: {
8508       ListClass: VideoTrackList,
8509       TrackClass: VideoTrack,
8510       capitalName: 'Video'
8511     },
8512     text: {
8513       ListClass: TextTrackList,
8514       TrackClass: TextTrack,
8515       capitalName: 'Text'
8516     }
8517   };
8518   Object.keys(NORMAL).forEach(function (type) {
8519     NORMAL[type].getterName = type + "Tracks";
8520     NORMAL[type].privateName = type + "Tracks_";
8521   });
8522   var REMOTE = {
8523     remoteText: {
8524       ListClass: TextTrackList,
8525       TrackClass: TextTrack,
8526       capitalName: 'RemoteText',
8527       getterName: 'remoteTextTracks',
8528       privateName: 'remoteTextTracks_'
8529     },
8530     remoteTextEl: {
8531       ListClass: HtmlTrackElementList,
8532       TrackClass: HTMLTrackElement,
8533       capitalName: 'RemoteTextTrackEls',
8534       getterName: 'remoteTextTrackEls',
8535       privateName: 'remoteTextTrackEls_'
8536     }
8537   };
8538 
8539   var ALL = _extends_1({}, NORMAL, REMOTE);
8540 
8541   REMOTE.names = Object.keys(REMOTE);
8542   NORMAL.names = Object.keys(NORMAL);
8543   ALL.names = [].concat(REMOTE.names).concat(NORMAL.names);
8544 
8545   /**
8546    * Copyright 2013 vtt.js Contributors
8547    *
8548    * Licensed under the Apache License, Version 2.0 (the "License");
8549    * you may not use this file except in compliance with the License.
8550    * You may obtain a copy of the License at
8551    *
8552    *   http://www.apache.org/licenses/LICENSE-2.0
8553    *
8554    * Unless required by applicable law or agreed to in writing, software
8555    * distributed under the License is distributed on an "AS IS" BASIS,
8556    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8557    * See the License for the specific language governing permissions and
8558    * limitations under the License.
8559    */
8560 
8561   /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
8562 
8563   /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
8564 
8565   var _objCreate = Object.create || function () {
8566     function F() {}
8567 
8568     return function (o) {
8569       if (arguments.length !== 1) {
8570         throw new Error('Object.create shim only accepts one parameter.');
8571       }
8572 
8573       F.prototype = o;
8574       return new F();
8575     };
8576   }(); // Creates a new ParserError object from an errorData object. The errorData
8577   // object should have default code and message properties. The default message
8578   // property can be overriden by passing in a message parameter.
8579   // See ParsingError.Errors below for acceptable errors.
8580 
8581 
8582   function ParsingError(errorData, message) {
8583     this.name = "ParsingError";
8584     this.code = errorData.code;
8585     this.message = message || errorData.message;
8586   }
8587 
8588   ParsingError.prototype = _objCreate(Error.prototype);
8589   ParsingError.prototype.constructor = ParsingError; // ParsingError metadata for acceptable ParsingErrors.
8590 
8591   ParsingError.Errors = {
8592     BadSignature: {
8593       code: 0,
8594       message: "Malformed WebVTT signature."
8595     },
8596     BadTimeStamp: {
8597       code: 1,
8598       message: "Malformed time stamp."
8599     }
8600   }; // Try to parse input as a time stamp.
8601 
8602   function parseTimeStamp(input) {
8603     function computeSeconds(h, m, s, f) {
8604       return (h | 0) * 3600 + (m | 0) * 60 + (s | 0) + (f | 0) / 1000;
8605     }
8606 
8607     var m = input.match(/^(\d+):(\d{1,2})(:\d{1,2})?\.(\d{3})/);
8608 
8609     if (!m) {
8610       return null;
8611     }
8612 
8613     if (m[3]) {
8614       // Timestamp takes the form of [hours]:[minutes]:[seconds].[milliseconds]
8615       return computeSeconds(m[1], m[2], m[3].replace(":", ""), m[4]);
8616     } else if (m[1] > 59) {
8617       // Timestamp takes the form of [hours]:[minutes].[milliseconds]
8618       // First position is hours as it's over 59.
8619       return computeSeconds(m[1], m[2], 0, m[4]);
8620     } else {
8621       // Timestamp takes the form of [minutes]:[seconds].[milliseconds]
8622       return computeSeconds(0, m[1], m[2], m[4]);
8623     }
8624   } // A settings object holds key/value pairs and will ignore anything but the first
8625   // assignment to a specific key.
8626 
8627 
8628   function Settings() {
8629     this.values = _objCreate(null);
8630   }
8631 
8632   Settings.prototype = {
8633     // Only accept the first assignment to any key.
8634     set: function set(k, v) {
8635       if (!this.get(k) && v !== "") {
8636         this.values[k] = v;
8637       }
8638     },
8639     // Return the value for a key, or a default value.
8640     // If 'defaultKey' is passed then 'dflt' is assumed to be an object with
8641     // a number of possible default values as properties where 'defaultKey' is
8642     // the key of the property that will be chosen; otherwise it's assumed to be
8643     // a single value.
8644     get: function get(k, dflt, defaultKey) {
8645       if (defaultKey) {
8646         return this.has(k) ? this.values[k] : dflt[defaultKey];
8647       }
8648 
8649       return this.has(k) ? this.values[k] : dflt;
8650     },
8651     // Check whether we have a value for a key.
8652     has: function has(k) {
8653       return k in this.values;
8654     },
8655     // Accept a setting if its one of the given alternatives.
8656     alt: function alt(k, v, a) {
8657       for (var n = 0; n < a.length; ++n) {
8658         if (v === a[n]) {
8659           this.set(k, v);
8660           break;
8661         }
8662       }
8663     },
8664     // Accept a setting if its a valid (signed) integer.
8665     integer: function integer(k, v) {
8666       if (/^-?\d+$/.test(v)) {
8667         // integer
8668         this.set(k, parseInt(v, 10));
8669       }
8670     },
8671     // Accept a setting if its a valid percentage.
8672     percent: function percent(k, v) {
8673       var m;
8674 
8675       if (m = v.match(/^([\d]{1,3})(\.[\d]*)?%$/)) {
8676         v = parseFloat(v);
8677 
8678         if (v >= 0 && v <= 100) {
8679           this.set(k, v);
8680           return true;
8681         }
8682       }
8683 
8684       return false;
vendor: 5,650 bytes, lines 8685-8886
8685     }
8686   }; // Helper function to parse input into groups separated by 'groupDelim', and
8687   // interprete each group as a key/value pair separated by 'keyValueDelim'.
8688 
8689   function parseOptions(input, callback, keyValueDelim, groupDelim) {
8690     var groups = groupDelim ? input.split(groupDelim) : [input];
8691 
8692     for (var i in groups) {
8693       if (typeof groups[i] !== "string") {
8694         continue;
8695       }
8696 
8697       var kv = groups[i].split(keyValueDelim);
8698 
8699       if (kv.length !== 2) {
8700         continue;
8701       }
8702 
8703       var k = kv[0];
8704       var v = kv[1];
8705       callback(k, v);
8706     }
8707   }
8708 
8709   function parseCue(input, cue, regionList) {
8710     // Remember the original input if we need to throw an error.
8711     var oInput = input; // 4.1 WebVTT timestamp
8712 
8713     function consumeTimeStamp() {
8714       var ts = parseTimeStamp(input);
8715 
8716       if (ts === null) {
8717         throw new ParsingError(ParsingError.Errors.BadTimeStamp, "Malformed timestamp: " + oInput);
8718       } // Remove time stamp from input.
8719 
8720 
8721       input = input.replace(/^[^\sa-zA-Z-]+/, "");
8722       return ts;
8723     } // 4.4.2 WebVTT cue settings
8724 
8725 
8726     function consumeCueSettings(input, cue) {
8727       var settings = new Settings();
8728       parseOptions(input, function (k, v) {
8729         switch (k) {
8730           case "region":
8731             // Find the last region we parsed with the same region id.
8732             for (var i = regionList.length - 1; i >= 0; i--) {
8733               if (regionList[i].id === v) {
8734                 settings.set(k, regionList[i].region);
8735                 break;
8736               }
8737             }
8738 
8739             break;
8740 
8741           case "vertical":
8742             settings.alt(k, v, ["rl", "lr"]);
8743             break;
8744 
8745           case "line":
8746             var vals = v.split(","),
8747                 vals0 = vals[0];
8748             settings.integer(k, vals0);
8749             settings.percent(k, vals0) ? settings.set("snapToLines", false) : null;
8750             settings.alt(k, vals0, ["auto"]);
8751 
8752             if (vals.length === 2) {
8753               settings.alt("lineAlign", vals[1], ["start", "center", "end"]);
8754             }
8755 
8756             break;
8757 
8758           case "position":
8759             vals = v.split(",");
8760             settings.percent(k, vals[0]);
8761 
8762             if (vals.length === 2) {
8763               settings.alt("positionAlign", vals[1], ["start", "center", "end"]);
8764             }
8765 
8766             break;
8767 
8768           case "size":
8769             settings.percent(k, v);
8770             break;
8771 
8772           case "align":
8773             settings.alt(k, v, ["start", "center", "end", "left", "right"]);
8774             break;
8775         }
8776       }, /:/, /\s/); // Apply default values for any missing fields.
8777 
8778       cue.region = settings.get("region", null);
8779       cue.vertical = settings.get("vertical", "");
8780 
8781       try {
8782         cue.line = settings.get("line", "auto");
8783       } catch (e) {}
8784 
8785       cue.lineAlign = settings.get("lineAlign", "start");
8786       cue.snapToLines = settings.get("snapToLines", true);
8787       cue.size = settings.get("size", 100); // Safari still uses the old middle value and won't accept center
8788 
8789       try {
8790         cue.align = settings.get("align", "center");
8791       } catch (e) {
8792         cue.align = settings.get("align", "middle");
8793       }
8794 
8795       try {
8796         cue.position = settings.get("position", "auto");
8797       } catch (e) {
8798         cue.position = settings.get("position", {
8799           start: 0,
8800           left: 0,
8801           center: 50,
8802           middle: 50,
8803           end: 100,
8804           right: 100
8805         }, cue.align);
8806       }
8807 
8808       cue.positionAlign = settings.get("positionAlign", {
8809         start: "start",
8810         left: "start",
8811         center: "center",
8812         middle: "center",
8813         end: "end",
8814         right: "end"
8815       }, cue.align);
8816     }
8817 
8818     function skipWhitespace() {
8819       input = input.replace(/^\s+/, "");
8820     } // 4.1 WebVTT cue timings.
8821 
8822 
8823     skipWhitespace();
8824     cue.startTime = consumeTimeStamp(); // (1) collect cue start time
8825 
8826     skipWhitespace();
8827 
8828     if (input.substr(0, 3) !== "-->") {
8829       // (3) next characters must match "-->"
8830       throw new ParsingError(ParsingError.Errors.BadTimeStamp, "Malformed time stamp (time stamps must be separated by '-->'): " + oInput);
8831     }
8832 
8833     input = input.substr(3);
8834     skipWhitespace();
8835     cue.endTime = consumeTimeStamp(); // (5) collect cue end time
8836     // 4.1 WebVTT cue settings list.
8837 
8838     skipWhitespace();
8839     consumeCueSettings(input, cue);
8840   }
8841 
8842   var TEXTAREA_ELEMENT = document.createElement("textarea");
8843   var TAG_NAME = {
8844     c: "span",
8845     i: "i",
8846     b: "b",
8847     u: "u",
8848     ruby: "ruby",
8849     rt: "rt",
8850     v: "span",
8851     lang: "span"
8852   }; // 5.1 default text color
8853   // 5.2 default text background color is equivalent to text color with bg_ prefix
8854 
8855   var DEFAULT_COLOR_CLASS = {
8856     white: 'rgba(255,255,255,1)',
8857     lime: 'rgba(0,255,0,1)',
8858     cyan: 'rgba(0,255,255,1)',
8859     red: 'rgba(255,0,0,1)',
8860     yellow: 'rgba(255,255,0,1)',
8861     magenta: 'rgba(255,0,255,1)',
8862     blue: 'rgba(0,0,255,1)',
8863     black: 'rgba(0,0,0,1)'
8864   };
8865   var TAG_ANNOTATION = {
8866     v: "title",
8867     lang: "lang"
8868   };
8869   var NEEDS_PARENT = {
8870     rt: "ruby"
8871   }; // Parse content into a document fragment.
8872 
8873   function parseContent(window, input) {
8874     function nextToken() {
8875       // Check for end-of-string.
8876       if (!input) {
8877         return null;
8878       } // Consume 'n' characters from the input.
8879 
8880 
8881       function consume(result) {
8882         input = input.substr(result.length);
8883         return result;
8884       }
8885 
8886       var m = input.match(/^([^<]*)(<[^>
vendor: 23,858 bytes, lines 8886-9546
8886]*>?)?/); // If there is some text before the next tag, return it, otherwise return
8887       // the tag.
8888 
8889       return consume(m[1] ? m[1] : m[2]);
8890     }
8891 
8892     function unescape(s) {
8893       TEXTAREA_ELEMENT.innerHTML = s;
8894       s = TEXTAREA_ELEMENT.textContent;
8895       TEXTAREA_ELEMENT.textContent = "";
8896       return s;
8897     }
8898 
8899     function shouldAdd(current, element) {
8900       return !NEEDS_PARENT[element.localName] || NEEDS_PARENT[element.localName] === current.localName;
8901     } // Create an element for this tag.
8902 
8903 
8904     function createElement(type, annotation) {
8905       var tagName = TAG_NAME[type];
8906 
8907       if (!tagName) {
8908         return null;
8909       }
8910 
8911       var element = window.document.createElement(tagName);
8912       var name = TAG_ANNOTATION[type];
8913 
8914       if (name && annotation) {
8915         element[name] = annotation.trim();
8916       }
8917 
8918       return element;
8919     }
8920 
8921     var rootDiv = window.document.createElement("div"),
8922         current = rootDiv,
8923         t,
8924         tagStack = [];
8925 
8926     while ((t = nextToken()) !== null) {
8927       if (t[0] === '<') {
8928         if (t[1] === "/") {
8929           // If the closing tag matches, move back up to the parent node.
8930           if (tagStack.length && tagStack[tagStack.length - 1] === t.substr(2).replace(">", "")) {
8931             tagStack.pop();
8932             current = current.parentNode;
8933           } // Otherwise just ignore the end tag.
8934 
8935 
8936           continue;
8937         }
8938 
8939         var ts = parseTimeStamp(t.substr(1, t.length - 2));
8940         var node;
8941 
8942         if (ts) {
8943           // Timestamps are lead nodes as well.
8944           node = window.document.createProcessingInstruction("timestamp", ts);
8945           current.appendChild(node);
8946           continue;
8947         }
8948 
8949         var m = t.match(/^<([^.\s/0-9>]+)(\.[^\s\\>]+)?([^>\\]+)?(\\?)>?$/); // If we can't parse the tag, skip to the next tag.
8950 
8951         if (!m) {
8952           continue;
8953         } // Try to construct an element, and ignore the tag if we couldn't.
8954 
8955 
8956         node = createElement(m[1], m[3]);
8957 
8958         if (!node) {
8959           continue;
8960         } // Determine if the tag should be added based on the context of where it
8961         // is placed in the cuetext.
8962 
8963 
8964         if (!shouldAdd(current, node)) {
8965           continue;
8966         } // Set the class list (as a list of classes, separated by space).
8967 
8968 
8969         if (m[2]) {
8970           var classes = m[2].split('.');
8971           classes.forEach(function (cl) {
8972             var bgColor = /^bg_/.test(cl); // slice out `bg_` if it's a background color
8973 
8974             var colorName = bgColor ? cl.slice(3) : cl;
8975 
8976             if (DEFAULT_COLOR_CLASS.hasOwnProperty(colorName)) {
8977               var propName = bgColor ? 'background-color' : 'color';
8978               var propValue = DEFAULT_COLOR_CLASS[colorName];
8979               node.style[propName] = propValue;
8980             }
8981           });
8982           node.className = classes.join(' ');
8983         } // Append the node to the current node, and enter the scope of the new
8984         // node.
8985 
8986 
8987         tagStack.push(m[1]);
8988         current.appendChild(node);
8989         current = node;
8990         continue;
8991       } // Text nodes are leaf nodes.
8992 
8993 
8994       current.appendChild(window.document.createTextNode(unescape(t)));
8995     }
8996 
8997     return rootDiv;
8998   } // This is a list of all the Unicode characters that have a strong
8999   // right-to-left category. What this means is that these characters are
9000   // written right-to-left for sure. It was generated by pulling all the strong
9001   // right-to-left characters out of the Unicode data table. That table can
9002   // found at: http://www.unicode.org/Public/UNIDATA/UnicodeData.txt
9003 
9004 
9005   var strongRTLRanges = [[0x5be, 0x5be], [0x5c0, 0x5c0], [0x5c3, 0x5c3], [0x5c6, 0x5c6], [0x5d0, 0x5ea], [0x5f0, 0x5f4], [0x608, 0x608], [0x60b, 0x60b], [0x60d, 0x60d], [0x61b, 0x61b], [0x61e, 0x64a], [0x66d, 0x66f], [0x671, 0x6d5], [0x6e5, 0x6e6], [0x6ee, 0x6ef], [0x6fa, 0x70d], [0x70f, 0x710], [0x712, 0x72f], [0x74d, 0x7a5], [0x7b1, 0x7b1], [0x7c0, 0x7ea], [0x7f4, 0x7f5], [0x7fa, 0x7fa], [0x800, 0x815], [0x81a, 0x81a], [0x824, 0x824], [0x828, 0x828], [0x830, 0x83e], [0x840, 0x858], [0x85e, 0x85e], [0x8a0, 0x8a0], [0x8a2, 0x8ac], [0x200f, 0x200f], [0xfb1d, 0xfb1d], [0xfb1f, 0xfb28], [0xfb2a, 0xfb36], [0xfb38, 0xfb3c], [0xfb3e, 0xfb3e], [0xfb40, 0xfb41], [0xfb43, 0xfb44], [0xfb46, 0xfbc1], [0xfbd3, 0xfd3d], [0xfd50, 0xfd8f], [0xfd92, 0xfdc7], [0xfdf0, 0xfdfc], [0xfe70, 0xfe74], [0xfe76, 0xfefc], [0x10800, 0x10805], [0x10808, 0x10808], [0x1080a, 0x10835], [0x10837, 0x10838], [0x1083c, 0x1083c], [0x1083f, 0x10855], [0x10857, 0x1085f], [0x10900, 0x1091b], [0x10920, 0x10939], [0x1093f, 0x1093f], [0x10980, 0x109b7], [0x109be, 0x109bf], [0x10a00, 0x10a00], [0x10a10, 0x10a13], [0x10a15, 0x10a17], [0x10a19, 0x10a33], [0x10a40, 0x10a47], [0x10a50, 0x10a58], [0x10a60, 0x10a7f], [0x10b00, 0x10b35], [0x10b40, 0x10b55], [0x10b58, 0x10b72], [0x10b78, 0x10b7f], [0x10c00, 0x10c48], [0x1ee00, 0x1ee03], [0x1ee05, 0x1ee1f], [0x1ee21, 0x1ee22], [0x1ee24, 0x1ee24], [0x1ee27, 0x1ee27], [0x1ee29, 0x1ee32], [0x1ee34, 0x1ee37], [0x1ee39, 0x1ee39], [0x1ee3b, 0x1ee3b], [0x1ee42, 0x1ee42], [0x1ee47, 0x1ee47], [0x1ee49, 0x1ee49], [0x1ee4b, 0x1ee4b], [0x1ee4d, 0x1ee4f], [0x1ee51, 0x1ee52], [0x1ee54, 0x1ee54], [0x1ee57, 0x1ee57], [0x1ee59, 0x1ee59], [0x1ee5b, 0x1ee5b], [0x1ee5d, 0x1ee5d], [0x1ee5f, 0x1ee5f], [0x1ee61, 0x1ee62], [0x1ee64, 0x1ee64], [0x1ee67, 0x1ee6a], [0x1ee6c, 0x1ee72], [0x1ee74, 0x1ee77], [0x1ee79, 0x1ee7c], [0x1ee7e, 0x1ee7e], [0x1ee80, 0x1ee89], [0x1ee8b, 0x1ee9b], [0x1eea1, 0x1eea3], [0x1eea5, 0x1eea9], [0x1eeab, 0x1eebb], [0x10fffd, 0x10fffd]];
9006 
9007   function isStrongRTLChar(charCode) {
9008     for (var i = 0; i < strongRTLRanges.length; i++) {
9009       var currentRange = strongRTLRanges[i];
9010 
9011       if (charCode >= currentRange[0] && charCode <= currentRange[1]) {
9012         return true;
9013       }
9014     }
9015 
9016     return false;
9017   }
9018 
9019   function determineBidi(cueDiv) {
9020     var nodeStack = [],
9021         text = "",
9022         charCode;
9023 
9024     if (!cueDiv || !cueDiv.childNodes) {
9025       return "ltr";
9026     }
9027 
9028     function pushNodes(nodeStack, node) {
9029       for (var i = node.childNodes.length - 1; i >= 0; i--) {
9030         nodeStack.push(node.childNodes[i]);
9031       }
9032     }
9033 
9034     function nextTextNode(nodeStack) {
9035       if (!nodeStack || !nodeStack.length) {
9036         return null;
9037       }
9038 
9039       var node = nodeStack.pop(),
9040           text = node.textContent || node.innerText;
9041 
9042       if (text) {
9043         // TODO: This should match all unicode type B characters (paragraph
9044         // separator characters). See issue #115.
9045         var m = text.match(/^.*(\n|\r)/);
9046 
9047         if (m) {
9048           nodeStack.length = 0;
9049           return m[0];
9050         }
9051 
9052         return text;
9053       }
9054 
9055       if (node.tagName === "ruby") {
9056         return nextTextNode(nodeStack);
9057       }
9058 
9059       if (node.childNodes) {
9060         pushNodes(nodeStack, node);
9061         return nextTextNode(nodeStack);
9062       }
9063     }
9064 
9065     pushNodes(nodeStack, cueDiv);
9066 
9067     while (text = nextTextNode(nodeStack)) {
9068       for (var i = 0; i < text.length; i++) {
9069         charCode = text.charCodeAt(i);
9070 
9071         if (isStrongRTLChar(charCode)) {
9072           return "rtl";
9073         }
9074       }
9075     }
9076 
9077     return "ltr";
9078   }
9079 
9080   function computeLinePos(cue) {
9081     if (typeof cue.line === "number" && (cue.snapToLines || cue.line >= 0 && cue.line <= 100)) {
9082       return cue.line;
9083     }
9084 
9085     if (!cue.track || !cue.track.textTrackList || !cue.track.textTrackList.mediaElement) {
9086       return -1;
9087     }
9088 
9089     var track = cue.track,
9090         trackList = track.textTrackList,
9091         count = 0;
9092 
9093     for (var i = 0; i < trackList.length && trackList[i] !== track; i++) {
9094       if (trackList[i].mode === "showing") {
9095         count++;
9096       }
9097     }
9098 
9099     return ++count * -1;
9100   }
9101 
9102   function StyleBox() {} // Apply styles to a div. If there is no div passed then it defaults to the
9103   // div on 'this'.
9104 
9105 
9106   StyleBox.prototype.applyStyles = function (styles, div) {
9107     div = div || this.div;
9108 
9109     for (var prop in styles) {
9110       if (styles.hasOwnProperty(prop)) {
9111         div.style[prop] = styles[prop];
9112       }
9113     }
9114   };
9115 
9116   StyleBox.prototype.formatStyle = function (val, unit) {
9117     return val === 0 ? 0 : val + unit;
9118   }; // Constructs the computed display state of the cue (a div). Places the div
9119   // into the overlay which should be a block level element (usually a div).
9120 
9121 
9122   function CueStyleBox(window, cue, styleOptions) {
9123     StyleBox.call(this);
9124     this.cue = cue; // Parse our cue's text into a DOM tree rooted at 'cueDiv'. This div will
9125     // have inline positioning and will function as the cue background box.
9126 
9127     this.cueDiv = parseContent(window, cue.text);
9128     var styles = {
9129       color: "rgba(255, 255, 255, 1)",
9130       backgroundColor: "rgba(0, 0, 0, 0.8)",
9131       position: "relative",
9132       left: 0,
9133       right: 0,
9134       top: 0,
9135       bottom: 0,
9136       display: "inline",
9137       writingMode: cue.vertical === "" ? "horizontal-tb" : cue.vertical === "lr" ? "vertical-lr" : "vertical-rl",
9138       unicodeBidi: "plaintext"
9139     };
9140     this.applyStyles(styles, this.cueDiv); // Create an absolutely positioned div that will be used to position the cue
9141     // div. Note, all WebVTT cue-setting alignments are equivalent to the CSS
9142     // mirrors of them except middle instead of center on Safari.
9143 
9144     this.div = window.document.createElement("div");
9145     styles = {
9146       direction: determineBidi(this.cueDiv),
9147       writingMode: cue.vertical === "" ? "horizontal-tb" : cue.vertical === "lr" ? "vertical-lr" : "vertical-rl",
9148       unicodeBidi: "plaintext",
9149       textAlign: cue.align === "middle" ? "center" : cue.align,
9150       font: styleOptions.font,
9151       whiteSpace: "pre-line",
9152       position: "absolute"
9153     };
9154     this.applyStyles(styles);
9155     this.div.appendChild(this.cueDiv); // Calculate the distance from the reference edge of the viewport to the text
9156     // position of the cue box. The reference edge will be resolved later when
9157     // the box orientation styles are applied.
9158 
9159     var textPos = 0;
9160 
9161     switch (cue.positionAlign) {
9162       case "start":
9163         textPos = cue.position;
9164         break;
9165 
9166       case "center":
9167         textPos = cue.position - cue.size / 2;
9168         break;
9169 
9170       case "end":
9171         textPos = cue.position - cue.size;
9172         break;
9173     } // Horizontal box orientation; textPos is the distance from the left edge of the
9174     // area to the left edge of the box and cue.size is the distance extending to
9175     // the right from there.
9176 
9177 
9178     if (cue.vertical === "") {
9179       this.applyStyles({
9180         left: this.formatStyle(textPos, "%"),
9181         width: this.formatStyle(cue.size, "%")
9182       }); // Vertical box orientation; textPos is the distance from the top edge of the
9183       // area to the top edge of the box and cue.size is the height extending
9184       // downwards from there.
9185     } else {
9186       this.applyStyles({
9187         top: this.formatStyle(textPos, "%"),
9188         height: this.formatStyle(cue.size, "%")
9189       });
9190     }
9191 
9192     this.move = function (box) {
9193       this.applyStyles({
9194         top: this.formatStyle(box.top, "px"),
9195         bottom: this.formatStyle(box.bottom, "px"),
9196         left: this.formatStyle(box.left, "px"),
9197         right: this.formatStyle(box.right, "px"),
9198         height: this.formatStyle(box.height, "px"),
9199         width: this.formatStyle(box.width, "px")
9200       });
9201     };
9202   }
9203 
9204   CueStyleBox.prototype = _objCreate(StyleBox.prototype);
9205   CueStyleBox.prototype.constructor = CueStyleBox; // Represents the co-ordinates of an Element in a way that we can easily
9206   // compute things with such as if it overlaps or intersects with another Element.
9207   // Can initialize it with either a StyleBox or another BoxPosition.
9208 
9209   function BoxPosition(obj) {
9210     // Either a BoxPosition was passed in and we need to copy it, or a StyleBox
9211     // was passed in and we need to copy the results of 'getBoundingClientRect'
9212     // as the object returned is readonly. All co-ordinate values are in reference
9213     // to the viewport origin (top left).
9214     var lh, height, width, top;
9215 
9216     if (obj.div) {
9217       height = obj.div.offsetHeight;
9218       width = obj.div.offsetWidth;
9219       top = obj.div.offsetTop;
9220       var rects = (rects = obj.div.childNodes) && (rects = rects[0]) && rects.getClientRects && rects.getClientRects();
9221       obj = obj.div.getBoundingClientRect(); // In certain cases the outter div will be slightly larger then the sum of
9222       // the inner div's lines. This could be due to bold text, etc, on some platforms.
9223       // In this case we should get the average line height and use that. This will
9224       // result in the desired behaviour.
9225 
9226       lh = rects ? Math.max(rects[0] && rects[0].height || 0, obj.height / rects.length) : 0;
9227     }
9228 
9229     this.left = obj.left;
9230     this.right = obj.right;
9231     this.top = obj.top || top;
9232     this.height = obj.height || height;
9233     this.bottom = obj.bottom || top + (obj.height || height);
9234     this.width = obj.width || width;
9235     this.lineHeight = lh !== undefined ? lh : obj.lineHeight;
9236   } // Move the box along a particular axis. Optionally pass in an amount to move
9237   // the box. If no amount is passed then the default is the line height of the
9238   // box.
9239 
9240 
9241   BoxPosition.prototype.move = function (axis, toMove) {
9242     toMove = toMove !== undefined ? toMove : this.lineHeight;
9243 
9244     switch (axis) {
9245       case "+x":
9246         this.left += toMove;
9247         this.right += toMove;
9248         break;
9249 
9250       case "-x":
9251         this.left -= toMove;
9252         this.right -= toMove;
9253         break;
9254 
9255       case "+y":
9256         this.top += toMove;
9257         this.bottom += toMove;
9258         break;
9259 
9260       case "-y":
9261         this.top -= toMove;
9262         this.bottom -= toMove;
9263         break;
9264     }
9265   }; // Check if this box overlaps another box, b2.
9266 
9267 
9268   BoxPosition.prototype.overlaps = function (b2) {
9269     return this.left < b2.right && this.right > b2.left && this.top < b2.bottom && this.bottom > b2.top;
9270   }; // Check if this box overlaps any other boxes in boxes.
9271 
9272 
9273   BoxPosition.prototype.overlapsAny = function (boxes) {
9274     for (var i = 0; i < boxes.length; i++) {
9275       if (this.overlaps(boxes[i])) {
9276         return true;
9277       }
9278     }
9279 
9280     return false;
9281   }; // Check if this box is within another box.
9282 
9283 
9284   BoxPosition.prototype.within = function (container) {
9285     return this.top >= container.top && this.bottom <= container.bottom && this.left >= container.left && this.right <= container.right;
9286   }; // Check if this box is entirely within the container or it is overlapping
9287   // on the edge opposite of the axis direction passed. For example, if "+x" is
9288   // passed and the box is overlapping on the left edge of the container, then
9289   // return true.
9290 
9291 
9292   BoxPosition.prototype.overlapsOppositeAxis = function (container, axis) {
9293     switch (axis) {
9294       case "+x":
9295         return this.left < container.left;
9296 
9297       case "-x":
9298         return this.right > container.right;
9299 
9300       case "+y":
9301         return this.top < container.top;
9302 
9303       case "-y":
9304         return this.bottom > container.bottom;
9305     }
9306   }; // Find the percentage of the area that this box is overlapping with another
9307   // box.
9308 
9309 
9310   BoxPosition.prototype.intersectPercentage = function (b2) {
9311     var x = Math.max(0, Math.min(this.right, b2.right) - Math.max(this.left, b2.left)),
9312         y = Math.max(0, Math.min(this.bottom, b2.bottom) - Math.max(this.top, b2.top)),
9313         intersectArea = x * y;
9314     return intersectArea / (this.height * this.width);
9315   }; // Convert the positions from this box to CSS compatible positions using
9316   // the reference container's positions. This has to be done because this
9317   // box's positions are in reference to the viewport origin, whereas, CSS
9318   // values are in referecne to their respective edges.
9319 
9320 
9321   BoxPosition.prototype.toCSSCompatValues = function (reference) {
9322     return {
9323       top: this.top - reference.top,
9324       bottom: reference.bottom - this.bottom,
9325       left: this.left - reference.left,
9326       right: reference.right - this.right,
9327       height: this.height,
9328       width: this.width
9329     };
9330   }; // Get an object that represents the box's position without anything extra.
9331   // Can pass a StyleBox, HTMLElement, or another BoxPositon.
9332 
9333 
9334   BoxPosition.getSimpleBoxPosition = function (obj) {
9335     var height = obj.div ? obj.div.offsetHeight : obj.tagName ? obj.offsetHeight : 0;
9336     var width = obj.div ? obj.div.offsetWidth : obj.tagName ? obj.offsetWidth : 0;
9337     var top = obj.div ? obj.div.offsetTop : obj.tagName ? obj.offsetTop : 0;
9338     obj = obj.div ? obj.div.getBoundingClientRect() : obj.tagName ? obj.getBoundingClientRect() : obj;
9339     var ret = {
9340       left: obj.left,
9341       right: obj.right,
9342       top: obj.top || top,
9343       height: obj.height || height,
9344       bottom: obj.bottom || top + (obj.height || height),
9345       width: obj.width || width
9346     };
9347     return ret;
9348   }; // Move a StyleBox to its specified, or next best, position. The containerBox
9349   // is the box that contains the StyleBox, such as a div. boxPositions are
9350   // a list of other boxes that the styleBox can't overlap with.
9351 
9352 
9353   function moveBoxToLinePosition(window, styleBox, containerBox, boxPositions) {
9354     // Find the best position for a cue box, b, on the video. The axis parameter
9355     // is a list of axis, the order of which, it will move the box along. For example:
9356     // Passing ["+x", "-x"] will move the box first along the x axis in the positive
9357     // direction. If it doesn't find a good position for it there it will then move
9358     // it along the x axis in the negative direction.
9359     function findBestPosition(b, axis) {
9360       var bestPosition,
9361           specifiedPosition = new BoxPosition(b),
9362           percentage = 1; // Highest possible so the first thing we get is better.
9363 
9364       for (var i = 0; i < axis.length; i++) {
9365         while (b.overlapsOppositeAxis(containerBox, axis[i]) || b.within(containerBox) && b.overlapsAny(boxPositions)) {
9366           b.move(axis[i]);
9367         } // We found a spot where we aren't overlapping anything. This is our
9368         // best position.
9369 
9370 
9371         if (b.within(containerBox)) {
9372           return b;
9373         }
9374 
9375         var p = b.intersectPercentage(containerBox); // If we're outside the container box less then we were on our last try
9376         // then remember this position as the best position.
9377 
9378         if (percentage > p) {
9379           bestPosition = new BoxPosition(b);
9380           percentage = p;
9381         } // Reset the box position to the specified position.
9382 
9383 
9384         b = new BoxPosition(specifiedPosition);
9385       }
9386 
9387       return bestPosition || specifiedPosition;
9388     }
9389 
9390     var boxPosition = new BoxPosition(styleBox),
9391         cue = styleBox.cue,
9392         linePos = computeLinePos(cue),
9393         axis = []; // If we have a line number to align the cue to.
9394 
9395     if (cue.snapToLines) {
9396       var size;
9397 
9398       switch (cue.vertical) {
9399         case "":
9400           axis = ["+y", "-y"];
9401           size = "height";
9402           break;
9403 
9404         case "rl":
9405           axis = ["+x", "-x"];
9406           size = "width";
9407           break;
9408 
9409         case "lr":
9410           axis = ["-x", "+x"];
9411           size = "width";
9412           break;
9413       }
9414 
9415       var step = boxPosition.lineHeight,
9416           position = step * Math.round(linePos),
9417           maxPosition = containerBox[size] + step,
9418           initialAxis = axis[0]; // If the specified intial position is greater then the max position then
9419       // clamp the box to the amount of steps it would take for the box to
9420       // reach the max position.
9421 
9422       if (Math.abs(position) > maxPosition) {
9423         position = position < 0 ? -1 : 1;
9424         position *= Math.ceil(maxPosition / step) * step;
9425       } // If computed line position returns negative then line numbers are
9426       // relative to the bottom of the video instead of the top. Therefore, we
9427       // need to increase our initial position by the length or width of the
9428       // video, depending on the writing direction, and reverse our axis directions.
9429 
9430 
9431       if (linePos < 0) {
9432         position += cue.vertical === "" ? containerBox.height : containerBox.width;
9433         axis = axis.reverse();
9434       } // Move the box to the specified position. This may not be its best
9435       // position.
9436 
9437 
9438       boxPosition.move(initialAxis, position);
9439     } else {
9440       // If we have a percentage line value for the cue.
9441       var calculatedPercentage = boxPosition.lineHeight / containerBox.height * 100;
9442 
9443       switch (cue.lineAlign) {
9444         case "center":
9445           linePos -= calculatedPercentage / 2;
9446           break;
9447 
9448         case "end":
9449           linePos -= calculatedPercentage;
9450           break;
9451       } // Apply initial line position to the cue box.
9452 
9453 
9454       switch (cue.vertical) {
9455         case "":
9456           styleBox.applyStyles({
9457             top: styleBox.formatStyle(linePos, "%")
9458           });
9459           break;
9460 
9461         case "rl":
9462           styleBox.applyStyles({
9463             left: styleBox.formatStyle(linePos, "%")
9464           });
9465           break;
9466 
9467         case "lr":
9468           styleBox.applyStyles({
9469             right: styleBox.formatStyle(linePos, "%")
9470           });
9471           break;
9472       }
9473 
9474       axis = ["+y", "-x", "+x", "-y"]; // Get the box position again after we've applied the specified positioning
9475       // to it.
9476 
9477       boxPosition = new BoxPosition(styleBox);
9478     }
9479 
9480     var bestPosition = findBestPosition(boxPosition, axis);
9481     styleBox.move(bestPosition.toCSSCompatValues(containerBox));
9482   }
9483 
9484   function WebVTT$1() {} // Nothing
9485   // Helper to allow strings to be decoded instead of the default binary utf8 data.
9486 
9487 
9488   WebVTT$1.StringDecoder = function () {
9489     return {
9490       decode: function decode(data) {
9491         if (!data) {
9492           return "";
9493         }
9494 
9495         if (typeof data !== "string") {
9496           throw new Error("Error - expected string data.");
9497         }
9498 
9499         return decodeURIComponent(encodeURIComponent(data));
9500       }
9501     };
9502   };
9503 
9504   WebVTT$1.convertCueToDOMTree = function (window, cuetext) {
9505     if (!window || !cuetext) {
9506       return null;
9507     }
9508 
9509     return parseContent(window, cuetext);
9510   };
9511 
9512   var FONT_SIZE_PERCENT = 0.05;
9513   var FONT_STYLE = "sans-serif";
9514   var CUE_BACKGROUND_PADDING = "1.5%"; // Runs the processing model over the cues and regions passed to it.
9515   // @param overlay A block level element (usually a div) that the computed cues
9516   //                and regions will be placed into.
9517 
9518   WebVTT$1.processCues = function (window, cues, overlay) {
9519     if (!window || !cues || !overlay) {
9520       return null;
9521     } // Remove all previous children.
9522 
9523 
9524     while (overlay.firstChild) {
9525       overlay.removeChild(overlay.firstChild);
9526     }
9527 
9528     var paddedOverlay = window.document.createElement("div");
9529     paddedOverlay.style.position = "absolute";
9530     paddedOverlay.style.left = "0";
9531     paddedOverlay.style.right = "0";
9532     paddedOverlay.style.top = "0";
9533     paddedOverlay.style.bottom = "0";
9534     paddedOverlay.style.margin = CUE_BACKGROUND_PADDING;
9535     overlay.appendChild(paddedOverlay); // Determine if we need to compute the display states of the cues. This could
9536     // be the case if a cue's state has been changed since the last computation or
9537     // if it has not been computed yet.
9538 
9539     function shouldCompute(cues) {
9540       for (var i = 0; i < cues.length; i++) {
9541         if (cues[i].hasBeenReset || !cues[i].displayState) {
9542           return true;
9543         }
9544       }
9545 
9546       return false;
vendor: 7,094 bytes, lines 9547-9776
9547     } // We don't need to recompute the cues' display states. Just reuse them.
9548 
9549 
9550     if (!shouldCompute(cues)) {
9551       for (var i = 0; i < cues.length; i++) {
9552         paddedOverlay.appendChild(cues[i].displayState);
9553       }
9554 
9555       return;
9556     }
9557 
9558     var boxPositions = [],
9559         containerBox = BoxPosition.getSimpleBoxPosition(paddedOverlay),
9560         fontSize = Math.round(containerBox.height * FONT_SIZE_PERCENT * 100) / 100;
9561     var styleOptions = {
9562       font: fontSize + "px " + FONT_STYLE
9563     };
9564 
9565     (function () {
9566       var styleBox, cue;
9567 
9568       for (var i = 0; i < cues.length; i++) {
9569         cue = cues[i]; // Compute the intial position and styles of the cue div.
9570 
9571         styleBox = new CueStyleBox(window, cue, styleOptions);
9572         paddedOverlay.appendChild(styleBox.div); // Move the cue div to it's correct line position.
9573 
9574         moveBoxToLinePosition(window, styleBox, containerBox, boxPositions); // Remember the computed div so that we don't have to recompute it later
9575         // if we don't have too.
9576 
9577         cue.displayState = styleBox.div;
9578         boxPositions.push(BoxPosition.getSimpleBoxPosition(styleBox));
9579       }
9580     })();
9581   };
9582 
9583   WebVTT$1.Parser = function (window, vttjs, decoder) {
9584     if (!decoder) {
9585       decoder = vttjs;
9586       vttjs = {};
9587     }
9588 
9589     if (!vttjs) {
9590       vttjs = {};
9591     }
9592 
9593     this.window = window;
9594     this.vttjs = vttjs;
9595     this.state = "INITIAL";
9596     this.buffer = "";
9597     this.decoder = decoder || new TextDecoder("utf8");
9598     this.regionList = [];
9599   };
9600 
9601   WebVTT$1.Parser.prototype = {
9602     // If the error is a ParsingError then report it to the consumer if
9603     // possible. If it's not a ParsingError then throw it like normal.
9604     reportOrThrowError: function reportOrThrowError(e) {
9605       if (e instanceof ParsingError) {
9606         this.onparsingerror && this.onparsingerror(e);
9607       } else {
9608         throw e;
9609       }
9610     },
9611     parse: function parse(data) {
9612       var self = this; // If there is no data then we won't decode it, but will just try to parse
9613       // whatever is in buffer already. This may occur in circumstances, for
9614       // example when flush() is called.
9615 
9616       if (data) {
9617         // Try to decode the data that we received.
9618         self.buffer += self.decoder.decode(data, {
9619           stream: true
9620         });
9621       }
9622 
9623       function collectNextLine() {
9624         var buffer = self.buffer;
9625         var pos = 0;
9626 
9627         while (pos < buffer.length && buffer[pos] !== '\r' && buffer[pos] !== '\n') {
9628           ++pos;
9629         }
9630 
9631         var line = buffer.substr(0, pos); // Advance the buffer early in case we fail below.
9632 
9633         if (buffer[pos] === '\r') {
9634           ++pos;
9635         }
9636 
9637         if (buffer[pos] === '\n') {
9638           ++pos;
9639         }
9640 
9641         self.buffer = buffer.substr(pos);
9642         return line;
9643       } // 3.4 WebVTT region and WebVTT region settings syntax
9644 
9645 
9646       function parseRegion(input) {
9647         var settings = new Settings();
9648         parseOptions(input, function (k, v) {
9649           switch (k) {
9650             case "id":
9651               settings.set(k, v);
9652               break;
9653 
9654             case "width":
9655               settings.percent(k, v);
9656               break;
9657 
9658             case "lines":
9659               settings.integer(k, v);
9660               break;
9661 
9662             case "regionanchor":
9663             case "viewportanchor":
9664               var xy = v.split(',');
9665 
9666               if (xy.length !== 2) {
9667                 break;
9668               } // We have to make sure both x and y parse, so use a temporary
9669               // settings object here.
9670 
9671 
9672               var anchor = new Settings();
9673               anchor.percent("x", xy[0]);
9674               anchor.percent("y", xy[1]);
9675 
9676               if (!anchor.has("x") || !anchor.has("y")) {
9677                 break;
9678               }
9679 
9680               settings.set(k + "X", anchor.get("x"));
9681               settings.set(k + "Y", anchor.get("y"));
9682               break;
9683 
9684             case "scroll":
9685               settings.alt(k, v, ["up"]);
9686               break;
9687           }
9688         }, /=/, /\s/); // Create the region, using default values for any values that were not
9689         // specified.
9690 
9691         if (settings.has("id")) {
9692           var region = new (self.vttjs.VTTRegion || self.window.VTTRegion)();
9693           region.width = settings.get("width", 100);
9694           region.lines = settings.get("lines", 3);
9695           region.regionAnchorX = settings.get("regionanchorX", 0);
9696           region.regionAnchorY = settings.get("regionanchorY", 100);
9697           region.viewportAnchorX = settings.get("viewportanchorX", 0);
9698           region.viewportAnchorY = settings.get("viewportanchorY", 100);
9699           region.scroll = settings.get("scroll", ""); // Register the region.
9700 
9701           self.onregion && self.onregion(region); // Remember the VTTRegion for later in case we parse any VTTCues that
9702           // reference it.
9703 
9704           self.regionList.push({
9705             id: settings.get("id"),
9706             region: region
9707           });
9708         }
9709       } // draft-pantos-http-live-streaming-20
9710       // https://tools.ietf.org/html/draft-pantos-http-live-streaming-20#section-3.5
9711       // 3.5 WebVTT
9712 
9713 
9714       function parseTimestampMap(input) {
9715         var settings = new Settings();
9716         parseOptions(input, function (k, v) {
9717           switch (k) {
9718             case "MPEGT":
9719               settings.integer(k + 'S', v);
9720               break;
9721 
9722             case "LOCA":
9723               settings.set(k + 'L', parseTimeStamp(v));
9724               break;
9725           }
9726         }, /[^\d]:/, /,/);
9727         self.ontimestampmap && self.ontimestampmap({
9728           "MPEGTS": settings.get("MPEGTS"),
9729           "LOCAL": settings.get("LOCAL")
9730         });
9731       } // 3.2 WebVTT metadata header syntax
9732 
9733 
9734       function parseHeader(input) {
9735         if (input.match(/X-TIMESTAMP-MAP/)) {
9736           // This line contains HLS X-TIMESTAMP-MAP metadata
9737           parseOptions(input, function (k, v) {
9738             switch (k) {
9739               case "X-TIMESTAMP-MAP":
9740                 parseTimestampMap(v);
9741                 break;
9742             }
9743           }, /=/);
9744         } else {
9745           parseOptions(input, function (k, v) {
9746             switch (k) {
9747               case "Region":
9748                 // 3.3 WebVTT region metadata header syntax
9749                 parseRegion(v);
9750                 break;
9751             }
9752           }, /:/);
9753         }
9754       } // 5.1 WebVTT file parsing.
9755 
9756 
9757       try {
9758         var line;
9759 
9760         if (self.state === "INITIAL") {
9761           // We can't start parsing until we have the first line.
9762           if (!/\r\n|\n/.test(self.buffer)) {
9763             return this;
9764           }
9765 
9766           line = collectNextLine();
9767           var m = line.match(/^WEBVTT([ \t].*)?$/);
9768 
9769           if (!m || !m[0]) {
9770             throw new ParsingError(ParsingError.Errors.BadSignature);
9771           }
9772 
9773           self.state = "HEADER";
9774         }
9775 
9776         var alreadyCollectedLine = false;
vendor: 4,922 bytes, lines 9777-9933
9777 
9778         while (self.buffer) {
9779           // We can't parse a line until we have the full line.
9780           if (!/\r\n|\n/.test(self.buffer)) {
9781             return this;
9782           }
9783 
9784           if (!alreadyCollectedLine) {
9785             line = collectNextLine();
9786           } else {
9787             alreadyCollectedLine = false;
9788           }
9789 
9790           switch (self.state) {
9791             case "HEADER":
9792               // 13-18 - Allow a header (metadata) under the WEBVTT line.
9793               if (/:/.test(line)) {
9794                 parseHeader(line);
9795               } else if (!line) {
9796                 // An empty line terminates the header and starts the body (cues).
9797                 self.state = "ID";
9798               }
9799 
9800               continue;
9801 
9802             case "NOTE":
9803               // Ignore NOTE blocks.
9804               if (!line) {
9805                 self.state = "ID";
9806               }
9807 
9808               continue;
9809 
9810             case "ID":
9811               // Check for the start of NOTE blocks.
9812               if (/^NOTE($|[ \t])/.test(line)) {
9813                 self.state = "NOTE";
9814                 break;
9815               } // 19-29 - Allow any number of line terminators, then initialize new cue values.
9816 
9817 
9818               if (!line) {
9819                 continue;
9820               }
9821 
9822               self.cue = new (self.vttjs.VTTCue || self.window.VTTCue)(0, 0, ""); // Safari still uses the old middle value and won't accept center
9823 
9824               try {
9825                 self.cue.align = "center";
9826               } catch (e) {
9827                 self.cue.align = "middle";
9828               }
9829 
9830               self.state = "CUE"; // 30-39 - Check if self line contains an optional identifier or timing data.
9831 
9832               if (line.indexOf("-->") === -1) {
9833                 self.cue.id = line;
9834                 continue;
9835               }
9836 
9837             // Process line as start of a cue.
9838 
9839             /*falls through*/
9840 
9841             case "CUE":
9842               // 40 - Collect cue timings and settings.
9843               try {
9844                 parseCue(line, self.cue, self.regionList);
9845               } catch (e) {
9846                 self.reportOrThrowError(e); // In case of an error ignore rest of the cue.
9847 
9848                 self.cue = null;
9849                 self.state = "BADCUE";
9850                 continue;
9851               }
9852 
9853               self.state = "CUETEXT";
9854               continue;
9855 
9856             case "CUETEXT":
9857               var hasSubstring = line.indexOf("-->") !== -1; // 34 - If we have an empty line then report the cue.
9858               // 35 - If we have the special substring '-->' then report the cue,
9859               // but do not collect the line as we need to process the current
9860               // one as a new cue.
9861 
9862               if (!line || hasSubstring && (alreadyCollectedLine = true)) {
9863                 // We are done parsing self cue.
9864                 self.oncue && self.oncue(self.cue);
9865                 self.cue = null;
9866                 self.state = "ID";
9867                 continue;
9868               }
9869 
9870               if (self.cue.text) {
9871                 self.cue.text += "\n";
9872               }
9873 
9874               self.cue.text += line.replace(/\u2028/g, '\n').replace(/u2029/g, '\n');
9875               continue;
9876 
9877             case "BADCUE":
9878               // BADCUE
9879               // 54-62 - Collect and discard the remaining cue.
9880               if (!line) {
9881                 self.state = "ID";
9882               }
9883 
9884               continue;
9885           }
9886         }
9887       } catch (e) {
9888         self.reportOrThrowError(e); // If we are currently parsing a cue, report what we have.
9889 
9890         if (self.state === "CUETEXT" && self.cue && self.oncue) {
9891           self.oncue(self.cue);
9892         }
9893 
9894         self.cue = null; // Enter BADWEBVTT state if header was not parsed correctly otherwise
9895         // another exception occurred so enter BADCUE state.
9896 
9897         self.state = self.state === "INITIAL" ? "BADWEBVTT" : "BADCUE";
9898       }
9899 
9900       return this;
9901     },
9902     flush: function flush() {
9903       var self = this;
9904 
9905       try {
9906         // Finish decoding the stream.
9907         self.buffer += self.decoder.decode(); // Synthesize the end of the current cue or region.
9908 
9909         if (self.cue || self.state === "HEADER") {
9910           self.buffer += "\n\n";
9911           self.parse();
9912         } // If we've flushed, parsed, and we're still on the INITIAL state then
9913         // that means we don't have enough of the stream to parse the first
9914         // line.
9915 
9916 
9917         if (self.state === "INITIAL") {
9918           throw new ParsingError(ParsingError.Errors.BadSignature);
9919         }
9920       } catch (e) {
9921         self.reportOrThrowError(e);
9922       }
9923 
9924       self.onflush && self.onflush();
9925       return this;
9926     }
9927   };
9928   var vtt = WebVTT$1;
9929 
9930   /**
9931    * Copyright 2013 vtt.js Contributors
9932    *
9933    * Licensed under the Apache License, Version 2.0 (the "License");
9934    * you may not use this file except in compliance with the License.
9935    * You may obtain a copy of the License at
9936    *
9937    *   http://www.apache.org/licenses/LICENSE-2.0
9938    *
9939    * Unless required by applicable law or agreed to in writing, software
9940    * distributed under the License is distributed on an "AS IS" BASIS,
9941    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9942    * See the License for the specific language governing permissions and
9943    * limitations under the License.
9944    */
9945   var autoKeyword = "auto";
9946   var directionSetting = {
9947     "": 1,
9948     "lr": 1,
9949     "rl": 1
9950   };
9951   var alignSetting = {
9952     "start": 1,
9953     "center": 1,
9954     "end": 1,
9955     "left": 1,
9956     "right": 1,
9957     "auto": 1,
9958     "line-left": 1,
9959     "line-right": 1
9960   };
9961 
9962   function findDirectionSetting(value) {
9963     if (typeof value !== "string") {
9964       return false;
9965     }
9966 
9967     var dir = directionSetting[value.toLowerCase()];
9968     return dir ? value.toLowerCase() : false;
9969   }
9970 
9971   function findAlignSetting(value) {
9972     if (typeof value !== "string") {
9973       return false;
9974     }
9975 
9976     var align = alignSetting[value.toLowerCase()];
9977     return align ? value.toLowerCase() : false;
9978   }
9979 
9980   function VTTCue(startTime, endTime, text) {
9981     /**
9982      * Shim implementation specific properties. These properties are not in
9983      * the spec.
9984      */
9985     // Lets us know when the VTTCue's data has changed in such a way that we need
9986     // to recompute its display state. This lets us compute its display state
9987     // lazily.
9988     this.hasBeenReset = false;
9989     /**
9990      * VTTCue and TextTrackCue properties
9991      * http://dev.w3.org/html5/webvtt/#vttcue-interface
9992      */
9993 
9994     var _id = "";
9995     var _pauseOnExit = false;
9996     var _startTime = startTime;
9997     var _endTime = endTime;
9998     var _text = text;
9999     var _region = null;
10000     var _vertical = "";
10001     var _snapToLines = true;
10002     var _line = "auto";
10003     var _lineAlign = "start";
10004     var _position = "auto";
10005     var _positionAlign = "auto";
10006     var _size = 100;
10007     var _align = "center";
10008     Object.defineProperties(this, {
10009       "id": {
10010         enumerable: true,
10011         get: function get() {
10012           return _id;
10013         },
10014         set: function set(value) {
10015           _id = "" + value;
10016         }
10017       },
10018       "pauseOnExit": {
10019         enumerable: true,
10020         get: function get() {
10021           return _pauseOnExit;
10022         },
10023         set: function set(value) {
10024           _pauseOnExit = !!value;
10025         }
10026       },
10027       "startTime": {
10028         enumerable: true,
10029         get: function get() {
10030           return _startTime;
10031         },
10032         set: function set(value) {
10033           if (typeof value !== "number") {
10034             throw new TypeError("Start time must be set to a number.");
10035           }
10036 
10037           _startTime = value;
10038           this.hasBeenReset = true;
10039         }
10040       },
10041       "endTime": {
10042         enumerable: true,
10043         get: function get() {
10044           return _endTime;
10045         },
10046         set: function set(value) {
10047           if (typeof value !== "number") {
10048             throw new TypeError("End time must be set to a number.");
10049           }
10050 
10051           _endTime = value;
10052           this.hasBeenReset = true;
10053         }
10054       },
10055       "text": {
10056         enumerable: true,
10057         get: function get() {
10058           return _text;
10059         },
10060         set: function set(value) {
10061           _text = "" + value;
10062           this.hasBeenReset = true;
10063         }
10064       },
10065       "region": {
10066         enumerable: true,
10067         get: function get() {
10068           return _region;
10069         },
10070         set: function set(value) {
10071           _region = value;
10072           this.hasBeenReset = true;
10073         }
10074       },
10075       "vertical": {
10076         enumerable: true,
10077         get: function get() {
10078           return _vertical;
10079         },
10080         set: function set(value) {
10081           var setting = findDirectionSetting(value); // Have to check for false because the setting an be an empty string.
10082 
10083           if (setting === false) {
vendor: 4,233 bytes, lines 10084-10233
10084             throw new SyntaxError("Vertical: an invalid or illegal direction string was specified.");
10085           }
10086 
10087           _vertical = setting;
10088           this.hasBeenReset = true;
10089         }
10090       },
10091       "snapToLines": {
10092         enumerable: true,
10093         get: function get() {
10094           return _snapToLines;
10095         },
10096         set: function set(value) {
10097           _snapToLines = !!value;
10098           this.hasBeenReset = true;
10099         }
10100       },
10101       "line": {
10102         enumerable: true,
10103         get: function get() {
10104           return _line;
10105         },
10106         set: function set(value) {
10107           if (typeof value !== "number" && value !== autoKeyword) {
10108             throw new SyntaxError("Line: an invalid number or illegal string was specified.");
10109           }
10110 
10111           _line = value;
10112           this.hasBeenReset = true;
10113         }
10114       },
10115       "lineAlign": {
10116         enumerable: true,
10117         get: function get() {
10118           return _lineAlign;
10119         },
10120         set: function set(value) {
10121           var setting = findAlignSetting(value);
10122 
10123           if (!setting) {
10124             console.warn("lineAlign: an invalid or illegal string was specified.");
10125           } else {
10126             _lineAlign = setting;
10127             this.hasBeenReset = true;
10128           }
10129         }
10130       },
10131       "position": {
10132         enumerable: true,
10133         get: function get() {
10134           return _position;
10135         },
10136         set: function set(value) {
10137           if (value < 0 || value > 100) {
10138             throw new Error("Position must be between 0 and 100.");
10139           }
10140 
10141           _position = value;
10142           this.hasBeenReset = true;
10143         }
10144       },
10145       "positionAlign": {
10146         enumerable: true,
10147         get: function get() {
10148           return _positionAlign;
10149         },
10150         set: function set(value) {
10151           var setting = findAlignSetting(value);
10152 
10153           if (!setting) {
10154             console.warn("positionAlign: an invalid or illegal string was specified.");
10155           } else {
10156             _positionAlign = setting;
10157             this.hasBeenReset = true;
10158           }
10159         }
10160       },
10161       "size": {
10162         enumerable: true,
10163         get: function get() {
10164           return _size;
10165         },
10166         set: function set(value) {
10167           if (value < 0 || value > 100) {
10168             throw new Error("Size must be between 0 and 100.");
10169           }
10170 
10171           _size = value;
10172           this.hasBeenReset = true;
10173         }
10174       },
10175       "align": {
10176         enumerable: true,
10177         get: function get() {
10178           return _align;
10179         },
10180         set: function set(value) {
10181           var setting = findAlignSetting(value);
10182 
10183           if (!setting) {
10184             throw new SyntaxError("align: an invalid or illegal alignment string was specified.");
10185           }
10186 
10187           _align = setting;
10188           this.hasBeenReset = true;
10189         }
10190       }
10191     });
10192     /**
10193      * Other <track> spec defined properties
10194      */
10195     // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#text-track-cue-display-state
10196 
10197     this.displayState = undefined;
10198   }
10199   /**
10200    * VTTCue methods
10201    */
10202 
10203 
10204   VTTCue.prototype.getCueAsHTML = function () {
10205     // Assume WebVTT.convertCueToDOMTree is on the global.
10206     return WebVTT.convertCueToDOMTree(window, this.text);
10207   };
10208 
10209   var vttcue = VTTCue;
10210 
10211   /**
10212    * Copyright 2013 vtt.js Contributors
10213    *
10214    * Licensed under the Apache License, Version 2.0 (the "License");
10215    * you may not use this file except in compliance with the License.
10216    * You may obtain a copy of the License at
10217    *
10218    *   http://www.apache.org/licenses/LICENSE-2.0
10219    *
10220    * Unless required by applicable law or agreed to in writing, software
10221    * distributed under the License is distributed on an "AS IS" BASIS,
10222    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10223    * See the License for the specific language governing permissions and
10224    * limitations under the License.
10225    */
10226   var scrollSetting = {
10227     "": true,
10228     "up": true
10229   };
10230 
10231   function findScrollSetting(value) {
10232     if (typeof value !== "string") {
10233       return false;
10234     }
10235 
10236     var scroll = scrollSetting[value.toLowerCase()];
10237     return scroll ? value.toLowerCase() : false;
10238   }
10239 
10240   function isValidPercentValue(value) {
10241     return typeof value === "number" && value >= 0 && value <= 100;
10242   } // VTTRegion shim http://dev.w3.org/html5/webvtt/#vttregion-interface
10243 
10244 
10245   function VTTRegion() {
10246     var _width = 100;
10247     var _lines = 3;
10248     var _regionAnchorX = 0;
10249     var _regionAnchorY = 100;
10250     var _viewportAnchorX = 0;
10251     var _viewportAnchorY = 100;
10252     var _scroll = "";
10253     Object.defineProperties(this, {
10254       "width": {
10255         enumerable: true,
10256         get: function get() {
10257           return _width;
10258         },
10259         set: function set(value) {
10260           if (!isValidPercentValue(value)) {
10261             throw new Error("Width must be between 0 and 100.");
10262           }
10263 
10264           _width = value;
10265         }
10266       },
10267       "lines": {
10268         enumerable: true,
10269         get: function get() {
10270           return _lines;
10271         },
10272         set: function set(value) {
10273           if (typeof value !== "number") {
10274             throw new TypeError("Lines must be set to a number.");
10275           }
10276 
10277           _lines = value;
10278         }
10279       },
10280       "regionAnchorY": {
10281         enumerable: true,
10282         get: function get() {
10283           return _regionAnchorY;
10284         },
10285         set: function set(value) {
10286           if (!isValidPercentValue(value)) {
10287             throw new Error("RegionAnchorX must be between 0 and 100.");
10288           }
10289 
10290           _regionAnchorY = value;
10291         }
10292       },
10293       "regionAnchorX": {
10294         enumerable: true,
10295         get: function get() {
10296           return _regionAnchorX;
10297         },
10298         set: function set(value) {
10299           if (!isValidPercentValue(value)) {
10300             throw new Error("RegionAnchorY must be between 0 and 100.");
10301           }
10302 
10303           _regionAnchorX = value;
10304         }
10305       },
10306       "viewportAnchorY": {
10307         enumerable: true,
10308         get: function get() {
10309           return _viewportAnchorY;
10310         },
10311         set: function set(value) {
10312           if (!isValidPercentValue(value)) {
10313             throw new Error("ViewportAnchorY must be between 0 and 100.");
10314           }
10315 
10316           _viewportAnchorY = value;
10317         }
10318       },
10319       "viewportAnchorX": {
10320         enumerable: true,
10321         get: function get() {
10322           return _viewportAnchorX;
10323         },
10324         set: function set(value) {
10325           if (!isValidPercentValue(value)) {
10326             throw new Error("ViewportAnchorX must be between 0 and 100.");
10327           }
10328 
10329           _viewportAnchorX = value;
10330         }
10331       },
10332       "scroll": {
10333         enumerable: true,
10334         get: function get() {
10335           return _scroll;
10336         },
10337         set: function set(value) {
10338           var setting = findScrollSetting(value); // Have to check for false as an empty string is a legal value.
10339 
10340           if (setting === false) {
10341             console.warn("Scroll: an invalid or illegal string was specified.");
10342           } else {
10343             _scroll = setting;
10344           }
10345         }
10346       }
10347     });
10348   }
10349 
10350   var vttregion = VTTRegion;
10351 
10352   var browserIndex = createCommonjsModule(function (module) {
10353     /**
10354      * Copyright 2013 vtt.js Contributors
10355      *
10356      * Licensed under the Apache License, Version 2.0 (the "License");
10357      * you may not use this file except in compliance with the License.
10358      * You may obtain a copy of the License at
10359      *
10360      *   http://www.apache.org/licenses/LICENSE-2.0
10361      *
10362      * Unless required by applicable law or agreed to in writing, software
10363      * distributed under the License is distributed on an "AS IS" BASIS,
10364      * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10365      * See the License for the specific language governing permissions and
10366      * limitations under the License.
10367      */
10368     // Default exports for Node. Export the extended versions of VTTCue and
10369     // VTTRegion in Node since we likely want the capability to convert back and
10370     // forth between JSON. If we don't then it's not that big of a deal since we're
10371     // off browser.
10372     var vttjs = module.exports = {
10373       WebVTT: vtt,
10374       VTTCue: vttcue,
10375       VTTRegion: vttregion
10376     };
10377     window$1.vttjs = vttjs;
10378     window$1.WebVTT = vttjs.WebVTT;
10379     var cueShim = vttjs.VTTCue;
10380     var regionShim = vttjs.VTTRegion;
10381     var nativeVTTCue = window$1.VTTCue;
10382     var nativeVTTRegion = window$1.VTTRegion;
10383 
10384     vttjs.shim = function () {
10385       window$1.VTTCue = cueShim;
10386       window$1.VTTRegion = regionShim;
10387     };
10388 
10389     vttjs.restore = function () {
10390       window$1.VTTCue = nativeVTTCue;
10391       window$1.VTTRegion = nativeVTTRegion;
10392     };
10393 
10394     if (!window$1.VTTCue) {
10395       vttjs.shim();
10396     }
10397   });
10398   var browserIndex_1 = browserIndex.WebVTT;
10399   var browserIndex_2 = browserIndex.VTTCue;
10400   var browserIndex_3 = browserIndex.VTTRegion;
10401 
10402   /**
10403    * An Object containing a structure like: `{src: 'url', type: 'mimetype'}` or string
10404    * that just contains the src url alone.
10405    * * `var SourceObject = {src: 'http://ex.com/video.mp4', type: 'video/mp4'};`
10406      * `var SourceString = 'http://example.com/some-video.mp4';`
10407    *
10408    * @typedef {Object|string} Tech~SourceObject
10409    *
10410    * @property {string} src
10411    *           The url to the source
10412    *
10413    * @property {string} type
10414    *           The mime type of the source
10415    */
10416 
10417   /**
10418    * A function used by {@link Tech} to create a new {@link TextTrack}.
10419    *
10420    * @private
10421    *
10422    * @param {Tech} self
10423    *        An instance of the Tech class.
10424    *
10425    * @param {string} kind
10426    *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata)
10427    *
10428    * @param {string} [label]
10429    *        Label to identify the text track
10430    *
10431    * @param {string} [language]
10432    *        Two letter language abbreviation
10433    *
10434    * @param {Object} [options={}]
10435    *        An object with additional text track options
10436    *
10437    * @return {TextTrack}
10438    *          The text track that was created.
10439    */
10440 
10441   function createTrackHelper(self, kind, label, language, options) {
10442     if (options === void 0) {
10443       options = {};
10444     }
10445 
10446     var tracks = self.textTracks();
10447     options.kind = kind;
10448 
10449     if (label) {
10450       options.label = label;
10451     }
10452 
10453     if (language) {
10454       options.language = language;
10455     }
10456 
10457     options.tech = self;
10458     var track = new ALL.text.TrackClass(options);
10459     tracks.addTrack(track);
10460     return track;
10461   }
10462   /**
10463    * This is the base class for media playback technology controllers, such as
10464    * {@link Flash} and {@link HTML5}
10465    *
10466    * @extends Component
10467    */
10468 
10469 
10470   var Tech =
10471   /*#__PURE__*/
10472   function (_Component) {
10473     inheritsLoose(Tech, _Component);
10474 
10475     /**
10476     * Create an instance of this Tech.
10477     *
10478     * @param {Object} [options]
10479     *        The key/value store of player options.
10480     *
10481     * @param {Component~ReadyCallback} ready
10482     *        Callback function to call when the `HTML5` Tech is ready.
10483     */
10484     function Tech(options, ready) {
10485       var _this;
10486 
10487       if (options === void 0) {
10488         options = {};
10489       }
10490 
10491       if (ready === void 0) {
10492         ready = function ready() {};
10493       }
10494 
10495       // we don't want the tech to report user activity automatically.
vendor: 38,994 bytes, lines 10496-11793
10496       // This is done manually in addControlsListeners
10497       options.reportTouchActivity = false;
10498       _this = _Component.call(this, null, options, ready) || this; // keep track of whether the current source has played at all to
10499       // implement a very limited played()
10500 
10501       _this.hasStarted_ = false;
10502 
10503       _this.on('playing', function () {
10504         this.hasStarted_ = true;
10505       });
10506 
10507       _this.on('loadstart', function () {
10508         this.hasStarted_ = false;
10509       });
10510 
10511       ALL.names.forEach(function (name) {
10512         var props = ALL[name];
10513 
10514         if (options && options[props.getterName]) {
10515           _this[props.privateName] = options[props.getterName];
10516         }
10517       }); // Manually track progress in cases where the browser/flash player doesn't report it.
10518 
10519       if (!_this.featuresProgressEvents) {
10520         _this.manualProgressOn();
10521       } // Manually track timeupdates in cases where the browser/flash player doesn't report it.
10522 
10523 
10524       if (!_this.featuresTimeupdateEvents) {
10525         _this.manualTimeUpdatesOn();
10526       }
10527 
10528       ['Text', 'Audio', 'Video'].forEach(function (track) {
10529         if (options["native" + track + "Tracks"] === false) {
10530           _this["featuresNative" + track + "Tracks"] = false;
10531         }
10532       });
10533 
10534       if (options.nativeCaptions === false || options.nativeTextTracks === false) {
10535         _this.featuresNativeTextTracks = false;
10536       } else if (options.nativeCaptions === true || options.nativeTextTracks === true) {
10537         _this.featuresNativeTextTracks = true;
10538       }
10539 
10540       if (!_this.featuresNativeTextTracks) {
10541         _this.emulateTextTracks();
10542       }
10543 
10544       _this.preloadTextTracks = options.preloadTextTracks !== false;
10545       _this.autoRemoteTextTracks_ = new ALL.text.ListClass();
10546 
10547       _this.initTrackListeners(); // Turn on component tap events only if not using native controls
10548 
10549 
10550       if (!options.nativeControlsForTouch) {
10551         _this.emitTapEvents();
10552       }
10553 
10554       if (_this.constructor) {
10555         _this.name_ = _this.constructor.name || 'Unknown Tech';
10556       }
10557 
10558       return _this;
10559     }
10560     /**
10561      * A special function to trigger source set in a way that will allow player
10562      * to re-trigger if the player or tech are not ready yet.
10563      *
10564      * @fires Tech#sourceset
10565      * @param {string} src The source string at the time of the source changing.
10566      */
10567 
10568 
10569     var _proto = Tech.prototype;
10570 
10571     _proto.triggerSourceset = function triggerSourceset(src) {
10572       var _this2 = this;
10573 
10574       if (!this.isReady_) {
10575         // on initial ready we have to trigger source set
10576         // 1ms after ready so that player can watch for it.
10577         this.one('ready', function () {
10578           return _this2.setTimeout(function () {
10579             return _this2.triggerSourceset(src);
10580           }, 1);
10581         });
10582       }
10583       /**
10584        * Fired when the source is set on the tech causing the media element
10585        * to reload.
10586        *
10587        * @see {@link Player#event:sourceset}
10588        * @event Tech#sourceset
10589        * @type {EventTarget~Event}
10590        */
10591 
10592 
10593       this.trigger({
10594         src: src,
10595         type: 'sourceset'
10596       });
10597     }
10598     /* Fallbacks for unsupported event types
10599     ================================================================================ */
10600 
10601     /**
10602      * Polyfill the `progress` event for browsers that don't support it natively.
10603      *
10604      * @see {@link Tech#trackProgress}
10605      */
10606     ;
10607 
10608     _proto.manualProgressOn = function manualProgressOn() {
10609       this.on('durationchange', this.onDurationChange);
10610       this.manualProgress = true; // Trigger progress watching when a source begins loading
10611 
10612       this.one('ready', this.trackProgress);
10613     }
10614     /**
10615      * Turn off the polyfill for `progress` events that was created in
10616      * {@link Tech#manualProgressOn}
10617      */
10618     ;
10619 
10620     _proto.manualProgressOff = function manualProgressOff() {
10621       this.manualProgress = false;
10622       this.stopTrackingProgress();
10623       this.off('durationchange', this.onDurationChange);
10624     }
10625     /**
10626      * This is used to trigger a `progress` event when the buffered percent changes. It
10627      * sets an interval function that will be called every 500 milliseconds to check if the
10628      * buffer end percent has changed.
10629      *
10630      * > This function is called by {@link Tech#manualProgressOn}
10631      *
10632      * @param {EventTarget~Event} event
10633      *        The `ready` event that caused this to run.
10634      *
10635      * @listens Tech#ready
10636      * @fires Tech#progress
10637      */
10638     ;
10639 
10640     _proto.trackProgress = function trackProgress(event) {
10641       this.stopTrackingProgress();
10642       this.progressInterval = this.setInterval(bind(this, function () {
10643         // Don't trigger unless buffered amount is greater than last time
10644         var numBufferedPercent = this.bufferedPercent();
10645 
10646         if (this.bufferedPercent_ !== numBufferedPercent) {
10647           /**
10648            * See {@link Player#progress}
10649            *
10650            * @event Tech#progress
10651            * @type {EventTarget~Event}
10652            */
10653           this.trigger('progress');
10654         }
10655 
10656         this.bufferedPercent_ = numBufferedPercent;
10657 
10658         if (numBufferedPercent === 1) {
10659           this.stopTrackingProgress();
10660         }
10661       }), 500);
10662     }
10663     /**
10664      * Update our internal duration on a `durationchange` event by calling
10665      * {@link Tech#duration}.
10666      *
10667      * @param {EventTarget~Event} event
10668      *        The `durationchange` event that caused this to run.
10669      *
10670      * @listens Tech#durationchange
10671      */
10672     ;
10673 
10674     _proto.onDurationChange = function onDurationChange(event) {
10675       this.duration_ = this.duration();
10676     }
10677     /**
10678      * Get and create a `TimeRange` object for buffering.
10679      *
10680      * @return {TimeRange}
10681      *         The time range object that was created.
10682      */
10683     ;
10684 
10685     _proto.buffered = function buffered() {
10686       return createTimeRanges(0, 0);
10687     }
10688     /**
10689      * Get the percentage of the current video that is currently buffered.
10690      *
10691      * @return {number}
10692      *         A number from 0 to 1 that represents the decimal percentage of the
10693      *         video that is buffered.
10694      *
10695      */
10696     ;
10697 
10698     _proto.bufferedPercent = function bufferedPercent$1() {
10699       return bufferedPercent(this.buffered(), this.duration_);
10700     }
10701     /**
10702      * Turn off the polyfill for `progress` events that was created in
10703      * {@link Tech#manualProgressOn}
10704      * Stop manually tracking progress events by clearing the interval that was set in
10705      * {@link Tech#trackProgress}.
10706      */
10707     ;
10708 
10709     _proto.stopTrackingProgress = function stopTrackingProgress() {
10710       this.clearInterval(this.progressInterval);
10711     }
10712     /**
10713      * Polyfill the `timeupdate` event for browsers that don't support it.
10714      *
10715      * @see {@link Tech#trackCurrentTime}
10716      */
10717     ;
10718 
10719     _proto.manualTimeUpdatesOn = function manualTimeUpdatesOn() {
10720       this.manualTimeUpdates = true;
10721       this.on('play', this.trackCurrentTime);
10722       this.on('pause', this.stopTrackingCurrentTime);
10723     }
10724     /**
10725      * Turn off the polyfill for `timeupdate` events that was created in
10726      * {@link Tech#manualTimeUpdatesOn}
10727      */
10728     ;
10729 
10730     _proto.manualTimeUpdatesOff = function manualTimeUpdatesOff() {
10731       this.manualTimeUpdates = false;
10732       this.stopTrackingCurrentTime();
10733       this.off('play', this.trackCurrentTime);
10734       this.off('pause', this.stopTrackingCurrentTime);
10735     }
10736     /**
10737      * Sets up an interval function to track current time and trigger `timeupdate` every
10738      * 250 milliseconds.
10739      *
10740      * @listens Tech#play
10741      * @triggers Tech#timeupdate
10742      */
10743     ;
10744 
10745     _proto.trackCurrentTime = function trackCurrentTime() {
10746       if (this.currentTimeInterval) {
10747         this.stopTrackingCurrentTime();
10748       }
10749 
10750       this.currentTimeInterval = this.setInterval(function () {
10751         /**
10752          * Triggered at an interval of 250ms to indicated that time is passing in the video.
10753          *
10754          * @event Tech#timeupdate
10755          * @type {EventTarget~Event}
10756          */
10757         this.trigger({
10758           type: 'timeupdate',
10759           target: this,
10760           manuallyTriggered: true
10761         }); // 42 = 24 fps // 250 is what Webkit uses // FF uses 15
10762       }, 250);
10763     }
10764     /**
10765      * Stop the interval function created in {@link Tech#trackCurrentTime} so that the
10766      * `timeupdate` event is no longer triggered.
10767      *
10768      * @listens {Tech#pause}
10769      */
10770     ;
10771 
10772     _proto.stopTrackingCurrentTime = function stopTrackingCurrentTime() {
10773       this.clearInterval(this.currentTimeInterval); // #1002 - if the video ends right before the next timeupdate would happen,
10774       // the progress bar won't make it all the way to the end
10775 
10776       this.trigger({
10777         type: 'timeupdate',
10778         target: this,
10779         manuallyTriggered: true
10780       });
10781     }
10782     /**
10783      * Turn off all event polyfills, clear the `Tech`s {@link AudioTrackList},
10784      * {@link VideoTrackList}, and {@link TextTrackList}, and dispose of this Tech.
10785      *
10786      * @fires Component#dispose
10787      */
10788     ;
10789 
10790     _proto.dispose = function dispose() {
10791       // clear out all tracks because we can't reuse them between techs
10792       this.clearTracks(NORMAL.names); // Turn off any manual progress or timeupdate tracking
10793 
10794       if (this.manualProgress) {
10795         this.manualProgressOff();
10796       }
10797 
10798       if (this.manualTimeUpdates) {
10799         this.manualTimeUpdatesOff();
10800       }
10801 
10802       _Component.prototype.dispose.call(this);
10803     }
10804     /**
10805      * Clear out a single `TrackList` or an array of `TrackLists` given their names.
10806      *
10807      * > Note: Techs without source handlers should call this between sources for `video`
10808      *         & `audio` tracks. You don't want to use them between tracks!
10809      *
10810      * @param {string[]|string} types
10811      *        TrackList names to clear, valid names are `video`, `audio`, and
10812      *        `text`.
10813      */
10814     ;
10815 
10816     _proto.clearTracks = function clearTracks(types) {
10817       var _this3 = this;
10818 
10819       types = [].concat(types); // clear out all tracks because we can't reuse them between techs
10820 
10821       types.forEach(function (type) {
10822         var list = _this3[type + "Tracks"]() || [];
10823         var i = list.length;
10824 
10825         while (i--) {
10826           var track = list[i];
10827 
10828           if (type === 'text') {
10829             _this3.removeRemoteTextTrack(track);
10830           }
10831 
10832           list.removeTrack(track);
10833         }
10834       });
10835     }
10836     /**
10837      * Remove any TextTracks added via addRemoteTextTrack that are
10838      * flagged for automatic garbage collection
10839      */
10840     ;
10841 
10842     _proto.cleanupAutoTextTracks = function cleanupAutoTextTracks() {
10843       var list = this.autoRemoteTextTracks_ || [];
10844       var i = list.length;
10845 
10846       while (i--) {
10847         var track = list[i];
10848         this.removeRemoteTextTrack(track);
10849       }
10850     }
10851     /**
10852      * Reset the tech, which will removes all sources and reset the internal readyState.
10853      *
10854      * @abstract
10855      */
10856     ;
10857 
10858     _proto.reset = function reset() {}
10859     /**
10860      * Get or set an error on the Tech.
10861      *
10862      * @param {MediaError} [err]
10863      *        Error to set on the Tech
10864      *
10865      * @return {MediaError|null}
10866      *         The current error object on the tech, or null if there isn't one.
10867      */
10868     ;
10869 
10870     _proto.error = function error(err) {
10871       if (err !== undefined) {
10872         this.error_ = new MediaError(err);
10873         this.trigger('error');
10874       }
10875 
10876       return this.error_;
10877     }
10878     /**
10879      * Returns the `TimeRange`s that have been played through for the current source.
10880      *
10881      * > NOTE: This implementation is incomplete. It does not track the played `TimeRange`.
10882      *         It only checks whether the source has played at all or not.
10883      *
10884      * @return {TimeRange}
10885      *         - A single time range if this video has played
10886      *         - An empty set of ranges if not.
10887      */
10888     ;
10889 
10890     _proto.played = function played() {
10891       if (this.hasStarted_) {
10892         return createTimeRanges(0, 0);
10893       }
10894 
10895       return createTimeRanges();
10896     }
10897     /**
10898      * Causes a manual time update to occur if {@link Tech#manualTimeUpdatesOn} was
10899      * previously called.
10900      *
10901      * @fires Tech#timeupdate
10902      */
10903     ;
10904 
10905     _proto.setCurrentTime = function setCurrentTime() {
10906       // improve the accuracy of manual timeupdates
10907       if (this.manualTimeUpdates) {
10908         /**
10909          * A manual `timeupdate` event.
10910          *
10911          * @event Tech#timeupdate
10912          * @type {EventTarget~Event}
10913          */
10914         this.trigger({
10915           type: 'timeupdate',
10916           target: this,
10917           manuallyTriggered: true
10918         });
10919       }
10920     }
10921     /**
10922      * Turn on listeners for {@link VideoTrackList}, {@link {AudioTrackList}, and
10923      * {@link TextTrackList} events.
10924      *
10925      * This adds {@link EventTarget~EventListeners} for `addtrack`, and  `removetrack`.
10926      *
10927      * @fires Tech#audiotrackchange
10928      * @fires Tech#videotrackchange
10929      * @fires Tech#texttrackchange
10930      */
10931     ;
10932 
10933     _proto.initTrackListeners = function initTrackListeners() {
10934       var _this4 = this;
10935 
10936       /**
10937         * Triggered when tracks are added or removed on the Tech {@link AudioTrackList}
10938         *
10939         * @event Tech#audiotrackchange
10940         * @type {EventTarget~Event}
10941         */
10942 
10943       /**
10944         * Triggered when tracks are added or removed on the Tech {@link VideoTrackList}
10945         *
10946         * @event Tech#videotrackchange
10947         * @type {EventTarget~Event}
10948         */
10949 
10950       /**
10951         * Triggered when tracks are added or removed on the Tech {@link TextTrackList}
10952         *
10953         * @event Tech#texttrackchange
10954         * @type {EventTarget~Event}
10955         */
10956       NORMAL.names.forEach(function (name) {
10957         var props = NORMAL[name];
10958 
10959         var trackListChanges = function trackListChanges() {
10960           _this4.trigger(name + "trackchange");
10961         };
10962 
10963         var tracks = _this4[props.getterName]();
10964 
10965         tracks.addEventListener('removetrack', trackListChanges);
10966         tracks.addEventListener('addtrack', trackListChanges);
10967 
10968         _this4.on('dispose', function () {
10969           tracks.removeEventListener('removetrack', trackListChanges);
10970           tracks.removeEventListener('addtrack', trackListChanges);
10971         });
10972       });
10973     }
10974     /**
10975      * Emulate TextTracks using vtt.js if necessary
10976      *
10977      * @fires Tech#vttjsloaded
10978      * @fires Tech#vttjserror
10979      */
10980     ;
10981 
10982     _proto.addWebVttScript_ = function addWebVttScript_() {
10983       var _this5 = this;
10984 
10985       if (window$1.WebVTT) {
10986         return;
10987       } // Initially, Tech.el_ is a child of a dummy-div wait until the Component system
10988       // signals that the Tech is ready at which point Tech.el_ is part of the DOM
10989       // before inserting the WebVTT script
10990 
10991 
10992       if (document.body.contains(this.el())) {
10993         // load via require if available and vtt.js script location was not passed in
10994         // as an option. novtt builds will turn the above require call into an empty object
10995         // which will cause this if check to always fail.
10996         if (!this.options_['vtt.js'] && isPlain(browserIndex) && Object.keys(browserIndex).length > 0) {
10997           this.trigger('vttjsloaded');
10998           return;
10999         } // load vtt.js via the script location option or the cdn of no location was
11000         // passed in
11001 
11002 
11003         var script = document.createElement('script');
11004         script.src = this.options_['vtt.js'] || 'https://vjs.zencdn.net/vttjs/0.14.1/vtt.min.js';
11005 
11006         script.onload = function () {
11007           /**
11008            * Fired when vtt.js is loaded.
11009            *
11010            * @event Tech#vttjsloaded
11011            * @type {EventTarget~Event}
11012            */
11013           _this5.trigger('vttjsloaded');
11014         };
11015 
11016         script.onerror = function () {
11017           /**
11018            * Fired when vtt.js was not loaded due to an error
11019            *
11020            * @event Tech#vttjsloaded
11021            * @type {EventTarget~Event}
11022            */
11023           _this5.trigger('vttjserror');
11024         };
11025 
11026         this.on('dispose', function () {
11027           script.onload = null;
11028           script.onerror = null;
11029         }); // but have not loaded yet and we set it to true before the inject so that
11030         // we don't overwrite the injected window.WebVTT if it loads right away
11031 
11032         window$1.WebVTT = true;
11033         this.el().parentNode.appendChild(script);
11034       } else {
11035         this.ready(this.addWebVttScript_);
11036       }
11037     }
11038     /**
11039      * Emulate texttracks
11040      *
11041      */
11042     ;
11043 
11044     _proto.emulateTextTracks = function emulateTextTracks() {
11045       var _this6 = this;
11046 
11047       var tracks = this.textTracks();
11048       var remoteTracks = this.remoteTextTracks();
11049 
11050       var handleAddTrack = function handleAddTrack(e) {
11051         return tracks.addTrack(e.track);
11052       };
11053 
11054       var handleRemoveTrack = function handleRemoveTrack(e) {
11055         return tracks.removeTrack(e.track);
11056       };
11057 
11058       remoteTracks.on('addtrack', handleAddTrack);
11059       remoteTracks.on('removetrack', handleRemoveTrack);
11060       this.addWebVttScript_();
11061 
11062       var updateDisplay = function updateDisplay() {
11063         return _this6.trigger('texttrackchange');
11064       };
11065 
11066       var textTracksChanges = function textTracksChanges() {
11067         updateDisplay();
11068 
11069         for (var i = 0; i < tracks.length; i++) {
11070           var track = tracks[i];
11071           track.removeEventListener('cuechange', updateDisplay);
11072 
11073           if (track.mode === 'showing') {
11074             track.addEventListener('cuechange', updateDisplay);
11075           }
11076         }
11077       };
11078 
11079       textTracksChanges();
11080       tracks.addEventListener('change', textTracksChanges);
11081       tracks.addEventListener('addtrack', textTracksChanges);
11082       tracks.addEventListener('removetrack', textTracksChanges);
11083       this.on('dispose', function () {
11084         remoteTracks.off('addtrack', handleAddTrack);
11085         remoteTracks.off('removetrack', handleRemoveTrack);
11086         tracks.removeEventListener('change', textTracksChanges);
11087         tracks.removeEventListener('addtrack', textTracksChanges);
11088         tracks.removeEventListener('removetrack', textTracksChanges);
11089 
11090         for (var i = 0; i < tracks.length; i++) {
11091           var track = tracks[i];
11092           track.removeEventListener('cuechange', updateDisplay);
11093         }
11094       });
11095     }
11096     /**
11097      * Create and returns a remote {@link TextTrack} object.
11098      *
11099      * @param {string} kind
11100      *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata)
11101      *
11102      * @param {string} [label]
11103      *        Label to identify the text track
11104      *
11105      * @param {string} [language]
11106      *        Two letter language abbreviation
11107      *
11108      * @return {TextTrack}
11109      *         The TextTrack that gets created.
11110      */
11111     ;
11112 
11113     _proto.addTextTrack = function addTextTrack(kind, label, language) {
11114       if (!kind) {
11115         throw new Error('TextTrack kind is required but was not provided');
11116       }
11117 
11118       return createTrackHelper(this, kind, label, language);
11119     }
11120     /**
11121      * Create an emulated TextTrack for use by addRemoteTextTrack
11122      *
11123      * This is intended to be overridden by classes that inherit from
11124      * Tech in order to create native or custom TextTracks.
11125      *
11126      * @param {Object} options
11127      *        The object should contain the options to initialize the TextTrack with.
11128      *
11129      * @param {string} [options.kind]
11130      *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata).
11131      *
11132      * @param {string} [options.label].
11133      *        Label to identify the text track
11134      *
11135      * @param {string} [options.language]
11136      *        Two letter language abbreviation.
11137      *
11138      * @return {HTMLTrackElement}
11139      *         The track element that gets created.
11140      */
11141     ;
11142 
11143     _proto.createRemoteTextTrack = function createRemoteTextTrack(options) {
11144       var track = mergeOptions(options, {
11145         tech: this
11146       });
11147       return new REMOTE.remoteTextEl.TrackClass(track);
11148     }
11149     /**
11150      * Creates a remote text track object and returns an html track element.
11151      *
11152      * > Note: This can be an emulated {@link HTMLTrackElement} or a native one.
11153      *
11154      * @param {Object} options
11155      *        See {@link Tech#createRemoteTextTrack} for more detailed properties.
11156      *
11157      * @param {boolean} [manualCleanup=true]
11158      *        - When false: the TextTrack will be automatically removed from the video
11159      *          element whenever the source changes
11160      *        - When True: The TextTrack will have to be cleaned up manually
11161      *
11162      * @return {HTMLTrackElement}
11163      *         An Html Track Element.
11164      *
11165      * @deprecated The default functionality for this function will be equivalent
11166      *             to "manualCleanup=false" in the future. The manualCleanup parameter will
11167      *             also be removed.
11168      */
11169     ;
11170 
11171     _proto.addRemoteTextTrack = function addRemoteTextTrack(options, manualCleanup) {
11172       var _this7 = this;
11173 
11174       if (options === void 0) {
11175         options = {};
11176       }
11177 
11178       var htmlTrackElement = this.createRemoteTextTrack(options);
11179 
11180       if (manualCleanup !== true && manualCleanup !== false) {
11181         // deprecation warning
11182         log.warn('Calling addRemoteTextTrack without explicitly setting the "manualCleanup" parameter to `true` is deprecated and default to `false` in future version of video.js');
11183         manualCleanup = true;
11184       } // store HTMLTrackElement and TextTrack to remote list
11185 
11186 
11187       this.remoteTextTrackEls().addTrackElement_(htmlTrackElement);
11188       this.remoteTextTracks().addTrack(htmlTrackElement.track);
11189 
11190       if (manualCleanup !== true) {
11191         // create the TextTrackList if it doesn't exist
11192         this.ready(function () {
11193           return _this7.autoRemoteTextTracks_.addTrack(htmlTrackElement.track);
11194         });
11195       }
11196 
11197       return htmlTrackElement;
11198     }
11199     /**
11200      * Remove a remote text track from the remote `TextTrackList`.
11201      *
11202      * @param {TextTrack} track
11203      *        `TextTrack` to remove from the `TextTrackList`
11204      */
11205     ;
11206 
11207     _proto.removeRemoteTextTrack = function removeRemoteTextTrack(track) {
11208       var trackElement = this.remoteTextTrackEls().getTrackElementByTrack_(track); // remove HTMLTrackElement and TextTrack from remote list
11209 
11210       this.remoteTextTrackEls().removeTrackElement_(trackElement);
11211       this.remoteTextTracks().removeTrack(track);
11212       this.autoRemoteTextTracks_.removeTrack(track);
11213     }
11214     /**
11215      * Gets available media playback quality metrics as specified by the W3C's Media
11216      * Playback Quality API.
11217      *
11218      * @see [Spec]{@link https://wicg.github.io/media-playback-quality}
11219      *
11220      * @return {Object}
11221      *         An object with supported media playback quality metrics
11222      *
11223      * @abstract
11224      */
11225     ;
11226 
11227     _proto.getVideoPlaybackQuality = function getVideoPlaybackQuality() {
11228       return {};
11229     }
11230     /**
11231      * Attempt to create a floating video window always on top of other windows
11232      * so that users may continue consuming media while they interact with other
11233      * content sites, or applications on their device.
11234      *
11235      * @see [Spec]{@link https://wicg.github.io/picture-in-picture}
11236      *
11237      * @return {Promise|undefined}
11238      *         A promise with a Picture-in-Picture window if the browser supports
11239      *         Promises (or one was passed in as an option). It returns undefined
11240      *         otherwise.
11241      *
11242      * @abstract
11243      */
11244     ;
11245 
11246     _proto.requestPictureInPicture = function requestPictureInPicture() {
11247       var PromiseClass = this.options_.Promise || window$1.Promise;
11248 
11249       if (PromiseClass) {
11250         return PromiseClass.reject();
11251       }
11252     }
11253     /**
11254      * A method to set a poster from a `Tech`.
11255      *
11256      * @abstract
11257      */
11258     ;
11259 
11260     _proto.setPoster = function setPoster() {}
11261     /**
11262      * A method to check for the presence of the 'playsinline' <video> attribute.
11263      *
11264      * @abstract
11265      */
11266     ;
11267 
11268     _proto.playsinline = function playsinline() {}
11269     /**
11270      * A method to set or unset the 'playsinline' <video> attribute.
11271      *
11272      * @abstract
11273      */
11274     ;
11275 
11276     _proto.setPlaysinline = function setPlaysinline() {}
11277     /**
11278      * Attempt to force override of native audio tracks.
11279      *
11280      * @param {boolean} override - If set to true native audio will be overridden,
11281      * otherwise native audio will potentially be used.
11282      *
11283      * @abstract
11284      */
11285     ;
11286 
11287     _proto.overrideNativeAudioTracks = function overrideNativeAudioTracks() {}
11288     /**
11289      * Attempt to force override of native video tracks.
11290      *
11291      * @param {boolean} override - If set to true native video will be overridden,
11292      * otherwise native video will potentially be used.
11293      *
11294      * @abstract
11295      */
11296     ;
11297 
11298     _proto.overrideNativeVideoTracks = function overrideNativeVideoTracks() {}
11299     /*
11300      * Check if the tech can support the given mime-type.
11301      *
11302      * The base tech does not support any type, but source handlers might
11303      * overwrite this.
11304      *
11305      * @param  {string} type
11306      *         The mimetype to check for support
11307      *
11308      * @return {string}
11309      *         'probably', 'maybe', or empty string
11310      *
11311      * @see [Spec]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canPlayType}
11312      *
11313      * @abstract
11314      */
11315     ;
11316 
11317     _proto.canPlayType = function canPlayType() {
11318       return '';
11319     }
11320     /**
11321      * Check if the type is supported by this tech.
11322      *
11323      * The base tech does not support any type, but source handlers might
11324      * overwrite this.
11325      *
11326      * @param {string} type
11327      *        The media type to check
11328      * @return {string} Returns the native video element's response
11329      */
11330     ;
11331 
11332     Tech.canPlayType = function canPlayType() {
11333       return '';
11334     }
11335     /**
11336      * Check if the tech can support the given source
11337      *
11338      * @param {Object} srcObj
11339      *        The source object
11340      * @param {Object} options
11341      *        The options passed to the tech
11342      * @return {string} 'probably', 'maybe', or '' (empty string)
11343      */
11344     ;
11345 
11346     Tech.canPlaySource = function canPlaySource(srcObj, options) {
11347       return Tech.canPlayType(srcObj.type);
11348     }
11349     /*
11350      * Return whether the argument is a Tech or not.
11351      * Can be passed either a Class like `Html5` or a instance like `player.tech_`
11352      *
11353      * @param {Object} component
11354      *        The item to check
11355      *
11356      * @return {boolean}
11357      *         Whether it is a tech or not
11358      *         - True if it is a tech
11359      *         - False if it is not
11360      */
11361     ;
11362 
11363     Tech.isTech = function isTech(component) {
11364       return component.prototype instanceof Tech || component instanceof Tech || component === Tech;
11365     }
11366     /**
11367      * Registers a `Tech` into a shared list for videojs.
11368      *
11369      * @param {string} name
11370      *        Name of the `Tech` to register.
11371      *
11372      * @param {Object} tech
11373      *        The `Tech` class to register.
11374      */
11375     ;
11376 
11377     Tech.registerTech = function registerTech(name, tech) {
11378       if (!Tech.techs_) {
11379         Tech.techs_ = {};
11380       }
11381 
11382       if (!Tech.isTech(tech)) {
11383         throw new Error("Tech " + name + " must be a Tech");
11384       }
11385 
11386       if (!Tech.canPlayType) {
11387         throw new Error('Techs must have a static canPlayType method on them');
11388       }
11389 
11390       if (!Tech.canPlaySource) {
11391         throw new Error('Techs must have a static canPlaySource method on them');
11392       }
11393 
11394       name = toTitleCase(name);
11395       Tech.techs_[name] = tech;
11396       Tech.techs_[toLowerCase(name)] = tech;
11397 
11398       if (name !== 'Tech') {
11399         // camel case the techName for use in techOrder
11400         Tech.defaultTechOrder_.push(name);
11401       }
11402 
11403       return tech;
11404     }
11405     /**
11406      * Get a `Tech` from the shared list by name.
11407      *
11408      * @param {string} name
11409      *        `camelCase` or `TitleCase` name of the Tech to get
11410      *
11411      * @return {Tech|undefined}
11412      *         The `Tech` or undefined if there was no tech with the name requested.
11413      */
11414     ;
11415 
11416     Tech.getTech = function getTech(name) {
11417       if (!name) {
11418         return;
11419       }
11420 
11421       if (Tech.techs_ && Tech.techs_[name]) {
11422         return Tech.techs_[name];
11423       }
11424 
11425       name = toTitleCase(name);
11426 
11427       if (window$1 && window$1.videojs && window$1.videojs[name]) {
11428         log.warn("The " + name + " tech was added to the videojs object when it should be registered using videojs.registerTech(name, tech)");
11429         return window$1.videojs[name];
11430       }
11431     };
11432 
11433     return Tech;
11434   }(Component);
11435   /**
11436    * Get the {@link VideoTrackList}
11437    *
11438    * @returns {VideoTrackList}
11439    * @method Tech.prototype.videoTracks
11440    */
11441 
11442   /**
11443    * Get the {@link AudioTrackList}
11444    *
11445    * @returns {AudioTrackList}
11446    * @method Tech.prototype.audioTracks
11447    */
11448 
11449   /**
11450    * Get the {@link TextTrackList}
11451    *
11452    * @returns {TextTrackList}
11453    * @method Tech.prototype.textTracks
11454    */
11455 
11456   /**
11457    * Get the remote element {@link TextTrackList}
11458    *
11459    * @returns {TextTrackList}
11460    * @method Tech.prototype.remoteTextTracks
11461    */
11462 
11463   /**
11464    * Get the remote element {@link HtmlTrackElementList}
11465    *
11466    * @returns {HtmlTrackElementList}
11467    * @method Tech.prototype.remoteTextTrackEls
11468    */
11469 
11470 
11471   ALL.names.forEach(function (name) {
11472     var props = ALL[name];
11473 
11474     Tech.prototype[props.getterName] = function () {
11475       this[props.privateName] = this[props.privateName] || new props.ListClass();
11476       return this[props.privateName];
11477     };
11478   });
11479   /**
11480    * List of associated text tracks
11481    *
11482    * @type {TextTrackList}
11483    * @private
11484    * @property Tech#textTracks_
11485    */
11486 
11487   /**
11488    * List of associated audio tracks.
11489    *
11490    * @type {AudioTrackList}
11491    * @private
11492    * @property Tech#audioTracks_
11493    */
11494 
11495   /**
11496    * List of associated video tracks.
11497    *
11498    * @type {VideoTrackList}
11499    * @private
11500    * @property Tech#videoTracks_
11501    */
11502 
11503   /**
11504    * Boolean indicating whether the `Tech` supports volume control.
11505    *
11506    * @type {boolean}
11507    * @default
11508    */
11509 
11510   Tech.prototype.featuresVolumeControl = true;
11511   /**
11512    * Boolean indicating whether the `Tech` supports muting volume.
11513    *
11514    * @type {bolean}
11515    * @default
11516    */
11517 
11518   Tech.prototype.featuresMuteControl = true;
11519   /**
11520    * Boolean indicating whether the `Tech` supports fullscreen resize control.
11521    * Resizing plugins using request fullscreen reloads the plugin
11522    *
11523    * @type {boolean}
11524    * @default
11525    */
11526 
11527   Tech.prototype.featuresFullscreenResize = false;
11528   /**
11529    * Boolean indicating whether the `Tech` supports changing the speed at which the video
11530    * plays. Examples:
11531    *   - Set player to play 2x (twice) as fast
11532    *   - Set player to play 0.5x (half) as fast
11533    *
11534    * @type {boolean}
11535    * @default
11536    */
11537 
11538   Tech.prototype.featuresPlaybackRate = false;
11539   /**
11540    * Boolean indicating whether the `Tech` supports the `progress` event. This is currently
11541    * not triggered by video-js-swf. This will be used to determine if
11542    * {@link Tech#manualProgressOn} should be called.
11543    *
11544    * @type {boolean}
11545    * @default
11546    */
11547 
11548   Tech.prototype.featuresProgressEvents = false;
11549   /**
11550    * Boolean indicating whether the `Tech` supports the `sourceset` event.
11551    *
11552    * A tech should set this to `true` and then use {@link Tech#triggerSourceset}
11553    * to trigger a {@link Tech#event:sourceset} at the earliest time after getting
11554    * a new source.
11555    *
11556    * @type {boolean}
11557    * @default
11558    */
11559 
11560   Tech.prototype.featuresSourceset = false;
11561   /**
11562    * Boolean indicating whether the `Tech` supports the `timeupdate` event. This is currently
11563    * not triggered by video-js-swf. This will be used to determine if
11564    * {@link Tech#manualTimeUpdates} should be called.
11565    *
11566    * @type {boolean}
11567    * @default
11568    */
11569 
11570   Tech.prototype.featuresTimeupdateEvents = false;
11571   /**
11572    * Boolean indicating whether the `Tech` supports the native `TextTrack`s.
11573    * This will help us integrate with native `TextTrack`s if the browser supports them.
11574    *
11575    * @type {boolean}
11576    * @default
11577    */
11578 
11579   Tech.prototype.featuresNativeTextTracks = false;
11580   /**
11581    * A functional mixin for techs that want to use the Source Handler pattern.
11582    * Source handlers are scripts for handling specific formats.
11583    * The source handler pattern is used for adaptive formats (HLS, DASH) that
11584    * manually load video data and feed it into a Source Buffer (Media Source Extensions)
11585    * Example: `Tech.withSourceHandlers.call(MyTech);`
11586    *
11587    * @param {Tech} _Tech
11588    *        The tech to add source handler functions to.
11589    *
11590    * @mixes Tech~SourceHandlerAdditions
11591    */
11592 
11593   Tech.withSourceHandlers = function (_Tech) {
11594     /**
11595      * Register a source handler
11596      *
11597      * @param {Function} handler
11598      *        The source handler class
11599      *
11600      * @param {number} [index]
11601      *        Register it at the following index
11602      */
11603     _Tech.registerSourceHandler = function (handler, index) {
11604       var handlers = _Tech.sourceHandlers;
11605 
11606       if (!handlers) {
11607         handlers = _Tech.sourceHandlers = [];
11608       }
11609 
11610       if (index === undefined) {
11611         // add to the end of the list
11612         index = handlers.length;
11613       }
11614 
11615       handlers.splice(index, 0, handler);
11616     };
11617     /**
11618      * Check if the tech can support the given type. Also checks the
11619      * Techs sourceHandlers.
11620      *
11621      * @param {string} type
11622      *         The mimetype to check.
11623      *
11624      * @return {string}
11625      *         'probably', 'maybe', or '' (empty string)
11626      */
11627 
11628 
11629     _Tech.canPlayType = function (type) {
11630       var handlers = _Tech.sourceHandlers || [];
11631       var can;
11632 
11633       for (var i = 0; i < handlers.length; i++) {
11634         can = handlers[i].canPlayType(type);
11635 
11636         if (can) {
11637           return can;
11638         }
11639       }
11640 
11641       return '';
11642     };
11643     /**
11644      * Returns the first source handler that supports the source.
11645      *
11646      * TODO: Answer question: should 'probably' be prioritized over 'maybe'
11647      *
11648      * @param {Tech~SourceObject} source
11649      *        The source object
11650      *
11651      * @param {Object} options
11652      *        The options passed to the tech
11653      *
11654      * @return {SourceHandler|null}
11655      *          The first source handler that supports the source or null if
11656      *          no SourceHandler supports the source
11657      */
11658 
11659 
11660     _Tech.selectSourceHandler = function (source, options) {
11661       var handlers = _Tech.sourceHandlers || [];
11662       var can;
11663 
11664       for (var i = 0; i < handlers.length; i++) {
11665         can = handlers[i].canHandleSource(source, options);
11666 
11667         if (can) {
11668           return handlers[i];
11669         }
11670       }
11671 
11672       return null;
11673     };
11674     /**
11675      * Check if the tech can support the given source.
11676      *
11677      * @param {Tech~SourceObject} srcObj
11678      *        The source object
11679      *
11680      * @param {Object} options
11681      *        The options passed to the tech
11682      *
11683      * @return {string}
11684      *         'probably', 'maybe', or '' (empty string)
11685      */
11686 
11687 
11688     _Tech.canPlaySource = function (srcObj, options) {
11689       var sh = _Tech.selectSourceHandler(srcObj, options);
11690 
11691       if (sh) {
11692         return sh.canHandleSource(srcObj, options);
11693       }
11694 
11695       return '';
11696     };
11697     /**
11698      * When using a source handler, prefer its implementation of
11699      * any function normally provided by the tech.
11700      */
11701 
11702 
11703     var deferrable = ['seekable', 'seeking', 'duration'];
11704     /**
11705      * A wrapper around {@link Tech#seekable} that will call a `SourceHandler`s seekable
11706      * function if it exists, with a fallback to the Techs seekable function.
11707      *
11708      * @method _Tech.seekable
11709      */
11710 
11711     /**
11712      * A wrapper around {@link Tech#duration} that will call a `SourceHandler`s duration
11713      * function if it exists, otherwise it will fallback to the techs duration function.
11714      *
11715      * @method _Tech.duration
11716      */
11717 
11718     deferrable.forEach(function (fnName) {
11719       var originalFn = this[fnName];
11720 
11721       if (typeof originalFn !== 'function') {
11722         return;
11723       }
11724 
11725       this[fnName] = function () {
11726         if (this.sourceHandler_ && this.sourceHandler_[fnName]) {
11727           return this.sourceHandler_[fnName].apply(this.sourceHandler_, arguments);
11728         }
11729 
11730         return originalFn.apply(this, arguments);
11731       };
11732     }, _Tech.prototype);
11733     /**
11734      * Create a function for setting the source using a source object
11735      * and source handlers.
11736      * Should never be called unless a source handler was found.
11737      *
11738      * @param {Tech~SourceObject} source
11739      *        A source object with src and type keys
11740      */
11741 
11742     _Tech.prototype.setSource = function (source) {
11743       var sh = _Tech.selectSourceHandler(source, this.options_);
11744 
11745       if (!sh) {
11746         // Fall back to a native source hander when unsupported sources are
11747         // deliberately set
11748         if (_Tech.nativeSourceHandler) {
11749           sh = _Tech.nativeSourceHandler;
11750         } else {
11751           log.error('No source handler found for the current source.');
11752         }
11753       } // Dispose any existing source handler
11754 
11755 
11756       this.disposeSourceHandler();
11757       this.off('dispose', this.disposeSourceHandler);
11758 
11759       if (sh !== _Tech.nativeSourceHandler) {
11760         this.currentSource_ = source;
11761       }
11762 
11763       this.sourceHandler_ = sh.handleSource(source, this, this.options_);
11764       this.one('dispose', this.disposeSourceHandler);
11765     };
11766     /**
11767      * Clean up any existing SourceHandlers and listeners when the Tech is disposed.
11768      *
11769      * @listens Tech#dispose
11770      */
11771 
11772 
11773     _Tech.prototype.disposeSourceHandler = function () {
11774       // if we have a source and get another one
11775       // then we are loading something new
11776       // than clear all of our current tracks
11777       if (this.currentSource_) {
11778         this.clearTracks(['audio', 'video']);
11779         this.currentSource_ = null;
11780       } // always clean up auto-text tracks
11781 
11782 
11783       this.cleanupAutoTextTracks();
11784 
11785       if (this.sourceHandler_) {
11786         if (this.sourceHandler_.dispose) {
11787           this.sourceHandler_.dispose();
11788         }
11789 
11790         this.sourceHandler_ = null;
11791       }
11792     };
11793   };
vendor: 4,490 bytes, lines 11793-11943
11793 // The base Tech class needs to be registered as a Component. It is the only
11794   // Tech that can be registered as a Component.
11795 
11796 
11797   Component.registerComponent('Tech', Tech);
11798   Tech.registerTech('Tech', Tech);
11799   /**
11800    * A list of techs that should be added to techOrder on Players
11801    *
11802    * @private
11803    */
11804 
11805   Tech.defaultTechOrder_ = [];
11806 
11807   /**
11808    * @file middleware.js
11809    * @module middleware
11810    */
11811   var middlewares = {};
11812   var middlewareInstances = {};
11813   var TERMINATOR = {};
11814   /**
11815    * A middleware object is a plain JavaScript object that has methods that
11816    * match the {@link Tech} methods found in the lists of allowed
11817    * {@link module:middleware.allowedGetters|getters},
11818    * {@link module:middleware.allowedSetters|setters}, and
11819    * {@link module:middleware.allowedMediators|mediators}.
11820    *
11821    * @typedef {Object} MiddlewareObject
11822    */
11823 
11824   /**
11825    * A middleware factory function that should return a
11826    * {@link module:middleware~MiddlewareObject|MiddlewareObject}.
11827    *
11828    * This factory will be called for each player when needed, with the player
11829    * passed in as an argument.
11830    *
11831    * @callback MiddlewareFactory
11832    * @param {Player} player
11833    *        A Video.js player.
11834    */
11835 
11836   /**
11837    * Define a middleware that the player should use by way of a factory function
11838    * that returns a middleware object.
11839    *
11840    * @param  {string} type
11841    *         The MIME type to match or `"*"` for all MIME types.
11842    *
11843    * @param  {MiddlewareFactory} middleware
11844    *         A middleware factory function that will be executed for
11845    *         matching types.
11846    */
11847 
11848   function use(type, middleware) {
11849     middlewares[type] = middlewares[type] || [];
11850     middlewares[type].push(middleware);
11851   }
11852   /**
11853    * Asynchronously sets a source using middleware by recursing through any
11854    * matching middlewares and calling `setSource` on each, passing along the
11855    * previous returned value each time.
11856    *
11857    * @param  {Player} player
11858    *         A {@link Player} instance.
11859    *
11860    * @param  {Tech~SourceObject} src
11861    *         A source object.
11862    *
11863    * @param  {Function}
11864    *         The next middleware to run.
11865    */
11866 
11867   function setSource(player, src, next) {
11868     player.setTimeout(function () {
11869       return setSourceHelper(src, middlewares[src.type], next, player);
11870     }, 1);
11871   }
11872   /**
11873    * When the tech is set, passes the tech to each middleware's `setTech` method.
11874    *
11875    * @param {Object[]} middleware
11876    *        An array of middleware instances.
11877    *
11878    * @param {Tech} tech
11879    *        A Video.js tech.
11880    */
11881 
11882   function setTech(middleware, tech) {
11883     middleware.forEach(function (mw) {
11884       return mw.setTech && mw.setTech(tech);
11885     });
11886   }
11887   /**
11888    * Calls a getter on the tech first, through each middleware
11889    * from right to left to the player.
11890    *
11891    * @param  {Object[]} middleware
11892    *         An array of middleware instances.
11893    *
11894    * @param  {Tech} tech
11895    *         The current tech.
11896    *
11897    * @param  {string} method
11898    *         A method name.
11899    *
11900    * @return {Mixed}
11901    *         The final value from the tech after middleware has intercepted it.
11902    */
11903 
11904   function get(middleware, tech, method) {
11905     return middleware.reduceRight(middlewareIterator(method), tech[method]());
11906   }
11907   /**
11908    * Takes the argument given to the player and calls the setter method on each
11909    * middleware from left to right to the tech.
11910    *
11911    * @param  {Object[]} middleware
11912    *         An array of middleware instances.
11913    *
11914    * @param  {Tech} tech
11915    *         The current tech.
11916    *
11917    * @param  {string} method
11918    *         A method name.
11919    *
11920    * @param  {Mixed} arg
11921    *         The value to set on the tech.
11922    *
11923    * @return {Mixed}
11924    *         The return value of the `method` of the `tech`.
11925    */
11926 
11927   function set(middleware, tech, method, arg) {
11928     return tech[method](middleware.reduce(middlewareIterator(method), arg));
11929   }
11930   /**
11931    * Takes the argument given to the player and calls the `call` version of the
11932    * method on each middleware from left to right.
11933    *
11934    * Then, call the passed in method on the tech and return the result unchanged
11935    * back to the player, through middleware, this time from right to left.
11936    *
11937    * @param  {Object[]} middleware
11938    *         An array of middleware instances.
11939    *
11940    * @param  {Tech} tech
11941    *         The current tech.
11942    *
11943    * @param  {string} method
vendor: 191,679 bytes, lines 11944-18483
11944    *         A method name.
11945    *
11946    * @param  {Mixed} arg
11947    *         The value to set on the tech.
11948    *
11949    * @return {Mixed}
11950    *         The return value of the `method` of the `tech`, regardless of the
11951    *         return values of middlewares.
11952    */
11953 
11954   function mediate(middleware, tech, method, arg) {
11955     if (arg === void 0) {
11956       arg = null;
11957     }
11958 
11959     var callMethod = 'call' + toTitleCase(method);
11960     var middlewareValue = middleware.reduce(middlewareIterator(callMethod), arg);
11961     var terminated = middlewareValue === TERMINATOR; // deprecated. The `null` return value should instead return TERMINATOR to
11962     // prevent confusion if a techs method actually returns null.
11963 
11964     var returnValue = terminated ? null : tech[method](middlewareValue);
11965     executeRight(middleware, method, returnValue, terminated);
11966     return returnValue;
11967   }
11968   /**
11969    * Enumeration of allowed getters where the keys are method names.
11970    *
11971    * @type {Object}
11972    */
11973 
11974   var allowedGetters = {
11975     buffered: 1,
11976     currentTime: 1,
11977     duration: 1,
11978     muted: 1,
11979     played: 1,
11980     paused: 1,
11981     seekable: 1,
11982     volume: 1
11983   };
11984   /**
11985    * Enumeration of allowed setters where the keys are method names.
11986    *
11987    * @type {Object}
11988    */
11989 
11990   var allowedSetters = {
11991     setCurrentTime: 1,
11992     setMuted: 1,
11993     setVolume: 1
11994   };
11995   /**
11996    * Enumeration of allowed mediators where the keys are method names.
11997    *
11998    * @type {Object}
11999    */
12000 
12001   var allowedMediators = {
12002     play: 1,
12003     pause: 1
12004   };
12005 
12006   function middlewareIterator(method) {
12007     return function (value, mw) {
12008       // if the previous middleware terminated, pass along the termination
12009       if (value === TERMINATOR) {
12010         return TERMINATOR;
12011       }
12012 
12013       if (mw[method]) {
12014         return mw[method](value);
12015       }
12016 
12017       return value;
12018     };
12019   }
12020 
12021   function executeRight(mws, method, value, terminated) {
12022     for (var i = mws.length - 1; i >= 0; i--) {
12023       var mw = mws[i];
12024 
12025       if (mw[method]) {
12026         mw[method](terminated, value);
12027       }
12028     }
12029   }
12030   /**
12031    * Clear the middleware cache for a player.
12032    *
12033    * @param  {Player} player
12034    *         A {@link Player} instance.
12035    */
12036 
12037 
12038   function clearCacheForPlayer(player) {
12039     middlewareInstances[player.id()] = null;
12040   }
12041   /**
12042    * {
12043    *  [playerId]: [[mwFactory, mwInstance], ...]
12044    * }
12045    *
12046    * @private
12047    */
12048 
12049   function getOrCreateFactory(player, mwFactory) {
12050     var mws = middlewareInstances[player.id()];
12051     var mw = null;
12052 
12053     if (mws === undefined || mws === null) {
12054       mw = mwFactory(player);
12055       middlewareInstances[player.id()] = [[mwFactory, mw]];
12056       return mw;
12057     }
12058 
12059     for (var i = 0; i < mws.length; i++) {
12060       var _mws$i = mws[i],
12061           mwf = _mws$i[0],
12062           mwi = _mws$i[1];
12063 
12064       if (mwf !== mwFactory) {
12065         continue;
12066       }
12067 
12068       mw = mwi;
12069     }
12070 
12071     if (mw === null) {
12072       mw = mwFactory(player);
12073       mws.push([mwFactory, mw]);
12074     }
12075 
12076     return mw;
12077   }
12078 
12079   function setSourceHelper(src, middleware, next, player, acc, lastRun) {
12080     if (src === void 0) {
12081       src = {};
12082     }
12083 
12084     if (middleware === void 0) {
12085       middleware = [];
12086     }
12087 
12088     if (acc === void 0) {
12089       acc = [];
12090     }
12091 
12092     if (lastRun === void 0) {
12093       lastRun = false;
12094     }
12095 
12096     var _middleware = middleware,
12097         mwFactory = _middleware[0],
12098         mwrest = _middleware.slice(1); // if mwFactory is a string, then we're at a fork in the road
12099 
12100 
12101     if (typeof mwFactory === 'string') {
12102       setSourceHelper(src, middlewares[mwFactory], next, player, acc, lastRun); // if we have an mwFactory, call it with the player to get the mw,
12103       // then call the mw's setSource method
12104     } else if (mwFactory) {
12105       var mw = getOrCreateFactory(player, mwFactory); // if setSource isn't present, implicitly select this middleware
12106 
12107       if (!mw.setSource) {
12108         acc.push(mw);
12109         return setSourceHelper(src, mwrest, next, player, acc, lastRun);
12110       }
12111 
12112       mw.setSource(assign({}, src), function (err, _src) {
12113         // something happened, try the next middleware on the current level
12114         // make sure to use the old src
12115         if (err) {
12116           return setSourceHelper(src, mwrest, next, player, acc, lastRun);
12117         } // we've succeeded, now we need to go deeper
12118 
12119 
12120         acc.push(mw); // if it's the same type, continue down the current chain
12121         // otherwise, we want to go down the new chain
12122 
12123         setSourceHelper(_src, src.type === _src.type ? mwrest : middlewares[_src.type], next, player, acc, lastRun);
12124       });
12125     } else if (mwrest.length) {
12126       setSourceHelper(src, mwrest, next, player, acc, lastRun);
12127     } else if (lastRun) {
12128       next(src, acc);
12129     } else {
12130       setSourceHelper(src, middlewares['*'], next, player, acc, true);
12131     }
12132   }
12133 
12134   /**
12135    * Mimetypes
12136    *
12137    * @see http://hul.harvard.edu/ois/////systems/wax/wax-public-help/mimetypes.htm
12138    * @typedef Mimetypes~Kind
12139    * @enum
12140    */
12141 
12142   var MimetypesKind = {
12143     opus: 'video/ogg',
12144     ogv: 'video/ogg',
12145     mp4: 'video/mp4',
12146     mov: 'video/mp4',
12147     m4v: 'video/mp4',
12148     mkv: 'video/x-matroska',
12149     m4a: 'audio/mp4',
12150     mp3: 'audio/mpeg',
12151     aac: 'audio/aac',
12152     oga: 'audio/ogg',
12153     m3u8: 'application/x-mpegURL',
12154     jpg: 'image/jpeg',
12155     jpeg: 'image/jpeg',
12156     gif: 'image/gif',
12157     png: 'image/png',
12158     svg: 'image/svg+xml',
12159     webp: 'image/webp'
12160   };
12161   /**
12162    * Get the mimetype of a given src url if possible
12163    *
12164    * @param {string} src
12165    *        The url to the src
12166    *
12167    * @return {string}
12168    *         return the mimetype if it was known or empty string otherwise
12169    */
12170 
12171   var getMimetype = function getMimetype(src) {
12172     if (src === void 0) {
12173       src = '';
12174     }
12175 
12176     var ext = getFileExtension(src);
12177     var mimetype = MimetypesKind[ext.toLowerCase()];
12178     return mimetype || '';
12179   };
12180   /**
12181    * Find the mime type of a given source string if possible. Uses the player
12182    * source cache.
12183    *
12184    * @param {Player} player
12185    *        The player object
12186    *
12187    * @param {string} src
12188    *        The source string
12189    *
12190    * @return {string}
12191    *         The type that was found
12192    */
12193 
12194   var findMimetype = function findMimetype(player, src) {
12195     if (!src) {
12196       return '';
12197     } // 1. check for the type in the `source` cache
12198 
12199 
12200     if (player.cache_.source.src === src && player.cache_.source.type) {
12201       return player.cache_.source.type;
12202     } // 2. see if we have this source in our `currentSources` cache
12203 
12204 
12205     var matchingSources = player.cache_.sources.filter(function (s) {
12206       return s.src === src;
12207     });
12208 
12209     if (matchingSources.length) {
12210       return matchingSources[0].type;
12211     } // 3. look for the src url in source elements and use the type there
12212 
12213 
12214     var sources = player.$$('source');
12215 
12216     for (var i = 0; i < sources.length; i++) {
12217       var s = sources[i];
12218 
12219       if (s.type && s.src && s.src === src) {
12220         return s.type;
12221       }
12222     } // 4. finally fallback to our list of mime types based on src url extension
12223 
12224 
12225     return getMimetype(src);
12226   };
12227 
12228   /**
12229    * @module filter-source
12230    */
12231   /**
12232    * Filter out single bad source objects or multiple source objects in an
12233    * array. Also flattens nested source object arrays into a 1 dimensional
12234    * array of source objects.
12235    *
12236    * @param {Tech~SourceObject|Tech~SourceObject[]} src
12237    *        The src object to filter
12238    *
12239    * @return {Tech~SourceObject[]}
12240    *         An array of sourceobjects containing only valid sources
12241    *
12242    * @private
12243    */
12244 
12245   var filterSource = function filterSource(src) {
12246     // traverse array
12247     if (Array.isArray(src)) {
12248       var newsrc = [];
12249       src.forEach(function (srcobj) {
12250         srcobj = filterSource(srcobj);
12251 
12252         if (Array.isArray(srcobj)) {
12253           newsrc = newsrc.concat(srcobj);
12254         } else if (isObject(srcobj)) {
12255           newsrc.push(srcobj);
12256         }
12257       });
12258       src = newsrc;
12259     } else if (typeof src === 'string' && src.trim()) {
12260       // convert string into object
12261       src = [fixSource({
12262         src: src
12263       })];
12264     } else if (isObject(src) && typeof src.src === 'string' && src.src && src.src.trim()) {
12265       // src is already valid
12266       src = [fixSource(src)];
12267     } else {
12268       // invalid source, turn it into an empty array
12269       src = [];
12270     }
12271 
12272     return src;
12273   };
12274   /**
12275    * Checks src mimetype, adding it when possible
12276    *
12277    * @param {Tech~SourceObject} src
12278    *        The src object to check
12279    * @return {Tech~SourceObject}
12280    *        src Object with known type
12281    */
12282 
12283 
12284   function fixSource(src) {
12285     if (!src.type) {
12286       var mimetype = getMimetype(src.src);
12287 
12288       if (mimetype) {
12289         src.type = mimetype;
12290       }
12291     }
12292 
12293     return src;
12294   }
12295 
12296   /**
12297    * The `MediaLoader` is the `Component` that decides which playback technology to load
12298    * when a player is initialized.
12299    *
12300    * @extends Component
12301    */
12302 
12303   var MediaLoader =
12304   /*#__PURE__*/
12305   function (_Component) {
12306     inheritsLoose(MediaLoader, _Component);
12307 
12308     /**
12309      * Create an instance of this class.
12310      *
12311      * @param {Player} player
12312      *        The `Player` that this class should attach to.
12313      *
12314      * @param {Object} [options]
12315      *        The key/value store of player options.
12316      *
12317      * @param {Component~ReadyCallback} [ready]
12318      *        The function that is run when this component is ready.
12319      */
12320     function MediaLoader(player, options, ready) {
12321       var _this;
12322 
12323       // MediaLoader has no element
12324       var options_ = mergeOptions({
12325         createEl: false
12326       }, options);
12327       _this = _Component.call(this, player, options_, ready) || this; // If there are no sources when the player is initialized,
12328       // load the first supported playback technology.
12329 
12330       if (!options.playerOptions.sources || options.playerOptions.sources.length === 0) {
12331         for (var i = 0, j = options.playerOptions.techOrder; i < j.length; i++) {
12332           var techName = toTitleCase(j[i]);
12333           var tech = Tech.getTech(techName); // Support old behavior of techs being registered as components.
12334           // Remove once that deprecated behavior is removed.
12335 
12336           if (!techName) {
12337             tech = Component.getComponent(techName);
12338           } // Check if the browser supports this technology
12339 
12340 
12341           if (tech && tech.isSupported()) {
12342             player.loadTech_(techName);
12343             break;
12344           }
12345         }
12346       } else {
12347         // Loop through playback technologies (HTML5, Flash) and check for support.
12348         // Then load the best source.
12349         // A few assumptions here:
12350         //   All playback technologies respect preload false.
12351         player.src(options.playerOptions.sources);
12352       }
12353 
12354       return _this;
12355     }
12356 
12357     return MediaLoader;
12358   }(Component);
12359 
12360   Component.registerComponent('MediaLoader', MediaLoader);
12361 
12362   /**
12363    * Component which is clickable or keyboard actionable, but is not a
12364    * native HTML button.
12365    *
12366    * @extends Component
12367    */
12368 
12369   var ClickableComponent =
12370   /*#__PURE__*/
12371   function (_Component) {
12372     inheritsLoose(ClickableComponent, _Component);
12373 
12374     /**
12375      * Creates an instance of this class.
12376      *
12377      * @param  {Player} player
12378      *         The `Player` that this class should be attached to.
12379      *
12380      * @param  {Object} [options]
12381      *         The key/value store of player options.
12382      *
12383      * @param  {function} [options.clickHandler]
12384      *         The function to call when the button is clicked / activated
12385      */
12386     function ClickableComponent(player, options) {
12387       var _this;
12388 
12389       _this = _Component.call(this, player, options) || this;
12390 
12391       _this.emitTapEvents();
12392 
12393       _this.enable();
12394 
12395       return _this;
12396     }
12397     /**
12398      * Create the `ClickableComponent`s DOM element.
12399      *
12400      * @param {string} [tag=div]
12401      *        The element's node type.
12402      *
12403      * @param {Object} [props={}]
12404      *        An object of properties that should be set on the element.
12405      *
12406      * @param {Object} [attributes={}]
12407      *        An object of attributes that should be set on the element.
12408      *
12409      * @return {Element}
12410      *         The element that gets created.
12411      */
12412 
12413 
12414     var _proto = ClickableComponent.prototype;
12415 
12416     _proto.createEl = function createEl(tag, props, attributes) {
12417       if (tag === void 0) {
12418         tag = 'div';
12419       }
12420 
12421       if (props === void 0) {
12422         props = {};
12423       }
12424 
12425       if (attributes === void 0) {
12426         attributes = {};
12427       }
12428 
12429       props = assign({
12430         innerHTML: '<span aria-hidden="true" class="vjs-icon-placeholder"></span>',
12431         className: this.buildCSSClass(),
12432         tabIndex: 0
12433       }, props);
12434 
12435       if (tag === 'button') {
12436         log.error("Creating a ClickableComponent with an HTML element of " + tag + " is not supported; use a Button instead.");
12437       } // Add ARIA attributes for clickable element which is not a native HTML button
12438 
12439 
12440       attributes = assign({
12441         role: 'button'
12442       }, attributes);
12443       this.tabIndex_ = props.tabIndex;
12444 
12445       var el = _Component.prototype.createEl.call(this, tag, props, attributes);
12446 
12447       this.createControlTextEl(el);
12448       return el;
12449     };
12450 
12451     _proto.dispose = function dispose() {
12452       // remove controlTextEl_ on dispose
12453       this.controlTextEl_ = null;
12454 
12455       _Component.prototype.dispose.call(this);
12456     }
12457     /**
12458      * Create a control text element on this `ClickableComponent`
12459      *
12460      * @param {Element} [el]
12461      *        Parent element for the control text.
12462      *
12463      * @return {Element}
12464      *         The control text element that gets created.
12465      */
12466     ;
12467 
12468     _proto.createControlTextEl = function createControlTextEl(el) {
12469       this.controlTextEl_ = createEl('span', {
12470         className: 'vjs-control-text'
12471       }, {
12472         // let the screen reader user know that the text of the element may change
12473         'aria-live': 'polite'
12474       });
12475 
12476       if (el) {
12477         el.appendChild(this.controlTextEl_);
12478       }
12479 
12480       this.controlText(this.controlText_, el);
12481       return this.controlTextEl_;
12482     }
12483     /**
12484      * Get or set the localize text to use for the controls on the `ClickableComponent`.
12485      *
12486      * @param {string} [text]
12487      *        Control text for element.
12488      *
12489      * @param {Element} [el=this.el()]
12490      *        Element to set the title on.
12491      *
12492      * @return {string}
12493      *         - The control text when getting
12494      */
12495     ;
12496 
12497     _proto.controlText = function controlText(text, el) {
12498       if (el === void 0) {
12499         el = this.el();
12500       }
12501 
12502       if (text === undefined) {
12503         return this.controlText_ || 'Need Text';
12504       }
12505 
12506       var localizedText = this.localize(text);
12507       this.controlText_ = text;
12508       textContent(this.controlTextEl_, localizedText);
12509 
12510       if (!this.nonIconControl) {
12511         // Set title attribute if only an icon is shown
12512         el.setAttribute('title', localizedText);
12513       }
12514     }
12515     /**
12516      * Builds the default DOM `className`.
12517      *
12518      * @return {string}
12519      *         The DOM `className` for this object.
12520      */
12521     ;
12522 
12523     _proto.buildCSSClass = function buildCSSClass() {
12524       return "vjs-control vjs-button " + _Component.prototype.buildCSSClass.call(this);
12525     }
12526     /**
12527      * Enable this `ClickableComponent`
12528      */
12529     ;
12530 
12531     _proto.enable = function enable() {
12532       if (!this.enabled_) {
12533         this.enabled_ = true;
12534         this.removeClass('vjs-disabled');
12535         this.el_.setAttribute('aria-disabled', 'false');
12536 
12537         if (typeof this.tabIndex_ !== 'undefined') {
12538           this.el_.setAttribute('tabIndex', this.tabIndex_);
12539         }
12540 
12541         this.on(['tap', 'click'], this.handleClick);
12542         this.on('keydown', this.handleKeyDown);
12543       }
12544     }
12545     /**
12546      * Disable this `ClickableComponent`
12547      */
12548     ;
12549 
12550     _proto.disable = function disable() {
12551       this.enabled_ = false;
12552       this.addClass('vjs-disabled');
12553       this.el_.setAttribute('aria-disabled', 'true');
12554 
12555       if (typeof this.tabIndex_ !== 'undefined') {
12556         this.el_.removeAttribute('tabIndex');
12557       }
12558 
12559       this.off('mouseover', this.handleMouseOver);
12560       this.off('mouseout', this.handleMouseOut);
12561       this.off(['tap', 'click'], this.handleClick);
12562       this.off('keydown', this.handleKeyDown);
12563     }
12564     /**
12565      * Event handler that is called when a `ClickableComponent` receives a
12566      * `click` or `tap` event.
12567      *
12568      * @param {EventTarget~Event} event
12569      *        The `tap` or `click` event that caused this function to be called.
12570      *
12571      * @listens tap
12572      * @listens click
12573      * @abstract
12574      */
12575     ;
12576 
12577     _proto.handleClick = function handleClick(event) {
12578       if (this.options_.clickHandler) {
12579         this.options_.clickHandler.call(this, arguments);
12580       }
12581     }
12582     /**
12583      * Event handler that is called when a `ClickableComponent` receives a
12584      * `keydown` event.
12585      *
12586      * By default, if the key is Space or Enter, it will trigger a `click` event.
12587      *
12588      * @param {EventTarget~Event} event
12589      *        The `keydown` event that caused this function to be called.
12590      *
12591      * @listens keydown
12592      */
12593     ;
12594 
12595     _proto.handleKeyDown = function handleKeyDown(event) {
12596       // Support Space or Enter key operation to fire a click event. Also,
12597       // prevent the event from propagating through the DOM and triggering
12598       // Player hotkeys.
12599       if (keycode.isEventKey(event, 'Space') || keycode.isEventKey(event, 'Enter')) {
12600         event.preventDefault();
12601         event.stopPropagation();
12602         this.trigger('click');
12603       } else {
12604         // Pass keypress handling up for unsupported keys
12605         _Component.prototype.handleKeyDown.call(this, event);
12606       }
12607     };
12608 
12609     return ClickableComponent;
12610   }(Component);
12611 
12612   Component.registerComponent('ClickableComponent', ClickableComponent);
12613 
12614   /**
12615    * A `ClickableComponent` that handles showing the poster image for the player.
12616    *
12617    * @extends ClickableComponent
12618    */
12619 
12620   var PosterImage =
12621   /*#__PURE__*/
12622   function (_ClickableComponent) {
12623     inheritsLoose(PosterImage, _ClickableComponent);
12624 
12625     /**
12626      * Create an instance of this class.
12627      *
12628      * @param {Player} player
12629      *        The `Player` that this class should attach to.
12630      *
12631      * @param {Object} [options]
12632      *        The key/value store of player options.
12633      */
12634     function PosterImage(player, options) {
12635       var _this;
12636 
12637       _this = _ClickableComponent.call(this, player, options) || this;
12638 
12639       _this.update();
12640 
12641       player.on('posterchange', bind(assertThisInitialized(_this), _this.update));
12642       return _this;
12643     }
12644     /**
12645      * Clean up and dispose of the `PosterImage`.
12646      */
12647 
12648 
12649     var _proto = PosterImage.prototype;
12650 
12651     _proto.dispose = function dispose() {
12652       this.player().off('posterchange', this.update);
12653 
12654       _ClickableComponent.prototype.dispose.call(this);
12655     }
12656     /**
12657      * Create the `PosterImage`s DOM element.
12658      *
12659      * @return {Element}
12660      *         The element that gets created.
12661      */
12662     ;
12663 
12664     _proto.createEl = function createEl$1() {
12665       var el = createEl('div', {
12666         className: 'vjs-poster',
12667         // Don't want poster to be tabbable.
12668         tabIndex: -1
12669       });
12670       return el;
12671     }
12672     /**
12673      * An {@link EventTarget~EventListener} for {@link Player#posterchange} events.
12674      *
12675      * @listens Player#posterchange
12676      *
12677      * @param {EventTarget~Event} [event]
12678      *        The `Player#posterchange` event that triggered this function.
12679      */
12680     ;
12681 
12682     _proto.update = function update(event) {
12683       var url = this.player().poster();
12684       this.setSrc(url); // If there's no poster source we should display:none on this component
12685       // so it's not still clickable or right-clickable
12686 
12687       if (url) {
12688         this.show();
12689       } else {
12690         this.hide();
12691       }
12692     }
12693     /**
12694      * Set the source of the `PosterImage` depending on the display method.
12695      *
12696      * @param {string} url
12697      *        The URL to the source for the `PosterImage`.
12698      */
12699     ;
12700 
12701     _proto.setSrc = function setSrc(url) {
12702       var backgroundImage = ''; // Any falsy value should stay as an empty string, otherwise
12703       // this will throw an extra error
12704 
12705       if (url) {
12706         backgroundImage = "url(\"" + url + "\")";
12707       }
12708 
12709       this.el_.style.backgroundImage = backgroundImage;
12710     }
12711     /**
12712      * An {@link EventTarget~EventListener} for clicks on the `PosterImage`. See
12713      * {@link ClickableComponent#handleClick} for instances where this will be triggered.
12714      *
12715      * @listens tap
12716      * @listens click
12717      * @listens keydown
12718      *
12719      * @param {EventTarget~Event} event
12720      +        The `click`, `tap` or `keydown` event that caused this function to be called.
12721      */
12722     ;
12723 
12724     _proto.handleClick = function handleClick(event) {
12725       // We don't want a click to trigger playback when controls are disabled
12726       if (!this.player_.controls()) {
12727         return;
12728       }
12729 
12730       if (this.player_.tech(true)) {
12731         this.player_.tech(true).focus();
12732       }
12733 
12734       if (this.player_.paused()) {
12735         silencePromise(this.player_.play());
12736       } else {
12737         this.player_.pause();
12738       }
12739     };
12740 
12741     return PosterImage;
12742   }(ClickableComponent);
12743 
12744   Component.registerComponent('PosterImage', PosterImage);
12745 
12746   var darkGray = '#222';
12747   var lightGray = '#ccc';
12748   var fontMap = {
12749     monospace: 'monospace',
12750     sansSerif: 'sans-serif',
12751     serif: 'serif',
12752     monospaceSansSerif: '"Andale Mono", "Lucida Console", monospace',
12753     monospaceSerif: '"Courier New", monospace',
12754     proportionalSansSerif: 'sans-serif',
12755     proportionalSerif: 'serif',
12756     casual: '"Comic Sans MS", Impact, fantasy',
12757     script: '"Monotype Corsiva", cursive',
12758     smallcaps: '"Andale Mono", "Lucida Console", monospace, sans-serif'
12759   };
12760   /**
12761    * Construct an rgba color from a given hex color code.
12762    *
12763    * @param {number} color
12764    *        Hex number for color, like #f0e or #f604e2.
12765    *
12766    * @param {number} opacity
12767    *        Value for opacity, 0.0 - 1.0.
12768    *
12769    * @return {string}
12770    *         The rgba color that was created, like 'rgba(255, 0, 0, 0.3)'.
12771    */
12772 
12773   function constructColor(color, opacity) {
12774     var hex;
12775 
12776     if (color.length === 4) {
12777       // color looks like "#f0e"
12778       hex = color[1] + color[1] + color[2] + color[2] + color[3] + color[3];
12779     } else if (color.length === 7) {
12780       // color looks like "#f604e2"
12781       hex = color.slice(1);
12782     } else {
12783       throw new Error('Invalid color code provided, ' + color + '; must be formatted as e.g. #f0e or #f604e2.');
12784     }
12785 
12786     return 'rgba(' + parseInt(hex.slice(0, 2), 16) + ',' + parseInt(hex.slice(2, 4), 16) + ',' + parseInt(hex.slice(4, 6), 16) + ',' + opacity + ')';
12787   }
12788   /**
12789    * Try to update the style of a DOM element. Some style changes will throw an error,
12790    * particularly in IE8. Those should be noops.
12791    *
12792    * @param {Element} el
12793    *        The DOM element to be styled.
12794    *
12795    * @param {string} style
12796    *        The CSS property on the element that should be styled.
12797    *
12798    * @param {string} rule
12799    *        The style rule that should be applied to the property.
12800    *
12801    * @private
12802    */
12803 
12804   function tryUpdateStyle(el, style, rule) {
12805     try {
12806       el.style[style] = rule;
12807     } catch (e) {
12808       // Satisfies linter.
12809       return;
12810     }
12811   }
12812   /**
12813    * The component for displaying text track cues.
12814    *
12815    * @extends Component
12816    */
12817 
12818 
12819   var TextTrackDisplay =
12820   /*#__PURE__*/
12821   function (_Component) {
12822     inheritsLoose(TextTrackDisplay, _Component);
12823 
12824     /**
12825      * Creates an instance of this class.
12826      *
12827      * @param {Player} player
12828      *        The `Player` that this class should be attached to.
12829      *
12830      * @param {Object} [options]
12831      *        The key/value store of player options.
12832      *
12833      * @param {Component~ReadyCallback} [ready]
12834      *        The function to call when `TextTrackDisplay` is ready.
12835      */
12836     function TextTrackDisplay(player, options, ready) {
12837       var _this;
12838 
12839       _this = _Component.call(this, player, options, ready) || this;
12840       var updateDisplayHandler = bind(assertThisInitialized(_this), _this.updateDisplay);
12841       player.on('loadstart', bind(assertThisInitialized(_this), _this.toggleDisplay));
12842       player.on('texttrackchange', updateDisplayHandler);
12843       player.on('loadedmetadata', bind(assertThisInitialized(_this), _this.preselectTrack)); // This used to be called during player init, but was causing an error
12844       // if a track should show by default and the display hadn't loaded yet.
12845       // Should probably be moved to an external track loader when we support
12846       // tracks that don't need a display.
12847 
12848       player.ready(bind(assertThisInitialized(_this), function () {
12849         if (player.tech_ && player.tech_.featuresNativeTextTracks) {
12850           this.hide();
12851           return;
12852         }
12853 
12854         player.on('fullscreenchange', updateDisplayHandler);
12855         player.on('playerresize', updateDisplayHandler);
12856         window$1.addEventListener('orientationchange', updateDisplayHandler);
12857         player.on('dispose', function () {
12858           return window$1.removeEventListener('orientationchange', updateDisplayHandler);
12859         });
12860         var tracks = this.options_.playerOptions.tracks || [];
12861 
12862         for (var i = 0; i < tracks.length; i++) {
12863           this.player_.addRemoteTextTrack(tracks[i], true);
12864         }
12865 
12866         this.preselectTrack();
12867       }));
12868       return _this;
12869     }
12870     /**
12871     * Preselect a track following this precedence:
12872     * - matches the previously selected {@link TextTrack}'s language and kind
12873     * - matches the previously selected {@link TextTrack}'s language only
12874     * - is the first default captions track
12875     * - is the first default descriptions track
12876     *
12877     * @listens Player#loadstart
12878     */
12879 
12880 
12881     var _proto = TextTrackDisplay.prototype;
12882 
12883     _proto.preselectTrack = function preselectTrack() {
12884       var modes = {
12885         captions: 1,
12886         subtitles: 1
12887       };
12888       var trackList = this.player_.textTracks();
12889       var userPref = this.player_.cache_.selectedLanguage;
12890       var firstDesc;
12891       var firstCaptions;
12892       var preferredTrack;
12893 
12894       for (var i = 0; i < trackList.length; i++) {
12895         var track = trackList[i];
12896 
12897         if (userPref && userPref.enabled && userPref.language && userPref.language === track.language && track.kind in modes) {
12898           // Always choose the track that matches both language and kind
12899           if (track.kind === userPref.kind) {
12900             preferredTrack = track; // or choose the first track that matches language
12901           } else if (!preferredTrack) {
12902             preferredTrack = track;
12903           } // clear everything if offTextTrackMenuItem was clicked
12904 
12905         } else if (userPref && !userPref.enabled) {
12906           preferredTrack = null;
12907           firstDesc = null;
12908           firstCaptions = null;
12909         } else if (track["default"]) {
12910           if (track.kind === 'descriptions' && !firstDesc) {
12911             firstDesc = track;
12912           } else if (track.kind in modes && !firstCaptions) {
12913             firstCaptions = track;
12914           }
12915         }
12916       } // The preferredTrack matches the user preference and takes
12917       // precedence over all the other tracks.
12918       // So, display the preferredTrack before the first default track
12919       // and the subtitles/captions track before the descriptions track
12920 
12921 
12922       if (preferredTrack) {
12923         preferredTrack.mode = 'showing';
12924       } else if (firstCaptions) {
12925         firstCaptions.mode = 'showing';
12926       } else if (firstDesc) {
12927         firstDesc.mode = 'showing';
12928       }
12929     }
12930     /**
12931      * Turn display of {@link TextTrack}'s from the current state into the other state.
12932      * There are only two states:
12933      * - 'shown'
12934      * - 'hidden'
12935      *
12936      * @listens Player#loadstart
12937      */
12938     ;
12939 
12940     _proto.toggleDisplay = function toggleDisplay() {
12941       if (this.player_.tech_ && this.player_.tech_.featuresNativeTextTracks) {
12942         this.hide();
12943       } else {
12944         this.show();
12945       }
12946     }
12947     /**
12948      * Create the {@link Component}'s DOM element.
12949      *
12950      * @return {Element}
12951      *         The element that was created.
12952      */
12953     ;
12954 
12955     _proto.createEl = function createEl() {
12956       return _Component.prototype.createEl.call(this, 'div', {
12957         className: 'vjs-text-track-display'
12958       }, {
12959         'aria-live': 'off',
12960         'aria-atomic': 'true'
12961       });
12962     }
12963     /**
12964      * Clear all displayed {@link TextTrack}s.
12965      */
12966     ;
12967 
12968     _proto.clearDisplay = function clearDisplay() {
12969       if (typeof window$1.WebVTT === 'function') {
12970         window$1.WebVTT.processCues(window$1, [], this.el_);
12971       }
12972     }
12973     /**
12974      * Update the displayed TextTrack when a either a {@link Player#texttrackchange} or
12975      * a {@link Player#fullscreenchange} is fired.
12976      *
12977      * @listens Player#texttrackchange
12978      * @listens Player#fullscreenchange
12979      */
12980     ;
12981 
12982     _proto.updateDisplay = function updateDisplay() {
12983       var tracks = this.player_.textTracks();
12984       var allowMultipleShowingTracks = this.options_.allowMultipleShowingTracks;
12985       this.clearDisplay();
12986 
12987       if (allowMultipleShowingTracks) {
12988         var showingTracks = [];
12989 
12990         for (var _i = 0; _i < tracks.length; ++_i) {
12991           var track = tracks[_i];
12992 
12993           if (track.mode !== 'showing') {
12994             continue;
12995           }
12996 
12997           showingTracks.push(track);
12998         }
12999 
13000         this.updateForTrack(showingTracks);
13001         return;
13002       } //  Track display prioritization model: if multiple tracks are 'showing',
13003       //  display the first 'subtitles' or 'captions' track which is 'showing',
13004       //  otherwise display the first 'descriptions' track which is 'showing'
13005 
13006 
13007       var descriptionsTrack = null;
13008       var captionsSubtitlesTrack = null;
13009       var i = tracks.length;
13010 
13011       while (i--) {
13012         var _track = tracks[i];
13013 
13014         if (_track.mode === 'showing') {
13015           if (_track.kind === 'descriptions') {
13016             descriptionsTrack = _track;
13017           } else {
13018             captionsSubtitlesTrack = _track;
13019           }
13020         }
13021       }
13022 
13023       if (captionsSubtitlesTrack) {
13024         if (this.getAttribute('aria-live') !== 'off') {
13025           this.setAttribute('aria-live', 'off');
13026         }
13027 
13028         this.updateForTrack(captionsSubtitlesTrack);
13029       } else if (descriptionsTrack) {
13030         if (this.getAttribute('aria-live') !== 'assertive') {
13031           this.setAttribute('aria-live', 'assertive');
13032         }
13033 
13034         this.updateForTrack(descriptionsTrack);
13035       }
13036     }
13037     /**
13038      * Style {@Link TextTrack} activeCues according to {@Link TextTrackSettings}.
13039      *
13040      * @param {TextTrack} track
13041      *        Text track object containing active cues to style.
13042      */
13043     ;
13044 
13045     _proto.updateDisplayState = function updateDisplayState(track) {
13046       var overrides = this.player_.textTrackSettings.getValues();
13047       var cues = track.activeCues;
13048       var i = cues.length;
13049 
13050       while (i--) {
13051         var cue = cues[i];
13052 
13053         if (!cue) {
13054           continue;
13055         }
13056 
13057         var cueDiv = cue.displayState;
13058 
13059         if (overrides.color) {
13060           cueDiv.firstChild.style.color = overrides.color;
13061         }
13062 
13063         if (overrides.textOpacity) {
13064           tryUpdateStyle(cueDiv.firstChild, 'color', constructColor(overrides.color || '#fff', overrides.textOpacity));
13065         }
13066 
13067         if (overrides.backgroundColor) {
13068           cueDiv.firstChild.style.backgroundColor = overrides.backgroundColor;
13069         }
13070 
13071         if (overrides.backgroundOpacity) {
13072           tryUpdateStyle(cueDiv.firstChild, 'backgroundColor', constructColor(overrides.backgroundColor || '#000', overrides.backgroundOpacity));
13073         }
13074 
13075         if (overrides.windowColor) {
13076           if (overrides.windowOpacity) {
13077             tryUpdateStyle(cueDiv, 'backgroundColor', constructColor(overrides.windowColor, overrides.windowOpacity));
13078           } else {
13079             cueDiv.style.backgroundColor = overrides.windowColor;
13080           }
13081         }
13082 
13083         if (overrides.edgeStyle) {
13084           if (overrides.edgeStyle === 'dropshadow') {
13085             cueDiv.firstChild.style.textShadow = "2px 2px 3px " + darkGray + ", 2px 2px 4px " + darkGray + ", 2px 2px 5px " + darkGray;
13086           } else if (overrides.edgeStyle === 'raised') {
13087             cueDiv.firstChild.style.textShadow = "1px 1px " + darkGray + ", 2px 2px " + darkGray + ", 3px 3px " + darkGray;
13088           } else if (overrides.edgeStyle === 'depressed') {
13089             cueDiv.firstChild.style.textShadow = "1px 1px " + lightGray + ", 0 1px " + lightGray + ", -1px -1px " + darkGray + ", 0 -1px " + darkGray;
13090           } else if (overrides.edgeStyle === 'uniform') {
13091             cueDiv.firstChild.style.textShadow = "0 0 4px " + darkGray + ", 0 0 4px " + darkGray + ", 0 0 4px " + darkGray + ", 0 0 4px " + darkGray;
13092           }
13093         }
13094 
13095         if (overrides.fontPercent && overrides.fontPercent !== 1) {
13096           var fontSize = window$1.parseFloat(cueDiv.style.fontSize);
13097           cueDiv.style.fontSize = fontSize * overrides.fontPercent + 'px';
13098           cueDiv.style.height = 'auto';
13099           cueDiv.style.top = 'auto';
13100           cueDiv.style.bottom = '2px';
13101         }
13102 
13103         if (overrides.fontFamily && overrides.fontFamily !== 'default') {
13104           if (overrides.fontFamily === 'small-caps') {
13105             cueDiv.firstChild.style.fontVariant = 'small-caps';
13106           } else {
13107             cueDiv.firstChild.style.fontFamily = fontMap[overrides.fontFamily];
13108           }
13109         }
13110       }
13111     }
13112     /**
13113      * Add an {@link TextTrack} to to the {@link Tech}s {@link TextTrackList}.
13114      *
13115      * @param {TextTrack|TextTrack[]} tracks
13116      *        Text track object or text track array to be added to the list.
13117      */
13118     ;
13119 
13120     _proto.updateForTrack = function updateForTrack(tracks) {
13121       if (!Array.isArray(tracks)) {
13122         tracks = [tracks];
13123       }
13124 
13125       if (typeof window$1.WebVTT !== 'function' || tracks.every(function (track) {
13126         return !track.activeCues;
13127       })) {
13128         return;
13129       }
13130 
13131       var cues = []; // push all active track cues
13132 
13133       for (var i = 0; i < tracks.length; ++i) {
13134         var track = tracks[i];
13135 
13136         for (var j = 0; j < track.activeCues.length; ++j) {
13137           cues.push(track.activeCues[j]);
13138         }
13139       } // removes all cues before it processes new ones
13140 
13141 
13142       window$1.WebVTT.processCues(window$1, cues, this.el_); // add unique class to each language text track & add settings styling if necessary
13143 
13144       for (var _i2 = 0; _i2 < tracks.length; ++_i2) {
13145         var _track2 = tracks[_i2];
13146 
13147         for (var _j = 0; _j < _track2.activeCues.length; ++_j) {
13148           var cueEl = _track2.activeCues[_j].displayState;
13149           addClass(cueEl, 'vjs-text-track-cue');
13150           addClass(cueEl, 'vjs-text-track-cue-' + (_track2.language ? _track2.language : _i2));
13151         }
13152 
13153         if (this.player_.textTrackSettings) {
13154           this.updateDisplayState(_track2);
13155         }
13156       }
13157     };
13158 
13159     return TextTrackDisplay;
13160   }(Component);
13161 
13162   Component.registerComponent('TextTrackDisplay', TextTrackDisplay);
13163 
13164   /**
13165    * A loading spinner for use during waiting/loading events.
13166    *
13167    * @extends Component
13168    */
13169 
13170   var LoadingSpinner =
13171   /*#__PURE__*/
13172   function (_Component) {
13173     inheritsLoose(LoadingSpinner, _Component);
13174 
13175     function LoadingSpinner() {
13176       return _Component.apply(this, arguments) || this;
13177     }
13178 
13179     var _proto = LoadingSpinner.prototype;
13180 
13181     /**
13182      * Create the `LoadingSpinner`s DOM element.
13183      *
13184      * @return {Element}
13185      *         The dom element that gets created.
13186      */
13187     _proto.createEl = function createEl$1() {
13188       var isAudio = this.player_.isAudio();
13189       var playerType = this.localize(isAudio ? 'Audio Player' : 'Video Player');
13190       var controlText = createEl('span', {
13191         className: 'vjs-control-text',
13192         innerHTML: this.localize('{1} is loading.', [playerType])
13193       });
13194 
13195       var el = _Component.prototype.createEl.call(this, 'div', {
13196         className: 'vjs-loading-spinner',
13197         dir: 'ltr'
13198       });
13199 
13200       el.appendChild(controlText);
13201       return el;
13202     };
13203 
13204     return LoadingSpinner;
13205   }(Component);
13206 
13207   Component.registerComponent('LoadingSpinner', LoadingSpinner);
13208 
13209   /**
13210    * Base class for all buttons.
13211    *
13212    * @extends ClickableComponent
13213    */
13214 
13215   var Button =
13216   /*#__PURE__*/
13217   function (_ClickableComponent) {
13218     inheritsLoose(Button, _ClickableComponent);
13219 
13220     function Button() {
13221       return _ClickableComponent.apply(this, arguments) || this;
13222     }
13223 
13224     var _proto = Button.prototype;
13225 
13226     /**
13227      * Create the `Button`s DOM element.
13228      *
13229      * @param {string} [tag="button"]
13230      *        The element's node type. This argument is IGNORED: no matter what
13231      *        is passed, it will always create a `button` element.
13232      *
13233      * @param {Object} [props={}]
13234      *        An object of properties that should be set on the element.
13235      *
13236      * @param {Object} [attributes={}]
13237      *        An object of attributes that should be set on the element.
13238      *
13239      * @return {Element}
13240      *         The element that gets created.
13241      */
13242     _proto.createEl = function createEl(tag, props, attributes) {
13243       if (props === void 0) {
13244         props = {};
13245       }
13246 
13247       if (attributes === void 0) {
13248         attributes = {};
13249       }
13250 
13251       tag = 'button';
13252       props = assign({
13253         innerHTML: '<span aria-hidden="true" class="vjs-icon-placeholder"></span>',
13254         className: this.buildCSSClass()
13255       }, props); // Add attributes for button element
13256 
13257       attributes = assign({
13258         // Necessary since the default button type is "submit"
13259         type: 'button'
13260       }, attributes);
13261       var el = Component.prototype.createEl.call(this, tag, props, attributes);
13262       this.createControlTextEl(el);
13263       return el;
13264     }
13265     /**
13266      * Add a child `Component` inside of this `Button`.
13267      *
13268      * @param {string|Component} child
13269      *        The name or instance of a child to add.
13270      *
13271      * @param {Object} [options={}]
13272      *        The key/value store of options that will get passed to children of
13273      *        the child.
13274      *
13275      * @return {Component}
13276      *         The `Component` that gets added as a child. When using a string the
13277      *         `Component` will get created by this process.
13278      *
13279      * @deprecated since version 5
13280      */
13281     ;
13282 
13283     _proto.addChild = function addChild(child, options) {
13284       if (options === void 0) {
13285         options = {};
13286       }
13287 
13288       var className = this.constructor.name;
13289       log.warn("Adding an actionable (user controllable) child to a Button (" + className + ") is not supported; use a ClickableComponent instead."); // Avoid the error message generated by ClickableComponent's addChild method
13290 
13291       return Component.prototype.addChild.call(this, child, options);
13292     }
13293     /**
13294      * Enable the `Button` element so that it can be activated or clicked. Use this with
13295      * {@link Button#disable}.
13296      */
13297     ;
13298 
13299     _proto.enable = function enable() {
13300       _ClickableComponent.prototype.enable.call(this);
13301 
13302       this.el_.removeAttribute('disabled');
13303     }
13304     /**
13305      * Disable the `Button` element so that it cannot be activated or clicked. Use this with
13306      * {@link Button#enable}.
13307      */
13308     ;
13309 
13310     _proto.disable = function disable() {
13311       _ClickableComponent.prototype.disable.call(this);
13312 
13313       this.el_.setAttribute('disabled', 'disabled');
13314     }
13315     /**
13316      * This gets called when a `Button` has focus and `keydown` is triggered via a key
13317      * press.
13318      *
13319      * @param {EventTarget~Event} event
13320      *        The event that caused this function to get called.
13321      *
13322      * @listens keydown
13323      */
13324     ;
13325 
13326     _proto.handleKeyDown = function handleKeyDown(event) {
13327       // Ignore Space or Enter key operation, which is handled by the browser for
13328       // a button - though not for its super class, ClickableComponent. Also,
13329       // prevent the event from propagating through the DOM and triggering Player
13330       // hotkeys. We do not preventDefault here because we _want_ the browser to
13331       // handle it.
13332       if (keycode.isEventKey(event, 'Space') || keycode.isEventKey(event, 'Enter')) {
13333         event.stopPropagation();
13334         return;
13335       } // Pass keypress handling up for unsupported keys
13336 
13337 
13338       _ClickableComponent.prototype.handleKeyDown.call(this, event);
13339     };
13340 
13341     return Button;
13342   }(ClickableComponent);
13343 
13344   Component.registerComponent('Button', Button);
13345 
13346   /**
13347    * The initial play button that shows before the video has played. The hiding of the
13348    * `BigPlayButton` get done via CSS and `Player` states.
13349    *
13350    * @extends Button
13351    */
13352 
13353   var BigPlayButton =
13354   /*#__PURE__*/
13355   function (_Button) {
13356     inheritsLoose(BigPlayButton, _Button);
13357 
13358     function BigPlayButton(player, options) {
13359       var _this;
13360 
13361       _this = _Button.call(this, player, options) || this;
13362       _this.mouseused_ = false;
13363 
13364       _this.on('mousedown', _this.handleMouseDown);
13365 
13366       return _this;
13367     }
13368     /**
13369      * Builds the default DOM `className`.
13370      *
13371      * @return {string}
13372      *         The DOM `className` for this object. Always returns 'vjs-big-play-button'.
13373      */
13374 
13375 
13376     var _proto = BigPlayButton.prototype;
13377 
13378     _proto.buildCSSClass = function buildCSSClass() {
13379       return 'vjs-big-play-button';
13380     }
13381     /**
13382      * This gets called when a `BigPlayButton` "clicked". See {@link ClickableComponent}
13383      * for more detailed information on what a click can be.
13384      *
13385      * @param {EventTarget~Event} event
13386      *        The `keydown`, `tap`, or `click` event that caused this function to be
13387      *        called.
13388      *
13389      * @listens tap
13390      * @listens click
13391      */
13392     ;
13393 
13394     _proto.handleClick = function handleClick(event) {
13395       var playPromise = this.player_.play(); // exit early if clicked via the mouse
13396 
13397       if (this.mouseused_ && event.clientX && event.clientY) {
13398         var sourceIsEncrypted = this.player_.usingPlugin('eme') && this.player_.eme.sessions && this.player_.eme.sessions.length > 0;
13399         silencePromise(playPromise);
13400 
13401         if (this.player_.tech(true) && // We've observed a bug in IE and Edge when playing back DRM content where
13402         // calling .focus() on the video element causes the video to go black,
13403         // so we avoid it in that specific case
13404         !((IE_VERSION || IS_EDGE) && sourceIsEncrypted)) {
13405           this.player_.tech(true).focus();
13406         }
13407 
13408         return;
13409       }
13410 
13411       var cb = this.player_.getChild('controlBar');
13412       var playToggle = cb && cb.getChild('playToggle');
13413 
13414       if (!playToggle) {
13415         this.player_.tech(true).focus();
13416         return;
13417       }
13418 
13419       var playFocus = function playFocus() {
13420         return playToggle.focus();
13421       };
13422 
13423       if (isPromise(playPromise)) {
13424         playPromise.then(playFocus, function () {});
13425       } else {
13426         this.setTimeout(playFocus, 1);
13427       }
13428     };
13429 
13430     _proto.handleKeyDown = function handleKeyDown(event) {
13431       this.mouseused_ = false;
13432 
13433       _Button.prototype.handleKeyDown.call(this, event);
13434     };
13435 
13436     _proto.handleMouseDown = function handleMouseDown(event) {
13437       this.mouseused_ = true;
13438     };
13439 
13440     return BigPlayButton;
13441   }(Button);
13442   /**
13443    * The text that should display over the `BigPlayButton`s controls. Added to for localization.
13444    *
13445    * @type {string}
13446    * @private
13447    */
13448 
13449 
13450   BigPlayButton.prototype.controlText_ = 'Play Video';
13451   Component.registerComponent('BigPlayButton', BigPlayButton);
13452 
13453   /**
13454    * The `CloseButton` is a `{@link Button}` that fires a `close` event when
13455    * it gets clicked.
13456    *
13457    * @extends Button
13458    */
13459 
13460   var CloseButton =
13461   /*#__PURE__*/
13462   function (_Button) {
13463     inheritsLoose(CloseButton, _Button);
13464 
13465     /**
13466     * Creates an instance of the this class.
13467     *
13468     * @param  {Player} player
13469     *         The `Player` that this class should be attached to.
13470     *
13471     * @param  {Object} [options]
13472     *         The key/value store of player options.
13473     */
13474     function CloseButton(player, options) {
13475       var _this;
13476 
13477       _this = _Button.call(this, player, options) || this;
13478 
13479       _this.controlText(options && options.controlText || _this.localize('Close'));
13480 
13481       return _this;
13482     }
13483     /**
13484     * Builds the default DOM `className`.
13485     *
13486     * @return {string}
13487     *         The DOM `className` for this object.
13488     */
13489 
13490 
13491     var _proto = CloseButton.prototype;
13492 
13493     _proto.buildCSSClass = function buildCSSClass() {
13494       return "vjs-close-button " + _Button.prototype.buildCSSClass.call(this);
13495     }
13496     /**
13497      * This gets called when a `CloseButton` gets clicked. See
13498      * {@link ClickableComponent#handleClick} for more information on when
13499      * this will be triggered
13500      *
13501      * @param {EventTarget~Event} event
13502      *        The `keydown`, `tap`, or `click` event that caused this function to be
13503      *        called.
13504      *
13505      * @listens tap
13506      * @listens click
13507      * @fires CloseButton#close
13508      */
13509     ;
13510 
13511     _proto.handleClick = function handleClick(event) {
13512       /**
13513        * Triggered when the a `CloseButton` is clicked.
13514        *
13515        * @event CloseButton#close
13516        * @type {EventTarget~Event}
13517        *
13518        * @property {boolean} [bubbles=false]
13519        *           set to false so that the close event does not
13520        *           bubble up to parents if there is no listener
13521        */
13522       this.trigger({
13523         type: 'close',
13524         bubbles: false
13525       });
13526     }
13527     /**
13528      * Event handler that is called when a `CloseButton` receives a
13529      * `keydown` event.
13530      *
13531      * By default, if the key is Esc, it will trigger a `click` event.
13532      *
13533      * @param {EventTarget~Event} event
13534      *        The `keydown` event that caused this function to be called.
13535      *
13536      * @listens keydown
13537      */
13538     ;
13539 
13540     _proto.handleKeyDown = function handleKeyDown(event) {
13541       // Esc button will trigger `click` event
13542       if (keycode.isEventKey(event, 'Esc')) {
13543         event.preventDefault();
13544         event.stopPropagation();
13545         this.trigger('click');
13546       } else {
13547         // Pass keypress handling up for unsupported keys
13548         _Button.prototype.handleKeyDown.call(this, event);
13549       }
13550     };
13551 
13552     return CloseButton;
13553   }(Button);
13554 
13555   Component.registerComponent('CloseButton', CloseButton);
13556 
13557   /**
13558    * Button to toggle between play and pause.
13559    *
13560    * @extends Button
13561    */
13562 
13563   var PlayToggle =
13564   /*#__PURE__*/
13565   function (_Button) {
13566     inheritsLoose(PlayToggle, _Button);
13567 
13568     /**
13569      * Creates an instance of this class.
13570      *
13571      * @param {Player} player
13572      *        The `Player` that this class should be attached to.
13573      *
13574      * @param {Object} [options={}]
13575      *        The key/value store of player options.
13576      */
13577     function PlayToggle(player, options) {
13578       var _this;
13579 
13580       if (options === void 0) {
13581         options = {};
13582       }
13583 
13584       _this = _Button.call(this, player, options) || this; // show or hide replay icon
13585 
13586       options.replay = options.replay === undefined || options.replay;
13587 
13588       _this.on(player, 'play', _this.handlePlay);
13589 
13590       _this.on(player, 'pause', _this.handlePause);
13591 
13592       if (options.replay) {
13593         _this.on(player, 'ended', _this.handleEnded);
13594       }
13595 
13596       return _this;
13597     }
13598     /**
13599      * Builds the default DOM `className`.
13600      *
13601      * @return {string}
13602      *         The DOM `className` for this object.
13603      */
13604 
13605 
13606     var _proto = PlayToggle.prototype;
13607 
13608     _proto.buildCSSClass = function buildCSSClass() {
13609       return "vjs-play-control " + _Button.prototype.buildCSSClass.call(this);
13610     }
13611     /**
13612      * This gets called when an `PlayToggle` is "clicked". See
13613      * {@link ClickableComponent} for more detailed information on what a click can be.
13614      *
13615      * @param {EventTarget~Event} [event]
13616      *        The `keydown`, `tap`, or `click` event that caused this function to be
13617      *        called.
13618      *
13619      * @listens tap
13620      * @listens click
13621      */
13622     ;
13623 
13624     _proto.handleClick = function handleClick(event) {
13625       if (this.player_.paused()) {
13626         this.player_.play();
13627       } else {
13628         this.player_.pause();
13629       }
13630     }
13631     /**
13632      * This gets called once after the video has ended and the user seeks so that
13633      * we can change the replay button back to a play button.
13634      *
13635      * @param {EventTarget~Event} [event]
13636      *        The event that caused this function to run.
13637      *
13638      * @listens Player#seeked
13639      */
13640     ;
13641 
13642     _proto.handleSeeked = function handleSeeked(event) {
13643       this.removeClass('vjs-ended');
13644 
13645       if (this.player_.paused()) {
13646         this.handlePause(event);
13647       } else {
13648         this.handlePlay(event);
13649       }
13650     }
13651     /**
13652      * Add the vjs-playing class to the element so it can change appearance.
13653      *
13654      * @param {EventTarget~Event} [event]
13655      *        The event that caused this function to run.
13656      *
13657      * @listens Player#play
13658      */
13659     ;
13660 
13661     _proto.handlePlay = function handlePlay(event) {
13662       this.removeClass('vjs-ended');
13663       this.removeClass('vjs-paused');
13664       this.addClass('vjs-playing'); // change the button text to "Pause"
13665 
13666       this.controlText('Pause');
13667     }
13668     /**
13669      * Add the vjs-paused class to the element so it can change appearance.
13670      *
13671      * @param {EventTarget~Event} [event]
13672      *        The event that caused this function to run.
13673      *
13674      * @listens Player#pause
13675      */
13676     ;
13677 
13678     _proto.handlePause = function handlePause(event) {
13679       this.removeClass('vjs-playing');
13680       this.addClass('vjs-paused'); // change the button text to "Play"
13681 
13682       this.controlText('Play');
13683     }
13684     /**
13685      * Add the vjs-ended class to the element so it can change appearance
13686      *
13687      * @param {EventTarget~Event} [event]
13688      *        The event that caused this function to run.
13689      *
13690      * @listens Player#ended
13691      */
13692     ;
13693 
13694     _proto.handleEnded = function handleEnded(event) {
13695       this.removeClass('vjs-playing');
13696       this.addClass('vjs-ended'); // change the button text to "Replay"
13697 
13698       this.controlText('Replay'); // on the next seek remove the replay button
13699 
13700       this.one(this.player_, 'seeked', this.handleSeeked);
13701     };
13702 
13703     return PlayToggle;
13704   }(Button);
13705   /**
13706    * The text that should display over the `PlayToggle`s controls. Added for localization.
13707    *
13708    * @type {string}
13709    * @private
13710    */
13711 
13712 
13713   PlayToggle.prototype.controlText_ = 'Play';
13714   Component.registerComponent('PlayToggle', PlayToggle);
13715 
13716   /**
13717    * @file format-time.js
13718    * @module format-time
13719    */
13720 
13721   /**
13722    * Format seconds as a time string, H:MM:SS or M:SS. Supplying a guide (in
13723    * seconds) will force a number of leading zeros to cover the length of the
13724    * guide.
13725    *
13726    * @private
13727    * @param  {number} seconds
13728    *         Number of seconds to be turned into a string
13729    *
13730    * @param  {number} guide
13731    *         Number (in seconds) to model the string after
13732    *
13733    * @return {string}
13734    *         Time formatted as H:MM:SS or M:SS
13735    */
13736   var defaultImplementation = function defaultImplementation(seconds, guide) {
13737     seconds = seconds < 0 ? 0 : seconds;
13738     var s = Math.floor(seconds % 60);
13739     var m = Math.floor(seconds / 60 % 60);
13740     var h = Math.floor(seconds / 3600);
13741     var gm = Math.floor(guide / 60 % 60);
13742     var gh = Math.floor(guide / 3600); // handle invalid times
13743 
13744     if (isNaN(seconds) || seconds === Infinity) {
13745       // '-' is false for all relational operators (e.g. <, >=) so this setting
13746       // will add the minimum number of fields specified by the guide
13747       h = m = s = '-';
13748     } // Check if we need to show hours
13749 
13750 
13751     h = h > 0 || gh > 0 ? h + ':' : ''; // If hours are showing, we may need to add a leading zero.
13752     // Always show at least one digit of minutes.
13753 
13754     m = ((h || gm >= 10) && m < 10 ? '0' + m : m) + ':'; // Check if leading zero is need for seconds
13755 
13756     s = s < 10 ? '0' + s : s;
13757     return h + m + s;
13758   }; // Internal pointer to the current implementation.
13759 
13760 
13761   var implementation = defaultImplementation;
13762   /**
13763    * Replaces the default formatTime implementation with a custom implementation.
13764    *
13765    * @param {Function} customImplementation
13766    *        A function which will be used in place of the default formatTime
13767    *        implementation. Will receive the current time in seconds and the
13768    *        guide (in seconds) as arguments.
13769    */
13770 
13771   function setFormatTime(customImplementation) {
13772     implementation = customImplementation;
13773   }
13774   /**
13775    * Resets formatTime to the default implementation.
13776    */
13777 
13778   function resetFormatTime() {
13779     implementation = defaultImplementation;
13780   }
13781   /**
13782    * Delegates to either the default time formatting function or a custom
13783    * function supplied via `setFormatTime`.
13784    *
13785    * Formats seconds as a time string (H:MM:SS or M:SS). Supplying a
13786    * guide (in seconds) will force a number of leading zeros to cover the
13787    * length of the guide.
13788    *
13789    * @static
13790    * @example  formatTime(125, 600) === "02:05"
13791    * @param    {number} seconds
13792    *           Number of seconds to be turned into a string
13793    *
13794    * @param    {number} guide
13795    *           Number (in seconds) to model the string after
13796    *
13797    * @return   {string}
13798    *           Time formatted as H:MM:SS or M:SS
13799    */
13800 
13801   function formatTime(seconds, guide) {
13802     if (guide === void 0) {
13803       guide = seconds;
13804     }
13805 
13806     return implementation(seconds, guide);
13807   }
13808 
13809   /**
13810    * Displays time information about the video
13811    *
13812    * @extends Component
13813    */
13814 
13815   var TimeDisplay =
13816   /*#__PURE__*/
13817   function (_Component) {
13818     inheritsLoose(TimeDisplay, _Component);
13819 
13820     /**
13821      * Creates an instance of this class.
13822      *
13823      * @param {Player} player
13824      *        The `Player` that this class should be attached to.
13825      *
13826      * @param {Object} [options]
13827      *        The key/value store of player options.
13828      */
13829     function TimeDisplay(player, options) {
13830       var _this;
13831 
13832       _this = _Component.call(this, player, options) || this;
13833 
13834       _this.on(player, ['timeupdate', 'ended'], _this.updateContent);
13835 
13836       _this.updateTextNode_();
13837 
13838       return _this;
13839     }
13840     /**
13841      * Create the `Component`'s DOM element
13842      *
13843      * @return {Element}
13844      *         The element that was created.
13845      */
13846 
13847 
13848     var _proto = TimeDisplay.prototype;
13849 
13850     _proto.createEl = function createEl$1() {
13851       var className = this.buildCSSClass();
13852 
13853       var el = _Component.prototype.createEl.call(this, 'div', {
13854         className: className + " vjs-time-control vjs-control",
13855         innerHTML: "<span class=\"vjs-control-text\" role=\"presentation\">" + this.localize(this.labelText_) + "\xA0</span>"
13856       });
13857 
13858       this.contentEl_ = createEl('span', {
13859         className: className + "-display"
13860       }, {
13861         // tell screen readers not to automatically read the time as it changes
13862         'aria-live': 'off',
13863         // span elements have no implicit role, but some screen readers (notably VoiceOver)
13864         // treat them as a break between items in the DOM when using arrow keys
13865         // (or left-to-right swipes on iOS) to read contents of a page. Using
13866         // role='presentation' causes VoiceOver to NOT treat this span as a break.
13867         'role': 'presentation'
13868       });
13869       el.appendChild(this.contentEl_);
13870       return el;
13871     };
13872 
13873     _proto.dispose = function dispose() {
13874       this.contentEl_ = null;
13875       this.textNode_ = null;
13876 
13877       _Component.prototype.dispose.call(this);
13878     }
13879     /**
13880      * Updates the time display text node with a new time
13881      *
13882      * @param {number} [time=0] the time to update to
13883      *
13884      * @private
13885      */
13886     ;
13887 
13888     _proto.updateTextNode_ = function updateTextNode_(time) {
13889       var _this2 = this;
13890 
13891       if (time === void 0) {
13892         time = 0;
13893       }
13894 
13895       time = formatTime(time);
13896 
13897       if (this.formattedTime_ === time) {
13898         return;
13899       }
13900 
13901       this.formattedTime_ = time;
13902       this.requestAnimationFrame(function () {
13903         if (!_this2.contentEl_) {
13904           return;
13905         }
13906 
13907         var oldNode = _this2.textNode_;
13908         _this2.textNode_ = document.createTextNode(_this2.formattedTime_);
13909 
13910         if (!_this2.textNode_) {
13911           return;
13912         }
13913 
13914         if (oldNode) {
13915           _this2.contentEl_.replaceChild(_this2.textNode_, oldNode);
13916         } else {
13917           _this2.contentEl_.appendChild(_this2.textNode_);
13918         }
13919       });
13920     }
13921     /**
13922      * To be filled out in the child class, should update the displayed time
13923      * in accordance with the fact that the current time has changed.
13924      *
13925      * @param {EventTarget~Event} [event]
13926      *        The `timeupdate`  event that caused this to run.
13927      *
13928      * @listens Player#timeupdate
13929      */
13930     ;
13931 
13932     _proto.updateContent = function updateContent(event) {};
13933 
13934     return TimeDisplay;
13935   }(Component);
13936   /**
13937    * The text that is added to the `TimeDisplay` for screen reader users.
13938    *
13939    * @type {string}
13940    * @private
13941    */
13942 
13943 
13944   TimeDisplay.prototype.labelText_ = 'Time';
13945   /**
13946    * The text that should display over the `TimeDisplay`s controls. Added to for localization.
13947    *
13948    * @type {string}
13949    * @private
13950    *
13951    * @deprecated in v7; controlText_ is not used in non-active display Components
13952    */
13953 
13954   TimeDisplay.prototype.controlText_ = 'Time';
13955   Component.registerComponent('TimeDisplay', TimeDisplay);
13956 
13957   /**
13958    * Displays the current time
13959    *
13960    * @extends Component
13961    */
13962 
13963   var CurrentTimeDisplay =
13964   /*#__PURE__*/
13965   function (_TimeDisplay) {
13966     inheritsLoose(CurrentTimeDisplay, _TimeDisplay);
13967 
13968     function CurrentTimeDisplay() {
13969       return _TimeDisplay.apply(this, arguments) || this;
13970     }
13971 
13972     var _proto = CurrentTimeDisplay.prototype;
13973 
13974     /**
13975      * Builds the default DOM `className`.
13976      *
13977      * @return {string}
13978      *         The DOM `className` for this object.
13979      */
13980     _proto.buildCSSClass = function buildCSSClass() {
13981       return 'vjs-current-time';
13982     }
13983     /**
13984      * Update current time display
13985      *
13986      * @param {EventTarget~Event} [event]
13987      *        The `timeupdate` event that caused this function to run.
13988      *
13989      * @listens Player#timeupdate
13990      */
13991     ;
13992 
13993     _proto.updateContent = function updateContent(event) {
13994       // Allows for smooth scrubbing, when player can't keep up.
13995       var time;
13996 
13997       if (this.player_.ended()) {
13998         time = this.player_.duration();
13999       } else {
14000         time = this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime();
14001       }
14002 
14003       this.updateTextNode_(time);
14004     };
14005 
14006     return CurrentTimeDisplay;
14007   }(TimeDisplay);
14008   /**
14009    * The text that is added to the `CurrentTimeDisplay` for screen reader users.
14010    *
14011    * @type {string}
14012    * @private
14013    */
14014 
14015 
14016   CurrentTimeDisplay.prototype.labelText_ = 'Current Time';
14017   /**
14018    * The text that should display over the `CurrentTimeDisplay`s controls. Added to for localization.
14019    *
14020    * @type {string}
14021    * @private
14022    *
14023    * @deprecated in v7; controlText_ is not used in non-active display Components
14024    */
14025 
14026   CurrentTimeDisplay.prototype.controlText_ = 'Current Time';
14027   Component.registerComponent('CurrentTimeDisplay', CurrentTimeDisplay);
14028 
14029   /**
14030    * Displays the duration
14031    *
14032    * @extends Component
14033    */
14034 
14035   var DurationDisplay =
14036   /*#__PURE__*/
14037   function (_TimeDisplay) {
14038     inheritsLoose(DurationDisplay, _TimeDisplay);
14039 
14040     /**
14041      * Creates an instance of this class.
14042      *
14043      * @param {Player} player
14044      *        The `Player` that this class should be attached to.
14045      *
14046      * @param {Object} [options]
14047      *        The key/value store of player options.
14048      */
14049     function DurationDisplay(player, options) {
14050       var _this;
14051 
14052       _this = _TimeDisplay.call(this, player, options) || this; // we do not want to/need to throttle duration changes,
14053       // as they should always display the changed duration as
14054       // it has changed
14055 
14056       _this.on(player, 'durationchange', _this.updateContent); // Listen to loadstart because the player duration is reset when a new media element is loaded,
14057       // but the durationchange on the user agent will not fire.
14058       // @see [Spec]{@link https://www.w3.org/TR/2011/WD-html5-20110113/video.html#media-element-load-algorithm}
14059 
14060 
14061       _this.on(player, 'loadstart', _this.updateContent); // Also listen for timeupdate (in the parent) and loadedmetadata because removing those
14062       // listeners could have broken dependent applications/libraries. These
14063       // can likely be removed for 7.0.
14064 
14065 
14066       _this.on(player, 'loadedmetadata', _this.updateContent);
14067 
14068       return _this;
14069     }
14070     /**
14071      * Builds the default DOM `className`.
14072      *
14073      * @return {string}
14074      *         The DOM `className` for this object.
14075      */
14076 
14077 
14078     var _proto = DurationDisplay.prototype;
14079 
14080     _proto.buildCSSClass = function buildCSSClass() {
14081       return 'vjs-duration';
14082     }
14083     /**
14084      * Update duration time display.
14085      *
14086      * @param {EventTarget~Event} [event]
14087      *        The `durationchange`, `timeupdate`, or `loadedmetadata` event that caused
14088      *        this function to be called.
14089      *
14090      * @listens Player#durationchange
14091      * @listens Player#timeupdate
14092      * @listens Player#loadedmetadata
14093      */
14094     ;
14095 
14096     _proto.updateContent = function updateContent(event) {
14097       var duration = this.player_.duration();
14098       this.updateTextNode_(duration);
14099     };
14100 
14101     return DurationDisplay;
14102   }(TimeDisplay);
14103   /**
14104    * The text that is added to the `DurationDisplay` for screen reader users.
14105    *
14106    * @type {string}
14107    * @private
14108    */
14109 
14110 
14111   DurationDisplay.prototype.labelText_ = 'Duration';
14112   /**
14113    * The text that should display over the `DurationDisplay`s controls. Added to for localization.
14114    *
14115    * @type {string}
14116    * @private
14117    *
14118    * @deprecated in v7; controlText_ is not used in non-active display Components
14119    */
14120 
14121   DurationDisplay.prototype.controlText_ = 'Duration';
14122   Component.registerComponent('DurationDisplay', DurationDisplay);
14123 
14124   /**
14125    * The separator between the current time and duration.
14126    * Can be hidden if it's not needed in the design.
14127    *
14128    * @extends Component
14129    */
14130 
14131   var TimeDivider =
14132   /*#__PURE__*/
14133   function (_Component) {
14134     inheritsLoose(TimeDivider, _Component);
14135 
14136     function TimeDivider() {
14137       return _Component.apply(this, arguments) || this;
14138     }
14139 
14140     var _proto = TimeDivider.prototype;
14141 
14142     /**
14143      * Create the component's DOM element
14144      *
14145      * @return {Element}
14146      *         The element that was created.
14147      */
14148     _proto.createEl = function createEl() {
14149       return _Component.prototype.createEl.call(this, 'div', {
14150         className: 'vjs-time-control vjs-time-divider',
14151         innerHTML: '<div><span>/</span></div>'
14152       }, {
14153         // this element and its contents can be hidden from assistive techs since
14154         // it is made extraneous by the announcement of the control text
14155         // for the current time and duration displays
14156         'aria-hidden': true
14157       });
14158     };
14159 
14160     return TimeDivider;
14161   }(Component);
14162 
14163   Component.registerComponent('TimeDivider', TimeDivider);
14164 
14165   /**
14166    * Displays the time left in the video
14167    *
14168    * @extends Component
14169    */
14170 
14171   var RemainingTimeDisplay =
14172   /*#__PURE__*/
14173   function (_TimeDisplay) {
14174     inheritsLoose(RemainingTimeDisplay, _TimeDisplay);
14175 
14176     /**
14177      * Creates an instance of this class.
14178      *
14179      * @param {Player} player
14180      *        The `Player` that this class should be attached to.
14181      *
14182      * @param {Object} [options]
14183      *        The key/value store of player options.
14184      */
14185     function RemainingTimeDisplay(player, options) {
14186       var _this;
14187 
14188       _this = _TimeDisplay.call(this, player, options) || this;
14189 
14190       _this.on(player, 'durationchange', _this.updateContent);
14191 
14192       return _this;
14193     }
14194     /**
14195      * Builds the default DOM `className`.
14196      *
14197      * @return {string}
14198      *         The DOM `className` for this object.
14199      */
14200 
14201 
14202     var _proto = RemainingTimeDisplay.prototype;
14203 
14204     _proto.buildCSSClass = function buildCSSClass() {
14205       return 'vjs-remaining-time';
14206     }
14207     /**
14208      * Create the `Component`'s DOM element with the "minus" characted prepend to the time
14209      *
14210      * @return {Element}
14211      *         The element that was created.
14212      */
14213     ;
14214 
14215     _proto.createEl = function createEl$1() {
14216       var el = _TimeDisplay.prototype.createEl.call(this);
14217 
14218       el.insertBefore(createEl('span', {}, {
14219         'aria-hidden': true
14220       }, '-'), this.contentEl_);
14221       return el;
14222     }
14223     /**
14224      * Update remaining time display.
14225      *
14226      * @param {EventTarget~Event} [event]
14227      *        The `timeupdate` or `durationchange` event that caused this to run.
14228      *
14229      * @listens Player#timeupdate
14230      * @listens Player#durationchange
14231      */
14232     ;
14233 
14234     _proto.updateContent = function updateContent(event) {
14235       if (typeof this.player_.duration() !== 'number') {
14236         return;
14237       }
14238 
14239       var time; // @deprecated We should only use remainingTimeDisplay
14240       // as of video.js 7
14241 
14242       if (this.player_.ended()) {
14243         time = 0;
14244       } else if (this.player_.remainingTimeDisplay) {
14245         time = this.player_.remainingTimeDisplay();
14246       } else {
14247         time = this.player_.remainingTime();
14248       }
14249 
14250       this.updateTextNode_(time);
14251     };
14252 
14253     return RemainingTimeDisplay;
14254   }(TimeDisplay);
14255   /**
14256    * The text that is added to the `RemainingTimeDisplay` for screen reader users.
14257    *
14258    * @type {string}
14259    * @private
14260    */
14261 
14262 
14263   RemainingTimeDisplay.prototype.labelText_ = 'Remaining Time';
14264   /**
14265    * The text that should display over the `RemainingTimeDisplay`s controls. Added to for localization.
14266    *
14267    * @type {string}
14268    * @private
14269    *
14270    * @deprecated in v7; controlText_ is not used in non-active display Components
14271    */
14272 
14273   RemainingTimeDisplay.prototype.controlText_ = 'Remaining Time';
14274   Component.registerComponent('RemainingTimeDisplay', RemainingTimeDisplay);
14275 
14276   /**
14277    * Displays the live indicator when duration is Infinity.
14278    *
14279    * @extends Component
14280    */
14281 
14282   var LiveDisplay =
14283   /*#__PURE__*/
14284   function (_Component) {
14285     inheritsLoose(LiveDisplay, _Component);
14286 
14287     /**
14288      * Creates an instance of this class.
14289      *
14290      * @param {Player} player
14291      *        The `Player` that this class should be attached to.
14292      *
14293      * @param {Object} [options]
14294      *        The key/value store of player options.
14295      */
14296     function LiveDisplay(player, options) {
14297       var _this;
14298 
14299       _this = _Component.call(this, player, options) || this;
14300 
14301       _this.updateShowing();
14302 
14303       _this.on(_this.player(), 'durationchange', _this.updateShowing);
14304 
14305       return _this;
14306     }
14307     /**
14308      * Create the `Component`'s DOM element
14309      *
14310      * @return {Element}
14311      *         The element that was created.
14312      */
14313 
14314 
14315     var _proto = LiveDisplay.prototype;
14316 
14317     _proto.createEl = function createEl$1() {
14318       var el = _Component.prototype.createEl.call(this, 'div', {
14319         className: 'vjs-live-control vjs-control'
14320       });
14321 
14322       this.contentEl_ = createEl('div', {
14323         className: 'vjs-live-display',
14324         innerHTML: "<span class=\"vjs-control-text\">" + this.localize('Stream Type') + "\xA0</span>" + this.localize('LIVE')
14325       }, {
14326         'aria-live': 'off'
14327       });
14328       el.appendChild(this.contentEl_);
14329       return el;
14330     };
14331 
14332     _proto.dispose = function dispose() {
14333       this.contentEl_ = null;
14334 
14335       _Component.prototype.dispose.call(this);
14336     }
14337     /**
14338      * Check the duration to see if the LiveDisplay should be showing or not. Then show/hide
14339      * it accordingly
14340      *
14341      * @param {EventTarget~Event} [event]
14342      *        The {@link Player#durationchange} event that caused this function to run.
14343      *
14344      * @listens Player#durationchange
14345      */
14346     ;
14347 
14348     _proto.updateShowing = function updateShowing(event) {
14349       if (this.player().duration() === Infinity) {
14350         this.show();
14351       } else {
14352         this.hide();
14353       }
14354     };
14355 
14356     return LiveDisplay;
14357   }(Component);
14358 
14359   Component.registerComponent('LiveDisplay', LiveDisplay);
14360 
14361   /**
14362    * Displays the live indicator when duration is Infinity.
14363    *
14364    * @extends Component
14365    */
14366 
14367   var SeekToLive =
14368   /*#__PURE__*/
14369   function (_Button) {
14370     inheritsLoose(SeekToLive, _Button);
14371 
14372     /**
14373      * Creates an instance of this class.
14374      *
14375      * @param {Player} player
14376      *        The `Player` that this class should be attached to.
14377      *
14378      * @param {Object} [options]
14379      *        The key/value store of player options.
14380      */
14381     function SeekToLive(player, options) {
14382       var _this;
14383 
14384       _this = _Button.call(this, player, options) || this;
14385 
14386       _this.updateLiveEdgeStatus();
14387 
14388       if (_this.player_.liveTracker) {
14389         _this.on(_this.player_.liveTracker, 'liveedgechange', _this.updateLiveEdgeStatus);
14390       }
14391 
14392       return _this;
14393     }
14394     /**
14395      * Create the `Component`'s DOM element
14396      *
14397      * @return {Element}
14398      *         The element that was created.
14399      */
14400 
14401 
14402     var _proto = SeekToLive.prototype;
14403 
14404     _proto.createEl = function createEl$1() {
14405       var el = _Button.prototype.createEl.call(this, 'button', {
14406         className: 'vjs-seek-to-live-control vjs-control'
14407       });
14408 
14409       this.textEl_ = createEl('span', {
14410         className: 'vjs-seek-to-live-text',
14411         innerHTML: this.localize('LIVE')
14412       }, {
14413         'aria-hidden': 'true'
14414       });
14415       el.appendChild(this.textEl_);
14416       return el;
14417     }
14418     /**
14419      * Update the state of this button if we are at the live edge
14420      * or not
14421      */
14422     ;
14423 
14424     _proto.updateLiveEdgeStatus = function updateLiveEdgeStatus(e) {
14425       // default to live edge
14426       if (!this.player_.liveTracker || this.player_.liveTracker.atLiveEdge()) {
14427         this.setAttribute('aria-disabled', true);
14428         this.addClass('vjs-at-live-edge');
14429         this.controlText('Seek to live, currently playing live');
14430       } else {
14431         this.setAttribute('aria-disabled', false);
14432         this.removeClass('vjs-at-live-edge');
14433         this.controlText('Seek to live, currently behind live');
14434       }
14435     }
14436     /**
14437      * On click bring us as near to the live point as possible.
14438      * This requires that we wait for the next `live-seekable-change`
14439      * event which will happen every segment length seconds.
14440      */
14441     ;
14442 
14443     _proto.handleClick = function handleClick() {
14444       this.player_.liveTracker.seekToLiveEdge();
14445     }
14446     /**
14447      * Dispose of the element and stop tracking
14448      */
14449     ;
14450 
14451     _proto.dispose = function dispose() {
14452       if (this.player_.liveTracker) {
14453         this.off(this.player_.liveTracker, 'liveedgechange', this.updateLiveEdgeStatus);
14454       }
14455 
14456       this.textEl_ = null;
14457 
14458       _Button.prototype.dispose.call(this);
14459     };
14460 
14461     return SeekToLive;
14462   }(Button);
14463 
14464   SeekToLive.prototype.controlText_ = 'Seek to live, currently playing live';
14465   Component.registerComponent('SeekToLive', SeekToLive);
14466 
14467   /**
14468    * Keep a number between a min and a max value
14469    *
14470    * @param {number} number
14471    *        The number to clamp
14472    *
14473    * @param {number} min
14474    *        The minimum value
14475    * @param {number} max
14476    *        The maximum value
14477    *
14478    * @return {number}
14479    *         the clamped number
14480    */
14481   var clamp = function clamp(number, min, max) {
14482     number = Number(number);
14483     return Math.min(max, Math.max(min, isNaN(number) ? min : number));
14484   };
14485 
14486   /**
14487    * The base functionality for a slider. Can be vertical or horizontal.
14488    * For instance the volume bar or the seek bar on a video is a slider.
14489    *
14490    * @extends Component
14491    */
14492 
14493   var Slider =
14494   /*#__PURE__*/
14495   function (_Component) {
14496     inheritsLoose(Slider, _Component);
14497 
14498     /**
14499     * Create an instance of this class
14500     *
14501     * @param {Player} player
14502     *        The `Player` that this class should be attached to.
14503     *
14504     * @param {Object} [options]
14505     *        The key/value store of player options.
14506     */
14507     function Slider(player, options) {
14508       var _this;
14509 
14510       _this = _Component.call(this, player, options) || this; // Set property names to bar to match with the child Slider class is looking for
14511 
14512       _this.bar = _this.getChild(_this.options_.barName); // Set a horizontal or vertical class on the slider depending on the slider type
14513 
14514       _this.vertical(!!_this.options_.vertical);
14515 
14516       _this.enable();
14517 
14518       return _this;
14519     }
14520     /**
14521      * Are controls are currently enabled for this slider or not.
14522      *
14523      * @return {boolean}
14524      *         true if controls are enabled, false otherwise
14525      */
14526 
14527 
14528     var _proto = Slider.prototype;
14529 
14530     _proto.enabled = function enabled() {
14531       return this.enabled_;
14532     }
14533     /**
14534      * Enable controls for this slider if they are disabled
14535      */
14536     ;
14537 
14538     _proto.enable = function enable() {
14539       if (this.enabled()) {
14540         return;
14541       }
14542 
14543       this.on('mousedown', this.handleMouseDown);
14544       this.on('touchstart', this.handleMouseDown);
14545       this.on('keydown', this.handleKeyDown);
14546       this.on('click', this.handleClick); // TODO: deprecated, controlsvisible does not seem to be fired
14547 
14548       this.on(this.player_, 'controlsvisible', this.update);
14549 
14550       if (this.playerEvent) {
14551         this.on(this.player_, this.playerEvent, this.update);
14552       }
14553 
14554       this.removeClass('disabled');
14555       this.setAttribute('tabindex', 0);
14556       this.enabled_ = true;
14557     }
14558     /**
14559      * Disable controls for this slider if they are enabled
14560      */
14561     ;
14562 
14563     _proto.disable = function disable() {
14564       if (!this.enabled()) {
14565         return;
14566       }
14567 
14568       var doc = this.bar.el_.ownerDocument;
14569       this.off('mousedown', this.handleMouseDown);
14570       this.off('touchstart', this.handleMouseDown);
14571       this.off('keydown', this.handleKeyDown);
14572       this.off('click', this.handleClick);
14573       this.off(this.player_, 'controlsvisible', this.update);
14574       this.off(doc, 'mousemove', this.handleMouseMove);
14575       this.off(doc, 'mouseup', this.handleMouseUp);
14576       this.off(doc, 'touchmove', this.handleMouseMove);
14577       this.off(doc, 'touchend', this.handleMouseUp);
14578       this.removeAttribute('tabindex');
14579       this.addClass('disabled');
14580 
14581       if (this.playerEvent) {
14582         this.off(this.player_, this.playerEvent, this.update);
14583       }
14584 
14585       this.enabled_ = false;
14586     }
14587     /**
14588      * Create the `Slider`s DOM element.
14589      *
14590      * @param {string} type
14591      *        Type of element to create.
14592      *
14593      * @param {Object} [props={}]
14594      *        List of properties in Object form.
14595      *
14596      * @param {Object} [attributes={}]
14597      *        list of attributes in Object form.
14598      *
14599      * @return {Element}
14600      *         The element that gets created.
14601      */
14602     ;
14603 
14604     _proto.createEl = function createEl(type, props, attributes) {
14605       if (props === void 0) {
14606         props = {};
14607       }
14608 
14609       if (attributes === void 0) {
14610         attributes = {};
14611       }
14612 
14613       // Add the slider element class to all sub classes
14614       props.className = props.className + ' vjs-slider';
14615       props = assign({
14616         tabIndex: 0
14617       }, props);
14618       attributes = assign({
14619         'role': 'slider',
14620         'aria-valuenow': 0,
14621         'aria-valuemin': 0,
14622         'aria-valuemax': 100,
14623         'tabIndex': 0
14624       }, attributes);
14625       return _Component.prototype.createEl.call(this, type, props, attributes);
14626     }
14627     /**
14628      * Handle `mousedown` or `touchstart` events on the `Slider`.
14629      *
14630      * @param {EventTarget~Event} event
14631      *        `mousedown` or `touchstart` event that triggered this function
14632      *
14633      * @listens mousedown
14634      * @listens touchstart
14635      * @fires Slider#slideractive
14636      */
14637     ;
14638 
14639     _proto.handleMouseDown = function handleMouseDown(event) {
14640       var doc = this.bar.el_.ownerDocument;
14641 
14642       if (event.type === 'mousedown') {
14643         event.preventDefault();
14644       } // Do not call preventDefault() on touchstart in Chrome
14645       // to avoid console warnings. Use a 'touch-action: none' style
14646       // instead to prevent unintented scrolling.
14647       // https://developers.google.com/web/updates/2017/01/scrolling-intervention
14648 
14649 
14650       if (event.type === 'touchstart' && !IS_CHROME) {
14651         event.preventDefault();
14652       }
14653 
14654       blockTextSelection();
14655       this.addClass('vjs-sliding');
14656       /**
14657        * Triggered when the slider is in an active state
14658        *
14659        * @event Slider#slideractive
14660        * @type {EventTarget~Event}
14661        */
14662 
14663       this.trigger('slideractive');
14664       this.on(doc, 'mousemove', this.handleMouseMove);
14665       this.on(doc, 'mouseup', this.handleMouseUp);
14666       this.on(doc, 'touchmove', this.handleMouseMove);
14667       this.on(doc, 'touchend', this.handleMouseUp);
14668       this.handleMouseMove(event);
14669     }
14670     /**
14671      * Handle the `mousemove`, `touchmove`, and `mousedown` events on this `Slider`.
14672      * The `mousemove` and `touchmove` events will only only trigger this function during
14673      * `mousedown` and `touchstart`. This is due to {@link Slider#handleMouseDown} and
14674      * {@link Slider#handleMouseUp}.
14675      *
14676      * @param {EventTarget~Event} event
14677      *        `mousedown`, `mousemove`, `touchstart`, or `touchmove` event that triggered
14678      *        this function
14679      *
14680      * @listens mousemove
14681      * @listens touchmove
14682      */
14683     ;
14684 
14685     _proto.handleMouseMove = function handleMouseMove(event) {}
14686     /**
14687      * Handle `mouseup` or `touchend` events on the `Slider`.
14688      *
14689      * @param {EventTarget~Event} event
14690      *        `mouseup` or `touchend` event that triggered this function.
14691      *
14692      * @listens touchend
14693      * @listens mouseup
14694      * @fires Slider#sliderinactive
14695      */
14696     ;
14697 
14698     _proto.handleMouseUp = function handleMouseUp() {
14699       var doc = this.bar.el_.ownerDocument;
14700       unblockTextSelection();
14701       this.removeClass('vjs-sliding');
14702       /**
14703        * Triggered when the slider is no longer in an active state.
14704        *
14705        * @event Slider#sliderinactive
14706        * @type {EventTarget~Event}
14707        */
14708 
14709       this.trigger('sliderinactive');
14710       this.off(doc, 'mousemove', this.handleMouseMove);
14711       this.off(doc, 'mouseup', this.handleMouseUp);
14712       this.off(doc, 'touchmove', this.handleMouseMove);
14713       this.off(doc, 'touchend', this.handleMouseUp);
14714       this.update();
14715     }
14716     /**
14717      * Update the progress bar of the `Slider`.
14718      *
14719      * @return {number}
14720      *          The percentage of progress the progress bar represents as a
14721      *          number from 0 to 1.
14722      */
14723     ;
14724 
14725     _proto.update = function update() {
14726       var _this2 = this;
14727 
14728       // In VolumeBar init we have a setTimeout for update that pops and update
14729       // to the end of the execution stack. The player is destroyed before then
14730       // update will cause an error
14731       // If there's no bar...
14732       if (!this.el_ || !this.bar) {
14733         return;
14734       } // clamp progress between 0 and 1
14735       // and only round to four decimal places, as we round to two below
14736 
14737 
14738       var progress = this.getProgress();
14739 
14740       if (progress === this.progress_) {
14741         return;
14742       }
14743 
14744       this.progress_ = progress;
14745       this.requestAnimationFrame(function () {
14746         // Set the new bar width or height
14747         var sizeKey = _this2.vertical() ? 'height' : 'width'; // Convert to a percentage for css value
14748 
14749         _this2.bar.el().style[sizeKey] = (progress * 100).toFixed(2) + '%';
14750       });
14751       return progress;
14752     }
14753     /**
14754      * Get the percentage of the bar that should be filled
14755      * but clamped and rounded.
14756      *
14757      * @return {number}
14758      *         percentage filled that the slider is
14759      */
14760     ;
14761 
14762     _proto.getProgress = function getProgress() {
14763       return clamp(this.getPercent(), 0, 1).toFixed(4);
14764     }
14765     /**
14766      * Calculate distance for slider
14767      *
14768      * @param {EventTarget~Event} event
14769      *        The event that caused this function to run.
14770      *
14771      * @return {number}
14772      *         The current position of the Slider.
14773      *         - position.x for vertical `Slider`s
14774      *         - position.y for horizontal `Slider`s
14775      */
14776     ;
14777 
14778     _proto.calculateDistance = function calculateDistance(event) {
14779       var position = getPointerPosition(this.el_, event);
14780 
14781       if (this.vertical()) {
14782         return position.y;
14783       }
14784 
14785       return position.x;
14786     }
14787     /**
14788      * Handle a `keydown` event on the `Slider`. Watches for left, rigth, up, and down
14789      * arrow keys. This function will only be called when the slider has focus. See
14790      * {@link Slider#handleFocus} and {@link Slider#handleBlur}.
14791      *
14792      * @param {EventTarget~Event} event
14793      *        the `keydown` event that caused this function to run.
14794      *
14795      * @listens keydown
14796      */
14797     ;
14798 
14799     _proto.handleKeyDown = function handleKeyDown(event) {
14800       // Left and Down Arrows
14801       if (keycode.isEventKey(event, 'Left') || keycode.isEventKey(event, 'Down')) {
14802         event.preventDefault();
14803         event.stopPropagation();
14804         this.stepBack(); // Up and Right Arrows
14805       } else if (keycode.isEventKey(event, 'Right') || keycode.isEventKey(event, 'Up')) {
14806         event.preventDefault();
14807         event.stopPropagation();
14808         this.stepForward();
14809       } else {
14810         // Pass keydown handling up for unsupported keys
14811         _Component.prototype.handleKeyDown.call(this, event);
14812       }
14813     }
14814     /**
14815      * Listener for click events on slider, used to prevent clicks
14816      *   from bubbling up to parent elements like button menus.
14817      *
14818      * @param {Object} event
14819      *        Event that caused this object to run
14820      */
14821     ;
14822 
14823     _proto.handleClick = function handleClick(event) {
14824       event.stopPropagation();
14825       event.preventDefault();
14826     }
14827     /**
14828      * Get/set if slider is horizontal for vertical
14829      *
14830      * @param {boolean} [bool]
14831      *        - true if slider is vertical,
14832      *        - false is horizontal
14833      *
14834      * @return {boolean}
14835      *         - true if slider is vertical, and getting
14836      *         - false if the slider is horizontal, and getting
14837      */
14838     ;
14839 
14840     _proto.vertical = function vertical(bool) {
14841       if (bool === undefined) {
14842         return this.vertical_ || false;
14843       }
14844 
14845       this.vertical_ = !!bool;
14846 
14847       if (this.vertical_) {
14848         this.addClass('vjs-slider-vertical');
14849       } else {
14850         this.addClass('vjs-slider-horizontal');
14851       }
14852     };
14853 
14854     return Slider;
14855   }(Component);
14856 
14857   Component.registerComponent('Slider', Slider);
14858 
14859   var percentify = function percentify(time, end) {
14860     return clamp(time / end * 100, 0, 100).toFixed(2) + '%';
14861   };
14862   /**
14863    * Shows loading progress
14864    *
14865    * @extends Component
14866    */
14867 
14868 
14869   var LoadProgressBar =
14870   /*#__PURE__*/
14871   function (_Component) {
14872     inheritsLoose(LoadProgressBar, _Component);
14873 
14874     /**
14875      * Creates an instance of this class.
14876      *
14877      * @param {Player} player
14878      *        The `Player` that this class should be attached to.
14879      *
14880      * @param {Object} [options]
14881      *        The key/value store of player options.
14882      */
14883     function LoadProgressBar(player, options) {
14884       var _this;
14885 
14886       _this = _Component.call(this, player, options) || this;
14887       _this.partEls_ = [];
14888 
14889       _this.on(player, 'progress', _this.update);
14890 
14891       return _this;
14892     }
14893     /**
14894      * Create the `Component`'s DOM element
14895      *
14896      * @return {Element}
14897      *         The element that was created.
14898      */
14899 
14900 
14901     var _proto = LoadProgressBar.prototype;
14902 
14903     _proto.createEl = function createEl$1() {
14904       var el = _Component.prototype.createEl.call(this, 'div', {
14905         className: 'vjs-load-progress'
14906       });
14907 
14908       var wrapper = createEl('span', {
14909         className: 'vjs-control-text'
14910       });
14911       var loadedText = createEl('span', {
14912         textContent: this.localize('Loaded')
14913       });
14914       var separator = document.createTextNode(': ');
14915       this.percentageEl_ = createEl('span', {
14916         className: 'vjs-control-text-loaded-percentage',
14917         textContent: '0%'
14918       });
14919       el.appendChild(wrapper);
14920       wrapper.appendChild(loadedText);
14921       wrapper.appendChild(separator);
14922       wrapper.appendChild(this.percentageEl_);
14923       return el;
14924     };
14925 
14926     _proto.dispose = function dispose() {
14927       this.partEls_ = null;
14928       this.percentageEl_ = null;
14929 
14930       _Component.prototype.dispose.call(this);
14931     }
14932     /**
14933      * Update progress bar
14934      *
14935      * @param {EventTarget~Event} [event]
14936      *        The `progress` event that caused this function to run.
14937      *
14938      * @listens Player#progress
14939      */
14940     ;
14941 
14942     _proto.update = function update(event) {
14943       var _this2 = this;
14944 
14945       this.requestAnimationFrame(function () {
14946         var liveTracker = _this2.player_.liveTracker;
14947 
14948         var buffered = _this2.player_.buffered();
14949 
14950         var duration = liveTracker && liveTracker.isLive() ? liveTracker.seekableEnd() : _this2.player_.duration();
14951 
14952         var bufferedEnd = _this2.player_.bufferedEnd();
14953 
14954         var children = _this2.partEls_;
14955         var percent = percentify(bufferedEnd, duration);
14956 
14957         if (_this2.percent_ !== percent) {
14958           // update the width of the progress bar
14959           _this2.el_.style.width = percent; // update the control-text
14960 
14961           textContent(_this2.percentageEl_, percent);
14962           _this2.percent_ = percent;
14963         } // add child elements to represent the individual buffered time ranges
14964 
14965 
14966         for (var i = 0; i < buffered.length; i++) {
14967           var start = buffered.start(i);
14968           var end = buffered.end(i);
14969           var part = children[i];
14970 
14971           if (!part) {
14972             part = _this2.el_.appendChild(createEl());
14973             children[i] = part;
14974           } //  only update if changed
14975 
14976 
14977           if (part.dataset.start === start && part.dataset.end === end) {
14978             continue;
14979           }
14980 
14981           part.dataset.start = start;
14982           part.dataset.end = end; // set the percent based on the width of the progress bar (bufferedEnd)
14983 
14984           part.style.left = percentify(start, bufferedEnd);
14985           part.style.width = percentify(end - start, bufferedEnd);
14986         } // remove unused buffered range elements
14987 
14988 
14989         for (var _i = children.length; _i > buffered.length; _i--) {
14990           _this2.el_.removeChild(children[_i - 1]);
14991         }
14992 
14993         children.length = buffered.length;
14994       });
14995     };
14996 
14997     return LoadProgressBar;
14998   }(Component);
14999 
15000   Component.registerComponent('LoadProgressBar', LoadProgressBar);
15001 
15002   /**
15003    * Time tooltips display a time above the progress bar.
15004    *
15005    * @extends Component
15006    */
15007 
15008   var TimeTooltip =
15009   /*#__PURE__*/
15010   function (_Component) {
15011     inheritsLoose(TimeTooltip, _Component);
15012 
15013     /**
15014      * Creates an instance of this class.
15015      *
15016      * @param {Player} player
15017      *        The {@link Player} that this class should be attached to.
15018      *
15019      * @param {Object} [options]
15020      *        The key/value store of player options.
15021      */
15022     function TimeTooltip(player, options) {
15023       var _this;
15024 
15025       _this = _Component.call(this, player, options) || this;
15026       _this.update = throttle(bind(assertThisInitialized(_this), _this.update), UPDATE_REFRESH_INTERVAL);
15027       return _this;
15028     }
15029     /**
15030      * Create the time tooltip DOM element
15031      *
15032      * @return {Element}
15033      *         The element that was created.
15034      */
15035 
15036 
15037     var _proto = TimeTooltip.prototype;
15038 
15039     _proto.createEl = function createEl() {
15040       return _Component.prototype.createEl.call(this, 'div', {
15041         className: 'vjs-time-tooltip'
15042       }, {
15043         'aria-hidden': 'true'
15044       });
15045     }
15046     /**
15047      * Updates the position of the time tooltip relative to the `SeekBar`.
15048      *
15049      * @param {Object} seekBarRect
15050      *        The `ClientRect` for the {@link SeekBar} element.
15051      *
15052      * @param {number} seekBarPoint
15053      *        A number from 0 to 1, representing a horizontal reference point
15054      *        from the left edge of the {@link SeekBar}
15055      */
15056     ;
15057 
15058     _proto.update = function update(seekBarRect, seekBarPoint, content) {
15059       var tooltipRect = getBoundingClientRect(this.el_);
15060       var playerRect = getBoundingClientRect(this.player_.el());
15061       var seekBarPointPx = seekBarRect.width * seekBarPoint; // do nothing if either rect isn't available
15062       // for example, if the player isn't in the DOM for testing
15063 
15064       if (!playerRect || !tooltipRect) {
15065         return;
15066       } // This is the space left of the `seekBarPoint` available within the bounds
15067       // of the player. We calculate any gap between the left edge of the player
15068       // and the left edge of the `SeekBar` and add the number of pixels in the
15069       // `SeekBar` before hitting the `seekBarPoint`
15070 
15071 
15072       var spaceLeftOfPoint = seekBarRect.left - playerRect.left + seekBarPointPx; // This is the space right of the `seekBarPoint` available within the bounds
15073       // of the player. We calculate the number of pixels from the `seekBarPoint`
15074       // to the right edge of the `SeekBar` and add to that any gap between the
15075       // right edge of the `SeekBar` and the player.
15076 
15077       var spaceRightOfPoint = seekBarRect.width - seekBarPointPx + (playerRect.right - seekBarRect.right); // This is the number of pixels by which the tooltip will need to be pulled
15078       // further to the right to center it over the `seekBarPoint`.
15079 
15080       var pullTooltipBy = tooltipRect.width / 2; // Adjust the `pullTooltipBy` distance to the left or right depending on
15081       // the results of the space calculations above.
15082 
15083       if (spaceLeftOfPoint < pullTooltipBy) {
15084         pullTooltipBy += pullTooltipBy - spaceLeftOfPoint;
15085       } else if (spaceRightOfPoint < pullTooltipBy) {
15086         pullTooltipBy = spaceRightOfPoint;
15087       } // Due to the imprecision of decimal/ratio based calculations and varying
15088       // rounding behaviors, there are cases where the spacing adjustment is off
15089       // by a pixel or two. This adds insurance to these calculations.
15090 
15091 
15092       if (pullTooltipBy < 0) {
15093         pullTooltipBy = 0;
15094       } else if (pullTooltipBy > tooltipRect.width) {
15095         pullTooltipBy = tooltipRect.width;
15096       }
15097 
15098       this.el_.style.right = "-" + pullTooltipBy + "px";
15099       this.write(content);
15100     }
15101     /**
15102      * Write the time to the tooltip DOM element.
15103      *
15104      * @param {string} content
15105      *        The formatted time for the tooltip.
15106      */
15107     ;
15108 
15109     _proto.write = function write(content) {
15110       textContent(this.el_, content);
15111     }
15112     /**
15113      * Updates the position of the time tooltip relative to the `SeekBar`.
15114      *
15115      * @param {Object} seekBarRect
15116      *        The `ClientRect` for the {@link SeekBar} element.
15117      *
15118      * @param {number} seekBarPoint
15119      *        A number from 0 to 1, representing a horizontal reference point
15120      *        from the left edge of the {@link SeekBar}
15121      *
15122      * @param {number} time
15123      *        The time to update the tooltip to, not used during live playback
15124      *
15125      * @param {Function} cb
15126      *        A function that will be called during the request animation frame
15127      *        for tooltips that need to do additional animations from the default
15128      */
15129     ;
15130 
15131     _proto.updateTime = function updateTime(seekBarRect, seekBarPoint, time, cb) {
15132       var _this2 = this;
15133 
15134       // If there is an existing rAF ID, cancel it so we don't over-queue.
15135       if (this.rafId_) {
15136         this.cancelAnimationFrame(this.rafId_);
15137       }
15138 
15139       this.rafId_ = this.requestAnimationFrame(function () {
15140         var content;
15141 
15142         var duration = _this2.player_.duration();
15143 
15144         if (_this2.player_.liveTracker && _this2.player_.liveTracker.isLive()) {
15145           var liveWindow = _this2.player_.liveTracker.liveWindow();
15146 
15147           var secondsBehind = liveWindow - seekBarPoint * liveWindow;
15148           content = (secondsBehind < 1 ? '' : '-') + formatTime(secondsBehind, liveWindow);
15149         } else {
15150           content = formatTime(time, duration);
15151         }
15152 
15153         _this2.update(seekBarRect, seekBarPoint, content);
15154 
15155         if (cb) {
15156           cb();
15157         }
15158       });
15159     };
15160 
15161     return TimeTooltip;
15162   }(Component);
15163 
15164   Component.registerComponent('TimeTooltip', TimeTooltip);
15165 
15166   /**
15167    * Used by {@link SeekBar} to display media playback progress as part of the
15168    * {@link ProgressControl}.
15169    *
15170    * @extends Component
15171    */
15172 
15173   var PlayProgressBar =
15174   /*#__PURE__*/
15175   function (_Component) {
15176     inheritsLoose(PlayProgressBar, _Component);
15177 
15178     /**
15179      * Creates an instance of this class.
15180      *
15181      * @param {Player} player
15182      *        The {@link Player} that this class should be attached to.
15183      *
15184      * @param {Object} [options]
15185      *        The key/value store of player options.
15186      */
15187     function PlayProgressBar(player, options) {
15188       var _this;
15189 
15190       _this = _Component.call(this, player, options) || this;
15191       _this.update = throttle(bind(assertThisInitialized(_this), _this.update), UPDATE_REFRESH_INTERVAL);
15192       return _this;
15193     }
15194     /**
15195      * Create the the DOM element for this class.
15196      *
15197      * @return {Element}
15198      *         The element that was created.
15199      */
15200 
15201 
15202     var _proto = PlayProgressBar.prototype;
15203 
15204     _proto.createEl = function createEl() {
15205       return _Component.prototype.createEl.call(this, 'div', {
15206         className: 'vjs-play-progress vjs-slider-bar'
15207       }, {
15208         'aria-hidden': 'true'
15209       });
15210     }
15211     /**
15212      * Enqueues updates to its own DOM as well as the DOM of its
15213      * {@link TimeTooltip} child.
15214      *
15215      * @param {Object} seekBarRect
15216      *        The `ClientRect` for the {@link SeekBar} element.
15217      *
15218      * @param {number} seekBarPoint
15219      *        A number from 0 to 1, representing a horizontal reference point
15220      *        from the left edge of the {@link SeekBar}
15221      */
15222     ;
15223 
15224     _proto.update = function update(seekBarRect, seekBarPoint) {
15225       var timeTooltip = this.getChild('timeTooltip');
15226 
15227       if (!timeTooltip) {
15228         return;
15229       }
15230 
15231       var time = this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime();
15232       timeTooltip.updateTime(seekBarRect, seekBarPoint, time);
15233     };
15234 
15235     return PlayProgressBar;
15236   }(Component);
15237   /**
15238    * Default options for {@link PlayProgressBar}.
15239    *
15240    * @type {Object}
15241    * @private
15242    */
15243 
15244 
15245   PlayProgressBar.prototype.options_ = {
15246     children: []
15247   }; // Time tooltips should not be added to a player on mobile devices
15248 
15249   if (!IS_IOS && !IS_ANDROID) {
15250     PlayProgressBar.prototype.options_.children.push('timeTooltip');
15251   }
15252 
15253   Component.registerComponent('PlayProgressBar', PlayProgressBar);
15254 
15255   /**
15256    * The {@link MouseTimeDisplay} component tracks mouse movement over the
15257    * {@link ProgressControl}. It displays an indicator and a {@link TimeTooltip}
15258    * indicating the time which is represented by a given point in the
15259    * {@link ProgressControl}.
15260    *
15261    * @extends Component
15262    */
15263 
15264   var MouseTimeDisplay =
15265   /*#__PURE__*/
15266   function (_Component) {
15267     inheritsLoose(MouseTimeDisplay, _Component);
15268 
15269     /**
15270      * Creates an instance of this class.
15271      *
15272      * @param {Player} player
15273      *        The {@link Player} that this class should be attached to.
15274      *
15275      * @param {Object} [options]
15276      *        The key/value store of player options.
15277      */
15278     function MouseTimeDisplay(player, options) {
15279       var _this;
15280 
15281       _this = _Component.call(this, player, options) || this;
15282       _this.update = throttle(bind(assertThisInitialized(_this), _this.update), UPDATE_REFRESH_INTERVAL);
15283       return _this;
15284     }
15285     /**
15286      * Create the DOM element for this class.
15287      *
15288      * @return {Element}
15289      *         The element that was created.
15290      */
15291 
15292 
15293     var _proto = MouseTimeDisplay.prototype;
15294 
15295     _proto.createEl = function createEl() {
15296       return _Component.prototype.createEl.call(this, 'div', {
15297         className: 'vjs-mouse-display'
15298       });
15299     }
15300     /**
15301      * Enqueues updates to its own DOM as well as the DOM of its
15302      * {@link TimeTooltip} child.
15303      *
15304      * @param {Object} seekBarRect
15305      *        The `ClientRect` for the {@link SeekBar} element.
15306      *
15307      * @param {number} seekBarPoint
15308      *        A number from 0 to 1, representing a horizontal reference point
15309      *        from the left edge of the {@link SeekBar}
15310      */
15311     ;
15312 
15313     _proto.update = function update(seekBarRect, seekBarPoint) {
15314       var _this2 = this;
15315 
15316       var time = seekBarPoint * this.player_.duration();
15317       this.getChild('timeTooltip').updateTime(seekBarRect, seekBarPoint, time, function () {
15318         _this2.el_.style.left = seekBarRect.width * seekBarPoint + "px";
15319       });
15320     };
15321 
15322     return MouseTimeDisplay;
15323   }(Component);
15324   /**
15325    * Default options for `MouseTimeDisplay`
15326    *
15327    * @type {Object}
15328    * @private
15329    */
15330 
15331 
15332   MouseTimeDisplay.prototype.options_ = {
15333     children: ['timeTooltip']
15334   };
15335   Component.registerComponent('MouseTimeDisplay', MouseTimeDisplay);
15336 
15337   var STEP_SECONDS = 5; // The multiplier of STEP_SECONDS that PgUp/PgDown move the timeline.
15338 
15339   var PAGE_KEY_MULTIPLIER = 12; // The interval at which the bar should update as it progresses.
15340 
15341   var UPDATE_REFRESH_INTERVAL$1 = 30;
15342   /**
15343    * Seek bar and container for the progress bars. Uses {@link PlayProgressBar}
15344    * as its `bar`.
15345    *
15346    * @extends Slider
15347    */
15348 
15349   var SeekBar =
15350   /*#__PURE__*/
15351   function (_Slider) {
15352     inheritsLoose(SeekBar, _Slider);
15353 
15354     /**
15355      * Creates an instance of this class.
15356      *
15357      * @param {Player} player
15358      *        The `Player` that this class should be attached to.
15359      *
15360      * @param {Object} [options]
15361      *        The key/value store of player options.
15362      */
15363     function SeekBar(player, options) {
15364       var _this;
15365 
15366       _this = _Slider.call(this, player, options) || this;
15367 
15368       _this.setEventHandlers_();
15369 
15370       return _this;
15371     }
15372     /**
15373      * Sets the event handlers
15374      *
15375      * @private
15376      */
15377 
15378 
15379     var _proto = SeekBar.prototype;
15380 
15381     _proto.setEventHandlers_ = function setEventHandlers_() {
15382       this.update_ = bind(this, this.update);
15383       this.update = throttle(this.update_, UPDATE_REFRESH_INTERVAL$1);
15384       this.on(this.player_, ['ended', 'durationchange', 'timeupdate'], this.update);
15385 
15386       if (this.player_.liveTracker) {
15387         this.on(this.player_.liveTracker, 'liveedgechange', this.update);
15388       } // when playing, let's ensure we smoothly update the play progress bar
15389       // via an interval
15390 
15391 
15392       this.updateInterval = null;
15393       this.on(this.player_, ['playing'], this.enableInterval_);
15394       this.on(this.player_, ['ended', 'pause', 'waiting'], this.disableInterval_); // we don't need to update the play progress if the document is hidden,
15395       // also, this causes the CPU to spike and eventually crash the page on IE11.
15396 
15397       if ('hidden' in document && 'visibilityState' in document) {
15398         this.on(document, 'visibilitychange', this.toggleVisibility_);
15399       }
15400     };
15401 
15402     _proto.toggleVisibility_ = function toggleVisibility_(e) {
15403       if (document.hidden) {
15404         this.disableInterval_(e);
15405       } else {
15406         this.enableInterval_(); // we just switched back to the page and someone may be looking, so, update ASAP
15407 
15408         this.update();
15409       }
15410     };
15411 
15412     _proto.enableInterval_ = function enableInterval_() {
15413       if (this.updateInterval) {
15414         return;
15415       }
15416 
15417       this.updateInterval = this.setInterval(this.update, UPDATE_REFRESH_INTERVAL$1);
15418     };
15419 
15420     _proto.disableInterval_ = function disableInterval_(e) {
15421       if (this.player_.liveTracker && this.player_.liveTracker.isLive() && e.type !== 'ended') {
15422         return;
15423       }
15424 
15425       if (!this.updateInterval) {
15426         return;
15427       }
15428 
15429       this.clearInterval(this.updateInterval);
15430       this.updateInterval = null;
15431     }
15432     /**
15433      * Create the `Component`'s DOM element
15434      *
15435      * @return {Element}
15436      *         The element that was created.
15437      */
15438     ;
15439 
15440     _proto.createEl = function createEl() {
15441       return _Slider.prototype.createEl.call(this, 'div', {
15442         className: 'vjs-progress-holder'
15443       }, {
15444         'aria-label': this.localize('Progress Bar')
15445       });
15446     }
15447     /**
15448      * This function updates the play progress bar and accessibility
15449      * attributes to whatever is passed in.
15450      *
15451      * @param {EventTarget~Event} [event]
15452      *        The `timeupdate` or `ended` event that caused this to run.
15453      *
15454      * @listens Player#timeupdate
15455      *
15456      * @return {number}
15457      *          The current percent at a number from 0-1
15458      */
15459     ;
15460 
15461     _proto.update = function update(event) {
15462       var _this2 = this;
15463 
15464       var percent = _Slider.prototype.update.call(this);
15465 
15466       this.requestAnimationFrame(function () {
15467         var currentTime = _this2.player_.ended() ? _this2.player_.duration() : _this2.getCurrentTime_();
15468         var liveTracker = _this2.player_.liveTracker;
15469 
15470         var duration = _this2.player_.duration();
15471 
15472         if (liveTracker && liveTracker.isLive()) {
15473           duration = _this2.player_.liveTracker.liveCurrentTime();
15474         }
15475 
15476         if (_this2.percent_ !== percent) {
15477           // machine readable value of progress bar (percentage complete)
15478           _this2.el_.setAttribute('aria-valuenow', (percent * 100).toFixed(2));
15479 
15480           _this2.percent_ = percent;
15481         }
15482 
15483         if (_this2.currentTime_ !== currentTime || _this2.duration_ !== duration) {
15484           // human readable value of progress bar (time complete)
15485           _this2.el_.setAttribute('aria-valuetext', _this2.localize('progress bar timing: currentTime={1} duration={2}', [formatTime(currentTime, duration), formatTime(duration, duration)], '{1} of {2}'));
15486 
15487           _this2.currentTime_ = currentTime;
15488           _this2.duration_ = duration;
15489         }
15490       });
15491       return percent;
15492     }
15493     /**
15494      * Get the value of current time but allows for smooth scrubbing,
15495      * when player can't keep up.
15496      *
15497      * @return {number}
15498      *         The current time value to display
15499      *
15500      * @private
15501      */
15502     ;
15503 
15504     _proto.getCurrentTime_ = function getCurrentTime_() {
15505       return this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime();
15506     }
15507     /**
15508      * Get the percentage of media played so far.
15509      *
15510      * @return {number}
15511      *         The percentage of media played so far (0 to 1).
15512      */
15513     ;
15514 
15515     _proto.getPercent = function getPercent() {
15516       var currentTime = this.getCurrentTime_();
15517       var percent;
15518       var liveTracker = this.player_.liveTracker;
15519 
15520       if (liveTracker && liveTracker.isLive()) {
15521         percent = (currentTime - liveTracker.seekableStart()) / liveTracker.liveWindow(); // prevent the percent from changing at the live edge
15522 
15523         if (liveTracker.atLiveEdge()) {
15524           percent = 1;
15525         }
15526       } else {
15527         percent = currentTime / this.player_.duration();
15528       }
15529 
15530       return percent;
15531     }
15532     /**
15533      * Handle mouse down on seek bar
15534      *
15535      * @param {EventTarget~Event} event
15536      *        The `mousedown` event that caused this to run.
15537      *
15538      * @listens mousedown
15539      */
15540     ;
15541 
15542     _proto.handleMouseDown = function handleMouseDown(event) {
15543       if (!isSingleLeftClick(event)) {
15544         return;
15545       } // Stop event propagation to prevent double fire in progress-control.js
15546 
15547 
15548       event.stopPropagation();
15549       this.player_.scrubbing(true);
15550       this.videoWasPlaying = !this.player_.paused();
15551       this.player_.pause();
15552 
15553       _Slider.prototype.handleMouseDown.call(this, event);
15554     }
15555     /**
15556      * Handle mouse move on seek bar
15557      *
15558      * @param {EventTarget~Event} event
15559      *        The `mousemove` event that caused this to run.
15560      *
15561      * @listens mousemove
15562      */
15563     ;
15564 
15565     _proto.handleMouseMove = function handleMouseMove(event) {
15566       if (!isSingleLeftClick(event)) {
15567         return;
15568       }
15569 
15570       var newTime;
15571       var distance = this.calculateDistance(event);
15572       var liveTracker = this.player_.liveTracker;
15573 
15574       if (!liveTracker || !liveTracker.isLive()) {
15575         newTime = distance * this.player_.duration(); // Don't let video end while scrubbing.
15576 
15577         if (newTime === this.player_.duration()) {
15578           newTime = newTime - 0.1;
15579         }
15580       } else {
15581         var seekableStart = liveTracker.seekableStart();
15582         var seekableEnd = liveTracker.liveCurrentTime();
15583         newTime = seekableStart + distance * liveTracker.liveWindow(); // Don't let video end while scrubbing.
15584 
15585         if (newTime >= seekableEnd) {
15586           newTime = seekableEnd;
15587         } // Compensate for precision differences so that currentTime is not less
15588         // than seekable start
15589 
15590 
15591         if (newTime <= seekableStart) {
15592           newTime = seekableStart + 0.1;
15593         } // On android seekableEnd can be Infinity sometimes,
15594         // this will cause newTime to be Infinity, which is
15595         // not a valid currentTime.
15596 
15597 
15598         if (newTime === Infinity) {
15599           return;
15600         }
15601       } // Set new time (tell player to seek to new time)
15602 
15603 
15604       this.player_.currentTime(newTime);
15605     };
15606 
15607     _proto.enable = function enable() {
15608       _Slider.prototype.enable.call(this);
15609 
15610       var mouseTimeDisplay = this.getChild('mouseTimeDisplay');
15611 
15612       if (!mouseTimeDisplay) {
15613         return;
15614       }
15615 
15616       mouseTimeDisplay.show();
15617     };
15618 
15619     _proto.disable = function disable() {
15620       _Slider.prototype.disable.call(this);
15621 
15622       var mouseTimeDisplay = this.getChild('mouseTimeDisplay');
15623 
15624       if (!mouseTimeDisplay) {
15625         return;
15626       }
15627 
15628       mouseTimeDisplay.hide();
15629     }
15630     /**
15631      * Handle mouse up on seek bar
15632      *
15633      * @param {EventTarget~Event} event
15634      *        The `mouseup` event that caused this to run.
15635      *
15636      * @listens mouseup
15637      */
15638     ;
15639 
15640     _proto.handleMouseUp = function handleMouseUp(event) {
15641       _Slider.prototype.handleMouseUp.call(this, event); // Stop event propagation to prevent double fire in progress-control.js
15642 
15643 
15644       if (event) {
15645         event.stopPropagation();
15646       }
15647 
15648       this.player_.scrubbing(false);
15649       /**
15650        * Trigger timeupdate because we're done seeking and the time has changed.
15651        * This is particularly useful for if the player is paused to time the time displays.
15652        *
15653        * @event Tech#timeupdate
15654        * @type {EventTarget~Event}
15655        */
15656 
15657       this.player_.trigger({
15658         type: 'timeupdate',
15659         target: this,
15660         manuallyTriggered: true
15661       });
15662 
15663       if (this.videoWasPlaying) {
15664         silencePromise(this.player_.play());
15665       } else {
15666         // We're done seeking and the time has changed.
15667         // If the player is paused, make sure we display the correct time on the seek bar.
15668         this.update_();
15669       }
15670     }
15671     /**
15672      * Move more quickly fast forward for keyboard-only users
15673      */
15674     ;
15675 
15676     _proto.stepForward = function stepForward() {
15677       this.player_.currentTime(this.player_.currentTime() + STEP_SECONDS);
15678     }
15679     /**
15680      * Move more quickly rewind for keyboard-only users
15681      */
15682     ;
15683 
15684     _proto.stepBack = function stepBack() {
15685       this.player_.currentTime(this.player_.currentTime() - STEP_SECONDS);
15686     }
15687     /**
15688      * Toggles the playback state of the player
15689      * This gets called when enter or space is used on the seekbar
15690      *
15691      * @param {EventTarget~Event} event
15692      *        The `keydown` event that caused this function to be called
15693      *
15694      */
15695     ;
15696 
15697     _proto.handleAction = function handleAction(event) {
15698       if (this.player_.paused()) {
15699         this.player_.play();
15700       } else {
15701         this.player_.pause();
15702       }
15703     }
15704     /**
15705      * Called when this SeekBar has focus and a key gets pressed down.
15706      * Supports the following keys:
15707      *
15708      *   Space or Enter key fire a click event
15709      *   Home key moves to start of the timeline
15710      *   End key moves to end of the timeline
15711      *   Digit "0" through "9" keys move to 0%, 10% ... 80%, 90% of the timeline
15712      *   PageDown key moves back a larger step than ArrowDown
15713      *   PageUp key moves forward a large step
15714      *
15715      * @param {EventTarget~Event} event
15716      *        The `keydown` event that caused this function to be called.
15717      *
15718      * @listens keydown
15719      */
15720     ;
15721 
15722     _proto.handleKeyDown = function handleKeyDown(event) {
15723       if (keycode.isEventKey(event, 'Space') || keycode.isEventKey(event, 'Enter')) {
15724         event.preventDefault();
15725         event.stopPropagation();
15726         this.handleAction(event);
15727       } else if (keycode.isEventKey(event, 'Home')) {
15728         event.preventDefault();
15729         event.stopPropagation();
15730         this.player_.currentTime(0);
15731       } else if (keycode.isEventKey(event, 'End')) {
15732         event.preventDefault();
15733         event.stopPropagation();
15734         this.player_.currentTime(this.player_.duration());
15735       } else if (/^[0-9]$/.test(keycode(event))) {
15736         event.preventDefault();
15737         event.stopPropagation();
15738         var gotoFraction = (keycode.codes[keycode(event)] - keycode.codes['0']) * 10.0 / 100.0;
15739         this.player_.currentTime(this.player_.duration() * gotoFraction);
15740       } else if (keycode.isEventKey(event, 'PgDn')) {
15741         event.preventDefault();
15742         event.stopPropagation();
15743         this.player_.currentTime(this.player_.currentTime() - STEP_SECONDS * PAGE_KEY_MULTIPLIER);
15744       } else if (keycode.isEventKey(event, 'PgUp')) {
15745         event.preventDefault();
15746         event.stopPropagation();
15747         this.player_.currentTime(this.player_.currentTime() + STEP_SECONDS * PAGE_KEY_MULTIPLIER);
15748       } else {
15749         // Pass keydown handling up for unsupported keys
15750         _Slider.prototype.handleKeyDown.call(this, event);
15751       }
15752     };
15753 
15754     return SeekBar;
15755   }(Slider);
15756   /**
15757    * Default options for the `SeekBar`
15758    *
15759    * @type {Object}
15760    * @private
15761    */
15762 
15763 
15764   SeekBar.prototype.options_ = {
15765     children: ['loadProgressBar', 'playProgressBar'],
15766     barName: 'playProgressBar'
15767   }; // MouseTimeDisplay tooltips should not be added to a player on mobile devices
15768 
15769   if (!IS_IOS && !IS_ANDROID) {
15770     SeekBar.prototype.options_.children.splice(1, 0, 'mouseTimeDisplay');
15771   }
15772 
15773   Component.registerComponent('SeekBar', SeekBar);
15774 
15775   /**
15776    * The Progress Control component contains the seek bar, load progress,
15777    * and play progress.
15778    *
15779    * @extends Component
15780    */
15781 
15782   var ProgressControl =
15783   /*#__PURE__*/
15784   function (_Component) {
15785     inheritsLoose(ProgressControl, _Component);
15786 
15787     /**
15788      * Creates an instance of this class.
15789      *
15790      * @param {Player} player
15791      *        The `Player` that this class should be attached to.
15792      *
15793      * @param {Object} [options]
15794      *        The key/value store of player options.
15795      */
15796     function ProgressControl(player, options) {
15797       var _this;
15798 
15799       _this = _Component.call(this, player, options) || this;
15800       _this.handleMouseMove = throttle(bind(assertThisInitialized(_this), _this.handleMouseMove), UPDATE_REFRESH_INTERVAL);
15801       _this.throttledHandleMouseSeek = throttle(bind(assertThisInitialized(_this), _this.handleMouseSeek), UPDATE_REFRESH_INTERVAL);
15802 
15803       _this.enable();
15804 
15805       return _this;
15806     }
15807     /**
15808      * Create the `Component`'s DOM element
15809      *
15810      * @return {Element}
15811      *         The element that was created.
15812      */
15813 
15814 
15815     var _proto = ProgressControl.prototype;
15816 
15817     _proto.createEl = function createEl() {
15818       return _Component.prototype.createEl.call(this, 'div', {
15819         className: 'vjs-progress-control vjs-control'
15820       });
15821     }
15822     /**
15823      * When the mouse moves over the `ProgressControl`, the pointer position
15824      * gets passed down to the `MouseTimeDisplay` component.
15825      *
15826      * @param {EventTarget~Event} event
15827      *        The `mousemove` event that caused this function to run.
15828      *
15829      * @listen mousemove
15830      */
15831     ;
15832 
15833     _proto.handleMouseMove = function handleMouseMove(event) {
15834       var seekBar = this.getChild('seekBar');
15835 
15836       if (!seekBar) {
15837         return;
15838       }
15839 
15840       var playProgressBar = seekBar.getChild('playProgressBar');
15841       var mouseTimeDisplay = seekBar.getChild('mouseTimeDisplay');
15842 
15843       if (!playProgressBar && !mouseTimeDisplay) {
15844         return;
15845       }
15846 
15847       var seekBarEl = seekBar.el();
15848       var seekBarRect = getBoundingClientRect(seekBarEl);
15849       var seekBarPoint = getPointerPosition(seekBarEl, event).x; // The default skin has a gap on either side of the `SeekBar`. This means
15850       // that it's possible to trigger this behavior outside the boundaries of
15851       // the `SeekBar`. This ensures we stay within it at all times.
15852 
15853       seekBarPoint = clamp(0, 1, seekBarPoint);
15854 
15855       if (mouseTimeDisplay) {
15856         mouseTimeDisplay.update(seekBarRect, seekBarPoint);
15857       }
15858 
15859       if (playProgressBar) {
15860         playProgressBar.update(seekBarRect, seekBar.getProgress());
15861       }
15862     }
15863     /**
15864      * A throttled version of the {@link ProgressControl#handleMouseSeek} listener.
15865      *
15866      * @method ProgressControl#throttledHandleMouseSeek
15867      * @param {EventTarget~Event} event
15868      *        The `mousemove` event that caused this function to run.
15869      *
15870      * @listen mousemove
15871      * @listen touchmove
15872      */
15873 
15874     /**
15875      * Handle `mousemove` or `touchmove` events on the `ProgressControl`.
15876      *
15877      * @param {EventTarget~Event} event
15878      *        `mousedown` or `touchstart` event that triggered this function
15879      *
15880      * @listens mousemove
15881      * @listens touchmove
15882      */
15883     ;
15884 
15885     _proto.handleMouseSeek = function handleMouseSeek(event) {
15886       var seekBar = this.getChild('seekBar');
15887 
15888       if (seekBar) {
15889         seekBar.handleMouseMove(event);
15890       }
15891     }
15892     /**
15893      * Are controls are currently enabled for this progress control.
15894      *
15895      * @return {boolean}
15896      *         true if controls are enabled, false otherwise
15897      */
15898     ;
15899 
15900     _proto.enabled = function enabled() {
15901       return this.enabled_;
15902     }
15903     /**
15904      * Disable all controls on the progress control and its children
15905      */
15906     ;
15907 
15908     _proto.disable = function disable() {
15909       this.children().forEach(function (child) {
15910         return child.disable && child.disable();
15911       });
15912 
15913       if (!this.enabled()) {
15914         return;
15915       }
15916 
15917       this.off(['mousedown', 'touchstart'], this.handleMouseDown);
15918       this.off(this.el_, 'mousemove', this.handleMouseMove);
15919       this.handleMouseUp();
15920       this.addClass('disabled');
15921       this.enabled_ = false;
15922     }
15923     /**
15924      * Enable all controls on the progress control and its children
15925      */
15926     ;
15927 
15928     _proto.enable = function enable() {
15929       this.children().forEach(function (child) {
15930         return child.enable && child.enable();
15931       });
15932 
15933       if (this.enabled()) {
15934         return;
15935       }
15936 
15937       this.on(['mousedown', 'touchstart'], this.handleMouseDown);
15938       this.on(this.el_, 'mousemove', this.handleMouseMove);
15939       this.removeClass('disabled');
15940       this.enabled_ = true;
15941     }
15942     /**
15943      * Handle `mousedown` or `touchstart` events on the `ProgressControl`.
15944      *
15945      * @param {EventTarget~Event} event
15946      *        `mousedown` or `touchstart` event that triggered this function
15947      *
15948      * @listens mousedown
15949      * @listens touchstart
15950      */
15951     ;
15952 
15953     _proto.handleMouseDown = function handleMouseDown(event) {
15954       var doc = this.el_.ownerDocument;
15955       var seekBar = this.getChild('seekBar');
15956 
15957       if (seekBar) {
15958         seekBar.handleMouseDown(event);
15959       }
15960 
15961       this.on(doc, 'mousemove', this.throttledHandleMouseSeek);
15962       this.on(doc, 'touchmove', this.throttledHandleMouseSeek);
15963       this.on(doc, 'mouseup', this.handleMouseUp);
15964       this.on(doc, 'touchend', this.handleMouseUp);
15965     }
15966     /**
15967      * Handle `mouseup` or `touchend` events on the `ProgressControl`.
15968      *
15969      * @param {EventTarget~Event} event
15970      *        `mouseup` or `touchend` event that triggered this function.
15971      *
15972      * @listens touchend
15973      * @listens mouseup
15974      */
15975     ;
15976 
15977     _proto.handleMouseUp = function handleMouseUp(event) {
15978       var doc = this.el_.ownerDocument;
15979       var seekBar = this.getChild('seekBar');
15980 
15981       if (seekBar) {
15982         seekBar.handleMouseUp(event);
15983       }
15984 
15985       this.off(doc, 'mousemove', this.throttledHandleMouseSeek);
15986       this.off(doc, 'touchmove', this.throttledHandleMouseSeek);
15987       this.off(doc, 'mouseup', this.handleMouseUp);
15988       this.off(doc, 'touchend', this.handleMouseUp);
15989     };
15990 
15991     return ProgressControl;
15992   }(Component);
15993   /**
15994    * Default options for `ProgressControl`
15995    *
15996    * @type {Object}
15997    * @private
15998    */
15999 
16000 
16001   ProgressControl.prototype.options_ = {
16002     children: ['seekBar']
16003   };
16004   Component.registerComponent('ProgressControl', ProgressControl);
16005 
16006   /**
16007    * Toggle Picture-in-Picture mode
16008    *
16009    * @extends Button
16010    */
16011 
16012   var PictureInPictureToggle =
16013   /*#__PURE__*/
16014   function (_Button) {
16015     inheritsLoose(PictureInPictureToggle, _Button);
16016 
16017     /**
16018      * Creates an instance of this class.
16019      *
16020      * @param {Player} player
16021      *        The `Player` that this class should be attached to.
16022      *
16023      * @param {Object} [options]
16024      *        The key/value store of player options.
16025      *
16026      * @listens Player#enterpictureinpicture
16027      * @listens Player#leavepictureinpicture
16028      */
16029     function PictureInPictureToggle(player, options) {
16030       var _this;
16031 
16032       _this = _Button.call(this, player, options) || this;
16033 
16034       _this.on(player, ['enterpictureinpicture', 'leavepictureinpicture'], _this.handlePictureInPictureChange); // TODO: Activate button on player loadedmetadata event.
16035       // TODO: Deactivate button on player emptied event.
16036       // TODO: Deactivate button if disablepictureinpicture attribute is present.
16037 
16038 
16039       if (!document.pictureInPictureEnabled) {
16040         _this.disable();
16041       }
16042 
16043       return _this;
16044     }
16045     /**
16046      * Builds the default DOM `className`.
16047      *
16048      * @return {string}
16049      *         The DOM `className` for this object.
16050      */
16051 
16052 
16053     var _proto = PictureInPictureToggle.prototype;
16054 
16055     _proto.buildCSSClass = function buildCSSClass() {
16056       return "vjs-picture-in-picture-control " + _Button.prototype.buildCSSClass.call(this);
16057     }
16058     /**
16059      * Handles enterpictureinpicture and leavepictureinpicture on the player and change control text accordingly.
16060      *
16061      * @param {EventTarget~Event} [event]
16062      *        The {@link Player#enterpictureinpicture} or {@link Player#leavepictureinpicture} event that caused this function to be
16063      *        called.
16064      *
16065      * @listens Player#enterpictureinpicture
16066      * @listens Player#leavepictureinpicture
16067      */
16068     ;
16069 
16070     _proto.handlePictureInPictureChange = function handlePictureInPictureChange(event) {
16071       if (this.player_.isInPictureInPicture()) {
16072         this.controlText('Exit Picture-in-Picture');
16073       } else {
16074         this.controlText('Picture-in-Picture');
16075       }
16076     }
16077     /**
16078      * This gets called when an `PictureInPictureToggle` is "clicked". See
16079      * {@link ClickableComponent} for more detailed information on what a click can be.
16080      *
16081      * @param {EventTarget~Event} [event]
16082      *        The `keydown`, `tap`, or `click` event that caused this function to be
16083      *        called.
16084      *
16085      * @listens tap
16086      * @listens click
16087      */
16088     ;
16089 
16090     _proto.handleClick = function handleClick(event) {
16091       if (!this.player_.isInPictureInPicture()) {
16092         this.player_.requestPictureInPicture();
16093       } else {
16094         this.player_.exitPictureInPicture();
16095       }
16096     };
16097 
16098     return PictureInPictureToggle;
16099   }(Button);
16100   /**
16101    * The text that should display over the `PictureInPictureToggle`s controls. Added for localization.
16102    *
16103    * @type {string}
16104    * @private
16105    */
16106 
16107 
16108   PictureInPictureToggle.prototype.controlText_ = 'Picture-in-Picture';
16109   Component.registerComponent('PictureInPictureToggle', PictureInPictureToggle);
16110 
16111   /**
16112    * Toggle fullscreen video
16113    *
16114    * @extends Button
16115    */
16116 
16117   var FullscreenToggle =
16118   /*#__PURE__*/
16119   function (_Button) {
16120     inheritsLoose(FullscreenToggle, _Button);
16121 
16122     /**
16123      * Creates an instance of this class.
16124      *
16125      * @param {Player} player
16126      *        The `Player` that this class should be attached to.
16127      *
16128      * @param {Object} [options]
16129      *        The key/value store of player options.
16130      */
16131     function FullscreenToggle(player, options) {
16132       var _this;
16133 
16134       _this = _Button.call(this, player, options) || this;
16135 
16136       _this.on(player, 'fullscreenchange', _this.handleFullscreenChange);
16137 
16138       if (document[player.fsApi_.fullscreenEnabled] === false) {
16139         _this.disable();
16140       }
16141 
16142       return _this;
16143     }
16144     /**
16145      * Builds the default DOM `className`.
16146      *
16147      * @return {string}
16148      *         The DOM `className` for this object.
16149      */
16150 
16151 
16152     var _proto = FullscreenToggle.prototype;
16153 
16154     _proto.buildCSSClass = function buildCSSClass() {
16155       return "vjs-fullscreen-control " + _Button.prototype.buildCSSClass.call(this);
16156     }
16157     /**
16158      * Handles fullscreenchange on the player and change control text accordingly.
16159      *
16160      * @param {EventTarget~Event} [event]
16161      *        The {@link Player#fullscreenchange} event that caused this function to be
16162      *        called.
16163      *
16164      * @listens Player#fullscreenchange
16165      */
16166     ;
16167 
16168     _proto.handleFullscreenChange = function handleFullscreenChange(event) {
16169       if (this.player_.isFullscreen()) {
16170         this.controlText('Non-Fullscreen');
16171       } else {
16172         this.controlText('Fullscreen');
16173       }
16174     }
16175     /**
16176      * This gets called when an `FullscreenToggle` is "clicked". See
16177      * {@link ClickableComponent} for more detailed information on what a click can be.
16178      *
16179      * @param {EventTarget~Event} [event]
16180      *        The `keydown`, `tap`, or `click` event that caused this function to be
16181      *        called.
16182      *
16183      * @listens tap
16184      * @listens click
16185      */
16186     ;
16187 
16188     _proto.handleClick = function handleClick(event) {
16189       if (!this.player_.isFullscreen()) {
16190         this.player_.requestFullscreen();
16191       } else {
16192         this.player_.exitFullscreen();
16193       }
16194     };
16195 
16196     return FullscreenToggle;
16197   }(Button);
16198   /**
16199    * The text that should display over the `FullscreenToggle`s controls. Added for localization.
16200    *
16201    * @type {string}
16202    * @private
16203    */
16204 
16205 
16206   FullscreenToggle.prototype.controlText_ = 'Fullscreen';
16207   Component.registerComponent('FullscreenToggle', FullscreenToggle);
16208 
16209   /**
16210    * Check if volume control is supported and if it isn't hide the
16211    * `Component` that was passed  using the `vjs-hidden` class.
16212    *
16213    * @param {Component} self
16214    *        The component that should be hidden if volume is unsupported
16215    *
16216    * @param {Player} player
16217    *        A reference to the player
16218    *
16219    * @private
16220    */
16221   var checkVolumeSupport = function checkVolumeSupport(self, player) {
16222     // hide volume controls when they're not supported by the current tech
16223     if (player.tech_ && !player.tech_.featuresVolumeControl) {
16224       self.addClass('vjs-hidden');
16225     }
16226 
16227     self.on(player, 'loadstart', function () {
16228       if (!player.tech_.featuresVolumeControl) {
16229         self.addClass('vjs-hidden');
16230       } else {
16231         self.removeClass('vjs-hidden');
16232       }
16233     });
16234   };
16235 
16236   /**
16237    * Shows volume level
16238    *
16239    * @extends Component
16240    */
16241 
16242   var VolumeLevel =
16243   /*#__PURE__*/
16244   function (_Component) {
16245     inheritsLoose(VolumeLevel, _Component);
16246 
16247     function VolumeLevel() {
16248       return _Component.apply(this, arguments) || this;
16249     }
16250 
16251     var _proto = VolumeLevel.prototype;
16252 
16253     /**
16254      * Create the `Component`'s DOM element
16255      *
16256      * @return {Element}
16257      *         The element that was created.
16258      */
16259     _proto.createEl = function createEl() {
16260       return _Component.prototype.createEl.call(this, 'div', {
16261         className: 'vjs-volume-level',
16262         innerHTML: '<span class="vjs-control-text"></span>'
16263       });
16264     };
16265 
16266     return VolumeLevel;
16267   }(Component);
16268 
16269   Component.registerComponent('VolumeLevel', VolumeLevel);
16270 
16271   /**
16272    * The bar that contains the volume level and can be clicked on to adjust the level
16273    *
16274    * @extends Slider
16275    */
16276 
16277   var VolumeBar =
16278   /*#__PURE__*/
16279   function (_Slider) {
16280     inheritsLoose(VolumeBar, _Slider);
16281 
16282     /**
16283      * Creates an instance of this class.
16284      *
16285      * @param {Player} player
16286      *        The `Player` that this class should be attached to.
16287      *
16288      * @param {Object} [options]
16289      *        The key/value store of player options.
16290      */
16291     function VolumeBar(player, options) {
16292       var _this;
16293 
16294       _this = _Slider.call(this, player, options) || this;
16295 
16296       _this.on('slideractive', _this.updateLastVolume_);
16297 
16298       _this.on(player, 'volumechange', _this.updateARIAAttributes);
16299 
16300       player.ready(function () {
16301         return _this.updateARIAAttributes();
16302       });
16303       return _this;
16304     }
16305     /**
16306      * Create the `Component`'s DOM element
16307      *
16308      * @return {Element}
16309      *         The element that was created.
16310      */
16311 
16312 
16313     var _proto = VolumeBar.prototype;
16314 
16315     _proto.createEl = function createEl() {
16316       return _Slider.prototype.createEl.call(this, 'div', {
16317         className: 'vjs-volume-bar vjs-slider-bar'
16318       }, {
16319         'aria-label': this.localize('Volume Level'),
16320         'aria-live': 'polite'
16321       });
16322     }
16323     /**
16324      * Handle mouse down on volume bar
16325      *
16326      * @param {EventTarget~Event} event
16327      *        The `mousedown` event that caused this to run.
16328      *
16329      * @listens mousedown
16330      */
16331     ;
16332 
16333     _proto.handleMouseDown = function handleMouseDown(event) {
16334       if (!isSingleLeftClick(event)) {
16335         return;
16336       }
16337 
16338       _Slider.prototype.handleMouseDown.call(this, event);
16339     }
16340     /**
16341      * Handle movement events on the {@link VolumeMenuButton}.
16342      *
16343      * @param {EventTarget~Event} event
16344      *        The event that caused this function to run.
16345      *
16346      * @listens mousemove
16347      */
16348     ;
16349 
16350     _proto.handleMouseMove = function handleMouseMove(event) {
16351       if (!isSingleLeftClick(event)) {
16352         return;
16353       }
16354 
16355       this.checkMuted();
16356       this.player_.volume(this.calculateDistance(event));
16357     }
16358     /**
16359      * If the player is muted unmute it.
16360      */
16361     ;
16362 
16363     _proto.checkMuted = function checkMuted() {
16364       if (this.player_.muted()) {
16365         this.player_.muted(false);
16366       }
16367     }
16368     /**
16369      * Get percent of volume level
16370      *
16371      * @return {number}
16372      *         Volume level percent as a decimal number.
16373      */
16374     ;
16375 
16376     _proto.getPercent = function getPercent() {
16377       if (this.player_.muted()) {
16378         return 0;
16379       }
16380 
16381       return this.player_.volume();
16382     }
16383     /**
16384      * Increase volume level for keyboard users
16385      */
16386     ;
16387 
16388     _proto.stepForward = function stepForward() {
16389       this.checkMuted();
16390       this.player_.volume(this.player_.volume() + 0.1);
16391     }
16392     /**
16393      * Decrease volume level for keyboard users
16394      */
16395     ;
16396 
16397     _proto.stepBack = function stepBack() {
16398       this.checkMuted();
16399       this.player_.volume(this.player_.volume() - 0.1);
16400     }
16401     /**
16402      * Update ARIA accessibility attributes
16403      *
16404      * @param {EventTarget~Event} [event]
16405      *        The `volumechange` event that caused this function to run.
16406      *
16407      * @listens Player#volumechange
16408      */
16409     ;
16410 
16411     _proto.updateARIAAttributes = function updateARIAAttributes(event) {
16412       var ariaValue = this.player_.muted() ? 0 : this.volumeAsPercentage_();
16413       this.el_.setAttribute('aria-valuenow', ariaValue);
16414       this.el_.setAttribute('aria-valuetext', ariaValue + '%');
16415     }
16416     /**
16417      * Returns the current value of the player volume as a percentage
16418      *
16419      * @private
16420      */
16421     ;
16422 
16423     _proto.volumeAsPercentage_ = function volumeAsPercentage_() {
16424       return Math.round(this.player_.volume() * 100);
16425     }
16426     /**
16427      * When user starts dragging the VolumeBar, store the volume and listen for
16428      * the end of the drag. When the drag ends, if the volume was set to zero,
16429      * set lastVolume to the stored volume.
16430      *
16431      * @listens slideractive
16432      * @private
16433      */
16434     ;
16435 
16436     _proto.updateLastVolume_ = function updateLastVolume_() {
16437       var _this2 = this;
16438 
16439       var volumeBeforeDrag = this.player_.volume();
16440       this.one('sliderinactive', function () {
16441         if (_this2.player_.volume() === 0) {
16442           _this2.player_.lastVolume_(volumeBeforeDrag);
16443         }
16444       });
16445     };
16446 
16447     return VolumeBar;
16448   }(Slider);
16449   /**
16450    * Default options for the `VolumeBar`
16451    *
16452    * @type {Object}
16453    * @private
16454    */
16455 
16456 
16457   VolumeBar.prototype.options_ = {
16458     children: ['volumeLevel'],
16459     barName: 'volumeLevel'
16460   };
16461   /**
16462    * Call the update event for this Slider when this event happens on the player.
16463    *
16464    * @type {string}
16465    */
16466 
16467   VolumeBar.prototype.playerEvent = 'volumechange';
16468   Component.registerComponent('VolumeBar', VolumeBar);
16469 
16470   /**
16471    * The component for controlling the volume level
16472    *
16473    * @extends Component
16474    */
16475 
16476   var VolumeControl =
16477   /*#__PURE__*/
16478   function (_Component) {
16479     inheritsLoose(VolumeControl, _Component);
16480 
16481     /**
16482      * Creates an instance of this class.
16483      *
16484      * @param {Player} player
16485      *        The `Player` that this class should be attached to.
16486      *
16487      * @param {Object} [options={}]
16488      *        The key/value store of player options.
16489      */
16490     function VolumeControl(player, options) {
16491       var _this;
16492 
16493       if (options === void 0) {
16494         options = {};
16495       }
16496 
16497       options.vertical = options.vertical || false; // Pass the vertical option down to the VolumeBar if
16498       // the VolumeBar is turned on.
16499 
16500       if (typeof options.volumeBar === 'undefined' || isPlain(options.volumeBar)) {
16501         options.volumeBar = options.volumeBar || {};
16502         options.volumeBar.vertical = options.vertical;
16503       }
16504 
16505       _this = _Component.call(this, player, options) || this; // hide this control if volume support is missing
16506 
16507       checkVolumeSupport(assertThisInitialized(_this), player);
16508       _this.throttledHandleMouseMove = throttle(bind(assertThisInitialized(_this), _this.handleMouseMove), UPDATE_REFRESH_INTERVAL);
16509 
16510       _this.on('mousedown', _this.handleMouseDown);
16511 
16512       _this.on('touchstart', _this.handleMouseDown); // while the slider is active (the mouse has been pressed down and
16513       // is dragging) or in focus we do not want to hide the VolumeBar
16514 
16515 
16516       _this.on(_this.volumeBar, ['focus', 'slideractive'], function () {
16517         _this.volumeBar.addClass('vjs-slider-active');
16518 
16519         _this.addClass('vjs-slider-active');
16520 
16521         _this.trigger('slideractive');
16522       });
16523 
16524       _this.on(_this.volumeBar, ['blur', 'sliderinactive'], function () {
16525         _this.volumeBar.removeClass('vjs-slider-active');
16526 
16527         _this.removeClass('vjs-slider-active');
16528 
16529         _this.trigger('sliderinactive');
16530       });
16531 
16532       return _this;
16533     }
16534     /**
16535      * Create the `Component`'s DOM element
16536      *
16537      * @return {Element}
16538      *         The element that was created.
16539      */
16540 
16541 
16542     var _proto = VolumeControl.prototype;
16543 
16544     _proto.createEl = function createEl() {
16545       var orientationClass = 'vjs-volume-horizontal';
16546 
16547       if (this.options_.vertical) {
16548         orientationClass = 'vjs-volume-vertical';
16549       }
16550 
16551       return _Component.prototype.createEl.call(this, 'div', {
16552         className: "vjs-volume-control vjs-control " + orientationClass
16553       });
16554     }
16555     /**
16556      * Handle `mousedown` or `touchstart` events on the `VolumeControl`.
16557      *
16558      * @param {EventTarget~Event} event
16559      *        `mousedown` or `touchstart` event that triggered this function
16560      *
16561      * @listens mousedown
16562      * @listens touchstart
16563      */
16564     ;
16565 
16566     _proto.handleMouseDown = function handleMouseDown(event) {
16567       var doc = this.el_.ownerDocument;
16568       this.on(doc, 'mousemove', this.throttledHandleMouseMove);
16569       this.on(doc, 'touchmove', this.throttledHandleMouseMove);
16570       this.on(doc, 'mouseup', this.handleMouseUp);
16571       this.on(doc, 'touchend', this.handleMouseUp);
16572     }
16573     /**
16574      * Handle `mouseup` or `touchend` events on the `VolumeControl`.
16575      *
16576      * @param {EventTarget~Event} event
16577      *        `mouseup` or `touchend` event that triggered this function.
16578      *
16579      * @listens touchend
16580      * @listens mouseup
16581      */
16582     ;
16583 
16584     _proto.handleMouseUp = function handleMouseUp(event) {
16585       var doc = this.el_.ownerDocument;
16586       this.off(doc, 'mousemove', this.throttledHandleMouseMove);
16587       this.off(doc, 'touchmove', this.throttledHandleMouseMove);
16588       this.off(doc, 'mouseup', this.handleMouseUp);
16589       this.off(doc, 'touchend', this.handleMouseUp);
16590     }
16591     /**
16592      * Handle `mousedown` or `touchstart` events on the `VolumeControl`.
16593      *
16594      * @param {EventTarget~Event} event
16595      *        `mousedown` or `touchstart` event that triggered this function
16596      *
16597      * @listens mousedown
16598      * @listens touchstart
16599      */
16600     ;
16601 
16602     _proto.handleMouseMove = function handleMouseMove(event) {
16603       this.volumeBar.handleMouseMove(event);
16604     };
16605 
16606     return VolumeControl;
16607   }(Component);
16608   /**
16609    * Default options for the `VolumeControl`
16610    *
16611    * @type {Object}
16612    * @private
16613    */
16614 
16615 
16616   VolumeControl.prototype.options_ = {
16617     children: ['volumeBar']
16618   };
16619   Component.registerComponent('VolumeControl', VolumeControl);
16620 
16621   /**
16622    * Check if muting volume is supported and if it isn't hide the mute toggle
16623    * button.
16624    *
16625    * @param {Component} self
16626    *        A reference to the mute toggle button
16627    *
16628    * @param {Player} player
16629    *        A reference to the player
16630    *
16631    * @private
16632    */
16633   var checkMuteSupport = function checkMuteSupport(self, player) {
16634     // hide mute toggle button if it's not supported by the current tech
16635     if (player.tech_ && !player.tech_.featuresMuteControl) {
16636       self.addClass('vjs-hidden');
16637     }
16638 
16639     self.on(player, 'loadstart', function () {
16640       if (!player.tech_.featuresMuteControl) {
16641         self.addClass('vjs-hidden');
16642       } else {
16643         self.removeClass('vjs-hidden');
16644       }
16645     });
16646   };
16647 
16648   /**
16649    * A button component for muting the audio.
16650    *
16651    * @extends Button
16652    */
16653 
16654   var MuteToggle =
16655   /*#__PURE__*/
16656   function (_Button) {
16657     inheritsLoose(MuteToggle, _Button);
16658 
16659     /**
16660      * Creates an instance of this class.
16661      *
16662      * @param {Player} player
16663      *        The `Player` that this class should be attached to.
16664      *
16665      * @param {Object} [options]
16666      *        The key/value store of player options.
16667      */
16668     function MuteToggle(player, options) {
16669       var _this;
16670 
16671       _this = _Button.call(this, player, options) || this; // hide this control if volume support is missing
16672 
16673       checkMuteSupport(assertThisInitialized(_this), player);
16674 
16675       _this.on(player, ['loadstart', 'volumechange'], _this.update);
16676 
16677       return _this;
16678     }
16679     /**
16680      * Builds the default DOM `className`.
16681      *
16682      * @return {string}
16683      *         The DOM `className` for this object.
16684      */
16685 
16686 
16687     var _proto = MuteToggle.prototype;
16688 
16689     _proto.buildCSSClass = function buildCSSClass() {
16690       return "vjs-mute-control " + _Button.prototype.buildCSSClass.call(this);
16691     }
16692     /**
16693      * This gets called when an `MuteToggle` is "clicked". See
16694      * {@link ClickableComponent} for more detailed information on what a click can be.
16695      *
16696      * @param {EventTarget~Event} [event]
16697      *        The `keydown`, `tap`, or `click` event that caused this function to be
16698      *        called.
16699      *
16700      * @listens tap
16701      * @listens click
16702      */
16703     ;
16704 
16705     _proto.handleClick = function handleClick(event) {
16706       var vol = this.player_.volume();
16707       var lastVolume = this.player_.lastVolume_();
16708 
16709       if (vol === 0) {
16710         var volumeToSet = lastVolume < 0.1 ? 0.1 : lastVolume;
16711         this.player_.volume(volumeToSet);
16712         this.player_.muted(false);
16713       } else {
16714         this.player_.muted(this.player_.muted() ? false : true);
16715       }
16716     }
16717     /**
16718      * Update the `MuteToggle` button based on the state of `volume` and `muted`
16719      * on the player.
16720      *
16721      * @param {EventTarget~Event} [event]
16722      *        The {@link Player#loadstart} event if this function was called
16723      *        through an event.
16724      *
16725      * @listens Player#loadstart
16726      * @listens Player#volumechange
16727      */
16728     ;
16729 
16730     _proto.update = function update(event) {
16731       this.updateIcon_();
16732       this.updateControlText_();
16733     }
16734     /**
16735      * Update the appearance of the `MuteToggle` icon.
16736      *
16737      * Possible states (given `level` variable below):
16738      * - 0: crossed out
16739      * - 1: zero bars of volume
16740      * - 2: one bar of volume
16741      * - 3: two bars of volume
16742      *
16743      * @private
16744      */
16745     ;
16746 
16747     _proto.updateIcon_ = function updateIcon_() {
16748       var vol = this.player_.volume();
16749       var level = 3; // in iOS when a player is loaded with muted attribute
16750       // and volume is changed with a native mute button
16751       // we want to make sure muted state is updated
16752 
16753       if (IS_IOS && this.player_.tech_ && this.player_.tech_.el_) {
16754         this.player_.muted(this.player_.tech_.el_.muted);
16755       }
16756 
16757       if (vol === 0 || this.player_.muted()) {
16758         level = 0;
16759       } else if (vol < 0.33) {
16760         level = 1;
16761       } else if (vol < 0.67) {
16762         level = 2;
16763       } // TODO improve muted icon classes
16764 
16765 
16766       for (var i = 0; i < 4; i++) {
16767         removeClass(this.el_, "vjs-vol-" + i);
16768       }
16769 
16770       addClass(this.el_, "vjs-vol-" + level);
16771     }
16772     /**
16773      * If `muted` has changed on the player, update the control text
16774      * (`title` attribute on `vjs-mute-control` element and content of
16775      * `vjs-control-text` element).
16776      *
16777      * @private
16778      */
16779     ;
16780 
16781     _proto.updateControlText_ = function updateControlText_() {
16782       var soundOff = this.player_.muted() || this.player_.volume() === 0;
16783       var text = soundOff ? 'Unmute' : 'Mute';
16784 
16785       if (this.controlText() !== text) {
16786         this.controlText(text);
16787       }
16788     };
16789 
16790     return MuteToggle;
16791   }(Button);
16792   /**
16793    * The text that should display over the `MuteToggle`s controls. Added for localization.
16794    *
16795    * @type {string}
16796    * @private
16797    */
16798 
16799 
16800   MuteToggle.prototype.controlText_ = 'Mute';
16801   Component.registerComponent('MuteToggle', MuteToggle);
16802 
16803   /**
16804    * A Component to contain the MuteToggle and VolumeControl so that
16805    * they can work together.
16806    *
16807    * @extends Component
16808    */
16809 
16810   var VolumePanel =
16811   /*#__PURE__*/
16812   function (_Component) {
16813     inheritsLoose(VolumePanel, _Component);
16814 
16815     /**
16816      * Creates an instance of this class.
16817      *
16818      * @param {Player} player
16819      *        The `Player` that this class should be attached to.
16820      *
16821      * @param {Object} [options={}]
16822      *        The key/value store of player options.
16823      */
16824     function VolumePanel(player, options) {
16825       var _this;
16826 
16827       if (options === void 0) {
16828         options = {};
16829       }
16830 
16831       if (typeof options.inline !== 'undefined') {
16832         options.inline = options.inline;
16833       } else {
16834         options.inline = true;
16835       } // pass the inline option down to the VolumeControl as vertical if
16836       // the VolumeControl is on.
16837 
16838 
16839       if (typeof options.volumeControl === 'undefined' || isPlain(options.volumeControl)) {
16840         options.volumeControl = options.volumeControl || {};
16841         options.volumeControl.vertical = !options.inline;
16842       }
16843 
16844       _this = _Component.call(this, player, options) || this;
16845 
16846       _this.on(player, ['loadstart'], _this.volumePanelState_);
16847 
16848       _this.on(_this.muteToggle, 'keyup', _this.handleKeyPress);
16849 
16850       _this.on(_this.volumeControl, 'keyup', _this.handleVolumeControlKeyUp);
16851 
16852       _this.on('keydown', _this.handleKeyPress);
16853 
16854       _this.on('mouseover', _this.handleMouseOver);
16855 
16856       _this.on('mouseout', _this.handleMouseOut); // while the slider is active (the mouse has been pressed down and
16857       // is dragging) we do not want to hide the VolumeBar
16858 
16859 
16860       _this.on(_this.volumeControl, ['slideractive'], _this.sliderActive_);
16861 
16862       _this.on(_this.volumeControl, ['sliderinactive'], _this.sliderInactive_);
16863 
16864       return _this;
16865     }
16866     /**
16867      * Add vjs-slider-active class to the VolumePanel
16868      *
16869      * @listens VolumeControl#slideractive
16870      * @private
16871      */
16872 
16873 
16874     var _proto = VolumePanel.prototype;
16875 
16876     _proto.sliderActive_ = function sliderActive_() {
16877       this.addClass('vjs-slider-active');
16878     }
16879     /**
16880      * Removes vjs-slider-active class to the VolumePanel
16881      *
16882      * @listens VolumeControl#sliderinactive
16883      * @private
16884      */
16885     ;
16886 
16887     _proto.sliderInactive_ = function sliderInactive_() {
16888       this.removeClass('vjs-slider-active');
16889     }
16890     /**
16891      * Adds vjs-hidden or vjs-mute-toggle-only to the VolumePanel
16892      * depending on MuteToggle and VolumeControl state
16893      *
16894      * @listens Player#loadstart
16895      * @private
16896      */
16897     ;
16898 
16899     _proto.volumePanelState_ = function volumePanelState_() {
16900       // hide volume panel if neither volume control or mute toggle
16901       // are displayed
16902       if (this.volumeControl.hasClass('vjs-hidden') && this.muteToggle.hasClass('vjs-hidden')) {
16903         this.addClass('vjs-hidden');
16904       } // if only mute toggle is visible we don't want
16905       // volume panel expanding when hovered or active
16906 
16907 
16908       if (this.volumeControl.hasClass('vjs-hidden') && !this.muteToggle.hasClass('vjs-hidden')) {
16909         this.addClass('vjs-mute-toggle-only');
16910       }
16911     }
16912     /**
16913      * Create the `Component`'s DOM element
16914      *
16915      * @return {Element}
16916      *         The element that was created.
16917      */
16918     ;
16919 
16920     _proto.createEl = function createEl() {
16921       var orientationClass = 'vjs-volume-panel-horizontal';
16922 
16923       if (!this.options_.inline) {
16924         orientationClass = 'vjs-volume-panel-vertical';
16925       }
16926 
16927       return _Component.prototype.createEl.call(this, 'div', {
16928         className: "vjs-volume-panel vjs-control " + orientationClass
16929       });
16930     }
16931     /**
16932      * Dispose of the `volume-panel` and all child components.
16933      */
16934     ;
16935 
16936     _proto.dispose = function dispose() {
16937       this.handleMouseOut();
16938 
16939       _Component.prototype.dispose.call(this);
16940     }
16941     /**
16942      * Handles `keyup` events on the `VolumeControl`, looking for ESC, which closes
16943      * the volume panel and sets focus on `MuteToggle`.
16944      *
16945      * @param {EventTarget~Event} event
16946      *        The `keyup` event that caused this function to be called.
16947      *
16948      * @listens keyup
16949      */
16950     ;
16951 
16952     _proto.handleVolumeControlKeyUp = function handleVolumeControlKeyUp(event) {
16953       if (keycode.isEventKey(event, 'Esc')) {
16954         this.muteToggle.focus();
16955       }
16956     }
16957     /**
16958      * This gets called when a `VolumePanel` gains hover via a `mouseover` event.
16959      * Turns on listening for `mouseover` event. When they happen it
16960      * calls `this.handleMouseOver`.
16961      *
16962      * @param {EventTarget~Event} event
16963      *        The `mouseover` event that caused this function to be called.
16964      *
16965      * @listens mouseover
16966      */
16967     ;
16968 
16969     _proto.handleMouseOver = function handleMouseOver(event) {
16970       this.addClass('vjs-hover');
16971       on(document, 'keyup', bind(this, this.handleKeyPress));
16972     }
16973     /**
16974      * This gets called when a `VolumePanel` gains hover via a `mouseout` event.
16975      * Turns on listening for `mouseout` event. When they happen it
16976      * calls `this.handleMouseOut`.
16977      *
16978      * @param {EventTarget~Event} event
16979      *        The `mouseout` event that caused this function to be called.
16980      *
16981      * @listens mouseout
16982      */
16983     ;
16984 
16985     _proto.handleMouseOut = function handleMouseOut(event) {
16986       this.removeClass('vjs-hover');
16987       off(document, 'keyup', bind(this, this.handleKeyPress));
16988     }
16989     /**
16990      * Handles `keyup` event on the document or `keydown` event on the `VolumePanel`,
16991      * looking for ESC, which hides the `VolumeControl`.
16992      *
16993      * @param {EventTarget~Event} event
16994      *        The keypress that triggered this event.
16995      *
16996      * @listens keydown | keyup
16997      */
16998     ;
16999 
17000     _proto.handleKeyPress = function handleKeyPress(event) {
17001       if (keycode.isEventKey(event, 'Esc')) {
17002         this.handleMouseOut();
17003       }
17004     };
17005 
17006     return VolumePanel;
17007   }(Component);
17008   /**
17009    * Default options for the `VolumeControl`
17010    *
17011    * @type {Object}
17012    * @private
17013    */
17014 
17015 
17016   VolumePanel.prototype.options_ = {
17017     children: ['muteToggle', 'volumeControl']
17018   };
17019   Component.registerComponent('VolumePanel', VolumePanel);
17020 
17021   /**
17022    * The Menu component is used to build popup menus, including subtitle and
17023    * captions selection menus.
17024    *
17025    * @extends Component
17026    */
17027 
17028   var Menu =
17029   /*#__PURE__*/
17030   function (_Component) {
17031     inheritsLoose(Menu, _Component);
17032 
17033     /**
17034      * Create an instance of this class.
17035      *
17036      * @param {Player} player
17037      *        the player that this component should attach to
17038      *
17039      * @param {Object} [options]
17040      *        Object of option names and values
17041      *
17042      */
17043     function Menu(player, options) {
17044       var _this;
17045 
17046       _this = _Component.call(this, player, options) || this;
17047 
17048       if (options) {
17049         _this.menuButton_ = options.menuButton;
17050       }
17051 
17052       _this.focusedChild_ = -1;
17053 
17054       _this.on('keydown', _this.handleKeyDown); // All the menu item instances share the same blur handler provided by the menu container.
17055 
17056 
17057       _this.boundHandleBlur_ = bind(assertThisInitialized(_this), _this.handleBlur);
17058       _this.boundHandleTapClick_ = bind(assertThisInitialized(_this), _this.handleTapClick);
17059       return _this;
17060     }
17061     /**
17062      * Add event listeners to the {@link MenuItem}.
17063      *
17064      * @param {Object} component
17065      *        The instance of the `MenuItem` to add listeners to.
17066      *
17067      */
17068 
17069 
17070     var _proto = Menu.prototype;
17071 
17072     _proto.addEventListenerForItem = function addEventListenerForItem(component) {
17073       if (!(component instanceof Component)) {
17074         return;
17075       }
17076 
17077       this.on(component, 'blur', this.boundHandleBlur_);
17078       this.on(component, ['tap', 'click'], this.boundHandleTapClick_);
17079     }
17080     /**
17081      * Remove event listeners from the {@link MenuItem}.
17082      *
17083      * @param {Object} component
17084      *        The instance of the `MenuItem` to remove listeners.
17085      *
17086      */
17087     ;
17088 
17089     _proto.removeEventListenerForItem = function removeEventListenerForItem(component) {
17090       if (!(component instanceof Component)) {
17091         return;
17092       }
17093 
17094       this.off(component, 'blur', this.boundHandleBlur_);
17095       this.off(component, ['tap', 'click'], this.boundHandleTapClick_);
17096     }
17097     /**
17098      * This method will be called indirectly when the component has been added
17099      * before the component adds to the new menu instance by `addItem`.
17100      * In this case, the original menu instance will remove the component
17101      * by calling `removeChild`.
17102      *
17103      * @param {Object} component
17104      *        The instance of the `MenuItem`
17105      */
17106     ;
17107 
17108     _proto.removeChild = function removeChild(component) {
17109       if (typeof component === 'string') {
17110         component = this.getChild(component);
17111       }
17112 
17113       this.removeEventListenerForItem(component);
17114 
17115       _Component.prototype.removeChild.call(this, component);
17116     }
17117     /**
17118      * Add a {@link MenuItem} to the menu.
17119      *
17120      * @param {Object|string} component
17121      *        The name or instance of the `MenuItem` to add.
17122      *
17123      */
17124     ;
17125 
17126     _proto.addItem = function addItem(component) {
17127       var childComponent = this.addChild(component);
17128 
17129       if (childComponent) {
17130         this.addEventListenerForItem(childComponent);
17131       }
17132     }
17133     /**
17134      * Create the `Menu`s DOM element.
17135      *
17136      * @return {Element}
17137      *         the element that was created
17138      */
17139     ;
17140 
17141     _proto.createEl = function createEl$1() {
17142       var contentElType = this.options_.contentElType || 'ul';
17143       this.contentEl_ = createEl(contentElType, {
17144         className: 'vjs-menu-content'
17145       });
17146       this.contentEl_.setAttribute('role', 'menu');
17147 
17148       var el = _Component.prototype.createEl.call(this, 'div', {
17149         append: this.contentEl_,
17150         className: 'vjs-menu'
17151       });
17152 
17153       el.appendChild(this.contentEl_); // Prevent clicks from bubbling up. Needed for Menu Buttons,
17154       // where a click on the parent is significant
17155 
17156       on(el, 'click', function (event) {
17157         event.preventDefault();
17158         event.stopImmediatePropagation();
17159       });
17160       return el;
17161     };
17162 
17163     _proto.dispose = function dispose() {
17164       this.contentEl_ = null;
17165       this.boundHandleBlur_ = null;
17166       this.boundHandleTapClick_ = null;
17167 
17168       _Component.prototype.dispose.call(this);
17169     }
17170     /**
17171      * Called when a `MenuItem` loses focus.
17172      *
17173      * @param {EventTarget~Event} event
17174      *        The `blur` event that caused this function to be called.
17175      *
17176      * @listens blur
17177      */
17178     ;
17179 
17180     _proto.handleBlur = function handleBlur(event) {
17181       var relatedTarget = event.relatedTarget || document.activeElement; // Close menu popup when a user clicks outside the menu
17182 
17183       if (!this.children().some(function (element) {
17184         return element.el() === relatedTarget;
17185       })) {
17186         var btn = this.menuButton_;
17187 
17188         if (btn && btn.buttonPressed_ && relatedTarget !== btn.el().firstChild) {
17189           btn.unpressButton();
17190         }
17191       }
17192     }
17193     /**
17194      * Called when a `MenuItem` gets clicked or tapped.
17195      *
17196      * @param {EventTarget~Event} event
17197      *        The `click` or `tap` event that caused this function to be called.
17198      *
17199      * @listens click,tap
17200      */
17201     ;
17202 
17203     _proto.handleTapClick = function handleTapClick(event) {
17204       // Unpress the associated MenuButton, and move focus back to it
17205       if (this.menuButton_) {
17206         this.menuButton_.unpressButton();
17207         var childComponents = this.children();
17208 
17209         if (!Array.isArray(childComponents)) {
17210           return;
17211         }
17212 
17213         var foundComponent = childComponents.filter(function (component) {
17214           return component.el() === event.target;
17215         })[0];
17216 
17217         if (!foundComponent) {
17218           return;
17219         } // don't focus menu button if item is a caption settings item
17220         // because focus will move elsewhere
17221 
17222 
17223         if (foundComponent.name() !== 'CaptionSettingsMenuItem') {
17224           this.menuButton_.focus();
17225         }
17226       }
17227     }
17228     /**
17229      * Handle a `keydown` event on this menu. This listener is added in the constructor.
17230      *
17231      * @param {EventTarget~Event} event
17232      *        A `keydown` event that happened on the menu.
17233      *
17234      * @listens keydown
17235      */
17236     ;
17237 
17238     _proto.handleKeyDown = function handleKeyDown(event) {
17239       // Left and Down Arrows
17240       if (keycode.isEventKey(event, 'Left') || keycode.isEventKey(event, 'Down')) {
17241         event.preventDefault();
17242         event.stopPropagation();
17243         this.stepForward(); // Up and Right Arrows
17244       } else if (keycode.isEventKey(event, 'Right') || keycode.isEventKey(event, 'Up')) {
17245         event.preventDefault();
17246         event.stopPropagation();
17247         this.stepBack();
17248       }
17249     }
17250     /**
17251      * Move to next (lower) menu item for keyboard users.
17252      */
17253     ;
17254 
17255     _proto.stepForward = function stepForward() {
17256       var stepChild = 0;
17257 
17258       if (this.focusedChild_ !== undefined) {
17259         stepChild = this.focusedChild_ + 1;
17260       }
17261 
17262       this.focus(stepChild);
17263     }
17264     /**
17265      * Move to previous (higher) menu item for keyboard users.
17266      */
17267     ;
17268 
17269     _proto.stepBack = function stepBack() {
17270       var stepChild = 0;
17271 
17272       if (this.focusedChild_ !== undefined) {
17273         stepChild = this.focusedChild_ - 1;
17274       }
17275 
17276       this.focus(stepChild);
17277     }
17278     /**
17279      * Set focus on a {@link MenuItem} in the `Menu`.
17280      *
17281      * @param {Object|string} [item=0]
17282      *        Index of child item set focus on.
17283      */
17284     ;
17285 
17286     _proto.focus = function focus(item) {
17287       if (item === void 0) {
17288         item = 0;
17289       }
17290 
17291       var children = this.children().slice();
17292       var haveTitle = children.length && children[0].className && /vjs-menu-title/.test(children[0].className);
17293 
17294       if (haveTitle) {
17295         children.shift();
17296       }
17297 
17298       if (children.length > 0) {
17299         if (item < 0) {
17300           item = 0;
17301         } else if (item >= children.length) {
17302           item = children.length - 1;
17303         }
17304 
17305         this.focusedChild_ = item;
17306         children[item].el_.focus();
17307       }
17308     };
17309 
17310     return Menu;
17311   }(Component);
17312 
17313   Component.registerComponent('Menu', Menu);
17314 
17315   /**
17316    * A `MenuButton` class for any popup {@link Menu}.
17317    *
17318    * @extends Component
17319    */
17320 
17321   var MenuButton =
17322   /*#__PURE__*/
17323   function (_Component) {
17324     inheritsLoose(MenuButton, _Component);
17325 
17326     /**
17327      * Creates an instance of this class.
17328      *
17329      * @param {Player} player
17330      *        The `Player` that this class should be attached to.
17331      *
17332      * @param {Object} [options={}]
17333      *        The key/value store of player options.
17334      */
17335     function MenuButton(player, options) {
17336       var _this;
17337 
17338       if (options === void 0) {
17339         options = {};
17340       }
17341 
17342       _this = _Component.call(this, player, options) || this;
17343       _this.menuButton_ = new Button(player, options);
17344 
17345       _this.menuButton_.controlText(_this.controlText_);
17346 
17347       _this.menuButton_.el_.setAttribute('aria-haspopup', 'true'); // Add buildCSSClass values to the button, not the wrapper
17348 
17349 
17350       var buttonClass = Button.prototype.buildCSSClass();
17351       _this.menuButton_.el_.className = _this.buildCSSClass() + ' ' + buttonClass;
17352 
17353       _this.menuButton_.removeClass('vjs-control');
17354 
17355       _this.addChild(_this.menuButton_);
17356 
17357       _this.update();
17358 
17359       _this.enabled_ = true;
17360 
17361       _this.on(_this.menuButton_, 'tap', _this.handleClick);
17362 
17363       _this.on(_this.menuButton_, 'click', _this.handleClick);
17364 
17365       _this.on(_this.menuButton_, 'keydown', _this.handleKeyDown);
17366 
17367       _this.on(_this.menuButton_, 'mouseenter', function () {
17368         _this.addClass('vjs-hover');
17369 
17370         _this.menu.show();
17371 
17372         on(document, 'keyup', bind(assertThisInitialized(_this), _this.handleMenuKeyUp));
17373       });
17374 
17375       _this.on('mouseleave', _this.handleMouseLeave);
17376 
17377       _this.on('keydown', _this.handleSubmenuKeyDown);
17378 
17379       return _this;
17380     }
17381     /**
17382      * Update the menu based on the current state of its items.
17383      */
17384 
17385 
17386     var _proto = MenuButton.prototype;
17387 
17388     _proto.update = function update() {
17389       var menu = this.createMenu();
17390 
17391       if (this.menu) {
17392         this.menu.dispose();
17393         this.removeChild(this.menu);
17394       }
17395 
17396       this.menu = menu;
17397       this.addChild(menu);
17398       /**
17399        * Track the state of the menu button
17400        *
17401        * @type {Boolean}
17402        * @private
17403        */
17404 
17405       this.buttonPressed_ = false;
17406       this.menuButton_.el_.setAttribute('aria-expanded', 'false');
17407 
17408       if (this.items && this.items.length <= this.hideThreshold_) {
17409         this.hide();
17410       } else {
17411         this.show();
17412       }
17413     }
17414     /**
17415      * Create the menu and add all items to it.
17416      *
17417      * @return {Menu}
17418      *         The constructed menu
17419      */
17420     ;
17421 
17422     _proto.createMenu = function createMenu() {
17423       var menu = new Menu(this.player_, {
17424         menuButton: this
17425       });
17426       /**
17427        * Hide the menu if the number of items is less than or equal to this threshold. This defaults
17428        * to 0 and whenever we add items which can be hidden to the menu we'll increment it. We list
17429        * it here because every time we run `createMenu` we need to reset the value.
17430        *
17431        * @protected
17432        * @type {Number}
17433        */
17434 
17435       this.hideThreshold_ = 0; // Add a title list item to the top
17436 
17437       if (this.options_.title) {
17438         var titleEl = createEl('li', {
17439           className: 'vjs-menu-title',
17440           innerHTML: toTitleCase(this.options_.title),
17441           tabIndex: -1
17442         });
17443         this.hideThreshold_ += 1;
17444         var titleComponent = new Component(this.player_, {
17445           el: titleEl
17446         });
17447         menu.addItem(titleComponent);
17448       }
17449 
17450       this.items = this.createItems();
17451 
17452       if (this.items) {
17453         // Add menu items to the menu
17454         for (var i = 0; i < this.items.length; i++) {
17455           menu.addItem(this.items[i]);
17456         }
17457       }
17458 
17459       return menu;
17460     }
17461     /**
17462      * Create the list of menu items. Specific to each subclass.
17463      *
17464      * @abstract
17465      */
17466     ;
17467 
17468     _proto.createItems = function createItems() {}
17469     /**
17470      * Create the `MenuButtons`s DOM element.
17471      *
17472      * @return {Element}
17473      *         The element that gets created.
17474      */
17475     ;
17476 
17477     _proto.createEl = function createEl() {
17478       return _Component.prototype.createEl.call(this, 'div', {
17479         className: this.buildWrapperCSSClass()
17480       }, {});
17481     }
17482     /**
17483      * Allow sub components to stack CSS class names for the wrapper element
17484      *
17485      * @return {string}
17486      *         The constructed wrapper DOM `className`
17487      */
17488     ;
17489 
17490     _proto.buildWrapperCSSClass = function buildWrapperCSSClass() {
17491       var menuButtonClass = 'vjs-menu-button'; // If the inline option is passed, we want to use different styles altogether.
17492 
17493       if (this.options_.inline === true) {
17494         menuButtonClass += '-inline';
17495       } else {
17496         menuButtonClass += '-popup';
17497       } // TODO: Fix the CSS so that this isn't necessary
17498 
17499 
17500       var buttonClass = Button.prototype.buildCSSClass();
17501       return "vjs-menu-button " + menuButtonClass + " " + buttonClass + " " + _Component.prototype.buildCSSClass.call(this);
17502     }
17503     /**
17504      * Builds the default DOM `className`.
17505      *
17506      * @return {string}
17507      *         The DOM `className` for this object.
17508      */
17509     ;
17510 
17511     _proto.buildCSSClass = function buildCSSClass() {
17512       var menuButtonClass = 'vjs-menu-button'; // If the inline option is passed, we want to use different styles altogether.
17513 
17514       if (this.options_.inline === true) {
17515         menuButtonClass += '-inline';
17516       } else {
17517         menuButtonClass += '-popup';
17518       }
17519 
17520       return "vjs-menu-button " + menuButtonClass + " " + _Component.prototype.buildCSSClass.call(this);
17521     }
17522     /**
17523      * Get or set the localized control text that will be used for accessibility.
17524      *
17525      * > NOTE: This will come from the internal `menuButton_` element.
17526      *
17527      * @param {string} [text]
17528      *        Control text for element.
17529      *
17530      * @param {Element} [el=this.menuButton_.el()]
17531      *        Element to set the title on.
17532      *
17533      * @return {string}
17534      *         - The control text when getting
17535      */
17536     ;
17537 
17538     _proto.controlText = function controlText(text, el) {
17539       if (el === void 0) {
17540         el = this.menuButton_.el();
17541       }
17542 
17543       return this.menuButton_.controlText(text, el);
17544     }
17545     /**
17546      * Dispose of the `menu-button` and all child components.
17547      */
17548     ;
17549 
17550     _proto.dispose = function dispose() {
17551       this.handleMouseLeave();
17552 
17553       _Component.prototype.dispose.call(this);
17554     }
17555     /**
17556      * Handle a click on a `MenuButton`.
17557      * See {@link ClickableComponent#handleClick} for instances where this is called.
17558      *
17559      * @param {EventTarget~Event} event
17560      *        The `keydown`, `tap`, or `click` event that caused this function to be
17561      *        called.
17562      *
17563      * @listens tap
17564      * @listens click
17565      */
17566     ;
17567 
17568     _proto.handleClick = function handleClick(event) {
17569       if (this.buttonPressed_) {
17570         this.unpressButton();
17571       } else {
17572         this.pressButton();
17573       }
17574     }
17575     /**
17576      * Handle `mouseleave` for `MenuButton`.
17577      *
17578      * @param {EventTarget~Event} event
17579      *        The `mouseleave` event that caused this function to be called.
17580      *
17581      * @listens mouseleave
17582      */
17583     ;
17584 
17585     _proto.handleMouseLeave = function handleMouseLeave(event) {
17586       this.removeClass('vjs-hover');
17587       off(document, 'keyup', bind(this, this.handleMenuKeyUp));
17588     }
17589     /**
17590      * Set the focus to the actual button, not to this element
17591      */
17592     ;
17593 
17594     _proto.focus = function focus() {
17595       this.menuButton_.focus();
17596     }
17597     /**
17598      * Remove the focus from the actual button, not this element
17599      */
17600     ;
17601 
17602     _proto.blur = function blur() {
17603       this.menuButton_.blur();
17604     }
17605     /**
17606      * Handle tab, escape, down arrow, and up arrow keys for `MenuButton`. See
17607      * {@link ClickableComponent#handleKeyDown} for instances where this is called.
17608      *
17609      * @param {EventTarget~Event} event
17610      *        The `keydown` event that caused this function to be called.
17611      *
17612      * @listens keydown
17613      */
17614     ;
17615 
17616     _proto.handleKeyDown = function handleKeyDown(event) {
17617       // Escape or Tab unpress the 'button'
17618       if (keycode.isEventKey(event, 'Esc') || keycode.isEventKey(event, 'Tab')) {
17619         if (this.buttonPressed_) {
17620           this.unpressButton();
17621         } // Don't preventDefault for Tab key - we still want to lose focus
17622 
17623 
17624         if (!keycode.isEventKey(event, 'Tab')) {
17625           event.preventDefault(); // Set focus back to the menu button's button
17626 
17627           this.menuButton_.focus();
17628         } // Up Arrow or Down Arrow also 'press' the button to open the menu
17629 
17630       } else if (keycode.isEventKey(event, 'Up') || keycode.isEventKey(event, 'Down')) {
17631         if (!this.buttonPressed_) {
17632           event.preventDefault();
17633           this.pressButton();
17634         }
17635       }
17636     }
17637     /**
17638      * Handle a `keyup` event on a `MenuButton`. The listener for this is added in
17639      * the constructor.
17640      *
17641      * @param {EventTarget~Event} event
17642      *        Key press event
17643      *
17644      * @listens keyup
17645      */
17646     ;
17647 
17648     _proto.handleMenuKeyUp = function handleMenuKeyUp(event) {
17649       // Escape hides popup menu
17650       if (keycode.isEventKey(event, 'Esc') || keycode.isEventKey(event, 'Tab')) {
17651         this.removeClass('vjs-hover');
17652       }
17653     }
17654     /**
17655      * This method name now delegates to `handleSubmenuKeyDown`. This means
17656      * anyone calling `handleSubmenuKeyPress` will not see their method calls
17657      * stop working.
17658      *
17659      * @param {EventTarget~Event} event
17660      *        The event that caused this function to be called.
17661      */
17662     ;
17663 
17664     _proto.handleSubmenuKeyPress = function handleSubmenuKeyPress(event) {
17665       this.handleSubmenuKeyDown(event);
17666     }
17667     /**
17668      * Handle a `keydown` event on a sub-menu. The listener for this is added in
17669      * the constructor.
17670      *
17671      * @param {EventTarget~Event} event
17672      *        Key press event
17673      *
17674      * @listens keydown
17675      */
17676     ;
17677 
17678     _proto.handleSubmenuKeyDown = function handleSubmenuKeyDown(event) {
17679       // Escape or Tab unpress the 'button'
17680       if (keycode.isEventKey(event, 'Esc') || keycode.isEventKey(event, 'Tab')) {
17681         if (this.buttonPressed_) {
17682           this.unpressButton();
17683         } // Don't preventDefault for Tab key - we still want to lose focus
17684 
17685 
17686         if (!keycode.isEventKey(event, 'Tab')) {
17687           event.preventDefault(); // Set focus back to the menu button's button
17688 
17689           this.menuButton_.focus();
17690         }
17691       }
17692     }
17693     /**
17694      * Put the current `MenuButton` into a pressed state.
17695      */
17696     ;
17697 
17698     _proto.pressButton = function pressButton() {
17699       if (this.enabled_) {
17700         this.buttonPressed_ = true;
17701         this.menu.show();
17702         this.menu.lockShowing();
17703         this.menuButton_.el_.setAttribute('aria-expanded', 'true'); // set the focus into the submenu, except on iOS where it is resulting in
17704         // undesired scrolling behavior when the player is in an iframe
17705 
17706         if (IS_IOS && isInFrame()) {
17707           // Return early so that the menu isn't focused
17708           return;
17709         }
17710 
17711         this.menu.focus();
17712       }
17713     }
17714     /**
17715      * Take the current `MenuButton` out of a pressed state.
17716      */
17717     ;
17718 
17719     _proto.unpressButton = function unpressButton() {
17720       if (this.enabled_) {
17721         this.buttonPressed_ = false;
17722         this.menu.unlockShowing();
17723         this.menu.hide();
17724         this.menuButton_.el_.setAttribute('aria-expanded', 'false');
17725       }
17726     }
17727     /**
17728      * Disable the `MenuButton`. Don't allow it to be clicked.
17729      */
17730     ;
17731 
17732     _proto.disable = function disable() {
17733       this.unpressButton();
17734       this.enabled_ = false;
17735       this.addClass('vjs-disabled');
17736       this.menuButton_.disable();
17737     }
17738     /**
17739      * Enable the `MenuButton`. Allow it to be clicked.
17740      */
17741     ;
17742 
17743     _proto.enable = function enable() {
17744       this.enabled_ = true;
17745       this.removeClass('vjs-disabled');
17746       this.menuButton_.enable();
17747     };
17748 
17749     return MenuButton;
17750   }(Component);
17751 
17752   Component.registerComponent('MenuButton', MenuButton);
17753 
17754   /**
17755    * The base class for buttons that toggle specific  track types (e.g. subtitles).
17756    *
17757    * @extends MenuButton
17758    */
17759 
17760   var TrackButton =
17761   /*#__PURE__*/
17762   function (_MenuButton) {
17763     inheritsLoose(TrackButton, _MenuButton);
17764 
17765     /**
17766      * Creates an instance of this class.
17767      *
17768      * @param {Player} player
17769      *        The `Player` that this class should be attached to.
17770      *
17771      * @param {Object} [options]
17772      *        The key/value store of player options.
17773      */
17774     function TrackButton(player, options) {
17775       var _this;
17776 
17777       var tracks = options.tracks;
17778       _this = _MenuButton.call(this, player, options) || this;
17779 
17780       if (_this.items.length <= 1) {
17781         _this.hide();
17782       }
17783 
17784       if (!tracks) {
17785         return assertThisInitialized(_this);
17786       }
17787 
17788       var updateHandler = bind(assertThisInitialized(_this), _this.update);
17789       tracks.addEventListener('removetrack', updateHandler);
17790       tracks.addEventListener('addtrack', updateHandler);
17791 
17792       _this.player_.on('ready', updateHandler);
17793 
17794       _this.player_.on('dispose', function () {
17795         tracks.removeEventListener('removetrack', updateHandler);
17796         tracks.removeEventListener('addtrack', updateHandler);
17797       });
17798 
17799       return _this;
17800     }
17801 
17802     return TrackButton;
17803   }(MenuButton);
17804 
17805   Component.registerComponent('TrackButton', TrackButton);
17806 
17807   /**
17808    * @file menu-keys.js
17809    */
17810 
17811   /**
17812     * All keys used for operation of a menu (`MenuButton`, `Menu`, and `MenuItem`)
17813     * Note that 'Enter' and 'Space' are not included here (otherwise they would
17814     * prevent the `MenuButton` and `MenuItem` from being keyboard-clickable)
17815     * @typedef MenuKeys
17816     * @array
17817     */
17818   var MenuKeys = ['Tab', 'Esc', 'Up', 'Down', 'Right', 'Left'];
17819 
17820   /**
17821    * The component for a menu item. `<li>`
17822    *
17823    * @extends ClickableComponent
17824    */
17825 
17826   var MenuItem =
17827   /*#__PURE__*/
17828   function (_ClickableComponent) {
17829     inheritsLoose(MenuItem, _ClickableComponent);
17830 
17831     /**
17832      * Creates an instance of the this class.
17833      *
17834      * @param {Player} player
17835      *        The `Player` that this class should be attached to.
17836      *
17837      * @param {Object} [options={}]
17838      *        The key/value store of player options.
17839      *
17840      */
17841     function MenuItem(player, options) {
17842       var _this;
17843 
17844       _this = _ClickableComponent.call(this, player, options) || this;
17845       _this.selectable = options.selectable;
17846       _this.isSelected_ = options.selected || false;
17847       _this.multiSelectable = options.multiSelectable;
17848 
17849       _this.selected(_this.isSelected_);
17850 
17851       if (_this.selectable) {
17852         if (_this.multiSelectable) {
17853           _this.el_.setAttribute('role', 'menuitemcheckbox');
17854         } else {
17855           _this.el_.setAttribute('role', 'menuitemradio');
17856         }
17857       } else {
17858         _this.el_.setAttribute('role', 'menuitem');
17859       }
17860 
17861       return _this;
17862     }
17863     /**
17864      * Create the `MenuItem's DOM element
17865      *
17866      * @param {string} [type=li]
17867      *        Element's node type, not actually used, always set to `li`.
17868      *
17869      * @param {Object} [props={}]
17870      *        An object of properties that should be set on the element
17871      *
17872      * @param {Object} [attrs={}]
17873      *        An object of attributes that should be set on the element
17874      *
17875      * @return {Element}
17876      *         The element that gets created.
17877      */
17878 
17879 
17880     var _proto = MenuItem.prototype;
17881 
17882     _proto.createEl = function createEl(type, props, attrs) {
17883       // The control is textual, not just an icon
17884       this.nonIconControl = true;
17885       return _ClickableComponent.prototype.createEl.call(this, 'li', assign({
17886         className: 'vjs-menu-item',
17887         innerHTML: "<span class=\"vjs-menu-item-text\">" + this.localize(this.options_.label) + "</span>",
17888         tabIndex: -1
17889       }, props), attrs);
17890     }
17891     /**
17892      * Ignore keys which are used by the menu, but pass any other ones up. See
17893      * {@link ClickableComponent#handleKeyDown} for instances where this is called.
17894      *
17895      * @param {EventTarget~Event} event
17896      *        The `keydown` event that caused this function to be called.
17897      *
17898      * @listens keydown
17899      */
17900     ;
17901 
17902     _proto.handleKeyDown = function handleKeyDown(event) {
17903       if (!MenuKeys.some(function (key) {
17904         return keycode.isEventKey(event, key);
17905       })) {
17906         // Pass keydown handling up for unused keys
17907         _ClickableComponent.prototype.handleKeyDown.call(this, event);
17908       }
17909     }
17910     /**
17911      * Any click on a `MenuItem` puts it into the selected state.
17912      * See {@link ClickableComponent#handleClick} for instances where this is called.
17913      *
17914      * @param {EventTarget~Event} event
17915      *        The `keydown`, `tap`, or `click` event that caused this function to be
17916      *        called.
17917      *
17918      * @listens tap
17919      * @listens click
17920      */
17921     ;
17922 
17923     _proto.handleClick = function handleClick(event) {
17924       this.selected(true);
17925     }
17926     /**
17927      * Set the state for this menu item as selected or not.
17928      *
17929      * @param {boolean} selected
17930      *        if the menu item is selected or not
17931      */
17932     ;
17933 
17934     _proto.selected = function selected(_selected) {
17935       if (this.selectable) {
17936         if (_selected) {
17937           this.addClass('vjs-selected');
17938           this.el_.setAttribute('aria-checked', 'true'); // aria-checked isn't fully supported by browsers/screen readers,
17939           // so indicate selected state to screen reader in the control text.
17940 
17941           this.controlText(', selected');
17942           this.isSelected_ = true;
17943         } else {
17944           this.removeClass('vjs-selected');
17945           this.el_.setAttribute('aria-checked', 'false'); // Indicate un-selected state to screen reader
17946 
17947           this.controlText('');
17948           this.isSelected_ = false;
17949         }
17950       }
17951     };
17952 
17953     return MenuItem;
17954   }(ClickableComponent);
17955 
17956   Component.registerComponent('MenuItem', MenuItem);
17957 
17958   /**
17959    * The specific menu item type for selecting a language within a text track kind
17960    *
17961    * @extends MenuItem
17962    */
17963 
17964   var TextTrackMenuItem =
17965   /*#__PURE__*/
17966   function (_MenuItem) {
17967     inheritsLoose(TextTrackMenuItem, _MenuItem);
17968 
17969     /**
17970      * Creates an instance of this class.
17971      *
17972      * @param {Player} player
17973      *        The `Player` that this class should be attached to.
17974      *
17975      * @param {Object} [options]
17976      *        The key/value store of player options.
17977      */
17978     function TextTrackMenuItem(player, options) {
17979       var _this;
17980 
17981       var track = options.track;
17982       var tracks = player.textTracks(); // Modify options for parent MenuItem class's init.
17983 
17984       options.label = track.label || track.language || 'Unknown';
17985       options.selected = track.mode === 'showing';
17986       _this = _MenuItem.call(this, player, options) || this;
17987       _this.track = track; // Determine the relevant kind(s) of tracks for this component and filter
17988       // out empty kinds.
17989 
17990       _this.kinds = (options.kinds || [options.kind || _this.track.kind]).filter(Boolean);
17991 
17992       var changeHandler = function changeHandler() {
17993         for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
17994           args[_key] = arguments[_key];
17995         }
17996 
17997         _this.handleTracksChange.apply(assertThisInitialized(_this), args);
17998       };
17999 
18000       var selectedLanguageChangeHandler = function selectedLanguageChangeHandler() {
18001         for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
18002           args[_key2] = arguments[_key2];
18003         }
18004 
18005         _this.handleSelectedLanguageChange.apply(assertThisInitialized(_this), args);
18006       };
18007 
18008       player.on(['loadstart', 'texttrackchange'], changeHandler);
18009       tracks.addEventListener('change', changeHandler);
18010       tracks.addEventListener('selectedlanguagechange', selectedLanguageChangeHandler);
18011 
18012       _this.on('dispose', function () {
18013         player.off(['loadstart', 'texttrackchange'], changeHandler);
18014         tracks.removeEventListener('change', changeHandler);
18015         tracks.removeEventListener('selectedlanguagechange', selectedLanguageChangeHandler);
18016       }); // iOS7 doesn't dispatch change events to TextTrackLists when an
18017       // associated track's mode changes. Without something like
18018       // Object.observe() (also not present on iOS7), it's not
18019       // possible to detect changes to the mode attribute and polyfill
18020       // the change event. As a poor substitute, we manually dispatch
18021       // change events whenever the controls modify the mode.
18022 
18023 
18024       if (tracks.onchange === undefined) {
18025         var event;
18026 
18027         _this.on(['tap', 'click'], function () {
18028           if (typeof window$1.Event !== 'object') {
18029             // Android 2.3 throws an Illegal Constructor error for window.Event
18030             try {
18031               event = new window$1.Event('change');
18032             } catch (err) {// continue regardless of error
18033             }
18034           }
18035 
18036           if (!event) {
18037             event = document.createEvent('Event');
18038             event.initEvent('change', true, true);
18039           }
18040 
18041           tracks.dispatchEvent(event);
18042         });
18043       } // set the default state based on current tracks
18044 
18045 
18046       _this.handleTracksChange();
18047 
18048       return _this;
18049     }
18050     /**
18051      * This gets called when an `TextTrackMenuItem` is "clicked". See
18052      * {@link ClickableComponent} for more detailed information on what a click can be.
18053      *
18054      * @param {EventTarget~Event} event
18055      *        The `keydown`, `tap`, or `click` event that caused this function to be
18056      *        called.
18057      *
18058      * @listens tap
18059      * @listens click
18060      */
18061 
18062 
18063     var _proto = TextTrackMenuItem.prototype;
18064 
18065     _proto.handleClick = function handleClick(event) {
18066       var referenceTrack = this.track;
18067       var tracks = this.player_.textTracks();
18068 
18069       _MenuItem.prototype.handleClick.call(this, event);
18070 
18071       if (!tracks) {
18072         return;
18073       }
18074 
18075       for (var i = 0; i < tracks.length; i++) {
18076         var track = tracks[i]; // If the track from the text tracks list is not of the right kind,
18077         // skip it. We do not want to affect tracks of incompatible kind(s).
18078 
18079         if (this.kinds.indexOf(track.kind) === -1) {
18080           continue;
18081         } // If this text track is the component's track and it is not showing,
18082         // set it to showing.
18083 
18084 
18085         if (track === referenceTrack) {
18086           if (track.mode !== 'showing') {
18087             track.mode = 'showing';
18088           } // If this text track is not the component's track and it is not
18089           // disabled, set it to disabled.
18090 
18091         } else if (track.mode !== 'disabled') {
18092           track.mode = 'disabled';
18093         }
18094       }
18095     }
18096     /**
18097      * Handle text track list change
18098      *
18099      * @param {EventTarget~Event} event
18100      *        The `change` event that caused this function to be called.
18101      *
18102      * @listens TextTrackList#change
18103      */
18104     ;
18105 
18106     _proto.handleTracksChange = function handleTracksChange(event) {
18107       var shouldBeSelected = this.track.mode === 'showing'; // Prevent redundant selected() calls because they may cause
18108       // screen readers to read the appended control text unnecessarily
18109 
18110       if (shouldBeSelected !== this.isSelected_) {
18111         this.selected(shouldBeSelected);
18112       }
18113     };
18114 
18115     _proto.handleSelectedLanguageChange = function handleSelectedLanguageChange(event) {
18116       if (this.track.mode === 'showing') {
18117         var selectedLanguage = this.player_.cache_.selectedLanguage; // Don't replace the kind of track across the same language
18118 
18119         if (selectedLanguage && selectedLanguage.enabled && selectedLanguage.language === this.track.language && selectedLanguage.kind !== this.track.kind) {
18120           return;
18121         }
18122 
18123         this.player_.cache_.selectedLanguage = {
18124           enabled: true,
18125           language: this.track.language,
18126           kind: this.track.kind
18127         };
18128       }
18129     };
18130 
18131     _proto.dispose = function dispose() {
18132       // remove reference to track object on dispose
18133       this.track = null;
18134 
18135       _MenuItem.prototype.dispose.call(this);
18136     };
18137 
18138     return TextTrackMenuItem;
18139   }(MenuItem);
18140 
18141   Component.registerComponent('TextTrackMenuItem', TextTrackMenuItem);
18142 
18143   /**
18144    * A special menu item for turning of a specific type of text track
18145    *
18146    * @extends TextTrackMenuItem
18147    */
18148 
18149   var OffTextTrackMenuItem =
18150   /*#__PURE__*/
18151   function (_TextTrackMenuItem) {
18152     inheritsLoose(OffTextTrackMenuItem, _TextTrackMenuItem);
18153 
18154     /**
18155      * Creates an instance of this class.
18156      *
18157      * @param {Player} player
18158      *        The `Player` that this class should be attached to.
18159      *
18160      * @param {Object} [options]
18161      *        The key/value store of player options.
18162      */
18163     function OffTextTrackMenuItem(player, options) {
18164       // Create pseudo track info
18165       // Requires options['kind']
18166       options.track = {
18167         player: player,
18168         // it is no longer necessary to store `kind` or `kinds` on the track itself
18169         // since they are now stored in the `kinds` property of all instances of
18170         // TextTrackMenuItem, but this will remain for backwards compatibility
18171         kind: options.kind,
18172         kinds: options.kinds,
18173         "default": false,
18174         mode: 'disabled'
18175       };
18176 
18177       if (!options.kinds) {
18178         options.kinds = [options.kind];
18179       }
18180 
18181       if (options.label) {
18182         options.track.label = options.label;
18183       } else {
18184         options.track.label = options.kinds.join(' and ') + ' off';
18185       } // MenuItem is selectable
18186 
18187 
18188       options.selectable = true; // MenuItem is NOT multiSelectable (i.e. only one can be marked "selected" at a time)
18189 
18190       options.multiSelectable = false;
18191       return _TextTrackMenuItem.call(this, player, options) || this;
18192     }
18193     /**
18194      * Handle text track change
18195      *
18196      * @param {EventTarget~Event} event
18197      *        The event that caused this function to run
18198      */
18199 
18200 
18201     var _proto = OffTextTrackMenuItem.prototype;
18202 
18203     _proto.handleTracksChange = function handleTracksChange(event) {
18204       var tracks = this.player().textTracks();
18205       var shouldBeSelected = true;
18206 
18207       for (var i = 0, l = tracks.length; i < l; i++) {
18208         var track = tracks[i];
18209 
18210         if (this.options_.kinds.indexOf(track.kind) > -1 && track.mode === 'showing') {
18211           shouldBeSelected = false;
18212           break;
18213         }
18214       } // Prevent redundant selected() calls because they may cause
18215       // screen readers to read the appended control text unnecessarily
18216 
18217 
18218       if (shouldBeSelected !== this.isSelected_) {
18219         this.selected(shouldBeSelected);
18220       }
18221     };
18222 
18223     _proto.handleSelectedLanguageChange = function handleSelectedLanguageChange(event) {
18224       var tracks = this.player().textTracks();
18225       var allHidden = true;
18226 
18227       for (var i = 0, l = tracks.length; i < l; i++) {
18228         var track = tracks[i];
18229 
18230         if (['captions', 'descriptions', 'subtitles'].indexOf(track.kind) > -1 && track.mode === 'showing') {
18231           allHidden = false;
18232           break;
18233         }
18234       }
18235 
18236       if (allHidden) {
18237         this.player_.cache_.selectedLanguage = {
18238           enabled: false
18239         };
18240       }
18241     };
18242 
18243     return OffTextTrackMenuItem;
18244   }(TextTrackMenuItem);
18245 
18246   Component.registerComponent('OffTextTrackMenuItem', OffTextTrackMenuItem);
18247 
18248   /**
18249    * The base class for buttons that toggle specific text track types (e.g. subtitles)
18250    *
18251    * @extends MenuButton
18252    */
18253 
18254   var TextTrackButton =
18255   /*#__PURE__*/
18256   function (_TrackButton) {
18257     inheritsLoose(TextTrackButton, _TrackButton);
18258 
18259     /**
18260      * Creates an instance of this class.
18261      *
18262      * @param {Player} player
18263      *        The `Player` that this class should be attached to.
18264      *
18265      * @param {Object} [options={}]
18266      *        The key/value store of player options.
18267      */
18268     function TextTrackButton(player, options) {
18269       if (options === void 0) {
18270         options = {};
18271       }
18272 
18273       options.tracks = player.textTracks();
18274       return _TrackButton.call(this, player, options) || this;
18275     }
18276     /**
18277      * Create a menu item for each text track
18278      *
18279      * @param {TextTrackMenuItem[]} [items=[]]
18280      *        Existing array of items to use during creation
18281      *
18282      * @return {TextTrackMenuItem[]}
18283      *         Array of menu items that were created
18284      */
18285 
18286 
18287     var _proto = TextTrackButton.prototype;
18288 
18289     _proto.createItems = function createItems(items, TrackMenuItem) {
18290       if (items === void 0) {
18291         items = [];
18292       }
18293 
18294       if (TrackMenuItem === void 0) {
18295         TrackMenuItem = TextTrackMenuItem;
18296       }
18297 
18298       // Label is an override for the [track] off label
18299       // USed to localise captions/subtitles
18300       var label;
18301 
18302       if (this.label_) {
18303         label = this.label_ + " off";
18304       } // Add an OFF menu item to turn all tracks off
18305 
18306 
18307       items.push(new OffTextTrackMenuItem(this.player_, {
18308         kinds: this.kinds_,
18309         kind: this.kind_,
18310         label: label
18311       }));
18312       this.hideThreshold_ += 1;
18313       var tracks = this.player_.textTracks();
18314 
18315       if (!Array.isArray(this.kinds_)) {
18316         this.kinds_ = [this.kind_];
18317       }
18318 
18319       for (var i = 0; i < tracks.length; i++) {
18320         var track = tracks[i]; // only add tracks that are of an appropriate kind and have a label
18321 
18322         if (this.kinds_.indexOf(track.kind) > -1) {
18323           var item = new TrackMenuItem(this.player_, {
18324             track: track,
18325             kinds: this.kinds_,
18326             kind: this.kind_,
18327             // MenuItem is selectable
18328             selectable: true,
18329             // MenuItem is NOT multiSelectable (i.e. only one can be marked "selected" at a time)
18330             multiSelectable: false
18331           });
18332           item.addClass("vjs-" + track.kind + "-menu-item");
18333           items.push(item);
18334         }
18335       }
18336 
18337       return items;
18338     };
18339 
18340     return TextTrackButton;
18341   }(TrackButton);
18342 
18343   Component.registerComponent('TextTrackButton', TextTrackButton);
18344 
18345   /**
18346    * The chapter track menu item
18347    *
18348    * @extends MenuItem
18349    */
18350 
18351   var ChaptersTrackMenuItem =
18352   /*#__PURE__*/
18353   function (_MenuItem) {
18354     inheritsLoose(ChaptersTrackMenuItem, _MenuItem);
18355 
18356     /**
18357      * Creates an instance of this class.
18358      *
18359      * @param {Player} player
18360      *        The `Player` that this class should be attached to.
18361      *
18362      * @param {Object} [options]
18363      *        The key/value store of player options.
18364      */
18365     function ChaptersTrackMenuItem(player, options) {
18366       var _this;
18367 
18368       var track = options.track;
18369       var cue = options.cue;
18370       var currentTime = player.currentTime(); // Modify options for parent MenuItem class's init.
18371 
18372       options.selectable = true;
18373       options.multiSelectable = false;
18374       options.label = cue.text;
18375       options.selected = cue.startTime <= currentTime && currentTime < cue.endTime;
18376       _this = _MenuItem.call(this, player, options) || this;
18377       _this.track = track;
18378       _this.cue = cue;
18379       track.addEventListener('cuechange', bind(assertThisInitialized(_this), _this.update));
18380       return _this;
18381     }
18382     /**
18383      * This gets called when an `ChaptersTrackMenuItem` is "clicked". See
18384      * {@link ClickableComponent} for more detailed information on what a click can be.
18385      *
18386      * @param {EventTarget~Event} [event]
18387      *        The `keydown`, `tap`, or `click` event that caused this function to be
18388      *        called.
18389      *
18390      * @listens tap
18391      * @listens click
18392      */
18393 
18394 
18395     var _proto = ChaptersTrackMenuItem.prototype;
18396 
18397     _proto.handleClick = function handleClick(event) {
18398       _MenuItem.prototype.handleClick.call(this);
18399 
18400       this.player_.currentTime(this.cue.startTime);
18401       this.update(this.cue.startTime);
18402     }
18403     /**
18404      * Update chapter menu item
18405      *
18406      * @param {EventTarget~Event} [event]
18407      *        The `cuechange` event that caused this function to run.
18408      *
18409      * @listens TextTrack#cuechange
18410      */
18411     ;
18412 
18413     _proto.update = function update(event) {
18414       var cue = this.cue;
18415       var currentTime = this.player_.currentTime(); // vjs.log(currentTime, cue.startTime);
18416 
18417       this.selected(cue.startTime <= currentTime && currentTime < cue.endTime);
18418     };
18419 
18420     return ChaptersTrackMenuItem;
18421   }(MenuItem);
18422 
18423   Component.registerComponent('ChaptersTrackMenuItem', ChaptersTrackMenuItem);
18424 
18425   /**
18426    * The button component for toggling and selecting chapters
18427    * Chapters act much differently than other text tracks
18428    * Cues are navigation vs. other tracks of alternative languages
18429    *
18430    * @extends TextTrackButton
18431    */
18432 
18433   var ChaptersButton =
18434   /*#__PURE__*/
18435   function (_TextTrackButton) {
18436     inheritsLoose(ChaptersButton, _TextTrackButton);
18437 
18438     /**
18439      * Creates an instance of this class.
18440      *
18441      * @param {Player} player
18442      *        The `Player` that this class should be attached to.
18443      *
18444      * @param {Object} [options]
18445      *        The key/value store of player options.
18446      *
18447      * @param {Component~ReadyCallback} [ready]
18448      *        The function to call when this function is ready.
18449      */
18450     function ChaptersButton(player, options, ready) {
18451       return _TextTrackButton.call(this, player, options, ready) || this;
18452     }
18453     /**
18454      * Builds the default DOM `className`.
18455      *
18456      * @return {string}
18457      *         The DOM `className` for this object.
18458      */
18459 
18460 
18461     var _proto = ChaptersButton.prototype;
18462 
18463     _proto.buildCSSClass = function buildCSSClass() {
18464       return "vjs-chapters-button " + _TextTrackButton.prototype.buildCSSClass.call(this);
18465     };
18466 
18467     _proto.buildWrapperCSSClass = function buildWrapperCSSClass() {
18468       return "vjs-chapters-button " + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
18469     }
18470     /**
18471      * Update the menu based on the current state of its items.
18472      *
18473      * @param {EventTarget~Event} [event]
18474      *        An event that triggered this function to run.
18475      *
18476      * @listens TextTrackList#addtrack
18477      * @listens TextTrackList#removetrack
18478      * @listens TextTrackList#change
18479      */
18480     ;
18481 
18482     _proto.update = function update(event) {
18483       if (!this.track_ || event && (event.type === 'addtrack' || event.type === 'removetrack')) {
vendor: 1,553 bytes, lines 18484-18537
18484         this.setTrack(this.findChaptersTrack());
18485       }
18486 
18487       _TextTrackButton.prototype.update.call(this);
18488     }
18489     /**
18490      * Set the currently selected track for the chapters button.
18491      *
18492      * @param {TextTrack} track
18493      *        The new track to select. Nothing will change if this is the currently selected
18494      *        track.
18495      */
18496     ;
18497 
18498     _proto.setTrack = function setTrack(track) {
18499       if (this.track_ === track) {
18500         return;
18501       }
18502 
18503       if (!this.updateHandler_) {
18504         this.updateHandler_ = this.update.bind(this);
18505       } // here this.track_ refers to the old track instance
18506 
18507 
18508       if (this.track_) {
18509         var remoteTextTrackEl = this.player_.remoteTextTrackEls().getTrackElementByTrack_(this.track_);
18510 
18511         if (remoteTextTrackEl) {
18512           remoteTextTrackEl.removeEventListener('load', this.updateHandler_);
18513         }
18514 
18515         this.track_ = null;
18516       }
18517 
18518       this.track_ = track; // here this.track_ refers to the new track instance
18519 
18520       if (this.track_) {
18521         this.track_.mode = 'hidden';
18522 
18523         var _remoteTextTrackEl = this.player_.remoteTextTrackEls().getTrackElementByTrack_(this.track_);
18524 
18525         if (_remoteTextTrackEl) {
18526           _remoteTextTrackEl.addEventListener('load', this.updateHandler_);
18527         }
18528       }
18529     }
18530     /**
18531      * Find the track object that is currently in use by this ChaptersButton
18532      *
18533      * @return {TextTrack|undefined}
18534      *         The current track or undefined if none was found.
18535      */
18536     ;
18537 
vendor: 16,174 bytes, lines 18538-19115
18538     _proto.findChaptersTrack = function findChaptersTrack() {
18539       var tracks = this.player_.textTracks() || [];
18540 
18541       for (var i = tracks.length - 1; i >= 0; i--) {
18542         // We will always choose the last track as our chaptersTrack
18543         var track = tracks[i];
18544 
18545         if (track.kind === this.kind_) {
18546           return track;
18547         }
18548       }
18549     }
18550     /**
18551      * Get the caption for the ChaptersButton based on the track label. This will also
18552      * use the current tracks localized kind as a fallback if a label does not exist.
18553      *
18554      * @return {string}
18555      *         The tracks current label or the localized track kind.
18556      */
18557     ;
18558 
18559     _proto.getMenuCaption = function getMenuCaption() {
18560       if (this.track_ && this.track_.label) {
18561         return this.track_.label;
18562       }
18563 
18564       return this.localize(toTitleCase(this.kind_));
18565     }
18566     /**
18567      * Create menu from chapter track
18568      *
18569      * @return {Menu}
18570      *         New menu for the chapter buttons
18571      */
18572     ;
18573 
18574     _proto.createMenu = function createMenu() {
18575       this.options_.title = this.getMenuCaption();
18576       return _TextTrackButton.prototype.createMenu.call(this);
18577     }
18578     /**
18579      * Create a menu item for each text track
18580      *
18581      * @return {TextTrackMenuItem[]}
18582      *         Array of menu items
18583      */
18584     ;
18585 
18586     _proto.createItems = function createItems() {
18587       var items = [];
18588 
18589       if (!this.track_) {
18590         return items;
18591       }
18592 
18593       var cues = this.track_.cues;
18594 
18595       if (!cues) {
18596         return items;
18597       }
18598 
18599       for (var i = 0, l = cues.length; i < l; i++) {
18600         var cue = cues[i];
18601         var mi = new ChaptersTrackMenuItem(this.player_, {
18602           track: this.track_,
18603           cue: cue
18604         });
18605         items.push(mi);
18606       }
18607 
18608       return items;
18609     };
18610 
18611     return ChaptersButton;
18612   }(TextTrackButton);
18613   /**
18614    * `kind` of TextTrack to look for to associate it with this menu.
18615    *
18616    * @type {string}
18617    * @private
18618    */
18619 
18620 
18621   ChaptersButton.prototype.kind_ = 'chapters';
18622   /**
18623    * The text that should display over the `ChaptersButton`s controls. Added for localization.
18624    *
18625    * @type {string}
18626    * @private
18627    */
18628 
18629   ChaptersButton.prototype.controlText_ = 'Chapters';
18630   Component.registerComponent('ChaptersButton', ChaptersButton);
18631 
18632   /**
18633    * The button component for toggling and selecting descriptions
18634    *
18635    * @extends TextTrackButton
18636    */
18637 
18638   var DescriptionsButton =
18639   /*#__PURE__*/
18640   function (_TextTrackButton) {
18641     inheritsLoose(DescriptionsButton, _TextTrackButton);
18642 
18643     /**
18644      * Creates an instance of this class.
18645      *
18646      * @param {Player} player
18647      *        The `Player` that this class should be attached to.
18648      *
18649      * @param {Object} [options]
18650      *        The key/value store of player options.
18651      *
18652      * @param {Component~ReadyCallback} [ready]
18653      *        The function to call when this component is ready.
18654      */
18655     function DescriptionsButton(player, options, ready) {
18656       var _this;
18657 
18658       _this = _TextTrackButton.call(this, player, options, ready) || this;
18659       var tracks = player.textTracks();
18660       var changeHandler = bind(assertThisInitialized(_this), _this.handleTracksChange);
18661       tracks.addEventListener('change', changeHandler);
18662 
18663       _this.on('dispose', function () {
18664         tracks.removeEventListener('change', changeHandler);
18665       });
18666 
18667       return _this;
18668     }
18669     /**
18670      * Handle text track change
18671      *
18672      * @param {EventTarget~Event} event
18673      *        The event that caused this function to run
18674      *
18675      * @listens TextTrackList#change
18676      */
18677 
18678 
18679     var _proto = DescriptionsButton.prototype;
18680 
18681     _proto.handleTracksChange = function handleTracksChange(event) {
18682       var tracks = this.player().textTracks();
18683       var disabled = false; // Check whether a track of a different kind is showing
18684 
18685       for (var i = 0, l = tracks.length; i < l; i++) {
18686         var track = tracks[i];
18687 
18688         if (track.kind !== this.kind_ && track.mode === 'showing') {
18689           disabled = true;
18690           break;
18691         }
18692       } // If another track is showing, disable this menu button
18693 
18694 
18695       if (disabled) {
18696         this.disable();
18697       } else {
18698         this.enable();
18699       }
18700     }
18701     /**
18702      * Builds the default DOM `className`.
18703      *
18704      * @return {string}
18705      *         The DOM `className` for this object.
18706      */
18707     ;
18708 
18709     _proto.buildCSSClass = function buildCSSClass() {
18710       return "vjs-descriptions-button " + _TextTrackButton.prototype.buildCSSClass.call(this);
18711     };
18712 
18713     _proto.buildWrapperCSSClass = function buildWrapperCSSClass() {
18714       return "vjs-descriptions-button " + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
18715     };
18716 
18717     return DescriptionsButton;
18718   }(TextTrackButton);
18719   /**
18720    * `kind` of TextTrack to look for to associate it with this menu.
18721    *
18722    * @type {string}
18723    * @private
18724    */
18725 
18726 
18727   DescriptionsButton.prototype.kind_ = 'descriptions';
18728   /**
18729    * The text that should display over the `DescriptionsButton`s controls. Added for localization.
18730    *
18731    * @type {string}
18732    * @private
18733    */
18734 
18735   DescriptionsButton.prototype.controlText_ = 'Descriptions';
18736   Component.registerComponent('DescriptionsButton', DescriptionsButton);
18737 
18738   /**
18739    * The button component for toggling and selecting subtitles
18740    *
18741    * @extends TextTrackButton
18742    */
18743 
18744   var SubtitlesButton =
18745   /*#__PURE__*/
18746   function (_TextTrackButton) {
18747     inheritsLoose(SubtitlesButton, _TextTrackButton);
18748 
18749     /**
18750      * Creates an instance of this class.
18751      *
18752      * @param {Player} player
18753      *        The `Player` that this class should be attached to.
18754      *
18755      * @param {Object} [options]
18756      *        The key/value store of player options.
18757      *
18758      * @param {Component~ReadyCallback} [ready]
18759      *        The function to call when this component is ready.
18760      */
18761     function SubtitlesButton(player, options, ready) {
18762       return _TextTrackButton.call(this, player, options, ready) || this;
18763     }
18764     /**
18765      * Builds the default DOM `className`.
18766      *
18767      * @return {string}
18768      *         The DOM `className` for this object.
18769      */
18770 
18771 
18772     var _proto = SubtitlesButton.prototype;
18773 
18774     _proto.buildCSSClass = function buildCSSClass() {
18775       return "vjs-subtitles-button " + _TextTrackButton.prototype.buildCSSClass.call(this);
18776     };
18777 
18778     _proto.buildWrapperCSSClass = function buildWrapperCSSClass() {
18779       return "vjs-subtitles-button " + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
18780     };
18781 
18782     return SubtitlesButton;
18783   }(TextTrackButton);
18784   /**
18785    * `kind` of TextTrack to look for to associate it with this menu.
18786    *
18787    * @type {string}
18788    * @private
18789    */
18790 
18791 
18792   SubtitlesButton.prototype.kind_ = 'subtitles';
18793   /**
18794    * The text that should display over the `SubtitlesButton`s controls. Added for localization.
18795    *
18796    * @type {string}
18797    * @private
18798    */
18799 
18800   SubtitlesButton.prototype.controlText_ = 'Subtitles';
18801   Component.registerComponent('SubtitlesButton', SubtitlesButton);
18802 
18803   /**
18804    * The menu item for caption track settings menu
18805    *
18806    * @extends TextTrackMenuItem
18807    */
18808 
18809   var CaptionSettingsMenuItem =
18810   /*#__PURE__*/
18811   function (_TextTrackMenuItem) {
18812     inheritsLoose(CaptionSettingsMenuItem, _TextTrackMenuItem);
18813 
18814     /**
18815      * Creates an instance of this class.
18816      *
18817      * @param {Player} player
18818      *        The `Player` that this class should be attached to.
18819      *
18820      * @param {Object} [options]
18821      *        The key/value store of player options.
18822      */
18823     function CaptionSettingsMenuItem(player, options) {
18824       var _this;
18825 
18826       options.track = {
18827         player: player,
18828         kind: options.kind,
18829         label: options.kind + ' settings',
18830         selectable: false,
18831         "default": false,
18832         mode: 'disabled'
18833       }; // CaptionSettingsMenuItem has no concept of 'selected'
18834 
18835       options.selectable = false;
18836       options.name = 'CaptionSettingsMenuItem';
18837       _this = _TextTrackMenuItem.call(this, player, options) || this;
18838 
18839       _this.addClass('vjs-texttrack-settings');
18840 
18841       _this.controlText(', opens ' + options.kind + ' settings dialog');
18842 
18843       return _this;
18844     }
18845     /**
18846      * This gets called when an `CaptionSettingsMenuItem` is "clicked". See
18847      * {@link ClickableComponent} for more detailed information on what a click can be.
18848      *
18849      * @param {EventTarget~Event} [event]
18850      *        The `keydown`, `tap`, or `click` event that caused this function to be
18851      *        called.
18852      *
18853      * @listens tap
18854      * @listens click
18855      */
18856 
18857 
18858     var _proto = CaptionSettingsMenuItem.prototype;
18859 
18860     _proto.handleClick = function handleClick(event) {
18861       this.player().getChild('textTrackSettings').open();
18862     };
18863 
18864     return CaptionSettingsMenuItem;
18865   }(TextTrackMenuItem);
18866 
18867   Component.registerComponent('CaptionSettingsMenuItem', CaptionSettingsMenuItem);
18868 
18869   /**
18870    * The button component for toggling and selecting captions
18871    *
18872    * @extends TextTrackButton
18873    */
18874 
18875   var CaptionsButton =
18876   /*#__PURE__*/
18877   function (_TextTrackButton) {
18878     inheritsLoose(CaptionsButton, _TextTrackButton);
18879 
18880     /**
18881      * Creates an instance of this class.
18882      *
18883      * @param {Player} player
18884      *        The `Player` that this class should be attached to.
18885      *
18886      * @param {Object} [options]
18887      *        The key/value store of player options.
18888      *
18889      * @param {Component~ReadyCallback} [ready]
18890      *        The function to call when this component is ready.
18891      */
18892     function CaptionsButton(player, options, ready) {
18893       return _TextTrackButton.call(this, player, options, ready) || this;
18894     }
18895     /**
18896      * Builds the default DOM `className`.
18897      *
18898      * @return {string}
18899      *         The DOM `className` for this object.
18900      */
18901 
18902 
18903     var _proto = CaptionsButton.prototype;
18904 
18905     _proto.buildCSSClass = function buildCSSClass() {
18906       return "vjs-captions-button " + _TextTrackButton.prototype.buildCSSClass.call(this);
18907     };
18908 
18909     _proto.buildWrapperCSSClass = function buildWrapperCSSClass() {
18910       return "vjs-captions-button " + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
18911     }
18912     /**
18913      * Create caption menu items
18914      *
18915      * @return {CaptionSettingsMenuItem[]}
18916      *         The array of current menu items.
18917      */
18918     ;
18919 
18920     _proto.createItems = function createItems() {
18921       var items = [];
18922 
18923       if (!(this.player().tech_ && this.player().tech_.featuresNativeTextTracks) && this.player().getChild('textTrackSettings')) {
18924         items.push(new CaptionSettingsMenuItem(this.player_, {
18925           kind: this.kind_
18926         }));
18927         this.hideThreshold_ += 1;
18928       }
18929 
18930       return _TextTrackButton.prototype.createItems.call(this, items);
18931     };
18932 
18933     return CaptionsButton;
18934   }(TextTrackButton);
18935   /**
18936    * `kind` of TextTrack to look for to associate it with this menu.
18937    *
18938    * @type {string}
18939    * @private
18940    */
18941 
18942 
18943   CaptionsButton.prototype.kind_ = 'captions';
18944   /**
18945    * The text that should display over the `CaptionsButton`s controls. Added for localization.
18946    *
18947    * @type {string}
18948    * @private
18949    */
18950 
18951   CaptionsButton.prototype.controlText_ = 'Captions';
18952   Component.registerComponent('CaptionsButton', CaptionsButton);
18953 
18954   /**
18955    * SubsCapsMenuItem has an [cc] icon to distinguish captions from subtitles
18956    * in the SubsCapsMenu.
18957    *
18958    * @extends TextTrackMenuItem
18959    */
18960 
18961   var SubsCapsMenuItem =
18962   /*#__PURE__*/
18963   function (_TextTrackMenuItem) {
18964     inheritsLoose(SubsCapsMenuItem, _TextTrackMenuItem);
18965 
18966     function SubsCapsMenuItem() {
18967       return _TextTrackMenuItem.apply(this, arguments) || this;
18968     }
18969 
18970     var _proto = SubsCapsMenuItem.prototype;
18971 
18972     _proto.createEl = function createEl(type, props, attrs) {
18973       var innerHTML = "<span class=\"vjs-menu-item-text\">" + this.localize(this.options_.label);
18974 
18975       if (this.options_.track.kind === 'captions') {
18976         innerHTML += "\n        <span aria-hidden=\"true\" class=\"vjs-icon-placeholder\"></span>\n        <span class=\"vjs-control-text\"> " + this.localize('Captions') + "</span>\n      ";
18977       }
18978 
18979       innerHTML += '</span>';
18980 
18981       var el = _TextTrackMenuItem.prototype.createEl.call(this, type, assign({
18982         innerHTML: innerHTML
18983       }, props), attrs);
18984 
18985       return el;
18986     };
18987 
18988     return SubsCapsMenuItem;
18989   }(TextTrackMenuItem);
18990 
18991   Component.registerComponent('SubsCapsMenuItem', SubsCapsMenuItem);
18992 
18993   /**
18994    * The button component for toggling and selecting captions and/or subtitles
18995    *
18996    * @extends TextTrackButton
18997    */
18998 
18999   var SubsCapsButton =
19000   /*#__PURE__*/
19001   function (_TextTrackButton) {
19002     inheritsLoose(SubsCapsButton, _TextTrackButton);
19003 
19004     function SubsCapsButton(player, options) {
19005       var _this;
19006 
19007       if (options === void 0) {
19008         options = {};
19009       }
19010 
19011       _this = _TextTrackButton.call(this, player, options) || this; // Although North America uses "captions" in most cases for
19012       // "captions and subtitles" other locales use "subtitles"
19013 
19014       _this.label_ = 'subtitles';
19015 
19016       if (['en', 'en-us', 'en-ca', 'fr-ca'].indexOf(_this.player_.language_) > -1) {
19017         _this.label_ = 'captions';
19018       }
19019 
19020       _this.menuButton_.controlText(toTitleCase(_this.label_));
19021 
19022       return _this;
19023     }
19024     /**
19025      * Builds the default DOM `className`.
19026      *
19027      * @return {string}
19028      *         The DOM `className` for this object.
19029      */
19030 
19031 
19032     var _proto = SubsCapsButton.prototype;
19033 
19034     _proto.buildCSSClass = function buildCSSClass() {
19035       return "vjs-subs-caps-button " + _TextTrackButton.prototype.buildCSSClass.call(this);
19036     };
19037 
19038     _proto.buildWrapperCSSClass = function buildWrapperCSSClass() {
19039       return "vjs-subs-caps-button " + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
19040     }
19041     /**
19042      * Create caption/subtitles menu items
19043      *
19044      * @return {CaptionSettingsMenuItem[]}
19045      *         The array of current menu items.
19046      */
19047     ;
19048 
19049     _proto.createItems = function createItems() {
19050       var items = [];
19051 
19052       if (!(this.player().tech_ && this.player().tech_.featuresNativeTextTracks) && this.player().getChild('textTrackSettings')) {
19053         items.push(new CaptionSettingsMenuItem(this.player_, {
19054           kind: this.label_
19055         }));
19056         this.hideThreshold_ += 1;
19057       }
19058 
19059       items = _TextTrackButton.prototype.createItems.call(this, items, SubsCapsMenuItem);
19060       return items;
19061     };
19062 
19063     return SubsCapsButton;
19064   }(TextTrackButton);
19065   /**
19066    * `kind`s of TextTrack to look for to associate it with this menu.
19067    *
19068    * @type {array}
19069    * @private
19070    */
19071 
19072 
19073   SubsCapsButton.prototype.kinds_ = ['captions', 'subtitles'];
19074   /**
19075    * The text that should display over the `SubsCapsButton`s controls.
19076    *
19077    *
19078    * @type {string}
19079    * @private
19080    */
19081 
19082   SubsCapsButton.prototype.controlText_ = 'Subtitles';
19083   Component.registerComponent('SubsCapsButton', SubsCapsButton);
19084 
19085   /**
19086    * An {@link AudioTrack} {@link MenuItem}
19087    *
19088    * @extends MenuItem
19089    */
19090 
19091   var AudioTrackMenuItem =
19092   /*#__PURE__*/
19093   function (_MenuItem) {
19094     inheritsLoose(AudioTrackMenuItem, _MenuItem);
19095 
19096     /**
19097      * Creates an instance of this class.
19098      *
19099      * @param {Player} player
19100      *        The `Player` that this class should be attached to.
19101      *
19102      * @param {Object} [options]
19103      *        The key/value store of player options.
19104      */
19105     function AudioTrackMenuItem(player, options) {
19106       var _this;
19107 
19108       var track = options.track;
19109       var tracks = player.audioTracks(); // Modify options for parent MenuItem class's init.
19110 
19111       options.label = track.label || track.language || 'Unknown';
19112       options.selected = track.enabled;
19113       _this = _MenuItem.call(this, player, options) || this;
19114       _this.track = track;
19115 
vendor: 5,857 bytes, lines 19116-19315
19116       _this.addClass("vjs-" + track.kind + "-menu-item");
19117 
19118       var changeHandler = function changeHandler() {
19119         for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
19120           args[_key] = arguments[_key];
19121         }
19122 
19123         _this.handleTracksChange.apply(assertThisInitialized(_this), args);
19124       };
19125 
19126       tracks.addEventListener('change', changeHandler);
19127 
19128       _this.on('dispose', function () {
19129         tracks.removeEventListener('change', changeHandler);
19130       });
19131 
19132       return _this;
19133     }
19134 
19135     var _proto = AudioTrackMenuItem.prototype;
19136 
19137     _proto.createEl = function createEl(type, props, attrs) {
19138       var innerHTML = "<span class=\"vjs-menu-item-text\">" + this.localize(this.options_.label);
19139 
19140       if (this.options_.track.kind === 'main-desc') {
19141         innerHTML += "\n        <span aria-hidden=\"true\" class=\"vjs-icon-placeholder\"></span>\n        <span class=\"vjs-control-text\"> " + this.localize('Descriptions') + "</span>\n      ";
19142       }
19143 
19144       innerHTML += '</span>';
19145 
19146       var el = _MenuItem.prototype.createEl.call(this, type, assign({
19147         innerHTML: innerHTML
19148       }, props), attrs);
19149 
19150       return el;
19151     }
19152     /**
19153      * This gets called when an `AudioTrackMenuItem is "clicked". See {@link ClickableComponent}
19154      * for more detailed information on what a click can be.
19155      *
19156      * @param {EventTarget~Event} [event]
19157      *        The `keydown`, `tap`, or `click` event that caused this function to be
19158      *        called.
19159      *
19160      * @listens tap
19161      * @listens click
19162      */
19163     ;
19164 
19165     _proto.handleClick = function handleClick(event) {
19166       var tracks = this.player_.audioTracks();
19167 
19168       _MenuItem.prototype.handleClick.call(this, event);
19169 
19170       for (var i = 0; i < tracks.length; i++) {
19171         var track = tracks[i];
19172         track.enabled = track === this.track;
19173       }
19174     }
19175     /**
19176      * Handle any {@link AudioTrack} change.
19177      *
19178      * @param {EventTarget~Event} [event]
19179      *        The {@link AudioTrackList#change} event that caused this to run.
19180      *
19181      * @listens AudioTrackList#change
19182      */
19183     ;
19184 
19185     _proto.handleTracksChange = function handleTracksChange(event) {
19186       this.selected(this.track.enabled);
19187     };
19188 
19189     return AudioTrackMenuItem;
19190   }(MenuItem);
19191 
19192   Component.registerComponent('AudioTrackMenuItem', AudioTrackMenuItem);
19193 
19194   /**
19195    * The base class for buttons that toggle specific {@link AudioTrack} types.
19196    *
19197    * @extends TrackButton
19198    */
19199 
19200   var AudioTrackButton =
19201   /*#__PURE__*/
19202   function (_TrackButton) {
19203     inheritsLoose(AudioTrackButton, _TrackButton);
19204 
19205     /**
19206      * Creates an instance of this class.
19207      *
19208      * @param {Player} player
19209      *        The `Player` that this class should be attached to.
19210      *
19211      * @param {Object} [options={}]
19212      *        The key/value store of player options.
19213      */
19214     function AudioTrackButton(player, options) {
19215       if (options === void 0) {
19216         options = {};
19217       }
19218 
19219       options.tracks = player.audioTracks();
19220       return _TrackButton.call(this, player, options) || this;
19221     }
19222     /**
19223      * Builds the default DOM `className`.
19224      *
19225      * @return {string}
19226      *         The DOM `className` for this object.
19227      */
19228 
19229 
19230     var _proto = AudioTrackButton.prototype;
19231 
19232     _proto.buildCSSClass = function buildCSSClass() {
19233       return "vjs-audio-button " + _TrackButton.prototype.buildCSSClass.call(this);
19234     };
19235 
19236     _proto.buildWrapperCSSClass = function buildWrapperCSSClass() {
19237       return "vjs-audio-button " + _TrackButton.prototype.buildWrapperCSSClass.call(this);
19238     }
19239     /**
19240      * Create a menu item for each audio track
19241      *
19242      * @param {AudioTrackMenuItem[]} [items=[]]
19243      *        An array of existing menu items to use.
19244      *
19245      * @return {AudioTrackMenuItem[]}
19246      *         An array of menu items
19247      */
19248     ;
19249 
19250     _proto.createItems = function createItems(items) {
19251       if (items === void 0) {
19252         items = [];
19253       }
19254 
19255       // if there's only one audio track, there no point in showing it
19256       this.hideThreshold_ = 1;
19257       var tracks = this.player_.audioTracks();
19258 
19259       for (var i = 0; i < tracks.length; i++) {
19260         var track = tracks[i];
19261         items.push(new AudioTrackMenuItem(this.player_, {
19262           track: track,
19263           // MenuItem is selectable
19264           selectable: true,
19265           // MenuItem is NOT multiSelectable (i.e. only one can be marked "selected" at a time)
19266           multiSelectable: false
19267         }));
19268       }
19269 
19270       return items;
19271     };
19272 
19273     return AudioTrackButton;
19274   }(TrackButton);
19275   /**
19276    * The text that should display over the `AudioTrackButton`s controls. Added for localization.
19277    *
19278    * @type {string}
19279    * @private
19280    */
19281 
19282 
19283   AudioTrackButton.prototype.controlText_ = 'Audio Track';
19284   Component.registerComponent('AudioTrackButton', AudioTrackButton);
19285 
19286   /**
19287    * The specific menu item type for selecting a playback rate.
19288    *
19289    * @extends MenuItem
19290    */
19291 
19292   var PlaybackRateMenuItem =
19293   /*#__PURE__*/
19294   function (_MenuItem) {
19295     inheritsLoose(PlaybackRateMenuItem, _MenuItem);
19296 
19297     /**
19298      * Creates an instance of this class.
19299      *
19300      * @param {Player} player
19301      *        The `Player` that this class should be attached to.
19302      *
19303      * @param {Object} [options]
19304      *        The key/value store of player options.
19305      */
19306     function PlaybackRateMenuItem(player, options) {
19307       var _this;
19308 
19309       var label = options.rate;
19310       var rate = parseFloat(label, 10); // Modify options for parent MenuItem class's init.
19311 
19312       options.label = label;
19313       options.selected = rate === 1;
19314       options.selectable = true;
19315       options.multiSelectable = false;
vendor: 4,544 bytes, lines 19316-19482
19316       _this = _MenuItem.call(this, player, options) || this;
19317       _this.label = label;
19318       _this.rate = rate;
19319 
19320       _this.on(player, 'ratechange', _this.update);
19321 
19322       return _this;
19323     }
19324     /**
19325      * This gets called when an `PlaybackRateMenuItem` is "clicked". See
19326      * {@link ClickableComponent} for more detailed information on what a click can be.
19327      *
19328      * @param {EventTarget~Event} [event]
19329      *        The `keydown`, `tap`, or `click` event that caused this function to be
19330      *        called.
19331      *
19332      * @listens tap
19333      * @listens click
19334      */
19335 
19336 
19337     var _proto = PlaybackRateMenuItem.prototype;
19338 
19339     _proto.handleClick = function handleClick(event) {
19340       _MenuItem.prototype.handleClick.call(this);
19341 
19342       this.player().playbackRate(this.rate);
19343     }
19344     /**
19345      * Update the PlaybackRateMenuItem when the playbackrate changes.
19346      *
19347      * @param {EventTarget~Event} [event]
19348      *        The `ratechange` event that caused this function to run.
19349      *
19350      * @listens Player#ratechange
19351      */
19352     ;
19353 
19354     _proto.update = function update(event) {
19355       this.selected(this.player().playbackRate() === this.rate);
19356     };
19357 
19358     return PlaybackRateMenuItem;
19359   }(MenuItem);
19360   /**
19361    * The text that should display over the `PlaybackRateMenuItem`s controls. Added for localization.
19362    *
19363    * @type {string}
19364    * @private
19365    */
19366 
19367 
19368   PlaybackRateMenuItem.prototype.contentElType = 'button';
19369   Component.registerComponent('PlaybackRateMenuItem', PlaybackRateMenuItem);
19370 
19371   /**
19372    * The component for controlling the playback rate.
19373    *
19374    * @extends MenuButton
19375    */
19376 
19377   var PlaybackRateMenuButton =
19378   /*#__PURE__*/
19379   function (_MenuButton) {
19380     inheritsLoose(PlaybackRateMenuButton, _MenuButton);
19381 
19382     /**
19383      * Creates an instance of this class.
19384      *
19385      * @param {Player} player
19386      *        The `Player` that this class should be attached to.
19387      *
19388      * @param {Object} [options]
19389      *        The key/value store of player options.
19390      */
19391     function PlaybackRateMenuButton(player, options) {
19392       var _this;
19393 
19394       _this = _MenuButton.call(this, player, options) || this;
19395 
19396       _this.updateVisibility();
19397 
19398       _this.updateLabel();
19399 
19400       _this.on(player, 'loadstart', _this.updateVisibility);
19401 
19402       _this.on(player, 'ratechange', _this.updateLabel);
19403 
19404       return _this;
19405     }
19406     /**
19407      * Create the `Component`'s DOM element
19408      *
19409      * @return {Element}
19410      *         The element that was created.
19411      */
19412 
19413 
19414     var _proto = PlaybackRateMenuButton.prototype;
19415 
19416     _proto.createEl = function createEl$1() {
19417       var el = _MenuButton.prototype.createEl.call(this);
19418 
19419       this.labelEl_ = createEl('div', {
19420         className: 'vjs-playback-rate-value',
19421         innerHTML: '1x'
19422       });
19423       el.appendChild(this.labelEl_);
19424       return el;
19425     };
19426 
19427     _proto.dispose = function dispose() {
19428       this.labelEl_ = null;
19429 
19430       _MenuButton.prototype.dispose.call(this);
19431     }
19432     /**
19433      * Builds the default DOM `className`.
19434      *
19435      * @return {string}
19436      *         The DOM `className` for this object.
19437      */
19438     ;
19439 
19440     _proto.buildCSSClass = function buildCSSClass() {
19441       return "vjs-playback-rate " + _MenuButton.prototype.buildCSSClass.call(this);
19442     };
19443 
19444     _proto.buildWrapperCSSClass = function buildWrapperCSSClass() {
19445       return "vjs-playback-rate " + _MenuButton.prototype.buildWrapperCSSClass.call(this);
19446     }
19447     /**
19448      * Create the playback rate menu
19449      *
19450      * @return {Menu}
19451      *         Menu object populated with {@link PlaybackRateMenuItem}s
19452      */
19453     ;
19454 
19455     _proto.createMenu = function createMenu() {
19456       var menu = new Menu(this.player());
19457       var rates = this.playbackRates();
19458 
19459       if (rates) {
19460         for (var i = rates.length - 1; i >= 0; i--) {
19461           menu.addChild(new PlaybackRateMenuItem(this.player(), {
19462             rate: rates[i] + 'x'
19463           }));
19464         }
19465       }
19466 
19467       return menu;
19468     }
19469     /**
19470      * Updates ARIA accessibility attributes
19471      */
19472     ;
19473 
19474     _proto.updateARIAAttributes = function updateARIAAttributes() {
19475       // Current playback rate
19476       this.el().setAttribute('aria-valuenow', this.player().playbackRate());
19477     }
19478     /**
19479      * This gets called when an `PlaybackRateMenuButton` is "clicked". See
19480      * {@link ClickableComponent} for more detailed information on what a click can be.
19481      *
19482      * @param {EventTarget~Event} [event]
vendor: 5,076 bytes, lines 19483-19669
19483      *        The `keydown`, `tap`, or `click` event that caused this function to be
19484      *        called.
19485      *
19486      * @listens tap
19487      * @listens click
19488      */
19489     ;
19490 
19491     _proto.handleClick = function handleClick(event) {
19492       // select next rate option
19493       var currentRate = this.player().playbackRate();
19494       var rates = this.playbackRates(); // this will select first one if the last one currently selected
19495 
19496       var newRate = rates[0];
19497 
19498       for (var i = 0; i < rates.length; i++) {
19499         if (rates[i] > currentRate) {
19500           newRate = rates[i];
19501           break;
19502         }
19503       }
19504 
19505       this.player().playbackRate(newRate);
19506     }
19507     /**
19508      * Get possible playback rates
19509      *
19510      * @return {Array}
19511      *         All possible playback rates
19512      */
19513     ;
19514 
19515     _proto.playbackRates = function playbackRates() {
19516       return this.options_.playbackRates || this.options_.playerOptions && this.options_.playerOptions.playbackRates;
19517     }
19518     /**
19519      * Get whether playback rates is supported by the tech
19520      * and an array of playback rates exists
19521      *
19522      * @return {boolean}
19523      *         Whether changing playback rate is supported
19524      */
19525     ;
19526 
19527     _proto.playbackRateSupported = function playbackRateSupported() {
19528       return this.player().tech_ && this.player().tech_.featuresPlaybackRate && this.playbackRates() && this.playbackRates().length > 0;
19529     }
19530     /**
19531      * Hide playback rate controls when they're no playback rate options to select
19532      *
19533      * @param {EventTarget~Event} [event]
19534      *        The event that caused this function to run.
19535      *
19536      * @listens Player#loadstart
19537      */
19538     ;
19539 
19540     _proto.updateVisibility = function updateVisibility(event) {
19541       if (this.playbackRateSupported()) {
19542         this.removeClass('vjs-hidden');
19543       } else {
19544         this.addClass('vjs-hidden');
19545       }
19546     }
19547     /**
19548      * Update button label when rate changed
19549      *
19550      * @param {EventTarget~Event} [event]
19551      *        The event that caused this function to run.
19552      *
19553      * @listens Player#ratechange
19554      */
19555     ;
19556 
19557     _proto.updateLabel = function updateLabel(event) {
19558       if (this.playbackRateSupported()) {
19559         this.labelEl_.innerHTML = this.player().playbackRate() + 'x';
19560       }
19561     };
19562 
19563     return PlaybackRateMenuButton;
19564   }(MenuButton);
19565   /**
19566    * The text that should display over the `FullscreenToggle`s controls. Added for localization.
19567    *
19568    * @type {string}
19569    * @private
19570    */
19571 
19572 
19573   PlaybackRateMenuButton.prototype.controlText_ = 'Playback Rate';
19574   Component.registerComponent('PlaybackRateMenuButton', PlaybackRateMenuButton);
19575 
19576   /**
19577    * Just an empty spacer element that can be used as an append point for plugins, etc.
19578    * Also can be used to create space between elements when necessary.
19579    *
19580    * @extends Component
19581    */
19582 
19583   var Spacer =
19584   /*#__PURE__*/
19585   function (_Component) {
19586     inheritsLoose(Spacer, _Component);
19587 
19588     function Spacer() {
19589       return _Component.apply(this, arguments) || this;
19590     }
19591 
19592     var _proto = Spacer.prototype;
19593 
19594     /**
19595     * Builds the default DOM `className`.
19596     *
19597     * @return {string}
19598     *         The DOM `className` for this object.
19599     */
19600     _proto.buildCSSClass = function buildCSSClass() {
19601       return "vjs-spacer " + _Component.prototype.buildCSSClass.call(this);
19602     }
19603     /**
19604      * Create the `Component`'s DOM element
19605      *
19606      * @return {Element}
19607      *         The element that was created.
19608      */
19609     ;
19610 
19611     _proto.createEl = function createEl() {
19612       return _Component.prototype.createEl.call(this, 'div', {
19613         className: this.buildCSSClass()
19614       });
19615     };
19616 
19617     return Spacer;
19618   }(Component);
19619 
19620   Component.registerComponent('Spacer', Spacer);
19621 
19622   /**
19623    * Spacer specifically meant to be used as an insertion point for new plugins, etc.
19624    *
19625    * @extends Spacer
19626    */
19627 
19628   var CustomControlSpacer =
19629   /*#__PURE__*/
19630   function (_Spacer) {
19631     inheritsLoose(CustomControlSpacer, _Spacer);
19632 
19633     function CustomControlSpacer() {
19634       return _Spacer.apply(this, arguments) || this;
19635     }
19636 
19637     var _proto = CustomControlSpacer.prototype;
19638 
19639     /**
19640      * Builds the default DOM `className`.
19641      *
19642      * @return {string}
19643      *         The DOM `className` for this object.
19644      */
19645     _proto.buildCSSClass = function buildCSSClass() {
19646       return "vjs-custom-control-spacer " + _Spacer.prototype.buildCSSClass.call(this);
19647     }
19648     /**
19649      * Create the `Component`'s DOM element
19650      *
19651      * @return {Element}
19652      *         The element that was created.
19653      */
19654     ;
19655 
19656     _proto.createEl = function createEl() {
19657       var el = _Spacer.prototype.createEl.call(this, {
19658         className: this.buildCSSClass()
19659       }); // No-flex/table-cell mode requires there be some content
19660       // in the cell to fill the remaining space of the table.
19661 
19662 
19663       el.innerHTML = "\xA0";
19664       return el;
19665     };
19666 
19667     return CustomControlSpacer;
19668   }(Spacer);
19669 
vendor: 24,726 bytes, lines 19670-20450
19670   Component.registerComponent('CustomControlSpacer', CustomControlSpacer);
19671 
19672   /**
19673    * Container of main controls.
19674    *
19675    * @extends Component
19676    */
19677 
19678   var ControlBar =
19679   /*#__PURE__*/
19680   function (_Component) {
19681     inheritsLoose(ControlBar, _Component);
19682 
19683     function ControlBar() {
19684       return _Component.apply(this, arguments) || this;
19685     }
19686 
19687     var _proto = ControlBar.prototype;
19688 
19689     /**
19690      * Create the `Component`'s DOM element
19691      *
19692      * @return {Element}
19693      *         The element that was created.
19694      */
19695     _proto.createEl = function createEl() {
19696       return _Component.prototype.createEl.call(this, 'div', {
19697         className: 'vjs-control-bar',
19698         dir: 'ltr'
19699       });
19700     };
19701 
19702     return ControlBar;
19703   }(Component);
19704   /**
19705    * Default options for `ControlBar`
19706    *
19707    * @type {Object}
19708    * @private
19709    */
19710 
19711 
19712   ControlBar.prototype.options_ = {
19713     children: ['playToggle', 'volumePanel', 'currentTimeDisplay', 'timeDivider', 'durationDisplay', 'progressControl', 'liveDisplay', 'seekToLive', 'remainingTimeDisplay', 'customControlSpacer', 'playbackRateMenuButton', 'chaptersButton', 'descriptionsButton', 'subsCapsButton', 'audioTrackButton', 'fullscreenToggle']
19714   };
19715 
19716   if ('exitPictureInPicture' in document) {
19717     ControlBar.prototype.options_.children.splice(ControlBar.prototype.options_.children.length - 1, 0, 'pictureInPictureToggle');
19718   }
19719 
19720   Component.registerComponent('ControlBar', ControlBar);
19721 
19722   /**
19723    * A display that indicates an error has occurred. This means that the video
19724    * is unplayable.
19725    *
19726    * @extends ModalDialog
19727    */
19728 
19729   var ErrorDisplay =
19730   /*#__PURE__*/
19731   function (_ModalDialog) {
19732     inheritsLoose(ErrorDisplay, _ModalDialog);
19733 
19734     /**
19735      * Creates an instance of this class.
19736      *
19737      * @param  {Player} player
19738      *         The `Player` that this class should be attached to.
19739      *
19740      * @param  {Object} [options]
19741      *         The key/value store of player options.
19742      */
19743     function ErrorDisplay(player, options) {
19744       var _this;
19745 
19746       _this = _ModalDialog.call(this, player, options) || this;
19747 
19748       _this.on(player, 'error', _this.open);
19749 
19750       return _this;
19751     }
19752     /**
19753      * Builds the default DOM `className`.
19754      *
19755      * @return {string}
19756      *         The DOM `className` for this object.
19757      *
19758      * @deprecated Since version 5.
19759      */
19760 
19761 
19762     var _proto = ErrorDisplay.prototype;
19763 
19764     _proto.buildCSSClass = function buildCSSClass() {
19765       return "vjs-error-display " + _ModalDialog.prototype.buildCSSClass.call(this);
19766     }
19767     /**
19768      * Gets the localized error message based on the `Player`s error.
19769      *
19770      * @return {string}
19771      *         The `Player`s error message localized or an empty string.
19772      */
19773     ;
19774 
19775     _proto.content = function content() {
19776       var error = this.player().error();
19777       return error ? this.localize(error.message) : '';
19778     };
19779 
19780     return ErrorDisplay;
19781   }(ModalDialog);
19782   /**
19783    * The default options for an `ErrorDisplay`.
19784    *
19785    * @private
19786    */
19787 
19788 
19789   ErrorDisplay.prototype.options_ = _extends_1({}, ModalDialog.prototype.options_, {
19790     pauseOnOpen: false,
19791     fillAlways: true,
19792     temporary: false,
19793     uncloseable: true
19794   });
19795   Component.registerComponent('ErrorDisplay', ErrorDisplay);
19796 
19797   var LOCAL_STORAGE_KEY = 'vjs-text-track-settings';
19798   var COLOR_BLACK = ['#000', 'Black'];
19799   var COLOR_BLUE = ['#00F', 'Blue'];
19800   var COLOR_CYAN = ['#0FF', 'Cyan'];
19801   var COLOR_GREEN = ['#0F0', 'Green'];
19802   var COLOR_MAGENTA = ['#F0F', 'Magenta'];
19803   var COLOR_RED = ['#F00', 'Red'];
19804   var COLOR_WHITE = ['#FFF', 'White'];
19805   var COLOR_YELLOW = ['#FF0', 'Yellow'];
19806   var OPACITY_OPAQUE = ['1', 'Opaque'];
19807   var OPACITY_SEMI = ['0.5', 'Semi-Transparent'];
19808   var OPACITY_TRANS = ['0', 'Transparent']; // Configuration for the various <select> elements in the DOM of this component.
19809   //
19810   // Possible keys include:
19811   //
19812   // `default`:
19813   //   The default option index. Only needs to be provided if not zero.
19814   // `parser`:
19815   //   A function which is used to parse the value from the selected option in
19816   //   a customized way.
19817   // `selector`:
19818   //   The selector used to find the associated <select> element.
19819 
19820   var selectConfigs = {
19821     backgroundColor: {
19822       selector: '.vjs-bg-color > select',
19823       id: 'captions-background-color-%s',
19824       label: 'Color',
19825       options: [COLOR_BLACK, COLOR_WHITE, COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_YELLOW, COLOR_MAGENTA, COLOR_CYAN]
19826     },
19827     backgroundOpacity: {
19828       selector: '.vjs-bg-opacity > select',
19829       id: 'captions-background-opacity-%s',
19830       label: 'Transparency',
19831       options: [OPACITY_OPAQUE, OPACITY_SEMI, OPACITY_TRANS]
19832     },
19833     color: {
19834       selector: '.vjs-fg-color > select',
19835       id: 'captions-foreground-color-%s',
19836       label: 'Color',
19837       options: [COLOR_WHITE, COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_YELLOW, COLOR_MAGENTA, COLOR_CYAN]
19838     },
19839     edgeStyle: {
19840       selector: '.vjs-edge-style > select',
19841       id: '%s',
19842       label: 'Text Edge Style',
19843       options: [['none', 'None'], ['raised', 'Raised'], ['depressed', 'Depressed'], ['uniform', 'Uniform'], ['dropshadow', 'Dropshadow']]
19844     },
19845     fontFamily: {
19846       selector: '.vjs-font-family > select',
19847       id: 'captions-font-family-%s',
19848       label: 'Font Family',
19849       options: [['proportionalSansSerif', 'Proportional Sans-Serif'], ['monospaceSansSerif', 'Monospace Sans-Serif'], ['proportionalSerif', 'Proportional Serif'], ['monospaceSerif', 'Monospace Serif'], ['casual', 'Casual'], ['script', 'Script'], ['small-caps', 'Small Caps']]
19850     },
19851     fontPercent: {
19852       selector: '.vjs-font-percent > select',
19853       id: 'captions-font-size-%s',
19854       label: 'Font Size',
19855       options: [['0.50', '50%'], ['0.75', '75%'], ['1.00', '100%'], ['1.25', '125%'], ['1.50', '150%'], ['1.75', '175%'], ['2.00', '200%'], ['3.00', '300%'], ['4.00', '400%']],
19856       "default": 2,
19857       parser: function parser(v) {
19858         return v === '1.00' ? null : Number(v);
19859       }
19860     },
19861     textOpacity: {
19862       selector: '.vjs-text-opacity > select',
19863       id: 'captions-foreground-opacity-%s',
19864       label: 'Transparency',
19865       options: [OPACITY_OPAQUE, OPACITY_SEMI]
19866     },
19867     // Options for this object are defined below.
19868     windowColor: {
19869       selector: '.vjs-window-color > select',
19870       id: 'captions-window-color-%s',
19871       label: 'Color'
19872     },
19873     // Options for this object are defined below.
19874     windowOpacity: {
19875       selector: '.vjs-window-opacity > select',
19876       id: 'captions-window-opacity-%s',
19877       label: 'Transparency',
19878       options: [OPACITY_TRANS, OPACITY_SEMI, OPACITY_OPAQUE]
19879     }
19880   };
19881   selectConfigs.windowColor.options = selectConfigs.backgroundColor.options;
19882   /**
19883    * Get the actual value of an option.
19884    *
19885    * @param  {string} value
19886    *         The value to get
19887    *
19888    * @param  {Function} [parser]
19889    *         Optional function to adjust the value.
19890    *
19891    * @return {Mixed}
19892    *         - Will be `undefined` if no value exists
19893    *         - Will be `undefined` if the given value is "none".
19894    *         - Will be the actual value otherwise.
19895    *
19896    * @private
19897    */
19898 
19899   function parseOptionValue(value, parser) {
19900     if (parser) {
19901       value = parser(value);
19902     }
19903 
19904     if (value && value !== 'none') {
19905       return value;
19906     }
19907   }
19908   /**
19909    * Gets the value of the selected <option> element within a <select> element.
19910    *
19911    * @param  {Element} el
19912    *         the element to look in
19913    *
19914    * @param  {Function} [parser]
19915    *         Optional function to adjust the value.
19916    *
19917    * @return {Mixed}
19918    *         - Will be `undefined` if no value exists
19919    *         - Will be `undefined` if the given value is "none".
19920    *         - Will be the actual value otherwise.
19921    *
19922    * @private
19923    */
19924 
19925 
19926   function getSelectedOptionValue(el, parser) {
19927     var value = el.options[el.options.selectedIndex].value;
19928     return parseOptionValue(value, parser);
19929   }
19930   /**
19931    * Sets the selected <option> element within a <select> element based on a
19932    * given value.
19933    *
19934    * @param {Element} el
19935    *        The element to look in.
19936    *
19937    * @param {string} value
19938    *        the property to look on.
19939    *
19940    * @param {Function} [parser]
19941    *        Optional function to adjust the value before comparing.
19942    *
19943    * @private
19944    */
19945 
19946 
19947   function setSelectedOption(el, value, parser) {
19948     if (!value) {
19949       return;
19950     }
19951 
19952     for (var i = 0; i < el.options.length; i++) {
19953       if (parseOptionValue(el.options[i].value, parser) === value) {
19954         el.selectedIndex = i;
19955         break;
19956       }
19957     }
19958   }
19959   /**
19960    * Manipulate Text Tracks settings.
19961    *
19962    * @extends ModalDialog
19963    */
19964 
19965 
19966   var TextTrackSettings =
19967   /*#__PURE__*/
19968   function (_ModalDialog) {
19969     inheritsLoose(TextTrackSettings, _ModalDialog);
19970 
19971     /**
19972      * Creates an instance of this class.
19973      *
19974      * @param {Player} player
19975      *         The `Player` that this class should be attached to.
19976      *
19977      * @param {Object} [options]
19978      *         The key/value store of player options.
19979      */
19980     function TextTrackSettings(player, options) {
19981       var _this;
19982 
19983       options.temporary = false;
19984       _this = _ModalDialog.call(this, player, options) || this;
19985       _this.updateDisplay = bind(assertThisInitialized(_this), _this.updateDisplay); // fill the modal and pretend we have opened it
19986 
19987       _this.fill();
19988 
19989       _this.hasBeenOpened_ = _this.hasBeenFilled_ = true;
19990       _this.endDialog = createEl('p', {
19991         className: 'vjs-control-text',
19992         textContent: _this.localize('End of dialog window.')
19993       });
19994 
19995       _this.el().appendChild(_this.endDialog);
19996 
19997       _this.setDefaults(); // Grab `persistTextTrackSettings` from the player options if not passed in child options
19998 
19999 
20000       if (options.persistTextTrackSettings === undefined) {
20001         _this.options_.persistTextTrackSettings = _this.options_.playerOptions.persistTextTrackSettings;
20002       }
20003 
20004       _this.on(_this.$('.vjs-done-button'), 'click', function () {
20005         _this.saveSettings();
20006 
20007         _this.close();
20008       });
20009 
20010       _this.on(_this.$('.vjs-default-button'), 'click', function () {
20011         _this.setDefaults();
20012 
20013         _this.updateDisplay();
20014       });
20015 
20016       each(selectConfigs, function (config) {
20017         _this.on(_this.$(config.selector), 'change', _this.updateDisplay);
20018       });
20019 
20020       if (_this.options_.persistTextTrackSettings) {
20021         _this.restoreSettings();
20022       }
20023 
20024       return _this;
20025     }
20026 
20027     var _proto = TextTrackSettings.prototype;
20028 
20029     _proto.dispose = function dispose() {
20030       this.endDialog = null;
20031 
20032       _ModalDialog.prototype.dispose.call(this);
20033     }
20034     /**
20035      * Create a <select> element with configured options.
20036      *
20037      * @param {string} key
20038      *        Configuration key to use during creation.
20039      *
20040      * @return {string}
20041      *         An HTML string.
20042      *
20043      * @private
20044      */
20045     ;
20046 
20047     _proto.createElSelect_ = function createElSelect_(key, legendId, type) {
20048       var _this2 = this;
20049 
20050       if (legendId === void 0) {
20051         legendId = '';
20052       }
20053 
20054       if (type === void 0) {
20055         type = 'label';
20056       }
20057 
20058       var config = selectConfigs[key];
20059       var id = config.id.replace('%s', this.id_);
20060       var selectLabelledbyIds = [legendId, id].join(' ').trim();
20061       return ["<" + type + " id=\"" + id + "\" class=\"" + (type === 'label' ? 'vjs-label' : '') + "\">", this.localize(config.label), "</" + type + ">", "<select aria-labelledby=\"" + selectLabelledbyIds + "\">"].concat(config.options.map(function (o) {
20062         var optionId = id + '-' + o[1].replace(/\W+/g, '');
20063         return ["<option id=\"" + optionId + "\" value=\"" + o[0] + "\" ", "aria-labelledby=\"" + selectLabelledbyIds + " " + optionId + "\">", _this2.localize(o[1]), '</option>'].join('');
20064       })).concat('</select>').join('');
20065     }
20066     /**
20067      * Create foreground color element for the component
20068      *
20069      * @return {string}
20070      *         An HTML string.
20071      *
20072      * @private
20073      */
20074     ;
20075 
20076     _proto.createElFgColor_ = function createElFgColor_() {
20077       var legendId = "captions-text-legend-" + this.id_;
20078       return ['<fieldset class="vjs-fg-color vjs-track-setting">', "<legend id=\"" + legendId + "\">", this.localize('Text'), '</legend>', this.createElSelect_('color', legendId), '<span class="vjs-text-opacity vjs-opacity">', this.createElSelect_('textOpacity', legendId), '</span>', '</fieldset>'].join('');
20079     }
20080     /**
20081      * Create background color element for the component
20082      *
20083      * @return {string}
20084      *         An HTML string.
20085      *
20086      * @private
20087      */
20088     ;
20089 
20090     _proto.createElBgColor_ = function createElBgColor_() {
20091       var legendId = "captions-background-" + this.id_;
20092       return ['<fieldset class="vjs-bg-color vjs-track-setting">', "<legend id=\"" + legendId + "\">", this.localize('Background'), '</legend>', this.createElSelect_('backgroundColor', legendId), '<span class="vjs-bg-opacity vjs-opacity">', this.createElSelect_('backgroundOpacity', legendId), '</span>', '</fieldset>'].join('');
20093     }
20094     /**
20095      * Create window color element for the component
20096      *
20097      * @return {string}
20098      *         An HTML string.
20099      *
20100      * @private
20101      */
20102     ;
20103 
20104     _proto.createElWinColor_ = function createElWinColor_() {
20105       var legendId = "captions-window-" + this.id_;
20106       return ['<fieldset class="vjs-window-color vjs-track-setting">', "<legend id=\"" + legendId + "\">", this.localize('Window'), '</legend>', this.createElSelect_('windowColor', legendId), '<span class="vjs-window-opacity vjs-opacity">', this.createElSelect_('windowOpacity', legendId), '</span>', '</fieldset>'].join('');
20107     }
20108     /**
20109      * Create color elements for the component
20110      *
20111      * @return {Element}
20112      *         The element that was created
20113      *
20114      * @private
20115      */
20116     ;
20117 
20118     _proto.createElColors_ = function createElColors_() {
20119       return createEl('div', {
20120         className: 'vjs-track-settings-colors',
20121         innerHTML: [this.createElFgColor_(), this.createElBgColor_(), this.createElWinColor_()].join('')
20122       });
20123     }
20124     /**
20125      * Create font elements for the component
20126      *
20127      * @return {Element}
20128      *         The element that was created.
20129      *
20130      * @private
20131      */
20132     ;
20133 
20134     _proto.createElFont_ = function createElFont_() {
20135       return createEl('div', {
20136         className: 'vjs-track-settings-font',
20137         innerHTML: ['<fieldset class="vjs-font-percent vjs-track-setting">', this.createElSelect_('fontPercent', '', 'legend'), '</fieldset>', '<fieldset class="vjs-edge-style vjs-track-setting">', this.createElSelect_('edgeStyle', '', 'legend'), '</fieldset>', '<fieldset class="vjs-font-family vjs-track-setting">', this.createElSelect_('fontFamily', '', 'legend'), '</fieldset>'].join('')
20138       });
20139     }
20140     /**
20141      * Create controls for the component
20142      *
20143      * @return {Element}
20144      *         The element that was created.
20145      *
20146      * @private
20147      */
20148     ;
20149 
20150     _proto.createElControls_ = function createElControls_() {
20151       var defaultsDescription = this.localize('restore all settings to the default values');
20152       return createEl('div', {
20153         className: 'vjs-track-settings-controls',
20154         innerHTML: ["<button type=\"button\" class=\"vjs-default-button\" title=\"" + defaultsDescription + "\">", this.localize('Reset'), "<span class=\"vjs-control-text\"> " + defaultsDescription + "</span>", '</button>', "<button type=\"button\" class=\"vjs-done-button\">" + this.localize('Done') + "</button>"].join('')
20155       });
20156     };
20157 
20158     _proto.content = function content() {
20159       return [this.createElColors_(), this.createElFont_(), this.createElControls_()];
20160     };
20161 
20162     _proto.label = function label() {
20163       return this.localize('Caption Settings Dialog');
20164     };
20165 
20166     _proto.description = function description() {
20167       return this.localize('Beginning of dialog window. Escape will cancel and close the window.');
20168     };
20169 
20170     _proto.buildCSSClass = function buildCSSClass() {
20171       return _ModalDialog.prototype.buildCSSClass.call(this) + ' vjs-text-track-settings';
20172     }
20173     /**
20174      * Gets an object of text track settings (or null).
20175      *
20176      * @return {Object}
20177      *         An object with config values parsed from the DOM or localStorage.
20178      */
20179     ;
20180 
20181     _proto.getValues = function getValues() {
20182       var _this3 = this;
20183 
20184       return reduce(selectConfigs, function (accum, config, key) {
20185         var value = getSelectedOptionValue(_this3.$(config.selector), config.parser);
20186 
20187         if (value !== undefined) {
20188           accum[key] = value;
20189         }
20190 
20191         return accum;
20192       }, {});
20193     }
20194     /**
20195      * Sets text track settings from an object of values.
20196      *
20197      * @param {Object} values
20198      *        An object with config values parsed from the DOM or localStorage.
20199      */
20200     ;
20201 
20202     _proto.setValues = function setValues(values) {
20203       var _this4 = this;
20204 
20205       each(selectConfigs, function (config, key) {
20206         setSelectedOption(_this4.$(config.selector), values[key], config.parser);
20207       });
20208     }
20209     /**
20210      * Sets all `<select>` elements to their default values.
20211      */
20212     ;
20213 
20214     _proto.setDefaults = function setDefaults() {
20215       var _this5 = this;
20216 
20217       each(selectConfigs, function (config) {
20218         var index = config.hasOwnProperty('default') ? config["default"] : 0;
20219         _this5.$(config.selector).selectedIndex = index;
20220       });
20221     }
20222     /**
20223      * Restore texttrack settings from localStorage
20224      */
20225     ;
20226 
20227     _proto.restoreSettings = function restoreSettings() {
20228       var values;
20229 
20230       try {
20231         values = JSON.parse(window$1.localStorage.getItem(LOCAL_STORAGE_KEY));
20232       } catch (err) {
20233         log.warn(err);
20234       }
20235 
20236       if (values) {
20237         this.setValues(values);
20238       }
20239     }
20240     /**
20241      * Save text track settings to localStorage
20242      */
20243     ;
20244 
20245     _proto.saveSettings = function saveSettings() {
20246       if (!this.options_.persistTextTrackSettings) {
20247         return;
20248       }
20249 
20250       var values = this.getValues();
20251 
20252       try {
20253         if (Object.keys(values).length) {
20254           window$1.localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(values));
20255         } else {
20256           window$1.localStorage.removeItem(LOCAL_STORAGE_KEY);
20257         }
20258       } catch (err) {
20259         log.warn(err);
20260       }
20261     }
20262     /**
20263      * Update display of text track settings
20264      */
20265     ;
20266 
20267     _proto.updateDisplay = function updateDisplay() {
20268       var ttDisplay = this.player_.getChild('textTrackDisplay');
20269 
20270       if (ttDisplay) {
20271         ttDisplay.updateDisplay();
20272       }
20273     }
20274     /**
20275      * conditionally blur the element and refocus the captions button
20276      *
20277      * @private
20278      */
20279     ;
20280 
20281     _proto.conditionalBlur_ = function conditionalBlur_() {
20282       this.previouslyActiveEl_ = null;
20283       var cb = this.player_.controlBar;
20284       var subsCapsBtn = cb && cb.subsCapsButton;
20285       var ccBtn = cb && cb.captionsButton;
20286 
20287       if (subsCapsBtn) {
20288         subsCapsBtn.focus();
20289       } else if (ccBtn) {
20290         ccBtn.focus();
20291       }
20292     };
20293 
20294     return TextTrackSettings;
20295   }(ModalDialog);
20296 
20297   Component.registerComponent('TextTrackSettings', TextTrackSettings);
20298 
20299   /**
20300    * A Resize Manager. It is in charge of triggering `playerresize` on the player in the right conditions.
20301    *
20302    * It'll either create an iframe and use a debounced resize handler on it or use the new {@link https://wicg.github.io/ResizeObserver/|ResizeObserver}.
20303    *
20304    * If the ResizeObserver is available natively, it will be used. A polyfill can be passed in as an option.
20305    * If a `playerresize` event is not needed, the ResizeManager component can be removed from the player, see the example below.
20306    * @example <caption>How to disable the resize manager</caption>
20307    * const player = videojs('#vid', {
20308    *   resizeManager: false
20309    * });
20310    *
20311    * @see {@link https://wicg.github.io/ResizeObserver/|ResizeObserver specification}
20312    *
20313    * @extends Component
20314    */
20315 
20316   var ResizeManager =
20317   /*#__PURE__*/
20318   function (_Component) {
20319     inheritsLoose(ResizeManager, _Component);
20320 
20321     /**
20322      * Create the ResizeManager.
20323      *
20324      * @param {Object} player
20325      *        The `Player` that this class should be attached to.
20326      *
20327      * @param {Object} [options]
20328      *        The key/value store of ResizeManager options.
20329      *
20330      * @param {Object} [options.ResizeObserver]
20331      *        A polyfill for ResizeObserver can be passed in here.
20332      *        If this is set to null it will ignore the native ResizeObserver and fall back to the iframe fallback.
20333      */
20334     function ResizeManager(player, options) {
20335       var _this;
20336 
20337       var RESIZE_OBSERVER_AVAILABLE = options.ResizeObserver || window$1.ResizeObserver; // if `null` was passed, we want to disable the ResizeObserver
20338 
20339       if (options.ResizeObserver === null) {
20340         RESIZE_OBSERVER_AVAILABLE = false;
20341       } // Only create an element when ResizeObserver isn't available
20342 
20343 
20344       var options_ = mergeOptions({
20345         createEl: !RESIZE_OBSERVER_AVAILABLE,
20346         reportTouchActivity: false
20347       }, options);
20348       _this = _Component.call(this, player, options_) || this;
20349       _this.ResizeObserver = options.ResizeObserver || window$1.ResizeObserver;
20350       _this.loadListener_ = null;
20351       _this.resizeObserver_ = null;
20352       _this.debouncedHandler_ = debounce(function () {
20353         _this.resizeHandler();
20354       }, 100, false, assertThisInitialized(_this));
20355 
20356       if (RESIZE_OBSERVER_AVAILABLE) {
20357         _this.resizeObserver_ = new _this.ResizeObserver(_this.debouncedHandler_);
20358 
20359         _this.resizeObserver_.observe(player.el());
20360       } else {
20361         _this.loadListener_ = function () {
20362           if (!_this.el_ || !_this.el_.contentWindow) {
20363             return;
20364           }
20365 
20366           var debouncedHandler_ = _this.debouncedHandler_;
20367 
20368           var unloadListener_ = _this.unloadListener_ = function () {
20369             off(this, 'resize', debouncedHandler_);
20370             off(this, 'unload', unloadListener_);
20371             unloadListener_ = null;
20372           }; // safari and edge can unload the iframe before resizemanager dispose
20373           // we have to dispose of event handlers correctly before that happens
20374 
20375 
20376           on(_this.el_.contentWindow, 'unload', unloadListener_);
20377           on(_this.el_.contentWindow, 'resize', debouncedHandler_);
20378         };
20379 
20380         _this.one('load', _this.loadListener_);
20381       }
20382 
20383       return _this;
20384     }
20385 
20386     var _proto = ResizeManager.prototype;
20387 
20388     _proto.createEl = function createEl() {
20389       return _Component.prototype.createEl.call(this, 'iframe', {
20390         className: 'vjs-resize-manager',
20391         tabIndex: -1
20392       }, {
20393         'aria-hidden': 'true'
20394       });
20395     }
20396     /**
20397      * Called when a resize is triggered on the iframe or a resize is observed via the ResizeObserver
20398      *
20399      * @fires Player#playerresize
20400      */
20401     ;
20402 
20403     _proto.resizeHandler = function resizeHandler() {
20404       /**
20405        * Called when the player size has changed
20406        *
20407        * @event Player#playerresize
20408        * @type {EventTarget~Event}
20409        */
20410       // make sure player is still around to trigger
20411       // prevents this from causing an error after dispose
20412       if (!this.player_ || !this.player_.trigger) {
20413         return;
20414       }
20415 
20416       this.player_.trigger('playerresize');
20417     };
20418 
20419     _proto.dispose = function dispose() {
20420       if (this.debouncedHandler_) {
20421         this.debouncedHandler_.cancel();
20422       }
20423 
20424       if (this.resizeObserver_) {
20425         if (this.player_.el()) {
20426           this.resizeObserver_.unobserve(this.player_.el());
20427         }
20428 
20429         this.resizeObserver_.disconnect();
20430       }
20431 
20432       if (this.loadListener_) {
20433         this.off('load', this.loadListener_);
20434       }
20435 
20436       if (this.el_ && this.el_.contentWindow && this.unloadListener_) {
20437         this.unloadListener_.call(this.el_.contentWindow);
20438       }
20439 
20440       this.ResizeObserver = null;
20441       this.resizeObserver = null;
20442       this.debouncedHandler_ = null;
20443       this.loadListener_ = null;
20444 
20445       _Component.prototype.dispose.call(this);
20446     };
20447 
20448     return ResizeManager;
20449   }(Component);
20450 
20451   Component.registerComponent('ResizeManager', ResizeManager);
20452 
20453   /**
20454    * Computes the median of an array.
20455    *
20456    * @param {number[]} arr
20457    *        Input array of numbers.
20458    *
20459    * @return {number}
20460    *        Median value.
20461    */
20462   var median = function median(arr) {
20463     var mid = Math.floor(arr.length / 2);
20464     var sortedList = [].concat(arr).sort(function (a, b) {
20465       return a - b;
20466     });
20467     return arr.length % 2 !== 0 ? sortedList[mid] : (sortedList[mid - 1] + sortedList[mid]) / 2;
20468   };
20469 
20470   /* track when we are at the live edge, and other helpers for live playback */
20471 
20472   var LiveTracker =
20473   /*#__PURE__*/
20474   function (_Component) {
20475     inheritsLoose(LiveTracker, _Component);
20476 
20477     function LiveTracker(player, options) {
20478       var _this;
20479 
20480       // LiveTracker does not need an element
20481       var options_ = mergeOptions({
20482         createEl: false
20483       }, options);
20484       _this = _Component.call(this, player, options_) || this;
20485 
20486       _this.reset_();
20487 
20488       _this.on(_this.player_, 'durationchange', _this.handleDurationchange); // we don't need to track live playback if the document is hidden,
20489       // also, tracking when the document is hidden can
20490       // cause the CPU to spike and eventually crash the page on IE11.
20491 
20492 
20493       if (IE_VERSION && 'hidden' in document && 'visibilityState' in document) {
20494         _this.on(document, 'visibilitychange', _this.handleVisibilityChange);
20495       }
20496 
20497       return _this;
20498     }
20499 
20500     var _proto = LiveTracker.prototype;
20501 
20502     _proto.handleVisibilityChange = function handleVisibilityChange() {
20503       if (this.player_.duration() !== Infinity) {
20504         return;
20505       }
20506 
20507       if (document.hidden) {
20508         this.stopTracking();
20509       } else {
20510         this.startTracking();
20511       }
20512     };
20513 
20514     _proto.isBehind_ = function isBehind_() {
20515       // don't report that we are behind until a timeupdate has been seen
20516       if (!this.timeupdateSeen_) {
20517         return false;
20518       }
20519 
20520       var liveCurrentTime = this.liveCurrentTime();
20521       var currentTime = this.player_.currentTime(); // the live edge window is the amount of seconds away from live
20522       // that a player can be, but still be considered live.
20523       // we add 0.07 because the live tracking happens every 30ms
20524       // and we want some wiggle room for short segment live playback
20525 
20526       var liveEdgeWindow = this.seekableIncrement_ * 2 + 0.07; // on Android liveCurrentTime can bee Infinity, because seekableEnd
20527       // can be Infinity, so we handle that case.
20528 
20529       return liveCurrentTime !== Infinity && liveCurrentTime - liveEdgeWindow >= currentTime;
20530     } // all the functionality for tracking when seek end changes
20531     // and for tracking how far past seek end we should be
20532     ;
20533 
20534     _proto.trackLive_ = function trackLive_() {
20535       this.pastSeekEnd_ = this.pastSeekEnd_;
20536       var seekable = this.player_.seekable(); // skip undefined seekable
20537 
20538       if (!seekable || !seekable.length) {
20539         return;
20540       }
20541 
20542       var newSeekEnd = this.seekableEnd(); // we can only tell if we are behind live, when seekable changes
20543       // once we detect that seekable has changed we check the new seek
20544       // end against current time, with a fudge value of half a second.
20545 
20546       if (newSeekEnd !== this.lastSeekEnd_) {
20547         if (this.lastSeekEnd_) {
20548           // we try to get the best fit value for the seeking increment
20549           // variable from the last 12 values.
20550           this.seekableIncrementList_ = this.seekableIncrementList_.slice(-11);
20551           this.seekableIncrementList_.push(Math.abs(newSeekEnd - this.lastSeekEnd_));
20552 
20553           if (this.seekableIncrementList_.length > 3) {
20554             this.seekableIncrement_ = median(this.seekableIncrementList_);
20555           }
20556         }
20557 
20558         this.pastSeekEnd_ = 0;
20559         this.lastSeekEnd_ = newSeekEnd;
20560         this.trigger('seekableendchange');
20561       } // we should reset pastSeekEnd when the value
20562       // is much higher than seeking increment.
20563 
20564 
20565       if (this.pastSeekEnd() > this.seekableIncrement_ * 1.5) {
20566         this.pastSeekEnd_ = 0;
20567       } else {
20568         this.pastSeekEnd_ = this.pastSeekEnd() + 0.03;
20569       }
20570 
20571       if (this.isBehind_() !== this.behindLiveEdge()) {
20572         this.behindLiveEdge_ = this.isBehind_();
20573         this.trigger('liveedgechange');
20574       }
20575     }
20576     /**
20577      * handle a durationchange event on the player
20578      * and start/stop tracking accordingly.
20579      */
20580     ;
20581 
20582     _proto.handleDurationchange = function handleDurationchange() {
20583       if (this.player_.duration() === Infinity) {
20584         this.startTracking();
20585       } else {
20586         this.stopTracking();
20587       }
20588     }
20589     /**
20590      * start tracking live playback
20591      */
20592     ;
20593 
20594     _proto.startTracking = function startTracking() {
20595       var _this2 = this;
20596 
20597       if (this.isTracking()) {
20598         return;
20599       } // If we haven't seen a timeupdate, we need to check whether playback
20600       // began before this component started tracking. This can happen commonly
20601       // when using autoplay.
20602 
20603 
20604       if (!this.timeupdateSeen_) {
20605         this.timeupdateSeen_ = this.player_.hasStarted();
20606       }
20607 
20608       this.trackingInterval_ = this.setInterval(this.trackLive_, 30);
20609       this.trackLive_();
20610       this.on(this.player_, 'play', this.trackLive_);
20611       this.on(this.player_, 'pause', this.trackLive_); // this is to prevent showing that we are not live
20612       // before a video starts to play
20613 
20614       if (!this.timeupdateSeen_) {
20615         this.one(this.player_, 'play', this.handlePlay);
20616 
20617         this.handleTimeupdate = function () {
20618           _this2.timeupdateSeen_ = true;
20619           _this2.handleTimeupdate = null;
20620         };
20621 
20622         this.one(this.player_, 'timeupdate', this.handleTimeupdate);
20623       }
20624     };
20625 
20626     _proto.handlePlay = function handlePlay() {
20627       this.one(this.player_, 'timeupdate', this.seekToLiveEdge);
20628     }
20629     /**
20630      * Stop tracking, and set all internal variables to
20631      * their initial value.
20632      */
20633     ;
20634 
20635     _proto.reset_ = function reset_() {
20636       this.pastSeekEnd_ = 0;
20637       this.lastSeekEnd_ = null;
20638       this.behindLiveEdge_ = null;
20639       this.timeupdateSeen_ = false;
vendor: 28,696 bytes, lines 20640-21515
20640       this.clearInterval(this.trackingInterval_);
20641       this.trackingInterval_ = null;
20642       this.seekableIncrement_ = 12;
20643       this.seekableIncrementList_ = [];
20644       this.off(this.player_, 'play', this.trackLive_);
20645       this.off(this.player_, 'pause', this.trackLive_);
20646       this.off(this.player_, 'play', this.handlePlay);
20647       this.off(this.player_, 'timeupdate', this.seekToLiveEdge);
20648 
20649       if (this.handleTimeupdate) {
20650         this.off(this.player_, 'timeupdate', this.handleTimeupdate);
20651         this.handleTimeupdate = null;
20652       }
20653     }
20654     /**
20655      * stop tracking live playback
20656      */
20657     ;
20658 
20659     _proto.stopTracking = function stopTracking() {
20660       if (!this.isTracking()) {
20661         return;
20662       }
20663 
20664       this.reset_();
20665     }
20666     /**
20667      * A helper to get the player seekable end
20668      * so that we don't have to null check everywhere
20669      */
20670     ;
20671 
20672     _proto.seekableEnd = function seekableEnd() {
20673       var seekable = this.player_.seekable();
20674       var seekableEnds = [];
20675       var i = seekable ? seekable.length : 0;
20676 
20677       while (i--) {
20678         seekableEnds.push(seekable.end(i));
20679       } // grab the furthest seekable end after sorting, or if there are none
20680       // default to Infinity
20681 
20682 
20683       return seekableEnds.length ? seekableEnds.sort()[seekableEnds.length - 1] : Infinity;
20684     }
20685     /**
20686      * A helper to get the player seekable start
20687      * so that we don't have to null check everywhere
20688      */
20689     ;
20690 
20691     _proto.seekableStart = function seekableStart() {
20692       var seekable = this.player_.seekable();
20693       var seekableStarts = [];
20694       var i = seekable ? seekable.length : 0;
20695 
20696       while (i--) {
20697         seekableStarts.push(seekable.start(i));
20698       } // grab the first seekable start after sorting, or if there are none
20699       // default to 0
20700 
20701 
20702       return seekableStarts.length ? seekableStarts.sort()[0] : 0;
20703     }
20704     /**
20705      * Get the live time window
20706      */
20707     ;
20708 
20709     _proto.liveWindow = function liveWindow() {
20710       var liveCurrentTime = this.liveCurrentTime();
20711 
20712       if (liveCurrentTime === Infinity) {
20713         return Infinity;
20714       }
20715 
20716       return liveCurrentTime - this.seekableStart();
20717     }
20718     /**
20719      * Determines if the player is live, only checks if this component
20720      * is tracking live playback or not
20721      */
20722     ;
20723 
20724     _proto.isLive = function isLive() {
20725       return this.isTracking();
20726     }
20727     /**
20728      * Determines if currentTime is at the live edge and won't fall behind
20729      * on each seekableendchange
20730      */
20731     ;
20732 
20733     _proto.atLiveEdge = function atLiveEdge() {
20734       return !this.behindLiveEdge();
20735     }
20736     /**
20737      * get what we expect the live current time to be
20738      */
20739     ;
20740 
20741     _proto.liveCurrentTime = function liveCurrentTime() {
20742       return this.pastSeekEnd() + this.seekableEnd();
20743     }
20744     /**
20745      * Returns how far past seek end we expect current time to be
20746      */
20747     ;
20748 
20749     _proto.pastSeekEnd = function pastSeekEnd() {
20750       return this.pastSeekEnd_;
20751     }
20752     /**
20753      * If we are currently behind the live edge, aka currentTime will be
20754      * behind on a seekableendchange
20755      */
20756     ;
20757 
20758     _proto.behindLiveEdge = function behindLiveEdge() {
20759       return this.behindLiveEdge_;
20760     };
20761 
20762     _proto.isTracking = function isTracking() {
20763       return typeof this.trackingInterval_ === 'number';
20764     }
20765     /**
20766      * Seek to the live edge if we are behind the live edge
20767      */
20768     ;
20769 
20770     _proto.seekToLiveEdge = function seekToLiveEdge() {
20771       if (this.atLiveEdge()) {
20772         return;
20773       }
20774 
20775       this.player_.currentTime(this.liveCurrentTime());
20776 
20777       if (this.player_.paused()) {
20778         this.player_.play();
20779       }
20780     };
20781 
20782     _proto.dispose = function dispose() {
20783       this.stopTracking();
20784 
20785       _Component.prototype.dispose.call(this);
20786     };
20787 
20788     return LiveTracker;
20789   }(Component);
20790 
20791   Component.registerComponent('LiveTracker', LiveTracker);
20792 
20793   /**
20794    * This function is used to fire a sourceset when there is something
20795    * similar to `mediaEl.load()` being called. It will try to find the source via
20796    * the `src` attribute and then the `<source>` elements. It will then fire `sourceset`
20797    * with the source that was found or empty string if we cannot know. If it cannot
20798    * find a source then `sourceset` will not be fired.
20799    *
20800    * @param {Html5} tech
20801    *        The tech object that sourceset was setup on
20802    *
20803    * @return {boolean}
20804    *         returns false if the sourceset was not fired and true otherwise.
20805    */
20806 
20807   var sourcesetLoad = function sourcesetLoad(tech) {
20808     var el = tech.el(); // if `el.src` is set, that source will be loaded.
20809 
20810     if (el.hasAttribute('src')) {
20811       tech.triggerSourceset(el.src);
20812       return true;
20813     }
20814     /**
20815      * Since there isn't a src property on the media element, source elements will be used for
20816      * implementing the source selection algorithm. This happens asynchronously and
20817      * for most cases were there is more than one source we cannot tell what source will
20818      * be loaded, without re-implementing the source selection algorithm. At this time we are not
20819      * going to do that. There are three special cases that we do handle here though:
20820      *
20821      * 1. If there are no sources, do not fire `sourceset`.
20822      * 2. If there is only one `<source>` with a `src` property/attribute that is our `src`
20823      * 3. If there is more than one `<source>` but all of them have the same `src` url.
20824      *    That will be our src.
20825      */
20826 
20827 
20828     var sources = tech.$$('source');
20829     var srcUrls = [];
20830     var src = ''; // if there are no sources, do not fire sourceset
20831 
20832     if (!sources.length) {
20833       return false;
20834     } // only count valid/non-duplicate source elements
20835 
20836 
20837     for (var i = 0; i < sources.length; i++) {
20838       var url = sources[i].src;
20839 
20840       if (url && srcUrls.indexOf(url) === -1) {
20841         srcUrls.push(url);
20842       }
20843     } // there were no valid sources
20844 
20845 
20846     if (!srcUrls.length) {
20847       return false;
20848     } // there is only one valid source element url
20849     // use that
20850 
20851 
20852     if (srcUrls.length === 1) {
20853       src = srcUrls[0];
20854     }
20855 
20856     tech.triggerSourceset(src);
20857     return true;
20858   };
20859   /**
20860    * our implementation of an `innerHTML` descriptor for browsers
20861    * that do not have one.
20862    */
20863 
20864 
20865   var innerHTMLDescriptorPolyfill = Object.defineProperty({}, 'innerHTML', {
20866     get: function get() {
20867       return this.cloneNode(true).innerHTML;
20868     },
20869     set: function set(v) {
20870       // make a dummy node to use innerHTML on
20871       var dummy = document.createElement(this.nodeName.toLowerCase()); // set innerHTML to the value provided
20872 
20873       dummy.innerHTML = v; // make a document fragment to hold the nodes from dummy
20874 
20875       var docFrag = document.createDocumentFragment(); // copy all of the nodes created by the innerHTML on dummy
20876       // to the document fragment
20877 
20878       while (dummy.childNodes.length) {
20879         docFrag.appendChild(dummy.childNodes[0]);
20880       } // remove content
20881 
20882 
20883       this.innerText = ''; // now we add all of that html in one by appending the
20884       // document fragment. This is how innerHTML does it.
20885 
20886       window$1.Element.prototype.appendChild.call(this, docFrag); // then return the result that innerHTML's setter would
20887 
20888       return this.innerHTML;
20889     }
20890   });
20891   /**
20892    * Get a property descriptor given a list of priorities and the
20893    * property to get.
20894    */
20895 
20896   var getDescriptor = function getDescriptor(priority, prop) {
20897     var descriptor = {};
20898 
20899     for (var i = 0; i < priority.length; i++) {
20900       descriptor = Object.getOwnPropertyDescriptor(priority[i], prop);
20901 
20902       if (descriptor && descriptor.set && descriptor.get) {
20903         break;
20904       }
20905     }
20906 
20907     descriptor.enumerable = true;
20908     descriptor.configurable = true;
20909     return descriptor;
20910   };
20911 
20912   var getInnerHTMLDescriptor = function getInnerHTMLDescriptor(tech) {
20913     return getDescriptor([tech.el(), window$1.HTMLMediaElement.prototype, window$1.Element.prototype, innerHTMLDescriptorPolyfill], 'innerHTML');
20914   };
20915   /**
20916    * Patches browser internal functions so that we can tell synchronously
20917    * if a `<source>` was appended to the media element. For some reason this
20918    * causes a `sourceset` if the the media element is ready and has no source.
20919    * This happens when:
20920    * - The page has just loaded and the media element does not have a source.
20921    * - The media element was emptied of all sources, then `load()` was called.
20922    *
20923    * It does this by patching the following functions/properties when they are supported:
20924    *
20925    * - `append()` - can be used to add a `<source>` element to the media element
20926    * - `appendChild()` - can be used to add a `<source>` element to the media element
20927    * - `insertAdjacentHTML()` -  can be used to add a `<source>` element to the media element
20928    * - `innerHTML` -  can be used to add a `<source>` element to the media element
20929    *
20930    * @param {Html5} tech
20931    *        The tech object that sourceset is being setup on.
20932    */
20933 
20934 
20935   var firstSourceWatch = function firstSourceWatch(tech) {
20936     var el = tech.el(); // make sure firstSourceWatch isn't setup twice.
20937 
20938     if (el.resetSourceWatch_) {
20939       return;
20940     }
20941 
20942     var old = {};
20943     var innerDescriptor = getInnerHTMLDescriptor(tech);
20944 
20945     var appendWrapper = function appendWrapper(appendFn) {
20946       return function () {
20947         for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
20948           args[_key] = arguments[_key];
20949         }
20950 
20951         var retval = appendFn.apply(el, args);
20952         sourcesetLoad(tech);
20953         return retval;
20954       };
20955     };
20956 
20957     ['append', 'appendChild', 'insertAdjacentHTML'].forEach(function (k) {
20958       if (!el[k]) {
20959         return;
20960       } // store the old function
20961 
20962 
20963       old[k] = el[k]; // call the old function with a sourceset if a source
20964       // was loaded
20965 
20966       el[k] = appendWrapper(old[k]);
20967     });
20968     Object.defineProperty(el, 'innerHTML', mergeOptions(innerDescriptor, {
20969       set: appendWrapper(innerDescriptor.set)
20970     }));
20971 
20972     el.resetSourceWatch_ = function () {
20973       el.resetSourceWatch_ = null;
20974       Object.keys(old).forEach(function (k) {
20975         el[k] = old[k];
20976       });
20977       Object.defineProperty(el, 'innerHTML', innerDescriptor);
20978     }; // on the first sourceset, we need to revert our changes
20979 
20980 
20981     tech.one('sourceset', el.resetSourceWatch_);
20982   };
20983   /**
20984    * our implementation of a `src` descriptor for browsers
20985    * that do not have one.
20986    */
20987 
20988 
20989   var srcDescriptorPolyfill = Object.defineProperty({}, 'src', {
20990     get: function get() {
20991       if (this.hasAttribute('src')) {
20992         return getAbsoluteURL(window$1.Element.prototype.getAttribute.call(this, 'src'));
20993       }
20994 
20995       return '';
20996     },
20997     set: function set(v) {
20998       window$1.Element.prototype.setAttribute.call(this, 'src', v);
20999       return v;
21000     }
21001   });
21002 
21003   var getSrcDescriptor = function getSrcDescriptor(tech) {
21004     return getDescriptor([tech.el(), window$1.HTMLMediaElement.prototype, srcDescriptorPolyfill], 'src');
21005   };
21006   /**
21007    * setup `sourceset` handling on the `Html5` tech. This function
21008    * patches the following element properties/functions:
21009    *
21010    * - `src` - to determine when `src` is set
21011    * - `setAttribute()` - to determine when `src` is set
21012    * - `load()` - this re-triggers the source selection algorithm, and can
21013    *              cause a sourceset.
21014    *
21015    * If there is no source when we are adding `sourceset` support or during a `load()`
21016    * we also patch the functions listed in `firstSourceWatch`.
21017    *
21018    * @param {Html5} tech
21019    *        The tech to patch
21020    */
21021 
21022 
21023   var setupSourceset = function setupSourceset(tech) {
21024     if (!tech.featuresSourceset) {
21025       return;
21026     }
21027 
21028     var el = tech.el(); // make sure sourceset isn't setup twice.
21029 
21030     if (el.resetSourceset_) {
21031       return;
21032     }
21033 
21034     var srcDescriptor = getSrcDescriptor(tech);
21035     var oldSetAttribute = el.setAttribute;
21036     var oldLoad = el.load;
21037     Object.defineProperty(el, 'src', mergeOptions(srcDescriptor, {
21038       set: function set(v) {
21039         var retval = srcDescriptor.set.call(el, v); // we use the getter here to get the actual value set on src
21040 
21041         tech.triggerSourceset(el.src);
21042         return retval;
21043       }
21044     }));
21045 
21046     el.setAttribute = function (n, v) {
21047       var retval = oldSetAttribute.call(el, n, v);
21048 
21049       if (/src/i.test(n)) {
21050         tech.triggerSourceset(el.src);
21051       }
21052 
21053       return retval;
21054     };
21055 
21056     el.load = function () {
21057       var retval = oldLoad.call(el); // if load was called, but there was no source to fire
21058       // sourceset on. We have to watch for a source append
21059       // as that can trigger a `sourceset` when the media element
21060       // has no source
21061 
21062       if (!sourcesetLoad(tech)) {
21063         tech.triggerSourceset('');
21064         firstSourceWatch(tech);
21065       }
21066 
21067       return retval;
21068     };
21069 
21070     if (el.currentSrc) {
21071       tech.triggerSourceset(el.currentSrc);
21072     } else if (!sourcesetLoad(tech)) {
21073       firstSourceWatch(tech);
21074     }
21075 
21076     el.resetSourceset_ = function () {
21077       el.resetSourceset_ = null;
21078       el.load = oldLoad;
21079       el.setAttribute = oldSetAttribute;
21080       Object.defineProperty(el, 'src', srcDescriptor);
21081 
21082       if (el.resetSourceWatch_) {
21083         el.resetSourceWatch_();
21084       }
21085     };
21086   };
21087 
21088   /**
21089    * Object.defineProperty but "lazy", which means that the value is only set after
21090    * it retrieved the first time, rather than being set right away.
21091    *
21092    * @param {Object} obj the object to set the property on
21093    * @param {string} key the key for the property to set
21094    * @param {Function} getValue the function used to get the value when it is needed.
21095    * @param {boolean} setter wether a setter shoould be allowed or not
21096    */
21097   var defineLazyProperty = function defineLazyProperty(obj, key, getValue, setter) {
21098     if (setter === void 0) {
21099       setter = true;
21100     }
21101 
21102     var set = function set(value) {
21103       return Object.defineProperty(obj, key, {
21104         value: value,
21105         enumerable: true,
21106         writable: true
21107       });
21108     };
21109 
21110     var options = {
21111       configurable: true,
21112       enumerable: true,
21113       get: function get() {
21114         var value = getValue();
21115         set(value);
21116         return value;
21117       }
21118     };
21119 
21120     if (setter) {
21121       options.set = set;
21122     }
21123 
21124     return Object.defineProperty(obj, key, options);
21125   };
21126 
21127   /**
21128    * HTML5 Media Controller - Wrapper for HTML5 Media API
21129    *
21130    * @mixes Tech~SourceHandlerAdditions
21131    * @extends Tech
21132    */
21133 
21134   var Html5 =
21135   /*#__PURE__*/
21136   function (_Tech) {
21137     inheritsLoose(Html5, _Tech);
21138 
21139     /**
21140     * Create an instance of this Tech.
21141     *
21142     * @param {Object} [options]
21143     *        The key/value store of player options.
21144     *
21145     * @param {Component~ReadyCallback} ready
21146     *        Callback function to call when the `HTML5` Tech is ready.
21147     */
21148     function Html5(options, ready) {
21149       var _this;
21150 
21151       _this = _Tech.call(this, options, ready) || this;
21152       var source = options.source;
21153       var crossoriginTracks = false; // Set the source if one is provided
21154       // 1) Check if the source is new (if not, we want to keep the original so playback isn't interrupted)
21155       // 2) Check to see if the network state of the tag was failed at init, and if so, reset the source
21156       // anyway so the error gets fired.
21157 
21158       if (source && (_this.el_.currentSrc !== source.src || options.tag && options.tag.initNetworkState_ === 3)) {
21159         _this.setSource(source);
21160       } else {
21161         _this.handleLateInit_(_this.el_);
21162       } // setup sourceset after late sourceset/init
21163 
21164 
21165       if (options.enableSourceset) {
21166         _this.setupSourcesetHandling_();
21167       }
21168 
21169       if (_this.el_.hasChildNodes()) {
21170         var nodes = _this.el_.childNodes;
21171         var nodesLength = nodes.length;
21172         var removeNodes = [];
21173 
21174         while (nodesLength--) {
21175           var node = nodes[nodesLength];
21176           var nodeName = node.nodeName.toLowerCase();
21177 
21178           if (nodeName === 'track') {
21179             if (!_this.featuresNativeTextTracks) {
21180               // Empty video tag tracks so the built-in player doesn't use them also.
21181               // This may not be fast enough to stop HTML5 browsers from reading the tags
21182               // so we'll need to turn off any default tracks if we're manually doing
21183               // captions and subtitles. videoElement.textTracks
21184               removeNodes.push(node);
21185             } else {
21186               // store HTMLTrackElement and TextTrack to remote list
21187               _this.remoteTextTrackEls().addTrackElement_(node);
21188 
21189               _this.remoteTextTracks().addTrack(node.track);
21190 
21191               _this.textTracks().addTrack(node.track);
21192 
21193               if (!crossoriginTracks && !_this.el_.hasAttribute('crossorigin') && isCrossOrigin(node.src)) {
21194                 crossoriginTracks = true;
21195               }
21196             }
21197           }
21198         }
21199 
21200         for (var i = 0; i < removeNodes.length; i++) {
21201           _this.el_.removeChild(removeNodes[i]);
21202         }
21203       }
21204 
21205       _this.proxyNativeTracks_();
21206 
21207       if (_this.featuresNativeTextTracks && crossoriginTracks) {
21208         log.warn('Text Tracks are being loaded from another origin but the crossorigin attribute isn\'t used.\n' + 'This may prevent text tracks from loading.');
21209       } // prevent iOS Safari from disabling metadata text tracks during native playback
21210 
21211 
21212       _this.restoreMetadataTracksInIOSNativePlayer_(); // Determine if native controls should be used
21213       // Our goal should be to get the custom controls on mobile solid everywhere
21214       // so we can remove this all together. Right now this will block custom
21215       // controls on touch enabled laptops like the Chrome Pixel
21216 
21217 
21218       if ((TOUCH_ENABLED || IS_IPHONE || IS_NATIVE_ANDROID) && options.nativeControlsForTouch === true) {
21219         _this.setControls(true);
21220       } // on iOS, we want to proxy `webkitbeginfullscreen` and `webkitendfullscreen`
21221       // into a `fullscreenchange` event
21222 
21223 
21224       _this.proxyWebkitFullscreen_();
21225 
21226       _this.triggerReady();
21227 
21228       return _this;
21229     }
21230     /**
21231      * Dispose of `HTML5` media element and remove all tracks.
21232      */
21233 
21234 
21235     var _proto = Html5.prototype;
21236 
21237     _proto.dispose = function dispose() {
21238       if (this.el_ && this.el_.resetSourceset_) {
21239         this.el_.resetSourceset_();
21240       }
21241 
21242       Html5.disposeMediaElement(this.el_);
21243       this.options_ = null; // tech will handle clearing of the emulated track list
21244 
21245       _Tech.prototype.dispose.call(this);
21246     }
21247     /**
21248      * Modify the media element so that we can detect when
21249      * the source is changed. Fires `sourceset` just after the source has changed
21250      */
21251     ;
21252 
21253     _proto.setupSourcesetHandling_ = function setupSourcesetHandling_() {
21254       setupSourceset(this);
21255     }
21256     /**
21257      * When a captions track is enabled in the iOS Safari native player, all other
21258      * tracks are disabled (including metadata tracks), which nulls all of their
21259      * associated cue points. This will restore metadata tracks to their pre-fullscreen
21260      * state in those cases so that cue points are not needlessly lost.
21261      *
21262      * @private
21263      */
21264     ;
21265 
21266     _proto.restoreMetadataTracksInIOSNativePlayer_ = function restoreMetadataTracksInIOSNativePlayer_() {
21267       var textTracks = this.textTracks();
21268       var metadataTracksPreFullscreenState; // captures a snapshot of every metadata track's current state
21269 
21270       var takeMetadataTrackSnapshot = function takeMetadataTrackSnapshot() {
21271         metadataTracksPreFullscreenState = [];
21272 
21273         for (var i = 0; i < textTracks.length; i++) {
21274           var track = textTracks[i];
21275 
21276           if (track.kind === 'metadata') {
21277             metadataTracksPreFullscreenState.push({
21278               track: track,
21279               storedMode: track.mode
21280             });
21281           }
21282         }
21283       }; // snapshot each metadata track's initial state, and update the snapshot
21284       // each time there is a track 'change' event
21285 
21286 
21287       takeMetadataTrackSnapshot();
21288       textTracks.addEventListener('change', takeMetadataTrackSnapshot);
21289       this.on('dispose', function () {
21290         return textTracks.removeEventListener('change', takeMetadataTrackSnapshot);
21291       });
21292 
21293       var restoreTrackMode = function restoreTrackMode() {
21294         for (var i = 0; i < metadataTracksPreFullscreenState.length; i++) {
21295           var storedTrack = metadataTracksPreFullscreenState[i];
21296 
21297           if (storedTrack.track.mode === 'disabled' && storedTrack.track.mode !== storedTrack.storedMode) {
21298             storedTrack.track.mode = storedTrack.storedMode;
21299           }
21300         } // we only want this handler to be executed on the first 'change' event
21301 
21302 
21303         textTracks.removeEventListener('change', restoreTrackMode);
21304       }; // when we enter fullscreen playback, stop updating the snapshot and
21305       // restore all track modes to their pre-fullscreen state
21306 
21307 
21308       this.on('webkitbeginfullscreen', function () {
21309         textTracks.removeEventListener('change', takeMetadataTrackSnapshot); // remove the listener before adding it just in case it wasn't previously removed
21310 
21311         textTracks.removeEventListener('change', restoreTrackMode);
21312         textTracks.addEventListener('change', restoreTrackMode);
21313       }); // start updating the snapshot again after leaving fullscreen
21314 
21315       this.on('webkitendfullscreen', function () {
21316         // remove the listener before adding it just in case it wasn't previously removed
21317         textTracks.removeEventListener('change', takeMetadataTrackSnapshot);
21318         textTracks.addEventListener('change', takeMetadataTrackSnapshot); // remove the restoreTrackMode handler in case it wasn't triggered during fullscreen playback
21319 
21320         textTracks.removeEventListener('change', restoreTrackMode);
21321       });
21322     }
21323     /**
21324      * Attempt to force override of tracks for the given type
21325      *
21326      * @param {string} type - Track type to override, possible values include 'Audio',
21327      * 'Video', and 'Text'.
21328      * @param {boolean} override - If set to true native audio/video will be overridden,
21329      * otherwise native audio/video will potentially be used.
21330      * @private
21331      */
21332     ;
21333 
21334     _proto.overrideNative_ = function overrideNative_(type, override) {
21335       var _this2 = this;
21336 
21337       // If there is no behavioral change don't add/remove listeners
21338       if (override !== this["featuresNative" + type + "Tracks"]) {
21339         return;
21340       }
21341 
21342       var lowerCaseType = type.toLowerCase();
21343 
21344       if (this[lowerCaseType + "TracksListeners_"]) {
21345         Object.keys(this[lowerCaseType + "TracksListeners_"]).forEach(function (eventName) {
21346           var elTracks = _this2.el()[lowerCaseType + "Tracks"];
21347 
21348           elTracks.removeEventListener(eventName, _this2[lowerCaseType + "TracksListeners_"][eventName]);
21349         });
21350       }
21351 
21352       this["featuresNative" + type + "Tracks"] = !override;
21353       this[lowerCaseType + "TracksListeners_"] = null;
21354       this.proxyNativeTracksForType_(lowerCaseType);
21355     }
21356     /**
21357      * Attempt to force override of native audio tracks.
21358      *
21359      * @param {boolean} override - If set to true native audio will be overridden,
21360      * otherwise native audio will potentially be used.
21361      */
21362     ;
21363 
21364     _proto.overrideNativeAudioTracks = function overrideNativeAudioTracks(override) {
21365       this.overrideNative_('Audio', override);
21366     }
21367     /**
21368      * Attempt to force override of native video tracks.
21369      *
21370      * @param {boolean} override - If set to true native video will be overridden,
21371      * otherwise native video will potentially be used.
21372      */
21373     ;
21374 
21375     _proto.overrideNativeVideoTracks = function overrideNativeVideoTracks(override) {
21376       this.overrideNative_('Video', override);
21377     }
21378     /**
21379      * Proxy native track list events for the given type to our track
21380      * lists if the browser we are playing in supports that type of track list.
21381      *
21382      * @param {string} name - Track type; values include 'audio', 'video', and 'text'
21383      * @private
21384      */
21385     ;
21386 
21387     _proto.proxyNativeTracksForType_ = function proxyNativeTracksForType_(name) {
21388       var _this3 = this;
21389 
21390       var props = NORMAL[name];
21391       var elTracks = this.el()[props.getterName];
21392       var techTracks = this[props.getterName]();
21393 
21394       if (!this["featuresNative" + props.capitalName + "Tracks"] || !elTracks || !elTracks.addEventListener) {
21395         return;
21396       }
21397 
21398       var listeners = {
21399         change: function change(e) {
21400           techTracks.trigger({
21401             type: 'change',
21402             target: techTracks,
21403             currentTarget: techTracks,
21404             srcElement: techTracks
21405           });
21406         },
21407         addtrack: function addtrack(e) {
21408           techTracks.addTrack(e.track);
21409         },
21410         removetrack: function removetrack(e) {
21411           techTracks.removeTrack(e.track);
21412         }
21413       };
21414 
21415       var removeOldTracks = function removeOldTracks() {
21416         var removeTracks = [];
21417 
21418         for (var i = 0; i < techTracks.length; i++) {
21419           var found = false;
21420 
21421           for (var j = 0; j < elTracks.length; j++) {
21422             if (elTracks[j] === techTracks[i]) {
21423               found = true;
21424               break;
21425             }
21426           }
21427 
21428           if (!found) {
21429             removeTracks.push(techTracks[i]);
21430           }
21431         }
21432 
21433         while (removeTracks.length) {
21434           techTracks.removeTrack(removeTracks.shift());
21435         }
21436       };
21437 
21438       this[props.getterName + 'Listeners_'] = listeners;
21439       Object.keys(listeners).forEach(function (eventName) {
21440         var listener = listeners[eventName];
21441         elTracks.addEventListener(eventName, listener);
21442 
21443         _this3.on('dispose', function (e) {
21444           return elTracks.removeEventListener(eventName, listener);
21445         });
21446       }); // Remove (native) tracks that are not used anymore
21447 
21448       this.on('loadstart', removeOldTracks);
21449       this.on('dispose', function (e) {
21450         return _this3.off('loadstart', removeOldTracks);
21451       });
21452     }
21453     /**
21454      * Proxy all native track list events to our track lists if the browser we are playing
21455      * in supports that type of track list.
21456      *
21457      * @private
21458      */
21459     ;
21460 
21461     _proto.proxyNativeTracks_ = function proxyNativeTracks_() {
21462       var _this4 = this;
21463 
21464       NORMAL.names.forEach(function (name) {
21465         _this4.proxyNativeTracksForType_(name);
21466       });
21467     }
21468     /**
21469      * Create the `Html5` Tech's DOM element.
21470      *
21471      * @return {Element}
21472      *         The element that gets created.
21473      */
21474     ;
21475 
21476     _proto.createEl = function createEl() {
21477       var el = this.options_.tag; // Check if this browser supports moving the element into the box.
21478       // On the iPhone video will break if you move the element,
21479       // So we have to create a brand new element.
21480       // If we ingested the player div, we do not need to move the media element.
21481 
21482       if (!el || !(this.options_.playerElIngest || this.movingMediaElementInDOM)) {
21483         // If the original tag is still there, clone and remove it.
21484         if (el) {
21485           var clone = el.cloneNode(true);
21486 
21487           if (el.parentNode) {
21488             el.parentNode.insertBefore(clone, el);
21489           }
21490 
21491           Html5.disposeMediaElement(el);
21492           el = clone;
21493         } else {
21494           el = document.createElement('video'); // determine if native controls should be used
21495 
21496           var tagAttributes = this.options_.tag && getAttributes(this.options_.tag);
21497           var attributes = mergeOptions({}, tagAttributes);
21498 
21499           if (!TOUCH_ENABLED || this.options_.nativeControlsForTouch !== true) {
21500             delete attributes.controls;
21501           }
21502 
21503           setAttributes(el, assign(attributes, {
21504             id: this.options_.techId,
21505             "class": 'vjs-tech'
21506           }));
21507         }
21508 
21509         el.playerId = this.options_.playerId;
21510       }
21511 
21512       if (typeof this.options_.preload !== 'undefined') {
21513         setAttribute(el, 'preload', this.options_.preload);
21514       } // Update specific tag settings, in case they were overridden
21515       // `autoplay` has to be *last* so that `muted` and `playsinline` are present
vendor: 8,072 bytes, lines 21516-21760
21516       // when iOS/Safari or other browsers attempt to autoplay.
21517 
21518 
21519       var settingsAttrs = ['loop', 'muted', 'playsinline', 'autoplay'];
21520 
21521       for (var i = 0; i < settingsAttrs.length; i++) {
21522         var attr = settingsAttrs[i];
21523         var value = this.options_[attr];
21524 
21525         if (typeof value !== 'undefined') {
21526           if (value) {
21527             setAttribute(el, attr, attr);
21528           } else {
21529             removeAttribute(el, attr);
21530           }
21531 
21532           el[attr] = value;
21533         }
21534       }
21535 
21536       return el;
21537     }
21538     /**
21539      * This will be triggered if the loadstart event has already fired, before videojs was
21540      * ready. Two known examples of when this can happen are:
21541      * 1. If we're loading the playback object after it has started loading
21542      * 2. The media is already playing the (often with autoplay on) then
21543      *
21544      * This function will fire another loadstart so that videojs can catchup.
21545      *
21546      * @fires Tech#loadstart
21547      *
21548      * @return {undefined}
21549      *         returns nothing.
21550      */
21551     ;
21552 
21553     _proto.handleLateInit_ = function handleLateInit_(el) {
21554       if (el.networkState === 0 || el.networkState === 3) {
21555         // The video element hasn't started loading the source yet
21556         // or didn't find a source
21557         return;
21558       }
21559 
21560       if (el.readyState === 0) {
21561         // NetworkState is set synchronously BUT loadstart is fired at the
21562         // end of the current stack, usually before setInterval(fn, 0).
21563         // So at this point we know loadstart may have already fired or is
21564         // about to fire, and either way the player hasn't seen it yet.
21565         // We don't want to fire loadstart prematurely here and cause a
21566         // double loadstart so we'll wait and see if it happens between now
21567         // and the next loop, and fire it if not.
21568         // HOWEVER, we also want to make sure it fires before loadedmetadata
21569         // which could also happen between now and the next loop, so we'll
21570         // watch for that also.
21571         var loadstartFired = false;
21572 
21573         var setLoadstartFired = function setLoadstartFired() {
21574           loadstartFired = true;
21575         };
21576 
21577         this.on('loadstart', setLoadstartFired);
21578 
21579         var triggerLoadstart = function triggerLoadstart() {
21580           // We did miss the original loadstart. Make sure the player
21581           // sees loadstart before loadedmetadata
21582           if (!loadstartFired) {
21583             this.trigger('loadstart');
21584           }
21585         };
21586 
21587         this.on('loadedmetadata', triggerLoadstart);
21588         this.ready(function () {
21589           this.off('loadstart', setLoadstartFired);
21590           this.off('loadedmetadata', triggerLoadstart);
21591 
21592           if (!loadstartFired) {
21593             // We did miss the original native loadstart. Fire it now.
21594             this.trigger('loadstart');
21595           }
21596         });
21597         return;
21598       } // From here on we know that loadstart already fired and we missed it.
21599       // The other readyState events aren't as much of a problem if we double
21600       // them, so not going to go to as much trouble as loadstart to prevent
21601       // that unless we find reason to.
21602 
21603 
21604       var eventsToTrigger = ['loadstart']; // loadedmetadata: newly equal to HAVE_METADATA (1) or greater
21605 
21606       eventsToTrigger.push('loadedmetadata'); // loadeddata: newly increased to HAVE_CURRENT_DATA (2) or greater
21607 
21608       if (el.readyState >= 2) {
21609         eventsToTrigger.push('loadeddata');
21610       } // canplay: newly increased to HAVE_FUTURE_DATA (3) or greater
21611 
21612 
21613       if (el.readyState >= 3) {
21614         eventsToTrigger.push('canplay');
21615       } // canplaythrough: newly equal to HAVE_ENOUGH_DATA (4)
21616 
21617 
21618       if (el.readyState >= 4) {
21619         eventsToTrigger.push('canplaythrough');
21620       } // We still need to give the player time to add event listeners
21621 
21622 
21623       this.ready(function () {
21624         eventsToTrigger.forEach(function (type) {
21625           this.trigger(type);
21626         }, this);
21627       });
21628     }
21629     /**
21630      * Set current time for the `HTML5` tech.
21631      *
21632      * @param {number} seconds
21633      *        Set the current time of the media to this.
21634      */
21635     ;
21636 
21637     _proto.setCurrentTime = function setCurrentTime(seconds) {
21638       try {
21639         this.el_.currentTime = seconds;
21640       } catch (e) {
21641         log(e, 'Video is not ready. (Video.js)'); // this.warning(VideoJS.warnings.videoNotReady);
21642       }
21643     }
21644     /**
21645      * Get the current duration of the HTML5 media element.
21646      *
21647      * @return {number}
21648      *         The duration of the media or 0 if there is no duration.
21649      */
21650     ;
21651 
21652     _proto.duration = function duration() {
21653       var _this5 = this;
21654 
21655       // Android Chrome will report duration as Infinity for VOD HLS until after
21656       // playback has started, which triggers the live display erroneously.
21657       // Return NaN if playback has not started and trigger a durationupdate once
21658       // the duration can be reliably known.
21659       if (this.el_.duration === Infinity && IS_ANDROID && IS_CHROME && this.el_.currentTime === 0) {
21660         // Wait for the first `timeupdate` with currentTime > 0 - there may be
21661         // several with 0
21662         var checkProgress = function checkProgress() {
21663           if (_this5.el_.currentTime > 0) {
21664             // Trigger durationchange for genuinely live video
21665             if (_this5.el_.duration === Infinity) {
21666               _this5.trigger('durationchange');
21667             }
21668 
21669             _this5.off('timeupdate', checkProgress);
21670           }
21671         };
21672 
21673         this.on('timeupdate', checkProgress);
21674         return NaN;
21675       }
21676 
21677       return this.el_.duration || NaN;
21678     }
21679     /**
21680      * Get the current width of the HTML5 media element.
21681      *
21682      * @return {number}
21683      *         The width of the HTML5 media element.
21684      */
21685     ;
21686 
21687     _proto.width = function width() {
21688       return this.el_.offsetWidth;
21689     }
21690     /**
21691      * Get the current height of the HTML5 media element.
21692      *
21693      * @return {number}
21694      *         The height of the HTML5 media element.
21695      */
21696     ;
21697 
21698     _proto.height = function height() {
21699       return this.el_.offsetHeight;
21700     }
21701     /**
21702      * Proxy iOS `webkitbeginfullscreen` and `webkitendfullscreen` into
21703      * `fullscreenchange` event.
21704      *
21705      * @private
21706      * @fires fullscreenchange
21707      * @listens webkitendfullscreen
21708      * @listens webkitbeginfullscreen
21709      * @listens webkitbeginfullscreen
21710      */
21711     ;
21712 
21713     _proto.proxyWebkitFullscreen_ = function proxyWebkitFullscreen_() {
21714       var _this6 = this;
21715 
21716       if (!('webkitDisplayingFullscreen' in this.el_)) {
21717         return;
21718       }
21719 
21720       var endFn = function endFn() {
21721         this.trigger('fullscreenchange', {
21722           isFullscreen: false
21723         });
21724       };
21725 
21726       var beginFn = function beginFn() {
21727         if ('webkitPresentationMode' in this.el_ && this.el_.webkitPresentationMode !== 'picture-in-picture') {
21728           this.one('webkitendfullscreen', endFn);
21729           this.trigger('fullscreenchange', {
21730             isFullscreen: true
21731           });
21732         }
21733       };
21734 
21735       this.on('webkitbeginfullscreen', beginFn);
21736       this.on('dispose', function () {
21737         _this6.off('webkitbeginfullscreen', beginFn);
21738 
21739         _this6.off('webkitendfullscreen', endFn);
21740       });
21741     }
21742     /**
21743      * Check if fullscreen is supported on the current playback device.
21744      *
21745      * @return {boolean}
21746      *         - True if fullscreen is supported.
21747      *         - False if fullscreen is not supported.
21748      */
21749     ;
21750 
21751     _proto.supportsFullScreen = function supportsFullScreen() {
21752       if (typeof this.el_.webkitEnterFullScreen === 'function') {
21753         var userAgent = window$1.navigator && window$1.navigator.userAgent || ''; // Seems to be broken in Chromium/Chrome && Safari in Leopard
21754 
21755         if (/Android/.test(userAgent) || !/Chrome|Mac OS X 10.5/.test(userAgent)) {
21756           return true;
21757         }
21758       }
21759 
21760       return false;
vendor: 33,393 bytes, lines 21761-22754
21761     }
21762     /**
21763      * Request that the `HTML5` Tech enter fullscreen.
21764      */
21765     ;
21766 
21767     _proto.enterFullScreen = function enterFullScreen() {
21768       var video = this.el_;
21769 
21770       if (video.paused && video.networkState <= video.HAVE_METADATA) {
21771         // attempt to prime the video element for programmatic access
21772         // this isn't necessary on the desktop but shouldn't hurt
21773         this.el_.play(); // playing and pausing synchronously during the transition to fullscreen
21774         // can get iOS ~6.1 devices into a play/pause loop
21775 
21776         this.setTimeout(function () {
21777           video.pause();
21778           video.webkitEnterFullScreen();
21779         }, 0);
21780       } else {
21781         video.webkitEnterFullScreen();
21782       }
21783     }
21784     /**
21785      * Request that the `HTML5` Tech exit fullscreen.
21786      */
21787     ;
21788 
21789     _proto.exitFullScreen = function exitFullScreen() {
21790       this.el_.webkitExitFullScreen();
21791     }
21792     /**
21793      * Create a floating video window always on top of other windows so that users may
21794      * continue consuming media while they interact with other content sites, or
21795      * applications on their device.
21796      *
21797      * @see [Spec]{@link https://wicg.github.io/picture-in-picture}
21798      *
21799      * @return {Promise}
21800      *         A promise with a Picture-in-Picture window.
21801      */
21802     ;
21803 
21804     _proto.requestPictureInPicture = function requestPictureInPicture() {
21805       return this.el_.requestPictureInPicture();
21806     }
21807     /**
21808      * A getter/setter for the `Html5` Tech's source object.
21809      * > Note: Please use {@link Html5#setSource}
21810      *
21811      * @param {Tech~SourceObject} [src]
21812      *        The source object you want to set on the `HTML5` techs element.
21813      *
21814      * @return {Tech~SourceObject|undefined}
21815      *         - The current source object when a source is not passed in.
21816      *         - undefined when setting
21817      *
21818      * @deprecated Since version 5.
21819      */
21820     ;
21821 
21822     _proto.src = function src(_src) {
21823       if (_src === undefined) {
21824         return this.el_.src;
21825       } // Setting src through `src` instead of `setSrc` will be deprecated
21826 
21827 
21828       this.setSrc(_src);
21829     }
21830     /**
21831      * Reset the tech by removing all sources and then calling
21832      * {@link Html5.resetMediaElement}.
21833      */
21834     ;
21835 
21836     _proto.reset = function reset() {
21837       Html5.resetMediaElement(this.el_);
21838     }
21839     /**
21840      * Get the current source on the `HTML5` Tech. Falls back to returning the source from
21841      * the HTML5 media element.
21842      *
21843      * @return {Tech~SourceObject}
21844      *         The current source object from the HTML5 tech. With a fallback to the
21845      *         elements source.
21846      */
21847     ;
21848 
21849     _proto.currentSrc = function currentSrc() {
21850       if (this.currentSource_) {
21851         return this.currentSource_.src;
21852       }
21853 
21854       return this.el_.currentSrc;
21855     }
21856     /**
21857      * Set controls attribute for the HTML5 media Element.
21858      *
21859      * @param {string} val
21860      *        Value to set the controls attribute to
21861      */
21862     ;
21863 
21864     _proto.setControls = function setControls(val) {
21865       this.el_.controls = !!val;
21866     }
21867     /**
21868      * Create and returns a remote {@link TextTrack} object.
21869      *
21870      * @param {string} kind
21871      *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata)
21872      *
21873      * @param {string} [label]
21874      *        Label to identify the text track
21875      *
21876      * @param {string} [language]
21877      *        Two letter language abbreviation
21878      *
21879      * @return {TextTrack}
21880      *         The TextTrack that gets created.
21881      */
21882     ;
21883 
21884     _proto.addTextTrack = function addTextTrack(kind, label, language) {
21885       if (!this.featuresNativeTextTracks) {
21886         return _Tech.prototype.addTextTrack.call(this, kind, label, language);
21887       }
21888 
21889       return this.el_.addTextTrack(kind, label, language);
21890     }
21891     /**
21892      * Creates either native TextTrack or an emulated TextTrack depending
21893      * on the value of `featuresNativeTextTracks`
21894      *
21895      * @param {Object} options
21896      *        The object should contain the options to initialize the TextTrack with.
21897      *
21898      * @param {string} [options.kind]
21899      *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata).
21900      *
21901      * @param {string} [options.label]
21902      *        Label to identify the text track
21903      *
21904      * @param {string} [options.language]
21905      *        Two letter language abbreviation.
21906      *
21907      * @param {boolean} [options.default]
21908      *        Default this track to on.
21909      *
21910      * @param {string} [options.id]
21911      *        The internal id to assign this track.
21912      *
21913      * @param {string} [options.src]
21914      *        A source url for the track.
21915      *
21916      * @return {HTMLTrackElement}
21917      *         The track element that gets created.
21918      */
21919     ;
21920 
21921     _proto.createRemoteTextTrack = function createRemoteTextTrack(options) {
21922       if (!this.featuresNativeTextTracks) {
21923         return _Tech.prototype.createRemoteTextTrack.call(this, options);
21924       }
21925 
21926       var htmlTrackElement = document.createElement('track');
21927 
21928       if (options.kind) {
21929         htmlTrackElement.kind = options.kind;
21930       }
21931 
21932       if (options.label) {
21933         htmlTrackElement.label = options.label;
21934       }
21935 
21936       if (options.language || options.srclang) {
21937         htmlTrackElement.srclang = options.language || options.srclang;
21938       }
21939 
21940       if (options["default"]) {
21941         htmlTrackElement["default"] = options["default"];
21942       }
21943 
21944       if (options.id) {
21945         htmlTrackElement.id = options.id;
21946       }
21947 
21948       if (options.src) {
21949         htmlTrackElement.src = options.src;
21950       }
21951 
21952       return htmlTrackElement;
21953     }
21954     /**
21955      * Creates a remote text track object and returns an html track element.
21956      *
21957      * @param {Object} options The object should contain values for
21958      * kind, language, label, and src (location of the WebVTT file)
21959      * @param {boolean} [manualCleanup=true] if set to false, the TextTrack will be
21960      * automatically removed from the video element whenever the source changes
21961      * @return {HTMLTrackElement} An Html Track Element.
21962      * This can be an emulated {@link HTMLTrackElement} or a native one.
21963      * @deprecated The default value of the "manualCleanup" parameter will default
21964      * to "false" in upcoming versions of Video.js
21965      */
21966     ;
21967 
21968     _proto.addRemoteTextTrack = function addRemoteTextTrack(options, manualCleanup) {
21969       var htmlTrackElement = _Tech.prototype.addRemoteTextTrack.call(this, options, manualCleanup);
21970 
21971       if (this.featuresNativeTextTracks) {
21972         this.el().appendChild(htmlTrackElement);
21973       }
21974 
21975       return htmlTrackElement;
21976     }
21977     /**
21978      * Remove remote `TextTrack` from `TextTrackList` object
21979      *
21980      * @param {TextTrack} track
21981      *        `TextTrack` object to remove
21982      */
21983     ;
21984 
21985     _proto.removeRemoteTextTrack = function removeRemoteTextTrack(track) {
21986       _Tech.prototype.removeRemoteTextTrack.call(this, track);
21987 
21988       if (this.featuresNativeTextTracks) {
21989         var tracks = this.$$('track');
21990         var i = tracks.length;
21991 
21992         while (i--) {
21993           if (track === tracks[i] || track === tracks[i].track) {
21994             this.el().removeChild(tracks[i]);
21995           }
21996         }
21997       }
21998     }
21999     /**
22000      * Gets available media playback quality metrics as specified by the W3C's Media
22001      * Playback Quality API.
22002      *
22003      * @see [Spec]{@link https://wicg.github.io/media-playback-quality}
22004      *
22005      * @return {Object}
22006      *         An object with supported media playback quality metrics
22007      */
22008     ;
22009 
22010     _proto.getVideoPlaybackQuality = function getVideoPlaybackQuality() {
22011       if (typeof this.el().getVideoPlaybackQuality === 'function') {
22012         return this.el().getVideoPlaybackQuality();
22013       }
22014 
22015       var videoPlaybackQuality = {};
22016 
22017       if (typeof this.el().webkitDroppedFrameCount !== 'undefined' && typeof this.el().webkitDecodedFrameCount !== 'undefined') {
22018         videoPlaybackQuality.droppedVideoFrames = this.el().webkitDroppedFrameCount;
22019         videoPlaybackQuality.totalVideoFrames = this.el().webkitDecodedFrameCount;
22020       }
22021 
22022       if (window$1.performance && typeof window$1.performance.now === 'function') {
22023         videoPlaybackQuality.creationTime = window$1.performance.now();
22024       } else if (window$1.performance && window$1.performance.timing && typeof window$1.performance.timing.navigationStart === 'number') {
22025         videoPlaybackQuality.creationTime = window$1.Date.now() - window$1.performance.timing.navigationStart;
22026       }
22027 
22028       return videoPlaybackQuality;
22029     };
22030 
22031     return Html5;
22032   }(Tech);
22033   /* HTML5 Support Testing ---------------------------------------------------- */
22034 
22035   /**
22036    * Element for testing browser HTML5 media capabilities
22037    *
22038    * @type {Element}
22039    * @constant
22040    * @private
22041    */
22042 
22043 
22044   defineLazyProperty(Html5, 'TEST_VID', function () {
22045     if (!isReal()) {
22046       return;
22047     }
22048 
22049     var video = document.createElement('video');
22050     var track = document.createElement('track');
22051     track.kind = 'captions';
22052     track.srclang = 'en';
22053     track.label = 'English';
22054     video.appendChild(track);
22055     return video;
22056   });
22057   /**
22058    * Check if HTML5 media is supported by this browser/device.
22059    *
22060    * @return {boolean}
22061    *         - True if HTML5 media is supported.
22062    *         - False if HTML5 media is not supported.
22063    */
22064 
22065   Html5.isSupported = function () {
22066     // IE with no Media Player is a LIAR! (#984)
22067     try {
22068       Html5.TEST_VID.volume = 0.5;
22069     } catch (e) {
22070       return false;
22071     }
22072 
22073     return !!(Html5.TEST_VID && Html5.TEST_VID.canPlayType);
22074   };
22075   /**
22076    * Check if the tech can support the given type
22077    *
22078    * @param {string} type
22079    *        The mimetype to check
22080    * @return {string} 'probably', 'maybe', or '' (empty string)
22081    */
22082 
22083 
22084   Html5.canPlayType = function (type) {
22085     return Html5.TEST_VID.canPlayType(type);
22086   };
22087   /**
22088    * Check if the tech can support the given source
22089    *
22090    * @param {Object} srcObj
22091    *        The source object
22092    * @param {Object} options
22093    *        The options passed to the tech
22094    * @return {string} 'probably', 'maybe', or '' (empty string)
22095    */
22096 
22097 
22098   Html5.canPlaySource = function (srcObj, options) {
22099     return Html5.canPlayType(srcObj.type);
22100   };
22101   /**
22102    * Check if the volume can be changed in this browser/device.
22103    * Volume cannot be changed in a lot of mobile devices.
22104    * Specifically, it can't be changed from 1 on iOS.
22105    *
22106    * @return {boolean}
22107    *         - True if volume can be controlled
22108    *         - False otherwise
22109    */
22110 
22111 
22112   Html5.canControlVolume = function () {
22113     // IE will error if Windows Media Player not installed #3315
22114     try {
22115       var volume = Html5.TEST_VID.volume;
22116       Html5.TEST_VID.volume = volume / 2 + 0.1;
22117       return volume !== Html5.TEST_VID.volume;
22118     } catch (e) {
22119       return false;
22120     }
22121   };
22122   /**
22123    * Check if the volume can be muted in this browser/device.
22124    * Some devices, e.g. iOS, don't allow changing volume
22125    * but permits muting/unmuting.
22126    *
22127    * @return {bolean}
22128    *      - True if volume can be muted
22129    *      - False otherwise
22130    */
22131 
22132 
22133   Html5.canMuteVolume = function () {
22134     try {
22135       var muted = Html5.TEST_VID.muted; // in some versions of iOS muted property doesn't always
22136       // work, so we want to set both property and attribute
22137 
22138       Html5.TEST_VID.muted = !muted;
22139 
22140       if (Html5.TEST_VID.muted) {
22141         setAttribute(Html5.TEST_VID, 'muted', 'muted');
22142       } else {
22143         removeAttribute(Html5.TEST_VID, 'muted', 'muted');
22144       }
22145 
22146       return muted !== Html5.TEST_VID.muted;
22147     } catch (e) {
22148       return false;
22149     }
22150   };
22151   /**
22152    * Check if the playback rate can be changed in this browser/device.
22153    *
22154    * @return {boolean}
22155    *         - True if playback rate can be controlled
22156    *         - False otherwise
22157    */
22158 
22159 
22160   Html5.canControlPlaybackRate = function () {
22161     // Playback rate API is implemented in Android Chrome, but doesn't do anything
22162     // https://github.com/videojs/video.js/issues/3180
22163     if (IS_ANDROID && IS_CHROME && CHROME_VERSION < 58) {
22164       return false;
22165     } // IE will error if Windows Media Player not installed #3315
22166 
22167 
22168     try {
22169       var playbackRate = Html5.TEST_VID.playbackRate;
22170       Html5.TEST_VID.playbackRate = playbackRate / 2 + 0.1;
22171       return playbackRate !== Html5.TEST_VID.playbackRate;
22172     } catch (e) {
22173       return false;
22174     }
22175   };
22176   /**
22177    * Check if we can override a video/audio elements attributes, with
22178    * Object.defineProperty.
22179    *
22180    * @return {boolean}
22181    *         - True if builtin attributes can be overridden
22182    *         - False otherwise
22183    */
22184 
22185 
22186   Html5.canOverrideAttributes = function () {
22187     // if we cannot overwrite the src/innerHTML property, there is no support
22188     // iOS 7 safari for instance cannot do this.
22189     try {
22190       var noop = function noop() {};
22191 
22192       Object.defineProperty(document.createElement('video'), 'src', {
22193         get: noop,
22194         set: noop
22195       });
22196       Object.defineProperty(document.createElement('audio'), 'src', {
22197         get: noop,
22198         set: noop
22199       });
22200       Object.defineProperty(document.createElement('video'), 'innerHTML', {
22201         get: noop,
22202         set: noop
22203       });
22204       Object.defineProperty(document.createElement('audio'), 'innerHTML', {
22205         get: noop,
22206         set: noop
22207       });
22208     } catch (e) {
22209       return false;
22210     }
22211 
22212     return true;
22213   };
22214   /**
22215    * Check to see if native `TextTrack`s are supported by this browser/device.
22216    *
22217    * @return {boolean}
22218    *         - True if native `TextTrack`s are supported.
22219    *         - False otherwise
22220    */
22221 
22222 
22223   Html5.supportsNativeTextTracks = function () {
22224     return IS_ANY_SAFARI || IS_IOS && IS_CHROME;
22225   };
22226   /**
22227    * Check to see if native `VideoTrack`s are supported by this browser/device
22228    *
22229    * @return {boolean}
22230    *        - True if native `VideoTrack`s are supported.
22231    *        - False otherwise
22232    */
22233 
22234 
22235   Html5.supportsNativeVideoTracks = function () {
22236     return !!(Html5.TEST_VID && Html5.TEST_VID.videoTracks);
22237   };
22238   /**
22239    * Check to see if native `AudioTrack`s are supported by this browser/device
22240    *
22241    * @return {boolean}
22242    *        - True if native `AudioTrack`s are supported.
22243    *        - False otherwise
22244    */
22245 
22246 
22247   Html5.supportsNativeAudioTracks = function () {
22248     return !!(Html5.TEST_VID && Html5.TEST_VID.audioTracks);
22249   };
22250   /**
22251    * An array of events available on the Html5 tech.
22252    *
22253    * @private
22254    * @type {Array}
22255    */
22256 
22257 
22258   Html5.Events = ['loadstart', 'suspend', 'abort', 'error', 'emptied', 'stalled', 'loadedmetadata', 'loadeddata', 'canplay', 'canplaythrough', 'playing', 'waiting', 'seeking', 'seeked', 'ended', 'durationchange', 'timeupdate', 'progress', 'play', 'pause', 'ratechange', 'resize', 'volumechange'];
22259   /**
22260    * Boolean indicating whether the `Tech` supports volume control.
22261    *
22262    * @type {boolean}
22263    * @default {@link Html5.canControlVolume}
22264    */
22265 
22266   /**
22267    * Boolean indicating whether the `Tech` supports muting volume.
22268    *
22269    * @type {bolean}
22270    * @default {@link Html5.canMuteVolume}
22271    */
22272 
22273   /**
22274    * Boolean indicating whether the `Tech` supports changing the speed at which the media
22275    * plays. Examples:
22276    *   - Set player to play 2x (twice) as fast
22277    *   - Set player to play 0.5x (half) as fast
22278    *
22279    * @type {boolean}
22280    * @default {@link Html5.canControlPlaybackRate}
22281    */
22282 
22283   /**
22284    * Boolean indicating whether the `Tech` supports the `sourceset` event.
22285    *
22286    * @type {boolean}
22287    * @default
22288    */
22289 
22290   /**
22291    * Boolean indicating whether the `HTML5` tech currently supports native `TextTrack`s.
22292    *
22293    * @type {boolean}
22294    * @default {@link Html5.supportsNativeTextTracks}
22295    */
22296 
22297   /**
22298    * Boolean indicating whether the `HTML5` tech currently supports native `VideoTrack`s.
22299    *
22300    * @type {boolean}
22301    * @default {@link Html5.supportsNativeVideoTracks}
22302    */
22303 
22304   /**
22305    * Boolean indicating whether the `HTML5` tech currently supports native `AudioTrack`s.
22306    *
22307    * @type {boolean}
22308    * @default {@link Html5.supportsNativeAudioTracks}
22309    */
22310 
22311   [['featuresVolumeControl', 'canControlVolume'], ['featuresMuteControl', 'canMuteVolume'], ['featuresPlaybackRate', 'canControlPlaybackRate'], ['featuresSourceset', 'canOverrideAttributes'], ['featuresNativeTextTracks', 'supportsNativeTextTracks'], ['featuresNativeVideoTracks', 'supportsNativeVideoTracks'], ['featuresNativeAudioTracks', 'supportsNativeAudioTracks']].forEach(function (_ref) {
22312     var key = _ref[0],
22313         fn = _ref[1];
22314     defineLazyProperty(Html5.prototype, key, function () {
22315       return Html5[fn]();
22316     }, true);
22317   });
22318   /**
22319    * Boolean indicating whether the `HTML5` tech currently supports the media element
22320    * moving in the DOM. iOS breaks if you move the media element, so this is set this to
22321    * false there. Everywhere else this should be true.
22322    *
22323    * @type {boolean}
22324    * @default
22325    */
22326 
22327   Html5.prototype.movingMediaElementInDOM = !IS_IOS; // TODO: Previous comment: No longer appears to be used. Can probably be removed.
22328   //       Is this true?
22329 
22330   /**
22331    * Boolean indicating whether the `HTML5` tech currently supports automatic media resize
22332    * when going into fullscreen.
22333    *
22334    * @type {boolean}
22335    * @default
22336    */
22337 
22338   Html5.prototype.featuresFullscreenResize = true;
22339   /**
22340    * Boolean indicating whether the `HTML5` tech currently supports the progress event.
22341    * If this is false, manual `progress` events will be triggered instead.
22342    *
22343    * @type {boolean}
22344    * @default
22345    */
22346 
22347   Html5.prototype.featuresProgressEvents = true;
22348   /**
22349    * Boolean indicating whether the `HTML5` tech currently supports the timeupdate event.
22350    * If this is false, manual `timeupdate` events will be triggered instead.
22351    *
22352    * @default
22353    */
22354 
22355   Html5.prototype.featuresTimeupdateEvents = true; // HTML5 Feature detection and Device Fixes --------------------------------- //
22356 
22357   var canPlayType;
22358 
22359   Html5.patchCanPlayType = function () {
22360     // Android 4.0 and above can play HLS to some extent but it reports being unable to do so
22361     // Firefox and Chrome report correctly
22362     if (ANDROID_VERSION >= 4.0 && !IS_FIREFOX && !IS_CHROME) {
22363       canPlayType = Html5.TEST_VID && Html5.TEST_VID.constructor.prototype.canPlayType;
22364 
22365       Html5.TEST_VID.constructor.prototype.canPlayType = function (type) {
22366         var mpegurlRE = /^application\/(?:x-|vnd\.apple\.)mpegurl/i;
22367 
22368         if (type && mpegurlRE.test(type)) {
22369           return 'maybe';
22370         }
22371 
22372         return canPlayType.call(this, type);
22373       };
22374     }
22375   };
22376 
22377   Html5.unpatchCanPlayType = function () {
22378     var r = Html5.TEST_VID.constructor.prototype.canPlayType;
22379 
22380     if (canPlayType) {
22381       Html5.TEST_VID.constructor.prototype.canPlayType = canPlayType;
22382     }
22383 
22384     return r;
22385   }; // by default, patch the media element
22386 
22387 
22388   Html5.patchCanPlayType();
22389 
22390   Html5.disposeMediaElement = function (el) {
22391     if (!el) {
22392       return;
22393     }
22394 
22395     if (el.parentNode) {
22396       el.parentNode.removeChild(el);
22397     } // remove any child track or source nodes to prevent their loading
22398 
22399 
22400     while (el.hasChildNodes()) {
22401       el.removeChild(el.firstChild);
22402     } // remove any src reference. not setting `src=''` because that causes a warning
22403     // in firefox
22404 
22405 
22406     el.removeAttribute('src'); // force the media element to update its loading state by calling load()
22407     // however IE on Windows 7N has a bug that throws an error so need a try/catch (#793)
22408 
22409     if (typeof el.load === 'function') {
22410       // wrapping in an iife so it's not deoptimized (#1060#discussion_r10324473)
22411       (function () {
22412         try {
22413           el.load();
22414         } catch (e) {// not supported
22415         }
22416       })();
22417     }
22418   };
22419 
22420   Html5.resetMediaElement = function (el) {
22421     if (!el) {
22422       return;
22423     }
22424 
22425     var sources = el.querySelectorAll('source');
22426     var i = sources.length;
22427 
22428     while (i--) {
22429       el.removeChild(sources[i]);
22430     } // remove any src reference.
22431     // not setting `src=''` because that throws an error
22432 
22433 
22434     el.removeAttribute('src');
22435 
22436     if (typeof el.load === 'function') {
22437       // wrapping in an iife so it's not deoptimized (#1060#discussion_r10324473)
22438       (function () {
22439         try {
22440           el.load();
22441         } catch (e) {// satisfy linter
22442         }
22443       })();
22444     }
22445   };
22446   /* Native HTML5 element property wrapping ----------------------------------- */
22447   // Wrap native boolean attributes with getters that check both property and attribute
22448   // The list is as followed:
22449   // muted, defaultMuted, autoplay, controls, loop, playsinline
22450 
22451 
22452   [
22453   /**
22454    * Get the value of `muted` from the media element. `muted` indicates
22455    * that the volume for the media should be set to silent. This does not actually change
22456    * the `volume` attribute.
22457    *
22458    * @method Html5#muted
22459    * @return {boolean}
22460    *         - True if the value of `volume` should be ignored and the audio set to silent.
22461    *         - False if the value of `volume` should be used.
22462    *
22463    * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted}
22464    */
22465   'muted',
22466   /**
22467    * Get the value of `defaultMuted` from the media element. `defaultMuted` indicates
22468    * whether the media should start muted or not. Only changes the default state of the
22469    * media. `muted` and `defaultMuted` can have different values. {@link Html5#muted} indicates the
22470    * current state.
22471    *
22472    * @method Html5#defaultMuted
22473    * @return {boolean}
22474    *         - The value of `defaultMuted` from the media element.
22475    *         - True indicates that the media should start muted.
22476    *         - False indicates that the media should not start muted
22477    *
22478    * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultmuted}
22479    */
22480   'defaultMuted',
22481   /**
22482    * Get the value of `autoplay` from the media element. `autoplay` indicates
22483    * that the media should start to play as soon as the page is ready.
22484    *
22485    * @method Html5#autoplay
22486    * @return {boolean}
22487    *         - The value of `autoplay` from the media element.
22488    *         - True indicates that the media should start as soon as the page loads.
22489    *         - False indicates that the media should not start as soon as the page loads.
22490    *
22491    * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay}
22492    */
22493   'autoplay',
22494   /**
22495    * Get the value of `controls` from the media element. `controls` indicates
22496    * whether the native media controls should be shown or hidden.
22497    *
22498    * @method Html5#controls
22499    * @return {boolean}
22500    *         - The value of `controls` from the media element.
22501    *         - True indicates that native controls should be showing.
22502    *         - False indicates that native controls should be hidden.
22503    *
22504    * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-controls}
22505    */
22506   'controls',
22507   /**
22508    * Get the value of `loop` from the media element. `loop` indicates
22509    * that the media should return to the start of the media and continue playing once
22510    * it reaches the end.
22511    *
22512    * @method Html5#loop
22513    * @return {boolean}
22514    *         - The value of `loop` from the media element.
22515    *         - True indicates that playback should seek back to start once
22516    *           the end of a media is reached.
22517    *         - False indicates that playback should not loop back to the start when the
22518    *           end of the media is reached.
22519    *
22520    * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop}
22521    */
22522   'loop',
22523   /**
22524    * Get the value of `playsinline` from the media element. `playsinline` indicates
22525    * to the browser that non-fullscreen playback is preferred when fullscreen
22526    * playback is the native default, such as in iOS Safari.
22527    *
22528    * @method Html5#playsinline
22529    * @return {boolean}
22530    *         - The value of `playsinline` from the media element.
22531    *         - True indicates that the media should play inline.
22532    *         - False indicates that the media should not play inline.
22533    *
22534    * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
22535    */
22536   'playsinline'].forEach(function (prop) {
22537     Html5.prototype[prop] = function () {
22538       return this.el_[prop] || this.el_.hasAttribute(prop);
22539     };
22540   }); // Wrap native boolean attributes with setters that set both property and attribute
22541   // The list is as followed:
22542   // setMuted, setDefaultMuted, setAutoplay, setLoop, setPlaysinline
22543   // setControls is special-cased above
22544 
22545   [
22546   /**
22547    * Set the value of `muted` on the media element. `muted` indicates that the current
22548    * audio level should be silent.
22549    *
22550    * @method Html5#setMuted
22551    * @param {boolean} muted
22552    *        - True if the audio should be set to silent
22553    *        - False otherwise
22554    *
22555    * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted}
22556    */
22557   'muted',
22558   /**
22559    * Set the value of `defaultMuted` on the media element. `defaultMuted` indicates that the current
22560    * audio level should be silent, but will only effect the muted level on intial playback..
22561    *
22562    * @method Html5.prototype.setDefaultMuted
22563    * @param {boolean} defaultMuted
22564    *        - True if the audio should be set to silent
22565    *        - False otherwise
22566    *
22567    * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultmuted}
22568    */
22569   'defaultMuted',
22570   /**
22571    * Set the value of `autoplay` on the media element. `autoplay` indicates
22572    * that the media should start to play as soon as the page is ready.
22573    *
22574    * @method Html5#setAutoplay
22575    * @param {boolean} autoplay
22576    *         - True indicates that the media should start as soon as the page loads.
22577    *         - False indicates that the media should not start as soon as the page loads.
22578    *
22579    * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay}
22580    */
22581   'autoplay',
22582   /**
22583    * Set the value of `loop` on the media element. `loop` indicates
22584    * that the media should return to the start of the media and continue playing once
22585    * it reaches the end.
22586    *
22587    * @method Html5#setLoop
22588    * @param {boolean} loop
22589    *         - True indicates that playback should seek back to start once
22590    *           the end of a media is reached.
22591    *         - False indicates that playback should not loop back to the start when the
22592    *           end of the media is reached.
22593    *
22594    * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop}
22595    */
22596   'loop',
22597   /**
22598    * Set the value of `playsinline` from the media element. `playsinline` indicates
22599    * to the browser that non-fullscreen playback is preferred when fullscreen
22600    * playback is the native default, such as in iOS Safari.
22601    *
22602    * @method Html5#setPlaysinline
22603    * @param {boolean} playsinline
22604    *         - True indicates that the media should play inline.
22605    *         - False indicates that the media should not play inline.
22606    *
22607    * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
22608    */
22609   'playsinline'].forEach(function (prop) {
22610     Html5.prototype['set' + toTitleCase(prop)] = function (v) {
22611       this.el_[prop] = v;
22612 
22613       if (v) {
22614         this.el_.setAttribute(prop, prop);
22615       } else {
22616         this.el_.removeAttribute(prop);
22617       }
22618     };
22619   }); // Wrap native properties with a getter
22620   // The list is as followed
22621   // paused, currentTime, buffered, volume, poster, preload, error, seeking
22622   // seekable, ended, playbackRate, defaultPlaybackRate, played, networkState
22623   // readyState, videoWidth, videoHeight
22624 
22625   [
22626   /**
22627    * Get the value of `paused` from the media element. `paused` indicates whether the media element
22628    * is currently paused or not.
22629    *
22630    * @method Html5#paused
22631    * @return {boolean}
22632    *         The value of `paused` from the media element.
22633    *
22634    * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-paused}
22635    */
22636   'paused',
22637   /**
22638    * Get the value of `currentTime` from the media element. `currentTime` indicates
22639    * the current second that the media is at in playback.
22640    *
22641    * @method Html5#currentTime
22642    * @return {number}
22643    *         The value of `currentTime` from the media element.
22644    *
22645    * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-currenttime}
22646    */
22647   'currentTime',
22648   /**
22649    * Get the value of `buffered` from the media element. `buffered` is a `TimeRange`
22650    * object that represents the parts of the media that are already downloaded and
22651    * available for playback.
22652    *
22653    * @method Html5#buffered
22654    * @return {TimeRange}
22655    *         The value of `buffered` from the media element.
22656    *
22657    * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-buffered}
22658    */
22659   'buffered',
22660   /**
22661    * Get the value of `volume` from the media element. `volume` indicates
22662    * the current playback volume of audio for a media. `volume` will be a value from 0
22663    * (silent) to 1 (loudest and default).
22664    *
22665    * @method Html5#volume
22666    * @return {number}
22667    *         The value of `volume` from the media element. Value will be between 0-1.
22668    *
22669    * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-a-volume}
22670    */
22671   'volume',
22672   /**
22673    * Get the value of `poster` from the media element. `poster` indicates
22674    * that the url of an image file that can/will be shown when no media data is available.
22675    *
22676    * @method Html5#poster
22677    * @return {string}
22678    *         The value of `poster` from the media element. Value will be a url to an
22679    *         image.
22680    *
22681    * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-video-poster}
22682    */
22683   'poster',
22684   /**
22685    * Get the value of `preload` from the media element. `preload` indicates
22686    * what should download before the media is interacted with. It can have the following
22687    * values:
22688    * - none: nothing should be downloaded
22689    * - metadata: poster and the first few frames of the media may be downloaded to get
22690    *   media dimensions and other metadata
22691    * - auto: allow the media and metadata for the media to be downloaded before
22692    *    interaction
22693    *
22694    * @method Html5#preload
22695    * @return {string}
22696    *         The value of `preload` from the media element. Will be 'none', 'metadata',
22697    *         or 'auto'.
22698    *
22699    * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-preload}
22700    */
22701   'preload',
22702   /**
22703    * Get the value of the `error` from the media element. `error` indicates any
22704    * MediaError that may have occurred during playback. If error returns null there is no
22705    * current error.
22706    *
22707    * @method Html5#error
22708    * @return {MediaError|null}
22709    *         The value of `error` from the media element. Will be `MediaError` if there
22710    *         is a current error and null otherwise.
22711    *
22712    * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-error}
22713    */
22714   'error',
22715   /**
22716    * Get the value of `seeking` from the media element. `seeking` indicates whether the
22717    * media is currently seeking to a new position or not.
22718    *
22719    * @method Html5#seeking
22720    * @return {boolean}
22721    *         - The value of `seeking` from the media element.
22722    *         - True indicates that the media is currently seeking to a new position.
22723    *         - False indicates that the media is not seeking to a new position at this time.
22724    *
22725    * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-seeking}
22726    */
22727   'seeking',
22728   /**
22729    * Get the value of `seekable` from the media element. `seekable` returns a
22730    * `TimeRange` object indicating ranges of time that can currently be `seeked` to.
22731    *
22732    * @method Html5#seekable
22733    * @return {TimeRange}
22734    *         The value of `seekable` from the media element. A `TimeRange` object
22735    *         indicating the current ranges of time that can be seeked to.
22736    *
22737    * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-seekable}
22738    */
22739   'seekable',
22740   /**
22741    * Get the value of `ended` from the media element. `ended` indicates whether
22742    * the media has reached the end or not.
22743    *
22744    * @method Html5#ended
22745    * @return {boolean}
22746    *         - The value of `ended` from the media element.
22747    *         - True indicates that the media has ended.
22748    *         - False indicates that the media has not ended.
22749    *
22750    * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-ended}
22751    */
22752   'ended',
22753   /**
22754    * Get the value of `playbackRate` from the media element. `playbackRate` indicates
22755    * the rate at which the media is currently playing back. Examples:
22756    *   - if playbackRate is set to 2, media will play twice as fast.
22757    *   - if playbackRate is set to 0.5, media will play half as fast.
22758    *
22759    * @method Html5#playbackRate
22760    * @return {number}
22761    *         The value of `playbackRate` from the media element. A number indicating
22762    *         the current playback speed of the media, where 1 is normal speed.
22763    *
22764    * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
22765    */
22766   'playbackRate',
22767   /**
22768    * Get the value of `defaultPlaybackRate` from the media element. `defaultPlaybackRate` indicates
22769    * the rate at which the media is currently playing back. This value will not indicate the current
22770    * `playbackRate` after playback has started, use {@link Html5#playbackRate} for that.
22771    *
22772    * Examples:
22773    *   - if defaultPlaybackRate is set to 2, media will play twice as fast.
22774    *   - if defaultPlaybackRate is set to 0.5, media will play half as fast.
22775    *
22776    * @method Html5.prototype.defaultPlaybackRate
22777    * @return {number}
22778    *         The value of `defaultPlaybackRate` from the media element. A number indicating
22779    *         the current playback speed of the media, where 1 is normal speed.
22780    *
22781    * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
22782    */
22783   'defaultPlaybackRate',
22784   /**
22785    * Get the value of `played` from the media element. `played` returns a `TimeRange`
22786    * object representing points in the media timeline that have been played.
22787    *
22788    * @method Html5#played
22789    * @return {TimeRange}
22790    *         The value of `played` from the media element. A `TimeRange` object indicating
22791    *         the ranges of time that have been played.
22792    *
22793    * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-played}
22794    */
22795   'played',
22796   /**
22797    * Get the value of `networkState` from the media element. `networkState` indicates
22798    * the current network state. It returns an enumeration from the following list:
22799    * - 0: NETWORK_EMPTY
22800    * - 1: NETWORK_IDLE
22801    * - 2: NETWORK_LOADING
22802    * - 3: NETWORK_NO_SOURCE
22803    *
22804    * @method Html5#networkState
22805    * @return {number}
22806    *         The value of `networkState` from the media element. This will be a number
22807    *         from the list in the description.
22808    *
22809    * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-networkstate}
22810    */
22811   'networkState',
22812   /**
22813    * Get the value of `readyState` from the media element. `readyState` indicates
22814    * the current state of the media element. It returns an enumeration from the
22815    * following list:
22816    * - 0: HAVE_NOTHING
22817    * - 1: HAVE_METADATA
22818    * - 2: HAVE_CURRENT_DATA
22819    * - 3: HAVE_FUTURE_DATA
22820    * - 4: HAVE_ENOUGH_DATA
22821    *
22822    * @method Html5#readyState
22823    * @return {number}
22824    *         The value of `readyState` from the media element. This will be a number
22825    *         from the list in the description.
22826    *
22827    * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#ready-states}
22828    */
22829   'readyState',
22830   /**
22831    * Get the value of `videoWidth` from the video element. `videoWidth` indicates
22832    * the current width of the video in css pixels.
22833    *
22834    * @method Html5#videoWidth
22835    * @return {number}
22836    *         The value of `videoWidth` from the video element. This will be a number
22837    *         in css pixels.
22838    *
22839    * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-video-videowidth}
22840    */
22841   'videoWidth',
22842   /**
22843    * Get the value of `videoHeight` from the video element. `videoHeight` indicates
22844    * the current height of the video in css pixels.
22845    *
22846    * @method Html5#videoHeight
22847    * @return {number}
22848    *         The value of `videoHeight` from the video element. This will be a number
22849    *         in css pixels.
22850    *
22851    * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-video-videowidth}
22852    */
22853   'videoHeight'].forEach(function (prop) {
22854     Html5.prototype[prop] = function () {
22855       return this.el_[prop];
22856     };
22857   }); // Wrap native properties with a setter in this format:
22858   // set + toTitleCase(name)
22859   // The list is as follows:
22860   // setVolume, setSrc, setPoster, setPreload, setPlaybackRate, setDefaultPlaybackRate
22861 
22862   [
22863   /**
22864    * Set the value of `volume` on the media element. `volume` indicates the current
22865    * audio level as a percentage in decimal form. This means that 1 is 100%, 0.5 is 50%, and
22866    * so on.
22867    *
22868    * @method Html5#setVolume
22869    * @param {number} percentAsDecimal
22870    *        The volume percent as a decimal. Valid range is from 0-1.
22871    *
22872    * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-a-volume}
22873    */
22874   'volume',
22875   /**
22876    * Set the value of `src` on the media element. `src` indicates the current
22877    * {@link Tech~SourceObject} for the media.
22878    *
22879    * @method Html5#setSrc
22880    * @param {Tech~SourceObject} src
22881    *        The source object to set as the current source.
22882    *
22883    * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-src}
22884    */
22885   'src',
22886   /**
22887    * Set the value of `poster` on the media element. `poster` is the url to
22888    * an image file that can/will be shown when no media data is available.
22889    *
22890    * @method Html5#setPoster
22891    * @param {string} poster
22892    *        The url to an image that should be used as the `poster` for the media
22893    *        element.
22894    *
22895    * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-poster}
22896    */
22897   'poster',
22898   /**
22899    * Set the value of `preload` on the media element. `preload` indicates
22900    * what should download before the media is interacted with. It can have the following
22901    * values:
22902    * - none: nothing should be downloaded
22903    * - metadata: poster and the first few frames of the media may be downloaded to get
22904    *   media dimensions and other metadata
22905    * - auto: allow the media and metadata for the media to be downloaded before
22906    *    interaction
22907    *
22908    * @method Html5#setPreload
22909    * @param {string} preload
22910    *         The value of `preload` to set on the media element. Must be 'none', 'metadata',
22911    *         or 'auto'.
22912    *
22913    * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-preload}
22914    */
22915   'preload',
22916   /**
22917    * Set the value of `playbackRate` on the media element. `playbackRate` indicates
22918    * the rate at which the media should play back. Examples:
22919    *   - if playbackRate is set to 2, media will play twice as fast.
22920    *   - if playbackRate is set to 0.5, media will play half as fast.
22921    *
22922    * @method Html5#setPlaybackRate
22923    * @return {number}
22924    *         The value of `playbackRate` from the media element. A number indicating
22925    *         the current playback speed of the media, where 1 is normal speed.
22926    *
22927    * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
22928    */
22929   'playbackRate',
22930   /**
22931    * Set the value of `defaultPlaybackRate` on the media element. `defaultPlaybackRate` indicates
22932    * the rate at which the media should play back upon initial startup. Changing this value
22933    * after a video has started will do nothing. Instead you should used {@link Html5#setPlaybackRate}.
22934    *
22935    * Example Values:
22936    *   - if playbackRate is set to 2, media will play twice as fast.
22937    *   - if playbackRate is set to 0.5, media will play half as fast.
22938    *
22939    * @method Html5.prototype.setDefaultPlaybackRate
22940    * @return {number}
22941    *         The value of `defaultPlaybackRate` from the media element. A number indicating
22942    *         the current playback speed of the media, where 1 is normal speed.
22943    *
22944    * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultplaybackrate}
22945    */
22946   'defaultPlaybackRate'].forEach(function (prop) {
22947     Html5.prototype['set' + toTitleCase(prop)] = function (v) {
22948       this.el_[prop] = v;
22949     };
22950   }); // wrap native functions with a function
22951   // The list is as follows:
22952   // pause, load, play
22953 
22954   [
22955   /**
22956    * A wrapper around the media elements `pause` function. This will call the `HTML5`
22957    * media elements `pause` function.
22958    *
22959    * @method Html5#pause
22960    * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-pause}
22961    */
22962   'pause',
22963   /**
22964    * A wrapper around the media elements `load` function. This will call the `HTML5`s
22965    * media element `load` function.
22966    *
22967    * @method Html5#load
22968    * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-load}
22969    */
22970   'load',
22971   /**
22972    * A wrapper around the media elements `play` function. This will call the `HTML5`s
22973    * media element `play` function.
22974    *
22975    * @method Html5#play
22976    * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-play}
22977    */
22978   'play'].forEach(function (prop) {
22979     Html5.prototype[prop] = function () {
22980       return this.el_[prop]();
22981     };
22982   });
22983   Tech.withSourceHandlers(Html5);
22984   /**
22985    * Native source handler for Html5, simply passes the source to the media element.
22986    *
22987    * @property {Tech~SourceObject} source
22988    *        The source object
22989    *
22990    * @property {Html5} tech
22991    *        The instance of the HTML5 tech.
22992    */
22993 
22994   Html5.nativeSourceHandler = {};
22995   /**
22996    * Check if the media element can play the given mime type.
22997    *
22998    * @param {string} type
22999    *        The mimetype to check
23000    *
23001    * @return {string}
23002    *         'probably', 'maybe', or '' (empty string)
23003    */
23004 
23005   Html5.nativeSourceHandler.canPlayType = function (type) {
23006     // IE without MediaPlayer throws an error (#519)
23007     try {
23008       return Html5.TEST_VID.canPlayType(type);
23009     } catch (e) {
23010       return '';
23011     }
23012   };
23013   /**
23014    * Check if the media element can handle a source natively.
23015    *
23016    * @param {Tech~SourceObject} source
23017    *         The source object
23018    *
23019    * @param {Object} [options]
23020    *         Options to be passed to the tech.
23021    *
23022    * @return {string}
23023    *         'probably', 'maybe', or '' (empty string).
23024    */
23025 
23026 
23027   Html5.nativeSourceHandler.canHandleSource = function (source, options) {
23028     // If a type was provided we should rely on that
23029     if (source.type) {
23030       return Html5.nativeSourceHandler.canPlayType(source.type); // If no type, fall back to checking 'video/[EXTENSION]'
23031     } else if (source.src) {
23032       var ext = getFileExtension(source.src);
23033       return Html5.nativeSourceHandler.canPlayType("video/" + ext);
23034     }
23035 
23036     return '';
23037   };
23038   /**
23039    * Pass the source to the native media element.
23040    *
23041    * @param {Tech~SourceObject} source
23042    *        The source object
23043    *
23044    * @param {Html5} tech
23045    *        The instance of the Html5 tech
23046    *
23047    * @param {Object} [options]
23048    *        The options to pass to the source
23049    */
23050 
23051 
23052   Html5.nativeSourceHandler.handleSource = function (source, tech, options) {
23053     tech.setSrc(source.src);
23054   };
23055   /**
23056    * A noop for the native dispose function, as cleanup is not needed.
23057    */
23058 
23059 
23060   Html5.nativeSourceHandler.dispose = function () {}; // Register the native source handler
23061 
23062 
23063   Html5.registerSourceHandler(Html5.nativeSourceHandler);
23064   Tech.registerTech('Html5', Html5);
23065 
23066   // on the player when they happen
23067 
23068   var TECH_EVENTS_RETRIGGER = [
23069   /**
23070    * Fired while the user agent is downloading media data.
23071    *
23072    * @event Player#progress
23073    * @type {EventTarget~Event}
23074    */
23075 
23076   /**
23077    * Retrigger the `progress` event that was triggered by the {@link Tech}.
23078    *
23079    * @private
23080    * @method Player#handleTechProgress_
23081    * @fires Player#progress
23082    * @listens Tech#progress
23083    */
23084   'progress',
23085   /**
23086    * Fires when the loading of an audio/video is aborted.
23087    *
23088    * @event Player#abort
23089    * @type {EventTarget~Event}
23090    */
23091 
23092   /**
23093    * Retrigger the `abort` event that was triggered by the {@link Tech}.
23094    *
23095    * @private
23096    * @method Player#handleTechAbort_
23097    * @fires Player#abort
23098    * @listens Tech#abort
23099    */
23100   'abort',
23101   /**
23102    * Fires when the browser is intentionally not getting media data.
23103    *
23104    * @event Player#suspend
23105    * @type {EventTarget~Event}
23106    */
23107 
23108   /**
23109    * Retrigger the `suspend` event that was triggered by the {@link Tech}.
23110    *
23111    * @private
23112    * @method Player#handleTechSuspend_
23113    * @fires Player#suspend
23114    * @listens Tech#suspend
23115    */
23116   'suspend',
23117   /**
23118    * Fires when the current playlist is empty.
23119    *
23120    * @event Player#emptied
23121    * @type {EventTarget~Event}
23122    */
23123 
23124   /**
23125    * Retrigger the `emptied` event that was triggered by the {@link Tech}.
23126    *
23127    * @private
23128    * @method Player#handleTechEmptied_
23129    * @fires Player#emptied
23130    * @listens Tech#emptied
23131    */
23132   'emptied',
23133   /**
23134    * Fires when the browser is trying to get media data, but data is not available.
23135    *
23136    * @event Player#stalled
23137    * @type {EventTarget~Event}
23138    */
23139 
23140   /**
23141    * Retrigger the `stalled` event that was triggered by the {@link Tech}.
23142    *
23143    * @private
23144    * @method Player#handleTechStalled_
23145    * @fires Player#stalled
23146    * @listens Tech#stalled
23147    */
23148   'stalled',
23149   /**
23150    * Fires when the browser has loaded meta data for the audio/video.
23151    *
23152    * @event Player#loadedmetadata
23153    * @type {EventTarget~Event}
23154    */
23155 
23156   /**
23157    * Retrigger the `stalled` event that was triggered by the {@link Tech}.
23158    *
23159    * @private
23160    * @method Player#handleTechLoadedmetadata_
23161    * @fires Player#loadedmetadata
23162    * @listens Tech#loadedmetadata
23163    */
23164   'loadedmetadata',
23165   /**
23166    * Fires when the browser has loaded the current frame of the audio/video.
23167    *
23168    * @event Player#loadeddata
23169    * @type {event}
23170    */
23171 
23172   /**
23173    * Retrigger the `loadeddata` event that was triggered by the {@link Tech}.
23174    *
23175    * @private
23176    * @method Player#handleTechLoaddeddata_
23177    * @fires Player#loadeddata
23178    * @listens Tech#loadeddata
23179    */
23180   'loadeddata',
23181   /**
23182    * Fires when the current playback position has changed.
23183    *
23184    * @event Player#timeupdate
23185    * @type {event}
23186    */
23187 
23188   /**
23189    * Retrigger the `timeupdate` event that was triggered by the {@link Tech}.
23190    *
23191    * @private
23192    * @method Player#handleTechTimeUpdate_
23193    * @fires Player#timeupdate
23194    * @listens Tech#timeupdate
23195    */
23196   'timeupdate',
23197   /**
23198    * Fires when the video's intrinsic dimensions change
23199    *
23200    * @event Player#resize
23201    * @type {event}
23202    */
23203 
23204   /**
23205    * Retrigger the `resize` event that was triggered by the {@link Tech}.
23206    *
23207    * @private
23208    * @method Player#handleTechResize_
23209    * @fires Player#resize
23210    * @listens Tech#resize
23211    */
23212   'resize',
23213   /**
23214    * Fires when the volume has been changed
23215    *
23216    * @event Player#volumechange
23217    * @type {event}
23218    */
23219 
23220   /**
23221    * Retrigger the `volumechange` event that was triggered by the {@link Tech}.
23222    *
23223    * @private
23224    * @method Player#handleTechVolumechange_
23225    * @fires Player#volumechange
23226    * @listens Tech#volumechange
23227    */
23228   'volumechange',
23229   /**
23230    * Fires when the text track has been changed
23231    *
23232    * @event Player#texttrackchange
23233    * @type {event}
23234    */
23235 
23236   /**
23237    * Retrigger the `texttrackchange` event that was triggered by the {@link Tech}.
23238    *
23239    * @private
23240    * @method Player#handleTechTexttrackchange_
23241    * @fires Player#texttrackchange
23242    * @listens Tech#texttrackchange
23243    */
23244   'texttrackchange']; // events to queue when playback rate is zero
23245   // this is a hash for the sole purpose of mapping non-camel-cased event names
23246   // to camel-cased function names
23247 
23248   var TECH_EVENTS_QUEUE = {
vendor: 23,312 bytes, lines 23249-23945
23249     canplay: 'CanPlay',
23250     canplaythrough: 'CanPlayThrough',
23251     playing: 'Playing',
23252     seeked: 'Seeked'
23253   };
23254   var BREAKPOINT_ORDER = ['tiny', 'xsmall', 'small', 'medium', 'large', 'xlarge', 'huge'];
23255   var BREAKPOINT_CLASSES = {}; // grep: vjs-layout-tiny
23256   // grep: vjs-layout-x-small
23257   // grep: vjs-layout-small
23258   // grep: vjs-layout-medium
23259   // grep: vjs-layout-large
23260   // grep: vjs-layout-x-large
23261   // grep: vjs-layout-huge
23262 
23263   BREAKPOINT_ORDER.forEach(function (k) {
23264     var v = k.charAt(0) === 'x' ? "x-" + k.substring(1) : k;
23265     BREAKPOINT_CLASSES[k] = "vjs-layout-" + v;
23266   });
23267   var DEFAULT_BREAKPOINTS = {
23268     tiny: 210,
23269     xsmall: 320,
23270     small: 425,
23271     medium: 768,
23272     large: 1440,
23273     xlarge: 2560,
23274     huge: Infinity
23275   };
23276   /**
23277    * An instance of the `Player` class is created when any of the Video.js setup methods
23278    * are used to initialize a video.
23279    *
23280    * After an instance has been created it can be accessed globally in two ways:
23281    * 1. By calling `videojs('example_video_1');`
23282    * 2. By using it directly via  `videojs.players.example_video_1;`
23283    *
23284    * @extends Component
23285    */
23286 
23287   var Player =
23288   /*#__PURE__*/
23289   function (_Component) {
23290     inheritsLoose(Player, _Component);
23291 
23292     /**
23293      * Create an instance of this class.
23294      *
23295      * @param {Element} tag
23296      *        The original video DOM element used for configuring options.
23297      *
23298      * @param {Object} [options]
23299      *        Object of option names and values.
23300      *
23301      * @param {Component~ReadyCallback} [ready]
23302      *        Ready callback function.
23303      */
23304     function Player(tag, options, ready) {
23305       var _this;
23306 
23307       // Make sure tag ID exists
23308       tag.id = tag.id || options.id || "vjs_video_" + newGUID(); // Set Options
23309       // The options argument overrides options set in the video tag
23310       // which overrides globally set options.
23311       // This latter part coincides with the load order
23312       // (tag must exist before Player)
23313 
23314       options = assign(Player.getTagSettings(tag), options); // Delay the initialization of children because we need to set up
23315       // player properties first, and can't use `this` before `super()`
23316 
23317       options.initChildren = false; // Same with creating the element
23318 
23319       options.createEl = false; // don't auto mixin the evented mixin
23320 
23321       options.evented = false; // we don't want the player to report touch activity on itself
23322       // see enableTouchActivity in Component
23323 
23324       options.reportTouchActivity = false; // If language is not set, get the closest lang attribute
23325 
23326       if (!options.language) {
23327         if (typeof tag.closest === 'function') {
23328           var closest = tag.closest('[lang]');
23329 
23330           if (closest && closest.getAttribute) {
23331             options.language = closest.getAttribute('lang');
23332           }
23333         } else {
23334           var element = tag;
23335 
23336           while (element && element.nodeType === 1) {
23337             if (getAttributes(element).hasOwnProperty('lang')) {
23338               options.language = element.getAttribute('lang');
23339               break;
23340             }
23341 
23342             element = element.parentNode;
23343           }
23344         }
23345       } // Run base component initializing with new options
23346 
23347 
23348       _this = _Component.call(this, null, options, ready) || this; // Create bound methods for document listeners.
23349 
23350       _this.boundDocumentFullscreenChange_ = bind(assertThisInitialized(_this), _this.documentFullscreenChange_);
23351       _this.boundFullWindowOnEscKey_ = bind(assertThisInitialized(_this), _this.fullWindowOnEscKey); // create logger
23352 
23353       _this.log = createLogger$1(_this.id_); // Hold our own reference to fullscreen api so it can be mocked in tests
23354 
23355       _this.fsApi_ = FullscreenApi; // Tracks when a tech changes the poster
23356 
23357       _this.isPosterFromTech_ = false; // Holds callback info that gets queued when playback rate is zero
23358       // and a seek is happening
23359 
23360       _this.queuedCallbacks_ = []; // Turn off API access because we're loading a new tech that might load asynchronously
23361 
23362       _this.isReady_ = false; // Init state hasStarted_
23363 
23364       _this.hasStarted_ = false; // Init state userActive_
23365 
23366       _this.userActive_ = false; // if the global option object was accidentally blown away by
23367       // someone, bail early with an informative error
23368 
23369       if (!_this.options_ || !_this.options_.techOrder || !_this.options_.techOrder.length) {
23370         throw new Error('No techOrder specified. Did you overwrite ' + 'videojs.options instead of just changing the ' + 'properties you want to override?');
23371       } // Store the original tag used to set options
23372 
23373 
23374       _this.tag = tag; // Store the tag attributes used to restore html5 element
23375 
23376       _this.tagAttributes = tag && getAttributes(tag); // Update current language
23377 
23378       _this.language(_this.options_.language); // Update Supported Languages
23379 
23380 
23381       if (options.languages) {
23382         // Normalise player option languages to lowercase
23383         var languagesToLower = {};
23384         Object.getOwnPropertyNames(options.languages).forEach(function (name) {
23385           languagesToLower[name.toLowerCase()] = options.languages[name];
23386         });
23387         _this.languages_ = languagesToLower;
23388       } else {
23389         _this.languages_ = Player.prototype.options_.languages;
23390       }
23391 
23392       _this.resetCache_(); // Set poster
23393 
23394 
23395       _this.poster_ = options.poster || ''; // Set controls
23396 
23397       _this.controls_ = !!options.controls; // Original tag settings stored in options
23398       // now remove immediately so native controls don't flash.
23399       // May be turned back on by HTML5 tech if nativeControlsForTouch is true
23400 
23401       tag.controls = false;
23402       tag.removeAttribute('controls');
23403       _this.changingSrc_ = false;
23404       _this.playCallbacks_ = [];
23405       _this.playTerminatedQueue_ = []; // the attribute overrides the option
23406 
23407       if (tag.hasAttribute('autoplay')) {
23408         _this.autoplay(true);
23409       } else {
23410         // otherwise use the setter to validate and
23411         // set the correct value.
23412         _this.autoplay(_this.options_.autoplay);
23413       } // check plugins
23414 
23415 
23416       if (options.plugins) {
23417         Object.keys(options.plugins).forEach(function (name) {
23418           if (typeof _this[name] !== 'function') {
23419             throw new Error("plugin \"" + name + "\" does not exist");
23420           }
23421         });
23422       }
23423       /*
23424        * Store the internal state of scrubbing
23425        *
23426        * @private
23427        * @return {Boolean} True if the user is scrubbing
23428        */
23429 
23430 
23431       _this.scrubbing_ = false;
23432       _this.el_ = _this.createEl(); // Make this an evented object and use `el_` as its event bus.
23433 
23434       evented(assertThisInitialized(_this), {
23435         eventBusKey: 'el_'
23436       });
23437 
23438       if (_this.fluid_) {
23439         _this.on('playerreset', _this.updateStyleEl_);
23440       } // We also want to pass the original player options to each component and plugin
23441       // as well so they don't need to reach back into the player for options later.
23442       // We also need to do another copy of this.options_ so we don't end up with
23443       // an infinite loop.
23444 
23445 
23446       var playerOptionsCopy = mergeOptions(_this.options_); // Load plugins
23447 
23448       if (options.plugins) {
23449         Object.keys(options.plugins).forEach(function (name) {
23450           _this[name](options.plugins[name]);
23451         });
23452       }
23453 
23454       _this.options_.playerOptions = playerOptionsCopy;
23455       _this.middleware_ = [];
23456 
23457       _this.initChildren(); // Set isAudio based on whether or not an audio tag was used
23458 
23459 
23460       _this.isAudio(tag.nodeName.toLowerCase() === 'audio'); // Update controls className. Can't do this when the controls are initially
23461       // set because the element doesn't exist yet.
23462 
23463 
23464       if (_this.controls()) {
23465         _this.addClass('vjs-controls-enabled');
23466       } else {
23467         _this.addClass('vjs-controls-disabled');
23468       } // Set ARIA label and region role depending on player type
23469 
23470 
23471       _this.el_.setAttribute('role', 'region');
23472 
23473       if (_this.isAudio()) {
23474         _this.el_.setAttribute('aria-label', _this.localize('Audio Player'));
23475       } else {
23476         _this.el_.setAttribute('aria-label', _this.localize('Video Player'));
23477       }
23478 
23479       if (_this.isAudio()) {
23480         _this.addClass('vjs-audio');
23481       }
23482 
23483       if (_this.flexNotSupported_()) {
23484         _this.addClass('vjs-no-flex');
23485       } // TODO: Make this smarter. Toggle user state between touching/mousing
23486       // using events, since devices can have both touch and mouse events.
23487       // TODO: Make this check be performed again when the window switches between monitors
23488       // (See https://github.com/videojs/video.js/issues/5683)
23489 
23490 
23491       if (TOUCH_ENABLED) {
23492         _this.addClass('vjs-touch-enabled');
23493       } // iOS Safari has broken hover handling
23494 
23495 
23496       if (!IS_IOS) {
23497         _this.addClass('vjs-workinghover');
23498       } // Make player easily findable by ID
23499 
23500 
23501       Player.players[_this.id_] = assertThisInitialized(_this); // Add a major version class to aid css in plugins
23502 
23503       var majorVersion = version.split('.')[0];
23504 
23505       _this.addClass("vjs-v" + majorVersion); // When the player is first initialized, trigger activity so components
23506       // like the control bar show themselves if needed
23507 
23508 
23509       _this.userActive(true);
23510 
23511       _this.reportUserActivity();
23512 
23513       _this.one('play', _this.listenForUserActivity_);
23514 
23515       _this.on('stageclick', _this.handleStageClick_);
23516 
23517       _this.on('keydown', _this.handleKeyDown);
23518 
23519       _this.breakpoints(_this.options_.breakpoints);
23520 
23521       _this.responsive(_this.options_.responsive);
23522 
23523       return _this;
23524     }
23525     /**
23526      * Destroys the video player and does any necessary cleanup.
23527      *
23528      * This is especially helpful if you are dynamically adding and removing videos
23529      * to/from the DOM.
23530      *
23531      * @fires Player#dispose
23532      */
23533 
23534 
23535     var _proto = Player.prototype;
23536 
23537     _proto.dispose = function dispose() {
23538       var _this2 = this;
23539 
23540       /**
23541        * Called when the player is being disposed of.
23542        *
23543        * @event Player#dispose
23544        * @type {EventTarget~Event}
23545        */
23546       this.trigger('dispose'); // prevent dispose from being called twice
23547 
23548       this.off('dispose'); // Make sure all player-specific document listeners are unbound. This is
23549 
23550       off(document, this.fsApi_.fullscreenchange, this.boundDocumentFullscreenChange_);
23551       off(document, 'keydown', this.boundFullWindowOnEscKey_);
23552 
23553       if (this.styleEl_ && this.styleEl_.parentNode) {
23554         this.styleEl_.parentNode.removeChild(this.styleEl_);
23555         this.styleEl_ = null;
23556       } // Kill reference to this player
23557 
23558 
23559       Player.players[this.id_] = null;
23560 
23561       if (this.tag && this.tag.player) {
23562         this.tag.player = null;
23563       }
23564 
23565       if (this.el_ && this.el_.player) {
23566         this.el_.player = null;
23567       }
23568 
23569       if (this.tech_) {
23570         this.tech_.dispose();
23571         this.isPosterFromTech_ = false;
23572         this.poster_ = '';
23573       }
23574 
23575       if (this.playerElIngest_) {
23576         this.playerElIngest_ = null;
23577       }
23578 
23579       if (this.tag) {
23580         this.tag = null;
23581       }
23582 
23583       clearCacheForPlayer(this); // remove all event handlers for track lists
23584       // all tracks and track listeners are removed on
23585       // tech dispose
23586 
23587       ALL.names.forEach(function (name) {
23588         var props = ALL[name];
23589 
23590         var list = _this2[props.getterName](); // if it is not a native list
23591         // we have to manually remove event listeners
23592 
23593 
23594         if (list && list.off) {
23595           list.off();
23596         }
23597       }); // the actual .el_ is removed here
23598 
23599       _Component.prototype.dispose.call(this);
23600     }
23601     /**
23602      * Create the `Player`'s DOM element.
23603      *
23604      * @return {Element}
23605      *         The DOM element that gets created.
23606      */
23607     ;
23608 
23609     _proto.createEl = function createEl() {
23610       var tag = this.tag;
23611       var el;
23612       var playerElIngest = this.playerElIngest_ = tag.parentNode && tag.parentNode.hasAttribute && tag.parentNode.hasAttribute('data-vjs-player');
23613       var divEmbed = this.tag.tagName.toLowerCase() === 'video-js';
23614 
23615       if (playerElIngest) {
23616         el = this.el_ = tag.parentNode;
23617       } else if (!divEmbed) {
23618         el = this.el_ = _Component.prototype.createEl.call(this, 'div');
23619       } // Copy over all the attributes from the tag, including ID and class
23620       // ID will now reference player box, not the video tag
23621 
23622 
23623       var attrs = getAttributes(tag);
23624 
23625       if (divEmbed) {
23626         el = this.el_ = tag;
23627         tag = this.tag = document.createElement('video');
23628 
23629         while (el.children.length) {
23630           tag.appendChild(el.firstChild);
23631         }
23632 
23633         if (!hasClass(el, 'video-js')) {
23634           addClass(el, 'video-js');
23635         }
23636 
23637         el.appendChild(tag);
23638         playerElIngest = this.playerElIngest_ = el; // move properties over from our custom `video-js` element
23639         // to our new `video` element. This will move things like
23640         // `src` or `controls` that were set via js before the player
23641         // was initialized.
23642 
23643         Object.keys(el).forEach(function (k) {
23644           try {
23645             tag[k] = el[k];
23646           } catch (e) {// we got a a property like outerHTML which we can't actually copy, ignore it
23647           }
23648         });
23649       } // set tabindex to -1 to remove the video element from the focus order
23650 
23651 
23652       tag.setAttribute('tabindex', '-1');
23653       attrs.tabindex = '-1'; // Workaround for #4583 (JAWS+IE doesn't announce BPB or play button), and
23654       // for the same issue with Chrome (on Windows) with JAWS.
23655       // See https://github.com/FreedomScientific/VFO-standards-support/issues/78
23656       // Note that we can't detect if JAWS is being used, but this ARIA attribute
23657       //  doesn't change behavior of IE11 or Chrome if JAWS is not being used
23658 
23659       if (IE_VERSION || IS_CHROME && IS_WINDOWS) {
23660         tag.setAttribute('role', 'application');
23661         attrs.role = 'application';
23662       } // Remove width/height attrs from tag so CSS can make it 100% width/height
23663 
23664 
23665       tag.removeAttribute('width');
23666       tag.removeAttribute('height');
23667 
23668       if ('width' in attrs) {
23669         delete attrs.width;
23670       }
23671 
23672       if ('height' in attrs) {
23673         delete attrs.height;
23674       }
23675 
23676       Object.getOwnPropertyNames(attrs).forEach(function (attr) {
23677         // don't copy over the class attribute to the player element when we're in a div embed
23678         // the class is already set up properly in the divEmbed case
23679         // and we want to make sure that the `video-js` class doesn't get lost
23680         if (!(divEmbed && attr === 'class')) {
23681           el.setAttribute(attr, attrs[attr]);
23682         }
23683 
23684         if (divEmbed) {
23685           tag.setAttribute(attr, attrs[attr]);
23686         }
23687       }); // Update tag id/class for use as HTML5 playback tech
23688       // Might think we should do this after embedding in container so .vjs-tech class
23689       // doesn't flash 100% width/height, but class only applies with .video-js parent
23690 
23691       tag.playerId = tag.id;
23692       tag.id += '_html5_api';
23693       tag.className = 'vjs-tech'; // Make player findable on elements
23694 
23695       tag.player = el.player = this; // Default state of video is paused
23696 
23697       this.addClass('vjs-paused'); // Add a style element in the player that we'll use to set the width/height
23698       // of the player in a way that's still overrideable by CSS, just like the
23699       // video element
23700 
23701       if (window$1.VIDEOJS_NO_DYNAMIC_STYLE !== true) {
23702         this.styleEl_ = createStyleElement('vjs-styles-dimensions');
23703         var defaultsStyleEl = $('.vjs-styles-defaults');
23704         var head = $('head');
23705         head.insertBefore(this.styleEl_, defaultsStyleEl ? defaultsStyleEl.nextSibling : head.firstChild);
23706       }
23707 
23708       this.fill_ = false;
23709       this.fluid_ = false; // Pass in the width/height/aspectRatio options which will update the style el
23710 
23711       this.width(this.options_.width);
23712       this.height(this.options_.height);
23713       this.fill(this.options_.fill);
23714       this.fluid(this.options_.fluid);
23715       this.aspectRatio(this.options_.aspectRatio); // Hide any links within the video/audio tag,
23716       // because IE doesn't hide them completely from screen readers.
23717 
23718       var links = tag.getElementsByTagName('a');
23719 
23720       for (var i = 0; i < links.length; i++) {
23721         var linkEl = links.item(i);
23722         addClass(linkEl, 'vjs-hidden');
23723         linkEl.setAttribute('hidden', 'hidden');
23724       } // insertElFirst seems to cause the networkState to flicker from 3 to 2, so
23725       // keep track of the original for later so we can know if the source originally failed
23726 
23727 
23728       tag.initNetworkState_ = tag.networkState; // Wrap video tag in div (el/box) container
23729 
23730       if (tag.parentNode && !playerElIngest) {
23731         tag.parentNode.insertBefore(el, tag);
23732       } // insert the tag as the first child of the player element
23733       // then manually add it to the children array so that this.addChild
23734       // will work properly for other components
23735       //
23736       // Breaks iPhone, fixed in HTML5 setup.
23737 
23738 
23739       prependTo(tag, el);
23740       this.children_.unshift(tag); // Set lang attr on player to ensure CSS :lang() in consistent with player
23741       // if it's been set to something different to the doc
23742 
23743       this.el_.setAttribute('lang', this.language_);
23744       this.el_ = el;
23745       return el;
23746     }
23747     /**
23748      * A getter/setter for the `Player`'s width. Returns the player's configured value.
23749      * To get the current width use `currentWidth()`.
23750      *
23751      * @param {number} [value]
23752      *        The value to set the `Player`'s width to.
23753      *
23754      * @return {number}
23755      *         The current width of the `Player` when getting.
23756      */
23757     ;
23758 
23759     _proto.width = function width(value) {
23760       return this.dimension('width', value);
23761     }
23762     /**
23763      * A getter/setter for the `Player`'s height. Returns the player's configured value.
23764      * To get the current height use `currentheight()`.
23765      *
23766      * @param {number} [value]
23767      *        The value to set the `Player`'s heigth to.
23768      *
23769      * @return {number}
23770      *         The current height of the `Player` when getting.
23771      */
23772     ;
23773 
23774     _proto.height = function height(value) {
23775       return this.dimension('height', value);
23776     }
23777     /**
23778      * A getter/setter for the `Player`'s width & height.
23779      *
23780      * @param {string} dimension
23781      *        This string can be:
23782      *        - 'width'
23783      *        - 'height'
23784      *
23785      * @param {number} [value]
23786      *        Value for dimension specified in the first argument.
23787      *
23788      * @return {number}
23789      *         The dimension arguments value when getting (width/height).
23790      */
23791     ;
23792 
23793     _proto.dimension = function dimension(_dimension, value) {
23794       var privDimension = _dimension + '_';
23795 
23796       if (value === undefined) {
23797         return this[privDimension] || 0;
23798       }
23799 
23800       if (value === '' || value === 'auto') {
23801         // If an empty string is given, reset the dimension to be automatic
23802         this[privDimension] = undefined;
23803         this.updateStyleEl_();
23804         return;
23805       }
23806 
23807       var parsedVal = parseFloat(value);
23808 
23809       if (isNaN(parsedVal)) {
23810         log.error("Improper value \"" + value + "\" supplied for for " + _dimension);
23811         return;
23812       }
23813 
23814       this[privDimension] = parsedVal;
23815       this.updateStyleEl_();
23816     }
23817     /**
23818      * A getter/setter/toggler for the vjs-fluid `className` on the `Player`.
23819      *
23820      * Turning this on will turn off fill mode.
23821      *
23822      * @param {boolean} [bool]
23823      *        - A value of true adds the class.
23824      *        - A value of false removes the class.
23825      *        - No value will be a getter.
23826      *
23827      * @return {boolean|undefined}
23828      *         - The value of fluid when getting.
23829      *         - `undefined` when setting.
23830      */
23831     ;
23832 
23833     _proto.fluid = function fluid(bool) {
23834       if (bool === undefined) {
23835         return !!this.fluid_;
23836       }
23837 
23838       this.fluid_ = !!bool;
23839 
23840       if (isEvented(this)) {
23841         this.off('playerreset', this.updateStyleEl_);
23842       }
23843 
23844       if (bool) {
23845         this.addClass('vjs-fluid');
23846         this.fill(false);
23847         addEventedCallback(function () {
23848           this.on('playerreset', this.updateStyleEl_);
23849         });
23850       } else {
23851         this.removeClass('vjs-fluid');
23852       }
23853 
23854       this.updateStyleEl_();
23855     }
23856     /**
23857      * A getter/setter/toggler for the vjs-fill `className` on the `Player`.
23858      *
23859      * Turning this on will turn off fluid mode.
23860      *
23861      * @param {boolean} [bool]
23862      *        - A value of true adds the class.
23863      *        - A value of false removes the class.
23864      *        - No value will be a getter.
23865      *
23866      * @return {boolean|undefined}
23867      *         - The value of fluid when getting.
23868      *         - `undefined` when setting.
23869      */
23870     ;
23871 
23872     _proto.fill = function fill(bool) {
23873       if (bool === undefined) {
23874         return !!this.fill_;
23875       }
23876 
23877       this.fill_ = !!bool;
23878 
23879       if (bool) {
23880         this.addClass('vjs-fill');
23881         this.fluid(false);
23882       } else {
23883         this.removeClass('vjs-fill');
23884       }
23885     }
23886     /**
23887      * Get/Set the aspect ratio
23888      *
23889      * @param {string} [ratio]
23890      *        Aspect ratio for player
23891      *
23892      * @return {string|undefined}
23893      *         returns the current aspect ratio when getting
23894      */
23895 
23896     /**
23897      * A getter/setter for the `Player`'s aspect ratio.
23898      *
23899      * @param {string} [ratio]
23900      *        The value to set the `Player's aspect ratio to.
23901      *
23902      * @return {string|undefined}
23903      *         - The current aspect ratio of the `Player` when getting.
23904      *         - undefined when setting
23905      */
23906     ;
23907 
23908     _proto.aspectRatio = function aspectRatio(ratio) {
23909       if (ratio === undefined) {
23910         return this.aspectRatio_;
23911       } // Check for width:height format
23912 
23913 
23914       if (!/^\d+\:\d+$/.test(ratio)) {
23915         throw new Error('Improper value supplied for aspect ratio. The format should be width:height, for example 16:9.');
23916       }
23917 
23918       this.aspectRatio_ = ratio; // We're assuming if you set an aspect ratio you want fluid mode,
23919       // because in fixed mode you could calculate width and height yourself.
23920 
23921       this.fluid(true);
23922       this.updateStyleEl_();
23923     }
23924     /**
23925      * Update styles of the `Player` element (height, width and aspect ratio).
23926      *
23927      * @private
23928      * @listens Tech#loadedmetadata
23929      */
23930     ;
23931 
23932     _proto.updateStyleEl_ = function updateStyleEl_() {
23933       if (window$1.VIDEOJS_NO_DYNAMIC_STYLE === true) {
23934         var _width = typeof this.width_ === 'number' ? this.width_ : this.options_.width;
23935 
23936         var _height = typeof this.height_ === 'number' ? this.height_ : this.options_.height;
23937 
23938         var techEl = this.tech_ && this.tech_.el();
23939 
23940         if (techEl) {
23941           if (_width >= 0) {
23942             techEl.width = _width;
23943           }
23944 
23945           if (_height >= 0) {
vendor: 8,710 bytes, lines 23946-24155
23946             techEl.height = _height;
23947           }
23948         }
23949 
23950         return;
23951       }
23952 
23953       var width;
23954       var height;
23955       var aspectRatio;
23956       var idClass; // The aspect ratio is either used directly or to calculate width and height.
23957 
23958       if (this.aspectRatio_ !== undefined && this.aspectRatio_ !== 'auto') {
23959         // Use any aspectRatio that's been specifically set
23960         aspectRatio = this.aspectRatio_;
23961       } else if (this.videoWidth() > 0) {
23962         // Otherwise try to get the aspect ratio from the video metadata
23963         aspectRatio = this.videoWidth() + ':' + this.videoHeight();
23964       } else {
23965         // Or use a default. The video element's is 2:1, but 16:9 is more common.
23966         aspectRatio = '16:9';
23967       } // Get the ratio as a decimal we can use to calculate dimensions
23968 
23969 
23970       var ratioParts = aspectRatio.split(':');
23971       var ratioMultiplier = ratioParts[1] / ratioParts[0];
23972 
23973       if (this.width_ !== undefined) {
23974         // Use any width that's been specifically set
23975         width = this.width_;
23976       } else if (this.height_ !== undefined) {
23977         // Or calulate the width from the aspect ratio if a height has been set
23978         width = this.height_ / ratioMultiplier;
23979       } else {
23980         // Or use the video's metadata, or use the video el's default of 300
23981         width = this.videoWidth() || 300;
23982       }
23983 
23984       if (this.height_ !== undefined) {
23985         // Use any height that's been specifically set
23986         height = this.height_;
23987       } else {
23988         // Otherwise calculate the height from the ratio and the width
23989         height = width * ratioMultiplier;
23990       } // Ensure the CSS class is valid by starting with an alpha character
23991 
23992 
23993       if (/^[^a-zA-Z]/.test(this.id())) {
23994         idClass = 'dimensions-' + this.id();
23995       } else {
23996         idClass = this.id() + '-dimensions';
23997       } // Ensure the right class is still on the player for the style element
23998 
23999 
24000       this.addClass(idClass);
24001       setTextContent(this.styleEl_, "\n      ." + idClass + " {\n        width: " + width + "px;\n        height: " + height + "px;\n      }\n\n      ." + idClass + ".vjs-fluid {\n        padding-top: " + ratioMultiplier * 100 + "%;\n      }\n    ");
24002     }
24003     /**
24004      * Load/Create an instance of playback {@link Tech} including element
24005      * and API methods. Then append the `Tech` element in `Player` as a child.
24006      *
24007      * @param {string} techName
24008      *        name of the playback technology
24009      *
24010      * @param {string} source
24011      *        video source
24012      *
24013      * @private
24014      */
24015     ;
24016 
24017     _proto.loadTech_ = function loadTech_(techName, source) {
24018       var _this3 = this;
24019 
24020       // Pause and remove current playback technology
24021       if (this.tech_) {
24022         this.unloadTech_();
24023       }
24024 
24025       var titleTechName = toTitleCase(techName);
24026       var camelTechName = techName.charAt(0).toLowerCase() + techName.slice(1); // get rid of the HTML5 video tag as soon as we are using another tech
24027 
24028       if (titleTechName !== 'Html5' && this.tag) {
24029         Tech.getTech('Html5').disposeMediaElement(this.tag);
24030         this.tag.player = null;
24031         this.tag = null;
24032       }
24033 
24034       this.techName_ = titleTechName; // Turn off API access because we're loading a new tech that might load asynchronously
24035 
24036       this.isReady_ = false; // if autoplay is a string we pass false to the tech
24037       // because the player is going to handle autoplay on `loadstart`
24038 
24039       var autoplay = typeof this.autoplay() === 'string' ? false : this.autoplay(); // Grab tech-specific options from player options and add source and parent element to use.
24040 
24041       var techOptions = {
24042         source: source,
24043         autoplay: autoplay,
24044         'nativeControlsForTouch': this.options_.nativeControlsForTouch,
24045         'playerId': this.id(),
24046         'techId': this.id() + "_" + camelTechName + "_api",
24047         'playsinline': this.options_.playsinline,
24048         'preload': this.options_.preload,
24049         'loop': this.options_.loop,
24050         'muted': this.options_.muted,
24051         'poster': this.poster(),
24052         'language': this.language(),
24053         'playerElIngest': this.playerElIngest_ || false,
24054         'vtt.js': this.options_['vtt.js'],
24055         'canOverridePoster': !!this.options_.techCanOverridePoster,
24056         'enableSourceset': this.options_.enableSourceset,
24057         'Promise': this.options_.Promise
24058       };
24059       ALL.names.forEach(function (name) {
24060         var props = ALL[name];
24061         techOptions[props.getterName] = _this3[props.privateName];
24062       });
24063       assign(techOptions, this.options_[titleTechName]);
24064       assign(techOptions, this.options_[camelTechName]);
24065       assign(techOptions, this.options_[techName.toLowerCase()]);
24066 
24067       if (this.tag) {
24068         techOptions.tag = this.tag;
24069       }
24070 
24071       if (source && source.src === this.cache_.src && this.cache_.currentTime > 0) {
24072         techOptions.startTime = this.cache_.currentTime;
24073       } // Initialize tech instance
24074 
24075 
24076       var TechClass = Tech.getTech(techName);
24077 
24078       if (!TechClass) {
24079         throw new Error("No Tech named '" + titleTechName + "' exists! '" + titleTechName + "' should be registered using videojs.registerTech()'");
24080       }
24081 
24082       this.tech_ = new TechClass(techOptions); // player.triggerReady is always async, so don't need this to be async
24083 
24084       this.tech_.ready(bind(this, this.handleTechReady_), true);
24085       textTrackConverter.jsonToTextTracks(this.textTracksJson_ || [], this.tech_); // Listen to all HTML5-defined events and trigger them on the player
24086 
24087       TECH_EVENTS_RETRIGGER.forEach(function (event) {
24088         _this3.on(_this3.tech_, event, _this3["handleTech" + toTitleCase(event) + "_"]);
24089       });
24090       Object.keys(TECH_EVENTS_QUEUE).forEach(function (event) {
24091         _this3.on(_this3.tech_, event, function (eventObj) {
24092           if (_this3.tech_.playbackRate() === 0 && _this3.tech_.seeking()) {
24093             _this3.queuedCallbacks_.push({
24094               callback: _this3["handleTech" + TECH_EVENTS_QUEUE[event] + "_"].bind(_this3),
24095               event: eventObj
24096             });
24097 
24098             return;
24099           }
24100 
24101           _this3["handleTech" + TECH_EVENTS_QUEUE[event] + "_"](eventObj);
24102         });
24103       });
24104       this.on(this.tech_, 'loadstart', this.handleTechLoadStart_);
24105       this.on(this.tech_, 'sourceset', this.handleTechSourceset_);
24106       this.on(this.tech_, 'waiting', this.handleTechWaiting_);
24107       this.on(this.tech_, 'ended', this.handleTechEnded_);
24108       this.on(this.tech_, 'seeking', this.handleTechSeeking_);
24109       this.on(this.tech_, 'play', this.handleTechPlay_);
24110       this.on(this.tech_, 'firstplay', this.handleTechFirstPlay_);
24111       this.on(this.tech_, 'pause', this.handleTechPause_);
24112       this.on(this.tech_, 'durationchange', this.handleTechDurationChange_);
24113       this.on(this.tech_, 'fullscreenchange', this.handleTechFullscreenChange_);
24114       this.on(this.tech_, 'enterpictureinpicture', this.handleTechEnterPictureInPicture_);
24115       this.on(this.tech_, 'leavepictureinpicture', this.handleTechLeavePictureInPicture_);
24116       this.on(this.tech_, 'error', this.handleTechError_);
24117       this.on(this.tech_, 'loadedmetadata', this.updateStyleEl_);
24118       this.on(this.tech_, 'posterchange', this.handleTechPosterChange_);
24119       this.on(this.tech_, 'textdata', this.handleTechTextData_);
24120       this.on(this.tech_, 'ratechange', this.handleTechRateChange_);
24121       this.usingNativeControls(this.techGet_('controls'));
24122 
24123       if (this.controls() && !this.usingNativeControls()) {
24124         this.addTechControlsListeners_();
24125       } // Add the tech element in the DOM if it was not already there
24126       // Make sure to not insert the original video element if using Html5
24127 
24128 
24129       if (this.tech_.el().parentNode !== this.el() && (titleTechName !== 'Html5' || !this.tag)) {
24130         prependTo(this.tech_.el(), this.el());
24131       } // Get rid of the original video tag reference after the first tech is loaded
24132 
24133 
24134       if (this.tag) {
24135         this.tag.player = null;
24136         this.tag = null;
24137       }
24138     }
24139     /**
24140      * Unload and dispose of the current playback {@link Tech}.
24141      *
24142      * @private
24143      */
24144     ;
24145 
24146     _proto.unloadTech_ = function unloadTech_() {
24147       var _this4 = this;
24148 
24149       // Save the current text tracks so that we can reuse the same text tracks with the next tech
24150       ALL.names.forEach(function (name) {
24151         var props = ALL[name];
24152         _this4[props.privateName] = _this4[props.getterName]();
24153       });
24154       this.textTracksJson_ = textTrackConverter.textTracksToJson(this.tech_);
24155       this.isReady_ = false;
vendor: 10,042 bytes, lines 24156-24442
24156       this.tech_.dispose();
24157       this.tech_ = false;
24158 
24159       if (this.isPosterFromTech_) {
24160         this.poster_ = '';
24161         this.trigger('posterchange');
24162       }
24163 
24164       this.isPosterFromTech_ = false;
24165     }
24166     /**
24167      * Return a reference to the current {@link Tech}.
24168      * It will print a warning by default about the danger of using the tech directly
24169      * but any argument that is passed in will silence the warning.
24170      *
24171      * @param {*} [safety]
24172      *        Anything passed in to silence the warning
24173      *
24174      * @return {Tech}
24175      *         The Tech
24176      */
24177     ;
24178 
24179     _proto.tech = function tech(safety) {
24180       if (safety === undefined) {
24181         log.warn('Using the tech directly can be dangerous. I hope you know what you\'re doing.\n' + 'See https://github.com/videojs/video.js/issues/2617 for more info.\n');
24182       }
24183 
24184       return this.tech_;
24185     }
24186     /**
24187      * Set up click and touch listeners for the playback element
24188      *
24189      * - On desktops: a click on the video itself will toggle playback
24190      * - On mobile devices: a click on the video toggles controls
24191      *   which is done by toggling the user state between active and
24192      *   inactive
24193      * - A tap can signal that a user has become active or has become inactive
24194      *   e.g. a quick tap on an iPhone movie should reveal the controls. Another
24195      *   quick tap should hide them again (signaling the user is in an inactive
24196      *   viewing state)
24197      * - In addition to this, we still want the user to be considered inactive after
24198      *   a few seconds of inactivity.
24199      *
24200      * > Note: the only part of iOS interaction we can't mimic with this setup
24201      * is a touch and hold on the video element counting as activity in order to
24202      * keep the controls showing, but that shouldn't be an issue. A touch and hold
24203      * on any controls will still keep the user active
24204      *
24205      * @private
24206      */
24207     ;
24208 
24209     _proto.addTechControlsListeners_ = function addTechControlsListeners_() {
24210       // Make sure to remove all the previous listeners in case we are called multiple times.
24211       this.removeTechControlsListeners_(); // Some browsers (Chrome & IE) don't trigger a click on a flash swf, but do
24212       // trigger mousedown/up.
24213       // http://stackoverflow.com/questions/1444562/javascript-onclick-event-over-flash-object
24214       // Any touch events are set to block the mousedown event from happening
24215 
24216       this.on(this.tech_, 'mouseup', this.handleTechClick_);
24217       this.on(this.tech_, 'dblclick', this.handleTechDoubleClick_); // If the controls were hidden we don't want that to change without a tap event
24218       // so we'll check if the controls were already showing before reporting user
24219       // activity
24220 
24221       this.on(this.tech_, 'touchstart', this.handleTechTouchStart_);
24222       this.on(this.tech_, 'touchmove', this.handleTechTouchMove_);
24223       this.on(this.tech_, 'touchend', this.handleTechTouchEnd_); // The tap listener needs to come after the touchend listener because the tap
24224       // listener cancels out any reportedUserActivity when setting userActive(false)
24225 
24226       this.on(this.tech_, 'tap', this.handleTechTap_);
24227     }
24228     /**
24229      * Remove the listeners used for click and tap controls. This is needed for
24230      * toggling to controls disabled, where a tap/touch should do nothing.
24231      *
24232      * @private
24233      */
24234     ;
24235 
24236     _proto.removeTechControlsListeners_ = function removeTechControlsListeners_() {
24237       // We don't want to just use `this.off()` because there might be other needed
24238       // listeners added by techs that extend this.
24239       this.off(this.tech_, 'tap', this.handleTechTap_);
24240       this.off(this.tech_, 'touchstart', this.handleTechTouchStart_);
24241       this.off(this.tech_, 'touchmove', this.handleTechTouchMove_);
24242       this.off(this.tech_, 'touchend', this.handleTechTouchEnd_);
24243       this.off(this.tech_, 'mouseup', this.handleTechClick_);
24244       this.off(this.tech_, 'dblclick', this.handleTechDoubleClick_);
24245     }
24246     /**
24247      * Player waits for the tech to be ready
24248      *
24249      * @private
24250      */
24251     ;
24252 
24253     _proto.handleTechReady_ = function handleTechReady_() {
24254       this.triggerReady(); // Keep the same volume as before
24255 
24256       if (this.cache_.volume) {
24257         this.techCall_('setVolume', this.cache_.volume);
24258       } // Look if the tech found a higher resolution poster while loading
24259 
24260 
24261       this.handleTechPosterChange_(); // Update the duration if available
24262 
24263       this.handleTechDurationChange_();
24264     }
24265     /**
24266      * Retrigger the `loadstart` event that was triggered by the {@link Tech}. This
24267      * function will also trigger {@link Player#firstplay} if it is the first loadstart
24268      * for a video.
24269      *
24270      * @fires Player#loadstart
24271      * @fires Player#firstplay
24272      * @listens Tech#loadstart
24273      * @private
24274      */
24275     ;
24276 
24277     _proto.handleTechLoadStart_ = function handleTechLoadStart_() {
24278       // TODO: Update to use `emptied` event instead. See #1277.
24279       this.removeClass('vjs-ended');
24280       this.removeClass('vjs-seeking'); // reset the error state
24281 
24282       this.error(null); // Update the duration
24283 
24284       this.handleTechDurationChange_(); // If it's already playing we want to trigger a firstplay event now.
24285       // The firstplay event relies on both the play and loadstart events
24286       // which can happen in any order for a new source
24287 
24288       if (!this.paused()) {
24289         /**
24290          * Fired when the user agent begins looking for media data
24291          *
24292          * @event Player#loadstart
24293          * @type {EventTarget~Event}
24294          */
24295         this.trigger('loadstart');
24296         this.trigger('firstplay');
24297       } else {
24298         // reset the hasStarted state
24299         this.hasStarted(false);
24300         this.trigger('loadstart');
24301       } // autoplay happens after loadstart for the browser,
24302       // so we mimic that behavior
24303 
24304 
24305       this.manualAutoplay_(this.autoplay());
24306     }
24307     /**
24308      * Handle autoplay string values, rather than the typical boolean
24309      * values that should be handled by the tech. Note that this is not
24310      * part of any specification. Valid values and what they do can be
24311      * found on the autoplay getter at Player#autoplay()
24312      */
24313     ;
24314 
24315     _proto.manualAutoplay_ = function manualAutoplay_(type) {
24316       var _this5 = this;
24317 
24318       if (!this.tech_ || typeof type !== 'string') {
24319         return;
24320       }
24321 
24322       var muted = function muted() {
24323         var previouslyMuted = _this5.muted();
24324 
24325         _this5.muted(true);
24326 
24327         var restoreMuted = function restoreMuted() {
24328           _this5.muted(previouslyMuted);
24329         }; // restore muted on play terminatation
24330 
24331 
24332         _this5.playTerminatedQueue_.push(restoreMuted);
24333 
24334         var mutedPromise = _this5.play();
24335 
24336         if (!isPromise(mutedPromise)) {
24337           return;
24338         }
24339 
24340         return mutedPromise["catch"](restoreMuted);
24341       };
24342 
24343       var promise; // if muted defaults to true
24344       // the only thing we can do is call play
24345 
24346       if (type === 'any' && this.muted() !== true) {
24347         promise = this.play();
24348 
24349         if (isPromise(promise)) {
24350           promise = promise["catch"](muted);
24351         }
24352       } else if (type === 'muted' && this.muted() !== true) {
24353         promise = muted();
24354       } else {
24355         promise = this.play();
24356       }
24357 
24358       if (!isPromise(promise)) {
24359         return;
24360       }
24361 
24362       return promise.then(function () {
24363         _this5.trigger({
24364           type: 'autoplay-success',
24365           autoplay: type
24366         });
24367       })["catch"](function (e) {
24368         _this5.trigger({
24369           type: 'autoplay-failure',
24370           autoplay: type
24371         });
24372       });
24373     }
24374     /**
24375      * Update the internal source caches so that we return the correct source from
24376      * `src()`, `currentSource()`, and `currentSources()`.
24377      *
24378      * > Note: `currentSources` will not be updated if the source that is passed in exists
24379      *         in the current `currentSources` cache.
24380      *
24381      *
24382      * @param {Tech~SourceObject} srcObj
24383      *        A string or object source to update our caches to.
24384      */
24385     ;
24386 
24387     _proto.updateSourceCaches_ = function updateSourceCaches_(srcObj) {
24388       if (srcObj === void 0) {
24389         srcObj = '';
24390       }
24391 
24392       var src = srcObj;
24393       var type = '';
24394 
24395       if (typeof src !== 'string') {
24396         src = srcObj.src;
24397         type = srcObj.type;
24398       } // make sure all the caches are set to default values
24399       // to prevent null checking
24400 
24401 
24402       this.cache_.source = this.cache_.source || {};
24403       this.cache_.sources = this.cache_.sources || []; // try to get the type of the src that was passed in
24404 
24405       if (src && !type) {
24406         type = findMimetype(this, src);
24407       } // update `currentSource` cache always
24408 
24409 
24410       this.cache_.source = mergeOptions({}, srcObj, {
24411         src: src,
24412         type: type
24413       });
24414       var matchingSources = this.cache_.sources.filter(function (s) {
24415         return s.src && s.src === src;
24416       });
24417       var sourceElSources = [];
24418       var sourceEls = this.$$('source');
24419       var matchingSourceEls = [];
24420 
24421       for (var i = 0; i < sourceEls.length; i++) {
24422         var sourceObj = getAttributes(sourceEls[i]);
24423         sourceElSources.push(sourceObj);
24424 
24425         if (sourceObj.src && sourceObj.src === src) {
24426           matchingSourceEls.push(sourceObj.src);
24427         }
24428       } // if we have matching source els but not matching sources
24429       // the current source cache is not up to date
24430 
24431 
24432       if (matchingSourceEls.length && !matchingSources.length) {
24433         this.cache_.sources = sourceElSources; // if we don't have matching source or source els set the
24434         // sources cache to the `currentSource` cache
24435       } else if (!matchingSources.length) {
24436         this.cache_.sources = [this.cache_.source];
24437       } // update the tech `src` cache
24438 
24439 
24440       this.cache_.src = src;
24441     }
24442     /**
24443      * *EXPERIMENTAL* Fired when the source is set or changed on the {@link Tech}
24444      * causing the media element to reload.
24445      *
24446      * It will fire for the initial source and each subsequent source.
24447      * This event is a custom event from Video.js and is triggered by the {@link Tech}.
24448      *
24449      * The event object for this event contains a `src` property that will contain the source
24450      * that was available when the event was triggered. This is generally only necessary if Video.js
24451      * is switching techs while the source was being changed.
24452      *
24453      * It is also fired when `load` is called on the player (or media element)
24454      * because the {@link https://html.spec.whatwg.org/multipage/media.html#dom-media-load|specification for `load`}
24455      * says that the resource selection algorithm needs to be aborted and restarted.
24456      * In this case, it is very likely that the `src` property will be set to the
24457      * empty string `""` to indicate we do not know what the source will be but
24458      * that it is changing.
24459      *
24460      * *This event is currently still experimental and may change in minor releases.*
24461      * __To use this, pass `enableSourceset` option to the player.__
24462      *
24463      * @event Player#sourceset
24464      * @type {EventTarget~Event}
24465      * @prop {string} src
24466      *                The source url available when the `sourceset` was triggered.
24467      *                It will be an empty string if we cannot know what the source is
24468      *                but know that the source will change.
24469      */
24470 
24471     /**
24472      * Retrigger the `sourceset` event that was triggered by the {@link Tech}.
24473      *
24474      * @fires Player#sourceset
24475      * @listens Tech#sourceset
24476      * @private
24477      */
24478     ;
24479 
24480     _proto.handleTechSourceset_ = function handleTechSourceset_(event) {
24481       var _this6 = this;
24482 
24483       // only update the source cache when the source
24484       // was not updated using the player api
24485       if (!this.changingSrc_) {
24486         var updateSourceCaches = function updateSourceCaches(src) {
24487           return _this6.updateSourceCaches_(src);
24488         };
24489 
24490         var playerSrc = this.currentSource().src;
24491         var eventSrc = event.src; // if we have a playerSrc that is not a blob, and a tech src that is a blob
24492 
24493         if (playerSrc && !/^blob:/.test(playerSrc) && /^blob:/.test(eventSrc)) {
24494           // if both the tech source and the player source were updated we assume
24495           // something like @videojs/http-streaming did the sourceset and skip updating the source cache.
24496           if (!this.lastSource_ || this.lastSource_.tech !== eventSrc && this.lastSource_.player !== playerSrc) {
24497             updateSourceCaches = function updateSourceCaches() {};
24498           }
24499         } // update the source to the intial source right away
24500         // in some cases this will be empty string
24501 
24502 
24503         updateSourceCaches(eventSrc); // if the `sourceset` `src` was an empty string
24504         // wait for a `loadstart` to update the cache to `currentSrc`.
24505         // If a sourceset happens before a `loadstart`, we reset the state
24506 
24507         if (!event.src) {
24508           this.tech_.any(['sourceset', 'loadstart'], function (e) {
24509             // if a sourceset happens before a `loadstart` there
24510             // is nothing to do as this `handleTechSourceset_`
24511             // will be called again and this will be handled there.
24512             if (e.type === 'sourceset') {
24513               return;
24514             }
24515 
24516             var techSrc = _this6.techGet('currentSrc');
24517 
24518             _this6.lastSource_.tech = techSrc;
24519 
24520             _this6.updateSourceCaches_(techSrc);
24521           });
24522         }
24523       }
24524 
24525       this.lastSource_ = {
24526         player: this.currentSource().src,
24527         tech: event.src
24528       };
24529       this.trigger({
24530         src: event.src,
24531         type: 'sourceset'
24532       });
24533     }
24534     /**
24535      * Add/remove the vjs-has-started class
24536      *
24537      * @fires Player#firstplay
24538      *
24539      * @param {boolean} request
24540      *        - true: adds the class
24541      *        - false: remove the class
24542      *
24543      * @return {boolean}
24544      *         the boolean value of hasStarted_
24545      */
24546     ;
24547 
24548     _proto.hasStarted = function hasStarted(request) {
24549       if (request === undefined) {
24550         // act as getter, if we have no request to change
24551         return this.hasStarted_;
24552       }
24553 
24554       if (request === this.hasStarted_) {
24555         return;
24556       }
24557 
24558       this.hasStarted_ = request;
24559 
24560       if (this.hasStarted_) {
24561         this.addClass('vjs-has-started');
24562         this.trigger('firstplay');
24563       } else {
24564         this.removeClass('vjs-has-started');
24565       }
24566     }
24567     /**
24568      * Fired whenever the media begins or resumes playback
24569      *
24570      * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-play}
24571      * @fires Player#play
24572      * @listens Tech#play
24573      * @private
24574      */
24575     ;
24576 
24577     _proto.handleTechPlay_ = function handleTechPlay_() {
24578       this.removeClass('vjs-ended');
24579       this.removeClass('vjs-paused');
24580       this.addClass('vjs-playing'); // hide the poster when the user hits play
24581 
24582       this.hasStarted(true);
24583       /**
24584        * Triggered whenever an {@link Tech#play} event happens. Indicates that
24585        * playback has started or resumed.
24586        *
24587        * @event Player#play
24588        * @type {EventTarget~Event}
24589        */
24590 
24591       this.trigger('play');
24592     }
24593     /**
24594      * Retrigger the `ratechange` event that was triggered by the {@link Tech}.
24595      *
24596      * If there were any events queued while the playback rate was zero, fire
24597      * those events now.
24598      *
24599      * @private
24600      * @method Player#handleTechRateChange_
24601      * @fires Player#ratechange
24602      * @listens Tech#ratechange
24603      */
24604     ;
24605 
24606     _proto.handleTechRateChange_ = function handleTechRateChange_() {
24607       if (this.tech_.playbackRate() > 0 && this.cache_.lastPlaybackRate === 0) {
24608         this.queuedCallbacks_.forEach(function (queued) {
24609           return queued.callback(queued.event);
24610         });
24611         this.queuedCallbacks_ = [];
24612       }
24613 
24614       this.cache_.lastPlaybackRate = this.tech_.playbackRate();
24615       /**
24616        * Fires when the playing speed of the audio/video is changed
24617        *
24618        * @event Player#ratechange
24619        * @type {event}
24620        */
24621 
24622       this.trigger('ratechange');
24623     }
24624     /**
24625      * Retrigger the `waiting` event that was triggered by the {@link Tech}.
24626      *
24627      * @fires Player#waiting
24628      * @listens Tech#waiting
24629      * @private
24630      */
24631     ;
24632 
24633     _proto.handleTechWaiting_ = function handleTechWaiting_() {
24634       var _this7 = this;
24635 
24636       this.addClass('vjs-waiting');
24637       /**
24638        * A readyState change on the DOM element has caused playback to stop.
24639        *
24640        * @event Player#waiting
24641        * @type {EventTarget~Event}
24642        */
24643 
24644       this.trigger('waiting'); // Browsers may emit a timeupdate event after a waiting event. In order to prevent
24645       // premature removal of the waiting class, wait for the time to change.
24646 
24647       var timeWhenWaiting = this.currentTime();
24648 
24649       var timeUpdateListener = function timeUpdateListener() {
24650         if (timeWhenWaiting !== _this7.currentTime()) {
24651           _this7.removeClass('vjs-waiting');
24652 
24653           _this7.off('timeupdate', timeUpdateListener);
24654         }
24655       };
24656 
24657       this.on('timeupdate', timeUpdateListener);
24658     }
24659     /**
24660      * Retrigger the `canplay` event that was triggered by the {@link Tech}.
24661      * > Note: This is not consistent between browsers. See #1351
24662      *
24663      * @fires Player#canplay
24664      * @listens Tech#canplay
24665      * @private
24666      */
24667     ;
24668 
24669     _proto.handleTechCanPlay_ = function handleTechCanPlay_() {
24670       this.removeClass('vjs-waiting');
24671       /**
24672        * The media has a readyState of HAVE_FUTURE_DATA or greater.
24673        *
24674        * @event Player#canplay
24675        * @type {EventTarget~Event}
24676        */
24677 
24678       this.trigger('canplay');
24679     }
24680     /**
24681      * Retrigger the `canplaythrough` event that was triggered by the {@link Tech}.
24682      *
24683      * @fires Player#canplaythrough
24684      * @listens Tech#canplaythrough
24685      * @private
24686      */
24687     ;
24688 
24689     _proto.handleTechCanPlayThrough_ = function handleTechCanPlayThrough_() {
24690       this.removeClass('vjs-waiting');
24691       /**
24692        * The media has a readyState of HAVE_ENOUGH_DATA or greater. This means that the
24693        * entire media file can be played without buffering.
24694        *
24695        * @event Player#canplaythrough
24696        * @type {EventTarget~Event}
24697        */
24698 
24699       this.trigger('canplaythrough');
24700     }
24701     /**
24702      * Retrigger the `playing` event that was triggered by the {@link Tech}.
24703      *
24704      * @fires Player#playing
24705      * @listens Tech#playing
24706      * @private
24707      */
24708     ;
24709 
24710     _proto.handleTechPlaying_ = function handleTechPlaying_() {
24711       this.removeClass('vjs-waiting');
24712       /**
24713        * The media is no longer blocked from playback, and has started playing.
24714        *
24715        * @event Player#playing
24716        * @type {EventTarget~Event}
24717        */
24718 
24719       this.trigger('playing');
24720     }
24721     /**
24722      * Retrigger the `seeking` event that was triggered by the {@link Tech}.
24723      *
24724      * @fires Player#seeking
24725      * @listens Tech#seeking
24726      * @private
24727      */
24728     ;
24729 
24730     _proto.handleTechSeeking_ = function handleTechSeeking_() {
24731       this.addClass('vjs-seeking');
24732       /**
24733        * Fired whenever the player is jumping to a new time
24734        *
24735        * @event Player#seeking
24736        * @type {EventTarget~Event}
24737        */
24738 
24739       this.trigger('seeking');
24740     }
24741     /**
24742      * Retrigger the `seeked` event that was triggered by the {@link Tech}.
24743      *
24744      * @fires Player#seeked
24745      * @listens Tech#seeked
24746      * @private
24747      */
24748     ;
24749 
24750     _proto.handleTechSeeked_ = function handleTechSeeked_() {
24751       this.removeClass('vjs-seeking');
24752       this.removeClass('vjs-ended');
24753       /**
24754        * Fired when the player has finished jumping to a new time
24755        *
24756        * @event Player#seeked
24757        * @type {EventTarget~Event}
24758        */
24759 
24760       this.trigger('seeked');
24761     }
24762     /**
24763      * Retrigger the `firstplay` event that was triggered by the {@link Tech}.
24764      *
24765      * @fires Player#firstplay
24766      * @listens Tech#firstplay
24767      * @deprecated As of 6.0 firstplay event is deprecated.
24768      *             As of 6.0 passing the `starttime` option to the player and the firstplay event are deprecated.
24769      * @private
24770      */
24771     ;
24772 
24773     _proto.handleTechFirstPlay_ = function handleTechFirstPlay_() {
24774       // If the first starttime attribute is specified
24775       // then we will start at the given offset in seconds
24776       if (this.options_.starttime) {
24777         log.warn('Passing the `starttime` option to the player will be deprecated 
vendor: 26,018 bytes, lines 24777-25653
24777in 6.0');
24778         this.currentTime(this.options_.starttime);
24779       }
24780 
24781       this.addClass('vjs-has-started');
24782       /**
24783        * Fired the first time a video is played. Not part of the HLS spec, and this is
24784        * probably not the best implementation yet, so use sparingly. If you don't have a
24785        * reason to prevent playback, use `myPlayer.one('play');` instead.
24786        *
24787        * @event Player#firstplay
24788        * @deprecated As of 6.0 firstplay event is deprecated.
24789        * @type {EventTarget~Event}
24790        */
24791 
24792       this.trigger('firstplay');
24793     }
24794     /**
24795      * Retrigger the `pause` event that was triggered by the {@link Tech}.
24796      *
24797      * @fires Player#pause
24798      * @listens Tech#pause
24799      * @private
24800      */
24801     ;
24802 
24803     _proto.handleTechPause_ = function handleTechPause_() {
24804       this.removeClass('vjs-playing');
24805       this.addClass('vjs-paused');
24806       /**
24807        * Fired whenever the media has been paused
24808        *
24809        * @event Player#pause
24810        * @type {EventTarget~Event}
24811        */
24812 
24813       this.trigger('pause');
24814     }
24815     /**
24816      * Retrigger the `ended` event that was triggered by the {@link Tech}.
24817      *
24818      * @fires Player#ended
24819      * @listens Tech#ended
24820      * @private
24821      */
24822     ;
24823 
24824     _proto.handleTechEnded_ = function handleTechEnded_() {
24825       this.addClass('vjs-ended');
24826 
24827       if (this.options_.loop) {
24828         this.currentTime(0);
24829         this.play();
24830       } else if (!this.paused()) {
24831         this.pause();
24832       }
24833       /**
24834        * Fired when the end of the media resource is reached (currentTime == duration)
24835        *
24836        * @event Player#ended
24837        * @type {EventTarget~Event}
24838        */
24839 
24840 
24841       this.trigger('ended');
24842     }
24843     /**
24844      * Fired when the duration of the media resource is first known or changed
24845      *
24846      * @listens Tech#durationchange
24847      * @private
24848      */
24849     ;
24850 
24851     _proto.handleTechDurationChange_ = function handleTechDurationChange_() {
24852       this.duration(this.techGet_('duration'));
24853     }
24854     /**
24855      * Handle a click on the media element to play/pause
24856      *
24857      * @param {EventTarget~Event} event
24858      *        the event that caused this function to trigger
24859      *
24860      * @listens Tech#mouseup
24861      * @private
24862      */
24863     ;
24864 
24865     _proto.handleTechClick_ = function handleTechClick_(event) {
24866       if (!isSingleLeftClick(event)) {
24867         return;
24868       } // When controls are disabled a click should not toggle playback because
24869       // the click is considered a control
24870 
24871 
24872       if (!this.controls_) {
24873         return;
24874       }
24875 
24876       if (this.paused()) {
24877         silencePromise(this.play());
24878       } else {
24879         this.pause();
24880       }
24881     }
24882     /**
24883      * Handle a double-click on the media element to enter/exit fullscreen
24884      *
24885      * @param {EventTarget~Event} event
24886      *        the event that caused this function to trigger
24887      *
24888      * @listens Tech#dblclick
24889      * @private
24890      */
24891     ;
24892 
24893     _proto.handleTechDoubleClick_ = function handleTechDoubleClick_(event) {
24894       if (!this.controls_) {
24895         return;
24896       } // we do not want to toggle fullscreen state
24897       // when double-clicking inside a control bar or a modal
24898 
24899 
24900       var inAllowedEls = Array.prototype.some.call(this.$$('.vjs-control-bar, .vjs-modal-dialog'), function (el) {
24901         return el.contains(event.target);
24902       });
24903 
24904       if (!inAllowedEls) {
24905         /*
24906          * options.userActions.doubleClick
24907          *
24908          * If `undefined` or `true`, double-click toggles fullscreen if controls are present
24909          * Set to `false` to disable double-click handling
24910          * Set to a function to substitute an external double-click handler
24911          */
24912         if (this.options_ === undefined || this.options_.userActions === undefined || this.options_.userActions.doubleClick === undefined || this.options_.userActions.doubleClick !== false) {
24913           if (this.options_ !== undefined && this.options_.userActions !== undefined && typeof this.options_.userActions.doubleClick === 'function') {
24914             this.options_.userActions.doubleClick.call(this, event);
24915           } else if (this.isFullscreen()) {
24916             this.exitFullscreen();
24917           } else {
24918             this.requestFullscreen();
24919           }
24920         }
24921       }
24922     }
24923     /**
24924      * Handle a tap on the media element. It will toggle the user
24925      * activity state, which hides and shows the controls.
24926      *
24927      * @listens Tech#tap
24928      * @private
24929      */
24930     ;
24931 
24932     _proto.handleTechTap_ = function handleTechTap_() {
24933       this.userActive(!this.userActive());
24934     }
24935     /**
24936      * Handle touch to start
24937      *
24938      * @listens Tech#touchstart
24939      * @private
24940      */
24941     ;
24942 
24943     _proto.handleTechTouchStart_ = function handleTechTouchStart_() {
24944       this.userWasActive = this.userActive();
24945     }
24946     /**
24947      * Handle touch to move
24948      *
24949      * @listens Tech#touchmove
24950      * @private
24951      */
24952     ;
24953 
24954     _proto.handleTechTouchMove_ = function handleTechTouchMove_() {
24955       if (this.userWasActive) {
24956         this.reportUserActivity();
24957       }
24958     }
24959     /**
24960      * Handle touch to end
24961      *
24962      * @param {EventTarget~Event} event
24963      *        the touchend event that triggered
24964      *        this function
24965      *
24966      * @listens Tech#touchend
24967      * @private
24968      */
24969     ;
24970 
24971     _proto.handleTechTouchEnd_ = function handleTechTouchEnd_(event) {
24972       // Stop the mouse events from also happening
24973       event.preventDefault();
24974     }
24975     /**
24976      * native click events on the SWF aren't triggered on IE11, Win8.1RT
24977      * use stageclick events triggered from inside the SWF instead
24978      *
24979      * @private
24980      * @listens stageclick
24981      */
24982     ;
24983 
24984     _proto.handleStageClick_ = function handleStageClick_() {
24985       this.reportUserActivity();
24986     }
24987     /**
24988      * @private
24989      */
24990     ;
24991 
24992     _proto.toggleFullscreenClass_ = function toggleFullscreenClass_() {
24993       if (this.isFullscreen()) {
24994         this.addClass('vjs-fullscreen');
24995       } else {
24996         this.removeClass('vjs-fullscreen');
24997       }
24998     }
24999     /**
25000      * when the document fschange event triggers it calls this
25001      */
25002     ;
25003 
25004     _proto.documentFullscreenChange_ = function documentFullscreenChange_(e) {
25005       var el = this.el();
25006       var isFs = document[this.fsApi_.fullscreenElement] === el;
25007 
25008       if (!isFs && el.matches) {
25009         isFs = el.matches(':' + this.fsApi_.fullscreen);
25010       } else if (!isFs && el.msMatchesSelector) {
25011         isFs = el.msMatchesSelector(':' + this.fsApi_.fullscreen);
25012       }
25013 
25014       this.isFullscreen(isFs); // If cancelling fullscreen, remove event listener.
25015 
25016       if (this.isFullscreen() === false) {
25017         off(document, this.fsApi_.fullscreenchange, this.boundDocumentFullscreenChange_);
25018       }
25019 
25020       if (this.fsApi_.prefixed) {
25021         /**
25022          * @event Player#fullscreenchange
25023          * @type {EventTarget~Event}
25024          */
25025         this.trigger('fullscreenchange');
25026       }
25027     }
25028     /**
25029      * Handle Tech Fullscreen Change
25030      *
25031      * @param {EventTarget~Event} event
25032      *        the fullscreenchange event that triggered this function
25033      *
25034      * @param {Object} data
25035      *        the data that was sent with the event
25036      *
25037      * @private
25038      * @listens Tech#fullscreenchange
25039      * @fires Player#fullscreenchange
25040      */
25041     ;
25042 
25043     _proto.handleTechFullscreenChange_ = function handleTechFullscreenChange_(event, data) {
25044       if (data) {
25045         this.isFullscreen(data.isFullscreen);
25046       }
25047       /**
25048        * Fired when going in and out of fullscreen.
25049        *
25050        * @event Player#fullscreenchange
25051        * @type {EventTarget~Event}
25052        */
25053 
25054 
25055       this.trigger('fullscreenchange');
25056     }
25057     /**
25058      * @private
25059      */
25060     ;
25061 
25062     _proto.togglePictureInPictureClass_ = function togglePictureInPictureClass_() {
25063       if (this.isInPictureInPicture()) {
25064         this.addClass('vjs-picture-in-picture');
25065       } else {
25066         this.removeClass('vjs-picture-in-picture');
25067       }
25068     }
25069     /**
25070      * Handle Tech Enter Picture-in-Picture.
25071      *
25072      * @param {EventTarget~Event} event
25073      *        the enterpictureinpicture event that triggered this function
25074      *
25075      * @private
25076      * @listens Tech#enterpictureinpicture
25077      */
25078     ;
25079 
25080     _proto.handleTechEnterPictureInPicture_ = function handleTechEnterPictureInPicture_(event) {
25081       this.isInPictureInPicture(true);
25082     }
25083     /**
25084      * Handle Tech Leave Picture-in-Picture.
25085      *
25086      * @param {EventTarget~Event} event
25087      *        the leavepictureinpicture event that triggered this function
25088      *
25089      * @private
25090      * @listens Tech#leavepictureinpicture
25091      */
25092     ;
25093 
25094     _proto.handleTechLeavePictureInPicture_ = function handleTechLeavePictureInPicture_(event) {
25095       this.isInPictureInPicture(false);
25096     }
25097     /**
25098      * Fires when an error occurred during the loading of an audio/video.
25099      *
25100      * @private
25101      * @listens Tech#error
25102      */
25103     ;
25104 
25105     _proto.handleTechError_ = function handleTechError_() {
25106       var error = this.tech_.error();
25107       this.error(error);
25108     }
25109     /**
25110      * Retrigger the `textdata` event that was triggered by the {@link Tech}.
25111      *
25112      * @fires Player#textdata
25113      * @listens Tech#textdata
25114      * @private
25115      */
25116     ;
25117 
25118     _proto.handleTechTextData_ = function handleTechTextData_() {
25119       var data = null;
25120 
25121       if (arguments.length > 1) {
25122         data = arguments[1];
25123       }
25124       /**
25125        * Fires when we get a textdata event from tech
25126        *
25127        * @event Player#textdata
25128        * @type {EventTarget~Event}
25129        */
25130 
25131 
25132       this.trigger('textdata', data);
25133     }
25134     /**
25135      * Get object for cached values.
25136      *
25137      * @return {Object}
25138      *         get the current object cache
25139      */
25140     ;
25141 
25142     _proto.getCache = function getCache() {
25143       return this.cache_;
25144     }
25145     /**
25146      * Resets the internal cache object.
25147      *
25148      * Using this function outside the player constructor or reset method may
25149      * have unintended side-effects.
25150      *
25151      * @private
25152      */
25153     ;
25154 
25155     _proto.resetCache_ = function resetCache_() {
25156       this.cache_ = {
25157         // Right now, the currentTime is not _really_ cached because it is always
25158         // retrieved from the tech (see: currentTime). However, for completeness,
25159         // we set it to zero here to ensure that if we do start actually caching
25160         // it, we reset it along with everything else.
25161         currentTime: 0,
25162         inactivityTimeout: this.options_.inactivityTimeout,
25163         duration: NaN,
25164         lastVolume: 1,
25165         lastPlaybackRate: this.defaultPlaybackRate(),
25166         media: null,
25167         src: '',
25168         source: {},
25169         sources: [],
25170         volume: 1
25171       };
25172     }
25173     /**
25174      * Pass values to the playback tech
25175      *
25176      * @param {string} [method]
25177      *        the method to call
25178      *
25179      * @param {Object} arg
25180      *        the argument to pass
25181      *
25182      * @private
25183      */
25184     ;
25185 
25186     _proto.techCall_ = function techCall_(method, arg) {
25187       // If it's not ready yet, call method when it is
25188       this.ready(function () {
25189         if (method in allowedSetters) {
25190           return set(this.middleware_, this.tech_, method, arg);
25191         } else if (method in allowedMediators) {
25192           return mediate(this.middleware_, this.tech_, method, arg);
25193         }
25194 
25195         try {
25196           if (this.tech_) {
25197             this.tech_[method](arg);
25198           }
25199         } catch (e) {
25200           log(e);
25201           throw e;
25202         }
25203       }, true);
25204     }
25205     /**
25206      * Get calls can't wait for the tech, and sometimes don't need to.
25207      *
25208      * @param {string} method
25209      *        Tech method
25210      *
25211      * @return {Function|undefined}
25212      *         the method or undefined
25213      *
25214      * @private
25215      */
25216     ;
25217 
25218     _proto.techGet_ = function techGet_(method) {
25219       if (!this.tech_ || !this.tech_.isReady_) {
25220         return;
25221       }
25222 
25223       if (method in allowedGetters) {
25224         return get(this.middleware_, this.tech_, method);
25225       } else if (method in allowedMediators) {
25226         return mediate(this.middleware_, this.tech_, method);
25227       } // Flash likes to die and reload when you hide or reposition it.
25228       // In these cases the object methods go away and we get errors.
25229       // When that happens we'll catch the errors and inform tech that it's not ready any more.
25230 
25231 
25232       try {
25233         return this.tech_[method]();
25234       } catch (e) {
25235         // When building additional tech libs, an expected method may not be defined yet
25236         if (this.tech_[method] === undefined) {
25237           log("Video.js: " + method + " method not defined for " + this.techName_ + " playback technology.", e);
25238           throw e;
25239         } // When a method isn't available on the object it throws a TypeError
25240 
25241 
25242         if (e.name === 'TypeError') {
25243           log("Video.js: " + method + " unavailable on " + this.techName_ + " playback technology element.", e);
25244           this.tech_.isReady_ = false;
25245           throw e;
25246         } // If error unknown, just log and throw
25247 
25248 
25249         log(e);
25250         throw e;
25251       }
25252     }
25253     /**
25254      * Attempt to begin playback at the first opportunity.
25255      *
25256      * @return {Promise|undefined}
25257      *         Returns a promise if the browser supports Promises (or one
25258      *         was passed in as an option). This promise will be resolved on
25259      *         the return value of play. If this is undefined it will fulfill the
25260      *         promise chain otherwise the promise chain will be fulfilled when
25261      *         the promise from play is fulfilled.
25262      */
25263     ;
25264 
25265     _proto.play = function play() {
25266       var _this8 = this;
25267 
25268       var PromiseClass = this.options_.Promise || window$1.Promise;
25269 
25270       if (PromiseClass) {
25271         return new PromiseClass(function (resolve) {
25272           _this8.play_(resolve);
25273         });
25274       }
25275 
25276       return this.play_();
25277     }
25278     /**
25279      * The actual logic for play, takes a callback that will be resolved on the
25280      * return value of play. This allows us to resolve to the play promise if there
25281      * is one on modern browsers.
25282      *
25283      * @private
25284      * @param {Function} [callback]
25285      *        The callback that should be called when the techs play is actually called
25286      */
25287     ;
25288 
25289     _proto.play_ = function play_(callback) {
25290       var _this9 = this;
25291 
25292       if (callback === void 0) {
25293         callback = silencePromise;
25294       }
25295 
25296       this.playCallbacks_.push(callback);
25297       var isSrcReady = Boolean(!this.changingSrc_ && (this.src() || this.currentSrc())); // treat calls to play_ somewhat like the `one` event function
25298 
25299       if (this.waitToPlay_) {
25300         this.off(['ready', 'loadstart'], this.waitToPlay_);
25301         this.waitToPlay_ = null;
25302       } // if the player/tech is not ready or the src itself is not ready
25303       // queue up a call to play on `ready` or `loadstart`
25304 
25305 
25306       if (!this.isReady_ || !isSrcReady) {
25307         this.waitToPlay_ = function (e) {
25308           _this9.play_();
25309         };
25310 
25311         this.one(['ready', 'loadstart'], this.waitToPlay_); // if we are in Safari, there is a high chance that loadstart will trigger after the gesture timeperiod
25312         // in that case, we need to prime the video element by calling load so it'll be ready in time
25313 
25314         if (!isSrcReady && (IS_ANY_SAFARI || IS_IOS)) {
25315           this.load();
25316         }
25317 
25318         return;
25319       } // If the player/tech is ready and we have a source, we can attempt playback.
25320 
25321 
25322       var val = this.techGet_('play'); // play was terminated if the returned value is null
25323 
25324       if (val === null) {
25325         this.runPlayTerminatedQueue_();
25326       } else {
25327         this.runPlayCallbacks_(val);
25328       }
25329     }
25330     /**
25331      * These functions will be run when if play is terminated. If play
25332      * runPlayCallbacks_ is run these function will not be run. This allows us
25333      * to differenciate between a terminated play and an actual call to play.
25334      */
25335     ;
25336 
25337     _proto.runPlayTerminatedQueue_ = function runPlayTerminatedQueue_() {
25338       var queue = this.playTerminatedQueue_.slice(0);
25339       this.playTerminatedQueue_ = [];
25340       queue.forEach(function (q) {
25341         q();
25342       });
25343     }
25344     /**
25345      * When a callback to play is delayed we have to run these
25346      * callbacks when play is actually called on the tech. This function
25347      * runs the callbacks that were delayed and accepts the return value
25348      * from the tech.
25349      *
25350      * @param {undefined|Promise} val
25351      *        The return value from the tech.
25352      */
25353     ;
25354 
25355     _proto.runPlayCallbacks_ = function runPlayCallbacks_(val) {
25356       var callbacks = this.playCallbacks_.slice(0);
25357       this.playCallbacks_ = []; // clear play terminatedQueue since we finished a real play
25358 
25359       this.playTerminatedQueue_ = [];
25360       callbacks.forEach(function (cb) {
25361         cb(val);
25362       });
25363     }
25364     /**
25365      * Pause the video playback
25366      *
25367      * @return {Player}
25368      *         A reference to the player object this function was called on
25369      */
25370     ;
25371 
25372     _proto.pause = function pause() {
25373       this.techCall_('pause');
25374     }
25375     /**
25376      * Check if the player is paused or has yet to play
25377      *
25378      * @return {boolean}
25379      *         - false: if the media is currently playing
25380      *         - true: if media is not currently playing
25381      */
25382     ;
25383 
25384     _proto.paused = function paused() {
25385       // The initial state of paused should be true (in Safari it's actually false)
25386       return this.techGet_('paused') === false ? false : true;
25387     }
25388     /**
25389      * Get a TimeRange object representing the current ranges of time that the user
25390      * has played.
25391      *
25392      * @return {TimeRange}
25393      *         A time range object that represents all the increments of time that have
25394      *         been played.
25395      */
25396     ;
25397 
25398     _proto.played = function played() {
25399       return this.techGet_('played') || createTimeRanges(0, 0);
25400     }
25401     /**
25402      * Returns whether or not the user is "scrubbing". Scrubbing is
25403      * when the user has clicked the progress bar handle and is
25404      * dragging it along the progress bar.
25405      *
25406      * @param {boolean} [isScrubbing]
25407      *        whether the user is or is not scrubbing
25408      *
25409      * @return {boolean}
25410      *         The value of scrubbing when getting
25411      */
25412     ;
25413 
25414     _proto.scrubbing = function scrubbing(isScrubbing) {
25415       if (typeof isScrubbing === 'undefined') {
25416         return this.scrubbing_;
25417       }
25418 
25419       this.scrubbing_ = !!isScrubbing;
25420 
25421       if (isScrubbing) {
25422         this.addClass('vjs-scrubbing');
25423       } else {
25424         this.removeClass('vjs-scrubbing');
25425       }
25426     }
25427     /**
25428      * Get or set the current time (in seconds)
25429      *
25430      * @param {number|string} [seconds]
25431      *        The time to seek to in seconds
25432      *
25433      * @return {number}
25434      *         - the current time in seconds when getting
25435      */
25436     ;
25437 
25438     _proto.currentTime = function currentTime(seconds) {
25439       if (typeof seconds !== 'undefined') {
25440         if (seconds < 0) {
25441           seconds = 0;
25442         }
25443 
25444         this.techCall_('setCurrentTime', seconds);
25445         return;
25446       } // cache last currentTime and return. default to 0 seconds
25447       //
25448       // Caching the currentTime is meant to prevent a massive amount of reads on the tech's
25449       // currentTime when scrubbing, but may not provide much performance benefit afterall.
25450       // Should be tested. Also something has to read the actual current time or the cache will
25451       // never get updated.
25452 
25453 
25454       this.cache_.currentTime = this.techGet_('currentTime') || 0;
25455       return this.cache_.currentTime;
25456     }
25457     /**
25458      * Normally gets the length in time of the video in seconds;
25459      * in all but the rarest use cases an argument will NOT be passed to the method
25460      *
25461      * > **NOTE**: The video must have started loading before the duration can be
25462      * known, and in the case of Flash, may not be known until the video starts
25463      * playing.
25464      *
25465      * @fires Player#durationchange
25466      *
25467      * @param {number} [seconds]
25468      *        The duration of the video to set in seconds
25469      *
25470      * @return {number}
25471      *         - The duration of the video in seconds when getting
25472      */
25473     ;
25474 
25475     _proto.duration = function duration(seconds) {
25476       if (seconds === undefined) {
25477         // return NaN if the duration is not known
25478         return this.cache_.duration !== undefined ? this.cache_.duration : NaN;
25479       }
25480 
25481       seconds = parseFloat(seconds); // Standardize on Infinity for signaling video is live
25482 
25483       if (seconds < 0) {
25484         seconds = Infinity;
25485       }
25486 
25487       if (seconds !== this.cache_.duration) {
25488         // Cache the last set value for optimized scrubbing (esp. Flash)
25489         this.cache_.duration = seconds;
25490 
25491         if (seconds === Infinity) {
25492           this.addClass('vjs-live');
25493 
25494           if (this.options_.liveui && this.player_.liveTracker) {
25495             this.addClass('vjs-liveui');
25496           }
25497         } else {
25498           this.removeClass('vjs-live');
25499           this.removeClass('vjs-liveui');
25500         }
25501 
25502         if (!isNaN(seconds)) {
25503           // Do not fire durationchange unless the duration value is known.
25504           // @see [Spec]{@link https://www.w3.org/TR/2011/WD-html5-20110113/video.html#media-element-load-algorithm}
25505 
25506           /**
25507            * @event Player#durationchange
25508            * @type {EventTarget~Event}
25509            */
25510           this.trigger('durationchange');
25511         }
25512       }
25513     }
25514     /**
25515      * Calculates how much time is left in the video. Not part
25516      * of the native video API.
25517      *
25518      * @return {number}
25519      *         The time remaining in seconds
25520      */
25521     ;
25522 
25523     _proto.remainingTime = function remainingTime() {
25524       return this.duration() - this.currentTime();
25525     }
25526     /**
25527      * A remaining time function that is intented to be used when
25528      * the time is to be displayed directly to the user.
25529      *
25530      * @return {number}
25531      *         The rounded time remaining in seconds
25532      */
25533     ;
25534 
25535     _proto.remainingTimeDisplay = function remainingTimeDisplay() {
25536       return Math.floor(this.duration()) - Math.floor(this.currentTime());
25537     } //
25538     // Kind of like an array of portions of the video that have been downloaded.
25539 
25540     /**
25541      * Get a TimeRange object with an array of the times of the video
25542      * that have been downloaded. If you just want the percent of the
25543      * video that's been downloaded, use bufferedPercent.
25544      *
25545      * @see [Buffered Spec]{@link http://dev.w3.org/html5/spec/video.html#dom-media-buffered}
25546      *
25547      * @return {TimeRange}
25548      *         A mock TimeRange object (following HTML spec)
25549      */
25550     ;
25551 
25552     _proto.buffered = function buffered() {
25553       var buffered = this.techGet_('buffered');
25554 
25555       if (!buffered || !buffered.length) {
25556         buffered = createTimeRanges(0, 0);
25557       }
25558 
25559       return buffered;
25560     }
25561     /**
25562      * Get the percent (as a decimal) of the video that's been downloaded.
25563      * This method is not a part of the native HTML video API.
25564      *
25565      * @return {number}
25566      *         A decimal between 0 and 1 representing the percent
25567      *         that is buffered 0 being 0% and 1 being 100%
25568      */
25569     ;
25570 
25571     _proto.bufferedPercent = function bufferedPercent$1() {
25572       return bufferedPercent(this.buffered(), this.duration());
25573     }
25574     /**
25575      * Get the ending time of the last buffered time range
25576      * This is used in the progress bar to encapsulate all time ranges.
25577      *
25578      * @return {number}
25579      *         The end of the last buffered time range
25580      */
25581     ;
25582 
25583     _proto.bufferedEnd = function bufferedEnd() {
25584       var buffered = this.buffered();
25585       var duration = this.duration();
25586       var end = buffered.end(buffered.length - 1);
25587 
25588       if (end > duration) {
25589         end = duration;
25590       }
25591 
25592       return end;
25593     }
25594     /**
25595      * Get or set the current volume of the media
25596      *
25597      * @param  {number} [percentAsDecimal]
25598      *         The new volume as a decimal percent:
25599      *         - 0 is muted/0%/off
25600      *         - 1.0 is 100%/full
25601      *         - 0.5 is half volume or 50%
25602      *
25603      * @return {number}
25604      *         The current volume as a percent when getting
25605      */
25606     ;
25607 
25608     _proto.volume = function volume(percentAsDecimal) {
25609       var vol;
25610 
25611       if (percentAsDecimal !== undefined) {
25612         // Force value to between 0 and 1
25613         vol = Math.max(0, Math.min(1, parseFloat(percentAsDecimal)));
25614         this.cache_.volume = vol;
25615         this.techCall_('setVolume', vol);
25616 
25617         if (vol > 0) {
25618           this.lastVolume_(vol);
25619         }
25620 
25621         return;
25622       } // Default to 1 when returning current volume.
25623 
25624 
25625       vol = parseFloat(this.techGet_('volume'));
25626       return isNaN(vol) ? 1 : vol;
25627     }
25628     /**
25629      * Get the current muted state, or turn mute on or off
25630      *
25631      * @param {boolean} [muted]
25632      *        - true to mute
25633      *        - false to unmute
25634      *
25635      * @return {boolean}
25636      *         - true if mute is on and getting
25637      *         - false if mute is off and getting
25638      */
25639     ;
25640 
25641     _proto.muted = function muted(_muted) {
25642       if (_muted !== undefined) {
25643         this.techCall_('setMuted', _muted);
25644         return;
25645       }
25646 
25647       return this.techGet_('muted') || false;
25648     }
25649     /**
25650      * Get the current defaultMuted state, or turn defaultMuted on or off. defaultMuted
25651      * indicates the state of muted on initial playback.
25652      *
25653      * ```js
25654      *   var myPlayer = videojs('some-player-id');
25655      *
25656      *   myPlayer.src("http://www.example.com/path/to/video.mp4");
25657      *
25658      *   // get, should be false
25659      *   console.log(myPlayer.defaultMuted());
25660      *   // set to true
25661      *   myPlayer.defaultMuted(true);
25662      *   // get should be true
25663      *   console.log(myPlayer.defaultMuted());
25664      * ```
25665      *
25666      * @param {boolean} [defaultMuted]
25667      *        - true to mute
25668      *        - false to unmute
25669      *
25670      * @return {boolean|Player}
25671      *         - true if defaultMuted is on and getting
25672      *         - false if defaultMuted is off and getting
25673      *         - A reference to the current player when setting
25674      */
25675     ;
25676 
25677     _proto.defaultMuted = function defaultMuted(_defaultMuted) {
25678       if (_defaultMuted !== undefined) {
25679         return this.techCall_('setDefaultMuted', _defaultMuted);
25680       }
25681 
25682       return this.techGet_('defaultMuted') || false;
25683     }
25684     /**
25685      * Get the last volume, or set it
25686      *
25687      * @param  {number} [percentAsDecimal]
25688      *         The new last volume as a decimal percent:
25689      *         - 0 is muted/0%/off
25690      *         - 1.0 is 100%/full
25691      *         - 0.5 is half volume or 50%
25692      *
25693      * @return {number}
25694      *         the current value of lastVolume as a percent when getting
25695      *
25696      * @private
25697      */
25698     ;
25699 
25700     _proto.lastVolume_ = function lastVolume_(percentAsDecimal) {
25701       if (percentAsDecimal !== undefined && percentAsDecimal !== 0) {
25702         this.cache_.lastVolume = percentAsDecimal;
25703         return;
25704       }
25705 
25706       return this.cache_.lastVolume;
25707     }
25708     /**
25709      * Check if current tech can support native fullscreen
25710      * (e.g. with built in controls like iOS, so not our flash swf)
25711      *
25712      * @return {boolean}
25713      *         if native fullscreen is supported
25714      */
25715     ;
25716 
25717     _proto.supportsFullScreen = function supportsFullScreen() {
25718       return this.techGet_('supportsFullScreen') || false;
25719     }
25720     /**
25721      * Check if the player is in fullscreen mode or tell the player that it
25722      * is or is not in fullscreen mode.
25723      *
25724      * > NOTE: As of the latest HTML5 spec, isFullscreen is no longer an official
25725      * property and instead document.fullscreenElement is used. But isFullscreen is
25726      * still a valuable property for internal player workings.
25727      *
25728      * @param  {boolean} [isFS]
25729      *         Set the players current fullscreen state
25730      *
25731      * @return {boolean}
25732      *         - true if fullscreen is on and getting
25733      *         - false if fullscreen is off and getting
25734      */
25735     ;
25736 
25737     _proto.isFullscreen = function isFullscreen(isFS) {
25738       if (isFS !== undefined) {
25739         this.isFullscreen_ = !!isFS;
25740         this.toggleFullscreenClass_();
25741         return;
25742       }
25743 
25744       return !!this.isFullscreen_;
25745     }
25746     /**
25747      * Increase the size of the video to full screen
25748      * In some browsers, full screen is not supported natively, so it enters
25749      * "full window mode", where the video fills the browser window.
25750      * In browsers and devices that support native full screen, sometimes the
25751      * browser's default controls will be shown, and not the Video.js custom skin.
25752      * This includes most mobile devices (iOS, Android) and older versions of
25753      * Safari.
25754      *
25755      * @param  {Object} [fullscreenOptions]
25756      *         Override the player fullscreen options
25757      *
25758      * @fires Player#fullscreenchange
25759      */
25760     ;
25761 
25762     _proto.requestFullscreen = function requestFullscreen(fullscreenOptions) {
25763       var fsOptions;
25764       this.isFullscreen(true);
25765 
25766       if (this.fsApi_.requestFullscreen) {
25767         // the browser supports going fullscreen at the element level so we can
25768         // take the controls fullscreen as well as the video
25769         // Trigger fullscreenchange event after change
25770         // We have to specifically add this each time, and remove
25771         // when canceling fullscreen. Otherwise if there's multiple
25772         // players on a page, they would all be reacting to the same fullscreen
25773         // events
25774         on(document, this.fsApi_.fullscreenchange, this.boundDocumentFullscreenChange_); // only pass FullscreenOptions to requestFullscreen if it isn't prefixed
25775 
25776         if (!this.fsApi_.prefixed) {
25777           fsOptions = this.options_.fullscreen && this.options_.fullscreen.options || {};
25778 
25779           if (fullscreenOptions !== undefined) {
25780             fsOptions = fullscreenOptions;
25781           }
25782         }
25783 
25784         silencePromise(this.el_[this.fsApi_.requestFullscreen](fsOptions));
25785       } else if (this.tech_.supportsFullScreen()) {
25786         // we can't take the video.js controls fullscreen but we can go fullscreen
25787         // with native controls
25788         this.techCall_('enterFullScreen');
25789       } else {
25790         // fullscreen isn't supported so we'll just stretch the video element to
25791         // fill the viewport
25792         this.enterFullWindow();
25793         /**
25794          * @event Player#fullscreenchange
25795          * @type {EventTarget~Event}
25796          */
25797 
25798         this.trigger('fullscreenchange');
25799       }
25800     }
25801     /**
25802      * Return the video to its normal size after having been in full screen mode
25803      *
25804      * @fires Player#fullscreenchange
25805      */
25806     ;
25807 
25808     _proto.exitFullscreen = function exitFullscreen() {
25809       this.isFullscreen(false); // Check for browser element fullscreen support
25810 
25811       if (this.fsApi_.requestFullscreen) {
25812         silencePromise(document[this.fsApi_.exitFullscreen]());
25813       } else if (this.tech_.supportsFullScreen()) {
25814         this.techCall_('exitFullScreen');
25815       } else {
25816         this.exitFullWindow();
25817         /**
25818          * @event Player#fullscreenchange
25819          * @type {EventTarget~Event}
25820          */
25821 
25822         this.trigger('fullscreenchange');
25823       }
25824     }
25825     /**
25826      * When fullscreen isn't supported we can stretch the
25827      * video container to as wide as the browser will let us.
25828      *
25829      * @fires Player#enterFullWindow
25830      */
25831     ;
25832 
25833     _proto.enterFullWindow = function enterFullWindow() {
25834       this.isFullWindow = true; // Storing original doc overflow value to return to when fullscreen is off
25835 
25836       this.docOrigOverflow = document.documentElement.style.overflow; // Add listener for esc key to exit fullscreen
25837 
25838       on(document, 'keydown', this.boundFullWindowOnEscKey_); // Hide any scroll bars
25839 
25840       document.documentElement.style.overflow = 'hidden'; // Apply fullscreen styles
25841 
25842       addClass(document.body, 'vjs-full-window');
25843       /**
25844        * @event Player#enterFullWindow
25845        * @type {EventTarget~Event}
25846        */
25847 
25848       this.trigger('enterFullWindow');
25849     }
25850     /**
25851      * Check for call to either exit full window or
25852      * full screen on ESC key
25853      *
25854      * @param {string} event
25855      *        Event to check for key press
25856      */
25857     ;
25858 
25859     _proto.fullWindowOnEscKey = function fullWindowOnEscKey(event) {
25860       if (keycode.isEventKey(event, 'Esc')) {
25861         if (this.isFullscreen() === true) {
25862           this.exitFullscreen();
25863         } else {
25864           this.exitFullWindow();
25865         }
25866       }
25867     }
25868     /**
25869      * Exit full window
25870      *
25871      * @fires Player#exitFullWindow
25872      */
25873     ;
25874 
25875     _proto.exitFullWindow = function exitFullWindow() {
25876       this.isFullWindow = false;
vendor: 9,198 bytes, lines 25877-26137
25877       off(document, 'keydown', this.boundFullWindowOnEscKey_); // Unhide scroll bars.
25878 
25879       document.documentElement.style.overflow = this.docOrigOverflow; // Remove fullscreen styles
25880 
25881       removeClass(document.body, 'vjs-full-window'); // Resize the box, controller, and poster to original sizes
25882       // this.positionAll();
25883 
25884       /**
25885        * @event Player#exitFullWindow
25886        * @type {EventTarget~Event}
25887        */
25888 
25889       this.trigger('exitFullWindow');
25890     }
25891     /**
25892      * Check if the player is in Picture-in-Picture mode or tell the player that it
25893      * is or is not in Picture-in-Picture mode.
25894      *
25895      * @param  {boolean} [isPiP]
25896      *         Set the players current Picture-in-Picture state
25897      *
25898      * @return {boolean}
25899      *         - true if Picture-in-Picture is on and getting
25900      *         - false if Picture-in-Picture is off and getting
25901      */
25902     ;
25903 
25904     _proto.isInPictureInPicture = function isInPictureInPicture(isPiP) {
25905       if (isPiP !== undefined) {
25906         this.isInPictureInPicture_ = !!isPiP;
25907         this.togglePictureInPictureClass_();
25908         return;
25909       }
25910 
25911       return !!this.isInPictureInPicture_;
25912     }
25913     /**
25914      * Create a floating video window always on top of other windows so that users may
25915      * continue consuming media while they interact with other content sites, or
25916      * applications on their device.
25917      *
25918      * @see [Spec]{@link https://wicg.github.io/picture-in-picture}
25919      *
25920      * @fires Player#enterpictureinpicture
25921      *
25922      * @return {Promise}
25923      *         A promise with a Picture-in-Picture window.
25924      */
25925     ;
25926 
25927     _proto.requestPictureInPicture = function requestPictureInPicture() {
25928       if ('pictureInPictureEnabled' in document) {
25929         /**
25930          * This event fires when the player enters picture in picture mode
25931          *
25932          * @event Player#enterpictureinpicture
25933          * @type {EventTarget~Event}
25934          */
25935         return this.techGet_('requestPictureInPicture');
25936       }
25937     }
25938     /**
25939      * Exit Picture-in-Picture mode.
25940      *
25941      * @see [Spec]{@link https://wicg.github.io/picture-in-picture}
25942      *
25943      * @fires Player#leavepictureinpicture
25944      *
25945      * @return {Promise}
25946      *         A promise.
25947      */
25948     ;
25949 
25950     _proto.exitPictureInPicture = function exitPictureInPicture() {
25951       if ('pictureInPictureEnabled' in document) {
25952         /**
25953          * This event fires when the player leaves picture in picture mode
25954          *
25955          * @event Player#leavepictureinpicture
25956          * @type {EventTarget~Event}
25957          */
25958         return document.exitPictureInPicture();
25959       }
25960     }
25961     /**
25962      * Called when this Player has focus and a key gets pressed down, or when
25963      * any Component of this player receives a key press that it doesn't handle.
25964      * This allows player-wide hotkeys (either as defined below, or optionally
25965      * by an external function).
25966      *
25967      * @param {EventTarget~Event} event
25968      *        The `keydown` event that caused this function to be called.
25969      *
25970      * @listens keydown
25971      */
25972     ;
25973 
25974     _proto.handleKeyDown = function handleKeyDown(event) {
25975       var userActions = this.options_.userActions; // Bail out if hotkeys are not configured.
25976 
25977       if (!userActions || !userActions.hotkeys) {
25978         return;
25979       } // Function that determines whether or not to exclude an element from
25980       // hotkeys handling.
25981 
25982 
25983       var excludeElement = function excludeElement(el) {
25984         var tagName = el.tagName.toLowerCase(); // The first and easiest test is for `contenteditable` elements.
25985 
25986         if (el.isContentEditable) {
25987           return true;
25988         } // Inputs matching these types will still trigger hotkey handling as
25989         // they are not text inputs.
25990 
25991 
25992         var allowedInputTypes = ['button', 'checkbox', 'hidden', 'radio', 'reset', 'submit'];
25993 
25994         if (tagName === 'input') {
25995           return allowedInputTypes.indexOf(el.type) === -1;
25996         } // The final test is by tag name. These tags will be excluded entirely.
25997 
25998 
25999         var excludedTags = ['textarea'];
26000         return excludedTags.indexOf(tagName) !== -1;
26001       }; // Bail out if the user is focused on an interactive form element.
26002 
26003 
26004       if (excludeElement(this.el_.ownerDocument.activeElement)) {
26005         return;
26006       }
26007 
26008       if (typeof userActions.hotkeys === 'function') {
26009         userActions.hotkeys.call(this, event);
26010       } else {
26011         this.handleHotkeys(event);
26012       }
26013     }
26014     /**
26015      * Called when this Player receives a hotkey keydown event.
26016      * Supported player-wide hotkeys are:
26017      *
26018      *   f          - toggle fullscreen
26019      *   m          - toggle mute
26020      *   k or Space - toggle play/pause
26021      *
26022      * @param {EventTarget~Event} event
26023      *        The `keydown` event that caused this function to be called.
26024      */
26025     ;
26026 
26027     _proto.handleHotkeys = function handleHotkeys(event) {
26028       var hotkeys = this.options_.userActions ? this.options_.userActions.hotkeys : {}; // set fullscreenKey, muteKey, playPauseKey from `hotkeys`, use defaults if not set
26029 
26030       var _hotkeys$fullscreenKe = hotkeys.fullscreenKey,
26031           fullscreenKey = _hotkeys$fullscreenKe === void 0 ? function (keydownEvent) {
26032         return keycode.isEventKey(keydownEvent, 'f');
26033       } : _hotkeys$fullscreenKe,
26034           _hotkeys$muteKey = hotkeys.muteKey,
26035           muteKey = _hotkeys$muteKey === void 0 ? function (keydownEvent) {
26036         return keycode.isEventKey(keydownEvent, 'm');
26037       } : _hotkeys$muteKey,
26038           _hotkeys$playPauseKey = hotkeys.playPauseKey,
26039           playPauseKey = _hotkeys$playPauseKey === void 0 ? function (keydownEvent) {
26040         return keycode.isEventKey(keydownEvent, 'k') || keycode.isEventKey(keydownEvent, 'Space');
26041       } : _hotkeys$playPauseKey;
26042 
26043       if (fullscreenKey.call(this, event)) {
26044         event.preventDefault();
26045         event.stopPropagation();
26046         var FSToggle = Component.getComponent('FullscreenToggle');
26047 
26048         if (document[this.fsApi_.fullscreenEnabled] !== false) {
26049           FSToggle.prototype.handleClick.call(this, event);
26050         }
26051       } else if (muteKey.call(this, event)) {
26052         event.preventDefault();
26053         event.stopPropagation();
26054         var MuteToggle = Component.getComponent('MuteToggle');
26055         MuteToggle.prototype.handleClick.call(this, event);
26056       } else if (playPauseKey.call(this, event)) {
26057         event.preventDefault();
26058         event.stopPropagation();
26059         var PlayToggle = Component.getComponent('PlayToggle');
26060         PlayToggle.prototype.handleClick.call(this, event);
26061       }
26062     }
26063     /**
26064      * Check whether the player can play a given mimetype
26065      *
26066      * @see https://www.w3.org/TR/2011/WD-html5-20110113/video.html#dom-navigator-canplaytype
26067      *
26068      * @param {string} type
26069      *        The mimetype to check
26070      *
26071      * @return {string}
26072      *         'probably', 'maybe', or '' (empty string)
26073      */
26074     ;
26075 
26076     _proto.canPlayType = function canPlayType(type) {
26077       var can; // Loop through each playback technology in the options order
26078 
26079       for (var i = 0, j = this.options_.techOrder; i < j.length; i++) {
26080         var techName = j[i];
26081         var tech = Tech.getTech(techName); // Support old behavior of techs being registered as components.
26082         // Remove once that deprecated behavior is removed.
26083 
26084         if (!tech) {
26085           tech = Component.getComponent(techName);
26086         } // Check if the current tech is defined before continuing
26087 
26088 
26089         if (!tech) {
26090           log.error("The \"" + techName + "\" tech is undefined. Skipped browser support check for that tech.");
26091           continue;
26092         } // Check if the browser supports this technology
26093 
26094 
26095         if (tech.isSupported()) {
26096           can = tech.canPlayType(type);
26097 
26098           if (can) {
26099             return can;
26100           }
26101         }
26102       }
26103 
26104       return '';
26105     }
26106     /**
26107      * Select source based on tech-order or source-order
26108      * Uses source-order selection if `options.sourceOrder` is truthy. Otherwise,
26109      * defaults to tech-order selection
26110      *
26111      * @param {Array} sources
26112      *        The sources for a media asset
26113      *
26114      * @return {Object|boolean}
26115      *         Object of source and tech order or false
26116      */
26117     ;
26118 
26119     _proto.selectSource = function selectSource(sources) {
26120       var _this10 = this;
26121 
26122       // Get only the techs specified in `techOrder` that exist and are supported by the
26123       // current platform
26124       var techs = this.options_.techOrder.map(function (techName) {
26125         return [techName, Tech.getTech(techName)];
26126       }).filter(function (_ref) {
26127         var techName = _ref[0],
26128             tech = _ref[1];
26129 
26130         // Check if the current tech is defined before continuing
26131         if (tech) {
26132           // Check if the browser supports this technology
26133           return tech.isSupported();
26134         }
26135 
26136         log.error("The \"" + techName + "\" tech is undefined. Skipped browser support check for that tech.");
26137         return false;
vendor: 5,368 bytes, lines 26138-26297
26138       }); // Iterate over each `innerArray` element once per `outerArray` element and execute
26139       // `tester` with both. If `tester` returns a non-falsy value, exit early and return
26140       // that value.
26141 
26142       var findFirstPassingTechSourcePair = function findFirstPassingTechSourcePair(outerArray, innerArray, tester) {
26143         var found;
26144         outerArray.some(function (outerChoice) {
26145           return innerArray.some(function (innerChoice) {
26146             found = tester(outerChoice, innerChoice);
26147 
26148             if (found) {
26149               return true;
26150             }
26151           });
26152         });
26153         return found;
26154       };
26155 
26156       var foundSourceAndTech;
26157 
26158       var flip = function flip(fn) {
26159         return function (a, b) {
26160           return fn(b, a);
26161         };
26162       };
26163 
26164       var finder = function finder(_ref2, source) {
26165         var techName = _ref2[0],
26166             tech = _ref2[1];
26167 
26168         if (tech.canPlaySource(source, _this10.options_[techName.toLowerCase()])) {
26169           return {
26170             source: source,
26171             tech: techName
26172           };
26173         }
26174       }; // Depending on the truthiness of `options.sourceOrder`, we swap the order of techs and sources
26175       // to select from them based on their priority.
26176 
26177 
26178       if (this.options_.sourceOrder) {
26179         // Source-first ordering
26180         foundSourceAndTech = findFirstPassingTechSourcePair(sources, techs, flip(finder));
26181       } else {
26182         // Tech-first ordering
26183         foundSourceAndTech = findFirstPassingTechSourcePair(techs, sources, finder);
26184       }
26185 
26186       return foundSourceAndTech || false;
26187     }
26188     /**
26189      * Get or set the video source.
26190      *
26191      * @param {Tech~SourceObject|Tech~SourceObject[]|string} [source]
26192      *        A SourceObject, an array of SourceObjects, or a string referencing
26193      *        a URL to a media source. It is _highly recommended_ that an object
26194      *        or array of objects is used here, so that source selection
26195      *        algorithms can take the `type` into account.
26196      *
26197      *        If not provided, this method acts as a getter.
26198      *
26199      * @return {string|undefined}
26200      *         If the `source` argument is missing, returns the current source
26201      *         URL. Otherwise, returns nothing/undefined.
26202      */
26203     ;
26204 
26205     _proto.src = function src(source) {
26206       var _this11 = this;
26207 
26208       // getter usage
26209       if (typeof source === 'undefined') {
26210         return this.cache_.src || '';
26211       } // filter out invalid sources and turn our source into
26212       // an array of source objects
26213 
26214 
26215       var sources = filterSource(source); // if a source was passed in then it is invalid because
26216       // it was filtered to a zero length Array. So we have to
26217       // show an error
26218 
26219       if (!sources.length) {
26220         this.setTimeout(function () {
26221           this.error({
26222             code: 4,
26223             message: this.localize(this.options_.notSupportedMessage)
26224           });
26225         }, 0);
26226         return;
26227       } // intial sources
26228 
26229 
26230       this.changingSrc_ = true;
26231       this.cache_.sources = sources;
26232       this.updateSourceCaches_(sources[0]); // middlewareSource is the source after it has been changed by middleware
26233 
26234       setSource(this, sources[0], function (middlewareSource, mws) {
26235         _this11.middleware_ = mws; // since sourceSet is async we have to update the cache again after we select a source since
26236         // the source that is selected could be out of order from the cache update above this callback.
26237 
26238         _this11.cache_.sources = sources;
26239 
26240         _this11.updateSourceCaches_(middlewareSource);
26241 
26242         var err = _this11.src_(middlewareSource);
26243 
26244         if (err) {
26245           if (sources.length > 1) {
26246             return _this11.src(sources.slice(1));
26247           }
26248 
26249           _this11.changingSrc_ = false; // We need to wrap this in a timeout to give folks a chance to add error event handlers
26250 
26251           _this11.setTimeout(function () {
26252             this.error({
26253               code: 4,
26254               message: this.localize(this.options_.notSupportedMessage)
26255             });
26256           }, 0); // we could not find an appropriate tech, but let's still notify the delegate that this is it
26257           // this needs a better comment about why this is needed
26258 
26259 
26260           _this11.triggerReady();
26261 
26262           return;
26263         }
26264 
26265         setTech(mws, _this11.tech_);
26266       });
26267     }
26268     /**
26269      * Set the source object on the tech, returns a boolean that indicates whether
26270      * there is a tech that can play the source or not
26271      *
26272      * @param {Tech~SourceObject} source
26273      *        The source object to set on the Tech
26274      *
26275      * @return {boolean}
26276      *         - True if there is no Tech to playback this source
26277      *         - False otherwise
26278      *
26279      * @private
26280      */
26281     ;
26282 
26283     _proto.src_ = function src_(source) {
26284       var _this12 = this;
26285 
26286       var sourceTech = this.selectSource([source]);
26287 
26288       if (!sourceTech) {
26289         return true;
26290       }
26291 
26292       if (!titleCaseEquals(sourceTech.tech, this.techName_)) {
26293         this.changingSrc_ = true; // load this technology with the chosen source
26294 
26295         this.loadTech_(sourceTech.tech, sourceTech.source);
26296         this.tech_.ready(function () {
26297           _this12.changingSrc_ = false;
vendor: 37,911 bytes, lines 26298-27480
26298         });
26299         return false;
26300       } // wait until the tech is ready to set the source
26301       // and set it synchronously if possible (#2326)
26302 
26303 
26304       this.ready(function () {
26305         // The setSource tech method was added with source handlers
26306         // so older techs won't support it
26307         // We need to check the direct prototype for the case where subclasses
26308         // of the tech do not support source handlers
26309         if (this.tech_.constructor.prototype.hasOwnProperty('setSource')) {
26310           this.techCall_('setSource', source);
26311         } else {
26312           this.techCall_('src', source.src);
26313         }
26314 
26315         this.changingSrc_ = false;
26316       }, true);
26317       return false;
26318     }
26319     /**
26320      * Begin loading the src data.
26321      */
26322     ;
26323 
26324     _proto.load = function load() {
26325       this.techCall_('load');
26326     }
26327     /**
26328      * Reset the player. Loads the first tech in the techOrder,
26329      * removes all the text tracks in the existing `tech`,
26330      * and calls `reset` on the `tech`.
26331      */
26332     ;
26333 
26334     _proto.reset = function reset() {
26335       var _this13 = this;
26336 
26337       var PromiseClass = this.options_.Promise || window$1.Promise;
26338 
26339       if (this.paused() || !PromiseClass) {
26340         this.doReset_();
26341       } else {
26342         var playPromise = this.play();
26343         silencePromise(playPromise.then(function () {
26344           return _this13.doReset_();
26345         }));
26346       }
26347     };
26348 
26349     _proto.doReset_ = function doReset_() {
26350       if (this.tech_) {
26351         this.tech_.clearTracks('text');
26352       }
26353 
26354       this.resetCache_();
26355       this.poster('');
26356       this.loadTech_(this.options_.techOrder[0], null);
26357       this.techCall_('reset');
26358       this.resetControlBarUI_();
26359 
26360       if (isEvented(this)) {
26361         this.trigger('playerreset');
26362       }
26363     }
26364     /**
26365      * Reset Control Bar's UI by calling sub-methods that reset
26366      * all of Control Bar's components
26367      */
26368     ;
26369 
26370     _proto.resetControlBarUI_ = function resetControlBarUI_() {
26371       this.resetProgressBar_();
26372       this.resetPlaybackRate_();
26373       this.resetVolumeBar_();
26374     }
26375     /**
26376      * Reset tech's progress so progress bar is reset in the UI
26377      */
26378     ;
26379 
26380     _proto.resetProgressBar_ = function resetProgressBar_() {
26381       this.currentTime(0);
26382       var _this$controlBar = this.controlBar,
26383           durationDisplay = _this$controlBar.durationDisplay,
26384           remainingTimeDisplay = _this$controlBar.remainingTimeDisplay;
26385 
26386       if (durationDisplay) {
26387         durationDisplay.updateContent();
26388       }
26389 
26390       if (remainingTimeDisplay) {
26391         remainingTimeDisplay.updateContent();
26392       }
26393     }
26394     /**
26395      * Reset Playback ratio
26396      */
26397     ;
26398 
26399     _proto.resetPlaybackRate_ = function resetPlaybackRate_() {
26400       this.playbackRate(this.defaultPlaybackRate());
26401       this.handleTechRateChange_();
26402     }
26403     /**
26404      * Reset Volume bar
26405      */
26406     ;
26407 
26408     _proto.resetVolumeBar_ = function resetVolumeBar_() {
26409       this.volume(1.0);
26410       this.trigger('volumechange');
26411     }
26412     /**
26413      * Returns all of the current source objects.
26414      *
26415      * @return {Tech~SourceObject[]}
26416      *         The current source objects
26417      */
26418     ;
26419 
26420     _proto.currentSources = function currentSources() {
26421       var source = this.currentSource();
26422       var sources = []; // assume `{}` or `{ src }`
26423 
26424       if (Object.keys(source).length !== 0) {
26425         sources.push(source);
26426       }
26427 
26428       return this.cache_.sources || sources;
26429     }
26430     /**
26431      * Returns the current source object.
26432      *
26433      * @return {Tech~SourceObject}
26434      *         The current source object
26435      */
26436     ;
26437 
26438     _proto.currentSource = function currentSource() {
26439       return this.cache_.source || {};
26440     }
26441     /**
26442      * Returns the fully qualified URL of the current source value e.g. http://mysite.com/video.mp4
26443      * Can be used in conjunction with `currentType` to assist in rebuilding the current source object.
26444      *
26445      * @return {string}
26446      *         The current source
26447      */
26448     ;
26449 
26450     _proto.currentSrc = function currentSrc() {
26451       return this.currentSource() && this.currentSource().src || '';
26452     }
26453     /**
26454      * Get the current source type e.g. video/mp4
26455      * This can allow you rebuild the current source object so that you could load the same
26456      * source and tech later
26457      *
26458      * @return {string}
26459      *         The source MIME type
26460      */
26461     ;
26462 
26463     _proto.currentType = function currentType() {
26464       return this.currentSource() && this.currentSource().type || '';
26465     }
26466     /**
26467      * Get or set the preload attribute
26468      *
26469      * @param {boolean} [value]
26470      *        - true means that we should preload
26471      *        - false means that we should not preload
26472      *
26473      * @return {string}
26474      *         The preload attribute value when getting
26475      */
26476     ;
26477 
26478     _proto.preload = function preload(value) {
26479       if (value !== undefined) {
26480         this.techCall_('setPreload', value);
26481         this.options_.preload = value;
26482         return;
26483       }
26484 
26485       return this.techGet_('preload');
26486     }
26487     /**
26488      * Get or set the autoplay option. When this is a boolean it will
26489      * modify the attribute on the tech. When this is a string the attribute on
26490      * the tech will be removed and `Player` will handle autoplay on loadstarts.
26491      *
26492      * @param {boolean|string} [value]
26493      *        - true: autoplay using the browser behavior
26494      *        - false: do not autoplay
26495      *        - 'play': call play() on every loadstart
26496      *        - 'muted': call muted() then play() on every loadstart
26497      *        - 'any': call play() on every loadstart. if that fails call muted() then play().
26498      *        - *: values other than those listed here will be set `autoplay` to true
26499      *
26500      * @return {boolean|string}
26501      *         The current value of autoplay when getting
26502      */
26503     ;
26504 
26505     _proto.autoplay = function autoplay(value) {
26506       // getter usage
26507       if (value === undefined) {
26508         return this.options_.autoplay || false;
26509       }
26510 
26511       var techAutoplay; // if the value is a valid string set it to that
26512 
26513       if (typeof value === 'string' && /(any|play|muted)/.test(value)) {
26514         this.options_.autoplay = value;
26515         this.manualAutoplay_(value);
26516         techAutoplay = false; // any falsy value sets autoplay to false in the browser,
26517         // lets do the same
26518       } else if (!value) {
26519         this.options_.autoplay = false; // any other value (ie truthy) sets autoplay to true
26520       } else {
26521         this.options_.autoplay = true;
26522       }
26523 
26524       techAutoplay = typeof techAutoplay === 'undefined' ? this.options_.autoplay : techAutoplay; // if we don't have a tech then we do not queue up
26525       // a setAutoplay call on tech ready. We do this because the
26526       // autoplay option will be passed in the constructor and we
26527       // do not need to set it twice
26528 
26529       if (this.tech_) {
26530         this.techCall_('setAutoplay', techAutoplay);
26531       }
26532     }
26533     /**
26534      * Set or unset the playsinline attribute.
26535      * Playsinline tells the browser that non-fullscreen playback is preferred.
26536      *
26537      * @param {boolean} [value]
26538      *        - true means that we should try to play inline by default
26539      *        - false means that we should use the browser's default playback mode,
26540      *          which in most cases is inline. iOS Safari is a notable exception
26541      *          and plays fullscreen by default.
26542      *
26543      * @return {string|Player}
26544      *         - the current value of playsinline
26545      *         - the player when setting
26546      *
26547      * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
26548      */
26549     ;
26550 
26551     _proto.playsinline = function playsinline(value) {
26552       if (value !== undefined) {
26553         this.techCall_('setPlaysinline', value);
26554         this.options_.playsinline = value;
26555         return this;
26556       }
26557 
26558       return this.techGet_('playsinline');
26559     }
26560     /**
26561      * Get or set the loop attribute on the video element.
26562      *
26563      * @param {boolean} [value]
26564      *        - true means that we should loop the video
26565      *        - false means that we should not loop the video
26566      *
26567      * @return {boolean}
26568      *         The current value of loop when getting
26569      */
26570     ;
26571 
26572     _proto.loop = function loop(value) {
26573       if (value !== undefined) {
26574         this.techCall_('setLoop', value);
26575         this.options_.loop = value;
26576         return;
26577       }
26578 
26579       return this.techGet_('loop');
26580     }
26581     /**
26582      * Get or set the poster image source url
26583      *
26584      * @fires Player#posterchange
26585      *
26586      * @param {string} [src]
26587      *        Poster image source URL
26588      *
26589      * @return {string}
26590      *         The current value of poster when getting
26591      */
26592     ;
26593 
26594     _proto.poster = function poster(src) {
26595       if (src === undefined) {
26596         return this.poster_;
26597       } // The correct way to remove a poster is to set as an empty string
26598       // other falsey values will throw errors
26599 
26600 
26601       if (!src) {
26602         src = '';
26603       }
26604 
26605       if (src === this.poster_) {
26606         return;
26607       } // update the internal poster variable
26608 
26609 
26610       this.poster_ = src; // update the tech's poster
26611 
26612       this.techCall_('setPoster', src);
26613       this.isPosterFromTech_ = false; // alert components that the poster has been set
26614 
26615       /**
26616        * This event fires when the poster image is changed on the player.
26617        *
26618        * @event Player#posterchange
26619        * @type {EventTarget~Event}
26620        */
26621 
26622       this.trigger('posterchange');
26623     }
26624     /**
26625      * Some techs (e.g. YouTube) can provide a poster source in an
26626      * asynchronous way. We want the poster component to use this
26627      * poster source so that it covers up the tech's controls.
26628      * (YouTube's play button). However we only want to use this
26629      * source if the player user hasn't set a poster through
26630      * the normal APIs.
26631      *
26632      * @fires Player#posterchange
26633      * @listens Tech#posterchange
26634      * @private
26635      */
26636     ;
26637 
26638     _proto.handleTechPosterChange_ = function handleTechPosterChange_() {
26639       if ((!this.poster_ || this.options_.techCanOverridePoster) && this.tech_ && this.tech_.poster) {
26640         var newPoster = this.tech_.poster() || '';
26641 
26642         if (newPoster !== this.poster_) {
26643           this.poster_ = newPoster;
26644           this.isPosterFromTech_ = true; // Let components know the poster has changed
26645 
26646           this.trigger('posterchange');
26647         }
26648       }
26649     }
26650     /**
26651      * Get or set whether or not the controls are showing.
26652      *
26653      * @fires Player#controlsenabled
26654      *
26655      * @param {boolean} [bool]
26656      *        - true to turn controls on
26657      *        - false to turn controls off
26658      *
26659      * @return {boolean}
26660      *         The current value of controls when getting
26661      */
26662     ;
26663 
26664     _proto.controls = function controls(bool) {
26665       if (bool === undefined) {
26666         return !!this.controls_;
26667       }
26668 
26669       bool = !!bool; // Don't trigger a change event unless it actually changed
26670 
26671       if (this.controls_ === bool) {
26672         return;
26673       }
26674 
26675       this.controls_ = bool;
26676 
26677       if (this.usingNativeControls()) {
26678         this.techCall_('setControls', bool);
26679       }
26680 
26681       if (this.controls_) {
26682         this.removeClass('vjs-controls-disabled');
26683         this.addClass('vjs-controls-enabled');
26684         /**
26685          * @event Player#controlsenabled
26686          * @type {EventTarget~Event}
26687          */
26688 
26689         this.trigger('controlsenabled');
26690 
26691         if (!this.usingNativeControls()) {
26692           this.addTechControlsListeners_();
26693         }
26694       } else {
26695         this.removeClass('vjs-controls-enabled');
26696         this.addClass('vjs-controls-disabled');
26697         /**
26698          * @event Player#controlsdisabled
26699          * @type {EventTarget~Event}
26700          */
26701 
26702         this.trigger('controlsdisabled');
26703 
26704         if (!this.usingNativeControls()) {
26705           this.removeTechControlsListeners_();
26706         }
26707       }
26708     }
26709     /**
26710      * Toggle native controls on/off. Native controls are the controls built into
26711      * devices (e.g. default iPhone controls), Flash, or other techs
26712      * (e.g. Vimeo Controls)
26713      * **This should only be set by the current tech, because only the tech knows
26714      * if it can support native controls**
26715      *
26716      * @fires Player#usingnativecontrols
26717      * @fires Player#usingcustomcontrols
26718      *
26719      * @param {boolean} [bool]
26720      *        - true to turn native controls on
26721      *        - false to turn native controls off
26722      *
26723      * @return {boolean}
26724      *         The current value of native controls when getting
26725      */
26726     ;
26727 
26728     _proto.usingNativeControls = function usingNativeControls(bool) {
26729       if (bool === undefined) {
26730         return !!this.usingNativeControls_;
26731       }
26732 
26733       bool = !!bool; // Don't trigger a change event unless it actually changed
26734 
26735       if (this.usingNativeControls_ === bool) {
26736         return;
26737       }
26738 
26739       this.usingNativeControls_ = bool;
26740 
26741       if (this.usingNativeControls_) {
26742         this.addClass('vjs-using-native-controls');
26743         /**
26744          * player is using the native device controls
26745          *
26746          * @event Player#usingnativecontrols
26747          * @type {EventTarget~Event}
26748          */
26749 
26750         this.trigger('usingnativecontrols');
26751       } else {
26752         this.removeClass('vjs-using-native-controls');
26753         /**
26754          * player is using the custom HTML controls
26755          *
26756          * @event Player#usingcustomcontrols
26757          * @type {EventTarget~Event}
26758          */
26759 
26760         this.trigger('usingcustomcontrols');
26761       }
26762     }
26763     /**
26764      * Set or get the current MediaError
26765      *
26766      * @fires Player#error
26767      *
26768      * @param  {MediaError|string|number} [err]
26769      *         A MediaError or a string/number to be turned
26770      *         into a MediaError
26771      *
26772      * @return {MediaError|null}
26773      *         The current MediaError when getting (or null)
26774      */
26775     ;
26776 
26777     _proto.error = function error(err) {
26778       if (err === undefined) {
26779         return this.error_ || null;
26780       } // Suppress the first error message for no compatible source until
26781       // user interaction
26782 
26783 
26784       if (this.options_.suppressNotSupportedError && err && err.code === 4) {
26785         var triggerSuppressedError = function triggerSuppressedError() {
26786           this.error(err);
26787         };
26788 
26789         this.options_.suppressNotSupportedError = false;
26790         this.any(['click', 'touchstart'], triggerSuppressedError);
26791         this.one('loadstart', function () {
26792           this.off(['click', 'touchstart'], triggerSuppressedError);
26793         });
26794         return;
26795       } // restoring to default
26796 
26797 
26798       if (err === null) {
26799         this.error_ = err;
26800         this.removeClass('vjs-error');
26801 
26802         if (this.errorDisplay) {
26803           this.errorDisplay.close();
26804         }
26805 
26806         return;
26807       }
26808 
26809       this.error_ = new MediaError(err); // add the vjs-error classname to the player
26810 
26811       this.addClass('vjs-error'); // log the name of the error type and any message
26812       // IE11 logs "[object object]" and required you to expand message to see error object
26813 
26814       log.error("(CODE:" + this.error_.code + " " + MediaError.errorTypes[this.error_.code] + ")", this.error_.message, this.error_);
26815       /**
26816        * @event Player#error
26817        * @type {EventTarget~Event}
26818        */
26819 
26820       this.trigger('error');
26821       return;
26822     }
26823     /**
26824      * Report user activity
26825      *
26826      * @param {Object} event
26827      *        Event object
26828      */
26829     ;
26830 
26831     _proto.reportUserActivity = function reportUserActivity(event) {
26832       this.userActivity_ = true;
26833     }
26834     /**
26835      * Get/set if user is active
26836      *
26837      * @fires Player#useractive
26838      * @fires Player#userinactive
26839      *
26840      * @param {boolean} [bool]
26841      *        - true if the user is active
26842      *        - false if the user is inactive
26843      *
26844      * @return {boolean}
26845      *         The current value of userActive when getting
26846      */
26847     ;
26848 
26849     _proto.userActive = function userActive(bool) {
26850       if (bool === undefined) {
26851         return this.userActive_;
26852       }
26853 
26854       bool = !!bool;
26855 
26856       if (bool === this.userActive_) {
26857         return;
26858       }
26859 
26860       this.userActive_ = bool;
26861 
26862       if (this.userActive_) {
26863         this.userActivity_ = true;
26864         this.removeClass('vjs-user-inactive');
26865         this.addClass('vjs-user-active');
26866         /**
26867          * @event Player#useractive
26868          * @type {EventTarget~Event}
26869          */
26870 
26871         this.trigger('useractive');
26872         return;
26873       } // Chrome/Safari/IE have bugs where when you change the cursor it can
26874       // trigger a mousemove event. This causes an issue when you're hiding
26875       // the cursor when the user is inactive, and a mousemove signals user
26876       // activity. Making it impossible to go into inactive mode. Specifically
26877       // this happens in fullscreen when we really need to hide the cursor.
26878       //
26879       // When this gets resolved in ALL browsers it can be removed
26880       // https://code.google.com/p/chromium/issues/detail?id=103041
26881 
26882 
26883       if (this.tech_) {
26884         this.tech_.one('mousemove', function (e) {
26885           e.stopPropagation();
26886           e.preventDefault();
26887         });
26888       }
26889 
26890       this.userActivity_ = false;
26891       this.removeClass('vjs-user-active');
26892       this.addClass('vjs-user-inactive');
26893       /**
26894        * @event Player#userinactive
26895        * @type {EventTarget~Event}
26896        */
26897 
26898       this.trigger('userinactive');
26899     }
26900     /**
26901      * Listen for user activity based on timeout value
26902      *
26903      * @private
26904      */
26905     ;
26906 
26907     _proto.listenForUserActivity_ = function listenForUserActivity_() {
26908       var mouseInProgress;
26909       var lastMoveX;
26910       var lastMoveY;
26911       var handleActivity = bind(this, this.reportUserActivity);
26912 
26913       var handleMouseMove = function handleMouseMove(e) {
26914         // #1068 - Prevent mousemove spamming
26915         // Chrome Bug: https://code.google.com/p/chromium/issues/detail?id=366970
26916         if (e.screenX !== lastMoveX || e.screenY !== lastMoveY) {
26917           lastMoveX = e.screenX;
26918           lastMoveY = e.screenY;
26919           handleActivity();
26920         }
26921       };
26922 
26923       var handleMouseDown = function handleMouseDown() {
26924         handleActivity(); // For as long as the they are touching the device or have their mouse down,
26925         // we consider them active even if they're not moving their finger or mouse.
26926         // So we want to continue to update that they are active
26927 
26928         this.clearInterval(mouseInProgress); // Setting userActivity=true now and setting the interval to the same time
26929         // as the activityCheck interval (250) should ensure we never miss the
26930         // next activityCheck
26931 
26932         mouseInProgress = this.setInterval(handleActivity, 250);
26933       };
26934 
26935       var handleMouseUp = function handleMouseUp(event) {
26936         handleActivity(); // Stop the interval that maintains activity if the mouse/touch is down
26937 
26938         this.clearInterval(mouseInProgress);
26939       }; // Any mouse movement will be considered user activity
26940 
26941 
26942       this.on('mousedown', handleMouseDown);
26943       this.on('mousemove', handleMouseMove);
26944       this.on('mouseup', handleMouseUp);
26945       var controlBar = this.getChild('controlBar'); // Fixes bug on Android & iOS where when tapping progressBar (when control bar is displayed)
26946       // controlBar would no longer be hidden by default timeout.
26947 
26948       if (controlBar && !IS_IOS && !IS_ANDROID) {
26949         controlBar.on('mouseenter', function (event) {
26950           this.player().cache_.inactivityTimeout = this.player().options_.inactivityTimeout;
26951           this.player().options_.inactivityTimeout = 0;
26952         });
26953         controlBar.on('mouseleave', function (event) {
26954           this.player().options_.inactivityTimeout = this.player().cache_.inactivityTimeout;
26955         });
26956       } // Listen for keyboard navigation
26957       // Shouldn't need to use inProgress interval because of key repeat
26958 
26959 
26960       this.on('keydown', handleActivity);
26961       this.on('keyup', handleActivity); // Run an interval every 250 milliseconds instead of stuffing everything into
26962       // the mousemove/touchmove function itself, to prevent performance degradation.
26963       // `this.reportUserActivity` simply sets this.userActivity_ to true, which
26964       // then gets picked up by this loop
26965       // http://ejohn.org/blog/learning-from-twitter/
26966 
26967       var inactivityTimeout;
26968       this.setInterval(function () {
26969         // Check to see if mouse/touch activity has happened
26970         if (!this.userActivity_) {
26971           return;
26972         } // Reset the activity tracker
26973 
26974 
26975         this.userActivity_ = false; // If the user state was inactive, set the state to active
26976 
26977         this.userActive(true); // Clear any existing inactivity timeout to start the timer over
26978 
26979         this.clearTimeout(inactivityTimeout);
26980         var timeout = this.options_.inactivityTimeout;
26981 
26982         if (timeout <= 0) {
26983           return;
26984         } // In <timeout> milliseconds, if no more activity has occurred the
26985         // user will be considered inactive
26986 
26987 
26988         inactivityTimeout = this.setTimeout(function () {
26989           // Protect against the case where the inactivityTimeout can trigger just
26990           // before the next user activity is picked up by the activity check loop
26991           // causing a flicker
26992           if (!this.userActivity_) {
26993             this.userActive(false);
26994           }
26995         }, timeout);
26996       }, 250);
26997     }
26998     /**
26999      * Gets or sets the current playback rate. A playback rate of
27000      * 1.0 represents normal speed and 0.5 would indicate half-speed
27001      * playback, for instance.
27002      *
27003      * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-playbackrate
27004      *
27005      * @param {number} [rate]
27006      *       New playback rate to set.
27007      *
27008      * @return {number}
27009      *         The current playback rate when getting or 1.0
27010      */
27011     ;
27012 
27013     _proto.playbackRate = function playbackRate(rate) {
27014       if (rate !== undefined) {
27015         // NOTE: this.cache_.lastPlaybackRate is set from the tech handler
27016         // that is registered above
27017         this.techCall_('setPlaybackRate', rate);
27018         return;
27019       }
27020 
27021       if (this.tech_ && this.tech_.featuresPlaybackRate) {
27022         return this.cache_.lastPlaybackRate || this.techGet_('playbackRate');
27023       }
27024 
27025       return 1.0;
27026     }
27027     /**
27028      * Gets or sets the current default playback rate. A default playback rate of
27029      * 1.0 represents normal speed and 0.5 would indicate half-speed playback, for instance.
27030      * defaultPlaybackRate will only represent what the initial playbackRate of a video was, not
27031      * not the current playbackRate.
27032      *
27033      * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-defaultplaybackrate
27034      *
27035      * @param {number} [rate]
27036      *       New default playback rate to set.
27037      *
27038      * @return {number|Player}
27039      *         - The default playback rate when getting or 1.0
27040      *         - the player when setting
27041      */
27042     ;
27043 
27044     _proto.defaultPlaybackRate = function defaultPlaybackRate(rate) {
27045       if (rate !== undefined) {
27046         return this.techCall_('setDefaultPlaybackRate', rate);
27047       }
27048 
27049       if (this.tech_ && this.tech_.featuresPlaybackRate) {
27050         return this.techGet_('defaultPlaybackRate');
27051       }
27052 
27053       return 1.0;
27054     }
27055     /**
27056      * Gets or sets the audio flag
27057      *
27058      * @param {boolean} bool
27059      *        - true signals that this is an audio player
27060      *        - false signals that this is not an audio player
27061      *
27062      * @return {boolean}
27063      *         The current value of isAudio when getting
27064      */
27065     ;
27066 
27067     _proto.isAudio = function isAudio(bool) {
27068       if (bool !== undefined) {
27069         this.isAudio_ = !!bool;
27070         return;
27071       }
27072 
27073       return !!this.isAudio_;
27074     }
27075     /**
27076      * A helper method for adding a {@link TextTrack} to our
27077      * {@link TextTrackList}.
27078      *
27079      * In addition to the W3C settings we allow adding additional info through options.
27080      *
27081      * @see http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-addtexttrack
27082      *
27083      * @param {string} [kind]
27084      *        the kind of TextTrack you are adding
27085      *
27086      * @param {string} [label]
27087      *        the label to give the TextTrack label
27088      *
27089      * @param {string} [language]
27090      *        the language to set on the TextTrack
27091      *
27092      * @return {TextTrack|undefined}
27093      *         the TextTrack that was added or undefined
27094      *         if there is no tech
27095      */
27096     ;
27097 
27098     _proto.addTextTrack = function addTextTrack(kind, label, language) {
27099       if (this.tech_) {
27100         return this.tech_.addTextTrack(kind, label, language);
27101       }
27102     }
27103     /**
27104      * Create a remote {@link TextTrack} and an {@link HTMLTrackElement}. It will
27105      * automatically removed from the video element whenever the source changes, unless
27106      * manualCleanup is set to false.
27107      *
27108      * @param {Object} options
27109      *        Options to pass to {@link HTMLTrackElement} during creation. See
27110      *        {@link HTMLTrackElement} for object properties that you should use.
27111      *
27112      * @param {boolean} [manualCleanup=true] if set to false, the TextTrack will be
27113      *
27114      * @return {HtmlTrackElement}
27115      *         the HTMLTrackElement that was created and added
27116      *         to the HtmlTrackElementList and the remote
27117      *         TextTrackList
27118      *
27119      * @deprecated The default value of the "manualCleanup" parameter will default
27120      *             to "false" in upcoming versions of Video.js
27121      */
27122     ;
27123 
27124     _proto.addRemoteTextTrack = function addRemoteTextTrack(options, manualCleanup) {
27125       if (this.tech_) {
27126         return this.tech_.addRemoteTextTrack(options, manualCleanup);
27127       }
27128     }
27129     /**
27130      * Remove a remote {@link TextTrack} from the respective
27131      * {@link TextTrackList} and {@link HtmlTrackElementList}.
27132      *
27133      * @param {Object} track
27134      *        Remote {@link TextTrack} to remove
27135      *
27136      * @return {undefined}
27137      *         does not return anything
27138      */
27139     ;
27140 
27141     _proto.removeRemoteTextTrack = function removeRemoteTextTrack(obj) {
27142       if (obj === void 0) {
27143         obj = {};
27144       }
27145 
27146       var _obj = obj,
27147           track = _obj.track;
27148 
27149       if (!track) {
27150         track = obj;
27151       } // destructure the input into an object with a track argument, defaulting to arguments[0]
27152       // default the whole argument to an empty object if nothing was passed in
27153 
27154 
27155       if (this.tech_) {
27156         return this.tech_.removeRemoteTextTrack(track);
27157       }
27158     }
27159     /**
27160      * Gets available media playback quality metrics as specified by the W3C's Media
27161      * Playback Quality API.
27162      *
27163      * @see [Spec]{@link https://wicg.github.io/media-playback-quality}
27164      *
27165      * @return {Object|undefined}
27166      *         An object with supported media playback quality metrics or undefined if there
27167      *         is no tech or the tech does not support it.
27168      */
27169     ;
27170 
27171     _proto.getVideoPlaybackQuality = function getVideoPlaybackQuality() {
27172       return this.techGet_('getVideoPlaybackQuality');
27173     }
27174     /**
27175      * Get video width
27176      *
27177      * @return {number}
27178      *         current video width
27179      */
27180     ;
27181 
27182     _proto.videoWidth = function videoWidth() {
27183       return this.tech_ && this.tech_.videoWidth && this.tech_.videoWidth() || 0;
27184     }
27185     /**
27186      * Get video height
27187      *
27188      * @return {number}
27189      *         current video height
27190      */
27191     ;
27192 
27193     _proto.videoHeight = function videoHeight() {
27194       return this.tech_ && this.tech_.videoHeight && this.tech_.videoHeight() || 0;
27195     }
27196     /**
27197      * The player's language code
27198      * NOTE: The language should be set in the player options if you want the
27199      * the controls to be built with a specific language. Changing the language
27200      * later will not update controls text.
27201      *
27202      * @param {string} [code]
27203      *        the language code to set the player to
27204      *
27205      * @return {string}
27206      *         The current language code when getting
27207      */
27208     ;
27209 
27210     _proto.language = function language(code) {
27211       if (code === undefined) {
27212         return this.language_;
27213       }
27214 
27215       this.language_ = String(code).toLowerCase();
27216     }
27217     /**
27218      * Get the player's language dictionary
27219      * Merge every time, because a newly added plugin might call videojs.addLanguage() at any time
27220      * Languages specified directly in the player options have precedence
27221      *
27222      * @return {Array}
27223      *         An array of of supported languages
27224      */
27225     ;
27226 
27227     _proto.languages = function languages() {
27228       return mergeOptions(Player.prototype.options_.languages, this.languages_);
27229     }
27230     /**
27231      * returns a JavaScript object reperesenting the current track
27232      * information. **DOES not return it as JSON**
27233      *
27234      * @return {Object}
27235      *         Object representing the current of track info
27236      */
27237     ;
27238 
27239     _proto.toJSON = function toJSON() {
27240       var options = mergeOptions(this.options_);
27241       var tracks = options.tracks;
27242       options.tracks = [];
27243 
27244       for (var i = 0; i < tracks.length; i++) {
27245         var track = tracks[i]; // deep merge tracks and null out player so no circular references
27246 
27247         track = mergeOptions(track);
27248         track.player = undefined;
27249         options.tracks[i] = track;
27250       }
27251 
27252       return options;
27253     }
27254     /**
27255      * Creates a simple modal dialog (an instance of the {@link ModalDialog}
27256      * component) that immediately overlays the player with arbitrary
27257      * content and removes itself when closed.
27258      *
27259      * @param {string|Function|Element|Array|null} content
27260      *        Same as {@link ModalDialog#content}'s param of the same name.
27261      *        The most straight-forward usage is to provide a string or DOM
27262      *        element.
27263      *
27264      * @param {Object} [options]
27265      *        Extra options which will be passed on to the {@link ModalDialog}.
27266      *
27267      * @return {ModalDialog}
27268      *         the {@link ModalDialog} that was created
27269      */
27270     ;
27271 
27272     _proto.createModal = function createModal(content, options) {
27273       var _this14 = this;
27274 
27275       options = options || {};
27276       options.content = content || '';
27277       var modal = new ModalDialog(this, options);
27278       this.addChild(modal);
27279       modal.on('dispose', function () {
27280         _this14.removeChild(modal);
27281       });
27282       modal.open();
27283       return modal;
27284     }
27285     /**
27286      * Change breakpoint classes when the player resizes.
27287      *
27288      * @private
27289      */
27290     ;
27291 
27292     _proto.updateCurrentBreakpoint_ = function updateCurrentBreakpoint_() {
27293       if (!this.responsive()) {
27294         return;
27295       }
27296 
27297       var currentBreakpoint = this.currentBreakpoint();
27298       var currentWidth = this.currentWidth();
27299 
27300       for (var i = 0; i < BREAKPOINT_ORDER.length; i++) {
27301         var candidateBreakpoint = BREAKPOINT_ORDER[i];
27302         var maxWidth = this.breakpoints_[candidateBreakpoint];
27303 
27304         if (currentWidth <= maxWidth) {
27305           // The current breakpoint did not change, nothing to do.
27306           if (currentBreakpoint === candidateBreakpoint) {
27307             return;
27308           } // Only remove a class if there is a current breakpoint.
27309 
27310 
27311           if (currentBreakpoint) {
27312             this.removeClass(BREAKPOINT_CLASSES[currentBreakpoint]);
27313           }
27314 
27315           this.addClass(BREAKPOINT_CLASSES[candidateBreakpoint]);
27316           this.breakpoint_ = candidateBreakpoint;
27317           break;
27318         }
27319       }
27320     }
27321     /**
27322      * Removes the current breakpoint.
27323      *
27324      * @private
27325      */
27326     ;
27327 
27328     _proto.removeCurrentBreakpoint_ = function removeCurrentBreakpoint_() {
27329       var className = this.currentBreakpointClass();
27330       this.breakpoint_ = '';
27331 
27332       if (className) {
27333         this.removeClass(className);
27334       }
27335     }
27336     /**
27337      * Get or set breakpoints on the player.
27338      *
27339      * Calling this method with an object or `true` will remove any previous
27340      * custom breakpoints and start from the defaults again.
27341      *
27342      * @param  {Object|boolean} [breakpoints]
27343      *         If an object is given, it can be used to provide custom
27344      *         breakpoints. If `true` is given, will set default breakpoints.
27345      *         If this argument is not given, will simply return the current
27346      *         breakpoints.
27347      *
27348      * @param  {number} [breakpoints.tiny]
27349      *         The maximum width for the "vjs-layout-tiny" class.
27350      *
27351      * @param  {number} [breakpoints.xsmall]
27352      *         The maximum width for the "vjs-layout-x-small" class.
27353      *
27354      * @param  {number} [breakpoints.small]
27355      *         The maximum width for the "vjs-layout-small" class.
27356      *
27357      * @param  {number} [breakpoints.medium]
27358      *         The maximum width for the "vjs-layout-medium" class.
27359      *
27360      * @param  {number} [breakpoints.large]
27361      *         The maximum width for the "vjs-layout-large" class.
27362      *
27363      * @param  {number} [breakpoints.xlarge]
27364      *         The maximum width for the "vjs-layout-x-large" class.
27365      *
27366      * @param  {number} [breakpoints.huge]
27367      *         The maximum width for the "vjs-layout-huge" class.
27368      *
27369      * @return {Object}
27370      *         An object mapping breakpoint names to maximum width values.
27371      */
27372     ;
27373 
27374     _proto.breakpoints = function breakpoints(_breakpoints) {
27375       // Used as a getter.
27376       if (_breakpoints === undefined) {
27377         return assign(this.breakpoints_);
27378       }
27379 
27380       this.breakpoint_ = '';
27381       this.breakpoints_ = assign({}, DEFAULT_BREAKPOINTS, _breakpoints); // When breakpoint definitions change, we need to update the currently
27382       // selected breakpoint.
27383 
27384       this.updateCurrentBreakpoint_(); // Clone the breakpoints before returning.
27385 
27386       return assign(this.breakpoints_);
27387     }
27388     /**
27389      * Get or set a flag indicating whether or not this player should adjust
27390      * its UI based on its dimensions.
27391      *
27392      * @param  {boolean} value
27393      *         Should be `true` if the player should adjust its UI based on its
27394      *         dimensions; otherwise, should be `false`.
27395      *
27396      * @return {boolean}
27397      *         Will be `true` if this player should adjust its UI based on its
27398      *         dimensions; otherwise, will be `false`.
27399      */
27400     ;
27401 
27402     _proto.responsive = function responsive(value) {
27403       // Used as a getter.
27404       if (value === undefined) {
27405         return this.responsive_;
27406       }
27407 
27408       value = Boolean(value);
27409       var current = this.responsive_; // Nothing changed.
27410 
27411       if (value === current) {
27412         return;
27413       } // The value actually changed, set it.
27414 
27415 
27416       this.responsive_ = value; // Start listening for breakpoints and set the initial breakpoint if the
27417       // player is now responsive.
27418 
27419       if (value) {
27420         this.on('playerresize', this.updateCurrentBreakpoint_);
27421         this.updateCurrentBreakpoint_(); // Stop listening for breakpoints if the player is no longer responsive.
27422       } else {
27423         this.off('playerresize', this.updateCurrentBreakpoint_);
27424         this.removeCurrentBreakpoint_();
27425       }
27426 
27427       return value;
27428     }
27429     /**
27430      * Get current breakpoint name, if any.
27431      *
27432      * @return {string}
27433      *         If there is currently a breakpoint set, returns a the key from the
27434      *         breakpoints object matching it. Otherwise, returns an empty string.
27435      */
27436     ;
27437 
27438     _proto.currentBreakpoint = function currentBreakpoint() {
27439       return this.breakpoint_;
27440     }
27441     /**
27442      * Get the current breakpoint class name.
27443      *
27444      * @return {string}
27445      *         The matching class name (e.g. `"vjs-layout-tiny"` or
27446      *         `"vjs-layout-large"`) for the current breakpoint. Empty string if
27447      *         there is no current breakpoint.
27448      */
27449     ;
27450 
27451     _proto.currentBreakpointClass = function currentBreakpointClass() {
27452       return BREAKPOINT_CLASSES[this.breakpoint_] || '';
27453     }
27454     /**
27455      * An object that describes a single piece of media.
27456      *
27457      * Properties that are not part of this type description will be retained; so,
27458      * this can be viewed as a generic metadata storage mechanism as well.
27459      *
27460      * @see      {@link https://wicg.github.io/mediasession/#the-mediametadata-interface}
27461      * @typedef  {Object} Player~MediaObject
27462      *
27463      * @property {string} [album]
27464      *           Unused, except if this object is passed to the `MediaSession`
27465      *           API.
27466      *
27467      * @property {string} [artist]
27468      *           Unused, except if this object is passed to the `MediaSession`
27469      *           API.
27470      *
27471      * @property {Object[]} [artwork]
27472      *           Unused, except if this object is passed to the `MediaSession`
27473      *           API. If not specified, will be populated via the `poster`, if
27474      *           available.
27475      *
27476      * @property {string} [poster]
27477      *           URL to an image that will display before playback.
27478      *
27479      * @property {Tech~SourceObject|Tech~SourceObject[]|string} [src]
27480      *           A single source object, an array of source objects, or a string
27481      *           referencing a URL to a media source. It is _highly recommended_
27482      *           that an object or array of objects is used here, so that source
27483      *           selection algorithms can take the `type` into account.
27484      *
27485      * @property {string} [title]
27486      *           Unused, except if this object is passed to the `MediaSession`
27487      *           API.
27488      *
27489      * @property {Object[]} [textTracks]
27490      *           An array of objects to be used to create text tracks, following
27491      *           the {@link https://www.w3.org/TR/html50/embedded-content-0.html#the-track-element|native track element format}.
27492      *           For ease of removal, these will be created as "remote" text
27493      *           tracks and set to automatically clean up on source changes.
27494      *
27495      *           These objects may have properties like `src`, `kind`, `label`,
27496      *           and `language`, see {@link Tech#createRemoteTextTrack}.
27497      */
27498 
27499     /**
27500      * Populate the player using a {@link Player~MediaObject|MediaObject}.
27501      *
27502      * @param  {Player~MediaObject} media
27503      *         A media object.
27504      *
27505      * @param  {Function} ready
27506      *         A callback to be called when the player is ready.
27507      */
27508     ;
27509 
27510     _proto.loadMedia = function loadMedia(media, ready) {
27511       var _this15 = this;
27512 
27513       if (!media || typeof media !== 'object') {
27514         return;
27515       }
27516 
27517       this.reset(); // Clone the media object so it cannot be mutated from outside.
27518 
27519       this.cache_.media = mergeOptions(media);
27520       var _this$cache_$media = this.cache_.media,
27521           artwork = _this$cache_$media.artwork,
27522           poster = _this$cache_$media.poster,
27523           src = _this$cache_$media.src,
27524           textTracks = _this$cache_$media.textTracks; // If `artwork` is not given, create it using `poster`.
27525 
27526       if (!artwork && poster) {
27527         this.cache_.media.artwork = [{
27528           src: poster,
27529           type: getMimetype(poster)
27530         }];
27531       }
27532 
27533       if (src) {
27534         this.src(src);
27535       }
27536 
27537       if (poster) {
27538         this.poster(poster);
27539       }
27540 
27541       if (Array.isArray(textTracks)) {
27542         textTracks.forEach(function (tt) {
27543           return _this15.addRemoteTextTrack(tt, false);
27544         });
27545       }
27546 
27547       this.ready(ready);
27548     }
27549     /**
27550      * Get a clone of the current {@link Player~MediaObject} for this player.
27551      *
27552      * If the `loadMedia` method has not been used, will attempt to return a
27553      * {@link Player~MediaObject} based on the current state of the player.
27554      *
27555      * @return {Player~MediaObject}
27556      */
27557     ;
27558 
27559     _proto.getMedia = function getMedia() {
27560       if (!this.cache_.media) {
27561         var poster = this.poster();
27562         var src = this.currentSources();
27563         var textTracks = Array.prototype.map.call(this.remoteTextTracks(), function (tt) {
27564           return {
27565             kind: tt.kind,
27566             label: tt.label,
27567             language: tt.language,
27568             src: tt.src
27569           };
27570         });
27571         var media = {
27572           src: src,
27573           textTracks: textTracks
27574         };
27575 
27576         if (poster) {
27577           media.poster = poster;
27578           media.artwork = [{
27579             src: media.poster,
27580             type: getMimetype(media.poster)
27581           }];
27582         }
27583 
27584         return media;
27585       }
27586 
27587       return mergeOptions(this.cache_.media);
27588     }
27589     /**
27590      * Gets tag settings
27591      *
27592      * @param {Element} tag
27593      *        The player tag
27594      *
27595      * @return {Object}
27596      *         An object containing all of the settings
27597      *         for a player tag
27598      */
27599     ;
27600 
27601     Player.getTagSettings = function getTagSettings(tag) {
27602       var baseOptions = {
27603         sources: [],
27604         tracks: []
27605       };
27606       var tagOptions = getAttributes(tag);
27607       var dataSetup = tagOptions['data-setup'];
27608 
27609       if (hasClass(tag, 'vjs-fill')) {
27610         tagOptions.fill = true;
27611       }
27612 
27613       if (hasClass(tag, 'vjs-fluid')) {
27614         tagOptions.fluid = true;
27615       } // Check if data-setup attr exists.
27616 
27617 
27618       if (dataSetup !== null) {
27619         // Parse options JSON
27620         // If empty string, make it a parsable json object.
27621         var _safeParseTuple = tuple(dataSetup || '{}'),
27622             err = _safeParseTuple[0],
27623             data = _safeParseTuple[1];
27624 
27625         if (err) {
27626           log.error(err);
27627         }
27628 
27629         assign(tagOptions, data);
27630       }
27631 
27632       assign(baseOptions, tagOptions); // Get tag children settings
27633 
27634       if (tag.hasChildNodes()) {
27635         var children = tag.childNodes;
27636 
27637         for (var i = 0, j = children.length; i < j; i++) {
27638           var child = children[i]; // Change case needed: http://ejohn.org/blog/nodename-case-sensitivity/
27639 
27640           var childName = child.nodeName.toLowerCase();
27641 
27642           if (childName === 'source') {
27643             baseOptions.sources.push(getAttributes(child));
27644           } else if (childName === 'track') {
27645             baseOptions.tracks.push(getAttributes(child));
27646           }
27647         }
27648       }
27649 
27650       return baseOptions;
27651     }
27652     /**
27653      * Determine whether or not flexbox is supported
27654      *
27655      * @return {boolean}
27656      *         - true if flexbox is supported
27657      *         - false if flexbox is not supported
27658      */
27659     ;
27660 
27661     _proto.flexNotSupported_ = function flexNotSupported_() {
27662       var elem = document.createElement('i'); // Note: We don't actually use flexBasis (or flexOrder), but it's one of the more
27663       // common flex features that we can rely on when checking for flex support.
27664 
27665       return !('flexBasis' in elem.style || 'webkitFlexBasis' in elem.style || 'mozFlexBasis' in elem.style || 'msFlexBasis' in elem.style || // IE10-specific (2012 flex spec), available for completeness
27666       'msFlexOrder' in elem.style);
27667     };
27668 
27669     return Player;
27670   }(Component);
27671   /**
27672    * Get the {@link VideoTrackList}
27673    * @link https://html.spec.whatwg.org/multipage/embedded-content.html#videotracklist
27674    *
27675    * @return {VideoTrackList}
27676    *         the current video track list
27677    *
27678    * @method Player.prototype.videoTracks
27679    */
27680 
27681   /**
27682    * Get the {@link AudioTrackList}
27683    * @link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotracklist
27684    *
27685    * @return {AudioTrackList}
27686    *         the current audio track list
27687    *
27688    * @method Player.prototype.audioTracks
27689    */
27690 
27691   /**
27692    * Get the {@link TextTrackList}
27693    *
27694    * @link http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-texttracks
27695    *
27696    * @return {TextTrackList}
27697    *         the current text track list
27698    *
27699    * @method Player.prototype.textTracks
27700    */
27701 
27702   /**
27703    * Get the remote {@link TextTrackList}
27704    *
27705    * @return {TextTrackList}
27706    *         The current remote text track list
27707    *
27708    * @method Player.prototype.remoteTextTracks
27709    */
27710 
27711   /**
27712    * Get the remote {@link HtmlTrackElementList} tracks.
27713    *
27714    * @return {HtmlTrackElementList}
27715    *         The current remote text track element list
27716    *
27717    * @method Player.prototype.remoteTextTrackEls
27718    */
27719 
27720 
vendor: 4,869 bytes, lines 27721-27853
27721   ALL.names.forEach(function (name) {
27722     var props = ALL[name];
27723 
27724     Player.prototype[props.getterName] = function () {
27725       if (this.tech_) {
27726         return this.tech_[props.getterName]();
27727       } // if we have not yet loadTech_, we create {video,audio,text}Tracks_
27728       // these will be passed to the tech during loading
27729 
27730 
27731       this[props.privateName] = this[props.privateName] || new props.ListClass();
27732       return this[props.privateName];
27733     };
27734   });
27735   /**
27736    * Global enumeration of players.
27737    *
27738    * The keys are the player IDs and the values are either the {@link Player}
27739    * instance or `null` for disposed players.
27740    *
27741    * @type {Object}
27742    */
27743 
27744   Player.players = {};
27745   var navigator = window$1.navigator;
27746   /*
27747    * Player instance options, surfaced using options
27748    * options = Player.prototype.options_
27749    * Make changes in options, not here.
27750    *
27751    * @type {Object}
27752    * @private
27753    */
27754 
27755   Player.prototype.options_ = {
27756     // Default order of fallback technology
27757     techOrder: Tech.defaultTechOrder_,
27758     html5: {},
27759     flash: {},
27760     // default inactivity timeout
27761     inactivityTimeout: 2000,
27762     // default playback rates
27763     playbackRates: [],
27764     // Add playback rate selection by adding rates
27765     // 'playbackRates': [0.5, 1, 1.5, 2],
27766     liveui: false,
27767     // Included control sets
27768     children: ['mediaLoader', 'posterImage', 'textTrackDisplay', 'loadingSpinner', 'bigPlayButton', 'liveTracker', 'controlBar', 'errorDisplay', 'textTrackSettings', 'resizeManager'],
27769     language: navigator && (navigator.languages && navigator.languages[0] || navigator.userLanguage || navigator.language) || 'en',
27770     // locales and their language translations
27771     languages: {},
27772     // Default message to show when a video cannot be played.
27773     notSupportedMessage: 'No compatible source was found for this media.',
27774     fullscreen: {
27775       options: {
27776         navigationUI: 'hide'
27777       }
27778     },
27779     breakpoints: {},
27780     responsive: false
27781   };
27782   [
27783   /**
27784    * Returns whether or not the player is in the "ended" state.
27785    *
27786    * @return {Boolean} True if the player is in the ended state, false if not.
27787    * @method Player#ended
27788    */
27789   'ended',
27790   /**
27791    * Returns whether or not the player is in the "seeking" state.
27792    *
27793    * @return {Boolean} True if the player is in the seeking state, false if not.
27794    * @method Player#seeking
27795    */
27796   'seeking',
27797   /**
27798    * Returns the TimeRanges of the media that are currently available
27799    * for seeking to.
27800    *
27801    * @return {TimeRanges} the seekable intervals of the media timeline
27802    * @method Player#seekable
27803    */
27804   'seekable',
27805   /**
27806    * Returns the current state of network activity for the element, from
27807    * the codes in the list below.
27808    * - NETWORK_EMPTY (numeric value 0)
27809    *   The element has not yet been initialised. All attributes are in
27810    *   their initial states.
27811    * - NETWORK_IDLE (numeric value 1)
27812    *   The element's resource selection algorithm is active and has
27813    *   selected a resource, but it is not actually using the network at
27814    *   this time.
27815    * - NETWORK_LOADING (numeric value 2)
27816    *   The user agent is actively trying to download data.
27817    * - NETWORK_NO_SOURCE (numeric value 3)
27818    *   The element's resource selection algorithm is active, but it has
27819    *   not yet found a resource to use.
27820    *
27821    * @see https://html.spec.whatwg.org/multipage/embedded-content.html#network-states
27822    * @return {number} the current network activity state
27823    * @method Player#networkState
27824    */
27825   'networkState',
27826   /**
27827    * Returns a value that expresses the current state of the element
27828    * with respect to rendering the current playback position, from the
27829    * codes in the list below.
27830    * - HAVE_NOTHING (numeric value 0)
27831    *   No information regarding the media resource is available.
27832    * - HAVE_METADATA (numeric value 1)
27833    *   Enough of the resource has been obtained that the duration of the
27834    *   resource is available.
27835    * - HAVE_CURRENT_DATA (numeric value 2)
27836    *   Data for the immediate current playback position is available.
27837    * - HAVE_FUTURE_DATA (numeric value 3)
27838    *   Data for the immediate current playback position is available, as
27839    *   well as enough data for the user agent to advance the current
27840    *   playback position in the direction of playback.
27841    * - HAVE_ENOUGH_DATA (numeric value 4)
27842    *   The user agent estimates that enough data is available for
27843    *   playback to proceed uninterrupted.
27844    *
27845    * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-readystate
27846    * @return {number} the current playback rendering state
27847    * @method Player#readyState
27848    */
27849   'readyState'].forEach(function (fn) {
27850     Player.prototype[fn] = function () {
27851       return this.techGet_(fn);
27852     };
27853   });
27854   TECH_EVENTS_RETRIGGER.forEach(function (event) {
27855     Player.prototype["handleTech" + toTitleCase(event) + "_"] = function () {
27856       return this.trigger(event);
27857     };
27858   });
27859   /**
27860    * Fired when the player has initial duration and dimension information
27861    *
27862    * @event Player#loadedmetadata
27863    * @type {EventTarget~Event}
27864    */
27865 
27866   /**
27867    * Fired when the player has downloaded data at the current playback position
27868    *
27869    * @event Player#loadeddata
27870    * @type {EventTarget~Event}
27871    */
27872 
27873   /**
27874    * Fired when the current playback position has changed *
27875    * During playback this is fired every 15-250 milliseconds, depending on the
27876    * playback technology in use.
27877    *
27878    * @event Player#timeupdate
27879    * @type {EventTarget~Event}
27880    */
27881 
27882   /**
27883    * Fired when the volume changes
27884    *
27885    * @event Player#volumechange
27886    * @type {EventTarget~Event}
27887    */
27888 
27889   /**
27890    * Reports whether or not a player has a plugin available.
27891    *
27892    * This does not report whether or not the plugin has ever been initialized
27893    * on this player. For that, [usingPlugin]{@link Player#usingPlugin}.
27894    *
27895    * @method Player#hasPlugin
27896    * @param  {string}  name
27897    *         The name of a plugin.
27898    *
27899    * @return {boolean}
27900    *         Whether or not this player has the requested plugin available.
27901    */
27902 
27903   /**
27904    * Reports whether or not a player is using a plugin by name.
27905    *
27906    * For basic plugins, this only reports whether the plugin has _ever_ been
27907    * initialized on this player.
27908    *
27909    * @method Player#usingPlugin
27910    * @param  {string} name
27911    *         The name of a plugin.
27912    *
27913    * @return {boolean}
27914    *         Whether or not this player is using the requested plugin.
27915    */
27916 
27917   Component.registerComponent('Player', Player);
27918 
27919   var setPrototypeOf = createCommonjsModule(function (module) {
27920     function _setPrototypeOf(o, p) {
27921       module.exports = _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
27922         o.__proto__ = p;
27923         return o;
27924       };
27925 
27926       return _setPrototypeOf(o, p);
27927     }
27928 
27929     module.exports = _setPrototypeOf;
27930   });
27931 
27932   var construct = createCommonjsModule(function (module) {
27933     function isNativeReflectConstruct() {
27934       if (typeof Reflect === "undefined" || !Reflect.construct) return false;
27935       if (Reflect.construct.sham) return false;
27936       if (typeof Proxy === "function") return true;
27937 
27938       try {
27939         Date.prototype.toString.call(Reflect.construct(Date, [], function () {}));
27940         return true;
27941       } catch (e) {
27942         return false;
27943       }
27944     }
27945 
27946     function _construct(Parent, args, Class) {
27947       if (isNativeReflectConstruct()) {
27948         module.exports = _construct = Reflect.construct;
27949       } else {
27950         module.exports = _construct = function _construct(Parent, args, Class) {
27951           var a = [null];
27952           a.push.apply(a, args);
27953           var Constructor = Function.bind.apply(Parent, a);
27954           var instance = new Constructor();
27955           if (Class) setPrototypeOf(instance, Class.prototype);
27956           return instance;
27957         };
27958       }
27959 
27960       return _construct.apply(null, arguments);
27961     }
27962 
27963     module.exports = _construct;
27964   });
27965 
27966   /**
27967    * The base plugin name.
27968    *
27969    * @private
27970    * @constant
27971    * @type {string}
27972    */
27973 
27974   var BASE_PLUGIN_NAME = 'plugin';
27975   /**
27976    * The key on which a player's active plugins cache is stored.
27977    *
27978    * @private
27979    * @constant
27980    * @type     {string}
27981    */
27982 
27983   var PLUGIN_CACHE_KEY = 'activePlugins_';
27984   /**
27985    * Stores registered plugins in a private space.
27986    *
27987    * @private
27988    * @type    {Object}
27989    */
27990 
27991   var pluginStorage = {};
27992   /**
27993    * Reports whether or not a plugin has been registered.
27994    *
27995    * @private
27996    * @param   {string} name
27997    *          The name of a plugin.
27998    *
27999    * @return {boolean}
28000    *          Whether or not the plugin has been registered.
28001    */
28002 
28003   var pluginExists = function pluginExists(name) {
28004     return pluginStorage.hasOwnProperty(name);
28005   };
28006   /**
28007    * Get a single registered plugin by name.
28008    *
28009    * @private
28010    * @param   {string} name
28011    *          The name of a plugin.
28012    *
28013    * @return {Function|undefined}
28014    *          The plugin (or undefined).
28015    */
28016 
28017 
28018   var getPlugin = function getPlugin(name) {
28019     return pluginExists(name) ? pluginStorage[name] : undefined;
28020   };
28021   /**
28022    * Marks a plugin as "active" on a player.
28023    *
28024    * Also, ensures that the player has an object for tracking active plugins.
28025    *
28026    * @private
28027    * @param   {Player} player
28028    *          A Video.js player instance.
28029    *
28030    * @param   {string} name
28031    *          The name of a plugin.
28032    */
28033 
28034 
28035   var markPluginAsActive = function markPluginAsActive(player, name) {
28036     player[PLUGIN_CACHE_KEY] = player[PLUGIN_CACHE_KEY] || {};
28037     player[PLUGIN_CACHE_KEY][name] = true;
28038   };
28039   /**
28040    * Triggers a pair of plugin setup events.
28041    *
28042    * @private
28043    * @param  {Player} player
28044    *         A Video.js player instance.
28045    *
28046    * @param  {Plugin~PluginEventHash} hash
28047    *         A plugin event hash.
28048    *
28049    * @param  {boolean} [before]
28050    *         If true, prefixes the event name with "before". In other words,
28051    *         use this to trigger "beforepluginsetup" instead of "pluginsetup".
28052    */
28053 
28054 
28055   var triggerSetupEvent = function triggerSetupEvent(player, hash, before) {
28056     var eventName = (before ? 'before' : '') + 'pluginsetup';
28057     player.trigger(eventName, hash);
28058     player.trigger(eventName + ':' + hash.name, hash);
28059   };
28060   /**
28061    * Takes a basic plugin function and returns a wrapper function which marks
28062    * on the player that the plugin has been activated.
28063    *
28064    * @private
28065    * @param   {string} name
28066    *          The name of the plugin.
28067    *
28068    * @param   {Function} plugin
28069    *          The basic plugin.
28070    *
28071    * @return {Function}
28072    *          A wrapper function for the given plugin.
28073    */
28074 
28075 
28076   var createBasicPlugin = function createBasicPlugin(name, plugin) {
28077     var basicPluginWrapper = function basicPluginWrapper() {
28078       // We trigger the "beforepluginsetup" and "pluginsetup" events on the player
28079       // regardless, but we want the hash to be consistent with the hash provided
28080       // for advanced plugins.
28081       //
28082       // The only potentially counter-intuitive thing here is the `instance` in
28083       // the "pluginsetup" event is the value returned by the `plugin` function.
28084       triggerSetupEvent(this, {
28085         name: name,
28086         plugin: plugin,
28087         instance: null
28088       }, true);
28089       var instance = plugin.apply(this, arguments);
28090       markPluginAsActive(this, name);
28091       triggerSetupEvent(this, {
28092         name: name,
28093         plugin: plugin,
28094         instance: instance
28095       });
28096       return instance;
28097     };
28098 
vendor: 9,187 bytes, lines 28099-28380
28099     Object.keys(plugin).forEach(function (prop) {
28100       basicPluginWrapper[prop] = plugin[prop];
28101     });
28102     return basicPluginWrapper;
28103   };
28104   /**
28105    * Takes a plugin sub-class and returns a factory function for generating
28106    * instances of it.
28107    *
28108    * This factory function will replace itself with an instance of the requested
28109    * sub-class of Plugin.
28110    *
28111    * @private
28112    * @param   {string} name
28113    *          The name of the plugin.
28114    *
28115    * @param   {Plugin} PluginSubClass
28116    *          The advanced plugin.
28117    *
28118    * @return {Function}
28119    */
28120 
28121 
28122   var createPluginFactory = function createPluginFactory(name, PluginSubClass) {
28123     // Add a `name` property to the plugin prototype so that each plugin can
28124     // refer to itself by name.
28125     PluginSubClass.prototype.name = name;
28126     return function () {
28127       triggerSetupEvent(this, {
28128         name: name,
28129         plugin: PluginSubClass,
28130         instance: null
28131       }, true);
28132 
28133       for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
28134         args[_key] = arguments[_key];
28135       }
28136 
28137       var instance = construct(PluginSubClass, [this].concat(args)); // The plugin is replaced by a function that returns the current instance.
28138 
28139 
28140       this[name] = function () {
28141         return instance;
28142       };
28143 
28144       triggerSetupEvent(this, instance.getEventHash());
28145       return instance;
28146     };
28147   };
28148   /**
28149    * Parent class for all advanced plugins.
28150    *
28151    * @mixes   module:evented~EventedMixin
28152    * @mixes   module:stateful~StatefulMixin
28153    * @fires   Player#beforepluginsetup
28154    * @fires   Player#beforepluginsetup:$name
28155    * @fires   Player#pluginsetup
28156    * @fires   Player#pluginsetup:$name
28157    * @listens Player#dispose
28158    * @throws  {Error}
28159    *          If attempting to instantiate the base {@link Plugin} class
28160    *          directly instead of via a sub-class.
28161    */
28162 
28163 
28164   var Plugin =
28165   /*#__PURE__*/
28166   function () {
28167     /**
28168      * Creates an instance of this class.
28169      *
28170      * Sub-classes should call `super` to ensure plugins are properly initialized.
28171      *
28172      * @param {Player} player
28173      *        A Video.js player instance.
28174      */
28175     function Plugin(player) {
28176       if (this.constructor === Plugin) {
28177         throw new Error('Plugin must be sub-classed; not directly instantiated.');
28178       }
28179 
28180       this.player = player; // Make this object evented, but remove the added `trigger` method so we
28181       // use the prototype version instead.
28182 
28183       evented(this);
28184       delete this.trigger;
28185       stateful(this, this.constructor.defaultState);
28186       markPluginAsActive(player, this.name); // Auto-bind the dispose method so we can use it as a listener and unbind
28187       // it later easily.
28188 
28189       this.dispose = bind(this, this.dispose); // If the player is disposed, dispose the plugin.
28190 
28191       player.on('dispose', this.dispose);
28192     }
28193     /**
28194      * Get the version of the plugin that was set on <pluginName>.VERSION
28195      */
28196 
28197 
28198     var _proto = Plugin.prototype;
28199 
28200     _proto.version = function version() {
28201       return this.constructor.VERSION;
28202     }
28203     /**
28204      * Each event triggered by plugins includes a hash of additional data with
28205      * conventional properties.
28206      *
28207      * This returns that object or mutates an existing hash.
28208      *
28209      * @param   {Object} [hash={}]
28210      *          An object to be used as event an event hash.
28211      *
28212      * @return {Plugin~PluginEventHash}
28213      *          An event hash object with provided properties mixed-in.
28214      */
28215     ;
28216 
28217     _proto.getEventHash = function getEventHash(hash) {
28218       if (hash === void 0) {
28219         hash = {};
28220       }
28221 
28222       hash.name = this.name;
28223       hash.plugin = this.constructor;
28224       hash.instance = this;
28225       return hash;
28226     }
28227     /**
28228      * Triggers an event on the plugin object and overrides
28229      * {@link module:evented~EventedMixin.trigger|EventedMixin.trigger}.
28230      *
28231      * @param   {string|Object} event
28232      *          An event type or an object with a type property.
28233      *
28234      * @param   {Object} [hash={}]
28235      *          Additional data hash to merge with a
28236      *          {@link Plugin~PluginEventHash|PluginEventHash}.
28237      *
28238      * @return {boolean}
28239      *          Whether or not default was prevented.
28240      */
28241     ;
28242 
28243     _proto.trigger = function trigger$1(event, hash) {
28244       if (hash === void 0) {
28245         hash = {};
28246       }
28247 
28248       return trigger(this.eventBusEl_, event, this.getEventHash(hash));
28249     }
28250     /**
28251      * Handles "statechanged" events on the plugin. No-op by default, override by
28252      * subclassing.
28253      *
28254      * @abstract
28255      * @param    {Event} e
28256      *           An event object provided by a "statechanged" event.
28257      *
28258      * @param    {Object} e.changes
28259      *           An object describing changes that occurred with the "statechanged"
28260      *           event.
28261      */
28262     ;
28263 
28264     _proto.handleStateChanged = function handleStateChanged(e) {}
28265     /**
28266      * Disposes a plugin.
28267      *
28268      * Subclasses can override this if they want, but for the sake of safety,
28269      * it's probably best to subscribe the "dispose" event.
28270      *
28271      * @fires Plugin#dispose
28272      */
28273     ;
28274 
28275     _proto.dispose = function dispose() {
28276       var name = this.name,
28277           player = this.player;
28278       /**
28279        * Signals that a advanced plugin is about to be disposed.
28280        *
28281        * @event Plugin#dispose
28282        * @type  {EventTarget~Event}
28283        */
28284 
28285       this.trigger('dispose');
28286       this.off();
28287       player.off('dispose', this.dispose); // Eliminate any possible sources of leaking memory by clearing up
28288       // references between the player and the plugin instance and nulling out
28289       // the plugin's state and replacing methods with a function that throws.
28290 
28291       player[PLUGIN_CACHE_KEY][name] = false;
28292       this.player = this.state = null; // Finally, replace the plugin name on the player with a new factory
28293       // function, so that the plugin is ready to be set up again.
28294 
28295       player[name] = createPluginFactory(name, pluginStorage[name]);
28296     }
28297     /**
28298      * Determines if a plugin is a basic plugin (i.e. not a sub-class of `Plugin`).
28299      *
28300      * @param   {string|Function} plugin
28301      *          If a string, matches the name of a plugin. If a function, will be
28302      *          tested directly.
28303      *
28304      * @return {boolean}
28305      *          Whether or not a plugin is a basic plugin.
28306      */
28307     ;
28308 
28309     Plugin.isBasic = function isBasic(plugin) {
28310       var p = typeof plugin === 'string' ? getPlugin(plugin) : plugin;
28311       return typeof p === 'function' && !Plugin.prototype.isPrototypeOf(p.prototype);
28312     }
28313     /**
28314      * Register a Video.js plugin.
28315      *
28316      * @param   {string} name
28317      *          The name of the plugin to be registered. Must be a string and
28318      *          must not match an existing plugin or a method on the `Player`
28319      *          prototype.
28320      *
28321      * @param   {Function} plugin
28322      *          A sub-class of `Plugin` or a function for basic plugins.
28323      *
28324      * @return {Function}
28325      *          For advanced plugins, a factory function for that plugin. For
28326      *          basic plugins, a wrapper function that initializes the plugin.
28327      */
28328     ;
28329 
28330     Plugin.registerPlugin = function registerPlugin(name, plugin) {
28331       if (typeof name !== 'string') {
28332         throw new Error("Illegal plugin name, \"" + name + "\", must be a string, was " + typeof name + ".");
28333       }
28334 
28335       if (pluginExists(name)) {
28336         log.warn("A plugin named \"" + name + "\" already exists. You may want to avoid re-registering plugins!");
28337       } else if (Player.prototype.hasOwnProperty(name)) {
28338         throw new Error("Illegal plugin name, \"" + name + "\", cannot share a name with an existing player method!");
28339       }
28340 
28341       if (typeof plugin !== 'function') {
28342         throw new Error("Illegal plugin for \"" + name + "\", must be a function, was " + typeof plugin + ".");
28343       }
28344 
28345       pluginStorage[name] = plugin; // Add a player prototype method for all sub-classed plugins (but not for
28346       // the base Plugin class).
28347 
28348       if (name !== BASE_PLUGIN_NAME) {
28349         if (Plugin.isBasic(plugin)) {
28350           Player.prototype[name] = createBasicPlugin(name, plugin);
28351         } else {
28352           Player.prototype[name] = createPluginFactory(name, plugin);
28353         }
28354       }
28355 
28356       return plugin;
28357     }
28358     /**
28359      * De-register a Video.js plugin.
28360      *
28361      * @param  {string} name
28362      *         The name of the plugin to be de-registered. Must be a string that
28363      *         matches an existing plugin.
28364      *
28365      * @throws {Error}
28366      *         If an attempt is made to de-register the base plugin.
28367      */
28368     ;
28369 
28370     Plugin.deregisterPlugin = function deregisterPlugin(name) {
28371       if (name === BASE_PLUGIN_NAME) {
28372         throw new Error('Cannot de-register base plugin.');
28373       }
28374 
28375       if (pluginExists(name)) {
28376         delete pluginStorage[name];
28377         delete Player.prototype[name];
28378       }
28379     }
28380     /**
28381      * Gets an object containing multiple Video.js plugins.
28382      *
28383      * @param   {Array} [names]
28384      *          If provided, should be an array of plugin names. Defaults to _all_
28385      *          plugin names.
28386      *
28387      * @return {Object|undefined}
28388      *          An object containing plugin(s) associated with their name(s) or
28389      *          `undefined` if no matching plugins exist).
28390      */
28391     ;
28392 
28393     Plugin.getPlugins = function getPlugins(names) {
28394       if (names === void 0) {
28395         names = Object.keys(pluginStorage);
28396       }
28397 
28398       var result;
28399       names.forEach(function (name) {
28400         var plugin = getPlugin(name);
28401 
28402         if (plugin) {
28403           result = result || {};
28404           result[name] = plugin;
28405         }
28406       });
28407       return result;
28408     }
28409     /**
28410      * Gets a plugin's version, if available
28411      *
28412      * @param   {string} name
28413      *          The name of a plugin.
28414      *
28415      * @return {string}
28416      *          The plugin's version or an empty string.
28417      */
28418     ;
28419 
28420     Plugin.getPluginVersion = function getPluginVersion(name) {
28421       var plugin = getPlugin(name);
28422       return plugin && plugin.VERSION || '';
28423     };
28424 
28425     return Plugin;
28426   }();
28427   /**
28428    * Gets a plugin by name if it exists.
28429    *
28430    * @static
28431    * @method   getPlugin
28432    * @memberOf Plugin
28433    * @param    {string} name
28434    *           The name of a plugin.
28435    *
28436    * @returns  {Function|undefined}
28437    *           The plugin (or `undefined`).
28438    */
28439 
28440 
28441   Plugin.getPlugin = getPlugin;
28442   /**
28443    * The name of the base plugin class as it is registered.
28444    *
28445    * @type {string}
28446    */
28447 
28448   Plugin.BASE_PLUGIN_NAME = BASE_PLUGIN_NAME;
28449   Plugin.registerPlugin(BASE_PLUGIN_NAME, Plugin);
28450   /**
28451    * Documented in player.js
28452    *
28453    * @ignore
28454    */
28455 
28456   Player.prototype.usingPlugin = function (name) {
28457     return !!this[PLUGIN_CACHE_KEY] && this[PLUGIN_CACHE_KEY][name] === true;
28458   };
28459   /**
28460    * Documented in player.js
28461    *
28462    * @ignore
28463    */
28464 
28465 
28466   Player.prototype.hasPlugin = function (name) {
28467     return !!pluginExists(name);
28468   };
28469   /**
28470    * Signals that a plugin is about to be set up on a player.
28471    *
28472    * @event    Player#beforepluginsetup
28473    * @type     {Plugin~PluginEventHash}
28474    */
28475 
28476   /**
28477    * Signals that a plugin is about to be set up on a player - by name. The name
28478    * is the name of the plugin.
28479    *
28480    * @event    Player#beforepluginsetup:$name
28481    * @type     {Plugin~PluginEventHash}
28482    */
28483 
28484   /**
28485    * Signals that a plugin has just been set up on a player.
28486    *
28487    * @event    Player#pluginsetup
28488    * @type     {Plugin~PluginEventHash}
28489    */
28490 
28491   /**
28492    * Signals that a plugin has just been set up on a player - by name. The name
28493    * is the name of the plugin.
28494    *
28495    * @event    Player#pluginsetup:$name
28496    * @type     {Plugin~PluginEventHash}
28497    */
28498 
28499   /**
28500    * @typedef  {Object} Plugin~PluginEventHash
28501    *
28502    * @property {string} instance
28503    *           For basic plugins, the return value of the plugin function. For
28504    *           advanced plugins, the plugin instance on which the event is fired.
28505    *
28506    * @property {string} name
28507    *           The name of the plugin.
28508    *
28509    * @property {string} plugin
28510    *           For basic plugins, the plugin function. For advanced plugins, the
28511    *           plugin class/constructor.
28512    */
28513 
28514   function _inherits(subClass, superClass) {
28515     if (typeof superClass !== "function" && superClass !== null) {
28516       throw new TypeError("Super expression must either be null or a function");
28517     }
28518 
28519     subClass.prototype = Object.create(superClass && superClass.prototype, {
28520       constructor: {
28521         value: subClass,
28522         writable: true,
28523         configurable: true
28524       }
28525     });
28526     if (superClass) setPrototypeOf(subClass, superClass);
28527   }
28528 
28529   var inherits = _inherits;
28530 
28531   /**
28532    * @file extend.js
28533    * @module extend
28534    */
28535   /**
28536    * Used to subclass an existing class by emulating ES subclassing using the
28537    * `extends` keyword.
28538    *
28539    * @function
28540    * @example
28541    * var MyComponent = videojs.extend(videojs.getComponent('Component'), {
28542    *   myCustomMethod: function() {
28543    *     // Do things in my method.
28544    *   }
28545    * });
28546    *
28547    * @param    {Function} superClass
28548    *           The class to inherit from
28549    *
28550    * @param    {Object}   [subClassMethods={}]
28551    *           Methods of the new class
28552    *
28553    * @return   {Function}
28554    *           The new class with subClassMethods that inherited superClass.
28555    */
28556 
28557   var extend = function extend(superClass, subClassMethods) {
28558     if (subClassMethods === void 0) {
28559       subClassMethods = {};
28560     }
28561 
28562     var subClass = function subClass() {
28563       superClass.apply(this, arguments);
28564     };
28565 
28566     var methods = {};
28567 
28568     if (typeof subClassMethods === 'object') {
28569       if (subClassMethods.constructor !== Object.prototype.constructor) {
28570         subClass = subClassMethods.constructor;
28571       }
28572 
28573       methods = subClassMethods;
28574     } else if (typeof subClassMethods === 'function') {
28575       subClass = subClassMethods;
28576     }
28577 
28578     inherits(subClass, superClass); // this is needed for backward-compatibility and node compatibility.
28579 
28580 
28581     if (superClass) {
28582       subClass.super_ = superClass;
28583     } // Extend subObj's prototype with functions and other properties from props
28584 
28585 
28586     for (var name in methods) {
28587       if (methods.hasOwnProperty(name)) {
28588         subClass.prototype[name] = methods[name];
28589       }
28590     }
28591 
28592     return subClass;
28593   };
28594 
28595   /**
28596    * @file video.js
28597    * @module videojs
28598    */
28599   /**
28600    * Normalize an `id` value by trimming off a leading `#`
28601    *
28602    * @private
28603    * @param   {string} id
28604    *          A string, maybe with a leading `#`.
28605    *
28606    * @return {string}
28607    *          The string, without any leading `#`.
28608    */
28609 
28610   var normalizeId = function normalizeId(id) {
28611     return id.indexOf('#') === 0 ? id.slice(1) : id;
28612   };
28613   /**
28614    * The `videojs()` function doubles as the main function for users to create a
28615    * {@link Player} instance as well as the main library namespace.
28616    *
28617    * It can also be used as a getter for a pre-existing {@link Player} instance.
28618    * However, we _strongly_ recommend using `videojs.getPlayer()` for this
28619    * purpose because it avoids any potential for unintended initialization.
28620    *
28621    * Due to [limitations](https://github.com/jsdoc3/jsdoc/issues/955#issuecomment-313829149)
28622    * of our JSDoc template, we cannot properly document this as both a function
28623    * and a namespace, so its function signature is documented here.
28624    *
28625    * #### Arguments
28626    * ##### id
28627    * string|Element, **required**
28628    *
28629    * Video element or video element ID.
28630    *
28631    * ##### options
28632    * Object, optional
28633    *
28634    * Options object for providing settings.
28635    * See: [Options Guide](https://docs.videojs.com/tutorial-options.html).
28636    *
28637    * ##### ready
28638    * {@link Component~ReadyCallback}, optional
28639    *
28640    * A function to be called when the {@link Player} and {@link Tech} are ready.
28641    *
28642    * #### Return Value
28643    *
28644    * The `videojs()` function returns a {@link Player} instance.
28645    *
28646    * @namespace
28647    *
28648    * @borrows AudioTrack as AudioTrack
28649    * @borrows Component.getComponent as getComponent
28650    * @borrows module:computed-style~computedStyle as computedStyle
28651    * @borrows module:events.on as on
28652    * @borrows module:events.one as one
28653    * @borrows module:events.off as off
28654    * @borrows module:events.trigger as trigger
28655    * @borrows EventTarget as EventTarget
28656    * @borrows module:extend~extend as extend
28657    * @borrows module:fn.bind as bind
28658    * @borrows module:format-time.formatTime as formatTime
28659    * @borrows module:format-time.resetFormatTime as resetFormatTime
28660    * @borrows module:format-time.setFormatTime as setFormatTime
28661    * @borrows module:merge-options.mergeOptions as mergeOptions
28662    * @borrows module:middleware.use as use
28663    * @borrows Player.players as players
28664    * @borrows Plugin.registerPlugin as registerPlugin
28665    * @borrows Plugin.deregisterPlugin as deregisterPlugin
28666    * @borrows Plugin.getPlugins as getPlugins
28667    * @borrows Plugin.getPlugin as getPlugin
28668    * @borrows Plugin.getPluginVersion as getPluginVersion
28669    * @borrows Tech.getTech as getTech
28670    * @borrows Tech.registerTech as registerTech
28671    * @borrows TextTrack as TextTrack
28672    * @borrows module:time-ranges.createTimeRanges as createTimeRange
28673    * @borrows module:time-ranges.createTimeRanges as createTimeRanges
28674    * @borrows module:url.isCrossOrigin as isCrossOrigin
28675    * @borrows module:url.parseUrl as parseUrl
28676    * @borrows VideoTrack as VideoTrack
28677    *
28678    * @param  {string|Element}
28678 id
28679    *         Video element or video element ID.
28680    *
28681    * @param  {Object} [options]
28682    *         Options object for providing settings.
28683    *         See: [Options Guide](https://docs.videojs.com/tutorial-options.html).
28684    *
28685    * @param  {Component~ReadyCallback} [ready]
28686    *         A function to be called when the {@link Player} and {@link Tech} are
28687    *         ready.
28688    *
28689    * @return {Player}
28690    *         The `videojs()` function returns a {@link Player|Player} instance.
28691    */
28692 
28693 
28694   function videojs$1(id, options, ready) {
28695     var player = videojs$1.getPlayer(id);
28696 
28697     if (player) {
28698       if (options) {
28699         log.warn("Player \"" + id + "\" is already initialised. Options will not be applied.");
28700       }
28701 
28702       if (ready) {
28703         player.ready(ready);
28704       }
28705 
28706       return player;
28707     }
28708 
28709     var el = typeof id === 'string' ? $('#' + normalizeId(id)) : id;
28710 
28711     if (!isEl(el)) {
28712       throw new TypeError('The element or ID supplied is not valid. (videojs)');
28713     } // document.body.contains(el) will only check if el is contained within that one document.
28714     // This causes problems for elements in iframes.
28715     // Instead, use the element's ownerDocument instead of the global document.
28716     // This will make sure that the element is indeed in the dom of that document.
28717     // Additionally, check that the document in question has a default view.
28718     // If the document is no longer attached to the dom, the defaultView of the document will be null.
28719 
28720 
28721     if (!el.ownerDocument.defaultView || !el.ownerDocument.body.contains(el)) {
28722       log.warn('The element supplied is not included in the DOM');
28723     }
28724 
28725     options = options || {};
28726     videojs$1.hooks('beforesetup').forEach(function (hookFunction) {
28727       var opts = hookFunction(el, mergeOptions(options));
28728 
28729       if (!isObject(opts) || Array.isArray(opts)) {
28730         log.error('please return an object in beforesetup hooks');
28731         return;
28732       }
28733 
28734       options = mergeOptions(options, opts);
28735     }); // We get the current "Player" component here in case an integration has
28736     // replaced it with a custom player.
28737 
28738     var PlayerComponent = Component.getComponent('Player');
28739     player = new PlayerComponent(el, options, ready);
28740     videojs$1.hooks('setup').forEach(function (hookFunction) {
28741       return hookFunction(player);
28742     });
28743     return player;
28744   }
28745   /**
28746    * An Object that contains lifecycle hooks as keys which point to an array
28747    * of functions that are run when a lifecycle is triggered
28748    *
28749    * @private
28750    */
28751 
28752 
28753   videojs$1.hooks_ = {};
28754   /**
28755    * Get a list of hooks for a specific lifecycle
28756    *
28757    * @param  {string} type
28758    *         the lifecyle to get hooks from
28759    *
28760    * @param  {Function|Function[]} [fn]
28761    *         Optionally add a hook (or hooks) to the lifecycle that your are getting.
28762    *
28763    * @return {Array}
28764    *         an array of hooks, or an empty array if there are none.
28765    */
28766 
28767   videojs$1.hooks = function (type, fn) {
28768     videojs$1.hooks_[type] = videojs$1.hooks_[type] || [];
28769 
28770     if (fn) {
28771       videojs$1.hooks_[type] = videojs$1.hooks_[type].concat(fn);
28772     }
28773 
28774     return videojs$1.hooks_[type];
28775   };
28776   /**
28777    * Add a function hook to a specific videojs lifecycle.
28778    *
28779    * @param {string} type
28780    *        the lifecycle to hook the function to.
28781    *
28782    * @param {Function|Function[]}
28783    *        The function or array of functions to attach.
28784    */
28785 
28786 
28787   videojs$1.hook = function (type, fn) {
28788     videojs$1.hooks(type, fn);
28789   };
28790   /**
28791    * Add a function hook that will only run once to a specific videojs lifecycle.
28792    *
28793    * @param {string} type
28794    *        the lifecycle to hook the function to.
28795    *
28796    * @param {Function|Function[]}
28797    *        The function or array of functions to attach.
28798    */
28799 
28800 
28801   videojs$1.hookOnce = function (type, fn) {
28802     videojs$1.hooks(type, [].concat(fn).map(function (original) {
28803       var wrapper = function wrapper() {
28804         videojs$1.removeHook(type, wrapper);
28805         return original.apply(void 0, arguments);
28806       };
28807 
28808       return wrapper;
28809     }));
28810   };
28811   /**
28812    * Remove a hook from a specific videojs lifecycle.
28813    *
28814    * @param  {string} type
28815    *         the lifecycle that the function hooked to
28816    *
28817    * @param  {Function} fn
28818    *         The hooked function to remove
28819    *
28820    * @return {boolean}
28821    *         The function that was removed or undef
28822    */
28823 
28824 
vendor: 9,104 bytes, lines 28825-29121
28825   videojs$1.removeHook = function (type, fn) {
28826     var index = videojs$1.hooks(type).indexOf(fn);
28827 
28828     if (index <= -1) {
28829       return false;
28830     }
28831 
28832     videojs$1.hooks_[type] = videojs$1.hooks_[type].slice();
28833     videojs$1.hooks_[type].splice(index, 1);
28834     return true;
28835   }; // Add default styles
28836 
28837 
28838   if (window$1.VIDEOJS_NO_DYNAMIC_STYLE !== true && isReal()) {
28839     var style = $('.vjs-styles-defaults');
28840 
28841     if (!style) {
28842       style = createStyleElement('vjs-styles-defaults');
28843       var head = $('head');
28844 
28845       if (head) {
28846         head.insertBefore(style, head.firstChild);
28847       }
28848 
28849       setTextContent(style, "\n      .video-js {\n        width: 300px;\n        height: 150px;\n      }\n\n      .vjs-fluid {\n        padding-top: 56.25%\n      }\n    ");
28850     }
28851   } // Run Auto-load players
28852   // You have to wait at least once in case this script is loaded after your
28853   // video in the DOM (weird behavior only with minified version)
28854 
28855 
28856   autoSetupTimeout(1, videojs$1);
28857   /**
28858    * Current Video.js version. Follows [semantic versioning](https://semver.org/).
28859    *
28860    * @type {string}
28861    */
28862 
28863   videojs$1.VERSION = version;
28864   /**
28865    * The global options object. These are the settings that take effect
28866    * if no overrides are specified when the player is created.
28867    *
28868    * @type {Object}
28869    */
28870 
28871   videojs$1.options = Player.prototype.options_;
28872   /**
28873    * Get an object with the currently created players, keyed by player ID
28874    *
28875    * @return {Object}
28876    *         The created players
28877    */
28878 
28879   videojs$1.getPlayers = function () {
28880     return Player.players;
28881   };
28882   /**
28883    * Get a single player based on an ID or DOM element.
28884    *
28885    * This is useful if you want to check if an element or ID has an associated
28886    * Video.js player, but not create one if it doesn't.
28887    *
28888    * @param   {string|Element} id
28889    *          An HTML element - `<video>`, `<audio>`, or `<video-js>` -
28890    *          or a string matching the `id` of such an element.
28891    *
28892    * @return {Player|undefined}
28893    *          A player instance or `undefined` if there is no player instance
28894    *          matching the argument.
28895    */
28896 
28897 
28898   videojs$1.getPlayer = function (id) {
28899     var players = Player.players;
28900     var tag;
28901 
28902     if (typeof id === 'string') {
28903       var nId = normalizeId(id);
28904       var player = players[nId];
28905 
28906       if (player) {
28907         return player;
28908       }
28909 
28910       tag = $('#' + nId);
28911     } else {
28912       tag = id;
28913     }
28914 
28915     if (isEl(tag)) {
28916       var _tag = tag,
28917           _player = _tag.player,
28918           playerId = _tag.playerId; // Element may have a `player` property referring to an already created
28919       // player instance. If so, return that.
28920 
28921       if (_player || players[playerId]) {
28922         return _player || players[playerId];
28923       }
28924     }
28925   };
28926   /**
28927    * Returns an array of all current players.
28928    *
28929    * @return {Array}
28930    *         An array of all players. The array will be in the order that
28931    *         `Object.keys` provides, which could potentially vary between
28932    *         JavaScript engines.
28933    *
28934    */
28935 
28936 
28937   videojs$1.getAllPlayers = function () {
28938     return (// Disposed players leave a key with a `null` value, so we need to make sure
28939       // we filter those out.
28940       Object.keys(Player.players).map(function (k) {
28941         return Player.players[k];
28942       }).filter(Boolean)
28943     );
28944   };
28945 
28946   videojs$1.players = Player.players;
28947   videojs$1.getComponent = Component.getComponent;
28948   /**
28949    * Register a component so it can referred to by name. Used when adding to other
28950    * components, either through addChild `component.addChild('myComponent')` or through
28951    * default children options  `{ children: ['myComponent'] }`.
28952    *
28953    * > NOTE: You could also just initialize the component before adding.
28954    * `component.addChild(new MyComponent());`
28955    *
28956    * @param {string} name
28957    *        The class name of the component
28958    *
28959    * @param {Component} comp
28960    *        The component class
28961    *
28962    * @return {Component}
28963    *         The newly registered component
28964    */
28965 
28966   videojs$1.registerComponent = function (name, comp) {
28967     if (Tech.isTech(comp)) {
28968       log.warn("The " + name + " tech was registered as a component. It should instead be registered using videojs.registerTech(name, tech)");
28969     }
28970 
28971     Component.registerComponent.call(Component, name, comp);
28972   };
28973 
28974   videojs$1.getTech = Tech.getTech;
28975   videojs$1.registerTech = Tech.registerTech;
28976   videojs$1.use = use;
28977   /**
28978    * An object that can be returned by a middleware to signify
28979    * that the middleware is being terminated.
28980    *
28981    * @type {object}
28982    * @property {object} middleware.TERMINATOR
28983    */
28984 
28985   Object.defineProperty(videojs$1, 'middleware', {
28986     value: {},
28987     writeable: false,
28988     enumerable: true
28989   });
28990   Object.defineProperty(videojs$1.middleware, 'TERMINATOR', {
28991     value: TERMINATOR,
28992     writeable: false,
28993     enumerable: true
28994   });
28995   /**
28996    * A reference to the {@link module:browser|browser utility module} as an object.
28997    *
28998    * @type {Object}
28999    * @see  {@link module:browser|browser}
29000    */
29001 
29002   videojs$1.browser = browser;
29003   /**
29004    * Use {@link module:browser.TOUCH_ENABLED|browser.TOUCH_ENABLED} instead; only
29005    * included for backward-compatibility with 4.x.
29006    *
29007    * @deprecated Since version 5.0, use {@link module:browser.TOUCH_ENABLED|browser.TOUCH_ENABLED instead.
29008    * @type {boolean}
29009    */
29010 
29011   videojs$1.TOUCH_ENABLED = TOUCH_ENABLED;
29012   videojs$1.extend = extend;
29013   videojs$1.mergeOptions = mergeOptions;
29014   videojs$1.bind = bind;
29015   videojs$1.registerPlugin = Plugin.registerPlugin;
29016   videojs$1.deregisterPlugin = Plugin.deregisterPlugin;
29017   /**
29018    * Deprecated method to register a plugin with Video.js
29019    *
29020    * @deprecated videojs.plugin() is deprecated; use videojs.registerPlugin() instead
29021    *
29022    * @param {string} name
29023    *        The plugin name
29024    *
29025    * @param {Plugin|Function} plugin
29026    *         The plugin sub-class or function
29027    */
29028 
29029   videojs$1.plugin = function (name, plugin) {
29030     log.warn('videojs.plugin() is deprecated; use videojs.registerPlugin() instead');
29031     return Plugin.registerPlugin(name, plugin);
29032   };
29033 
29034   videojs$1.getPlugins = Plugin.getPlugins;
29035   videojs$1.getPlugin = Plugin.getPlugin;
29036   videojs$1.getPluginVersion = Plugin.getPluginVersion;
29037   /**
29038    * Adding languages so that they're available to all players.
29039    * Example: `videojs.addLanguage('es', { 'Hello': 'Hola' });`
29040    *
29041    * @param {string} code
29042    *        The language code or dictionary property
29043    *
29044    * @param {Object} data
29045    *        The data values to be translated
29046    *
29047    * @return {Object}
29048    *         The resulting language dictionary object
29049    */
29050 
29051   videojs$1.addLanguage = function (code, data) {
29052     var _mergeOptions;
29053 
29054     code = ('' + code).toLowerCase();
29055     videojs$1.options.languages = mergeOptions(videojs$1.options.languages, (_mergeOptions = {}, _mergeOptions[code] = data, _mergeOptions));
29056     return videojs$1.options.languages[code];
29057   };
29058   /**
29059    * A reference to the {@link module:log|log utility module} as an object.
29060    *
29061    * @type {Function}
29062    * @see  {@link module:log|log}
29063    */
29064 
29065 
29066   videojs$1.log = log;
29067   videojs$1.createLogger = createLogger$1;
29068   videojs$1.createTimeRange = videojs$1.createTimeRanges = createTimeRanges;
29069   videojs$1.formatTime = formatTime;
29070   videojs$1.setFormatTime = setFormatTime;
29071   videojs$1.resetFormatTime = resetFormatTime;
29072   videojs$1.parseUrl = parseUrl;
29073   videojs$1.isCrossOrigin = isCrossOrigin;
29074   videojs$1.EventTarget = EventTarget;
29075   videojs$1.on = on;
29076   videojs$1.one = one;
29077   videojs$1.off = off;
29078   videojs$1.trigger = trigger;
29079   /**
29080    * A cross-browser XMLHttpRequest wrapper.
29081    *
29082    * @function
29083    * @param    {Object} options
29084    *           Settings for the request.
29085    *
29086    * @return   {XMLHttpRequest|XDomainRequest}
29087    *           The request object.
29088    *
29089    * @see      https://github.com/Raynos/xhr
29090    */
29091 
29092   videojs$1.xhr = xhr;
29093   videojs$1.TextTrack = TextTrack;
29094   videojs$1.AudioTrack = AudioTrack;
29095   videojs$1.VideoTrack = VideoTrack;
29096   ['isEl', 'isTextNode', 'createEl', 'hasClass', 'addClass', 'removeClass', 'toggleClass', 'setAttributes', 'getAttributes', 'emptyEl', 'appendContent', 'insertContent'].forEach(function (k) {
29097     videojs$1[k] = function () {
29098       log.warn("videojs." + k + "() is deprecated; use videojs.dom." + k + "() instead");
29099       return Dom[k].apply(null, arguments);
29100     };
29101   });
29102   videojs$1.computedStyle = computedStyle;
29103   /**
29104    * A reference to the {@link module:dom|DOM utility module} as an object.
29105    *
29106    * @type {Object}
29107    * @see  {@link module:dom|dom}
29108    */
29109 
29110   videojs$1.dom = Dom;
29111   /**
29112    * A reference to the {@link module:url|URL utility module} as an object.
29113    *
29114    * @type {Object}
29115    * @see  {@link module:url|url}
29116    */
29117 
29118   videojs$1.url = Url;
29119   videojs$1.defineLazyProperty = defineLazyProperty;
29120 
29121   var urlToolkit = createCommonjsModule(function (module, exports
vendor: 4,749 bytes, lines 29121-29228
29121) {
29122     // see https://tools.ietf.org/html/rfc1808
29123 
29124     /* jshint ignore:start */
29125     (function (root) {
29126       /* jshint ignore:end */
29127       var URL_REGEX = /^((?:[a-zA-Z0-9+\-.]+:)?)(\/\/[^\/?#]*)?((?:[^\/\?#]*\/)*.*?)??(;.*?)?(\?.*?)?(#.*?)?$/;
29128       var FIRST_SEGMENT_REGEX = /^([^\/?#]*)(.*)$/;
29129       var SLASH_DOT_REGEX = /(?:\/|^)\.(?=\/)/g;
29130       var SLASH_DOT_DOT_REGEX = /(?:\/|^)\.\.\/(?!\.\.\/).*?(?=\/)/g;
29131       var URLToolkit = {
29132         // jshint ignore:line
29133         // If opts.alwaysNormalize is true then the path will always be normalized even when it starts with / or //
29134         // E.g
29135         // With opts.alwaysNormalize = false (default, spec compliant)
29136         // http://a.com/b/cd + /e/f/../g => http://a.com/e/f/../g
29137         // With opts.alwaysNormalize = true (not spec compliant)
29138         // http://a.com/b/cd + /e/f/../g => http://a.com/e/g
29139         buildAbsoluteURL: function buildAbsoluteURL(baseURL, relativeURL, opts) {
29140           opts = opts || {}; // remove any remaining space and CRLF
29141 
29142           baseURL = baseURL.trim();
29143           relativeURL = relativeURL.trim();
29144 
29145           if (!relativeURL) {
29146             // 2a) If the embedded URL is entirely empty, it inherits the
29147             // entire base URL (i.e., is set equal to the base URL)
29148             // and we are done.
29149             if (!opts.alwaysNormalize) {
29150               return baseURL;
29151             }
29152 
29153             var basePartsForNormalise = URLToolkit.parseURL(baseURL);
29154 
29155             if (!basePartsForNormalise) {
29156               throw new Error('Error trying to parse base URL.');
29157             }
29158 
29159             basePartsForNormalise.path = URLToolkit.normalizePath(basePartsForNormalise.path);
29160             return URLToolkit.buildURLFromParts(basePartsForNormalise);
29161           }
29162 
29163           var relativeParts = URLToolkit.parseURL(relativeURL);
29164 
29165           if (!relativeParts) {
29166             throw new Error('Error trying to parse relative URL.');
29167           }
29168 
29169           if (relativeParts.scheme) {
29170             // 2b) If the embedded URL starts with a scheme name, it is
29171             // interpreted as an absolute URL and we are done.
29172             if (!opts.alwaysNormalize) {
29173               return relativeURL;
29174             }
29175 
29176             relativeParts.path = URLToolkit.normalizePath(relativeParts.path);
29177             return URLToolkit.buildURLFromParts(relativeParts);
29178           }
29179 
29180           var baseParts = URLToolkit.parseURL(baseURL);
29181 
29182           if (!baseParts) {
29183             throw new Error('Error trying to parse base URL.');
29184           }
29185 
29186           if (!baseParts.netLoc && baseParts.path && baseParts.path[0] !== '/') {
29187             // If netLoc missing and path doesn't start with '/', assume everthing before the first '/' is the netLoc
29188             // This causes 'example.com/a' to be handled as '//example.com/a' instead of '/example.com/a'
29189             var pathParts = FIRST_SEGMENT_REGEX.exec(baseParts.path);
29190             baseParts.netLoc = pathParts[1];
29191             baseParts.path = pathParts[2];
29192           }
29193 
29194           if (baseParts.netLoc && !baseParts.path) {
29195             baseParts.path = '/';
29196           }
29197 
29198           var builtParts = {
29199             // 2c) Otherwise, the embedded URL inherits the scheme of
29200             // the base URL.
29201             scheme: baseParts.scheme,
29202             netLoc: relativeParts.netLoc,
29203             path: null,
29204             params: relativeParts.params,
29205             query: relativeParts.query,
29206             fragment: relativeParts.fragment
29207           };
29208 
29209           if (!relativeParts.netLoc) {
29210             // 3) If the embedded URL's <net_loc> is non-empty, we skip to
29211             // Step 7.  Otherwise, the embedded URL inherits the <net_loc>
29212             // (if any) of the base URL.
29213             builtParts.netLoc = baseParts.netLoc; // 4) If the embedded URL path is preceded by a slash "/", the
29214             // path is not relative and we skip to Step 7.
29215 
29216             if (relativeParts.path[0] !== '/') {
29217               if (!relativeParts.path) {
29218                 // 5) If the embedded URL path is empty (and not preceded by a
29219                 // slash), then the embedded URL inherits the base URL path
29220                 builtParts.path = baseParts.path; // 5a) if the embedded URL's <params> is non-empty, we skip to
29221                 // step 7; otherwise, it inherits the <params> of the base
29222                 // URL (if any) and
29223 
29224                 if (!relativeParts.params) {
29225                   builtParts.params = baseParts.params; // 5b) if the embedded URL's <query> is non-empty, we skip to
29226                   // step 7; otherwise, it inherits the <query> of the base
29227                   // URL (if any) and we skip to step 7.
29228 
29229                   if (!relativeParts.query) {
29230                     builtParts.query = baseParts.query;
29231                   }
29232                 }
29233               } else {
29234                 // 6) The last segment of the base URL's path (anything
29235                 // following the rightmost slash "/", or the entire path if no
29236                 // slash is present) is removed and the embedded URL's path is
29237                 // appended in its place.
29238                 var baseURLPath = baseParts.path;
29239                 var newPath = baseURLPath.substring(0, baseURLPath.lastIndexOf('/') + 1) + relativeParts.path;
29240                 builtParts.path = URLToolkit.normalizePath(newPath);
29241               }
29242             }
29243           }
29244 
29245           if (builtParts.path === null) {
29246             builtParts.path = opts.alwaysNormalize ? URLToolkit.normalizePath(relativeParts.path) : relativeParts.path;
29247           }
29248 
29249           return URLToolkit.buildURLFromParts(builtParts);
29250         },
29251         parseURL: function parseURL(url) {
29252           var parts = URL_REGEX.exec(url);
29253 
29254           if (!parts) {
29255             return null;
29256           }
29257 
29258           return {
29259             scheme: parts[1] || '',
29260             netLoc: parts[2] || '',
29261             path: parts[3] || '',
29262             params: parts[4] || '',
29263             query: parts[5] || '',
29264             fragment: parts[6] || ''
29265           };
29266         },
29267         normalizePath: function normalizePath(path) {
29268           // The following operations are
29269           // then applied, in order, to the new path:
29270           // 6a) All occurrences of "./", where "." is a complete path
29271           // segment, are removed.
29272           // 6b) If the path ends with "." as a complete path segment,
29273           // that "." is removed.
29274           path = path.split('').reverse().join('').replace(SLASH_DOT_REGEX, ''); // 6c) All occurrences of "<segment>/../", where <segment> is a
29275           // complete path segment not equal to "..", are removed.
29276           // Removal of these path segments is performed iteratively,
29277           // removing the leftmost matching pattern on each iteration,
29278           // until no matching pattern remains.
29279           // 6d) If the path ends with "<segment>/..", where <segment> is a
29280           // complete path segment not equal to "..", that
29281           // "<segment>/.." is removed.
29282 
29283           while (path.length !== (path = path.replace(SLASH_DOT_DOT_REGEX, '')).length) {} // jshint ignore:line
29284 
29285 
29286           return path.split('').reverse().join('');
29287         },
29288         buildURLFromParts: function buildURLFromParts(parts) {
29289           return parts.scheme + parts.netLoc + parts.path + parts.params + parts.query + parts.fragment;
29290         }
29291       };
29292       /* jshint ignore:start */
29293 
29294       module.exports = URLToolkit;
29295     })();
29296     /* jshint ignore:end */
29297 
29298   });
29299 
29300   /*! @name m3u8-parser @version 4.4.0 @license Apache-2.0 */
29301 
29302   function _extends() {
29303     _extends = Object.assign || function (target) {
29304       for (var i = 1; i < arguments.length; i++) {
29305         var source = arguments[i];
29306 
29307         for (var key in source) {
29308           if (Object.prototype.hasOwnProperty.call(source, key)) {
29309             target[key] = source[key];
29310           }
29311         }
29312       }
29313 
29314       return target;
29315     };
29316 
29317     return _extends.apply(this, arguments);
29318   }
29319 
29320   function _inheritsLoose$1(subClass, superClass) {
29321     subClass.prototype = Object.create(superClass.prototype);
29322     subClass.prototype.constructor = subClass;
29323     subClass.__proto__ = superClass;
29324   }
29325 
29326   function _assertThisInitialized$1(self) {
29327     if (self === void 0) {
29328       throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
29329     }
29330 
29331     return self;
29332   }
29333   /**
29334    * @file stream.js
29335    */
29336 
29337   /**
29338    * A lightweight readable stream implementation that handles event dispatching.
29339    *
29340    * @class Stream
29341    */
29342 
29343 
29344   var Stream =
29345   /*#__PURE__*/
29346   function () {
29347     function Stream() {
29348       this.listeners = {};
29349     }
29350     /**
29351      * Add a listener for a specified event type.
29352      *
29353      * @param {string} type the event name
29354      * @param {Function} listener the callback to be invoked when an event of
29355      * the specified type occurs
29356      */
29357 
29358 
29359     var _proto = Stream.prototype;
29360 
29361     _proto.on = function on(type, listener) {
29362       if (!this.listeners[type]) {
29363         this.listeners[type] = [];
29364       }
29365 
29366       this.listeners[type].push(listener);
29367     }
29368     /**
29369      * Remove a listener for a specified event type.
29370      *
29371      * @param {string} type the event name
29372      * @param {Function} listener  a function previously registered for this
29373      * type of event through `on`
29374      * @return {boolean} if we could turn it off or not
29375      */
29376     ;
29377 
29378     _proto.off = function off(type, listener) {
29379       if (!this.listeners[type]) {
29380         return false;
vendor: 40,557 bytes, lines 29381-30723
29381       }
29382 
29383       var index = this.listeners[type].indexOf(listener);
29384       this.listeners[type].splice(index, 1);
29385       return index > -1;
29386     }
29387     /**
29388      * Trigger an event of the specified type on this stream. Any additional
29389      * arguments to this function are passed as parameters to event listeners.
29390      *
29391      * @param {string} type the event name
29392      */
29393     ;
29394 
29395     _proto.trigger = function trigger(type) {
29396       var callbacks = this.listeners[type];
29397       var i;
29398       var length;
29399       var args;
29400 
29401       if (!callbacks) {
29402         return;
29403       } // Slicing the arguments on every invocation of this method
29404       // can add a significant amount of overhead. Avoid the
29405       // intermediate object creation for the common case of a
29406       // single callback argument
29407 
29408 
29409       if (arguments.length === 2) {
29410         length = callbacks.length;
29411 
29412         for (i = 0; i < length; ++i) {
29413           callbacks[i].call(this, arguments[1]);
29414         }
29415       } else {
29416         args = Array.prototype.slice.call(arguments, 1);
29417         length = callbacks.length;
29418 
29419         for (i = 0; i < length; ++i) {
29420           callbacks[i].apply(this, args);
29421         }
29422       }
29423     }
29424     /**
29425      * Destroys the stream and cleans up.
29426      */
29427     ;
29428 
29429     _proto.dispose = function dispose() {
29430       this.listeners = {};
29431     }
29432     /**
29433      * Forwards all `data` events on this stream to the destination stream. The
29434      * destination stream should provide a method `push` to receive the data
29435      * events as they arrive.
29436      *
29437      * @param {Stream} destination the stream that will receive all `data` events
29438      * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
29439      */
29440     ;
29441 
29442     _proto.pipe = function pipe(destination) {
29443       this.on('data', function (data) {
29444         destination.push(data);
29445       });
29446     };
29447 
29448     return Stream;
29449   }();
29450   /**
29451    * A stream that buffers string input and generates a `data` event for each
29452    * line.
29453    *
29454    * @class LineStream
29455    * @extends Stream
29456    */
29457 
29458 
29459   var LineStream =
29460   /*#__PURE__*/
29461   function (_Stream) {
29462     _inheritsLoose$1(LineStream, _Stream);
29463 
29464     function LineStream() {
29465       var _this;
29466 
29467       _this = _Stream.call(this) || this;
29468       _this.buffer = '';
29469       return _this;
29470     }
29471     /**
29472      * Add new data to be parsed.
29473      *
29474      * @param {string} data the text to process
29475      */
29476 
29477 
29478     var _proto = LineStream.prototype;
29479 
29480     _proto.push = function push(data) {
29481       var nextNewline;
29482       this.buffer += data;
29483       nextNewline = this.buffer.indexOf('\n');
29484 
29485       for (; nextNewline > -1; nextNewline = this.buffer.indexOf('\n')) {
29486         this.trigger('data', this.buffer.substring(0, nextNewline));
29487         this.buffer = this.buffer.substring(nextNewline + 1);
29488       }
29489     };
29490 
29491     return LineStream;
29492   }(Stream);
29493   /**
29494    * "forgiving" attribute list psuedo-grammar:
29495    * attributes -> keyvalue (',' keyvalue)*
29496    * keyvalue   -> key '=' value
29497    * key        -> [^=]*
29498    * value      -> '"' [^"]* '"' | [^,]*
29499    */
29500 
29501 
29502   var attributeSeparator = function attributeSeparator() {
29503     var key = '[^=]*';
29504     var value = '"[^"]*"|[^,]*';
29505     var keyvalue = '(?:' + key + ')=(?:' + value + ')';
29506     return new RegExp('(?:^|,)(' + keyvalue + ')');
29507   };
29508   /**
29509    * Parse attributes from a line given the separator
29510    *
29511    * @param {string} attributes the attribute line to parse
29512    */
29513 
29514 
29515   var parseAttributes = function parseAttributes(attributes) {
29516     // split the string using attributes as the separator
29517     var attrs = attributes.split(attributeSeparator());
29518     var result = {};
29519     var i = attrs.length;
29520     var attr;
29521 
29522     while (i--) {
29523       // filter out unmatched portions of the string
29524       if (attrs[i] === '') {
29525         continue;
29526       } // split the key and value
29527 
29528 
29529       attr = /([^=]*)=(.*)/.exec(attrs[i]).slice(1); // trim whitespace and remove optional quotes around the value
29530 
29531       attr[0] = attr[0].replace(/^\s+|\s+$/g, '');
29532       attr[1] = attr[1].replace(/^\s+|\s+$/g, '');
29533       attr[1] = attr[1].replace(/^['"](.*)['"]$/g, '$1');
29534       result[attr[0]] = attr[1];
29535     }
29536 
29537     return result;
29538   };
29539   /**
29540    * A line-level M3U8 parser event stream. It expects to receive input one
29541    * line at a time and performs a context-free parse of its contents. A stream
29542    * interpretation of a manifest can be useful if the manifest is expected to
29543    * be too large to fit comfortably into memory or the entirety of the input
29544    * is not immediately available. Otherwise, it's probably much easier to work
29545    * with a regular `Parser` object.
29546    *
29547    * Produces `data` events with an object that captures the parser's
29548    * interpretation of the input. That object has a property `tag` that is one
29549    * of `uri`, `comment`, or `tag`. URIs only have a single additional
29550    * property, `line`, which captures the entirety of the input without
29551    * interpretation. Comments similarly have a single additional property
29552    * `text` which is the input without the leading `#`.
29553    *
29554    * Tags always have a property `tagType` which is the lower-cased version of
29555    * the M3U8 directive without the `#EXT` or `#EXT-X-` prefix. For instance,
29556    * `#EXT-X-MEDIA-SEQUENCE` becomes `media-sequence` when parsed. Unrecognized
29557    * tags are given the tag type `unknown` and a single additional property
29558    * `data` with the remainder of the input.
29559    *
29560    * @class ParseStream
29561    * @extends Stream
29562    */
29563 
29564 
29565   var ParseStream =
29566   /*#__PURE__*/
29567   function (_Stream) {
29568     _inheritsLoose$1(ParseStream, _Stream);
29569 
29570     function ParseStream() {
29571       var _this;
29572 
29573       _this = _Stream.call(this) || this;
29574       _this.customParsers = [];
29575       _this.tagMappers = [];
29576       return _this;
29577     }
29578     /**
29579      * Parses an additional line of input.
29580      *
29581      * @param {string} line a single line of an M3U8 file to parse
29582      */
29583 
29584 
29585     var _proto = ParseStream.prototype;
29586 
29587     _proto.push = function push(line) {
29588       var _this2 = this;
29589 
29590       var match;
29591       var event; // strip whitespace
29592 
29593       line = line.trim();
29594 
29595       if (line.length === 0) {
29596         // ignore empty lines
29597         return;
29598       } // URIs
29599 
29600 
29601       if (line[0] !== '#') {
29602         this.trigger('data', {
29603           type: 'uri',
29604           uri: line
29605         });
29606         return;
29607       } // map tags
29608 
29609 
29610       var newLines = this.tagMappers.reduce(function (acc, mapper) {
29611         var mappedLine = mapper(line); // skip if unchanged
29612 
29613         if (mappedLine === line) {
29614           return acc;
29615         }
29616 
29617         return acc.concat([mappedLine]);
29618       }, [line]);
29619       newLines.forEach(function (newLine) {
29620         for (var i = 0; i < _this2.customParsers.length; i++) {
29621           if (_this2.customParsers[i].call(_this2, newLine)) {
29622             return;
29623           }
29624         } // Comments
29625 
29626 
29627         if (newLine.indexOf('#EXT') !== 0) {
29628           _this2.trigger('data', {
29629             type: 'comment',
29630             text: newLine.slice(1)
29631           });
29632 
29633           return;
29634         } // strip off any carriage returns here so the regex matching
29635         // doesn't have to account for them.
29636 
29637 
29638         newLine = newLine.replace('\r', ''); // Tags
29639 
29640         match = /^#EXTM3U/.exec(newLine);
29641 
29642         if (match) {
29643           _this2.trigger('data', {
29644             type: 'tag',
29645             tagType: 'm3u'
29646           });
29647 
29648           return;
29649         }
29650 
29651         match = /^#EXTINF:?([0-9\.]*)?,?(.*)?$/.exec(newLine);
29652 
29653         if (match) {
29654           event = {
29655             type: 'tag',
29656             tagType: 'inf'
29657           };
29658 
29659           if (match[1]) {
29660             event.duration = parseFloat(match[1]);
29661           }
29662 
29663           if (match[2]) {
29664             event.title = match[2];
29665           }
29666 
29667           _this2.trigger('data', event);
29668 
29669           return;
29670         }
29671 
29672         match = /^#EXT-X-TARGETDURATION:?([0-9.]*)?/.exec(newLine);
29673 
29674         if (match) {
29675           event = {
29676             type: 'tag',
29677             tagType: 'targetduration'
29678           };
29679 
29680           if (match[1]) {
29681             event.duration = parseInt(match[1], 10);
29682           }
29683 
29684           _this2.trigger('data', event);
29685 
29686           return;
29687         }
29688 
29689         match = /^#ZEN-TOTAL-DURATION:?([0-9.]*)?/.exec(newLine);
29690 
29691         if (match) {
29692           event = {
29693             type: 'tag',
29694             tagType: 'totalduration'
29695           };
29696 
29697           if (match[1]) {
29698             event.duration = parseInt(match[1], 10);
29699           }
29700 
29701           _this2.trigger('data', event);
29702 
29703           return;
29704         }
29705 
29706         match = /^#EXT-X-VERSION:?([0-9.]*)?/.exec(newLine);
29707 
29708         if (match) {
29709           event = {
29710             type: 'tag',
29711             tagType: 'version'
29712           };
29713 
29714           if (match[1]) {
29715             event.version = parseInt(match[1], 10);
29716           }
29717 
29718           _this2.trigger('data', event);
29719 
29720           return;
29721         }
29722 
29723         match = /^#EXT-X-MEDIA-SEQUENCE:?(\-?[0-9.]*)?/.exec(newLine);
29724 
29725         if (match) {
29726           event = {
29727             type: 'tag',
29728             tagType: 'media-sequence'
29729           };
29730 
29731           if (match[1]) {
29732             event.number = parseInt(match[1], 10);
29733           }
29734 
29735           _this2.trigger('data', event);
29736 
29737           return;
29738         }
29739 
29740         match = /^#EXT-X-DISCONTINUITY-SEQUENCE:?(\-?[0-9.]*)?/.exec(newLine);
29741 
29742         if (match) {
29743           event = {
29744             type: 'tag',
29745             tagType: 'discontinuity-sequence'
29746           };
29747 
29748           if (match[1]) {
29749             event.number = parseInt(match[1], 10);
29750           }
29751 
29752           _this2.trigger('data', event);
29753 
29754           return;
29755         }
29756 
29757         match = /^#EXT-X-PLAYLIST-TYPE:?(.*)?$/.exec(newLine);
29758 
29759         if (match) {
29760           event = {
29761             type: 'tag',
29762             tagType: 'playlist-type'
29763           };
29764 
29765           if (match[1]) {
29766             event.playlistType = match[1];
29767           }
29768 
29769           _this2.trigger('data', event);
29770 
29771           return;
29772         }
29773 
29774         match = /^#EXT-X-BYTERANGE:?([0-9.]*)?@?([0-9.]*)?/.exec(newLine);
29775 
29776         if (match) {
29777           event = {
29778             type: 'tag',
29779             tagType: 'byterange'
29780           };
29781 
29782           if (match[1]) {
29783             event.length = parseInt(match[1], 10);
29784           }
29785 
29786           if (match[2]) {
29787             event.offset = parseInt(match[2], 10);
29788           }
29789 
29790           _this2.trigger('data', event);
29791 
29792           return;
29793         }
29794 
29795         match = /^#EXT-X-ALLOW-CACHE:?(YES|NO)?/.exec(newLine);
29796 
29797         if (match) {
29798           event = {
29799             type: 'tag',
29800             tagType: 'allow-cache'
29801           };
29802 
29803           if (match[1]) {
29804             event.allowed = !/NO/.test(match[1]);
29805           }
29806 
29807           _this2.trigger('data', event);
29808 
29809           return;
29810         }
29811 
29812         match = /^#EXT-X-MAP:?(.*)$/.exec(newLine);
29813 
29814         if (match) {
29815           event = {
29816             type: 'tag',
29817             tagType: 'map'
29818           };
29819 
29820           if (match[1]) {
29821             var attributes = parseAttributes(match[1]);
29822 
29823             if (attributes.URI) {
29824               event.uri = attributes.URI;
29825             }
29826 
29827             if (attributes.BYTERANGE) {
29828               var _attributes$BYTERANGE = attributes.BYTERANGE.split('@'),
29829                   length = _attributes$BYTERANGE[0],
29830                   offset = _attributes$BYTERANGE[1];
29831 
29832               event.byterange = {};
29833 
29834               if (length) {
29835                 event.byterange.length = parseInt(length, 10);
29836               }
29837 
29838               if (offset) {
29839                 event.byterange.offset = parseInt(offset, 10);
29840               }
29841             }
29842           }
29843 
29844           _this2.trigger('data', event);
29845 
29846           return;
29847         }
29848 
29849         match = /^#EXT-X-STREAM-INF:?(.*)$/.exec(newLine);
29850 
29851         if (match) {
29852           event = {
29853             type: 'tag',
29854             tagType: 'stream-inf'
29855           };
29856 
29857           if (match[1]) {
29858             event.attributes = parseAttributes(match[1]);
29859 
29860             if (event.attributes.RESOLUTION) {
29861               var split = event.attributes.RESOLUTION.split('x');
29862               var resolution = {};
29863 
29864               if (split[0]) {
29865                 resolution.width = parseInt(split[0], 10);
29866               }
29867 
29868               if (split[1]) {
29869                 resolution.height = parseInt(split[1], 10);
29870               }
29871 
29872               event.attributes.RESOLUTION = resolution;
29873             }
29874 
29875             if (event.attributes.BANDWIDTH) {
29876               event.attributes.BANDWIDTH = parseInt(event.attributes.BANDWIDTH, 10);
29877             }
29878 
29879             if (event.attributes['PROGRAM-ID']) {
29880               event.attributes['PROGRAM-ID'] = parseInt(event.attributes['PROGRAM-ID'], 10);
29881             }
29882           }
29883 
29884           _this2.trigger('data', event);
29885 
29886           return;
29887         }
29888 
29889         match = /^#EXT-X-MEDIA:?(.*)$/.exec(newLine);
29890 
29891         if (match) {
29892           event = {
29893             type: 'tag',
29894             tagType: 'media'
29895           };
29896 
29897           if (match[1]) {
29898             event.attributes = parseAttributes(match[1]);
29899           }
29900 
29901           _this2.trigger('data', event);
29902 
29903           return;
29904         }
29905 
29906         match = /^#EXT-X-ENDLIST/.exec(newLine);
29907 
29908         if (match) {
29909           _this2.trigger('data', {
29910             type: 'tag',
29911             tagType: 'endlist'
29912           });
29913 
29914           return;
29915         }
29916 
29917         match = /^#EXT-X-DISCONTINUITY/.exec(newLine);
29918 
29919         if (match) {
29920           _this2.trigger('data', {
29921             type: 'tag',
29922             tagType: 'discontinuity'
29923           });
29924 
29925           return;
29926         }
29927 
29928         match = /^#EXT-X-PROGRAM-DATE-TIME:?(.*)$/.exec(newLine);
29929 
29930         if (match) {
29931           event = {
29932             type: 'tag',
29933             tagType: 'program-date-time'
29934           };
29935 
29936           if (match[1]) {
29937             event.dateTimeString = match[1];
29938             event.dateTimeObject = new Date(match[1]);
29939           }
29940 
29941           _this2.trigger('data', event);
29942 
29943           return;
29944         }
29945 
29946         match = /^#EXT-X-KEY:?(.*)$/.exec(newLine);
29947 
29948         if (match) {
29949           event = {
29950             type: 'tag',
29951             tagType: 'key'
29952           };
29953 
29954           if (match[1]) {
29955             event.attributes = parseAttributes(match[1]); // parse the IV string into a Uint32Array
29956 
29957             if (event.attributes.IV) {
29958               if (event.attributes.IV.substring(0, 2).toLowerCase() === '0x') {
29959                 event.attributes.IV = event.attributes.IV.substring(2);
29960               }
29961 
29962               event.attributes.IV = event.attributes.IV.match(/.{8}/g);
29963               event.attributes.IV[0] = parseInt(event.attributes.IV[0], 16);
29964               event.attributes.IV[1] = parseInt(event.attributes.IV[1], 16);
29965               event.attributes.IV[2] = parseInt(event.attributes.IV[2], 16);
29966               event.attributes.IV[3] = parseInt(event.attributes.IV[3], 16);
29967               event.attributes.IV = new Uint32Array(event.attributes.IV);
29968             }
29969           }
29970 
29971           _this2.trigger('data', event);
29972 
29973           return;
29974         }
29975 
29976         match = /^#EXT-X-START:?(.*)$/.exec(newLine);
29977 
29978         if (match) {
29979           event = {
29980             type: 'tag',
29981             tagType: 'start'
29982           };
29983 
29984           if (match[1]) {
29985             event.attributes = parseAttributes(match[1]);
29986             event.attributes['TIME-OFFSET'] = parseFloat(event.attributes['TIME-OFFSET']);
29987             event.attributes.PRECISE = /YES/.test(event.attributes.PRECISE);
29988           }
29989 
29990           _this2.trigger('data', event);
29991 
29992           return;
29993         }
29994 
29995         match = /^#EXT-X-CUE-OUT-CONT:?(.*)?$/.exec(newLine);
29996 
29997         if (match) {
29998           event = {
29999             type: 'tag',
30000             tagType: 'cue-out-cont'
30001           };
30002 
30003           if (match[1]) {
30004             event.data = match[1];
30005           } else {
30006             event.data = '';
30007           }
30008 
30009           _this2.trigger('data', event);
30010 
30011           return;
30012         }
30013 
30014         match = /^#EXT-X-CUE-OUT:?(.*)?$/.exec(newLine);
30015 
30016         if (match) {
30017           event = {
30018             type: 'tag',
30019             tagType: 'cue-out'
30020           };
30021 
30022           if (match[1]) {
30023             event.data = match[1];
30024           } else {
30025             event.data = '';
30026           }
30027 
30028           _this2.trigger('data', event);
30029 
30030           return;
30031         }
30032 
30033         match = /^#EXT-X-CUE-IN:?(.*)?$/.exec(newLine);
30034 
30035         if (match) {
30036           event = {
30037             type: 'tag',
30038             tagType: 'cue-in'
30039           };
30040 
30041           if (match[1]) {
30042             event.data = match[1];
30043           } else {
30044             event.data = '';
30045           }
30046 
30047           _this2.trigger('data', event);
30048 
30049           return;
30050         } // unknown tag type
30051 
30052 
30053         _this2.trigger('data', {
30054           type: 'tag',
30055           data: newLine.slice(4)
30056         });
30057       });
30058     }
30059     /**
30060      * Add a parser for custom headers
30061      *
30062      * @param {Object}   options              a map of options for the added parser
30063      * @param {RegExp}   options.expression   a regular expression to match the custom header
30064      * @param {string}   options.customType   the custom type to register to the output
30065      * @param {Function} [options.dataParser] function to parse the line into an object
30066      * @param {boolean}  [options.segment]    should tag data be attached to the segment object
30067      */
30068     ;
30069 
30070     _proto.addParser = function addParser(_ref) {
30071       var _this3 = this;
30072 
30073       var expression = _ref.expression,
30074           customType = _ref.customType,
30075           dataParser = _ref.dataParser,
30076           segment = _ref.segment;
30077 
30078       if (typeof dataParser !== 'function') {
30079         dataParser = function dataParser(line) {
30080           return line;
30081         };
30082       }
30083 
30084       this.customParsers.push(function (line) {
30085         var match = expression.exec(line);
30086 
30087         if (match) {
30088           _this3.trigger('data', {
30089             type: 'custom',
30090             data: dataParser(line),
30091             customType: customType,
30092             segment: segment
30093           });
30094 
30095           return true;
30096         }
30097       });
30098     }
30099     /**
30100      * Add a custom header mapper
30101      *
30102      * @param {Object}   options
30103      * @param {RegExp}   options.expression   a regular expression to match the custom header
30104      * @param {Function} options.map          function to translate tag into a different tag
30105      */
30106     ;
30107 
30108     _proto.addTagMapper = function addTagMapper(_ref2) {
30109       var expression = _ref2.expression,
30110           map = _ref2.map;
30111 
30112       var mapFn = function mapFn(line) {
30113         if (expression.test(line)) {
30114           return map(line);
30115         }
30116 
30117         return line;
30118       };
30119 
30120       this.tagMappers.push(mapFn);
30121     };
30122 
30123     return ParseStream;
30124   }(Stream);
30125 
30126   function decodeB64ToUint8Array(b64Text) {
30127     var decodedString = window$1.atob(b64Text || '');
30128     var array = new Uint8Array(decodedString.length);
30129 
30130     for (var i = 0; i < decodedString.length; i++) {
30131       array[i] = decodedString.charCodeAt(i);
30132     }
30133 
30134     return array;
30135   }
30136   /**
30137    * A parser for M3U8 files. The current interpretation of the input is
30138    * exposed as a property `manifest` on parser objects. It's just two lines to
30139    * create and parse a manifest once you have the contents available as a string:
30140    *
30141    * ```js
30142    * var parser = new m3u8.Parser();
30143    * parser.push(xhr.responseText);
30144    * ```
30145    *
30146    * New input can later be applied to update the manifest object by calling
30147    * `push` again.
30148    *
30149    * The parser attempts to create a usable manifest object even if the
30150    * underlying input is somewhat nonsensical. It emits `info` and `warning`
30151    * events during the parse if it encounters input that seems invalid or
30152    * requires some property of the manifest object to be defaulted.
30153    *
30154    * @class Parser
30155    * @extends Stream
30156    */
30157 
30158 
30159   var Parser =
30160   /*#__PURE__*/
30161   function (_Stream) {
30162     _inheritsLoose$1(Parser, _Stream);
30163 
30164     function Parser() {
30165       var _this;
30166 
30167       _this = _Stream.call(this) || this;
30168       _this.lineStream = new LineStream();
30169       _this.parseStream = new ParseStream();
30170 
30171       _this.lineStream.pipe(_this.parseStream);
30172       /* eslint-disable consistent-this */
30173 
30174 
30175       var self = _assertThisInitialized$1(_this);
30176       /* eslint-enable consistent-this */
30177 
30178 
30179       var uris = [];
30180       var currentUri = {}; // if specified, the active EXT-X-MAP definition
30181 
30182       var currentMap; // if specified, the active decryption key
30183 
30184       var _key;
30185 
30186       var noop = function noop() {};
30187 
30188       var defaultMediaGroups = {
30189         'AUDIO': {},
30190         'VIDEO': {},
30191         'CLOSED-CAPTIONS': {},
30192         'SUBTITLES': {}
30193       }; // This is the Widevine UUID from DASH IF IOP. The same exact string is
30194       // used in MPDs with Widevine encrypted streams.
30195 
30196       var widevineUuid = 'urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed'; // group segments into numbered timelines delineated by discontinuities
30197 
30198       var currentTimeline = 0; // the manifest is empty until the parse stream begins delivering data
30199 
30200       _this.manifest = {
30201         allowCache: true,
30202         discontinuityStarts: [],
30203         segments: []
30204       }; // update the manifest with the m3u8 entry from the parse stream
30205 
30206       _this.parseStream.on('data', function (entry) {
30207         var mediaGroup;
30208         var rendition;
30209         ({
30210           tag: function tag() {
30211             // switch based on the tag type
30212             (({
30213               'allow-cache': function allowCache() {
30214                 this.manifest.allowCache = entry.allowed;
30215 
30216                 if (!('allowed' in entry)) {
30217                   this.trigger('info', {
30218                     message: 'defaulting allowCache to YES'
30219                   });
30220                   this.manifest.allowCache = true;
30221                 }
30222               },
30223               byterange: function byterange() {
30224                 var byterange = {};
30225 
30226                 if ('length' in entry) {
30227                   currentUri.byterange = byterange;
30228                   byterange.length = entry.length;
30229 
30230                   if (!('offset' in entry)) {
30231                     this.trigger('info', {
30232                       message: 'defaulting offset to zero'
30233                     });
30234                     entry.offset = 0;
30235                   }
30236                 }
30237 
30238                 if ('offset' in entry) {
30239                   currentUri.byterange = byterange;
30240                   byterange.offset = entry.offset;
30241                 }
30242               },
30243               endlist: function endlist() {
30244                 this.manifest.endList = true;
30245               },
30246               inf: function inf() {
30247                 if (!('mediaSequence' in this.manifest)) {
30248                   this.manifest.mediaSequence = 0;
30249                   this.trigger('info', {
30250                     message: 'defaulting media sequence to zero'
30251                   });
30252                 }
30253 
30254                 if (!('discontinuitySequence' in this.manifest)) {
30255                   this.manifest.discontinuitySequence = 0;
30256                   this.trigger('info', {
30257                     message: 'defaulting discontinuity sequence to zero'
30258                   });
30259                 }
30260 
30261                 if (entry.duration > 0) {
30262                   currentUri.duration = entry.duration;
30263                 }
30264 
30265                 if (entry.duration === 0) {
30266                   currentUri.duration = 0.01;
30267                   this.trigger('info', {
30268                     message: 'updating zero segment duration to a small value'
30269                   });
30270                 }
30271 
30272                 this.manifest.segments = uris;
30273               },
30274               key: function key() {
30275                 if (!entry.attributes) {
30276                   this.trigger('warn', {
30277                     message: 'ignoring key declaration without attribute list'
30278                   });
30279                   return;
30280                 } // clear the active encryption key
30281 
30282 
30283                 if (entry.attributes.METHOD === 'NONE') {
30284                   _key = null;
30285                   return;
30286                 }
30287 
30288                 if (!entry.attributes.URI) {
30289                   this.trigger('warn', {
30290                     message: 'ignoring key declaration without URI'
30291                   });
30292                   return;
30293                 } // check if the content is encrypted for Widevine
30294                 // Widevine/HLS spec: https://storage.googleapis.com/wvdocs/Widevine_DRM_HLS.pdf
30295 
30296 
30297                 if (entry.attributes.KEYFORMAT === widevineUuid) {
30298                   var VALID_METHODS = ['SAMPLE-AES', 'SAMPLE-AES-CTR', 'SAMPLE-AES-CENC'];
30299 
30300                   if (VALID_METHODS.indexOf(entry.attributes.METHOD) === -1) {
30301                     this.trigger('warn', {
30302                       message: 'invalid key method provided for Widevine'
30303                     });
30304                     return;
30305                   }
30306 
30307                   if (entry.attributes.METHOD === 'SAMPLE-AES-CENC') {
30308                     this.trigger('warn', {
30309                       message: 'SAMPLE-AES-CENC is deprecated, please use SAMPLE-AES-CTR instead'
30310                     });
30311                   }
30312 
30313                   if (entry.attributes.URI.substring(0, 23) !== 'data:text/plain;base64,') {
30314                     this.trigger('warn', {
30315                       message: 'invalid key URI provided for Widevine'
30316                     });
30317                     return;
30318                   }
30319 
30320                   if (!(entry.attributes.KEYID && entry.attributes.KEYID.substring(0, 2) === '0x')) {
30321                     this.trigger('warn', {
30322                       message: 'invalid key ID provided for Widevine'
30323                     });
30324                     return;
30325                   } // if Widevine key attributes are valid, store them as `contentProtection`
30326                   // on the manifest to emulate Widevine tag structure in a DASH mpd
30327 
30328 
30329                   this.manifest.contentProtection = {
30330                     'com.widevine.alpha': {
30331                       attributes: {
30332                         schemeIdUri: entry.attributes.KEYFORMAT,
30333                         // remove '0x' from the key id string
30334                         keyId: entry.attributes.KEYID.substring(2)
30335                       },
30336                       // decode the base64-encoded PSSH box
30337                       pssh: decodeB64ToUint8Array(entry.attributes.URI.split(',')[1])
30338                     }
30339                   };
30340                   return;
30341                 }
30342 
30343                 if (!entry.attributes.METHOD) {
30344                   this.trigger('warn', {
30345                     message: 'defaulting key method to AES-128'
30346                   });
30347                 } // setup an encryption key for upcoming segments
30348 
30349 
30350                 _key = {
30351                   method: entry.attributes.METHOD || 'AES-128',
30352                   uri: entry.attributes.URI
30353                 };
30354 
30355                 if (typeof entry.attributes.IV !== 'undefined') {
30356                   _key.iv = entry.attributes.IV;
30357                 }
30358               },
30359               'media-sequence': function mediaSequence() {
30360                 if (!isFinite(entry.number)) {
30361                   this.trigger('warn', {
30362                     message: 'ignoring invalid media sequence: ' + entry.number
30363                   });
30364                   return;
30365                 }
30366 
30367                 this.manifest.mediaSequence = entry.number;
30368               },
30369               'discontinuity-sequence': function discontinuitySequence() {
30370                 if (!isFinite(entry.number)) {
30371                   this.trigger('warn', {
30372                     message: 'ignoring invalid discontinuity sequence: ' + entry.number
30373                   });
30374                   return;
30375                 }
30376 
30377                 this.manifest.discontinuitySequence = entry.number;
30378                 currentTimeline = entry.number;
30379               },
30380               'playlist-type': function playlistType() {
30381                 if (!/VOD|EVENT/.test(entry.playlistType)) {
30382                   this.trigger('warn', {
30383                     message: 'ignoring unknown playlist type: ' + entry.playlist
30384                   });
30385                   return;
30386                 }
30387 
30388                 this.manifest.playlistType = entry.playlistType;
30389               },
30390               map: function map() {
30391                 currentMap = {};
30392 
30393                 if (entry.uri) {
30394                   currentMap.uri = entry.uri;
30395                 }
30396 
30397                 if (entry.byterange) {
30398                   currentMap.byterange = entry.byterange;
30399                 }
30400               },
30401               'stream-inf': function streamInf() {
30402                 this.manifest.playlists = uris;
30403                 this.manifest.mediaGroups = this.manifest.mediaGroups || defaultMediaGroups;
30404 
30405                 if (!entry.attributes) {
30406                   this.trigger('warn', {
30407                     message: 'ignoring empty stream-inf attributes'
30408                   });
30409                   return;
30410                 }
30411 
30412                 if (!currentUri.attributes) {
30413                   currentUri.attributes = {};
30414                 }
30415 
30416                 _extends(currentUri.attributes, entry.attributes);
30417               },
30418               media: function media() {
30419                 this.manifest.mediaGroups = this.manifest.mediaGroups || defaultMediaGroups;
30420 
30421                 if (!(entry.attributes && entry.attributes.TYPE && entry.attributes['GROUP-ID'] && entry.attributes.NAME)) {
30422                   this.trigger('warn', {
30423                     message: 'ignoring incomplete or missing media group'
30424                   });
30425                   return;
30426                 } // find the media group, creating defaults as necessary
30427 
30428 
30429                 var mediaGroupType = this.manifest.mediaGroups[entry.attributes.TYPE];
30430                 mediaGroupType[entry.attributes['GROUP-ID']] = mediaGroupType[entry.attributes['GROUP-ID']] || {};
30431                 mediaGroup = mediaGroupType[entry.attributes['GROUP-ID']]; // collect the rendition metadata
30432 
30433                 rendition = {
30434                   "default": /yes/i.test(entry.attributes.DEFAULT)
30435                 };
30436 
30437                 if (rendition["default"]) {
30438                   rendition.autoselect = true;
30439                 } else {
30440                   rendition.autoselect = /yes/i.test(entry.attributes.AUTOSELECT);
30441                 }
30442 
30443                 if (entry.attributes.LANGUAGE) {
30444                   rendition.language = entry.attributes.LANGUAGE;
30445                 }
30446 
30447                 if (entry.attributes.URI) {
30448                   rendition.uri = entry.attributes.URI;
30449                 }
30450 
30451                 if (entry.attributes['INSTREAM-ID']) {
30452                   rendition.instreamId = entry.attributes['INSTREAM-ID'];
30453                 }
30454 
30455                 if (entry.attributes.CHARACTERISTICS) {
30456                   rendition.characteristics = entry.attributes.CHARACTERISTICS;
30457                 }
30458 
30459                 if (entry.attributes.FORCED) {
30460                   rendition.forced = /yes/i.test(entry.attributes.FORCED);
30461                 } // insert the new rendition
30462 
30463 
30464                 mediaGroup[entry.attributes.NAME] = rendition;
30465               },
30466               discontinuity: function discontinuity() {
30467                 currentTimeline += 1;
30468                 currentUri.discontinuity = true;
30469                 this.manifest.discontinuityStarts.push(uris.length);
30470               },
30471               'program-date-time': function programDateTime() {
30472                 if (typeof this.manifest.dateTimeString === 'undefined') {
30473                   // PROGRAM-DATE-TIME is a media-segment tag, but for backwards
30474                   // compatibility, we add the first occurence of the PROGRAM-DATE-TIME tag
30475                   // to the manifest object
30476                   // TODO: Consider removing this in future major version
30477                   this.manifest.dateTimeString = entry.dateTimeString;
30478                   this.manifest.dateTimeObject = entry.dateTimeObject;
30479                 }
30480 
30481                 currentUri.dateTimeString = entry.dateTimeString;
30482                 currentUri.dateTimeObject = entry.dateTimeObject;
30483               },
30484               targetduration: function targetduration() {
30485                 if (!isFinite(entry.duration) || entry.duration < 0) {
30486                   this.trigger('warn', {
30487                     message: 'ignoring invalid target duration: ' + entry.duration
30488                   });
30489                   return;
30490                 }
30491 
30492                 this.manifest.targetDuration = entry.duration;
30493               },
30494               totalduration: function totalduration() {
30495                 if (!isFinite(entry.duration) || entry.duration < 0) {
30496                   this.trigger('warn', {
30497                     message: 'ignoring invalid total duration: ' + entry.duration
30498                   });
30499                   return;
30500                 }
30501 
30502                 this.manifest.totalDuration = entry.duration;
30503               },
30504               start: function start() {
30505                 if (!entry.attributes || isNaN(entry.attributes['TIME-OFFSET'])) {
30506                   this.trigger('warn', {
30507                     message: 'ignoring start declaration without appropriate attribute list'
30508                   });
30509                   return;
30510                 }
30511 
30512                 this.manifest.start = {
30513                   timeOffset: entry.attributes['TIME-OFFSET'],
30514                   precise: entry.attributes.PRECISE
30515                 };
30516               },
30517               'cue-out': function cueOut() {
30518                 currentUri.cueOut = entry.data;
30519               },
30520               'cue-out-cont': function cueOutCont() {
30521                 currentUri.cueOutCont = entry.data;
30522               },
30523               'cue-in': function cueIn() {
30524                 currentUri.cueIn = entry.data;
30525               }
30526             })[entry.tagType] || noop).call(self);
30527           },
30528           uri: function uri() {
30529             currentUri.uri = entry.uri;
30530             uris.push(currentUri); // if no explicit duration was declared, use the target duration
30531 
30532             if (this.manifest.targetDuration && !('duration' in currentUri)) {
30533               this.trigger('warn', {
30534                 message: 'defaulting segment duration to the target duration'
30535               });
30536               currentUri.duration = this.manifest.targetDuration;
30537             } // annotate with encryption information, if necessary
30538 
30539 
30540             if (_key) {
30541               currentUri.key = _key;
30542             }
30543 
30544             currentUri.timeline = currentTimeline; // annotate with initialization segment information, if necessary
30545 
30546             if (currentMap) {
30547               currentUri.map = currentMap;
30548             } // prepare for the next URI
30549 
30550 
30551             currentUri = {};
30552           },
30553           comment: function comment() {// comments are not important for playback
30554           },
30555           custom: function custom() {
30556             // if this is segment-level data attach the output to the segment
30557             if (entry.segment) {
30558               currentUri.custom = currentUri.custom || {};
30559               currentUri.custom[entry.customType] = entry.data; // if this is manifest-level data attach to the top level manifest object
30560             } else {
30561               this.manifest.custom = this.manifest.custom || {};
30562               this.manifest.custom[entry.customType] = entry.data;
30563             }
30564           }
30565         })[entry.type].call(self);
30566       });
30567 
30568       return _this;
30569     }
30570     /**
30571      * Parse the input string and update the manifest object.
30572      *
30573      * @param {string} chunk a potentially incomplete portion of the manifest
30574      */
30575 
30576 
30577     var _proto = Parser.prototype;
30578 
30579     _proto.push = function push(chunk) {
30580       this.lineStream.push(chunk);
30581     }
30582     /**
30583      * Flush any remaining input. This can be handy if the last line of an M3U8
30584      * manifest did not contain a trailing newline but the file has been
30585      * completely received.
30586      */
30587     ;
30588 
30589     _proto.end = function end() {
30590       // flush any buffered input
30591       this.lineStream.push('\n');
30592     }
30593     /**
30594      * Add an additional parser for non-standard tags
30595      *
30596      * @param {Object}   options              a map of options for the added parser
30597      * @param {RegExp}   options.expression   a regular expression to match the custom header
30598      * @param {string}   options.type         the type to register to the output
30599      * @param {Function} [options.dataParser] function to parse the line into an object
30600      * @param {boolean}  [options.segment]    should tag data be attached to the segment object
30601      */
30602     ;
30603 
30604     _proto.addParser = function addParser(options) {
30605       this.parseStream.addParser(options);
30606     }
30607     /**
30608      * Add a custom header mapper
30609      *
30610      * @param {Object}   options
30611      * @param {RegExp}   options.expression   a regular expression to match the custom header
30612      * @param {Function} options.map          function to translate tag into a different tag
30613      */
30614     ;
30615 
30616     _proto.addTagMapper = function addTagMapper(options) {
30617       this.parseStream.addTagMapper(options);
30618     };
30619 
30620     return Parser;
30621   }(Stream);
30622 
30623   /*! @name mpd-parser @version 0.8.1 @license Apache-2.0 */
30624 
30625   var isObject$1 = function isObject(obj) {
30626     return !!obj && typeof obj === 'object';
30627   };
30628 
30629   var merge = function merge() {
30630     for (var _len = arguments.length, objects = new Array(_len), _key = 0; _key < _len; _key++) {
30631       objects[_key] = arguments[_key];
30632     }
30633 
30634     return objects.reduce(function (result, source) {
30635       Object.keys(source).forEach(function (key) {
30636         if (Array.isArray(result[key]) && Array.isArray(source[key])) {
30637           result[key] = result[key].concat(source[key]);
30638         } else if (isObject$1(result[key]) && isObject$1(source[key])) {
30639           result[key] = merge(result[key], source[key]);
30640         } else {
30641           result[key] = source[key];
30642         }
30643       });
30644       return result;
30645     }, {});
30646   };
30647 
30648   var values = function values(o) {
30649     return Object.keys(o).map(function (k) {
30650       return o[k];
30651     });
30652   };
30653 
30654   var range = function range(start, end) {
30655     var result = [];
30656 
30657     for (var i = start; i < end; i++) {
30658       result.push(i);
30659     }
30660 
30661     return result;
30662   };
30663 
30664   var flatten = function flatten(lists) {
30665     return lists.reduce(function (x, y) {
30666       return x.concat(y);
30667     }, []);
30668   };
30669 
30670   var from = function from(list) {
30671     if (!list.length) {
30672       return [];
30673     }
30674 
30675     var result = [];
30676 
30677     for (var i = 0; i < list.length; i++) {
30678       result.push(list[i]);
30679     }
30680 
30681     return result;
30682   };
30683 
30684   var findIndexes = function findIndexes(l, key) {
30685     return l.reduce(function (a, e, i) {
30686       if (e[key]) {
30687         a.push(i);
30688       }
30689 
30690       return a;
30691     }, []);
30692   };
30693 
30694   var errors = {
30695     INVALID_NUMBER_OF_PERIOD: 'INVALID_NUMBER_OF_PERIOD',
30696     DASH_EMPTY_MANIFEST: 'DASH_EMPTY_MANIFEST',
30697     DASH_INVALID_XML: 'DASH_INVALID_XML',
30698     NO_BASE_URL: 'NO_BASE_URL',
30699     MISSING_SEGMENT_INFORMATION: 'MISSING_SEGMENT_INFORMATION',
30700     SEGMENT_TIME_UNSPECIFIED: 'SEGMENT_TIME_UNSPECIFIED',
30701     UNSUPPORTED_UTC_TIMING_SCHEME: 'UNSUPPORTED_UTC_TIMING_SCHEME'
30702   };
30703 
30704   function createCommonjsModule$1(fn, module) {
30705     return module = {
30706       exports: {}
30707     }, fn(module, module.exports), module.exports;
30708   }
30709 
30710   var urlToolkit$1 = createCommonjsModule$1(function (module, exports) {
30711     // see https://tools.ietf.org/html/rfc1808
30712 
30713     /* jshint ignore:start */
30714     (function (root) {
30715       /* jshint ignore:end */
30716       var URL_REGEX = /^((?:[a-zA-Z0-9+\-.]+:)?)(\/\/[^\/?#]*)?((?:[^\/\?#]*\/)*.*?)??(;.*?)?(\?.*?)?(#.*?)?$/;
30717       var FIRST_SEGMENT_REGEX = /^([^\/?#]*)(.*)$/;
30718       var SLASH_DOT_REGEX = /(?:\/|^)\.(?=\/)/g;
30719       var SLASH_DOT_DOT_REGEX = /(?:\/|^)\.\.\/(?!\.\.\/).*?(?=\/)/g;
30720       var URLToolkit = {
30721         // jshint ignore:line
30722         // If opts.alwaysNormalize is true then the path will always be normalized even when it starts with / or //
30723         // E.g
vendor: 4,132 bytes, lines 30724-30817
30724         // With opts.alwaysNormalize = false (default, spec compliant)
30725         // http://a.com/b/cd + /e/f/../g => http://a.com/e/f/../g
30726         // With opts.alwaysNormalize = true (not spec compliant)
30727         // http://a.com/b/cd + /e/f/../g => http://a.com/e/g
30728         buildAbsoluteURL: function buildAbsoluteURL(baseURL, relativeURL, opts) {
30729           opts = opts || {}; // remove any remaining space and CRLF
30730 
30731           baseURL = baseURL.trim();
30732           relativeURL = relativeURL.trim();
30733 
30734           if (!relativeURL) {
30735             // 2a) If the embedded URL is entirely empty, it inherits the
30736             // entire base URL (i.e., is set equal to the base URL)
30737             // and we are done.
30738             if (!opts.alwaysNormalize) {
30739               return baseURL;
30740             }
30741 
30742             var basePartsForNormalise = URLToolkit.parseURL(baseURL);
30743 
30744             if (!basePartsForNormalise) {
30745               throw new Error('Error trying to parse base URL.');
30746             }
30747 
30748             basePartsForNormalise.path = URLToolkit.normalizePath(basePartsForNormalise.path);
30749             return URLToolkit.buildURLFromParts(basePartsForNormalise);
30750           }
30751 
30752           var relativeParts = URLToolkit.parseURL(relativeURL);
30753 
30754           if (!relativeParts) {
30755             throw new Error('Error trying to parse relative URL.');
30756           }
30757 
30758           if (relativeParts.scheme) {
30759             // 2b) If the embedded URL starts with a scheme name, it is
30760             // interpreted as an absolute URL and we are done.
30761             if (!opts.alwaysNormalize) {
30762               return relativeURL;
30763             }
30764 
30765             relativeParts.path = URLToolkit.normalizePath(relativeParts.path);
30766             return URLToolkit.buildURLFromParts(relativeParts);
30767           }
30768 
30769           var baseParts = URLToolkit.parseURL(baseURL);
30770 
30771           if (!baseParts) {
30772             throw new Error('Error trying to parse base URL.');
30773           }
30774 
30775           if (!baseParts.netLoc && baseParts.path && baseParts.path[0] !== '/') {
30776             // If netLoc missing and path doesn't start with '/', assume everthing before the first '/' is the netLoc
30777             // This causes 'example.com/a' to be handled as '//example.com/a' instead of '/example.com/a'
30778             var pathParts = FIRST_SEGMENT_REGEX.exec(baseParts.path);
30779             baseParts.netLoc = pathParts[1];
30780             baseParts.path = pathParts[2];
30781           }
30782 
30783           if (baseParts.netLoc && !baseParts.path) {
30784             baseParts.path = '/';
30785           }
30786 
30787           var builtParts = {
30788             // 2c) Otherwise, the embedded URL inherits the scheme of
30789             // the base URL.
30790             scheme: baseParts.scheme,
30791             netLoc: relativeParts.netLoc,
30792             path: null,
30793             params: relativeParts.params,
30794             query: relativeParts.query,
30795             fragment: relativeParts.fragment
30796           };
30797 
30798           if (!relativeParts.netLoc) {
30799             // 3) If the embedded URL's <net_loc> is non-empty, we skip to
30800             // Step 7.  Otherwise, the embedded URL inherits the <net_loc>
30801             // (if any) of the base URL.
30802             builtParts.netLoc = baseParts.netLoc; // 4) If the embedded URL path is preceded by a slash "/", the
30803             // path is not relative and we skip to Step 7.
30804 
30805             if (relativeParts.path[0] !== '/') {
30806               if (!relativeParts.path) {
30807                 // 5) If the embedded URL path is empty (and not preceded by a
30808                 // slash), then the embedded URL inherits the base URL path
30809                 builtParts.path = baseParts.path; // 5a) if the embedded URL's <params> is non-empty, we skip to
30810                 // step 7; otherwise, it inherits the <params> of the base
30811                 // URL (if any) and
30812 
30813                 if (!relativeParts.params) {
30814                   builtParts.params = baseParts.params; // 5b) if the embedded URL's <query> is non-empty, we skip to
30815                   // step 7; otherwise, it inherits the <query> of the base
30816                   // URL (if any) and we skip to step 7.
30817 
vendor: 4,112 bytes, lines 30818-30920
30818                   if (!relativeParts.query) {
30819                     builtParts.query = baseParts.query;
30820                   }
30821                 }
30822               } else {
30823                 // 6) The last segment of the base URL's path (anything
30824                 // following the rightmost slash "/", or the entire path if no
30825                 // slash is present) is removed and the embedded URL's path is
30826                 // appended in its place.
30827                 var baseURLPath = baseParts.path;
30828                 var newPath = baseURLPath.substring(0, baseURLPath.lastIndexOf('/') + 1) + relativeParts.path;
30829                 builtParts.path = URLToolkit.normalizePath(newPath);
30830               }
30831             }
30832           }
30833 
30834           if (builtParts.path === null) {
30835             builtParts.path = opts.alwaysNormalize ? URLToolkit.normalizePath(relativeParts.path) : relativeParts.path;
30836           }
30837 
30838           return URLToolkit.buildURLFromParts(builtParts);
30839         },
30840         parseURL: function parseURL(url) {
30841           var parts = URL_REGEX.exec(url);
30842 
30843           if (!parts) {
30844             return null;
30845           }
30846 
30847           return {
30848             scheme: parts[1] || '',
30849             netLoc: parts[2] || '',
30850             path: parts[3] || '',
30851             params: parts[4] || '',
30852             query: parts[5] || '',
30853             fragment: parts[6] || ''
30854           };
30855         },
30856         normalizePath: function normalizePath(path) {
30857           // The following operations are
30858           // then applied, in order, to the new path:
30859           // 6a) All occurrences of "./", where "." is a complete path
30860           // segment, are removed.
30861           // 6b) If the path ends with "." as a complete path segment,
30862           // that "." is removed.
30863           path = path.split('').reverse().join('').replace(SLASH_DOT_REGEX, ''); // 6c) All occurrences of "<segment>/../", where <segment> is a
30864           // complete path segment not equal to "..", are removed.
30865           // Removal of these path segments is performed iteratively,
30866           // removing the leftmost matching pattern on each iteration,
30867           // until no matching pattern remains.
30868           // 6d) If the path ends with "<segment>/..", where <segment> is a
30869           // complete path segment not equal to "..", that
30870           // "<segment>/.." is removed.
30871 
30872           while (path.length !== (path = path.replace(SLASH_DOT_DOT_REGEX, '')).length) {} // jshint ignore:line
30873 
30874 
30875           return path.split('').reverse().join('');
30876         },
30877         buildURLFromParts: function buildURLFromParts(parts) {
30878           return parts.scheme + parts.netLoc + parts.path + parts.params + parts.query + parts.fragment;
30879         }
30880       };
30881       /* jshint ignore:start */
30882 
30883       module.exports = URLToolkit;
30884     })();
30885     /* jshint ignore:end */
30886 
30887   });
30888 
30889   var resolveUrl = function resolveUrl(baseUrl, relativeUrl) {
30890     // return early if we don't need to resolve
30891     if (/^[a-z]+:/i.test(relativeUrl)) {
30892       return relativeUrl;
30893     } // if the base URL is relative then combine with the current location
30894 
30895 
30896     if (!/\/\//i.test(baseUrl)) {
30897       baseUrl = urlToolkit$1.buildAbsoluteURL(window$1.location.href, baseUrl);
30898     }
30899 
30900     return urlToolkit$1.buildAbsoluteURL(baseUrl, relativeUrl);
30901   };
30902   /**
30903    * @typedef {Object} SingleUri
30904    * @property {string} uri - relative location of segment
30905    * @property {string} resolvedUri - resolved location of segment
30906    * @property {Object} byterange - Object containing information on how to make byte range
30907    *   requests following byte-range-spec per RFC2616.
30908    * @property {String} byterange.length - length of range request
30909    * @property {String} byterange.offset - byte offset of range request
30910    *
30911    * @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.1
30912    */
30913 
30914   /**
30915    * Converts a URLType node (5.3.9.2.3 Table 13) to a segment object
30916    * that conforms to how m3u8-parser is structured
30917    *
30918    * @see https://github.com/videojs/m3u8-parser
30919    *
30920    * @param {string} baseUrl - baseUrl provided by <BaseUrl>
30920 nodes
30921    * @param {string} source - source url for segment
30922    * @param {string} range - optional range used for range calls,
30923    *   follows  RFC 2616, Clause 14.35.1
30924    * @return {SingleUri} full segment information transformed into a format similar
30925    *   to m3u8-parser
30926    */
30927 
30928 
30929   var urlTypeToSegment = function urlTypeToSegment(_ref) {
30930     var _ref$baseUrl = _ref.baseUrl,
30931         baseUrl = _ref$baseUrl === void 0 ? '' : _ref$baseUrl,
30932         _ref$source = _ref.source,
30933         source = _ref$source === void 0 ? '' : _ref$source,
30934         _ref$range = _ref.range,
30935         range = _ref$range === void 0 ? '' : _ref$range,
30936         _ref$indexRange = _ref.indexRange,
30937         indexRange = _ref$indexRange === void 0 ? '' : _ref$indexRange;
30938     var segment = {
30939       uri: source,
30940       resolvedUri: resolveUrl(baseUrl || '', source)
30941     };
30942 
30943     if (range || indexRange) {
30944       var rangeStr = range ? range : indexRange;
30945       var ranges = rangeStr.split('-');
30946       var startRange = parseInt(ranges[0], 10);
30947       var endRange = parseInt(ranges[1], 10); // byterange should be inclusive according to
30948       // RFC 2616, Clause 14.35.1
30949 
30950       segment.byterange = {
30951         length: endRange - startRange + 1,
30952         offset: startRange
30953       };
30954     }
30955 
30956     return segment;
30957   };
30958 
30959   var byteRangeToString = function byteRangeToString(byterange) {
30960     // `endRange` is one less than `offset + length` because the HTTP range
30961     // header uses inclusive ranges
30962     var endRange = byterange.offset + byterange.length - 1;
30963     return byterange.offset + "-" + endRange;
30964   };
30965   /**
30966    * Functions for calculating the range of available segments in static and dynamic
30967    * manifests.
30968    */
30969 
30970 
30971   var segmentRange = {
30972     /**
30973      * Returns the entire range of available segments for a static MPD
30974      *
30975      * @param {Object} attributes
30976      *        Inheritied MPD attributes
30977      * @return {{ start: number, end: number }}
30978      *         The start and end numbers for available segments
30979      */
30980     "static": function _static(attributes) {
30981       var duration = attributes.duration,
30982           _attributes$timescale = attributes.timescale,
30983           timescale = _attributes$timescale === void 0 ? 1 : _attributes$timescale,
30984           sourceDuration = attributes.sourceDuration;
30985       return {
30986         start: 0,
30987         end: Math.ceil(sourceDuration / (duration / timescale))
30988       };
30989     },
30990 
30991     /**
30992      * Returns the current live window range of available segments for a dynamic MPD
30993      *
30994      * @param {Object} attributes
30995      *        Inheritied MPD attributes
30996      * @return {{ start: number, end: number }}
30997      *         The start and end numbers for available segments
30998      */
30999     dynamic: function dynamic(attributes) {
31000       var NOW = attributes.NOW,
31001           clientOffset = attributes.clientOffset,
31002           availabilityStartTime = attributes.availabilityStartTime,
31003           _attributes$timescale2 = attributes.timescale,
31004           timescale = _attributes$timescale2 === void 0 ? 1 : _attributes$timescale2,
31005           duration = attributes.duration,
31006           _attributes$start = attributes.start,
31007           start = _attributes$start === void 0 ? 0 : _attributes$start,
31008           _attributes$minimumUp = attributes.minimumUpdatePeriod,
31009           minimumUpdatePeriod = _attributes$minimumUp === void 0 ? 0 : _attributes$minimumUp,
31010           _attributes$timeShift = attributes.timeShiftBufferDepth,
31011           timeShiftBufferDepth = _attributes$timeShift === void 0 ? Infinity : _attributes$timeShift;
31012       var now = (NOW + clientOffset) / 1000;
31013       var periodStartWC = availabilityStartTime + start;
31014       var periodEndWC = now + minimumUpdatePeriod;
31015       var periodDuration = periodEndWC - periodStartWC;
31016       var segmentCount = Math.ceil(periodDuration * timescale / duration);
31017       var availableStart = Math.floor((now - periodStartWC - timeShiftBufferDepth) * timescale / duration);
31018       var availableEnd = Math.floor((now - periodStartWC) * timescale / duration);
31019       return {
31020         start: Math.max(0, availableStart),
31021         end: Math.min(segmentCount, availableEnd)
31022       };
31023     }
31024   };
31025   /**
31026    * Maps a range of numbers to objects with information needed to build the corresponding
31027    * segment list
31028    *
31029    * @name toSegmentsCallback
31030    * @function
31031    * @param {number} number
31032    *        Number of the segment
31033    * @param {number} index
31034    *        Index of the number in the range list
31035    * @return {{ number: Number, duration: Number, timeline: Number, time: Number }}
31036    *         Object with segment timing and duration info
31037    */
31038 
31039   /**
31040    * Returns a callback for Array.prototype.map for mapping a range of numbers to
31041    * information needed to build the segment list.
31042    *
31043    * @param {Object} attributes
31044    *        Inherited MPD attributes
31045    * @return {toSegmentsCallback}
31046    *         Callback map function
31047    */
31048 
31049   var toSegments = function toSegments(attributes) {
31050     return function (number, index) {
31051       var duration = attributes.duration,
31052           _attributes$timescale3 = attributes.timescale,
31053           timescale = _attributes$timescale3 === void 0 ? 1 : _attributes$timescale3,
31054           periodIndex = attributes.periodIndex,
31055           _attributes$startNumb = attributes.startNumber,
31056           startNumber = _attributes$startNumb === void 0 ? 1 : _attributes$startNumb;
31057       return {
31058         number: startNumber + number,
31059         duration: duration / timescale,
31060         timeline: periodIndex,
31061         time: index * duration
31062       };
31063     };
31064   };
31065   /**
31066    * Returns a list of objects containing segment timing and duration info used for
31067    * building the list of segments. This uses the @duration attribute specified
31068    * in the MPD manifest to derive the range of segments.
31069    *
31070    * @param {Object} attributes
31071    *        Inherited MPD attributes
31072    * @return {{number: number, duration: number, time: number, timeline: number}[]}
31073    *         List of Objects with segment timing and duration info
31074    */
31075 
31076 
31077   var parseByDuration = function parseByDuration(attributes) {
31078     var _attributes$type = attributes.type,
31079         type = _attributes$type === void 0 ? 'static' : _attributes$type,
31080         duration = attributes.duration,
31081         _attributes$timescale4 = attributes.timescale,
31082         timescale = _attributes$timescale4 === void 0 ? 1 : _attributes$timescale4,
31083         sourceDuration = attributes.sourceDuration;
31084 
31085     var _segmentRange$type = segmentRange[type](attributes),
31086         start = _segmentRange$type.start,
31087         end = _segmentRange$type.end;
31088 
31089     var segments = range(start, end).map(toSegments(attributes));
31090 
31091     if (type === 'static') {
31092       var index = segments.length - 1; // final segment may be less than full segment duration
31093 
31094       segments[index].duration = sourceDuration - duration / timescale * index;
31095     }
31096 
31097     return segments;
31098   };
31099   /**
31100    * Translates SegmentBase into a set of segments.
31101    * (DASH SPEC Section 5.3.9.3.2) contains a set of <SegmentURL> nodes.  Each
31102    * node should be translated into segment.
31103    *
31104    * @param {Object} attributes
31105    *   Object containing all inherited attributes from parent elements with attribute
31106    *   names as keys
31107    * @return {Object.<Array>} list of segments
31108    */
31109 
31110 
31111   var segmentsFromBase = function segmentsFromBase(attributes) {
31112     var baseUrl = attributes.baseUrl,
31113         _attributes$initializ = attributes.initialization,
31114         initialization = _attributes$initializ === void 0 ? {} : _attributes$initializ,
31115         sourceDuration = attributes.sourceDuration,
31116         _attributes$timescale = attributes.timescale,
31117         timescale = _attributes$timescale === void 0 ? 1 : _attributes$timescale,
31118         _attributes$indexRang = attributes.indexRange,
31119         indexRange = _attributes$indexRang === void 0 ? '' : _attributes$indexRang,
31120         duration = attributes.duration; // base url is required for SegmentBase to work, per spec (Section 5.3.9.2.1)
31121 
31122     if (!baseUrl) {
31123       throw new Error(errors.NO_BASE_URL);
31124     }
31125 
31126     var initSegment = urlTypeToSegment({
31127       baseUrl: baseUrl,
31128       source: initialization.sourceURL,
31129       range: initialization.range
31130     });
31131     var segment = urlTypeToSegment({
31132       baseUrl: baseUrl,
31133       source: baseUrl,
31134       indexRange: indexRange
31135     });
31136     segment.map = initSegment; // If there is a duration, use it, otherwise use the given duration of the source
31137     // (since SegmentBase is only for one total segment)
31138 
31139     if (duration) {
31140       var segmentTimeInfo = parseByDuration(attributes);
31141 
31142       if (segmentTimeInfo.length) {
31143         segment.duration = segmentTimeInfo[0].duration;
31144         segment.timeline = segmentTimeInfo[0].timeline;
31145       }
31146     } else if (sourceDuration) {
31147       segment.duration = sourceDuration / timescale;
31148       segment.timeline = 0;
31149     } // This is used for mediaSequence
31150 
31151 
31152     segment.number = 0;
31153     return [segment];
31154   };
31155   /**
31156    * Given a playlist, a sidx box, and a baseUrl, update the segment list of the playlist
31157    * according to the sidx information given.
31158    *
31159    * playlist.sidx has metadadata about the sidx where-as the sidx param
31160    * is the parsed sidx box itself.
31161    *
31162    * @param {Object} playlist the playlist to update the sidx information for
31163    * @param {Object} sidx the parsed sidx box
31164    * @return {Object} the playlist object with the updated sidx information
31165    */
31166 
31167 
31168   var addSegmentsToPlaylist = function addSegmentsToPlaylist(playlist, sidx, baseUrl) {
31169     // Retain init segment information
31170     var initSegment = playlist.sidx.map ? playlist.sidx.map : null; // Retain source duration from initial master manifest parsing
31171 
31172     var sourceDuration = playlist.sidx.duration; // Retain source timeline
31173 
31174     var timeline = playlist.timeline || 0;
31175     var sidxByteRange = playlist.sidx.byterange;
31176     var sidxEnd = sidxByteRange.offset + sidxByteRange.length; // Retain timescale of the parsed sidx
31177 
31178     var timescale = sidx.timescale; // referenceType 1 refers to other sidx boxes
31179 
31180     var mediaReferences = sidx.references.filter(function (r) {
31181       return r.referenceType !== 1;
31182     });
31183     var segments = []; // firstOffset is the offset from the end of the sidx box
31184 
31185     var startIndex = sidxEnd + sidx.firstOffset;
31186 
31187     for (var i = 0; i < mediaReferences.length; i++) {
31188       var reference = sidx.references[i]; // size of the referenced (sub)segment
31189 
31190       var size = reference.referencedSize; // duration of the referenced (sub)segment, in  the  timescale
31191       // this will be converted to seconds when generating segments
31192 
31193       var duration = reference.subsegmentDuration; // should be an inclusive range
31194 
31195       var endIndex = startIndex + size - 1;
31196       var indexRange = startIndex + "-" + endIndex;
31197       var attributes = {
31198         baseUrl: baseUrl,
31199         timescale: timescale,
31200         timeline: timeline,
31201         // this is used in parseByDuration
31202         periodIndex: timeline,
31203         duration: duration,
31204         sourceDuration: sourceDuration,
31205         indexRange: indexRange
31206       };
31207       var segment = segmentsFromBase(attributes)[0];
31208 
31209       if (initSegment) {
31210         segment.map = initSegment;
31211       }
31212 
31213       segments.push(segment);
31214       startIndex += size;
31215     }
31216 
31217     playlist.segments = segments;
31218     return playlist;
31219   };
31220 
31221   var mergeDiscontiguousPlaylists = function mergeDiscontiguousPlaylists(playlists) {
31222     var mergedPlaylists = values(playlists.reduce(function (acc, playlist) {
31223       // assuming playlist IDs are the same across periods
31224       // TODO: handle multiperiod where representation sets are not the same
31225       // across periods
31226       var name = playlist.attributes.id + (playlist.attributes.lang || ''); // Periods after first
31227 
31228       if (acc[name]) {
31229         var _acc$name$segments; // first segment of subsequent periods signal a discontinuity
31230 
31231 
31232         if (playlist.segments[0]) {
31233           playlist.segments[0].discontinuity = true;
31234         }
31235 
31236         (_acc$name$segments = acc[name].segments).push.apply(_acc$name$segments, playlist.segments); // bubble up contentProtection, this assumes all DRM content
31237         // has the same contentProtection
31238 
31239 
31240         if (playlist.attributes.contentProtection) {
31241           acc[name].attributes.contentProtection = playlist.attributes.contentProtection;
31242         }
31243       } else {
31244         // first Period
31245         acc[name] = playlist;
31246       }
31247 
31248       return acc;
31249     }, {}));
31250     return mergedPlaylists.map(function (playlist) {
31251       playlist.discontinuityStarts = findIndexes(playlist.segments, 'discontinuity');
31252       return playlist;
31253     });
31254   };
31255 
31256   var addSegmentInfoFromSidx = function addSegmentInfoFromSidx(playlists, sidxMapping) {
31257     if (sidxMapping === void 0) {
31258       sidxMapping = {};
31259     }
31260 
31261     if (!Object.keys(sidxMapping).length) {
31262       return playlists;
31263     }
31264 
31265     for (var i in playlists) {
31266       var playlist = playlists[i];
31267 
31268       if (!playlist.sidx) {
31269         continue;
31270       }
31271 
31272       var sidxKey = playlist.sidx.uri + '-' + byteRangeToString(playlist.sidx.byterange);
31273       var sidxMatch = sidxMapping[sidxKey] && sidxMapping[sidxKey].sidx;
31274 
31275       if (playlist.sidx && sidxMatch) {
31276         addSegmentsToPlaylist(playlist, sidxMatch, playlist.sidx.resolvedUri);
31277       }
31278     }
31279 
31280     return playlists;
31281   };
31282 
31283   var formatAudioPlaylist = function formatAudioPlaylist(_ref) {
31284     var _attributes;
31285 
31286     var attributes = _ref.attributes,
31287         segments = _ref.segments,
31288         sidx = _ref.sidx;
31289     var playlist = {
31290       attributes: (_attributes = {
31291         NAME: attributes.id,
31292         BANDWIDTH: attributes.bandwidth,
31293         CODECS: attributes.codecs
31294       }, _attributes['PROGRAM-ID'] = 1, _attributes),
31295       uri: '',
31296       endList: (attributes.type || 'static') === 'static',
31297       timeline: attributes.periodIndex,
31298       resolvedUri: '',
31299       targetDuration: attributes.duration,
31300       segments: segments,
31301       mediaSequence: segments.length ? segments[0].number : 1
31302     };
31303 
31304     if (attributes.contentProtection) {
31305       playlist.contentProtection = attributes.contentProtection;
31306     }
31307 
31308     if (sidx) {
31309       playlist.sidx = sidx;
31310     }
31311 
31312     return playlist;
31313   };
31314 
31315   var formatVttPlaylist = function formatVttPlaylist(_ref2) {
31316     var _attributes2;
31317 
31318     var attributes = _ref2.attributes,
31319         segments = _ref2.segments;
31320 
31321     if (typeof segments === 'undefined') {
31322       // vtt tracks may use single file in BaseURL
31323       segments = [{
31324         uri: attributes.baseUrl,
31325         timeline: attributes.periodIndex,
31326         resolvedUri: attributes.baseUrl || '',
31327         duration: attributes.sourceDuration,
31328         number: 0
31329       }]; // targetDuration should be the same duration as the only segment
31330 
31331       attributes.duration = attributes.sourceDuration;
31332     }
31333 
31334     return {
31335       attributes: (_attributes2 = {
31336         NAME: attributes.id,
31337         BANDWIDTH: attributes.bandwidth
31338       }, _attributes2['PROGRAM-ID'] = 1, _attributes2),
31339       uri: '',
31340       endList: (attributes.type || 'static') === 'static',
31341       timeline: attributes.periodIndex,
31342       resolvedUri: attributes.baseUrl || '',
31343       targetDuration: attributes.duration,
31344       segments: segments,
31345       mediaSequence: segments.length ? segments[0].number : 1
31346     };
31347   };
31348 
31349   var organizeAudioPlaylists = function organizeAudioPlaylists(playlists, sidxMapping) {
31350     if (sidxMapping === void 0) {
31351       sidxMapping = {};
31352     }
31353 
31354     var mainPlaylist;
31355     var formattedPlaylists = playlists.reduce(function (a, playlist) {
31356       var role = playlist.attributes.role && playlist.attributes.role.value || '';
31357       var language = playlist.attributes.lang || '';
31358       var label = 'main';
31359 
31360       if (language) {
31361         var roleLabel = role ? " (" + role + ")" : '';
31362         label = "" + playlist.attributes.lang + roleLabel;
31363       } // skip if we already have the highest quality audio for a language
31364 
31365 
31366       if (a[label] && a[label].playlists[0].attributes.BANDWIDTH > playlist.attributes.bandwidth) {
31367         return a;
31368       }
31369 
31370       a[label] = {
31371         language: language,
31372         autoselect: true,
31373         "default": role === 'main',
31374         playlists: addSegmentInfoFromSidx([formatAudioPlaylist(playlist)], sidxMapping),
31375         uri: ''
31376       };
31377 
31378       if (typeof mainPlaylist === 'undefined' && role === 'main') {
31379         mainPlaylist = playlist;
31380         mainPlaylist["default"] = true;
31381       }
31382 
31383       return a;
31384     }, {}); // if no playlists have role "main", mark the first as main
31385 
31386     if (!mainPlaylist) {
31387       var firstLabel = Object.keys(formattedPlaylists)[0];
31388       formattedPlaylists[firstLabel]["default"] = true;
31389     }
31390 
31391     return formattedPlaylists;
31392   };
31393 
31394   var organizeVttPlaylists = function organizeVttPlaylists(playlists, sidxMapping) {
31395     if (sidxMapping === void 0) {
31396       sidxMapping = {};
31397     }
31398 
31399     return playlists.reduce(function (a, playlist) {
31400       var label = playlist.attributes.lang || 'text'; // skip if we already have subtitles
31401 
31402       if (a[label]) {
31403         return a;
31404       }
31405 
31406       a[label] = {
31407         language: label,
31408         "default": false,
31409         autoselect: false,
31410         playlists: addSegmentInfoFromSidx([formatVttPlaylist(playlist)], sidxMapping),
31411         uri: ''
31412       };
31413       return a;
31414     }, {});
31415   };
31416 
31417   var formatVideoPlaylist = function formatVideoPlaylist(_ref3) {
31418     var _attributes3;
31419 
31420     var attributes = _ref3.attributes,
31421         segments = _ref3.segments,
31422         sidx = _ref3.sidx;
31423     var playlist = {
31424       attributes: (_attributes3 = {
31425         NAME: attributes.id,
31426         AUDIO: 'audio',
31427         SUBTITLES: 'subs',
31428         RESOLUTION: {
31429           width: attributes.width,
31430           height: attributes.height
31431         },
31432         CODECS: attributes.codecs,
31433         BANDWIDTH: attributes.bandwidth
31434       }, _attributes3['PROGRAM-ID'] = 1, _attributes3),
31435       uri: '',
31436       endList: (attributes.type || 'static') === 'static',
31437       timeline: attributes.periodIndex,
31438       resolvedUri: '',
31439       targetDuration: attributes.duration,
31440       segments: segments,
31441       mediaSequence: segments.length ? segments[0].number : 1
31442     };
31443 
31444     if (attributes.contentProtection) {
31445       playlist.contentProtection = attributes.contentProtection;
31446     }
31447 
31448     if (sidx) {
31449       playlist.sidx = sidx;
31450     }
31451 
31452     return playlist;
31453   };
31454 
31455   var toM3u8 = function toM3u8(dashPlaylists, sidxMapping) {
31456     var _mediaGroups;
31457 
31458     if (sidxMapping === void 0) {
31459       sidxMapping = {};
31460     }
31461 
31462     if (!dashPlaylists.length) {
31463       return {};
31464     } // grab all master attributes
31465 
31466 
31467     var _dashPlaylists$0$attr = dashPlaylists[0].attributes,
31468         duration = _dashPlaylists$0$attr.sourceDuration,
31469         _dashPlaylists$0$attr2 = _dashPlaylists$0$attr.minimumUpdatePeriod,
31470         minimumUpdatePeriod = _dashPlaylists$0$attr2 === void 0 ? 0 : _dashPlaylists$0$attr2;
31471 
31472     var videoOnly = function videoOnly(_ref4) {
31473       var attributes = _ref4.attributes;
31474       return attributes.mimeType === 'video/mp4' || attributes.contentType === 'video';
31475     };
31476 
31477     var audioOnly = function audioOnly(_ref5) {
31478       var attributes = _ref5.attributes;
31479       return attributes.mimeType === 'audio/mp4' || attributes.contentType === 'audio';
31480     };
31481 
31482     var vttOnly = function vttOnly(_ref6) {
31483       var attributes = _ref6.attributes;
31484       return attributes.mimeType === 'text/vtt' || attributes.contentType === 'text';
31485     };
31486 
31487     var videoPlaylists = mergeDiscontiguousPlaylists(dashPlaylists.filter(videoOnly)).map(formatVideoPlaylist);
31488     var audioPlaylists = mergeDiscontiguousPlaylists(dashPlaylists.filter(audioOnly));
31489     var vttPlaylists = dashPlaylists.filter(vttOnly);
31490     var master = {
31491       allowCache: true,
31492       discontinuityStarts: [],
31493       segments: [],
31494       endList: true,
31495       mediaGroups: (_mediaGroups = {
31496         AUDIO: {},
31497         VIDEO: {}
31498       }, _mediaGroups['CLOSED-CAPTIONS'] = {}, _mediaGroups.SUBTITLES = {}, _mediaGroups),
31499       uri: '',
31500       duration: duration,
31501       playlists: addSegmentInfoFromSidx(videoPlaylists, sidxMapping),
31502       minimumUpdatePeriod: minimumUpdatePeriod * 1000
31503     };
31504 
31505     if (audioPlaylists.length) {
31506       master.mediaGroups.AUDIO.audio = organizeAudioPlaylists(audioPlaylists, sidxMapping);
31507     }
31508 
31509     if (vttPlaylists.length) {
31510       master.mediaGroups.SUBTITLES.subs = organizeVttPlaylists(vttPlaylists, sidxMapping);
31511     }
31512 
31513     return master;
31514   };
31515   /**
31516    * Calculates the R (repetition) value for a live stream (for the final segment
31517    * in a manifest where the r value is negative 1)
31518    *
31519    * @param {Object} attributes
31520    *        Object containing all inherited attributes from parent elements with attribute
31521    *        names as keys
31522    * @param {number} time
31523    *        current time (typically the total time up until the final segment)
31524    * @param {number} duration
31525    *        duration property for the given <S />
31526    *
31527    * @return {number}
31528    *        R value to reach the end of the given period
31529    */
31530 
31531 
31532   var getLiveRValue = function getLiveRValue(attributes, time, duration) {
31533     var NOW = attributes.NOW,
31534         clientOffset = attributes.clientOffset,
31535         availabilityStartTime = attributes.availabilityStartTime,
31536         _attributes$timescale = attributes.timescale,
31537         timescale = _attributes$timescale === void 0 ? 1 : _attributes$timescale,
31538         _attributes$start = attributes.start,
31539         start = _attributes$start === void 0 ? 0 : _attributes$start,
31540 
vendor: 11,178 bytes, lines 31540-31837
31540        _attributes$minimumUp = attributes.minimumUpdatePeriod,
31541         minimumUpdatePeriod = _attributes$minimumUp === void 0 ? 0 : _attributes$minimumUp;
31542     var now = (NOW + clientOffset) / 1000;
31543     var periodStartWC = availabilityStartTime + start;
31544     var periodEndWC = now + minimumUpdatePeriod;
31545     var periodDuration = periodEndWC - periodStartWC;
31546     return Math.ceil((periodDuration * timescale - time) / duration);
31547   };
31548   /**
31549    * Uses information provided by SegmentTemplate.SegmentTimeline to determine segment
31550    * timing and duration
31551    *
31552    * @param {Object} attributes
31553    *        Object containing all inherited attributes from parent elements with attribute
31554    *        names as keys
31555    * @param {Object[]} segmentTimeline
31556    *        List of objects representing the attributes of each S element contained within
31557    *
31558    * @return {{number: number, duration: number, time: number, timeline: number}[]}
31559    *         List of Objects with segment timing and duration info
31560    */
31561 
31562 
31563   var parseByTimeline = function parseByTimeline(attributes, segmentTimeline) {
31564     var _attributes$type = attributes.type,
31565         type = _attributes$type === void 0 ? 'static' : _attributes$type,
31566         _attributes$minimumUp2 = attributes.minimumUpdatePeriod,
31567         minimumUpdatePeriod = _attributes$minimumUp2 === void 0 ? 0 : _attributes$minimumUp2,
31568         _attributes$media = attributes.media,
31569         media = _attributes$media === void 0 ? '' : _attributes$media,
31570         sourceDuration = attributes.sourceDuration,
31571         _attributes$timescale2 = attributes.timescale,
31572         timescale = _attributes$timescale2 === void 0 ? 1 : _attributes$timescale2,
31573         _attributes$startNumb = attributes.startNumber,
31574         startNumber = _attributes$startNumb === void 0 ? 1 : _attributes$startNumb,
31575         timeline = attributes.periodIndex;
31576     var segments = [];
31577     var time = -1;
31578 
31579     for (var sIndex = 0; sIndex < segmentTimeline.length; sIndex++) {
31580       var S = segmentTimeline[sIndex];
31581       var duration = S.d;
31582       var repeat = S.r || 0;
31583       var segmentTime = S.t || 0;
31584 
31585       if (time < 0) {
31586         // first segment
31587         time = segmentTime;
31588       }
31589 
31590       if (segmentTime && segmentTime > time) {
31591         // discontinuity
31592         // TODO: How to handle this type of discontinuity
31593         // timeline++ here would treat it like HLS discontuity and content would
31594         // get appended without gap
31595         // E.G.
31596         //  <S t="0" d="1" />
31597         //  <S d="1" />
31598         //  <S d="1" />
31599         //  <S t="5" d="1" />
31600         // would have $Time$ values of [0, 1, 2, 5]
31601         // should this be appened at time positions [0, 1, 2, 3],(#EXT-X-DISCONTINUITY)
31602         // or [0, 1, 2, gap, gap, 5]? (#EXT-X-GAP)
31603         // does the value of sourceDuration consider this when calculating arbitrary
31604         // negative @r repeat value?
31605         // E.G. Same elements as above with this added at the end
31606         //  <S d="1" r="-1" />
31607         //  with a sourceDuration of 10
31608         // Would the 2 gaps be included in the time duration calculations resulting in
31609         // 8 segments with $Time$ values of [0, 1, 2, 5, 6, 7, 8, 9] or 10 segments
31610         // with $Time$ values of [0, 1, 2, 5, 6, 7, 8, 9, 10, 11] ?
31611         time = segmentTime;
31612       }
31613 
31614       var count = void 0;
31615 
31616       if (repeat < 0) {
31617         var nextS = sIndex + 1;
31618 
31619         if (nextS === segmentTimeline.length) {
31620           // last segment
31621           if (type === 'dynamic' && minimumUpdatePeriod > 0 && media.indexOf('$Number$') > 0) {
31622             count = getLiveRValue(attributes, time, duration);
31623           } else {
31624             // TODO: This may be incorrect depending on conclusion of TODO above
31625             count = (sourceDuration * timescale - time) / duration;
31626           }
31627         } else {
31628           count = (segmentTimeline[nextS].t - time) / duration;
31629         }
31630       } else {
31631         count = repeat + 1;
31632       }
31633 
31634       var end = startNumber + segments.length + count;
31635       var number = startNumber + segments.length;
31636 
31637       while (number < end) {
31638         segments.push({
31639           number: number,
31640           duration: duration / timescale,
31641           time: time,
31642           timeline: timeline
31643         });
31644         time += duration;
31645         number++;
31646       }
31647     }
31648 
31649     return segments;
31650   };
31651 
31652   var identifierPattern = /\$([A-z]*)(?:(%0)([0-9]+)d)?\$/g;
31653   /**
31654    * Replaces template identifiers with corresponding values. To be used as the callback
31655    * for String.prototype.replace
31656    *
31657    * @name replaceCallback
31658    * @function
31659    * @param {string} match
31660    *        Entire match of identifier
31661    * @param {string} identifier
31662    *        Name of matched identifier
31663    * @param {string} format
31664    *        Format tag string. Its presence indicates that padding is expected
31665    * @param {string} width
31666    *        Desired length of the replaced value. Values less than this width shall be left
31667    *        zero padded
31668    * @return {string}
31669    *         Replacement for the matched identifier
31670    */
31671 
31672   /**
31673    * Returns a function to be used as a callback for String.prototype.replace to replace
31674    * template identifiers
31675    *
31676    * @param {Obect} values
31677    *        Object containing values that shall be used to replace known identifiers
31678    * @param {number} values.RepresentationID
31679    *        Value of the Representation@id attribute
31680    * @param {number} values.Number
31681    *        Number of the corresponding segment
31682    * @param {number} values.Bandwidth
31683    *        Value of the Representation@bandwidth attribute.
31684    * @param {number} values.Time
31685    *        Timestamp value of the corresponding segment
31686    * @return {replaceCallback}
31687    *         Callback to be used with String.prototype.replace to replace identifiers
31688    */
31689 
31690   var identifierReplacement = function identifierReplacement(values) {
31691     return function (match, identifier, format, width) {
31692       if (match === '$$') {
31693         // escape sequence
31694         return '$';
31695       }
31696 
31697       if (typeof values[identifier] === 'undefined') {
31698         return match;
31699       }
31700 
31701       var value = '' + values[identifier];
31702 
31703       if (identifier === 'RepresentationID') {
31704         // Format tag shall not be present with RepresentationID
31705         return value;
31706       }
31707 
31708       if (!format) {
31709         width = 1;
31710       } else {
31711         width = parseInt(width, 10);
31712       }
31713 
31714       if (value.length >= width) {
31715         return value;
31716       }
31717 
31718       return "" + new Array(width - value.length + 1).join('0') + value;
31719     };
31720   };
31721   /**
31722    * Constructs a segment url from a template string
31723    *
31724    * @param {string} url
31725    *        Template string to construct url from
31726    * @param {Obect} values
31727    *        Object containing values that shall be used to replace known identifiers
31728    * @param {number} values.RepresentationID
31729    *        Value of the Representation@id attribute
31730    * @param {number} values.Number
31731    *        Number of the corresponding segment
31732    * @param {number} values.Bandwidth
31733    *        Value of the Representation@bandwidth attribute.
31734    * @param {number} values.Time
31735    *        Timestamp value of the corresponding segment
31736    * @return {string}
31737    *         Segment url with identifiers replaced
31738    */
31739 
31740 
31741   var constructTemplateUrl = function constructTemplateUrl(url, values) {
31742     return url.replace(identifierPattern, identifierReplacement(values));
31743   };
31744   /**
31745    * Generates a list of objects containing timing and duration information about each
31746    * segment needed to generate segment uris and the complete segment object
31747    *
31748    * @param {Object} attributes
31749    *        Object containing all inherited attributes from parent elements with attribute
31750    *        names as keys
31751    * @param {Object[]|undefined} segmentTimeline
31752    *        List of objects representing the attributes of each S element contained within
31753    *        the SegmentTimeline element
31754    * @return {{number: number, duration: number, time: number, timeline: number}[]}
31755    *         List of Objects with segment timing and duration info
31756    */
31757 
31758 
31759   var parseTemplateInfo = function parseTemplateInfo(attributes, segmentTimeline) {
31760     if (!attributes.duration && !segmentTimeline) {
31761       // if neither @duration or SegmentTimeline are present, then there shall be exactly
31762       // one media segment
31763       return [{
31764         number: attributes.startNumber || 1,
31765         duration: attributes.sourceDuration,
31766         time: 0,
31767         timeline: attributes.periodIndex
31768       }];
31769     }
31770 
31771     if (attributes.duration) {
31772       return parseByDuration(attributes);
31773     }
31774 
31775     return parseByTimeline(attributes, segmentTimeline);
31776   };
31777   /**
31778    * Generates a list of segments using information provided by the SegmentTemplate element
31779    *
31780    * @param {Object} attributes
31781    *        Object containing all inherited attributes from parent elements with attribute
31782    *        names as keys
31783    * @param {Object[]|undefined} segmentTimeline
31784    *        List of objects representing the attributes of each S element contained within
31785    *        the SegmentTimeline element
31786    * @return {Object[]}
31787    *         List of segment objects
31788    */
31789 
31790 
31791   var segmentsFromTemplate = function segmentsFromTemplate(attributes, segmentTimeline) {
31792     var templateValues = {
31793       RepresentationID: attributes.id,
31794       Bandwidth: attributes.bandwidth || 0
31795     };
31796     var _attributes$initializ = attributes.initialization,
31797         initialization = _attributes$initializ === void 0 ? {
31798       sourceURL: '',
31799       range: ''
31800     } : _attributes$initializ;
31801     var mapSegment = urlTypeToSegment({
31802       baseUrl: attributes.baseUrl,
31803       source: constructTemplateUrl(initialization.sourceURL, templateValues),
31804       range: initialization.range
31805     });
31806     var segments = parseTemplateInfo(attributes, segmentTimeline);
31807     return segments.map(function (segment) {
31808       templateValues.Number = segment.number;
31809       templateValues.Time = segment.time;
31810       var uri = constructTemplateUrl(attributes.media || '', templateValues);
31811       return {
31812         uri: uri,
31813         timeline: segment.timeline,
31814         duration: segment.duration,
31815         resolvedUri: resolveUrl(attributes.baseUrl || '', uri),
31816         map: mapSegment,
31817         number: segment.number
31818       };
31819     });
31820   };
31821   /**
31822    * Converts a <SegmentUrl> (of type URLType from the DASH spec 5.3.9.2 Table 14)
31823    * to an object that matches the output of a segment in videojs/mpd-parser
31824    *
31825    * @param {Object} attributes
31826    *   Object containing all inherited attributes from parent elements with attribute
31827    *   names as keys
31828    * @param {Object} segmentUrl
31829    *   <SegmentURL> node to translate into a segment object
31830    * @return {Object} translated segment object
31831    */
31832 
31833 
31834   var SegmentURLToSegmentObject = function SegmentURLToSegmentObject(attributes, segmentUrl) {
31835     var baseUrl = attributes.baseUrl,
31836         _attributes$initializ = attributes.initialization,
31837         initialization = _attributes$initializ === void 0 ? {}
31837 : _attributes$initializ;
31838     var initSegment = urlTypeToSegment({
31839       baseUrl: baseUrl,
31840       source: initialization.sourceURL,
31841       range: initialization.range
31842     });
31843     var segment = urlTypeToSegment({
31844       baseUrl: baseUrl,
31845       source: segmentUrl.media,
31846       range: segmentUrl.mediaRange
31847     });
31848     segment.map = initSegment;
31849     return segment;
31850   };
31851   /**
31852    * Generates a list of segments using information provided by the SegmentList element
31853    * SegmentList (DASH SPEC Section 5.3.9.3.2) contains a set of <SegmentURL> nodes.  Each
31854    * node should be translated into segment.
31855    *
31856    * @param {Object} attributes
31857    *   Object containing all inherited attributes from parent elements with attribute
31858    *   names as keys
31859    * @param {Object[]|undefined} segmentTimeline
31860    *        List of objects representing the attributes of each S element contained within
31861    *        the SegmentTimeline element
31862    * @return {Object.<Array>} list of segments
31863    */
31864 
31865 
31866   var segmentsFromList = function segmentsFromList(
vendor: 4,561 bytes, lines 31866-31992
31866attributes, segmentTimeline) {
31867     var duration = attributes.duration,
31868         _attributes$segmentUr = attributes.segmentUrls,
31869         segmentUrls = _attributes$segmentUr === void 0 ? [] : _attributes$segmentUr; // Per spec (5.3.9.2.1) no way to determine segment duration OR
31870     // if both SegmentTimeline and @duration are defined, it is outside of spec.
31871 
31872     if (!duration && !segmentTimeline || duration && segmentTimeline) {
31873       throw new Error(errors.SEGMENT_TIME_UNSPECIFIED);
31874     }
31875 
31876     var segmentUrlMap = segmentUrls.map(function (segmentUrlObject) {
31877       return SegmentURLToSegmentObject(attributes, segmentUrlObject);
31878     });
31879     var segmentTimeInfo;
31880 
31881     if (duration) {
31882       segmentTimeInfo = parseByDuration(attributes);
31883     }
31884 
31885     if (segmentTimeline) {
31886       segmentTimeInfo = parseByTimeline(attributes, segmentTimeline);
31887     }
31888 
31889     var segments = segmentTimeInfo.map(function (segmentTime, index) {
31890       if (segmentUrlMap[index]) {
31891         var segment = segmentUrlMap[index];
31892         segment.timeline = segmentTime.timeline;
31893         segment.duration = segmentTime.duration;
31894         segment.number = segmentTime.number;
31895         return segment;
31896       } // Since we're mapping we should get rid of any blank segments (in case
31897       // the given SegmentTimeline is handling for more elements than we have
31898       // SegmentURLs for).
31899 
31900     }).filter(function (segment) {
31901       return segment;
31902     });
31903     return segments;
31904   };
31905 
31906   var generateSegments = function generateSegments(_ref) {
31907     var attributes = _ref.attributes,
31908         segmentInfo = _ref.segmentInfo;
31909     var segmentAttributes;
31910     var segmentsFn;
31911 
31912     if (segmentInfo.template) {
31913       segmentsFn = segmentsFromTemplate;
31914       segmentAttributes = merge(attributes, segmentInfo.template);
31915     } else if (segmentInfo.base) {
31916       segmentsFn = segmentsFromBase;
31917       segmentAttributes = merge(attributes, segmentInfo.base);
31918     } else if (segmentInfo.list) {
31919       segmentsFn = segmentsFromList;
31920       segmentAttributes = merge(attributes, segmentInfo.list);
31921     }
31922 
31923     var segmentsInfo = {
31924       attributes: attributes
31925     };
31926 
31927     if (!segmentsFn) {
31928       return segmentsInfo;
31929     }
31930 
31931     var segments = segmentsFn(segmentAttributes, segmentInfo.timeline); // The @duration attribute will be used to determin the playlist's targetDuration which
31932     // must be in seconds. Since we've generated the segment list, we no longer need
31933     // @duration to be in @timescale units, so we can convert it here.
31934 
31935     if (segmentAttributes.duration) {
31936       var _segmentAttributes = segmentAttributes,
31937           duration = _segmentAttributes.duration,
31938           _segmentAttributes$ti = _segmentAttributes.timescale,
31939           timescale = _segmentAttributes$ti === void 0 ? 1 : _segmentAttributes$ti;
31940       segmentAttributes.duration = duration / timescale;
31941     } else if (segments.length) {
31942       // if there is no @duration attribute, use the largest segment duration as
31943       // as target duration
31944       segmentAttributes.duration = segments.reduce(function (max, segment) {
31945         return Math.max(max, Math.ceil(segment.duration));
31946       }, 0);
31947     } else {
31948       segmentAttributes.duration = 0;
31949     }
31950 
31951     segmentsInfo.attributes = segmentAttributes;
31952     segmentsInfo.segments = segments; // This is a sidx box without actual segment information
31953 
31954     if (segmentInfo.base && segmentAttributes.indexRange) {
31955       segmentsInfo.sidx = segments[0];
31956       segmentsInfo.segments = [];
31957     }
31958 
31959     return segmentsInfo;
31960   };
31961 
31962   var toPlaylists = function toPlaylists(representations) {
31963     return representations.map(generateSegments);
31964   };
31965 
31966   var findChildren = function findChildren(element, name) {
31967     return from(element.childNodes).filter(function (_ref) {
31968       var tagName = _ref.tagName;
31969       return tagName === name;
31970     });
31971   };
31972 
31973   var getContent = function getContent(element) {
31974     return element.textContent.trim();
31975   };
31976 
31977   var parseDuration = function parseDuration(str) {
31978     var SECONDS_IN_YEAR = 365 * 24 * 60 * 60;
31979     var SECONDS_IN_MONTH = 30 * 24 * 60 * 60;
31980     var SECONDS_IN_DAY = 24 * 60 * 60;
31981     var SECONDS_IN_HOUR = 60 * 60;
31982     var SECONDS_IN_MIN = 60; // P10Y10M10DT10H10M10.1S
31983 
31984     var durationRegex = /P(?:(\d*)Y)?(?:(\d*)M)?(?:(\d*)D)?(?:T(?:(\d*)H)?(?:(\d*)M)?(?:([\d.]*)S)?)?/;
31985     var match = durationRegex.exec(str);
31986 
31987     if (!match) {
31988       return 0;
31989     }
31990 
31991     var _match$slice = match.slice(1),
31992         year = _match$slice[0],
vendor: 12,672 bytes, lines 31993-32362
31993         month = _match$slice[1],
31994         day = _match$slice[2],
31995         hour = _match$slice[3],
31996         minute = _match$slice[4],
31997         second = _match$slice[5];
31998 
31999     return parseFloat(year || 0) * SECONDS_IN_YEAR + parseFloat(month || 0) * SECONDS_IN_MONTH + parseFloat(day || 0) * SECONDS_IN_DAY + parseFloat(hour || 0) * SECONDS_IN_HOUR + parseFloat(minute || 0) * SECONDS_IN_MIN + parseFloat(second || 0);
32000   };
32001 
32002   var parseDate = function parseDate(str) {
32003     // Date format without timezone according to ISO 8601
32004     // YYY-MM-DDThh:mm:ss.ssssss
32005     var dateRegex = /^\d+-\d+-\d+T\d+:\d+:\d+(\.\d+)?$/; // If the date string does not specifiy a timezone, we must specifiy UTC. This is
32006     // expressed by ending with 'Z'
32007 
32008     if (dateRegex.test(str)) {
32009       str += 'Z';
32010     }
32011 
32012     return Date.parse(str);
32013   };
32014 
32015   var parsers = {
32016     /**
32017      * Specifies the duration of the entire Media Presentation. Format is a duration string
32018      * as specified in ISO 8601
32019      *
32020      * @param {string} value
32021      *        value of attribute as a string
32022      * @return {number}
32023      *         The duration in seconds
32024      */
32025     mediaPresentationDuration: function mediaPresentationDuration(value) {
32026       return parseDuration(value);
32027     },
32028 
32029     /**
32030      * Specifies the Segment availability start time for all Segments referred to in this
32031      * MPD. For a dynamic manifest, it specifies the anchor for the earliest availability
32032      * time. Format is a date string as specified in ISO 8601
32033      *
32034      * @param {string} value
32035      *        value of attribute as a string
32036      * @return {number}
32037      *         The date as seconds from unix epoch
32038      */
32039     availabilityStartTime: function availabilityStartTime(value) {
32040       return parseDate(value) / 1000;
32041     },
32042 
32043     /**
32044      * Specifies the smallest period between potential changes to the MPD. Format is a
32045      * duration string as specified in ISO 8601
32046      *
32047      * @param {string} value
32048      *        value of attribute as a string
32049      * @return {number}
32050      *         The duration in seconds
32051      */
32052     minimumUpdatePeriod: function minimumUpdatePeriod(value) {
32053       return parseDuration(value);
32054     },
32055 
32056     /**
32057      * Specifies the duration of the smallest time shifting buffer for any Representation
32058      * in the MPD. Format is a duration string as specified in ISO 8601
32059      *
32060      * @param {string} value
32061      *        value of attribute as a string
32062      * @return {number}
32063      *         The duration in seconds
32064      */
32065     timeShiftBufferDepth: function timeShiftBufferDepth(value) {
32066       return parseDuration(value);
32067     },
32068 
32069     /**
32070      * Specifies the PeriodStart time of the Period relative to the availabilityStarttime.
32071      * Format is a duration string as specified in ISO 8601
32072      *
32073      * @param {string} value
32074      *        value of attribute as a string
32075      * @return {number}
32076      *         The duration in seconds
32077      */
32078     start: function start(value) {
32079       return parseDuration(value);
32080     },
32081 
32082     /**
32083      * Specifies the width of the visual presentation
32084      *
32085      * @param {string} value
32086      *        value of attribute as a string
32087      * @return {number}
32088      *         The parsed width
32089      */
32090     width: function width(value) {
32091       return parseInt(value, 10);
32092     },
32093 
32094     /**
32095      * Specifies the height of the visual presentation
32096      *
32097      * @param {string} value
32098      *        value of attribute as a string
32099      * @return {number}
32100      *         The parsed height
32101      */
32102     height: function height(value) {
32103       return parseInt(value, 10);
32104     },
32105 
32106     /**
32107      * Specifies the bitrate of the representation
32108      *
32109      * @param {string} value
32110      *        value of attribute as a string
32111      * @return {number}
32112      *         The parsed bandwidth
32113      */
32114     bandwidth: function bandwidth(value) {
32115       return parseInt(value, 10);
32116     },
32117 
32118     /**
32119      * Specifies the number of the first Media Segment in this Representation in the Period
32120      *
32121      * @param {string} value
32122      *        value of attribute as a string
32123      * @return {number}
32124      *         The parsed number
32125      */
32126     startNumber: function startNumber(value) {
32127       return parseInt(value, 10);
32128     },
32129 
32130     /**
32131      * Specifies the timescale in units per seconds
32132      *
32133      * @param {string} value
32134      *        value of attribute as a string
32135      * @return {number}
32136      *         The aprsed timescale
32137      */
32138     timescale: function timescale(value) {
32139       return parseInt(value, 10);
32140     },
32141 
32142     /**
32143      * Specifies the constant approximate Segment duration
32144      * NOTE: The <Period> element also contains an @duration attribute. This duration
32145      *       specifies the duration of the Period. This attribute is currently not
32146      *       supported by the rest of the parser, however we still check for it to prevent
32147      *       errors.
32148      *
32149      * @param {string} value
32150      *        value of attribute as a string
32151      * @return {number}
32152      *         The parsed duration
32153      */
32154     duration: function duration(value) {
32155       var parsedValue = parseInt(value, 10);
32156 
32157       if (isNaN(parsedValue)) {
32158         return parseDuration(value);
32159       }
32160 
32161       return parsedValue;
32162     },
32163 
32164     /**
32165      * Specifies the Segment duration, in units of the value of the @timescale.
32166      *
32167      * @param {string} value
32168      *        value of attribute as a string
32169      * @return {number}
32170      *         The parsed duration
32171      */
32172     d: function d(value) {
32173       return parseInt(value, 10);
32174     },
32175 
32176     /**
32177      * Specifies the MPD start time, in @timescale units, the first Segment in the series
32178      * starts relative to the beginning of the Period
32179      *
32180      * @param {string} value
32181      *        value of attribute as a string
32182      * @return {number}
32183      *         The parsed time
32184      */
32185     t: function t(value) {
32186       return parseInt(value, 10);
32187     },
32188 
32189     /**
32190      * Specifies the repeat count of the number of following contiguous Segments with the
32191      * same duration expressed by the value of @d
32192      *
32193      * @param {string} value
32194      *        value of attribute as a string
32195      * @return {number}
32196      *         The parsed number
32197      */
32198     r: function r(value) {
32199       return parseInt(value, 10);
32200     },
32201 
32202     /**
32203      * Default parser for all other attributes. Acts as a no-op and just returns the value
32204      * as a string
32205      *
32206      * @param {string} value
32207      *        value of attribute as a string
32208      * @return {string}
32209      *         Unparsed value
32210      */
32211     DEFAULT: function DEFAULT(value) {
32212       return value;
32213     }
32214   };
32215   /**
32216    * Gets all the attributes and values of the provided node, parses attributes with known
32217    * types, and returns an object with attribute names mapped to values.
32218    *
32219    * @param {Node} el
32220    *        The node to parse attributes from
32221    * @return {Object}
32222    *         Object with all attributes of el parsed
32223    */
32224 
32225   var parseAttributes$1 = function parseAttributes(el) {
32226     if (!(el && el.attributes)) {
32227       return {};
32228     }
32229 
32230     return from(el.attributes).reduce(function (a, e) {
32231       var parseFn = parsers[e.name] || parsers.DEFAULT;
32232       a[e.name] = parseFn(e.value);
32233       return a;
32234     }, {});
32235   };
32236 
32237   function decodeB64ToUint8Array$1(b64Text) {
32238     var decodedString = window$1.atob(b64Text);
32239     var array = new Uint8Array(decodedString.length);
32240 
32241     for (var i = 0; i < decodedString.length; i++) {
32242       array[i] = decodedString.charCodeAt(i);
32243     }
32244 
32245     return array;
32246   }
32247 
32248   var keySystemsMap = {
32249     'urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b': 'org.w3.clearkey',
32250     'urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed': 'com.widevine.alpha',
32251     'urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95': 'com.microsoft.playready',
32252     'urn:uuid:f239e769-efa3-4850-9c16-a903c6932efb': 'com.adobe.primetime'
32253   };
32254   /**
32255    * Builds a list of urls that is the product of the reference urls and BaseURL values
32256    *
32257    * @param {string[]} referenceUrls
32258    *        List of reference urls to resolve to
32259    * @param {Node[]} baseUrlElements
32260    *        List of BaseURL nodes from the mpd
32261    * @return {string[]}
32262    *         List of resolved urls
32263    */
32264 
32265   var buildBaseUrls = function buildBaseUrls(referenceUrls, baseUrlElements) {
32266     if (!baseUrlElements.length) {
32267       return referenceUrls;
32268     }
32269 
32270     return flatten(referenceUrls.map(function (reference) {
32271       return baseUrlElements.map(function (baseUrlElement) {
32272         return resolveUrl(reference, getContent(baseUrlElement));
32273       });
32274     }));
32275   };
32276   /**
32277    * Contains all Segment information for its containing AdaptationSet
32278    *
32279    * @typedef {Object} SegmentInformation
32280    * @property {Object|undefined} template
32281    *           Contains the attributes for the SegmentTemplate node
32282    * @property {Object[]|undefined} timeline
32283    *           Contains a list of atrributes for each S node within the SegmentTimeline node
32284    * @property {Object|undefined} list
32285    *           Contains the attributes for the SegmentList node
32286    * @property {Object|undefined} base
32287    *           Contains the attributes for the SegmentBase node
32288    */
32289 
32290   /**
32291    * Returns all available Segment information contained within the AdaptationSet node
32292    *
32293    * @param {Node} adaptationSet
32294    *        The AdaptationSet node to get Segment information from
32295    * @return {SegmentInformation}
32296    *         The Segment information contained within the provided AdaptationSet
32297    */
32298 
32299 
32300   var getSegmentInformation = function getSegmentInformation(adaptationSet) {
32301     var segmentTemplate = findChildren(adaptationSet, 'SegmentTemplate')[0];
32302     var segmentList = findChildren(adaptationSet, 'SegmentList')[0];
32303     var segmentUrls = segmentList && findChildren(segmentList, 'SegmentURL').map(function (s) {
32304       return merge({
32305         tag: 'SegmentURL'
32306       }, parseAttributes$1(s));
32307     });
32308     var segmentBase = findChildren(adaptationSet, 'SegmentBase')[0];
32309     var segmentTimelineParentNode = segmentList || segmentTemplate;
32310     var segmentTimeline = segmentTimelineParentNode && findChildren(segmentTimelineParentNode, 'SegmentTimeline')[0];
32311     var segmentInitializationParentNode = segmentList || segmentBase || segmentTemplate;
32312     var segmentInitialization = segmentInitializationParentNode && findChildren(segmentInitializationParentNode, 'Initialization')[0]; // SegmentTemplate is handled slightly differently, since it can have both
32313     // @initialization and an <Initialization> node.  @initialization can be templated,
32314     // while the node can have a url and range specified.  If the <SegmentTemplate> has
32315     // both @initialization and an <Initialization> subelement we opt to override with
32316     // the node, as this interaction is not defined in the spec.
32317 
32318     var template = segmentTemplate && parseAttributes$1(segmentTemplate);
32319 
32320     if (template && segmentInitialization) {
32321       template.initialization = segmentInitialization && parseAttributes$1(segmentInitialization);
32322     } else if (template && template.initialization) {
32323       // If it is @initialization we convert it to an object since this is the format that
32324       // later functions will rely on for the initialization segment.  This is only valid
32325       // for <SegmentTemplate>
32326       template.initialization = {
32327         sourceURL: template.initialization
32328       };
32329     }
32330 
32331     var segmentInfo = {
32332       template: template,
32333       timeline: segmentTimeline && findChildren(segmentTimeline, 'S').map(function (s) {
32334         return parseAttributes$1(s);
32335       }),
32336       list: segmentList && merge(parseAttributes$1(segmentList), {
32337         segmentUrls: segmentUrls,
32338         initialization: parseAttributes$1(segmentInitialization)
32339       }),
32340       base: segmentBase && merge(parseAttributes$1(segmentBase), {
32341         initialization: parseAttributes$1(segmentInitialization)
32342       })
32343     };
32344     Object.keys(segmentInfo).forEach(function (key) {
32345       if (!segmentInfo[key]) {
32346         delete segmentInfo[key];
32347       }
32348     });
32349     return segmentInfo;
32350   };
32351   /**
32352    * Contains Segment information and attributes needed to construct a Playlist object
32353    * from a Representation
32354    *
32355    * @typedef {Object} RepresentationInformation
32356    * @property {SegmentInformation} segmentInfo
32357    *           Segment information for this Representation
32358    * @property {Object} attributes
32359    *           Inherited attributes for this Representation
32360    */
32361 
32362   /**
32363    * Maps a Representation node to an object containing Segment information and attributes
32364    *
32365    * @name inheritBaseUrlsCallback
32366    * @function
32367    * @param {Node} representation
32368    *        Representation node from the mpd
32369    * @return {RepresentationInformation}
32370    *         Representation information needed to construct a Playlist object
32371    */
32372 
32373   /**
32374    * Returns a callback for Array.prototype.map for mapping Representation nodes to
32375    * Segment information and attributes using inherited BaseURL nodes.
32376    *
32377    * @param {Object} adaptationSetAttributes
32378    *        Contains attributes inherited by the AdaptationSet
32379    * @param {string[]} adaptationSetBaseUrls
32380    *        Contains list of resolved base urls inherited by the AdaptationSet
32381    * @param {SegmentInformation} adaptationSetSegmentInfo
32382    *        Contains Segment information for the AdaptationSet
32383    * @return {inheritBaseUrlsCallback}
32384    *         Callback map function
32385    */
32386 
32387 
32388   var inheritBaseUrls = function inheritBaseUrls(adaptationSetAttributes, adaptationSetBaseUrls, adaptationSetSegmentInfo) {
32389     return function (representation) {
32390       var repBaseUrlElements = findChildren(representation, 'BaseURL');
32391       var repBaseUrls = buildBaseUrls(adaptationSetBaseUrls, repBaseUrlElements);
32392       var attributes = merge(adaptationSetAttributes, parseAttributes$1(representation));
32393       var representationSegmentInfo = getSegmentInformation(representation);
32394       return repBaseUrls.map(function (baseUrl) {
32395         return {
32396           segmentInfo: merge(adaptationSetSegmentInfo, representationSegmentInfo),
32397           attributes: merge(attributes, {
32398             baseUrl: baseUrl
32399           })
32400         };
32401       });
32402     };
32403   };
32404   /**
32405    * Tranforms a series of content protection nodes to
32406    * an object containing pssh data by key system
32407    *
32408    * @param {Node[]} contentProtectionNodes
32409    *        Content protection nodes
32410    * @return {Object}
32411    *        Object containing pssh data by key system
32412    */
32413 
32414 
32415   var generateKeySystemInformation = function generateKeySystemInformation(contentProtectionNodes) {
32416     return contentProtectionNodes.reduce(function (acc, node) {
32417       var attributes = parseAttributes$1(node);
32418       var keySystem = keySystemsMap[attributes.schemeIdUri];
32419 
32420       if (keySystem) {
32421         acc[keySystem] = {
32422           attributes: attributes
32423         };
32424         var psshNode = findChildren(node, 'cenc:pssh')[0];
32425 
32426         if (psshNode) {
32427           var pssh = getContent(psshNode);
32428           var psshBuffer = pssh && decodeB64ToUint8Array$1(pssh);
32429           acc[keySystem].pssh = psshBuffer;
32430         }
32431       }
32432 
32433       return acc;
32434     }, {});
32435   };
32436   /**
32437    * Maps an AdaptationSet node to a list of Representation information objects
32438    *
32439    * @name toRepresentationsCallback
32440    * @function
32441    * @param {Node} adaptationSet
32442    *        AdaptationSet node from the mpd
32443    * @return {RepresentationInformation[]}
32444    *         List of objects containing Representaion information
32445    */
32446 
32447   /**
32448    * Returns a callback for Array.prototype.map for mapping AdaptationSet nodes to a list of
32449    * Representation information objects
32450    *
32451    * @param {Object} periodAttributes
32452    *        Contains attributes inherited by the Period
32453    * @param {string[]} periodBaseUrls
32454    *        Contains list of resolved base urls inherited by the Period
32455    * @param {string[]} periodSegmentInfo
32456    *        Contains Segment Information at the period level
32457    * @return {toRepresentationsCallback}
32458    *         Callback map function
32459    */
32460 
32461 
32462   var toRepresentations = function toRepresentations(periodAttributes, periodBaseUrls, periodSegmentInfo) {
32463     return function (adaptationSet) {
32464       var adaptationSetAttributes = parseAttributes$1(adaptationSet);
32465       var adaptationSetBaseUrls = buildBaseUrls(periodBaseUrls, findChildren(adaptationSet, 'BaseURL'));
32466       var role = findChildren(adaptationSet, 'Role')[0];
32467       var roleAttributes = {
32468         role: parseAttributes$1(role)
32469       };
32470       var attrs = merge(periodAttributes, adaptationSetAttributes, roleAttributes);
32471       var contentProtection = generateKeySystemInformation(findChildren(adaptationSet, 'ContentProtection'));
32472 
32473       if (Object.keys(contentProtection).length) {
32474         attrs = merge(attrs, {
32475           contentProtection: contentProtection
32476         });
32477       }
32478 
32479       var segmentInfo = getSegmentInformation(adaptationSet);
32480       var representations = findChildren(adaptationSet, 'Representation');
32481       var adaptationSetSegmentInfo = merge(periodSegmentInfo, segmentInfo);
32482       return flatten(representations.map(inheritBaseUrls(attrs, adaptationSetBaseUrls, adaptationSetSegmentInfo)));
32483     };
32484   };
32485   /**
32486    * Maps an Period node to a list of Representation inforamtion objects for all
32487    * AdaptationSet nodes contained within the Period
32488    *
32489    * @name toAdaptationSetsCallback
32490    * @function
32491    * @param {Node} period
32492    *        Period node from the mpd
32493    * @param {number} periodIndex
32494    *        Index of the Period within the mpd
32495    * @return {RepresentationInformation[]}
32496    *         List of objects containing Representaion information
32497    */
32498 
32499   /**
32500    * Returns a callback for Array.prototype.map for mapping Period nodes to a list of
32501    * Representation information objects
32502    *
32503    * @param {Object} mpdAttributes
32504    *        Contains attributes inherited by the mpd
32505    * @param {string[]} mpdBaseUrls
32506    *        Contains list of resolved base urls inherited by the mpd
32507    * @return {toAdaptationSetsCallback}
32508    *         Callback map function
32509    */
32510 
32511 
32512   var toAdaptationSets = function toAdaptationSets(mpdAttributes, mpdBaseUrls) {
32513     return function (period, index) {
32514       var periodBaseUrls = buildBaseUrls(mpdBaseUrls, findChildren(period, 'BaseURL'));
32515       var periodAtt = parseAttributes$1(period);
32516       var parsedPeriodId = parseInt(periodAtt.id, 10); // fallback to mapping index if Period@id is not a number
32517 
32518       var periodIndex = window$1.isNaN(parsedPeriodId) ? index : parsedPeriodId;
32519       var periodAttributes = merge(mpdAttributes, {
32520         periodIndex: periodIndex
32521       });
32522       var adaptationSets = findChildren(period, 'AdaptationSet');
32523       var periodSegmentInfo = getSegmentInformation(period);
32524       return flatten(adaptationSets.map(toRepresentations(periodAttributes, periodBaseUrls, periodSegmentInfo)));
32525     };
32526   };
32527   /**
32528    * Traverses the mpd xml tree to generate a list of Representation information objects
32529    * that have inherited attributes from parent nodes
32530    *
32531    * @param {Node} mpd
32532    *        The root node of the mpd
32533    * @param {Object} options
32534    *        Available options for inheritAttributes
32535    * @param {string} options.manifestUri
32536    *        The uri source of the mpd
32537    * @param {number} options.NOW
32538    *        Current time per DASH IOP.  Default is current time in ms since epoch
32539    * @param {number} options.clientOffset
32540    *        Client time difference from NOW (in milliseconds)
32541    * @return {RepresentationInformation[]}
32542    *         List of objects containing Representation information
32543    */
32544 
32545 
32546   var inheritAttributes = function inheritAttributes(mpd, options) {
32547     if (options === void 0) {
32548       options = {};
32549     }
32550 
32551     var _options = options,
32552         _options$manifestUri = _options.manifestUri,
32553         manifestUri = _options$manifestUri === void 0 ? '' : _options$manifestUri,
32554         _options$NOW = _options.NOW,
32555         NOW = _options$NOW === void 0 ? Date.now() : _options$NOW,
32556         _options$clientOffset = _options.clientOffset,
32557         clientOffset = _options$clientOffset === void 0 ? 0 : _options$clientOffset;
vendor: 7,205 bytes, lines 32558-32796
32558     var periods = findChildren(mpd, 'Period');
32559 
32560     if (!periods.length) {
32561       throw new Error(errors.INVALID_NUMBER_OF_PERIOD);
32562     }
32563 
32564     var mpdAttributes = parseAttributes$1(mpd);
32565     var mpdBaseUrls = buildBaseUrls([manifestUri], findChildren(mpd, 'BaseURL'));
32566     mpdAttributes.sourceDuration = mpdAttributes.mediaPresentationDuration || 0;
32567     mpdAttributes.NOW = NOW;
32568     mpdAttributes.clientOffset = clientOffset;
32569     return flatten(periods.map(toAdaptationSets(mpdAttributes, mpdBaseUrls)));
32570   };
32571 
32572   var stringToMpdXml = function stringToMpdXml(manifestString) {
32573     if (manifestString === '') {
32574       throw new Error(errors.DASH_EMPTY_MANIFEST);
32575     }
32576 
32577     var parser = new window$1.DOMParser();
32578     var xml = parser.parseFromString(manifestString, 'application/xml');
32579     var mpd = xml && xml.documentElement.tagName === 'MPD' ? xml.documentElement : null;
32580 
32581     if (!mpd || mpd && mpd.getElementsByTagName('parsererror').length > 0) {
32582       throw new Error(errors.DASH_INVALID_XML);
32583     }
32584 
32585     return mpd;
32586   };
32587   /**
32588    * Parses the manifest for a UTCTiming node, returning the nodes attributes if found
32589    *
32590    * @param {string} mpd
32591    *        XML string of the MPD manifest
32592    * @return {Object|null}
32593    *         Attributes of UTCTiming node specified in the manifest. Null if none found
32594    */
32595 
32596 
32597   var parseUTCTimingScheme = function parseUTCTimingScheme(mpd) {
32598     var UTCTimingNode = findChildren(mpd, 'UTCTiming')[0];
32599 
32600     if (!UTCTimingNode) {
32601       return null;
32602     }
32603 
32604     var attributes = parseAttributes$1(UTCTimingNode);
32605 
32606     switch (attributes.schemeIdUri) {
32607       case 'urn:mpeg:dash:utc:http-head:2014':
32608       case 'urn:mpeg:dash:utc:http-head:2012':
32609         attributes.method = 'HEAD';
32610         break;
32611 
32612       case 'urn:mpeg:dash:utc:http-xsdate:2014':
32613       case 'urn:mpeg:dash:utc:http-iso:2014':
32614       case 'urn:mpeg:dash:utc:http-xsdate:2012':
32615       case 'urn:mpeg:dash:utc:http-iso:2012':
32616         attributes.method = 'GET';
32617         break;
32618 
32619       case 'urn:mpeg:dash:utc:direct:2014':
32620       case 'urn:mpeg:dash:utc:direct:2012':
32621         attributes.method = 'DIRECT';
32622         attributes.value = Date.parse(attributes.value);
32623         break;
32624 
32625       case 'urn:mpeg:dash:utc:http-ntp:2014':
32626       case 'urn:mpeg:dash:utc:ntp:2014':
32627       case 'urn:mpeg:dash:utc:sntp:2014':
32628       default:
32629         throw new Error(errors.UNSUPPORTED_UTC_TIMING_SCHEME);
32630     }
32631 
32632     return attributes;
32633   };
32634 
32635   var parse = function parse(manifestString, options) {
32636     if (options === void 0) {
32637       options = {};
32638     }
32639 
32640     return toM3u8(toPlaylists(inheritAttributes(stringToMpdXml(manifestString), options)), options.sidxMapping);
32641   };
32642   /**
32643    * Parses the manifest for a UTCTiming node, returning the nodes attributes if found
32644    *
32645    * @param {string} manifestString
32646    *        XML string of the MPD manifest
32647    * @return {Object|null}
32648    *         Attributes of UTCTiming node specified in the manifest. Null if none found
32649    */
32650 
32651 
32652   var parseUTCTiming = function parseUTCTiming(manifestString) {
32653     return parseUTCTimingScheme(stringToMpdXml(manifestString));
32654   };
32655 
32656   /**
32657    * mux.js
32658    *
32659    * Copyright (c) Brightcove
32660    * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
32661    */
32662   var toUnsigned = function toUnsigned(value) {
32663     return value >>> 0;
32664   };
32665 
32666   var toHexString = function toHexString(value) {
32667     return ('00' + value.toString(16)).slice(-2);
32668   };
32669 
32670   var bin = {
32671     toUnsigned: toUnsigned,
32672     toHexString: toHexString
32673   };
32674 
32675   var inspectMp4,
32676       _textifyMp,
32677       toUnsigned$1 = bin.toUnsigned,
32678       parseMp4Date = function parseMp4Date(seconds) {
32679     return new Date(seconds * 1000 - 2082844800000);
32680   },
32681       parseSampleFlags = function parseSampleFlags(flags) {
32682     return {
32683       isLeading: (flags[0] & 0x0c) >>> 2,
32684       dependsOn: flags[0] & 0x03,
32685       isDependedOn: (flags[1] & 0xc0) >>> 6,
32686       hasRedundancy: (flags[1] & 0x30) >>> 4,
32687       paddingValue: (flags[1] & 0x0e) >>> 1,
32688       isNonSyncSample: flags[1] & 0x01,
32689       degradationPriority: flags[2] << 8 | flags[3]
32690     };
32691   },
32692 
32693   /**
32694    * Returns the string representation of an ASCII encoded four byte buffer.
32695    * @param buffer {Uint8Array} a four-byte buffer to translate
32696    * @return {string} the corresponding string
32697    */
32698   parseType = function parseType(buffer) {
32699     var result = '';
32700     result += String.fromCharCode(buffer[0]);
32701     result += String.fromCharCode(buffer[1]);
32702     result += String.fromCharCode(buffer[2]);
32703     result += String.fromCharCode(buffer[3]);
32704     return result;
32705   },
32706       // Find the data for a box specified by its path
32707   findBox = function findBox(data, path) {
32708     var results = [],
32709         i,
32710         size,
32711         type,
32712         end,
32713         subresults;
32714 
32715     if (!path.length) {
32716       // short-circuit the search for empty paths
32717       return null;
32718     }
32719 
32720     for (i = 0; i < data.byteLength;) {
32721       size = toUnsigned$1(data[i] << 24 | data[i + 1] << 16 | data[i + 2] << 8 | data[i + 3]);
32722       type = parseType(data.subarray(i + 4, i + 8));
32723       end = size > 1 ? i + size : data.byteLength;
32724 
32725       if (type === path[0]) {
32726         if (path.length === 1) {
32727           // this is the end of the path and we've found the box we were
32728           // looking for
32729           results.push(data.subarray(i + 8, end));
32730         } else {
32731           // recursively search for the next box along the path
32732           subresults = findBox(data.subarray(i + 8, end), path.slice(1));
32733 
32734           if (subresults.length) {
32735             results = results.concat(subresults);
32736           }
32737         }
32738       }
32739 
32740       i = end;
32741     } // we've finished searching all of data
32742 
32743 
32744     return results;
32745   },
32746       nalParse = function nalParse(avcStream) {
32747     var avcView = new DataView(avcStream.buffer, avcStream.byteOffset, avcStream.byteLength),
32748         result = [],
32749         i,
32750         length;
32751 
32752     for (i = 0; i + 4 < avcStream.length; i += length) {
32753       length = avcView.getUint32(i);
32754       i += 4; // bail if this doesn't appear to be an H264 stream
32755 
32756       if (length <= 0) {
32757         result.push('<span style=\'color:red;\'>MALFORMED DATA</span>');
32758         continue;
32759       }
32760 
32761       switch (avcStream[i] & 0x1F) {
32762         case 0x01:
32763           result.push('slice_layer_without_partitioning_rbsp');
32764           break;
32765 
32766         case 0x05:
32767           result.push('slice_layer_without_partitioning_rbsp_idr');
32768           break;
32769 
32770         case 0x06:
32771           result.push('sei_rbsp');
32772           break;
32773 
32774         case 0x07:
32775           result.push('seq_parameter_set_rbsp');
32776           break;
32777 
32778         case 0x08:
32779           result.push('pic_parameter_set_rbsp');
32780           break;
32781 
32782         case 0x09:
32783           result.push('access_unit_delimiter_rbsp');
32784           break;
32785 
32786         default:
32787           result.push('UNKNOWN NAL - ' + avcStream[i] & 0x1F);
32788           break;
32789       }
32790     }
32791 
32792     return result;
32793   },
32794       // registry of handlers for individual mp4 box types
32795   parse$1 = {
32796     // codingname, not a first-class box type. stsd entries share the
vendor: 6,697 bytes, lines 32797-32997
32797     // same format as real boxes so the parsing infrastructure can be
32798     // shared
32799     avc1: function avc1(data) {
32800       var view = new DataView(data.buffer, data.byteOffset, data.byteLength);
32801       return {
32802         dataReferenceIndex: view.getUint16(6),
32803         width: view.getUint16(24),
32804         height: view.getUint16(26),
32805         horizresolution: view.getUint16(28) + view.getUint16(30) / 16,
32806         vertresolution: view.getUint16(32) + view.getUint16(34) / 16,
32807         frameCount: view.getUint16(40),
32808         depth: view.getUint16(74),
32809         config: inspectMp4(data.subarray(78, data.byteLength))
32810       };
32811     },
32812     avcC: function avcC(data) {
32813       var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
32814           result = {
32815         configurationVersion: data[0],
32816         avcProfileIndication: data[1],
32817         profileCompatibility: data[2],
32818         avcLevelIndication: data[3],
32819         lengthSizeMinusOne: data[4] & 0x03,
32820         sps: [],
32821         pps: []
32822       },
32823           numOfSequenceParameterSets = data[5] & 0x1f,
32824           numOfPictureParameterSets,
32825           nalSize,
32826           offset,
32827           i; // iterate past any SPSs
32828 
32829       offset = 6;
32830 
32831       for (i = 0; i < numOfSequenceParameterSets; i++) {
32832         nalSize = view.getUint16(offset);
32833         offset += 2;
32834         result.sps.push(new Uint8Array(data.subarray(offset, offset + nalSize)));
32835         offset += nalSize;
32836       } // iterate past any PPSs
32837 
32838 
32839       numOfPictureParameterSets = data[offset];
32840       offset++;
32841 
32842       for (i = 0; i < numOfPictureParameterSets; i++) {
32843         nalSize = view.getUint16(offset);
32844         offset += 2;
32845         result.pps.push(new Uint8Array(data.subarray(offset, offset + nalSize)));
32846         offset += nalSize;
32847       }
32848 
32849       return result;
32850     },
32851     btrt: function btrt(data) {
32852       var view = new DataView(data.buffer, data.byteOffset, data.byteLength);
32853       return {
32854         bufferSizeDB: view.getUint32(0),
32855         maxBitrate: view.getUint32(4),
32856         avgBitrate: view.getUint32(8)
32857       };
32858     },
32859     esds: function esds(data) {
32860       return {
32861         version: data[0],
32862         flags: new Uint8Array(data.subarray(1, 4)),
32863         esId: data[6] << 8 | data[7],
32864         streamPriority: data[8] & 0x1f,
32865         decoderConfig: {
32866           objectProfileIndication: data[11],
32867           streamType: data[12] >>> 2 & 0x3f,
32868           bufferSize: data[13] << 16 | data[14] << 8 | data[15],
32869           maxBitrate: data[16] << 24 | data[17] << 16 | data[18] << 8 | data[19],
32870           avgBitrate: data[20] << 24 | data[21] << 16 | data[22] << 8 | data[23],
32871           decoderConfigDescriptor: {
32872             tag: data[24],
32873             length: data[25],
32874             audioObjectType: data[26] >>> 3 & 0x1f,
32875             samplingFrequencyIndex: (data[26] & 0x07) << 1 | data[27] >>> 7 & 0x01,
32876             channelConfiguration: data[27] >>> 3 & 0x0f
32877           }
32878         }
32879       };
32880     },
32881     ftyp: function ftyp(data) {
32882       var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
32883           result = {
32884         majorBrand: parseType(data.subarray(0, 4)),
32885         minorVersion: view.getUint32(4),
32886         compatibleBrands: []
32887       },
32888           i = 8;
32889 
32890       while (i < data.byteLength) {
32891         result.compatibleBrands.push(parseType(data.subarray(i, i + 4)));
32892         i += 4;
32893       }
32894 
32895       return result;
32896     },
32897     dinf: function dinf(data) {
32898       return {
32899         boxes: inspectMp4(data)
32900       };
32901     },
32902     dref: function dref(data) {
32903       return {
32904         version: data[0],
32905         flags: new Uint8Array(data.subarray(1, 4)),
32906         dataReferences: inspectMp4(data.subarray(8))
32907       };
32908     },
32909     hdlr: function hdlr(data) {
32910       var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
32911           result = {
32912         version: view.getUint8(0),
32913         flags: new Uint8Array(data.subarray(1, 4)),
32914         handlerType: parseType(data.subarray(8, 12)),
32915         name: ''
32916       },
32917           i = 8; // parse out the name field
32918 
32919       for (i = 24; i < data.byteLength; i++) {
32920         if (data[i] === 0x00) {
32921           // the name field is null-terminated
32922           i++;
32923           break;
32924         }
32925 
32926         result.name += String.fromCharCode(data[i]);
32927       } // decode UTF-8 to javascript's internal representation
32928       // see http://ecmanaut.blogspot.com/2006/07/encoding-decoding-utf8-in-javascript.html
32929 
32930 
32931       result.name = decodeURIComponent(escape(result.name));
32932       return result;
32933     },
32934     mdat: function mdat(data) {
32935       return {
32936         byteLength: data.byteLength,
32937         nals: nalParse(data)
32938       };
32939     },
32940     mdhd: function mdhd(data) {
32941       var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
32942           i = 4,
32943           language,
32944           result = {
32945         version: view.getUint8(0),
32946         flags: new Uint8Array(data.subarray(1, 4)),
32947         language: ''
32948       };
32949 
32950       if (result.version === 1) {
32951         i += 4;
32952         result.creationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes
32953 
32954         i += 8;
32955         result.modificationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes
32956 
32957         i += 4;
32958         result.timescale = view.getUint32(i);
32959         i += 8;
32960         result.duration = view.getUint32(i); // truncating top 4 bytes
32961       } else {
32962         result.creationTime = parseMp4Date(view.getUint32(i));
32963         i += 4;
32964         result.modificationTime = parseMp4Date(view.getUint32(i));
32965         i += 4;
32966         result.timescale = view.getUint32(i);
32967         i += 4;
32968         result.duration = view.getUint32(i);
32969       }
32970 
32971       i += 4; // language is stored as an ISO-639-2/T code in an array of three 5-bit fields
32972       // each field is the packed difference between its ASCII value and 0x60
32973 
32974       language = view.getUint16(i);
32975       result.language += String.fromCharCode((language >> 10) + 0x60);
32976       result.language += String.fromCharCode(((language & 0x03e0) >> 5) + 0x60);
32977       result.language += String.fromCharCode((language & 0x1f) + 0x60);
32978       return result;
32979     },
32980     mdia: function mdia(data) {
32981       return {
32982         boxes: inspectMp4(data)
32983       };
32984     },
32985     mfhd: function mfhd(data) {
32986       return {
32987         version: data[0],
32988         flags: new Uint8Array(data.subarray(1, 4)),
32989         sequenceNumber: data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]
32990       };
32991     },
32992     minf: function minf(data) {
32993       return {
32994         boxes: inspectMp4(data)
32995       };
32996     },
32997     // codingname, not a first-class box type. stsd entries share the
vendor: 25,421 bytes, lines 32998-33757
32998     // same format as real boxes so the parsing infrastructure can be
32999     // shared
33000     mp4a: function mp4a(data) {
33001       var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
33002           result = {
33003         // 6 bytes reserved
33004         dataReferenceIndex: view.getUint16(6),
33005         // 4 + 4 bytes reserved
33006         channelcount: view.getUint16(16),
33007         samplesize: view.getUint16(18),
33008         // 2 bytes pre_defined
33009         // 2 bytes reserved
33010         samplerate: view.getUint16(24) + view.getUint16(26) / 65536
33011       }; // if there are more bytes to process, assume this is an ISO/IEC
33012       // 14496-14 MP4AudioSampleEntry and parse the ESDBox
33013 
33014       if (data.byteLength > 28) {
33015         result.streamDescriptor = inspectMp4(data.subarray(28))[0];
33016       }
33017 
33018       return result;
33019     },
33020     moof: function moof(data) {
33021       return {
33022         boxes: inspectMp4(data)
33023       };
33024     },
33025     moov: function moov(data) {
33026       return {
33027         boxes: inspectMp4(data)
33028       };
33029     },
33030     mvex: function mvex(data) {
33031       return {
33032         boxes: inspectMp4(data)
33033       };
33034     },
33035     mvhd: function mvhd(data) {
33036       var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
33037           i = 4,
33038           result = {
33039         version: view.getUint8(0),
33040         flags: new Uint8Array(data.subarray(1, 4))
33041       };
33042 
33043       if (result.version === 1) {
33044         i += 4;
33045         result.creationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes
33046 
33047         i += 8;
33048         result.modificationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes
33049 
33050         i += 4;
33051         result.timescale = view.getUint32(i);
33052         i += 8;
33053         result.duration = view.getUint32(i); // truncating top 4 bytes
33054       } else {
33055         result.creationTime = parseMp4Date(view.getUint32(i));
33056         i += 4;
33057         result.modificationTime = parseMp4Date(view.getUint32(i));
33058         i += 4;
33059         result.timescale = view.getUint32(i);
33060         i += 4;
33061         result.duration = view.getUint32(i);
33062       }
33063 
33064       i += 4; // convert fixed-point, base 16 back to a number
33065 
33066       result.rate = view.getUint16(i) + view.getUint16(i + 2) / 16;
33067       i += 4;
33068       result.volume = view.getUint8(i) + view.getUint8(i + 1) / 8;
33069       i += 2;
33070       i += 2;
33071       i += 2 * 4;
33072       result.matrix = new Uint32Array(data.subarray(i, i + 9 * 4));
33073       i += 9 * 4;
33074       i += 6 * 4;
33075       result.nextTrackId = view.getUint32(i);
33076       return result;
33077     },
33078     pdin: function pdin(data) {
33079       var view = new DataView(data.buffer, data.byteOffset, data.byteLength);
33080       return {
33081         version: view.getUint8(0),
33082         flags: new Uint8Array(data.subarray(1, 4)),
33083         rate: view.getUint32(4),
33084         initialDelay: view.getUint32(8)
33085       };
33086     },
33087     sdtp: function sdtp(data) {
33088       var result = {
33089         version: data[0],
33090         flags: new Uint8Array(data.subarray(1, 4)),
33091         samples: []
33092       },
33093           i;
33094 
33095       for (i = 4; i < data.byteLength; i++) {
33096         result.samples.push({
33097           dependsOn: (data[i] & 0x30) >> 4,
33098           isDependedOn: (data[i] & 0x0c) >> 2,
33099           hasRedundancy: data[i] & 0x03
33100         });
33101       }
33102 
33103       return result;
33104     },
33105     sidx: function sidx(data) {
33106       var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
33107           result = {
33108         version: data[0],
33109         flags: new Uint8Array(data.subarray(1, 4)),
33110         references: [],
33111         referenceId: view.getUint32(4),
33112         timescale: view.getUint32(8),
33113         earliestPresentationTime: view.getUint32(12),
33114         firstOffset: view.getUint32(16)
33115       },
33116           referenceCount = view.getUint16(22),
33117           i;
33118 
33119       for (i = 24; referenceCount; i += 12, referenceCount--) {
33120         result.references.push({
33121           referenceType: (data[i] & 0x80) >>> 7,
33122           referencedSize: view.getUint32(i) & 0x7FFFFFFF,
33123           subsegmentDuration: view.getUint32(i + 4),
33124           startsWithSap: !!(data[i + 8] & 0x80),
33125           sapType: (data[i + 8] & 0x70) >>> 4,
33126           sapDeltaTime: view.getUint32(i + 8) & 0x0FFFFFFF
33127         });
33128       }
33129 
33130       return result;
33131     },
33132     smhd: function smhd(data) {
33133       return {
33134         version: data[0],
33135         flags: new Uint8Array(data.subarray(1, 4)),
33136         balance: data[4] + data[5] / 256
33137       };
33138     },
33139     stbl: function stbl(data) {
33140       return {
33141         boxes: inspectMp4(data)
33142       };
33143     },
33144     stco: function stco(data) {
33145       var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
33146           result = {
33147         version: data[0],
33148         flags: new Uint8Array(data.subarray(1, 4)),
33149         chunkOffsets: []
33150       },
33151           entryCount = view.getUint32(4),
33152           i;
33153 
33154       for (i = 8; entryCount; i += 4, entryCount--) {
33155         result.chunkOffsets.push(view.getUint32(i));
33156       }
33157 
33158       return result;
33159     },
33160     stsc: function stsc(data) {
33161       var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
33162           entryCount = view.getUint32(4),
33163           result = {
33164         version: data[0],
33165         flags: new Uint8Array(data.subarray(1, 4)),
33166         sampleToChunks: []
33167       },
33168           i;
33169 
33170       for (i = 8; entryCount; i += 12, entryCount--) {
33171         result.sampleToChunks.push({
33172           firstChunk: view.getUint32(i),
33173           samplesPerChunk: view.getUint32(i + 4),
33174           sampleDescriptionIndex: view.getUint32(i + 8)
33175         });
33176       }
33177 
33178       return result;
33179     },
33180     stsd: function stsd(data) {
33181       return {
33182         version: data[0],
33183         flags: new Uint8Array(data.subarray(1, 4)),
33184         sampleDescriptions: inspectMp4(data.subarray(8))
33185       };
33186     },
33187     stsz: function stsz(data) {
33188       var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
33189           result = {
33190         version: data[0],
33191         flags: new Uint8Array(data.subarray(1, 4)),
33192         sampleSize: view.getUint32(4),
33193         entries: []
33194       },
33195           i;
33196 
33197       for (i = 12; i < data.byteLength; i += 4) {
33198         result.entries.push(view.getUint32(i));
33199       }
33200 
33201       return result;
33202     },
33203     stts: function stts(data) {
33204       var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
33205           result = {
33206         version: data[0],
33207         flags: new Uint8Array(data.subarray(1, 4)),
33208         timeToSamples: []
33209       },
33210           entryCount = view.getUint32(4),
33211           i;
33212 
33213       for (i = 8; entryCount; i += 8, entryCount--) {
33214         result.timeToSamples.push({
33215           sampleCount: view.getUint32(i),
33216           sampleDelta: view.getUint32(i + 4)
33217         });
33218       }
33219 
33220       return result;
33221     },
33222     styp: function styp(data) {
33223       return parse$1.ftyp(data);
33224     },
33225     tfdt: function tfdt(data) {
33226       var result = {
33227         version: data[0],
33228         flags: new Uint8Array(data.subarray(1, 4)),
33229         baseMediaDecodeTime: toUnsigned$1(data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7])
33230       };
33231 
33232       if (result.version === 1) {
33233         result.baseMediaDecodeTime *= Math.pow(2, 32);
33234         result.baseMediaDecodeTime += toUnsigned$1(data[8] << 24 | data[9] << 16 | data[10] << 8 | data[11]);
33235       }
33236 
33237       return result;
33238     },
33239     tfhd: function tfhd(data) {
33240       var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
33241           result = {
33242         version: data[0],
33243         flags: new Uint8Array(data.subarray(1, 4)),
33244         trackId: view.getUint32(4)
33245       },
33246           baseDataOffsetPresent = result.flags[2] & 0x01,
33247           sampleDescriptionIndexPresent = result.flags[2] & 0x02,
33248           defaultSampleDurationPresent = result.flags[2] & 0x08,
33249           defaultSampleSizePresent = result.flags[2] & 0x10,
33250           defaultSampleFlagsPresent = result.flags[2] & 0x20,
33251           durationIsEmpty = result.flags[0] & 0x010000,
33252           defaultBaseIsMoof = result.flags[0] & 0x020000,
33253           i;
33254       i = 8;
33255 
33256       if (baseDataOffsetPresent) {
33257         i += 4; // truncate top 4 bytes
33258         // FIXME: should we read the full 64 bits?
33259 
33260         result.baseDataOffset = view.getUint32(12);
33261         i += 4;
33262       }
33263 
33264       if (sampleDescriptionIndexPresent) {
33265         result.sampleDescriptionIndex = view.getUint32(i);
33266         i += 4;
33267       }
33268 
33269       if (defaultSampleDurationPresent) {
33270         result.defaultSampleDuration = view.getUint32(i);
33271         i += 4;
33272       }
33273 
33274       if (defaultSampleSizePresent) {
33275         result.defaultSampleSize = view.getUint32(i);
33276         i += 4;
33277       }
33278 
33279       if (defaultSampleFlagsPresent) {
33280         result.defaultSampleFlags = view.getUint32(i);
33281       }
33282 
33283       if (durationIsEmpty) {
33284         result.durationIsEmpty = true;
33285       }
33286 
33287       if (!baseDataOffsetPresent && defaultBaseIsMoof) {
33288         result.baseDataOffsetIsMoof = true;
33289       }
33290 
33291       return result;
33292     },
33293     tkhd: function tkhd(data) {
33294       var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
33295           i = 4,
33296           result = {
33297         version: view.getUint8(0),
33298         flags: new Uint8Array(data.subarray(1, 4))
33299       };
33300 
33301       if (result.version === 1) {
33302         i += 4;
33303         result.creationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes
33304 
33305         i += 8;
33306         result.modificationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes
33307 
33308         i += 4;
33309         result.trackId = view.getUint32(i);
33310         i += 4;
33311         i += 8;
33312         result.duration = view.getUint32(i); // truncating top 4 bytes
33313       } else {
33314         result.creationTime = parseMp4Date(view.getUint32(i));
33315         i += 4;
33316         result.modificationTime = parseMp4Date(view.getUint32(i));
33317         i += 4;
33318         result.trackId = view.getUint32(i);
33319         i += 4;
33320         i += 4;
33321         result.duration = view.getUint32(i);
33322       }
33323 
33324       i += 4;
33325       i += 2 * 4;
33326       result.layer = view.getUint16(i);
33327       i += 2;
33328       result.alternateGroup = view.getUint16(i);
33329       i += 2; // convert fixed-point, base 16 back to a number
33330 
33331       result.volume = view.getUint8(i) + view.getUint8(i + 1) / 8;
33332       i += 2;
33333       i += 2;
33334       result.matrix = new Uint32Array(data.subarray(i, i + 9 * 4));
33335       i += 9 * 4;
33336       result.width = view.getUint16(i) + view.getUint16(i + 2) / 16;
33337       i += 4;
33338       result.height = view.getUint16(i) + view.getUint16(i + 2) / 16;
33339       return result;
33340     },
33341     traf: function traf(data) {
33342       return {
33343         boxes: inspectMp4(data)
33344       };
33345     },
33346     trak: function trak(data) {
33347       return {
33348         boxes: inspectMp4(data)
33349       };
33350     },
33351     trex: function trex(data) {
33352       var view = new DataView(data.buffer, data.byteOffset, data.byteLength);
33353       return {
33354         version: data[0],
33355         flags: new Uint8Array(data.subarray(1, 4)),
33356         trackId: view.getUint32(4),
33357         defaultSampleDescriptionIndex: view.getUint32(8),
33358         defaultSampleDuration: view.getUint32(12),
33359         defaultSampleSize: view.getUint32(16),
33360         sampleDependsOn: data[20] & 0x03,
33361         sampleIsDependedOn: (data[21] & 0xc0) >> 6,
33362         sampleHasRedundancy: (data[21] & 0x30) >> 4,
33363         samplePaddingValue: (data[21] & 0x0e) >> 1,
33364         sampleIsDifferenceSample: !!(data[21] & 0x01),
33365         sampleDegradationPriority: view.getUint16(22)
33366       };
33367     },
33368     trun: function trun(data) {
33369       var result = {
33370         version: data[0],
33371         flags: new Uint8Array(data.subarray(1, 4)),
33372         samples: []
33373       },
33374           view = new DataView(data.buffer, data.byteOffset, data.byteLength),
33375           // Flag interpretation
33376       dataOffsetPresent = result.flags[2] & 0x01,
33377           // compare with 2nd byte of 0x1
33378       firstSampleFlagsPresent = result.flags[2] & 0x04,
33379           // compare with 2nd byte of 0x4
33380       sampleDurationPresent = result.flags[1] & 0x01,
33381           // compare with 2nd byte of 0x100
33382       sampleSizePresent = result.flags[1] & 0x02,
33383           // compare with 2nd byte of 0x200
33384       sampleFlagsPresent = result.flags[1] & 0x04,
33385           // compare with 2nd byte of 0x400
33386       sampleCompositionTimeOffsetPresent = result.flags[1] & 0x08,
33387           // compare with 2nd byte of 0x800
33388       sampleCount = view.getUint32(4),
33389           offset = 8,
33390           sample;
33391 
33392       if (dataOffsetPresent) {
33393         // 32 bit signed integer
33394         result.dataOffset = view.getInt32(offset);
33395         offset += 4;
33396       } // Overrides the flags for the first sample only. The order of
33397       // optional values will be: duration, size, compositionTimeOffset
33398 
33399 
33400       if (firstSampleFlagsPresent && sampleCount) {
33401         sample = {
33402           flags: parseSampleFlags(data.subarray(offset, offset + 4))
33403         };
33404         offset += 4;
33405 
33406         if (sampleDurationPresent) {
33407           sample.duration = view.getUint32(offset);
33408           offset += 4;
33409         }
33410 
33411         if (sampleSizePresent) {
33412           sample.size = view.getUint32(offset);
33413           offset += 4;
33414         }
33415 
33416         if (sampleCompositionTimeOffsetPresent) {
33417           // Note: this should be a signed int if version is 1
33418           sample.compositionTimeOffset = view.getUint32(offset);
33419           offset += 4;
33420         }
33421 
33422         result.samples.push(sample);
33423         sampleCount--;
33424       }
33425 
33426       while (sampleCount--) {
33427         sample = {};
33428 
33429         if (sampleDurationPresent) {
33430           sample.duration = view.getUint32(offset);
33431           offset += 4;
33432         }
33433 
33434         if (sampleSizePresent) {
33435           sample.size = view.getUint32(offset);
33436           offset += 4;
33437         }
33438 
33439         if (sampleFlagsPresent) {
33440           sample.flags = parseSampleFlags(data.subarray(offset, offset + 4));
33441           offset += 4;
33442         }
33443 
33444         if (sampleCompositionTimeOffsetPresent) {
33445           // Note: this should be a signed int if version is 1
33446           sample.compositionTimeOffset = view.getUint32(offset);
33447           offset += 4;
33448         }
33449 
33450         result.samples.push(sample);
33451       }
33452 
33453       return result;
33454     },
33455     'url ': function url(data) {
33456       return {
33457         version: data[0],
33458         flags: new Uint8Array(data.subarray(1, 4))
33459       };
33460     },
33461     vmhd: function vmhd(data) {
33462       var view = new DataView(data.buffer, data.byteOffset, data.byteLength);
33463       return {
33464         version: data[0],
33465         flags: new Uint8Array(data.subarray(1, 4)),
33466         graphicsmode: view.getUint16(4),
33467         opcolor: new Uint16Array([view.getUint16(6), view.getUint16(8), view.getUint16(10)])
33468       };
33469     }
33470   };
33471   /**
33472    * Return a javascript array of box objects parsed from an ISO base
33473    * media file.
33474    * @param data {Uint8Array} the binary data of the media to be inspected
33475    * @return {array} a javascript array of potentially nested box objects
33476    */
33477 
33478 
33479   inspectMp4 = function inspectMp4(data) {
33480     var i = 0,
33481         result = [],
33482         view,
33483         size,
33484         type,
33485         end,
33486         box; // Convert data from Uint8Array to ArrayBuffer, to follow Dataview API
33487 
33488     var ab = new ArrayBuffer(data.length);
33489     var v = new Uint8Array(ab);
33490 
33491     for (var z = 0; z < data.length; ++z) {
33492       v[z] = data[z];
33493     }
33494 
33495     view = new DataView(ab);
33496 
33497     while (i < data.byteLength) {
33498       // parse box data
33499       size = view.getUint32(i);
33500       type = parseType(data.subarray(i + 4, i + 8));
33501       end = size > 1 ? i + size : data.byteLength; // parse type-specific data
33502 
33503       box = (parse$1[type] || function (data) {
33504         return {
33505           data: data
33506         };
33507       })(data.subarray(i + 8, end));
33508 
33509       box.size = size;
33510       box.type = type; // store this box and move to the next
33511 
33512       result.push(box);
33513       i = end;
33514     }
33515 
33516     return result;
33517   };
33518   /**
33519    * Returns a textual representation of the javascript represtentation
33520    * of an MP4 file. You can use it as an alternative to
33521    * JSON.stringify() to compare inspected MP4s.
33522    * @param inspectedMp4 {array} the parsed array of boxes in an MP4
33523    * file
33524    * @param depth {number} (optional) the number of ancestor boxes of
33525    * the elements of inspectedMp4. Assumed to be zero if unspecified.
33526    * @return {string} a text representation of the parsed MP4
33527    */
33528 
33529 
33530   _textifyMp = function textifyMp4(inspectedMp4, depth) {
33531     var indent;
33532     depth = depth || 0;
33533     indent = new Array(depth * 2 + 1).join(' '); // iterate over all the boxes
33534 
33535     return inspectedMp4.map(function (box, index) {
33536       // list the box type first at the current indentation level
33537       return indent + box.type + '\n' + // the type is already included and handle child boxes separately
33538       Object.keys(box).filter(function (key) {
33539         return key !== 'type' && key !== 'boxes'; // output all the box properties
33540       }).map(function (key) {
33541         var prefix = indent + '  ' + key + ': ',
33542             value = box[key]; // print out raw bytes as hexademical
33543 
33544         if (value instanceof Uint8Array || value instanceof Uint32Array) {
33545           var bytes = Array.prototype.slice.call(new Uint8Array(value.buffer, value.byteOffset, value.byteLength)).map(function (_byte) {
33546             return ' ' + ('00' + _byte.toString(16)).slice(-2);
33547           }).join('').match(/.{1,24}/g);
33548 
33549           if (!bytes) {
33550             return prefix + '<>';
33551           }
33552 
33553           if (bytes.length === 1) {
33554             return prefix + '<' + bytes.join('').slice(1) + '>';
33555           }
33556 
33557           return prefix + '<\n' + bytes.map(function (line) {
33558             return indent + '  ' + line;
33559           }).join('\n') + '\n' + indent + '  >';
33560         } // stringify generic objects
33561 
33562 
33563         return prefix + JSON.stringify(value, null, 2).split('\n').map(function (line, index) {
33564           if (index === 0) {
33565             return line;
33566           }
33567 
33568           return indent + '  ' + line;
33569         }).join('\n');
33570       }).join('\n') + ( // recursively textify the child boxes
33571       box.boxes ? '\n' + _textifyMp(box.boxes, depth + 1) : '');
33572     }).join('\n');
33573   };
33574 
33575   var mp4Inspector = {
33576     inspect: inspectMp4,
33577     textify: _textifyMp,
33578     parseType: parseType,
33579     findBox: findBox,
33580     parseTraf: parse$1.traf,
33581     parseTfdt: parse$1.tfdt,
33582     parseHdlr: parse$1.hdlr,
33583     parseTfhd: parse$1.tfhd,
33584     parseTrun: parse$1.trun,
33585     parseSidx: parse$1.sidx
33586   };
33587 
33588   var toUnsigned$2 = bin.toUnsigned;
33589   var toHexString$1 = bin.toHexString;
33590   var timescale, startTime, compositionStartTime, getVideoTrackIds, getTracks;
33591   /**
33592    * Parses an MP4 initialization segment and extracts the timescale
33593    * values for any declared tracks. Timescale values indicate the
33594    * number of clock ticks per second to assume for time-based values
33595    * elsewhere in the MP4.
33596    *
33597    * To determine the start time of an MP4, you need two pieces of
33598    * information: the timescale unit and the earliest base media decode
33599    * time. Multiple timescales can be specified within an MP4 but the
33600    * base media decode time is always expressed in the timescale from
33601    * the media header box for the track:
33602    * ```
33603    * moov > trak > mdia > mdhd.timescale
33604    * ```
33605    * @param init {Uint8Array} the bytes of the init segment
33606    * @return {object} a hash of track ids to timescale values or null if
33607    * the init segment is malformed.
33608    */
33609 
33610   timescale = function timescale(init) {
33611     var result = {},
33612         traks = mp4Inspector.findBox(init, ['moov', 'trak']); // mdhd timescale
33613 
33614     return traks.reduce(function (result, trak) {
33615       var tkhd, version, index, id, mdhd;
33616       tkhd = mp4Inspector.findBox(trak, ['tkhd'])[0];
33617 
33618       if (!tkhd) {
33619         return null;
33620       }
33621 
33622       version = tkhd[0];
33623       index = version === 0 ? 12 : 20;
33624       id = toUnsigned$2(tkhd[index] << 24 | tkhd[index + 1] << 16 | tkhd[index + 2] << 8 | tkhd[index + 3]);
33625       mdhd = mp4Inspector.findBox(trak, ['mdia', 'mdhd'])[0];
33626 
33627       if (!mdhd) {
33628         return null;
33629       }
33630 
33631       version = mdhd[0];
33632       index = version === 0 ? 12 : 20;
33633       result[id] = toUnsigned$2(mdhd[index] << 24 | mdhd[index + 1] << 16 | mdhd[index + 2] << 8 | mdhd[index + 3]);
33634       return result;
33635     }, result);
33636   };
33637   /**
33638    * Determine the base media decode start time, in seconds, for an MP4
33639    * fragment. If multiple fragments are specified, the earliest time is
33640    * returned.
33641    *
33642    * The base media decode time can be parsed from track fragment
33643    * metadata:
33644    * ```
33645    * moof > traf > tfdt.baseMediaDecodeTime
33646    * ```
33647    * It requires the timescale value from the mdhd to interpret.
33648    *
33649    * @param timescale {object} a hash of track ids to timescale values.
33650    * @return {number} the earliest base media decode start time for the
33651    * fragment, in seconds
33652    */
33653 
33654 
33655   startTime = function startTime(timescale, fragment) {
33656     var trafs, baseTimes, result; // we need info from two childrend of each track fragment box
33657 
33658     trafs = mp4Inspector.findBox(fragment, ['moof', 'traf']); // determine the start times for each track
33659 
33660     baseTimes = [].concat.apply([], trafs.map(function (traf) {
33661       return mp4Inspector.findBox(traf, ['tfhd']).map(function (tfhd) {
33662         var id, scale, baseTime; // get the track id from the tfhd
33663 
33664         id = toUnsigned$2(tfhd[4] << 24 | tfhd[5] << 16 | tfhd[6] << 8 | tfhd[7]); // assume a 90kHz clock if no timescale was specified
33665 
33666         scale = timescale[id] || 90e3; // get the base media decode time from the tfdt
33667 
33668         baseTime = mp4Inspector.findBox(traf, ['tfdt']).map(function (tfdt) {
33669           var version, result;
33670           version = tfdt[0];
33671           result = toUnsigned$2(tfdt[4] << 24 | tfdt[5] << 16 | tfdt[6] << 8 | tfdt[7]);
33672 
33673           if (version === 1) {
33674             result *= Math.pow(2, 32);
33675             result += toUnsigned$2(tfdt[8] << 24 | tfdt[9] << 16 | tfdt[10] << 8 | tfdt[11]);
33676           }
33677 
33678           return result;
33679         })[0];
33680         baseTime = baseTime || Infinity; // convert base time to seconds
33681 
33682         return baseTime / scale;
33683       });
33684     })); // return the minimum
33685 
33686     result = Math.min.apply(null, baseTimes);
33687     return isFinite(result) ? result : 0;
33688   };
33689   /**
33690    * Determine the composition start, in seconds, for an MP4
33691    * fragment.
33692    *
33693    * The composition start time of a fragment can be calculated using the base
33694    * media decode time, composition time offset, and timescale, as follows:
33695    *
33696    * compositionStartTime = (baseMediaDecodeTime + compositionTimeOffset) / timescale
33697    *
33698    * All of the aforementioned information is contained within a media fragment's
33699    * `traf` box, except for timescale info, which comes from the initialization
33700    * segment, so a track id (also contained within a `traf`) is also necessary to
33701    * associate it with a timescale
33702    *
33703    *
33704    * @param timescales {object} - a hash of track ids to timescale values.
33705    * @param fragment {Unit8Array} - the bytes of a media segment
33706    * @return {number} the composition start time for the fragment, in seconds
33707    **/
33708 
33709 
33710   compositionStartTime = function compositionStartTime(timescales, fragment) {
33711     var trafBoxes = mp4Inspector.findBox(fragment, ['moof', 'traf']);
33712     var baseMediaDecodeTime = 0;
33713     var compositionTimeOffset = 0;
33714     var trackId;
33715 
33716     if (trafBoxes && trafBoxes.length) {
33717       // The spec states that track run samples contained within a `traf` box are contiguous, but
33718       // it does not explicitly state whether the `traf` boxes themselves are contiguous.
33719       // We will assume that they are, so we only need the first to calculate start time.
33720       var parsedTraf = mp4Inspector.parseTraf(trafBoxes[0]);
33721 
33722       for (var i = 0; i < parsedTraf.boxes.length; i++) {
33723         if (parsedTraf.boxes[i].type === 'tfhd') {
33724           trackId = parsedTraf.boxes[i].trackId;
33725         } else if (parsedTraf.boxes[i].type === 'tfdt') {
33726           baseMediaDecodeTime = parsedTraf.boxes[i].baseMediaDecodeTime;
33727         } else if (parsedTraf.boxes[i].type === 'trun' && parsedTraf.boxes[i].samples.length) {
33728           compositionTimeOffset = parsedTraf.boxes[i].samples[0].compositionTimeOffset || 0;
33729         }
33730       }
33731     } // Get timescale for this specific track. Assume a 90kHz clock if no timescale was
33732     // specified.
33733 
33734 
33735     var timescale = timescales[trackId] || 90e3; // return the composition start time, in seconds
33736 
33737     return (baseMediaDecodeTime + compositionTimeOffset) / timescale;
33738   };
33739   /**
33740     * Find the trackIds of the video tracks in this source.
33741     * Found by parsing the Handler Reference and Track Header Boxes:
33742     *   moov > trak > mdia > hdlr
33743     *   moov > trak > tkhd
33744     *
33745     * @param {Uint8Array} init - The bytes of the init segment for this source
33746     * @return {Number[]} A list of trackIds
33747     *
33748     * @see ISO-BMFF-12/2015, Section 8.4.3
33749    **/
33750 
33751 
33752   getVideoTrackIds = function getVideoTrackIds(init) {
33753     var traks = mp4Inspector.findBox(init, ['moov', 'trak']);
33754     var videoTrackIds = [];
33755     traks.forEach(function (trak) {
33756       var hdlrs = mp4Inspector.findBox(trak, ['mdia', 'hdlr']);
33757       var tkhds = mp4Inspector.findBox(trak, ['tkhd']);
vendor: 8,593 bytes, lines 33758-34019
33758       hdlrs.forEach(function (hdlr, index) {
33759         var handlerType = mp4Inspector.parseType(hdlr.subarray(8, 12));
33760         var tkhd = tkhds[index];
33761         var view;
33762         var version;
33763         var trackId;
33764 
33765         if (handlerType === 'vide') {
33766           view = new DataView(tkhd.buffer, tkhd.byteOffset, tkhd.byteLength);
33767           version = view.getUint8(0);
33768           trackId = version === 0 ? view.getUint32(12) : view.getUint32(20);
33769           videoTrackIds.push(trackId);
33770         }
33771       });
33772     });
33773     return videoTrackIds;
33774   };
33775   /**
33776    * Get all the video, audio, and hint tracks from a non fragmented
33777    * mp4 segment
33778    */
33779 
33780 
33781   getTracks = function getTracks(init) {
33782     var traks = mp4Inspector.findBox(init, ['moov', 'trak']);
33783     var tracks = [];
33784     traks.forEach(function (trak) {
33785       var track = {};
33786       var tkhd = mp4Inspector.findBox(trak, ['tkhd'])[0];
33787       var view, version; // id
33788 
33789       if (tkhd) {
33790         view = new DataView(tkhd.buffer, tkhd.byteOffset, tkhd.byteLength);
33791         version = view.getUint8(0);
33792         track.id = version === 0 ? view.getUint32(12) : view.getUint32(20);
33793       }
33794 
33795       var hdlr = mp4Inspector.findBox(trak, ['mdia', 'hdlr'])[0]; // type
33796 
33797       if (hdlr) {
33798         var type = mp4Inspector.parseType(hdlr.subarray(8, 12));
33799 
33800         if (type === 'vide') {
33801           track.type = 'video';
33802         } else if (type === 'soun') {
33803           track.type = 'audio';
33804         } else {
33805           track.type = type;
33806         }
33807       } // codec
33808 
33809 
33810       var stsd = mp4Inspector.findBox(trak, ['mdia', 'minf', 'stbl', 'stsd'])[0];
33811 
33812       if (stsd) {
33813         var sampleDescriptions = stsd.subarray(8); // gives the codec type string
33814 
33815         track.codec = mp4Inspector.parseType(sampleDescriptions.subarray(4, 8));
33816         var codecBox = mp4Inspector.findBox(sampleDescriptions, [track.codec])[0];
33817         var codecConfig, codecConfigType;
33818 
33819         if (codecBox) {
33820           // https://tools.ietf.org/html/rfc6381#section-3.3
33821           if (/^[a-z]vc[1-9]$/i.test(track.codec)) {
33822             // we don't need anything but the "config" parameter of the
33823             // avc1 codecBox
33824             codecConfig = codecBox.subarray(78);
33825             codecConfigType = mp4Inspector.parseType(codecConfig.subarray(4, 8));
33826 
33827             if (codecConfigType === 'avcC' && codecConfig.length > 11) {
33828               track.codec += '.'; // left padded with zeroes for single digit hex
33829               // profile idc
33830 
33831               track.codec += toHexString$1(codecConfig[9]); // the byte containing the constraint_set flags
33832 
33833               track.codec += toHexString$1(codecConfig[10]); // level idc
33834 
33835               track.codec += toHexString$1(codecConfig[11]);
33836             } else {
33837               // TODO: show a warning that we couldn't parse the codec
33838               // and are using the default
33839               track.codec = 'avc1.4d400d';
33840             }
33841           } else if (/^mp4[a,v]$/i.test(track.codec)) {
33842             // we do not need anything but the streamDescriptor of the mp4a codecBox
33843             codecConfig = codecBox.subarray(28);
33844             codecConfigType = mp4Inspector.parseType(codecConfig.subarray(4, 8));
33845 
33846             if (codecConfigType === 'esds' && codecConfig.length > 20 && codecConfig[19] !== 0) {
33847               track.codec += '.' + toHexString$1(codecConfig[19]); // this value is only a single digit
33848 
33849               track.codec += '.' + toHexString$1(codecConfig[20] >>> 2 & 0x3f).replace(/^0/, '');
33850             } else {
33851               // TODO: show a warning that we couldn't parse the codec
33852               // and are using the default
33853               track.codec = 'mp4a.40.2';
33854             }
33855           }
33856         }
33857       }
33858 
33859       var mdhd = mp4Inspector.findBox(trak, ['mdia', 'mdhd'])[0];
33860 
33861       if (mdhd && tkhd) {
33862         var index = version === 0 ? 12 : 20;
33863         track.timescale = toUnsigned$2(mdhd[index] << 24 | mdhd[index + 1] << 16 | mdhd[index + 2] << 8 | mdhd[index + 3]);
33864       }
33865 
33866       tracks.push(track);
33867     });
33868     return tracks;
33869   };
33870 
33871   var probe = {
33872     // export mp4 inspector's findBox and parseType for backwards compatibility
33873     findBox: mp4Inspector.findBox,
33874     parseType: mp4Inspector.parseType,
33875     timescale: timescale,
33876     startTime: startTime,
33877     compositionStartTime: compositionStartTime,
33878     videoTrackIds: getVideoTrackIds,
33879     tracks: getTracks
33880   };
33881 
33882   /**
33883    * mux.js
33884    *
33885    * Copyright (c) Brightcove
33886    * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
33887    *
33888    * Reads in-band caption information from a video elementary
33889    * stream. Captions must follow the CEA-708 standard for injection
33890    * into an MPEG-2 transport streams.
33891    * @see https://en.wikipedia.org/wiki/CEA-708
33892    * @see https://www.gpo.gov/fdsys/pkg/CFR-2007-title47-vol1/pdf/CFR-2007-title47-vol1-sec15-119.pdf
33893    */
33894   // payload type field to indicate how they are to be
33895   // interpreted. CEAS-708 caption content is always transmitted with
33896   // payload type 0x04.
33897 
33898   var USER_DATA_REGISTERED_ITU_T_T35 = 4,
33899       RBSP_TRAILING_BITS = 128;
33900   /**
33901     * Parse a supplemental enhancement information (SEI) NAL unit.
33902     * Stops parsing once a message of type ITU T T35 has been found.
33903     *
33904     * @param bytes {Uint8Array} the bytes of a SEI NAL unit
33905     * @return {object} the parsed SEI payload
33906     * @see Rec. ITU-T H.264, 7.3.2.3.1
33907     */
33908 
33909   var parseSei = function parseSei(bytes) {
33910     var i = 0,
33911         result = {
33912       payloadType: -1,
33913       payloadSize: 0
33914     },
33915         payloadType = 0,
33916         payloadSize = 0; // go through the sei_rbsp parsing each each individual sei_message
33917 
33918     while (i < bytes.byteLength) {
33919       // stop once we have hit the end of the sei_rbsp
33920       if (bytes[i] === RBSP_TRAILING_BITS) {
33921         break;
33922       } // Parse payload type
33923 
33924 
33925       while (bytes[i] === 0xFF) {
33926         payloadType += 255;
33927         i++;
33928       }
33929 
33930       payloadType += bytes[i++]; // Parse payload size
33931 
33932       while (bytes[i] === 0xFF) {
33933         payloadSize += 255;
33934         i++;
33935       }
33936 
33937       payloadSize += bytes[i++]; // this sei_message is a 608/708 caption so save it and break
33938       // there can only ever be one caption message in a frame's sei
33939 
33940       if (!result.payload && payloadType === USER_DATA_REGISTERED_ITU_T_T35) {
33941         result.payloadType = payloadType;
33942         result.payloadSize = payloadSize;
33943         result.payload = bytes.subarray(i, i + payloadSize);
33944         break;
33945       } // skip the payload and parse the next message
33946 
33947 
33948       i += payloadSize;
33949       payloadType = 0;
33950       payloadSize = 0;
33951     }
33952 
33953     return result;
33954   }; // see ANSI/SCTE 128-1 (2013), section 8.1
33955 
33956 
33957   var parseUserData = function parseUserData(sei) {
33958     // itu_t_t35_contry_code must be 181 (United States) for
33959     // captions
33960     if (sei.payload[0] !== 181) {
33961       return null;
33962     } // itu_t_t35_provider_code should be 49 (ATSC) for captions
33963 
33964 
33965     if ((sei.payload[1] << 8 | sei.payload[2]) !== 49) {
33966       return null;
33967     } // the user_identifier should be "GA94" to indicate ATSC1 data
33968 
33969 
33970     if (String.fromCharCode(sei.payload[3], sei.payload[4], sei.payload[5], sei.payload[6]) !== 'GA94') {
33971       return null;
33972     } // finally, user_data_type_code should be 0x03 for caption data
33973 
33974 
33975     if (sei.payload[7] !== 0x03) {
33976       return null;
33977     } // return the user_data_type_structure and strip the trailing
33978     // marker bits
33979 
33980 
33981     return sei.payload.subarray(8, sei.payload.length - 1);
33982   }; // see CEA-708-D, section 4.4
33983 
33984 
33985   var parseCaptionPackets = function parseCaptionPackets(pts, userData) {
33986     var results = [],
33987         i,
33988         count,
33989         offset,
33990         data; // if this is just filler, return immediately
33991 
33992     if (!(userData[0] & 0x40)) {
33993       return results;
33994     } // parse out the cc_data_1 and cc_data_2 fields
33995 
33996 
33997     count = userData[0] & 0x1f;
33998 
33999     for (i = 0; i < count; i++) {
34000       offset = i * 3;
34001       data = {
34002         type: userData[offset + 2] & 0x03,
34003         pts: pts
34004       }; // capture cc data when cc_valid is 1
34005 
34006       if (userData[offset + 2] & 0x04) {
34007         data.ccData = userData[offset + 3] << 8 | userData[offset + 4];
34008         results.push(data);
34009       }
34010     }
34011 
34012     return results;
34013   };
34014 
34015   var discardEmulationPreventionBytes = function discardEmulationPreventionBytes(data) {
34016     var length = data.byteLength,
34017         emulationPreventionBytesPositions = [],
34018         i = 1,
34019         newLength,
vendor: 8,481 bytes, lines 34020-34279
34020         newData; // Find all `Emulation Prevention Bytes`
34021 
34022     while (i < length - 2) {
34023       if (data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 0x03) {
34024         emulationPreventionBytesPositions.push(i + 2);
34025         i += 2;
34026       } else {
34027         i++;
34028       }
34029     } // If no Emulation Prevention Bytes were found just return the original
34030     // array
34031 
34032 
34033     if (emulationPreventionBytesPositions.length === 0) {
34034       return data;
34035     } // Create a new array to hold the NAL unit data
34036 
34037 
34038     newLength = length - emulationPreventionBytesPositions.length;
34039     newData = new Uint8Array(newLength);
34040     var sourceIndex = 0;
34041 
34042     for (i = 0; i < newLength; sourceIndex++, i++) {
34043       if (sourceIndex === emulationPreventionBytesPositions[0]) {
34044         // Skip this byte
34045         sourceIndex++; // Remove this position index
34046 
34047         emulationPreventionBytesPositions.shift();
34048       }
34049 
34050       newData[i] = data[sourceIndex];
34051     }
34052 
34053     return newData;
34054   }; // exports
34055 
34056 
34057   var captionPacketParser = {
34058     parseSei: parseSei,
34059     parseUserData: parseUserData,
34060     parseCaptionPackets: parseCaptionPackets,
34061     discardEmulationPreventionBytes: discardEmulationPreventionBytes,
34062     USER_DATA_REGISTERED_ITU_T_T35: USER_DATA_REGISTERED_ITU_T_T35
34063   };
34064 
34065   /**
34066    * mux.js
34067    *
34068    * Copyright (c) Brightcove
34069    * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
34070    *
34071    * A lightweight readable stream implemention that handles event dispatching.
34072    * Objects that inherit from streams should call init in their constructors.
34073    */
34074 
34075   var Stream$1 = function Stream() {
34076     this.init = function () {
34077       var listeners = {};
34078       /**
34079        * Add a listener for a specified event type.
34080        * @param type {string} the event name
34081        * @param listener {function} the callback to be invoked when an event of
34082        * the specified type occurs
34083        */
34084 
34085       this.on = function (type, listener) {
34086         if (!listeners[type]) {
34087           listeners[type] = [];
34088         }
34089 
34090         listeners[type] = listeners[type].concat(listener);
34091       };
34092       /**
34093        * Remove a listener for a specified event type.
34094        * @param type {string} the event name
34095        * @param listener {function} a function previously registered for this
34096        * type of event through `on`
34097        */
34098 
34099 
34100       this.off = function (type, listener) {
34101         var index;
34102 
34103         if (!listeners[type]) {
34104           return false;
34105         }
34106 
34107         index = listeners[type].indexOf(listener);
34108         listeners[type] = listeners[type].slice();
34109         listeners[type].splice(index, 1);
34110         return index > -1;
34111       };
34112       /**
34113        * Trigger an event of the specified type on this stream. Any additional
34114        * arguments to this function are passed as parameters to event listeners.
34115        * @param type {string} the event name
34116        */
34117 
34118 
34119       this.trigger = function (type) {
34120         var callbacks, i, length, args;
34121         callbacks = listeners[type];
34122 
34123         if (!callbacks) {
34124           return;
34125         } // Slicing the arguments on every invocation of this method
34126         // can add a significant amount of overhead. Avoid the
34127         // intermediate object creation for the common case of a
34128         // single callback argument
34129 
34130 
34131         if (arguments.length === 2) {
34132           length = callbacks.length;
34133 
34134           for (i = 0; i < length; ++i) {
34135             callbacks[i].call(this, arguments[1]);
34136           }
34137         } else {
34138           args = [];
34139           i = arguments.length;
34140 
34141           for (i = 1; i < arguments.length; ++i) {
34142             args.push(arguments[i]);
34143           }
34144 
34145           length = callbacks.length;
34146 
34147           for (i = 0; i < length; ++i) {
34148             callbacks[i].apply(this, args);
34149           }
34150         }
34151       };
34152       /**
34153        * Destroys the stream and cleans up.
34154        */
34155 
34156 
34157       this.dispose = function () {
34158         listeners = {};
34159       };
34160     };
34161   };
34162   /**
34163    * Forwards all `data` events on this stream to the destination stream. The
34164    * destination stream should provide a method `push` to receive the data
34165    * events as they arrive.
34166    * @param destination {stream} the stream that will receive all `data` events
34167    * @param autoFlush {boolean} if false, we will not call `flush` on the destination
34168    *                            when the current stream emits a 'done' event
34169    * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
34170    */
34171 
34172 
34173   Stream$1.prototype.pipe = function (destination) {
34174     this.on('data', function (data) {
34175       destination.push(data);
34176     });
34177     this.on('done', function (flushSource) {
34178       destination.flush(flushSource);
34179     });
34180     this.on('partialdone', function (flushSource) {
34181       destination.partialFlush(flushSource);
34182     });
34183     this.on('endedtimeline', function (flushSource) {
34184       destination.endTimeline(flushSource);
34185     });
34186     this.on('reset', function (flushSource) {
34187       destination.reset(flushSource);
34188     });
34189     return destination;
34190   }; // Default stream functions that are expected to be overridden to perform
34191   // actual work. These are provided by the prototype as a sort of no-op
34192   // implementation so that we don't have to check for their existence in the
34193   // `pipe` function above.
34194 
34195 
34196   Stream$1.prototype.push = function (data) {
34197     this.trigger('data', data);
34198   };
34199 
34200   Stream$1.prototype.flush = function (flushSource) {
34201     this.trigger('done', flushSource);
34202   };
34203 
34204   Stream$1.prototype.partialFlush = function (flushSource) {
34205     this.trigger('partialdone', flushSource);
34206   };
34207 
34208   Stream$1.prototype.endTimeline = function (flushSource) {
34209     this.trigger('endedtimeline', flushSource);
34210   };
34211 
34212   Stream$1.prototype.reset = function (flushSource) {
34213     this.trigger('reset', flushSource);
34214   };
34215 
34216   var stream = Stream$1;
34217 
34218   // Link To Transport
34219   // -----------------
34220 
34221 
34222   var CaptionStream = function CaptionStream() {
34223     CaptionStream.prototype.init.call(this);
34224     this.captionPackets_ = [];
34225     this.ccStreams_ = [new Cea608Stream(0, 0), // eslint-disable-line no-use-before-define
34226     new Cea608Stream(0, 1), // eslint-disable-line no-use-before-define
34227     new Cea608Stream(1, 0), // eslint-disable-line no-use-before-define
34228     new Cea608Stream(1, 1) // eslint-disable-line no-use-before-define
34229     ];
34230     this.reset(); // forward data and done events from CCs to this CaptionStream
34231 
34232     this.ccStreams_.forEach(function (cc) {
34233       cc.on('data', this.trigger.bind(this, 'data'));
34234       cc.on('partialdone', this.trigger.bind(this, 'partialdone'));
34235       cc.on('done', this.trigger.bind(this, 'done'));
34236     }, this);
34237   };
34238 
34239   CaptionStream.prototype = new stream();
34240 
34241   CaptionStream.prototype.push = function (event) {
34242     var sei, userData, newCaptionPackets; // only examine SEI NALs
34243 
34244     if (event.nalUnitType !== 'sei_rbsp') {
34245       return;
34246     } // parse the sei
34247 
34248 
34249     sei = captionPacketParser.parseSei(event.escapedRBSP); // ignore everything but user_data_registered_itu_t_t35
34250 
34251     if (sei.payloadType !== captionPacketParser.USER_DATA_REGISTERED_ITU_T_T35) {
34252       return;
34253     } // parse out the user data payload
34254 
34255 
34256     userData = captionPacketParser.parseUserData(sei); // ignore unrecognized userData
34257 
34258     if (!userData) {
34259       return;
34260     } // Sometimes, the same segment # will be downloaded twice. To stop the
34261     // caption data from being processed twice, we track the latest dts we've
34262     // received and ignore everything with a dts before that. However, since
34263     // data for a specific dts can be split across packets on either side of
34264     // a segment boundary, we need to make sure we *don't* ignore the packets
34265     // from the *next* segment that have dts === this.latestDts_. By constantly
34266     // tracking the number of packets received with dts === this.latestDts_, we
34267     // know how many should be ignored once we start receiving duplicates.
34268 
34269 
34270     if (event.dts < this.latestDts_) {
34271       // We've started getting older data, so set the flag.
34272       this.ignoreNextEqualDts_ = true;
34273       return;
34274     } else if (event.dts === this.latestDts_ && this.ignoreNextEqualDts_) {
34275       this.numSameDts_--;
34276 
34277       if (!this.numSameDts_) {
34278         // We've received the last duplicate packet, time to start processing again
34279         this.ignoreNextEqualDts_ = false;
vendor: 6,649 bytes, lines 34280-34526
34280       }
34281 
34282       return;
34283     } // parse out CC data packets and save them for later
34284 
34285 
34286     newCaptionPackets = captionPacketParser.parseCaptionPackets(event.pts, userData);
34287     this.captionPackets_ = this.captionPackets_.concat(newCaptionPackets);
34288 
34289     if (this.latestDts_ !== event.dts) {
34290       this.numSameDts_ = 0;
34291     }
34292 
34293     this.numSameDts_++;
34294     this.latestDts_ = event.dts;
34295   };
34296 
34297   CaptionStream.prototype.flushCCStreams = function (flushType) {
34298     this.ccStreams_.forEach(function (cc) {
34299       return flushType === 'flush' ? cc.flush() : cc.partialFlush();
34300     }, this);
34301   };
34302 
34303   CaptionStream.prototype.flushStream = function (flushType) {
34304     // make sure we actually parsed captions before proceeding
34305     if (!this.captionPackets_.length) {
34306       this.flushCCStreams(flushType);
34307       return;
34308     } // In Chrome, the Array#sort function is not stable so add a
34309     // presortIndex that we can use to ensure we get a stable-sort
34310 
34311 
34312     this.captionPackets_.forEach(function (elem, idx) {
34313       elem.presortIndex = idx;
34314     }); // sort caption byte-pairs based on their PTS values
34315 
34316     this.captionPackets_.sort(function (a, b) {
34317       if (a.pts === b.pts) {
34318         return a.presortIndex - b.presortIndex;
34319       }
34320 
34321       return a.pts - b.pts;
34322     });
34323     this.captionPackets_.forEach(function (packet) {
34324       if (packet.type < 2) {
34325         // Dispatch packet to the right Cea608Stream
34326         this.dispatchCea608Packet(packet);
34327       } // this is where an 'else' would go for a dispatching packets
34328       // to a theoretical Cea708Stream that handles SERVICEn data
34329 
34330     }, this);
34331     this.captionPackets_.length = 0;
34332     this.flushCCStreams(flushType);
34333   };
34334 
34335   CaptionStream.prototype.flush = function () {
34336     return this.flushStream('flush');
34337   }; // Only called if handling partial data
34338 
34339 
34340   CaptionStream.prototype.partialFlush = function () {
34341     return this.flushStream('partialFlush');
34342   };
34343 
34344   CaptionStream.prototype.reset = function () {
34345     this.latestDts_ = null;
34346     this.ignoreNextEqualDts_ = false;
34347     this.numSameDts_ = 0;
34348     this.activeCea608Channel_ = [null, null];
34349     this.ccStreams_.forEach(function (ccStream) {
34350       ccStream.reset();
34351     });
34352   }; // From the CEA-608 spec:
34353 
34354   /*
34355    * When XDS sub-packets are interleaved with other services, the end of each sub-packet shall be followed
34356    * by a control pair to change to a different service. When any of the control codes from 0x10 to 0x1F is
34357    * used to begin a control code pair, it indicates the return to captioning or Text data. The control code pair
34358    * and subsequent data should then be processed according to the FCC rules. It may be necessary for the
34359    * line 21 data encoder to automatically insert a control code pair (i.e. RCL, RU2, RU3, RU4, RDC, or RTD)
34360    * to switch to captioning or Text.
34361   */
34362   // With that in mind, we ignore any data between an XDS control code and a
34363   // subsequent closed-captioning control code.
34364 
34365 
34366   CaptionStream.prototype.dispatchCea608Packet = function (packet) {
34367     // NOTE: packet.type is the CEA608 field
34368     if (this.setsTextOrXDSActive(packet)) {
34369       this.activeCea608Channel_[packet.type] = null;
34370     } else if (this.setsChannel1Active(packet)) {
34371       this.activeCea608Channel_[packet.type] = 0;
34372     } else if (this.setsChannel2Active(packet)) {
34373       this.activeCea608Channel_[packet.type] = 1;
34374     }
34375 
34376     if (this.activeCea608Channel_[packet.type] === null) {
34377       // If we haven't received anything to set the active channel, or the
34378       // packets are Text/XDS data, discard the data; we don't want jumbled
34379       // captions
34380       return;
34381     }
34382 
34383     this.ccStreams_[(packet.type << 1) + this.activeCea608Channel_[packet.type]].push(packet);
34384   };
34385 
34386   CaptionStream.prototype.setsChannel1Active = function (packet) {
34387     return (packet.ccData & 0x7800) === 0x1000;
34388   };
34389 
34390   CaptionStream.prototype.setsChannel2Active = function (packet) {
34391     return (packet.ccData & 0x7800) === 0x1800;
34392   };
34393 
34394   CaptionStream.prototype.setsTextOrXDSActive = function (packet) {
34395     return (packet.ccData & 0x7100) === 0x0100 || (packet.ccData & 0x78fe) === 0x102a || (packet.ccData & 0x78fe) === 0x182a;
34396   }; // ----------------------
34397   // Session to Application
34398   // ----------------------
34399   // This hash maps non-ASCII, special, and extended character codes to their
34400   // proper Unicode equivalent. The first keys that are only a single byte
34401   // are the non-standard ASCII characters, which simply map the CEA608 byte
34402   // to the standard ASCII/Unicode. The two-byte keys that follow are the CEA608
34403   // character codes, but have their MSB bitmasked with 0x03 so that a lookup
34404   // can be performed regardless of the field and data channel on which the
34405   // character code was received.
34406 
34407 
34408   var CHARACTER_TRANSLATION = {
34409     0x2a: 0xe1,
34410     // á
34411     0x5c: 0xe9,
34412     // é
34413     0x5e: 0xed,
34414     // í
34415     0x5f: 0xf3,
34416     // ó
34417     0x60: 0xfa,
34418     // ú
34419     0x7b: 0xe7,
34420     // ç
34421     0x7c: 0xf7,
34422     // ÷
34423     0x7d: 0xd1,
34424     // Ñ
34425     0x7e: 0xf1,
34426     // ñ
34427     0x7f: 0x2588,
34428     // █
34429     0x0130: 0xae,
34430     // ®
34431     0x0131: 0xb0,
34432     // °
34433     0x0132: 0xbd,
34434     // ½
34435     0x0133: 0xbf,
34436     // ¿
34437     0x0134: 0x2122,
34438     // ™
34439     0x0135: 0xa2,
34440     // ¢
34441     0x0136: 0xa3,
34442     // £
34443     0x0137: 0x266a,
34444     // ♪
34445     0x0138: 0xe0,
34446     // à
34447     0x0139: 0xa0,
34448     //
34449     0x013a: 0xe8,
34450     // è
34451     0x013b: 0xe2,
34452     // â
34453     0x013c: 0xea,
34454     // ê
34455     0x013d: 0xee,
34456     // î
34457     0x013e: 0xf4,
34458     // ô
34459     0x013f: 0xfb,
34460     // û
34461     0x0220: 0xc1,
34462     // Á
34463     0x0221: 0xc9,
34464     // É
34465     0x0222: 0xd3,
34466     // Ó
34467     0x0223: 0xda,
34468     // Ú
34469     0x0224: 0xdc,
34470     // Ü
34471     0x0225: 0xfc,
34472     // ü
34473     0x0226: 0x2018,
34474     // ‘
34475     0x0227: 0xa1,
34476     // ¡
34477     0x0228: 0x2a,
34478     // *
34479     0x0229: 0x27,
34480     // '
34481     0x022a: 0x2014,
34482     // —
34483     0x022b: 0xa9,
34484     // ©
34485     0x022c: 0x2120,
34486     // ℠
34487     0x022d: 0x2022,
34488     // •
34489     0x022e: 0x201c,
34490     // “
34491     0x022f: 0x201d,
34492     // ”
34493     0x0230: 0xc0,
34494     // À
34495     0x0231: 0xc2,
34496     // Â
34497     0x0232: 0xc7,
34498     // Ç
34499     0x0233: 0xc8,
34500     // È
34501     0x0234: 0xca,
34502     // Ê
34503     0x0235: 0xcb,
34504     // Ë
34505     0x0236: 0xeb,
34506     // ë
34507     0x0237: 0xce,
34508     // Î
34509     0x0238: 0xcf,
34510     // Ï
34511     0x0239: 0xef,
34512     // ï
34513     0x023a: 0xd4,
34514     // Ô
34515     0x023b: 0xd9,
34516     // Ù
34517     0x023c: 0xf9,
34518     // ù
34519     0x023d: 0xdb,
34520     // Û
34521     0x023e: 0xab,
34522     // «
34523     0x023f: 0xbb,
34524     // »
34525     0x0320: 0xc3,
34526     // Ã
vendor: 18,966 bytes, lines 34527-35059
34527     0x0321: 0xe3,
34528     // ã
34529     0x0322: 0xcd,
34530     // Í
34531     0x0323: 0xcc,
34532     // Ì
34533     0x0324: 0xec,
34534     // ì
34535     0x0325: 0xd2,
34536     // Ò
34537     0x0326: 0xf2,
34538     // ò
34539     0x0327: 0xd5,
34540     // Õ
34541     0x0328: 0xf5,
34542     // õ
34543     0x0329: 0x7b,
34544     // {
34545     0x032a: 0x7d,
34546     // }
34547     0x032b: 0x5c,
34548     // \
34549     0x032c: 0x5e,
34550     // ^
34551     0x032d: 0x5f,
34552     // _
34553     0x032e: 0x7c,
34554     // |
34555     0x032f: 0x7e,
34556     // ~
34557     0x0330: 0xc4,
34558     // Ä
34559     0x0331: 0xe4,
34560     // ä
34561     0x0332: 0xd6,
34562     // Ö
34563     0x0333: 0xf6,
34564     // ö
34565     0x0334: 0xdf,
34566     // ß
34567     0x0335: 0xa5,
34568     // ¥
34569     0x0336: 0xa4,
34570     // ¤
34571     0x0337: 0x2502,
34572     // │
34573     0x0338: 0xc5,
34574     // Å
34575     0x0339: 0xe5,
34576     // å
34577     0x033a: 0xd8,
34578     // Ø
34579     0x033b: 0xf8,
34580     // ø
34581     0x033c: 0x250c,
34582     // ┌
34583     0x033d: 0x2510,
34584     // ┐
34585     0x033e: 0x2514,
34586     // └
34587     0x033f: 0x2518 // ┘
34588 
34589   };
34590 
34591   var getCharFromCode = function getCharFromCode(code) {
34592     if (code === null) {
34593       return '';
34594     }
34595 
34596     code = CHARACTER_TRANSLATION[code] || code;
34597     return String.fromCharCode(code);
34598   }; // the index of the last row in a CEA-608 display buffer
34599 
34600 
34601   var BOTTOM_ROW = 14; // This array is used for mapping PACs -> row #, since there's no way of
34602   // getting it through bit logic.
34603 
34604   var ROWS = [0x1100, 0x1120, 0x1200, 0x1220, 0x1500, 0x1520, 0x1600, 0x1620, 0x1700, 0x1720, 0x1000, 0x1300, 0x1320, 0x1400, 0x1420]; // CEA-608 captions are rendered onto a 34x15 matrix of character
34605   // cells. The "bottom" row is the last element in the outer array.
34606 
34607   var createDisplayBuffer = function createDisplayBuffer() {
34608     var result = [],
34609         i = BOTTOM_ROW + 1;
34610 
34611     while (i--) {
34612       result.push('');
34613     }
34614 
34615     return result;
34616   };
34617 
34618   var Cea608Stream = function Cea608Stream(field, dataChannel) {
34619     Cea608Stream.prototype.init.call(this);
34620     this.field_ = field || 0;
34621     this.dataChannel_ = dataChannel || 0;
34622     this.name_ = 'CC' + ((this.field_ << 1 | this.dataChannel_) + 1);
34623     this.setConstants();
34624     this.reset();
34625 
34626     this.push = function (packet) {
34627       var data, swap, char0, char1, text; // remove the parity bits
34628 
34629       data = packet.ccData & 0x7f7f; // ignore duplicate control codes; the spec demands they're sent twice
34630 
34631       if (data === this.lastControlCode_) {
34632         this.lastControlCode_ = null;
34633         return;
34634       } // Store control codes
34635 
34636 
34637       if ((data & 0xf000) === 0x1000) {
34638         this.lastControlCode_ = data;
34639       } else if (data !== this.PADDING_) {
34640         this.lastControlCode_ = null;
34641       }
34642 
34643       char0 = data >>> 8;
34644       char1 = data & 0xff;
34645 
34646       if (data === this.PADDING_) {
34647         return;
34648       } else if (data === this.RESUME_CAPTION_LOADING_) {
34649         this.mode_ = 'popOn';
34650       } else if (data === this.END_OF_CAPTION_) {
34651         // If an EOC is received while in paint-on mode, the displayed caption
34652         // text should be swapped to non-displayed memory as if it was a pop-on
34653         // caption. Because of that, we should explicitly switch back to pop-on
34654         // mode
34655         this.mode_ = 'popOn';
34656         this.clearFormatting(packet.pts); // if a caption was being displayed, it's gone now
34657 
34658         this.flushDisplayed(packet.pts); // flip memory
34659 
34660         swap = this.displayed_;
34661         this.displayed_ = this.nonDisplayed_;
34662         this.nonDisplayed_ = swap; // start measuring the time to display the caption
34663 
34664         this.startPts_ = packet.pts;
34665       } else if (data === this.ROLL_UP_2_ROWS_) {
34666         this.rollUpRows_ = 2;
34667         this.setRollUp(packet.pts);
34668       } else if (data === this.ROLL_UP_3_ROWS_) {
34669         this.rollUpRows_ = 3;
34670         this.setRollUp(packet.pts);
34671       } else if (data === this.ROLL_UP_4_ROWS_) {
34672         this.rollUpRows_ = 4;
34673         this.setRollUp(packet.pts);
34674       } else if (data === this.CARRIAGE_RETURN_) {
34675         this.clearFormatting(packet.pts);
34676         this.flushDisplayed(packet.pts);
34677         this.shiftRowsUp_();
34678         this.startPts_ = packet.pts;
34679       } else if (data === this.BACKSPACE_) {
34680         if (this.mode_ === 'popOn') {
34681           this.nonDisplayed_[this.row_] = this.nonDisplayed_[this.row_].slice(0, -1);
34682         } else {
34683           this.displayed_[this.row_] = this.displayed_[this.row_].slice(0, -1);
34684         }
34685       } else if (data === this.ERASE_DISPLAYED_MEMORY_) {
34686         this.flushDisplayed(packet.pts);
34687         this.displayed_ = createDisplayBuffer();
34688       } else if (data === this.ERASE_NON_DISPLAYED_MEMORY_) {
34689         this.nonDisplayed_ = createDisplayBuffer();
34690       } else if (data === this.RESUME_DIRECT_CAPTIONING_) {
34691         if (this.mode_ !== 'paintOn') {
34692           // NOTE: This should be removed when proper caption positioning is
34693           // implemented
34694           this.flushDisplayed(packet.pts);
34695           this.displayed_ = createDisplayBuffer();
34696         }
34697 
34698         this.mode_ = 'paintOn';
34699         this.startPts_ = packet.pts; // Append special characters to caption text
34700       } else if (this.isSpecialCharacter(char0, char1)) {
34701         // Bitmask char0 so that we can apply character transformations
34702         // regardless of field and data channel.
34703         // Then byte-shift to the left and OR with char1 so we can pass the
34704         // entire character code to `getCharFromCode`.
34705         char0 = (char0 & 0x03) << 8;
34706         text = getCharFromCode(char0 | char1);
34707         this[this.mode_](packet.pts, text);
34708         this.column_++; // Append extended characters to caption text
34709       } else if (this.isExtCharacter(char0, char1)) {
34710         // Extended characters always follow their "non-extended" equivalents.
34711         // IE if a "è" is desired, you'll always receive "eè"; non-compliant
34712         // decoders are supposed to drop the "è", while compliant decoders
34713         // backspace the "e" and insert "è".
34714         // Delete the previous character
34715         if (this.mode_ === 'popOn') {
34716           this.nonDisplayed_[this.row_] = this.nonDisplayed_[this.row_].slice(0, -1);
34717         } else {
34718           this.displayed_[this.row_] = this.displayed_[this.row_].slice(0, -1);
34719         } // Bitmask char0 so that we can apply character transformations
34720         // regardless of field and data channel.
34721         // Then byte-shift to the left and OR with char1 so we can pass the
34722         // entire character code to `getCharFromCode`.
34723 
34724 
34725         char0 = (char0 & 0x03) << 8;
34726         text = getCharFromCode(char0 | char1);
34727         this[this.mode_](packet.pts, text);
34728         this.column_++; // Process mid-row codes
34729       } else if (this.isMidRowCode(char0, char1)) {
34730         // Attributes are not additive, so clear all formatting
34731         this.clearFormatting(packet.pts); // According to the standard, mid-row codes
34732         // should be replaced with spaces, so add one now
34733 
34734         this[this.mode_](packet.pts, ' ');
34735         this.column_++;
34736 
34737         if ((char1 & 0xe) === 0xe) {
34738           this.addFormatting(packet.pts, ['i']);
34739         }
34740 
34741         if ((char1 & 0x1) === 0x1) {
34742           this.addFormatting(packet.pts, ['u']);
34743         } // Detect offset control codes and adjust cursor
34744 
34745       } else if (this.isOffsetControlCode(char0, char1)) {
34746         // Cursor position is set by indent PAC (see below) in 4-column
34747         // increments, with an additional offset code of 1-3 to reach any
34748         // of the 32 columns specified by CEA-608. So all we need to do
34749         // here is increment the column cursor by the given offset.
34750         this.column_ += char1 & 0x03; // Detect PACs (Preamble Address Codes)
34751       } else if (this.isPAC(char0, char1)) {
34752         // There's no logic for PAC -> row mapping, so we have to just
34753         // find the row code in an array and use its index :(
34754         var row = ROWS.indexOf(data & 0x1f20); // Configure the caption window if we're in roll-up mode
34755 
34756         if (this.mode_ === 'rollUp') {
34757           // This implies that the base row is incorrectly set.
34758           // As per the recommendation in CEA-608(Base Row Implementation), defer to the number
34759           // of roll-up rows set.
34760           if (row - this.rollUpRows_ + 1 < 0) {
34761             row = this.rollUpRows_ - 1;
34762           }
34763 
34764           this.setRollUp(packet.pts, row);
34765         }
34766 
34767         if (row !== this.row_) {
34768           // formatting is only persistent for current row
34769           this.clearFormatting(packet.pts);
34770           this.row_ = row;
34771         } // All PACs can apply underline, so detect and apply
34772         // (All odd-numbered second bytes set underline)
34773 
34774 
34775         if (char1 & 0x1 && this.formatting_.indexOf('u') === -1) {
34776           this.addFormatting(packet.pts, ['u']);
34777         }
34778 
34779         if ((data & 0x10) === 0x10) {
34780           // We've got an indent level code. Each successive even number
34781           // increments the column cursor by 4, so we can get the desired
34782           // column position by bit-shifting to the right (to get n/2)
34783           // and multiplying by 4.
34784           this.column_ = ((data & 0xe) >> 1) * 4;
34785         }
34786 
34787         if (this.isColorPAC(char1)) {
34788           // it's a color code, though we only support white, which
34789           // can be either normal or italicized. white italics can be
34790           // either 0x4e or 0x6e depending on the row, so we just
34791           // bitwise-and with 0xe to see if italics should be turned on
34792           if ((char1 & 0xe) === 0xe) {
34793             this.addFormatting(packet.pts, ['i']);
34794           }
34795         } // We have a normal character in char0, and possibly one in char1
34796 
34797       } else if (this.isNormalChar(char0)) {
34798         if (char1 === 0x00) {
34799           char1 = null;
34800         }
34801 
34802         text = getCharFromCode(char0);
34803         text += getCharFromCode(char1);
34804         this[this.mode_](packet.pts, text);
34805         this.column_ += text.length;
34806       } // finish data processing
34807 
34808     };
34809   };
34810 
34811   Cea608Stream.prototype = new stream(); // Trigger a cue point that captures the current state of the
34812   // display buffer
34813 
34814   Cea608Stream.prototype.flushDisplayed = function (pts) {
34815     var content = this.displayed_ // remove spaces from the start and end of the string
34816     .map(function (row) {
34817       try {
34818         return row.trim();
34819       } catch (e) {
34820         // Ordinarily, this shouldn't happen. However, caption
34821         // parsing errors should not throw exceptions and
34822         // break playback.
34823         // eslint-disable-next-line no-console
34824         console.error('Skipping malformed caption.');
34825         return '';
34826       }
34827     }) // combine all text rows to display in one cue
34828     .join('\n') // and remove blank rows from the start and end, but not the middle
34829     .replace(/^\n+|\n+$/g, '');
34830 
34831     if (content.length) {
34832       this.trigger('data', {
34833         startPts: this.startPts_,
34834         endPts: pts,
34835         text: content,
34836         stream: this.name_
34837       });
34838     }
34839   };
34840   /**
34841    * Zero out the data, used for startup and on seek
34842    */
34843 
34844 
34845   Cea608Stream.prototype.reset = function () {
34846     this.mode_ = 'popOn'; // When in roll-up mode, the index of the last row that will
34847     // actually display captions. If a caption is shifted to a row
34848     // with a lower index than this, it is cleared from the display
34849     // buffer
34850 
34851     this.topRow_ = 0;
34852     this.startPts_ = 0;
34853     this.displayed_ = createDisplayBuffer();
34854     this.nonDisplayed_ = createDisplayBuffer();
34855     this.lastControlCode_ = null; // Track row and column for proper line-breaking and spacing
34856 
34857     this.column_ = 0;
34858     this.row_ = BOTTOM_ROW;
34859     this.rollUpRows_ = 2; // This variable holds currently-applied formatting
34860 
34861     this.formatting_ = [];
34862   };
34863   /**
34864    * Sets up control code and related constants for this instance
34865    */
34866 
34867 
34868   Cea608Stream.prototype.setConstants = function () {
34869     // The following attributes have these uses:
34870     // ext_ :    char0 for mid-row codes, and the base for extended
34871     //           chars (ext_+0, ext_+1, and ext_+2 are char0s for
34872     //           extended codes)
34873     // control_: char0 for control codes, except byte-shifted to the
34874     //           left so that we can do this.control_ | CONTROL_CODE
34875     // offset_:  char0 for tab offset codes
34876     //
34877     // It's also worth noting that control codes, and _only_ control codes,
34878     // differ between field 1 and field2. Field 2 control codes are always
34879     // their field 1 value plus 1. That's why there's the "| field" on the
34880     // control value.
34881     if (this.dataChannel_ === 0) {
34882       this.BASE_ = 0x10;
34883       this.EXT_ = 0x11;
34884       this.CONTROL_ = (0x14 | this.field_) << 8;
34885       this.OFFSET_ = 0x17;
34886     } else if (this.dataChannel_ === 1) {
34887       this.BASE_ = 0x18;
34888       this.EXT_ = 0x19;
34889       this.CONTROL_ = (0x1c | this.field_) << 8;
34890       this.OFFSET_ = 0x1f;
34891     } // Constants for the LSByte command codes recognized by Cea608Stream. This
34892     // list is not exhaustive. For a more comprehensive listing and semantics see
34893     // http://www.gpo.gov/fdsys/pkg/CFR-2010-title47-vol1/pdf/CFR-2010-title47-vol1-sec15-119.pdf
34894     // Padding
34895 
34896 
34897     this.PADDING_ = 0x0000; // Pop-on Mode
34898 
34899     this.RESUME_CAPTION_LOADING_ = this.CONTROL_ | 0x20;
34900     this.END_OF_CAPTION_ = this.CONTROL_ | 0x2f; // Roll-up Mode
34901 
34902     this.ROLL_UP_2_ROWS_ = this.CONTROL_ | 0x25;
34903     this.ROLL_UP_3_ROWS_ = this.CONTROL_ | 0x26;
34904     this.ROLL_UP_4_ROWS_ = this.CONTROL_ | 0x27;
34905     this.CARRIAGE_RETURN_ = this.CONTROL_ | 0x2d; // paint-on mode
34906 
34907     this.RESUME_DIRECT_CAPTIONING_ = this.CONTROL_ | 0x29; // Erasure
34908 
34909     this.BACKSPACE_ = this.CONTROL_ | 0x21;
34910     this.ERASE_DISPLAYED_MEMORY_ = this.CONTROL_ | 0x2c;
34911     this.ERASE_NON_DISPLAYED_MEMORY_ = this.CONTROL_ | 0x2e;
34912   };
34913   /**
34914    * Detects if the 2-byte packet data is a special character
34915    *
34916    * Special characters have a second byte in the range 0x30 to 0x3f,
34917    * with the first byte being 0x11 (for data channel 1) or 0x19 (for
34918    * data channel 2).
34919    *
34920    * @param  {Integer} char0 The first byte
34921    * @param  {Integer} char1 The second byte
34922    * @return {Boolean}       Whether the 2 bytes are an special character
34923    */
34924 
34925 
34926   Cea608Stream.prototype.isSpecialCharacter = function (char0, char1) {
34927     return char0 === this.EXT_ && char1 >= 0x30 && char1 <= 0x3f;
34928   };
34929   /**
34930    * Detects if the 2-byte packet data is an extended character
34931    *
34932    * Extended characters have a second byte in the range 0x20 to 0x3f,
34933    * with the first byte being 0x12 or 0x13 (for data channel 1) or
34934    * 0x1a or 0x1b (for data channel 2).
34935    *
34936    * @param  {Integer} char0 The first byte
34937    * @param  {Integer} char1 The second byte
34938    * @return {Boolean}       Whether the 2 bytes are an extended character
34939    */
34940 
34941 
34942   Cea608Stream.prototype.isExtCharacter = function (char0, char1) {
34943     return (char0 === this.EXT_ + 1 || char0 === this.EXT_ + 2) && char1 >= 0x20 && char1 <= 0x3f;
34944   };
34945   /**
34946    * Detects if the 2-byte packet is a mid-row code
34947    *
34948    * Mid-row codes have a second byte in the range 0x20 to 0x2f, with
34949    * the first byte being 0x11 (for data channel 1) or 0x19 (for data
34950    * channel 2).
34951    *
34952    * @param  {Integer} char0 The first byte
34953    * @param  {Integer} char1 The second byte
34954    * @return {Boolean}       Whether the 2 bytes are a mid-row code
34955    */
34956 
34957 
34958   Cea608Stream.prototype.isMidRowCode = function (char0, char1) {
34959     return char0 === this.EXT_ && char1 >= 0x20 && char1 <= 0x2f;
34960   };
34961   /**
34962    * Detects if the 2-byte packet is an offset control code
34963    *
34964    * Offset control codes have a second byte in the range 0x21 to 0x23,
34965    * with the first byte being 0x17 (for data channel 1) or 0x1f (for
34966    * data channel 2).
34967    *
34968    * @param  {Integer} char0 The first byte
34969    * @param  {Integer} char1 The second byte
34970    * @return {Boolean}       Whether the 2 bytes are an offset control code
34971    */
34972 
34973 
34974   Cea608Stream.prototype.isOffsetControlCode = function (char0, char1) {
34975     return char0 === this.OFFSET_ && char1 >= 0x21 && char1 <= 0x23;
34976   };
34977   /**
34978    * Detects if the 2-byte packet is a Preamble Address Code
34979    *
34980    * PACs have a first byte in the range 0x10 to 0x17 (for data channel 1)
34981    * or 0x18 to 0x1f (for data channel 2), with the second byte in the
34982    * range 0x40 to 0x7f.
34983    *
34984    * @param  {Integer} char0 The first byte
34985    * @param  {Integer} char1 The second byte
34986    * @return {Boolean}       Whether the 2 bytes are a PAC
34987    */
34988 
34989 
34990   Cea608Stream.prototype.isPAC = function (char0, char1) {
34991     return char0 >= this.BASE_ && char0 < this.BASE_ + 8 && char1 >= 0x40 && char1 <= 0x7f;
34992   };
34993   /**
34994    * Detects if a packet's second byte is in the range of a PAC color code
34995    *
34996    * PAC color codes have the second byte be in the range 0x40 to 0x4f, or
34997    * 0x60 to 0x6f.
34998    *
34999    * @param  {Integer} char1 The second byte
35000    * @return {Boolean}       Whether the byte is a color PAC
35001    */
35002 
35003 
35004   Cea608Stream.prototype.isColorPAC = function (char1) {
35005     return char1 >= 0x40 && char1 <= 0x4f || char1 >= 0x60 && char1 <= 0x7f;
35006   };
35007   /**
35008    * Detects if a single byte is in the range of a normal character
35009    *
35010    * Normal text bytes are in the range 0x20 to 0x7f.
35011    *
35012    * @param  {Integer} char  The byte
35013    * @return {Boolean}       Whether the byte is a normal character
35014    */
35015 
35016 
35017   Cea608Stream.prototype.isNormalChar = function (_char) {
35018     return _char >= 0x20 && _char <= 0x7f;
35019   };
35020   /**
35021    * Configures roll-up
35022    *
35023    * @param  {Integer} pts         Current PTS
35024    * @param  {Integer} newBaseRow  Used by PACs to slide the current window to
35025    *                               a new position
35026    */
35027 
35028 
35029   Cea608Stream.prototype.setRollUp = function (pts, newBaseRow) {
35030     // Reset the base row to the bottom row when switching modes
35031     if (this.mode_ !== 'rollUp') {
35032       this.row_ = BOTTOM_ROW;
35033       this.mode_ = 'rollUp'; // Spec says to wipe memories when switching to roll-up
35034 
35035       this.flushDisplayed(pts);
35036       this.nonDisplayed_ = createDisplayBuffer();
35037       this.displayed_ = createDisplayBuffer();
35038     }
35039 
35040     if (newBaseRow !== undefined && newBaseRow !== this.row_) {
35041       // move currently displayed captions (up or down) to the new base row
35042       for (var i = 0; i < this.rollUpRows_; i++) {
35043         this.displayed_[newBaseRow - i] = this.displayed_[this.row_ - i];
35044         this.displayed_[this.row_ - i] = '';
35045       }
35046     }
35047 
35048     if (newBaseRow === undefined) {
35049       newBaseRow = this.row_;
35050     }
35051 
35052     this.topRow_ = newBaseRow - this.rollUpRows_ + 1;
35053   }; // Adds the opening HTML tag for the passed character to the caption text,
35054   // and keeps track of it for later closing
35055 
35056 
35057   Cea608Stream.prototype.addFormatting = function (pts, format) {
35058     this.formatting_ = this.formatting_.concat(format);
35059     var text = format.reduce(function (text, format) {
vendor: 2,998 bytes, lines 35060-35160
35060       return text + '<' + format + '>';
35061     }, '');
35062     this[this.mode_](pts, text);
35063   }; // Adds HTML closing tags for current formatting to caption text and
35064   // clears remembered formatting
35065 
35066 
35067   Cea608Stream.prototype.clearFormatting = function (pts) {
35068     if (!this.formatting_.length) {
35069       return;
35070     }
35071 
35072     var text = this.formatting_.reverse().reduce(function (text, format) {
35073       return text + '</' + format + '>';
35074     }, '');
35075     this.formatting_ = [];
35076     this[this.mode_](pts, text);
35077   }; // Mode Implementations
35078 
35079 
35080   Cea608Stream.prototype.popOn = function (pts, text) {
35081     var baseRow = this.nonDisplayed_[this.row_]; // buffer characters
35082 
35083     baseRow += text;
35084     this.nonDisplayed_[this.row_] = baseRow;
35085   };
35086 
35087   Cea608Stream.prototype.rollUp = function (pts, text) {
35088     var baseRow = this.displayed_[this.row_];
35089     baseRow += text;
35090     this.displayed_[this.row_] = baseRow;
35091   };
35092 
35093   Cea608Stream.prototype.shiftRowsUp_ = function () {
35094     var i; // clear out inactive rows
35095 
35096     for (i = 0; i < this.topRow_; i++) {
35097       this.displayed_[i] = '';
35098     }
35099 
35100     for (i = this.row_ + 1; i < BOTTOM_ROW + 1; i++) {
35101       this.displayed_[i] = '';
35102     } // shift displayed rows up
35103 
35104 
35105     for (i = this.topRow_; i < this.row_; i++) {
35106       this.displayed_[i] = this.displayed_[i + 1];
35107     } // clear out the bottom row
35108 
35109 
35110     this.displayed_[this.row_] = '';
35111   };
35112 
35113   Cea608Stream.prototype.paintOn = function (pts, text) {
35114     var baseRow = this.displayed_[this.row_];
35115     baseRow += text;
35116     this.displayed_[this.row_] = baseRow;
35117   }; // exports
35118 
35119 
35120   var captionStream = {
35121     CaptionStream: CaptionStream,
35122     Cea608Stream: Cea608Stream
35123   };
35124 
35125   var discardEmulationPreventionBytes$1 = captionPacketParser.discardEmulationPreventionBytes;
35126   var CaptionStream$1 = captionStream.CaptionStream;
35127   /**
35128     * Maps an offset in the mdat to a sample based on the the size of the samples.
35129     * Assumes that `parseSamples` has been called first.
35130     *
35131     * @param {Number} offset - The offset into the mdat
35132     * @param {Object[]} samples - An array of samples, parsed using `parseSamples`
35133     * @return {?Object} The matching sample, or null if no match was found.
35134     *
35135     * @see ISO-BMFF-12/2015, Section 8.8.8
35136    **/
35137 
35138   var mapToSample = function mapToSample(offset, samples) {
35139     var approximateOffset = offset;
35140 
35141     for (var i = 0; i < samples.length; i++) {
35142       var sample = samples[i];
35143 
35144       if (approximateOffset < sample.size) {
35145         return sample;
35146       }
35147 
35148       approximateOffset -= sample.size;
35149     }
35150 
35151     return null;
35152   };
35153   /**
35154     * Finds SEI nal units contained in a Media Data Box.
35155     * Assumes that `parseSamples` has been called first.
35156     *
35157     * @param {Uint8Array} avcStream - The bytes of the mdat
35158     * @param {Object[]} samples - The samples parsed out by `parseSamples`
35159     * @param {Number} trackId - The trackId of this video track
35160     * @return {Object[]}
vendor: 4,298 bytes, lines 35160-35285
35160 seiNals - the parsed SEI NALUs found.
35161     *   The contents of the seiNal should match what is expected by
35162     *   CaptionStream.push (nalUnitType, size, data, escapedRBSP, pts, dts)
35163     *
35164     * @see ISO-BMFF-12/2015, Section 8.1.1
35165     * @see Rec. ITU-T H.264, 7.3.2.3.1
35166    **/
35167 
35168 
35169   var findSeiNals = function findSeiNals(avcStream, samples, trackId) {
35170     var avcView = new DataView(avcStream.buffer, avcStream.byteOffset, avcStream.byteLength),
35171         result = [],
35172         seiNal,
35173         i,
35174         length,
35175         lastMatchedSample;
35176 
35177     for (i = 0; i + 4 < avcStream.length; i += length) {
35178       length = avcView.getUint32(i);
35179       i += 4; // Bail if this doesn't appear to be an H264 stream
35180 
35181       if (length <= 0) {
35182         continue;
35183       }
35184 
35185       switch (avcStream[i] & 0x1F) {
35186         case 0x06:
35187           var data = avcStream.subarray(i + 1, i + 1 + length);
35188           var matchingSample = mapToSample(i, samples);
35189           seiNal = {
35190             nalUnitType: 'sei_rbsp',
35191             size: length,
35192             data: data,
35193             escapedRBSP: discardEmulationPreventionBytes$1(data),
35194             trackId: trackId
35195           };
35196 
35197           if (matchingSample) {
35198             seiNal.pts = matchingSample.pts;
35199             seiNal.dts = matchingSample.dts;
35200             lastMatchedSample = matchingSample;
35201           } else {
35202             // If a matching sample cannot be found, use the last
35203             // sample's values as they should be as close as possible
35204             seiNal.pts = lastMatchedSample.pts;
35205             seiNal.dts = lastMatchedSample.dts;
35206           }
35207 
35208           result.push(seiNal);
35209           break;
35210 
35211         default:
35212           break;
35213       }
35214     }
35215 
35216     return result;
35217   };
35218   /**
35219     * Parses sample information out of Track Run Boxes and calculates
35220     * the absolute presentation and decode timestamps of each sample.
35221     *
35222     * @param {Array<Uint8Array>} truns - The Trun Run boxes to be parsed
35223     * @param {Number} baseMediaDecodeTime - base media decode time from tfdt
35224         @see ISO-BMFF-12/2015, Section 8.8.12
35225     * @param {Object} tfhd - The parsed Track Fragment Header
35226     *   @see inspect.parseTfhd
35227     * @return {Object[]} the parsed samples
35228     *
35229     * @see ISO-BMFF-12/2015, Section 8.8.8
35230    **/
35231 
35232 
35233   var parseSamples = function parseSamples(truns, baseMediaDecodeTime, tfhd) {
35234     var currentDts = baseMediaDecodeTime;
35235     var defaultSampleDuration = tfhd.defaultSampleDuration || 0;
35236     var defaultSampleSize = tfhd.defaultSampleSize || 0;
35237     var trackId = tfhd.trackId;
35238     var allSamples = [];
35239     truns.forEach(function (trun) {
35240       // Note: We currently do not parse the sample table as well
35241       // as the trun. It's possible some sources will require this.
35242       // moov > trak > mdia > minf > stbl
35243       var trackRun = mp4Inspector.parseTrun(trun);
35244       var samples = trackRun.samples;
35245       samples.forEach(function (sample) {
35246         if (sample.duration === undefined) {
35247           sample.duration = defaultSampleDuration;
35248         }
35249 
35250         if (sample.size === undefined) {
35251           sample.size = defaultSampleSize;
35252         }
35253 
35254         sample.trackId = trackId;
35255         sample.dts = currentDts;
35256 
35257         if (sample.compositionTimeOffset === undefined) {
35258           sample.compositionTimeOffset = 0;
35259         }
35260 
35261         sample.pts = currentDts + sample.compositionTimeOffset;
35262         currentDts += sample.duration;
35263       });
35264       allSamples = allSamples.concat(samples);
35265     });
35266     return allSamples;
35267   };
35268   /**
35269     * Parses out caption nals from an FMP4 segment's video tracks.
35270     *
35271     * @param {Uint8Array} segment - The bytes of a single segment
35272     * @param {Number} videoTrackId - The trackId of a video track in the segment
35273     * @return {Object.<Number, Object[]>} A mapping of video trackId to
35274     *   a list of seiNals found in that track
35275    **/
35276 
35277 
35278   var parseCaptionNals = function parseCaptionNals(segment, videoTrackId) {
35279     // To get the samples
35280     var trafs = probe.findBox(segment, ['moof', 'traf']); // To get SEI NAL units
35281 
35282     var mdats = probe.findBox(segment, ['mdat']);
35283     var captionNals = {};
35284     var mdatTrafPairs = []; // Pair up each traf with a mdat as moofs and mdats are in pairs
35285 
vendor: 3,771 bytes, lines 35286-35388
35286     mdats.forEach(function (mdat, index) {
35287       var matchingTraf = trafs[index];
35288       mdatTrafPairs.push({
35289         mdat: mdat,
35290         traf: matchingTraf
35291       });
35292     });
35293     mdatTrafPairs.forEach(function (pair) {
35294       var mdat = pair.mdat;
35295       var traf = pair.traf;
35296       var tfhd = probe.findBox(traf, ['tfhd']); // Exactly 1 tfhd per traf
35297 
35298       var headerInfo = mp4Inspector.parseTfhd(tfhd[0]);
35299       var trackId = headerInfo.trackId;
35300       var tfdt = probe.findBox(traf, ['tfdt']); // Either 0 or 1 tfdt per traf
35301 
35302       var baseMediaDecodeTime = tfdt.length > 0 ? mp4Inspector.parseTfdt(tfdt[0]).baseMediaDecodeTime : 0;
35303       var truns = probe.findBox(traf, ['trun']);
35304       var samples;
35305       var seiNals; // Only parse video data for the chosen video track
35306 
35307       if (videoTrackId === trackId && truns.length > 0) {
35308         samples = parseSamples(truns, baseMediaDecodeTime, headerInfo);
35309         seiNals = findSeiNals(mdat, samples, trackId);
35310 
35311         if (!captionNals[trackId]) {
35312           captionNals[trackId] = [];
35313         }
35314 
35315         captionNals[trackId] = captionNals[trackId].concat(seiNals);
35316       }
35317     });
35318     return captionNals;
35319   };
35320   /**
35321     * Parses out inband captions from an MP4 container and returns
35322     * caption objects that can be used by WebVTT and the TextTrack API.
35323     * @see https://developer.mozilla.org/en-US/docs/Web/API/VTTCue
35324     * @see https://developer.mozilla.org/en-US/docs/Web/API/TextTrack
35325     * Assumes that `probe.getVideoTrackIds` and `probe.timescale` have been called first
35326     *
35327     * @param {Uint8Array} segment - The fmp4 segment containing embedded captions
35328     * @param {Number} trackId - The id of the video track to parse
35329     * @param {Number} timescale - The timescale for the video track from the init segment
35330     *
35331     * @return {?Object[]} parsedCaptions - A list of captions or null if no video tracks
35332     * @return {Number} parsedCaptions[].startTime - The time to show the caption in seconds
35333     * @return {Number} parsedCaptions[].endTime - The time to stop showing the caption in seconds
35334     * @return {String} parsedCaptions[].text - The visible content of the caption
35335    **/
35336 
35337 
35338   var parseEmbeddedCaptions = function parseEmbeddedCaptions(segment, trackId, timescale) {
35339     var seiNals; // the ISO-BMFF spec says that trackId can't be zero, but there's some broken content out there
35340 
35341     if (trackId === null) {
35342       return null;
35343     }
35344 
35345     seiNals = parseCaptionNals(segment, trackId);
35346     return {
35347       seiNals: seiNals[trackId],
35348       timescale: timescale
35349     };
35350   };
35351   /**
35352     * Converts SEI NALUs into captions that can be used by video.js
35353    **/
35354 
35355 
35356   var CaptionParser = function CaptionParser() {
35357     var isInitialized = false;
35358     var captionStream; // Stores segments seen before trackId and timescale are set
35359 
35360     var segmentCache; // Stores video track ID of the track being parsed
35361 
35362     var trackId; // Stores the timescale of the track being parsed
35363 
35364     var timescale; // Stores captions parsed so far
35365 
35366     var parsedCaptions; // Stores whether we are receiving partial data or not
35367 
35368     var parsingPartial;
35369     /**
35370       * A method to indicate whether a CaptionParser has been initalized
35371       * @returns {Boolean}
35372      **/
35373 
35374     this.isInitialized = function () {
35375       return isInitialized;
35376     };
35377     /**
35378       * Initializes the underlying CaptionStream, SEI NAL parsing
35379       * and management, and caption collection
35380      **/
35381 
35382 
35383     this.init = function (options) {
35384       captionStream = new CaptionStream$1();
35385       isInitialized = true;
35386       parsingPartial = options ? options.isPartial : false; // Collect dispatched captions
35387 
35388       captionStream.on('data', function (event) {
vendor: 4,550 bytes, lines 35389-35538
35389         // Convert to seconds in the source's timescale
35390         event.startTime = event.startPts / timescale;
35391         event.endTime = event.endPts / timescale;
35392         parsedCaptions.captions.push(event);
35393         parsedCaptions.captionStreams[event.stream] = true;
35394       });
35395     };
35396     /**
35397       * Determines if a new video track will be selected
35398       * or if the timescale changed
35399       * @return {Boolean}
35400      **/
35401 
35402 
35403     this.isNewInit = function (videoTrackIds, timescales) {
35404       if (videoTrackIds && videoTrackIds.length === 0 || timescales && typeof timescales === 'object' && Object.keys(timescales).length === 0) {
35405         return false;
35406       }
35407 
35408       return trackId !== videoTrackIds[0] || timescale !== timescales[trackId];
35409     };
35410     /**
35411       * Parses out SEI captions and interacts with underlying
35412       * CaptionStream to return dispatched captions
35413       *
35414       * @param {Uint8Array} segment - The fmp4 segment containing embedded captions
35415       * @param {Number[]} videoTrackIds - A list of video tracks found in the init segment
35416       * @param {Object.<Number, Number>} timescales - The timescales found in the init segment
35417       * @see parseEmbeddedCaptions
35418       * @see m2ts/caption-stream.js
35419      **/
35420 
35421 
35422     this.parse = function (segment, videoTrackIds, timescales) {
35423       var parsedData;
35424 
35425       if (!this.isInitialized()) {
35426         return null; // This is not likely to be a video segment
35427       } else if (!videoTrackIds || !timescales) {
35428         return null;
35429       } else if (this.isNewInit(videoTrackIds, timescales)) {
35430         // Use the first video track only as there is no
35431         // mechanism to switch to other video tracks
35432         trackId = videoTrackIds[0];
35433         timescale = timescales[trackId]; // If an init segment has not been seen yet, hold onto segment
35434         // data until we have one.
35435         // the ISO-BMFF spec says that trackId can't be zero, but there's some broken content out there
35436       } else if (trackId === null || !timescale) {
35437         segmentCache.push(segment);
35438         return null;
35439       } // Now that a timescale and trackId is set, parse cached segments
35440 
35441 
35442       while (segmentCache.length > 0) {
35443         var cachedSegment = segmentCache.shift();
35444         this.parse(cachedSegment, videoTrackIds, timescales);
35445       }
35446 
35447       parsedData = parseEmbeddedCaptions(segment, trackId, timescale);
35448 
35449       if (parsedData === null || !parsedData.seiNals) {
35450         return null;
35451       }
35452 
35453       this.pushNals(parsedData.seiNals); // Force the parsed captions to be dispatched
35454 
35455       this.flushStream();
35456       return parsedCaptions;
35457     };
35458     /**
35459       * Pushes SEI NALUs onto CaptionStream
35460       * @param {Object[]} nals - A list of SEI nals parsed using `parseCaptionNals`
35461       * Assumes that `parseCaptionNals` has been called first
35462       * @see m2ts/caption-stream.js
35463       **/
35464 
35465 
35466     this.pushNals = function (nals) {
35467       if (!this.isInitialized() || !nals || nals.length === 0) {
35468         return null;
35469       }
35470 
35471       nals.forEach(function (nal) {
35472         captionStream.push(nal);
35473       });
35474     };
35475     /**
35476       * Flushes underlying CaptionStream to dispatch processed, displayable captions
35477       * @see m2ts/caption-stream.js
35478      **/
35479 
35480 
35481     this.flushStream = function () {
35482       if (!this.isInitialized()) {
35483         return null;
35484       }
35485 
35486       if (!parsingPartial) {
35487         captionStream.flush();
35488       } else {
35489         captionStream.partialFlush();
35490       }
35491     };
35492     /**
35493       * Reset caption buckets for new data
35494      **/
35495 
35496 
35497     this.clearParsedCaptions = function () {
35498       parsedCaptions.captions = [];
35499       parsedCaptions.captionStreams = {};
35500     };
35501     /**
35502       * Resets underlying CaptionStream
35503       * @see m2ts/caption-stream.js
35504      **/
35505 
35506 
35507     this.resetCaptionStream = function () {
35508       if (!this.isInitialized()) {
35509         return null;
35510       }
35511 
35512       captionStream.reset();
35513     };
35514     /**
35515       * Convenience method to clear all captions flushed from the
35516       * CaptionStream and still being parsed
35517       * @see m2ts/caption-stream.js
35518      **/
35519 
35520 
35521     this.clearAllCaptions = function () {
35522       this.clearParsedCaptions();
35523       this.resetCaptionStream();
35524     };
35525     /**
35526       * Reset caption parser
35527      **/
35528 
35529 
35530     this.reset = function () {
35531       segmentCache = [];
35532       trackId = null;
35533       timescale = null;
35534 
35535       if (!parsedCaptions) {
35536         parsedCaptions = {
35537           captions: [],
35538           // CC1, CC2, CC3, CC4
vendor: 7,393 bytes, lines 35539-35772
35539           captionStreams: {}
35540         };
35541       } else {
35542         this.clearParsedCaptions();
35543       }
35544 
35545       this.resetCaptionStream();
35546     };
35547 
35548     this.reset();
35549   };
35550 
35551   var captionParser = CaptionParser;
35552 
35553   /**
35554    * mux.js
35555    *
35556    * Copyright (c) Brightcove
35557    * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
35558    */
35559 
35560   var streamTypes = {
35561     H264_STREAM_TYPE: 0x1B,
35562     ADTS_STREAM_TYPE: 0x0F,
35563     METADATA_STREAM_TYPE: 0x15
35564   };
35565 
35566   var MAX_TS = 8589934592;
35567   var RO_THRESH = 4294967296;
35568   var TYPE_SHARED = 'shared';
35569 
35570   var handleRollover = function handleRollover(value, reference) {
35571     var direction = 1;
35572 
35573     if (value > reference) {
35574       // If the current timestamp value is greater than our reference timestamp and we detect a
35575       // timestamp rollover, this means the roll over is happening in the opposite direction.
35576       // Example scenario: Enter a long stream/video just after a rollover occurred. The reference
35577       // point will be set to a small number, e.g. 1. The user then seeks backwards over the
35578       // rollover point. In loading this segment, the timestamp values will be very large,
35579       // e.g. 2^33 - 1. Since this comes before the data we loaded previously, we want to adjust
35580       // the time stamp to be `value - 2^33`.
35581       direction = -1;
35582     } // Note: A seek forwards or back that is greater than the RO_THRESH (2^32, ~13 hours) will
35583     // cause an incorrect adjustment.
35584 
35585 
35586     while (Math.abs(reference - value) > RO_THRESH) {
35587       value += direction * MAX_TS;
35588     }
35589 
35590     return value;
35591   };
35592 
35593   var TimestampRolloverStream = function TimestampRolloverStream(type) {
35594     var lastDTS, referenceDTS;
35595     TimestampRolloverStream.prototype.init.call(this); // The "shared" type is used in cases where a stream will contain muxed
35596     // video and audio. We could use `undefined` here, but having a string
35597     // makes debugging a little clearer.
35598 
35599     this.type_ = type || TYPE_SHARED;
35600 
35601     this.push = function (data) {
35602       // Any "shared" rollover streams will accept _all_ data. Otherwise,
35603       // streams will only accept data that matches their type.
35604       if (this.type_ !== TYPE_SHARED && data.type !== this.type_) {
35605         return;
35606       }
35607 
35608       if (referenceDTS === undefined) {
35609         referenceDTS = data.dts;
35610       }
35611 
35612       data.dts = handleRollover(data.dts, referenceDTS);
35613       data.pts = handleRollover(data.pts, referenceDTS);
35614       lastDTS = data.dts;
35615       this.trigger('data', data);
35616     };
35617 
35618     this.flush = function () {
35619       referenceDTS = lastDTS;
35620       this.trigger('done');
35621     };
35622 
35623     this.endTimeline = function () {
35624       this.flush();
35625       this.trigger('endedtimeline');
35626     };
35627 
35628     this.discontinuity = function () {
35629       referenceDTS = void 0;
35630       lastDTS = void 0;
35631     };
35632 
35633     this.reset = function () {
35634       this.discontinuity();
35635       this.trigger('reset');
35636     };
35637   };
35638 
35639   TimestampRolloverStream.prototype = new stream();
35640   var timestampRolloverStream = {
35641     TimestampRolloverStream: TimestampRolloverStream,
35642     handleRollover: handleRollover
35643   };
35644 
35645   var parsePid = function parsePid(packet) {
35646     var pid = packet[1] & 0x1f;
35647     pid <<= 8;
35648     pid |= packet[2];
35649     return pid;
35650   };
35651 
35652   var parsePayloadUnitStartIndicator = function parsePayloadUnitStartIndicator(packet) {
35653     return !!(packet[1] & 0x40);
35654   };
35655 
35656   var parseAdaptionField = function parseAdaptionField(packet) {
35657     var offset = 0; // if an adaption field is present, its length is specified by the
35658     // fifth byte of the TS packet header. The adaptation field is
35659     // used to add stuffing to PES packets that don't fill a complete
35660     // TS packet, and to specify some forms of timing and control data
35661     // that we do not currently use.
35662 
35663     if ((packet[3] & 0x30) >>> 4 > 0x01) {
35664       offset += packet[4] + 1;
35665     }
35666 
35667     return offset;
35668   };
35669 
35670   var parseType$1 = function parseType(packet, pmtPid) {
35671     var pid = parsePid(packet);
35672 
35673     if (pid === 0) {
35674       return 'pat';
35675     } else if (pid === pmtPid) {
35676       return 'pmt';
35677     } else if (pmtPid) {
35678       return 'pes';
35679     }
35680 
35681     return null;
35682   };
35683 
35684   var parsePat = function parsePat(packet) {
35685     var pusi = parsePayloadUnitStartIndicator(packet);
35686     var offset = 4 + parseAdaptionField(packet);
35687 
35688     if (pusi) {
35689       offset += packet[offset] + 1;
35690     }
35691 
35692     return (packet[offset + 10] & 0x1f) << 8 | packet[offset + 11];
35693   };
35694 
35695   var parsePmt = function parsePmt(packet) {
35696     var programMapTable = {};
35697     var pusi = parsePayloadUnitStartIndicator(packet);
35698     var payloadOffset = 4 + parseAdaptionField(packet);
35699 
35700     if (pusi) {
35701       payloadOffset += packet[payloadOffset] + 1;
35702     } // PMTs can be sent ahead of the time when they should actually
35703     // take effect. We don't believe this should ever be the case
35704     // for HLS but we'll ignore "forward" PMT declarations if we see
35705     // them. Future PMT declarations have the current_next_indicator
35706     // set to zero.
35707 
35708 
35709     if (!(packet[payloadOffset + 5] & 0x01)) {
35710       return;
35711     }
35712 
35713     var sectionLength, tableEnd, programInfoLength; // the mapping table ends at the end of the current section
35714 
35715     sectionLength = (packet[payloadOffset + 1] & 0x0f) << 8 | packet[payloadOffset + 2];
35716     tableEnd = 3 + sectionLength - 4; // to determine where the table is, we have to figure out how
35717     // long the program info descriptors are
35718 
35719     programInfoLength = (packet[payloadOffset + 10] & 0x0f) << 8 | packet[payloadOffset + 11]; // advance the offset to the first entry in the mapping table
35720 
35721     var offset = 12 + programInfoLength;
35722 
35723     while (offset < tableEnd) {
35724       var i = payloadOffset + offset; // add an entry that maps the elementary_pid to the stream_type
35725 
35726       programMapTable[(packet[i + 1] & 0x1F) << 8 | packet[i + 2]] = packet[i]; // move to the next table entry
35727       // skip past the elementary stream descriptors, if present
35728 
35729       offset += ((packet[i + 3] & 0x0F) << 8 | packet[i + 4]) + 5;
35730     }
35731 
35732     return programMapTable;
35733   };
35734 
35735   var parsePesType = function parsePesType(packet, programMapTable) {
35736     var pid = parsePid(packet);
35737     var type = programMapTable[pid];
35738 
35739     switch (type) {
35740       case streamTypes.H264_STREAM_TYPE:
35741         return 'video';
35742 
35743       case streamTypes.ADTS_STREAM_TYPE:
35744         return 'audio';
35745 
35746       case streamTypes.METADATA_STREAM_TYPE:
35747         return 'timed-metadata';
35748 
35749       default:
35750         return null;
35751     }
35752   };
35753 
35754   var parsePesTime = function parsePesTime(packet) {
35755     var pusi = parsePayloadUnitStartIndicator(packet);
35756 
35757     if (!pusi) {
35758       return null;
35759     }
35760 
35761     var offset = 4 + parseAdaptionField(packet);
35762 
35763     if (offset >= packet.byteLength) {
35764       // From the H 222.0 MPEG-TS spec
35765       // "For transport stream packets carrying PES packets, stuffing is needed when there
35766       //  is insufficient PES packet data to completely fill the transport stream packet
35767       //  payload bytes. Stuffing is accomplished by defining an adaptation field longer than
35768       //  the sum of the lengths of the data elements in it, so that the payload bytes
35769       //  remaining after the adaptation field exactly accommodates the available PES packet
35770       //  data."
35771       //
35772       // If the offset is >
vendor: 5,828 bytes, lines 35772-35949
35772= the length of the packet, then the packet contains no data
35773       // and instead is just adaption field stuffing bytes
35774       return null;
35775     }
35776 
35777     var pes = null;
35778     var ptsDtsFlags; // PES packets may be annotated with a PTS value, or a PTS value
35779     // and a DTS value. Determine what combination of values is
35780     // available to work with.
35781 
35782     ptsDtsFlags = packet[offset + 7]; // PTS and DTS are normally stored as a 33-bit number.  Javascript
35783     // performs all bitwise operations on 32-bit integers but javascript
35784     // supports a much greater range (52-bits) of integer using standard
35785     // mathematical operations.
35786     // We construct a 31-bit value using bitwise operators over the 31
35787     // most significant bits and then multiply by 4 (equal to a left-shift
35788     // of 2) before we add the final 2 least significant bits of the
35789     // timestamp (equal to an OR.)
35790 
35791     if (ptsDtsFlags & 0xC0) {
35792       pes = {}; // the PTS and DTS are not written out directly. For information
35793       // on how they are encoded, see
35794       // http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
35795 
35796       pes.pts = (packet[offset + 9] & 0x0E) << 27 | (packet[offset + 10] & 0xFF) << 20 | (packet[offset + 11] & 0xFE) << 12 | (packet[offset + 12] & 0xFF) << 5 | (packet[offset + 13] & 0xFE) >>> 3;
35797       pes.pts *= 4; // Left shift by 2
35798 
35799       pes.pts += (packet[offset + 13] & 0x06) >>> 1; // OR by the two LSBs
35800 
35801       pes.dts = pes.pts;
35802 
35803       if (ptsDtsFlags & 0x40) {
35804         pes.dts = (packet[offset + 14] & 0x0E) << 27 | (packet[offset + 15] & 0xFF) << 20 | (packet[offset + 16] & 0xFE) << 12 | (packet[offset + 17] & 0xFF) << 5 | (packet[offset + 18] & 0xFE) >>> 3;
35805         pes.dts *= 4; // Left shift by 2
35806 
35807         pes.dts += (packet[offset + 18] & 0x06) >>> 1; // OR by the two LSBs
35808       }
35809     }
35810 
35811     return pes;
35812   };
35813 
35814   var parseNalUnitType = function parseNalUnitType(type) {
35815     switch (type) {
35816       case 0x05:
35817         return 'slice_layer_without_partitioning_rbsp_idr';
35818 
35819       case 0x06:
35820         return 'sei_rbsp';
35821 
35822       case 0x07:
35823         return 'seq_parameter_set_rbsp';
35824 
35825       case 0x08:
35826         return 'pic_parameter_set_rbsp';
35827 
35828       case 0x09:
35829         return 'access_unit_delimiter_rbsp';
35830 
35831       default:
35832         return null;
35833     }
35834   };
35835 
35836   var videoPacketContainsKeyFrame = function videoPacketContainsKeyFrame(packet) {
35837     var offset = 4 + parseAdaptionField(packet);
35838     var frameBuffer = packet.subarray(offset);
35839     var frameI = 0;
35840     var frameSyncPoint = 0;
35841     var foundKeyFrame = false;
35842     var nalType; // advance the sync point to a NAL start, if necessary
35843 
35844     for (; frameSyncPoint < frameBuffer.byteLength - 3; frameSyncPoint++) {
35845       if (frameBuffer[frameSyncPoint + 2] === 1) {
35846         // the sync point is properly aligned
35847         frameI = frameSyncPoint + 5;
35848         break;
35849       }
35850     }
35851 
35852     while (frameI < frameBuffer.byteLength) {
35853       // look at the current byte to determine if we've hit the end of
35854       // a NAL unit boundary
35855       switch (frameBuffer[frameI]) {
35856         case 0:
35857           // skip past non-sync sequences
35858           if (frameBuffer[frameI - 1] !== 0) {
35859             frameI += 2;
35860             break;
35861           } else if (frameBuffer[frameI - 2] !== 0) {
35862             frameI++;
35863             break;
35864           }
35865 
35866           if (frameSyncPoint + 3 !== frameI - 2) {
35867             nalType = parseNalUnitType(frameBuffer[frameSyncPoint + 3] & 0x1f);
35868 
35869             if (nalType === 'slice_layer_without_partitioning_rbsp_idr') {
35870               foundKeyFrame = true;
35871             }
35872           } // drop trailing zeroes
35873 
35874 
35875           do {
35876             frameI++;
35877           } while (frameBuffer[frameI] !== 1 && frameI < frameBuffer.length);
35878 
35879           frameSyncPoint = frameI - 2;
35880           frameI += 3;
35881           break;
35882 
35883         case 1:
35884           // skip past non-sync sequences
35885           if (frameBuffer[frameI - 1] !== 0 || frameBuffer[frameI - 2] !== 0) {
35886             frameI += 3;
35887             break;
35888           }
35889 
35890           nalType = parseNalUnitType(frameBuffer[frameSyncPoint + 3] & 0x1f);
35891 
35892           if (nalType === 'slice_layer_without_partitioning_rbsp_idr') {
35893             foundKeyFrame = true;
35894           }
35895 
35896           frameSyncPoint = frameI - 2;
35897           frameI += 3;
35898           break;
35899 
35900         default:
35901           // the current byte isn't a one or zero, so it cannot be part
35902           // of a sync sequence
35903           frameI += 3;
35904           break;
35905       }
35906     }
35907 
35908     frameBuffer = frameBuffer.subarray(frameSyncPoint);
35909     frameI -= frameSyncPoint;
35910     frameSyncPoint = 0; // parse the final nal
35911 
35912     if (frameBuffer && frameBuffer.byteLength > 3) {
35913       nalType = parseNalUnitType(frameBuffer[frameSyncPoint + 3] & 0x1f);
35914 
35915       if (nalType === 'slice_layer_without_partitioning_rbsp_idr') {
35916         foundKeyFrame = true;
35917       }
35918     }
35919 
35920     return foundKeyFrame;
35921   };
35922 
35923   var probe$1 = {
35924     parseType: parseType$1,
35925     parsePat: parsePat,
35926     parsePmt: parsePmt,
35927     parsePayloadUnitStartIndicator: parsePayloadUnitStartIndicator,
35928     parsePesType: parsePesType,
35929     parsePesTime: parsePesTime,
35930     videoPacketContainsKeyFrame: videoPacketContainsKeyFrame
35931   };
35932 
35933   /**
35934    * mux.js
35935    *
35936    * Copyright (c) Brightcove
35937    * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
35938    *
35939    * Utilities to detect basic properties and metadata about Aac data.
35940    */
35941 
35942   var ADTS_SAMPLING_FREQUENCIES = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350];
35943 
35944   var isLikelyAacData = function isLikelyAacData(data) {
35945     if (data[0] === 'I'.charCodeAt(0) && data[1] === 'D'.charCodeAt(0) && data[2] === '3'.charCodeAt(0)) {
35946       return true;
35947     }
35948 
35949     return false;
vendor: 5,626 bytes, lines 35950-36127
35950   };
35951 
35952   var parseSyncSafeInteger = function parseSyncSafeInteger(data) {
35953     return data[0] << 21 | data[1] << 14 | data[2] << 7 | data[3];
35954   }; // return a percent-encoded representation of the specified byte range
35955   // @see http://en.wikipedia.org/wiki/Percent-encoding
35956 
35957 
35958   var percentEncode = function percentEncode(bytes, start, end) {
35959     var i,
35960         result = '';
35961 
35962     for (i = start; i < end; i++) {
35963       result += '%' + ('00' + bytes[i].toString(16)).slice(-2);
35964     }
35965 
35966     return result;
35967   }; // return the string representation of the specified byte range,
35968   // interpreted as ISO-8859-1.
35969 
35970 
35971   var parseIso88591 = function parseIso88591(bytes, start, end) {
35972     return unescape(percentEncode(bytes, start, end)); // jshint ignore:line
35973   };
35974 
35975   var parseId3TagSize = function parseId3TagSize(header, byteIndex) {
35976     var returnSize = header[byteIndex + 6] << 21 | header[byteIndex + 7] << 14 | header[byteIndex + 8] << 7 | header[byteIndex + 9],
35977         flags = header[byteIndex + 5],
35978         footerPresent = (flags & 16) >> 4;
35979 
35980     if (footerPresent) {
35981       return returnSize + 20;
35982     }
35983 
35984     return returnSize + 10;
35985   };
35986 
35987   var parseAdtsSize = function parseAdtsSize(header, byteIndex) {
35988     var lowThree = (header[byteIndex + 5] & 0xE0) >> 5,
35989         middle = header[byteIndex + 4] << 3,
35990         highTwo = header[byteIndex + 3] & 0x3 << 11;
35991     return highTwo | middle | lowThree;
35992   };
35993 
35994   var parseType$2 = function parseType(header, byteIndex) {
35995     if (header[byteIndex] === 'I'.charCodeAt(0) && header[byteIndex + 1] === 'D'.charCodeAt(0) && header[byteIndex + 2] === '3'.charCodeAt(0)) {
35996       return 'timed-metadata';
35997     } else if (header[byteIndex] & 0xff === 0xff && (header[byteIndex + 1] & 0xf0) === 0xf0) {
35998       return 'audio';
35999     }
36000 
36001     return null;
36002   };
36003 
36004   var parseSampleRate = function parseSampleRate(packet) {
36005     var i = 0;
36006 
36007     while (i + 5 < packet.length) {
36008       if (packet[i] !== 0xFF || (packet[i + 1] & 0xF6) !== 0xF0) {
36009         // If a valid header was not found,  jump one forward and attempt to
36010         // find a valid ADTS header starting at the next byte
36011         i++;
36012         continue;
36013       }
36014 
36015       return ADTS_SAMPLING_FREQUENCIES[(packet[i + 2] & 0x3c) >>> 2];
36016     }
36017 
36018     return null;
36019   };
36020 
36021   var parseAacTimestamp = function parseAacTimestamp(packet) {
36022     var frameStart, frameSize, frame, frameHeader; // find the start of the first frame and the end of the tag
36023 
36024     frameStart = 10;
36025 
36026     if (packet[5] & 0x40) {
36027       // advance the frame start past the extended header
36028       frameStart += 4; // header size field
36029 
36030       frameStart += parseSyncSafeInteger(packet.subarray(10, 14));
36031     } // parse one or more ID3 frames
36032     // http://id3.org/id3v2.3.0#ID3v2_frame_overview
36033 
36034 
36035     do {
36036       // determine the number of bytes in this frame
36037       frameSize = parseSyncSafeInteger(packet.subarray(frameStart + 4, frameStart + 8));
36038 
36039       if (frameSize < 1) {
36040         return null;
36041       }
36042 
36043       frameHeader = String.fromCharCode(packet[frameStart], packet[frameStart + 1], packet[frameStart + 2], packet[frameStart + 3]);
36044 
36045       if (frameHeader === 'PRIV') {
36046         frame = packet.subarray(frameStart + 10, frameStart + frameSize + 10);
36047 
36048         for (var i = 0; i < frame.byteLength; i++) {
36049           if (frame[i] === 0) {
36050             var owner = parseIso88591(frame, 0, i);
36051 
36052             if (owner === 'com.apple.streaming.transportStreamTimestamp') {
36053               var d = frame.subarray(i + 1);
36054               var size = (d[3] & 0x01) << 30 | d[4] << 22 | d[5] << 14 | d[6] << 6 | d[7] >>> 2;
36055               size *= 4;
36056               size += d[7] & 0x03;
36057               return size;
36058             }
36059 
36060             break;
36061           }
36062         }
36063       }
36064 
36065       frameStart += 10; // advance past the frame header
36066 
36067       frameStart += frameSize; // advance past the frame body
36068     } while (frameStart < packet.byteLength);
36069 
36070     return null;
36071   };
36072 
36073   var utils = {
36074     isLikelyAacData: isLikelyAacData,
36075     parseId3TagSize: parseId3TagSize,
36076     parseAdtsSize: parseAdtsSize,
36077     parseType: parseType$2,
36078     parseSampleRate: parseSampleRate,
36079     parseAacTimestamp: parseAacTimestamp
36080   };
36081 
36082   /**
36083    * mux.js
36084    *
36085    * Copyright (c) Brightcove
36086    * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
36087    */
36088   var ONE_SECOND_IN_TS = 90000,
36089       // 90kHz clock
36090   secondsToVideoTs,
36091       secondsToAudioTs,
36092       videoTsToSeconds,
36093       audioTsToSeconds,
36094       audioTsToVideoTs,
36095       videoTsToAudioTs,
36096       metadataTsToSeconds;
36097 
36098   secondsToVideoTs = function secondsToVideoTs(seconds) {
36099     return seconds * ONE_SECOND_IN_TS;
36100   };
36101 
36102   secondsToAudioTs = function secondsToAudioTs(seconds, sampleRate) {
36103     return seconds * sampleRate;
36104   };
36105 
36106   videoTsToSeconds = function videoTsToSeconds(timestamp) {
36107     return timestamp / ONE_SECOND_IN_TS;
36108   };
36109 
36110   audioTsToSeconds = function audioTsToSeconds(timestamp, sampleRate) {
36111     return timestamp / sampleRate;
36112   };
36113 
36114   audioTsToVideoTs = function audioTsToVideoTs(timestamp, sampleRate) {
36115     return secondsToVideoTs(audioTsToSeconds(timestamp, sampleRate));
36116   };
36117 
36118   videoTsToAudioTs = function videoTsToAudioTs(timestamp, sampleRate) {
36119     return secondsToAudioTs(videoTsToSeconds(timestamp), sampleRate);
36120   };
36121   /**
36122    * Adjust ID3 tag or caption timing information by the timeline pts values
36123    * (if keepOriginalTimestamps is false) and convert to seconds
36124    */
36125 
36126 
36127   metadataTsToSeconds = function metadataTsToSeconds(timestamp, timelineStartPts, 
vendor: 4,162 bytes, lines 36127-36267
36127keepOriginalTimestamps) {
36128     return videoTsToSeconds(keepOriginalTimestamps ? timestamp : timestamp - timelineStartPts);
36129   };
36130 
36131   var clock = {
36132     ONE_SECOND_IN_TS: ONE_SECOND_IN_TS,
36133     secondsToVideoTs: secondsToVideoTs,
36134     secondsToAudioTs: secondsToAudioTs,
36135     videoTsToSeconds: videoTsToSeconds,
36136     audioTsToSeconds: audioTsToSeconds,
36137     audioTsToVideoTs: audioTsToVideoTs,
36138     videoTsToAudioTs: videoTsToAudioTs,
36139     metadataTsToSeconds: metadataTsToSeconds
36140   };
36141 
36142   var handleRollover$1 = timestampRolloverStream.handleRollover;
36143   var probe$2 = {};
36144   probe$2.ts = probe$1;
36145   probe$2.aac = utils;
36146   var ONE_SECOND_IN_TS$1 = clock.ONE_SECOND_IN_TS;
36147   var MP2T_PACKET_LENGTH = 188,
36148       // bytes
36149   SYNC_BYTE = 0x47;
36150   /**
36151    * walks through segment data looking for pat and pmt packets to parse out
36152    * program map table information
36153    */
36154 
36155   var parsePsi_ = function parsePsi_(bytes, pmt) {
36156     var startIndex = 0,
36157         endIndex = MP2T_PACKET_LENGTH,
36158         packet,
36159         type;
36160 
36161     while (endIndex < bytes.byteLength) {
36162       // Look for a pair of start and end sync bytes in the data..
36163       if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) {
36164         // We found a packet
36165         packet = bytes.subarray(startIndex, endIndex);
36166         type = probe$2.ts.parseType(packet, pmt.pid);
36167 
36168         switch (type) {
36169           case 'pat':
36170             if (!pmt.pid) {
36171               pmt.pid = probe$2.ts.parsePat(packet);
36172             }
36173 
36174             break;
36175 
36176           case 'pmt':
36177             if (!pmt.table) {
36178               pmt.table = probe$2.ts.parsePmt(packet);
36179             }
36180 
36181             break;
36182 
36183           default:
36184             break;
36185         } // Found the pat and pmt, we can stop walking the segment
36186 
36187 
36188         if (pmt.pid && pmt.table) {
36189           return;
36190         }
36191 
36192         startIndex += MP2T_PACKET_LENGTH;
36193         endIndex += MP2T_PACKET_LENGTH;
36194         continue;
36195       } // If we get here, we have somehow become de-synchronized and we need to step
36196       // forward one byte at a time until we find a pair of sync bytes that denote
36197       // a packet
36198 
36199 
36200       startIndex++;
36201       endIndex++;
36202     }
36203   };
36204   /**
36205    * walks through the segment data from the start and end to get timing information
36206    * for the first and last audio pes packets
36207    */
36208 
36209 
36210   var parseAudioPes_ = function parseAudioPes_(bytes, pmt, result) {
36211     var startIndex = 0,
36212         endIndex = MP2T_PACKET_LENGTH,
36213         packet,
36214         type,
36215         pesType,
36216         pusi,
36217         parsed;
36218     var endLoop = false; // Start walking from start of segment to get first audio packet
36219 
36220     while (endIndex <= bytes.byteLength) {
36221       // Look for a pair of start and end sync bytes in the data..
36222       if (bytes[startIndex] === SYNC_BYTE && (bytes[endIndex] === SYNC_BYTE || endIndex === bytes.byteLength)) {
36223         // We found a packet
36224         packet = bytes.subarray(startIndex, endIndex);
36225         type = probe$2.ts.parseType(packet, pmt.pid);
36226 
36227         switch (type) {
36228           case 'pes':
36229             pesType = probe$2.ts.parsePesType(packet, pmt.table);
36230             pusi = probe$2.ts.parsePayloadUnitStartIndicator(packet);
36231 
36232             if (pesType === 'audio' && pusi) {
36233               parsed = probe$2.ts.parsePesTime(packet);
36234 
36235               if (parsed) {
36236                 parsed.type = 'audio';
36237                 result.audio.push(parsed);
36238                 endLoop = true;
36239               }
36240             }
36241 
36242             break;
36243 
36244           default:
36245             break;
36246         }
36247 
36248         if (endLoop) {
36249           break;
36250         }
36251 
36252         startIndex += MP2T_PACKET_LENGTH;
36253         endIndex += MP2T_PACKET_LENGTH;
36254         continue;
36255       } // If we get here, we have somehow become de-synchronized and we need to step
36256       // forward one byte at a time until we find a pair of sync bytes that denote
36257       // a packet
36258 
36259 
36260       startIndex++;
36261       endIndex++;
36262     } // Start walking from end of segment to get last audio packet
36263 
36264 
36265     endIndex = bytes.byteLength;
36266     startIndex = endIndex - MP2T_PACKET_LENGTH;
36267     endLoop = false;
vendor: 5,143 bytes, lines 36268-36420
36268 
36269     while (startIndex >= 0) {
36270       // Look for a pair of start and end sync bytes in the data..
36271       if (bytes[startIndex] === SYNC_BYTE && (bytes[endIndex] === SYNC_BYTE || endIndex === bytes.byteLength)) {
36272         // We found a packet
36273         packet = bytes.subarray(startIndex, endIndex);
36274         type = probe$2.ts.parseType(packet, pmt.pid);
36275 
36276         switch (type) {
36277           case 'pes':
36278             pesType = probe$2.ts.parsePesType(packet, pmt.table);
36279             pusi = probe$2.ts.parsePayloadUnitStartIndicator(packet);
36280 
36281             if (pesType === 'audio' && pusi) {
36282               parsed = probe$2.ts.parsePesTime(packet);
36283 
36284               if (parsed) {
36285                 parsed.type = 'audio';
36286                 result.audio.push(parsed);
36287                 endLoop = true;
36288               }
36289             }
36290 
36291             break;
36292 
36293           default:
36294             break;
36295         }
36296 
36297         if (endLoop) {
36298           break;
36299         }
36300 
36301         startIndex -= MP2T_PACKET_LENGTH;
36302         endIndex -= MP2T_PACKET_LENGTH;
36303         continue;
36304       } // If we get here, we have somehow become de-synchronized and we need to step
36305       // forward one byte at a time until we find a pair of sync bytes that denote
36306       // a packet
36307 
36308 
36309       startIndex--;
36310       endIndex--;
36311     }
36312   };
36313   /**
36314    * walks through the segment data from the start and end to get timing information
36315    * for the first and last video pes packets as well as timing information for the first
36316    * key frame.
36317    */
36318 
36319 
36320   var parseVideoPes_ = function parseVideoPes_(bytes, pmt, result) {
36321     var startIndex = 0,
36322         endIndex = MP2T_PACKET_LENGTH,
36323         packet,
36324         type,
36325         pesType,
36326         pusi,
36327         parsed,
36328         frame,
36329         i,
36330         pes;
36331     var endLoop = false;
36332     var currentFrame = {
36333       data: [],
36334       size: 0
36335     }; // Start walking from start of segment to get first video packet
36336 
36337     while (endIndex < bytes.byteLength) {
36338       // Look for a pair of start and end sync bytes in the data..
36339       if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) {
36340         // We found a packet
36341         packet = bytes.subarray(startIndex, endIndex);
36342         type = probe$2.ts.parseType(packet, pmt.pid);
36343 
36344         switch (type) {
36345           case 'pes':
36346             pesType = probe$2.ts.parsePesType(packet, pmt.table);
36347             pusi = probe$2.ts.parsePayloadUnitStartIndicator(packet);
36348 
36349             if (pesType === 'video') {
36350               if (pusi && !endLoop) {
36351                 parsed = probe$2.ts.parsePesTime(packet);
36352 
36353                 if (parsed) {
36354                   parsed.type = 'video';
36355                   result.video.push(parsed);
36356                   endLoop = true;
36357                 }
36358               }
36359 
36360               if (!result.firstKeyFrame) {
36361                 if (pusi) {
36362                   if (currentFrame.size !== 0) {
36363                     frame = new Uint8Array(currentFrame.size);
36364                     i = 0;
36365 
36366                     while (currentFrame.data.length) {
36367                       pes = currentFrame.data.shift();
36368                       frame.set(pes, i);
36369                       i += pes.byteLength;
36370                     }
36371 
36372                     if (probe$2.ts.videoPacketContainsKeyFrame(frame)) {
36373                       var firstKeyFrame = probe$2.ts.parsePesTime(frame); // PTS/DTS may not be available. Simply *not* setting
36374                       // the keyframe seems to work fine with HLS playback
36375                       // and definitely preferable to a crash with TypeError...
36376 
36377                       if (firstKeyFrame) {
36378                         result.firstKeyFrame = firstKeyFrame;
36379                         result.firstKeyFrame.type = 'video';
36380                       } else {
36381                         // eslint-disable-next-line
36382                         console.warn('Failed to extract PTS/DTS from PES at first keyframe. ' + 'This could be an unusual TS segment, or else mux.js did not ' + 'parse your TS segment correctly. If you know your TS ' + 'segments do contain PTS/DTS on keyframes please file a bug ' + 'report! You can try ffprobe to double check for yourself.');
36383                       }
36384                     }
36385 
36386                     currentFrame.size = 0;
36387                   }
36388                 }
36389 
36390                 currentFrame.data.push(packet);
36391                 currentFrame.size += packet.byteLength;
36392               }
36393             }
36394 
36395             break;
36396 
36397           default:
36398             break;
36399         }
36400 
36401         if (endLoop && result.firstKeyFrame) {
36402           break;
36403         }
36404 
36405         startIndex += MP2T_PACKET_LENGTH;
36406         endIndex += MP2T_PACKET_LENGTH;
36407         continue;
36408       } // If we get here, we have somehow become de-synchronized and we need to step
36409       // forward one byte at a time until we find a pair of sync bytes that denote
36410       // a packet
36411 
36412 
36413       startIndex++;
36414       endIndex++;
36415     } // Start walking from end of segment to get last video packet
36416 
36417 
36418     endIndex = bytes.byteLength;
36419     startIndex = endIndex - MP2T_PACKET_LENGTH;
36420     endLoop = false;
vendor: 42,857 bytes, lines 36421-37735
36421 
36422     while (startIndex >= 0) {
36423       // Look for a pair of start and end sync bytes in the data..
36424       if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) {
36425         // We found a packet
36426         packet = bytes.subarray(startIndex, endIndex);
36427         type = probe$2.ts.parseType(packet, pmt.pid);
36428 
36429         switch (type) {
36430           case 'pes':
36431             pesType = probe$2.ts.parsePesType(packet, pmt.table);
36432             pusi = probe$2.ts.parsePayloadUnitStartIndicator(packet);
36433 
36434             if (pesType === 'video' && pusi) {
36435               parsed = probe$2.ts.parsePesTime(packet);
36436 
36437               if (parsed) {
36438                 parsed.type = 'video';
36439                 result.video.push(parsed);
36440                 endLoop = true;
36441               }
36442             }
36443 
36444             break;
36445 
36446           default:
36447             break;
36448         }
36449 
36450         if (endLoop) {
36451           break;
36452         }
36453 
36454         startIndex -= MP2T_PACKET_LENGTH;
36455         endIndex -= MP2T_PACKET_LENGTH;
36456         continue;
36457       } // If we get here, we have somehow become de-synchronized and we need to step
36458       // forward one byte at a time until we find a pair of sync bytes that denote
36459       // a packet
36460 
36461 
36462       startIndex--;
36463       endIndex--;
36464     }
36465   };
36466   /**
36467    * Adjusts the timestamp information for the segment to account for
36468    * rollover and convert to seconds based on pes packet timescale (90khz clock)
36469    */
36470 
36471 
36472   var adjustTimestamp_ = function adjustTimestamp_(segmentInfo, baseTimestamp) {
36473     if (segmentInfo.audio && segmentInfo.audio.length) {
36474       var audioBaseTimestamp = baseTimestamp;
36475 
36476       if (typeof audioBaseTimestamp === 'undefined') {
36477         audioBaseTimestamp = segmentInfo.audio[0].dts;
36478       }
36479 
36480       segmentInfo.audio.forEach(function (info) {
36481         info.dts = handleRollover$1(info.dts, audioBaseTimestamp);
36482         info.pts = handleRollover$1(info.pts, audioBaseTimestamp); // time in seconds
36483 
36484         info.dtsTime = info.dts / ONE_SECOND_IN_TS$1;
36485         info.ptsTime = info.pts / ONE_SECOND_IN_TS$1;
36486       });
36487     }
36488 
36489     if (segmentInfo.video && segmentInfo.video.length) {
36490       var videoBaseTimestamp = baseTimestamp;
36491 
36492       if (typeof videoBaseTimestamp === 'undefined') {
36493         videoBaseTimestamp = segmentInfo.video[0].dts;
36494       }
36495 
36496       segmentInfo.video.forEach(function (info) {
36497         info.dts = handleRollover$1(info.dts, videoBaseTimestamp);
36498         info.pts = handleRollover$1(info.pts, videoBaseTimestamp); // time in seconds
36499 
36500         info.dtsTime = info.dts / ONE_SECOND_IN_TS$1;
36501         info.ptsTime = info.pts / ONE_SECOND_IN_TS$1;
36502       });
36503 
36504       if (segmentInfo.firstKeyFrame) {
36505         var frame = segmentInfo.firstKeyFrame;
36506         frame.dts = handleRollover$1(frame.dts, videoBaseTimestamp);
36507         frame.pts = handleRollover$1(frame.pts, videoBaseTimestamp); // time in seconds
36508 
36509         frame.dtsTime = frame.dts / ONE_SECOND_IN_TS$1;
36510         frame.ptsTime = frame.dts / ONE_SECOND_IN_TS$1;
36511       }
36512     }
36513   };
36514   /**
36515    * inspects the aac data stream for start and end time information
36516    */
36517 
36518 
36519   var inspectAac_ = function inspectAac_(bytes) {
36520     var endLoop = false,
36521         audioCount = 0,
36522         sampleRate = null,
36523         timestamp = null,
36524         frameSize = 0,
36525         byteIndex = 0,
36526         packet;
36527 
36528     while (bytes.length - byteIndex >= 3) {
36529       var type = probe$2.aac.parseType(bytes, byteIndex);
36530 
36531       switch (type) {
36532         case 'timed-metadata':
36533           // Exit early because we don't have enough to parse
36534           // the ID3 tag header
36535           if (bytes.length - byteIndex < 10) {
36536             endLoop = true;
36537             break;
36538           }
36539 
36540           frameSize = probe$2.aac.parseId3TagSize(bytes, byteIndex); // Exit early if we don't have enough in the buffer
36541           // to emit a full packet
36542 
36543           if (frameSize > bytes.length) {
36544             endLoop = true;
36545             break;
36546           }
36547 
36548           if (timestamp === null) {
36549             packet = bytes.subarray(byteIndex, byteIndex + frameSize);
36550             timestamp = probe$2.aac.parseAacTimestamp(packet);
36551           }
36552 
36553           byteIndex += frameSize;
36554           break;
36555 
36556         case 'audio':
36557           // Exit early because we don't have enough to parse
36558           // the ADTS frame header
36559           if (bytes.length - byteIndex < 7) {
36560             endLoop = true;
36561             break;
36562           }
36563 
36564           frameSize = probe$2.aac.parseAdtsSize(bytes, byteIndex); // Exit early if we don't have enough in the buffer
36565           // to emit a full packet
36566 
36567           if (frameSize > bytes.length) {
36568             endLoop = true;
36569             break;
36570           }
36571 
36572           if (sampleRate === null) {
36573             packet = bytes.subarray(byteIndex, byteIndex + frameSize);
36574             sampleRate = probe$2.aac.parseSampleRate(packet);
36575           }
36576 
36577           audioCount++;
36578           byteIndex += frameSize;
36579           break;
36580 
36581         default:
36582           byteIndex++;
36583           break;
36584       }
36585 
36586       if (endLoop) {
36587         return null;
36588       }
36589     }
36590 
36591     if (sampleRate === null || timestamp === null) {
36592       return null;
36593     }
36594 
36595     var audioTimescale = ONE_SECOND_IN_TS$1 / sampleRate;
36596     var result = {
36597       audio: [{
36598         type: 'audio',
36599         dts: timestamp,
36600         pts: timestamp
36601       }, {
36602         type: 'audio',
36603         dts: timestamp + audioCount * 1024 * audioTimescale,
36604         pts: timestamp + audioCount * 1024 * audioTimescale
36605       }]
36606     };
36607     return result;
36608   };
36609   /**
36610    * inspects the transport stream segment data for start and end time information
36611    * of the audio and video tracks (when present) as well as the first key frame's
36612    * start time.
36613    */
36614 
36615 
36616   var inspectTs_ = function inspectTs_(bytes) {
36617     var pmt = {
36618       pid: null,
36619       table: null
36620     };
36621     var result = {};
36622     parsePsi_(bytes, pmt);
36623 
36624     for (var pid in pmt.table) {
36625       if (pmt.table.hasOwnProperty(pid)) {
36626         var type = pmt.table[pid];
36627 
36628         switch (type) {
36629           case streamTypes.H264_STREAM_TYPE:
36630             result.video = [];
36631             parseVideoPes_(bytes, pmt, result);
36632 
36633             if (result.video.length === 0) {
36634               delete result.video;
36635             }
36636 
36637             break;
36638 
36639           case streamTypes.ADTS_STREAM_TYPE:
36640             result.audio = [];
36641             parseAudioPes_(bytes, pmt, result);
36642 
36643             if (result.audio.length === 0) {
36644               delete result.audio;
36645             }
36646 
36647             break;
36648 
36649           default:
36650             break;
36651         }
36652       }
36653     }
36654 
36655     return result;
36656   };
36657   /**
36658    * Inspects segment byte data and returns an object with start and end timing information
36659    *
36660    * @param {Uint8Array} bytes The segment byte data
36661    * @param {Number} baseTimestamp Relative reference timestamp used when adjusting frame
36662    *  timestamps for rollover. This value must be in 90khz clock.
36663    * @return {Object} Object containing start and end frame timing info of segment.
36664    */
36665 
36666 
36667   var inspect = function inspect(bytes, baseTimestamp) {
36668     var isAacData = probe$2.aac.isLikelyAacData(bytes);
36669     var result;
36670 
36671     if (isAacData) {
36672       result = inspectAac_(bytes);
36673     } else {
36674       result = inspectTs_(bytes);
36675     }
36676 
36677     if (!result || !result.audio && !result.video) {
36678       return null;
36679     }
36680 
36681     adjustTimestamp_(result, baseTimestamp);
36682     return result;
36683   };
36684 
36685   var tsInspector = {
36686     inspect: inspect,
36687     parseAudioPes_: parseAudioPes_
36688   };
36689 
36690   /*
36691    * pkcs7.pad
36692    * https://github.com/brightcove/pkcs7
36693    *
36694    * Copyright (c) 2014 Brightcove
36695    * Licensed under the apache2 license.
36696    */
36697   /**
36698    * Returns the subarray of a Uint8Array without PKCS#7 padding.
36699    * @param padded {Uint8Array} unencrypted bytes that have been padded
36700    * @return {Uint8Array} the unpadded bytes
36701    * @see http://tools.ietf.org/html/rfc5652
36702    */
36703 
36704   function unpad(padded) {
36705     return padded.subarray(0, padded.byteLength - padded[padded.byteLength - 1]);
36706   }
36707 
36708   var classCallCheck = function classCallCheck(instance, Constructor) {
36709     if (!(instance instanceof Constructor)) {
36710       throw new TypeError("Cannot call a class as a function");
36711     }
36712   };
36713 
36714   var createClass = function () {
36715     function defineProperties(target, props) {
36716       for (var i = 0; i < props.length; i++) {
36717         var descriptor = props[i];
36718         descriptor.enumerable = descriptor.enumerable || false;
36719         descriptor.configurable = true;
36720         if ("value" in descriptor) descriptor.writable = true;
36721         Object.defineProperty(target, descriptor.key, descriptor);
36722       }
36723     }
36724 
36725     return function (Constructor, protoProps, staticProps) {
36726       if (protoProps) defineProperties(Constructor.prototype, protoProps);
36727       if (staticProps) defineProperties(Constructor, staticProps);
36728       return Constructor;
36729     };
36730   }();
36731 
36732   var inherits$1 = function inherits(subClass, superClass) {
36733     if (typeof superClass !== "function" && superClass !== null) {
36734       throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
36735     }
36736 
36737     subClass.prototype = Object.create(superClass && superClass.prototype, {
36738       constructor: {
36739         value: subClass,
36740         enumerable: false,
36741         writable: true,
36742         configurable: true
36743       }
36744     });
36745     if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
36746   };
36747 
36748   var possibleConstructorReturn = function possibleConstructorReturn(self, call) {
36749     if (!self) {
36750       throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
36751     }
36752 
36753     return call && (typeof call === "object" || typeof call === "function") ? call : self;
36754   };
36755   /**
36756    * @file aes.js
36757    *
36758    * This file contains an adaptation of the AES decryption algorithm
36759    * from the Standford Javascript Cryptography Library. That work is
36760    * covered by the following copyright and permissions notice:
36761    *
36762    * Copyright 2009-2010 Emily Stark, Mike Hamburg, Dan Boneh.
36763    * All rights reserved.
36764    *
36765    * Redistribution and use in source and binary forms, with or without
36766    * modification, are permitted provided that the following conditions are
36767    * met:
36768    *
36769    * 1. Redistributions of source code must retain the above copyright
36770    *    notice, this list of conditions and the following disclaimer.
36771    *
36772    * 2. Redistributions in binary form must reproduce the above
36773    *    copyright notice, this list of conditions and the following
36774    *    disclaimer in the documentation and/or other materials provided
36775    *    with the distribution.
36776    *
36777    * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
36778    * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
36779    * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
36780    * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR CONTRIBUTORS BE
36781    * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
36782    * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
36783    * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
36784    * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
36785    * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
36786    * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
36787    * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36788    *
36789    * The views and conclusions contained in the software and documentation
36790    * are those of the authors and should not be interpreted as representing
36791    * official policies, either expressed or implied, of the authors.
36792    */
36793 
36794   /**
36795    * Expand the S-box tables.
36796    *
36797    * @private
36798    */
36799 
36800 
36801   var precompute = function precompute() {
36802     var tables = [[[], [], [], [], []], [[], [], [], [], []]];
36803     var encTable = tables[0];
36804     var decTable = tables[1];
36805     var sbox = encTable[4];
36806     var sboxInv = decTable[4];
36807     var i = void 0;
36808     var x = void 0;
36809     var xInv = void 0;
36810     var d = [];
36811     var th = [];
36812     var x2 = void 0;
36813     var x4 = void 0;
36814     var x8 = void 0;
36815     var s = void 0;
36816     var tEnc = void 0;
36817     var tDec = void 0; // Compute double and third tables
36818 
36819     for (i = 0; i < 256; i++) {
36820       th[(d[i] = i << 1 ^ (i >> 7) * 283) ^ i] = i;
36821     }
36822 
36823     for (x = xInv = 0; !sbox[x]; x ^= x2 || 1, xInv = th[xInv] || 1) {
36824       // Compute sbox
36825       s = xInv ^ xInv << 1 ^ xInv << 2 ^ xInv << 3 ^ xInv << 4;
36826       s = s >> 8 ^ s & 255 ^ 99;
36827       sbox[x] = s;
36828       sboxInv[s] = x; // Compute MixColumns
36829 
36830       x8 = d[x4 = d[x2 = d[x]]];
36831       tDec = x8 * 0x1010101 ^ x4 * 0x10001 ^ x2 * 0x101 ^ x * 0x1010100;
36832       tEnc = d[s] * 0x101 ^ s * 0x1010100;
36833 
36834       for (i = 0; i < 4; i++) {
36835         encTable[i][x] = tEnc = tEnc << 24 ^ tEnc >>> 8;
36836         decTable[i][s] = tDec = tDec << 24 ^ tDec >>> 8;
36837       }
36838     } // Compactify. Considerable speedup on Firefox.
36839 
36840 
36841     for (i = 0; i < 5; i++) {
36842       encTable[i] = encTable[i].slice(0);
36843       decTable[i] = decTable[i].slice(0);
36844     }
36845 
36846     return tables;
36847   };
36848 
36849   var aesTables = null;
36850   /**
36851    * Schedule out an AES key for both encryption and decryption. This
36852    * is a low-level class. Use a cipher mode to do bulk encryption.
36853    *
36854    * @class AES
36855    * @param key {Array} The key as an array of 4, 6 or 8 words.
36856    */
36857 
36858   var AES = function () {
36859     function AES(key) {
36860       classCallCheck(this, AES);
36861       /**
36862        * The expanded S-box and inverse S-box tables. These will be computed
36863        * on the client so that we don't have to send them down the wire.
36864        *
36865        * There are two tables, _tables[0] is for encryption and
36866        * _tables[1] is for decryption.
36867        *
36868        * The first 4 sub-tables are the expanded S-box with MixColumns. The
36869        * last (_tables[01][4]) is the S-box itself.
36870        *
36871        * @private
36872        */
36873       // if we have yet to precompute the S-box tables
36874       // do so now
36875 
36876       if (!aesTables) {
36877         aesTables = precompute();
36878       } // then make a copy of that object for use
36879 
36880 
36881       this._tables = [[aesTables[0][0].slice(), aesTables[0][1].slice(), aesTables[0][2].slice(), aesTables[0][3].slice(), aesTables[0][4].slice()], [aesTables[1][0].slice(), aesTables[1][1].slice(), aesTables[1][2].slice(), aesTables[1][3].slice(), aesTables[1][4].slice()]];
36882       var i = void 0;
36883       var j = void 0;
36884       var tmp = void 0;
36885       var encKey = void 0;
36886       var decKey = void 0;
36887       var sbox = this._tables[0][4];
36888       var decTable = this._tables[1];
36889       var keyLen = key.length;
36890       var rcon = 1;
36891 
36892       if (keyLen !== 4 && keyLen !== 6 && keyLen !== 8) {
36893         throw new Error('Invalid aes key size');
36894       }
36895 
36896       encKey = key.slice(0);
36897       decKey = [];
36898       this._key = [encKey, decKey]; // schedule encryption keys
36899 
36900       for (i = keyLen; i < 4 * keyLen + 28; i++) {
36901         tmp = encKey[i - 1]; // apply sbox
36902 
36903         if (i % keyLen === 0 || keyLen === 8 && i % keyLen === 4) {
36904           tmp = sbox[tmp >>> 24] << 24 ^ sbox[tmp >> 16 & 255] << 16 ^ sbox[tmp >> 8 & 255] << 8 ^ sbox[tmp & 255]; // shift rows and add rcon
36905 
36906           if (i % keyLen === 0) {
36907             tmp = tmp << 8 ^ tmp >>> 24 ^ rcon << 24;
36908             rcon = rcon << 1 ^ (rcon >> 7) * 283;
36909           }
36910         }
36911 
36912         encKey[i] = encKey[i - keyLen] ^ tmp;
36913       } // schedule decryption keys
36914 
36915 
36916       for (j = 0; i; j++, i--) {
36917         tmp = encKey[j & 3 ? i : i - 4];
36918 
36919         if (i <= 4 || j < 4) {
36920           decKey[j] = tmp;
36921         } else {
36922           decKey[j] = decTable[0][sbox[tmp >>> 24]] ^ decTable[1][sbox[tmp >> 16 & 255]] ^ decTable[2][sbox[tmp >> 8 & 255]] ^ decTable[3][sbox[tmp & 255]];
36923         }
36924       }
36925     }
36926     /**
36927      * Decrypt 16 bytes, specified as four 32-bit words.
36928      *
36929      * @param {Number} encrypted0 the first word to decrypt
36930      * @param {Number} encrypted1 the second word to decrypt
36931      * @param {Number} encrypted2 the third word to decrypt
36932      * @param {Number} encrypted3 the fourth word to decrypt
36933      * @param {Int32Array} out the array to write the decrypted words
36934      * into
36935      * @param {Number} offset the offset into the output array to start
36936      * writing results
36937      * @return {Array} The plaintext.
36938      */
36939 
36940 
36941     AES.prototype.decrypt = function decrypt(encrypted0, encrypted1, encrypted2, encrypted3, out, offset) {
36942       var key = this._key[1]; // state variables a,b,c,d are loaded with pre-whitened data
36943 
36944       var a = encrypted0 ^ key[0];
36945       var b = encrypted3 ^ key[1];
36946       var c = encrypted2 ^ key[2];
36947       var d = encrypted1 ^ key[3];
36948       var a2 = void 0;
36949       var b2 = void 0;
36950       var c2 = void 0; // key.length === 2 ?
36951 
36952       var nInnerRounds = key.length / 4 - 2;
36953       var i = void 0;
36954       var kIndex = 4;
36955       var table = this._tables[1]; // load up the tables
36956 
36957       var table0 = table[0];
36958       var table1 = table[1];
36959       var table2 = table[2];
36960       var table3 = table[3];
36961       var sbox = table[4]; // Inner rounds. Cribbed from OpenSSL.
36962 
36963       for (i = 0; i < nInnerRounds; i++) {
36964         a2 = table0[a >>> 24] ^ table1[b >> 16 & 255] ^ table2[c >> 8 & 255] ^ table3[d & 255] ^ key[kIndex];
36965         b2 = table0[b >>> 24] ^ table1[c >> 16 & 255] ^ table2[d >> 8 & 255] ^ table3[a & 255] ^ key[kIndex + 1];
36966         c2 = table0[c >>> 24] ^ table1[d >> 16 & 255] ^ table2[a >> 8 & 255] ^ table3[b & 255] ^ key[kIndex + 2];
36967         d = table0[d >>> 24] ^ table1[a >> 16 & 255] ^ table2[b >> 8 & 255] ^ table3[c & 255] ^ key[kIndex + 3];
36968         kIndex += 4;
36969         a = a2;
36970         b = b2;
36971         c = c2;
36972       } // Last round.
36973 
36974 
36975       for (i = 0; i < 4; i++) {
36976         out[(3 & -i) + offset] = sbox[a >>> 24] << 24 ^ sbox[b >> 16 & 255] << 16 ^ sbox[c >> 8 & 255] << 8 ^ sbox[d & 255] ^ key[kIndex++];
36977         a2 = a;
36978         a = b;
36979         b = c;
36980         c = d;
36981         d = a2;
36982       }
36983     };
36984 
36985     return AES;
36986   }();
36987   /**
36988    * @file stream.js
36989    */
36990 
36991   /**
36992    * A lightweight readable stream implemention that handles event dispatching.
36993    *
36994    * @class Stream
36995    */
36996 
36997 
36998   var Stream$2 = function () {
36999     function Stream() {
37000       classCallCheck(this, Stream);
37001       this.listeners = {};
37002     }
37003     /**
37004      * Add a listener for a specified event type.
37005      *
37006      * @param {String} type the event name
37007      * @param {Function} listener the callback to be invoked when an event of
37008      * the specified type occurs
37009      */
37010 
37011 
37012     Stream.prototype.on = function on(type, listener) {
37013       if (!this.listeners[type]) {
37014         this.listeners[type] = [];
37015       }
37016 
37017       this.listeners[type].push(listener);
37018     };
37019     /**
37020      * Remove a listener for a specified event type.
37021      *
37022      * @param {String} type the event name
37023      * @param {Function} listener  a function previously registered for this
37024      * type of event through `on`
37025      * @return {Boolean} if we could turn it off or not
37026      */
37027 
37028 
37029     Stream.prototype.off = function off(type, listener) {
37030       if (!this.listeners[type]) {
37031         return false;
37032       }
37033 
37034       var index = this.listeners[type].indexOf(listener);
37035       this.listeners[type].splice(index, 1);
37036       return index > -1;
37037     };
37038     /**
37039      * Trigger an event of the specified type on this stream. Any additional
37040      * arguments to this function are passed as parameters to event listeners.
37041      *
37042      * @param {String} type the event name
37043      */
37044 
37045 
37046     Stream.prototype.trigger = function trigger(type) {
37047       var callbacks = this.listeners[type];
37048 
37049       if (!callbacks) {
37050         return;
37051       } // Slicing the arguments on every invocation of this method
37052       // can add a significant amount of overhead. Avoid the
37053       // intermediate object creation for the common case of a
37054       // single callback argument
37055 
37056 
37057       if (arguments.length === 2) {
37058         var length = callbacks.length;
37059 
37060         for (var i = 0; i < length; ++i) {
37061           callbacks[i].call(this, arguments[1]);
37062         }
37063       } else {
37064         var args = Array.prototype.slice.call(arguments, 1);
37065         var _length = callbacks.length;
37066 
37067         for (var _i = 0; _i < _length; ++_i) {
37068           callbacks[_i].apply(this, args);
37069         }
37070       }
37071     };
37072     /**
37073      * Destroys the stream and cleans up.
37074      */
37075 
37076 
37077     Stream.prototype.dispose = function dispose() {
37078       this.listeners = {};
37079     };
37080     /**
37081      * Forwards all `data` events on this stream to the destination stream. The
37082      * destination stream should provide a method `push` to receive the data
37083      * events as they arrive.
37084      *
37085      * @param {Stream} destination the stream that will receive all `data` events
37086      * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
37087      */
37088 
37089 
37090     Stream.prototype.pipe = function pipe(destination) {
37091       this.on('data', function (data) {
37092         destination.push(data);
37093       });
37094     };
37095 
37096     return Stream;
37097   }();
37098   /**
37099    * @file async-stream.js
37100    */
37101 
37102   /**
37103    * A wrapper around the Stream class to use setTiemout
37104    * and run stream "jobs" Asynchronously
37105    *
37106    * @class AsyncStream
37107    * @extends Stream
37108    */
37109 
37110 
37111   var AsyncStream = function (_Stream) {
37112     inherits$1(AsyncStream, _Stream);
37113 
37114     function AsyncStream() {
37115       classCallCheck(this, AsyncStream);
37116 
37117       var _this = possibleConstructorReturn(this, _Stream.call(this, Stream$2));
37118 
37119       _this.jobs = [];
37120       _this.delay = 1;
37121       _this.timeout_ = null;
37122       return _this;
37123     }
37124     /**
37125      * process an async job
37126      *
37127      * @private
37128      */
37129 
37130 
37131     AsyncStream.prototype.processJob_ = function processJob_() {
37132       this.jobs.shift()();
37133 
37134       if (this.jobs.length) {
37135         this.timeout_ = setTimeout(this.processJob_.bind(this), this.delay);
37136       } else {
37137         this.timeout_ = null;
37138       }
37139     };
37140     /**
37141      * push a job into the stream
37142      *
37143      * @param {Function} job the job to push into the stream
37144      */
37145 
37146 
37147     AsyncStream.prototype.push = function push(job) {
37148       this.jobs.push(job);
37149 
37150       if (!this.timeout_) {
37151         this.timeout_ = setTimeout(this.processJob_.bind(this), this.delay);
37152       }
37153     };
37154 
37155     return AsyncStream;
37156   }(Stream$2);
37157   /**
37158    * @file decrypter.js
37159    *
37160    * An asynchronous implementation of AES-128 CBC decryption with
37161    * PKCS#7 padding.
37162    */
37163 
37164   /**
37165    * Convert network-order (big-endian) bytes into their little-endian
37166    * representation.
37167    */
37168 
37169 
37170   var ntoh = function ntoh(word) {
37171     return word << 24 | (word & 0xff00) << 8 | (word & 0xff0000) >> 8 | word >>> 24;
37172   };
37173   /**
37174    * Decrypt bytes using AES-128 with CBC and PKCS#7 padding.
37175    *
37176    * @param {Uint8Array} encrypted the encrypted bytes
37177    * @param {Uint32Array} key the bytes of the decryption key
37178    * @param {Uint32Array} initVector the initialization vector (IV) to
37179    * use for the first round of CBC.
37180    * @return {Uint8Array} the decrypted bytes
37181    *
37182    * @see http://en.wikipedia.org/wiki/Advanced_Encryption_Standard
37183    * @see http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_Block_Chaining_.28CBC.29
37184    * @see https://tools.ietf.org/html/rfc2315
37185    */
37186 
37187 
37188   var decrypt = function decrypt(encrypted, key, initVector) {
37189     // word-level access to the encrypted bytes
37190     var encrypted32 = new Int32Array(encrypted.buffer, encrypted.byteOffset, encrypted.byteLength >> 2);
37191     var decipher = new AES(Array.prototype.slice.call(key)); // byte and word-level access for the decrypted output
37192 
37193     var decrypted = new Uint8Array(encrypted.byteLength);
37194     var decrypted32 = new Int32Array(decrypted.buffer); // temporary variables for working with the IV, encrypted, and
37195     // decrypted data
37196 
37197     var init0 = void 0;
37198     var init1 = void 0;
37199     var init2 = void 0;
37200     var init3 = void 0;
37201     var encrypted0 = void 0;
37202     var encrypted1 = void 0;
37203     var encrypted2 = void 0;
37204     var encrypted3 = void 0; // iteration variable
37205 
37206     var wordIx = void 0; // pull out the words of the IV to ensure we don't modify the
37207     // passed-in reference and easier access
37208 
37209     init0 = initVector[0];
37210     init1 = initVector[1];
37211     init2 = initVector[2];
37212     init3 = initVector[3]; // decrypt four word sequences, applying cipher-block chaining (CBC)
37213     // to each decrypted block
37214 
37215     for (wordIx = 0; wordIx < encrypted32.length; wordIx += 4) {
37216       // convert big-endian (network order) words into little-endian
37217       // (javascript order)
37218       encrypted0 = ntoh(encrypted32[wordIx]);
37219       encrypted1 = ntoh(encrypted32[wordIx + 1]);
37220       encrypted2 = ntoh(encrypted32[wordIx + 2]);
37221       encrypted3 = ntoh(encrypted32[wordIx + 3]); // decrypt the block
37222 
37223       decipher.decrypt(encrypted0, encrypted1, encrypted2, encrypted3, decrypted32, wordIx); // XOR with the IV, and restore network byte-order to obtain the
37224       // plaintext
37225 
37226       decrypted32[wordIx] = ntoh(decrypted32[wordIx] ^ init0);
37227       decrypted32[wordIx + 1] = ntoh(decrypted32[wordIx + 1] ^ init1);
37228       decrypted32[wordIx + 2] = ntoh(decrypted32[wordIx + 2] ^ init2);
37229       decrypted32[wordIx + 3] = ntoh(decrypted32[wordIx + 3] ^ init3); // setup the IV for the next round
37230 
37231       init0 = encrypted0;
37232       init1 = encrypted1;
37233       init2 = encrypted2;
37234       init3 = encrypted3;
37235     }
37236 
37237     return decrypted;
37238   };
37239   /**
37240    * The `Decrypter` class that manages decryption of AES
37241    * data through `AsyncStream` objects and the `decrypt`
37242    * function
37243    *
37244    * @param {Uint8Array} encrypted the encrypted bytes
37245    * @param {Uint32Array} key the bytes of the decryption key
37246    * @param {Uint32Array} initVector the initialization vector (IV) to
37247    * @param {Function} done the function to run when done
37248    * @class Decrypter
37249    */
37250 
37251 
37252   var Decrypter = function () {
37253     function Decrypter(encrypted, key, initVector, done) {
37254       classCallCheck(this, Decrypter);
37255       var step = Decrypter.STEP;
37256       var encrypted32 = new Int32Array(encrypted.buffer);
37257       var decrypted = new Uint8Array(encrypted.byteLength);
37258       var i = 0;
37259       this.asyncStream_ = new AsyncStream(); // split up the encryption job and do the individual chunks asynchronously
37260 
37261       this.asyncStream_.push(this.decryptChunk_(encrypted32.subarray(i, i + step), key, initVector, decrypted));
37262 
37263       for (i = step; i < encrypted32.length; i += step) {
37264         initVector = new Uint32Array([ntoh(encrypted32[i - 4]), ntoh(encrypted32[i - 3]), ntoh(encrypted32[i - 2]), ntoh(encrypted32[i - 1])]);
37265         this.asyncStream_.push(this.decryptChunk_(encrypted32.subarray(i, i + step), key, initVector, decrypted));
37266       } // invoke the done() callback when everything is finished
37267 
37268 
37269       this.asyncStream_.push(function () {
37270         // remove pkcs#7 padding from the decrypted bytes
37271         done(null, unpad(decrypted));
37272       });
37273     }
37274     /**
37275      * a getter for step the maximum number of bytes to process at one time
37276      *
37277      * @return {Number} the value of step 32000
37278      */
37279 
37280     /**
37281      * @private
37282      */
37283 
37284 
37285     Decrypter.prototype.decryptChunk_ = function decryptChunk_(encrypted, key, initVector, decrypted) {
37286       return function () {
37287         var bytes = decrypt(encrypted, key, initVector);
37288         decrypted.set(bytes, encrypted.byteOffset);
37289       };
37290     };
37291 
37292     createClass(Decrypter, null, [{
37293       key: 'STEP',
37294       get: function get$$1() {
37295         // 4 * 8000;
37296         return 32000;
37297       }
37298     }]);
37299     return Decrypter;
37300   }();
37301 
37302   /**
37303    * @videojs/http-streaming
37304    * @version 1.11.2
37305    * @copyright 2019 Brightcove, Inc
37306    * @license Apache-2.0
37307    */
37308   /**
37309    * @file resolve-url.js - Handling how URLs are resolved and manipulated
37310    */
37311 
37312   var resolveUrl$1 = function resolveUrl(baseURL, relativeURL) {
37313     // return early if we don't need to resolve
37314     if (/^[a-z]+:/i.test(relativeURL)) {
37315       return relativeURL;
37316     } // if the base URL is relative then combine with the current location
37317 
37318 
37319     if (!/\/\//i.test(baseURL)) {
37320       baseURL = urlToolkit.buildAbsoluteURL(window$1.location.href, baseURL);
37321     }
37322 
37323     return urlToolkit.buildAbsoluteURL(baseURL, relativeURL);
37324   };
37325   /**
37326    * Checks whether xhr request was redirected and returns correct url depending
37327    * on `handleManifestRedirects` option
37328    *
37329    * @api private
37330    *
37331    * @param  {String} url - an url being requested
37332    * @param  {XMLHttpRequest} req - xhr request result
37333    *
37334    * @return {String}
37335    */
37336 
37337 
37338   var resolveManifestRedirect = function resolveManifestRedirect(handleManifestRedirect, url, req) {
37339     // To understand how the responseURL below is set and generated:
37340     // - https://fetch.spec.whatwg.org/#concept-response-url
37341     // - https://fetch.spec.whatwg.org/#atomic-http-redirect-handling
37342     if (handleManifestRedirect && req.responseURL && url !== req.responseURL) {
37343       return req.responseURL;
37344     }
37345 
37346     return url;
37347   };
37348 
37349   var classCallCheck$1 = function classCallCheck(instance, Constructor) {
37350     if (!(instance instanceof Constructor)) {
37351       throw new TypeError("Cannot call a class as a function");
37352     }
37353   };
37354 
37355   var createClass$1 = function () {
37356     function defineProperties(target, props) {
37357       for (var i = 0; i < props.length; i++) {
37358         var descriptor = props[i];
37359         descriptor.enumerable = descriptor.enumerable || false;
37360         descriptor.configurable = true;
37361         if ("value" in descriptor) descriptor.writable = true;
37362         Object.defineProperty(target, descriptor.key, descriptor);
37363       }
37364     }
37365 
37366     return function (Constructor, protoProps, staticProps) {
37367       if (protoProps) defineProperties(Constructor.prototype, protoProps);
37368       if (staticProps) defineProperties(Constructor, staticProps);
37369       return Constructor;
37370     };
37371   }();
37372 
37373   var get$1 = function get(object, property, receiver) {
37374     if (object === null) object = Function.prototype;
37375     var desc = Object.getOwnPropertyDescriptor(object, property);
37376 
37377     if (desc === undefined) {
37378       var parent = Object.getPrototypeOf(object);
37379 
37380       if (parent === null) {
37381         return undefined;
37382       } else {
37383         return get(parent, property, receiver);
37384       }
37385     } else if ("value" in desc) {
37386       return desc.value;
37387     } else {
37388       var getter = desc.get;
37389 
37390       if (getter === undefined) {
37391         return undefined;
37392       }
37393 
37394       return getter.call(receiver);
37395     }
37396   };
37397 
37398   var inherits$2 = function inherits(subClass, superClass) {
37399     if (typeof superClass !== "function" && superClass !== null) {
37400       throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
37401     }
37402 
37403     subClass.prototype = Object.create(superClass && superClass.prototype, {
37404       constructor: {
37405         value: subClass,
37406         enumerable: false,
37407         writable: true,
37408         configurable: true
37409       }
37410     });
37411     if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
37412   };
37413 
37414   var possibleConstructorReturn$1 = function possibleConstructorReturn(self, call) {
37415     if (!self) {
37416       throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
37417     }
37418 
37419     return call && (typeof call === "object" || typeof call === "function") ? call : self;
37420   };
37421 
37422   var slicedToArray = function () {
37423     function sliceIterator(arr, i) {
37424       var _arr = [];
37425       var _n = true;
37426       var _d = false;
37427       var _e = undefined;
37428 
37429       try {
37430         for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
37431           _arr.push(_s.value);
37432 
37433           if (i && _arr.length === i) break;
37434         }
37435       } catch (err) {
37436         _d = true;
37437         _e = err;
37438       } finally {
37439         try {
37440           if (!_n && _i["return"]) _i["return"]();
37441         } finally {
37442           if (_d) throw _e;
37443         }
37444       }
37445 
37446       return _arr;
37447     }
37448 
37449     return function (arr, i) {
37450       if (Array.isArray(arr)) {
37451         return arr;
37452       } else if (Symbol.iterator in Object(arr)) {
37453         return sliceIterator(arr, i);
37454       } else {
37455         throw new TypeError("Invalid attempt to destructure non-iterable instance");
37456       }
37457     };
37458   }();
37459   /**
37460    * @file playlist-loader.js
37461    *
37462    * A state machine that manages the loading, caching, and updating of
37463    * M3U8 playlists.
37464    *
37465    */
37466 
37467 
37468   var mergeOptions$1 = videojs$1.mergeOptions,
37469       EventTarget$1 = videojs$1.EventTarget,
37470       log$1 = videojs$1.log;
37471   /**
37472    * Loops through all supported media groups in master and calls the provided
37473    * callback for each group
37474    *
37475    * @param {Object} master
37476    *        The parsed master manifest object
37477    * @param {Function} callback
37478    *        Callback to call for each media group
37479    */
37480 
37481   var forEachMediaGroup = function forEachMediaGroup(master, callback) {
37482     ['AUDIO', 'SUBTITLES'].forEach(function (mediaType) {
37483       for (var groupKey in master.mediaGroups[mediaType]) {
37484         for (var labelKey in master.mediaGroups[mediaType][groupKey]) {
37485           var mediaProperties = master.mediaGroups[mediaType][groupKey][labelKey];
37486           callback(mediaProperties, mediaType, groupKey, labelKey);
37487         }
37488       }
37489     });
37490   };
37491   /**
37492     * Returns a new array of segments that is the result of merging
37493     * properties from an older list of segments onto an updated
37494     * list. No properties on the updated playlist will be overridden.
37495     *
37496     * @param {Array} original the outdated list of segments
37497     * @param {Array} update the updated list of segments
37498     * @param {Number=} offset the index of the first update
37499     * segment in the original segment list. For non-live playlists,
37500     * this should always be zero and does not need to be
37501     * specified. For live playlists, it should be the difference
37502     * between the media sequence numbers in the original and updated
37503     * playlists.
37504     * @return a list of merged segment objects
37505     */
37506 
37507 
37508   var updateSegments = function updateSegments(original, update, offset) {
37509     var result = update.slice();
37510     offset = offset || 0;
37511     var length = Math.min(original.length, update.length + offset);
37512 
37513     for (var i = offset; i < length; i++) {
37514       result[i - offset] = mergeOptions$1(original[i], result[i - offset]);
37515     }
37516 
37517     return result;
37518   };
37519 
37520   var resolveSegmentUris = function resolveSegmentUris(segment, baseUri) {
37521     if (!segment.resolvedUri) {
37522       segment.resolvedUri = resolveUrl$1(baseUri, segment.uri);
37523     }
37524 
37525     if (segment.key && !segment.key.resolvedUri) {
37526       segment.key.resolvedUri = resolveUrl$1(baseUri, segment.key.uri);
37527     }
37528 
37529     if (segment.map && !segment.map.resolvedUri) {
37530       segment.map.resolvedUri = resolveUrl$1(baseUri, segment.map.uri);
37531     }
37532   };
37533   /**
37534     * Returns a new master playlist that is the result of merging an
37535     * updated media playlist into the original version. If the
37536     * updated media playlist does not match any of the playlist
37537     * entries in the original master playlist, null is returned.
37538     *
37539     * @param {Object} master a parsed master M3U8 object
37540     * @param {Object} media a parsed media M3U8 object
37541     * @return {Object} a new object that represents the original
37542     * master playlist with the updated media playlist merged in, or
37543     * null if the merge produced no change.
37544     */
37545 
37546 
37547   var updateMaster = function updateMaster(master, media) {
37548     var result = mergeOptions$1(master, {});
37549     var playlist = result.playlists[media.uri];
37550 
37551     if (!playlist) {
37552       return null;
37553     } // consider the playlist unchanged if the number of segments is equal, the media
37554     // sequence number is unchanged, and this playlist hasn't become the end of the playlist
37555 
37556 
37557     if (playlist.segments && media.segments && playlist.segments.length === media.segments.length && playlist.endList === media.endList && playlist.mediaSequence === media.mediaSequence) {
37558       return null;
37559     }
37560 
37561     var mergedPlaylist = mergeOptions$1(playlist, media); // if the update could overlap existing segment information, merge the two segment lists
37562 
37563     if (playlist.segments) {
37564       mergedPlaylist.segments = updateSegments(playlist.segments, media.segments, media.mediaSequence - playlist.mediaSequence);
37565     } // resolve any segment URIs to prevent us from having to do it later
37566 
37567 
37568     mergedPlaylist.segments.forEach(function (segment) {
37569       resolveSegmentUris(segment, mergedPlaylist.resolvedUri);
37570     }); // TODO Right now in the playlists array there are two references to each playlist, one
37571     // that is referenced by index, and one by URI. The index reference may no longer be
37572     // necessary.
37573 
37574     for (var i = 0; i < result.playlists.length; i++) {
37575       if (result.playlists[i].uri === media.uri) {
37576         result.playlists[i] = mergedPlaylist;
37577       }
37578     }
37579 
37580     result.playlists[media.uri] = mergedPlaylist;
37581     return result;
37582   };
37583 
37584   var setupMediaPlaylists = function setupMediaPlaylists(master) {
37585     // setup by-URI lookups and resolve media playlist URIs
37586     var i = master.playlists.length;
37587 
37588     while (i--) {
37589       var playlist = master.playlists[i];
37590       master.playlists[playlist.uri] = playlist;
37591       playlist.resolvedUri = resolveUrl$1(master.uri, playlist.uri);
37592       playlist.id = i;
37593 
37594       if (!playlist.attributes) {
37595         // Although the spec states an #EXT-X-STREAM-INF tag MUST have a
37596         // BANDWIDTH attribute, we can play the stream without it. This means a poorly
37597         // formatted master playlist may not have an attribute list. An attributes
37598         // property is added here to prevent undefined references when we encounter
37599         // this scenario.
37600         playlist.attributes = {};
37601         log$1.warn('Invalid playlist STREAM-INF detected. Missing BANDWIDTH attribute.');
37602       }
37603     }
37604   };
37605 
37606   var resolveMediaGroupUris = function resolveMediaGroupUris(master) {
37607     forEachMediaGroup(master, function (properties) {
37608       if (properties.uri) {
37609         properties.resolvedUri = resolveUrl$1(master.uri, properties.uri);
37610       }
37611     });
37612   };
37613   /**
37614    * Calculates the time to wait before refreshing a live playlist
37615    *
37616    * @param {Object} media
37617    *        The current media
37618    * @param {Boolean} update
37619    *        True if there were any updates from the last refresh, false otherwise
37620    * @return {Number}
37621    *         The time in ms to wait before refreshing the live playlist
37622    */
37623 
37624 
37625   var refreshDelay = function refreshDelay(media, update) {
37626     var lastSegment = media.segments[media.segments.length - 1];
37627     var delay = void 0;
37628 
37629     if (update && lastSegment && lastSegment.duration) {
37630       delay = lastSegment.duration * 1000;
37631     } else {
37632       // if the playlist is unchanged since the last reload or last segment duration
37633       // cannot be determined, try again after half the target duration
37634       delay = (media.targetDuration || 10) * 500;
37635     }
37636 
37637     return delay;
37638   };
37639   /**
37640    * Load a playlist from a remote location
37641    *
37642    * @class PlaylistLoader
37643    * @extends Stream
37644    * @param {String} srcUrl the url to start with
37645    * @param {Boolean} withCredentials the withCredentials xhr option
37646    * @constructor
37647    */
37648 
37649 
37650   var PlaylistLoader = function (_EventTarget) {
37651     inherits$2(PlaylistLoader, _EventTarget);
37652 
37653     function PlaylistLoader(srcUrl, hls) {
37654       var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
37655       classCallCheck$1(this, PlaylistLoader);
37656 
37657       var _this = possibleConstructorReturn$1(this, (PlaylistLoader.__proto__ || Object.getPrototypeOf(PlaylistLoader)).call(this));
37658 
37659       var _options$withCredenti = options.withCredentials,
37660           withCredentials = _options$withCredenti === undefined ? false : _options$withCredenti,
37661           _options$handleManife = options.handleManifestRedirects,
37662           handleManifestRedirects = _options$handleManife === undefined ? false : _options$handleManife;
37663       _this.srcUrl = srcUrl;
37664       _this.hls_ = hls;
37665       _this.withCredentials = withCredentials;
37666       _this.handleManifestRedirects = handleManifestRedirects;
37667       var hlsOptions = hls.options_;
37668       _this.customTagParsers = hlsOptions && hlsOptions.customTagParsers || [];
37669       _this.customTagMappers = hlsOptions && hlsOptions.customTagMappers || [];
37670 
37671       if (!_this.srcUrl) {
37672         throw new Error('A non-empty playlist URL is required');
37673       } // initialize the loader state
37674 
37675 
37676       _this.state = 'HAVE_NOTHING'; // live playlist staleness timeout
37677 
37678       _this.on('mediaupdatetimeout', function () {
37679         if (_this.state !== 'HAVE_METADATA') {
37680           // only refresh the media playlist if no other activity is going on
37681           return;
37682         }
37683 
37684         _this.state = 'HAVE_CURRENT_METADATA';
37685         _this.request = _this.hls_.xhr({
37686           uri: resolveUrl$1(_this.master.uri, _this.media().uri),
37687           withCredentials: _this.withCredentials
37688         }, function (error, req) {
37689           // disposed
37690           if (!_this.request) {
37691             return;
37692           }
37693 
37694           if (error) {
37695             return _this.playlistRequestError(_this.request, _this.media().uri, 'HAVE_METADATA');
37696           }
37697 
37698           _this.haveMetadata(_this.request, _this.media().uri);
37699         });
37700       });
37701 
37702       return _this;
37703     }
37704 
37705     createClass$1(PlaylistLoader, [{
37706       key: 'playlistRequestError',
37707       value: function playlistRequestError(xhr, url, startingState) {
37708         // any in-flight request is now finished
37709         this.request = null;
37710 
37711         if (startingState) {
37712           this.state = startingState;
37713         }
37714 
37715         this.error = {
37716           playlist: this.master.playlists[url],
37717           status: xhr.status,
37718           message: 'HLS playlist request error at URL: ' + url + '.',
37719           responseText: xhr.responseText,
37720           code: xhr.status >= 500 ? 4 : 2
37721         };
37722         this.trigger('error');
37723       } // update the playlist loader's state in response to a new or
37724       // updated playlist.
37725 
37726     }, {
37727       key: 'haveMetadata',
37728       value: function haveMetadata(xhr, url) {
37729         var _this2 = this; // any in-flight request is now finished
37730 
37731 
37732         this.request = null;
37733         this.state = 'HAVE_METADATA';
37734         var parser = new Parser(); // adding custom tag parsers
37735 
vendor: 5,589 bytes, lines 37736-37900
37736         this.customTagParsers.forEach(function (customParser) {
37737           return parser.addParser(customParser);
37738         }); // adding custom tag mappers
37739 
37740         this.customTagMappers.forEach(function (mapper) {
37741           return parser.addTagMapper(mapper);
37742         });
37743         parser.push(xhr.responseText);
37744         parser.end();
37745         parser.manifest.uri = url; // m3u8-parser does not attach an attributes property to media playlists so make
37746         // sure that the property is attached to avoid undefined reference errors
37747 
37748         parser.manifest.attributes = parser.manifest.attributes || {}; // merge this playlist into the master
37749 
37750         var update = updateMaster(this.master, parser.manifest);
37751         this.targetDuration = parser.manifest.targetDuration;
37752 
37753         if (update) {
37754           this.master = update;
37755           this.media_ = this.master.playlists[parser.manifest.uri];
37756         } else {
37757           this.trigger('playlistunchanged');
37758         } // refresh live playlists after a target duration passes
37759 
37760 
37761         if (!this.media().endList) {
37762           window$1.clearTimeout(this.mediaUpdateTimeout);
37763           this.mediaUpdateTimeout = window$1.setTimeout(function () {
37764             _this2.trigger('mediaupdatetimeout');
37765           }, refreshDelay(this.media(), !!update));
37766         }
37767 
37768         this.trigger('loadedplaylist');
37769       }
37770       /**
37771        * Abort any outstanding work and clean up.
37772        */
37773 
37774     }, {
37775       key: 'dispose',
37776       value: function dispose() {
37777         this.stopRequest();
37778         window$1.clearTimeout(this.mediaUpdateTimeout);
37779         window$1.clearTimeout(this.finalRenditionTimeout);
37780       }
37781     }, {
37782       key: 'stopRequest',
37783       value: function stopRequest() {
37784         if (this.request) {
37785           var oldRequest = this.request;
37786           this.request = null;
37787           oldRequest.onreadystatechange = null;
37788           oldRequest.abort();
37789         }
37790       }
37791       /**
37792        * When called without any arguments, returns the currently
37793        * active media playlist. When called with a single argument,
37794        * triggers the playlist loader to asynchronously switch to the
37795        * specified media playlist. Calling this method while the
37796        * loader is in the HAVE_NOTHING causes an error to be emitted
37797        * but otherwise has no effect.
37798        *
37799        * @param {Object=} playlist the parsed media playlist
37800        * object to switch to
37801        * @param {Boolean=} is this the last available playlist
37802        *
37803        * @return {Playlist} the current loaded media
37804        */
37805 
37806     }, {
37807       key: 'media',
37808       value: function media(playlist, isFinalRendition) {
37809         var _this3 = this; // getter
37810 
37811 
37812         if (!playlist) {
37813           return this.media_;
37814         } // setter
37815 
37816 
37817         if (this.state === 'HAVE_NOTHING') {
37818           throw new Error('Cannot switch media playlist from ' + this.state);
37819         } // find the playlist object if the target playlist has been
37820         // specified by URI
37821 
37822 
37823         if (typeof playlist === 'string') {
37824           if (!this.master.playlists[playlist]) {
37825             throw new Error('Unknown playlist URI: ' + playlist);
37826           }
37827 
37828           playlist = this.master.playlists[playlist];
37829         }
37830 
37831         window$1.clearTimeout(this.finalRenditionTimeout);
37832 
37833         if (isFinalRendition) {
37834           var delay = playlist.targetDuration / 2 * 1000 || 5 * 1000;
37835           this.finalRenditionTimeout = window$1.setTimeout(this.media.bind(this, playlist, false), delay);
37836           return;
37837         }
37838 
37839         var startingState = this.state;
37840         var mediaChange = !this.media_ || playlist.uri !== this.media_.uri; // switch to fully loaded playlists immediately
37841 
37842         if (this.master.playlists[playlist.uri].endList) {
37843           // abort outstanding playlist requests
37844           if (this.request) {
37845             this.request.onreadystatechange = null;
37846             this.request.abort();
37847             this.request = null;
37848           }
37849 
37850           this.state = 'HAVE_METADATA';
37851           this.media_ = playlist; // trigger media change if the active media has been updated
37852 
37853           if (mediaChange) {
37854             this.trigger('mediachanging');
37855             this.trigger('mediachange');
37856           }
37857 
37858           return;
37859         } // switching to the active playlist is a no-op
37860 
37861 
37862         if (!mediaChange) {
37863           return;
37864         }
37865 
37866         this.state = 'SWITCHING_MEDIA'; // there is already an outstanding playlist request
37867 
37868         if (this.request) {
37869           if (playlist.resolvedUri === this.request.url) {
37870             // requesting to switch to the same playlist multiple times
37871             // has no effect after the first
37872             return;
37873           }
37874 
37875           this.request.onreadystatechange = null;
37876           this.request.abort();
37877           this.request = null;
37878         } // request the new playlist
37879 
37880 
37881         if (this.media_) {
37882           this.trigger('mediachanging');
37883         }
37884 
37885         this.request = this.hls_.xhr({
37886           uri: playlist.resolvedUri,
37887           withCredentials: this.withCredentials
37888         }, function (error, req) {
37889           // disposed
37890           if (!_this3.request) {
37891             return;
37892           }
37893 
37894           playlist.resolvedUri = resolveManifestRedirect(_this3.handleManifestRedirects, playlist.resolvedUri, req);
37895 
37896           if (error) {
37897             return _this3.playlistRequestError(_this3.request, playlist.uri, startingState);
37898           }
37899 
37900           _this3.haveMetadata(req, playlist.uri);
vendor: 29,940 bytes, lines 37900-38875
37900 // fire loadedmetadata the first time a media playlist is loaded
37901 
37902 
37903           if (startingState === 'HAVE_MASTER') {
37904             _this3.trigger('loadedmetadata');
37905           } else {
37906             _this3.trigger('mediachange');
37907           }
37908         });
37909       }
37910       /**
37911        * pause loading of the playlist
37912        */
37913 
37914     }, {
37915       key: 'pause',
37916       value: function pause() {
37917         this.stopRequest();
37918         window$1.clearTimeout(this.mediaUpdateTimeout);
37919 
37920         if (this.state === 'HAVE_NOTHING') {
37921           // If we pause the loader before any data has been retrieved, its as if we never
37922           // started, so reset to an unstarted state.
37923           this.started = false;
37924         } // Need to restore state now that no activity is happening
37925 
37926 
37927         if (this.state === 'SWITCHING_MEDIA') {
37928           // if the loader was in the process of switching media, it should either return to
37929           // HAVE_MASTER or HAVE_METADATA depending on if the loader has loaded a media
37930           // playlist yet. This is determined by the existence of loader.media_
37931           if (this.media_) {
37932             this.state = 'HAVE_METADATA';
37933           } else {
37934             this.state = 'HAVE_MASTER';
37935           }
37936         } else if (this.state === 'HAVE_CURRENT_METADATA') {
37937           this.state = 'HAVE_METADATA';
37938         }
37939       }
37940       /**
37941        * start loading of the playlist
37942        */
37943 
37944     }, {
37945       key: 'load',
37946       value: function load(isFinalRendition) {
37947         var _this4 = this;
37948 
37949         window$1.clearTimeout(this.mediaUpdateTimeout);
37950         var media = this.media();
37951 
37952         if (isFinalRendition) {
37953           var delay = media ? media.targetDuration / 2 * 1000 : 5 * 1000;
37954           this.mediaUpdateTimeout = window$1.setTimeout(function () {
37955             return _this4.load();
37956           }, delay);
37957           return;
37958         }
37959 
37960         if (!this.started) {
37961           this.start();
37962           return;
37963         }
37964 
37965         if (media && !media.endList) {
37966           this.trigger('mediaupdatetimeout');
37967         } else {
37968           this.trigger('loadedplaylist');
37969         }
37970       }
37971       /**
37972        * start loading of the playlist
37973        */
37974 
37975     }, {
37976       key: 'start',
37977       value: function start() {
37978         var _this5 = this;
37979 
37980         this.started = true; // request the specified URL
37981 
37982         this.request = this.hls_.xhr({
37983           uri: this.srcUrl,
37984           withCredentials: this.withCredentials
37985         }, function (error, req) {
37986           // disposed
37987           if (!_this5.request) {
37988             return;
37989           } // clear the loader's request reference
37990 
37991 
37992           _this5.request = null;
37993 
37994           if (error) {
37995             _this5.error = {
37996               status: req.status,
37997               message: 'HLS playlist request error at URL: ' + _this5.srcUrl + '.',
37998               responseText: req.responseText,
37999               // MEDIA_ERR_NETWORK
38000               code: 2
38001             };
38002 
38003             if (_this5.state === 'HAVE_NOTHING') {
38004               _this5.started = false;
38005             }
38006 
38007             return _this5.trigger('error');
38008           }
38009 
38010           var parser = new Parser(); // adding custom tag parsers
38011 
38012           _this5.customTagParsers.forEach(function (customParser) {
38013             return parser.addParser(customParser);
38014           }); // adding custom tag mappers
38015 
38016 
38017           _this5.customTagMappers.forEach(function (mapper) {
38018             return parser.addTagMapper(mapper);
38019           });
38020 
38021           parser.push(req.responseText);
38022           parser.end();
38023           _this5.state = 'HAVE_MASTER';
38024           _this5.srcUrl = resolveManifestRedirect(_this5.handleManifestRedirects, _this5.srcUrl, req);
38025           parser.manifest.uri = _this5.srcUrl; // loaded a master playlist
38026 
38027           if (parser.manifest.playlists) {
38028             _this5.master = parser.manifest;
38029             setupMediaPlaylists(_this5.master);
38030             resolveMediaGroupUris(_this5.master);
38031 
38032             _this5.trigger('loadedplaylist');
38033 
38034             if (!_this5.request) {
38035               // no media playlist was specifically selected so start
38036               // from the first listed one
38037               _this5.media(parser.manifest.playlists[0]);
38038             }
38039 
38040             return;
38041           } // loaded a media playlist
38042           // infer a master playlist if none was previously requested
38043 
38044 
38045           _this5.master = {
38046             mediaGroups: {
38047               'AUDIO': {},
38048               'VIDEO': {},
38049               'CLOSED-CAPTIONS': {},
38050               'SUBTITLES': {}
38051             },
38052             uri: window$1.location.href,
38053             playlists: [{
38054               uri: _this5.srcUrl,
38055               id: 0,
38056               resolvedUri: _this5.srcUrl,
38057               // m3u8-parser does not attach an attributes property to media playlists so make
38058               // sure that the property is attached to avoid undefined reference errors
38059               attributes: {}
38060             }]
38061           };
38062           _this5.master.playlists[_this5.srcUrl] = _this5.master.playlists[0];
38063 
38064           _this5.haveMetadata(req, _this5.srcUrl);
38065 
38066           return _this5.trigger('loadedmetadata');
38067         });
38068       }
38069     }]);
38070     return PlaylistLoader;
38071   }(EventTarget$1);
38072   /**
38073    * @file playlist.js
38074    *
38075    * Playlist related utilities.
38076    */
38077 
38078 
38079   var createTimeRange = videojs$1.createTimeRange;
38080   /**
38081    * walk backward until we find a duration we can use
38082    * or return a failure
38083    *
38084    * @param {Playlist} playlist the playlist to walk through
38085    * @param {Number} endSequence the mediaSequence to stop walking on
38086    */
38087 
38088   var backwardDuration = function backwardDuration(playlist, endSequence) {
38089     var result = 0;
38090     var i = endSequence - playlist.mediaSequence; // if a start time is available for segment immediately following
38091     // the interval, use it
38092 
38093     var segment = playlist.segments[i]; // Walk backward until we find the latest segment with timeline
38094     // information that is earlier than endSequence
38095 
38096     if (segment) {
38097       if (typeof segment.start !== 'undefined') {
38098         return {
38099           result: segment.start,
38100           precise: true
38101         };
38102       }
38103 
38104       if (typeof segment.end !== 'undefined') {
38105         return {
38106           result: segment.end - segment.duration,
38107           precise: true
38108         };
38109       }
38110     }
38111 
38112     while (i--) {
38113       segment = playlist.segments[i];
38114 
38115       if (typeof segment.end !== 'undefined') {
38116         return {
38117           result: result + segment.end,
38118           precise: true
38119         };
38120       }
38121 
38122       result += segment.duration;
38123 
38124       if (typeof segment.start !== 'undefined') {
38125         return {
38126           result: result + segment.start,
38127           precise: true
38128         };
38129       }
38130     }
38131 
38132     return {
38133       result: result,
38134       precise: false
38135     };
38136   };
38137   /**
38138    * walk forward until we find a duration we can use
38139    * or return a failure
38140    *
38141    * @param {Playlist} playlist the playlist to walk through
38142    * @param {Number} endSequence the mediaSequence to stop walking on
38143    */
38144 
38145 
38146   var forwardDuration = function forwardDuration(playlist, endSequence) {
38147     var result = 0;
38148     var segment = void 0;
38149     var i = endSequence - playlist.mediaSequence; // Walk forward until we find the earliest segment with timeline
38150     // information
38151 
38152     for (; i < playlist.segments.length; i++) {
38153       segment = playlist.segments[i];
38154 
38155       if (typeof segment.start !== 'undefined') {
38156         return {
38157           result: segment.start - result,
38158           precise: true
38159         };
38160       }
38161 
38162       result += segment.duration;
38163 
38164       if (typeof segment.end !== 'undefined') {
38165         return {
38166           result: segment.end - result,
38167           precise: true
38168         };
38169       }
38170     } // indicate we didn't find a useful duration estimate
38171 
38172 
38173     return {
38174       result: -1,
38175       precise: false
38176     };
38177   };
38178   /**
38179     * Calculate the media duration from the segments associated with a
38180     * playlist. The duration of a subinterval of the available segments
38181     * may be calculated by specifying an end index.
38182     *
38183     * @param {Object} playlist a media playlist object
38184     * @param {Number=} endSequence an exclusive upper boundary
38185     * for the playlist.  Defaults to playlist length.
38186     * @param {Number} expired the amount of time that has dropped
38187     * off the front of the playlist in a live scenario
38188     * @return {Number} the duration between the first available segment
38189     * and end index.
38190     */
38191 
38192 
38193   var intervalDuration = function intervalDuration(playlist, endSequence, expired) {
38194     var backward = void 0;
38195     var forward = void 0;
38196 
38197     if (typeof endSequence === 'undefined') {
38198       endSequence = playlist.mediaSequence + playlist.segments.length;
38199     }
38200 
38201     if (endSequence < playlist.mediaSequence) {
38202       return 0;
38203     } // do a backward walk to estimate the duration
38204 
38205 
38206     backward = backwardDuration(playlist, endSequence);
38207 
38208     if (backward.precise) {
38209       // if we were able to base our duration estimate on timing
38210       // information provided directly from the Media Source, return
38211       // it
38212       return backward.result;
38213     } // walk forward to see if a precise duration estimate can be made
38214     // that way
38215 
38216 
38217     forward = forwardDuration(playlist, endSequence);
38218 
38219     if (forward.precise) {
38220       // we found a segment that has been buffered and so it's
38221       // position is known precisely
38222       return forward.result;
38223     } // return the less-precise, playlist-based duration estimate
38224 
38225 
38226     return backward.result + expired;
38227   };
38228   /**
38229     * Calculates the duration of a playlist. If a start and end index
38230     * are specified, the duration will be for the subset of the media
38231     * timeline between those two indices. The total duration for live
38232     * playlists is always Infinity.
38233     *
38234     * @param {Object} playlist a media playlist object
38235     * @param {Number=} endSequence an exclusive upper
38236     * boundary for the playlist. Defaults to the playlist media
38237     * sequence number plus its length.
38238     * @param {Number=} expired the amount of time that has
38239     * dropped off the front of the playlist in a live scenario
38240     * @return {Number} the duration between the start index and end
38241     * index.
38242     */
38243 
38244 
38245   var duration = function duration(playlist, endSequence, expired) {
38246     if (!playlist) {
38247       return 0;
38248     }
38249 
38250     if (typeof expired !== 'number') {
38251       expired = 0;
38252     } // if a slice of the total duration is not requested, use
38253     // playlist-level duration indicators when they're present
38254 
38255 
38256     if (typeof endSequence === 'undefined') {
38257       // if present, use the duration specified in the playlist
38258       if (playlist.totalDuration) {
38259         return playlist.totalDuration;
38260       } // duration should be Infinity for live playlists
38261 
38262 
38263       if (!playlist.endList) {
38264         return window$1.Infinity;
38265       }
38266     } // calculate the total duration based on the segment durations
38267 
38268 
38269     return intervalDuration(playlist, endSequence, expired);
38270   };
38271   /**
38272     * Calculate the time between two indexes in the current playlist
38273     * neight the start- nor the end-index need to be within the current
38274     * playlist in which case, the targetDuration of the playlist is used
38275     * to approximate the durations of the segments
38276     *
38277     * @param {Object} playlist a media playlist object
38278     * @param {Number} startIndex
38279     * @param {Number} endIndex
38280     * @return {Number} the number of seconds between startIndex and endIndex
38281     */
38282 
38283 
38284   var sumDurations = function sumDurations(playlist, startIndex, endIndex) {
38285     var durations = 0;
38286 
38287     if (startIndex > endIndex) {
38288       var _ref = [endIndex, startIndex];
38289       startIndex = _ref[0];
38290       endIndex = _ref[1];
38291     }
38292 
38293     if (startIndex < 0) {
38294       for (var i = startIndex; i < Math.min(0, endIndex); i++) {
38295         durations += playlist.targetDuration;
38296       }
38297 
38298       startIndex = 0;
38299     }
38300 
38301     for (var _i = startIndex; _i < endIndex; _i++) {
38302       durations += playlist.segments[_i].duration;
38303     }
38304 
38305     return durations;
38306   };
38307   /**
38308    * Determines the media index of the segment corresponding to the safe edge of the live
38309    * window which is the duration of the last segment plus 2 target durations from the end
38310    * of the playlist.
38311    *
38312    * @param {Object} playlist
38313    *        a media playlist object
38314    * @return {Number}
38315    *         The media index of the segment at the safe live point. 0 if there is no "safe"
38316    *         point.
38317    * @function safeLiveIndex
38318    */
38319 
38320 
38321   var safeLiveIndex = function safeLiveIndex(playlist) {
38322     if (!playlist.segments.length) {
38323       return 0;
38324     }
38325 
38326     var i = playlist.segments.length - 1;
38327     var distanceFromEnd = playlist.segments[i].duration || playlist.targetDuration;
38328     var safeDistance = distanceFromEnd + playlist.targetDuration * 2;
38329 
38330     while (i--) {
38331       distanceFromEnd += playlist.segments[i].duration;
38332 
38333       if (distanceFromEnd >= safeDistance) {
38334         break;
38335       }
38336     }
38337 
38338     return Math.max(0, i);
38339   };
38340   /**
38341    * Calculates the playlist end time
38342    *
38343    * @param {Object} playlist a media playlist object
38344    * @param {Number=} expired the amount of time that has
38345    *                  dropped off the front of the playlist in a live scenario
38346    * @param {Boolean|false} useSafeLiveEnd a boolean value indicating whether or not the
38347    *                        playlist end calculation should consider the safe live end
38348    *                        (truncate the playlist end by three segments). This is normally
38349    *                        used for calculating the end of the playlist's seekable range.
38350    * @returns {Number} the end time of playlist
38351    * @function playlistEnd
38352    */
38353 
38354 
38355   var playlistEnd = function playlistEnd(playlist, expired, useSafeLiveEnd) {
38356     if (!playlist || !playlist.segments) {
38357       return null;
38358     }
38359 
38360     if (playlist.endList) {
38361       return duration(playlist);
38362     }
38363 
38364     if (expired === null) {
38365       return null;
38366     }
38367 
38368     expired = expired || 0;
38369     var endSequence = useSafeLiveEnd ? safeLiveIndex(playlist) : playlist.segments.length;
38370     return intervalDuration(playlist, playlist.mediaSequence + endSequence, expired);
38371   };
38372   /**
38373     * Calculates the interval of time that is currently seekable in a
38374     * playlist. The returned time ranges are relative to the earliest
38375     * moment in the specified playlist that is still available. A full
38376     * seekable implementation for live streams would need to offset
38377     * these values by the duration of content that has expired from the
38378     * stream.
38379     *
38380     * @param {Object} playlist a media playlist object
38381     * dropped off the front of the playlist in a live scenario
38382     * @param {Number=} expired the amount of time that has
38383     * dropped off the front of the playlist in a live scenario
38384     * @return {TimeRanges} the periods of time that are valid targets
38385     * for seeking
38386     */
38387 
38388 
38389   var seekable = function seekable(playlist, expired) {
38390     var useSafeLiveEnd = true;
38391     var seekableStart = expired || 0;
38392     var seekableEnd = playlistEnd(playlist, expired, useSafeLiveEnd);
38393 
38394     if (seekableEnd === null) {
38395       return createTimeRange();
38396     }
38397 
38398     return createTimeRange(seekableStart, seekableEnd);
38399   };
38400 
38401   var isWholeNumber = function isWholeNumber(num) {
38402     return num - Math.floor(num) === 0;
38403   };
38404 
38405   var roundSignificantDigit = function roundSignificantDigit(increment, num) {
38406     // If we have a whole number, just add 1 to it
38407     if (isWholeNumber(num)) {
38408       return num + increment * 0.1;
38409     }
38410 
38411     var numDecimalDigits = num.toString().split('.')[1].length;
38412 
38413     for (var i = 1; i <= numDecimalDigits; i++) {
38414       var scale = Math.pow(10, i);
38415       var temp = num * scale;
38416 
38417       if (isWholeNumber(temp) || i === numDecimalDigits) {
38418         return (temp + increment) / scale;
38419       }
38420     }
38421   };
38422 
38423   var ceilLeastSignificantDigit = roundSignificantDigit.bind(null, 1);
38424   var floorLeastSignificantDigit = roundSignificantDigit.bind(null, -1);
38425   /**
38426    * Determine the index and estimated starting time of the segment that
38427    * contains a specified playback position in a media playlist.
38428    *
38429    * @param {Object} playlist the media playlist to query
38430    * @param {Number} currentTime The number of seconds since the earliest
38431    * possible position to determine the containing segment for
38432    * @param {Number} startIndex
38433    * @param {Number} startTime
38434    * @return {Object}
38435    */
38436 
38437   var getMediaInfoForTime = function getMediaInfoForTime(playlist, currentTime, startIndex, startTime) {
38438     var i = void 0;
38439     var segment = void 0;
38440     var numSegments = playlist.segments.length;
38441     var time = currentTime - startTime;
38442 
38443     if (time < 0) {
38444       // Walk backward from startIndex in the playlist, adding durations
38445       // until we find a segment that contains `time` and return it
38446       if (startIndex > 0) {
38447         for (i = startIndex - 1; i >= 0; i--) {
38448           segment = playlist.segments[i];
38449           time += floorLeastSignificantDigit(segment.duration);
38450 
38451           if (time > 0) {
38452             return {
38453               mediaIndex: i,
38454               startTime: startTime - sumDurations(playlist, startIndex, i)
38455             };
38456           }
38457         }
38458       } // We were unable to find a good segment within the playlist
38459       // so select the first segment
38460 
38461 
38462       return {
38463         mediaIndex: 0,
38464         startTime: currentTime
38465       };
38466     } // When startIndex is negative, we first walk forward to first segment
38467     // adding target durations. If we "run out of time" before getting to
38468     // the first segment, return the first segment
38469 
38470 
38471     if (startIndex < 0) {
38472       for (i = startIndex; i < 0; i++) {
38473         time -= playlist.targetDuration;
38474 
38475         if (time < 0) {
38476           return {
38477             mediaIndex: 0,
38478             startTime: currentTime
38479           };
38480         }
38481       }
38482 
38483       startIndex = 0;
38484     } // Walk forward from startIndex in the playlist, subtracting durations
38485     // until we find a segment that contains `time` and return it
38486 
38487 
38488     for (i = startIndex; i < numSegments; i++) {
38489       segment = playlist.segments[i];
38490       time -= ceilLeastSignificantDigit(segment.duration);
38491 
38492       if (time < 0) {
38493         return {
38494           mediaIndex: i,
38495           startTime: startTime + sumDurations(playlist, startIndex, i)
38496         };
38497       }
38498     } // We are out of possible candidates so load the last one...
38499 
38500 
38501     return {
38502       mediaIndex: numSegments - 1,
38503       startTime: currentTime
38504     };
38505   };
38506   /**
38507    * Check whether the playlist is blacklisted or not.
38508    *
38509    * @param {Object} playlist the media playlist object
38510    * @return {boolean} whether the playlist is blacklisted or not
38511    * @function isBlacklisted
38512    */
38513 
38514 
38515   var isBlacklisted = function isBlacklisted(playlist) {
38516     return playlist.excludeUntil && playlist.excludeUntil > Date.now();
38517   };
38518   /**
38519    * Check whether the playlist is compatible with current playback configuration or has
38520    * been blacklisted permanently for being incompatible.
38521    *
38522    * @param {Object} playlist the media playlist object
38523    * @return {boolean} whether the playlist is incompatible or not
38524    * @function isIncompatible
38525    */
38526 
38527 
38528   var isIncompatible = function isIncompatible(playlist) {
38529     return playlist.excludeUntil && playlist.excludeUntil === Infinity;
38530   };
38531   /**
38532    * Check whether the playlist is enabled or not.
38533    *
38534    * @param {Object} playlist the media playlist object
38535    * @return {boolean} whether the playlist is enabled or not
38536    * @function isEnabled
38537    */
38538 
38539 
38540   var isEnabled = function isEnabled(playlist) {
38541     var blacklisted = isBlacklisted(playlist);
38542     return !playlist.disabled && !blacklisted;
38543   };
38544   /**
38545    * Check whether the playlist has been manually disabled through the representations api.
38546    *
38547    * @param {Object} playlist the media playlist object
38548    * @return {boolean} whether the playlist is disabled manually or not
38549    * @function isDisabled
38550    */
38551 
38552 
38553   var isDisabled = function isDisabled(playlist) {
38554     return playlist.disabled;
38555   };
38556   /**
38557    * Returns whether the current playlist is an AES encrypted HLS stream
38558    *
38559    * @return {Boolean} true if it's an AES encrypted HLS stream
38560    */
38561 
38562 
38563   var isAes = function isAes(media) {
38564     for (var i = 0; i < media.segments.length; i++) {
38565       if (media.segments[i].key) {
38566         return true;
38567       }
38568     }
38569 
38570     return false;
38571   };
38572   /**
38573    * Returns whether the current playlist contains fMP4
38574    *
38575    * @return {Boolean} true if the playlist contains fMP4
38576    */
38577 
38578 
38579   var isFmp4 = function isFmp4(media) {
38580     for (var i = 0; i < media.segments.length; i++) {
38581       if (media.segments[i].map) {
38582         return true;
38583       }
38584     }
38585 
38586     return false;
38587   };
38588   /**
38589    * Checks if the playlist has a value for the specified attribute
38590    *
38591    * @param {String} attr
38592    *        Attribute to check for
38593    * @param {Object} playlist
38594    *        The media playlist object
38595    * @return {Boolean}
38596    *         Whether the playlist contains a value for the attribute or not
38597    * @function hasAttribute
38598    */
38599 
38600 
38601   var hasAttribute = function hasAttribute(attr, playlist) {
38602     return playlist.attributes && playlist.attributes[attr];
38603   };
38604   /**
38605    * Estimates the time required to complete a segment download from the specified playlist
38606    *
38607    * @param {Number} segmentDuration
38608    *        Duration of requested segment
38609    * @param {Number} bandwidth
38610    *        Current measured bandwidth of the player
38611    * @param {Object} playlist
38612    *        The media playlist object
38613    * @param {Number=} bytesReceived
38614    *        Number of bytes already received for the request. Defaults to 0
38615    * @return {Number|NaN}
38616    *         The estimated time to request the segment. NaN if bandwidth information for
38617    *         the given playlist is unavailable
38618    * @function estimateSegmentRequestTime
38619    */
38620 
38621 
38622   var estimateSegmentRequestTime = function estimateSegmentRequestTime(segmentDuration, bandwidth, playlist) {
38623     var bytesReceived = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
38624 
38625     if (!hasAttribute('BANDWIDTH', playlist)) {
38626       return NaN;
38627     }
38628 
38629     var size = segmentDuration * playlist.attributes.BANDWIDTH;
38630     return (size - bytesReceived * 8) / bandwidth;
38631   };
38632   /*
38633    * Returns whether the current playlist is the lowest rendition
38634    *
38635    * @return {Boolean} true if on lowest rendition
38636    */
38637 
38638 
38639   var isLowestEnabledRendition = function isLowestEnabledRendition(master, media) {
38640     if (master.playlists.length === 1) {
38641       return true;
38642     }
38643 
38644     var currentBandwidth = media.attributes.BANDWIDTH || Number.MAX_VALUE;
38645     return master.playlists.filter(function (playlist) {
38646       if (!isEnabled(playlist)) {
38647         return false;
38648       }
38649 
38650       return (playlist.attributes.BANDWIDTH || 0) < currentBandwidth;
38651     }).length === 0;
38652   }; // exports
38653 
38654 
38655   var Playlist = {
38656     duration: duration,
38657     seekable: seekable,
38658     safeLiveIndex: safeLiveIndex,
38659     getMediaInfoForTime: getMediaInfoForTime,
38660     isEnabled: isEnabled,
38661     isDisabled: isDisabled,
38662     isBlacklisted: isBlacklisted,
38663     isIncompatible: isIncompatible,
38664     playlistEnd: playlistEnd,
38665     isAes: isAes,
38666     isFmp4: isFmp4,
38667     hasAttribute: hasAttribute,
38668     estimateSegmentRequestTime: estimateSegmentRequestTime,
38669     isLowestEnabledRendition: isLowestEnabledRendition
38670   };
38671   /**
38672    * @file xhr.js
38673    */
38674 
38675   var videojsXHR = videojs$1.xhr,
38676       mergeOptions$1$1 = videojs$1.mergeOptions;
38677 
38678   var xhrFactory = function xhrFactory() {
38679     var xhr = function XhrFunction(options, callback) {
38680       // Add a default timeout for all hls requests
38681       options = mergeOptions$1$1({
38682         timeout: 45e3
38683       }, options); // Allow an optional user-specified function to modify the option
38684       // object before we construct the xhr request
38685 
38686       var beforeRequest = XhrFunction.beforeRequest || videojs$1.Hls.xhr.beforeRequest;
38687 
38688       if (beforeRequest && typeof beforeRequest === 'function') {
38689         var newOptions = beforeRequest(options);
38690 
38691         if (newOptions) {
38692           options = newOptions;
38693         }
38694       }
38695 
38696       var request = videojsXHR(options, function (error, response) {
38697         var reqResponse = request.response;
38698 
38699         if (!error && reqResponse) {
38700           request.responseTime = Date.now();
38701           request.roundTripTime = request.responseTime - request.requestTime;
38702           request.bytesReceived = reqResponse.byteLength || reqResponse.length;
38703 
38704           if (!request.bandwidth) {
38705             request.bandwidth = Math.floor(request.bytesReceived / request.roundTripTime * 8 * 1000);
38706           }
38707         }
38708 
38709         if (response.headers) {
38710           request.responseHeaders = response.headers;
38711         } // videojs.xhr now uses a specific code on the error
38712         // object to signal that a request has timed out instead
38713         // of setting a boolean on the request object
38714 
38715 
38716         if (error && error.code === 'ETIMEDOUT') {
38717           request.timedout = true;
38718         } // videojs.xhr no longer considers status codes outside of 200 and 0
38719         // (for file uris) to be errors, but the old XHR did, so emulate that
38720         // behavior. Status 206 may be used in response to byterange requests.
38721 
38722 
38723         if (!error && !request.aborted && response.statusCode !== 200 && response.statusCode !== 206 && response.statusCode !== 0) {
38724           error = new Error('XHR Failed with a response of: ' + (request && (reqResponse || request.responseText)));
38725         }
38726 
38727         callback(error, request);
38728       });
38729       var originalAbort = request.abort;
38730 
38731       request.abort = function () {
38732         request.aborted = true;
38733         return originalAbort.apply(request, arguments);
38734       };
38735 
38736       request.uri = options.uri;
38737       request.requestTime = Date.now();
38738       return request;
38739     };
38740 
38741     return xhr;
38742   };
38743   /**
38744    * Turns segment byterange into a string suitable for use in
38745    * HTTP Range requests
38746    *
38747    * @param {Object} byterange - an object with two values defining the start and end
38748    *                             of a byte-range
38749    */
38750 
38751 
38752   var byterangeStr = function byterangeStr(byterange) {
38753     var byterangeStart = void 0;
38754     var byterangeEnd = void 0; // `byterangeEnd` is one less than `offset + length` because the HTTP range
38755     // header uses inclusive ranges
38756 
38757     byterangeEnd = byterange.offset + byterange.length - 1;
38758     byterangeStart = byterange.offset;
38759     return 'bytes=' + byterangeStart + '-' + byterangeEnd;
38760   };
38761   /**
38762    * Defines headers for use in the xhr request for a particular segment.
38763    *
38764    * @param {Object} segment - a simplified copy of the segmentInfo object
38765    *                           from SegmentLoader
38766    */
38767 
38768 
38769   var segmentXhrHeaders = function segmentXhrHeaders(segment) {
38770     var headers = {};
38771 
38772     if (segment.byterange) {
38773       headers.Range = byterangeStr(segment.byterange);
38774     }
38775 
38776     return headers;
38777   };
38778   /**
38779    * @file bin-utils.js
38780    */
38781 
38782   /**
38783    * convert a TimeRange to text
38784    *
38785    * @param {TimeRange} range the timerange to use for conversion
38786    * @param {Number} i the iterator on the range to convert
38787    */
38788 
38789 
38790   var textRange = function textRange(range, i) {
38791     return range.start(i) + '-' + range.end(i);
38792   };
38793   /**
38794    * format a number as hex string
38795    *
38796    * @param {Number} e The number
38797    * @param {Number} i the iterator
38798    */
38799 
38800 
38801   var formatHexString = function formatHexString(e, i) {
38802     var value = e.toString(16);
38803     return '00'.substring(0, 2 - value.length) + value + (i % 2 ? ' ' : '');
38804   };
38805 
38806   var formatAsciiString = function formatAsciiString(e) {
38807     if (e >= 0x20 && e < 0x7e) {
38808       return String.fromCharCode(e);
38809     }
38810 
38811     return '.';
38812   };
38813   /**
38814    * Creates an object for sending to a web worker modifying properties that are TypedArrays
38815    * into a new object with seperated properties for the buffer, byteOffset, and byteLength.
38816    *
38817    * @param {Object} message
38818    *        Object of properties and values to send to the web worker
38819    * @return {Object}
38820    *         Modified message with TypedArray values expanded
38821    * @function createTransferableMessage
38822    */
38823 
38824 
38825   var createTransferableMessage = function createTransferableMessage(message) {
38826     var transferable = {};
38827     Object.keys(message).forEach(function (key) {
38828       var value = message[key];
38829 
38830       if (ArrayBuffer.isView(value)) {
38831         transferable[key] = {
38832           bytes: value.buffer,
38833           byteOffset: value.byteOffset,
38834           byteLength: value.byteLength
38835         };
38836       } else {
38837         transferable[key] = value;
38838       }
38839     });
38840     return transferable;
38841   };
38842   /**
38843    * Returns a unique string identifier for a media initialization
38844    * segment.
38845    */
38846 
38847 
38848   var initSegmentId = function initSegmentId(initSegment) {
38849     var byterange = initSegment.byterange || {
38850       length: Infinity,
38851       offset: 0
38852     };
38853     return [byterange.length, byterange.offset, initSegment.resolvedUri].join(',');
38854   };
38855   /**
38856    * Returns a unique string identifier for a media segment key.
38857    */
38858 
38859 
38860   var segmentKeyId = function segmentKeyId(key) {
38861     return key.resolvedUri;
38862   };
38863   /**
38864    * utils to help dump binary data to the console
38865    */
38866 
38867 
38868   var hexDump = function hexDump(data) {
38869     var bytes = Array.prototype.slice.call(data);
38870     var step = 16;
38871     var result = '';
38872     var hex = void 0;
38873     var ascii = void 0;
38874 
38875     for (var j = 0;
vendor: 19,733 bytes, lines 38875-39435
38875 j < bytes.length / step; j++) {
38876       hex = bytes.slice(j * step, j * step + step).map(formatHexString).join('');
38877       ascii = bytes.slice(j * step, j * step + step).map(formatAsciiString).join('');
38878       result += hex + ' ' + ascii + '\n';
38879     }
38880 
38881     return result;
38882   };
38883 
38884   var tagDump = function tagDump(_ref) {
38885     var bytes = _ref.bytes;
38886     return hexDump(bytes);
38887   };
38888 
38889   var textRanges = function textRanges(ranges) {
38890     var result = '';
38891     var i = void 0;
38892 
38893     for (i = 0; i < ranges.length; i++) {
38894       result += textRange(ranges, i) + ' ';
38895     }
38896 
38897     return result;
38898   };
38899 
38900   var utils$1 =
38901   /*#__PURE__*/
38902   Object.freeze({
38903     createTransferableMessage: createTransferableMessage,
38904     initSegmentId: initSegmentId,
38905     segmentKeyId: segmentKeyId,
38906     hexDump: hexDump,
38907     tagDump: tagDump,
38908     textRanges: textRanges
38909   }); // TODO handle fmp4 case where the timing info is accurate and doesn't involve transmux
38910   // Add 25% to the segment duration to account for small discrepencies in segment timing.
38911   // 25% was arbitrarily chosen, and may need to be refined over time.
38912 
38913   var SEGMENT_END_FUDGE_PERCENT = 0.25;
38914   /**
38915    * Converts a player time (any time that can be gotten/set from player.currentTime(),
38916    * e.g., any time within player.seekable().start(0) to player.seekable().end(0)) to a
38917    * program time (any time referencing the real world (e.g., EXT-X-PROGRAM-DATE-TIME)).
38918    *
38919    * The containing segment is required as the EXT-X-PROGRAM-DATE-TIME serves as an "anchor
38920    * point" (a point where we have a mapping from program time to player time, with player
38921    * time being the post transmux start of the segment).
38922    *
38923    * For more details, see [this doc](../../docs/program-time-from-player-time.md).
38924    *
38925    * @param {Number} playerTime the player time
38926    * @param {Object} segment the segment which contains the player time
38927    * @return {Date} program time
38928    */
38929 
38930   var playerTimeToProgramTime = function playerTimeToProgramTime(playerTime, segment) {
38931     if (!segment.dateTimeObject) {
38932       // Can't convert without an "anchor point" for the program time (i.e., a time that can
38933       // be used to map the start of a segment with a real world time).
38934       return null;
38935     }
38936 
38937     var transmuxerPrependedSeconds = segment.videoTimingInfo.transmuxerPrependedSeconds;
38938     var transmuxedStart = segment.videoTimingInfo.transmuxedPresentationStart; // get the start of the content from before old content is prepended
38939 
38940     var startOfSegment = transmuxedStart + transmuxerPrependedSeconds;
38941     var offsetFromSegmentStart = playerTime - startOfSegment;
38942     return new Date(segment.dateTimeObject.getTime() + offsetFromSegmentStart * 1000);
38943   };
38944 
38945   var originalSegmentVideoDuration = function originalSegmentVideoDuration(videoTimingInfo) {
38946     return videoTimingInfo.transmuxedPresentationEnd - videoTimingInfo.transmuxedPresentationStart - videoTimingInfo.transmuxerPrependedSeconds;
38947   };
38948   /**
38949    * Finds a segment that contains the time requested given as an ISO-8601 string. The
38950    * returned segment might be an estimate or an accurate match.
38951    *
38952    * @param {String} programTime The ISO-8601 programTime to find a match for
38953    * @param {Object} playlist A playlist object to search within
38954    */
38955 
38956 
38957   var findSegmentForProgramTime = function findSegmentForProgramTime(programTime, playlist) {
38958     // Assumptions:
38959     //  - verifyProgramDateTimeTags has already been run
38960     //  - live streams have been started
38961     var dateTimeObject = void 0;
38962 
38963     try {
38964       dateTimeObject = new Date(programTime);
38965     } catch (e) {
38966       return null;
38967     }
38968 
38969     if (!playlist || !playlist.segments || playlist.segments.length === 0) {
38970       return null;
38971     }
38972 
38973     var segment = playlist.segments[0];
38974 
38975     if (dateTimeObject < segment.dateTimeObject) {
38976       // Requested time is before stream start.
38977       return null;
38978     }
38979 
38980     for (var i = 0; i < playlist.segments.length - 1; i++) {
38981       segment = playlist.segments[i];
38982       var nextSegmentStart = playlist.segments[i + 1].dateTimeObject;
38983 
38984       if (dateTimeObject < nextSegmentStart) {
38985         break;
38986       }
38987     }
38988 
38989     var lastSegment = playlist.segments[playlist.segments.length - 1];
38990     var lastSegmentStart = lastSegment.dateTimeObject;
38991     var lastSegmentDuration = lastSegment.videoTimingInfo ? originalSegmentVideoDuration(lastSegment.videoTimingInfo) : lastSegment.duration + lastSegment.duration * SEGMENT_END_FUDGE_PERCENT;
38992     var lastSegmentEnd = new Date(lastSegmentStart.getTime() + lastSegmentDuration * 1000);
38993 
38994     if (dateTimeObject > lastSegmentEnd) {
38995       // Beyond the end of the stream, or our best guess of the end of the stream.
38996       return null;
38997     }
38998 
38999     if (dateTimeObject > lastSegmentStart) {
39000       segment = lastSegment;
39001     }
39002 
39003     return {
39004       segment: segment,
39005       estimatedStart: segment.videoTimingInfo ? segment.videoTimingInfo.transmuxedPresentationStart : Playlist.duration(playlist, playlist.mediaSequence + playlist.segments.indexOf(segment)),
39006       // Although, given that all segments have accurate date time objects, the segment
39007       // selected should be accurate, unless the video has been transmuxed at some point
39008       // (determined by the presence of the videoTimingInfo object), the segment's "player
39009       // time" (the start time in the player) can't be considered accurate.
39010       type: segment.videoTimingInfo ? 'accurate' : 'estimate'
39011     };
39012   };
39013   /**
39014    * Finds a segment that contains the given player time(in seconds).
39015    *
39016    * @param {Number} time The player time to find a match for
39017    * @param {Object} playlist A playlist object to search within
39018    */
39019 
39020 
39021   var findSegmentForPlayerTime = function findSegmentForPlayerTime(time, playlist) {
39022     // Assumptions:
39023     // - there will always be a segment.duration
39024     // - we can start from zero
39025     // - segments are in time order
39026     if (!playlist || !playlist.segments || playlist.segments.length === 0) {
39027       return null;
39028     }
39029 
39030     var segmentEnd = 0;
39031     var segment = void 0;
39032 
39033     for (var i = 0; i < playlist.segments.length; i++) {
39034       segment = playlist.segments[i]; // videoTimingInfo is set after the segment is downloaded and transmuxed, and
39035       // should contain the most accurate values we have for the segment's player times.
39036       //
39037       // Use the accurate transmuxedPresentationEnd value if it is available, otherwise fall
39038       // back to an estimate based on the manifest derived (inaccurate) segment.duration, to
39039       // calculate an end value.
39040 
39041       segmentEnd = segment.videoTimingInfo ? segment.videoTimingInfo.transmuxedPresentationEnd : segmentEnd + segment.duration;
39042 
39043       if (time <= segmentEnd) {
39044         break;
39045       }
39046     }
39047 
39048     var lastSegment = playlist.segments[playlist.segments.length - 1];
39049 
39050     if (lastSegment.videoTimingInfo && lastSegment.videoTimingInfo.transmuxedPresentationEnd < time) {
39051       // The time requested is beyond the stream end.
39052       return null;
39053     }
39054 
39055     if (time > segmentEnd) {
39056       // The time is within or beyond the last segment.
39057       //
39058       // Check to see if the time is beyond a reasonable guess of the end of the stream.
39059       if (time > segmentEnd + lastSegment.duration * SEGMENT_END_FUDGE_PERCENT) {
39060         // Technically, because the duration value is only an estimate, the time may still
39061         // exist in the last segment, however, there isn't enough information to make even
39062         // a reasonable estimate.
39063         return null;
39064       }
39065 
39066       segment = lastSegment;
39067     }
39068 
39069     return {
39070       segment: segment,
39071       estimatedStart: segment.videoTimingInfo ? segment.videoTimingInfo.transmuxedPresentationStart : segmentEnd - segment.duration,
39072       // Because videoTimingInfo is only set after transmux, it is the only way to get
39073       // accurate timing values.
39074       type: segment.videoTimingInfo ? 'accurate' : 'estimate'
39075     };
39076   };
39077   /**
39078    * Gives the offset of the comparisonTimestamp from the programTime timestamp in seconds.
39079    * If the offset returned is positive, the programTime occurs after the
39080    * comparisonTimestamp.
39081    * If the offset is negative, the programTime occurs before the comparisonTimestamp.
39082    *
39083    * @param {String} comparisonTimeStamp An ISO-8601 timestamp to compare against
39084    * @param {String} programTime The programTime as an ISO-8601 string
39085    * @return {Number} offset
39086    */
39087 
39088 
39089   var getOffsetFromTimestamp = function getOffsetFromTimestamp(comparisonTimeStamp, programTime) {
39090     var segmentDateTime = void 0;
39091     var programDateTime = void 0;
39092 
39093     try {
39094       segmentDateTime = new Date(comparisonTimeStamp);
39095       programDateTime = new Date(programTime);
39096     } catch (e) {// TODO handle error
39097     }
39098 
39099     var segmentTimeEpoch = segmentDateTime.getTime();
39100     var programTimeEpoch = programDateTime.getTime();
39101     return (programTimeEpoch - segmentTimeEpoch) / 1000;
39102   };
39103   /**
39104    * Checks that all segments in this playlist have programDateTime tags.
39105    *
39106    * @param {Object} playlist A playlist object
39107    */
39108 
39109 
39110   var verifyProgramDateTimeTags = function verifyProgramDateTimeTags(playlist) {
39111     if (!playlist.segments || playlist.segments.length === 0) {
39112       return false;
39113     }
39114 
39115     for (var i = 0; i < playlist.segments.length; i++) {
39116       var segment = playlist.segments[i];
39117 
39118       if (!segment.dateTimeObject) {
39119         return false;
39120       }
39121     }
39122 
39123     return true;
39124   };
39125   /**
39126    * Returns the programTime of the media given a playlist and a playerTime.
39127    * The playlist must have programDateTime tags for a programDateTime tag to be returned.
39128    * If the segments containing the time requested have not been buffered yet, an estimate
39129    * may be returned to the callback.
39130    *
39131    * @param {Object} args
39132    * @param {Object} args.playlist A playlist object to search within
39133    * @param {Number} time A playerTime in seconds
39134    * @param {Function} callback(err, programTime)
39135    * @returns {String} err.message A detailed error message
39136    * @returns {Object} programTime
39137    * @returns {Number} programTime.mediaSeconds The streamTime in seconds
39138    * @returns {String} programTime.programDateTime The programTime as an ISO-8601 String
39139    */
39140 
39141 
39142   var getProgramTime = function getProgramTime(_ref) {
39143     var playlist = _ref.playlist,
39144         _ref$time = _ref.time,
39145         time = _ref$time === undefined ? undefined : _ref$time,
39146         callback = _ref.callback;
39147 
39148     if (!callback) {
39149       throw new Error('getProgramTime: callback must be provided');
39150     }
39151 
39152     if (!playlist || time === undefined) {
39153       return callback({
39154         message: 'getProgramTime: playlist and time must be provided'
39155       });
39156     }
39157 
39158     var matchedSegment = findSegmentForPlayerTime(time, playlist);
39159 
39160     if (!matchedSegment) {
39161       return callback({
39162         message: 'valid programTime was not found'
39163       });
39164     }
39165 
39166     if (matchedSegment.type === 'estimate') {
39167       return callback({
39168         message: 'Accurate programTime could not be determined.' + ' Please seek to e.seekTime and try again',
39169         seekTime: matchedSegment.estimatedStart
39170       });
39171     }
39172 
39173     var programTimeObject = {
39174       mediaSeconds: time
39175     };
39176     var programTime = playerTimeToProgramTime(time, matchedSegment.segment);
39177 
39178     if (programTime) {
39179       programTimeObject.programDateTime = programTime.toISOString();
39180     }
39181 
39182     return callback(null, programTimeObject);
39183   };
39184   /**
39185    * Seeks in the player to a time that matches the given programTime ISO-8601 string.
39186    *
39187    * @param {Object} args
39188    * @param {String} args.programTime A programTime to seek to as an ISO-8601 String
39189    * @param {Object} args.playlist A playlist to look within
39190    * @param {Number} args.retryCount The number of times to try for an accurate seek. Default is 2.
39191    * @param {Function} args.seekTo A method to perform a seek
39192    * @param {Boolean} args.pauseAfterSeek Whether to end in a paused state after seeking. Default is true.
39193    * @param {Object} args.tech The tech to seek on
39194    * @param {Function} args.callback(err, newTime) A callback to return the new time to
39195    * @returns {String} err.message A detailed error message
39196    * @returns {Number} newTime The exact time that was seeked to in seconds
39197    */
39198 
39199 
39200   var seekToProgramTime = function seekToProgramTime(_ref2) {
39201     var programTime = _ref2.programTime,
39202         playlist = _ref2.playlist,
39203         _ref2$retryCount = _ref2.retryCount,
39204         retryCount = _ref2$retryCount === undefined ? 2 : _ref2$retryCount,
39205         seekTo = _ref2.seekTo,
39206         _ref2$pauseAfterSeek = _ref2.pauseAfterSeek,
39207         pauseAfterSeek = _ref2$pauseAfterSeek === undefined ? true : _ref2$pauseAfterSeek,
39208         tech = _ref2.tech,
39209         callback = _ref2.callback;
39210 
39211     if (!callback) {
39212       throw new Error('seekToProgramTime: callback must be provided');
39213     }
39214 
39215     if (typeof programTime === 'undefined' || !playlist || !seekTo) {
39216       return callback({
39217         message: 'seekToProgramTime: programTime, seekTo and playlist must be provided'
39218       });
39219     }
39220 
39221     if (!playlist.endList && !tech.hasStarted_) {
39222       return callback({
39223         message: 'player must be playing a live stream to start buffering'
39224       });
39225     }
39226 
39227     if (!verifyProgramDateTimeTags(playlist)) {
39228       return callback({
39229         message: 'programDateTime tags must be provided in the manifest ' + playlist.resolvedUri
39230       });
39231     }
39232 
39233     var matchedSegment = findSegmentForProgramTime(programTime, playlist); // no match
39234 
39235     if (!matchedSegment) {
39236       return callback({
39237         message: programTime + ' was not found in the stream'
39238       });
39239     }
39240 
39241     var segment = matchedSegment.segment;
39242     var mediaOffset = getOffsetFromTimestamp(segment.dateTimeObject, programTime);
39243 
39244     if (matchedSegment.type === 'estimate') {
39245       // we've run out of retries
39246       if (retryCount === 0) {
39247         return callback({
39248           message: programTime + ' is not buffered yet. Try again'
39249         });
39250       }
39251 
39252       seekTo(matchedSegment.estimatedStart + mediaOffset);
39253       tech.one('seeked', function () {
39254         seekToProgramTime({
39255           programTime: programTime,
39256           playlist: playlist,
39257           retryCount: retryCount - 1,
39258           seekTo: seekTo,
39259           pauseAfterSeek: pauseAfterSeek,
39260           tech: tech,
39261           callback: callback
39262         });
39263       });
39264       return;
39265     } // Since the segment.start value is determined from the buffered end or ending time
39266     // of the prior segment, the seekToTime doesn't need to account for any transmuxer
39267     // modifications.
39268 
39269 
39270     var seekToTime = segment.start + mediaOffset;
39271 
39272     var seekedCallback = function seekedCallback() {
39273       return callback(null, tech.currentTime());
39274     }; // listen for seeked event
39275 
39276 
39277     tech.one('seeked', seekedCallback); // pause before seeking as video.js will restore this state
39278 
39279     if (pauseAfterSeek) {
39280       tech.pause();
39281     }
39282 
39283     seekTo(seekToTime);
39284   };
39285   /**
39286    * ranges
39287    *
39288    * Utilities for working with TimeRanges.
39289    *
39290    */
39291   // Fudge factor to account for TimeRanges rounding
39292 
39293 
39294   var TIME_FUDGE_FACTOR = 1 / 30; // Comparisons between time values such as current time and the end of the buffered range
39295   // can be misleading because of precision differences or when the current media has poorly
39296   // aligned audio and video, which can cause values to be slightly off from what you would
39297   // expect. This value is what we consider to be safe to use in such comparisons to account
39298   // for these scenarios.
39299 
39300   var SAFE_TIME_DELTA = TIME_FUDGE_FACTOR * 3;
39301 
39302   var filterRanges = function filterRanges(timeRanges, predicate) {
39303     var results = [];
39304     var i = void 0;
39305 
39306     if (timeRanges && timeRanges.length) {
39307       // Search for ranges that match the predicate
39308       for (i = 0; i < timeRanges.length; i++) {
39309         if (predicate(timeRanges.start(i), timeRanges.end(i))) {
39310           results.push([timeRanges.start(i), timeRanges.end(i)]);
39311         }
39312       }
39313     }
39314 
39315     return videojs$1.createTimeRanges(results);
39316   };
39317   /**
39318    * Attempts to find the buffered TimeRange that contains the specified
39319    * time.
39320    * @param {TimeRanges} buffered - the TimeRanges object to query
39321    * @param {number} time  - the time to filter on.
39322    * @returns {TimeRanges} a new TimeRanges object
39323    */
39324 
39325 
39326   var findRange = function findRange(buffered, time) {
39327     return filterRanges(buffered, function (start, end) {
39328       return start - SAFE_TIME_DELTA <= time && end + SAFE_TIME_DELTA >= time;
39329     });
39330   };
39331   /**
39332    * Returns the TimeRanges that begin later than the specified time.
39333    * @param {TimeRanges} timeRanges - the TimeRanges object to query
39334    * @param {number} time - the time to filter on.
39335    * @returns {TimeRanges} a new TimeRanges object.
39336    */
39337 
39338 
39339   var findNextRange = function findNextRange(timeRanges, time) {
39340     return filterRanges(timeRanges, function (start) {
39341       return start - TIME_FUDGE_FACTOR >= time;
39342     });
39343   };
39344   /**
39345    * Returns gaps within a list of TimeRanges
39346    * @param {TimeRanges} buffered - the TimeRanges object
39347    * @return {TimeRanges} a TimeRanges object of gaps
39348    */
39349 
39350 
39351   var findGaps = function findGaps(buffered) {
39352     if (buffered.length < 2) {
39353       return videojs$1.createTimeRanges();
39354     }
39355 
39356     var ranges = [];
39357 
39358     for (var i = 1; i < buffered.length; i++) {
39359       var start = buffered.end(i - 1);
39360       var end = buffered.start(i);
39361       ranges.push([start, end]);
39362     }
39363 
39364     return videojs$1.createTimeRanges(ranges);
39365   };
39366   /**
39367    * Gets a human readable string for a TimeRange
39368    *
39369    * @param {TimeRange} range
39370    * @returns {String} a human readable string
39371    */
39372 
39373 
39374   var printableRange = function printableRange(range) {
39375     var strArr = [];
39376 
39377     if (!range || !range.length) {
39378       return '';
39379     }
39380 
39381     for (var i = 0; i < range.length; i++) {
39382       strArr.push(range.start(i) + ' => ' + range.end(i));
39383     }
39384 
39385     return strArr.join(', ');
39386   };
39387   /**
39388    * Calculates the amount of time left in seconds until the player hits the end of the
39389    * buffer and causes a rebuffer
39390    *
39391    * @param {TimeRange} buffered
39392    *        The state of the buffer
39393    * @param {Numnber} currentTime
39394    *        The current time of the player
39395    * @param {Number} playbackRate
39396    *        The current playback rate of the player. Defaults to 1.
39397    * @return {Number}
39398    *         Time until the player has to start rebuffering in seconds.
39399    * @function timeUntilRebuffer
39400    */
39401 
39402 
39403   var timeUntilRebuffer = function timeUntilRebuffer(buffered, currentTime) {
39404     var playbackRate = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
39405     var bufferedEnd = buffered.length ? buffered.end(buffered.length - 1) : 0;
39406     return (bufferedEnd - currentTime) / playbackRate;
39407   };
39408   /**
39409    * Converts a TimeRanges object into an array representation
39410    * @param {TimeRanges} timeRanges
39411    * @returns {Array}
39412    */
39413 
39414 
39415   var timeRangesToArray = function timeRangesToArray(timeRanges) {
39416     var timeRangesList = [];
39417 
39418     for (var i = 0; i < timeRanges.length; i++) {
39419       timeRangesList.push({
39420         start: timeRanges.start(i),
39421         end: timeRanges.end(i)
39422       });
39423     }
39424 
39425     return timeRangesList;
39426   };
39427   /**
39428    * @file create-text-tracks-if-necessary.js
39429    */
39430 
39431   /**
39432    * Create text tracks on video.js if they exist on a segment.
39433    *
39434    * @param {Object} sourceBuffer the VSB or FSB
39435    * @param {Object}
vendor: 17,131 bytes, lines 39435-39964
39435 mediaSource the HTML media source
39436    * @param {Object} segment the segment that may contain the text track
39437    * @private
39438    */
39439 
39440 
39441   var createTextTracksIfNecessary = function createTextTracksIfNecessary(sourceBuffer, mediaSource, segment) {
39442     var player = mediaSource.player_; // create an in-band caption track if one is present in the segment
39443 
39444     if (segment.captions && segment.captions.length) {
39445       if (!sourceBuffer.inbandTextTracks_) {
39446         sourceBuffer.inbandTextTracks_ = {};
39447       }
39448 
39449       for (var trackId in segment.captionStreams) {
39450         if (!sourceBuffer.inbandTextTracks_[trackId]) {
39451           player.tech_.trigger({
39452             type: 'usage',
39453             name: 'hls-608'
39454           });
39455           var track = player.textTracks().getTrackById(trackId);
39456 
39457           if (track) {
39458             // Resuse an existing track with a CC# id because this was
39459             // very likely created by videojs-contrib-hls from information
39460             // in the m3u8 for us to use
39461             sourceBuffer.inbandTextTracks_[trackId] = track;
39462           } else {
39463             // Otherwise, create a track with the default `CC#` label and
39464             // without a language
39465             sourceBuffer.inbandTextTracks_[trackId] = player.addRemoteTextTrack({
39466               kind: 'captions',
39467               id: trackId,
39468               label: trackId
39469             }, false).track;
39470           }
39471         }
39472       }
39473     }
39474 
39475     if (segment.metadata && segment.metadata.length && !sourceBuffer.metadataTrack_) {
39476       sourceBuffer.metadataTrack_ = player.addRemoteTextTrack({
39477         kind: 'metadata',
39478         label: 'Timed Metadata'
39479       }, false).track;
39480       sourceBuffer.metadataTrack_.inBandMetadataTrackDispatchType = segment.metadata.dispatchType;
39481     }
39482   };
39483   /**
39484    * @file remove-cues-from-track.js
39485    */
39486 
39487   /**
39488    * Remove cues from a track on video.js.
39489    *
39490    * @param {Double} start start of where we should remove the cue
39491    * @param {Double} end end of where the we should remove the cue
39492    * @param {Object} track the text track to remove the cues from
39493    * @private
39494    */
39495 
39496 
39497   var removeCuesFromTrack = function removeCuesFromTrack(start, end, track) {
39498     var i = void 0;
39499     var cue = void 0;
39500 
39501     if (!track) {
39502       return;
39503     }
39504 
39505     if (!track.cues) {
39506       return;
39507     }
39508 
39509     i = track.cues.length;
39510 
39511     while (i--) {
39512       cue = track.cues[i]; // Remove any overlapping cue
39513 
39514       if (cue.startTime <= end && cue.endTime >= start) {
39515         track.removeCue(cue);
39516       }
39517     }
39518   };
39519   /**
39520    * @file add-text-track-data.js
39521    */
39522 
39523   /**
39524    * Define properties on a cue for backwards compatability,
39525    * but warn the user that the way that they are using it
39526    * is depricated and will be removed at a later date.
39527    *
39528    * @param {Cue} cue the cue to add the properties on
39529    * @private
39530    */
39531 
39532 
39533   var deprecateOldCue = function deprecateOldCue(cue) {
39534     Object.defineProperties(cue.frame, {
39535       id: {
39536         get: function get() {
39537           videojs$1.log.warn('cue.frame.id is deprecated. Use cue.value.key instead.');
39538           return cue.value.key;
39539         }
39540       },
39541       value: {
39542         get: function get() {
39543           videojs$1.log.warn('cue.frame.value is deprecated. Use cue.value.data instead.');
39544           return cue.value.data;
39545         }
39546       },
39547       privateData: {
39548         get: function get() {
39549           videojs$1.log.warn('cue.frame.privateData is deprecated. Use cue.value.data instead.');
39550           return cue.value.data;
39551         }
39552       }
39553     });
39554   };
39555 
39556   var durationOfVideo = function durationOfVideo(duration) {
39557     var dur = void 0;
39558 
39559     if (isNaN(duration) || Math.abs(duration) === Infinity) {
39560       dur = Number.MAX_VALUE;
39561     } else {
39562       dur = duration;
39563     }
39564 
39565     return dur;
39566   };
39567   /**
39568    * Add text track data to a source handler given the captions and
39569    * metadata from the buffer.
39570    *
39571    * @param {Object} sourceHandler the virtual source buffer
39572    * @param {Array} captionArray an array of caption data
39573    * @param {Array} metadataArray an array of meta data
39574    * @private
39575    */
39576 
39577 
39578   var addTextTrackData = function addTextTrackData(sourceHandler, captionArray, metadataArray) {
39579     var Cue = window$1.WebKitDataCue || window$1.VTTCue;
39580 
39581     if (captionArray) {
39582       captionArray.forEach(function (caption) {
39583         var track = caption.stream;
39584         this.inbandTextTracks_[track].addCue(new Cue(caption.startTime + this.timestampOffset, caption.endTime + this.timestampOffset, caption.text));
39585       }, sourceHandler);
39586     }
39587 
39588     if (metadataArray) {
39589       var videoDuration = durationOfVideo(sourceHandler.mediaSource_.duration);
39590       metadataArray.forEach(function (metadata) {
39591         var time = metadata.cueTime + this.timestampOffset; // if time isn't a finite number between 0 and Infinity, like NaN,
39592         // ignore this bit of metadata.
39593         // This likely occurs when you have an non-timed ID3 tag like TIT2,
39594         // which is the "Title/Songname/Content description" frame
39595 
39596         if (typeof time !== 'number' || window$1.isNaN(time) || time < 0 || !(time < Infinity)) {
39597           return;
39598         }
39599 
39600         metadata.frames.forEach(function (frame) {
39601           var cue = new Cue(time, time, frame.value || frame.url || frame.data || '');
39602           cue.frame = frame;
39603           cue.value = frame;
39604           deprecateOldCue(cue);
39605           this.metadataTrack_.addCue(cue);
39606         }, this);
39607       }, sourceHandler); // Updating the metadeta cues so that
39608       // the endTime of each cue is the startTime of the next cue
39609       // the endTime of last cue is the duration of the video
39610 
39611       if (sourceHandler.metadataTrack_ && sourceHandler.metadataTrack_.cues && sourceHandler.metadataTrack_.cues.length) {
39612         var cues = sourceHandler.metadataTrack_.cues;
39613         var cuesArray = []; // Create a copy of the TextTrackCueList...
39614         // ...disregarding cues with a falsey value
39615 
39616         for (var i = 0; i < cues.length; i++) {
39617           if (cues[i]) {
39618             cuesArray.push(cues[i]);
39619           }
39620         } // Group cues by their startTime value
39621 
39622 
39623         var cuesGroupedByStartTime = cuesArray.reduce(function (obj, cue) {
39624           var timeSlot = obj[cue.startTime] || [];
39625           timeSlot.push(cue);
39626           obj[cue.startTime] = timeSlot;
39627           return obj;
39628         }, {}); // Sort startTimes by ascending order
39629 
39630         var sortedStartTimes = Object.keys(cuesGroupedByStartTime).sort(function (a, b) {
39631           return Number(a) - Number(b);
39632         }); // Map each cue group's endTime to the next group's startTime
39633 
39634         sortedStartTimes.forEach(function (startTime, idx) {
39635           var cueGroup = cuesGroupedByStartTime[startTime];
39636           var nextTime = Number(sortedStartTimes[idx + 1]) || videoDuration; // Map each cue's endTime the next group's startTime
39637 
39638           cueGroup.forEach(function (cue) {
39639             cue.endTime = nextTime;
39640           });
39641         });
39642       }
39643     }
39644   };
39645 
39646   var win = typeof window !== 'undefined' ? window : {},
39647       TARGET = typeof Symbol === 'undefined' ? '__target' : Symbol(),
39648       SCRIPT_TYPE = 'application/javascript',
39649       BlobBuilder = win.BlobBuilder || win.WebKitBlobBuilder || win.MozBlobBuilder || win.MSBlobBuilder,
39650       URL = win.URL || win.webkitURL || URL && URL.msURL,
39651       Worker = win.Worker;
39652   /**
39653    * Returns a wrapper around Web Worker code that is constructible.
39654    *
39655    * @function shimWorker
39656    *
39657    * @param { String }    filename    The name of the file
39658    * @param { Function }  fn          Function wrapping the code of the worker
39659    */
39660 
39661   function shimWorker(filename, fn) {
39662     return function ShimWorker(forceFallback) {
39663       var o = this;
39664 
39665       if (!fn) {
39666         return new Worker(filename);
39667       } else if (Worker && !forceFallback) {
39668         // Convert the function's inner code to a string to construct the worker
39669         var source = fn.toString().replace(/^function.+?{/, '').slice(0, -1),
39670             objURL = createSourceObject(source);
39671         this[TARGET] = new Worker(objURL);
39672         wrapTerminate(this[TARGET], objURL);
39673         return this[TARGET];
39674       } else {
39675         var selfShim = {
39676           postMessage: function postMessage(m) {
39677             if (o.onmessage) {
39678               setTimeout(function () {
39679                 o.onmessage({
39680                   data: m,
39681                   target: selfShim
39682                 });
39683               });
39684             }
39685           }
39686         };
39687         fn.call(selfShim);
39688 
39689         this.postMessage = function (m) {
39690           setTimeout(function () {
39691             selfShim.onmessage({
39692               data: m,
39693               target: o
39694             });
39695           });
39696         };
39697 
39698         this.isThisThread = true;
39699       }
39700     };
39701   } // Test Worker capabilities
39702 
39703 
39704   if (Worker) {
39705     var testWorker,
39706         objURL = createSourceObject('self.onmessage = function () {}'),
39707         testArray = new Uint8Array(1);
39708 
39709     try {
39710       testWorker = new Worker(objURL); // Native browser on some Samsung devices throws for transferables, let's detect it
39711 
39712       testWorker.postMessage(testArray, [testArray.buffer]);
39713     } catch (e) {
39714       Worker = null;
39715     } finally {
39716       URL.revokeObjectURL(objURL);
39717 
39718       if (testWorker) {
39719         testWorker.terminate();
39720       }
39721     }
39722   }
39723 
39724   function createSourceObject(str) {
39725     try {
39726       return URL.createObjectURL(new Blob([str], {
39727         type: SCRIPT_TYPE
39728       }));
39729     } catch (e) {
39730       var blob = new BlobBuilder();
39731       blob.append(str);
39732       return URL.createObjectURL(blob.getBlob(type));
39733     }
39734   }
39735 
39736   function wrapTerminate(worker, objURL) {
39737     if (!worker || !objURL) return;
39738     var term = worker.terminate;
39739     worker.objURL = objURL;
39740 
39741     worker.terminate = function () {
39742       if (worker.objURL) URL.revokeObjectURL(worker.objURL);
39743       term.call(worker);
39744     };
39745   }
39746 
39747   var TransmuxWorker = new shimWorker("./transmuxer-worker.worker.js", function (window, document$$1) {
39748     var self = this;
39749 
39750     var transmuxerWorker = function () {
39751       /**
39752        * mux.js
39753        *
39754        * Copyright (c) Brightcove
39755        * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
39756        *
39757        * A lightweight readable stream implemention that handles event dispatching.
39758        * Objects that inherit from streams should call init in their constructors.
39759        */
39760       var Stream = function Stream() {
39761         this.init = function () {
39762           var listeners = {};
39763           /**
39764            * Add a listener for a specified event type.
39765            * @param type {string} the event name
39766            * @param listener {function} the callback to be invoked when an event of
39767            * the specified type occurs
39768            */
39769 
39770           this.on = function (type, listener) {
39771             if (!listeners[type]) {
39772               listeners[type] = [];
39773             }
39774 
39775             listeners[type] = listeners[type].concat(listener);
39776           };
39777           /**
39778            * Remove a listener for a specified event type.
39779            * @param type {string} the event name
39780            * @param listener {function} a function previously registered for this
39781            * type of event through `on`
39782            */
39783 
39784 
39785           this.off = function (type, listener) {
39786             var index;
39787 
39788             if (!listeners[type]) {
39789               return false;
39790             }
39791 
39792             index = listeners[type].indexOf(listener);
39793             listeners[type] = listeners[type].slice();
39794             listeners[type].splice(index, 1);
39795             return index > -1;
39796           };
39797           /**
39798            * Trigger an event of the specified type on this stream. Any additional
39799            * arguments to this function are passed as parameters to event listeners.
39800            * @param type {string} the event name
39801            */
39802 
39803 
39804           this.trigger = function (type) {
39805             var callbacks, i, length, args;
39806             callbacks = listeners[type];
39807 
39808             if (!callbacks) {
39809               return;
39810             } // Slicing the arguments on every invocation of this method
39811             // can add a significant amount of overhead. Avoid the
39812             // intermediate object creation for the common case of a
39813             // single callback argument
39814 
39815 
39816             if (arguments.length === 2) {
39817               length = callbacks.length;
39818 
39819               for (i = 0; i < length; ++i) {
39820                 callbacks[i].call(this, arguments[1]);
39821               }
39822             } else {
39823               args = [];
39824               i = arguments.length;
39825 
39826               for (i = 1; i < arguments.length; ++i) {
39827                 args.push(arguments[i]);
39828               }
39829 
39830               length = callbacks.length;
39831 
39832               for (i = 0; i < length; ++i) {
39833                 callbacks[i].apply(this, args);
39834               }
39835             }
39836           };
39837           /**
39838            * Destroys the stream and cleans up.
39839            */
39840 
39841 
39842           this.dispose = function () {
39843             listeners = {};
39844           };
39845         };
39846       };
39847       /**
39848        * Forwards all `data` events on this stream to the destination stream. The
39849        * destination stream should provide a method `push` to receive the data
39850        * events as they arrive.
39851        * @param destination {stream} the stream that will receive all `data` events
39852        * @param autoFlush {boolean} if false, we will not call `flush` on the destination
39853        *                            when the current stream emits a 'done' event
39854        * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
39855        */
39856 
39857 
39858       Stream.prototype.pipe = function (destination) {
39859         this.on('data', function (data) {
39860           destination.push(data);
39861         });
39862         this.on('done', function (flushSource) {
39863           destination.flush(flushSource);
39864         });
39865         this.on('partialdone', function (flushSource) {
39866           destination.partialFlush(flushSource);
39867         });
39868         this.on('endedtimeline', function (flushSource) {
39869           destination.endTimeline(flushSource);
39870         });
39871         this.on('reset', function (flushSource) {
39872           destination.reset(flushSource);
39873         });
39874         return destination;
39875       }; // Default stream functions that are expected to be overridden to perform
39876       // actual work. These are provided by the prototype as a sort of no-op
39877       // implementation so that we don't have to check for their existence in the
39878       // `pipe` function above.
39879 
39880 
39881       Stream.prototype.push = function (data) {
39882         this.trigger('data', data);
39883       };
39884 
39885       Stream.prototype.flush = function (flushSource) {
39886         this.trigger('done', flushSource);
39887       };
39888 
39889       Stream.prototype.partialFlush = function (flushSource) {
39890         this.trigger('partialdone', flushSource);
39891       };
39892 
39893       Stream.prototype.endTimeline = function (flushSource) {
39894         this.trigger('endedtimeline', flushSource);
39895       };
39896 
39897       Stream.prototype.reset = function (flushSource) {
39898         this.trigger('reset', flushSource);
39899       };
39900 
39901       var stream = Stream;
39902       /**
39903        * mux.js
39904        *
39905        * Copyright (c) Brightcove
39906        * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
39907        *
39908        * Functions that generate fragmented MP4s suitable for use with Media
39909        * Source Extensions.
39910        */
39911 
39912       var UINT32_MAX = Math.pow(2, 32) - 1;
39913       var box, dinf, esds, ftyp, mdat, mfhd, minf, moof, moov, mvex, mvhd, trak, tkhd, mdia, mdhd, hdlr, sdtp, stbl, stsd, traf, trex, trun, types, MAJOR_BRAND, MINOR_VERSION, AVC1_BRAND, VIDEO_HDLR, AUDIO_HDLR, HDLR_TYPES, VMHD, SMHD, DREF, STCO, STSC, STSZ, STTS; // pre-calculate constants
39914 
39915       (function () {
39916         var i;
39917         types = {
39918           avc1: [],
39919           // codingname
39920           avcC: [],
39921           btrt: [],
39922           dinf: [],
39923           dref: [],
39924           esds: [],
39925           ftyp: [],
39926           hdlr: [],
39927           mdat: [],
39928           mdhd: [],
39929           mdia: [],
39930           mfhd: [],
39931           minf: [],
39932           moof: [],
39933           moov: [],
39934           mp4a: [],
39935           // codingname
39936           mvex: [],
39937           mvhd: [],
39938           pasp: [],
39939           sdtp: [],
39940           smhd: [],
39941           stbl: [],
39942           stco: [],
39943           stsc: [],
39944           stsd: [],
39945           stsz: [],
39946           stts: [],
39947           styp: [],
39948           tfdt: [],
39949           tfhd: [],
39950           traf: [],
39951           trak: [],
39952           trun: [],
39953           trex: [],
39954           tkhd: [],
39955           vmhd: []
39956         }; // In environments where Uint8Array is undefined (e.g., IE8), skip set up so that we
39957         // don't throw an error
39958 
39959         if (typeof Uint8Array === 'undefined') {
39960           return;
39961         }
39962 
39963         for (i in types) {
39964           if (types.hasOwnProperty(i)) {
vendor: 16,384 bytes, lines 39965-40334
39965             types[i] = [i.charCodeAt(0), i.charCodeAt(1), i.charCodeAt(2), i.charCodeAt(3)];
39966           }
39967         }
39968 
39969         MAJOR_BRAND = new Uint8Array(['i'.charCodeAt(0), 's'.charCodeAt(0), 'o'.charCodeAt(0), 'm'.charCodeAt(0)]);
39970         AVC1_BRAND = new Uint8Array(['a'.charCodeAt(0), 'v'.charCodeAt(0), 'c'.charCodeAt(0), '1'.charCodeAt(0)]);
39971         MINOR_VERSION = new Uint8Array([0, 0, 0, 1]);
39972         VIDEO_HDLR = new Uint8Array([0x00, // version 0
39973         0x00, 0x00, 0x00, // flags
39974         0x00, 0x00, 0x00, 0x00, // pre_defined
39975         0x76, 0x69, 0x64, 0x65, // handler_type: 'vide'
39976         0x00, 0x00, 0x00, 0x00, // reserved
39977         0x00, 0x00, 0x00, 0x00, // reserved
39978         0x00, 0x00, 0x00, 0x00, // reserved
39979         0x56, 0x69, 0x64, 0x65, 0x6f, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'VideoHandler'
39980         ]);
39981         AUDIO_HDLR = new Uint8Array([0x00, // version 0
39982         0x00, 0x00, 0x00, // flags
39983         0x00, 0x00, 0x00, 0x00, // pre_defined
39984         0x73, 0x6f, 0x75, 0x6e, // handler_type: 'soun'
39985         0x00, 0x00, 0x00, 0x00, // reserved
39986         0x00, 0x00, 0x00, 0x00, // reserved
39987         0x00, 0x00, 0x00, 0x00, // reserved
39988         0x53, 0x6f, 0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'SoundHandler'
39989         ]);
39990         HDLR_TYPES = {
39991           video: VIDEO_HDLR,
39992           audio: AUDIO_HDLR
39993         };
39994         DREF = new Uint8Array([0x00, // version 0
39995         0x00, 0x00, 0x00, // flags
39996         0x00, 0x00, 0x00, 0x01, // entry_count
39997         0x00, 0x00, 0x00, 0x0c, // entry_size
39998         0x75, 0x72, 0x6c, 0x20, // 'url' type
39999         0x00, // version 0
40000         0x00, 0x00, 0x01 // entry_flags
40001         ]);
40002         SMHD = new Uint8Array([0x00, // version
40003         0x00, 0x00, 0x00, // flags
40004         0x00, 0x00, // balance, 0 means centered
40005         0x00, 0x00 // reserved
40006         ]);
40007         STCO = new Uint8Array([0x00, // version
40008         0x00, 0x00, 0x00, // flags
40009         0x00, 0x00, 0x00, 0x00 // entry_count
40010         ]);
40011         STSC = STCO;
40012         STSZ = new Uint8Array([0x00, // version
40013         0x00, 0x00, 0x00, // flags
40014         0x00, 0x00, 0x00, 0x00, // sample_size
40015         0x00, 0x00, 0x00, 0x00 // sample_count
40016         ]);
40017         STTS = STCO;
40018         VMHD = new Uint8Array([0x00, // version
40019         0x00, 0x00, 0x01, // flags
40020         0x00, 0x00, // graphicsmode
40021         0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // opcolor
40022         ]);
40023       })();
40024 
40025       box = function box(type) {
40026         var payload = [],
40027             size = 0,
40028             i,
40029             result,
40030             view;
40031 
40032         for (i = 1; i < arguments.length; i++) {
40033           payload.push(arguments[i]);
40034         }
40035 
40036         i = payload.length; // calculate the total size we need to allocate
40037 
40038         while (i--) {
40039           size += payload[i].byteLength;
40040         }
40041 
40042         result = new Uint8Array(size + 8);
40043         view = new DataView(result.buffer, result.byteOffset, result.byteLength);
40044         view.setUint32(0, result.byteLength);
40045         result.set(type, 4); // copy the payload into the result
40046 
40047         for (i = 0, size = 8; i < payload.length; i++) {
40048           result.set(payload[i], size);
40049           size += payload[i].byteLength;
40050         }
40051 
40052         return result;
40053       };
40054 
40055       dinf = function dinf() {
40056         return box(types.dinf, box(types.dref, DREF));
40057       };
40058 
40059       esds = function esds(track) {
40060         return box(types.esds, new Uint8Array([0x00, // version
40061         0x00, 0x00, 0x00, // flags
40062         // ES_Descriptor
40063         0x03, // tag, ES_DescrTag
40064         0x19, // length
40065         0x00, 0x00, // ES_ID
40066         0x00, // streamDependenceFlag, URL_flag, reserved, streamPriority
40067         // DecoderConfigDescriptor
40068         0x04, // tag, DecoderConfigDescrTag
40069         0x11, // length
40070         0x40, // object type
40071         0x15, // streamType
40072         0x00, 0x06, 0x00, // bufferSizeDB
40073         0x00, 0x00, 0xda, 0xc0, // maxBitrate
40074         0x00, 0x00, 0xda, 0xc0, // avgBitrate
40075         // DecoderSpecificInfo
40076         0x05, // tag, DecoderSpecificInfoTag
40077         0x02, // length
40078         // ISO/IEC 14496-3, AudioSpecificConfig
40079         // for samplingFrequencyIndex see ISO/IEC 13818-7:2006, 8.1.3.2.2, Table 35
40080         track.audioobjecttype << 3 | track.samplingfrequencyindex >>> 1, track.samplingfrequencyindex << 7 | track.channelcount << 3, 0x06, 0x01, 0x02 // GASpecificConfig
40081         ]));
40082       };
40083 
40084       ftyp = function ftyp() {
40085         return box(types.ftyp, MAJOR_BRAND, MINOR_VERSION, MAJOR_BRAND, AVC1_BRAND);
40086       };
40087 
40088       hdlr = function hdlr(type) {
40089         return box(types.hdlr, HDLR_TYPES[type]);
40090       };
40091 
40092       mdat = function mdat(data) {
40093         return box(types.mdat, data);
40094       };
40095 
40096       mdhd = function mdhd(track) {
40097         var result = new Uint8Array([0x00, // version 0
40098         0x00, 0x00, 0x00, // flags
40099         0x00, 0x00, 0x00, 0x02, // creation_time
40100         0x00, 0x00, 0x00, 0x03, // modification_time
40101         0x00, 0x01, 0x5f, 0x90, // timescale, 90,000 "ticks" per second
40102         track.duration >>> 24 & 0xFF, track.duration >>> 16 & 0xFF, track.duration >>> 8 & 0xFF, track.duration & 0xFF, // duration
40103         0x55, 0xc4, // 'und' language (undetermined)
40104         0x00, 0x00]); // Use the sample rate from the track metadata, when it is
40105         // defined. The sample rate can be parsed out of an ADTS header, for
40106         // instance.
40107 
40108         if (track.samplerate) {
40109           result[12] = track.samplerate >>> 24 & 0xFF;
40110           result[13] = track.samplerate >>> 16 & 0xFF;
40111           result[14] = track.samplerate >>> 8 & 0xFF;
40112           result[15] = track.samplerate & 0xFF;
40113         }
40114 
40115         return box(types.mdhd, result);
40116       };
40117 
40118       mdia = function mdia(track) {
40119         return box(types.mdia, mdhd(track), hdlr(track.type), minf(track));
40120       };
40121 
40122       mfhd = function mfhd(sequenceNumber) {
40123         return box(types.mfhd, new Uint8Array([0x00, 0x00, 0x00, 0x00, // flags
40124         (sequenceNumber & 0xFF000000) >> 24, (sequenceNumber & 0xFF0000) >> 16, (sequenceNumber & 0xFF00) >> 8, sequenceNumber & 0xFF // sequence_number
40125         ]));
40126       };
40127 
40128       minf = function minf(track) {
40129         return box(types.minf, track.type === 'video' ? box(types.vmhd, VMHD) : box(types.smhd, SMHD), dinf(), stbl(track));
40130       };
40131 
40132       moof = function moof(sequenceNumber, tracks) {
40133         var trackFragments = [],
40134             i = tracks.length; // build traf boxes for each track fragment
40135 
40136         while (i--) {
40137           trackFragments[i] = traf(tracks[i]);
40138         }
40139 
40140         return box.apply(null, [types.moof, mfhd(sequenceNumber)].concat(trackFragments));
40141       };
40142       /**
40143        * Returns a movie box.
40144        * @param tracks {array} the tracks associated with this movie
40145        * @see ISO/IEC 14496-12:2012(E), section 8.2.1
40146        */
40147 
40148 
40149       moov = function moov(tracks) {
40150         var i = tracks.length,
40151             boxes = [];
40152 
40153         while (i--) {
40154           boxes[i] = trak(tracks[i]);
40155         }
40156 
40157         return box.apply(null, [types.moov, mvhd(0xffffffff)].concat(boxes).concat(mvex(tracks)));
40158       };
40159 
40160       mvex = function mvex(tracks) {
40161         var i = tracks.length,
40162             boxes = [];
40163 
40164         while (i--) {
40165           boxes[i] = trex(tracks[i]);
40166         }
40167 
40168         return box.apply(null, [types.mvex].concat(boxes));
40169       };
40170 
40171       mvhd = function mvhd(duration) {
40172         var bytes = new Uint8Array([0x00, // version 0
40173         0x00, 0x00, 0x00, // flags
40174         0x00, 0x00, 0x00, 0x01, // creation_time
40175         0x00, 0x00, 0x00, 0x02, // modification_time
40176         0x00, 0x01, 0x5f, 0x90, // timescale, 90,000 "ticks" per second
40177         (duration & 0xFF000000) >> 24, (duration & 0xFF0000) >> 16, (duration & 0xFF00) >> 8, duration & 0xFF, // duration
40178         0x00, 0x01, 0x00, 0x00, // 1.0 rate
40179         0x01, 0x00, // 1.0 volume
40180         0x00, 0x00, // reserved
40181         0x00, 0x00, 0x00, 0x00, // reserved
40182         0x00, 0x00, 0x00, 0x00, // reserved
40183         0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, // transformation: unity matrix
40184         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // pre_defined
40185         0xff, 0xff, 0xff, 0xff // next_track_ID
40186         ]);
40187         return box(types.mvhd, bytes);
40188       };
40189 
40190       sdtp = function sdtp(track) {
40191         var samples = track.samples || [],
40192             bytes = new Uint8Array(4 + samples.length),
40193             flags,
40194             i; // leave the full box header (4 bytes) all zero
40195         // write the sample table
40196 
40197         for (i = 0; i < samples.length; i++) {
40198           flags = samples[i].flags;
40199           bytes[i + 4] = flags.dependsOn << 4 | flags.isDependedOn << 2 | flags.hasRedundancy;
40200         }
40201 
40202         return box(types.sdtp, bytes);
40203       };
40204 
40205       stbl = function stbl(track) {
40206         return box(types.stbl, stsd(track), box(types.stts, STTS), box(types.stsc, STSC), box(types.stsz, STSZ), box(types.stco, STCO));
40207       };
40208 
40209       (function () {
40210         var videoSample, audioSample;
40211 
40212         stsd = function stsd(track) {
40213           return box(types.stsd, new Uint8Array([0x00, // version 0
40214           0x00, 0x00, 0x00, // flags
40215           0x00, 0x00, 0x00, 0x01]), track.type === 'video' ? videoSample(track) : audioSample(track));
40216         };
40217 
40218         videoSample = function videoSample(track) {
40219           var sps = track.sps || [],
40220               pps = track.pps || [],
40221               sequenceParameterSets = [],
40222               pictureParameterSets = [],
40223               i,
40224               avc1Box; // assemble the SPSs
40225 
40226           for (i = 0; i < sps.length; i++) {
40227             sequenceParameterSets.push((sps[i].byteLength & 0xFF00) >>> 8);
40228             sequenceParameterSets.push(sps[i].byteLength & 0xFF); // sequenceParameterSetLength
40229 
40230             sequenceParameterSets = sequenceParameterSets.concat(Array.prototype.slice.call(sps[i])); // SPS
40231           } // assemble the PPSs
40232 
40233 
40234           for (i = 0; i < pps.length; i++) {
40235             pictureParameterSets.push((pps[i].byteLength & 0xFF00) >>> 8);
40236             pictureParameterSets.push(pps[i].byteLength & 0xFF);
40237             pictureParameterSets = pictureParameterSets.concat(Array.prototype.slice.call(pps[i]));
40238           }
40239 
40240           avc1Box = [types.avc1, new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
40241           0x00, 0x01, // data_reference_index
40242           0x00, 0x00, // pre_defined
40243           0x00, 0x00, // reserved
40244           0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // pre_defined
40245           (track.width & 0xff00) >> 8, track.width & 0xff, // width
40246           (track.height & 0xff00) >> 8, track.height & 0xff, // height
40247           0x00, 0x48, 0x00, 0x00, // horizresolution
40248           0x00, 0x48, 0x00, 0x00, // vertresolution
40249           0x00, 0x00, 0x00, 0x00, // reserved
40250           0x00, 0x01, // frame_count
40251           0x13, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x6a, 0x73, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x2d, 0x68, 0x6c, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // compressorname
40252           0x00, 0x18, // depth = 24
40253           0x11, 0x11 // pre_defined = -1
40254           ]), box(types.avcC, new Uint8Array([0x01, // configurationVersion
40255           track.profileIdc, // AVCProfileIndication
40256           track.profileCompatibility, // profile_compatibility
40257           track.levelIdc, // AVCLevelIndication
40258           0xff // lengthSizeMinusOne, hard-coded to 4 bytes
40259           ].concat([sps.length], // numOfSequenceParameterSets
40260           sequenceParameterSets, // "SPS"
40261           [pps.length], // numOfPictureParameterSets
40262           pictureParameterSets // "PPS"
40263           ))), box(types.btrt, new Uint8Array([0x00, 0x1c, 0x9c, 0x80, // bufferSizeDB
40264           0x00, 0x2d, 0xc6, 0xc0, // maxBitrate
40265           0x00, 0x2d, 0xc6, 0xc0 // avgBitrate
40266           ]))];
40267 
40268           if (track.sarRatio) {
40269             var hSpacing = track.sarRatio[0],
40270                 vSpacing = track.sarRatio[1];
40271             avc1Box.push(box(types.pasp, new Uint8Array([(hSpacing & 0xFF000000) >> 24, (hSpacing & 0xFF0000) >> 16, (hSpacing & 0xFF00) >> 8, hSpacing & 0xFF, (vSpacing & 0xFF000000) >> 24, (vSpacing & 0xFF0000) >> 16, (vSpacing & 0xFF00) >> 8, vSpacing & 0xFF])));
40272           }
40273 
40274           return box.apply(null, avc1Box);
40275         };
40276 
40277         audioSample = function audioSample(track) {
40278           return box(types.mp4a, new Uint8Array([// SampleEntry, ISO/IEC 14496-12
40279           0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
40280           0x00, 0x01, // data_reference_index
40281           // AudioSampleEntry, ISO/IEC 14496-12
40282           0x00, 0x00, 0x00, 0x00, // reserved
40283           0x00, 0x00, 0x00, 0x00, // reserved
40284           (track.channelcount & 0xff00) >> 8, track.channelcount & 0xff, // channelcount
40285           (track.samplesize & 0xff00) >> 8, track.samplesize & 0xff, // samplesize
40286           0x00, 0x00, // pre_defined
40287           0x00, 0x00, // reserved
40288           (track.samplerate & 0xff00) >> 8, track.samplerate & 0xff, 0x00, 0x00 // samplerate, 16.16
40289           // MP4AudioSampleEntry, ISO/IEC 14496-14
40290           ]), esds(track));
40291         };
40292       })();
40293 
40294       tkhd = function tkhd(track) {
40295         var result = new Uint8Array([0x00, // version 0
40296         0x00, 0x00, 0x07, // flags
40297         0x00, 0x00, 0x00, 0x00, // creation_time
40298         0x00, 0x00, 0x00, 0x00, // modification_time
40299         (track.id & 0xFF000000) >> 24, (track.id & 0xFF0000) >> 16, (track.id & 0xFF00) >> 8, track.id & 0xFF, // track_ID
40300         0x00, 0x00, 0x00, 0x00, // reserved
40301         (track.duration & 0xFF000000) >> 24, (track.duration & 0xFF0000) >> 16, (track.duration & 0xFF00) >> 8, track.duration & 0xFF, // duration
40302         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
40303         0x00, 0x00, // layer
40304         0x00, 0x00, // alternate_group
40305         0x01, 0x00, // non-audio track volume
40306         0x00, 0x00, // reserved
40307         0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, // transformation: unity matrix
40308         (track.width & 0xFF00) >> 8, track.width & 0xFF, 0x00, 0x00, // width
40309         (track.height & 0xFF00) >> 8, track.height & 0xFF, 0x00, 0x00 // height
40310         ]);
40311         return box(types.tkhd, result);
40312       };
40313       /**
40314        * Generate a track fragment (traf) box. A traf box collects metadata
40315        * about tracks in a movie fragment (moof) box.
40316        */
40317 
40318 
40319       traf = function traf(track) {
40320         var trackFragmentHeader, trackFragmentDecodeTime, trackFragmentRun, sampleDependencyTable, dataOffset, upperWordBaseMediaDecodeTime, lowerWordBaseMediaDecodeTime;
40321         trackFragmentHeader = box(types.tfhd, new Uint8Array([0x00, // version 0
40322         0x00, 0x00, 0x3a, // flags
40323         (track.id & 0xFF000000) >> 24, (track.id & 0xFF0000) >> 16, (track.id & 0xFF00) >> 8, track.id & 0xFF, // track_ID
40324         0x00, 0x00, 0x00, 0x01, // sample_description_index
40325         0x00, 0x00, 0x00, 0x00, // default_sample_duration
40326         0x00, 0x00, 0x00, 0x00, // default_sample_size
40327         0x00, 0x00, 0x00, 0x00 // default_sample_flags
40328         ]));
40329         upperWordBaseMediaDecodeTime = Math.floor(track.baseMediaDecodeTime / (UINT32_MAX + 1));
40330         lowerWordBaseMediaDecodeTime = Math.floor(track.baseMediaDecodeTime % (UINT32_MAX + 1));
40331         trackFragmentDecodeTime = box(types.tfdt, new Uint8Array([0x01, // version 1
40332         0x00, 0x00, 0x00, // flags
40333         // baseMediaDecodeTime
40334         upperWordBaseMediaDecodeTime >>> 24 & 0xFF, upperWordBaseMediaDecodeTime >>> 16 & 0xFF, upperWordBaseMediaDecodeTime >>> 8 & 0xFF, upperWordBaseMediaDecodeTime & 0xFF, lowerWordBaseMediaDecodeTime >>> 24 & 0xFF, lowerWordBaseMediaDecodeTime >>> 16 & 0xFF, lowerWordBaseMediaDecodeTime >>> 8 & 0xFF, lowerWordBaseMediaDecodeTime &
vendor: 10,330 bytes, lines 40334-40579
40334 0xFF])); // the data offset specifies the number of bytes from the start of
40335         // the containing moof to the first payload byte of the associated
40336         // mdat
40337 
40338         dataOffset = 32 + // tfhd
40339         20 + // tfdt
40340         8 + // traf header
40341         16 + // mfhd
40342         8 + // moof header
40343         8; // mdat header
40344         // audio tracks require less metadata
40345 
40346         if (track.type === 'audio') {
40347           trackFragmentRun = trun(track, dataOffset);
40348           return box(types.traf, trackFragmentHeader, trackFragmentDecodeTime, trackFragmentRun);
40349         } // video tracks should contain an independent and disposable samples
40350         // box (sdtp)
40351         // generate one and adjust offsets to match
40352 
40353 
40354         sampleDependencyTable = sdtp(track);
40355         trackFragmentRun = trun(track, sampleDependencyTable.length + dataOffset);
40356         return box(types.traf, trackFragmentHeader, trackFragmentDecodeTime, trackFragmentRun, sampleDependencyTable);
40357       };
40358       /**
40359        * Generate a track box.
40360        * @param track {object} a track definition
40361        * @return {Uint8Array} the track box
40362        */
40363 
40364 
40365       trak = function trak(track) {
40366         track.duration = track.duration || 0xffffffff;
40367         return box(types.trak, tkhd(track), mdia(track));
40368       };
40369 
40370       trex = function trex(track) {
40371         var result = new Uint8Array([0x00, // version 0
40372         0x00, 0x00, 0x00, // flags
40373         (track.id & 0xFF000000) >> 24, (track.id & 0xFF0000) >> 16, (track.id & 0xFF00) >> 8, track.id & 0xFF, // track_ID
40374         0x00, 0x00, 0x00, 0x01, // default_sample_description_index
40375         0x00, 0x00, 0x00, 0x00, // default_sample_duration
40376         0x00, 0x00, 0x00, 0x00, // default_sample_size
40377         0x00, 0x01, 0x00, 0x01 // default_sample_flags
40378         ]); // the last two bytes of default_sample_flags is the sample
40379         // degradation priority, a hint about the importance of this sample
40380         // relative to others. Lower the degradation priority for all sample
40381         // types other than video.
40382 
40383         if (track.type !== 'video') {
40384           result[result.length - 1] = 0x00;
40385         }
40386 
40387         return box(types.trex, result);
40388       };
40389 
40390       (function () {
40391         var audioTrun, videoTrun, trunHeader; // This method assumes all samples are uniform. That is, if a
40392         // duration is present for the first sample, it will be present for
40393         // all subsequent samples.
40394         // see ISO/IEC 14496-12:2012, Section 8.8.8.1
40395 
40396         trunHeader = function trunHeader(samples, offset) {
40397           var durationPresent = 0,
40398               sizePresent = 0,
40399               flagsPresent = 0,
40400               compositionTimeOffset = 0; // trun flag constants
40401 
40402           if (samples.length) {
40403             if (samples[0].duration !== undefined) {
40404               durationPresent = 0x1;
40405             }
40406 
40407             if (samples[0].size !== undefined) {
40408               sizePresent = 0x2;
40409             }
40410 
40411             if (samples[0].flags !== undefined) {
40412               flagsPresent = 0x4;
40413             }
40414 
40415             if (samples[0].compositionTimeOffset !== undefined) {
40416               compositionTimeOffset = 0x8;
40417             }
40418           }
40419 
40420           return [0x00, // version 0
40421           0x00, durationPresent | sizePresent | flagsPresent | compositionTimeOffset, 0x01, // flags
40422           (samples.length & 0xFF000000) >>> 24, (samples.length & 0xFF0000) >>> 16, (samples.length & 0xFF00) >>> 8, samples.length & 0xFF, // sample_count
40423           (offset & 0xFF000000) >>> 24, (offset & 0xFF0000) >>> 16, (offset & 0xFF00) >>> 8, offset & 0xFF // data_offset
40424           ];
40425         };
40426 
40427         videoTrun = function videoTrun(track, offset) {
40428           var bytes, samples, sample, i;
40429           samples = track.samples || [];
40430           offset += 8 + 12 + 16 * samples.length;
40431           bytes = trunHeader(samples, offset);
40432 
40433           for (i = 0; i < samples.length; i++) {
40434             sample = samples[i];
40435             bytes = bytes.concat([(sample.duration & 0xFF000000) >>> 24, (sample.duration & 0xFF0000) >>> 16, (sample.duration & 0xFF00) >>> 8, sample.duration & 0xFF, // sample_duration
40436             (sample.size & 0xFF000000) >>> 24, (sample.size & 0xFF0000) >>> 16, (sample.size & 0xFF00) >>> 8, sample.size & 0xFF, // sample_size
40437             sample.flags.isLeading << 2 | sample.flags.dependsOn, sample.flags.isDependedOn << 6 | sample.flags.hasRedundancy << 4 | sample.flags.paddingValue << 1 | sample.flags.isNonSyncSample, sample.flags.degradationPriority & 0xF0 << 8, sample.flags.degradationPriority & 0x0F, // sample_flags
40438             (sample.compositionTimeOffset & 0xFF000000) >>> 24, (sample.compositionTimeOffset & 0xFF0000) >>> 16, (sample.compositionTimeOffset & 0xFF00) >>> 8, sample.compositionTimeOffset & 0xFF // sample_composition_time_offset
40439             ]);
40440           }
40441 
40442           return box(types.trun, new Uint8Array(bytes));
40443         };
40444 
40445         audioTrun = function audioTrun(track, offset) {
40446           var bytes, samples, sample, i;
40447           samples = track.samples || [];
40448           offset += 8 + 12 + 8 * samples.length;
40449           bytes = trunHeader(samples, offset);
40450 
40451           for (i = 0; i < samples.length; i++) {
40452             sample = samples[i];
40453             bytes = bytes.concat([(sample.duration & 0xFF000000) >>> 24, (sample.duration & 0xFF0000) >>> 16, (sample.duration & 0xFF00) >>> 8, sample.duration & 0xFF, // sample_duration
40454             (sample.size & 0xFF000000) >>> 24, (sample.size & 0xFF0000) >>> 16, (sample.size & 0xFF00) >>> 8, sample.size & 0xFF]); // sample_size
40455           }
40456 
40457           return box(types.trun, new Uint8Array(bytes));
40458         };
40459 
40460         trun = function trun(track, offset) {
40461           if (track.type === 'audio') {
40462             return audioTrun(track, offset);
40463           }
40464 
40465           return videoTrun(track, offset);
40466         };
40467       })();
40468 
40469       var mp4Generator = {
40470         ftyp: ftyp,
40471         mdat: mdat,
40472         moof: moof,
40473         moov: moov,
40474         initSegment: function initSegment(tracks) {
40475           var fileType = ftyp(),
40476               movie = moov(tracks),
40477               result;
40478           result = new Uint8Array(fileType.byteLength + movie.byteLength);
40479           result.set(fileType);
40480           result.set(movie, fileType.byteLength);
40481           return result;
40482         }
40483       };
40484       /**
40485        * mux.js
40486        *
40487        * Copyright (c) Brightcove
40488        * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
40489        */
40490       // Convert an array of nal units into an array of frames with each frame being
40491       // composed of the nal units that make up that frame
40492       // Also keep track of cummulative data about the frame from the nal units such
40493       // as the frame duration, starting pts, etc.
40494 
40495       var groupNalsIntoFrames = function groupNalsIntoFrames(nalUnits) {
40496         var i,
40497             currentNal,
40498             currentFrame = [],
40499             frames = []; // TODO added for LHLS, make sure this is OK
40500 
40501         frames.byteLength = 0;
40502         frames.nalCount = 0;
40503         frames.duration = 0;
40504         currentFrame.byteLength = 0;
40505 
40506         for (i = 0; i < nalUnits.length; i++) {
40507           currentNal = nalUnits[i]; // Split on 'aud'-type nal units
40508 
40509           if (currentNal.nalUnitType === 'access_unit_delimiter_rbsp') {
40510             // Since the very first nal unit is expected to be an AUD
40511             // only push to the frames array when currentFrame is not empty
40512             if (currentFrame.length) {
40513               currentFrame.duration = currentNal.dts - currentFrame.dts; // TODO added for LHLS, make sure this is OK
40514 
40515               frames.byteLength += currentFrame.byteLength;
40516               frames.nalCount += currentFrame.length;
40517               frames.duration += currentFrame.duration;
40518               frames.push(currentFrame);
40519             }
40520 
40521             currentFrame = [currentNal];
40522             currentFrame.byteLength = currentNal.data.byteLength;
40523             currentFrame.pts = currentNal.pts;
40524             currentFrame.dts = currentNal.dts;
40525           } else {
40526             // Specifically flag key frames for ease of use later
40527             if (currentNal.nalUnitType === 'slice_layer_without_partitioning_rbsp_idr') {
40528               currentFrame.keyFrame = true;
40529             }
40530 
40531             currentFrame.duration = currentNal.dts - currentFrame.dts;
40532             currentFrame.byteLength += currentNal.data.byteLength;
40533             currentFrame.push(currentNal);
40534           }
40535         } // For the last frame, use the duration of the previous frame if we
40536         // have nothing better to go on
40537 
40538 
40539         if (frames.length && (!currentFrame.duration || currentFrame.duration <= 0)) {
40540           currentFrame.duration = frames[frames.length - 1].duration;
40541         } // Push the final frame
40542         // TODO added for LHLS, make sure this is OK
40543 
40544 
40545         frames.byteLength += currentFrame.byteLength;
40546         frames.nalCount += currentFrame.length;
40547         frames.duration += currentFrame.duration;
40548         frames.push(currentFrame);
40549         return frames;
40550       }; // Convert an array of frames into an array of Gop with each Gop being composed
40551       // of the frames that make up that Gop
40552       // Also keep track of cummulative data about the Gop from the frames such as the
40553       // Gop duration, starting pts, etc.
40554 
40555 
40556       var groupFramesIntoGops = function groupFramesIntoGops(frames) {
40557         var i,
40558             currentFrame,
40559             currentGop = [],
40560             gops = []; // We must pre-set some of the values on the Gop since we
40561         // keep running totals of these values
40562 
40563         currentGop.byteLength = 0;
40564         currentGop.nalCount = 0;
40565         currentGop.duration = 0;
40566         currentGop.pts = frames[0].pts;
40567         currentGop.dts = frames[0].dts; // store some metadata about all the Gops
40568 
40569         gops.byteLength = 0;
40570         gops.nalCount = 0;
40571         gops.duration = 0;
40572         gops.pts = frames[0].pts;
40573         gops.dts = frames[0].dts;
40574 
40575         for (i = 0; i < frames.length; i++) {
40576           currentFrame = frames[i];
40577 
40578           if (currentFrame.keyFrame) {
40579             // Since the very first frame is expected to be an keyframe
vendor: 3,087 bytes, lines 40580-40667
40580             // only push to the gops array when currentGop is not empty
40581             if (currentGop.length) {
40582               gops.push(currentGop);
40583               gops.byteLength += currentGop.byteLength;
40584               gops.nalCount += currentGop.nalCount;
40585               gops.duration += currentGop.duration;
40586             }
40587 
40588             currentGop = [currentFrame];
40589             currentGop.nalCount = currentFrame.length;
40590             currentGop.byteLength = currentFrame.byteLength;
40591             currentGop.pts = currentFrame.pts;
40592             currentGop.dts = currentFrame.dts;
40593             currentGop.duration = currentFrame.duration;
40594           } else {
40595             currentGop.duration += currentFrame.duration;
40596             currentGop.nalCount += currentFrame.length;
40597             currentGop.byteLength += currentFrame.byteLength;
40598             currentGop.push(currentFrame);
40599           }
40600         }
40601 
40602         if (gops.length && currentGop.duration <= 0) {
40603           currentGop.duration = gops[gops.length - 1].duration;
40604         }
40605 
40606         gops.byteLength += currentGop.byteLength;
40607         gops.nalCount += currentGop.nalCount;
40608         gops.duration += currentGop.duration; // push the final Gop
40609 
40610         gops.push(currentGop);
40611         return gops;
40612       };
40613       /*
40614        * Search for the first keyframe in the GOPs and throw away all frames
40615        * until that keyframe. Then extend the duration of the pulled keyframe
40616        * and pull the PTS and DTS of the keyframe so that it covers the time
40617        * range of the frames that were disposed.
40618        *
40619        * @param {Array} gops video GOPs
40620        * @returns {Array} modified video GOPs
40621        */
40622 
40623 
40624       var extendFirstKeyFrame = function extendFirstKeyFrame(gops) {
40625         var currentGop;
40626 
40627         if (!gops[0][0].keyFrame && gops.length > 1) {
40628           // Remove the first GOP
40629           currentGop = gops.shift();
40630           gops.byteLength -= currentGop.byteLength;
40631           gops.nalCount -= currentGop.nalCount; // Extend the first frame of what is now the
40632           // first gop to cover the time period of the
40633           // frames we just removed
40634 
40635           gops[0][0].dts = currentGop.dts;
40636           gops[0][0].pts = currentGop.pts;
40637           gops[0][0].duration += currentGop.duration;
40638         }
40639 
40640         return gops;
40641       };
40642       /**
40643        * Default sample object
40644        * see ISO/IEC 14496-12:2012, section 8.6.4.3
40645        */
40646 
40647 
40648       var createDefaultSample = function createDefaultSample() {
40649         return {
40650           size: 0,
40651           flags: {
40652             isLeading: 0,
40653             dependsOn: 1,
40654             isDependedOn: 0,
40655             hasRedundancy: 0,
40656             degradationPriority: 0,
40657             isNonSyncSample: 1
40658           }
40659         };
40660       };
40661       /*
40662        * Collates information from a video frame into an object for eventual
40663        * entry into an MP4 sample table.
40664        *
40665        * @param {Object} frame the video frame
40666        * @param {Number} dataOffset the byte offset to position the sample
40667        * @return {Object}
vendor: 7,724 bytes, lines 40667-40877
40667 object containing sample table info for a frame
40668        */
40669 
40670 
40671       var sampleForFrame = function sampleForFrame(frame, dataOffset) {
40672         var sample = createDefaultSample();
40673         sample.dataOffset = dataOffset;
40674         sample.compositionTimeOffset = frame.pts - frame.dts;
40675         sample.duration = frame.duration;
40676         sample.size = 4 * frame.length; // Space for nal unit size
40677 
40678         sample.size += frame.byteLength;
40679 
40680         if (frame.keyFrame) {
40681           sample.flags.dependsOn = 2;
40682           sample.flags.isNonSyncSample = 0;
40683         }
40684 
40685         return sample;
40686       }; // generate the track's sample table from an array of gops
40687 
40688 
40689       var generateSampleTable = function generateSampleTable(gops, baseDataOffset) {
40690         var h,
40691             i,
40692             sample,
40693             currentGop,
40694             currentFrame,
40695             dataOffset = baseDataOffset || 0,
40696             samples = [];
40697 
40698         for (h = 0; h < gops.length; h++) {
40699           currentGop = gops[h];
40700 
40701           for (i = 0; i < currentGop.length; i++) {
40702             currentFrame = currentGop[i];
40703             sample = sampleForFrame(currentFrame, dataOffset);
40704             dataOffset += sample.size;
40705             samples.push(sample);
40706           }
40707         }
40708 
40709         return samples;
40710       }; // generate the track's raw mdat data from an array of gops
40711 
40712 
40713       var concatenateNalData = function concatenateNalData(gops) {
40714         var h,
40715             i,
40716             j,
40717             currentGop,
40718             currentFrame,
40719             currentNal,
40720             dataOffset = 0,
40721             nalsByteLength = gops.byteLength,
40722             numberOfNals = gops.nalCount,
40723             totalByteLength = nalsByteLength + 4 * numberOfNals,
40724             data = new Uint8Array(totalByteLength),
40725             view = new DataView(data.buffer); // For each Gop..
40726 
40727         for (h = 0; h < gops.length; h++) {
40728           currentGop = gops[h]; // For each Frame..
40729 
40730           for (i = 0; i < currentGop.length; i++) {
40731             currentFrame = currentGop[i]; // For each NAL..
40732 
40733             for (j = 0; j < currentFrame.length; j++) {
40734               currentNal = currentFrame[j];
40735               view.setUint32(dataOffset, currentNal.data.byteLength);
40736               dataOffset += 4;
40737               data.set(currentNal.data, dataOffset);
40738               dataOffset += currentNal.data.byteLength;
40739             }
40740           }
40741         }
40742 
40743         return data;
40744       }; // generate the track's sample table from a frame
40745 
40746 
40747       var generateSampleTableForFrame = function generateSampleTableForFrame(frame, baseDataOffset) {
40748         var sample,
40749             dataOffset = baseDataOffset || 0,
40750             samples = [];
40751         sample = sampleForFrame(frame, dataOffset);
40752         samples.push(sample);
40753         return samples;
40754       }; // generate the track's raw mdat data from a frame
40755 
40756 
40757       var concatenateNalDataForFrame = function concatenateNalDataForFrame(frame) {
40758         var i,
40759             currentNal,
40760             dataOffset = 0,
40761             nalsByteLength = frame.byteLength,
40762             numberOfNals = frame.length,
40763             totalByteLength = nalsByteLength + 4 * numberOfNals,
40764             data = new Uint8Array(totalByteLength),
40765             view = new DataView(data.buffer); // For each NAL..
40766 
40767         for (i = 0; i < frame.length; i++) {
40768           currentNal = frame[i];
40769           view.setUint32(dataOffset, currentNal.data.byteLength);
40770           dataOffset += 4;
40771           data.set(currentNal.data, dataOffset);
40772           dataOffset += currentNal.data.byteLength;
40773         }
40774 
40775         return data;
40776       };
40777 
40778       var frameUtils = {
40779         groupNalsIntoFrames: groupNalsIntoFrames,
40780         groupFramesIntoGops: groupFramesIntoGops,
40781         extendFirstKeyFrame: extendFirstKeyFrame,
40782         generateSampleTable: generateSampleTable,
40783         concatenateNalData: concatenateNalData,
40784         generateSampleTableForFrame: generateSampleTableForFrame,
40785         concatenateNalDataForFrame: concatenateNalDataForFrame
40786       };
40787       /**
40788        * mux.js
40789        *
40790        * Copyright (c) Brightcove
40791        * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
40792        */
40793 
40794       var highPrefix = [33, 16, 5, 32, 164, 27];
40795       var lowPrefix = [33, 65, 108, 84, 1, 2, 4, 8, 168, 2, 4, 8, 17, 191, 252];
40796 
40797       var zeroFill = function zeroFill(count) {
40798         var a = [];
40799 
40800         while (count--) {
40801           a.push(0);
40802         }
40803 
40804         return a;
40805       };
40806 
40807       var makeTable = function makeTable(metaTable) {
40808         return Object.keys(metaTable).reduce(function (obj, key) {
40809           obj[key] = new Uint8Array(metaTable[key].reduce(function (arr, part) {
40810             return arr.concat(part);
40811           }, []));
40812           return obj;
40813         }, {});
40814       }; // Frames-of-silence to use for filling in missing AAC frames
40815 
40816 
40817       var coneOfSilence = {
40818         96000: [highPrefix, [227, 64], zeroFill(154), [56]],
40819         88200: [highPrefix, [231], zeroFill(170), [56]],
40820         64000: [highPrefix, [248, 192], zeroFill(240), [56]],
40821         48000: [highPrefix, [255, 192], zeroFill(268), [55, 148, 128], zeroFill(54), [112]],
40822         44100: [highPrefix, [255, 192], zeroFill(268), [55, 163, 128], zeroFill(84), [112]],
40823         32000: [highPrefix, [255, 192], zeroFill(268), [55, 234], zeroFill(226), [112]],
40824         24000: [highPrefix, [255, 192], zeroFill(268), [55, 255, 128], zeroFill(268), [111, 112], zeroFill(126), [224]],
40825         16000: [highPrefix, [255, 192], zeroFill(268), [55, 255, 128], zeroFill(268), [111, 255], zeroFill(269), [223, 108], zeroFill(195), [1, 192]],
40826         12000: [lowPrefix, zeroFill(268), [3, 127, 248], zeroFill(268), [6, 255, 240], zeroFill(268), [13, 255, 224], zeroFill(268), [27, 253, 128], zeroFill(259), [56]],
40827         11025: [lowPrefix, zeroFill(268), [3, 127, 248], zeroFill(268), [6, 255, 240], zeroFill(268), [13, 255, 224], zeroFill(268), [27, 255, 192], zeroFill(268), [55, 175, 128], zeroFill(108), [112]],
40828         8000: [lowPrefix, zeroFill(268), [3, 121, 16], zeroFill(47), [7]]
40829       };
40830       var silence = makeTable(coneOfSilence);
40831       /**
40832        * mux.js
40833        *
40834        * Copyright (c) Brightcove
40835        * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
40836        */
40837 
40838       var ONE_SECOND_IN_TS = 90000,
40839           // 90kHz clock
40840       secondsToVideoTs,
40841           secondsToAudioTs,
40842           videoTsToSeconds,
40843           audioTsToSeconds,
40844           audioTsToVideoTs,
40845           videoTsToAudioTs,
40846           metadataTsToSeconds;
40847 
40848       secondsToVideoTs = function secondsToVideoTs(seconds) {
40849         return seconds * ONE_SECOND_IN_TS;
40850       };
40851 
40852       secondsToAudioTs = function secondsToAudioTs(seconds, sampleRate) {
40853         return seconds * sampleRate;
40854       };
40855 
40856       videoTsToSeconds = function videoTsToSeconds(timestamp) {
40857         return timestamp / ONE_SECOND_IN_TS;
40858       };
40859 
40860       audioTsToSeconds = function audioTsToSeconds(timestamp, sampleRate) {
40861         return timestamp / sampleRate;
40862       };
40863 
40864       audioTsToVideoTs = function audioTsToVideoTs(timestamp, sampleRate) {
40865         return secondsToVideoTs(audioTsToSeconds(timestamp, sampleRate));
40866       };
40867 
40868       videoTsToAudioTs = function videoTsToAudioTs(timestamp, sampleRate) {
40869         return secondsToAudioTs(videoTsToSeconds(timestamp), sampleRate);
40870       };
40871       /**
40872        * Adjust ID3 tag or caption timing information by the timeline pts values
40873        * (if keepOriginalTimestamps is false) and convert to seconds
40874        */
40875 
40876 
40877       metadataTsToSeconds = function metadataTsToSeconds(timestamp, timelineStartPts, 
vendor: 4,368 bytes, lines 40877-40989
40877keepOriginalTimestamps) {
40878         return videoTsToSeconds(keepOriginalTimestamps ? timestamp : timestamp - timelineStartPts);
40879       };
40880 
40881       var clock = {
40882         ONE_SECOND_IN_TS: ONE_SECOND_IN_TS,
40883         secondsToVideoTs: secondsToVideoTs,
40884         secondsToAudioTs: secondsToAudioTs,
40885         videoTsToSeconds: videoTsToSeconds,
40886         audioTsToSeconds: audioTsToSeconds,
40887         audioTsToVideoTs: audioTsToVideoTs,
40888         videoTsToAudioTs: videoTsToAudioTs,
40889         metadataTsToSeconds: metadataTsToSeconds
40890       };
40891       /**
40892        * mux.js
40893        *
40894        * Copyright (c) Brightcove
40895        * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
40896        */
40897 
40898       /**
40899        * Sum the `byteLength` properties of the data in each AAC frame
40900        */
40901 
40902       var sumFrameByteLengths = function sumFrameByteLengths(array) {
40903         var i,
40904             currentObj,
40905             sum = 0; // sum the byteLength's all each nal unit in the frame
40906 
40907         for (i = 0; i < array.length; i++) {
40908           currentObj = array[i];
40909           sum += currentObj.data.byteLength;
40910         }
40911 
40912         return sum;
40913       }; // Possibly pad (prefix) the audio track with silence if appending this track
40914       // would lead to the introduction of a gap in the audio buffer
40915 
40916 
40917       var prefixWithSilence = function prefixWithSilence(track, frames, audioAppendStartTs, videoBaseMediaDecodeTime) {
40918         var baseMediaDecodeTimeTs,
40919             frameDuration = 0,
40920             audioGapDuration = 0,
40921             audioFillFrameCount = 0,
40922             audioFillDuration = 0,
40923             silentFrame,
40924             i,
40925             firstFrame;
40926 
40927         if (!frames.length) {
40928           return;
40929         }
40930 
40931         baseMediaDecodeTimeTs = clock.audioTsToVideoTs(track.baseMediaDecodeTime, track.samplerate); // determine frame clock duration based on sample rate, round up to avoid overfills
40932 
40933         frameDuration = Math.ceil(clock.ONE_SECOND_IN_TS / (track.samplerate / 1024));
40934 
40935         if (audioAppendStartTs && videoBaseMediaDecodeTime) {
40936           // insert the shortest possible amount (audio gap or audio to video gap)
40937           audioGapDuration = baseMediaDecodeTimeTs - Math.max(audioAppendStartTs, videoBaseMediaDecodeTime); // number of full frames in the audio gap
40938 
40939           audioFillFrameCount = Math.floor(audioGapDuration / frameDuration);
40940           audioFillDuration = audioFillFrameCount * frameDuration;
40941         } // don't attempt to fill gaps smaller than a single frame or larger
40942         // than a half second
40943 
40944 
40945         if (audioFillFrameCount < 1 || audioFillDuration > clock.ONE_SECOND_IN_TS / 2) {
40946           return;
40947         }
40948 
40949         silentFrame = silence[track.samplerate];
40950 
40951         if (!silentFrame) {
40952           // we don't have a silent frame pregenerated for the sample rate, so use a frame
40953           // from the content instead
40954           silentFrame = frames[0].data;
40955         }
40956 
40957         for (i = 0; i < audioFillFrameCount; i++) {
40958           firstFrame = frames[0];
40959           frames.splice(0, 0, {
40960             data: silentFrame,
40961             dts: firstFrame.dts - frameDuration,
40962             pts: firstFrame.pts - frameDuration
40963           });
40964         }
40965 
40966         track.baseMediaDecodeTime -= Math.floor(clock.videoTsToAudioTs(audioFillDuration, track.samplerate));
40967       }; // If the audio segment extends before the earliest allowed dts
40968       // value, remove AAC frames until starts at or after the earliest
40969       // allowed DTS so that we don't end up with a negative baseMedia-
40970       // DecodeTime for the audio track
40971 
40972 
40973       var trimAdtsFramesByEarliestDts = function trimAdtsFramesByEarliestDts(adtsFrames, track, earliestAllowedDts) {
40974         if (track.minSegmentDts >= earliestAllowedDts) {
40975           return adtsFrames;
40976         } // We will need to recalculate the earliest segment Dts
40977 
40978 
40979         track.minSegmentDts = Infinity;
40980         return adtsFrames.filter(function (currentFrame) {
40981           // If this is an allowed frame, keep it and record it's Dts
40982           if (currentFrame.dts >= earliestAllowedDts) {
40983             track.minSegmentDts = Math.min(track.minSegmentDts, currentFrame.dts);
40984             track.minSegmentPts = track.minSegmentDts;
40985             return true;
40986           } // Otherwise, discard it
40987 
40988 
40989           return false;
vendor: 14,025 bytes, lines 40990-41382
40990         });
40991       }; // generate the track's raw mdat data from an array of frames
40992 
40993 
40994       var generateSampleTable$1 = function generateSampleTable(frames) {
40995         var i,
40996             currentFrame,
40997             samples = [];
40998 
40999         for (i = 0; i < frames.length; i++) {
41000           currentFrame = frames[i];
41001           samples.push({
41002             size: currentFrame.data.byteLength,
41003             duration: 1024 // For AAC audio, all samples contain 1024 samples
41004 
41005           });
41006         }
41007 
41008         return samples;
41009       }; // generate the track's sample table from an array of frames
41010 
41011 
41012       var concatenateFrameData = function concatenateFrameData(frames) {
41013         var i,
41014             currentFrame,
41015             dataOffset = 0,
41016             data = new Uint8Array(sumFrameByteLengths(frames));
41017 
41018         for (i = 0; i < frames.length; i++) {
41019           currentFrame = frames[i];
41020           data.set(currentFrame.data, dataOffset);
41021           dataOffset += currentFrame.data.byteLength;
41022         }
41023 
41024         return data;
41025       };
41026 
41027       var audioFrameUtils = {
41028         prefixWithSilence: prefixWithSilence,
41029         trimAdtsFramesByEarliestDts: trimAdtsFramesByEarliestDts,
41030         generateSampleTable: generateSampleTable$1,
41031         concatenateFrameData: concatenateFrameData
41032       };
41033       /**
41034        * mux.js
41035        *
41036        * Copyright (c) Brightcove
41037        * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
41038        */
41039 
41040       var ONE_SECOND_IN_TS$1 = clock.ONE_SECOND_IN_TS;
41041       /**
41042        * Store information about the start and end of the track and the
41043        * duration for each frame/sample we process in order to calculate
41044        * the baseMediaDecodeTime
41045        */
41046 
41047       var collectDtsInfo = function collectDtsInfo(track, data) {
41048         if (typeof data.pts === 'number') {
41049           if (track.timelineStartInfo.pts === undefined) {
41050             track.timelineStartInfo.pts = data.pts;
41051           }
41052 
41053           if (track.minSegmentPts === undefined) {
41054             track.minSegmentPts = data.pts;
41055           } else {
41056             track.minSegmentPts = Math.min(track.minSegmentPts, data.pts);
41057           }
41058 
41059           if (track.maxSegmentPts === undefined) {
41060             track.maxSegmentPts = data.pts;
41061           } else {
41062             track.maxSegmentPts = Math.max(track.maxSegmentPts, data.pts);
41063           }
41064         }
41065 
41066         if (typeof data.dts === 'number') {
41067           if (track.timelineStartInfo.dts === undefined) {
41068             track.timelineStartInfo.dts = data.dts;
41069           }
41070 
41071           if (track.minSegmentDts === undefined) {
41072             track.minSegmentDts = data.dts;
41073           } else {
41074             track.minSegmentDts = Math.min(track.minSegmentDts, data.dts);
41075           }
41076 
41077           if (track.maxSegmentDts === undefined) {
41078             track.maxSegmentDts = data.dts;
41079           } else {
41080             track.maxSegmentDts = Math.max(track.maxSegmentDts, data.dts);
41081           }
41082         }
41083       };
41084       /**
41085        * Clear values used to calculate the baseMediaDecodeTime between
41086        * tracks
41087        */
41088 
41089 
41090       var clearDtsInfo = function clearDtsInfo(track) {
41091         delete track.minSegmentDts;
41092         delete track.maxSegmentDts;
41093         delete track.minSegmentPts;
41094         delete track.maxSegmentPts;
41095       };
41096       /**
41097        * Calculate the track's baseMediaDecodeTime based on the earliest
41098        * DTS the transmuxer has ever seen and the minimum DTS for the
41099        * current track
41100        * @param track {object} track metadata configuration
41101        * @param keepOriginalTimestamps {boolean} If true, keep the timestamps
41102        *        in the source; false to adjust the first segment to start at 0.
41103        */
41104 
41105 
41106       var calculateTrackBaseMediaDecodeTime = function calculateTrackBaseMediaDecodeTime(track, keepOriginalTimestamps) {
41107         var baseMediaDecodeTime,
41108             scale,
41109             minSegmentDts = track.minSegmentDts; // Optionally adjust the time so the first segment starts at zero.
41110 
41111         if (!keepOriginalTimestamps) {
41112           minSegmentDts -= track.timelineStartInfo.dts;
41113         } // track.timelineStartInfo.baseMediaDecodeTime is the location, in time, where
41114         // we want the start of the first segment to be placed
41115 
41116 
41117         baseMediaDecodeTime = track.timelineStartInfo.baseMediaDecodeTime; // Add to that the distance this segment is from the very first
41118 
41119         baseMediaDecodeTime += minSegmentDts; // baseMediaDecodeTime must not become negative
41120 
41121         baseMediaDecodeTime = Math.max(0, baseMediaDecodeTime);
41122 
41123         if (track.type === 'audio') {
41124           // Audio has a different clock equal to the sampling_rate so we need to
41125           // scale the PTS values into the clock rate of the track
41126           scale = track.samplerate / ONE_SECOND_IN_TS$1;
41127           baseMediaDecodeTime *= scale;
41128           baseMediaDecodeTime = Math.floor(baseMediaDecodeTime);
41129         }
41130 
41131         return baseMediaDecodeTime;
41132       };
41133 
41134       var trackDecodeInfo = {
41135         clearDtsInfo: clearDtsInfo,
41136         calculateTrackBaseMediaDecodeTime: calculateTrackBaseMediaDecodeTime,
41137         collectDtsInfo: collectDtsInfo
41138       };
41139       /**
41140        * mux.js
41141        *
41142        * Copyright (c) Brightcove
41143        * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
41144        *
41145        * Reads in-band caption information from a video elementary
41146        * stream. Captions must follow the CEA-708 standard for injection
41147        * into an MPEG-2 transport streams.
41148        * @see https://en.wikipedia.org/wiki/CEA-708
41149        * @see https://www.gpo.gov/fdsys/pkg/CFR-2007-title47-vol1/pdf/CFR-2007-title47-vol1-sec15-119.pdf
41150        */
41151       // Supplemental enhancement information (SEI) NAL units have a
41152       // payload type field to indicate how they are to be
41153       // interpreted. CEAS-708 caption content is always transmitted with
41154       // payload type 0x04.
41155 
41156       var USER_DATA_REGISTERED_ITU_T_T35 = 4,
41157           RBSP_TRAILING_BITS = 128;
41158       /**
41159         * Parse a supplemental enhancement information (SEI) NAL unit.
41160         * Stops parsing once a message of type ITU T T35 has been found.
41161         *
41162         * @param bytes {Uint8Array} the bytes of a SEI NAL unit
41163         * @return {object} the parsed SEI payload
41164         * @see Rec. ITU-T H.264, 7.3.2.3.1
41165         */
41166 
41167       var parseSei = function parseSei(bytes) {
41168         var i = 0,
41169             result = {
41170           payloadType: -1,
41171           payloadSize: 0
41172         },
41173             payloadType = 0,
41174             payloadSize = 0; // go through the sei_rbsp parsing each each individual sei_message
41175 
41176         while (i < bytes.byteLength) {
41177           // stop once we have hit the end of the sei_rbsp
41178           if (bytes[i] === RBSP_TRAILING_BITS) {
41179             break;
41180           } // Parse payload type
41181 
41182 
41183           while (bytes[i] === 0xFF) {
41184             payloadType += 255;
41185             i++;
41186           }
41187 
41188           payloadType += bytes[i++]; // Parse payload size
41189 
41190           while (bytes[i] === 0xFF) {
41191             payloadSize += 255;
41192             i++;
41193           }
41194 
41195           payloadSize += bytes[i++]; // this sei_message is a 608/708 caption so save it and break
41196           // there can only ever be one caption message in a frame's sei
41197 
41198           if (!result.payload && payloadType === USER_DATA_REGISTERED_ITU_T_T35) {
41199             result.payloadType = payloadType;
41200             result.payloadSize = payloadSize;
41201             result.payload = bytes.subarray(i, i + payloadSize);
41202             break;
41203           } // skip the payload and parse the next message
41204 
41205 
41206           i += payloadSize;
41207           payloadType = 0;
41208           payloadSize = 0;
41209         }
41210 
41211         return result;
41212       }; // see ANSI/SCTE 128-1 (2013), section 8.1
41213 
41214 
41215       var parseUserData = function parseUserData(sei) {
41216         // itu_t_t35_contry_code must be 181 (United States) for
41217         // captions
41218         if (sei.payload[0] !== 181) {
41219           return null;
41220         } // itu_t_t35_provider_code should be 49 (ATSC) for captions
41221 
41222 
41223         if ((sei.payload[1] << 8 | sei.payload[2]) !== 49) {
41224           return null;
41225         } // the user_identifier should be "GA94" to indicate ATSC1 data
41226 
41227 
41228         if (String.fromCharCode(sei.payload[3], sei.payload[4], sei.payload[5], sei.payload[6]) !== 'GA94') {
41229           return null;
41230         } // finally, user_data_type_code should be 0x03 for caption data
41231 
41232 
41233         if (sei.payload[7] !== 0x03) {
41234           return null;
41235         } // return the user_data_type_structure and strip the trailing
41236         // marker bits
41237 
41238 
41239         return sei.payload.subarray(8, sei.payload.length - 1);
41240       }; // see CEA-708-D, section 4.4
41241 
41242 
41243       var parseCaptionPackets = function parseCaptionPackets(pts, userData) {
41244         var results = [],
41245             i,
41246             count,
41247             offset,
41248             data; // if this is just filler, return immediately
41249 
41250         if (!(userData[0] & 0x40)) {
41251           return results;
41252         } // parse out the cc_data_1 and cc_data_2 fields
41253 
41254 
41255         count = userData[0] & 0x1f;
41256 
41257         for (i = 0; i < count; i++) {
41258           offset = i * 3;
41259           data = {
41260             type: userData[offset + 2] & 0x03,
41261             pts: pts
41262           }; // capture cc data when cc_valid is 1
41263 
41264           if (userData[offset + 2] & 0x04) {
41265             data.ccData = userData[offset + 3] << 8 | userData[offset + 4];
41266             results.push(data);
41267           }
41268         }
41269 
41270         return results;
41271       };
41272 
41273       var discardEmulationPreventionBytes = function discardEmulationPreventionBytes(data) {
41274         var length = data.byteLength,
41275             emulationPreventionBytesPositions = [],
41276             i = 1,
41277             newLength,
41278             newData; // Find all `Emulation Prevention Bytes`
41279 
41280         while (i < length - 2) {
41281           if (data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 0x03) {
41282             emulationPreventionBytesPositions.push(i + 2);
41283             i += 2;
41284           } else {
41285             i++;
41286           }
41287         } // If no Emulation Prevention Bytes were found just return the original
41288         // array
41289 
41290 
41291         if (emulationPreventionBytesPositions.length === 0) {
41292           return data;
41293         } // Create a new array to hold the NAL unit data
41294 
41295 
41296         newLength = length - emulationPreventionBytesPositions.length;
41297         newData = new Uint8Array(newLength);
41298         var sourceIndex = 0;
41299 
41300         for (i = 0; i < newLength; sourceIndex++, i++) {
41301           if (sourceIndex === emulationPreventionBytesPositions[0]) {
41302             // Skip this byte
41303             sourceIndex++; // Remove this position index
41304 
41305             emulationPreventionBytesPositions.shift();
41306           }
41307 
41308           newData[i] = data[sourceIndex];
41309         }
41310 
41311         return newData;
41312       }; // exports
41313 
41314 
41315       var captionPacketParser = {
41316         parseSei: parseSei,
41317         parseUserData: parseUserData,
41318         parseCaptionPackets: parseCaptionPackets,
41319         discardEmulationPreventionBytes: discardEmulationPreventionBytes,
41320         USER_DATA_REGISTERED_ITU_T_T35: USER_DATA_REGISTERED_ITU_T_T35
41321       }; // -----------------
41322       // Link To Transport
41323       // -----------------
41324 
41325       var CaptionStream = function CaptionStream() {
41326         CaptionStream.prototype.init.call(this);
41327         this.captionPackets_ = [];
41328         this.ccStreams_ = [new Cea608Stream(0, 0), // eslint-disable-line no-use-before-define
41329         new Cea608Stream(0, 1), // eslint-disable-line no-use-before-define
41330         new Cea608Stream(1, 0), // eslint-disable-line no-use-before-define
41331         new Cea608Stream(1, 1) // eslint-disable-line no-use-before-define
41332         ];
41333         this.reset(); // forward data and done events from CCs to this CaptionStream
41334 
41335         this.ccStreams_.forEach(function (cc) {
41336           cc.on('data', this.trigger.bind(this, 'data'));
41337           cc.on('partialdone', this.trigger.bind(this, 'partialdone'));
41338           cc.on('done', this.trigger.bind(this, 'done'));
41339         }, this);
41340       };
41341 
41342       CaptionStream.prototype = new stream();
41343 
41344       CaptionStream.prototype.push = function (event) {
41345         var sei, userData, newCaptionPackets; // only examine SEI NALs
41346 
41347         if (event.nalUnitType !== 'sei_rbsp') {
41348           return;
41349         } // parse the sei
41350 
41351 
41352         sei = captionPacketParser.parseSei(event.escapedRBSP); // ignore everything but user_data_registered_itu_t_t35
41353 
41354         if (sei.payloadType !== captionPacketParser.USER_DATA_REGISTERED_ITU_T_T35) {
41355           return;
41356         } // parse out the user data payload
41357 
41358 
41359         userData = captionPacketParser.parseUserData(sei); // ignore unrecognized userData
41360 
41361         if (!userData) {
41362           return;
41363         } // Sometimes, the same segment # will be downloaded twice. To stop the
41364         // caption data from being processed twice, we track the latest dts we've
41365         // received and ignore everything with a dts before that. However, since
41366         // data for a specific dts can be split across packets on either side of
41367         // a segment boundary, we need to make sure we *don't* ignore the packets
41368         // from the *next* segment that have dts === this.latestDts_. By constantly
41369         // tracking the number of packets received with dts === this.latestDts_, we
41370         // know how many should be ignored once we start receiving duplicates.
41371 
41372 
41373         if (event.dts < this.latestDts_) {
41374           // We've started getting older data, so set the flag.
41375           this.ignoreNextEqualDts_ = true;
41376           return;
41377         } else if (event.dts === this.latestDts_ && this.ignoreNextEqualDts_) {
41378           this.numSameDts_--;
41379 
41380           if (!this.numSameDts_) {
41381             // We've received the last duplicate packet, time to start processing again
41382             this.ignoreNextEqualDts_ = false;
vendor: 6,377 bytes, lines 41383-41569
41383           }
41384 
41385           return;
41386         } // parse out CC data packets and save them for later
41387 
41388 
41389         newCaptionPackets = captionPacketParser.parseCaptionPackets(event.pts, userData);
41390         this.captionPackets_ = this.captionPackets_.concat(newCaptionPackets);
41391 
41392         if (this.latestDts_ !== event.dts) {
41393           this.numSameDts_ = 0;
41394         }
41395 
41396         this.numSameDts_++;
41397         this.latestDts_ = event.dts;
41398       };
41399 
41400       CaptionStream.prototype.flushCCStreams = function (flushType) {
41401         this.ccStreams_.forEach(function (cc) {
41402           return flushType === 'flush' ? cc.flush() : cc.partialFlush();
41403         }, this);
41404       };
41405 
41406       CaptionStream.prototype.flushStream = function (flushType) {
41407         // make sure we actually parsed captions before proceeding
41408         if (!this.captionPackets_.length) {
41409           this.flushCCStreams(flushType);
41410           return;
41411         } // In Chrome, the Array#sort function is not stable so add a
41412         // presortIndex that we can use to ensure we get a stable-sort
41413 
41414 
41415         this.captionPackets_.forEach(function (elem, idx) {
41416           elem.presortIndex = idx;
41417         }); // sort caption byte-pairs based on their PTS values
41418 
41419         this.captionPackets_.sort(function (a, b) {
41420           if (a.pts === b.pts) {
41421             return a.presortIndex - b.presortIndex;
41422           }
41423 
41424           return a.pts - b.pts;
41425         });
41426         this.captionPackets_.forEach(function (packet) {
41427           if (packet.type < 2) {
41428             // Dispatch packet to the right Cea608Stream
41429             this.dispatchCea608Packet(packet);
41430           } // this is where an 'else' would go for a dispatching packets
41431           // to a theoretical Cea708Stream that handles SERVICEn data
41432 
41433         }, this);
41434         this.captionPackets_.length = 0;
41435         this.flushCCStreams(flushType);
41436       };
41437 
41438       CaptionStream.prototype.flush = function () {
41439         return this.flushStream('flush');
41440       }; // Only called if handling partial data
41441 
41442 
41443       CaptionStream.prototype.partialFlush = function () {
41444         return this.flushStream('partialFlush');
41445       };
41446 
41447       CaptionStream.prototype.reset = function () {
41448         this.latestDts_ = null;
41449         this.ignoreNextEqualDts_ = false;
41450         this.numSameDts_ = 0;
41451         this.activeCea608Channel_ = [null, null];
41452         this.ccStreams_.forEach(function (ccStream) {
41453           ccStream.reset();
41454         });
41455       }; // From the CEA-608 spec:
41456 
41457       /*
41458        * When XDS sub-packets are interleaved with other services, the end of each sub-packet shall be followed
41459        * by a control pair to change to a different service. When any of the control codes from 0x10 to 0x1F is
41460        * used to begin a control code pair, it indicates the return to captioning or Text data. The control code pair
41461        * and subsequent data should then be processed according to the FCC rules. It may be necessary for the
41462        * line 21 data encoder to automatically insert a control code pair (i.e. RCL, RU2, RU3, RU4, RDC, or RTD)
41463        * to switch to captioning or Text.
41464       */
41465       // With that in mind, we ignore any data between an XDS control code and a
41466       // subsequent closed-captioning control code.
41467 
41468 
41469       CaptionStream.prototype.dispatchCea608Packet = function (packet) {
41470         // NOTE: packet.type is the CEA608 field
41471         if (this.setsTextOrXDSActive(packet)) {
41472           this.activeCea608Channel_[packet.type] = null;
41473         } else if (this.setsChannel1Active(packet)) {
41474           this.activeCea608Channel_[packet.type] = 0;
41475         } else if (this.setsChannel2Active(packet)) {
41476           this.activeCea608Channel_[packet.type] = 1;
41477         }
41478 
41479         if (this.activeCea608Channel_[packet.type] === null) {
41480           // If we haven't received anything to set the active channel, or the
41481           // packets are Text/XDS data, discard the data; we don't want jumbled
41482           // captions
41483           return;
41484         }
41485 
41486         this.ccStreams_[(packet.type << 1) + this.activeCea608Channel_[packet.type]].push(packet);
41487       };
41488 
41489       CaptionStream.prototype.setsChannel1Active = function (packet) {
41490         return (packet.ccData & 0x7800) === 0x1000;
41491       };
41492 
41493       CaptionStream.prototype.setsChannel2Active = function (packet) {
41494         return (packet.ccData & 0x7800) === 0x1800;
41495       };
41496 
41497       CaptionStream.prototype.setsTextOrXDSActive = function (packet) {
41498         return (packet.ccData & 0x7100) === 0x0100 || (packet.ccData & 0x78fe) === 0x102a || (packet.ccData & 0x78fe) === 0x182a;
41499       }; // ----------------------
41500       // Session to Application
41501       // ----------------------
41502       // This hash maps non-ASCII, special, and extended character codes to their
41503       // proper Unicode equivalent. The first keys that are only a single byte
41504       // are the non-standard ASCII characters, which simply map the CEA608 byte
41505       // to the standard ASCII/Unicode. The two-byte keys that follow are the CEA608
41506       // character codes, but have their MSB bitmasked with 0x03 so that a lookup
41507       // can be performed regardless of the field and data channel on which the
41508       // character code was received.
41509 
41510 
41511       var CHARACTER_TRANSLATION = {
41512         0x2a: 0xe1,
41513         // á
41514         0x5c: 0xe9,
41515         // é
41516         0x5e: 0xed,
41517         // í
41518         0x5f: 0xf3,
41519         // ó
41520         0x60: 0xfa,
41521         // ú
41522         0x7b: 0xe7,
41523         // ç
41524         0x7c: 0xf7,
41525         // ÷
41526         0x7d: 0xd1,
41527         // Ñ
41528         0x7e: 0xf1,
41529         // ñ
41530         0x7f: 0x2588,
41531         // █
41532         0x0130: 0xae,
41533         // ®
41534         0x0131: 0xb0,
41535         // °
41536         0x0132: 0xbd,
41537         // ½
41538         0x0133: 0xbf,
41539         // ¿
41540         0x0134: 0x2122,
41541         // ™
41542         0x0135: 0xa2,
41543         // ¢
41544         0x0136: 0xa3,
41545         // £
41546         0x0137: 0x266a,
41547         // ♪
41548         0x0138: 0xe0,
41549         // à
41550         0x0139: 0xa0,
41551         //
41552         0x013a: 0xe8,
41553         // è
41554         0x013b: 0xe2,
41555         // â
41556         0x013c: 0xea,
41557         // ê
41558         0x013d: 0xee,
41559         // î
41560         0x013e: 0xf4,
41561         // ô
41562         0x013f: 0xfb,
41563         // û
41564         0x0220: 0xc1,
41565         // Á
41566         0x0221: 0xc9,
41567         // É
41568         0x0222: 0xd3,
41569         // Ó
vendor: 21,954 bytes, lines 41570-42162
41570         0x0223: 0xda,
41571         // Ú
41572         0x0224: 0xdc,
41573         // Ü
41574         0x0225: 0xfc,
41575         // ü
41576         0x0226: 0x2018,
41577         // ‘
41578         0x0227: 0xa1,
41579         // ¡
41580         0x0228: 0x2a,
41581         // *
41582         0x0229: 0x27,
41583         // '
41584         0x022a: 0x2014,
41585         // —
41586         0x022b: 0xa9,
41587         // ©
41588         0x022c: 0x2120,
41589         // ℠
41590         0x022d: 0x2022,
41591         // •
41592         0x022e: 0x201c,
41593         // “
41594         0x022f: 0x201d,
41595         // ”
41596         0x0230: 0xc0,
41597         // À
41598         0x0231: 0xc2,
41599         // Â
41600         0x0232: 0xc7,
41601         // Ç
41602         0x0233: 0xc8,
41603         // È
41604         0x0234: 0xca,
41605         // Ê
41606         0x0235: 0xcb,
41607         // Ë
41608         0x0236: 0xeb,
41609         // ë
41610         0x0237: 0xce,
41611         // Î
41612         0x0238: 0xcf,
41613         // Ï
41614         0x0239: 0xef,
41615         // ï
41616         0x023a: 0xd4,
41617         // Ô
41618         0x023b: 0xd9,
41619         // Ù
41620         0x023c: 0xf9,
41621         // ù
41622         0x023d: 0xdb,
41623         // Û
41624         0x023e: 0xab,
41625         // «
41626         0x023f: 0xbb,
41627         // »
41628         0x0320: 0xc3,
41629         // Ã
41630         0x0321: 0xe3,
41631         // ã
41632         0x0322: 0xcd,
41633         // Í
41634         0x0323: 0xcc,
41635         // Ì
41636         0x0324: 0xec,
41637         // ì
41638         0x0325: 0xd2,
41639         // Ò
41640         0x0326: 0xf2,
41641         // ò
41642         0x0327: 0xd5,
41643         // Õ
41644         0x0328: 0xf5,
41645         // õ
41646         0x0329: 0x7b,
41647         // {
41648         0x032a: 0x7d,
41649         // }
41650         0x032b: 0x5c,
41651         // \
41652         0x032c: 0x5e,
41653         // ^
41654         0x032d: 0x5f,
41655         // _
41656         0x032e: 0x7c,
41657         // |
41658         0x032f: 0x7e,
41659         // ~
41660         0x0330: 0xc4,
41661         // Ä
41662         0x0331: 0xe4,
41663         // ä
41664         0x0332: 0xd6,
41665         // Ö
41666         0x0333: 0xf6,
41667         // ö
41668         0x0334: 0xdf,
41669         // ß
41670         0x0335: 0xa5,
41671         // ¥
41672         0x0336: 0xa4,
41673         // ¤
41674         0x0337: 0x2502,
41675         // │
41676         0x0338: 0xc5,
41677         // Å
41678         0x0339: 0xe5,
41679         // å
41680         0x033a: 0xd8,
41681         // Ø
41682         0x033b: 0xf8,
41683         // ø
41684         0x033c: 0x250c,
41685         // ┌
41686         0x033d: 0x2510,
41687         // ┐
41688         0x033e: 0x2514,
41689         // └
41690         0x033f: 0x2518 // ┘
41691 
41692       };
41693 
41694       var getCharFromCode = function getCharFromCode(code) {
41695         if (code === null) {
41696           return '';
41697         }
41698 
41699         code = CHARACTER_TRANSLATION[code] || code;
41700         return String.fromCharCode(code);
41701       }; // the index of the last row in a CEA-608 display buffer
41702 
41703 
41704       var BOTTOM_ROW = 14; // This array is used for mapping PACs -> row #, since there's no way of
41705       // getting it through bit logic.
41706 
41707       var ROWS = [0x1100, 0x1120, 0x1200, 0x1220, 0x1500, 0x1520, 0x1600, 0x1620, 0x1700, 0x1720, 0x1000, 0x1300, 0x1320, 0x1400, 0x1420]; // CEA-608 captions are rendered onto a 34x15 matrix of character
41708       // cells. The "bottom" row is the last element in the outer array.
41709 
41710       var createDisplayBuffer = function createDisplayBuffer() {
41711         var result = [],
41712             i = BOTTOM_ROW + 1;
41713 
41714         while (i--) {
41715           result.push('');
41716         }
41717 
41718         return result;
41719       };
41720 
41721       var Cea608Stream = function Cea608Stream(field, dataChannel) {
41722         Cea608Stream.prototype.init.call(this);
41723         this.field_ = field || 0;
41724         this.dataChannel_ = dataChannel || 0;
41725         this.name_ = 'CC' + ((this.field_ << 1 | this.dataChannel_) + 1);
41726         this.setConstants();
41727         this.reset();
41728 
41729         this.push = function (packet) {
41730           var data, swap, char0, char1, text; // remove the parity bits
41731 
41732           data = packet.ccData & 0x7f7f; // ignore duplicate control codes; the spec demands they're sent twice
41733 
41734           if (data === this.lastControlCode_) {
41735             this.lastControlCode_ = null;
41736             return;
41737           } // Store control codes
41738 
41739 
41740           if ((data & 0xf000) === 0x1000) {
41741             this.lastControlCode_ = data;
41742           } else if (data !== this.PADDING_) {
41743             this.lastControlCode_ = null;
41744           }
41745 
41746           char0 = data >>> 8;
41747           char1 = data & 0xff;
41748 
41749           if (data === this.PADDING_) {
41750             return;
41751           } else if (data === this.RESUME_CAPTION_LOADING_) {
41752             this.mode_ = 'popOn';
41753           } else if (data === this.END_OF_CAPTION_) {
41754             // If an EOC is received while in paint-on mode, the displayed caption
41755             // text should be swapped to non-displayed memory as if it was a pop-on
41756             // caption. Because of that, we should explicitly switch back to pop-on
41757             // mode
41758             this.mode_ = 'popOn';
41759             this.clearFormatting(packet.pts); // if a caption was being displayed, it's gone now
41760 
41761             this.flushDisplayed(packet.pts); // flip memory
41762 
41763             swap = this.displayed_;
41764             this.displayed_ = this.nonDisplayed_;
41765             this.nonDisplayed_ = swap; // start measuring the time to display the caption
41766 
41767             this.startPts_ = packet.pts;
41768           } else if (data === this.ROLL_UP_2_ROWS_) {
41769             this.rollUpRows_ = 2;
41770             this.setRollUp(packet.pts);
41771           } else if (data === this.ROLL_UP_3_ROWS_) {
41772             this.rollUpRows_ = 3;
41773             this.setRollUp(packet.pts);
41774           } else if (data === this.ROLL_UP_4_ROWS_) {
41775             this.rollUpRows_ = 4;
41776             this.setRollUp(packet.pts);
41777           } else if (data === this.CARRIAGE_RETURN_) {
41778             this.clearFormatting(packet.pts);
41779             this.flushDisplayed(packet.pts);
41780             this.shiftRowsUp_();
41781             this.startPts_ = packet.pts;
41782           } else if (data === this.BACKSPACE_) {
41783             if (this.mode_ === 'popOn') {
41784               this.nonDisplayed_[this.row_] = this.nonDisplayed_[this.row_].slice(0, -1);
41785             } else {
41786               this.displayed_[this.row_] = this.displayed_[this.row_].slice(0, -1);
41787             }
41788           } else if (data === this.ERASE_DISPLAYED_MEMORY_) {
41789             this.flushDisplayed(packet.pts);
41790             this.displayed_ = createDisplayBuffer();
41791           } else if (data === this.ERASE_NON_DISPLAYED_MEMORY_) {
41792             this.nonDisplayed_ = createDisplayBuffer();
41793           } else if (data === this.RESUME_DIRECT_CAPTIONING_) {
41794             if (this.mode_ !== 'paintOn') {
41795               // NOTE: This should be removed when proper caption positioning is
41796               // implemented
41797               this.flushDisplayed(packet.pts);
41798               this.displayed_ = createDisplayBuffer();
41799             }
41800 
41801             this.mode_ = 'paintOn';
41802             this.startPts_ = packet.pts; // Append special characters to caption text
41803           } else if (this.isSpecialCharacter(char0, char1)) {
41804             // Bitmask char0 so that we can apply character transformations
41805             // regardless of field and data channel.
41806             // Then byte-shift to the left and OR with char1 so we can pass the
41807             // entire character code to `getCharFromCode`.
41808             char0 = (char0 & 0x03) << 8;
41809             text = getCharFromCode(char0 | char1);
41810             this[this.mode_](packet.pts, text);
41811             this.column_++; // Append extended characters to caption text
41812           } else if (this.isExtCharacter(char0, char1)) {
41813             // Extended characters always follow their "non-extended" equivalents.
41814             // IE if a "è" is desired, you'll always receive "eè"; non-compliant
41815             // decoders are supposed to drop the "è", while compliant decoders
41816             // backspace the "e" and insert "è".
41817             // Delete the previous character
41818             if (this.mode_ === 'popOn') {
41819               this.nonDisplayed_[this.row_] = this.nonDisplayed_[this.row_].slice(0, -1);
41820             } else {
41821               this.displayed_[this.row_] = this.displayed_[this.row_].slice(0, -1);
41822             } // Bitmask char0 so that we can apply character transformations
41823             // regardless of field and data channel.
41824             // Then byte-shift to the left and OR with char1 so we can pass the
41825             // entire character code to `getCharFromCode`.
41826 
41827 
41828             char0 = (char0 & 0x03) << 8;
41829             text = getCharFromCode(char0 | char1);
41830             this[this.mode_](packet.pts, text);
41831             this.column_++; // Process mid-row codes
41832           } else if (this.isMidRowCode(char0, char1)) {
41833             // Attributes are not additive, so clear all formatting
41834             this.clearFormatting(packet.pts); // According to the standard, mid-row codes
41835             // should be replaced with spaces, so add one now
41836 
41837             this[this.mode_](packet.pts, ' ');
41838             this.column_++;
41839 
41840             if ((char1 & 0xe) === 0xe) {
41841               this.addFormatting(packet.pts, ['i']);
41842             }
41843 
41844             if ((char1 & 0x1) === 0x1) {
41845               this.addFormatting(packet.pts, ['u']);
41846             } // Detect offset control codes and adjust cursor
41847 
41848           } else if (this.isOffsetControlCode(char0, char1)) {
41849             // Cursor position is set by indent PAC (see below) in 4-column
41850             // increments, with an additional offset code of 1-3 to reach any
41851             // of the 32 columns specified by CEA-608. So all we need to do
41852             // here is increment the column cursor by the given offset.
41853             this.column_ += char1 & 0x03; // Detect PACs (Preamble Address Codes)
41854           } else if (this.isPAC(char0, char1)) {
41855             // There's no logic for PAC -> row mapping, so we have to just
41856             // find the row code in an array and use its index :(
41857             var row = ROWS.indexOf(data & 0x1f20); // Configure the caption window if we're in roll-up mode
41858 
41859             if (this.mode_ === 'rollUp') {
41860               // This implies that the base row is incorrectly set.
41861               // As per the recommendation in CEA-608(Base Row Implementation), defer to the number
41862               // of roll-up rows set.
41863               if (row - this.rollUpRows_ + 1 < 0) {
41864                 row = this.rollUpRows_ - 1;
41865               }
41866 
41867               this.setRollUp(packet.pts, row);
41868             }
41869 
41870             if (row !== this.row_) {
41871               // formatting is only persistent for current row
41872               this.clearFormatting(packet.pts);
41873               this.row_ = row;
41874             } // All PACs can apply underline, so detect and apply
41875             // (All odd-numbered second bytes set underline)
41876 
41877 
41878             if (char1 & 0x1 && this.formatting_.indexOf('u') === -1) {
41879               this.addFormatting(packet.pts, ['u']);
41880             }
41881 
41882             if ((data & 0x10) === 0x10) {
41883               // We've got an indent level code. Each successive even number
41884               // increments the column cursor by 4, so we can get the desired
41885               // column position by bit-shifting to the right (to get n/2)
41886               // and multiplying by 4.
41887               this.column_ = ((data & 0xe) >> 1) * 4;
41888             }
41889 
41890             if (this.isColorPAC(char1)) {
41891               // it's a color code, though we only support white, which
41892               // can be either normal or italicized. white italics can be
41893               // either 0x4e or 0x6e depending on the row, so we just
41894               // bitwise-and with 0xe to see if italics should be turned on
41895               if ((char1 & 0xe) === 0xe) {
41896                 this.addFormatting(packet.pts, ['i']);
41897               }
41898             } // We have a normal character in char0, and possibly one in char1
41899 
41900           } else if (this.isNormalChar(char0)) {
41901             if (char1 === 0x00) {
41902               char1 = null;
41903             }
41904 
41905             text = getCharFromCode(char0);
41906             text += getCharFromCode(char1);
41907             this[this.mode_](packet.pts, text);
41908             this.column_ += text.length;
41909           } // finish data processing
41910 
41911         };
41912       };
41913 
41914       Cea608Stream.prototype = new stream(); // Trigger a cue point that captures the current state of the
41915       // display buffer
41916 
41917       Cea608Stream.prototype.flushDisplayed = function (pts) {
41918         var content = this.displayed_ // remove spaces from the start and end of the string
41919         .map(function (row) {
41920           try {
41921             return row.trim();
41922           } catch (e) {
41923             // Ordinarily, this shouldn't happen. However, caption
41924             // parsing errors should not throw exceptions and
41925             // break playback.
41926             // eslint-disable-next-line no-console
41927             console.error('Skipping malformed caption.');
41928             return '';
41929           }
41930         }) // combine all text rows to display in one cue
41931         .join('\n') // and remove blank rows from the start and end, but not the middle
41932         .replace(/^\n+|\n+$/g, '');
41933 
41934         if (content.length) {
41935           this.trigger('data', {
41936             startPts: this.startPts_,
41937             endPts: pts,
41938             text: content,
41939             stream: this.name_
41940           });
41941         }
41942       };
41943       /**
41944        * Zero out the data, used for startup and on seek
41945        */
41946 
41947 
41948       Cea608Stream.prototype.reset = function () {
41949         this.mode_ = 'popOn'; // When in roll-up mode, the index of the last row that will
41950         // actually display captions. If a caption is shifted to a row
41951         // with a lower index than this, it is cleared from the display
41952         // buffer
41953 
41954         this.topRow_ = 0;
41955         this.startPts_ = 0;
41956         this.displayed_ = createDisplayBuffer();
41957         this.nonDisplayed_ = createDisplayBuffer();
41958         this.lastControlCode_ = null; // Track row and column for proper line-breaking and spacing
41959 
41960         this.column_ = 0;
41961         this.row_ = BOTTOM_ROW;
41962         this.rollUpRows_ = 2; // This variable holds currently-applied formatting
41963 
41964         this.formatting_ = [];
41965       };
41966       /**
41967        * Sets up control code and related constants for this instance
41968        */
41969 
41970 
41971       Cea608Stream.prototype.setConstants = function () {
41972         // The following attributes have these uses:
41973         // ext_ :    char0 for mid-row codes, and the base for extended
41974         //           chars (ext_+0, ext_+1, and ext_+2 are char0s for
41975         //           extended codes)
41976         // control_: char0 for control codes, except byte-shifted to the
41977         //           left so that we can do this.control_ | CONTROL_CODE
41978         // offset_:  char0 for tab offset codes
41979         //
41980         // It's also worth noting that control codes, and _only_ control codes,
41981         // differ between field 1 and field2. Field 2 control codes are always
41982         // their field 1 value plus 1. That's why there's the "| field" on the
41983         // control value.
41984         if (this.dataChannel_ === 0) {
41985           this.BASE_ = 0x10;
41986           this.EXT_ = 0x11;
41987           this.CONTROL_ = (0x14 | this.field_) << 8;
41988           this.OFFSET_ = 0x17;
41989         } else if (this.dataChannel_ === 1) {
41990           this.BASE_ = 0x18;
41991           this.EXT_ = 0x19;
41992           this.CONTROL_ = (0x1c | this.field_) << 8;
41993           this.OFFSET_ = 0x1f;
41994         } // Constants for the LSByte command codes recognized by Cea608Stream. This
41995         // list is not exhaustive. For a more comprehensive listing and semantics see
41996         // http://www.gpo.gov/fdsys/pkg/CFR-2010-title47-vol1/pdf/CFR-2010-title47-vol1-sec15-119.pdf
41997         // Padding
41998 
41999 
42000         this.PADDING_ = 0x0000; // Pop-on Mode
42001 
42002         this.RESUME_CAPTION_LOADING_ = this.CONTROL_ | 0x20;
42003         this.END_OF_CAPTION_ = this.CONTROL_ | 0x2f; // Roll-up Mode
42004 
42005         this.ROLL_UP_2_ROWS_ = this.CONTROL_ | 0x25;
42006         this.ROLL_UP_3_ROWS_ = this.CONTROL_ | 0x26;
42007         this.ROLL_UP_4_ROWS_ = this.CONTROL_ | 0x27;
42008         this.CARRIAGE_RETURN_ = this.CONTROL_ | 0x2d; // paint-on mode
42009 
42010         this.RESUME_DIRECT_CAPTIONING_ = this.CONTROL_ | 0x29; // Erasure
42011 
42012         this.BACKSPACE_ = this.CONTROL_ | 0x21;
42013         this.ERASE_DISPLAYED_MEMORY_ = this.CONTROL_ | 0x2c;
42014         this.ERASE_NON_DISPLAYED_MEMORY_ = this.CONTROL_ | 0x2e;
42015       };
42016       /**
42017        * Detects if the 2-byte packet data is a special character
42018        *
42019        * Special characters have a second byte in the range 0x30 to 0x3f,
42020        * with the first byte being 0x11 (for data channel 1) or 0x19 (for
42021        * data channel 2).
42022        *
42023        * @param  {Integer} char0 The first byte
42024        * @param  {Integer} char1 The second byte
42025        * @return {Boolean}       Whether the 2 bytes are an special character
42026        */
42027 
42028 
42029       Cea608Stream.prototype.isSpecialCharacter = function (char0, char1) {
42030         return char0 === this.EXT_ && char1 >= 0x30 && char1 <= 0x3f;
42031       };
42032       /**
42033        * Detects if the 2-byte packet data is an extended character
42034        *
42035        * Extended characters have a second byte in the range 0x20 to 0x3f,
42036        * with the first byte being 0x12 or 0x13 (for data channel 1) or
42037        * 0x1a or 0x1b (for data channel 2).
42038        *
42039        * @param  {Integer} char0 The first byte
42040        * @param  {Integer} char1 The second byte
42041        * @return {Boolean}       Whether the 2 bytes are an extended character
42042        */
42043 
42044 
42045       Cea608Stream.prototype.isExtCharacter = function (char0, char1) {
42046         return (char0 === this.EXT_ + 1 || char0 === this.EXT_ + 2) && char1 >= 0x20 && char1 <= 0x3f;
42047       };
42048       /**
42049        * Detects if the 2-byte packet is a mid-row code
42050        *
42051        * Mid-row codes have a second byte in the range 0x20 to 0x2f, with
42052        * the first byte being 0x11 (for data channel 1) or 0x19 (for data
42053        * channel 2).
42054        *
42055        * @param  {Integer} char0 The first byte
42056        * @param  {Integer} char1 The second byte
42057        * @return {Boolean}       Whether the 2 bytes are a mid-row code
42058        */
42059 
42060 
42061       Cea608Stream.prototype.isMidRowCode = function (char0, char1) {
42062         return char0 === this.EXT_ && char1 >= 0x20 && char1 <= 0x2f;
42063       };
42064       /**
42065        * Detects if the 2-byte packet is an offset control code
42066        *
42067        * Offset control codes have a second byte in the range 0x21 to 0x23,
42068        * with the first byte being 0x17 (for data channel 1) or 0x1f (for
42069        * data channel 2).
42070        *
42071        * @param  {Integer} char0 The first byte
42072        * @param  {Integer} char1 The second byte
42073        * @return {Boolean}       Whether the 2 bytes are an offset control code
42074        */
42075 
42076 
42077       Cea608Stream.prototype.isOffsetControlCode = function (char0, char1) {
42078         return char0 === this.OFFSET_ && char1 >= 0x21 && char1 <= 0x23;
42079       };
42080       /**
42081        * Detects if the 2-byte packet is a Preamble Address Code
42082        *
42083        * PACs have a first byte in the range 0x10 to 0x17 (for data channel 1)
42084        * or 0x18 to 0x1f (for data channel 2), with the second byte in the
42085        * range 0x40 to 0x7f.
42086        *
42087        * @param  {Integer} char0 The first byte
42088        * @param  {Integer} char1 The second byte
42089        * @return {Boolean}       Whether the 2 bytes are a PAC
42090        */
42091 
42092 
42093       Cea608Stream.prototype.isPAC = function (char0, char1) {
42094         return char0 >= this.BASE_ && char0 < this.BASE_ + 8 && char1 >= 0x40 && char1 <= 0x7f;
42095       };
42096       /**
42097        * Detects if a packet's second byte is in the range of a PAC color code
42098        *
42099        * PAC color codes have the second byte be in the range 0x40 to 0x4f, or
42100        * 0x60 to 0x6f.
42101        *
42102        * @param  {Integer} char1 The second byte
42103        * @return {Boolean}       Whether the byte is a color PAC
42104        */
42105 
42106 
42107       Cea608Stream.prototype.isColorPAC = function (char1) {
42108         return char1 >= 0x40 && char1 <= 0x4f || char1 >= 0x60 && char1 <= 0x7f;
42109       };
42110       /**
42111        * Detects if a single byte is in the range of a normal character
42112        *
42113        * Normal text bytes are in the range 0x20 to 0x7f.
42114        *
42115        * @param  {Integer} char  The byte
42116        * @return {Boolean}       Whether the byte is a normal character
42117        */
42118 
42119 
42120       Cea608Stream.prototype.isNormalChar = function (_char) {
42121         return _char >= 0x20 && _char <= 0x7f;
42122       };
42123       /**
42124        * Configures roll-up
42125        *
42126        * @param  {Integer} pts         Current PTS
42127        * @param  {Integer} newBaseRow  Used by PACs to slide the current window to
42128        *                               a new position
42129        */
42130 
42131 
42132       Cea608Stream.prototype.setRollUp = function (pts, newBaseRow) {
42133         // Reset the base row to the bottom row when switching modes
42134         if (this.mode_ !== 'rollUp') {
42135           this.row_ = BOTTOM_ROW;
42136           this.mode_ = 'rollUp'; // Spec says to wipe memories when switching to roll-up
42137 
42138           this.flushDisplayed(pts);
42139           this.nonDisplayed_ = createDisplayBuffer();
42140           this.displayed_ = createDisplayBuffer();
42141         }
42142 
42143         if (newBaseRow !== undefined && newBaseRow !== this.row_) {
42144           // move currently displayed captions (up or down) to the new base row
42145           for (var i = 0; i < this.rollUpRows_; i++) {
42146             this.displayed_[newBaseRow - i] = this.displayed_[this.row_ - i];
42147             this.displayed_[this.row_ - i] = '';
42148           }
42149         }
42150 
42151         if (newBaseRow === undefined) {
42152           newBaseRow = this.row_;
42153         }
42154 
42155         this.topRow_ = newBaseRow - this.rollUpRows_ + 1;
42156       }; // Adds the opening HTML tag for the passed character to the caption text,
42157       // and keeps track of it for later closing
42158 
42159 
42160       Cea608Stream.prototype.addFormatting = function (pts, format) {
42161         this.formatting_ = this.formatting_.concat(format);
42162         var text = format.reduce(function (text, format) {
vendor: 13,205 bytes, lines 42163-42525
42163           return text + '<' + format + '>';
42164         }, '');
42165         this[this.mode_](pts, text);
42166       }; // Adds HTML closing tags for current formatting to caption text and
42167       // clears remembered formatting
42168 
42169 
42170       Cea608Stream.prototype.clearFormatting = function (pts) {
42171         if (!this.formatting_.length) {
42172           return;
42173         }
42174 
42175         var text = this.formatting_.reverse().reduce(function (text, format) {
42176           return text + '</' + format + '>';
42177         }, '');
42178         this.formatting_ = [];
42179         this[this.mode_](pts, text);
42180       }; // Mode Implementations
42181 
42182 
42183       Cea608Stream.prototype.popOn = function (pts, text) {
42184         var baseRow = this.nonDisplayed_[this.row_]; // buffer characters
42185 
42186         baseRow += text;
42187         this.nonDisplayed_[this.row_] = baseRow;
42188       };
42189 
42190       Cea608Stream.prototype.rollUp = function (pts, text) {
42191         var baseRow = this.displayed_[this.row_];
42192         baseRow += text;
42193         this.displayed_[this.row_] = baseRow;
42194       };
42195 
42196       Cea608Stream.prototype.shiftRowsUp_ = function () {
42197         var i; // clear out inactive rows
42198 
42199         for (i = 0; i < this.topRow_; i++) {
42200           this.displayed_[i] = '';
42201         }
42202 
42203         for (i = this.row_ + 1; i < BOTTOM_ROW + 1; i++) {
42204           this.displayed_[i] = '';
42205         } // shift displayed rows up
42206 
42207 
42208         for (i = this.topRow_; i < this.row_; i++) {
42209           this.displayed_[i] = this.displayed_[i + 1];
42210         } // clear out the bottom row
42211 
42212 
42213         this.displayed_[this.row_] = '';
42214       };
42215 
42216       Cea608Stream.prototype.paintOn = function (pts, text) {
42217         var baseRow = this.displayed_[this.row_];
42218         baseRow += text;
42219         this.displayed_[this.row_] = baseRow;
42220       }; // exports
42221 
42222 
42223       var captionStream = {
42224         CaptionStream: CaptionStream,
42225         Cea608Stream: Cea608Stream
42226       };
42227       /**
42228        * mux.js
42229        *
42230        * Copyright (c) Brightcove
42231        * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
42232        */
42233 
42234       var streamTypes = {
42235         H264_STREAM_TYPE: 0x1B,
42236         ADTS_STREAM_TYPE: 0x0F,
42237         METADATA_STREAM_TYPE: 0x15
42238       };
42239       var MAX_TS = 8589934592;
42240       var RO_THRESH = 4294967296;
42241       var TYPE_SHARED = 'shared';
42242 
42243       var handleRollover = function handleRollover(value, reference) {
42244         var direction = 1;
42245 
42246         if (value > reference) {
42247           // If the current timestamp value is greater than our reference timestamp and we detect a
42248           // timestamp rollover, this means the roll over is happening in the opposite direction.
42249           // Example scenario: Enter a long stream/video just after a rollover occurred. The reference
42250           // point will be set to a small number, e.g. 1. The user then seeks backwards over the
42251           // rollover point. In loading this segment, the timestamp values will be very large,
42252           // e.g. 2^33 - 1. Since this comes before the data we loaded previously, we want to adjust
42253           // the time stamp to be `value - 2^33`.
42254           direction = -1;
42255         } // Note: A seek forwards or back that is greater than the RO_THRESH (2^32, ~13 hours) will
42256         // cause an incorrect adjustment.
42257 
42258 
42259         while (Math.abs(reference - value) > RO_THRESH) {
42260           value += direction * MAX_TS;
42261         }
42262 
42263         return value;
42264       };
42265 
42266       var TimestampRolloverStream = function TimestampRolloverStream(type) {
42267         var lastDTS, referenceDTS;
42268         TimestampRolloverStream.prototype.init.call(this); // The "shared" type is used in cases where a stream will contain muxed
42269         // video and audio. We could use `undefined` here, but having a string
42270         // makes debugging a little clearer.
42271 
42272         this.type_ = type || TYPE_SHARED;
42273 
42274         this.push = function (data) {
42275           // Any "shared" rollover streams will accept _all_ data. Otherwise,
42276           // streams will only accept data that matches their type.
42277           if (this.type_ !== TYPE_SHARED && data.type !== this.type_) {
42278             return;
42279           }
42280 
42281           if (referenceDTS === undefined) {
42282             referenceDTS = data.dts;
42283           }
42284 
42285           data.dts = handleRollover(data.dts, referenceDTS);
42286           data.pts = handleRollover(data.pts, referenceDTS);
42287           lastDTS = data.dts;
42288           this.trigger('data', data);
42289         };
42290 
42291         this.flush = function () {
42292           referenceDTS = lastDTS;
42293           this.trigger('done');
42294         };
42295 
42296         this.endTimeline = function () {
42297           this.flush();
42298           this.trigger('endedtimeline');
42299         };
42300 
42301         this.discontinuity = function () {
42302           referenceDTS = void 0;
42303           lastDTS = void 0;
42304         };
42305 
42306         this.reset = function () {
42307           this.discontinuity();
42308           this.trigger('reset');
42309         };
42310       };
42311 
42312       TimestampRolloverStream.prototype = new stream();
42313       var timestampRolloverStream = {
42314         TimestampRolloverStream: TimestampRolloverStream,
42315         handleRollover: handleRollover
42316       };
42317 
42318       var percentEncode = function percentEncode(bytes, start, end) {
42319         var i,
42320             result = '';
42321 
42322         for (i = start; i < end; i++) {
42323           result += '%' + ('00' + bytes[i].toString(16)).slice(-2);
42324         }
42325 
42326         return result;
42327       },
42328           // return the string representation of the specified byte range,
42329       // interpreted as UTf-8.
42330       parseUtf8 = function parseUtf8(bytes, start, end) {
42331         return decodeURIComponent(percentEncode(bytes, start, end));
42332       },
42333           // return the string representation of the specified byte range,
42334       // interpreted as ISO-8859-1.
42335       parseIso88591 = function parseIso88591(bytes, start, end) {
42336         return unescape(percentEncode(bytes, start, end)); // jshint ignore:line
42337       },
42338           parseSyncSafeInteger = function parseSyncSafeInteger(data) {
42339         return data[0] << 21 | data[1] << 14 | data[2] << 7 | data[3];
42340       },
42341           tagParsers = {
42342         TXXX: function TXXX(tag) {
42343           var i;
42344 
42345           if (tag.data[0] !== 3) {
42346             // ignore frames with unrecognized character encodings
42347             return;
42348           }
42349 
42350           for (i = 1; i < tag.data.length; i++) {
42351             if (tag.data[i] === 0) {
42352               // parse the text fields
42353               tag.description = parseUtf8(tag.data, 1, i); // do not include the null terminator in the tag value
42354 
42355               tag.value = parseUtf8(tag.data, i + 1, tag.data.length).replace(/\0*$/, '');
42356               break;
42357             }
42358           }
42359 
42360           tag.data = tag.value;
42361         },
42362         WXXX: function WXXX(tag) {
42363           var i;
42364 
42365           if (tag.data[0] !== 3) {
42366             // ignore frames with unrecognized character encodings
42367             return;
42368           }
42369 
42370           for (i = 1; i < tag.data.length; i++) {
42371             if (tag.data[i] === 0) {
42372               // parse the description and URL fields
42373               tag.description = parseUtf8(tag.data, 1, i);
42374               tag.url = parseUtf8(tag.data, i + 1, tag.data.length);
42375               break;
42376             }
42377           }
42378         },
42379         PRIV: function PRIV(tag) {
42380           var i;
42381 
42382           for (i = 0; i < tag.data.length; i++) {
42383             if (tag.data[i] === 0) {
42384               // parse the description and URL fields
42385               tag.owner = parseIso88591(tag.data, 0, i);
42386               break;
42387             }
42388           }
42389 
42390           tag.privateData = tag.data.subarray(i + 1);
42391           tag.data = tag.privateData;
42392         }
42393       },
42394           _MetadataStream;
42395 
42396       _MetadataStream = function MetadataStream(options) {
42397         var settings = {
42398           debug: !!(options && options.debug),
42399           // the bytes of the program-level descriptor field in MP2T
42400           // see ISO/IEC 13818-1:2013 (E), section 2.6 "Program and
42401           // program element descriptors"
42402           descriptor: options && options.descriptor
42403         },
42404             // the total size in bytes of the ID3 tag being parsed
42405         tagSize = 0,
42406             // tag data that is not complete enough to be parsed
42407         buffer = [],
42408             // the total number of bytes currently in the buffer
42409         bufferSize = 0,
42410             i;
42411 
42412         _MetadataStream.prototype.init.call(this); // calculate the text track in-band metadata track dispatch type
42413         // https://html.spec.whatwg.org/multipage/embedded-content.html#steps-to-expose-a-media-resource-specific-text-track
42414 
42415 
42416         this.dispatchType = streamTypes.METADATA_STREAM_TYPE.toString(16);
42417 
42418         if (settings.descriptor) {
42419           for (i = 0; i < settings.descriptor.length; i++) {
42420             this.dispatchType += ('00' + settings.descriptor[i].toString(16)).slice(-2);
42421           }
42422         }
42423 
42424         this.push = function (chunk) {
42425           var tag, frameStart, frameSize, frame, i, frameHeader;
42426 
42427           if (chunk.type !== 'timed-metadata') {
42428             return;
42429           } // if data_alignment_indicator is set in the PES header,
42430           // we must have the start of a new ID3 tag. Assume anything
42431           // remaining in the buffer was malformed and throw it out
42432 
42433 
42434           if (chunk.dataAlignmentIndicator) {
42435             bufferSize = 0;
42436             buffer.length = 0;
42437           } // ignore events that don't look like ID3 data
42438 
42439 
42440           if (buffer.length === 0 && (chunk.data.length < 10 || chunk.data[0] !== 'I'.charCodeAt(0) || chunk.data[1] !== 'D'.charCodeAt(0) || chunk.data[2] !== '3'.charCodeAt(0))) {
42441             if (settings.debug) {
42442               // eslint-disable-next-line no-console
42443               console.log('Skipping unrecognized metadata packet');
42444             }
42445 
42446             return;
42447           } // add this chunk to the data we've collected so far
42448 
42449 
42450           buffer.push(chunk);
42451           bufferSize += chunk.data.byteLength; // grab the size of the entire frame from the ID3 header
42452 
42453           if (buffer.length === 1) {
42454             // the frame size is transmitted as a 28-bit integer in the
42455             // last four bytes of the ID3 header.
42456             // The most significant bit of each byte is dropped and the
42457             // results concatenated to recover the actual value.
42458             tagSize = parseSyncSafeInteger(chunk.data.subarray(6, 10)); // ID3 reports the tag size excluding the header but it's more
42459             // convenient for our comparisons to include it
42460 
42461             tagSize += 10;
42462           } // if the entire frame has not arrived, wait for more data
42463 
42464 
42465           if (bufferSize < tagSize) {
42466             return;
42467           } // collect the entire frame so it can be parsed
42468 
42469 
42470           tag = {
42471             data: new Uint8Array(tagSize),
42472             frames: [],
42473             pts: buffer[0].pts,
42474             dts: buffer[0].dts
42475           };
42476 
42477           for (i = 0; i < tagSize;) {
42478             tag.data.set(buffer[0].data.subarray(0, tagSize - i), i);
42479             i += buffer[0].data.byteLength;
42480             bufferSize -= buffer[0].data.byteLength;
42481             buffer.shift();
42482           } // find the start of the first frame and the end of the tag
42483 
42484 
42485           frameStart = 10;
42486 
42487           if (tag.data[5] & 0x40) {
42488             // advance the frame start past the extended header
42489             frameStart += 4; // header size field
42490 
42491             frameStart += parseSyncSafeInteger(tag.data.subarray(10, 14)); // clip any padding off the end
42492 
42493             tagSize -= parseSyncSafeInteger(tag.data.subarray(16, 20));
42494           } // parse one or more ID3 frames
42495           // http://id3.org/id3v2.3.0#ID3v2_frame_overview
42496 
42497 
42498           do {
42499             // determine the number of bytes in this frame
42500             frameSize = parseSyncSafeInteger(tag.data.subarray(frameStart + 4, frameStart + 8));
42501 
42502             if (frameSize < 1) {
42503               // eslint-disable-next-line no-console
42504               return console.log('Malformed ID3 frame encountered. Skipping metadata parsing.');
42505             }
42506 
42507             frameHeader = String.fromCharCode(tag.data[frameStart], tag.data[frameStart + 1], tag.data[frameStart + 2], tag.data[frameStart + 3]);
42508             frame = {
42509               id: frameHeader,
42510               data: tag.data.subarray(frameStart + 10, frameStart + frameSize + 10)
42511             };
42512             frame.key = frame.id;
42513 
42514             if (tagParsers[frame.id]) {
42515               tagParsers[frame.id](frame); // handle the special PRIV frame used to indicate the start
42516               // time for raw AAC data
42517 
42518               if (frame.owner === 'com.apple.streaming.transportStreamTimestamp') {
42519                 var d = frame.data,
42520                     size = (d[3] & 0x01) << 30 | d[4] << 22 | d[5] << 14 | d[6] << 6 | d[7] >>> 2;
42521                 size *= 4;
42522                 size += d[7] & 0x03;
42523                 frame.timeStamp = size; // in raw AAC, all subsequent data will be timestamped based
42524                 // on the value of this frame
42525                 // we couldn't have known the appropriate pts and dts before
vendor: 10,312 bytes, lines 42526-42782
42526                 // parsing this ID3 tag so set those values now
42527 
42528                 if (tag.pts === undefined && tag.dts === undefined) {
42529                   tag.pts = frame.timeStamp;
42530                   tag.dts = frame.timeStamp;
42531                 }
42532 
42533                 this.trigger('timestamp', frame);
42534               }
42535             }
42536 
42537             tag.frames.push(frame);
42538             frameStart += 10; // advance past the frame header
42539 
42540             frameStart += frameSize; // advance past the frame body
42541           } while (frameStart < tagSize);
42542 
42543           this.trigger('data', tag);
42544         };
42545       };
42546 
42547       _MetadataStream.prototype = new stream();
42548       var metadataStream = _MetadataStream;
42549       var TimestampRolloverStream$1 = timestampRolloverStream.TimestampRolloverStream; // object types
42550 
42551       var _TransportPacketStream, _TransportParseStream, _ElementaryStream; // constants
42552 
42553 
42554       var MP2T_PACKET_LENGTH = 188,
42555           // bytes
42556       SYNC_BYTE = 0x47;
42557       /**
42558        * Splits an incoming stream of binary data into MPEG-2 Transport
42559        * Stream packets.
42560        */
42561 
42562       _TransportPacketStream = function TransportPacketStream() {
42563         var buffer = new Uint8Array(MP2T_PACKET_LENGTH),
42564             bytesInBuffer = 0;
42565 
42566         _TransportPacketStream.prototype.init.call(this); // Deliver new bytes to the stream.
42567 
42568         /**
42569          * Split a stream of data into M2TS packets
42570         **/
42571 
42572 
42573         this.push = function (bytes) {
42574           var startIndex = 0,
42575               endIndex = MP2T_PACKET_LENGTH,
42576               everything; // If there are bytes remaining from the last segment, prepend them to the
42577           // bytes that were pushed in
42578 
42579           if (bytesInBuffer) {
42580             everything = new Uint8Array(bytes.byteLength + bytesInBuffer);
42581             everything.set(buffer.subarray(0, bytesInBuffer));
42582             everything.set(bytes, bytesInBuffer);
42583             bytesInBuffer = 0;
42584           } else {
42585             everything = bytes;
42586           } // While we have enough data for a packet
42587 
42588 
42589           while (endIndex < everything.byteLength) {
42590             // Look for a pair of start and end sync bytes in the data..
42591             if (everything[startIndex] === SYNC_BYTE && everything[endIndex] === SYNC_BYTE) {
42592               // We found a packet so emit it and jump one whole packet forward in
42593               // the stream
42594               this.trigger('data', everything.subarray(startIndex, endIndex));
42595               startIndex += MP2T_PACKET_LENGTH;
42596               endIndex += MP2T_PACKET_LENGTH;
42597               continue;
42598             } // If we get here, we have somehow become de-synchronized and we need to step
42599             // forward one byte at a time until we find a pair of sync bytes that denote
42600             // a packet
42601 
42602 
42603             startIndex++;
42604             endIndex++;
42605           } // If there was some data left over at the end of the segment that couldn't
42606           // possibly be a whole packet, keep it because it might be the start of a packet
42607           // that continues in the next segment
42608 
42609 
42610           if (startIndex < everything.byteLength) {
42611             buffer.set(everything.subarray(startIndex), 0);
42612             bytesInBuffer = everything.byteLength - startIndex;
42613           }
42614         };
42615         /**
42616          * Passes identified M2TS packets to the TransportParseStream to be parsed
42617         **/
42618 
42619 
42620         this.flush = function () {
42621           // If the buffer contains a whole packet when we are being flushed, emit it
42622           // and empty the buffer. Otherwise hold onto the data because it may be
42623           // important for decoding the next segment
42624           if (bytesInBuffer === MP2T_PACKET_LENGTH && buffer[0] === SYNC_BYTE) {
42625             this.trigger('data', buffer);
42626             bytesInBuffer = 0;
42627           }
42628 
42629           this.trigger('done');
42630         };
42631 
42632         this.endTimeline = function () {
42633           this.flush();
42634           this.trigger('endedtimeline');
42635         };
42636 
42637         this.reset = function () {
42638           bytesInBuffer = 0;
42639           this.trigger('reset');
42640         };
42641       };
42642 
42643       _TransportPacketStream.prototype = new stream();
42644       /**
42645        * Accepts an MP2T TransportPacketStream and emits data events with parsed
42646        * forms of the individual transport stream packets.
42647        */
42648 
42649       _TransportParseStream = function TransportParseStream() {
42650         var parsePsi, parsePat, parsePmt, self;
42651 
42652         _TransportParseStream.prototype.init.call(this);
42653 
42654         self = this;
42655         this.packetsWaitingForPmt = [];
42656         this.programMapTable = undefined;
42657 
42658         parsePsi = function parsePsi(payload, psi) {
42659           var offset = 0; // PSI packets may be split into multiple sections and those
42660           // sections may be split into multiple packets. If a PSI
42661           // section starts in this packet, the payload_unit_start_indicator
42662           // will be true and the first byte of the payload will indicate
42663           // the offset from the current position to the start of the
42664           // section.
42665 
42666           if (psi.payloadUnitStartIndicator) {
42667             offset += payload[offset] + 1;
42668           }
42669 
42670           if (psi.type === 'pat') {
42671             parsePat(payload.subarray(offset), psi);
42672           } else {
42673             parsePmt(payload.subarray(offset), psi);
42674           }
42675         };
42676 
42677         parsePat = function parsePat(payload, pat) {
42678           pat.section_number = payload[7]; // eslint-disable-line camelcase
42679 
42680           pat.last_section_number = payload[8]; // eslint-disable-line camelcase
42681           // skip the PSI header and parse the first PMT entry
42682 
42683           self.pmtPid = (payload[10] & 0x1F) << 8 | payload[11];
42684           pat.pmtPid = self.pmtPid;
42685         };
42686         /**
42687          * Parse out the relevant fields of a Program Map Table (PMT).
42688          * @param payload {Uint8Array} the PMT-specific portion of an MP2T
42689          * packet. The first byte in this array should be the table_id
42690          * field.
42691          * @param pmt {object} the object that should be decorated with
42692          * fields parsed from the PMT.
42693          */
42694 
42695 
42696         parsePmt = function parsePmt(payload, pmt) {
42697           var sectionLength, tableEnd, programInfoLength, offset; // PMTs can be sent ahead of the time when they should actually
42698           // take effect. We don't believe this should ever be the case
42699           // for HLS but we'll ignore "forward" PMT declarations if we see
42700           // them. Future PMT declarations have the current_next_indicator
42701           // set to zero.
42702 
42703           if (!(payload[5] & 0x01)) {
42704             return;
42705           } // overwrite any existing program map table
42706 
42707 
42708           self.programMapTable = {
42709             video: null,
42710             audio: null,
42711             'timed-metadata': {}
42712           }; // the mapping table ends at the end of the current section
42713 
42714           sectionLength = (payload[1] & 0x0f) << 8 | payload[2];
42715           tableEnd = 3 + sectionLength - 4; // to determine where the table is, we have to figure out how
42716           // long the program info descriptors are
42717 
42718           programInfoLength = (payload[10] & 0x0f) << 8 | payload[11]; // advance the offset to the first entry in the mapping table
42719 
42720           offset = 12 + programInfoLength;
42721 
42722           while (offset < tableEnd) {
42723             var streamType = payload[offset];
42724             var pid = (payload[offset + 1] & 0x1F) << 8 | payload[offset + 2]; // only map a single elementary_pid for audio and video stream types
42725             // TODO: should this be done for metadata too? for now maintain behavior of
42726             //       multiple metadata streams
42727 
42728             if (streamType === streamTypes.H264_STREAM_TYPE && self.programMapTable.video === null) {
42729               self.programMapTable.video = pid;
42730             } else if (streamType === streamTypes.ADTS_STREAM_TYPE && self.programMapTable.audio === null) {
42731               self.programMapTable.audio = pid;
42732             } else if (streamType === streamTypes.METADATA_STREAM_TYPE) {
42733               // map pid to stream type for metadata streams
42734               self.programMapTable['timed-metadata'][pid] = streamType;
42735             } // move to the next table entry
42736             // skip past the elementary stream descriptors, if present
42737 
42738 
42739             offset += ((payload[offset + 3] & 0x0F) << 8 | payload[offset + 4]) + 5;
42740           } // record the map on the packet as well
42741 
42742 
42743           pmt.programMapTable = self.programMapTable;
42744         };
42745         /**
42746          * Deliver a new MP2T packet to the next stream in the pipeline.
42747          */
42748 
42749 
42750         this.push = function (packet) {
42751           var result = {},
42752               offset = 4;
42753           result.payloadUnitStartIndicator = !!(packet[1] & 0x40); // pid is a 13-bit field starting at the last bit of packet[1]
42754 
42755           result.pid = packet[1] & 0x1f;
42756           result.pid <<= 8;
42757           result.pid |= packet[2]; // if an adaption field is present, its length is specified by the
42758           // fifth byte of the TS packet header. The adaptation field is
42759           // used to add stuffing to PES packets that don't fill a complete
42760           // TS packet, and to specify some forms of timing and control data
42761           // that we do not currently use.
42762 
42763           if ((packet[3] & 0x30) >>> 4 > 0x01) {
42764             offset += packet[offset] + 1;
42765           } // parse the rest of the packet based on the type
42766 
42767 
42768           if (result.pid === 0) {
42769             result.type = 'pat';
42770             parsePsi(packet.subarray(offset), result);
42771             this.trigger('data', result);
42772           } else if (result.pid === this.pmtPid) {
42773             result.type = 'pmt';
42774             parsePsi(packet.subarray(offset), result);
42775             this.trigger('data', result); // if there are any packets waiting for a PMT to be found, process them now
42776 
42777             while (this.packetsWaitingForPmt.length) {
42778               this.processPes_.apply(this, this.packetsWaitingForPmt.shift());
42779             }
42780           } else if (this.programMapTable === undefined) {
42781             // When we have not seen a PMT yet, defer further processing of
42782             // PES packets until one has been parsed
vendor: 9,723 bytes, lines 42783-43041
42783             this.packetsWaitingForPmt.push([packet, offset, result]);
42784           } else {
42785             this.processPes_(packet, offset, result);
42786           }
42787         };
42788 
42789         this.processPes_ = function (packet, offset, result) {
42790           // set the appropriate stream type
42791           if (result.pid === this.programMapTable.video) {
42792             result.streamType = streamTypes.H264_STREAM_TYPE;
42793           } else if (result.pid === this.programMapTable.audio) {
42794             result.streamType = streamTypes.ADTS_STREAM_TYPE;
42795           } else {
42796             // if not video or audio, it is timed-metadata or unknown
42797             // if unknown, streamType will be undefined
42798             result.streamType = this.programMapTable['timed-metadata'][result.pid];
42799           }
42800 
42801           result.type = 'pes';
42802           result.data = packet.subarray(offset);
42803           this.trigger('data', result);
42804         };
42805       };
42806 
42807       _TransportParseStream.prototype = new stream();
42808       _TransportParseStream.STREAM_TYPES = {
42809         h264: 0x1b,
42810         adts: 0x0f
42811       };
42812       /**
42813        * Reconsistutes program elementary stream (PES) packets from parsed
42814        * transport stream packets. That is, if you pipe an
42815        * mp2t.TransportParseStream into a mp2t.ElementaryStream, the output
42816        * events will be events which capture the bytes for individual PES
42817        * packets plus relevant metadata that has been extracted from the
42818        * container.
42819        */
42820 
42821       _ElementaryStream = function ElementaryStream() {
42822         var self = this,
42823             // PES packet fragments
42824         video = {
42825           data: [],
42826           size: 0
42827         },
42828             audio = {
42829           data: [],
42830           size: 0
42831         },
42832             timedMetadata = {
42833           data: [],
42834           size: 0
42835         },
42836             programMapTable,
42837             parsePes = function parsePes(payload, pes) {
42838           var ptsDtsFlags; // get the packet length, this will be 0 for video
42839 
42840           pes.packetLength = 6 + (payload[4] << 8 | payload[5]); // find out if this packets starts a new keyframe
42841 
42842           pes.dataAlignmentIndicator = (payload[6] & 0x04) !== 0; // PES packets may be annotated with a PTS value, or a PTS value
42843           // and a DTS value. Determine what combination of values is
42844           // available to work with.
42845 
42846           ptsDtsFlags = payload[7]; // PTS and DTS are normally stored as a 33-bit number.  Javascript
42847           // performs all bitwise operations on 32-bit integers but javascript
42848           // supports a much greater range (52-bits) of integer using standard
42849           // mathematical operations.
42850           // We construct a 31-bit value using bitwise operators over the 31
42851           // most significant bits and then multiply by 4 (equal to a left-shift
42852           // of 2) before we add the final 2 least significant bits of the
42853           // timestamp (equal to an OR.)
42854 
42855           if (ptsDtsFlags & 0xC0) {
42856             // the PTS and DTS are not written out directly. For information
42857             // on how they are encoded, see
42858             // http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
42859             pes.pts = (payload[9] & 0x0E) << 27 | (payload[10] & 0xFF) << 20 | (payload[11] & 0xFE) << 12 | (payload[12] & 0xFF) << 5 | (payload[13] & 0xFE) >>> 3;
42860             pes.pts *= 4; // Left shift by 2
42861 
42862             pes.pts += (payload[13] & 0x06) >>> 1; // OR by the two LSBs
42863 
42864             pes.dts = pes.pts;
42865 
42866             if (ptsDtsFlags & 0x40) {
42867               pes.dts = (payload[14] & 0x0E) << 27 | (payload[15] & 0xFF) << 20 | (payload[16] & 0xFE) << 12 | (payload[17] & 0xFF) << 5 | (payload[18] & 0xFE) >>> 3;
42868               pes.dts *= 4; // Left shift by 2
42869 
42870               pes.dts += (payload[18] & 0x06) >>> 1; // OR by the two LSBs
42871             }
42872           } // the data section starts immediately after the PES header.
42873           // pes_header_data_length specifies the number of header bytes
42874           // that follow the last byte of the field.
42875 
42876 
42877           pes.data = payload.subarray(9 + payload[8]);
42878         },
42879 
42880         /**
42881           * Pass completely parsed PES packets to the next stream in the pipeline
42882          **/
42883         flushStream = function flushStream(stream$$1, type, forceFlush) {
42884           var packetData = new Uint8Array(stream$$1.size),
42885               event = {
42886             type: type
42887           },
42888               i = 0,
42889               offset = 0,
42890               packetFlushable = false,
42891               fragment; // do nothing if there is not enough buffered data for a complete
42892           // PES header
42893 
42894           if (!stream$$1.data.length || stream$$1.size < 9) {
42895             return;
42896           }
42897 
42898           event.trackId = stream$$1.data[0].pid; // reassemble the packet
42899 
42900           for (i = 0; i < stream$$1.data.length; i++) {
42901             fragment = stream$$1.data[i];
42902             packetData.set(fragment.data, offset);
42903             offset += fragment.data.byteLength;
42904           } // parse assembled packet's PES header
42905 
42906 
42907           parsePes(packetData, event); // non-video PES packets MUST have a non-zero PES_packet_length
42908           // check that there is enough stream data to fill the packet
42909 
42910           packetFlushable = type === 'video' || event.packetLength <= stream$$1.size; // flush pending packets if the conditions are right
42911 
42912           if (forceFlush || packetFlushable) {
42913             stream$$1.size = 0;
42914             stream$$1.data.length = 0;
42915           } // only emit packets that are complete. this is to avoid assembling
42916           // incomplete PES packets due to poor segmentation
42917 
42918 
42919           if (packetFlushable) {
42920             self.trigger('data', event);
42921           }
42922         };
42923 
42924         _ElementaryStream.prototype.init.call(this);
42925         /**
42926          * Identifies M2TS packet types and parses PES packets using metadata
42927          * parsed from the PMT
42928          **/
42929 
42930 
42931         this.push = function (data) {
42932           ({
42933             pat: function pat() {// we have to wait for the PMT to arrive as well before we
42934               // have any meaningful metadata
42935             },
42936             pes: function pes() {
42937               var stream$$1, streamType;
42938 
42939               switch (data.streamType) {
42940                 case streamTypes.H264_STREAM_TYPE:
42941                 case streamTypes.H264_STREAM_TYPE:
42942                   stream$$1 = video;
42943                   streamType = 'video';
42944                   break;
42945 
42946                 case streamTypes.ADTS_STREAM_TYPE:
42947                   stream$$1 = audio;
42948                   streamType = 'audio';
42949                   break;
42950 
42951                 case streamTypes.METADATA_STREAM_TYPE:
42952                   stream$$1 = timedMetadata;
42953                   streamType = 'timed-metadata';
42954                   break;
42955 
42956                 default:
42957                   // ignore unknown stream types
42958                   return;
42959               } // if a new packet is starting, we can flush the completed
42960               // packet
42961 
42962 
42963               if (data.payloadUnitStartIndicator) {
42964                 flushStream(stream$$1, streamType, true);
42965               } // buffer this fragment until we are sure we've received the
42966               // complete payload
42967 
42968 
42969               stream$$1.data.push(data);
42970               stream$$1.size += data.data.byteLength;
42971             },
42972             pmt: function pmt() {
42973               var event = {
42974                 type: 'metadata',
42975                 tracks: []
42976               };
42977               programMapTable = data.programMapTable; // translate audio and video streams to tracks
42978 
42979               if (programMapTable.video !== null) {
42980                 event.tracks.push({
42981                   timelineStartInfo: {
42982                     baseMediaDecodeTime: 0
42983                   },
42984                   id: +programMapTable.video,
42985                   codec: 'avc',
42986                   type: 'video'
42987                 });
42988               }
42989 
42990               if (programMapTable.audio !== null) {
42991                 event.tracks.push({
42992                   timelineStartInfo: {
42993                     baseMediaDecodeTime: 0
42994                   },
42995                   id: +programMapTable.audio,
42996                   codec: 'adts',
42997                   type: 'audio'
42998                 });
42999               }
43000 
43001               self.trigger('data', event);
43002             }
43003           })[data.type]();
43004         };
43005 
43006         this.reset = function () {
43007           video.size = 0;
43008           video.data.length = 0;
43009           audio.size = 0;
43010           audio.data.length = 0;
43011           this.trigger('reset');
43012         };
43013         /**
43014          * Flush any remaining input. Video PES packets may be of variable
43015          * length. Normally, the start of a new video packet can trigger the
43016          * finalization of the previous packet. That is not possible if no
43017          * more video is forthcoming, however. In that case, some other
43018          * mechanism (like the end of the file) has to be employed. When it is
43019          * clear that no additional data is forthcoming, calling this method
43020          * will flush the buffered packets.
43021          */
43022 
43023 
43024         this.flushStreams_ = function () {
43025           // !!THIS ORDER IS IMPORTANT!!
43026           // video first then audio
43027           flushStream(video, 'video');
43028           flushStream(audio, 'audio');
43029           flushStream(timedMetadata, 'timed-metadata');
43030         };
43031 
43032         this.flush = function () {
43033           this.flushStreams_();
43034           this.trigger('done');
43035         };
43036       };
43037 
43038       _ElementaryStream.prototype = new stream();
43039       var m2ts = {
43040         PAT_PID: 0x0000,
43041         MP2T_PACKET_LENGTH: MP2T_PACKET_LENGTH,
vendor: 6,108 bytes, lines 43042-43209
43042         TransportPacketStream: _TransportPacketStream,
43043         TransportParseStream: _TransportParseStream,
43044         ElementaryStream: _ElementaryStream,
43045         TimestampRolloverStream: TimestampRolloverStream$1,
43046         CaptionStream: captionStream.CaptionStream,
43047         Cea608Stream: captionStream.Cea608Stream,
43048         MetadataStream: metadataStream
43049       };
43050 
43051       for (var type in streamTypes) {
43052         if (streamTypes.hasOwnProperty(type)) {
43053           m2ts[type] = streamTypes[type];
43054         }
43055       }
43056 
43057       var m2ts_1 = m2ts;
43058       var ONE_SECOND_IN_TS$2 = clock.ONE_SECOND_IN_TS;
43059 
43060       var _AdtsStream;
43061 
43062       var ADTS_SAMPLING_FREQUENCIES = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350];
43063       /*
43064        * Accepts a ElementaryStream and emits data events with parsed
43065        * AAC Audio Frames of the individual packets. Input audio in ADTS
43066        * format is unpacked and re-emitted as AAC frames.
43067        *
43068        * @see http://wiki.multimedia.cx/index.php?title=ADTS
43069        * @see http://wiki.multimedia.cx/?title=Understanding_AAC
43070        */
43071 
43072       _AdtsStream = function AdtsStream(handlePartialSegments) {
43073         var buffer,
43074             frameNum = 0;
43075 
43076         _AdtsStream.prototype.init.call(this);
43077 
43078         this.push = function (packet) {
43079           var i = 0,
43080               frameLength,
43081               protectionSkipBytes,
43082               frameEnd,
43083               oldBuffer,
43084               sampleCount,
43085               adtsFrameDuration;
43086 
43087           if (!handlePartialSegments) {
43088             frameNum = 0;
43089           }
43090 
43091           if (packet.type !== 'audio') {
43092             // ignore non-audio data
43093             return;
43094           } // Prepend any data in the buffer to the input data so that we can parse
43095           // aac frames the cross a PES packet boundary
43096 
43097 
43098           if (buffer) {
43099             oldBuffer = buffer;
43100             buffer = new Uint8Array(oldBuffer.byteLength + packet.data.byteLength);
43101             buffer.set(oldBuffer);
43102             buffer.set(packet.data, oldBuffer.byteLength);
43103           } else {
43104             buffer = packet.data;
43105           } // unpack any ADTS frames which have been fully received
43106           // for details on the ADTS header, see http://wiki.multimedia.cx/index.php?title=ADTS
43107 
43108 
43109           while (i + 5 < buffer.length) {
43110             // Look for the start of an ADTS header..
43111             if (buffer[i] !== 0xFF || (buffer[i + 1] & 0xF6) !== 0xF0) {
43112               // If a valid header was not found,  jump one forward and attempt to
43113               // find a valid ADTS header starting at the next byte
43114               i++;
43115               continue;
43116             } // The protection skip bit tells us if we have 2 bytes of CRC data at the
43117             // end of the ADTS header
43118 
43119 
43120             protectionSkipBytes = (~buffer[i + 1] & 0x01) * 2; // Frame length is a 13 bit integer starting 16 bits from the
43121             // end of the sync sequence
43122 
43123             frameLength = (buffer[i + 3] & 0x03) << 11 | buffer[i + 4] << 3 | (buffer[i + 5] & 0xe0) >> 5;
43124             sampleCount = ((buffer[i + 6] & 0x03) + 1) * 1024;
43125             adtsFrameDuration = sampleCount * ONE_SECOND_IN_TS$2 / ADTS_SAMPLING_FREQUENCIES[(buffer[i + 2] & 0x3c) >>> 2];
43126             frameEnd = i + frameLength; // If we don't have enough data to actually finish this ADTS frame, return
43127             // and wait for more data
43128 
43129             if (buffer.byteLength < frameEnd) {
43130               return;
43131             } // Otherwise, deliver the complete AAC frame
43132 
43133 
43134             this.trigger('data', {
43135               pts: packet.pts + frameNum * adtsFrameDuration,
43136               dts: packet.dts + frameNum * adtsFrameDuration,
43137               sampleCount: sampleCount,
43138               audioobjecttype: (buffer[i + 2] >>> 6 & 0x03) + 1,
43139               channelcount: (buffer[i + 2] & 1) << 2 | (buffer[i + 3] & 0xc0) >>> 6,
43140               samplerate: ADTS_SAMPLING_FREQUENCIES[(buffer[i + 2] & 0x3c) >>> 2],
43141               samplingfrequencyindex: (buffer[i + 2] & 0x3c) >>> 2,
43142               // assume ISO/IEC 14496-12 AudioSampleEntry default of 16
43143               samplesize: 16,
43144               data: buffer.subarray(i + 7 + protectionSkipBytes, frameEnd)
43145             });
43146             frameNum++; // If the buffer is empty, clear it and return
43147 
43148             if (buffer.byteLength === frameEnd) {
43149               buffer = undefined;
43150               return;
43151             } // Remove the finished frame from the buffer and start the process again
43152 
43153 
43154             buffer = buffer.subarray(frameEnd);
43155           }
43156         };
43157 
43158         this.flush = function () {
43159           frameNum = 0;
43160           this.trigger('done');
43161         };
43162 
43163         this.reset = function () {
43164           buffer = void 0;
43165           this.trigger('reset');
43166         };
43167 
43168         this.endTimeline = function () {
43169           buffer = void 0;
43170           this.trigger('endedtimeline');
43171         };
43172       };
43173 
43174       _AdtsStream.prototype = new stream();
43175       var adts = _AdtsStream;
43176       /**
43177        * mux.js
43178        *
43179        * Copyright (c) Brightcove
43180        * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
43181        */
43182 
43183       var ExpGolomb;
43184       /**
43185        * Parser for exponential Golomb codes, a variable-bitwidth number encoding
43186        * scheme used by h264.
43187        */
43188 
43189       ExpGolomb = function ExpGolomb(workingData) {
43190         var // the number of bytes left to examine in workingData
43191         workingBytesAvailable = workingData.byteLength,
43192             // the current word being examined
43193         workingWord = 0,
43194             // :uint
43195         // the number of bits left to examine in the current word
43196         workingBitsAvailable = 0; // :uint;
43197         // ():uint
43198 
43199         this.length = function () {
43200           return 8 * workingBytesAvailable;
43201         }; // ():uint
43202 
43203 
43204         this.bitsAvailable = function () {
43205           return 8 * workingBytesAvailable + workingBitsAvailable;
43206         }; // ():void
43207 
43208 
43209         this.loadWord = function () {
vendor: 8,404 bytes, lines 43210-43487
43210           var position = workingData.byteLength - workingBytesAvailable,
43211               workingBytes = new Uint8Array(4),
43212               availableBytes = Math.min(4, workingBytesAvailable);
43213 
43214           if (availableBytes === 0) {
43215             throw new Error('no bytes available');
43216           }
43217 
43218           workingBytes.set(workingData.subarray(position, position + availableBytes));
43219           workingWord = new DataView(workingBytes.buffer).getUint32(0); // track the amount of workingData that has been processed
43220 
43221           workingBitsAvailable = availableBytes * 8;
43222           workingBytesAvailable -= availableBytes;
43223         }; // (count:int):void
43224 
43225 
43226         this.skipBits = function (count) {
43227           var skipBytes; // :int
43228 
43229           if (workingBitsAvailable > count) {
43230             workingWord <<= count;
43231             workingBitsAvailable -= count;
43232           } else {
43233             count -= workingBitsAvailable;
43234             skipBytes = Math.floor(count / 8);
43235             count -= skipBytes * 8;
43236             workingBytesAvailable -= skipBytes;
43237             this.loadWord();
43238             workingWord <<= count;
43239             workingBitsAvailable -= count;
43240           }
43241         }; // (size:int):uint
43242 
43243 
43244         this.readBits = function (size) {
43245           var bits = Math.min(workingBitsAvailable, size),
43246               // :uint
43247           valu = workingWord >>> 32 - bits; // :uint
43248           // if size > 31, handle error
43249 
43250           workingBitsAvailable -= bits;
43251 
43252           if (workingBitsAvailable > 0) {
43253             workingWord <<= bits;
43254           } else if (workingBytesAvailable > 0) {
43255             this.loadWord();
43256           }
43257 
43258           bits = size - bits;
43259 
43260           if (bits > 0) {
43261             return valu << bits | this.readBits(bits);
43262           }
43263 
43264           return valu;
43265         }; // ():uint
43266 
43267 
43268         this.skipLeadingZeros = function () {
43269           var leadingZeroCount; // :uint
43270 
43271           for (leadingZeroCount = 0; leadingZeroCount < workingBitsAvailable; ++leadingZeroCount) {
43272             if ((workingWord & 0x80000000 >>> leadingZeroCount) !== 0) {
43273               // the first bit of working word is 1
43274               workingWord <<= leadingZeroCount;
43275               workingBitsAvailable -= leadingZeroCount;
43276               return leadingZeroCount;
43277             }
43278           } // we exhausted workingWord and still have not found a 1
43279 
43280 
43281           this.loadWord();
43282           return leadingZeroCount + this.skipLeadingZeros();
43283         }; // ():void
43284 
43285 
43286         this.skipUnsignedExpGolomb = function () {
43287           this.skipBits(1 + this.skipLeadingZeros());
43288         }; // ():void
43289 
43290 
43291         this.skipExpGolomb = function () {
43292           this.skipBits(1 + this.skipLeadingZeros());
43293         }; // ():uint
43294 
43295 
43296         this.readUnsignedExpGolomb = function () {
43297           var clz = this.skipLeadingZeros(); // :uint
43298 
43299           return this.readBits(clz + 1) - 1;
43300         }; // ():int
43301 
43302 
43303         this.readExpGolomb = function () {
43304           var valu = this.readUnsignedExpGolomb(); // :int
43305 
43306           if (0x01 & valu) {
43307             // the number is odd if the low order bit is set
43308             return 1 + valu >>> 1; // add 1 to make it even, and divide by 2
43309           }
43310 
43311           return -1 * (valu >>> 1); // divide by two then make it negative
43312         }; // Some convenience functions
43313         // :Boolean
43314 
43315 
43316         this.readBoolean = function () {
43317           return this.readBits(1) === 1;
43318         }; // ():int
43319 
43320 
43321         this.readUnsignedByte = function () {
43322           return this.readBits(8);
43323         };
43324 
43325         this.loadWord();
43326       };
43327 
43328       var expGolomb = ExpGolomb;
43329 
43330       var _H264Stream, _NalByteStream;
43331 
43332       var PROFILES_WITH_OPTIONAL_SPS_DATA;
43333       /**
43334        * Accepts a NAL unit byte stream and unpacks the embedded NAL units.
43335        */
43336 
43337       _NalByteStream = function NalByteStream() {
43338         var syncPoint = 0,
43339             i,
43340             buffer;
43341 
43342         _NalByteStream.prototype.init.call(this);
43343         /*
43344          * Scans a byte stream and triggers a data event with the NAL units found.
43345          * @param {Object} data Event received from H264Stream
43346          * @param {Uint8Array} data.data The h264 byte stream to be scanned
43347          *
43348          * @see H264Stream.push
43349          */
43350 
43351 
43352         this.push = function (data) {
43353           var swapBuffer;
43354 
43355           if (!buffer) {
43356             buffer = data.data;
43357           } else {
43358             swapBuffer = new Uint8Array(buffer.byteLength + data.data.byteLength);
43359             swapBuffer.set(buffer);
43360             swapBuffer.set(data.data, buffer.byteLength);
43361             buffer = swapBuffer;
43362           }
43363 
43364           var len = buffer.byteLength; // Rec. ITU-T H.264, Annex B
43365           // scan for NAL unit boundaries
43366           // a match looks like this:
43367           // 0 0 1 .. NAL .. 0 0 1
43368           // ^ sync point        ^ i
43369           // or this:
43370           // 0 0 1 .. NAL .. 0 0 0
43371           // ^ sync point        ^ i
43372           // advance the sync point to a NAL start, if necessary
43373 
43374           for (; syncPoint < len - 3; syncPoint++) {
43375             if (buffer[syncPoint + 2] === 1) {
43376               // the sync point is properly aligned
43377               i = syncPoint + 5;
43378               break;
43379             }
43380           }
43381 
43382           while (i < len) {
43383             // look at the current byte to determine if we've hit the end of
43384             // a NAL unit boundary
43385             switch (buffer[i]) {
43386               case 0:
43387                 // skip past non-sync sequences
43388                 if (buffer[i - 1] !== 0) {
43389                   i += 2;
43390                   break;
43391                 } else if (buffer[i - 2] !== 0) {
43392                   i++;
43393                   break;
43394                 } // deliver the NAL unit if it isn't empty
43395 
43396 
43397                 if (syncPoint + 3 !== i - 2) {
43398                   this.trigger('data', buffer.subarray(syncPoint + 3, i - 2));
43399                 } // drop trailing zeroes
43400 
43401 
43402                 do {
43403                   i++;
43404                 } while (buffer[i] !== 1 && i < len);
43405 
43406                 syncPoint = i - 2;
43407                 i += 3;
43408                 break;
43409 
43410               case 1:
43411                 // skip past non-sync sequences
43412                 if (buffer[i - 1] !== 0 || buffer[i - 2] !== 0) {
43413                   i += 3;
43414                   break;
43415                 } // deliver the NAL unit
43416 
43417 
43418                 this.trigger('data', buffer.subarray(syncPoint + 3, i - 2));
43419                 syncPoint = i - 2;
43420                 i += 3;
43421                 break;
43422 
43423               default:
43424                 // the current byte isn't a one or zero, so it cannot be part
43425                 // of a sync sequence
43426                 i += 3;
43427                 break;
43428             }
43429           } // filter out the NAL units that were delivered
43430 
43431 
43432           buffer = buffer.subarray(syncPoint);
43433           i -= syncPoint;
43434           syncPoint = 0;
43435         };
43436 
43437         this.reset = function () {
43438           buffer = null;
43439           syncPoint = 0;
43440           this.trigger('reset');
43441         };
43442 
43443         this.flush = function () {
43444           // deliver the last buffered NAL unit
43445           if (buffer && buffer.byteLength > 3) {
43446             this.trigger('data', buffer.subarray(syncPoint + 3));
43447           } // reset the stream state
43448 
43449 
43450           buffer = null;
43451           syncPoint = 0;
43452           this.trigger('done');
43453         };
43454 
43455         this.endTimeline = function () {
43456           this.flush();
43457           this.trigger('endedtimeline');
43458         };
43459       };
43460 
43461       _NalByteStream.prototype = new stream(); // values of profile_idc that indicate additional fields are included in the SPS
43462       // see Recommendation ITU-T H.264 (4/2013),
43463       // 7.3.2.1.1 Sequence parameter set data syntax
43464 
43465       PROFILES_WITH_OPTIONAL_SPS_DATA = {
43466         100: true,
43467         110: true,
43468         122: true,
43469         244: true,
43470         44: true,
43471         83: true,
43472         86: true,
43473         118: true,
43474         128: true,
43475         138: true,
43476         139: true,
43477         134: true
43478       };
43479       /**
43480        * Accepts input from a ElementaryStream and produces H.264 NAL unit data
43481        * events.
43482        */
43483 
43484       _H264Stream = function H264Stream() {
43485         var nalByteStream = new _NalByteStream(),
43486             self,
43487             trackId,
43488             currentPts,
43489             currentDts,
43490             discardEmulationPreventionBytes,
43491             readSequenceParameterSet,
43492             skipScalingList;
43493 
43494         _H264Stream.prototype.init.call(this);
43495 
43496         self = this;
43497         /*
43498          * Pushes a packet from a stream onto the NalByteStream
43499          *
43500          * @param {Object} packet - A packet received from a stream
43501          * @param {Uint8Array} packet.data - The raw bytes of the packet
43502          * @param {Number} packet.dts - Decode timestamp of the packet
43503          * @param {Number} packet.pts - Presentation timestamp of the packet
43504          * @param {Number} packet.trackId - The id of the h264 track this packet came from
43505          * @param {('video'|'audio')} packet.type - The type of packet
43506          *
43507          */
43508 
43509         this.push = function (packet) {
43510           if (packet.type !== 'video') {
43511             return;
43512           }
43513 
43514           trackId = packet.trackId;
43515           currentPts = packet.pts;
43516           currentDts = packet.dts;
43517           nalByteStream.push(packet);
43518         };
43519         /*
43520          * Identify NAL unit types and pass on the NALU, trackId, presentation and decode timestamps
43521          * for the NALUs to the next stream component.
43522          * Also, preprocess caption and sequence parameter NALUs.
43523          *
43524          * @param {Uint8Array} data - A NAL unit identified by `NalByteStream.push`
43525          * @see NalByteStream.push
43526          */
43527 
43528 
43529         nalByteStream.on('data', function (data) {
43530           var event = {
43531             trackId: trackId,
vendor: 12,657 bytes, lines 43532-43901
43532             pts: currentPts,
43533             dts: currentDts,
43534             data: data
43535           };
43536 
43537           switch (data[0] & 0x1f) {
43538             case 0x05:
43539               event.nalUnitType = 'slice_layer_without_partitioning_rbsp_idr';
43540               break;
43541 
43542             case 0x06:
43543               event.nalUnitType = 'sei_rbsp';
43544               event.escapedRBSP = discardEmulationPreventionBytes(data.subarray(1));
43545               break;
43546 
43547             case 0x07:
43548               event.nalUnitType = 'seq_parameter_set_rbsp';
43549               event.escapedRBSP = discardEmulationPreventionBytes(data.subarray(1));
43550               event.config = readSequenceParameterSet(event.escapedRBSP);
43551               break;
43552 
43553             case 0x08:
43554               event.nalUnitType = 'pic_parameter_set_rbsp';
43555               break;
43556 
43557             case 0x09:
43558               event.nalUnitType = 'access_unit_delimiter_rbsp';
43559               break;
43560 
43561             default:
43562               break;
43563           } // This triggers data on the H264Stream
43564 
43565 
43566           self.trigger('data', event);
43567         });
43568         nalByteStream.on('done', function () {
43569           self.trigger('done');
43570         });
43571         nalByteStream.on('partialdone', function () {
43572           self.trigger('partialdone');
43573         });
43574         nalByteStream.on('reset', function () {
43575           self.trigger('reset');
43576         });
43577         nalByteStream.on('endedtimeline', function () {
43578           self.trigger('endedtimeline');
43579         });
43580 
43581         this.flush = function () {
43582           nalByteStream.flush();
43583         };
43584 
43585         this.partialFlush = function () {
43586           nalByteStream.partialFlush();
43587         };
43588 
43589         this.reset = function () {
43590           nalByteStream.reset();
43591         };
43592 
43593         this.endTimeline = function () {
43594           nalByteStream.endTimeline();
43595         };
43596         /**
43597          * Advance the ExpGolomb decoder past a scaling list. The scaling
43598          * list is optionally transmitted as part of a sequence parameter
43599          * set and is not relevant to transmuxing.
43600          * @param count {number} the number of entries in this scaling list
43601          * @param expGolombDecoder {object} an ExpGolomb pointed to the
43602          * start of a scaling list
43603          * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1
43604          */
43605 
43606 
43607         skipScalingList = function skipScalingList(count, expGolombDecoder) {
43608           var lastScale = 8,
43609               nextScale = 8,
43610               j,
43611               deltaScale;
43612 
43613           for (j = 0; j < count; j++) {
43614             if (nextScale !== 0) {
43615               deltaScale = expGolombDecoder.readExpGolomb();
43616               nextScale = (lastScale + deltaScale + 256) % 256;
43617             }
43618 
43619             lastScale = nextScale === 0 ? lastScale : nextScale;
43620           }
43621         };
43622         /**
43623          * Expunge any "Emulation Prevention" bytes from a "Raw Byte
43624          * Sequence Payload"
43625          * @param data {Uint8Array} the bytes of a RBSP from a NAL
43626          * unit
43627          * @return {Uint8Array} the RBSP without any Emulation
43628          * Prevention Bytes
43629          */
43630 
43631 
43632         discardEmulationPreventionBytes = function discardEmulationPreventionBytes(data) {
43633           var length = data.byteLength,
43634               emulationPreventionBytesPositions = [],
43635               i = 1,
43636               newLength,
43637               newData; // Find all `Emulation Prevention Bytes`
43638 
43639           while (i < length - 2) {
43640             if (data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 0x03) {
43641               emulationPreventionBytesPositions.push(i + 2);
43642               i += 2;
43643             } else {
43644               i++;
43645             }
43646           } // If no Emulation Prevention Bytes were found just return the original
43647           // array
43648 
43649 
43650           if (emulationPreventionBytesPositions.length === 0) {
43651             return data;
43652           } // Create a new array to hold the NAL unit data
43653 
43654 
43655           newLength = length - emulationPreventionBytesPositions.length;
43656           newData = new Uint8Array(newLength);
43657           var sourceIndex = 0;
43658 
43659           for (i = 0; i < newLength; sourceIndex++, i++) {
43660             if (sourceIndex === emulationPreventionBytesPositions[0]) {
43661               // Skip this byte
43662               sourceIndex++; // Remove this position index
43663 
43664               emulationPreventionBytesPositions.shift();
43665             }
43666 
43667             newData[i] = data[sourceIndex];
43668           }
43669 
43670           return newData;
43671         };
43672         /**
43673          * Read a sequence parameter set and return some interesting video
43674          * properties. A sequence parameter set is the H264 metadata that
43675          * describes the properties of upcoming video frames.
43676          * @param data {Uint8Array} the bytes of a sequence parameter set
43677          * @return {object} an object with configuration parsed from the
43678          * sequence parameter set, including the dimensions of the
43679          * associated video frames.
43680          */
43681 
43682 
43683         readSequenceParameterSet = function readSequenceParameterSet(data) {
43684           var frameCropLeftOffset = 0,
43685               frameCropRightOffset = 0,
43686               frameCropTopOffset = 0,
43687               frameCropBottomOffset = 0,
43688               sarScale = 1,
43689               expGolombDecoder,
43690               profileIdc,
43691               levelIdc,
43692               profileCompatibility,
43693               chromaFormatIdc,
43694               picOrderCntType,
43695               numRefFramesInPicOrderCntCycle,
43696               picWidthInMbsMinus1,
43697               picHeightInMapUnitsMinus1,
43698               frameMbsOnlyFlag,
43699               scalingListCount,
43700               sarRatio,
43701               aspectRatioIdc,
43702               i;
43703           expGolombDecoder = new expGolomb(data);
43704           profileIdc = expGolombDecoder.readUnsignedByte(); // profile_idc
43705 
43706           profileCompatibility = expGolombDecoder.readUnsignedByte(); // constraint_set[0-5]_flag
43707 
43708           levelIdc = expGolombDecoder.readUnsignedByte(); // level_idc u(8)
43709 
43710           expGolombDecoder.skipUnsignedExpGolomb(); // seq_parameter_set_id
43711           // some profiles have more optional data we don't need
43712 
43713           if (PROFILES_WITH_OPTIONAL_SPS_DATA[profileIdc]) {
43714             chromaFormatIdc = expGolombDecoder.readUnsignedExpGolomb();
43715 
43716             if (chromaFormatIdc === 3) {
43717               expGolombDecoder.skipBits(1); // separate_colour_plane_flag
43718             }
43719 
43720             expGolombDecoder.skipUnsignedExpGolomb(); // bit_depth_luma_minus8
43721 
43722             expGolombDecoder.skipUnsignedExpGolomb(); // bit_depth_chroma_minus8
43723 
43724             expGolombDecoder.skipBits(1); // qpprime_y_zero_transform_bypass_flag
43725 
43726             if (expGolombDecoder.readBoolean()) {
43727               // seq_scaling_matrix_present_flag
43728               scalingListCount = chromaFormatIdc !== 3 ? 8 : 12;
43729 
43730               for (i = 0; i < scalingListCount; i++) {
43731                 if (expGolombDecoder.readBoolean()) {
43732                   // seq_scaling_list_present_flag[ i ]
43733                   if (i < 6) {
43734                     skipScalingList(16, expGolombDecoder);
43735                   } else {
43736                     skipScalingList(64, expGolombDecoder);
43737                   }
43738                 }
43739               }
43740             }
43741           }
43742 
43743           expGolombDecoder.skipUnsignedExpGolomb(); // log2_max_frame_num_minus4
43744 
43745           picOrderCntType = expGolombDecoder.readUnsignedExpGolomb();
43746 
43747           if (picOrderCntType === 0) {
43748             expGolombDecoder.readUnsignedExpGolomb(); // log2_max_pic_order_cnt_lsb_minus4
43749           } else if (picOrderCntType === 1) {
43750             expGolombDecoder.skipBits(1); // delta_pic_order_always_zero_flag
43751 
43752             expGolombDecoder.skipExpGolomb(); // offset_for_non_ref_pic
43753 
43754             expGolombDecoder.skipExpGolomb(); // offset_for_top_to_bottom_field
43755 
43756             numRefFramesInPicOrderCntCycle = expGolombDecoder.readUnsignedExpGolomb();
43757 
43758             for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) {
43759               expGolombDecoder.skipExpGolomb(); // offset_for_ref_frame[ i ]
43760             }
43761           }
43762 
43763           expGolombDecoder.skipUnsignedExpGolomb(); // max_num_ref_frames
43764 
43765           expGolombDecoder.skipBits(1); // gaps_in_frame_num_value_allowed_flag
43766 
43767           picWidthInMbsMinus1 = expGolombDecoder.readUnsignedExpGolomb();
43768           picHeightInMapUnitsMinus1 = expGolombDecoder.readUnsignedExpGolomb();
43769           frameMbsOnlyFlag = expGolombDecoder.readBits(1);
43770 
43771           if (frameMbsOnlyFlag === 0) {
43772             expGolombDecoder.skipBits(1); // mb_adaptive_frame_field_flag
43773           }
43774 
43775           expGolombDecoder.skipBits(1); // direct_8x8_inference_flag
43776 
43777           if (expGolombDecoder.readBoolean()) {
43778             // frame_cropping_flag
43779             frameCropLeftOffset = expGolombDecoder.readUnsignedExpGolomb();
43780             frameCropRightOffset = expGolombDecoder.readUnsignedExpGolomb();
43781             frameCropTopOffset = expGolombDecoder.readUnsignedExpGolomb();
43782             frameCropBottomOffset = expGolombDecoder.readUnsignedExpGolomb();
43783           }
43784 
43785           if (expGolombDecoder.readBoolean()) {
43786             // vui_parameters_present_flag
43787             if (expGolombDecoder.readBoolean()) {
43788               // aspect_ratio_info_present_flag
43789               aspectRatioIdc = expGolombDecoder.readUnsignedByte();
43790 
43791               switch (aspectRatioIdc) {
43792                 case 1:
43793                   sarRatio = [1, 1];
43794                   break;
43795 
43796                 case 2:
43797                   sarRatio = [12, 11];
43798                   break;
43799 
43800                 case 3:
43801                   sarRatio = [10, 11];
43802                   break;
43803 
43804                 case 4:
43805                   sarRatio = [16, 11];
43806                   break;
43807 
43808                 case 5:
43809                   sarRatio = [40, 33];
43810                   break;
43811 
43812                 case 6:
43813                   sarRatio = [24, 11];
43814                   break;
43815 
43816                 case 7:
43817                   sarRatio = [20, 11];
43818                   break;
43819 
43820                 case 8:
43821                   sarRatio = [32, 11];
43822                   break;
43823 
43824                 case 9:
43825                   sarRatio = [80, 33];
43826                   break;
43827 
43828                 case 10:
43829                   sarRatio = [18, 11];
43830                   break;
43831 
43832                 case 11:
43833                   sarRatio = [15, 11];
43834                   break;
43835 
43836                 case 12:
43837                   sarRatio = [64, 33];
43838                   break;
43839 
43840                 case 13:
43841                   sarRatio = [160, 99];
43842                   break;
43843 
43844                 case 14:
43845                   sarRatio = [4, 3];
43846                   break;
43847 
43848                 case 15:
43849                   sarRatio = [3, 2];
43850                   break;
43851 
43852                 case 16:
43853                   sarRatio = [2, 1];
43854                   break;
43855 
43856                 case 255:
43857                   {
43858                     sarRatio = [expGolombDecoder.readUnsignedByte() << 8 | expGolombDecoder.readUnsignedByte(), expGolombDecoder.readUnsignedByte() << 8 | expGolombDecoder.readUnsignedByte()];
43859                     break;
43860                   }
43861               }
43862 
43863               if (sarRatio) {
43864                 sarScale = sarRatio[0] / sarRatio[1];
43865               }
43866             }
43867           }
43868 
43869           return {
43870             profileIdc: profileIdc,
43871             levelIdc: levelIdc,
43872             profileCompatibility: profileCompatibility,
43873             width: Math.ceil(((picWidthInMbsMinus1 + 1) * 16 - frameCropLeftOffset * 2 - frameCropRightOffset * 2) * sarScale),
43874             height: (2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16 - frameCropTopOffset * 2 - frameCropBottomOffset * 2,
43875             sarRatio: sarRatio
43876           };
43877         };
43878       };
43879 
43880       _H264Stream.prototype = new stream();
43881       var h264 = {
43882         H264Stream: _H264Stream,
43883         NalByteStream: _NalByteStream
43884       };
43885       /**
43886        * mux.js
43887        *
43888        * Copyright (c) Brightcove
43889        * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
43890        *
43891        * Utilities to detect basic properties and metadata about Aac data.
43892        */
43893 
43894       var ADTS_SAMPLING_FREQUENCIES$1 = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350];
43895 
43896       var isLikelyAacData = function isLikelyAacData(data) {
43897         if (data[0] === 'I'.charCodeAt(0) && data[1] === 'D'.charCodeAt(0) && data[2] === '3'.charCodeAt(0)) {
43898           return true;
43899         }
43900 
43901         return false;
vendor: 7,509 bytes, lines 43902-44108
43902       };
43903 
43904       var parseSyncSafeInteger$1 = function parseSyncSafeInteger(data) {
43905         return data[0] << 21 | data[1] << 14 | data[2] << 7 | data[3];
43906       }; // return a percent-encoded representation of the specified byte range
43907       // @see http://en.wikipedia.org/wiki/Percent-encoding
43908 
43909 
43910       var percentEncode$1 = function percentEncode(bytes, start, end) {
43911         var i,
43912             result = '';
43913 
43914         for (i = start; i < end; i++) {
43915           result += '%' + ('00' + bytes[i].toString(16)).slice(-2);
43916         }
43917 
43918         return result;
43919       }; // return the string representation of the specified byte range,
43920       // interpreted as ISO-8859-1.
43921 
43922 
43923       var parseIso88591$1 = function parseIso88591(bytes, start, end) {
43924         return unescape(percentEncode$1(bytes, start, end)); // jshint ignore:line
43925       };
43926 
43927       var parseId3TagSize = function parseId3TagSize(header, byteIndex) {
43928         var returnSize = header[byteIndex + 6] << 21 | header[byteIndex + 7] << 14 | header[byteIndex + 8] << 7 | header[byteIndex + 9],
43929             flags = header[byteIndex + 5],
43930             footerPresent = (flags & 16) >> 4;
43931 
43932         if (footerPresent) {
43933           return returnSize + 20;
43934         }
43935 
43936         return returnSize + 10;
43937       };
43938 
43939       var parseAdtsSize = function parseAdtsSize(header, byteIndex) {
43940         var lowThree = (header[byteIndex + 5] & 0xE0) >> 5,
43941             middle = header[byteIndex + 4] << 3,
43942             highTwo = header[byteIndex + 3] & 0x3 << 11;
43943         return highTwo | middle | lowThree;
43944       };
43945 
43946       var parseType = function parseType(header, byteIndex) {
43947         if (header[byteIndex] === 'I'.charCodeAt(0) && header[byteIndex + 1] === 'D'.charCodeAt(0) && header[byteIndex + 2] === '3'.charCodeAt(0)) {
43948           return 'timed-metadata';
43949         } else if (header[byteIndex] & 0xff === 0xff && (header[byteIndex + 1] & 0xf0) === 0xf0) {
43950           return 'audio';
43951         }
43952 
43953         return null;
43954       };
43955 
43956       var parseSampleRate = function parseSampleRate(packet) {
43957         var i = 0;
43958 
43959         while (i + 5 < packet.length) {
43960           if (packet[i] !== 0xFF || (packet[i + 1] & 0xF6) !== 0xF0) {
43961             // If a valid header was not found,  jump one forward and attempt to
43962             // find a valid ADTS header starting at the next byte
43963             i++;
43964             continue;
43965           }
43966 
43967           return ADTS_SAMPLING_FREQUENCIES$1[(packet[i + 2] & 0x3c) >>> 2];
43968         }
43969 
43970         return null;
43971       };
43972 
43973       var parseAacTimestamp = function parseAacTimestamp(packet) {
43974         var frameStart, frameSize, frame, frameHeader; // find the start of the first frame and the end of the tag
43975 
43976         frameStart = 10;
43977 
43978         if (packet[5] & 0x40) {
43979           // advance the frame start past the extended header
43980           frameStart += 4; // header size field
43981 
43982           frameStart += parseSyncSafeInteger$1(packet.subarray(10, 14));
43983         } // parse one or more ID3 frames
43984         // http://id3.org/id3v2.3.0#ID3v2_frame_overview
43985 
43986 
43987         do {
43988           // determine the number of bytes in this frame
43989           frameSize = parseSyncSafeInteger$1(packet.subarray(frameStart + 4, frameStart + 8));
43990 
43991           if (frameSize < 1) {
43992             return null;
43993           }
43994 
43995           frameHeader = String.fromCharCode(packet[frameStart], packet[frameStart + 1], packet[frameStart + 2], packet[frameStart + 3]);
43996 
43997           if (frameHeader === 'PRIV') {
43998             frame = packet.subarray(frameStart + 10, frameStart + frameSize + 10);
43999 
44000             for (var i = 0; i < frame.byteLength; i++) {
44001               if (frame[i] === 0) {
44002                 var owner = parseIso88591$1(frame, 0, i);
44003 
44004                 if (owner === 'com.apple.streaming.transportStreamTimestamp') {
44005                   var d = frame.subarray(i + 1);
44006                   var size = (d[3] & 0x01) << 30 | d[4] << 22 | d[5] << 14 | d[6] << 6 | d[7] >>> 2;
44007                   size *= 4;
44008                   size += d[7] & 0x03;
44009                   return size;
44010                 }
44011 
44012                 break;
44013               }
44014             }
44015           }
44016 
44017           frameStart += 10; // advance past the frame header
44018 
44019           frameStart += frameSize; // advance past the frame body
44020         } while (frameStart < packet.byteLength);
44021 
44022         return null;
44023       };
44024 
44025       var utils = {
44026         isLikelyAacData: isLikelyAacData,
44027         parseId3TagSize: parseId3TagSize,
44028         parseAdtsSize: parseAdtsSize,
44029         parseType: parseType,
44030         parseSampleRate: parseSampleRate,
44031         parseAacTimestamp: parseAacTimestamp
44032       }; // Constants
44033 
44034       var _AacStream;
44035       /**
44036        * Splits an incoming stream of binary data into ADTS and ID3 Frames.
44037        */
44038 
44039 
44040       _AacStream = function AacStream() {
44041         var everything = new Uint8Array(),
44042             timeStamp = 0;
44043 
44044         _AacStream.prototype.init.call(this);
44045 
44046         this.setTimestamp = function (timestamp) {
44047           timeStamp = timestamp;
44048         };
44049 
44050         this.push = function (bytes) {
44051           var frameSize = 0,
44052               byteIndex = 0,
44053               bytesLeft,
44054               chunk,
44055               packet,
44056               tempLength; // If there are bytes remaining from the last segment, prepend them to the
44057           // bytes that were pushed in
44058 
44059           if (everything.length) {
44060             tempLength = everything.length;
44061             everything = new Uint8Array(bytes.byteLength + tempLength);
44062             everything.set(everything.subarray(0, tempLength));
44063             everything.set(bytes, tempLength);
44064           } else {
44065             everything = bytes;
44066           }
44067 
44068           while (everything.length - byteIndex >= 3) {
44069             if (everything[byteIndex] === 'I'.charCodeAt(0) && everything[byteIndex + 1] === 'D'.charCodeAt(0) && everything[byteIndex + 2] === '3'.charCodeAt(0)) {
44070               // Exit early because we don't have enough to parse
44071               // the ID3 tag header
44072               if (everything.length - byteIndex < 10) {
44073                 break;
44074               } // check framesize
44075 
44076 
44077               frameSize = utils.parseId3TagSize(everything, byteIndex); // Exit early if we don't have enough in the buffer
44078               // to emit a full packet
44079               // Add to byteIndex to support multiple ID3 tags in sequence
44080 
44081               if (byteIndex + frameSize > everything.length) {
44082                 break;
44083               }
44084 
44085               chunk = {
44086                 type: 'timed-metadata',
44087                 data: everything.subarray(byteIndex, byteIndex + frameSize)
44088               };
44089               this.trigger('data', chunk);
44090               byteIndex += frameSize;
44091               continue;
44092             } else if ((everything[byteIndex] & 0xff) === 0xff && (everything[byteIndex + 1] & 0xf0) === 0xf0) {
44093               // Exit early because we don't have enough to parse
44094               // the ADTS frame header
44095               if (everything.length - byteIndex < 7) {
44096                 break;
44097               }
44098 
44099               frameSize = utils.parseAdtsSize(everything, byteIndex); // Exit early if we don't have enough in the buffer
44100               // to emit a full packet
44101 
44102               if (byteIndex + frameSize > everything.length) {
44103                 break;
44104               }
44105 
44106               packet = {
44107                 type: 'audio',
44108                 data: everything.subarray(byteIndex, byteIndex + frameSize),
vendor: 8,212 bytes, lines 44109-44319
44109                 pts: timeStamp,
44110                 dts: timeStamp
44111               };
44112               this.trigger('data', packet);
44113               byteIndex += frameSize;
44114               continue;
44115             }
44116 
44117             byteIndex++;
44118           }
44119 
44120           bytesLeft = everything.length - byteIndex;
44121 
44122           if (bytesLeft > 0) {
44123             everything = everything.subarray(byteIndex);
44124           } else {
44125             everything = new Uint8Array();
44126           }
44127         };
44128 
44129         this.reset = function () {
44130           everything = new Uint8Array();
44131           this.trigger('reset');
44132         };
44133 
44134         this.endTimeline = function () {
44135           everything = new Uint8Array();
44136           this.trigger('endedtimeline');
44137         };
44138       };
44139 
44140       _AacStream.prototype = new stream();
44141       var aac = _AacStream;
44142       var H264Stream = h264.H264Stream;
44143       var isLikelyAacData$1 = utils.isLikelyAacData;
44144       var ONE_SECOND_IN_TS$3 = clock.ONE_SECOND_IN_TS; // constants
44145 
44146       var AUDIO_PROPERTIES = ['audioobjecttype', 'channelcount', 'samplerate', 'samplingfrequencyindex', 'samplesize'];
44147       var VIDEO_PROPERTIES = ['width', 'height', 'profileIdc', 'levelIdc', 'profileCompatibility', 'sarRatio']; // object types
44148 
44149       var _VideoSegmentStream, _AudioSegmentStream, _Transmuxer, _CoalesceStream;
44150       /**
44151        * Compare two arrays (even typed) for same-ness
44152        */
44153 
44154 
44155       var arrayEquals = function arrayEquals(a, b) {
44156         var i;
44157 
44158         if (a.length !== b.length) {
44159           return false;
44160         } // compare the value of each element in the array
44161 
44162 
44163         for (i = 0; i < a.length; i++) {
44164           if (a[i] !== b[i]) {
44165             return false;
44166           }
44167         }
44168 
44169         return true;
44170       };
44171 
44172       var generateVideoSegmentTimingInfo = function generateVideoSegmentTimingInfo(baseMediaDecodeTime, startDts, startPts, endDts, endPts, prependedContentDuration) {
44173         var ptsOffsetFromDts = startPts - startDts,
44174             decodeDuration = endDts - startDts,
44175             presentationDuration = endPts - startPts; // The PTS and DTS values are based on the actual stream times from the segment,
44176         // however, the player time values will reflect a start from the baseMediaDecodeTime.
44177         // In order to provide relevant values for the player times, base timing info on the
44178         // baseMediaDecodeTime and the DTS and PTS durations of the segment.
44179 
44180         return {
44181           start: {
44182             dts: baseMediaDecodeTime,
44183             pts: baseMediaDecodeTime + ptsOffsetFromDts
44184           },
44185           end: {
44186             dts: baseMediaDecodeTime + decodeDuration,
44187             pts: baseMediaDecodeTime + presentationDuration
44188           },
44189           prependedContentDuration: prependedContentDuration,
44190           baseMediaDecodeTime: baseMediaDecodeTime
44191         };
44192       };
44193       /**
44194        * Constructs a single-track, ISO BMFF media segment from AAC data
44195        * events. The output of this stream can be fed to a SourceBuffer
44196        * configured with a suitable initialization segment.
44197        * @param track {object} track metadata configuration
44198        * @param options {object} transmuxer options object
44199        * @param options.keepOriginalTimestamps {boolean} If true, keep the timestamps
44200        *        in the source; false to adjust the first segment to start at 0.
44201        */
44202 
44203 
44204       _AudioSegmentStream = function AudioSegmentStream(track, options) {
44205         var adtsFrames = [],
44206             sequenceNumber = 0,
44207             earliestAllowedDts = 0,
44208             audioAppendStartTs = 0,
44209             videoBaseMediaDecodeTime = Infinity;
44210         options = options || {};
44211 
44212         _AudioSegmentStream.prototype.init.call(this);
44213 
44214         this.push = function (data) {
44215           trackDecodeInfo.collectDtsInfo(track, data);
44216 
44217           if (track) {
44218             AUDIO_PROPERTIES.forEach(function (prop) {
44219               track[prop] = data[prop];
44220             });
44221           } // buffer audio data until end() is called
44222 
44223 
44224           adtsFrames.push(data);
44225         };
44226 
44227         this.setEarliestDts = function (earliestDts) {
44228           earliestAllowedDts = earliestDts - track.timelineStartInfo.baseMediaDecodeTime;
44229         };
44230 
44231         this.setVideoBaseMediaDecodeTime = function (baseMediaDecodeTime) {
44232           videoBaseMediaDecodeTime = baseMediaDecodeTime;
44233         };
44234 
44235         this.setAudioAppendStart = function (timestamp) {
44236           audioAppendStartTs = timestamp;
44237         };
44238 
44239         this.flush = function () {
44240           var frames, moof, mdat, boxes, frameDuration; // return early if no audio data has been observed
44241 
44242           if (adtsFrames.length === 0) {
44243             this.trigger('done', 'AudioSegmentStream');
44244             return;
44245           }
44246 
44247           frames = audioFrameUtils.trimAdtsFramesByEarliestDts(adtsFrames, track, earliestAllowedDts);
44248           track.baseMediaDecodeTime = trackDecodeInfo.calculateTrackBaseMediaDecodeTime(track, options.keepOriginalTimestamps);
44249           audioFrameUtils.prefixWithSilence(track, frames, audioAppendStartTs, videoBaseMediaDecodeTime); // we have to build the index from byte locations to
44250           // samples (that is, adts frames) in the audio data
44251 
44252           track.samples = audioFrameUtils.generateSampleTable(frames); // concatenate the audio data to constuct the mdat
44253 
44254           mdat = mp4Generator.mdat(audioFrameUtils.concatenateFrameData(frames));
44255           adtsFrames = [];
44256           moof = mp4Generator.moof(sequenceNumber, [track]);
44257           boxes = new Uint8Array(moof.byteLength + mdat.byteLength); // bump the sequence number for next time
44258 
44259           sequenceNumber++;
44260           boxes.set(moof);
44261           boxes.set(mdat, moof.byteLength);
44262           trackDecodeInfo.clearDtsInfo(track);
44263           frameDuration = Math.ceil(ONE_SECOND_IN_TS$3 * 1024 / track.samplerate); // TODO this check was added to maintain backwards compatibility (particularly with
44264           // tests) on adding the timingInfo event. However, it seems unlikely that there's a
44265           // valid use-case where an init segment/data should be triggered without associated
44266           // frames. Leaving for now, but should be looked into.
44267 
44268           if (frames.length) {
44269             this.trigger('timingInfo', {
44270               start: frames[0].dts,
44271               end: frames[0].dts + frames.length * frameDuration
44272             });
44273           }
44274 
44275           this.trigger('data', {
44276             track: track,
44277             boxes: boxes
44278           });
44279           this.trigger('done', 'AudioSegmentStream');
44280         };
44281 
44282         this.reset = function () {
44283           trackDecodeInfo.clearDtsInfo(track);
44284           adtsFrames = [];
44285           this.trigger('reset');
44286         };
44287       };
44288 
44289       _AudioSegmentStream.prototype = new stream();
44290       /**
44291        * Constructs a single-track, ISO BMFF media segment from H264 data
44292        * events. The output of this stream can be fed to a SourceBuffer
44293        * configured with a suitable initialization segment.
44294        * @param track {object} track metadata configuration
44295        * @param options {object} transmuxer options object
44296        * @param options.alignGopsAtEnd {boolean} If true, start from the end of the
44297        *        gopsToAlignWith list when attempting to align gop pts
44298        * @param options.keepOriginalTimestamps {boolean} If true, keep the timestamps
44299        *        in the source; false to adjust the first segment to start at 0.
44300        */
44301 
44302       _VideoSegmentStream = function VideoSegmentStream(track, options) {
44303         var sequenceNumber = 0,
44304             nalUnits = [],
44305             gopsToAlignWith = [],
44306             config,
44307             pps;
44308         options = options || {};
44309 
44310         _VideoSegmentStream.prototype.init.call(this);
44311 
44312         delete track.minPTS;
44313         this.gopCache_ = [];
44314         /**
44315           * Constructs a ISO BMFF segment given H264 nalUnits
44316           * @param {Object} nalUnit A data event representing a nalUnit
44317           * @param {String} nalUnit.nalUnitType
44318           * @param {Object} nalUnit.config Properties for a mp4 track
44319           * @param {Uint8Array}
vendor: 12,289 bytes, lines 44319-44625
44319 nalUnit.data The nalUnit bytes
44320           * @see lib/codecs/h264.js
44321          **/
44322 
44323         this.push = function (nalUnit) {
44324           trackDecodeInfo.collectDtsInfo(track, nalUnit); // record the track config
44325 
44326           if (nalUnit.nalUnitType === 'seq_parameter_set_rbsp' && !config) {
44327             config = nalUnit.config;
44328             track.sps = [nalUnit.data];
44329             VIDEO_PROPERTIES.forEach(function (prop) {
44330               track[prop] = config[prop];
44331             }, this);
44332           }
44333 
44334           if (nalUnit.nalUnitType === 'pic_parameter_set_rbsp' && !pps) {
44335             pps = nalUnit.data;
44336             track.pps = [nalUnit.data];
44337           } // buffer video until flush() is called
44338 
44339 
44340           nalUnits.push(nalUnit);
44341         };
44342         /**
44343           * Pass constructed ISO BMFF track and boxes on to the
44344           * next stream in the pipeline
44345          **/
44346 
44347 
44348         this.flush = function () {
44349           var frames,
44350               gopForFusion,
44351               gops,
44352               moof,
44353               mdat,
44354               boxes,
44355               prependedContentDuration = 0,
44356               firstGop,
44357               lastGop; // Throw away nalUnits at the start of the byte stream until
44358           // we find the first AUD
44359 
44360           while (nalUnits.length) {
44361             if (nalUnits[0].nalUnitType === 'access_unit_delimiter_rbsp') {
44362               break;
44363             }
44364 
44365             nalUnits.shift();
44366           } // Return early if no video data has been observed
44367 
44368 
44369           if (nalUnits.length === 0) {
44370             this.resetStream_();
44371             this.trigger('done', 'VideoSegmentStream');
44372             return;
44373           } // Organize the raw nal-units into arrays that represent
44374           // higher-level constructs such as frames and gops
44375           // (group-of-pictures)
44376 
44377 
44378           frames = frameUtils.groupNalsIntoFrames(nalUnits);
44379           gops = frameUtils.groupFramesIntoGops(frames); // If the first frame of this fragment is not a keyframe we have
44380           // a problem since MSE (on Chrome) requires a leading keyframe.
44381           //
44382           // We have two approaches to repairing this situation:
44383           // 1) GOP-FUSION:
44384           //    This is where we keep track of the GOPS (group-of-pictures)
44385           //    from previous fragments and attempt to find one that we can
44386           //    prepend to the current fragment in order to create a valid
44387           //    fragment.
44388           // 2) KEYFRAME-PULLING:
44389           //    Here we search for the first keyframe in the fragment and
44390           //    throw away all the frames between the start of the fragment
44391           //    and that keyframe. We then extend the duration and pull the
44392           //    PTS of the keyframe forward so that it covers the time range
44393           //    of the frames that were disposed of.
44394           //
44395           // #1 is far prefereable over #2 which can cause "stuttering" but
44396           // requires more things to be just right.
44397 
44398           if (!gops[0][0].keyFrame) {
44399             // Search for a gop for fusion from our gopCache
44400             gopForFusion = this.getGopForFusion_(nalUnits[0], track);
44401 
44402             if (gopForFusion) {
44403               // in order to provide more accurate timing information about the segment, save
44404               // the number of seconds prepended to the original segment due to GOP fusion
44405               prependedContentDuration = gopForFusion.duration;
44406               gops.unshift(gopForFusion); // Adjust Gops' metadata to account for the inclusion of the
44407               // new gop at the beginning
44408 
44409               gops.byteLength += gopForFusion.byteLength;
44410               gops.nalCount += gopForFusion.nalCount;
44411               gops.pts = gopForFusion.pts;
44412               gops.dts = gopForFusion.dts;
44413               gops.duration += gopForFusion.duration;
44414             } else {
44415               // If we didn't find a candidate gop fall back to keyframe-pulling
44416               gops = frameUtils.extendFirstKeyFrame(gops);
44417             }
44418           } // Trim gops to align with gopsToAlignWith
44419 
44420 
44421           if (gopsToAlignWith.length) {
44422             var alignedGops;
44423 
44424             if (options.alignGopsAtEnd) {
44425               alignedGops = this.alignGopsAtEnd_(gops);
44426             } else {
44427               alignedGops = this.alignGopsAtStart_(gops);
44428             }
44429 
44430             if (!alignedGops) {
44431               // save all the nals in the last GOP into the gop cache
44432               this.gopCache_.unshift({
44433                 gop: gops.pop(),
44434                 pps: track.pps,
44435                 sps: track.sps
44436               }); // Keep a maximum of 6 GOPs in the cache
44437 
44438               this.gopCache_.length = Math.min(6, this.gopCache_.length); // Clear nalUnits
44439 
44440               nalUnits = []; // return early no gops can be aligned with desired gopsToAlignWith
44441 
44442               this.resetStream_();
44443               this.trigger('done', 'VideoSegmentStream');
44444               return;
44445             } // Some gops were trimmed. clear dts info so minSegmentDts and pts are correct
44446             // when recalculated before sending off to CoalesceStream
44447 
44448 
44449             trackDecodeInfo.clearDtsInfo(track);
44450             gops = alignedGops;
44451           }
44452 
44453           trackDecodeInfo.collectDtsInfo(track, gops); // First, we have to build the index from byte locations to
44454           // samples (that is, frames) in the video data
44455 
44456           track.samples = frameUtils.generateSampleTable(gops); // Concatenate the video data and construct the mdat
44457 
44458           mdat = mp4Generator.mdat(frameUtils.concatenateNalData(gops));
44459           track.baseMediaDecodeTime = trackDecodeInfo.calculateTrackBaseMediaDecodeTime(track, options.keepOriginalTimestamps);
44460           this.trigger('processedGopsInfo', gops.map(function (gop) {
44461             return {
44462               pts: gop.pts,
44463               dts: gop.dts,
44464               byteLength: gop.byteLength
44465             };
44466           }));
44467           firstGop = gops[0];
44468           lastGop = gops[gops.length - 1];
44469           this.trigger('segmentTimingInfo', generateVideoSegmentTimingInfo(track.baseMediaDecodeTime, firstGop.dts, firstGop.pts, lastGop.dts + lastGop.duration, lastGop.pts + lastGop.duration, prependedContentDuration));
44470           this.trigger('timingInfo', {
44471             start: gops[0].dts,
44472             end: gops[gops.length - 1].dts + gops[gops.length - 1].duration
44473           }); // save all the nals in the last GOP into the gop cache
44474 
44475           this.gopCache_.unshift({
44476             gop: gops.pop(),
44477             pps: track.pps,
44478             sps: track.sps
44479           }); // Keep a maximum of 6 GOPs in the cache
44480 
44481           this.gopCache_.length = Math.min(6, this.gopCache_.length); // Clear nalUnits
44482 
44483           nalUnits = [];
44484           this.trigger('baseMediaDecodeTime', track.baseMediaDecodeTime);
44485           this.trigger('timelineStartInfo', track.timelineStartInfo);
44486           moof = mp4Generator.moof(sequenceNumber, [track]); // it would be great to allocate this array up front instead of
44487           // throwing away hundreds of media segment fragments
44488 
44489           boxes = new Uint8Array(moof.byteLength + mdat.byteLength); // Bump the sequence number for next time
44490 
44491           sequenceNumber++;
44492           boxes.set(moof);
44493           boxes.set(mdat, moof.byteLength);
44494           this.trigger('data', {
44495             track: track,
44496             boxes: boxes
44497           });
44498           this.resetStream_(); // Continue with the flush process now
44499 
44500           this.trigger('done', 'VideoSegmentStream');
44501         };
44502 
44503         this.reset = function () {
44504           this.resetStream_();
44505           nalUnits = [];
44506           this.gopCache_.length = 0;
44507           gopsToAlignWith.length = 0;
44508           this.trigger('reset');
44509         };
44510 
44511         this.resetStream_ = function () {
44512           trackDecodeInfo.clearDtsInfo(track); // reset config and pps because they may differ across segments
44513           // for instance, when we are rendition switching
44514 
44515           config = undefined;
44516           pps = undefined;
44517         }; // Search for a candidate Gop for gop-fusion from the gop cache and
44518         // return it or return null if no good candidate was found
44519 
44520 
44521         this.getGopForFusion_ = function (nalUnit) {
44522           var halfSecond = 45000,
44523               // Half-a-second in a 90khz clock
44524           allowableOverlap = 10000,
44525               // About 3 frames @ 30fps
44526           nearestDistance = Infinity,
44527               dtsDistance,
44528               nearestGopObj,
44529               currentGop,
44530               currentGopObj,
44531               i; // Search for the GOP nearest to the beginning of this nal unit
44532 
44533           for (i = 0; i < this.gopCache_.length; i++) {
44534             currentGopObj = this.gopCache_[i];
44535             currentGop = currentGopObj.gop; // Reject Gops with different SPS or PPS
44536 
44537             if (!(track.pps && arrayEquals(track.pps[0], currentGopObj.pps[0])) || !(track.sps && arrayEquals(track.sps[0], currentGopObj.sps[0]))) {
44538               continue;
44539             } // Reject Gops that would require a negative baseMediaDecodeTime
44540 
44541 
44542             if (currentGop.dts < track.timelineStartInfo.dts) {
44543               continue;
44544             } // The distance between the end of the gop and the start of the nalUnit
44545 
44546 
44547             dtsDistance = nalUnit.dts - currentGop.dts - currentGop.duration; // Only consider GOPS that start before the nal unit and end within
44548             // a half-second of the nal unit
44549 
44550             if (dtsDistance >= -allowableOverlap && dtsDistance <= halfSecond) {
44551               // Always use the closest GOP we found if there is more than
44552               // one candidate
44553               if (!nearestGopObj || nearestDistance > dtsDistance) {
44554                 nearestGopObj = currentGopObj;
44555                 nearestDistance = dtsDistance;
44556               }
44557             }
44558           }
44559 
44560           if (nearestGopObj) {
44561             return nearestGopObj.gop;
44562           }
44563 
44564           return null;
44565         }; // trim gop list to the first gop found that has a matching pts with a gop in the list
44566         // of gopsToAlignWith starting from the START of the list
44567 
44568 
44569         this.alignGopsAtStart_ = function (gops) {
44570           var alignIndex, gopIndex, align, gop, byteLength, nalCount, duration, alignedGops;
44571           byteLength = gops.byteLength;
44572           nalCount = gops.nalCount;
44573           duration = gops.duration;
44574           alignIndex = gopIndex = 0;
44575 
44576           while (alignIndex < gopsToAlignWith.length && gopIndex < gops.length) {
44577             align = gopsToAlignWith[alignIndex];
44578             gop = gops[gopIndex];
44579 
44580             if (align.pts === gop.pts) {
44581               break;
44582             }
44583 
44584             if (gop.pts > align.pts) {
44585               // this current gop starts after the current gop we want to align on, so increment
44586               // align index
44587               alignIndex++;
44588               continue;
44589             } // current gop starts before the current gop we want to align on. so increment gop
44590             // index
44591 
44592 
44593             gopIndex++;
44594             byteLength -= gop.byteLength;
44595             nalCount -= gop.nalCount;
44596             duration -= gop.duration;
44597           }
44598 
44599           if (gopIndex === 0) {
44600             // no gops to trim
44601             return gops;
44602           }
44603 
44604           if (gopIndex === gops.length) {
44605             // all gops trimmed, skip appending all gops
44606             return null;
44607           }
44608 
44609           alignedGops = gops.slice(gopIndex);
44610           alignedGops.byteLength = byteLength;
44611           alignedGops.duration = duration;
44612           alignedGops.nalCount = nalCount;
44613           alignedGops.pts = alignedGops[0].pts;
44614           alignedGops.dts = alignedGops[0].dts;
44615           return alignedGops;
44616         }; // trim gop list to the first gop found that has a matching pts with a gop in the list
44617         // of gopsToAlignWith starting from the END of the list
44618 
44619 
44620         this.alignGopsAtEnd_ = function (gops) {
44621           var alignIndex, gopIndex, align, gop, alignEndIndex, matchFound;
44622           alignIndex = gopsToAlignWith.length - 1;
44623           gopIndex = gops.length - 1;
44624           alignEndIndex = null;
44625           matchFound = false;
vendor: 4,995 bytes, lines 44626-44772
44626 
44627           while (alignIndex >= 0 && gopIndex >= 0) {
44628             align = gopsToAlignWith[alignIndex];
44629             gop = gops[gopIndex];
44630 
44631             if (align.pts === gop.pts) {
44632               matchFound = true;
44633               break;
44634             }
44635 
44636             if (align.pts > gop.pts) {
44637               alignIndex--;
44638               continue;
44639             }
44640 
44641             if (alignIndex === gopsToAlignWith.length - 1) {
44642               // gop.pts is greater than the last alignment candidate. If no match is found
44643               // by the end of this loop, we still want to append gops that come after this
44644               // point
44645               alignEndIndex = gopIndex;
44646             }
44647 
44648             gopIndex--;
44649           }
44650 
44651           if (!matchFound && alignEndIndex === null) {
44652             return null;
44653           }
44654 
44655           var trimIndex;
44656 
44657           if (matchFound) {
44658             trimIndex = gopIndex;
44659           } else {
44660             trimIndex = alignEndIndex;
44661           }
44662 
44663           if (trimIndex === 0) {
44664             return gops;
44665           }
44666 
44667           var alignedGops = gops.slice(trimIndex);
44668           var metadata = alignedGops.reduce(function (total, gop) {
44669             total.byteLength += gop.byteLength;
44670             total.duration += gop.duration;
44671             total.nalCount += gop.nalCount;
44672             return total;
44673           }, {
44674             byteLength: 0,
44675             duration: 0,
44676             nalCount: 0
44677           });
44678           alignedGops.byteLength = metadata.byteLength;
44679           alignedGops.duration = metadata.duration;
44680           alignedGops.nalCount = metadata.nalCount;
44681           alignedGops.pts = alignedGops[0].pts;
44682           alignedGops.dts = alignedGops[0].dts;
44683           return alignedGops;
44684         };
44685 
44686         this.alignGopsWith = function (newGopsToAlignWith) {
44687           gopsToAlignWith = newGopsToAlignWith;
44688         };
44689       };
44690 
44691       _VideoSegmentStream.prototype = new stream();
44692       /**
44693        * A Stream that can combine multiple streams (ie. audio & video)
44694        * into a single output segment for MSE. Also supports audio-only
44695        * and video-only streams.
44696        * @param options {object} transmuxer options object
44697        * @param options.keepOriginalTimestamps {boolean} If true, keep the timestamps
44698        *        in the source; false to adjust the first segment to start at media timeline start.
44699        */
44700 
44701       _CoalesceStream = function CoalesceStream(options, metadataStream) {
44702         // Number of Tracks per output segment
44703         // If greater than 1, we combine multiple
44704         // tracks into a single segment
44705         this.numberOfTracks = 0;
44706         this.metadataStream = metadataStream;
44707         options = options || {};
44708 
44709         if (typeof options.remux !== 'undefined') {
44710           this.remuxTracks = !!options.remux;
44711         } else {
44712           this.remuxTracks = true;
44713         }
44714 
44715         if (typeof options.keepOriginalTimestamps === 'boolean') {
44716           this.keepOriginalTimestamps = options.keepOriginalTimestamps;
44717         } else {
44718           this.keepOriginalTimestamps = false;
44719         }
44720 
44721         this.pendingTracks = [];
44722         this.videoTrack = null;
44723         this.pendingBoxes = [];
44724         this.pendingCaptions = [];
44725         this.pendingMetadata = [];
44726         this.pendingBytes = 0;
44727         this.emittedTracks = 0;
44728 
44729         _CoalesceStream.prototype.init.call(this); // Take output from multiple
44730 
44731 
44732         this.push = function (output) {
44733           // buffer incoming captions until the associated video segment
44734           // finishes
44735           if (output.text) {
44736             return this.pendingCaptions.push(output);
44737           } // buffer incoming id3 tags until the final flush
44738 
44739 
44740           if (output.frames) {
44741             return this.pendingMetadata.push(output);
44742           } // Add this track to the list of pending tracks and store
44743           // important information required for the construction of
44744           // the final segment
44745 
44746 
44747           this.pendingTracks.push(output.track);
44748           this.pendingBytes += output.boxes.byteLength; // TODO: is there an issue for this against chrome?
44749           // We unshift audio and push video because
44750           // as of Chrome 75 when switching from
44751           // one init segment to another if the video
44752           // mdat does not appear after the audio mdat
44753           // only audio will play for the duration of our transmux.
44754 
44755           if (output.track.type === 'video') {
44756             this.videoTrack = output.track;
44757             this.pendingBoxes.push(output.boxes);
44758           }
44759 
44760           if (output.track.type === 'audio') {
44761             this.audioTrack = output.track;
44762             this.pendingBoxes.unshift(output.boxes);
44763           }
44764         };
44765       };
44766 
44767       _CoalesceStream.prototype = new stream();
44768 
44769       _CoalesceStream.prototype.flush = function (flushSource) {
44770         var offset = 0,
44771             event = {
44772           captions: [],
vendor: 4,830 bytes, lines 44773-44878
44773           captionStreams: {},
44774           metadata: [],
44775           info: {}
44776         },
44777             caption,
44778             id3,
44779             initSegment,
44780             timelineStartPts = 0,
44781             i;
44782 
44783         if (this.pendingTracks.length < this.numberOfTracks) {
44784           if (flushSource !== 'VideoSegmentStream' && flushSource !== 'AudioSegmentStream') {
44785             // Return because we haven't received a flush from a data-generating
44786             // portion of the segment (meaning that we have only recieved meta-data
44787             // or captions.)
44788             return;
44789           } else if (this.remuxTracks) {
44790             // Return until we have enough tracks from the pipeline to remux (if we
44791             // are remuxing audio and video into a single MP4)
44792             return;
44793           } else if (this.pendingTracks.length === 0) {
44794             // In the case where we receive a flush without any data having been
44795             // received we consider it an emitted track for the purposes of coalescing
44796             // `done` events.
44797             // We do this for the case where there is an audio and video track in the
44798             // segment but no audio data. (seen in several playlists with alternate
44799             // audio tracks and no audio present in the main TS segments.)
44800             this.emittedTracks++;
44801 
44802             if (this.emittedTracks >= this.numberOfTracks) {
44803               this.trigger('done');
44804               this.emittedTracks = 0;
44805             }
44806 
44807             return;
44808           }
44809         }
44810 
44811         if (this.videoTrack) {
44812           timelineStartPts = this.videoTrack.timelineStartInfo.pts;
44813           VIDEO_PROPERTIES.forEach(function (prop) {
44814             event.info[prop] = this.videoTrack[prop];
44815           }, this);
44816         } else if (this.audioTrack) {
44817           timelineStartPts = this.audioTrack.timelineStartInfo.pts;
44818           AUDIO_PROPERTIES.forEach(function (prop) {
44819             event.info[prop] = this.audioTrack[prop];
44820           }, this);
44821         }
44822 
44823         if (this.videoTrack || this.audioTrack) {
44824           if (this.pendingTracks.length === 1) {
44825             event.type = this.pendingTracks[0].type;
44826           } else {
44827             event.type = 'combined';
44828           }
44829 
44830           this.emittedTracks += this.pendingTracks.length;
44831           initSegment = mp4Generator.initSegment(this.pendingTracks); // Create a new typed array to hold the init segment
44832 
44833           event.initSegment = new Uint8Array(initSegment.byteLength); // Create an init segment containing a moov
44834           // and track definitions
44835 
44836           event.initSegment.set(initSegment); // Create a new typed array to hold the moof+mdats
44837 
44838           event.data = new Uint8Array(this.pendingBytes); // Append each moof+mdat (one per track) together
44839 
44840           for (i = 0; i < this.pendingBoxes.length; i++) {
44841             event.data.set(this.pendingBoxes[i], offset);
44842             offset += this.pendingBoxes[i].byteLength;
44843           } // Translate caption PTS times into second offsets to match the
44844           // video timeline for the segment, and add track info
44845 
44846 
44847           for (i = 0; i < this.pendingCaptions.length; i++) {
44848             caption = this.pendingCaptions[i];
44849             caption.startTime = clock.metadataTsToSeconds(caption.startPts, timelineStartPts, this.keepOriginalTimestamps);
44850             caption.endTime = clock.metadataTsToSeconds(caption.endPts, timelineStartPts, this.keepOriginalTimestamps);
44851             event.captionStreams[caption.stream] = true;
44852             event.captions.push(caption);
44853           } // Translate ID3 frame PTS times into second offsets to match the
44854           // video timeline for the segment
44855 
44856 
44857           for (i = 0; i < this.pendingMetadata.length; i++) {
44858             id3 = this.pendingMetadata[i];
44859             id3.cueTime = clock.metadataTsToSeconds(id3.pts, timelineStartPts, this.keepOriginalTimestamps);
44860             event.metadata.push(id3);
44861           } // We add this to every single emitted segment even though we only need
44862           // it for the first
44863 
44864 
44865           event.metadata.dispatchType = this.metadataStream.dispatchType; // Reset stream state
44866 
44867           this.pendingTracks.length = 0;
44868           this.videoTrack = null;
44869           this.pendingBoxes.length = 0;
44870           this.pendingCaptions.length = 0;
44871           this.pendingBytes = 0;
44872           this.pendingMetadata.length = 0; // Emit the built segment
44873           // We include captions and ID3 tags for backwards compatibility,
44874           // ideally we should send only video and audio in the data event
44875 
44876           this.trigger('data', event); // Emit each caption to the outside world
44877           // Ideally, this would happen immediately on parsing captions,
44878           // but we need to ensure that video data is sent back first
vendor: 12,292 bytes, lines 44879-45142
44879           // so that caption timing can be adjusted to match video timing
44880 
44881           for (i = 0; i < event.captions.length; i++) {
44882             caption = event.captions[i];
44883             this.trigger('caption', caption);
44884           } // Emit each id3 tag to the outside world
44885           // Ideally, this would happen immediately on parsing the tag,
44886           // but we need to ensure that video data is sent back first
44887           // so that ID3 frame timing can be adjusted to match video timing
44888 
44889 
44890           for (i = 0; i < event.metadata.length; i++) {
44891             id3 = event.metadata[i];
44892             this.trigger('id3Frame', id3);
44893           }
44894         } // Only emit `done` if all tracks have been flushed and emitted
44895 
44896 
44897         if (this.emittedTracks >= this.numberOfTracks) {
44898           this.trigger('done');
44899           this.emittedTracks = 0;
44900         }
44901       };
44902 
44903       _CoalesceStream.prototype.setRemux = function (val) {
44904         this.remuxTracks = val;
44905       };
44906       /**
44907        * A Stream that expects MP2T binary data as input and produces
44908        * corresponding media segments, suitable for use with Media Source
44909        * Extension (MSE) implementations that support the ISO BMFF byte
44910        * stream format, like Chrome.
44911        */
44912 
44913 
44914       _Transmuxer = function Transmuxer(options) {
44915         var self = this,
44916             hasFlushed = true,
44917             videoTrack,
44918             audioTrack;
44919 
44920         _Transmuxer.prototype.init.call(this);
44921 
44922         options = options || {};
44923         this.baseMediaDecodeTime = options.baseMediaDecodeTime || 0;
44924         this.transmuxPipeline_ = {};
44925 
44926         this.setupAacPipeline = function () {
44927           var pipeline = {};
44928           this.transmuxPipeline_ = pipeline;
44929           pipeline.type = 'aac';
44930           pipeline.metadataStream = new m2ts_1.MetadataStream(); // set up the parsing pipeline
44931 
44932           pipeline.aacStream = new aac();
44933           pipeline.audioTimestampRolloverStream = new m2ts_1.TimestampRolloverStream('audio');
44934           pipeline.timedMetadataTimestampRolloverStream = new m2ts_1.TimestampRolloverStream('timed-metadata');
44935           pipeline.adtsStream = new adts();
44936           pipeline.coalesceStream = new _CoalesceStream(options, pipeline.metadataStream);
44937           pipeline.headOfPipeline = pipeline.aacStream;
44938           pipeline.aacStream.pipe(pipeline.audioTimestampRolloverStream).pipe(pipeline.adtsStream);
44939           pipeline.aacStream.pipe(pipeline.timedMetadataTimestampRolloverStream).pipe(pipeline.metadataStream).pipe(pipeline.coalesceStream);
44940           pipeline.metadataStream.on('timestamp', function (frame) {
44941             pipeline.aacStream.setTimestamp(frame.timeStamp);
44942           });
44943           pipeline.aacStream.on('data', function (data) {
44944             if (data.type === 'timed-metadata' && !pipeline.audioSegmentStream) {
44945               audioTrack = audioTrack || {
44946                 timelineStartInfo: {
44947                   baseMediaDecodeTime: self.baseMediaDecodeTime
44948                 },
44949                 codec: 'adts',
44950                 type: 'audio'
44951               }; // hook up the audio segment stream to the first track with aac data
44952 
44953               pipeline.coalesceStream.numberOfTracks++;
44954               pipeline.audioSegmentStream = new _AudioSegmentStream(audioTrack, options);
44955               pipeline.audioSegmentStream.on('timingInfo', self.trigger.bind(self, 'audioTimingInfo')); // Set up the final part of the audio pipeline
44956 
44957               pipeline.adtsStream.pipe(pipeline.audioSegmentStream).pipe(pipeline.coalesceStream);
44958             } // emit pmt info
44959 
44960 
44961             self.trigger('trackinfo', {
44962               hasAudio: !!audioTrack,
44963               hasVideo: !!videoTrack
44964             });
44965           }); // Re-emit any data coming from the coalesce stream to the outside world
44966 
44967           pipeline.coalesceStream.on('data', this.trigger.bind(this, 'data')); // Let the consumer know we have finished flushing the entire pipeline
44968 
44969           pipeline.coalesceStream.on('done', this.trigger.bind(this, 'done'));
44970         };
44971 
44972         this.setupTsPipeline = function () {
44973           var pipeline = {};
44974           this.transmuxPipeline_ = pipeline;
44975           pipeline.type = 'ts';
44976           pipeline.metadataStream = new m2ts_1.MetadataStream(); // set up the parsing pipeline
44977 
44978           pipeline.packetStream = new m2ts_1.TransportPacketStream();
44979           pipeline.parseStream = new m2ts_1.TransportParseStream();
44980           pipeline.elementaryStream = new m2ts_1.ElementaryStream();
44981           pipeline.timestampRolloverStream = new m2ts_1.TimestampRolloverStream();
44982           pipeline.adtsStream = new adts();
44983           pipeline.h264Stream = new H264Stream();
44984           pipeline.captionStream = new m2ts_1.CaptionStream();
44985           pipeline.coalesceStream = new _CoalesceStream(options, pipeline.metadataStream);
44986           pipeline.headOfPipeline = pipeline.packetStream; // disassemble MPEG2-TS packets into elementary streams
44987 
44988           pipeline.packetStream.pipe(pipeline.parseStream).pipe(pipeline.elementaryStream).pipe(pipeline.timestampRolloverStream); // !!THIS ORDER IS IMPORTANT!!
44989           // demux the streams
44990 
44991           pipeline.timestampRolloverStream.pipe(pipeline.h264Stream);
44992           pipeline.timestampRolloverStream.pipe(pipeline.adtsStream);
44993           pipeline.timestampRolloverStream.pipe(pipeline.metadataStream).pipe(pipeline.coalesceStream); // Hook up CEA-608/708 caption stream
44994 
44995           pipeline.h264Stream.pipe(pipeline.captionStream).pipe(pipeline.coalesceStream);
44996           pipeline.elementaryStream.on('data', function (data) {
44997             var i;
44998 
44999             if (data.type === 'metadata') {
45000               i = data.tracks.length; // scan the tracks listed in the metadata
45001 
45002               while (i--) {
45003                 if (!videoTrack && data.tracks[i].type === 'video') {
45004                   videoTrack = data.tracks[i];
45005                   videoTrack.timelineStartInfo.baseMediaDecodeTime = self.baseMediaDecodeTime;
45006                 } else if (!audioTrack && data.tracks[i].type === 'audio') {
45007                   audioTrack = data.tracks[i];
45008                   audioTrack.timelineStartInfo.baseMediaDecodeTime = self.baseMediaDecodeTime;
45009                 }
45010               } // hook up the video segment stream to the first track with h264 data
45011 
45012 
45013               if (videoTrack && !pipeline.videoSegmentStream) {
45014                 pipeline.coalesceStream.numberOfTracks++;
45015                 pipeline.videoSegmentStream = new _VideoSegmentStream(videoTrack, options);
45016                 pipeline.videoSegmentStream.on('timelineStartInfo', function (timelineStartInfo) {
45017                   // When video emits timelineStartInfo data after a flush, we forward that
45018                   // info to the AudioSegmentStream, if it exists, because video timeline
45019                   // data takes precedence.
45020                   if (audioTrack) {
45021                     audioTrack.timelineStartInfo = timelineStartInfo; // On the first segment we trim AAC frames that exist before the
45022                     // very earliest DTS we have seen in video because Chrome will
45023                     // interpret any video track with a baseMediaDecodeTime that is
45024                     // non-zero as a gap.
45025 
45026                     pipeline.audioSegmentStream.setEarliestDts(timelineStartInfo.dts);
45027                   }
45028                 });
45029                 pipeline.videoSegmentStream.on('processedGopsInfo', self.trigger.bind(self, 'gopInfo'));
45030                 pipeline.videoSegmentStream.on('segmentTimingInfo', self.trigger.bind(self, 'videoSegmentTimingInfo'));
45031                 pipeline.videoSegmentStream.on('baseMediaDecodeTime', function (baseMediaDecodeTime) {
45032                   if (audioTrack) {
45033                     pipeline.audioSegmentStream.setVideoBaseMediaDecodeTime(baseMediaDecodeTime);
45034                   }
45035                 });
45036                 pipeline.videoSegmentStream.on('timingInfo', self.trigger.bind(self, 'videoTimingInfo')); // Set up the final part of the video pipeline
45037 
45038                 pipeline.h264Stream.pipe(pipeline.videoSegmentStream).pipe(pipeline.coalesceStream);
45039               }
45040 
45041               if (audioTrack && !pipeline.audioSegmentStream) {
45042                 // hook up the audio segment stream to the first track with aac data
45043                 pipeline.coalesceStream.numberOfTracks++;
45044                 pipeline.audioSegmentStream = new _AudioSegmentStream(audioTrack, options);
45045                 pipeline.audioSegmentStream.on('timingInfo', self.trigger.bind(self, 'audioTimingInfo')); // Set up the final part of the audio pipeline
45046 
45047                 pipeline.adtsStream.pipe(pipeline.audioSegmentStream).pipe(pipeline.coalesceStream);
45048               } // emit pmt info
45049 
45050 
45051               self.trigger('trackinfo', {
45052                 hasAudio: !!audioTrack,
45053                 hasVideo: !!videoTrack
45054               });
45055             }
45056           }); // Re-emit any data coming from the coalesce stream to the outside world
45057 
45058           pipeline.coalesceStream.on('data', this.trigger.bind(this, 'data'));
45059           pipeline.coalesceStream.on('id3Frame', function (id3Frame) {
45060             id3Frame.dispatchType = pipeline.metadataStream.dispatchType;
45061             self.trigger('id3Frame', id3Frame);
45062           });
45063           pipeline.coalesceStream.on('caption', this.trigger.bind(this, 'caption')); // Let the consumer know we have finished flushing the entire pipeline
45064 
45065           pipeline.coalesceStream.on('done', this.trigger.bind(this, 'done'));
45066         }; // hook up the segment streams once track metadata is delivered
45067 
45068 
45069         this.setBaseMediaDecodeTime = function (baseMediaDecodeTime) {
45070           var pipeline = this.transmuxPipeline_;
45071 
45072           if (!options.keepOriginalTimestamps) {
45073             this.baseMediaDecodeTime = baseMediaDecodeTime;
45074           }
45075 
45076           if (audioTrack) {
45077             audioTrack.timelineStartInfo.dts = undefined;
45078             audioTrack.timelineStartInfo.pts = undefined;
45079             trackDecodeInfo.clearDtsInfo(audioTrack);
45080 
45081             if (!options.keepOriginalTimestamps) {
45082               audioTrack.timelineStartInfo.baseMediaDecodeTime = baseMediaDecodeTime;
45083             }
45084 
45085             if (pipeline.audioTimestampRolloverStream) {
45086               pipeline.audioTimestampRolloverStream.discontinuity();
45087             }
45088           }
45089 
45090           if (videoTrack) {
45091             if (pipeline.videoSegmentStream) {
45092               pipeline.videoSegmentStream.gopCache_ = [];
45093             }
45094 
45095             videoTrack.timelineStartInfo.dts = undefined;
45096             videoTrack.timelineStartInfo.pts = undefined;
45097             trackDecodeInfo.clearDtsInfo(videoTrack);
45098             pipeline.captionStream.reset();
45099 
45100             if (!options.keepOriginalTimestamps) {
45101               videoTrack.timelineStartInfo.baseMediaDecodeTime = baseMediaDecodeTime;
45102             }
45103           }
45104 
45105           if (pipeline.timestampRolloverStream) {
45106             pipeline.timestampRolloverStream.discontinuity();
45107           }
45108         };
45109 
45110         this.setAudioAppendStart = function (timestamp) {
45111           if (audioTrack) {
45112             this.transmuxPipeline_.audioSegmentStream.setAudioAppendStart(timestamp);
45113           }
45114         };
45115 
45116         this.setRemux = function (val) {
45117           var pipeline = this.transmuxPipeline_;
45118           options.remux = val;
45119 
45120           if (pipeline && pipeline.coalesceStream) {
45121             pipeline.coalesceStream.setRemux(val);
45122           }
45123         };
45124 
45125         this.alignGopsWith = function (gopsToAlignWith) {
45126           if (videoTrack && this.transmuxPipeline_.videoSegmentStream) {
45127             this.transmuxPipeline_.videoSegmentStream.alignGopsWith(gopsToAlignWith);
45128           }
45129         }; // feed incoming data to the front of the parsing pipeline
45130 
45131 
45132         this.push = function (data) {
45133           if (hasFlushed) {
45134             var isAac = isLikelyAacData$1(data);
45135 
45136             if (isAac && this.transmuxPipeline_.type !== 'aac') {
45137               this.setupAacPipeline();
45138             } else if (!isAac && this.transmuxPipeline_.type !== 'ts') {
45139               this.setupTsPipeline();
45140             }
45141 
45142             hasFlushed = false;
vendor: 34,564 bytes, lines 45143-46172
45143           }
45144 
45145           this.transmuxPipeline_.headOfPipeline.push(data);
45146         }; // flush any buffered data
45147 
45148 
45149         this.flush = function () {
45150           hasFlushed = true; // Start at the top of the pipeline and flush all pending work
45151 
45152           this.transmuxPipeline_.headOfPipeline.flush();
45153         };
45154 
45155         this.endTimeline = function () {
45156           this.transmuxPipeline_.headOfPipeline.endTimeline();
45157         };
45158 
45159         this.reset = function () {
45160           if (this.transmuxPipeline_.headOfPipeline) {
45161             this.transmuxPipeline_.headOfPipeline.reset();
45162           }
45163         }; // Caption data has to be reset when seeking outside buffered range
45164 
45165 
45166         this.resetCaptions = function () {
45167           if (this.transmuxPipeline_.captionStream) {
45168             this.transmuxPipeline_.captionStream.reset();
45169           }
45170         };
45171       };
45172 
45173       _Transmuxer.prototype = new stream();
45174       var transmuxer = {
45175         Transmuxer: _Transmuxer,
45176         VideoSegmentStream: _VideoSegmentStream,
45177         AudioSegmentStream: _AudioSegmentStream,
45178         AUDIO_PROPERTIES: AUDIO_PROPERTIES,
45179         VIDEO_PROPERTIES: VIDEO_PROPERTIES,
45180         // exported for testing
45181         generateVideoSegmentTimingInfo: generateVideoSegmentTimingInfo
45182       };
45183 
45184       var classCallCheck = function classCallCheck(instance, Constructor) {
45185         if (!(instance instanceof Constructor)) {
45186           throw new TypeError("Cannot call a class as a function");
45187         }
45188       };
45189 
45190       var createClass = function () {
45191         function defineProperties(target, props) {
45192           for (var i = 0; i < props.length; i++) {
45193             var descriptor = props[i];
45194             descriptor.enumerable = descriptor.enumerable || false;
45195             descriptor.configurable = true;
45196             if ("value" in descriptor) descriptor.writable = true;
45197             Object.defineProperty(target, descriptor.key, descriptor);
45198           }
45199         }
45200 
45201         return function (Constructor, protoProps, staticProps) {
45202           if (protoProps) defineProperties(Constructor.prototype, protoProps);
45203           if (staticProps) defineProperties(Constructor, staticProps);
45204           return Constructor;
45205         };
45206       }();
45207       /**
45208        * @file transmuxer-worker.js
45209        */
45210 
45211       /**
45212        * Re-emits transmuxer events by converting them into messages to the
45213        * world outside the worker.
45214        *
45215        * @param {Object} transmuxer the transmuxer to wire events on
45216        * @private
45217        */
45218 
45219 
45220       var wireTransmuxerEvents = function wireTransmuxerEvents(self, transmuxer$$1) {
45221         transmuxer$$1.on('data', function (segment) {
45222           // transfer ownership of the underlying ArrayBuffer
45223           // instead of doing a copy to save memory
45224           // ArrayBuffers are transferable but generic TypedArrays are not
45225           // @link https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers#Passing_data_by_transferring_ownership_(transferable_objects)
45226           var initArray = segment.initSegment;
45227           segment.initSegment = {
45228             data: initArray.buffer,
45229             byteOffset: initArray.byteOffset,
45230             byteLength: initArray.byteLength
45231           };
45232           var typedArray = segment.data;
45233           segment.data = typedArray.buffer;
45234           self.postMessage({
45235             action: 'data',
45236             segment: segment,
45237             byteOffset: typedArray.byteOffset,
45238             byteLength: typedArray.byteLength
45239           }, [segment.data]);
45240         });
45241 
45242         if (transmuxer$$1.captionStream) {
45243           transmuxer$$1.captionStream.on('data', function (caption) {
45244             self.postMessage({
45245               action: 'caption',
45246               data: caption
45247             });
45248           });
45249         }
45250 
45251         transmuxer$$1.on('done', function (data) {
45252           self.postMessage({
45253             action: 'done'
45254           });
45255         });
45256         transmuxer$$1.on('gopInfo', function (gopInfo) {
45257           self.postMessage({
45258             action: 'gopInfo',
45259             gopInfo: gopInfo
45260           });
45261         });
45262         transmuxer$$1.on('videoSegmentTimingInfo', function (videoSegmentTimingInfo) {
45263           self.postMessage({
45264             action: 'videoSegmentTimingInfo',
45265             videoSegmentTimingInfo: videoSegmentTimingInfo
45266           });
45267         });
45268       };
45269       /**
45270        * All incoming messages route through this hash. If no function exists
45271        * to handle an incoming message, then we ignore the message.
45272        *
45273        * @class MessageHandlers
45274        * @param {Object} options the options to initialize with
45275        */
45276 
45277 
45278       var MessageHandlers = function () {
45279         function MessageHandlers(self, options) {
45280           classCallCheck(this, MessageHandlers);
45281           this.options = options || {};
45282           this.self = self;
45283           this.init();
45284         }
45285         /**
45286          * initialize our web worker and wire all the events.
45287          */
45288 
45289 
45290         createClass(MessageHandlers, [{
45291           key: 'init',
45292           value: function init() {
45293             if (this.transmuxer) {
45294               this.transmuxer.dispose();
45295             }
45296 
45297             this.transmuxer = new transmuxer.Transmuxer(this.options);
45298             wireTransmuxerEvents(this.self, this.transmuxer);
45299           }
45300           /**
45301            * Adds data (a ts segment) to the start of the transmuxer pipeline for
45302            * processing.
45303            *
45304            * @param {ArrayBuffer} data data to push into the muxer
45305            */
45306 
45307         }, {
45308           key: 'push',
45309           value: function push(data) {
45310             // Cast array buffer to correct type for transmuxer
45311             var segment = new Uint8Array(data.data, data.byteOffset, data.byteLength);
45312             this.transmuxer.push(segment);
45313           }
45314           /**
45315            * Recreate the transmuxer so that the next segment added via `push`
45316            * start with a fresh transmuxer.
45317            */
45318 
45319         }, {
45320           key: 'reset',
45321           value: function reset() {
45322             this.init();
45323           }
45324           /**
45325            * Set the value that will be used as the `baseMediaDecodeTime` time for the
45326            * next segment pushed in. Subsequent segments will have their `baseMediaDecodeTime`
45327            * set relative to the first based on the PTS values.
45328            *
45329            * @param {Object} data used to set the timestamp offset in the muxer
45330            */
45331 
45332         }, {
45333           key: 'setTimestampOffset',
45334           value: function setTimestampOffset(data) {
45335             var timestampOffset = data.timestampOffset || 0;
45336             this.transmuxer.setBaseMediaDecodeTime(Math.round(timestampOffset * 90000));
45337           }
45338         }, {
45339           key: 'setAudioAppendStart',
45340           value: function setAudioAppendStart(data) {
45341             this.transmuxer.setAudioAppendStart(Math.ceil(data.appendStart * 90000));
45342           }
45343           /**
45344            * Forces the pipeline to finish processing the last segment and emit it's
45345            * results.
45346            *
45347            * @param {Object} data event data, not really used
45348            */
45349 
45350         }, {
45351           key: 'flush',
45352           value: function flush(data) {
45353             this.transmuxer.flush();
45354           }
45355         }, {
45356           key: 'resetCaptions',
45357           value: function resetCaptions() {
45358             this.transmuxer.resetCaptions();
45359           }
45360         }, {
45361           key: 'alignGopsWith',
45362           value: function alignGopsWith(data) {
45363             this.transmuxer.alignGopsWith(data.gopsToAlignWith.slice());
45364           }
45365         }]);
45366         return MessageHandlers;
45367       }();
45368       /**
45369        * Our web wroker interface so that things can talk to mux.js
45370        * that will be running in a web worker. the scope is passed to this by
45371        * webworkify.
45372        *
45373        * @param {Object} self the scope for the web worker
45374        */
45375 
45376 
45377       var TransmuxerWorker = function TransmuxerWorker(self) {
45378         self.onmessage = function (event) {
45379           if (event.data.action === 'init' && event.data.options) {
45380             this.messageHandlers = new MessageHandlers(self, event.data.options);
45381             return;
45382           }
45383 
45384           if (!this.messageHandlers) {
45385             this.messageHandlers = new MessageHandlers(self);
45386           }
45387 
45388           if (event.data && event.data.action && event.data.action !== 'init') {
45389             if (this.messageHandlers[event.data.action]) {
45390               this.messageHandlers[event.data.action](event.data);
45391             }
45392           }
45393         };
45394       };
45395 
45396       var transmuxerWorker = new TransmuxerWorker(self);
45397       return transmuxerWorker;
45398     }();
45399   });
45400   /**
45401    * @file - codecs.js - Handles tasks regarding codec strings such as translating them to
45402    * codec strings, or translating codec strings into objects that can be examined.
45403    */
45404   // Default codec parameters if none were provided for video and/or audio
45405 
45406   var defaultCodecs = {
45407     videoCodec: 'avc1',
45408     videoObjectTypeIndicator: '.4d400d',
45409     // AAC-LC
45410     audioProfile: '2'
45411   };
45412   /**
45413    * Replace the old apple-style `avc1.<dd>.<dd>` codec string with the standard
45414    * `avc1.<hhhhhh>`
45415    *
45416    * @param {Array} codecs an array of codec strings to fix
45417    * @return {Array} the translated codec array
45418    * @private
45419    */
45420 
45421   var translateLegacyCodecs = function translateLegacyCodecs(codecs) {
45422     return codecs.map(function (codec) {
45423       return codec.replace(/avc1\.(\d+)\.(\d+)/i, function (orig, profile, avcLevel) {
45424         var profileHex = ('00' + Number(profile).toString(16)).slice(-2);
45425         var avcLevelHex = ('00' + Number(avcLevel).toString(16)).slice(-2);
45426         return 'avc1.' + profileHex + '00' + avcLevelHex;
45427       });
45428     });
45429   };
45430   /**
45431    * Parses a codec string to retrieve the number of codecs specified,
45432    * the video codec and object type indicator, and the audio profile.
45433    */
45434 
45435 
45436   var parseCodecs = function parseCodecs() {
45437     var codecs = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
45438     var result = {
45439       codecCount: 0
45440     };
45441     var parsed = void 0;
45442     result.codecCount = codecs.split(',').length;
45443     result.codecCount = result.codecCount || 2; // parse the video codec
45444 
45445     parsed = /(^|\s|,)+(avc[13])([^ ,]*)/i.exec(codecs);
45446 
45447     if (parsed) {
45448       result.videoCodec = parsed[2];
45449       result.videoObjectTypeIndicator = parsed[3];
45450     } // parse the last field of the audio codec
45451 
45452 
45453     result.audioProfile = /(^|\s|,)+mp4a.[0-9A-Fa-f]+\.([0-9A-Fa-f]+)/i.exec(codecs);
45454     result.audioProfile = result.audioProfile && result.audioProfile[2];
45455     return result;
45456   };
45457   /**
45458    * Replace codecs in the codec string with the old apple-style `avc1.<dd>.<dd>` to the
45459    * standard `avc1.<hhhhhh>`.
45460    *
45461    * @param codecString {String} the codec string
45462    * @return {String} the codec string with old apple-style codecs replaced
45463    *
45464    * @private
45465    */
45466 
45467 
45468   var mapLegacyAvcCodecs = function mapLegacyAvcCodecs(codecString) {
45469     return codecString.replace(/avc1\.(\d+)\.(\d+)/i, function (match) {
45470       return translateLegacyCodecs([match])[0];
45471     });
45472   };
45473   /**
45474    * Build a media mime-type string from a set of parameters
45475    * @param {String} type either 'audio' or 'video'
45476    * @param {String} container either 'mp2t' or 'mp4'
45477    * @param {Array} codecs an array of codec strings to add
45478    * @return {String} a valid media mime-type
45479    */
45480 
45481 
45482   var makeMimeTypeString = function makeMimeTypeString(type, container, codecs) {
45483     // The codecs array is filtered so that falsey values are
45484     // dropped and don't cause Array#join to create spurious
45485     // commas
45486     return type + '/' + container + '; codecs="' + codecs.filter(function (c) {
45487       return !!c;
45488     }).join(', ') + '"';
45489   };
45490   /**
45491    * Returns the type container based on information in the playlist
45492    * @param {Playlist} media the current media playlist
45493    * @return {String} a valid media container type
45494    */
45495 
45496 
45497   var getContainerType = function getContainerType(media) {
45498     // An initialization segment means the media playlist is an iframe
45499     // playlist or is using the mp4 container. We don't currently
45500     // support iframe playlists, so assume this is signalling mp4
45501     // fragments.
45502     if (media.segments && media.segments.length && media.segments[0].map) {
45503       return 'mp4';
45504     }
45505 
45506     return 'mp2t';
45507   };
45508   /**
45509    * Returns a set of codec strings parsed from the playlist or the default
45510    * codec strings if no codecs were specified in the playlist
45511    * @param {Playlist} media the current media playlist
45512    * @return {Object} an object with the video and audio codecs
45513    */
45514 
45515 
45516   var getCodecs = function getCodecs(media) {
45517     // if the codecs were explicitly specified, use them instead of the
45518     // defaults
45519     var mediaAttributes = media.attributes || {};
45520 
45521     if (mediaAttributes.CODECS) {
45522       return parseCodecs(mediaAttributes.CODECS);
45523     }
45524 
45525     return defaultCodecs;
45526   };
45527 
45528   var audioProfileFromDefault = function audioProfileFromDefault(master, audioGroupId) {
45529     if (!master.mediaGroups.AUDIO || !audioGroupId) {
45530       return null;
45531     }
45532 
45533     var audioGroup = master.mediaGroups.AUDIO[audioGroupId];
45534 
45535     if (!audioGroup) {
45536       return null;
45537     }
45538 
45539     for (var name in audioGroup) {
45540       var audioType = audioGroup[name];
45541 
45542       if (audioType["default"] && audioType.playlists) {
45543         // codec should be the same for all playlists within the audio type
45544         return parseCodecs(audioType.playlists[0].attributes.CODECS).audioProfile;
45545       }
45546     }
45547 
45548     return null;
45549   };
45550   /**
45551    * Calculates the MIME type strings for a working configuration of
45552    * SourceBuffers to play variant streams in a master playlist. If
45553    * there is no possible working configuration, an empty array will be
45554    * returned.
45555    *
45556    * @param master {Object} the m3u8 object for the master playlist
45557    * @param media {Object} the m3u8 object for the variant playlist
45558    * @return {Array} the MIME type strings. If the array has more than
45559    * one entry, the first element should be applied to the video
45560    * SourceBuffer and the second to the audio SourceBuffer.
45561    *
45562    * @private
45563    */
45564 
45565 
45566   var mimeTypesForPlaylist = function mimeTypesForPlaylist(master, media) {
45567     var containerType = getContainerType(media);
45568     var codecInfo = getCodecs(media);
45569     var mediaAttributes = media.attributes || {}; // Default condition for a traditional HLS (no demuxed audio/video)
45570 
45571     var isMuxed = true;
45572     var isMaat = false;
45573 
45574     if (!media) {
45575       // Not enough information
45576       return [];
45577     }
45578 
45579     if (master.mediaGroups.AUDIO && mediaAttributes.AUDIO) {
45580       var audioGroup = master.mediaGroups.AUDIO[mediaAttributes.AUDIO]; // Handle the case where we are in a multiple-audio track scenario
45581 
45582       if (audioGroup) {
45583         isMaat = true; // Start with the everything demuxed then...
45584 
45585         isMuxed = false; // ...check to see if any audio group tracks are muxed (ie. lacking a uri)
45586 
45587         for (var groupId in audioGroup) {
45588           // either a uri is present (if the case of HLS and an external playlist), or
45589           // playlists is present (in the case of DASH where we don't have external audio
45590           // playlists)
45591           if (!audioGroup[groupId].uri && !audioGroup[groupId].playlists) {
45592             isMuxed = true;
45593             break;
45594           }
45595         }
45596       }
45597     } // HLS with multiple-audio tracks must always get an audio codec.
45598     // Put another way, there is no way to have a video-only multiple-audio HLS!
45599 
45600 
45601     if (isMaat && !codecInfo.audioProfile) {
45602       if (!isMuxed) {
45603         // It is possible for codecs to be specified on the audio media group playlist but
45604         // not on the rendition playlist. This is mostly the case for DASH, where audio and
45605         // video are always separate (and separately specified).
45606         codecInfo.audioProfile = audioProfileFromDefault(master, mediaAttributes.AUDIO);
45607       }
45608 
45609       if (!codecInfo.audioProfile) {
45610         videojs$1.log.warn('Multiple audio tracks present but no audio codec string is specified. ' + 'Attempting to use the default audio codec (mp4a.40.2)');
45611         codecInfo.audioProfile = defaultCodecs.audioProfile;
45612       }
45613     } // Generate the final codec strings from the codec object generated above
45614 
45615 
45616     var codecStrings = {};
45617 
45618     if (codecInfo.videoCodec) {
45619       codecStrings.video = '' + codecInfo.videoCodec + codecInfo.videoObjectTypeIndicator;
45620     }
45621 
45622     if (codecInfo.audioProfile) {
45623       codecStrings.audio = 'mp4a.40.' + codecInfo.audioProfile;
45624     } // Finally, make and return an array with proper mime-types depending on
45625     // the configuration
45626 
45627 
45628     var justAudio = makeMimeTypeString('audio', containerType, [codecStrings.audio]);
45629     var justVideo = makeMimeTypeString('video', containerType, [codecStrings.video]);
45630     var bothVideoAudio = makeMimeTypeString('video', containerType, [codecStrings.video, codecStrings.audio]);
45631 
45632     if (isMaat) {
45633       if (!isMuxed && codecStrings.video) {
45634         return [justVideo, justAudio];
45635       }
45636 
45637       if (!isMuxed && !codecStrings.video) {
45638         // There is no muxed content and no video codec string, so this is an audio only
45639         // stream with alternate audio.
45640         return [justAudio, justAudio];
45641       } // There exists the possiblity that this will return a `video/container`
45642       // mime-type for the first entry in the array even when there is only audio.
45643       // This doesn't appear to be a problem and simplifies the code.
45644 
45645 
45646       return [bothVideoAudio, justAudio];
45647     } // If there is no video codec at all, always just return a single
45648     // audio/<container> mime-type
45649 
45650 
45651     if (!codecStrings.video) {
45652       return [justAudio];
45653     } // When not using separate audio media groups, audio and video is
45654     // *always* muxed
45655 
45656 
45657     return [bothVideoAudio];
45658   };
45659   /**
45660    * Parse a content type header into a type and parameters
45661    * object
45662    *
45663    * @param {String} type the content type header
45664    * @return {Object} the parsed content-type
45665    * @private
45666    */
45667 
45668 
45669   var parseContentType = function parseContentType(type) {
45670     var object = {
45671       type: '',
45672       parameters: {}
45673     };
45674     var parameters = type.trim().split(';'); // first parameter should always be content-type
45675 
45676     object.type = parameters.shift().trim();
45677     parameters.forEach(function (parameter) {
45678       var pair = parameter.trim().split('=');
45679 
45680       if (pair.length > 1) {
45681         var name = pair[0].replace(/"/g, '').trim();
45682         var value = pair[1].replace(/"/g, '').trim();
45683         object.parameters[name] = value;
45684       }
45685     });
45686     return object;
45687   };
45688   /**
45689    * Check if a codec string refers to an audio codec.
45690    *
45691    * @param {String} codec codec string to check
45692    * @return {Boolean} if this is an audio codec
45693    * @private
45694    */
45695 
45696 
45697   var isAudioCodec = function isAudioCodec(codec) {
45698     return /mp4a\.\d+.\d+/i.test(codec);
45699   };
45700   /**
45701    * Check if a codec string refers to a video codec.
45702    *
45703    * @param {String} codec codec string to check
45704    * @return {Boolean} if this is a video codec
45705    * @private
45706    */
45707 
45708 
45709   var isVideoCodec = function isVideoCodec(codec) {
45710     return /avc1\.[\da-f]+/i.test(codec);
45711   };
45712   /**
45713    * Returns a list of gops in the buffer that have a pts value of 3 seconds or more in
45714    * front of current time.
45715    *
45716    * @param {Array} buffer
45717    *        The current buffer of gop information
45718    * @param {Number} currentTime
45719    *        The current time
45720    * @param {Double} mapping
45721    *        Offset to map display time to stream presentation time
45722    * @return {Array}
45723    *         List of gops considered safe to append over
45724    */
45725 
45726 
45727   var gopsSafeToAlignWith = function gopsSafeToAlignWith(buffer, currentTime, mapping) {
45728     if (typeof currentTime === 'undefined' || currentTime === null || !buffer.length) {
45729       return [];
45730     } // pts value for current time + 3 seconds to give a bit more wiggle room
45731 
45732 
45733     var currentTimePts = Math.ceil((currentTime - mapping + 3) * 90000);
45734     var i = void 0;
45735 
45736     for (i = 0; i < buffer.length; i++) {
45737       if (buffer[i].pts > currentTimePts) {
45738         break;
45739       }
45740     }
45741 
45742     return buffer.slice(i);
45743   };
45744   /**
45745    * Appends gop information (timing and byteLength) received by the transmuxer for the
45746    * gops appended in the last call to appendBuffer
45747    *
45748    * @param {Array} buffer
45749    *        The current buffer of gop information
45750    * @param {Array} gops
45751    *        List of new gop information
45752    * @param {boolean} replace
45753    *        If true, replace the buffer with the new gop information. If false, append the
45754    *        new gop information to the buffer in the right location of time.
45755    * @return {Array}
45756    *         Updated list of gop information
45757    */
45758 
45759 
45760   var updateGopBuffer = function updateGopBuffer(buffer, gops, replace) {
45761     if (!gops.length) {
45762       return buffer;
45763     }
45764 
45765     if (replace) {
45766       // If we are in safe append mode, then completely overwrite the gop buffer
45767       // with the most recent appeneded data. This will make sure that when appending
45768       // future segments, we only try to align with gops that are both ahead of current
45769       // time and in the last segment appended.
45770       return gops.slice();
45771     }
45772 
45773     var start = gops[0].pts;
45774     var i = 0;
45775 
45776     for (i; i < buffer.length; i++) {
45777       if (buffer[i].pts >= start) {
45778         break;
45779       }
45780     }
45781 
45782     return buffer.slice(0, i).concat(gops);
45783   };
45784   /**
45785    * Removes gop information in buffer that overlaps with provided start and end
45786    *
45787    * @param {Array} buffer
45788    *        The current buffer of gop information
45789    * @param {Double} start
45790    *        position to start the remove at
45791    * @param {Double} end
45792    *        position to end the remove at
45793    * @param {Double} mapping
45794    *        Offset to map display time to stream presentation time
45795    */
45796 
45797 
45798   var removeGopBuffer = function removeGopBuffer(buffer, start, end, mapping) {
45799     var startPts = Math.ceil((start - mapping) * 90000);
45800     var endPts = Math.ceil((end - mapping) * 90000);
45801     var updatedBuffer = buffer.slice();
45802     var i = buffer.length;
45803 
45804     while (i--) {
45805       if (buffer[i].pts <= endPts) {
45806         break;
45807       }
45808     }
45809 
45810     if (i === -1) {
45811       // no removal because end of remove range is before start of buffer
45812       return updatedBuffer;
45813     }
45814 
45815     var j = i + 1;
45816 
45817     while (j--) {
45818       if (buffer[j].pts <= startPts) {
45819         break;
45820       }
45821     } // clamp remove range start to 0 index
45822 
45823 
45824     j = Math.max(j, 0);
45825     updatedBuffer.splice(j, i - j + 1);
45826     return updatedBuffer;
45827   };
45828 
45829   var buffered = function buffered(videoBuffer, audioBuffer, audioDisabled) {
45830     var start = null;
45831     var end = null;
45832     var arity = 0;
45833     var extents = [];
45834     var ranges = []; // neither buffer has been created yet
45835 
45836     if (!videoBuffer && !audioBuffer) {
45837       return videojs$1.createTimeRange();
45838     } // only one buffer is configured
45839 
45840 
45841     if (!videoBuffer) {
45842       return audioBuffer.buffered;
45843     }
45844 
45845     if (!audioBuffer) {
45846       return videoBuffer.buffered;
45847     } // both buffers are configured
45848 
45849 
45850     if (audioDisabled) {
45851       return videoBuffer.buffered;
45852     } // both buffers are empty
45853 
45854 
45855     if (videoBuffer.buffered.length === 0 && audioBuffer.buffered.length === 0) {
45856       return videojs$1.createTimeRange();
45857     } // Handle the case where we have both buffers and create an
45858     // intersection of the two
45859 
45860 
45861     var videoBuffered = videoBuffer.buffered;
45862     var audioBuffered = audioBuffer.buffered;
45863     var count = videoBuffered.length; // A) Gather up all start and end times
45864 
45865     while (count--) {
45866       extents.push({
45867         time: videoBuffered.start(count),
45868         type: 'start'
45869       });
45870       extents.push({
45871         time: videoBuffered.end(count),
45872         type: 'end'
45873       });
45874     }
45875 
45876     count = audioBuffered.length;
45877 
45878     while (count--) {
45879       extents.push({
45880         time: audioBuffered.start(count),
45881         type: 'start'
45882       });
45883       extents.push({
45884         time: audioBuffered.end(count),
45885         type: 'end'
45886       });
45887     } // B) Sort them by time
45888 
45889 
45890     extents.sort(function (a, b) {
45891       return a.time - b.time;
45892     }); // C) Go along one by one incrementing arity for start and decrementing
45893     //    arity for ends
45894 
45895     for (count = 0; count < extents.length; count++) {
45896       if (extents[count].type === 'start') {
45897         arity++; // D) If arity is ever incremented to 2 we are entering an
45898         //    overlapping range
45899 
45900         if (arity === 2) {
45901           start = extents[count].time;
45902         }
45903       } else if (extents[count].type === 'end') {
45904         arity--; // E) If arity is ever decremented to 1 we leaving an
45905         //    overlapping range
45906 
45907         if (arity === 1) {
45908           end = extents[count].time;
45909         }
45910       } // F) Record overlapping ranges
45911 
45912 
45913       if (start !== null && end !== null) {
45914         ranges.push([start, end]);
45915         start = null;
45916         end = null;
45917       }
45918     }
45919 
45920     return videojs$1.createTimeRanges(ranges);
45921   };
45922   /**
45923    * @file virtual-source-buffer.js
45924    */
45925 
45926 
45927   var ONE_SECOND_IN_TS$2 = 90000; // We create a wrapper around the SourceBuffer so that we can manage the
45928   // state of the `updating` property manually. We have to do this because
45929   // Firefox changes `updating` to false long before triggering `updateend`
45930   // events and that was causing strange problems in videojs-contrib-hls
45931 
45932   var makeWrappedSourceBuffer = function makeWrappedSourceBuffer(mediaSource, mimeType) {
45933     var sourceBuffer = mediaSource.addSourceBuffer(mimeType);
45934     var wrapper = Object.create(null);
45935     wrapper.updating = false;
45936     wrapper.realBuffer_ = sourceBuffer;
45937 
45938     var _loop = function _loop(key) {
45939       if (typeof sourceBuffer[key] === 'function') {
45940         wrapper[key] = function () {
45941           return sourceBuffer[key].apply(sourceBuffer, arguments);
45942         };
45943       } else if (typeof wrapper[key] === 'undefined') {
45944         Object.defineProperty(wrapper, key, {
45945           get: function get$$1() {
45946             return sourceBuffer[key];
45947           },
45948           set: function set$$1(v) {
45949             return sourceBuffer[key] = v;
45950           }
45951         });
45952       }
45953     };
45954 
45955     for (var key in sourceBuffer) {
45956       _loop(key);
45957     }
45958 
45959     return wrapper;
45960   };
45961   /**
45962    * VirtualSourceBuffers exist so that we can transmux non native formats
45963    * into a native format, but keep the same api as a native source buffer.
45964    * It creates a transmuxer, that works in its own thread (a web worker) and
45965    * that transmuxer muxes the data into a native format. VirtualSourceBuffer will
45966    * then send all of that data to the naive sourcebuffer so that it is
45967    * indestinguishable from a natively supported format.
45968    *
45969    * @param {HtmlMediaSource} mediaSource the parent mediaSource
45970    * @param {Array} codecs array of codecs that we will be dealing with
45971    * @class VirtualSourceBuffer
45972    * @extends video.js.EventTarget
45973    */
45974 
45975 
45976   var VirtualSourceBuffer = function (_videojs$EventTarget) {
45977     inherits$2(VirtualSourceBuffer, _videojs$EventTarget);
45978 
45979     function VirtualSourceBuffer(mediaSource, codecs) {
45980       classCallCheck$1(this, VirtualSourceBuffer);
45981 
45982       var _this = possibleConstructorReturn$1(this, (VirtualSourceBuffer.__proto__ || Object.getPrototypeOf(VirtualSourceBuffer)).call(this, videojs$1.EventTarget));
45983 
45984       _this.timestampOffset_ = 0;
45985       _this.pendingBuffers_ = [];
45986       _this.bufferUpdating_ = false;
45987       _this.mediaSource_ = mediaSource;
45988       _this.codecs_ = codecs;
45989       _this.audioCodec_ = null;
45990       _this.videoCodec_ = null;
45991       _this.audioDisabled_ = false;
45992       _this.appendAudioInitSegment_ = true;
45993       _this.gopBuffer_ = [];
45994       _this.timeMapping_ = 0;
45995       _this.safeAppend_ = videojs$1.browser.IE_VERSION >= 11;
45996       var options = {
45997         remux: false,
45998         alignGopsAtEnd: _this.safeAppend_
45999       };
46000 
46001       _this.codecs_.forEach(function (codec) {
46002         if (isAudioCodec(codec)) {
46003           _this.audioCodec_ = codec;
46004         } else if (isVideoCodec(codec)) {
46005           _this.videoCodec_ = codec;
46006         }
46007       }); // append muxed segments to their respective native buffers as
46008       // soon as they are available
46009 
46010 
46011       _this.transmuxer_ = new TransmuxWorker();
46012 
46013       _this.transmuxer_.postMessage({
46014         action: 'init',
46015         options: options
46016       });
46017 
46018       _this.transmuxer_.onmessage = function (event) {
46019         if (event.data.action === 'data') {
46020           return _this.data_(event);
46021         }
46022 
46023         if (event.data.action === 'done') {
46024           return _this.done_(event);
46025         }
46026 
46027         if (event.data.action === 'gopInfo') {
46028           return _this.appendGopInfo_(event);
46029         }
46030 
46031         if (event.data.action === 'videoSegmentTimingInfo') {
46032           return _this.videoSegmentTimingInfo_(event.data.videoSegmentTimingInfo);
46033         }
46034       }; // this timestampOffset is a property with the side-effect of resetting
46035       // baseMediaDecodeTime in the transmuxer on the setter
46036 
46037 
46038       Object.defineProperty(_this, 'timestampOffset', {
46039         get: function get$$1() {
46040           return this.timestampOffset_;
46041         },
46042         set: function set$$1(val) {
46043           if (typeof val === 'number' && val >= 0) {
46044             this.timestampOffset_ = val;
46045             this.appendAudioInitSegment_ = true; // reset gop buffer on timestampoffset as this signals a change in timeline
46046 
46047             this.gopBuffer_.length = 0;
46048             this.timeMapping_ = 0; // We have to tell the transmuxer to set the baseMediaDecodeTime to
46049             // the desired timestampOffset for the next segment
46050 
46051             this.transmuxer_.postMessage({
46052               action: 'setTimestampOffset',
46053               timestampOffset: val
46054             });
46055           }
46056         }
46057       }); // setting the append window affects both source buffers
46058 
46059       Object.defineProperty(_this, 'appendWindowStart', {
46060         get: function get$$1() {
46061           return (this.videoBuffer_ || this.audioBuffer_).appendWindowStart;
46062         },
46063         set: function set$$1(start) {
46064           if (this.videoBuffer_) {
46065             this.videoBuffer_.appendWindowStart = start;
46066           }
46067 
46068           if (this.audioBuffer_) {
46069             this.audioBuffer_.appendWindowStart = start;
46070           }
46071         }
46072       }); // this buffer is "updating" if either of its native buffers are
46073 
46074       Object.defineProperty(_this, 'updating', {
46075         get: function get$$1() {
46076           return !!(this.bufferUpdating_ || !this.audioDisabled_ && this.audioBuffer_ && this.audioBuffer_.updating || this.videoBuffer_ && this.videoBuffer_.updating);
46077         }
46078       }); // the buffered property is the intersection of the buffered
46079       // ranges of the native source buffers
46080 
46081       Object.defineProperty(_this, 'buffered', {
46082         get: function get$$1() {
46083           return buffered(this.videoBuffer_, this.audioBuffer_, this.audioDisabled_);
46084         }
46085       });
46086       return _this;
46087     }
46088     /**
46089      * When we get a data event from the transmuxer
46090      * we call this function and handle the data that
46091      * was sent to us
46092      *
46093      * @private
46094      * @param {Event} event the data event from the transmuxer
46095      */
46096 
46097 
46098     createClass$1(VirtualSourceBuffer, [{
46099       key: 'data_',
46100       value: function data_(event) {
46101         var segment = event.data.segment; // Cast ArrayBuffer to TypedArray
46102 
46103         segment.data = new Uint8Array(segment.data, event.data.byteOffset, event.data.byteLength);
46104         segment.initSegment = new Uint8Array(segment.initSegment.data, segment.initSegment.byteOffset, segment.initSegment.byteLength);
46105         createTextTracksIfNecessary(this, this.mediaSource_, segment); // Add the segments to the pendingBuffers array
46106 
46107         this.pendingBuffers_.push(segment);
46108         return;
46109       }
46110       /**
46111        * When we get a done event from the transmuxer
46112        * we call this function and we process all
46113        * of the pending data that we have been saving in the
46114        * data_ function
46115        *
46116        * @private
46117        * @param {Event} event the done event from the transmuxer
46118        */
46119 
46120     }, {
46121       key: 'done_',
46122       value: function done_(event) {
46123         // Don't process and append data if the mediaSource is closed
46124         if (this.mediaSource_.readyState === 'closed') {
46125           this.pendingBuffers_.length = 0;
46126           return;
46127         } // All buffers should have been flushed from the muxer
46128         // start processing anything we have received
46129 
46130 
46131         this.processPendingSegments_();
46132         return;
46133       }
46134     }, {
46135       key: 'videoSegmentTimingInfo_',
46136       value: function videoSegmentTimingInfo_(timingInfo) {
46137         var timingInfoInSeconds = {
46138           start: {
46139             decode: timingInfo.start.dts / ONE_SECOND_IN_TS$2,
46140             presentation: timingInfo.start.pts / ONE_SECOND_IN_TS$2
46141           },
46142           end: {
46143             decode: timingInfo.end.dts / ONE_SECOND_IN_TS$2,
46144             presentation: timingInfo.end.pts / ONE_SECOND_IN_TS$2
46145           },
46146           baseMediaDecodeTime: timingInfo.baseMediaDecodeTime / ONE_SECOND_IN_TS$2
46147         };
46148 
46149         if (timingInfo.prependedContentDuration) {
46150           timingInfoInSeconds.prependedContentDuration = timingInfo.prependedContentDuration / ONE_SECOND_IN_TS$2;
46151         }
46152 
46153         this.trigger({
46154           type: 'videoSegmentTimingInfo',
46155           videoSegmentTimingInfo: timingInfoInSeconds
46156         });
46157       }
46158       /**
46159        * Create our internal native audio/video source buffers and add
46160        * event handlers to them with the following conditions:
46161        * 1. they do not already exist on the mediaSource
46162        * 2. this VSB has a codec for them
46163        *
46164        * @private
46165        */
46166 
46167     }, {
46168       key: 'createRealSourceBuffers_',
46169       value: function createRealSourceBuffers_() {
46170         var _this2 = this;
46171 
46172         var types = ['audio', 'video'];
46173         types.forEach(function (type) {
46174           // Don't create a SourceBuffer of this type if we don't have a
46175           // codec for it
46176           if (!_this2[type + 'Codec_']) {
46177             return;
46178           } // Do nothing if a SourceBuffer of this type already exists
46179 
46180 
46181           if (_this2[type + 'Buffer_']) {
46182             return;
46183           }
46184 
46185           var buffer = null; // If the mediasource already has a SourceBuffer for the codec
46186           // use that
46187 
46188           if (_this2.mediaSource_[type + 'Buffer_']) {
46189             buffer = _this2.mediaSource_[type + 'Buffer_']; // In multiple audio track cases, the audio source buffer is disabled
46190             // on the main VirtualSourceBuffer by the HTMLMediaSource much earlier
46191             // than createRealSourceBuffers_ is called to create the second
46192             // VirtualSourceBuffer because that happens as a side-effect of
46193             // videojs-contrib-hls starting the audioSegmentLoader. As a result,
46194             // the audioBuffer is essentially "ownerless" and no one will toggle
vendor: 30,121 bytes, lines 46195-47020
46195             // the `updating` state back to false once the `updateend` event is received
46196             //
46197             // Setting `updating` to false manually will work around this
46198             // situation and allow work to continue
46199 
46200             buffer.updating = false;
46201           } else {
46202             var codecProperty = type + 'Codec_';
46203             var mimeType = type + '/mp4;codecs="' + _this2[codecProperty] + '"';
46204             buffer = makeWrappedSourceBuffer(_this2.mediaSource_.nativeMediaSource_, mimeType);
46205             _this2.mediaSource_[type + 'Buffer_'] = buffer;
46206           }
46207 
46208           _this2[type + 'Buffer_'] = buffer; // Wire up the events to the SourceBuffer
46209 
46210           ['update', 'updatestart', 'updateend'].forEach(function (event) {
46211             buffer.addEventListener(event, function () {
46212               // if audio is disabled
46213               if (type === 'audio' && _this2.audioDisabled_) {
46214                 return;
46215               }
46216 
46217               if (event === 'updateend') {
46218                 _this2[type + 'Buffer_'].updating = false;
46219               }
46220 
46221               var shouldTrigger = types.every(function (t) {
46222                 // skip checking audio's updating status if audio
46223                 // is not enabled
46224                 if (t === 'audio' && _this2.audioDisabled_) {
46225                   return true;
46226                 } // if the other type is updating we don't trigger
46227 
46228 
46229                 if (type !== t && _this2[t + 'Buffer_'] && _this2[t + 'Buffer_'].updating) {
46230                   return false;
46231                 }
46232 
46233                 return true;
46234               });
46235 
46236               if (shouldTrigger) {
46237                 return _this2.trigger(event);
46238               }
46239             });
46240           });
46241         });
46242       }
46243       /**
46244        * Emulate the native mediasource function, but our function will
46245        * send all of the proposed segments to the transmuxer so that we
46246        * can transmux them before we append them to our internal
46247        * native source buffers in the correct format.
46248        *
46249        * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/appendBuffer
46250        * @param {Uint8Array} segment the segment to append to the buffer
46251        */
46252 
46253     }, {
46254       key: 'appendBuffer',
46255       value: function appendBuffer(segment) {
46256         // Start the internal "updating" state
46257         this.bufferUpdating_ = true;
46258 
46259         if (this.audioBuffer_ && this.audioBuffer_.buffered.length) {
46260           var audioBuffered = this.audioBuffer_.buffered;
46261           this.transmuxer_.postMessage({
46262             action: 'setAudioAppendStart',
46263             appendStart: audioBuffered.end(audioBuffered.length - 1)
46264           });
46265         }
46266 
46267         if (this.videoBuffer_) {
46268           this.transmuxer_.postMessage({
46269             action: 'alignGopsWith',
46270             gopsToAlignWith: gopsSafeToAlignWith(this.gopBuffer_, this.mediaSource_.player_ ? this.mediaSource_.player_.currentTime() : null, this.timeMapping_)
46271           });
46272         }
46273 
46274         this.transmuxer_.postMessage({
46275           action: 'push',
46276           // Send the typed-array of data as an ArrayBuffer so that
46277           // it can be sent as a "Transferable" and avoid the costly
46278           // memory copy
46279           data: segment.buffer,
46280           // To recreate the original typed-array, we need information
46281           // about what portion of the ArrayBuffer it was a view into
46282           byteOffset: segment.byteOffset,
46283           byteLength: segment.byteLength
46284         }, [segment.buffer]);
46285         this.transmuxer_.postMessage({
46286           action: 'flush'
46287         });
46288       }
46289       /**
46290        * Appends gop information (timing and byteLength) received by the transmuxer for the
46291        * gops appended in the last call to appendBuffer
46292        *
46293        * @param {Event} event
46294        *        The gopInfo event from the transmuxer
46295        * @param {Array} event.data.gopInfo
46296        *        List of gop info to append
46297        */
46298 
46299     }, {
46300       key: 'appendGopInfo_',
46301       value: function appendGopInfo_(event) {
46302         this.gopBuffer_ = updateGopBuffer(this.gopBuffer_, event.data.gopInfo, this.safeAppend_);
46303       }
46304       /**
46305        * Emulate the native mediasource function and remove parts
46306        * of the buffer from any of our internal buffers that exist
46307        *
46308        * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/remove
46309        * @param {Double} start position to start the remove at
46310        * @param {Double} end position to end the remove at
46311        */
46312 
46313     }, {
46314       key: 'remove',
46315       value: function remove(start, end) {
46316         if (this.videoBuffer_) {
46317           this.videoBuffer_.updating = true;
46318           this.videoBuffer_.remove(start, end);
46319           this.gopBuffer_ = removeGopBuffer(this.gopBuffer_, start, end, this.timeMapping_);
46320         }
46321 
46322         if (!this.audioDisabled_ && this.audioBuffer_) {
46323           this.audioBuffer_.updating = true;
46324           this.audioBuffer_.remove(start, end);
46325         } // Remove Metadata Cues (id3)
46326 
46327 
46328         removeCuesFromTrack(start, end, this.metadataTrack_); // Remove Any Captions
46329 
46330         if (this.inbandTextTracks_) {
46331           for (var track in this.inbandTextTracks_) {
46332             removeCuesFromTrack(start, end, this.inbandTextTracks_[track]);
46333           }
46334         }
46335       }
46336       /**
46337        * Process any segments that the muxer has output
46338        * Concatenate segments together based on type and append them into
46339        * their respective sourceBuffers
46340        *
46341        * @private
46342        */
46343 
46344     }, {
46345       key: 'processPendingSegments_',
46346       value: function processPendingSegments_() {
46347         var sortedSegments = {
46348           video: {
46349             segments: [],
46350             bytes: 0
46351           },
46352           audio: {
46353             segments: [],
46354             bytes: 0
46355           },
46356           captions: [],
46357           metadata: []
46358         };
46359 
46360         if (!this.pendingBuffers_.length) {
46361           // We are no longer in the internal "updating" state
46362           this.trigger('updateend');
46363           this.bufferUpdating_ = false;
46364           return;
46365         } // Sort segments into separate video/audio arrays and
46366         // keep track of their total byte lengths
46367 
46368 
46369         sortedSegments = this.pendingBuffers_.reduce(function (segmentObj, segment) {
46370           var type = segment.type;
46371           var data = segment.data;
46372           var initSegment = segment.initSegment;
46373           segmentObj[type].segments.push(data);
46374           segmentObj[type].bytes += data.byteLength;
46375           segmentObj[type].initSegment = initSegment; // Gather any captions into a single array
46376 
46377           if (segment.captions) {
46378             segmentObj.captions = segmentObj.captions.concat(segment.captions);
46379           }
46380 
46381           if (segment.info) {
46382             segmentObj[type].info = segment.info;
46383           } // Gather any metadata into a single array
46384 
46385 
46386           if (segment.metadata) {
46387             segmentObj.metadata = segmentObj.metadata.concat(segment.metadata);
46388           }
46389 
46390           return segmentObj;
46391         }, sortedSegments); // Create the real source buffers if they don't exist by now since we
46392         // finally are sure what tracks are contained in the source
46393 
46394         if (!this.videoBuffer_ && !this.audioBuffer_) {
46395           // Remove any codecs that may have been specified by default but
46396           // are no longer applicable now
46397           if (sortedSegments.video.bytes === 0) {
46398             this.videoCodec_ = null;
46399           }
46400 
46401           if (sortedSegments.audio.bytes === 0) {
46402             this.audioCodec_ = null;
46403           }
46404 
46405           this.createRealSourceBuffers_();
46406         }
46407 
46408         if (sortedSegments.audio.info) {
46409           this.mediaSource_.trigger({
46410             type: 'audioinfo',
46411             info: sortedSegments.audio.info
46412           });
46413         }
46414 
46415         if (sortedSegments.video.info) {
46416           this.mediaSource_.trigger({
46417             type: 'videoinfo',
46418             info: sortedSegments.video.info
46419           });
46420         }
46421 
46422         if (this.appendAudioInitSegment_) {
46423           if (!this.audioDisabled_ && this.audioBuffer_) {
46424             sortedSegments.audio.segments.unshift(sortedSegments.audio.initSegment);
46425             sortedSegments.audio.bytes += sortedSegments.audio.initSegment.byteLength;
46426           }
46427 
46428           this.appendAudioInitSegment_ = false;
46429         }
46430 
46431         var triggerUpdateend = false; // Merge multiple video and audio segments into one and append
46432 
46433         if (this.videoBuffer_ && sortedSegments.video.bytes) {
46434           sortedSegments.video.segments.unshift(sortedSegments.video.initSegment);
46435           sortedSegments.video.bytes += sortedSegments.video.initSegment.byteLength;
46436           this.concatAndAppendSegments_(sortedSegments.video, this.videoBuffer_);
46437         } else if (this.videoBuffer_ && (this.audioDisabled_ || !this.audioBuffer_)) {
46438           // The transmuxer did not return any bytes of video, meaning it was all trimmed
46439           // for gop alignment. Since we have a video buffer and audio is disabled, updateend
46440           // will never be triggered by this source buffer, which will cause contrib-hls
46441           // to be stuck forever waiting for updateend. If audio is not disabled, updateend
46442           // will be triggered by the audio buffer, which will be sent upwards since the video
46443           // buffer will not be in an updating state.
46444           triggerUpdateend = true;
46445         } // Add text-track data for all
46446 
46447 
46448         addTextTrackData(this, sortedSegments.captions, sortedSegments.metadata);
46449 
46450         if (!this.audioDisabled_ && this.audioBuffer_) {
46451           this.concatAndAppendSegments_(sortedSegments.audio, this.audioBuffer_);
46452         }
46453 
46454         this.pendingBuffers_.length = 0;
46455 
46456         if (triggerUpdateend) {
46457           this.trigger('updateend');
46458         } // We are no longer in the internal "updating" state
46459 
46460 
46461         this.bufferUpdating_ = false;
46462       }
46463       /**
46464        * Combine all segments into a single Uint8Array and then append them
46465        * to the destination buffer
46466        *
46467        * @param {Object} segmentObj
46468        * @param {SourceBuffer} destinationBuffer native source buffer to append data to
46469        * @private
46470        */
46471 
46472     }, {
46473       key: 'concatAndAppendSegments_',
46474       value: function concatAndAppendSegments_(segmentObj, destinationBuffer) {
46475         var offset = 0;
46476         var tempBuffer = void 0;
46477 
46478         if (segmentObj.bytes) {
46479           tempBuffer = new Uint8Array(segmentObj.bytes); // Combine the individual segments into one large typed-array
46480 
46481           segmentObj.segments.forEach(function (segment) {
46482             tempBuffer.set(segment, offset);
46483             offset += segment.byteLength;
46484           });
46485 
46486           try {
46487             destinationBuffer.updating = true;
46488             destinationBuffer.appendBuffer(tempBuffer);
46489           } catch (error) {
46490             if (this.mediaSource_.player_) {
46491               this.mediaSource_.player_.error({
46492                 code: -3,
46493                 type: 'APPEND_BUFFER_ERR',
46494                 message: error.message,
46495                 originalError: error
46496               });
46497             }
46498           }
46499         }
46500       }
46501       /**
46502        * Emulate the native mediasource function. abort any soureBuffer
46503        * actions and throw out any un-appended data.
46504        *
46505        * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/abort
46506        */
46507 
46508     }, {
46509       key: 'abort',
46510       value: function abort() {
46511         if (this.videoBuffer_) {
46512           this.videoBuffer_.abort();
46513         }
46514 
46515         if (!this.audioDisabled_ && this.audioBuffer_) {
46516           this.audioBuffer_.abort();
46517         }
46518 
46519         if (this.transmuxer_) {
46520           this.transmuxer_.postMessage({
46521             action: 'reset'
46522           });
46523         }
46524 
46525         this.pendingBuffers_.length = 0;
46526         this.bufferUpdating_ = false;
46527       }
46528     }]);
46529     return VirtualSourceBuffer;
46530   }(videojs$1.EventTarget);
46531   /**
46532    * @file html-media-source.js
46533    */
46534 
46535   /**
46536    * Our MediaSource implementation in HTML, mimics native
46537    * MediaSource where/if possible.
46538    *
46539    * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource
46540    * @class HtmlMediaSource
46541    * @extends videojs.EventTarget
46542    */
46543 
46544 
46545   var HtmlMediaSource = function (_videojs$EventTarget) {
46546     inherits$2(HtmlMediaSource, _videojs$EventTarget);
46547 
46548     function HtmlMediaSource() {
46549       classCallCheck$1(this, HtmlMediaSource);
46550 
46551       var _this = possibleConstructorReturn$1(this, (HtmlMediaSource.__proto__ || Object.getPrototypeOf(HtmlMediaSource)).call(this));
46552 
46553       var property = void 0;
46554       _this.nativeMediaSource_ = new window$1.MediaSource(); // delegate to the native MediaSource's methods by default
46555 
46556       for (property in _this.nativeMediaSource_) {
46557         if (!(property in HtmlMediaSource.prototype) && typeof _this.nativeMediaSource_[property] === 'function') {
46558           _this[property] = _this.nativeMediaSource_[property].bind(_this.nativeMediaSource_);
46559         }
46560       } // emulate `duration` and `seekable` until seeking can be
46561       // handled uniformly for live streams
46562       // see https://github.com/w3c/media-source/issues/5
46563 
46564 
46565       _this.duration_ = NaN;
46566       Object.defineProperty(_this, 'duration', {
46567         get: function get$$1() {
46568           if (this.duration_ === Infinity) {
46569             return this.duration_;
46570           }
46571 
46572           return this.nativeMediaSource_.duration;
46573         },
46574         set: function set$$1(duration) {
46575           this.duration_ = duration;
46576 
46577           if (duration !== Infinity) {
46578             this.nativeMediaSource_.duration = duration;
46579             return;
46580           }
46581         }
46582       });
46583       Object.defineProperty(_this, 'seekable', {
46584         get: function get$$1() {
46585           if (this.duration_ === Infinity) {
46586             return videojs$1.createTimeRanges([[0, this.nativeMediaSource_.duration]]);
46587           }
46588 
46589           return this.nativeMediaSource_.seekable;
46590         }
46591       });
46592       Object.defineProperty(_this, 'readyState', {
46593         get: function get$$1() {
46594           return this.nativeMediaSource_.readyState;
46595         }
46596       });
46597       Object.defineProperty(_this, 'activeSourceBuffers', {
46598         get: function get$$1() {
46599           return this.activeSourceBuffers_;
46600         }
46601       }); // the list of virtual and native SourceBuffers created by this
46602       // MediaSource
46603 
46604       _this.sourceBuffers = [];
46605       _this.activeSourceBuffers_ = [];
46606       /**
46607        * update the list of active source buffers based upon various
46608        * imformation from HLS and video.js
46609        *
46610        * @private
46611        */
46612 
46613       _this.updateActiveSourceBuffers_ = function () {
46614         // Retain the reference but empty the array
46615         _this.activeSourceBuffers_.length = 0; // If there is only one source buffer, then it will always be active and audio will
46616         // be disabled based on the codec of the source buffer
46617 
46618         if (_this.sourceBuffers.length === 1) {
46619           var sourceBuffer = _this.sourceBuffers[0];
46620           sourceBuffer.appendAudioInitSegment_ = true;
46621           sourceBuffer.audioDisabled_ = !sourceBuffer.audioCodec_;
46622 
46623           _this.activeSourceBuffers_.push(sourceBuffer);
46624 
46625           return;
46626         } // There are 2 source buffers, a combined (possibly video only) source buffer and
46627         // and an audio only source buffer.
46628         // By default, the audio in the combined virtual source buffer is enabled
46629         // and the audio-only source buffer (if it exists) is disabled.
46630 
46631 
46632         var disableCombined = false;
46633         var disableAudioOnly = true; // TODO: maybe we can store the sourcebuffers on the track objects?
46634         // safari may do something like this
46635 
46636         for (var i = 0; i < _this.player_.audioTracks().length; i++) {
46637           var track = _this.player_.audioTracks()[i];
46638 
46639           if (track.enabled && track.kind !== 'main') {
46640             // The enabled track is an alternate audio track so disable the audio in
46641             // the combined source buffer and enable the audio-only source buffer.
46642             disableCombined = true;
46643             disableAudioOnly = false;
46644             break;
46645           }
46646         }
46647 
46648         _this.sourceBuffers.forEach(function (sourceBuffer, index) {
46649           /* eslinst-disable */
46650           // TODO once codecs are required, we can switch to using the codecs to determine
46651           //      what stream is the video stream, rather than relying on videoTracks
46652 
46653           /* eslinst-enable */
46654           sourceBuffer.appendAudioInitSegment_ = true;
46655 
46656           if (sourceBuffer.videoCodec_ && sourceBuffer.audioCodec_) {
46657             // combined
46658             sourceBuffer.audioDisabled_ = disableCombined;
46659           } else if (sourceBuffer.videoCodec_ && !sourceBuffer.audioCodec_) {
46660             // If the "combined" source buffer is video only, then we do not want
46661             // disable the audio-only source buffer (this is mostly for demuxed
46662             // audio and video hls)
46663             sourceBuffer.audioDisabled_ = true;
46664             disableAudioOnly = false;
46665           } else if (!sourceBuffer.videoCodec_ && sourceBuffer.audioCodec_) {
46666             // audio only
46667             // In the case of audio only with alternate audio and disableAudioOnly is true
46668             // this means we want to disable the audio on the alternate audio sourcebuffer
46669             // but not the main "combined" source buffer. The "combined" source buffer is
46670             // always at index 0, so this ensures audio won't be disabled in both source
46671             // buffers.
46672             sourceBuffer.audioDisabled_ = index ? disableAudioOnly : !disableAudioOnly;
46673 
46674             if (sourceBuffer.audioDisabled_) {
46675               return;
46676             }
46677           }
46678 
46679           _this.activeSourceBuffers_.push(sourceBuffer);
46680         });
46681       };
46682 
46683       _this.onPlayerMediachange_ = function () {
46684         _this.sourceBuffers.forEach(function (sourceBuffer) {
46685           sourceBuffer.appendAudioInitSegment_ = true;
46686         });
46687       };
46688 
46689       _this.onHlsReset_ = function () {
46690         _this.sourceBuffers.forEach(function (sourceBuffer) {
46691           if (sourceBuffer.transmuxer_) {
46692             sourceBuffer.transmuxer_.postMessage({
46693               action: 'resetCaptions'
46694             });
46695           }
46696         });
46697       };
46698 
46699       _this.onHlsSegmentTimeMapping_ = function (event) {
46700         _this.sourceBuffers.forEach(function (buffer) {
46701           return buffer.timeMapping_ = event.mapping;
46702         });
46703       }; // Re-emit MediaSource events on the polyfill
46704 
46705 
46706       ['sourceopen', 'sourceclose', 'sourceended'].forEach(function (eventName) {
46707         this.nativeMediaSource_.addEventListener(eventName, this.trigger.bind(this));
46708       }, _this); // capture the associated player when the MediaSource is
46709       // successfully attached
46710 
46711       _this.on('sourceopen', function (event) {
46712         // Get the player this MediaSource is attached to
46713         var video = document.querySelector('[src="' + _this.url_ + '"]');
46714 
46715         if (!video) {
46716           return;
46717         }
46718 
46719         _this.player_ = videojs$1(video.parentNode);
46720 
46721         if (!_this.player_) {
46722           return;
46723         } // hls-reset is fired by videojs.Hls on to the tech after the main SegmentLoader
46724         // resets its state and flushes the buffer
46725 
46726 
46727         _this.player_.tech_.on('hls-reset', _this.onHlsReset_); // hls-segment-time-mapping is fired by videojs.Hls on to the tech after the main
46728         // SegmentLoader inspects an MTS segment and has an accurate stream to display
46729         // time mapping
46730 
46731 
46732         _this.player_.tech_.on('hls-segment-time-mapping', _this.onHlsSegmentTimeMapping_);
46733 
46734         if (_this.player_.audioTracks && _this.player_.audioTracks()) {
46735           _this.player_.audioTracks().on('change', _this.updateActiveSourceBuffers_);
46736 
46737           _this.player_.audioTracks().on('addtrack', _this.updateActiveSourceBuffers_);
46738 
46739           _this.player_.audioTracks().on('removetrack', _this.updateActiveSourceBuffers_);
46740         }
46741 
46742         _this.player_.on('mediachange', _this.onPlayerMediachange_);
46743       });
46744 
46745       _this.on('sourceended', function (event) {
46746         var duration = durationOfVideo(_this.duration);
46747 
46748         for (var i = 0; i < _this.sourceBuffers.length; i++) {
46749           var sourcebuffer = _this.sourceBuffers[i];
46750           var cues = sourcebuffer.metadataTrack_ && sourcebuffer.metadataTrack_.cues;
46751 
46752           if (cues && cues.length) {
46753             cues[cues.length - 1].endTime = duration;
46754           }
46755         }
46756       }); // explicitly terminate any WebWorkers that were created
46757       // by SourceHandlers
46758 
46759 
46760       _this.on('sourceclose', function (event) {
46761         this.sourceBuffers.forEach(function (sourceBuffer) {
46762           if (sourceBuffer.transmuxer_) {
46763             sourceBuffer.transmuxer_.terminate();
46764           }
46765         });
46766         this.sourceBuffers.length = 0;
46767 
46768         if (!this.player_) {
46769           return;
46770         }
46771 
46772         if (this.player_.audioTracks && this.player_.audioTracks()) {
46773           this.player_.audioTracks().off('change', this.updateActiveSourceBuffers_);
46774           this.player_.audioTracks().off('addtrack', this.updateActiveSourceBuffers_);
46775           this.player_.audioTracks().off('removetrack', this.updateActiveSourceBuffers_);
46776         } // We can only change this if the player hasn't been disposed of yet
46777         // because `off` eventually tries to use the el_ property. If it has
46778         // been disposed of, then don't worry about it because there are no
46779         // event handlers left to unbind anyway
46780 
46781 
46782         if (this.player_.el_) {
46783           this.player_.off('mediachange', this.onPlayerMediachange_);
46784         }
46785 
46786         if (this.player_.tech_ && this.player_.tech_.el_) {
46787           this.player_.tech_.off('hls-reset', this.onHlsReset_);
46788           this.player_.tech_.off('hls-segment-time-mapping', this.onHlsSegmentTimeMapping_);
46789         }
46790       });
46791 
46792       return _this;
46793     }
46794     /**
46795      * Add a range that that can now be seeked to.
46796      *
46797      * @param {Double} start where to start the addition
46798      * @param {Double} end where to end the addition
46799      * @private
46800      */
46801 
46802 
46803     createClass$1(HtmlMediaSource, [{
46804       key: 'addSeekableRange_',
46805       value: function addSeekableRange_(start, end) {
46806         var error = void 0;
46807 
46808         if (this.duration !== Infinity) {
46809           error = new Error('MediaSource.addSeekableRange() can only be invoked ' + 'when the duration is Infinity');
46810           error.name = 'InvalidStateError';
46811           error.code = 11;
46812           throw error;
46813         }
46814 
46815         if (end > this.nativeMediaSource_.duration || isNaN(this.nativeMediaSource_.duration)) {
46816           this.nativeMediaSource_.duration = end;
46817         }
46818       }
46819       /**
46820        * Add a source buffer to the media source.
46821        *
46822        * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/addSourceBuffer
46823        * @param {String} type the content-type of the content
46824        * @return {Object} the created source buffer
46825        */
46826 
46827     }, {
46828       key: 'addSourceBuffer',
46829       value: function addSourceBuffer(type) {
46830         var buffer = void 0;
46831         var parsedType = parseContentType(type); // Create a VirtualSourceBuffer to transmux MPEG-2 transport
46832         // stream segments into fragmented MP4s
46833 
46834         if (/^(video|audio)\/mp2t$/i.test(parsedType.type)) {
46835           var codecs = [];
46836 
46837           if (parsedType.parameters && parsedType.parameters.codecs) {
46838             codecs = parsedType.parameters.codecs.split(',');
46839             codecs = translateLegacyCodecs(codecs);
46840             codecs = codecs.filter(function (codec) {
46841               return isAudioCodec(codec) || isVideoCodec(codec);
46842             });
46843           }
46844 
46845           if (codecs.length === 0) {
46846             codecs = ['avc1.4d400d', 'mp4a.40.2'];
46847           }
46848 
46849           buffer = new VirtualSourceBuffer(this, codecs);
46850 
46851           if (this.sourceBuffers.length !== 0) {
46852             // If another VirtualSourceBuffer already exists, then we are creating a
46853             // SourceBuffer for an alternate audio track and therefore we know that
46854             // the source has both an audio and video track.
46855             // That means we should trigger the manual creation of the real
46856             // SourceBuffers instead of waiting for the transmuxer to return data
46857             this.sourceBuffers[0].createRealSourceBuffers_();
46858             buffer.createRealSourceBuffers_(); // Automatically disable the audio on the first source buffer if
46859             // a second source buffer is ever created
46860 
46861             this.sourceBuffers[0].audioDisabled_ = true;
46862           }
46863         } else {
46864           // delegate to the native implementation
46865           buffer = this.nativeMediaSource_.addSourceBuffer(type);
46866         }
46867 
46868         this.sourceBuffers.push(buffer);
46869         return buffer;
46870       }
46871     }]);
46872     return HtmlMediaSource;
46873   }(videojs$1.EventTarget);
46874   /**
46875    * @file videojs-contrib-media-sources.js
46876    */
46877 
46878 
46879   var urlCount = 0; // ------------
46880   // Media Source
46881   // ------------
46882   // store references to the media sources so they can be connected
46883   // to a video element (a swf object)
46884   // TODO: can we store this somewhere local to this module?
46885 
46886   videojs$1.mediaSources = {};
46887   /**
46888    * Provide a method for a swf object to notify JS that a
46889    * media source is now open.
46890    *
46891    * @param {String} msObjectURL string referencing the MSE Object URL
46892    * @param {String} swfId the swf id
46893    */
46894 
46895   var open = function open(msObjectURL, swfId) {
46896     var mediaSource = videojs$1.mediaSources[msObjectURL];
46897 
46898     if (mediaSource) {
46899       mediaSource.trigger({
46900         type: 'sourceopen',
46901         swfId: swfId
46902       });
46903     } else {
46904       throw new Error('Media Source not found (Video.js)');
46905     }
46906   };
46907   /**
46908    * Check to see if the native MediaSource object exists and supports
46909    * an MP4 container with both H.264 video and AAC-LC audio.
46910    *
46911    * @return {Boolean} if  native media sources are supported
46912    */
46913 
46914 
46915   var supportsNativeMediaSources = function supportsNativeMediaSources() {
46916     return !!window$1.MediaSource && !!window$1.MediaSource.isTypeSupported && window$1.MediaSource.isTypeSupported('video/mp4;codecs="avc1.4d400d,mp4a.40.2"');
46917   };
46918   /**
46919    * An emulation of the MediaSource API so that we can support
46920    * native and non-native functionality. returns an instance of
46921    * HtmlMediaSource.
46922    *
46923    * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/MediaSource
46924    */
46925 
46926 
46927   var MediaSource = function MediaSource() {
46928     this.MediaSource = {
46929       open: open,
46930       supportsNativeMediaSources: supportsNativeMediaSources
46931     };
46932 
46933     if (supportsNativeMediaSources()) {
46934       return new HtmlMediaSource();
46935     }
46936 
46937     throw new Error('Cannot use create a virtual MediaSource for this video');
46938   };
46939 
46940   MediaSource.open = open;
46941   MediaSource.supportsNativeMediaSources = supportsNativeMediaSources;
46942   /**
46943    * A wrapper around the native URL for our MSE object
46944    * implementation, this object is exposed under videojs.URL
46945    *
46946    * @link https://developer.mozilla.org/en-US/docs/Web/API/URL/URL
46947    */
46948 
46949   var URL$1 = {
46950     /**
46951      * A wrapper around the native createObjectURL for our objects.
46952      * This function maps a native or emulated mediaSource to a blob
46953      * url so that it can be loaded into video.js
46954      *
46955      * @link https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL
46956      * @param {MediaSource} object the object to create a blob url to
46957      */
46958     createObjectURL: function createObjectURL(object) {
46959       var objectUrlPrefix = 'blob:vjs-media-source/';
46960       var url = void 0; // use the native MediaSource to generate an object URL
46961 
46962       if (object instanceof HtmlMediaSource) {
46963         url = window$1.URL.createObjectURL(object.nativeMediaSource_);
46964         object.url_ = url;
46965         return url;
46966       } // if the object isn't an emulated MediaSource, delegate to the
46967       // native implementation
46968 
46969 
46970       if (!(object instanceof HtmlMediaSource)) {
46971         url = window$1.URL.createObjectURL(object);
46972         object.url_ = url;
46973         return url;
46974       } // build a URL that can be used to map back to the emulated
46975       // MediaSource
46976 
46977 
46978       url = objectUrlPrefix + urlCount;
46979       urlCount++; // setup the mapping back to object
46980 
46981       videojs$1.mediaSources[url] = object;
46982       return url;
46983     }
46984   };
46985   videojs$1.MediaSource = MediaSource;
46986   videojs$1.URL = URL$1;
46987   var EventTarget$1$1 = videojs$1.EventTarget,
46988       mergeOptions$2 = videojs$1.mergeOptions;
46989   /**
46990    * Returns a new master manifest that is the result of merging an updated master manifest
46991    * into the original version.
46992    *
46993    * @param {Object} oldMaster
46994    *        The old parsed mpd object
46995    * @param {Object} newMaster
46996    *        The updated parsed mpd object
46997    * @return {Object}
46998    *         A new object representing the original master manifest with the updated media
46999    *         playlists merged in
47000    */
47001 
47002   var updateMaster$1 = function updateMaster$$1(oldMaster, newMaster) {
47003     var noChanges = void 0;
47004     var update = mergeOptions$2(oldMaster, {
47005       // These are top level properties that can be updated
47006       duration: newMaster.duration,
47007       minimumUpdatePeriod: newMaster.minimumUpdatePeriod
47008     }); // First update the playlists in playlist list
47009 
47010     for (var i = 0; i < newMaster.playlists.length; i++) {
47011       var playlistUpdate = updateMaster(update, newMaster.playlists[i]);
47012 
47013       if (playlistUpdate) {
47014         update = playlistUpdate;
47015       } else {
47016         noChanges = true;
47017       }
47018     } // Then update media group playlists
47019 
47020 
47021     forEachMediaGroup(newMaster, function (properties, type, group, label) {
47022       if (properties.playlists && properties.playlists.length) {
47023         var uri = properties.playlists[0].uri;
47024 
47025         var _playlistUpdate = updateMaster(update, properties.playlists[0]);
47026 
47027         if (_playlistUpdate) {
47028           update = _playlistUpdate; // update the playlist reference within media groups
47029 
47030           update.mediaGroups[type][group][label].playlists[0] = update.playlists[uri];
47031           noChanges = false;
47032         }
47033       }
47034     });
47035 
47036     if (noChanges) {
47037       return null;
47038     }
47039 
47040     return update;
47041   };
47042 
47043   var generateSidxKey = function generateSidxKey(sidxInfo) {
47044     // should be non-inclusive
47045     var sidxByteRangeEnd = sidxInfo.byterange.offset + sidxInfo.byterange.length - 1;
47046     return sidxInfo.uri + '-' + sidxInfo.byterange.offset + '-' + sidxByteRangeEnd;
47047   }; // SIDX should be equivalent if the URI and byteranges of the SIDX match.
47048   // If the SIDXs have maps, the two maps should match,
47049   // both `a` and `b` missing SIDXs is considered matching.
47050   // If `a` or `b` but not both have a map, they aren't matching.
47051 
47052 
47053   var equivalentSidx = function equivalentSidx(a, b) {
47054     var neitherMap = Boolean(!a.map && !b.map);
47055     var equivalentMap = neitherMap || Boolean(a.map && b.map && a.map.byterange.offset === b.map.byterange.offset && a.map.byterange.length === b.map.byterange.length);
47056     return equivalentMap && a.uri === b.uri && a.byterange.offset === b.byterange.offset && a.byterange.length === b.byterange.length;
47057   }; // exported for testing
47058 
47059 
47060   var compareSidxEntry = function compareSidxEntry(playlists, oldSidxMapping) {
47061     var newSidxMapping = {};
47062 
47063     for (var uri in playlists) {
47064       var playlist = playlists[uri];
47065       var currentSidxInfo = playlist.sidx;
47066 
47067       if (currentSidxInfo) {
47068         var key = generateSidxKey(currentSidxInfo);
47069 
47070         if (!oldSidxMapping[key]) {
47071           break;
47072         }
47073 
47074         var savedSidxInfo = oldSidxMapping[key].sidxInfo;
47075 
47076         if (equivalentSidx(savedSidxInfo, currentSidxInfo)) {
47077           newSidxMapping[key] = oldSidxMapping[key];
47078         }
47079       }
47080     }
47081 
47082     return newSidxMapping;
47083   };
47084   /**
47085    *  A function that filters out changed items as they need to be requested separately.
47086    *
47087    *  The method is exported for testing
47088    *
47089    *  @param {Object} masterXml the mpd XML
47090    *  @param {string} srcUrl the mpd url
47091    *  @param {Date} clientOffset a time difference between server and client (passed through and not used)
47092    *  @param {Object} oldSidxMapping the SIDX to compare against
47093    */
47094 
47095 
47096   var filterChangedSidxMappings = function filterChangedSidxMappings(masterXml, srcUrl, clientOffset, oldSidxMapping) {
47097     // Don't pass current sidx mapping
47098     var master = parse(masterXml, {
47099       manifestUri: srcUrl,
47100       clientOffset: clientOffset
47101     });
47102     var videoSidx = compareSidxEntry(master.playlists, oldSidxMapping);
47103     var mediaGroupSidx = videoSidx;
47104     forEachMediaGroup(master, function (properties, mediaType, groupKey, labelKey) {
47105       if (properties.playlists && properties.playlists.length) {
47106         var playlists = properties.playlists;
47107         mediaGroupSidx = mergeOptions$2(mediaGroupSidx, compareSidxEntry(playlists, oldSidxMapping));
47108       }
47109     });
47110     return mediaGroupSidx;
47111   }; // exported for testing
47112 
47113 
47114   var requestSidx_ = function requestSidx_(sidxRange, playlist, xhr, options, finishProcessingFn) {
47115     var sidxInfo = {
47116       // resolve the segment URL relative to the playlist
47117       uri: resolveManifestRedirect(options.handleManifestRedirects, sidxRange.resolvedUri),
47118       // resolvedUri: sidxRange.resolvedUri,
47119       byterange: sidxRange.byterange,
47120       // the segment's playlist
47121       playlist: playlist
47122     };
47123     var sidxRequestOptions = videojs$1.mergeOptions(sidxInfo, {
47124       responseType: 'arraybuffer',
47125       headers: segmentXhrHeaders(sidxInfo)
47126     });
47127     return xhr(sidxRequestOptions, finishProcessingFn);
47128   };
47129 
47130   var DashPlaylistLoader = function (_EventTarget) {
47131     inherits$2(DashPlaylistLoader, _EventTarget); // DashPlaylistLoader must accept either a src url or a playlist because subsequent
47132     // playlist loader setups from media groups will expect to be able to pass a playlist
47133     // (since there aren't external URLs to media playlists with DASH)
47134 
47135     function DashPlaylistLoader(srcUrlOrPlaylist, hls) {
47136       var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
47137       var masterPlaylistLoader = arguments[3];
47138       classCallCheck$1(this, DashPlaylistLoader);
47139 
47140       var _this = possibleConstructorReturn$1(this, (DashPlaylistLoader.__proto__ || Object.getPrototypeOf(DashPlaylistLoader)).call(this));
47141 
47142       var _options$withCredenti = options.withCredentials,
47143           withCredentials = _options$withCredenti === undefined ? false : _options$withCredenti,
47144           _options$handleManife = options.handleManifestRedirects,
47145           handleManifestRedirects = _options$handleManife === undefined ? false : _options$handleManife;
47146       _this.hls_ = hls;
47147       _this.withCredentials = withCredentials;
47148       _this.handleManifestRedirects = handleManifestRedirects;
47149 
47150       if (!srcUrlOrPlaylist) {
47151         throw new Error('A non-empty playlist URL or playlist is required');
47152       } // event naming?
47153 
47154 
47155       _this.on('minimumUpdatePeriod', function () {
47156         _this.refreshXml_();
47157       }); // live playlist staleness timeout
47158 
47159 
47160       _this.on('mediaupdatetimeout', function () {
47161         _this.refreshMedia_(_this.media().uri);
47162       });
47163 
47164       _this.state = 'HAVE_NOTHING';
47165       _this.loadedPlaylists_ = {}; // initialize the loader state
47166       // The masterPlaylistLoader will be created with a string
47167 
47168       if (typeof srcUrlOrPlaylist === 'string') {
47169         _this.srcUrl = srcUrlOrPlaylist; // TODO: reset sidxMapping between period changes
47170         // once multi-period is refactored
47171 
47172         _this.sidxMapping_ = {};
47173         return possibleConstructorReturn$1(_this);
47174       }
47175 
47176       _this.setupChildLoader(masterPlaylistLoader, srcUrlOrPlaylist);
47177 
47178       return _this;
47179     }
47180 
47181     createClass$1(DashPlaylistLoader, [{
47182       key: 'setupChildLoader',
47183       value: function setupChildLoader(masterPlaylistLoader, playlist) {
47184         this.masterPlaylistLoader_ = masterPlaylistLoader;
47185         this.childPlaylist_ = playlist;
47186       }
47187     }, {
47188       key: 'dispose',
47189       value: function dispose() {
47190         this.stopRequest();
47191         this.loadedPlaylists_ = {};
47192         window$1.clearTimeout(this.minimumUpdatePeriodTimeout_);
47193         window$1.clearTimeout(this.mediaRequest_);
47194         window$1.clearTimeout(this.mediaUpdateTimeout);
47195       }
47196     }, {
47197       key: 'hasPendingRequest',
47198       value: function hasPendingRequest() {
47199         return this.request || this.mediaRequest_;
47200       }
47201     }, {
47202       key: 'stopRequest',
47203       value: function stopRequest() {
47204         if (this.request) {
47205           var oldRequest = this.request;
47206           this.request = null;
47207           oldRequest.onreadystatechange = null;
47208           oldRequest.abort();
47209         }
47210       }
47211     }, {
47212       key: 'sidxRequestFinished_',
47213       value: function sidxRequestFinished_(playlist, master, startingState, doneFn) {
47214         var _this2 = this;
47215 
47216         return function (err, request) {
47217           // disposed
47218           if (!_this2.request) {
47219             return;
47220           } // pending request is cleared
47221 
47222 
47223           _this2.request = null;
47224 
47225           if (err) {
47226             _this2.error = {
47227               status: request.status,
47228               message: 'DASH playlist request error at URL: ' + playlist.uri,
47229               response: request.response,
47230               // MEDIA_ERR_NETWORK
47231               code: 2
47232             };
47233 
47234             if (startingState) {
47235               _this2.state = startingState;
47236             }
47237 
47238             _this2.trigger('error');
47239 
47240             return doneFn(master, null);
47241           }
47242 
47243           var bytes = new Uint8Array(request.response);
47244           var sidx = mp4Inspector.parseSidx(bytes.subarray(8));
47245           return doneFn(master, sidx);
47246         };
47247       }
47248     }, {
47249       key: 'media',
47250       value: function media(playlist) {
47251         var _this3 = this; // getter
47252 
47253 
47254         if (!playlist) {
47255           return this.media_;
47256         } // setter
47257 
47258 
47259         if (this.state === 'HAVE_NOTHING') {
47260           throw new Error('Cannot switch media playlist from ' + this.state);
47261         }
47262 
47263         var startingState = this.state; // find the playlist object if the target playlist has been specified by URI
47264 
47265         if (typeof playlist === 'string') {
47266           if (!this.master.playlists[playlist]) {
47267             throw new Error('Unknown playlist URI: ' + playlist);
47268           }
47269 
47270           playlist = this.master.playlists[playlist];
47271         }
47272 
47273         var mediaChange = !this.media_ || playlist.uri !== this.media_.uri; // switch to previously loaded playlists immediately
47274 
47275         if (mediaChange && this.loadedPlaylists_[playlist.uri] && this.loadedPlaylists_[playlist.uri].endList) {
47276           this.state = 'HAVE_METADATA';
47277           this.media_ = playlist; // trigger media change if the active media has been updated
47278 
47279           if (mediaChange) {
47280             this.trigger('mediachanging');
47281             this.trigger('mediachange');
47282           }
47283 
47284           return;
47285         } // switching to the active playlist is a no-op
47286 
47287 
47288         if (!mediaChange) {
47289           return;
47290         } // switching from an already loaded playlist
47291 
47292 
47293         if (this.media_) {
47294           this.trigger('mediachanging');
47295         }
47296 
47297         if (!playlist.sidx) {
47298           // Continue asynchronously if there is no sidx
47299           // wait one tick to allow haveMaster to run first on a child loader
47300           this.mediaRequest_ = window$1.setTimeout(this.haveMetadata.bind(this, {
47301             startingState: startingState,
47302             playlist: playlist
47303           }), 0); // exit early and don't do sidx work
47304 
47305           return;
47306         } // we have sidx mappings
47307 
47308 
47309         var oldMaster = void 0;
47310         var sidxMapping = void 0; // sidxMapping is used when parsing the masterXml, so store
47311         // it on the masterPlaylistLoader
47312 
47313         if (this.masterPlaylistLoader_) {
47314           oldMaster = this.masterPlaylistLoader_.master;
47315           sidxMapping = this.masterPlaylistLoader_.sidxMapping_;
47316         } else {
47317           oldMaster = this.master;
47318           sidxMapping = this.sidxMapping_;
47319         }
47320 
47321         var sidxKey = generateSidxKey(playlist.sidx);
47322         sidxMapping[sidxKey] = {
47323           sidxInfo: playlist.sidx
47324         };
47325         this.request = requestSidx_(playlist.sidx, playlist, this.hls_.xhr, {
47326           handleManifestRedirects: this.handleManifestRedirects
47327         }, this.sidxRequestFinished_(playlist, oldMaster, startingState, function (newMaster, sidx) {
47328           if (!newMaster || !sidx) {
47329             throw new Error('failed to request sidx');
47330           } // update loader's sidxMapping with parsed sidx box
47331 
47332 
47333           sidxMapping[sidxKey].sidx = sidx; // everything is ready just continue to haveMetadata
47334 
47335           _this3.haveMetadata({
47336             startingState: startingState,
47337             playlist: newMaster.playlists[playlist.uri]
47338           });
47339         }));
47340       }
47341     }, {
47342       key: 'haveMetadata',
47343       value: function haveMetadata(_ref) {
47344         var startingState = _ref.startingState,
47345             playlist = _ref.playlist;
47346         this.state = 'HAVE_METADATA';
47347         this.loadedPlaylists_[playlist.uri] = playlist;
47348         this.mediaRequest_ = null; // This will trigger loadedplaylist
47349 
47350         this.refreshMedia_(playlist.uri);
vendor: 10,203 bytes, lines 47350-47637
47350 // fire loadedmetadata the first time a media playlist is loaded
47351         // to resolve setup of media groups
47352 
47353         if (startingState === 'HAVE_MASTER') {
47354           this.trigger('loadedmetadata');
47355         } else {
47356           // trigger media change if the active media has been updated
47357           this.trigger('mediachange');
47358         }
47359       }
47360     }, {
47361       key: 'pause',
47362       value: function pause() {
47363         this.stopRequest();
47364         window$1.clearTimeout(this.mediaUpdateTimeout);
47365         window$1.clearTimeout(this.minimumUpdatePeriodTimeout_);
47366 
47367         if (this.state === 'HAVE_NOTHING') {
47368           // If we pause the loader before any data has been retrieved, its as if we never
47369           // started, so reset to an unstarted state.
47370           this.started = false;
47371         }
47372       }
47373     }, {
47374       key: 'load',
47375       value: function load(isFinalRendition) {
47376         var _this4 = this;
47377 
47378         window$1.clearTimeout(this.mediaUpdateTimeout);
47379         window$1.clearTimeout(this.minimumUpdatePeriodTimeout_);
47380         var media = this.media();
47381 
47382         if (isFinalRendition) {
47383           var delay = media ? media.targetDuration / 2 * 1000 : 5 * 1000;
47384           this.mediaUpdateTimeout = window$1.setTimeout(function () {
47385             return _this4.load();
47386           }, delay);
47387           return;
47388         } // because the playlists are internal to the manifest, load should either load the
47389         // main manifest, or do nothing but trigger an event
47390 
47391 
47392         if (!this.started) {
47393           this.start();
47394           return;
47395         }
47396 
47397         this.trigger('loadedplaylist');
47398       }
47399       /**
47400        * Parses the master xml string and updates playlist uri references
47401        *
47402        * @return {Object}
47403        *         The parsed mpd manifest object
47404        */
47405 
47406     }, {
47407       key: 'parseMasterXml',
47408       value: function parseMasterXml() {
47409         var master = parse(this.masterXml_, {
47410           manifestUri: this.srcUrl,
47411           clientOffset: this.clientOffset_,
47412           sidxMapping: this.sidxMapping_
47413         });
47414         master.uri = this.srcUrl; // Set up phony URIs for the playlists since we won't have external URIs for DASH
47415         // but reference playlists by their URI throughout the project
47416         // TODO: Should we create the dummy uris in mpd-parser as well (leaning towards yes).
47417 
47418         for (var i = 0; i < master.playlists.length; i++) {
47419           var phonyUri = 'placeholder-uri-' + i;
47420           master.playlists[i].uri = phonyUri; // set up by URI references
47421 
47422           master.playlists[phonyUri] = master.playlists[i];
47423         } // set up phony URIs for the media group playlists since we won't have external
47424         // URIs for DASH but reference playlists by their URI throughout the project
47425 
47426 
47427         forEachMediaGroup(master, function (properties, mediaType, groupKey, labelKey) {
47428           if (properties.playlists && properties.playlists.length) {
47429             var _phonyUri = 'placeholder-uri-' + mediaType + '-' + groupKey + '-' + labelKey;
47430 
47431             properties.playlists[0].uri = _phonyUri; // setup URI references
47432 
47433             master.playlists[_phonyUri] = properties.playlists[0];
47434           }
47435         });
47436         setupMediaPlaylists(master);
47437         resolveMediaGroupUris(master);
47438         return master;
47439       }
47440     }, {
47441       key: 'start',
47442       value: function start() {
47443         var _this5 = this;
47444 
47445         this.started = true; // We don't need to request the master manifest again
47446         // Call this asynchronously to match the xhr request behavior below
47447 
47448         if (this.masterPlaylistLoader_) {
47449           this.mediaRequest_ = window$1.setTimeout(this.haveMaster_.bind(this), 0);
47450           return;
47451         } // request the specified URL
47452 
47453 
47454         this.request = this.hls_.xhr({
47455           uri: this.srcUrl,
47456           withCredentials: this.withCredentials
47457         }, function (error, req) {
47458           // disposed
47459           if (!_this5.request) {
47460             return;
47461           } // clear the loader's request reference
47462 
47463 
47464           _this5.request = null;
47465 
47466           if (error) {
47467             _this5.error = {
47468               status: req.status,
47469               message: 'DASH playlist request error at URL: ' + _this5.srcUrl,
47470               responseText: req.responseText,
47471               // MEDIA_ERR_NETWORK
47472               code: 2
47473             };
47474 
47475             if (_this5.state === 'HAVE_NOTHING') {
47476               _this5.started = false;
47477             }
47478 
47479             return _this5.trigger('error');
47480           }
47481 
47482           _this5.masterXml_ = req.responseText;
47483 
47484           if (req.responseHeaders && req.responseHeaders.date) {
47485             _this5.masterLoaded_ = Date.parse(req.responseHeaders.date);
47486           } else {
47487             _this5.masterLoaded_ = Date.now();
47488           }
47489 
47490           _this5.srcUrl = resolveManifestRedirect(_this5.handleManifestRedirects, _this5.srcUrl, req);
47491 
47492           _this5.syncClientServerClock_(_this5.onClientServerClockSync_.bind(_this5));
47493         });
47494       }
47495       /**
47496        * Parses the master xml for UTCTiming node to sync the client clock to the server
47497        * clock. If the UTCTiming node requires a HEAD or GET request, that request is made.
47498        *
47499        * @param {Function} done
47500        *        Function to call when clock sync has completed
47501        */
47502 
47503     }, {
47504       key: 'syncClientServerClock_',
47505       value: function syncClientServerClock_(done) {
47506         var _this6 = this;
47507 
47508         var utcTiming = parseUTCTiming(this.masterXml_); // No UTCTiming element found in the mpd. Use Date header from mpd request as the
47509         // server clock
47510 
47511         if (utcTiming === null) {
47512           this.clientOffset_ = this.masterLoaded_ - Date.now();
47513           return done();
47514         }
47515 
47516         if (utcTiming.method === 'DIRECT') {
47517           this.clientOffset_ = utcTiming.value - Date.now();
47518           return done();
47519         }
47520 
47521         this.request = this.hls_.xhr({
47522           uri: resolveUrl$1(this.srcUrl, utcTiming.value),
47523           method: utcTiming.method,
47524           withCredentials: this.withCredentials
47525         }, function (error, req) {
47526           // disposed
47527           if (!_this6.request) {
47528             return;
47529           }
47530 
47531           if (error) {
47532             // sync request failed, fall back to using date header from mpd
47533             // TODO: log warning
47534             _this6.clientOffset_ = _this6.masterLoaded_ - Date.now();
47535             return done();
47536           }
47537 
47538           var serverTime = void 0;
47539 
47540           if (utcTiming.method === 'HEAD') {
47541             if (!req.responseHeaders || !req.responseHeaders.date) {
47542               // expected date header not preset, fall back to using date header from mpd
47543               // TODO: log warning
47544               serverTime = _this6.masterLoaded_;
47545             } else {
47546               serverTime = Date.parse(req.responseHeaders.date);
47547             }
47548           } else {
47549             serverTime = Date.parse(req.responseText);
47550           }
47551 
47552           _this6.clientOffset_ = serverTime - Date.now();
47553           done();
47554         });
47555       }
47556     }, {
47557       key: 'haveMaster_',
47558       value: function haveMaster_() {
47559         this.state = 'HAVE_MASTER'; // clear media request
47560 
47561         this.mediaRequest_ = null;
47562 
47563         if (!this.masterPlaylistLoader_) {
47564           this.master = this.parseMasterXml(); // We have the master playlist at this point, so
47565           // trigger this to allow MasterPlaylistController
47566           // to make an initial playlist selection
47567 
47568           this.trigger('loadedplaylist');
47569         } else if (!this.media_) {
47570           // no media playlist was specifically selected so select
47571           // the one the child playlist loader was created with
47572           this.media(this.childPlaylist_);
47573         }
47574       }
47575       /**
47576        * Handler for after client/server clock synchronization has happened. Sets up
47577        * xml refresh timer if specificed by the manifest.
47578        */
47579 
47580     }, {
47581       key: 'onClientServerClockSync_',
47582       value: function onClientServerClockSync_() {
47583         var _this7 = this;
47584 
47585         this.haveMaster_();
47586 
47587         if (!this.hasPendingRequest() && !this.media_) {
47588           this.media(this.master.playlists[0]);
47589         } // TODO: minimumUpdatePeriod can have a value of 0. Currently the manifest will not
47590         // be refreshed when this is the case. The inter-op guide says that when the
47591         // minimumUpdatePeriod is 0, the manifest should outline all currently available
47592         // segments, but future segments may require an update. I think a good solution
47593         // would be to update the manifest at the same rate that the media playlists
47594         // are "refreshed", i.e. every targetDuration.
47595 
47596 
47597         if (this.master && this.master.minimumUpdatePeriod) {
47598           this.minimumUpdatePeriodTimeout_ = window$1.setTimeout(function () {
47599             _this7.trigger('minimumUpdatePeriod');
47600           }, this.master.minimumUpdatePeriod);
47601         }
47602       }
47603       /**
47604        * Sends request to refresh the master xml and updates the parsed master manifest
47605        * TODO: Does the client offset need to be recalculated when the xml is refreshed?
47606        */
47607 
47608     }, {
47609       key: 'refreshXml_',
47610       value: function refreshXml_() {
47611         var _this8 = this; // The srcUrl here *may* need to pass through handleManifestsRedirects when
47612         // sidx is implemented
47613 
47614 
47615         this.request = this.hls_.xhr({
47616           uri: this.srcUrl,
47617           withCredentials: this.withCredentials
47618         }, function (error, req) {
47619           // disposed
47620           if (!_this8.request) {
47621             return;
47622           } // clear the loader's request reference
47623 
47624 
47625           _this8.request = null;
47626 
47627           if (error) {
47628             _this8.error = {
47629               status: req.status,
47630               message: 'DASH playlist request error at URL: ' + _this8.srcUrl,
47631               responseText: req.responseText,
47632               // MEDIA_ERR_NETWORK
47633               code: 2
47634             };
47635 
47636             if (_this8.state === 'HAVE_NOTHING') {
47637               _this8.started = false;
47638             }
47639 
47640             return _this8.trigger('error');
47641           }
47642 
47643           _this8.masterXml_ = req.responseText; // This will filter out updated sidx info from the mapping
47644 
47645           _this8.sidxMapping_ = filterChangedSidxMappings(_this8.masterXml_, _this8.srcUrl, _this8.clientOffset_, _this8.sidxMapping_);
47646 
47647           var master = _this8.parseMasterXml();
47648 
47649           var updatedMaster = updateMaster$1(_this8.master, master);
47650 
47651           var currentSidxInfo = _this8.media().sidx;
47652 
47653           if (updatedMaster) {
47654             if (currentSidxInfo) {
47655               var sidxKey = generateSidxKey(currentSidxInfo); // the sidx was updated, so the previous mapping was removed
47656 
47657               if (!_this8.sidxMapping_[sidxKey]) {
47658                 var playlist = _this8.media();
47659 
47660                 _this8.request = requestSidx_(playlist.sidx, playlist, _this8.hls_.xhr, {
47661                   handleManifestRedirects: _this8.handleManifestRedirects
47662                 }, _this8.sidxRequestFinished_(playlist, master, _this8.state, function (newMaster, sidx) {
47663                   if (!newMaster || !sidx) {
47664                     throw new Error('failed to request sidx on minimumUpdatePeriod');
47665                   } // update loader's sidxMapping with parsed sidx box
47666 
47667 
47668                   _this8.sidxMapping_[sidxKey].sidx = sidx;
47669                   _this8.minimumUpdatePeriodTimeout_ = window$1.setTimeout(function () {
47670                     _this8.trigger('minimumUpdatePeriod');
47671                   }, _this8.master.minimumUpdatePeriod); // TODO: do we need to reload the current playlist?
47672 
47673                   _this8.refreshMedia_(_this8.media().uri);
47674 
47675                   return;
47676                 }));
47677               }
47678             } else {
47679               _this8.master = updatedMaster;
47680             }
47681           }
47682 
47683           _this8.minimumUpdatePeriodTimeout_ = window$1.setTimeout(function () {
47684             _this8.trigger('minimumUpdatePeriod');
47685           }, _this8.master.minimumUpdatePeriod);
47686         });
47687       }
47688       /**
47689        * Refreshes the media playlist by re-parsing the master xml and updating playlist
47690        * references. If this is an alternate loader, the updated parsed manifest is retrieved
47691        * from the master loader.
47692        */
47693 
47694     }, {
47695       key: 'refreshMedia_',
47696       value: function refreshMedia_(mediaUri) {
47697         var _this9 = this;
47698 
47699         if (!mediaUri) {
47700           throw new Error('refreshMedia_ must take a media uri');
47701         }
47702 
47703         var oldMaster = void 0;
47704         var newMaster = void 0;
47705 
47706         if (this.masterPlaylistLoader_) {
47707           oldMaster = this.masterPlaylistLoader_.master;
47708           newMaster = this.masterPlaylistLoader_.parseMasterXml();
47709         } else {
47710           oldMaster = this.master;
47711           newMaster = this.parseMasterXml();
47712         }
47713 
47714         var updatedMaster = updateMaster$1(oldMaster, newMaster);
47715 
47716         if (updatedMaster) {
47717           if (this.masterPlaylistLoader_) {
47718             this.masterPlaylistLoader_.master = updatedMaster;
47719           } else {
47720             this.master = updatedMaster;
47721           }
47722 
47723           this.media_ = updatedMaster.playlists[mediaUri];
47724         } else {
47725           this.media_ = newMaster.playlists[mediaUri];
47726           this.trigger('playlistunchanged');
47727         }
47728 
47729         if (!this.media().endList) {
47730           this.mediaUpdateTimeout = window$1.setTimeout(function () {
47731             _this9.trigger('mediaupdatetimeout');
47732           }, refreshDelay(this.media(), !!updatedMaster));
47733         }
47734 
47735         this.trigger('loadedplaylist');
47736       }
47737     }]);
47738     return DashPlaylistLoader;
47739   }(EventTarget$1$1);
47740 
47741   var logger = function logger(source) {
47742     if (videojs$1.log.debug) {
47743       return videojs$1.log.debug.bind(videojs$1, 'VHS:', source + ' >');
47744     }
47745 
47746     return function () {};
47747   };
47748 
47749   function noop$1() {}
47750   /**
47751    * @file source-updater.js
47752    */
47753 
47754   /**
47755    * A queue of callbacks to be serialized and applied when a
47756    * MediaSource and its associated SourceBuffers are not in the
47757    * updating state. It is used by the segment loader to update the
47758    * underlying SourceBuffers when new data is loaded, for instance.
47759    *
47760    * @class SourceUpdater
47761    * @param {MediaSource} mediaSource the MediaSource to create the
47762    * SourceBuffer from
47763    * @param {String} mimeType the desired MIME type of the underlying
47764    * SourceBuffer
47765    * @param {Object} sourceBufferEmitter an event emitter that fires when a source buffer is
47766    * added to the media source
47767    */
47768 
47769 
47770   var SourceUpdater = function () {
47771     function SourceUpdater(mediaSource, mimeType, type, sourceBufferEmitter) {
47772       classCallCheck$1(this, SourceUpdater);
47773       this.callbacks_ = [];
47774       this.pendingCallback_ = null;
47775       this.timestampOffset_ = 0;
47776       this.mediaSource = mediaSource;
47777       this.processedAppend_ = false;
vendor: 13,384 bytes, lines 47778-48197
47778       this.type_ = type;
47779       this.mimeType_ = mimeType;
47780       this.logger_ = logger('SourceUpdater[' + type + '][' + mimeType + ']');
47781 
47782       if (mediaSource.readyState === 'closed') {
47783         mediaSource.addEventListener('sourceopen', this.createSourceBuffer_.bind(this, mimeType, sourceBufferEmitter));
47784       } else {
47785         this.createSourceBuffer_(mimeType, sourceBufferEmitter);
47786       }
47787     }
47788 
47789     createClass$1(SourceUpdater, [{
47790       key: 'createSourceBuffer_',
47791       value: function createSourceBuffer_(mimeType, sourceBufferEmitter) {
47792         var _this = this;
47793 
47794         this.sourceBuffer_ = this.mediaSource.addSourceBuffer(mimeType);
47795         this.logger_('created SourceBuffer');
47796 
47797         if (sourceBufferEmitter) {
47798           sourceBufferEmitter.trigger('sourcebufferadded');
47799 
47800           if (this.mediaSource.sourceBuffers.length < 2) {
47801             // There's another source buffer we must wait for before we can start updating
47802             // our own (or else we can get into a bad state, i.e., appending video/audio data
47803             // before the other video/audio source buffer is available and leading to a video
47804             // or audio only buffer).
47805             sourceBufferEmitter.on('sourcebufferadded', function () {
47806               _this.start_();
47807             });
47808             return;
47809           }
47810         }
47811 
47812         this.start_();
47813       }
47814     }, {
47815       key: 'start_',
47816       value: function start_() {
47817         var _this2 = this;
47818 
47819         this.started_ = true; // run completion handlers and process callbacks as updateend
47820         // events fire
47821 
47822         this.onUpdateendCallback_ = function () {
47823           var pendingCallback = _this2.pendingCallback_;
47824           _this2.pendingCallback_ = null;
47825           _this2.sourceBuffer_.removing = false;
47826 
47827           _this2.logger_('buffered [' + printableRange(_this2.buffered()) + ']');
47828 
47829           if (pendingCallback) {
47830             pendingCallback();
47831           }
47832 
47833           _this2.runCallback_();
47834         };
47835 
47836         this.sourceBuffer_.addEventListener('updateend', this.onUpdateendCallback_);
47837         this.runCallback_();
47838       }
47839       /**
47840        * Aborts the current segment and resets the segment parser.
47841        *
47842        * @param {Function} done function to call when done
47843        * @see http://w3c.github.io/media-source/#widl-SourceBuffer-abort-void
47844        */
47845 
47846     }, {
47847       key: 'abort',
47848       value: function abort(done) {
47849         var _this3 = this;
47850 
47851         if (this.processedAppend_) {
47852           this.queueCallback_(function () {
47853             _this3.sourceBuffer_.abort();
47854           }, done);
47855         }
47856       }
47857       /**
47858        * Queue an update to append an ArrayBuffer.
47859        *
47860        * @param {ArrayBuffer} bytes
47861        * @param {Function} done the function to call when done
47862        * @see http://www.w3.org/TR/media-source/#widl-SourceBuffer-appendBuffer-void-ArrayBuffer-data
47863        */
47864 
47865     }, {
47866       key: 'appendBuffer',
47867       value: function appendBuffer(config, done) {
47868         var _this4 = this;
47869 
47870         this.processedAppend_ = true;
47871         this.queueCallback_(function () {
47872           if (config.videoSegmentTimingInfoCallback) {
47873             _this4.sourceBuffer_.addEventListener('videoSegmentTimingInfo', config.videoSegmentTimingInfoCallback);
47874           }
47875 
47876           _this4.sourceBuffer_.appendBuffer(config.bytes);
47877         }, function () {
47878           if (config.videoSegmentTimingInfoCallback) {
47879             _this4.sourceBuffer_.removeEventListener('videoSegmentTimingInfo', config.videoSegmentTimingInfoCallback);
47880           }
47881 
47882           done();
47883         });
47884       }
47885       /**
47886        * Indicates what TimeRanges are buffered in the managed SourceBuffer.
47887        *
47888        * @see http://www.w3.org/TR/media-source/#widl-SourceBuffer-buffered
47889        */
47890 
47891     }, {
47892       key: 'buffered',
47893       value: function buffered() {
47894         if (!this.sourceBuffer_) {
47895           return videojs$1.createTimeRanges();
47896         }
47897 
47898         return this.sourceBuffer_.buffered;
47899       }
47900       /**
47901        * Queue an update to remove a time range from the buffer.
47902        *
47903        * @param {Number} start where to start the removal
47904        * @param {Number} end where to end the removal
47905        * @param {Function} [done=noop] optional callback to be executed when the remove
47906        * operation is complete
47907        * @see http://www.w3.org/TR/media-source/#widl-SourceBuffer-remove-void-double-start-unrestricted-double-end
47908        */
47909 
47910     }, {
47911       key: 'remove',
47912       value: function remove(start, end) {
47913         var _this5 = this;
47914 
47915         var done = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : noop$1;
47916 
47917         if (this.processedAppend_) {
47918           this.queueCallback_(function () {
47919             _this5.logger_('remove [' + start + ' => ' + end + ']');
47920 
47921             _this5.sourceBuffer_.removing = true;
47922 
47923             _this5.sourceBuffer_.remove(start, end);
47924           }, done);
47925         }
47926       }
47927       /**
47928        * Whether the underlying sourceBuffer is updating or not
47929        *
47930        * @return {Boolean} the updating status of the SourceBuffer
47931        */
47932 
47933     }, {
47934       key: 'updating',
47935       value: function updating() {
47936         // we are updating if the sourcebuffer is updating or
47937         return !this.sourceBuffer_ || this.sourceBuffer_.updating || // if we have a pending callback that is not our internal noop
47938         !!this.pendingCallback_ && this.pendingCallback_ !== noop$1;
47939       }
47940       /**
47941        * Set/get the timestampoffset on the SourceBuffer
47942        *
47943        * @return {Number} the timestamp offset
47944        */
47945 
47946     }, {
47947       key: 'timestampOffset',
47948       value: function timestampOffset(offset) {
47949         var _this6 = this;
47950 
47951         if (typeof offset !== 'undefined') {
47952           this.queueCallback_(function () {
47953             _this6.sourceBuffer_.timestampOffset = offset;
47954 
47955             _this6.runCallback_();
47956           });
47957           this.timestampOffset_ = offset;
47958         }
47959 
47960         return this.timestampOffset_;
47961       }
47962       /**
47963        * Queue a callback to run
47964        */
47965 
47966     }, {
47967       key: 'queueCallback_',
47968       value: function queueCallback_(callback, done) {
47969         this.callbacks_.push([callback.bind(this), done]);
47970         this.runCallback_();
47971       }
47972       /**
47973        * Run a queued callback
47974        */
47975 
47976     }, {
47977       key: 'runCallback_',
47978       value: function runCallback_() {
47979         var callbacks = void 0;
47980 
47981         if (!this.updating() && this.callbacks_.length && this.started_) {
47982           callbacks = this.callbacks_.shift();
47983           this.pendingCallback_ = callbacks[1];
47984           callbacks[0]();
47985         }
47986       }
47987       /**
47988        * dispose of the source updater and the underlying sourceBuffer
47989        */
47990 
47991     }, {
47992       key: 'dispose',
47993       value: function dispose() {
47994         var _this7 = this;
47995 
47996         var disposeFn = function disposeFn() {
47997           if (_this7.sourceBuffer_ && _this7.mediaSource.readyState === 'open') {
47998             _this7.sourceBuffer_.abort();
47999           }
48000 
48001           _this7.sourceBuffer_.removeEventListener('updateend', disposeFn);
48002         };
48003 
48004         this.sourceBuffer_.removeEventListener('updateend', this.onUpdateendCallback_);
48005 
48006         if (this.sourceBuffer_.removing) {
48007           this.sourceBuffer_.addEventListener('updateend', disposeFn);
48008         } else {
48009           disposeFn();
48010         }
48011       }
48012     }]);
48013     return SourceUpdater;
48014   }();
48015 
48016   var Config = {
48017     GOAL_BUFFER_LENGTH: 30,
48018     MAX_GOAL_BUFFER_LENGTH: 60,
48019     GOAL_BUFFER_LENGTH_RATE: 1,
48020     // 0.5 MB/s
48021     INITIAL_BANDWIDTH: 4194304,
48022     // A fudge factor to apply to advertised playlist bitrates to account for
48023     // temporary flucations in client bandwidth
48024     BANDWIDTH_VARIANCE: 1.2,
48025     // How much of the buffer must be filled before we consider upswitching
48026     BUFFER_LOW_WATER_LINE: 0,
48027     MAX_BUFFER_LOW_WATER_LINE: 30,
48028     BUFFER_LOW_WATER_LINE_RATE: 1
48029   };
48030   var REQUEST_ERRORS = {
48031     FAILURE: 2,
48032     TIMEOUT: -101,
48033     ABORTED: -102
48034   };
48035   /**
48036    * Abort all requests
48037    *
48038    * @param {Object} activeXhrs - an object that tracks all XHR requests
48039    */
48040 
48041   var abortAll = function abortAll(activeXhrs) {
48042     activeXhrs.forEach(function (xhr) {
48043       xhr.abort();
48044     });
48045   };
48046   /**
48047    * Gather important bandwidth stats once a request has completed
48048    *
48049    * @param {Object} request - the XHR request from which to gather stats
48050    */
48051 
48052 
48053   var getRequestStats = function getRequestStats(request) {
48054     return {
48055       bandwidth: request.bandwidth,
48056       bytesReceived: request.bytesReceived || 0,
48057       roundTripTime: request.roundTripTime || 0
48058     };
48059   };
48060   /**
48061    * If possible gather bandwidth stats as a request is in
48062    * progress
48063    *
48064    * @param {Event} progressEvent - an event object from an XHR's progress event
48065    */
48066 
48067 
48068   var getProgressStats = function getProgressStats(progressEvent) {
48069     var request = progressEvent.target;
48070     var roundTripTime = Date.now() - request.requestTime;
48071     var stats = {
48072       bandwidth: Infinity,
48073       bytesReceived: 0,
48074       roundTripTime: roundTripTime || 0
48075     };
48076     stats.bytesReceived = progressEvent.loaded; // This can result in Infinity if stats.roundTripTime is 0 but that is ok
48077     // because we should only use bandwidth stats on progress to determine when
48078     // abort a request early due to insufficient bandwidth
48079 
48080     stats.bandwidth = Math.floor(stats.bytesReceived / stats.roundTripTime * 8 * 1000);
48081     return stats;
48082   };
48083   /**
48084    * Handle all error conditions in one place and return an object
48085    * with all the information
48086    *
48087    * @param {Error|null} error - if non-null signals an error occured with the XHR
48088    * @param {Object} request -  the XHR request that possibly generated the error
48089    */
48090 
48091 
48092   var handleErrors = function handleErrors(error, request) {
48093     if (request.timedout) {
48094       return {
48095         status: request.status,
48096         message: 'HLS request timed-out at URL: ' + request.uri,
48097         code: REQUEST_ERRORS.TIMEOUT,
48098         xhr: request
48099       };
48100     }
48101 
48102     if (request.aborted) {
48103       return {
48104         status: request.status,
48105         message: 'HLS request aborted at URL: ' + request.uri,
48106         code: REQUEST_ERRORS.ABORTED,
48107         xhr: request
48108       };
48109     }
48110 
48111     if (error) {
48112       return {
48113         status: request.status,
48114         message: 'HLS request errored at URL: ' + request.uri,
48115         code: REQUEST_ERRORS.FAILURE,
48116         xhr: request
48117       };
48118     }
48119 
48120     return null;
48121   };
48122   /**
48123    * Handle responses for key data and convert the key data to the correct format
48124    * for the decryption step later
48125    *
48126    * @param {Object} segment - a simplified copy of the segmentInfo object
48127    *                           from SegmentLoader
48128    * @param {Function} finishProcessingFn - a callback to execute to continue processing
48129    *                                        this request
48130    */
48131 
48132 
48133   var handleKeyResponse = function handleKeyResponse(segment, finishProcessingFn) {
48134     return function (error, request) {
48135       var response = request.response;
48136       var errorObj = handleErrors(error, request);
48137 
48138       if (errorObj) {
48139         return finishProcessingFn(errorObj, segment);
48140       }
48141 
48142       if (response.byteLength !== 16) {
48143         return finishProcessingFn({
48144           status: request.status,
48145           message: 'Invalid HLS key at URL: ' + request.uri,
48146           code: REQUEST_ERRORS.FAILURE,
48147           xhr: request
48148         }, segment);
48149       }
48150 
48151       var view = new DataView(response);
48152       segment.key.bytes = new Uint32Array([view.getUint32(0), view.getUint32(4), view.getUint32(8), view.getUint32(12)]);
48153       return finishProcessingFn(null, segment);
48154     };
48155   };
48156   /**
48157    * Handle init-segment responses
48158    *
48159    * @param {Object} segment - a simplified copy of the segmentInfo object
48160    *                           from SegmentLoader
48161    * @param {Function} finishProcessingFn - a callback to execute to continue processing
48162    *                                        this request
48163    */
48164 
48165 
48166   var handleInitSegmentResponse = function handleInitSegmentResponse(segment, captionParser, finishProcessingFn) {
48167     return function (error, request) {
48168       var response = request.response;
48169       var errorObj = handleErrors(error, request);
48170 
48171       if (errorObj) {
48172         return finishProcessingFn(errorObj, segment);
48173       } // stop processing if received empty content
48174 
48175 
48176       if (response.byteLength === 0) {
48177         return finishProcessingFn({
48178           status: request.status,
48179           message: 'Empty HLS segment content at URL: ' + request.uri,
48180           code: REQUEST_ERRORS.FAILURE,
48181           xhr: request
48182         }, segment);
48183       }
48184 
48185       segment.map.bytes = new Uint8Array(request.response); // Initialize CaptionParser if it hasn't been yet
48186 
48187       if (captionParser && !captionParser.isInitialized()) {
48188         captionParser.init();
48189       }
48190 
48191       segment.map.timescales = probe.timescale(segment.map.bytes);
48192       segment.map.videoTrackIds = probe.videoTrackIds(segment.map.bytes);
48193       return finishProcessingFn(null, segment);
48194     };
48195   };
48196   /**
48197    * Response handler for segment-requests being sure to set the correct
48198    * property depending on whether the segment is encryped or not
48199    * Also records and keeps track of stats that are used for ABR purposes
48200    *
48201    * @param {Object} segment - a simplified copy of the segmentInfo object
48202    *                           from SegmentLoader
48203    * @param {Function} finishProcessingFn - a callback to execute to continue processing
48204    *                                        this request
48205    */
48206 
48207 
48208   var handleSegmentResponse = function handleSegmentResponse(segment, captionParser, finishProcessingFn) {
48209     return function (error, request) {
48210       var response = request.response;
48211       var errorObj = handleErrors(error, request);
48212       var parsed = void 0;
48213 
48214       if (errorObj) {
48215         return finishProcessingFn(errorObj, segment);
48216       } // stop processing if received empty content
48217 
48218 
48219       if (response.byteLength === 0) {
48220         return finishProcessingFn({
48221           status: request.status,
48222           message: 'Empty HLS segment content at URL: ' + request.uri,
48223           code: REQUEST_ERRORS.FAILURE,
48224           xhr: request
48225         }, segment);
48226       }
48227 
48228       segment.stats = getRequestStats(request);
48229 
48230       if (segment.key) {
48231         segment.encryptedBytes = new Uint8Array(request.response);
48232       } else {
48233         segment.bytes = new Uint8Array(request.response);
48234       } // This is likely an FMP4 and has the init segment.
48235       // Run through the CaptionParser in case there are captions.
48236 
48237 
48238       if (captionParser && segment.map && segment.map.bytes) {
48239         // Initialize CaptionParser if it hasn't been yet
48240         if (!captionParser.isInitialized()) {
48241           captionParser.init();
48242         }
48243 
48244         parsed = captionParser.parse(segment.bytes, segment.map.videoTrackIds, segment.map.timescales);
48245 
48246         if (parsed && parsed.captions) {
48247           segment.captionStreams = parsed.captionStreams;
48248           segment.fmp4Captions = parsed.captions;
48249         }
48250       }
48251 
48252       return finishProcessingFn(null, segment);
48253     };
48254   };
48255   /**
48256    * Decrypt the segment via the decryption web worker
48257    *
48258    * @param {WebWorker} decrypter - a WebWorker interface to AES-128 decryption routines
48259    * @param {Object} segment - a simplified copy of the segmentInfo object
48260    *                           from SegmentLoader
48261    * @param {Function} doneFn - a callback that is executed after decryption has completed
48262    */
48263 
48264 
48265   var decryptSegment = function decryptSegment(decrypter, segment, doneFn) {
48266     var decryptionHandler = function decryptionHandler(event) {
48267       if (event.data.source === segment.requestId) {
48268         decrypter.removeEventListener('message', decryptionHandler);
48269         var decrypted = event.data.decrypted;
48270         segment.bytes = new Uint8Array(decrypted.bytes, decrypted.byteOffset, decrypted.byteLength);
48271         return doneFn(null, segment);
48272       }
48273     };
48274 
48275     decrypter.addEventListener('message', decryptionHandler);
48276     var keyBytes = void 0;
48277 
48278     if (segment.key.bytes.slice) {
48279       keyBytes = segment.key.bytes.slice();
48280     } else {
48281       keyBytes = new Uint32Array(Array.prototype.slice.call(segment.key.bytes));
48282     } // this is an encrypted segment
48283     // incrementally decrypt the segment
48284 
48285 
48286     decrypter.postMessage(createTransferableMessage({
48287       source: segment.requestId,
48288       encrypted: segment.encryptedBytes,
48289       key: keyBytes,
48290       iv: segment.key.iv
48291     }), [segment.encryptedBytes.buffer, keyBytes.buffer]);
48292   };
48293   /**
48294    * This function waits for all XHRs to finish (with either success or failure)
48295    * before continueing processing via it's callback. The function gathers errors
48296    * from each request into a single errors array so that the error status for
48297    * each request can be examined later.
48298    *
48299    * @param {Object} activeXhrs - an object that tracks all XHR requests
48300    * @param {WebWorker} decrypter - a WebWorker interface to AES-128 decryption routines
48301    * @param {Function} doneFn - a callback that is executed after all resources have been
48302    *                            downloaded and any decryption completed
48303    */
48304 
48305 
48306   var waitForCompletion = function waitForCompletion(activeXhrs, decrypter, doneFn) {
48307     var count = 0;
48308     var didError = false;
48309     return function (error, segment) {
48310       if (didError) {
48311         return;
48312       }
48313 
48314       if (error) {
48315         didError = true; // If there are errors, we have to abort any outstanding requests
48316 
48317         abortAll(activeXhrs); // Even though the requests above are aborted, and in theory we could wait until we
48318         // handle the aborted events from those requests, there are some cases where we may
48319         // never get an aborted event. For instance, if the network connection is lost and
48320         // there were two requests, the first may have triggered an error immediately, while
48321         // the second request remains unsent. In that case, the aborted algorithm will not
48322         // trigger an abort: see https://xhr.spec.whatwg.org/#the-abort()-method
48323         //
48324         // We also can't rely on the ready state of the XHR, since the request that
48325         // triggered the connection error may also show as a ready state of 0 (unsent).
48326         // Therefore, we have to finish this group of requests immediately after the first
48327         // seen error.
48328 
48329         return doneFn(error, segment);
48330       }
48331 
48332       count += 1;
48333 
48334       if (count === activeXhrs.length) {
48335         // Keep track of when *all* of the requests have completed
48336         segment.endOfAllRequests = Date.now();
48337 
48338         if (segment.encryptedBytes) {
48339           return decryptSegment(decrypter, segment, doneFn);
48340         } // Otherwise, everything is ready just continue
48341 
48342 
48343         return doneFn(null, segment);
48344       }
48345     };
48346   };
48347   /**
48348    * Simple progress event callback handler that gathers some stats before
48349    * executing a provided callback with the `segment` object
48350    *
48351    * @param {Object} segment - a simplified copy of the segmentInfo object
48352    *                           from SegmentLoader
48353    * @param {Function} progressFn - a callback that is executed each time a progress event
48354    *                                is received
48355    * @param {Event} event - the progress event object from XMLHttpRequest
48356    */
48357 
48358 
48359   var handleProgress = function handleProgress(segment, progressFn) {
48360     return function (event) {
48361       segment.stats = videojs$1.mergeOptions(segment.stats, getProgressStats(event)); // record the time that we receive the first byte of data
48362 
48363       if (!segment.stats.firstBytesReceivedAt && segment.stats.bytesReceived) {
48364         segment.stats.firstBytesReceivedAt = Date.now();
48365       }
48366 
48367       return progressFn(event, segment);
48368     };
48369   };
48370   /**
48371    * Load all resources and does any processing necessary for a media-segment
48372    *
48373    * Features:
48374    *   decrypts the media-segment if it has a key uri and an iv
48375    *   aborts *all* requests if *any* one request fails
48376    *
48377    * The segment object, at minimum, has the following format:
48378    * {
48379    *   resolvedUri: String,
48380    *   [byterange]: {
48381    *     offset: Number,
48382    *     length: Number
48383    *   },
48384    *   [key]: {
48385    *     resolvedUri: String
48386    *     [byterange]: {
48387    *       offset: Number,
48388    *       length: Number
48389    *     },
48390    *     iv: {
48391    *       bytes: Uint32Array
48392    *     }
48393    *   },
48394    *   [map]: {
48395    *     resolvedUri: String,
48396    *     [byterange]: {
48397    *       offset: Number,
48398    *       length: Number
48399    *     },
48400    *     [bytes]: Uint8Array
48401    *   }
48402    * }
48403    * ...where [name] denotes optional properties
48404    *
48405    * @param {Function} xhr - an instance of the xhr wrapper in xhr.js
48406    * @param {Object} xhrOptions - the base options to provide to all xhr requests
48407    * @param {WebWorker} decryptionWorker - a WebWorker interface to AES-128
48408    *                                       decryption routines
48409    * @param {Object} segment - a simplified copy of the segmentInfo object
48410    *                           from SegmentLoader
48411    * @param {Function} progressFn - a callback that receives progress events from the main
48412    *                                segment's xhr request
48413    * @param {Function} doneFn - a callback that is executed only once all requests have
48414    *                            succeeded or failed
48415    * @returns {Function} a function that, when invoked, immediately aborts all
48416    *                     outstanding requests
48417    */
48418 
48419 
48420   var mediaSegmentRequest = function mediaSegmentRequest(xhr, xhrOptions, decryptionWorker, captionParser, segment, progressFn, doneFn) {
48421     var activeXhrs = [];
vendor: 17,376 bytes, lines 48422-48829
48422     var finishProcessingFn = waitForCompletion(activeXhrs, decryptionWorker, doneFn); // optionally, request the decryption key
48423 
48424     if (segment.key && !segment.key.bytes) {
48425       var keyRequestOptions = videojs$1.mergeOptions(xhrOptions, {
48426         uri: segment.key.resolvedUri,
48427         responseType: 'arraybuffer'
48428       });
48429       var keyRequestCallback = handleKeyResponse(segment, finishProcessingFn);
48430       var keyXhr = xhr(keyRequestOptions, keyRequestCallback);
48431       activeXhrs.push(keyXhr);
48432     } // optionally, request the associated media init segment
48433 
48434 
48435     if (segment.map && !segment.map.bytes) {
48436       var initSegmentOptions = videojs$1.mergeOptions(xhrOptions, {
48437         uri: segment.map.resolvedUri,
48438         responseType: 'arraybuffer',
48439         headers: segmentXhrHeaders(segment.map)
48440       });
48441       var initSegmentRequestCallback = handleInitSegmentResponse(segment, captionParser, finishProcessingFn);
48442       var initSegmentXhr = xhr(initSegmentOptions, initSegmentRequestCallback);
48443       activeXhrs.push(initSegmentXhr);
48444     }
48445 
48446     var segmentRequestOptions = videojs$1.mergeOptions(xhrOptions, {
48447       uri: segment.resolvedUri,
48448       responseType: 'arraybuffer',
48449       headers: segmentXhrHeaders(segment)
48450     });
48451     var segmentRequestCallback = handleSegmentResponse(segment, captionParser, finishProcessingFn);
48452     var segmentXhr = xhr(segmentRequestOptions, segmentRequestCallback);
48453     segmentXhr.addEventListener('progress', handleProgress(segment, progressFn));
48454     activeXhrs.push(segmentXhr);
48455     return function () {
48456       return abortAll(activeXhrs);
48457     };
48458   }; // Utilities
48459 
48460   /**
48461    * Returns the CSS value for the specified property on an element
48462    * using `getComputedStyle`. Firefox has a long-standing issue where
48463    * getComputedStyle() may return null when running in an iframe with
48464    * `display: none`.
48465    *
48466    * @see https://bugzilla.mozilla.org/show_bug.cgi?id=548397
48467    * @param {HTMLElement} el the htmlelement to work on
48468    * @param {string} the proprety to get the style for
48469    */
48470 
48471 
48472   var safeGetComputedStyle = function safeGetComputedStyle(el, property) {
48473     var result = void 0;
48474 
48475     if (!el) {
48476       return '';
48477     }
48478 
48479     result = window$1.getComputedStyle(el);
48480 
48481     if (!result) {
48482       return '';
48483     }
48484 
48485     return result[property];
48486   };
48487   /**
48488    * Resuable stable sort function
48489    *
48490    * @param {Playlists} array
48491    * @param {Function} sortFn Different comparators
48492    * @function stableSort
48493    */
48494 
48495 
48496   var stableSort = function stableSort(array, sortFn) {
48497     var newArray = array.slice();
48498     array.sort(function (left, right) {
48499       var cmp = sortFn(left, right);
48500 
48501       if (cmp === 0) {
48502         return newArray.indexOf(left) - newArray.indexOf(right);
48503       }
48504 
48505       return cmp;
48506     });
48507   };
48508   /**
48509    * A comparator function to sort two playlist object by bandwidth.
48510    *
48511    * @param {Object} left a media playlist object
48512    * @param {Object} right a media playlist object
48513    * @return {Number} Greater than zero if the bandwidth attribute of
48514    * left is greater than the corresponding attribute of right. Less
48515    * than zero if the bandwidth of right is greater than left and
48516    * exactly zero if the two are equal.
48517    */
48518 
48519 
48520   var comparePlaylistBandwidth = function comparePlaylistBandwidth(left, right) {
48521     var leftBandwidth = void 0;
48522     var rightBandwidth = void 0;
48523 
48524     if (left.attributes.BANDWIDTH) {
48525       leftBandwidth = left.attributes.BANDWIDTH;
48526     }
48527 
48528     leftBandwidth = leftBandwidth || window$1.Number.MAX_VALUE;
48529 
48530     if (right.attributes.BANDWIDTH) {
48531       rightBandwidth = right.attributes.BANDWIDTH;
48532     }
48533 
48534     rightBandwidth = rightBandwidth || window$1.Number.MAX_VALUE;
48535     return leftBandwidth - rightBandwidth;
48536   };
48537   /**
48538    * A comparator function to sort two playlist object by resolution (width).
48539    * @param {Object} left a media playlist object
48540    * @param {Object} right a media playlist object
48541    * @return {Number} Greater than zero if the resolution.width attribute of
48542    * left is greater than the corresponding attribute of right. Less
48543    * than zero if the resolution.width of right is greater than left and
48544    * exactly zero if the two are equal.
48545    */
48546 
48547 
48548   var comparePlaylistResolution = function comparePlaylistResolution(left, right) {
48549     var leftWidth = void 0;
48550     var rightWidth = void 0;
48551 
48552     if (left.attributes.RESOLUTION && left.attributes.RESOLUTION.width) {
48553       leftWidth = left.attributes.RESOLUTION.width;
48554     }
48555 
48556     leftWidth = leftWidth || window$1.Number.MAX_VALUE;
48557 
48558     if (right.attributes.RESOLUTION && right.attributes.RESOLUTION.width) {
48559       rightWidth = right.attributes.RESOLUTION.width;
48560     }
48561 
48562     rightWidth = rightWidth || window$1.Number.MAX_VALUE; // NOTE - Fallback to bandwidth sort as appropriate in cases where multiple renditions
48563     // have the same media dimensions/ resolution
48564 
48565     if (leftWidth === rightWidth && left.attributes.BANDWIDTH && right.attributes.BANDWIDTH) {
48566       return left.attributes.BANDWIDTH - right.attributes.BANDWIDTH;
48567     }
48568 
48569     return leftWidth - rightWidth;
48570   };
48571   /**
48572    * Chooses the appropriate media playlist based on bandwidth and player size
48573    *
48574    * @param {Object} master
48575    *        Object representation of the master manifest
48576    * @param {Number} playerBandwidth
48577    *        Current calculated bandwidth of the player
48578    * @param {Number} playerWidth
48579    *        Current width of the player element
48580    * @param {Number} playerHeight
48581    *        Current height of the player element
48582    * @param {Boolean} limitRenditionByPlayerDimensions
48583    *        True if the player width and height should be used during the selection, false otherwise
48584    * @return {Playlist} the highest bitrate playlist less than the
48585    * currently detected bandwidth, accounting for some amount of
48586    * bandwidth variance
48587    */
48588 
48589 
48590   var simpleSelector = function simpleSelector(master, playerBandwidth, playerWidth, playerHeight, limitRenditionByPlayerDimensions) {
48591     // convert the playlists to an intermediary representation to make comparisons easier
48592     var sortedPlaylistReps = master.playlists.map(function (playlist) {
48593       var width = void 0;
48594       var height = void 0;
48595       var bandwidth = void 0;
48596       width = playlist.attributes.RESOLUTION && playlist.attributes.RESOLUTION.width;
48597       height = playlist.attributes.RESOLUTION && playlist.attributes.RESOLUTION.height;
48598       bandwidth = playlist.attributes.BANDWIDTH;
48599       bandwidth = bandwidth || window$1.Number.MAX_VALUE;
48600       return {
48601         bandwidth: bandwidth,
48602         width: width,
48603         height: height,
48604         playlist: playlist
48605       };
48606     });
48607     stableSort(sortedPlaylistReps, function (left, right) {
48608       return left.bandwidth - right.bandwidth;
48609     }); // filter out any playlists that have been excluded due to
48610     // incompatible configurations
48611 
48612     sortedPlaylistReps = sortedPlaylistReps.filter(function (rep) {
48613       return !Playlist.isIncompatible(rep.playlist);
48614     }); // filter out any playlists that have been disabled manually through the representations
48615     // api or blacklisted temporarily due to playback errors.
48616 
48617     var enabledPlaylistReps = sortedPlaylistReps.filter(function (rep) {
48618       return Playlist.isEnabled(rep.playlist);
48619     });
48620 
48621     if (!enabledPlaylistReps.length) {
48622       // if there are no enabled playlists, then they have all been blacklisted or disabled
48623       // by the user through the representations api. In this case, ignore blacklisting and
48624       // fallback to what the user wants by using playlists the user has not disabled.
48625       enabledPlaylistReps = sortedPlaylistReps.filter(function (rep) {
48626         return !Playlist.isDisabled(rep.playlist);
48627       });
48628     } // filter out any variant that has greater effective bitrate
48629     // than the current estimated bandwidth
48630 
48631 
48632     var bandwidthPlaylistReps = enabledPlaylistReps.filter(function (rep) {
48633       return rep.bandwidth * Config.BANDWIDTH_VARIANCE < playerBandwidth;
48634     });
48635     var highestRemainingBandwidthRep = bandwidthPlaylistReps[bandwidthPlaylistReps.length - 1]; // get all of the renditions with the same (highest) bandwidth
48636     // and then taking the very first element
48637 
48638     var bandwidthBestRep = bandwidthPlaylistReps.filter(function (rep) {
48639       return rep.bandwidth === highestRemainingBandwidthRep.bandwidth;
48640     })[0]; // if we're not going to limit renditions by player size, make an early decision.
48641 
48642     if (limitRenditionByPlayerDimensions === false) {
48643       var _chosenRep = bandwidthBestRep || enabledPlaylistReps[0] || sortedPlaylistReps[0];
48644 
48645       return _chosenRep ? _chosenRep.playlist : null;
48646     } // filter out playlists without resolution information
48647 
48648 
48649     var haveResolution = bandwidthPlaylistReps.filter(function (rep) {
48650       return rep.width && rep.height;
48651     }); // sort variants by resolution
48652 
48653     stableSort(haveResolution, function (left, right) {
48654       return left.width - right.width;
48655     }); // if we have the exact resolution as the player use it
48656 
48657     var resolutionBestRepList = haveResolution.filter(function (rep) {
48658       return rep.width === playerWidth && rep.height === playerHeight;
48659     });
48660     highestRemainingBandwidthRep = resolutionBestRepList[resolutionBestRepList.length - 1]; // ensure that we pick the highest bandwidth variant that have exact resolution
48661 
48662     var resolutionBestRep = resolutionBestRepList.filter(function (rep) {
48663       return rep.bandwidth === highestRemainingBandwidthRep.bandwidth;
48664     })[0];
48665     var resolutionPlusOneList = void 0;
48666     var resolutionPlusOneSmallest = void 0;
48667     var resolutionPlusOneRep = void 0; // find the smallest variant that is larger than the player
48668     // if there is no match of exact resolution
48669 
48670     if (!resolutionBestRep) {
48671       resolutionPlusOneList = haveResolution.filter(function (rep) {
48672         return rep.width > playerWidth || rep.height > playerHeight;
48673       }); // find all the variants have the same smallest resolution
48674 
48675       resolutionPlusOneSmallest = resolutionPlusOneList.filter(function (rep) {
48676         return rep.width === resolutionPlusOneList[0].width && rep.height === resolutionPlusOneList[0].height;
48677       }); // ensure that we also pick the highest bandwidth variant that
48678       // is just-larger-than the video player
48679 
48680       highestRemainingBandwidthRep = resolutionPlusOneSmallest[resolutionPlusOneSmallest.length - 1];
48681       resolutionPlusOneRep = resolutionPlusOneSmallest.filter(function (rep) {
48682         return rep.bandwidth === highestRemainingBandwidthRep.bandwidth;
48683       })[0];
48684     } // fallback chain of variants
48685 
48686 
48687     var chosenRep = resolutionPlusOneRep || resolutionBestRep || bandwidthBestRep || enabledPlaylistReps[0] || sortedPlaylistReps[0];
48688     return chosenRep ? chosenRep.playlist : null;
48689   }; // Playlist Selectors
48690 
48691   /**
48692    * Chooses the appropriate media playlist based on the most recent
48693    * bandwidth estimate and the player size.
48694    *
48695    * Expects to be called within the context of an instance of HlsHandler
48696    *
48697    * @return {Playlist} the highest bitrate playlist less than the
48698    * currently detected bandwidth, accounting for some amount of
48699    * bandwidth variance
48700    */
48701 
48702 
48703   var lastBandwidthSelector = function lastBandwidthSelector() {
48704     return simpleSelector(this.playlists.master, this.systemBandwidth, parseInt(safeGetComputedStyle(this.tech_.el(), 'width'), 10), parseInt(safeGetComputedStyle(this.tech_.el(), 'height'), 10), this.limitRenditionByPlayerDimensions);
48705   };
48706   /**
48707    * Chooses the appropriate media playlist based on the potential to rebuffer
48708    *
48709    * @param {Object} settings
48710    *        Object of information required to use this selector
48711    * @param {Object} settings.master
48712    *        Object representation of the master manifest
48713    * @param {Number} settings.currentTime
48714    *        The current time of the player
48715    * @param {Number} settings.bandwidth
48716    *        Current measured bandwidth
48717    * @param {Number} settings.duration
48718    *        Duration of the media
48719    * @param {Number} settings.segmentDuration
48720    *        Segment duration to be used in round trip time calculations
48721    * @param {Number} settings.timeUntilRebuffer
48722    *        Time left in seconds until the player has to rebuffer
48723    * @param {Number} settings.currentTimeline
48724    *        The current timeline segments are being loaded from
48725    * @param {SyncController} settings.syncController
48726    *        SyncController for determining if we have a sync point for a given playlist
48727    * @return {Object|null}
48728    *         {Object} return.playlist
48729    *         The highest bandwidth playlist with the least amount of rebuffering
48730    *         {Number} return.rebufferingImpact
48731    *         The amount of time in seconds switching to this playlist will rebuffer. A
48732    *         negative value means that switching will cause zero rebuffering.
48733    */
48734 
48735 
48736   var minRebufferMaxBandwidthSelector = function minRebufferMaxBandwidthSelector(settings) {
48737     var master = settings.master,
48738         currentTime = settings.currentTime,
48739         bandwidth = settings.bandwidth,
48740         duration$$1 = settings.duration,
48741         segmentDuration = settings.segmentDuration,
48742         timeUntilRebuffer = settings.timeUntilRebuffer,
48743         currentTimeline = settings.currentTimeline,
48744         syncController = settings.syncController; // filter out any playlists that have been excluded due to
48745     // incompatible configurations
48746 
48747     var compatiblePlaylists = master.playlists.filter(function (playlist) {
48748       return !Playlist.isIncompatible(playlist);
48749     }); // filter out any playlists that have been disabled manually through the representations
48750     // api or blacklisted temporarily due to playback errors.
48751 
48752     var enabledPlaylists = compatiblePlaylists.filter(Playlist.isEnabled);
48753 
48754     if (!enabledPlaylists.length) {
48755       // if there are no enabled playlists, then they have all been blacklisted or disabled
48756       // by the user through the representations api. In this case, ignore blacklisting and
48757       // fallback to what the user wants by using playlists the user has not disabled.
48758       enabledPlaylists = compatiblePlaylists.filter(function (playlist) {
48759         return !Playlist.isDisabled(playlist);
48760       });
48761     }
48762 
48763     var bandwidthPlaylists = enabledPlaylists.filter(Playlist.hasAttribute.bind(null, 'BANDWIDTH'));
48764     var rebufferingEstimates = bandwidthPlaylists.map(function (playlist) {
48765       var syncPoint = syncController.getSyncPoint(playlist, duration$$1, currentTimeline, currentTime); // If there is no sync point for this playlist, switching to it will require a
48766       // sync request first. This will double the request time
48767 
48768       var numRequests = syncPoint ? 1 : 2;
48769       var requestTimeEstimate = Playlist.estimateSegmentRequestTime(segmentDuration, bandwidth, playlist);
48770       var rebufferingImpact = requestTimeEstimate * numRequests - timeUntilRebuffer;
48771       return {
48772         playlist: playlist,
48773         rebufferingImpact: rebufferingImpact
48774       };
48775     });
48776     var noRebufferingPlaylists = rebufferingEstimates.filter(function (estimate) {
48777       return estimate.rebufferingImpact <= 0;
48778     }); // Sort by bandwidth DESC
48779 
48780     stableSort(noRebufferingPlaylists, function (a, b) {
48781       return comparePlaylistBandwidth(b.playlist, a.playlist);
48782     });
48783 
48784     if (noRebufferingPlaylists.length) {
48785       return noRebufferingPlaylists[0];
48786     }
48787 
48788     stableSort(rebufferingEstimates, function (a, b) {
48789       return a.rebufferingImpact - b.rebufferingImpact;
48790     });
48791     return rebufferingEstimates[0] || null;
48792   };
48793   /**
48794    * Chooses the appropriate media playlist, which in this case is the lowest bitrate
48795    * one with video.  If no renditions with video exist, return the lowest audio rendition.
48796    *
48797    * Expects to be called within the context of an instance of HlsHandler
48798    *
48799    * @return {Object|null}
48800    *         {Object} return.playlist
48801    *         The lowest bitrate playlist that contains a video codec.  If no such rendition
48802    *         exists pick the lowest audio rendition.
48803    */
48804 
48805 
48806   var lowestBitrateCompatibleVariantSelector = function lowestBitrateCompatibleVariantSelector() {
48807     // filter out any playlists that have been excluded due to
48808     // incompatible configurations or playback errors
48809     var playlists = this.playlists.master.playlists.filter(Playlist.isEnabled); // Sort ascending by bitrate
48810 
48811     stableSort(playlists, function (a, b) {
48812       return comparePlaylistBandwidth(a, b);
48813     }); // Parse and assume that playlists with no video codec have no video
48814     // (this is not necessarily true, although it is generally true).
48815     //
48816     // If an entire manifest has no valid videos everything will get filtered
48817     // out.
48818 
48819     var playlistsWithVideo = playlists.filter(function (playlist) {
48820       return parseCodecs(playlist.attributes.CODECS).videoCodec;
48821     });
48822     return playlistsWithVideo[0] || null;
48823   };
48824   /**
48825    * Create captions text tracks on video.js if they do not exist
48826    *
48827    * @param {Object} inbandTextTracks a reference to current inbandTextTracks
48828    * @param {Object} tech the video.js tech
48829    * @param {Object}
48829 captionStreams the caption streams to create
48830    * @private
48831    */
48832 
48833 
48834   var createCaptionsTrackIfNotExists = function createCaptionsTrackIfNotExists(inbandTextTracks, tech, captionStreams) {
48835     for (var trackId in captionStreams) {
48836       if (!inbandTextTracks[trackId]) {
48837         tech.trigger({
48838           type: 'usage',
48839           name: 'hls-608'
48840         });
48841         var track = tech.textTracks().getTrackById(trackId);
48842 
48843         if (track) {
48844           // Resuse an existing track with a CC# id because this was
48845           // very likely created by videojs-contrib-hls from information
48846           // in the m3u8 for us to use
48847           inbandTextTracks[trackId] = track;
48848         } else {
48849           // Otherwise, create a track with the default `CC#` label and
48850           // without a language
48851           inbandTextTracks[trackId] = tech.addRemoteTextTrack({
48852             kind: 'captions',
48853             id: trackId,
48854             label: trackId
48855           }, false).track;
48856         }
48857       }
48858     }
48859   };
48860 
48861   var addCaptionData = function addCaptionData(_ref) {
48862     var inbandTextTracks = _ref.inbandTextTracks,
48863         captionArray = _ref.captionArray,
48864         timestampOffset = _ref.timestampOffset;
48865 
48866     if (!captionArray) {
48867       return;
48868     }
48869 
48870     var Cue = window.WebKitDataCue || window.VTTCue;
vendor: 122,673 bytes, lines 48871-52217
48871     captionArray.forEach(function (caption) {
48872       var track = caption.stream;
48873       var startTime = caption.startTime;
48874       var endTime = caption.endTime;
48875 
48876       if (!inbandTextTracks[track]) {
48877         return;
48878       }
48879 
48880       startTime += timestampOffset;
48881       endTime += timestampOffset;
48882       inbandTextTracks[track].addCue(new Cue(startTime, endTime, caption.text));
48883     });
48884   };
48885   /**
48886    * @file segment-loader.js
48887    */
48888   // in ms
48889 
48890 
48891   var CHECK_BUFFER_DELAY = 500;
48892   /**
48893    * Determines if we should call endOfStream on the media source based
48894    * on the state of the buffer or if appened segment was the final
48895    * segment in the playlist.
48896    *
48897    * @param {Object} playlist a media playlist object
48898    * @param {Object} mediaSource the MediaSource object
48899    * @param {Number} segmentIndex the index of segment we last appended
48900    * @returns {Boolean} do we need to call endOfStream on the MediaSource
48901    */
48902 
48903   var detectEndOfStream = function detectEndOfStream(playlist, mediaSource, segmentIndex) {
48904     if (!playlist || !mediaSource) {
48905       return false;
48906     }
48907 
48908     var segments = playlist.segments; // determine a few boolean values to help make the branch below easier
48909     // to read
48910 
48911     var appendedLastSegment = segmentIndex === segments.length; // if we've buffered to the end of the video, we need to call endOfStream
48912     // so that MediaSources can trigger the `ended` event when it runs out of
48913     // buffered data instead of waiting for me
48914 
48915     return playlist.endList && mediaSource.readyState === 'open' && appendedLastSegment;
48916   };
48917 
48918   var finite = function finite(num) {
48919     return typeof num === 'number' && isFinite(num);
48920   };
48921 
48922   var illegalMediaSwitch = function illegalMediaSwitch(loaderType, startingMedia, newSegmentMedia) {
48923     // Although these checks should most likely cover non 'main' types, for now it narrows
48924     // the scope of our checks.
48925     if (loaderType !== 'main' || !startingMedia || !newSegmentMedia) {
48926       return null;
48927     }
48928 
48929     if (!newSegmentMedia.containsAudio && !newSegmentMedia.containsVideo) {
48930       return 'Neither audio nor video found in segment.';
48931     }
48932 
48933     if (startingMedia.containsVideo && !newSegmentMedia.containsVideo) {
48934       return 'Only audio found in segment when we expected video.' + ' We can\'t switch to audio only from a stream that had video.' + ' To get rid of this message, please add codec information to the manifest.';
48935     }
48936 
48937     if (!startingMedia.containsVideo && newSegmentMedia.containsVideo) {
48938       return 'Video found in segment when we expected only audio.' + ' We can\'t switch to a stream with video from an audio only stream.' + ' To get rid of this message, please add codec information to the manifest.';
48939     }
48940 
48941     return null;
48942   };
48943   /**
48944    * Calculates a time value that is safe to remove from the back buffer without interupting
48945    * playback.
48946    *
48947    * @param {TimeRange} seekable
48948    *        The current seekable range
48949    * @param {Number} currentTime
48950    *        The current time of the player
48951    * @param {Number} targetDuration
48952    *        The target duration of the current playlist
48953    * @return {Number}
48954    *         Time that is safe to remove from the back buffer without interupting playback
48955    */
48956 
48957 
48958   var safeBackBufferTrimTime = function safeBackBufferTrimTime(seekable$$1, currentTime, targetDuration) {
48959     var removeToTime = void 0;
48960 
48961     if (seekable$$1.length && seekable$$1.start(0) > 0 && seekable$$1.start(0) < currentTime) {
48962       // If we have a seekable range use that as the limit for what can be removed safely
48963       removeToTime = seekable$$1.start(0);
48964     } else {
48965       // otherwise remove anything older than 30 seconds before the current play head
48966       removeToTime = currentTime - 30;
48967     } // Don't allow removing from the buffer within target duration of current time
48968     // to avoid the possibility of removing the GOP currently being played which could
48969     // cause playback stalls.
48970 
48971 
48972     return Math.min(removeToTime, currentTime - targetDuration);
48973   };
48974 
48975   var segmentInfoString = function segmentInfoString(segmentInfo) {
48976     var _segmentInfo$segment = segmentInfo.segment,
48977         start = _segmentInfo$segment.start,
48978         end = _segmentInfo$segment.end,
48979         _segmentInfo$playlist = segmentInfo.playlist,
48980         seq = _segmentInfo$playlist.mediaSequence,
48981         id = _segmentInfo$playlist.id,
48982         _segmentInfo$playlist2 = _segmentInfo$playlist.segments,
48983         segments = _segmentInfo$playlist2 === undefined ? [] : _segmentInfo$playlist2,
48984         index = segmentInfo.mediaIndex,
48985         timeline = segmentInfo.timeline;
48986     return ['appending [' + index + '] of [' + seq + ', ' + (seq + segments.length) + '] from playlist [' + id + ']', '[' + start + ' => ' + end + '] in timeline [' + timeline + ']'].join(' ');
48987   };
48988   /**
48989    * An object that manages segment loading and appending.
48990    *
48991    * @class SegmentLoader
48992    * @param {Object} options required and optional options
48993    * @extends videojs.EventTarget
48994    */
48995 
48996 
48997   var SegmentLoader = function (_videojs$EventTarget) {
48998     inherits$2(SegmentLoader, _videojs$EventTarget);
48999 
49000     function SegmentLoader(settings) {
49001       classCallCheck$1(this, SegmentLoader); // check pre-conditions
49002 
49003       var _this = possibleConstructorReturn$1(this, (SegmentLoader.__proto__ || Object.getPrototypeOf(SegmentLoader)).call(this));
49004 
49005       if (!settings) {
49006         throw new TypeError('Initialization settings are required');
49007       }
49008 
49009       if (typeof settings.currentTime !== 'function') {
49010         throw new TypeError('No currentTime getter specified');
49011       }
49012 
49013       if (!settings.mediaSource) {
49014         throw new TypeError('No MediaSource specified');
49015       } // public properties
49016 
49017 
49018       _this.bandwidth = settings.bandwidth;
49019       _this.throughput = {
49020         rate: 0,
49021         count: 0
49022       };
49023       _this.roundTrip = NaN;
49024 
49025       _this.resetStats_();
49026 
49027       _this.mediaIndex = null; // private settings
49028 
49029       _this.hasPlayed_ = settings.hasPlayed;
49030       _this.currentTime_ = settings.currentTime;
49031       _this.seekable_ = settings.seekable;
49032       _this.seeking_ = settings.seeking;
49033       _this.duration_ = settings.duration;
49034       _this.mediaSource_ = settings.mediaSource;
49035       _this.hls_ = settings.hls;
49036       _this.loaderType_ = settings.loaderType;
49037       _this.startingMedia_ = void 0;
49038       _this.segmentMetadataTrack_ = settings.segmentMetadataTrack;
49039       _this.goalBufferLength_ = settings.goalBufferLength;
49040       _this.sourceType_ = settings.sourceType;
49041       _this.inbandTextTracks_ = settings.inbandTextTracks;
49042       _this.state_ = 'INIT'; // private instance variables
49043 
49044       _this.checkBufferTimeout_ = null;
49045       _this.error_ = void 0;
49046       _this.currentTimeline_ = -1;
49047       _this.pendingSegment_ = null;
49048       _this.mimeType_ = null;
49049       _this.sourceUpdater_ = null;
49050       _this.xhrOptions_ = null; // Fragmented mp4 playback
49051 
49052       _this.activeInitSegmentId_ = null;
49053       _this.initSegments_ = {}; // HLSe playback
49054 
49055       _this.cacheEncryptionKeys_ = settings.cacheEncryptionKeys;
49056       _this.keyCache_ = {}; // Fmp4 CaptionParser
49057 
49058       if (_this.loaderType_ === 'main') {
49059         _this.captionParser_ = new captionParser();
49060       } else {
49061         _this.captionParser_ = null;
49062       }
49063 
49064       _this.decrypter_ = settings.decrypter; // Manages the tracking and generation of sync-points, mappings
49065       // between a time in the display time and a segment index within
49066       // a playlist
49067 
49068       _this.syncController_ = settings.syncController;
49069       _this.syncPoint_ = {
49070         segmentIndex: 0,
49071         time: 0
49072       };
49073 
49074       _this.syncController_.on('syncinfoupdate', function () {
49075         return _this.trigger('syncinfoupdate');
49076       });
49077 
49078       _this.mediaSource_.addEventListener('sourceopen', function () {
49079         return _this.ended_ = false;
49080       }); // ...for determining the fetch location
49081 
49082 
49083       _this.fetchAtBuffer_ = false;
49084       _this.logger_ = logger('SegmentLoader[' + _this.loaderType_ + ']');
49085       Object.defineProperty(_this, 'state', {
49086         get: function get$$1() {
49087           return this.state_;
49088         },
49089         set: function set$$1(newState) {
49090           if (newState !== this.state_) {
49091             this.logger_(this.state_ + ' -> ' + newState);
49092             this.state_ = newState;
49093           }
49094         }
49095       });
49096       return _this;
49097     }
49098     /**
49099      * reset all of our media stats
49100      *
49101      * @private
49102      */
49103 
49104 
49105     createClass$1(SegmentLoader, [{
49106       key: 'resetStats_',
49107       value: function resetStats_() {
49108         this.mediaBytesTransferred = 0;
49109         this.mediaRequests = 0;
49110         this.mediaRequestsAborted = 0;
49111         this.mediaRequestsTimedout = 0;
49112         this.mediaRequestsErrored = 0;
49113         this.mediaTransferDuration = 0;
49114         this.mediaSecondsLoaded = 0;
49115       }
49116       /**
49117        * dispose of the SegmentLoader and reset to the default state
49118        */
49119 
49120     }, {
49121       key: 'dispose',
49122       value: function dispose() {
49123         this.state = 'DISPOSED';
49124         this.pause();
49125         this.abort_();
49126 
49127         if (this.sourceUpdater_) {
49128           this.sourceUpdater_.dispose();
49129         }
49130 
49131         this.resetStats_();
49132 
49133         if (this.captionParser_) {
49134           this.captionParser_.reset();
49135         }
49136       }
49137       /**
49138        * abort anything that is currently doing on with the SegmentLoader
49139        * and reset to a default state
49140        */
49141 
49142     }, {
49143       key: 'abort',
49144       value: function abort() {
49145         if (this.state !== 'WAITING') {
49146           if (this.pendingSegment_) {
49147             this.pendingSegment_ = null;
49148           }
49149 
49150           return;
49151         }
49152 
49153         this.abort_(); // We aborted the requests we were waiting on, so reset the loader's state to READY
49154         // since we are no longer "waiting" on any requests. XHR callback is not always run
49155         // when the request is aborted. This will prevent the loader from being stuck in the
49156         // WAITING state indefinitely.
49157 
49158         this.state = 'READY'; // don't wait for buffer check timeouts to begin fetching the
49159         // next segment
49160 
49161         if (!this.paused()) {
49162           this.monitorBuffer_();
49163         }
49164       }
49165       /**
49166        * abort all pending xhr requests and null any pending segements
49167        *
49168        * @private
49169        */
49170 
49171     }, {
49172       key: 'abort_',
49173       value: function abort_() {
49174         if (this.pendingSegment_) {
49175           this.pendingSegment_.abortRequests();
49176         } // clear out the segment being processed
49177 
49178 
49179         this.pendingSegment_ = null;
49180       }
49181       /**
49182        * set an error on the segment loader and null out any pending segements
49183        *
49184        * @param {Error} error the error to set on the SegmentLoader
49185        * @return {Error} the error that was set or that is currently set
49186        */
49187 
49188     }, {
49189       key: 'error',
49190       value: function error(_error) {
49191         if (typeof _error !== 'undefined') {
49192           this.error_ = _error;
49193         }
49194 
49195         this.pendingSegment_ = null;
49196         return this.error_;
49197       }
49198     }, {
49199       key: 'endOfStream',
49200       value: function endOfStream() {
49201         this.ended_ = true;
49202         this.pause();
49203         this.trigger('ended');
49204       }
49205       /**
49206        * Indicates which time ranges are buffered
49207        *
49208        * @return {TimeRange}
49209        *         TimeRange object representing the current buffered ranges
49210        */
49211 
49212     }, {
49213       key: 'buffered_',
49214       value: function buffered_() {
49215         if (!this.sourceUpdater_) {
49216           return videojs$1.createTimeRanges();
49217         }
49218 
49219         return this.sourceUpdater_.buffered();
49220       }
49221       /**
49222        * Gets and sets init segment for the provided map
49223        *
49224        * @param {Object} map
49225        *        The map object representing the init segment to get or set
49226        * @param {Boolean=} set
49227        *        If true, the init segment for the provided map should be saved
49228        * @return {Object}
49229        *         map object for desired init segment
49230        */
49231 
49232     }, {
49233       key: 'initSegment',
49234       value: function initSegment(map) {
49235         var set$$1 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
49236 
49237         if (!map) {
49238           return null;
49239         }
49240 
49241         var id = initSegmentId(map);
49242         var storedMap = this.initSegments_[id];
49243 
49244         if (set$$1 && !storedMap && map.bytes) {
49245           this.initSegments_[id] = storedMap = {
49246             resolvedUri: map.resolvedUri,
49247             byterange: map.byterange,
49248             bytes: map.bytes,
49249             timescales: map.timescales,
49250             videoTrackIds: map.videoTrackIds
49251           };
49252         }
49253 
49254         return storedMap || map;
49255       }
49256       /**
49257        * Gets and sets key for the provided key
49258        *
49259        * @param {Object} key
49260        *        The key object representing the key to get or set
49261        * @param {Boolean=} set
49262        *        If true, the key for the provided key should be saved
49263        * @return {Object}
49264        *         Key object for desired key
49265        */
49266 
49267     }, {
49268       key: 'segmentKey',
49269       value: function segmentKey(key) {
49270         var set$$1 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
49271 
49272         if (!key) {
49273           return null;
49274         }
49275 
49276         var id = segmentKeyId(key);
49277         var storedKey = this.keyCache_[id]; // TODO: We should use the HTTP Expires header to invalidate our cache per
49278         // https://tools.ietf.org/html/draft-pantos-http-live-streaming-23#section-6.2.3
49279 
49280         if (this.cacheEncryptionKeys_ && set$$1 && !storedKey && key.bytes) {
49281           this.keyCache_[id] = storedKey = {
49282             resolvedUri: key.resolvedUri,
49283             bytes: key.bytes
49284           };
49285         }
49286 
49287         var result = {
49288           resolvedUri: (storedKey || key).resolvedUri
49289         };
49290 
49291         if (storedKey) {
49292           result.bytes = storedKey.bytes;
49293         }
49294 
49295         return result;
49296       }
49297       /**
49298        * Returns true if all configuration required for loading is present, otherwise false.
49299        *
49300        * @return {Boolean} True if the all configuration is ready for loading
49301        * @private
49302        */
49303 
49304     }, {
49305       key: 'couldBeginLoading_',
49306       value: function couldBeginLoading_() {
49307         return this.playlist_ && ( // the source updater is created when init_ is called, so either having a
49308         // source updater or being in the INIT state with a mimeType is enough
49309         // to say we have all the needed configuration to start loading.
49310         this.sourceUpdater_ || this.mimeType_ && this.state === 'INIT') && !this.paused();
49311       }
49312       /**
49313        * load a playlist and start to fill the buffer
49314        */
49315 
49316     }, {
49317       key: 'load',
49318       value: function load() {
49319         // un-pause
49320         this.monitorBuffer_(); // if we don't have a playlist yet, keep waiting for one to be
49321         // specified
49322 
49323         if (!this.playlist_) {
49324           return;
49325         } // not sure if this is the best place for this
49326 
49327 
49328         this.syncController_.setDateTimeMapping(this.playlist_); // if all the configuration is ready, initialize and begin loading
49329 
49330         if (this.state === 'INIT' && this.couldBeginLoading_()) {
49331           return this.init_();
49332         } // if we're in the middle of processing a segment already, don't
49333         // kick off an additional segment request
49334 
49335 
49336         if (!this.couldBeginLoading_() || this.state !== 'READY' && this.state !== 'INIT') {
49337           return;
49338         }
49339 
49340         this.state = 'READY';
49341       }
49342       /**
49343        * Once all the starting parameters have been specified, begin
49344        * operation. This method should only be invoked from the INIT
49345        * state.
49346        *
49347        * @private
49348        */
49349 
49350     }, {
49351       key: 'init_',
49352       value: function init_() {
49353         this.state = 'READY';
49354         this.sourceUpdater_ = new SourceUpdater(this.mediaSource_, this.mimeType_, this.loaderType_, this.sourceBufferEmitter_);
49355         this.resetEverything();
49356         return this.monitorBuffer_();
49357       }
49358       /**
49359        * set a playlist on the segment loader
49360        *
49361        * @param {PlaylistLoader} media the playlist to set on the segment loader
49362        */
49363 
49364     }, {
49365       key: 'playlist',
49366       value: function playlist(newPlaylist) {
49367         var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
49368 
49369         if (!newPlaylist) {
49370           return;
49371         }
49372 
49373         var oldPlaylist = this.playlist_;
49374         var segmentInfo = this.pendingSegment_;
49375         this.playlist_ = newPlaylist;
49376         this.xhrOptions_ = options; // when we haven't started playing yet, the start of a live playlist
49377         // is always our zero-time so force a sync update each time the playlist
49378         // is refreshed from the server
49379 
49380         if (!this.hasPlayed_()) {
49381           newPlaylist.syncInfo = {
49382             mediaSequence: newPlaylist.mediaSequence,
49383             time: 0
49384           };
49385         }
49386 
49387         var oldId = null;
49388 
49389         if (oldPlaylist) {
49390           if (oldPlaylist.id) {
49391             oldId = oldPlaylist.id;
49392           } else if (oldPlaylist.uri) {
49393             oldId = oldPlaylist.uri;
49394           }
49395         }
49396 
49397         this.logger_('playlist update [' + oldId + ' => ' + (newPlaylist.id || newPlaylist.uri) + ']'); // in VOD, this is always a rendition switch (or we updated our syncInfo above)
49398         // in LIVE, we always want to update with new playlists (including refreshes)
49399 
49400         this.trigger('syncinfoupdate'); // if we were unpaused but waiting for a playlist, start
49401         // buffering now
49402 
49403         if (this.state === 'INIT' && this.couldBeginLoading_()) {
49404           return this.init_();
49405         }
49406 
49407         if (!oldPlaylist || oldPlaylist.uri !== newPlaylist.uri) {
49408           if (this.mediaIndex !== null) {
49409             // we must "resync" the segment loader when we switch renditions and
49410             // the segment loader is already synced to the previous rendition
49411             this.resyncLoader();
49412           } // the rest of this function depends on `oldPlaylist` being defined
49413 
49414 
49415           return;
49416         } // we reloaded the same playlist so we are in a live scenario
49417         // and we will likely need to adjust the mediaIndex
49418 
49419 
49420         var mediaSequenceDiff = newPlaylist.mediaSequence - oldPlaylist.mediaSequence;
49421         this.logger_('live window shift [' + mediaSequenceDiff + ']'); // update the mediaIndex on the SegmentLoader
49422         // this is important because we can abort a request and this value must be
49423         // equal to the last appended mediaIndex
49424 
49425         if (this.mediaIndex !== null) {
49426           this.mediaIndex -= mediaSequenceDiff;
49427         } // update the mediaIndex on the SegmentInfo object
49428         // this is important because we will update this.mediaIndex with this value
49429         // in `handleUpdateEnd_` after the segment has been successfully appended
49430 
49431 
49432         if (segmentInfo) {
49433           segmentInfo.mediaIndex -= mediaSequenceDiff; // we need to update the referenced segment so that timing information is
49434           // saved for the new playlist's segment, however, if the segment fell off the
49435           // playlist, we can leave the old reference and just lose the timing info
49436 
49437           if (segmentInfo.mediaIndex >= 0) {
49438             segmentInfo.segment = newPlaylist.segments[segmentInfo.mediaIndex];
49439           }
49440         }
49441 
49442         this.syncController_.saveExpiredSegmentInfo(oldPlaylist, newPlaylist);
49443       }
49444       /**
49445        * Prevent the loader from fetching additional segments. If there
49446        * is a segment request outstanding, it will finish processing
49447        * before the loader halts. A segment loader can be unpaused by
49448        * calling load().
49449        */
49450 
49451     }, {
49452       key: 'pause',
49453       value: function pause() {
49454         if (this.checkBufferTimeout_) {
49455           window$1.clearTimeout(this.checkBufferTimeout_);
49456           this.checkBufferTimeout_ = null;
49457         }
49458       }
49459       /**
49460        * Returns whether the segment loader is fetching additional
49461        * segments when given the opportunity. This property can be
49462        * modified through calls to pause() and load().
49463        */
49464 
49465     }, {
49466       key: 'paused',
49467       value: function paused() {
49468         return this.checkBufferTimeout_ === null;
49469       }
49470       /**
49471        * create/set the following mimetype on the SourceBuffer through a
49472        * SourceUpdater
49473        *
49474        * @param {String} mimeType the mime type string to use
49475        * @param {Object} sourceBufferEmitter an event emitter that fires when a source buffer
49476        * is added to the media source
49477        */
49478 
49479     }, {
49480       key: 'mimeType',
49481       value: function mimeType(_mimeType, sourceBufferEmitter) {
49482         if (this.mimeType_) {
49483           return;
49484         }
49485 
49486         this.mimeType_ = _mimeType;
49487         this.sourceBufferEmitter_ = sourceBufferEmitter; // if we were unpaused but waiting for a sourceUpdater, start
49488         // buffering now
49489 
49490         if (this.state === 'INIT' && this.couldBeginLoading_()) {
49491           this.init_();
49492         }
49493       }
49494       /**
49495        * Delete all the buffered data and reset the SegmentLoader
49496        * @param {Function} [done] an optional callback to be executed when the remove
49497        * operation is complete
49498        */
49499 
49500     }, {
49501       key: 'resetEverything',
49502       value: function resetEverything(done) {
49503         this.ended_ = false;
49504         this.resetLoader();
49505         this.remove(0, this.duration_(), done); // clears fmp4 captions
49506 
49507         if (this.captionParser_) {
49508           this.captionParser_.clearAllCaptions();
49509         }
49510 
49511         this.trigger('reseteverything');
49512       }
49513       /**
49514        * Force the SegmentLoader to resync and start loading around the currentTime instead
49515        * of starting at the end of the buffer
49516        *
49517        * Useful for fast quality changes
49518        */
49519 
49520     }, {
49521       key: 'resetLoader',
49522       value: function resetLoader() {
49523         this.fetchAtBuffer_ = false;
49524         this.resyncLoader();
49525       }
49526       /**
49527        * Force the SegmentLoader to restart synchronization and make a conservative guess
49528        * before returning to the simple walk-forward method
49529        */
49530 
49531     }, {
49532       key: 'resyncLoader',
49533       value: function resyncLoader() {
49534         this.mediaIndex = null;
49535         this.syncPoint_ = null;
49536         this.abort();
49537       }
49538       /**
49539        * Remove any data in the source buffer between start and end times
49540        * @param {Number} start - the start time of the region to remove from the buffer
49541        * @param {Number} end - the end time of the region to remove from the buffer
49542        * @param {Function} [done] - an optional callback to be executed when the remove
49543        * operation is complete
49544        */
49545 
49546     }, {
49547       key: 'remove',
49548       value: function remove(start, end, done) {
49549         if (this.sourceUpdater_) {
49550           this.sourceUpdater_.remove(start, end, done);
49551         }
49552 
49553         removeCuesFromTrack(start, end, this.segmentMetadataTrack_);
49554 
49555         if (this.inbandTextTracks_) {
49556           for (var id in this.inbandTextTracks_) {
49557             removeCuesFromTrack(start, end, this.inbandTextTracks_[id]);
49558           }
49559         }
49560       }
49561       /**
49562        * (re-)schedule monitorBufferTick_ to run as soon as possible
49563        *
49564        * @private
49565        */
49566 
49567     }, {
49568       key: 'monitorBuffer_',
49569       value: function monitorBuffer_() {
49570         if (this.checkBufferTimeout_) {
49571           window$1.clearTimeout(this.checkBufferTimeout_);
49572         }
49573 
49574         this.checkBufferTimeout_ = window$1.setTimeout(this.monitorBufferTick_.bind(this), 1);
49575       }
49576       /**
49577        * As long as the SegmentLoader is in the READY state, periodically
49578        * invoke fillBuffer_().
49579        *
49580        * @private
49581        */
49582 
49583     }, {
49584       key: 'monitorBufferTick_',
49585       value: function monitorBufferTick_() {
49586         if (this.state === 'READY') {
49587           this.fillBuffer_();
49588         }
49589 
49590         if (this.checkBufferTimeout_) {
49591           window$1.clearTimeout(this.checkBufferTimeout_);
49592         }
49593 
49594         this.checkBufferTimeout_ = window$1.setTimeout(this.monitorBufferTick_.bind(this), CHECK_BUFFER_DELAY);
49595       }
49596       /**
49597        * fill the buffer with segements unless the sourceBuffers are
49598        * currently updating
49599        *
49600        * Note: this function should only ever be called by monitorBuffer_
49601        * and never directly
49602        *
49603        * @private
49604        */
49605 
49606     }, {
49607       key: 'fillBuffer_',
49608       value: function fillBuffer_() {
49609         if (this.sourceUpdater_.updating()) {
49610           return;
49611         }
49612 
49613         if (!this.syncPoint_) {
49614           this.syncPoint_ = this.syncController_.getSyncPoint(this.playlist_, this.duration_(), this.currentTimeline_, this.currentTime_());
49615         } // see if we need to begin loading immediately
49616 
49617 
49618         var segmentInfo = this.checkBuffer_(this.buffered_(), this.playlist_, this.mediaIndex, this.hasPlayed_(), this.currentTime_(), this.syncPoint_);
49619 
49620         if (!segmentInfo) {
49621           return;
49622         }
49623 
49624         if (this.isEndOfStream_(segmentInfo.mediaIndex)) {
49625           this.endOfStream();
49626           return;
49627         }
49628 
49629         if (segmentInfo.mediaIndex === this.playlist_.segments.length - 1 && this.mediaSource_.readyState === 'ended' && !this.seeking_()) {
49630           return;
49631         } // We will need to change timestampOffset of the sourceBuffer if:
49632         // - The segment.timeline !== this.currentTimeline
49633         //   (we are crossing a discontinuity somehow)
49634         // - The "timestampOffset" for the start of this segment is less than
49635         //   the currently set timestampOffset
49636         // Also, clear captions if we are crossing a discontinuity boundary
49637         // Previously, we changed the timestampOffset if the start of this segment
49638         // is less than the currently set timestampOffset but this isn't wanted
49639         // as it can produce bad behavior, especially around long running
49640         // live streams
49641 
49642 
49643         if (segmentInfo.timeline !== this.currentTimeline_) {
49644           this.syncController_.reset();
49645           segmentInfo.timestampOffset = segmentInfo.startOfSegment;
49646 
49647           if (this.captionParser_) {
49648             this.captionParser_.clearAllCaptions();
49649           }
49650         }
49651 
49652         this.loadSegment_(segmentInfo);
49653       }
49654       /**
49655        * Determines if this segment loader is at the end of it's stream.
49656        *
49657        * @param {Number} mediaIndex the index of segment we last appended
49658        * @param {Object} [playlist=this.playlist_] a media playlist object
49659        * @returns {Boolean} true if at end of stream, false otherwise.
49660        */
49661 
49662     }, {
49663       key: 'isEndOfStream_',
49664       value: function isEndOfStream_(mediaIndex) {
49665         var playlist = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.playlist_;
49666         return detectEndOfStream(playlist, this.mediaSource_, mediaIndex) && !this.sourceUpdater_.updating();
49667       }
49668       /**
49669        * Determines what segment request should be made, given current playback
49670        * state.
49671        *
49672        * @param {TimeRanges} buffered - the state of the buffer
49673        * @param {Object} playlist - the playlist object to fetch segments from
49674        * @param {Number} mediaIndex - the previous mediaIndex fetched or null
49675        * @param {Boolean} hasPlayed - a flag indicating whether we have played or not
49676        * @param {Number} currentTime - the playback position in seconds
49677        * @param {Object} syncPoint - a segment info object that describes the
49678        * @returns {Object} a segment request object that describes the segment to load
49679        */
49680 
49681     }, {
49682       key: 'checkBuffer_',
49683       value: function checkBuffer_(buffered, playlist, mediaIndex, hasPlayed, currentTime, syncPoint) {
49684         var lastBufferedEnd = 0;
49685         var startOfSegment = void 0;
49686 
49687         if (buffered.length) {
49688           lastBufferedEnd = buffered.end(buffered.length - 1);
49689         }
49690 
49691         var bufferedTime = Math.max(0, lastBufferedEnd - currentTime);
49692 
49693         if (!playlist.segments.length) {
49694           return null;
49695         } // if there is plenty of content buffered, and the video has
49696         // been played before relax for awhile
49697 
49698 
49699         if (bufferedTime >= this.goalBufferLength_()) {
49700           return null;
49701         } // if the video has not yet played once, and we already have
49702         // one segment downloaded do nothing
49703 
49704 
49705         if (!hasPlayed && bufferedTime >= 1) {
49706           return null;
49707         } // When the syncPoint is null, there is no way of determining a good
49708         // conservative segment index to fetch from
49709         // The best thing to do here is to get the kind of sync-point data by
49710         // making a request
49711 
49712 
49713         if (syncPoint === null) {
49714           mediaIndex = this.getSyncSegmentCandidate_(playlist);
49715           return this.generateSegmentInfo_(playlist, mediaIndex, null, true);
49716         } // Under normal playback conditions fetching is a simple walk forward
49717 
49718 
49719         if (mediaIndex !== null) {
49720           var segment = playlist.segments[mediaIndex];
49721           startOfSegment = lastBufferedEnd;
49722           return this.generateSegmentInfo_(playlist, mediaIndex + 1, startOfSegment, false);
49723         } // There is a sync-point but the lack of a mediaIndex indicates that
49724         // we need to make a good conservative guess about which segment to
49725         // fetch
49726 
49727 
49728         if (this.fetchAtBuffer_) {
49729           // Find the segment containing the end of the buffer
49730           var mediaSourceInfo = Playlist.getMediaInfoForTime(playlist, lastBufferedEnd, syncPoint.segmentIndex, syncPoint.time);
49731           mediaIndex = mediaSourceInfo.mediaIndex;
49732           startOfSegment = mediaSourceInfo.startTime;
49733         } else {
49734           // Find the segment containing currentTime
49735           var _mediaSourceInfo = Playlist.getMediaInfoForTime(playlist, currentTime, syncPoint.segmentIndex, syncPoint.time);
49736 
49737           mediaIndex = _mediaSourceInfo.mediaIndex;
49738           startOfSegment = _mediaSourceInfo.startTime;
49739         }
49740 
49741         return this.generateSegmentInfo_(playlist, mediaIndex, startOfSegment, false);
49742       }
49743       /**
49744        * The segment loader has no recourse except to fetch a segment in the
49745        * current playlist and use the internal timestamps in that segment to
49746        * generate a syncPoint. This function returns a good candidate index
49747        * for that process.
49748        *
49749        * @param {Object} playlist - the playlist object to look for a
49750        * @returns {Number} An index of a segment from the playlist to load
49751        */
49752 
49753     }, {
49754       key: 'getSyncSegmentCandidate_',
49755       value: function getSyncSegmentCandidate_(playlist) {
49756         var _this2 = this;
49757 
49758         if (this.currentTimeline_ === -1) {
49759           return 0;
49760         }
49761 
49762         var segmentIndexArray = playlist.segments.map(function (s, i) {
49763           return {
49764             timeline: s.timeline,
49765             segmentIndex: i
49766           };
49767         }).filter(function (s) {
49768           return s.timeline === _this2.currentTimeline_;
49769         });
49770 
49771         if (segmentIndexArray.length) {
49772           return segmentIndexArray[Math.min(segmentIndexArray.length - 1, 1)].segmentIndex;
49773         }
49774 
49775         return Math.max(playlist.segments.length - 1, 0);
49776       }
49777     }, {
49778       key: 'generateSegmentInfo_',
49779       value: function generateSegmentInfo_(playlist, mediaIndex, startOfSegment, isSyncRequest) {
49780         if (mediaIndex < 0 || mediaIndex >= playlist.segments.length) {
49781           return null;
49782         }
49783 
49784         var segment = playlist.segments[mediaIndex];
49785         return {
49786           requestId: 'segment-loader-' + Math.random(),
49787           // resolve the segment URL relative to the playlist
49788           uri: segment.resolvedUri,
49789           // the segment's mediaIndex at the time it was requested
49790           mediaIndex: mediaIndex,
49791           // whether or not to update the SegmentLoader's state with this
49792           // segment's mediaIndex
49793           isSyncRequest: isSyncRequest,
49794           startOfSegment: startOfSegment,
49795           // the segment's playlist
49796           playlist: playlist,
49797           // unencrypted bytes of the segment
49798           bytes: null,
49799           // when a key is defined for this segment, the encrypted bytes
49800           encryptedBytes: null,
49801           // The target timestampOffset for this segment when we append it
49802           // to the source buffer
49803           timestampOffset: null,
49804           // The timeline that the segment is in
49805           timeline: segment.timeline,
49806           // The expected duration of the segment in seconds
49807           duration: segment.duration,
49808           // retain the segment in case the playlist updates while doing an async process
49809           segment: segment
49810         };
49811       }
49812       /**
49813        * Determines if the network has enough bandwidth to complete the current segment
49814        * request in a timely manner. If not, the request will be aborted early and bandwidth
49815        * updated to trigger a playlist switch.
49816        *
49817        * @param {Object} stats
49818        *        Object containing stats about the request timing and size
49819        * @return {Boolean} True if the request was aborted, false otherwise
49820        * @private
49821        */
49822 
49823     }, {
49824       key: 'abortRequestEarly_',
49825       value: function abortRequestEarly_(stats) {
49826         if (this.hls_.tech_.paused() || // Don't abort if the current playlist is on the lowestEnabledRendition
49827         // TODO: Replace using timeout with a boolean indicating whether this playlist is
49828         //       the lowestEnabledRendition.
49829         !this.xhrOptions_.timeout || // Don't abort if we have no bandwidth information to estimate segment sizes
49830         !this.playlist_.attributes.BANDWIDTH) {
49831           return false;
49832         } // Wait at least 1 second since the first byte of data has been received before
49833         // using the calculated bandwidth from the progress event to allow the bitrate
49834         // to stabilize
49835 
49836 
49837         if (Date.now() - (stats.firstBytesReceivedAt || Date.now()) < 1000) {
49838           return false;
49839         }
49840 
49841         var currentTime = this.currentTime_();
49842         var measuredBandwidth = stats.bandwidth;
49843         var segmentDuration = this.pendingSegment_.duration;
49844         var requestTimeRemaining = Playlist.estimateSegmentRequestTime(segmentDuration, measuredBandwidth, this.playlist_, stats.bytesReceived); // Subtract 1 from the timeUntilRebuffer so we still consider an early abort
49845         // if we are only left with less than 1 second when the request completes.
49846         // A negative timeUntilRebuffering indicates we are already rebuffering
49847 
49848         var timeUntilRebuffer$$1 = timeUntilRebuffer(this.buffered_(), currentTime, this.hls_.tech_.playbackRate()) - 1; // Only consider aborting early if the estimated time to finish the download
49849         // is larger than the estimated time until the player runs out of forward buffer
49850 
49851         if (requestTimeRemaining <= timeUntilRebuffer$$1) {
49852           return false;
49853         }
49854 
49855         var switchCandidate = minRebufferMaxBandwidthSelector({
49856           master: this.hls_.playlists.master,
49857           currentTime: currentTime,
49858           bandwidth: measuredBandwidth,
49859           duration: this.duration_(),
49860           segmentDuration: segmentDuration,
49861           timeUntilRebuffer: timeUntilRebuffer$$1,
49862           currentTimeline: this.currentTimeline_,
49863           syncController: this.syncController_
49864         });
49865 
49866         if (!switchCandidate) {
49867           return;
49868         }
49869 
49870         var rebufferingImpact = requestTimeRemaining - timeUntilRebuffer$$1;
49871         var timeSavedBySwitching = rebufferingImpact - switchCandidate.rebufferingImpact;
49872         var minimumTimeSaving = 0.5; // If we are already rebuffering, increase the amount of variance we add to the
49873         // potential round trip time of the new request so that we are not too aggressive
49874         // with switching to a playlist that might save us a fraction of a second.
49875 
49876         if (timeUntilRebuffer$$1 <= TIME_FUDGE_FACTOR) {
49877           minimumTimeSaving = 1;
49878         }
49879 
49880         if (!switchCandidate.playlist || switchCandidate.playlist.uri === this.playlist_.uri || timeSavedBySwitching < minimumTimeSaving) {
49881           return false;
49882         } // set the bandwidth to that of the desired playlist being sure to scale by
49883         // BANDWIDTH_VARIANCE and add one so the playlist selector does not exclude it
49884         // don't trigger a bandwidthupdate as the bandwidth is artifial
49885 
49886 
49887         this.bandwidth = switchCandidate.playlist.attributes.BANDWIDTH * Config.BANDWIDTH_VARIANCE + 1;
49888         this.abort();
49889         this.trigger('earlyabort');
49890         return true;
49891       }
49892       /**
49893        * XHR `progress` event handler
49894        *
49895        * @param {Event}
49896        *        The XHR `progress` event
49897        * @param {Object} simpleSegment
49898        *        A simplified segment object copy
49899        * @private
49900        */
49901 
49902     }, {
49903       key: 'handleProgress_',
49904       value: function handleProgress_(event, simpleSegment) {
49905         if (!this.pendingSegment_ || simpleSegment.requestId !== this.pendingSegment_.requestId || this.abortRequestEarly_(simpleSegment.stats)) {
49906           return;
49907         }
49908 
49909         this.trigger('progress');
49910       }
49911       /**
49912        * load a specific segment from a request into the buffer
49913        *
49914        * @private
49915        */
49916 
49917     }, {
49918       key: 'loadSegment_',
49919       value: function loadSegment_(segmentInfo) {
49920         this.state = 'WAITING';
49921         this.pendingSegment_ = segmentInfo;
49922         this.trimBackBuffer_(segmentInfo);
49923         segmentInfo.abortRequests = mediaSegmentRequest(this.hls_.xhr, this.xhrOptions_, this.decrypter_, this.captionParser_, this.createSimplifiedSegmentObj_(segmentInfo), // progress callback
49924         this.handleProgress_.bind(this), this.segmentRequestFinished_.bind(this));
49925       }
49926       /**
49927        * trim the back buffer so that we don't have too much data
49928        * in the source buffer
49929        *
49930        * @private
49931        *
49932        * @param {Object} segmentInfo - the current segment
49933        */
49934 
49935     }, {
49936       key: 'trimBackBuffer_',
49937       value: function trimBackBuffer_(segmentInfo) {
49938         var removeToTime = safeBackBufferTrimTime(this.seekable_(), this.currentTime_(), this.playlist_.targetDuration || 10); // Chrome has a hard limit of 150MB of
49939         // buffer and a very conservative "garbage collector"
49940         // We manually clear out the old buffer to ensure
49941         // we don't trigger the QuotaExceeded error
49942         // on the source buffer during subsequent appends
49943 
49944         if (removeToTime > 0) {
49945           this.remove(0, removeToTime);
49946         }
49947       }
49948       /**
49949        * created a simplified copy of the segment object with just the
49950        * information necessary to perform the XHR and decryption
49951        *
49952        * @private
49953        *
49954        * @param {Object} segmentInfo - the current segment
49955        * @returns {Object} a simplified segment object copy
49956        */
49957 
49958     }, {
49959       key: 'createSimplifiedSegmentObj_',
49960       value: function createSimplifiedSegmentObj_(segmentInfo) {
49961         var segment = segmentInfo.segment;
49962         var simpleSegment = {
49963           resolvedUri: segment.resolvedUri,
49964           byterange: segment.byterange,
49965           requestId: segmentInfo.requestId
49966         };
49967 
49968         if (segment.key) {
49969           // if the media sequence is greater than 2^32, the IV will be incorrect
49970           // assuming 10s segments, that would be about 1300 years
49971           var iv = segment.key.iv || new Uint32Array([0, 0, 0, segmentInfo.mediaIndex + segmentInfo.playlist.mediaSequence]);
49972           simpleSegment.key = this.segmentKey(segment.key);
49973           simpleSegment.key.iv = iv;
49974         }
49975 
49976         if (segment.map) {
49977           simpleSegment.map = this.initSegment(segment.map);
49978         }
49979 
49980         return simpleSegment;
49981       }
49982       /**
49983        * Handle the callback from the segmentRequest function and set the
49984        * associated SegmentLoader state and errors if necessary
49985        *
49986        * @private
49987        */
49988 
49989     }, {
49990       key: 'segmentRequestFinished_',
49991       value: function segmentRequestFinished_(error, simpleSegment) {
49992         // every request counts as a media request even if it has been aborted
49993         // or canceled due to a timeout
49994         this.mediaRequests += 1;
49995 
49996         if (simpleSegment.stats) {
49997           this.mediaBytesTransferred += simpleSegment.stats.bytesReceived;
49998           this.mediaTransferDuration += simpleSegment.stats.roundTripTime;
49999         } // The request was aborted and the SegmentLoader has already been reset
50000 
50001 
50002         if (!this.pendingSegment_) {
50003           this.mediaRequestsAborted += 1;
50004           return;
50005         } // the request was aborted and the SegmentLoader has already started
50006         // another request. this can happen when the timeout for an aborted
50007         // request triggers due to a limitation in the XHR library
50008         // do not count this as any sort of request or we risk double-counting
50009 
50010 
50011         if (simpleSegment.requestId !== this.pendingSegment_.requestId) {
50012           return;
50013         } // an error occurred from the active pendingSegment_ so reset everything
50014 
50015 
50016         if (error) {
50017           this.pendingSegment_ = null;
50018           this.state = 'READY'; // the requests were aborted just record the aborted stat and exit
50019           // this is not a true error condition and nothing corrective needs
50020           // to be done
50021 
50022           if (error.code === REQUEST_ERRORS.ABORTED) {
50023             this.mediaRequestsAborted += 1;
50024             return;
50025           }
50026 
50027           this.pause(); // the error is really just that at least one of the requests timed-out
50028           // set the bandwidth to a very low value and trigger an ABR switch to
50029           // take emergency action
50030 
50031           if (error.code === REQUEST_ERRORS.TIMEOUT) {
50032             this.mediaRequestsTimedout += 1;
50033             this.bandwidth = 1;
50034             this.roundTrip = NaN;
50035             this.trigger('bandwidthupdate');
50036             return;
50037           } // if control-flow has arrived here, then the error is real
50038           // emit an error event to blacklist the current playlist
50039 
50040 
50041           this.mediaRequestsErrored += 1;
50042           this.error(error);
50043           this.trigger('error');
50044           return;
50045         } // the response was a success so set any bandwidth stats the request
50046         // generated for ABR purposes
50047 
50048 
50049         this.bandwidth = simpleSegment.stats.bandwidth;
50050         this.roundTrip = simpleSegment.stats.roundTripTime; // if this request included an initialization segment, save that data
50051         // to the initSegment cache
50052 
50053         if (simpleSegment.map) {
50054           simpleSegment.map = this.initSegment(simpleSegment.map, true);
50055         } // if this request included a segment key, save that data in the cache
50056 
50057 
50058         if (simpleSegment.key) {
50059           this.segmentKey(simpleSegment.key, true);
50060         }
50061 
50062         this.processSegmentResponse_(simpleSegment);
50063       }
50064       /**
50065        * Move any important data from the simplified segment object
50066        * back to the real segment object for future phases
50067        *
50068        * @private
50069        */
50070 
50071     }, {
50072       key: 'processSegmentResponse_',
50073       value: function processSegmentResponse_(simpleSegment) {
50074         var segmentInfo = this.pendingSegment_;
50075         segmentInfo.bytes = simpleSegment.bytes;
50076 
50077         if (simpleSegment.map) {
50078           segmentInfo.segment.map.bytes = simpleSegment.map.bytes;
50079         }
50080 
50081         segmentInfo.endOfAllRequests = simpleSegment.endOfAllRequests; // This has fmp4 captions, add them to text tracks
50082 
50083         if (simpleSegment.fmp4Captions) {
50084           createCaptionsTrackIfNotExists(this.inbandTextTracks_, this.hls_.tech_, simpleSegment.captionStreams);
50085           addCaptionData({
50086             inbandTextTracks: this.inbandTextTracks_,
50087             captionArray: simpleSegment.fmp4Captions,
50088             // fmp4s will not have a timestamp offset
50089             timestampOffset: 0
50090           }); // Reset stored captions since we added parsed
50091           // captions to a text track at this point
50092 
50093           if (this.captionParser_) {
50094             this.captionParser_.clearParsedCaptions();
50095           }
50096         }
50097 
50098         this.handleSegment_();
50099       }
50100       /**
50101        * append a decrypted segement to the SourceBuffer through a SourceUpdater
50102        *
50103        * @private
50104        */
50105 
50106     }, {
50107       key: 'handleSegment_',
50108       value: function handleSegment_() {
50109         var _this3 = this;
50110 
50111         if (!this.pendingSegment_) {
50112           this.state = 'READY';
50113           return;
50114         }
50115 
50116         var segmentInfo = this.pendingSegment_;
50117         var segment = segmentInfo.segment;
50118         var timingInfo = this.syncController_.probeSegmentInfo(segmentInfo); // When we have our first timing info, determine what media types this loader is
50119         // dealing with. Although we're maintaining extra state, it helps to preserve the
50120         // separation of segment loader from the actual source buffers.
50121 
50122         if (typeof this.startingMedia_ === 'undefined' && timingInfo && ( // Guard against cases where we're not getting timing info at all until we are
50123         // certain that all streams will provide it.
50124         timingInfo.containsAudio || timingInfo.containsVideo)) {
50125           this.startingMedia_ = {
50126             containsAudio: timingInfo.containsAudio,
50127             containsVideo: timingInfo.containsVideo
50128           };
50129         }
50130 
50131         var illegalMediaSwitchError = illegalMediaSwitch(this.loaderType_, this.startingMedia_, timingInfo);
50132 
50133         if (illegalMediaSwitchError) {
50134           this.error({
50135             message: illegalMediaSwitchError,
50136             blacklistDuration: Infinity
50137           });
50138           this.trigger('error');
50139           return;
50140         }
50141 
50142         if (segmentInfo.isSyncRequest) {
50143           this.trigger('syncinfoupdate');
50144           this.pendingSegment_ = null;
50145           this.state = 'READY';
50146           return;
50147         }
50148 
50149         if (segmentInfo.timestampOffset !== null && segmentInfo.timestampOffset !== this.sourceUpdater_.timestampOffset()) {
50150           // Subtract any difference between the PTS and DTS times of the first frame
50151           // from the timeStampOffset (which currently equals the buffered.end) to prevent
50152           // creating any gaps in the buffer
50153           if (timingInfo && timingInfo.segmentTimestampInfo) {
50154             var ptsStartTime = timingInfo.segmentTimestampInfo[0].ptsTime;
50155             var dtsStartTime = timingInfo.segmentTimestampInfo[0].dtsTime;
50156             segmentInfo.timestampOffset -= ptsStartTime - dtsStartTime;
50157           }
50158 
50159           this.sourceUpdater_.timestampOffset(segmentInfo.timestampOffset); // fired when a timestamp offset is set in HLS (can also identify discontinuities)
50160 
50161           this.trigger('timestampoffset');
50162         }
50163 
50164         var timelineMapping = this.syncController_.mappingForTimeline(segmentInfo.timeline);
50165 
50166         if (timelineMapping !== null) {
50167           this.trigger({
50168             type: 'segmenttimemapping',
50169             mapping: timelineMapping
50170           });
50171         }
50172 
50173         this.state = 'APPENDING'; // if the media initialization segment is changing, append it
50174         // before the content segment
50175 
50176         if (segment.map) {
50177           var initId = initSegmentId(segment.map);
50178 
50179           if (!this.activeInitSegmentId_ || this.activeInitSegmentId_ !== initId) {
50180             var initSegment = this.initSegment(segment.map);
50181             this.sourceUpdater_.appendBuffer({
50182               bytes: initSegment.bytes
50183             }, function () {
50184               _this3.activeInitSegmentId_ = initId;
50185             });
50186           }
50187         }
50188 
50189         segmentInfo.byteLength = segmentInfo.bytes.byteLength;
50190 
50191         if (typeof segment.start === 'number' && typeof segment.end === 'number') {
50192           this.mediaSecondsLoaded += segment.end - segment.start;
50193         } else {
50194           this.mediaSecondsLoaded += segment.duration;
50195         }
50196 
50197         this.logger_(segmentInfoString(segmentInfo));
50198         this.sourceUpdater_.appendBuffer({
50199           bytes: segmentInfo.bytes,
50200           videoSegmentTimingInfoCallback: this.handleVideoSegmentTimingInfo_.bind(this, segmentInfo.requestId)
50201         }, this.handleUpdateEnd_.bind(this));
50202       }
50203     }, {
50204       key: 'handleVideoSegmentTimingInfo_',
50205       value: function handleVideoSegmentTimingInfo_(requestId, event) {
50206         if (!this.pendingSegment_ || requestId !== this.pendingSegment_.requestId) {
50207           return;
50208         }
50209 
50210         var segment = this.pendingSegment_.segment;
50211 
50212         if (!segment.videoTimingInfo) {
50213           segment.videoTimingInfo = {};
50214         }
50215 
50216         segment.videoTimingInfo.transmuxerPrependedSeconds = event.videoSegmentTimingInfo.prependedContentDuration || 0;
50217         segment.videoTimingInfo.transmuxedPresentationStart = event.videoSegmentTimingInfo.start.presentation;
50218         segment.videoTimingInfo.transmuxedPresentationEnd = event.videoSegmentTimingInfo.end.presentation; // mainly used as a reference for debugging
50219 
50220         segment.videoTimingInfo.baseMediaDecodeTime = event.videoSegmentTimingInfo.baseMediaDecodeTime;
50221       }
50222       /**
50223        * callback to run when appendBuffer is finished. detects if we are
50224        * in a good state to do things with the data we got, or if we need
50225        * to wait for more
50226        *
50227        * @private
50228        */
50229 
50230     }, {
50231       key: 'handleUpdateEnd_',
50232       value: function handleUpdateEnd_() {
50233         if (!this.pendingSegment_) {
50234           this.state = 'READY';
50235 
50236           if (!this.paused()) {
50237             this.monitorBuffer_();
50238           }
50239 
50240           return;
50241         }
50242 
50243         var segmentInfo = this.pendingSegment_;
50244         var segment = segmentInfo.segment;
50245         var isWalkingForward = this.mediaIndex !== null;
50246         this.pendingSegment_ = null;
50247         this.recordThroughput_(segmentInfo);
50248         this.addSegmentMetadataCue_(segmentInfo);
50249         this.state = 'READY';
50250         this.mediaIndex = segmentInfo.mediaIndex;
50251         this.fetchAtBuffer_ = true;
50252         this.currentTimeline_ = segmentInfo.timeline; // We must update the syncinfo to recalculate the seekable range before
50253         // the following conditional otherwise it may consider this a bad "guess"
50254         // and attempt to resync when the post-update seekable window and live
50255         // point would mean that this was the perfect segment to fetch
50256 
50257         this.trigger('syncinfoupdate'); // If we previously appended a segment that ends more than 3 targetDurations before
50258         // the currentTime_ that means that our conservative guess was too conservative.
50259         // In that case, reset the loader state so that we try to use any information gained
50260         // from the previous request to create a new, more accurate, sync-point.
50261 
50262         if (segment.end && this.currentTime_() - segment.end > segmentInfo.playlist.targetDuration * 3) {
50263           this.resetEverything();
50264           return;
50265         } // Don't do a rendition switch unless we have enough time to get a sync segment
50266         // and conservatively guess
50267 
50268 
50269         if (isWalkingForward) {
50270           this.trigger('bandwidthupdate');
50271         }
50272 
50273         this.trigger('progress'); // any time an update finishes and the last segment is in the
50274         // buffer, end the stream. this ensures the "ended" event will
50275         // fire if playback reaches that point.
50276 
50277         if (this.isEndOfStream_(segmentInfo.mediaIndex + 1, segmentInfo.playlist)) {
50278           this.endOfStream();
50279         }
50280 
50281         if (!this.paused()) {
50282           this.monitorBuffer_();
50283         }
50284       }
50285       /**
50286        * Records the current throughput of the decrypt, transmux, and append
50287        * portion of the semgment pipeline. `throughput.rate` is a the cumulative
50288        * moving average of the throughput. `throughput.count` is the number of
50289        * data points in the average.
50290        *
50291        * @private
50292        * @param {Object} segmentInfo the object returned by loadSegment
50293        */
50294 
50295     }, {
50296       key: 'recordThroughput_',
50297       value: function recordThroughput_(segmentInfo) {
50298         var rate = this.throughput.rate; // Add one to the time to ensure that we don't accidentally attempt to divide
50299         // by zero in the case where the throughput is ridiculously high
50300 
50301         var segmentProcessingTime = Date.now() - segmentInfo.endOfAllRequests + 1; // Multiply by 8000 to convert from bytes/millisecond to bits/second
50302 
50303         var segmentProcessingThroughput = Math.floor(segmentInfo.byteLength / segmentProcessingTime * 8 * 1000); // This is just a cumulative moving average calculation:
50304         //   newAvg = oldAvg + (sample - oldAvg) / (sampleCount + 1)
50305 
50306         this.throughput.rate += (segmentProcessingThroughput - rate) / ++this.throughput.count;
50307       }
50308       /**
50309        * Adds a cue to the segment-metadata track with some metadata information about the
50310        * segment
50311        *
50312        * @private
50313        * @param {Object} segmentInfo
50314        *        the object returned by loadSegment
50315        * @method addSegmentMetadataCue_
50316        */
50317 
50318     }, {
50319       key: 'addSegmentMetadataCue_',
50320       value: function addSegmentMetadataCue_(segmentInfo) {
50321         if (!this.segmentMetadataTrack_) {
50322           return;
50323         }
50324 
50325         var segment = segmentInfo.segment;
50326         var start = segment.start;
50327         var end = segment.end; // Do not try adding the cue if the start and end times are invalid.
50328 
50329         if (!finite(start) || !finite(end)) {
50330           return;
50331         }
50332 
50333         removeCuesFromTrack(start, end, this.segmentMetadataTrack_);
50334         var Cue = window$1.WebKitDataCue || window$1.VTTCue;
50335         var value = {
50336           custom: segment.custom,
50337           dateTimeObject: segment.dateTimeObject,
50338           dateTimeString: segment.dateTimeString,
50339           bandwidth: segmentInfo.playlist.attributes.BANDWIDTH,
50340           resolution: segmentInfo.playlist.attributes.RESOLUTION,
50341           codecs: segmentInfo.playlist.attributes.CODECS,
50342           byteLength: segmentInfo.byteLength,
50343           uri: segmentInfo.uri,
50344           timeline: segmentInfo.timeline,
50345           playlist: segmentInfo.playlist.uri,
50346           start: start,
50347           end: end
50348         };
50349         var data = JSON.stringify(value);
50350         var cue = new Cue(start, end, data); // Attach the metadata to the value property of the cue to keep consistency between
50351         // the differences of WebKitDataCue in safari and VTTCue in other browsers
50352 
50353         cue.value = value;
50354         this.segmentMetadataTrack_.addCue(cue);
50355       }
50356     }]);
50357     return SegmentLoader;
50358   }(videojs$1.EventTarget);
50359 
50360   var uint8ToUtf8 = function uint8ToUtf8(uintArray) {
50361     return decodeURIComponent(escape(String.fromCharCode.apply(null, uintArray)));
50362   };
50363   /**
50364    * @file vtt-segment-loader.js
50365    */
50366 
50367 
50368   var VTT_LINE_TERMINATORS = new Uint8Array('\n\n'.split('').map(function (_char2) {
50369     return _char2.charCodeAt(0);
50370   }));
50371   /**
50372    * An object that manages segment loading and appending.
50373    *
50374    * @class VTTSegmentLoader
50375    * @param {Object} options required and optional options
50376    * @extends videojs.EventTarget
50377    */
50378 
50379   var VTTSegmentLoader = function (_SegmentLoader) {
50380     inherits$2(VTTSegmentLoader, _SegmentLoader);
50381 
50382     function VTTSegmentLoader(settings) {
50383       var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
50384       classCallCheck$1(this, VTTSegmentLoader); // SegmentLoader requires a MediaSource be specified or it will throw an error;
50385       // however, VTTSegmentLoader has no need of a media source, so delete the reference
50386 
50387       var _this = possibleConstructorReturn$1(this, (VTTSegmentLoader.__proto__ || Object.getPrototypeOf(VTTSegmentLoader)).call(this, settings, options));
50388 
50389       _this.mediaSource_ = null;
50390       _this.subtitlesTrack_ = null;
50391       return _this;
50392     }
50393     /**
50394      * Indicates which time ranges are buffered
50395      *
50396      * @return {TimeRange}
50397      *         TimeRange object representing the current buffered ranges
50398      */
50399 
50400 
50401     createClass$1(VTTSegmentLoader, [{
50402       key: 'buffered_',
50403       value: function buffered_() {
50404         if (!this.subtitlesTrack_ || !this.subtitlesTrack_.cues.length) {
50405           return videojs$1.createTimeRanges();
50406         }
50407 
50408         var cues = this.subtitlesTrack_.cues;
50409         var start = cues[0].startTime;
50410         var end = cues[cues.length - 1].startTime;
50411         return videojs$1.createTimeRanges([[start, end]]);
50412       }
50413       /**
50414        * Gets and sets init segment for the provided map
50415        *
50416        * @param {Object} map
50417        *        The map object representing the init segment to get or set
50418        * @param {Boolean=} set
50419        *        If true, the init segment for the provided map should be saved
50420        * @return {Object}
50421        *         map object for desired init segment
50422        */
50423 
50424     }, {
50425       key: 'initSegment',
50426       value: function initSegment(map) {
50427         var set$$1 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
50428 
50429         if (!map) {
50430           return null;
50431         }
50432 
50433         var id = initSegmentId(map);
50434         var storedMap = this.initSegments_[id];
50435 
50436         if (set$$1 && !storedMap && map.bytes) {
50437           // append WebVTT line terminators to the media initialization segment if it exists
50438           // to follow the WebVTT spec (https://w3c.github.io/webvtt/#file-structure) that
50439           // requires two or more WebVTT line terminators between the WebVTT header and the
50440           // rest of the file
50441           var combinedByteLength = VTT_LINE_TERMINATORS.byteLength + map.bytes.byteLength;
50442           var combinedSegment = new Uint8Array(combinedByteLength);
50443           combinedSegment.set(map.bytes);
50444           combinedSegment.set(VTT_LINE_TERMINATORS, map.bytes.byteLength);
50445           this.initSegments_[id] = storedMap = {
50446             resolvedUri: map.resolvedUri,
50447             byterange: map.byterange,
50448             bytes: combinedSegment
50449           };
50450         }
50451 
50452         return storedMap || map;
50453       }
50454       /**
50455        * Returns true if all configuration required for loading is present, otherwise false.
50456        *
50457        * @return {Boolean} True if the all configuration is ready for loading
50458        * @private
50459        */
50460 
50461     }, {
50462       key: 'couldBeginLoading_',
50463       value: function couldBeginLoading_() {
50464         return this.playlist_ && this.subtitlesTrack_ && !this.paused();
50465       }
50466       /**
50467        * Once all the starting parameters have been specified, begin
50468        * operation. This method should only be invoked from the INIT
50469        * state.
50470        *
50471        * @private
50472        */
50473 
50474     }, {
50475       key: 'init_',
50476       value: function init_() {
50477         this.state = 'READY';
50478         this.resetEverything();
50479         return this.monitorBuffer_();
50480       }
50481       /**
50482        * Set a subtitle track on the segment loader to add subtitles to
50483        *
50484        * @param {TextTrack=} track
50485        *        The text track to add loaded subtitles to
50486        * @return {TextTrack}
50487        *        Returns the subtitles track
50488        */
50489 
50490     }, {
50491       key: 'track',
50492       value: function track(_track) {
50493         if (typeof _track === 'undefined') {
50494           return this.subtitlesTrack_;
50495         }
50496 
50497         this.subtitlesTrack_ = _track; // if we were unpaused but waiting for a sourceUpdater, start
50498         // buffering now
50499 
50500         if (this.state === 'INIT' && this.couldBeginLoading_()) {
50501           this.init_();
50502         }
50503 
50504         return this.subtitlesTrack_;
50505       }
50506       /**
50507        * Remove any data in the source buffer between start and end times
50508        * @param {Number} start - the start time of the region to remove from the buffer
50509        * @param {Number} end - the end time of the region to remove from the buffer
50510        */
50511 
50512     }, {
50513       key: 'remove',
50514       value: function remove(start, end) {
50515         removeCuesFromTrack(start, end, this.subtitlesTrack_);
50516       }
50517       /**
50518        * fill the buffer with segements unless the sourceBuffers are
50519        * currently updating
50520        *
50521        * Note: this function should only ever be called by monitorBuffer_
50522        * and never directly
50523        *
50524        * @private
50525        */
50526 
50527     }, {
50528       key: 'fillBuffer_',
50529       value: function fillBuffer_() {
50530         var _this2 = this;
50531 
50532         if (!this.syncPoint_) {
50533           this.syncPoint_ = this.syncController_.getSyncPoint(this.playlist_, this.duration_(), this.currentTimeline_, this.currentTime_());
50534         } // see if we need to begin loading immediately
50535 
50536 
50537         var segmentInfo = this.checkBuffer_(this.buffered_(), this.playlist_, this.mediaIndex, this.hasPlayed_(), this.currentTime_(), this.syncPoint_);
50538         segmentInfo = this.skipEmptySegments_(segmentInfo);
50539 
50540         if (!segmentInfo) {
50541           return;
50542         }
50543 
50544         if (this.syncController_.timestampOffsetForTimeline(segmentInfo.timeline) === null) {
50545           // We don't have the timestamp offset that we need to sync subtitles.
50546           // Rerun on a timestamp offset or user interaction.
50547           var checkTimestampOffset = function checkTimestampOffset() {
50548             _this2.state = 'READY';
50549 
50550             if (!_this2.paused()) {
50551               // if not paused, queue a buffer check as soon as possible
50552               _this2.monitorBuffer_();
50553             }
50554           };
50555 
50556           this.syncController_.one('timestampoffset', checkTimestampOffset);
50557           this.state = 'WAITING_ON_TIMELINE';
50558           return;
50559         }
50560 
50561         this.loadSegment_(segmentInfo);
50562       }
50563       /**
50564        * Prevents the segment loader from requesting segments we know contain no subtitles
50565        * by walking forward until we find the next segment that we don't know whether it is
50566        * empty or not.
50567        *
50568        * @param {Object} segmentInfo
50569        *        a segment info object that describes the current segment
50570        * @return {Object}
50571        *         a segment info object that describes the current segment
50572        */
50573 
50574     }, {
50575       key: 'skipEmptySegments_',
50576       value: function skipEmptySegments_(segmentInfo) {
50577         while (segmentInfo && segmentInfo.segment.empty) {
50578           segmentInfo = this.generateSegmentInfo_(segmentInfo.playlist, segmentInfo.mediaIndex + 1, segmentInfo.startOfSegment + segmentInfo.duration, segmentInfo.isSyncRequest);
50579         }
50580 
50581         return segmentInfo;
50582       }
50583       /**
50584        * append a decrypted segement to the SourceBuffer through a SourceUpdater
50585        *
50586        * @private
50587        */
50588 
50589     }, {
50590       key: 'handleSegment_',
50591       value: function handleSegment_() {
50592         var _this3 = this;
50593 
50594         if (!this.pendingSegment_ || !this.subtitlesTrack_) {
50595           this.state = 'READY';
50596           return;
50597         }
50598 
50599         this.state = 'APPENDING';
50600         var segmentInfo = this.pendingSegment_;
50601         var segment = segmentInfo.segment; // Make sure that vttjs has loaded, otherwise, wait till it finished loading
50602 
50603         if (typeof window$1.WebVTT !== 'function' && this.subtitlesTrack_ && this.subtitlesTrack_.tech_) {
50604           var loadHandler = void 0;
50605 
50606           var errorHandler = function errorHandler() {
50607             _this3.subtitlesTrack_.tech_.off('vttjsloaded', loadHandler);
50608 
50609             _this3.error({
50610               message: 'Error loading vtt.js'
50611             });
50612 
50613             _this3.state = 'READY';
50614 
50615             _this3.pause();
50616 
50617             _this3.trigger('error');
50618           };
50619 
50620           loadHandler = function loadHandler() {
50621             _this3.subtitlesTrack_.tech_.off('vttjserror', errorHandler);
50622 
50623             _this3.handleSegment_();
50624           };
50625 
50626           this.state = 'WAITING_ON_VTTJS';
50627           this.subtitlesTrack_.tech_.one('vttjsloaded', loadHandler);
50628           this.subtitlesTrack_.tech_.one('vttjserror', errorHandler);
50629           return;
50630         }
50631 
50632         segment.requested = true;
50633 
50634         try {
50635           this.parseVTTCues_(segmentInfo);
50636         } catch (e) {
50637           this.error({
50638             message: e.message
50639           });
50640           this.state = 'READY';
50641           this.pause();
50642           return this.trigger('error');
50643         }
50644 
50645         this.updateTimeMapping_(segmentInfo, this.syncController_.timelines[segmentInfo.timeline], this.playlist_);
50646 
50647         if (segmentInfo.isSyncRequest) {
50648           this.trigger('syncinfoupdate');
50649           this.pendingSegment_ = null;
50650           this.state = 'READY';
50651           return;
50652         }
50653 
50654         segmentInfo.byteLength = segmentInfo.bytes.byteLength;
50655         this.mediaSecondsLoaded += segment.duration;
50656 
50657         if (segmentInfo.cues.length) {
50658           // remove any overlapping cues to prevent doubling
50659           this.remove(segmentInfo.cues[0].endTime, segmentInfo.cues[segmentInfo.cues.length - 1].endTime);
50660         }
50661 
50662         segmentInfo.cues.forEach(function (cue) {
50663           _this3.subtitlesTrack_.addCue(cue);
50664         });
50665         this.handleUpdateEnd_();
50666       }
50667       /**
50668        * Uses the WebVTT parser to parse the segment response
50669        *
50670        * @param {Object} segmentInfo
50671        *        a segment info object that describes the current segment
50672        * @private
50673        */
50674 
50675     }, {
50676       key: 'parseVTTCues_',
50677       value: function parseVTTCues_(segmentInfo) {
50678         var decoder = void 0;
50679         var decodeBytesToString = false;
50680 
50681         if (typeof window$1.TextDecoder === 'function') {
50682           decoder = new window$1.TextDecoder('utf8');
50683         } else {
50684           decoder = window$1.WebVTT.StringDecoder();
50685           decodeBytesToString = true;
50686         }
50687 
50688         var parser = new window$1.WebVTT.Parser(window$1, window$1.vttjs, decoder);
50689         segmentInfo.cues = [];
50690         segmentInfo.timestampmap = {
50691           MPEGTS: 0,
50692           LOCAL: 0
50693         };
50694         parser.oncue = segmentInfo.cues.push.bind(segmentInfo.cues);
50695 
50696         parser.ontimestampmap = function (map) {
50697           return segmentInfo.timestampmap = map;
50698         };
50699 
50700         parser.onparsingerror = function (error) {
50701           videojs$1.log.warn('Error encountered when parsing cues: ' + error.message);
50702         };
50703 
50704         if (segmentInfo.segment.map) {
50705           var mapData = segmentInfo.segment.map.bytes;
50706 
50707           if (decodeBytesToString) {
50708             mapData = uint8ToUtf8(mapData);
50709           }
50710 
50711           parser.parse(mapData);
50712         }
50713 
50714         var segmentData = segmentInfo.bytes;
50715 
50716         if (decodeBytesToString) {
50717           segmentData = uint8ToUtf8(segmentData);
50718         }
50719 
50720         parser.parse(segmentData);
50721         parser.flush();
50722       }
50723       /**
50724        * Updates the start and end times of any cues parsed by the WebVTT parser using
50725        * the information parsed from the X-TIMESTAMP-MAP header and a TS to media time mapping
50726        * from the SyncController
50727        *
50728        * @param {Object} segmentInfo
50729        *        a segment info object that describes the current segment
50730        * @param {Object} mappingObj
50731        *        object containing a mapping from TS to media time
50732        * @param {Object} playlist
50733        *        the playlist object containing the segment
50734        * @private
50735        */
50736 
50737     }, {
50738       key: 'updateTimeMapping_',
50739       value: function updateTimeMapping_(segmentInfo, mappingObj, playlist) {
50740         var segment = segmentInfo.segment;
50741 
50742         if (!mappingObj) {
50743           // If the sync controller does not have a mapping of TS to Media Time for the
50744           // timeline, then we don't have enough information to update the cue
50745           // start/end times
50746           return;
50747         }
50748 
50749         if (!segmentInfo.cues.length) {
50750           // If there are no cues, we also do not have enough information to figure out
50751           // segment timing. Mark that the segment contains no cues so we don't re-request
50752           // an empty segment.
50753           segment.empty = true;
50754           return;
50755         }
50756 
50757         var timestampmap = segmentInfo.timestampmap;
50758         var diff = timestampmap.MPEGTS / 90000 - timestampmap.LOCAL + mappingObj.mapping;
50759         segmentInfo.cues.forEach(function (cue) {
50760           // First convert cue time to TS time using the timestamp-map provided within the vtt
50761           cue.startTime += diff;
50762           cue.endTime += diff;
50763         });
50764 
50765         if (!playlist.syncInfo) {
50766           var firstStart = segmentInfo.cues[0].startTime;
50767           var lastStart = segmentInfo.cues[segmentInfo.cues.length - 1].startTime;
50768           playlist.syncInfo = {
50769             mediaSequence: playlist.mediaSequence + segmentInfo.mediaIndex,
50770             time: Math.min(firstStart, lastStart - segment.duration)
50771           };
50772         }
50773       }
50774     }]);
50775     return VTTSegmentLoader;
50776   }(SegmentLoader);
50777   /**
50778    * @file ad-cue-tags.js
50779    */
50780 
50781   /**
50782    * Searches for an ad cue that overlaps with the given mediaTime
50783    */
50784 
50785 
50786   var findAdCue = function findAdCue(track, mediaTime) {
50787     var cues = track.cues;
50788 
50789     for (var i = 0; i < cues.length; i++) {
50790       var cue = cues[i];
50791 
50792       if (mediaTime >= cue.adStartTime && mediaTime <= cue.adEndTime) {
50793         return cue;
50794       }
50795     }
50796 
50797     return null;
50798   };
50799 
50800   var updateAdCues = function updateAdCues(media, track) {
50801     var offset = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
50802 
50803     if (!media.segments) {
50804       return;
50805     }
50806 
50807     var mediaTime = offset;
50808     var cue = void 0;
50809 
50810     for (var i = 0; i < media.segments.length; i++) {
50811       var segment = media.segments[i];
50812 
50813       if (!cue) {
50814         // Since the cues will span for at least the segment duration, adding a fudge
50815         // factor of half segment duration will prevent duplicate cues from being
50816         // created when timing info is not exact (e.g. cue start time initialized
50817         // at 10.006677, but next call mediaTime is 10.003332 )
50818         cue = findAdCue(track, mediaTime + segment.duration / 2);
50819       }
50820 
50821       if (cue) {
50822         if ('cueIn' in segment) {
50823           // Found a CUE-IN so end the cue
50824           cue.endTime = mediaTime;
50825           cue.adEndTime = mediaTime;
50826           mediaTime += segment.duration;
50827           cue = null;
50828           continue;
50829         }
50830 
50831         if (mediaTime < cue.endTime) {
50832           // Already processed this mediaTime for this cue
50833           mediaTime += segment.duration;
50834           continue;
50835         } // otherwise extend cue until a CUE-IN is found
50836 
50837 
50838         cue.endTime += segment.duration;
50839       } else {
50840         if ('cueOut' in segment) {
50841           cue = new window$1.VTTCue(mediaTime, mediaTime + segment.duration, segment.cueOut);
50842           cue.adStartTime = mediaTime; // Assumes tag format to be
50843           // #EXT-X-CUE-OUT:30
50844 
50845           cue.adEndTime = mediaTime + parseFloat(segment.cueOut);
50846           track.addCue(cue);
50847         }
50848 
50849         if ('cueOutCont' in segment) {
50850           // Entered into the middle of an ad cue
50851           var adOffset = void 0;
50852           var adTotal = void 0; // Assumes tag formate to be
50853           // #EXT-X-CUE-OUT-CONT:10/30
50854 
50855           var _segment$cueOutCont$s = segment.cueOutCont.split('/').map(parseFloat);
50856 
50857           var _segment$cueOutCont$s2 = slicedToArray(_segment$cueOutCont$s, 2);
50858 
50859           adOffset = _segment$cueOutCont$s2[0];
50860           adTotal = _segment$cueOutCont$s2[1];
50861           cue = new window$1.VTTCue(mediaTime, mediaTime + segment.duration, '');
50862           cue.adStartTime = mediaTime - adOffset;
50863           cue.adEndTime = cue.adStartTime + adTotal;
50864           track.addCue(cue);
50865         }
50866       }
50867 
50868       mediaTime += segment.duration;
50869     }
50870   };
50871   /**
50872    * @file sync-controller.js
50873    */
50874 
50875 
50876   var tsprobe = tsInspector.inspect;
50877   var syncPointStrategies = [// Stategy "VOD": Handle the VOD-case where the sync-point is *always*
50878   //                the equivalence display-time 0 === segment-index 0
50879   {
50880     name: 'VOD',
50881     run: function run(syncController, playlist, duration$$1, currentTimeline, currentTime) {
50882       if (duration$$1 !== Infinity) {
50883         var syncPoint = {
50884           time: 0,
50885           segmentIndex: 0
50886         };
50887         return syncPoint;
50888       }
50889 
50890       return null;
50891     }
50892   }, // Stategy "ProgramDateTime": We have a program-date-time tag in this playlist
50893   {
50894     name: 'ProgramDateTime',
50895     run: function run(syncController, playlist, duration$$1, currentTimeline, currentTime) {
50896       if (!syncController.datetimeToDisplayTime) {
50897         return null;
50898       }
50899 
50900       var segments = playlist.segments || [];
50901       var syncPoint = null;
50902       var lastDistance = null;
50903       currentTime = currentTime || 0;
50904 
50905       for (var i = 0; i < segments.length; i++) {
50906         var segment = segments[i];
50907 
50908         if (segment.dateTimeObject) {
50909           var segmentTime = segment.dateTimeObject.getTime() / 1000;
50910           var segmentStart = segmentTime + syncController.datetimeToDisplayTime;
50911           var distance = Math.abs(currentTime - segmentStart); // Once the distance begins to increase, or if distance is 0, we have passed
50912           // currentTime and can stop looking for better candidates
50913 
50914           if (lastDistance !== null && (distance === 0 || lastDistance < distance)) {
50915             break;
50916           }
50917 
50918           lastDistance = distance;
50919           syncPoint = {
50920             time: segmentStart,
50921             segmentIndex: i
50922           };
50923         }
50924       }
50925 
50926       return syncPoint;
50927     }
50928   }, // Stategy "Segment": We have a known time mapping for a timeline and a
50929   //                    segment in the current timeline with timing data
50930   {
50931     name: 'Segment',
50932     run: function run(syncController, playlist, duration$$1, currentTimeline, currentTime) {
50933       var segments = playlist.segments || [];
50934       var syncPoint = null;
50935       var lastDistance = null;
50936       currentTime = currentTime || 0;
50937 
50938       for (var i = 0; i < segments.length; i++) {
50939         var segment = segments[i];
50940 
50941         if (segment.timeline === currentTimeline && typeof segment.start !== 'undefined') {
50942           var distance = Math.abs(currentTime - segment.start); // Once the distance begins to increase, we have passed
50943           // currentTime and can stop looking for better candidates
50944 
50945           if (lastDistance !== null && lastDistance < distance) {
50946             break;
50947           }
50948 
50949           if (!syncPoint || lastDistance === null || lastDistance >= distance) {
50950             lastDistance = distance;
50951             syncPoint = {
50952               time: segment.start,
50953               segmentIndex: i
50954             };
50955           }
50956         }
50957       }
50958 
50959       return syncPoint;
50960     }
50961   }, // Stategy "Discontinuity": We have a discontinuity with a known
50962   //                          display-time
50963   {
50964     name: 'Discontinuity',
50965     run: function run(syncController, playlist, duration$$1, currentTimeline, currentTime) {
50966       var syncPoint = null;
50967       currentTime = currentTime || 0;
50968 
50969       if (playlist.discontinuityStarts && playlist.discontinuityStarts.length) {
50970         var lastDistance = null;
50971 
50972         for (var i = 0; i < playlist.discontinuityStarts.length; i++) {
50973           var segmentIndex = playlist.discontinuityStarts[i];
50974           var discontinuity = playlist.discontinuitySequence + i + 1;
50975           var discontinuitySync = syncController.discontinuities[discontinuity];
50976 
50977           if (discontinuitySync) {
50978             var distance = Math.abs(currentTime - discontinuitySync.time); // Once the distance begins to increase, we have passed
50979             // currentTime and can stop looking for better candidates
50980 
50981             if (lastDistance !== null && lastDistance < distance) {
50982               break;
50983             }
50984 
50985             if (!syncPoint || lastDistance === null || lastDistance >= distance) {
50986               lastDistance = distance;
50987               syncPoint = {
50988                 time: discontinuitySync.time,
50989                 segmentIndex: segmentIndex
50990               };
50991             }
50992           }
50993         }
50994       }
50995 
50996       return syncPoint;
50997     }
50998   }, // Stategy "Playlist": We have a playlist with a known mapping of
50999   //                     segment index to display time
51000   {
51001     name: 'Playlist',
51002     run: function run(syncController, playlist, duration$$1, currentTimeline, currentTime) {
51003       if (playlist.syncInfo) {
51004         var syncPoint = {
51005           time: playlist.syncInfo.time,
51006           segmentIndex: playlist.syncInfo.mediaSequence - playlist.mediaSequence
51007         };
51008         return syncPoint;
51009       }
51010 
51011       return null;
51012     }
51013   }];
51014 
51015   var SyncController = function (_videojs$EventTarget) {
51016     inherits$2(SyncController, _videojs$EventTarget);
51017 
51018     function SyncController() {
51019       classCallCheck$1(this, SyncController); // Segment Loader state variables...
51020       // ...for synching across variants
51021 
51022       var _this = possibleConstructorReturn$1(this, (SyncController.__proto__ || Object.getPrototypeOf(SyncController)).call(this));
51023 
51024       _this.inspectCache_ = undefined; // ...for synching across variants
51025 
51026       _this.timelines = [];
51027       _this.discontinuities = [];
51028       _this.datetimeToDisplayTime = null;
51029       _this.logger_ = logger('SyncController');
51030       return _this;
51031     }
51032     /**
51033      * Find a sync-point for the playlist specified
51034      *
51035      * A sync-point is defined as a known mapping from display-time to
51036      * a segment-index in the current playlist.
51037      *
51038      * @param {Playlist} playlist
51039      *        The playlist that needs a sync-point
51040      * @param {Number} duration
51041      *        Duration of the MediaSource (Infinite if playing a live source)
51042      * @param {Number} currentTimeline
51043      *        The last timeline from which a segment was loaded
51044      * @returns {Object}
51045      *          A sync-point object
51046      */
51047 
51048 
51049     createClass$1(SyncController, [{
51050       key: 'getSyncPoint',
51051       value: function getSyncPoint(playlist, duration$$1, currentTimeline, currentTime) {
51052         var syncPoints = this.runStrategies_(playlist, duration$$1, currentTimeline, currentTime);
51053 
51054         if (!syncPoints.length) {
51055           // Signal that we need to attempt to get a sync-point manually
51056           // by fetching a segment in the playlist and constructing
51057           // a sync-point from that information
51058           return null;
51059         } // Now find the sync-point that is closest to the currentTime because
51060         // that should result in the most accurate guess about which segment
51061         // to fetch
51062 
51063 
51064         return this.selectSyncPoint_(syncPoints, {
51065           key: 'time',
51066           value: currentTime
51067         });
51068       }
51069       /**
51070        * Calculate the amount of time that has expired off the playlist during playback
51071        *
51072        * @param {Playlist} playlist
51073        *        Playlist object to calculate expired from
51074        * @param {Number} duration
51075        *        Duration of the MediaSource (Infinity if playling a live source)
51076        * @returns {Number|null}
51077        *          The amount of time that has expired off the playlist during playback. Null
51078        *          if no sync-points for the playlist can be found.
51079        */
51080 
51081     }, {
51082       key: 'getExpiredTime',
51083       value: function getExpiredTime(playlist, duration$$1) {
51084         if (!playlist || !playlist.segments) {
51085           return null;
51086         }
51087 
51088         var syncPoints = this.runStrategies_(playlist, duration$$1, playlist.discontinuitySequence, 0); // Without sync-points, there is not enough information to determine the expired time
51089 
51090         if (!syncPoints.length) {
51091           return null;
51092         }
51093 
51094         var syncPoint = this.selectSyncPoint_(syncPoints, {
51095           key: 'segmentIndex',
51096           value: 0
51097         }); // If the sync-point is beyond the start of the playlist, we want to subtract the
51098         // duration from index 0 to syncPoint.segmentIndex instead of adding.
51099 
51100         if (syncPoint.segmentIndex > 0) {
51101           syncPoint.time *= -1;
51102         }
51103 
51104         return Math.abs(syncPoint.time + sumDurations(playlist, syncPoint.segmentIndex, 0));
51105       }
51106       /**
51107        * Runs each sync-point strategy and returns a list of sync-points returned by the
51108        * strategies
51109        *
51110        * @private
51111        * @param {Playlist} playlist
51112        *        The playlist that needs a sync-point
51113        * @param {Number} duration
51114        *        Duration of the MediaSource (Infinity if playing a live source)
51115        * @param {Number} currentTimeline
51116        *        The last timeline from which a segment was loaded
51117        * @returns {Array}
51118        *          A list of sync-point objects
51119        */
51120 
51121     }, {
51122       key: 'runStrategies_',
51123       value: function runStrategies_(playlist, duration$$1, currentTimeline, currentTime) {
51124         var syncPoints = []; // Try to find a sync-point in by utilizing various strategies...
51125 
51126         for (var i = 0; i < syncPointStrategies.length; i++) {
51127           var strategy = syncPointStrategies[i];
51128           var syncPoint = strategy.run(this, playlist, duration$$1, currentTimeline, currentTime);
51129 
51130           if (syncPoint) {
51131             syncPoint.strategy = strategy.name;
51132             syncPoints.push({
51133               strategy: strategy.name,
51134               syncPoint: syncPoint
51135             });
51136           }
51137         }
51138 
51139         return syncPoints;
51140       }
51141       /**
51142        * Selects the sync-point nearest the specified target
51143        *
51144        * @private
51145        * @param {Array} syncPoints
51146        *        List of sync-points to select from
51147        * @param {Object} target
51148        *        Object specifying the property and value we are targeting
51149        * @param {String} target.key
51150        *        Specifies the property to target. Must be either 'time' or 'segmentIndex'
51151        * @param {Number} target.value
51152        *        The value to target for the specified key.
51153        * @returns {Object}
51154        *          The sync-point nearest the target
51155        */
51156 
51157     }, {
51158       key: 'selectSyncPoint_',
51159       value: function selectSyncPoint_(syncPoints, target) {
51160         var bestSyncPoint = syncPoints[0].syncPoint;
51161         var bestDistance = Math.abs(syncPoints[0].syncPoint[target.key] - target.value);
51162         var bestStrategy = syncPoints[0].strategy;
51163 
51164         for (var i = 1; i < syncPoints.length; i++) {
51165           var newDistance = Math.abs(syncPoints[i].syncPoint[target.key] - target.value);
51166 
51167           if (newDistance < bestDistance) {
51168             bestDistance = newDistance;
51169             bestSyncPoint = syncPoints[i].syncPoint;
51170             bestStrategy = syncPoints[i].strategy;
51171           }
51172         }
51173 
51174         this.logger_('syncPoint for [' + target.key + ': ' + target.value + '] chosen with strategy' + (' [' + bestStrategy + ']: [time:' + bestSyncPoint.time + ',') + (' segmentIndex:' + bestSyncPoint.segmentIndex + ']'));
51175         return bestSyncPoint;
51176       }
51177       /**
51178        * Save any meta-data present on the segments when segments leave
51179        * the live window to the playlist to allow for synchronization at the
51180        * playlist level later.
51181        *
51182        * @param {Playlist} oldPlaylist - The previous active playlist
51183        * @param {Playlist} newPlaylist - The updated and most current playlist
51184        */
51185 
51186     }, {
51187       key: 'saveExpiredSegmentInfo',
51188       value: function saveExpiredSegmentInfo(oldPlaylist, newPlaylist) {
51189         var mediaSequenceDiff = newPlaylist.mediaSequence - oldPlaylist.mediaSequence; // When a segment expires from the playlist and it has a start time
51190         // save that information as a possible sync-point reference in future
51191 
51192         for (var i = mediaSequenceDiff - 1; i >= 0; i--) {
51193           var lastRemovedSegment = oldPlaylist.segments[i];
51194 
51195           if (lastRemovedSegment && typeof lastRemovedSegment.start !== 'undefined') {
51196             newPlaylist.syncInfo = {
51197               mediaSequence: oldPlaylist.mediaSequence + i,
51198               time: lastRemovedSegment.start
51199             };
51200             this.logger_('playlist refresh sync: [time:' + newPlaylist.syncInfo.time + ',' + (' mediaSequence: ' + newPlaylist.syncInfo.mediaSequence + ']'));
51201             this.trigger('syncinfoupdate');
51202             break;
51203           }
51204         }
51205       }
51206       /**
51207        * Save the mapping from playlist's ProgramDateTime to display. This should
51208        * only ever happen once at the start of playback.
51209        *
51210        * @param {Playlist} playlist - The currently active playlist
51211        */
51212 
51213     }, {
51214       key: 'setDateTimeMapping',
51215       value: function setDateTimeMapping(playlist) {
51216         if (!this.datetimeToDisplayTime && playlist.segments && playlist.segments.length && playlist.segments[0].dateTimeObject) {
51217           var playlistTimestamp = playlist.segments[0].dateTimeObject.getTime() / 1000;
51218           this.datetimeToDisplayTime = -playlistTimestamp;
51219         }
51220       }
51221       /**
51222        * Reset the state of the inspection cache when we do a rendition
51223        * switch
51224        */
51225 
51226     }, {
51227       key: 'reset',
51228       value: function reset() {
51229         this.inspectCache_ = undefined;
51230       }
51231       /**
51232        * Probe or inspect a fmp4 or an mpeg2-ts segment to determine the start
51233        * and end of the segment in it's internal "media time". Used to generate
51234        * mappings from that internal "media time" to the display time that is
51235        * shown on the player.
51236        *
51237        * @param {SegmentInfo} segmentInfo - The current active request information
51238        */
51239 
51240     }, {
51241       key: 'probeSegmentInfo',
51242       value: function probeSegmentInfo(segmentInfo) {
51243         var segment = segmentInfo.segment;
51244         var playlist = segmentInfo.playlist;
51245         var timingInfo = void 0;
51246 
51247         if (segment.map) {
51248           timingInfo = this.probeMp4Segment_(segmentInfo);
51249         } else {
51250           timingInfo = this.probeTsSegment_(segmentInfo);
51251         }
51252 
51253         if (timingInfo) {
51254           if (this.calculateSegmentTimeMapping_(segmentInfo, timingInfo)) {
51255             this.saveDiscontinuitySyncInfo_(segmentInfo); // If the playlist does not have sync information yet, record that information
51256             // now with segment timing information
51257 
51258             if (!playlist.syncInfo) {
51259               playlist.syncInfo = {
51260                 mediaSequence: playlist.mediaSequence + segmentInfo.mediaIndex,
51261                 time: segment.start
51262               };
51263             }
51264           }
51265         }
51266 
51267         return timingInfo;
51268       }
51269       /**
51270        * Probe an fmp4 segment to determine the start of the segment
51271        * in it's internal "composition time", which is equal to the base
51272        * media decode time plus the composition time offset value
51273        *
51274        * @private
51275        * @param {SegmentInfo} segmentInfo - The current active request information
51276        * @return {object} The start and end time of the current segment in "composition time"
51277        */
51278 
51279     }, {
51280       key: 'probeMp4Segment_',
51281       value: function probeMp4Segment_(segmentInfo) {
51282         var segment = segmentInfo.segment; // get timescales from init segment
51283 
51284         var timescales = probe.timescale(segment.map.bytes); // calculate composition start time using the timescales and information
51285         // contained within the media segment
51286 
51287         var compositionStartTime = probe.compositionStartTime(timescales, segmentInfo.bytes);
51288 
51289         if (segmentInfo.timestampOffset !== null) {
51290           segmentInfo.timestampOffset -= compositionStartTime;
51291         }
51292 
51293         return {
51294           start: compositionStartTime,
51295           end: compositionStartTime + segment.duration
51296         };
51297       }
51298       /**
51299        * Probe an mpeg2-ts segment to determine the start and end of the segment
51300        * in it's internal "media time".
51301        *
51302        * @private
51303        * @param {SegmentInfo} segmentInfo - The current active request information
51304        * @return {object} The start and end time of the current segment in "media time"
51305        */
51306 
51307     }, {
51308       key: 'probeTsSegment_',
51309       value: function probeTsSegment_(segmentInfo) {
51310         var timeInfo = tsprobe(segmentInfo.bytes, this.inspectCache_);
51311         var segmentStartTime = void 0;
51312         var segmentEndTime = void 0;
51313         var segmentTimestampInfo = void 0;
51314 
51315         if (!timeInfo) {
51316           return null;
51317         }
51318 
51319         if (timeInfo.video && timeInfo.video.length === 2) {
51320           this.inspectCache_ = timeInfo.video[1].dts;
51321           segmentStartTime = timeInfo.video[0].dtsTime;
51322           segmentEndTime = timeInfo.video[1].dtsTime;
51323           segmentTimestampInfo = timeInfo.video;
51324         } else if (timeInfo.audio && timeInfo.audio.length === 2) {
51325           this.inspectCache_ = timeInfo.audio[1].dts;
51326           segmentStartTime = timeInfo.audio[0].dtsTime;
51327           segmentEndTime = timeInfo.audio[1].dtsTime;
51328           segmentTimestampInfo = timeInfo.audio;
51329         }
51330 
51331         var probedInfo = {
51332           segmentTimestampInfo: segmentTimestampInfo,
51333           start: segmentStartTime,
51334           end: segmentEndTime,
51335           containsVideo: timeInfo.video && timeInfo.video.length === 2,
51336           containsAudio: timeInfo.audio && timeInfo.audio.length === 2
51337         };
51338         return probedInfo;
51339       }
51340     }, {
51341       key: 'timestampOffsetForTimeline',
51342       value: function timestampOffsetForTimeline(timeline) {
51343         if (typeof this.timelines[timeline] === 'undefined') {
51344           return null;
51345         }
51346 
51347         return this.timelines[timeline].time;
51348       }
51349     }, {
51350       key: 'mappingForTimeline',
51351       value: function mappingForTimeline(timeline) {
51352         if (typeof this.timelines[timeline] === 'undefined') {
51353           return null;
51354         }
51355 
51356         return this.timelines[timeline].mapping;
51357       }
51358       /**
51359        * Use the "media time" for a segment to generate a mapping to "display time" and
51360        * save that display time to the segment.
51361        *
51362        * @private
51363        * @param {SegmentInfo} segmentInfo
51364        *        The current active request information
51365        * @param {object} timingInfo
51366        *        The start and end time of the current segment in "media time"
51367        * @returns {Boolean}
51368        *          Returns false if segment time mapping could not be calculated
51369        */
51370 
51371     }, {
51372       key: 'calculateSegmentTimeMapping_',
51373       value: function calculateSegmentTimeMapping_(segmentInfo, timingInfo) {
51374         var segment = segmentInfo.segment;
51375         var mappingObj = this.timelines[segmentInfo.timeline];
51376 
51377         if (segmentInfo.timestampOffset !== null) {
51378           mappingObj = {
51379             time: segmentInfo.startOfSegment,
51380             mapping: segmentInfo.startOfSegment - timingInfo.start
51381           };
51382           this.timelines[segmentInfo.timeline] = mappingObj;
51383           this.trigger('timestampoffset');
51384           this.logger_('time mapping for timeline ' + segmentInfo.timeline + ': ' + ('[time: ' + mappingObj.time + '] [mapping: ' + mappingObj.mapping + ']'));
51385           segment.start = segmentInfo.startOfSegment;
51386           segment.end = timingInfo.end + mappingObj.mapping;
51387         } else if (mappingObj) {
51388           segment.start = timingInfo.start + mappingObj.mapping;
51389           segment.end = timingInfo.end + mappingObj.mapping;
51390         } else {
51391           return false;
51392         }
51393 
51394         return true;
51395       }
51396       /**
51397        * Each time we have discontinuity in the playlist, attempt to calculate the location
51398        * in display of the start of the discontinuity and save that. We also save an accuracy
51399        * value so that we save values with the most accuracy (closest to 0.)
51400        *
51401        * @private
51402        * @param {SegmentInfo} segmentInfo - The current active request information
51403        */
51404 
51405     }, {
51406       key: 'saveDiscontinuitySyncInfo_',
51407       value: function saveDiscontinuitySyncInfo_(segmentInfo) {
51408         var playlist = segmentInfo.playlist;
51409         var segment = segmentInfo.segment; // If the current segment is a discontinuity then we know exactly where
51410         // the start of the range and it's accuracy is 0 (greater accuracy values
51411         // mean more approximation)
51412 
51413         if (segment.discontinuity) {
51414           this.discontinuities[segment.timeline] = {
51415             time: segment.start,
51416             accuracy: 0
51417           };
51418         } else if (playlist.discontinuityStarts && playlist.discontinuityStarts.length) {
51419           // Search for future discontinuities that we can provide better timing
51420           // information for and save that information for sync purposes
51421           for (var i = 0; i < playlist.discontinuityStarts.length; i++) {
51422             var segmentIndex = playlist.discontinuityStarts[i];
51423             var discontinuity = playlist.discontinuitySequence + i + 1;
51424             var mediaIndexDiff = segmentIndex - segmentInfo.mediaIndex;
51425             var accuracy = Math.abs(mediaIndexDiff);
51426 
51427             if (!this.discontinuities[discontinuity] || this.discontinuities[discontinuity].accuracy > accuracy) {
51428               var time = void 0;
51429 
51430               if (mediaIndexDiff < 0) {
51431                 time = segment.start - sumDurations(playlist, segmentInfo.mediaIndex, segmentIndex);
51432               } else {
51433                 time = segment.end + sumDurations(playlist, segmentInfo.mediaIndex + 1, segmentIndex);
51434               }
51435 
51436               this.discontinuities[discontinuity] = {
51437                 time: time,
51438                 accuracy: accuracy
51439               };
51440             }
51441           }
51442         }
51443       }
51444     }]);
51445     return SyncController;
51446   }(videojs$1.EventTarget);
51447 
51448   var Decrypter$1 = new shimWorker("./decrypter-worker.worker.js", function (window, document$$1) {
51449     var self = this;
51450 
51451     var decrypterWorker = function () {
51452       /*
51453        * pkcs7.pad
51454        * https://github.com/brightcove/pkcs7
51455        *
51456        * Copyright (c) 2014 Brightcove
51457        * Licensed under the apache2 license.
51458        */
51459 
51460       /**
51461        * Returns the subarray of a Uint8Array without PKCS#7 padding.
51462        * @param padded {Uint8Array} unencrypted bytes that have been padded
51463        * @return {Uint8Array} the unpadded bytes
51464        * @see http://tools.ietf.org/html/rfc5652
51465        */
51466       function unpad(padded) {
51467         return padded.subarray(0, padded.byteLength - padded[padded.byteLength - 1]);
51468       }
51469 
51470       var classCallCheck = function classCallCheck(instance, Constructor) {
51471         if (!(instance instanceof Constructor)) {
51472           throw new TypeError("Cannot call a class as a function");
51473         }
51474       };
51475 
51476       var createClass = function () {
51477         function defineProperties(target, props) {
51478           for (var i = 0; i < props.length; i++) {
51479             var descriptor = props[i];
51480             descriptor.enumerable = descriptor.enumerable || false;
51481             descriptor.configurable = true;
51482             if ("value" in descriptor) descriptor.writable = true;
51483             Object.defineProperty(target, descriptor.key, descriptor);
51484           }
51485         }
51486 
51487         return function (Constructor, protoProps, staticProps) {
51488           if (protoProps) defineProperties(Constructor.prototype, protoProps);
51489           if (staticProps) defineProperties(Constructor, staticProps);
51490           return Constructor;
51491         };
51492       }();
51493 
51494       var inherits = function inherits(subClass, superClass) {
51495         if (typeof superClass !== "function" && superClass !== null) {
51496           throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
51497         }
51498 
51499         subClass.prototype = Object.create(superClass && superClass.prototype, {
51500           constructor: {
51501             value: subClass,
51502             enumerable: false,
51503             writable: true,
51504             configurable: true
51505           }
51506         });
51507         if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
51508       };
51509 
51510       var possibleConstructorReturn = function possibleConstructorReturn(self, call) {
51511         if (!self) {
51512           throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
51513         }
51514 
51515         return call && (typeof call === "object" || typeof call === "function") ? call : self;
51516       };
51517       /**
51518        * @file aes.js
51519        *
51520        * This file contains an adaptation of the AES decryption algorithm
51521        * from the Standford Javascript Cryptography Library. That work is
51522        * covered by the following copyright and permissions notice:
51523        *
51524        * Copyright 2009-2010 Emily Stark, Mike Hamburg, Dan Boneh.
51525        * All rights reserved.
51526        *
51527        * Redistribution and use in source and binary forms, with or without
51528        * modification, are permitted provided that the following conditions are
51529        * met:
51530        *
51531        * 1. Redistributions of source code must retain the above copyright
51532        *    notice, this list of conditions and the following disclaimer.
51533        *
51534        * 2. Redistributions in binary form must reproduce the above
51535        *    copyright notice, this list of conditions and the following
51536        *    disclaimer in the documentation and/or other materials provided
51537        *    with the distribution.
51538        *
51539        * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
51540        * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
51541        * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
51542        * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR CONTRIBUTORS BE
51543        * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
51544        * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
51545        * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
51546        * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
51547        * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
51548        * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
51549        * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
51550        *
51551        * The views and conclusions contained in the software and documentation
51552        * are those of the authors and should not be interpreted as representing
51553        * official policies, either expressed or implied, of the authors.
51554        */
51555 
51556       /**
51557        * Expand the S-box tables.
51558        *
51559        * @private
51560        */
51561 
51562 
51563       var precompute = function precompute() {
51564         var tables = [[[], [], [], [], []], [[], [], [], [], []]];
51565         var encTable = tables[0];
51566         var decTable = tables[1];
51567         var sbox = encTable[4];
51568         var sboxInv = decTable[4];
51569         var i = void 0;
51570         var x = void 0;
51571         var xInv = void 0;
51572         var d = [];
51573         var th = [];
51574         var x2 = void 0;
51575         var x4 = void 0;
51576         var x8 = void 0;
51577         var s = void 0;
51578         var tEnc = void 0;
51579         var tDec = void 0; // Compute double and third tables
51580 
51581         for (i = 0; i < 256; i++) {
51582           th[(d[i] = i << 1 ^ (i >> 7) * 283) ^ i] = i;
51583         }
51584 
51585         for (x = xInv = 0; !sbox[x]; x ^= x2 || 1, xInv = th[xInv] || 1) {
51586           // Compute sbox
51587           s = xInv ^ xInv << 1 ^ xInv << 2 ^ xInv << 3 ^ xInv << 4;
51588           s = s >> 8 ^ s & 255 ^ 99;
51589           sbox[x] = s;
51590           sboxInv[s] = x; // Compute MixColumns
51591 
51592           x8 = d[x4 = d[x2 = d[x]]];
51593           tDec = x8 * 0x1010101 ^ x4 * 0x10001 ^ x2 * 0x101 ^ x * 0x1010100;
51594           tEnc = d[s] * 0x101 ^ s * 0x1010100;
51595 
51596           for (i = 0; i < 4; i++) {
51597             encTable[i][x] = tEnc = tEnc << 24 ^ tEnc >>> 8;
51598             decTable[i][s] = tDec = tDec << 24 ^ tDec >>> 8;
51599           }
51600         } // Compactify. Considerable speedup on Firefox.
51601 
51602 
51603         for (i = 0; i < 5; i++) {
51604           encTable[i] = encTable[i].slice(0);
51605           decTable[i] = decTable[i].slice(0);
51606         }
51607 
51608         return tables;
51609       };
51610 
51611       var aesTables = null;
51612       /**
51613        * Schedule out an AES key for both encryption and decryption. This
51614        * is a low-level class. Use a cipher mode to do bulk encryption.
51615        *
51616        * @class AES
51617        * @param key {Array} The key as an array of 4, 6 or 8 words.
51618        */
51619 
51620       var AES = function () {
51621         function AES(key) {
51622           classCallCheck(this, AES);
51623           /**
51624            * The expanded S-box and inverse S-box tables. These will be computed
51625            * on the client so that we don't have to send them down the wire.
51626            *
51627            * There are two tables, _tables[0] is for encryption and
51628            * _tables[1] is for decryption.
51629            *
51630            * The first 4 sub-tables are the expanded S-box with MixColumns. The
51631            * last (_tables[01][4]) is the S-box itself.
51632            *
51633            * @private
51634            */
51635           // if we have yet to precompute the S-box tables
51636           // do so now
51637 
51638           if (!aesTables) {
51639             aesTables = precompute();
51640           } // then make a copy of that object for use
51641 
51642 
51643           this._tables = [[aesTables[0][0].slice(), aesTables[0][1].slice(), aesTables[0][2].slice(), aesTables[0][3].slice(), aesTables[0][4].slice()], [aesTables[1][0].slice(), aesTables[1][1].slice(), aesTables[1][2].slice(), aesTables[1][3].slice(), aesTables[1][4].slice()]];
51644           var i = void 0;
51645           var j = void 0;
51646           var tmp = void 0;
51647           var encKey = void 0;
51648           var decKey = void 0;
51649           var sbox = this._tables[0][4];
51650           var decTable = this._tables[1];
51651           var keyLen = key.length;
51652           var rcon = 1;
51653 
51654           if (keyLen !== 4 && keyLen !== 6 && keyLen !== 8) {
51655             throw new Error('Invalid aes key size');
51656           }
51657 
51658           encKey = key.slice(0);
51659           decKey = [];
51660           this._key = [encKey, decKey]; // schedule encryption keys
51661 
51662           for (i = keyLen; i < 4 * keyLen + 28; i++) {
51663             tmp = encKey[i - 1]; // apply sbox
51664 
51665             if (i % keyLen === 0 || keyLen === 8 && i % keyLen === 4) {
51666               tmp = sbox[tmp >>> 24] << 24 ^ sbox[tmp >> 16 & 255] << 16 ^ sbox[tmp >> 8 & 255] << 8 ^ sbox[tmp & 255]; // shift rows and add rcon
51667 
51668               if (i % keyLen === 0) {
51669                 tmp = tmp << 8 ^ tmp >>> 24 ^ rcon << 24;
51670                 rcon = rcon << 1 ^ (rcon >> 7) * 283;
51671               }
51672             }
51673 
51674             encKey[i] = encKey[i - keyLen] ^ tmp;
51675           } // schedule decryption keys
51676 
51677 
51678           for (j = 0; i; j++, i--) {
51679             tmp = encKey[j & 3 ? i : i - 4];
51680 
51681             if (i <= 4 || j < 4) {
51682               decKey[j] = tmp;
51683             } else {
51684               decKey[j] = decTable[0][sbox[tmp >>> 24]] ^ decTable[1][sbox[tmp >> 16 & 255]] ^ decTable[2][sbox[tmp >> 8 & 255]] ^ decTable[3][sbox[tmp & 255]];
51685             }
51686           }
51687         }
51688         /**
51689          * Decrypt 16 bytes, specified as four 32-bit words.
51690          *
51691          * @param {Number} encrypted0 the first word to decrypt
51692          * @param {Number} encrypted1 the second word to decrypt
51693          * @param {Number} encrypted2 the third word to decrypt
51694          * @param {Number} encrypted3 the fourth word to decrypt
51695          * @param {Int32Array} out the array to write the decrypted words
51696          * into
51697          * @param {Number} offset the offset into the output array to start
51698          * writing results
51699          * @return {Array} The plaintext.
51700          */
51701 
51702 
51703         AES.prototype.decrypt = function decrypt$$1(encrypted0, encrypted1, encrypted2, encrypted3, out, offset) {
51704           var key = this._key[1]; // state variables a,b,c,d are loaded with pre-whitened data
51705 
51706           var a = encrypted0 ^ key[0];
51707           var b = encrypted3 ^ key[1];
51708           var c = encrypted2 ^ key[2];
51709           var d = encrypted1 ^ key[3];
51710           var a2 = void 0;
51711           var b2 = void 0;
51712           var c2 = void 0; // key.length === 2 ?
51713 
51714           var nInnerRounds = key.length / 4 - 2;
51715           var i = void 0;
51716           var kIndex = 4;
51717           var table = this._tables[1]; // load up the tables
51718 
51719           var table0 = table[0];
51720           var table1 = table[1];
51721           var table2 = table[2];
51722           var table3 = table[3];
51723           var sbox = table[4]; // Inner rounds. Cribbed from OpenSSL.
51724 
51725           for (i = 0; i < nInnerRounds; i++) {
51726             a2 = table0[a >>> 24] ^ table1[b >> 16 & 255] ^ table2[c >> 8 & 255] ^ table3[d & 255] ^ key[kIndex];
51727             b2 = table0[b >>> 24] ^ table1[c >> 16 & 255] ^ table2[d >> 8 & 255] ^ table3[a & 255] ^ key[kIndex + 1];
51728             c2 = table0[c >>> 24] ^ table1[d >> 16 & 255] ^ table2[a >> 8 & 255] ^ table3[b & 255] ^ key[kIndex + 2];
51729             d = table0[d >>> 24] ^ table1[a >> 16 & 255] ^ table2[b >> 8 & 255] ^ table3[c & 255] ^ key[kIndex + 3];
51730             kIndex += 4;
51731             a = a2;
51732             b = b2;
51733             c = c2;
51734           } // Last round.
51735 
51736 
51737           for (i = 0; i < 4; i++) {
51738             out[(3 & -i) + offset] = sbox[a >>> 24] << 24 ^ sbox[b >> 16 & 255] << 16 ^ sbox[c >> 8 & 255] << 8 ^ sbox[d & 255] ^ key[kIndex++];
51739             a2 = a;
51740             a = b;
51741             b = c;
51742             c = d;
51743             d = a2;
51744           }
51745         };
51746 
51747         return AES;
51748       }();
51749       /**
51750        * @file stream.js
51751        */
51752 
51753       /**
51754        * A lightweight readable stream implemention that handles event dispatching.
51755        *
51756        * @class Stream
51757        */
51758 
51759 
51760       var Stream = function () {
51761         function Stream() {
51762           classCallCheck(this, Stream);
51763           this.listeners = {};
51764         }
51765         /**
51766          * Add a listener for a specified event type.
51767          *
51768          * @param {String} type the event name
51769          * @param {Function} listener the callback to be invoked when an event of
51770          * the specified type occurs
51771          */
51772 
51773 
51774         Stream.prototype.on = function on(type, listener) {
51775           if (!this.listeners[type]) {
51776             this.listeners[type] = [];
51777           }
51778 
51779           this.listeners[type].push(listener);
51780         };
51781         /**
51782          * Remove a listener for a specified event type.
51783          *
51784          * @param {String} type the event name
51785          * @param {Function} listener  a function previously registered for this
51786          * type of event through `on`
51787          * @return {Boolean} if we could turn it off or not
51788          */
51789 
51790 
51791         Stream.prototype.off = function off(type, listener) {
51792           if (!this.listeners[type]) {
51793             return false;
51794           }
51795 
51796           var index = this.listeners[type].indexOf(listener);
51797           this.listeners[type].splice(index, 1);
51798           return index > -1;
51799         };
51800         /**
51801          * Trigger an event of the specified type on this stream. Any additional
51802          * arguments to this function are passed as parameters to event listeners.
51803          *
51804          * @param {String} type the event name
51805          */
51806 
51807 
51808         Stream.prototype.trigger = function trigger(type) {
51809           var callbacks = this.listeners[type];
51810 
51811           if (!callbacks) {
51812             return;
51813           } // Slicing the arguments on every invocation of this method
51814           // can add a significant amount of overhead. Avoid the
51815           // intermediate object creation for the common case of a
51816           // single callback argument
51817 
51818 
51819           if (arguments.length === 2) {
51820             var length = callbacks.length;
51821 
51822             for (var i = 0; i < length; ++i) {
51823               callbacks[i].call(this, arguments[1]);
51824             }
51825           } else {
51826             var args = Array.prototype.slice.call(arguments, 1);
51827             var _length = callbacks.length;
51828 
51829             for (var _i = 0; _i < _length; ++_i) {
51830               callbacks[_i].apply(this, args);
51831             }
51832           }
51833         };
51834         /**
51835          * Destroys the stream and cleans up.
51836          */
51837 
51838 
51839         Stream.prototype.dispose = function dispose() {
51840           this.listeners = {};
51841         };
51842         /**
51843          * Forwards all `data` events on this stream to the destination stream. The
51844          * destination stream should provide a method `push` to receive the data
51845          * events as they arrive.
51846          *
51847          * @param {Stream} destination the stream that will receive all `data` events
51848          * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
51849          */
51850 
51851 
51852         Stream.prototype.pipe = function pipe(destination) {
51853           this.on('data', function (data) {
51854             destination.push(data);
51855           });
51856         };
51857 
51858         return Stream;
51859       }();
51860       /**
51861        * @file async-stream.js
51862        */
51863 
51864       /**
51865        * A wrapper around the Stream class to use setTiemout
51866        * and run stream "jobs" Asynchronously
51867        *
51868        * @class AsyncStream
51869        * @extends Stream
51870        */
51871 
51872 
51873       var AsyncStream$$1 = function (_Stream) {
51874         inherits(AsyncStream$$1, _Stream);
51875 
51876         function AsyncStream$$1() {
51877           classCallCheck(this, AsyncStream$$1);
51878 
51879           var _this = possibleConstructorReturn(this, _Stream.call(this, Stream));
51880 
51881           _this.jobs = [];
51882           _this.delay = 1;
51883           _this.timeout_ = null;
51884           return _this;
51885         }
51886         /**
51887          * process an async job
51888          *
51889          * @private
51890          */
51891 
51892 
51893         AsyncStream$$1.prototype.processJob_ = function processJob_() {
51894           this.jobs.shift()();
51895 
51896           if (this.jobs.length) {
51897             this.timeout_ = setTimeout(this.processJob_.bind(this), this.delay);
51898           } else {
51899             this.timeout_ = null;
51900           }
51901         };
51902         /**
51903          * push a job into the stream
51904          *
51905          * @param {Function} job the job to push into the stream
51906          */
51907 
51908 
51909         AsyncStream$$1.prototype.push = function push(job) {
51910           this.jobs.push(job);
51911 
51912           if (!this.timeout_) {
51913             this.timeout_ = setTimeout(this.processJob_.bind(this), this.delay);
51914           }
51915         };
51916 
51917         return AsyncStream$$1;
51918       }(Stream);
51919       /**
51920        * @file decrypter.js
51921        *
51922        * An asynchronous implementation of AES-128 CBC decryption with
51923        * PKCS#7 padding.
51924        */
51925 
51926       /**
51927        * Convert network-order (big-endian) bytes into their little-endian
51928        * representation.
51929        */
51930 
51931 
51932       var ntoh = function ntoh(word) {
51933         return word << 24 | (word & 0xff00) << 8 | (word & 0xff0000) >> 8 | word >>> 24;
51934       };
51935       /**
51936        * Decrypt bytes using AES-128 with CBC and PKCS#7 padding.
51937        *
51938        * @param {Uint8Array} encrypted the encrypted bytes
51939        * @param {Uint32Array} key the bytes of the decryption key
51940        * @param {Uint32Array} initVector the initialization vector (IV) to
51941        * use for the first round of CBC.
51942        * @return {Uint8Array} the decrypted bytes
51943        *
51944        * @see http://en.wikipedia.org/wiki/Advanced_Encryption_Standard
51945        * @see http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_Block_Chaining_.28CBC.29
51946        * @see https://tools.ietf.org/html/rfc2315
51947        */
51948 
51949 
51950       var decrypt$$1 = function decrypt$$1(encrypted, key, initVector) {
51951         // word-level access to the encrypted bytes
51952         var encrypted32 = new Int32Array(encrypted.buffer, encrypted.byteOffset, encrypted.byteLength >> 2);
51953         var decipher = new AES(Array.prototype.slice.call(key)); // byte and word-level access for the decrypted output
51954 
51955         var decrypted = new Uint8Array(encrypted.byteLength);
51956         var decrypted32 = new Int32Array(decrypted.buffer); // temporary variables for working with the IV, encrypted, and
51957         // decrypted data
51958 
51959         var init0 = void 0;
51960         var init1 = void 0;
51961         var init2 = void 0;
51962         var init3 = void 0;
51963         var encrypted0 = void 0;
51964         var encrypted1 = void 0;
51965         var encrypted2 = void 0;
51966         var encrypted3 = void 0; // iteration variable
51967 
51968         var wordIx = void 0; // pull out the words of the IV to ensure we don't modify the
51969         // passed-in reference and easier access
51970 
51971         init0 = initVector[0];
51972         init1 = initVector[1];
51973         init2 = initVector[2];
51974         init3 = initVector[3]; // decrypt four word sequences, applying cipher-block chaining (CBC)
51975         // to each decrypted block
51976 
51977         for (wordIx = 0; wordIx < encrypted32.length; wordIx += 4) {
51978           // convert big-endian (network order) words into little-endian
51979           // (javascript order)
51980           encrypted0 = ntoh(encrypted32[wordIx]);
51981           encrypted1 = ntoh(encrypted32[wordIx + 1]);
51982           encrypted2 = ntoh(encrypted32[wordIx + 2]);
51983           encrypted3 = ntoh(encrypted32[wordIx + 3]); // decrypt the block
51984 
51985           decipher.decrypt(encrypted0, encrypted1, encrypted2, encrypted3, decrypted32, wordIx); // XOR with the IV, and restore network byte-order to obtain the
51986           // plaintext
51987 
51988           decrypted32[wordIx] = ntoh(decrypted32[wordIx] ^ init0);
51989           decrypted32[wordIx + 1] = ntoh(decrypted32[wordIx + 1] ^ init1);
51990           decrypted32[wordIx + 2] = ntoh(decrypted32[wordIx + 2] ^ init2);
51991           decrypted32[wordIx + 3] = ntoh(decrypted32[wordIx + 3] ^ init3); // setup the IV for the next round
51992 
51993           init0 = encrypted0;
51994           init1 = encrypted1;
51995           init2 = encrypted2;
51996           init3 = encrypted3;
51997         }
51998 
51999         return decrypted;
52000       };
52001       /**
52002        * The `Decrypter` class that manages decryption of AES
52003        * data through `AsyncStream` objects and the `decrypt`
52004        * function
52005        *
52006        * @param {Uint8Array} encrypted the encrypted bytes
52007        * @param {Uint32Array} key the bytes of the decryption key
52008        * @param {Uint32Array} initVector the initialization vector (IV) to
52009        * @param {Function} done the function to run when done
52010        * @class Decrypter
52011        */
52012 
52013 
52014       var Decrypter$$1 = function () {
52015         function Decrypter$$1(encrypted, key, initVector, done) {
52016           classCallCheck(this, Decrypter$$1);
52017           var step = Decrypter$$1.STEP;
52018           var encrypted32 = new Int32Array(encrypted.buffer);
52019           var decrypted = new Uint8Array(encrypted.byteLength);
52020           var i = 0;
52021           this.asyncStream_ = new AsyncStream$$1(); // split up the encryption job and do the individual chunks asynchronously
52022 
52023           this.asyncStream_.push(this.decryptChunk_(encrypted32.subarray(i, i + step), key, initVector, decrypted));
52024 
52025           for (i = step; i < encrypted32.length; i += step) {
52026             initVector = new Uint32Array([ntoh(encrypted32[i - 4]), ntoh(encrypted32[i - 3]), ntoh(encrypted32[i - 2]), ntoh(encrypted32[i - 1])]);
52027             this.asyncStream_.push(this.decryptChunk_(encrypted32.subarray(i, i + step), key, initVector, decrypted));
52028           } // invoke the done() callback when everything is finished
52029 
52030 
52031           this.asyncStream_.push(function () {
52032             // remove pkcs#7 padding from the decrypted bytes
52033             done(null, unpad(decrypted));
52034           });
52035         }
52036         /**
52037          * a getter for step the maximum number of bytes to process at one time
52038          *
52039          * @return {Number} the value of step 32000
52040          */
52041 
52042         /**
52043          * @private
52044          */
52045 
52046 
52047         Decrypter$$1.prototype.decryptChunk_ = function decryptChunk_(encrypted, key, initVector, decrypted) {
52048           return function () {
52049             var bytes = decrypt$$1(encrypted, key, initVector);
52050             decrypted.set(bytes, encrypted.byteOffset);
52051           };
52052         };
52053 
52054         createClass(Decrypter$$1, null, [{
52055           key: 'STEP',
52056           get: function get$$1() {
52057             // 4 * 8000;
52058             return 32000;
52059           }
52060         }]);
52061         return Decrypter$$1;
52062       }();
52063       /**
52064        * @file bin-utils.js
52065        */
52066 
52067       /**
52068        * Creates an object for sending to a web worker modifying properties that are TypedArrays
52069        * into a new object with seperated properties for the buffer, byteOffset, and byteLength.
52070        *
52071        * @param {Object} message
52072        *        Object of properties and values to send to the web worker
52073        * @return {Object}
52074        *         Modified message with TypedArray values expanded
52075        * @function createTransferableMessage
52076        */
52077 
52078 
52079       var createTransferableMessage = function createTransferableMessage(message) {
52080         var transferable = {};
52081         Object.keys(message).forEach(function (key) {
52082           var value = message[key];
52083 
52084           if (ArrayBuffer.isView(value)) {
52085             transferable[key] = {
52086               bytes: value.buffer,
52087               byteOffset: value.byteOffset,
52088               byteLength: value.byteLength
52089             };
52090           } else {
52091             transferable[key] = value;
52092           }
52093         });
52094         return transferable;
52095       };
52096       /**
52097        * Our web worker interface so that things can talk to aes-decrypter
52098        * that will be running in a web worker. the scope is passed to this by
52099        * webworkify.
52100        *
52101        * @param {Object} self
52102        *        the scope for the web worker
52103        */
52104 
52105 
52106       var DecrypterWorker = function DecrypterWorker(self) {
52107         self.onmessage = function (event) {
52108           var data = event.data;
52109           var encrypted = new Uint8Array(data.encrypted.bytes, data.encrypted.byteOffset, data.encrypted.byteLength);
52110           var key = new Uint32Array(data.key.bytes, data.key.byteOffset, data.key.byteLength / 4);
52111           var iv = new Uint32Array(data.iv.bytes, data.iv.byteOffset, data.iv.byteLength / 4);
52112           /* eslint-disable no-new, handle-callback-err */
52113 
52114           new Decrypter$$1(encrypted, key, iv, function (err, bytes) {
52115             self.postMessage(createTransferableMessage({
52116               source: data.source,
52117               decrypted: bytes
52118             }), [bytes.buffer]);
52119           });
52120           /* eslint-enable */
52121         };
52122       };
52123 
52124       var decrypterWorker = new DecrypterWorker(self);
52125       return decrypterWorker;
52126     }();
52127   });
52128   /**
52129    * Convert the properties of an HLS track into an audioTrackKind.
52130    *
52131    * @private
52132    */
52133 
52134   var audioTrackKind_ = function audioTrackKind_(properties) {
52135     var kind = properties["default"] ? 'main' : 'alternative';
52136 
52137     if (properties.characteristics && properties.characteristics.indexOf('public.accessibility.describes-video') >= 0) {
52138       kind = 'main-desc';
52139     }
52140 
52141     return kind;
52142   };
52143   /**
52144    * Pause provided segment loader and playlist loader if active
52145    *
52146    * @param {SegmentLoader} segmentLoader
52147    *        SegmentLoader to pause
52148    * @param {Object} mediaType
52149    *        Active media type
52150    * @function stopLoaders
52151    */
52152 
52153 
52154   var stopLoaders = function stopLoaders(segmentLoader, mediaType) {
52155     segmentLoader.abort();
52156     segmentLoader.pause();
52157 
52158     if (mediaType && mediaType.activePlaylistLoader) {
52159       mediaType.activePlaylistLoader.pause();
52160       mediaType.activePlaylistLoader = null;
52161     }
52162   };
52163   /**
52164    * Start loading provided segment loader and playlist loader
52165    *
52166    * @param {PlaylistLoader} playlistLoader
52167    *        PlaylistLoader to start loading
52168    * @param {Object} mediaType
52169    *        Active media type
52170    * @function startLoaders
52171    */
52172 
52173 
52174   var startLoaders = function startLoaders(playlistLoader, mediaType) {
52175     // Segment loader will be started after `loadedmetadata` or `loadedplaylist` from the
52176     // playlist loader
52177     mediaType.activePlaylistLoader = playlistLoader;
52178     playlistLoader.load();
52179   };
52180   /**
52181    * Returns a function to be called when the media group changes. It performs a
52182    * non-destructive (preserve the buffer) resync of the SegmentLoader. This is because a
52183    * change of group is merely a rendition switch of the same content at another encoding,
52184    * rather than a change of content, such as switching audio from English to Spanish.
52185    *
52186    * @param {String} type
52187    *        MediaGroup type
52188    * @param {Object} settings
52189    *        Object containing required information for media groups
52190    * @return {Function}
52191    *         Handler for a non-destructive resync of SegmentLoader when the active media
52192    *         group changes.
52193    * @function onGroupChanged
52194    */
52195 
52196 
52197   var onGroupChanged = function onGroupChanged(type, settings) {
52198     return function () {
52199       var _settings$segmentLoad = settings.segmentLoaders,
52200           segmentLoader = _settings$segmentLoad[type],
52201           mainSegmentLoader = _settings$segmentLoad.main,
52202           mediaType = settings.mediaTypes[type];
52203       var activeTrack = mediaType.activeTrack();
52204       var activeGroup = mediaType.activeGroup(activeTrack);
52205       var previousActiveLoader = mediaType.activePlaylistLoader;
52206       stopLoaders(segmentLoader, mediaType);
52207 
52208       if (!activeGroup) {
52209         // there is no group active
52210         return;
52211       }
52212 
52213       if (!activeGroup.playlistLoader) {
52214         if (previousActiveLoader) {
52215           // The previous group had a playlist loader but the new active group does not
52216           // this means we are switching from demuxed to muxed audio. In this case we want to
52217           // do a destructive reset of the main segment loader and n
vendor: 16,384 bytes, lines 52217-52644
52217ot restart the audio
52218           // loaders.
52219           mainSegmentLoader.resetEverything();
52220         }
52221 
52222         return;
52223       } // Non-destructive resync
52224 
52225 
52226       segmentLoader.resyncLoader();
52227       startLoaders(activeGroup.playlistLoader, mediaType);
52228     };
52229   };
52230   /**
52231    * Returns a function to be called when the media track changes. It performs a
52232    * destructive reset of the SegmentLoader to ensure we start loading as close to
52233    * currentTime as possible.
52234    *
52235    * @param {String} type
52236    *        MediaGroup type
52237    * @param {Object} settings
52238    *        Object containing required information for media groups
52239    * @return {Function}
52240    *         Handler for a destructive reset of SegmentLoader when the active media
52241    *         track changes.
52242    * @function onTrackChanged
52243    */
52244 
52245 
52246   var onTrackChanged = function onTrackChanged(type, settings) {
52247     return function () {
52248       var _settings$segmentLoad2 = settings.segmentLoaders,
52249           segmentLoader = _settings$segmentLoad2[type],
52250           mainSegmentLoader = _settings$segmentLoad2.main,
52251           mediaType = settings.mediaTypes[type];
52252       var activeTrack = mediaType.activeTrack();
52253       var activeGroup = mediaType.activeGroup(activeTrack);
52254       var previousActiveLoader = mediaType.activePlaylistLoader;
52255       stopLoaders(segmentLoader, mediaType);
52256 
52257       if (!activeGroup) {
52258         // there is no group active so we do not want to restart loaders
52259         return;
52260       }
52261 
52262       if (!activeGroup.playlistLoader) {
52263         // when switching from demuxed audio/video to muxed audio/video (noted by no playlist
52264         // loader for the audio group), we want to do a destructive reset of the main segment
52265         // loader and not restart the audio loaders
52266         mainSegmentLoader.resetEverything();
52267         return;
52268       }
52269 
52270       if (previousActiveLoader === activeGroup.playlistLoader) {
52271         // Nothing has actually changed. This can happen because track change events can fire
52272         // multiple times for a "single" change. One for enabling the new active track, and
52273         // one for disabling the track that was active
52274         startLoaders(activeGroup.playlistLoader, mediaType);
52275         return;
52276       }
52277 
52278       if (segmentLoader.track) {
52279         // For WebVTT, set the new text track in the segmentloader
52280         segmentLoader.track(activeTrack);
52281       } // destructive reset
52282 
52283 
52284       segmentLoader.resetEverything();
52285       startLoaders(activeGroup.playlistLoader, mediaType);
52286     };
52287   };
52288 
52289   var onError = {
52290     /**
52291      * Returns a function to be called when a SegmentLoader or PlaylistLoader encounters
52292      * an error.
52293      *
52294      * @param {String} type
52295      *        MediaGroup type
52296      * @param {Object} settings
52297      *        Object containing required information for media groups
52298      * @return {Function}
52299      *         Error handler. Logs warning (or error if the playlist is blacklisted) to
52300      *         console and switches back to default audio track.
52301      * @function onError.AUDIO
52302      */
52303     AUDIO: function AUDIO(type, settings) {
52304       return function () {
52305         var segmentLoader = settings.segmentLoaders[type],
52306             mediaType = settings.mediaTypes[type],
52307             blacklistCurrentPlaylist = settings.blacklistCurrentPlaylist;
52308         stopLoaders(segmentLoader, mediaType); // switch back to default audio track
52309 
52310         var activeTrack = mediaType.activeTrack();
52311         var activeGroup = mediaType.activeGroup();
52312         var id = (activeGroup.filter(function (group) {
52313           return group["default"];
52314         })[0] || activeGroup[0]).id;
52315         var defaultTrack = mediaType.tracks[id];
52316 
52317         if (activeTrack === defaultTrack) {
52318           // Default track encountered an error. All we can do now is blacklist the current
52319           // rendition and hope another will switch audio groups
52320           blacklistCurrentPlaylist({
52321             message: 'Problem encountered loading the default audio track.'
52322           });
52323           return;
52324         }
52325 
52326         videojs$1.log.warn('Problem encountered loading the alternate audio track.' + 'Switching back to default.');
52327 
52328         for (var trackId in mediaType.tracks) {
52329           mediaType.tracks[trackId].enabled = mediaType.tracks[trackId] === defaultTrack;
52330         }
52331 
52332         mediaType.onTrackChanged();
52333       };
52334     },
52335 
52336     /**
52337      * Returns a function to be called when a SegmentLoader or PlaylistLoader encounters
52338      * an error.
52339      *
52340      * @param {String} type
52341      *        MediaGroup type
52342      * @param {Object} settings
52343      *        Object containing required information for media groups
52344      * @return {Function}
52345      *         Error handler. Logs warning to console and disables the active subtitle track
52346      * @function onError.SUBTITLES
52347      */
52348     SUBTITLES: function SUBTITLES(type, settings) {
52349       return function () {
52350         var segmentLoader = settings.segmentLoaders[type],
52351             mediaType = settings.mediaTypes[type];
52352         videojs$1.log.warn('Problem encountered loading the subtitle track.' + 'Disabling subtitle track.');
52353         stopLoaders(segmentLoader, mediaType);
52354         var track = mediaType.activeTrack();
52355 
52356         if (track) {
52357           track.mode = 'disabled';
52358         }
52359 
52360         mediaType.onTrackChanged();
52361       };
52362     }
52363   };
52364   var setupListeners = {
52365     /**
52366      * Setup event listeners for audio playlist loader
52367      *
52368      * @param {String} type
52369      *        MediaGroup type
52370      * @param {PlaylistLoader|null} playlistLoader
52371      *        PlaylistLoader to register listeners on
52372      * @param {Object} settings
52373      *        Object containing required information for media groups
52374      * @function setupListeners.AUDIO
52375      */
52376     AUDIO: function AUDIO(type, playlistLoader, settings) {
52377       if (!playlistLoader) {
52378         // no playlist loader means audio will be muxed with the video
52379         return;
52380       }
52381 
52382       var tech = settings.tech,
52383           requestOptions = settings.requestOptions,
52384           segmentLoader = settings.segmentLoaders[type];
52385       playlistLoader.on('loadedmetadata', function () {
52386         var media = playlistLoader.media();
52387         segmentLoader.playlist(media, requestOptions); // if the video is already playing, or if this isn't a live video and preload
52388         // permits, start downloading segments
52389 
52390         if (!tech.paused() || media.endList && tech.preload() !== 'none') {
52391           segmentLoader.load();
52392         }
52393       });
52394       playlistLoader.on('loadedplaylist', function () {
52395         segmentLoader.playlist(playlistLoader.media(), requestOptions); // If the player isn't paused, ensure that the segment loader is running
52396 
52397         if (!tech.paused()) {
52398           segmentLoader.load();
52399         }
52400       });
52401       playlistLoader.on('error', onError[type](type, settings));
52402     },
52403 
52404     /**
52405      * Setup event listeners for subtitle playlist loader
52406      *
52407      * @param {String} type
52408      *        MediaGroup type
52409      * @param {PlaylistLoader|null} playlistLoader
52410      *        PlaylistLoader to register listeners on
52411      * @param {Object} settings
52412      *        Object containing required information for media groups
52413      * @function setupListeners.SUBTITLES
52414      */
52415     SUBTITLES: function SUBTITLES(type, playlistLoader, settings) {
52416       var tech = settings.tech,
52417           requestOptions = settings.requestOptions,
52418           segmentLoader = settings.segmentLoaders[type],
52419           mediaType = settings.mediaTypes[type];
52420       playlistLoader.on('loadedmetadata', function () {
52421         var media = playlistLoader.media();
52422         segmentLoader.playlist(media, requestOptions);
52423         segmentLoader.track(mediaType.activeTrack()); // if the video is already playing, or if this isn't a live video and preload
52424         // permits, start downloading segments
52425 
52426         if (!tech.paused() || media.endList && tech.preload() !== 'none') {
52427           segmentLoader.load();
52428         }
52429       });
52430       playlistLoader.on('loadedplaylist', function () {
52431         segmentLoader.playlist(playlistLoader.media(), requestOptions); // If the player isn't paused, ensure that the segment loader is running
52432 
52433         if (!tech.paused()) {
52434           segmentLoader.load();
52435         }
52436       });
52437       playlistLoader.on('error', onError[type](type, settings));
52438     }
52439   };
52440   var initialize = {
52441     /**
52442      * Setup PlaylistLoaders and AudioTracks for the audio groups
52443      *
52444      * @param {String} type
52445      *        MediaGroup type
52446      * @param {Object} settings
52447      *        Object containing required information for media groups
52448      * @function initialize.AUDIO
52449      */
52450     'AUDIO': function AUDIO(type, settings) {
52451       var hls = settings.hls,
52452           sourceType = settings.sourceType,
52453           segmentLoader = settings.segmentLoaders[type],
52454           requestOptions = settings.requestOptions,
52455           mediaGroups = settings.master.mediaGroups,
52456           _settings$mediaTypes$ = settings.mediaTypes[type],
52457           groups = _settings$mediaTypes$.groups,
52458           tracks = _settings$mediaTypes$.tracks,
52459           masterPlaylistLoader = settings.masterPlaylistLoader; // force a default if we have none
52460 
52461       if (!mediaGroups[type] || Object.keys(mediaGroups[type]).length === 0) {
52462         mediaGroups[type] = {
52463           main: {
52464             "default": {
52465               "default": true
52466             }
52467           }
52468         };
52469       }
52470 
52471       for (var groupId in mediaGroups[type]) {
52472         if (!groups[groupId]) {
52473           groups[groupId] = [];
52474         } // List of playlists that have an AUDIO attribute value matching the current
52475         // group ID
52476 
52477 
52478         for (var variantLabel in mediaGroups[type][groupId]) {
52479           var properties = mediaGroups[type][groupId][variantLabel];
52480           var playlistLoader = void 0;
52481 
52482           if (properties.resolvedUri) {
52483             playlistLoader = new PlaylistLoader(properties.resolvedUri, hls, requestOptions);
52484           } else if (properties.playlists && sourceType === 'dash') {
52485             playlistLoader = new DashPlaylistLoader(properties.playlists[0], hls, requestOptions, masterPlaylistLoader);
52486           } else {
52487             // no resolvedUri means the audio is muxed with the video when using this
52488             // audio track
52489             playlistLoader = null;
52490           }
52491 
52492           properties = videojs$1.mergeOptions({
52493             id: variantLabel,
52494             playlistLoader: playlistLoader
52495           }, properties);
52496           setupListeners[type](type, properties.playlistLoader, settings);
52497           groups[groupId].push(properties);
52498 
52499           if (typeof tracks[variantLabel] === 'undefined') {
52500             var track = new videojs$1.AudioTrack({
52501               id: variantLabel,
52502               kind: audioTrackKind_(properties),
52503               enabled: false,
52504               language: properties.language,
52505               "default": properties["default"],
52506               label: variantLabel
52507             });
52508             tracks[variantLabel] = track;
52509           }
52510         }
52511       } // setup single error event handler for the segment loader
52512 
52513 
52514       segmentLoader.on('error', onError[type](type, settings));
52515     },
52516 
52517     /**
52518      * Setup PlaylistLoaders and TextTracks for the subtitle groups
52519      *
52520      * @param {String} type
52521      *        MediaGroup type
52522      * @param {Object} settings
52523      *        Object containing required information for media groups
52524      * @function initialize.SUBTITLES
52525      */
52526     'SUBTITLES': function SUBTITLES(type, settings) {
52527       var tech = settings.tech,
52528           hls = settings.hls,
52529           sourceType = settings.sourceType,
52530           segmentLoader = settings.segmentLoaders[type],
52531           requestOptions = settings.requestOptions,
52532           mediaGroups = settings.master.mediaGroups,
52533           _settings$mediaTypes$2 = settings.mediaTypes[type],
52534           groups = _settings$mediaTypes$2.groups,
52535           tracks = _settings$mediaTypes$2.tracks,
52536           masterPlaylistLoader = settings.masterPlaylistLoader;
52537 
52538       for (var groupId in mediaGroups[type]) {
52539         if (!groups[groupId]) {
52540           groups[groupId] = [];
52541         }
52542 
52543         for (var variantLabel in mediaGroups[type][groupId]) {
52544           if (mediaGroups[type][groupId][variantLabel].forced) {
52545             // Subtitle playlists with the forced attribute are not selectable in Safari.
52546             // According to Apple's HLS Authoring Specification:
52547             //   If content has forced subtitles and regular subtitles in a given language,
52548             //   the regular subtitles track in that language MUST contain both the forced
52549             //   subtitles and the regular subtitles for that language.
52550             // Because of this requirement and that Safari does not add forced subtitles,
52551             // forced subtitles are skipped here to maintain consistent experience across
52552             // all platforms
52553             continue;
52554           }
52555 
52556           var properties = mediaGroups[type][groupId][variantLabel];
52557           var playlistLoader = void 0;
52558 
52559           if (sourceType === 'hls') {
52560             playlistLoader = new PlaylistLoader(properties.resolvedUri, hls, requestOptions);
52561           } else if (sourceType === 'dash') {
52562             playlistLoader = new DashPlaylistLoader(properties.playlists[0], hls, requestOptions, masterPlaylistLoader);
52563           }
52564 
52565           properties = videojs$1.mergeOptions({
52566             id: variantLabel,
52567             playlistLoader: playlistLoader
52568           }, properties);
52569           setupListeners[type](type, properties.playlistLoader, settings);
52570           groups[groupId].push(properties);
52571 
52572           if (typeof tracks[variantLabel] === 'undefined') {
52573             var track = tech.addRemoteTextTrack({
52574               id: variantLabel,
52575               kind: 'subtitles',
52576               "default": properties["default"] && properties.autoselect,
52577               language: properties.language,
52578               label: variantLabel
52579             }, false).track;
52580             tracks[variantLabel] = track;
52581           }
52582         }
52583       } // setup single error event handler for the segment loader
52584 
52585 
52586       segmentLoader.on('error', onError[type](type, settings));
52587     },
52588 
52589     /**
52590      * Setup TextTracks for the closed-caption groups
52591      *
52592      * @param {String} type
52593      *        MediaGroup type
52594      * @param {Object} settings
52595      *        Object containing required information for media groups
52596      * @function initialize['CLOSED-CAPTIONS']
52597      */
52598     'CLOSED-CAPTIONS': function CLOSEDCAPTIONS(type, settings) {
52599       var tech = settings.tech,
52600           mediaGroups = settings.master.mediaGroups,
52601           _settings$mediaTypes$3 = settings.mediaTypes[type],
52602           groups = _settings$mediaTypes$3.groups,
52603           tracks = _settings$mediaTypes$3.tracks;
52604 
52605       for (var groupId in mediaGroups[type]) {
52606         if (!groups[groupId]) {
52607           groups[groupId] = [];
52608         }
52609 
52610         for (var variantLabel in mediaGroups[type][groupId]) {
52611           var properties = mediaGroups[type][groupId][variantLabel]; // We only support CEA608 captions for now, so ignore anything that
52612           // doesn't use a CCx INSTREAM-ID
52613 
52614           if (!properties.instreamId.match(/CC\d/)) {
52615             continue;
52616           } // No PlaylistLoader is required for Closed-Captions because the captions are
52617           // embedded within the video stream
52618 
52619 
52620           groups[groupId].push(videojs$1.mergeOptions({
52621             id: variantLabel
52622           }, properties));
52623 
52624           if (typeof tracks[variantLabel] === 'undefined') {
52625             var track = tech.addRemoteTextTrack({
52626               id: properties.instreamId,
52627               kind: 'captions',
52628               "default": properties["default"] && properties.autoselect,
52629               language: properties.language,
52630               label: variantLabel
52631             }, false).track;
52632             tracks[variantLabel] = track;
52633           }
52634         }
52635       }
52636     }
52637   };
52638   /**
52639    * Returns a function used to get the active group of the provided type
52640    *
52641    * @param {String} type
52642    *        MediaGroup type
52643    * @param {Object} settings
52644    *        Object containing requi
vendor: 37,520 bytes, lines 52644-53639
52644red information for media groups
52645    * @return {Function}
52646    *         Function that returns the active media group for the provided type. Takes an
52647    *         optional parameter {TextTrack} track. If no track is provided, a list of all
52648    *         variants in the group, otherwise the variant corresponding to the provided
52649    *         track is returned.
52650    * @function activeGroup
52651    */
52652 
52653   var activeGroup = function activeGroup(type, settings) {
52654     return function (track) {
52655       var masterPlaylistLoader = settings.masterPlaylistLoader,
52656           groups = settings.mediaTypes[type].groups;
52657       var media = masterPlaylistLoader.media();
52658 
52659       if (!media) {
52660         return null;
52661       }
52662 
52663       var variants = null;
52664 
52665       if (media.attributes[type]) {
52666         variants = groups[media.attributes[type]];
52667       }
52668 
52669       variants = variants || groups.main;
52670 
52671       if (typeof track === 'undefined') {
52672         return variants;
52673       }
52674 
52675       if (track === null) {
52676         // An active track was specified so a corresponding group is expected. track === null
52677         // means no track is currently active so there is no corresponding group
52678         return null;
52679       }
52680 
52681       return variants.filter(function (props) {
52682         return props.id === track.id;
52683       })[0] || null;
52684     };
52685   };
52686 
52687   var activeTrack = {
52688     /**
52689      * Returns a function used to get the active track of type provided
52690      *
52691      * @param {String} type
52692      *        MediaGroup type
52693      * @param {Object} settings
52694      *        Object containing required information for media groups
52695      * @return {Function}
52696      *         Function that returns the active media track for the provided type. Returns
52697      *         null if no track is active
52698      * @function activeTrack.AUDIO
52699      */
52700     AUDIO: function AUDIO(type, settings) {
52701       return function () {
52702         var tracks = settings.mediaTypes[type].tracks;
52703 
52704         for (var id in tracks) {
52705           if (tracks[id].enabled) {
52706             return tracks[id];
52707           }
52708         }
52709 
52710         return null;
52711       };
52712     },
52713 
52714     /**
52715      * Returns a function used to get the active track of type provided
52716      *
52717      * @param {String} type
52718      *        MediaGroup type
52719      * @param {Object} settings
52720      *        Object containing required information for media groups
52721      * @return {Function}
52722      *         Function that returns the active media track for the provided type. Returns
52723      *         null if no track is active
52724      * @function activeTrack.SUBTITLES
52725      */
52726     SUBTITLES: function SUBTITLES(type, settings) {
52727       return function () {
52728         var tracks = settings.mediaTypes[type].tracks;
52729 
52730         for (var id in tracks) {
52731           if (tracks[id].mode === 'showing') {
52732             return tracks[id];
52733           }
52734         }
52735 
52736         return null;
52737       };
52738     }
52739   };
52740   /**
52741    * Setup PlaylistLoaders and Tracks for media groups (Audio, Subtitles,
52742    * Closed-Captions) specified in the master manifest.
52743    *
52744    * @param {Object} settings
52745    *        Object containing required information for setting up the media groups
52746    * @param {SegmentLoader} settings.segmentLoaders.AUDIO
52747    *        Audio segment loader
52748    * @param {SegmentLoader} settings.segmentLoaders.SUBTITLES
52749    *        Subtitle segment loader
52750    * @param {SegmentLoader} settings.segmentLoaders.main
52751    *        Main segment loader
52752    * @param {Tech} settings.tech
52753    *        The tech of the player
52754    * @param {Object} settings.requestOptions
52755    *        XHR request options used by the segment loaders
52756    * @param {PlaylistLoader} settings.masterPlaylistLoader
52757    *        PlaylistLoader for the master source
52758    * @param {HlsHandler} settings.hls
52759    *        HLS SourceHandler
52760    * @param {Object} settings.master
52761    *        The parsed master manifest
52762    * @param {Object} settings.mediaTypes
52763    *        Object to store the loaders, tracks, and utility methods for each media type
52764    * @param {Function} settings.blacklistCurrentPlaylist
52765    *        Blacklists the current rendition and forces a rendition switch.
52766    * @function setupMediaGroups
52767    */
52768 
52769   var setupMediaGroups = function setupMediaGroups(settings) {
52770     ['AUDIO', 'SUBTITLES', 'CLOSED-CAPTIONS'].forEach(function (type) {
52771       initialize[type](type, settings);
52772     });
52773     var mediaTypes = settings.mediaTypes,
52774         masterPlaylistLoader = settings.masterPlaylistLoader,
52775         tech = settings.tech,
52776         hls = settings.hls; // setup active group and track getters and change event handlers
52777 
52778     ['AUDIO', 'SUBTITLES'].forEach(function (type) {
52779       mediaTypes[type].activeGroup = activeGroup(type, settings);
52780       mediaTypes[type].activeTrack = activeTrack[type](type, settings);
52781       mediaTypes[type].onGroupChanged = onGroupChanged(type, settings);
52782       mediaTypes[type].onTrackChanged = onTrackChanged(type, settings);
52783     }); // DO NOT enable the default subtitle or caption track.
52784     // DO enable the default audio track
52785 
52786     var audioGroup = mediaTypes.AUDIO.activeGroup();
52787     var groupId = (audioGroup.filter(function (group) {
52788       return group["default"];
52789     })[0] || audioGroup[0]).id;
52790     mediaTypes.AUDIO.tracks[groupId].enabled = true;
52791     mediaTypes.AUDIO.onTrackChanged();
52792     masterPlaylistLoader.on('mediachange', function () {
52793       ['AUDIO', 'SUBTITLES'].forEach(function (type) {
52794         return mediaTypes[type].onGroupChanged();
52795       });
52796     }); // custom audio track change event handler for usage event
52797 
52798     var onAudioTrackChanged = function onAudioTrackChanged() {
52799       mediaTypes.AUDIO.onTrackChanged();
52800       tech.trigger({
52801         type: 'usage',
52802         name: 'hls-audio-change'
52803       });
52804     };
52805 
52806     tech.audioTracks().addEventListener('change', onAudioTrackChanged);
52807     tech.remoteTextTracks().addEventListener('change', mediaTypes.SUBTITLES.onTrackChanged);
52808     hls.on('dispose', function () {
52809       tech.audioTracks().removeEventListener('change', onAudioTrackChanged);
52810       tech.remoteTextTracks().removeEventListener('change', mediaTypes.SUBTITLES.onTrackChanged);
52811     }); // clear existing audio tracks and add the ones we just created
52812 
52813     tech.clearTracks('audio');
52814 
52815     for (var id in mediaTypes.AUDIO.tracks) {
52816       tech.audioTracks().addTrack(mediaTypes.AUDIO.tracks[id]);
52817     }
52818   };
52819   /**
52820    * Creates skeleton object used to store the loaders, tracks, and utility methods for each
52821    * media type
52822    *
52823    * @return {Object}
52824    *         Object to store the loaders, tracks, and utility methods for each media type
52825    * @function createMediaTypes
52826    */
52827 
52828 
52829   var createMediaTypes = function createMediaTypes() {
52830     var mediaTypes = {};
52831     ['AUDIO', 'SUBTITLES', 'CLOSED-CAPTIONS'].forEach(function (type) {
52832       mediaTypes[type] = {
52833         groups: {},
52834         tracks: {},
52835         activePlaylistLoader: null,
52836         activeGroup: noop$1,
52837         activeTrack: noop$1,
52838         onGroupChanged: noop$1,
52839         onTrackChanged: noop$1
52840       };
52841     });
52842     return mediaTypes;
52843   };
52844   /**
52845    * @file master-playlist-controller.js
52846    */
52847 
52848 
52849   var ABORT_EARLY_BLACKLIST_SECONDS = 60 * 2;
52850   var Hls = void 0; // SegmentLoader stats that need to have each loader's
52851   // values summed to calculate the final value
52852 
52853   var loaderStats = ['mediaRequests', 'mediaRequestsAborted', 'mediaRequestsTimedout', 'mediaRequestsErrored', 'mediaTransferDuration', 'mediaBytesTransferred'];
52854 
52855   var sumLoaderStat = function sumLoaderStat(stat) {
52856     return this.audioSegmentLoader_[stat] + this.mainSegmentLoader_[stat];
52857   };
52858   /**
52859    * the master playlist controller controller all interactons
52860    * between playlists and segmentloaders. At this time this mainly
52861    * involves a master playlist and a series of audio playlists
52862    * if they are available
52863    *
52864    * @class MasterPlaylistController
52865    * @extends videojs.EventTarget
52866    */
52867 
52868 
52869   var MasterPlaylistController = function (_videojs$EventTarget) {
52870     inherits$2(MasterPlaylistController, _videojs$EventTarget);
52871 
52872     function MasterPlaylistController(options) {
52873       classCallCheck$1(this, MasterPlaylistController);
52874 
52875       var _this = possibleConstructorReturn$1(this, (MasterPlaylistController.__proto__ || Object.getPrototypeOf(MasterPlaylistController)).call(this));
52876 
52877       var url = options.url,
52878           handleManifestRedirects = options.handleManifestRedirects,
52879           withCredentials = options.withCredentials,
52880           tech = options.tech,
52881           bandwidth = options.bandwidth,
52882           externHls = options.externHls,
52883           useCueTags = options.useCueTags,
52884           blacklistDuration = options.blacklistDuration,
52885           enableLowInitialPlaylist = options.enableLowInitialPlaylist,
52886           sourceType = options.sourceType,
52887           seekTo = options.seekTo,
52888           cacheEncryptionKeys = options.cacheEncryptionKeys;
52889 
52890       if (!url) {
52891         throw new Error('A non-empty playlist URL is required');
52892       }
52893 
52894       Hls = externHls;
52895       _this.withCredentials = withCredentials;
52896       _this.tech_ = tech;
52897       _this.hls_ = tech.hls;
52898       _this.seekTo_ = seekTo;
52899       _this.sourceType_ = sourceType;
52900       _this.useCueTags_ = useCueTags;
52901       _this.blacklistDuration = blacklistDuration;
52902       _this.enableLowInitialPlaylist = enableLowInitialPlaylist;
52903 
52904       if (_this.useCueTags_) {
52905         _this.cueTagsTrack_ = _this.tech_.addTextTrack('metadata', 'ad-cues');
52906         _this.cueTagsTrack_.inBandMetadataTrackDispatchType = '';
52907       }
52908 
52909       _this.requestOptions_ = {
52910         withCredentials: withCredentials,
52911         handleManifestRedirects: handleManifestRedirects,
52912         timeout: null
52913       };
52914       _this.mediaTypes_ = createMediaTypes();
52915       _this.mediaSource = new videojs$1.MediaSource(); // load the media source into the player
52916 
52917       _this.mediaSource.addEventListener('sourceopen', _this.handleSourceOpen_.bind(_this));
52918 
52919       _this.seekable_ = videojs$1.createTimeRanges();
52920 
52921       _this.hasPlayed_ = function () {
52922         return false;
52923       };
52924 
52925       _this.syncController_ = new SyncController(options);
52926       _this.segmentMetadataTrack_ = tech.addRemoteTextTrack({
52927         kind: 'metadata',
52928         label: 'segment-metadata'
52929       }, false).track;
52930       _this.decrypter_ = new Decrypter$1();
52931       _this.inbandTextTracks_ = {};
52932       var segmentLoaderSettings = {
52933         hls: _this.hls_,
52934         mediaSource: _this.mediaSource,
52935         currentTime: _this.tech_.currentTime.bind(_this.tech_),
52936         seekable: function seekable$$1() {
52937           return _this.seekable();
52938         },
52939         seeking: function seeking() {
52940           return _this.tech_.seeking();
52941         },
52942         duration: function duration$$1() {
52943           return _this.mediaSource.duration;
52944         },
52945         hasPlayed: function hasPlayed() {
52946           return _this.hasPlayed_();
52947         },
52948         goalBufferLength: function goalBufferLength() {
52949           return _this.goalBufferLength();
52950         },
52951         bandwidth: bandwidth,
52952         syncController: _this.syncController_,
52953         decrypter: _this.decrypter_,
52954         sourceType: _this.sourceType_,
52955         inbandTextTracks: _this.inbandTextTracks_,
52956         cacheEncryptionKeys: cacheEncryptionKeys
52957       };
52958       _this.masterPlaylistLoader_ = _this.sourceType_ === 'dash' ? new DashPlaylistLoader(url, _this.hls_, _this.requestOptions_) : new PlaylistLoader(url, _this.hls_, _this.requestOptions_);
52959 
52960       _this.setupMasterPlaylistLoaderListeners_(); // setup segment loaders
52961       // combined audio/video or just video when alternate audio track is selected
52962 
52963 
52964       _this.mainSegmentLoader_ = new SegmentLoader(videojs$1.mergeOptions(segmentLoaderSettings, {
52965         segmentMetadataTrack: _this.segmentMetadataTrack_,
52966         loaderType: 'main'
52967       }), options); // alternate audio track
52968 
52969       _this.audioSegmentLoader_ = new SegmentLoader(videojs$1.mergeOptions(segmentLoaderSettings, {
52970         loaderType: 'audio'
52971       }), options);
52972       _this.subtitleSegmentLoader_ = new VTTSegmentLoader(videojs$1.mergeOptions(segmentLoaderSettings, {
52973         loaderType: 'vtt'
52974       }), options);
52975 
52976       _this.setupSegmentLoaderListeners_(); // Create SegmentLoader stat-getters
52977 
52978 
52979       loaderStats.forEach(function (stat) {
52980         _this[stat + '_'] = sumLoaderStat.bind(_this, stat);
52981       });
52982       _this.logger_ = logger('MPC');
52983 
52984       _this.masterPlaylistLoader_.load();
52985 
52986       return _this;
52987     }
52988     /**
52989      * Register event handlers on the master playlist loader. A helper
52990      * function for construction time.
52991      *
52992      * @private
52993      */
52994 
52995 
52996     createClass$1(MasterPlaylistController, [{
52997       key: 'setupMasterPlaylistLoaderListeners_',
52998       value: function setupMasterPlaylistLoaderListeners_() {
52999         var _this2 = this;
53000 
53001         this.masterPlaylistLoader_.on('loadedmetadata', function () {
53002           var media = _this2.masterPlaylistLoader_.media();
53003 
53004           var requestTimeout = media.targetDuration * 1.5 * 1000; // If we don't have any more available playlists, we don't want to
53005           // timeout the request.
53006 
53007           if (isLowestEnabledRendition(_this2.masterPlaylistLoader_.master, _this2.masterPlaylistLoader_.media())) {
53008             _this2.requestOptions_.timeout = 0;
53009           } else {
53010             _this2.requestOptions_.timeout = requestTimeout;
53011           } // if this isn't a live video and preload permits, start
53012           // downloading segments
53013 
53014 
53015           if (media.endList && _this2.tech_.preload() !== 'none') {
53016             _this2.mainSegmentLoader_.playlist(media, _this2.requestOptions_);
53017 
53018             _this2.mainSegmentLoader_.load();
53019           }
53020 
53021           setupMediaGroups({
53022             sourceType: _this2.sourceType_,
53023             segmentLoaders: {
53024               AUDIO: _this2.audioSegmentLoader_,
53025               SUBTITLES: _this2.subtitleSegmentLoader_,
53026               main: _this2.mainSegmentLoader_
53027             },
53028             tech: _this2.tech_,
53029             requestOptions: _this2.requestOptions_,
53030             masterPlaylistLoader: _this2.masterPlaylistLoader_,
53031             hls: _this2.hls_,
53032             master: _this2.master(),
53033             mediaTypes: _this2.mediaTypes_,
53034             blacklistCurrentPlaylist: _this2.blacklistCurrentPlaylist.bind(_this2)
53035           });
53036 
53037           _this2.triggerPresenceUsage_(_this2.master(), media);
53038 
53039           try {
53040             _this2.setupSourceBuffers_();
53041           } catch (e) {
53042             videojs$1.log.warn('Failed to create SourceBuffers', e);
53043             return _this2.mediaSource.endOfStream('decode');
53044           }
53045 
53046           _this2.setupFirstPlay();
53047 
53048           if (!_this2.mediaTypes_.AUDIO.activePlaylistLoader || _this2.mediaTypes_.AUDIO.activePlaylistLoader.media()) {
53049             _this2.trigger('selectedinitialmedia');
53050           } else {
53051             // We must wait for the active audio playlist loader to
53052             // finish setting up before triggering this event so the
53053             // representations API and EME setup is correct
53054             _this2.mediaTypes_.AUDIO.activePlaylistLoader.one('loadedmetadata', function () {
53055               _this2.trigger('selectedinitialmedia');
53056             });
53057           }
53058         });
53059         this.masterPlaylistLoader_.on('loadedplaylist', function () {
53060           var updatedPlaylist = _this2.masterPlaylistLoader_.media();
53061 
53062           if (!updatedPlaylist) {
53063             // blacklist any variants that are not supported by the browser before selecting
53064             // an initial media as the playlist selectors do not consider browser support
53065             _this2.excludeUnsupportedVariants_();
53066 
53067             var selectedMedia = void 0;
53068 
53069             if (_this2.enableLowInitialPlaylist) {
53070               selectedMedia = _this2.selectInitialPlaylist();
53071             }
53072 
53073             if (!selectedMedia) {
53074               selectedMedia = _this2.selectPlaylist();
53075             }
53076 
53077             _this2.initialMedia_ = selectedMedia;
53078 
53079             _this2.masterPlaylistLoader_.media(_this2.initialMedia_);
53080 
53081             return;
53082           }
53083 
53084           if (_this2.useCueTags_) {
53085             _this2.updateAdCues_(updatedPlaylist);
53086           } // TODO: Create a new event on the PlaylistLoader that signals
53087           // that the segments have changed in some way and use that to
53088           // update the SegmentLoader instead of doing it twice here and
53089           // on `mediachange`
53090 
53091 
53092           _this2.mainSegmentLoader_.playlist(updatedPlaylist, _this2.requestOptions_);
53093 
53094           _this2.updateDuration(); // If the player isn't paused, ensure that the segment loader is running,
53095           // as it is possible that it was temporarily stopped while waiting for
53096           // a playlist (e.g., in case the playlist errored and we re-requested it).
53097 
53098 
53099           if (!_this2.tech_.paused()) {
53100             _this2.mainSegmentLoader_.load();
53101 
53102             if (_this2.audioSegmentLoader_) {
53103               _this2.audioSegmentLoader_.load();
53104             }
53105           }
53106 
53107           if (!updatedPlaylist.endList) {
53108             var addSeekableRange = function addSeekableRange() {
53109               var seekable$$1 = _this2.seekable();
53110 
53111               if (seekable$$1.length !== 0) {
53112                 _this2.mediaSource.addSeekableRange_(seekable$$1.start(0), seekable$$1.end(0));
53113               }
53114             };
53115 
53116             if (_this2.duration() !== Infinity) {
53117               var onDurationchange = function onDurationchange() {
53118                 if (_this2.duration() === Infinity) {
53119                   addSeekableRange();
53120                 } else {
53121                   _this2.tech_.one('durationchange', onDurationchange);
53122                 }
53123               };
53124 
53125               _this2.tech_.one('durationchange', onDurationchange);
53126             } else {
53127               addSeekableRange();
53128             }
53129           }
53130         });
53131         this.masterPlaylistLoader_.on('error', function () {
53132           _this2.blacklistCurrentPlaylist(_this2.masterPlaylistLoader_.error);
53133         });
53134         this.masterPlaylistLoader_.on('mediachanging', function () {
53135           _this2.mainSegmentLoader_.abort();
53136 
53137           _this2.mainSegmentLoader_.pause();
53138         });
53139         this.masterPlaylistLoader_.on('mediachange', function () {
53140           var media = _this2.masterPlaylistLoader_.media();
53141 
53142           var requestTimeout = media.targetDuration * 1.5 * 1000; // If we don't have any more available playlists, we don't want to
53143           // timeout the request.
53144 
53145           if (isLowestEnabledRendition(_this2.masterPlaylistLoader_.master, _this2.masterPlaylistLoader_.media())) {
53146             _this2.requestOptions_.timeout = 0;
53147           } else {
53148             _this2.requestOptions_.timeout = requestTimeout;
53149           } // TODO: Create a new event on the PlaylistLoader that signals
53150           // that the segments have changed in some way and use that to
53151           // update the SegmentLoader instead of doing it twice here and
53152           // on `loadedplaylist`
53153 
53154 
53155           _this2.mainSegmentLoader_.playlist(media, _this2.requestOptions_);
53156 
53157           _this2.mainSegmentLoader_.load();
53158 
53159           _this2.tech_.trigger({
53160             type: 'mediachange',
53161             bubbles: true
53162           });
53163         });
53164         this.masterPlaylistLoader_.on('playlistunchanged', function () {
53165           var updatedPlaylist = _this2.masterPlaylistLoader_.media();
53166 
53167           var playlistOutdated = _this2.stuckAtPlaylistEnd_(updatedPlaylist);
53168 
53169           if (playlistOutdated) {
53170             // Playlist has stopped updating and we're stuck at its end. Try to
53171             // blacklist it and switch to another playlist in the hope that that
53172             // one is updating (and give the player a chance to re-adjust to the
53173             // safe live point).
53174             _this2.blacklistCurrentPlaylist({
53175               message: 'Playlist no longer updating.'
53176             }); // useful for monitoring QoS
53177 
53178 
53179             _this2.tech_.trigger('playliststuck');
53180           }
53181         });
53182         this.masterPlaylistLoader_.on('renditiondisabled', function () {
53183           _this2.tech_.trigger({
53184             type: 'usage',
53185             name: 'hls-rendition-disabled'
53186           });
53187         });
53188         this.masterPlaylistLoader_.on('renditionenabled', function () {
53189           _this2.tech_.trigger({
53190             type: 'usage',
53191             name: 'hls-rendition-enabled'
53192           });
53193         });
53194       }
53195       /**
53196        * A helper function for triggerring presence usage events once per source
53197        *
53198        * @private
53199        */
53200 
53201     }, {
53202       key: 'triggerPresenceUsage_',
53203       value: function triggerPresenceUsage_(master, media) {
53204         var mediaGroups = master.mediaGroups || {};
53205         var defaultDemuxed = true;
53206         var audioGroupKeys = Object.keys(mediaGroups.AUDIO);
53207 
53208         for (var mediaGroup in mediaGroups.AUDIO) {
53209           for (var label in mediaGroups.AUDIO[mediaGroup]) {
53210             var properties = mediaGroups.AUDIO[mediaGroup][label];
53211 
53212             if (!properties.uri) {
53213               defaultDemuxed = false;
53214             }
53215           }
53216         }
53217 
53218         if (defaultDemuxed) {
53219           this.tech_.trigger({
53220             type: 'usage',
53221             name: 'hls-demuxed'
53222           });
53223         }
53224 
53225         if (Object.keys(mediaGroups.SUBTITLES).length) {
53226           this.tech_.trigger({
53227             type: 'usage',
53228             name: 'hls-webvtt'
53229           });
53230         }
53231 
53232         if (Hls.Playlist.isAes(media)) {
53233           this.tech_.trigger({
53234             type: 'usage',
53235             name: 'hls-aes'
53236           });
53237         }
53238 
53239         if (Hls.Playlist.isFmp4(media)) {
53240           this.tech_.trigger({
53241             type: 'usage',
53242             name: 'hls-fmp4'
53243           });
53244         }
53245 
53246         if (audioGroupKeys.length && Object.keys(mediaGroups.AUDIO[audioGroupKeys[0]]).length > 1) {
53247           this.tech_.trigger({
53248             type: 'usage',
53249             name: 'hls-alternate-audio'
53250           });
53251         }
53252 
53253         if (this.useCueTags_) {
53254           this.tech_.trigger({
53255             type: 'usage',
53256             name: 'hls-playlist-cue-tags'
53257           });
53258         }
53259       }
53260       /**
53261        * Register event handlers on the segment loaders. A helper function
53262        * for construction time.
53263        *
53264        * @private
53265        */
53266 
53267     }, {
53268       key: 'setupSegmentLoaderListeners_',
53269       value: function setupSegmentLoaderListeners_() {
53270         var _this3 = this;
53271 
53272         this.mainSegmentLoader_.on('bandwidthupdate', function () {
53273           var nextPlaylist = _this3.selectPlaylist();
53274 
53275           var currentPlaylist = _this3.masterPlaylistLoader_.media();
53276 
53277           var buffered = _this3.tech_.buffered();
53278 
53279           var forwardBuffer = buffered.length ? buffered.end(buffered.length - 1) - _this3.tech_.currentTime() : 0;
53280 
53281           var bufferLowWaterLine = _this3.bufferLowWaterLine(); // If the playlist is live, then we want to not take low water line into account.
53282           // This is because in LIVE, the player plays 3 segments from the end of the
53283           // playlist, and if `BUFFER_LOW_WATER_LINE` is greater than the duration availble
53284           // in those segments, a viewer will never experience a rendition upswitch.
53285 
53286 
53287           if (!currentPlaylist.endList || // For the same reason as LIVE, we ignore the low water line when the VOD
53288           // duration is below the max potential low water line
53289           _this3.duration() < Config.MAX_BUFFER_LOW_WATER_LINE || // we want to switch down to lower resolutions quickly to continue playback, but
53290           nextPlaylist.attributes.BANDWIDTH < currentPlaylist.attributes.BANDWIDTH || // ensure we have some buffer before we switch up to prevent us running out of
53291           // buffer while loading a higher rendition.
53292           forwardBuffer >= bufferLowWaterLine) {
53293             _this3.masterPlaylistLoader_.media(nextPlaylist);
53294           }
53295 
53296           _this3.tech_.trigger('bandwidthupdate');
53297         });
53298         this.mainSegmentLoader_.on('progress', function () {
53299           _this3.trigger('progress');
53300         });
53301         this.mainSegmentLoader_.on('error', function () {
53302           _this3.blacklistCurrentPlaylist(_this3.mainSegmentLoader_.error());
53303         });
53304         this.mainSegmentLoader_.on('syncinfoupdate', function () {
53305           _this3.onSyncInfoUpdate_();
53306         });
53307         this.mainSegmentLoader_.on('timestampoffset', function () {
53308           _this3.tech_.trigger({
53309             type: 'usage',
53310             name: 'hls-timestamp-offset'
53311           });
53312         });
53313         this.audioSegmentLoader_.on('syncinfoupdate', function () {
53314           _this3.onSyncInfoUpdate_();
53315         });
53316         this.mainSegmentLoader_.on('ended', function () {
53317           _this3.onEndOfStream();
53318         });
53319         this.mainSegmentLoader_.on('earlyabort', function () {
53320           _this3.blacklistCurrentPlaylist({
53321             message: 'Aborted early because there isn\'t enough bandwidth to complete the ' + 'request without rebuffering.'
53322           }, ABORT_EARLY_BLACKLIST_SECONDS);
53323         });
53324         this.mainSegmentLoader_.on('reseteverything', function () {
53325           // If playing an MTS stream, a videojs.MediaSource is listening for
53326           // hls-reset to reset caption parsing state in the transmuxer
53327           _this3.tech_.trigger('hls-reset');
53328         });
53329         this.mainSegmentLoader_.on('segmenttimemapping', function (event) {
53330           // If playing an MTS stream in html, a videojs.MediaSource is listening for
53331           // hls-segment-time-mapping update its internal mapping of stream to display time
53332           _this3.tech_.trigger({
53333             type: 'hls-segment-time-mapping',
53334             mapping: event.mapping
53335           });
53336         });
53337         this.audioSegmentLoader_.on('ended', function () {
53338           _this3.onEndOfStream();
53339         });
53340       }
53341     }, {
53342       key: 'mediaSecondsLoaded_',
53343       value: function mediaSecondsLoaded_() {
53344         return Math.max(this.audioSegmentLoader_.mediaSecondsLoaded + this.mainSegmentLoader_.mediaSecondsLoaded);
53345       }
53346       /**
53347        * Call load on our SegmentLoaders
53348        */
53349 
53350     }, {
53351       key: 'load',
53352       value: function load() {
53353         this.mainSegmentLoader_.load();
53354 
53355         if (this.mediaTypes_.AUDIO.activePlaylistLoader) {
53356           this.audioSegmentLoader_.load();
53357         }
53358 
53359         if (this.mediaTypes_.SUBTITLES.activePlaylistLoader) {
53360           this.subtitleSegmentLoader_.load();
53361         }
53362       }
53363       /**
53364        * Re-tune playback quality level for the current player
53365        * conditions without performing destructive actions, like
53366        * removing already buffered content
53367        *
53368        * @private
53369        */
53370 
53371     }, {
53372       key: 'smoothQualityChange_',
53373       value: function smoothQualityChange_() {
53374         var media = this.selectPlaylist();
53375 
53376         if (media !== this.masterPlaylistLoader_.media()) {
53377           this.masterPlaylistLoader_.media(media);
53378           this.mainSegmentLoader_.resetLoader(); // don't need to reset audio as it is reset when media changes
53379         }
53380       }
53381       /**
53382        * Re-tune playback quality level for the current player
53383        * conditions. This method will perform destructive actions like removing
53384        * already buffered content in order to readjust the currently active
53385        * playlist quickly. This is good for manual quality changes
53386        *
53387        * @private
53388        */
53389 
53390     }, {
53391       key: 'fastQualityChange_',
53392       value: function fastQualityChange_() {
53393         var _this4 = this;
53394 
53395         var media = this.selectPlaylist();
53396 
53397         if (media === this.masterPlaylistLoader_.media()) {
53398           return;
53399         }
53400 
53401         this.masterPlaylistLoader_.media(media); // Delete all buffered data to allow an immediate quality switch, then seek to give
53402         // the browser a kick to remove any cached frames from the previous rendtion (.04 seconds
53403         // ahead is roughly the minimum that will accomplish this across a variety of content
53404         // in IE and Edge, but seeking in place is sufficient on all other browsers)
53405         // Edge/IE bug: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/14600375/
53406         // Chrome bug: https://bugs.chromium.org/p/chromium/issues/detail?id=651904
53407 
53408         this.mainSegmentLoader_.resetEverything(function () {
53409           // Since this is not a typical seek, we avoid the seekTo method which can cause segments
53410           // from the previously enabled rendition to load before the new playlist has finished loading
53411           if (videojs$1.browser.IE_VERSION || videojs$1.browser.IS_EDGE) {
53412             _this4.tech_.setCurrentTime(_this4.tech_.currentTime() + 0.04);
53413           } else {
53414             _this4.tech_.setCurrentTime(_this4.tech_.currentTime());
53415           }
53416         }); // don't need to reset audio as it is reset when media changes
53417       }
53418       /**
53419        * Begin playback.
53420        */
53421 
53422     }, {
53423       key: 'play',
53424       value: function play() {
53425         if (this.setupFirstPlay()) {
53426           return;
53427         }
53428 
53429         if (this.tech_.ended()) {
53430           this.seekTo_(0);
53431         }
53432 
53433         if (this.hasPlayed_()) {
53434           this.load();
53435         }
53436 
53437         var seekable$$1 = this.tech_.seekable(); // if the viewer has paused and we fell out of the live window,
53438         // seek forward to the live point
53439 
53440         if (this.tech_.duration() === Infinity) {
53441           if (this.tech_.currentTime() < seekable$$1.start(0)) {
53442             return this.seekTo_(seekable$$1.end(seekable$$1.length - 1));
53443           }
53444         }
53445       }
53446       /**
53447        * Seek to the latest media position if this is a live video and the
53448        * player and video are loaded and initialized.
53449        */
53450 
53451     }, {
53452       key: 'setupFirstPlay',
53453       value: function setupFirstPlay() {
53454         var _this5 = this;
53455 
53456         var media = this.masterPlaylistLoader_.media(); // Check that everything is ready to begin buffering for the first call to play
53457         //  If 1) there is no active media
53458         //     2) the player is paused
53459         //     3) the first play has already been setup
53460         // then exit early
53461 
53462         if (!media || this.tech_.paused() || this.hasPlayed_()) {
53463           return false;
53464         } // when the video is a live stream
53465 
53466 
53467         if (!media.endList) {
53468           var seekable$$1 = this.seekable();
53469 
53470           if (!seekable$$1.length) {
53471             // without a seekable range, the player cannot seek to begin buffering at the live
53472             // point
53473             return false;
53474           }
53475 
53476           if (videojs$1.browser.IE_VERSION && this.tech_.readyState() === 0) {
53477             // IE11 throws an InvalidStateError if you try to set currentTime while the
53478             // readyState is 0, so it must be delayed until the tech fires loadedmetadata.
53479             this.tech_.one('loadedmetadata', function () {
53480               _this5.trigger('firstplay');
53481 
53482               _this5.seekTo_(seekable$$1.end(0));
53483 
53484               _this5.hasPlayed_ = function () {
53485                 return true;
53486               };
53487             });
53488             return false;
53489           } // trigger firstplay to inform the source handler to ignore the next seek event
53490 
53491 
53492           this.trigger('firstplay'); // seek to the live point
53493 
53494           this.seekTo_(seekable$$1.end(0));
53495         }
53496 
53497         this.hasPlayed_ = function () {
53498           return true;
53499         }; // we can begin loading now that everything is ready
53500 
53501 
53502         this.load();
53503         return true;
53504       }
53505       /**
53506        * handle the sourceopen event on the MediaSource
53507        *
53508        * @private
53509        */
53510 
53511     }, {
53512       key: 'handleSourceOpen_',
53513       value: function handleSourceOpen_() {
53514         // Only attempt to create the source buffer if none already exist.
53515         // handleSourceOpen is also called when we are "re-opening" a source buffer
53516         // after `endOfStream` has been called (in response to a seek for instance)
53517         try {
53518           this.setupSourceBuffers_();
53519         } catch (e) {
53520           videojs$1.log.warn('Failed to create Source Buffers', e);
53521           return this.mediaSource.endOfStream('decode');
53522         } // if autoplay is enabled, begin playback. This is duplicative of
53523         // code in video.js but is required because play() must be invoked
53524         // *after* the media source has opened.
53525 
53526 
53527         if (this.tech_.autoplay()) {
53528           var playPromise = this.tech_.play(); // Catch/silence error when a pause interrupts a play request
53529           // on browsers which return a promise
53530 
53531           if (typeof playPromise !== 'undefined' && typeof playPromise.then === 'function') {
53532             playPromise.then(null, function (e) {});
53533           }
53534         }
53535 
53536         this.trigger('sourceopen');
53537       }
53538       /**
53539        * Calls endOfStream on the media source when all active stream types have called
53540        * endOfStream
53541        *
53542        * @param {string} streamType
53543        *        Stream type of the segment loader that called endOfStream
53544        * @private
53545        */
53546 
53547     }, {
53548       key: 'onEndOfStream',
53549       value: function onEndOfStream() {
53550         var isEndOfStream = this.mainSegmentLoader_.ended_;
53551 
53552         if (this.mediaTypes_.AUDIO.activePlaylistLoader) {
53553           // if the audio playlist loader exists, then alternate audio is active
53554           if (!this.mainSegmentLoader_.startingMedia_ || this.mainSegmentLoader_.startingMedia_.containsVideo) {
53555             // if we do not know if the main segment loader contains video yet or if we
53556             // definitively know the main segment loader contains video, then we need to wait
53557             // for both main and audio segment loaders to call endOfStream
53558             isEndOfStream = isEndOfStream && this.audioSegmentLoader_.ended_;
53559           } else {
53560             // otherwise just rely on the audio loader
53561             isEndOfStream = this.audioSegmentLoader_.ended_;
53562           }
53563         }
53564 
53565         if (!isEndOfStream) {
53566           return;
53567         }
53568 
53569         this.logger_('calling mediaSource.endOfStream()'); // on chrome calling endOfStream can sometimes cause an exception,
53570         // even when the media source is in a valid state.
53571 
53572         try {
53573           this.mediaSource.endOfStream();
53574         } catch (e) {
53575           videojs$1.log.warn('Failed to call media source endOfStream', e);
53576         }
53577       }
53578       /**
53579        * Check if a playlist has stopped being updated
53580        * @param {Object} playlist the media playlist object
53581        * @return {boolean} whether the playlist has stopped being updated or not
53582        */
53583 
53584     }, {
53585       key: 'stuckAtPlaylistEnd_',
53586       value: function stuckAtPlaylistEnd_(playlist) {
53587         var seekable$$1 = this.seekable();
53588 
53589         if (!seekable$$1.length) {
53590           // playlist doesn't have enough information to determine whether we are stuck
53591           return false;
53592         }
53593 
53594         var expired = this.syncController_.getExpiredTime(playlist, this.mediaSource.duration);
53595 
53596         if (expired === null) {
53597           return false;
53598         } // does not use the safe live end to calculate playlist end, since we
53599         // don't want to say we are stuck while there is still content
53600 
53601 
53602         var absolutePlaylistEnd = Hls.Playlist.playlistEnd(playlist, expired);
53603         var currentTime = this.tech_.currentTime();
53604         var buffered = this.tech_.buffered();
53605 
53606         if (!buffered.length) {
53607           // return true if the playhead reached the absolute end of the playlist
53608           return absolutePlaylistEnd - currentTime <= SAFE_TIME_DELTA;
53609         }
53610 
53611         var bufferedEnd = buffered.end(buffered.length - 1); // return true if there is too little buffer left and buffer has reached absolute
53612         // end of playlist
53613 
53614         return bufferedEnd - currentTime <= SAFE_TIME_DELTA && absolutePlaylistEnd - bufferedEnd <= SAFE_TIME_DELTA;
53615       }
53616       /**
53617        * Blacklists a playlist when an error occurs for a set amount of time
53618        * making it unavailable for selection by the rendition selection algorithm
53619        * and then forces a new playlist (rendition) selection.
53620        *
53621        * @param {Object=} error an optional error that may include the playlist
53622        * to blacklist
53623        * @param {Number=} blacklistDuration an optional number of seconds to blacklist the
53624        * playlist
53625        */
53626 
53627     }, {
53628       key: 'blacklistCurrentPlaylist',
53629       value: function blacklistCurrentPlaylist() {
53630         var error = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
53631         var blacklistDuration = arguments[1];
53632         var currentPlaylist = void 0;
53633         var nextPlaylist = void 0; // If the `error` was generated by the playlist loader, it will contain
53634         // the playlist we were trying to load (but failed) and that should be
53635         // blacklisted instead of the currently selected playlist which is likely
53636         // out-of-date in this scenario
53637 
53638         currentPlaylist = error.playlist || this.masterPlaylistLoader_.media();
53639         blacklistDuration = blacklistDuration || error.blacklistDuration || this.blackl
53639istDuration; // If there is no current playlist, then an error occurred while we were
53640         // trying to load the master OR while we were disposing of the tech
53641 
53642         if (!currentPlaylist) {
53643           this.error = error;
53644 
53645           try {
53646             return this.mediaSource.endOfStream('network');
53647           } catch (e) {
53648             return this.trigger('error');
53649           }
53650         }
53651 
53652         var isFinalRendition = this.masterPlaylistLoader_.master.playlists.filter(isEnabled).length === 1;
53653         var playlists = this.masterPlaylistLoader_.master.playlists;
53654 
53655         if (playlists.length === 1) {
53656           // Never blacklisting this playlist because it's the only playlist
53657           videojs$1.log.warn('Problem encountered with the current ' + 'HLS playlist. Trying again since it is the only playlist.');
53658           this.tech_.trigger('retryplaylist');
53659           return this.masterPlaylistLoader_.load(isFinalRendition);
53660         }
53661 
53662         if (isFinalRendition) {
53663           // Since we're on the final non-blacklisted playlist, and we're about to blacklist
53664           // it, instead of erring the player or retrying this playlist, clear out the current
53665           // blacklist. This allows other playlists to be attempted in case any have been
53666           // fixed.
53667           videojs$1.log.warn('Removing all playlists from the blacklist because the last ' + 'rendition is about to be blacklisted.');
vendor: 11,849 bytes, lines 53668-54010
53668           playlists.forEach(function (playlist) {
53669             if (playlist.excludeUntil !== Infinity) {
53670               delete playlist.excludeUntil;
53671             }
53672           }); // Technically we are retrying a playlist, in that we are simply retrying a previous
53673           // playlist. This is needed for users relying on the retryplaylist event to catch a
53674           // case where the player might be stuck and looping through "dead" playlists.
53675 
53676           this.tech_.trigger('retryplaylist');
53677         } // Blacklist this playlist
53678 
53679 
53680         currentPlaylist.excludeUntil = Date.now() + blacklistDuration * 1000;
53681         this.tech_.trigger('blacklistplaylist');
53682         this.tech_.trigger({
53683           type: 'usage',
53684           name: 'hls-rendition-blacklisted'
53685         }); // Select a new playlist
53686 
53687         nextPlaylist = this.selectPlaylist();
53688         videojs$1.log.warn('Problem encountered with the current HLS playlist.' + (error.message ? ' ' + error.message : '') + ' Switching to another playlist.');
53689         return this.masterPlaylistLoader_.media(nextPlaylist, isFinalRendition);
53690       }
53691       /**
53692        * Pause all segment loaders
53693        */
53694 
53695     }, {
53696       key: 'pauseLoading',
53697       value: function pauseLoading() {
53698         this.mainSegmentLoader_.pause();
53699 
53700         if (this.mediaTypes_.AUDIO.activePlaylistLoader) {
53701           this.audioSegmentLoader_.pause();
53702         }
53703 
53704         if (this.mediaTypes_.SUBTITLES.activePlaylistLoader) {
53705           this.subtitleSegmentLoader_.pause();
53706         }
53707       }
53708       /**
53709        * set the current time on all segment loaders
53710        *
53711        * @param {TimeRange} currentTime the current time to set
53712        * @return {TimeRange} the current time
53713        */
53714 
53715     }, {
53716       key: 'setCurrentTime',
53717       value: function setCurrentTime(currentTime) {
53718         var buffered = findRange(this.tech_.buffered(), currentTime);
53719 
53720         if (!(this.masterPlaylistLoader_ && this.masterPlaylistLoader_.media())) {
53721           // return immediately if the metadata is not ready yet
53722           return 0;
53723         } // it's clearly an edge-case but don't thrown an error if asked to
53724         // seek within an empty playlist
53725 
53726 
53727         if (!this.masterPlaylistLoader_.media().segments) {
53728           return 0;
53729         } // In flash playback, the segment loaders should be reset on every seek, even
53730         // in buffer seeks. If the seek location is already buffered, continue buffering as
53731         // usual
53732         // TODO: redo this comment
53733 
53734 
53735         if (buffered && buffered.length) {
53736           return currentTime;
53737         } // cancel outstanding requests so we begin buffering at the new
53738         // location
53739 
53740 
53741         this.mainSegmentLoader_.resetEverything();
53742         this.mainSegmentLoader_.abort();
53743 
53744         if (this.mediaTypes_.AUDIO.activePlaylistLoader) {
53745           this.audioSegmentLoader_.resetEverything();
53746           this.audioSegmentLoader_.abort();
53747         }
53748 
53749         if (this.mediaTypes_.SUBTITLES.activePlaylistLoader) {
53750           this.subtitleSegmentLoader_.resetEverything();
53751           this.subtitleSegmentLoader_.abort();
53752         } // start segment loader loading in case they are paused
53753 
53754 
53755         this.load();
53756       }
53757       /**
53758        * get the current duration
53759        *
53760        * @return {TimeRange} the duration
53761        */
53762 
53763     }, {
53764       key: 'duration',
53765       value: function duration$$1() {
53766         if (!this.masterPlaylistLoader_) {
53767           return 0;
53768         }
53769 
53770         if (this.mediaSource) {
53771           return this.mediaSource.duration;
53772         }
53773 
53774         return Hls.Playlist.duration(this.masterPlaylistLoader_.media());
53775       }
53776       /**
53777        * check the seekable range
53778        *
53779        * @return {TimeRange} the seekable range
53780        */
53781 
53782     }, {
53783       key: 'seekable',
53784       value: function seekable$$1() {
53785         return this.seekable_;
53786       }
53787     }, {
53788       key: 'onSyncInfoUpdate_',
53789       value: function onSyncInfoUpdate_() {
53790         var mainSeekable = void 0;
53791         var audioSeekable = void 0;
53792 
53793         if (!this.masterPlaylistLoader_) {
53794           return;
53795         }
53796 
53797         var media = this.masterPlaylistLoader_.media();
53798 
53799         if (!media) {
53800           return;
53801         }
53802 
53803         var expired = this.syncController_.getExpiredTime(media, this.mediaSource.duration);
53804 
53805         if (expired === null) {
53806           // not enough information to update seekable
53807           return;
53808         }
53809 
53810         mainSeekable = Hls.Playlist.seekable(media, expired);
53811 
53812         if (mainSeekable.length === 0) {
53813           return;
53814         }
53815 
53816         if (this.mediaTypes_.AUDIO.activePlaylistLoader) {
53817           media = this.mediaTypes_.AUDIO.activePlaylistLoader.media();
53818           expired = this.syncController_.getExpiredTime(media, this.mediaSource.duration);
53819 
53820           if (expired === null) {
53821             return;
53822           }
53823 
53824           audioSeekable = Hls.Playlist.seekable(media, expired);
53825 
53826           if (audioSeekable.length === 0) {
53827             return;
53828           }
53829         }
53830 
53831         var oldEnd = void 0;
53832         var oldStart = void 0;
53833 
53834         if (this.seekable_ && this.seekable_.length) {
53835           oldEnd = this.seekable_.end(0);
53836           oldStart = this.seekable_.start(0);
53837         }
53838 
53839         if (!audioSeekable) {
53840           // seekable has been calculated based on buffering video data so it
53841           // can be returned directly
53842           this.seekable_ = mainSeekable;
53843         } else if (audioSeekable.start(0) > mainSeekable.end(0) || mainSeekable.start(0) > audioSeekable.end(0)) {
53844           // seekables are pretty far off, rely on main
53845           this.seekable_ = mainSeekable;
53846         } else {
53847           this.seekable_ = videojs$1.createTimeRanges([[audioSeekable.start(0) > mainSeekable.start(0) ? audioSeekable.start(0) : mainSeekable.start(0), audioSeekable.end(0) < mainSeekable.end(0) ? audioSeekable.end(0) : mainSeekable.end(0)]]);
53848         } // seekable is the same as last time
53849 
53850 
53851         if (this.seekable_ && this.seekable_.length) {
53852           if (this.seekable_.end(0) === oldEnd && this.seekable_.start(0) === oldStart) {
53853             return;
53854           }
53855         }
53856 
53857         this.logger_('seekable updated [' + printableRange(this.seekable_) + ']');
53858         this.tech_.trigger('seekablechanged');
53859       }
53860       /**
53861        * Update the player duration
53862        */
53863 
53864     }, {
53865       key: 'updateDuration',
53866       value: function updateDuration() {
53867         var _this6 = this;
53868 
53869         var oldDuration = this.mediaSource.duration;
53870         var newDuration = Hls.Playlist.duration(this.masterPlaylistLoader_.media());
53871         var buffered = this.tech_.buffered();
53872 
53873         var setDuration = function setDuration() {
53874           // on firefox setting the duration may sometimes cause an exception
53875           // even if the media source is open and source buffers are not
53876           // updating, something about the media source being in an invalid state.
53877           _this6.logger_('Setting duration from ' + _this6.mediaSource.duration + ' => ' + newDuration);
53878 
53879           try {
53880             _this6.mediaSource.duration = newDuration;
53881           } catch (e) {
53882             videojs$1.log.warn('Failed to set media source duration', e);
53883           }
53884 
53885           _this6.tech_.trigger('durationchange');
53886 
53887           _this6.mediaSource.removeEventListener('sourceopen', setDuration);
53888         };
53889 
53890         if (buffered.length > 0) {
53891           newDuration = Math.max(newDuration, buffered.end(buffered.length - 1));
53892         } // if the duration has changed, invalidate the cached value
53893 
53894 
53895         if (oldDuration !== newDuration) {
53896           // update the duration
53897           if (this.mediaSource.readyState !== 'open') {
53898             this.mediaSource.addEventListener('sourceopen', setDuration);
53899           } else {
53900             setDuration();
53901           }
53902         }
53903       }
53904       /**
53905        * dispose of the MasterPlaylistController and everything
53906        * that it controls
53907        */
53908 
53909     }, {
53910       key: 'dispose',
53911       value: function dispose() {
53912         var _this7 = this;
53913 
53914         this.decrypter_.terminate();
53915         this.masterPlaylistLoader_.dispose();
53916         this.mainSegmentLoader_.dispose();
53917         ['AUDIO', 'SUBTITLES'].forEach(function (type) {
53918           var groups = _this7.mediaTypes_[type].groups;
53919 
53920           for (var id in groups) {
53921             groups[id].forEach(function (group) {
53922               if (group.playlistLoader) {
53923                 group.playlistLoader.dispose();
53924               }
53925             });
53926           }
53927         });
53928         this.audioSegmentLoader_.dispose();
53929         this.subtitleSegmentLoader_.dispose();
53930       }
53931       /**
53932        * return the master playlist object if we have one
53933        *
53934        * @return {Object} the master playlist object that we parsed
53935        */
53936 
53937     }, {
53938       key: 'master',
53939       value: function master() {
53940         return this.masterPlaylistLoader_.master;
53941       }
53942       /**
53943        * return the currently selected playlist
53944        *
53945        * @return {Object} the currently selected playlist object that we parsed
53946        */
53947 
53948     }, {
53949       key: 'media',
53950       value: function media() {
53951         // playlist loader will not return media if it has not been fully loaded
53952         return this.masterPlaylistLoader_.media() || this.initialMedia_;
53953       }
53954       /**
53955        * setup our internal source buffers on our segment Loaders
53956        *
53957        * @private
53958        */
53959 
53960     }, {
53961       key: 'setupSourceBuffers_',
53962       value: function setupSourceBuffers_() {
53963         var media = this.masterPlaylistLoader_.media();
53964         var mimeTypes = void 0; // wait until a media playlist is available and the Media Source is
53965         // attached
53966 
53967         if (!media || this.mediaSource.readyState !== 'open') {
53968           return;
53969         }
53970 
53971         mimeTypes = mimeTypesForPlaylist(this.masterPlaylistLoader_.master, media);
53972 
53973         if (mimeTypes.length < 1) {
53974           this.error = 'No compatible SourceBuffer configuration for the variant stream:' + media.resolvedUri;
53975           return this.mediaSource.endOfStream('decode');
53976         }
53977 
53978         this.configureLoaderMimeTypes_(mimeTypes); // exclude any incompatible variant streams from future playlist
53979         // selection
53980 
53981         this.excludeIncompatibleVariants_(media);
53982       }
53983     }, {
53984       key: 'configureLoaderMimeTypes_',
53985       value: function configureLoaderMimeTypes_(mimeTypes) {
53986         // If the content is demuxed, we can't start appending segments to a source buffer
53987         // until both source buffers are set up, or else the browser may not let us add the
53988         // second source buffer (it will assume we are playing either audio only or video
53989         // only).
53990         var sourceBufferEmitter = // If there is more than one mime type
53991         mimeTypes.length > 1 && // and the first mime type does not have muxed video and audio
53992         mimeTypes[0].indexOf(',') === -1 && // and the two mime types are different (they can be the same in the case of audio
53993         // only with alternate audio)
53994         mimeTypes[0] !== mimeTypes[1] ? // then we want to wait on the second source buffer
53995         new videojs$1.EventTarget() : // otherwise there is no need to wait as the content is either audio only,
53996         // video only, or muxed content.
53997         null;
53998         this.mainSegmentLoader_.mimeType(mimeTypes[0], sourceBufferEmitter);
53999 
54000         if (mimeTypes[1]) {
54001           this.audioSegmentLoader_.mimeType(mimeTypes[1], sourceBufferEmitter);
54002         }
54003       }
54004       /**
54005        * Blacklists playlists with codecs that are unsupported by the browser.
54006        */
54007 
54008     }, {
54009       key: 'excludeUnsupportedVariants_',
54010       value: function excludeUnsupportedVariants_() {
54011         this.master().playlists.forEach(function (variant) {
54012           if (variant.attributes.CODECS && window$1.MediaSource && window$1.MediaSource.isTypeSupported && !window$1.MediaSource.isTypeSupported('video/mp4; codecs="' + mapLegacyAvcCodecs(variant.attributes.CODECS) + '"')) {
54013             variant.excludeUntil = Infinity;
54014           }
54015         });
54016       }
54017       /**
54018        * Blacklist playlists that are known to be codec or
54019        * stream-incompatible with the SourceBuffer configuration. For
54020        * instance, Media Source Extensions would cause the video element to
54021        * stall waiting for video data if you switched from a variant with
54022        * video and audio to an audio-only one.
54023        *
54024        * @param {Object} media a media playlist compatible with the current
54025        * set of SourceBuffers. Variants in the current master playlist that
54026        * do not appear to have compatible codec or stream configurations
54027        * will be excluded from the default playlist selection algorithm
54028        * indefinitely.
54029        * @private
54030        */
54031 
54032     }, {
54033       key: 'excludeIncompatibleVariants_',
54034       value: function excludeIncompatibleVariants_(media) {
54035         var codecCount = 2;
54036         var videoCodec = null;
54037         var codecs = void 0;
54038 
54039         if (media.attributes.CODECS) {
54040           codecs = parseCodecs(media.attributes.CODECS);
54041           videoCodec = codecs.videoCodec;
54042           codecCount = codecs.codecCount;
54043         }
54044 
vendor: 28,187 bytes, lines 54045-54819
54045         this.master().playlists.forEach(function (variant) {
54046           var variantCodecs = {
54047             codecCount: 2,
54048             videoCodec: null
54049           };
54050 
54051           if (variant.attributes.CODECS) {
54052             variantCodecs = parseCodecs(variant.attributes.CODECS);
54053           } // if the streams differ in the presence or absence of audio or
54054           // video, they are incompatible
54055 
54056 
54057           if (variantCodecs.codecCount !== codecCount) {
54058             variant.excludeUntil = Infinity;
54059           } // if h.264 is specified on the current playlist, some flavor of
54060           // it must be specified on all compatible variants
54061 
54062 
54063           if (variantCodecs.videoCodec !== videoCodec) {
54064             variant.excludeUntil = Infinity;
54065           }
54066         });
54067       }
54068     }, {
54069       key: 'updateAdCues_',
54070       value: function updateAdCues_(media) {
54071         var offset = 0;
54072         var seekable$$1 = this.seekable();
54073 
54074         if (seekable$$1.length) {
54075           offset = seekable$$1.start(0);
54076         }
54077 
54078         updateAdCues(media, this.cueTagsTrack_, offset);
54079       }
54080       /**
54081        * Calculates the desired forward buffer length based on current time
54082        *
54083        * @return {Number} Desired forward buffer length in seconds
54084        */
54085 
54086     }, {
54087       key: 'goalBufferLength',
54088       value: function goalBufferLength() {
54089         var currentTime = this.tech_.currentTime();
54090         var initial = Config.GOAL_BUFFER_LENGTH;
54091         var rate = Config.GOAL_BUFFER_LENGTH_RATE;
54092         var max = Math.max(initial, Config.MAX_GOAL_BUFFER_LENGTH);
54093         return Math.min(initial + currentTime * rate, max);
54094       }
54095       /**
54096        * Calculates the desired buffer low water line based on current time
54097        *
54098        * @return {Number} Desired buffer low water line in seconds
54099        */
54100 
54101     }, {
54102       key: 'bufferLowWaterLine',
54103       value: function bufferLowWaterLine() {
54104         var currentTime = this.tech_.currentTime();
54105         var initial = Config.BUFFER_LOW_WATER_LINE;
54106         var rate = Config.BUFFER_LOW_WATER_LINE_RATE;
54107         var max = Math.max(initial, Config.MAX_BUFFER_LOW_WATER_LINE);
54108         return Math.min(initial + currentTime * rate, max);
54109       }
54110     }]);
54111     return MasterPlaylistController;
54112   }(videojs$1.EventTarget);
54113   /**
54114    * Returns a function that acts as the Enable/disable playlist function.
54115    *
54116    * @param {PlaylistLoader} loader - The master playlist loader
54117    * @param {String} playlistUri - uri of the playlist
54118    * @param {Function} changePlaylistFn - A function to be called after a
54119    * playlist's enabled-state has been changed. Will NOT be called if a
54120    * playlist's enabled-state is unchanged
54121    * @param {Boolean=} enable - Value to set the playlist enabled-state to
54122    * or if undefined returns the current enabled-state for the playlist
54123    * @return {Function} Function for setting/getting enabled
54124    */
54125 
54126 
54127   var enableFunction = function enableFunction(loader, playlistUri, changePlaylistFn) {
54128     return function (enable) {
54129       var playlist = loader.master.playlists[playlistUri];
54130       var incompatible = isIncompatible(playlist);
54131       var currentlyEnabled = isEnabled(playlist);
54132 
54133       if (typeof enable === 'undefined') {
54134         return currentlyEnabled;
54135       }
54136 
54137       if (enable) {
54138         delete playlist.disabled;
54139       } else {
54140         playlist.disabled = true;
54141       }
54142 
54143       if (enable !== currentlyEnabled && !incompatible) {
54144         // Ensure the outside world knows about our changes
54145         changePlaylistFn();
54146 
54147         if (enable) {
54148           loader.trigger('renditionenabled');
54149         } else {
54150           loader.trigger('renditiondisabled');
54151         }
54152       }
54153 
54154       return enable;
54155     };
54156   };
54157   /**
54158    * The representation object encapsulates the publicly visible information
54159    * in a media playlist along with a setter/getter-type function (enabled)
54160    * for changing the enabled-state of a particular playlist entry
54161    *
54162    * @class Representation
54163    */
54164 
54165 
54166   var Representation = function Representation(hlsHandler, playlist, id) {
54167     classCallCheck$1(this, Representation);
54168     var mpc = hlsHandler.masterPlaylistController_,
54169         smoothQualityChange = hlsHandler.options_.smoothQualityChange; // Get a reference to a bound version of the quality change function
54170 
54171     var changeType = smoothQualityChange ? 'smooth' : 'fast';
54172     var qualityChangeFunction = mpc[changeType + 'QualityChange_'].bind(mpc); // some playlist attributes are optional
54173 
54174     if (playlist.attributes.RESOLUTION) {
54175       var resolution = playlist.attributes.RESOLUTION;
54176       this.width = resolution.width;
54177       this.height = resolution.height;
54178     }
54179 
54180     this.bandwidth = playlist.attributes.BANDWIDTH; // The id is simply the ordinality of the media playlist
54181     // within the master playlist
54182 
54183     this.id = id; // Partially-apply the enableFunction to create a playlist-
54184     // specific variant
54185 
54186     this.enabled = enableFunction(hlsHandler.playlists, playlist.uri, qualityChangeFunction);
54187   };
54188   /**
54189    * A mixin function that adds the `representations` api to an instance
54190    * of the HlsHandler class
54191    * @param {HlsHandler} hlsHandler - An instance of HlsHandler to add the
54192    * representation API into
54193    */
54194 
54195 
54196   var renditionSelectionMixin = function renditionSelectionMixin(hlsHandler) {
54197     var playlists = hlsHandler.playlists; // Add a single API-specific function to the HlsHandler instance
54198 
54199     hlsHandler.representations = function () {
54200       return playlists.master.playlists.filter(function (media) {
54201         return !isIncompatible(media);
54202       }).map(function (e, i) {
54203         return new Representation(hlsHandler, e, e.uri);
54204       });
54205     };
54206   };
54207   /**
54208    * @file playback-watcher.js
54209    *
54210    * Playback starts, and now my watch begins. It shall not end until my death. I shall
54211    * take no wait, hold no uncleared timeouts, father no bad seeks. I shall wear no crowns
54212    * and win no glory. I shall live and die at my post. I am the corrector of the underflow.
54213    * I am the watcher of gaps. I am the shield that guards the realms of seekable. I pledge
54214    * my life and honor to the Playback Watch, for this Player and all the Players to come.
54215    */
54216   // Set of events that reset the playback-watcher time check logic and clear the timeout
54217 
54218 
54219   var timerCancelEvents = ['seeking', 'seeked', 'pause', 'playing', 'error'];
54220   /**
54221    * @class PlaybackWatcher
54222    */
54223 
54224   var PlaybackWatcher = function () {
54225     /**
54226      * Represents an PlaybackWatcher object.
54227      * @constructor
54228      * @param {object} options an object that includes the tech and settings
54229      */
54230     function PlaybackWatcher(options) {
54231       var _this = this;
54232 
54233       classCallCheck$1(this, PlaybackWatcher);
54234       this.tech_ = options.tech;
54235       this.seekable = options.seekable;
54236       this.seekTo = options.seekTo;
54237       this.allowSeeksWithinUnsafeLiveWindow = options.allowSeeksWithinUnsafeLiveWindow;
54238       this.media = options.media;
54239       this.consecutiveUpdates = 0;
54240       this.lastRecordedTime = null;
54241       this.timer_ = null;
54242       this.checkCurrentTimeTimeout_ = null;
54243       this.logger_ = logger('PlaybackWatcher');
54244       this.logger_('initialize');
54245 
54246       var canPlayHandler = function canPlayHandler() {
54247         return _this.monitorCurrentTime_();
54248       };
54249 
54250       var waitingHandler = function waitingHandler() {
54251         return _this.techWaiting_();
54252       };
54253 
54254       var cancelTimerHandler = function cancelTimerHandler() {
54255         return _this.cancelTimer_();
54256       };
54257 
54258       var fixesBadSeeksHandler = function fixesBadSeeksHandler() {
54259         return _this.fixesBadSeeks_();
54260       };
54261 
54262       this.tech_.on('seekablechanged', fixesBadSeeksHandler);
54263       this.tech_.on('waiting', waitingHandler);
54264       this.tech_.on(timerCancelEvents, cancelTimerHandler);
54265       this.tech_.on('canplay', canPlayHandler); // Define the dispose function to clean up our events
54266 
54267       this.dispose = function () {
54268         _this.logger_('dispose');
54269 
54270         _this.tech_.off('seekablechanged', fixesBadSeeksHandler);
54271 
54272         _this.tech_.off('waiting', waitingHandler);
54273 
54274         _this.tech_.off(timerCancelEvents, cancelTimerHandler);
54275 
54276         _this.tech_.off('canplay', canPlayHandler);
54277 
54278         if (_this.checkCurrentTimeTimeout_) {
54279           window$1.clearTimeout(_this.checkCurrentTimeTimeout_);
54280         }
54281 
54282         _this.cancelTimer_();
54283       };
54284     }
54285     /**
54286      * Periodically check current time to see if playback stopped
54287      *
54288      * @private
54289      */
54290 
54291 
54292     createClass$1(PlaybackWatcher, [{
54293       key: 'monitorCurrentTime_',
54294       value: function monitorCurrentTime_() {
54295         this.checkCurrentTime_();
54296 
54297         if (this.checkCurrentTimeTimeout_) {
54298           window$1.clearTimeout(this.checkCurrentTimeTimeout_);
54299         } // 42 = 24 fps // 250 is what Webkit uses // FF uses 15
54300 
54301 
54302         this.checkCurrentTimeTimeout_ = window$1.setTimeout(this.monitorCurrentTime_.bind(this), 250);
54303       }
54304       /**
54305        * The purpose of this function is to emulate the "waiting" event on
54306        * browsers that do not emit it when they are waiting for more
54307        * data to continue playback
54308        *
54309        * @private
54310        */
54311 
54312     }, {
54313       key: 'checkCurrentTime_',
54314       value: function checkCurrentTime_() {
54315         if (this.tech_.seeking() && this.fixesBadSeeks_()) {
54316           this.consecutiveUpdates = 0;
54317           this.lastRecordedTime = this.tech_.currentTime();
54318           return;
54319         }
54320 
54321         if (this.tech_.paused() || this.tech_.seeking()) {
54322           return;
54323         }
54324 
54325         var currentTime = this.tech_.currentTime();
54326         var buffered = this.tech_.buffered();
54327 
54328         if (this.lastRecordedTime === currentTime && (!buffered.length || currentTime + SAFE_TIME_DELTA >= buffered.end(buffered.length - 1))) {
54329           // If current time is at the end of the final buffered region, then any playback
54330           // stall is most likely caused by buffering in a low bandwidth environment. The tech
54331           // should fire a `waiting` event in this scenario, but due to browser and tech
54332           // inconsistencies. Calling `techWaiting_` here allows us to simulate
54333           // responding to a native `waiting` event when the tech fails to emit one.
54334           return this.techWaiting_();
54335         }
54336 
54337         if (this.consecutiveUpdates >= 5 && currentTime === this.lastRecordedTime) {
54338           this.consecutiveUpdates++;
54339           this.waiting_();
54340         } else if (currentTime === this.lastRecordedTime) {
54341           this.consecutiveUpdates++;
54342         } else {
54343           this.consecutiveUpdates = 0;
54344           this.lastRecordedTime = currentTime;
54345         }
54346       }
54347       /**
54348        * Cancels any pending timers and resets the 'timeupdate' mechanism
54349        * designed to detect that we are stalled
54350        *
54351        * @private
54352        */
54353 
54354     }, {
54355       key: 'cancelTimer_',
54356       value: function cancelTimer_() {
54357         this.consecutiveUpdates = 0;
54358 
54359         if (this.timer_) {
54360           this.logger_('cancelTimer_');
54361           clearTimeout(this.timer_);
54362         }
54363 
54364         this.timer_ = null;
54365       }
54366       /**
54367        * Fixes situations where there's a bad seek
54368        *
54369        * @return {Boolean} whether an action was taken to fix the seek
54370        * @private
54371        */
54372 
54373     }, {
54374       key: 'fixesBadSeeks_',
54375       value: function fixesBadSeeks_() {
54376         var seeking = this.tech_.seeking();
54377 
54378         if (!seeking) {
54379           return false;
54380         }
54381 
54382         var seekable = this.seekable();
54383         var currentTime = this.tech_.currentTime();
54384         var isAfterSeekableRange = this.afterSeekableWindow_(seekable, currentTime, this.media(), this.allowSeeksWithinUnsafeLiveWindow);
54385         var seekTo = void 0;
54386 
54387         if (isAfterSeekableRange) {
54388           var seekableEnd = seekable.end(seekable.length - 1); // sync to live point (if VOD, our seekable was updated and we're simply adjusting)
54389 
54390           seekTo = seekableEnd;
54391         }
54392 
54393         if (this.beforeSeekableWindow_(seekable, currentTime)) {
54394           var seekableStart = seekable.start(0); // sync to the beginning of the live window
54395           // provide a buffer of .1 seconds to handle rounding/imprecise numbers
54396 
54397           seekTo = seekableStart + SAFE_TIME_DELTA;
54398         }
54399 
54400         if (typeof seekTo !== 'undefined') {
54401           this.logger_('Trying to seek outside of seekable at time ' + currentTime + ' with ' + ('seekable range ' + printableRange(seekable) + '. Seeking to ') + (seekTo + '.'));
54402           this.seekTo(seekTo);
54403           return true;
54404         }
54405 
54406         return false;
54407       }
54408       /**
54409        * Handler for situations when we determine the player is waiting.
54410        *
54411        * @private
54412        */
54413 
54414     }, {
54415       key: 'waiting_',
54416       value: function waiting_() {
54417         if (this.techWaiting_()) {
54418           return;
54419         } // All tech waiting checks failed. Use last resort correction
54420 
54421 
54422         var currentTime = this.tech_.currentTime();
54423         var buffered = this.tech_.buffered();
54424         var currentRange = findRange(buffered, currentTime); // Sometimes the player can stall for unknown reasons within a contiguous buffered
54425         // region with no indication that anything is amiss (seen in Firefox). Seeking to
54426         // currentTime is usually enough to kickstart the player. This checks that the player
54427         // is currently within a buffered region before attempting a corrective seek.
54428         // Chrome does not appear to continue `timeupdate` events after a `waiting` event
54429         // until there is ~ 3 seconds of forward buffer available. PlaybackWatcher should also
54430         // make sure there is ~3 seconds of forward buffer before taking any corrective action
54431         // to avoid triggering an `unknownwaiting` event when the network is slow.
54432 
54433         if (currentRange.length && currentTime + 3 <= currentRange.end(0)) {
54434           this.cancelTimer_();
54435           this.seekTo(currentTime);
54436           this.logger_('Stopped at ' + currentTime + ' while inside a buffered region ' + ('[' + currentRange.start(0) + ' -> ' + currentRange.end(0) + ']. Attempting to resume ') + 'playback by seeking to the current time.'); // unknown waiting corrections may be useful for monitoring QoS
54437 
54438           this.tech_.trigger({
54439             type: 'usage',
54440             name: 'hls-unknown-waiting'
54441           });
54442           return;
54443         }
54444       }
54445       /**
54446        * Handler for situations when the tech fires a `waiting` event
54447        *
54448        * @return {Boolean}
54449        *         True if an action (or none) was needed to correct the waiting. False if no
54450        *         checks passed
54451        * @private
54452        */
54453 
54454     }, {
54455       key: 'techWaiting_',
54456       value: function techWaiting_() {
54457         var seekable = this.seekable();
54458         var currentTime = this.tech_.currentTime();
54459 
54460         if (this.tech_.seeking() && this.fixesBadSeeks_()) {
54461           // Tech is seeking or bad seek fixed, no action needed
54462           return true;
54463         }
54464 
54465         if (this.tech_.seeking() || this.timer_ !== null) {
54466           // Tech is seeking or already waiting on another action, no action needed
54467           return true;
54468         }
54469 
54470         if (this.beforeSeekableWindow_(seekable, currentTime)) {
54471           var livePoint = seekable.end(seekable.length - 1);
54472           this.logger_('Fell out of live window at time ' + currentTime + '. Seeking to ' + ('live point (seekable end) ' + livePoint));
54473           this.cancelTimer_();
54474           this.seekTo(livePoint); // live window resyncs may be useful for monitoring QoS
54475 
54476           this.tech_.trigger({
54477             type: 'usage',
54478             name: 'hls-live-resync'
54479           });
54480           return true;
54481         }
54482 
54483         var buffered = this.tech_.buffered();
54484         var nextRange = findNextRange(buffered, currentTime);
54485 
54486         if (this.videoUnderflow_(nextRange, buffered, currentTime)) {
54487           // Even though the video underflowed and was stuck in a gap, the audio overplayed
54488           // the gap, leading currentTime into a buffered range. Seeking to currentTime
54489           // allows the video to catch up to the audio position without losing any audio
54490           // (only suffering ~3 seconds of frozen video and a pause in audio playback).
54491           this.cancelTimer_();
54492           this.seekTo(currentTime); // video underflow may be useful for monitoring QoS
54493 
54494           this.tech_.trigger({
54495             type: 'usage',
54496             name: 'hls-video-underflow'
54497           });
54498           return true;
54499         } // check for gap
54500 
54501 
54502         if (nextRange.length > 0) {
54503           var difference = nextRange.start(0) - currentTime;
54504           this.logger_('Stopped at ' + currentTime + ', setting timer for ' + difference + ', seeking ' + ('to ' + nextRange.start(0)));
54505           this.timer_ = setTimeout(this.skipTheGap_.bind(this), difference * 1000, currentTime);
54506           return true;
54507         } // All checks failed. Returning false to indicate failure to correct waiting
54508 
54509 
54510         return false;
54511       }
54512     }, {
54513       key: 'afterSeekableWindow_',
54514       value: function afterSeekableWindow_(seekable, currentTime, playlist) {
54515         var allowSeeksWithinUnsafeLiveWindow = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
54516 
54517         if (!seekable.length) {
54518           // we can't make a solid case if there's no seekable, default to false
54519           return false;
54520         }
54521 
54522         var allowedEnd = seekable.end(seekable.length - 1) + SAFE_TIME_DELTA;
54523         var isLive = !playlist.endList;
54524 
54525         if (isLive && allowSeeksWithinUnsafeLiveWindow) {
54526           allowedEnd = seekable.end(seekable.length - 1) + playlist.targetDuration * 3;
54527         }
54528 
54529         if (currentTime > allowedEnd) {
54530           return true;
54531         }
54532 
54533         return false;
54534       }
54535     }, {
54536       key: 'beforeSeekableWindow_',
54537       value: function beforeSeekableWindow_(seekable, currentTime) {
54538         if (seekable.length && // can't fall before 0 and 0 seekable start identifies VOD stream
54539         seekable.start(0) > 0 && currentTime < seekable.start(0) - SAFE_TIME_DELTA) {
54540           return true;
54541         }
54542 
54543         return false;
54544       }
54545     }, {
54546       key: 'videoUnderflow_',
54547       value: function videoUnderflow_(nextRange, buffered, currentTime) {
54548         if (nextRange.length === 0) {
54549           // Even if there is no available next range, there is still a possibility we are
54550           // stuck in a gap due to video underflow.
54551           var gap = this.gapFromVideoUnderflow_(buffered, currentTime);
54552 
54553           if (gap) {
54554             this.logger_('Encountered a gap in video from ' + gap.start + ' to ' + gap.end + '. ' + ('Seeking to current time ' + currentTime));
54555             return true;
54556           }
54557         }
54558 
54559         return false;
54560       }
54561       /**
54562        * Timer callback. If playback still has not proceeded, then we seek
54563        * to the start of the next buffered region.
54564        *
54565        * @private
54566        */
54567 
54568     }, {
54569       key: 'skipTheGap_',
54570       value: function skipTheGap_(scheduledCurrentTime) {
54571         var buffered = this.tech_.buffered();
54572         var currentTime = this.tech_.currentTime();
54573         var nextRange = findNextRange(buffered, currentTime);
54574         this.cancelTimer_();
54575 
54576         if (nextRange.length === 0 || currentTime !== scheduledCurrentTime) {
54577           return;
54578         }
54579 
54580         this.logger_('skipTheGap_:', 'currentTime:', currentTime, 'scheduled currentTime:', scheduledCurrentTime, 'nextRange start:', nextRange.start(0)); // only seek if we still have not played
54581 
54582         this.seekTo(nextRange.start(0) + TIME_FUDGE_FACTOR);
54583         this.tech_.trigger({
54584           type: 'usage',
54585           name: 'hls-gap-skip'
54586         });
54587       }
54588     }, {
54589       key: 'gapFromVideoUnderflow_',
54590       value: function gapFromVideoUnderflow_(buffered, currentTime) {
54591         // At least in Chrome, if there is a gap in the video buffer, the audio will continue
54592         // playing for ~3 seconds after the video gap starts. This is done to account for
54593         // video buffer underflow/underrun (note that this is not done when there is audio
54594         // buffer underflow/underrun -- in that case the video will stop as soon as it
54595         // encounters the gap, as audio stalls are more noticeable/jarring to a user than
54596         // video stalls). The player's time will reflect the playthrough of audio, so the
54597         // time will appear as if we are in a buffered region, even if we are stuck in a
54598         // "gap."
54599         //
54600         // Example:
54601         // video buffer:   0 => 10.1, 10.2 => 20
54602         // audio buffer:   0 => 20
54603         // overall buffer: 0 => 10.1, 10.2 => 20
54604         // current time: 13
54605         //
54606         // Chrome's video froze at 10 seconds, where the video buffer encountered the gap,
54607         // however, the audio continued playing until it reached ~3 seconds past the gap
54608         // (13 seconds), at which point it stops as well. Since current time is past the
54609         // gap, findNextRange will return no ranges.
54610         //
54611         // To check for this issue, we see if there is a gap that starts somewhere within
54612         // a 3 second range (3 seconds +/- 1 second) back from our current time.
54613         var gaps = findGaps(buffered);
54614 
54615         for (var i = 0; i < gaps.length; i++) {
54616           var start = gaps.start(i);
54617           var end = gaps.end(i); // gap is starts no more than 4 seconds back
54618 
54619           if (currentTime - start < 4 && currentTime - start > 2) {
54620             return {
54621               start: start,
54622               end: end
54623             };
54624           }
54625         }
54626 
54627         return null;
54628       }
54629     }]);
54630     return PlaybackWatcher;
54631   }();
54632 
54633   var defaultOptions = {
54634     errorInterval: 30,
54635     getSource: function getSource(next) {
54636       var tech = this.tech({
54637         IWillNotUseThisInPlugins: true
54638       });
54639       var sourceObj = tech.currentSource_;
54640       return next(sourceObj);
54641     }
54642   };
54643   /**
54644    * Main entry point for the plugin
54645    *
54646    * @param {Player} player a reference to a videojs Player instance
54647    * @param {Object} [options] an object with plugin options
54648    * @private
54649    */
54650 
54651   var initPlugin = function initPlugin(player, options) {
54652     var lastCalled = 0;
54653     var seekTo = 0;
54654     var localOptions = videojs$1.mergeOptions(defaultOptions, options);
54655     player.ready(function () {
54656       player.trigger({
54657         type: 'usage',
54658         name: 'hls-error-reload-initialized'
54659       });
54660     });
54661     /**
54662      * Player modifications to perform that must wait until `loadedmetadata`
54663      * has been triggered
54664      *
54665      * @private
54666      */
54667 
54668     var loadedMetadataHandler = function loadedMetadataHandler() {
54669       if (seekTo) {
54670         player.currentTime(seekTo);
54671       }
54672     };
54673     /**
54674      * Set the source on the player element, play, and seek if necessary
54675      *
54676      * @param {Object} sourceObj An object specifying the source url and mime-type to play
54677      * @private
54678      */
54679 
54680 
54681     var setSource = function setSource(sourceObj) {
54682       if (sourceObj === null || sourceObj === undefined) {
54683         return;
54684       }
54685 
54686       seekTo = player.duration() !== Infinity && player.currentTime() || 0;
54687       player.one('loadedmetadata', loadedMetadataHandler);
54688       player.src(sourceObj);
54689       player.trigger({
54690         type: 'usage',
54691         name: 'hls-error-reload'
54692       });
54693       player.play();
54694     };
54695     /**
54696      * Attempt to get a source from either the built-in getSource function
54697      * or a custom function provided via the options
54698      *
54699      * @private
54700      */
54701 
54702 
54703     var errorHandler = function errorHandler() {
54704       // Do not attempt to reload the source if a source-reload occurred before
54705       // 'errorInterval' time has elapsed since the last source-reload
54706       if (Date.now() - lastCalled < localOptions.errorInterval * 1000) {
54707         player.trigger({
54708           type: 'usage',
54709           name: 'hls-error-reload-canceled'
54710         });
54711         return;
54712       }
54713 
54714       if (!localOptions.getSource || typeof localOptions.getSource !== 'function') {
54715         videojs$1.log.error('ERROR: reloadSourceOnError - The option getSource must be a function!');
54716         return;
54717       }
54718 
54719       lastCalled = Date.now();
54720       return localOptions.getSource.call(player, setSource);
54721     };
54722     /**
54723      * Unbind any event handlers that were bound by the plugin
54724      *
54725      * @private
54726      */
54727 
54728 
54729     var cleanupEvents = function cleanupEvents() {
54730       player.off('loadedmetadata', loadedMetadataHandler);
54731       player.off('error', errorHandler);
54732       player.off('dispose', cleanupEvents);
54733     };
54734     /**
54735      * Cleanup before re-initializing the plugin
54736      *
54737      * @param {Object} [newOptions] an object with plugin options
54738      * @private
54739      */
54740 
54741 
54742     var reinitPlugin = function reinitPlugin(newOptions) {
54743       cleanupEvents();
54744       initPlugin(player, newOptions);
54745     };
54746 
54747     player.on('error', errorHandler);
54748     player.on('dispose', cleanupEvents); // Overwrite the plugin function so that we can correctly cleanup before
54749     // initializing the plugin
54750 
54751     player.reloadSourceOnError = reinitPlugin;
54752   };
54753   /**
54754    * Reload the source when an error is detected as long as there
54755    * wasn't an error previously within the last 30 seconds
54756    *
54757    * @param {Object} [options] an object with plugin options
54758    */
54759 
54760 
54761   var reloadSourceOnError = function reloadSourceOnError(options) {
54762     initPlugin(this, options);
54763   };
54764 
54765   var version$1 = "1.11.2"; // since VHS handles HLS and DASH (and in the future, more types), use * to capture all
54766 
54767   videojs$1.use('*', function (player) {
54768     return {
54769       setSource: function setSource(srcObj, next) {
54770         // pass null as the first argument to indicate that the source is not rejected
54771         next(null, srcObj);
54772       },
54773       // VHS needs to know when seeks happen. For external seeks (generated at the player
54774       // level), this middleware will capture the action. For internal seeks (generated at
54775       // the tech level), we use a wrapped function so that we can handle it on our own
54776       // (specified elsewhere).
54777       setCurrentTime: function setCurrentTime(time) {
54778         if (player.vhs && player.currentSource().src === player.vhs.source_.src) {
54779           player.vhs.setCurrentTime(time);
54780         }
54781 
54782         return time;
54783       },
54784       // Sync VHS after play requests.
54785       // This specifically handles replay where the order of actions is
54786       // play, video element will seek to 0 (skipping the setCurrentTime middleware)
54787       // then triggers a play event.
54788       play: function play() {
54789         if (player.vhs && player.currentSource().src === player.vhs.source_.src) {
54790           player.vhs.setCurrentTime(player.tech_.currentTime());
54791         }
54792       }
54793     };
54794   });
54795   /**
54796    * @file videojs-http-streaming.js
54797    *
54798    * The main file for the HLS project.
54799    * License: https://github.com/videojs/videojs-http-streaming/blob/master/LICENSE
54800    */
54801 
54802   var Hls$1 = {
54803     PlaylistLoader: PlaylistLoader,
54804     Playlist: Playlist,
54805     Decrypter: Decrypter,
54806     AsyncStream: AsyncStream,
54807     decrypt: decrypt,
54808     utils: utils$1,
54809     STANDARD_PLAYLIST_SELECTOR: lastBandwidthSelector,
54810     INITIAL_PLAYLIST_SELECTOR: lowestBitrateCompatibleVariantSelector,
54811     comparePlaylistBandwidth: comparePlaylistBandwidth,
54812     comparePlaylistResolution: comparePlaylistResolution,
54813     xhr: xhrFactory()
54814   }; // Define getter/setters for config properites
54815 
54816   ['GOAL_BUFFER_LENGTH', 'MAX_GOAL_BUFFER_LENGTH', 'GOAL_BUFFER_LENGTH_RATE', 'BUFFER_LOW_WATER_LINE', 'MAX_BUFFER_LOW_WATER_LINE', 'BUFFER_LOW_WATER_LINE_RATE', 'BANDWIDTH_VARIANCE'].forEach(function (prop) {
54817     Object.defineProperty(Hls$1, prop, {
54818       get: function get$$1() {
54819         videojs$1.log.warn('using Hls.' + prop + ' is UNSAFE be sure you know 
54819what you are doing');
54820         return Config[prop];
54821       },
54822       set: function set$$1(value) {
54823         videojs$1.log.warn('using Hls.' + prop + ' is UNSAFE be sure you know what you are doing');
54824 
54825         if (typeof value !== 'number' || value < 0) {
54826           videojs$1.log.warn('value of Hls.' + prop + ' must be greater than or equal to 0');
54827           return;
54828         }
54829 
54830         Config[prop] = value;
54831       }
54832     });
54833   });
54834   var LOCAL_STORAGE_KEY$1 = 'videojs-vhs';
54835 
54836   var simpleTypeFromSourceType = function simpleTypeFromSourceType(type) {
54837     var mpegurlRE = /^(audio|video|application)\/(x-|vnd\.apple\.)?mpegurl/i;
54838 
54839     if (mpegurlRE.test(type)) {
54840       return 'hls';
54841     }
54842 
54843     var dashRE = /^application\/dash\+xml/i;
54844 
54845     if (dashRE.test(type)) {
54846       return 'dash';
54847     }
54848 
54849     return null;
54850   };
54851   /**
54852    * Updates the selectedIndex of the QualityLevelList when a mediachange happens in hls.
54853    *
54854    * @param {QualityLevelList} qualityLevels The QualityLevelList to update.
54855    * @param {PlaylistLoader} playlistLoader PlaylistLoader containing the new media info.
54856    * @function handleHlsMediaChange
54857    */
54858 
54859 
54860   var handleHlsMediaChange = function handleHlsMediaChange(qualityLevels, playlistLoader) {
54861     var newPlaylist = playlistLoader.media();
54862     var selectedIndex = -1;
54863 
54864     for (var i = 0; i < qualityLevels.length; i++) {
54865       if (qualityLevels[i].id === newPlaylist.uri) {
54866         selectedIndex = i;
54867         break;
54868       }
54869     }
54870 
54871     qualityLevels.selectedIndex_ = selectedIndex;
54872     qualityLevels.trigger({
54873       selectedIndex: selectedIndex,
54874       type: 'change'
54875     });
54876   };
54877   /**
54878    * Adds quality levels to list once playlist metadata is available
54879    *
54880    * @param {QualityLevelList} qualityLevels The QualityLevelList to attach events to.
54881    * @param {Object} hls Hls object to listen to for media events.
54882    * @function handleHlsLoadedMetadata
54883    */
54884 
54885 
54886   var handleHlsLoadedMetadata = function handleHlsLoadedMetadata(qualityLevels, hls) {
54887     hls.representations().forEach(function (rep) {
54888       qualityLevels.addQualityLevel(rep);
54889     });
54890     handleHlsMediaChange(qualityLevels, hls.playlists);
54891   }; // HLS is a source handler, not a tech. Make sure attempts to use it
54892   // as one do not cause exceptions.
54893 
54894 
54895   Hls$1.canPlaySource = function () {
54896     return videojs$1.log.warn('HLS is no longer a tech. Please remove it from ' + 'your player\'s techOrder.');
54897   };
54898 
54899   var emeKeySystems = function emeKeySystems(keySystemOptions, mainSegmentLoader, audioSegmentLoader) {
54900     if (!keySystemOptions) {
54901       return keySystemOptions;
54902     }
54903 
54904     var videoMimeType = void 0;
54905     var audioMimeType = void 0; // if there is a mimeType associated with the audioSegmentLoader, then the audio
54906     // and video mimeType and codec strings are already in the format we need to
54907     // pass with the other key systems
54908 
54909     if (audioSegmentLoader.mimeType_) {
54910       videoMimeType = mainSegmentLoader.mimeType_;
54911       audioMimeType = audioSegmentLoader.mimeType_; // if there is no audioSegmentLoader mimeType, then we have to create the
54912       // the audio and video mimeType/codec strings from information extrapolated
54913       // from the mainSegmentLoader mimeType (ex. 'video/mp4; codecs="mp4, avc1"' -->
54914       // 'video/mp4; codecs="avc1"' and 'audio/mp4; codecs="mp4"')
54915     } else {
54916       var parsedMimeType = parseContentType(mainSegmentLoader.mimeType_);
54917       var codecs = parsedMimeType.parameters.codecs.split(',');
54918       var audioCodec = void 0;
54919       var videoCodec = void 0;
54920       codecs.forEach(function (codec) {
54921         codec = codec.trim();
54922 
54923         if (isAudioCodec(codec)) {
54924           audioCodec = codec;
54925         } else if (isVideoCodec(codec)) {
54926           videoCodec = codec;
54927         }
54928       });
54929       videoMimeType = parsedMimeType.type + '; codecs="' + videoCodec + '"';
54930       audioMimeType = parsedMimeType.type.replace('video', 'audio') + '; codecs="' + audioCodec + '"';
54931     } // upsert the content types based on the selected playlist
54932 
54933 
54934     var keySystemContentTypes = {};
54935     var videoPlaylist = mainSegmentLoader.playlist_;
54936 
54937     for (var keySystem in keySystemOptions) {
54938       keySystemContentTypes[keySystem] = {
54939         audioContentType: audioMimeType,
54940         videoContentType: videoMimeType
54941       };
54942 
54943       if (videoPlaylist.contentProtection && videoPlaylist.contentProtection[keySystem] && videoPlaylist.contentProtection[keySystem].pssh) {
54944         keySystemContentTypes[keySystem].pssh = videoPlaylist.contentProtection[keySystem].pssh;
54945       } // videojs-contrib-eme accepts the option of specifying: 'com.some.cdm': 'url'
54946       // so we need to prevent overwriting the URL entirely
54947 
54948 
54949       if (typeof keySystemOptions[keySystem] === 'string') {
54950         keySystemContentTypes[keySystem].url = keySystemOptions[keySystem];
54951       }
54952     }
54953 
54954     return videojs$1.mergeOptions(keySystemOptions, keySystemContentTypes);
54955   };
54956 
54957   var setupEmeOptions = function setupEmeOptions(hlsHandler) {
54958     var mainSegmentLoader = hlsHandler.masterPlaylistController_.mainSegmentLoader_;
54959     var audioSegmentLoader = hlsHandler.masterPlaylistController_.audioSegmentLoader_;
54960     var player = videojs$1.players[hlsHandler.tech_.options_.playerId];
54961 
54962     if (player.eme) {
54963       var sourceOptions = emeKeySystems(hlsHandler.source_.keySystems, mainSegmentLoader, audioSegmentLoader);
54964 
54965       if (sourceOptions) {
54966         player.currentSource().keySystems = sourceOptions; // Works around https://bugs.chromium.org/p/chromium/issues/detail?id=895449
54967         // in non-IE11 browsers. In IE11 this is too early to initialize media keys
54968 
54969         if (!(videojs$1.browser.IE_VERSION === 11) && player.eme.initializeMediaKeys) {
54970           player.eme.initializeMediaKeys();
54971         }
54972       }
54973     }
54974   };
54975 
54976   var getVhsLocalStorage = function getVhsLocalStorage() {
54977     if (!window.localStorage) {
54978       return null;
54979     }
54980 
54981     var storedObject = window.localStorage.getItem(LOCAL_STORAGE_KEY$1);
54982 
54983     if (!storedObject) {
54984       return null;
54985     }
54986 
54987     try {
54988       return JSON.parse(storedObject);
54989     } catch (e) {
54990       // someone may have tampered with the value
54991       return null;
54992     }
54993   };
54994 
54995   var updateVhsLocalStorage = function updateVhsLocalStorage(options) {
54996     if (!window.localStorage) {
54997       return false;
54998     }
54999 
55000     var objectToStore = getVhsLocalStorage();
55001     objectToStore = objectToStore ? videojs$1.mergeOptions(objectToStore, options) : options;
55002 
55003     try {
55004       window.localStorage.setItem(LOCAL_STORAGE_KEY$1, JSON.stringify(objectToStore));
55005     } catch (e) {
55006       // Throws if storage is full (e.g., always on iOS 5+ Safari private mode, where
55007       // storage is set to 0).
55008       // https://developer.mozilla.org/en-US/docs/Web/API/Storage/setItem#Exceptions
55009       // No need to perform any operation.
55010       return false;
55011     }
55012 
55013     return objectToStore;
55014   };
55015   /**
55016    * Whether the browser has built-in HLS support.
55017    */
55018 
55019 
55020   Hls$1.supportsNativeHls = function () {
55021     var video = document.createElement('video'); // native HLS is definitely not supported if HTML5 video isn't
55022 
55023     if (!videojs$1.getTech('Html5').isSupported()) {
55024       return false;
55025     } // HLS manifests can go by many mime-types
55026 
55027 
55028     var canPlay = [// Apple santioned
55029     'application/vnd.apple.mpegurl', // Apple sanctioned for backwards compatibility
55030     'audio/mpegurl', // Very common
55031     'audio/x-mpegurl', // Very common
vendor: 16,384 bytes, lines 55032-55444
55032     'application/x-mpegurl', // Included for completeness
55033     'video/x-mpegurl', 'video/mpegurl', 'application/mpegurl'];
55034     return canPlay.some(function (canItPlay) {
55035       return /maybe|probably/i.test(video.canPlayType(canItPlay));
55036     });
55037   }();
55038 
55039   Hls$1.supportsNativeDash = function () {
55040     if (!videojs$1.getTech('Html5').isSupported()) {
55041       return false;
55042     }
55043 
55044     return /maybe|probably/i.test(document.createElement('video').canPlayType('application/dash+xml'));
55045   }();
55046 
55047   Hls$1.supportsTypeNatively = function (type) {
55048     if (type === 'hls') {
55049       return Hls$1.supportsNativeHls;
55050     }
55051 
55052     if (type === 'dash') {
55053       return Hls$1.supportsNativeDash;
55054     }
55055 
55056     return false;
55057   };
55058   /**
55059    * HLS is a source handler, not a tech. Make sure attempts to use it
55060    * as one do not cause exceptions.
55061    */
55062 
55063 
55064   Hls$1.isSupported = function () {
55065     return videojs$1.log.warn('HLS is no longer a tech. Please remove it from ' + 'your player\'s techOrder.');
55066   };
55067 
55068   var Component$1 = videojs$1.getComponent('Component');
55069   /**
55070    * The Hls Handler object, where we orchestrate all of the parts
55071    * of HLS to interact with video.js
55072    *
55073    * @class HlsHandler
55074    * @extends videojs.Component
55075    * @param {Object} source the soruce object
55076    * @param {Tech} tech the parent tech object
55077    * @param {Object} options optional and required options
55078    */
55079 
55080   var HlsHandler = function (_Component) {
55081     inherits$2(HlsHandler, _Component);
55082 
55083     function HlsHandler(source, tech, options) {
55084       classCallCheck$1(this, HlsHandler); // tech.player() is deprecated but setup a reference to HLS for
55085       // backwards-compatibility
55086 
55087       var _this = possibleConstructorReturn$1(this, (HlsHandler.__proto__ || Object.getPrototypeOf(HlsHandler)).call(this, tech, options.hls));
55088 
55089       if (tech.options_ && tech.options_.playerId) {
55090         var _player = videojs$1(tech.options_.playerId);
55091 
55092         if (!_player.hasOwnProperty('hls')) {
55093           Object.defineProperty(_player, 'hls', {
55094             get: function get$$1() {
55095               videojs$1.log.warn('player.hls is deprecated. Use player.tech().hls instead.');
55096               tech.trigger({
55097                 type: 'usage',
55098                 name: 'hls-player-access'
55099               });
55100               return _this;
55101             },
55102             configurable: true
55103           });
55104         } // Set up a reference to the HlsHandler from player.vhs. This allows users to start
55105         // migrating from player.tech_.hls... to player.vhs... for API access. Although this
55106         // isn't the most appropriate form of reference for video.js (since all APIs should
55107         // be provided through core video.js), it is a common pattern for plugins, and vhs
55108         // will act accordingly.
55109 
55110 
55111         _player.vhs = _this; // deprecated, for backwards compatibility
55112 
55113         _player.dash = _this;
55114         _this.player_ = _player;
55115       }
55116 
55117       _this.tech_ = tech;
55118       _this.source_ = source;
55119       _this.stats = {};
55120 
55121       _this.setOptions_();
55122 
55123       if (_this.options_.overrideNative && tech.overrideNativeAudioTracks && tech.overrideNativeVideoTracks) {
55124         tech.overrideNativeAudioTracks(true);
55125         tech.overrideNativeVideoTracks(true);
55126       } else if (_this.options_.overrideNative && (tech.featuresNativeVideoTracks || tech.featuresNativeAudioTracks)) {
55127         // overriding native HLS only works if audio tracks have been emulated
55128         // error early if we're misconfigured
55129         throw new Error('Overriding native HLS requires emulated tracks. ' + 'See https://git.io/vMpjB');
55130       } // listen for fullscreenchange events for this player so that we
55131       // can adjust our quality selection quickly
55132 
55133 
55134       _this.on(document, ['fullscreenchange', 'webkitfullscreenchange', 'mozfullscreenchange', 'MSFullscreenChange'], function (event) {
55135         var fullscreenElement = document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement;
55136 
55137         if (fullscreenElement && fullscreenElement.contains(_this.tech_.el())) {
55138           _this.masterPlaylistController_.smoothQualityChange_();
55139         }
55140       }); // Handle seeking when looping - middleware doesn't handle this seek event from the tech
55141 
55142 
55143       _this.on(_this.tech_, 'seeking', function () {
55144         if (this.tech_.currentTime() === 0 && this.tech_.player_.loop()) {
55145           this.setCurrentTime(0);
55146         }
55147       });
55148 
55149       _this.on(_this.tech_, 'error', function () {
55150         if (this.masterPlaylistController_) {
55151           this.masterPlaylistController_.pauseLoading();
55152         }
55153       });
55154 
55155       _this.on(_this.tech_, 'play', _this.play);
55156 
55157       return _this;
55158     }
55159 
55160     createClass$1(HlsHandler, [{
55161       key: 'setOptions_',
55162       value: function setOptions_() {
55163         var _this2 = this; // defaults
55164 
55165 
55166         this.options_.withCredentials = this.options_.withCredentials || false;
55167         this.options_.handleManifestRedirects = this.options_.handleManifestRedirects || false;
55168         this.options_.limitRenditionByPlayerDimensions = this.options_.limitRenditionByPlayerDimensions === false ? false : true;
55169         this.options_.smoothQualityChange = this.options_.smoothQualityChange || false;
55170         this.options_.useBandwidthFromLocalStorage = typeof this.source_.useBandwidthFromLocalStorage !== 'undefined' ? this.source_.useBandwidthFromLocalStorage : this.options_.useBandwidthFromLocalStorage || false;
55171         this.options_.customTagParsers = this.options_.customTagParsers || [];
55172         this.options_.customTagMappers = this.options_.customTagMappers || [];
55173         this.options_.cacheEncryptionKeys = this.options_.cacheEncryptionKeys || false;
55174 
55175         if (typeof this.options_.blacklistDuration !== 'number') {
55176           this.options_.blacklistDuration = 5 * 60;
55177         }
55178 
55179         if (typeof this.options_.bandwidth !== 'number') {
55180           if (this.options_.useBandwidthFromLocalStorage) {
55181             var storedObject = getVhsLocalStorage();
55182 
55183             if (storedObject && storedObject.bandwidth) {
55184               this.options_.bandwidth = storedObject.bandwidth;
55185               this.tech_.trigger({
55186                 type: 'usage',
55187                 name: 'hls-bandwidth-from-local-storage'
55188               });
55189             }
55190 
55191             if (storedObject && storedObject.throughput) {
55192               this.options_.throughput = storedObject.throughput;
55193               this.tech_.trigger({
55194                 type: 'usage',
55195                 name: 'hls-throughput-from-local-storage'
55196               });
55197             }
55198           }
55199         } // if bandwidth was not set by options or pulled from local storage, start playlist
55200         // selection at a reasonable bandwidth
55201 
55202 
55203         if (typeof this.options_.bandwidth !== 'number') {
55204           this.options_.bandwidth = Config.INITIAL_BANDWIDTH;
55205         } // If the bandwidth number is unchanged from the initial setting
55206         // then this takes precedence over the enableLowInitialPlaylist option
55207 
55208 
55209         this.options_.enableLowInitialPlaylist = this.options_.enableLowInitialPlaylist && this.options_.bandwidth === Config.INITIAL_BANDWIDTH; // grab options passed to player.src
55210 
55211         ['withCredentials', 'limitRenditionByPlayerDimensions', 'bandwidth', 'smoothQualityChange', 'customTagParsers', 'customTagMappers', 'handleManifestRedirects', 'cacheEncryptionKeys'].forEach(function (option) {
55212           if (typeof _this2.source_[option] !== 'undefined') {
55213             _this2.options_[option] = _this2.source_[option];
55214           }
55215         });
55216         this.limitRenditionByPlayerDimensions = this.options_.limitRenditionByPlayerDimensions;
55217       }
55218       /**
55219        * called when player.src gets called, handle a new source
55220        *
55221        * @param {Object} src the source object to handle
55222        */
55223 
55224     }, {
55225       key: 'src',
55226       value: function src(_src, type) {
55227         var _this3 = this; // do nothing if the src is falsey
55228 
55229 
55230         if (!_src) {
55231           return;
55232         }
55233 
55234         this.setOptions_(); // add master playlist controller options
55235 
55236         this.options_.url = this.source_.src;
55237         this.options_.tech = this.tech_;
55238         this.options_.externHls = Hls$1;
55239         this.options_.sourceType = simpleTypeFromSourceType(type); // Whenever we seek internally, we should update both the tech and call our own
55240         // setCurrentTime function. This is needed because "seeking" events aren't always
55241         // reliable. External seeks (via the player object) are handled via middleware.
55242 
55243         this.options_.seekTo = function (time) {
55244           _this3.tech_.setCurrentTime(time);
55245 
55246           _this3.setCurrentTime(time);
55247         };
55248 
55249         this.masterPlaylistController_ = new MasterPlaylistController(this.options_);
55250         this.playbackWatcher_ = new PlaybackWatcher(videojs$1.mergeOptions(this.options_, {
55251           seekable: function seekable$$1() {
55252             return _this3.seekable();
55253           },
55254           media: function media() {
55255             return _this3.masterPlaylistController_.media();
55256           }
55257         }));
55258         this.masterPlaylistController_.on('error', function () {
55259           var player = videojs$1.players[_this3.tech_.options_.playerId];
55260           player.error(_this3.masterPlaylistController_.error);
55261         }); // `this` in selectPlaylist should be the HlsHandler for backwards
55262         // compatibility with < v2
55263 
55264         this.masterPlaylistController_.selectPlaylist = this.selectPlaylist ? this.selectPlaylist.bind(this) : Hls$1.STANDARD_PLAYLIST_SELECTOR.bind(this);
55265         this.masterPlaylistController_.selectInitialPlaylist = Hls$1.INITIAL_PLAYLIST_SELECTOR.bind(this); // re-expose some internal objects for backwards compatibility with < v2
55266 
55267         this.playlists = this.masterPlaylistController_.masterPlaylistLoader_;
55268         this.mediaSource = this.masterPlaylistController_.mediaSource; // Proxy assignment of some properties to the master playlist
55269         // controller. Using a custom property for backwards compatibility
55270         // with < v2
55271 
55272         Object.defineProperties(this, {
55273           selectPlaylist: {
55274             get: function get$$1() {
55275               return this.masterPlaylistController_.selectPlaylist;
55276             },
55277             set: function set$$1(selectPlaylist) {
55278               this.masterPlaylistController_.selectPlaylist = selectPlaylist.bind(this);
55279             }
55280           },
55281           throughput: {
55282             get: function get$$1() {
55283               return this.masterPlaylistController_.mainSegmentLoader_.throughput.rate;
55284             },
55285             set: function set$$1(throughput) {
55286               this.masterPlaylistController_.mainSegmentLoader_.throughput.rate = throughput; // By setting `count` to 1 the throughput value becomes the starting value
55287               // for the cumulative average
55288 
55289               this.masterPlaylistController_.mainSegmentLoader_.throughput.count = 1;
55290             }
55291           },
55292           bandwidth: {
55293             get: function get$$1() {
55294               return this.masterPlaylistController_.mainSegmentLoader_.bandwidth;
55295             },
55296             set: function set$$1(bandwidth) {
55297               this.masterPlaylistController_.mainSegmentLoader_.bandwidth = bandwidth; // setting the bandwidth manually resets the throughput counter
55298               // `count` is set to zero that current value of `rate` isn't included
55299               // in the cumulative average
55300 
55301               this.masterPlaylistController_.mainSegmentLoader_.throughput = {
55302                 rate: 0,
55303                 count: 0
55304               };
55305             }
55306           },
55307 
55308           /**
55309            * `systemBandwidth` is a combination of two serial processes bit-rates. The first
55310            * is the network bitrate provided by `bandwidth` and the second is the bitrate of
55311            * the entire process after that - decryption, transmuxing, and appending - provided
55312            * by `throughput`.
55313            *
55314            * Since the two process are serial, the overall system bandwidth is given by:
55315            *   sysBandwidth = 1 / (1 / bandwidth + 1 / throughput)
55316            */
55317           systemBandwidth: {
55318             get: function get$$1() {
55319               var invBandwidth = 1 / (this.bandwidth || 1);
55320               var invThroughput = void 0;
55321 
55322               if (this.throughput > 0) {
55323                 invThroughput = 1 / this.throughput;
55324               } else {
55325                 invThroughput = 0;
55326               }
55327 
55328               var systemBitrate = Math.floor(1 / (invBandwidth + invThroughput));
55329               return systemBitrate;
55330             },
55331             set: function set$$1() {
55332               videojs$1.log.error('The "systemBandwidth" property is read-only');
55333             }
55334           }
55335         });
55336 
55337         if (this.options_.bandwidth) {
55338           this.bandwidth = this.options_.bandwidth;
55339         }
55340 
55341         if (this.options_.throughput) {
55342           this.throughput = this.options_.throughput;
55343         }
55344 
55345         Object.defineProperties(this.stats, {
55346           bandwidth: {
55347             get: function get$$1() {
55348               return _this3.bandwidth || 0;
55349             },
55350             enumerable: true
55351           },
55352           mediaRequests: {
55353             get: function get$$1() {
55354               return _this3.masterPlaylistController_.mediaRequests_() || 0;
55355             },
55356             enumerable: true
55357           },
55358           mediaRequestsAborted: {
55359             get: function get$$1() {
55360               return _this3.masterPlaylistController_.mediaRequestsAborted_() || 0;
55361             },
55362             enumerable: true
55363           },
55364           mediaRequestsTimedout: {
55365             get: function get$$1() {
55366               return _this3.masterPlaylistController_.mediaRequestsTimedout_() || 0;
55367             },
55368             enumerable: true
55369           },
55370           mediaRequestsErrored: {
55371             get: function get$$1() {
55372               return _this3.masterPlaylistController_.mediaRequestsErrored_() || 0;
55373             },
55374             enumerable: true
55375           },
55376           mediaTransferDuration: {
55377             get: function get$$1() {
55378               return _this3.masterPlaylistController_.mediaTransferDuration_() || 0;
55379             },
55380             enumerable: true
55381           },
55382           mediaBytesTransferred: {
55383             get: function get$$1() {
55384               return _this3.masterPlaylistController_.mediaBytesTransferred_() || 0;
55385             },
55386             enumerable: true
55387           },
55388           mediaSecondsLoaded: {
55389             get: function get$$1() {
55390               return _this3.masterPlaylistController_.mediaSecondsLoaded_() || 0;
55391             },
55392             enumerable: true
55393           },
55394           buffered: {
55395             get: function get$$1() {
55396               return timeRangesToArray(_this3.tech_.buffered());
55397             },
55398             enumerable: true
55399           },
55400           currentTime: {
55401             get: function get$$1() {
55402               return _this3.tech_.currentTime();
55403             },
55404             enumerable: true
55405           },
55406           currentSource: {
55407             get: function get$$1() {
55408               return _this3.tech_.currentSource_;
55409             },
55410             enumerable: true
55411           },
55412           currentTech: {
55413             get: function get$$1() {
55414               return _this3.tech_.name_;
55415             },
55416             enumerable: true
55417           },
55418           duration: {
55419             get: function get$$1() {
55420               return _this3.tech_.duration();
55421             },
55422             enumerable: true
55423           },
55424           master: {
55425             get: function get$$1() {
55426               return _this3.playlists.master;
55427             },
55428             enumerable: true
55429           },
55430           playerDimensions: {
55431             get: function get$$1() {
55432               return _this3.tech_.currentDimensions();
55433             },
55434             enumerable: true
55435           },
55436           seekable: {
55437             get: function get$$1() {
55438               return timeRangesToArray(_this3.tech_.seekable());
55439             },
55440             enumerable: true
55441           },
55442           timestamp: {
55443             get: function get$$1() {
55444          
vendor: 3,650 bytes, lines 55444-55559
55444     return Date.now();
55445             },
55446             enumerable: true
55447           },
55448           videoPlaybackQuality: {
55449             get: function get$$1() {
55450               return _this3.tech_.getVideoPlaybackQuality();
55451             },
55452             enumerable: true
55453           }
55454         });
55455         this.tech_.one('canplay', this.masterPlaylistController_.setupFirstPlay.bind(this.masterPlaylistController_));
55456         this.tech_.on('bandwidthupdate', function () {
55457           if (_this3.options_.useBandwidthFromLocalStorage) {
55458             updateVhsLocalStorage({
55459               bandwidth: _this3.bandwidth,
55460               throughput: Math.round(_this3.throughput)
55461             });
55462           }
55463         });
55464         this.masterPlaylistController_.on('selectedinitialmedia', function () {
55465           // Add the manual rendition mix-in to HlsHandler
55466           renditionSelectionMixin(_this3);
55467           setupEmeOptions(_this3);
55468         }); // the bandwidth of the primary segment loader is our best
55469         // estimate of overall bandwidth
55470 
55471         this.on(this.masterPlaylistController_, 'progress', function () {
55472           this.tech_.trigger('progress');
55473         });
55474         this.tech_.ready(function () {
55475           return _this3.setupQualityLevels_();
55476         }); // do nothing if the tech has been disposed already
55477         // this can occur if someone sets the src in player.ready(), for instance
55478 
55479         if (!this.tech_.el()) {
55480           return;
55481         }
55482 
55483         this.tech_.src(videojs$1.URL.createObjectURL(this.masterPlaylistController_.mediaSource));
55484       }
55485       /**
55486        * Initializes the quality levels and sets listeners to update them.
55487        *
55488        * @method setupQualityLevels_
55489        * @private
55490        */
55491 
55492     }, {
55493       key: 'setupQualityLevels_',
55494       value: function setupQualityLevels_() {
55495         var _this4 = this;
55496 
55497         var player = videojs$1.players[this.tech_.options_.playerId];
55498 
55499         if (player && player.qualityLevels) {
55500           this.qualityLevels_ = player.qualityLevels();
55501           this.masterPlaylistController_.on('selectedinitialmedia', function () {
55502             handleHlsLoadedMetadata(_this4.qualityLevels_, _this4);
55503           });
55504           this.playlists.on('mediachange', function () {
55505             handleHlsMediaChange(_this4.qualityLevels_, _this4.playlists);
55506           });
55507         }
55508       }
55509       /**
55510        * Begin playing the video.
55511        */
55512 
55513     }, {
55514       key: 'play',
55515       value: function play() {
55516         this.masterPlaylistController_.play();
55517       }
55518       /**
55519        * a wrapper around the function in MasterPlaylistController
55520        */
55521 
55522     }, {
55523       key: 'setCurrentTime',
55524       value: function setCurrentTime(currentTime) {
55525         this.masterPlaylistController_.setCurrentTime(currentTime);
55526       }
55527       /**
55528        * a wrapper around the function in MasterPlaylistController
55529        */
55530 
55531     }, {
55532       key: 'duration',
55533       value: function duration$$1() {
55534         return this.masterPlaylistController_.duration();
55535       }
55536       /**
55537        * a wrapper around the function in MasterPlaylistController
55538        */
55539 
55540     }, {
55541       key: 'seekable',
55542       value: function seekable$$1() {
55543         return this.masterPlaylistController_.seekable();
55544       }
55545       /**
55546        * Abort all outstanding work and cleanup.
55547        */
55548 
55549     }, {
55550       key: 'dispose',
55551       value: function dispose() {
55552         if (this.playbackWatcher_) {
55553           this.playbackWatcher_.dispose();
55554         }
55555 
55556         if (this.masterPlaylistController_) {
55557           this.masterPlaylistController_.dispose();
55558         }
55559 
55560         if (this.qualityLevels_) {
55561           this.qualityLevels_.dispose();
55562         }
55563 
55564         if (this.player_) {
55565           delete this.player_.vhs;
55566           delete this.player_.dash;
55567           delete this.player_.hls;
55568         }
55569 
55570         if (this.tech_ && this.tech_.hls) {
55571           delete this.tech_.hls;
55572         }
55573 
55574         get$1(HlsHandler.prototype.__proto__ || Object.getPrototypeOf(HlsHandler.prototype), 'dispose', this).call(this);
55575       }
55576     }, {
55577       key: 'convertToProgramTime',
55578       value: function convertToProgramTime(time, callback) {
55579         return getProgramTime({
55580           playlist: this.masterPlaylistController_.media(),
55581           time: time,
55582           callback: callback
55583         });
55584       } // the player must be playing before calling this
55585 
55586     }, {
55587       key: 'seekToProgramTime',
55588       value: function seekToProgramTime$$1(programTime, callback) {
55589         var pauseAfterSeek = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
55590         var retryCount = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 2;
55591         return seekToProgramTime({
55592           programTime: programTime,
55593           playlist: this.masterPlaylistController_.media(),
55594           retryCount: retryCount,
55595           pauseAfterSeek: pauseAfterSeek,
55596           seekTo: this.options_.seekTo,
55597           tech: this.options_.tech,
55598           callback: callback
55599         });
55600       }
55601     }]);
55602     return HlsHandler;
55603   }(Component$1);
55604   /**
55605    * The Source Handler object, which informs video.js what additional
55606    * MIME types are supported and sets up playback. It is registered
55607    * automatically to the appropriate tech based on the capabilities of
55608    * the browser it is running in. It is not necessary to use or modify
55609    * this object in normal usage.
55610    */
55611 
55612 
55613   var HlsSourceHandler = {
55614     name: 'videojs-http-streaming',
55615     VERSION: version$1,
55616     canHandleSource: function canHandleSource(srcObj) {
55617       var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
55618       var localOptions = videojs$1.mergeOptions(videojs$1.options, options);
55619       return HlsSourceHandler.canPlayType(srcObj.type, localOptions);
55620     },
55621     handleSource: function handleSource(source, tech) {
55622       var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
55623       var localOptions = videojs$1.mergeOptions(videojs$1.options, options);
55624       tech.hls = new HlsHandler(source, tech, localOptions);
55625       tech.hls.xhr = xhrFactory();
55626       tech.hls.src(source.src, source.type);
55627       return tech.hls;
55628     },
55629     canPlayType: function canPlayType(type) {
55630       var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
55631 
55632       var _videojs$mergeOptions = videojs$1.mergeOptions(videojs$1.options, options),
55633           overrideNative = _videojs$mergeOptions.hls.overrideNative;
55634 
55635       var supportedType = simpleTypeFromSourceType(type);
55636       var canUseMsePlayback = supportedType && (!Hls$1.supportsTypeNatively(supportedType) || overrideNative);
55637       return canUseMsePlayback ? 'maybe' : '';
55638     }
55639   };
55640 
55641   if (typeof videojs$1.MediaSource === 'undefined' || typeof videojs$1.URL === 'undefined') {
55642     videojs$1.MediaSource = MediaSource;
55643     videojs$1.URL = URL$1;
55644   } // register source handlers with the appropriate techs
55645 
55646 
55647   if (MediaSource.supportsNativeMediaSources()) {
55648     videojs$1.getTech('Html5').registerSourceHandler(HlsSourceHandler, 0);
55649   }
55650 
55651   videojs$1.HlsHandler = HlsHandler;
55652   videojs$1.HlsSourceHandler = HlsSourceHandler;
55653   videojs$1.Hls = Hls$1;
55654 
55655   if (!videojs$1.use) {
55656     videojs$1.registerComponent('Hls', Hls$1);
55657   }
55658 
55659   videojs$1.options.hls = videojs$1.options.hls || {};
55660 
55661   if (videojs$1.registerPlugin) {
55662     videojs$1.registerPlugin('reloadSourceOnError', reloadSourceOnError);
55663   } else {
55664     videojs$1.plugin('reloadSourceOnError', reloadSourceOnError);
55665   }
55666 
55667   return videojs$1;
55668 
55669 }));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.